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