s3-lib: Do not set an empty string in split_domain_user()
[samba.git] / source3 / libnet / libnet_keytab.c
index cec39273e3b2323545a78a7e7940625932a2776b..9aefac1f00f4ee39fecbfdf89944aea4edb899f9 100644 (file)
@@ -3,6 +3,7 @@
    dump the remote SAM using rpc samsync operations
 
    Copyright (C) Guenther Deschner 2008.
+   Copyright (C) Michael Adam 2008
 
    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
@@ -19,7 +20,9 @@
 */
 
 #include "includes.h"
-#include "libnet/libnet.h"
+#include "smb_krb5.h"
+#include "ads.h"
+#include "libnet/libnet_keytab.h"
 
 #ifdef HAVE_KRB5
 
@@ -63,7 +66,7 @@ krb5_error_code libnet_keytab_init(TALLOC_CTX *mem_ctx,
 
        struct libnet_keytab_context *r;
 
-       r = TALLOC_ZERO_P(mem_ctx, struct libnet_keytab_context);
+       r = talloc_zero(mem_ctx, struct libnet_keytab_context);
        if (!r) {
                return ENOMEM;
        }
@@ -78,7 +81,10 @@ krb5_error_code libnet_keytab_init(TALLOC_CTX *mem_ctx,
                return ret;
        }
 
-       ret = smb_krb5_open_keytab(context, keytab_name, true, &keytab);
+       ret = smb_krb5_kt_open_relative(context,
+                                       keytab_name,
+                                       true, /* write_access */
+                                       &keytab);
        if (ret) {
                DEBUG(1,("keytab_init: smb_krb5_open_keytab failed (%s)\n",
                        error_message(ret)));
@@ -86,7 +92,7 @@ krb5_error_code libnet_keytab_init(TALLOC_CTX *mem_ctx,
                return ret;
        }
 
-       ret = smb_krb5_keytab_name(mem_ctx, context, keytab, &keytab_string);
+       ret = smb_krb5_kt_get_name(mem_ctx, context, keytab, &keytab_string);
        if (ret) {
                krb5_kt_close(context, keytab);
                krb5_free_context(context);
@@ -96,6 +102,7 @@ krb5_error_code libnet_keytab_init(TALLOC_CTX *mem_ctx,
        r->context = context;
        r->keytab = keytab;
        r->keytab_name = keytab_string;
+       r->clean_old_entries = false;
 
        *ctx = r;
 
@@ -105,29 +112,220 @@ krb5_error_code libnet_keytab_init(TALLOC_CTX *mem_ctx,
 /****************************************************************
 ****************************************************************/
 
+/**
+ * Remove all entries that have the given principal, kvno and enctype.
+ */
+static krb5_error_code libnet_keytab_remove_entries(krb5_context context,
+                                                   krb5_keytab keytab,
+                                                   const char *principal,
+                                                   int kvno,
+                                                   const krb5_enctype enctype,
+                                                   bool ignore_kvno)
+{
+       krb5_error_code ret;
+       krb5_kt_cursor cursor;
+       krb5_keytab_entry kt_entry;
+
+       ZERO_STRUCT(kt_entry);
+       ZERO_STRUCT(cursor);
+
+       ret = krb5_kt_start_seq_get(context, keytab, &cursor);
+       if (ret) {
+               return 0;
+       }
+
+       while (krb5_kt_next_entry(context, keytab, &kt_entry, &cursor) == 0)
+       {
+               krb5_keyblock *keyp;
+               char *princ_s = NULL;
+
+               if (kt_entry.vno != kvno && !ignore_kvno) {
+                       goto cont;
+               }
+
+               keyp = KRB5_KT_KEY(&kt_entry);
+
+               if (KRB5_KEY_TYPE(keyp) != enctype) {
+                       goto cont;
+               }
+
+               ret = smb_krb5_unparse_name(talloc_tos(), context, kt_entry.principal,
+                                           &princ_s);
+               if (ret) {
+                       DEBUG(5, ("smb_krb5_unparse_name failed (%s)\n",
+                                 error_message(ret)));
+                       goto cont;
+               }
+
+               if (strcmp(principal, princ_s) != 0) {
+                       goto cont;
+               }
+
+               /* match found - remove */
+
+               DEBUG(10, ("found entry for principal %s, kvno %d, "
+                          "enctype %d - trying to remove it\n",
+                          princ_s, kt_entry.vno, KRB5_KEY_TYPE(keyp)));
+
+               ret = krb5_kt_end_seq_get(context, keytab, &cursor);
+               ZERO_STRUCT(cursor);
+               if (ret) {
+                       DEBUG(5, ("krb5_kt_end_seq_get failed (%s)\n",
+                                 error_message(ret)));
+                       goto cont;
+               }
+
+               ret = krb5_kt_remove_entry(context, keytab,
+                                          &kt_entry);
+               if (ret) {
+                       DEBUG(5, ("krb5_kt_remove_entry failed (%s)\n",
+                                 error_message(ret)));
+                       goto cont;
+               }
+               DEBUG(10, ("removed entry for principal %s, kvno %d, "
+                          "enctype %d\n", princ_s, kt_entry.vno,
+                          KRB5_KEY_TYPE(keyp)));
+
+               ret = krb5_kt_start_seq_get(context, keytab, &cursor);
+               if (ret) {
+                       DEBUG(5, ("krb5_kt_start_seq_get failed (%s)\n",
+                                 error_message(ret)));
+                       goto cont;
+               }
+
+cont:
+               smb_krb5_kt_free_entry(context, &kt_entry);
+               TALLOC_FREE(princ_s);
+       }
+
+       ret = krb5_kt_end_seq_get(context, keytab, &cursor);
+       if (ret) {
+               DEBUG(5, ("krb5_kt_end_seq_get failed (%s)\n",
+                         error_message(ret)));
+       }
+
+       return ret;
+}
+
+static krb5_error_code libnet_keytab_add_entry(krb5_context context,
+                                              krb5_keytab keytab,
+                                              krb5_kvno kvno,
+                                              const char *princ_s,
+                                              krb5_enctype enctype,
+                                              krb5_data password)
+{
+       krb5_keyblock *keyp;
+       krb5_keytab_entry kt_entry;
+       krb5_error_code ret;
+       krb5_principal salt_princ = NULL;
+       char *salt_princ_s;
+
+       /* remove duplicates first ... */
+       ret = libnet_keytab_remove_entries(context, keytab, princ_s, kvno,
+                                          enctype, false);
+       if (ret) {
+               DEBUG(1, ("libnet_keytab_remove_entries failed: %s\n",
+                         error_message(ret)));
+       }
+
+       ZERO_STRUCT(kt_entry);
+
+       kt_entry.vno = kvno;
+
+       ret = smb_krb5_parse_name(context, princ_s, &kt_entry.principal);
+       if (ret) {
+               DEBUG(1, ("smb_krb5_parse_name(%s) failed (%s)\n",
+                         princ_s, error_message(ret)));
+               return ret;
+       }
+
+       keyp = KRB5_KT_KEY(&kt_entry);
+
+       salt_princ_s = kerberos_fetch_salt_princ_for_host_princ(context,
+                                                               princ_s,
+                                                               enctype);
+       if (salt_princ_s == NULL) {
+               ret = KRB5KRB_ERR_GENERIC;
+               goto done;
+       }
+
+       ret = krb5_parse_name(context, salt_princ_s, &salt_princ);
+       SAFE_FREE(salt_princ_s);
+       if (ret != 0) {
+               ret = KRB5KRB_ERR_GENERIC;
+               goto done;
+       }
+
+       ret = create_kerberos_key_from_string(context,
+                                             kt_entry.principal,
+                                             salt_princ,
+                                             &password,
+                                             keyp,
+                                             enctype,
+                                             true);
+       krb5_free_principal(context, salt_princ);
+       if (ret != 0) {
+               ret = KRB5KRB_ERR_GENERIC;
+               goto done;
+       }
+
+       ret = krb5_kt_add_entry(context, keytab, &kt_entry);
+       if (ret) {
+               DEBUG(1, ("adding entry to keytab failed (%s)\n",
+                         error_message(ret)));
+       }
+
+done:
+       krb5_free_keyblock_contents(context, keyp);
+       krb5_free_principal(context, kt_entry.principal);
+       ZERO_STRUCT(kt_entry);
+       smb_krb5_kt_free_entry(context, &kt_entry);
+
+       return ret;
+}
+
 krb5_error_code libnet_keytab_add(struct libnet_keytab_context *ctx)
 {
-#if defined(ENCTYPE_ARCFOUR_HMAC)
        krb5_error_code ret = 0;
-       krb5_enctype enctypes[2] = { ENCTYPE_ARCFOUR_HMAC, 0 };
-       int i;
+       uint32_t i;
+
+
+       if (ctx->clean_old_entries) {
+               DEBUG(0, ("cleaning old entries...\n"));
+               for (i=0; i < ctx->count; i++) {
+                       struct libnet_keytab_entry *entry = &ctx->entries[i];
+
+                       ret = libnet_keytab_remove_entries(ctx->context,
+                                                          ctx->keytab,
+                                                          entry->principal,
+                                                          0,
+                                                          entry->enctype,
+                                                          true);
+                       if (ret) {
+                               DEBUG(1,("libnet_keytab_add: Failed to remove "
+                                        "old entries for %s (enctype %u): %s\n",
+                                        entry->principal, entry->enctype,
+                                        error_message(ret)));
+                               return ret;
+                       }
+               }
+       }
 
        for (i=0; i<ctx->count; i++) {
 
                struct libnet_keytab_entry *entry = &ctx->entries[i];
                krb5_data password;
 
+               ZERO_STRUCT(password);
                password.data = (char *)entry->password.data;
                password.length = entry->password.length;
 
-               ret = smb_krb5_kt_add_entry_ext(ctx->context,
-                                               ctx->keytab,
-                                               entry->kvno,
-                                               entry->principal,
-                                               enctypes,
-                                               password,
-                                               true,
-                                               true);
+               ret = libnet_keytab_add_entry(ctx->context,
+                                             ctx->keytab,
+                                             entry->kvno,
+                                             entry->principal,
+                                             entry->enctype,
+                                             password);
                if (ret) {
                        DEBUG(1,("libnet_keytab_add: "
                                "Failed to add entry to keytab file\n"));
@@ -136,13 +334,12 @@ krb5_error_code libnet_keytab_add(struct libnet_keytab_context *ctx)
        }
 
        return ret;
-#else
-       return -1;
-#endif /* defined(ENCTYPE_ARCFOUR_HMAC) */
 }
 
 struct libnet_keytab_entry *libnet_keytab_search(struct libnet_keytab_context *ctx,
-                                                const char *principal, int kvno,
+                                                const char *principal,
+                                                int kvno,
+                                                const krb5_enctype enctype,
                                                 TALLOC_CTX *mem_ctx)
 {
        krb5_error_code ret = 0;
@@ -155,67 +352,110 @@ struct libnet_keytab_entry *libnet_keytab_search(struct libnet_keytab_context *c
 
        ret = krb5_kt_start_seq_get(ctx->context, ctx->keytab, &cursor);
        if (ret) {
+               DEBUG(10, ("krb5_kt_start_seq_get failed: %s\n",
+                         error_message(ret)));
                return NULL;
        }
 
-       while (krb5_kt_next_entry(ctx->context, ctx->keytab, &kt_entry, &cursor) == 0) {
+       while (krb5_kt_next_entry(ctx->context, ctx->keytab, &kt_entry, &cursor) == 0)
+       {
+               krb5_keyblock *keyp;
                char *princ_s = NULL;
 
+               entry = NULL;
+
                if (kt_entry.vno != kvno) {
-                       smb_krb5_kt_free_entry(ctx->context, &kt_entry);
-                       continue;
+                       goto cont;
                }
 
-               ret = smb_krb5_unparse_name(ctx->context, kt_entry.principal, &princ_s);
-               if (ret) {
-                       smb_krb5_kt_free_entry(ctx->context, &kt_entry);
-                       continue;
-               }
+               keyp = KRB5_KT_KEY(&kt_entry);
 
-               if (strcmp(principal, princ_s) != 0) {
-                       smb_krb5_kt_free_entry(ctx->context, &kt_entry);
-                       SAFE_FREE(princ_s);
-                       continue;
+               if (KRB5_KEY_TYPE(keyp) != enctype) {
+                       goto cont;
                }
 
                entry = talloc_zero(mem_ctx, struct libnet_keytab_entry);
                if (!entry) {
-                       smb_krb5_kt_free_entry(ctx->context, &kt_entry);
-                       SAFE_FREE(princ_s);
-                       break;
+                       DEBUG(3, ("talloc failed\n"));
+                       goto fail;
+               }
+
+               ret = smb_krb5_unparse_name(entry, ctx->context, kt_entry.principal,
+                                           &princ_s);
+               if (ret) {
+                       goto cont;
                }
 
-               entry->name = talloc_strdup(entry, princ_s);
-               if (!entry->name) {
-                       smb_krb5_kt_free_entry(ctx->context, &kt_entry);
-                       SAFE_FREE(princ_s);
-                       TALLOC_FREE(entry);
-                       break;
+               if (strcmp(principal, princ_s) != 0) {
+                       goto cont;
                }
 
                entry->principal = talloc_strdup(entry, princ_s);
                if (!entry->principal) {
-                       smb_krb5_kt_free_entry(ctx->context, &kt_entry);
-                       SAFE_FREE(princ_s);
-                       TALLOC_FREE(entry);
-                       break;
+                       DEBUG(3, ("talloc_strdup_failed\n"));
+                       goto fail;
                }
 
-               entry->password = data_blob_talloc(entry, kt_entry.key.contents, kt_entry.key.length);
+               entry->name = talloc_move(entry, &princ_s);
+
+               entry->password = data_blob_talloc(entry, KRB5_KEY_DATA(keyp),
+                                                  KRB5_KEY_LENGTH(keyp));
                if (!entry->password.data) {
-                       smb_krb5_kt_free_entry(ctx->context, &kt_entry);
-                       SAFE_FREE(princ_s);
-                       TALLOC_FREE(entry);
-                       break;
+                       DEBUG(3, ("data_blob_talloc failed\n"));
+                       goto fail;
                }
 
+               DEBUG(10, ("found entry\n"));
+
+               smb_krb5_kt_free_entry(ctx->context, &kt_entry);
+               break;
+
+fail:
                smb_krb5_kt_free_entry(ctx->context, &kt_entry);
-               SAFE_FREE(princ_s);
+               TALLOC_FREE(entry);
                break;
+
+cont:
+               smb_krb5_kt_free_entry(ctx->context, &kt_entry);
+               TALLOC_FREE(entry);
+               continue;
        }
 
        krb5_kt_end_seq_get(ctx->context, ctx->keytab, &cursor);
        return entry;
 }
 
+/**
+ * Helper function to add data to the list
+ * of keytab entries. It builds the prefix from the input.
+ */
+NTSTATUS libnet_keytab_add_to_keytab_entries(TALLOC_CTX *mem_ctx,
+                                            struct libnet_keytab_context *ctx,
+                                            uint32_t kvno,
+                                            const char *name,
+                                            const char *prefix,
+                                            const krb5_enctype enctype,
+                                            DATA_BLOB blob)
+{
+       struct libnet_keytab_entry entry;
+
+       entry.kvno = kvno;
+       entry.name = talloc_strdup(mem_ctx, name);
+       entry.principal = talloc_asprintf(mem_ctx, "%s%s%s@%s",
+                                         prefix ? prefix : "",
+                                         prefix ? "/" : "",
+                                         name, ctx->dns_domain_name);
+       entry.enctype = enctype;
+       entry.password = blob;
+       NT_STATUS_HAVE_NO_MEMORY(entry.name);
+       NT_STATUS_HAVE_NO_MEMORY(entry.principal);
+       NT_STATUS_HAVE_NO_MEMORY(entry.password.data);
+
+       ADD_TO_ARRAY(mem_ctx, struct libnet_keytab_entry, entry,
+                    &ctx->entries, &ctx->count);
+       NT_STATUS_HAVE_NO_MEMORY(ctx->entries);
+
+       return NT_STATUS_OK;
+}
+
 #endif /* HAVE_KRB5 */