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