File Coverage

blib/lib/Samba/LDAP/User.pm
Criterion Covered Total %
statement 45 505 8.9
branch 0 270 0.0
condition 0 50 0.0
subroutine 15 36 41.6
pod 10 10 100.0
total 70 871 8.0


line stmt bran cond sub pod time code
1             package Samba::LDAP::User;
2              
3             # Returned by Perl::MinimumVersion 0.11
4             require 5.006;
5              
6 4     4   66611 use warnings;
  4         10  
  4         402  
7 4     4   23 use strict;
  4         7  
  4         174  
8 4     4   21 use Carp qw(carp croak);
  4         9  
  4         283  
9 4     4   1320 use Regexp::DefaultFlags;
  4         5220  
  4         36  
10 4     4   1936 use Readonly;
  4         4987  
  4         227  
11 4     4   2229 use Crypt::SmbHash;
  4         32769  
  4         247  
12 4     4   34 use Digest::MD5 qw(md5);
  4         13  
  4         233  
13 4     4   4249 use Digest::SHA1 qw(sha1);
  4         3834  
  4         306  
14 4     4   4197 use MIME::Base64 qw(encode_base64);
  4         3477  
  4         321  
15 4     4   4356 use List::MoreUtils qw( any );
  4         4206  
  4         337  
16 4     4   4396 use Unicode::MapUTF8 qw(to_utf8 from_utf8);
  4         833450  
  4         483  
17 4     4   16510 use UNIVERSAL::require;
  4         13337  
  4         48  
18 4     4   147 use base qw(Samba::LDAP::Base);
  4         11  
  4         1038  
19 4     4   625 use Samba::LDAP;
  4         13  
  4         57  
20 4     4   2247 use Samba::LDAP::Group;
  4         22  
  4         50  
21              
22             our $VERSION = '0.05';
23              
24             #
25             # Add Log::Log4perl to all our classes!!!!
26             #
27              
28             # Our USAGE messages
29             Readonly my $DELETE_USER_USAGE => 'Usage: delete_user(
30             {
31             user => \'ghenry\',
32             homedir => \'1\',
33             }
34             );';
35              
36             Readonly my $CHANGE_PASSWORD_USAGE => 'Usage: change_password(
37             {
38             user => \'ghenry\',
39             oldpass => "$oldpass",
40             newpass => "$newpass",
41             samba => \'1\',
42             }
43             );';
44              
45             Readonly my $ADD_USER_USAGE => 'Usage: add_user(
46             {
47             user => \'ghenry\',
48             newpass => "$newpass",
49             windows_user => \'1\',
50             ox => \'1\',
51             }
52             );';
53              
54             Readonly my $GET_NEXT_ID_USAGE =>
55             'Usage: _get_next_id( $self->{usersdn}, "$attribute" );';
56              
57             #========================================================================
58             # -- PUBLIC METHODS --
59             #========================================================================
60              
61             #------------------------------------------------------------------------
62             # change_password( {
63             # user => 'ghenry',
64             # oldpass => "$oldpass",
65             # newpass => "$newpass",
66             # samba => '1', # Update only Samba pass, can be
67             # } # unix => '1' for unix pass only
68             # );
69             # Note the {} round the args, to report errors at compile time.
70             #
71             # Change user password in LDAP Directory
72             #
73             # Checks the users exists first, then changes the password
74             # If user doesn't exist, returns the error etc.
75             #
76             # If no oldpass arg is passed, binds as rootdn and sets a password
77             #
78             # Default is to add a Samba "and" unix password
79             #------------------------------------------------------------------------
80              
81             sub change_password {
82 0     0 1   my $self = shift;
83 0           my %args = (
84             samba => 1,
85             unix => 1,
86             @_, # argument pair list goes here
87             );
88              
89             # Required arguments
90 0           my @required_args = ( $args{user}, $args{newpass}, );
91             croak $CHANGE_PASSWORD_USAGE
92 0 0   0     if any { !defined $_ } @required_args;
  0            
93              
94             # Die straight away if passwords are the same. Should really be checked
95             # by the script/web app using this method, but hey ;-)
96 0 0         croak "Passwords are the same!\n"
97             if ( defined( $args{oldpass} ) eq $args{newpass} );
98              
99             # Set the $dn
100 0           my $dn;
101 0 0         if ( defined $args{dn} ) {
102 0           $dn = $args{dn};
103             }
104             else {
105 0           $dn = "uid=$args{user},$self->{usersdn}";
106             }
107              
108 0 0 0       if ( $args{user} && $args{oldpass} && $args{newpass} ) {
      0        
109 0           $self->{masterDN} = "uid=$args{user},$self->{usersdn}";
110 0           $self->{masterPw} = "$args{oldpass}";
111              
112             # Check the users password if they exist
113 0 0         if ( !$self->is_valid_user( $dn, $args{oldpass} ) ) {
114 0           $self->error("Authentication failure for $args{user}");
115 0           croak $self->error();
116             }
117             }
118              
119             # test existence of user in LDAP
120 0 0         if ( !defined( $self->_get_user_dn( $args{user} ) ) ) {
121 0           $self->error("user $args{user} doesn't exist");
122 0           croak $self->error();
123             }
124              
125             # Generate the password to be used for Unix and Samba pass
126 0           my $hash_password = $self->make_hash(
127             clear_pass => $args{newpass},
128             hash_encrypt_format => $self->{hash_encrypt},
129             crypt_salt_format => $self->{crypt_salt_format},
130             );
131              
132             # Check if a hash was generated, otherwise die
133 0 0 0       chomp($hash_password)
134             if ( defined($hash_password) )
135             or croak "I cannot generate the proper hash!\n";
136              
137             # Get ready to bind
138 0           my $ldap = Samba::LDAP->new();
139              
140             # If no oldpass argument, bind and set a password as the rootdn
141             # Bind details are set in the oldpass check above.
142 0 0 0       if ( $args{user} && $args{newpass} ) {
143 0           $ldap = $ldap->connect_ldap_master();
144             }
145              
146             # Change Samba password if they are actually a Samba user
147 0 0 0       if ( $self->is_samba_user( $args{user} ) && $args{samba} ) {
148              
149             # generate LanManager and NT clear text passwords
150 0           my ( $sambaLMPassword, $sambaNTPassword ) = ntlmgen $args{newpass};
151              
152             # the sambaPwdLastSet attribute must be updated
153 0           my $date = time;
154 0           my @mods;
155              
156             # Start setting the modifications
157 0           push( @mods, 'sambaLMPassword' => $sambaLMPassword );
158 0           push( @mods, 'sambaNTPassword' => $sambaNTPassword );
159 0           push( @mods, 'sambaPwdLastSet' => $date );
160              
161 0 0         if ( defined( $self->{defaultMaxPasswordAge} ) ) {
162 0           my $new_sambaPwdMustChange =
163             $date + $self->{defaultMaxPasswordAge} * 24 * 60 * 60;
164              
165 0           push( @mods, 'sambaPwdMustChange' => $new_sambaPwdMustChange );
166              
167             # This should only be done by the rootdn, need to put a better
168             # check in here
169 0 0         push( @mods, 'sambaAcctFlags' => '[U]' ) if ( !$args{user} );
170             }
171              
172             # Let's change nt/lm passwords
173 0           my $modify = $ldap->modify( "$dn", 'replace' => {@mods}, );
174 0 0         $modify->code && warn "Failed to modify entry: ", $modify->error;
175              
176             }
177              
178             # Update 'userPassword' field
179 0 0         if ( defined( $args{unix} ) ) {
180 0           my $modify =
181             $ldap->modify( "$dn",
182             changes => [ replace => [ userPassword => "$hash_password" ] ], );
183 0 0         $modify->code && warn "Unable to change password: ", $modify->error;
184             }
185              
186             # take the session down
187 0           $ldap->unbind;
188              
189 0           return "Password changed.";
190             }
191              
192             #------------------------------------------------------------------------
193             # add_user()
194             #
195             # Adds a new LDAP user. Various options
196             #------------------------------------------------------------------------
197              
198             sub add_user {
199 0     0 1   my $self = shift;
200 0           my %args = (
201             @_, # argument pair list goes here
202             );
203 0           my $username = $args{user};
204              
205             #my $oldpass = $args{oldpass};
206 0           my $newpass = $args{newpass};
207              
208             # Required arguments
209 0           my @required_args = ( $username, $newpass, );
210             croak $ADD_USER_USAGE
211 0 0   0     if any { !defined $_ } @required_args;
  0            
212              
213             # Die straight away if passwords are the same. Should really be checked
214             # by the script/web app using this method, but hey ;-)
215             #die "Passwords are the same!\n" if ( $args{oldpass} eq $args{newpass} );
216              
217             # For computers account, add a trailing dollar if missing
218 0 0         if ( defined( $args{workstation} ) ) {
219 0 0         if ( $username =~ /[^\$]$/s ) {
220 0           $username .= "\$";
221             }
222             }
223              
224             # untaint $username (can finish with one or two $)
225 0 0         if ( $username =~ /^([\w -.]+\$?)$/ ) {
226 0           $username = $1;
227             }
228             else {
229 0           $self->error("illegal username\n");
230 0           die $self->error();
231             }
232              
233             # User must not exist in LDAP (should it be nss-wide ?)
234             # $rc is return code. We are looking for '1'
235 0           my ( $rc, $dn ) = $self->_get_user_dn2($username);
236              
237 0 0 0       if ( $rc and defined($dn) ) {
    0          
238 0           $self->error("User $username already exists, can not add.\n");
239 0           croak $self->error();
240             }
241             elsif ( !$rc ) {
242 0           $self->error("error retrieving details\n");
243 0           croak $self->error();
244             }
245              
246             # Read options
247             # we create the user in the specified ou (relative to the users suffix)
248 0           my $user_ou = $args{ou};
249 0           my $node;
250              
251             # Connect
252 0           my $ldap_start = Samba::LDAP->new();
253 0           my $ldap = $ldap_start->connect_ldap_master();
254              
255 0 0         if ( defined $user_ou ) {
256 0 0         if ( !( $user_ou =~ m{^ou=(.*)} ) ) {
257 0           $node = $user_ou;
258 0           $user_ou = "ou=$user_ou";
259             }
260             else {
261 0           ($node) = ( $user_ou =~ m{ou=(.*)} );
262             }
263              
264             # if the ou does not exist, we create it
265 0           my $mesg = $ldap->search(
266             base => $self->{usersdn},
267             scope => 'one',
268             filter => "(&(objectClass=organizationalUnit)(ou=$node))"
269             );
270              
271 0 0         $mesg->code && die $mesg->error;
272              
273 0 0         if ( $mesg->count eq 0 ) {
274              
275             # add organizational unit
276 0           my $add = $ldap->add(
277             "ou=$node,$self->{usersdn}",
278             attr => [
279             'objectclass' => [ 'top', 'organizationalUnit' ],
280             'ou' => "$node"
281             ]
282             );
283 0 0         $add->code && die "failed to add entry: ", $add->error;
284             }
285              
286 0           $self->{usersdn} = "$user_ou,$self->{usersdn}";
287             }
288              
289 0           my $userUidNumber = $args{user_uid};
290              
291 0 0         if ( !defined($userUidNumber) ) {
    0          
292 0           $userUidNumber = $self->_get_next_id( $self->{usersdn}, 'uidNumber' );
293             }
294             elsif ( getpwuid($userUidNumber) ) {
295 0           carp "Uid already $userUidNumber exists.\n";
296             }
297              
298 0           my $createGroup = 0;
299 0           my $userGidNumber = $args{group};
300 0           my $group = Samba::LDAP::Group->new();
301              
302             # gid not specified ?
303 0 0         if ( !defined($userGidNumber) ) {
304              
305             # windows machine => $self->{defaultComputerGid}
306 0 0         if ( defined( $args{workstation} ) ) {
307 0           $userGidNumber = $self->{defaultComputerGid};
308             }
309             else {
310              
311             # user will have gid = $self->{defaultUserGid}
312 0           $userGidNumber = $self->{defaultUserGid};
313             }
314             }
315             else {
316 0           my $gid;
317              
318 0 0         if ( ( $gid = $group->parse_group($userGidNumber) ) < 0 ) {
319 0           $self->error("unknown group $userGidNumber\n");
320 0           croak $self->error();
321             }
322 0           $userGidNumber = $gid;
323             }
324              
325 0           my $group_entry;
326             my $userGroupSID;
327 0           my $userRid;
328 0           my $user_sid;
329              
330 0 0 0       if ( defined $args{windows_user} or defined $args{trust_account} ) {
331              
332             # as grouprid we use the value of the sambaSID attribute for
333             # group of gidNumber=$userGidNumber
334              
335 0           $group_entry = $group->read_group_entry_gid($userGidNumber);
336 0           $userGroupSID = $group_entry->get_value('sambaSID');
337 0 0         unless ($userGroupSID) {
338 0           $self->error( "Error: SID not set for unix group $userGidNumber\n"
339             . "check if your unix group is mapped to an NT group\n" );
340 0           die $self->error();
341             }
342              
343             # as rid we use 2 * uid + 1000
344 0           $userRid = 2 * $userUidNumber + 1000;
345              
346             # let's test if this SID already exist
347 0           $user_sid = "$self->{SID}-$userRid";
348              
349 0           my $test_exist_sid =
350             $ldap_start->does_sid_exist( $user_sid, $self->{usersdn} );
351 0 0         if ( $test_exist_sid->count == 1 ) {
352 0           $self->{sid_message} = "User SID already owned by\n";
353              
354             # there should not exist more than one entry, but ...
355 0           foreach my $entry ( $test_exist_sid->all_entries ) {
356 0           my $dn = $entry->dn;
357 0           chomp($dn);
358 0           $self->{sid_message} .= "$dn\n";
359             }
360 0           croak $self->{sid_message};
361             }
362             }
363              
364 0           my $userHomeDirectory;
365 0           my ( $givenName, $userCN, $userSN );
366 0           my @userMailLocal;
367 0           my @userMailTo;
368 0           my $tmp;
369              
370 0 0         if ( !defined( $userHomeDirectory = $args{homedir} ) ) {
371 0           $userHomeDirectory = $self->_subst_user( $self->{userHome}, $username );
372             }
373              
374             # RFC 2256
375             # sn: : nom (option S)
376             # givenName: prenom (option N)
377             # cn: person's full name
378 0           $userHomeDirectory =~ s{\/\/}{\/};
379              
380 0 0         $self->{userLoginShell} = $tmp if ( defined( $tmp = $args{shell} ) );
381 0 0         $self->{userGecos} = $tmp if ( defined( $tmp = $args{gecos} ) );
382 0 0         $self->{skeletonDir} = $tmp if ( defined( $tmp = $args{skeleton_dir} ) );
383              
384 0   0       $givenName = ( $self->_utf8Encode( $args{surname} ) || $username );
385 0   0       $userSN = ( $self->_utf8Encode( $args{family_name} ) || $username );
386              
387 0 0 0       if ( $args{surname} and $args{family_name} ) {
388 0           $userCN = "$givenName" . " $userSN";
389             }
390             else {
391 0           $userCN = $username;
392             }
393              
394             # $args{local_mail_address} and $args{mail_to_address} arguments are HoA
395             #
396             # Passed by:
397             # local_mail_address => [ "ghenry@suretecsystems.com", "me@me.com" ];
398             # mail_to_address => [ "ghenry@ghenry.co.uk", "ghenry@perl.me.uk" ];
399              
400 0 0 0       if ( defined( $args{local_mail_address} )
401             or defined( $args{mail_to_address} ) )
402             {
403 0           @userMailLocal = @{ $args{local_mail_address} };
  0            
404 0           @userMailTo = @{ $args{mail_to_address} };
  0            
405             }
406              
407             # Machine Account
408 0 0 0       if ( defined( $args{workstation} ) or defined( $args{trust_account} ) ) {
409              
410             # if args{workstation} and username doesn't end with '$'char => we add it
411 0 0 0       if ( $args{workstation} and !( $username =~ m{\$$} ) ) {
412 0           $username .= '$';
413             }
414              
415 0           my $machine = Samba::LDAP::Machine->new();
416 0 0         if (
417             !$machine->add_posix_machine(
418             {
419             user => $username,
420             uid => $userUidNumber,
421             gid => $userGidNumber,
422             time_to_wait => $args{time_to_wait},
423             }
424             )
425             )
426             {
427 0           $self->error("error while adding posix account\n");
428 0           die $self->error();
429             }
430              
431 0 0         if ( defined( $args{trust_account} ) ) {
432              
433             # For machine trust account
434             # Objectclass sambaSAMAccount must be added now !
435 0           my ( $lmpassword, $ntpassword ) = ntlmgen $newpass;
436 0           my $date = time;
437              
438 0           my $modify = $ldap->modify(
439             "uid=$username,$self->{computersdn}",
440             changes => [
441             replace => [
442             objectClass => [
443             'top', 'person',
444             'organizationalPerson', 'inetOrgPerson',
445             'posixAccount', 'sambaSAMAccount'
446             ]
447             ],
448             add => [ sambaLogonTime => '0' ],
449             add => [ sambaLogoffTime => '2147483647' ],
450             add => [ sambaKickoffTime => '2147483647' ],
451             add => [ sambaPwdCanChange => '0' ],
452             add => [ sambaPwdMustChange => '2147483647' ],
453             add => [ sambaPwdLastSet => "$date" ],
454             add => [ sambaAcctFlags => '[I ]' ],
455             add => [ sambaLMPassword => "$lmpassword" ],
456             add => [ sambaNTPassword => "$ntpassword" ],
457             add => [ sambaSID => "$user_sid" ],
458             add => [ sambaPrimaryGroupSID => "$self->{SID}-515" ]
459             ]
460             );
461              
462 0 0         $modify->code && die "failed to add entry: ", $modify->error;
463             }
464              
465 0           $ldap->unbind;
466 0           return;
467             }
468              
469             # USER ACCOUNT
470             # add posix account first
471              
472             # if AIX account, inetOrgPerson obectclass can't be used
473 0           my $add;
474 0 0         if ( defined( $args{aix} ) ) {
475 0           $add = $ldap->add(
476             "uid=$username,$self->{usersdn}",
477             attr => [
478             'objectclass' => [
479             'top', 'person',
480             'organizationalPerson', 'posixAccount',
481             'shadowAccount'
482             ],
483             'cn' => "$userCN",
484             'sn' => "$userSN",
485             'uid' => "$username",
486             'uidNumber' => "$userUidNumber",
487             'gidNumber' => "$userGidNumber",
488             'homeDirectory' => "$userHomeDirectory",
489             'loginShell' => "$self->{userLoginShell}",
490             'gecos' => "$self->{userGecos}",
491             'userPassword' => "{crypt}x",
492             ]
493             );
494             }
495             else {
496 0           $add = $ldap->add(
497             "uid=$username,$self->{usersdn}",
498             attr => [
499             'objectclass' => [
500             'top', 'person',
501             'organizationalPerson', 'inetOrgPerson',
502             'posixAccount', 'shadowAccount'
503             ],
504             'cn' => "$userCN",
505             'sn' => "$userSN",
506             'givenName' => "$givenName",
507             'uid' => "$username",
508             'uidNumber' => "$userUidNumber",
509             'gidNumber' => "$userGidNumber",
510             'homeDirectory' => "$userHomeDirectory",
511             'loginShell' => "$self->{userLoginShell}",
512             'gecos' => "$self->{userGecos}",
513             'userPassword' => "{crypt}x",
514             ]
515             );
516             }
517 0 0         $add->code && carp "failed to add entry: ", $add->error;
518              
519             # Add to an LDAP group
520 0 0         if ( $userGidNumber != $self->{defaultUserGid} ) {
521 0           $group->add_to_group( $userGidNumber, $username );
522             }
523              
524 0           my $grouplist;
525              
526             # Adds to supplementary groups
527 0 0         if ( defined( $args{groups} ) ) {
528 0           $group->add_to_groups( $args{groups}, $username );
529             }
530              
531             # If user was created successfully then we should create his/her home dir
532 0 0         if ( defined( $tmp = $args{homedir} ) ) {
533 0 0         unless ( $username =~ /\$$/ ) {
534 0 0         if ( !( -e $userHomeDirectory ) ) {
535 0           system "mkdir $userHomeDirectory 2>/dev/null";
536 0           system
537             "cp -a $self->{skeletonDir}/.[a-z,A-Z]* $self->{skeletonDir}/* $userHomeDirectory 2>/dev/null";
538 0           system
539             "chown -R $userUidNumber:$userGidNumber $userHomeDirectory 2>/dev/null";
540              
541 0 0         if ( defined $self->{userHomeDirectoryMode} ) {
542 0           system
543             "chmod $self->{userHomeDirectoryMode} $userHomeDirectory 2>/dev/null";
544             }
545             else {
546 0           system "chmod 700 $userHomeDirectory 2>/dev/null";
547             }
548             }
549             }
550             }
551              
552             # we start to define mail adresses if option $args{homedir} or $args{mail_to_address} is given in option
553 0           my @adds;
554 0 0         if (@userMailLocal) {
555 0           my @mail;
556 0           foreach my $m (@userMailLocal) {
557 0           my $domain = $self->{mailDomain};
558 0 0         if ( $m =~ /^(.+)@/ ) {
559 0           push( @mail, $m );
560              
561             # mailLocalAddress contains only the first part
562 0           $m = $1;
563             }
564             else {
565 0 0         push( @mail, $m . ( $domain ? '@' . $domain : '' ) );
566             }
567             }
568 0           push( @adds, 'mailLocalAddress' => [@userMailLocal] );
569 0           push( @adds, 'mail' => [@mail] );
570             }
571 0 0         if (@userMailTo) {
572 0           push( @adds, 'mailRoutingAddress' => [@userMailTo] );
573             }
574 0 0 0       if ( @userMailLocal || @userMailTo ) {
575 0           push( @adds, 'objectClass' => 'inetLocalMailRecipient' );
576             }
577              
578             # Add OX User Infos
579 0 0         if ( defined( $args{ox} ) ) {
580 0           my $modify = $ldap->modify(
581             "uid=$username,$self->{usersdn}",
582             changes => [
583             add => [ objectclass => ['OXUserObject'] ],
584             add => [ shadowMin => "-1" ],
585             add => [ shadowMax => "99999" ],
586             add => [ shadowWarning => "-1" ],
587             add => [ shadowExpire => "-1" ],
588             add => [ shadowInactive => "-1" ],
589             add => [ mail => "$username\@$self->{mailDomain}" ],
590             add => [ mailDomain => "$self->{mailDomain}" ],
591             add => [ preferredLanguage => "EN" ],
592             add => [ OXAppointmentDays => "5" ],
593             add => [ OXGroupID => "500" ],
594             add => [ OXTaskDays => "5" ],
595             add => [ OXTimeZone => "Europe/London" ],
596             add => [ o => "Suretec Systems Ltd." ],
597             add => [ userCountry => "Scotland" ],
598             add => [ mailEnabled => "OK" ],
599             add => [ lnetMailAccess => "TRUE" ],
600             ]
601             );
602 0 0         $modify->code && die "failed to add entry: ", $modify->error;
603              
604 0           my $add = $ldap->add(
605             "ou=addr,uid=$username,$self->{usersdn}",
606             attr => [
607             'objectclass' => [ 'top', 'organizationalUnit' ],
608             'ou' => "addr"
609             ]
610             );
611 0 0         $add->code && warn "failed to add entry: ", $add->error;
612              
613 0           my $modify2 = $ldap->modify(
614             "cn=AddressAdmins,o=AddressBook,ou=OxObjects,$self->{suffix}",
615             changes => [ add => [ member => "uid=$username,$self->{usersdn}" ] ]
616             );
617 0 0         $modify2->code && die "failed to modify entry: ", $modify2->error;
618              
619             #system "/usr/local/openxchange/sbin/addusersql_ox --username=$username --lang=EN";
620             }
621              
622             # Add Samba user infos
623 0 0         if ( defined( $args{windows_user} ) ) {
624 0 0         if ( !$self->{with_smbpasswd} ) {
625              
626 0           my $winmagic = 2147483647;
627 0           my $valpwdcanchange = 0;
628 0           my $valpwdmustchange = $winmagic;
629 0           my $valpwdlastset = 0;
630 0           my $valacctflags = "[UX]";
631              
632 0 0         if ( defined( $tmp = $args{can_change_pass} ) ) {
633 0 0         if ( $tmp != 0 ) {
634 0           $valpwdcanchange = "0";
635             }
636             else {
637 0           $valpwdcanchange = "$winmagic";
638             }
639             }
640              
641 0 0         if ( defined( $tmp = $args{must_change_pass} ) ) {
642 0 0         if ( $tmp != 0 ) {
643 0           $valpwdmustchange = "0";
644              
645             # To force a user to change his password:
646             # . the attribute sambaPwdLastSet must be != 0
647             # . the attribute sambaAcctFlags must not match the 'X' flag
648 0           $valpwdlastset = $winmagic;
649 0           $valacctflags = "[U]";
650             }
651             else {
652 0           $valpwdmustchange = "$winmagic";
653             }
654             }
655              
656 0 0         if ( defined( $tmp = $args{account_flags} ) ) {
657 0           $valacctflags = "$tmp";
658             }
659              
660 0           my $modify = $ldap->modify(
661             "uid=$username,$self->{usersdn}",
662             changes => [
663             add => [ objectClass => 'sambaSAMAccount' ],
664             add => [ sambaPwdLastSet => "$valpwdlastset" ],
665             add => [ sambaLogonTime => '0' ],
666             add => [ sambaLogoffTime => '2147483647' ],
667             add => [ sambaKickoffTime => '2147483647' ],
668             add => [ sambaPwdCanChange => "$valpwdcanchange" ],
669             add => [ sambaPwdMustChange => "$valpwdmustchange" ],
670             add => [ displayName => "$self->{userGecos}" ],
671             add => [ sambaAcctFlags => "$valacctflags" ],
672             add => [ sambaSID => "$self->{SID}-$userRid" ]
673             ]
674             );
675              
676 0 0         $modify->code && die "failed to add entry: ", $modify->error;
677              
678             }
679             else {
680 0           my $FILE = "|smbpasswd -s -a $username >/dev/null";
681 0 0         open( FILE, $FILE ) || die "$!\n";
682 0           print FILE <
683             x
684             x
685             EOF
686 0           close FILE;
687 0 0         if ($?) {
688 0           $self->error("Error adding samba account\n");
689 0           die $self->error();
690             }
691             }
692              
693 0 0         $tmp =
694             defined( $args{logon_script} )
695             ? $args{logon_script}
696             : $self->{userScript};
697 0           my $valscriptpath = $self->_subst_user( $tmp, $username );
698              
699 0 0         $tmp =
700             defined( $args{home_path} ) ? $args{home_path} : $self->{userSmbHome};
701 0           my $valsmbhome = $self->_subst_user( $tmp, $username );
702              
703 0 0         my $valhomedrive =
704             defined( $args{home_drive} )
705             ? $args{home_drive}
706             : $self->{userHomeDrive};
707              
708             # If the letter is given without the ":" symbol, we add it
709 0 0 0       $valhomedrive .= ':' if ( $valhomedrive && $valhomedrive !~ /:$/ );
710              
711 0 0         $tmp =
712             defined( $args{user_profile} )
713             ? $args{user_profile}
714             : $self->{userProfile};
715 0           my $valprofilepath = $self->_subst_user( $tmp, $username );
716              
717 0 0         if ($valhomedrive) {
718 0           push( @adds, 'sambaHomeDrive' => $valhomedrive );
719             }
720 0 0         if ($valsmbhome) {
721 0           push( @adds, 'sambaHomePath' => $valsmbhome );
722             }
723              
724 0 0         if ($valprofilepath) {
725 0           push( @adds, 'sambaProfilePath' => $valprofilepath );
726             }
727 0 0         if ($valscriptpath) {
728 0           push( @adds, 'sambaLogonScript' => $valscriptpath );
729             }
730 0 0         if ( !$self->{with_smbpasswd} ) {
731 0           push( @adds, 'sambaPrimaryGroupSID' => $userGroupSID );
732 0           push( @adds, 'sambaLMPassword' => "XXX" );
733 0           push( @adds, 'sambaNTPassword' => "XXX" );
734             }
735 0           my $modify =
736             $ldap->modify( "uid=$username,$self->{usersdn}", add => {@adds} );
737              
738 0 0         $modify->code && die "failed to add entry: ", $modify->error;
739             }
740              
741             # add AIX user
742 0 0         if ( defined( $args{aix_user} ) ) {
743 0           my $modify = $ldap->modify(
744             "uid=$username,$self->{usersdn}",
745             changes => [
746             add => [ objectClass => 'aixAuxAccount' ],
747             add => [ passwordChar => "!" ],
748             add => [ isAdministrator => "false" ]
749             ]
750             );
751              
752 0 0         $modify->code && die "failed to add entry: ", $modify->error;
753             }
754              
755             # Finally, set their password
756 0 0         if ( defined( $args{newpass} ) ) {
757 0           $self->change_password(
758             user => "$args{user}",
759             newpass => "$newpass",
760             );
761             }
762              
763 0           $ldap->unbind; # take down session
764              
765 0           return;
766             }
767              
768             #------------------------------------------------------------------------
769             # delete_user( user => 'ghenry', )
770             #
771             # Delete the LDAP user and remove their home drive (if homedir => '1',)
772             #
773             # In addition to the original userdel script, this searches for
774             # subordinate objects, and deletes them first
775             #------------------------------------------------------------------------
776              
777             sub delete_user {
778 0     0 1   my $self = shift;
779 0           my %args = (
780             @_, # argument pair list goes here
781             );
782              
783             # Required arguments
784 0           my @required_args = ( $args{user}, );
785             croak $DELETE_USER_USAGE
786 0 0   0     if any { !defined $_ } @required_args;
  0            
787              
788 0           my $user = $args{user};
789              
790 0           my $dn_line;
791 0 0         if ( !defined( $dn_line = $self->_get_user_dn($user) ) ) {
792 0           $self->error("User $user doesn't exist\n");
793 0           croak $self->error();
794             }
795              
796             # Get ready to remove them from the Directory
797 0           my $ldap = Samba::LDAP->new();
798              
799             # Remove user from groups
800 0           my $group = Samba::LDAP::Group->new();
801 0           my @groups = $group->find_groups($user);
802              
803 0 0         if (@groups) {
804 0           for my $gname (@groups) {
805 0 0         if ( $gname ne "" ) {
806 0           $group->remove_from_group( $gname, $user );
807             }
808             }
809             }
810              
811 0           my $dn = $ldap->get_dn_from_line($dn_line);
812              
813 0           $ldap = $ldap->connect_ldap_master();
814              
815             # Here we do a Sub-Tree search, with the users DN as the base to
816             # find anything below.
817 0           my $mesg = $ldap->search(
818             base => "uid=$user,$self->{usersdn}",
819             scope => 'sub',
820             filter => "(objectclass=*)",
821             );
822 0 0         $mesg->code && croak $mesg->error;
823              
824 0           my @entries = $mesg->all_entries;
825 0           foreach my $entr (@entries) {
826              
827             # Remove sub-entries, but move on if we hit the actual user
828 0 0         next if ( $entr->dn =~ m{^uid} );
829              
830 0           my $modify = $ldap->delete( $entr->dn );
831 0 0         $modify->code
832             && croak "Failed to delete sub-trees of user $user, ", $modify->error;
833             }
834              
835             # Now delete the top level user
836 0           my $modify = $ldap->delete($dn);
837 0 0         $modify->code
838             && croak "Failed to delete user '$user', ", $modify->error;
839              
840             # Remove their Home Drive
841 0           my $homedir;
842 0 0         if ( defined( $args{homedir} ) ) {
843 0           $homedir = $self->get_homedir($user);
844              
845 0 0         if ( $homedir !~ /^\/.+\/(.*)$user/ ) {
846 0           $self->error("Refusing to delete this home directory: $homedir\n");
847 0           croak $self->error();
848             }
849             }
850              
851 0 0         if ($homedir) {
852 0           my $module = 'File::Path';
853 0 0         $module->require or die $@;
854              
855             # Delete it!
856 0           rmtree($homedir);
857             }
858              
859 0           my $nscd_status = system "/etc/init.d/nscd status >/dev/null 2>&1";
860              
861 0 0         if ( $nscd_status == 0 ) {
862 0           system "/etc/init.d/nscd restart > /dev/null 2>&1";
863             }
864              
865 0           $ldap->unbind; # take down session
866              
867 0           return;
868             }
869              
870             #
871             # Replace the next 3 methods with (don't like code repetition):
872             #
873             # foreach (qw/valid samba unix/) {
874             # *{"is_${_}_user"} = sub { shift->_generic_is_user($_) };
875             # }
876             #
877             #
878             # Or use something like __PACKAGE__->mk_accessors(...)
879             # or make __PACKAGE__->mk_is_user_methods(qw/valid samba unix/);
880             #
881             # Even Sub::Install would do.
882             #
883              
884             #------------------------------------------------------------------------
885             # disable_user( $username )
886             #
887             # Disable a user by clearing their password and disabling in Samba
888             #------------------------------------------------------------------------
889              
890             sub disable_user {
891 0     0 1   my $self = shift;
892 0           my $user = shift;
893              
894 0           $self->error("Need username!\n");
895 0 0         croak $self->error() if !defined($user);
896              
897 0           my $dn_line;
898 0 0         if ( !defined( $dn_line = $self->_get_user_dn($user) ) ) {
899 0           $self->error("User $user doesn't exist\n");
900 0           croak $self->error();
901             }
902              
903 0           my $ldap = Samba::LDAP->new();
904 0           my $dn = $ldap->get_dn_from_line($dn_line);
905              
906 0           $ldap = $ldap->connect_ldap_slave();
907              
908             # Put test in here to see is user has already been disabled.
909             #
910             # Does it matter if they have? Changes will just be made again, so
911             # mayeb test not needed.
912             #
913             #my $mesg = $ldap->search (
914             # base => $self->{suffix},
915             # scope => $self->{scope},
916             # filter =>"($dn)",
917             # attrs => 'UserPassword',
918             # );
919             #$mesg->code && croak $mesg->error;
920              
921 0           my $modify =
922             $ldap->modify( "$dn",
923             changes => [ replace => [ userPassword => '{crypt}!x' ] ] );
924 0 0         $modify->code && croak "failed to modify entry: ", $modify->error;
925              
926 0 0         if ( $self->is_samba_user($user) ) {
927 0           my $modify =
928             $ldap->modify( "$dn",
929             changes => [ replace => [ sambaAcctFlags => '[D ]' ] ] );
930 0 0         $modify->code && croak "failed to modify entry: ", $modify->error;
931             }
932              
933 0           return;
934             }
935              
936             #------------------------------------------------------------------------
937             # is_valid_user( $dn,$password )
938             #
939             # bind to a directory with the user dn and password. Returns 1 on success
940             # and 0 on failure
941             #------------------------------------------------------------------------
942              
943             sub is_valid_user {
944 0     0 1   my $self = shift;
945 0           my $dn = shift;
946 0           my $oldpass = shift;
947              
948 0 0         my $ldap_slave = Net::LDAP->new(
949             $self->{slaveLDAP},
950             port => $self->{slavePort},
951             version => 3,
952             timeout => 60,
953             )
954             or carp "LDAP error: Can't contact slave ldap server ($@)\n
955             =>trying to contact the master server\n";
956              
957 0 0         if ( !$ldap_slave ) {
958              
959             # connection to the slave failed: trying to contact the master ...
960 0 0         $ldap_slave = Net::LDAP->new(
961             $self->{masterLDAP},
962             port => $self->{masterPort},
963             version => 3,
964             timeout => 60,
965             ) or carp "LDAP error: Can't contact master ldap server ($@)\n";
966             }
967              
968 0 0         if ($ldap_slave) {
969 0 0         if ( $self->{ldapTLS} == 1 ) {
970 0           $ldap_slave->start_tls(
971             verify => $self->{verify},
972             clientcert => $self->{clientcert},
973             clientkey => $self->{clientkey},
974             cafile => $self->{cafile},
975             );
976             }
977              
978 0           my $mesg = $ldap_slave->bind( dn => $dn, password => $oldpass );
979              
980 0 0         if ( $mesg->code == 0 ) {
981 0           $ldap_slave->unbind;
982 0           return 1;
983             }
984             else {
985 0 0         if ( $ldap_slave->bind() ) {
986 0           $ldap_slave->unbind;
987 0           return 0;
988             }
989             else {
990 0           $self->error("The LDAP directory is not available.");
991 0           $ldap_slave->unbind;
992 0           return 0;
993             }
994 0           die "Problem: contact your administrator";
995             }
996             }
997              
998 0           return $self->error();
999             }
1000              
1001             #------------------------------------------------------------------------
1002             # is_samba_user( $username )
1003             #
1004             # Check user is a Samba user in the LDAP directory
1005             #
1006             # returns 1
1007             #------------------------------------------------------------------------
1008              
1009             sub is_samba_user {
1010 0     0 1   my $self = shift;
1011 0           my $user = shift;
1012              
1013 0           my $ldap = Samba::LDAP->new();
1014 0           $ldap = $ldap->connect_ldap_master();
1015              
1016 0           my $mesg = $ldap->search(
1017             base => $self->{suffix},
1018             scope => $self->{scope},
1019             filter => "(&(objectClass=sambaSamAccount)(uid=$user))"
1020             );
1021              
1022 0 0         $mesg->code && die $mesg->error;
1023 0           return ( $mesg->count != 0 );
1024             }
1025              
1026             #------------------------------------------------------------------------
1027             # is_unix_user( $username )
1028             #
1029             # Check user is a Unix user in the LDAP directory
1030             #
1031             # returns 1 if user found
1032             #------------------------------------------------------------------------
1033              
1034             sub is_unix_user {
1035 0     0 1   my $self = shift;
1036 0           my $user = shift;
1037              
1038 0           my $ldap = Samba::LDAP->new();
1039 0           $ldap = $ldap->connect_ldap_master();
1040              
1041 0           my $mesg = $ldap->search(
1042             base => $self->{suffix},
1043             scope => $self->{scope},
1044             filter => "(&(objectClass=posixAccount)(uid=$user))"
1045             );
1046 0 0         $mesg->code && croak $mesg->error;
1047 0           return ( $mesg->count != 0 );
1048             }
1049              
1050             #------------------------------------------------------------------------
1051             # is_nonldap_unix_user()
1052             #
1053             # Description here
1054             #------------------------------------------------------------------------
1055              
1056             sub is_nonldap_unix_user {
1057 0     0 1   my $self = shift;
1058             }
1059              
1060             #------------------------------------------------------------------------
1061             # get_homedir()
1062             #
1063             # Discovery the home directory from the user entry in the Directory
1064             # Server
1065             #
1066             # Returns undef, if not found.
1067             #------------------------------------------------------------------------
1068              
1069             sub get_homedir {
1070 0     0 1   my $self = shift;
1071 0           my $user = shift;
1072 0           my $homeDir = '';
1073 0           my $entry;
1074              
1075 0           my $ldap = Samba::LDAP->new();
1076 0           $ldap = $ldap->connect_ldap_master();
1077              
1078 0           my $mesg = $ldap->search(
1079             base => $self->{usersdn},
1080             scope => $self->{scope},
1081             filter => "(&(objectclass=posixAccount)(uid=$user))"
1082             );
1083 0 0         $mesg->code && croak $mesg->error;
1084              
1085 0           my $nb = $mesg->count;
1086 0 0         if ( $nb > 1 ) {
1087 0           carp "Aborting: there are $nb existing users named $user\n";
1088 0           foreach $entry ( $mesg->all_entries ) {
1089 0           my $dn = $entry->dn;
1090 0           print " $dn\n";
1091             }
1092 0           return;
1093             }
1094             else {
1095 0           $entry = $mesg->shift_entry();
1096 0 0         if ( defined $entry ) {
1097 0           $homeDir = $entry->get_value('homeDirectory');
1098             }
1099             }
1100              
1101 0           chomp $homeDir;
1102 0 0         if ( $homeDir eq '' ) {
1103 0           return undef;
1104             }
1105 0           return $homeDir;
1106             }
1107              
1108             #------------------------------------------------------------------------
1109             # make_hash( {
1110             # clear_pass => 'original_pass',
1111             # hash_encrypt_format => 'SSHA',
1112             # crypt_salt_format => '%s',
1113             # }
1114             # )
1115             #
1116             # A substitute for slappasswd tool
1117             #
1118             # Generates a hash which is one of the following RFC 2307 schemes:
1119             # CRYPT, MD5, SMD5, SHA, SSHA, and CLEARTEXT
1120             #
1121             # SSHA is default
1122             # '%s' is a default crypt_salt_format
1123             #------------------------------------------------------------------------
1124              
1125             sub make_hash {
1126 0     0 1   my $self = shift;
1127 0           my %args = (
1128             hash_encrypt_format => 'SSHA',
1129             crypt_salt_format => '%s',
1130             @_, # argument pair list goes here
1131             );
1132              
1133             # Save args for laziness ;-)
1134 0           my $clear_pass = $args{clear_pass};
1135              
1136             # Complain if no password passed.
1137 0           $self->error("Need password to hash!\n");
1138 0 0         croak $self->error() if !defined($clear_pass);
1139              
1140 0           my $hash_encrypt = '{' . $args{hash_encrypt_format} . '}';
1141 0           my $crypt_salt_format = $args{crypt_salt_format};
1142              
1143 0 0 0       if ( $hash_encrypt eq '{CRYPT}' && defined($crypt_salt_format) ) {
    0          
    0          
    0          
    0          
    0          
1144              
1145             # Generate CRYPT hash
1146             # for unix md5crypt $crypt_salt_format = '$1$%.8s'
1147 0           my $salt = sprintf( $crypt_salt_format, $self->_make_salt() );
1148 0           $self->{hash_pass} = '{CRYPT}' . crypt( $clear_pass, $salt );
1149             }
1150             elsif ( $hash_encrypt eq '{MD5}' ) {
1151              
1152             # Generate MD5 hash
1153 0           $self->{hash_pass} = '{MD5}' . encode_base64( md5($clear_pass), '' );
1154             }
1155             elsif ( $hash_encrypt eq '{SMD5}' ) {
1156              
1157             # Generate SMD5 hash (MD5 with salt)
1158 0           my $salt = $self->_make_salt(4);
1159 0           $self->{hash_pass} =
1160             '{SMD5}' . encode_base64( md5( $clear_pass . $salt ) . $salt, '' );
1161             }
1162             elsif ( $hash_encrypt eq '{SHA}' ) {
1163              
1164             # Generate SHA1 hash
1165 0           $self->{hash_pass} = '{SHA}' . encode_base64( sha1($clear_pass), '' );
1166             }
1167             elsif ( $hash_encrypt eq '{SSHA}' ) {
1168              
1169             # Generate SSHA hash (SHA1 with salt)
1170 0           my $salt = $self->_make_salt(4);
1171 0           $self->{hash_pass} =
1172             '{SSHA}' . encode_base64( sha1( $clear_pass . $salt ) . $salt, '' );
1173             }
1174             elsif ( $hash_encrypt eq '{CLEARTEXT}' ) {
1175 0           $self->{hash_pass} = $clear_pass;
1176             }
1177             else {
1178 0           $self->error("Bad format $self->{hash_encrypt_format}\n");
1179 0           return $self->error();
1180             }
1181              
1182 0           return $self->{hash_pass};
1183             }
1184              
1185             #========================================================================
1186             # -- PRIVATE METHODS --
1187             #========================================================================
1188              
1189             #------------------------------------------------------------------------
1190             # _get_next_id( $self->{usersdn}, $attribute )
1191             #
1192             # Get the next id for the new user in add_user() and make the change in
1193             # the directory, i.e. increase uidNumber by 1.
1194             # $attribute is something like uidNumber
1195             #------------------------------------------------------------------------
1196              
1197             sub _get_next_id {
1198 0     0     my $self = shift;
1199 0           my $ldap_base_dn = shift;
1200 0           my $attribute = shift;
1201              
1202             # Required arguments
1203 0           my @required_args = ( $ldap_base_dn, $attribute, );
1204             croak $GET_NEXT_ID_USAGE
1205 0 0   0     if any { !defined $_ } @required_args;
  0            
1206              
1207 0           my $tries = 0;
1208 0           my $found = 0;
1209 0           my $next_uid_mesg;
1210             my $nextuid;
1211              
1212 0           my $ldap = Samba::LDAP->new();
1213 0           $ldap = $ldap->connect_ldap_slave();
1214              
1215 0 0         if ( $ldap_base_dn =~ m/$self->{usersdn}/i ) {
1216              
1217             # when adding a new user, we'll check if the uidNumber available is not
1218             # already used for a computer's account
1219 0           $ldap_base_dn = $self->{suffix};
1220             }
1221 0           do {
1222 0           $next_uid_mesg = $ldap->search(
1223             base => $self->{sambaUnixIdPooldn},
1224             filter => '(objectClass=sambaUnixIdPool)',
1225             scope => 'base',
1226             );
1227 0 0         $next_uid_mesg->code && die "Error looking for next uid";
1228              
1229 0 0         if ( $next_uid_mesg->count != 1 ) {
1230 0           die "Could not find base dn, to get next $attribute";
1231             }
1232 0           my $entry = $next_uid_mesg->entry(0);
1233              
1234 0           $nextuid = $entry->get_value($attribute);
1235 0           my $modify =
1236             $ldap->modify( "$self->{sambaUnixIdPooldn}",
1237             changes => [ replace => [ $attribute => $nextuid + 1 ], ], );
1238 0 0         $modify->code && die "Error: ", $modify->error;
1239              
1240             # let's check if the id found is really free (in ou=Groups or ou=Users)...
1241 0           my $check_uid_mesg = $ldap->search(
1242             base => $ldap_base_dn,
1243             filter => "($attribute=$nextuid)",
1244             );
1245 0 0         $check_uid_mesg->code
1246             && die "Cannot confirm $attribute $nextuid is free";
1247              
1248 0 0         if ( $check_uid_mesg->count == 0 ) {
1249 0           $found = 1;
1250 0           return $nextuid;
1251             }
1252 0           $tries++;
1253 0           print "Cannot confirm $attribute $nextuid is free: checking for the next
1254             one\n"
1255             } while ( $found != 1 );
1256              
1257 0           die "Could not allocate $attribute!";
1258             }
1259              
1260             #------------------------------------------------------------------------
1261             # _utf8Encode( $user )
1262             #
1263             # Wrapper for to_utf8
1264             #-----------------------------------------------------------------------
1265              
1266             sub _utf8Encode {
1267 0     0     my $self = shift;
1268 0           my $to_encode = shift;
1269              
1270 0           return to_utf8(
1271             -string => $to_encode,
1272             -charset => 'ISO-8859-1',
1273             );
1274             }
1275              
1276             #------------------------------------------------------------------------
1277             # _utf8Decode( $user )
1278             #
1279             # Wrapper for from_utf8
1280             #-----------------------------------------------------------------------
1281              
1282             sub _utf8Decode {
1283 0     0     my $self = shift;
1284 0           my $to_decode = shift;
1285              
1286 0           return from_utf8(
1287             -string => $to_decode,
1288             -charset => 'ISO-8859-1',
1289             );
1290             }
1291              
1292             #------------------------------------------------------------------------
1293             # _get_user_dn( $user )
1294             #
1295             # Searches for a users distinguised name
1296             #------------------------------------------------------------------------
1297              
1298             sub _get_user_dn {
1299 0     0     my $self = shift;
1300 0           my $user = shift;
1301              
1302 0           my $ldap = Samba::LDAP->new();
1303 0           $ldap = $ldap->connect_ldap_slave();
1304              
1305 0           my $mesg = $ldap->search(
1306             base => $self->{suffix},
1307             scope => $self->{scope},
1308             filter => "(&(objectclass=posixAccount)(uid=$user))"
1309             );
1310 0 0         $mesg->code && die $mesg->error;
1311              
1312 0           for my $entry ( $mesg->all_entries ) {
1313 0           $self->{dn} = $entry->dn;
1314             }
1315              
1316             # Shut down session
1317 0           $ldap->unbind;
1318              
1319 0 0         if ( !$self->{dn} ) {
1320 0           croak "Can not find $user user";
1321             }
1322              
1323 0           my $dn = $self->{dn};
1324 0           chomp($dn);
1325              
1326 0           $dn = "dn: " . $dn;
1327              
1328 0           return $dn;
1329             }
1330              
1331             #------------------------------------------------------------------------
1332             # _get_user_dn2( $user )
1333             #
1334             # Same as above, but returns 1 this time as well.
1335             #------------------------------------------------------------------------
1336              
1337             sub _get_user_dn2 {
1338 0     0     my $self = shift;
1339 0           my $user = shift;
1340              
1341 0           my $ldap = Samba::LDAP->new();
1342 0           $ldap = $ldap->connect_ldap_slave();
1343              
1344 0           my $mesg = $ldap->search(
1345             base => $self->{suffix},
1346             scope => $self->{scope},
1347             filter => "(&(objectclass=posixAccount)(uid=$user))"
1348             );
1349 0 0         $mesg->code && die $mesg->error;
1350              
1351 0           for my $entry ( $mesg->all_entries ) {
1352 0           $self->{dn} = $entry->dn;
1353             }
1354              
1355             # Shut down session
1356 0           $ldap->unbind;
1357              
1358 0           my $dn = $self->{dn};
1359              
1360 0 0         if ( defined($dn) ) {
1361 0           chomp($dn);
1362              
1363 0           $dn = "dn: " . $dn;
1364 0           return ( 1, $dn );
1365             }
1366             else {
1367 0           return ( 1, undef );
1368             }
1369 0           return;
1370             }
1371              
1372             #------------------------------------------------------------------------
1373             # _subst_user( $string, $username )
1374             #
1375             # Replaces the %U in the main settings with their username (don't like
1376             # and will replace on new version)
1377             #
1378             #------------------------------------------------------------------------
1379              
1380             sub _subst_user {
1381 0     0     my $self = shift;
1382 0           my $str = shift;
1383 0           my $username = shift;
1384              
1385 0 0         $str =~ s/%U/$username/ if ($str);
1386 0           return ($str);
1387             }
1388              
1389             #------------------------------------------------------------------------
1390             # _make_salt( $length )
1391             #
1392             # Generates salt
1393             #
1394             # Pretty much the same as the Crypt::Salt module from CPAN, except our
1395             # $length is 32 by default
1396             #------------------------------------------------------------------------
1397              
1398             sub _make_salt {
1399 0     0     my $self = shift;
1400 0   0       my $length = shift || '32';
1401              
1402 0           my @tab = ( '.', '/', 0 .. 9, 'A' .. 'Z', 'a' .. 'z' );
1403              
1404 0           return join "", @tab[ map { rand 64 } ( 1 .. $length ) ];
  0            
1405             }
1406              
1407             1; # Magic true value required at end of module
1408              
1409             __END__