• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500v2-V1.0.0.60_1.0.38/ap/gpl/samba-3.0.13/examples/LDAP/smbldap-tools-0.8.7/
1#!/usr/bin/perl -w
2
3# This script was modified by Pawel Wieleba on 2004/10/09.
4
5# LDAP to unix password sync script for samba
6# $Id: smbldap-passwd,v 1.16 2005/02/13 12:15:11 jtournier Exp $
7
8#  This code was developped by IDEALX (http://IDEALX.org/) and
9#  contributors (their names can be found in the CONTRIBUTORS file).
10#
11#                 Copyright (C) 2001-2002 IDEALX
12#
13#  This program is free software; you can redistribute it and/or
14#  modify it under the terms of the GNU General Public License
15#  as published by the Free Software Foundation; either version 2
16#  of the License, or (at your option) any later version.
17#
18#  This program is distributed in the hope that it will be useful,
19#  but WITHOUT ANY WARRANTY; without even the implied warranty of
20#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21#  GNU General Public License for more details.
22#
23#  You should have received a copy of the GNU General Public License
24#  along with this program; if not, write to the Free Software
25#  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
26#  USA.
27
28#  Purpose :
29#       . ldap-unix passwd sync for SAMBA>2.2.2 + LDAP
30#       . may also replace /bin/passwd
31
32# untaint environment
33$ENV{'PATH'}='/bin:/usr/bin';
34$ENV{'SHELL'} = '/bin/sh';
35delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
36
37use strict;
38use FindBin;
39use FindBin qw($RealBin);
40use lib "$RealBin/";
41use smbldap_tools;
42
43use Crypt::SmbHash;
44use Digest::MD5 qw(md5);
45use Digest::SHA1 qw(sha1);
46use MIME::Base64 qw(encode_base64);
47
48# function declaration
49sub make_hash;
50sub make_salt;
51
52my $user;
53my $oldpass;
54my $ret;
55
56my $arg;
57my $update_only_userPasswd=0;
58
59foreach $arg (@ARGV) {
60  if ($< != 0) {
61    die "Only root can specify parameters\n";
62  } else {
63    if ( ($arg eq '-?') || ($arg eq '--help') ) {
64      print_banner;
65      print "Usage: $0 [username]\n";
66      print "  -u             update only unix password (userPasswd)\n";
67      print "  -?, --help     show this help message\n";
68      exit (6);
69    } elsif ($arg eq '-u') {
70      $update_only_userPasswd=1;
71    } elsif (substr($arg,0) ne '-') {
72      $user = $arg;
73    }
74    $oldpass = 1;
75  }
76}
77
78if (!defined($user)) {
79  $user = getpwuid($<);		# $user=$ENV{"USER"};
80}
81
82# check if $user variable is not tainted
83# [TODO] create proper user mask
84$user =~ /^([-\@\ \w.]+\$?)$/ and $user = $1 or
85  die "$0: username '$user' is tainted\n";
86
87
88my ($dn,$ldap_master);
89# First, connecting to the directory
90if ($< != 0) {
91  # non-root user
92  if (!defined($oldpass)) {
93    # prompt for password
94    print "UNIX password: ";
95    system "stty -echo" if (-t STDIN);
96    chomp($oldpass=<STDIN>); 
97    system "stty echo" if (-t STDIN);
98    print "\n";
99
100    $config{masterDN}="uid=$user,$config{usersdn}";
101    $config{masterPw}="$oldpass";
102    $ldap_master=connect_ldap_master();
103    $dn=$config{masterDN};
104    if (!is_user_valid($user, $dn, $oldpass)) {
105      print "Authentication failure\n";
106      exit (10);
107    }
108  }
109} else {
110  # root user
111  $ldap_master=connect_ldap_master();
112  # test existence of user in LDAP
113  my $dn_line;
114  if (!defined($dn_line = get_user_dn($user))) {
115    print "$0: user $user doesn't exist\n";
116    exit (10);
117  }
118  $dn = get_dn_from_line($dn_line);
119}
120
121my $samba = is_samba_user($user);
122print "Changing password for $user\n";
123
124# prompt for new password
125
126my $pass;
127my $pass2;
128
129print "New password : ";
130system "stty -echo" if (-t STDIN);
131chomp($pass=<STDIN>);
132system "stty echo" if (-t STDIN);
133print "\n";
134
135print "Retype new password : ";
136system "stty -echo" if (-t STDIN);
137chomp($pass2=<STDIN>);
138system "stty echo" if (-t STDIN);
139print "\n";
140
141if ($pass ne $pass2) {
142  print "New passwords don't match!\n";
143  exit (10);
144}
145
146# Prepare '$hash_password' for 'userPassword'
147my $hash_password;
148# Generate password hash
149if ($config{with_slappasswd}) {
150  # checking if password is tainted: nothing is changed!!!!
151  # essential for perl 5.8
152  ($pass =~ /^(.*)$/ and $pass=$1) or
153    die "$0: user password is tainted\n";
154
155  # use slappasswd to generate hash
156  if ( $config{hash_encrypt} eq "CRYPT" && defined($config{crypt_salt_format}) ) {
157    open BUF, "-|" or
158      exec "$config{slappasswd}",
159	"-h","{$config{hash_encrypt}}",
160          "-c","$config{crypt_salt_format}",
161	    "-s","$pass";
162    $hash_password = <BUF>;
163    close BUF;
164  } else {
165    open(BUF, "-|") or
166      exec "$config{slappasswd}",
167	"-h","{$config{hash_encrypt}}",
168          "-s","$pass";
169    $hash_password = <BUF>;
170    close BUF;
171  }
172} else {
173  # use perl libraries to generate hash
174  $hash_password = make_hash($pass,$config{hash_encrypt},$config{crypt_salt_format});
175}
176# check if a hash was generated, otherwise die
177defined($hash_password) or
178  die "I cannot generate the proper hash!\n";
179chomp($hash_password);
180
181# First, connecting to the directory
182if ($< != 0) {
183  # if we are not root, we close the connection to re-open it as a normal user
184  $ldap_master->unbind;
185  $config{masterDN}="uid=$user,$config{usersdn}";
186  $config{masterPw}="$oldpass";
187  $ldap_master=connect_ldap_master();
188}
189
190# only modify smb passwords if smb user
191if ($samba == 1 and $update_only_userPasswd == 0) {
192  if (!$config{with_smbpasswd}) {
193    # generate LanManager and NT clear text passwords
194    my ($sambaLMPassword,$sambaNTPassword) = ntlmgen $pass;
195    # the sambaPwdLastSet must be updating
196    my $date=time;
197    my @mods;
198    push(@mods, 'sambaLMPassword' => $sambaLMPassword);
199    push(@mods, 'sambaNTPassword' => $sambaNTPassword);
200    push(@mods, 'sambaPwdLastSet' => $date);
201    if (defined $config{defaultMaxPasswordAge}) {
202      my $new_sambaPwdMustChange=$date+$config{defaultMaxPasswordAge}*24*60*60;
203      push(@mods, 'sambaPwdMustChange' => $new_sambaPwdMustChange);
204      if ($< ==0) {
205	push(@mods, 'sambaAcctFlags' => '[U]');
206      }
207    }
208    # Let's change nt/lm passwords
209    my $modify = $ldap_master->modify ( "$dn",
210					'replace' => { @mods }
211				      );
212    $modify->code && warn "failed to modify entry: ", $modify->error ;
213
214  } else {
215    if ($< != 0) {
216      my $FILE="|$config{smbpasswd} -s >/dev/null";
217      open (FILE, $FILE) || die "$!\n";
218      print FILE <<EOF;
219$oldpass
220$pass
221$pass
222EOF
223      ;
224      close FILE;
225    } else {
226      open FILE,"|-" or
227	exec "$config{smbpasswd}","$user","-s";
228      local $SIG{PIPE} = sub {die "buffer pipe terminated" };
229      print FILE <<EOF;
230$pass
231$pass
232EOF
233      ;
234      close FILE;
235    }
236  }
237}
238
239# Update 'userPassword' field
240my $modify = $ldap_master->modify ( "$dn",
241				    changes => [
242						replace => [userPassword => "$hash_password"]
243					       ]
244				  );
245$modify->code && warn "Unable to change password : ", $modify->error ;
246
247# take down session
248$ldap_master->unbind;
249
250exit 0;
251
252# Generates hash to be one of the following RFC 2307 schemes:
253# CRYPT,  MD5,  SMD5,  SHA, SSHA,  and  CLEARTEXT
254# SSHA is default
255# '%s' is a default crypt_salt_format
256# A substitute for slappasswd tool
257sub make_hash
258  {
259    my $hash_encrypt;
260    my $crypt_salt_format;
261
262    my $clear_pass=$_[0] or return undef;
263    $hash_encrypt='{' . $_[1] . '}' or $hash_encrypt = "{SSHA}";
264    $crypt_salt_format=$_[2] or $crypt_salt_format = '%s';
265
266    my $hash_pass;
267    if ($hash_encrypt eq "{CRYPT}" && defined($crypt_salt_format)) {
268      # Generate CRYPT hash
269      # for unix md5crypt $crypt_salt_format = '$1$%.8s'
270      my $salt = sprintf($crypt_salt_format,make_salt());
271      $hash_pass = "{CRYPT}" . crypt($clear_pass,$salt);
272
273    } elsif ($hash_encrypt eq "{MD5}") {
274      # Generate MD5 hash
275      $hash_pass = "{MD5}" . encode_base64( md5($clear_pass),'' );
276
277    } elsif ($hash_encrypt eq "{SMD5}") {
278      # Generate SMD5 hash (MD5 with salt)
279      my $salt = make_salt(4);
280      $hash_pass = "{SMD5}" . encode_base64( md5($clear_pass . $salt) . $salt,'');
281
282    } elsif ($hash_encrypt eq "{SHA}") {
283      # Generate SHA1 hash
284      $hash_pass = "{SHA}" . encode_base64( sha1($clear_pass),'' );
285
286    } elsif ($hash_encrypt eq "{SSHA}") {
287      # Generate SSHA hash (SHA1 with salt)
288      my $salt = make_salt(4);
289      $hash_pass = "{SSHA}" . encode_base64( sha1($clear_pass . $salt) . $salt,'' );
290
291    } elsif ($hash_encrypt eq "{CLEARTEXT}") {
292      $hash_pass=$clear_pass;
293
294    } else {
295      $hash_pass=undef;
296    }
297    return $hash_pass;
298  }
299
300# Generates salt
301# Similar to Crypt::Salt module from CPAN
302sub make_salt
303  {
304    my $length=32;
305    $length = $_[0] if exists($_[0]);
306  
307    my @tab = ('.', '/', 0..9, 'A'..'Z', 'a'..'z');
308    return join "",@tab[map {rand 64} (1..$length)];
309  }
310
311# - The End
312
313=head1 NAME
314
315smbldap-passwd - change user password
316
317=head1 SYNOPSIS
318
319smbldap-passwd [name]
320
321=head1 DESCRIPTION
322
323smbldap-passwd changes passwords for user accounts. A normal user may only change the password for their own account, the super user may change the password for any account.
324
325Password Changes
326 The user is first prompted for their old password, if one is present. This password is then tested against the stored password by binding to the server. The user has only one chance to enter the correct passwword. The super user is permitted to bypass this step so that forgotten passwords may be changed.
327 The user is then prompted for a replacement password. As a general guideline, passwords should consist of 6 to 8 characters including one or more from each of following sets:
328
329Lower case alphabetics
330
331Upper case alphabetics
332
333Digits 0 thru 9
334
335Punctuation marks
336
337Password will prompt again and compare the second entry against the first. Both entries are require to match in order for the password to be changed.
338
339=head1 SEE ALSO
340
341       passwd(1)
342
343=cut
344
345#'
346