1 /* kdc.c --- Key distribution (AS/TGS) functions.
2  * Copyright (C) 2002-2013 Simon Josefsson
3  *
4  * This file is part of Shishi.
5  *
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.
10  *
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.
15  *
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
20  *
21  */
22 
23 #include "internal.h"
24 
25 /**
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.
32  *
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
37  * realm in AS-REQ.
38  *
39  * Return value: Returns SHISHI_OK iff successful.
40  **/
41 int
shishi_as_derive_salt(Shishi * handle,Shishi_asn1 asreq,Shishi_asn1 asrep,char ** salt,size_t * saltlen)42 shishi_as_derive_salt (Shishi * handle,
43 		       Shishi_asn1 asreq,
44 		       Shishi_asn1 asrep, char **salt, size_t * saltlen)
45 {
46   size_t i, n;
47   char *format;
48   int res;
49 
50   res = shishi_asn1_number_of_elements (handle, asrep, "padata", &n);
51   if (res == SHISHI_ASN1_NO_ELEMENT)
52     n = 0;
53   else if (res != SHISHI_OK)
54     return res;
55 
56   for (i = 1; i <= n; i++)
57     {
58       int patype;
59 
60       asprintf (&format, "padata.?%ld.padata-type", i);
61       res = shishi_asn1_read_int32 (handle, asrep, format, &patype);
62       free (format);
63       if (res != SHISHI_OK)
64 	return res;
65 
66       if (patype == SHISHI_PA_PW_SALT)
67 	{
68 	  asprintf (&format, "padata.?%ld.padata-value", i);
69 	  res = shishi_asn1_read (handle, asrep, format, salt, saltlen);
70 	  free (format);
71 	  if (res != SHISHI_OK)
72 	    return res;
73 
74 	  return SHISHI_OK;
75 	}
76     }
77 
78   res = shishi_kdcreq_realm (handle, asreq, salt, saltlen);
79   if (res != SHISHI_OK)
80     return res;
81 
82   res = shishi_asn1_number_of_elements (handle, asreq,
83 					"req-body.cname.name-string", &n);
84   if (res != SHISHI_OK)
85     return res;
86 
87   for (i = 1; i <= n; i++)
88     {
89       char *tmp;
90       size_t tmplen;
91 
92       asprintf (&format, "req-body.cname.name-string.?%ld", i);
93       res = shishi_asn1_read (handle, asreq, format, &tmp, &tmplen);
94       free (format);
95       if (res != SHISHI_OK)
96 	return res;
97 
98       *saltlen += tmplen;
99 
100       *salt = xrealloc (*salt, *saltlen + 1);
101       memcpy (*salt + *saltlen - tmplen, tmp, tmplen);
102       (*salt)[*saltlen] = '\0';
103       free (tmp);
104     }
105 
106   return SHISHI_OK;
107 }
108 
109 int
shishi_kdcreq_sendrecv_hint(Shishi * handle,Shishi_asn1 kdcreq,Shishi_asn1 * kdcrep,Shishi_tkts_hint * hint)110 shishi_kdcreq_sendrecv_hint (Shishi * handle,
111 			     Shishi_asn1 kdcreq,
112 			     Shishi_asn1 * kdcrep, Shishi_tkts_hint * hint)
113 {
114   char *der;
115   size_t der_len;
116   size_t buflen;
117   char *buffer;
118   char *realm;
119   size_t realmlen;
120   int res;
121 
122   res = shishi_asn1_to_der (handle, kdcreq, &der, &der_len);
123   if (res != SHISHI_OK)
124     {
125       shishi_error_printf (handle, "Could not DER encode AS-REQ: %s\n",
126 			   shishi_strerror (res));
127       return res;
128     }
129 
130   res = shishi_asn1_read (handle, kdcreq, "req-body.realm",
131 			  &realm, &realmlen);
132   if (res != SHISHI_OK)
133     {
134       shishi_error_printf (handle, "Could not get realm: %s\n",
135 			   shishi_error (handle));
136       return res;
137     }
138   realm = xrealloc (realm, realmlen + 1);
139   realm[realmlen] = '\0';
140 
141   res = shishi_kdc_sendrecv_hint (handle, realm, der, der_len,
142 				  &buffer, &buflen, hint);
143   if (res != SHISHI_OK)
144     {
145       shishi_error_printf (handle, "Could not send to KDC: %s\n",
146 			   shishi_error (handle));
147       return res;
148     }
149   free (realm);
150   free (der);
151 
152   if (VERBOSEASN1 (handle))
153     printf ("received %ld bytes\n", buflen);
154 
155   *kdcrep = shishi_der2asn1_asrep (handle, buffer, buflen);
156   if (*kdcrep == NULL)
157     {
158       *kdcrep = shishi_der2asn1_tgsrep (handle, buffer, buflen);
159       if (*kdcrep == NULL)
160 	{
161 	  *kdcrep = shishi_der2asn1_kdcrep (handle, buffer, buflen);
162 	  if (*kdcrep == NULL)
163 	    {
164 	      *kdcrep = shishi_der2asn1_krberror (handle, buffer, buflen);
165 	      if (*kdcrep == NULL)
166 		{
167 		  shishi_error_printf
168 		    (handle, "Could not DER decode AS-REP/KRB-ERROR: %s",
169 		     shishi_error (handle));
170 		  return SHISHI_ASN1_ERROR;
171 		}
172 
173 	      shishi_error_clear (handle);
174 	      return SHISHI_GOT_KRBERROR;
175 	    }
176 	  else
177 	    {
178 	      printf
179 		("Buggy server replied with KDC-REP instead of AS-REP\n");
180 	    }
181 	}
182     }
183   free (buffer);
184 
185   return SHISHI_OK;
186 }
187 
188 int
shishi_kdcreq_sendrecv(Shishi * handle,Shishi_asn1 kdcreq,Shishi_asn1 * kdcrep)189 shishi_kdcreq_sendrecv (Shishi * handle, Shishi_asn1 kdcreq,
190 			Shishi_asn1 * kdcrep)
191 {
192   return shishi_kdcreq_sendrecv_hint (handle, kdcreq, kdcrep, NULL);
193 }
194 
195 /**
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.
200  *
201  * Set crealm in KDC-REP to value in EncTicketPart.
202  *
203  * Return value: Returns SHISHI_OK if successful.
204  **/
205 int
shishi_kdc_copy_crealm(Shishi * handle,Shishi_asn1 kdcrep,Shishi_asn1 encticketpart)206 shishi_kdc_copy_crealm (Shishi * handle,
207 			Shishi_asn1 kdcrep, Shishi_asn1 encticketpart)
208 {
209   char *buf;
210   size_t buflen;
211   int res;
212 
213   res = shishi_asn1_read (handle, encticketpart, "crealm", &buf, &buflen);
214   if (res != SHISHI_OK)
215     return res;
216 
217   res = shishi_asn1_write (handle, kdcrep, "crealm", buf, buflen);
218   free (buf);
219   if (res != SHISHI_OK)
220     return res;
221 
222   return SHISHI_OK;
223 }
224 
225 /**
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.
230  *
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().
234  *
235  * Return value: Returns SHISHI_OK if successful,
236  * SHISHI_REALM_MISMATCH if the values differ, or an error code.
237  **/
238 int
shishi_as_check_crealm(Shishi * handle,Shishi_asn1 asreq,Shishi_asn1 asrep)239 shishi_as_check_crealm (Shishi * handle, Shishi_asn1 asreq, Shishi_asn1 asrep)
240 {
241   char *reqrealm, *reprealm;
242   size_t reqrealmlen, reprealmlen;
243   int res;
244 
245   res = shishi_asn1_read (handle, asreq, "req-body.realm",
246 			  &reqrealm, &reqrealmlen);
247   if (res != SHISHI_OK)
248     {
249       shishi_error_printf (handle, "Could not read request realm: %s\n",
250 			   shishi_strerror (res));
251       return res;
252     }
253 
254   res = shishi_asn1_read (handle, asrep, "crealm", &reprealm, &reprealmlen);
255   if (res != SHISHI_OK)
256     {
257       shishi_error_printf (handle, "Could not read reply realm: %s\n",
258 			   shishi_strerror (res));
259       return res;
260     }
261 
262   reqrealm[reqrealmlen] = '\0';
263   reprealm[reprealmlen] = '\0';
264 
265   if (VERBOSEASN1 (handle))
266     {
267       printf ("request realm: %s\n", reqrealm);
268       printf ("reply realm: %s\n", reprealm);
269     }
270 
271   res = strcmp (reqrealm, reprealm) != 0;
272 
273   free (reqrealm);
274   free (reprealm);
275 
276   if (res)
277     return SHISHI_REALM_MISMATCH;
278 
279   return SHISHI_OK;
280 }
281 
282 /**
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.
287  *
288  * Set cname in KDC-REP to value in EncTicketPart.
289  *
290  * Return value: Returns SHISHI_OK if successful.
291  **/
292 int
shishi_kdc_copy_cname(Shishi * handle,Shishi_asn1 kdcrep,Shishi_asn1 encticketpart)293 shishi_kdc_copy_cname (Shishi * handle,
294 		       Shishi_asn1 kdcrep, Shishi_asn1 encticketpart)
295 {
296   char *buf;
297   char *format;
298   size_t buflen, i, n;
299   int res;
300 
301   res = shishi_asn1_read (handle, encticketpart,
302 			  "cname.name-type", &buf, &buflen);
303   if (res != SHISHI_OK)
304     return res;
305 
306   res = shishi_asn1_write (handle, kdcrep, "cname.name-type", buf, buflen);
307   free (buf);
308   if (res != SHISHI_OK)
309     return res;
310 
311   res = shishi_asn1_number_of_elements (handle, encticketpart,
312 					"cname.name-string", &n);
313   if (res != SHISHI_OK)
314     return res;
315 
316   res = shishi_asn1_write (handle, kdcrep, "cname.name-string", NULL, 0);
317   if (res != SHISHI_OK)
318     return res;
319 
320   for (i = 1; i <= n; i++)
321     {
322       res = shishi_asn1_write (handle, kdcrep, "cname.name-string", "NEW", 1);
323       if (res != SHISHI_OK)
324 	return res;
325 
326       asprintf (&format, "cname.name-string.?%ld", i);
327       res = shishi_asn1_read (handle, encticketpart, format, &buf, &buflen);
328       free (format);
329       if (res != SHISHI_OK)
330 	return res;
331 
332       asprintf (&format, "cname.name-string.?%ld", i);
333       res = shishi_asn1_write (handle, kdcrep, format, buf, buflen);
334       free (format);
335       free (buf);
336       if (res != SHISHI_OK)
337 	return res;
338     }
339 
340   return SHISHI_OK;
341 }
342 
343 /**
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.
348  *
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().
352  *
353  * Return value: Returns SHISHI_OK if successful,
354  * SHISHI_CNAME_MISMATCH if the values differ, or an error code.
355  **/
356 int
shishi_as_check_cname(Shishi * handle,Shishi_asn1 asreq,Shishi_asn1 asrep)357 shishi_as_check_cname (Shishi * handle, Shishi_asn1 asreq, Shishi_asn1 asrep)
358 {
359   char *reqcname, *repcname;
360   size_t reqcnamelen, repcnamelen, i, j;
361   char *format;
362   int res;
363 
364   /* We do not compare msg-type as recommended on the ietf-krb-wg list */
365 
366   res = shishi_asn1_number_of_elements (handle, asreq,
367 					"req-body.cname.name-string", &i);
368   if (res != SHISHI_OK)
369     return res;
370 
371   res = shishi_asn1_number_of_elements (handle, asrep,
372 					"cname.name-string", &j);
373   if (res != SHISHI_OK)
374     return res;
375 
376   if (i != j)
377     return SHISHI_CNAME_MISMATCH;
378 
379   for (i = 1; i <= j; i++)
380     {
381       asprintf (&format, "req-body.cname.name-string.?%ld", i);
382       res = shishi_asn1_read (handle, asreq, format, &reqcname, &reqcnamelen);
383       free (format);
384       if (res != SHISHI_OK)
385 	return res;
386 
387       asprintf (&format, "cname.name-string.?%ld", i);
388       res = shishi_asn1_read (handle, asrep, format, &repcname, &repcnamelen);
389       free (format);
390       if (res != SHISHI_OK)
391 	return res;
392 
393       if (VERBOSEASN1 (handle))
394 	{
395 	  reqcname[reqcnamelen] = '\0';
396 	  repcname[repcnamelen] = '\0';
397 	  printf ("request cname %ld: %s\n", i, reqcname);
398 	  printf ("reply cname %ld: %s\n", i, repcname);
399 	}
400 
401       res = (reqcnamelen != repcnamelen) ||
402 	(memcmp (reqcname, repcname, reqcnamelen) != 0);
403 
404       free (reqcname);
405       free (repcname);
406 
407       if (res)
408 	return SHISHI_CNAME_MISMATCH;
409     }
410 
411   return SHISHI_OK;
412 }
413 
414 /**
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.
419  *
420  * Set nonce in EncKDCRepPart to value in KDC-REQ.
421  *
422  * Return value: Returns SHISHI_OK if successful.
423  **/
424 int
shishi_kdc_copy_nonce(Shishi * handle,Shishi_asn1 kdcreq,Shishi_asn1 enckdcreppart)425 shishi_kdc_copy_nonce (Shishi * handle,
426 		       Shishi_asn1 kdcreq, Shishi_asn1 enckdcreppart)
427 {
428   int res;
429   uint32_t nonce;
430 
431   res = shishi_kdcreq_nonce (handle, kdcreq, &nonce);
432   if (res != SHISHI_OK)
433     return res;
434 
435   res = shishi_enckdcreppart_nonce_set (handle, enckdcreppart, nonce);
436   if (res != SHISHI_OK)
437     return res;
438 
439   return SHISHI_OK;
440 }
441 
442 static int
shishi_kdc_check_nonce_1(Shishi * handle,char * reqnonce,size_t reqnoncelen,char * repnonce,size_t repnoncelen)443 shishi_kdc_check_nonce_1 (Shishi * handle,
444 			  char *reqnonce, size_t reqnoncelen,
445 			  char *repnonce, size_t repnoncelen)
446 {
447   if (VERBOSENOISE (handle))
448     {
449       size_t i;
450 
451       printf ("request nonce (len=%ld) ", reqnoncelen);
452       for (i = 0; i < reqnoncelen; i++)
453 	printf ("%02x", reqnonce[i] & 0xFF);
454       printf ("\n");
455       printf ("reply nonce (len=%ld) ", repnoncelen);
456       for (i = 0; i < repnoncelen; i++)
457 	printf ("%02x", repnonce[i] & 0xFF);
458       printf ("\n");
459     }
460 
461   if (reqnoncelen > 4 && repnoncelen == 4)
462     {
463       /* This case warrants some explanation.
464        *
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.
473        *
474        */
475 
476       if (memcmp (reqnonce + reqnoncelen - 4, repnonce, 4) != 0)
477 	return SHISHI_NONCE_MISMATCH;
478 
479       shishi_warn (handle, "server truncated long nonce to 4 bytes");
480 
481       return SHISHI_OK;
482     }
483 
484   if (reqnoncelen != repnoncelen ||
485       memcmp (reqnonce, repnonce, repnoncelen) != 0)
486     return SHISHI_NONCE_MISMATCH;
487 
488   return SHISHI_OK;
489 }
490 
491 /**
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.
496  *
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.
500  *
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.
505  **/
506 int
shishi_kdc_check_nonce(Shishi * handle,Shishi_asn1 kdcreq,Shishi_asn1 enckdcreppart)507 shishi_kdc_check_nonce (Shishi * handle,
508 			Shishi_asn1 kdcreq, Shishi_asn1 enckdcreppart)
509 {
510   char *reqnonce;
511   char *repnonce;
512   size_t reqnoncelen, repnoncelen;
513   int res;
514 
515   res = shishi_asn1_read (handle, kdcreq, "req-body.nonce",
516 			  &reqnonce, &reqnoncelen);
517   if (res != SHISHI_OK)
518     {
519       shishi_error_printf (handle, "Could not read request nonce: %s\n",
520 			   shishi_strerror (res));
521       return res;
522     }
523 
524   res = shishi_asn1_read (handle, enckdcreppart, "nonce",
525 			  &repnonce, &repnoncelen);
526   if (res != SHISHI_OK)
527     {
528       free (reqnonce);
529       shishi_error_printf (handle, "Could not read reply nonce: %s\n",
530 			   shishi_strerror (res));
531       return res;
532     }
533 
534   res = shishi_kdc_check_nonce_1 (handle, reqnonce, reqnoncelen,
535 				  repnonce, repnoncelen);
536 
537   free (reqnonce);
538   free (repnonce);
539 
540   return res;
541 }
542 
543 /**
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.
551  *
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.
556  *
557  * Return value: Returns SHISHI_OK iff the TGS client exchange was
558  * successful.
559  **/
560 int
shishi_tgs_process(Shishi * handle,Shishi_asn1 tgsreq,Shishi_asn1 tgsrep,Shishi_asn1 authenticator,Shishi_asn1 oldenckdcreppart,Shishi_asn1 * enckdcreppart)561 shishi_tgs_process (Shishi * handle,
562 		    Shishi_asn1 tgsreq,
563 		    Shishi_asn1 tgsrep,
564 		    Shishi_asn1 authenticator,
565 		    Shishi_asn1 oldenckdcreppart, Shishi_asn1 * enckdcreppart)
566 {
567   Shishi_key *tktkey;
568   Shishi_key *subkey;
569   int use_subkey;
570   int etype;
571   int res;
572 
573   res = shishi_kdcrep_get_enc_part_etype (handle, tgsrep, &etype);
574   if (res != SHISHI_OK)
575     return res;
576 
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)
580     return res;
581 
582   res = shishi_enckdcreppart_get_key (handle, oldenckdcreppart, &tktkey);
583   if (res != SHISHI_OK)
584     return res;
585 
586   if (etype != shishi_key_type (use_subkey ? subkey : tktkey))
587     res = SHISHI_TGSREP_BAD_KEYTYPE;
588   else
589     res = shishi_kdc_process (handle, tgsreq, tgsrep,
590 			      use_subkey ? subkey : tktkey,
591 			      use_subkey ?
592 			      SHISHI_KEYUSAGE_ENCTGSREPPART_AUTHENTICATOR_KEY
593 			      : SHISHI_KEYUSAGE_ENCTGSREPPART_SESSION_KEY,
594 			      enckdcreppart);
595 
596   /* Entire if statement to work around buggy KDCs. */
597   if (use_subkey && (res == SHISHI_CRYPTO_ERROR ||
598 		     res == SHISHI_TGSREP_BAD_KEYTYPE))
599     {
600       int tmpres;
601 
602       /* Try again using key from ticket instead of subkey */
603       if (etype != shishi_key_type (tktkey))
604 	tmpres = SHISHI_TGSREP_BAD_KEYTYPE;
605       else
606 	tmpres = shishi_kdc_process (handle, tgsreq, tgsrep, tktkey,
607 				     SHISHI_KEYUSAGE_ENCTGSREPPART_SESSION_KEY,
608 				     enckdcreppart);
609 
610       /* if bug workaround code didn't help, return original error. */
611       if (tmpres != SHISHI_OK)
612 	return res;
613 
614       shishi_warn (handle, "KDC bug: Reply encrypted using wrong key.");
615 
616       res = tmpres;
617     }
618 
619   if (res != SHISHI_OK)
620     return res;
621 
622   return SHISHI_OK;
623 }
624 
625 /**
626  * shishi_as_process:
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.
632  *
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.
637  *
638  * Return value: Returns SHISHI_OK iff the AS client exchange was
639  * successful.
640  **/
641 int
shishi_as_process(Shishi * handle,Shishi_asn1 asreq,Shishi_asn1 asrep,const char * string,Shishi_asn1 * enckdcreppart)642 shishi_as_process (Shishi * handle,
643 		   Shishi_asn1 asreq,
644 		   Shishi_asn1 asrep,
645 		   const char *string, Shishi_asn1 * enckdcreppart)
646 {
647   char *salt;
648   size_t saltlen;
649   int res;
650   Shishi_key *key;
651   int keytype;
652 
653   res = shishi_as_derive_salt (handle, asreq, asrep, &salt, &saltlen);
654   if (res != SHISHI_OK)
655     return res;
656 
657   res = shishi_kdcrep_get_enc_part_etype (handle, asrep, &keytype);
658   if (res != SHISHI_OK)
659     return res;
660 
661   res = shishi_key_from_string (handle, keytype,
662 				string, strlen (string),
663 				salt, saltlen, NULL, &key);
664   if (res != SHISHI_OK)
665     return res;
666 
667   if (VERBOSENOISE (handle))
668     shishi_key_print (handle, stderr, key);
669 
670   res = shishi_kdc_process (handle, asreq, asrep, key,
671 			    SHISHI_KEYUSAGE_ENCASREPPART, enckdcreppart);
672 
673   return res;
674 }
675 
676 /**
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.
684  *
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_kdc_check_nonce()) and if the exchange was a AS exchange,
691  * it also compares cname and crealm (shishi_as_check_cname() and
692  * shishi_as_check_crealm()).
693  *
694  * Usually the shishi_as_process() and shishi_tgs_process() functions
695  * should be used instead, since they simplify the decryption key
696  * computation.
697  *
698  * Return value: Returns SHISHI_OK iff the KDC client exchange was
699  * successful.
700  **/
701 int
shishi_kdc_process(Shishi * handle,Shishi_asn1 kdcreq,Shishi_asn1 kdcrep,Shishi_key * key,int keyusage,Shishi_asn1 * enckdcreppart)702 shishi_kdc_process (Shishi * handle,
703 		    Shishi_asn1 kdcreq,
704 		    Shishi_asn1 kdcrep,
705 		    Shishi_key * key, int keyusage,
706 		    Shishi_asn1 * enckdcreppart)
707 {
708   int res;
709   int msgtype;
710 
711   /*
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).
727    */
728 
729   msgtype = 0;
730   res = shishi_asn1_read_integer (handle, kdcrep, "msg-type", &msgtype);
731   if (res != SHISHI_OK)
732     return res;
733 
734   if (msgtype == SHISHI_MSGTYPE_AS_REP)
735     {
736       res = shishi_as_check_crealm (handle, kdcreq, kdcrep);
737       if (res != SHISHI_OK)
738 	return res;
739 
740       res = shishi_as_check_cname (handle, kdcreq, kdcrep);
741       if (res != SHISHI_OK)
742 	return res;
743     }
744 
745   res = shishi_kdcrep_decrypt (handle, kdcrep, key, keyusage, enckdcreppart);
746   if (res != SHISHI_OK)
747     return res;
748 
749   res = shishi_kdc_check_nonce (handle, kdcreq, *enckdcreppart);
750   if (res != SHISHI_OK)
751     return res;
752 
753   return SHISHI_OK;
754 }
755