krb5_error_code _kdc_find_etype(krb5_context context, const hdb_entry_ex *princ, krb5_enctype *etypes, unsigned len, Key **ret_key, krb5_enctype *ret_etype) { int i; krb5_error_code ret = KRB5KDC_ERR_ETYPE_NOSUPP; krb5_salt def_salt; krb5_get_pw_salt (context, princ->entry.principal, &def_salt); for(i = 0; ret != 0 && i < len ; i++) { Key *key = NULL; if (krb5_enctype_valid(context, etypes[i]) != 0 && !_kdc_is_weak_exception(princ->entry.principal, etypes[i])) continue; while (hdb_next_enctype2key(context, &princ->entry, etypes[i], &key) == 0) { if (key->key.keyvalue.length == 0) { ret = KRB5KDC_ERR_NULL_KEY; continue; } *ret_key = key; *ret_etype = etypes[i]; ret = 0; if (is_default_salt_p(&def_salt, key)) { krb5_free_salt (context, def_salt); return ret; } } } krb5_free_salt (context, def_salt); return ret; }
static krb5_error_code add_padata(krb5_context context, METHOD_DATA *md, krb5_principal client, krb5_key_proc key_proc, krb5_const_pointer keyseed, krb5_enctype *enctypes, unsigned netypes, krb5_salt *salt) { krb5_error_code ret; PA_DATA *pa2; krb5_salt salt2; krb5_enctype *ep; size_t i; if(salt == NULL) { /* default to standard salt */ ret = krb5_get_pw_salt (context, client, &salt2); if (ret) return ret; salt = &salt2; } if (!enctypes) { enctypes = context->etypes; netypes = 0; for (ep = enctypes; *ep != (krb5_enctype)ETYPE_NULL; ep++) netypes++; } pa2 = realloc (md->val, (md->len + netypes) * sizeof(*md->val)); if (pa2 == NULL) { krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); return ENOMEM; } md->val = pa2; for (i = 0; i < netypes; ++i) { krb5_keyblock *key; ret = (*key_proc)(context, enctypes[i], *salt, keyseed, &key); if (ret) continue; ret = make_pa_enc_timestamp (context, &md->val[md->len], enctypes[i], key); krb5_free_keyblock (context, key); if (ret) return ret; ++md->len; } if(salt == &salt2) krb5_free_salt(context, salt2); return 0; }
static struct pa_info_data * pa_etype_info(krb5_context context, const krb5_principal client, const AS_REQ *asreq, struct pa_info_data *paid, heim_octet_string *data) { krb5_error_code ret; ETYPE_INFO e; size_t sz; int i, j; memset(&e, 0, sizeof(e)); ret = decode_ETYPE_INFO(data->data, data->length, &e, &sz); if (ret) goto out; if (e.len == 0) goto out; for (j = 0; j < asreq->req_body.etype.len; j++) { for (i = 0; i < e.len; i++) { if (asreq->req_body.etype.val[j] == e.val[i].etype) { krb5_salt salt; salt.salttype = KRB5_PW_SALT; if (e.val[i].salt == NULL) ret = krb5_get_pw_salt(context, client, &salt); else { salt.saltvalue = *e.val[i].salt; ret = 0; } if (e.val[i].salttype) salt.salttype = *e.val[i].salttype; if (ret == 0) { ret = set_paid(paid, context, e.val[i].etype, salt.salttype, salt.saltvalue.data, salt.saltvalue.length, NULL); if (e.val[i].salt == NULL) krb5_free_salt(context, salt); } if (ret == 0) { free_ETYPE_INFO(&e); return paid; } } } } out: free_ETYPE_INFO(&e); return NULL; }
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_string_to_key_data (krb5_context context, krb5_enctype enctype, krb5_data password, krb5_principal principal, krb5_keyblock *key) { krb5_error_code ret; krb5_salt salt; ret = krb5_get_pw_salt(context, principal, &salt); if(ret) return ret; ret = krb5_string_to_key_data_salt(context, enctype, password, salt, key); krb5_free_salt(context, salt); return ret; }
static krb5_error_code add_enc_ts_padata(krb5_context context, METHOD_DATA *md, krb5_principal client, krb5_s2k_proc keyproc, krb5_const_pointer keyseed, krb5_enctype *enctypes, unsigned netypes, krb5_salt *salt, krb5_data *s2kparams) { krb5_error_code ret; krb5_salt salt2; krb5_enctype *ep; int i; if(salt == NULL) { /* default to standard salt */ ret = krb5_get_pw_salt (context, client, &salt2); if (ret) return ret; salt = &salt2; } if (!enctypes) { enctypes = context->etypes; netypes = 0; for (ep = enctypes; *ep != ETYPE_NULL; ep++) netypes++; } for (i = 0; i < netypes; ++i) { krb5_keyblock *key; ret = (*keyproc)(context, enctypes[i], keyseed, *salt, s2kparams, &key); if (ret) continue; ret = make_pa_enc_timestamp (context, md, enctypes[i], key); krb5_free_keyblock (context, key); if (ret) return ret; } if(salt == &salt2) krb5_free_salt(context, salt2); return 0; }
int create_kerberos_key_from_string_direct(krb5_context context, krb5_principal host_princ, krb5_data *password, krb5_keyblock *key, krb5_enctype enctype) { int ret; krb5_salt salt; ret = krb5_get_pw_salt(context, host_princ, &salt); if (ret) { DEBUG(1,("krb5_get_pw_salt failed (%s)\n", error_message(ret))); return ret; } ret = krb5_string_to_key_salt(context, enctype, (const char *)password->data, salt, key); krb5_free_salt(context, salt); return ret; }
kadm5_ret_t kadm5_s_rename_principal(void *server_handle, krb5_principal source, krb5_principal target) { kadm5_server_context *context = server_handle; kadm5_ret_t ret; hdb_entry_ex ent; krb5_principal oldname; memset(&ent, 0, sizeof(ent)); if(krb5_principal_compare(context->context, source, target)) return KADM5_DUP; /* XXX is this right? */ ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0); if(ret) return ret; ret = context->db->hdb_fetch_kvno(context->context, context->db, source, HDB_F_GET_ANY|HDB_F_ADMIN_DATA, 0, &ent); if(ret){ context->db->hdb_close(context->context, context->db); goto out; } ret = _kadm5_set_modifier(context, &ent.entry); if(ret) goto out2; { /* fix salt */ size_t i; Salt salt; krb5_salt salt2; memset(&salt, 0, sizeof(salt)); krb5_get_pw_salt(context->context, source, &salt2); salt.type = hdb_pw_salt; salt.salt = salt2.saltvalue; for(i = 0; i < ent.entry.keys.len; i++){ if(ent.entry.keys.val[i].salt == NULL){ ent.entry.keys.val[i].salt = malloc(sizeof(*ent.entry.keys.val[i].salt)); if(ent.entry.keys.val[i].salt == NULL) return ENOMEM; ret = copy_Salt(&salt, ent.entry.keys.val[i].salt); if(ret) break; } } krb5_free_salt(context->context, salt2); } if(ret) goto out2; oldname = ent.entry.principal; ent.entry.principal = target; ret = hdb_seal_keys(context->context, context->db, &ent.entry); if (ret) { ent.entry.principal = oldname; goto out2; } kadm5_log_rename (context, source, &ent.entry); ret = context->db->hdb_store(context->context, context->db, 0, &ent); if(ret){ ent.entry.principal = oldname; goto out2; } ret = context->db->hdb_remove(context->context, context->db, oldname); ent.entry.principal = oldname; out2: context->db->hdb_close(context->context, context->db); hdb_free_entry(context->context, &ent); out: return _kadm5_error_code(ret); }
static krb5_error_code parse_key_set(krb5_context context, const char *key, krb5_enctype **ret_enctypes, size_t *ret_num_enctypes, krb5_salt *salt, krb5_principal principal) { const char *p; char buf[3][256]; int num_buf = 0; int i, num_enctypes = 0; krb5_enctype e; const krb5_enctype *enctypes = NULL; krb5_error_code ret; p = key; *ret_enctypes = NULL; *ret_num_enctypes = 0; /* split p in a list of :-separated strings */ for(num_buf = 0; num_buf < 3; num_buf++) if(strsep_copy(&p, ":", buf[num_buf], sizeof(buf[num_buf])) == -1) break; salt->saltvalue.data = NULL; salt->saltvalue.length = 0; for(i = 0; i < num_buf; i++) { if(enctypes == NULL && num_buf > 1) { /* this might be a etype specifier */ /* XXX there should be a string_to_etypes handling special cases like `des' and `all' */ if(strcmp(buf[i], "des") == 0) { enctypes = des_etypes; num_enctypes = sizeof(des_etypes)/sizeof(des_etypes[0]); } else if(strcmp(buf[i], "des3") == 0) { e = ETYPE_DES3_CBC_SHA1; enctypes = &e; num_enctypes = 1; } else { ret = krb5_string_to_enctype(context, buf[i], &e); if (ret == 0) { enctypes = &e; num_enctypes = 1; } else return ret; } continue; } if(salt->salttype == 0) { /* interpret string as a salt specifier, if no etype is set, this sets default values */ /* XXX should perhaps use string_to_salttype, but that interface sucks */ if(strcmp(buf[i], "pw-salt") == 0) { if(enctypes == NULL) { enctypes = all_etypes; num_enctypes = sizeof(all_etypes)/sizeof(all_etypes[0]); } salt->salttype = KRB5_PW_SALT; } else if(strcmp(buf[i], "afs3-salt") == 0) { if(enctypes == NULL) { enctypes = des_etypes; num_enctypes = sizeof(des_etypes)/sizeof(des_etypes[0]); } salt->salttype = KRB5_AFS3_SALT; } continue; } { /* if there is a final string, use it as the string to salt with, this is mostly useful with null salt for v4 compat, and a cell name for afs compat */ salt->saltvalue.data = strdup(buf[i]); if (salt->saltvalue.data == NULL) { krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); return ENOMEM; } salt->saltvalue.length = strlen(buf[i]); } } if(enctypes == NULL || salt->salttype == 0) { krb5_set_error_message(context, EINVAL, "bad value for default_keys `%s'", key); return EINVAL; } /* if no salt was specified make up default salt */ if(salt->saltvalue.data == NULL) { if(salt->salttype == KRB5_PW_SALT) ret = krb5_get_pw_salt(context, principal, salt); else if(salt->salttype == KRB5_AFS3_SALT) { krb5_const_realm realm = krb5_principal_get_realm(context, principal); salt->saltvalue.data = strdup(realm); if(salt->saltvalue.data == NULL) { krb5_set_error_message(context, ENOMEM, "out of memory while " "parsing salt specifiers"); return ENOMEM; } strlwr(salt->saltvalue.data); salt->saltvalue.length = strlen(realm); } } *ret_enctypes = malloc(sizeof(enctypes[0]) * num_enctypes); if (*ret_enctypes == NULL) { krb5_free_salt(context, *salt); krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); return ENOMEM; } memcpy(*ret_enctypes, enctypes, sizeof(enctypes[0]) * num_enctypes); *ret_num_enctypes = num_enctypes; return 0; }
int main(int argc, char **argv) { krb5_context context; krb5_principal princ; krb5_salt salt; int optidx; char buf[1024]; krb5_enctype etype; krb5_error_code ret; optidx = krb5_program_setup(&context, argc, argv, args, num_args, NULL); if(help) usage(0); if(version){ print_version (NULL); return 0; } argc -= optidx; argv += optidx; if (argc > 1) usage(1); if(!version5 && !version4 && !afs) version5 = 1; ret = krb5_string_to_enctype(context, keytype_str, &etype); if(ret) krb5_err(context, 1, ret, "krb5_string_to_enctype"); if((etype != (krb5_enctype)ETYPE_DES_CBC_CRC && etype != (krb5_enctype)ETYPE_DES_CBC_MD4 && etype != (krb5_enctype)ETYPE_DES_CBC_MD5) && (afs || version4)) { if(!version5) { etype = ETYPE_DES_CBC_CRC; } else { krb5_errx(context, 1, "DES is the only valid keytype for AFS and Kerberos 4"); } } if(version5 && principal == NULL){ printf("Kerberos v5 principal: "); if(fgets(buf, sizeof(buf), stdin) == NULL) return 1; buf[strcspn(buf, "\r\n")] = '\0'; principal = estrdup(buf); } if(afs && cell == NULL){ printf("AFS cell: "); if(fgets(buf, sizeof(buf), stdin) == NULL) return 1; buf[strcspn(buf, "\r\n")] = '\0'; cell = estrdup(buf); } if(argv[0]) password = argv[0]; if(password == NULL){ if(UI_UTIL_read_pw_string(buf, sizeof(buf), "Password: "******"Kerberos 5 (%s)"); krb5_free_salt(context, salt); } if(version4){ salt.salttype = KRB5_PW_SALT; salt.saltvalue.length = 0; salt.saltvalue.data = NULL; tokey(context, ETYPE_DES_CBC_MD5, password, salt, "Kerberos 4"); } if(afs){ salt.salttype = KRB5_AFS3_SALT; salt.saltvalue.length = strlen(cell); salt.saltvalue.data = cell; tokey(context, ETYPE_DES_CBC_MD5, password, salt, "AFS"); } return 0; }
kadm5_ret_t kadm5_s_rename_principal(void *server_handle, krb5_principal source, krb5_principal target) { kadm5_server_context *context = server_handle; kadm5_ret_t ret; hdb_entry_ex ent; krb5_principal oldname; memset(&ent, 0, sizeof(ent)); if (krb5_principal_compare(context->context, source, target)) return KADM5_DUP; /* XXX is this right? */ if (!context->keep_open) { ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0); if(ret) return ret; } ret = kadm5_log_init(context); if (ret) goto out; ret = context->db->hdb_fetch_kvno(context->context, context->db, source, HDB_F_GET_ANY|HDB_F_ADMIN_DATA, 0, &ent); if (ret) goto out2; oldname = ent.entry.principal; ret = _kadm5_set_modifier(context, &ent.entry); if (ret) goto out3; { /* fix salt */ size_t i; Salt salt; krb5_salt salt2; memset(&salt, 0, sizeof(salt)); krb5_get_pw_salt(context->context, source, &salt2); salt.type = hdb_pw_salt; salt.salt = salt2.saltvalue; for(i = 0; i < ent.entry.keys.len; i++){ if(ent.entry.keys.val[i].salt == NULL){ ent.entry.keys.val[i].salt = malloc(sizeof(*ent.entry.keys.val[i].salt)); if (ent.entry.keys.val[i].salt == NULL) ret = ENOMEM; else ret = copy_Salt(&salt, ent.entry.keys.val[i].salt); if (ret) break; } } krb5_free_salt(context->context, salt2); } if (ret) goto out3; /* Borrow target */ ent.entry.principal = target; ret = hdb_seal_keys(context->context, context->db, &ent.entry); if (ret) goto out3; /* This logs the change for iprop and writes to the HDB */ ret = kadm5_log_rename(context, source, &ent.entry); out3: ent.entry.principal = oldname; /* Unborrow target */ hdb_free_entry(context->context, &ent); out2: (void) kadm5_log_end(context); out: if (!context->keep_open) { kadm5_ret_t ret2; ret2 = context->db->hdb_close(context->context, context->db); if (ret == 0 && ret2 != 0) ret = ret2; } return _kadm5_error_code(ret); }
static void format_field(kadm5_principal_ent_t princ, unsigned int field, unsigned int subfield, char *buf, size_t buf_len, int condensed) { switch(field) { case KADM5_PRINCIPAL: if(condensed) krb5_unparse_name_fixed_short(context, princ->principal, buf, buf_len); else krb5_unparse_name_fixed(context, princ->principal, buf, buf_len); break; case KADM5_PRINC_EXPIRE_TIME: time_t2str(princ->princ_expire_time, buf, buf_len, !condensed); break; case KADM5_PW_EXPIRATION: time_t2str(princ->pw_expiration, buf, buf_len, !condensed); break; case KADM5_LAST_PWD_CHANGE: time_t2str(princ->last_pwd_change, buf, buf_len, !condensed); break; case KADM5_MAX_LIFE: deltat2str(princ->max_life, buf, buf_len); break; case KADM5_MAX_RLIFE: deltat2str(princ->max_renewable_life, buf, buf_len); break; case KADM5_MOD_TIME: time_t2str(princ->mod_date, buf, buf_len, !condensed); break; case KADM5_MOD_NAME: if (princ->mod_name == NULL) strlcpy(buf, "unknown", buf_len); else if(condensed) krb5_unparse_name_fixed_short(context, princ->mod_name, buf, buf_len); else krb5_unparse_name_fixed(context, princ->mod_name, buf, buf_len); break; case KADM5_ATTRIBUTES: attributes2str (princ->attributes, buf, buf_len); break; case KADM5_KVNO: snprintf(buf, buf_len, "%d", princ->kvno); break; case KADM5_MKVNO: /* XXX libkadm5srv decrypts the keys, so mkvno is always 0. */ strlcpy(buf, "unknown", buf_len); break; case KADM5_LAST_SUCCESS: time_t2str(princ->last_success, buf, buf_len, !condensed); break; case KADM5_LAST_FAILED: time_t2str(princ->last_failed, buf, buf_len, !condensed); break; case KADM5_FAIL_AUTH_COUNT: snprintf(buf, buf_len, "%d", princ->fail_auth_count); break; case KADM5_POLICY: if(princ->policy != NULL) strlcpy(buf, princ->policy, buf_len); else strlcpy(buf, "none", buf_len); break; case KADM5_KEY_DATA:{ krb5_salt def_salt; int i; char buf2[1024]; krb5_get_pw_salt (context, princ->principal, &def_salt); *buf = '\0'; for (i = 0; i < princ->n_key_data; ++i) { format_keytype(&princ->key_data[i], &def_salt, buf2, sizeof(buf2)); if(i > 0) strlcat(buf, ", ", buf_len); strlcat(buf, buf2, buf_len); } krb5_free_salt (context, def_salt); break; } case KADM5_TL_DATA: { krb5_tl_data *tl; for (tl = princ->tl_data; tl != NULL; tl = tl->tl_data_next) if ((unsigned)tl->tl_data_type == subfield) break; if (tl == NULL) { strlcpy(buf, "", buf_len); break; } switch (subfield) { case KRB5_TL_PASSWORD: snprintf(buf, buf_len, "\"%.*s\"", (int)tl->tl_data_length, (const char *)tl->tl_data_contents); break; case KRB5_TL_PKINIT_ACL: { HDB_Ext_PKINIT_acl acl; size_t size; int ret; size_t i; ret = decode_HDB_Ext_PKINIT_acl(tl->tl_data_contents, tl->tl_data_length, &acl, &size); if (ret) { snprintf(buf, buf_len, "failed to decode ACL"); break; } buf[0] = '\0'; for (i = 0; i < acl.len; i++) { strlcat(buf, "subject: ", buf_len); strlcat(buf, acl.val[i].subject, buf_len); if (acl.val[i].issuer) { strlcat(buf, " issuer:", buf_len); strlcat(buf, *acl.val[i].issuer, buf_len); } if (acl.val[i].anchor) { strlcat(buf, " anchor:", buf_len); strlcat(buf, *acl.val[i].anchor, buf_len); } if (i + 1 < acl.len) strlcat(buf, ", ", buf_len); } free_HDB_Ext_PKINIT_acl(&acl); break; } case KRB5_TL_ALIASES: { HDB_Ext_Aliases alias; size_t size; int ret; size_t i; ret = decode_HDB_Ext_Aliases(tl->tl_data_contents, tl->tl_data_length, &alias, &size); if (ret) { snprintf(buf, buf_len, "failed to decode alias"); break; } buf[0] = '\0'; for (i = 0; i < alias.aliases.len; i++) { char *p; ret = krb5_unparse_name(context, &alias.aliases.val[i], &p); if (ret) break; if (i > 0) strlcat(buf, " ", buf_len); strlcat(buf, p, buf_len); free(p); } free_HDB_Ext_Aliases(&alias); break; } default: snprintf(buf, buf_len, "unknown type %d", subfield); break; } break; } default: strlcpy(buf, "<unknown>", buf_len); break; } }
static ssize_t append_mit_key(krb5_context context, krb5_storage *sp, krb5_const_principal princ, unsigned int kvno, Key *key) { krb5_error_code ret; krb5_salt k5salt; ssize_t sz; size_t key_versions = key->salt ? 2 : 1; size_t decrypted_key_length; char buf[2]; krb5_data keylenbytes; unsigned int salttype; sz = append_string(context, sp, "\t%u\t%u\t%d\t%d\t", key_versions, kvno, key->key.keytype, key->key.keyvalue.length + 2); if (sz == -1) return sz; ret = krb5_enctype_keysize(context, key->key.keytype, &decrypted_key_length); if (ret) return -1; /* XXX we lose the error code */ buf[0] = decrypted_key_length & 0xff; buf[1] = (decrypted_key_length & 0xff00) >> 8; keylenbytes.data = buf; keylenbytes.length = sizeof (buf); sz = append_hex(context, sp, 1, 1, &keylenbytes); if (sz == -1) return sz; sz = append_hex(context, sp, 1, 1, &key->key.keyvalue); if (!key->salt) return sz; /* Map salt to MIT KDB style */ switch (key->salt->type) { case KRB5_PADATA_PW_SALT: /* * Compute normal salt and then see whether it matches the stored one */ ret = krb5_get_pw_salt(context, princ, &k5salt); if (ret) return -1; if (k5salt.saltvalue.length == key->salt->salt.length && memcmp(k5salt.saltvalue.data, key->salt->salt.data, k5salt.saltvalue.length) == 0) salttype = KRB5_KDB_SALTTYPE_NORMAL; /* matches */ else if (key->salt->salt.length == strlen(princ->realm) && memcmp(key->salt->salt.data, princ->realm, key->salt->salt.length) == 0) salttype = KRB5_KDB_SALTTYPE_ONLYREALM; /* matches realm */ else if (key->salt->salt.length == k5salt.saltvalue.length - strlen(princ->realm) && memcmp((char *)k5salt.saltvalue.data + strlen(princ->realm), key->salt->salt.data, key->salt->salt.length) == 0) salttype = KRB5_KDB_SALTTYPE_NOREALM; /* matches w/o realm */ else salttype = KRB5_KDB_SALTTYPE_NORMAL; /* hope for best */ break; case KRB5_PADATA_AFS3_SALT: salttype = KRB5_KDB_SALTTYPE_AFS3; break; default: return -1; } sz = append_string(context, sp, "\t%u\t%u\t", salttype, key->salt->salt.length); if (sz == -1) return sz; return append_hex(context, sp, 1, 1, &key->salt->salt); }
static krb5_error_code process_pa_data_to_key(krb5_context context, krb5_get_init_creds_ctx *ctx, krb5_creds *creds, AS_REQ *a, AS_REP *rep, const krb5_krbhst_info *hi, krb5_keyblock **key) { struct pa_info_data paid, *ppaid = NULL; krb5_error_code ret; krb5_enctype etype; PA_DATA *pa; memset(&paid, 0, sizeof(paid)); etype = rep->enc_part.etype; if (rep->padata) { paid.etype = etype; ppaid = process_pa_info(context, creds->client, a, &paid, rep->padata); } if (ppaid == NULL) { ret = krb5_get_pw_salt (context, creds->client, &paid.salt); if (ret) return ret; paid.etype = etype; paid.s2kparams = NULL; } pa = NULL; if (rep->padata) { int idx = 0; pa = krb5_find_padata(rep->padata->val, rep->padata->len, KRB5_PADATA_PK_AS_REP, &idx); if (pa == NULL) { idx = 0; pa = krb5_find_padata(rep->padata->val, rep->padata->len, KRB5_PADATA_PK_AS_REP_19, &idx); } } if (pa && ctx->pk_init_ctx) { #ifdef PKINIT ret = _krb5_pk_rd_pa_reply(context, a->req_body.realm, ctx->pk_init_ctx, etype, hi, ctx->pk_nonce, &ctx->req_buffer, pa, key); #else ret = EINVAL; krb5_set_error_message(context, ret, N_("no support for PKINIT compiled in", "")); #endif } else if (ctx->keyseed) ret = pa_data_to_key_plain(context, creds->client, ctx, paid.salt, paid.s2kparams, etype, key); else { ret = EINVAL; krb5_set_error_message(context, ret, N_("No usable pa data type", "")); } free_paid(context, &paid); return ret; }
krb5_error_code _kdc_find_etype(krb5_context context, krb5_boolean use_strongest_session_key, krb5_boolean is_preauth, hdb_entry_ex *princ, krb5_enctype *etypes, unsigned len, krb5_enctype *ret_enctype, Key **ret_key) { krb5_error_code ret; krb5_salt def_salt; krb5_enctype enctype = ETYPE_NULL; Key *key; int i; /* We'll want to avoid keys with v4 salted keys in the pre-auth case... */ ret = krb5_get_pw_salt(context, princ->entry.principal, &def_salt); if (ret) return ret; ret = KRB5KDC_ERR_ETYPE_NOSUPP; if (use_strongest_session_key) { const krb5_enctype *p; krb5_enctype clientbest = ETYPE_NULL; int j; /* * Pick the strongest key that the KDC, target service, and * client all support, using the local cryptosystem enctype * list in strongest-to-weakest order to drive the search. * * This is not what RFC4120 says to do, but it encourages * adoption of stronger enctypes. This doesn't play well with * clients that have multiple Kerberos client implementations * available with different supported enctype lists. */ /* drive the search with local supported enctypes list */ p = krb5_kerberos_enctypes(context); for (i = 0; p[i] != ETYPE_NULL && enctype == ETYPE_NULL; i++) { if (krb5_enctype_valid(context, p[i]) != 0) continue; /* check that the client supports it too */ for (j = 0; j < len && enctype == ETYPE_NULL; j++) { if (p[i] != etypes[j]) continue; /* save best of union of { client, crypto system } */ if (clientbest == ETYPE_NULL) clientbest = p[i]; /* check target princ support */ ret = hdb_enctype2key(context, &princ->entry, p[i], &key); if (ret) continue; if (is_preauth && !is_default_salt_p(&def_salt, key)) continue; enctype = p[i]; } } if (clientbest != ETYPE_NULL && enctype == ETYPE_NULL) enctype = clientbest; else if (enctype == ETYPE_NULL) ret = KRB5KDC_ERR_ETYPE_NOSUPP; if (ret == 0 && ret_enctype != NULL) *ret_enctype = enctype; if (ret == 0 && ret_key != NULL) *ret_key = key; } else { /* * Pick the first key from the client's enctype list that is * supported by the cryptosystem and by the given principal. * * RFC4120 says we SHOULD pick the first _strong_ key from the * client's list... not the first key... If the admin disallows * weak enctypes in krb5.conf and selects this key selection * algorithm, then we get exactly what RFC4120 says. */ for(key = NULL, i = 0; ret != 0 && i < len; i++, key = NULL) { if (krb5_enctype_valid(context, etypes[i]) != 0 && !_kdc_is_weak_exception(princ->entry.principal, etypes[i])) continue; while (hdb_next_enctype2key(context, &princ->entry, etypes[i], &key) == 0) { if (key->key.keyvalue.length == 0) { ret = KRB5KDC_ERR_NULL_KEY; continue; } if (ret_key != NULL) *ret_key = key; if (ret_enctype != NULL) *ret_enctype = etypes[i]; ret = 0; if (is_preauth && is_default_salt_p(&def_salt, key)) goto out; } } } out: krb5_free_salt (context, def_salt); return ret; }
kadm5_ret_t kadm5_s_get_principal(void *server_handle, krb5_principal princ, kadm5_principal_ent_t out, uint32_t mask) { kadm5_server_context *context = server_handle; kadm5_ret_t ret; hdb_entry_ex ent; memset(&ent, 0, sizeof(ent)); ret = context->db->hdb_open(context->context, context->db, O_RDONLY, 0); if(ret) return ret; ret = context->db->hdb_fetch_kvno(context->context, context->db, princ, HDB_F_DECRYPT|HDB_F_GET_ANY|HDB_F_ADMIN_DATA, 0, &ent); context->db->hdb_close(context->context, context->db); if(ret) return _kadm5_error_code(ret); memset(out, 0, sizeof(*out)); if(mask & KADM5_PRINCIPAL) ret = krb5_copy_principal(context->context, ent.entry.principal, &out->principal); if(ret) goto out; if(mask & KADM5_PRINC_EXPIRE_TIME && ent.entry.valid_end) out->princ_expire_time = *ent.entry.valid_end; if(mask & KADM5_PW_EXPIRATION && ent.entry.pw_end) out->pw_expiration = *ent.entry.pw_end; if(mask & KADM5_LAST_PWD_CHANGE) hdb_entry_get_pw_change_time(&ent.entry, &out->last_pwd_change); if(mask & KADM5_ATTRIBUTES){ out->attributes |= ent.entry.flags.postdate ? 0 : KRB5_KDB_DISALLOW_POSTDATED; out->attributes |= ent.entry.flags.forwardable ? 0 : KRB5_KDB_DISALLOW_FORWARDABLE; out->attributes |= ent.entry.flags.initial ? KRB5_KDB_DISALLOW_TGT_BASED : 0; out->attributes |= ent.entry.flags.renewable ? 0 : KRB5_KDB_DISALLOW_RENEWABLE; out->attributes |= ent.entry.flags.proxiable ? 0 : KRB5_KDB_DISALLOW_PROXIABLE; out->attributes |= ent.entry.flags.invalid ? KRB5_KDB_DISALLOW_ALL_TIX : 0; out->attributes |= ent.entry.flags.require_preauth ? KRB5_KDB_REQUIRES_PRE_AUTH : 0; out->attributes |= ent.entry.flags.server ? 0 : KRB5_KDB_DISALLOW_SVR; out->attributes |= ent.entry.flags.change_pw ? KRB5_KDB_PWCHANGE_SERVICE : 0; out->attributes |= ent.entry.flags.ok_as_delegate ? KRB5_KDB_OK_AS_DELEGATE : 0; out->attributes |= ent.entry.flags.trusted_for_delegation ? KRB5_KDB_TRUSTED_FOR_DELEGATION : 0; out->attributes |= ent.entry.flags.allow_kerberos4 ? KRB5_KDB_ALLOW_KERBEROS4 : 0; out->attributes |= ent.entry.flags.allow_digest ? KRB5_KDB_ALLOW_DIGEST : 0; } if(mask & KADM5_MAX_LIFE) { if(ent.entry.max_life) out->max_life = *ent.entry.max_life; else out->max_life = INT_MAX; } if(mask & KADM5_MOD_TIME) { if(ent.entry.modified_by) out->mod_date = ent.entry.modified_by->time; else out->mod_date = ent.entry.created_by.time; } if(mask & KADM5_MOD_NAME) { if(ent.entry.modified_by) { if (ent.entry.modified_by->principal != NULL) ret = krb5_copy_principal(context->context, ent.entry.modified_by->principal, &out->mod_name); } else if(ent.entry.created_by.principal != NULL) ret = krb5_copy_principal(context->context, ent.entry.created_by.principal, &out->mod_name); else out->mod_name = NULL; } if(ret) goto out; if(mask & KADM5_KVNO) out->kvno = ent.entry.kvno; if(mask & KADM5_MKVNO) { size_t n; out->mkvno = 0; /* XXX */ for(n = 0; n < ent.entry.keys.len; n++) if(ent.entry.keys.val[n].mkvno) { out->mkvno = *ent.entry.keys.val[n].mkvno; /* XXX this isn't right */ break; } } #if 0 /* XXX implement */ if(mask & KADM5_AUX_ATTRIBUTES) ; if(mask & KADM5_LAST_SUCCESS) ; if(mask & KADM5_LAST_FAILED) ; if(mask & KADM5_FAIL_AUTH_COUNT) ; #endif if(mask & KADM5_POLICY) out->policy = NULL; if(mask & KADM5_MAX_RLIFE) { if(ent.entry.max_renew) out->max_renewable_life = *ent.entry.max_renew; else out->max_renewable_life = INT_MAX; } if(mask & KADM5_KEY_DATA){ size_t i; Key *key; krb5_key_data *kd; krb5_salt salt; krb5_data *sp; krb5_get_pw_salt(context->context, ent.entry.principal, &salt); out->key_data = malloc(ent.entry.keys.len * sizeof(*out->key_data)); if (out->key_data == NULL && ent.entry.keys.len != 0) { ret = ENOMEM; goto out; } for(i = 0; i < ent.entry.keys.len; i++){ key = &ent.entry.keys.val[i]; kd = &out->key_data[i]; kd->key_data_ver = 2; kd->key_data_kvno = ent.entry.kvno; kd->key_data_type[0] = key->key.keytype; if(key->salt) kd->key_data_type[1] = key->salt->type; else kd->key_data_type[1] = KRB5_PADATA_PW_SALT; /* setup key */ kd->key_data_length[0] = key->key.keyvalue.length; kd->key_data_contents[0] = malloc(kd->key_data_length[0]); if(kd->key_data_contents[0] == NULL && kd->key_data_length[0] != 0){ ret = ENOMEM; break; } memcpy(kd->key_data_contents[0], key->key.keyvalue.data, kd->key_data_length[0]); /* setup salt */ if(key->salt) sp = &key->salt->salt; else sp = &salt.saltvalue; kd->key_data_length[1] = sp->length; kd->key_data_contents[1] = malloc(kd->key_data_length[1]); if(kd->key_data_length[1] != 0 && kd->key_data_contents[1] == NULL) { memset(kd->key_data_contents[0], 0, kd->key_data_length[0]); ret = ENOMEM; break; } memcpy(kd->key_data_contents[1], sp->data, kd->key_data_length[1]); out->n_key_data = i + 1; } krb5_free_salt(context->context, salt); } if(ret){ kadm5_free_principal_ent(context, out); goto out; } if(mask & KADM5_TL_DATA) { time_t last_pw_expire; const HDB_Ext_PKINIT_acl *acl; const HDB_Ext_Aliases *aliases; ret = hdb_entry_get_pw_change_time(&ent.entry, &last_pw_expire); if (ret == 0 && last_pw_expire) { unsigned char buf[4]; _krb5_put_int(buf, last_pw_expire, sizeof(buf)); ret = add_tl_data(out, KRB5_TL_LAST_PWD_CHANGE, buf, sizeof(buf)); } if(ret){ kadm5_free_principal_ent(context, out); goto out; } /* * If the client was allowed to get key data, let it have the * password too. */ if(mask & KADM5_KEY_DATA) { heim_utf8_string pw; ret = hdb_entry_get_password(context->context, context->db, &ent.entry, &pw); if (ret == 0) { ret = add_tl_data(out, KRB5_TL_PASSWORD, pw, strlen(pw) + 1); free(pw); } krb5_clear_error_message(context->context); } ret = hdb_entry_get_pkinit_acl(&ent.entry, &acl); if (ret == 0 && acl) { krb5_data buf; size_t len; ASN1_MALLOC_ENCODE(HDB_Ext_PKINIT_acl, buf.data, buf.length, acl, &len, ret); if (ret) { kadm5_free_principal_ent(context, out); goto out; } if (len != buf.length) krb5_abortx(context->context, "internal ASN.1 encoder error"); ret = add_tl_data(out, KRB5_TL_PKINIT_ACL, buf.data, buf.length); free(buf.data); if (ret) { kadm5_free_principal_ent(context, out); goto out; } } if(ret){ kadm5_free_principal_ent(context, out); goto out; } ret = hdb_entry_get_aliases(&ent.entry, &aliases); if (ret == 0 && aliases) { krb5_data buf; size_t len; ASN1_MALLOC_ENCODE(HDB_Ext_Aliases, buf.data, buf.length, aliases, &len, ret); if (ret) { kadm5_free_principal_ent(context, out); goto out; } if (len != buf.length) krb5_abortx(context->context, "internal ASN.1 encoder error"); ret = add_tl_data(out, KRB5_TL_ALIASES, buf.data, buf.length); free(buf.data); if (ret) { kadm5_free_principal_ent(context, out); goto out; } } if(ret){ kadm5_free_principal_ent(context, out); goto out; } } out: hdb_free_entry(context->context, &ent); return _kadm5_error_code(ret); }
kadm5_ret_t kadm5_s_get_principal(void *server_handle, krb5_principal princ, kadm5_principal_ent_t out, uint32_t mask) { kadm5_server_context *context = server_handle; kadm5_ret_t ret; hdb_entry_ex ent; memset(&ent, 0, sizeof(ent)); if (!context->keep_open) { ret = context->db->hdb_open(context->context, context->db, O_RDONLY, 0); if(ret) return ret; } ret = context->db->hdb_fetch_kvno(context->context, context->db, princ, HDB_F_DECRYPT|HDB_F_ALL_KVNOS| HDB_F_GET_ANY|HDB_F_ADMIN_DATA, 0, &ent); if (!context->keep_open) context->db->hdb_close(context->context, context->db); if(ret) return _kadm5_error_code(ret); memset(out, 0, sizeof(*out)); if(mask & KADM5_PRINCIPAL) ret = krb5_copy_principal(context->context, ent.entry.principal, &out->principal); if(ret) goto out; if(mask & KADM5_PRINC_EXPIRE_TIME && ent.entry.valid_end) out->princ_expire_time = *ent.entry.valid_end; if(mask & KADM5_PW_EXPIRATION && ent.entry.pw_end) out->pw_expiration = *ent.entry.pw_end; if(mask & KADM5_LAST_PWD_CHANGE) hdb_entry_get_pw_change_time(&ent.entry, &out->last_pwd_change); if(mask & KADM5_ATTRIBUTES){ out->attributes |= ent.entry.flags.postdate ? 0 : KRB5_KDB_DISALLOW_POSTDATED; out->attributes |= ent.entry.flags.forwardable ? 0 : KRB5_KDB_DISALLOW_FORWARDABLE; out->attributes |= ent.entry.flags.initial ? KRB5_KDB_DISALLOW_TGT_BASED : 0; out->attributes |= ent.entry.flags.renewable ? 0 : KRB5_KDB_DISALLOW_RENEWABLE; out->attributes |= ent.entry.flags.proxiable ? 0 : KRB5_KDB_DISALLOW_PROXIABLE; out->attributes |= ent.entry.flags.invalid ? KRB5_KDB_DISALLOW_ALL_TIX : 0; out->attributes |= ent.entry.flags.require_preauth ? KRB5_KDB_REQUIRES_PRE_AUTH : 0; out->attributes |= ent.entry.flags.server ? 0 : KRB5_KDB_DISALLOW_SVR; out->attributes |= ent.entry.flags.change_pw ? KRB5_KDB_PWCHANGE_SERVICE : 0; out->attributes |= ent.entry.flags.ok_as_delegate ? KRB5_KDB_OK_AS_DELEGATE : 0; out->attributes |= ent.entry.flags.trusted_for_delegation ? KRB5_KDB_TRUSTED_FOR_DELEGATION : 0; out->attributes |= ent.entry.flags.allow_kerberos4 ? KRB5_KDB_ALLOW_KERBEROS4 : 0; out->attributes |= ent.entry.flags.allow_digest ? KRB5_KDB_ALLOW_DIGEST : 0; } if(mask & KADM5_MAX_LIFE) { if(ent.entry.max_life) out->max_life = *ent.entry.max_life; else out->max_life = INT_MAX; } if(mask & KADM5_MOD_TIME) { if(ent.entry.modified_by) out->mod_date = ent.entry.modified_by->time; else out->mod_date = ent.entry.created_by.time; } if(mask & KADM5_MOD_NAME) { if(ent.entry.modified_by) { if (ent.entry.modified_by->principal != NULL) ret = krb5_copy_principal(context->context, ent.entry.modified_by->principal, &out->mod_name); } else if(ent.entry.created_by.principal != NULL) ret = krb5_copy_principal(context->context, ent.entry.created_by.principal, &out->mod_name); else out->mod_name = NULL; } if(ret) goto out; if(mask & KADM5_KVNO) out->kvno = ent.entry.kvno; if(mask & KADM5_MKVNO) { size_t n; out->mkvno = 0; /* XXX */ for(n = 0; n < ent.entry.keys.len; n++) if(ent.entry.keys.val[n].mkvno) { out->mkvno = *ent.entry.keys.val[n].mkvno; /* XXX this isn't right */ break; } } #if 0 /* XXX implement */ if(mask & KADM5_AUX_ATTRIBUTES) ; if(mask & KADM5_LAST_SUCCESS) ; if(mask & KADM5_LAST_FAILED) ; if(mask & KADM5_FAIL_AUTH_COUNT) ; #endif if(mask & KADM5_POLICY) { HDB_extension *ext; ext = hdb_find_extension(&ent.entry, choice_HDB_extension_data_policy); if (ext == NULL) { out->policy = strdup("default"); /* It's OK if we retun NULL instead of "default" */ } else { out->policy = strdup(ext->data.u.policy); if (out->policy == NULL) { ret = ENOMEM; goto out; } } } if(mask & KADM5_MAX_RLIFE) { if(ent.entry.max_renew) out->max_renewable_life = *ent.entry.max_renew; else out->max_renewable_life = INT_MAX; } if(mask & KADM5_KEY_DATA){ size_t i; size_t n_keys = ent.entry.keys.len; krb5_salt salt; HDB_extension *ext; HDB_Ext_KeySet *hist_keys = NULL; ext = hdb_find_extension(&ent.entry, choice_HDB_extension_data_hist_keys); if (ext != NULL) hist_keys = &ext->data.u.hist_keys; krb5_get_pw_salt(context->context, ent.entry.principal, &salt); for (i = 0; hist_keys != NULL && i < hist_keys->len; i++) n_keys += hist_keys->val[i].keys.len; out->key_data = malloc(n_keys * sizeof(*out->key_data)); if (out->key_data == NULL && n_keys != 0) { ret = ENOMEM; goto out; } out->n_key_data = 0; ret = copy_keyset_to_kadm5(context, ent.entry.kvno, ent.entry.keys.len, ent.entry.keys.val, &salt, out); if (ret) goto out; for (i = 0; hist_keys != NULL && i < hist_keys->len; i++) { ret = copy_keyset_to_kadm5(context, hist_keys->val[i].kvno, hist_keys->val[i].keys.len, hist_keys->val[i].keys.val, &salt, out); if (ret) goto out; } krb5_free_salt(context->context, salt); assert( out->n_key_data == n_keys ); } if(ret){ kadm5_free_principal_ent(context, out); goto out; } if(mask & KADM5_TL_DATA) { time_t last_pw_expire; const HDB_Ext_PKINIT_acl *acl; const HDB_Ext_Aliases *aliases; ret = hdb_entry_get_pw_change_time(&ent.entry, &last_pw_expire); if (ret == 0 && last_pw_expire) { unsigned char buf[4]; _krb5_put_int(buf, last_pw_expire, sizeof(buf)); ret = add_tl_data(out, KRB5_TL_LAST_PWD_CHANGE, buf, sizeof(buf)); } if(ret){ kadm5_free_principal_ent(context, out); goto out; } /* * If the client was allowed to get key data, let it have the * password too. */ if(mask & KADM5_KEY_DATA) { heim_utf8_string pw; ret = hdb_entry_get_password(context->context, context->db, &ent.entry, &pw); if (ret == 0) { ret = add_tl_data(out, KRB5_TL_PASSWORD, pw, strlen(pw) + 1); free(pw); } krb5_clear_error_message(context->context); } ret = hdb_entry_get_pkinit_acl(&ent.entry, &acl); if (ret == 0 && acl) { krb5_data buf; size_t len; ASN1_MALLOC_ENCODE(HDB_Ext_PKINIT_acl, buf.data, buf.length, acl, &len, ret); if (ret) { kadm5_free_principal_ent(context, out); goto out; } if (len != buf.length) krb5_abortx(context->context, "internal ASN.1 encoder error"); ret = add_tl_data(out, KRB5_TL_PKINIT_ACL, buf.data, buf.length); free(buf.data); if (ret) { kadm5_free_principal_ent(context, out); goto out; } } if(ret){ kadm5_free_principal_ent(context, out); goto out; } ret = hdb_entry_get_aliases(&ent.entry, &aliases); if (ret == 0 && aliases) { krb5_data buf; size_t len; ASN1_MALLOC_ENCODE(HDB_Ext_Aliases, buf.data, buf.length, aliases, &len, ret); if (ret) { kadm5_free_principal_ent(context, out); goto out; } if (len != buf.length) krb5_abortx(context->context, "internal ASN.1 encoder error"); ret = add_tl_data(out, KRB5_TL_ALIASES, buf.data, buf.length); free(buf.data); if (ret) { kadm5_free_principal_ent(context, out); goto out; } } if(ret){ kadm5_free_principal_ent(context, out); goto out; } } out: hdb_free_entry(context->context, &ent); return _kadm5_error_code(ret); }
krb5_error_code KRB5_LIB_FUNCTION krb5_get_in_cred(krb5_context context, krb5_flags options, const krb5_addresses *addrs, const krb5_enctype *etypes, const krb5_preauthtype *ptypes, const krb5_preauthdata *preauth, krb5_key_proc key_proc, krb5_const_pointer keyseed, krb5_decrypt_proc decrypt_proc, krb5_const_pointer decryptarg, krb5_creds *creds, krb5_kdc_rep *ret_as_reply) { krb5_error_code ret; AS_REQ a; krb5_kdc_rep rep; krb5_data req, resp; size_t len; krb5_salt salt; krb5_keyblock *key; size_t size; KDCOptions opts; PA_DATA *pa; krb5_enctype etype; krb5_preauthdata *my_preauth = NULL; unsigned nonce; int done; opts = int2KDCOptions(options); krb5_generate_random_block (&nonce, sizeof(nonce)); nonce &= 0xffffffff; do { done = 1; ret = init_as_req (context, opts, creds, addrs, etypes, ptypes, preauth, key_proc, keyseed, nonce, &a); if (my_preauth) { free_ETYPE_INFO(&my_preauth->val[0].info); free (my_preauth->val); my_preauth = NULL; } if (ret) return ret; ASN1_MALLOC_ENCODE(AS_REQ, req.data, req.length, &a, &len, ret); free_AS_REQ(&a); if (ret) return ret; if(len != req.length) krb5_abortx(context, "internal error in ASN.1 encoder"); ret = krb5_sendto_kdc (context, &req, &creds->client->realm, &resp); krb5_data_free(&req); if (ret) return ret; memset (&rep, 0, sizeof(rep)); ret = decode_AS_REP(resp.data, resp.length, &rep.kdc_rep, &size); if(ret) { /* let's try to parse it as a KRB-ERROR */ KRB_ERROR error; int ret2; ret2 = krb5_rd_error(context, &resp, &error); if(ret2 && resp.data && ((char*)resp.data)[0] == 4) ret = KRB5KRB_AP_ERR_V4_REPLY; krb5_data_free(&resp); if (ret2 == 0) { ret = krb5_error_from_rd_error(context, &error, creds); /* if no preauth was set and KDC requires it, give it one more try */ if (!ptypes && !preauth && ret == KRB5KDC_ERR_PREAUTH_REQUIRED #if 0 || ret == KRB5KDC_ERR_BADOPTION #endif && set_ptypes(context, &error, &ptypes, &my_preauth)) { done = 0; preauth = my_preauth; krb5_free_error_contents(context, &error); krb5_clear_error_string(context); continue; } if(ret_as_reply) ret_as_reply->error = error; else free_KRB_ERROR (&error); return ret; } return ret; } krb5_data_free(&resp); } while(!done); pa = NULL; etype = rep.kdc_rep.enc_part.etype; if(rep.kdc_rep.padata){ int i = 0; pa = krb5_find_padata(rep.kdc_rep.padata->val, rep.kdc_rep.padata->len, KRB5_PADATA_PW_SALT, &i); if(pa == NULL) { i = 0; pa = krb5_find_padata(rep.kdc_rep.padata->val, rep.kdc_rep.padata->len, KRB5_PADATA_AFS3_SALT, &i); } } if(pa) { salt.salttype = pa->padata_type; salt.saltvalue = pa->padata_value; ret = (*key_proc)(context, etype, salt, keyseed, &key); } else { /* make a v5 salted pa-data */ ret = krb5_get_pw_salt (context, creds->client, &salt); if (ret) goto out; ret = (*key_proc)(context, etype, salt, keyseed, &key); krb5_free_salt(context, salt); } if (ret) goto out; { unsigned flags = 0; if (opts.request_anonymous) flags |= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH; ret = _krb5_extract_ticket(context, &rep, creds, key, keyseed, KRB5_KU_AS_REP_ENC_PART, NULL, nonce, flags, decrypt_proc, decryptarg); } memset (key->keyvalue.data, 0, key->keyvalue.length); krb5_free_keyblock_contents (context, key); free (key); out: if (ret == 0 && ret_as_reply) *ret_as_reply = rep; else krb5_free_kdc_rep (context, &rep); return ret; }