s4-libnet: Implement export_keytab without HDB
authorAndreas Schneider <asn@samba.org>
Mon, 29 Feb 2016 14:12:02 +0000 (15:12 +0100)
committerStefan Metzmacher <metze@samba.org>
Thu, 10 Mar 2016 05:52:25 +0000 (06:52 +0100)
This is used by 'samba-tool domain exportkeytab'. This loads the HDB
Samba backend thus needs access to samdb. To avoid using heimdal
specific code here, we could talk to samdb directly and write a
keytab file.

Signed-off-by: Andreas Schneider <asn@samba.org>
Reviewed-by: Stefan Metzmacher <metze@samba.org>
source4/libnet/libnet_export_keytab.c

index 295624e9f7110a428db20b390a374b7cd37e3f72..bdd77c0d1c6c56140975f136ec29dd846863d392 100644 (file)
@@ -2,6 +2,7 @@
    Unix SMB/CIFS implementation.
 
    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2009
+   Copyright (C) Andreas Schneider <asn@samba.org> 2016
 
    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
 #include "kdc/samba_kdc.h"
 #include "libnet/libnet_export_keytab.h"
 
-NTSTATUS libnet_export_keytab(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_export_keytab *r)
-{
-       krb5_error_code ret;
-       struct smb_krb5_context *smb_krb5_context;
-       const char *from_keytab;
+#include "kdc/db-glue.h"
+#include "kdc/sdb.h"
 
-       /* Register hdb-samba4 hooks for use as a keytab */
+static NTSTATUS sdb_kt_copy(TALLOC_CTX *mem_ctx,
+                           krb5_context context,
+                           struct samba_kdc_db_context *db_ctx,
+                           const char *keytab_name,
+                           const char *principal,
+                           const char **error_string)
+{
+       struct sdb_entry_ex sentry = {
+               .free_entry = NULL,
+       };
+       krb5_keytab keytab;
+       krb5_error_code code = 0;
+       NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+       char *entry_principal = NULL;
+       bool copy_one_principal = (principal != NULL);
+       krb5_data password;
 
-       struct samba_kdc_base_context *base_ctx = talloc_zero(mem_ctx, struct samba_kdc_base_context);
-       if (!base_ctx) {
-               return NT_STATUS_NO_MEMORY; 
+       code = smb_krb5_open_keytab_relative(context,
+                                            keytab_name,
+                                            true, /* write_access */
+                                            &keytab);
+       if (code != 0) {
+               *error_string = talloc_asprintf(mem_ctx,
+                                               "Failed to open keytab: %s",
+                                               keytab_name);
+               status = NT_STATUS_NO_SUCH_FILE;
+               goto done;
        }
 
-       base_ctx->ev_ctx = ctx->event_ctx;
-       base_ctx->lp_ctx = ctx->lp_ctx;
+       for (code = samba_kdc_firstkey(context, db_ctx, &sentry);
+            code == 0;
+            code = samba_kdc_nextkey(context, db_ctx, &sentry)) {
+               bool principal_found = false;
+               int i;
 
-       from_keytab = talloc_asprintf(base_ctx, "HDB:samba4&%p", base_ctx);
-       if (!from_keytab) {
-               return NT_STATUS_NO_MEMORY;
+               code = krb5_unparse_name(context,
+                                        sentry.entry.principal,
+                                        &entry_principal);
+               if (code != 0) {
+                       *error_string = smb_get_krb5_error_message(context,
+                                                                  code,
+                                                                  mem_ctx);
+                       status = NT_STATUS_UNSUCCESSFUL;
+                       goto done;
+               }
+
+               if (principal != NULL) {
+                       int cmp;
+
+                       cmp = strcmp(principal, entry_principal);
+                       if (cmp == 0) {
+                               principal_found = true;
+                       }
+               }
+
+               if (sentry.entry.keys.len == 0 ||
+                   (copy_one_principal && !principal_found)) {
+                       SAFE_FREE(entry_principal);
+                       sdb_free_entry(&sentry);
+                       sentry = (struct sdb_entry_ex) {
+                               .free_entry = NULL,
+                       };
+
+                       continue;
+               }
+
+               for (i = 0; i < sentry.entry.keys.len; i++) {
+                       struct sdb_key *s = &(sentry.entry.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.entry.kvno,
+                                                    entry_principal,
+                                                    NULL,
+                                                    enctype,
+                                                    &password,
+                                                    true,    /* no_salt */
+                                                    false);  /* keeyp_old_entries */
+                       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;
+                       }
+               }
+
+               if (principal_found) {
+                       break;
+               }
+
+               SAFE_FREE(entry_principal);
+               sdb_free_entry(&sentry);
+               sentry = (struct sdb_entry_ex) {
+                       .free_entry = NULL,
+               };
+       }
+
+       if (code != 0 && code != SDB_ERR_NOENTRY) {
+               *error_string = smb_get_krb5_error_message(context,
+                                                          code,
+                                                          mem_ctx);
+               status = NT_STATUS_NO_SUCH_USER;
+               goto done;
        }
 
+       status = NT_STATUS_OK;
+done:
+       SAFE_FREE(entry_principal);
+       sdb_free_entry(&sentry);
+
+       return status;
+}
+
+NTSTATUS libnet_export_keytab(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_export_keytab *r)
+{
+       krb5_error_code ret;
+       struct smb_krb5_context *smb_krb5_context;
+       struct samba_kdc_base_context *base_ctx;
+       struct samba_kdc_db_context *db_ctx = NULL;
+       const char *error_string = NULL;
+       NTSTATUS status;
+
        ret = smb_krb5_init_context(ctx, ctx->lp_ctx, &smb_krb5_context);
        if (ret) {
                return NT_STATUS_NO_MEMORY; 
        }
 
-       ret = krb5_plugin_register(smb_krb5_context->krb5_context, 
-                                  PLUGIN_TYPE_DATA, "hdb",
-                                  &hdb_samba4_interface);
-       if(ret) {
+       base_ctx = talloc_zero(mem_ctx, struct samba_kdc_base_context);
+       if (base_ctx == NULL) {
                return NT_STATUS_NO_MEMORY;
        }
 
-       ret = krb5_kt_register(smb_krb5_context->krb5_context, &hdb_kt_ops);
-       if(ret) {
-               return NT_STATUS_NO_MEMORY;
+       base_ctx->ev_ctx = ctx->event_ctx;
+       base_ctx->lp_ctx = ctx->lp_ctx;
+
+       status = samba_kdc_setup_db_ctx(mem_ctx, base_ctx, &db_ctx);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
        }
 
-       if (r->in.principal) {
-               ret = kt_copy_one_principal(smb_krb5_context->krb5_context, from_keytab, r->in.keytab_name, r->in.principal, 0, samba_all_enctypes());
+       if (r->in.principal != NULL) {
+               DEBUG(0, ("Export one principal to %s\n", r->in.keytab_name));
+               status = sdb_kt_copy(mem_ctx,
+                                    smb_krb5_context->krb5_context,
+                                    db_ctx,
+                                    r->in.keytab_name,
+                                    r->in.principal,
+                                    &error_string);
        } else {
                unlink(r->in.keytab_name);
-               ret = kt_copy(smb_krb5_context->krb5_context, from_keytab, r->in.keytab_name);
+               DEBUG(0, ("Export complete keytab to %s\n", r->in.keytab_name));
+               status = sdb_kt_copy(mem_ctx,
+                                    smb_krb5_context->krb5_context,
+                                    db_ctx,
+                                    r->in.keytab_name,
+                                    NULL,
+                                    &error_string);
        }
 
-       if(ret) {
-               r->out.error_string = smb_get_krb5_error_message(smb_krb5_context->krb5_context,
-                                                                ret, mem_ctx);
-               if (ret == KRB5_KT_NOTFOUND) {
-                       return NT_STATUS_NO_SUCH_USER;
-               } else {
-                       return NT_STATUS_UNSUCCESSFUL;
-               }
+       talloc_free(db_ctx);
+       talloc_free(base_ctx);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               r->out.error_string = error_string;
        }
-       return NT_STATUS_OK;
+
+       return status;
 }