static krb5_error_code LDAP_entry2mods(krb5_context context, HDB * db, hdb_entry_ex * ent, LDAPMessage * msg, LDAPMod *** pmods) { krb5_error_code ret; krb5_boolean is_new_entry; char *tmp = NULL; LDAPMod **mods = NULL; hdb_entry_ex orig; unsigned long oflags, nflags; int i; krb5_boolean is_samba_account = FALSE; krb5_boolean is_account = FALSE; krb5_boolean is_heimdal_entry = FALSE; krb5_boolean is_heimdal_principal = FALSE; struct berval **vals; *pmods = NULL; if (msg != NULL) { ret = LDAP_message2entry(context, db, msg, 0, &orig); if (ret) goto out; is_new_entry = FALSE; vals = ldap_get_values_len(HDB2LDAP(db), msg, "objectClass"); if (vals) { int num_objectclasses = ldap_count_values_len(vals); for (i=0; i < num_objectclasses; i++) { if (bervalstrcmp(vals[i], "sambaSamAccount")) is_samba_account = TRUE; else if (bervalstrcmp(vals[i], structural_object)) is_account = TRUE; else if (bervalstrcmp(vals[i], "krb5Principal")) is_heimdal_principal = TRUE; else if (bervalstrcmp(vals[i], "krb5KDCEntry")) is_heimdal_entry = TRUE; } ldap_value_free_len(vals); } /* * If this is just a "account" entry and no other objectclass * is hanging on this entry, it's really a new entry. */ if (is_samba_account == FALSE && is_heimdal_principal == FALSE && is_heimdal_entry == FALSE) { if (is_account == TRUE) { is_new_entry = TRUE; } else { ret = HDB_ERR_NOENTRY; goto out; } } } else is_new_entry = TRUE; if (is_new_entry) { /* to make it perfectly obvious we're depending on * orig being intiialized to zero */ memset(&orig, 0, sizeof(orig)); ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass", "top"); if (ret) goto out; /* account is the structural object class */ if (is_account == FALSE) { ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass", structural_object); is_account = TRUE; if (ret) goto out; } ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass", "krb5Principal"); is_heimdal_principal = TRUE; if (ret) goto out; ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass", "krb5KDCEntry"); is_heimdal_entry = TRUE; if (ret) goto out; } if (is_new_entry || krb5_principal_compare(context, ent->entry.principal, orig.entry.principal) == FALSE) { if (is_heimdal_principal || is_heimdal_entry) { ret = krb5_unparse_name(context, ent->entry.principal, &tmp); if (ret) goto out; ret = LDAP_addmod(&mods, LDAP_MOD_REPLACE, "krb5PrincipalName", tmp); if (ret) { free(tmp); goto out; } free(tmp); } if (is_account || is_samba_account) { ret = krb5_unparse_name_short(context, ent->entry.principal, &tmp); if (ret) goto out; ret = LDAP_addmod(&mods, LDAP_MOD_REPLACE, "uid", tmp); if (ret) { free(tmp); goto out; } free(tmp); } } if (is_heimdal_entry && (ent->entry.kvno != orig.entry.kvno || is_new_entry)) { ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE, "krb5KeyVersionNumber", ent->entry.kvno); if (ret) goto out; } if (is_heimdal_entry && ent->entry.valid_start) { if (orig.entry.valid_end == NULL || (*(ent->entry.valid_start) != *(orig.entry.valid_start))) { ret = LDAP_addmod_generalized_time(&mods, LDAP_MOD_REPLACE, "krb5ValidStart", ent->entry.valid_start); if (ret) goto out; } } if (ent->entry.valid_end) { if (orig.entry.valid_end == NULL || (*(ent->entry.valid_end) != *(orig.entry.valid_end))) { if (is_heimdal_entry) { ret = LDAP_addmod_generalized_time(&mods, LDAP_MOD_REPLACE, "krb5ValidEnd", ent->entry.valid_end); if (ret) goto out; } if (is_samba_account) { ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE, "sambaKickoffTime", *(ent->entry.valid_end)); if (ret) goto out; } } } if (ent->entry.pw_end) { if (orig.entry.pw_end == NULL || (*(ent->entry.pw_end) != *(orig.entry.pw_end))) { if (is_heimdal_entry) { ret = LDAP_addmod_generalized_time(&mods, LDAP_MOD_REPLACE, "krb5PasswordEnd", ent->entry.pw_end); if (ret) goto out; } if (is_samba_account) { ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE, "sambaPwdMustChange", *(ent->entry.pw_end)); if (ret) goto out; } } } #if 0 /* we we have last_pw_change */ if (is_samba_account && ent->entry.last_pw_change) { if (orig.entry.last_pw_change == NULL || (*(ent->entry.last_pw_change) != *(orig.entry.last_pw_change))) { ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE, "sambaPwdLastSet", *(ent->entry.last_pw_change)); if (ret) goto out; } } #endif if (is_heimdal_entry && ent->entry.max_life) { if (orig.entry.max_life == NULL || (*(ent->entry.max_life) != *(orig.entry.max_life))) { ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE, "krb5MaxLife", *(ent->entry.max_life)); if (ret) goto out; } } if (is_heimdal_entry && ent->entry.max_renew) { if (orig.entry.max_renew == NULL || (*(ent->entry.max_renew) != *(orig.entry.max_renew))) { ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE, "krb5MaxRenew", *(ent->entry.max_renew)); if (ret) goto out; } } oflags = HDBFlags2int(orig.entry.flags); nflags = HDBFlags2int(ent->entry.flags); if (is_heimdal_entry && oflags != nflags) { ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE, "krb5KDCFlags", nflags); if (ret) goto out; } /* Remove keys if they exists, and then replace keys. */ if (!is_new_entry && orig.entry.keys.len > 0) { vals = ldap_get_values_len(HDB2LDAP(db), msg, "krb5Key"); if (vals) { ldap_value_free_len(vals); ret = LDAP_addmod(&mods, LDAP_MOD_DELETE, "krb5Key", NULL); if (ret) goto out; } } for (i = 0; i < ent->entry.keys.len; i++) { if (is_samba_account && ent->entry.keys.val[i].key.keytype == ETYPE_ARCFOUR_HMAC_MD5) { char *ntHexPassword; char *nt; time_t now = time(NULL); /* the key might have been 'sealed', but samba passwords are clear in the directory */ ret = hdb_unseal_key(context, db, &ent->entry.keys.val[i]); if (ret) goto out; nt = ent->entry.keys.val[i].key.keyvalue.data; /* store in ntPassword, not krb5key */ ret = hex_encode(nt, 16, &ntHexPassword); if (ret < 0) { ret = ENOMEM; krb5_set_error_message(context, ret, "hdb-ldap: failed to " "hex encode key"); goto out; } ret = LDAP_addmod(&mods, LDAP_MOD_REPLACE, "sambaNTPassword", ntHexPassword); free(ntHexPassword); if (ret) goto out; ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE, "sambaPwdLastSet", now); if (ret) goto out; /* have to kill the LM passwod if it exists */ vals = ldap_get_values_len(HDB2LDAP(db), msg, "sambaLMPassword"); if (vals) { ldap_value_free_len(vals); ret = LDAP_addmod(&mods, LDAP_MOD_DELETE, "sambaLMPassword", NULL); if (ret) goto out; } } else if (is_heimdal_entry) { unsigned char *buf; size_t len, buf_size; ASN1_MALLOC_ENCODE(Key, buf, buf_size, &ent->entry.keys.val[i], &len, ret); if (ret) goto out; if(buf_size != len) krb5_abortx(context, "internal error in ASN.1 encoder"); /* addmod_len _owns_ the key, doesn't need to copy it */ ret = LDAP_addmod_len(&mods, LDAP_MOD_ADD, "krb5Key", buf, len); if (ret) goto out; } } if (ent->entry.etypes) { int add_krb5EncryptionType = 0; /* * Only add/modify krb5EncryptionType if it's a new heimdal * entry or krb5EncryptionType already exists on the entry. */ if (!is_new_entry) { vals = ldap_get_values_len(HDB2LDAP(db), msg, "krb5EncryptionType"); if (vals) { ldap_value_free_len(vals); ret = LDAP_addmod(&mods, LDAP_MOD_DELETE, "krb5EncryptionType", NULL); if (ret) goto out; add_krb5EncryptionType = 1; } } else if (is_heimdal_entry) add_krb5EncryptionType = 1; if (add_krb5EncryptionType) { for (i = 0; i < ent->entry.etypes->len; i++) { if (is_samba_account && ent->entry.keys.val[i].key.keytype == ETYPE_ARCFOUR_HMAC_MD5) { ; } else if (is_heimdal_entry) { ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_ADD, "krb5EncryptionType", ent->entry.etypes->val[i]); if (ret) goto out; } } } } /* for clarity */ ret = 0; out: if (ret == 0) *pmods = mods; else if (mods != NULL) { ldap_mods_free(mods, 1); *pmods = NULL; } if (msg) hdb_free_entry(context, &orig); return ret; }
static krb5_error_code entry2string_int (krb5_context context, krb5_storage *sp, hdb_entry *ent) { char *p; int i; krb5_error_code ret; /* --- principal */ ret = krb5_unparse_name(context, ent->principal, &p); if(ret) return ret; append_string(context, sp, "%s ", p); free(p); /* --- kvno */ append_string(context, sp, "%d", ent->kvno); /* --- keys */ for(i = 0; i < ent->keys.len; i++){ /* --- mkvno, keytype */ if(ent->keys.val[i].mkvno) append_string(context, sp, ":%d:%d:", *ent->keys.val[i].mkvno, ent->keys.val[i].key.keytype); else append_string(context, sp, "::%d:", ent->keys.val[i].key.keytype); /* --- keydata */ append_hex(context, sp, &ent->keys.val[i].key.keyvalue); append_string(context, sp, ":"); /* --- salt */ if(ent->keys.val[i].salt){ append_string(context, sp, "%u/", ent->keys.val[i].salt->type); append_hex(context, sp, &ent->keys.val[i].salt->salt); }else append_string(context, sp, "-"); } append_string(context, sp, " "); /* --- created by */ append_event(context, sp, &ent->created_by); /* --- modified by */ append_event(context, sp, ent->modified_by); /* --- valid start */ if(ent->valid_start) append_string(context, sp, "%s ", time2str(*ent->valid_start)); else append_string(context, sp, "- "); /* --- valid end */ if(ent->valid_end) append_string(context, sp, "%s ", time2str(*ent->valid_end)); else append_string(context, sp, "- "); /* --- password ends */ if(ent->pw_end) append_string(context, sp, "%s ", time2str(*ent->pw_end)); else append_string(context, sp, "- "); /* --- max life */ if(ent->max_life) append_string(context, sp, "%d ", *ent->max_life); else append_string(context, sp, "- "); /* --- max renewable life */ if(ent->max_renew) append_string(context, sp, "%d ", *ent->max_renew); else append_string(context, sp, "- "); /* --- flags */ append_string(context, sp, "%d ", HDBFlags2int(ent->flags)); /* --- generation number */ if(ent->generation) { append_string(context, sp, "%s:%d:%d", time2str(ent->generation->time), ent->generation->usec, ent->generation->gen); } else append_string(context, sp, "-"); return 0; }
static krb5_error_code entry2string_int (krb5_context context, krb5_storage *sp, hdb_entry *ent) { char *p; size_t i; krb5_error_code ret; /* --- principal */ ret = krb5_unparse_name(context, ent->principal, &p); if(ret) return ret; append_string(context, sp, "%s ", p); free(p); /* --- kvno */ append_string(context, sp, "%d", ent->kvno); /* --- keys */ for(i = 0; i < ent->keys.len; i++){ /* --- mkvno, keytype */ if(ent->keys.val[i].mkvno) append_string(context, sp, ":%d:%d:", *ent->keys.val[i].mkvno, ent->keys.val[i].key.keytype); else append_string(context, sp, "::%d:", ent->keys.val[i].key.keytype); /* --- keydata */ append_hex(context, sp, &ent->keys.val[i].key.keyvalue); append_string(context, sp, ":"); /* --- salt */ if(ent->keys.val[i].salt){ append_string(context, sp, "%u/", ent->keys.val[i].salt->type); append_hex(context, sp, &ent->keys.val[i].salt->salt); }else append_string(context, sp, "-"); } append_string(context, sp, " "); /* --- created by */ append_event(context, sp, &ent->created_by); /* --- modified by */ append_event(context, sp, ent->modified_by); /* --- valid start */ if(ent->valid_start) append_string(context, sp, "%s ", time2str(*ent->valid_start)); else append_string(context, sp, "- "); /* --- valid end */ if(ent->valid_end) append_string(context, sp, "%s ", time2str(*ent->valid_end)); else append_string(context, sp, "- "); /* --- password ends */ if(ent->pw_end) append_string(context, sp, "%s ", time2str(*ent->pw_end)); else append_string(context, sp, "- "); /* --- max life */ if(ent->max_life) append_string(context, sp, "%d ", *ent->max_life); else append_string(context, sp, "- "); /* --- max renewable life */ if(ent->max_renew) append_string(context, sp, "%d ", *ent->max_renew); else append_string(context, sp, "- "); /* --- flags */ append_string(context, sp, "%d ", HDBFlags2int(ent->flags)); /* --- generation number */ if(ent->generation) { append_string(context, sp, "%s:%d:%d ", time2str(ent->generation->time), ent->generation->usec, ent->generation->gen); } else append_string(context, sp, "- "); /* --- extensions */ if(ent->extensions && ent->extensions->len > 0) { for(i = 0; i < ent->extensions->len; i++) { void *d; size_t size, sz = 0; ASN1_MALLOC_ENCODE(HDB_extension, d, size, &ent->extensions->val[i], &sz, ret); if (ret) { krb5_clear_error_message(context); return ret; } if(size != sz) krb5_abortx(context, "internal asn.1 encoder error"); if (hex_encode(d, size, &p) < 0) { free(d); krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); return ENOMEM; } free(d); append_string(context, sp, "%s%s", p, ent->extensions->len - 1 != i ? ":" : ""); free(p); } } else append_string(context, sp, "-"); return 0; }
static void print_entry(kadm5_server_context *server_context, uint32_t ver, time_t timestamp, enum kadm_ops op, uint32_t len, krb5_storage *sp, void *ctx) { char t[256]; int32_t mask; hdb_entry ent; krb5_principal source; char *name1, *name2; krb5_data data; krb5_context scontext = server_context->context; off_t end = krb5_storage_seek(sp, 0, SEEK_CUR) + len; krb5_error_code ret; strftime(t, sizeof(t), "%Y-%m-%d %H:%M:%S", localtime(×tamp)); if((int)op < (int)kadm_get || (int)op > (int)kadm_nop) { printf("unknown op: %d\n", op); krb5_storage_seek(sp, end, SEEK_SET); return; } printf ("%s: ver = %u, timestamp = %s, len = %u\n", op_names[op], ver, t, len); switch(op) { case kadm_delete: krb5_ret_principal(sp, &source); krb5_unparse_name(scontext, source, &name1); printf(" %s\n", name1); free(name1); krb5_free_principal(scontext, source); break; case kadm_rename: ret = krb5_data_alloc(&data, len); if (ret) krb5_err (scontext, 1, ret, "kadm_rename: data alloc: %d", len); krb5_ret_principal(sp, &source); krb5_storage_read(sp, data.data, data.length); hdb_value2entry(scontext, &data, &ent); krb5_unparse_name(scontext, source, &name1); krb5_unparse_name(scontext, ent.principal, &name2); printf(" %s -> %s\n", name1, name2); free(name1); free(name2); krb5_free_principal(scontext, source); free_hdb_entry(&ent); break; case kadm_create: ret = krb5_data_alloc(&data, len); if (ret) krb5_err (scontext, 1, ret, "kadm_create: data alloc: %d", len); krb5_storage_read(sp, data.data, data.length); ret = hdb_value2entry(scontext, &data, &ent); if(ret) abort(); mask = ~0; goto foo; case kadm_modify: ret = krb5_data_alloc(&data, len); if (ret) krb5_err (scontext, 1, ret, "kadm_modify: data alloc: %d", len); krb5_ret_int32(sp, &mask); krb5_storage_read(sp, data.data, data.length); ret = hdb_value2entry(scontext, &data, &ent); if(ret) abort(); foo: if(ent.principal /* mask & KADM5_PRINCIPAL */) { krb5_unparse_name(scontext, ent.principal, &name1); printf(" principal = %s\n", name1); free(name1); } if(mask & KADM5_PRINC_EXPIRE_TIME) { if(ent.valid_end == NULL) { strlcpy(t, "never", sizeof(t)); } else { strftime(t, sizeof(t), "%Y-%m-%d %H:%M:%S", localtime(ent.valid_end)); } printf(" expires = %s\n", t); } if(mask & KADM5_PW_EXPIRATION) { if(ent.pw_end == NULL) { strlcpy(t, "never", sizeof(t)); } else { strftime(t, sizeof(t), "%Y-%m-%d %H:%M:%S", localtime(ent.pw_end)); } printf(" password exp = %s\n", t); } if(mask & KADM5_LAST_PWD_CHANGE) { } if(mask & KADM5_ATTRIBUTES) { unparse_flags(HDBFlags2int(ent.flags), asn1_HDBFlags_units(), t, sizeof(t)); printf(" attributes = %s\n", t); } if(mask & KADM5_MAX_LIFE) { if(ent.max_life == NULL) strlcpy(t, "for ever", sizeof(t)); else unparse_time(*ent.max_life, t, sizeof(t)); printf(" max life = %s\n", t); } if(mask & KADM5_MAX_RLIFE) { if(ent.max_renew == NULL) strlcpy(t, "for ever", sizeof(t)); else unparse_time(*ent.max_renew, t, sizeof(t)); printf(" max rlife = %s\n", t); } if(mask & KADM5_MOD_TIME) { printf(" mod time\n"); } if(mask & KADM5_MOD_NAME) { printf(" mod name\n"); } if(mask & KADM5_KVNO) { printf(" kvno = %d\n", ent.kvno); } if(mask & KADM5_MKVNO) { printf(" mkvno\n"); } if(mask & KADM5_AUX_ATTRIBUTES) { printf(" aux attributes\n"); } if(mask & KADM5_POLICY) { printf(" policy\n"); } if(mask & KADM5_POLICY_CLR) { printf(" mod time\n"); } if(mask & KADM5_LAST_SUCCESS) { printf(" last success\n"); } if(mask & KADM5_LAST_FAILED) { printf(" last failed\n"); } if(mask & KADM5_FAIL_AUTH_COUNT) { printf(" fail auth count\n"); } if(mask & KADM5_KEY_DATA) { printf(" key data\n"); } if(mask & KADM5_TL_DATA) { printf(" tl data\n"); } free_hdb_entry(&ent); break; case kadm_nop : break; default: abort(); } krb5_storage_seek(sp, end, SEEK_SET); }