diff options
24 files changed, 1385 insertions, 535 deletions
diff --git a/source4/libnet/config.mk b/source4/libnet/config.mk index 11b8bdf928..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 \ @@ -10,6 +11,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_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 @@ -920,23 +875,6 @@ static NTSTATUS libnet_Join_primary_domain(struct libnet_context *ctx, } /* - * 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 */ sct = talloc_asprintf(tmp_mem, "%d", r->in.join_type); @@ -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__ */ 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 <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>. +*/ + + +#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 <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>. +*/ + +#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..9d32088fe6 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 <abartlet@samba.org> 2004-2005 + Copyright (C) Stefan Metzmacher 2004-2006 + Copyright (C) Brad Henry 2005 + Copyright (C) Andrew Bartlett <abartlet@samba.org> 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,686 @@ #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" +#include "param/provision.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 { + 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; + struct provision_result result; + NTSTATUS status; + + 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_name; + 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, &result); + + if (!NT_STATUS_IS_OK(status)) { + return status; + } - 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; + s->ldb = result.samdb; + s->lp_ctx = result.lp_ctx; - 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; - } + 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; } - 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_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); + + for (i=0; i < linked_attributes_count; i++) { + const struct dsdb_attribute *sa; + + if (!linked_attributes[i].identifier) { + return NT_STATUS_FOOBAR; + } - /* 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 */ + if (!linked_attributes[i].value.blob) { + return NT_STATUS_FOOBAR; + } - nt_status = dcerpc_secondary_connection(c->out.dcerpc_pipe, &p, - c->out.dcerpc_pipe->binding); + 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, - "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 (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); + } } - nt_status = dcerpc_bind_auth_schannel(samsync_ctx, p, &ndr_table_netlogon, - machine_account, ctx->lp_ctx, DCERPC_AUTH_LEVEL_PRIVACY); + return NT_STATUS_OK; +} - 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; +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; + struct ldb_message *msg; + int ldb_ret; + uint32_t i; + NTSTATUS status; + + const char *account_name; + const char *netbios_name; + + r->out.error_string = NULL; + + s = talloc_zero(mem_ctx , struct vampire_state); + if (!s) { + return NT_STATUS_NO_MEMORY; } - state = talloc(samsync_ctx, struct libnet_SamSync_state); - if (!state) { + 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; + } + } + + account_name = talloc_asprintf(join, "%s$", netbios_name); + if (!account_name) { 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 */ + talloc_free(s); + return NT_STATUS_NO_MEMORY; + } - 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; - } + 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; + + 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; + 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(ctx, s, &b); + if (!NT_STATUS_IS_OK(status)) { + printf("libnet_BecomeDC() failed - %s\n", nt_errstr(status)); + talloc_free(s); + return status; } - /* get NETLOGON credentials */ + 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; + } - 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; + 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; } - /* 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 < msg->num_elements; i++) { + msg->elements[i].flags = LDB_FLAG_MOD_REPLACE; + } - 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; - } + 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; + } - /* 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)); + set_secrets = talloc_zero(s, struct libnet_set_join_secrets); + if (!set_secrets) { + return NT_STATUS_NO_MEMORY; + } - 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; + 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; } - talloc_free(samsync_ctx); - return nt_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 d2ac30fe14..5e0c7594b2 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 <abartlet@samba.org> 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,22 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#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; + const char *targetdir; } in; + struct { + struct dom_sid *domain_sid; + const char *domain_name; 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__ */ diff --git a/source4/param/provision.c b/source4/param/provision.c index e55d984e55..70ef618b6e 100644 --- a/source4/param/provision.c +++ b/source4/param/provision.c @@ -64,14 +64,12 @@ NTSTATUS provision_bare(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx, return NT_STATUS_UNSUCCESSFUL; } - DEBUG(0,("New Server[%s] in Site[%s]\n", settings->dns_name, - settings->site_name)); + DEBUG(0,("New Server in Site[%s]\n", + settings->site_name)); DEBUG(0,("DSA Instance [%s]\n" - "\tobjectGUID[%s]\n" "\tinvocationId[%s]\n", settings->ntds_dn_str, - settings->ntds_guid == NULL?"None":GUID_string(mem_ctx, settings->ntds_guid), settings->invocation_id == NULL?"None":GUID_string(mem_ctx, settings->invocation_id))); DEBUG(0,("Pathes under targetdir[%s]\n", diff --git a/source4/param/provision.h b/source4/param/provision.h index 6b5bc798df..af9685d292 100644 --- a/source4/param/provision.h +++ b/source4/param/provision.h @@ -21,7 +21,6 @@ #define _PROVISION_H_ struct provision_settings { - const char *dns_name; const char *site_name; const char *root_dn_str; const char *domain_dn_str; @@ -33,7 +32,6 @@ struct provision_settings { const char *host_ip; const char *realm; const char *domain; - const struct GUID *ntds_guid; const char *ntds_dn_str; const char *machine_password; const char *targetdir; diff --git a/source4/scripting/python/samba/provision.py b/source4/scripting/python/samba/provision.py index ccf67a0b4a..a8d188e7a3 100644 --- a/source4/scripting/python/samba/provision.py +++ b/source4/scripting/python/samba/provision.py @@ -87,6 +87,7 @@ class ProvisionNames: self.domain = None self.hostname = None self.sitename = None + self.smbconf = None class ProvisionResult: def __init__(self): @@ -262,6 +263,8 @@ def provision_paths_from_lp(lp, dnsdomain): paths.netlogon = lp.get("path", "netlogon") + paths.smbconf = lp.configfile() + return paths @@ -690,6 +693,7 @@ def setup_self_join(samdb, names, "CONFIGDN": names.configdn, "SCHEMADN": names.schemadn, "DOMAINDN": names.domaindn, + "SERVERDN": names.serverdn, "INVOCATIONID": invocationid, "NETBIOSNAME": names.netbiosname, "DEFAULTSITE": names.sitename, @@ -737,8 +741,6 @@ def setup_samdb(path, setup_path, session_info, credentials, lp, return samdb message("Pre-loading the Samba 4 and AD schema") - samdb = SamDB(path, session_info=session_info, - credentials=credentials, lp=lp) samdb.set_domain_sid(domainsid) if serverrole == "domain controller": samdb.set_invocation_id(invocationid) @@ -773,6 +775,7 @@ def setup_samdb(path, setup_path, session_info, credentials, lp, "NETBIOSNAME": names.netbiosname, "DEFAULTSITE": names.sitename, "CONFIGDN": names.configdn, + "SERVERDN": names.serverdn, "POLICYGUID": policyguid, "DOMAINDN": names.domaindn, "DOMAINGUID_MOD": domainguid_mod, @@ -803,6 +806,7 @@ def setup_samdb(path, setup_path, session_info, credentials, lp, "NETBIOSNAME": names.netbiosname, "DEFAULTSITE": names.sitename, "CONFIGDN": names.configdn, + "SERVERDN": names.serverdn }) message("Setting up sam.ldb Samba4 schema") @@ -821,6 +825,7 @@ def setup_samdb(path, setup_path, session_info, credentials, lp, "DOMAIN": names.domain, "SCHEMADN": names.schemadn, "DOMAINDN": names.domaindn, + "SERVERDN": names.serverdn }) message("Setting up display specifiers") @@ -845,6 +850,7 @@ def setup_samdb(path, setup_path, session_info, credentials, lp, "NETBIOSNAME": names.netbiosname, "DEFAULTSITE": names.sitename, "CONFIGDN": names.configdn, + "SERVERDN": names.serverdn }) if fill == FILL_FULL: @@ -1004,20 +1010,25 @@ def provision(setup_dir, message, session_info, ldap_backend_type=ldap_backend_type) if lp.get("server role") == "domain controller": - policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies", - "{" + policyguid + "}") - os.makedirs(policy_path, 0755) - os.makedirs(os.path.join(policy_path, "Machine"), 0755) - os.makedirs(os.path.join(policy_path, "User"), 0755) - if not os.path.isdir(paths.netlogon): + if paths.netlogon is None: + message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.") + message("Please either remove %s or see the template at %s" % + ( paths.smbconf, setup_path("provision.smb.conf.dc"))) + assert(paths.netlogon is not None) + + if paths.sysvol is None: + message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.") + message("Please either remove %s or see the template at %s" % + (paths.smbconf, setup_path("provision.smb.conf.dc"))) + assert(paths.sysvol is not None) + + policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies", + "{" + policyguid + "}") + os.makedirs(policy_path, 0755) + os.makedirs(os.path.join(policy_path, "Machine"), 0755) + os.makedirs(os.path.join(policy_path, "User"), 0755) + if not os.path.isdir(paths.netlogon): os.makedirs(paths.netlogon, 0755) - secrets_ldb = Ldb(paths.secrets, session_info=session_info, - credentials=credentials, lp=lp) - secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=names.realm, - netbiosname=names.netbiosname, domainsid=domainsid, - keytab_path=paths.keytab, samdb_url=paths.samdb, - dns_keytab_path=paths.dns_keytab, dnspass=dnspass, - machinepass=machinepass, dnsdomain=names.dnsdomain) if samdb_fill == FILL_FULL: setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn, @@ -1029,6 +1040,14 @@ def provision(setup_dir, message, session_info, # Only make a zone file on the first DC, it should be replicated with DNS replication if serverrole == "domain controller": + secrets_ldb = Ldb(paths.secrets, session_info=session_info, + credentials=credentials, lp=lp) + secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=names.realm, + netbiosname=names.netbiosname, domainsid=domainsid, + keytab_path=paths.keytab, samdb_url=paths.samdb, + dns_keytab_path=paths.dns_keytab, dnspass=dnspass, + machinepass=machinepass, dnsdomain=names.dnsdomain) + samdb = SamDB(paths.samdb, session_info=session_info, credentials=credentials, lp=lp) diff --git a/source4/setup/provision.ldif b/source4/setup/provision.ldif index 37b6bdaa60..2f734e83b2 100644 --- a/source4/setup/provision.ldif +++ b/source4/setup/provision.ldif @@ -30,7 +30,7 @@ objectclass: rIDManager cn: RID Manager$ systemFlags: 2348810240 isCriticalSystemObject: TRUE -fSMORoleOwner: CN=NTDS Settings,CN=${NETBIOSNAME},CN=Servers,CN=${DEFAULTSITE},CN=Sites,${CONFIGDN} +fSMORoleOwner: CN=NTDS Settings,${SERVERDN} rIDAvailablePool: 4611686014132423217 dn: CN=DomainUpdates,CN=System,${DOMAINDN} @@ -50,7 +50,7 @@ objectclass: infrastructureUpdate cn: Infrastructure systemFlags: 2348810240 isCriticalSystemObject: TRUE -fSMORoleOwner: CN=NTDS Settings,CN=${NETBIOSNAME},CN=Servers,CN=${DEFAULTSITE},CN=Sites,${CONFIGDN} +fSMORoleOwner: CN=NTDS Settings,${SERVERDN} dn: CN=Builtin,${DOMAINDN} objectClass: top diff --git a/source4/setup/provision_basedn_modify.ldif b/source4/setup/provision_basedn_modify.ldif index dadfda720e..f5e1bb5add 100644 --- a/source4/setup/provision_basedn_modify.ldif +++ b/source4/setup/provision_basedn_modify.ldif @@ -65,7 +65,7 @@ replace: objectCategory objectCategory: CN=Domain-DNS,${SCHEMADN} - replace: fSMORoleOwner -fSMORoleOwner: CN=NTDS Settings,CN=${NETBIOSNAME},CN=Servers,CN=${DEFAULTSITE},CN=Sites,${CONFIGDN} +fSMORoleOwner: CN=NTDS Settings,${SERVERDN} - replace: isCriticalSystemObject isCriticalSystemObject: TRUE diff --git a/source4/setup/provision_configuration.ldif b/source4/setup/provision_configuration.ldif index 0fe90b0739..2a7357d7ad 100644 --- a/source4/setup/provision_configuration.ldif +++ b/source4/setup/provision_configuration.ldif @@ -7,7 +7,7 @@ objectClass: crossRefContainer cn: Partitions systemFlags: 2147483648 msDS-Behavior-Version: 0 -fSMORoleOwner: CN=NTDS Settings,CN=${NETBIOSNAME},CN=Servers,CN=${DEFAULTSITE},CN=Sites,${CONFIGDN} +fSMORoleOwner: CN=NTDS Settings,${SERVERDN} dn: CN=Enterprise Configuration,CN=Partitions,${CONFIGDN} objectClass: top diff --git a/source4/setup/provision_schema_basedn_modify.ldif b/source4/setup/provision_schema_basedn_modify.ldif index 4e690376d7..4e8267a303 100644 --- a/source4/setup/provision_schema_basedn_modify.ldif +++ b/source4/setup/provision_schema_basedn_modify.ldif @@ -4,7 +4,7 @@ dn: ${SCHEMADN} changetype: modify replace: fSMORoleOwner -fSMORoleOwner: CN=NTDS Settings,CN=${NETBIOSNAME},CN=Servers,CN=${DEFAULTSITE},CN=Sites,${CONFIGDN} +fSMORoleOwner: CN=NTDS Settings,${SERVERDN} - replace: objectVersion objectVersion: 30 diff --git a/source4/setup/provision_self_join.ldif b/source4/setup/provision_self_join.ldif index a0cfa7eb23..c91e2f4c19 100644 --- a/source4/setup/provision_self_join.ldif +++ b/source4/setup/provision_self_join.ldif @@ -36,7 +36,7 @@ isCriticalSystemObject: TRUE sambaPassword:: ${DNSPASS_B64} showInAdvancedViewOnly: TRUE -dn: CN=${NETBIOSNAME},CN=Servers,CN=${DEFAULTSITE},CN=Sites,${CONFIGDN} +dn: ${SERVERDN} objectClass: top objectClass: server cn: ${NETBIOSNAME} @@ -44,7 +44,7 @@ systemFlags: 1375731712 dNSHostName: ${DNSNAME} serverReference: CN=${NETBIOSNAME},OU=Domain Controllers,${DOMAINDN} -dn: CN=NTDS Settings,CN=${NETBIOSNAME},CN=Servers,CN=${DEFAULTSITE},CN=Sites,${CONFIGDN} +dn: CN=NTDS Settings,${SERVERDN} objectClass: top objectClass: applicationSettings objectClass: nTDSDSA diff --git a/source4/setup/tests/blackbox_provision.sh b/source4/setup/tests/blackbox_provision.sh index 75d4fcfcb4..4db226778c 100755 --- a/source4/setup/tests/blackbox_provision.sh +++ b/source4/setup/tests/blackbox_provision.sh @@ -31,6 +31,8 @@ testit "simple-default" $PYTHON ./setup/provision $CONFIGURATION --domain=FOO -- testit "simple-dc" $PYTHON ./setup/provision $CONFIGURATION --server-role="dc" --domain=FOO --realm=foo.example.com --domain-sid=S-1-5-21-4177067393-1453636373-93818738 --targetdir=$PREFIX/simple-dc testit "simple-member" $PYTHON ./setup/provision $CONFIGURATION --server-role="member" --domain=FOO --realm=foo.example.com --targetdir=$PREFIX/simple-member testit "simple-standalone" $PYTHON ./setup/provision $CONFIGURATION --server-role="standalone" --domain=FOO --realm=foo.example.com --targetdir=$PREFIX/simple-standalone +testit "blank-dc" $PYTHON ./setup/provision $CONFIGURATION --server-role="dc" --domain=FOO --realm=foo.example.com --domain-sid=S-1-5-21-4177067393-1453636373-93818738 --targetdir=$PREFIX/simple-dc --blank +testit "partitions-only-dc" $PYTHON ./setup/provision $CONFIGURATION --server-role="dc" --domain=FOO --realm=foo.example.com --domain-sid=S-1-5-21-4177067393-1453636373-93818738 --targetdir=$PREFIX/simple-dc --partitions-only reprovision() { $PYTHON ./setup/provision $CONFIGURATION --domain=FOO --realm=foo.example.com --targetdir="$PREFIX/reprovision" diff --git a/source4/torture/libnet/libnet_BecomeDC.c b/source4/torture/libnet/libnet_BecomeDC.c index a9a7773649..4d57a84582 100644 --- a/source4/torture/libnet/libnet_BecomeDC.c +++ b/source4/torture/libnet/libnet_BecomeDC.c @@ -69,15 +69,7 @@ static NTSTATUS test_become_dc_prepare_db(void *private_data, 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.dns_name = p->dest_dsa->dns_name; 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; @@ -92,35 +84,8 @@ static NTSTATUS test_become_dc_prepare_db(void *private_data, status = provision_bare(s, s->lp_ctx, &settings, &result); - smbconf = talloc_asprintf(lp_ctx, "%s/%s", s->targetdir, "/etc/smb.conf"); - - ok = lp_load(lp_ctx, smbconf); - if (!ok) { - DEBUG(0,("Failed load freshly generated smb.conf '%s'\n", smbconf)); - return NT_STATUS_INVALID_PARAMETER; - } - - s->ldb = ldb_wrap_connect(s, lp_ctx, lp_sam_url(lp_ctx), - system_session(s, lp_ctx), - NULL, 0, NULL); - if (!s->ldb) { - DEBUG(0,("Failed to open '%s'\n", lp_sam_url(lp_ctx))); - return NT_STATUS_INTERNAL_DB_ERROR; - } - - 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; - + s->ldb = result.samdb; + s->lp_ctx = result.lp_ctx; return NT_STATUS_OK; diff --git a/source4/torture/local/torture.c b/source4/torture/local/torture.c index 17d83456d5..7935f6cc35 100644 --- a/source4/torture/local/torture.c +++ b/source4/torture/local/torture.c @@ -43,7 +43,7 @@ static bool test_tempdir(struct torture_context *tctx) static bool test_provision(struct torture_context *tctx) { NTSTATUS status; - struct provision_settings *settings = talloc(tctx, struct provision_settings); + struct provision_settings *settings = talloc_zero(tctx, struct provision_settings); struct provision_result result; char *targetdir = NULL; @@ -51,7 +51,6 @@ static bool test_provision(struct torture_context *tctx) "torture_temp_dir should return NT_STATUS_OK" ); settings->targetdir = talloc_steal(settings, targetdir); - settings->dns_name = "example.com"; settings->site_name = "SOME-SITE-NAME"; settings->root_dn_str = "DC=EXAMPLE,DC=COM"; settings->domain_dn_str = "DC=EXAMPLE,DC=COM"; @@ -61,7 +60,7 @@ static bool test_provision(struct torture_context *tctx) settings->netbios_name = "FOO"; settings->realm = "EXAMPLE.COM"; settings->domain = "EXAMPLE"; - settings->ntds_guid = NULL; + settings->netbios_name = "torture"; settings->ntds_dn_str = NULL; settings->machine_password = "geheim"; diff --git a/source4/utils/net/net.c b/source4/utils/net/net.c index e0865c4416..c908ea6279 100644 --- a/source4/utils/net/net.c +++ b/source4/utils/net/net.c @@ -102,7 +102,8 @@ static const struct net_functable net_functable[] = { {"time", "get remote server's time\n", net_time, net_time_usage}, {"join", "join a domain\n", net_join, net_join_usage}, {"samdump", "dump the sam of a domain\n", net_samdump, net_samdump_usage}, - {"samsync", "synchronise into the local ldb the sam of a domain\n", net_samsync_ldb, net_samsync_ldb_usage}, + {"vampire", "join and syncronise an AD domain onto the local server\n", net_vampire, net_vampire_usage}, + {"samsync", "synchronise into the local ldb the sam of an NT4 domain\n", net_samsync_ldb, net_samsync_ldb_usage}, {"user", "manage user accounts\n", net_user, net_user_usage}, {NULL, NULL, NULL, NULL} }; diff --git a/source4/utils/net/net_join.c b/source4/utils/net/net_join.c index 08a4fbd4a1..abdcbf6027 100644 --- a/source4/utils/net/net_join.c +++ b/source4/utils/net/net_join.c @@ -100,3 +100,70 @@ int net_join_help(struct net_context *ctx, int argc, const char **argv) d_printf("Joins domain as either member or backup domain controller.\n"); return 0; } + +int net_vampire(struct net_context *ctx, int argc, const char **argv) +{ + NTSTATUS status; + struct libnet_context *libnetctx; + struct libnet_Vampire *r; + char *tmp, *targetdir = NULL; + const char *domain_name; + + switch (argc) { + case 0: /* no args -> fail */ + return net_vampire_usage(ctx, argc, argv); + case 1: /* only DOMAIN */ + tmp = talloc_strdup(ctx->mem_ctx, argv[0]); + break; + case 2: /* domain and target dir */ + tmp = talloc_strdup(ctx->mem_ctx, argv[0]); + targetdir = talloc_strdup(ctx->mem_ctx, argv[1]); + break; + default: /* too many args -> fail */ + return net_vampire_usage(ctx, argc, argv); + } + + domain_name = tmp; + + libnetctx = libnet_context_init(NULL, ctx->lp_ctx); + if (!libnetctx) { + return -1; + } + libnetctx->cred = ctx->credentials; + r = talloc(ctx->mem_ctx, struct libnet_Vampire); + if (!r) { + return -1; + } + /* prepare parameters for the vampire */ + r->in.netbios_name = lp_netbios_name(ctx->lp_ctx); + r->in.domain_name = domain_name; + r->in.targetdir = targetdir; + r->out.error_string = NULL; + + /* do the domain vampire */ + status = libnet_Vampire(libnetctx, r, r); + + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, "Vampire of domain failed: %s\n", + r->out.error_string ? r->out.error_string : nt_errstr(status)); + talloc_free(r); + talloc_free(libnetctx); + return -1; + } + d_printf("Vampired domain %s (%s)\n", r->out.domain_name, dom_sid_string(ctx->mem_ctx, r->out.domain_sid)); + + talloc_free(libnetctx); + return 0; +} + +int net_vampire_usage(struct net_context *ctx, int argc, const char **argv) +{ + d_printf("net vampire <domain> [options]\n"); + return 0; +} + +int net_vampire_help(struct net_context *ctx, int argc, const char **argv) +{ + d_printf("Vampires domain as either member or backup domain controller.\n"); + return 0; +} diff --git a/source4/utils/net/net_vampire.c b/source4/utils/net/net_vampire.c index c798112d7b..4f6371d617 100644 --- a/source4/utils/net/net_vampire.c +++ b/source4/utils/net/net_vampire.c @@ -169,7 +169,7 @@ int net_samsync_ldb(struct net_context *ctx, int argc, const char **argv) int net_samsync_ldb_usage(struct net_context *ctx, int argc, const char **argv) { - d_printf("net samsync_ldb\n"); + d_printf("net samsync\n"); return 0; } diff --git a/source4/winbind/wb_sids2xids.c b/source4/winbind/wb_sids2xids.c index 302b915ff5..6b89caf465 100644 --- a/source4/winbind/wb_sids2xids.c +++ b/source4/winbind/wb_sids2xids.c @@ -67,15 +67,13 @@ NTSTATUS wb_sids2xids_recv(struct composite_context *ctx, struct id_mapping **ids) { NTSTATUS status = composite_wait(ctx); + struct sids2xids_state *state = talloc_get_type(ctx->private_data, + struct sids2xids_state); DEBUG(5, ("wb_sids2xids_recv called\n")); - if (NT_STATUS_IS_OK(status)) { - struct sids2xids_state *state = - talloc_get_type(ctx->private_data, - struct sids2xids_state); - *ids = state->ids; - } + *ids = state->ids; + talloc_free(ctx); return status; } diff --git a/source4/winbind/wb_xids2sids.c b/source4/winbind/wb_xids2sids.c index 1be394d276..a1cf2667ff 100644 --- a/source4/winbind/wb_xids2sids.c +++ b/source4/winbind/wb_xids2sids.c @@ -67,15 +67,13 @@ NTSTATUS wb_xids2sids_recv(struct composite_context *ctx, struct id_mapping **ids) { NTSTATUS status = composite_wait(ctx); + struct xids2sids_state *state = talloc_get_type(ctx->private_data, + struct xids2sids_state); DEBUG(5, ("wb_xids2sids_recv called.\n")); - if (NT_STATUS_IS_OK(status)) { - struct xids2sids_state *state = - talloc_get_type(ctx->private_data, - struct xids2sids_state); - *ids = state->ids; - } + *ids = state->ids; + talloc_free(ctx); return status; } |