1#!/usr/bin/perl -w 2use strict; 3package smbldap_tools; 4use Net::LDAP; 5use Crypt::SmbHash; 6use Unicode::MapUTF8 qw(to_utf8 from_utf8); 7 8 9# $Id: smbldap_tools.pm,v 1.1.1.1 2010-07-16 07:33:12 winniec Exp $ 10# 11# This code was developped by IDEALX (http://IDEALX.org/) and 12# contributors (their names can be found in the CONTRIBUTORS file). 13# 14# Copyright (C) 2001-2002 IDEALX 15# 16# This program is free software; you can redistribute it and/or 17# modify it under the terms of the GNU General Public License 18# as published by the Free Software Foundation; either version 2 19# of the License, or (at your option) any later version. 20# 21# This program is distributed in the hope that it will be useful, 22# but WITHOUT ANY WARRANTY; without even the implied warranty of 23# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24# GNU General Public License for more details. 25# 26# You should have received a copy of the GNU General Public License 27# along with this program; if not, write to the Free Software 28# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 29# USA. 30 31 32# ugly funcs using global variables and spawning openldap clients 33 34my $smbldap_conf; 35if (-e "/etc/smbldap-tools/smbldap.conf") { 36 $smbldap_conf="/etc/smbldap-tools/smbldap.conf"; 37} else { 38 $smbldap_conf="/etc/opt/IDEALX/smbldap-tools/smbldap.conf"; 39} 40 41my $smbldap_bind_conf; 42if (-e "/etc/smbldap-tools/smbldap_bind.conf") { 43 $smbldap_bind_conf="/etc/smbldap-tools/smbldap_bind.conf"; 44} else { 45 $smbldap_bind_conf="/etc/opt/IDEALX/smbldap-tools/smbldap_bind.conf"; 46} 47my $samba_conf; 48if (-e "/etc/samba/smb.conf") { 49 $samba_conf="/etc/samba/smb.conf"; 50} else { 51 $samba_conf="/usr/local/samba/lib/smb.conf"; 52} 53 54use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); 55use Exporter; 56$VERSION = 1.00; 57 58@ISA = qw(Exporter); 59use vars qw(%config $ldap); 60 61@EXPORT = qw( 62 get_user_dn 63 get_group_dn 64 is_group_member 65 is_samba_user 66 is_unix_user 67 is_nonldap_unix_user 68 is_user_valid 69 does_sid_exist 70 get_dn_from_line 71 add_posix_machine 72 add_samba_machine 73 add_samba_machine_smbpasswd 74 group_add_user 75 add_grouplist_user 76 disable_user 77 delete_user 78 group_add 79 group_del 80 get_homedir 81 read_user 82 read_user_entry 83 read_group 84 read_group_entry 85 read_group_entry_gid 86 find_groups_of 87 parse_group 88 group_remove_member 89 group_get_members 90 do_ldapadd 91 do_ldapmodify 92 get_user_dn2 93 connect_ldap_master 94 connect_ldap_slave 95 group_type_by_name 96 subst_configvar 97 read_config 98 read_parameter 99 subst_user 100 split_arg_comma 101 list_union 102 list_minus 103 get_next_id 104 print_banner 105 getDomainName 106 getLocalSID 107 utf8Encode 108 utf8Decode 109 %config 110 ); 111 112sub print_banner 113 { 114 print STDERR "(c) Jerome Tournier - IDEALX 2004 (http://www.idealx.com)- Licensed under the GPL\n" 115 unless $config{no_banner}; 116 } 117 118sub read_parameter 119 { 120 my $line=shift; 121 ## check for a param = value 122 if ($_=~/=/) { 123 my ($param,$val); 124 if ($_=~/\s*.*?\s*=\s*".*"/) { 125 ($param,$val) = /\s*(.*?)\s*=\s*"(.*)"/; 126 } elsif ($_=~/\s*.*?\s*=\s*'.*'/) { 127 ($param,$val) = /\s*(.*?)\s*=\s*'(.*)'/; 128 } else { 129 ($param,$val) = /\s*(.*?)\s*=\s*(.*)/; 130 } 131 return ($param,$val); 132 } 133 } 134 135sub subst_configvar 136 { 137 my $value = shift; 138 my $vars = shift; 139 140 $value =~ s/\$\{([^}]+)\}/$vars->{$1} ? $vars->{$1} : $1/eg; 141 return $value; 142 } 143 144sub read_conf 145 { 146 my %conf; 147 open (CONFIGFILE, "$smbldap_conf") || die "Unable to open $smbldap_conf for reading !\n"; 148 while (<CONFIGFILE>) { 149 chomp($_); 150 ## throw away comments 151 next if ( /^\s*#/ || /^\s*$/ || /^\s*\;/); 152 ## check for a param = value 153 my ($parameter,$value)=read_parameter($_); 154 $value = &subst_configvar($value, \%conf); 155 $conf{$parameter}=$value; 156 } 157 close (CONFIGFILE); 158 159 if ($< == 0) { 160 open (CONFIGFILE, "$smbldap_bind_conf") || die "Unable to open $smbldap_bind_conf for reading !\n"; 161 while (<CONFIGFILE>) { 162 chomp($_); 163 ## throw away comments 164 next if ( /^\s*#/ || /^\s*$/ || /^\s*\;/); 165 ## check for a param = value 166 my ($parameter,$value)=read_parameter($_); 167 $value = &subst_configvar($value, \%conf); 168 $conf{$parameter}=$value; 169 } 170 close (CONFIGFILE); 171 } else { 172 $conf{slaveDN}=$conf{slavePw}=$conf{masterDN}=$conf{masterPw}=""; 173 } 174 # automatically find SID 175 if (not $conf{SID}) { 176 $conf{SID} = getLocalSID() || 177 die "Unable to determine domain SID: please edit your smbldap.conf, 178 or start your samba server for a few minutes to allow for SID generation to proceed\n"; 179 } 180 return(%conf); 181 } 182 183sub read_smbconf 184 { 185 my %conf; 186 my $smbconf="$samba_conf"; 187 open (CONFIGFILE, "$smbconf") || die "Unable to open $smbconf for reading !\n"; 188 my $global=0; 189 my $prevline=""; 190 while (<CONFIGFILE>) { 191 chomp; 192 if (/^(.*)\\$/) { 193 $prevline.=$1; 194 next; 195 } 196 $_=$prevline.$_; 197 $prevline=""; 198 if (/^\[global\]/) { 199 $global=1; 200 } 201 if ($global == 1) { 202 if (/^\[/ and !/\[global\]/) { 203 $global=0; 204 } else { 205 ## throw away comments 206 #next if ( ! /workgroup/i ); 207 next if ( /^\s*#/ || /^\s*$/ || /^\s*\;/ || /\[/); 208 ## check for a param = value 209 my ($parameter,$value)=read_parameter($_); 210 $value = &subst_configvar($value, \%conf); 211 $conf{$parameter}=$value; 212 } 213 } 214 } 215 close (CONFIGFILE); 216 return(%conf); 217 } 218my %smbconf=read_smbconf(); 219 220sub getLocalSID { 221 my $string = `LANG= PATH=/opt/IDEALX/bin:/usr/local/bin:/usr/bin:/bin net getlocalsid 2>/dev/null`; 222 my ($domain,$sid)=($string =~ m/^SID for domain (\S+) is: (\S+)$/ ); 223 224 return $sid; 225} 226 227# let's read the configurations file... 228%config=read_conf(); 229 230sub get_parameter { 231 # this function return the value for a parameter. The name of the parameter can be either this 232 # defined in smb.conf or smbldap.conf 233 my $parameter_smb=shift; 234 my $parameter_smbldap=shift; 235 if (defined $config{$parameter_smbldap} and $config{$parameter_smbldap} ne "") { 236 return $config{$parameter_smbldap}; 237 } elsif (defined $smbconf{$parameter_smb} and $smbconf{$parameter_smb} ne "") { 238 return $smbconf{$parameter_smb}; 239 } else { 240 #print "could not find parameter's value (parameter given: $parameter_smbldap or $parameter_smb) !!\n"; 241 undef $smbconf{$parameter_smb}; 242 } 243 244} 245 246$config{sambaDomain}=get_parameter("workgroup","sambaDomain"); 247$config{suffix}=get_parameter("ldap suffix","suffix"); 248$config{usersdn}=get_parameter("ldap user suffix","usersdn"); 249if ($config{usersdn} !~ m/,/ ) {$config{usersdn}=$config{usersdn}.",".$config{suffix};} 250$config{groupsdn}=get_parameter("ldap group suffix","groupsdn"); 251if ($config{groupsdn} !~ m/,/ ) {$config{groupsdn}=$config{groupsdn}.",".$config{suffix};} 252$config{computersdn}=get_parameter("ldap machine suffix","computersdn"); 253if ($config{computersdn} !~ m/,/ ) {$config{computersdn}=$config{computersdn}.",".$config{suffix};} 254$config{idmapdn}=get_parameter("ldap idmap suffix","idmapdn"); 255if (defined $config{idmapdn}) { 256 if ($config{idmapdn} !~ m/,/ ) {$config{idmapdn}=$config{idmapdn}.",".$config{suffix};} 257} 258 259# next uidNumber and gidNumber available are stored in sambaDomainName object 260if (!defined $config{sambaUnixIdPooldn}) { 261 $config{sambaUnixIdPooldn}="sambaDomainName=$config{sambaDomain},$config{suffix}"; 262} 263if (!defined $config{masterLDAP}) { 264 $config{masterLDAP}="127.0.0.1"; 265} 266if (!defined $config{masterPort}) { 267 $config{masterPort}="389"; 268} 269if (!defined $config{slaveLDAP}) { 270 $config{slaveLDAP}="127.0.0.1"; 271} 272if (!defined $config{slavePort}) { 273 $config{slavePort}="389"; 274} 275if (!defined $config{ldapTLS}) { 276 $config{ldapTLS}="0"; 277} 278 279sub connect_ldap_master 280 { 281 # bind to a directory with dn and password 282 my $ldap_master = Net::LDAP->new( 283 "$config{masterLDAP}", 284 port => "$config{masterPort}", 285 version => 3, 286 timeout => 60, 287 # debug => 0xffff, 288 ) 289 or die "erreur LDAP: Can't contact master ldap server ($@)"; 290 if ($config{ldapTLS} == 1) { 291 $ldap_master->start_tls( 292 verify => "$config{verify}", 293 clientcert => "$config{clientcert}", 294 clientkey => "$config{clientkey}", 295 cafile => "$config{cafile}" 296 ); 297 } 298 $ldap_master->bind ( "$config{masterDN}", 299 password => "$config{masterPw}" 300 ); 301 $ldap=$ldap_master; 302 return($ldap_master); 303 } 304 305sub connect_ldap_slave 306 { 307 # bind to a directory with dn and password 308 my $conf_cert; 309 my $ldap_slave = Net::LDAP->new( 310 "$config{slaveLDAP}", 311 port => "$config{slavePort}", 312 version => 3, 313 timeout => 60, 314 # debug => 0xffff, 315 ) 316 or warn "erreur LDAP: Can't contact slave ldap server ($@)\n=>trying to contact the master server\n"; 317 if (!$ldap_slave) { 318 # connection to the slave failed: trying to contact the master ... 319 $ldap_slave = Net::LDAP->new( 320 "$config{masterLDAP}", 321 port => "$config{masterPort}", 322 version => 3, 323 timeout => 60, 324 # debug => 0xffff, 325 ) 326 or die "erreur LDAP: Can't contact master ldap server ($@)\n"; 327 } 328 if ($ldap_slave) { 329 if ($config{ldapTLS} == 1) { 330 $ldap_slave->start_tls( 331 verify => "$config{verify}", 332 clientcert => "$config{clientcert}", 333 clientkey => "$config{clientkey}", 334 cafile => "$config{cafile}" 335 ); 336 } 337 $ldap_slave->bind ( "$config{masterDN}", 338 password => "$config{masterPw}" 339 ); 340 $ldap=$ldap_slave; 341 return($ldap_slave); 342 } 343 } 344 345sub get_user_dn 346 { 347 my $user = shift; 348 my $dn=''; 349 my $mesg = $ldap->search ( base => $config{suffix}, 350 scope => $config{scope}, 351 filter => "(&(objectclass=posixAccount)(uid=$user))" 352 ); 353 $mesg->code && die $mesg->error; 354 foreach my $entry ($mesg->all_entries) { 355 $dn= $entry->dn; 356 } 357 chomp($dn); 358 if ($dn eq '') { 359 return undef; 360 } 361 $dn="dn: ".$dn; 362 return $dn; 363 } 364 365 366sub get_user_dn2 367 { 368 my $user = shift; 369 my $dn=''; 370 my $mesg = $ldap->search ( base => $config{suffix}, 371 scope => $config{scope}, 372 filter => "(&(objectclass=posixAccount)(uid=$user))" 373 ); 374 $mesg->code && warn "failed to perform search; ", $mesg->error; 375 376 foreach my $entry ($mesg->all_entries) { 377 $dn= $entry->dn; 378 } 379 chomp($dn); 380 if ($dn eq '') { 381 return (1,undef); 382 } 383 $dn="dn: ".$dn; 384 return (1,$dn); 385 } 386 387 388sub get_group_dn 389 { 390 my $group = shift; 391 my $dn=''; 392 my $filter; 393 if ($group =~ /^\d+$/) { 394 $filter="(&(objectclass=posixGroup)(|(cn=$group)(gidNumber=$group)))"; 395 } else { 396 $filter="(&(objectclass=posixGroup)(cn=$group))"; 397 } 398 my $mesg = $ldap->search ( base => $config{groupsdn}, 399 scope => $config{scope}, 400 filter => $filter 401 ); 402 $mesg->code && die $mesg->error; 403 foreach my $entry ($mesg->all_entries) { 404 $dn= $entry->dn; 405 } 406 chomp($dn); 407 if ($dn eq '') { 408 return undef; 409 } 410 $dn="dn: ".$dn; 411 return $dn; 412 } 413 414# return (success, dn) 415# bool = is_samba_user($username) 416sub is_samba_user 417 { 418 my $user = shift; 419 my $mesg = $ldap->search ( base => $config{suffix}, 420 scope => $config{scope}, 421 filter => "(&(objectClass=sambaSamAccount)(uid=$user))" 422 ); 423 $mesg->code && die $mesg->error; 424 return ($mesg->count ne 0); 425 } 426 427sub is_unix_user 428 { 429 my $user = shift; 430 my $mesg = $ldap->search ( base => $config{suffix}, 431 scope => $config{scope}, 432 filter => "(&(objectClass=posixAccount)(uid=$user))" 433 ); 434 $mesg->code && die $mesg->error; 435 return ($mesg->count ne 0); 436 } 437 438sub is_nonldap_unix_user 439 { 440 my $user = shift; 441 my $uid = getpwnam($user); 442 443 if ($uid) { 444 return 1; 445 } else { 446 return 0; 447 } 448} 449 450 451sub is_group_member 452 { 453 my $dn_group = shift; 454 my $user = shift; 455 my $mesg = $ldap->search ( base => $dn_group, 456 scope => 'base', 457 filter => "(&(memberUid=$user))" 458 ); 459 $mesg->code && die $mesg->error; 460 return ($mesg->count ne 0); 461 } 462 463# all entries = does_sid_exist($sid,$config{scope}) 464sub does_sid_exist 465 { 466 my $sid = shift; 467 my $dn_group=shift; 468 my $mesg = $ldap->search ( base => $dn_group, 469 scope => $config{scope}, 470 filter => "(sambaSID=$sid)" 471 #filter => "(&(objectClass=sambaSAMAccount|objectClass=sambaGroupMapping)(sambaSID=$sid))" 472 ); 473 $mesg->code && die $mesg->error; 474 return ($mesg); 475 } 476 477# try to bind with user dn and password to validate current password 478sub is_user_valid 479 { 480 my ($user, $dn, $pass) = @_; 481 my $userLdap = Net::LDAP->new( 482 "$config{slaveLDAP}", 483 port => "$config{slavePort}", 484 version => 3, 485 timeout => 60 486 ) 487 or warn "erreur LDAP: Can't contact slave ldap server ($@)\n=>trying to contact the master server\n"; 488 if (!$userLdap) { 489 # connection to the slave failed: trying to contact the master ... 490 $userLdap = Net::LDAP->new( 491 "$config{masterLDAP}", 492 port => "$config{masterPort}", 493 version => 3, 494 timeout => 60 495 ) 496 or die "erreur LDAP: Can't contact master ldap server ($@)\n"; 497 } 498 if ($userLdap) { 499 if ($config{ldapTLS} == 1) { 500 $userLdap->start_tls( 501 verify => "$config{verify}", 502 clientcert => "$config{clientcert}", 503 clientkey => "$config{clientkey}", 504 cafile => "$config{cafile}" 505 ); 506 } 507 my $mesg= $userLdap->bind (dn => $dn, password => $pass ); 508 if ($mesg->code eq 0) { 509 $userLdap->unbind; 510 return 1; 511 } else { 512 if ($userLdap->bind()) { 513 $userLdap->unbind; 514 return 0; 515 } else { 516 print ("The LDAP directory is not available.\n Check the server, cables ..."); 517 $userLdap->unbind; 518 return 0; 519 } 520 die "Problem : contact your administrator"; 521 } 522 } 523 } 524 525 526# dn = get_dn_from_line ($dn_line) 527# helper to get "a=b,c=d" from "dn: a=b,c=d" 528sub get_dn_from_line 529 { 530 my $dn = shift; 531 $dn =~ s/^dn: //; 532 return $dn; 533 } 534 535 536# success = add_posix_machine($user, $uid, $gid) 537sub add_posix_machine 538 { 539 my ($user,$uid,$gid,$wait) = @_; 540 if (!defined $wait) { 541 $wait=0; 542 } 543 # bind to a directory with dn and password 544 my $add = $ldap->add ( "uid=$user,$config{computersdn}", 545 attr => [ 546 'objectclass' => ['top', 'person', 'organizationalPerson', 'inetOrgPerson', 'posixAccount'], 547 'cn' => "$user", 548 'sn' => "$user", 549 'uid' => "$user", 550 'uidNumber' => "$uid", 551 'gidNumber' => "$gid", 552 'homeDirectory' => '/dev/null', 553 'loginShell' => '/bin/false', 554 'description' => 'Computer', 555 'gecos' => 'Computer', 556 ] 557 ); 558 559 $add->code && warn "failed to add entry: ", $add->error ; 560 sleep($wait); 561 return 1; 562 } 563 564 565# success = add_samba_machine_smbpasswd($computername) 566sub add_samba_machine_smbpasswd 567 { 568 my $user = shift; 569 system "smbpasswd -a -m $user"; 570 return 1; 571 } 572 573sub add_samba_machine 574 { 575 my ($user, $uid) = @_; 576 my $sambaSID = 2 * $uid + 1000; 577 my $name = $user; 578 $name =~ s/.$//s; 579 580 my ($lmpassword,$ntpassword) = ntlmgen $name; 581 my $modify = $ldap->modify ( "uid=$user,$config{computersdn}", 582 changes => [ 583 replace => [objectClass => ['inetOrgPerson', 'posixAccount', 'sambaSAMAccount']], 584 add => [sambaPwdLastSet => '0'], 585 add => [sambaLogonTime => '0'], 586 add => [sambaLogoffTime => '2147483647'], 587 add => [sambaKickoffTime => '2147483647'], 588 add => [sambaPwdCanChange => '0'], 589 add => [sambaPwdMustChange => '0'], 590 add => [sambaAcctFlags => '[W ]'], 591 add => [sambaLMPassword => "$lmpassword"], 592 add => [sambaNTPassword => "$ntpassword"], 593 add => [sambaSID => "$config{SID}-$sambaSID"], 594 add => [sambaPrimaryGroupSID => "$config{SID}-0"] 595 ] 596 ); 597 598 $modify->code && die "failed to add entry: ", $modify->error ; 599 600 return 1; 601 } 602 603sub group_add_user 604 { 605 my ($group, $userid) = @_; 606 my $members=''; 607 my $dn_line = get_group_dn($group); 608 if (!defined(get_group_dn($group))) { 609 print "$0: group \"$group\" doesn't exist\n"; 610 exit (6); 611 } 612 if (!defined($dn_line)) { 613 return 1; 614 } 615 my $dn = get_dn_from_line("$dn_line"); 616 # on look if the user is already present in the group 617 my $is_member=is_group_member($dn,$userid); 618 if ($is_member == 1) { 619 print "User \"$userid\" already member of the group \"$group\".\n"; 620 } else { 621 # bind to a directory with dn and password 622 # It does not matter if the user already exist, Net::LDAP will add the user 623 # if he does not exist, and ignore him if his already in the directory. 624 my $modify = $ldap->modify ( "$dn", 625 changes => [ 626 add => [memberUid => $userid] 627 ] 628 ); 629 $modify->code && die "failed to modify entry: ", $modify->error ; 630 } 631 } 632 633sub group_del 634 { 635 my $group_dn=shift; 636 # bind to a directory with dn and password 637 my $modify = $ldap->delete ($group_dn); 638 $modify->code && die "failed to delete group : ", $modify->error ; 639 } 640 641sub add_grouplist_user 642 { 643 my ($grouplist, $user) = @_; 644 my @array = split(/,/, $grouplist); 645 foreach my $group (@array) { 646 group_add_user($group, $user); 647 } 648 } 649 650sub disable_user 651 { 652 my $user = shift; 653 my $dn_line; 654 my $dn = get_dn_from_line($dn_line); 655 656 if (!defined($dn_line = get_user_dn($user))) { 657 print "$0: user $user doesn't exist\n"; 658 exit (10); 659 } 660 my $modify = $ldap->modify ( "$dn", 661 changes => [ 662 replace => [userPassword => '{crypt}!x'] 663 ] 664 ); 665 $modify->code && die "failed to modify entry: ", $modify->error ; 666 667 if (is_samba_user($user)) { 668 my $modify = $ldap->modify ( "$dn", 669 changes => [ 670 replace => [sambaAcctFlags => '[D ]'] 671 ] 672 ); 673 $modify->code && die "failed to modify entry: ", $modify->error ; 674 } 675 } 676 677# delete_user($user) 678sub delete_user 679 { 680 my $user = shift; 681 my $dn_line; 682 683 if (!defined($dn_line = get_user_dn($user))) { 684 print "$0: user $user doesn't exist\n"; 685 exit (10); 686 } 687 688 my $dn = get_dn_from_line($dn_line); 689 my $modify = $ldap->delete($dn); 690 } 691 692# $gid = group_add($groupname, $group_gid, $force_using_existing_gid) 693sub group_add 694 { 695 my ($gname, $gid, $force) = @_; 696 my $nscd_status = system "/etc/init.d/nscd status >/dev/null 2>&1"; 697 if ($nscd_status == 0) { 698 system "/etc/init.d/nscd stop > /dev/null 2>&1"; 699 } 700 if (!defined($gid)) { 701 #while (defined(getgrgid($config{GID_START}))) { 702 # $config{GID_START}++; 703 #} 704 #$gid = $config{GID_START}; 705 $gid=get_next_id($config{groupsdn},"gidNumber"); 706 } else { 707 if (!defined($force)) { 708 if (defined(getgrgid($gid))) { 709 return undef; 710 } 711 } 712 } 713 if ($nscd_status == 0) { 714 system "/etc/init.d/nscd start > /dev/null 2>&1"; 715 } 716 my $modify = $ldap->add ( "cn=$gname,$config{groupsdn}", 717 attrs => [ 718 objectClass => [ 'top', 'posixGroup' ], 719 cn => "$gname", 720 gidNumber => "$gid" 721 ] 722 ); 723 724 $modify->code && die "failed to add entry: ", $modify->error ; 725 return $gid; 726 } 727 728# $homedir = get_homedir ($user) 729sub get_homedir 730 { 731 my $user = shift; 732 my $homeDir=''; 733 my $entry; 734 my $mesg = $ldap->search ( 735 base =>$config{usersdn}, 736 scope => $config{scope}, 737 filter => "(&(objectclass=posixAccount)(uid=$user))" 738 ); 739 $mesg->code && die $mesg->error; 740 741 my $nb=$mesg->count; 742 if ($nb > 1) { 743 print "Aborting: there are $nb existing user named $user\n"; 744 foreach $entry ($mesg->all_entries) { 745 my $dn=$entry->dn; 746 print " $dn\n"; 747 } 748 exit (4); 749 } else { 750 $entry = $mesg->shift_entry(); 751 $homeDir= $entry->get_value("homeDirectory"); 752 } 753 754 chomp $homeDir; 755 if ($homeDir eq '') { 756 return undef; 757 } 758 return $homeDir; 759 } 760 761# search for an user 762sub read_user 763 { 764 my $user = shift; 765 my $lines =''; 766 my $mesg = $ldap->search ( # perform a search 767 base => $config{suffix}, 768 scope => $config{scope}, 769 filter => "(&(objectclass=posixAccount)(uid=$user))" 770 ); 771 772 $mesg->code && die $mesg->error; 773 foreach my $entry ($mesg->all_entries) { 774 $lines.= "dn: " . $entry->dn."\n"; 775 foreach my $attr ($entry->attributes) { 776 { 777 $lines.= $attr.": ".join(',', $entry->get_value($attr))."\n"; 778 } 779 } 780 } 781 chomp $lines; 782 if ($lines eq '') { 783 return undef; 784 } 785 return $lines; 786 } 787 788# search for a user 789# return the attributes in an array 790sub read_user_entry 791 { 792 my $user = shift; 793 my $mesg = $ldap->search ( # perform a search 794 base => $config{suffix}, 795 scope => $config{scope}, 796 filter => "(&(objectclass=posixAccount)(uid=$user))" 797 ); 798 799 $mesg->code && die $mesg->error; 800 my $entry = $mesg->entry(); 801 return $entry; 802 } 803 804# search for a group 805sub read_group 806 { 807 my $user = shift; 808 my $lines =''; 809 my $mesg = $ldap->search ( # perform a search 810 base => $config{groupsdn}, 811 scope => $config{scope}, 812 filter => "(&(objectclass=posixGroup)(cn=$user))" 813 ); 814 815 $mesg->code && die $mesg->error; 816 foreach my $entry ($mesg->all_entries) { 817 $lines.= "dn: " . $entry->dn."\n"; 818 foreach my $attr ($entry->attributes) { 819 { 820 $lines.= $attr.": ".join(',', $entry->get_value($attr))."\n"; 821 } 822 } 823 } 824 chomp $lines; 825 if ($lines eq '') { 826 return undef; 827 } 828 return $lines; 829 } 830 831# find groups of a given user 832##### MODIFIE ######## 833sub find_groups_of { 834 my $user = shift; 835 my @groups = (); 836 my $mesg = $ldap->search ( # perform a search 837 base => $config{groupsdn}, 838 scope => $config{scope}, 839 filter => "(&(objectclass=posixGroup)(memberuid=$user))" 840 ); 841 $mesg->code && die $mesg->error; 842 843 my $entry; 844 while ($entry = $mesg->shift_entry()) { 845 push(@groups, scalar($entry->get_value('cn'))); 846 } 847 return (@groups); 848} 849 850sub read_group_entry { 851 my $group = shift; 852 my $entry; 853 my %res; 854 my $mesg = $ldap->search ( # perform a search 855 base => $config{groupsdn}, 856 scope => $config{scope}, 857 filter => "(&(objectclass=posixGroup)(cn=$group))" 858 ); 859 860 $mesg->code && die $mesg->error; 861 my $nb=$mesg->count; 862 if ($nb > 1) { 863 print "Error: $nb groups exist \"cn=$group\"\n"; 864 foreach $entry ($mesg->all_entries) { 865 my $dn=$entry->dn; print " $dn\n"; 866 } 867 exit 11; 868 } else { 869 $entry = $mesg->shift_entry(); 870 } 871 return $entry; 872} 873 874sub read_group_entry_gid { 875 my $group = shift; 876 my %res; 877 my $mesg = $ldap->search ( # perform a search 878 base => $config{groupsdn}, 879 scope => $config{scope}, 880 filter => "(&(objectclass=posixGroup)(gidNumber=$group))" 881 ); 882 883 $mesg->code && die $mesg->error; 884 my $entry = $mesg->shift_entry(); 885 return $entry; 886} 887 888# return the gidnumber for a group given as name or gid 889# -1 : bad group name 890# -2 : bad gidnumber 891sub parse_group 892 { 893 my $userGidNumber = shift; 894 if ($userGidNumber =~ /[^\d]/ ) { 895 my $gname = $userGidNumber; 896 my $gidnum = getgrnam($gname); 897 if ($gidnum !~ /\d+/) { 898 return -1; 899 } else { 900 $userGidNumber = $gidnum; 901 } 902 } elsif (!defined(getgrgid($userGidNumber))) { 903 return -2; 904 } 905 return $userGidNumber; 906 } 907 908# remove $user from $group 909sub group_remove_member 910 { 911 my ($group, $user) = @_; 912 my $members=''; 913 my $grp_line = get_group_dn($group); 914 if (!defined($grp_line)) { 915 return 0; 916 } 917 my $dn = get_dn_from_line($grp_line); 918 # we test if the user exist in the group 919 my $is_member=is_group_member($dn,$user); 920 if ($is_member == 1) { 921 # delete only the user from the group 922 my $modify = $ldap->modify ( "$dn", 923 changes => [ 924 delete => [memberUid => ["$user"]] 925 ] 926 ); 927 $modify->code && die "failed to delete entry: ", $modify->error ; 928 } 929 return 1; 930 } 931 932sub group_get_members 933 { 934 my ($group) = @_; 935 my $members; 936 my @resultat; 937 my $grp_line = get_group_dn($group); 938 if (!defined($grp_line)) { 939 return 0; 940 } 941 my $mesg = $ldap->search ( 942 base => $config{groupsdn}, 943 scope => $config{scope}, 944 filter => "(&(objectclass=posixgroup)(cn=$group))" 945 ); 946 $mesg->code && die $mesg->error; 947 foreach my $entry ($mesg->all_entries) { 948 foreach my $attr ($entry->attributes) { 949 if ($attr=~/\bmemberUid\b/) { 950 foreach my $ent ($entry->get_value($attr)) { 951 push (@resultat,$ent); 952 } 953 } 954 } 955 } 956 return @resultat; 957 } 958 959sub do_ldapmodify 960 { 961 my $ldif = shift; 962 my $FILE = "|$config{ldapmodify} -r >/dev/null"; 963 open (FILE, $FILE) || die "$!\n"; 964 print FILE <<EOF; 965$ldif 966EOF 967 ; 968 close FILE; 969 my $rc = $?; 970 return $rc; 971 } 972 973sub group_type_by_name { 974 my $type_name = shift; 975 my %groupmap = ( 976 'domain' => 2, 977 'local' => 4, 978 'builtin' => 5 979 ); 980 return $groupmap{$type_name}; 981} 982 983sub subst_user 984 { 985 my ($str, $username) = @_; 986 $str =~ s/%U/$username/ if ($str); 987 return($str); 988 } 989 990# all given mails are stored in a table (remove the comma separated) 991sub split_arg_comma { 992 my $arg = shift; 993 my @args; 994 if (defined($arg)) { 995 if ($arg eq '-') { 996 @args = ( ); 997 } else { 998 @args = split(/\s*,\s*/, $arg); 999 } 1000 } 1001 return (@args); 1002} 1003 1004sub list_union { 1005 my ($list1, $list2) = @_; 1006 my @res = @$list1; 1007 foreach my $e (@$list2) { 1008 if (! grep($_ eq $e, @$list1)) { 1009 push(@res, $e); 1010 } 1011 } 1012 return @res; 1013} 1014 1015sub list_minus { 1016 my ($list1, $list2) = @_; 1017 my @res = (); 1018 foreach my $e (@$list1) { 1019 if (! grep( $_ eq $e, @$list2 )) { 1020 push(@res, $e); 1021 } 1022 } 1023 return @res; 1024} 1025 1026sub get_next_id($$) { 1027 my $ldap_base_dn = shift; 1028 my $attribute = shift; 1029 my $tries = 0; 1030 my $found=0; 1031 my $next_uid_mesg; 1032 my $nextuid; 1033 if ($ldap_base_dn =~ m/$config{usersdn}/i) { 1034 # when adding a new user, we'll check if the uidNumber available is not 1035 # already used for a computer's account 1036 $ldap_base_dn=$config{suffix} 1037 } 1038 do { 1039 $next_uid_mesg = $ldap->search( 1040 base => $config{sambaUnixIdPooldn}, 1041 filter => "(objectClass=sambaUnixIdPool)", 1042 scope => "base" 1043 ); 1044 $next_uid_mesg->code && die "Error looking for next uid"; 1045 if ($next_uid_mesg->count != 1) { 1046 die "Could not find base dn, to get next $attribute"; 1047 } 1048 my $entry = $next_uid_mesg->entry(0); 1049 1050 $nextuid = $entry->get_value($attribute); 1051 my $modify=$ldap->modify( "$config{sambaUnixIdPooldn}", 1052 changes => [ 1053 replace => [ $attribute => $nextuid + 1 ] 1054 ] 1055 ); 1056 $modify->code && die "Error: ", $modify->error; 1057 # let's check if the id found is really free (in ou=Groups or ou=Users)... 1058 my $check_uid_mesg = $ldap->search( 1059 base => $ldap_base_dn, 1060 filter => "($attribute=$nextuid)", 1061 ); 1062 $check_uid_mesg->code && die "Cannot confirm $attribute $nextuid is free"; 1063 if ($check_uid_mesg->count == 0) { 1064 $found=1; 1065 return $nextuid; 1066 } 1067 $tries++; 1068 print "Cannot confirm $attribute $nextuid is free: checking for the next one\n" 1069 } while ($found != 1); 1070 die "Could not allocate $attribute!"; 1071} 1072 1073sub utf8Encode { 1074 my $arg = shift; 1075 1076 return to_utf8( 1077 -string=> $arg, 1078 -charset => 'ISO-8859-1', 1079 ); 1080} 1081 1082sub utf8Decode { 1083 my $arg = shift; 1084 1085 return from_utf8( 1086 -string=> $arg, 1087 -charset => 'ISO-8859-1', 1088 ); 1089} 1090 10911; 1092 1093