diff options
-rw-r--r-- | source3/Makefile.in | 1 | ||||
-rw-r--r-- | source3/libnet/libnet_samsync.c | 3 | ||||
-rw-r--r-- | source3/libnet/libnet_samsync.h | 8 | ||||
-rw-r--r-- | source3/libnet/libnet_samsync_keytab.c | 319 | ||||
-rw-r--r-- | source3/utils/net_proto.h | 9 | ||||
-rw-r--r-- | source3/utils/net_rpc.c | 9 | ||||
-rw-r--r-- | source3/utils/net_rpc_samsync.c | 77 |
7 files changed, 423 insertions, 3 deletions
diff --git a/source3/Makefile.in b/source3/Makefile.in index 67d4ed1ac6..4f680cc56f 100644 --- a/source3/Makefile.in +++ b/source3/Makefile.in @@ -896,6 +896,7 @@ LIBNET_OBJ = libnet/libnet_join.o \ libnet/libnet_samsync_ldif.o \ libnet/libnet_samsync_passdb.o \ libnet/libnet_samsync_display.o \ + libnet/libnet_samsync_keytab.o \ librpc/gen_ndr/ndr_libnet_join.o NET_OBJ1 = utils/net.o utils/net_ads.o utils/net_help.o \ diff --git a/source3/libnet/libnet_samsync.c b/source3/libnet/libnet_samsync.c index e170acc560..dcf5f9c39f 100644 --- a/source3/libnet/libnet_samsync.c +++ b/source3/libnet/libnet_samsync.c @@ -254,6 +254,9 @@ static const char *samsync_debug_str(TALLOC_CTX *mem_ctx, case NET_SAMSYNC_MODE_FETCH_LDIF: action = "Fetching (to ldif)"; break; + case NET_SAMSYNC_MODE_FETCH_KEYTAB: + action = "Fetching (to keytab)"; + break; default: action = "Unknown"; break; diff --git a/source3/libnet/libnet_samsync.h b/source3/libnet/libnet_samsync.h index de0225c6fc..8559043f5a 100644 --- a/source3/libnet/libnet_samsync.h +++ b/source3/libnet/libnet_samsync.h @@ -21,7 +21,8 @@ enum net_samsync_mode { NET_SAMSYNC_MODE_FETCH_PASSDB = 0, NET_SAMSYNC_MODE_FETCH_LDIF = 1, - NET_SAMSYNC_MODE_DUMP = 2 + NET_SAMSYNC_MODE_FETCH_KEYTAB = 2, + NET_SAMSYNC_MODE_DUMP = 3 }; struct samsync_context; @@ -65,3 +66,8 @@ NTSTATUS display_sam_entries(TALLOC_CTX *mem_ctx, struct netr_DELTA_ENUM_ARRAY *r, NTSTATUS status, struct samsync_context *ctx); +NTSTATUS fetch_sam_entries_keytab(TALLOC_CTX *mem_ctx, + enum netr_SamDatabaseID database_id, + struct netr_DELTA_ENUM_ARRAY *r, + NTSTATUS status, + struct samsync_context *ctx); diff --git a/source3/libnet/libnet_samsync_keytab.c b/source3/libnet/libnet_samsync_keytab.c new file mode 100644 index 0000000000..2208a71563 --- /dev/null +++ b/source3/libnet/libnet_samsync_keytab.c @@ -0,0 +1,319 @@ +/* + Unix SMB/CIFS implementation. + dump the remote SAM using rpc samsync operations + + Copyright (C) Guenther Deschner 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 + 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/>. +*/ + +#include "includes.h" +#include "utils/net.h" + +#if defined(HAVE_ADS) && defined(ENCTYPE_ARCFOUR_HMAC) + +/**************************************************************** +****************************************************************/ + +struct samsync_keytab_entry { + const char *name; + const char *principal; + DATA_BLOB password; + uint32_t kvno; +}; + +struct samsync_keytab_context { + krb5_context context; + krb5_keytab keytab; + const char *keytab_name; + ADS_STRUCT *ads; + const char *dns_domain_name; + uint8_t zero_buf[16]; + uint32_t count; + struct samsync_keytab_entry *entries; +}; + +/**************************************************************** +****************************************************************/ + +static int keytab_close(struct samsync_keytab_context *ctx) +{ + if (!ctx) { + return 0; + } + + if (ctx->keytab && ctx->context) { + krb5_kt_close(ctx->context, ctx->keytab); + } + + if (ctx->context) { + krb5_free_context(ctx->context); + } + + if (ctx->ads) { + ads_destroy(&ctx->ads); + } + + TALLOC_FREE(ctx); + + return 0; +} + +/**************************************************************** +****************************************************************/ + +static krb5_error_code keytab_init(TALLOC_CTX *mem_ctx, + const char *keytab_name, + struct samsync_keytab_context **ctx) +{ + krb5_error_code ret = 0; + krb5_context context = NULL; + krb5_keytab keytab = NULL; + const char *keytab_string = NULL; + + struct samsync_keytab_context *r; + + r = TALLOC_ZERO_P(mem_ctx, struct samsync_keytab_context); + if (!r) { + return ENOMEM; + } + + talloc_set_destructor(r, keytab_close); + + initialize_krb5_error_table(); + ret = krb5_init_context(&context); + if (ret) { + DEBUG(1,("keytab_init: could not krb5_init_context: %s\n", + error_message(ret))); + return ret; + } + + ret = smb_krb5_open_keytab(context, keytab_name, true, &keytab); + if (ret) { + DEBUG(1,("keytab_init: smb_krb5_open_keytab failed (%s)\n", + error_message(ret))); + krb5_free_context(context); + return ret; + } + + ret = smb_krb5_keytab_name(mem_ctx, context, keytab, &keytab_string); + if (ret) { + krb5_kt_close(context, keytab); + krb5_free_context(context); + return ret; + } + + r->context = context; + r->keytab = keytab; + r->keytab_name = keytab_string; + + *ctx = r; + + return 0; +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS keytab_ad_connect(TALLOC_CTX *mem_ctx, + const char *domain_name, + const char *username, + const char *password, + struct samsync_keytab_context *ctx) +{ + NTSTATUS status; + ADS_STATUS ad_status; + ADS_STRUCT *ads; + struct netr_DsRGetDCNameInfo *info = NULL; + const char *dc; + + status = dsgetdcname(mem_ctx, NULL, domain_name, NULL, NULL, 0, &info); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + dc = strip_hostname(info->dc_unc); + + ads = ads_init(NULL, domain_name, dc); + NT_STATUS_HAVE_NO_MEMORY(ads); + + if (getenv(KRB5_ENV_CCNAME) == NULL) { + setenv(KRB5_ENV_CCNAME, "MEMORY:libnet_samsync_keytab", 1); + } + + ads->auth.user_name = SMB_STRDUP(username); + ads->auth.password = SMB_STRDUP(password); + + ad_status = ads_connect_user_creds(ads); + if (!ADS_ERR_OK(ad_status)) { + return NT_STATUS_UNSUCCESSFUL; + } + + ctx->ads = ads; + + ctx->dns_domain_name = talloc_strdup_upper(mem_ctx, ads->config.realm); + NT_STATUS_HAVE_NO_MEMORY(ctx->dns_domain_name); + + return NT_STATUS_OK; +} + +/**************************************************************** +****************************************************************/ + +static krb5_error_code keytab_add(struct samsync_keytab_context *ctx) +{ + krb5_error_code ret = 0; + krb5_enctype enctypes[2] = { ENCTYPE_ARCFOUR_HMAC, 0 }; + int i; + + for (i=0; i<ctx->count; i++) { + + struct samsync_keytab_entry *entry = &ctx->entries[i]; + krb5_data password; + krb5_kvno kvno; + + kvno = ads_get_kvno(ctx->ads, entry->name); + + password.data = (char *)entry->password.data; + password.length = entry->password.length; + + ret = smb_krb5_kt_add_entry(ctx->context, + ctx->keytab, + kvno, + entry->principal, + enctypes, + password, + true); + if (ret) { + DEBUG(1,("keytab_add: Failed to add entry to keytab file\n")); + return ret; + } + } + + return ret; +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS fetch_sam_entry_keytab(TALLOC_CTX *mem_ctx, + enum netr_SamDatabaseID database_id, + uint32_t rid, + struct netr_DELTA_USER *r, + NTSTATUS status, + struct samsync_keytab_context *ctx) +{ + uchar nt_passwd[16]; + struct samsync_keytab_entry *entry; + + if (memcmp(r->ntpassword.hash, ctx->zero_buf, 16) == 0) { + return NT_STATUS_OK; + } + + entry = TALLOC_ZERO_P(mem_ctx, struct samsync_keytab_entry); + NT_STATUS_HAVE_NO_MEMORY(entry); + + sam_pwd_hash(rid, r->ntpassword.hash, nt_passwd, 0); + + entry->name = talloc_strdup(mem_ctx, r->account_name.string); + entry->principal = talloc_asprintf(mem_ctx, "%s@%s", + r->account_name.string, + ctx->dns_domain_name); + entry->password = data_blob_talloc(mem_ctx, nt_passwd, 16); + + NT_STATUS_HAVE_NO_MEMORY(entry->name); + NT_STATUS_HAVE_NO_MEMORY(entry->principal); + + ADD_TO_ARRAY(mem_ctx, struct samsync_keytab_entry, *entry, + &ctx->entries, &ctx->count); + + return NT_STATUS_OK; +} + +/**************************************************************** +****************************************************************/ + +NTSTATUS fetch_sam_entries_keytab(TALLOC_CTX *mem_ctx, + enum netr_SamDatabaseID database_id, + struct netr_DELTA_ENUM_ARRAY *r, + NTSTATUS result, + struct samsync_context *ctx) +{ + NTSTATUS status = NT_STATUS_OK; + krb5_error_code ret = 0; + struct samsync_keytab_context *keytab_ctx = NULL; + int i; + + ret = keytab_init(mem_ctx, ctx->output_filename, &keytab_ctx); + if (ret) { + status = krb5_to_nt_status(ret); + goto out; + } + + status = keytab_ad_connect(mem_ctx, + ctx->domain_name, + ctx->username, + ctx->password, + keytab_ctx); + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + + for (i = 0; i < r->num_deltas; i++) { + + if (r->delta_enum[i].delta_type != NETR_DELTA_USER) { + continue; + } + + status = fetch_sam_entry_keytab(mem_ctx, database_id, + r->delta_enum[i].delta_id_union.rid, + r->delta_enum[i].delta_union.user, + result, + keytab_ctx); + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + } + + ret = keytab_add(keytab_ctx); + if (ret) { + status = krb5_to_nt_status(ret); + ctx->error_message = talloc_asprintf(mem_ctx, + "Failed to add entries to keytab %s: %s", + keytab_ctx->keytab_name, error_message(ret)); + goto out; + } + + ctx->result_message = talloc_asprintf(mem_ctx, + "vampired %d accounts to keytab %s", + keytab_ctx->count, + keytab_ctx->keytab_name); + out: + TALLOC_FREE(keytab_ctx); + + return status; +} + +#else + +NTSTATUS fetch_sam_entries_keytab(TALLOC_CTX *mem_ctx, + enum netr_SamDatabaseID database_id, + struct netr_DELTA_ENUM_ARRAY *r, + NTSTATUS result, + struct samsync_context *ctx) +{ + return NT_STATUS_NOT_SUPPORTED; +} + +#endif /* defined(HAVE_ADS) && defined(ENCTYPE_ARCFOUR_HMAC) */ diff --git a/source3/utils/net_proto.h b/source3/utils/net_proto.h index a370d3d6b2..10b1e4709a 100644 --- a/source3/utils/net_proto.h +++ b/source3/utils/net_proto.h @@ -368,6 +368,15 @@ NTSTATUS rpc_vampire_ldif_internals(struct net_context *c, TALLOC_CTX *mem_ctx, int argc, const char **argv); +NTSTATUS rpc_vampire_keytab_internals(struct net_context *c, + const DOM_SID *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv); +int rpc_vampire_keytab(struct net_context *c, int argc, const char **argv); /* The following definitions come from utils/net_rpc_service.c */ diff --git a/source3/utils/net_rpc.c b/source3/utils/net_rpc.c index 6a7c638e2a..19566bdf09 100644 --- a/source3/utils/net_rpc.c +++ b/source3/utils/net_rpc.c @@ -6818,6 +6818,15 @@ static int rpc_vampire(struct net_context *c, int argc, const char **argv) "net rpc vampire ldif\n" " Dump remote SAM database to LDIF file or stdout" }, + { + "keytab", + rpc_vampire_keytab, + NET_TRANSPORT_RPC, + "Dump remote SAM database to Kerberos Keytab", + "net rpc vampire keytab\n" + " Dump remote SAM database to Kerberos keytab file" + }, + {NULL, NULL, 0, NULL, NULL} }; diff --git a/source3/utils/net_rpc_samsync.c b/source3/utils/net_rpc_samsync.c index c941338b32..e4aa343d06 100644 --- a/source3/utils/net_rpc_samsync.c +++ b/source3/utils/net_rpc_samsync.c @@ -73,11 +73,13 @@ NTSTATUS rpc_samdump_internals(struct net_context *c, int rpc_vampire_usage(struct net_context *c, int argc, const char **argv) { - d_printf("net rpc vampire [ldif [<ldif-filename>] [options]\n" + d_printf("net rpc vampire ([ldif [<ldif-filename>] | [keytab] [<keytab-filename]) [options]\n" "\t to pull accounts from a remote PDC where we are a BDC\n" "\t\t no args puts accounts in local passdb from smb.conf\n" "\t\t ldif - put accounts in ldif format (file defaults to " - "/tmp/tmp.ldif\n"); + "/tmp/tmp.ldif)\n" + "\t\t keytab - put account passwords in krb5 keytab (defaults " + "to system keytab)\n"); net_common_flags_usage(c, argc, argv); return -1; @@ -226,3 +228,74 @@ int rpc_vampire_ldif(struct net_context *c, int argc, const char **argv) return run_rpc_command(c, NULL, PI_NETLOGON, 0, rpc_vampire_ldif_internals, argc, argv); } + + +NTSTATUS rpc_vampire_keytab_internals(struct net_context *c, + const DOM_SID *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + NTSTATUS status; + struct samsync_context *ctx = NULL; + + status = libnet_samsync_init_context(mem_ctx, + domain_sid, + &ctx); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (argc >= 1) { + ctx->output_filename = argv[0]; + } + + ctx->mode = NET_SAMSYNC_MODE_FETCH_KEYTAB; + ctx->cli = pipe_hnd; + ctx->delta_fn = fetch_sam_entries_keytab; + ctx->domain_name = domain_name; + ctx->username = c->opt_user_name; + ctx->password = c->opt_password; + + /* fetch domain */ + status = libnet_samsync(SAM_DATABASE_DOMAIN, ctx); + + if (!NT_STATUS_IS_OK(status) && ctx->error_message) { + d_fprintf(stderr, "%s\n", ctx->error_message); + goto out; + } + + if (ctx->result_message) { + d_fprintf(stdout, "%s\n", ctx->result_message); + } + + out: + TALLOC_FREE(ctx); + + return status; +} + +/** + * Basic function for 'net rpc vampire keytab' + * + * @param c A net_context structure + * @param argc Standard main() style argc + * @param argc Standard main() style argv. Initial components are already + * stripped + **/ + +int rpc_vampire_keytab(struct net_context *c, int argc, const char **argv) +{ + if (c->display_usage) { + d_printf("Usage\n" + "net rpc vampire keytab\n" + " Dump remote SAM database to Kerberos keytab file\n"); + return 0; + } + + return run_rpc_command(c, NULL, PI_NETLOGON, 0, rpc_vampire_keytab_internals, + argc, argv); +} |