#include "includes.h"
#include "system/kerberos.h"
#include "auth/credentials/credentials.h"
+#include "auth/credentials/credentials_krb5.h"
#include "auth/kerberos/kerberos.h"
#include "auth/kerberos/kerberos_util.h"
#include "auth/kerberos/kerberos_srv_keytab.h"
+#include "librpc/gen_ndr/ndr_gmsa.h"
+#include "dsdb/samdb/samdb.h"
static void keytab_principals_free(krb5_context context,
uint32_t num_principals,
return ret;
}
+NTSTATUS smb_krb5_fill_keytab_gmsa_keys(TALLOC_CTX *mem_ctx,
+ struct smb_krb5_context *smb_krb5_context,
+ krb5_keytab keytab,
+ krb5_principal principal,
+ struct ldb_context *samdb,
+ struct ldb_dn *dn,
+ bool include_previous,
+ const char **error_string)
+{
+ const char *gmsa_attrs[] = {
+ "msDS-ManagedPassword",
+ "msDS-KeyVersionNumber",
+ "sAMAccountName",
+ "msDS-SupportedEncryptionTypes",
+ NULL
+ };
+
+ NTSTATUS status;
+ struct ldb_message *msg;
+ const struct ldb_val *managed_password_blob;
+ const char *managed_pw_utf8;
+ const char *previous_managed_pw_utf8;
+ const char *username;
+ const char *salt_principal;
+ uint32_t kvno = 0;
+ uint32_t supported_enctypes = 0;
+ krb5_context context = smb_krb5_context->krb5_context;
+ struct cli_credentials *cred = NULL;
+ const char *realm = NULL;
+
+ /*
+ * Search for msDS-ManagedPassword (and other attributes to
+ * avoid a race) as this was not in the original search.
+ */
+ int ret;
+
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = dsdb_search_one(samdb,
+ tmp_ctx,
+ &msg,
+ dn,
+ LDB_SCOPE_BASE,
+ gmsa_attrs, 0,
+ "(objectClass=msDS-GroupManagedServiceAccount)");
+
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ /*
+ * Race condition, object has gone, or just wasn't a
+ * gMSA
+ */
+ *error_string = talloc_asprintf(mem_ctx,
+ "Did not find gMSA at %s",
+ ldb_dn_get_linearized(dn));
+ TALLOC_FREE(tmp_ctx);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ if (ret != LDB_SUCCESS) {
+ *error_string = talloc_asprintf(mem_ctx,
+ "Error looking for gMSA at %s: %s",
+ ldb_dn_get_linearized(dn), ldb_errstring(samdb));
+ TALLOC_FREE(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* Extract out passwords */
+ managed_password_blob = ldb_msg_find_ldb_val(msg, "msDS-ManagedPassword");
+
+ if (managed_password_blob == NULL) {
+ /*
+ * No password set on this yet or not readable by this user
+ */
+ *error_string = talloc_asprintf(mem_ctx,
+ "Did not find msDS-ManagedPassword at %s",
+ ldb_dn_get_extended_linearized(mem_ctx, msg->dn, 1));
+ TALLOC_FREE(tmp_ctx);
+ return NT_STATUS_NO_USER_KEYS;
+ }
+
+ cred = cli_credentials_init(tmp_ctx);
+ if (cred == NULL) {
+ *error_string = talloc_asprintf(mem_ctx,
+ "Could not allocate cli_credentials for %s",
+ ldb_dn_get_linearized(msg->dn));
+ TALLOC_FREE(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ realm = smb_krb5_principal_get_realm(tmp_ctx,
+ context,
+ principal);
+ if (realm == NULL) {
+ *error_string = talloc_asprintf(mem_ctx,
+ "Could not allocate copy of realm for %s",
+ ldb_dn_get_linearized(msg->dn));
+ TALLOC_FREE(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ cli_credentials_set_realm(cred, realm, CRED_SPECIFIED);
+
+ username = ldb_msg_find_attr_as_string(msg, "sAMAccountName", NULL);
+ if (username == NULL) {
+ *error_string = talloc_asprintf(mem_ctx,
+ "No sAMAccountName on %s",
+ ldb_dn_get_linearized(msg->dn));
+ TALLOC_FREE(tmp_ctx);
+ return NT_STATUS_INVALID_ACCOUNT_NAME;
+ }
+
+ cli_credentials_set_username(cred, username, CRED_SPECIFIED);
+
+ kvno = ldb_msg_find_attr_as_uint(msg, "msDS-KeyVersionNumber", 0);
+
+ cli_credentials_set_kvno(cred, kvno);
+
+ supported_enctypes = ldb_msg_find_attr_as_uint(msg,
+ "msDS-SupportedEncryptionTypes",
+ ENC_HMAC_SHA1_96_AES256);
+ /*
+ * We trim this down to just the salted AES types, as the
+ * passwords are now wrong for rc4-hmac due to the mapping of
+ * invalid sequences in UTF16_MUNGED -> UTF8 string conversion
+ * within cli_credentials_get_password(). Users using this new
+ * feature won't be using such weak crypto anyway. If
+ * required we could also set the NT Hash as a key directly,
+ * this is just a limitation of smb_krb5_fill_keytab() taking
+ * a simple string as input.
+ */
+ supported_enctypes &= ENC_STRONG_SALTED_TYPES;
+
+ /* Update the keytab */
+
+ status = cli_credentials_set_gmsa_passwords(cred,
+ managed_password_blob,
+ error_string);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ *error_string = talloc_asprintf(mem_ctx,
+ "Could not parse gMSA passwords on %s: %s",
+ ldb_dn_get_linearized(msg->dn),
+ *error_string);
+ TALLOC_FREE(tmp_ctx);
+ return status;
+ }
+
+ managed_pw_utf8 = cli_credentials_get_password(cred);
+
+ previous_managed_pw_utf8 = cli_credentials_get_old_password(cred);
+
+ salt_principal = cli_credentials_get_salt_principal(cred, tmp_ctx);
+ if (salt_principal == NULL) {
+ *error_string = talloc_asprintf(mem_ctx,
+ "Failed to generated salt principal for %s",
+ ldb_dn_get_linearized(msg->dn));
+ TALLOC_FREE(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = smb_krb5_fill_keytab(tmp_ctx,
+ salt_principal,
+ kvno,
+ managed_pw_utf8,
+ previous_managed_pw_utf8,
+ supported_enctypes,
+ 1,
+ &principal,
+ context,
+ keytab,
+ include_previous,
+ error_string);
+ if (ret) {
+ *error_string = talloc_asprintf(mem_ctx,
+ "Failed to add keys from %s to keytab: %s",
+ ldb_dn_get_linearized(msg->dn),
+ *error_string);
+ TALLOC_FREE(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return NT_STATUS_OK;
+}
+
/**
* @brief Update a Kerberos keytab and removes any obsolete keytab entries.
*
#include "auth/kerberos/kerberos.h"
#include "auth/kerberos/kerberos_credentials.h"
#include "auth/kerberos/kerberos_util.h"
+#include "auth/kerberos/kerberos_srv_keytab.h"
#include "kdc/samba_kdc.h"
#include "libnet/libnet_export_keytab.h"
-
#include "kdc/db-glue.h"
#include "kdc/sdb.h"
krb5_data password;
bool keys_exported = false;
krb5_context context = smb_krb5_context->krb5_context;
+ TALLOC_CTX *tmp_ctx = NULL;
code = smb_krb5_kt_open_relative(context,
keytab_name,
for (; code == 0; code = samba_kdc_nextkey(context, db_ctx, &sentry)) {
int i;
bool found_previous = false;
-
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
code = krb5_unparse_name(context,
sentry.principal,
&entry_principal);
}
}
- if (sentry.keys.len == 0) {
- SAFE_FREE(entry_principal);
- sdb_entry_free(&sentry);
-
- continue;
- }
-
- for (i = 0; i < sentry.keys.len; i++) {
- struct sdb_key *s = &(sentry.keys.val[i]);
- krb5_enctype enctype;
-
- enctype = KRB5_KEY_TYPE(&(s->key));
- password.length = KRB5_KEY_LENGTH(&s->key);
- password.data = (char *)KRB5_KEY_DATA(&s->key);
-
- DBG_INFO("smb_krb5_kt_add_entry for enctype=0x%04x\n",
- (int)enctype);
- code = smb_krb5_kt_add_entry(context,
- keytab,
- sentry.kvno,
- entry_principal,
- NULL,
- enctype,
- &password,
- true); /* no_salt */
- if (code != 0) {
- status = NT_STATUS_UNSUCCESSFUL;
- *error_string = smb_get_krb5_error_message(context,
- code,
- mem_ctx);
- DEBUG(0, ("smb_krb5_kt_add_entry failed code=%d, error = %s\n",
- code, *error_string));
+ /*
+ * If this was a gMSA and we did not just read the
+ * keys directly, then generate them
+ */
+ if (sentry.skdc_entry->group_managed_service_account
+ && sentry.keys.len == 0) {
+ struct ldb_dn *dn = sentry.skdc_entry->msg->dn;
+ /*
+ * for error message only, but we are about to
+ * destroy the string name, so write this out
+ * now
+ */
+ const char *extended_dn =
+ ldb_dn_get_extended_linearized(mem_ctx,
+ dn,
+ 1);
+
+ /*
+ * Modify the DN in the entry (not needed by
+ * the KDC code any longer) to be minimal, so
+ * we can search on it over LDAP.
+ */
+ ldb_dn_minimise(dn);
+
+ status = smb_krb5_fill_keytab_gmsa_keys(tmp_ctx,
+ smb_krb5_context,
+ keytab,
+ sentry.principal,
+ db_ctx->samdb,
+ dn,
+ found_previous ? false : true,
+ error_string);
+ if (NT_STATUS_IS_OK(status)) {
+ keys_exported = true;
+ } else if (copy_one_principal) {
+ *error_string = talloc_asprintf(mem_ctx,
+ "Failed to write gMSA password for %s to keytab: %s\n",
+ principal,
+ *error_string);
goto done;
+ } else if (!NT_STATUS_EQUAL(status, NT_STATUS_NO_USER_KEYS)) {
+ *error_string = talloc_asprintf(mem_ctx,
+ "Failed to write gMSA password for %s to keytab: %s\n",
+ extended_dn,
+ *error_string);
+ goto done;
+ }
+ } else {
+ for (i = 0; i < sentry.keys.len; i++) {
+ struct sdb_key *s = &(sentry.keys.val[i]);
+ krb5_enctype enctype;
+
+ enctype = KRB5_KEY_TYPE(&(s->key));
+ password.length = KRB5_KEY_LENGTH(&s->key);
+ password.data = (char *)KRB5_KEY_DATA(&s->key);
+
+ DBG_INFO("smb_krb5_kt_add_entry for enctype=0x%04x\n",
+ (int)enctype);
+ code = smb_krb5_kt_add_entry(context,
+ keytab,
+ sentry.kvno,
+ entry_principal,
+ NULL,
+ enctype,
+ &password,
+ true); /* no_salt */
+ if (code != 0) {
+ status = NT_STATUS_UNSUCCESSFUL;
+ *error_string = smb_get_krb5_error_message(context,
+ code,
+ mem_ctx);
+ DEBUG(0, ("smb_krb5_kt_add_entry failed code=%d, error = %s\n",
+ code, *error_string));
+ goto done;
+ }
+ keys_exported = true;
}
- keys_exported = true;
}
if (copy_one_principal) {
break;
}
+ TALLOC_FREE(tmp_ctx);
SAFE_FREE(entry_principal);
sdb_entry_free(&sentry);
}
}
done:
+ TALLOC_FREE(tmp_ctx);
SAFE_FREE(entry_principal);
sdb_entry_free(&sentry);