r25299: Modify the provision script to take an additional argument: --server-role
[samba.git] / source4 / scripting / libjs / provision.js
1 /*
2         backend code for provisioning a Samba4 server
3         Copyright Andrew Tridgell 2005
4         Released under the GNU GPL v2 or later
5 */
6
7 sys = sys_init();
8
9 /*
10   return true if the current install seems to be OK
11 */
12 function install_ok(session_info, credentials)
13 {
14         var lp = loadparm_init();
15         var ldb = ldb_init();
16         ldb.session_info = session_info;
17         ldb.credentials = credentials;
18         if (lp.get("realm") == "") {
19                 return false;
20         }
21         var ok = ldb.connect(lp.get("sam database"));
22         if (!ok) {
23                 return false;
24         }
25         var res = ldb.search("(cn=Administrator)");
26         if (res.error != 0 || res.msgs.length != 1) {
27                 return false;
28         }
29         return true;
30 }
31
32 /*
33   find a user or group from a list of possibilities
34 */
35 function findnss()
36 {
37         var i;
38         assert(arguments.length >= 2);
39         var nssfn = arguments[0];
40         for (i=1;i<arguments.length;i++) {
41                 if (nssfn(arguments[i]) != undefined) {
42                         return arguments[i];
43                 }
44         }
45         printf("Unable to find user/group for %s\n", arguments[1]);
46         assert(i<arguments.length);
47 }
48
49 /*
50    add a foreign security principle
51  */
52 function add_foreign(ldb, subobj, sid, desc)
53 {
54         var add = sprintf("
55 dn: CN=%s,CN=ForeignSecurityPrincipals,%s
56 objectClass: top
57 objectClass: foreignSecurityPrincipal
58 description: %s
59 ",
60                           sid, subobj.DOMAINDN, desc);
61         /* deliberately ignore errors from this, as the records may
62            already exist */
63         ldb.add(add);
64 }
65
66
67 /*
68   setup a mapping between a sam name and a unix name
69  */
70 function setup_name_mapping(info, ldb, sid, unixname)
71 {
72         var attrs = new Array("dn");
73         var res = ldb.search(sprintf("objectSid=%s", sid), 
74                              info.subobj.DOMAINDN, ldb.SCOPE_SUBTREE, attrs);
75         if (res.error != 0 || res.msgs.length != 1) {
76                 info.message("Failed to find record for objectSid %s\n", sid);
77                 return false;
78         }
79         var mod = sprintf("
80 dn: %s
81 changetype: modify
82 replace: unixName
83 unixName: %s
84 ",
85                           res.msgs[0].dn, unixname);
86         var ok = ldb.modify(mod);
87         if (ok.error != 0) {
88                 info.message("name mapping for %s failed - %s\n",
89                              sid, ldb.errstring());
90                 return false;
91         }
92         return true;
93 }
94
95 /*
96   return current time as a nt time string
97 */
98 function nttime()
99 {
100         return "" + sys.nttime();
101 }
102
103 /*
104   return current time as a ldap time string
105 */
106 function ldaptime()
107 {
108         return sys.ldaptime(sys.nttime());
109 }
110
111 /*
112   return a date string suitable for a dns zone serial number
113 */
114 function datestring()
115 {
116         var t = sys.ntgmtime(sys.nttime());
117         return sprintf("%04u%02u%02u%02u",
118                        t.tm_year+1900, t.tm_mon+1, t.tm_mday, t.tm_hour);
119 }
120
121 /*
122   return first host IP
123 */
124 function hostip()
125 {
126         var list = sys.interfaces();
127         return list[0];
128 }
129
130
131 /*
132   return first part of hostname
133 */
134 function hostname()
135 {
136         var s = split(".", sys.hostname());
137         return s[0];
138 }
139
140 /* the ldb is in bad shape, possibly due to being built from an
141    incompatible previous version of the code, so delete it
142    completely */
143 function ldb_delete(info, ldb)
144 {
145         info.message("Deleting " + ldb.filename + "\n");
146         var lp = loadparm_init();
147         sys.unlink(sprintf("%s/%s", lp.get("private dir"), ldb.filename));
148         ldb.transaction_cancel();
149         ldb.close();
150         var ok = ldb.connect(ldb.filename);
151         ldb.transaction_start();
152         assert(ok);
153 }
154
155 /*
156   erase an ldb, removing all records
157 */
158 function ldb_erase(info, ldb)
159 {
160         var res;
161
162         /* delete the specials */
163         ldb.del("@INDEXLIST");
164         ldb.del("@ATTRIBUTES");
165         ldb.del("@MODULES");
166         ldb.del("@PARTITION");
167         ldb.del("@KLUDGEACL");
168
169         /* and the rest */
170         attrs = new Array("dn");
171         var basedn = "";
172         var res = ldb.search("(&(|(objectclass=*)(dn=*))(!(dn=@BASEINFO)))", basedn, ldb.SCOPE_SUBTREE, attrs);
173         var i;
174         if (res.error != 0) {
175                 ldb_delete(info, ldb);
176                 return;
177         }
178         for (i=0;i<res.msgs.length;i++) {
179                 ldb.del(res.msgs[i].dn);
180         }
181
182         var res = ldb.search("(&(|(objectclass=*)(dn=*))(!(dn=@BASEINFO)))", basedn, ldb.SCOPE_SUBTREE, attrs);
183         if (res.error != 0 || res.msgs.length != 0) {
184                 ldb_delete(info, ldb);
185                 return;
186         }
187         assert(res.msgs.length == 0);
188 }
189
190 /*
191   erase an ldb, removing all records
192 */
193 function ldb_erase_partitions(info, ldb, ldapbackend)
194 {
195         var rootDSE_attrs = new Array("namingContexts");
196         var lp = loadparm_init();
197         var j;
198
199         var res = ldb.search("(objectClass=*)", "", ldb.SCOPE_BASE, rootDSE_attrs);
200         assert(res.error == 0);
201         assert(res.msgs.length == 1);
202         if (typeof(res.msgs[0].namingContexts) == "undefined") {
203                 return;
204         }       
205         for (j=0; j<res.msgs[0].namingContexts.length; j++) {
206                 var anything = "(|(objectclass=*)(dn=*))";
207                 var attrs = new Array("dn");
208                 var basedn = res.msgs[0].namingContexts[j];
209                 var k;
210                 var previous_remaining = 1;
211                 var current_remaining = 0;
212
213                 if (ldapbackend && (basedn == info.subobj.DOMAINDN)) {
214                         /* Only delete objects that were created by provision */
215                         anything = "(objectcategory=*)";
216                 }
217
218                 for (k=0; k < 10 && (previous_remaining != current_remaining); k++) {
219                         /* and the rest */
220                         var res2 = ldb.search(anything, basedn, ldb.SCOPE_SUBTREE, attrs);
221                         var i;
222                         if (res2.error != 0) {
223                                 info.message("ldb search failed: " + res.errstr + "\n");
224                                 continue;
225                         }
226                         previous_remaining = current_remaining;
227                         current_remaining = res2.msgs.length;
228                         for (i=0;i<res2.msgs.length;i++) {
229                                 ldb.del(res2.msgs[i].dn);
230                         }
231                         
232                         var res3 = ldb.search(anything, basedn, ldb.SCOPE_SUBTREE, attrs);
233                         if (res3.error != 0) {
234                                 info.message("ldb search failed: " + res.errstr + "\n");
235                                 continue;
236                         }
237                         if (res3.msgs.length != 0) {
238                                 info.message("Failed to delete all records under " + basedn + ", " + res3.msgs.length + " records remaining\n");
239                         }
240                 }
241         }
242 }
243
244 function open_ldb(info, dbname, erase)
245 {
246         var ldb = ldb_init();
247         ldb.session_info = info.session_info;
248         ldb.credentials = info.credentials;
249         ldb.filename = dbname;
250
251         var connect_ok = ldb.connect(dbname);
252         if (!connect_ok) {
253                 var lp = loadparm_init();
254                 sys.unlink(sprintf("%s/%s", lp.get("private dir"), dbname));
255                 connect_ok = ldb.connect(dbname);
256                 assert(connect_ok);
257         }
258
259         ldb.transaction_start();
260
261         if (erase) {
262                 ldb_erase(info, ldb);   
263         }
264         return ldb;
265 }
266
267
268 /*
269   setup a ldb in the private dir
270  */
271 function setup_add_ldif(ldif, info, ldb, failok)
272 {
273         var lp = loadparm_init();
274         var src = lp.get("setup directory") + "/" + ldif;
275
276         var data = sys.file_load(src);
277         data = substitute_var(data, info.subobj);
278
279         var add_res = ldb.add(data);
280         if (add_res.error != 0) {
281                 info.message("ldb load failed: " + add_res.errstr + "\n");
282                 if (!failok) {
283                         assert(add_res.error == 0);
284                 }
285         }
286         return (add_res.error == 0);
287 }
288
289 function setup_modify_ldif(ldif, info, ldb, failok)
290 {
291         var lp = loadparm_init();
292         var src = lp.get("setup directory") + "/" + ldif;
293
294         var data = sys.file_load(src);
295         data = substitute_var(data, info.subobj);
296
297         var mod_res = ldb.modify(data);
298         if (mod_res.error != 0) {
299                 info.message("ldb load failed: " + mod_res.errstr + "\n");
300                 if (!failok) {
301                         assert(mod_res.error == 0);
302                 }
303         }
304         return (mod_res.error == 0);
305 }
306
307
308 function setup_ldb(ldif, info, dbname) 
309 {
310         var erase = true;
311         var failok = false;
312
313         if (arguments.length >= 4) {
314                 erase = arguments[3];
315         }
316         if (arguments.length == 5) {
317                 failok = arguments[4];
318         }
319         var ldb = open_ldb(info, dbname, erase);
320         if (setup_add_ldif(ldif, info, ldb, failok)) {
321                 var commit_ok = ldb.transaction_commit();
322                 if (!commit_ok) {
323                         info.message("ldb commit failed: " + ldb.errstring() + "\n");
324                         assert(commit_ok);
325                 }
326         }
327 }
328
329 /*
330   setup a ldb in the private dir
331  */
332 function setup_ldb_modify(ldif, info, ldb)
333 {
334         var lp = loadparm_init();
335
336         var src = lp.get("setup directory") + "/" + ldif;
337
338         var data = sys.file_load(src);
339         data = substitute_var(data, info.subobj);
340
341         var mod_res = ldb.modify(data);
342         if (mod_res.error != 0) {
343                 info.message("ldb load failed: " + mod_res.errstr + "\n");
344                 return (mod_res.error == 0);
345         }
346         return (mod_res.error == 0);
347 }
348
349 /*
350   setup a file in the private dir
351  */
352 function setup_file(template, message, fname, subobj)
353 {
354         var lp = loadparm_init();
355         var f = fname;
356         var src = lp.get("setup directory") + "/" + template;
357
358         sys.unlink(f);
359
360         var data = sys.file_load(src);
361         data = substitute_var(data, subobj);
362
363         ok = sys.file_save(f, data);
364         if (!ok) {
365                 message("failed to create file: " + f + "\n");
366                 assert(ok);
367         }
368 }
369
370 function provision_default_paths(subobj)
371 {
372         /* subobj.DNSDOMAIN isn't available at this point */
373         var dnsdomain = strlower(subobj.REALM);
374         var lp = loadparm_init();
375         var paths = new Object();
376         paths.smbconf = lp.get("config file");
377         paths.shareconf = lp.get("private dir") + "/" + "share.ldb";
378         paths.samdb = lp.get("sam database");
379         paths.secrets = lp.get("secrets database");
380         paths.keytab = "secrets.keytab";
381         paths.dns_keytab = "dns.keytab";
382         paths.dns_keytab_abs = lp.get("private dir") + "/" + paths.dns_keytab;
383         paths.dns = lp.get("private dir") + "/" + dnsdomain + ".zone";
384         paths.named_conf = lp.get("private dir") + "/named.conf";
385         paths.winsdb = "wins.ldb";
386         paths.ldapdir = lp.get("private dir") + "/ldap";
387         paths.ldap_basedn_ldif = paths.ldapdir + "/" + dnsdomain + ".ldif";
388         paths.ldap_config_basedn_ldif = paths.ldapdir + "/" + dnsdomain + "-config.ldif";
389         paths.ldap_schema_basedn_ldif = paths.ldapdir + "/" + dnsdomain + "-schema.ldif";
390
391         paths.sysvol = lp.get("sysvol", "path");
392
393         if (paths.sysvol == undefined) {
394                 paths.sysvol = lp.get("lock dir") + "/sysvol";
395         }
396         
397         paths.netlogon = lp.get("netlogon", "path");
398         
399         if (paths.netlogon == undefined) {
400                 paths.netlogon = paths.sysvol + "/" + dnsdomain + "/scripts";
401         }
402
403         return paths;
404 }
405
406
407 /*
408   setup reasonable name mappings for sam names to unix names
409 */
410 function setup_name_mappings(info, ldb)
411 {
412         var lp = loadparm_init();
413         var attrs = new Array("objectSid");
414         var subobj = info.subobj;
415
416         res = ldb.search("objectSid=*", subobj.DOMAINDN, ldb.SCOPE_BASE, attrs);
417         assert(res.error == 0);
418         assert(res.msgs.length == 1 && res.msgs[0].objectSid != undefined);
419         var sid = res.msgs[0].objectSid;
420
421         /* add some foreign sids if they are not present already */
422         add_foreign(ldb, subobj, "S-1-5-7",  "Anonymous");
423         add_foreign(ldb, subobj, "S-1-1-0",  "World");
424         add_foreign(ldb, subobj, "S-1-5-2",  "Network");
425         add_foreign(ldb, subobj, "S-1-5-18", "System");
426         add_foreign(ldb, subobj, "S-1-5-11", "Authenticated Users");
427
428         /* some well known sids */
429         setup_name_mapping(info, ldb, "S-1-5-7",  subobj.NOBODY);
430         setup_name_mapping(info, ldb, "S-1-1-0",  subobj.NOGROUP);
431         setup_name_mapping(info, ldb, "S-1-5-2",  subobj.NOGROUP);
432         setup_name_mapping(info, ldb, "S-1-5-18", subobj.ROOT);
433         setup_name_mapping(info, ldb, "S-1-5-11", subobj.USERS);
434         setup_name_mapping(info, ldb, "S-1-5-32-544", subobj.WHEEL);
435         setup_name_mapping(info, ldb, "S-1-5-32-545", subobj.USERS);
436         setup_name_mapping(info, ldb, "S-1-5-32-546", subobj.NOGROUP);
437         setup_name_mapping(info, ldb, "S-1-5-32-551", subobj.BACKUP);
438
439         /* and some well known domain rids */
440         setup_name_mapping(info, ldb, sid + "-500", subobj.ROOT);
441         setup_name_mapping(info, ldb, sid + "-518", subobj.WHEEL);
442         setup_name_mapping(info, ldb, sid + "-519", subobj.WHEEL);
443         setup_name_mapping(info, ldb, sid + "-512", subobj.WHEEL);
444         setup_name_mapping(info, ldb, sid + "-513", subobj.USERS);
445         setup_name_mapping(info, ldb, sid + "-520", subobj.WHEEL);
446
447         return true;
448 }
449
450 function provision_fix_subobj(subobj, paths)
451 {
452         var ldb = ldb_init();
453         
454         subobj.REALM       = strupper(subobj.REALM);
455         subobj.HOSTNAME    = strlower(subobj.HOSTNAME);
456         subobj.DOMAIN      = strupper(subobj.DOMAIN);
457         assert(valid_netbios_name(subobj.DOMAIN));
458         subobj.NETBIOSNAME = strupper(subobj.HOSTNAME);
459         assert(valid_netbios_name(subobj.NETBIOSNAME));
460         subobj.DNSDOMAIN    = strlower(subobj.REALM);
461         subobj.DNSNAME      = sprintf("%s.%s", 
462                                       strlower(subobj.HOSTNAME), 
463                                       subobj.DNSDOMAIN);
464         var rdn_list = split(".", subobj.DNSDOMAIN);
465         subobj.DOMAINDN     = "DC=" + join(",DC=", rdn_list);
466         subobj.ROOTDN       = subobj.DOMAINDN;
467         subobj.CONFIGDN     = "CN=Configuration," + subobj.ROOTDN;
468         subobj.SCHEMADN     = "CN=Schema," + subobj.CONFIGDN;
469
470         subobj.MACHINEPASS_B64 = ldb.encode(subobj.MACHINEPASS);
471         subobj.KRBTGTPASS_B64  = ldb.encode(subobj.KRBTGTPASS);
472         subobj.ADMINPASS_B64   = ldb.encode(subobj.ADMINPASS);
473         subobj.DNSPASS_B64     = ldb.encode(subobj.DNSPASS);
474
475         var rdns = split(",", subobj.DOMAINDN);
476         subobj.RDN_DC = substr(rdns[0], strlen("DC="));
477
478         subobj.SAM_LDB          = "tdb://" + paths.samdb;
479         subobj.SECRETS_KEYTAB   = paths.keytab;
480         subobj.DNS_KEYTAB       = paths.dns_keytab;
481         subobj.DNS_KEYTAB_ABS   = paths.dns_keytab_abs;
482
483         subobj.LDAPDIR = paths.ldapdir;
484         var ldap_path_list = split("/", paths.ldapdir);
485         subobj.LDAPI_URI = "ldapi://" + join("%2F", ldap_path_list) + "%2Fldapi";
486
487         subobj.LDAPMANAGERDN = "cn=Manager," + subobj.DOMAINDN;
488
489         subobj.NETLOGONPATH = paths.netlogon;
490         subobj.SYSVOLPATH = paths.sysvol;
491
492         if (subobj.DOMAIN_CONF == undefined) {
493                 subobj.DOMAIN_CONF = subobj.DOMAIN;
494         }
495         if (subobj.REALM_CONF == undefined) {
496                 subobj.REALM_CONF = subobj.REALM;
497         }
498         if (subobj.SERVERROLE != "domain controller") {
499                 subobj.REALM = subobj.HOSTNAME;
500                 subobj.DOMAIN = subobj.HOSTNAME;
501         }
502
503         return true;
504 }
505
506 function provision_become_dc(subobj, message, erase, paths, session_info)
507 {
508         var lp = loadparm_init();
509         var sys = sys_init();
510         var info = new Object();
511
512         var ok = provision_fix_subobj(subobj, paths);
513         assert(ok);
514
515         info.subobj = subobj;
516         info.message = message;
517         info.session_info = session_info;
518
519         /* Also wipes the database */
520         message("Setting up " + paths.samdb + " partitions\n");
521         setup_ldb("provision_partitions.ldif", info, paths.samdb);
522
523         var samdb = open_ldb(info, paths.samdb, false);
524
525         message("Setting up " + paths.samdb + " attributes\n");
526         setup_add_ldif("provision_init.ldif", info, samdb, false);
527
528         message("Setting up " + paths.samdb + " rootDSE\n");
529         setup_add_ldif("provision_rootdse_add.ldif", info, samdb, false);
530
531         if (erase) {
532                 message("Erasing data from partitions\n");
533                 ldb_erase_partitions(info, samdb, undefined);
534         }
535
536         message("Setting up " + paths.samdb + " indexes\n");
537         setup_add_ldif("provision_index.ldif", info, samdb, false);
538
539         message("Setting up " + paths.samdb + " templates\n");
540         setup_add_ldif("provision_templates.ldif", info, samdb, false);
541
542         ok = samdb.transaction_commit();
543         assert(ok);
544
545         message("Setting up " + paths.secrets + "\n");
546         setup_ldb("secrets_init.ldif", info, paths.secrets);
547
548         setup_ldb("secrets.ldif", info, paths.secrets, false);
549
550         setup_ldb("secrets_dc.ldif", info, paths.secrets, false);
551
552         return true;
553 }
554
555 /*
556   provision samba4 - caution, this wipes all existing data!
557 */
558 function provision(subobj, message, blank, paths, session_info, credentials, ldapbackend)
559 {
560         var lp = loadparm_init();
561         var sys = sys_init();
562         var info = new Object();
563
564         var ok = provision_fix_subobj(subobj, paths);
565         assert(ok);
566
567         if (subobj.DOMAINGUID != undefined) {
568                 subobj.DOMAINGUID_MOD = sprintf("replace: objectGUID\nobjectGUID: %s\n-", subobj.DOMAINGUID);
569         } else {
570                 subobj.DOMAINGUID_MOD = "";
571         }
572
573         if (subobj.HOSTGUID != undefined) {
574                 subobj.HOSTGUID_ADD = sprintf("objectGUID: %s", subobj.HOSTGUID);
575         } else {
576                 subobj.HOSTGUID_ADD = "";
577         }
578
579         info.subobj = subobj;
580         info.message = message;
581         info.credentials = credentials;
582         info.session_info = session_info;
583
584         /* only install a new smb.conf if there isn't one there already */
585         var st = sys.stat(paths.smbconf);
586         if (st == undefined) {
587                 var smbconfsuffix;
588                 if (subobj.ROLE == "domain controller") {
589                         smbconfsuffix = "dc";
590                 } else if (subobj.ROLE == "member server") {
591                         smbconfsuffix = "member";
592                 } else {
593                         smbconfsuffix = subobj.ROLE;
594                 }
595                 message("Setting up " + paths.smbconf +"\n");
596                 setup_file("provision.smb.conf." + smbconfsuffix, info.message, paths.smbconf, subobj);
597                 lp.reload();
598         }
599         /* only install a new shares config db if there is none */
600         st = sys.stat(paths.shareconf);
601         if (st == undefined) {
602                 message("Setting up share.ldb\n");
603                 setup_ldb("share.ldif", info, paths.shareconf);
604         }
605
606         message("Setting up " + paths.secrets + "\n");
607         setup_ldb("secrets_init.ldif", info, paths.secrets);
608         setup_ldb("secrets.ldif", info, paths.secrets, false);
609
610         message("Setting up the registry\n");
611         var reg = reg_open();
612         reg.apply_patchfile(lp.get("setup directory") + "/provision.reg")
613
614         message("Setting up sam.ldb partitions\n");
615         /* Also wipes the database */
616         setup_ldb("provision_partitions.ldif", info, paths.samdb);
617
618         var samdb = open_ldb(info, paths.samdb, false);
619
620         message("Setting up sam.ldb attributes\n");
621         setup_add_ldif("provision_init.ldif", info, samdb, false);
622
623         message("Setting up sam.ldb rootDSE\n");
624         setup_add_ldif("provision_rootdse_add.ldif", info, samdb, false);
625
626         message("Erasing data from partitions\n");
627         ldb_erase_partitions(info, samdb, ldapbackend);
628         
629         message("Adding DomainDN: " + subobj.DOMAINDN + " (permitted to fail)\n");
630         var add_ok = setup_add_ldif("provision_basedn.ldif", info, samdb, true);
631         message("Modifying DomainDN: " + subobj.DOMAINDN + "\n");
632         var modify_ok = setup_ldb_modify("provision_basedn_modify.ldif", info, samdb);
633         if (!modify_ok) {
634                 if (!add_ok) {
635                         message("%s", "Failed to both add and modify " + subobj.DOMAINDN + " in target " + subobj.DOMAINDN_LDB + ": " + samdb.errstring() + "\n");
636                         message("Perhaps you need to run the provision script with the --ldap-base-dn option, and add this record to the backend manually\n"); 
637                 };
638                 assert(modify_ok);
639         };
640
641         message("Adding configuration container (permitted to fail)\n");
642         var add_ok = setup_add_ldif("provision_configuration_basedn.ldif", info, samdb, true);
643         message("Modifying configuration container\n");
644         var modify_ok = setup_ldb_modify("provision_configuration_basedn_modify.ldif", info, samdb);
645         if (!modify_ok) {
646                 if (!add_ok) {
647                         message("%s", "Failed to both add and modify " + subobj.CONFIGDN + " in target " + subobj.CONFIGDN_LDB + ": " + samdb.errstring() + "\n");
648                         message("Perhaps you need to run the provision script with the --ldap-base-dn option, and add this record to the backend manually\n"); 
649                         assert(modify_ok);
650                 }
651                 assert(modify_ok);
652         }
653
654         message("Adding schema container (permitted to fail)\n");
655         var add_ok = setup_add_ldif("provision_schema_basedn.ldif", info, samdb, true);
656         message("Modifying schema container\n");
657         var modify_ok = setup_ldb_modify("provision_schema_basedn_modify.ldif", info, samdb);
658         if (!modify_ok) {
659                 if (!add_ok) {
660                         message("%s", "Failed to both add and modify " + subobj.SCHEMADN + " in target " + subobj.SCHEMADN_LDB + ": " + samdb.errstring() + "\n");
661                         message("Perhaps you need to run the provision script with the --ldap-base-dn option, and add this record to the backend manually\n"); 
662                         assert(modify_ok);
663                 }
664                 message("Failed to modify the schema container: " + samdb.errstring() + "\n");
665                 assert(modify_ok);
666         }
667
668         message("Setting up sam.ldb Samba4 schema\n");
669         setup_add_ldif("schema_samba4.ldif", info, samdb, false);
670         message("Setting up sam.ldb AD schema\n");
671         setup_add_ldif("schema.ldif", info, samdb, false);
672
673         // (hack) Reload, now we have the schema loaded.  
674         var commit_ok = samdb.transaction_commit();
675         if (!commit_ok) {
676                 info.message("samdb commit failed: " + samdb.errstring() + "\n");
677                 assert(commit_ok);
678         }
679         samdb.close();
680
681         samdb = open_ldb(info, paths.samdb, false);
682
683         message("Setting up sam.ldb configuration data\n");
684         setup_add_ldif("provision_configuration.ldif", info, samdb, false);
685
686         message("Setting up display specifiers\n");
687         setup_add_ldif("display_specifiers.ldif", info, samdb, false);
688         message("Setting up sam.ldb templates\n");
689         setup_add_ldif("provision_templates.ldif", info, samdb, false);
690
691         message("Adding users container (permitted to fail)\n");
692         var add_ok = setup_add_ldif("provision_users_add.ldif", info, samdb, true);
693         message("Modifying users container\n");
694         var modify_ok = setup_ldb_modify("provision_users_modify.ldif", info, samdb);
695         if (!modify_ok) {
696                 if (!add_ok) {
697                         message("Failed to both add and modify the users container\n");
698                         assert(modify_ok);
699                 }
700                 assert(modify_ok);
701         }
702         message("Adding computers container (permitted to fail)\n");
703         var add_ok = setup_add_ldif("provision_computers_add.ldif", info, samdb, true);
704         message("Modifying computers container\n");
705         var modify_ok = setup_ldb_modify("provision_computers_modify.ldif", info, samdb);
706         if (!modify_ok) {
707                 if (!add_ok) {
708                         message("Failed to both add and modify the computers container\n");
709                         assert(modify_ok);
710                 }
711                 assert(modify_ok);
712         }
713
714         message("Setting up sam.ldb data\n");
715         setup_add_ldif("provision.ldif", info, samdb, false);
716
717         if (blank != false) {
718                 message("Setting up sam.ldb index\n");
719                 setup_add_ldif("provision_index.ldif", info, samdb, false);
720
721                 message("Setting up sam.ldb rootDSE marking as syncronized\n");
722                 setup_modify_ldif("provision_rootdse_modify.ldif", info, samdb, false);
723
724                 var commit_ok = samdb.transaction_commit();
725                 if (!commit_ok) {
726                         info.message("ldb commit failed: " + samdb.errstring() + "\n");
727                         assert(commit_ok);
728                 }
729                 return true;
730         }
731
732 //      message("Activate schema module");
733 //      setup_modify_ldif("schema_activation.ldif", info, samdb, false);
734 //
735 //      // (hack) Reload, now we have the schema loaded.  
736 //      var commit_ok = samdb.transaction_commit();
737 //      if (!commit_ok) {
738 //              info.message("samdb commit failed: " + samdb.errstring() + "\n");
739 //              assert(commit_ok);
740 //      }
741 //      samdb.close();
742 //
743 //      samdb = open_ldb(info, paths.samdb, false);
744 //
745         message("Setting up sam.ldb users and groups\n");
746         setup_add_ldif("provision_users.ldif", info, samdb, false);
747
748         if (subobj.SERVERROLE == "domain controller") {
749                 message("Setting up self join\n");
750                 setup_add_ldif("provision_self_join.ldif", info, samdb, false);
751                 setup_add_ldif("provision_group_policy.ldif", info, samdb, false);
752
753                 sys.mkdir(paths.sysvol, 0755);
754                 sys.mkdir(paths.sysvol + "/"+ subobj.DNSDOMAIN, 0755);
755                 sys.mkdir(paths.sysvol + "/"+ subobj.DNSDOMAIN + "/Policies", 0755);
756                 sys.mkdir(paths.sysvol + "/"+ subobj.DNSDOMAIN + "/Policies/{" + subobj.POLICYGUID + "}", 0755);
757                 sys.mkdir(paths.sysvol + "/"+ subobj.DNSDOMAIN + "/Policies/{" + subobj.POLICYGUID + "}/Machine", 0755);
758                 sys.mkdir(paths.sysvol + "/"+ subobj.DNSDOMAIN + "/Policies/{" + subobj.POLICYGUID + "}/User", 0755);
759
760                 sys.mkdir(paths.netlogon, 0755);
761
762                 setup_ldb("secrets_dc.ldif", info, paths.secrets, false);
763
764         }
765
766         if (setup_name_mappings(info, samdb) == false) {
767                 return false;
768         }
769
770         message("Setting up sam.ldb index\n");
771         setup_add_ldif("provision_index.ldif", info, samdb, false);
772
773         message("Setting up sam.ldb rootDSE marking as syncronized\n");
774         setup_modify_ldif("provision_rootdse_modify.ldif", info, samdb, false);
775
776         var commit_ok = samdb.transaction_commit();
777         if (!commit_ok) {
778                 info.message("samdb commit failed: " + samdb.errstring() + "\n");
779                 assert(commit_ok);
780         }
781
782         return true;
783 }
784
785 /*
786   provision just the schema into a temporary ldb, so we can run ad2oLschema on it
787 */
788 function provision_schema(subobj, message, tmp_schema_path, paths)
789 {
790         var lp = loadparm_init();
791         var sys = sys_init();
792         var info = new Object();
793
794         var ok = provision_fix_subobj(subobj, paths);
795         assert(ok);
796
797         info.subobj = subobj;
798         info.message = message;
799
800         message("Setting up sam.ldb partitions\n");
801
802         /* This will erase anything in the tmp db */
803         var samdb = open_ldb(info, tmp_schema_path, true);
804
805         message("Adding schema container (permitted to fail)\n");
806         var add_ok = setup_add_ldif("provision_schema_basedn.ldif", info, samdb, true);
807         message("Modifying schema container\n");
808         var modify_ok = setup_ldb_modify("provision_schema_basedn_modify.ldif", info, samdb);
809         if (!modify_ok) {
810                 if (!add_ok) {
811                         message("Failed to both add and modify schema dn: " + samdb.errstring() + "\n");
812                         message("Perhaps you need to run the provision script with the --ldap-base-dn option, and add this record to the backend manually\n"); 
813                         assert(modify_ok);
814                 }
815                 message("Failed to modify the schema container: " + samdb.errstring() + "\n");
816                 assert(modify_ok);
817         }
818
819         message("Setting up sam.ldb Samba4 schema\n");
820         setup_add_ldif("schema_samba4.ldif", info, samdb, false);
821         message("Setting up sam.ldb AD schema\n");
822         setup_add_ldif("schema.ldif", info, samdb, false);
823
824         var commit_ok = samdb.transaction_commit();
825         if (!commit_ok) {
826                 info.message("samdb commit failed: " + samdb.errstring() + "\n");
827                 assert(commit_ok);
828         }
829         samdb.close();
830 }
831
832 /* Write out a DNS zone file, from the info in the current database */
833 function provision_dns(subobj, message, paths, session_info, credentials)
834 {
835         var lp = loadparm_init();
836         if (subobj.SERVERROLE != "domain controller") {
837                 message("No DNS zone required for role %s\n", subobj.SERVERROLE);
838                 return;
839         }
840         message("Setting up DNS zone: " + subobj.DNSDOMAIN + " \n");
841         var ldb = ldb_init();
842         ldb.session_info = session_info;
843         ldb.credentials = credentials;
844
845         /* connect to the sam */
846         var ok = ldb.connect(paths.samdb);
847         assert(ok);
848
849         /* These values may have changed, due to an incoming SamSync,
850            or may not have been specified, so fetch them from the database */
851
852         var attrs = new Array("objectGUID");
853         res = ldb.search("objectGUID=*", subobj.DOMAINDN, ldb.SCOPE_BASE, attrs);
854         assert(res.error == 0);
855         assert(res.msgs.length == 1);
856         assert(res.msgs[0].objectGUID != undefined);
857         subobj.DOMAINGUID = res.msgs[0].objectGUID;
858
859         subobj.HOSTGUID = searchone(ldb, subobj.DOMAINDN, "(&(objectClass=computer)(cn=" + subobj.NETBIOSNAME + "))", "objectGUID");
860         assert(subobj.HOSTGUID != undefined);
861
862         setup_file("provision.zone", 
863                    message, paths.dns, 
864                    subobj);
865
866         setup_file("named.conf", 
867                    message, paths.named_conf, 
868                    subobj);
869
870         message("Please install the zone located in " + paths.dns + " into your DNS server.  A sample BIND configuration snippit is at " + paths.named_conf + "\n");
871 }
872
873 /* Write out a DNS zone file, from the info in the current database */
874 function provision_ldapbase(subobj, message, paths)
875 {
876         var ok = provision_fix_subobj(subobj, paths);
877         assert(ok);
878
879         message("Setting up LDAP base entry: " + subobj.DOMAINDN + " \n");
880         var rdns = split(",", subobj.DOMAINDN);
881         subobj.EXTENSIBLEOBJECT = "objectClass: extensibleObject";
882
883         subobj.RDN_DC = substr(rdns[0], strlen("DC="));
884
885         sys.mkdir(paths.ldapdir, 0700);
886
887         setup_file("provision_basedn.ldif", 
888                    message, paths.ldap_basedn_ldif, 
889                    subobj);
890
891         setup_file("provision_configuration_basedn.ldif", 
892                    message, paths.ldap_config_basedn_ldif, 
893                    subobj);
894
895         setup_file("provision_schema_basedn.ldif", 
896                    message, paths.ldap_schema_basedn_ldif, 
897                    subobj);
898
899 }
900
901
902 /*
903   guess reasonably default options for provisioning
904 */
905 function provision_guess()
906 {
907         var subobj = new Object();
908         var nss = nss_init();
909         var lp = loadparm_init();
910         var rdn_list;
911         random_init(local);
912
913         subobj.SERVERROLE   = strlower(lp.get("server role"));
914         subobj.REALM        = strupper(lp.get("realm"));
915         subobj.DOMAIN       = lp.get("workgroup");
916         subobj.HOSTNAME     = hostname();
917
918         assert(subobj.REALM);
919         assert(subobj.DOMAIN);
920         assert(subobj.HOSTNAME);
921         
922         subobj.VERSION      = version();
923         subobj.HOSTIP       = hostip();
924         subobj.DOMAINSID    = randsid();
925         subobj.INVOCATIONID = randguid();
926         subobj.POLICYGUID   = randguid();
927         subobj.KRBTGTPASS   = randpass(12);
928         subobj.MACHINEPASS  = randpass(12);
929         subobj.DNSPASS  = randpass(12);
930         subobj.ADMINPASS    = randpass(12);
931         subobj.LDAPMANAGERPASS     = randpass(12);
932         subobj.DEFAULTSITE  = "Default-First-Site-Name";
933         subobj.NEWGUID      = randguid;
934         subobj.NTTIME       = nttime;
935         subobj.LDAPTIME     = ldaptime;
936         subobj.DATESTRING   = datestring;
937         subobj.ROOT         = findnss(nss.getpwnam, "root");
938         subobj.NOBODY       = findnss(nss.getpwnam, "nobody");
939         subobj.NOGROUP      = findnss(nss.getgrnam, "nogroup", "nobody");
940         subobj.WHEEL        = findnss(nss.getgrnam, "wheel", "root", "staff", "adm");
941         subobj.BACKUP       = findnss(nss.getgrnam, "backup", "wheel", "root", "staff");
942         subobj.USERS        = findnss(nss.getgrnam, "users", "guest", "other", "unknown", "usr");
943
944         //Add modules to the list to activate them by default
945         //beware often order is important
946         //
947         // Some Known ordering constraints:
948         // - rootdse must be first, as it makes redirects from "" -> cn=rootdse
949         // - samldb must be before password_hash, because password_hash checks
950         //   that the objectclass is of type person (filled in by samldb)
951         // - partition must be last
952         // - each partition has its own module list then
953         modules_list        = new Array("rootdse",
954                                         "kludge_acl",
955                                         "paged_results",
956                                         "server_sort",
957                                         "extended_dn",
958                                         "asq",
959                                         "samldb",
960                                         "operational",
961                                         "objectclass",
962                                         "rdn_name",
963                                         "show_deleted",
964                                         "partition");
965         subobj.MODULES_LIST = join(",", modules_list);
966         subobj.DOMAINDN_LDB = "users.ldb";
967         subobj.CONFIGDN_LDB = "configuration.ldb";
968         subobj.SCHEMADN_LDB = "schema.ldb";
969         subobj.DOMAINDN_MOD = "pdc_fsmo,password_hash";
970         subobj.CONFIGDN_MOD = "naming_fsmo";
971         subobj.SCHEMADN_MOD = "schema_fsmo";
972         subobj.DOMAINDN_MOD2 = ",objectguid";
973         subobj.CONFIGDN_MOD2 = ",objectguid";
974         subobj.SCHEMADN_MOD2 = ",objectguid";
975
976         subobj.EXTENSIBLEOBJECT = "# no objectClass: extensibleObject for local ldb";
977         subobj.ACI              = "# no aci for local ldb";
978
979         return subobj;
980 }
981
982 /*
983   search for one attribute as a string
984  */
985 function searchone(ldb, basedn, expression, attribute)
986 {
987         var attrs = new Array(attribute);
988         res = ldb.search(expression, basedn, ldb.SCOPE_SUBTREE, attrs);
989         if (res.error != 0 ||
990             res.msgs.length != 1 ||
991             res.msgs[0][attribute] == undefined) {
992                 return undefined;
993         }
994         return res.msgs[0][attribute];
995 }
996
997 /*
998   modify an account to remove the 
999 */
1000 function enable_account(ldb, user_dn)
1001 {
1002         var attrs = new Array("userAccountControl");
1003         var res = ldb.search(NULL, user_dn, ldb.SCOPE_ONELEVEL, attrs);
1004         assert(res.error == 0);
1005         assert(res.msgs.length == 1);
1006         var userAccountControl = res.msgs[0].userAccountControl;
1007         userAccountControl = userAccountControl - 2; /* remove disabled bit */
1008         var mod = sprintf("
1009 dn: %s
1010 changetype: modify
1011 replace: userAccountControl
1012 userAccountControl: %u
1013 ", 
1014                           user_dn, userAccountControl);
1015         var ok = ldb.modify(mod);
1016         return (ok.error == 0); 
1017 }
1018
1019
1020 /*
1021   add a new user record
1022 */
1023 function newuser(username, unixname, password, message, session_info, credentials)
1024 {
1025         var lp = loadparm_init();
1026         var samdb = lp.get("sam database");
1027         var ldb = ldb_init();
1028         random_init(local);
1029         ldb.session_info = session_info;
1030         ldb.credentials = credentials;
1031
1032         /* connect to the sam */
1033         var ok = ldb.connect(samdb);
1034         assert(ok);
1035
1036         ldb.transaction_start();
1037
1038         /* find the DNs for the domain and the domain users group */
1039         var attrs = new Array("defaultNamingContext");
1040         res = ldb.search("defaultNamingContext=*", "", ldb.SCOPE_BASE, attrs);
1041         assert(res.error == 0);
1042         assert(res.msgs.length == 1 && res.msgs[0].defaultNamingContext != undefined);
1043         var domain_dn = res.msgs[0].defaultNamingContext;
1044         assert(domain_dn != undefined);
1045         var dom_users = searchone(ldb, domain_dn, "name=Domain Users", "dn");
1046         assert(dom_users != undefined);
1047
1048         var user_dn = sprintf("CN=%s,CN=Users,%s", username, domain_dn);
1049
1050
1051         /*
1052           the new user record. note the reliance on the samdb module to fill
1053           in a sid, guid etc
1054         */
1055         var ldif = sprintf("
1056 dn: %s
1057 sAMAccountName: %s
1058 memberOf: %s
1059 unixName: %s
1060 sambaPassword: %s
1061 objectClass: user
1062 ",
1063                            user_dn, username, dom_users,
1064                            unixname, password);
1065         /*
1066           add the user to the users group as well
1067         */
1068         var modgroup = sprintf("
1069 dn: %s
1070 changetype: modify
1071 add: member
1072 member: %s
1073 ", 
1074                                dom_users, user_dn);
1075
1076
1077         /*
1078           now the real work
1079         */
1080         message("Adding user %s\n", user_dn);
1081         ok = ldb.add(ldif);
1082         if (ok.error != 0) {
1083                 message("Failed to add %s - %s\n", user_dn, ok.errstr);
1084                 return false;
1085         }
1086
1087         message("Modifying group %s\n", dom_users);
1088         ok = ldb.modify(modgroup);
1089         if (ok.error != 0) {
1090                 message("Failed to modify %s - %s\n", dom_users, ok.errstr);
1091                 return false;
1092         }
1093
1094         /*
1095           modify the userAccountControl to remove the disabled bit
1096         */
1097         ok = enable_account(ldb, user_dn);
1098         if (ok) {
1099                 ldb.transaction_commit();
1100         }
1101         return ok;
1102 }
1103
1104 // Check whether a name is valid as a NetBIOS name. 
1105 // FIXME: There are probably more constraints here. 
1106 // crh has a paragraph on this in his book (1.4.1.1)
1107 function valid_netbios_name(name)
1108 {
1109         if (strlen(name) > 13) return false;
1110         return true;
1111 }
1112
1113 function provision_validate(subobj, message)
1114 {
1115         var lp = loadparm_init();
1116
1117         if (!valid_netbios_name(subobj.DOMAIN)) {
1118                 message("Invalid NetBIOS name for domain\n");
1119                 return false;
1120         }
1121
1122         if (!valid_netbios_name(subobj.NETBIOSNAME)) {
1123                 message("Invalid NetBIOS name for host\n");
1124                 return false;
1125         }
1126
1127
1128         if (strupper(lp.get("workgroup")) != strupper(subobj.DOMAIN_CONF)) {
1129                 message("workgroup '%s' in smb.conf must match chosen domain '%s'\n",
1130                         lp.get("workgroup"), subobj.DOMAIN_CONF);
1131                 return false;
1132         }
1133
1134         if (strupper(lp.get("realm")) != strupper(subobj.REALM_CONF)) {
1135                 message("realm '%s' in smb.conf must match chosen realm '%s'\n",
1136                         lp.get("realm"), subobj.REALM_CONF);
1137                 return false;
1138         }
1139
1140         if (strupper(lp.get("server role")) != strupper(subobj.SERVERROLE)) {
1141                 message("server role '%s' in smb.conf must match chosen role '%s'\n",
1142                         lp.get("server role"), subobj.SERVERROLE);
1143                 return false;
1144         }
1145
1146         return true;
1147 }
1148
1149 function join_domain(domain, netbios_name, join_type, creds, message) 
1150 {
1151         var ctx = NetContext(creds);
1152         var joindom = new Object();
1153         joindom.domain = domain;
1154         joindom.join_type = join_type;
1155         joindom.netbios_name = netbios_name;
1156         if (!ctx.JoinDomain(joindom)) {
1157                 message("Domain Join failed: " + joindom.error_string);
1158                 return false;
1159         }
1160         return true;
1161 }
1162
1163 /* Vampire a remote domain.  Session info and credentials are required for for
1164  * access to our local database (might be remote ldap)
1165  */ 
1166
1167 function vampire(domain, session_info, credentials, message) {
1168         var ctx = NetContext(credentials);
1169         var vampire_ctx = new Object();
1170         var machine_creds = credentials_init();
1171         machine_creds.set_domain(form.DOMAIN);
1172         if (!machine_creds.set_machine_account()) {
1173                 message("Failed to access domain join information!");
1174                 return false;
1175         }
1176         vampire_ctx.machine_creds = machine_creds;
1177         vampire_ctx.session_info = session_info;
1178         if (!ctx.SamSyncLdb(vampire_ctx)) {
1179                 message("Migration of remote domain to Samba failed: " + vampire_ctx.error_string);
1180                 return false;
1181         }
1182
1183         return true;
1184 }
1185
1186 return 0;