summaryrefslogtreecommitdiffstats
path: root/source4/auth/kerberos/srv_keytab.c
diff options
context:
space:
mode:
Diffstat (limited to 'source4/auth/kerberos/srv_keytab.c')
-rw-r--r--source4/auth/kerberos/srv_keytab.c407
1 files changed, 407 insertions, 0 deletions
diff --git a/source4/auth/kerberos/srv_keytab.c b/source4/auth/kerberos/srv_keytab.c
new file mode 100644
index 0000000..8eaa150
--- /dev/null
+++ b/source4/auth/kerberos/srv_keytab.c
@@ -0,0 +1,407 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Kerberos utility functions
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file srv_keytab.c
+ *
+ * @brief Kerberos keytab utility functions
+ *
+ */
+
+#include "includes.h"
+#include "system/kerberos.h"
+#include "auth/credentials/credentials.h"
+#include "auth/kerberos/kerberos.h"
+#include "auth/kerberos/kerberos_util.h"
+#include "auth/kerberos/kerberos_srv_keytab.h"
+
+static void keytab_principals_free(krb5_context context,
+ uint32_t num_principals,
+ krb5_principal *set)
+{
+ uint32_t i;
+
+ for (i = 0; i < num_principals; i++) {
+ krb5_free_principal(context, set[i]);
+ }
+}
+
+static krb5_error_code keytab_add_keys(TALLOC_CTX *parent_ctx,
+ uint32_t num_principals,
+ krb5_principal *principals,
+ krb5_principal salt_princ,
+ int kvno,
+ const char *password_s,
+ krb5_context context,
+ krb5_enctype *enctypes,
+ krb5_keytab keytab,
+ const char **error_string)
+{
+ unsigned int i, p;
+ krb5_error_code ret;
+ krb5_data password;
+ char *unparsed;
+
+ password.data = discard_const_p(char, password_s);
+ password.length = strlen(password_s);
+
+ for (i = 0; enctypes[i]; i++) {
+ krb5_keytab_entry entry;
+
+ ZERO_STRUCT(entry);
+
+ ret = smb_krb5_create_key_from_string(context,
+ salt_princ,
+ NULL,
+ &password,
+ enctypes[i],
+ KRB5_KT_KEY(&entry));
+ if (ret != 0) {
+ *error_string = talloc_strdup(parent_ctx,
+ "Failed to create key from string");
+ return ret;
+ }
+
+ entry.vno = kvno;
+
+ for (p = 0; p < num_principals; p++) {
+ unparsed = NULL;
+ entry.principal = principals[p];
+ ret = krb5_kt_add_entry(context, keytab, &entry);
+ if (ret != 0) {
+ char *k5_error_string =
+ smb_get_krb5_error_message(context,
+ ret, NULL);
+ krb5_unparse_name(context,
+ principals[p], &unparsed);
+ *error_string = talloc_asprintf(parent_ctx,
+ "Failed to add enctype %d entry for "
+ "%s(kvno %d) to keytab: %s\n",
+ (int)enctypes[i], unparsed,
+ kvno, k5_error_string);
+
+ free(unparsed);
+ talloc_free(k5_error_string);
+ krb5_free_keyblock_contents(context,
+ KRB5_KT_KEY(&entry));
+ return ret;
+ }
+
+ DEBUG(5, ("Added key (kvno %d) to keytab (enctype %d)\n",
+ kvno, (int)enctypes[i]));
+ }
+ krb5_free_keyblock_contents(context, KRB5_KT_KEY(&entry));
+ }
+ return 0;
+}
+
+static krb5_error_code create_keytab(TALLOC_CTX *parent_ctx,
+ const char *samAccountName,
+ const char *realm,
+ const char *saltPrincipal,
+ int kvno,
+ const char *new_secret,
+ const char *old_secret,
+ uint32_t supp_enctypes,
+ uint32_t num_principals,
+ krb5_principal *principals,
+ krb5_context context,
+ krb5_keytab keytab,
+ bool add_old,
+ const char **perror_string)
+{
+ krb5_error_code ret;
+ krb5_principal salt_princ = NULL;
+ krb5_enctype *enctypes;
+ TALLOC_CTX *mem_ctx;
+ const char *error_string = NULL;
+
+ if (!new_secret) {
+ /* There is no password here, so nothing to do */
+ return 0;
+ }
+
+ mem_ctx = talloc_new(parent_ctx);
+ if (!mem_ctx) {
+ *perror_string = talloc_strdup(parent_ctx,
+ "unable to allocate tmp_ctx for create_keytab");
+ return ENOMEM;
+ }
+
+ /* The salt used to generate these entries may be different however,
+ * fetch that */
+ ret = krb5_parse_name(context, saltPrincipal, &salt_princ);
+ if (ret) {
+ *perror_string = smb_get_krb5_error_message(context,
+ ret,
+ parent_ctx);
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ ret = ms_suptypes_to_ietf_enctypes(mem_ctx, supp_enctypes, &enctypes);
+ if (ret) {
+ *perror_string = talloc_asprintf(parent_ctx,
+ "create_keytab: generating list of "
+ "encryption types failed (%s)\n",
+ smb_get_krb5_error_message(context,
+ ret, mem_ctx));
+ goto done;
+ }
+
+ ret = keytab_add_keys(mem_ctx,
+ num_principals,
+ principals,
+ salt_princ, kvno, new_secret,
+ context, enctypes, keytab, &error_string);
+ if (ret) {
+ *perror_string = talloc_steal(parent_ctx, error_string);
+ goto done;
+ }
+
+ if (old_secret && add_old && kvno != 0) {
+ ret = keytab_add_keys(mem_ctx,
+ num_principals,
+ principals,
+ salt_princ, kvno - 1, old_secret,
+ context, enctypes, keytab, &error_string);
+ if (ret) {
+ *perror_string = talloc_steal(parent_ctx, error_string);
+ }
+ }
+
+done:
+ krb5_free_principal(context, salt_princ);
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+/**
+ * @brief Update a Kerberos keytab and removes any obsolete keytab entries.
+ *
+ * If the keytab does not exist, this function will create one.
+ *
+ * @param[in] parent_ctx Talloc memory context
+ * @param[in] context Kerberos context
+ * @param[in] keytab_name Keytab to open
+ * @param[in] samAccountName User account to update
+ * @param[in] realm Kerberos realm
+ * @param[in] SPNs Service principal names to update
+ * @param[in] num_SPNs Length of SPNs
+ * @param[in] saltPrincipal Salt used for AES encryption.
+ * Required, unless delete_all_kvno is set.
+ * @param[in] old_secret Old password
+ * @param[in] new_secret New password
+ * @param[in] kvno Current key version number
+ * @param[in] supp_enctypes msDS-SupportedEncryptionTypes bit-field
+ * @param[in] delete_all_kvno Removes all obsolete entries, without
+ * recreating the keytab.
+ * @param[out] _keytab If supplied, returns the keytab
+ * @param[out] perror_string Error string on failure
+ *
+ * @return 0 on success, errno on failure
+ */
+krb5_error_code smb_krb5_update_keytab(TALLOC_CTX *parent_ctx,
+ krb5_context context,
+ const char *keytab_name,
+ const char *samAccountName,
+ const char *realm,
+ const char **SPNs,
+ int num_SPNs,
+ const char *saltPrincipal,
+ const char *new_secret,
+ const char *old_secret,
+ int kvno,
+ uint32_t supp_enctypes,
+ bool delete_all_kvno,
+ krb5_keytab *_keytab,
+ const char **perror_string)
+{
+ krb5_keytab keytab = NULL;
+ krb5_error_code ret;
+ bool found_previous = false;
+ TALLOC_CTX *tmp_ctx = NULL;
+ krb5_principal *principals = NULL;
+ uint32_t num_principals = 0;
+ char *upper_realm;
+ const char *error_string = NULL;
+
+ if (keytab_name == NULL) {
+ return ENOENT;
+ }
+
+ ret = krb5_kt_resolve(context, keytab_name, &keytab);
+ if (ret) {
+ *perror_string = smb_get_krb5_error_message(context,
+ ret, parent_ctx);
+ return ret;
+ }
+
+ DEBUG(5, ("Opened keytab %s\n", keytab_name));
+
+ tmp_ctx = talloc_new(parent_ctx);
+ if (!tmp_ctx) {
+ *perror_string = talloc_strdup(parent_ctx,
+ "Failed to allocate memory context");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ upper_realm = strupper_talloc(tmp_ctx, realm);
+ if (upper_realm == NULL) {
+ *perror_string = talloc_strdup(parent_ctx,
+ "Cannot allocate memory to upper case realm");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = smb_krb5_create_principals_array(tmp_ctx,
+ context,
+ samAccountName,
+ upper_realm,
+ num_SPNs,
+ SPNs,
+ &num_principals,
+ &principals,
+ &error_string);
+ if (ret != 0) {
+ *perror_string = talloc_asprintf(parent_ctx,
+ "Failed to load principals from ldb message: %s\n",
+ error_string);
+ goto done;
+ }
+
+ ret = smb_krb5_remove_obsolete_keytab_entries(tmp_ctx,
+ context,
+ keytab,
+ num_principals,
+ principals,
+ kvno,
+ &found_previous,
+ &error_string);
+ if (ret != 0) {
+ *perror_string = talloc_asprintf(parent_ctx,
+ "Failed to remove old principals from keytab: %s\n",
+ error_string);
+ goto done;
+ }
+
+ if (!delete_all_kvno) {
+ /* Create a new keytab. If during the cleanout we found
+ * entries for kvno -1, then don't try and duplicate them.
+ * Otherwise, add kvno, and kvno -1 */
+ if (saltPrincipal == NULL) {
+ *perror_string = talloc_strdup(parent_ctx,
+ "No saltPrincipal provided");
+ ret = EINVAL;
+ goto done;
+ }
+
+ ret = create_keytab(tmp_ctx,
+ samAccountName, upper_realm, saltPrincipal,
+ kvno, new_secret, old_secret,
+ supp_enctypes,
+ num_principals,
+ principals,
+ context, keytab,
+ found_previous ? false : true,
+ &error_string);
+ if (ret) {
+ *perror_string = talloc_steal(parent_ctx, error_string);
+ }
+ }
+
+ if (ret == 0 && _keytab != NULL) {
+ /* caller wants the keytab handle back */
+ *_keytab = keytab;
+ }
+
+done:
+ keytab_principals_free(context, num_principals, principals);
+ if (ret != 0 || _keytab == NULL) {
+ krb5_kt_close(context, keytab);
+ }
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+/**
+ * @brief Wrapper around smb_krb5_update_keytab() for creating an in-memory keytab
+ *
+ * @param[in] parent_ctx Talloc memory context
+ * @param[in] context Kerberos context
+ * @param[in] new_secret New password
+ * @param[in] samAccountName User account to update
+ * @param[in] realm Kerberos realm
+ * @param[in] salt_principal Salt used for AES encryption.
+ * Required, unless delete_all_kvno is set.
+ * @param[in] kvno Current key version number
+ * @param[out] keytab If supplied, returns the keytab
+ * @param[out] keytab_name Returns the created keytab name
+ *
+ * @return 0 on success, errno on failure
+ */
+krb5_error_code smb_krb5_create_memory_keytab(TALLOC_CTX *parent_ctx,
+ krb5_context context,
+ const char *new_secret,
+ const char *samAccountName,
+ const char *realm,
+ const char *salt_principal,
+ int kvno,
+ krb5_keytab *keytab,
+ const char **keytab_name)
+{
+ krb5_error_code ret;
+ TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
+ const char *rand_string;
+ const char *error_string = NULL;
+ if (!mem_ctx) {
+ return ENOMEM;
+ }
+
+ rand_string = generate_random_str(mem_ctx, 16);
+ if (!rand_string) {
+ talloc_free(mem_ctx);
+ return ENOMEM;
+ }
+
+ *keytab_name = talloc_asprintf(mem_ctx, "MEMORY:%s", rand_string);
+ if (*keytab_name == NULL) {
+ talloc_free(mem_ctx);
+ return ENOMEM;
+ }
+
+ ret = smb_krb5_update_keytab(mem_ctx, context,
+ *keytab_name, samAccountName, realm,
+ NULL, 0, salt_principal, new_secret, NULL,
+ kvno, ENC_ALL_TYPES,
+ false, keytab, &error_string);
+ if (ret == 0) {
+ talloc_steal(parent_ctx, *keytab_name);
+ } else {
+ DEBUG(0, ("Failed to create in-memory keytab: %s\n",
+ error_string));
+ *keytab_name = NULL;
+ }
+ talloc_free(mem_ctx);
+ return ret;
+}