This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
+ the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
*/
#include "includes.h"
-#include "librpc/gen_ndr/ndr_samr.h"
+#include "torture/torture.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"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+
+#include "libcli/auth/libcli_auth.h"
+#include "torture/rpc/rpc.h"
+#include "libcli/security/security.h"
+#include "param/param.h"
struct test_join {
struct dcerpc_pipe *p;
- const char *machine_password;
struct policy_handle user_handle;
+ struct libnet_JoinDomain *libnet_r;
+ struct dom_sid *dom_sid;
+ const char *dom_netbios_name;
+ const char *dom_dns_name;
+ struct dom_sid *user_sid;
+ struct GUID user_guid;
+ const char *netbios_name;
};
struct policy_handle user_handle;
uint32_t rid;
struct samr_LookupNames n;
- struct samr_String sname;
+ struct lsa_String sname;
struct samr_OpenUser r;
sname.string = name;
}
/*
- join the domain as a test machine
+ create a test user in the domain
an opaque pointer is returned. Pass it to torture_leave_domain()
when finished
*/
-struct test_join *torture_join_domain(const char *machine_name,
- const char *domain,
- uint16_t acct_flags,
- const char **machine_password)
+
+struct test_join *torture_create_testuser(struct torture_context *torture,
+ const char *username,
+ const char *domain,
+ uint16_t acct_type,
+ const char **random_password)
{
NTSTATUS status;
struct samr_Connect c;
uint32_t access_granted;
uint32_t rid;
DATA_BLOB session_key;
- struct samr_String name;
+ struct lsa_String name;
+
int policy_min_pw_len = 0;
struct test_join *join;
+ char *random_pw;
+ const char *dc_binding = torture_setting_string(torture, "dc_binding", NULL);
join = talloc(NULL, struct test_join);
if (join == NULL) {
ZERO_STRUCTP(join);
printf("Connecting to SAMR\n");
-
- status = torture_rpc_connection(&join->p,
- DCERPC_SAMR_NAME,
- DCERPC_SAMR_UUID,
- DCERPC_SAMR_VERSION);
+
+ if (dc_binding) {
+ status = dcerpc_pipe_connect(join,
+ &join->p,
+ dc_binding,
+ &ndr_table_samr,
+ cmdline_credentials, NULL, torture->lp_ctx);
+
+ } else {
+ status = torture_rpc_connection(torture,
+ &join->p,
+ &ndr_table_samr);
+ }
if (!NT_STATUS_IS_OK(status)) {
- goto failed;
+ return NULL;
}
c.in.system_name = NULL;
errstr = dcerpc_errstr(join, join->p->last_fault_code);
}
printf("samr_Connect failed - %s\n", errstr);
- goto failed;
+ return NULL;
}
printf("Opening domain %s\n", domain);
goto failed;
}
+ talloc_steal(join, l.out.sid);
+ join->dom_sid = l.out.sid;
+ join->dom_netbios_name = talloc_strdup(join, domain);
+ if (!join->dom_netbios_name) goto failed;
+
o.in.connect_handle = &handle;
o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
o.in.sid = l.out.sid;
goto failed;
}
- printf("Creating machine account %s\n", machine_name);
+ printf("Creating account %s\n", username);
again:
- name.string = talloc_asprintf(join, "%s$", machine_name);
+ name.string = username;
r.in.domain_handle = &domain_handle;
r.in.account_name = &name;
- r.in.acct_flags = acct_flags;
+ r.in.acct_flags = acct_type;
r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
r.out.user_handle = &join->user_handle;
r.out.access_granted = &access_granted;
goto failed;
}
+ join->user_sid = dom_sid_add_rid(join, join->dom_sid, rid);
+
pwp.in.user_handle = &join->user_handle;
status = dcerpc_samr_GetUserPwInfo(join->p, join, &pwp);
policy_min_pw_len = pwp.out.info.min_password_length;
}
- join->machine_password = generate_random_str(join, MAX(8, policy_min_pw_len));
+ random_pw = generate_random_str(join, MAX(8, policy_min_pw_len));
- printf("Setting machine account password '%s'\n", join->machine_password);
+ printf("Setting account password '%s'\n", random_pw);
+ ZERO_STRUCT(u);
s.in.user_handle = &join->user_handle;
s.in.info = &u;
s.in.level = 24;
- encode_pw_buffer(u.info24.password.data, join->machine_password, STR_UNICODE);
- u.info24.pw_len = strlen(join->machine_password);
+ encode_pw_buffer(u.info24.password.data, random_pw, STR_UNICODE);
+ u.info24.pw_len = strlen(random_pw);
status = dcerpc_fetch_session_key(join->p, &session_key);
if (!NT_STATUS_IS_OK(status)) {
goto failed;
}
+ ZERO_STRUCT(u);
s.in.user_handle = &join->user_handle;
s.in.info = &u;
- s.in.level = 16;
+ s.in.level = 21;
+
+ u.info21.acct_flags = acct_type | ACB_PWNOEXP;
+ u.info21.fields_present = SAMR_FIELD_ACCT_FLAGS | SAMR_FIELD_DESCRIPTION | SAMR_FIELD_COMMENT | SAMR_FIELD_FULL_NAME;
- u.info16.acct_flags = acct_flags;
+ u.info21.comment.string = talloc_asprintf(join,
+ "Tortured by Samba4: %s",
+ timestring(join, time(NULL)));
+
+ u.info21.full_name.string = talloc_asprintf(join,
+ "Torture account for Samba4: %s",
+ timestring(join, time(NULL)));
+
+ u.info21.description.string = talloc_asprintf(join,
+ "Samba4 torture account created by host %s: %s",
+ lp_netbios_name(torture->lp_ctx),
+ timestring(join, time(NULL)));
- printf("Resetting ACB flags\n");
+ printf("Resetting ACB flags, force pw change time\n");
status = dcerpc_samr_SetUserInfo(join->p, join, &s);
if (!NT_STATUS_IS_OK(status)) {
goto failed;
}
- if (machine_password) {
- *machine_password = join->machine_password;
+ if (random_password) {
+ *random_password = random_pw;
}
return join;
return NULL;
}
+
+_PUBLIC_ struct test_join *torture_join_domain(struct torture_context *tctx,
+ const char *machine_name,
+ uint32_t acct_flags,
+ struct cli_credentials **machine_credentials)
+{
+ NTSTATUS status;
+ struct libnet_context *libnet_ctx;
+ struct libnet_JoinDomain *libnet_r;
+ struct test_join *tj;
+ struct samr_SetUserInfo s;
+ union samr_UserInfo u;
+
+ tj = talloc(tctx, 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(tctx->ev, tctx->lp_ctx);
+ if (!libnet_ctx) {
+ talloc_free(tj);
+ return NULL;
+ }
+
+ tj->libnet_r = libnet_r;
+
+ libnet_ctx->cred = cmdline_credentials;
+ libnet_r->in.binding = torture_setting_string(tctx, "binding", NULL);
+ if (!libnet_r->in.binding) {
+ libnet_r->in.binding = talloc_asprintf(libnet_r, "ncacn_np:%s", torture_setting_string(tctx, "host", NULL));
+ }
+ 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;
+ libnet_r->in.recreate_account = true;
+
+ status = libnet_JoinDomain(libnet_ctx, libnet_r, libnet_r);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (libnet_r->out.error_string) {
+ DEBUG(0, ("Domain join failed - %s\n", libnet_r->out.error_string));
+ } else {
+ 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 = libnet_r->out.domain_sid;
+ talloc_steal(tj, libnet_r->out.domain_sid);
+ tj->dom_netbios_name = libnet_r->out.domain_name;
+ talloc_steal(tj, libnet_r->out.domain_name);
+ tj->dom_dns_name = libnet_r->out.realm;
+ talloc_steal(tj, libnet_r->out.realm);
+ tj->user_guid = libnet_r->out.account_guid;
+ tj->netbios_name = talloc_strdup(tj, machine_name);
+ if (!tj->netbios_name) {
+ talloc_free(tj);
+ return NULL;
+ }
+
+ ZERO_STRUCT(u);
+ s.in.user_handle = &tj->user_handle;
+ s.in.info = &u;
+ s.in.level = 21;
+
+ u.info21.fields_present = SAMR_FIELD_DESCRIPTION | SAMR_FIELD_COMMENT | SAMR_FIELD_FULL_NAME;
+ u.info21.comment.string = talloc_asprintf(tj,
+ "Tortured by Samba4: %s",
+ timestring(tj, time(NULL)));
+ u.info21.full_name.string = talloc_asprintf(tj,
+ "Torture account for Samba4: %s",
+ timestring(tj, time(NULL)));
+
+ u.info21.description.string = talloc_asprintf(tj,
+ "Samba4 torture account created by host %s: %s",
+ lp_netbios_name(tctx->lp_ctx), timestring(tj, time(NULL)));
+
+ status = dcerpc_samr_SetUserInfo(tj->p, tj, &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetUserInfo (non-critical) failed - %s\n", nt_errstr(status));
+ }
+
+ *machine_credentials = cli_credentials_init(tj);
+ cli_credentials_set_conf(*machine_credentials, tctx->lp_ctx);
+ cli_credentials_set_workstation(*machine_credentials, machine_name, CRED_SPECIFIED);
+ cli_credentials_set_domain(*machine_credentials, libnet_r->out.domain_name, CRED_SPECIFIED);
+ if (libnet_r->out.realm) {
+ cli_credentials_set_realm(*machine_credentials, libnet_r->out.realm, CRED_SPECIFIED);
+ }
+ cli_credentials_set_username(*machine_credentials, libnet_r->in.account_name, CRED_SPECIFIED);
+ cli_credentials_set_password(*machine_credentials, libnet_r->out.join_password, CRED_SPECIFIED);
+ if (acct_flags & ACB_SVRTRUST) {
+ cli_credentials_set_secure_channel_type(*machine_credentials,
+ SEC_CHAN_BDC);
+ } else if (acct_flags & ACB_WSTRUST) {
+ cli_credentials_set_secure_channel_type(*machine_credentials,
+ SEC_CHAN_WKSTA);
+ } else {
+ DEBUG(0, ("Invalid account type specificed to torture_join_domain\n"));
+ talloc_free(*machine_credentials);
+ return NULL;
+ }
+
+ return tj;
+}
+
struct dcerpc_pipe *torture_join_samr_pipe(struct test_join *join)
{
return join->p;
}
+struct policy_handle *torture_join_samr_user_policy(struct test_join *join)
+{
+ return &join->user_handle;
+}
+
+static NTSTATUS torture_leave_ads_domain(TALLOC_CTX *mem_ctx, struct libnet_JoinDomain *libnet_r)
+{
+ 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_new(tmp_ctx, ldb_ctx, libnet_r->out.server_dn_str);
+ if (! ldb_dn_validate(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;
+ }
+
+ ldb_set_opaque(ldb_ctx, "credentials", cmdline_credentials);
+
+ 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 NT_STATUS_OK;
+}
+
/*
leave the domain, deleting the machine acct
*/
-void torture_leave_domain(struct test_join *join)
+
+_PUBLIC_ 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");
- }
+ if (!join) {
+ return;
}
-
- if (join->p) {
- torture_rpc_close(join->p);
+ 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);
}
+/*
+ return the dom sid for a test join
+*/
+_PUBLIC_ const struct dom_sid *torture_join_sid(struct test_join *join)
+{
+ return join->dom_sid;
+}
+
+const struct dom_sid *torture_join_user_sid(struct test_join *join)
+{
+ return join->user_sid;
+}
+
+const char *torture_join_netbios_name(struct test_join *join)
+{
+ return join->netbios_name;
+}
+
+const struct GUID *torture_join_user_guid(struct test_join *join)
+{
+ return &join->user_guid;
+}
+const char *torture_join_dom_netbios_name(struct test_join *join)
+{
+ return join->dom_netbios_name;
+}
+
+const char *torture_join_dom_dns_name(struct test_join *join)
+{
+ return join->dom_dns_name;
+}
+
+const char *torture_join_server_dn_str(struct test_join *join)
+{
+ if (join->libnet_r) {
+ return join->libnet_r->out.server_dn_str;
+ }
+ return NULL;
+}
+
+
+#if 0 /* Left as the documentation of the join process, but see new implementation in libnet_become_dc.c */
struct test_join_ads_dc {
struct test_join *join;
};
struct test_join_ads_dc *torture_join_domain_ads_dc(const char *machine_name,
const char *domain,
- const char **machine_password)
+ struct cli_credentials **machine_credentials)
{
struct test_join_ads_dc *join;
return NULL;
}
- join->join = torture_join_domain(machine_name, domain,
+ join->join = torture_join_domain(machine_name,
ACB_SVRTRUST,
- machine_password);
+ machine_credentials);
if (!join->join) {
return NULL;
}
- /* do netlogon DrsEnumerateDomainTrusts */
-
- /* modify userAccountControl from 4096 to 532480 */
+/* W2K: */
+ /* W2K: modify userAccountControl from 4096 to 532480 */
- /* modify RDN to OU=Domain Controllers and skip the $ from server name */
+ /* W2K: modify RDN to OU=Domain Controllers and skip the $ from server name */
/* ask objectVersion of Schema Partition */
* serverReferenz = CN=<machine_name>,OU=Domain Controllers,...
*/
- /* DsReplicaAdd to create the CN=NTDS Settings,CN=<machine_name>,CN=Servers,CN=Default-First-Site-Name, ...
- * needs to be tested
+ /* DsAddEntry to create the CN=NTDS Settings,CN=<machine_name>,CN=Servers,CN=Default-First-Site-Name, ...
+ *
*/
- return join;
-}
-
-void torture_leave_domain_ads_dc(struct test_join_ads_dc *join)
-{
+ /* replicate CN=Schema,CN=Configuration,...
+ * using DRSUAPI_DS_BIND_GUID_W2K ("6abec3d1-3054-41c8-a362-5a0c5b7d5d71")
+ *
+ */
- if (join->join) {
- torture_leave_domain(join->join);
- }
+ /* replicate CN=Configuration,...
+ * using DRSUAPI_DS_BIND_GUID_W2K ("6abec3d1-3054-41c8-a362-5a0c5b7d5d71")
+ *
+ */
- talloc_free(join);
+ /* replicate Domain Partition
+ * using DRSUAPI_DS_BIND_GUID_W2K ("6abec3d1-3054-41c8-a362-5a0c5b7d5d71")
+ *
+ */
+
+ /* call DsReplicaUpdateRefs() for all partitions like this:
+ * req1: struct drsuapi_DsReplicaUpdateRefsRequest1
+ * naming_context : *
+ * naming_context: struct drsuapi_DsReplicaObjectIdentifier
+ * __ndr_size : 0x000000ae (174)
+ * __ndr_size_sid : 0x00000000 (0)
+ * guid : 00000000-0000-0000-0000-000000000000
+ * sid : S-0-0
+ * dn : 'CN=Schema,CN=Configuration,DC=w2k3,DC=vmnet1,DC=vm,DC=base'
+ * dest_dsa_dns_name : *
+ * dest_dsa_dns_name : '4a0df188-a0b8-47ea-bbe5-e614723f16dd._msdcs.w2k3.vmnet1.vm.base'
+ * dest_dsa_guid : 4a0df188-a0b8-47ea-bbe5-e614723f16dd
+ * options : 0x0000001c (28)
+ * 0: DRSUAPI_DS_REPLICA_UPDATE_ASYNCHRONOUS_OPERATION
+ * 0: DRSUAPI_DS_REPLICA_UPDATE_WRITEABLE
+ * 1: DRSUAPI_DS_REPLICA_UPDATE_ADD_REFERENCE
+ * 1: DRSUAPI_DS_REPLICA_UPDATE_DELETE_REFERENCE
+ * 1: DRSUAPI_DS_REPLICA_UPDATE_0x00000010
+ *
+ * 4a0df188-a0b8-47ea-bbe5-e614723f16dd is the objectGUID the DsAddEntry() returned for the
+ * CN=NTDS Settings,CN=<machine_name>,CN=Servers,CN=Default-First-Site-Name, ...
+ */
+
+/* W2K3: see libnet/libnet_become_dc.c */
+ return join;
}
+
+#endif