diff options
-rw-r--r-- | source4/auth/credentials/credentials_files.c | 29 | ||||
-rw-r--r-- | source4/auth/credentials/credentials_krb5.c | 14 | ||||
-rw-r--r-- | source4/auth/kerberos/kerberos_util.c | 4 | ||||
-rw-r--r-- | source4/auth/kerberos/krb5_init_context.c | 2 | ||||
-rw-r--r-- | source4/heimdal/lib/hdb/hdb-protos.h | 6 | ||||
-rw-r--r-- | source4/heimdal/lib/hdb/hdb.c | 5 | ||||
-rw-r--r-- | source4/heimdal/lib/hdb/keytab.c | 276 | ||||
-rw-r--r-- | source4/heimdal/lib/krb5/krb5.h | 3 | ||||
-rw-r--r-- | source4/heimdal_build/config.mk | 2 | ||||
-rw-r--r-- | source4/kdc/config.mk | 15 | ||||
-rw-r--r-- | source4/kdc/hdb-ldb.c | 21 | ||||
-rw-r--r-- | source4/kdc/kdc.c | 9 | ||||
-rw-r--r-- | source4/kdc/kdc.h | 4 | ||||
-rw-r--r-- | source4/kdc/kpasswdd.c | 5 | ||||
-rw-r--r-- | source4/setup/secrets.ldif | 8 |
15 files changed, 369 insertions, 34 deletions
diff --git a/source4/auth/credentials/credentials_files.c b/source4/auth/credentials/credentials_files.c index 219869cf3a..53350b8ed0 100644 --- a/source4/auth/credentials/credentials_files.c +++ b/source4/auth/credentials/credentials_files.c @@ -267,17 +267,12 @@ NTSTATUS cli_credentials_set_secrets(struct cli_credentials *cred, cli_credentials_set_nt_hash(cred, &hash, CRED_SPECIFIED); } else { - - DEBUG(1, ("Could not find 'secret' in join record to domain: %s\n", - cli_credentials_get_domain(cred))); - - /* set anonymous as the fallback, if the machine account won't work */ - cli_credentials_set_anonymous(cred); - - talloc_free(mem_ctx); - return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; + cli_credentials_set_password(cred, NULL, CRED_SPECIFIED); } + } else { + cli_credentials_set_password(cred, password, CRED_SPECIFIED); } + domain = ldb_msg_find_string(msgs[0], "flatname", NULL); if (domain) { @@ -290,9 +285,6 @@ NTSTATUS cli_credentials_set_secrets(struct cli_credentials *cred, } cli_credentials_set_username(cred, machine_account, CRED_SPECIFIED); - if (password) { - cli_credentials_set_password(cred, password, CRED_SPECIFIED); - } cli_credentials_set_kvno(cred, ldb_msg_find_int(msgs[0], "msDS-KeyVersionNumber", 0)); @@ -417,13 +409,14 @@ NTSTATUS cli_credentials_update_all_keytabs(TALLOC_CTX *parent_ctx) return NT_STATUS_ACCESS_DENIED; } - /* search for the secret record */ + /* search for the secret record, but only of things we can + * actually update */ ldb_ret = gendb_search(ldb, mem_ctx, NULL, &msgs, attrs, - "objectClass=kerberosSecret"); + "(&(objectClass=kerberosSecret)(|(secret=*)(ntPwdHash=*)))"); if (ldb_ret == -1) { - DEBUG(1, ("Error looking for kerberos type secrets to push into a keytab")); + DEBUG(1, ("Error looking for kerberos type secrets to push into a keytab:: %s", ldb_errstring(ldb))); talloc_free(mem_ctx); return NT_STATUS_INTERNAL_DB_CORRUPTION; } @@ -442,15 +435,13 @@ NTSTATUS cli_credentials_update_all_keytabs(TALLOC_CTX *parent_ctx) if (!NT_STATUS_IS_OK(status)) { DEBUG(1, ("Failed to read secrets for keytab update for %s\n", filter)); - talloc_free(mem_ctx); - return status; + continue; } ret = cli_credentials_update_keytab(creds); if (ret != 0) { DEBUG(1, ("Failed to update keytab for %s\n", filter)); - talloc_free(mem_ctx); - return NT_STATUS_UNSUCCESSFUL; + continue; } } return NT_STATUS_OK; diff --git a/source4/auth/credentials/credentials_krb5.c b/source4/auth/credentials/credentials_krb5.c index 5f40ca1046..29b70d9a53 100644 --- a/source4/auth/credentials/credentials_krb5.c +++ b/source4/auth/credentials/credentials_krb5.c @@ -43,6 +43,20 @@ int cli_credentials_get_krb5_context(struct cli_credentials *cred, return 0; } +/* This needs to be called directly after the cli_credentials_init(), + * otherwise we might have problems with the krb5 context already + * being here. + */ +NTSTATUS cli_credentials_set_krb5_context(struct cli_credentials *cred, + struct smb_krb5_context *smb_krb5_context) +{ + if (!talloc_reference(cred, smb_krb5_context)) { + return NT_STATUS_NO_MEMORY; + } + cred->smb_krb5_context = smb_krb5_context; + return NT_STATUS_OK; +} + int cli_credentials_set_from_ccache(struct cli_credentials *cred, enum credentials_obtained obtained) { diff --git a/source4/auth/kerberos/kerberos_util.c b/source4/auth/kerberos/kerberos_util.c index ad0e18b2e7..776b591ba4 100644 --- a/source4/auth/kerberos/kerberos_util.c +++ b/source4/auth/kerberos/kerberos_util.c @@ -397,9 +397,9 @@ static int create_keytab(TALLOC_CTX *parent_ctx, const struct samr_Password *mach_pwd; mach_pwd = cli_credentials_get_nt_hash(machine_account, mem_ctx); if (!mach_pwd) { - talloc_free(mem_ctx); DEBUG(1, ("create_keytab: Domain trust informaton for account %s not available\n", cli_credentials_get_principal(machine_account, mem_ctx))); + talloc_free(mem_ctx); return EINVAL; } ret = krb5_keyblock_init(smb_krb5_context->krb5_context, @@ -410,6 +410,7 @@ static int create_keytab(TALLOC_CTX *parent_ctx, DEBUG(1, ("create_keytab: krb5_keyblock_init failed: %s\n", smb_get_krb5_error_message(smb_krb5_context->krb5_context, ret, mem_ctx))); + talloc_free(mem_ctx); return ret; } @@ -516,6 +517,7 @@ static krb5_error_code remove_old_entries(TALLOC_CTX *parent_ctx, switch (ret) { case 0: break; + case HEIM_ERR_OPNOTSUPP: case ENOENT: case KRB5_KT_END: /* no point enumerating if there isn't anything here */ diff --git a/source4/auth/kerberos/krb5_init_context.c b/source4/auth/kerberos/krb5_init_context.c index 8e52ac5e3f..830c803524 100644 --- a/source4/auth/kerberos/krb5_init_context.c +++ b/source4/auth/kerberos/krb5_init_context.c @@ -448,6 +448,8 @@ static void smb_krb5_send_and_recv_close_func(krb5_context context, void *data) return ret; } + (*smb_krb5_context)->krb5_context->mem_ctx = *smb_krb5_context; + talloc_steal(parent_ctx, *smb_krb5_context); talloc_free(tmp_ctx); diff --git a/source4/heimdal/lib/hdb/hdb-protos.h b/source4/heimdal/lib/hdb/hdb-protos.h index c221175e41..67e19f7e4a 100644 --- a/source4/heimdal/lib/hdb/hdb-protos.h +++ b/source4/heimdal/lib/hdb/hdb-protos.h @@ -491,6 +491,12 @@ hdb_ldapi_create ( const char */*arg*/); krb5_error_code +hdb_ldb_create ( + krb5_context /*context*/, + HDB ** /*db*/, + const char */*arg*/); + +krb5_error_code hdb_list_builtin ( krb5_context /*context*/, char **/*list*/); diff --git a/source4/heimdal/lib/hdb/hdb.c b/source4/heimdal/lib/hdb/hdb.c index 5631d05332..406a50ecbd 100644 --- a/source4/heimdal/lib/hdb/hdb.c +++ b/source4/heimdal/lib/hdb/hdb.c @@ -55,6 +55,9 @@ static struct hdb_method methods[] = { {"ldap:", hdb_ldap_create}, {"ldapi:", hdb_ldapi_create}, #endif +#ifdef _SAMBA_BUILD_ + {"ldb:", hdb_ldb_create}, +#endif {NULL, NULL} }; @@ -395,6 +398,6 @@ hdb_create(krb5_context context, HDB **db, const char *filename) h = find_dynamic_method (context, filename, &residual); #endif if (h == NULL) - krb5_errx(context, 1, "No database support! (hdb_create)"); + krb5_errx(context, 1, "No database support! (hdb_create(%s))", filename); return (*h->create)(context, db, residual); } diff --git a/source4/heimdal/lib/hdb/keytab.c b/source4/heimdal/lib/hdb/keytab.c new file mode 100644 index 0000000000..21ee2f4274 --- /dev/null +++ b/source4/heimdal/lib/hdb/keytab.c @@ -0,0 +1,276 @@ +/* + * Copyright (c) 1999 - 2002 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "hdb_locl.h" + +/* keytab backend for HDB databases */ + +RCSID("$Id: keytab.c,v 1.8 2005/12/12 12:35:36 lha Exp $"); + +struct hdb_data { + char *dbname; + char *mkey; +}; + +/* + * the format for HDB keytabs is: + * HDB:[database:file:mkey] + */ + +static krb5_error_code +hdb_resolve(krb5_context context, const char *name, krb5_keytab id) +{ + struct hdb_data *d; + const char *db, *mkey; + + d = malloc(sizeof(*d)); + if(d == NULL) { + krb5_set_error_string(context, "malloc: out of memory"); + return ENOMEM; + } + db = name; + mkey = strrchr(name, ':'); + if(mkey == NULL || mkey[1] == '\0') { + if(*name == '\0') + d->dbname = NULL; + else { + d->dbname = strdup(name); + if(d->dbname == NULL) { + free(d); + krb5_set_error_string(context, "malloc: out of memory"); + return ENOMEM; + } + } + d->mkey = NULL; + } else { + if((mkey - db) == 0) { + d->dbname = NULL; + } else { + d->dbname = malloc(mkey - db); + if(d->dbname == NULL) { + free(d); + krb5_set_error_string(context, "malloc: out of memory"); + return ENOMEM; + } + memmove(d->dbname, db, mkey - db); + d->dbname[mkey - db] = '\0'; + } + d->mkey = strdup(mkey + 1); + if(d->mkey == NULL) { + free(d->dbname); + free(d); + krb5_set_error_string(context, "malloc: out of memory"); + return ENOMEM; + } + } + id->data = d; + return 0; +} + +static krb5_error_code +hdb_close(krb5_context context, krb5_keytab id) +{ + struct hdb_data *d = id->data; + + free(d->dbname); + free(d->mkey); + free(d); + return 0; +} + +static krb5_error_code +hdb_get_name(krb5_context context, + krb5_keytab id, + char *name, + size_t namesize) +{ + struct hdb_data *d = id->data; + + snprintf(name, namesize, "%s%s%s", + d->dbname ? d->dbname : "", + (d->dbname || d->mkey) ? ":" : "", + d->mkey ? d->mkey : ""); + return 0; +} + +static void +set_config (krb5_context context, + const krb5_config_binding *binding, + const char **dbname, + const char **mkey) +{ + *dbname = krb5_config_get_string(context, binding, "dbname", NULL); + *mkey = krb5_config_get_string(context, binding, "mkey_file", NULL); +} + +/* + * try to figure out the database (`dbname') and master-key (`mkey') + * that should be used for `principal'. + */ + +static void +find_db (krb5_context context, + const char **dbname, + const char **mkey, + krb5_const_principal principal) +{ + const krb5_config_binding *top_bind = NULL; + const krb5_config_binding *default_binding = NULL; + const krb5_config_binding *db; + krb5_realm *prealm = krb5_princ_realm(context, rk_UNCONST(principal)); + + *dbname = *mkey = NULL; + + while ((db = + krb5_config_get_next(context, + NULL, + &top_bind, + krb5_config_list, + "kdc", + "database", + NULL)) != NULL) { + const char *p; + + p = krb5_config_get_string (context, db, "realm", NULL); + if (p == NULL) { + if(default_binding) { + krb5_warnx(context, "WARNING: more than one realm-less " + "database specification"); + krb5_warnx(context, "WARNING: using the first encountered"); + } else + default_binding = db; + } else if (strcmp (*prealm, p) == 0) { + set_config (context, db, dbname, mkey); + break; + } + } + if (*dbname == NULL && default_binding != NULL) + set_config (context, default_binding, dbname, mkey); + if (*dbname == NULL) + *dbname = HDB_DEFAULT_DB; +} + +/* + * find the keytab entry in `id' for `principal, kvno, enctype' and return + * it in `entry'. return 0 or an error code + */ + +static krb5_error_code +hdb_get_entry(krb5_context context, + krb5_keytab id, + krb5_const_principal principal, + krb5_kvno kvno, + krb5_enctype enctype, + krb5_keytab_entry *entry) +{ + hdb_entry_ex ent; + krb5_error_code ret; + struct hdb_data *d = id->data; + int i; + HDB *db; + const char *dbname = d->dbname; + const char *mkey = d->mkey; + + if (dbname == NULL) + find_db (context, &dbname, &mkey, principal); + + ret = hdb_create (context, &db, dbname); + if (ret) + return ret; + ret = hdb_set_master_keyfile (context, db, mkey); + if (ret) { + (*db->hdb_destroy)(context, db); + return ret; + } + + ret = (*db->hdb_open)(context, db, O_RDONLY, 0); + if (ret) { + (*db->hdb_destroy)(context, db); + return ret; + } + + ret = (*db->hdb_fetch)(context, db, HDB_F_DECRYPT, principal, HDB_ENT_TYPE_SERVER, &ent); + + /* Shutdown the hdb on error */ + if(ret == HDB_ERR_NOENTRY) { + (*db->hdb_close)(context, db); + (*db->hdb_destroy)(context, db); + return KRB5_KT_NOTFOUND; + } else if (ret) { + (*db->hdb_close)(context, db); + (*db->hdb_destroy)(context, db); + return ret; + } + if(kvno && ent.entry.kvno != kvno) { + /* The order here matters, we must free these in this order + * due to hdb-ldb and Samba4's talloc */ + hdb_free_entry(context, &ent); + (*db->hdb_close)(context, db); + (*db->hdb_destroy)(context, db); + return KRB5_KT_NOTFOUND; + } + if(enctype == 0) + if(ent.entry.keys.len > 0) + enctype = ent.entry.keys.val[0].key.keytype; + ret = KRB5_KT_NOTFOUND; + for(i = 0; i < ent.entry.keys.len; i++) { + if(ent.entry.keys.val[i].key.keytype == enctype) { + krb5_copy_principal(context, principal, &entry->principal); + entry->vno = ent.entry.kvno; + krb5_copy_keyblock_contents(context, + &ent.entry.keys.val[i].key, + &entry->keyblock); + ret = 0; + break; + } + } + /* The order here matters, we must free these in this order + * due to hdb-ldb and Samba4's talloc */ + hdb_free_entry(context, &ent); + (*db->hdb_close)(context, db); + (*db->hdb_destroy)(context, db); + return ret; +} + +krb5_kt_ops hdb_kt_ops = { + "HDB", + hdb_resolve, + hdb_get_name, + hdb_close, + hdb_get_entry, + NULL, /* start_seq_get */ + NULL, /* next_entry */ + NULL, /* end_seq_get */ + NULL, /* add */ + NULL /* remove */ +}; diff --git a/source4/heimdal/lib/krb5/krb5.h b/source4/heimdal/lib/krb5/krb5.h index adee4708e6..9814817600 100644 --- a/source4/heimdal/lib/krb5/krb5.h +++ b/source4/heimdal/lib/krb5/krb5.h @@ -451,6 +451,9 @@ typedef struct krb5_context_data { int large_msg_size; krb5_boolean fdns; /* Lookup hostnames to find full name, or send as-is */ struct send_and_recv *send_and_recv; /* Alternate functions for KDC communication */ + void *mem_ctx; /* Some parts of Samba4 need a valid + memory context (under the event + context) to use */ } krb5_context_data; enum { diff --git a/source4/heimdal_build/config.mk b/source4/heimdal_build/config.mk index 0d901258b3..0e65266526 100644 --- a/source4/heimdal_build/config.mk +++ b/source4/heimdal_build/config.mk @@ -23,6 +23,7 @@ OBJ_FILES = \ ../heimdal/lib/hdb/hdb.o \ ../heimdal/lib/hdb/ext.o \ ../heimdal/lib/hdb/keys.o \ + ../heimdal/lib/hdb/keytab.o \ ../heimdal/lib/hdb/mkey.o \ ../heimdal/lib/hdb/ndbm.o \ ../heimdal/lib/hdb/asn1_Event.o \ @@ -40,6 +41,7 @@ OBJ_FILES = \ ../heimdal/lib/hdb/asn1_Salt.o \ ../heimdal/lib/hdb/asn1_hdb_entry.o \ ../heimdal/lib/hdb/hdb_err.o +REQUIRED_SUBSYSTEMS = HDB_LDB NOPROTO = YES # End SUBSYSTEM HEIMDAL_HDB ####################### diff --git a/source4/kdc/config.mk b/source4/kdc/config.mk index f1aef75df5..32f10c93a5 100644 --- a/source4/kdc/config.mk +++ b/source4/kdc/config.mk @@ -6,10 +6,21 @@ NOPROTO = YES OBJ_FILES = \ kdc.o \ - pac-glue.o \ - hdb-ldb.o \ kpasswdd.o REQUIRED_SUBSYSTEMS = \ LIBLDB KERBEROS_LIB HEIMDAL_KDC HEIMDAL_HDB # End SUBSYSTEM KDC ####################### + +####################### +# Start SUBSYSTEM KDC +[SUBSYSTEM::HDB_LDB] +NOPROTO = YES +OBJ_FILES = \ + hdb-ldb.o \ + pac-glue.o +REQUIRED_SUBSYSTEMS = \ + LIBLDB KERBEROS_LIB HEIMDAL_HDB +# End SUBSYSTEM KDC +####################### + diff --git a/source4/kdc/hdb-ldb.c b/source4/kdc/hdb-ldb.c index 43009c1c1b..a155e24e7e 100644 --- a/source4/kdc/hdb-ldb.c +++ b/source4/kdc/hdb-ldb.c @@ -948,8 +948,13 @@ static krb5_error_code LDB_destroy(krb5_context context, HDB *db) return 0; } -NTSTATUS hdb_ldb_create(TALLOC_CTX *mem_ctx, - krb5_context context, struct HDB **db, const char *arg) +/* This interface is to be called by the KDC, which is expecting Samba + * calling conventions. It is also called by a wrapper + * (hdb_ldb_create) from the kpasswdd -> krb5 -> keytab_hdb -> hdb + * code */ + +NTSTATUS kdc_hdb_ldb_create(TALLOC_CTX *mem_ctx, + krb5_context context, struct HDB **db, const char *arg) { NTSTATUS nt_status; struct auth_session_info *session_info; @@ -1008,3 +1013,15 @@ NTSTATUS hdb_ldb_create(TALLOC_CTX *mem_ctx, return NT_STATUS_OK; } + +krb5_error_code hdb_ldb_create(krb5_context context, struct HDB **db, const char *arg) +{ + NTSTATUS nt_status; + /* Disgusting, ugly hack, but it means one less private hook */ + nt_status = kdc_hdb_ldb_create(context->mem_ctx, context, db, arg); + + if (NT_STATUS_IS_OK(nt_status)) { + return 0; + } + return EINVAL; +} diff --git a/source4/kdc/kdc.c b/source4/kdc/kdc.c index 12672bee53..4b958fdce8 100644 --- a/source4/kdc/kdc.c +++ b/source4/kdc/kdc.c @@ -570,13 +570,18 @@ static void kdc_task_init(struct task_server *task) } kdc->config->num_db = 1; - status = hdb_ldb_create(kdc, kdc->smb_krb5_context->krb5_context, - &kdc->config->db[0], NULL); + status = kdc_hdb_ldb_create(kdc, kdc->smb_krb5_context->krb5_context, + &kdc->config->db[0], NULL); if (!NT_STATUS_IS_OK(status)) { task_server_terminate(task, "kdc: hdb_ldb_create (setup KDC database) failed"); return; } + ret = krb5_kt_register(kdc->smb_krb5_context->krb5_context, &hdb_kt_ops); + if(ret) { + task_server_terminate(task, "kdc: failed to register hdb keytab"); + return; + } /* start listening on the configured network interfaces */ status = kdc_startup_interfaces(kdc); if (!NT_STATUS_IS_OK(status)) { diff --git a/source4/kdc/kdc.h b/source4/kdc/kdc.h index 1038c7df95..df6c5889e6 100644 --- a/source4/kdc/kdc.h +++ b/source4/kdc/kdc.h @@ -29,8 +29,8 @@ struct kdc_server; -NTSTATUS hdb_ldb_create(TALLOC_CTX *mem_ctx, - krb5_context context, struct HDB **db, const char *arg); +NTSTATUS kdc_hdb_ldb_create(TALLOC_CTX *mem_ctx, + krb5_context context, struct HDB **db, const char *arg); BOOL kpasswdd_process(struct kdc_server *kdc, TALLOC_CTX *mem_ctx, DATA_BLOB *input, diff --git a/source4/kdc/kpasswdd.c b/source4/kdc/kpasswdd.c index 05aced904d..8e6448435b 100644 --- a/source4/kdc/kpasswdd.c +++ b/source4/kdc/kpasswdd.c @@ -457,7 +457,10 @@ BOOL kpasswdd_process(struct kdc_server *kdc, DEBUG(1, ("Failed to init server credentials\n")); return False; } - + + /* We want the credentials subsystem to use the krb5 context + * we already have, rather than a new context */ + cli_credentials_set_krb5_context(server_credentials, kdc->smb_krb5_context); cli_credentials_set_conf(server_credentials); nt_status = cli_credentials_set_stored_principal(server_credentials, "kadmin/changepw"); if (!NT_STATUS_IS_OK(nt_status)) { diff --git a/source4/setup/secrets.ldif b/source4/setup/secrets.ldif index 43c3f69c9d..8c3c6917ae 100644 --- a/source4/setup/secrets.ldif +++ b/source4/setup/secrets.ldif @@ -38,18 +38,18 @@ msDS-KeyVersionNumber: 1 objectSid: ${DOMAINSID} privateKeytab: secrets.keytab +# A hook from our credentials system into HDB, as we must be on a KDC, +# we can look directly into the database. dn: samAccountName=krbtgt,flatname=${DOMAIN},CN=Principals objectClass: top objectClass: secret objectClass: kerberosSecret flatname: ${DOMAIN} realm: ${REALM} -secret: ${KRBTGTPASS} sAMAccountName: krbtgt whenCreated: ${LDAPTIME} whenChanged: ${LDAPTIME} -msDS-KeyVersionNumber: 1 objectSid: ${DOMAINSID} servicePrincipalName: kadmin/changepw -saltPrincipal: krbtgt@${REALM} -privateKeytab: secrets.keytab +krb5Keytab: HDB:ldb:sam.ldb:/dev/null +#The /dev/null here is a HACK, but it matches the Heimdal format. |