Update gnulib files.
[gsasl.git] / lib / kerberos_v5 / kerberos_v5.c
blobacee244a3203b2f6b1380aa6350103efed7ba187
1 /* kerberos_v5.c --- Implementation of experimental SASL mechanism KERBEROS_V5.
2 * Copyright (C) 2003, 2004 Simon Josefsson
4 * This file is part of GNU SASL Library.
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.
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.
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.
21 * NB! Shishi is licensed under GPL, so linking GSASL with it require
22 * that you follow the GPL for GSASL as well.
26 #include "kerberos_v5.h"
28 /* Get Shishi API. */
29 #include <shishi.h>
31 #ifdef HAVE_NETINET_IN_H
32 #include <netinet/in.h> /* ntohl */
33 #endif
35 #define DEBUG 0
37 #define BITMAP_LEN 1
38 #define MAXBUF_LEN 4
39 #define RANDOM_LEN 16
40 #define MUTUAL (1 << 3)
42 #define SERVER_HELLO_LEN BITMAP_LEN + MAXBUF_LEN + RANDOM_LEN
43 #define CLIENT_HELLO_LEN BITMAP_LEN + MAXBUF_LEN
45 #define MAXBUF_DEFAULT 65536
47 /* Client */
49 #ifdef USE_CLIENT
51 struct _Gsasl_kerberos_v5_client_state
53 int step;
54 char serverhello[BITMAP_LEN + MAXBUF_LEN + RANDOM_LEN];
55 int serverqops;
56 int clientqop;
57 int servermutual;
58 uint32_t servermaxbuf;
59 uint32_t clientmaxbuf;
60 Shishi *sh;
61 Shishi_tkt *tkt;
62 Shishi_as *as;
63 Shishi_ap *ap;
64 Shishi_key *sessionkey;
65 Shishi_safe *safe;
68 int
69 _gsasl_kerberos_v5_client_init (Gsasl_ctx * ctx)
71 if (!shishi_check_version (SHISHI_VERSION))
72 return GSASL_UNKNOWN_MECHANISM;
74 return GSASL_OK;
77 int
78 _gsasl_kerberos_v5_client_start (Gsasl_session * sctx, void **mech_data)
80 struct _Gsasl_kerberos_v5_client_state *state;
81 Gsasl_ctx *ctx;
82 int err;
84 state = malloc (sizeof (*state));
85 if (state == NULL)
86 return GSASL_MALLOC_ERROR;
88 memset (state, 0, sizeof (*state));
90 err = shishi_init (&state->sh);
91 if (err)
92 return GSASL_KERBEROS_V5_INIT_ERROR;
94 state->step = 0;
95 state->clientqop = GSASL_QOP_AUTH_INT;
97 *mech_data = state;
99 return GSASL_OK;
102 #define STEP_FIRST 0
103 #define STEP_NONINFRA_SEND_ASREQ 1
104 #define STEP_NONINFRA_WAIT_ASREP 2
105 #define STEP_NONINFRA_SEND_APREQ 3
106 #define STEP_NONINFRA_WAIT_APREP 4
107 #define STEP_SUCCESS 5
110 _gsasl_kerberos_v5_client_step (Gsasl_session * sctx,
111 void *mech_data,
112 const char *input,
113 size_t input_len,
114 char *output, size_t * output_len)
116 struct _Gsasl_kerberos_v5_client_state *state = mech_data;
117 Gsasl_client_callback_authentication_id cb_authentication_id;
118 Gsasl_client_callback_authorization_id cb_authorization_id;
119 Gsasl_client_callback_qop cb_qop;
120 Gsasl_client_callback_realm cb_realm;
121 Gsasl_client_callback_password cb_password;
122 Gsasl_client_callback_service cb_service;
123 Gsasl_client_callback_maxbuf cb_maxbuf;
124 Gsasl_ctx *ctx;
125 int res;
126 int len;
128 ctx = gsasl_client_ctx_get (sctx);
129 if (ctx == NULL)
130 return GSASL_CANNOT_GET_CTX;
132 /* These are optional */
133 cb_realm = gsasl_client_callback_realm_get (ctx);
134 cb_service = gsasl_client_callback_service_get (ctx);
135 cb_authentication_id = gsasl_client_callback_authentication_id_get (ctx);
136 cb_authorization_id = gsasl_client_callback_authorization_id_get (ctx);
137 cb_qop = gsasl_client_callback_qop_get (ctx);
138 cb_maxbuf = gsasl_client_callback_maxbuf_get (ctx);
140 /* Only optionally needed in infrastructure mode */
141 cb_password = gsasl_client_callback_password_get (ctx);
142 if (cb_password == NULL)
143 return GSASL_NEED_CLIENT_PASSWORD_CALLBACK;
145 /* I think we really need this one */
146 cb_service = gsasl_client_callback_service_get (ctx);
147 if (cb_service == NULL)
148 return GSASL_NEED_CLIENT_SERVICE_CALLBACK;
150 switch (state->step)
152 case STEP_FIRST:
153 if (input == NULL)
155 *output_len = 0;
156 return GSASL_NEEDS_MORE;
159 if (input_len != SERVER_HELLO_LEN)
160 return GSASL_MECHANISM_PARSE_ERROR;
162 memcpy (state->serverhello, input, input_len);
165 unsigned char serverbitmap;
167 memcpy (&serverbitmap, input, BITMAP_LEN);
168 state->serverqops = 0;
169 if (serverbitmap & GSASL_QOP_AUTH)
170 state->serverqops |= GSASL_QOP_AUTH;
171 if (serverbitmap & GSASL_QOP_AUTH_INT)
172 state->serverqops |= GSASL_QOP_AUTH_INT;
173 if (serverbitmap & GSASL_QOP_AUTH_CONF)
174 state->serverqops |= GSASL_QOP_AUTH_CONF;
175 if (serverbitmap & MUTUAL)
176 state->servermutual = 1;
178 memcpy (&state->servermaxbuf, &input[BITMAP_LEN], MAXBUF_LEN);
179 state->servermaxbuf = ntohl (state->servermaxbuf);
181 if (cb_qop)
182 state->clientqop = cb_qop (sctx, state->serverqops);
184 if (!(state->serverqops & state->clientqop &
185 (GSASL_QOP_AUTH | GSASL_QOP_AUTH_INT | GSASL_QOP_AUTH_CONF)))
186 return GSASL_AUTHENTICATION_ERROR;
188 /* XXX for now we require server authentication */
189 if (!state->servermutual)
190 return GSASL_AUTHENTICATION_ERROR;
192 /* Decide policy here: non-infrastructure, infrastructure or proxy.
194 * A callback to decide should be added, but without the default
195 * should be:
197 * IF shishi_tktset_get_for_server() THEN
198 * INFRASTRUCTURE MODE
199 * ELSE IF shishi_realm_for_server(server) THEN
200 * PROXY INFRASTRUCTURE (then fallback to NIM?)
201 * ELSE
202 * NON-INFRASTRUCTURE MODE
204 state->step = STEP_NONINFRA_SEND_APREQ; /* only NIM for now.. */
205 /* fall through */
207 case STEP_NONINFRA_SEND_ASREQ:
208 res = shishi_as (state->sh, &state->as);
209 if (res)
210 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
212 if (cb_authentication_id) /* Shishi defaults to one otherwise */
214 len = *output_len - 1;
215 res = cb_authentication_id (sctx, output, &len);
216 if (res != GSASL_OK)
217 return res;
218 output[len] = '\0';
220 res = shishi_kdcreq_set_cname (state->sh, shishi_as_req (state->as),
221 SHISHI_NT_UNKNOWN, output);
222 if (res != GSASL_OK)
223 return res;
226 if (cb_realm)
228 len = *output_len - 1;
229 res = cb_realm (sctx, output, &len);
230 if (res != GSASL_OK)
231 return res;
233 else
234 len = 0;
236 output[len] = '\0';
237 res = shishi_kdcreq_set_realm (state->sh, shishi_as_req (state->as),
238 output);
239 if (res != GSASL_OK)
240 return res;
242 if (cb_service)
244 char *sname[3];
245 size_t servicelen = 0;
246 size_t hostnamelen = 0;
248 res = cb_service (sctx, NULL, &servicelen, NULL, &hostnamelen,
249 /* XXX support servicename a'la DIGEST-MD5 too? */
250 NULL, NULL);
251 if (res != GSASL_OK)
252 return res;
254 if (*output_len < servicelen + 1 + hostnamelen + 1)
255 return GSASL_TOO_SMALL_BUFFER;
257 sname[0] = &output[0];
258 sname[1] = &output[servicelen + 2];
259 sname[2] = NULL;
261 res = cb_service (sctx, sname[0], &servicelen,
262 sname[1], &hostnamelen, NULL, NULL);
263 if (res != GSASL_OK)
264 return res;
266 sname[0][servicelen] = '\0';
267 sname[1][hostnamelen] = '\0';
269 res = shishi_kdcreq_set_sname (state->sh, shishi_as_req (state->as),
270 SHISHI_NT_UNKNOWN, sname);
271 if (res != GSASL_OK)
272 return res;
275 /* XXX query application for encryption types and set the etype
276 field? Already configured by shishi though... */
278 res = shishi_a2d (state->sh, shishi_as_req (state->as),
279 output, output_len);
280 if (res != SHISHI_OK)
281 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
283 state->step = STEP_NONINFRA_WAIT_ASREP;
285 res = GSASL_NEEDS_MORE;
286 break;
288 case STEP_NONINFRA_WAIT_ASREP:
289 if (shishi_as_rep_der_set (state->as, input, input_len) != SHISHI_OK)
290 return GSASL_MECHANISM_PARSE_ERROR;
292 /* XXX? password stored in callee's output buffer */
293 len = *output_len - 1;
294 res = cb_password (sctx, output, &len);
295 if (res != GSASL_OK && res != GSASL_NEEDS_MORE)
296 return res;
297 output[len] = '\0';
299 res = shishi_as_rep_process (state->as, NULL, output);
300 if (res != SHISHI_OK)
301 return GSASL_AUTHENTICATION_ERROR;
303 state->step = STEP_NONINFRA_SEND_APREQ;
304 /* fall through */
306 case STEP_NONINFRA_SEND_APREQ:
307 if (*output_len <= CLIENT_HELLO_LEN + SERVER_HELLO_LEN)
308 return GSASL_TOO_SMALL_BUFFER;
310 if (!(state->clientqop & ~GSASL_QOP_AUTH))
311 state->clientmaxbuf = 0;
312 else if (cb_maxbuf)
313 state->clientmaxbuf = cb_maxbuf (sctx, state->servermaxbuf);
314 else
315 state->clientmaxbuf = MAXBUF_DEFAULT;
317 /* XXX for now we require server authentication */
318 output[0] = state->clientqop | MUTUAL;
320 uint32_t tmp;
322 tmp = ntohl (state->clientmaxbuf);
323 memcpy (&output[BITMAP_LEN], &tmp, MAXBUF_LEN);
325 memcpy (&output[CLIENT_HELLO_LEN], state->serverhello,
326 SERVER_HELLO_LEN);
328 if (cb_authorization_id)
330 len = *output_len - CLIENT_HELLO_LEN + SERVER_HELLO_LEN;
331 res = cb_authorization_id (sctx, &output[CLIENT_HELLO_LEN +
332 SERVER_HELLO_LEN], &len);
334 else
335 len = 0;
337 len += CLIENT_HELLO_LEN + SERVER_HELLO_LEN;
338 res = shishi_ap_tktoptionsdata (state->sh,
339 &state->ap,
340 shishi_as_tkt (state->as),
341 SHISHI_APOPTIONS_MUTUAL_REQUIRED,
342 output, len);
343 if (res != SHISHI_OK)
344 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
346 res = shishi_authenticator_add_authorizationdata
347 (state->sh, shishi_ap_authenticator (state->ap), -1, output, len);
348 if (res != SHISHI_OK)
349 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
351 /* XXX set realm in AP-REQ and Authenticator */
353 res = shishi_ap_req_der (state->ap, output, output_len);
354 if (res != SHISHI_OK)
355 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
357 state->step = STEP_NONINFRA_WAIT_APREP;
359 res = GSASL_NEEDS_MORE;
360 break;
362 case STEP_NONINFRA_WAIT_APREP:
363 if (shishi_ap_rep_der_set (state->ap, input, input_len) != SHISHI_OK)
364 return GSASL_MECHANISM_PARSE_ERROR;
366 res = shishi_ap_rep_verify (state->ap);
367 if (res != SHISHI_OK)
368 return GSASL_AUTHENTICATION_ERROR;
370 state->step = STEP_SUCCESS;
372 /* XXX support AP session keys */
373 state->sessionkey = shishi_tkt_key (shishi_as_tkt (state->as));
375 *output_len = 0;
376 res = GSASL_OK;
377 break;
379 default:
380 res = GSASL_MECHANISM_CALLED_TOO_MANY_TIMES;
381 break;
384 return res;
388 _gsasl_kerberos_v5_client_encode (Gsasl_session * sctx,
389 void *mech_data,
390 const char *input,
391 size_t input_len,
392 char *output, size_t * output_len)
394 struct _Gsasl_kerberos_v5_client_state *state = mech_data;
395 int res;
397 if (state && state->sessionkey && state->clientqop & GSASL_QOP_AUTH_CONF)
399 /* XXX */
401 else if (state && state->sessionkey
402 && state->clientqop & GSASL_QOP_AUTH_INT)
404 res = shishi_safe (state->sh, &state->safe);
405 if (res != SHISHI_OK)
406 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
408 res = shishi_safe_set_user_data (state->sh,
409 shishi_safe_safe (state->safe),
410 input, input_len);
411 if (res != SHISHI_OK)
412 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
414 res = shishi_safe_build (state->safe, state->sessionkey);
415 if (res != SHISHI_OK)
416 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
418 res = shishi_safe_safe_der (state->safe, output, output_len);
419 if (res != SHISHI_OK)
420 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
422 else
424 *output_len = input_len;
425 if (output)
426 memcpy (output, input, input_len);
427 return GSASL_OK;
430 return GSASL_OK;
434 _gsasl_kerberos_v5_client_decode (Gsasl_session * sctx,
435 void *mech_data,
436 const char *input,
437 size_t input_len,
438 char *output, size_t * output_len)
440 struct _Gsasl_kerberos_v5_client_state *state = mech_data;
442 puts ("cdecode");
444 if (state && state->sessionkey && state->clientqop & GSASL_QOP_AUTH_CONF)
446 /* XXX */
448 else if (state && state->sessionkey
449 && state->clientqop & GSASL_QOP_AUTH_INT)
451 puts ("decode");
453 else
455 *output_len = input_len;
456 if (output)
457 memcpy (output, input, input_len);
458 return GSASL_OK;
461 return GSASL_OK;
465 _gsasl_kerberos_v5_client_finish (Gsasl_session * sctx, void *mech_data)
467 struct _Gsasl_kerberos_v5_client_state *state = mech_data;
469 shishi_done (state->sh);
470 free (state);
472 return GSASL_OK;
475 #endif /* USE_CLIENT */
477 /* Server */
479 #ifdef USE_SERVER
481 struct _Gsasl_kerberos_v5_server_state
483 int firststep;
484 Shishi *sh;
485 char serverhello[BITMAP_LEN + MAXBUF_LEN + RANDOM_LEN];
486 char *random;
487 int serverqops;
488 uint32_t servermaxbuf;
489 int clientqop;
490 int clientmutual;
491 uint32_t clientmaxbuf;
492 char *username;
493 char *userrealm;
494 char *serverrealm;
495 char *serverservice;
496 char *serverhostname;
497 char *password;
498 Shishi_key *userkey; /* user's key derived with string2key */
499 Shishi_key *sessionkey; /* shared between client and server */
500 Shishi_key *sessiontktkey; /* known only by server */
501 Shishi_ap *ap;
502 Shishi_as *as;
503 Shishi_safe *safe;
507 _gsasl_kerberos_v5_server_init (Gsasl_ctx * ctx)
509 if (!shishi_check_version (SHISHI_VERSION))
510 return GSASL_UNKNOWN_MECHANISM;
512 return GSASL_OK;
516 _gsasl_kerberos_v5_server_start (Gsasl_session * sctx, void **mech_data)
518 struct _Gsasl_kerberos_v5_server_state *state;
519 int err;
521 state = malloc (sizeof (*state));
522 if (state == NULL)
523 return GSASL_MALLOC_ERROR;
524 memset (state, 0, sizeof (*state));
526 state->random = (char *) malloc (RANDOM_LEN);
527 if (state->random == NULL)
528 return GSASL_MALLOC_ERROR;
530 err = shishi_init_server (&state->sh);
531 if (err)
532 return GSASL_KERBEROS_V5_INIT_ERROR;
534 err = shishi_randomize (state->sh, state->random, RANDOM_LEN);
535 if (err)
536 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
538 /* This can be pretty much anything, the client will never have it. */
539 err = shishi_key_random (state->sh, SHISHI_AES256_CTS_HMAC_SHA1_96,
540 &state->sessiontktkey);
541 if (err)
542 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
544 err = shishi_as (state->sh, &state->as);
545 if (err)
546 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
548 state->firststep = 1;
549 state->serverqops = GSASL_QOP_AUTH | GSASL_QOP_AUTH_INT;
551 *mech_data = state;
553 return GSASL_OK;
557 _gsasl_kerberos_v5_server_step (Gsasl_session * sctx,
558 void *mech_data,
559 const char *input,
560 size_t input_len,
561 char *output, size_t * output_len)
563 struct _Gsasl_kerberos_v5_server_state *state = mech_data;
564 Gsasl_server_callback_realm cb_realm;
565 Gsasl_server_callback_qop cb_qop;
566 Gsasl_server_callback_maxbuf cb_maxbuf;
567 Gsasl_server_callback_cipher cb_cipher;
568 Gsasl_server_callback_retrieve cb_retrieve;
569 Gsasl_server_callback_service cb_service;
570 unsigned char buf[BUFSIZ];
571 size_t buflen;
572 Gsasl_ctx *ctx;
573 ASN1_TYPE asn1;
574 int err;
576 ctx = gsasl_server_ctx_get (sctx);
577 if (ctx == NULL)
578 return GSASL_CANNOT_GET_CTX;
580 cb_realm = gsasl_server_callback_realm_get (ctx);
581 cb_qop = gsasl_server_callback_qop_get (ctx);
582 cb_maxbuf = gsasl_server_callback_maxbuf_get (ctx);
583 cb_retrieve = gsasl_server_callback_retrieve_get (ctx);
584 cb_service = gsasl_server_callback_service_get (ctx);
585 if (cb_service == NULL)
586 return GSASL_NEED_SERVER_SERVICE_CALLBACK;
588 if (state->firststep)
590 uint32_t tmp;
591 unsigned char *p;
594 * The initial server packet should contain one octet containing
595 * a bit mask of supported security layers, four octets
596 * indicating the maximum cipher-text buffer size the server is
597 * able to receive (or 0 if no security layers are supported) in
598 * network byte order, and then 16 octets containing random data
599 * (see [4] on how random data might be generated).
601 * The security layers and their corresponding bit-masks are as
602 * follows:
604 * Bit 0 No security layer
605 * Bit 1 Integrity (KRB-SAFE) protection
606 * Bit 2 Privacy (KRB-PRIV) protection
607 * Bit 3 Mutual authentication is required (AP option MUTUAL-
608 * REQUIRED must also be present).
610 * Other bit-masks may be defined in the future; bits which are
611 * not understood must be negotiated off.
614 if (output && *output_len < BITMAP_LEN + MAXBUF_LEN + RANDOM_LEN)
615 return GSASL_TOO_SMALL_BUFFER;
617 p = &state->serverhello[0];
619 if (cb_qop)
620 state->serverqops = cb_qop (sctx);
621 *p = 0;
622 if (state->serverqops & GSASL_QOP_AUTH)
623 *p |= GSASL_QOP_AUTH;
624 if (state->serverqops & GSASL_QOP_AUTH_INT)
625 *p |= GSASL_QOP_AUTH_INT;
626 if (state->serverqops & GSASL_QOP_AUTH_CONF)
627 *p |= GSASL_QOP_AUTH_CONF;
628 /* XXX we always require mutual authentication for now */
629 *p |= MUTUAL;
631 if (!(state->serverqops & ~GSASL_QOP_AUTH))
632 state->servermaxbuf = 0;
633 else if (cb_maxbuf)
634 state->servermaxbuf = cb_maxbuf (sctx);
635 else
636 state->servermaxbuf = MAXBUF_DEFAULT;
638 tmp = htonl (state->servermaxbuf);
639 memcpy (&state->serverhello[BITMAP_LEN], &tmp, MAXBUF_LEN);
640 memcpy (&state->serverhello[BITMAP_LEN + MAXBUF_LEN],
641 state->random, RANDOM_LEN);
643 if (output)
644 memcpy (output, state->serverhello, SERVER_HELLO_LEN);
645 *output_len = BITMAP_LEN + MAXBUF_LEN + RANDOM_LEN;
647 state->firststep = 0;
649 return GSASL_NEEDS_MORE;
652 if (cb_retrieve)
654 /* Non-infrastructure mode */
656 if (*output_len < 2048)
657 return GSASL_TOO_SMALL_BUFFER;
659 if (shishi_as_req_der_set (state->as, input, input_len) == SHISHI_OK)
661 Shishi_tkt *tkt;
662 int etype, i;
664 tkt = shishi_as_tkt (state->as);
665 if (!tkt)
666 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
668 i = 1;
671 err = shishi_kdcreq_etype (state->sh,
672 shishi_as_req (state->as),
673 &etype, i);
674 if (err == SHISHI_OK && shishi_cipher_supported_p (etype))
675 break;
677 while (err == SHISHI_OK);
678 if (err != SHISHI_OK)
679 return err;
681 /* XXX use a "preferred server kdc etype" from shishi instead? */
682 err = shishi_key_random (state->sh, etype, &state->sessionkey);
683 if (err)
684 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
686 err = shishi_tkt_key_set (tkt, state->sessionkey);
687 if (err)
688 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
690 buflen = sizeof (buf) - 1;
691 err = shishi_kdcreq_cname_get (state->sh,
692 shishi_as_req (state->as),
693 buf, &buflen);
694 if (err != SHISHI_OK)
695 return err;
696 buf[buflen] = '\0';
697 state->username = strdup (buf);
699 buflen = sizeof (buf) - 1;
700 err = shishi_kdcreq_realm_get (state->sh,
701 shishi_as_req (state->as),
702 buf, &buflen);
703 if (err != SHISHI_OK)
704 return err;
705 buf[buflen] = '\0';
706 state->userrealm = strdup (buf);
708 buflen = sizeof (buf) - 1;
709 err = cb_retrieve (sctx, state->username, NULL, state->userrealm,
710 NULL, &buflen);
711 if (err != GSASL_OK)
712 return err;
714 state->password = malloc (buflen + 1);
715 if (state->password == NULL)
716 return GSASL_MALLOC_ERROR;
718 err = cb_retrieve (sctx, state->username, NULL, state->userrealm,
719 state->password, &buflen);
720 if (err != GSASL_OK)
721 return err;
722 state->password[buflen] = '\0';
724 buflen = sizeof (buf) - 1;
725 if (cb_realm)
727 err = cb_realm (sctx, buf, &buflen, 0);
728 if (err != GSASL_OK)
729 return err;
731 else
732 buflen = 0;
733 buf[buflen] = '\0';
734 state->serverrealm = strdup (buf);
736 buflen = sizeof (buf) - 1;
737 err = cb_service (sctx, buf, &buflen, NULL, NULL);
738 if (err != GSASL_OK)
739 return err;
740 buf[buflen] = '\0';
741 state->serverservice = strdup (buf);
743 buflen = sizeof (buf) - 1;
744 err = cb_service (sctx, NULL, NULL, buf, &buflen);
745 if (err != GSASL_OK)
746 return err;
747 buf[buflen] = '\0';
748 state->serverhostname = strdup (buf);
750 /* XXX do some checking on realm and server name? Right now
751 we simply doesn't care about what client requested and
752 return a ticket for this server. This is bad. */
754 err = shishi_tkt_clientrealm_set (tkt, state->userrealm,
755 state->username);
756 if (err)
757 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
760 char *p;
761 p = malloc (strlen (state->serverservice) + strlen ("/") +
762 strlen (state->serverhostname) + 1);
763 if (p == NULL)
764 return GSASL_MALLOC_ERROR;
765 sprintf (p, "%s/%s", state->serverservice, state->serverhostname);
766 err = shishi_tkt_serverrealm_set (tkt, state->serverrealm, p);
767 free (p);
768 if (err)
769 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
772 buflen = sizeof (buf);
773 err = shishi_as_derive_salt (state->sh,
774 shishi_as_req (state->as),
775 shishi_as_rep (state->as),
776 buf, &buflen);
777 if (err != SHISHI_OK)
778 return err;
780 err = shishi_key_from_string (state->sh,
781 etype,
782 state->password,
783 strlen (state->password),
784 buf, buflen, NULL, &state->userkey);
785 if (err != SHISHI_OK)
786 return err;
788 err = shishi_tkt_build (tkt, state->sessiontktkey);
789 if (err)
790 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
792 err = shishi_as_rep_build (state->as, state->userkey);
793 if (err)
794 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
796 #if DEBUG
797 shishi_kdcreq_print (state->sh, stderr, shishi_as_req (state->as));
798 shishi_encticketpart_print (state->sh, stderr,
799 shishi_tkt_encticketpart (tkt));
800 shishi_ticket_print (state->sh, stderr, shishi_tkt_ticket (tkt));
801 shishi_enckdcreppart_print (state->sh, stderr,
802 shishi_tkt_enckdcreppart (state->as));
803 shishi_kdcrep_print (state->sh, stderr, shishi_as_rep (state->as));
804 #endif
806 err = shishi_as_rep_der (state->as, output, output_len);
807 if (err)
808 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
810 return GSASL_NEEDS_MORE;
812 else if ((asn1 = shishi_der2asn1_apreq (state->sh, input, input_len)))
814 int adtype;
816 err = shishi_ap (state->sh, &state->ap);
817 if (err)
818 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
820 shishi_ap_req_set (state->ap, asn1);
822 err = shishi_ap_req_process (state->ap, state->sessiontktkey);
823 if (err)
824 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
826 #if DEBUG
827 shishi_apreq_print (state->sh, stderr, shishi_ap_req (state->ap));
828 shishi_ticket_print (state->sh, stderr,
829 shishi_tkt_ticket (shishi_ap_tkt (state->ap)));
830 shishi_authenticator_print (state->sh, stderr,
831 shishi_ap_authenticator (state->ap));
832 #endif
834 buflen = sizeof (buf);
835 err = shishi_authenticator_authorizationdata
836 (state->sh, shishi_ap_authenticator (state->ap),
837 &adtype, buf, &buflen, 1);
838 if (err)
839 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
841 if (adtype != 0xFF /* -1 in one-complements form */ ||
842 buflen < CLIENT_HELLO_LEN + SERVER_HELLO_LEN)
843 return GSASL_AUTHENTICATION_ERROR;
846 unsigned char clientbitmap;
848 memcpy (&clientbitmap, &buf[0], BITMAP_LEN);
849 state->clientqop = 0;
850 if (clientbitmap & GSASL_QOP_AUTH)
851 state->clientqop |= GSASL_QOP_AUTH;
852 if (clientbitmap & GSASL_QOP_AUTH_INT)
853 state->clientqop |= GSASL_QOP_AUTH_INT;
854 if (clientbitmap & GSASL_QOP_AUTH_CONF)
855 state->clientqop |= GSASL_QOP_AUTH_CONF;
856 if (clientbitmap & MUTUAL)
857 state->clientmutual = 1;
859 memcpy (&state->clientmaxbuf, &input[BITMAP_LEN], MAXBUF_LEN);
860 state->clientmaxbuf = ntohl (state->clientmaxbuf);
862 if (!(state->clientqop & state->serverqops))
863 return GSASL_AUTHENTICATION_ERROR;
865 /* XXX check clientmaxbuf too */
867 if (memcmp (&buf[CLIENT_HELLO_LEN],
868 state->serverhello, SERVER_HELLO_LEN) != 0)
869 return GSASL_AUTHENTICATION_ERROR;
872 char cksum[BUFSIZ];
873 int cksumlen;
874 int cksumtype;
875 Shishi_key *key;
877 key = shishi_tkt_key (shishi_as_tkt (state->as));
878 cksumtype =
879 shishi_cipher_defaultcksumtype (shishi_key_type (key));
880 cksumlen = sizeof (cksum);
881 err = shishi_checksum (state->sh, key,
882 SHISHI_KEYUSAGE_APREQ_AUTHENTICATOR_CKSUM,
883 cksumtype, buf, buflen, cksum, &cksumlen);
884 if (err != SHISHI_OK)
885 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
887 buflen = sizeof (buf);
888 err = shishi_authenticator_cksum
889 (state->sh,
890 shishi_ap_authenticator (state->ap), &cksumtype, buf, &buflen);
891 if (err != SHISHI_OK)
892 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
894 if (buflen != cksumlen || memcmp (buf, cksum, buflen) != 0)
895 return GSASL_AUTHENTICATION_ERROR;
898 /* XXX use authorization_id */
900 if (state->clientmutual)
902 err = shishi_ap_rep_build (state->ap);
903 if (err)
904 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
906 err = shishi_ap_rep_der (state->ap, output, output_len);
907 if (err)
908 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
910 else
911 *output_len = 0;
913 return GSASL_OK;
916 else
918 /* XXX Currently we only handle AS-REQ and AP-REQ in
919 non-infrastructure mode. Supporting infrastructure mode is
920 simple, just send the AS-REQ to the KDC and wait for AS-REP
921 instead of creating AS-REP locally.
923 We should probably have a callback to decide policy:
924 1) non-infrastructure mode (NIM) only
925 2) infrastructure mode (IM) only
926 3) proxied infrastructure mode (PIM) only
927 4) NIM with fallback to IM (useful for local server overrides)
928 5) IM with fallback to NIM (useful for admins if KDC is offline)
929 6) ...etc with PIM too
931 return GSASL_NEED_SERVER_RETRIEVE_CALLBACK;
934 *output_len = 0;
935 return GSASL_NEEDS_MORE;
939 _gsasl_kerberos_v5_server_encode (Gsasl_session * sctx,
940 void *mech_data,
941 const char *input,
942 size_t input_len,
943 char *output, size_t * output_len)
945 struct _Gsasl_kerberos_v5_server_state *state = mech_data;
946 int res;
948 if (state && state->sessionkey && state->clientqop & GSASL_QOP_AUTH_CONF)
950 /* XXX */
952 else if (state && state->sessionkey
953 && state->clientqop & GSASL_QOP_AUTH_INT)
955 res = shishi_safe (state->sh, &state->safe);
956 if (res != SHISHI_OK)
957 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
959 res = shishi_safe_set_user_data (state->sh,
960 shishi_safe_safe (state->safe),
961 input, input_len);
962 if (res != SHISHI_OK)
963 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
965 res = shishi_safe_build (state->safe, state->sessionkey);
966 if (res != SHISHI_OK)
967 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
969 res = shishi_safe_safe_der (state->safe, output, output_len);
970 if (res != SHISHI_OK)
971 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
973 else
975 *output_len = input_len;
976 if (output)
977 memcpy (output, input, input_len);
978 return GSASL_OK;
981 return GSASL_OK;
985 _gsasl_kerberos_v5_server_decode (Gsasl_session * sctx,
986 void *mech_data,
987 const char *input,
988 size_t input_len,
989 char *output, size_t * output_len)
991 struct _Gsasl_kerberos_v5_server_state *state = mech_data;
992 int res;
994 if (state && state->sessionkey && state->clientqop & GSASL_QOP_AUTH_CONF)
996 /* XXX */
998 else if (state && state->sessionkey
999 && state->clientqop & GSASL_QOP_AUTH_INT)
1001 Shishi_asn1 asn1safe;
1003 res = shishi_safe (state->sh, &state->safe);
1004 if (res != SHISHI_OK)
1005 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
1007 res = shishi_safe_safe_der_set (state->safe, input, input_len);
1008 printf ("len %d err %d\n", input_len, res);
1009 if (res != SHISHI_OK)
1010 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
1012 res = shishi_safe_verify (state->safe, state->sessionkey);
1013 if (res != SHISHI_OK)
1014 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
1016 res = shishi_safe_user_data (state->sh, shishi_safe_safe (state->safe),
1017 output, output_len);
1018 if (res != SHISHI_OK)
1019 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
1020 printf ("len=%d\n", *output_len);
1021 return GSASL_OK;
1023 else
1025 *output_len = input_len;
1026 if (output)
1027 memcpy (output, input, input_len);
1028 return GSASL_OK;
1032 return GSASL_OK;
1036 _gsasl_kerberos_v5_server_finish (Gsasl_session * sctx, void *mech_data)
1038 struct _Gsasl_kerberos_v5_server_state *state = mech_data;
1040 shishi_done (state->sh);
1041 if (state->username)
1042 free (state->username);
1043 if (state->password)
1044 free (state->password);
1045 if (state->random)
1046 free (state->random);
1047 free (state);
1049 return GSASL_OK;
1051 #endif /* USE_SERVER */