--- /dev/null
+#!/usr/bin/perl -w
+use strict;
+package smbldap_tools;
+use Net::LDAP;
+use Crypt::SmbHash;
+
+
+# $Id: smbldap_tools.pm,v 1.62 2005/05/27 14:28:47 jtournier Exp $
+#
+# This code was developped by IDEALX (http://IDEALX.org/) and
+# contributors (their names can be found in the CONTRIBUTORS file).
+#
+# Copyright (C) 2001-2002 IDEALX
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+# USA.
+
+
+# ugly funcs using global variables and spawning openldap clients
+
+my $smbldap_conf;
+if (-e "/etc/smbldap-tools/smbldap.conf") {
+ $smbldap_conf="/etc/smbldap-tools/smbldap.conf";
+} else {
+ $smbldap_conf="/etc/opt/IDEALX/smbldap-tools/smbldap.conf";
+}
+my $smbldap_bind_conf;
+if (-e "/etc/smbldap-tools/smbldap_bind.conf") {
+ $smbldap_bind_conf="/etc/smbldap-tools/smbldap_bind.conf";
+} else {
+ $smbldap_bind_conf="/etc/opt/IDEALX/smbldap-tools/smbldap_bind.conf";
+}
+my $samba_conf;
+if (-e "/etc/samba/smb.conf") {
+ $samba_conf="/etc/samba/smb.conf";
+} else {
+ $samba_conf="/usr/local/samba/lib/smb.conf";
+}
+
+use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
+use Exporter;
+$VERSION = 1.00;
+
+@ISA = qw(Exporter);
+use vars qw(%config $ldap);
+
+@EXPORT = qw(
+ get_user_dn
+ get_group_dn
+ is_group_member
+ is_samba_user
+ is_unix_user
+ is_user_valid
+ does_sid_exist
+ get_dn_from_line
+ add_posix_machine
+ add_samba_machine
+ add_samba_machine_smbpasswd
+ group_add_user
+ add_grouplist_user
+ disable_user
+ delete_user
+ group_add
+ group_del
+ get_homedir
+ read_user
+ read_user_entry
+ read_group
+ read_group_entry
+ read_group_entry_gid
+ find_groups_of
+ parse_group
+ group_remove_member
+ group_get_members
+ do_ldapadd
+ do_ldapmodify
+ get_user_dn2
+ connect_ldap_master
+ connect_ldap_slave
+ group_type_by_name
+ subst_configvar
+ read_config
+ read_parameter
+ subst_user
+ split_arg_comma
+ list_union
+ list_minus
+ get_next_id
+ print_banner
+ getDomainName
+ getLocalSID
+ %config
+ );
+
+sub print_banner
+ {
+ print STDERR "(c) Jerome Tournier - IDEALX 2004 (http://www.idealx.com)- Licensed under the GPL\n"
+ unless $config{no_banner};
+ }
+
+sub read_parameter
+ {
+ my $line=shift;
+ ## check for a param = value
+ if ($_=~/=/) {
+ my ($param,$val);
+ if ($_=~/\s*.*?\s*=\s*".*"/) {
+ ($param,$val) = /\s*(.*?)\s*=\s*"(.*)"/;
+ } elsif ($_=~/\s*.*?\s*=\s*'.*'/) {
+ ($param,$val) = /\s*(.*?)\s*=\s*'(.*)'/;
+ } else {
+ ($param,$val) = /\s*(.*?)\s*=\s*(.*)/;
+ }
+ return ($param,$val);
+ }
+ }
+
+sub subst_configvar
+ {
+ my $value = shift;
+ my $vars = shift;
+
+ $value =~ s/\$\{([^}]+)\}/$vars->{$1} ? $vars->{$1} : $1/eg;
+ return $value;
+ }
+
+sub read_conf
+ {
+ my %conf;
+ open (CONFIGFILE, "$smbldap_conf") || die "Unable to open $smbldap_conf for reading !\n";
+ while (<CONFIGFILE>) {
+ chomp($_);
+ ## throw away comments
+ next if ( /^\s*#/ || /^\s*$/ || /^\s*\;/);
+ ## check for a param = value
+ my ($parameter,$value)=read_parameter($_);
+ $value = &subst_configvar($value, \%conf);
+ $conf{$parameter}=$value;
+ }
+ close (CONFIGFILE);
+
+ if ($< == 0) {
+ open (CONFIGFILE, "$smbldap_bind_conf") || die "Unable to open $smbldap_bind_conf for reading !\n";
+ while (<CONFIGFILE>) {
+ chomp($_);
+ ## throw away comments
+ next if ( /^\s*#/ || /^\s*$/ || /^\s*\;/);
+ ## check for a param = value
+ my ($parameter,$value)=read_parameter($_);
+ $value = &subst_configvar($value, \%conf);
+ $conf{$parameter}=$value;
+ }
+ close (CONFIGFILE);
+ } else {
+ $conf{slaveDN}=$conf{slavePw}=$conf{masterDN}=$conf{masterPw}="";
+ }
+ # automatically find SID
+ if (not $conf{SID}) {
+ $conf{SID} = getLocalSID() ||
+ die "Unable to determine domain SID: please edit your smbldap.conf,
+ or start your samba server for a few minutes to allow for SID generation to proceed\n";
+ }
+ return(%conf);
+ }
+
+sub read_smbconf
+ {
+ my %conf;
+ my $smbconf="$samba_conf";
+ open (CONFIGFILE, "$smbconf") || die "Unable to open $smbconf for reading !\n";
+ my $global=0;
+ while (<CONFIGFILE>) {
+ chomp;
+ if (/^\[global\]/) {
+ $global=1;
+ }
+ if ($global == 1) {
+ if (/^\[/ and !/\[global\]/) {
+ $global=0;
+ } else {
+ ## throw away comments
+ #next if ( ! /workgroup/i );
+ next if ( /^\s*#/ || /^\s*$/ || /^\s*\;/ || /\[/);
+ ## check for a param = value
+ my ($parameter,$value)=read_parameter($_);
+ $value = &subst_configvar($value, \%conf);
+ $conf{$parameter}=$value;
+ }
+ }
+ }
+ close (CONFIGFILE);
+ return(%conf);
+ }
+my %smbconf=read_smbconf();
+
+sub getLocalSID {
+ my $string = `LANG= PATH=/opt/IDEALX/bin:/usr/local/bin:/usr/bin:/bin net getlocalsid 2>/dev/null`;
+ my ($domain,$sid)=($string =~ m/^SID for domain (\S+) is: (\S+)$/ );
+
+ return $sid;
+}
+
+# let's read the configurations file...
+%config=read_conf();
+
+sub get_parameter {
+ # this function return the value for a parameter. The name of the parameter can be either this
+ # defined in smb.conf or smbldap.conf
+ my $parameter_smb=shift;
+ my $parameter_smbldap=shift;
+ if (defined $config{$parameter_smbldap} and $config{$parameter_smbldap} ne "") {
+ return $config{$parameter_smbldap};
+ } elsif (defined $smbconf{$parameter_smb} and $smbconf{$parameter_smb} ne "") {
+ return $smbconf{$parameter_smb};
+ } else {
+ #print "could not find parameter's value (parameter given: $parameter_smbldap or $parameter_smb) !!\n";
+ undef $smbconf{$parameter_smb};
+ }
+
+}
+
+$config{sambaDomain}=get_parameter("workgroup","sambaDomain");
+$config{suffix}=get_parameter("ldap suffix","suffix");
+$config{usersdn}=get_parameter("ldap user suffix","usersdn");
+if ($config{usersdn} !~ m/,/ ) {$config{usersdn}=$config{usersdn}.",".$config{suffix};}
+$config{groupsdn}=get_parameter("ldap group suffix","groupsdn");
+if ($config{groupsdn} !~ m/,/ ) {$config{groupsdn}=$config{groupsdn}.",".$config{suffix};}
+$config{computersdn}=get_parameter("ldap machine suffix","computersdn");
+if ($config{computersdn} !~ m/,/ ) {$config{computersdn}=$config{computersdn}.",".$config{suffix};}
+$config{idmapdn}=get_parameter("ldap idmap suffix","idmapdn");
+if (defined $config{idmapdn}) {
+ if ($config{idmapdn} !~ m/,/ ) {$config{idmapdn}=$config{idmapdn}.",".$config{suffix};}
+}
+
+# next uidNumber and gidNumber available are stored in sambaDomainName object
+if (!defined $config{sambaUnixIdPooldn}) {
+ $config{sambaUnixIdPooldn}="sambaDomainName=$config{sambaDomain},$config{suffix}";
+}
+if (!defined $config{masterLDAP}) {
+ $config{masterLDAP}="127.0.0.1";
+}
+if (!defined $config{masterPort}) {
+ $config{masterPort}="389";
+}
+if (!defined $config{slaveLDAP}) {
+ $config{slaveLDAP}="127.0.0.1";
+}
+if (!defined $config{slavePort}) {
+ $config{slavePort}="389";
+}
+if (!defined $config{ldapTLS}) {
+ $config{ldapTLS}="0";
+}
+
+sub connect_ldap_master
+ {
+ # bind to a directory with dn and password
+ my $ldap_master = Net::LDAP->new(
+ "$config{masterLDAP}",
+ port => "$config{masterPort}",
+ version => 3,
+ timeout => 60,
+ # debug => 0xffff,
+ )
+ or die "erreur LDAP: Can't contact master ldap server ($@)";
+ if ($config{ldapTLS} == 1) {
+ $ldap_master->start_tls(
+ verify => "$config{verify}",
+ clientcert => "$config{clientcert}",
+ clientkey => "$config{clientkey}",
+ cafile => "$config{cafile}"
+ );
+ }
+ $ldap_master->bind ( "$config{masterDN}",
+ password => "$config{masterPw}"
+ );
+ $ldap=$ldap_master;
+ return($ldap_master);
+ }
+
+sub connect_ldap_slave
+ {
+ # bind to a directory with dn and password
+ my $conf_cert;
+ my $ldap_slave = Net::LDAP->new(
+ "$config{slaveLDAP}",
+ port => "$config{slavePort}",
+ version => 3,
+ timeout => 60,
+ # debug => 0xffff,
+ )
+ or warn "erreur LDAP: Can't contact slave ldap server ($@)\n=>trying to contact the master server\n";
+ if (!$ldap_slave) {
+ # connection to the slave failed: trying to contact the master ...
+ $ldap_slave = Net::LDAP->new(
+ "$config{masterLDAP}",
+ port => "$config{masterPort}",
+ version => 3,
+ timeout => 60,
+ # debug => 0xffff,
+ )
+ or die "erreur LDAP: Can't contact master ldap server ($@)\n";
+ }
+ if ($ldap_slave) {
+ if ($config{ldapTLS} == 1) {
+ $ldap_slave->start_tls(
+ verify => "$config{verify}",
+ clientcert => "$config{clientcert}",
+ clientkey => "$config{clientkey}",
+ cafile => "$config{cafile}"
+ );
+ }
+ $ldap_slave->bind ( "$config{masterDN}",
+ password => "$config{masterPw}"
+ );
+ $ldap=$ldap_slave;
+ return($ldap_slave);
+ }
+ }
+
+sub get_user_dn
+ {
+ my $user = shift;
+ my $dn='';
+ my $mesg = $ldap->search ( base => $config{suffix},
+ scope => $config{scope},
+ filter => "(&(objectclass=posixAccount)(uid=$user))"
+ );
+ $mesg->code && die $mesg->error;
+ foreach my $entry ($mesg->all_entries) {
+ $dn= $entry->dn;
+ }
+ chomp($dn);
+ if ($dn eq '') {
+ return undef;
+ }
+ $dn="dn: ".$dn;
+ return $dn;
+ }
+
+
+sub get_user_dn2
+ {
+ my $user = shift;
+ my $dn='';
+ my $mesg = $ldap->search ( base => $config{suffix},
+ scope => $config{scope},
+ filter => "(&(objectclass=posixAccount)(uid=$user))"
+ );
+ $mesg->code && warn "failed to perform search; ", $mesg->error;
+
+ foreach my $entry ($mesg->all_entries) {
+ $dn= $entry->dn;
+ }
+ chomp($dn);
+ if ($dn eq '') {
+ return (1,undef);
+ }
+ $dn="dn: ".$dn;
+ return (1,$dn);
+ }
+
+
+sub get_group_dn
+ {
+ my $group = shift;
+ my $dn='';
+ my $filter;
+ if ($group =~ /^\d+$/) {
+ $filter="(&(objectclass=posixGroup)(|(cn=$group)(gidNumber=$group)))";
+ } else {
+ $filter="(&(objectclass=posixGroup)(cn=$group))";
+ }
+ my $mesg = $ldap->search ( base => $config{groupsdn},
+ scope => $config{scope},
+ filter => $filter
+ );
+ $mesg->code && die $mesg->error;
+ foreach my $entry ($mesg->all_entries) {
+ $dn= $entry->dn;
+ }
+ chomp($dn);
+ if ($dn eq '') {
+ return undef;
+ }
+ $dn="dn: ".$dn;
+ return $dn;
+ }
+
+# return (success, dn)
+# bool = is_samba_user($username)
+sub is_samba_user
+ {
+ my $user = shift;
+ my $mesg = $ldap->search ( base => $config{suffix},
+ scope => $config{scope},
+ filter => "(&(objectClass=sambaSamAccount)(uid=$user))"
+ );
+ $mesg->code && die $mesg->error;
+ return ($mesg->count ne 0);
+ }
+
+sub is_unix_user
+ {
+ my $user = shift;
+ my $mesg = $ldap->search ( base => $config{suffix},
+ scope => $config{scope},
+ filter => "(&(objectClass=posixAccount)(uid=$user))"
+ );
+ $mesg->code && die $mesg->error;
+ return ($mesg->count ne 0);
+ }
+
+sub is_group_member
+ {
+ my $dn_group = shift;
+ my $user = shift;
+ my $mesg = $ldap->search ( base => $dn_group,
+ scope => 'base',
+ filter => "(&(memberUid=$user))"
+ );
+ $mesg->code && die $mesg->error;
+ return ($mesg->count ne 0);
+ }
+
+# all entries = does_sid_exist($sid,$config{scope})
+sub does_sid_exist
+ {
+ my $sid = shift;
+ my $dn_group=shift;
+ my $mesg = $ldap->search ( base => $dn_group,
+ scope => $config{scope},
+ filter => "(sambaSID=$sid)"
+ #filter => "(&(objectClass=sambaSAMAccount|objectClass=sambaGroupMapping)(sambaSID=$sid))"
+ );
+ $mesg->code && die $mesg->error;
+ return ($mesg);
+ }
+
+# try to bind with user dn and password to validate current password
+sub is_user_valid
+ {
+ my ($user, $dn, $pass) = @_;
+ my $userLdap = Net::LDAP->new($config{slaveLDAP}) or die "erreur LDAP";
+ my $mesg= $userLdap->bind (dn => $dn, password => $pass );
+ if ($mesg->code eq 0) {
+ $userLdap->unbind;
+ return 1;
+ } else {
+ if ($userLdap->bind()) {
+ $userLdap->unbind;
+ return 0;
+ } else {
+ print ("The LDAP directory is not available.\n Check the server, cables ...");
+ $userLdap->unbind;
+ return 0;
+ }
+ die "Problem : contact your administrator";
+ }
+ }
+
+
+# dn = get_dn_from_line ($dn_line)
+# helper to get "a=b,c=d" from "dn: a=b,c=d"
+sub get_dn_from_line
+ {
+ my $dn = shift;
+ $dn =~ s/^dn: //;
+ return $dn;
+ }
+
+
+# success = add_posix_machine($user, $uid, $gid)
+sub add_posix_machine
+ {
+ my ($user,$uid,$gid,$wait) = @_;
+ if (!defined $wait) {
+ $wait=0;
+ }
+ # bind to a directory with dn and password
+ my $add = $ldap->add ( "uid=$user,$config{computersdn}",
+ attr => [
+ 'objectclass' => ['top','inetOrgPerson', 'posixAccount'],
+ 'cn' => "$user",
+ 'sn' => "$user",
+ 'uid' => "$user",
+ 'uidNumber' => "$uid",
+ 'gidNumber' => "$gid",
+ 'homeDirectory' => '/dev/null',
+ 'loginShell' => '/bin/false',
+ 'description' => 'Computer',
+ 'gecos' => 'Computer',
+ ]
+ );
+
+ $add->code && warn "failed to add entry: ", $add->error ;
+ sleep($wait);
+ return 1;
+ }
+
+
+# success = add_samba_machine_smbpasswd($computername)
+sub add_samba_machine_smbpasswd
+ {
+ my $user = shift;
+ system "smbpasswd -a -m $user";
+ return 1;
+ }
+
+sub add_samba_machine
+ {
+ my ($user, $uid) = @_;
+ my $sambaSID = 2 * $uid + 1000;
+ my $name = $user;
+ $name =~ s/.$//s;
+
+ my ($lmpassword,$ntpassword) = ntlmgen $name;
+ my $modify = $ldap->modify ( "uid=$user,$config{computersdn}",
+ changes => [
+ replace => [objectClass => ['inetOrgPerson', 'posixAccount', 'sambaSAMAccount']],
+ add => [sambaPwdLastSet => '0'],
+ add => [sambaLogonTime => '0'],
+ add => [sambaLogoffTime => '2147483647'],
+ add => [sambaKickoffTime => '2147483647'],
+ add => [sambaPwdCanChange => '0'],
+ add => [sambaPwdMustChange => '0'],
+ add => [sambaAcctFlags => '[W ]'],
+ add => [sambaLMPassword => "$lmpassword"],
+ add => [sambaNTPassword => "$ntpassword"],
+ add => [sambaSID => "$config{SID}-$sambaSID"],
+ add => [sambaPrimaryGroupSID => "$config{SID}-0"]
+ ]
+ );
+
+ $modify->code && die "failed to add entry: ", $modify->error ;
+
+ return 1;
+ }
+
+sub group_add_user
+ {
+ my ($group, $userid) = @_;
+ my $members='';
+ my $dn_line = get_group_dn($group);
+ if (!defined(get_group_dn($group))) {
+ print "$0: group \"$group\" doesn't exist\n";
+ exit (6);
+ }
+ if (!defined($dn_line)) {
+ return 1;
+ }
+ my $dn = get_dn_from_line("$dn_line");
+ # on look if the user is already present in the group
+ my $is_member=is_group_member($dn,$userid);
+ if ($is_member == 1) {
+ print "User \"$userid\" already member of the group \"$group\".\n";
+ } else {
+ # bind to a directory with dn and password
+ # It does not matter if the user already exist, Net::LDAP will add the user
+ # if he does not exist, and ignore him if his already in the directory.
+ my $modify = $ldap->modify ( "$dn",
+ changes => [
+ add => [memberUid => $userid]
+ ]
+ );
+ $modify->code && die "failed to modify entry: ", $modify->error ;
+ }
+ }
+
+sub group_del
+ {
+ my $group_dn=shift;
+ # bind to a directory with dn and password
+ my $modify = $ldap->delete ($group_dn);
+ $modify->code && die "failed to delete group : ", $modify->error ;
+ }
+
+sub add_grouplist_user
+ {
+ my ($grouplist, $user) = @_;
+ my @array = split(/,/, $grouplist);
+ foreach my $group (@array) {
+ group_add_user($group, $user);
+ }
+ }
+
+sub disable_user
+ {
+ my $user = shift;
+ my $dn_line;
+ my $dn = get_dn_from_line($dn_line);
+
+ if (!defined($dn_line = get_user_dn($user))) {
+ print "$0: user $user doesn't exist\n";
+ exit (10);
+ }
+ my $modify = $ldap->modify ( "$dn",
+ changes => [
+ replace => [userPassword => '{crypt}!x']
+ ]
+ );
+ $modify->code && die "failed to modify entry: ", $modify->error ;
+
+ if (is_samba_user($user)) {
+ my $modify = $ldap->modify ( "$dn",
+ changes => [
+ replace => [sambaAcctFlags => '[D ]']
+ ]
+ );
+ $modify->code && die "failed to modify entry: ", $modify->error ;
+ }
+ }
+
+# delete_user($user)
+sub delete_user
+ {
+ my $user = shift;
+ my $dn_line;
+
+ if (!defined($dn_line = get_user_dn($user))) {
+ print "$0: user $user doesn't exist\n";
+ exit (10);
+ }
+
+ my $dn = get_dn_from_line($dn_line);
+ my $modify = $ldap->delete($dn);
+ }
+
+# $gid = group_add($groupname, $group_gid, $force_using_existing_gid)
+sub group_add
+ {
+ my ($gname, $gid, $force) = @_;
+ my $nscd_status = system "/etc/init.d/nscd status >/dev/null 2>&1";
+ if ($nscd_status == 0) {
+ system "/etc/init.d/nscd stop > /dev/null 2>&1";
+ }
+ if (!defined($gid)) {
+ #while (defined(getgrgid($config{GID_START}))) {
+ # $config{GID_START}++;
+ #}
+ #$gid = $config{GID_START};
+ $gid=get_next_id($config{groupsdn},"gidNumber");
+ } else {
+ if (!defined($force)) {
+ if (defined(getgrgid($gid))) {
+ return undef;
+ }
+ }
+ }
+ if ($nscd_status == 0) {
+ system "/etc/init.d/nscd start > /dev/null 2>&1";
+ }
+ my $modify = $ldap->add ( "cn=$gname,$config{groupsdn}",
+ attrs => [
+ objectClass => 'posixGroup',
+ cn => "$gname",
+ gidNumber => "$gid"
+ ]
+ );
+
+ $modify->code && die "failed to add entry: ", $modify->error ;
+ return $gid;
+ }
+
+# $homedir = get_homedir ($user)
+sub get_homedir
+ {
+ my $user = shift;
+ my $homeDir='';
+ my $entry;
+ my $mesg = $ldap->search (
+ base =>$config{usersdn},
+ scope => $config{scope},
+ filter => "(&(objectclass=posixAccount)(uid=$user))"
+ );
+ $mesg->code && die $mesg->error;
+
+ my $nb=$mesg->count;
+ if ($nb > 1) {
+ print "Aborting: there are $nb existing user named $user\n";
+ foreach $entry ($mesg->all_entries) {
+ my $dn=$entry->dn;
+ print " $dn\n";
+ }
+ exit (4);
+ } else {
+ $entry = $mesg->shift_entry();
+ $homeDir= $entry->get_value("homeDirectory");
+ }
+
+ chomp $homeDir;
+ if ($homeDir eq '') {
+ return undef;
+ }
+ return $homeDir;
+ }
+
+# search for an user
+sub read_user
+ {
+ my $user = shift;
+ my $lines ='';
+ my $mesg = $ldap->search ( # perform a search
+ base => $config{suffix},
+ scope => $config{scope},
+ filter => "(&(objectclass=posixAccount)(uid=$user))"
+ );
+
+ $mesg->code && die $mesg->error;
+ foreach my $entry ($mesg->all_entries) {
+ $lines.= "dn: " . $entry->dn."\n";
+ foreach my $attr ($entry->attributes) {
+ {
+ $lines.= $attr.": ".join(',', $entry->get_value($attr))."\n";
+ }
+ }
+ }
+ chomp $lines;
+ if ($lines eq '') {
+ return undef;
+ }
+ return $lines;
+ }
+
+# search for a user
+# return the attributes in an array
+sub read_user_entry
+ {
+ my $user = shift;
+ my $mesg = $ldap->search ( # perform a search
+ base => $config{suffix},
+ scope => $config{scope},
+ filter => "(&(objectclass=posixAccount)(uid=$user))"
+ );
+
+ $mesg->code && die $mesg->error;
+ my $entry = $mesg->entry();
+ return $entry;
+ }
+
+# search for a group
+sub read_group
+ {
+ my $user = shift;
+ my $lines ='';
+ my $mesg = $ldap->search ( # perform a search
+ base => $config{groupsdn},
+ scope => $config{scope},
+ filter => "(&(objectclass=posixGroup)(cn=$user))"
+ );
+
+ $mesg->code && die $mesg->error;
+ foreach my $entry ($mesg->all_entries) {
+ $lines.= "dn: " . $entry->dn."\n";
+ foreach my $attr ($entry->attributes) {
+ {
+ $lines.= $attr.": ".join(',', $entry->get_value($attr))."\n";
+ }
+ }
+ }
+ chomp $lines;
+ if ($lines eq '') {
+ return undef;
+ }
+ return $lines;
+ }
+
+# find groups of a given user
+##### MODIFIE ########
+sub find_groups_of {
+ my $user = shift;
+ my @groups = ();
+ my $mesg = $ldap->search ( # perform a search
+ base => $config{groupsdn},
+ scope => $config{scope},
+ filter => "(&(objectclass=posixGroup)(memberuid=$user))"
+ );
+ $mesg->code && die $mesg->error;
+
+ my $entry;
+ while ($entry = $mesg->shift_entry()) {
+ push(@groups, scalar($entry->get_value('cn')));
+ }
+ return (@groups);
+}
+
+sub read_group_entry {
+ my $group = shift;
+ my $entry;
+ my %res;
+ my $mesg = $ldap->search ( # perform a search
+ base => $config{groupsdn},
+ scope => $config{scope},
+ filter => "(&(objectclass=posixGroup)(cn=$group))"
+ );
+
+ $mesg->code && die $mesg->error;
+ my $nb=$mesg->count;
+ if ($nb > 1) {
+ print "Error: $nb groups exist \"cn=$group\"\n";
+ foreach $entry ($mesg->all_entries) {
+ my $dn=$entry->dn; print " $dn\n";
+ }
+ exit 11;
+ } else {
+ $entry = $mesg->shift_entry();
+ }
+ return $entry;
+}
+
+sub read_group_entry_gid {
+ my $group = shift;
+ my %res;
+ my $mesg = $ldap->search ( # perform a search
+ base => $config{groupsdn},
+ scope => $config{scope},
+ filter => "(&(objectclass=posixGroup)(gidNumber=$group))"
+ );
+
+ $mesg->code && die $mesg->error;
+ my $entry = $mesg->shift_entry();
+ return $entry;
+}
+
+# return the gidnumber for a group given as name or gid
+# -1 : bad group name
+# -2 : bad gidnumber
+sub parse_group
+ {
+ my $userGidNumber = shift;
+ if ($userGidNumber =~ /[^\d]/ ) {
+ my $gname = $userGidNumber;
+ my $gidnum = getgrnam($gname);
+ if ($gidnum !~ /\d+/) {
+ return -1;
+ } else {
+ $userGidNumber = $gidnum;
+ }
+ } elsif (!defined(getgrgid($userGidNumber))) {
+ return -2;
+ }
+ return $userGidNumber;
+ }
+
+# remove $user from $group
+sub group_remove_member
+ {
+ my ($group, $user) = @_;
+ my $members='';
+ my $grp_line = get_group_dn($group);
+ if (!defined($grp_line)) {
+ return 0;
+ }
+ my $dn = get_dn_from_line($grp_line);
+ # we test if the user exist in the group
+ my $is_member=is_group_member($dn,$user);
+ if ($is_member == 1) {
+ # delete only the user from the group
+ my $modify = $ldap->modify ( "$dn",
+ changes => [
+ delete => [memberUid => ["$user"]]
+ ]
+ );
+ $modify->code && die "failed to delete entry: ", $modify->error ;
+ }
+ return 1;
+ }
+
+sub group_get_members
+ {
+ my ($group) = @_;
+ my $members;
+ my @resultat;
+ my $grp_line = get_group_dn($group);
+ if (!defined($grp_line)) {
+ return 0;
+ }
+ my $mesg = $ldap->search (
+ base => $config{groupsdn},
+ scope => $config{scope},
+ filter => "(&(objectclass=posixgroup)(cn=$group))"
+ );
+ $mesg->code && die $mesg->error;
+ foreach my $entry ($mesg->all_entries) {
+ foreach my $attr ($entry->attributes) {
+ if ($attr=~/\bmemberUid\b/) {
+ foreach my $ent ($entry->get_value($attr)) {
+ push (@resultat,$ent);
+ }
+ }
+ }
+ }
+ return @resultat;
+ }
+
+sub do_ldapmodify
+ {
+ my $ldif = shift;
+ my $FILE = "|$config{ldapmodify} -r >/dev/null";
+ open (FILE, $FILE) || die "$!\n";
+ print FILE <<EOF;
+$ldif
+EOF
+ ;
+ close FILE;
+ my $rc = $?;
+ return $rc;
+ }
+
+sub group_type_by_name {
+ my $type_name = shift;
+ my %groupmap = (
+ 'domain' => 2,
+ 'local' => 4,
+ 'builtin' => 5
+ );
+ return $groupmap{$type_name};
+}
+
+sub subst_user
+ {
+ my ($str, $username) = @_;
+ $str =~ s/%U/$username/ if ($str);
+ return($str);
+ }
+
+# all given mails are stored in a table (remove the comma separated)
+sub split_arg_comma {
+ my $arg = shift;
+ my @args;
+ if (defined($arg)) {
+ if ($arg eq '-') {
+ @args = ( );
+ } else {
+ @args = split(/\s*,\s*/, $arg);
+ }
+ }
+ return (@args);
+}
+
+sub list_union {
+ my ($list1, $list2) = @_;
+ my @res = @$list1;
+ foreach my $e (@$list2) {
+ if (! grep($_ eq $e, @$list1)) {
+ push(@res, $e);
+ }
+ }
+ return @res;
+}
+
+sub list_minus {
+ my ($list1, $list2) = @_;
+ my @res = ();
+ foreach my $e (@$list1) {
+ if (! grep( $_ eq $e, @$list2 )) {
+ push(@res, $e);
+ }
+ }
+ return @res;
+}
+
+sub get_next_id($$) {
+ my $ldap_base_dn = shift;
+ my $attribute = shift;
+ my $tries = 0;
+ my $found=0;
+ my $next_uid_mesg;
+ my $nextuid;
+ if ($ldap_base_dn =~ m/$config{usersdn}/i) {
+ # when adding a new user, we'll check if the uidNumber available is not
+ # already used for a computer's account
+ $ldap_base_dn=$config{suffix}
+ }
+ do {
+ $next_uid_mesg = $ldap->search(
+ base => $config{sambaUnixIdPooldn},
+ filter => "(objectClass=sambaUnixIdPool)",
+ scope => "base"
+ );
+ $next_uid_mesg->code && die "Error looking for next uid";
+ if ($next_uid_mesg->count != 1) {
+ die "Could not find base dn, to get next $attribute";
+ }
+ my $entry = $next_uid_mesg->entry(0);
+
+ $nextuid = $entry->get_value($attribute);
+ my $modify=$ldap->modify( "$config{sambaUnixIdPooldn}",
+ changes => [
+ replace => [ $attribute => $nextuid + 1 ]
+ ]
+ );
+ $modify->code && die "Error: ", $modify->error;
+ # let's check if the id found is really free (in ou=Groups or ou=Users)...
+ my $check_uid_mesg = $ldap->search(
+ base => $ldap_base_dn,
+ filter => "($attribute=$nextuid)",
+ );
+ $check_uid_mesg->code && die "Cannot confirm $attribute $nextuid is free";
+ if ($check_uid_mesg->count == 0) {
+ $found=1;
+ return $nextuid;
+ }
+ $tries++;
+ print "Cannot confirm $attribute $nextuid is free: checking for the next one\n"
+ } while ($found != 1);
+ die "Could not allocate $attribute!";
+}
+
+1;
+