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
, size_t * 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
++)
92 tmplen
= *saltlen
- len
;
94 return SHISHI_TOO_SMALL_BUFFER
;
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
)
115 int der_len
, out_len
;
116 char buffer
[BUFSIZ
]; /* XXX dynamically allocate this? */
117 char realm
[BUFSIZ
]; /* XXX dynamically allocate this */
121 res
= shishi_new_a2d (handle
, kdcreq
, &der
, &der_len
);
122 if (res
!= SHISHI_OK
)
124 shishi_error_printf (handle
, "Could not DER encode AS-REQ: %s\n",
125 shishi_strerror (res
));
129 realmlen
= sizeof (realm
);
130 res
= shishi_asn1_field (handle
, kdcreq
, realm
, &realmlen
,
132 if (res
!= SHISHI_OK
)
134 shishi_error_printf (handle
, "Could not get realm: %s\n",
135 shishi_strerror_details (handle
));
138 realm
[realmlen
] = '\0';
140 out_len
= sizeof (buffer
);
141 res
= shishi_kdc_sendrecv (handle
, realm
, der
, der_len
, buffer
, &out_len
);
142 if (res
!= SHISHI_OK
)
144 shishi_error_printf (handle
, "Could not send to KDC: %s\n",
145 shishi_strerror_details (handle
));
150 if (VERBOSEASN1 (handle
))
151 printf ("received %d bytes\n", out_len
);
153 *kdcrep
= shishi_der2asn1_asrep (handle
, buffer
, out_len
);
156 *kdcrep
= shishi_der2asn1_tgsrep (handle
, buffer
, out_len
);
159 *kdcrep
= shishi_der2asn1_kdcrep (handle
, buffer
, out_len
);
162 *kdcrep
= shishi_der2asn1_krberror (handle
, buffer
, out_len
);
166 (handle
, "Could not DER decode AS-REP/KRB-ERROR: %s",
167 shishi_strerror_details (handle
));
168 return SHISHI_ASN1_ERROR
;
171 shishi_error_clear (handle
);
172 return SHISHI_GOT_KRBERROR
;
177 ("Buggy server replied with KDC-REP instead of AS-REP\n");
186 * shishi_kdc_copy_crealm:
187 * @handle: shishi handle as allocated by shishi_init().
188 * @kdcreq: KDC-REQ to read crealm from.
189 * @encticketpart: EncTicketPart to set crealm in.
191 * Set crealm in KDC-REP to value in EncTicketPart.
193 * Return value: Returns SHISHI_OK if successful.
196 shishi_kdc_copy_crealm (Shishi
* handle
,
197 Shishi_asn1 kdcrep
, Shishi_asn1 encticketpart
)
199 unsigned char buf
[BUFSIZ
];
203 buf
[0] = '\0'; /* XXX if crealm is empty, buflen == 0 which
204 causes libtasn1 to strlen(buf)... */
206 res
= shishi_asn1_read (handle
, encticketpart
, "crealm", buf
, &buflen
);
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", 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 if (strcmp (reqrealm
, reprealm
) != 0)
264 return SHISHI_REALM_MISMATCH
;
270 * shishi_kdc_copy_crealm:
271 * @handle: shishi handle as allocated by shishi_init().
272 * @kdcreq: KDC-REQ to read cname from.
273 * @encticketpart: EncTicketPart to set cname in.
275 * Set cname in KDC-REP to value in EncTicketPart.
277 * Return value: Returns SHISHI_OK if successful.
280 shishi_kdc_copy_cname (Shishi
* handle
,
281 Shishi_asn1 kdcrep
, Shishi_asn1 encticketpart
)
283 unsigned char buf
[BUFSIZ
];
290 res
= shishi_asn1_read (handle
, encticketpart
,
291 "cname.name-type", buf
, &buflen
);
292 if (res
!= SHISHI_OK
)
295 res
= shishi_asn1_write (handle
, kdcrep
, "cname.name-type", buf
, buflen
);
296 if (res
!= SHISHI_OK
)
299 res
= shishi_asn1_number_of_elements (handle
, encticketpart
,
300 "cname.name-string", &n
);
301 if (res
!= SHISHI_OK
)
304 res
= shishi_asn1_write (handle
, kdcrep
, "cname.name-string", NULL
, 0);
305 if (res
!= SHISHI_OK
)
308 for (i
= 1; i
<= n
; i
++)
310 res
= shishi_asn1_write (handle
, kdcrep
, "cname.name-string", "NEW", 1);
311 if (res
!= SHISHI_OK
)
314 sprintf (format
, "cname.name-string.?%d", i
);
316 res
= shishi_asn1_read (handle
, encticketpart
, format
, buf
, &buflen
);
317 if (res
!= SHISHI_OK
)
320 sprintf (format
, "cname.name-string.?%d", i
);
321 res
= shishi_asn1_write (handle
, kdcrep
, format
, buf
, buflen
);
322 if (res
!= SHISHI_OK
)
330 * shishi_as_check_cname:
331 * @handle: shishi handle as allocated by shishi_init().
332 * @kdcreq: AS-REQ to compare client name field in.
333 * @kdcrep: AS-REP to compare client name field in.
335 * Verify that AS-REQ.req-body.realm and AS-REP.crealm fields matches.
336 * This is one of the steps that has to be performed when processing a
337 * AS-REQ and AS-REP exchange, see shishi_kdc_process().
339 * Return value: Returns SHISHI_OK if successful,
340 * SHISHI_CNAME_MISMATCH if the values differ, or an error code.
343 shishi_as_check_cname (Shishi
* handle
, Shishi_asn1 asreq
, Shishi_asn1 asrep
)
345 char reqcname
[BUFSIZ
], repcname
[BUFSIZ
];
346 int reqcnamelen
, repcnamelen
;
351 /* We do not compare msg-type as recommended on the ietf-krb-wg list */
353 res
= shishi_asn1_number_of_elements (handle
, asreq
,
354 "req-body.cname.name-string", &i
);
355 if (res
!= SHISHI_OK
)
358 res
= shishi_asn1_number_of_elements (handle
, asrep
,
359 "cname.name-string", &j
);
360 if (res
!= SHISHI_OK
)
364 return SHISHI_CNAME_MISMATCH
;
366 for (i
= 1; i
<= j
; i
++)
368 sprintf (format
, "req-body.cname.name-string.?%d", i
);
369 reqcnamelen
= sizeof (reqcname
);
370 res
= shishi_asn1_read (handle
, asreq
, format
, reqcname
, &reqcnamelen
);
371 if (res
!= SHISHI_OK
)
374 sprintf (format
, "cname.name-string.?%d", i
);
375 repcnamelen
= sizeof (repcname
);
376 res
= shishi_asn1_read (handle
, asrep
, format
, repcname
, &repcnamelen
);
377 if (res
!= SHISHI_OK
)
380 if (VERBOSEASN1 (handle
))
382 reqcname
[reqcnamelen
] = '\0';
383 repcname
[repcnamelen
] = '\0';
384 printf ("request cname %d: %s\n", i
, reqcname
);
385 printf ("reply cname %d: %s\n", i
, repcname
);
388 if (reqcnamelen
!= repcnamelen
)
389 return SHISHI_CNAME_MISMATCH
;
391 if (memcmp (reqcname
, repcname
, reqcnamelen
) != 0)
392 return SHISHI_CNAME_MISMATCH
;
399 * shishi_kdc_copy_nonce:
400 * @handle: shishi handle as allocated by shishi_init().
401 * @kdcreq: KDC-REQ to read nonce from.
402 * @enckdcreppart: EncKDCRepPart to set nonce in.
404 * Set nonce in EncKDCRepPart to value in KDC-REQ.
406 * Return value: Returns SHISHI_OK if successful.
409 shishi_kdc_copy_nonce (Shishi
* handle
,
410 Shishi_asn1 kdcreq
, Shishi_asn1 enckdcreppart
)
415 res
= shishi_kdcreq_nonce (handle
, kdcreq
, &nonce
);
416 if (res
!= SHISHI_OK
)
419 res
= shishi_enckdcreppart_nonce_set (handle
, enckdcreppart
, nonce
);
420 if (res
!= SHISHI_OK
)
427 * shishi_kdc_check_nonce:
428 * @handle: shishi handle as allocated by shishi_init().
429 * @kdcreq: KDC-REQ to compare nonce field in.
430 * @enckdcreppart: Encrypted KDC-REP part to compare nonce field in.
432 * Verify that KDC-REQ.req-body.nonce and EncKDCRepPart.nonce fields
433 * matches. This is one of the steps that has to be performed when
434 * processing a KDC-REQ and KDC-REP exchange.
436 * Return value: Returns SHISHI_OK if successful,
437 * SHISHI_NONCE_LENGTH_MISMATCH if the nonces have different lengths
438 * (usually indicates that buggy server truncated nonce to 4 bytes),
439 * SHISHI_NONCE_MISMATCH if the values differ, or an error code.
442 shishi_kdc_check_nonce (Shishi
* handle
,
443 Shishi_asn1 kdcreq
, Shishi_asn1 enckdcreppart
)
445 unsigned char reqnonce
[BUFSIZ
];
446 unsigned char repnonce
[BUFSIZ
];
447 int reqnoncelen
= BUFSIZ
;
448 int repnoncelen
= BUFSIZ
;
451 res
= shishi_asn1_read (handle
, kdcreq
, "req-body.nonce",
452 reqnonce
, &reqnoncelen
);
453 if (res
!= SHISHI_OK
)
455 shishi_error_printf (handle
, "Could not read request nonce: %s\n",
456 shishi_strerror (res
));
460 res
= shishi_asn1_read (handle
, enckdcreppart
, "nonce",
461 repnonce
, &repnoncelen
);
462 if (res
!= SHISHI_OK
)
464 shishi_error_printf (handle
, "Could not read reply nonce: %s\n",
465 shishi_strerror (res
));
469 if (VERBOSEASN1 (handle
))
473 printf ("request nonce (len=%d) ", reqnoncelen
);
474 for (i
= 0; i
< reqnoncelen
; i
++)
475 printf ("%02X", reqnonce
[i
]);
477 printf ("reply nonce (len=%d) ", repnoncelen
);
478 for (i
= 0; i
< repnoncelen
; i
++)
479 printf ("%02X", repnonce
[i
]);
483 if (reqnoncelen
> 4 && repnoncelen
== 4)
485 /* This case warrants some explanation.
487 * RFC 1510 didn't restrict nonce to 4 bytes, so the nonce field
488 * may be longer. There are KDCs that will accept longer nonces
489 * but truncated them to 4 bytes in the response. If we happen
490 * to parse such a KDC request, we consider it OK even though it
491 * isn't. I doubt this is a security problem, because you need
492 * to break the integrity protection of the encryption system
493 * as well as guess the nonce correctly. The nonce doesn't seem
494 * to serve any purpose at all, really.
498 if (memcmp (reqnonce
+ reqnoncelen
- 4, repnonce
, 4) != 0)
499 return SHISHI_NONCE_MISMATCH
;
501 fprintf (stderr
, "warning: server truncated long nonce to 4 bytes\n");
506 if (reqnoncelen
!= repnoncelen
||
507 memcmp (reqnonce
, repnonce
, repnoncelen
) != 0)
508 return SHISHI_NONCE_MISMATCH
;
514 * shishi_tgs_process:
515 * @handle: shishi handle as allocated by shishi_init().
516 * @kdcreq: input variable that holds the sent KDC-REQ.
517 * @kdcrep: input variable that holds the received KDC-REP.
518 * @oldenckdcreppart: input variable with EncKDCRepPart used in request.
519 * @enckdcreppart: output variable that holds new EncKDCRepPart.
521 * Process a TGS client exchange and output decrypted EncKDCRepPart
522 * which holds details for the new ticket received. This function
523 * simply derives the encryption key from the ticket used to construct
524 * the TGS request and calls shishi_kdc_process(), which see.
526 * Return value: Returns SHISHI_OK iff the TGS client exchange was
530 shishi_tgs_process (Shishi
* handle
,
533 Shishi_asn1 oldenckdcreppart
, Shishi_asn1
* enckdcreppart
)
539 res
= shishi_kdcrep_get_enc_part_etype (handle
, tgsrep
, &etype
);
540 if (res
!= SHISHI_OK
)
543 res
= shishi_enckdcreppart_get_key (handle
, oldenckdcreppart
, &key
);
544 if (res
!= SHISHI_OK
)
547 if (etype
!= shishi_key_type (key
))
548 return SHISHI_TGSREP_BAD_KEYTYPE
;
550 res
= shishi_kdc_process (handle
, tgsreq
, tgsrep
, key
,
551 SHISHI_KEYUSAGE_ENCTGSREPPART_SESSION_KEY
,
559 * @handle: shishi handle as allocated by shishi_init().
560 * @kdcreq: input variable that holds the sent KDC-REQ.
561 * @kdcrep: input variable that holds the received KDC-REP.
562 * @string: input variable with zero terminated password.
563 * @enckdcreppart: output variable that holds new EncKDCRepPart.
565 * Process an AS client exchange and output decrypted EncKDCRepPart
566 * which holds details for the new ticket received. This function
567 * simply derives the encryption key from the password and calls
568 * shishi_kdc_process(), which see.
570 * Return value: Returns SHISHI_OK iff the AS client exchange was
574 shishi_as_process (Shishi
* handle
,
577 const char *string
, Shishi_asn1
* enckdcreppart
)
579 unsigned char salt
[BUFSIZ
];
585 saltlen
= sizeof (salt
);
586 res
= shishi_as_derive_salt (handle
, asreq
, asrep
, salt
, &saltlen
);
587 if (res
!= SHISHI_OK
)
590 res
= shishi_kdcrep_get_enc_part_etype (handle
, asrep
, &keytype
);
591 if (res
!= SHISHI_OK
)
594 res
= shishi_key_from_string (handle
, keytype
,
595 string
, strlen (string
),
596 salt
, saltlen
, NULL
, &key
);
597 if (res
!= SHISHI_OK
)
600 if (VERBOSENOICE (handle
))
601 shishi_key_print (handle
, stderr
, key
);
603 res
= shishi_kdc_process (handle
, asreq
, asrep
, key
,
604 SHISHI_KEYUSAGE_ENCASREPPART
, enckdcreppart
);
610 * shishi_kdc_process:
611 * @handle: shishi handle as allocated by shishi_init().
612 * @kdcreq: input variable that holds the sent KDC-REQ.
613 * @kdcrep: input variable that holds the received KDC-REP.
614 * @keytype: input variable that holds type of key.
615 * @key: input array with key to decrypt encrypted part of KDC-REP with.
616 * @keylen: size of input array with key.
617 * @enckdcreppart: output variable that holds new EncKDCRepPart.
619 * Process a KDC client exchange and output decrypted EncKDCRepPart
620 * which holds details for the new ticket received. Use
621 * shishi_kdcrep_get_ticket() to extract the ticket. This function
622 * verifies the various conditions that must hold if the response is
623 * to be considered valid, specifically it compares nonces
624 * (shishi_check_nonces()) and if the exchange was a AS exchange, it
625 * also compares cname and crealm (shishi_check_cname() and
626 * shishi_check_crealm()).
628 * Usually the shishi_as_process() and shishi_tgs_process() functions
629 * should be used instead, since they simplify the decryption key
632 * Return value: Returns SHISHI_OK iff the KDC client exchange was
636 shishi_kdc_process (Shishi
* handle
,
639 Shishi_key
* key
, int keyusage
,
640 Shishi_asn1
* enckdcreppart
)
646 If the reply message type is KRB_AS_REP, then the client verifies
647 that the cname and crealm fields in the cleartext portion of the
648 reply match what it requested. If any padata fields are present,
649 they may be used to derive the proper secret key to decrypt the
650 message. The client decrypts the encrypted part of the response
651 using its secret key, verifies that the nonce in the encrypted
652 part matches the nonce it supplied in its request (to detect
653 replays). It also verifies that the sname and srealm in the
654 response match those in the request (or are otherwise expected
655 values), and that the host address field is also correct. It then
656 stores the ticket, session key, start and expiration times, and
657 other information for later use. The key-expiration field from the
658 encrypted part of the response may be checked to notify the user
659 of impending key expiration (the client program could then suggest
660 remedial action, such as a password change).
664 res
= shishi_asn1_read_integer (handle
, kdcrep
, "msg-type", &msgtype
);
665 if (res
!= SHISHI_OK
)
668 if (msgtype
== SHISHI_MSGTYPE_AS_REP
)
670 res
= shishi_as_check_crealm (handle
, kdcreq
, kdcrep
);
671 if (res
!= SHISHI_OK
)
674 res
= shishi_as_check_cname (handle
, kdcreq
, kdcrep
);
675 if (res
!= SHISHI_OK
)
679 res
= shishi_kdcrep_decrypt (handle
, kdcrep
, key
, keyusage
, enckdcreppart
);
680 if (res
!= SHISHI_OK
)
683 res
= shishi_kdc_check_nonce (handle
, kdcreq
, *enckdcreppart
);
684 if (res
!= SHISHI_OK
)