#!/usr/bin/perl -w
use strict;
use FileHandle;

# Version of this installation script!
my $installerVersion=2.3;

#--------------------------------------------------------------------------
#                             Documentation
#--------------------------------------------------------------------------
sub help {
print '
#--------------------------------------------------------------------------
#
#                     NWZ SuSe-Linux installation script
#
# Author: O. Rubner (2015-2020).
# Version '.$installerVersion.
'#
# Call: nwz_suse [-h -sv]
#
# Description:
#
# Prepare an OpenSuSE-Linux system for authentification with the NWZ
# domain.
#
# Install an Open-SuSE-Linux from the scratch and run this script afterwards.
# Then follow the instructions.
#
# -------------------------------------------------------------------------
# Options:
#       -h      print this help information
#       -sv     print SuSE-Versions for which this script is tested
#--------------------------------------------------------------------------
';
}

#--------------------------------------------------------------------------
#                  Global variables and command line options
#--------------------------------------------------------------------------

my @SuseVersions = ("15.0","15.1","15.2","Tumbleweed");

my @modules= ("openldap2", "krb5", "krb5-client", "samba", "pam_mount", "sssd", "sssd-tools", "sssd-ad");

my @cfgfiles = ("/etc/krb5.conf", "/etc/openldap/ldap.conf","/etc/sssd/sssd.conf", "/etc/samba/smb.conf",
                "/etc/nsswitch.conf", "/etc/security/pam_mount.conf.xml");


my $version;

my $YUSER;
my $HOSTNAME;

#--- modes switched by commandline options -------

#--------------------------------------------------------------------------
#                             Main program
#--------------------------------------------------------------------------
{
  my $val;

  print "\n    NWZ install for OpenSuSE-Linux\n";
  print "          O. Rubner (2015-2020)\n\n";
  print "               Version ".$installerVersion."\n\n";

  # check for command line options
  &checkOptions(@ARGV);

  # check for correct Suse version
  &checkSuseVersion();

  # check for root
  if ($ENV{"USER"} ne "root") {
    print "Please login as root!\n";
    exit(1);
  }

  # -------------------------
  #    Ask for preliminary work
  # -------------------------
  $HOSTNAME=`hostname`;
  chomp $HOSTNAME;

  print "Has the computer \"$HOSTNAME\" been entered in \n";
  print "the active directory (Pre-Staging)? (y/N) \n";
  $val=<>;
  chomp $val;
  print"\n";
  exit if (lc $val ne "y");

  # -------------------------
  #    do everything
  # -------------------------

  # install additional modules
  &installmodules();

  # copy nwz-configuration files (krb5, ldap, samba, sssd)
  &copyfiles();

  # configure nsswitch
  &nssconfig();

  # configure nscd
  &nscdconfig();

  # configure pam modules
  &pamconfig();

  # configure pam_mount
  print "\n Do you want to mount the home directories from your own server? (y/N)\n";
  $val=<>;
  chomp $val;
  print"\n";
  if (lc $val eq "y") {

    print "Enter your server name: ";
    my $HOMESERVER = <>;
    chomp $HOMESERVER;

    &pam_mountconfig($HOMESERVER);
  }

  # join domain and test
  &setupfinal();


	# clear sssd cache
	`systemctl stop sssd`;
	`sss_cache -E`;
	`systemctl start sssd`;

  # Finished
  print "\n  Installation finished!\n";
  print "\n  Please restart system!\n";

}


#--------------------------------------------------------------------------
#                             Subroutines
#--------------------------------------------------------------------------

# ---------------------------------------------------------------------
# Check for correct OS version
# ---------------------------------------------------------------------
sub checkSuseVersion {

  my $vers = join "|",@SuseVersions;
  $vers = "(".$vers.")";

  my $osname = `grep "\^NAME=" /etc/os-release | awk -F"=" '{print \$2}'`;
  $osname=~s/^\s*|\s+$//g;

  my $distro = `grep "\^ID=" /etc/os-release | awk -F"=" '{print \$2}'`;
  $distro=~s/^\s*|\s+$//g;

  $version = `grep "VERSION_ID=" /etc/os-release | awk -F "\\"" '{print \$2}'`;
  my $retval = `grep "VERSION_ID=" /etc/os-release | awk -F "\\"" '{print \$2}' | grep -E "$vers"`;

  # Tumbleweed
  if ($retval eq "") {
    $retval = `grep "Tumbleweed" /etc/os-release`;
  }

  print "Distribution: $osname\n";
  print "Version     : $version\n";

  if (index($distro,"opensuse")==-1 || $retval eq "") {
    print "This script works only for OpenSUSE Versions:\n";
    print "       $vers \n\n";
    exit(1);
  }
}

# ---------------------------------------------------------------------
# Check for and install additional modules
# ---------------------------------------------------------------------
sub installmodules {

  my $mod;

  # If Suse 13.2 => install gdm
  if ($version == "13.2") {
      push(@modules,"gdm");
  }

  # install needed modules
  my $count = 1;
  foreach $mod (@modules) {
      print "---------------------------------\n";
      print "Installing $mod ... ($count/".($#modules+1).")\n";
      `zypper --non-interactive -q install $mod`;
      $count++;
  }

  print "---------------------------------\n";
}

# ---------------------------------------------------------------------
# Configure nsswitch
# ---------------------------------------------------------------------
sub nssconfig {

  # update /etc/nsswitch
  &replaceline("passwd:","^[^#].+","passwd:         compat sss","/etc/nsswitch.conf");
  &replaceline("group:","^[^#].+","group:          compat sss","/etc/nsswitch.conf");
}

# ---------------------------------------------------------------------
# Configure pam
# ---------------------------------------------------------------------
sub pamconfig {

  # ------------------------------------------------
  # Use pam-config
  # ------------------------------------------------
  `pam-config -a --sss`;
  `pam-config -a --mkhomedir`;
  `pam-config --service common-session -a --mount`;
  `pam-config --service common-auth -a --mount`;


}

# ---------------------------------------------------------------------
# Configure pam_mount.conf.xml for home directories
# ---------------------------------------------------------------------
sub pam_mountconfig {

  my $homeServer = $_[0];
  my $file = new FileHandle;


# avoid double entries if there is already one

  if (! `grep nwz /etc/security/pam_mount.conf.xml`) {

    # remove end tag
    &replaceline("<\\/pam_mount>",".+","","/etc/security/pam_mount.conf.xml");

    open($file,">>/etc/security/pam_mount.conf.xml");
    print $file "\n";

    print $file '<volume user="*" fstype="cifs" server="'.$homeServer.'" path="%(USER)" mountpoint="~" options="dir_mode=0700,file_mode=0600,serverino,nobrl,workgroup=NWZ" />';

    print $file "\n";

    print $file '<umount> umount %(MNTPT) </umount>';

    # add end tag
    print $file "\n\n</pam_mount>\n";

    close $file;
  }

}

# ---------------------------------------------------------------------
# Configure nscd (=switch it off!)
# ---------------------------------------------------------------------
sub nscdconfig {

  if (-e "/etc/nscd.conf") {
    `systemctl disable nscd`;
    `systemctl stop nscd`;
  }

}

# ---------------------------------------------------------------------
# Join domain, start sssd and test
# ---------------------------------------------------------------------
sub setupfinal {

  # Just to be sure: stop the nsc-demon
  `systemctl stop nscd`;

  print "Administrator username (nwz-yaccount): ";
  $YUSER = <>;
  chomp $YUSER;

  if (system("net ads join -U $YUSER 2>/dev/null")!=0) {
      print "\nCheck if the computer ".$HOSTNAME." \n";
      print "   1) has been entered correctly in Pre-Staging (lower/upper case!).\n";
      print "   2) has an admin group in Pre-Staging to which ".$YUSER." belongs.\n\n";
      print "Else ask your local NWZ-guru!\n\n";
      exit(1);
  }

  # start and enable sssd
  `systemctl restart sssd`;
  `systemctl enable sssd`;


}

# ---------------------------------------------------------------------
# Backup configuration files and copy nwz files
# ---------------------------------------------------------------------
sub copyfiles {

  my ($file, $backupfile);
  my $retval;

  print "Copying configuration files ...\n";

  foreach $file (@cfgfiles) {

    next if (! -e $file);

    $backupfile = $file.".bak";

    if (-e $backupfile) {
      print "$backupfile already exists.\n";
      print "Overwrite? (y/N) ";
      $retval = <>;
      chomp $retval;
      print"\n";

      `cp $file $backupfile` if (lc($retval) eq "y");
    }
    else {
      `cp $file $backupfile`;
    }
  }

  &createsamba;
  &createsss;

}

#--------------------------------------------------------------------------
# create /etc/krb5.conf
#--------------------------------------------------------------------------
sub createkrb5 {

  `echo "[libdefaults]
        default_realm = NWZ.WWU.DE
        dns_lookup_kdc = true
        clockskew = 300
        forwardable = true
        rdns = false
        ignore_acceptor_hostname = true
        renew_lifetime = 365d
        canonicalize = true

[realms]
        NWZ.WWU.DE = {
                kdc = nwz.wwu.de
                default_domain = nwz.wwu.de
                admin_server = nwz.wwu.de
        }

[domain_realm]
        .nwz.wwu.de = NWZ.WWU.DE

[logging]
    kdc = FILE:/var/log/krb5/krb5kdc.log
    admin_server = FILE:/var/log/krb5/kadmind.log
    default = SYSLOG:NOTICE:DAEMON

" > /etc/krb5.conf`;

}

#--------------------------------------------------------------------------
# create /etc/openldap/ldap.conf
#--------------------------------------------------------------------------
sub createldap {

  `echo "#
# LDAP Defaults
#

# See ldap.conf(5) for details
# This file should be world readable but not world writable.

#BASE   dc=example,dc=com
#URI    ldap://ldap.example.com ldap://ldap-master.example.com:666

URI     ldap://nwz.wwu.de
BASE    dc=nwz,dc=wwu,dc=de

#SIZELIMIT      12
#TIMELIMIT      15
#DEREF          never

" > /etc/openldap/ldap.conf`;

}

#--------------------------------------------------------------------------
# create /etc/samba/smb.conf
#--------------------------------------------------------------------------
sub createsamba {

  `echo "[global]
        workgroup = NWZ
        kerberos method = system keytab
        passdb backend = tdbsam
        printing = cups
        printcap name = cups
        printcap cache time = 750
        cups options = raw
        map to guest = Bad User
        usershare allow guests = No
        security = ads
        wins support = No
        realm = NWZ.WWU.DE
        wins server = NWZ.UNI-MUENSTER.DE

#[homes]
#       comment = Home Directories
#       valid users = %S, %D%w%S
#       browseable = No
#       read only = No
#       inherit acls = Yes
#[profiles]
#       comment = Network Profiles Service
#       path = %H
#       read only = No
#       store dos attributes = Yes
#       create mask = 0600
#       directory mask = 0700
#[users]
#       comment = All users
#       path = /home
#       read only = No
#       inherit acls = Yes
#       veto files = /aquota.user/groups/shares/
#[groups]
#       comment = All groups
#       path = /home/groups
#       read only = No
#       inherit acls = Yes
#[printers]
#       comment = All Printers
#       path = /var/tmp
#       printable = Yes
#       create mask = 0600
#       browseable = No
#[print$]
#       comment = Printer Drivers
#       path = /var/lib/samba/drivers
#       write list = \@ntadmin root
#       force group = ntadmin
#       create mask = 0664
#       directory mask = 0775

" > /etc/samba/smb.conf`;

}

#--------------------------------------------------------------------------
# create /etc/sssd/sssd.conf
#--------------------------------------------------------------------------
sub createsss {

  `echo "[sssd]
config_file_version = 2
#debug_level = 0x03FF
services = nss, pam
domains = NWZ

[nss]
#debug_level = 0xFFFF
filter_users = root
filter_groups = root

[pam]
#debug_level = 0xFFFF
pam_verbosity = 2

[domain/NWZ]
#debug_level = 0x03FF
ad_server = nwz.wwu.de
ad_domain = nwz.wwu.de

id_provider = ad
auth_provider = ad
chpass_provider = ad
dyndns_update = false

krb5_renewable_lifetime = 180d
krb5_renew_interval = 300

cache_credentials = true

ldap_schema = AD
ldap_sasl_canonicalize = true

enumerate = false
subdomain_enumerate = false

ldap_search_base = DC=nwz,DC=wwu,DC=de
ldap_user_search_base = OU=Fachbereiche,OU=Domain Members,DC=nwz,DC=wwu,DC=de
ldap_group_search_Base = OU=Fachbereiche,OU=Domain Members,DC=nwz,DC=wwu,DC=de
ldap_id_mapping = false
ldap_user_object_class = user
ldap_group_object_class = group
ldap_user_name = sAMAccountName
ldap_user_home_directory = unixHomeDirectory
ldap_user_principal = userPrincipalName

" > /etc/sssd/sssd.conf`;

# sssd wont start with the wrong permissions
`chmod go-r /etc/sssd/sssd.conf`;

}

#--------------------------------------------------------------------------
# Find line(s) containing $key in $filename and replace the string $old by
# $new. $filename must contain the complete path.
# $key and $old can be any regular expression.
#
# If $old==".+" the complete line is replaced by $new.
#
# Call:
#       replaceline($key,$old,$new,$filename)
#
#--------------------------------------------------------------------------
sub replaceline {

  my $key=$_[0];
  my $old=$_[1];
  my $new=$_[2];
  my $filename=$_[3];

  my $filein = new FileHandle;
  my $fileContent;
  open($filein,"<$filename") || die "No file $filename found!\n";

  while (<$filein>) {
      if ($_=~/$key/) {
          $_ =~ s/$old/$new/g;
      }
      $fileContent .= $_;
  }

  close($filein);
  open($filein,">$filename") || die "Could not open $filename for writing!\n";
  print $filein $fileContent;
}

#--------------------------------------------------------------------------
# Find line(s) containing $key in $file and insert the string $newline either
# before (before==1) or after (before==0) this line.
# $file must contain the complete path.
# $key can be any regular expression.
# $newline must not contain a newline character (it is added automatically at the end).
#
# Call:
#       insertline($key,$newline,$file,before)
#
#--------------------------------------------------------------------------
sub insertline {

  my $key=$_[0];
  my $newline=$_[1];
  my $filename=$_[2];
  my $before=$_[3];

  my $filein = new FileHandle;
  my $fileContent;
  open($filein,"<$filename") || die "No file $filename found!\n";

  while (<$filein>) {
      $fileContent .= $_ if ($before==0);
      if ($_=~/$key/) {
          $fileContent .= "$newline\n";
      }
      $fileContent .= $_ if ($before==1);
  }

  close($filein);
  open($filein,">$filename") || die "Could not open $filename for writing!\n";
  print $filein $fileContent;
}

#--------------------------------------------------------------------------
# check command line options
#
#--------------------------------------------------------------------------
sub checkOptions {

  my @options = split(/-/,join(" ",@ARGV));
  my $opt;

  # check for options
  foreach $opt (@options) {

    # display help
    if ($opt eq "h" || $opt eq "help") {
      &help;
      exit;
    }

    # display correct SuSE versions
    if ($opt eq "sv") {
      my $vers = join " | ",@SuseVersions;
      print "Tested OpenSuSE Versions:\n   $vers \n\n";
      exit;
    }


  }

  undef(@ARGV);
}

