adb958a236d8be121c78edd297e41c6eadd8e810
[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         if (acc.fullname == undefined) {
118                 acc.fullname = acc.username;
119         }
120         
121         assert(acc.fullname != undefined);
122         assert(acc.nt_username != undefined);
123
124         var ldif = sprintf(
125 "dn: cn=%s,%s
126 objectClass: top
127 objectClass: user
128 lastLogon: %d
129 lastLogoff: %d
130 unixName: %s
131 sAMAccountName: %s
132 cn: %s
133 description: %s
134 primaryGroupID: %d
135 badPwdcount: %d
136 logonCount: %d
137 samba3Domain: %s
138 samba3DirDrive: %s
139 samba3MungedDial: %s
140 samba3Homedir: %s
141 samba3LogonScript: %s
142 samba3ProfilePath: %s
143 samba3Workstations: %s
144 samba3KickOffTime: %d
145 samba3BadPwdTime: %d
146 samba3PassLastSetTime: %d
147 samba3PassCanChangeTime: %d
148 samba3PassMustChangeTime: %d
149 objectSid: %s-%d
150 lmPwdHash:: %s
151 ntPwdHash:: %s
152
153 ", ldb.dn_escape(acc.fullname), domaindn, acc.logon_time, acc.logoff_time, acc.username, acc.nt_username, acc.nt_username, 
154
155 acc.acct_desc, acc.group_rid, acc.bad_password_count, acc.logon_count,
156 acc.domain, acc.dir_drive, acc.munged_dial, acc.homedir, acc.logon_script, 
157 acc.profile_path, acc.workstations, acc.kickoff_time, acc.bad_password_time, 
158 acc.pass_last_set_time, acc.pass_can_change_time, acc.pass_must_change_time, domainsid, acc.user_rid,
159         ldb.encode(acc.lm_pw), ldb.encode(acc.nt_pw)); 
160
161         return ldif;
162 }
163
164 function upgrade_sam_group(grp,domaindn)
165 {
166         var nss = nss_init();
167
168         var gr;
169         if (grp.sid_name_use == 5) { // Well-known group
170                 return undefined;
171         }
172
173         if (grp.nt_name == "Domain Guests" ||
174             grp.nt_name == "Domain Users" ||
175             grp.nt_name == "Domain Admins") {
176                 return undefined;
177         }
178         
179         if (grp.gid == -1) {
180                 gr = nss.getgrnam(grp.nt_name);
181         } else {
182                 gr = nss.getgrgid(grp.gid);
183         }
184
185         if (gr == undefined) {
186                 grp.unixname = "UNKNOWN";
187         } else {
188                 grp.unixname = gr.gr_name;
189         }
190
191         assert(grp.unixname != undefined);
192         
193         var ldif = sprintf(
194 "dn: cn=%s,%s
195 objectClass: top
196 objectClass: group
197 description: %s
198 cn: %s
199 objectSid: %s
200 unixName: %s
201 samba3SidNameUse: %d
202 ", grp.nt_name, domaindn, 
203 grp.comment, grp.nt_name, grp.sid, grp.unixname, grp.sid_name_use);
204
205         return ldif;
206 }
207
208 function upgrade_winbind(samba3,domaindn)
209 {
210         var ldif = sprintf("
211                 
212 dn: dc=none
213 userHwm: %d
214 groupHwm: %d
215
216 ", samba3.idmap.user_hwm, samba3.idmap.group_hwm);
217
218         for (var i in samba3.idmap.mappings) {
219                 var m = samba3.idmap.mappings[i];
220                 ldif = ldif + sprintf("
221 dn: SID=%s,%s
222 SID: %s
223 type: %d
224 unixID: %d", m.sid, domaindn, m.sid, m.type, m.unix_id);
225         }
226         
227         return ldif;
228 }
229 */
230
231 function upgrade_wins(samba3)
232 {
233         var ldif = "";
234         var version_id = 0;
235
236         for (i in samba3.winsentries) {
237                 var rType;
238                 var rState;
239                 var nType;
240                 var numIPs = 0;
241                 var e = samba3.winsentries[i];
242                 var now = sys.nttime();
243                 var ttl = sys.unix2nttime(e.ttl);
244
245                 version_id++;
246
247                 for (var i in e.ips) {
248                         numIPs++;
249                 }
250
251                 if (e.type == 0x1C) {
252                         rType = 0x2;
253                 } else if (sys.bitAND(e.type, 0x80)) {
254                         if (numIPs > 1) {
255                                 rType = 0x2;
256                         } else {
257                                 rType = 0x1;
258                         }
259                 } else {
260                         if (numIPs > 1) {
261                                 rType = 0x3;
262                         } else {
263                                 rType = 0x0;
264                         }
265                 }
266
267                 if (ttl > now) {
268                         rState = 0x0;/* active */
269                 } else {
270                         rState = 0x1;/* released */             
271                 }
272
273                 nType = (sys.bitAND(e.nb_flags,0x60)>>5);
274
275                 ldif = ldif + sprintf("
276 dn: name=%s,type=0x%02X
277 type: 0x%02X
278 name: %s
279 objectClass: winsRecord
280 recordType: %u
281 recordState: %u
282 nodeType: %u
283 isStatic: 0
284 expireTime: %s
285 versionID: %llu
286 ", e.name, e.type, e.type, e.name, 
287    rType, rState, nType, 
288    sys.ldaptime(ttl), version_id);
289
290                 for (var i in e.ips) {
291                         ldif = ldif + sprintf("address: %s\n", e.ips[i]);
292                 }
293         }
294
295         ldif = ldif + sprintf("
296 dn: CN=VERSION
297 objectClass: winsMaxVersion
298 maxVersion: %llu
299 ", version_id);
300
301         return ldif;
302 }
303
304 function upgrade_provision(samba3)
305 {
306         var subobj = new Object();
307         var nss = nss_init();
308         var lp = loadparm_init();
309         var rdn_list;
310
311         var domainname = samba3.configuration.get("workgroup");
312         
313         if (domainname == undefined) {
314                 domainname = samba3.secrets.domains[0].name;
315                 println("No domain specified in smb.conf file, assuming '" + domainname + "'");
316         }
317         
318         var domsec = samba3.find_domainsecrets(domainname);
319         var hostsec = samba3.find_domainsecrets(hostname());
320         var realm = samba3.configuration.get("realm");
321
322         if (realm == undefined) {
323                 realm = domainname;
324                 println("No realm specified in smb.conf file, assuming '" + realm + "'");
325         }
326         random_init(local);
327
328         subobj.REALM        = realm;
329         subobj.DOMAIN       = domainname;
330         subobj.HOSTNAME     = hostname();
331
332         assert(subobj.REALM);
333         assert(subobj.DOMAIN);
334         assert(subobj.HOSTNAME);
335
336         subobj.HOSTIP       = hostip();
337         if (domsec != undefined) {
338                 subobj.DOMAINGUID   = domsec.guid;
339                 subobj.DOMAINSID    = domsec.sid;
340         } else {
341                 println("Can't find domain secrets for '" + domainname + "'; using random SID and GUID");
342                 subobj.DOMAINGUID = randguid();
343                 subobj.DOMAINSID = randsid();
344         }
345         
346         if (hostsec) {
347                 subobj.HOSTGUID     = hostsec.guid;
348         } else {
349                 subobj.HOSTGUID = randguid();
350         }
351         subobj.INVOCATIONID = randguid();
352         subobj.KRBTGTPASS   = randpass(12);
353         subobj.MACHINEPASS  = randpass(12);
354         subobj.ADMINPASS    = randpass(12);
355         subobj.DEFAULTSITE  = "Default-First-Site-Name";
356         subobj.NEWGUID      = randguid;
357         subobj.NTTIME       = nttime;
358         subobj.LDAPTIME     = ldaptime;
359         subobj.DATESTRING   = datestring;
360         subobj.USN          = nextusn;
361         subobj.ROOT         = findnss(nss.getpwnam, "root");
362         subobj.NOBODY       = findnss(nss.getpwnam, "nobody");
363         subobj.NOGROUP      = findnss(nss.getgrnam, "nogroup", "nobody");
364         subobj.WHEEL        = findnss(nss.getgrnam, "wheel", "root");
365         subobj.USERS        = findnss(nss.getgrnam, "users", "guest", "other");
366         subobj.DNSDOMAIN    = strlower(subobj.REALM);
367         subobj.DNSNAME      = sprintf("%s.%s", 
368                                       strlower(subobj.HOSTNAME), 
369                                       subobj.DNSDOMAIN);
370         subobj.BASEDN       = "DC=" + join(",DC=", split(".", subobj.REALM));
371         rdn_list = split(".", subobj.REALM);
372         return subobj;
373 }
374
375 smbconf_keep = new Array(
376         "dos charset", 
377         "unix charset",
378         "display charset",
379         "comment",
380         "path",
381         "directory",
382         "workgroup",
383         "realm",
384         "netbios name",
385         "netbios aliases",
386         "netbios scope",
387         "server string",
388         "interfaces",
389         "bind interfaces only",
390         "security",
391         "auth methods",
392         "encrypt passwords",
393         "null passwords",
394         "obey pam restrictions",
395         "password server",
396         "smb passwd file",
397         "private dir",
398         "passwd chat",
399         "password level",
400         "lanman auth",
401         "ntlm auth",
402         "client NTLMv2 auth",
403         "client lanman auth",
404         "client plaintext auth",
405         "read only",
406         "hosts allow",
407         "hosts deny",
408         "log level",
409         "debuglevel",
410         "log file",
411         "smb ports",
412         "large readwrite",
413         "max protocol",
414         "min protocol",
415         "unicode",
416         "read raw",
417         "write raw",
418         "disable netbios",
419         "nt status support",
420         "announce version",
421         "announce as",
422         "max mux",
423         "max xmit",
424         "name resolve order",
425         "max wins ttl",
426         "min wins ttl",
427         "time server",
428         "unix extensions",
429         "use spnego",
430         "server signing",
431         "client signing",
432         "max connections",
433         "paranoid server security",
434         "socket options",
435         "strict sync",
436         "max print jobs",
437         "printable",
438         "print ok",
439         "printer name",
440         "printer",
441         "map system",
442         "map hidden",
443         "map archive",
444         "preferred master",
445         "prefered master",
446         "local master",
447         "browseable",
448         "browsable",
449         "wins server",
450         "wins support",
451         "csc policy",
452         "strict locking",
453         "config file",
454         "preload",
455         "auto services",
456         "lock dir",
457         "lock directory",
458         "pid directory",
459         "socket address",
460         "copy",
461         "include",
462         "available",
463         "volume",
464         "fstype",
465         "panic action",
466         "msdfs root",
467         "host msdfs",
468         "winbind separator");
469
470 /*
471    Remove configuration variables not present in Samba4
472         oldconf: Old configuration structure
473         mark: Whether removed configuration variables should be 
474                 kept in the new configuration as "samba3:<name>"
475  */
476 function upgrade_smbconf(oldconf,mark)
477 {
478         var data = oldconf.data();
479         var newconf = param_init();
480
481         for (var s in data) {
482                 for (var p in data[s]) {
483                         var keep = false;
484                         for (var k in smbconf_keep) { 
485                                 if (smbconf_keep[k] == p) {
486                                         keep = true;
487                                         break;
488                                 }
489                         }
490
491                         if (keep) {
492                                 newconf.set(s, p, oldconf.get(s, p));
493                         } else if (mark) {
494                                 newconf.set(s, "samba3:"+p, oldconf.get(s,p));
495                         }
496                 }
497         }
498
499         if (oldconf.get("domain logons") == "True") {
500                 newconf.set("server role", "domain controller");
501         } else {
502                 if (oldconf.get("security") == "user") {
503                         newconf.set("server role", "standalone");
504                 } else {
505                         newconf.set("server role", "member server");
506                 }
507         }
508
509         return newconf;
510 }
511
512 function upgrade(subobj, samba3, message, paths, session_info, credentials)
513 {
514         var ret = 0;
515         var lp = loadparm_init();
516         var samdb = ldb_init();
517         samdb.session_info = session_info;
518         samdb.credentials = credentials;
519         var ok = samdb.connect(paths.samdb);
520         if (!ok) {
521                 info.message("samdb connect failed: " + samdb.errstring() + "\n");
522                 assert(ok);
523         }
524
525         message("Writing configuration\n");
526         var newconf = upgrade_smbconf(samba3.configuration,true);
527         newconf.save(paths.smbconf);
528
529         message("Importing account policies\n");
530         var ldif = upgrade_sam_policy(samba3,subobj.BASEDN);
531         ok = samdb.modify(ldif);
532         if (!ok) {
533                 message("samdb load failed: " + samdb.errstring() + "\n");
534                 assert(ok);
535         }
536         var regdb = ldb_init();
537         ok = regdb.connect(paths.hklm);
538         if (!ok) {
539                 message("registry connect: " + regdb.errstring() + "\n");
540                 assert(ok);
541         }
542
543         ok = regdb.modify(sprintf("
544 dn: value=RefusePasswordChange,key=Parameters,key=Netlogon,key=Services,key=CurrentControlSet,key=System,HIVE=NONE
545 replace: type
546 type: 4
547 replace: data
548 data: %d
549 ", samba3.policy.refuse_machine_password_change));
550         if (!ok) {
551                 message("registry load failed: " + regdb.errstring() + "\n");
552                 assert(ok);
553         }
554
555         message("Importing users\n");
556         for (var i in samba3.samaccounts) {
557                 var msg = "... " + samba3.samaccounts[i].username;
558                 var ldif = upgrade_sam_account(samdb,samba3.samaccounts[i],subobj.BASEDN,subobj.DOMAINSID);
559                 ok = samdb.add(ldif);
560                 if (!ok && samdb.errstring() != "Record exists") { 
561                         msg = msg + "... error: " + samdb.errstring();
562                         ret = ret + 1; 
563                 }
564                 message(msg + "\n");
565         }
566
567         message("Importing groups\n");
568         for (var i in samba3.groupmappings) {
569                 var msg = "... " + samba3.groupmappings[i].nt_name;
570                 var ldif = upgrade_sam_group(samba3.groupmappings[i],subobj.BASEDN);
571                 if (ldif != undefined) {
572                         ok = samdb.add(ldif);
573                         if (!ok && samdb.errstring() != "Record exists") { 
574                                 msg = msg + "... error: " + samdb.errstring();
575                                 ret = ret + 1; 
576                         }
577                 }
578                 message(msg + "\n");
579         }
580
581         message("Importing registry data\n");
582         var hives = new Array("hkcr","hkcu","hklm","hkpd","hku","hkpt"); 
583         for (var i in hives) {
584                 var hn = hives[i];
585                 message("... " + hn + "\n");
586                 regdb = ldb_init();
587                 ok = regdb.connect(paths[hn]);
588                 assert(ok);
589                 var ldif = upgrade_registry(samba3.registry, hn, regdb);
590                 for (var j in ldif) {
591                         var msg = "... ... " + j;
592                         ok = regdb.add(ldif[j]);
593                         if (!ok && regdb.errstring() != "Record exists") { 
594                                 msg = msg + "... error: " + regdb.errstring();
595                                 ret = ret + 1; 
596                         }
597                         message(msg + "\n");
598                 }
599         }
600
601
602         message("Importing WINS data\n");
603         var winsdb = ldb_init();
604         ok = winsdb.connect(paths.winsdb);
605         assert(ok);
606         ldb_erase(winsdb);
607
608         var ldif = upgrade_wins(samba3);
609         ok = winsdb.add(ldif);
610         assert(ok);
611
612         // figure out ldapurl, if applicable
613         var ldapurl = undefined;
614         var pdb = samba3.configuration.get_list("passdb backend");
615         if (pdb != undefined) {
616                 for (var b in pdb) {
617                         if (strlen(pdb[b]) >= 7) {
618                                 if (substr(pdb[b], 0, 7) == "ldapsam") {
619                                         ldapurl = substr(pdb[b], 8);
620                                 }
621                         }
622                 }
623         }
624
625         // URL was not specified in passdb backend but ldap /is/ used
626         if (ldapurl == "") {
627                 ldapurl = "ldap://" + samba3.configuration.get("ldap server");
628         }
629
630         // Enable samba3sam module if original passdb backend was ldap
631         if (ldapurl != undefined) {
632                 message("Enabling Samba3 LDAP mappings for SAM database\n");
633
634                 ok = samdb.modify("
635 dn: @MODULES
636 changetype: modify
637 replace: @LIST
638 @LIST: samldb,operational,objectguid,rdn_name,samba3sam
639 ");
640                 if (!ok) {
641                         message("Error enabling samba3sam module: " + samdb.errstring() + "\n");
642                         ret = ret + 1;
643                 }
644
645                 ok = samdb.add(sprintf("
646 dn: @MAP=samba3sam
647 @MAP_URL: %s", ldapurl));
648                 assert(ok);
649
650         }
651
652         return ret;
653 }
654
655 function upgrade_verify(subobj, samba3,paths,message)
656 {
657         message("Verifying account policies\n");
658         var samldb = ldb_init();
659         var ne = 0;
660
661         var ok = samldb.connect(paths.samdb);
662         assert(ok);
663
664         for (var i in samba3.samaccounts) {
665                 var msg = samldb.search("(&(sAMAccountName=" + samba3.samaccounts[i].nt_username + ")(objectclass=user))");
666                 assert(msg.length >= 1);
667         }
668         
669         // FIXME
670 }