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