r4585: don't consider LookupSids3 failing with NT_STATUS_ACCESS_DENIED (as w2k3 does) or
[samba.git] / source4 / torture / rpc / testjoin.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    utility code to join/leave a domain
5
6    Copyright (C) Andrew Tridgell 2004
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 /*
24   this code is used by other torture modules to join/leave a domain
25   as either a member, bdc or thru a trust relationship
26 */
27
28 #include "includes.h"
29 #include "librpc/gen_ndr/ndr_samr.h"
30
31 struct test_join {
32         struct dcerpc_pipe *p;
33         const char *machine_password;
34         struct policy_handle user_handle;
35 };
36
37
38 static NTSTATUS DeleteUser_byname(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, 
39                                   struct policy_handle *handle, const char *name)
40 {
41         NTSTATUS status;
42         struct samr_DeleteUser d;
43         struct policy_handle user_handle;
44         uint32_t rid;
45         struct samr_LookupNames n;
46         struct samr_String sname;
47         struct samr_OpenUser r;
48
49         sname.string = name;
50
51         n.in.domain_handle = handle;
52         n.in.num_names = 1;
53         n.in.names = &sname;
54
55         status = dcerpc_samr_LookupNames(p, mem_ctx, &n);
56         if (NT_STATUS_IS_OK(status)) {
57                 rid = n.out.rids.ids[0];
58         } else {
59                 return status;
60         }
61
62         r.in.domain_handle = handle;
63         r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
64         r.in.rid = rid;
65         r.out.user_handle = &user_handle;
66
67         status = dcerpc_samr_OpenUser(p, mem_ctx, &r);
68         if (!NT_STATUS_IS_OK(status)) {
69                 printf("OpenUser(%s) failed - %s\n", name, nt_errstr(status));
70                 return status;
71         }
72
73         d.in.user_handle = &user_handle;
74         d.out.user_handle = &user_handle;
75         status = dcerpc_samr_DeleteUser(p, mem_ctx, &d);
76         if (!NT_STATUS_IS_OK(status)) {
77                 return status;
78         }
79
80         return NT_STATUS_OK;
81 }
82
83 /*
84   join the domain as a test machine
85   an opaque pointer is returned. Pass it to torture_leave_domain() 
86   when finished
87 */
88 struct test_join *torture_join_domain(const char *machine_name, 
89                                       const char *domain,
90                                       uint16 acct_flags,
91                                       const char **machine_password)
92 {
93         NTSTATUS status;
94         struct samr_Connect c;
95         struct samr_CreateUser2 r;
96         struct samr_OpenDomain o;
97         struct samr_LookupDomain l;
98         struct samr_GetUserPwInfo pwp;
99         struct samr_SetUserInfo s;
100         union samr_UserInfo u;
101         struct policy_handle handle;
102         struct policy_handle domain_handle;
103         uint32_t access_granted;
104         uint32_t rid;
105         DATA_BLOB session_key;
106         struct samr_String name;
107         int policy_min_pw_len = 0;
108         struct test_join *join;
109
110         join = talloc_p(NULL, struct test_join);
111         if (join == NULL) {
112                 return NULL;
113         }
114
115         ZERO_STRUCTP(join);
116
117         printf("Connecting to SAMR\n");
118
119         status = torture_rpc_connection(&join->p, 
120                                         DCERPC_SAMR_NAME,
121                                         DCERPC_SAMR_UUID,
122                                         DCERPC_SAMR_VERSION);
123         if (!NT_STATUS_IS_OK(status)) {
124                 goto failed;
125         }
126
127         c.in.system_name = NULL;
128         c.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
129         c.out.connect_handle = &handle;
130
131         status = dcerpc_samr_Connect(join->p, join, &c);
132         if (!NT_STATUS_IS_OK(status)) {
133                 const char *errstr = nt_errstr(status);
134                 if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
135                         errstr = dcerpc_errstr(join, join->p->last_fault_code);
136                 }
137                 printf("samr_Connect failed - %s\n", errstr);
138                 goto failed;
139         }
140
141         printf("Opening domain %s\n", domain);
142
143         name.string = domain;
144         l.in.connect_handle = &handle;
145         l.in.domain = &name;
146
147         status = dcerpc_samr_LookupDomain(join->p, join, &l);
148         if (!NT_STATUS_IS_OK(status)) {
149                 printf("LookupDomain failed - %s\n", nt_errstr(status));
150                 goto failed;
151         }
152
153         o.in.connect_handle = &handle;
154         o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
155         o.in.sid = l.out.sid;
156         o.out.domain_handle = &domain_handle;
157
158         status = dcerpc_samr_OpenDomain(join->p, join, &o);
159         if (!NT_STATUS_IS_OK(status)) {
160                 printf("OpenDomain failed - %s\n", nt_errstr(status));
161                 goto failed;
162         }
163
164         printf("Creating machine account %s\n", machine_name);
165
166 again:
167         name.string = talloc_asprintf(join, "%s$", machine_name);
168         r.in.domain_handle = &domain_handle;
169         r.in.account_name = &name;
170         r.in.acct_flags = acct_flags;
171         r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
172         r.out.user_handle = &join->user_handle;
173         r.out.access_granted = &access_granted;
174         r.out.rid = &rid;
175
176         status = dcerpc_samr_CreateUser2(join->p, join, &r);
177
178         if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
179                 status = DeleteUser_byname(join->p, join, &domain_handle, name.string);
180                 if (NT_STATUS_IS_OK(status)) {
181                         goto again;
182                 }
183         }
184
185         if (!NT_STATUS_IS_OK(status)) {
186                 printf("CreateUser2 failed - %s\n", nt_errstr(status));
187                 goto failed;
188         }
189
190         pwp.in.user_handle = &join->user_handle;
191
192         status = dcerpc_samr_GetUserPwInfo(join->p, join, &pwp);
193         if (NT_STATUS_IS_OK(status)) {
194                 policy_min_pw_len = pwp.out.info.min_password_length;
195         }
196
197         join->machine_password = generate_random_str(join, MAX(8, policy_min_pw_len));
198
199         printf("Setting machine account password '%s'\n", join->machine_password);
200
201         s.in.user_handle = &join->user_handle;
202         s.in.info = &u;
203         s.in.level = 24;
204
205         encode_pw_buffer(u.info24.password.data, join->machine_password, STR_UNICODE);
206         u.info24.pw_len = strlen(join->machine_password);
207
208         status = dcerpc_fetch_session_key(join->p, &session_key);
209         if (!NT_STATUS_IS_OK(status)) {
210                 printf("SetUserInfo level %u - no session key - %s\n",
211                        s.in.level, nt_errstr(status));
212                 torture_leave_domain(join);
213                 goto failed;
214         }
215
216         arcfour_crypt_blob(u.info24.password.data, 516, &session_key);
217
218         status = dcerpc_samr_SetUserInfo(join->p, join, &s);
219         if (!NT_STATUS_IS_OK(status)) {
220                 printf("SetUserInfo failed - %s\n", nt_errstr(status));
221                 goto failed;
222         }
223
224         s.in.user_handle = &join->user_handle;
225         s.in.info = &u;
226         s.in.level = 16;
227
228         u.info16.acct_flags = acct_flags;
229
230         printf("Resetting ACB flags\n");
231
232         status = dcerpc_samr_SetUserInfo(join->p, join, &s);
233         if (!NT_STATUS_IS_OK(status)) {
234                 printf("SetUserInfo failed - %s\n", nt_errstr(status));
235                 goto failed;
236         }
237
238         if (machine_password) {
239                 *machine_password = join->machine_password;
240         }
241
242         return join;
243
244 failed:
245         torture_leave_domain(join);
246         return NULL;
247 }
248
249 struct dcerpc_pipe *torture_join_samr_pipe(struct test_join *join) 
250 {
251         return join->p;
252 }
253
254 /*
255   leave the domain, deleting the machine acct
256 */
257 void torture_leave_domain(struct test_join *join)
258 {
259         struct samr_DeleteUser d;
260         NTSTATUS status;
261
262         if (!GUID_all_zero(&join->user_handle.uuid)) {
263                 d.in.user_handle = &join->user_handle;
264                 d.out.user_handle = &join->user_handle;
265                 
266                 status = dcerpc_samr_DeleteUser(join->p, join, &d);
267                 if (!NT_STATUS_IS_OK(status)) {
268                         printf("Delete of machine account failed\n");
269                 }
270         }
271
272         if (join->p) {
273                 torture_rpc_close(join->p);
274         }
275
276         talloc_free(join);
277 }
278
279
280 struct test_join_ads_dc {
281         struct test_join *join;
282 };
283
284 struct test_join_ads_dc *torture_join_domain_ads_dc(const char *machine_name, 
285                                                     const char *domain,
286                                                     const char **machine_password)
287 {
288         struct test_join_ads_dc *join;
289
290         join = talloc_p(NULL, struct test_join_ads_dc);
291         if (join == NULL) {
292                 return NULL;
293         }
294
295         join->join = torture_join_domain(machine_name, domain,
296                                         ACB_SVRTRUST,
297                                         machine_password);
298
299         if (!join->join) {
300                 return NULL;
301         }
302
303         /* do netlogon DrsEnumerateDomainTrusts */
304
305         /* modify userAccountControl from 4096 to 532480 */
306         
307         /* modify RDN to OU=Domain Controllers and skip the $ from server name */
308
309         /* ask objectVersion of Schema Partition */
310
311         /* ask rIDManagerReferenz of the Domain Partition */
312
313         /* ask fsMORoleOwner of the RID-Manager$ object
314          * returns CN=NTDS Settings,CN=<DC>,CN=Servers,CN=Default-First-Site-Name, ...
315          */
316
317         /* ask for dnsHostName of CN=<DC>,CN=Servers,CN=Default-First-Site-Name, ... */
318
319         /* ask for objectGUID of CN=NTDS Settings,CN=<DC>,CN=Servers,CN=Default-First-Site-Name, ... */
320
321         /* ask for * of CN=Default-First-Site-Name, ... */
322
323         /* search (&(|(objectClass=user)(objectClass=computer))(sAMAccountName=<machine_name>$)) in Domain Partition 
324          * attributes : distinguishedName, userAccountControl
325          */
326
327         /* ask * for CN=<machine_name>,CN=Servers,CN=Default-First-Site-Name,... 
328          * should fail with noSuchObject
329          */
330
331         /* add CN=<machine_name>,CN=Servers,CN=Default-First-Site-Name,... 
332          *
333          * objectClass = server
334          * systemFlags = 50000000
335          * serverReferenz = CN=<machine_name>,OU=Domain Controllers,...
336          */
337
338         /* ask for * of CN=NTDS Settings,CN=<machine_name>,CN=Servers,CN=Default-First-Site-Name, ...
339          * should fail with noSuchObject
340          */
341
342         /* search for (ncname=<domain_nc>) in CN=Partitions,CN=Configuration,... 
343          * attributes: ncName, dnsRoot
344          */
345
346         /* modify add CN=<machine_name>,CN=Servers,CN=Default-First-Site-Name,...
347          * serverReferenz = CN=<machine_name>,OU=Domain Controllers,...
348          * should fail with attributeOrValueExists
349          */
350
351         /* modify replace CN=<machine_name>,CN=Servers,CN=Default-First-Site-Name,...
352          * serverReferenz = CN=<machine_name>,OU=Domain Controllers,...
353          */
354
355         /* DsReplicaAdd to create the CN=NTDS Settings,CN=<machine_name>,CN=Servers,CN=Default-First-Site-Name, ...
356          * needs to be tested
357          */
358
359         return join;
360 }
361                 
362 void torture_leave_domain_ads_dc(struct test_join_ads_dc *join)
363 {
364
365         if (join->join) {
366                 torture_leave_domain(join->join);
367         }
368
369         talloc_free(join);
370 }