From c438b327af4cf5ba96ed3f7e02b6327b9d06c1ae Mon Sep 17 00:00:00 2001 From: Will Fiveash Date: Fri, 15 Aug 2008 00:38:41 +0000 Subject: a stash file is not a keytab Note, this is the commit for the associated Krb Consortium project: Projects/Masterkey Keytab Stash ticket: 194 git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@20661 dc483132-0cff-0310-8789-dd5450dbe970 --- src/lib/kadm5/admin.h | 3 +- src/lib/kadm5/alt_prof.c | 5 + src/lib/kadm5/srv/server_kdb.c | 3 +- src/lib/kadm5/unit-test/api.2/init-v2.exp | 2 +- src/lib/kdb/kdb5.c | 58 ++++-- src/lib/kdb/kdb5.h | 4 +- src/lib/kdb/kdb_default.c | 311 +++++++++++++++++++++--------- src/lib/krb5/keytab/kt_file.c | 2 - src/lib/krb5/keytab/kt_memory.c | 2 - src/lib/krb5/keytab/kt_srvtab.c | 2 - 10 files changed, 283 insertions(+), 109 deletions(-) (limited to 'src/lib') diff --git a/src/lib/kadm5/admin.h b/src/lib/kadm5/admin.h index 9013305..2817195 100644 --- a/src/lib/kadm5/admin.h +++ b/src/lib/kadm5/admin.h @@ -139,6 +139,7 @@ typedef long kadm5_ret_t; #define KADM5_CONFIG_POLL_TIME 0x04000000 #define KADM5_CONFIG_IPROP_LOGFILE 0x08000000 #define KADM5_CONFIG_IPROP_PORT 0x10000000 +#define KADM5_CONFIG_KVNO 0x20000000 /* * permission bits */ @@ -256,7 +257,7 @@ typedef struct _kadm5_config_params { krb5_flags flags; krb5_key_salt_tuple *keysalts; krb5_int32 num_keysalts; - + krb5_kvno kvno; bool_t iprop_enabled; uint32_t iprop_ulogsize; krb5_deltat iprop_poll_time; diff --git a/src/lib/kadm5/alt_prof.c b/src/lib/kadm5/alt_prof.c index c8bc6b0..cfcbd79 100644 --- a/src/lib/kadm5/alt_prof.c +++ b/src/lib/kadm5/alt_prof.c @@ -501,6 +501,11 @@ krb5_error_code kadm5_get_config_params(context, use_kdc_config, params.realm = lrealm; params.mask |= KADM5_CONFIG_REALM; } + + if (params_in->mask & KADM5_CONFIG_KVNO) { + params.kvno = params_in->kvno; + params.mask |= KADM5_CONFIG_KVNO; + } /* * XXX These defaults should to work on both client and * server. kadm5_get_config_params can be implemented as a diff --git a/src/lib/kadm5/srv/server_kdb.c b/src/lib/kadm5/srv/server_kdb.c index 700b53a..836cd00 100644 --- a/src/lib/kadm5/srv/server_kdb.c +++ b/src/lib/kadm5/srv/server_kdb.c @@ -54,6 +54,7 @@ krb5_error_code kdb_init_master(kadm5_server_handle_t handle, master_keyblock.enctype, from_kbd, FALSE /* only prompt once */, handle->params.stash_file, + NULL /* don't care about kvno */, NULL /* I'm not sure about this, but it's what the kdc does --marc */, &master_keyblock); @@ -61,7 +62,7 @@ krb5_error_code kdb_init_master(kadm5_server_handle_t handle, goto done; if ((ret = krb5_db_verify_master_key(handle->context, master_princ, - &master_keyblock))) { + IGNORE_VNO, &master_keyblock))) { krb5_db_fini(handle->context); return ret; } diff --git a/src/lib/kadm5/unit-test/api.2/init-v2.exp b/src/lib/kadm5/unit-test/api.2/init-v2.exp index 21f6260..24bfb09 100644 --- a/src/lib/kadm5/unit-test/api.2/init-v2.exp +++ b/src/lib/kadm5/unit-test/api.2/init-v2.exp @@ -147,7 +147,7 @@ proc test108 {} { [config_params {KADM5_CONFIG_MKEY_NAME} does/not/exist] \ $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ server_handle - } "KDB_NOMASTERKEY" + } "KRB5_KDB_CANTREAD_STORED" } if {! $RPC} test108 diff --git a/src/lib/kdb/kdb5.c b/src/lib/kdb/kdb5.c index f25864d..67ceacb 100644 --- a/src/lib/kdb/kdb5.c +++ b/src/lib/kdb/kdb5.c @@ -1390,8 +1390,9 @@ krb5_db_get_mkey(krb5_context kcontext, krb5_keyblock ** key) krb5_error_code krb5_db_store_master_key(krb5_context kcontext, - char *db_arg, + char *keyfile, krb5_principal mname, + krb5_kvno kvno, krb5_keyblock * key, char *master_pwd) { krb5_error_code status = 0; @@ -1411,8 +1412,9 @@ krb5_db_store_master_key(krb5_context kcontext, } status = dal_handle->lib_handle->vftabl.store_master_key(kcontext, - db_arg, + keyfile, mname, + kvno, key, master_pwd); get_errmsg(kcontext, status); kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE); @@ -1425,18 +1427,20 @@ char *krb5_mkey_pwd_prompt1 = KRB5_KDC_MKEY_1; char *krb5_mkey_pwd_prompt2 = KRB5_KDC_MKEY_2; 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_db_fetch_mkey(krb5_context context, + krb5_principal mname, + krb5_enctype etype, + krb5_boolean fromkeyboard, + krb5_boolean twice, + char * db_args, + krb5_kvno * kvno, + 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)); @@ -1460,6 +1464,30 @@ krb5_db_fetch_mkey(krb5_context context, retval = krb5_c_string_to_key(context, etype, &pwd, salt ? salt : &scratch, key); + /* + * If a kvno pointer was passed in and it dereferences the IGNORE_VNO + * value then it should be assigned the value of the kvno associated + * with the current mkey princ key if that princ entry is available + * otherwise assign 1 which is the default kvno value for the mkey + * princ. + */ + if (kvno != NULL && *kvno == IGNORE_VNO) { + int nentries = 1; + krb5_boolean more; + krb5_error_code rc; + krb5_db_entry master_entry; + + rc = krb5_db_get_principal(context, mname, + &master_entry, &nentries, &more); + + if (rc == 0 && nentries == 1 && more == FALSE) + *kvno = (krb5_kvno) master_entry.key_data->key_data_kvno; + else + *kvno = 1; + + if (rc == 0 && nentries) + krb5_db_free_principal(context, &master_entry, nentries); + } if (!salt) krb5_xfree(scratch.data); @@ -1485,7 +1513,7 @@ krb5_db_fetch_mkey(krb5_context context, retval = dal_handle->lib_handle->vftabl.fetch_master_key(context, mname, &tmp_key, - &kvno, + kvno, db_args); get_errmsg(context, retval); kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE); @@ -1515,8 +1543,10 @@ krb5_db_fetch_mkey(krb5_context context, } krb5_error_code -krb5_db_verify_master_key(krb5_context kcontext, - krb5_principal mprinc, krb5_keyblock * mkey) +krb5_db_verify_master_key(krb5_context kcontext, + krb5_principal mprinc, + krb5_kvno kvno, + krb5_keyblock * mkey) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; @@ -1535,7 +1565,9 @@ krb5_db_verify_master_key(krb5_context kcontext, } status = dal_handle->lib_handle->vftabl.verify_master_key(kcontext, - mprinc, mkey); + mprinc, + kvno, + mkey); get_errmsg(kcontext, status); kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE); diff --git a/src/lib/kdb/kdb5.h b/src/lib/kdb/kdb5.h index 328ce25..f9f4494 100644 --- a/src/lib/kdb/kdb5.h +++ b/src/lib/kdb/kdb5.h @@ -139,17 +139,19 @@ typedef struct _kdb_vftabl{ krb5_error_code (*store_master_key) (krb5_context kcontext, char *db_arg, krb5_principal mname, + krb5_kvno kvno, krb5_keyblock *key, char *master_pwd); krb5_error_code (*fetch_master_key) (krb5_context kcontext, krb5_principal mname, krb5_keyblock *key, - int *kvno, + krb5_kvno *kvno, char *db_args); krb5_error_code (*verify_master_key) (krb5_context kcontext, krb5_principal mprinc, + krb5_kvno kvno, krb5_keyblock *mkey); krb5_error_code (*dbe_search_enctype) (krb5_context kcontext, diff --git a/src/lib/kdb/kdb_default.c b/src/lib/kdb/kdb_default.c index 22d3af2..bf4ba35 100644 --- a/src/lib/kdb/kdb_default.c +++ b/src/lib/kdb/kdb_default.c @@ -133,102 +133,117 @@ krb5_dbe_def_search_enctype(kcontext, dbentp, start, ktype, stype, kvno, kdatap) #endif krb5_error_code -krb5_def_store_mkey(context, keyfile, mname, key, master_pwd) - krb5_context context; - char *keyfile; - krb5_principal mname; - krb5_keyblock *key; - char *master_pwd; +krb5_def_store_mkey(krb5_context context, + char *keyfile, + krb5_principal mname, + krb5_kvno kvno, + krb5_keyblock *key, + char *master_pwd) { - FILE *kf; krb5_error_code retval = 0; - krb5_ui_2 enctype; - krb5_ui_4 keylength; char defkeyfile[MAXPATHLEN+1]; + char *tmp_ktname = NULL, *tmp_ktpath; krb5_data *realm = krb5_princ_realm(context, mname); -#if HAVE_UMASK - mode_t oumask; -#endif + krb5_keytab kt; + krb5_keytab_entry new_entry; + struct stat stb; + int statrc; if (!keyfile) { - (void) strcpy(defkeyfile, DEFAULT_KEYFILE_STUB); - (void) strncat(defkeyfile, realm->data, - min(sizeof(defkeyfile)-sizeof(DEFAULT_KEYFILE_STUB)-1, - realm->length)); - defkeyfile[sizeof(defkeyfile) - 1] = '\0'; - keyfile = defkeyfile; + (void) strcpy(defkeyfile, DEFAULT_KEYFILE_STUB); + (void) strncat(defkeyfile, realm->data, + min(sizeof(defkeyfile)-sizeof(DEFAULT_KEYFILE_STUB)-1, + realm->length)); + defkeyfile[sizeof(defkeyfile) - 1] = '\0'; + keyfile = defkeyfile; } -#if HAVE_UMASK - oumask = umask(077); -#endif -#ifdef ANSI_STDIO - if (!(kf = fopen(keyfile, "wb"))) -#else - if (!(kf = fopen(keyfile, "w"))) -#endif - { - int e = errno; -#if HAVE_UMASK - (void) umask(oumask); -#endif - krb5_set_error_message (context, e, - "%s accessing file '%s'", - error_message (e), keyfile); - return e; + /* + * XXX making the assumption that the keyfile is in a dir that requires root + * privilege to write to thus making timing attacks unlikely. + */ + if ((statrc = stat(keyfile, &stb)) >= 0) { + /* if keyfile exists it better be a regular file */ + if (!S_ISREG(stb.st_mode)) { + retval = EINVAL; + krb5_set_error_message (context, retval, + "keyfile (%s) is not a regular file: %s", + keyfile, error_message(retval)); + goto out; + } } - set_cloexec_file(kf); -#if BIG_ENDIAN_MASTER_KEY - enctype = htons((uint16_t) key->enctype); - keylength = htonl((uint32_t) key->length); -#else - enctype = key->enctype; - keylength = key->length; -#endif - if ((fwrite((krb5_pointer) &enctype, - 2, 1, kf) != 1) || - (fwrite((krb5_pointer) &keylength, - sizeof(keylength), 1, kf) != 1) || - (fwrite((krb5_pointer) key->contents, - sizeof(key->contents[0]), (unsigned) key->length, - kf) != key->length)) { - retval = errno; - (void) fclose(kf); - } else if (fclose(kf) == EOF) - retval = errno; -#if HAVE_UMASK - (void) umask(oumask); -#endif + + /* Use temp keytab file name in case creation of keytab fails */ + + /* create temp file template for use by mktemp() */ + if ((retval = asprintf(&tmp_ktname, "WRFILE:%s_XXXXXX", keyfile)) < 0) { + krb5_set_error_message (context, retval, + "Could not create temp keytab file name."); + goto out; + } + + if (mktemp(tmp_ktname) == NULL) { + retval = errno; + krb5_set_error_message (context, retval, + "Could not create temp stash file: %s", + error_message(errno)); + goto out; + } + + /* create new stash keytab using temp file name */ + retval = krb5_kt_resolve(context, tmp_ktname, &kt); + if (retval != 0) + goto out; + + memset((char *) &new_entry, 0, sizeof(new_entry)); + new_entry.principal = mname; + new_entry.key = *key; + new_entry.vno = kvno; + + /* + * Set tmp_ktpath to point to the keyfile path (skip WRFILE:). Subtracting + * 1 to account for NULL terminator in sizeof calculation of a string + * constant. Used further down. + */ + tmp_ktpath = tmp_ktname + (sizeof("WRFILE:") - 1); + + retval = krb5_kt_add_entry(context, kt, &new_entry); + if (retval != 0) { + /* delete tmp keyfile if it exists and an error occurrs */ + if (stat(keyfile, &stb) >= 0) + (void) unlink(tmp_ktpath); + } else { + /* rename original keyfile to original filename */ + if (rename(tmp_ktpath, keyfile) < 0) { + retval = errno; + krb5_set_error_message (context, retval, + "rename of temporary keyfile (%s) to (%s) failed: %s", + tmp_ktpath, keyfile, error_message(errno)); + } + } + +out: + if (tmp_ktname != NULL) + free(tmp_ktname); + return retval; } - -krb5_error_code -krb5_db_def_fetch_mkey( krb5_context context, - krb5_principal mname, - krb5_keyblock *key, - int *kvno, - char *db_args) +static krb5_error_code +krb5_db_def_fetch_mkey_stash(krb5_context context, + const char *keyfile, + krb5_keyblock *key, + krb5_kvno *kvno) { - krb5_error_code retval; + krb5_error_code retval = 0; krb5_ui_2 enctype; krb5_ui_4 keylength; - char defkeyfile[MAXPATHLEN+1]; - krb5_data *realm = krb5_princ_realm(context, mname); FILE *kf = NULL; - retval = 0; - key->magic = KV5M_KEYBLOCK; - (void) strcpy(defkeyfile, DEFAULT_KEYFILE_STUB); - (void) strncat(defkeyfile, realm->data, - min(sizeof(defkeyfile)-sizeof(DEFAULT_KEYFILE_STUB)-1, - realm->length)); - defkeyfile[sizeof(defkeyfile) - 1] = '\0'; - #ifdef ANSI_STDIO - if (!(kf = fopen((db_args) ? db_args : defkeyfile, "rb"))) + if (!(kf = fopen(keyfile, "rb"))) #else - if (!(kf = fopen((db_args) ? db_args : defkeyfile, "r"))) + if (!(kf = fopen(keyfile, "r"))) #endif return KRB5_KDB_CANTREAD_STORED; set_cloexec_file(kf); @@ -260,7 +275,7 @@ krb5_db_def_fetch_mkey( krb5_context context, #else key->length = keylength; #endif - + if (!key->length || ((int) key->length) < 0) { retval = KRB5_KDB_BADSTORED_MKEY; goto errout; @@ -271,9 +286,8 @@ krb5_db_def_fetch_mkey( krb5_context context, goto errout; } - if (fread((krb5_pointer) key->contents, - sizeof(key->contents[0]), key->length, kf) - != key->length) { + if (fread((krb5_pointer) key->contents, sizeof(key->contents[0]), + key->length, kf) != key->length) { retval = KRB5_KDB_CANTREAD_STORED; memset(key->contents, 0, key->length); free(key->contents); @@ -281,20 +295,137 @@ krb5_db_def_fetch_mkey( krb5_context context, } else retval = 0; - *kvno = 0; + /* + * Note, the old stash format did not store the kvno and at this point it + * can be assumed to be 1 as is the case for the mkey princ. If the kvno is + * passed in and isn't ignore_vno just leave it alone as this could cause + * verifcation trouble if the mkey princ is using a kvno other than 1. + */ + if (kvno && *kvno == IGNORE_VNO) + *kvno = 1; errout: (void) fclose(kf); return retval; +} +static krb5_error_code +krb5_db_def_fetch_mkey_keytab(krb5_context context, + const char *keyfile, + krb5_principal mname, + krb5_keyblock *key, + krb5_kvno *kvno) +{ + krb5_error_code retval = 0; + krb5_keytab kt; + krb5_keytab_entry kt_ent; + krb5_enctype enctype = IGNORE_ENCTYPE; + + if ((retval = krb5_kt_resolve(context, keyfile, &kt)) != 0) + goto errout; + + /* override default */ + if (key->enctype != ENCTYPE_UNKNOWN) + enctype = key->enctype; + + if ((retval = krb5_kt_get_entry(context, kt, mname, + kvno ? *kvno : IGNORE_VNO, + enctype, + &kt_ent)) == 0) { + + if (key->enctype == ENCTYPE_UNKNOWN) + key->enctype = kt_ent.key.enctype; + + if (((int) kt_ent.key.length) < 0) { + retval = KRB5_KDB_BADSTORED_MKEY; + krb5_kt_free_entry(context, &kt_ent); + goto errout; + } + + key->length = kt_ent.key.length; + + /* + * If a kvno pointer was passed in and it dereferences the + * IGNORE_VNO value then it should be assigned the value of the kvno + * found in the keytab otherwise the KNVO specified should be the + * same as the one returned from the keytab. + */ + if (kvno != NULL && *kvno == IGNORE_VNO) + *kvno = kt_ent.vno; + + /* + * kt_ent will be free'd so need to allocate and copy key contents for + * output to caller. + */ + if (!(key->contents = (krb5_octet *)malloc(key->length))) { + retval = ENOMEM; + krb5_kt_free_entry(context, &kt_ent); + goto errout; + } + memcpy(key->contents, kt_ent.key.contents, kt_ent.key.length); + krb5_kt_free_entry(context, &kt_ent); + } + +errout: + return retval; } +krb5_error_code +krb5_db_def_fetch_mkey(krb5_context context, + krb5_principal mname, + krb5_keyblock *key, + krb5_kvno *kvno, + char *db_args) +{ + krb5_error_code retval_ofs = 0, retval_kt = 0; + char keyfile[MAXPATHLEN+1]; + krb5_data *realm = krb5_princ_realm(context, mname); + + key->magic = KV5M_KEYBLOCK; + + if (db_args != NULL) { + (void) strncpy(keyfile, db_args, sizeof(keyfile)); + } else { + (void) strcpy(keyfile, DEFAULT_KEYFILE_STUB); + (void) strncat(keyfile, realm->data, + min(sizeof(keyfile)-sizeof(DEFAULT_KEYFILE_STUB)-1, + realm->length)); + } + /* null terminate no matter what */ + keyfile[sizeof(keyfile) - 1] = '\0'; + + /* assume the master key is in a keytab */ + retval_kt = krb5_db_def_fetch_mkey_keytab(context, keyfile, mname, key, kvno); + if (retval_kt != 0) { + /* + * If it's not in a keytab, fall back and try getting the mkey from the + * older stash file format. + */ + retval_ofs = krb5_db_def_fetch_mkey_stash(context, keyfile, key, kvno); + } + + if (retval_kt != 0 && retval_ofs != 0) { + /* + * Error, not able to get mkey from either file format. Note, in order + * to try to return a more correct error, the logic below is assuming + * that if either of the stash reading functions returned + * KRB5_KDB_BADSTORED_MKEY then this is probably the real error. + */ + krb5_set_error_message (context, KRB5_KDB_CANTREAD_STORED, + "Can not fetch master key either from keytab (error: %s) or old " + "format (error %s).", error_message(retval_kt), + error_message(retval_ofs)); + return KRB5_KDB_CANTREAD_STORED; + } else { + return 0; + } +} krb5_error_code -krb5_def_verify_master_key(context, mprinc, mkey) - krb5_context context; - krb5_principal mprinc; - krb5_keyblock *mkey; +krb5_def_verify_master_key(krb5_context context, + krb5_principal mprinc, + krb5_kvno kvno, + krb5_keyblock *mkey) { krb5_error_code retval; krb5_db_entry master_entry; @@ -329,6 +460,14 @@ krb5_def_verify_master_key(context, mprinc, mkey) retval = KRB5_KDB_BADMASTERKEY; } + if (kvno != IGNORE_VNO && + kvno != (krb5_kvno) master_entry.key_data->key_data_kvno) { + retval = KRB5_KDB_BADMASTERKEY; + krb5_set_error_message (context, retval, + "User specified mkeyVNO (%u) does not match master key princ's KVNO (%u)", + kvno, master_entry.key_data->key_data_kvno); + } + memset((char *)tempkey.contents, 0, tempkey.length); krb5_xfree(tempkey.contents); krb5_db_free_principal(context, &master_entry, nprinc); diff --git a/src/lib/krb5/keytab/kt_file.c b/src/lib/krb5/keytab/kt_file.c index cc254dd..357bb12 100644 --- a/src/lib/krb5/keytab/kt_file.c +++ b/src/lib/krb5/keytab/kt_file.c @@ -37,8 +37,6 @@ /* * Constants */ -#define IGNORE_VNO 0 -#define IGNORE_ENCTYPE 0 #define KRB5_KT_VNO_1 0x0501 /* krb v5, keytab version 1 (DCE compat) */ #define KRB5_KT_VNO 0x0502 /* krb v5, keytab version 2 (standard) */ diff --git a/src/lib/krb5/keytab/kt_memory.c b/src/lib/krb5/keytab/kt_memory.c index db392e6..bf9634e 100644 --- a/src/lib/krb5/keytab/kt_memory.c +++ b/src/lib/krb5/keytab/kt_memory.c @@ -39,8 +39,6 @@ /* * Constants */ -#define IGNORE_VNO 0 -#define IGNORE_ENCTYPE 0 /* * Types diff --git a/src/lib/krb5/keytab/kt_srvtab.c b/src/lib/krb5/keytab/kt_srvtab.c index e3dd009..7754644 100644 --- a/src/lib/krb5/keytab/kt_srvtab.c +++ b/src/lib/krb5/keytab/kt_srvtab.c @@ -30,8 +30,6 @@ /* * Constants */ -#define IGNORE_VNO 0 -#define IGNORE_ENCTYPE 0 #define KRB5_KT_VNO_1 0x0501 /* krb v5, keytab version 1 (DCE compat) */ #define KRB5_KT_VNO 0x0502 /* krb v5, keytab version 2 (standard) */ -- cgit v1.1