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