2 * Copyright 1995 by the Massachusetts Institute of Technology. All
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.
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.
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.
36 static krb5_error_code obtain_enc_ts_padata
41 krb5_error_code ( * )(krb5_context
,
51 static krb5_error_code process_pw_salt
56 krb5_error_code ( * )(krb5_context
,
62 krb5_error_code ( * )(krb5_context
,
63 const krb5_keyblock
*,
71 static krb5_error_code obtain_sam_padata
76 krb5_error_code ( * )(krb5_context
,
86 static const krb5_preauth_ops preauth_systems
[] = {
89 KRB5_PADATA_ENC_TIMESTAMP
,
103 KRB5_PADATA_AFS3_SALT
,
110 KRB5_PADATA_SAM_CHALLENGE
,
115 { KV5M_PREAUTH_OPS
, -1 }
118 static krb5_error_code find_pa_system
119 (krb5_preauthtype type
, const krb5_preauth_ops
**Preauth_proc
);
121 /* some typedef's for the function args to make things look a bit cleaner */
123 typedef krb5_error_code (*git_key_proc
) (krb5_context
,
129 typedef krb5_error_code (*git_decrypt_proc
) (krb5_context
,
130 const krb5_keyblock
*,
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
)
136 krb5_error_code retval
;
137 krb5_etype_info etype_info
= 0;
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
;
149 if (preauth_to_use
== NULL
)
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? */
158 scratch
.length
= (*pa
)->length
;
159 scratch
.data
= (char *) (*pa
)->contents
;
160 retval
= decode_krb5_etype_info(&scratch
, &etype_info
);
163 if (etype_info
[0] == NULL
) {
164 krb5_free_etype_info(context
, etype_info
);
170 if ((send_pa_list
= malloc((size
+1) * sizeof(krb5_pa_data
*))) == NULL
)
173 send_pa
= send_pa_list
;
176 enctype
= request
->ktype
[0];
178 salt
.length
= SALT_TYPE_NO_LENGTH
;
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 */
185 salt
.length
= etype_info
[0]->length
;
187 if (salt
.length
== SALT_TYPE_NO_LENGTH
) {
189 * This will set the salt length
191 if ((retval
= krb5_principal2salt(context
, request
->client
, &salt
)))
196 if ((retval
= (*key_proc
)(context
, enctype
, &salt
, key_seed
,
201 for (pa
= preauth_to_use
; *pa
; pa
++) {
202 if (find_pa_system((*pa
)->pa_type
, &ops
))
205 if (ops
->obtain
== 0)
208 retval
= ((ops
)->obtain
)(context
, *pa
, etype_info
, def_enc_key
,
209 key_proc
, key_seed
, creds
,
221 if (send_pa_list
[0]) {
222 request
->padata
= send_pa_list
;
228 krb5_free_etype_info(context
, etype_info
);
230 krb5_xfree(salt
.data
);
232 krb5_free_pa_data(context
, send_pa_list
);
234 krb5_free_keyblock(context
, def_enc_key
);
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
)
242 krb5_error_code retval
= 0;
243 const krb5_preauth_ops
* ops
;
247 *do_more
= 0; /* By default, we don't need to repeat... */
248 if (as_reply
->padata
== 0)
251 for (pa
= as_reply
->padata
; *pa
; pa
++) {
252 if (find_pa_system((*pa
)->pa_type
, &ops
))
255 if (ops
->process
== 0)
258 retval
= ((ops
)->process
)(context
, *pa
, request
, as_reply
,
259 key_proc
, keyseed
, decrypt_proc
,
260 decrypt_key
, creds
, do_more
, &done
);
272 * This routine is the "obtain" function for the ENC_TIMESTAMP
273 * preauthentication type. It take the current time and encrypts it
276 static krb5_error_code
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
)
279 krb5_pa_enc_ts pa_enc
;
280 krb5_error_code retval
;
282 krb5_enc_data enc_data
;
285 retval
= krb5_us_timeofday(context
, &pa_enc
.patimestamp
, &pa_enc
.pausec
);
289 if ((retval
= encode_krb5_pa_enc_ts(&pa_enc
, &scratch
)) != 0)
292 enc_data
.ciphertext
.data
= 0;
294 if ((retval
= krb5_encrypt_helper(context
, def_enc_key
,
295 KRB5_KEYUSAGE_AS_REQ_PA_ENC_TS
,
296 scratch
, &enc_data
)))
299 krb5_free_data(context
, scratch
);
302 if ((retval
= encode_krb5_enc_data(&enc_data
, &scratch
)) != 0)
305 if ((pa
= malloc(sizeof(krb5_pa_data
))) == NULL
) {
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
;
324 krb5_free_data(context
, scratch
);
325 if (enc_data
.ciphertext
.data
)
326 krb5_xfree(enc_data
.ciphertext
.data
);
330 static krb5_error_code
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
)
333 krb5_error_code retval
;
336 if (*decrypt_key
!= 0)
339 salt
.data
= (char *) padata
->contents
;
341 (padata
->pa_type
== KRB5_PADATA_AFS3_SALT
)?(SALT_TYPE_AFS_LENGTH
):(padata
->length
);
343 if ((retval
= (*key_proc
)(context
, as_reply
->enc_part
.enctype
,
344 &salt
, keyseed
, decrypt_key
))) {
352 static krb5_error_code
353 find_pa_system(krb5_preauthtype type
, const krb5_preauth_ops
**preauth
)
355 const krb5_preauth_ops
*ap
= preauth_systems
;
357 while ((ap
->type
!= -1) && (ap
->type
!= type
))
360 return(KRB5_PREAUTH_BAD_TYPE
);
366 extern const char *krb5_default_pwd_prompt1
;
368 static krb5_error_code
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
)
371 krb5_enctype enctype
;
372 krb5_error_code retval
;
373 const char *oldprompt
;
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
;
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
);
388 krb5_default_pwd_prompt1
= oldprompt
;
392 char *handle_sam_labels(krb5_sam_challenge
*sc
)
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
;
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";
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";
415 case PA_SAM_TYPE_ACTIVCARD_DEC
: /* Digital Pathways */
416 case PA_SAM_TYPE_ACTIVCARD_HEX
: /* Digital Pathways */
417 label
= "Challenge for Activcard mechanism";
419 case PA_SAM_TYPE_SKEY_K0
: /* S/key where KDC has key 0 */
420 label
= "Challenge for Enhanced S/Key mechanism";
422 case PA_SAM_TYPE_SKEY
: /* Traditional S/Key */
423 label
= "Challenge for Traditional S/Key mechanism";
425 case PA_SAM_TYPE_SECURID
: /* Security Dynamics */
426 label
= "Challenge for Security Dynamics mechanism";
428 case PA_SAM_TYPE_SECURID_PREDICT
: /* predictive Security Dynamics */
429 label
= "Challenge for Security Dynamics mechanism";
433 label_len
= strlen(label
);
434 prompt_len
= strlen(prompt
);
438 Challenge for Digital Pathways mechanism: [134591]
441 p
= prompt1
= malloc(label_len
+ strlen(sep1
) +
442 challenge_len
+ strlen(sep2
) +
443 prompt_len
+ strlen(sep3
) + 1);
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
);
452 strncpy(p
, prompt
, prompt_len
); p
+= prompt_len
;
453 strcpy(p
, sep3
); /* p += strlen(sep3); */
458 * This routine is the "obtain" function for the SAM_CHALLENGE
459 * preauthentication type. It presents the challenge...
461 static krb5_error_code
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
)
464 krb5_error_code retval
;
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;
475 tmpsam
.length
= in_padata
->length
;
476 tmpsam
.data
= (char *) in_padata
->contents
;
477 retval
= decode_krb5_sam_challenge(&tmpsam
, &sam_challenge
);
481 if (sam_challenge
->sam_flags
& KRB5_SAM_MUST_PK_ENCRYPT_SAD
) {
482 return KRB5_SAM_UNSUPPORTED
;
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
;
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
)
500 prompt
= handle_sam_labels(sam_challenge
);
501 if (prompt
== NULL
) {
505 retval
= krb5_read_password(context
, prompt
, 0, passcode
, &pcsize
);
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
);
518 retval
= sam_get_pass_from_user(context
, etype_info
, key_proc
,
519 key_seed
, request
, &sam_use_key
,
524 enc_sam_response_enc
.sam_sad
.length
= 0;
527 return KRB5_SAM_UNSUPPORTED
;
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
,
538 if ((retval
= krb5_encrypt_data(context
,
539 sam_use_key
?sam_use_key
:def_enc_key
,
541 &sam_response
.sam_enc_nonce_or_ts
)))
544 krb5_free_data(context
, scratch
);
547 /* sam_enc_key is reserved for future use */
548 sam_response
.sam_enc_key
.ciphertext
.length
= 0;
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
;
557 if ((retval
= encode_krb5_sam_response(&sam_response
, &scratch
)) != 0)
560 if ((pa
= malloc(sizeof(krb5_pa_data
))) == NULL
) {
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! */
577 krb5_free_data(context
, scratch
);
579 krb5_xfree(sam_challenge
);