1/*	$NetBSD: recvauth.c,v 1.2 2017/01/28 21:31:49 christos Exp $	*/
2
3/*
4 * Copyright (c) 1997-2007 Kungliga Tekniska H��gskolan
5 * (Royal Institute of Technology, Stockholm, Sweden).
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * 3. Neither the name of the Institute nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36#include "krb5_locl.h"
37
38/*
39 * See `sendauth.c' for the format.
40 */
41
42static krb5_boolean
43match_exact(const void *data, const char *appl_version)
44{
45    return strcmp(data, appl_version) == 0;
46}
47
48/**
49 * Perform the server side of the sendauth protocol.
50 *
51 * @param context       Kerberos 5 context.
52 * @param auth_context  authentication context of the peer.
53 * @param p_fd          socket associated to the connection.
54 * @param appl_version  server-specific string.
55 * @param server        server principal.
56 * @param flags         if KRB5_RECVAUTH_IGNORE_VERSION is set, skip the sendauth version
57 *                      part of the protocol.
58 * @param keytab        server keytab.
59 * @param ticket        on success, set to the authenticated client credentials.
60 *                      Must be deallocated with krb5_free_ticket(). If not
61 *                      interested, pass a NULL value.
62 *
63 * @return 0 to indicate success. Otherwise a Kerberos error code is
64 *         returned, see krb5_get_error_message().
65 */
66KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
67krb5_recvauth(krb5_context context,
68	      krb5_auth_context *auth_context,
69	      krb5_pointer p_fd,
70	      const char *appl_version,
71	      krb5_principal server,
72	      int32_t flags,
73	      krb5_keytab keytab,
74	      krb5_ticket **ticket)
75{
76    return krb5_recvauth_match_version(context, auth_context, p_fd,
77				       match_exact, appl_version,
78				       server, flags,
79				       keytab, ticket);
80}
81
82/**
83 * Perform the server side of the sendauth protocol like krb5_recvauth(), but support
84 * a user-specified callback, \a match_appl_version, to perform the match of the application
85 * version \a match_data.
86 */
87KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
88krb5_recvauth_match_version(krb5_context context,
89			    krb5_auth_context *auth_context,
90			    krb5_pointer p_fd,
91			    krb5_boolean (*match_appl_version)(const void *,
92							       const char*),
93			    const void *match_data,
94			    krb5_principal server,
95			    int32_t flags,
96			    krb5_keytab keytab,
97			    krb5_ticket **ticket)
98{
99    krb5_error_code ret;
100    const char *version = KRB5_SENDAUTH_VERSION;
101    char her_version[sizeof(KRB5_SENDAUTH_VERSION)];
102    char *her_appl_version;
103    uint32_t len;
104    u_char repl;
105    krb5_data data;
106    krb5_flags ap_options;
107    ssize_t n;
108
109    /*
110     * If there are no addresses in auth_context, get them from `fd'.
111     */
112
113    if (*auth_context == NULL) {
114	ret = krb5_auth_con_init (context, auth_context);
115	if (ret)
116	    return ret;
117    }
118
119    ret = krb5_auth_con_setaddrs_from_fd (context,
120					  *auth_context,
121					  p_fd);
122    if (ret)
123	return ret;
124
125    /*
126     * Expect SENDAUTH protocol version.
127     */
128    if(!(flags & KRB5_RECVAUTH_IGNORE_VERSION)) {
129	n = krb5_net_read (context, p_fd, &len, 4);
130	if (n < 0) {
131	    ret = errno ? errno : EINVAL;
132	    krb5_set_error_message(context, ret, "read: %s", strerror(ret));
133	    return ret;
134	}
135	if (n == 0) {
136	    krb5_set_error_message(context, KRB5_SENDAUTH_BADAUTHVERS,
137				   N_("Failed to receive sendauth data", ""));
138	    return KRB5_SENDAUTH_BADAUTHVERS;
139	}
140	len = ntohl(len);
141	if (len != sizeof(her_version)
142	    || krb5_net_read (context, p_fd, her_version, len) != len
143	    || strncmp (version, her_version, len)) {
144	    repl = 1;
145	    krb5_net_write (context, p_fd, &repl, 1);
146	    krb5_clear_error_message (context);
147	    return KRB5_SENDAUTH_BADAUTHVERS;
148	}
149    }
150
151    /*
152     * Expect application protocol version.
153     */
154    n = krb5_net_read (context, p_fd, &len, 4);
155    if (n < 0) {
156	ret = errno ? errno : EINVAL;
157	krb5_set_error_message(context, ret, "read: %s", strerror(ret));
158	return ret;
159    }
160    if (n == 0) {
161	krb5_clear_error_message (context);
162	return KRB5_SENDAUTH_BADAPPLVERS;
163    }
164    len = ntohl(len);
165    her_appl_version = malloc (len);
166    if (her_appl_version == NULL) {
167	repl = 2;
168	krb5_net_write (context, p_fd, &repl, 1);
169	return krb5_enomem(context);
170    }
171    if (krb5_net_read (context, p_fd, her_appl_version, len) != len
172	|| !(*match_appl_version)(match_data, her_appl_version)) {
173	repl = 2;
174	krb5_net_write (context, p_fd, &repl, 1);
175	krb5_set_error_message(context, KRB5_SENDAUTH_BADAPPLVERS,
176			       N_("wrong sendauth application version (%s)", ""),
177			       her_appl_version);
178	free (her_appl_version);
179	return KRB5_SENDAUTH_BADAPPLVERS;
180    }
181    free (her_appl_version);
182
183    /*
184     * Send OK.
185     */
186    repl = 0;
187    if (krb5_net_write (context, p_fd, &repl, 1) != 1) {
188	ret = errno ? errno : EINVAL;
189	krb5_set_error_message(context, ret, "write: %s", strerror(ret));
190	return ret;
191    }
192
193    /*
194     * Until here, the fields in the message were in cleartext and unauthenticated.
195     * From now on, Kerberos kicks in.
196     */
197
198    /*
199     * Expect AP_REQ.
200     */
201    krb5_data_zero (&data);
202    ret = krb5_read_message (context, p_fd, &data);
203    if (ret)
204	return ret;
205
206    ret = krb5_rd_req (context,
207		       auth_context,
208		       &data,
209		       server,
210		       keytab,
211		       &ap_options,
212		       ticket);
213    krb5_data_free (&data);
214    if (ret) {
215	krb5_data error_data;
216	krb5_error_code ret2;
217
218	ret2 = krb5_mk_error (context,
219			      ret,
220			      NULL,
221			      NULL,
222			      NULL,
223			      server,
224			      NULL,
225			      NULL,
226			      &error_data);
227	if (ret2 == 0) {
228	    krb5_write_message (context, p_fd, &error_data);
229	    krb5_data_free (&error_data);
230	}
231	return ret;
232    }
233
234    /*
235     * Send OK.
236     */
237    len = 0;
238    if (krb5_net_write (context, p_fd, &len, 4) != 4) {
239	ret = errno ? errno : EINVAL;
240	krb5_set_error_message(context, ret, "write: %s", strerror(ret));
241	krb5_free_ticket(context, *ticket);
242	*ticket = NULL;
243	return ret;
244    }
245
246    /*
247     * If client requires mutual authentication, send AP_REP.
248     */
249    if (ap_options & AP_OPTS_MUTUAL_REQUIRED) {
250	ret = krb5_mk_rep (context, *auth_context, &data);
251	if (ret) {
252	    krb5_free_ticket(context, *ticket);
253	    *ticket = NULL;
254	    return ret;
255	}
256
257	ret = krb5_write_message (context, p_fd, &data);
258	if (ret) {
259	    krb5_free_ticket(context, *ticket);
260	    *ticket = NULL;
261	    return ret;
262	}
263	krb5_data_free (&data);
264    }
265    return 0;
266}
267