kadm5_ret_t _kadm5_set_keys(kadm5_server_context *context, hdb_entry *ent, const char *password) { Key *keys; size_t num_keys; kadm5_ret_t ret; ret = hdb_generate_key_set_password(context->context, ent->principal, password, &keys, &num_keys); if (ret) return ret; _kadm5_free_keys (context->context, ent->keys.len, ent->keys.val); ent->keys.val = keys; ent->keys.len = num_keys; hdb_entry_set_pw_change_time(context->context, ent, 0); if (krb5_config_get_bool_default(context->context, NULL, FALSE, "kadmin", "save-password", NULL)) { ret = hdb_entry_set_password(context->context, context->db, ent, password); if (ret) return ret; } return 0; }
kadm5_ret_t _kadm5_set_keys2(kadm5_server_context *context, hdb_entry *ent, int16_t n_key_data, krb5_key_data *key_data) { krb5_error_code ret; int i; unsigned len; Key *keys; len = n_key_data; keys = malloc (len * sizeof(*keys)); if (keys == NULL) return ENOMEM; _kadm5_init_keys (keys, len); for(i = 0; i < n_key_data; i++) { keys[i].mkvno = NULL; keys[i].key.keytype = key_data[i].key_data_type[0]; ret = krb5_data_copy(&keys[i].key.keyvalue, key_data[i].key_data_contents[0], key_data[i].key_data_length[0]); if(ret) goto out; if(key_data[i].key_data_ver == 2) { Salt *salt; salt = calloc(1, sizeof(*salt)); if(salt == NULL) { ret = ENOMEM; goto out; } keys[i].salt = salt; salt->type = key_data[i].key_data_type[1]; krb5_data_copy(&salt->salt, key_data[i].key_data_contents[1], key_data[i].key_data_length[1]); } else keys[i].salt = NULL; } _kadm5_free_keys (context->context, ent->keys.len, ent->keys.val); ent->keys.len = len; ent->keys.val = keys; hdb_entry_set_pw_change_time(context->context, ent, 0); hdb_entry_clear_password(context->context, ent); return 0; out: _kadm5_free_keys (context->context, len, keys); return ret; }
static kadm5_ret_t perform_tl_data(krb5_context context, HDB *db, hdb_entry_ex *ent, const krb5_tl_data *tl_data) { kadm5_ret_t ret = 0; if (tl_data->tl_data_type == KRB5_TL_PASSWORD) { heim_utf8_string pw = tl_data->tl_data_contents; if (pw[tl_data->tl_data_length] != '\0') return KADM5_BAD_TL_TYPE; ret = hdb_entry_set_password(context, db, &ent->entry, pw); } else if (tl_data->tl_data_type == KRB5_TL_LAST_PWD_CHANGE) { unsigned char *s; time_t t; if (tl_data->tl_data_length != 4) return KADM5_BAD_TL_TYPE; s = tl_data->tl_data_contents; t = s[0] | (s[1] << 8) | (s[2] << 16) | (s[3] << 24); ret = hdb_entry_set_pw_change_time(context, &ent->entry, t); } else if (tl_data->tl_data_type == KRB5_TL_EXTENSION) { HDB_extension ext; ret = decode_HDB_extension(tl_data->tl_data_contents, tl_data->tl_data_length, &ext, NULL); if (ret) return KADM5_BAD_TL_TYPE; ret = hdb_replace_extension(context, &ent->entry, &ext); free_HDB_extension(&ext); } else { return KADM5_BAD_TL_TYPE; } return ret; }
kadm5_ret_t _kadm5_set_keys3(kadm5_server_context *context, hdb_entry *ent, int n_keys, krb5_keyblock *keyblocks) { krb5_error_code ret; int i; unsigned len; Key *keys; len = n_keys; keys = malloc (len * sizeof(*keys)); if (keys == NULL) return ENOMEM; _kadm5_init_keys (keys, len); for(i = 0; i < n_keys; i++) { keys[i].mkvno = NULL; ret = krb5_copy_keyblock_contents (context->context, &keyblocks[i], &keys[i].key); if(ret) goto out; keys[i].salt = NULL; } _kadm5_free_keys (context->context, ent->keys.len, ent->keys.val); ent->keys.len = len; ent->keys.val = keys; hdb_entry_set_pw_change_time(context->context, ent, 0); hdb_entry_clear_password(context->context, ent); return 0; out: _kadm5_free_keys (context->context, len, keys); return ret; }
kadm5_ret_t _kadm5_set_keys_randomly (kadm5_server_context *context, hdb_entry *ent, krb5_keyblock **new_keys, int *n_keys) { krb5_keyblock *kblock = NULL; kadm5_ret_t ret = 0; int i, des_keyblock; size_t num_keys; Key *keys; ret = hdb_generate_key_set(context->context, ent->principal, &keys, &num_keys, 1); if (ret) return ret; kblock = malloc(num_keys * sizeof(kblock[0])); if (kblock == NULL) { ret = ENOMEM; _kadm5_free_keys (context->context, num_keys, keys); return ret; } memset(kblock, 0, num_keys * sizeof(kblock[0])); des_keyblock = -1; for (i = 0; i < num_keys; i++) { /* * To make sure all des keys are the the same we generate only * the first one and then copy key to all other des keys. */ if (des_keyblock != -1 && is_des_key_p(keys[i].key.keytype)) { ret = krb5_copy_keyblock_contents (context->context, &kblock[des_keyblock], &kblock[i]); if (ret) goto out; kblock[i].keytype = keys[i].key.keytype; } else { ret = krb5_generate_random_keyblock (context->context, keys[i].key.keytype, &kblock[i]); if (ret) goto out; if (is_des_key_p(keys[i].key.keytype)) des_keyblock = i; } ret = krb5_copy_keyblock_contents (context->context, &kblock[i], &keys[i].key); if (ret) goto out; } out: if(ret) { for (i = 0; i < num_keys; ++i) krb5_free_keyblock_contents (context->context, &kblock[i]); free(kblock); _kadm5_free_keys (context->context, num_keys, keys); return ret; } _kadm5_free_keys (context->context, ent->keys.len, ent->keys.val); ent->keys.val = keys; ent->keys.len = num_keys; *new_keys = kblock; *n_keys = num_keys; hdb_entry_set_pw_change_time(context->context, ent, 0); hdb_entry_clear_password(context->context, ent); return 0; }
/** * Server-side function to set new keys for a principal. */ kadm5_ret_t kadm5_s_setkey_principal_3(void *server_handle, krb5_principal princ, krb5_boolean keepold, int n_ks_tuple, krb5_key_salt_tuple *ks_tuple, krb5_keyblock *keyblocks, int n_keys) { kadm5_server_context *context = server_handle; hdb_entry_ex ent; kadm5_ret_t ret = 0; memset(&ent, 0, sizeof(ent)); 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) { if (!context->keep_open) context->db->hdb_close(context->context, context->db); return ret; } ret = context->db->hdb_fetch_kvno(context->context, context->db, princ, HDB_F_GET_ANY|HDB_F_ADMIN_DATA, 0, &ent); if (ret) { (void) kadm5_log_end(context); if (!context->keep_open) context->db->hdb_close(context->context, context->db); return ret; } if (keepold) { ret = hdb_add_current_keys_to_history(context->context, &ent.entry); } else ret = hdb_clear_extension(context->context, &ent.entry, choice_HDB_extension_data_hist_keys); /* * Though in practice all real calls to this function will pass an empty * ks_tuple, and cannot in any case employ any salts that require * additional data, we go the extra mile to set any requested salt type * along with a zero length salt value. While we're at it we check that * each ks_tuple's enctype matches the corresponding key enctype. */ if (ret == 0) { int i; free_Keys(&ent.entry.keys); for (i = 0; i < n_keys; ++i) { Key k; Salt s; k.mkvno = 0; k.key = keyblocks[i]; if (n_ks_tuple == 0) k.salt = 0; else { if (ks_tuple[i].ks_enctype != keyblocks[i].keytype) { ret = KADM5_SETKEY3_ETYPE_MISMATCH; break; } s.type = ks_tuple[i].ks_salttype; s.salt.data = 0; s.opaque = 0; k.salt = &s; } if ((ret = add_Keys(&ent.entry.keys, &k)) != 0) break; } } if (ret == 0) { ent.entry.kvno++; ent.entry.flags.require_pwchange = 0; hdb_entry_set_pw_change_time(context->context, &ent.entry, 0); hdb_entry_clear_password(context->context, &ent.entry); if ((ret = hdb_seal_keys(context->context, context->db, &ent.entry)) == 0 && (ret = _kadm5_set_modifier(context, &ent.entry)) == 0 && (ret = _kadm5_bump_pw_expire(context, &ent.entry)) == 0) ret = kadm5_log_modify(context, &ent.entry, KADM5_ATTRIBUTES | KADM5_PRINCIPAL | KADM5_MOD_NAME | KADM5_MOD_TIME | KADM5_KEY_DATA | KADM5_KVNO | KADM5_PW_EXPIRATION | KADM5_TL_DATA); } hdb_free_entry(context->context, &ent); (void) kadm5_log_end(context); if (!context->keep_open) context->db->hdb_close(context->context, context->db); return _kadm5_error_code(ret); }
/* * Construct an hdb_entry from a directory entry. */ static krb5_error_code LDAP_message2entry(krb5_context context, HDB * db, LDAPMessage * msg, hdb_entry_ex * ent) { char *unparsed_name = NULL, *dn = NULL, *ntPasswordIN = NULL; char *samba_acct_flags = NULL; struct berval **keys; struct berval **vals; int tmp, tmp_time, i, ret, have_arcfour = 0; memset(ent, 0, sizeof(*ent)); ent->entry.flags = int2HDBFlags(0); ret = LDAP_get_string_value(db, msg, "krb5PrincipalName", &unparsed_name); if (ret == 0) { ret = krb5_parse_name(context, unparsed_name, &ent->entry.principal); if (ret) goto out; } else { ret = LDAP_get_string_value(db, msg, "uid", &unparsed_name); if (ret == 0) { ret = krb5_parse_name(context, unparsed_name, &ent->entry.principal); if (ret) goto out; } else { krb5_set_error_message(context, HDB_ERR_NOENTRY, "hdb-ldap: ldap entry missing" "principal name"); return HDB_ERR_NOENTRY; } } { int integer; ret = LDAP_get_integer_value(db, msg, "krb5KeyVersionNumber", &integer); if (ret) ent->entry.kvno = 0; else ent->entry.kvno = integer; } keys = ldap_get_values_len(HDB2LDAP(db), msg, "krb5Key"); if (keys != NULL) { int i; size_t l; ent->entry.keys.len = ldap_count_values_len(keys); ent->entry.keys.val = (Key *) calloc(ent->entry.keys.len, sizeof(Key)); if (ent->entry.keys.val == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, "calloc: out of memory"); goto out; } for (i = 0; i < ent->entry.keys.len; i++) { decode_Key((unsigned char *) keys[i]->bv_val, (size_t) keys[i]->bv_len, &ent->entry.keys.val[i], &l); } ber_bvecfree(keys); } else { #if 1 /* * This violates the ASN1 but it allows a principal to * be related to a general directory entry without creating * the keys. Hopefully it's OK. */ ent->entry.keys.len = 0; ent->entry.keys.val = NULL; #else ret = HDB_ERR_NOENTRY; goto out; #endif } vals = ldap_get_values_len(HDB2LDAP(db), msg, "krb5EncryptionType"); if (vals != NULL) { int i; ent->entry.etypes = malloc(sizeof(*(ent->entry.etypes))); if (ent->entry.etypes == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret,"malloc: out of memory"); goto out; } ent->entry.etypes->len = ldap_count_values_len(vals); ent->entry.etypes->val = calloc(ent->entry.etypes->len, sizeof(int)); if (ent->entry.etypes->val == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); ent->entry.etypes->len = 0; goto out; } for (i = 0; i < ent->entry.etypes->len; i++) { char *buf; buf = malloc(vals[i]->bv_len + 1); if (buf == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); goto out; } memcpy(buf, vals[i]->bv_val, vals[i]->bv_len); buf[vals[i]->bv_len] = '\0'; ent->entry.etypes->val[i] = atoi(buf); free(buf); } ldap_value_free_len(vals); } for (i = 0; i < ent->entry.keys.len; i++) { if (ent->entry.keys.val[i].key.keytype == ETYPE_ARCFOUR_HMAC_MD5) { have_arcfour = 1; break; } } /* manually construct the NT (type 23) key */ ret = LDAP_get_string_value(db, msg, "sambaNTPassword", &ntPasswordIN); if (ret == 0 && have_arcfour == 0) { unsigned *etypes; Key *keys; int i; keys = realloc(ent->entry.keys.val, (ent->entry.keys.len + 1) * sizeof(ent->entry.keys.val[0])); if (keys == NULL) { free(ntPasswordIN); ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); goto out; } ent->entry.keys.val = keys; memset(&ent->entry.keys.val[ent->entry.keys.len], 0, sizeof(Key)); ent->entry.keys.val[ent->entry.keys.len].key.keytype = ETYPE_ARCFOUR_HMAC_MD5; ret = krb5_data_alloc (&ent->entry.keys.val[ent->entry.keys.len].key.keyvalue, 16); if (ret) { krb5_set_error_message(context, ret, "malloc: out of memory"); free(ntPasswordIN); ret = ENOMEM; goto out; } ret = hex_decode(ntPasswordIN, ent->entry.keys.val[ent->entry.keys.len].key.keyvalue.data, 16); ent->entry.keys.len++; if (ent->entry.etypes == NULL) { ent->entry.etypes = malloc(sizeof(*(ent->entry.etypes))); if (ent->entry.etypes == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); goto out; } ent->entry.etypes->val = NULL; ent->entry.etypes->len = 0; } for (i = 0; i < ent->entry.etypes->len; i++) if (ent->entry.etypes->val[i] == ETYPE_ARCFOUR_HMAC_MD5) break; /* If there is no ARCFOUR enctype, add one */ if (i == ent->entry.etypes->len) { etypes = realloc(ent->entry.etypes->val, (ent->entry.etypes->len + 1) * sizeof(ent->entry.etypes->val[0])); if (etypes == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); goto out; } ent->entry.etypes->val = etypes; ent->entry.etypes->val[ent->entry.etypes->len] = ETYPE_ARCFOUR_HMAC_MD5; ent->entry.etypes->len++; } } ret = LDAP_get_generalized_time_value(db, msg, "createTimestamp", &ent->entry.created_by.time); if (ret) ent->entry.created_by.time = time(NULL); ent->entry.created_by.principal = NULL; ret = LDAP_get_string_value(db, msg, "creatorsName", &dn); if (ret == 0) { if (LDAP_dn2principal(context, db, dn, &ent->entry.created_by.principal) != 0) { ent->entry.created_by.principal = NULL; } free(dn); } ent->entry.modified_by = (Event *) malloc(sizeof(Event)); if (ent->entry.modified_by == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); goto out; } ret = LDAP_get_generalized_time_value(db, msg, "modifyTimestamp", &ent->entry.modified_by->time); if (ret == 0) { ret = LDAP_get_string_value(db, msg, "modifiersName", &dn); if (LDAP_dn2principal(context, db, dn, &ent->entry.modified_by->principal)) ent->entry.modified_by->principal = NULL; free(dn); } else { free(ent->entry.modified_by); ent->entry.modified_by = NULL; } ent->entry.valid_start = malloc(sizeof(*ent->entry.valid_start)); if (ent->entry.valid_start == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); goto out; } ret = LDAP_get_generalized_time_value(db, msg, "krb5ValidStart", ent->entry.valid_start); if (ret) { /* OPTIONAL */ free(ent->entry.valid_start); ent->entry.valid_start = NULL; } ent->entry.valid_end = malloc(sizeof(*ent->entry.valid_end)); if (ent->entry.valid_end == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); goto out; } ret = LDAP_get_generalized_time_value(db, msg, "krb5ValidEnd", ent->entry.valid_end); if (ret) { /* OPTIONAL */ free(ent->entry.valid_end); ent->entry.valid_end = NULL; } ret = LDAP_get_integer_value(db, msg, "sambaKickoffTime", &tmp_time); if (ret == 0) { if (ent->entry.valid_end == NULL) { ent->entry.valid_end = malloc(sizeof(*ent->entry.valid_end)); if (ent->entry.valid_end == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); goto out; } } *ent->entry.valid_end = tmp_time; } ent->entry.pw_end = malloc(sizeof(*ent->entry.pw_end)); if (ent->entry.pw_end == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); goto out; } ret = LDAP_get_generalized_time_value(db, msg, "krb5PasswordEnd", ent->entry.pw_end); if (ret) { /* OPTIONAL */ free(ent->entry.pw_end); ent->entry.pw_end = NULL; } ret = LDAP_get_integer_value(db, msg, "sambaPwdLastSet", &tmp_time); if (ret == 0) { time_t delta; if (ent->entry.pw_end == NULL) { ent->entry.pw_end = malloc(sizeof(*ent->entry.pw_end)); if (ent->entry.pw_end == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); goto out; } } delta = krb5_config_get_time_default(context, NULL, 365 * 24 * 60 * 60, "kadmin", "password_lifetime", NULL); *ent->entry.pw_end = tmp_time + delta; } ret = LDAP_get_integer_value(db, msg, "sambaPwdMustChange", &tmp_time); if (ret == 0) { if (ent->entry.pw_end == NULL) { ent->entry.pw_end = malloc(sizeof(*ent->entry.pw_end)); if (ent->entry.pw_end == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); goto out; } } *ent->entry.pw_end = tmp_time; } /* OPTIONAL */ ret = LDAP_get_integer_value(db, msg, "sambaPwdLastSet", &tmp_time); if (ret == 0) hdb_entry_set_pw_change_time(context, &ent->entry, tmp_time); { int max_life; ent->entry.max_life = malloc(sizeof(*ent->entry.max_life)); if (ent->entry.max_life == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); goto out; } ret = LDAP_get_integer_value(db, msg, "krb5MaxLife", &max_life); if (ret) { free(ent->entry.max_life); ent->entry.max_life = NULL; } else *ent->entry.max_life = max_life; } { int max_renew; ent->entry.max_renew = malloc(sizeof(*ent->entry.max_renew)); if (ent->entry.max_renew == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); goto out; } ret = LDAP_get_integer_value(db, msg, "krb5MaxRenew", &max_renew); if (ret) { free(ent->entry.max_renew); ent->entry.max_renew = NULL; } else *ent->entry.max_renew = max_renew; } ret = LDAP_get_integer_value(db, msg, "krb5KDCFlags", &tmp); if (ret) tmp = 0; ent->entry.flags = int2HDBFlags(tmp); /* Try and find Samba flags to put into the mix */ ret = LDAP_get_string_value(db, msg, "sambaAcctFlags", &samba_acct_flags); if (ret == 0) { /* parse the [UXW...] string: 'N' No password 'D' Disabled 'H' Homedir required 'T' Temp account. 'U' User account (normal) 'M' MNS logon user account - what is this ? 'W' Workstation account 'S' Server account 'L' Locked account 'X' No Xpiry on password 'I' Interdomain trust account */ int i; int flags_len = strlen(samba_acct_flags); if (flags_len < 2) goto out2; if (samba_acct_flags[0] != '[' || samba_acct_flags[flags_len - 1] != ']') goto out2; /* Allow forwarding */ if (samba_forwardable) ent->entry.flags.forwardable = TRUE; for (i=0; i < flags_len; i++) { switch (samba_acct_flags[i]) { case ' ': case '[': case ']': break; case 'N': /* how to handle no password in kerberos? */ break; case 'D': ent->entry.flags.invalid = TRUE; break; case 'H': break; case 'T': /* temp duplicate */ ent->entry.flags.invalid = TRUE; break; case 'U': ent->entry.flags.client = TRUE; break; case 'M': break; case 'W': case 'S': ent->entry.flags.server = TRUE; ent->entry.flags.client = TRUE; break; case 'L': ent->entry.flags.invalid = TRUE; break; case 'X': if (ent->entry.pw_end) { free(ent->entry.pw_end); ent->entry.pw_end = NULL; } break; case 'I': ent->entry.flags.server = TRUE; ent->entry.flags.client = TRUE; break; } } out2: free(samba_acct_flags); } ret = 0; out: if (unparsed_name) free(unparsed_name); if (ret) hdb_free_entry(context, ent); return ret; }