#!/usr/bin/perl ##### user configurable variables $ldap_server = "ldap.mesd.k12.or.us"; $ldap_port = 389; $binddn = "dc=k12,dc=or,dc=us"; # also see the attributes table at the end of the script $tablebgcolor = 'ddccdd'; $bgcolor = '99cccc'; $headercolor = gold; ##### non-configurable stuff use CGI qw(:standard); use CGI::Carp qw(fatalsToBrowser); use Net::LDAP; use File::Basename; # grab this CGI's name ($cgi_name) = fileparse($0); # forwards sub init_attributes; sub do_intro; ##### main code body ###### # new CGI, get the user's name $query = new CGI; $user = $query->param('user'); $password = $query->param('secret'); $dname = $query->param('distinguished_name'); # information about adds/deletes/modifies $do_add = $query->param('Add'); $do_delete = $query->param('Delete'); $do_modify = $query->param('Modify'); $attrib_field = $query->param('attrib_field'); $attrib_value = $query->param('attrib_value'); $userpassword1 = $query->param('userpassword1'); $userpassword2 = $query->param('userpassword2'); $attrib_value_orig = $query->param('attrib_value_orig'); # prime attributes variable %attributes = init_attributes; # html headers print $query->header; print $query->start_html( -title => 'MESD Directory Information', -bgcolor => $bgcolor); ### if the $user variable is no set, we don't know who to look for, ### go to the intro page: if ( ! $user ) { do_intro }; ### if the $dname variable is set, we know exactly who we are looking ### for, now we need to figure out what to do to them: if ( $dname ) { if ( $do_add ) { change_entry( 'add', $attrib_field, $attrib_value, $attrib_value_orig, $password, $dname, $ldap_server, $ldap_port ) } elsif ( $do_delete ) { $attrib_value = $attrib_value_orig; # make sure no changes were made change_entry( 'delete', $attrib_field, $attrib_value, $attrib_value_orig, $password, $dname, $ldap_server, $ldap_port ) } elsif ( $do_modify ) { change_entry( 'modify', $attrib_field, $attrib_value, $attrib_value_orig, $password, $dname, $ldap_server, $ldap_port ) } else { create_user_table($password, $dname, $query, $ldap_server, $ldap_port) } } ### if $user is set, but $dname is not we need to search for the user: ### my $ldap = new Net::LDAP ($ldap_server, port =>$ldap_port); $ldap->bind(DN => $binddn, password=> $ldap_port) || die "fail to bind"; # set up the ldap search $mesg = $ldap->search( base => $binddn, filter => "(|(cn=*$user*)(mail=*$user*)(uid=*$user*))", attrs => "dn" ); # do the search $num_found=0; while ( $entry = $mesg->pop_entry() ) { $target_dn = $entry->dn() ; ($junk, $target_name, $more_junk) = split ("=", $target_dn); ($target_name) = split (",", $target_name); $dn_list{$target_dn} = $target_name; $num_found++; } $ldap->unbind(); if ($num_found == 0) { not_found }; if ($num_found > 1) { pick_from_list($password, %dn_list) }; if ($num_found == 1) { $dname = $target_dn; $query->param( -name => 'distinguished_name', -value => $target_dn); create_user_table($password, $target_dn, $query, $ldap_server, $ldap_port) }; ###### end main code body ###### ###### Sub routines ###### sub not_found { print ("No names match, please try again
"); do_intro; } ##### sub do_intro { print h1("MESD Directory Information"); print $query->start_form(-action=>$cgi_name); print (""); print (""); print "
"); print ("Please Log In
Your Name: "; print $query->textfield(-name=>'user'); print ("
"); print "Password: "; print $query->password_field(-name=>'secret'); print ("
"); print $query->submit(-name=>'login'); print $query->endform; print $query->end_html; exit; } ##### sub pick_from_list { ($password, %dn_list) = @_; print h1("More than one name was found, please pick one:"); print $query->start_form(-action=>$cgi_name); print ("
"); print (""); print (""); print $query->submit; print $query->endform; exit; } ##### sub create_user_table { ($password, $dn, $query, $ldap_server, $ldap_port) = @_; ($junk, $cn, $junk) = split ("=", $dn); ($cn, $junk) = split (",", $cn); # make a new CGI # $query = new CGI; # prime attributes variable %attributes = init_attributes; # make the connection to the ldap server my $ldap = new Net::LDAP ($ldap_server, port =>$ldap_port); $ldap->bind(DN => $dn, password=> $password) || die "fail to bind"; # set up the ldap search $mesg = $ldap->search( base => $dn, filter => "(cn=$cn)" ); $attr = $mesg->entry; foreach $entry ($attr->get(userpassword)) { $pword = $entry } ; if ( ! valid_password($pword, $password) ) { print h1("Username or Password Incorrect"); $query->param(-name=>'user', -value=>''); $query->param(-name=>'secret', -value=>''); do_intro; }; print ("
"); print h1("MESD Directory information for $cn"); # change password table print (""); print (""); print (""); print $query->endform; print ("
"); print ("Change Your Password
"); print $query->start_form(-action=>$cgi_name); print ("Enter new password: "); print $query->password_field(-name =>'userpassword1', -size =>20); print (""); print $query->submit(-name=>'Modify', -value=>'Change'); print ("
Verify: "); print $query->password_field(-name =>'userpassword2', -size =>20); print (""); print (""); print (""); print (""); # get old password $mesg = $ldap->search( base => $dn, filter => "(cn=$cn)"); $attr = $mesg->entry; foreach $entry ($attr->get(userpassword)) { $attrib_value_orig = $entry } ; print (""); print (""); print ("

"); # directory info table print (""); print (""); foreach $entry ($mesg->all_entries) { foreach $attr ( $entry->attributes ) { foreach $attr_value ( $entry->get($attr) ) { $description = $attributes{$attr}[0]; $user_may_edit = $attributes{$attr}[1]; $user_may_delete = $attributes{$attr}[2]; $user_may_add = $attributes{$attr}[3]; $user_may_view = $attributes{$attr}[4]; if ($user_may_view) { if ($user_may_edit) { print $query->start_form(-action=>$cgi_name); print (""); print $query->endform; } else { print $query->start_form(action=>none); print (""); print (" "); print $query->endform; } } } } } print ("
"); print ("Directory Information
 ", $description, ""); print $query->textfield( -name =>'attrib_value', -default =>$attr_value, -size =>35 ); print $query->reset; print (""); print (""); print (""); print (""); print (""); print (""); if ($user_may_edit) { print $query->submit(-name=>'Modify', -value=>'Change') } if ($user_may_delete) { print $query->submit(-name=>'Delete', -value=>'Delete') } print ("
  ", $description, "   ", $attr_value, ""); print (" 

"); # add more info table print (""); print (""); print ("
"); print ("Add additional information to the directory
"); print $query->start_form(-action=>$cgi_name); print (""); print $query->textfield(-name=>'attrib_value', -size=>35); print $query->reset; print (""); print (""); print (""); print (""); print $query->submit(-name=>'Add', -value=>' Add '); # print $query->submit(-name=>'Add', -value=>'Add', -width=>'100%'); print ("
"); print $query->endform; print ("

"); $ldap->unbind(); print $query->end_html; exit; } ########## sub change_entry { ($action, $attrib, $attrib_value, $attrib_value_orig, $password, $dn, $ldap_server, $ldap_port) = @_; # prime attributes variable %attributes = init_attributes; my $ldap = new Net::LDAP ($ldap_server, port =>$ldap_port) || die "failed"; $ldap->bind( dn => $dn, password => $password ) || die "$@"; $user_may_edit = $attributes{$attrib}[1]; $user_may_delete = $attributes{$attrib}[2]; $user_may_add = $attributes{$attrib}[3]; if ( $action eq "add" && $user_may_add ) { $result = $ldap->modify($dn, $action => { $attrib => $attrib_value }) } elsif ( $action eq "delete" && $user_may_delete ) { $result = $ldap->modify($dn, $action => { $attrib => $attrib_value }) } elsif ($action eq "modify" && $user_may_edit) { if ($attrib eq 'userpassword') { # make sure it's a reasonable pw check_password($userpassword1, $userpassword2); $attrib_value = encrypt_password($userpassword1, random_salt) } $result = $ldap->modify($dn, changes => [ delete => [ $attrib => [$attrib_value_orig]], add => [ $attrib => $attrib_value] ] ); } $result->code && die $result->error; $ldap->unbind; if ( $action eq "add" ) { print ("Added: ") } elsif ( $action eq "delete") { print ("Deleted: ") } else { if ($attrib eq 'userpassword') { print ("Password has been changed"); } else { print ("New Value: ") } } print ( "$attributes{$attrib}[0] = $attrib_value") unless ($attrib eq 'userpassword'); print ("
"); print $query->start_form(-action=>$cgi_name); print (""); print (""); print (""); print $query->submit(-name=>'Return') unless ($attrib eq 'userpassword'); print $query->endform; print (""); print $query->start_form(-action=>$cgi_name); print $query->submit(-name=>'Exit'); print $query->endform; print ("
"); print $query->end_html; exit; } ########## sub check_password { $password_to_check = shift; $password_double_check = shift; if ( $password_to_check ne $password_double_check ) { print h1("Error: passwords do not match, please try again"); print $query->start_form(-action=>$cgi_name); print (""); print (""); print (""); print $query->submit(-name=>'Return'); print $query->endform; exit; } if ( length($password_to_check) < 4 ) { print h1("Error: password must be at least 4 characters"); print $query->start_form(-action=>$cgi_name); print (""); print (""); print (""); print $query->submit(-name=>'Return'); print $query->endform; exit; } } ########## sub valid_password { ($ldap_password, $entered_password) = @_; $salt = substr $ldap_password, 7, 2; my $result = 0; if ( $ldap_password eq encrypt_password($entered_password, $salt)) { $result = 1; }; return $result; } ########## sub random_salt { my @range = ('A'..'Z','a'..'z','0'..'9','.','/'); my $highest_array_value = $#range; my $random_number1 = rand $highest_array_value; my $random_number2 = rand $highest_array_value; $return_salt = $range[$random_number1] . $range[$random_number2]; return $return_salt; } ########## sub encrypt_password { my ($password, $salt) = @_; $return_encrypted_password = '{crypt}' . crypt($password, $salt); return $return_encrypted_password; } ########## sub sanitize_data { #hmm, I probably should break down & read the RFC on what needs to #be escaped rather than just escaping everything. # search filters rcf1558: *() # dn rcf2253: ,+"\<>; my ($data) = @_; $data =~ s/([\&;\`'\\\|"*?~<>^\(\)\[\]\{\}\$\n\r])/\\$1/g; $data =~ s/\0//g; return $data; } ########## sub init_attributes { # attribute syntax: # # Attribute Name => Description, user may: e d a v # d e d i # i l d e # t e w # t # e %attributes = ( audio => [ "Audio" , 0, 0, 0, 0 ], c => [ "Country" , 0, 0, 0, 1 ], carlicense => [ "Car License Number" , 1, 1, 1, 1 ], cn => [ "Full Name" , 1, 0, 0, 1 ], dc => [ "Domain Component" , 0, 0, 0, 0 ], distinguishedname => [ "Distinguised Name" , 0, 0, 0, 0 ], jpegphoto => [ "Photo" , 0, 0, 0, 0 ], departmentnumber => [ "Department Number" , 0, 0, 0, 0 ], description => [ "Description" , 1, 1, 1, 1 ], dn => [ "Distinguised Name" , 0, 0, 0, 0 ], facsimiletelephonenumber => [ "Fax Number" , 1, 1, 1, 1 ], gidnumber => [ "Group ID Number" , 0, 0, 0, 0 ], givenname => [ "First Name" , 1, 0, 0, 1 ], homepostaladdress => [ "Home Postal Address" , 0, 0, 0, 0 ], homedirectory => [ "Home Directory" , 0, 0, 0, 0 ], homephone => [ "Home Phone Number" , 0, 0, 0, 0 ], l => [ "City" , 1, 1, 1, 1 ], initials => [ "Initials" , 1, 1, 1, 1 ], labeledurl => [ "Web Page" , 1, 1, 1, 1 ], houseidentifier => [ "Building" , 1, 1, 1, 1 ], loginshell => [ "Login Shell" , 0, 0, 0, 0 ], mail => [ "Email Address" , 0, 0, 0, 1 ], maildrop => [ "Email Destination" , 0, 0, 0, 0 ], mailacceptinggeneralid => [ "Email Alias" , 0, 0, 0, 0 ], mobile => [ "Cell Phone" , 1, 1, 1, 1 ], o => [ "Organization" , 0, 0, 0, 0 ], objectclass => [ "Object Class" , 0, 0, 0, 0 ], ou => [ "Department" , 0, 0, 0, 1 ], pager => [ "Pager" , 1, 1, 1, 1 ], postofficebox => [ "Post Office Box" , 1, 1, 1, 1 ], postaladdress => [ "Postal Address" , 1, 1, 1, 1 ], postalcode => [ "Postal Code" , 1, 1, 1, 1 ], registeredaddress => [ "Registered Mail Address", 0, 0, 0, 0 ], roomnumber => [ "Room Number" , 1, 1, 1, 1 ], seealso => [ "Also See" , 1, 1, 1, 1 ], sn => [ "Last Name" , 1, 0, 0, 1 ], st => [ "State" , 1, 1, 1, 1 ], street => [ "Street" , 1, 1, 1, 1 ], telephonenumber => [ "Telephone Number" , 1, 1, 1, 1 ], title => [ "Title" , 0, 0, 0, 1 ], uid => [ "Login Name" , 0, 0, 0, 1 ], uidnumber => [ "User ID Number" , 0, 0, 0, 0 ], userpassword => [ "Password" , 1, 0, 0, 0 ] ); return %attributes; }