r4528: merge Fedora spec file fixes
[Samba.git] / examples / LDAP / smbldap-tools / smbldap_tools.pm
blobd33a65b7d17355d1367ad8dbf550c5658fadb149
1 #! /usr/bin/perl -w
2 use strict;
3 package smbldap_tools;
4 use smbldap_conf;
5 use Net::LDAP;
7 # This code was developped by IDEALX (http://IDEALX.org/) and
8 # contributors (their names can be found in the CONTRIBUTORS file).
10 # Copyright (C) 2001-2002 IDEALX
12 # This program is free software; you can redistribute it and/or
13 # modify it under the terms of the GNU General Public License
14 # as published by the Free Software Foundation; either version 2
15 # of the License, or (at your option) any later version.
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
22 # You should have received a copy of the GNU General Public License
23 # along with this program; if not, write to the Free Software
24 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
25 # USA.
28 # ugly funcs using global variables and spawning openldap clients
30 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
31 use Exporter;
32 $VERSION = 1.00;
34 @ISA = qw(Exporter);
36 @EXPORT = qw(
37 get_user_dn
38 get_group_dn
39 is_group_member
40 is_samba_user
41 is_unix_user
42 is_user_valid
43 does_sid_exist
44 get_dn_from_line
45 add_posix_machine
46 add_samba_machine
47 add_samba_machine_mkntpwd
48 group_add_user
49 add_grouplist_user
50 disable_user
51 delete_user
52 group_add
53 group_del
54 get_homedir
55 read_user
56 read_user_entry
57 read_group
58 read_group_entry
59 read_group_entry_gid
60 find_groups_of
61 parse_group
62 group_remove_member
63 group_get_members
64 do_ldapadd
65 do_ldapmodify
66 get_user_dn2
67 connect_ldap_master
68 connect_ldap_slave
69 group_type_by_name
72 sub connect_ldap_master
74 # bind to a directory with dn and password
75 my $ldap_master = Net::LDAP->new(
76 "$masterLDAP",
77 port => "$masterPort",
78 version => 3,
79 # debug => 0xffff,
81 or die "erreur LDAP: Can't contact master ldap server ($@)";
82 if ($ldapSSL == 1) {
83 $ldap_master->start_tls(
84 # verify => 'require',
85 # clientcert => 'mycert.pem',
86 # clientkey => 'mykey.pem',
87 # decryptkey => sub { 'secret'; },
88 # capath => '/usr/local/cacerts/'
91 $ldap_master->bind ( "$binddn",
92 password => "$masterPw"
94 return($ldap_master);
97 sub connect_ldap_slave
99 # bind to a directory with dn and password
100 my $ldap_slave = Net::LDAP->new(
101 "$slaveLDAP",
102 port => "$slavePort",
103 version => 3,
104 # debug => 0xffff,
106 or die "erreur LDAP: Can't contact slave ldap server ($@)";
107 if ($ldapSSL == 1) {
108 $ldap_slave->start_tls(
109 # verify => 'require',
110 # clientcert => 'mycert.pem',
111 # clientkey => 'mykey.pem',
112 # decryptkey => sub { 'secret'; },
113 # capath => '/usr/local/cacerts/'
116 $ldap_slave->bind ( "$binddn",
117 password => "$slavePw"
119 return($ldap_slave);
122 sub get_user_dn
124 my $user = shift;
125 my $dn='';
126 my $ldap_slave=connect_ldap_slave();
127 my $mesg = $ldap_slave->search ( base => $suffix,
128 scope => $scope,
129 filter => "(&(objectclass=posixAccount)(uid=$user))"
131 $mesg->code && die $mesg->error;
132 foreach my $entry ($mesg->all_entries) {
133 $dn= $entry->dn;
135 $ldap_slave->unbind;
136 chomp($dn);
137 if ($dn eq '') {
138 return undef;
140 $dn="dn: ".$dn;
141 return $dn;
145 sub get_user_dn2
147 my $user = shift;
148 my $dn='';
149 my $ldap_slave=connect_ldap_slave();
150 my $mesg = $ldap_slave->search ( base => $suffix,
151 scope => $scope,
152 filter => "(&(objectclass=posixAccount)(uid=$user))"
154 $mesg->code && warn "failed to perform search; ", $mesg->error;
156 foreach my $entry ($mesg->all_entries) {
157 $dn= $entry->dn;
159 $ldap_slave->unbind;
160 chomp($dn);
161 if ($dn eq '') {
162 return (1,undef);
164 $dn="dn: ".$dn;
165 return (1,$dn);
169 sub get_group_dn
171 my $group = shift;
172 my $dn='';
173 my $ldap_slave=connect_ldap_slave();
174 my $mesg = $ldap_slave->search ( base => $groupsdn,
175 scope => $scope,
176 filter => "(&(objectclass=posixGroup)(|(cn=$group)(gidNumber=$group)))"
178 $mesg->code && die $mesg->error;
179 foreach my $entry ($mesg->all_entries) {
180 $dn= $entry->dn;
182 $ldap_slave->unbind;
183 chomp($dn);
184 if ($dn eq '') {
185 return undef;
187 $dn="dn: ".$dn;
188 return $dn;
191 # return (success, dn)
192 # bool = is_samba_user($username)
193 sub is_samba_user
195 my $user = shift;
196 my $ldap_slave=connect_ldap_slave();
197 my $mesg = $ldap_slave->search ( base => $suffix,
198 scope => $scope,
199 filter => "(&(objectClass=sambaSamAccount)(uid=$user))"
201 $mesg->code && die $mesg->error;
202 $ldap_slave->unbind;
203 return ($mesg->count ne 0);
206 sub is_unix_user
208 my $user = shift;
209 my $ldap_slave=connect_ldap_slave();
210 my $mesg = $ldap_slave->search ( base => $suffix,
211 scope => $scope,
212 filter => "(&(objectClass=posixAccount)(uid=$user))"
214 $mesg->code && die $mesg->error;
215 $ldap_slave->unbind;
216 return ($mesg->count ne 0);
219 sub is_group_member
221 my $dn_group = shift;
222 my $user = shift;
223 my $ldap_slave=connect_ldap_slave();
224 my $mesg = $ldap_slave->search ( base => $dn_group,
225 scope => 'base',
226 filter => "(&(memberUid=$user))"
228 $mesg->code && die $mesg->error;
229 $ldap_slave->unbind;
230 return ($mesg->count ne 0);
233 # all entries = does_sid_exist($sid,$scope)
234 sub does_sid_exist
236 my $sid = shift;
237 my $dn_group=shift;
238 my $ldap_slave=connect_ldap_slave();
239 my $mesg = $ldap_slave->search ( base => $dn_group,
240 scope => $scope,
241 filter => "(sambaSID=$sid)"
242 #filter => "(&(objectClass=sambaSamAccount|objectClass=sambaGroupMapping)(sambaSID=$sid))"
244 $mesg->code && die $mesg->error;
245 $ldap_slave->unbind;
246 return ($mesg);
249 # try to bind with user dn and password to validate current password
250 sub is_user_valid
252 my ($user, $dn, $pass) = @_;
253 my $ldap = Net::LDAP->new($slaveLDAP) or die "erreur LDAP";
254 my $mesg= $ldap->bind (dn => $dn, password => $pass );
255 if ($mesg->code eq 0) {
256 $ldap->unbind;
257 return 1;
258 } else {
259 if ($ldap->bind()) {
260 $ldap->unbind;
261 return 0;
262 } else {
263 print ("The LDAP directory is not available.\n Check the server, cables ...");
264 $ldap->unbind;
265 return 0;
267 die "Problem : contact your administrator";
272 # dn = get_dn_from_line ($dn_line)
273 # helper to get "a=b,c=d" from "dn: a=b,c=d"
274 sub get_dn_from_line
276 my $dn = shift;
277 $dn =~ s/^dn: //;
278 return $dn;
282 # success = add_posix_machine($user, $uid, $gid)
283 sub add_posix_machine
285 my ($user, $uid, $gid) = @_;
286 # bind to a directory with dn and password
287 my $ldap_master=connect_ldap_master();
288 my $add = $ldap_master->add ( "uid=$user,$computersdn",
289 attr => [
290 'objectclass' => ['top','inetOrgPerson', 'posixAccount'],
291 'cn' => "$user",
292 'sn' => "$user",
293 'uid' => "$user",
294 'uidNumber' => "$uid",
295 'gidNumber' => "$gid",
296 'homeDirectory' => '/dev/null',
297 'loginShell' => '/bin/false',
298 'description' => 'Computer',
302 $add->code && warn "failed to add entry: ", $add->error ;
303 # take down the session
304 $ldap_master->unbind;
309 # success = add_samba_machine($computername)
310 sub add_samba_machine
312 my $user = shift;
313 system "smbpasswd -a -m $user";
314 return 1;
317 sub add_samba_machine_mkntpwd
319 my ($user, $uid) = @_;
320 my $sambaSID = 2 * $uid + 1000;
321 my $name = $user;
322 $name =~ s/.$//s;
324 if ($mk_ntpasswd eq '') {
325 print "Either set \$with_smbpasswd = 1 or specify \$mk_ntpasswd\n";
326 return 0;
329 my $ntpwd = `$mk_ntpasswd '$name'`;
330 chomp(my $lmpassword = substr($ntpwd, 0, index($ntpwd, ':')));
331 chomp(my $ntpassword = substr($ntpwd, index($ntpwd, ':')+1));
333 my $ldap_master=connect_ldap_master();
334 my $modify = $ldap_master->modify ( "uid=$user,$computersdn",
335 changes => [
336 replace => [objectClass => ['inetOrgPerson', 'posixAccount', 'sambaSamAccount']],
337 add => [sambaPwdLastSet => '0'],
338 add => [sambaLogonTime => '0'],
339 add => [sambaLogoffTime => '2147483647'],
340 add => [sambaKickoffTime => '2147483647'],
341 add => [sambaPwdCanChange => '0'],
342 add => [sambaPwdMustChange => '0'],
343 add => [sambaAcctFlags => '[W ]'],
344 add => [sambaLMPassword => "$lmpassword"],
345 add => [sambaNTPassword => "$ntpassword"],
346 add => [sambaSID => "$SID-$sambaSID"],
347 add => [sambaPrimaryGroupSID => "$SID-0"]
351 $modify->code && die "failed to add entry: ", $modify->error ;
353 return 1;
354 # take down the session
355 $ldap_master->unbind;
360 sub group_add_user
362 my ($group, $userid) = @_;
363 my $members='';
364 my $dn_line = get_group_dn($group);
365 if (!defined(get_group_dn($group))) {
366 print "$0: group \"$group\" doesn't exist\n";
367 exit (6);
369 if (!defined($dn_line)) {
370 return 1;
372 my $dn = get_dn_from_line("$dn_line");
373 # on look if the user is already present in the group
374 my $is_member=is_group_member($dn,$userid);
375 if ($is_member == 1) {
376 print "User \"$userid\" already member of the group \"$group\".\n";
377 } else {
378 # bind to a directory with dn and password
379 my $ldap_master=connect_ldap_master();
380 # It does not matter if the user already exist, Net::LDAP will add the user
381 # if he does not exist, and ignore him if his already in the directory.
382 my $modify = $ldap_master->modify ( "$dn",
383 changes => [
384 add => [memberUid => $userid]
387 $modify->code && die "failed to modify entry: ", $modify->error ;
388 # take down session
389 $ldap_master->unbind;
393 sub group_del
395 my $group_dn=shift;
396 # bind to a directory with dn and password
397 my $ldap_master=connect_ldap_master();
398 my $modify = $ldap_master->delete ($group_dn);
399 $modify->code && die "failed to delete group : ", $modify->error ;
400 # take down session
401 $ldap_master->unbind;
404 sub add_grouplist_user
406 my ($grouplist, $user) = @_;
407 my @array = split(/,/, $grouplist);
408 foreach my $group (@array) {
409 group_add_user($group, $user);
413 sub disable_user
415 my $user = shift;
416 my $dn_line;
417 my $dn = get_dn_from_line($dn_line);
419 if (!defined($dn_line = get_user_dn($user))) {
420 print "$0: user $user doesn't exist\n";
421 exit (10);
423 my $ldap_master=connect_ldap_master();
424 my $modify = $ldap_master->modify ( "$dn",
425 changes => [
426 replace => [userPassword => '{crypt}!x']
429 $modify->code && die "failed to modify entry: ", $modify->error ;
431 if (is_samba_user($user)) {
432 my $modify = $ldap_master->modify ( "$dn",
433 changes => [
434 replace => [sambaAcctFlags => '[D ]']
437 $modify->code && die "failed to modify entry: ", $modify->error ;
439 # take down session
440 $ldap_master->unbind;
443 # delete_user($user)
444 sub delete_user
446 my $user = shift;
447 my $dn_line;
449 if (!defined($dn_line = get_user_dn($user))) {
450 print "$0: user $user doesn't exist\n";
451 exit (10);
454 my $dn = get_dn_from_line($dn_line);
455 my $ldap_master=connect_ldap_master();
456 my $modify = $ldap_master->delete($dn);
457 $ldap_master->unbind;
460 # $gid = group_add($groupname, $group_gid, $force_using_existing_gid)
461 sub group_add
463 my ($gname, $gid, $force) = @_;
464 my $nscd_status = system "/etc/init.d/nscd status >/dev/null 2>&1";
465 if ($nscd_status == 0) {
466 system "/etc/init.d/nscd stop > /dev/null 2>&1";
468 if (!defined($gid)) {
469 while (defined(getgrgid($GID_START))) {
470 $GID_START++;
472 $gid = $GID_START;
473 } else {
474 if (!defined($force)) {
475 if (defined(getgrgid($gid))) {
476 return undef;
480 if ($nscd_status == 0) {
481 system "/etc/init.d/nscd start > /dev/null 2>&1";
483 my $ldap_master=connect_ldap_master();
484 my $modify = $ldap_master->add ( "cn=$gname,$groupsdn",
485 attrs => [
486 objectClass => 'posixGroup',
487 cn => "$gname",
488 gidNumber => "$gid"
492 $modify->code && die "failed to add entry: ", $modify->error ;
493 # take down session
494 $ldap_master->unbind;
495 return $gid;
498 # $homedir = get_homedir ($user)
499 sub get_homedir
501 my $user = shift;
502 my $homeDir='';
503 my $ldap_slave=connect_ldap_slave();
504 my $mesg = $ldap_slave->search (
505 base =>$suffix,
506 scope => $scope,
507 filter => "(&(objectclass=posixAccount)(uid=$user))"
509 $mesg->code && die $mesg->error;
510 foreach my $entry ($mesg->all_entries) {
511 foreach my $attr ($entry->attributes) {
512 if ($attr=~/\bhomeDirectory\b/) {
513 foreach my $ent ($entry->get_value($attr)) {
514 $homeDir.= $attr.": ".$ent."\n";
519 $ldap_slave->unbind;
520 chomp $homeDir;
521 if ($homeDir eq '') {
522 return undef;
524 $homeDir =~ s/^homeDirectory: //;
525 return $homeDir;
528 # search for an user
529 sub read_user
531 my $user = shift;
532 my $lines ='';
533 my $ldap_slave=connect_ldap_slave();
534 my $mesg = $ldap_slave->search ( # perform a search
535 base => $suffix,
536 scope => $scope,
537 filter => "(&(objectclass=posixAccount)(uid=$user))"
540 $mesg->code && die $mesg->error;
541 foreach my $entry ($mesg->all_entries) {
542 $lines.= "dn: " . $entry->dn."\n";
543 foreach my $attr ($entry->attributes) {
545 $lines.= $attr.": ".join(',', $entry->get_value($attr))."\n";
549 # take down session
550 $ldap_slave->unbind;
551 chomp $lines;
552 if ($lines eq '') {
553 return undef;
555 return $lines;
558 # search for a user
559 # return the attributes in an array
560 sub read_user_entry
562 my $user = shift;
563 my $ldap_slave=connect_ldap_slave();
564 my $mesg = $ldap_slave->search ( # perform a search
565 base => $suffix,
566 scope => $scope,
567 filter => "(&(objectclass=posixAccount)(uid=$user))"
570 $mesg->code && die $mesg->error;
571 my $entry = $mesg->entry();
572 $ldap_slave->unbind;
573 return $entry;
576 # search for a group
577 sub read_group
579 my $user = shift;
580 my $lines ='';
581 my $ldap_slave=connect_ldap_slave();
582 my $mesg = $ldap_slave->search ( # perform a search
583 base => $groupsdn,
584 scope => $scope,
585 filter => "(&(objectclass=posixGroup)(cn=$user))"
588 $mesg->code && die $mesg->error;
589 foreach my $entry ($mesg->all_entries) {
590 $lines.= "dn: " . $entry->dn."\n";
591 foreach my $attr ($entry->attributes) {
593 $lines.= $attr.": ".join(',', $entry->get_value($attr))."\n";
597 # take down session
598 $ldap_slave->unbind;
599 chomp $lines;
600 if ($lines eq '') {
601 return undef;
603 return $lines;
606 # find groups of a given user
607 ##### MODIFIE ########
608 sub find_groups_of
610 my $user = shift;
611 my $lines ='';
612 my $ldap_slave=connect_ldap_slave;
613 my $mesg = $ldap_slave->search ( # perform a search
614 base => $groupsdn,
615 scope => $scope,
616 filter => "(&(objectclass=posixGroup)(memberuid=$user))"
618 $mesg->code && die $mesg->error;
619 foreach my $entry ($mesg->all_entries) {
620 $lines.= "dn: ".$entry->dn."\n";
622 $ldap_slave->unbind;
623 chomp($lines);
624 if ($lines eq '') {
625 return undef;
627 return $lines;
630 sub read_group_entry {
631 my $group = shift;
632 my $entry;
633 my %res;
634 my $ldap_slave=connect_ldap_slave();
635 my $mesg = $ldap_slave->search ( # perform a search
636 base => $groupsdn,
637 scope => $scope,
638 filter => "(&(objectclass=posixGroup)(cn=$group))"
641 $mesg->code && die $mesg->error;
642 my $nb=$mesg->count;
643 if ($nb > 1) {
644 print "Error: $nb groups exist \"cn=$group\"\n";
645 foreach $entry ($mesg->all_entries) { my $dn=$entry->dn; print " $dn\n"; }
646 exit 11;
647 } else {
648 $entry = $mesg->shift_entry();
650 return $entry;
653 sub read_group_entry_gid {
654 my $group = shift;
655 my %res;
656 my $ldap_slave=connect_ldap_slave();
657 my $mesg = $ldap_slave->search ( # perform a search
658 base => $groupsdn,
659 scope => $scope,
660 filter => "(&(objectclass=posixGroup)(gidNumber=$group))"
663 $mesg->code && die $mesg->error;
664 my $entry = $mesg->shift_entry();
665 return $entry;
668 # return the gidnumber for a group given as name or gid
669 # -1 : bad group name
670 # -2 : bad gidnumber
671 sub parse_group
673 my $userGidNumber = shift;
674 if ($userGidNumber =~ /[^\d]/ ) {
675 my $gname = $userGidNumber;
676 my $gidnum = getgrnam($gname);
677 if ($gidnum !~ /\d+/) {
678 return -1;
679 } else {
680 $userGidNumber = $gidnum;
682 } elsif (!defined(getgrgid($userGidNumber))) {
683 return -2;
685 return $userGidNumber;
688 # remove $user from $group
689 sub group_remove_member
691 my ($group, $user) = @_;
692 my $members='';
693 my $grp_line = get_group_dn($group);
694 if (!defined($grp_line)) {
695 return 0;
697 my $dn = get_dn_from_line($grp_line);
698 # we test if the user exist in the group
699 my $is_member=is_group_member($dn,$user);
700 if ($is_member == 1) {
701 my $ldap_master=connect_ldap_master();
702 # delete only the user from the group
703 my $modify = $ldap_master->modify ( "$dn",
704 changes => [
705 delete => [memberUid => ["$user"]]
708 $modify->code && die "failed to delete entry: ", $modify->error ;
709 $ldap_master->unbind;
711 return 1;
714 sub group_get_members
716 my ($group) = @_;
717 my $members;
718 my @resultat;
719 my $grp_line = get_group_dn($group);
720 if (!defined($grp_line)) {
721 return 0;
724 my $ldap = Net::LDAP->new($slaveLDAP) or die "erreur LDAP";
725 $ldap->bind ;
726 my $mesg = $ldap->search (
727 base => $groupsdn,
728 scope => $scope,
729 filter => "(&(objectclass=posixgroup)(cn=$group))"
731 $mesg->code && die $mesg->error;
732 foreach my $entry ($mesg->all_entries) {
733 foreach my $attr ($entry->attributes) {
734 if ($attr=~/\bmemberUid\b/) {
735 foreach my $ent ($entry->get_value($attr)) {
736 push (@resultat,$ent);
741 return @resultat;
744 sub do_ldapmodify
746 my $ldif = shift;
747 my $FILE = "|$ldapmodify -r >/dev/null";
748 open (FILE, $FILE) || die "$!\n";
749 print FILE <<EOF;
750 $ldif
753 close FILE;
754 my $rc = $?;
755 return $rc;
758 sub group_type_by_name {
759 my $type_name = shift;
760 my %groupmap = (
761 'domain' => 2,
762 'local' => 4,
763 'builtin' => 5
765 return $groupmap{$type_name};