# # Copyright (c) 2001 SuSE GmbH Nuernberg, Germany. All rights reserved. # Copyright (c) 2002 SuSE Linux AG Nuernberg, Germany. All rights reserved. # # # $Id: change_passwd.pm,v 1.4 2003/10/17 14:27:13 varkoly Exp $ # $| = 1; # do not buffer stdout package change_passwd; use strict; use CGI qw(-no_xhtml); use CGI::Carp qw(fatalsToBrowser); use Net::LDAP; use Digest::MD5; use MIME::Base64; use LDAPUtils; use Display; use Time::Local; use vars qw(@ISA); use subs qw(exit); # Select the correct exit function *exit = $ENV{MOD_PERL} ? \&Apache::exit : sub { CORE::exit }; @ISA = qw(Display); sub new { my $this = shift; my $self = new Display(@_); return bless $self, $this; } sub display { my $this = shift; my $cgi = $this->{"cgi"}; my $doit = $cgi->param('doit'); if(defined $doit && $doit eq "change") { $this->change_pass(); } else { $this->startup(); } } sub startup { my $this = shift; my $q = $this->{"cgi"}; my $session = $this->{"session"}; my $confParam = $session->{"confParam"}; my $message = ($session->getLanguage($confParam->{LANG},"CHANGE_PASSWD"))->data(); my $sessionID = $confParam->{"sessionID"}; my $LOGINuid = $this->{"uid"}; my $filter = $q->param('filter'); # C SCHULSERVER # my $uid = ( $LOGINuid eq "cyrus" ? $q->param('USER') : $LOGINuid ); my $uid = ""; if ( $LOGINuid eq "cyrus" || $confParam->{admin} ) { $uid = $q->param('USER') ; } else { $uid = $LOGINuid ; } if ( $uid eq '' ){ $uid = $LOGINuid ; } # E SCHULSERVER my $old_passwd = $q->param('old_passwd'); my $new_passwd = $q->param('new_passwd'); my $new2_passwd = $q->param('new2_passwd'); my $tab = $q->param('tab'); my $stab = $q->param('stab'); my $ldapbase = $confParam->{baseDN}; my $LOGINpasswd = $confParam->{passwd}; my $ldaphost = $confParam->{LDAPserver}; my $ldapport = $confParam->{LDAPport}; my $inbetween = ""; my $bind_dn = ""; my $bind_pass = ""; my $errs = ""; # C SCHULSERVER #if($LOGINuid eq "cyrus") { if($LOGINuid eq "cyrus" || $confParam->{admin} ) { # E SCHULSERVER $bind_dn = "uid=".$LOGINuid.",".$ldapbase; } else { $bind_dn = "uid=".$uid.",".$ldapbase; } $bind_pass = $LOGINpasswd; my $ldap = Net::LDAP->new("$ldaphost", port => $ldapport, version => 3); if(!defined $ldap) { $this->display_error("can't contact LDAP Server", "$confParam->{cgi_path}/menu.pl?sessionID=$sessionID"); exit; } my $mesg = $ldap->bind ( # bind to a directory with dn and password dn => "$bind_dn", password => "$bind_pass" ); if($mesg->code != 0) { $errs = "bind $message->{failed}\n\n"; $errs .= &LDAPerror($mesg); $this->display_error($errs, "$confParam->{cgi_path}/menu.pl?sessionID=$sessionID"); exit; } $mesg = $ldap->search( base => $ldapbase, scope => "one", filter=> "uid=$uid", attrs=> [ "userPassword" ] ); if( $mesg->count <= 0 ) { $ldap->unbind; $errs = "search $message->{failed}\n\n"; $errs .= &LDAPerror($mesg); $this->display_error($errs, "$confParam->{cgi_path}/menu.pl?sessionID=$sessionID"); exit; } my $res = $mesg->as_struct; my $mech = $res->{"uid=$uid,$ldapbase"}->{userpassword}->[0]; $mech =~ s/^{([\w]+)}.*/$1/; $mech = uc($mech); $ldap->unbind; #print STDERR "mech=$mech\n"; my $hidden = $q->hidden(-name=>"USER", -value=>$uid). $q->hidden(-name=>"doit", -value=>"change"). $q->hidden(-name=>"tab", -value=>$tab). $q->hidden(-name=>"stab", -value=>$stab). $q->hidden(-name=>"sessionID", -value=>$sessionID). $q->hidden(-name=>"filter", -value=>$filter). $q->end_form(); # C SCHULSERVER # if($LOGINuid eq "cyrus") { if($LOGINuid ne $uid) { # E SCHULSERVER $inbetween = $q->Tr( [ $this->help_th(2, $message->{change_passwd}, "CHANGE_PASSWD_ADMIN"), $q->td({-colspan=>2, -class=>'AdminSubHeader'}, "$message->{for_user}: $uid"), $q->td( ["$message->{new_pass}", $q->password_field(-name=>'new_passwd', -size=>20)]), $q->td( ["$message->{new2_pass}", $q->password_field(-name=>'new2_passwd', -size=>20)]), $q->td( ["$message->{pwmech}", $q->popup_menu(-name=>'pw_mech', -values=>["CRYPT","SMD5"], -defaults=>$mech)]), $q->td( ["$message->{must_change}", $q->checkbox(-name=>'must_change', -label=>"", -checked=>"")]), $q->td({-colspan=>2, -align=>"center"}, $q->submit(-class=>"button",-name=>"$message->{update}"). $q->hidden(-name=>"filter", -value=>$filter). $q->hidden(-name=>"filtered", -value=>1).$hidden) ]); } else { $inbetween = $q->Tr( [ $this->help_th(2, $message->{change_passwd}, "CHANGE_PASSWD_USER"), ( defined $confParam->{mustchangepasswd} ? $q->td({-colspan=>2, -class=>'AdminSubHeaderRed'}, "$message->{password_expired_red}") : $q->td({-colspan=>2, -class=>'AdminSubHeader'}, "$message->{for_user}: $uid") ), $q->td(["$message->{old_pass}", $q->password_field(-name=>'old_passwd', -size=>20)]), $q->td(["$message->{new_pass}", $q->password_field(-name=>'new_passwd', -size=>20)]), $q->td(["$message->{new2_pass}", $q->password_field(-name=>'new2_passwd', -size=>20)]), $q->td( ["$message->{pwmech}", $q->popup_menu(-name=>'pw_mech', -values=>["CRYPT","SMD5"], -default=>$mech) ]), $q->td({-colspan=>2, -align=>"center"}, $q->submit(-class=>"button",-name=>"$message->{update}").$hidden) ]); } my $html = ""; $html .= $q->start_table({-class=>'AdminBorder', -cellspacing=>2, -cellpadding=>0, -width=>"100%"}); $html .= $q->start_Tr(); $html .= $q->start_td(); $html .= $q->start_table({-cellspacing=>0, -cellpadding=>2, -width=>"100%"}); $html .= $q->start_form(-action=>$confParam->{cgi_path}."/change_passwd.pl"); $html .= $inbetween; $html .= $q->end_form(); $html .= $q->end_table(); $html .= $q->end_td(); $html .= $q->end_Tr(); $html .= $q->end_table(); # C SCHULSERVER #if($LOGINuid eq "cyrus") { if($LOGINuid eq "cyrus" || $confParam->{admin} ) { # E SCHULSERVER $this->back_url("$confParam->{cgi_path}/browse_user.pl?sessionID=$sessionID\&tab=$tab\&stab=$stab\&filter=$filter\&filtered=1"); } $this->SUPER::display($html); } sub change_pass { my $this = shift; my $q = $this->{"cgi"}; my $session = $this->{"session"}; my $confParam = $session->{"confParam"}; my $message = ($session->getLanguage($confParam->{LANG},"CHANGE_PASSWD"))->data(); my $sessionID = $confParam->{"sessionID"}; my $LOGINuid = $this->{"uid"}; my $filter = $q->param('filter'); my $uid = $q->param('USER'); my $old_passwd = $q->param('old_passwd'); my $new_passwd = $q->param('new_passwd'); my $new2_passwd = $q->param('new2_passwd'); my $tab = $q->param('tab'); my $stab = $q->param('stab'); my $pwmech = $q->param('pw_mech'); my $must_change = $q->param('must_change'); my $LOGINpasswd = $confParam->{passwd}; my $ldapbase = $confParam->{baseDN}; my $ldaphost = $confParam->{LDAPserver}; my $ldapport = $confParam->{LDAPport}; my $errs = ""; # C SCHULSERVER #if($uid eq "" || $new_passwd eq "" || $new2_passwd eq "" || ( $LOGINuid ne "cyrus" && $old_passwd eq "" )) { if($uid eq "" || $new_passwd eq "" || $new2_passwd eq "" || ( $LOGINuid ne "cyrus" && !$confParam->{admin} && $old_passwd eq "" )) { # E SCHULSERVER $this->display_error($message->{miss_some_values}, "$confParam->{cgi_path}/change_passwd.pl?sessionID=$sessionID". "\&filter=$filter&filtered=1\&tab=$tab\&stab=$stab\&USER=$uid"); exit; } elsif($new_passwd ne $new2_passwd) { $this->display_error($message->{new_passwds_not_equal}, "$confParam->{cgi_path}/change_passwd.pl?sessionID=$sessionID". "\&filter=$filter&filtered=1\&tab=$tab\&stab=$stab\&USER=$uid"); exit; } elsif($new_passwd eq $old_passwd) { $this->display_error($message->{old_equals_new}, "$confParam->{cgi_path}/change_passwd.pl?sessionID=$sessionID". "\&filter=$filter&filtered=1\&tab=$tab\&stab=$stab\&USER=$uid"); exit; } elsif(length($new_passwd) < 5 || ( $pwmech eq "SMD5" ? 0 : length($new_passwd) > 8 ) ) { $this->display_error($message->{incorrect_passwd_length}, "$confParam->{cgi_path}/change_passwd.pl?sessionID=$sessionID". "\&filter=$filter&filtered=1\&tab=$tab\&stab=$stab\&USER=$uid"); exit; } else { my $bind_dn = ""; my $bind_pass = ""; # C SCHULSERVER #if($LOGINuid eq "cyrus") { if($LOGINuid eq "cyrus" || $confParam->{admin} ) { # E SCHULSERVER $bind_dn = "uid=".$LOGINuid.",".$ldapbase; $bind_pass = $LOGINpasswd; } else { $bind_dn = "uid=".$uid.",".$ldapbase; $bind_pass = $old_passwd; } my $ldap = Net::LDAP->new("$ldaphost", port => $ldapport, version => 3); if(!defined $ldap) { $this->display_error("can't contact LDAP Server", "$confParam->{cgi_path}/menu.pl?sessionID=$sessionID"); exit; } my $mesg = $ldap->bind ( # bind to a directory with dn and password dn => "$bind_dn", password => "$bind_pass" ); if($mesg->code != 0) { $errs = "bind $message->{failed}\n\n"; $errs .= &LDAPerror($mesg); $this->display_error($errs, "$confParam->{cgi_path}/menu.pl?sessionID=$sessionID"); exit; } my $crypt_passwd = "*"; if( $pwmech eq "CRYPT" ) { my $salt = pack("C2",(int(rand 26)+65),(int(rand 26)+65)); $crypt_passwd = crypt $new_passwd,$salt; $crypt_passwd = "{crypt}".$crypt_passwd; } elsif( $pwmech eq "SMD5" ) { my $salt = pack("C5",(int(rand 26)+65),(int(rand 26)+65),(int(rand 26)+65), (int(rand 26)+65), (int(rand 26)+65)); my $ctx = new Digest::MD5(); $ctx->add($new_passwd); $ctx->add($salt); $crypt_passwd = "{smd5}".encode_base64($ctx->digest.$salt, ""); } my $days_since_1970 = int(timelocal(localtime()) / 3600 / 24); my @mod_op; push @mod_op, "replace", [ "userpassword", "$crypt_passwd" ]; if( defined $must_change && $must_change ne "" ) { push @mod_op, "replace", [ "shadowlastchange", "0" ]; } else { push @mod_op, "replace", [ "shadowlastchange", "$days_since_1970" ]; } $mesg = $ldap->modify( "uid=$uid,$ldapbase", changes => \@mod_op ); if($mesg->code != 0) { $errs = "$message->{change_passwd} $message->{failed}\n\n"; $errs .= &LDAPerror($mesg); $ldap->unbind; # take down session $this->display_error($errs, "$confParam->{cgi_path}/menu.pl?sessionID=$sessionID"); exit; } # C SCHULSERVER #if($LOGINuid ne "cyrus") { if($LOGINuid eq $uid ) { # E SCHULSERVER $mesg = $session->modify({passwd => "$new_passwd"} ); if( $mesg->code() ne "OK" ) { $this->display_error($message->{write2session_failed}, "$confParam->{cgi_path}/menu.pl?sessionID=$sessionID"); exit; } $session->load($sessionID); } # look whether this user is smb enabled my ($ret, $err) = LDAP_is_samba_user($confParam, $ldap, $uid); if( $ret == 1 ) { #print STDERR "changing sambaAccount\n"; $mesg = $session->suad_change_smbpw($uid, $old_passwd, $new_passwd); if( $mesg->code() ne "OK" ) { $this->display_error("$message->{samba_pw_failed}: ".$mesg->code(), "$confParam->{cgi_path}/menu.pl?sessionID=$sessionID"); exit; } }elsif( $ret == -1 ){ $this->display_error("search failed: ".$mesg->code(), "$confParam->{cgi_path}/menu.pl?sessionID=$sessionID"); exit; } $ldap->unbind; # take down session my $url; if( defined $confParam->{mustchangepasswd} ) { my %temp = ("mustchangepasswd" => undef); $session->modify(\%temp); $session->load($sessionID); $message = ($session->getLanguage($session->{confParam}->{LANG},"CHANGE_PASSWD"))->data(); if( ( defined $::CONF_GLOBAL{'GENERAL'}{'DisableGroupwareLogin'} && $::CONF_GLOBAL{'GENERAL'}{'DisableGroupwareLogin'} eq "true" ) || $confParam->{loginDestination} eq "config" ) { $url = "$confParam->{cgi_path}/menu.pl?sessionID=$sessionID"; } elsif( $confParam->{loginDestination} eq "groupware" ) { my $remotehost = $::CONF_GLOBAL{'GENERAL'}{'GroupwareHostname'}; if( defined $remotehost && $remotehost ne "" ) { $remotehost = &getPROTO().$remotehost; } else { $remotehost = ""; } $url = "$remotehost/servlet/intranet?SITE=beforeAuth&sessionID=$sessionID"; } elsif( $confParam->{loginDestination} eq "webmail" ) { my $remotehost = $::CONF_GLOBAL{'GENERAL'}{'GroupwareHostname'}; if( defined $remotehost && $remotehost ne "" ) { $remotehost = &getPROTO().$remotehost; } else { $remotehost = ""; } $url = "$remotehost/servlet/webmail?sessionID=$sessionID\&SITE=mauth"; } } else { $url = "$confParam->{cgi_path}/change_passwd.pl?sessionID=$sessionID\&tab=$tab\&stab=$stab"; } # # comment this out, if you want to also update /etc/sasldb # to use strong auth mechs # #if($LOGINuid eq "cyrus") { # $mesg = $session->suad_saslsetpw($uid, $new_passwd); # if( $mesg->code() ne "OK" ) { # $this->display_error("Failed to set SASL password", # "$confParam->{cgi_path}/change_passwd.pl?sessionID=$sessionID\&tab=$tab\&stab=$stab"); # } #} if($LOGINuid eq "cyrus") { $this->display_msg("$message->{change_passwd_success}", "$confParam->{cgi_path}/browse_user.pl?sessionID=$sessionID". "\&filter=$filter&filtered=1\&tab=$tab\&stab=$stab", 3); } else { $this->display_msg("$message->{change_passwd_success}", $url, 3); } } } 1;