r5048: made the provision.pl script much less error prone (you don't need to
[sfrench/samba-autobuild/.git] / source4 / script / provision.pl
index e71c065328f77604bf3e85153207d02bbb6dc0b6..1730f0859a7d2c27de07b7cf9f084f01063c7a4e 100755 (executable)
@@ -1,14 +1,25 @@
 #!/usr/bin/perl -w
 
 use strict;
+use Socket;
+use Getopt::Long;
 
-my $hostname = `hostname`;
-chomp $hostname;
-my $realm = "bludom.tridgell.net";
-my $domain = "BLUDOM";
-my $dnsname = "$hostname.$realm";
-
-my $basedn = "DC=" . join(",DN=", split(/\./, $realm));
+my $opt_hostname = `hostname`;
+chomp $opt_hostname;
+my $opt_hostip;
+my $opt_realm;
+my $opt_domain;
+my $opt_adminpass;
+my $opt_nobody;
+my $opt_nogroup;
+my $opt_wheel;
+my $opt_users;
+my $dnsdomain;
+my $netbiosname;
+my $dnsname;
+my $basedn;
+my $defaultsite = "Default-First-Site-Name";
+my $usn = 1;
 
 # return the current NTTIME as an integer
 sub nttime()
@@ -27,16 +38,35 @@ sub randguid()
        my $r3 = int(rand(2**16));
        my $r4 = int(rand(2**16));
        my $r5 = int(rand(2**32));
-       return sprintf("%08x-%04x-%04x-%04x-%08x", $r1, $r2, $r3, $r4, $r5);
+       my $r6 = int(rand(2**16));
+       return sprintf("%08x-%04x-%04x-%04x-%08x%04x", $r1, $r2, $r3, $r4, $r5, $r6);
 }
 
+my $opt_domainguid = randguid();
+my $opt_hostguid = randguid();
+my $opt_invocationid = randguid();
+
 sub randsid()
 {
        return sprintf("S-1-5-21-%d-%d-%d", 
                       int(rand(10**8)), int(rand(10**8)), int(rand(10**8)));
 }
 
-my $domainsid = randsid();
+my $opt_domainsid = randsid();
+
+# generate a random password. Poor algorithm :(
+sub randpass()
+{
+       my $pass = "";
+       my $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ%\$!~";
+       for (my $i=0;$i<8;$i++) {
+               my $c = int(rand(length($chars)));
+               $pass .= substr($chars, $c, 1);
+       }
+       return $pass;
+}
+
+my $joinpass = randpass();
 
 sub ldaptime()
 {
@@ -56,21 +86,37 @@ sub substitute($)
        }
 
        if ($var eq "DOMAINSID") {
-               return $domainsid;
+               return $opt_domainsid;
        }
 
        if ($var eq "DOMAIN") {
-               return $domain;
+               return $opt_domain;
+       }
+
+       if ($var eq "REALM") {
+               return $opt_realm;
+       }
+
+       if ($var eq "DNSDOMAIN") {
+               return $dnsdomain;
        }
 
        if ($var eq "HOSTNAME") {
-               return $hostname;
+               return $opt_hostname;
+       }
+
+       if ($var eq "NETBIOSNAME") {
+               return $netbiosname;
        }
 
        if ($var eq "DNSNAME") {
                return $dnsname;
        }
 
+       if ($var eq "HOSTIP") {
+               return $opt_hostip;
+       }
+
        if ($var eq "LDAPTIME") {
                return ldaptime();
        }
@@ -79,13 +125,96 @@ sub substitute($)
                return randguid();
        }
 
+       if ($var eq "NEWSCHEMAGUID") {
+               return randguid();
+       }
+
+       if ($var eq "DOMAINGUID") {
+               return $opt_domainguid;
+       }
+
+       if ($var eq "HOSTGUID") {
+               return $opt_hostguid;
+       }
+
+       if ($var eq "INVOCATIONID") {
+               return $opt_invocationid;
+       }
+
+       if ($var eq "DEFAULTSITE") {
+               return $defaultsite;
+       }
+
+       if ($var eq "ADMINPASS") {
+               return $opt_adminpass;
+       }
+
+       if ($var eq "RANDPASS") {
+           return randpass();
+       }
+
+       if ($var eq "JOINPASS") {
+           return $joinpass;
+       }
+
        if ($var eq "NTTIME") {
                return "" . nttime();
        }
 
+       if ($var eq "WHEEL") {
+               return $opt_wheel;
+       }
+
+       if ($var eq "NOBODY") {
+               return $opt_nobody;
+       }
+
+       if ($var eq "NOGROUP") {
+               return $opt_nogroup;
+       }
+
+       if ($var eq "USERS") {
+               return $opt_users;
+       }
+
+       if ($var eq "USN") {
+               my $ret = $usn;
+               $usn = $ret + 1;
+               return $ret;
+       }
+
        die "ERROR: Uknown substitution variable $var\n";
 }
 
+
+####################################################################
+# substitute all variables in a string
+sub apply_substitutions($)
+{
+       my $data = shift;
+       my $res = "";
+       while ($data =~ /(.*?)\$\{(\w*)\}(.*)/s) {
+               my $sub = substitute($2);
+               $res .= "$1$sub";
+               $data = $3;
+       }
+       $res .= $data;
+       return $res;
+}
+
+
+#####################################################################
+# write a string into a file
+sub FileSave($$)
+{
+    my($filename) = shift;
+    my($v) = shift;
+    local(*FILE);
+    open(FILE, ">$filename") || die "can't open $filename";    
+    print FILE $v;
+    close(FILE);
+}
+
 #####################################################################
 # read a file into a string
 sub FileLoad($)
@@ -101,16 +230,214 @@ sub FileLoad($)
     return $data;
 }
 
+#######################################################################
+# add a foreign security principle
+sub add_foreign($$$)
+{
+       my $sid = shift;
+       my $desc = shift;
+       my $unixname = shift;
+       return "
+dn: CN=$sid,CN=ForeignSecurityPrincipals,\${BASEDN}
+objectClass: top
+objectClass: foreignSecurityPrincipal
+cn: $sid
+description: $desc
+instanceType: 4
+whenCreated: \${LDAPTIME}
+whenChanged: \${LDAPTIME}
+uSNCreated: 1
+uSNChanged: 1
+showInAdvancedViewOnly: TRUE
+name: $sid
+objectGUID: \${NEWGUID}
+objectSid: $sid
+objectCategory: CN=Foreign-Security-Principal,CN=Schema,CN=Configuration,\${BASEDN}
+unixName: $unixname
+
+";
+}
+
+############################################
+# show some help
+sub ShowHelp()
+{
+       print "
+Samba4 provisioning
+
+provision.pl [options]
+ --realm       REALM           set realm
+ --domain      DOMAIN          set domain
+ --domain-guid GUID            set domainguid (otherwise random)
+ --domain-sid  SID             set domainsid (otherwise random)
+ --host-name   HOSTNAME        set hostname
+ --host-ip     IPADDRESS       set ipaddress
+ --host-guid   GUID            set hostguid (otherwise random)
+ --invocationid        GUID            set invocationid (otherwise random)
+ --adminpass   PASSWORD        choose admin password (otherwise random)
+ --nobody      USERNAME        choose 'nobody' user
+ --nogroup     GROUPNAME       choose 'nogroup' group
+ --wheel       GROUPNAME       choose 'wheel' privileged group
+ --users       GROUPNAME       choose 'users' group
+
+You must provide at least a realm and domain
+
+";
+       exit(1);
+}
+
+my $opt_help;
+
+GetOptions(
+           'help|h|?' => \$opt_help, 
+           'realm=s' => \$opt_realm,
+           'domain=s' => \$opt_domain,
+           'domain-guid=s' => \$opt_domainguid,
+           'domain-sid=s' => \$opt_domainsid,
+           'host-name=s' => \$opt_hostname,
+           'host-ip=s' => \$opt_hostip,
+           'host-guid=s' => \$opt_hostguid,
+           'invocationid=s' => \$opt_invocationid,
+           'adminpass=s' => \$opt_adminpass,
+           'nobody=s' => \$opt_nobody,
+           'nogroup=s' => \$opt_nogroup,
+           'wheel=s' => \$opt_wheel,
+           'users=s' => \$opt_users,
+           );
+
+if ($opt_help || 
+    !$opt_realm ||
+    !$opt_domain ||
+    !$opt_hostname) {
+       ShowHelp();
+}
+
+$opt_realm=uc($opt_realm);
+$opt_domain=uc($opt_domain);
+$opt_hostname=lc($opt_hostname);
+$netbiosname=uc($opt_hostname);
+
+if (!$opt_hostip) {
+       my $hip = gethostbyname($opt_hostname);
+       if (defined $hip) {
+               $opt_hostip = inet_ntoa($hip);
+       } else {
+               $opt_hostip = "<0.0.0.0>";
+       }
+}
+
+print "Provisioning host '$opt_hostname'[$opt_hostip] for domain '$opt_domain' in realm '$opt_realm'\n";
+
+if (!$opt_nobody) {
+       if (defined getpwnam("nobody")) {
+               $opt_nobody = "nobody";
+       }
+}
+
+if (!$opt_nogroup) {
+       if (defined getgrnam("nogroup")) {
+               $opt_nogroup = "nogroup";
+       } elsif (defined getgrnam("nobody")) {
+               $opt_nogroup = "nobody";
+       }
+}
+
+if (!$opt_wheel) {
+       if (defined getgrnam("wheel")) {
+               $opt_wheel = "wheel";
+       } elsif (defined getgrnam("root")) {
+               $opt_wheel = "root";
+       }
+}
+
+if (!$opt_users) {
+       if (defined getgrnam("users")) {
+               $opt_users = "users";
+       }
+}
+
+$opt_nobody || die "Unable to determine a user for 'nobody'\n";
+$opt_nogroup || die "Unable to determine a group for 'nogroup'\n";
+$opt_users || die "Unable to determine a group for 'user'\n";
+$opt_wheel || die "Unable to determine a group for 'wheel'\n";
+
+print "Using nobody='$opt_nobody'  nogroup='$opt_nogroup'  wheel='$opt_wheel'  users='$opt_users'\n";
+
+print "generating ldif ...\n";
+
+$dnsdomain = lc($opt_realm);
+$dnsname = lc($opt_hostname).".".$dnsdomain;
+$basedn = "DC=" . join(",DC=", split(/\./, $opt_realm));
 
 my $data = FileLoad("provision.ldif") || die "Unable to load provision.ldif\n";
 
-my $res = "";
+$data .= add_foreign("S-1-5-7", "Anonymous", "\${NOBODY}");
+$data .= add_foreign("S-1-1-0", "World", "\${NOGROUP}");
+$data .= add_foreign("S-1-5-2", "Network", "\${NOGROUP}");
+$data .= add_foreign("S-1-5-18", "System", "root");
+$data .= add_foreign("S-1-5-11", "Authenticated Users", "\${USERS}");
 
-while ($data =~ /(.*?)\$\{(\w*)\}(.*)/s) {
-       my $sub = substitute($2);
-       $res .= "$1$sub";
-       $data = $3;
+if (!$opt_adminpass) {
+       $opt_adminpass = randpass();
+       print "chose random Administrator password '$opt_adminpass'\n";
 }
 
-print $res . $data;
+# allow provisioning to be run from the source directory
+$ENV{"PATH"} .= ":bin";
+
+
+my $res = apply_substitutions($data);
+
+my $newdb = "newdb." . int(rand(1000));
+
+print "Putting new database files in $newdb\n";
+
+mkdir($newdb) || die "Unable to create temporary directory $newdb\n";
+
+FileSave("$newdb/sam.ldif", $res);
+
+print "creating $newdb/sam.ldb ...\n";
+
+system("ldbadd -H $newdb/sam.ldb $newdb/sam.ldif") == 0 || die "Failed to create sam.ldb\n";
+
+$data = FileLoad("rootdse.ldif") || die "Unable to load rootdse.ldif\n";
+
+$res = apply_substitutions($data);
+
+FileSave("$newdb/rootdse.ldif", $res);
+
+print "creating $newdb/rootdse.ldb ...\n";
+
+system("ldbadd -H $newdb/rootdse.ldb $newdb/rootdse.ldif") == 0 || die "Failed to create rootdse.ldb\n";
+
+$data = FileLoad("secrets.ldif") || die "Unable to load secrets.ldif\n";
+
+$res = apply_substitutions($data);
+
+FileSave("$newdb/secrets.ldif", $res);
+
+print "creating $newdb/secrets.ldb ...\n";
+
+system("ldbadd -H $newdb/secrets.ldb $newdb/secrets.ldif") == 0 || die "Failed to create secrets.ldb\n";
+
+$data = FileLoad("provision.zone") || die "Unable to load provision.zone\n";
+
+$res = apply_substitutions($data);
+
+print "saving dns zone to $newdb/dns.zone ...\n";
+
+FileSave("$dnsdomain.zone", $res);
+
+print "creating $newdb/hklm.ldb ... \n";
+
+system("ldbadd -H $newdb/hklm.ldb hklm.ldif") == 0 || die "Failed to create hklm.ldb\n";
+
+print "
+
+Installation:
+- Please move $newdb/*.ldb to the private/ directory of your
+  Samba4 installation
+- Please use $newdb/dnsdomain.zone in BIND on your dns server
+";
+