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