syncing up docs, examples, & packaging from 3.0
[bbaumbach/samba-autobuild/.git] / examples / LDAP / smbldap-tools / smbldap_tools.pm
1 #! /usr/bin/perl -w
2 use strict;
3 package smbldap_tools;
4 use smbldap_conf;
5 use Net::LDAP;
6
7 #  This code was developped by IDEALX (http://IDEALX.org/) and
8 #  contributors (their names can be found in the CONTRIBUTORS file).
9 #
10 #                 Copyright (C) 2001-2002 IDEALX
11 #
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.
16 #
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.
21 #
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.
26
27
28 # ugly funcs using global variables and spawning openldap clients
29
30 use vars       qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
31 use Exporter;
32 $VERSION = 1.00;
33
34 @ISA = qw(Exporter);
35
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 get_dn_from_line
44 add_posix_machine
45 add_samba_machine
46 add_samba_machine_mkntpwd
47 group_add_user
48 add_grouplist_user
49 disable_user
50 delete_user
51 group_add
52                          group_del
53 get_homedir
54 read_user
55                          read_user_entry
56 read_group
57 find_groups_of
58 parse_group
59 group_remove_member
60 group_get_members
61 do_ldapadd
62 do_ldapmodify
63 get_user_dn2
64                          connect_ldap_master
65                          connect_ldap_slave
66 );
67
68 sub connect_ldap_master
69   {
70         # bind to a directory with dn and password
71         my $ldap_master = Net::LDAP->new(
72                                                                          "$masterLDAP",
73                                                                          port => "$masterPort",
74                                                                          version => 3,
75                                                                          # debug => 0xffff,
76                                                                         )
77           or die "erreur LDAP: Can't contact master ldap server ($@)";
78         if ($ldapSSL == 1) {
79           $ldap_master->start_tls(
80                                                           # verify => 'require',
81                                                           # clientcert => 'mycert.pem',
82                                                           # clientkey => 'mykey.pem',
83                                                           # decryptkey => sub { 'secret'; },
84                                                           # capath => '/usr/local/cacerts/'
85                                                          );
86         }
87         $ldap_master->bind ( "$binddn",
88                                                  password => "$masterPw"
89                                            );
90         return($ldap_master);
91   }
92
93 sub connect_ldap_slave
94   {
95         # bind to a directory with dn and password
96         my $ldap_slave = Net::LDAP->new(
97                                                                         "$slaveLDAP",
98                                                                         port => "$slavePort",
99                                                                         version => 3,
100                                                                         # debug => 0xffff,
101                                                                    )
102           or die "erreur LDAP: Can't contact slave ldap server ($@)";
103         if ($ldapSSL == 1) {
104           $ldap_slave->start_tls(
105                                                          # verify => 'require',
106                                                          # clientcert => 'mycert.pem',
107                                                          # clientkey => 'mykey.pem',
108                                                          # decryptkey => sub { 'secret'; },
109                                                          # capath => '/usr/local/cacerts/'
110                                                         );
111         }
112         $ldap_slave->bind ( "$binddn",
113                                                 password => "$slavePw"
114                                           );
115         return($ldap_slave);
116   }
117
118 sub get_user_dn
119 {
120     my $user = shift;
121     my $dn='';
122     my $ldap_slave=connect_ldap_slave();
123     my  $mesg = $ldap_slave->search (    base   => $suffix,
124                                    scope => $scope,
125                                    filter => "(&(objectclass=posixAccount)(uid=$user))"
126                               );
127     $mesg->code && die $mesg->error;
128     foreach my $entry ($mesg->all_entries) {
129           $dn= $entry->dn;
130         }
131     $ldap_slave->unbind;
132     chomp($dn);
133     if ($dn eq '') {
134         return undef;
135     }
136     $dn="dn: ".$dn;
137     return $dn;
138 }
139
140
141 sub get_user_dn2
142 {
143     my $user = shift;
144     my $dn='';
145     my $ldap_slave=connect_ldap_slave();
146     my  $mesg = $ldap_slave->search (    base   => $suffix,
147                                    scope => $scope,
148                                filter => "(&(objectclass=posixAccount)(uid=$user))"
149                               );
150     $mesg->code && warn "failed to perform search; ", $mesg->error;
151
152     foreach my $entry ($mesg->all_entries) {
153         $dn= $entry->dn;
154     }
155     $ldap_slave->unbind;
156     chomp($dn);
157     if ($dn eq '') {
158         return (1,undef);
159     }
160     $dn="dn: ".$dn;
161     return (1,$dn);
162 }
163
164
165 sub get_group_dn
166   {
167       my $group = shift;
168       my $dn='';
169         my $ldap_slave=connect_ldap_slave();
170         my  $mesg = $ldap_slave->search (    base   => $groupsdn,
171                                      scope => $scope,
172                                      filter => "(&(objectclass=posixGroup)(|(cn=$group)(gidNumber=$group)))"
173                                 );
174       $mesg->code && die $mesg->error;
175       foreach my $entry ($mesg->all_entries) {
176           $dn= $entry->dn;
177         }
178         $ldap_slave->unbind;
179       chomp($dn);
180       if ($dn eq '') {
181           return undef;
182       }
183       $dn="dn: ".$dn;
184       return $dn;
185   }
186
187 # return (success, dn)
188 # bool = is_samba_user($username)
189 sub is_samba_user
190   {
191       my $user = shift;
192         my $ldap_slave=connect_ldap_slave();
193         my $mesg = $ldap_slave->search (    base   => $suffix,
194                                     scope => $scope,
195                                     filter => "(&(objectClass=sambaSamAccount)(uid=$user))"
196                                );
197       $mesg->code && die $mesg->error;
198         $ldap_slave->unbind;
199         return ($mesg->count ne 0);
200   }
201
202 sub is_unix_user
203   {
204         my $user = shift;
205         my $ldap_slave=connect_ldap_slave();
206         my $mesg = $ldap_slave->search (    base   => $suffix,
207                                                                                 scope => $scope,
208                                                                                 filter => "(&(objectClass=posixAccount)(uid=$user))"
209                                                                    );
210         $mesg->code && die $mesg->error;
211         $ldap_slave->unbind;
212         return ($mesg->count ne 0);
213   }
214
215 sub is_group_member
216   {
217         my $dn_group = shift;
218         my $user = shift;
219         my $ldap_slave=connect_ldap_slave();
220         my $mesg = $ldap_slave->search (
221                                                                         base   => "$dn_group",
222                                                                         scope => 'base',
223                                                                         filter => "(&(memberUid=$user))"
224                                                                    );
225         $mesg->code && die $mesg->error;
226         $ldap_slave->unbind;
227       return ($mesg->count ne 0);
228   }
229
230
231 # try to bind with user dn and password to validate current password
232 sub is_user_valid
233   {
234       my ($user, $dn, $pass) = @_;
235       my $ldap = Net::LDAP->new($slaveLDAP) or die "erreur LDAP";
236       my $mesg= $ldap->bind (dn => $dn, password => $pass );
237         if ($mesg->code eq 0) {
238             $ldap->unbind;
239             return 1;
240         } else {
241             if($ldap->bind()) {
242                 $ldap->unbind;
243                 return 0;
244             } else {
245                 print ("The LDAP directory is not available.\n Check the server, cables ...");
246                 $ldap->unbind;
247                 return 0;
248           }
249           die "Problem : contact your administrator";
250         }
251 }
252
253 # dn = get_dn_from_line ($dn_line)
254 # helper to get "a=b,c=d" from "dn: a=b,c=d"
255 sub get_dn_from_line
256   {
257       my $dn = shift;
258       $dn =~ s/^dn: //;
259       return $dn;
260   }
261
262 # success = add_posix_machine($user, $uid, $gid)
263 sub add_posix_machine
264   {
265       my ($user, $uid, $gid) = @_;
266         # bind to a directory with dn and password
267         my $ldap_master=connect_ldap_master();
268         my $add = $ldap_master->add ( "uid=$user,$computersdn",
269                                                                   attr => [
270                                                                                    'objectclass' => ['top','inetOrgPerson', 'posixAccount'],
271                                                                                    'cn'   => "$user",
272                                                                                    'sn'   => "$user",
273                                                                                    'uid'   => "$user",
274                                                                                    'uidNumber'   => "$uid",
275                                                                                    'gidNumber'   => "$gid",
276                                                                                    'homeDirectory'   => '/dev/null',
277                                                                                    'loginShell'   => '/bin/false',
278                                                                                    'description'   => 'Computer',
279                                                                                   ]
280                                                                 );
281         
282         $add->code && warn "failed to add entry: ", $add->error ;
283         # take down the session
284         $ldap_master->unbind;
285
286   }
287
288
289 # success = add_samba_machine($computername)
290 sub add_samba_machine
291 {
292     my $user = shift;
293     system "smbpasswd -a -m $user";
294     return 1;
295 }
296
297 sub add_samba_machine_mkntpwd
298   {
299       my ($user, $uid) = @_;
300       my $sambaSID = 2 * $uid + 1000;
301       my $name = $user;
302       $name =~ s/.$//s;
303
304       if ($mk_ntpasswd eq '') {
305           print "Either set \$with_smbpasswd = 1 or specify \$mk_ntpasswd\n";
306           return 0;
307       }
308
309       my $ntpwd = `$mk_ntpasswd '$name'`;
310       chomp(my $lmpassword = substr($ntpwd, 0, index($ntpwd, ':')));
311       chomp(my $ntpassword = substr($ntpwd, index($ntpwd, ':')+1));
312
313         my $ldap_master=connect_ldap_master();
314         my $modify = $ldap_master->modify ( "uid=$user,$computersdn",
315                                                                                 changes => [
316                                                                                                         replace => [objectClass => ['inetOrgPerson', 'posixAccount', 'sambaSAMAccount']],
317                                                                                                         add => [sambaPwdLastSet => '0'],
318                                                                                                         add => [sambaLogonTime => '0'],
319                                                                                                         add => [sambaLogoffTime => '2147483647'],
320                                                                                                         add => [sambaKickoffTime => '2147483647'],
321                                                                                                         add => [sambaPwdCanChange => '0'],
322                                                                                                         add => [sambaPwdMustChange => '0'],
323                                                                                                         add => [sambaAcctFlags => '[W          ]'],
324                                                                                                         add => [sambaLMPassword => "$lmpassword"],
325                                                                                                         add => [sambaNTPassword => "$ntpassword"],
326                                                                                                         add => [sambaSID => "$SID-$sambaSID"],
327                                                                                                         add => [sambaPrimaryGroupSID => "$SID-0"]
328                                                                                                    ]
329                                                                           );
330         
331         $modify->code && die "failed to add entry: ", $modify->error ;
332
333       return 1;
334         # take down the session
335         $ldap_master->unbind;
336
337   }
338
339
340 sub group_add_user
341   {
342       my ($group, $userid) = @_;
343       my $members='';
344       my $dn_line = get_group_dn($group);
345         if (!defined(get_group_dn($group))) {
346           print "$0: group \"$group\" doesn't exist\n";
347           exit (6); 
348         }
349       if (!defined($dn_line)) {
350           return 1;
351       }
352         my $dn = get_dn_from_line("$dn_line");
353         # on look if the user is already present in the group
354         my $is_member=is_group_member($dn,$userid);
355         if ($is_member == 1) {
356           print "User \"$userid\" already member of the group \"$group\".\n";
357         } else {
358           # bind to a directory with dn and password
359           my $ldap_master=connect_ldap_master();
360           # It does not matter if the user already exist, Net::LDAP will add the user
361           # if he does not exist, and ignore him if his already in the directory.
362           my $modify = $ldap_master->modify ( "$dn",
363                                                                                   changes => [
364                                                                                                           add => [memberUid => $userid]
365                                                                                                          ]
366                                                                                 );
367           $modify->code && die "failed to modify entry: ", $modify->error ;
368           # take down session
369           $ldap_master->unbind;
370             }
371       }
372
373 sub group_del
374   {
375         my $group_dn=shift;
376         # bind to a directory with dn and password
377         my $ldap_master=connect_ldap_master();
378         my $modify = $ldap_master->delete ($group_dn);
379         $modify->code && die "failed to delete group : ", $modify->error ;
380         # take down session
381         $ldap_master->unbind;
382   }
383
384 sub add_grouplist_user
385   {
386       my ($grouplist, $user) = @_;
387       my @array = split(/,/, $grouplist);
388       foreach my $group (@array) {
389           group_add_user($group, $user);
390       }
391   }
392
393 sub disable_user
394   {
395       my $user = shift;
396       my $dn_line;
397         my $dn = get_dn_from_line($dn_line);
398
399       if (!defined($dn_line = get_user_dn($user))) {
400           print "$0: user $user doesn't exist\n";
401           exit (10);
402       }
403         my $ldap_master=connect_ldap_master();
404         my $modify = $ldap_master->modify ( "$dn",
405                                                                                 changes => [
406                                                                                                         replace => [userPassword => '{crypt}!x']
407                                                                                                    ]
408                                                                           );
409         $modify->code && die "failed to modify entry: ", $modify->error ;
410
411       if (is_samba_user($user)) {
412           my $modify = $ldap_master->modify ( "$dn",
413                                                                                   changes => [
414                                                                                                           replace => [sambaAcctFlags => '[D       ]']
415                                                                                                          ]
416                                                                                 );
417           $modify->code && die "failed to modify entry: ", $modify->error ;
418       }
419         # take down session
420         $ldap_master->unbind;
421   }
422
423 # delete_user($user)
424 sub delete_user
425   {
426       my $user = shift;
427       my $dn_line;
428
429       if (!defined($dn_line = get_user_dn($user))) {
430           print "$0: user $user doesn't exist\n";
431           exit (10);
432       }
433
434       my $dn = get_dn_from_line($dn_line);
435         my $ldap_master=connect_ldap_master();
436         my $modify = $ldap_master->delete($dn);
437         $ldap_master->unbind;
438   }
439
440 # $success = group_add($groupname, $group_gid, $force_using_existing_gid)
441 sub group_add
442   {
443       my ($gname, $gid, $force) = @_;
444       my $nscd_status = system "/etc/init.d/nscd status >/dev/null 2>&1";
445       if ($nscd_status == 0) {
446           system "/etc/init.d/nscd stop > /dev/null 2>&1";
447       }
448       if (!defined($gid)) {
449           while (defined(getgrgid($GID_START))) {
450               $GID_START++;
451           }
452           $gid = $GID_START;
453       } else {
454           if (!defined($force)) {
455               if (defined(getgrgid($gid))) {
456                   return 0;
457               }
458           }
459       }
460       if ($nscd_status == 0) {
461           system "/etc/init.d/nscd start > /dev/null 2>&1";
462       }
463         my $ldap_master=connect_ldap_master();
464         my $modify = $ldap_master->add ( "cn=$gname,$groupsdn",
465                                                                          attrs => [
466                                                                                            objectClass => 'posixGroup',
467                                                                                            cn => "$gname",
468                                                                                            gidNumber => "$gid"
469                                                                                           ]
470                                                                    );
471         
472         $modify->code && die "failed to add entry: ", $modify->error ;
473         # take down session
474         $ldap_master->unbind;
475       return 1;
476   }
477
478 # $homedir = get_homedir ($user)
479 sub get_homedir
480   {
481       my $user = shift;
482       my $homeDir='';
483         my $ldap_slave=connect_ldap_slave();
484         my  $mesg = $ldap_slave->search (
485                                                                          base   =>$suffix,
486                                                                          scope => $scope,
487                                                                          filter => "(&(objectclass=posixAccount)(uid=$user))"
488                                                                         );
489       $mesg->code && die $mesg->error;
490       foreach my $entry ($mesg->all_entries){
491           foreach my $attr ($entry->attributes) {
492                 if ($attr=~/\bhomeDirectory\b/){
493                     foreach my $ent($entry->get_value($attr)) {
494                         $homeDir.= $attr.": ".$ent."\n";
495                     }
496                 }
497             }
498       }
499         $ldap_slave->unbind;
500       chomp $homeDir;
501       if ($homeDir eq '') {
502           return undef;
503       }
504       $homeDir =~ s/^homeDirectory: //;
505       return $homeDir;
506   }
507
508 # search for an user
509 sub read_user
510   {
511       my $user = shift;
512       my $lines ='';
513         my $ldap_slave=connect_ldap_slave();
514         my $mesg = $ldap_slave->search ( # perform a search
515                                  base   => $suffix,
516                                  scope => $scope,
517                                  filter => "(&(objectclass=posixAccount)(uid=$user))"
518                                 );
519
520       $mesg->code && die $mesg->error;
521       foreach my $entry ($mesg->all_entries) {
522           $lines.= "dn: " . $entry->dn."\n";
523           foreach my $attr ($entry->attributes) {
524               {
525                   $lines.= $attr.": ".join(',', $entry->get_value($attr))."\n";
526               }
527           }
528       }
529         # take down session
530         $ldap_slave->unbind;
531       chomp $lines;
532       if ($lines eq '') {
533           return undef;
534       }
535       return $lines;
536   }
537
538 # search for a user
539 # return the attributes in an array
540 sub read_user_entry
541   {
542         my $user = shift;
543         my $ldap_slave=connect_ldap_slave();
544         my  $mesg = $ldap_slave->search ( # perform a search
545                                                                          base   => $suffix,
546                                                                          scope => $scope,
547                                                                          filter => "(&(objectclass=posixAccount)(uid=$user))"
548                                                                         );
549
550         $mesg->code && die $mesg->error;
551         my $entry = $mesg->entry();
552         $ldap_slave->unbind;
553         return $entry;
554   }
555
556 # search for a group
557 sub read_group
558   {
559       my $user = shift;
560       my $lines ='';
561         my $ldap_slave=connect_ldap_slave();
562         my  $mesg = $ldap_slave->search ( # perform a search
563                                  base   => $groupsdn,
564                                  scope => $scope,
565                                  filter => "(&(objectclass=posixGroup)(cn=$user))"
566                                 );
567
568       $mesg->code && die $mesg->error;
569       foreach my $entry ($mesg->all_entries) {
570           $lines.= "dn: " . $entry->dn."\n";
571           foreach my $attr ($entry->attributes) {
572               {
573                   $lines.= $attr.": ".join(',', $entry->get_value($attr))."\n";
574               }
575           }
576       }
577         # take down session
578         $ldap_slave->unbind;
579       chomp $lines;
580       if ($lines eq '') {
581           return undef;
582       }
583       return $lines;
584   }
585
586 # find groups of a given user
587 ##### MODIFIE ########
588 sub find_groups_of
589   {
590       my $user = shift;
591       my $lines ='';
592         my $ldap_slave=connect_ldap_slave;
593         my  $mesg = $ldap_slave->search ( # perform a search
594                                  base   => $groupsdn,
595                                  scope => $scope,
596                                  filter => "(&(objectclass=posixGroup)(memberuid=$user))"
597                                 );
598       $mesg->code && die $mesg->error;
599       foreach my $entry ($mesg->all_entries) {
600           $lines.= "dn: ".$entry->dn."\n";
601       }
602         $ldap_slave->unbind;
603       chomp($lines);
604         if ($lines eq '') {
605           return undef;
606         }
607       return $lines;
608   }
609
610 # return the gidnumber for a group given as name or gid
611 # -1 : bad group name
612 # -2 : bad gidnumber
613 sub parse_group
614   {
615       my $userGidNumber = shift;
616       if ($userGidNumber =~ /[^\d]/ ) {
617           my $gname = $userGidNumber;
618           my $gidnum = getgrnam($gname);
619           if ($gidnum !~ /\d+/) {
620               return -1;
621           } else {
622               $userGidNumber = $gidnum;
623           }
624       } elsif (!defined(getgrgid($userGidNumber))) {
625           return -2;
626       }
627       return $userGidNumber;
628   }
629
630 # remove $user from $group
631 sub group_remove_member
632   {
633       my ($group, $user) = @_;
634       my $members='';
635       my $grp_line = get_group_dn($group);
636       if (!defined($grp_line)) {
637           return 0;
638       }
639         my $dn = get_dn_from_line($grp_line);
640         # we test if the user exist in the group
641         my $is_member=is_group_member($dn,$user);
642         if ($is_member == 1) {
643           my $ldap_master=connect_ldap_master();
644           # delete only the user from the group
645           my $modify = $ldap_master->modify ( "$dn",
646                                                                                   changes => [
647                                                                                                           delete => [memberUid => ["$user"]]
648                                                                                                          ]
649                                 );
650           $modify->code && die "failed to delete entry: ", $modify->error ;
651           $ldap_master->unbind;
652                 }
653       return 1;
654   }
655
656 sub group_get_members
657   {
658       my ($group) = @_;
659       my $members;
660       my @resultat;
661       my $grp_line = get_group_dn($group);
662         if (!defined($grp_line)) {
663           return 0;
664         }
665
666       my $ldap = Net::LDAP->new($slaveLDAP) or die "erreur LDAP";
667       $ldap->bind ;
668         my  $mesg = $ldap->search (
669                                                            base   => $groupsdn,
670                                      scope => $scope,
671                                      filter => "(&(objectclass=posixgroup)(cn=$group))"
672                                 );
673       $mesg->code && die $mesg->error;
674       foreach my $entry ($mesg->all_entries){
675           foreach my $attr ($entry->attributes){
676               if ($attr=~/\bmemberUid\b/){
677                   foreach my $ent ($entry->get_value($attr)) {
678                         push (@resultat,$ent);
679               }
680           }
681       }
682 }
683         return @resultat;
684   }
685
686 sub do_ldapmodify
687   {
688       my $ldif = shift;
689       my $FILE = "|$ldapmodify -r >/dev/null";
690       open (FILE, $FILE) || die "$!\n";
691       print FILE <<EOF;
692 $ldif
693 EOF
694       ;
695       close FILE;
696       my $rc = $?;
697       return $rc;
698   }
699
700 1;
701