Mercurial > illumos > onarm
comparison usr/src/cmd/krb5/krb5kdc/kdc_preauth.c @ 0:c9caec207d52 b86
Initial porting based on b86
author | Koji Uno <koji.uno@sun.com> |
---|---|
date | Tue, 02 Jun 2009 18:56:50 +0900 |
parents | |
children | 1a15d5aaf794 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:c9caec207d52 |
---|---|
1 /* | |
2 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. | |
3 * Use is subject to license terms. | |
4 */ | |
5 | |
6 #pragma ident "@(#)kdc_preauth.c 1.11 06/09/27 SMI" | |
7 | |
8 /* | |
9 * kdc/kdc_preauth.c | |
10 * | |
11 * Copyright 1995, 2003 by the Massachusetts Institute of Technology. | |
12 * All Rights Reserved. | |
13 * | |
14 * Export of this software from the United States of America may | |
15 * require a specific license from the United States Government. | |
16 * It is the responsibility of any person or organization contemplating | |
17 * export to obtain such a license before exporting. | |
18 * | |
19 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and | |
20 * distribute this software and its documentation for any purpose and | |
21 * without fee is hereby granted, provided that the above copyright | |
22 * notice appear in all copies and that both that copyright notice and | |
23 * this permission notice appear in supporting documentation, and that | |
24 * the name of M.I.T. not be used in advertising or publicity pertaining | |
25 * to distribution of the software without specific, written prior | |
26 * permission. Furthermore if you modify this software you must label | |
27 * your software as modified software and not distribute it in such a | |
28 * fashion that it might be confused with the original M.I.T. software. | |
29 * M.I.T. makes no representations about the suitability of | |
30 * this software for any purpose. It is provided "as is" without express | |
31 * or implied warranty. | |
32 * | |
33 * Preauthentication routines for the KDC. | |
34 */ | |
35 | |
36 /* | |
37 * Copyright (C) 1998 by the FundsXpress, INC. | |
38 * | |
39 * All rights reserved. | |
40 * | |
41 * Export of this software from the United States of America may require | |
42 * a specific license from the United States Government. It is the | |
43 * responsibility of any person or organization contemplating export to | |
44 * obtain such a license before exporting. | |
45 * | |
46 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and | |
47 * distribute this software and its documentation for any purpose and | |
48 * without fee is hereby granted, provided that the above copyright | |
49 * notice appear in all copies and that both that copyright notice and | |
50 * this permission notice appear in supporting documentation, and that | |
51 * the name of FundsXpress. not be used in advertising or publicity pertaining | |
52 * to distribution of the software without specific, written prior | |
53 * permission. FundsXpress makes no representations about the suitability of | |
54 * this software for any purpose. It is provided "as is" without express | |
55 * or implied warranty. | |
56 * | |
57 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR | |
58 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED | |
59 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. | |
60 */ | |
61 | |
62 #include "k5-int.h" | |
63 #include "kdc_util.h" | |
64 #include "extern.h" | |
65 #include "com_err.h" | |
66 #include <assert.h> | |
67 #include <stdio.h> | |
68 #include "adm_proto.h" | |
69 #include <libintl.h> | |
70 #include <syslog.h> | |
71 | |
72 #include <assert.h> | |
73 | |
74 /* XXX This is ugly and should be in a header file somewhere */ | |
75 #ifndef KRB5INT_DES_TYPES_DEFINED | |
76 #define KRB5INT_DES_TYPES_DEFINED | |
77 typedef unsigned char des_cblock[8]; /* crypto-block size */ | |
78 #endif | |
79 typedef des_cblock mit_des_cblock; | |
80 extern void mit_des_fixup_key_parity (mit_des_cblock ); | |
81 extern int mit_des_is_weak_key (mit_des_cblock ); | |
82 | |
83 typedef krb5_error_code (*verify_proc) | |
84 (krb5_context, krb5_db_entry *client, | |
85 krb5_kdc_req *request, | |
86 krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data *data); | |
87 | |
88 typedef krb5_error_code (*edata_proc) | |
89 (krb5_context, krb5_kdc_req *request, | |
90 krb5_db_entry *client, krb5_db_entry *server, | |
91 krb5_pa_data *data); | |
92 | |
93 typedef krb5_error_code (*return_proc) | |
94 (krb5_context, krb5_pa_data * padata, | |
95 krb5_db_entry *client, | |
96 krb5_kdc_req *request, krb5_kdc_rep *reply, | |
97 krb5_key_data *client_key, | |
98 krb5_keyblock *encrypting_key, | |
99 krb5_pa_data **send_pa); | |
100 | |
101 typedef struct _krb5_preauth_systems { | |
102 char * name; | |
103 int type; | |
104 int flags; | |
105 edata_proc get_edata; | |
106 verify_proc verify_padata; | |
107 return_proc return_padata; | |
108 } krb5_preauth_systems; | |
109 | |
110 static krb5_error_code verify_enc_timestamp | |
111 (krb5_context, krb5_db_entry *client, | |
112 krb5_kdc_req *request, | |
113 krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data *data); | |
114 | |
115 static krb5_error_code get_etype_info | |
116 (krb5_context, krb5_kdc_req *request, | |
117 krb5_db_entry *client, krb5_db_entry *server, | |
118 krb5_pa_data *data); | |
119 static krb5_error_code | |
120 get_etype_info2(krb5_context context, krb5_kdc_req *request, | |
121 krb5_db_entry *client, krb5_db_entry *server, | |
122 krb5_pa_data *pa_data); | |
123 static krb5_error_code | |
124 return_etype_info2(krb5_context, krb5_pa_data * padata, | |
125 krb5_db_entry *client, | |
126 krb5_kdc_req *request, krb5_kdc_rep *reply, | |
127 krb5_key_data *client_key, | |
128 krb5_keyblock *encrypting_key, | |
129 krb5_pa_data **send_pa); | |
130 | |
131 static krb5_error_code return_pw_salt | |
132 (krb5_context, krb5_pa_data * padata, | |
133 krb5_db_entry *client, | |
134 krb5_kdc_req *request, krb5_kdc_rep *reply, | |
135 krb5_key_data *client_key, | |
136 krb5_keyblock *encrypting_key, | |
137 krb5_pa_data **send_pa); | |
138 | |
139 /* SAM preauth support */ | |
140 static krb5_error_code verify_sam_response | |
141 (krb5_context, krb5_db_entry *client, | |
142 krb5_kdc_req *request, | |
143 krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data *data); | |
144 | |
145 static krb5_error_code get_sam_edata | |
146 (krb5_context, krb5_kdc_req *request, | |
147 krb5_db_entry *client, krb5_db_entry *server, | |
148 krb5_pa_data *data); | |
149 static krb5_error_code return_sam_data | |
150 (krb5_context, krb5_pa_data * padata, | |
151 krb5_db_entry *client, | |
152 krb5_kdc_req *request, krb5_kdc_rep *reply, | |
153 krb5_key_data *client_key, | |
154 krb5_keyblock *encrypting_key, | |
155 krb5_pa_data **send_pa); | |
156 /* | |
157 * Preauth property flags | |
158 */ | |
159 #define PA_HARDWARE 0x00000001 | |
160 #define PA_REQUIRED 0x00000002 | |
161 #define PA_SUFFICIENT 0x00000004 | |
162 /* Not really a padata, so don't include it in the etype list*/ | |
163 #define PA_PSEUDO 0x00000008 | |
164 | |
165 static krb5_preauth_systems preauth_systems[] = { | |
166 { | |
167 "timestamp", | |
168 KRB5_PADATA_ENC_TIMESTAMP, | |
169 0, | |
170 0, | |
171 verify_enc_timestamp, | |
172 0 | |
173 }, | |
174 { | |
175 "etype-info", | |
176 KRB5_PADATA_ETYPE_INFO, | |
177 0, | |
178 get_etype_info, | |
179 0, | |
180 0 | |
181 }, | |
182 { | |
183 "etype-info2", | |
184 KRB5_PADATA_ETYPE_INFO2, | |
185 0, | |
186 get_etype_info2, | |
187 0, | |
188 return_etype_info2 | |
189 }, | |
190 { | |
191 "pw-salt", | |
192 KRB5_PADATA_PW_SALT, | |
193 PA_PSEUDO, /* Don't include this in the error list */ | |
194 0, | |
195 0, | |
196 return_pw_salt | |
197 }, | |
198 { | |
199 "sam-response", | |
200 KRB5_PADATA_SAM_RESPONSE, | |
201 0, | |
202 0, | |
203 verify_sam_response, | |
204 return_sam_data | |
205 }, | |
206 { | |
207 "sam-challenge", | |
208 KRB5_PADATA_SAM_CHALLENGE, | |
209 PA_HARDWARE, /* causes get_preauth_hint_list to use this */ | |
210 get_sam_edata, | |
211 0, | |
212 0 | |
213 }, | |
214 { "[end]", -1,} | |
215 }; | |
216 | |
217 #define MAX_PREAUTH_SYSTEMS (sizeof(preauth_systems)/sizeof(preauth_systems[0])) | |
218 | |
219 static krb5_error_code | |
220 find_pa_system(int type, krb5_preauth_systems **preauth) | |
221 { | |
222 krb5_preauth_systems *ap = preauth_systems; | |
223 | |
224 while ((ap->type != -1) && (ap->type != type)) | |
225 ap++; | |
226 if (ap->type == -1) | |
227 return(KRB5_PREAUTH_BAD_TYPE); | |
228 *preauth = ap; | |
229 return 0; | |
230 } | |
231 | |
232 const char *missing_required_preauth(krb5_db_entry *client, | |
233 krb5_db_entry *server, | |
234 krb5_enc_tkt_part *enc_tkt_reply) | |
235 { | |
236 #if 0 | |
237 /* | |
238 * If this is the pwchange service, and the pre-auth bit is set, | |
239 * allow it even if the HW preauth would normally be required. | |
240 * | |
241 * Sandia national labs wanted this for some strange reason... we | |
242 * leave it disabled normally. | |
243 */ | |
244 if (isflagset(server->attributes, KRB5_KDB_PWCHANGE_SERVICE) && | |
245 isflagset(enc_tkt_reply->flags, TKT_FLG_PRE_AUTH)) | |
246 return 0; | |
247 #endif | |
248 | |
249 #ifdef DEBUG | |
250 krb5_klog_syslog (LOG_DEBUG, | |
251 "client needs %spreauth, %shw preauth; request has %spreauth, %shw preauth", | |
252 isflagset (client->attributes, KRB5_KDB_REQUIRES_PRE_AUTH) ? "" : "no ", | |
253 isflagset (client->attributes, KRB5_KDB_REQUIRES_HW_AUTH) ? "" : "no ", | |
254 isflagset (enc_tkt_reply->flags, TKT_FLG_PRE_AUTH) ? "" : "no ", | |
255 isflagset (enc_tkt_reply->flags, TKT_FLG_HW_AUTH) ? "" : "no "); | |
256 #endif | |
257 | |
258 if (isflagset(client->attributes, KRB5_KDB_REQUIRES_PRE_AUTH) && | |
259 !isflagset(enc_tkt_reply->flags, TKT_FLG_PRE_AUTH)) | |
260 return "NEEDED_PREAUTH"; | |
261 | |
262 if (isflagset(client->attributes, KRB5_KDB_REQUIRES_HW_AUTH) && | |
263 !isflagset(enc_tkt_reply->flags, TKT_FLG_HW_AUTH)) | |
264 return "NEEDED_HW_PREAUTH"; | |
265 | |
266 return 0; | |
267 } | |
268 | |
269 void get_preauth_hint_list(krb5_kdc_req *request, krb5_db_entry *client, | |
270 krb5_db_entry *server, krb5_data *e_data) | |
271 { | |
272 int hw_only; | |
273 krb5_preauth_systems *ap; | |
274 krb5_pa_data **pa_data, **pa; | |
275 krb5_data *edat; | |
276 krb5_error_code retval; | |
277 | |
278 /* Zero these out in case we need to abort */ | |
279 e_data->length = 0; | |
280 e_data->data = 0; | |
281 | |
282 hw_only = isflagset(client->attributes, KRB5_KDB_REQUIRES_HW_AUTH); | |
283 pa_data = malloc(sizeof(krb5_pa_data *) * (MAX_PREAUTH_SYSTEMS+1)); | |
284 if (pa_data == 0) | |
285 return; | |
286 memset(pa_data, 0, sizeof(krb5_pa_data *) * (MAX_PREAUTH_SYSTEMS+1)); | |
287 pa = pa_data; | |
288 | |
289 for (ap = preauth_systems; ap->type != -1; ap++) { | |
290 if (hw_only && !(ap->flags & PA_HARDWARE)) | |
291 continue; | |
292 if (ap->flags & PA_PSEUDO) | |
293 continue; | |
294 *pa = malloc(sizeof(krb5_pa_data)); | |
295 if (*pa == 0) | |
296 goto errout; | |
297 memset(*pa, 0, sizeof(krb5_pa_data)); | |
298 (*pa)->magic = KV5M_PA_DATA; | |
299 (*pa)->pa_type = ap->type; | |
300 if (ap->get_edata) { | |
301 retval = (ap->get_edata)(kdc_context, request, client, server, *pa); | |
302 if (retval) { | |
303 /* just failed on this type, continue */ | |
304 free(*pa); | |
305 *pa = 0; | |
306 continue; | |
307 } | |
308 } | |
309 pa++; | |
310 } | |
311 if (pa_data[0] == 0) { | |
312 krb5_klog_syslog (LOG_INFO, | |
313 "%spreauth required but hint list is empty", | |
314 hw_only ? "hw" : ""); | |
315 } | |
316 retval = encode_krb5_padata_sequence((const krb5_pa_data **) pa_data, | |
317 &edat); | |
318 if (retval) | |
319 goto errout; | |
320 *e_data = *edat; | |
321 free(edat); | |
322 | |
323 errout: | |
324 krb5_free_pa_data(kdc_context, pa_data); | |
325 return; | |
326 } | |
327 | |
328 /* | |
329 * This routine is called to verify the preauthentication information | |
330 * for a V5 request. | |
331 * | |
332 * Returns 0 if the pre-authentication is valid, non-zero to indicate | |
333 * an error code of some sort. | |
334 */ | |
335 | |
336 krb5_error_code | |
337 check_padata (krb5_context context, krb5_db_entry *client, | |
338 krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply) | |
339 { | |
340 krb5_error_code retval = 0; | |
341 krb5_pa_data **padata; | |
342 krb5_preauth_systems *pa_sys; | |
343 int pa_ok = 0, pa_found = 0; | |
344 | |
345 if (request->padata == 0) | |
346 return 0; | |
347 | |
348 #ifdef DEBUG | |
349 krb5_klog_syslog (LOG_DEBUG, "checking padata"); | |
350 #endif | |
351 for (padata = request->padata; *padata; padata++) { | |
352 #ifdef DEBUG | |
353 krb5_klog_syslog (LOG_DEBUG, ".. pa_type 0x%x", (*padata)->pa_type); | |
354 #endif | |
355 if (find_pa_system((*padata)->pa_type, &pa_sys)) | |
356 continue; | |
357 #ifdef DEBUG | |
358 krb5_klog_syslog (LOG_DEBUG, ".. pa_type %s", pa_sys->name); | |
359 #endif | |
360 if (pa_sys->verify_padata == 0) | |
361 continue; | |
362 pa_found++; | |
363 retval = pa_sys->verify_padata(context, client, request, | |
364 enc_tkt_reply, *padata); | |
365 if (retval) { | |
366 krb5_klog_syslog (LOG_INFO, "preauth (%s) verify failure: %s", | |
367 pa_sys->name, error_message (retval)); | |
368 if (pa_sys->flags & PA_REQUIRED) { | |
369 pa_ok = 0; | |
370 break; | |
371 } | |
372 } else { | |
373 #ifdef DEBUG | |
374 krb5_klog_syslog (LOG_DEBUG, ".. .. ok"); | |
375 #endif | |
376 pa_ok = 1; | |
377 if (pa_sys->flags & PA_SUFFICIENT) | |
378 break; | |
379 } | |
380 } | |
381 if (pa_ok) | |
382 return 0; | |
383 | |
384 /* pa system was not found, but principal doesn't require preauth */ | |
385 if (!pa_found && | |
386 !isflagset(client->attributes, KRB5_KDB_REQUIRES_PRE_AUTH) && | |
387 !isflagset(client->attributes, KRB5_KDB_REQUIRES_HW_AUTH)) | |
388 return 0; | |
389 | |
390 if (!pa_found) | |
391 krb5_klog_syslog (LOG_INFO, "no valid preauth type found: %s", | |
392 error_message (retval)); | |
393 /* The following switch statement allows us | |
394 * to return some preauth system errors back to the client. | |
395 */ | |
396 switch(retval) { | |
397 case KRB5KRB_AP_ERR_BAD_INTEGRITY: | |
398 case KRB5KRB_AP_ERR_SKEW: | |
399 return retval; | |
400 default: | |
401 return KRB5KDC_ERR_PREAUTH_FAILED; | |
402 } | |
403 } | |
404 | |
405 /* | |
406 * return_padata creates any necessary preauthentication | |
407 * structures which should be returned by the KDC to the client | |
408 */ | |
409 krb5_error_code | |
410 return_padata(krb5_context context, krb5_db_entry *client, | |
411 krb5_kdc_req *request, krb5_kdc_rep *reply, | |
412 krb5_key_data *client_key, krb5_keyblock *encrypting_key) | |
413 { | |
414 krb5_error_code retval; | |
415 krb5_pa_data ** padata; | |
416 krb5_pa_data ** send_pa_list; | |
417 krb5_pa_data ** send_pa; | |
418 krb5_pa_data * pa = 0; | |
419 krb5_preauth_systems * ap; | |
420 int size = 0; | |
421 | |
422 for (ap = preauth_systems; ap->type != -1; ap++) { | |
423 if (ap->return_padata) | |
424 size++; | |
425 } | |
426 | |
427 if ((send_pa_list = malloc((size+1) * sizeof(krb5_pa_data *))) == NULL) | |
428 return ENOMEM; | |
429 | |
430 send_pa = send_pa_list; | |
431 *send_pa = 0; | |
432 | |
433 for (ap = preauth_systems; ap->type != -1; ap++) { | |
434 if (ap->return_padata == 0) | |
435 continue; | |
436 pa = 0; | |
437 if (request->padata) { | |
438 for (padata = request->padata; *padata; padata++) { | |
439 if ((*padata)->pa_type == ap->type) { | |
440 pa = *padata; | |
441 break; | |
442 } | |
443 } | |
444 } | |
445 if ((retval = ap->return_padata(context, pa, client, request, reply, | |
446 client_key, encrypting_key, send_pa))) | |
447 goto cleanup; | |
448 | |
449 if (*send_pa) | |
450 send_pa++; | |
451 *send_pa = 0; | |
452 } | |
453 | |
454 retval = 0; | |
455 | |
456 if (send_pa_list[0]) { | |
457 reply->padata = send_pa_list; | |
458 send_pa_list = 0; | |
459 } | |
460 | |
461 cleanup: | |
462 if (send_pa_list) | |
463 krb5_free_pa_data(context, send_pa_list); | |
464 return (retval); | |
465 } | |
466 | |
467 static krb5_boolean | |
468 enctype_requires_etype_info_2(krb5_enctype enctype) | |
469 { | |
470 switch(enctype) { | |
471 case ENCTYPE_DES_CBC_CRC: | |
472 case ENCTYPE_DES_CBC_MD4: | |
473 case ENCTYPE_DES_CBC_MD5: | |
474 case ENCTYPE_DES3_CBC_SHA1: | |
475 case ENCTYPE_DES3_CBC_RAW: | |
476 case ENCTYPE_ARCFOUR_HMAC: | |
477 case ENCTYPE_ARCFOUR_HMAC_EXP : | |
478 return 0; | |
479 default: | |
480 if (krb5_c_valid_enctype(enctype)) | |
481 return 1; | |
482 else return 0; | |
483 } | |
484 } | |
485 | |
486 static krb5_boolean | |
487 request_contains_enctype (krb5_context context, const krb5_kdc_req *request, | |
488 krb5_enctype enctype) | |
489 { | |
490 int i; | |
491 for (i =0; i < request->nktypes; i++) | |
492 if (request->ktype[i] == enctype) | |
493 return 1; | |
494 return 0; | |
495 } | |
496 | |
497 | |
498 static krb5_error_code | |
499 verify_enc_timestamp(krb5_context context, krb5_db_entry *client, | |
500 krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply, | |
501 krb5_pa_data *pa) | |
502 { | |
503 krb5_pa_enc_ts * pa_enc = 0; | |
504 krb5_error_code retval; | |
505 krb5_data scratch; | |
506 krb5_data enc_ts_data; | |
507 krb5_enc_data *enc_data = 0; | |
508 krb5_keyblock key; | |
509 krb5_key_data * client_key; | |
510 krb5_int32 start; | |
511 krb5_timestamp timenow; | |
512 krb5_error_code decrypt_err; | |
513 | |
514 (void) memset(&key, 0, sizeof(krb5_keyblock)); | |
515 scratch.data = (char *) pa->contents; | |
516 scratch.length = pa->length; | |
517 | |
518 enc_ts_data.data = 0; | |
519 | |
520 if ((retval = decode_krb5_enc_data(&scratch, &enc_data)) != 0) | |
521 goto cleanup; | |
522 | |
523 enc_ts_data.length = enc_data->ciphertext.length; | |
524 if ((enc_ts_data.data = (char *) malloc(enc_ts_data.length)) == NULL) | |
525 goto cleanup; | |
526 | |
527 start = 0; | |
528 decrypt_err = 0; | |
529 while (1) { | |
530 if ((retval = krb5_dbe_search_enctype(context, client, | |
531 &start, enc_data->enctype, | |
532 -1, 0, &client_key))) | |
533 goto cleanup; | |
534 | |
535 if ((retval = krb5_dbekd_decrypt_key_data(context, &master_keyblock, | |
536 client_key, &key, NULL))) | |
537 goto cleanup; | |
538 | |
539 key.enctype = enc_data->enctype; | |
540 | |
541 retval = krb5_c_decrypt(context, &key, KRB5_KEYUSAGE_AS_REQ_PA_ENC_TS, | |
542 0, enc_data, &enc_ts_data); | |
543 krb5_free_keyblock_contents(context, &key); | |
544 if (retval == 0) | |
545 break; | |
546 else | |
547 decrypt_err = retval; | |
548 } | |
549 | |
550 if ((retval = decode_krb5_pa_enc_ts(&enc_ts_data, &pa_enc)) != 0) | |
551 goto cleanup; | |
552 | |
553 if ((retval = krb5_timeofday(context, &timenow)) != 0) | |
554 goto cleanup; | |
555 | |
556 if (labs(timenow - pa_enc->patimestamp) > context->clockskew) { | |
557 retval = KRB5KRB_AP_ERR_SKEW; | |
558 goto cleanup; | |
559 } | |
560 | |
561 setflag(enc_tkt_reply->flags, TKT_FLG_PRE_AUTH); | |
562 | |
563 retval = 0; | |
564 | |
565 cleanup: | |
566 if (enc_data) { | |
567 krb5_free_data_contents(context, &enc_data->ciphertext); | |
568 free(enc_data); | |
569 } | |
570 krb5_free_data_contents(context, &enc_ts_data); | |
571 if (pa_enc) | |
572 free(pa_enc); | |
573 /* | |
574 * If we get NO_MATCHING_KEY and decryption previously failed, and | |
575 * we failed to find any other keys of the correct enctype after | |
576 * that failed decryption, it probably means that the password was | |
577 * incorrect. | |
578 */ | |
579 if (retval == KRB5_KDB_NO_MATCHING_KEY && decrypt_err != 0) | |
580 retval = decrypt_err; | |
581 return retval; | |
582 } | |
583 | |
584 static krb5_error_code | |
585 _make_etype_info_entry(krb5_context context, | |
586 krb5_kdc_req *request, krb5_key_data *client_key, | |
587 krb5_enctype etype, krb5_etype_info_entry **entry, | |
588 int etype_info2) | |
589 { | |
590 krb5_data salt; | |
591 krb5_etype_info_entry * tmp_entry; | |
592 krb5_error_code retval; | |
593 | |
594 if ((tmp_entry = malloc(sizeof(krb5_etype_info_entry))) == NULL) | |
595 return ENOMEM; | |
596 | |
597 salt.data = 0; | |
598 | |
599 tmp_entry->magic = KV5M_ETYPE_INFO_ENTRY; | |
600 tmp_entry->etype = etype; | |
601 tmp_entry->length = KRB5_ETYPE_NO_SALT; | |
602 tmp_entry->salt = 0; | |
603 tmp_entry->s2kparams.data = NULL; | |
604 tmp_entry->s2kparams.length = 0; | |
605 retval = get_salt_from_key(context, request->client, | |
606 client_key, &salt); | |
607 if (retval) | |
608 goto fail; | |
609 if (etype_info2 && client_key->key_data_ver > 1 && | |
610 client_key->key_data_type[1] == KRB5_KDB_SALTTYPE_AFS3) { | |
611 switch (etype) { | |
612 case ENCTYPE_DES_CBC_CRC: | |
613 case ENCTYPE_DES_CBC_MD4: | |
614 case ENCTYPE_DES_CBC_MD5: | |
615 tmp_entry->s2kparams.data = malloc(1); | |
616 if (tmp_entry->s2kparams.data == NULL) { | |
617 retval = ENOMEM; | |
618 goto fail; | |
619 } | |
620 tmp_entry->s2kparams.length = 1; | |
621 tmp_entry->s2kparams.data[0] = 1; | |
622 break; | |
623 default: | |
624 break; | |
625 } | |
626 } | |
627 | |
628 if (salt.length >= 0) { | |
629 tmp_entry->length = salt.length; | |
630 tmp_entry->salt = (unsigned char *) salt.data; | |
631 salt.data = 0; | |
632 } | |
633 *entry = tmp_entry; | |
634 return 0; | |
635 | |
636 fail: | |
637 if (tmp_entry) { | |
638 if (tmp_entry->s2kparams.data) | |
639 free(tmp_entry->s2kparams.data); | |
640 free(tmp_entry); | |
641 } | |
642 if (salt.data) | |
643 free(salt.data); | |
644 return retval; | |
645 } | |
646 /* | |
647 * This function returns the etype information for a particular | |
648 * client, to be passed back in the preauth list in the KRB_ERROR | |
649 * message. It supports generating both etype_info and etype_info2 | |
650 * as most of the work is the same. | |
651 */ | |
652 static krb5_error_code | |
653 etype_info_helper(krb5_context context, krb5_kdc_req *request, | |
654 krb5_db_entry *client, krb5_db_entry *server, | |
655 krb5_pa_data *pa_data, int etype_info2) | |
656 { | |
657 krb5_etype_info_entry ** entry = 0; | |
658 krb5_key_data *client_key; | |
659 krb5_error_code retval; | |
660 krb5_data * scratch; | |
661 krb5_enctype db_etype; | |
662 int i = 0; | |
663 int start = 0; | |
664 int seen_des = 0; | |
665 | |
666 entry = malloc((client->n_key_data * 2 + 1) * sizeof(krb5_etype_info_entry *)); | |
667 if (entry == NULL) | |
668 return ENOMEM; | |
669 entry[0] = NULL; | |
670 | |
671 while (1) { | |
672 retval = krb5_dbe_search_enctype(context, client, &start, -1, | |
673 -1, 0, &client_key); | |
674 if (retval == KRB5_KDB_NO_MATCHING_KEY) | |
675 break; | |
676 if (retval) | |
677 goto cleanup; | |
678 db_etype = client_key->key_data_type[0]; | |
679 if (db_etype == ENCTYPE_DES_CBC_MD4) | |
680 db_etype = ENCTYPE_DES_CBC_MD5; | |
681 | |
682 if (request_contains_enctype(context, request, db_etype)) { | |
683 assert(etype_info2 || | |
684 !enctype_requires_etype_info_2(db_etype)); | |
685 if ((retval = _make_etype_info_entry(context, request, client_key, | |
686 db_etype, &entry[i], etype_info2)) != 0) { | |
687 goto cleanup; | |
688 } | |
689 entry[i+1] = 0; | |
690 i++; | |
691 } | |
692 | |
693 /* | |
694 * If there is a des key in the kdb, try the "similar" enctypes, | |
695 * avoid duplicate entries. | |
696 */ | |
697 if (!seen_des) { | |
698 switch (db_etype) { | |
699 case ENCTYPE_DES_CBC_MD5: | |
700 db_etype = ENCTYPE_DES_CBC_CRC; | |
701 break; | |
702 case ENCTYPE_DES_CBC_CRC: | |
703 db_etype = ENCTYPE_DES_CBC_MD5; | |
704 break; | |
705 default: | |
706 continue; | |
707 | |
708 } | |
709 if (request_contains_enctype(context, request, db_etype)) { | |
710 if ((retval = _make_etype_info_entry(context, request, | |
711 client_key, db_etype, &entry[i], etype_info2)) != 0) { | |
712 goto cleanup; | |
713 } | |
714 entry[i+1] = 0; | |
715 i++; | |
716 } | |
717 seen_des++; | |
718 } | |
719 } | |
720 if (etype_info2) | |
721 retval = encode_krb5_etype_info2((const krb5_etype_info_entry **) entry, | |
722 &scratch); | |
723 else retval = encode_krb5_etype_info((const krb5_etype_info_entry **) entry, | |
724 &scratch); | |
725 if (retval) | |
726 goto cleanup; | |
727 pa_data->contents = (unsigned char *)scratch->data; | |
728 pa_data->length = scratch->length; | |
729 /* | |
730 * note, don't free scratch->data as it is in use (don't use | |
731 * krb5_free_data() either). | |
732 */ | |
733 free(scratch); | |
734 | |
735 retval = 0; | |
736 | |
737 cleanup: | |
738 if (entry) | |
739 krb5_free_etype_info(context, entry); | |
740 return retval; | |
741 } | |
742 | |
743 static krb5_error_code | |
744 get_etype_info(krb5_context context, krb5_kdc_req *request, | |
745 krb5_db_entry *client, krb5_db_entry *server, | |
746 krb5_pa_data *pa_data) | |
747 { | |
748 int i; | |
749 for (i=0; i < request->nktypes; i++) { | |
750 if (enctype_requires_etype_info_2(request->ktype[i])) | |
751 return KRB5KDC_ERR_PADATA_TYPE_NOSUPP ;;;; /*Caller will | |
752 * skip this | |
753 * type*/ | |
754 } | |
755 return etype_info_helper(context, request, client, server, pa_data, 0); | |
756 } | |
757 | |
758 static krb5_error_code | |
759 get_etype_info2(krb5_context context, krb5_kdc_req *request, | |
760 krb5_db_entry *client, krb5_db_entry *server, | |
761 krb5_pa_data *pa_data) | |
762 { | |
763 return etype_info_helper( context, request, client, server, pa_data, 1); | |
764 } | |
765 | |
766 static krb5_error_code | |
767 return_etype_info2(krb5_context context, krb5_pa_data * padata, | |
768 krb5_db_entry *client, | |
769 krb5_kdc_req *request, krb5_kdc_rep *reply, | |
770 krb5_key_data *client_key, | |
771 krb5_keyblock *encrypting_key, | |
772 krb5_pa_data **send_pa) | |
773 { | |
774 krb5_error_code retval; | |
775 krb5_pa_data *tmp_padata; | |
776 krb5_etype_info_entry **entry = NULL; | |
777 krb5_data *scratch = NULL; | |
778 tmp_padata = malloc( sizeof(krb5_pa_data)); | |
779 if (tmp_padata == NULL) | |
780 return ENOMEM; | |
781 tmp_padata->pa_type = KRB5_PADATA_ETYPE_INFO2; | |
782 entry = malloc(2 * sizeof(krb5_etype_info_entry *)); | |
783 if (entry == NULL) { | |
784 retval = ENOMEM; | |
785 goto cleanup; | |
786 } | |
787 entry[0] = NULL; | |
788 entry[1] = NULL; | |
789 /* using encrypting_key->enctype as this is specified in rfc4120 */ | |
790 retval = _make_etype_info_entry(context, request, client_key, encrypting_key->enctype, | |
791 entry, 1); | |
792 if (retval) | |
793 goto cleanup; | |
794 retval = encode_krb5_etype_info2((const krb5_etype_info_entry **) entry, &scratch); | |
795 if (retval) | |
796 goto cleanup; | |
797 tmp_padata->contents = (uchar_t *)scratch->data; | |
798 tmp_padata->length = scratch->length; | |
799 *send_pa = tmp_padata; | |
800 | |
801 /* For cleanup - we no longer own the contents of the krb5_data | |
802 * only to pointer to the krb5_data | |
803 */ | |
804 scratch->data = 0; | |
805 | |
806 cleanup: | |
807 if (entry) | |
808 krb5_free_etype_info(context, entry); | |
809 if (retval) { | |
810 if (tmp_padata) | |
811 free(tmp_padata); | |
812 } | |
813 if (scratch) | |
814 krb5_free_data(context, scratch); | |
815 return retval; | |
816 } | |
817 | |
818 | |
819 static krb5_error_code | |
820 return_pw_salt(krb5_context context, krb5_pa_data *in_padata, | |
821 krb5_db_entry *client, krb5_kdc_req *request, | |
822 krb5_kdc_rep *reply, krb5_key_data *client_key, | |
823 krb5_keyblock *encrypting_key, krb5_pa_data **send_pa) | |
824 { | |
825 krb5_error_code retval; | |
826 krb5_pa_data * padata; | |
827 krb5_data * scratch; | |
828 krb5_data salt_data; | |
829 int i; | |
830 | |
831 for (i = 0; i < request->nktypes; i++) { | |
832 if (enctype_requires_etype_info_2(request->ktype[i])) | |
833 return 0; | |
834 } | |
835 if (client_key->key_data_ver == 1 || | |
836 client_key->key_data_type[1] == KRB5_KDB_SALTTYPE_NORMAL) | |
837 return 0; | |
838 | |
839 if ((padata = malloc(sizeof(krb5_pa_data))) == NULL) | |
840 return ENOMEM; | |
841 padata->magic = KV5M_PA_DATA; | |
842 padata->pa_type = KRB5_PADATA_PW_SALT; | |
843 | |
844 switch (client_key->key_data_type[1]) { | |
845 case KRB5_KDB_SALTTYPE_V4: | |
846 /* send an empty (V4) salt */ | |
847 padata->contents = 0; | |
848 padata->length = 0; | |
849 break; | |
850 case KRB5_KDB_SALTTYPE_NOREALM: | |
851 if ((retval = krb5_principal2salt_norealm(kdc_context, | |
852 request->client, | |
853 &salt_data))) | |
854 goto cleanup; | |
855 padata->contents = (krb5_octet *)salt_data.data; | |
856 padata->length = salt_data.length; | |
857 break; | |
858 case KRB5_KDB_SALTTYPE_AFS3: | |
859 /* send an AFS style realm-based salt */ | |
860 /* for now, just pass the realm back and let the client | |
861 do the work. In the future, add a kdc configuration | |
862 variable that specifies the old cell name. */ | |
863 padata->pa_type = KRB5_PADATA_AFS3_SALT; | |
864 /* it would be just like ONLYREALM, but we need to pass the 0 */ | |
865 scratch = krb5_princ_realm(kdc_context, request->client); | |
866 if ((padata->contents = malloc(scratch->length+1)) == NULL) { | |
867 retval = ENOMEM; | |
868 goto cleanup; | |
869 } | |
870 memcpy(padata->contents, scratch->data, scratch->length); | |
871 padata->length = scratch->length+1; | |
872 padata->contents[scratch->length] = 0; | |
873 break; | |
874 case KRB5_KDB_SALTTYPE_ONLYREALM: | |
875 scratch = krb5_princ_realm(kdc_context, request->client); | |
876 if ((padata->contents = malloc(scratch->length)) == NULL) { | |
877 retval = ENOMEM; | |
878 goto cleanup; | |
879 } | |
880 memcpy(padata->contents, scratch->data, scratch->length); | |
881 padata->length = scratch->length; | |
882 break; | |
883 case KRB5_KDB_SALTTYPE_SPECIAL: | |
884 if ((padata->contents = malloc(client_key->key_data_length[1])) | |
885 == NULL) { | |
886 retval = ENOMEM; | |
887 goto cleanup; | |
888 } | |
889 memcpy(padata->contents, client_key->key_data_contents[1], | |
890 client_key->key_data_length[1]); | |
891 padata->length = client_key->key_data_length[1]; | |
892 break; | |
893 default: | |
894 free(padata); | |
895 return 0; | |
896 } | |
897 | |
898 *send_pa = padata; | |
899 return 0; | |
900 | |
901 cleanup: | |
902 free(padata); | |
903 return retval; | |
904 } | |
905 | |
906 static krb5_error_code | |
907 return_sam_data(krb5_context context, krb5_pa_data *in_padata, | |
908 krb5_db_entry *client, krb5_kdc_req *request, | |
909 krb5_kdc_rep *reply, krb5_key_data *client_key, | |
910 krb5_keyblock *encrypting_key, krb5_pa_data **send_pa) | |
911 { | |
912 krb5_error_code retval; | |
913 krb5_data scratch; | |
914 int i; | |
915 | |
916 krb5_sam_response *sr = 0; | |
917 krb5_predicted_sam_response *psr = 0; | |
918 | |
919 if (in_padata == 0) | |
920 return 0; | |
921 | |
922 /* | |
923 * We start by doing the same thing verify_sam_response() does: | |
924 * extract the psr from the padata (which is an sr). Nothing | |
925 * here should generate errors! We've already successfully done | |
926 * all this once. | |
927 */ | |
928 | |
929 scratch.data = (char *) in_padata->contents; /* SUNWresync121 XXX */ | |
930 scratch.length = in_padata->length; | |
931 | |
932 if ((retval = decode_krb5_sam_response(&scratch, &sr))) { | |
933 com_err("krb5kdc", retval, | |
934 gettext("return_sam_data(): decode_krb5_sam_response failed")); | |
935 goto cleanup; | |
936 } | |
937 | |
938 { | |
939 krb5_enc_data tmpdata; | |
940 | |
941 tmpdata.enctype = ENCTYPE_UNKNOWN; | |
942 tmpdata.ciphertext = sr->sam_track_id; | |
943 | |
944 scratch.length = tmpdata.ciphertext.length; | |
945 if ((scratch.data = (char *) malloc(scratch.length)) == NULL) { | |
946 retval = ENOMEM; | |
947 goto cleanup; | |
948 } | |
949 | |
950 if ((retval = krb5_c_decrypt(context, &psr_key, /* XXX */ 0, 0, | |
951 &tmpdata, &scratch))) { | |
952 com_err("krb5kdc", retval, | |
953 gettext("return_sam_data(): decrypt track_id failed")); | |
954 free(scratch.data); | |
955 goto cleanup; | |
956 } | |
957 } | |
958 | |
959 if ((retval = decode_krb5_predicted_sam_response(&scratch, &psr))) { | |
960 com_err("krb5kdc", retval, | |
961 gettext( | |
962 "return_sam_data(): decode_krb5_predicted_sam_response failed")); | |
963 free(scratch.data); | |
964 goto cleanup; | |
965 } | |
966 | |
967 /* We could use sr->sam_flags, but it may be absent or altered. */ | |
968 if (psr->sam_flags & KRB5_SAM_MUST_PK_ENCRYPT_SAD) { | |
969 com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED, | |
970 gettext("Unsupported SAM flag must-pk-encrypt-sad")); | |
971 goto cleanup; | |
972 } | |
973 if (psr->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD) { | |
974 /* No key munging */ | |
975 goto cleanup; | |
976 } | |
977 if (psr->sam_flags & KRB5_SAM_USE_SAD_AS_KEY) { | |
978 /* Use sam_key instead of client key */ | |
979 krb5_free_keyblock_contents(context, encrypting_key); | |
980 krb5_copy_keyblock_contents(context, &psr->sam_key, encrypting_key); | |
981 /* XXX Attach a useful pa_data */ | |
982 goto cleanup; | |
983 } | |
984 | |
985 /* Otherwise (no flags set), we XOR the keys */ | |
986 /* XXX The passwords-04 draft is underspecified here wrt different | |
987 key types. We will do what I hope to get into the -05 draft. */ | |
988 { | |
989 krb5_octet *p = encrypting_key->contents; | |
990 krb5_octet *q = psr->sam_key.contents; | |
991 int length = ((encrypting_key->length < psr->sam_key.length) | |
992 ? encrypting_key->length | |
993 : psr->sam_key.length); | |
994 | |
995 for (i = 0; i < length; i++) | |
996 p[i] ^= q[i]; | |
997 } | |
998 | |
999 /* Post-mixing key correction */ | |
1000 switch (encrypting_key->enctype) { | |
1001 case ENCTYPE_DES_CBC_CRC: | |
1002 case ENCTYPE_DES_CBC_MD4: | |
1003 case ENCTYPE_DES_CBC_MD5: | |
1004 case ENCTYPE_DES_CBC_RAW: | |
1005 mit_des_fixup_key_parity(encrypting_key->contents); | |
1006 if (mit_des_is_weak_key(encrypting_key->contents)) | |
1007 ((krb5_octet *) encrypting_key->contents)[7] ^= 0xf0; | |
1008 break; | |
1009 | |
1010 /* XXX case ENCTYPE_DES3_CBC_MD5: listed in 1510bis-04 draft */ | |
1011 case ENCTYPE_DES3_CBC_SHA: /* XXX deprecated? */ | |
1012 case ENCTYPE_DES3_CBC_RAW: | |
1013 case ENCTYPE_DES3_CBC_SHA1: | |
1014 for (i = 0; i < 3; i++) { | |
1015 mit_des_fixup_key_parity(encrypting_key->contents + i * 8); | |
1016 if (mit_des_is_weak_key(encrypting_key->contents + i * 8)) | |
1017 ((krb5_octet *) encrypting_key->contents)[7 + i * 8] ^= 0xf0; | |
1018 } | |
1019 break; | |
1020 | |
1021 default: | |
1022 com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED, | |
1023 gettext("Unimplemented keytype for SAM key mixing")); | |
1024 goto cleanup; | |
1025 } | |
1026 | |
1027 /* XXX Attach a useful pa_data */ | |
1028 cleanup: | |
1029 if (sr) | |
1030 krb5_free_sam_response(context, sr); | |
1031 if (psr) | |
1032 krb5_free_predicted_sam_response(context, psr); | |
1033 | |
1034 return retval; | |
1035 } | |
1036 | |
1037 static struct { | |
1038 char* name; | |
1039 int sam_type; | |
1040 } *sam_ptr, sam_inst_map[] = { | |
1041 #if 0 /* SUNWresync121 - unsupported hardware and kdc.log annoyance */ | |
1042 { "SNK4", PA_SAM_TYPE_DIGI_PATH, }, | |
1043 { "SECURID", PA_SAM_TYPE_SECURID, }, | |
1044 { "GRAIL", PA_SAM_TYPE_GRAIL, }, | |
1045 #endif | |
1046 { 0, 0 }, | |
1047 }; | |
1048 | |
1049 static krb5_error_code | |
1050 get_sam_edata(krb5_context context, krb5_kdc_req *request, | |
1051 krb5_db_entry *client, krb5_db_entry *server, | |
1052 krb5_pa_data *pa_data) | |
1053 { | |
1054 krb5_error_code retval; | |
1055 krb5_sam_challenge sc; | |
1056 krb5_predicted_sam_response psr; | |
1057 krb5_data * scratch; | |
1058 krb5_keyblock encrypting_key; | |
1059 char response[9]; | |
1060 char inputblock[8]; | |
1061 krb5_data predict_response; | |
1062 | |
1063 (void) memset(&encrypting_key, 0, sizeof(krb5_keyblock)); | |
1064 (void) memset(&sc, 0, sizeof(sc)); | |
1065 (void) memset(&psr, 0, sizeof(psr)); | |
1066 | |
1067 /* Given the client name we can figure out what type of preauth | |
1068 they need. The spec is currently for querying the database for | |
1069 names that match the types of preauth used. Later we should | |
1070 make this mapping show up in kdc.conf. In the meantime, we | |
1071 hardcode the following: | |
1072 /SNK4 -- Digital Pathways SNK/4 preauth. | |
1073 /GRAIL -- experimental preauth | |
1074 The first one found is used. See sam_inst_map above. | |
1075 | |
1076 For SNK4 in particular, the key in the database is the key for | |
1077 the device; kadmin needs a special interface for it. | |
1078 */ | |
1079 | |
1080 { | |
1081 int npr = 1; | |
1082 krb5_boolean more; | |
1083 krb5_db_entry assoc; | |
1084 krb5_key_data *assoc_key; | |
1085 krb5_principal newp; | |
1086 int probeslot; | |
1087 | |
1088 sc.sam_type = 0; | |
1089 | |
1090 retval = krb5_copy_principal(kdc_context, request->client, &newp); | |
1091 if (retval) { | |
1092 com_err(gettext("krb5kdc"), | |
1093 retval, | |
1094 gettext("copying client name for preauth probe")); | |
1095 return retval; | |
1096 } | |
1097 | |
1098 probeslot = krb5_princ_size(context, newp)++; | |
1099 krb5_princ_name(kdc_context, newp) = | |
1100 realloc(krb5_princ_name(kdc_context, newp), | |
1101 krb5_princ_size(context, newp) * sizeof(krb5_data)); | |
1102 | |
1103 for(sam_ptr = sam_inst_map; sam_ptr->name; sam_ptr++) { | |
1104 krb5_princ_component(kdc_context,newp,probeslot)->data = sam_ptr->name; | |
1105 krb5_princ_component(kdc_context,newp,probeslot)->length = | |
1106 strlen(sam_ptr->name); | |
1107 npr = 1; | |
1108 retval = krb5_db_get_principal(kdc_context, newp, &assoc, &npr, (uint *)&more); | |
1109 if(!retval && npr) { | |
1110 sc.sam_type = sam_ptr->sam_type; | |
1111 break; | |
1112 } | |
1113 } | |
1114 | |
1115 krb5_princ_component(kdc_context,newp,probeslot)->data = 0; | |
1116 krb5_princ_component(kdc_context,newp,probeslot)->length = 0; | |
1117 krb5_princ_size(context, newp)--; | |
1118 | |
1119 krb5_free_principal(kdc_context, newp); | |
1120 | |
1121 /* if sc.sam_type is set, it worked */ | |
1122 if (sc.sam_type) { | |
1123 /* so use assoc to get the key out! */ | |
1124 { | |
1125 /* here's what do_tgs_req does */ | |
1126 retval = krb5_dbe_find_enctype(kdc_context, &assoc, | |
1127 ENCTYPE_DES_CBC_RAW, | |
1128 KRB5_KDB_SALTTYPE_NORMAL, | |
1129 0, /* Get highest kvno */ | |
1130 &assoc_key); | |
1131 if (retval) { | |
1132 char *sname; | |
1133 krb5_unparse_name(kdc_context, request->client, &sname); | |
1134 com_err(gettext("krb5kdc"), | |
1135 retval, | |
1136 gettext("snk4 finding the enctype and key <%s>"), | |
1137 sname); | |
1138 free(sname); | |
1139 return retval; | |
1140 } | |
1141 /* convert server.key into a real key */ | |
1142 retval = krb5_dbekd_decrypt_key_data(kdc_context, | |
1143 &master_keyblock, | |
1144 assoc_key, &encrypting_key, | |
1145 NULL); | |
1146 if (retval) { | |
1147 com_err(gettext("krb5kdc"), | |
1148 retval, | |
1149 gettext("snk4 pulling out key entry")); | |
1150 return retval; | |
1151 } | |
1152 /* now we can use encrypting_key... */ | |
1153 } | |
1154 } else { | |
1155 /* SAM is not an option - so don't return as hint */ | |
1156 return KRB5_PREAUTH_BAD_TYPE; | |
1157 } | |
1158 } | |
1159 sc.magic = KV5M_SAM_CHALLENGE; | |
1160 psr.sam_flags = sc.sam_flags = KRB5_SAM_USE_SAD_AS_KEY; | |
1161 | |
1162 /* Replay prevention */ | |
1163 if ((retval = krb5_copy_principal(context, request->client, &psr.client))) | |
1164 return retval; | |
1165 #ifdef USE_RCACHE | |
1166 if ((retval = krb5_us_timeofday(context, &psr.stime, &psr.susec))) | |
1167 return retval; | |
1168 #endif /* USE_RCACHE */ | |
1169 | |
1170 switch (sc.sam_type) { | |
1171 case PA_SAM_TYPE_GRAIL: | |
1172 sc.sam_type_name.data = "Experimental System"; | |
1173 sc.sam_type_name.length = strlen(sc.sam_type_name.data); | |
1174 sc.sam_challenge_label.data = "experimental challenge label"; | |
1175 sc.sam_challenge_label.length = strlen(sc.sam_challenge_label.data); | |
1176 sc.sam_challenge.data = "12345"; | |
1177 sc.sam_challenge.length = strlen(sc.sam_challenge.data); | |
1178 | |
1179 #if 0 /* Enable this to test "normal" (no flags set) mode. */ | |
1180 psr.sam_flags = sc.sam_flags = 0; | |
1181 #endif | |
1182 | |
1183 psr.magic = KV5M_PREDICTED_SAM_RESPONSE; | |
1184 /* string2key on sc.sam_challenge goes in here */ | |
1185 /* eblock is just to set the enctype */ | |
1186 { | |
1187 const krb5_enctype type = ENCTYPE_DES_CBC_MD5; | |
1188 | |
1189 if ((retval = krb5_c_string_to_key(context, type, &sc.sam_challenge, | |
1190 0 /* salt */, &psr.sam_key))) | |
1191 goto cleanup; | |
1192 | |
1193 if ((retval = encode_krb5_predicted_sam_response(&psr, &scratch))) | |
1194 goto cleanup; | |
1195 | |
1196 { | |
1197 size_t enclen; | |
1198 krb5_enc_data tmpdata; | |
1199 | |
1200 if ((retval = krb5_c_encrypt_length(context, | |
1201 psr_key.enctype, | |
1202 scratch->length, &enclen))) | |
1203 goto cleanup; | |
1204 | |
1205 if ((tmpdata.ciphertext.data = (char *) malloc(enclen)) == NULL) { | |
1206 retval = ENOMEM; | |
1207 goto cleanup; | |
1208 } | |
1209 tmpdata.ciphertext.length = enclen; | |
1210 | |
1211 if ((retval = krb5_c_encrypt(context, &psr_key, | |
1212 /* XXX */ 0, 0, scratch, &tmpdata))) | |
1213 goto cleanup; | |
1214 | |
1215 sc.sam_track_id = tmpdata.ciphertext; | |
1216 } | |
1217 } | |
1218 | |
1219 sc.sam_response_prompt.data = "response prompt"; | |
1220 sc.sam_response_prompt.length = strlen(sc.sam_response_prompt.data); | |
1221 sc.sam_pk_for_sad.length = 0; | |
1222 sc.sam_nonce = 0; | |
1223 /* Generate checksum */ | |
1224 /*krb5_checksum_size(context, ctype)*/ | |
1225 /*krb5_calculate_checksum(context,ctype,in,in_length,seed, | |
1226 seed_length,outcksum) */ | |
1227 /*krb5_verify_checksum(context,ctype,cksum,in,in_length,seed, | |
1228 seed_length) */ | |
1229 #if 0 /* XXX a) glue appears broken; b) this gives up the SAD */ | |
1230 sc.sam_cksum.contents = (krb5_octet *) | |
1231 malloc(krb5_checksum_size(context, CKSUMTYPE_RSA_MD5_DES)); | |
1232 if (sc.sam_cksum.contents == NULL) return(ENOMEM); | |
1233 | |
1234 retval = krb5_calculate_checksum(context, CKSUMTYPE_RSA_MD5_DES, | |
1235 sc.sam_challenge.data, | |
1236 sc.sam_challenge.length, | |
1237 psr.sam_key.contents, /* key */ | |
1238 psr.sam_key.length, /* key length */ | |
1239 &sc.sam_cksum); | |
1240 if (retval) { free(sc.sam_cksum.contents); return(retval); } | |
1241 #endif /* 0 */ | |
1242 | |
1243 retval = encode_krb5_sam_challenge(&sc, &scratch); | |
1244 if (retval) goto cleanup; | |
1245 pa_data->magic = KV5M_PA_DATA; | |
1246 pa_data->pa_type = KRB5_PADATA_SAM_CHALLENGE; | |
1247 pa_data->contents = (unsigned char *) scratch->data; | |
1248 pa_data->length = scratch->length; | |
1249 | |
1250 retval = 0; | |
1251 break; | |
1252 case PA_SAM_TYPE_DIGI_PATH: | |
1253 sc.sam_type_name.data = "Digital Pathways"; | |
1254 sc.sam_type_name.length = strlen(sc.sam_type_name.data); | |
1255 #if 1 | |
1256 sc.sam_challenge_label.data = "Enter the following on your keypad"; | |
1257 sc.sam_challenge_label.length = strlen(sc.sam_challenge_label.data); | |
1258 #endif | |
1259 /* generate digit string, take it mod 1000000 (six digits.) */ | |
1260 { | |
1261 int j; | |
1262 krb5_keyblock session_key; | |
1263 char outputblock[8]; | |
1264 int i; | |
1265 | |
1266 (void) memset(&session_key, 0, sizeof(krb5_keyblock)); | |
1267 | |
1268 (void) memset(inputblock, 0, 8); | |
1269 | |
1270 retval = krb5_c_make_random_key(kdc_context, ENCTYPE_DES_CBC_CRC, | |
1271 &session_key); | |
1272 | |
1273 if (retval) { | |
1274 /* random key failed */ | |
1275 com_err(gettext("krb5kdc"), | |
1276 retval, | |
1277 gettext("generating random challenge for preauth")); | |
1278 return retval; | |
1279 } | |
1280 /* now session_key has a key which we can pick bits out of */ | |
1281 /* we need six decimal digits. Grab 6 bytes, div 2, mod 10 each. */ | |
1282 if (session_key.length != 8) { | |
1283 retval = KRB5KDC_ERR_ETYPE_NOSUPP, | |
1284 com_err(gettext("krb5kdc"), | |
1285 retval = KRB5KDC_ERR_ETYPE_NOSUPP, | |
1286 gettext("keytype didn't match code expectations")); | |
1287 return retval; | |
1288 } | |
1289 for(i = 0; i<6; i++) { | |
1290 inputblock[i] = '0' + ((session_key.contents[i]/2) % 10); | |
1291 } | |
1292 if (session_key.contents) | |
1293 krb5_free_keyblock_contents(kdc_context, &session_key); | |
1294 | |
1295 /* retval = krb5_finish_key(kdc_context, &eblock); */ | |
1296 /* now we have inputblock containing the 8 byte input to DES... */ | |
1297 sc.sam_challenge.data = inputblock; | |
1298 sc.sam_challenge.length = 6; | |
1299 | |
1300 encrypting_key.enctype = ENCTYPE_DES_CBC_RAW; | |
1301 | |
1302 if (retval) { | |
1303 com_err(gettext("krb5kdc"), | |
1304 retval, | |
1305 gettext("snk4 processing key")); | |
1306 } | |
1307 | |
1308 { | |
1309 krb5_data plain; | |
1310 krb5_enc_data cipher; | |
1311 | |
1312 plain.length = 8; | |
1313 plain.data = inputblock; | |
1314 | |
1315 /* XXX I know this is enough because of the fixed raw enctype. | |
1316 if it's not, the underlying code will return a reasonable | |
1317 error, which should never happen */ | |
1318 cipher.ciphertext.length = 8; | |
1319 cipher.ciphertext.data = outputblock; | |
1320 | |
1321 if ((retval = krb5_c_encrypt(kdc_context, &encrypting_key, | |
1322 /* XXX */ 0, 0, &plain, &cipher))) { | |
1323 com_err(gettext("krb5kdc"), | |
1324 retval, | |
1325 gettext("snk4 response generation failed")); | |
1326 return retval; | |
1327 } | |
1328 } | |
1329 | |
1330 /* now output block is the raw bits of the response; convert it | |
1331 to display form */ | |
1332 for (j=0; j<4; j++) { | |
1333 char n[2]; | |
1334 int k; | |
1335 n[0] = outputblock[j] & 0xf; | |
1336 n[1] = (outputblock[j]>>4) & 0xf; | |
1337 for (k=0; k<2; k++) { | |
1338 if(n[k] > 9) n[k] = ((n[k]-1)>>2); | |
1339 /* This is equivalent to: | |
1340 if(n[k]>=0xa && n[k]<=0xc) n[k] = 2; | |
1341 if(n[k]>=0xd && n[k]<=0xf) n[k] = 3; | |
1342 */ | |
1343 } | |
1344 /* for v4, we keygen: *(j+(char*)&key1) = (n[1]<<4) | n[0]; */ | |
1345 /* for v5, we just generate a string */ | |
1346 response[2*j+0] = '0' + n[1]; | |
1347 response[2*j+1] = '0' + n[0]; | |
1348 /* and now, response has what we work with. */ | |
1349 } | |
1350 response[8] = 0; | |
1351 predict_response.data = response; | |
1352 predict_response.length = 8; | |
1353 #if 0 /* for debugging, hack the output too! */ | |
1354 sc.sam_challenge_label.data = response; | |
1355 sc.sam_challenge_label.length = strlen(sc.sam_challenge_label.data); | |
1356 #endif | |
1357 } | |
1358 | |
1359 psr.magic = KV5M_PREDICTED_SAM_RESPONSE; | |
1360 /* string2key on sc.sam_challenge goes in here */ | |
1361 /* eblock is just to set the enctype */ | |
1362 { | |
1363 retval = krb5_c_string_to_key(context, ENCTYPE_DES_CBC_MD5, | |
1364 &predict_response, 0 /* salt */, | |
1365 &psr.sam_key); | |
1366 if (retval) goto cleanup; | |
1367 | |
1368 retval = encode_krb5_predicted_sam_response(&psr, &scratch); | |
1369 if (retval) goto cleanup; | |
1370 | |
1371 { | |
1372 size_t enclen; | |
1373 krb5_enc_data tmpdata; | |
1374 | |
1375 if ((retval = krb5_c_encrypt_length(context, | |
1376 psr_key.enctype, | |
1377 scratch->length, &enclen))) | |
1378 goto cleanup; | |
1379 | |
1380 if ((tmpdata.ciphertext.data = (char *) malloc(enclen)) == NULL) { | |
1381 retval = ENOMEM; | |
1382 goto cleanup; | |
1383 } | |
1384 tmpdata.ciphertext.length = enclen; | |
1385 | |
1386 if ((retval = krb5_c_encrypt(context, &psr_key, | |
1387 /* XXX */ 0, 0, scratch, &tmpdata))) | |
1388 goto cleanup; | |
1389 | |
1390 sc.sam_track_id = tmpdata.ciphertext; | |
1391 } | |
1392 if (retval) goto cleanup; | |
1393 } | |
1394 | |
1395 sc.sam_response_prompt.data = "Enter the displayed response"; | |
1396 sc.sam_response_prompt.length = strlen(sc.sam_response_prompt.data); | |
1397 sc.sam_pk_for_sad.length = 0; | |
1398 sc.sam_nonce = 0; | |
1399 /* Generate checksum */ | |
1400 /*krb5_checksum_size(context, ctype)*/ | |
1401 /*krb5_calculate_checksum(context,ctype,in,in_length,seed, | |
1402 seed_length,outcksum) */ | |
1403 /*krb5_verify_checksum(context,ctype,cksum,in,in_length,seed, | |
1404 seed_length) */ | |
1405 #if 0 /* XXX a) glue appears broken; b) this gives up the SAD */ | |
1406 sc.sam_cksum.contents = (krb5_octet *) | |
1407 malloc(krb5_checksum_size(context, CKSUMTYPE_RSA_MD5_DES)); | |
1408 if (sc.sam_cksum.contents == NULL) return(ENOMEM); | |
1409 | |
1410 retval = krb5_calculate_checksum(context, CKSUMTYPE_RSA_MD5_DES, | |
1411 sc.sam_challenge.data, | |
1412 sc.sam_challenge.length, | |
1413 psr.sam_key.contents, /* key */ | |
1414 psr.sam_key.length, /* key length */ | |
1415 &sc.sam_cksum); | |
1416 if (retval) { free(sc.sam_cksum.contents); return(retval); } | |
1417 #endif /* 0 */ | |
1418 | |
1419 retval = encode_krb5_sam_challenge(&sc, &scratch); | |
1420 if (retval) goto cleanup; | |
1421 pa_data->magic = KV5M_PA_DATA; | |
1422 pa_data->pa_type = KRB5_PADATA_SAM_CHALLENGE; | |
1423 pa_data->contents = (unsigned char *) scratch->data; | |
1424 pa_data->length = scratch->length; | |
1425 | |
1426 retval = 0; | |
1427 break; | |
1428 } | |
1429 | |
1430 cleanup: | |
1431 krb5_free_keyblock_contents(context, &encrypting_key); | |
1432 return retval; | |
1433 } | |
1434 | |
1435 static krb5_error_code | |
1436 verify_sam_response(krb5_context context, krb5_db_entry *client, | |
1437 krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply, | |
1438 krb5_pa_data *pa) | |
1439 { | |
1440 krb5_error_code retval; | |
1441 krb5_data scratch; | |
1442 krb5_sam_response *sr = 0; | |
1443 krb5_predicted_sam_response *psr = 0; | |
1444 krb5_enc_sam_response_enc *esre = 0; | |
1445 krb5_timestamp timenow; | |
1446 char *princ_req = 0, *princ_psr = 0; | |
1447 | |
1448 scratch.data = (char *) pa->contents; | |
1449 scratch.length = pa->length; | |
1450 | |
1451 if ((retval = decode_krb5_sam_response(&scratch, &sr))) { | |
1452 scratch.data = 0; | |
1453 com_err("krb5kdc", retval, gettext("decode_krb5_sam_response failed")); | |
1454 goto cleanup; | |
1455 } | |
1456 | |
1457 /* XXX We can only handle the challenge/response model of SAM. | |
1458 See passwords-04, par 4.1, 4.2 */ | |
1459 { | |
1460 krb5_enc_data tmpdata; | |
1461 | |
1462 tmpdata.enctype = ENCTYPE_UNKNOWN; | |
1463 tmpdata.ciphertext = sr->sam_track_id; | |
1464 | |
1465 scratch.length = tmpdata.ciphertext.length; | |
1466 if ((scratch.data = (char *) malloc(scratch.length)) == NULL) { | |
1467 retval = ENOMEM; | |
1468 goto cleanup; | |
1469 } | |
1470 | |
1471 if ((retval = krb5_c_decrypt(context, &psr_key, /* XXX */ 0, 0, | |
1472 &tmpdata, &scratch))) { | |
1473 com_err(gettext("krb5kdc"), | |
1474 retval, | |
1475 gettext("decrypt track_id failed")); | |
1476 goto cleanup; | |
1477 } | |
1478 } | |
1479 | |
1480 if ((retval = decode_krb5_predicted_sam_response(&scratch, &psr))) { | |
1481 com_err(gettext("krb5kdc"), retval, | |
1482 gettext("decode_krb5_predicted_sam_response failed -- replay attack?")); | |
1483 goto cleanup; | |
1484 } | |
1485 | |
1486 /* Replay detection */ | |
1487 if ((retval = krb5_unparse_name(context, request->client, &princ_req))) | |
1488 goto cleanup; | |
1489 if ((retval = krb5_unparse_name(context, psr->client, &princ_psr))) | |
1490 goto cleanup; | |
1491 if (strcmp(princ_req, princ_psr) != 0) { | |
1492 com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED, | |
1493 gettext("Principal mismatch in SAM psr! -- replay attack?")); | |
1494 goto cleanup; | |
1495 } | |
1496 | |
1497 if ((retval = krb5_timeofday(context, &timenow))) | |
1498 goto cleanup; | |
1499 | |
1500 #ifdef USE_RCACHE | |
1501 { | |
1502 krb5_donot_replay rep; | |
1503 extern krb5_deltat rc_lifetime; | |
1504 /* | |
1505 * Verify this response came back in a timely manner. | |
1506 * We do this b/c otherwise very old (expunged from the rcache) | |
1507 * psr's would be able to be replayed. | |
1508 */ | |
1509 if (timenow - psr->stime > rc_lifetime) { | |
1510 com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED, | |
1511 gettext("SAM psr came back too late! -- replay attack?")); | |
1512 goto cleanup; | |
1513 } | |
1514 | |
1515 /* Now check the replay cache. */ | |
1516 rep.client = princ_psr; | |
1517 rep.server = "SAM/rc"; /* Should not match any principal name. */ | |
1518 rep.ctime = psr->stime; | |
1519 rep.cusec = psr->susec; | |
1520 retval = krb5_rc_store(kdc_context, kdc_rcache, &rep); | |
1521 if (retval) { | |
1522 com_err("krb5kdc", retval, gettext("SAM psr replay attack!")); | |
1523 goto cleanup; | |
1524 } | |
1525 } | |
1526 #endif /* USE_RCACHE */ | |
1527 | |
1528 | |
1529 { | |
1530 free(scratch.data); | |
1531 scratch.length = sr->sam_enc_nonce_or_ts.ciphertext.length; | |
1532 if ((scratch.data = (char *) malloc(scratch.length)) == NULL) { | |
1533 retval = ENOMEM; | |
1534 goto cleanup; | |
1535 } | |
1536 | |
1537 if ((retval = krb5_c_decrypt(context, &psr->sam_key, /* XXX */ 0, | |
1538 0, &sr->sam_enc_nonce_or_ts, &scratch))) { | |
1539 com_err("krb5kdc", retval, gettext("decrypt nonce_or_ts failed")); | |
1540 goto cleanup; | |
1541 } | |
1542 } | |
1543 | |
1544 if ((retval = decode_krb5_enc_sam_response_enc(&scratch, &esre))) { | |
1545 com_err("krb5kdc", retval, gettext("decode_krb5_enc_sam_response_enc failed")); | |
1546 goto cleanup; | |
1547 } | |
1548 | |
1549 if (esre->sam_timestamp != sr->sam_patimestamp) { | |
1550 retval = KRB5KDC_ERR_PREAUTH_FAILED; | |
1551 goto cleanup; | |
1552 } | |
1553 | |
1554 if (labs(timenow - sr->sam_patimestamp) > context->clockskew) { | |
1555 retval = KRB5KRB_AP_ERR_SKEW; | |
1556 goto cleanup; | |
1557 } | |
1558 | |
1559 setflag(enc_tkt_reply->flags, TKT_FLG_HW_AUTH); | |
1560 | |
1561 cleanup: | |
1562 if (retval) com_err(gettext("krb5kdc"), | |
1563 retval, | |
1564 gettext("sam verify failure")); | |
1565 if (scratch.data) free(scratch.data); | |
1566 if (sr) free(sr); | |
1567 if (psr) free(psr); | |
1568 if (esre) free(esre); | |
1569 if (princ_psr) free(princ_psr); | |
1570 if (princ_req) free(princ_req); | |
1571 | |
1572 return retval; | |
1573 } |