/* * This routine is the "obtain" function for the SAM_CHALLENGE * preauthentication type. It presents the challenge... */ 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) { krb5_error_code retval; krb5_data * scratch; krb5_data tmpsam; krb5_pa_data * pa; 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_keyblock * sam_use_key = 0; char * prompt; tmpsam.length = in_padata->length; tmpsam.data = (char *) in_padata->contents; retval = decode_krb5_sam_challenge(&tmpsam, &sam_challenge); if (retval) return retval; if (sam_challenge->sam_flags & KRB5_SAM_MUST_PK_ENCRYPT_SAD) { return KRB5_SAM_UNSUPPORTED; } enc_sam_response_enc.sam_nonce = sam_challenge->sam_nonce; if (!sam_challenge->sam_nonce) { retval = krb5_us_timeofday(context, &enc_sam_response_enc.sam_timestamp, &enc_sam_response_enc.sam_usec); sam_response.sam_patimestamp = enc_sam_response_enc.sam_timestamp; } if (retval) return retval; if (sam_challenge->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD) { /* encrypt passcode in key by stuffing it here */ unsigned int pcsize = 256; char *passcode = malloc(pcsize+1); if (passcode == NULL) return ENOMEM; prompt = handle_sam_labels(sam_challenge); if (prompt == NULL) { free(passcode); return ENOMEM; } retval = krb5_read_password(context, prompt, 0, passcode, &pcsize); free(prompt); if (retval) { free(passcode); return retval; } enc_sam_response_enc.sam_sad.data = passcode; enc_sam_response_enc.sam_sad.length = pcsize; } else if (sam_challenge->sam_flags & KRB5_SAM_USE_SAD_AS_KEY) { prompt = handle_sam_labels(sam_challenge); if (prompt == NULL) return ENOMEM; retval = sam_get_pass_from_user(context, etype_info, key_proc, key_seed, request, &sam_use_key, prompt); free(prompt); if (retval) return retval; enc_sam_response_enc.sam_sad.length = 0; } else { /* what *was* it? */ return KRB5_SAM_UNSUPPORTED; } /* so at this point, either sam_use_key is generated from the passcode * or enc_sam_response_enc.sam_sad is set to it, and we use * def_enc_key instead. */ /* encode the encoded part of the response */ if ((retval = encode_krb5_enc_sam_response_enc(&enc_sam_response_enc, &scratch)) != 0) return retval; if ((retval = krb5_encrypt_data(context, sam_use_key?sam_use_key:def_enc_key, 0, scratch, &sam_response.sam_enc_nonce_or_ts))) goto cleanup; krb5_free_data(context, scratch); scratch = 0; /* sam_enc_key is reserved for future use */ sam_response.sam_enc_key.ciphertext.length = 0; /* 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; if ((retval = encode_krb5_sam_response(&sam_response, &scratch)) != 0) return retval; if ((pa = malloc(sizeof(krb5_pa_data))) == NULL) { retval = ENOMEM; goto cleanup; } pa->magic = KV5M_PA_DATA; pa->pa_type = KRB5_PADATA_SAM_RESPONSE; pa->length = scratch->length; pa->contents = (krb5_octet *) scratch->data; scratch = 0; /* so we don't free it! */ *out_padata = pa; retval = 0; cleanup: if (scratch) krb5_free_data(context, scratch); if (sam_challenge) free(sam_challenge); return retval; }
/*ARGSUSED*/ 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; krb5_enc_data * enc_data; size_t enclen; 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_xfree(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 && *etype == 0) *etype = ENCTYPE_DES_CBC_CRC; if ((ret = (gak_fct)(context, request->client, *etype, prompter, prompter_data, salt, s2kparams, as_key, gak_data))) return(ret); } sprintf(name, "%.*s", SAMDATA(sam_challenge->sam_type_name, "SAM Authentication", sizeof(name) - 1)); sprintf(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); */ sprintf(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_xfree(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_xfree(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->data == NULL)) { if ((ret = krb5_principal2salt(context, request->client, &defsalt))) { krb5_xfree(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) krb5_xfree(defsalt.data); if (ret) { krb5_xfree(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 == -1) && (salt->data == NULL)) { if (ret = krb5_principal2salt(context, request->client, &defsalt)) { krb5_xfree(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) krb5_xfree(defsalt.data); if (ret) { krb5_xfree(sam_challenge); return(ret); } enc_sam_response_enc.sam_sad.length = 0; } else { /* Eventually, combine SAD with long-term key to get encryption key. */ 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_xfree(sam_challenge); /* encode the encoded part of the response */ if ((ret = encode_krb5_enc_sam_response_enc(&enc_sam_response_enc, &scratch))) return(ret); /* * Solaris Kerberos: * Using new crypto interface now so we can get rid of the * old modules. */ if ((ret = krb5_c_encrypt_length(context, as_key->enctype, scratch->length, &enclen))) { krb5_free_data(context, scratch); return(ret); } enc_data = &sam_response.sam_enc_nonce_or_ts; enc_data->magic = KV5M_ENC_DATA; enc_data->kvno = 0; enc_data->enctype = as_key->enctype; enc_data->ciphertext.length = enclen; if ((enc_data->ciphertext.data = MALLOC(enclen)) == NULL) { enc_data->ciphertext.length = 0; krb5_free_data(context, scratch); return(ENOMEM); } if ((ret = krb5_c_encrypt(context, as_key, 0, 0, scratch, enc_data))) { FREE(enc_data->ciphertext.data, enclen); enc_data->ciphertext.data = NULL; enc_data->ciphertext.length = 0; } 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; return(0); }