r20044: implement the drsuapi connect and ds_bind
[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 policy_handle bind_handle;
53         } drsuapi;
54
55         struct {
56                 /* input */
57                 const char *dns_name;
58                 const char *netbios_name;
59
60                 /* constructed */
61                 struct GUID guid;
62                 const char *dn_str;
63         } domain;
64
65         struct {
66                 /* constructed */
67                 const char *config_dn_str;
68         } forest;
69
70         struct {
71                 /* input */
72                 const char *address;
73
74                 /* constructed */
75                 const char *dns_name;
76                 const char *netbios_name;
77                 const char *site_name;
78         } source_dsa;
79
80         struct {
81                 /* input */
82                 const char *netbios_name;
83
84                 /* constructed */
85                 const char *dns_name;
86                 const char *site_name;
87                 const char *computer_dn_str;
88                 const char *server_dn_str;
89                 uint32_t user_account_control;
90         } dest_dsa;
91 };
92
93 static void unbecomeDC_connect_ldap(struct libnet_UnbecomeDC_state *s);
94
95 static void unbecomeDC_recv_cldap(struct cldap_request *req)
96 {
97         struct libnet_UnbecomeDC_state *s = talloc_get_type(req->async.private,
98                                             struct libnet_UnbecomeDC_state);
99         struct composite_context *c = s->creq;
100
101         c->status = cldap_netlogon_recv(req, s, &s->cldap.io);
102         if (!composite_is_ok(c)) return;
103
104         s->cldap.netlogon5 = s->cldap.io.out.netlogon.logon5;
105
106         s->domain.dns_name              = s->cldap.netlogon5.dns_domain;
107         s->domain.netbios_name          = s->cldap.netlogon5.domain;
108         s->domain.guid                  = s->cldap.netlogon5.domain_uuid;
109
110         s->source_dsa.dns_name          = s->cldap.netlogon5.pdc_dns_name;
111         s->source_dsa.netbios_name      = s->cldap.netlogon5.pdc_name;
112         s->source_dsa.site_name         = s->cldap.netlogon5.server_site;
113
114         s->dest_dsa.site_name           = s->cldap.netlogon5.client_site;
115
116         unbecomeDC_connect_ldap(s);
117 }
118
119 static void unbecomeDC_send_cldap(struct libnet_UnbecomeDC_state *s)
120 {
121         struct composite_context *c = s->creq;
122         struct cldap_request *req;
123
124         s->cldap.io.in.dest_address     = s->source_dsa.address;
125         s->cldap.io.in.realm            = s->domain.dns_name;
126         s->cldap.io.in.host             = s->dest_dsa.netbios_name;
127         s->cldap.io.in.user             = NULL;
128         s->cldap.io.in.domain_guid      = NULL;
129         s->cldap.io.in.domain_sid       = NULL;
130         s->cldap.io.in.acct_control     = -1;
131         s->cldap.io.in.version          = 6;
132
133         s->cldap.sock = cldap_socket_init(s, s->libnet->event_ctx);
134         if (composite_nomem(s->cldap.sock, c)) return;
135
136         req = cldap_netlogon_send(s->cldap.sock, &s->cldap.io);
137         if (composite_nomem(req, c)) return;
138         req->async.fn           = unbecomeDC_recv_cldap;
139         req->async.private      = s;
140 }
141
142 static NTSTATUS unbecomeDC_ldap_connect(struct libnet_UnbecomeDC_state *s)
143 {
144         char *url;
145
146         url = talloc_asprintf(s, "ldap://%s/", s->source_dsa.dns_name);
147         NT_STATUS_HAVE_NO_MEMORY(url);
148
149         s->ldap.ldb = ldb_wrap_connect(s, url,
150                                        NULL,
151                                        s->libnet->cred,
152                                        0, NULL);
153         talloc_free(url);
154         if (s->ldap.ldb == NULL) {
155                 return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
156         }
157
158         return NT_STATUS_OK;
159 }
160
161 static NTSTATUS unbecomeDC_ldap_rootdse(struct libnet_UnbecomeDC_state *s)
162 {
163         int ret;
164         struct ldb_result *r;
165         struct ldb_dn *basedn;
166         static const char *attrs[] = {
167                 "defaultNamingContext",
168                 "configurationNamingContext",
169                 NULL
170         };
171
172         basedn = ldb_dn_new(s, s->ldap.ldb, NULL);
173         NT_STATUS_HAVE_NO_MEMORY(basedn);
174
175         ret = ldb_search(s->ldap.ldb, basedn, LDB_SCOPE_BASE, 
176                          "(objectClass=*)", attrs, &r);
177         talloc_free(basedn);
178         if (ret != LDB_SUCCESS) {
179                 return NT_STATUS_LDAP(ret);
180         } else if (r->count != 1) {
181                 talloc_free(r);
182                 return NT_STATUS_INVALID_NETWORK_RESPONSE;
183         }
184         talloc_steal(s, r);
185
186         s->domain.dn_str        = ldb_msg_find_attr_as_string(r->msgs[0], "defaultNamingContext", NULL);
187         if (!s->domain.dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
188         talloc_steal(s, s->domain.dn_str);
189
190         s->forest.config_dn_str = ldb_msg_find_attr_as_string(r->msgs[0], "configurationNamingContext", NULL);
191         if (!s->forest.config_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
192         talloc_steal(s, s->forest.config_dn_str);
193
194         talloc_free(r);
195         return NT_STATUS_OK;
196 }
197
198 static NTSTATUS unbecomeDC_ldap_computer_object(struct libnet_UnbecomeDC_state *s)
199 {
200         int ret;
201         struct ldb_result *r;
202         struct ldb_dn *basedn;
203         char *filter;
204         static const char *attrs[] = {
205                 "distinguishedName",
206                 "userAccountControl",
207                 NULL
208         };
209
210         basedn = ldb_dn_new(s, s->ldap.ldb, s->domain.dn_str);
211         NT_STATUS_HAVE_NO_MEMORY(basedn);
212
213         filter = talloc_asprintf(basedn, "(&(|(objectClass=user)(objectClass=computer))(sAMAccountName=%s$))",
214                                  s->dest_dsa.netbios_name);
215         NT_STATUS_HAVE_NO_MEMORY(filter);
216
217         ret = ldb_search(s->ldap.ldb, basedn, LDB_SCOPE_SUBTREE, 
218                          filter, attrs, &r);
219         talloc_free(basedn);
220         if (ret != LDB_SUCCESS) {
221                 return NT_STATUS_LDAP(ret);
222         } else if (r->count != 1) {
223                 talloc_free(r);
224                 return NT_STATUS_INVALID_NETWORK_RESPONSE;
225         }
226
227         s->dest_dsa.computer_dn_str     = samdb_result_string(r->msgs[0], "distinguishedName", NULL);
228         if (!s->dest_dsa.computer_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
229         talloc_steal(s, s->dest_dsa.computer_dn_str);
230
231         s->dest_dsa.user_account_control = samdb_result_uint(r->msgs[0], "userAccountControl", 0);
232
233         talloc_free(r);
234         return NT_STATUS_OK;
235 }
236
237 static NTSTATUS unbecomeDC_ldap_modify_computer(struct libnet_UnbecomeDC_state *s)
238 {
239         int ret;
240         struct ldb_message *msg;
241         uint32_t user_account_control = UF_WORKSTATION_TRUST_ACCOUNT;
242         uint32_t i;
243
244         /* as the value is already as we want it to be, we're done */
245         if (s->dest_dsa.user_account_control == user_account_control) {
246                 return NT_STATUS_OK;
247         }
248
249         /* make a 'modify' msg, and only for serverReference */
250         msg = ldb_msg_new(s);
251         NT_STATUS_HAVE_NO_MEMORY(msg);
252         msg->dn = ldb_dn_new(msg, s->ldap.ldb, s->dest_dsa.computer_dn_str);
253         NT_STATUS_HAVE_NO_MEMORY(msg->dn);
254
255         ret = ldb_msg_add_fmt(msg, "userAccountControl", "%u", user_account_control);
256         if (ret != 0) {
257                 talloc_free(msg);
258                 return NT_STATUS_NO_MEMORY;
259         }
260
261         /* mark all the message elements (should be just one)
262            as LDB_FLAG_MOD_REPLACE */
263         for (i=0;i<msg->num_elements;i++) {
264                 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
265         }
266
267         ret = ldb_modify(s->ldap.ldb, msg);
268         talloc_free(msg);
269         if (ret != LDB_SUCCESS) {
270                 return NT_STATUS_LDAP(ret);
271         }
272
273         s->dest_dsa.user_account_control = user_account_control;
274
275         return NT_STATUS_OK;
276 }
277
278 static NTSTATUS unbecomeDC_ldap_move_computer(struct libnet_UnbecomeDC_state *s)
279 {
280         int ret;
281         struct ldb_result *r;
282         struct ldb_dn *basedn;
283         struct ldb_dn *old_dn;
284         struct ldb_dn *new_dn;
285         static const char *_1_1_attrs[] = {
286                 "1.1",
287                 NULL
288         };
289
290         basedn = ldb_dn_new_fmt(s, s->ldap.ldb, "<WKGUID=aa312825768811d1aded00c04fd8d5cd,%s>",
291                                 s->domain.dn_str);
292         NT_STATUS_HAVE_NO_MEMORY(basedn);
293
294         ret = ldb_search(s->ldap.ldb, basedn, LDB_SCOPE_BASE,
295                          "(objectClass=*)", _1_1_attrs, &r);
296         talloc_free(basedn);
297         if (ret != LDB_SUCCESS) {
298                 return NT_STATUS_LDAP(ret);
299         } else if (r->count != 1) {
300                 talloc_free(r);
301                 return NT_STATUS_INVALID_NETWORK_RESPONSE;
302         }
303
304         old_dn = ldb_dn_new(r, s->ldap.ldb, s->dest_dsa.computer_dn_str);
305         NT_STATUS_HAVE_NO_MEMORY(old_dn);
306
307         new_dn = r->msgs[0]->dn;
308
309         if (!ldb_dn_add_child_fmt(new_dn, "CN=%s", s->dest_dsa.netbios_name)) {
310                 talloc_free(r);
311                 return NT_STATUS_NO_MEMORY;
312         }
313
314         if (ldb_dn_compare(old_dn, new_dn) == 0) {
315                 /* we don't need to rename if the old and new dn match */
316                 talloc_free(r);
317                 return NT_STATUS_OK;
318         }
319
320         ret = ldb_rename(s->ldap.ldb, old_dn, new_dn);
321         talloc_free(r);
322         if (ret != LDB_SUCCESS) {
323                 return NT_STATUS_LDAP(ret);
324         }
325
326         return NT_STATUS_OK;
327 }
328
329 static void unbecomeDC_drsuapi_connect_send(struct libnet_UnbecomeDC_state *s);
330
331 static void unbecomeDC_connect_ldap(struct libnet_UnbecomeDC_state *s)
332 {
333         struct composite_context *c = s->creq;
334
335         c->status = unbecomeDC_ldap_connect(s);
336         if (!composite_is_ok(c)) return;
337
338         c->status = unbecomeDC_ldap_rootdse(s);
339         if (!composite_is_ok(c)) return;
340
341         c->status = unbecomeDC_ldap_computer_object(s);
342         if (!composite_is_ok(c)) return;
343
344         c->status = unbecomeDC_ldap_modify_computer(s);
345         if (!composite_is_ok(c)) return;
346
347         c->status = unbecomeDC_ldap_move_computer(s);
348         if (!composite_is_ok(c)) return;
349
350         unbecomeDC_drsuapi_connect_send(s);
351 }
352
353 static void unbecomeDC_drsuapi_connect_recv(struct composite_context *creq);
354
355 static void unbecomeDC_drsuapi_connect_send(struct libnet_UnbecomeDC_state *s)
356 {
357         struct composite_context *c = s->creq;
358         struct composite_context *creq;
359         char *binding_str;
360
361         binding_str = talloc_asprintf(s, "ncacn_ip_tcp:%s[seal]", s->source_dsa.dns_name);
362         if (composite_nomem(binding_str, c)) return;
363
364         c->status = dcerpc_parse_binding(s, binding_str, &s->drsuapi.binding);
365         talloc_free(binding_str);
366         if (!composite_is_ok(c)) return;
367
368         creq = dcerpc_pipe_connect_b_send(s, s->drsuapi.binding, &dcerpc_table_drsuapi,
369                                           s->libnet->cred, s->libnet->event_ctx);
370         composite_continue(c, creq, unbecomeDC_drsuapi_connect_recv, s);
371 }
372
373 static void unbecomeDC_drsuapi_bind_send(struct libnet_UnbecomeDC_state *s);
374
375 static void unbecomeDC_drsuapi_connect_recv(struct composite_context *req)
376 {
377         struct libnet_UnbecomeDC_state *s = talloc_get_type(req->async.private_data,
378                                             struct libnet_UnbecomeDC_state);
379         struct composite_context *c = s->creq;
380
381         c->status = dcerpc_pipe_connect_b_recv(req, s, &s->drsuapi.pipe);
382         if (!composite_is_ok(c)) return;
383
384         unbecomeDC_drsuapi_bind_send(s);
385 }
386
387 static void unbecomeDC_drsuapi_bind_recv(struct rpc_request *req);
388
389 static void unbecomeDC_drsuapi_bind_send(struct libnet_UnbecomeDC_state *s)
390 {
391         struct composite_context *c = s->creq;
392         struct rpc_request *req;
393
394         GUID_from_string(DRSUAPI_DS_BIND_GUID, &s->drsuapi.bind_guid);
395
396         s->drsuapi.bind_r.in.bind_guid = &s->drsuapi.bind_guid;
397         s->drsuapi.bind_r.in.bind_info = NULL;
398         s->drsuapi.bind_r.out.bind_handle = &s->drsuapi.bind_handle;
399
400         req = dcerpc_drsuapi_DsBind_send(s->drsuapi.pipe, s, &s->drsuapi.bind_r);
401         composite_continue_rpc(c, req, unbecomeDC_drsuapi_bind_recv, s);
402 }
403
404 static void unbecomeDC_drsuapi_remove_ds_server_send(struct libnet_UnbecomeDC_state *s);
405
406 static void unbecomeDC_drsuapi_bind_recv(struct rpc_request *req)
407 {
408         struct libnet_UnbecomeDC_state *s = talloc_get_type(req->async.private,
409                                             struct libnet_UnbecomeDC_state);
410         struct composite_context *c = s->creq;
411
412         c->status = dcerpc_ndr_request_recv(req);
413         if (!composite_is_ok(c)) return;
414
415         if (!W_ERROR_IS_OK(s->drsuapi.bind_r.out.result)) {
416                 composite_error(c, werror_to_ntstatus(s->drsuapi.bind_r.out.result));
417                 return;
418         }
419
420         unbecomeDC_drsuapi_remove_ds_server_send(s);
421 }
422
423 static void unbecomeDC_drsuapi_remove_ds_server_send(struct libnet_UnbecomeDC_state *s)
424 {
425         struct composite_context *c = s->creq;
426
427         composite_error(c, NT_STATUS_NOT_IMPLEMENTED);
428 }
429
430 struct composite_context *libnet_UnbecomeDC_send(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_UnbecomeDC *r)
431 {
432         struct composite_context *c;
433         struct libnet_UnbecomeDC_state *s;
434         char *tmp_name;
435
436         c = composite_create(mem_ctx, ctx->event_ctx);
437         if (c == NULL) return NULL;
438
439         s = talloc_zero(c, struct libnet_UnbecomeDC_state);
440         if (composite_nomem(s, c)) return c;
441         c->private_data = s;
442         s->creq         = c;
443         s->libnet       = ctx;
444
445         /* Domain input */
446         s->domain.dns_name      = talloc_strdup(s, r->in.domain_dns_name);
447         if (composite_nomem(s->domain.dns_name, c)) return c;
448         s->domain.netbios_name  = talloc_strdup(s, r->in.domain_netbios_name);
449         if (composite_nomem(s->domain.netbios_name, c)) return c;
450
451         /* Source DSA input */
452         s->source_dsa.address   = talloc_strdup(s, r->in.source_dsa_address);
453         if (composite_nomem(s->source_dsa.address, c)) return c;
454
455         /* Destination DSA input */
456         s->dest_dsa.netbios_name= talloc_strdup(s, r->in.dest_dsa_netbios_name);
457         if (composite_nomem(s->dest_dsa.netbios_name, c)) return c;
458
459         /* Destination DSA dns_name construction */
460         tmp_name                = strlower_talloc(s, s->dest_dsa.netbios_name);
461         if (composite_nomem(tmp_name, c)) return c;
462         s->dest_dsa.dns_name    = talloc_asprintf_append(tmp_name, ".%s",
463                                                          s->domain.dns_name);
464         if (composite_nomem(s->dest_dsa.dns_name, c)) return c;
465
466         unbecomeDC_send_cldap(s);
467         return c;
468 }
469
470 NTSTATUS libnet_UnbecomeDC_recv(struct composite_context *c, TALLOC_CTX *mem_ctx, struct libnet_UnbecomeDC *r)
471 {
472         NTSTATUS status;
473
474         status = composite_wait(c);
475
476         ZERO_STRUCT(r->out);
477
478         talloc_free(c);
479         return status;
480 }
481
482 NTSTATUS libnet_UnbecomeDC(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_UnbecomeDC *r)
483 {
484         NTSTATUS status;
485         struct composite_context *c;
486         c = libnet_UnbecomeDC_send(ctx, mem_ctx, r);
487         status = libnet_UnbecomeDC_recv(c, mem_ctx, r);
488         return status;
489 }