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
36static 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
51static 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
71static 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
86static 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
118static 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
123typedef krb5_error_code (*git_key_proc) (krb5_context,
124					 const krb5_enctype,
125					 krb5_data *,
126					 krb5_const_pointer,
127					 krb5_keyblock **);
128
129typedef krb5_error_code (*git_decrypt_proc) (krb5_context,
130					     const krb5_keyblock *,
131					     krb5_const_pointer,
132					     krb5_kdc_rep *);
133
134krb5_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
226cleanup:
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
239krb5_error_code
240krb5_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
267cleanup:
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 */
276static krb5_error_code
277obtain_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
322cleanup:
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
330static krb5_error_code
331process_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
352static krb5_error_code
353find_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
366extern const char *krb5_default_pwd_prompt1;
367
368static krb5_error_code
369sam_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}
391static
392char *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 */
461static krb5_error_code
462obtain_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
575cleanup:
576    if (scratch)
577	krb5_free_data(context, scratch);
578    if (sam_challenge)
579        krb5_xfree(sam_challenge);
580    return retval;
581}
582