void *pa_system_context,
void **pa_request_context);
-/* SAM preauth support */
-static krb5_error_code
-verify_sam_response(krb5_context, krb5_db_entry *client,
- krb5_data *req_pkt,
- krb5_kdc_req *request,
- krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data *data,
- preauth_get_entry_data_proc get_entry_data,
- void *pa_module_context,
- void **pa_request_context,
- krb5_data **e_data,
- krb5_authdata ***authz_data);
-
-static krb5_error_code
-get_sam_edata(krb5_context, krb5_kdc_req *request,
- krb5_db_entry *client, krb5_db_entry *server,
- preauth_get_entry_data_proc get_entry_data,
- void *pa_module_context,
- krb5_pa_data *data);
-static krb5_error_code
-return_sam_data(krb5_context, krb5_pa_data * padata,
- krb5_db_entry *client,
- krb5_data *req_pkt,
- krb5_kdc_req *request, krb5_kdc_rep *reply,
- krb5_key_data *client_key,
- krb5_keyblock *encrypting_key,
- krb5_pa_data **send_pa,
- preauth_get_entry_data_proc get_entry_data,
- void *pa_module_context,
- void **pa_request_context);
#if APPLE_PKINIT
/* PKINIT preauth support */
0,
return_pw_salt
},
- {
- "sam-response",
- KRB5_PADATA_SAM_RESPONSE,
- 0,
- NULL,
- NULL,
- NULL,
- 0,
- verify_sam_response,
- return_sam_data
- },
- {
- "sam-challenge",
- KRB5_PADATA_SAM_CHALLENGE,
- PA_HARDWARE, /* causes get_preauth_hint_list to use this */
- NULL,
- NULL,
- NULL,
- get_sam_edata,
- 0,
- 0
- },
{
"pac-request",
KRB5_PADATA_PAC_REQUEST,
return retval;
}
-static krb5_error_code
-return_sam_data(krb5_context context, krb5_pa_data *in_padata,
- krb5_db_entry *client, krb5_data *req_pkt, krb5_kdc_req *request,
- krb5_kdc_rep *reply, krb5_key_data *client_key,
- krb5_keyblock *encrypting_key, krb5_pa_data **send_pa,
- preauth_get_entry_data_proc sam_get_entry_data,
- void *pa_system_context,
- void **pa_request_context)
-{
- krb5_error_code retval;
- krb5_data scratch;
- int i;
-
- krb5_sam_response *sr = 0;
- krb5_predicted_sam_response *psr = 0;
-
- if (in_padata->contents == 0)
- return 0;
-
- /*
- * We start by doing the same thing verify_sam_response() does:
- * extract the psr from the padata (which is an sr). Nothing
- * here should generate errors! We've already successfully done
- * all this once.
- */
-
- scratch.data = (char *)in_padata->contents;
- scratch.length = in_padata->length;
-
- if ((retval = decode_krb5_sam_response(&scratch, &sr))) {
- kdc_err(context, retval,
- "return_sam_data(): decode_krb5_sam_response failed");
- goto cleanup;
- }
-
- {
- krb5_enc_data tmpdata;
-
- tmpdata.enctype = ENCTYPE_UNKNOWN;
- tmpdata.ciphertext = sr->sam_track_id;
-
- scratch.length = tmpdata.ciphertext.length;
- if ((scratch.data = (char *) malloc(scratch.length)) == NULL) {
- retval = ENOMEM;
- goto cleanup;
- }
-
- if ((retval = krb5_c_decrypt(context, &psr_key, /* XXX */ 0, 0,
- &tmpdata, &scratch))) {
- kdc_err(context, retval,
- "return_sam_data(): decrypt track_id failed");
- free(scratch.data);
- goto cleanup;
- }
- }
-
- if ((retval = decode_krb5_predicted_sam_response(&scratch, &psr))) {
- kdc_err(context, retval,
- "return_sam_data(): decode_krb5_predicted_sam_response failed");
- free(scratch.data);
- goto cleanup;
- }
-
- /* We could use sr->sam_flags, but it may be absent or altered. */
- if (psr->sam_flags & KRB5_SAM_MUST_PK_ENCRYPT_SAD) {
- kdc_err(context, retval = KRB5KDC_ERR_PREAUTH_FAILED,
- "Unsupported SAM flag must-pk-encrypt-sad");
- goto cleanup;
- }
- if (psr->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD) {
- /* No key munging */
- goto cleanup;
- }
- if (psr->sam_flags & KRB5_SAM_USE_SAD_AS_KEY) {
- /* Use sam_key instead of client key */
- krb5_free_keyblock_contents(context, encrypting_key);
- krb5_copy_keyblock_contents(context, &psr->sam_key, encrypting_key);
- /* XXX Attach a useful pa_data */
- goto cleanup;
- }
-
- /* Otherwise (no flags set), we XOR the keys */
- /* XXX The passwords-04 draft is underspecified here wrt different
- key types. We will do what I hope to get into the -05 draft. */
- {
- krb5_octet *p = encrypting_key->contents;
- krb5_octet *q = psr->sam_key.contents;
- int length = ((encrypting_key->length < psr->sam_key.length)
- ? encrypting_key->length
- : psr->sam_key.length);
-
- for (i = 0; i < length; i++)
- p[i] ^= q[i];
- }
-
- /* Post-mixing key correction */
- switch (encrypting_key->enctype) {
- case ENCTYPE_DES_CBC_CRC:
- case ENCTYPE_DES_CBC_MD4:
- case ENCTYPE_DES_CBC_MD5:
- case ENCTYPE_DES_CBC_RAW:
- mit_des_fixup_key_parity(encrypting_key->contents);
- if (mit_des_is_weak_key(encrypting_key->contents))
- ((krb5_octet *) encrypting_key->contents)[7] ^= 0xf0;
- break;
-
- /* XXX case ENCTYPE_DES3_CBC_MD5: listed in 1510bis-04 draft */
- case ENCTYPE_DES3_CBC_SHA: /* XXX deprecated? */
- case ENCTYPE_DES3_CBC_RAW:
- case ENCTYPE_DES3_CBC_SHA1:
- for (i = 0; i < 3; i++) {
- mit_des_fixup_key_parity(encrypting_key->contents + i * 8);
- if (mit_des_is_weak_key(encrypting_key->contents + i * 8))
- ((krb5_octet *) encrypting_key->contents)[7 + i * 8] ^= 0xf0;
- }
- break;
-
- default:
- kdc_err(context, retval = KRB5KDC_ERR_PREAUTH_FAILED,
- "Unimplemented keytype for SAM key mixing");
- goto cleanup;
- }
-
- /* XXX Attach a useful pa_data */
-cleanup:
- if (sr)
- krb5_free_sam_response(context, sr);
- if (psr)
- krb5_free_predicted_sam_response(context, psr);
-
- return retval;
-}
-
-static struct {
- char* name;
- int sam_type;
-} *sam_ptr, sam_inst_map[] = {
- { "SNK4", PA_SAM_TYPE_DIGI_PATH, },
- { "SECURID", PA_SAM_TYPE_SECURID, },
- { "GRAIL", PA_SAM_TYPE_GRAIL, },
- { 0, 0 },
-};
-
-static krb5_error_code
-get_sam_edata(krb5_context context, krb5_kdc_req *request,
- krb5_db_entry *client, krb5_db_entry *server,
- preauth_get_entry_data_proc sam_get_entry_data,
- void *pa_system_context, krb5_pa_data *pa_data)
-{
- krb5_error_code retval;
- krb5_sam_challenge sc;
- krb5_predicted_sam_response psr;
- krb5_data * scratch;
- krb5_keyblock encrypting_key, *mkey_ptr;
- char response[9];
- char inputblock[8];
- krb5_data predict_response;
-
- memset(&sc, 0, sizeof(sc));
- memset(&psr, 0, sizeof(psr));
-
- /*
- * Given the client name we can figure out what type of preauth
- * they need. The spec is currently for querying the database for
- * names that match the types of preauth used. Later we should
- * make this mapping show up in kdc.conf. In the meantime, we
- * hardcode the following:
- * /SNK4 -- Digital Pathways SNK/4 preauth.
- * /GRAIL -- experimental preauth
- * The first one found is used. See sam_inst_map above.
- *
- * For SNK4 in particular, the key in the database is the key for
- * the device; kadmin needs a special interface for it.
- */
-
- {
- krb5_db_entry *assoc;
- krb5_key_data *assoc_key;
- krb5_principal newp;
- int probeslot;
-
- sc.sam_type = 0;
-
- retval = krb5_copy_principal(kdc_context, request->client, &newp);
- if (retval) {
- kdc_err(kdc_context, retval, "copying client name for preauth probe");
- return retval;
- }
-
- probeslot = krb5_princ_size(context, newp)++;
- krb5_princ_name(kdc_context, newp) =
- realloc(krb5_princ_name(kdc_context, newp),
- krb5_princ_size(context, newp) * sizeof(krb5_data));
-
- for(sam_ptr = sam_inst_map; sam_ptr->name; sam_ptr++) {
- krb5_princ_component(kdc_context,newp,probeslot)->data = sam_ptr->name;
- krb5_princ_component(kdc_context,newp,probeslot)->length =
- strlen(sam_ptr->name);
- retval = krb5_db_get_principal(kdc_context, newp, 0, &assoc);
- if(retval == 0) {
- sc.sam_type = sam_ptr->sam_type;
- break;
- }
- }
-
- krb5_princ_component(kdc_context,newp,probeslot)->data = 0;
- krb5_princ_component(kdc_context,newp,probeslot)->length = 0;
- krb5_princ_size(context, newp)--;
-
- krb5_free_principal(kdc_context, newp);
-
- /* if sc.sam_type is set, it worked */
- if (sc.sam_type) {
- /* so use assoc to get the key out! */
- {
- if ((retval = krb5_dbe_find_mkey(context, master_keylist,
- assoc, &mkey_ptr))) {
- krb5_keylist_node *tmp_mkey_list;
- /* try refreshing the mkey list in case it's been updated */
- if (krb5_db_fetch_mkey_list(context, master_princ,
- &master_keyblock, 0,
- &tmp_mkey_list) == 0) {
- krb5_dbe_free_key_list(context, master_keylist);
- master_keylist = tmp_mkey_list;
- if ((retval = krb5_dbe_find_mkey(context, master_keylist,
- assoc, &mkey_ptr))) {
- return (retval);
- }
- } else {
- return (retval);
- }
- }
-
- /* here's what do_tgs_req does */
- retval = krb5_dbe_find_enctype(kdc_context, assoc,
- ENCTYPE_DES_CBC_RAW,
- KRB5_KDB_SALTTYPE_NORMAL,
- 0, /* Get highest kvno */
- &assoc_key);
- if (retval) {
- char *sname;
- krb5_unparse_name(kdc_context, request->client, &sname);
- kdc_err(kdc_context, retval,
- "snk4 finding the enctype and key <%s>", sname);
- free(sname);
- return retval;
- }
- /* convert server.key into a real key */
- retval = krb5_dbe_decrypt_key_data(kdc_context, mkey_ptr,
- assoc_key, &encrypting_key,
- NULL);
- if (retval) {
- kdc_err(kdc_context, retval,
- "snk4 pulling out key entry");
- return retval;
- }
- /* now we can use encrypting_key... */
- }
- } else {
- /* SAM is not an option - so don't return as hint */
- return KRB5_PREAUTH_BAD_TYPE;
- }
- }
- sc.magic = KV5M_SAM_CHALLENGE;
- psr.sam_flags = sc.sam_flags = KRB5_SAM_USE_SAD_AS_KEY;
-
- /* Replay prevention */
- if ((retval = krb5_copy_principal(context, request->client, &psr.client)))
- return retval;
-#ifdef USE_RCACHE
- if ((retval = krb5_us_timeofday(context, &psr.stime, &psr.susec)))
- return retval;
-#endif /* USE_RCACHE */
-
- switch (sc.sam_type) {
- case PA_SAM_TYPE_GRAIL:
- sc.sam_type_name.data = "Experimental System";
- sc.sam_type_name.length = strlen(sc.sam_type_name.data);
- sc.sam_challenge_label.data = "experimental challenge label";
- sc.sam_challenge_label.length = strlen(sc.sam_challenge_label.data);
- sc.sam_challenge.data = "12345";
- sc.sam_challenge.length = strlen(sc.sam_challenge.data);
-
-#if 0 /* Enable this to test "normal" (no flags set) mode. */
- psr.sam_flags = sc.sam_flags = 0;
-#endif
-
- psr.magic = KV5M_PREDICTED_SAM_RESPONSE;
- /* string2key on sc.sam_challenge goes in here */
- /* eblock is just to set the enctype */
- {
- const krb5_enctype type = ENCTYPE_DES_CBC_MD5;
-
- if ((retval = krb5_c_string_to_key(context, type, &sc.sam_challenge,
- 0 /* salt */, &psr.sam_key)))
- goto cleanup;
-
- if ((retval = encode_krb5_predicted_sam_response(&psr, &scratch)))
- goto cleanup;
-
- {
- size_t enclen;
- krb5_enc_data tmpdata;
-
- if ((retval = krb5_c_encrypt_length(context,
- psr_key.enctype,
- scratch->length, &enclen)))
- goto cleanup;
-
- if ((tmpdata.ciphertext.data = (char *) malloc(enclen)) == NULL) {
- retval = ENOMEM;
- goto cleanup;
- }
- tmpdata.ciphertext.length = enclen;
-
- if ((retval = krb5_c_encrypt(context, &psr_key,
- /* XXX */ 0, 0, scratch, &tmpdata)))
- goto cleanup;
-
- sc.sam_track_id = tmpdata.ciphertext;
- }
- }
-
- sc.sam_response_prompt.data = "response prompt";
- sc.sam_response_prompt.length = strlen(sc.sam_response_prompt.data);
- sc.sam_pk_for_sad.length = 0;
- sc.sam_nonce = 0;
- /* Generate checksum */
- /*krb5_checksum_size(context, ctype)*/
- /*krb5_calculate_checksum(context,ctype,in,in_length,seed,
- seed_length,outcksum) */
- /*krb5_verify_checksum(context,ctype,cksum,in,in_length,seed,
- seed_length) */
-#if 0 /* XXX a) glue appears broken; b) this gives up the SAD */
- sc.sam_cksum.contents = (krb5_octet *)
- malloc(krb5_checksum_size(context, CKSUMTYPE_RSA_MD5_DES));
- if (sc.sam_cksum.contents == NULL) return(ENOMEM);
-
- retval = krb5_calculate_checksum(context, CKSUMTYPE_RSA_MD5_DES,
- sc.sam_challenge.data,
- sc.sam_challenge.length,
- psr.sam_key.contents, /* key */
- psr.sam_key.length, /* key length */
- &sc.sam_cksum);
- if (retval) { free(sc.sam_cksum.contents); return(retval); }
-#endif /* 0 */
-
- retval = encode_krb5_sam_challenge(&sc, &scratch);
- if (retval) goto cleanup;
- pa_data->magic = KV5M_PA_DATA;
- pa_data->pa_type = KRB5_PADATA_SAM_CHALLENGE;
- pa_data->contents = (krb5_octet *)scratch->data;
- pa_data->length = scratch->length;
-
- retval = 0;
- break;
- case PA_SAM_TYPE_DIGI_PATH:
- sc.sam_type_name.data = "Digital Pathways";
- sc.sam_type_name.length = strlen(sc.sam_type_name.data);
-#if 1
- sc.sam_challenge_label.data = "Enter the following on your keypad";
- sc.sam_challenge_label.length = strlen(sc.sam_challenge_label.data);
-#endif
- /* generate digit string, take it mod 1000000 (six digits.) */
- {
- int j;
- krb5_keyblock session_key;
- char outputblock[8];
- int i;
-
- session_key.contents = 0;
-
- memset(inputblock, 0, 8);
-
- retval = krb5_c_make_random_key(kdc_context, ENCTYPE_DES_CBC_CRC,
- &session_key);
-
- if (retval) {
- /* random key failed */
- kdc_err(kdc_context, retval,
- "generating random challenge for preauth");
- return retval;
- }
- /* now session_key has a key which we can pick bits out of */
- /* we need six decimal digits. Grab 6 bytes, div 2, mod 10 each. */
- if (session_key.length != 8) {
- kdc_err(kdc_context, retval = KRB5KDC_ERR_ETYPE_NOSUPP,
- "keytype didn't match code expectations");
- return retval;
- }
- for(i = 0; i<6; i++) {
- inputblock[i] = '0' + ((session_key.contents[i]/2) % 10);
- }
- if (session_key.contents)
- krb5_free_keyblock_contents(kdc_context, &session_key);
-
- /* retval = krb5_finish_key(kdc_context, &eblock); */
- /* now we have inputblock containing the 8 byte input to DES... */
- sc.sam_challenge.data = inputblock;
- sc.sam_challenge.length = 6;
-
- encrypting_key.enctype = ENCTYPE_DES_CBC_RAW;
-
- if (retval)
- kdc_err(kdc_context, retval, "snk4 processing key");
-
- {
- krb5_data plain;
- krb5_enc_data cipher;
-
- plain.length = 8;
- plain.data = inputblock;
-
- /* XXX I know this is enough because of the fixed raw enctype.
- if it's not, the underlying code will return a reasonable
- error, which should never happen */
- cipher.ciphertext.length = 8;
- cipher.ciphertext.data = outputblock;
-
- if ((retval = krb5_c_encrypt(kdc_context, &encrypting_key,
- /* XXX */ 0, 0, &plain, &cipher))) {
- kdc_err(kdc_context, retval,
- "snk4 response generation failed");
- return retval;
- }
- }
-
- /* now output block is the raw bits of the response; convert it
- to display form */
- for (j=0; j<4; j++) {
- char n[2];
- int k;
- n[0] = outputblock[j] & 0xf;
- n[1] = (outputblock[j]>>4) & 0xf;
- for (k=0; k<2; k++) {
- if(n[k] > 9) n[k] = ((n[k]-1)>>2);
- /* This is equivalent to:
- if(n[k]>=0xa && n[k]<=0xc) n[k] = 2;
- if(n[k]>=0xd && n[k]<=0xf) n[k] = 3;
- */
- }
- /* for v4, we keygen: *(j+(char*)&key1) = (n[1]<<4) | n[0]; */
- /* for v5, we just generate a string */
- response[2*j+0] = '0' + n[1];
- response[2*j+1] = '0' + n[0];
- /* and now, response has what we work with. */
- }
- response[8] = 0;
- predict_response.data = response;
- predict_response.length = 8;
-#if 0 /* for debugging, hack the output too! */
- sc.sam_challenge_label.data = response;
- sc.sam_challenge_label.length = strlen(sc.sam_challenge_label.data);
-#endif
- }
-
- psr.magic = KV5M_PREDICTED_SAM_RESPONSE;
- /* string2key on sc.sam_challenge goes in here */
- /* eblock is just to set the enctype */
- {
- retval = krb5_c_string_to_key(context, ENCTYPE_DES_CBC_MD5,
- &predict_response, 0 /* salt */,
- &psr.sam_key);
- if (retval) goto cleanup;
-
- retval = encode_krb5_predicted_sam_response(&psr, &scratch);
- if (retval) goto cleanup;
-
- {
- size_t enclen;
- krb5_enc_data tmpdata;
-
- if ((retval = krb5_c_encrypt_length(context,
- psr_key.enctype,
- scratch->length, &enclen)))
- goto cleanup;
-
- if ((tmpdata.ciphertext.data = (char *) malloc(enclen)) == NULL) {
- retval = ENOMEM;
- goto cleanup;
- }
- tmpdata.ciphertext.length = enclen;
-
- if ((retval = krb5_c_encrypt(context, &psr_key,
- /* XXX */ 0, 0, scratch, &tmpdata)))
- goto cleanup;
-
- sc.sam_track_id = tmpdata.ciphertext;
- }
- if (retval) goto cleanup;
- }
-
- sc.sam_response_prompt.data = "Enter the displayed response";
- sc.sam_response_prompt.length = strlen(sc.sam_response_prompt.data);
- sc.sam_pk_for_sad.length = 0;
- sc.sam_nonce = 0;
- /* Generate checksum */
- /*krb5_checksum_size(context, ctype)*/
- /*krb5_calculate_checksum(context,ctype,in,in_length,seed,
- seed_length,outcksum) */
- /*krb5_verify_checksum(context,ctype,cksum,in,in_length,seed,
- seed_length) */
-#if 0 /* XXX a) glue appears broken; b) this gives up the SAD */
- sc.sam_cksum.contents = (krb5_octet *)
- malloc(krb5_checksum_size(context, CKSUMTYPE_RSA_MD5_DES));
- if (sc.sam_cksum.contents == NULL) return(ENOMEM);
-
- retval = krb5_calculate_checksum(context, CKSUMTYPE_RSA_MD5_DES,
- sc.sam_challenge.data,
- sc.sam_challenge.length,
- psr.sam_key.contents, /* key */
- psr.sam_key.length, /* key length */
- &sc.sam_cksum);
- if (retval) { free(sc.sam_cksum.contents); return(retval); }
-#endif /* 0 */
-
- retval = encode_krb5_sam_challenge(&sc, &scratch);
- if (retval) goto cleanup;
- pa_data->magic = KV5M_PA_DATA;
- pa_data->pa_type = KRB5_PADATA_SAM_CHALLENGE;
- pa_data->contents = (krb5_octet *)scratch->data;
- pa_data->length = scratch->length;
-
- retval = 0;
- break;
- }
-
-cleanup:
- krb5_free_keyblock_contents(context, &encrypting_key);
- return retval;
-}
-
-static krb5_error_code
-verify_sam_response(krb5_context context, krb5_db_entry *client,
- krb5_data *req_pkt,
- krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply,
- krb5_pa_data *pa,
- preauth_get_entry_data_proc sam_get_entry_data,
- void *pa_system_context,
- void **pa_request_context,
- krb5_data **e_data,
- krb5_authdata ***authz_data)
-{
- krb5_error_code retval;
- krb5_data scratch;
- krb5_sam_response *sr = 0;
- krb5_predicted_sam_response *psr = 0;
- krb5_enc_sam_response_enc *esre = 0;
- krb5_timestamp timenow;
- char *princ_req = 0, *princ_psr = 0;
-
- scratch.data = (char *)pa->contents;
- scratch.length = pa->length;
-
- if ((retval = decode_krb5_sam_response(&scratch, &sr))) {
- scratch.data = 0;
- kdc_err(context, retval, "decode_krb5_sam_response failed");
- goto cleanup;
- }
- /* XXX We can only handle the challenge/response model of SAM.
- See passwords-04, par 4.1, 4.2 */
- {
- krb5_enc_data tmpdata;
-
- tmpdata.enctype = ENCTYPE_UNKNOWN;
- tmpdata.ciphertext = sr->sam_track_id;
-
- scratch.length = tmpdata.ciphertext.length;
- if ((scratch.data = (char *) malloc(scratch.length)) == NULL) {
- retval = ENOMEM;
- goto cleanup;
- }
-
- if ((retval = krb5_c_decrypt(context, &psr_key, /* XXX */ 0, 0,
- &tmpdata, &scratch))) {
- kdc_err(context, retval, "decrypt track_id failed");
- goto cleanup;
- }
- }
-
- if ((retval = decode_krb5_predicted_sam_response(&scratch, &psr))) {
- kdc_err(context, retval,
- "decode_krb5_predicted_sam_response failed -- replay attack?");
- goto cleanup;
- }
-
- /* Replay detection */
- if ((retval = krb5_unparse_name(context, request->client, &princ_req)))
- goto cleanup;
- if ((retval = krb5_unparse_name(context, psr->client, &princ_psr)))
- goto cleanup;
- if (strcmp(princ_req, princ_psr) != 0) {
- kdc_err(context, retval = KRB5KDC_ERR_PREAUTH_FAILED,
- "Principal mismatch in SAM psr! -- replay attack?");
- goto cleanup;
- }
-
- if ((retval = krb5_timeofday(context, &timenow)))
- goto cleanup;
-
-#ifdef USE_RCACHE
- {
- krb5_donot_replay rep;
- extern krb5_deltat rc_lifetime;
- /*
- * Verify this response came back in a timely manner.
- * We do this b/c otherwise very old (expunged from the rcache)
- * psr's would be able to be replayed.
- */
- if (timenow - psr->stime > rc_lifetime) {
- kdc_err(context, retval = KRB5KDC_ERR_PREAUTH_FAILED,
- "SAM psr came back too late! -- replay attack?");
- goto cleanup;
- }
-
- /* Now check the replay cache. */
- rep.client = princ_psr;
- rep.server = "SAM/rc"; /* Should not match any principal name. */
- rep.msghash = NULL;
- rep.ctime = psr->stime;
- rep.cusec = psr->susec;
- retval = krb5_rc_store(kdc_context, kdc_rcache, &rep);
- if (retval) {
- kdc_err(kdc_context, retval, "SAM psr replay attack!");
- goto cleanup;
- }
- }
-#endif /* USE_RCACHE */
-
-
- {
- free(scratch.data);
- scratch.length = sr->sam_enc_nonce_or_ts.ciphertext.length;
- if ((scratch.data = (char *) malloc(scratch.length)) == NULL) {
- retval = ENOMEM;
- goto cleanup;
- }
-
- if ((retval = krb5_c_decrypt(context, &psr->sam_key, /* XXX */ 0,
- 0, &sr->sam_enc_nonce_or_ts, &scratch))) {
- kdc_err(context, retval, "decrypt nonce_or_ts failed");
- goto cleanup;
- }
- }
-
- if ((retval = decode_krb5_enc_sam_response_enc(&scratch, &esre))) {
- kdc_err(context, retval, "decode_krb5_enc_sam_response_enc failed");
- goto cleanup;
- }
-
- if (esre->sam_timestamp != sr->sam_patimestamp) {
- retval = KRB5KDC_ERR_PREAUTH_FAILED;
- goto cleanup;
- }
-
- if (labs(timenow - sr->sam_patimestamp) > context->clockskew) {
- retval = KRB5KRB_AP_ERR_SKEW;
- goto cleanup;
- }
-
- setflag(enc_tkt_reply->flags, TKT_FLG_HW_AUTH);
-
-cleanup:
- if (retval)
- kdc_err(context, retval, "sam verify failure");
- if (scratch.data) free(scratch.data);
- if (sr) free(sr);
- if (psr) free(psr);
- if (esre) free(esre);
- if (princ_psr) free(princ_psr);
- if (princ_req) free(princ_req);
-
- return retval;
-}
#if APPLE_PKINIT
/* PKINIT preauth support */
return(0);
}
-static char *
-sam_challenge_banner(krb5_int32 sam_type)
-{
- char *label;
-
- switch (sam_type) {
- case PA_SAM_TYPE_ENIGMA: /* Enigma Logic */
- label = "Challenge for Enigma Logic mechanism";
- break;
- case PA_SAM_TYPE_DIGI_PATH: /* Digital Pathways */
- case PA_SAM_TYPE_DIGI_PATH_HEX: /* Digital Pathways */
- label = "Challenge for Digital Pathways mechanism";
- break;
- case PA_SAM_TYPE_ACTIVCARD_DEC: /* Digital Pathways */
- case PA_SAM_TYPE_ACTIVCARD_HEX: /* Digital Pathways */
- label = "Challenge for Activcard mechanism";
- break;
- case PA_SAM_TYPE_SKEY_K0: /* S/key where KDC has key 0 */
- label = "Challenge for Enhanced S/Key mechanism";
- break;
- case PA_SAM_TYPE_SKEY: /* Traditional S/Key */
- label = "Challenge for Traditional S/Key mechanism";
- break;
- case PA_SAM_TYPE_SECURID: /* Security Dynamics */
- label = "Challenge for Security Dynamics mechanism";
- break;
- case PA_SAM_TYPE_SECURID_PREDICT: /* predictive Security Dynamics */
- label = "Challenge for Security Dynamics mechanism";
- break;
- default:
- label = "Challenge from authentication server";
- break;
- }
-
- return(label);
-}
-
-/* this macro expands to the int,ptr necessary for "%.*s" in an sprintf */
-
-#define SAMDATA(kdata, str, maxsize) \
- (int)((kdata.length)? \
- ((((kdata.length)<=(maxsize))?(kdata.length):strlen(str))): \
- strlen(str)), \
- (kdata.length)? \
- ((((kdata.length)<=(maxsize))?(kdata.data):(str))):(str)
-
-/* XXX Danger! This code is not in sync with the kerberos-password-02
- draft. This draft cannot be implemented as written. This code is
- compatible with earlier versions of mit krb5 and cygnus kerbnet. */
-
-static krb5_error_code
-pa_sam(krb5_context context, krb5_kdc_req *request, krb5_pa_data *in_padata,
- krb5_pa_data **out_padata, krb5_data *salt, krb5_data *s2kparams,
- krb5_enctype *etype, krb5_keyblock *as_key, krb5_prompter_fct prompter,
- void *prompter_data, krb5_gic_get_as_key_fct gak_fct, void *gak_data)
-{
- krb5_error_code ret;
- krb5_data tmpsam;
- char name[100], banner[100];
- char prompt[100], response[100];
- krb5_data response_data;
- krb5_prompt kprompt;
- krb5_prompt_type prompt_type;
- krb5_data defsalt;
- krb5_sam_challenge *sam_challenge = 0;
- krb5_sam_response sam_response;
- /* these two get encrypted and stuffed in to sam_response */
- krb5_enc_sam_response_enc enc_sam_response_enc;
- krb5_data * scratch;
- krb5_pa_data * pa;
-
- if (prompter == NULL)
- return EIO;
-
- tmpsam.length = in_padata->length;
- tmpsam.data = (char *) in_padata->contents;
- if ((ret = decode_krb5_sam_challenge(&tmpsam, &sam_challenge)))
- return(ret);
-
- if (sam_challenge->sam_flags & KRB5_SAM_MUST_PK_ENCRYPT_SAD) {
- krb5_free_sam_challenge(context, sam_challenge);
- return(KRB5_SAM_UNSUPPORTED);
- }
-
- /* If we need the password from the user (USE_SAD_AS_KEY not set), */
- /* then get it here. Exception for "old" KDCs with CryptoCard */
- /* support which uses the USE_SAD_AS_KEY flag, but still needs pwd */
-
- if (!(sam_challenge->sam_flags & KRB5_SAM_USE_SAD_AS_KEY) ||
- (sam_challenge->sam_type == PA_SAM_TYPE_CRYPTOCARD)) {
-
- /* etype has either been set by caller or by KRB5_PADATA_ETYPE_INFO */
- /* message from the KDC. If it is not set, pick an enctype that we */
- /* think the KDC will have for us. */
-
- if (*etype == 0)
- *etype = ENCTYPE_DES_CBC_CRC;
-
- if ((ret = (gak_fct)(context, request->client, *etype, prompter,
- prompter_data, salt, s2kparams, as_key,
- gak_data))) {
- krb5_free_sam_challenge(context, sam_challenge);
- return(ret);
- }
- TRACE_PREAUTH_SAM_KEY_GAK(context, as_key);
- }
- snprintf(name, sizeof(name), "%.*s",
- SAMDATA(sam_challenge->sam_type_name, "SAM Authentication",
- sizeof(name) - 1));
-
- snprintf(banner, sizeof(banner), "%.*s",
- SAMDATA(sam_challenge->sam_challenge_label,
- sam_challenge_banner(sam_challenge->sam_type),
- sizeof(banner)-1));
-
- /* sprintf(prompt, "Challenge is [%s], %s: ", challenge, prompt); */
- snprintf(prompt, sizeof(prompt), "%s%.*s%s%.*s",
- sam_challenge->sam_challenge.length?"Challenge is [":"",
- SAMDATA(sam_challenge->sam_challenge, "", 20),
- sam_challenge->sam_challenge.length?"], ":"",
- SAMDATA(sam_challenge->sam_response_prompt, "passcode", 55));
-
- response_data.data = response;
- response_data.length = sizeof(response);
-
- kprompt.prompt = prompt;
- kprompt.hidden = 1;
- kprompt.reply = &response_data;
- prompt_type = KRB5_PROMPT_TYPE_PREAUTH;
-
- /* PROMPTER_INVOCATION */
- krb5int_set_prompt_types(context, &prompt_type);
- if ((ret = ((*prompter)(context, prompter_data, name,
- banner, 1, &kprompt)))) {
- krb5_free_sam_challenge(context, sam_challenge);
- krb5int_set_prompt_types(context, 0);
- return(ret);
- }
- krb5int_set_prompt_types(context, 0);
-
- enc_sam_response_enc.sam_nonce = sam_challenge->sam_nonce;
- if (sam_challenge->sam_nonce == 0) {
- if ((ret = krb5_us_timeofday(context,
- &enc_sam_response_enc.sam_timestamp,
- &enc_sam_response_enc.sam_usec))) {
- krb5_free_sam_challenge(context,sam_challenge);
- return(ret);
- }
-
- sam_response.sam_patimestamp = enc_sam_response_enc.sam_timestamp;
- }
-
- /* XXX What if more than one flag is set? */
- if (sam_challenge->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD) {
-
- /* Most of this should be taken care of before we get here. We */
- /* will need the user's password and as_key to encrypt the SAD */
- /* and we want to preserve ordering of user prompts (first */
- /* password, then SAM data) so that user's won't be confused. */
-
- if (as_key->length) {
- krb5_free_keyblock_contents(context, as_key);
- as_key->length = 0;
- }
-
- /* generate a salt using the requested principal */
-
- if ((salt->length == -1 || salt->length == SALT_TYPE_AFS_LENGTH) && (salt->data == NULL)) {
- if ((ret = krb5_principal2salt(context, request->client,
- &defsalt))) {
- krb5_free_sam_challenge(context, sam_challenge);
- return(ret);
- }
-
- salt = &defsalt;
- } else {
- defsalt.length = 0;
- }
-
- /* generate a key using the supplied password */
-
- ret = krb5_c_string_to_key(context, ENCTYPE_DES_CBC_MD5,
- (krb5_data *)gak_data, salt, as_key);
-
- if (defsalt.length)
- free(defsalt.data);
-
- if (ret) {
- krb5_free_sam_challenge(context, sam_challenge);
- return(ret);
- }
-
- /* encrypt the passcode with the key from above */
-
- enc_sam_response_enc.sam_sad = response_data;
- } else if (sam_challenge->sam_flags & KRB5_SAM_USE_SAD_AS_KEY) {
-
- /* process the key as password */
-
- if (as_key->length) {
- krb5_free_keyblock_contents(context, as_key);
- as_key->length = 0;
- }
-
-#if 0
- if ((salt->length == SALT_TYPE_AFS_LENGTH) && (salt->data == NULL)) {
- if (ret = krb5_principal2salt(context, request->client,
- &defsalt)) {
- krb5_free_sam_challenge(context, sam_challenge);
- return(ret);
- }
-
- salt = &defsalt;
- } else {
- defsalt.length = 0;
- }
-#else
- defsalt.length = 0;
- salt = NULL;
-#endif
-
- /* XXX As of the passwords-04 draft, no enctype is specified,
- the server uses ENCTYPE_DES_CBC_MD5. In the future the
- server should send a PA-SAM-ETYPE-INFO containing the enctype. */
-
- ret = krb5_c_string_to_key(context, ENCTYPE_DES_CBC_MD5,
- &response_data, salt, as_key);
-
- if (defsalt.length)
- free(defsalt.data);
-
- if (ret) {
- krb5_free_sam_challenge(context, sam_challenge);
- return(ret);
- }
-
- enc_sam_response_enc.sam_sad.length = 0;
- } else {
- /* Eventually, combine SAD with long-term key to get
- encryption key. */
- krb5_free_sam_challenge(context, sam_challenge);
- return KRB5_PREAUTH_BAD_TYPE;
- }
-
- /* copy things from the challenge */
- sam_response.sam_nonce = sam_challenge->sam_nonce;
- sam_response.sam_flags = sam_challenge->sam_flags;
- sam_response.sam_track_id = sam_challenge->sam_track_id;
- sam_response.sam_type = sam_challenge->sam_type;
- sam_response.magic = KV5M_SAM_RESPONSE;
-
- krb5_free_sam_challenge(context, sam_challenge);
-
- /* encode the encoded part of the response */
- if ((ret = encode_krb5_enc_sam_response_enc(&enc_sam_response_enc,
- &scratch)))
- return(ret);
-
- ret = krb5_encrypt_helper(context, as_key, 0, scratch,
- &sam_response.sam_enc_nonce_or_ts);
-
- krb5_free_data(context, scratch);
-
- if (ret)
- return(ret);
-
- /* sam_enc_key is reserved for future use */
- sam_response.sam_enc_key.ciphertext.length = 0;
-
- if ((pa = malloc(sizeof(krb5_pa_data))) == NULL)
- return(ENOMEM);
-
- if ((ret = encode_krb5_sam_response(&sam_response, &scratch))) {
- free(pa);
- return(ret);
- }
-
- pa->magic = KV5M_PA_DATA;
- pa->pa_type = KRB5_PADATA_SAM_RESPONSE;
- pa->length = scratch->length;
- pa->contents = (krb5_octet *) scratch->data;
-
- *out_padata = pa;
-
- free(scratch);
-
- return(0);
-}
-
#if APPLE_PKINIT
/*
* PKINIT. One function to generate AS-REQ, one to parse AS-REP
}
#endif /* APPLE_PKINIT */
+/* this macro expands to the int,ptr necessary for "%.*s" in an sprintf */
+
+#define SAMDATA(kdata, str, maxsize) \
+ (int)((kdata.length)? \
+ ((((kdata.length)<=(maxsize))?(kdata.length):strlen(str))): \
+ strlen(str)), \
+ (kdata.length)? \
+ ((((kdata.length)<=(maxsize))?(kdata.data):(str))):(str)
+static char *
+sam_challenge_banner(krb5_int32 sam_type)
+{
+ char *label;
+
+ switch (sam_type) {
+ case PA_SAM_TYPE_ENIGMA: /* Enigma Logic */
+ label = "Challenge for Enigma Logic mechanism";
+ break;
+ case PA_SAM_TYPE_DIGI_PATH: /* Digital Pathways */
+ case PA_SAM_TYPE_DIGI_PATH_HEX: /* Digital Pathways */
+ label = "Challenge for Digital Pathways mechanism";
+ break;
+ case PA_SAM_TYPE_ACTIVCARD_DEC: /* Digital Pathways */
+ case PA_SAM_TYPE_ACTIVCARD_HEX: /* Digital Pathways */
+ label = "Challenge for Activcard mechanism";
+ break;
+ case PA_SAM_TYPE_SKEY_K0: /* S/key where KDC has key 0 */
+ label = "Challenge for Enhanced S/Key mechanism";
+ break;
+ case PA_SAM_TYPE_SKEY: /* Traditional S/Key */
+ label = "Challenge for Traditional S/Key mechanism";
+ break;
+ case PA_SAM_TYPE_SECURID: /* Security Dynamics */
+ label = "Challenge for Security Dynamics mechanism";
+ break;
+ case PA_SAM_TYPE_SECURID_PREDICT: /* predictive Security Dynamics */
+ label = "Challenge for Security Dynamics mechanism";
+ break;
+ default:
+ label = "Challenge from authentication server";
+ break;
+ }
+
+ return(label);
+}
+
static krb5_error_code
pa_sam_2(krb5_context context, krb5_kdc_req *request, krb5_pa_data *in_padata,
krb5_pa_data **out_padata, krb5_data *salt, krb5_data *s2kparams,
krb5int_set_prompt_types(context, (krb5_prompt_type *)NULL);
/* Generate salt used by string_to_key() */
- if ((salt->length == -1) && (salt->data == NULL)) {
+ if (((int) salt->length == -1) && (salt->data == NULL)) {
if ((retval =
krb5_principal2salt(context, request->client, &defsalt))) {
krb5_free_sam_challenge_2(context, sc2);
pa_sam_2,
PA_REAL,
},
- {
- KRB5_PADATA_SAM_CHALLENGE,
- pa_sam,
- PA_REAL,
- },
{
KRB5_PADATA_FX_COOKIE,
pa_fx_cookie,