r20061: use a DsBindInfo28 structure
[jra/samba/.git] / source4 / libnet / libnet_unbecome_dc.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    Copyright (C) Stefan Metzmacher      2006
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include "includes.h"
22 #include "libnet/libnet.h"
23 #include "libcli/composite/composite.h"
24 #include "libcli/cldap/cldap.h"
25 #include "lib/ldb/include/ldb.h"
26 #include "lib/ldb/include/ldb_errors.h"
27 #include "lib/db_wrap.h"
28 #include "dsdb/samdb/samdb.h"
29 #include "dsdb/common/flags.h"
30 #include "librpc/gen_ndr/ndr_drsuapi_c.h"
31
32 struct libnet_UnbecomeDC_state {
33         struct composite_context *creq;
34
35         struct libnet_context *libnet;
36
37         struct {
38                 struct cldap_socket *sock;
39                 struct cldap_netlogon io;
40                 struct nbt_cldap_netlogon_5 netlogon5;
41         } cldap;
42
43         struct {
44                 struct ldb_context *ldb;
45         } ldap;
46
47         struct {
48                 struct dcerpc_binding *binding;
49                 struct dcerpc_pipe *pipe;
50                 struct drsuapi_DsBind bind_r;
51                 struct GUID bind_guid;
52                 struct drsuapi_DsBindInfoCtr bind_info_ctr;
53                 struct drsuapi_DsBindInfo28 local_info28;
54                 struct drsuapi_DsBindInfo28 remote_info28;
55                 struct policy_handle bind_handle;
56                 struct drsuapi_DsRemoveDSServer rm_ds_srv_r;
57         } drsuapi;
58
59         struct {
60                 /* input */
61                 const char *dns_name;
62                 const char *netbios_name;
63
64                 /* constructed */
65                 struct GUID guid;
66                 const char *dn_str;
67         } domain;
68
69         struct {
70                 /* constructed */
71                 const char *config_dn_str;
72         } forest;
73
74         struct {
75                 /* input */
76                 const char *address;
77
78                 /* constructed */
79                 const char *dns_name;
80                 const char *netbios_name;
81                 const char *site_name;
82         } source_dsa;
83
84         struct {
85                 /* input */
86                 const char *netbios_name;
87
88                 /* constructed */
89                 const char *dns_name;
90                 const char *site_name;
91                 const char *computer_dn_str;
92                 const char *server_dn_str;
93                 uint32_t user_account_control;
94         } dest_dsa;
95 };
96
97 static void unbecomeDC_connect_ldap(struct libnet_UnbecomeDC_state *s);
98
99 static void unbecomeDC_recv_cldap(struct cldap_request *req)
100 {
101         struct libnet_UnbecomeDC_state *s = talloc_get_type(req->async.private,
102                                             struct libnet_UnbecomeDC_state);
103         struct composite_context *c = s->creq;
104
105         c->status = cldap_netlogon_recv(req, s, &s->cldap.io);
106         if (!composite_is_ok(c)) return;
107
108         s->cldap.netlogon5 = s->cldap.io.out.netlogon.logon5;
109
110         s->domain.dns_name              = s->cldap.netlogon5.dns_domain;
111         s->domain.netbios_name          = s->cldap.netlogon5.domain;
112         s->domain.guid                  = s->cldap.netlogon5.domain_uuid;
113
114         s->source_dsa.dns_name          = s->cldap.netlogon5.pdc_dns_name;
115         s->source_dsa.netbios_name      = s->cldap.netlogon5.pdc_name;
116         s->source_dsa.site_name         = s->cldap.netlogon5.server_site;
117
118         s->dest_dsa.site_name           = s->cldap.netlogon5.client_site;
119
120         unbecomeDC_connect_ldap(s);
121 }
122
123 static void unbecomeDC_send_cldap(struct libnet_UnbecomeDC_state *s)
124 {
125         struct composite_context *c = s->creq;
126         struct cldap_request *req;
127
128         s->cldap.io.in.dest_address     = s->source_dsa.address;
129         s->cldap.io.in.realm            = s->domain.dns_name;
130         s->cldap.io.in.host             = s->dest_dsa.netbios_name;
131         s->cldap.io.in.user             = NULL;
132         s->cldap.io.in.domain_guid      = NULL;
133         s->cldap.io.in.domain_sid       = NULL;
134         s->cldap.io.in.acct_control     = -1;
135         s->cldap.io.in.version          = 6;
136
137         s->cldap.sock = cldap_socket_init(s, s->libnet->event_ctx);
138         if (composite_nomem(s->cldap.sock, c)) return;
139
140         req = cldap_netlogon_send(s->cldap.sock, &s->cldap.io);
141         if (composite_nomem(req, c)) return;
142         req->async.fn           = unbecomeDC_recv_cldap;
143         req->async.private      = s;
144 }
145
146 static NTSTATUS unbecomeDC_ldap_connect(struct libnet_UnbecomeDC_state *s)
147 {
148         char *url;
149
150         url = talloc_asprintf(s, "ldap://%s/", s->source_dsa.dns_name);
151         NT_STATUS_HAVE_NO_MEMORY(url);
152
153         s->ldap.ldb = ldb_wrap_connect(s, url,
154                                        NULL,
155                                        s->libnet->cred,
156                                        0, NULL);
157         talloc_free(url);
158         if (s->ldap.ldb == NULL) {
159                 return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
160         }
161
162         return NT_STATUS_OK;
163 }
164
165 static NTSTATUS unbecomeDC_ldap_rootdse(struct libnet_UnbecomeDC_state *s)
166 {
167         int ret;
168         struct ldb_result *r;
169         struct ldb_dn *basedn;
170         static const char *attrs[] = {
171                 "defaultNamingContext",
172                 "configurationNamingContext",
173                 NULL
174         };
175
176         basedn = ldb_dn_new(s, s->ldap.ldb, NULL);
177         NT_STATUS_HAVE_NO_MEMORY(basedn);
178
179         ret = ldb_search(s->ldap.ldb, basedn, LDB_SCOPE_BASE, 
180                          "(objectClass=*)", attrs, &r);
181         talloc_free(basedn);
182         if (ret != LDB_SUCCESS) {
183                 return NT_STATUS_LDAP(ret);
184         } else if (r->count != 1) {
185                 talloc_free(r);
186                 return NT_STATUS_INVALID_NETWORK_RESPONSE;
187         }
188         talloc_steal(s, r);
189
190         s->domain.dn_str        = ldb_msg_find_attr_as_string(r->msgs[0], "defaultNamingContext", NULL);
191         if (!s->domain.dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
192         talloc_steal(s, s->domain.dn_str);
193
194         s->forest.config_dn_str = ldb_msg_find_attr_as_string(r->msgs[0], "configurationNamingContext", NULL);
195         if (!s->forest.config_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
196         talloc_steal(s, s->forest.config_dn_str);
197
198         s->dest_dsa.server_dn_str = talloc_asprintf(s, "CN=%s,CN=Servers,CN=%s,CN=Sites,%s",
199                                                     s->dest_dsa.netbios_name,
200                                                     s->dest_dsa.site_name,
201                                                     s->forest.config_dn_str);
202         NT_STATUS_HAVE_NO_MEMORY(s->dest_dsa.server_dn_str);
203
204         talloc_free(r);
205         return NT_STATUS_OK;
206 }
207
208 static NTSTATUS unbecomeDC_ldap_computer_object(struct libnet_UnbecomeDC_state *s)
209 {
210         int ret;
211         struct ldb_result *r;
212         struct ldb_dn *basedn;
213         char *filter;
214         static const char *attrs[] = {
215                 "distinguishedName",
216                 "userAccountControl",
217                 NULL
218         };
219
220         basedn = ldb_dn_new(s, s->ldap.ldb, s->domain.dn_str);
221         NT_STATUS_HAVE_NO_MEMORY(basedn);
222
223         filter = talloc_asprintf(basedn, "(&(|(objectClass=user)(objectClass=computer))(sAMAccountName=%s$))",
224                                  s->dest_dsa.netbios_name);
225         NT_STATUS_HAVE_NO_MEMORY(filter);
226
227         ret = ldb_search(s->ldap.ldb, basedn, LDB_SCOPE_SUBTREE, 
228                          filter, attrs, &r);
229         talloc_free(basedn);
230         if (ret != LDB_SUCCESS) {
231                 return NT_STATUS_LDAP(ret);
232         } else if (r->count != 1) {
233                 talloc_free(r);
234                 return NT_STATUS_INVALID_NETWORK_RESPONSE;
235         }
236
237         s->dest_dsa.computer_dn_str     = samdb_result_string(r->msgs[0], "distinguishedName", NULL);
238         if (!s->dest_dsa.computer_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
239         talloc_steal(s, s->dest_dsa.computer_dn_str);
240
241         s->dest_dsa.user_account_control = samdb_result_uint(r->msgs[0], "userAccountControl", 0);
242
243         talloc_free(r);
244         return NT_STATUS_OK;
245 }
246
247 static NTSTATUS unbecomeDC_ldap_modify_computer(struct libnet_UnbecomeDC_state *s)
248 {
249         int ret;
250         struct ldb_message *msg;
251         uint32_t user_account_control = UF_WORKSTATION_TRUST_ACCOUNT;
252         uint32_t i;
253
254         /* as the value is already as we want it to be, we're done */
255         if (s->dest_dsa.user_account_control == user_account_control) {
256                 return NT_STATUS_OK;
257         }
258
259         /* make a 'modify' msg, and only for serverReference */
260         msg = ldb_msg_new(s);
261         NT_STATUS_HAVE_NO_MEMORY(msg);
262         msg->dn = ldb_dn_new(msg, s->ldap.ldb, s->dest_dsa.computer_dn_str);
263         NT_STATUS_HAVE_NO_MEMORY(msg->dn);
264
265         ret = ldb_msg_add_fmt(msg, "userAccountControl", "%u", user_account_control);
266         if (ret != 0) {
267                 talloc_free(msg);
268                 return NT_STATUS_NO_MEMORY;
269         }
270
271         /* mark all the message elements (should be just one)
272            as LDB_FLAG_MOD_REPLACE */
273         for (i=0;i<msg->num_elements;i++) {
274                 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
275         }
276
277         ret = ldb_modify(s->ldap.ldb, msg);
278         talloc_free(msg);
279         if (ret != LDB_SUCCESS) {
280                 return NT_STATUS_LDAP(ret);
281         }
282
283         s->dest_dsa.user_account_control = user_account_control;
284
285         return NT_STATUS_OK;
286 }
287
288 static NTSTATUS unbecomeDC_ldap_move_computer(struct libnet_UnbecomeDC_state *s)
289 {
290         int ret;
291         struct ldb_result *r;
292         struct ldb_dn *basedn;
293         struct ldb_dn *old_dn;
294         struct ldb_dn *new_dn;
295         static const char *_1_1_attrs[] = {
296                 "1.1",
297                 NULL
298         };
299
300         basedn = ldb_dn_new_fmt(s, s->ldap.ldb, "<WKGUID=aa312825768811d1aded00c04fd8d5cd,%s>",
301                                 s->domain.dn_str);
302         NT_STATUS_HAVE_NO_MEMORY(basedn);
303
304         ret = ldb_search(s->ldap.ldb, basedn, LDB_SCOPE_BASE,
305                          "(objectClass=*)", _1_1_attrs, &r);
306         talloc_free(basedn);
307         if (ret != LDB_SUCCESS) {
308                 return NT_STATUS_LDAP(ret);
309         } else if (r->count != 1) {
310                 talloc_free(r);
311                 return NT_STATUS_INVALID_NETWORK_RESPONSE;
312         }
313
314         old_dn = ldb_dn_new(r, s->ldap.ldb, s->dest_dsa.computer_dn_str);
315         NT_STATUS_HAVE_NO_MEMORY(old_dn);
316
317         new_dn = r->msgs[0]->dn;
318
319         if (!ldb_dn_add_child_fmt(new_dn, "CN=%s", s->dest_dsa.netbios_name)) {
320                 talloc_free(r);
321                 return NT_STATUS_NO_MEMORY;
322         }
323
324         if (ldb_dn_compare(old_dn, new_dn) == 0) {
325                 /* we don't need to rename if the old and new dn match */
326                 talloc_free(r);
327                 return NT_STATUS_OK;
328         }
329
330         ret = ldb_rename(s->ldap.ldb, old_dn, new_dn);
331         talloc_free(r);
332         if (ret != LDB_SUCCESS) {
333                 return NT_STATUS_LDAP(ret);
334         }
335
336         return NT_STATUS_OK;
337 }
338
339 static void unbecomeDC_drsuapi_connect_send(struct libnet_UnbecomeDC_state *s);
340
341 static void unbecomeDC_connect_ldap(struct libnet_UnbecomeDC_state *s)
342 {
343         struct composite_context *c = s->creq;
344
345         c->status = unbecomeDC_ldap_connect(s);
346         if (!composite_is_ok(c)) return;
347
348         c->status = unbecomeDC_ldap_rootdse(s);
349         if (!composite_is_ok(c)) return;
350
351         c->status = unbecomeDC_ldap_computer_object(s);
352         if (!composite_is_ok(c)) return;
353
354         c->status = unbecomeDC_ldap_modify_computer(s);
355         if (!composite_is_ok(c)) return;
356
357         c->status = unbecomeDC_ldap_move_computer(s);
358         if (!composite_is_ok(c)) return;
359
360         unbecomeDC_drsuapi_connect_send(s);
361 }
362
363 static void unbecomeDC_drsuapi_connect_recv(struct composite_context *creq);
364
365 static void unbecomeDC_drsuapi_connect_send(struct libnet_UnbecomeDC_state *s)
366 {
367         struct composite_context *c = s->creq;
368         struct composite_context *creq;
369         char *binding_str;
370
371         binding_str = talloc_asprintf(s, "ncacn_ip_tcp:%s[seal]", s->source_dsa.dns_name);
372         if (composite_nomem(binding_str, c)) return;
373
374         c->status = dcerpc_parse_binding(s, binding_str, &s->drsuapi.binding);
375         talloc_free(binding_str);
376         if (!composite_is_ok(c)) return;
377
378         creq = dcerpc_pipe_connect_b_send(s, s->drsuapi.binding, &dcerpc_table_drsuapi,
379                                           s->libnet->cred, s->libnet->event_ctx);
380         composite_continue(c, creq, unbecomeDC_drsuapi_connect_recv, s);
381 }
382
383 static void unbecomeDC_drsuapi_bind_send(struct libnet_UnbecomeDC_state *s);
384
385 static void unbecomeDC_drsuapi_connect_recv(struct composite_context *req)
386 {
387         struct libnet_UnbecomeDC_state *s = talloc_get_type(req->async.private_data,
388                                             struct libnet_UnbecomeDC_state);
389         struct composite_context *c = s->creq;
390
391         c->status = dcerpc_pipe_connect_b_recv(req, s, &s->drsuapi.pipe);
392         if (!composite_is_ok(c)) return;
393
394         unbecomeDC_drsuapi_bind_send(s);
395 }
396
397 static void unbecomeDC_drsuapi_bind_recv(struct rpc_request *req);
398
399 static void unbecomeDC_drsuapi_bind_send(struct libnet_UnbecomeDC_state *s)
400 {
401         struct composite_context *c = s->creq;
402         struct rpc_request *req;
403         struct drsuapi_DsBindInfo28 *bind_info28;
404
405         GUID_from_string(DRSUAPI_DS_BIND_GUID, &s->drsuapi.bind_guid);
406
407         bind_info28                             = &s->drsuapi.local_info28;
408         bind_info28->supported_extensions       = 0;
409         bind_info28->site_guid                  = GUID_zero();
410         bind_info28->u1                         = 508;
411         bind_info28->repl_epoch                 = 0;
412
413         s->drsuapi.bind_info_ctr.length         = 28;
414         s->drsuapi.bind_info_ctr.info.info28    = *bind_info28;
415
416         s->drsuapi.bind_r.in.bind_guid = &s->drsuapi.bind_guid;
417         s->drsuapi.bind_r.in.bind_info = &s->drsuapi.bind_info_ctr;
418         s->drsuapi.bind_r.out.bind_handle = &s->drsuapi.bind_handle;
419
420         req = dcerpc_drsuapi_DsBind_send(s->drsuapi.pipe, s, &s->drsuapi.bind_r);
421         composite_continue_rpc(c, req, unbecomeDC_drsuapi_bind_recv, s);
422 }
423
424 static void unbecomeDC_drsuapi_remove_ds_server_send(struct libnet_UnbecomeDC_state *s);
425
426 static void unbecomeDC_drsuapi_bind_recv(struct rpc_request *req)
427 {
428         struct libnet_UnbecomeDC_state *s = talloc_get_type(req->async.private,
429                                             struct libnet_UnbecomeDC_state);
430         struct composite_context *c = s->creq;
431
432         c->status = dcerpc_ndr_request_recv(req);
433         if (!composite_is_ok(c)) return;
434
435         if (!W_ERROR_IS_OK(s->drsuapi.bind_r.out.result)) {
436                 composite_error(c, werror_to_ntstatus(s->drsuapi.bind_r.out.result));
437                 return;
438         }
439
440         ZERO_STRUCT(s->drsuapi.remote_info28);
441         if (s->drsuapi.bind_r.out.bind_info) {
442                 switch (s->drsuapi.bind_r.out.bind_info->length) {
443                 case 24: {
444                         struct drsuapi_DsBindInfo24 *info24;
445                         info24 = &s->drsuapi.bind_r.out.bind_info->info.info24;
446                         s->drsuapi.remote_info28.supported_extensions   = info24->supported_extensions;
447                         s->drsuapi.remote_info28.site_guid              = info24->site_guid;
448                         s->drsuapi.remote_info28.u1                     = info24->u1;
449                         s->drsuapi.remote_info28.repl_epoch             = 0;
450                         break;
451                 }
452                 case 28:
453                         s->drsuapi.remote_info28 = s->drsuapi.bind_r.out.bind_info->info.info28;
454                         break;
455                 }
456         }
457
458         unbecomeDC_drsuapi_remove_ds_server_send(s);
459 }
460
461 static void unbecomeDC_drsuapi_remove_ds_server_recv(struct rpc_request *req);
462
463 static void unbecomeDC_drsuapi_remove_ds_server_send(struct libnet_UnbecomeDC_state *s)
464 {
465         struct composite_context *c = s->creq;
466         struct rpc_request *req;
467         struct drsuapi_DsRemoveDSServer *r = &s->drsuapi.rm_ds_srv_r;
468
469         r->in.bind_handle       = &s->drsuapi.bind_handle;
470         r->in.level             = 1;
471         r->in.req.req1.server_dn= s->dest_dsa.server_dn_str;
472         r->in.req.req1.domain_dn= s->domain.dn_str;
473         r->in.req.req1.unknown  = 0x00000001;
474
475         req = dcerpc_drsuapi_DsRemoveDSServer_send(s->drsuapi.pipe, s, r);
476         composite_continue_rpc(c, req, unbecomeDC_drsuapi_remove_ds_server_recv, s);
477 }
478
479 static void unbecomeDC_drsuapi_remove_ds_server_recv(struct rpc_request *req)
480 {
481         struct libnet_UnbecomeDC_state *s = talloc_get_type(req->async.private,
482                                             struct libnet_UnbecomeDC_state);
483         struct composite_context *c = s->creq;
484         struct drsuapi_DsRemoveDSServer *r = &s->drsuapi.rm_ds_srv_r;
485
486         c->status = dcerpc_ndr_request_recv(req);
487         if (!composite_is_ok(c)) return;
488
489         if (!W_ERROR_IS_OK(r->out.result)) {
490                 composite_error(c, werror_to_ntstatus(r->out.result));
491                 return;
492         }
493
494         if (r->out.level != 1) {
495                 composite_error(c, NT_STATUS_INVALID_NETWORK_RESPONSE);
496                 return;
497         }
498                 
499         if (!W_ERROR_IS_OK(r->out.res.res1.status)) {
500                 composite_error(c, werror_to_ntstatus(r->out.res.res1.status));
501                 return;
502         }
503
504         composite_done(c);
505 }
506
507 struct composite_context *libnet_UnbecomeDC_send(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_UnbecomeDC *r)
508 {
509         struct composite_context *c;
510         struct libnet_UnbecomeDC_state *s;
511         char *tmp_name;
512
513         c = composite_create(mem_ctx, ctx->event_ctx);
514         if (c == NULL) return NULL;
515
516         s = talloc_zero(c, struct libnet_UnbecomeDC_state);
517         if (composite_nomem(s, c)) return c;
518         c->private_data = s;
519         s->creq         = c;
520         s->libnet       = ctx;
521
522         /* Domain input */
523         s->domain.dns_name      = talloc_strdup(s, r->in.domain_dns_name);
524         if (composite_nomem(s->domain.dns_name, c)) return c;
525         s->domain.netbios_name  = talloc_strdup(s, r->in.domain_netbios_name);
526         if (composite_nomem(s->domain.netbios_name, c)) return c;
527
528         /* Source DSA input */
529         s->source_dsa.address   = talloc_strdup(s, r->in.source_dsa_address);
530         if (composite_nomem(s->source_dsa.address, c)) return c;
531
532         /* Destination DSA input */
533         s->dest_dsa.netbios_name= talloc_strdup(s, r->in.dest_dsa_netbios_name);
534         if (composite_nomem(s->dest_dsa.netbios_name, c)) return c;
535
536         /* Destination DSA dns_name construction */
537         tmp_name                = strlower_talloc(s, s->dest_dsa.netbios_name);
538         if (composite_nomem(tmp_name, c)) return c;
539         s->dest_dsa.dns_name    = talloc_asprintf_append(tmp_name, ".%s",
540                                                          s->domain.dns_name);
541         if (composite_nomem(s->dest_dsa.dns_name, c)) return c;
542
543         unbecomeDC_send_cldap(s);
544         return c;
545 }
546
547 NTSTATUS libnet_UnbecomeDC_recv(struct composite_context *c, TALLOC_CTX *mem_ctx, struct libnet_UnbecomeDC *r)
548 {
549         NTSTATUS status;
550
551         status = composite_wait(c);
552
553         ZERO_STRUCT(r->out);
554
555         talloc_free(c);
556         return status;
557 }
558
559 NTSTATUS libnet_UnbecomeDC(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_UnbecomeDC *r)
560 {
561         NTSTATUS status;
562         struct composite_context *c;
563         c = libnet_UnbecomeDC_send(ctx, mem_ctx, r);
564         status = libnet_UnbecomeDC_recv(c, mem_ctx, r);
565         return status;
566 }