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