r20041: when unbecoming a dc:
[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
31 struct libnet_UnbecomeDC_state {
32         struct composite_context *creq;
33
34         struct libnet_context *libnet;
35
36         struct {
37                 struct cldap_socket *sock;
38                 struct cldap_netlogon io;
39                 struct nbt_cldap_netlogon_5 netlogon5;
40         } cldap;
41
42         struct {
43                 struct ldb_context *ldb;
44         } ldap;
45
46         struct {
47                 /* input */
48                 const char *dns_name;
49                 const char *netbios_name;
50
51                 /* constructed */
52                 struct GUID guid;
53                 const char *dn_str;
54         } domain;
55
56         struct {
57                 /* constructed */
58                 const char *config_dn_str;
59         } forest;
60
61         struct {
62                 /* input */
63                 const char *address;
64
65                 /* constructed */
66                 const char *dns_name;
67                 const char *netbios_name;
68                 const char *site_name;
69         } source_dsa;
70
71         struct {
72                 /* input */
73                 const char *netbios_name;
74
75                 /* constructed */
76                 const char *dns_name;
77                 const char *site_name;
78                 const char *computer_dn_str;
79                 const char *server_dn_str;
80                 uint32_t user_account_control;
81         } dest_dsa;
82 };
83
84 static void unbecomeDC_connect_ldap(struct libnet_UnbecomeDC_state *s);
85
86 static void unbecomeDC_recv_cldap(struct cldap_request *req)
87 {
88         struct libnet_UnbecomeDC_state *s = talloc_get_type(req->async.private,
89                                             struct libnet_UnbecomeDC_state);
90         struct composite_context *c = s->creq;
91
92         c->status = cldap_netlogon_recv(req, s, &s->cldap.io);
93         if (!composite_is_ok(c)) return;
94
95         s->cldap.netlogon5 = s->cldap.io.out.netlogon.logon5;
96
97         s->domain.dns_name              = s->cldap.netlogon5.dns_domain;
98         s->domain.netbios_name          = s->cldap.netlogon5.domain;
99         s->domain.guid                  = s->cldap.netlogon5.domain_uuid;
100
101         s->source_dsa.dns_name          = s->cldap.netlogon5.pdc_dns_name;
102         s->source_dsa.netbios_name      = s->cldap.netlogon5.pdc_name;
103         s->source_dsa.site_name         = s->cldap.netlogon5.server_site;
104
105         s->dest_dsa.site_name           = s->cldap.netlogon5.client_site;
106
107         unbecomeDC_connect_ldap(s);
108 }
109
110 static void unbecomeDC_send_cldap(struct libnet_UnbecomeDC_state *s)
111 {
112         struct composite_context *c = s->creq;
113         struct cldap_request *req;
114
115         s->cldap.io.in.dest_address     = s->source_dsa.address;
116         s->cldap.io.in.realm            = s->domain.dns_name;
117         s->cldap.io.in.host             = s->dest_dsa.netbios_name;
118         s->cldap.io.in.user             = NULL;
119         s->cldap.io.in.domain_guid      = NULL;
120         s->cldap.io.in.domain_sid       = NULL;
121         s->cldap.io.in.acct_control     = -1;
122         s->cldap.io.in.version          = 6;
123
124         s->cldap.sock = cldap_socket_init(s, s->libnet->event_ctx);
125         if (composite_nomem(s->cldap.sock, c)) return;
126
127         req = cldap_netlogon_send(s->cldap.sock, &s->cldap.io);
128         if (composite_nomem(req, c)) return;
129         req->async.fn           = unbecomeDC_recv_cldap;
130         req->async.private      = s;
131 }
132
133 static NTSTATUS unbecomeDC_ldap_connect(struct libnet_UnbecomeDC_state *s)
134 {
135         char *url;
136
137         url = talloc_asprintf(s, "ldap://%s/", s->source_dsa.dns_name);
138         NT_STATUS_HAVE_NO_MEMORY(url);
139
140         s->ldap.ldb = ldb_wrap_connect(s, url,
141                                        NULL,
142                                        s->libnet->cred,
143                                        0, NULL);
144         talloc_free(url);
145         if (s->ldap.ldb == NULL) {
146                 return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
147         }
148
149         return NT_STATUS_OK;
150 }
151
152 static NTSTATUS unbecomeDC_ldap_rootdse(struct libnet_UnbecomeDC_state *s)
153 {
154         int ret;
155         struct ldb_result *r;
156         struct ldb_dn *basedn;
157         static const char *attrs[] = {
158                 "defaultNamingContext",
159                 "configurationNamingContext",
160                 NULL
161         };
162
163         basedn = ldb_dn_new(s, s->ldap.ldb, NULL);
164         NT_STATUS_HAVE_NO_MEMORY(basedn);
165
166         ret = ldb_search(s->ldap.ldb, basedn, LDB_SCOPE_BASE, 
167                          "(objectClass=*)", attrs, &r);
168         talloc_free(basedn);
169         if (ret != LDB_SUCCESS) {
170                 return NT_STATUS_LDAP(ret);
171         } else if (r->count != 1) {
172                 talloc_free(r);
173                 return NT_STATUS_INVALID_NETWORK_RESPONSE;
174         }
175         talloc_steal(s, r);
176
177         s->domain.dn_str        = ldb_msg_find_attr_as_string(r->msgs[0], "defaultNamingContext", NULL);
178         if (!s->domain.dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
179         talloc_steal(s, s->domain.dn_str);
180
181         s->forest.config_dn_str = ldb_msg_find_attr_as_string(r->msgs[0], "configurationNamingContext", NULL);
182         if (!s->forest.config_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
183         talloc_steal(s, s->forest.config_dn_str);
184
185         talloc_free(r);
186         return NT_STATUS_OK;
187 }
188
189 static NTSTATUS unbecomeDC_ldap_computer_object(struct libnet_UnbecomeDC_state *s)
190 {
191         int ret;
192         struct ldb_result *r;
193         struct ldb_dn *basedn;
194         char *filter;
195         static const char *attrs[] = {
196                 "distinguishedName",
197                 "userAccountControl",
198                 NULL
199         };
200
201         basedn = ldb_dn_new(s, s->ldap.ldb, s->domain.dn_str);
202         NT_STATUS_HAVE_NO_MEMORY(basedn);
203
204         filter = talloc_asprintf(basedn, "(&(|(objectClass=user)(objectClass=computer))(sAMAccountName=%s$))",
205                                  s->dest_dsa.netbios_name);
206         NT_STATUS_HAVE_NO_MEMORY(filter);
207
208         ret = ldb_search(s->ldap.ldb, basedn, LDB_SCOPE_SUBTREE, 
209                          filter, attrs, &r);
210         talloc_free(basedn);
211         if (ret != LDB_SUCCESS) {
212                 return NT_STATUS_LDAP(ret);
213         } else if (r->count != 1) {
214                 talloc_free(r);
215                 return NT_STATUS_INVALID_NETWORK_RESPONSE;
216         }
217
218         s->dest_dsa.computer_dn_str     = samdb_result_string(r->msgs[0], "distinguishedName", NULL);
219         if (!s->dest_dsa.computer_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
220         talloc_steal(s, s->dest_dsa.computer_dn_str);
221
222         s->dest_dsa.user_account_control = samdb_result_uint(r->msgs[0], "userAccountControl", 0);
223
224         talloc_free(r);
225         return NT_STATUS_OK;
226 }
227
228 static NTSTATUS unbecomeDC_ldap_modify_computer(struct libnet_UnbecomeDC_state *s)
229 {
230         int ret;
231         struct ldb_message *msg;
232         uint32_t user_account_control = UF_WORKSTATION_TRUST_ACCOUNT;
233         uint32_t i;
234
235         /* as the value is already as we want it to be, we're done */
236         if (s->dest_dsa.user_account_control == user_account_control) {
237                 return NT_STATUS_OK;
238         }
239
240         /* make a 'modify' msg, and only for serverReference */
241         msg = ldb_msg_new(s);
242         NT_STATUS_HAVE_NO_MEMORY(msg);
243         msg->dn = ldb_dn_new(msg, s->ldap.ldb, s->dest_dsa.computer_dn_str);
244         NT_STATUS_HAVE_NO_MEMORY(msg->dn);
245
246         ret = ldb_msg_add_fmt(msg, "userAccountControl", "%u", user_account_control);
247         if (ret != 0) {
248                 talloc_free(msg);
249                 return NT_STATUS_NO_MEMORY;
250         }
251
252         /* mark all the message elements (should be just one)
253            as LDB_FLAG_MOD_REPLACE */
254         for (i=0;i<msg->num_elements;i++) {
255                 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
256         }
257
258         ret = ldb_modify(s->ldap.ldb, msg);
259         talloc_free(msg);
260         if (ret != LDB_SUCCESS) {
261                 return NT_STATUS_LDAP(ret);
262         }
263
264         s->dest_dsa.user_account_control = user_account_control;
265
266         return NT_STATUS_OK;
267 }
268
269 static void unbecomeDC_connect_ldap(struct libnet_UnbecomeDC_state *s)
270 {
271         struct composite_context *c = s->creq;
272
273         c->status = unbecomeDC_ldap_connect(s);
274         if (!composite_is_ok(c)) return;
275
276         c->status = unbecomeDC_ldap_rootdse(s);
277         if (!composite_is_ok(c)) return;
278
279         c->status = unbecomeDC_ldap_computer_object(s);
280         if (!composite_is_ok(c)) return;
281
282         c->status = unbecomeDC_ldap_modify_computer(s);
283         if (!composite_is_ok(c)) return;
284
285         composite_error(c, NT_STATUS_NOT_IMPLEMENTED);
286 }
287
288 struct composite_context *libnet_UnbecomeDC_send(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_UnbecomeDC *r)
289 {
290         struct composite_context *c;
291         struct libnet_UnbecomeDC_state *s;
292         char *tmp_name;
293
294         c = composite_create(mem_ctx, ctx->event_ctx);
295         if (c == NULL) return NULL;
296
297         s = talloc_zero(c, struct libnet_UnbecomeDC_state);
298         if (composite_nomem(s, c)) return c;
299         c->private_data = s;
300         s->creq         = c;
301         s->libnet       = ctx;
302
303         /* Domain input */
304         s->domain.dns_name      = talloc_strdup(s, r->in.domain_dns_name);
305         if (composite_nomem(s->domain.dns_name, c)) return c;
306         s->domain.netbios_name  = talloc_strdup(s, r->in.domain_netbios_name);
307         if (composite_nomem(s->domain.netbios_name, c)) return c;
308
309         /* Source DSA input */
310         s->source_dsa.address   = talloc_strdup(s, r->in.source_dsa_address);
311         if (composite_nomem(s->source_dsa.address, c)) return c;
312
313         /* Destination DSA input */
314         s->dest_dsa.netbios_name= talloc_strdup(s, r->in.dest_dsa_netbios_name);
315         if (composite_nomem(s->dest_dsa.netbios_name, c)) return c;
316
317         /* Destination DSA dns_name construction */
318         tmp_name                = strlower_talloc(s, s->dest_dsa.netbios_name);
319         if (composite_nomem(tmp_name, c)) return c;
320         s->dest_dsa.dns_name    = talloc_asprintf_append(tmp_name, ".%s",
321                                                          s->domain.dns_name);
322         if (composite_nomem(s->dest_dsa.dns_name, c)) return c;
323
324         unbecomeDC_send_cldap(s);
325         return c;
326 }
327
328 NTSTATUS libnet_UnbecomeDC_recv(struct composite_context *c, TALLOC_CTX *mem_ctx, struct libnet_UnbecomeDC *r)
329 {
330         NTSTATUS status;
331
332         status = composite_wait(c);
333
334         ZERO_STRUCT(r->out);
335
336         talloc_free(c);
337         return status;
338 }
339
340 NTSTATUS libnet_UnbecomeDC(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_UnbecomeDC *r)
341 {
342         NTSTATUS status;
343         struct composite_context *c;
344         c = libnet_UnbecomeDC_send(ctx, mem_ctx, r);
345         status = libnet_UnbecomeDC_recv(c, mem_ctx, r);
346         return status;
347 }