1 /* client.c --- Experimental SASL mechanism KERBEROS_V5, client side.
2 * Copyright (C) 2003-2021 Simon Josefsson
3 *
4 * This file is part of GNU SASL Library.
5 *
6 * GNU SASL Library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public License
8 * as published by the Free Software Foundation; either version 2.1 of
9 * the License, or (at your option) any later version.
10 *
11 * GNU SASL Library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with GNU SASL Library; if not, write to the Free
18 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 *
21 * NB! Shishi is licensed under GPL, so linking GSASL with it require
22 * that you follow the GPL for GSASL as well.
23 *
24 */
25
26 #include "kerberos_v5.h"
27
28 #include "shared.h"
29
30 struct _Gsasl_kerberos_v5_client_state
31 {
32 int step;
33 char serverhello[BITMAP_LEN + MAXBUF_LEN + RANDOM_LEN];
34 int serverqops;
35 int clientqop;
36 int servermutual;
37 uint32_t servermaxbuf;
38 uint32_t clientmaxbuf;
39 Shishi *sh;
40 Shishi_tkt *tkt;
41 Shishi_as *as;
42 Shishi_ap *ap;
43 Shishi_key *sessionkey;
44 Shishi_safe *safe;
45 };
46
47 int
_gsasl_kerberos_v5_client_init(Gsasl_ctx * ctx)48 _gsasl_kerberos_v5_client_init (Gsasl_ctx * ctx)
49 {
50 if (!shishi_check_version (SHISHI_VERSION))
51 return GSASL_UNKNOWN_MECHANISM;
52
53 return GSASL_OK;
54 }
55
56 int
_gsasl_kerberos_v5_client_start(Gsasl_session * sctx,void ** mech_data)57 _gsasl_kerberos_v5_client_start (Gsasl_session * sctx, void **mech_data)
58 {
59 struct _Gsasl_kerberos_v5_client_state *state;
60 Gsasl_ctx *ctx;
61 int err;
62
63 state = malloc (sizeof (*state));
64 if (state == NULL)
65 return GSASL_MALLOC_ERROR;
66
67 memset (state, 0, sizeof (*state));
68
69 err = shishi_init (&state->sh);
70 if (err)
71 return GSASL_KERBEROS_V5_INIT_ERROR;
72
73 state->step = 0;
74 state->clientqop = GSASL_QOP_AUTH_INT;
75
76 *mech_data = state;
77
78 return GSASL_OK;
79 }
80
81 #define STEP_FIRST 0
82 #define STEP_NONINFRA_SEND_ASREQ 1
83 #define STEP_NONINFRA_WAIT_ASREP 2
84 #define STEP_NONINFRA_SEND_APREQ 3
85 #define STEP_NONINFRA_WAIT_APREP 4
86 #define STEP_SUCCESS 5
87
88 int
_gsasl_kerberos_v5_client_step(Gsasl_session * sctx,void * mech_data,const char * input,size_t input_len,char * output,size_t * output_len)89 _gsasl_kerberos_v5_client_step (Gsasl_session * sctx,
90 void *mech_data,
91 const char *input,
92 size_t input_len,
93 char *output, size_t *output_len)
94 {
95 struct _Gsasl_kerberos_v5_client_state *state = mech_data;
96 Gsasl_client_callback_authentication_id cb_authentication_id;
97 Gsasl_client_callback_authorization_id cb_authorization_id;
98 Gsasl_client_callback_qop cb_qop;
99 Gsasl_client_callback_realm cb_realm;
100 Gsasl_client_callback_password cb_password;
101 Gsasl_client_callback_service cb_service;
102 Gsasl_client_callback_maxbuf cb_maxbuf;
103 Gsasl_ctx *ctx;
104 int res;
105 int len;
106
107 ctx = gsasl_client_ctx_get (sctx);
108 if (ctx == NULL)
109 return GSASL_CANNOT_GET_CTX;
110
111 /* These are optional */
112 cb_realm = gsasl_client_callback_realm_get (ctx);
113 cb_service = gsasl_client_callback_service_get (ctx);
114 cb_authentication_id = gsasl_client_callback_authentication_id_get (ctx);
115 cb_authorization_id = gsasl_client_callback_authorization_id_get (ctx);
116 cb_qop = gsasl_client_callback_qop_get (ctx);
117 cb_maxbuf = gsasl_client_callback_maxbuf_get (ctx);
118
119 /* Only optionally needed in infrastructure mode */
120 cb_password = gsasl_client_callback_password_get (ctx);
121 if (cb_password == NULL)
122 return GSASL_NEED_CLIENT_PASSWORD_CALLBACK;
123
124 /* I think we really need this one */
125 cb_service = gsasl_client_callback_service_get (ctx);
126 if (cb_service == NULL)
127 return GSASL_NEED_CLIENT_SERVICE_CALLBACK;
128
129 switch (state->step)
130 {
131 case STEP_FIRST:
132 if (input == NULL)
133 {
134 *output_len = 0;
135 return GSASL_NEEDS_MORE;
136 }
137
138 if (input_len != SERVER_HELLO_LEN)
139 return GSASL_MECHANISM_PARSE_ERROR;
140
141 memcpy (state->serverhello, input, input_len);
142
143 {
144 unsigned char serverbitmap;
145
146 memcpy (&serverbitmap, input, BITMAP_LEN);
147 state->serverqops = 0;
148 if (serverbitmap & GSASL_QOP_AUTH)
149 state->serverqops |= GSASL_QOP_AUTH;
150 if (serverbitmap & GSASL_QOP_AUTH_INT)
151 state->serverqops |= GSASL_QOP_AUTH_INT;
152 if (serverbitmap & GSASL_QOP_AUTH_CONF)
153 state->serverqops |= GSASL_QOP_AUTH_CONF;
154 if (serverbitmap & MUTUAL)
155 state->servermutual = 1;
156 }
157 memcpy (&state->servermaxbuf, &input[BITMAP_LEN], MAXBUF_LEN);
158 state->servermaxbuf = ntohl (state->servermaxbuf);
159
160 if (cb_qop)
161 state->clientqop = cb_qop (sctx, state->serverqops);
162
163 if (!(state->serverqops & state->clientqop &
164 (GSASL_QOP_AUTH | GSASL_QOP_AUTH_INT | GSASL_QOP_AUTH_CONF)))
165 return GSASL_AUTHENTICATION_ERROR;
166
167 /* XXX for now we require server authentication */
168 if (!state->servermutual)
169 return GSASL_AUTHENTICATION_ERROR;
170
171 /* Decide policy here: non-infrastructure, infrastructure or proxy.
172 *
173 * A callback to decide should be added, but without the default
174 * should be:
175 *
176 * IF shishi_tktset_get_for_server() THEN
177 * INFRASTRUCTURE MODE
178 * ELSE IF shishi_realm_for_server(server) THEN
179 * PROXY INFRASTRUCTURE (then fallback to NIM?)
180 * ELSE
181 * NON-INFRASTRUCTURE MODE
182 */
183 state->step = STEP_NONINFRA_SEND_APREQ; /* only NIM for now.. */
184 /* fall through */
185
186 case STEP_NONINFRA_SEND_ASREQ:
187 res = shishi_as (state->sh, &state->as);
188 if (res)
189 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
190
191 if (cb_authentication_id) /* Shishi defaults to one otherwise */
192 {
193 len = *output_len - 1;
194 res = cb_authentication_id (sctx, output, &len);
195 if (res != GSASL_OK)
196 return res;
197 output[len] = '\0';
198
199 res = shishi_kdcreq_set_cname (state->sh, shishi_as_req (state->as),
200 SHISHI_NT_UNKNOWN, output);
201 if (res != GSASL_OK)
202 return res;
203 }
204
205 if (cb_realm)
206 {
207 len = *output_len - 1;
208 res = cb_realm (sctx, output, &len);
209 if (res != GSASL_OK)
210 return res;
211 }
212 else
213 len = 0;
214
215 output[len] = '\0';
216 res = shishi_kdcreq_set_realm (state->sh, shishi_as_req (state->as),
217 output);
218 if (res != GSASL_OK)
219 return res;
220
221 if (cb_service)
222 {
223 char *sname[3];
224 size_t servicelen = 0;
225 size_t hostnamelen = 0;
226
227 res = cb_service (sctx, NULL, &servicelen, NULL, &hostnamelen,
228 /* XXX support servicename a'la DIGEST-MD5 too? */
229 NULL, NULL);
230 if (res != GSASL_OK)
231 return res;
232
233 if (*output_len < servicelen + 1 + hostnamelen + 1)
234 return GSASL_TOO_SMALL_BUFFER;
235
236 sname[0] = &output[0];
237 sname[1] = &output[servicelen + 2];
238 sname[2] = NULL;
239
240 res = cb_service (sctx, sname[0], &servicelen,
241 sname[1], &hostnamelen, NULL, NULL);
242 if (res != GSASL_OK)
243 return res;
244
245 sname[0][servicelen] = '\0';
246 sname[1][hostnamelen] = '\0';
247
248 res = shishi_kdcreq_set_sname (state->sh, shishi_as_req (state->as),
249 SHISHI_NT_UNKNOWN, sname);
250 if (res != GSASL_OK)
251 return res;
252 }
253
254 /* XXX query application for encryption types and set the etype
255 field? Already configured by shishi though... */
256
257 res = shishi_a2d (state->sh, shishi_as_req (state->as),
258 output, output_len);
259 if (res != SHISHI_OK)
260 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
261
262 state->step = STEP_NONINFRA_WAIT_ASREP;
263
264 res = GSASL_NEEDS_MORE;
265 break;
266
267 case STEP_NONINFRA_WAIT_ASREP:
268 if (shishi_as_rep_der_set (state->as, input, input_len) != SHISHI_OK)
269 return GSASL_MECHANISM_PARSE_ERROR;
270
271 /* XXX? password stored in callee's output buffer */
272 len = *output_len - 1;
273 res = cb_password (sctx, output, &len);
274 if (res != GSASL_OK && res != GSASL_NEEDS_MORE)
275 return res;
276 output[len] = '\0';
277
278 res = shishi_as_rep_process (state->as, NULL, output);
279 if (res != SHISHI_OK)
280 return GSASL_AUTHENTICATION_ERROR;
281
282 state->step = STEP_NONINFRA_SEND_APREQ;
283 /* fall through */
284
285 case STEP_NONINFRA_SEND_APREQ:
286 if (*output_len <= CLIENT_HELLO_LEN + SERVER_HELLO_LEN)
287 return GSASL_TOO_SMALL_BUFFER;
288
289 if (!(state->clientqop & ~GSASL_QOP_AUTH))
290 state->clientmaxbuf = 0;
291 else if (cb_maxbuf)
292 state->clientmaxbuf = cb_maxbuf (sctx, state->servermaxbuf);
293 else
294 state->clientmaxbuf = MAXBUF_DEFAULT;
295
296 /* XXX for now we require server authentication */
297 output[0] = state->clientqop | MUTUAL;
298 {
299 uint32_t tmp;
300
301 tmp = ntohl (state->clientmaxbuf);
302 memcpy (&output[BITMAP_LEN], &tmp, MAXBUF_LEN);
303 }
304 memcpy (&output[CLIENT_HELLO_LEN], state->serverhello,
305 SERVER_HELLO_LEN);
306
307 if (cb_authorization_id)
308 {
309 len = *output_len - CLIENT_HELLO_LEN + SERVER_HELLO_LEN;
310 res = cb_authorization_id (sctx, &output[CLIENT_HELLO_LEN +
311 SERVER_HELLO_LEN], &len);
312 }
313 else
314 len = 0;
315
316 len += CLIENT_HELLO_LEN + SERVER_HELLO_LEN;
317 res = shishi_ap_tktoptionsdata (state->sh,
318 &state->ap,
319 shishi_as_tkt (state->as),
320 SHISHI_APOPTIONS_MUTUAL_REQUIRED,
321 output, len);
322 if (res != SHISHI_OK)
323 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
324
325 res = shishi_authenticator_add_authorizationdata
326 (state->sh, shishi_ap_authenticator (state->ap), -1, output, len);
327 if (res != SHISHI_OK)
328 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
329
330 /* XXX set realm in AP-REQ and Authenticator */
331
332 res = shishi_ap_req_der (state->ap, output, output_len);
333 if (res != SHISHI_OK)
334 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
335
336 state->step = STEP_NONINFRA_WAIT_APREP;
337
338 res = GSASL_NEEDS_MORE;
339 break;
340
341 case STEP_NONINFRA_WAIT_APREP:
342 if (shishi_ap_rep_der_set (state->ap, input, input_len) != SHISHI_OK)
343 return GSASL_MECHANISM_PARSE_ERROR;
344
345 res = shishi_ap_rep_verify (state->ap);
346 if (res != SHISHI_OK)
347 return GSASL_AUTHENTICATION_ERROR;
348
349 state->step = STEP_SUCCESS;
350
351 /* XXX support AP session keys */
352 state->sessionkey = shishi_tkt_key (shishi_as_tkt (state->as));
353
354 *output_len = 0;
355 res = GSASL_OK;
356 break;
357
358 default:
359 res = GSASL_MECHANISM_CALLED_TOO_MANY_TIMES;
360 break;
361 }
362
363 return res;
364 }
365
366 int
_gsasl_kerberos_v5_client_encode(Gsasl_session * sctx,void * mech_data,const char * input,size_t input_len,char ** output,size_t * output_len)367 _gsasl_kerberos_v5_client_encode (Gsasl_session * sctx,
368 void *mech_data,
369 const char *input,
370 size_t input_len,
371 char **output, size_t *output_len)
372 {
373 struct _Gsasl_kerberos_v5_client_state *state = mech_data;
374 int res;
375
376 if (state && state->sessionkey && state->clientqop & GSASL_QOP_AUTH_CONF)
377 {
378 return GSASL_INTEGRITY_ERROR;
379 }
380 else if (state && state->sessionkey
381 && state->clientqop & GSASL_QOP_AUTH_INT)
382 {
383 res = shishi_safe (state->sh, &state->safe);
384 if (res != SHISHI_OK)
385 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
386
387 res = shishi_safe_set_user_data (state->sh,
388 shishi_safe_safe (state->safe),
389 input, input_len);
390 if (res != SHISHI_OK)
391 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
392
393 res = shishi_safe_build (state->safe, state->sessionkey);
394 if (res != SHISHI_OK)
395 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
396
397 res = shishi_safe_safe_der (state->safe, output, output_len);
398 if (res != SHISHI_OK)
399 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
400 }
401 else
402 {
403 *output_len = input_len;
404 *output = malloc (input_len);
405 if (!*output)
406 return GSASL_MALLOC_ERROR;
407 memcpy (*output, input, input_len);
408 }
409
410 return GSASL_OK;
411 }
412
413 int
_gsasl_kerberos_v5_client_decode(Gsasl_session * sctx,void * mech_data,const char * input,size_t input_len,char * output,size_t * output_len)414 _gsasl_kerberos_v5_client_decode (Gsasl_session * sctx,
415 void *mech_data,
416 const char *input,
417 size_t input_len,
418 char *output, size_t *output_len)
419 {
420 struct _Gsasl_kerberos_v5_client_state *state = mech_data;
421
422 if (state && state->sessionkey && state->clientqop & GSASL_QOP_AUTH_CONF)
423 {
424 return GSASL_INTEGRITY_ERROR;
425 }
426 else if (state && state->sessionkey
427 && state->clientqop & GSASL_QOP_AUTH_INT)
428 {
429 return GSASL_INTEGRITY_ERROR;
430 }
431 else
432 {
433 *output_len = input_len;
434 *output = malloc (input_len);
435 if (!*output)
436 return GSASL_MALLOC_ERROR;
437 memcpy (*output, input, input_len);
438 }
439
440 return GSASL_OK;
441 }
442
443 int
_gsasl_kerberos_v5_client_finish(Gsasl_session * sctx,void * mech_data)444 _gsasl_kerberos_v5_client_finish (Gsasl_session * sctx, void *mech_data)
445 {
446 struct _Gsasl_kerberos_v5_client_state *state = mech_data;
447
448 shishi_done (state->sh);
449 free (state);
450
451 return GSASL_OK;
452 }
453