r20025: - implement the windows2003update revision search
[jelmer/samba4-debian.git] / source / libnet / libnet_become_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
30 struct libnet_BecomeDC_state {
31         struct composite_context *creq;
32
33         struct libnet_context *libnet;
34
35         struct {
36                 struct cldap_socket *sock;
37                 struct cldap_netlogon io;
38                 struct nbt_cldap_netlogon_5 netlogon5;
39         } cldap;
40
41         struct becomeDC_ldap {
42                 struct ldb_context *ldb;
43                 const struct ldb_message *rootdse;
44         } ldap1;
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 *dns_name;
59                 const char *root_dn_str;
60                 const char *config_dn_str;
61                 const char *schema_dn_str;
62         } forest;
63
64         struct {
65                 /* input */
66                 const char *address;
67
68                 /* constructed */
69                 const char *dns_name;
70                 const char *netbios_name;
71                 const char *site_name;
72                 const char *server_dn_str;
73                 const char *ntds_dn_str;
74         } source_dsa;
75
76         struct {
77                 /* input */
78                 const char *netbios_name;
79
80                 /* constructed */
81                 const char *dns_name;
82                 const char *site_name;
83                 const char *computer_dn_str;
84                 const char *server_dn_str;
85                 const char *ntds_dn_str;
86         } dest_dsa;
87
88         struct {
89                 uint32_t domain_behavior_version;
90                 uint32_t config_behavior_version;
91                 uint32_t schema_object_version;
92                 uint32_t w2k3_update_revision;
93         } ads_options;
94
95         struct becomeDC_fsmo {
96                 const char *dns_name;
97                 const char *server_dn_str;
98                 const char *ntds_dn_str;
99                 struct GUID ntds_guid;
100         } infrastructure_fsmo;
101 };
102
103 static void becomeDC_connect_ldap1(struct libnet_BecomeDC_state *s);
104
105 static void becomeDC_recv_cldap(struct cldap_request *req)
106 {
107         struct libnet_BecomeDC_state *s = talloc_get_type(req->async.private,
108                                           struct libnet_BecomeDC_state);
109         struct composite_context *c = s->creq;
110
111         c->status = cldap_netlogon_recv(req, s, &s->cldap.io);
112         if (!composite_is_ok(c)) return;
113
114         s->cldap.netlogon5 = s->cldap.io.out.netlogon.logon5;
115
116         s->domain.dns_name              = s->cldap.netlogon5.dns_domain;
117         s->domain.netbios_name          = s->cldap.netlogon5.domain;
118         s->domain.guid                  = s->cldap.netlogon5.domain_uuid;
119
120         s->forest.dns_name              = s->cldap.netlogon5.forest;
121
122         s->source_dsa.dns_name          = s->cldap.netlogon5.pdc_dns_name;
123         s->source_dsa.netbios_name      = s->cldap.netlogon5.pdc_name;
124         s->source_dsa.site_name         = s->cldap.netlogon5.server_site;
125
126         s->dest_dsa.site_name           = s->cldap.netlogon5.client_site;
127
128         becomeDC_connect_ldap1(s);
129 }
130
131 static void becomeDC_send_cldap(struct libnet_BecomeDC_state *s)
132 {
133         struct composite_context *c = s->creq;
134         struct cldap_request *req;
135
136         s->cldap.io.in.dest_address     = s->source_dsa.address;
137         s->cldap.io.in.realm            = s->domain.dns_name;
138         s->cldap.io.in.host             = s->dest_dsa.netbios_name;
139         s->cldap.io.in.user             = NULL;
140         s->cldap.io.in.domain_guid      = NULL;
141         s->cldap.io.in.domain_sid       = NULL;
142         s->cldap.io.in.acct_control     = -1;
143         s->cldap.io.in.version          = 6;
144
145         s->cldap.sock = cldap_socket_init(s, s->libnet->event_ctx);
146         if (composite_nomem(s->cldap.sock, c)) return;
147
148         req = cldap_netlogon_send(s->cldap.sock, &s->cldap.io);
149         if (composite_nomem(req, c)) return;
150         req->async.fn           = becomeDC_recv_cldap;
151         req->async.private      = s;
152 }
153
154 static NTSTATUS becomeDC_ldap_connect(struct libnet_BecomeDC_state *s, struct becomeDC_ldap *ldap)
155 {
156         char *url;
157
158         url = talloc_asprintf(s, "ldap://%s/", s->source_dsa.dns_name);
159         NT_STATUS_HAVE_NO_MEMORY(url);
160
161         ldap->ldb = ldb_wrap_connect(s, url,
162                                      NULL,
163                                      s->libnet->cred,
164                                      0, NULL);
165         talloc_free(url);
166         if (ldap->ldb == NULL) {
167                 return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
168         }
169
170         return NT_STATUS_OK;
171 }
172
173 static NTSTATUS becomeDC_ldap1_rootdse(struct libnet_BecomeDC_state *s)
174 {
175         int ret;
176         struct ldb_result *r;
177         struct ldb_dn *basedn;
178         static const char *attrs[] = {
179                 "*",
180                 NULL
181         };
182
183         basedn = ldb_dn_new(s, s->ldap1.ldb, NULL);
184         NT_STATUS_HAVE_NO_MEMORY(basedn);
185
186         ret = ldb_search(s->ldap1.ldb, basedn, LDB_SCOPE_BASE, 
187                          "(objectClass=*)", attrs, &r);
188         talloc_free(basedn);
189         if (ret != LDB_SUCCESS) {
190                 return NT_STATUS_LDAP(ret);
191         } else if (r->count != 1) {
192                 talloc_free(r);
193                 return NT_STATUS_INVALID_NETWORK_RESPONSE;
194         }
195         talloc_steal(s, r);
196
197         s->ldap1.rootdse = r->msgs[0];
198
199         s->domain.dn_str        = ldb_msg_find_attr_as_string(s->ldap1.rootdse, "defaultNamingContext", NULL);
200         if (!s->domain.dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
201
202         s->forest.root_dn_str   = ldb_msg_find_attr_as_string(s->ldap1.rootdse, "rootDomainNamingContext", NULL);
203         if (!s->forest.root_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
204         s->forest.config_dn_str = ldb_msg_find_attr_as_string(s->ldap1.rootdse, "configurationNamingContext", NULL);
205         if (!s->forest.config_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
206         s->forest.schema_dn_str = ldb_msg_find_attr_as_string(s->ldap1.rootdse, "schemaNamingContext", NULL);
207         if (!s->forest.schema_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
208
209         s->source_dsa.server_dn_str     = ldb_msg_find_attr_as_string(s->ldap1.rootdse, "serverName", NULL);
210         if (!s->source_dsa.server_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
211         s->source_dsa.ntds_dn_str       = ldb_msg_find_attr_as_string(s->ldap1.rootdse, "dsServiceName", NULL);
212         if (!s->source_dsa.ntds_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
213
214         return NT_STATUS_OK;
215 }
216
217 static NTSTATUS becomeDC_ldap1_config_behavior_version(struct libnet_BecomeDC_state *s)
218 {
219         int ret;
220         struct ldb_result *r;
221         struct ldb_dn *basedn;
222         static const char *attrs[] = {
223                 "msDs-Behavior-Version",
224                 NULL
225         };
226
227         basedn = ldb_dn_new(s, s->ldap1.ldb, s->forest.config_dn_str);
228         NT_STATUS_HAVE_NO_MEMORY(basedn);
229
230         ret = ldb_search(s->ldap1.ldb, basedn, LDB_SCOPE_ONELEVEL,
231                          "(cn=Partitions)", attrs, &r);
232         talloc_free(basedn);
233         if (ret != LDB_SUCCESS) {
234                 return NT_STATUS_LDAP(ret);
235         } else if (r->count != 1) {
236                 talloc_free(r);
237                 return NT_STATUS_INVALID_NETWORK_RESPONSE;
238         }
239
240         s->ads_options.config_behavior_version = ldb_msg_find_attr_as_uint(r->msgs[0], "msDs-Behavior-Version", 0);
241
242         talloc_free(r);
243         return NT_STATUS_OK;
244 }
245
246 static NTSTATUS becomeDC_ldap1_domain_behavior_version(struct libnet_BecomeDC_state *s)
247 {
248         int ret;
249         struct ldb_result *r;
250         struct ldb_dn *basedn;
251         static const char *attrs[] = {
252                 "msDs-Behavior-Version",
253                 NULL
254         };
255
256         basedn = ldb_dn_new(s, s->ldap1.ldb, s->domain.dn_str);
257         NT_STATUS_HAVE_NO_MEMORY(basedn);
258
259         ret = ldb_search(s->ldap1.ldb, basedn, LDB_SCOPE_BASE,
260                          "(objectClass=*)", attrs, &r);
261         talloc_free(basedn);
262         if (ret != LDB_SUCCESS) {
263                 return NT_STATUS_LDAP(ret);
264         } else if (r->count != 1) {
265                 talloc_free(r);
266                 return NT_STATUS_INVALID_NETWORK_RESPONSE;
267         }
268
269         s->ads_options.domain_behavior_version = ldb_msg_find_attr_as_uint(r->msgs[0], "msDs-Behavior-Version", 0);
270
271         talloc_free(r);
272         return NT_STATUS_OK;
273 }
274
275 static NTSTATUS becomeDC_ldap1_schema_object_version(struct libnet_BecomeDC_state *s)
276 {
277         int ret;
278         struct ldb_result *r;
279         struct ldb_dn *basedn;
280         static const char *attrs[] = {
281                 "objectVersion",
282                 NULL
283         };
284
285         basedn = ldb_dn_new(s, s->ldap1.ldb, s->forest.schema_dn_str);
286         NT_STATUS_HAVE_NO_MEMORY(basedn);
287
288         ret = ldb_search(s->ldap1.ldb, basedn, LDB_SCOPE_BASE,
289                          "(objectClass=*)", attrs, &r);
290         talloc_free(basedn);
291         if (ret != LDB_SUCCESS) {
292                 return NT_STATUS_LDAP(ret);
293         } else if (r->count != 1) {
294                 talloc_free(r);
295                 return NT_STATUS_INVALID_NETWORK_RESPONSE;
296         }
297
298         s->ads_options.schema_object_version = ldb_msg_find_attr_as_uint(r->msgs[0], "objectVersion", 0);
299
300         talloc_free(r);
301         return NT_STATUS_OK;
302 }
303
304 static NTSTATUS becomeDC_ldap1_w2k3_update_revision(struct libnet_BecomeDC_state *s)
305 {
306         int ret;
307         struct ldb_result *r;
308         struct ldb_dn *basedn;
309         static const char *attrs[] = {
310                 "revision",
311                 NULL
312         };
313
314         basedn = ldb_dn_new_fmt(s, s->ldap1.ldb, "CN=Windows2003Update,CN=DomainUpdates,CN=System,%s",
315                                 s->domain.dn_str);
316         NT_STATUS_HAVE_NO_MEMORY(basedn);
317
318         ret = ldb_search(s->ldap1.ldb, basedn, LDB_SCOPE_BASE,
319                          "(objectClass=*)", attrs, &r);
320         talloc_free(basedn);
321         if (ret != LDB_SUCCESS) {
322                 return NT_STATUS_LDAP(ret);
323         } else if (r->count != 1) {
324                 talloc_free(r);
325                 return NT_STATUS_INVALID_NETWORK_RESPONSE;
326         }
327
328         s->ads_options.w2k3_update_revision = ldb_msg_find_attr_as_uint(r->msgs[0], "revision", 0);
329
330         talloc_free(r);
331         return NT_STATUS_OK;
332 }
333
334 static NTSTATUS becomeDC_ldap1_infrastructure_fsmo(struct libnet_BecomeDC_state *s)
335 {
336         int ret;
337         struct ldb_result *r;
338         struct ldb_dn *basedn;
339         struct ldb_dn *ntds_dn;
340         struct ldb_dn *server_dn;
341         static const char *_1_1_attrs[] = {
342                 "1.1",
343                 NULL
344         };
345         static const char *fsmo_attrs[] = {
346                 "fSMORoleOwner",
347                 NULL
348         };
349         static const char *dns_attrs[] = {
350                 "dnsHostName",
351                 NULL
352         };
353         static const char *guid_attrs[] = {
354                 "objectGUID",
355                 NULL
356         };
357
358         basedn = ldb_dn_new_fmt(s, s->ldap1.ldb, "<WKGUID=2fbac1870ade11d297c400c04fd8d5cd,%s>",
359                                 s->domain.dn_str);
360         NT_STATUS_HAVE_NO_MEMORY(basedn);
361
362         ret = ldb_search(s->ldap1.ldb, basedn, LDB_SCOPE_BASE,
363                          "(objectClass=*)", _1_1_attrs, &r);
364         talloc_free(basedn);
365         if (ret != LDB_SUCCESS) {
366                 return NT_STATUS_LDAP(ret);
367         } else if (r->count != 1) {
368                 talloc_free(r);
369                 return NT_STATUS_INVALID_NETWORK_RESPONSE;
370         }
371
372         basedn = talloc_steal(s, r->msgs[0]->dn);
373         talloc_free(r);
374
375         ret = ldb_search(s->ldap1.ldb, basedn, LDB_SCOPE_BASE,
376                          "(objectClass=*)", fsmo_attrs, &r);
377         talloc_free(basedn);
378         if (ret != LDB_SUCCESS) {
379                 return NT_STATUS_LDAP(ret);
380         } else if (r->count != 1) {
381                 talloc_free(r);
382                 return NT_STATUS_INVALID_NETWORK_RESPONSE;
383         }
384
385         s->infrastructure_fsmo.ntds_dn_str      = samdb_result_string(r->msgs[0], "fSMORoleOwner", NULL);
386         if (!s->infrastructure_fsmo.ntds_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
387         talloc_steal(s, s->infrastructure_fsmo.ntds_dn_str);
388
389         talloc_free(r);
390
391         ntds_dn = ldb_dn_new(s, s->ldap1.ldb, s->infrastructure_fsmo.ntds_dn_str);
392         NT_STATUS_HAVE_NO_MEMORY(ntds_dn);
393
394         server_dn = ldb_dn_get_parent(s, ntds_dn);
395         NT_STATUS_HAVE_NO_MEMORY(server_dn);
396
397         ret = ldb_search(s->ldap1.ldb, server_dn, LDB_SCOPE_BASE,
398                          "(objectClass=*)", dns_attrs, &r);
399         if (ret != LDB_SUCCESS) {
400                 return NT_STATUS_LDAP(ret);
401         } else if (r->count != 1) {
402                 talloc_free(r);
403                 return NT_STATUS_INVALID_NETWORK_RESPONSE;
404         }
405
406         s->infrastructure_fsmo.dns_name = samdb_result_string(r->msgs[0], "dnsHostName", NULL);
407         if (!s->infrastructure_fsmo.dns_name) return NT_STATUS_INVALID_NETWORK_RESPONSE;
408         talloc_steal(s, s->infrastructure_fsmo.dns_name);
409
410         talloc_free(r);
411
412         ret = ldb_search(s->ldap1.ldb, ntds_dn, LDB_SCOPE_BASE,
413                          "(objectClass=*)", guid_attrs, &r);
414         if (ret != LDB_SUCCESS) {
415                 return NT_STATUS_LDAP(ret);
416         } else if (r->count != 1) {
417                 talloc_free(r);
418                 return NT_STATUS_INVALID_NETWORK_RESPONSE;
419         }
420
421         s->infrastructure_fsmo.ntds_guid = samdb_result_guid(r->msgs[0], "objectGUID");
422
423         talloc_free(r);
424
425         return NT_STATUS_NOT_IMPLEMENTED;
426 }
427
428
429 static void becomeDC_connect_ldap1(struct libnet_BecomeDC_state *s)
430 {
431         struct composite_context *c = s->creq;
432
433         c->status = becomeDC_ldap_connect(s, &s->ldap1);
434         if (!composite_is_ok(c)) return;
435
436         c->status = becomeDC_ldap1_rootdse(s);
437         if (!composite_is_ok(c)) return;
438
439         c->status = becomeDC_ldap1_config_behavior_version(s);
440         if (!composite_is_ok(c)) return;
441
442         c->status = becomeDC_ldap1_domain_behavior_version(s);
443         if (!composite_is_ok(c)) return;
444
445         c->status = becomeDC_ldap1_schema_object_version(s);
446         if (!composite_is_ok(c)) return;
447
448         c->status = becomeDC_ldap1_w2k3_update_revision(s);
449         if (!composite_is_ok(c)) return;
450
451         c->status = becomeDC_ldap1_infrastructure_fsmo(s);
452         if (!composite_is_ok(c)) return;
453
454         composite_error(c, NT_STATUS_NOT_IMPLEMENTED);
455 }
456
457 struct composite_context *libnet_BecomeDC_send(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_BecomeDC *r)
458 {
459         struct composite_context *c;
460         struct libnet_BecomeDC_state *s;
461         char *tmp_name;
462
463         c = composite_create(mem_ctx, ctx->event_ctx);
464         if (c == NULL) return NULL;
465
466         s = talloc_zero(c, struct libnet_BecomeDC_state);
467         if (composite_nomem(s, c)) return c;
468         c->private_data = s;
469         s->creq         = c;
470         s->libnet       = ctx;
471
472         /* Domain input */
473         s->domain.dns_name      = talloc_strdup(s, r->in.domain_dns_name);
474         if (composite_nomem(s->domain.dns_name, c)) return c;
475         s->domain.netbios_name  = talloc_strdup(s, r->in.domain_netbios_name);
476         if (composite_nomem(s->domain.netbios_name, c)) return c;
477
478         /* Source DSA input */
479         s->source_dsa.address   = talloc_strdup(s, r->in.source_dsa_address);
480         if (composite_nomem(s->source_dsa.address, c)) return c;
481
482         /* Destination DSA input */
483         s->dest_dsa.netbios_name= talloc_strdup(s, r->in.dest_dsa_netbios_name);
484         if (composite_nomem(s->dest_dsa.netbios_name, c)) return c;
485
486         /* Destination DSA dns_name construction */
487         tmp_name                = strlower_talloc(s, s->dest_dsa.netbios_name);
488         if (composite_nomem(tmp_name, c)) return c;
489         s->dest_dsa.dns_name    = talloc_asprintf_append(tmp_name, ".%s",
490                                                          s->domain.dns_name);
491         if (composite_nomem(s->dest_dsa.dns_name, c)) return c;
492
493         becomeDC_send_cldap(s);
494         return c;
495 }
496
497 NTSTATUS libnet_BecomeDC_recv(struct composite_context *c, TALLOC_CTX *mem_ctx, struct libnet_BecomeDC *r)
498 {
499         NTSTATUS status;
500
501         status = composite_wait(c);
502
503         ZERO_STRUCT(r->out);
504
505         talloc_free(c);
506         return status;
507 }
508
509 NTSTATUS libnet_BecomeDC(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_BecomeDC *r)
510 {
511         NTSTATUS status;
512         struct composite_context *c;
513         c = libnet_BecomeDC_send(ctx, mem_ctx, r);
514         status = libnet_BecomeDC_recv(c, mem_ctx, r);
515         return status;
516 }