s3-talloc Change TALLOC_ZERO_P() to talloc_zero()
[nivanova/samba-autobuild/.git] / source3 / libnet / libnet_keytab.c
index 0b8327c38fa571640e7c5c3dacfdd7300872aba4..6349c2291f7371d232a76e3efc5620930e73879c 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;
        }
@@ -96,6 +99,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,6 +109,101 @@ 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,
@@ -116,6 +215,14 @@ static krb5_error_code libnet_keytab_add_entry(krb5_context context,
        krb5_keytab_entry kt_entry;
        krb5_error_code ret;
 
+       /* 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;
@@ -127,15 +234,7 @@ static krb5_error_code libnet_keytab_add_entry(krb5_context context,
                return ret;
        }
 
-#if !defined(HAVE_KRB5_KEYTAB_ENTRY_KEY) && !defined(HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK)
-#error krb5_keytab_entry has no key or keyblock member
-#endif
-#ifdef HAVE_KRB5_KEYTAB_ENTRY_KEY               /* MIT */
-       keyp = &kt_entry.key;
-#endif
-#ifdef HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK          /* Heimdal */
-       keyp = &kt_entry.keyblock;
-#endif
+       keyp = KRB5_KT_KEY(&kt_entry);
 
        if (create_kerberos_key_from_string(context, kt_entry.principal,
                                            &password, keyp, enctype, true))
@@ -161,27 +260,46 @@ done:
 
 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"));
@@ -190,9 +308,6 @@ 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,
@@ -211,22 +326,35 @@ 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)
        {
+               krb5_keyblock *keyp;
                char *princ_s = NULL;
 
+               entry = NULL;
+
                if (kt_entry.vno != kvno) {
                        goto cont;
                }
 
-               if (kt_entry.key.enctype != enctype) {
+               keyp = KRB5_KT_KEY(&kt_entry);
+
+               if (KRB5_KEY_TYPE(keyp) != enctype) {
                        goto cont;
                }
 
-               ret = smb_krb5_unparse_name(ctx->context, kt_entry.principal,
+               entry = talloc_zero(mem_ctx, struct libnet_keytab_entry);
+               if (!entry) {
+                       DEBUG(3, ("talloc failed\n"));
+                       goto fail;
+               }
+
+               ret = smb_krb5_unparse_name(entry, ctx->context, kt_entry.principal,
                                            &princ_s);
                if (ret) {
                        goto cont;
@@ -236,40 +364,34 @@ struct libnet_keytab_entry *libnet_keytab_search(struct libnet_keytab_context *c
                        goto cont;
                }
 
-               entry = talloc_zero(mem_ctx, struct libnet_keytab_entry);
-               if (!entry) {
-                       goto fail;
-               }
-
-               entry->name = talloc_strdup(entry, princ_s);
-               if (!entry->name) {
-                       goto fail;
-               }
-
                entry->principal = talloc_strdup(entry, princ_s);
                if (!entry->principal) {
+                       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) {
+                       DEBUG(3, ("data_blob_talloc failed\n"));
                        goto fail;
                }
 
+               DEBUG(10, ("found entry\n"));
+
                smb_krb5_kt_free_entry(ctx->context, &kt_entry);
-               SAFE_FREE(princ_s);
                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);
-               SAFE_FREE(princ_s);
+               TALLOC_FREE(entry);
                continue;
        }
 
@@ -278,93 +400,36 @@ cont:
 }
 
 /**
- * Remove all entries that have the given principal, kvno and enctype.
+ * Helper function to add data to the list
+ * of keytab entries. It builds the prefix from the input.
  */
-krb5_error_code libnet_keytab_remove_entries(struct libnet_keytab_context *ctx,
-                                            const char *principal,
-                                            int kvno,
-                                            const krb5_enctype enctype)
+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)
 {
-       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(ctx->context, ctx->keytab, &cursor);
-       if (ret) {
-               return 0;
-       }
-
-       while (krb5_kt_next_entry(ctx->context, ctx->keytab, &kt_entry, &cursor) == 0)
-       {
-               char *princ_s = NULL;
-
-               if (kt_entry.vno != kvno) {
-                       goto cont;
-               }
-
-               if (kt_entry.key.enctype != enctype) {
-                       goto cont;
-               }
-
-               ret = smb_krb5_unparse_name(ctx->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, kt_entry.key.enctype));
-
-               ret = krb5_kt_end_seq_get(ctx->context, ctx->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(ctx->context, ctx->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,
-                          kt_entry.key.enctype));
-
-               ret = krb5_kt_start_seq_get(ctx->context, ctx->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(ctx->context, &kt_entry);
-               SAFE_FREE(princ_s);
-       }
-
-       ret = krb5_kt_end_seq_get(ctx->context, ctx->keytab, &cursor);
-       if (ret) {
-               DEBUG(5, ("krb5_kt_end_seq_get failed (%s)\n",
-                         error_message(ret)));
-       }
-
-       return ret;
+       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 */