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 * @asrep: input AS-REP 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
, int *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 sprintf (format
, "padata.?%d.padata-type", i
);
63 res
= shishi_asn1_integer_field (handle
, asrep
, &patype
, format
);
67 if (patype
== SHISHI_PA_PW_SALT
)
69 sprintf (format
, "padata.?%d.padata-value", i
);
70 res
= shishi_asn1_read (handle
, asrep
, format
, salt
, saltlen
);
80 shishi_asn1_read (handle
, asreq
, "req-body.realm", salt
, &len
);
84 res
= shishi_asn1_number_of_elements (handle
, asreq
,
85 "req-body.cname.name-string",
90 for (i
= 1; i
<= n
; i
++)
92 tmplen
= *saltlen
- len
;
94 return SHISHI_TOO_SMALL_BUFFER
;
96 sprintf (format
, "req-body.cname.name-string.?%d", i
);
97 res
= shishi_asn1_read (handle
, asreq
, format
, salt
+ len
, &tmplen
);
110 shishi_kdcreq_sendrecv (Shishi
* handle
, Shishi_asn1 kdcreq
,
111 Shishi_asn1
* kdcrep
)
114 int der_len
, out_len
;
115 char buffer
[BUFSIZ
]; /* XXX dynamically allocate this? */
116 char realm
[BUFSIZ
]; /* XXX dynamically allocate this */
120 res
= shishi_new_a2d (handle
, kdcreq
, &der
, &der_len
);
121 if (res
!= SHISHI_OK
)
123 shishi_error_printf (handle
, "Could not DER encode AS-REQ: %s\n",
124 shishi_strerror (res
));
128 realmlen
= sizeof (realm
);
129 res
= shishi_asn1_field (handle
, kdcreq
, realm
, &realmlen
,
131 if (res
!= SHISHI_OK
)
133 shishi_error_printf (handle
, "Could not get realm: %s\n",
134 shishi_strerror_details (handle
));
137 realm
[realmlen
] = '\0';
139 out_len
= sizeof(buffer
);
140 res
= shishi_kdc_sendrecv (handle
, realm
, der
, der_len
, buffer
, &out_len
);
141 if (res
!= SHISHI_OK
)
143 shishi_error_printf (handle
, "Could not send to KDC: %s\n",
144 shishi_strerror_details (handle
));
149 if (VERBOSEASN1 (handle
))
150 printf ("received %d bytes\n", out_len
);
152 *kdcrep
= shishi_der2asn1_asrep (handle
, buffer
, out_len
);
155 *kdcrep
= shishi_der2asn1_tgsrep (handle
, buffer
, out_len
);
158 *kdcrep
= shishi_der2asn1_kdcrep (handle
, buffer
, out_len
);
161 *kdcrep
= shishi_der2asn1_krberror (handle
, buffer
, out_len
);
165 (handle
, "Could not DER decode AS-REP/KRB-ERROR: %s",
166 shishi_strerror_details (handle
));
167 return SHISHI_ASN1_ERROR
;
170 shishi_error_clear (handle
);
171 return SHISHI_GOT_KRBERROR
;
176 ("Buggy server replied with KDC-REP instead of AS-REP\n");
185 * shishi_kdc_copy_crealm:
186 * @handle: shishi handle as allocated by shishi_init().
187 * @kdcreq: KDC-REQ to read crealm from.
188 * @encticketpart: EncTicketPart to set crealm in.
190 * Set crealm in KDC-REP to value in EncTicketPart.
192 * Return value: Returns SHISHI_OK if successful.
195 shishi_kdc_copy_crealm (Shishi
* handle
,
196 Shishi_asn1 kdcrep
, Shishi_asn1 encticketpart
)
198 unsigned char buf
[BUFSIZ
];
202 buf
[0] = '\0'; /* XXX if crealm is empty, buflen == 0 which
203 causes libtasn1 to strlen(buf)... */
205 res
= shishi_asn1_read (handle
, encticketpart
, "crealm",
207 if (res
!= SHISHI_OK
)
210 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 * @kdcreq: AS-REQ to compare realm field in.
221 * @kdcrep: 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
[BUFSIZ
], reprealm
[BUFSIZ
];
234 int reqrealmlen
= BUFSIZ
, reprealmlen
= BUFSIZ
;
237 res
= shishi_asn1_read (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_read (handle
, asrep
, "crealm",
247 reprealm
, &reprealmlen
);
248 if (res
!= SHISHI_OK
)
250 shishi_error_printf (handle
, "Could not read reply realm: %s\n",
251 shishi_strerror (res
));
255 reqrealm
[reqrealmlen
] = '\0';
256 reprealm
[reprealmlen
] = '\0';
258 if (VERBOSEASN1 (handle
))
260 printf ("request realm: %s\n", reqrealm
);
261 printf ("reply realm: %s\n", reprealm
);
264 if (strcmp (reqrealm
, reprealm
) != 0)
265 return SHISHI_REALM_MISMATCH
;
271 * shishi_kdc_copy_crealm:
272 * @handle: shishi handle as allocated by shishi_init().
273 * @kdcreq: KDC-REQ to read cname from.
274 * @encticketpart: EncTicketPart to set cname in.
276 * Set cname in KDC-REP to value in EncTicketPart.
278 * Return value: Returns SHISHI_OK if successful.
281 shishi_kdc_copy_cname (Shishi
* handle
,
282 Shishi_asn1 kdcrep
, Shishi_asn1 encticketpart
)
284 unsigned char buf
[BUFSIZ
];
291 res
= shishi_asn1_read (handle
, encticketpart
,
292 "cname.name-type", buf
, &buflen
);
293 if (res
!= SHISHI_OK
)
296 res
= shishi_asn1_write (handle
, kdcrep
, "cname.name-type",
298 if (res
!= SHISHI_OK
)
301 res
= shishi_asn1_number_of_elements (handle
, encticketpart
,
304 if (res
!= SHISHI_OK
)
307 res
= shishi_asn1_write (handle
, kdcrep
, "cname.name-string",
309 if (res
!= SHISHI_OK
)
312 for (i
= 1; i
<= n
; i
++)
314 res
= shishi_asn1_write (handle
, kdcrep
, "cname.name-string",
316 if (res
!= SHISHI_OK
)
319 sprintf (format
, "cname.name-string.?%d", i
);
321 res
= shishi_asn1_read (handle
, encticketpart
, format
, buf
, &buflen
);
322 if (res
!= SHISHI_OK
)
325 sprintf (format
, "cname.name-string.?%d", i
);
326 res
= shishi_asn1_write (handle
, kdcrep
, format
, buf
, buflen
);
327 if (res
!= SHISHI_OK
)
335 * shishi_as_check_cname:
336 * @handle: shishi handle as allocated by shishi_init().
337 * @kdcreq: AS-REQ to compare client name field in.
338 * @kdcrep: AS-REP to compare client name field in.
340 * Verify that AS-REQ.req-body.realm and AS-REP.crealm fields matches.
341 * This is one of the steps that has to be performed when processing a
342 * AS-REQ and AS-REP exchange, see shishi_kdc_process().
344 * Return value: Returns SHISHI_OK if successful,
345 * SHISHI_CNAME_MISMATCH if the values differ, or an error code.
348 shishi_as_check_cname (Shishi
* handle
, Shishi_asn1 asreq
, Shishi_asn1 asrep
)
350 char reqcname
[BUFSIZ
], repcname
[BUFSIZ
];
351 int reqcnamelen
, repcnamelen
;
356 /* We do not compare msg-type as recommended on the ietf-krb-wg list */
358 res
= shishi_asn1_number_of_elements (handle
, asreq
,
359 "req-body.cname.name-string",
361 if (res
!= SHISHI_OK
)
364 res
= shishi_asn1_number_of_elements (handle
, asrep
,
365 "cname.name-string", &j
);
366 if (res
!= SHISHI_OK
)
370 return SHISHI_CNAME_MISMATCH
;
372 for (i
= 1; i
<= j
; i
++)
374 sprintf (format
, "req-body.cname.name-string.?%d", i
);
375 reqcnamelen
= sizeof (reqcname
);
376 res
= shishi_asn1_read (handle
, asreq
, format
, reqcname
, &reqcnamelen
);
377 if (res
!= SHISHI_OK
)
380 sprintf (format
, "cname.name-string.?%d", i
);
381 repcnamelen
= sizeof (repcname
);
382 res
= shishi_asn1_read (handle
, asrep
, format
, repcname
, &repcnamelen
);
383 if (res
!= SHISHI_OK
)
386 if (VERBOSEASN1 (handle
))
388 reqcname
[reqcnamelen
] = '\0';
389 repcname
[repcnamelen
] = '\0';
390 printf ("request cname %d: %s\n", i
, reqcname
);
391 printf ("reply cname %d: %s\n", i
, repcname
);
394 if (reqcnamelen
!= repcnamelen
)
395 return SHISHI_CNAME_MISMATCH
;
397 if (memcmp (reqcname
, repcname
, reqcnamelen
) != 0)
398 return SHISHI_CNAME_MISMATCH
;
405 * shishi_kdc_copy_nonce:
406 * @handle: shishi handle as allocated by shishi_init().
407 * @kdcreq: KDC-REQ to read nonce from.
408 * @enckdcreppart: EncKDCRepPart to set nonce in.
410 * Set nonce in EncKDCRepPart to value in KDC-REQ.
412 * Return value: Returns SHISHI_OK if successful.
415 shishi_kdc_copy_nonce (Shishi
* handle
,
416 Shishi_asn1 kdcreq
, Shishi_asn1 enckdcreppart
)
421 res
= shishi_kdcreq_nonce (handle
, kdcreq
, &nonce
);
422 if (res
!= SHISHI_OK
)
425 res
= shishi_enckdcreppart_nonce_set (handle
, enckdcreppart
, nonce
);
426 if (res
!= SHISHI_OK
)
433 * shishi_kdc_check_nonce:
434 * @handle: shishi handle as allocated by shishi_init().
435 * @kdcreq: KDC-REQ to compare nonce field in.
436 * @enckdcreppart: Encrypted KDC-REP part to compare nonce field in.
438 * Verify that KDC-REQ.req-body.nonce and EncKDCRepPart.nonce fields
439 * matches. This is one of the steps that has to be performed when
440 * processing a KDC-REQ and KDC-REP exchange.
442 * Return value: Returns SHISHI_OK if successful,
443 * SHISHI_NONCE_LENGTH_MISMATCH if the nonces have different lengths
444 * (usually indicates that buggy server truncated nonce to 4 bytes),
445 * SHISHI_NONCE_MISMATCH if the values differ, or an error code.
448 shishi_kdc_check_nonce (Shishi
* handle
,
449 Shishi_asn1 kdcreq
, Shishi_asn1 enckdcreppart
)
451 unsigned char reqnonce
[BUFSIZ
];
452 unsigned char repnonce
[BUFSIZ
];
453 int reqnoncelen
= BUFSIZ
;
454 int repnoncelen
= BUFSIZ
;
457 res
= shishi_asn1_read (handle
, kdcreq
, "req-body.nonce",
458 reqnonce
, &reqnoncelen
);
459 if (res
!= SHISHI_OK
)
461 shishi_error_printf (handle
, "Could not read request nonce: %s\n",
462 shishi_strerror (res
));
466 res
= shishi_asn1_read (handle
, enckdcreppart
, "nonce",
467 repnonce
, &repnoncelen
);
468 if (res
!= SHISHI_OK
)
470 shishi_error_printf (handle
, "Could not read reply nonce: %s\n",
471 shishi_strerror (res
));
475 if (VERBOSEASN1 (handle
))
479 printf ("request nonce (len=%d) ", reqnoncelen
);
480 for (i
= 0; i
< reqnoncelen
; i
++)
481 printf ("%02X", reqnonce
[i
]);
483 printf ("reply nonce (len=%d) ", repnoncelen
);
484 for (i
= 0; i
< repnoncelen
; i
++)
485 printf ("%02X", repnonce
[i
]);
489 if (reqnoncelen
> 4 && repnoncelen
== 4)
491 /* This case warrants some explanation.
493 * RFC 1510 didn't restrict nonce to 4 bytes, so the nonce field
494 * may be longer. There are KDCs that will accept longer nonces
495 * but truncated them to 4 bytes in the response. If we happen
496 * to parse such a KDC request, we consider it OK even though it
497 * isn't. I doubt this is a security problem, because you need
498 * to break the integrity protection of the encryption system
499 * as well as guess the nonce correctly. The nonce doesn't seem
500 * to serve any purpose at all, really.
504 if (memcmp (reqnonce
+ reqnoncelen
- 4, repnonce
, 4) != 0)
505 return SHISHI_NONCE_MISMATCH
;
507 fprintf (stderr
, "warning: server truncated long nonce to 4 bytes\n");
512 if (reqnoncelen
!= repnoncelen
||
513 memcmp (reqnonce
, repnonce
, repnoncelen
) != 0)
514 return SHISHI_NONCE_MISMATCH
;
520 * shishi_tgs_process:
521 * @handle: shishi handle as allocated by shishi_init().
522 * @kdcreq: input variable that holds the sent KDC-REQ.
523 * @kdcrep: input variable that holds the received KDC-REP.
524 * @oldenckdcreppart: input variable with EncKDCRepPart used in request.
525 * @enckdcreppart: output variable that holds new EncKDCRepPart.
527 * Process a TGS client exchange and output decrypted EncKDCRepPart
528 * which holds details for the new ticket received. This function
529 * simply derives the encryption key from the ticket used to construct
530 * the TGS request and calls shishi_kdc_process(), which see.
532 * Return value: Returns SHISHI_OK iff the TGS client exchange was
536 shishi_tgs_process (Shishi
* handle
,
539 Shishi_asn1 oldenckdcreppart
, Shishi_asn1
* enckdcreppart
)
545 res
= shishi_kdcrep_get_enc_part_etype (handle
, tgsrep
, &etype
);
546 if (res
!= SHISHI_OK
)
549 res
= shishi_enckdcreppart_get_key (handle
, oldenckdcreppart
, &key
);
550 if (res
!= SHISHI_OK
)
553 if (etype
!= shishi_key_type (key
))
554 return SHISHI_TGSREP_BAD_KEYTYPE
;
556 res
= shishi_kdc_process (handle
, tgsreq
, tgsrep
, key
,
557 SHISHI_KEYUSAGE_ENCTGSREPPART_SESSION_KEY
,
565 * @handle: shishi handle as allocated by shishi_init().
566 * @kdcreq: input variable that holds the sent KDC-REQ.
567 * @kdcrep: input variable that holds the received KDC-REP.
568 * @string: input variable with zero terminated password.
569 * @enckdcreppart: output variable that holds new EncKDCRepPart.
571 * Process an AS client exchange and output decrypted EncKDCRepPart
572 * which holds details for the new ticket received. This function
573 * simply derives the encryption key from the password and calls
574 * shishi_kdc_process(), which see.
576 * Return value: Returns SHISHI_OK iff the AS client exchange was
580 shishi_as_process (Shishi
* handle
,
583 const char *string
, Shishi_asn1
* enckdcreppart
)
585 unsigned char salt
[BUFSIZ
];
591 saltlen
= sizeof (salt
);
592 res
= shishi_as_derive_salt (handle
, asreq
, asrep
, salt
, &saltlen
);
593 if (res
!= SHISHI_OK
)
596 res
= shishi_kdcrep_get_enc_part_etype (handle
, asrep
, &keytype
);
597 if (res
!= SHISHI_OK
)
600 res
= shishi_key_from_string (handle
, keytype
,
601 string
, strlen (string
),
602 salt
, saltlen
, NULL
, &key
);
603 if (res
!= SHISHI_OK
)
606 if (VERBOSENOICE (handle
))
607 shishi_key_print (handle
, stderr
, key
);
609 res
= shishi_kdc_process (handle
, asreq
, asrep
, key
,
610 SHISHI_KEYUSAGE_ENCASREPPART
, enckdcreppart
);
616 * shishi_kdc_process:
617 * @handle: shishi handle as allocated by shishi_init().
618 * @kdcreq: input variable that holds the sent KDC-REQ.
619 * @kdcrep: input variable that holds the received KDC-REP.
620 * @keytype: input variable that holds type of key.
621 * @key: input array with key to decrypt encrypted part of KDC-REP with.
622 * @keylen: size of input array with key.
623 * @enckdcreppart: output variable that holds new EncKDCRepPart.
625 * Process a KDC client exchange and output decrypted EncKDCRepPart
626 * which holds details for the new ticket received. Use
627 * shishi_kdcrep_get_ticket() to extract the ticket. This function
628 * verifies the various conditions that must hold if the response is
629 * to be considered valid, specifically it compares nonces
630 * (shishi_check_nonces()) and if the exchange was a AS exchange, it
631 * also compares cname and crealm (shishi_check_cname() and
632 * shishi_check_crealm()).
634 * Usually the shishi_as_process() and shishi_tgs_process() functions
635 * should be used instead, since they simplify the decryption key
638 * Return value: Returns SHISHI_OK iff the KDC client exchange was
642 shishi_kdc_process (Shishi
* handle
,
645 Shishi_key
* key
, int keyusage
,
646 Shishi_asn1
* enckdcreppart
)
652 If the reply message type is KRB_AS_REP, then the client verifies
653 that the cname and crealm fields in the cleartext portion of the
654 reply match what it requested. If any padata fields are present,
655 they may be used to derive the proper secret key to decrypt the
656 message. The client decrypts the encrypted part of the response
657 using its secret key, verifies that the nonce in the encrypted
658 part matches the nonce it supplied in its request (to detect
659 replays). It also verifies that the sname and srealm in the
660 response match those in the request (or are otherwise expected
661 values), and that the host address field is also correct. It then
662 stores the ticket, session key, start and expiration times, and
663 other information for later use. The key-expiration field from the
664 encrypted part of the response may be checked to notify the user
665 of impending key expiration (the client program could then suggest
666 remedial action, such as a password change).
670 res
= shishi_asn1_integer_field (handle
, kdcrep
,
671 &msgtype
, "msg-type");
672 if (res
!= SHISHI_OK
)
675 if (msgtype
== SHISHI_MSGTYPE_AS_REP
)
677 res
= shishi_as_check_crealm (handle
, kdcreq
, kdcrep
);
678 if (res
!= SHISHI_OK
)
681 res
= shishi_as_check_cname (handle
, kdcreq
, kdcrep
);
682 if (res
!= SHISHI_OK
)
686 res
= shishi_kdcrep_decrypt (handle
, kdcrep
, key
, keyusage
, enckdcreppart
);
687 if (res
!= SHISHI_OK
)
690 res
= shishi_kdc_check_nonce (handle
, kdcreq
, *enckdcreppart
);
691 if (res
!= SHISHI_OK
)