summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source4/libnet/config.mk2
-rw-r--r--source4/libnet/libnet_join.c1247
-rw-r--r--source4/libnet/libnet_join.h28
-rw-r--r--source4/libnet/libnet_rpc.c12
-rw-r--r--source4/libnet/libnet_rpc.h4
-rw-r--r--source4/torture/config.mk1
-rw-r--r--source4/torture/nbt/dgram.c2
-rw-r--r--source4/torture/rpc/netlogon.c2
-rw-r--r--source4/torture/rpc/testjoin.c149
-rw-r--r--source4/torture/torture.c1
-rw-r--r--source4/utils/net/net_join.c24
11 files changed, 1075 insertions, 397 deletions
diff --git a/source4/libnet/config.mk b/source4/libnet/config.mk
index 77e48b82e7..d9617982ba 100644
--- a/source4/libnet/config.mk
+++ b/source4/libnet/config.mk
@@ -17,6 +17,6 @@ ADD_OBJ_FILES = \
libnet/userinfo.o \
libnet/userman.o \
libnet/domain.o
-REQUIRED_SUBSYSTEMS = RPC_NDR_SAMR RPC_NDR_LSA RPC_NDR_SRVSVC RPC_NDR_DRSUAPI LIBCLI_COMPOSITE LIBCLI_RESOLVE LIBSAMBA3
+REQUIRED_SUBSYSTEMS = RPC_NDR_SAMR RPC_NDR_LSA RPC_NDR_SRVSVC RPC_NDR_DRSUAPI LIBCLI_COMPOSITE LIBCLI_RESOLVE LIBSAMBA3 LIBCLI_CLDAP
# End SUBSYSTEM LIBNET
#################################
diff --git a/source4/libnet/libnet_join.c b/source4/libnet/libnet_join.c
index cef74492b8..3edad64259 100644
--- a/source4/libnet/libnet_join.c
+++ b/source4/libnet/libnet_join.c
@@ -3,6 +3,7 @@
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
@@ -25,7 +26,485 @@
#include "librpc/gen_ndr/ndr_lsa.h"
#include "librpc/gen_ndr/ndr_drsuapi.h"
#include "lib/ldb/include/ldb.h"
+#include "libcli/cldap/cldap.h"
#include "include/secrets.h"
+#include "librpc/gen_ndr/drsuapi.h"
+
+/*
+ * find out Site specific stuff:
+ * 1.) setup an CLDAP socket
+ * 2.) lookup the Site name
+ * 3.) Add entry CN=<netbios name>,CN=Servers,CN=<site name>,CN=Sites,CN=Configuration,<domain dn>.
+ * TODO: 4.) use DsAddEntry() to create CN=NTDS Settings,CN=<netbios name>,CN=Servers,CN=<site name>...
+ */
+static NTSTATUS libnet_JoinSite(struct libnet_context *ctx,
+ struct dcerpc_pipe *drsuapi_pipe,
+ struct policy_handle drsuapi_bind_handle,
+ struct ldb_context *remote_ldb,
+ struct libnet_JoinDomain *libnet_r)
+{
+ NTSTATUS status;
+ TALLOC_CTX *tmp_ctx;
+
+ struct cldap_socket *cldap = NULL;
+ struct cldap_netlogon search;
+
+ struct ldb_dn *server_dn;
+ struct ldb_message *msg;
+ int rtn;
+
+ const char *site_name;
+ const char *server_dn_str;
+ const char *config_dn_str;
+
+ tmp_ctx = talloc_named(libnet_r, 0, "libnet_JoinSite temp context");
+ if (!tmp_ctx) {
+ libnet_r->out.error_string = NULL;
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Resolve the site name. */
+
+ ZERO_STRUCT(search);
+ search.in.dest_address = libnet_r->out.samr_binding->host;
+ search.in.acct_control = -1;
+ search.in.version = 6;
+
+ cldap = cldap_socket_init(tmp_ctx, NULL);
+ status = cldap_netlogon(cldap, tmp_ctx, &search);
+ if (!NT_STATUS_IS_OK(status)) {
+ /* Default to using Default-First-Site-Name rather than returning status at this point. */
+ site_name = talloc_asprintf(tmp_ctx, "%s", "Default-First-Site-Name");
+ if (!site_name) {
+ libnet_r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else {
+ site_name = search.out.netlogon.logon5.site_name;
+ }
+
+ config_dn_str = talloc_asprintf(tmp_ctx, "CN=Configuration,%s", libnet_r->out.domain_dn_str);
+ if (!config_dn_str) {
+ libnet_r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ server_dn_str = talloc_asprintf(tmp_ctx, "CN=%s,CN=Servers,CN=%s,CN=Sites,%s",
+ libnet_r->in.netbios_name, site_name, config_dn_str);
+ if (!server_dn_str) {
+ libnet_r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /*
+ Add entry CN=<netbios name>,CN=Servers,CN=<site name>,CN=Sites,CN=Configuration,<domain dn>.
+ */
+ msg = ldb_msg_new(tmp_ctx);
+ if (!msg) {
+ libnet_r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rtn = ldb_msg_add_string(remote_ldb, msg, "objectClass", "server");
+ if (rtn != 0) {
+ libnet_r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ rtn = ldb_msg_add_string(remote_ldb, msg, "systemFlags", "50000000");
+ if (rtn != 0) {
+ libnet_r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ rtn = ldb_msg_add_string(remote_ldb, msg, "serverReference",libnet_r->out.account_dn_str);
+ if (rtn != 0) {
+ libnet_r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ server_dn = ldb_dn_explode(tmp_ctx, server_dn_str);
+ if (server_dn == NULL) {
+ libnet_r->out.error_string = talloc_asprintf(libnet_r,
+ "Invalid server dn: %s",
+ server_dn_str);
+ talloc_free(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ msg->dn = server_dn;
+ msg->elements->flags = LDB_FLAG_MOD_ADD;
+
+ rtn = ldb_add(remote_ldb, msg);
+ if (rtn != 0) {
+ libnet_r->out.error_string
+ = talloc_asprintf(libnet_r,
+ "Failed to add server entry %s: %s.",
+ server_dn_str,
+ ldb_errstring(remote_ldb));
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ DEBUG(0, ("We still need to perform a DsAddEntry() so that we can create the CN=NTDS Settings container.\n"));
+
+ /* Store the server DN in libnet_r */
+ libnet_r->out.server_dn_str = server_dn_str;
+ talloc_steal(libnet_r, server_dn_str);
+
+ talloc_free(tmp_ctx);
+ return NT_STATUS_OK;
+}
+
+/*
+ * complete a domain join, when joining to a AD domain:
+ * 1.) connect and bind to the DRSUAPI pipe
+ * 2.) do a DsCrackNames() to find the machine account dn
+ * 3.) connect to LDAP
+ * 4.) do an ldap search to find the "msDS-KeyVersionNumber" of the machine account
+ * 5.) set the servicePrincipalName's of the machine account via LDAP, (maybe we should use DsWriteAccountSpn()...)
+ * 6.) do a DsCrackNames() to find the domain dn
+ * 7.) find out Site specific stuff, look at libnet_JoinSite() for details
+ */
+static NTSTATUS libnet_JoinADSDomain(struct libnet_context *ctx, struct libnet_JoinDomain *r)
+{
+ NTSTATUS status;
+
+ TALLOC_CTX *tmp_ctx;
+
+ const char *realm = r->out.realm;
+
+ struct dcerpc_binding *samr_binding = r->out.samr_binding;
+
+ struct dcerpc_pipe *drsuapi_pipe;
+ struct dcerpc_binding *drsuapi_binding;
+ struct drsuapi_DsBind r_drsuapi_bind;
+ struct drsuapi_DsCrackNames r_crack_names;
+ struct drsuapi_DsNameString names[1];
+ struct policy_handle drsuapi_bind_handle;
+ struct GUID drsuapi_bind_guid;
+
+ struct ldb_context *remote_ldb;
+ const struct ldb_dn *account_dn;
+ const char *account_dn_str;
+ const char *remote_ldb_url;
+ struct ldb_message **msgs, *msg;
+
+ int rtn;
+
+ unsigned int kvno;
+
+ const char * const attrs[] = {
+ "msDS-KeyVersionNumber",
+ "servicePrincipalName",
+ "dNSHostName",
+ NULL,
+ };
+
+ r->out.error_string = NULL;
+
+ /* We need to convert between a samAccountName and domain to a
+ * DN in the directory. The correct way to do this is with
+ * DRSUAPI CrackNames */
+
+ /* Fiddle with the bindings, so get to DRSUAPI on
+ * NCACN_IP_TCP, sealed */
+ tmp_ctx = talloc_named(r, 0, "libnet_JoinADSDomain temp context");
+ if (!tmp_ctx) {
+ r->out.error_string = NULL;
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ drsuapi_binding = talloc(tmp_ctx, struct dcerpc_binding);
+ if (!drsuapi_binding) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *drsuapi_binding = *samr_binding;
+ drsuapi_binding->transport = NCACN_IP_TCP;
+ drsuapi_binding->endpoint = NULL;
+ drsuapi_binding->flags |= DCERPC_SEAL;
+
+ status = dcerpc_pipe_connect_b(tmp_ctx,
+ &drsuapi_pipe,
+ drsuapi_binding,
+ DCERPC_DRSUAPI_UUID,
+ DCERPC_DRSUAPI_VERSION,
+ ctx->cred,
+ ctx->event_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_asprintf(r,
+ "Connection to DRSUAPI pipe of PDC of domain '%s' failed: %s",
+ r->in.domain_name,
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ /* get a DRSUAPI pipe handle */
+ GUID_from_string(DRSUAPI_DS_BIND_GUID, &drsuapi_bind_guid);
+
+ r_drsuapi_bind.in.bind_guid = &drsuapi_bind_guid;
+ r_drsuapi_bind.in.bind_info = NULL;
+ r_drsuapi_bind.out.bind_handle = &drsuapi_bind_handle;
+
+ status = dcerpc_drsuapi_DsBind(drsuapi_pipe, tmp_ctx, &r_drsuapi_bind);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ r->out.error_string
+ = talloc_asprintf(r,
+ "dcerpc_drsuapi_DsBind for [%s\\%s] failed - %s\n",
+ r->in.domain_name, r->in.account_name,
+ dcerpc_errstr(tmp_ctx, drsuapi_pipe->last_fault_code));
+ talloc_free(tmp_ctx);
+ return status;
+ } else {
+ r->out.error_string
+ = talloc_asprintf(r,
+ "dcerpc_drsuapi_DsBind for [%s\\%s] failed - %s\n",
+ r->in.domain_name, r->in.account_name,
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return status;
+ }
+ } else if (!W_ERROR_IS_OK(r_drsuapi_bind.out.result)) {
+ r->out.error_string
+ = talloc_asprintf(r,
+ "DsBind failed - %s\n",
+ win_errstr(r_drsuapi_bind.out.result));
+ talloc_free(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* Actually 'crack' the names */
+ ZERO_STRUCT(r_crack_names);
+ r_crack_names.in.bind_handle = &drsuapi_bind_handle;
+ r_crack_names.in.level = 1;
+ r_crack_names.in.req.req1.unknown1 = 0x000004e4;
+ r_crack_names.in.req.req1.unknown2 = 0x00000407;
+ r_crack_names.in.req.req1.count = 1;
+ r_crack_names.in.req.req1.names = names;
+ r_crack_names.in.req.req1.format_flags = DRSUAPI_DS_NAME_FLAG_NO_FLAGS;
+ r_crack_names.in.req.req1.format_offered= DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY;
+ r_crack_names.in.req.req1.format_desired= DRSUAPI_DS_NAME_FORMAT_FQDN_1779;
+ names[0].str = dom_sid_string(tmp_ctx, r->out.account_sid);
+ if (!names[0].str) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = dcerpc_drsuapi_DsCrackNames(drsuapi_pipe, tmp_ctx, &r_crack_names);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ r->out.error_string
+ = talloc_asprintf(r,
+ "dcerpc_drsuapi_DsCrackNames for [%s] failed - %s\n",
+ names[0].str,
+ dcerpc_errstr(tmp_ctx, drsuapi_pipe->last_fault_code));
+ talloc_free(tmp_ctx);
+ return status;
+ } else {
+ r->out.error_string
+ = talloc_asprintf(r,
+ "dcerpc_drsuapi_DsCrackNames for [%s] failed - %s\n",
+ names[0].str,
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return status;
+ }
+ } else if (!W_ERROR_IS_OK(r_crack_names.out.result)) {
+ r->out.error_string
+ = talloc_asprintf(r,
+ "DsCrackNames failed - %s\n", win_errstr(r_crack_names.out.result));
+ talloc_free(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ } else if (r_crack_names.out.level != 1
+ || !r_crack_names.out.ctr.ctr1
+ || r_crack_names.out.ctr.ctr1->count != 1
+ || !r_crack_names.out.ctr.ctr1->array[0].result_name
+ || r_crack_names.out.ctr.ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
+ r->out.error_string = talloc_asprintf(r, "DsCrackNames failed\n");
+ talloc_free(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* Store the DN of our machine account. */
+ account_dn_str = r_crack_names.out.ctr.ctr1->array[0].result_name;
+
+ account_dn = ldb_dn_explode(tmp_ctx, account_dn_str);
+ if (!account_dn) {
+ r->out.error_string = talloc_asprintf(r, "Invalid account dn: %s",
+ account_dn_str);
+ talloc_free(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* Now we know the user's DN, open with LDAP, read and modify a few things */
+
+ remote_ldb_url = talloc_asprintf(tmp_ctx, "ldap://%s",
+ drsuapi_binding->host);
+ if (!remote_ldb_url) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ remote_ldb = ldb_wrap_connect(tmp_ctx, remote_ldb_url, 0, NULL);
+ if (!remote_ldb) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* search for the user's record */
+ rtn = ldb_search(remote_ldb, account_dn, LDB_SCOPE_BASE,
+ NULL, attrs, &msgs);
+ if (rtn != 1) {
+ r->out.error_string = talloc_asprintf(r, "ldb_search for %s failed - %s\n",
+ account_dn_str, ldb_errstring(remote_ldb));
+ talloc_free(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* If we have a kvno recorded in AD, we need it locally as well */
+ kvno = ldb_msg_find_uint(msgs[0], "msDS-KeyVersionNumber", 0);
+
+ /* Prepare a new message, for the modify */
+ msg = ldb_msg_new(tmp_ctx);
+ if (!msg) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ msg->dn = msgs[0]->dn;
+
+ {
+ const char *service_principal_name[2];
+ const char *dns_host_name = strlower_talloc(tmp_ctx,
+ talloc_asprintf(tmp_ctx,
+ "%s.%s",
+ r->in.netbios_name,
+ realm));
+
+ if (!dns_host_name) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ service_principal_name[0] = talloc_asprintf(tmp_ctx, "host/%s", dns_host_name);
+ if (!service_principal_name[0]) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ service_principal_name[1] = talloc_asprintf(tmp_ctx, "host/%s", strlower_talloc(tmp_ctx, r->in.netbios_name));
+ if (!service_principal_name[1]) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rtn = samdb_msg_add_string(remote_ldb, tmp_ctx, msg, "dNSHostName", dns_host_name);
+ if (rtn == -1) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ rtn = samdb_msg_add_string(remote_ldb, tmp_ctx, msg, "servicePrincipalName", service_principal_name[0]);
+ if (rtn == -1) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ rtn = samdb_msg_add_string(remote_ldb, tmp_ctx, msg, "servicePrincipalName", service_principal_name[1]);
+ if (rtn == -1) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rtn = samdb_replace(remote_ldb, tmp_ctx, msg);
+ if (rtn != 0) {
+ r->out.error_string
+ = talloc_asprintf(r,
+ "Failed to replace entries on %s\n",
+ ldb_dn_linearize(tmp_ctx, msg->dn));
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ }
+
+ /* DsCrackNames to find out the DN of the domain. */
+ r_crack_names.in.req.req1.format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT;
+ r_crack_names.in.req.req1.format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779;
+ names[0].str = talloc_asprintf(tmp_ctx, "%s\\", r->out.domain_name);
+ if (!names[0].str) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = dcerpc_drsuapi_DsCrackNames(drsuapi_pipe, tmp_ctx, &r_crack_names);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ r->out.error_string
+ = talloc_asprintf(r,
+ "dcerpc_drsuapi_DsCrackNames for [%s] failed - %s\n",
+ r->in.domain_name,
+ dcerpc_errstr(tmp_ctx, drsuapi_pipe->last_fault_code));
+ talloc_free(tmp_ctx);
+ return status;
+ } else {
+ r->out.error_string
+ = talloc_asprintf(r,
+ "dcerpc_drsuapi_DsCrackNames for [%s] failed - %s\n",
+ r->in.domain_name,
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return status;
+ }
+ } else if (!W_ERROR_IS_OK(r_crack_names.out.result)) {
+ r->out.error_string
+ = talloc_asprintf(r,
+ "DsCrackNames failed - %s\n", win_errstr(r_crack_names.out.result));
+ talloc_free(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ } else if (r_crack_names.out.level != 1
+ || !r_crack_names.out.ctr.ctr1
+ || r_crack_names.out.ctr.ctr1->count != 1
+ || !r_crack_names.out.ctr.ctr1->array[0].result_name
+ || r_crack_names.out.ctr.ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
+ r->out.error_string = talloc_asprintf(r, "DsCrackNames failed\n");
+ talloc_free(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* Store the account DN. */
+ r->out.account_dn_str = account_dn_str;
+ talloc_steal(r, account_dn_str);
+
+ /* Store the domain DN. */
+ r->out.domain_dn_str = r_crack_names.out.ctr.ctr1->array[0].result_name;
+ talloc_steal(r, r_crack_names.out.ctr.ctr1->array[0].result_name);
+
+ r->out.kvno = kvno;
+
+ status = libnet_JoinSite(ctx,
+ drsuapi_pipe, drsuapi_bind_handle,
+ remote_ldb, r);
+ talloc_free(tmp_ctx);
+
+ return status;
+}
/*
* do a domain join using DCERPC/SAMR calls
@@ -43,13 +522,15 @@
* 6. call libnet_SetPassword_samr_handle to set the password
*
* 7. do a samrSetUserInfo to set the account flags
+ * 8. do some ADS specific things when we join as Domain Controller,
+ * look at libnet_joinADSDomain() for the details
*/
NTSTATUS libnet_JoinDomain(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_JoinDomain *r)
{
TALLOC_CTX *tmp_ctx;
- NTSTATUS status;
- struct libnet_RpcConnect c;
+ NTSTATUS status, cu_status;
+ struct libnet_RpcConnect *c;
struct lsa_ObjectAttribute attr;
struct lsa_QosInfo qos;
struct lsa_OpenPolicy2 lsa_open_policy;
@@ -59,6 +540,7 @@ NTSTATUS libnet_JoinDomain(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, stru
struct dcerpc_binding *samr_binding;
struct dcerpc_pipe *samr_pipe;
+ struct dcerpc_pipe *lsa_pipe;
struct samr_Connect sc;
struct policy_handle p_handle;
struct samr_OpenDomain od;
@@ -66,68 +548,78 @@ NTSTATUS libnet_JoinDomain(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, stru
struct samr_LookupNames ln;
struct samr_OpenUser ou;
struct samr_CreateUser2 cu;
- struct policy_handle u_handle;
+ struct policy_handle *u_handle = NULL;
struct samr_QueryUserInfo qui;
struct samr_SetUserInfo sui;
union samr_UserInfo u_info;
union libnet_SetPassword r2;
struct samr_GetUserPwInfo pwp;
struct lsa_String samr_account_name;
-
- struct dcerpc_pipe *drsuapi_pipe;
- struct dcerpc_binding *drsuapi_binding;
- struct drsuapi_DsBind r_drsuapi_bind;
- struct drsuapi_DsCrackNames r_crack_names;
- struct drsuapi_DsNameString names[1];
- struct policy_handle drsuapi_bind_handle;
- struct GUID drsuapi_bind_guid;
-
- struct ldb_context *remote_ldb;
-
+
uint32_t acct_flags;
uint32_t rid, access_granted;
int policy_min_pw_len = 0;
- struct dom_sid *domain_sid;
- const char *domain_name;
+ struct dom_sid *domain_sid = NULL;
+ struct dom_sid *account_sid = NULL;
+ const char *domain_name = NULL;
+ const char *password_str = NULL;
const char *realm = NULL; /* Also flag for remote being AD */
- const struct ldb_dn *account_dn;
-
- char *remote_ldb_url;
- struct ldb_message **msgs, *msg;
- int ldb_ret;
-
- const char *attrs[] = {
- "msDS-KeyVersionNumber",
- "servicePrincipalName",
- "dNSHostName",
- NULL,
- };
-
+
+
+ r->out.error_string = NULL;
+ r2.samr_handle.out.error_string = NULL;
+
tmp_ctx = talloc_named(mem_ctx, 0, "libnet_Join temp context");
if (!tmp_ctx) {
r->out.error_string = NULL;
return NT_STATUS_NO_MEMORY;
}
-
-
+
+ u_handle = talloc(tmp_ctx, struct policy_handle);
+ if (!u_handle) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ samr_pipe = talloc(tmp_ctx, struct dcerpc_pipe);
+ if (!samr_pipe) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ c = talloc(tmp_ctx, struct libnet_RpcConnect);
+ if (!c) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
/* prepare connect to the LSA pipe of PDC */
- c.level = LIBNET_RPC_CONNECT_PDC;
- c.in.domain_name = r->in.domain_name;
- c.in.dcerpc_iface_name = DCERPC_LSARPC_NAME;
- c.in.dcerpc_iface_uuid = DCERPC_LSARPC_UUID;
- c.in.dcerpc_iface_version = DCERPC_LSARPC_VERSION;
-
+ if (r->in.level == LIBNET_JOINDOMAIN_AUTOMATIC) {
+ c->level = LIBNET_RPC_CONNECT_PDC;
+ c->in.domain_name = r->in.domain_name;
+ } else {
+ c->level = LIBNET_RPC_CONNECT_BINDING;
+ c->in.binding = r->in.binding;
+ }
+ c->in.dcerpc_iface_name = DCERPC_LSARPC_NAME;
+ c->in.dcerpc_iface_uuid = DCERPC_LSARPC_UUID;
+ c->in.dcerpc_iface_version = DCERPC_LSARPC_VERSION;
+
/* connect to the LSA pipe of the PDC */
- status = libnet_RpcConnect(ctx, tmp_ctx, &c);
+
+ status = libnet_RpcConnect(ctx, c, c);
if (!NT_STATUS_IS_OK(status)) {
r->out.error_string = talloc_asprintf(mem_ctx,
"Connection to LSA pipe of PDC of domain '%s' failed: %s",
r->in.domain_name, nt_errstr(status));
talloc_free(tmp_ctx);
return status;
- }
-
+ }
+ lsa_pipe = c->out.dcerpc_pipe;
/* Get an LSA policy handle */
@@ -145,11 +637,18 @@ NTSTATUS libnet_JoinDomain(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, stru
attr.sec_qos = &qos;
lsa_open_policy.in.attr = &attr;
- lsa_open_policy.in.system_name = talloc_asprintf(tmp_ctx, "\\%s", lp_netbios_name());
+
+ lsa_open_policy.in.system_name = talloc_asprintf(tmp_ctx, "\\");
+ if (!lsa_open_policy.in.system_name) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
lsa_open_policy.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
lsa_open_policy.out.handle = &lsa_p_handle;
- status = dcerpc_lsa_OpenPolicy2(c.out.dcerpc_pipe, tmp_ctx, &lsa_open_policy);
+ status = dcerpc_lsa_OpenPolicy2(lsa_pipe, tmp_ctx, &lsa_open_policy);
if (!NT_STATUS_IS_OK(status)) {
r->out.error_string = talloc_asprintf(mem_ctx,
"lsa_OpenPolicy2 failed: %s",
@@ -157,20 +656,20 @@ NTSTATUS libnet_JoinDomain(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, stru
talloc_free(tmp_ctx);
return status;
}
-
+
/* Look to see if this is ADS (a fault indicates NT4 or Samba 3.0) */
lsa_query_info2.in.handle = &lsa_p_handle;
lsa_query_info2.in.level = LSA_POLICY_INFO_DNS;
- status = dcerpc_lsa_QueryInfoPolicy2(c.out.dcerpc_pipe, tmp_ctx,
+ status = dcerpc_lsa_QueryInfoPolicy2(lsa_pipe, tmp_ctx,
&lsa_query_info2);
if (!NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
if (!NT_STATUS_IS_OK(status)) {
r->out.error_string = talloc_asprintf(mem_ctx,
- "lsa_QueryInfoPolicy2 failed: %s",
- nt_errstr(status));
+ "lsa_QueryInfoPolicy2 failed: %s",
+ nt_errstr(status));
talloc_free(tmp_ctx);
return status;
}
@@ -182,57 +681,53 @@ NTSTATUS libnet_JoinDomain(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, stru
lsa_query_info.in.handle = &lsa_p_handle;
lsa_query_info.in.level = LSA_POLICY_INFO_DOMAIN;
- status = dcerpc_lsa_QueryInfoPolicy(c.out.dcerpc_pipe, tmp_ctx,
+ status = dcerpc_lsa_QueryInfoPolicy(lsa_pipe, tmp_ctx,
&lsa_query_info);
-
+
if (!NT_STATUS_IS_OK(status)) {
r->out.error_string = talloc_asprintf(mem_ctx,
- "lsa_QueryInfoPolicy2 failed: %s",
- nt_errstr(status));
+ "lsa_QueryInfoPolicy2 failed: %s",
+ nt_errstr(status));
talloc_free(tmp_ctx);
return status;
}
+
domain_sid = lsa_query_info.out.info->domain.sid;
domain_name = lsa_query_info.out.info->domain.name.string;
-
- r->out.domain_sid = talloc_steal(mem_ctx, domain_sid);
- r->out.domain_name = talloc_steal(mem_ctx, domain_name);
- r->out.realm = talloc_steal(mem_ctx, realm);
/*
establish a SAMR connection, on the same CIFS transport
*/
/* Find the original binding string */
- status = dcerpc_parse_binding(tmp_ctx, c.out.dcerpc_pipe->conn->binding_string, &samr_binding);
+ status = dcerpc_parse_binding(tmp_ctx, lsa_pipe->conn->binding_string, &samr_binding);
if (!NT_STATUS_IS_OK(status)) {
- r->out.error_string
- = talloc_asprintf(mem_ctx,
- "Failed to parse dcerpc binding '%s'",
- c.out.dcerpc_pipe->conn->binding_string);
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "Failed to parse lsa binding '%s'",
+ lsa_pipe->conn->binding_string);
talloc_free(tmp_ctx);
return status;
}
/* Make binding string for samr, not the other pipe */
- status = dcerpc_epm_map_binding(tmp_ctx, samr_binding,
+ status = dcerpc_epm_map_binding(tmp_ctx, samr_binding,
DCERPC_SAMR_UUID, DCERPC_SAMR_VERSION,
- c.out.dcerpc_pipe->conn->event_ctx);
+ lsa_pipe->conn->event_ctx);
if (!NT_STATUS_IS_OK(status)) {
- r->out.error_string
- = talloc_asprintf(mem_ctx,
- "Failed to map DCERPC/TCP NCACN_NP pipe for '%s' - %s",
- DCERPC_NETLOGON_UUID, nt_errstr(status));
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "Failed to map DCERPC/TCP NCACN_NP pipe for '%s' - %s",
+ DCERPC_NETLOGON_UUID,
+ nt_errstr(status));
talloc_free(tmp_ctx);
return status;
}
/* Setup a SAMR connection */
- status = dcerpc_secondary_connection(c.out.dcerpc_pipe, &samr_pipe, samr_binding);
+ status = dcerpc_secondary_connection(lsa_pipe, &samr_pipe, samr_binding);
if (!NT_STATUS_IS_OK(status)) {
r->out.error_string = talloc_asprintf(mem_ctx,
- "SAMR secondary connection failed: %s",
- nt_errstr(status));
+ "SAMR secondary connection failed: %s",
+ nt_errstr(status));
talloc_free(tmp_ctx);
return status;
}
@@ -241,11 +736,11 @@ NTSTATUS libnet_JoinDomain(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, stru
DCERPC_SAMR_VERSION, ctx->cred);
if (!NT_STATUS_IS_OK(status)) {
r->out.error_string = talloc_asprintf(mem_ctx,
- "SAMR bind failed: %s",
- nt_errstr(status));
+ "SAMR bind failed: %s",
+ nt_errstr(status));
talloc_free(tmp_ctx);
return status;
- }
+ }
/* prepare samr_Connect */
ZERO_STRUCT(p_handle);
@@ -257,7 +752,7 @@ NTSTATUS libnet_JoinDomain(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, stru
status = dcerpc_samr_Connect(samr_pipe, tmp_ctx, &sc);
if (!NT_STATUS_IS_OK(status)) {
r->out.error_string = talloc_asprintf(mem_ctx,
- "samr_Connect failed: %s\n",
+ "samr_Connect failed: %s",
nt_errstr(status));
talloc_free(tmp_ctx);
return status;
@@ -266,13 +761,13 @@ NTSTATUS libnet_JoinDomain(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, stru
/* check result of samr_Connect */
if (!NT_STATUS_IS_OK(sc.out.result)) {
r->out.error_string = talloc_asprintf(mem_ctx,
- "samr_Connect failed: %s\n",
+ "samr_Connect failed: %s",
nt_errstr(sc.out.result));
status = sc.out.result;
talloc_free(tmp_ctx);
return status;
}
-
+
/* prepare samr_OpenDomain */
ZERO_STRUCT(d_handle);
od.in.connect_handle = &p_handle;
@@ -281,32 +776,34 @@ NTSTATUS libnet_JoinDomain(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, stru
od.out.domain_handle = &d_handle;
/* 4. do a samr_OpenDomain to get a domain handle */
- status = dcerpc_samr_OpenDomain(samr_pipe, tmp_ctx, &od);
+ status = dcerpc_samr_OpenDomain(samr_pipe, tmp_ctx, &od);
if (!NT_STATUS_IS_OK(status)) {
r->out.error_string = talloc_asprintf(mem_ctx,
- "samr_OpenDomain for [%s] failed: %s\n",
- r->in.domain_name, nt_errstr(status));
+ "samr_OpenDomain for [%s] failed: %s",
+ r->in.domain_name,
+ nt_errstr(status));
talloc_free(tmp_ctx);
return status;
}
-
+
/* prepare samr_CreateUser2 */
- ZERO_STRUCT(u_handle);
+ ZERO_STRUCTP(u_handle);
cu.in.domain_handle = &d_handle;
cu.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
samr_account_name.string = r->in.account_name;
cu.in.account_name = &samr_account_name;
cu.in.acct_flags = r->in.acct_type;
- cu.out.user_handle = &u_handle;
+ cu.out.user_handle = u_handle;
cu.out.rid = &rid;
cu.out.access_granted = &access_granted;
/* 4. do a samr_CreateUser2 to get an account handle, or an error */
- status = dcerpc_samr_CreateUser2(samr_pipe, tmp_ctx, &cu);
+ cu_status = dcerpc_samr_CreateUser2(samr_pipe, tmp_ctx, &cu);
+ status = cu_status;
if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
r->out.error_string = talloc_asprintf(mem_ctx,
- "samr_CreateUser2 for [%s] failed: %s\n",
- r->in.domain_name, nt_errstr(status));
+ "samr_CreateUser2 for [%s] failed: %s\n",
+ r->in.domain_name, nt_errstr(status));
talloc_free(tmp_ctx);
return status;
@@ -326,13 +823,13 @@ NTSTATUS libnet_JoinDomain(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, stru
status = dcerpc_samr_LookupNames(samr_pipe, tmp_ctx, &ln);
if (!NT_STATUS_IS_OK(status)) {
r->out.error_string = talloc_asprintf(mem_ctx,
- "samr_LookupNames for [%s] failed: %s\n",
- r->in.account_name, nt_errstr(status));
+ "samr_LookupNames for [%s] failed: %s",
+ r->in.account_name,
+ nt_errstr(status));
talloc_free(tmp_ctx);
return status;
}
-
/* check if we got one RID for the user */
if (ln.out.rids.count != 1) {
r->out.error_string = talloc_asprintf(mem_ctx,
@@ -344,75 +841,76 @@ NTSTATUS libnet_JoinDomain(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, stru
}
/* prepare samr_OpenUser */
- ZERO_STRUCT(u_handle);
+ ZERO_STRUCTP(u_handle);
ou.in.domain_handle = &d_handle;
ou.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
ou.in.rid = ln.out.rids.ids[0];
- ou.out.user_handle = &u_handle;
+ rid = ou.in.rid;
+ ou.out.user_handle = u_handle;
/* 6. do a samr_OpenUser to get a user handle */
- status = dcerpc_samr_OpenUser(samr_pipe, tmp_ctx, &ou);
+ status = dcerpc_samr_OpenUser(samr_pipe, tmp_ctx, &ou);
if (!NT_STATUS_IS_OK(status)) {
r->out.error_string = talloc_asprintf(mem_ctx,
- "samr_OpenUser for [%s] failed: %s\n",
- r->in.account_name, nt_errstr(status));
+ "samr_OpenUser for [%s] failed: %s",
+ r->in.account_name,
+ nt_errstr(status));
talloc_free(tmp_ctx);
return status;
}
}
-
/* Find out what password policy this user has */
- pwp.in.user_handle = &u_handle;
+ pwp.in.user_handle = u_handle;
- status = dcerpc_samr_GetUserPwInfo(samr_pipe, tmp_ctx, &pwp);
+ status = dcerpc_samr_GetUserPwInfo(samr_pipe, tmp_ctx, &pwp);
if (NT_STATUS_IS_OK(status)) {
policy_min_pw_len = pwp.out.info.min_password_length;
}
/* Grab a password of that minimum length */
- r->out.join_password = generate_random_str(mem_ctx, MAX(8, policy_min_pw_len));
+
+ password_str = generate_random_str(tmp_ctx, MAX(8, policy_min_pw_len));
r2.samr_handle.level = LIBNET_SET_PASSWORD_SAMR_HANDLE;
r2.samr_handle.in.account_name = r->in.account_name;
- r2.samr_handle.in.newpassword = r->out.join_password;
- r2.samr_handle.in.user_handle = &u_handle;
+ r2.samr_handle.in.newpassword = password_str;
+ r2.samr_handle.in.user_handle = u_handle;
r2.samr_handle.in.dcerpc_pipe = samr_pipe;
- status = libnet_SetPassword(ctx, tmp_ctx, &r2);
-
- r->out.error_string = r2.samr_handle.out.error_string;
-
+ status = libnet_SetPassword(ctx, tmp_ctx, &r2);
if (!NT_STATUS_IS_OK(status)) {
- talloc_free(tmp_ctx);
+ r->out.error_string = talloc_steal(mem_ctx, r2.samr_handle.out.error_string);
+ talloc_free(tmp_ctx);
return status;
}
/* prepare samr_QueryUserInfo (get flags) */
- qui.in.user_handle = &u_handle;
+ qui.in.user_handle = u_handle;
qui.in.level = 16;
status = dcerpc_samr_QueryUserInfo(samr_pipe, tmp_ctx, &qui);
if (!NT_STATUS_IS_OK(status)) {
- r->out.error_string
- = talloc_asprintf(mem_ctx,
- "samr_QueryUserInfo for [%s] failed: %s\n",
- r->in.account_name, nt_errstr(status));
- talloc_free(tmp_ctx);
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "samr_QueryUserInfo for [%s] failed: %s",
+ r->in.account_name,
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
return status;
}
+
if (!qui.out.info) {
status = NT_STATUS_INVALID_PARAMETER;
r->out.error_string
= talloc_asprintf(mem_ctx,
"samr_QueryUserInfo failed to return qui.out.info for [%s]: %s\n",
r->in.account_name, nt_errstr(status));
- talloc_free(tmp_ctx);
+ talloc_free(tmp_ctx);
return status;
}
- /* Possibly change account type */
- if ((qui.out.info->info16.acct_flags & (ACB_WSTRUST | ACB_SVRTRUST | ACB_DOMTRUST))
- != r->in.acct_type) {
+ /* Possibly change account type (if we are creating a new account) */
+ if (((qui.out.info->info16.acct_flags & (ACB_WSTRUST | ACB_SVRTRUST | ACB_DOMTRUST))
+ != r->in.acct_type) && (!NT_STATUS_EQUAL(cu_status, NT_STATUS_USER_EXISTS))) {
acct_flags = (qui.out.info->info16.acct_flags & ~(ACB_WSTRUST | ACB_SVRTRUST | ACB_DOMTRUST))
| r->in.acct_type;
} else {
@@ -422,212 +920,70 @@ NTSTATUS libnet_JoinDomain(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, stru
acct_flags = (acct_flags & ~ACB_DISABLED);
/* reset flags (if required) */
- if (acct_flags != qui.out.info->info16.acct_flags) {
+ if (acct_flags != qui.out.info->info16.acct_flags) {
ZERO_STRUCT(u_info);
u_info.info16.acct_flags = acct_flags;
- sui.in.user_handle = &u_handle;
+ sui.in.user_handle = u_handle;
sui.in.info = &u_info;
sui.in.level = 16;
dcerpc_samr_SetUserInfo(samr_pipe, tmp_ctx, &sui);
if (!NT_STATUS_IS_OK(status)) {
- r->out.error_string
- = talloc_asprintf(mem_ctx,
- "samr_SetUserInfo for [%s] failed to remove ACB_DISABLED flag: %s\n",
- r->in.account_name, nt_errstr(status));
- talloc_free(tmp_ctx);
- return status;
- }
- }
-
- /* Now, if it was AD, then we want to start looking changing a
- * few more things. Otherwise, we are done. */
- if (!realm) {
- r->out.realm = NULL;
- r->out.kvno = 0;
- talloc_free(tmp_ctx);
- return NT_STATUS_OK;
- }
-
- /* We need to convert between a samAccountName and domain to a
- * DN in the directory. The correct way to do this is with
- * DRSUAPI CrackNames */
-
-
- /* Fiddle with the bindings, so get to DRSUAPI on
- * NCACN_IP_TCP, sealed */
- drsuapi_binding = talloc(tmp_ctx, struct dcerpc_binding);
- *drsuapi_binding = *samr_binding;
- drsuapi_binding->transport = NCACN_IP_TCP;
- drsuapi_binding->endpoint = NULL;
- drsuapi_binding->flags |= DCERPC_SEAL;
-
- status = dcerpc_pipe_connect_b(tmp_ctx,
- &drsuapi_pipe,
- drsuapi_binding,
- DCERPC_DRSUAPI_UUID,
- DCERPC_DRSUAPI_VERSION,
- ctx->cred,
- ctx->event_ctx);
-
- if (!NT_STATUS_IS_OK(status)) {
- r->out.error_string = talloc_asprintf(mem_ctx,
- "Connection to DRSUAPI pipe of PDC of domain '%s' failed: %s",
- r->in.domain_name, nt_errstr(status));
- talloc_free(tmp_ctx);
- return status;
- }
-
- /* get a DRSUAPI pipe handle */
- GUID_from_string(DRSUAPI_DS_BIND_GUID, &drsuapi_bind_guid);
-
- r_drsuapi_bind.in.bind_guid = &drsuapi_bind_guid;
- r_drsuapi_bind.in.bind_info = NULL;
- r_drsuapi_bind.out.bind_handle = &drsuapi_bind_handle;
-
- status = dcerpc_drsuapi_DsBind(drsuapi_pipe, tmp_ctx, &r_drsuapi_bind);
- if (!NT_STATUS_IS_OK(status)) {
- if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
- r->out.error_string
- = talloc_asprintf(mem_ctx,
- "dcerpc_drsuapi_DsBind for [%s\\%s] failed - %s\n",
- domain_name, r->in.account_name,
- dcerpc_errstr(tmp_ctx, drsuapi_pipe->last_fault_code));
- talloc_free(tmp_ctx);
- return status;
- } else {
- r->out.error_string
- = talloc_asprintf(mem_ctx,
- "dcerpc_drsuapi_DsBind for [%s\\%s] failed - %s\n",
- domain_name, r->in.account_name,
- nt_errstr(status));
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "samr_SetUserInfo for [%s] failed to remove ACB_DISABLED flag: %s",
+ r->in.account_name,
+ nt_errstr(status));
talloc_free(tmp_ctx);
return status;
}
- } else if (!W_ERROR_IS_OK(r_drsuapi_bind.out.result)) {
- r->out.error_string
- = talloc_asprintf(mem_ctx,
- "DsBind failed - %s\n", win_errstr(r_drsuapi_bind.out.result));
- talloc_free(tmp_ctx);
- return NT_STATUS_UNSUCCESSFUL;
}
- /* Actually 'crack' the names */
- ZERO_STRUCT(r_crack_names);
- r_crack_names.in.bind_handle = &drsuapi_bind_handle;
- r_crack_names.in.level = 1;
- r_crack_names.in.req.req1.unknown1 = 0x000004e4;
- r_crack_names.in.req.req1.unknown2 = 0x00000407;
- r_crack_names.in.req.req1.count = 1;
- r_crack_names.in.req.req1.names = names;
- r_crack_names.in.req.req1.format_flags = DRSUAPI_DS_NAME_FLAG_NO_FLAGS;
- r_crack_names.in.req.req1.format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT;
- r_crack_names.in.req.req1.format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779;
- names[0].str = talloc_asprintf(tmp_ctx, "%s\\%s", domain_name, r->in.account_name);
-
- status = dcerpc_drsuapi_DsCrackNames(drsuapi_pipe, tmp_ctx, &r_crack_names);
- if (!NT_STATUS_IS_OK(status)) {
- if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
- r->out.error_string
- = talloc_asprintf(mem_ctx,
- "dcerpc_drsuapi_DsCrackNames for [%s\\%s] failed - %s\n",
- domain_name, r->in.account_name,
- dcerpc_errstr(tmp_ctx, drsuapi_pipe->last_fault_code));
- talloc_free(tmp_ctx);
- return status;
- } else {
- r->out.error_string
- = talloc_asprintf(mem_ctx,
- "dcerpc_drsuapi_DsCrackNames for [%s\\%s] failed - %s\n",
- domain_name, r->in.account_name,
- nt_errstr(status));
- talloc_free(tmp_ctx);
- return status;
- }
- } else if (!W_ERROR_IS_OK(r_crack_names.out.result)) {
- r->out.error_string
- = talloc_asprintf(mem_ctx,
- "DsCrackNames failed - %s\n", win_errstr(r_crack_names.out.result));
- talloc_free(tmp_ctx);
- return NT_STATUS_UNSUCCESSFUL;
- } else if (r_crack_names.out.level != 1
- || !r_crack_names.out.ctr.ctr1
- || r_crack_names.out.ctr.ctr1->count != 1
- || r_crack_names.out.ctr.ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
-
- r->out.error_string = talloc_asprintf(mem_ctx, "DsCrackNames failed\n");
+ account_sid = dom_sid_add_rid(mem_ctx, domain_sid, rid);
+ if (!account_sid) {
+ r->out.error_string = NULL;
talloc_free(tmp_ctx);
- return NT_STATUS_UNSUCCESSFUL;
- }
-
- account_dn = ldb_dn_explode(mem_ctx, r_crack_names.out.ctr.ctr1->array[0].result_name);
- if (account_dn == NULL) {
- r->out.error_string
- = talloc_asprintf(mem_ctx, "Invalid account dn: %s",
- r_crack_names.out.ctr.ctr1->array[0].result_name);
- return NT_STATUS_UNSUCCESSFUL;
- }
-
- /* Now we know the user's DN, open with LDAP, read and modify a few things */
-
- remote_ldb_url = talloc_asprintf(tmp_ctx, "ldap://%s",
- drsuapi_binding->host);
- remote_ldb = ldb_wrap_connect(tmp_ctx, remote_ldb_url, 0, NULL);
-
- if (!remote_ldb) {
- return NT_STATUS_UNSUCCESSFUL;
- }
-
- /* search for the user's record */
- ldb_ret = ldb_search(remote_ldb, account_dn, LDB_SCOPE_BASE,
- NULL, attrs, &msgs);
-
- if (ldb_ret != 1) {
- r->out.error_string
- = talloc_asprintf(mem_ctx,
- "ldb_search for %s failed - %s\n",
- ldb_dn_linearize(mem_ctx, account_dn),
- ldb_errstring(remote_ldb));
- return NT_STATUS_UNSUCCESSFUL;
- }
-
- /* If we have a kvno recorded in AD, we need it locally as well */
- r->out.kvno = ldb_msg_find_uint(msgs[0], "msDS-KeyVersionNumber", 0);
-
- /* Prepare a new message, for the modify */
- msg = ldb_msg_new(tmp_ctx);
- if (!msg) {
return NT_STATUS_NO_MEMORY;
}
- msg->dn = msgs[0]->dn;
-
- {
- char *service_principal_name[2];
- char *dns_host_name = strlower_talloc(mem_ctx,
- talloc_asprintf(mem_ctx,
- "%s.%s", lp_netbios_name(), realm));
- service_principal_name[0] = talloc_asprintf(tmp_ctx, "host/%s", dns_host_name);
- service_principal_name[1] = talloc_asprintf(tmp_ctx, "host/%s", strlower_talloc(mem_ctx, lp_netbios_name()));
+ r->out.join_password = password_str;
+ talloc_steal(mem_ctx, password_str);
+
+ r->out.domain_sid = domain_sid;
+ talloc_steal(mem_ctx, domain_sid);
+
+ r->out.account_sid = account_sid;
+ talloc_steal(mem_ctx, account_sid);
+
+ r->out.domain_name = domain_name;
+ talloc_steal(mem_ctx, domain_name);
+ r->out.realm = realm;
+ talloc_steal(mem_ctx, realm);
+ r->out.lsa_pipe = lsa_pipe;
+ talloc_steal(mem_ctx, lsa_pipe);
+ r->out.samr_pipe = samr_pipe;
+ talloc_steal(mem_ctx, samr_pipe);
+ r->out.samr_binding = samr_binding;
+ talloc_steal(mem_ctx, samr_binding);
+ r->out.user_handle = cu.out.user_handle;
+ talloc_steal(mem_ctx, cu.out.user_handle);
+ r->out.error_string = r2.samr_handle.out.error_string;
+ talloc_steal(mem_ctx, r2.samr_handle.out.error_string);
+ r->out.realm = NULL;
+ r->out.kvno = 0;
+ r->out.server_dn_str = NULL;
+ talloc_free(tmp_ctx);
- samdb_msg_add_string(remote_ldb, tmp_ctx, msg, "dNSHostName", dns_host_name);
- samdb_msg_add_string(remote_ldb, tmp_ctx, msg, "servicePrincipalName", service_principal_name[0]);
- samdb_msg_add_string(remote_ldb, tmp_ctx, msg, "servicePrincipalName", service_principal_name[1]);
+ /* Now, if it was AD, then we want to start looking changing a
+ * few more things. Otherwise, we are done. */
+ if (realm
+ && (r->in.acct_type == ACB_SVRTRUST)
+ && (!NT_STATUS_EQUAL(cu_status, NT_STATUS_USER_EXISTS))) {
- ldb_ret = samdb_replace(remote_ldb, tmp_ctx, msg);
- if (ldb_ret != 0) {
- r->out.error_string
- = talloc_asprintf(mem_ctx,
- "Failed to replace entries on %s\n",
- ldb_dn_linearize(mem_ctx, msg->dn));
- return NT_STATUS_INTERNAL_DB_CORRUPTION;
- }
+ status = libnet_JoinADSDomain(ctx, r);
+ return status;
}
- /* close connection */
- talloc_free(tmp_ctx);
-
return NT_STATUS_OK;
}
@@ -636,137 +992,294 @@ static NTSTATUS libnet_Join_primary_domain(struct libnet_context *ctx,
struct libnet_Join *r)
{
NTSTATUS status;
- int ret;
-
+ TALLOC_CTX *tmp_mem;
+ struct libnet_JoinDomain *r2;
+ int ret, rtn;
struct ldb_context *ldb;
- struct libnet_JoinDomain r2;
- const struct ldb_dn *base_dn = ldb_dn_explode(mem_ctx, "cn=Primary Domains");
- const struct ldb_val *prior_secret;
- const struct ldb_val *prior_modified_time;
+ const struct ldb_dn *base_dn;
struct ldb_message **msgs, *msg;
- char *sct;
- const char *attrs[] = {
+ const char *sct;
+ const char * const attrs[] = {
"whenChanged",
"secret",
- "priorSecret"
+ "priorSecret",
"priorChanged",
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.secure_channel_type == SEC_CHAN_BDC) {
- r2.in.acct_type = ACB_SVRTRUST;
+ acct_type = ACB_SVRTRUST;
} else if (r->in.secure_channel_type == SEC_CHAN_WKSTA) {
- r2.in.acct_type = ACB_WSTRUST;
+ acct_type = ACB_WSTRUST;
+ } else {
+ r->out.error_string = NULL;
+ talloc_free(tmp_mem);
+ return NT_STATUS_INVALID_PARAMETER;
}
- r2.in.domain_name = r->in.domain_name;
- r2.in.account_name = talloc_asprintf(mem_ctx, "%s$", lp_netbios_name());
+ if ((r->in.netbios_name != NULL) && (r->in.level != LIBNET_JOIN_AUTOMATIC)) {
+ netbios_name = r->in.netbios_name;
+ } else {
+ netbios_name = talloc_asprintf(tmp_mem, "%s", lp_netbios_name());
+ if (!netbios_name) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_mem);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
- /* Local secrets are stored in secrets.ldb */
- ldb = secrets_db_connect(mem_ctx);
+ 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
+ */
+ ldb = secrets_db_connect(tmp_mem);
if (!ldb) {
r->out.error_string
= talloc_asprintf(mem_ctx,
"Could not open secrets database\n");
+ talloc_free(tmp_mem);
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
}
- /* join domain */
- status = libnet_JoinDomain(ctx, mem_ctx, &r2);
-
- r->out.error_string = r2.out.error_string;
+ /*
+ * 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;
+ 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;
}
-
- sct = talloc_asprintf(mem_ctx, "%d", r->in.secure_channel_type);
- msg = ldb_msg_new(mem_ctx);
-
- /* search for the secret record */
- ret = gendb_search(ldb,
- mem_ctx, base_dn,
- &msgs, attrs,
- "(|" SECRETS_PRIMARY_DOMAIN_FILTER "(realm=%s))",
- r2.out.domain_name, r2.out.realm);
-
- msg->dn = ldb_dn_build_child(mem_ctx, "flatname", r2.out.domain_name, base_dn);
- samdb_msg_add_string(ldb, mem_ctx, msg, "flatname", r2.out.domain_name);
- if (r2.out.realm) {
- samdb_msg_add_string(ldb, mem_ctx, msg, "realm", r2.out.realm);
+ /*
+ * now prepare the record for secrets.ldb
+ */
+ sct = talloc_asprintf(tmp_mem, "%d", r->in.secure_channel_type);
+ if (!sct) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_mem);
+ return NT_STATUS_NO_MEMORY;
}
- samdb_msg_add_string(ldb, mem_ctx, msg, "objectClass", "primaryDomain");
- samdb_msg_add_string(ldb, mem_ctx, msg, "secret", r2.out.join_password);
- samdb_msg_add_string(ldb, mem_ctx, msg, "samAccountName", r2.in.account_name);
+ msg = ldb_msg_new(tmp_mem);
+ if (!msg) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_mem);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ base_dn = ldb_dn_explode(tmp_mem, "cn=Primary Domains");
+ if (!base_dn) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_mem);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg->dn = ldb_dn_build_child(tmp_mem, "flatname", r2->out.domain_name, base_dn);
+ if (!msg->dn) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_mem);
+ return NT_STATUS_NO_MEMORY;
+ }
- samdb_msg_add_string(ldb, mem_ctx, msg, "secureChannelType", sct);
+ rtn = samdb_msg_add_string(ldb, tmp_mem, msg, "flatname", r2->out.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 (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, "objectClass", "primaryDomain");
+ 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, "secret", r2->out.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);
+ 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, "secureChannelType", sct);
+ if (rtn == -1) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_mem);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (r2->out.kvno) {
+ rtn = samdb_msg_add_uint(ldb, tmp_mem, msg, "msDS-KeyVersionNumber",
+ r2->out.kvno);
+ if (rtn == -1) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_mem);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
- if (r2.out.kvno) {
- samdb_msg_add_uint(ldb, mem_ctx, msg, "msDS-KeyVersionNumber",
- r2.out.kvno);
+ if (r2->out.domain_sid) {
+ rtn = samdb_msg_add_dom_sid(ldb, tmp_mem, msg, "objectSid",
+ r2->out.domain_sid);
+ if (rtn == -1) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_mem);
+ return NT_STATUS_NO_MEMORY;
+ }
}
+ /*
+ * search for the secret record
+ * - remove the records we find
+ * - and fetch the old secret and store it under priorSecret
+ */
+ ret = gendb_search(ldb,
+ tmp_mem, base_dn,
+ &msgs, attrs,
+ "(|" SECRETS_PRIMARY_DOMAIN_FILTER "(realm=%s))",
+ r2->out.domain_name, r2->out.realm);
if (ret == 0) {
} else if (ret == -1) {
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));
+ r2->out.domain_name, r2->out.realm, ldb_errstring(ldb));
+ talloc_free(tmp_mem);
return NT_STATUS_INTERNAL_DB_CORRUPTION;
} else {
+ const struct ldb_val *prior_secret;
+ const struct ldb_val *prior_modified_time;
int i;
+
for (i = 0; i < ret; i++) {
ldb_delete(ldb, msgs[i]->dn);
}
prior_secret = ldb_msg_find_ldb_val(msgs[0], "secret");
if (prior_secret) {
- samdb_msg_set_value(ldb, mem_ctx, msg, "priorSecret", prior_secret);
+ rtn = samdb_msg_set_value(ldb, tmp_mem, msg, "priorSecret", prior_secret);
+ if (rtn == -1) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_mem);
+ return NT_STATUS_NO_MEMORY;
+ }
}
- samdb_msg_set_string(ldb, mem_ctx, msg, "secret", r2.out.join_password);
-
+ rtn = samdb_msg_set_string(ldb, tmp_mem, msg, "secret", r2->out.join_password);
+ if (rtn == -1) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_mem);
+ return NT_STATUS_NO_MEMORY;
+ }
+
prior_modified_time = ldb_msg_find_ldb_val(msgs[0],
"whenChanged");
if (prior_modified_time) {
- samdb_msg_set_value(ldb, mem_ctx, msg, "priorWhenChanged",
- prior_modified_time);
+ rtn = samdb_msg_set_value(ldb, tmp_mem, msg, "priorWhenChanged",
+ prior_modified_time);
+ if (rtn == -1) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_mem);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ rtn = samdb_msg_set_string(ldb, tmp_mem, msg, "samAccountName", r2->in.account_name);
+ if (rtn == -1) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_mem);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rtn = samdb_msg_set_string(ldb, tmp_mem, msg, "secureChannelType", sct);
+ if (rtn == -1) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_mem);
+ return NT_STATUS_NO_MEMORY;
}
-
- samdb_msg_set_string(ldb, mem_ctx, msg, "samAccountName", r2.in.account_name);
- samdb_msg_set_string(ldb, mem_ctx, msg, "secureChannelType", sct);
}
/* create the secret */
- ret = samdb_add(ldb, mem_ctx, msg);
+ ret = samdb_add(ldb, tmp_mem, msg);
if (ret != 0) {
- r->out.error_string
- = talloc_asprintf(mem_ctx,
- "Failed to create secret record %s\n",
- ldb_dn_linearize(ldb, msg->dn));
+ r->out.error_string = talloc_asprintf(mem_ctx, "Failed to create secret record %s\n",
+ ldb_dn_linearize(ldb, msg->dn));
+ talloc_free(tmp_mem);
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
+
+ /* move all out parameter to the callers TALLOC_CTX */
+ r->out.error_string = NULL;
+ r->out.join_password = r2->out.join_password;
+ talloc_steal(mem_ctx, r2->out.join_password);
+ r->out.domain_sid = r2->out.domain_sid;
+ talloc_steal(mem_ctx, r2->out.domain_sid);
+ talloc_free(tmp_mem);
return NT_STATUS_OK;
}
NTSTATUS libnet_Join(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_Join *r)
{
- NTSTATUS nt_status;
- struct libnet_Join r2;
- r2.in.secure_channel_type = r->in.secure_channel_type;
- r2.in.domain_name = r->in.domain_name;
-
- if ((r->in.secure_channel_type == SEC_CHAN_WKSTA)
- || (r->in.secure_channel_type == SEC_CHAN_BDC)) {
- nt_status = libnet_Join_primary_domain(ctx, mem_ctx, &r2);
- } else {
- r->out.error_string
- = talloc_asprintf(mem_ctx, "Invalid secure channel type specified (%08X) attempting to join domain %s",
- r->in.secure_channel_type, r->in.domain_name);
- return NT_STATUS_INVALID_PARAMETER;
+ switch (r->in.secure_channel_type) {
+ case SEC_CHAN_WKSTA:
+ return libnet_Join_primary_domain(ctx, mem_ctx, r);
+ case SEC_CHAN_BDC:
+ return libnet_Join_primary_domain(ctx, mem_ctx, r);
+ case SEC_CHAN_DOMAIN:
+ break;
}
- r->out.error_string = r2.out.error_string;
- return nt_status;
+
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "Invalid secure channel type specified (%08X) attempting to join domain %s",
+ r->in.secure_channel_type, r->in.domain_name);
+ return NT_STATUS_INVALID_PARAMETER;
}
diff --git a/source4/libnet/libnet_join.h b/source4/libnet/libnet_join.h
index bd8a6e2a2c..a08147b6d6 100644
--- a/source4/libnet/libnet_join.h
+++ b/source4/libnet/libnet_join.h
@@ -3,6 +3,7 @@
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
@@ -21,10 +22,23 @@
#include "librpc/gen_ndr/ndr_netlogon.h"
+enum libnet_Join_level {
+ LIBNET_JOIN_AUTOMATIC,
+ LIBNET_JOIN_SPECIFIED,
+};
+
+enum libnet_JoinDomain_level {
+ LIBNET_JOINDOMAIN_AUTOMATIC,
+ LIBNET_JOINDOMAIN_SPECIFIED,
+};
+
struct libnet_JoinDomain {
struct {
const char *domain_name;
const char *account_name;
+ const char *netbios_name;
+ const char *binding;
+ enum libnet_JoinDomain_level level;
uint32_t acct_type;
} in;
@@ -34,18 +48,30 @@ struct libnet_JoinDomain {
struct dom_sid *domain_sid;
const char *domain_name;
const char *realm;
- unsigned int kvno;
+ const char *domain_dn_str;
+ const char *account_dn_str;
+ const char *server_dn_str;
+ uint32_t kvno; /* msDS-KeyVersionNumber */
+ struct dcerpc_pipe *lsa_pipe;
+ struct dcerpc_pipe *samr_pipe;
+ struct dcerpc_binding *samr_binding;
+ struct policy_handle *user_handle;
+ struct dom_sid *account_sid;
} out;
};
struct libnet_Join {
struct {
const char *domain_name;
+ const char *netbios_name;
enum netr_SchannelType secure_channel_type;
+ enum libnet_Join_level level;
} in;
struct {
const char *error_string;
+ const char *join_password;
+ struct dom_sid *domain_sid;
} out;
};
diff --git a/source4/libnet/libnet_rpc.c b/source4/libnet/libnet_rpc.c
index 8184b9b828..7b07cedeac 100644
--- a/source4/libnet/libnet_rpc.c
+++ b/source4/libnet/libnet_rpc.c
@@ -36,8 +36,14 @@ static NTSTATUS libnet_RpcConnectSrv(struct libnet_context *ctx, TALLOC_CTX *mem
{
NTSTATUS status;
const char *binding = NULL;
-
- binding = talloc_asprintf(mem_ctx, "ncacn_np:%s", r->in.domain_name);
+ switch (r->level) {
+ case LIBNET_RPC_CONNECT_SERVER:
+ binding = talloc_asprintf(mem_ctx, "ncacn_np:%s", r->in.domain_name);
+ break;
+ case LIBNET_RPC_CONNECT_BINDING:
+ binding = r->in.binding;
+ break;
+ }
status = dcerpc_pipe_connect(mem_ctx, &r->out.dcerpc_pipe,
binding, r->in.dcerpc_iface_uuid,r->in.dcerpc_iface_version,
@@ -115,6 +121,8 @@ NTSTATUS libnet_RpcConnect(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, stru
switch (r->level) {
case LIBNET_RPC_CONNECT_SERVER:
return libnet_RpcConnectSrv(ctx, mem_ctx, r);
+ case LIBNET_RPC_CONNECT_BINDING:
+ return libnet_RpcConnectSrv(ctx, mem_ctx, r);
case LIBNET_RPC_CONNECT_PDC:
return libnet_RpcConnectPdc(ctx, mem_ctx, r);
}
diff --git a/source4/libnet/libnet_rpc.h b/source4/libnet/libnet_rpc.h
index 19a9d3f227..fd65985b13 100644
--- a/source4/libnet/libnet_rpc.h
+++ b/source4/libnet/libnet_rpc.h
@@ -25,7 +25,8 @@
enum libnet_RpcConnect_level {
LIBNET_RPC_CONNECT_SERVER, /* connect to a standalone rpc server */
- LIBNET_RPC_CONNECT_PDC /* connect to a domain pdc */
+ LIBNET_RPC_CONNECT_PDC, /* connect to a domain pdc */
+ LIBNET_RPC_CONNECT_BINDING /* specified binding string */
};
struct libnet_RpcConnect {
@@ -33,6 +34,7 @@ struct libnet_RpcConnect {
struct {
const char *domain_name;
+ const char *binding;
const char *dcerpc_iface_name;
const char *dcerpc_iface_uuid;
uint32_t dcerpc_iface_version;
diff --git a/source4/torture/config.mk b/source4/torture/config.mk
index 75a37a5765..9e331f4736 100644
--- a/source4/torture/config.mk
+++ b/source4/torture/config.mk
@@ -71,6 +71,7 @@ REQUIRED_SUBSYSTEMS = \
# Start SUBSYSTEM TORTURE_RPC
[SUBSYSTEM::TORTURE_RPC]
ADD_OBJ_FILES = \
+ torture/rpc/join.o \
torture/rpc/lsa.o \
torture/rpc/session_key.o \
torture/rpc/echo.o \
diff --git a/source4/torture/nbt/dgram.c b/source4/torture/nbt/dgram.c
index cbebb7564d..0df2fd8a70 100644
--- a/source4/torture/nbt/dgram.c
+++ b/source4/torture/nbt/dgram.c
@@ -217,7 +217,7 @@ static BOOL nbt_test_ntlogon(TALLOC_CTX *mem_ctx,
const char *password;
const char *dom_sid;
- join_ctx = torture_join_domain(TEST_NAME, lp_workgroup(),
+ join_ctx = torture_join_domain(TEST_NAME,
ACB_WSTRUST, &password);
if (join_ctx == NULL) {
printf("Failed to join domain %s as %s\n", lp_workgroup(), TEST_NAME);
diff --git a/source4/torture/rpc/netlogon.c b/source4/torture/rpc/netlogon.c
index a8d881f665..966a0f2e5b 100644
--- a/source4/torture/rpc/netlogon.c
+++ b/source4/torture/rpc/netlogon.c
@@ -1361,7 +1361,7 @@ BOOL torture_rpc_netlogon(void)
mem_ctx = talloc_init("torture_rpc_netlogon");
- join_ctx = torture_join_domain(TEST_MACHINE_NAME, lp_workgroup(), ACB_SVRTRUST,
+ join_ctx = torture_join_domain(TEST_MACHINE_NAME, ACB_SVRTRUST,
&machine_password);
if (!join_ctx) {
talloc_free(mem_ctx);
diff --git a/source4/torture/rpc/testjoin.c b/source4/torture/rpc/testjoin.c
index 1f5c9f3270..2c2eca74e7 100644
--- a/source4/torture/rpc/testjoin.c
+++ b/source4/torture/rpc/testjoin.c
@@ -29,10 +29,15 @@
#include "librpc/gen_ndr/ndr_samr.h"
#include "system/time.h"
#include "lib/crypto/crypto.h"
+#include "libnet/libnet.h"
+#include "lib/cmdline/popt_common.h"
+#include "lib/ldb/include/ldb.h"
+
struct test_join {
struct dcerpc_pipe *p;
struct policy_handle user_handle;
+ struct libnet_JoinDomain *libnet_r;
const char *dom_sid;
};
@@ -272,13 +277,59 @@ failed:
struct test_join *torture_join_domain(const char *machine_name,
- const char *domain,
- uint16_t acct_flags,
+ uint32_t acct_flags,
const char **machine_password)
{
- char *username = talloc_asprintf(NULL, "%s$", machine_name);
- struct test_join *tj = torture_create_testuser(username, domain, acct_flags, machine_password);
- talloc_free(username);
+ NTSTATUS status;
+ struct libnet_context *libnet_ctx;
+ struct libnet_JoinDomain *libnet_r;
+ struct test_join *tj;
+
+ tj = talloc(NULL, struct test_join);
+ if (!tj) return NULL;
+
+ libnet_r = talloc(tj, struct libnet_JoinDomain);
+ if (!libnet_r) {
+ talloc_free(tj);
+ return NULL;
+ }
+
+ libnet_ctx = libnet_context_init(NULL);
+ if (!libnet_ctx) {
+ talloc_free(tj);
+ return NULL;
+ }
+
+ tj->libnet_r = libnet_r;
+
+ libnet_ctx->cred = cmdline_credentials;
+ libnet_r->in.binding = lp_parm_string(-1, "torture", "binding");
+ libnet_r->in.level = LIBNET_JOINDOMAIN_SPECIFIED;
+ libnet_r->in.netbios_name = machine_name;
+ libnet_r->in.account_name = talloc_asprintf(libnet_r, "%s$", machine_name);
+ if (!libnet_r->in.account_name) {
+ talloc_free(tj);
+ return NULL;
+ }
+
+ libnet_r->in.acct_type = acct_flags;
+
+ status = libnet_JoinDomain(libnet_ctx, libnet_r, libnet_r);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Domain join failed - %s.\n", nt_errstr(status)));
+ talloc_free(tj);
+ return NULL;
+ }
+ tj->p = libnet_r->out.samr_pipe;
+ tj->user_handle = *libnet_r->out.user_handle;
+ tj->dom_sid = dom_sid_string(tj, libnet_r->out.domain_sid);
+ *machine_password = libnet_r->out.join_password;
+
+ DEBUG(0, ("%s joined domain %s (%s).\n",
+ libnet_r->in.netbios_name,
+ libnet_r->out.domain_name,
+ tj->dom_sid));
+
return tj;
}
@@ -292,24 +343,94 @@ struct policy_handle *torture_join_samr_user_policy(struct test_join *join)
return &join->user_handle;
}
+NTSTATUS torture_leave_ads_domain(TALLOC_CTX *mem_ctx, struct libnet_JoinDomain *libnet_r)
+{
+ NTSTATUS status;
+ int rtn;
+ TALLOC_CTX *tmp_ctx;
+
+ struct ldb_dn *server_dn;
+ struct ldb_context *ldb_ctx;
+
+ char *remote_ldb_url;
+
+ /* Check if we are a domain controller. If not, exit. */
+ if (!libnet_r->out.server_dn_str) {
+ return NT_STATUS_OK;
+ }
+
+ tmp_ctx = talloc_named(mem_ctx, 0, "torture_leave temporary context");
+ if (!tmp_ctx) {
+ libnet_r->out.error_string = NULL;
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ldb_ctx = ldb_init(tmp_ctx);
+ if (!ldb_ctx) {
+ libnet_r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Remove CN=Servers,... entry from the AD. */
+ server_dn = ldb_dn_explode(tmp_ctx, libnet_r->out.server_dn_str);
+ if (!server_dn) {
+ libnet_r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ remote_ldb_url = talloc_asprintf(tmp_ctx, "ldap://%s", libnet_r->out.samr_binding->host);
+ if (!remote_ldb_url) {
+ libnet_r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rtn = ldb_connect(ldb_ctx, remote_ldb_url, 0, NULL);
+ if (rtn != 0) {
+ libnet_r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ rtn = ldb_delete(ldb_ctx, server_dn);
+ if (rtn != 0) {
+ libnet_r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(0, ("%s removed successfully.\n", libnet_r->out.server_dn_str));
+
+ talloc_free(tmp_ctx);
+ return status;
+}
+
/*
leave the domain, deleting the machine acct
*/
+
void torture_leave_domain(struct test_join *join)
{
struct samr_DeleteUser d;
NTSTATUS status;
- if (!GUID_all_zero(&join->user_handle.uuid)) {
- d.in.user_handle = &join->user_handle;
- d.out.user_handle = &join->user_handle;
-
- status = dcerpc_samr_DeleteUser(join->p, join, &d);
- if (!NT_STATUS_IS_OK(status)) {
- printf("Delete of machine account failed\n");
- }
+ d.in.user_handle = &join->user_handle;
+ d.out.user_handle = &join->user_handle;
+
+ /* Delete machine account */
+ status = dcerpc_samr_DeleteUser(join->p, join, &d);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Delete of machine account failed\n");
+ } else {
+ printf("Delete of machine account was successful.\n");
}
+ if (join->libnet_r) {
+ status = torture_leave_ads_domain(join, join->libnet_r);
+ }
+
talloc_free(join);
}
@@ -337,7 +458,7 @@ struct test_join_ads_dc *torture_join_domain_ads_dc(const char *machine_name,
return NULL;
}
- join->join = torture_join_domain(machine_name, domain,
+ join->join = torture_join_domain(machine_name,
ACB_SVRTRUST,
machine_password);
diff --git a/source4/torture/torture.c b/source4/torture/torture.c
index b4aefb8780..930a63bbb2 100644
--- a/source4/torture/torture.c
+++ b/source4/torture/torture.c
@@ -2312,6 +2312,7 @@ static struct {
{"RPC-ROT", torture_rpc_rot, 0},
{"RPC-DSSETUP", torture_rpc_dssetup, 0},
{"RPC-ALTERCONTEXT", torture_rpc_alter_context, 0},
+ {"RPC-JOIN", torture_rpc_join, 0},
/* local (no server) testers */
{"LOCAL-NTLMSSP", torture_ntlmssp_self_check, 0},
diff --git a/source4/utils/net/net_join.c b/source4/utils/net/net_join.c
index a1455ef036..cb2ed3006b 100644
--- a/source4/utils/net/net_join.c
+++ b/source4/utils/net/net_join.c
@@ -29,7 +29,7 @@ int net_join(struct net_context *ctx, int argc, const char **argv)
{
NTSTATUS status;
struct libnet_context *libnetctx;
- struct libnet_Join r;
+ struct libnet_Join *r;
char *tmp;
const char *domain_name;
enum netr_SchannelType secure_channel_type = SEC_CHAN_WKSTA;
@@ -62,23 +62,29 @@ int net_join(struct net_context *ctx, int argc, const char **argv)
return -1;
}
libnetctx->cred = ctx->credentials;
-
+ r = talloc(ctx->mem_ctx, struct libnet_Join);
+ if (!r) {
+ return -1;
+ }
/* prepare password change */
- r.in.domain_name = domain_name;
- r.in.secure_channel_type = secure_channel_type;
- r.out.error_string = NULL;
+ r->in.netbios_name = lp_netbios_name();
+ r->in.domain_name = domain_name;
+ r->in.secure_channel_type = secure_channel_type;
+ r->in.level = LIBNET_JOIN_AUTOMATIC;
+ r->out.error_string = NULL;
/* do the domain join */
- status = libnet_Join(libnetctx, ctx->mem_ctx, &r);
+ status = libnet_Join(libnetctx, r, r);
+
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0,("libnet_Join returned %s: %s\n",
nt_errstr(status),
- r.out.error_string));
+ r->out.error_string));
+ talloc_free(r);
+ talloc_free(libnetctx);
return -1;
}
-
talloc_free(libnetctx);
-
return 0;
}