Add.
[gsasl.git] / lib / kerberos_v5 / server.c
blobd130ed4cb56fe9f747716ced1cc01fdcdd9673d1
1 /* server.c --- Experimental SASL mechanism KERBEROS_V5, server side.
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 #include "shared.h"
30 struct _Gsasl_kerberos_v5_server_state
32 int firststep;
33 Shishi *sh;
34 char serverhello[BITMAP_LEN + MAXBUF_LEN + RANDOM_LEN];
35 char *random;
36 int serverqops;
37 uint32_t servermaxbuf;
38 int clientqop;
39 int clientmutual;
40 uint32_t clientmaxbuf;
41 char *username;
42 char *userrealm;
43 char *serverrealm;
44 char *serverservice;
45 char *serverhostname;
46 char *password;
47 Shishi_key *userkey; /* user's key derived with string2key */
48 Shishi_key *sessionkey; /* shared between client and server */
49 Shishi_key *sessiontktkey; /* known only by server */
50 Shishi_ap *ap;
51 Shishi_as *as;
52 Shishi_safe *safe;
55 int
56 _gsasl_kerberos_v5_server_init (Gsasl_ctx * ctx)
58 if (!shishi_check_version (SHISHI_VERSION))
59 return GSASL_UNKNOWN_MECHANISM;
61 return GSASL_OK;
64 int
65 _gsasl_kerberos_v5_server_start (Gsasl_session * sctx, void **mech_data)
67 struct _Gsasl_kerberos_v5_server_state *state;
68 int err;
70 state = malloc (sizeof (*state));
71 if (state == NULL)
72 return GSASL_MALLOC_ERROR;
73 memset (state, 0, sizeof (*state));
75 state->random = (char *) malloc (RANDOM_LEN);
76 if (state->random == NULL)
77 return GSASL_MALLOC_ERROR;
79 err = shishi_init_server (&state->sh);
80 if (err)
81 return GSASL_KERBEROS_V5_INIT_ERROR;
83 err = shishi_randomize (state->sh, state->random, RANDOM_LEN);
84 if (err)
85 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
87 /* This can be pretty much anything, the client will never have it. */
88 err = shishi_key_random (state->sh, SHISHI_AES256_CTS_HMAC_SHA1_96,
89 &state->sessiontktkey);
90 if (err)
91 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
93 err = shishi_as (state->sh, &state->as);
94 if (err)
95 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
97 state->firststep = 1;
98 state->serverqops = GSASL_QOP_AUTH | GSASL_QOP_AUTH_INT;
100 *mech_data = state;
102 return GSASL_OK;
106 _gsasl_kerberos_v5_server_step (Gsasl_session * sctx,
107 void *mech_data,
108 const char *input,
109 size_t input_len,
110 char *output, size_t * output_len)
112 struct _Gsasl_kerberos_v5_server_state *state = mech_data;
113 Gsasl_server_callback_realm cb_realm;
114 Gsasl_server_callback_qop cb_qop;
115 Gsasl_server_callback_maxbuf cb_maxbuf;
116 Gsasl_server_callback_cipher cb_cipher;
117 Gsasl_server_callback_retrieve cb_retrieve;
118 Gsasl_server_callback_service cb_service;
119 unsigned char buf[BUFSIZ];
120 size_t buflen;
121 Gsasl_ctx *ctx;
122 ASN1_TYPE asn1;
123 int err;
125 ctx = gsasl_server_ctx_get (sctx);
126 if (ctx == NULL)
127 return GSASL_CANNOT_GET_CTX;
129 cb_realm = gsasl_server_callback_realm_get (ctx);
130 cb_qop = gsasl_server_callback_qop_get (ctx);
131 cb_maxbuf = gsasl_server_callback_maxbuf_get (ctx);
132 cb_retrieve = gsasl_server_callback_retrieve_get (ctx);
133 cb_service = gsasl_server_callback_service_get (ctx);
134 if (cb_service == NULL)
135 return GSASL_NEED_SERVER_SERVICE_CALLBACK;
137 if (state->firststep)
139 uint32_t tmp;
140 unsigned char *p;
143 * The initial server packet should contain one octet containing
144 * a bit mask of supported security layers, four octets
145 * indicating the maximum cipher-text buffer size the server is
146 * able to receive (or 0 if no security layers are supported) in
147 * network byte order, and then 16 octets containing random data
148 * (see [4] on how random data might be generated).
150 * The security layers and their corresponding bit-masks are as
151 * follows:
153 * Bit 0 No security layer
154 * Bit 1 Integrity (KRB-SAFE) protection
155 * Bit 2 Privacy (KRB-PRIV) protection
156 * Bit 3 Mutual authentication is required (AP option MUTUAL-
157 * REQUIRED must also be present).
159 * Other bit-masks may be defined in the future; bits which are
160 * not understood must be negotiated off.
163 if (output && *output_len < BITMAP_LEN + MAXBUF_LEN + RANDOM_LEN)
164 return GSASL_TOO_SMALL_BUFFER;
166 p = &state->serverhello[0];
168 if (cb_qop)
169 state->serverqops = cb_qop (sctx);
170 *p = 0;
171 if (state->serverqops & GSASL_QOP_AUTH)
172 *p |= GSASL_QOP_AUTH;
173 if (state->serverqops & GSASL_QOP_AUTH_INT)
174 *p |= GSASL_QOP_AUTH_INT;
175 if (state->serverqops & GSASL_QOP_AUTH_CONF)
176 *p |= GSASL_QOP_AUTH_CONF;
177 /* XXX we always require mutual authentication for now */
178 *p |= MUTUAL;
180 if (!(state->serverqops & ~GSASL_QOP_AUTH))
181 state->servermaxbuf = 0;
182 else if (cb_maxbuf)
183 state->servermaxbuf = cb_maxbuf (sctx);
184 else
185 state->servermaxbuf = MAXBUF_DEFAULT;
187 tmp = htonl (state->servermaxbuf);
188 memcpy (&state->serverhello[BITMAP_LEN], &tmp, MAXBUF_LEN);
189 memcpy (&state->serverhello[BITMAP_LEN + MAXBUF_LEN],
190 state->random, RANDOM_LEN);
192 if (output)
193 memcpy (output, state->serverhello, SERVER_HELLO_LEN);
194 *output_len = BITMAP_LEN + MAXBUF_LEN + RANDOM_LEN;
196 state->firststep = 0;
198 return GSASL_NEEDS_MORE;
201 if (cb_retrieve)
203 /* Non-infrastructure mode */
205 if (*output_len < 2048)
206 return GSASL_TOO_SMALL_BUFFER;
208 if (shishi_as_req_der_set (state->as, input, input_len) == SHISHI_OK)
210 Shishi_tkt *tkt;
211 int etype, i;
213 tkt = shishi_as_tkt (state->as);
214 if (!tkt)
215 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
217 i = 1;
220 err = shishi_kdcreq_etype (state->sh,
221 shishi_as_req (state->as),
222 &etype, i);
223 if (err == SHISHI_OK && shishi_cipher_supported_p (etype))
224 break;
226 while (err == SHISHI_OK);
227 if (err != SHISHI_OK)
228 return err;
230 /* XXX use a "preferred server kdc etype" from shishi instead? */
231 err = shishi_key_random (state->sh, etype, &state->sessionkey);
232 if (err)
233 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
235 err = shishi_tkt_key_set (tkt, state->sessionkey);
236 if (err)
237 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
239 buflen = sizeof (buf) - 1;
240 err = shishi_kdcreq_cname_get (state->sh,
241 shishi_as_req (state->as),
242 buf, &buflen);
243 if (err != SHISHI_OK)
244 return err;
245 buf[buflen] = '\0';
246 state->username = strdup (buf);
248 buflen = sizeof (buf) - 1;
249 err = shishi_kdcreq_realm_get (state->sh,
250 shishi_as_req (state->as),
251 buf, &buflen);
252 if (err != SHISHI_OK)
253 return err;
254 buf[buflen] = '\0';
255 state->userrealm = strdup (buf);
257 buflen = sizeof (buf) - 1;
258 err = cb_retrieve (sctx, state->username, NULL, state->userrealm,
259 NULL, &buflen);
260 if (err != GSASL_OK)
261 return err;
263 state->password = malloc (buflen + 1);
264 if (state->password == NULL)
265 return GSASL_MALLOC_ERROR;
267 err = cb_retrieve (sctx, state->username, NULL, state->userrealm,
268 state->password, &buflen);
269 if (err != GSASL_OK)
270 return err;
271 state->password[buflen] = '\0';
273 buflen = sizeof (buf) - 1;
274 if (cb_realm)
276 err = cb_realm (sctx, buf, &buflen, 0);
277 if (err != GSASL_OK)
278 return err;
280 else
281 buflen = 0;
282 buf[buflen] = '\0';
283 state->serverrealm = strdup (buf);
285 buflen = sizeof (buf) - 1;
286 err = cb_service (sctx, buf, &buflen, NULL, NULL);
287 if (err != GSASL_OK)
288 return err;
289 buf[buflen] = '\0';
290 state->serverservice = strdup (buf);
292 buflen = sizeof (buf) - 1;
293 err = cb_service (sctx, NULL, NULL, buf, &buflen);
294 if (err != GSASL_OK)
295 return err;
296 buf[buflen] = '\0';
297 state->serverhostname = strdup (buf);
299 /* XXX do some checking on realm and server name? Right now
300 we simply doesn't care about what client requested and
301 return a ticket for this server. This is bad. */
303 err = shishi_tkt_clientrealm_set (tkt, state->userrealm,
304 state->username);
305 if (err)
306 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
309 char *p;
310 p = malloc (strlen (state->serverservice) + strlen ("/") +
311 strlen (state->serverhostname) + 1);
312 if (p == NULL)
313 return GSASL_MALLOC_ERROR;
314 sprintf (p, "%s/%s", state->serverservice, state->serverhostname);
315 err = shishi_tkt_serverrealm_set (tkt, state->serverrealm, p);
316 free (p);
317 if (err)
318 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
321 buflen = sizeof (buf);
322 err = shishi_as_derive_salt (state->sh,
323 shishi_as_req (state->as),
324 shishi_as_rep (state->as),
325 buf, &buflen);
326 if (err != SHISHI_OK)
327 return err;
329 err = shishi_key_from_string (state->sh,
330 etype,
331 state->password,
332 strlen (state->password),
333 buf, buflen, NULL, &state->userkey);
334 if (err != SHISHI_OK)
335 return err;
337 err = shishi_tkt_build (tkt, state->sessiontktkey);
338 if (err)
339 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
341 err = shishi_as_rep_build (state->as, state->userkey);
342 if (err)
343 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
345 #if DEBUG
346 shishi_kdcreq_print (state->sh, stderr, shishi_as_req (state->as));
347 shishi_encticketpart_print (state->sh, stderr,
348 shishi_tkt_encticketpart (tkt));
349 shishi_ticket_print (state->sh, stderr, shishi_tkt_ticket (tkt));
350 shishi_enckdcreppart_print (state->sh, stderr,
351 shishi_tkt_enckdcreppart (state->as));
352 shishi_kdcrep_print (state->sh, stderr, shishi_as_rep (state->as));
353 #endif
355 err = shishi_as_rep_der (state->as, output, output_len);
356 if (err)
357 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
359 return GSASL_NEEDS_MORE;
361 else if ((asn1 = shishi_der2asn1_apreq (state->sh, input, input_len)))
363 int adtype;
365 err = shishi_ap (state->sh, &state->ap);
366 if (err)
367 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
369 shishi_ap_req_set (state->ap, asn1);
371 err = shishi_ap_req_process (state->ap, state->sessiontktkey);
372 if (err)
373 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
375 #if DEBUG
376 shishi_apreq_print (state->sh, stderr, shishi_ap_req (state->ap));
377 shishi_ticket_print (state->sh, stderr,
378 shishi_tkt_ticket (shishi_ap_tkt (state->ap)));
379 shishi_authenticator_print (state->sh, stderr,
380 shishi_ap_authenticator (state->ap));
381 #endif
383 buflen = sizeof (buf);
384 err = shishi_authenticator_authorizationdata
385 (state->sh, shishi_ap_authenticator (state->ap),
386 &adtype, buf, &buflen, 1);
387 if (err)
388 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
390 if (adtype != 0xFF /* -1 in one-complements form */ ||
391 buflen < CLIENT_HELLO_LEN + SERVER_HELLO_LEN)
392 return GSASL_AUTHENTICATION_ERROR;
395 unsigned char clientbitmap;
397 memcpy (&clientbitmap, &buf[0], BITMAP_LEN);
398 state->clientqop = 0;
399 if (clientbitmap & GSASL_QOP_AUTH)
400 state->clientqop |= GSASL_QOP_AUTH;
401 if (clientbitmap & GSASL_QOP_AUTH_INT)
402 state->clientqop |= GSASL_QOP_AUTH_INT;
403 if (clientbitmap & GSASL_QOP_AUTH_CONF)
404 state->clientqop |= GSASL_QOP_AUTH_CONF;
405 if (clientbitmap & MUTUAL)
406 state->clientmutual = 1;
408 memcpy (&state->clientmaxbuf, &input[BITMAP_LEN], MAXBUF_LEN);
409 state->clientmaxbuf = ntohl (state->clientmaxbuf);
411 if (!(state->clientqop & state->serverqops))
412 return GSASL_AUTHENTICATION_ERROR;
414 /* XXX check clientmaxbuf too */
416 if (memcmp (&buf[CLIENT_HELLO_LEN],
417 state->serverhello, SERVER_HELLO_LEN) != 0)
418 return GSASL_AUTHENTICATION_ERROR;
421 char cksum[BUFSIZ];
422 int cksumlen;
423 int cksumtype;
424 Shishi_key *key;
426 key = shishi_tkt_key (shishi_as_tkt (state->as));
427 cksumtype =
428 shishi_cipher_defaultcksumtype (shishi_key_type (key));
429 cksumlen = sizeof (cksum);
430 err = shishi_checksum (state->sh, key,
431 SHISHI_KEYUSAGE_APREQ_AUTHENTICATOR_CKSUM,
432 cksumtype, buf, buflen, cksum, &cksumlen);
433 if (err != SHISHI_OK)
434 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
436 buflen = sizeof (buf);
437 err = shishi_authenticator_cksum
438 (state->sh,
439 shishi_ap_authenticator (state->ap), &cksumtype, buf, &buflen);
440 if (err != SHISHI_OK)
441 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
443 if (buflen != cksumlen || memcmp (buf, cksum, buflen) != 0)
444 return GSASL_AUTHENTICATION_ERROR;
447 /* XXX use authorization_id */
449 if (state->clientmutual)
451 err = shishi_ap_rep_build (state->ap);
452 if (err)
453 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
455 err = shishi_ap_rep_der (state->ap, output, output_len);
456 if (err)
457 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
459 else
460 *output_len = 0;
462 return GSASL_OK;
465 else
467 /* XXX Currently we only handle AS-REQ and AP-REQ in
468 non-infrastructure mode. Supporting infrastructure mode is
469 simple, just send the AS-REQ to the KDC and wait for AS-REP
470 instead of creating AS-REP locally.
472 We should probably have a callback to decide policy:
473 1) non-infrastructure mode (NIM) only
474 2) infrastructure mode (IM) only
475 3) proxied infrastructure mode (PIM) only
476 4) NIM with fallback to IM (useful for local server overrides)
477 5) IM with fallback to NIM (useful for admins if KDC is offline)
478 6) ...etc with PIM too
480 return GSASL_NEED_SERVER_RETRIEVE_CALLBACK;
483 *output_len = 0;
484 return GSASL_NEEDS_MORE;
488 _gsasl_kerberos_v5_server_encode (Gsasl_session * sctx,
489 void *mech_data,
490 const char *input,
491 size_t input_len,
492 char *output, size_t * output_len)
494 struct _Gsasl_kerberos_v5_server_state *state = mech_data;
495 int res;
497 if (state && state->sessionkey && state->clientqop & GSASL_QOP_AUTH_CONF)
499 return GSASL_INTEGRITY_ERROR;
501 else if (state && state->sessionkey
502 && state->clientqop & GSASL_QOP_AUTH_INT)
504 res = shishi_safe (state->sh, &state->safe);
505 if (res != SHISHI_OK)
506 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
508 res = shishi_safe_set_user_data (state->sh,
509 shishi_safe_safe (state->safe),
510 input, input_len);
511 if (res != SHISHI_OK)
512 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
514 res = shishi_safe_build (state->safe, state->sessionkey);
515 if (res != SHISHI_OK)
516 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
518 res = shishi_safe_safe_der (state->safe, output, output_len);
519 if (res != SHISHI_OK)
520 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
522 else
524 *output_len = input_len;
525 *output = malloc (input_len);
526 if (!*output)
527 return GSASL_MALLOC_ERROR;
528 memcpy (*output, input, input_len);
531 return GSASL_OK;
535 _gsasl_kerberos_v5_server_decode (Gsasl_session * sctx,
536 void *mech_data,
537 const char *input,
538 size_t input_len,
539 char *output, size_t * output_len)
541 struct _Gsasl_kerberos_v5_server_state *state = mech_data;
542 int res;
544 if (state && state->sessionkey && state->clientqop & GSASL_QOP_AUTH_CONF)
546 return GSASL_INTEGRITY_ERROR;
548 else if (state && state->sessionkey
549 && state->clientqop & GSASL_QOP_AUTH_INT)
551 Shishi_asn1 asn1safe;
553 res = shishi_safe (state->sh, &state->safe);
554 if (res != SHISHI_OK)
555 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
557 res = shishi_safe_safe_der_set (state->safe, input, input_len);
558 if (res != SHISHI_OK)
559 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
561 res = shishi_safe_verify (state->safe, state->sessionkey);
562 if (res != SHISHI_OK)
563 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
565 res = shishi_safe_user_data (state->sh, shishi_safe_safe (state->safe),
566 output, output_len);
567 if (res != SHISHI_OK)
568 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
570 return GSASL_OK;
572 else
574 *output_len = input_len;
575 *output = malloc (input_len);
576 if (!*output)
577 return GSASL_MALLOC_ERROR;
578 memcpy (*output, input, input_len);
582 return GSASL_OK;
586 _gsasl_kerberos_v5_server_finish (Gsasl_session * sctx, void *mech_data)
588 struct _Gsasl_kerberos_v5_server_state *state = mech_data;
590 shishi_done (state->sh);
591 if (state->username)
592 free (state->username);
593 if (state->password)
594 free (state->password);
595 if (state->random)
596 free (state->random);
597 free (state);
599 return GSASL_OK;