From 2f8ac9a4221e4b7731422bd2ce05e59b5d75fb0a Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 9 Apr 2008 12:02:11 +1000 Subject: Start implementation of real 'net vampire' code. This will use DRS Replication (metze's thesis work) and possibly samsync, and will work outside the smbtorture process. Andrew Bartlett (This used to be commit 02a33165ca700f71cf09680ded35c87aa2e88552) --- source4/libnet/config.mk | 1 + source4/libnet/libnet.h | 1 + source4/libnet/libnet_samsync.c | 399 +++++++++++++++++ source4/libnet/libnet_samsync.h | 84 ++++ source4/libnet/libnet_vampire.c | 943 ++++++++++++++++++++++++++-------------- source4/libnet/libnet_vampire.h | 67 +-- 6 files changed, 1120 insertions(+), 375 deletions(-) create mode 100644 source4/libnet/libnet_samsync.c create mode 100644 source4/libnet/libnet_samsync.h (limited to 'source4/libnet') diff --git a/source4/libnet/config.mk b/source4/libnet/config.mk index 00af6b37f2..f7325c0c28 100644 --- a/source4/libnet/config.mk +++ b/source4/libnet/config.mk @@ -10,6 +10,7 @@ OBJ_FILES = \ libnet_become_dc.o \ libnet_unbecome_dc.o \ libnet_vampire.o \ + libnet_samsync.o \ libnet_samdump.o \ libnet_samdump_keytab.o \ libnet_samsync_ldb.o \ diff --git a/source4/libnet/libnet.h b/source4/libnet/libnet.h index 015661a074..b65a13ce37 100644 --- a/source4/libnet/libnet.h +++ b/source4/libnet/libnet.h @@ -68,6 +68,7 @@ struct libnet_context { #include "libnet/libnet_site.h" #include "libnet/libnet_become_dc.h" #include "libnet/libnet_unbecome_dc.h" +#include "libnet/libnet_samsync.h" #include "libnet/libnet_vampire.h" #include "libnet/libnet_user.h" #include "libnet/libnet_group.h" diff --git a/source4/libnet/libnet_samsync.c b/source4/libnet/libnet_samsync.c new file mode 100644 index 0000000000..0f82d98673 --- /dev/null +++ b/source4/libnet/libnet_samsync.c @@ -0,0 +1,399 @@ +/* + Unix SMB/CIFS implementation. + + Extract the user/system database from a remote SamSync server + + Copyright (C) Andrew Bartlett 2004-2005 + + 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 . +*/ + + +#include "includes.h" +#include "libnet/libnet.h" +#include "libcli/auth/libcli_auth.h" +#include "auth/gensec/gensec.h" +#include "auth/credentials/credentials.h" +#include "auth/gensec/schannel_proto.h" +#include "librpc/gen_ndr/ndr_netlogon.h" +#include "librpc/gen_ndr/ndr_netlogon_c.h" +#include "param/param.h" + + +/** + * Decrypt and extract the user's passwords. + * + * The writes decrypted (no longer 'RID encrypted' or arcfour encrypted) passwords back into the structure + */ +static NTSTATUS fix_user(TALLOC_CTX *mem_ctx, + struct creds_CredentialState *creds, + bool rid_crypt, + enum netr_SamDatabaseID database, + struct netr_DELTA_ENUM *delta, + char **error_string) +{ + + uint32_t rid = delta->delta_id_union.rid; + struct netr_DELTA_USER *user = delta->delta_union.user; + struct samr_Password lm_hash; + struct samr_Password nt_hash; + const char *username = user->account_name.string; + + if (rid_crypt) { + if (user->lm_password_present) { + sam_rid_crypt(rid, user->lmpassword.hash, lm_hash.hash, 0); + user->lmpassword = lm_hash; + } + + if (user->nt_password_present) { + sam_rid_crypt(rid, user->ntpassword.hash, nt_hash.hash, 0); + user->ntpassword = nt_hash; + } + } + + if (user->user_private_info.SensitiveData) { + DATA_BLOB data; + struct netr_USER_KEYS keys; + enum ndr_err_code ndr_err; + data.data = user->user_private_info.SensitiveData; + data.length = user->user_private_info.DataLength; + creds_arcfour_crypt(creds, data.data, data.length); + user->user_private_info.SensitiveData = data.data; + user->user_private_info.DataLength = data.length; + + ndr_err = ndr_pull_struct_blob(&data, mem_ctx, NULL, &keys, (ndr_pull_flags_fn_t)ndr_pull_netr_USER_KEYS); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + *error_string = talloc_asprintf(mem_ctx, "Failed to parse Sensitive Data for %s:", username); + dump_data(10, data.data, data.length); + return ndr_map_error2ntstatus(ndr_err); + } + + if (keys.keys.keys2.lmpassword.length == 16) { + if (rid_crypt) { + sam_rid_crypt(rid, keys.keys.keys2.lmpassword.pwd.hash, lm_hash.hash, 0); + user->lmpassword = lm_hash; + } else { + user->lmpassword = keys.keys.keys2.lmpassword.pwd; + } + user->lm_password_present = true; + } + if (keys.keys.keys2.ntpassword.length == 16) { + if (rid_crypt) { + sam_rid_crypt(rid, keys.keys.keys2.ntpassword.pwd.hash, nt_hash.hash, 0); + user->ntpassword = nt_hash; + } else { + user->ntpassword = keys.keys.keys2.ntpassword.pwd; + } + user->nt_password_present = true; + } + /* TODO: rid decrypt history fields */ + } + return NT_STATUS_OK; +} + +/** + * Decrypt and extract the secrets + * + * The writes decrypted secrets back into the structure + */ +static NTSTATUS fix_secret(TALLOC_CTX *mem_ctx, + struct creds_CredentialState *creds, + enum netr_SamDatabaseID database, + struct netr_DELTA_ENUM *delta, + char **error_string) +{ + struct netr_DELTA_SECRET *secret = delta->delta_union.secret; + creds_arcfour_crypt(creds, secret->current_cipher.cipher_data, + secret->current_cipher.maxlen); + + creds_arcfour_crypt(creds, secret->old_cipher.cipher_data, + secret->old_cipher.maxlen); + + return NT_STATUS_OK; +} + +/** + * Fix up the delta, dealing with encryption issues so that the final + * callback need only do the printing or application logic + */ + +static NTSTATUS fix_delta(TALLOC_CTX *mem_ctx, + struct creds_CredentialState *creds, + bool rid_crypt, + enum netr_SamDatabaseID database, + struct netr_DELTA_ENUM *delta, + char **error_string) +{ + NTSTATUS nt_status = NT_STATUS_OK; + *error_string = NULL; + switch (delta->delta_type) { + case NETR_DELTA_USER: + { + nt_status = fix_user(mem_ctx, + creds, + rid_crypt, + database, + delta, + error_string); + break; + } + case NETR_DELTA_SECRET: + { + nt_status = fix_secret(mem_ctx, + creds, + database, + delta, + error_string); + break; + } + default: + break; + } + return nt_status; +} + +NTSTATUS libnet_SamSync_netlogon(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_SamSync *r) +{ + NTSTATUS nt_status, dbsync_nt_status; + TALLOC_CTX *samsync_ctx, *loop_ctx, *delta_ctx; + struct creds_CredentialState *creds; + struct netr_DatabaseSync dbsync; + struct cli_credentials *machine_account; + struct dcerpc_pipe *p; + struct libnet_context *machine_net_ctx; + struct libnet_RpcConnect *c; + struct libnet_SamSync_state *state; + const enum netr_SamDatabaseID database_ids[] = {SAM_DATABASE_DOMAIN, SAM_DATABASE_BUILTIN, SAM_DATABASE_PRIVS}; + int i; + + samsync_ctx = talloc_named(mem_ctx, 0, "SamSync top context"); + + if (!r->in.machine_account) { + machine_account = cli_credentials_init(samsync_ctx); + if (!machine_account) { + talloc_free(samsync_ctx); + return NT_STATUS_NO_MEMORY; + } + cli_credentials_set_conf(machine_account, ctx->lp_ctx); + nt_status = cli_credentials_set_machine_account(machine_account, ctx->lp_ctx); + if (!NT_STATUS_IS_OK(nt_status)) { + r->out.error_string = talloc_strdup(mem_ctx, "Could not obtain machine account password - are we joined to the domain?"); + talloc_free(samsync_ctx); + return nt_status; + } + } else { + machine_account = r->in.machine_account; + } + + /* We cannot do this unless we are a BDC. Check, before we get odd errors later */ + if (cli_credentials_get_secure_channel_type(machine_account) != SEC_CHAN_BDC) { + r->out.error_string + = talloc_asprintf(mem_ctx, + "Our join to domain %s is not as a BDC (%d), please rejoin as a BDC", + cli_credentials_get_domain(machine_account), + cli_credentials_get_secure_channel_type(machine_account)); + talloc_free(samsync_ctx); + return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; + } + + c = talloc(samsync_ctx, struct libnet_RpcConnect); + if (!c) { + r->out.error_string = NULL; + talloc_free(samsync_ctx); + return NT_STATUS_NO_MEMORY; + } + + c->level = LIBNET_RPC_CONNECT_DC_INFO; + if (r->in.binding_string) { + c->in.binding = r->in.binding_string; + c->in.name = NULL; + } else { + c->in.binding = NULL; + c->in.name = cli_credentials_get_domain(machine_account); + } + + /* prepare connect to the NETLOGON pipe of PDC */ + c->in.dcerpc_iface = &ndr_table_netlogon; + + /* We must do this as the machine, not as any command-line + * user. So we override the credentials in the + * libnet_context */ + machine_net_ctx = talloc(samsync_ctx, struct libnet_context); + if (!machine_net_ctx) { + r->out.error_string = NULL; + talloc_free(samsync_ctx); + return NT_STATUS_NO_MEMORY; + } + *machine_net_ctx = *ctx; + machine_net_ctx->cred = machine_account; + + /* connect to the NETLOGON pipe of the PDC */ + nt_status = libnet_RpcConnect(machine_net_ctx, samsync_ctx, c); + if (!NT_STATUS_IS_OK(nt_status)) { + if (r->in.binding_string) { + r->out.error_string = talloc_asprintf(mem_ctx, + "Connection to NETLOGON pipe of DC %s failed: %s", + r->in.binding_string, c->out.error_string); + } else { + r->out.error_string = talloc_asprintf(mem_ctx, + "Connection to NETLOGON pipe of DC for %s failed: %s", + c->in.name, c->out.error_string); + } + talloc_free(samsync_ctx); + return nt_status; + } + + /* This makes a new pipe, on which we can do schannel. We + * should do this in the RpcConnect code, but the abstaction + * layers do not suit yet */ + + nt_status = dcerpc_secondary_connection(c->out.dcerpc_pipe, &p, + c->out.dcerpc_pipe->binding); + + if (!NT_STATUS_IS_OK(nt_status)) { + r->out.error_string = talloc_asprintf(mem_ctx, + "Secondary connection to NETLOGON pipe of DC %s failed: %s", + dcerpc_server_name(p), nt_errstr(nt_status)); + talloc_free(samsync_ctx); + return nt_status; + } + + nt_status = dcerpc_bind_auth_schannel(samsync_ctx, p, &ndr_table_netlogon, + machine_account, ctx->lp_ctx, DCERPC_AUTH_LEVEL_PRIVACY); + + if (!NT_STATUS_IS_OK(nt_status)) { + r->out.error_string = talloc_asprintf(mem_ctx, + "SCHANNEL authentication to NETLOGON pipe of DC %s failed: %s", + dcerpc_server_name(p), nt_errstr(nt_status)); + talloc_free(samsync_ctx); + return nt_status; + } + + state = talloc(samsync_ctx, struct libnet_SamSync_state); + if (!state) { + r->out.error_string = NULL; + talloc_free(samsync_ctx); + return nt_status; + } + + state->domain_name = c->out.domain_name; + state->domain_sid = c->out.domain_sid; + state->realm = c->out.realm; + state->domain_guid = c->out.guid; + state->machine_net_ctx = machine_net_ctx; + state->netlogon_pipe = p; + + /* initialise the callback layer. It may wish to contact the + * server with ldap, now we know the name */ + + if (r->in.init_fn) { + char *error_string; + nt_status = r->in.init_fn(samsync_ctx, + r->in.fn_ctx, + state, + &error_string); + if (!NT_STATUS_IS_OK(nt_status)) { + r->out.error_string = talloc_steal(mem_ctx, error_string); + talloc_free(samsync_ctx); + return nt_status; + } + } + + /* get NETLOGON credentials */ + + nt_status = dcerpc_schannel_creds(p->conn->security_state.generic_state, samsync_ctx, &creds); + if (!NT_STATUS_IS_OK(nt_status)) { + r->out.error_string = talloc_strdup(mem_ctx, "Could not obtain NETLOGON credentials from DCERPC/GENSEC layer"); + talloc_free(samsync_ctx); + return nt_status; + } + + /* Setup details for the synchronisation */ + dbsync.in.logon_server = talloc_asprintf(samsync_ctx, "\\\\%s", dcerpc_server_name(p)); + dbsync.in.computername = cli_credentials_get_workstation(machine_account); + dbsync.in.preferredmaximumlength = (uint32_t)-1; + ZERO_STRUCT(dbsync.in.return_authenticator); + + for (i=0;i< ARRAY_SIZE(database_ids); i++) { + dbsync.in.sync_context = 0; + dbsync.in.database_id = database_ids[i]; + + do { + int d; + loop_ctx = talloc_named(samsync_ctx, 0, "DatabaseSync loop context"); + creds_client_authenticator(creds, &dbsync.in.credential); + + dbsync_nt_status = dcerpc_netr_DatabaseSync(p, loop_ctx, &dbsync); + if (!NT_STATUS_IS_OK(dbsync_nt_status) && + !NT_STATUS_EQUAL(dbsync_nt_status, STATUS_MORE_ENTRIES)) { + r->out.error_string = talloc_asprintf(mem_ctx, "DatabaseSync failed - %s", nt_errstr(nt_status)); + talloc_free(samsync_ctx); + return nt_status; + } + + if (!creds_client_check(creds, &dbsync.out.return_authenticator.cred)) { + r->out.error_string = talloc_strdup(mem_ctx, "Credential chaining on incoming DatabaseSync failed"); + talloc_free(samsync_ctx); + return NT_STATUS_ACCESS_DENIED; + } + + dbsync.in.sync_context = dbsync.out.sync_context; + + /* For every single remote 'delta' entry: */ + for (d=0; d < dbsync.out.delta_enum_array->num_deltas; d++) { + char *error_string = NULL; + delta_ctx = talloc_named(loop_ctx, 0, "DatabaseSync delta context"); + /* 'Fix' elements, by decrypting and + * de-obfuscating the data */ + nt_status = fix_delta(delta_ctx, + creds, + r->in.rid_crypt, + dbsync.in.database_id, + &dbsync.out.delta_enum_array->delta_enum[d], + &error_string); + if (!NT_STATUS_IS_OK(nt_status)) { + r->out.error_string = talloc_steal(mem_ctx, error_string); + talloc_free(samsync_ctx); + return nt_status; + } + + /* Now call the callback. This will + * do something like print the data or + * write to an ldb */ + nt_status = r->in.delta_fn(delta_ctx, + r->in.fn_ctx, + dbsync.in.database_id, + &dbsync.out.delta_enum_array->delta_enum[d], + &error_string); + if (!NT_STATUS_IS_OK(nt_status)) { + r->out.error_string = talloc_steal(mem_ctx, error_string); + talloc_free(samsync_ctx); + return nt_status; + } + talloc_free(delta_ctx); + } + talloc_free(loop_ctx); + } while (NT_STATUS_EQUAL(dbsync_nt_status, STATUS_MORE_ENTRIES)); + + if (!NT_STATUS_IS_OK(dbsync_nt_status)) { + r->out.error_string = talloc_asprintf(mem_ctx, "libnet_SamSync_netlogon failed: unexpected inconsistancy. Should not get error %s here", nt_errstr(nt_status)); + talloc_free(samsync_ctx); + return dbsync_nt_status; + } + nt_status = NT_STATUS_OK; + } + talloc_free(samsync_ctx); + return nt_status; +} + diff --git a/source4/libnet/libnet_samsync.h b/source4/libnet/libnet_samsync.h new file mode 100644 index 0000000000..d2ac30fe14 --- /dev/null +++ b/source4/libnet/libnet_samsync.h @@ -0,0 +1,84 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Andrew Bartlett 2005 + + 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 . +*/ + +#include "librpc/gen_ndr/netlogon.h" + +struct libnet_SamSync_state { + struct libnet_context *machine_net_ctx; + struct dcerpc_pipe *netlogon_pipe; + const char *domain_name; + const struct dom_sid *domain_sid; + const char *realm; + struct GUID *domain_guid; +}; + +/* struct and enum for doing a remote domain vampire dump */ +struct libnet_SamSync { + struct { + const char *binding_string; + bool rid_crypt; + NTSTATUS (*init_fn)(TALLOC_CTX *mem_ctx, + void *private, + struct libnet_SamSync_state *samsync_state, + char **error_string); + NTSTATUS (*delta_fn)(TALLOC_CTX *mem_ctx, + void *private, + enum netr_SamDatabaseID database, + struct netr_DELTA_ENUM *delta, + char **error_string); + void *fn_ctx; + struct cli_credentials *machine_account; + } in; + struct { + const char *error_string; + } out; +}; + +struct libnet_SamDump { + struct { + const char *binding_string; + struct cli_credentials *machine_account; + } in; + struct { + const char *error_string; + } out; +}; + +struct libnet_SamDump_keytab { + struct { + const char *binding_string; + const char *keytab_name; + struct cli_credentials *machine_account; + } in; + struct { + const char *error_string; + } out; +}; + +struct libnet_samsync_ldb { + struct { + const char *binding_string; + struct cli_credentials *machine_account; + struct auth_session_info *session_info; + } in; + struct { + const char *error_string; + } out; +}; + diff --git a/source4/libnet/libnet_vampire.c b/source4/libnet/libnet_vampire.c index 0f82d98673..cd9167f541 100644 --- a/source4/libnet/libnet_vampire.c +++ b/source4/libnet/libnet_vampire.c @@ -1,9 +1,11 @@ /* Unix SMB/CIFS implementation. - Extract the user/system database from a remote SamSync server + Extract the user/system database from a remote server - Copyright (C) Andrew Bartlett 2004-2005 + Copyright (C) Stefan Metzmacher 2004-2006 + Copyright (C) Brad Henry 2005 + Copyright (C) Andrew Bartlett 2005-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 @@ -22,378 +24,683 @@ #include "includes.h" #include "libnet/libnet.h" -#include "libcli/auth/libcli_auth.h" -#include "auth/gensec/gensec.h" -#include "auth/credentials/credentials.h" -#include "auth/gensec/schannel_proto.h" -#include "librpc/gen_ndr/ndr_netlogon.h" -#include "librpc/gen_ndr/ndr_netlogon_c.h" +#include "lib/events/events.h" +#include "dsdb/samdb/samdb.h" +#include "lib/util/dlinklist.h" +#include "lib/ldb/include/ldb.h" +#include "lib/ldb/include/ldb_errors.h" +#include "librpc/ndr/libndr.h" +#include "librpc/gen_ndr/ndr_drsuapi.h" +#include "librpc/gen_ndr/ndr_drsblobs.h" +#include "librpc/gen_ndr/ndr_misc.h" +#include "system/time.h" +#include "lib/ldb_wrap.h" +#include "auth/auth.h" #include "param/param.h" +/* +List of tasks vampire.py must perform: +- Domain Join + - but don't write the secrets.ldb + - results for this should be enough to handle the provision +- if vampire method is samsync + - Provision using these results + - do we still want to support this NT4 technology? +- Start samsync with libnet code + - provision in the callback +- Write out the secrets database, using the code from libnet_Join + +*/ +struct vampire_state { + struct libnet_context *ctx; + const char *netbios_name; + struct libnet_JoinDomain *join; + struct cli_credentials *machine_account; + struct dsdb_schema *self_made_schema; + const struct dsdb_schema *schema; + + struct ldb_context *ldb; -/** - * Decrypt and extract the user's passwords. - * - * The writes decrypted (no longer 'RID encrypted' or arcfour encrypted) passwords back into the structure - */ -static NTSTATUS fix_user(TALLOC_CTX *mem_ctx, - struct creds_CredentialState *creds, - bool rid_crypt, - enum netr_SamDatabaseID database, - struct netr_DELTA_ENUM *delta, - char **error_string) + struct { + uint32_t object_count; + struct drsuapi_DsReplicaObjectListItemEx *first_object; + struct drsuapi_DsReplicaObjectListItemEx *last_object; + } schema_part; + + const char *targetdir; + + struct loadparm_context *lp_ctx; +}; + +static NTSTATUS vampire_prepare_db(void *private_data, + const struct libnet_BecomeDC_PrepareDB *p) { + struct vampire_state *s = talloc_get_type(private_data, struct vampire_state); + struct provision_settings settings; + NTSTATUS status; + bool ok; + struct loadparm_context *lp_ctx = loadparm_init(s); + char *smbconf; + + if (!lp_ctx) { + return NT_STATUS_NO_MEMORY; + } - uint32_t rid = delta->delta_id_union.rid; - struct netr_DELTA_USER *user = delta->delta_union.user; - struct samr_Password lm_hash; - struct samr_Password nt_hash; - const char *username = user->account_name.string; + settings.site_name = p->dest_dsa->site_name; + settings.root_dn_str = p->forest->root_dn_str; + settings.domain_dn_str = p->domain->dn_str; + settings.config_dn_str = p->forest->config_dn_str; + settings.schema_dn_str = p->forest->schema_dn_str; + settings.netbios_name = p->dest_dsa->netbios_name; + settings.realm = s->join->out.realm; + settings.domain = s->join->out.domain; + settings.server_dn_str = p->dest_dsa->server_dn_str; + settings.machine_password = generate_random_str(s, 16); + settings.targetdir = s->targetdir; + + status = provision_bare(s, s->lp_ctx, &settings); + + smbconf = talloc_asprintf(lp_ctx, "%s/%s", s->targetdir, "/etc/smb.conf"); - if (rid_crypt) { - if (user->lm_password_present) { - sam_rid_crypt(rid, user->lmpassword.hash, lm_hash.hash, 0); - user->lmpassword = lm_hash; - } - - if (user->nt_password_present) { - sam_rid_crypt(rid, user->ntpassword.hash, nt_hash.hash, 0); - user->ntpassword = nt_hash; - } + ok = lp_load(lp_ctx, smbconf); + if (!ok) { + DEBUG(0,("Failed load freshly generated smb.conf '%s'\n", smbconf)); + return NT_STATUS_INVALID_PARAMETER; } - if (user->user_private_info.SensitiveData) { - DATA_BLOB data; - struct netr_USER_KEYS keys; - enum ndr_err_code ndr_err; - data.data = user->user_private_info.SensitiveData; - data.length = user->user_private_info.DataLength; - creds_arcfour_crypt(creds, data.data, data.length); - user->user_private_info.SensitiveData = data.data; - user->user_private_info.DataLength = data.length; - - ndr_err = ndr_pull_struct_blob(&data, mem_ctx, NULL, &keys, (ndr_pull_flags_fn_t)ndr_pull_netr_USER_KEYS); - if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { - *error_string = talloc_asprintf(mem_ctx, "Failed to parse Sensitive Data for %s:", username); - dump_data(10, data.data, data.length); - return ndr_map_error2ntstatus(ndr_err); + s->ldb = samdb_connect(s, lp_ctx, + system_session(s, lp_ctx)); + if (!s->ldb) { + DEBUG(0,("Failed to open '%s'\n", lp_sam_url(lp_ctx))); + return NT_STATUS_INTERNAL_DB_ERROR; + } + + /* We must set these up to ensure the replMetaData is written correctly, before our NTDS Settings entry is replicated */ + ok = samdb_set_ntds_invocation_id(s->ldb, &p->dest_dsa->invocation_id); + if (!ok) { + DEBUG(0,("Failed to set cached ntds invocationId\n")); + return NT_STATUS_FOOBAR; + } + ok = samdb_set_ntds_objectGUID(s->ldb, &p->dest_dsa->ntds_guid); + if (!ok) { + DEBUG(0,("Failed to set cached ntds objectGUID\n")); + return NT_STATUS_FOOBAR; + } + + s->lp_ctx = lp_ctx; + + return NT_STATUS_OK; + + +} + +static NTSTATUS vampire_check_options(void *private_data, + const struct libnet_BecomeDC_CheckOptions *o) +{ + struct vampire_state *s = talloc_get_type(private_data, struct vampire_state); + + DEBUG(0,("Become DC [%s] of Domain[%s]/[%s]\n", + s->netbios_name, + o->domain->netbios_name, o->domain->dns_name)); + + DEBUG(0,("Promotion Partner is Server[%s] from Site[%s]\n", + o->source_dsa->dns_name, o->source_dsa->site_name)); + + DEBUG(0,("Options:crossRef behavior_version[%u]\n" + "\tschema object_version[%u]\n" + "\tdomain behavior_version[%u]\n" + "\tdomain w2k3_update_revision[%u]\n", + o->forest->crossref_behavior_version, + o->forest->schema_object_version, + o->domain->behavior_version, + o->domain->w2k3_update_revision)); + + return NT_STATUS_OK; +} + +static NTSTATUS vampire_apply_schema(struct vampire_state *s, + const struct libnet_BecomeDC_StoreChunk *c) +{ + WERROR status; + const struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr; + uint32_t total_object_count; + uint32_t object_count; + struct drsuapi_DsReplicaObjectListItemEx *first_object; + struct drsuapi_DsReplicaObjectListItemEx *cur; + uint32_t linked_attributes_count; + struct drsuapi_DsReplicaLinkedAttribute *linked_attributes; + const struct drsuapi_DsReplicaCursor2CtrEx *uptodateness_vector; + struct dsdb_extended_replicated_objects *objs; + struct repsFromTo1 *s_dsa; + char *tmp_dns_name; + struct ldb_message *msg; + struct ldb_val prefixMap_val; + struct ldb_message_element *prefixMap_el; + struct ldb_val schemaInfo_val; + uint32_t i; + int ret; + bool ok; + + DEBUG(0,("Analyze and apply schema objects\n")); + + s_dsa = talloc_zero(s, struct repsFromTo1); + NT_STATUS_HAVE_NO_MEMORY(s_dsa); + s_dsa->other_info = talloc(s_dsa, struct repsFromTo1OtherInfo); + NT_STATUS_HAVE_NO_MEMORY(s_dsa->other_info); + + switch (c->ctr_level) { + case 1: + mapping_ctr = &c->ctr1->mapping_ctr; + total_object_count = c->ctr1->total_object_count; + object_count = s->schema_part.object_count; + first_object = s->schema_part.first_object; + linked_attributes_count = 0; + linked_attributes = NULL; + s_dsa->highwatermark = c->ctr1->new_highwatermark; + s_dsa->source_dsa_obj_guid = c->ctr1->source_dsa_guid; + s_dsa->source_dsa_invocation_id = c->ctr1->source_dsa_invocation_id; + uptodateness_vector = NULL; /* TODO: map it */ + break; + case 6: + mapping_ctr = &c->ctr6->mapping_ctr; + total_object_count = c->ctr6->total_object_count; + object_count = s->schema_part.object_count; + first_object = s->schema_part.first_object; + linked_attributes_count = 0; /* TODO: ! */ + linked_attributes = NULL; /* TODO: ! */; + s_dsa->highwatermark = c->ctr6->new_highwatermark; + s_dsa->source_dsa_obj_guid = c->ctr6->source_dsa_guid; + s_dsa->source_dsa_invocation_id = c->ctr6->source_dsa_invocation_id; + uptodateness_vector = c->ctr6->uptodateness_vector; + break; + default: + return NT_STATUS_INVALID_PARAMETER; + } + + s_dsa->replica_flags = DRSUAPI_DS_REPLICA_NEIGHBOUR_WRITEABLE + | DRSUAPI_DS_REPLICA_NEIGHBOUR_SYNC_ON_STARTUP + | DRSUAPI_DS_REPLICA_NEIGHBOUR_DO_SCHEDULED_SYNCS; + memset(s_dsa->schedule, 0x11, sizeof(s_dsa->schedule)); + + tmp_dns_name = GUID_string(s_dsa->other_info, &s_dsa->source_dsa_obj_guid); + NT_STATUS_HAVE_NO_MEMORY(tmp_dns_name); + tmp_dns_name = talloc_asprintf_append_buffer(tmp_dns_name, "._msdcs.%s", c->forest->dns_name); + NT_STATUS_HAVE_NO_MEMORY(tmp_dns_name); + s_dsa->other_info->dns_name = tmp_dns_name; + + for (cur = first_object; cur; cur = cur->next_object) { + bool is_attr = false; + bool is_class = false; + + for (i=0; i < cur->object.attribute_ctr.num_attributes; i++) { + struct drsuapi_DsReplicaAttribute *a; + uint32_t j; + const char *oid = NULL; + + a = &cur->object.attribute_ctr.attributes[i]; + status = dsdb_map_int2oid(s->self_made_schema, a->attid, s, &oid); + if (!W_ERROR_IS_OK(status)) { + return werror_to_ntstatus(status); + } + + switch (a->attid) { + case DRSUAPI_ATTRIBUTE_objectClass: + for (j=0; j < a->value_ctr.num_values; j++) { + uint32_t val = 0xFFFFFFFF; + + if (a->value_ctr.values[i].blob + && a->value_ctr.values[i].blob->length == 4) { + val = IVAL(a->value_ctr.values[i].blob->data,0); + } + + if (val == DRSUAPI_OBJECTCLASS_attributeSchema) { + is_attr = true; + } + if (val == DRSUAPI_OBJECTCLASS_classSchema) { + is_class = true; + } + } + + break; + default: + break; + } } - if (keys.keys.keys2.lmpassword.length == 16) { - if (rid_crypt) { - sam_rid_crypt(rid, keys.keys.keys2.lmpassword.pwd.hash, lm_hash.hash, 0); - user->lmpassword = lm_hash; - } else { - user->lmpassword = keys.keys.keys2.lmpassword.pwd; + if (is_attr) { + struct dsdb_attribute *sa; + + sa = talloc_zero(s->self_made_schema, struct dsdb_attribute); + NT_STATUS_HAVE_NO_MEMORY(sa); + + status = dsdb_attribute_from_drsuapi(s->self_made_schema, &cur->object, s, sa); + if (!W_ERROR_IS_OK(status)) { + return werror_to_ntstatus(status); } - user->lm_password_present = true; + + DLIST_ADD_END(s->self_made_schema->attributes, sa, struct dsdb_attribute *); } - if (keys.keys.keys2.ntpassword.length == 16) { - if (rid_crypt) { - sam_rid_crypt(rid, keys.keys.keys2.ntpassword.pwd.hash, nt_hash.hash, 0); - user->ntpassword = nt_hash; - } else { - user->ntpassword = keys.keys.keys2.ntpassword.pwd; + + if (is_class) { + struct dsdb_class *sc; + + sc = talloc_zero(s->self_made_schema, struct dsdb_class); + NT_STATUS_HAVE_NO_MEMORY(sc); + + status = dsdb_class_from_drsuapi(s->self_made_schema, &cur->object, s, sc); + if (!W_ERROR_IS_OK(status)) { + return werror_to_ntstatus(status); } - user->nt_password_present = true; + + DLIST_ADD_END(s->self_made_schema->classes, sc, struct dsdb_class *); } - /* TODO: rid decrypt history fields */ } - return NT_STATUS_OK; -} -/** - * Decrypt and extract the secrets - * - * The writes decrypted secrets back into the structure - */ -static NTSTATUS fix_secret(TALLOC_CTX *mem_ctx, - struct creds_CredentialState *creds, - enum netr_SamDatabaseID database, - struct netr_DELTA_ENUM *delta, - char **error_string) -{ - struct netr_DELTA_SECRET *secret = delta->delta_union.secret; - creds_arcfour_crypt(creds, secret->current_cipher.cipher_data, - secret->current_cipher.maxlen); + /* attach the schema to the ldb */ + ret = dsdb_set_schema(s->ldb, s->self_made_schema); + if (ret != LDB_SUCCESS) { + return NT_STATUS_FOOBAR; + } + /* we don't want to access the self made schema anymore */ + s->self_made_schema = NULL; + s->schema = dsdb_get_schema(s->ldb); + + status = dsdb_extended_replicated_objects_commit(s->ldb, + c->partition->nc.dn, + mapping_ctr, + object_count, + first_object, + linked_attributes_count, + linked_attributes, + s_dsa, + uptodateness_vector, + c->gensec_skey, + s, &objs); + if (!W_ERROR_IS_OK(status)) { + DEBUG(0,("Failed to commit objects: %s\n", win_errstr(status))); + return werror_to_ntstatus(status); + } - creds_arcfour_crypt(creds, secret->old_cipher.cipher_data, - secret->old_cipher.maxlen); + if (lp_parm_bool(s->lp_ctx, NULL, "become dc", "dump objects", false)) { + for (i=0; i < objs->num_objects; i++) { + struct ldb_ldif ldif; + fprintf(stdout, "#\n"); + ldif.changetype = LDB_CHANGETYPE_NONE; + ldif.msg = objs->objects[i].msg; + ldb_ldif_write_file(s->ldb, stdout, &ldif); + NDR_PRINT_DEBUG(replPropertyMetaDataBlob, objs->objects[i].meta_data); + } + } + + msg = ldb_msg_new(objs); + NT_STATUS_HAVE_NO_MEMORY(msg); + msg->dn = objs->partition_dn; + + status = dsdb_get_oid_mappings_ldb(s->schema, msg, &prefixMap_val, &schemaInfo_val); + if (!W_ERROR_IS_OK(status)) { + DEBUG(0,("Failed dsdb_get_oid_mappings_ldb(%s)\n", win_errstr(status))); + return werror_to_ntstatus(status); + } + + /* we only add prefixMap here, because schemaInfo is a replicated attribute and already applied */ + ret = ldb_msg_add_value(msg, "prefixMap", &prefixMap_val, &prefixMap_el); + if (ret != LDB_SUCCESS) { + return NT_STATUS_FOOBAR; + } + prefixMap_el->flags = LDB_FLAG_MOD_REPLACE; + + ret = ldb_modify(s->ldb, msg); + if (ret != LDB_SUCCESS) { + DEBUG(0,("Failed to add prefixMap and schemaInfo %s\n", ldb_strerror(ret))); + return NT_STATUS_FOOBAR; + } + + talloc_free(s_dsa); + talloc_free(objs); + + /* reopen the ldb */ + talloc_free(s->ldb); /* this also free's the s->schema, because dsdb_set_schema() steals it */ + s->schema = NULL; + + DEBUG(0,("Reopen the SAM LDB with system credentials and a already stored schema\n")); + s->ldb = samdb_connect(s, s->lp_ctx, + system_session(s, s->lp_ctx)); + if (!s->ldb) { + DEBUG(0,("Failed to reopen sam.ldb\n")); + return NT_STATUS_INTERNAL_DB_ERROR; + } + + /* We must set these up to ensure the replMetaData is written correctly, before our NTDS Settings entry is replicated */ + ok = samdb_set_ntds_invocation_id(s->ldb, &c->dest_dsa->invocation_id); + if (!ok) { + DEBUG(0,("Failed to set cached ntds invocationId\n")); + return NT_STATUS_FOOBAR; + } + ok = samdb_set_ntds_objectGUID(s->ldb, &c->dest_dsa->ntds_guid); + if (!ok) { + DEBUG(0,("Failed to set cached ntds objectGUID\n")); + return NT_STATUS_FOOBAR; + } + + s->schema = dsdb_get_schema(s->ldb); + if (!s->schema) { + DEBUG(0,("Failed to get loaded dsdb_schema\n")); + return NT_STATUS_FOOBAR; + } return NT_STATUS_OK; } -/** - * Fix up the delta, dealing with encryption issues so that the final - * callback need only do the printing or application logic - */ - -static NTSTATUS fix_delta(TALLOC_CTX *mem_ctx, - struct creds_CredentialState *creds, - bool rid_crypt, - enum netr_SamDatabaseID database, - struct netr_DELTA_ENUM *delta, - char **error_string) +static NTSTATUS vampire_schema_chunk(void *private_data, + const struct libnet_BecomeDC_StoreChunk *c) { - NTSTATUS nt_status = NT_STATUS_OK; - *error_string = NULL; - switch (delta->delta_type) { - case NETR_DELTA_USER: - { - nt_status = fix_user(mem_ctx, - creds, - rid_crypt, - database, - delta, - error_string); + struct vampire_state *s = talloc_get_type(private_data, struct vampire_state); + WERROR status; + const struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr; + uint32_t total_object_count; + uint32_t object_count; + struct drsuapi_DsReplicaObjectListItemEx *first_object; + struct drsuapi_DsReplicaObjectListItemEx *cur; + + switch (c->ctr_level) { + case 1: + mapping_ctr = &c->ctr1->mapping_ctr; + total_object_count = c->ctr1->total_object_count; + object_count = c->ctr1->object_count; + first_object = c->ctr1->first_object; break; - } - case NETR_DELTA_SECRET: - { - nt_status = fix_secret(mem_ctx, - creds, - database, - delta, - error_string); + case 6: + mapping_ctr = &c->ctr6->mapping_ctr; + total_object_count = c->ctr6->total_object_count; + object_count = c->ctr6->object_count; + first_object = c->ctr6->first_object; break; - } default: - break; + return NT_STATUS_INVALID_PARAMETER; } - return nt_status; -} -NTSTATUS libnet_SamSync_netlogon(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_SamSync *r) -{ - NTSTATUS nt_status, dbsync_nt_status; - TALLOC_CTX *samsync_ctx, *loop_ctx, *delta_ctx; - struct creds_CredentialState *creds; - struct netr_DatabaseSync dbsync; - struct cli_credentials *machine_account; - struct dcerpc_pipe *p; - struct libnet_context *machine_net_ctx; - struct libnet_RpcConnect *c; - struct libnet_SamSync_state *state; - const enum netr_SamDatabaseID database_ids[] = {SAM_DATABASE_DOMAIN, SAM_DATABASE_BUILTIN, SAM_DATABASE_PRIVS}; - int i; - - samsync_ctx = talloc_named(mem_ctx, 0, "SamSync top context"); - - if (!r->in.machine_account) { - machine_account = cli_credentials_init(samsync_ctx); - if (!machine_account) { - talloc_free(samsync_ctx); - return NT_STATUS_NO_MEMORY; + if (total_object_count) { + DEBUG(0,("Schema-DN[%s] objects[%u/%u]\n", + c->partition->nc.dn, object_count, total_object_count)); + } else { + DEBUG(0,("Schema-DN[%s] objects[%u]\n", + c->partition->nc.dn, object_count)); + } + + if (!s->schema) { + s->self_made_schema = dsdb_new_schema(s, lp_iconv_convenience(s->lp_ctx)); + + NT_STATUS_HAVE_NO_MEMORY(s->self_made_schema); + + status = dsdb_load_oid_mappings_drsuapi(s->self_made_schema, mapping_ctr); + if (!W_ERROR_IS_OK(status)) { + return werror_to_ntstatus(status); } - cli_credentials_set_conf(machine_account, ctx->lp_ctx); - nt_status = cli_credentials_set_machine_account(machine_account, ctx->lp_ctx); - if (!NT_STATUS_IS_OK(nt_status)) { - r->out.error_string = talloc_strdup(mem_ctx, "Could not obtain machine account password - are we joined to the domain?"); - talloc_free(samsync_ctx); - return nt_status; + + s->schema = s->self_made_schema; + } else { + status = dsdb_verify_oid_mappings_drsuapi(s->schema, mapping_ctr); + if (!W_ERROR_IS_OK(status)) { + return werror_to_ntstatus(status); } + } + + if (!s->schema_part.first_object) { + s->schema_part.object_count = object_count; + s->schema_part.first_object = talloc_steal(s, first_object); } else { - machine_account = r->in.machine_account; + s->schema_part.object_count += object_count; + s->schema_part.last_object->next_object = talloc_steal(s->schema_part.last_object, + first_object); } + for (cur = first_object; cur->next_object; cur = cur->next_object) {} + s->schema_part.last_object = cur; - /* We cannot do this unless we are a BDC. Check, before we get odd errors later */ - if (cli_credentials_get_secure_channel_type(machine_account) != SEC_CHAN_BDC) { - r->out.error_string - = talloc_asprintf(mem_ctx, - "Our join to domain %s is not as a BDC (%d), please rejoin as a BDC", - cli_credentials_get_domain(machine_account), - cli_credentials_get_secure_channel_type(machine_account)); - talloc_free(samsync_ctx); - return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; + if (c->partition->highwatermark.tmp_highest_usn == c->partition->highwatermark.highest_usn) { + return vampire_apply_schema(s, c); } - c = talloc(samsync_ctx, struct libnet_RpcConnect); - if (!c) { - r->out.error_string = NULL; - talloc_free(samsync_ctx); - return NT_STATUS_NO_MEMORY; + return NT_STATUS_OK; +} + +static NTSTATUS vampire_store_chunk(void *private_data, + const struct libnet_BecomeDC_StoreChunk *c) +{ + struct vampire_state *s = talloc_get_type(private_data, struct vampire_state); + WERROR status; + const struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr; + uint32_t total_object_count; + uint32_t object_count; + struct drsuapi_DsReplicaObjectListItemEx *first_object; + uint32_t linked_attributes_count; + struct drsuapi_DsReplicaLinkedAttribute *linked_attributes; + const struct drsuapi_DsReplicaCursor2CtrEx *uptodateness_vector; + struct dsdb_extended_replicated_objects *objs; + struct repsFromTo1 *s_dsa; + char *tmp_dns_name; + uint32_t i; + + s_dsa = talloc_zero(s, struct repsFromTo1); + NT_STATUS_HAVE_NO_MEMORY(s_dsa); + s_dsa->other_info = talloc(s_dsa, struct repsFromTo1OtherInfo); + NT_STATUS_HAVE_NO_MEMORY(s_dsa->other_info); + + switch (c->ctr_level) { + case 1: + mapping_ctr = &c->ctr1->mapping_ctr; + total_object_count = c->ctr1->total_object_count; + object_count = c->ctr1->object_count; + first_object = c->ctr1->first_object; + linked_attributes_count = 0; + linked_attributes = NULL; + s_dsa->highwatermark = c->ctr1->new_highwatermark; + s_dsa->source_dsa_obj_guid = c->ctr1->source_dsa_guid; + s_dsa->source_dsa_invocation_id = c->ctr1->source_dsa_invocation_id; + uptodateness_vector = NULL; /* TODO: map it */ + break; + case 6: + mapping_ctr = &c->ctr6->mapping_ctr; + total_object_count = c->ctr6->total_object_count; + object_count = c->ctr6->object_count; + first_object = c->ctr6->first_object; + linked_attributes_count = c->ctr6->linked_attributes_count; + linked_attributes = c->ctr6->linked_attributes; + s_dsa->highwatermark = c->ctr6->new_highwatermark; + s_dsa->source_dsa_obj_guid = c->ctr6->source_dsa_guid; + s_dsa->source_dsa_invocation_id = c->ctr6->source_dsa_invocation_id; + uptodateness_vector = c->ctr6->uptodateness_vector; + break; + default: + return NT_STATUS_INVALID_PARAMETER; } - c->level = LIBNET_RPC_CONNECT_DC_INFO; - if (r->in.binding_string) { - c->in.binding = r->in.binding_string; - c->in.name = NULL; + s_dsa->replica_flags = DRSUAPI_DS_REPLICA_NEIGHBOUR_WRITEABLE + | DRSUAPI_DS_REPLICA_NEIGHBOUR_SYNC_ON_STARTUP + | DRSUAPI_DS_REPLICA_NEIGHBOUR_DO_SCHEDULED_SYNCS; + memset(s_dsa->schedule, 0x11, sizeof(s_dsa->schedule)); + + tmp_dns_name = GUID_string(s_dsa->other_info, &s_dsa->source_dsa_obj_guid); + NT_STATUS_HAVE_NO_MEMORY(tmp_dns_name); + tmp_dns_name = talloc_asprintf_append_buffer(tmp_dns_name, "._msdcs.%s", c->forest->dns_name); + NT_STATUS_HAVE_NO_MEMORY(tmp_dns_name); + s_dsa->other_info->dns_name = tmp_dns_name; + + if (total_object_count) { + DEBUG(0,("Partition[%s] objects[%u/%u]\n", + c->partition->nc.dn, object_count, total_object_count)); } else { - c->in.binding = NULL; - c->in.name = cli_credentials_get_domain(machine_account); + DEBUG(0,("Partition[%s] objects[%u]\n", + c->partition->nc.dn, object_count)); } - - /* prepare connect to the NETLOGON pipe of PDC */ - c->in.dcerpc_iface = &ndr_table_netlogon; - - /* We must do this as the machine, not as any command-line - * user. So we override the credentials in the - * libnet_context */ - machine_net_ctx = talloc(samsync_ctx, struct libnet_context); - if (!machine_net_ctx) { - r->out.error_string = NULL; - talloc_free(samsync_ctx); - return NT_STATUS_NO_MEMORY; + + status = dsdb_extended_replicated_objects_commit(s->ldb, + c->partition->nc.dn, + mapping_ctr, + object_count, + first_object, + linked_attributes_count, + linked_attributes, + s_dsa, + uptodateness_vector, + c->gensec_skey, + s, &objs); + if (!W_ERROR_IS_OK(status)) { + DEBUG(0,("Failed to commit objects: %s\n", win_errstr(status))); + return werror_to_ntstatus(status); } - *machine_net_ctx = *ctx; - machine_net_ctx->cred = machine_account; - - /* connect to the NETLOGON pipe of the PDC */ - nt_status = libnet_RpcConnect(machine_net_ctx, samsync_ctx, c); - if (!NT_STATUS_IS_OK(nt_status)) { - if (r->in.binding_string) { - r->out.error_string = talloc_asprintf(mem_ctx, - "Connection to NETLOGON pipe of DC %s failed: %s", - r->in.binding_string, c->out.error_string); - } else { - r->out.error_string = talloc_asprintf(mem_ctx, - "Connection to NETLOGON pipe of DC for %s failed: %s", - c->in.name, c->out.error_string); + + if (lp_parm_bool(s->lp_ctx, NULL, "become dc", "dump objects", false)) { + for (i=0; i < objs->num_objects; i++) { + struct ldb_ldif ldif; + fprintf(stdout, "#\n"); + ldif.changetype = LDB_CHANGETYPE_NONE; + ldif.msg = objs->objects[i].msg; + ldb_ldif_write_file(s->ldb, stdout, &ldif); + NDR_PRINT_DEBUG(replPropertyMetaDataBlob, objs->objects[i].meta_data); } - talloc_free(samsync_ctx); - return nt_status; } + talloc_free(s_dsa); + talloc_free(objs); - /* This makes a new pipe, on which we can do schannel. We - * should do this in the RpcConnect code, but the abstaction - * layers do not suit yet */ + for (i=0; i < linked_attributes_count; i++) { + const struct dsdb_attribute *sa; - nt_status = dcerpc_secondary_connection(c->out.dcerpc_pipe, &p, - c->out.dcerpc_pipe->binding); + if (!linked_attributes[i].identifier) { + return NT_STATUS_FOOBAR; + } - if (!NT_STATUS_IS_OK(nt_status)) { - r->out.error_string = talloc_asprintf(mem_ctx, - "Secondary connection to NETLOGON pipe of DC %s failed: %s", - dcerpc_server_name(p), nt_errstr(nt_status)); - talloc_free(samsync_ctx); - return nt_status; - } + if (!linked_attributes[i].value.blob) { + return NT_STATUS_FOOBAR; + } - nt_status = dcerpc_bind_auth_schannel(samsync_ctx, p, &ndr_table_netlogon, - machine_account, ctx->lp_ctx, DCERPC_AUTH_LEVEL_PRIVACY); + sa = dsdb_attribute_by_attributeID_id(s->schema, + linked_attributes[i].attid); + if (!sa) { + return NT_STATUS_FOOBAR; + } - if (!NT_STATUS_IS_OK(nt_status)) { - r->out.error_string = talloc_asprintf(mem_ctx, - "SCHANNEL authentication to NETLOGON pipe of DC %s failed: %s", - dcerpc_server_name(p), nt_errstr(nt_status)); - talloc_free(samsync_ctx); - return nt_status; + if (lp_parm_bool(s->lp_ctx, NULL, "become dc", "dump objects", false)) { + DEBUG(0,("# %s\n", sa->lDAPDisplayName)); + NDR_PRINT_DEBUG(drsuapi_DsReplicaLinkedAttribute, &linked_attributes[i]); + dump_data(0, + linked_attributes[i].value.blob->data, + linked_attributes[i].value.blob->length); + } } - state = talloc(samsync_ctx, struct libnet_SamSync_state); - if (!state) { - r->out.error_string = NULL; - talloc_free(samsync_ctx); - return nt_status; - } - - state->domain_name = c->out.domain_name; - state->domain_sid = c->out.domain_sid; - state->realm = c->out.realm; - state->domain_guid = c->out.guid; - state->machine_net_ctx = machine_net_ctx; - state->netlogon_pipe = p; - - /* initialise the callback layer. It may wish to contact the - * server with ldap, now we know the name */ + return NT_STATUS_OK; +} + +NTSTATUS libnet_vampire(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, + struct libnet_vampire *r) +{ + struct libnet_JoinDomain *join; + struct libnet_BecomeDC b; + struct libnet_UnbecomeDC u; + struct vampire_state *s; + struct ldb_message *msg; + int ldb_ret; + uint32_t i; + NTSTATUS status; + + const char *account_name; + const char *netbios_name; - if (r->in.init_fn) { - char *error_string; - nt_status = r->in.init_fn(samsync_ctx, - r->in.fn_ctx, - state, - &error_string); - if (!NT_STATUS_IS_OK(nt_status)) { - r->out.error_string = talloc_steal(mem_ctx, error_string); - talloc_free(samsync_ctx); - return nt_status; + r->out.error_string = NULL; + + s = talloc_zero(mem_ctx , struct vampire_state); + if (!s) { + return NT_STATUS_NO_MEMORY; + } + + join = talloc_zero(s, struct libnet_JoinDomain); + if (!join) { + return NT_STATUS_NO_MEMORY; + } + + if (r->in.netbios_name != NULL) { + netbios_name = r->in.netbios_name; + } else { + netbios_name = talloc_reference(join, lp_netbios_name(ctx->lp_ctx)); + if (!netbios_name) { + r->out.error_string = NULL; + talloc_free(s); + return NT_STATUS_NO_MEMORY; } } - /* get NETLOGON credentials */ + account_name = talloc_asprintf(join, "%s$", netbios_name); + if (!account_name) { + r->out.error_string = NULL; + talloc_free(s); + return NT_STATUS_NO_MEMORY; + } + + join->in.domain_name = r->in.domain_name; + join->in.account_name = account_name; + join->in.netbios_name = netbios_name; + join->in.level = LIBNET_JOINDOMAIN_AUTOMATIC; + join->in.acct_type = ACB_WSTRUST; + join->in.recreate_account = false; + status = libnet_JoinDomain(ctx, join, join); + if (!NT_STATUS_IS_OK(status)) { + r->out.error_string = talloc_steal(mem_ctx, join->out.error_string); + talloc_free(s); + return status; + } + + s->join = join; + + ZERO_STRUCT(b); + b.in.domain_dns_name = join->out.realm; + b.in.domain_netbios_name = join->out.domain_name; + b.in.domain_sid = join->out.domain_sid; + b.in.source_dsa_address = join->out.samr_binding->host; + b.in.dest_dsa_netbios_name = netbios_name; + + b.in.callbacks.private_data = s; + b.in.callbacks.check_options = vampire_check_options; + b.in.callbacks.prepare_db = vampire_prepare_db; + b.in.callbacks.schema_chunk = vampire_schema_chunk; + b.in.callbacks.config_chunk = vampire_store_chunk; + b.in.callbacks.domain_chunk = vampire_store_chunk; + + status = libnet_BecomeDC(s->ctx, s, &b); + if (!NT_STATUS_IS_OK(status)) { + printf("libnet_BecomeDC() failed - %s\n", nt_errstr(status)); + talloc_free(s); + return status; + } - nt_status = dcerpc_schannel_creds(p->conn->security_state.generic_state, samsync_ctx, &creds); - if (!NT_STATUS_IS_OK(nt_status)) { - r->out.error_string = talloc_strdup(mem_ctx, "Could not obtain NETLOGON credentials from DCERPC/GENSEC layer"); - talloc_free(samsync_ctx); - return nt_status; + msg = ldb_msg_new(s); + if (!msg) { + printf("ldb_msg_new() failed\n"); + talloc_free(s); + return NT_STATUS_NO_MEMORY; + } + msg->dn = ldb_dn_new(msg, s->ldb, "@ROOTDSE"); + if (!msg->dn) { + printf("ldb_msg_new(@ROOTDSE) failed\n"); + talloc_free(s); + return NT_STATUS_NO_MEMORY; } - /* Setup details for the synchronisation */ - dbsync.in.logon_server = talloc_asprintf(samsync_ctx, "\\\\%s", dcerpc_server_name(p)); - dbsync.in.computername = cli_credentials_get_workstation(machine_account); - dbsync.in.preferredmaximumlength = (uint32_t)-1; - ZERO_STRUCT(dbsync.in.return_authenticator); + ldb_ret = ldb_msg_add_string(msg, "isSynchronized", "TRUE"); + if (ldb_ret != LDB_SUCCESS) { + printf("ldb_msg_add_string(msg, isSynchronized, TRUE) failed: %d\n", ldb_ret); + talloc_free(s); + return NT_STATUS_NO_MEMORY; + } - for (i=0;i< ARRAY_SIZE(database_ids); i++) { - dbsync.in.sync_context = 0; - dbsync.in.database_id = database_ids[i]; - - do { - int d; - loop_ctx = talloc_named(samsync_ctx, 0, "DatabaseSync loop context"); - creds_client_authenticator(creds, &dbsync.in.credential); - - dbsync_nt_status = dcerpc_netr_DatabaseSync(p, loop_ctx, &dbsync); - if (!NT_STATUS_IS_OK(dbsync_nt_status) && - !NT_STATUS_EQUAL(dbsync_nt_status, STATUS_MORE_ENTRIES)) { - r->out.error_string = talloc_asprintf(mem_ctx, "DatabaseSync failed - %s", nt_errstr(nt_status)); - talloc_free(samsync_ctx); - return nt_status; - } - - if (!creds_client_check(creds, &dbsync.out.return_authenticator.cred)) { - r->out.error_string = talloc_strdup(mem_ctx, "Credential chaining on incoming DatabaseSync failed"); - talloc_free(samsync_ctx); - return NT_STATUS_ACCESS_DENIED; - } - - dbsync.in.sync_context = dbsync.out.sync_context; - - /* For every single remote 'delta' entry: */ - for (d=0; d < dbsync.out.delta_enum_array->num_deltas; d++) { - char *error_string = NULL; - delta_ctx = talloc_named(loop_ctx, 0, "DatabaseSync delta context"); - /* 'Fix' elements, by decrypting and - * de-obfuscating the data */ - nt_status = fix_delta(delta_ctx, - creds, - r->in.rid_crypt, - dbsync.in.database_id, - &dbsync.out.delta_enum_array->delta_enum[d], - &error_string); - if (!NT_STATUS_IS_OK(nt_status)) { - r->out.error_string = talloc_steal(mem_ctx, error_string); - talloc_free(samsync_ctx); - return nt_status; - } + for (i=0; i < msg->num_elements; i++) { + msg->elements[i].flags = LDB_FLAG_MOD_REPLACE; + } - /* Now call the callback. This will - * do something like print the data or - * write to an ldb */ - nt_status = r->in.delta_fn(delta_ctx, - r->in.fn_ctx, - dbsync.in.database_id, - &dbsync.out.delta_enum_array->delta_enum[d], - &error_string); - if (!NT_STATUS_IS_OK(nt_status)) { - r->out.error_string = talloc_steal(mem_ctx, error_string); - talloc_free(samsync_ctx); - return nt_status; - } - talloc_free(delta_ctx); - } - talloc_free(loop_ctx); - } while (NT_STATUS_EQUAL(dbsync_nt_status, STATUS_MORE_ENTRIES)); - - if (!NT_STATUS_IS_OK(dbsync_nt_status)) { - r->out.error_string = talloc_asprintf(mem_ctx, "libnet_SamSync_netlogon failed: unexpected inconsistancy. Should not get error %s here", nt_errstr(nt_status)); - talloc_free(samsync_ctx); - return dbsync_nt_status; - } - nt_status = NT_STATUS_OK; + printf("mark ROOTDSE with isSynchronized=TRUE\n"); + ldb_ret = ldb_modify(s->ldb, msg); + if (ldb_ret != LDB_SUCCESS) { + printf("ldb_modify() failed: %d\n", ldb_ret); + talloc_free(s); + return NT_STATUS_INTERNAL_DB_ERROR; } - talloc_free(samsync_ctx); - return nt_status; } - diff --git a/source4/libnet/libnet_vampire.h b/source4/libnet/libnet_vampire.h index d2ac30fe14..af461139ff 100644 --- a/source4/libnet/libnet_vampire.h +++ b/source4/libnet/libnet_vampire.h @@ -1,7 +1,9 @@ /* Unix SMB/CIFS implementation. - + + Copyright (C) Stefan Metzmacher 2004 Copyright (C) Andrew Bartlett 2005 + Copyright (C) Brad Henry 2005 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 @@ -17,68 +19,19 @@ along with this program. If not, see . */ -#include "librpc/gen_ndr/netlogon.h" - -struct libnet_SamSync_state { - struct libnet_context *machine_net_ctx; - struct dcerpc_pipe *netlogon_pipe; - const char *domain_name; - const struct dom_sid *domain_sid; - const char *realm; - struct GUID *domain_guid; -}; - -/* struct and enum for doing a remote domain vampire dump */ -struct libnet_SamSync { - struct { - const char *binding_string; - bool rid_crypt; - NTSTATUS (*init_fn)(TALLOC_CTX *mem_ctx, - void *private, - struct libnet_SamSync_state *samsync_state, - char **error_string); - NTSTATUS (*delta_fn)(TALLOC_CTX *mem_ctx, - void *private, - enum netr_SamDatabaseID database, - struct netr_DELTA_ENUM *delta, - char **error_string); - void *fn_ctx; - struct cli_credentials *machine_account; - } in; - struct { - const char *error_string; - } out; -}; - -struct libnet_SamDump { - struct { - const char *binding_string; - struct cli_credentials *machine_account; - } in; - struct { - const char *error_string; - } out; -}; +#ifndef __LIBNET_VAMPIRE_H__ +#define __LIBNET_VAMPIRE_H__ -struct libnet_SamDump_keytab { +struct libnet_vampire { struct { - const char *binding_string; - const char *keytab_name; - struct cli_credentials *machine_account; + const char *domain_name; + const char *netbios_name; } in; + struct { const char *error_string; } out; }; -struct libnet_samsync_ldb { - struct { - const char *binding_string; - struct cli_credentials *machine_account; - struct auth_session_info *session_info; - } in; - struct { - const char *error_string; - } out; -}; +#endif /* __LIBNET_VAMPIRE_H__ */ -- cgit From 29b25a1b043b1564a25765d553f3950cd5edb782 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 9 Apr 2008 14:56:24 +1000 Subject: Factor out filling in the secrets database. This allows the vampire code to start with a join, but fill in the secrets only when the process is compleated. Andrew Bartlett (This used to be commit c90751040e941d10234131852815e1cec1a54efe) --- source4/libnet/libnet_join.c | 196 +++++++++++++++++++++++++------------------ source4/libnet/libnet_join.h | 17 ++++ 2 files changed, 133 insertions(+), 80 deletions(-) (limited to 'source4/libnet') diff --git a/source4/libnet/libnet_join.c b/source4/libnet/libnet_join.c index 22134518d6..4549cd6e93 100644 --- a/source4/libnet/libnet_join.c +++ b/source4/libnet/libnet_join.c @@ -839,13 +839,11 @@ NTSTATUS libnet_JoinDomain(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, stru return status; } -static NTSTATUS libnet_Join_primary_domain(struct libnet_context *ctx, - TALLOC_CTX *mem_ctx, - struct libnet_Join *r) +NTSTATUS libnet_set_join_secrets(struct libnet_context *ctx, + TALLOC_CTX *mem_ctx, + struct libnet_set_join_secrets *r) { - NTSTATUS status; TALLOC_CTX *tmp_mem; - struct libnet_JoinDomain *r2; int ret, rtn; struct ldb_context *ldb; struct ldb_dn *base_dn; @@ -860,56 +858,13 @@ static NTSTATUS libnet_Join_primary_domain(struct libnet_context *ctx, "privateKeytab", NULL }; - uint32_t acct_type = 0; - const char *account_name; - const char *netbios_name; - - r->out.error_string = NULL; tmp_mem = talloc_new(mem_ctx); if (!tmp_mem) { return NT_STATUS_NO_MEMORY; } - r2 = talloc(tmp_mem, struct libnet_JoinDomain); - if (!r2) { - r->out.error_string = NULL; - talloc_free(tmp_mem); - return NT_STATUS_NO_MEMORY; - } - - if (r->in.join_type == SEC_CHAN_BDC) { - acct_type = ACB_SVRTRUST; - } else if (r->in.join_type == SEC_CHAN_WKSTA) { - acct_type = ACB_WSTRUST; - } else { - r->out.error_string = NULL; - talloc_free(tmp_mem); - return NT_STATUS_INVALID_PARAMETER; - } - - if (r->in.netbios_name != NULL) { - netbios_name = r->in.netbios_name; - } else { - netbios_name = talloc_reference(tmp_mem, lp_netbios_name(ctx->lp_ctx)); - if (!netbios_name) { - r->out.error_string = NULL; - talloc_free(tmp_mem); - return NT_STATUS_NO_MEMORY; - } - } - - account_name = talloc_asprintf(tmp_mem, "%s$", netbios_name); - if (!account_name) { - r->out.error_string = NULL; - talloc_free(tmp_mem); - return NT_STATUS_NO_MEMORY; - } - - /* - * Local secrets are stored in secrets.ldb - * open it to make sure we can write the info into it after the join - */ + /* Open the secrets database */ ldb = secrets_db_connect(tmp_mem, ctx->lp_ctx); if (!ldb) { r->out.error_string @@ -919,23 +874,6 @@ static NTSTATUS libnet_Join_primary_domain(struct libnet_context *ctx, return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; } - /* - * join the domain - */ - ZERO_STRUCTP(r2); - r2->in.domain_name = r->in.domain_name; - r2->in.account_name = account_name; - r2->in.netbios_name = netbios_name; - r2->in.level = LIBNET_JOINDOMAIN_AUTOMATIC; - r2->in.acct_type = acct_type; - r2->in.recreate_account = false; - status = libnet_JoinDomain(ctx, r2, r2); - if (!NT_STATUS_IS_OK(status)) { - r->out.error_string = talloc_steal(mem_ctx, r2->out.error_string); - talloc_free(tmp_mem); - return status; - } - /* * now prepare the record for secrets.ldb */ @@ -961,21 +899,21 @@ static NTSTATUS libnet_Join_primary_domain(struct libnet_context *ctx, } msg->dn = ldb_dn_copy(tmp_mem, base_dn); - if ( ! ldb_dn_add_child_fmt(msg->dn, "flatname=%s", r2->out.domain_name)) { + if ( ! ldb_dn_add_child_fmt(msg->dn, "flatname=%s", r->in.domain_name)) { r->out.error_string = NULL; talloc_free(tmp_mem); return NT_STATUS_NO_MEMORY; } - rtn = samdb_msg_add_string(ldb, tmp_mem, msg, "flatname", r2->out.domain_name); + rtn = samdb_msg_add_string(ldb, tmp_mem, msg, "flatname", r->in.domain_name); if (rtn == -1) { r->out.error_string = NULL; talloc_free(tmp_mem); return NT_STATUS_NO_MEMORY; } - if (r2->out.realm) { - rtn = samdb_msg_add_string(ldb, tmp_mem, msg, "realm", r2->out.realm); + if (r->in.realm) { + rtn = samdb_msg_add_string(ldb, tmp_mem, msg, "realm", r->in.realm); if (rtn == -1) { r->out.error_string = NULL; talloc_free(tmp_mem); @@ -997,14 +935,14 @@ static NTSTATUS libnet_Join_primary_domain(struct libnet_context *ctx, return NT_STATUS_NO_MEMORY; } - rtn = samdb_msg_add_string(ldb, tmp_mem, msg, "secret", r2->out.join_password); + rtn = samdb_msg_add_string(ldb, tmp_mem, msg, "secret", r->in.join_password); if (rtn == -1) { r->out.error_string = NULL; talloc_free(tmp_mem); return NT_STATUS_NO_MEMORY; } - rtn = samdb_msg_add_string(ldb, tmp_mem, msg, "samAccountName", r2->in.account_name); + rtn = samdb_msg_add_string(ldb, tmp_mem, msg, "samAccountName", r->in.account_name); if (rtn == -1) { r->out.error_string = NULL; talloc_free(tmp_mem); @@ -1018,9 +956,9 @@ static NTSTATUS libnet_Join_primary_domain(struct libnet_context *ctx, return NT_STATUS_NO_MEMORY; } - if (r2->out.kvno) { + if (r->in.kvno) { rtn = samdb_msg_add_uint(ldb, tmp_mem, msg, "msDS-KeyVersionNumber", - r2->out.kvno); + r->in.kvno); if (rtn == -1) { r->out.error_string = NULL; talloc_free(tmp_mem); @@ -1028,9 +966,9 @@ static NTSTATUS libnet_Join_primary_domain(struct libnet_context *ctx, } } - if (r2->out.domain_sid) { + if (r->in.domain_sid) { rtn = samdb_msg_add_dom_sid(ldb, tmp_mem, msg, "objectSid", - r2->out.domain_sid); + r->in.domain_sid); if (rtn == -1) { r->out.error_string = NULL; talloc_free(tmp_mem); @@ -1047,7 +985,7 @@ static NTSTATUS libnet_Join_primary_domain(struct libnet_context *ctx, tmp_mem, base_dn, &msgs, attrs, "(|" SECRETS_PRIMARY_DOMAIN_FILTER "(realm=%s))", - r2->out.domain_name, r2->out.realm); + r->in.domain_name, r->in.realm); if (ret == 0) { rtn = samdb_msg_set_string(ldb, tmp_mem, msg, "secretsKeytab", "secrets.keytab"); if (rtn == -1) { @@ -1059,7 +997,7 @@ static NTSTATUS libnet_Join_primary_domain(struct libnet_context *ctx, r->out.error_string = talloc_asprintf(mem_ctx, "Search for domain: %s and realm: %s failed: %s", - r2->out.domain_name, r2->out.realm, ldb_errstring(ldb)); + r->in.domain_name, r->in.realm, ldb_errstring(ldb)); talloc_free(tmp_mem); return NT_STATUS_INTERNAL_DB_CORRUPTION; } else { @@ -1082,7 +1020,7 @@ static NTSTATUS libnet_Join_primary_domain(struct libnet_context *ctx, return NT_STATUS_NO_MEMORY; } } - rtn = samdb_msg_set_string(ldb, tmp_mem, msg, "secret", r2->out.join_password); + rtn = samdb_msg_set_string(ldb, tmp_mem, msg, "secret", r->in.join_password); if (rtn == -1) { r->out.error_string = NULL; talloc_free(tmp_mem); @@ -1101,7 +1039,7 @@ static NTSTATUS libnet_Join_primary_domain(struct libnet_context *ctx, } } - rtn = samdb_msg_set_string(ldb, tmp_mem, msg, "samAccountName", r2->in.account_name); + rtn = samdb_msg_set_string(ldb, tmp_mem, msg, "samAccountName", r->in.account_name); if (rtn == -1) { r->out.error_string = NULL; talloc_free(tmp_mem); @@ -1146,6 +1084,104 @@ static NTSTATUS libnet_Join_primary_domain(struct libnet_context *ctx, return NT_STATUS_INTERNAL_DB_CORRUPTION; } + return NT_STATUS_OK; +} + +static NTSTATUS libnet_Join_primary_domain(struct libnet_context *ctx, + TALLOC_CTX *mem_ctx, + struct libnet_Join *r) +{ + NTSTATUS status; + TALLOC_CTX *tmp_mem; + struct libnet_JoinDomain *r2; + struct libnet_set_join_secrets *r3; + uint32_t acct_type = 0; + const char *account_name; + const char *netbios_name; + + r->out.error_string = NULL; + + tmp_mem = talloc_new(mem_ctx); + if (!tmp_mem) { + return NT_STATUS_NO_MEMORY; + } + + r2 = talloc(tmp_mem, struct libnet_JoinDomain); + if (!r2) { + r->out.error_string = NULL; + talloc_free(tmp_mem); + return NT_STATUS_NO_MEMORY; + } + + if (r->in.join_type == SEC_CHAN_BDC) { + acct_type = ACB_SVRTRUST; + } else if (r->in.join_type == SEC_CHAN_WKSTA) { + acct_type = ACB_WSTRUST; + } else { + r->out.error_string = NULL; + talloc_free(tmp_mem); + return NT_STATUS_INVALID_PARAMETER; + } + + if (r->in.netbios_name != NULL) { + netbios_name = r->in.netbios_name; + } else { + netbios_name = talloc_reference(tmp_mem, lp_netbios_name(ctx->lp_ctx)); + if (!netbios_name) { + r->out.error_string = NULL; + talloc_free(tmp_mem); + return NT_STATUS_NO_MEMORY; + } + } + + account_name = talloc_asprintf(tmp_mem, "%s$", netbios_name); + if (!account_name) { + r->out.error_string = NULL; + talloc_free(tmp_mem); + return NT_STATUS_NO_MEMORY; + } + + /* + * join the domain + */ + ZERO_STRUCTP(r2); + r2->in.domain_name = r->in.domain_name; + r2->in.account_name = account_name; + r2->in.netbios_name = netbios_name; + r2->in.level = LIBNET_JOINDOMAIN_AUTOMATIC; + r2->in.acct_type = acct_type; + r2->in.recreate_account = false; + status = libnet_JoinDomain(ctx, r2, r2); + if (!NT_STATUS_IS_OK(status)) { + r->out.error_string = talloc_steal(mem_ctx, r2->out.error_string); + talloc_free(tmp_mem); + return status; + } + + r3 = talloc(tmp_mem, struct libnet_set_join_secrets); + if (!r3) { + r->out.error_string = NULL; + talloc_free(tmp_mem); + return NT_STATUS_NO_MEMORY; + } + + ZERO_STRUCTP(r3); + r3->in.domain_name = r2->out.domain_name; + r3->in.realm = r2->out.realm; + r3->in.account_name = account_name; + r3->in.netbios_name = netbios_name; + r3->in.join_type = r->in.join_type; + r3->in.join_password = r2->out.join_password; + r3->in.kvno = r2->out.kvno; + r3->in.domain_sid = r2->out.domain_sid; + + status = libnet_set_join_secrets(ctx, r3, r3); + if (!NT_STATUS_IS_OK(status)) { + r->out.error_string = talloc_steal(mem_ctx, r3->out.error_string); + talloc_free(tmp_mem); + return status; + } + /* move all out parameter to the callers TALLOC_CTX */ r->out.error_string = NULL; r->out.join_password = r2->out.join_password; diff --git a/source4/libnet/libnet_join.h b/source4/libnet/libnet_join.h index 6da4564cb3..79884130d8 100644 --- a/source4/libnet/libnet_join.h +++ b/source4/libnet/libnet_join.h @@ -79,5 +79,22 @@ struct libnet_Join { } out; }; +struct libnet_set_join_secrets { + struct { + const char *domain_name; + const char *realm; + const char *netbios_name; + const char *account_name; + enum netr_SchannelType join_type; + const char *join_password; + int kvno; + struct dom_sid *domain_sid; + } in; + + struct { + const char *error_string; + } out; +}; + #endif /* __LIBNET_JOIN_H__ */ -- cgit From 8a982108a4f115e350c4246276f2ec886934c7fe Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 9 Apr 2008 14:59:32 +1000 Subject: Link the new vampire code togeather. This adds in the newly attached secrets handling, as well as an interface to the command line 'net' tool. Andrew Bartlett (This used to be commit 1282e3c39479aa580124206814b493370d10690a) --- source4/libnet/config.mk | 1 + source4/libnet/libnet_vampire.c | 50 +++++++++++++++++++++++++++++++---------- source4/libnet/libnet_vampire.h | 5 ++++- 3 files changed, 43 insertions(+), 13 deletions(-) (limited to 'source4/libnet') diff --git a/source4/libnet/config.mk b/source4/libnet/config.mk index 0890941398..231d67cf35 100644 --- a/source4/libnet/config.mk +++ b/source4/libnet/config.mk @@ -1,5 +1,6 @@ [SUBSYSTEM::LIBSAMBA-NET] PRIVATE_PROTO_HEADER = libnet_proto.h +PRIVATE_DEPENDENCIES = PROVISION OBJ_FILES = \ libnet.o \ libnet_passwd.o \ diff --git a/source4/libnet/libnet_vampire.c b/source4/libnet/libnet_vampire.c index cd9167f541..476b97954f 100644 --- a/source4/libnet/libnet_vampire.c +++ b/source4/libnet/libnet_vampire.c @@ -37,6 +37,7 @@ #include "lib/ldb_wrap.h" #include "auth/auth.h" #include "param/param.h" +#include "param/provision.h" /* List of tasks vampire.py must perform: @@ -52,7 +53,6 @@ List of tasks vampire.py must perform: */ struct vampire_state { - struct libnet_context *ctx; const char *netbios_name; struct libnet_JoinDomain *join; struct cli_credentials *machine_account; @@ -93,7 +93,7 @@ static NTSTATUS vampire_prepare_db(void *private_data, settings.schema_dn_str = p->forest->schema_dn_str; settings.netbios_name = p->dest_dsa->netbios_name; settings.realm = s->join->out.realm; - settings.domain = s->join->out.domain; + settings.domain = s->join->out.domain_name; settings.server_dn_str = p->dest_dsa->server_dn_str; settings.machine_password = generate_random_str(s, 16); settings.targetdir = s->targetdir; @@ -115,18 +115,13 @@ static NTSTATUS vampire_prepare_db(void *private_data, return NT_STATUS_INTERNAL_DB_ERROR; } - /* We must set these up to ensure the replMetaData is written correctly, before our NTDS Settings entry is replicated */ + /* We must set these up to ensure the replMetaData is written correctly, + before our NTDS Settings entry is replicated */ ok = samdb_set_ntds_invocation_id(s->ldb, &p->dest_dsa->invocation_id); if (!ok) { DEBUG(0,("Failed to set cached ntds invocationId\n")); return NT_STATUS_FOOBAR; } - ok = samdb_set_ntds_objectGUID(s->ldb, &p->dest_dsa->ntds_guid); - if (!ok) { - DEBUG(0,("Failed to set cached ntds objectGUID\n")); - return NT_STATUS_FOOBAR; - } - s->lp_ctx = lp_ctx; return NT_STATUS_OK; @@ -591,10 +586,11 @@ static NTSTATUS vampire_store_chunk(void *private_data, return NT_STATUS_OK; } -NTSTATUS libnet_vampire(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, - struct libnet_vampire *r) +NTSTATUS libnet_Vampire(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, + struct libnet_Vampire *r) { struct libnet_JoinDomain *join; + struct libnet_set_join_secrets *set_secrets; struct libnet_BecomeDC b; struct libnet_UnbecomeDC u; struct vampire_state *s; @@ -651,6 +647,8 @@ NTSTATUS libnet_vampire(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, s->join = join; + s->targetdir = r->in.targetdir; + ZERO_STRUCT(b); b.in.domain_dns_name = join->out.realm; b.in.domain_netbios_name = join->out.domain_name; @@ -665,7 +663,7 @@ NTSTATUS libnet_vampire(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, b.in.callbacks.config_chunk = vampire_store_chunk; b.in.callbacks.domain_chunk = vampire_store_chunk; - status = libnet_BecomeDC(s->ctx, s, &b); + status = libnet_BecomeDC(ctx, s, &b); if (!NT_STATUS_IS_OK(status)) { printf("libnet_BecomeDC() failed - %s\n", nt_errstr(status)); talloc_free(s); @@ -703,4 +701,32 @@ NTSTATUS libnet_vampire(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, talloc_free(s); return NT_STATUS_INTERNAL_DB_ERROR; } + + set_secrets = talloc_zero(s, struct libnet_set_join_secrets); + if (!set_secrets) { + return NT_STATUS_NO_MEMORY; + } + + set_secrets->in.domain_name = join->out.domain_name; + set_secrets->in.realm = join->out.realm; + set_secrets->in.account_name = account_name; + set_secrets->in.netbios_name = netbios_name; + set_secrets->in.join_type = SEC_CHAN_BDC; + set_secrets->in.join_password = join->out.join_password; + set_secrets->in.kvno = join->out.kvno; + set_secrets->in.domain_sid = join->out.domain_sid; + + status = libnet_set_join_secrets(ctx, set_secrets, set_secrets); + if (!NT_STATUS_IS_OK(status)) { + r->out.error_string = talloc_steal(mem_ctx, set_secrets->out.error_string); + talloc_free(s); + return status; + } + + r->out.domain_name = talloc_steal(r, join->out.domain_name); + r->out.domain_sid = talloc_steal(r, join->out.domain_sid); + talloc_free(s); + + return NT_STATUS_OK; + } diff --git a/source4/libnet/libnet_vampire.h b/source4/libnet/libnet_vampire.h index af461139ff..5e0c7594b2 100644 --- a/source4/libnet/libnet_vampire.h +++ b/source4/libnet/libnet_vampire.h @@ -22,13 +22,16 @@ #ifndef __LIBNET_VAMPIRE_H__ #define __LIBNET_VAMPIRE_H__ -struct libnet_vampire { +struct libnet_Vampire { struct { const char *domain_name; const char *netbios_name; + const char *targetdir; } in; struct { + struct dom_sid *domain_sid; + const char *domain_name; const char *error_string; } out; }; -- cgit From a1a3089199d9ad523e63148a5a2afcca8db39bdf Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Fri, 11 Apr 2008 19:15:24 +1000 Subject: Use the python-provided ldb and lp_ctx pointers in libnet_vampire.c By using the already open smb.conf and sam.ldb, we not only avoid overhead, but also remove the risk we could touch a different database. Andrew Bartlett (This used to be commit 38634183a074556c8dfdcb6affc60f4bcc15a3f0) --- source4/libnet/libnet_vampire.c | 35 ++++++----------------------------- 1 file changed, 6 insertions(+), 29 deletions(-) (limited to 'source4/libnet') diff --git a/source4/libnet/libnet_vampire.c b/source4/libnet/libnet_vampire.c index 476b97954f..9d32088fe6 100644 --- a/source4/libnet/libnet_vampire.c +++ b/source4/libnet/libnet_vampire.c @@ -77,14 +77,8 @@ static NTSTATUS vampire_prepare_db(void *private_data, { struct vampire_state *s = talloc_get_type(private_data, struct vampire_state); struct provision_settings settings; + struct provision_result result; NTSTATUS status; - bool ok; - struct loadparm_context *lp_ctx = loadparm_init(s); - char *smbconf; - - if (!lp_ctx) { - return NT_STATUS_NO_MEMORY; - } settings.site_name = p->dest_dsa->site_name; settings.root_dn_str = p->forest->root_dn_str; @@ -98,31 +92,14 @@ static NTSTATUS vampire_prepare_db(void *private_data, settings.machine_password = generate_random_str(s, 16); settings.targetdir = s->targetdir; - status = provision_bare(s, s->lp_ctx, &settings); - - smbconf = talloc_asprintf(lp_ctx, "%s/%s", s->targetdir, "/etc/smb.conf"); + status = provision_bare(s, s->lp_ctx, &settings, &result); - ok = lp_load(lp_ctx, smbconf); - if (!ok) { - DEBUG(0,("Failed load freshly generated smb.conf '%s'\n", smbconf)); - return NT_STATUS_INVALID_PARAMETER; + if (!NT_STATUS_IS_OK(status)) { + return status; } - s->ldb = samdb_connect(s, lp_ctx, - system_session(s, lp_ctx)); - if (!s->ldb) { - DEBUG(0,("Failed to open '%s'\n", lp_sam_url(lp_ctx))); - return NT_STATUS_INTERNAL_DB_ERROR; - } - - /* We must set these up to ensure the replMetaData is written correctly, - before our NTDS Settings entry is replicated */ - ok = samdb_set_ntds_invocation_id(s->ldb, &p->dest_dsa->invocation_id); - if (!ok) { - DEBUG(0,("Failed to set cached ntds invocationId\n")); - return NT_STATUS_FOOBAR; - } - s->lp_ctx = lp_ctx; + s->ldb = result.samdb; + s->lp_ctx = result.lp_ctx; return NT_STATUS_OK; -- cgit