krb5_error_code _adcli_krb5_keytab_add_entries (krb5_context k5, krb5_keytab keytab, krb5_principal principal, krb5_kvno kvno, krb5_data *password, krb5_enctype *enctypes, krb5_data *salt) { krb5_keytab_entry entry; krb5_error_code code; int i; for (i = 0; enctypes[i] != 0; i++) { memset (&entry, 0, sizeof(entry)); code = krb5_c_string_to_key (k5, enctypes[i], password, salt, &entry.key); if (code != 0) return code; entry.principal = principal; entry.vno = kvno; code = krb5_kt_add_entry (k5, keytab, &entry); entry.principal = NULL; krb5_free_keytab_entry_contents (k5, &entry); if (code != 0) return code; } return 0; }
void KRB5Keyblock::from_string(krb5_enctype enctype, const std::string &password, const std::string &salt) { #ifdef HEIMDAL krb5_data pass_data; krb5_salt salt_data; salt_data.salttype = KRB5_PW_SALT; salt_data.saltvalue.data = const_cast<char *>(salt.c_str()); salt_data.saltvalue.length = salt.length(); pass_data.data = const_cast<char *>(password.c_str()); pass_data.length = password.length(); krb5_error_code ret = krb5_string_to_key_data_salt(g_context.get(), enctype, pass_data, salt_data, &m_keyblock); if (ret) { throw KRB5Exception("krb5_string_to_key_data_salt", ret); } #else krb5_data salt_data, pass_data; salt_data.data = const_cast<char *>(salt.c_str()); salt_data.length = salt.length(); pass_data.data = const_cast<char *>(password.c_str()); pass_data.length = password.length(); krb5_error_code ret = krb5_c_string_to_key(g_context.get(), enctype, &pass_data, &salt_data, &m_keyblock); if (ret) { throw KRB5Exception("krb5_c_string_to_key", ret); } #endif }
krb5_error_code KRB5_CALLCONV krb5_string_to_key(krb5_context context, const krb5_encrypt_block *eblock, krb5_keyblock *keyblock, const krb5_data *data, const krb5_data *salt) { return(krb5_c_string_to_key(context, eblock->crypto_entry, data, salt, keyblock)); }
krb5_error_code srp_gen_keyblock( krb5_context krb5_ctx, char *enc_keytype, char *pass, char *salt, krb5_keyblock *key) { krb5_error_code krb_err = 0; krb5_enctype enctype; krb5_data pass_data = {0}; krb5_data salt_data = {0}; memset(&enctype, 0, sizeof(enctype)); pass_data.data = pass; pass_data.length = (int) strlen(pass); salt_data.data = salt; salt_data.length = (int) strlen(salt); #if 0 /* Prefer to use this, as it takes ENCTYPE_AES256_CTS_HMAC_SHA1_96 */ enctype = find_enctype(enc_keytype); if (!enctype) { krb_err = EINVAL; goto error; } #else krb_err = krb5_string_to_enctype( enc_keytype, &enctype); if (krb_err) { goto error; } #endif krb_err = krb5_c_string_to_key( krb5_ctx, enctype, &pass_data, &salt_data, key); if (krb_err) { goto error; } error: return krb_err; }
mit_krb5_error_code KRB5_CALLCONV krb5_string_to_key(mit_krb5_context context, const mit_krb5_encrypt_block * eblock, mit_krb5_keyblock * keyblock, const mit_krb5_data * data, const mit_krb5_data * salt) { LOG_ENTRY(); return krb5_c_string_to_key(context, eblock->crypto_entry, data, salt, keyblock); }
static void test_s2k (krb5_enctype enctype) { static const struct { const char *pass; const char *salt; } pairs[] = { { "password", "ATHENA.MIT.EDUraeburn" }, { "potatoe", "WHITEHOUSE.GOVdanny" }, { "penny", "EXAMPLE.COMbuckaroo", }, { ESZETT, "ATHENA.MIT.EDU" JURISIC }, { GCLEF, "EXAMPLE.COMpianist" }, }; int i; for (i = 0; i < ASIZE (pairs); i++) { const char *p = pairs[i].pass; const char *s = pairs[i].salt; krb5_data pd, sd; unsigned char key_contents[60]; krb5_keyblock key; krb5_error_code r; char buf[80]; pd.length = strlen (p); pd.data = (char *) p; sd.length = strlen (s); sd.data = (char *) s; key.contents = key_contents; assert (strlen (s) + 4 < sizeof (buf)); snprintf (buf, sizeof(buf), "\"%s\"", s); printf ( "salt:\t%s\n\t", buf); printhex (strlen(s), s); snprintf (buf, sizeof(buf), "\"%s\"", p); printf ("\npasswd:\t%s\n\t", buf); printhex (strlen(p), p); printf ("\n"); r = krb5_c_string_to_key (0, enctype, &pd, &sd, &key); printf ( "key:\t"); printhex (key.length, key.contents); printf ("\n\n"); } }
krb5_error_code krb5_db_fetch_mkey(krb5_context context, krb5_principal mname, krb5_enctype etype, krb5_boolean fromkeyboard, krb5_boolean twice, char *db_args, krb5_data * salt, krb5_keyblock * key) { krb5_error_code retval; char password[BUFSIZ]; krb5_data pwd; unsigned int size = sizeof(password); int kvno; krb5_keyblock tmp_key; memset(&tmp_key, 0, sizeof(tmp_key)); if (fromkeyboard) { krb5_data scratch; if ((retval = krb5_read_password(context, krb5_mkey_pwd_prompt1, twice ? krb5_mkey_pwd_prompt2 : 0, password, &size))) { goto clean_n_exit; } pwd.data = password; pwd.length = size; if (!salt) { retval = krb5_principal2salt(context, mname, &scratch); if (retval) goto clean_n_exit; } retval = krb5_c_string_to_key(context, etype, &pwd, salt ? salt : &scratch, key); if (!salt) krb5_xfree(scratch.data); memset(password, 0, sizeof(password)); /* erase it */ } else { kdb5_dal_handle *dal_handle; if (context->db_context == NULL) { retval = kdb_setup_lib_handle(context); if (retval) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) context->db_context; retval = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE); if (retval) { goto clean_n_exit; } tmp_key.enctype = key->enctype; retval = dal_handle->lib_handle->vftabl.fetch_master_key(context, mname, &tmp_key, &kvno, db_args); get_errmsg(context, retval); kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE); if (retval) { goto clean_n_exit; } key->contents = malloc(tmp_key.length); if (key->contents == NULL) { retval = ENOMEM; goto clean_n_exit; } key->magic = tmp_key.magic; key->enctype = tmp_key.enctype; key->length = tmp_key.length; memcpy(key->contents, tmp_key.contents, tmp_key.length); } clean_n_exit: if (tmp_key.contents) { memset(tmp_key.contents, 0, tmp_key.length); krb5_db_free(context, tmp_key.contents); } return retval; }
int create_keys(krb5_context krbctx, krb5_principal princ, char *password, const char *enctypes_string, struct keys_container *keys, char **err_msg) { struct krb_key_salt *ksdata; krb5_error_code krberr; krb5_data key_password; krb5_data *realm = NULL; int i, nkeys; int ret; *err_msg = NULL; ret = prep_ksdata(krbctx, enctypes_string, keys, err_msg); if (ret == 0) return 0; ksdata = keys->ksdata; nkeys = keys->nkeys; if (password) { key_password.data = password; key_password.length = strlen(password); realm = krb5_princ_realm(krbctx, princ); } for (i = 0; i < nkeys; i++) { krb5_data *salt; if (!password) { /* cool, random keys */ krberr = krb5_c_make_random_key(krbctx, ksdata[i].enctype, &ksdata[i].key); if (krberr) { *err_msg = _("Failed to create random key!\n"); return 0; } /* set the salt to NO_SALT as the key was random */ ksdata[i].salttype = NO_SALT; continue; } /* Make keys using password and required salt */ switch (ksdata[i].salttype) { case KRB5_KDB_SALTTYPE_ONLYREALM: krberr = krb5_copy_data(krbctx, realm, &salt); if (krberr) { *err_msg = _("Failed to create key!\n"); return 0; } ksdata[i].salt.length = salt->length; ksdata[i].salt.data = malloc(salt->length); if (!ksdata[i].salt.data) { *err_msg = _("Out of memory!\n"); return 0; } memcpy(ksdata[i].salt.data, salt->data, salt->length); krb5_free_data(krbctx, salt); break; case KRB5_KDB_SALTTYPE_NOREALM: krberr = ipa_krb5_principal2salt_norealm(krbctx, princ, &ksdata[i].salt); if (krberr) { *err_msg = _("Failed to create key!\n"); return 0; } break; case KRB5_KDB_SALTTYPE_NORMAL: krberr = krb5_principal2salt(krbctx, princ, &ksdata[i].salt); if (krberr) { *err_msg = _("Failed to create key!\n"); return 0; } break; /* no KRB5_KDB_SALTTYPE_V4, we do not support krb v4 */ case KRB5_KDB_SALTTYPE_AFS3: /* Comment from MIT sources: * * Why do we do this? Well, the afs_mit_string_to_key * * needs to use strlen, and the realm is not NULL * * terminated.... */ ksdata[i].salt.data = (char *)malloc(realm->length + 1); if (NULL == ksdata[i].salt.data) { *err_msg = _("Out of memory!\n"); return 0; } memcpy((char *)ksdata[i].salt.data, (char *)realm->data, realm->length); ksdata[i].salt.data[realm->length] = '\0'; /* AFS uses a special length (UGLY) */ ksdata[i].salt.length = SALT_TYPE_AFS_LENGTH; break; default: *err_msg = _("Bad or unsupported salt type.\n"); /* FIXME: fprintf(stderr, _("Bad or unsupported salt type (%d)!\n"), ksdata[i].salttype); */ return 0; } krberr = krb5_c_string_to_key(krbctx, ksdata[i].enctype, &key_password, &ksdata[i].salt, &ksdata[i].key); if (krberr) { *err_msg = _("Failed to create key!\n"); return 0; } /* set back salt length to real value if AFS3 */ if (ksdata[i].salttype == KRB5_KDB_SALTTYPE_AFS3) { ksdata[i].salt.length = realm->length; } } return nkeys; }
/* * Generate a krb5_key_data set by encrypting keys according to * enctype/salttype preferences */ krb5_error_code ipa_krb5_generate_key_data(krb5_context krbctx, krb5_principal principal, krb5_data pwd, int kvno, krb5_keyblock *kmkey, int num_encsalts, krb5_key_salt_tuple *encsalts, int *_num_keys, krb5_key_data **_keys) { krb5_error_code kerr; krb5_key_data *keys; int num_keys; int i; num_keys = num_encsalts; keys = calloc(num_keys, sizeof(krb5_key_data)); if (!keys) { return ENOMEM; } for (i = 0; i < num_keys; i++) { krb5_keyblock key; krb5_data salt; krb5_octet *ptr; krb5_data plain; krb5_enc_data cipher; krb5_int16 t; size_t len; salt.data = NULL; keys[i].key_data_ver = 2; /* we always have a salt */ keys[i].key_data_kvno = kvno; switch (encsalts[i].ks_salttype) { case KRB5_KDB_SALTTYPE_ONLYREALM: if (!principal->realm.data) { kerr = EINVAL; goto done; } salt.length = principal->realm.length; salt.data = malloc(salt.length); if (!salt.data) { kerr = ENOMEM; goto done; } memcpy(salt.data, principal->realm.data, salt.length); break; case KRB5_KDB_SALTTYPE_NOREALM: kerr = ipa_krb5_principal2salt_norealm(krbctx, principal, &salt); if (kerr) { goto done; } break; case KRB5_KDB_SALTTYPE_NORMAL: kerr = krb5_principal2salt(krbctx, principal, &salt); if (kerr) { goto done; } break; case KRB5_KDB_SALTTYPE_SPECIAL: kerr = ipa_get_random_salt(krbctx, &salt); if (kerr) { goto done; } break; case KRB5_KDB_SALTTYPE_V4: salt.length = 0; break; case KRB5_KDB_SALTTYPE_AFS3: if (!principal->realm.data) { kerr = EINVAL; goto done; } salt.data = strndup((char *)principal->realm.data, principal->realm.length); if (!salt.data) { kerr = ENOMEM; goto done; } salt.length = SALT_TYPE_AFS_LENGTH; /* special value */ break; default: kerr = EINVAL; goto done; } /* need to build the key now to manage the AFS salt.length * special case */ kerr = krb5_c_string_to_key(krbctx, encsalts[i].ks_enctype, &pwd, &salt, &key); if (kerr) { krb5_free_data_contents(krbctx, &salt); goto done; } if (salt.length == SALT_TYPE_AFS_LENGTH) { salt.length = strlen(salt.data); } kerr = krb5_c_encrypt_length(krbctx, kmkey->enctype, key.length, &len); if (kerr) { krb5int_c_free_keyblock_contents(krbctx, &key); krb5_free_data_contents(krbctx, &salt); goto done; } if ((ptr = (krb5_octet *) malloc(2 + len)) == NULL) { kerr = ENOMEM; krb5int_c_free_keyblock_contents(krbctx, &key); krb5_free_data_contents(krbctx, &salt); goto done; } t = htole16(key.length); memcpy(ptr, &t, 2); plain.length = key.length; plain.data = (char *)key.contents; cipher.ciphertext.length = len; cipher.ciphertext.data = (char *)ptr+2; kerr = krb5_c_encrypt(krbctx, kmkey, 0, 0, &plain, &cipher); if (kerr) { krb5int_c_free_keyblock_contents(krbctx, &key); krb5_free_data_contents(krbctx, &salt); free(ptr); goto done; } /* KrbSalt */ keys[i].key_data_type[1] = encsalts[i].ks_salttype; if (salt.length) { keys[i].key_data_length[1] = salt.length; keys[i].key_data_contents[1] = (krb5_octet *)salt.data; } /* EncryptionKey */ keys[i].key_data_type[0] = key.enctype; keys[i].key_data_length[0] = len + 2; keys[i].key_data_contents[0] = malloc(len + 2); if (!keys[i].key_data_contents[0]) { kerr = ENOMEM; krb5int_c_free_keyblock_contents(krbctx, &key); free(ptr); goto done; } memcpy(keys[i].key_data_contents[0], ptr, len + 2); /* make sure we free the memory used now that we are done with it */ krb5int_c_free_keyblock_contents(krbctx, &key); free(ptr); } *_num_keys = num_keys; *_keys = keys; kerr = 0; done: if (kerr) { ipa_krb5_free_key_data(keys, num_keys); } return kerr; }
static krb5_error_code sam2_process(krb5_context context, krb5_clpreauth_moddata moddata, krb5_clpreauth_modreq modreq, krb5_get_init_creds_opt *opt, krb5_clpreauth_callbacks cb, krb5_clpreauth_rock rock, krb5_kdc_req *request, krb5_data *encoded_request_body, krb5_data *encoded_previous_request, krb5_pa_data *padata, krb5_prompter_fct prompter, void *prompter_data, krb5_pa_data ***out_padata) { krb5_error_code retval; krb5_sam_challenge_2 *sc2 = NULL; krb5_sam_challenge_2_body *sc2b = NULL; krb5_data tmp_data; krb5_data response_data; char name[100], banner[100], prompt[100], response[100]; krb5_prompt kprompt; krb5_prompt_type prompt_type; krb5_data defsalt, *salt; krb5_checksum **cksum; krb5_data *scratch = NULL; krb5_boolean valid_cksum = 0; krb5_enc_sam_response_enc_2 enc_sam_response_enc_2; krb5_sam_response_2 sr2; size_t ciph_len; krb5_pa_data **sam_padata; if (prompter == NULL) return KRB5_LIBOS_CANTREADPWD; tmp_data.length = padata->length; tmp_data.data = (char *)padata->contents; if ((retval = decode_krb5_sam_challenge_2(&tmp_data, &sc2))) return(retval); retval = decode_krb5_sam_challenge_2_body(&sc2->sam_challenge_2_body, &sc2b); if (retval) { krb5_free_sam_challenge_2(context, sc2); return(retval); } if (!sc2->sam_cksum || ! *sc2->sam_cksum) { krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); return(KRB5_SAM_NO_CHECKSUM); } if (sc2b->sam_flags & KRB5_SAM_MUST_PK_ENCRYPT_SAD) { krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); return(KRB5_SAM_UNSUPPORTED); } if (!krb5_c_valid_enctype(sc2b->sam_etype)) { krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); return(KRB5_SAM_INVALID_ETYPE); } /* All of the above error checks are KDC-specific, that is, they */ /* assume a failure in the KDC reply. By returning anything other */ /* than KRB5_KDC_UNREACH, KRB5_PREAUTH_FAILED, */ /* KRB5_LIBOS_PWDINTR, or KRB5_REALM_CANT_RESOLVE, the client will */ /* most likely go on to try the AS_REQ against master KDC */ if (!(sc2b->sam_flags & KRB5_SAM_USE_SAD_AS_KEY)) { /* We will need the password to obtain the key used for */ /* the checksum, and encryption of the sam_response. */ /* Go ahead and get it now, preserving the ordering of */ /* prompts for the user. */ retval = (*rock->gak_fct)(context, request->client, sc2b->sam_etype, prompter, prompter_data, rock->salt, rock->s2kparams, rock->as_key, *rock->gak_data); if (retval) { krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); return(retval); } } snprintf(name, sizeof(name), "%.*s", SAMDATA(sc2b->sam_type_name, _("SAM Authentication"), sizeof(name) - 1)); snprintf(banner, sizeof(banner), "%.*s", SAMDATA(sc2b->sam_challenge_label, sam_challenge_banner(sc2b->sam_type), sizeof(banner)-1)); snprintf(prompt, sizeof(prompt), "%s%.*s%s%.*s", sc2b->sam_challenge.length?"Challenge is [":"", SAMDATA(sc2b->sam_challenge, "", 20), sc2b->sam_challenge.length?"], ":"", SAMDATA(sc2b->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; krb5int_set_prompt_types(context, &prompt_type); if ((retval = ((*prompter)(context, prompter_data, name, banner, 1, &kprompt)))) { krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); krb5int_set_prompt_types(context, 0); return(retval); } krb5int_set_prompt_types(context, (krb5_prompt_type *)NULL); /* Generate salt used by string_to_key() */ salt = rock->salt; if (((int) salt->length == -1) && (salt->data == NULL)) { if ((retval = krb5_principal2salt(context, request->client, &defsalt))) { krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); return(retval); } salt = &defsalt; } else { defsalt.length = 0; } /* Get encryption key to be used for checksum and sam_response */ if (!(sc2b->sam_flags & KRB5_SAM_USE_SAD_AS_KEY)) { /* as_key = string_to_key(password) */ if (rock->as_key->length) { krb5_free_keyblock_contents(context, rock->as_key); rock->as_key->length = 0; } /* generate a key using the supplied password */ retval = krb5_c_string_to_key(context, sc2b->sam_etype, (krb5_data *)*rock->gak_data, salt, rock->as_key); if (retval) { krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); if (defsalt.length) free(defsalt.data); return(retval); } if (!(sc2b->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD)) { /* as_key = combine_key (as_key, string_to_key(SAD)) */ krb5_keyblock tmp_kb; retval = krb5_c_string_to_key(context, sc2b->sam_etype, &response_data, salt, &tmp_kb); if (retval) { krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); if (defsalt.length) free(defsalt.data); return(retval); } /* This should be a call to the crypto library some day */ /* key types should already match the sam_etype */ retval = krb5int_c_combine_keys(context, rock->as_key, &tmp_kb, rock->as_key); if (retval) { krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); if (defsalt.length) free(defsalt.data); return(retval); } krb5_free_keyblock_contents(context, &tmp_kb); } if (defsalt.length) free(defsalt.data); } else { /* as_key = string_to_key(SAD) */ if (rock->as_key->length) { krb5_free_keyblock_contents(context, rock->as_key); rock->as_key->length = 0; } /* generate a key using the supplied password */ retval = krb5_c_string_to_key(context, sc2b->sam_etype, &response_data, salt, rock->as_key); if (defsalt.length) free(defsalt.data); if (retval) { krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); return(retval); } } /* Now we have a key, verify the checksum on the sam_challenge */ cksum = sc2->sam_cksum; for (; *cksum; cksum++) { if (!krb5_c_is_keyed_cksum((*cksum)->checksum_type)) continue; /* Check this cksum */ retval = krb5_c_verify_checksum(context, rock->as_key, KRB5_KEYUSAGE_PA_SAM_CHALLENGE_CKSUM, &sc2->sam_challenge_2_body, *cksum, &valid_cksum); if (retval) { krb5_free_data(context, scratch); krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); return(retval); } if (valid_cksum) break; } if (!valid_cksum) { krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); /* * Note: We return AP_ERR_BAD_INTEGRITY so upper-level applications * can interpret that as "password incorrect", which is probably * the best error we can return in this situation. */ return(KRB5KRB_AP_ERR_BAD_INTEGRITY); } /* fill in enc_sam_response_enc_2 */ enc_sam_response_enc_2.magic = KV5M_ENC_SAM_RESPONSE_ENC_2; enc_sam_response_enc_2.sam_nonce = sc2b->sam_nonce; if (sc2b->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD) { enc_sam_response_enc_2.sam_sad = response_data; } else { enc_sam_response_enc_2.sam_sad.data = NULL; enc_sam_response_enc_2.sam_sad.length = 0; } /* encode and encrypt enc_sam_response_enc_2 with as_key */ retval = encode_krb5_enc_sam_response_enc_2(&enc_sam_response_enc_2, &scratch); if (retval) { krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); return(retval); } /* Fill in sam_response_2 */ memset(&sr2, 0, sizeof(sr2)); sr2.sam_type = sc2b->sam_type; sr2.sam_flags = sc2b->sam_flags; sr2.sam_track_id = sc2b->sam_track_id; sr2.sam_nonce = sc2b->sam_nonce; /* Now take care of sr2.sam_enc_nonce_or_sad by encrypting encoded */ /* enc_sam_response_enc_2 from above */ retval = krb5_c_encrypt_length(context, rock->as_key->enctype, scratch->length, &ciph_len); if (retval) { krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); krb5_free_data(context, scratch); return(retval); } sr2.sam_enc_nonce_or_sad.ciphertext.length = ciph_len; sr2.sam_enc_nonce_or_sad.ciphertext.data = (char *)malloc(sr2.sam_enc_nonce_or_sad.ciphertext.length); if (!sr2.sam_enc_nonce_or_sad.ciphertext.data) { krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); krb5_free_data(context, scratch); return(ENOMEM); } retval = krb5_c_encrypt(context, rock->as_key, KRB5_KEYUSAGE_PA_SAM_RESPONSE, NULL, scratch, &sr2.sam_enc_nonce_or_sad); if (retval) { krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); krb5_free_data(context, scratch); krb5_free_data_contents(context, &sr2.sam_enc_nonce_or_sad.ciphertext); return(retval); } krb5_free_data(context, scratch); scratch = NULL; /* Encode the sam_response_2 */ retval = encode_krb5_sam_response_2(&sr2, &scratch); krb5_free_sam_challenge_2(context, sc2); krb5_free_sam_challenge_2_body(context, sc2b); krb5_free_data_contents(context, &sr2.sam_enc_nonce_or_sad.ciphertext); if (retval) { return (retval); } /* Almost there, just need to make padata ! */ sam_padata = malloc(2 * sizeof(*sam_padata)); if (sam_padata == NULL) { krb5_free_data(context, scratch); return(ENOMEM); } sam_padata[0] = malloc(sizeof(krb5_pa_data)); if (sam_padata[0] == NULL) { krb5_free_data(context, scratch); free(sam_padata); return(ENOMEM); } sam_padata[0]->magic = KV5M_PA_DATA; sam_padata[0]->pa_type = KRB5_PADATA_SAM_RESPONSE_2; sam_padata[0]->length = scratch->length; sam_padata[0]->contents = (krb5_octet *) scratch->data; free(scratch); sam_padata[1] = NULL; *out_padata = sam_padata; return(0); }
DWORD LwKrb5InitializeUserLoginCredentials( IN PCSTR pszUserPrincipalName, IN PCSTR pszPassword, IN uid_t uid, IN gid_t gid, IN LW_KRB5_LOGIN_FLAGS Flags, IN PCSTR pszServicePrincipal, IN PCSTR pszServiceRealm, IN PCSTR pszServicePassword, OUT PVOID* ppNdrPacInfo, OUT size_t* pNdrPacInfoSize, OUT PDWORD pdwGoodUntilTime ) { DWORD dwError = 0; krb5_error_code ret = 0; krb5_context ctx = NULL; krb5_ccache cc = NULL; // Free with krb5_free_cred_contents krb5_creds credsRequest = {0}; krb5_creds *pTgsCreds = NULL; krb5_ticket *pTgsTicket = NULL; krb5_ticket *pDecryptedTgs = NULL; krb5_auth_context authContext = NULL; krb5_data apReqPacket = {0}; krb5_keyblock serviceKey = {0}; krb5_data salt = {0}; // Do not free krb5_data machinePassword = {0}; krb5_flags flags = 0; krb5_int32 authcon_flags = 0; BOOLEAN bInLock = FALSE; PCSTR pszTempCacheName = NULL; PSTR pszTempCachePath = NULL; PVOID pNdrPacInfo = NULL; size_t ndrPacInfoSize = 0; DWORD dwGoodUntilTime = 0; ret = krb5_init_context(&ctx); BAIL_ON_KRB_ERROR(ctx, ret); /* Generates a new filed based credentials cache in /tmp. The file will * be owned by root and only accessible by root. */ ret = krb5_cc_new_unique( ctx, "FILE", "hint", &cc); BAIL_ON_KRB_ERROR(ctx, ret); if (Flags & LW_KRB5_LOGIN_FLAG_SMART_CARD) { dwError = LwKrb5GetTgtWithSmartCard( pszUserPrincipalName, pszPassword, krb5_cc_get_name(ctx, cc), &dwGoodUntilTime); } else { dwError = LwKrb5GetTgt( pszUserPrincipalName, pszPassword, krb5_cc_get_name(ctx, cc), &dwGoodUntilTime); } BAIL_ON_LW_ERROR(dwError); ret = krb5_parse_name(ctx, pszServicePrincipal, &credsRequest.server); BAIL_ON_KRB_ERROR(ctx, ret); ret = krb5_cc_get_principal(ctx, cc, &credsRequest.client); BAIL_ON_KRB_ERROR(ctx, ret); /* Get a TGS for our service using the tgt in the cache */ ret = krb5_get_credentials( ctx, 0, /*no options (not user to user encryption, and not only cached) */ cc, &credsRequest, &pTgsCreds); // Don't trust pTgsCreds on an unsuccessful return // This may be non-zero due to the krb5 libs following referrals // but has been freed in the krb5 libs themselves and any useful // tickets have already been cached. if (ret != 0) { pTgsCreds = NULL; } BAIL_ON_KRB_ERROR(ctx, ret); //No need to store the tgs in the cc. Kerberos does that automatically /* Generate an ap_req message, but don't send it anywhere. Just decode it * immediately. This is the only way to get kerberos to decrypt the tgs * using public APIs */ ret = krb5_mk_req_extended( ctx, &authContext, 0, /* no options necessary */ NULL, /* since this isn't a real ap_req, we don't have any supplemental data to send with it. */ pTgsCreds, &apReqPacket); BAIL_ON_KRB_ERROR(ctx, ret); /* Decode (but not decrypt) the tgs ticket so that we can figure out * which encryption type was used in it. */ ret = krb5_decode_ticket(&pTgsCreds->ticket, &pTgsTicket); /* The TGS ticket is encrypted with the machine password and salted with * the service principal. pszServicePrincipal could probably be used * directly, but it's safer to unparse pTgsCreds->server, because the KDC * sent that to us. */ salt.magic = KV5M_DATA; ret = krb5_unparse_name( ctx, pTgsCreds->server, &salt.data); BAIL_ON_KRB_ERROR(ctx, ret); salt.length = strlen(salt.data); machinePassword.magic = KV5M_DATA; machinePassword.data = (PSTR)pszServicePassword, machinePassword.length = strlen(pszServicePassword), /* Generate a key to decrypt the TGS */ ret = krb5_c_string_to_key( ctx, pTgsTicket->enc_part.enctype, &machinePassword, &salt, &serviceKey); BAIL_ON_KRB_ERROR(ctx, ret); /* Typically krb5_rd_req would decode the AP_REQ using the keytab, but * we don't want to depend on the keytab. As a side effect of kerberos' * user to user authentication support, if a key is explictly set on the * auth context, that key will be used to decrypt the TGS instead of the * keytab. * * By manually generating the key and setting it, we don't require * a keytab. */ if (authContext != NULL) { ret = krb5_auth_con_free(ctx, authContext); BAIL_ON_KRB_ERROR(ctx, ret); } ret = krb5_auth_con_init(ctx, &authContext); BAIL_ON_KRB_ERROR(ctx, ret); ret = krb5_auth_con_setuseruserkey( ctx, authContext, &serviceKey); BAIL_ON_KRB_ERROR(ctx, ret); /* Disable replay detection which is unnecessary and * can fail when authenticating large numbers of users. */ krb5_auth_con_getflags(ctx, authContext, &authcon_flags); krb5_auth_con_setflags(ctx, authContext, authcon_flags & ~KRB5_AUTH_CONTEXT_DO_TIME); if (pszServiceRealm) { ret = krb5_set_default_realm(ctx, pszServiceRealm); BAIL_ON_KRB_ERROR(ctx, ret); } /* This decrypts the TGS. As a side effect it ensures that the KDC that * the user's TGT came from is in the same realm that the machine was * joined to (this prevents users from spoofing the KDC). */ ret = krb5_rd_req( ctx, &authContext, &apReqPacket, pTgsCreds->server, NULL, /* we're not using the keytab */ &flags, &pDecryptedTgs); BAIL_ON_KRB_ERROR(ctx, ret); dwError = LwKrb5FindPac( ctx, pDecryptedTgs, &serviceKey, &pNdrPacInfo, &ndrPacInfoSize); BAIL_ON_LW_ERROR(dwError); if (Flags & LW_KRB5_LOGIN_FLAG_UPDATE_CACHE) { /* 1. Copy old credentials from the existing user creds cache to * the temporary cache. * 2. Delete the existing creds cache. * 3. Move the temporary cache file into the final path. */ dwError = pthread_mutex_lock(&gLwKrb5State.UserCacheMutex); BAIL_ON_LW_ERROR(dwError); bInLock = TRUE; dwError = LwKrb5CopyFromUserCache( ctx, cc, uid ); BAIL_ON_LW_ERROR(dwError); pszTempCacheName = krb5_cc_get_name(ctx, cc); if (!strncasecmp(pszTempCacheName, "FILE:", sizeof("FILE:")-1)) { pszTempCacheName += sizeof("FILE:") - 1; } dwError = LwAllocateString(pszTempCacheName, &pszTempCachePath); BAIL_ON_LW_ERROR(dwError); krb5_cc_close(ctx, cc); // Just to make sure no one accesses this now invalid pointer cc = NULL; dwError = LwKrb5MoveCCacheToUserPath( ctx, pszTempCachePath, uid, gid); if (dwError != LW_ERROR_SUCCESS) { /* Let the user login, even if we couldn't create the ccache for * them. Possible causes are: * 1. /tmp is readonly * 2. Another user maliciously setup a weird file (such as a * directory) where the ccache would go. * 3. Someone created a ccache in the small window after we delete * the old one and before we move in the new one. */ LW_LOG_WARNING("Unable to set up credentials cache with tgt for uid %ld", (long)uid); dwError = LwRemoveFile(pszTempCachePath); BAIL_ON_LW_ERROR(dwError); } } error: if (dwError) { LW_SAFE_FREE_MEMORY(pNdrPacInfo); ndrPacInfoSize = 0; dwGoodUntilTime = 0; } if (ctx) { // This function skips fields which are NULL krb5_free_cred_contents(ctx, &credsRequest); if (pTgsCreds != NULL) { krb5_free_creds(ctx, pTgsCreds); } if (pTgsTicket != NULL) { krb5_free_ticket(ctx, pTgsTicket); } if (pDecryptedTgs != NULL) { krb5_free_ticket(ctx, pDecryptedTgs); } if (authContext != NULL) { krb5_auth_con_free(ctx, authContext); } krb5_free_data_contents(ctx, &apReqPacket); krb5_free_data_contents(ctx, &salt); krb5_free_keyblock_contents(ctx, &serviceKey); if (cc != NULL) { krb5_cc_destroy(ctx, cc); } krb5_free_context(ctx); } if (bInLock) { pthread_mutex_unlock(&gLwKrb5State.UserCacheMutex); } LW_SAFE_FREE_STRING(pszTempCachePath); *ppNdrPacInfo = pNdrPacInfo; *pNdrPacInfoSize = ndrPacInfoSize; *pdwGoodUntilTime = dwGoodUntilTime; return dwError; }
void kdb5_add_mkey(int argc, char *argv[]) { int optchar; krb5_error_code retval; char *mkey_fullname; char *pw_str = 0; unsigned int pw_size = 0; int do_stash = 0; krb5_data pwd; krb5_kvno new_mkey_kvno; krb5_keyblock new_mkeyblock; krb5_enctype new_master_enctype = ENCTYPE_UNKNOWN; char *new_mkey_password; krb5_db_entry *master_entry; krb5_timestamp now; /* * The command table entry for this command causes open_db_and_mkey() to be * called first to open the KDB and get the current mkey. */ memset(&new_mkeyblock, 0, sizeof(new_mkeyblock)); memset(&master_princ, 0, sizeof(master_princ)); master_salt.data = NULL; while ((optchar = getopt(argc, argv, "e:s")) != -1) { switch(optchar) { case 'e': if (krb5_string_to_enctype(optarg, &new_master_enctype)) { com_err(progname, EINVAL, _("%s is an invalid enctype"), optarg); exit_status++; return; } break; case 's': do_stash++; break; case '?': default: usage(); return; } } if (new_master_enctype == ENCTYPE_UNKNOWN) new_master_enctype = global_params.enctype; /* assemble & parse the master key name */ if ((retval = krb5_db_setup_mkey_name(util_context, global_params.mkey_name, global_params.realm, &mkey_fullname, &master_princ))) { com_err(progname, retval, _("while setting up master key name")); exit_status++; return; } retval = krb5_db_get_principal(util_context, master_princ, 0, &master_entry); if (retval != 0) { com_err(progname, retval, _("while getting master key principal %s"), mkey_fullname); exit_status++; goto cleanup_return; } printf(_("Creating new master key for master key principal '%s'\n"), mkey_fullname); printf(_("You will be prompted for a new database Master Password.\n")); printf(_("It is important that you NOT FORGET this password.\n")); fflush(stdout); pw_size = 1024; pw_str = malloc(pw_size); if (pw_str == NULL) { com_err(progname, ENOMEM, _("while creating new master key")); exit_status++; goto cleanup_return; } retval = krb5_read_password(util_context, KRB5_KDC_MKEY_1, KRB5_KDC_MKEY_2, pw_str, &pw_size); if (retval) { com_err(progname, retval, _("while reading new master key from keyboard")); exit_status++; goto cleanup_return; } new_mkey_password = pw_str; pwd.data = new_mkey_password; pwd.length = strlen(new_mkey_password); retval = krb5_principal2salt(util_context, master_princ, &master_salt); if (retval) { com_err(progname, retval, _("while calculating master key salt")); exit_status++; goto cleanup_return; } retval = krb5_c_string_to_key(util_context, new_master_enctype, &pwd, &master_salt, &new_mkeyblock); if (retval) { com_err(progname, retval, _("while transforming master key from password")); exit_status++; goto cleanup_return; } new_mkey_kvno = get_next_kvno(util_context, master_entry); retval = add_new_mkey(util_context, master_entry, &new_mkeyblock, new_mkey_kvno); if (retval) { com_err(progname, retval, _("adding new master key to master principal")); exit_status++; goto cleanup_return; } if ((retval = krb5_timeofday(util_context, &now))) { com_err(progname, retval, _("while getting current time")); exit_status++; goto cleanup_return; } if ((retval = krb5_dbe_update_mod_princ_data(util_context, master_entry, now, master_princ))) { com_err(progname, retval, _("while updating the master key principal " "modification time")); exit_status++; goto cleanup_return; } if ((retval = krb5_db_put_principal(util_context, master_entry))) { (void) krb5_db_fini(util_context); com_err(progname, retval, _("while adding master key entry to the " "database")); exit_status++; goto cleanup_return; } if (do_stash) { retval = krb5_db_store_master_key(util_context, global_params.stash_file, master_princ, new_mkey_kvno, &new_mkeyblock, mkey_password); if (retval) { com_err(progname, errno, _("while storing key")); printf(_("Warning: couldn't stash master key.\n")); } } cleanup_return: /* clean up */ (void) krb5_db_fini(util_context); zap((char *)master_keyblock.contents, master_keyblock.length); free(master_keyblock.contents); zap((char *)new_mkeyblock.contents, new_mkeyblock.length); free(new_mkeyblock.contents); if (pw_str) { zap(pw_str, pw_size); free(pw_str); } free(master_salt.data); krb5_free_unparsed_name(util_context, mkey_fullname); return; }
/*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); }