r7940: use local path first for ldbadd in provisioning
[ira/wip.git] / source / setup / provision.pl
1 #!/usr/bin/perl -w
2
3 use strict;
4 use Socket;
5 use Getopt::Long;
6
7 my $opt_hostname = `hostname | cut -d. -f1`;
8 chomp $opt_hostname;
9 my $opt_hostip;
10 my $opt_realm;
11 my $opt_domain;
12 my $opt_adminpass;
13 my $opt_root;
14 my $opt_nobody;
15 my $opt_nogroup;
16 my $opt_wheel;
17 my $opt_users;
18 my $opt_outputdir;
19 my $opt_quiet;
20 my $dnsdomain;
21 my $netbiosname;
22 my $dnsname;
23 my $basedn;
24 my $defaultsite = "Default-First-Site-Name";
25 my $usn = 1;
26
27 # return the current NTTIME as an integer
28 sub nttime()
29 {
30         my $t = time();
31         $t += (369.0*365.25*24*60*60-(3.0*24*60*60+6.0*60*60));
32         $t *= 1.0e7;
33         return sprintf("%lld", $t);
34 }
35
36 # generate a random guid. Not a good algorithm.
37 sub randguid()
38 {
39         my $r1 = int(rand(2**32));
40         my $r2 = int(rand(2**16));
41         my $r3 = int(rand(2**16));
42         my $r4 = int(rand(2**16));
43         my $r5 = int(rand(2**32));
44         my $r6 = int(rand(2**16));
45         return sprintf("%08x-%04x-%04x-%04x-%08x%04x", $r1, $r2, $r3, $r4, $r5, $r6);
46 }
47
48 my $opt_domainguid = randguid();
49 my $opt_hostguid = randguid();
50 my $opt_invocationid = randguid();
51
52 sub randsid()
53 {
54         return sprintf("S-1-5-21-%d-%d-%d", 
55                        int(rand(10**8)), int(rand(10**8)), int(rand(10**8)));
56 }
57
58 my $opt_domainsid = randsid();
59
60 # generate a random password. Poor algorithm :(
61 sub randpass()
62 {
63         my $pass = "";
64         my $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ%\$!~";
65         for (my $i=0;$i<8;$i++) {
66                 my $c = int(rand(length($chars)));
67                 $pass .= substr($chars, $c, 1);
68         }
69         return $pass;
70 }
71
72 my $joinpass = randpass();
73
74 sub ldaptime()
75 {
76         my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) =  gmtime(time);
77         return sprintf "%04u%02u%02u%02u%02u%02u.0Z",
78         $year+1900, $mon+1, $mday, $hour, $min, $sec;
79 }
80
81 #######################
82 # substitute a single variable
83 sub substitute($)
84 {
85         my $var = shift;
86
87         if ($var eq "BASEDN") {
88                 return $basedn;
89         }
90
91         if ($var eq "DOMAINSID") {
92                 return $opt_domainsid;
93         }
94
95         if ($var eq "DOMAIN") {
96                 return $opt_domain;
97         }
98
99         if ($var eq "REALM") {
100                 return $opt_realm;
101         }
102
103         if ($var eq "DNSDOMAIN") {
104                 return $dnsdomain;
105         }
106
107         if ($var eq "HOSTNAME") {
108                 return $opt_hostname;
109         }
110
111         if ($var eq "NETBIOSNAME") {
112                 return $netbiosname;
113         }
114
115         if ($var eq "DNSNAME") {
116                 return $dnsname;
117         }
118
119         if ($var eq "HOSTIP") {
120                 return $opt_hostip;
121         }
122
123         if ($var eq "LDAPTIME") {
124                 return ldaptime();
125         }
126
127         if ($var eq "NEWGUID") {
128                 return randguid();
129         }
130
131         if ($var eq "NEWSCHEMAGUID") {
132                 return randguid();
133         }
134
135         if ($var eq "DOMAINGUID") {
136                 return $opt_domainguid;
137         }
138
139         if ($var eq "HOSTGUID") {
140                 return $opt_hostguid;
141         }
142
143         if ($var eq "INVOCATIONID") {
144                 return $opt_invocationid;
145         }
146
147         if ($var eq "DEFAULTSITE") {
148                 return $defaultsite;
149         }
150
151         if ($var eq "ADMINPASS") {
152                 return $opt_adminpass;
153         }
154
155         if ($var eq "RANDPASS") {
156             return randpass();
157         }
158
159         if ($var eq "JOINPASS") {
160             return $joinpass;
161         }
162
163         if ($var eq "NTTIME") {
164                 return "" . nttime();
165         }
166
167         if ($var eq "WHEEL") {
168                 return $opt_wheel;
169         }
170
171         if ($var eq "NOBODY") {
172                 return $opt_nobody;
173         }
174
175         if ($var eq "ROOT") {
176                 return $opt_root;
177         }
178
179         if ($var eq "NOGROUP") {
180                 return $opt_nogroup;
181         }
182
183         if ($var eq "USERS") {
184                 return $opt_users;
185         }
186
187         if ($var eq "USN") {
188                 my $ret = $usn;
189                 $usn = $ret + 1;
190                 return $ret;
191         }
192
193         die "ERROR: Uknown substitution variable $var\n";
194 }
195
196
197 ####################################################################
198 # substitute all variables in a string
199 sub apply_substitutions($)
200 {
201         my $data = shift;
202         my $res = "";
203         while ($data =~ /(.*?)\$\{(\w*)\}(.*)/s) {
204                 my $sub = substitute($2);
205                 $res .= "$1$sub";
206                 $data = $3;
207         }
208         $res .= $data;
209         return $res;
210 }
211
212
213 #####################################################################
214 # write a string into a file
215 sub FileSave($$)
216 {
217     my($filename) = shift;
218     my($v) = shift;
219     local(*FILE);
220     open(FILE, ">$filename") || die "can't open $filename";    
221     print FILE $v;
222     close(FILE);
223 }
224
225 #####################################################################
226 # read a file into a string
227 sub FileLoad($)
228 {
229     my($filename) = shift;
230     local(*INPUTFILE);
231     open(INPUTFILE, $filename) || return undef;
232     my($saved_delim) = $/;
233     undef $/;
234     my($data) = <INPUTFILE>;
235     close(INPUTFILE);
236     $/ = $saved_delim;
237     return $data;
238 }
239
240 #######################################################################
241 # add a foreign security principle
242 sub add_foreign($$$)
243 {
244         my $sid = shift;
245         my $desc = shift;
246         my $unixname = shift;
247         return "
248 dn: CN=$sid,CN=ForeignSecurityPrincipals,\${BASEDN}
249 objectClass: top
250 objectClass: foreignSecurityPrincipal
251 cn: $sid
252 description: $desc
253 instanceType: 4
254 whenCreated: \${LDAPTIME}
255 whenChanged: \${LDAPTIME}
256 uSNCreated: 1
257 uSNChanged: 1
258 showInAdvancedViewOnly: TRUE
259 name: $sid
260 objectGUID: \${NEWGUID}
261 objectSid: $sid
262 objectCategory: CN=Foreign-Security-Principal,CN=Schema,CN=Configuration,\${BASEDN}
263 unixName: $unixname
264
265 ";
266 }
267
268 ############################################
269 # show some help
270 sub ShowHelp()
271 {
272         print "
273 Samba4 provisioning
274
275 provision.pl [options]
276  --realm        REALM           set realm
277  --domain       DOMAIN          set domain
278  --domain-guid  GUID            set domainguid (otherwise random)
279  --domain-sid   SID             set domainsid (otherwise random)
280  --host-name    HOSTNAME        set hostname
281  --host-ip      IPADDRESS       set ipaddress
282  --host-guid    GUID            set hostguid (otherwise random)
283  --invocationid GUID            set invocationid (otherwise random)
284  --outputdir    OUTPUTDIR       set output directory
285  --adminpass    PASSWORD        choose admin password (otherwise random)
286  --root         USERNAME        choose 'root' unix username
287  --nobody       USERNAME        choose 'nobody' user
288  --nogroup      GROUPNAME       choose 'nogroup' group
289  --wheel        GROUPNAME       choose 'wheel' privileged group
290  --users        GROUPNAME       choose 'users' group
291  --quiet                        Be quiet
292
293 You must provide at least a realm and domain
294
295 ";
296         exit(1);
297 }
298
299 my $opt_help;
300
301 GetOptions(
302             'help|h|?' => \$opt_help, 
303             'realm=s' => \$opt_realm,
304             'domain=s' => \$opt_domain,
305             'domain-guid=s' => \$opt_domainguid,
306             'domain-sid=s' => \$opt_domainsid,
307             'host-name=s' => \$opt_hostname,
308             'host-ip=s' => \$opt_hostip,
309             'host-guid=s' => \$opt_hostguid,
310             'invocationid=s' => \$opt_invocationid,
311             'adminpass=s' => \$opt_adminpass,
312             'root=s' => \$opt_root,
313             'nobody=s' => \$opt_nobody,
314             'nogroup=s' => \$opt_nogroup,
315             'wheel=s' => \$opt_wheel,
316             'users=s' => \$opt_users,
317             'outputdir=s' => \$opt_outputdir,
318             'quiet' => \$opt_quiet
319             );
320
321 if ($opt_help || 
322     !$opt_realm ||
323     !$opt_domain ||
324     !$opt_hostname) {
325         ShowHelp();
326 }
327
328 $opt_realm=uc($opt_realm);
329 $opt_domain=uc($opt_domain);
330 $opt_hostname=lc($opt_hostname);
331 $netbiosname=uc($opt_hostname);
332
333 if (!$opt_hostip) {
334         my $hip = gethostbyname($opt_hostname);
335         if (defined $hip) {
336                 $opt_hostip = inet_ntoa($hip);
337         } else {
338                 $opt_hostip = "<0.0.0.0>";
339         }
340 }
341
342 $opt_quiet or print "Provisioning host '$opt_hostname'[$opt_hostip] for domain '$opt_domain' in realm '$opt_realm'\n"; 
343
344 if (!$opt_root) {
345         $opt_root = "root";
346 }
347
348 if (!$opt_nobody) {
349         if (defined getpwnam("nobody")) {
350                 $opt_nobody = "nobody";
351         }
352 }
353
354 if (!$opt_nogroup) {
355         if (defined getgrnam("nogroup")) {
356                 $opt_nogroup = "nogroup";
357         } elsif (defined getgrnam("nobody")) {
358                 $opt_nogroup = "nobody";
359         }
360 }
361
362 if (!$opt_wheel) {
363         if (defined getgrnam("wheel")) {
364                 $opt_wheel = "wheel";
365         } elsif (defined getgrnam("root")) {
366                 $opt_wheel = "root";
367         }
368 }
369
370 if (!$opt_users) {
371         if (defined getgrnam("users")) {
372                 $opt_users = "users";
373         } elsif (defined getgrnam("guest")) {
374                 $opt_users = "guest";
375         }
376 }
377
378 $opt_nobody || die "Unable to determine a user for 'nobody'\n";
379 $opt_nogroup || die "Unable to determine a group for 'nogroup'\n";
380 $opt_users || die "Unable to determine a group for 'users'\n";
381 $opt_wheel || die "Unable to determine a group for 'wheel'\n";
382
383 $opt_quiet or print "Using nobody='$opt_nobody'  nogroup='$opt_nogroup'  wheel='$opt_wheel'  users='$opt_users'\n";
384
385 $opt_quiet or print "generating ldif ...\n";
386
387 $dnsdomain = lc($opt_realm);
388 $dnsname = lc($opt_hostname).".".$dnsdomain;
389 $basedn = "DC=" . join(",DC=", split(/\./, $opt_realm));
390
391 my $data = FileLoad("setup/provision.ldif") || die "Unable to load provision.ldif\n";
392
393 $data .= add_foreign("S-1-5-7", "Anonymous", "\${NOBODY}");
394 $data .= add_foreign("S-1-1-0", "World", "\${NOGROUP}");
395 $data .= add_foreign("S-1-5-2", "Network", "\${NOGROUP}");
396 $data .= add_foreign("S-1-5-18", "System", "\${ROOT}");
397 $data .= add_foreign("S-1-5-11", "Authenticated Users", "\${USERS}");
398
399 if (!$opt_adminpass) {
400         $opt_adminpass = randpass();
401         print "chose random Administrator password '$opt_adminpass'\n";
402 }
403
404 # allow provisioning to be run from the source directory
405 $ENV{"PATH"} = "bin:../bin:" . $ENV{"PATH"};
406
407
408 my $res = apply_substitutions($data);
409
410 my $newdb = $opt_outputdir;
411
412 unless ($newdb) {
413         $newdb = "newdb." . int(rand(1000));
414 }
415
416 $opt_quiet or print "Putting new database files in $newdb\n";
417
418 unless ($opt_outputdir) {
419         mkdir($newdb, 0755) || die "Unable to create temporary directory $newdb\n";
420 }
421
422 FileSave("$newdb/sam.ldif", $res);
423
424 $opt_quiet or print "creating $newdb/sam.ldb ...\n";
425
426 system("ldbadd -H $newdb/sam.ldb $newdb/sam.ldif") == 0 || die "Failed to create sam.ldb\n";
427
428 $data = FileLoad("setup/rootdse.ldif") || die "Unable to load rootdse.ldif\n";
429
430 $res = apply_substitutions($data);
431
432 FileSave("$newdb/rootdse.ldif", $res);
433
434 $opt_quiet or print "creating $newdb/rootdse.ldb ...\n";
435
436 system("ldbadd -H $newdb/rootdse.ldb $newdb/rootdse.ldif") == 0 || die "Failed to create rootdse.ldb\n";
437
438 $data = FileLoad("setup/secrets.ldif") || die "Unable to load secrets.ldif\n";
439
440 $res = apply_substitutions($data);
441
442 FileSave("$newdb/secrets.ldif", $res);
443
444 $opt_quiet or print "creating $newdb/secrets.ldb ...\n";
445
446 system("ldbadd -H $newdb/secrets.ldb $newdb/secrets.ldif") == 0 || die "Failed to create secrets.ldb\n";
447
448 $data = FileLoad("setup/provision.zone") || die "Unable to load provision.zone\n";
449
450 $res = apply_substitutions($data);
451
452 $opt_quiet or print "saving dns zone to $newdb/$dnsdomain.zone ...\n";
453
454 FileSave("$newdb/$dnsdomain.zone", $res);
455
456 $data = FileLoad("setup/provision.smb.conf") || die "Unable to load provision.smb.conf\n";
457
458 $res = apply_substitutions($data);
459
460 $opt_quiet or print "saving smb.conf to $newdb/smb.conf ...\n";
461
462 FileSave("$newdb/smb.conf", $res);
463
464 $opt_quiet or print "creating $newdb/hklm.ldb ... \n";
465
466 system("ldbadd -H $newdb/hklm.ldb setup/hklm.ldif") == 0 || die "Failed to create hklm.ldb\n";
467
468 $opt_quiet or print "
469
470 Installation:
471 - Please move $newdb/*.ldb to the private/ directory of your
472   Samba4 installation
473 - Please move $newdb/smb.conf to the lib/ directory of your
474   Samba4 installation
475 - Please use $newdb/$dnsdomain.zone in BIND on your dns server
476 ";