ACC SHELL

Path : /usr/share/YaST2/modules/
File Upload :
Current File : //usr/share/YaST2/modules/SambaAD.pm

#! /usr/bin/perl
# File:		modules/SambaAD.pm
# Package:	Configuration of samba-client
# Summary:	Manage AD issues for samba-client
# Authors:	Jiri Suchomel <jsuchome@suse.cz>
#
# $Id: SambaAD.pm 47159 2008-04-29 13:27:37Z jsuchome $
#

package SambaAD;

use strict;
use Data::Dumper;

use YaST::YCP qw(:DATA :LOGGING);
use YaPI;

textdomain "samba-client";
our %TYPEINFO;

YaST::YCP::Import ("FileUtils");
YaST::YCP::Import ("Kerberos");
YaST::YCP::Import ("Mode");
YaST::YCP::Import ("SCR");
YaST::YCP::Import ("SambaConfig");

use constant {
    TRUE => 1,
    FALSE => 0,
};


# Active Directory server
my $ads		= "";

# Kerberos realm for AD
my $realm	= "";

# Read the list of available machine accounts in the current domain
#
# @param domain		AD domain
# @param user		user name
# @param password	password
# @return list
BEGIN{$TYPEINFO{GetMachines}= [
    "function", ["list","string"], "string", "string", "string"]}
sub GetMachines {

    my ($self, $domain, $user, $passwd) = @_;
    my @ret			= ();
    
    my $tmpdir		= SCR->Read (".target.tmpdir");
    my $conf_file	= $tmpdir."/smb.conf";
    my $krb_file	= $tmpdir."/krb5.conf";
    my $cmd		= "KRB5_CONFIG=$krb_file net ads search \"(objectclass=organizationalUnit)\" distinguishedName -s $conf_file -U '$user%". ($passwd||"") . "'";

    SCR->Write (".target.string", $krb_file, "[realms]\n\t$realm = {\n\tkdc = $ads\n\t}\n");
    SCR->Write (".target.string", $conf_file, "[global]\n\trealm = $realm\n\tsecurity = ADS\n\tworkgroup = $domain\n");

    my $result = SCR->Execute(".target.bash_output", $cmd);
    if ($result->{"exit"} eq 0) {
	foreach my $line (split (/\n/,$result->{"stdout"} || "")) {
	    if ($line =~ m/^distinguishedName:/) {
		my $dn	= $line;
		$dn	=~ s/^distinguishedName:([\t ]*)//g;
		push @ret, $dn if $dn;
	    }
	}
    }
    else {
	$cmd =~ s/(-U '[^%]*)%[^']*'/$1'/; # hide password in the log
	y2warning ("$cmd failed: ".Dumper($result));
	return undef;
    }
    return \@ret;
}

# Check if a given workgroup is a Active Directory domain and return the name
# of AD domain controler
#
# @param workgroup	the name of a workgroup to be tested
# @return string	non empty when ADS was found
BEGIN{$TYPEINFO{GetADS}=["function","string","string"]}
sub GetADS {

    my ($self, $workgroup) 	= @_;
    my $server			= "";

    y2milestone ("get ads: workgroup: $workgroup");
    
    if (Mode->config ()) {
	return "";
    }

    # use DNS for finding DC
    if (FileUtils->Exists ("/usr/bin/dig")) {

	# we have to select server from correct site - see bug #238249.
	# TODO use +short instead?
	my $out = SCR->Execute (".target.bash_output", "dig -t srv _ldap._tcp.dc._msdcs.$workgroup +noall +answer");
	y2debug ("dig output: ", Dumper ($out));
	my $tmpserver	= "";
	my @sites	= ();
	foreach my $line (split (/\n/,$out->{"stdout"} || "")) {
	    
	    y2debug ("line: $line");
	    next if $server ne "";
	    if ($line =~ m/$workgroup/ && $line !~ m/^;/) {
		$tmpserver   = "";
		$tmpserver	= (split (/[ \t]/, $line))[7] || ".";
		chop $tmpserver;
	    }
	    if ($tmpserver) {
		my $cmd	= "LANG=C net ads lookup -S $tmpserver";
		$out	= SCR->Execute (".target.bash_output", $cmd);
		if ($out->{"exit"} eq 0) {
		    foreach my $l (split (/\n/,$out->{"stdout"} || "")) {
			next if $server;
			$server = $tmpserver if ($l =~ m/Is the closest DC/ && $l =~ m/yes/);
			if ($l =~ m/Client Site Name/ && $l !~ m/Default-First-Site-Name/) {
			    my $site	= $l;
			    $site	=~ s/^Client Site Name:([\t ]*)//g;
			}
		    }
		}
	    }
	}
	y2debug ("server: $server");
	# there were no sites not "closest DC" -> take the only one result
	if (!$server && $tmpserver && not @sites) {
	    $server	= $tmpserver;
	}
	# we still don't know which server to choose, but we know list of sites
        elsif ($server eq "" && @sites) {
	    foreach my $site (@sites) {
		next if $server;
		$out	= SCR->Execute (".target.bash_output", "dig -t _ldap._tcp.$site._sites.dc._msdcs.$workgroup +noall +answer");
		y2debug ("dig output: ", Dumper ($out));
		if ($out->{"exit"} eq 0) {
		    foreach my $line (split (/\n/,$out->{"stdout"} || "")) {
			next if $server;
			if ($line =~ m/$workgroup/ && $line !~ m/^;/) {
			    $server      = (split (/[ \t]/, $line))[7] || ".";
			    chop $server;
			}
		    }
		}
	    }
	}
	y2debug ("server: $server");
    }
    
    # no success => try NETBIOS name resolution
    if ($server eq "") {

	# check for WINSSERVER in /var/lib/dhcpcd/dhcpcd-$IFNAME.info
	my $winsserver	= "";
	my $out	= SCR->Execute
	    (".target.bash_output","LANG=C ls /var/lib/dhcpcd/dhcpcd-*.info");
	if ($out->{"exit"} eq 0) {
	    foreach my $path (split (/\n/,$out->{"stdout"} || "")) {
		next if $winsserver ne "";
		my $file = SCR->Read (".target.string", $path);
		foreach my $line (split (/\n/, $file)) {
		    if ($line =~ m/^WINSSERVER=/) {
			$winsserver	= $line;
			$winsserver	=~ s/^WINSSERVER=//g;
			y2milestone ("winsserver: $winsserver");
		    }
		}
	    }
	}
	y2debug ("winsserver: $winsserver");

	# unicast query using nmblookup
	if ($winsserver ne "") {
	    $out = SCR->Execute (".target.bash_output", "LANG=C nmblookup -R -U $winsserver $workgroup#1b");
	    y2debug("nmblookup $winsserver $workgroup#1b output:",Dumper($out));
	    foreach my $line (split (/\n/,$out->{"stdout"} || "")) {
		next if $server ne "";
		next if $line =~ m/querying/;
		next if $line =~ m/failed/;
		if ($line =~ m/$workgroup/) {
		    my @parts	= split (/[ \t]/, $line);
		    $server	= $parts[0] || "";
		}
	    }
	}
    }
    if ($server eq "") {
	my $out = SCR->Execute (".target.bash_output", "LANG=C net LOOKUP DC $workgroup");
	y2debug ("net LOOKUP DC $workgroup: ", Dumper ($out));
	if ($out->{"exit"} eq 0) {
	    foreach my $line (split (/\n/,$out->{"stdout"} || "")) {
		if ($line ne "" && $server eq "") {
		    $server	= $line;
		    chomp $server;
		}
	    }
	}
    }
    if ($server ne "" &&
	SCR->Execute (".target.bash", "net ads lookup -U% -S $server") ne 0) {
	$server	= "";
    }
    y2milestone ("returning server: $server");
    return $server;
}

# Check if a given workgroup is a Active Directory domain and set the
# name of AD domain controler to global variable
#
# @param workgroup	the name of a workgroup to be tested
# @return string	non empty when ADS was found
BEGIN{$TYPEINFO{ReadADS}=["function","string","string"]}
sub ReadADS {

    my ($self, $workgroup) 	= @_;
    $ads			= $self->GetADS ($workgroup);
    return $ads;
}

# return the value of $ads
BEGIN{$TYPEINFO{ADS}=["function","string"]}
sub ADS {
    return $ads;
}

# Set the value of $ads
# return true if the new value is different from the previous one
BEGIN{$TYPEINFO{SetADS}=["function","boolean", "string"]}
sub SetADS {

    my ($self, $new_ads) 	= @_;
    if ($new_ads eq $ads) {
	return FALSE;
    }
    $ads = $new_ads;
    return TRUE;
}

    

# Get AD Domain name and return the name of work group ("Pre-Win2k Domain")
# @param domain	the domain user entered
# @param server AD server (used for querying)
# @return	workgroup (returns domain if anything fails)
BEGIN{$TYPEINFO{ADDomain2Workgroup}=["function","string","string", "string"]}
sub ADDomain2Workgroup {

    my ($self, $domain, $server) = @_;


    return "" if $server eq "";

    my $out	= SCR->Execute (".target.bash_output", "net ads lookup -S $server | grep 'Pre-Win2k Domain' | cut -f 2");

    y2debug ("net ads lookup -S $server: ", Dumper ($out));
    if ($out->{"exit"} ne 0 || $out->{"stdout"} eq "") {
	return $domain;
    }
    my $workgroup	= $out->{"stdout"};
    chomp $workgroup;
    y2milestone ("workgroup: $workgroup");
    return $workgroup;
}

# Return the value of AD work group ("Pre-Win2k Domain") for the current ADS
# @param domain	the domain user entered
# @return	workgroup (returns domain if anything fails)
BEGIN{$TYPEINFO{GetWorkgroup}=["function","string","string"]}
sub GetWorkgroup {

    my ($self, $domain)	= @_;
    return $self->ADDomain2Workgroup ($domain, $ads);
}


# Get the Kerberos realm for given AD DC
# @server	AD domain controler
# @return	the realm for Kerberos configuration; empty if none is available
BEGIN{$TYPEINFO{GetRealm}=["function","string", "string"]}
sub GetRealm {

    my ($self, $server) = @_;

    return "" if $server eq "";

    my $out	= SCR->Execute (".target.bash_output", "net ads info -S $server | grep Realm | cut -f 2 -d ' '");
    
    y2debug ("net ads info -S $server: ", Dumper ($out));

    if ($out->{"exit"} ne 0 || $out->{"stdout"} eq "") {
	return "";
    }
    my $ret	= $out->{"stdout"};
    chomp $ret;
    y2milestone ("realm: $ret");
    return $ret;
}

# Read the Kerberos realm for current AD DC and set it to global variable
# @return       the realm for Kerberos configuration
BEGIN{$TYPEINFO{ReadRealm}=["function","string"]}
sub ReadRealm {

    my $self	= shift;
    $realm 	= $self->GetRealm ($ads);
    return $realm;
}

# return the value of $realm
BEGIN{$TYPEINFO{Realm}=["function","string"]}
sub Realm {
    return $realm;
}

# Change samba configuration file (/etc/samba/smb.conf)
#
# @param status a new status
BEGIN{$TYPEINFO{AdjustSambaConfig}=["function","void","boolean"]}
sub AdjustSambaConfig {
    my ($self, $status) = @_;

    my $workgroup	= SambaConfig->GlobalGetStr ("workgroup", "");
    # remove special AD values if AD is not used
    my $remove	= (($ads || "") eq "");
    SambaConfig->GlobalSetMap({
	"security"			=> $remove ? "domain" : "ADS",
	"realm"				=> $remove ? undef : $realm,
	"template homedir"		=> $remove ? undef : "/home/%D/%U",
	"winbind refresh tickets"	=> $remove ? undef : "yes"
    });
    SambaConfig->WinbindGlobalSetMap({
	"krb5_auth"			=> $remove ? undef : "yes",
	"krb5_ccache_type"		=> $remove ? undef : "FILE"
    });
    if ($status) {
	if (SambaConfig->GlobalGetTruth ("domain logons", 0)) {
	    SambaConfig->GlobalSetTruth ("domain logons", 0)
	}
	if (SambaConfig->GlobalGetTruth ("domain master", 0)) {
	    SambaConfig->GlobalSetStr ("domain master", "Auto")
	}
    }
}

# Change Kerberos configuration (for AD). Uses current (previously read)
# value of ADS and Kerbers realm
#
# @param on the status of the winbind to be configured (true=enabled, false=disabled)
BEGIN{$TYPEINFO{AdjustKerberos}=["function","boolean","boolean"]}
sub AdjustKerberos {

    my ($self, $on) = @_;
    if (!$on || ($ads || "") eq "") { 
	# check if it is AD domain
	# when disabling, we do not have to change this configuration
	return TRUE;
    }
    my $domain	= "\L$realm";

    my $prev	= Progress->set (FALSE);
    Kerberos->Read ();
    Kerberos->Import ({
	"pam_login"		=> {
	    "use_kerberos"	=> YaST::YCP::Boolean (0)
	},
	"kerberos_client"	=> {
	    "default_realm"	=> $realm, 
	    "default_domain"	=> $domain,
	    "kdc_server"	=> $ads,
	    "trusted_servers"	=> $ads
	}
    });
    Kerberos->modified (TRUE);
    Kerberos->Write ();
    Progress->set ($prev);

    return TRUE;
}

42;

ACC SHELL 2018