1 /*
2 * Copyright 1995 by the Massachusetts Institute of Technology. All
3 * Rights Reserved.
4 *
5 * Export of this software from the United States of America may
6 * require a specific license from the United States Government.
7 * It is the responsibility of any person or organization contemplating
8 * export to obtain such a license before exporting.
9 *
10 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
11 * distribute this software and its documentation for any purpose and
12 * without fee is hereby granted, provided that the above copyright
13 * notice appear in all copies and that both that copyright notice and
14 * this permission notice appear in supporting documentation, and that
15 * the name of M.I.T. not be used in advertising or publicity pertaining
16 * to distribution of the software without specific, written prior
17 * permission. Furthermore if you modify this software you must label
18 * your software as modified software and not distribute it in such a
19 * fashion that it might be confused with the original M.I.T. software.
20 * M.I.T. makes no representations about the suitability of
21 * this software for any purpose. It is provided "as is" without express
22 * or implied warranty.
23 *
24 */
25
26 /*
27 * This file contains routines for establishing, verifying, and any other
28 * necessary functions, for utilizing the pre-authentication field of the
29 * kerberos kdc request, with various hardware/software verification devices.
30 */
31
32 #include "k5-int.h"
33 #include <stdio.h>
34 #include <time.h>
35
36 static krb5_error_code obtain_enc_ts_padata
37 (krb5_context,
38 krb5_pa_data *,
39 krb5_etype_info,
40 krb5_keyblock *,
41 krb5_error_code ( * )(krb5_context,
42 const krb5_enctype,
43 krb5_data *,
44 krb5_const_pointer,
45 krb5_keyblock **),
46 krb5_const_pointer,
47 krb5_creds *,
48 krb5_kdc_req *,
49 krb5_pa_data **);
50
51 static krb5_error_code process_pw_salt
52 (krb5_context,
53 krb5_pa_data *,
54 krb5_kdc_req *,
55 krb5_kdc_rep *,
56 krb5_error_code ( * )(krb5_context,
57 const krb5_enctype,
58 krb5_data *,
59 krb5_const_pointer,
60 krb5_keyblock **),
61 krb5_const_pointer,
62 krb5_error_code ( * )(krb5_context,
63 const krb5_keyblock *,
64 krb5_const_pointer,
65 krb5_kdc_rep * ),
66 krb5_keyblock **,
67 krb5_creds *,
68 krb5_int32 *,
69 krb5_int32 *);
70
71 static krb5_error_code obtain_sam_padata
72 (krb5_context,
73 krb5_pa_data *,
74 krb5_etype_info,
75 krb5_keyblock *,
76 krb5_error_code ( * )(krb5_context,
77 const krb5_enctype,
78 krb5_data *,
79 krb5_const_pointer,
80 krb5_keyblock **),
81 krb5_const_pointer,
82 krb5_creds *,
83 krb5_kdc_req *,
84 krb5_pa_data **);
85
86 static const krb5_preauth_ops preauth_systems[] = {
87 {
88 KV5M_PREAUTH_OPS,
89 KRB5_PADATA_ENC_TIMESTAMP,
90 0,
91 obtain_enc_ts_padata,
92 0,
93 },
94 {
95 KV5M_PREAUTH_OPS,
96 KRB5_PADATA_PW_SALT,
97 0,
98 0,
99 process_pw_salt,
100 },
101 {
102 KV5M_PREAUTH_OPS,
103 KRB5_PADATA_AFS3_SALT,
104 0,
105 0,
106 process_pw_salt,
107 },
108 {
109 KV5M_PREAUTH_OPS,
110 KRB5_PADATA_SAM_CHALLENGE,
111 0,
112 obtain_sam_padata,
113 0,
114 },
115 { KV5M_PREAUTH_OPS, -1 }
116 };
117
118 static krb5_error_code find_pa_system
119 (krb5_preauthtype type, const krb5_preauth_ops **Preauth_proc);
120
121 /* some typedef's for the function args to make things look a bit cleaner */
122
123 typedef krb5_error_code (*git_key_proc) (krb5_context,
124 const krb5_enctype,
125 krb5_data *,
126 krb5_const_pointer,
127 krb5_keyblock **);
128
129 typedef krb5_error_code (*git_decrypt_proc) (krb5_context,
130 const krb5_keyblock *,
131 krb5_const_pointer,
132 krb5_kdc_rep *);
133
krb5_obtain_padata(krb5_context context,krb5_pa_data ** preauth_to_use,git_key_proc key_proc,krb5_const_pointer key_seed,krb5_creds * creds,krb5_kdc_req * request)134 krb5_error_code krb5_obtain_padata(krb5_context context, krb5_pa_data **preauth_to_use, git_key_proc key_proc, krb5_const_pointer key_seed, krb5_creds *creds, krb5_kdc_req *request)
135 {
136 krb5_error_code retval;
137 krb5_etype_info etype_info = 0;
138 krb5_pa_data ** pa;
139 krb5_pa_data ** send_pa_list;
140 krb5_pa_data ** send_pa;
141 const krb5_preauth_ops *ops;
142 krb5_keyblock * def_enc_key = 0;
143 krb5_enctype enctype;
144 krb5_data salt;
145 krb5_data scratch;
146 int size;
147 int f_salt = 0;
148
149 if (preauth_to_use == NULL)
150 return 0;
151
152 for (pa = preauth_to_use, size=0; *pa; pa++, size++) {
153 if ((*pa)->pa_type == KRB5_PADATA_ETYPE_INFO) {
154 /* XXX use the first one. Is there another way to disambiguate? */
155 if (etype_info)
156 continue;
157
158 scratch.length = (*pa)->length;
159 scratch.data = (char *) (*pa)->contents;
160 retval = decode_krb5_etype_info(&scratch, &etype_info);
161 if (retval)
162 return retval;
163 if (etype_info[0] == NULL) {
164 krb5_free_etype_info(context, etype_info);
165 etype_info = NULL;
166 }
167 }
168 }
169
170 if ((send_pa_list = malloc((size+1) * sizeof(krb5_pa_data *))) == NULL)
171 return ENOMEM;
172
173 send_pa = send_pa_list;
174 *send_pa = 0;
175
176 enctype = request->ktype[0];
177 salt.data = 0;
178 salt.length = SALT_TYPE_NO_LENGTH;
179 if (etype_info) {
180 enctype = etype_info[0]->etype;
181 salt.data = (char *) etype_info[0]->salt;
182 if(etype_info[0]->length == KRB5_ETYPE_NO_SALT)
183 salt.length = SALT_TYPE_NO_LENGTH; /* XXX */
184 else
185 salt.length = etype_info[0]->length;
186 }
187 if (salt.length == SALT_TYPE_NO_LENGTH) {
188 /*
189 * This will set the salt length
190 */
191 if ((retval = krb5_principal2salt(context, request->client, &salt)))
192 return(retval);
193 f_salt = 1;
194 }
195
196 if ((retval = (*key_proc)(context, enctype, &salt, key_seed,
197 &def_enc_key)))
198 goto cleanup;
199
200
201 for (pa = preauth_to_use; *pa; pa++) {
202 if (find_pa_system((*pa)->pa_type, &ops))
203 continue;
204
205 if (ops->obtain == 0)
206 continue;
207
208 retval = ((ops)->obtain)(context, *pa, etype_info, def_enc_key,
209 key_proc, key_seed, creds,
210 request, send_pa);
211 if (retval)
212 goto cleanup;
213
214 if (*send_pa)
215 send_pa++;
216 *send_pa = 0;
217 }
218
219 retval = 0;
220
221 if (send_pa_list[0]) {
222 request->padata = send_pa_list;
223 send_pa_list = 0;
224 }
225
226 cleanup:
227 if (etype_info)
228 krb5_free_etype_info(context, etype_info);
229 if (f_salt)
230 krb5_xfree(salt.data);
231 if (send_pa_list)
232 krb5_free_pa_data(context, send_pa_list);
233 if (def_enc_key)
234 krb5_free_keyblock(context, def_enc_key);
235 return retval;
236
237 }
238
239 krb5_error_code
krb5_process_padata(krb5_context context,krb5_kdc_req * request,krb5_kdc_rep * as_reply,git_key_proc key_proc,krb5_const_pointer keyseed,git_decrypt_proc decrypt_proc,krb5_keyblock ** decrypt_key,krb5_creds * creds,krb5_int32 * do_more)240 krb5_process_padata(krb5_context context, krb5_kdc_req *request, krb5_kdc_rep *as_reply, git_key_proc key_proc, krb5_const_pointer keyseed, git_decrypt_proc decrypt_proc, krb5_keyblock **decrypt_key, krb5_creds *creds, krb5_int32 *do_more)
241 {
242 krb5_error_code retval = 0;
243 const krb5_preauth_ops * ops;
244 krb5_pa_data ** pa;
245 krb5_int32 done = 0;
246
247 *do_more = 0; /* By default, we don't need to repeat... */
248 if (as_reply->padata == 0)
249 return 0;
250
251 for (pa = as_reply->padata; *pa; pa++) {
252 if (find_pa_system((*pa)->pa_type, &ops))
253 continue;
254
255 if (ops->process == 0)
256 continue;
257
258 retval = ((ops)->process)(context, *pa, request, as_reply,
259 key_proc, keyseed, decrypt_proc,
260 decrypt_key, creds, do_more, &done);
261 if (retval)
262 goto cleanup;
263 if (done)
264 break;
265 }
266
267 cleanup:
268 return retval;
269 }
270
271 /*
272 * This routine is the "obtain" function for the ENC_TIMESTAMP
273 * preauthentication type. It take the current time and encrypts it
274 * in the user's key.
275 */
276 static krb5_error_code
obtain_enc_ts_padata(krb5_context context,krb5_pa_data * in_padata,krb5_etype_info etype_info,krb5_keyblock * def_enc_key,git_key_proc key_proc,krb5_const_pointer key_seed,krb5_creds * creds,krb5_kdc_req * request,krb5_pa_data ** out_padata)277 obtain_enc_ts_padata(krb5_context context, krb5_pa_data *in_padata, krb5_etype_info etype_info, krb5_keyblock *def_enc_key, git_key_proc key_proc, krb5_const_pointer key_seed, krb5_creds *creds, krb5_kdc_req *request, krb5_pa_data **out_padata)
278 {
279 krb5_pa_enc_ts pa_enc;
280 krb5_error_code retval;
281 krb5_data * scratch;
282 krb5_enc_data enc_data;
283 krb5_pa_data * pa;
284
285 retval = krb5_us_timeofday(context, &pa_enc.patimestamp, &pa_enc.pausec);
286 if (retval)
287 return retval;
288
289 if ((retval = encode_krb5_pa_enc_ts(&pa_enc, &scratch)) != 0)
290 return retval;
291
292 enc_data.ciphertext.data = 0;
293
294 if ((retval = krb5_encrypt_helper(context, def_enc_key,
295 KRB5_KEYUSAGE_AS_REQ_PA_ENC_TS,
296 scratch, &enc_data)))
297 goto cleanup;
298
299 krb5_free_data(context, scratch);
300 scratch = 0;
301
302 if ((retval = encode_krb5_enc_data(&enc_data, &scratch)) != 0)
303 goto cleanup;
304
305 if ((pa = malloc(sizeof(krb5_pa_data))) == NULL) {
306 retval = ENOMEM;
307 goto cleanup;
308 }
309
310 pa->magic = KV5M_PA_DATA;
311 pa->pa_type = KRB5_PADATA_ENC_TIMESTAMP;
312 pa->length = scratch->length;
313 pa->contents = (krb5_octet *) scratch->data;
314
315 *out_padata = pa;
316
317 krb5_xfree(scratch);
318 scratch = 0;
319
320 retval = 0;
321
322 cleanup:
323 if (scratch)
324 krb5_free_data(context, scratch);
325 if (enc_data.ciphertext.data)
326 krb5_xfree(enc_data.ciphertext.data);
327 return retval;
328 }
329
330 static krb5_error_code
process_pw_salt(krb5_context context,krb5_pa_data * padata,krb5_kdc_req * request,krb5_kdc_rep * as_reply,git_key_proc key_proc,krb5_const_pointer keyseed,git_decrypt_proc decrypt_proc,krb5_keyblock ** decrypt_key,krb5_creds * creds,krb5_int32 * do_more,krb5_int32 * done)331 process_pw_salt(krb5_context context, krb5_pa_data *padata, krb5_kdc_req *request, krb5_kdc_rep *as_reply, git_key_proc key_proc, krb5_const_pointer keyseed, git_decrypt_proc decrypt_proc, krb5_keyblock **decrypt_key, krb5_creds *creds, krb5_int32 *do_more, krb5_int32 *done)
332 {
333 krb5_error_code retval;
334 krb5_data salt;
335
336 if (*decrypt_key != 0)
337 return 0;
338
339 salt.data = (char *) padata->contents;
340 salt.length =
341 (padata->pa_type == KRB5_PADATA_AFS3_SALT)?(SALT_TYPE_AFS_LENGTH):(padata->length);
342
343 if ((retval = (*key_proc)(context, as_reply->enc_part.enctype,
344 &salt, keyseed, decrypt_key))) {
345 *decrypt_key = 0;
346 return retval;
347 }
348
349 return 0;
350 }
351
352 static krb5_error_code
find_pa_system(krb5_preauthtype type,const krb5_preauth_ops ** preauth)353 find_pa_system(krb5_preauthtype type, const krb5_preauth_ops **preauth)
354 {
355 const krb5_preauth_ops *ap = preauth_systems;
356
357 while ((ap->type != -1) && (ap->type != type))
358 ap++;
359 if (ap->type == -1)
360 return(KRB5_PREAUTH_BAD_TYPE);
361 *preauth = ap;
362 return 0;
363 }
364
365
366 extern const char *krb5_default_pwd_prompt1;
367
368 static krb5_error_code
sam_get_pass_from_user(krb5_context context,krb5_etype_info etype_info,git_key_proc key_proc,krb5_const_pointer key_seed,krb5_kdc_req * request,krb5_keyblock ** new_enc_key,const char * prompt)369 sam_get_pass_from_user(krb5_context context, krb5_etype_info etype_info, git_key_proc key_proc, krb5_const_pointer key_seed, krb5_kdc_req *request, krb5_keyblock **new_enc_key, const char *prompt)
370 {
371 krb5_enctype enctype;
372 krb5_error_code retval;
373 const char *oldprompt;
374
375 /* enctype = request->ktype[0]; */
376 enctype = ENCTYPE_DES_CBC_MD5;
377 /* hack with this first! */
378 oldprompt = krb5_default_pwd_prompt1;
379 krb5_default_pwd_prompt1 = prompt;
380 {
381 krb5_data newpw;
382 newpw.data = 0; newpw.length = 0;
383 /* we don't keep the new password, just the key... */
384 retval = (*key_proc)(context, enctype, 0,
385 (krb5_const_pointer)&newpw, new_enc_key);
386 krb5_xfree(newpw.data);
387 }
388 krb5_default_pwd_prompt1 = oldprompt;
389 return retval;
390 }
391 static
handle_sam_labels(krb5_sam_challenge * sc)392 char *handle_sam_labels(krb5_sam_challenge *sc)
393 {
394 char *label = sc->sam_challenge_label.data;
395 unsigned int label_len = sc->sam_challenge_label.length;
396 char *prompt = sc->sam_response_prompt.data;
397 unsigned int prompt_len = sc->sam_response_prompt.length;
398 char *challenge = sc->sam_challenge.data;
399 unsigned int challenge_len = sc->sam_challenge.length;
400 char *prompt1, *p;
401 char *sep1 = ": [";
402 char *sep2 = "]\n";
403 char *sep3 = ": ";
404
405 if (sc->sam_cksum.length == 0) {
406 /* or invalid -- but lets just handle presence now XXX */
407 switch (sc->sam_type) {
408 case PA_SAM_TYPE_ENIGMA: /* Enigma Logic */
409 label = "Challenge for Enigma Logic mechanism";
410 break;
411 case PA_SAM_TYPE_DIGI_PATH: /* Digital Pathways */
412 case PA_SAM_TYPE_DIGI_PATH_HEX: /* Digital Pathways */
413 label = "Challenge for Digital Pathways mechanism";
414 break;
415 case PA_SAM_TYPE_ACTIVCARD_DEC: /* Digital Pathways */
416 case PA_SAM_TYPE_ACTIVCARD_HEX: /* Digital Pathways */
417 label = "Challenge for Activcard mechanism";
418 break;
419 case PA_SAM_TYPE_SKEY_K0: /* S/key where KDC has key 0 */
420 label = "Challenge for Enhanced S/Key mechanism";
421 break;
422 case PA_SAM_TYPE_SKEY: /* Traditional S/Key */
423 label = "Challenge for Traditional S/Key mechanism";
424 break;
425 case PA_SAM_TYPE_SECURID: /* Security Dynamics */
426 label = "Challenge for Security Dynamics mechanism";
427 break;
428 case PA_SAM_TYPE_SECURID_PREDICT: /* predictive Security Dynamics */
429 label = "Challenge for Security Dynamics mechanism";
430 break;
431 }
432 prompt = "Passcode";
433 label_len = strlen(label);
434 prompt_len = strlen(prompt);
435 }
436
437 /* example:
438 Challenge for Digital Pathways mechanism: [134591]
439 Passcode:
440 */
441 p = prompt1 = malloc(label_len + strlen(sep1) +
442 challenge_len + strlen(sep2) +
443 prompt_len+ strlen(sep3) + 1);
444 if (p == NULL)
445 return NULL;
446 if (challenge_len) {
447 strncpy(p, label, label_len); p += label_len;
448 strcpy(p, sep1); p += strlen(sep1);
449 strncpy(p, challenge, challenge_len); p += challenge_len;
450 strcpy(p, sep2); p += strlen(sep2);
451 }
452 strncpy(p, prompt, prompt_len); p += prompt_len;
453 strcpy(p, sep3); /* p += strlen(sep3); */
454 return prompt1;
455 }
456
457 /*
458 * This routine is the "obtain" function for the SAM_CHALLENGE
459 * preauthentication type. It presents the challenge...
460 */
461 static krb5_error_code
obtain_sam_padata(krb5_context context,krb5_pa_data * in_padata,krb5_etype_info etype_info,krb5_keyblock * def_enc_key,git_key_proc key_proc,krb5_const_pointer key_seed,krb5_creds * creds,krb5_kdc_req * request,krb5_pa_data ** out_padata)462 obtain_sam_padata(krb5_context context, krb5_pa_data *in_padata, krb5_etype_info etype_info, krb5_keyblock *def_enc_key, git_key_proc key_proc, krb5_const_pointer key_seed, krb5_creds *creds, krb5_kdc_req *request, krb5_pa_data **out_padata)
463 {
464 krb5_error_code retval;
465 krb5_data * scratch;
466 krb5_data tmpsam;
467 krb5_pa_data * pa;
468 krb5_sam_challenge *sam_challenge = 0;
469 krb5_sam_response sam_response;
470 /* these two get encrypted and stuffed in to sam_response */
471 krb5_enc_sam_response_enc enc_sam_response_enc;
472 krb5_keyblock * sam_use_key = 0;
473 char * prompt;
474
475 tmpsam.length = in_padata->length;
476 tmpsam.data = (char *) in_padata->contents;
477 retval = decode_krb5_sam_challenge(&tmpsam, &sam_challenge);
478 if (retval)
479 return retval;
480
481 if (sam_challenge->sam_flags & KRB5_SAM_MUST_PK_ENCRYPT_SAD) {
482 return KRB5_SAM_UNSUPPORTED;
483 }
484
485 enc_sam_response_enc.sam_nonce = sam_challenge->sam_nonce;
486 if (!sam_challenge->sam_nonce) {
487 retval = krb5_us_timeofday(context,
488 &enc_sam_response_enc.sam_timestamp,
489 &enc_sam_response_enc.sam_usec);
490 sam_response.sam_patimestamp = enc_sam_response_enc.sam_timestamp;
491 }
492 if (retval)
493 return retval;
494 if (sam_challenge->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD) {
495 /* encrypt passcode in key by stuffing it here */
496 unsigned int pcsize = 256;
497 char *passcode = malloc(pcsize+1);
498 if (passcode == NULL)
499 return ENOMEM;
500 prompt = handle_sam_labels(sam_challenge);
501 if (prompt == NULL) {
502 free(passcode);
503 return ENOMEM;
504 }
505 retval = krb5_read_password(context, prompt, 0, passcode, &pcsize);
506 free(prompt);
507
508 if (retval) {
509 free(passcode);
510 return retval;
511 }
512 enc_sam_response_enc.sam_sad.data = passcode;
513 enc_sam_response_enc.sam_sad.length = pcsize;
514 } else if (sam_challenge->sam_flags & KRB5_SAM_USE_SAD_AS_KEY) {
515 prompt = handle_sam_labels(sam_challenge);
516 if (prompt == NULL)
517 return ENOMEM;
518 retval = sam_get_pass_from_user(context, etype_info, key_proc,
519 key_seed, request, &sam_use_key,
520 prompt);
521 free(prompt);
522 if (retval)
523 return retval;
524 enc_sam_response_enc.sam_sad.length = 0;
525 } else {
526 /* what *was* it? */
527 return KRB5_SAM_UNSUPPORTED;
528 }
529
530 /* so at this point, either sam_use_key is generated from the passcode
531 * or enc_sam_response_enc.sam_sad is set to it, and we use
532 * def_enc_key instead. */
533 /* encode the encoded part of the response */
534 if ((retval = encode_krb5_enc_sam_response_enc(&enc_sam_response_enc,
535 &scratch)) != 0)
536 return retval;
537
538 if ((retval = krb5_encrypt_data(context,
539 sam_use_key?sam_use_key:def_enc_key,
540 0, scratch,
541 &sam_response.sam_enc_nonce_or_ts)))
542 goto cleanup;
543
544 krb5_free_data(context, scratch);
545 scratch = 0;
546
547 /* sam_enc_key is reserved for future use */
548 sam_response.sam_enc_key.ciphertext.length = 0;
549
550 /* copy things from the challenge */
551 sam_response.sam_nonce = sam_challenge->sam_nonce;
552 sam_response.sam_flags = sam_challenge->sam_flags;
553 sam_response.sam_track_id = sam_challenge->sam_track_id;
554 sam_response.sam_type = sam_challenge->sam_type;
555 sam_response.magic = KV5M_SAM_RESPONSE;
556
557 if ((retval = encode_krb5_sam_response(&sam_response, &scratch)) != 0)
558 return retval;
559
560 if ((pa = malloc(sizeof(krb5_pa_data))) == NULL) {
561 retval = ENOMEM;
562 goto cleanup;
563 }
564
565 pa->magic = KV5M_PA_DATA;
566 pa->pa_type = KRB5_PADATA_SAM_RESPONSE;
567 pa->length = scratch->length;
568 pa->contents = (krb5_octet *) scratch->data;
569 scratch = 0; /* so we don't free it! */
570
571 *out_padata = pa;
572
573 retval = 0;
574
575 cleanup:
576 if (scratch)
577 krb5_free_data(context, scratch);
578 if (sam_challenge)
579 krb5_xfree(sam_challenge);
580 return retval;
581 }
582