summaryrefslogtreecommitdiff
path: root/source4/auth/kerberos/kerberos_util.c
diff options
context:
space:
mode:
authorAndrew Bartlett <abartlet@samba.org>2005-12-01 05:09:28 +0000
committerGerald (Jerry) Carter <jerry@samba.org>2007-10-10 13:46:56 -0500
commitb2c98db5072f03b969911bb908d9a4ad3f40105b (patch)
tree7382154bf0efb920c5c7158eb398ae0ce7acb5a5 /source4/auth/kerberos/kerberos_util.c
parent759bda2962fbde173a750fb8c9ce0a6d5f9e0f23 (diff)
downloadsamba-b2c98db5072f03b969911bb908d9a4ad3f40105b.tar.gz
samba-b2c98db5072f03b969911bb908d9a4ad3f40105b.tar.bz2
samba-b2c98db5072f03b969911bb908d9a4ad3f40105b.zip
r11993: As well as making an in-MEMORY keytab, allow a file-based keytab to be updated.
This allows a new password to be written in, and old entries removed (we keep kvno and kvno-1). Clean up the code a lot, and add comments on what it is doing... Andrew Bartlett (This used to be commit 0a911baabad60a43741269d29a96fdd74e54331a)
Diffstat (limited to 'source4/auth/kerberos/kerberos_util.c')
-rw-r--r--source4/auth/kerberos/kerberos_util.c419
1 files changed, 321 insertions, 98 deletions
diff --git a/source4/auth/kerberos/kerberos_util.c b/source4/auth/kerberos/kerberos_util.c
index b9f36b16e5..47476aa1b6 100644
--- a/source4/auth/kerberos/kerberos_util.c
+++ b/source4/auth/kerberos/kerberos_util.c
@@ -96,6 +96,11 @@ krb5_error_code salt_principal_from_credentials(TALLOC_CTX *parent_ctx,
return ret;
}
+/* Obtain the principal set on this context. Requires a
+ * smb_krb5_context because we are doing krb5 principal parsing with
+ * the library routines. The returned princ is placed in the talloc
+ * system by means of a destructor (do *not* free). */
+
krb5_error_code principal_from_credentials(TALLOC_CTX *parent_ctx,
struct cli_credentials *credentials,
struct smb_krb5_context *smb_krb5_context,
@@ -110,6 +115,7 @@ krb5_error_code principal_from_credentials(TALLOC_CTX *parent_ctx,
princ_string = cli_credentials_get_principal(credentials, mem_ctx);
+ /* A NULL here has meaning, as the gssapi server case will then use the principal from the client */
if (!princ_string) {
talloc_free(mem_ctx);
princ = NULL;
@@ -120,6 +126,8 @@ krb5_error_code principal_from_credentials(TALLOC_CTX *parent_ctx,
princ_string, princ);
if (ret == 0) {
+ /* This song-and-dance effectivly puts the principal
+ * into talloc, so we can't loose it. */
mem_ctx->smb_krb5_context = talloc_reference(mem_ctx, smb_krb5_context);
mem_ctx->principal = *princ;
talloc_set_destructor(mem_ctx, free_principal);
@@ -218,62 +226,142 @@ static int free_keytab(void *ptr) {
return 0;
}
- int create_memory_keytab(TALLOC_CTX *parent_ctx,
- struct cli_credentials *machine_account,
- struct smb_krb5_context *smb_krb5_context,
- struct keytab_container **keytab_container)
+struct enctypes_container {
+ struct smb_krb5_context *smb_krb5_context;
+ krb5_enctype *enctypes;
+};
+
+static int free_enctypes(void *ptr) {
+ struct enctypes_container *etc = ptr;
+ free_kerberos_etypes(etc->smb_krb5_context->krb5_context, etc->enctypes);
+ return 0;
+}
+
+static krb5_error_code keytab_add_keys(TALLOC_CTX *parent_ctx,
+ const char *princ_string,
+ krb5_principal princ,
+ krb5_principal salt_princ,
+ int kvno,
+ const char *password_s,
+ struct smb_krb5_context *smb_krb5_context,
+ krb5_keytab keytab)
{
+ int i;
krb5_error_code ret;
- const char *password_s;
- char *old_secret;
- krb5_data password;
- int i, kvno;
krb5_enctype *enctypes;
- krb5_principal salt_princ;
- krb5_principal princ;
- krb5_keytab keytab;
char *enctype_string = NULL;
-
+ struct enctypes_container *etc;
+ krb5_data password;
TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
if (!mem_ctx) {
return ENOMEM;
}
-
- *keytab_container = talloc(mem_ctx, struct keytab_container);
- ret = krb5_kt_resolve(smb_krb5_context->krb5_context, "MEMORY:", &keytab);
- if (ret) {
- DEBUG(1,("failed to generate a new krb5 keytab: %s\n",
+ etc = talloc(mem_ctx, struct enctypes_container);
+ if (!etc) {
+ talloc_free(mem_ctx);
+ return ENOMEM;
+ }
+ ret = get_kerberos_allowed_etypes(smb_krb5_context->krb5_context,
+ &enctypes);
+ if (ret != 0) {
+ DEBUG(1,("keytab_add_keys: getting encrption types failed (%s)\n",
error_message(ret)));
talloc_free(mem_ctx);
return ret;
}
- (*keytab_container)->smb_krb5_context = talloc_reference(*keytab_container, smb_krb5_context);
- (*keytab_container)->keytab = keytab;
+ etc->smb_krb5_context = talloc_reference(etc, smb_krb5_context);
+ etc->enctypes = enctypes;
- talloc_set_destructor(*keytab_container, free_keytab);
+ talloc_set_destructor(etc, free_enctypes);
- ret = salt_principal_from_credentials(mem_ctx, machine_account,
- smb_krb5_context,
- &salt_princ);
+ password.data = discard_const_p(char *, password_s);
+ password.length = strlen(password_s);
+
+ for (i=0; enctypes[i]; i++) {
+ krb5_keytab_entry entry;
+ ret = create_kerberos_key_from_string(smb_krb5_context->krb5_context,
+ salt_princ, &password, &entry.keyblock, enctypes[i]);
+ if (ret) {
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ entry.principal = princ;
+ entry.vno = kvno;
+ ret = krb5_kt_add_entry(smb_krb5_context->krb5_context, keytab, &entry);
+ if (ret != 0) {
+ DEBUG(1, ("Failed to add entry for %s(kvno %d) to keytab: %s",
+ princ_string,
+ kvno,
+ smb_get_krb5_error_message(smb_krb5_context->krb5_context,
+ ret, mem_ctx)));
+ talloc_free(mem_ctx);
+ krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
+ return ret;
+ }
+
+ enctype_string = NULL;
+ krb5_keytype_to_string(smb_krb5_context->krb5_context, enctypes[i], &enctype_string);
+ DEBUG(5, ("Added %s(kvno %d) to keytab (%s)\n",
+ princ_string, kvno,
+ enctype_string));
+ free(enctype_string);
+
+ krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
+ }
+ talloc_free(mem_ctx);
+ return 0;
+}
+
+static int create_keytab(TALLOC_CTX *parent_ctx,
+ struct cli_credentials *machine_account,
+ struct smb_krb5_context *smb_krb5_context,
+ struct keytab_container *keytab_container,
+ BOOL add_old)
+{
+ krb5_error_code ret;
+ const char *password_s;
+ char *enctype_string;
+ const char *old_secret;
+ int kvno;
+ krb5_principal salt_princ;
+ krb5_principal princ;
+ krb5_keytab keytab;
+ const char *princ_string;
+
+ TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
+ if (!mem_ctx) {
+ return ENOMEM;
+ }
+
+ keytab = keytab_container->keytab;
+
+ princ_string = cli_credentials_get_principal(machine_account, mem_ctx);
+ /* Get the principal we will store the new keytab entries under */
+ ret = principal_from_credentials(mem_ctx, machine_account, smb_krb5_context, &princ);
if (ret) {
- DEBUG(1,("create_memory_keytab: makeing salt principal failed (%s)\n",
+ DEBUG(1,("create_keytab: makeing krb5 principal failed (%s)\n",
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
ret, mem_ctx)));
talloc_free(mem_ctx);
return ret;
}
- ret = principal_from_credentials(mem_ctx, machine_account, smb_krb5_context, &princ);
+ /* The salt used to generate these entries may be different however, fetch that */
+ ret = salt_principal_from_credentials(mem_ctx, machine_account,
+ smb_krb5_context,
+ &salt_princ);
if (ret) {
- DEBUG(1,("create_memory_keytab: makeing krb5 principal failed (%s)\n",
+ DEBUG(1,("create_keytab: makeing salt principal failed (%s)\n",
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
ret, mem_ctx)));
talloc_free(mem_ctx);
return ret;
}
+ /* Finally, do the dance to get the password to put in the entry */
password_s = cli_credentials_get_password(machine_account);
if (!password_s) {
/* If we don't have the plaintext password, try for
@@ -284,7 +372,7 @@ static int free_keytab(void *ptr) {
mach_pwd = cli_credentials_get_nt_hash(machine_account, mem_ctx);
if (!mach_pwd) {
talloc_free(mem_ctx);
- DEBUG(1, ("create_memory_keytab: Domain trust informaton for account %s not available\n",
+ DEBUG(1, ("create_keytab: Domain trust informaton for account %s not available\n",
cli_credentials_get_principal(machine_account, mem_ctx)));
return EINVAL;
}
@@ -293,7 +381,7 @@ static int free_keytab(void *ptr) {
mach_pwd->hash, sizeof(mach_pwd->hash),
&entry.keyblock);
if (ret) {
- DEBUG(1, ("create_memory_keytab: krb5_keyblock_init failed: %s\n",
+ DEBUG(1, ("create_keytab: krb5_keyblock_init failed: %s\n",
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
ret, mem_ctx)));
return ret;
@@ -321,103 +409,238 @@ static int free_keytab(void *ptr) {
krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
- talloc_steal(parent_ctx, *keytab_container);
+ /* Can't go any further, we only have this one key */
talloc_free(mem_ctx);
return 0;
}
-
+
+ kvno = cli_credentials_get_kvno(machine_account);
/* good, we actually have the real plaintext */
+ ret = keytab_add_keys(mem_ctx, princ_string, princ, salt_princ,
+ kvno, password_s, smb_krb5_context, keytab);
+ if (!ret) {
+ talloc_free(mem_ctx);
+ return ret;
+ }
- ret = get_kerberos_allowed_etypes(smb_krb5_context->krb5_context,
- &enctypes);
+ if (!add_old || kvno == 0) {
+ talloc_free(mem_ctx);
+ return 0;
+ }
+
+ old_secret = cli_credentials_get_old_password(machine_account);
+ if (!old_secret) {
+ talloc_free(mem_ctx);
+ return 0;
+ }
+
+ ret = keytab_add_keys(mem_ctx, princ_string, princ, salt_princ,
+ kvno - 1, old_secret, smb_krb5_context, keytab);
+ if (!ret) {
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ talloc_free(mem_ctx);
+ return 0;
+}
+
+
+/*
+ * Walk the keytab, looking for entries of this principal name, with KVNO other than current kvno -1.
+ *
+ * These entries are now stale, we only keep the current, and previous entries around.
+ *
+ * Inspired by the code in Samba3 for 'use kerberos keytab'.
+ *
+ */
+
+static krb5_error_code remove_old_entries(TALLOC_CTX *parent_ctx,
+ struct cli_credentials *machine_account,
+ struct smb_krb5_context *smb_krb5_context,
+ krb5_keytab keytab, BOOL *found_previous)
+{
+ krb5_error_code ret, ret2;
+ krb5_kt_cursor cursor;
+ krb5_principal princ;
+ int kvno;
+ TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
+ const char *princ_string;
+ if (!mem_ctx) {
+ return ENOMEM;
+ }
+
+ *found_previous = False;
+ princ_string = cli_credentials_get_principal(machine_account, mem_ctx);
+
+ /* Get the principal we will store the new keytab entries under */
+ ret = principal_from_credentials(mem_ctx, machine_account, smb_krb5_context, &princ);
if (ret) {
- DEBUG(1,("create_memory_keytab: getting encrption types failed (%s)\n",
- error_message(ret)));
+ DEBUG(1,("update_keytab: makeing krb5 principal failed (%s)\n",
+ smb_get_krb5_error_message(smb_krb5_context->krb5_context,
+ ret, mem_ctx)));
talloc_free(mem_ctx);
return ret;
}
- password.data = discard_const_p(char *, password_s);
- password.length = strlen(password_s);
kvno = cli_credentials_get_kvno(machine_account);
- for (i=0; enctypes[i]; i++) {
+ /* for each entry in the keytab */
+ ret = krb5_kt_start_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);
+ switch (ret) {
+ case 0:
+ break;
+ case ENOENT:
+ case KRB5_KT_END:
+ /* no point enumerating if there isn't anything here */
+ talloc_free(mem_ctx);
+ return 0;
+ default:
+ DEBUG(1,("failed to open keytab for read of old entries: %s\n",
+ smb_get_krb5_error_message(smb_krb5_context->krb5_context,
+ ret, mem_ctx)));
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ while (!ret) {
krb5_keytab_entry entry;
- ret = create_kerberos_key_from_string(smb_krb5_context->krb5_context,
- salt_princ, &password, &entry.keyblock, enctypes[i]);
+ ret = krb5_kt_next_entry(smb_krb5_context->krb5_context, keytab, &entry, &cursor);
if (ret) {
- talloc_free(mem_ctx);
- return ret;
+ break;
}
-
- entry.principal = princ;
- entry.vno = kvno;
- ret = krb5_kt_add_entry(smb_krb5_context->krb5_context, keytab, &entry);
- if (ret) {
- DEBUG(1, ("Failed to add entry for %s to keytab: %s",
- cli_credentials_get_principal(machine_account, mem_ctx),
- smb_get_krb5_error_message(smb_krb5_context->krb5_context,
- ret, mem_ctx)));
- talloc_free(mem_ctx);
- krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
- return ret;
+ /* if it matches our principal */
+ if (!krb5_kt_compare(smb_krb5_context->krb5_context, &entry, princ, 0, 0)) {
+ /* Free the entry, it wasn't the one we were looking for anyway */
+ krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
+ continue;
}
- enctype_string = NULL;
- krb5_keytype_to_string(smb_krb5_context->krb5_context, enctypes[i], &enctype_string);
- DEBUG(5, ("Added %s(kvno %d) to keytab (%s)\n",
- cli_credentials_get_principal(machine_account, mem_ctx),
- cli_credentials_get_kvno(machine_account),
- enctype_string));
- free(enctype_string);
+ /* delete it, if it is not kvno -1 */
+ if (entry.vno != (kvno - 1 )) {
+ /* Release the enumeration. We are going to
+ * have to start this from the top again,
+ * because deletes during enumeration may not
+ * always be consistant.
+ *
+ * Also, the enumeration locks the keytab
+ */
- krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
- }
-
- old_secret = cli_credentials_get_old_password(machine_account);
- if (kvno != 0 && old_secret) {
- password.data = discard_const_p(char *, old_secret);
- password.length = strlen(old_secret);
-
- for (i=0; enctypes[i]; i++) {
- krb5_keytab_entry entry;
- ret = create_kerberos_key_from_string(smb_krb5_context->krb5_context,
- salt_princ, &password, &entry.keyblock, enctypes[i]);
- if (ret) {
+ krb5_kt_end_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);
+
+ ret = krb5_kt_remove_entry(smb_krb5_context->krb5_context, keytab, &entry);
+ krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
+
+ /* Deleted: Restart from the top */
+ ret2 = krb5_kt_start_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);
+ if (ret2) {
+ krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
+ DEBUG(1,("failed to restart enumeration of keytab: %s\n",
+ smb_get_krb5_error_message(smb_krb5_context->krb5_context,
+ ret, mem_ctx)));
+
talloc_free(mem_ctx);
- return ret;
+ return ret2;
}
-
- entry.principal = princ;
- entry.vno = kvno - 1;
- ret = krb5_kt_add_entry(smb_krb5_context->krb5_context, keytab, &entry);
+
if (ret) {
- DEBUG(1, ("Failed to add 'old password' entry for %s to keytab: %s",
- cli_credentials_get_principal(machine_account, mem_ctx),
- smb_get_krb5_error_message(smb_krb5_context->krb5_context,
- ret, mem_ctx)));
- talloc_free(mem_ctx);
- krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
- return ret;
+ break;
}
- enctype_string = NULL;
- krb5_keytype_to_string(smb_krb5_context->krb5_context, enctypes[i], &enctype_string);
- DEBUG(5, ("Added %s(kvno %d) to keytab (%s)\n",
- cli_credentials_get_principal(machine_account, mem_ctx),
- cli_credentials_get_kvno(machine_account),
- enctype_string));
- free(enctype_string);
-
- krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
+ } else {
+ *found_previous = True;
}
+
+ /* Free the entry, we don't need it any more */
+ krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
+
+
}
+ krb5_kt_end_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);
+
+ switch (ret) {
+ case 0:
+ break;
+ case ENOENT:
+ case KRB5_KT_END:
+ ret = 0;
+ break;
+ default:
+ DEBUG(1,("failed in deleting old entries for principal: %s: %s\n",
+ princ_string,
+ smb_get_krb5_error_message(smb_krb5_context->krb5_context,
+ ret, mem_ctx)));
+ }
+ talloc_free(mem_ctx);
+ return ret;
+}
- free_kerberos_etypes(smb_krb5_context->krb5_context, enctypes);
-
- talloc_steal(parent_ctx, *keytab_container);
+int update_keytab(TALLOC_CTX *parent_ctx,
+ struct cli_credentials *machine_account,
+ struct smb_krb5_context *smb_krb5_context,
+ struct keytab_container *keytab_container)
+{
+ krb5_error_code ret;
+ BOOL found_previous;
+ TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
+ if (!mem_ctx) {
+ return ENOMEM;
+ }
+
+ ret = remove_old_entries(mem_ctx, machine_account,
+ smb_krb5_context, keytab_container->keytab, &found_previous);
+ if (ret != 0) {
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ /* Create a new keytab. If during the cleanout we found
+ * entires for kvno -1, then don't try and duplicate them.
+ * Otherwise, add kvno, and kvno -1 */
+
+ ret = create_keytab(mem_ctx, machine_account, smb_krb5_context,
+ keytab_container,
+ found_previous ? False : True);
talloc_free(mem_ctx);
- return 0;
+ return ret;
}
+int create_memory_keytab(TALLOC_CTX *parent_ctx,
+ struct cli_credentials *machine_account,
+ struct smb_krb5_context *smb_krb5_context,
+ struct keytab_container **keytab_container)
+{
+ krb5_error_code ret;
+ TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
+ const char *keytab_name = "MEMORY:";
+ krb5_keytab keytab;
+ if (!mem_ctx) {
+ return ENOMEM;
+ }
+
+ *keytab_container = talloc(mem_ctx, struct keytab_container);
+
+ /* Find the keytab */
+ ret = krb5_kt_resolve(smb_krb5_context->krb5_context, keytab_name, &keytab);
+ if (ret) {
+ DEBUG(1,("failed to resolve keytab: %s: %s\n",
+ keytab_name,
+ smb_get_krb5_error_message(smb_krb5_context->krb5_context,
+ ret, mem_ctx)));
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ (*keytab_container)->smb_krb5_context = talloc_reference(*keytab_container, smb_krb5_context);
+ (*keytab_container)->keytab = keytab;
+
+ talloc_set_destructor(*keytab_container, free_keytab);
+
+ ret = update_keytab(mem_ctx, machine_account, smb_krb5_context, *keytab_container);
+ if (ret == 0) {
+ talloc_steal(parent_ctx, *keytab_container);
+ }
+ talloc_free(mem_ctx);
+ return ret;
+}