r10515: Handle replacement of "domain logons" and "domain master" by "server role"
[kai/samba.git] / source / scripting / libjs / upgrade.js
1 /*
2         backend code for upgrading from Samba3
3         Copyright Jelmer Vernooij 2005
4         Released under the GNU GPL v2 or later
5 */
6
7 libinclude("base.js");
8
9 function regkey_to_dn(name)
10 {
11         var dn = "hive=NONE";
12         var i = 0;
13
14         var as = split("/", name);
15
16         for (i in as) {
17                 if (i > 0) {
18                         dn = sprintf("key=%s,", as[i]) + dn;
19                 }
20         }
21
22         return dn;
23 }
24
25 /* Where prefix is any of:
26  * - HKLM
27  *   HKU
28  *   HKCR
29  *   HKPD
30  *   HKPT
31  */
32
33 function upgrade_registry(regdb,prefix,ldb)
34 {
35         assert(regdb != undefined);
36         var prefix_up = strupper(prefix);
37         var ldif = new Array();
38
39         for (var i in regdb.keys) {
40                 var rk = regdb.keys[i];
41                 var pts = split("/", rk.name);
42
43                 /* Only handle selected hive */
44                 if (strupper(pts[0]) != prefix_up) {
45                         continue;
46                 }
47
48                 var keydn = regkey_to_dn(rk.name);
49
50                 var pts = split("/", rk.name);
51
52                 /* Convert key name to dn */
53                 ldif[rk.name] = sprintf("
54 dn: %s
55 name: %s
56
57 ", keydn, pts[0]);
58                 
59                 for (var j in rk.values) {
60                         var rv = rk.values[j];
61
62                         ldif[rk.name + " (" + rv.name + ")"] = sprintf("
63 dn: %s,value=%s
64 value: %s
65 type: %d
66 data:: %s", keydn, rv.name, rv.name, rv.type, ldb.encode(rv.data));
67                 }
68         }
69
70         return ldif;
71 }
72
73 function upgrade_sam_policy(samba3,dn)
74 {
75         var ldif = sprintf("
76 dn: %s
77 changetype: modify
78 replace: minPwdLength
79 minPwdLength: %d
80 pwdHistoryLength: %d
81 minPwdAge: %d
82 maxPwdAge: %d
83 lockoutDuration: %d
84 samba3ResetCountMinutes: %d
85 samba3UserMustLogonToChangePassword: %d
86 samba3BadLockoutMinutes: %d
87 samba3DisconnectTime: %d
88
89 ", dn, samba3.policy.min_password_length, 
90         samba3.policy.password_history, samba3.policy.minimum_password_age,
91         samba3.policy.maximum_password_age, samba3.policy.lockout_duration,
92         samba3.policy.reset_count_minutes, samba3.policy.user_must_logon_to_change_password,
93         samba3.policy.bad_lockout_minutes, samba3.policy.disconnect_time
94 );
95         
96         return ldif;
97 }
98
99 function upgrade_sam_account(ldb,acc,domaindn,domainsid)
100 {
101         if (acc.nt_username == undefined) {
102                 acc.nt_username = acc.username;
103         }       
104
105         if (acc.nt_username == "") {
106                 acc.nt_username = acc.username;
107         }       
108
109         if (acc.fullname == undefined) {
110                 var pw = nss.getpwnam(acc.fullname);
111                 acc.fullname = pw.pw_gecos;
112         }
113
114         var pts = split(',', acc.fullname);
115         acc.fullname = pts[0];
116         
117         assert(acc.fullname != undefined);
118         assert(acc.nt_username != undefined);
119
120         var ldif = sprintf(
121 "dn: cn=%s,%s
122 objectClass: top
123 objectClass: user
124 lastLogon: %d
125 lastLogoff: %d
126 unixName: %s
127 name: %s
128 sAMAccountName: %s
129 cn: %s
130 description: %s
131 primaryGroupID: %d
132 badPwdcount: %d
133 logonCount: %d
134 samba3Domain: %s
135 samba3DirDrive: %s
136 samba3MungedDial: %s
137 samba3Homedir: %s
138 samba3LogonScript: %s
139 samba3ProfilePath: %s
140 samba3Workstations: %s
141 samba3KickOffTime: %d
142 samba3BadPwdTime: %d
143 samba3PassLastSetTime: %d
144 samba3PassCanChangeTime: %d
145 samba3PassMustChangeTime: %d
146 objectSid: %s-%d
147 lmPwdHash:: %s
148 ntPwdHash:: %s
149
150 ", acc.fullname, domaindn, acc.logon_time, acc.logoff_time, acc.username, acc.nt_username, acc.nt_username, 
151
152 acc.fullname, acc.acct_desc, acc.group_rid, acc.bad_password_count, acc.logon_count,
153 acc.domain, acc.dir_drive, acc.munged_dial, acc.homedir, acc.logon_script, 
154 acc.profile_path, acc.workstations, acc.kickoff_time, acc.bad_password_time, 
155 acc.pass_last_set_time, acc.pass_can_change_time, acc.pass_must_change_time, domainsid, acc.user_rid,
156         ldb.encode(acc.lm_pw), ldb.encode(acc.nt_pw)); 
157
158         return ldif;
159 }
160
161 function upgrade_sam_group(grp,domaindn)
162 {
163         var nss = nss_init();
164
165         var gr;
166         if (grp.sid_name_use == 5) { // Well-known group
167                 return undefined;
168         }
169
170         if (grp.nt_name == "Domain Guests" ||
171             grp.nt_name == "Domain Users" ||
172             grp.nt_name == "Domain Admins") {
173                 return undefined;
174         }
175         
176         if (grp.gid == -1) {
177                 gr = nss.getgrnam(grp.nt_name);
178         } else {
179                 gr = nss.getgrgid(grp.gid);
180         }
181
182         if (gr == undefined) {
183                 grp.unixname = "UNKNOWN";
184         } else {
185                 grp.unixname = gr.gr_name;
186         }
187
188         assert(grp.unixname != undefined);
189         
190         var ldif = sprintf(
191 "dn: cn=%s,%s
192 objectClass: top
193 objectClass: group
194 description: %s
195 cn: %s
196 objectSid: %s
197 unixName: %s
198 samba3SidNameUse: %d
199 ", grp.nt_name, domaindn, 
200 grp.comment, grp.nt_name, grp.sid, grp.unixname, grp.sid_name_use);
201
202         return ldif;
203 }
204
205 function upgrade_winbind(samba3,domaindn)
206 {
207         var ldif = sprintf("
208                 
209 dn: dc=none
210 userHwm: %d
211 groupHwm: %d
212
213 ", samba3.idmap.user_hwm, samba3.idmap.group_hwm);
214
215         for (var i in samba3.idmap.mappings) {
216                 var m = samba3.idmap.mappings[i];
217                 ldif = ldif + sprintf("
218 dn: SID=%s,%s
219 SID: %s
220 type: %d
221 unixID: %d", m.sid, domaindn, m.sid, m.type, m.unix_id);
222         }
223         
224         return ldif;
225 }
226 */
227
228 function upgrade_wins(samba3)
229 {
230         var ldif = "";
231         for (i in samba3.winsentries) {
232                 var e = samba3.winsentries[i];
233                 
234                 ldif = ldif + sprintf("
235 dn: type=%d,name=%s
236 name: %s
237 objectClass: wins
238 nbFlags: %x
239 expires: %s
240 ", e.type, e.name, e.name, e.type, e.nb_flags, sys.ldaptime(e.ttl));
241
242                 for (var i in e.ips) {
243                         ldif = ldif + sprintf("address: %s\n", e.ips[i]);
244                 }
245         }
246
247         return ldif;
248 }
249
250 function upgrade_provision(samba3)
251 {
252         var subobj = new Object();
253         var nss = nss_init();
254         var lp = loadparm_init();
255         var rdn_list;
256
257         var domainname = samba3.configuration.get("workgroup");
258         
259         if (domainname == undefined) {
260                 domainname = samba3.secrets.domains[0].name;
261                 println("No domain specified in smb.conf file, assuming '" + domainname + "'");
262         }
263         
264         var domsec = samba3.find_domainsecrets(domainname);
265         var hostsec = samba3.find_domainsecrets(hostname());
266         var realm = samba3.configuration.get("realm");
267
268         if (realm == undefined) {
269                 realm = domainname;
270                 println("No realm specified in smb.conf file, assuming '" + realm + "'");
271         }
272         random_init(local);
273
274         subobj.REALM        = realm;
275         subobj.DOMAIN       = domainname;
276         subobj.HOSTNAME     = hostname();
277
278         assert(subobj.REALM);
279         assert(subobj.DOMAIN);
280         assert(subobj.HOSTNAME);
281
282         subobj.HOSTIP       = hostip();
283         if (domsec != undefined) {
284                 subobj.DOMAINGUID   = domsec.guid;
285                 subobj.DOMAINSID    = domsec.sid;
286         } else {
287                 println("Can't find domain secrets for '" + domainname + "'; using random SID and GUID");
288                 subobj.DOMAINGUID = randguid();
289                 subobj.DOMAINSID = randsid();
290         }
291         
292         if (hostsec) {
293                 subobj.HOSTGUID     = hostsec.guid;
294         } else {
295                 subobj.HOSTGUID = randguid();
296         }
297         subobj.INVOCATIONID = randguid();
298         subobj.KRBTGTPASS   = randpass(12);
299         subobj.MACHINEPASS  = randpass(12);
300         subobj.ADMINPASS    = randpass(12);
301         subobj.DEFAULTSITE  = "Default-First-Site-Name";
302         subobj.NEWGUID      = randguid;
303         subobj.NTTIME       = nttime;
304         subobj.LDAPTIME     = ldaptime;
305         subobj.DATESTRING   = datestring;
306         subobj.USN          = nextusn;
307         subobj.ROOT         = findnss(nss.getpwnam, "root");
308         subobj.NOBODY       = findnss(nss.getpwnam, "nobody");
309         subobj.NOGROUP      = findnss(nss.getgrnam, "nogroup", "nobody");
310         subobj.WHEEL        = findnss(nss.getgrnam, "wheel", "root");
311         subobj.USERS        = findnss(nss.getgrnam, "users", "guest", "other");
312         subobj.DNSDOMAIN    = strlower(subobj.REALM);
313         subobj.DNSNAME      = sprintf("%s.%s", 
314                                       strlower(subobj.HOSTNAME), 
315                                       subobj.DNSDOMAIN);
316         subobj.BASEDN       = "DC=" + join(",DC=", split(".", subobj.REALM));
317         rdn_list = split(".", subobj.REALM);
318         return subobj;
319 }
320
321 smbconf_keep = new Array(
322         "dos charset", 
323         "unix charset",
324         "display charset",
325         "comment",
326         "path",
327         "directory",
328         "workgroup",
329         "realm",
330         "netbios name",
331         "netbios aliases",
332         "netbios scope",
333         "server string",
334         "interfaces",
335         "bind interfaces only",
336         "security",
337         "auth methods",
338         "encrypt passwords",
339         "null passwords",
340         "obey pam restrictions",
341         "password server",
342         "smb passwd file",
343         "private dir",
344         "passwd chat",
345         "password level",
346         "lanman auth",
347         "ntlm auth",
348         "client NTLMv2 auth",
349         "client lanman auth",
350         "client plaintext auth",
351         "read only",
352         "hosts allow",
353         "hosts deny",
354         "log level",
355         "debuglevel",
356         "log file",
357         "smb ports",
358         "large readwrite",
359         "max protocol",
360         "min protocol",
361         "unicode",
362         "read raw",
363         "write raw",
364         "disable netbios",
365         "nt status support",
366         "announce version",
367         "announce as",
368         "max mux",
369         "max xmit",
370         "name resolve order",
371         "max wins ttl",
372         "min wins ttl",
373         "time server",
374         "unix extensions",
375         "use spnego",
376         "server signing",
377         "client signing",
378         "max connections",
379         "paranoid server security",
380         "socket options",
381         "strict sync",
382         "max print jobs",
383         "printable",
384         "print ok",
385         "printer name",
386         "printer",
387         "map system",
388         "map hidden",
389         "map archive",
390         "preferred master",
391         "prefered master",
392         "local master",
393         "browseable",
394         "browsable",
395         "wins server",
396         "wins support",
397         "csc policy",
398         "strict locking",
399         "config file",
400         "preload",
401         "auto services",
402         "lock dir",
403         "lock directory",
404         "pid directory",
405         "socket address",
406         "copy",
407         "include",
408         "available",
409         "volume",
410         "fstype",
411         "panic action",
412         "msdfs root",
413         "host msdfs",
414         "winbind separator");
415
416 /*
417    Remove configuration variables not present in Samba4
418         oldconf: Old configuration structure
419         mark: Whether removed configuration variables should be 
420                 kept in the new configuration as "samba3:<name>"
421  */
422 function upgrade_smbconf(oldconf,mark)
423 {
424         var data = oldconf.data();
425         var newconf = param_init();
426
427         for (var s in data) {
428                 for (var p in data[s]) {
429                         var keep = false;
430                         for (var k in smbconf_keep) { 
431                                 if (smbconf_keep[k] == p) {
432                                         keep = true;
433                                         break;
434                                 }
435                         }
436
437                         if (keep) {
438                                 newconf.set(s, p, oldconf.get(s, p));
439                         } else if (mark) {
440                                 newconf.set(s, "samba3:"+p, oldconf.get(s,p));
441                         }
442                 }
443         }
444
445         if (oldconf.get("domain logons") == "True") {
446                 if (oldconf.get("domain master") == "True") {
447                         newconf.set("role", "pdc");
448                 } else {
449                         newconf.set("role", "bdc");
450                 }
451         } else {
452                 if (oldconf.get("domain master") == "True") {
453                         newconf.set("role", "standalone");
454                 } else {
455                         newconf.set("role", "member server");
456                 }
457         }
458
459         return newconf;
460 }
461
462 function upgrade(subobj, samba3, message, paths)
463 {
464         var ret = 0;
465         var lp = loadparm_init();
466         var samdb = ldb_init();
467         var ok = samdb.connect(paths.samdb);
468         assert(ok);
469
470         message("Writing configuration\n");
471         var newconf = upgrade_smbconf(samba3.configuration,true);
472         newconf.save(paths.smbconf);
473
474         message("Importing account policies\n");
475         var ldif = upgrade_sam_policy(samba3,subobj.BASEDN);
476         ok = samdb.modify(ldif);
477         assert(ok);
478
479         var regdb = ldb_init();
480         ok = regdb.connect(paths.hklm);
481         assert(ok);
482         ok = regdb.modify(sprintf("
483 dn: value=RefusePasswordChange,key=Parameters,key=Netlogon,key=Services,key=CurrentControlSet,key=System,HIVE=NONE
484 replace: type
485 type: 4
486 replace: data
487 data: %d
488 ", samba3.policy.refuse_machine_password_change));
489         assert(ok);
490
491         message("Importing users\n");
492         for (var i in samba3.samaccounts) {
493                 var msg = "... " + samba3.samaccounts[i].username;
494                 var ldif = upgrade_sam_account(samdb,samba3.samaccounts[i],subobj.BASEDN,subobj.DOMAINSID);
495                 ok = samdb.add(ldif);
496                 if (!ok && samdb.errstring() != "Record exists") { 
497                         msg = msg + "... error: " + samdb.errstring();
498                         ret = ret + 1; 
499                 }
500                 message(msg + "\n");
501         }
502
503         message("Importing groups\n");
504         for (var i in samba3.groupmappings) {
505                 var msg = "... " + samba3.groupmappings[i].nt_name;
506                 var ldif = upgrade_sam_group(samba3.groupmappings[i],subobj.BASEDN);
507                 if (ldif != undefined) {
508                         ok = samdb.add(ldif);
509                         if (!ok && samdb.errstring() != "Record exists") { 
510                                 msg = msg + "... error: " + samdb.errstring();
511                                 ret = ret + 1; 
512                         }
513                 }
514                 message(msg + "\n");
515         }
516
517         message("Importing registry data\n");
518         var hives = new Array("hkcr","hkcu","hklm","hkpd","hku","hkpt"); 
519         for (var i in hives) {
520                 var hn = hives[i];
521                 message("... " + hn + "\n");
522                 regdb = ldb_init();
523                 ok = regdb.connect(paths[hn]);
524                 assert(ok);
525                 var ldif = upgrade_registry(samba3.registry, hn, regdb);
526                 for (var j in ldif) {
527                         var msg = "... ... " + j;
528                         ok = regdb.add(ldif[j]);
529                         if (!ok && regdb.errstring() != "Record exists") { 
530                                 msg = msg + "... error: " + regdb.errstring();
531                                 ret = ret + 1; 
532                         }
533                         message(msg + "\n");
534                 }
535         }
536
537
538         message("Importing WINS data\n");
539         var winsdb = ldb_init();
540         ok = winsdb.connect(paths.winsdb);
541         assert(ok);
542         ldb_erase(winsdb);
543
544         var ldif = upgrade_wins(samba3);
545         ok = winsdb.add(ldif);
546         assert(ok);
547
548         // figure out ldapurl, if applicable
549         var ldapurl = undefined;
550         var pdb = samba3.configuration.get_list("passdb backend");
551         if (pdb != undefined) {
552                 for (var b in pdb) {
553                         if (substr(pdb[b], 0, 7) == "ldapsam") {
554                                 ldapurl = substr(pdb[b], 8);
555                         }
556                 }
557         }
558
559         // URL was not specified in passdb backend but ldap /is/ used
560         if (ldapurl == "") {
561                 ldapurl = "ldap://" + samba3.configuration.get("ldap server");
562         }
563
564         // Enable samba3sam module if original passdb backend was ldap
565         if (ldapurl != undefined) {
566                 message("Enabling Samba3 LDAP mappings for SAM database\n");
567
568                 ok = samdb.modify("
569 dn: @MODULES
570 changetype: modify
571 replace: @LIST
572 @LIST: samldb,timestamps,objectguid,rdn_name,samba3sam
573 ");
574                 if (!ok) {
575                         message("Error enabling samba3sam module: " + samdb.errstring() + "\n");
576                         ret = ret + 1;
577                 }
578
579                 ok = samdb.add(sprintf("
580 dn: @MAP=samba3sam
581 @MAP_URL: %s", ldapurl));
582                 assert(ok);
583
584         }
585
586         return ret;
587 }
588
589 function upgrade_verify(subobj, samba3,paths,message)
590 {
591         message("Verifying account policies\n");
592         var samldb = ldb_init();
593         var ne = 0;
594
595         var ok = samldb.connect(paths.samdb);
596         assert(ok);
597
598         for (var i in samba3.samaccounts) {
599                 var msg = samldb.search("(&(sAMAccountName=" + samba3.samaccounts[i].nt_username + ")(objectclass=user))");
600                 assert(msg.length >= 1);
601         }
602         
603         // FIXME
604 }