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