1 /* kdc.c --- Key distribution (AS/TGS) functions.
2 * Copyright (C) 2002, 2003, 2004, 2007 Simon Josefsson
4 * This file is part of Shishi.
6 * Shishi is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * Shishi is distributed in the hope that it will be useful, but
12 * 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, see http://www.gnu.org/licenses or write
18 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
19 * Floor, Boston, MA 02110-1301, USA
26 * shishi_as_derive_salt:
27 * @handle: shishi handle as allocated by shishi_init().
28 * @asreq: input AS-REQ variable.
29 * @asrep: input AS-REP variable.
30 * @salt: newly allocated output array with salt.
31 * @saltlen: 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
)
50 res
= shishi_asn1_number_of_elements (handle
, asrep
, "padata", &n
);
51 if (res
== SHISHI_ASN1_NO_ELEMENT
)
53 else if (res
!= SHISHI_OK
)
56 for (i
= 1; i
<= n
; i
++)
60 asprintf (&format
, "padata.?%d.padata-type", i
);
61 res
= shishi_asn1_read_int32 (handle
, asrep
, format
, &patype
);
66 if (patype
== SHISHI_PA_PW_SALT
)
68 asprintf (&format
, "padata.?%d.padata-value", i
);
69 res
= shishi_asn1_read (handle
, asrep
, format
, salt
, saltlen
);
78 res
= shishi_kdcreq_realm (handle
, asreq
, salt
, saltlen
);
82 res
= shishi_asn1_number_of_elements (handle
, asreq
,
83 "req-body.cname.name-string", &n
);
87 for (i
= 1; i
<= n
; i
++)
92 asprintf (&format
, "req-body.cname.name-string.?%d", i
);
93 res
= shishi_asn1_read (handle
, asreq
, format
, &tmp
, &tmplen
);
100 *salt
= xrealloc (*salt
, *saltlen
+ 1);
101 memcpy (*salt
+ *saltlen
- tmplen
, tmp
, tmplen
);
102 (*salt
)[*saltlen
] = '\0';
110 shishi_kdcreq_sendrecv_hint (Shishi
* handle
,
112 Shishi_asn1
* kdcrep
, Shishi_tkts_hint
* hint
)
122 res
= shishi_asn1_to_der (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_read (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_hint (handle
, realm
, der
, der_len
,
142 &buffer
, &buflen
, hint
);
143 if (res
!= SHISHI_OK
)
145 shishi_error_printf (handle
, "Could not send to KDC: %s\n",
146 shishi_error (handle
));
152 if (VERBOSEASN1 (handle
))
153 printf ("received %d bytes\n", buflen
);
155 *kdcrep
= shishi_der2asn1_asrep (handle
, buffer
, buflen
);
158 *kdcrep
= shishi_der2asn1_tgsrep (handle
, buffer
, buflen
);
161 *kdcrep
= shishi_der2asn1_kdcrep (handle
, buffer
, buflen
);
164 *kdcrep
= shishi_der2asn1_krberror (handle
, buffer
, buflen
);
168 (handle
, "Could not DER decode AS-REP/KRB-ERROR: %s",
169 shishi_error (handle
));
170 return SHISHI_ASN1_ERROR
;
173 shishi_error_clear (handle
);
174 return SHISHI_GOT_KRBERROR
;
179 ("Buggy server replied with KDC-REP instead of AS-REP\n");
189 shishi_kdcreq_sendrecv (Shishi
* handle
, Shishi_asn1 kdcreq
,
190 Shishi_asn1
* kdcrep
)
192 return shishi_kdcreq_sendrecv_hint (handle
, kdcreq
, kdcrep
, NULL
);
196 * shishi_kdc_copy_crealm:
197 * @handle: shishi handle as allocated by shishi_init().
198 * @kdcrep: KDC-REP to read crealm from.
199 * @encticketpart: EncTicketPart to set crealm in.
201 * Set crealm in KDC-REP to value in EncTicketPart.
203 * Return value: Returns SHISHI_OK if successful.
206 shishi_kdc_copy_crealm (Shishi
* handle
,
207 Shishi_asn1 kdcrep
, Shishi_asn1 encticketpart
)
213 res
= shishi_asn1_read (handle
, encticketpart
, "crealm", &buf
, &buflen
);
214 if (res
!= SHISHI_OK
)
217 res
= shishi_asn1_write (handle
, kdcrep
, "crealm", buf
, buflen
);
219 if (res
!= SHISHI_OK
)
226 * shishi_as_check_crealm:
227 * @handle: shishi handle as allocated by shishi_init().
228 * @asreq: AS-REQ to compare realm field in.
229 * @asrep: AS-REP to compare realm field in.
231 * Verify that AS-REQ.req-body.realm and AS-REP.crealm fields matches.
232 * This is one of the steps that has to be performed when processing a
233 * AS-REQ and AS-REP exchange, see shishi_kdc_process().
235 * Return value: Returns SHISHI_OK if successful,
236 * SHISHI_REALM_MISMATCH if the values differ, or an error code.
239 shishi_as_check_crealm (Shishi
* handle
, Shishi_asn1 asreq
, Shishi_asn1 asrep
)
241 char *reqrealm
, *reprealm
;
242 size_t reqrealmlen
, reprealmlen
;
245 res
= shishi_asn1_read (handle
, asreq
, "req-body.realm",
246 &reqrealm
, &reqrealmlen
);
247 if (res
!= SHISHI_OK
)
249 shishi_error_printf (handle
, "Could not read request realm: %s\n",
250 shishi_strerror (res
));
254 res
= shishi_asn1_read (handle
, asrep
, "crealm", &reprealm
, &reprealmlen
);
255 if (res
!= SHISHI_OK
)
257 shishi_error_printf (handle
, "Could not read reply realm: %s\n",
258 shishi_strerror (res
));
262 reqrealm
[reqrealmlen
] = '\0';
263 reprealm
[reprealmlen
] = '\0';
265 if (VERBOSEASN1 (handle
))
267 printf ("request realm: %s\n", reqrealm
);
268 printf ("reply realm: %s\n", reprealm
);
271 res
= strcmp (reqrealm
, reprealm
) != 0;
277 return SHISHI_REALM_MISMATCH
;
283 * shishi_kdc_copy_cname:
284 * @handle: shishi handle as allocated by shishi_init().
285 * @kdcrep: KDC-REQ to read cname from.
286 * @encticketpart: EncTicketPart to set cname in.
288 * Set cname in KDC-REP to value in EncTicketPart.
290 * Return value: Returns SHISHI_OK if successful.
293 shishi_kdc_copy_cname (Shishi
* handle
,
294 Shishi_asn1 kdcrep
, Shishi_asn1 encticketpart
)
301 res
= shishi_asn1_read (handle
, encticketpart
,
302 "cname.name-type", &buf
, &buflen
);
303 if (res
!= SHISHI_OK
)
306 res
= shishi_asn1_write (handle
, kdcrep
, "cname.name-type", buf
, buflen
);
308 if (res
!= SHISHI_OK
)
311 res
= shishi_asn1_number_of_elements (handle
, encticketpart
,
312 "cname.name-string", &n
);
313 if (res
!= SHISHI_OK
)
316 res
= shishi_asn1_write (handle
, kdcrep
, "cname.name-string", NULL
, 0);
317 if (res
!= SHISHI_OK
)
320 for (i
= 1; i
<= n
; i
++)
322 res
= shishi_asn1_write (handle
, kdcrep
, "cname.name-string", "NEW", 1);
323 if (res
!= SHISHI_OK
)
326 asprintf (&format
, "cname.name-string.?%d", i
);
327 res
= shishi_asn1_read (handle
, encticketpart
, format
, &buf
, &buflen
);
329 if (res
!= SHISHI_OK
)
332 asprintf (&format
, "cname.name-string.?%d", i
);
333 res
= shishi_asn1_write (handle
, kdcrep
, format
, buf
, buflen
);
336 if (res
!= SHISHI_OK
)
344 * shishi_as_check_cname:
345 * @handle: shishi handle as allocated by shishi_init().
346 * @asreq: AS-REQ to compare client name field in.
347 * @asrep: AS-REP to compare client name field in.
349 * Verify that AS-REQ.req-body.realm and AS-REP.crealm fields matches.
350 * This is one of the steps that has to be performed when processing a
351 * AS-REQ and AS-REP exchange, see shishi_kdc_process().
353 * Return value: Returns SHISHI_OK if successful,
354 * SHISHI_CNAME_MISMATCH if the values differ, or an error code.
357 shishi_as_check_cname (Shishi
* handle
, Shishi_asn1 asreq
, Shishi_asn1 asrep
)
359 char *reqcname
, *repcname
;
360 size_t reqcnamelen
, repcnamelen
, i
, j
;
364 /* We do not compare msg-type as recommended on the ietf-krb-wg list */
366 res
= shishi_asn1_number_of_elements (handle
, asreq
,
367 "req-body.cname.name-string", &i
);
368 if (res
!= SHISHI_OK
)
371 res
= shishi_asn1_number_of_elements (handle
, asrep
,
372 "cname.name-string", &j
);
373 if (res
!= SHISHI_OK
)
377 return SHISHI_CNAME_MISMATCH
;
379 for (i
= 1; i
<= j
; i
++)
381 asprintf (&format
, "req-body.cname.name-string.?%d", i
);
382 res
= shishi_asn1_read (handle
, asreq
, format
, &reqcname
, &reqcnamelen
);
384 if (res
!= SHISHI_OK
)
387 asprintf (&format
, "cname.name-string.?%d", i
);
388 res
= shishi_asn1_read (handle
, asrep
, format
, &repcname
, &repcnamelen
);
390 if (res
!= SHISHI_OK
)
393 if (VERBOSEASN1 (handle
))
395 reqcname
[reqcnamelen
] = '\0';
396 repcname
[repcnamelen
] = '\0';
397 printf ("request cname %d: %s\n", i
, reqcname
);
398 printf ("reply cname %d: %s\n", i
, repcname
);
401 res
= (reqcnamelen
!= repcnamelen
) ||
402 (memcmp (reqcname
, repcname
, reqcnamelen
) != 0);
408 return SHISHI_CNAME_MISMATCH
;
415 * shishi_kdc_copy_nonce:
416 * @handle: shishi handle as allocated by shishi_init().
417 * @kdcreq: KDC-REQ to read nonce from.
418 * @enckdcreppart: EncKDCRepPart to set nonce in.
420 * Set nonce in EncKDCRepPart to value in KDC-REQ.
422 * Return value: Returns SHISHI_OK if successful.
425 shishi_kdc_copy_nonce (Shishi
* handle
,
426 Shishi_asn1 kdcreq
, Shishi_asn1 enckdcreppart
)
431 res
= shishi_kdcreq_nonce (handle
, kdcreq
, &nonce
);
432 if (res
!= SHISHI_OK
)
435 res
= shishi_enckdcreppart_nonce_set (handle
, enckdcreppart
, nonce
);
436 if (res
!= SHISHI_OK
)
443 shishi_kdc_check_nonce_1 (Shishi
* handle
,
444 char *reqnonce
, size_t reqnoncelen
,
445 char *repnonce
, size_t repnoncelen
)
447 if (VERBOSENOISE (handle
))
451 printf ("request nonce (len=%d) ", reqnoncelen
);
452 for (i
= 0; i
< reqnoncelen
; i
++)
453 printf ("%02x", reqnonce
[i
] & 0xFF);
455 printf ("reply nonce (len=%d) ", repnoncelen
);
456 for (i
= 0; i
< repnoncelen
; i
++)
457 printf ("%02x", repnonce
[i
] & 0xFF);
461 if (reqnoncelen
> 4 && repnoncelen
== 4)
463 /* This case warrants some explanation.
465 * RFC 1510 didn't restrict nonce to 4 bytes, so the nonce field
466 * may be longer. There are KDCs that will accept longer nonces
467 * but truncated them to 4 bytes in the response. If we happen
468 * to parse such a KDC request, we consider it OK even though it
469 * isn't. I doubt this is a security problem, because you need
470 * to break the integrity protection of the encryption system
471 * as well as guess the nonce correctly. The nonce doesn't seem
472 * to serve any purpose at all, really.
476 if (memcmp (reqnonce
+ reqnoncelen
- 4, repnonce
, 4) != 0)
477 return SHISHI_NONCE_MISMATCH
;
479 shishi_warn (handle
, "server truncated long nonce to 4 bytes");
484 if (reqnoncelen
!= repnoncelen
||
485 memcmp (reqnonce
, repnonce
, repnoncelen
) != 0)
486 return SHISHI_NONCE_MISMATCH
;
492 * shishi_kdc_check_nonce:
493 * @handle: shishi handle as allocated by shishi_init().
494 * @kdcreq: KDC-REQ to compare nonce field in.
495 * @enckdcreppart: Encrypted KDC-REP part to compare nonce field in.
497 * Verify that KDC-REQ.req-body.nonce and EncKDCRepPart.nonce fields
498 * matches. This is one of the steps that has to be performed when
499 * processing a KDC-REQ and KDC-REP exchange.
501 * Return value: Returns SHISHI_OK if successful,
502 * SHISHI_NONCE_LENGTH_MISMATCH if the nonces have different lengths
503 * (usually indicates that buggy server truncated nonce to 4 bytes),
504 * SHISHI_NONCE_MISMATCH if the values differ, or an error code.
507 shishi_kdc_check_nonce (Shishi
* handle
,
508 Shishi_asn1 kdcreq
, Shishi_asn1 enckdcreppart
)
512 size_t reqnoncelen
, repnoncelen
;
515 res
= shishi_asn1_read (handle
, kdcreq
, "req-body.nonce",
516 &reqnonce
, &reqnoncelen
);
517 if (res
!= SHISHI_OK
)
519 shishi_error_printf (handle
, "Could not read request nonce: %s\n",
520 shishi_strerror (res
));
524 res
= shishi_asn1_read (handle
, enckdcreppart
, "nonce",
525 &repnonce
, &repnoncelen
);
526 if (res
!= SHISHI_OK
)
529 shishi_error_printf (handle
, "Could not read reply nonce: %s\n",
530 shishi_strerror (res
));
534 res
= shishi_kdc_check_nonce_1 (handle
, reqnonce
, reqnoncelen
,
535 repnonce
, repnoncelen
);
544 * shishi_tgs_process:
545 * @handle: shishi handle as allocated by shishi_init().
546 * @tgsreq: input variable that holds the sent KDC-REQ.
547 * @tgsrep: input variable that holds the received KDC-REP.
548 * @authenticator: input variable with Authenticator from AP-REQ in KDC-REQ.
549 * @oldenckdcreppart: input variable with EncKDCRepPart used in request.
550 * @enckdcreppart: output variable that holds new EncKDCRepPart.
552 * Process a TGS client exchange and output decrypted EncKDCRepPart
553 * which holds details for the new ticket received. This function
554 * simply derives the encryption key from the ticket used to construct
555 * the TGS request and calls shishi_kdc_process(), which see.
557 * Return value: Returns SHISHI_OK iff the TGS client exchange was
561 shishi_tgs_process (Shishi
* handle
,
564 Shishi_asn1 authenticator
,
565 Shishi_asn1 oldenckdcreppart
, Shishi_asn1
* enckdcreppart
)
573 res
= shishi_kdcrep_get_enc_part_etype (handle
, tgsrep
, &etype
);
574 if (res
!= SHISHI_OK
)
577 res
= shishi_authenticator_get_subkey (handle
, authenticator
, &subkey
);
578 use_subkey
= (res
!= SHISHI_ASN1_NO_ELEMENT
);
579 if (res
!= SHISHI_OK
&& res
!= SHISHI_ASN1_NO_ELEMENT
)
582 res
= shishi_enckdcreppart_get_key (handle
, oldenckdcreppart
, &tktkey
);
583 if (res
!= SHISHI_OK
)
586 if (etype
!= shishi_key_type (use_subkey
? subkey
: tktkey
))
587 res
= SHISHI_TGSREP_BAD_KEYTYPE
;
589 res
= shishi_kdc_process (handle
, tgsreq
, tgsrep
,
590 use_subkey
? subkey
: tktkey
,
592 SHISHI_KEYUSAGE_ENCTGSREPPART_AUTHENTICATOR_KEY
593 : SHISHI_KEYUSAGE_ENCTGSREPPART_SESSION_KEY
,
596 /* Entire if statement to work around buggy KDCs. */
597 if (use_subkey
&& (res
== SHISHI_CRYPTO_ERROR
||
598 res
== SHISHI_TGSREP_BAD_KEYTYPE
))
602 /* Try again using key from ticket instead of subkey */
603 if (etype
!= shishi_key_type (tktkey
))
604 tmpres
= SHISHI_TGSREP_BAD_KEYTYPE
;
606 tmpres
= shishi_kdc_process (handle
, tgsreq
, tgsrep
, tktkey
,
607 SHISHI_KEYUSAGE_ENCTGSREPPART_SESSION_KEY
,
610 /* if bug workaround code didn't help, return original error. */
611 if (tmpres
!= SHISHI_OK
)
614 shishi_warn (handle
, "KDC bug: Reply encrypted using wrong key.");
619 if (res
!= SHISHI_OK
)
627 * @handle: shishi handle as allocated by shishi_init().
628 * @asreq: input variable that holds the sent KDC-REQ.
629 * @asrep: input variable that holds the received KDC-REP.
630 * @string: input variable with zero terminated password.
631 * @enckdcreppart: output variable that holds new EncKDCRepPart.
633 * Process an AS client exchange and output decrypted EncKDCRepPart
634 * which holds details for the new ticket received. This function
635 * simply derives the encryption key from the password and calls
636 * shishi_kdc_process(), which see.
638 * Return value: Returns SHISHI_OK iff the AS client exchange was
642 shishi_as_process (Shishi
* handle
,
645 const char *string
, Shishi_asn1
* enckdcreppart
)
653 res
= shishi_as_derive_salt (handle
, asreq
, asrep
, &salt
, &saltlen
);
654 if (res
!= SHISHI_OK
)
657 res
= shishi_kdcrep_get_enc_part_etype (handle
, asrep
, &keytype
);
658 if (res
!= SHISHI_OK
)
661 res
= shishi_key_from_string (handle
, keytype
,
662 string
, strlen (string
),
663 salt
, saltlen
, NULL
, &key
);
664 if (res
!= SHISHI_OK
)
667 if (VERBOSENOISE (handle
))
668 shishi_key_print (handle
, stderr
, key
);
670 res
= shishi_kdc_process (handle
, asreq
, asrep
, key
,
671 SHISHI_KEYUSAGE_ENCASREPPART
, enckdcreppart
);
677 * shishi_kdc_process:
678 * @handle: shishi handle as allocated by shishi_init().
679 * @kdcreq: input variable that holds the sent KDC-REQ.
680 * @kdcrep: input variable that holds the received KDC-REP.
681 * @key: input array with key to decrypt encrypted part of KDC-REP with.
682 * @keyusage: kereros key usage value.
683 * @enckdcreppart: output variable that holds new EncKDCRepPart.
685 * Process a KDC client exchange and output decrypted EncKDCRepPart
686 * which holds details for the new ticket received. Use
687 * shishi_kdcrep_get_ticket() to extract the ticket. This function
688 * verifies the various conditions that must hold if the response is
689 * to be considered valid, specifically it compares nonces
690 * (shishi_check_nonces()) and if the exchange was a AS exchange, it
691 * also compares cname and crealm (shishi_check_cname() and
692 * shishi_check_crealm()).
694 * Usually the shishi_as_process() and shishi_tgs_process() functions
695 * should be used instead, since they simplify the decryption key
698 * Return value: Returns SHISHI_OK iff the KDC client exchange was
702 shishi_kdc_process (Shishi
* handle
,
705 Shishi_key
* key
, int keyusage
,
706 Shishi_asn1
* enckdcreppart
)
712 If the reply message type is KRB_AS_REP, then the client verifies
713 that the cname and crealm fields in the cleartext portion of the
714 reply match what it requested. If any padata fields are present,
715 they may be used to derive the proper secret key to decrypt the
716 message. The client decrypts the encrypted part of the response
717 using its secret key, verifies that the nonce in the encrypted
718 part matches the nonce it supplied in its request (to detect
719 replays). It also verifies that the sname and srealm in the
720 response match those in the request (or are otherwise expected
721 values), and that the host address field is also correct. It then
722 stores the ticket, session key, start and expiration times, and
723 other information for later use. The key-expiration field from the
724 encrypted part of the response may be checked to notify the user
725 of impending key expiration (the client program could then suggest
726 remedial action, such as a password change).
730 res
= shishi_asn1_read_integer (handle
, kdcrep
, "msg-type", &msgtype
);
731 if (res
!= SHISHI_OK
)
734 if (msgtype
== SHISHI_MSGTYPE_AS_REP
)
736 res
= shishi_as_check_crealm (handle
, kdcreq
, kdcrep
);
737 if (res
!= SHISHI_OK
)
740 res
= shishi_as_check_cname (handle
, kdcreq
, kdcrep
);
741 if (res
!= SHISHI_OK
)
745 res
= shishi_kdcrep_decrypt (handle
, kdcrep
, key
, keyusage
, enckdcreppart
);
746 if (res
!= SHISHI_OK
)
749 res
= shishi_kdc_check_nonce (handle
, kdcreq
, *enckdcreppart
);
750 if (res
!= SHISHI_OK
)