1 /* kdc.c Key distribution (AS/TGS) functions
2 * Copyright (C) 2002, 2003 Simon Josefsson
4 * This file is part of Shishi.
6 * Shishi is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * Shishi 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
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with Shishi; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 * shishi_as_derive_salt:
26 * @handle: shishi handle as allocated by shishi_init().
27 * @asreq: input AS-REQ variable.
28 * @asrep: input AS-REP variable.
29 * @salt: output array with salt.
30 * @saltlen: on input, maximum size of output array with salt, on output,
31 * holds actual size of output array with salt.
33 * Derive the salt that should be used when deriving a key via
34 * shishi_string_to_key() for an AS exchange. Currently this searches
35 * for PA-DATA of type SHISHI_PA_PW_SALT in the AS-REP and returns it
36 * if found, otherwise the salt is derived from the client name and
39 * Return value: Returns SHISHI_OK iff successful.
42 shishi_as_derive_salt (Shishi
* handle
,
44 Shishi_asn1 asrep
, char *salt
, size_t * saltlen
)
46 size_t len
= *saltlen
;
52 res
= shishi_asn1_number_of_elements (handle
, asrep
, "padata", &n
);
53 if (res
== SHISHI_ASN1_NO_ELEMENT
)
55 else if (res
!= SHISHI_OK
)
58 for (i
= 1; i
<= n
; i
++)
62 asprintf (&format
, "padata.?%d.padata-type", i
);
63 res
= shishi_asn1_read_int32 (handle
, asrep
, format
, &patype
);
68 if (patype
== SHISHI_PA_PW_SALT
)
70 asprintf (&format
, "padata.?%d.padata-value", i
);
71 res
= shishi_asn1_read (handle
, asrep
, format
, salt
, saltlen
);
81 res
= shishi_asn1_read (handle
, asreq
, "req-body.realm", salt
, &len
);
85 res
= shishi_asn1_number_of_elements (handle
, asreq
,
86 "req-body.cname.name-string", &n
);
90 for (i
= 1; i
<= n
; i
++)
93 return SHISHI_TOO_SMALL_BUFFER
;
94 tmplen
= *saltlen
- len
;
96 asprintf (&format
, "req-body.cname.name-string.?%d", i
);
97 res
= shishi_asn1_read (handle
, asreq
, format
, salt
+ len
, &tmplen
);
111 shishi_kdcreq_sendrecv (Shishi
* handle
, Shishi_asn1 kdcreq
,
112 Shishi_asn1
* kdcrep
)
122 res
= shishi_new_a2d (handle
, kdcreq
, &der
, &der_len
);
123 if (res
!= SHISHI_OK
)
125 shishi_error_printf (handle
, "Could not DER encode AS-REQ: %s\n",
126 shishi_strerror (res
));
130 res
= shishi_asn1_read2 (handle
, kdcreq
, "req-body.realm",
132 if (res
!= SHISHI_OK
)
134 shishi_error_printf (handle
, "Could not get realm: %s\n",
135 shishi_error (handle
));
138 realm
= xrealloc (realm
, realmlen
+ 1);
139 realm
[realmlen
] = '\0';
141 res
= shishi_kdc_sendrecv (handle
, realm
, der
, der_len
, &buffer
, &buflen
);
142 if (res
!= SHISHI_OK
)
144 shishi_error_printf (handle
, "Could not send to KDC: %s\n",
145 shishi_error (handle
));
151 if (VERBOSEASN1 (handle
))
152 printf ("received %d bytes\n", buflen
);
154 *kdcrep
= shishi_der2asn1_asrep (handle
, buffer
, buflen
);
157 *kdcrep
= shishi_der2asn1_tgsrep (handle
, buffer
, buflen
);
160 *kdcrep
= shishi_der2asn1_kdcrep (handle
, buffer
, buflen
);
163 *kdcrep
= shishi_der2asn1_krberror (handle
, buffer
, buflen
);
167 (handle
, "Could not DER decode AS-REP/KRB-ERROR: %s",
168 shishi_error (handle
));
169 return SHISHI_ASN1_ERROR
;
172 shishi_error_clear (handle
);
173 return SHISHI_GOT_KRBERROR
;
178 ("Buggy server replied with KDC-REP instead of AS-REP\n");
188 * shishi_kdc_copy_crealm:
189 * @handle: shishi handle as allocated by shishi_init().
190 * @kdcrep: KDC-REP to read crealm from.
191 * @encticketpart: EncTicketPart to set crealm in.
193 * Set crealm in KDC-REP to value in EncTicketPart.
195 * Return value: Returns SHISHI_OK if successful.
198 shishi_kdc_copy_crealm (Shishi
* handle
,
199 Shishi_asn1 kdcrep
, Shishi_asn1 encticketpart
)
205 res
= shishi_asn1_read2 (handle
, encticketpart
, "crealm", &buf
, &buflen
);
206 if (res
!= SHISHI_OK
)
209 res
= shishi_asn1_write (handle
, kdcrep
, "crealm", buf
, buflen
);
211 if (res
!= SHISHI_OK
)
218 * shishi_as_check_crealm:
219 * @handle: shishi handle as allocated by shishi_init().
220 * @asreq: AS-REQ to compare realm field in.
221 * @asrep: AS-REP to compare realm field in.
223 * Verify that AS-REQ.req-body.realm and AS-REP.crealm fields matches.
224 * This is one of the steps that has to be performed when processing a
225 * AS-REQ and AS-REP exchange, see shishi_kdc_process().
227 * Return value: Returns SHISHI_OK if successful,
228 * SHISHI_REALM_MISMATCH if the values differ, or an error code.
231 shishi_as_check_crealm (Shishi
* handle
, Shishi_asn1 asreq
, Shishi_asn1 asrep
)
233 char *reqrealm
, *reprealm
;
234 size_t reqrealmlen
, reprealmlen
;
237 res
= shishi_asn1_read2 (handle
, asreq
, "req-body.realm",
238 &reqrealm
, &reqrealmlen
);
239 if (res
!= SHISHI_OK
)
241 shishi_error_printf (handle
, "Could not read request realm: %s\n",
242 shishi_strerror (res
));
246 res
= shishi_asn1_read2 (handle
, asrep
, "crealm", &reprealm
, &reprealmlen
);
247 if (res
!= SHISHI_OK
)
249 shishi_error_printf (handle
, "Could not read reply realm: %s\n",
250 shishi_strerror (res
));
254 reqrealm
[reqrealmlen
] = '\0';
255 reprealm
[reprealmlen
] = '\0';
257 if (VERBOSEASN1 (handle
))
259 printf ("request realm: %s\n", reqrealm
);
260 printf ("reply realm: %s\n", reprealm
);
263 res
= strcmp (reqrealm
, reprealm
) != 0;
269 return SHISHI_REALM_MISMATCH
;
275 * shishi_kdc_copy_cname:
276 * @handle: shishi handle as allocated by shishi_init().
277 * @kdcrep: KDC-REQ to read cname from.
278 * @encticketpart: EncTicketPart to set cname in.
280 * Set cname in KDC-REP to value in EncTicketPart.
282 * Return value: Returns SHISHI_OK if successful.
285 shishi_kdc_copy_cname (Shishi
* handle
,
286 Shishi_asn1 kdcrep
, Shishi_asn1 encticketpart
)
294 res
= shishi_asn1_read2 (handle
, encticketpart
,
295 "cname.name-type", &buf
, &buflen
);
296 if (res
!= SHISHI_OK
)
299 res
= shishi_asn1_write (handle
, kdcrep
, "cname.name-type", buf
, buflen
);
301 if (res
!= SHISHI_OK
)
304 res
= shishi_asn1_number_of_elements (handle
, encticketpart
,
305 "cname.name-string", &n
);
306 if (res
!= SHISHI_OK
)
309 res
= shishi_asn1_write (handle
, kdcrep
, "cname.name-string", NULL
, 0);
310 if (res
!= SHISHI_OK
)
313 for (i
= 1; i
<= n
; i
++)
315 res
= shishi_asn1_write (handle
, kdcrep
, "cname.name-string", "NEW", 1);
316 if (res
!= SHISHI_OK
)
319 asprintf (&format
, "cname.name-string.?%d", i
);
320 res
= shishi_asn1_read2 (handle
, encticketpart
, format
, &buf
, &buflen
);
322 if (res
!= SHISHI_OK
)
325 asprintf (&format
, "cname.name-string.?%d", i
);
326 res
= shishi_asn1_write (handle
, kdcrep
, format
, buf
, buflen
);
329 if (res
!= SHISHI_OK
)
337 * shishi_as_check_cname:
338 * @handle: shishi handle as allocated by shishi_init().
339 * @asreq: AS-REQ to compare client name field in.
340 * @asrep: AS-REP to compare client name field in.
342 * Verify that AS-REQ.req-body.realm and AS-REP.crealm fields matches.
343 * This is one of the steps that has to be performed when processing a
344 * AS-REQ and AS-REP exchange, see shishi_kdc_process().
346 * Return value: Returns SHISHI_OK if successful,
347 * SHISHI_CNAME_MISMATCH if the values differ, or an error code.
350 shishi_as_check_cname (Shishi
* handle
, Shishi_asn1 asreq
, Shishi_asn1 asrep
)
352 char *reqcname
, *repcname
;
353 size_t reqcnamelen
, repcnamelen
;
358 /* We do not compare msg-type as recommended on the ietf-krb-wg list */
360 res
= shishi_asn1_number_of_elements (handle
, asreq
,
361 "req-body.cname.name-string", &i
);
362 if (res
!= SHISHI_OK
)
365 res
= shishi_asn1_number_of_elements (handle
, asrep
,
366 "cname.name-string", &j
);
367 if (res
!= SHISHI_OK
)
371 return SHISHI_CNAME_MISMATCH
;
373 for (i
= 1; i
<= j
; i
++)
375 asprintf (&format
, "req-body.cname.name-string.?%d", i
);
377 shishi_asn1_read2 (handle
, asreq
, format
, &reqcname
, &reqcnamelen
);
379 if (res
!= SHISHI_OK
)
382 asprintf (&format
, "cname.name-string.?%d", i
);
384 shishi_asn1_read2 (handle
, asrep
, format
, &repcname
, &repcnamelen
);
386 if (res
!= SHISHI_OK
)
389 if (VERBOSEASN1 (handle
))
391 reqcname
[reqcnamelen
] = '\0';
392 repcname
[repcnamelen
] = '\0';
393 printf ("request cname %d: %s\n", i
, reqcname
);
394 printf ("reply cname %d: %s\n", i
, repcname
);
397 res
= (reqcnamelen
!= repcnamelen
) ||
398 (memcmp (reqcname
, repcname
, reqcnamelen
) != 0);
404 return SHISHI_CNAME_MISMATCH
;
411 * shishi_kdc_copy_nonce:
412 * @handle: shishi handle as allocated by shishi_init().
413 * @kdcreq: KDC-REQ to read nonce from.
414 * @enckdcreppart: EncKDCRepPart to set nonce in.
416 * Set nonce in EncKDCRepPart to value in KDC-REQ.
418 * Return value: Returns SHISHI_OK if successful.
421 shishi_kdc_copy_nonce (Shishi
* handle
,
422 Shishi_asn1 kdcreq
, Shishi_asn1 enckdcreppart
)
427 res
= shishi_kdcreq_nonce (handle
, kdcreq
, &nonce
);
428 if (res
!= SHISHI_OK
)
431 res
= shishi_enckdcreppart_nonce_set (handle
, enckdcreppart
, nonce
);
432 if (res
!= SHISHI_OK
)
439 shishi_kdc_check_nonce_1 (Shishi
* handle
,
440 char *reqnonce
, size_t reqnoncelen
,
441 char *repnonce
, size_t repnoncelen
)
443 if (VERBOSENOICE (handle
))
447 printf ("request nonce (len=%d) ", reqnoncelen
);
448 for (i
= 0; i
< reqnoncelen
; i
++)
449 printf ("%02x", reqnonce
[i
] & 0xFF);
451 printf ("reply nonce (len=%d) ", repnoncelen
);
452 for (i
= 0; i
< repnoncelen
; i
++)
453 printf ("%02x", repnonce
[i
] & 0xFF);
457 if (reqnoncelen
> 4 && repnoncelen
== 4)
459 /* This case warrants some explanation.
461 * RFC 1510 didn't restrict nonce to 4 bytes, so the nonce field
462 * may be longer. There are KDCs that will accept longer nonces
463 * but truncated them to 4 bytes in the response. If we happen
464 * to parse such a KDC request, we consider it OK even though it
465 * isn't. I doubt this is a security problem, because you need
466 * to break the integrity protection of the encryption system
467 * as well as guess the nonce correctly. The nonce doesn't seem
468 * to serve any purpose at all, really.
472 if (memcmp (reqnonce
+ reqnoncelen
- 4, repnonce
, 4) != 0)
473 return SHISHI_NONCE_MISMATCH
;
475 shishi_warn (handle
, "server truncated long nonce to 4 bytes");
480 if (reqnoncelen
!= repnoncelen
||
481 memcmp (reqnonce
, repnonce
, repnoncelen
) != 0)
482 return SHISHI_NONCE_MISMATCH
;
488 * shishi_kdc_check_nonce:
489 * @handle: shishi handle as allocated by shishi_init().
490 * @kdcreq: KDC-REQ to compare nonce field in.
491 * @enckdcreppart: Encrypted KDC-REP part to compare nonce field in.
493 * Verify that KDC-REQ.req-body.nonce and EncKDCRepPart.nonce fields
494 * matches. This is one of the steps that has to be performed when
495 * processing a KDC-REQ and KDC-REP exchange.
497 * Return value: Returns SHISHI_OK if successful,
498 * SHISHI_NONCE_LENGTH_MISMATCH if the nonces have different lengths
499 * (usually indicates that buggy server truncated nonce to 4 bytes),
500 * SHISHI_NONCE_MISMATCH if the values differ, or an error code.
503 shishi_kdc_check_nonce (Shishi
* handle
,
504 Shishi_asn1 kdcreq
, Shishi_asn1 enckdcreppart
)
508 size_t reqnoncelen
, repnoncelen
;
511 res
= shishi_asn1_read2 (handle
, kdcreq
, "req-body.nonce",
512 &reqnonce
, &reqnoncelen
);
513 if (res
!= SHISHI_OK
)
515 shishi_error_printf (handle
, "Could not read request nonce: %s\n",
516 shishi_strerror (res
));
520 res
= shishi_asn1_read2 (handle
, enckdcreppart
, "nonce",
521 &repnonce
, &repnoncelen
);
522 if (res
!= SHISHI_OK
)
525 shishi_error_printf (handle
, "Could not read reply nonce: %s\n",
526 shishi_strerror (res
));
530 res
= shishi_kdc_check_nonce_1 (handle
, reqnonce
, reqnoncelen
,
531 repnonce
, repnoncelen
);
540 * shishi_tgs_process:
541 * @handle: shishi handle as allocated by shishi_init().
542 * @tgsreq: input variable that holds the sent KDC-REQ.
543 * @tgsrep: input variable that holds the received KDC-REP.
544 * @authenticator: input variable with Authenticator from AP-REQ in KDC-REQ.
545 * @oldenckdcreppart: input variable with EncKDCRepPart used in request.
546 * @enckdcreppart: output variable that holds new EncKDCRepPart.
548 * Process a TGS client exchange and output decrypted EncKDCRepPart
549 * which holds details for the new ticket received. This function
550 * simply derives the encryption key from the ticket used to construct
551 * the TGS request and calls shishi_kdc_process(), which see.
553 * Return value: Returns SHISHI_OK iff the TGS client exchange was
557 shishi_tgs_process (Shishi
* handle
,
560 Shishi_asn1 authenticator
,
561 Shishi_asn1 oldenckdcreppart
, Shishi_asn1
* enckdcreppart
)
569 res
= shishi_kdcrep_get_enc_part_etype (handle
, tgsrep
, &etype
);
570 if (res
!= SHISHI_OK
)
573 res
= shishi_authenticator_get_subkey (handle
, authenticator
, &subkey
);
574 use_subkey
= (res
!= SHISHI_ASN1_NO_ELEMENT
);
575 if (res
!= SHISHI_OK
&& res
!= SHISHI_ASN1_NO_ELEMENT
)
578 res
= shishi_enckdcreppart_get_key (handle
, oldenckdcreppart
, &tktkey
);
579 if (res
!= SHISHI_OK
)
582 if (etype
!= shishi_key_type (use_subkey
? subkey
: tktkey
))
583 res
= SHISHI_TGSREP_BAD_KEYTYPE
;
585 res
= shishi_kdc_process (handle
, tgsreq
, tgsrep
,
586 use_subkey
? subkey
: tktkey
,
588 SHISHI_KEYUSAGE_ENCTGSREPPART_AUTHENTICATOR_KEY
589 : SHISHI_KEYUSAGE_ENCTGSREPPART_SESSION_KEY
,
592 /* Entire if statement to work around buggy KDCs. */
593 if (use_subkey
&& (res
== SHISHI_CRYPTO_ERROR
||
594 res
== SHISHI_TGSREP_BAD_KEYTYPE
))
598 /* Try again using key from ticket instead of subkey */
599 if (etype
!= shishi_key_type (tktkey
))
600 tmpres
= SHISHI_TGSREP_BAD_KEYTYPE
;
602 tmpres
= shishi_kdc_process (handle
, tgsreq
, tgsrep
, tktkey
,
603 SHISHI_KEYUSAGE_ENCTGSREPPART_SESSION_KEY
,
606 /* if bug workaround code didn't help, return original error. */
607 if (tmpres
!= SHISHI_OK
)
610 shishi_warn (handle
, "KDC bug: Reply encrypted using wrong key.");
615 if (res
!= SHISHI_OK
)
623 * @handle: shishi handle as allocated by shishi_init().
624 * @asreq: input variable that holds the sent KDC-REQ.
625 * @asrep: input variable that holds the received KDC-REP.
626 * @string: input variable with zero terminated password.
627 * @enckdcreppart: output variable that holds new EncKDCRepPart.
629 * Process an AS client exchange and output decrypted EncKDCRepPart
630 * which holds details for the new ticket received. This function
631 * simply derives the encryption key from the password and calls
632 * shishi_kdc_process(), which see.
634 * Return value: Returns SHISHI_OK iff the AS client exchange was
638 shishi_as_process (Shishi
* handle
,
641 const char *string
, Shishi_asn1
* enckdcreppart
)
643 unsigned char salt
[BUFSIZ
];
649 saltlen
= sizeof (salt
);
650 res
= shishi_as_derive_salt (handle
, asreq
, asrep
, salt
, &saltlen
);
651 if (res
!= SHISHI_OK
)
654 res
= shishi_kdcrep_get_enc_part_etype (handle
, asrep
, &keytype
);
655 if (res
!= SHISHI_OK
)
658 res
= shishi_key_from_string (handle
, keytype
,
659 string
, strlen (string
),
660 salt
, saltlen
, NULL
, &key
);
661 if (res
!= SHISHI_OK
)
664 if (VERBOSENOICE (handle
))
665 shishi_key_print (handle
, stderr
, key
);
667 res
= shishi_kdc_process (handle
, asreq
, asrep
, key
,
668 SHISHI_KEYUSAGE_ENCASREPPART
, enckdcreppart
);
674 * shishi_kdc_process:
675 * @handle: shishi handle as allocated by shishi_init().
676 * @kdcreq: input variable that holds the sent KDC-REQ.
677 * @kdcrep: input variable that holds the received KDC-REP.
678 * @key: input array with key to decrypt encrypted part of KDC-REP with.
679 * @keyusage: kereros key usage value.
680 * @enckdcreppart: output variable that holds new EncKDCRepPart.
682 * Process a KDC client exchange and output decrypted EncKDCRepPart
683 * which holds details for the new ticket received. Use
684 * shishi_kdcrep_get_ticket() to extract the ticket. This function
685 * verifies the various conditions that must hold if the response is
686 * to be considered valid, specifically it compares nonces
687 * (shishi_check_nonces()) and if the exchange was a AS exchange, it
688 * also compares cname and crealm (shishi_check_cname() and
689 * shishi_check_crealm()).
691 * Usually the shishi_as_process() and shishi_tgs_process() functions
692 * should be used instead, since they simplify the decryption key
695 * Return value: Returns SHISHI_OK iff the KDC client exchange was
699 shishi_kdc_process (Shishi
* handle
,
702 Shishi_key
* key
, int keyusage
,
703 Shishi_asn1
* enckdcreppart
)
709 If the reply message type is KRB_AS_REP, then the client verifies
710 that the cname and crealm fields in the cleartext portion of the
711 reply match what it requested. If any padata fields are present,
712 they may be used to derive the proper secret key to decrypt the
713 message. The client decrypts the encrypted part of the response
714 using its secret key, verifies that the nonce in the encrypted
715 part matches the nonce it supplied in its request (to detect
716 replays). It also verifies that the sname and srealm in the
717 response match those in the request (or are otherwise expected
718 values), and that the host address field is also correct. It then
719 stores the ticket, session key, start and expiration times, and
720 other information for later use. The key-expiration field from the
721 encrypted part of the response may be checked to notify the user
722 of impending key expiration (the client program could then suggest
723 remedial action, such as a password change).
727 res
= shishi_asn1_read_integer (handle
, kdcrep
, "msg-type", &msgtype
);
728 if (res
!= SHISHI_OK
)
731 if (msgtype
== SHISHI_MSGTYPE_AS_REP
)
733 res
= shishi_as_check_crealm (handle
, kdcreq
, kdcrep
);
734 if (res
!= SHISHI_OK
)
737 res
= shishi_as_check_cname (handle
, kdcreq
, kdcrep
);
738 if (res
!= SHISHI_OK
)
742 res
= shishi_kdcrep_decrypt (handle
, kdcrep
, key
, keyusage
, enckdcreppart
);
743 if (res
!= SHISHI_OK
)
746 res
= shishi_kdc_check_nonce (handle
, kdcreq
, *enckdcreppart
);
747 if (res
!= SHISHI_OK
)