diff options
-rw-r--r-- | source4/libnet/config.mk | 2 | ||||
-rw-r--r-- | source4/libnet/libnet_join.c | 1247 | ||||
-rw-r--r-- | source4/libnet/libnet_join.h | 28 | ||||
-rw-r--r-- | source4/libnet/libnet_rpc.c | 12 | ||||
-rw-r--r-- | source4/libnet/libnet_rpc.h | 4 | ||||
-rw-r--r-- | source4/torture/config.mk | 1 | ||||
-rw-r--r-- | source4/torture/nbt/dgram.c | 2 | ||||
-rw-r--r-- | source4/torture/rpc/netlogon.c | 2 | ||||
-rw-r--r-- | source4/torture/rpc/testjoin.c | 149 | ||||
-rw-r--r-- | source4/torture/torture.c | 1 | ||||
-rw-r--r-- | source4/utils/net/net_join.c | 24 |
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; } |