Project

General

Profile

1 2341 sgarg
#!/usr/bin/perl -w
2
 #
3
 #  '$RCSfile$'
4
 #  Copyright: 2001 Regents of the University of California
5
 #
6
 #   '$Author$'
7
 #     '$Date$'
8
 # '$Revision$'
9
 #
10
 # This program is free software; you can redistribute it and/or modify
11
 # it under the terms of the GNU General Public License as published by
12
 # the Free Software Foundation; either version 2 of the License, or
13
 # (at your option) any later version.
14
 #
15
 # This program is distributed in the hope that it will be useful,
16
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 # GNU General Public License for more details.
19
 #
20
 # You should have received a copy of the GNU General Public License
21
 # along with this program; if not, write to the Free Software
22
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23
 #
24
25
#
26
# This is a web-based application for allowing users to register a new
27
# account for Metacat access.  We currently only support LDAP even
28
# though metacat could potentially support other types of directories.
29
#
30
use strict;       # turn on strict syntax checking.
31
use Template;     # load the template-toolkit module.
32
use CGI;          # load the CGI module.
33
use Net::LDAP;    # load the LDAP net libraries
34
use Net::SMTP;    # load the SMTP net libraries
35
use Digest::SHA1; # for creating the password hash
36
use MIME::Base64; # for creating the password hash
37
use URI;          # for parsing URL syntax
38
use AppConfig qw(:expand :argcount);
39
40
# Set up our default configuration
41
my $ldapurl = "@ldapurl@";
42 3175 tao
my $mainldapurl = "@ldapurl@";
43 2341 sgarg
my $root = "@user@";
44
my $rootpw = "@password@";
45
my $searchBase = "@ldapSearchBase@";
46
my $templatesDir = "@templates.dir@";
47
my $mailhost = "@mailhost@";
48
my $sender = "@sender@";
49 3175 tao
my $TIMEOUT = 20;
50 3177 tao
my $mainldapdownmessage = "The main ldap server " . $mainldapurl . " is down!";
51 2341 sgarg
52
# Get the CGI input variables
53
my $query = new CGI;
54
55
my $debug = 0;
56
57
#--------------------------------------------------------------------------80c->
58
# Set up the Template Toolkit to read html form templates
59
60
61
# set some configuration options for the template object
62
my $config_templates = {
63
             INCLUDE_PATH => $templatesDir,
64
             INTERPOLATE  => 0,
65
             POST_CHOMP   => 1,
66
             };
67
68
# create an instance of the template
69 3177 tao
my $template = Template->new($config_templates) || handleGeneralServerFailure($Template::ERROR);
70 2341 sgarg
71
# Read the ldapweb.cfg file
72
my $config = AppConfig->new({
73
    GLOBAL => { ARGCOUNT => ARGCOUNT_ONE, } });
74
75
$config->define("ldapurl", { ARGCOUNT => ARGCOUNT_HASH} );
76
$config->define("ldapsearchbase", { ARGCOUNT => ARGCOUNT_HASH} );
77
$config->define("dn", { ARGCOUNT => ARGCOUNT_HASH} );
78
$config->define("filter", { ARGCOUNT => ARGCOUNT_HASH} );
79
$config->define("user", { ARGCOUNT => ARGCOUNT_HASH} );
80
$config->define("password", { ARGCOUNT => ARGCOUNT_HASH} );
81
82
my $cfgfile = "ldapweb.cfg";
83
$config->file($cfgfile);
84
my $config_ldapurl = $config->get('ldapurl');
85
my $config_ldapsearchbase = $config->get('ldapsearchbase');
86
my $config_dn = $config->get('dn');
87
my $config_filter = $config->get('filter');
88
my $config_user = $config->get('user');
89
my $config_password = $config->get('password');
90
91
my @orglist;
92
foreach my $neworg (keys %$config_dn) {
93
    push(@orglist, $neworg);
94
    debug($neworg);
95
}
96
97
98
#--------------------------------------------------------------------------80c->
99
# Define the main program logic that calls subroutines to do the work
100
#--------------------------------------------------------------------------80c->
101
102
103
# The processing step we are handling
104
my $stage = $query->param('stage') || '@defaultStage@';
105
106
my $cfg = $query->param('cfg');
107
108
# define the possible stages
109
my %stages = (
110
              'initregister'      => \&handleInitRegister,
111
              'register'          => \&handleRegister,
112
              'registerconfirmed' => \&handleRegisterConfirmed,
113
              'simplesearch'      => \&handleSimpleSearch,
114
              'initaddentry'      => \&handleInitAddEntry,
115
              'addentry'          => \&handleAddEntry,
116
              'initmodifyentry'   => \&handleInitModifyEntry,
117
              'modifyentry'       => \&handleModifyEntry,
118 2972 jones
              'changepass'        => \&handleChangePassword,
119
              'initchangepass'    => \&handleInitialChangePassword,
120 2341 sgarg
              'resetpass'         => \&handleResetPassword,
121 2414 sgarg
              'initresetpass'     => \&handleInitialResetPassword,
122 2341 sgarg
             );
123
# call the appropriate routine based on the stage
124
if ( $stages{$stage} ) {
125
  $stages{$stage}->();
126
} else {
127
  &handleResponseMessage();
128
}
129
130
#--------------------------------------------------------------------------80c->
131
# Define the subroutines to do the work
132
#--------------------------------------------------------------------------80c->
133
134
135
#
136
# create the initial registration form
137
#
138
sub handleInitRegister {
139
  my $vars = shift;
140
141
  print "Content-type: text/html\n\n";
142
  # process the template files:
143
  my $templateVars = { stage => "register", cfg => $cfg };
144 2857 sgarg
  #$$templateVars{'orgList'} = \@orglist;
145 2341 sgarg
  $$templateVars{'orgList'} = \@orglist;
146
  $template->process( "@defaultHeader@", $templateVars);
147
  $template->process( "@register@", $templateVars);
148
  $template->process( "@defaultFooter@", $templateVars);
149
150
  exit();
151
}
152
153
#
154
# process input from the register stage, which occurs when
155
# a user submits form data to create a new account
156
#
157
sub handleRegister {
158
159
    print "Content-type: text/html\n\n";
160
161
    my $allParams = { 'givenName' => $query->param('givenName'),
162
                      'sn' => $query->param('sn'),
163
                      'o' => $query->param('o'),
164
                      'mail' => $query->param('mail'),
165
                      'uid' => $query->param('uid'),
166
                      'userPassword' => $query->param('userPassword'),
167
                      'userPassword2' => $query->param('userPassword2'),
168
                      'title' => $query->param('title'),
169
                      'telephoneNumber' => $query->param('telephoneNumber') };
170
    # Check that all required fields are provided and not null
171
    my @requiredParams = ( 'givenName', 'sn', 'o', 'mail',
172
                           'uid', 'userPassword', 'userPassword2');
173
    if (! paramsAreValid(@requiredParams)) {
174
        my $errorMessage = "Required information is missing. " .
175
            "Please fill in all required fields and resubmit the form.";
176
        my $templateVars = { stage => "register",
177
                             cfg => $cfg,
178
                             allParams => $allParams,
179
                             errorMessage => $errorMessage };
180 2972 jones
        $$templateVars{'orgList'} = \@orglist;
181 2341 sgarg
        $template->process( "@defaultHeader@", $templateVars);
182
        $template->process( "@register@", $templateVars);
183
        $template->process( "@defaultFooter@", $templateVars);
184
        exit(0);
185
    } else {
186 2972 jones
        my $o = $query->param('o');
187
        $ldapurl = $config_ldapurl->{$o};
188
        $searchBase = $config_ldapsearchbase->{$o};
189 2341 sgarg
    }
190
191
    # Search LDAP for matching entries that already exist
192
    # Some forms use a single text search box, whereas others search per
193
    # attribute.
194
    my $filter;
195
    if ($query->param('searchField')) {
196
197
      $filter = "(|" .
198
                "(uid=" . $query->param('searchField') . ") " .
199
                "(mail=" . $query->param('searchField') . ")" .
200
                "(&(sn=" . $query->param('searchField') . ") " .
201
                "(givenName=" . $query->param('searchField') . "))" .
202
                ")";
203
    } else {
204
      $filter = "(|" .
205
                "(uid=" . $query->param('uid') . ") " .
206
                "(mail=" . $query->param('mail') . ")" .
207
                "(&(sn=" . $query->param('sn') . ") " .
208
                "(givenName=" . $query->param('givenName') . "))" .
209
                ")";
210
    }
211
212
    my @attrs = [ 'uid', 'o', 'cn', 'mail', 'telephoneNumber', 'title' ];
213
    my $found = findExistingAccounts($ldapurl, $searchBase, $filter, \@attrs);
214
215
    # If entries match, send back a request to confirm new-user creation
216
    if ($found) {
217
      my $templateVars = { stage => "registerconfirmed",
218
                           cfg => $cfg,
219
                           allParams => $allParams,
220
                           foundAccounts => $found };
221
      $$templateVars{'orgList'} = \@orglist;
222
      $template->process( "@defaultHeader@", $templateVars);
223
      $template->process( "@registerMatch@", $templateVars);
224
      $template->process( "@register@", $templateVars);
225
      $template->process( "@defaultFooter@", $templateVars);
226
227
    # Otherwise, create a new user in the LDAP directory
228
    } else {
229 3175 tao
        #print("ingore create account\n");
230 2341 sgarg
        createAccount($allParams);
231
    }
232
233
    exit();
234
}
235
236
#
237
# process input from the registerconfirmed stage, which occurs when
238
# a user chooses to create an account despite similarities to other
239
# existing accounts
240
#
241
sub handleRegisterConfirmed {
242
243
    my $allParams = { 'givenName' => $query->param('givenName'),
244
                      'sn' => $query->param('sn'),
245 2857 sgarg
                      #'o' => $query->param('o'),
246
                      'o' => 'unaffiliated',
247 2341 sgarg
                      'mail' => $query->param('mail'),
248
                      'uid' => $query->param('uid'),
249
                      'userPassword' => $query->param('userPassword'),
250
                      'userPassword2' => $query->param('userPassword2'),
251
                      'title' => $query->param('title'),
252
                      'telephoneNumber' => $query->param('telephoneNumber') };
253
    print "Content-type: text/html\n\n";
254
    createAccount($allParams);
255
256
    exit();
257
}
258
259
#
260
# change a user's password upon request
261
#
262
sub handleChangePassword {
263
264
    print "Content-type: text/html\n\n";
265
266
    my $allParams = { 'test' => "1", };
267
    if ($query->param('uid')) {
268
        $$allParams{'uid'} = $query->param('uid');
269
    }
270
    if ($query->param('o')) {
271
        $$allParams{'o'} = $query->param('o');
272 2972 jones
        my $o = $query->param('o');
273
274
        $ldapurl = $config_ldapurl->{$o};
275 2341 sgarg
        $searchBase = $config_ldapsearchbase->{$o};
276
    }
277
278
279
    # Check that all required fields are provided and not null
280
    my @requiredParams = ( 'uid', 'o', 'oldpass',
281
                           'userPassword', 'userPassword2');
282
    if (! paramsAreValid(@requiredParams)) {
283
        my $errorMessage = "Required information is missing. " .
284
            "Please fill in all required fields and submit the form.";
285
        my $templateVars = { stage => "changepass",
286
                             cfg => $cfg,
287
                             allParams => $allParams,
288
                             errorMessage => $errorMessage };
289 2972 jones
        $$templateVars{'orgList'} = \@orglist;
290 2341 sgarg
        $template->process( "@defaultHeader@", $templateVars);
291
        $template->process( "@defaultChangePass@", $templateVars);
292
        $template->process( "@defaultFooter@", $templateVars);
293
        exit(0);
294
    }
295
296
    # We have all of the info we need, so try to change the password
297
    if ($query->param('userPassword') =~ $query->param('userPassword2')) {
298
299 2972 jones
        my $o = $query->param('o');
300
        $ldapurl = $config_ldapurl->{$o};
301 2341 sgarg
        $searchBase = $config_ldapsearchbase->{$o};
302 2972 jones
        $root = $config_user->{$o};
303
        $rootpw = $config_password->{$o};
304 2341 sgarg
305
        my $dn = "uid=" . $query->param('uid') . "," . $config_dn->{$o};;
306
        if ($query->param('o') =~ "LTER") {
307
            $template->process( "@defaultHeader@");
308
            $template->process( "@registerLter@");
309
            $template->process( "@defaultFooter@");
310
        } else {
311
            my $errorMessage = changePassword(
312
                    $dn, $query->param('userPassword'),
313
                    $dn, $query->param('oldpass'), $query->param('o'));
314 2972 jones
            if ($errorMessage) {
315 2341 sgarg
                my $templateVars = { stage => "changepass",
316
                                     cfg => $cfg,
317
                                     allParams => $allParams,
318
                                     errorMessage => $errorMessage };
319 2972 jones
                $$templateVars{'orgList'} = \@orglist;
320 2341 sgarg
                $template->process( "@defaultHeader@", $templateVars);
321
                $template->process( "@defaultChangePass@", $templateVars);
322
                $template->process( "@defaultFooter@", $templateVars);
323
                exit(0);
324
            } else {
325 2972 jones
                my $templateVars = { stage => "changepass",
326
                                     cfg => $cfg,
327
                                     allParams => $allParams };
328
                $$templateVars{'orgList'} = \@orglist;
329
                $template->process( "@defaultHeader@", $templateVars);
330
                $template->process( "@changePassSuccess@", $templateVars);
331
                $template->process( "@defaultFooter@", $templateVars);
332 2341 sgarg
                exit(0);
333
            }
334
        }
335
    } else {
336
        my $errorMessage = "The passwords do not match. Try again.";
337
        my $templateVars = { stage => "changepass",
338
                             cfg => $cfg,
339
                             allParams => $allParams,
340
                             errorMessage => $errorMessage };
341 2972 jones
        $$templateVars{'orgList'} = \@orglist;
342 2341 sgarg
        $template->process( "@defaultHeader@", $templateVars);
343
        $template->process( "@defaultChangePass@", $templateVars);
344
        $template->process( "@defaultFooter@", $templateVars);
345
        exit(0);
346
    }
347
}
348
349
#
350 2414 sgarg
# change a user's password upon request - no input params
351
# only display chagepass template without any error
352
#
353
sub handleInitialChangePassword {
354
    print "Content-type: text/html\n\n";
355
356
    my $allParams = { 'test' => "1", };
357
    my $errorMessage = "";
358
    my $templateVars = { stage => "changepass",
359
                         cfg => $cfg,
360
                         allParams => $allParams,
361
                         errorMessage => $errorMessage };
362
    $$templateVars{'orgList'} = \@orglist;
363
    $template->process( "@defaultHeader@", $templateVars);
364
    $template->process( "@defaultChangePass@", $templateVars);
365
    $template->process( "@defaultFooter@", $templateVars);
366
    exit(0);
367
}
368
369
#
370 2341 sgarg
# reset a user's password upon request
371
#
372
sub handleResetPassword {
373
374
    print "Content-type: text/html\n\n";
375
376
    my $allParams = { 'test' => "1", };
377
    if ($query->param('uid')) {
378
        $$allParams{'uid'} = $query->param('uid');
379
    }
380
    if ($query->param('o')) {
381
        $$allParams{'o'} = $query->param('o');
382 2972 jones
        my $o = $query->param('o');
383
384
        $ldapurl = $config_ldapurl->{$o};
385 2341 sgarg
        $searchBase = $config_ldapsearchbase->{$o};
386 2972 jones
        $root = $config_user->{$o};
387
        $rootpw = $config_password->{$o};
388 2341 sgarg
    }
389
390
    # Check that all required fields are provided and not null
391
    my @requiredParams = ( 'uid', 'o' );
392
    if (! paramsAreValid(@requiredParams)) {
393
        my $errorMessage = "Required information is missing. " .
394
            "Please fill in all required fields and submit the form.";
395
        my $templateVars = { stage => "resetpass",
396
                             cfg => $cfg,
397
                             allParams => $allParams,
398
                             errorMessage => $errorMessage };
399 2972 jones
        $$templateVars{'orgList'} = \@orglist;
400 2341 sgarg
        $template->process( "@defaultHeader@", $templateVars);
401
        $template->process( "@defaultResetPass@", $templateVars);
402
        $template->process( "@defaultFooter@", $templateVars);
403
        exit(0);
404
    }
405
406
    # We have all of the info we need, so try to change the password
407
    my $o = $query->param('o');
408
    my $dn = "uid=" . $query->param('uid') . "," . $config_dn->{$o};
409
    if ($query->param('o') =~ "LTER") {
410
        $template->process( "@defaultHeader@");
411
        $template->process( "@registerLter@");
412
        $template->process( "@defaultFooter@");
413
        exit(0);
414
    } else {
415
        my $errorMessage = "";
416
        my $recipient;
417
        my $userPass;
418
        my $entry = getLdapEntry($ldapurl, $searchBase,
419
                $query->param('uid'), $query->param('o'));
420
421
        if ($entry) {
422
            $recipient = $entry->get_value('mail');
423
            $userPass = getRandomPassword();
424
            $errorMessage = changePassword($dn, $userPass, $root, $rootpw, $query->param('o'));
425
        } else {
426
            $errorMessage = "User not found in database.  Please try again.";
427
        }
428
429
        if ($errorMessage) {
430
            my $templateVars = { stage => "resetpass",
431
                                 cfg => $cfg,
432
                                 allParams => $allParams,
433
                                 errorMessage => $errorMessage };
434 2972 jones
            $$templateVars{'orgList'} = \@orglist;
435 2341 sgarg
            $template->process( "@defaultHeader@", $templateVars);
436
            $template->process( "@defaultResetPass@", $templateVars);
437
            $template->process( "@defaultFooter@", $templateVars);
438
            exit(0);
439
        } else {
440
            my $errorMessage = sendPasswordNotification($query->param('uid'),
441 2972 jones
                    $query->param('o'), $userPass, $recipient, $cfg);
442 2341 sgarg
            my $templateVars = { stage => "resetpass",
443
                                 cfg => $cfg,
444
                                 allParams => $allParams,
445
                                 errorMessage => $errorMessage };
446 2972 jones
            $$templateVars{'orgList'} = \@orglist;
447 2341 sgarg
            $template->process( "@defaultHeader@", $templateVars);
448
            $template->process( "@resetPassSuccess@", $templateVars);
449
            $template->process( "@defaultFooter@", $templateVars);
450
            exit(0);
451
        }
452
    }
453
}
454
455
#
456 2414 sgarg
# reset a user's password upon request- no initial params
457
# only display resetpass template without any error
458
#
459
sub handleInitialResetPassword {
460
    print "Content-type: text/html\n\n";
461
    my $errorMessage = "";
462
    my $allParams = { 'test' => "1", };
463
    my $templateVars = { stage => "resetpass",
464
                         cfg => $cfg,
465
                         allParams => $allParams,
466
                         errorMessage => $errorMessage };
467
    $$templateVars{'orgList'} = \@orglist;
468
    $template->process( "@defaultHeader@", $templateVars);
469
    $template->process( "@defaultResetPass@", $templateVars);
470
    $template->process( "@defaultFooter@", $templateVars);
471
    exit(0);
472
}
473
474
#
475 2341 sgarg
# Construct a random string to use for a newly reset password
476
#
477
sub getRandomPassword {
478
    my $length = shift;
479
    if (!$length) {
480
        $length = 8;
481
    }
482
    my $newPass = "";
483
484
    my @chars = ( "A" .. "Z", "a" .. "z", 0 .. 9, qw(! @ $ ^) );
485
    $newPass = join("", @chars[ map { rand @chars } ( 1 .. $length ) ]);
486
    return $newPass;
487
}
488
489
#
490
# Change a password to a new value, binding as the provided user
491
#
492
sub changePassword {
493
    my $userDN = shift;
494
    my $userPass = shift;
495
    my $bindDN = shift;
496
    my $bindPass = shift;
497
    my $o = shift;
498
499
    my $ldapurl = $config_ldapurl->{$o};
500
    my $searchBase = $config_ldapsearchbase->{$o};
501
502
    my $errorMessage = 0;
503 3177 tao
    my $ldap;
504
    if ($ldapurl =~ $mainldapurl){
505
        #if main ldap server is down, a html file containing warning message will be returned
506
        $ldap = Net::LDAP->new($ldapurl, timeout => $TIMEOUT) or handleGeneralServerFailure($mainldapdownmessage);
507
    }
508
    else{
509
        #if a referral ldap server is down, we will ignore it silently
510
        $ldap = Net::LDAP->new($ldapurl, timeout => $TIMEOUT) or return;
511
    }
512 2972 jones
    #$ldap->start_tls( verify => 'require',
513
                      #cafile => '/usr/share/ssl/ldapcerts/cacert.pem');
514
    $ldap->start_tls( verify => 'none');
515 2341 sgarg
    my $bindresult = $ldap->bind( version => 3, dn => $bindDN,
516
                                  password => $bindPass );
517
    if ($bindresult->code) {
518
        $errorMessage = "Failed to log in. Are you sure your old " .
519
                        "password is correct? Try again...";
520
        return $errorMessage;
521
    }
522
523
    # Find the user here and change their entry
524
    my $newpass = createSeededPassHash($userPass);
525
    my $modifications = { userPassword => $newpass };
526
    my $result = $ldap->modify( $userDN, replace => { %$modifications });
527
528
    if ($result->code()) {
529
        my $errorMessage = "There was an error changing the password." .
530
                           "<br />\n" . $result->error;
531
    }
532
    $ldap->unbind;   # take down session
533
534
    return $errorMessage;
535
}
536
537
#
538
# generate a Seeded SHA1 hash of a plaintext password
539
#
540
sub createSeededPassHash {
541
    my $secret = shift;
542
543
    my $salt = "";
544
    for (my $i=0; $i < 4; $i++) {
545
        $salt .= int(rand(10));
546
    }
547
548
    my $ctx = Digest::SHA1->new;
549
    $ctx->add($secret);
550
    $ctx->add($salt);
551
    my $hashedPasswd = '{SSHA}' . encode_base64($ctx->digest . $salt ,'');
552
553
    return $hashedPasswd;
554
}
555
556
#
557
# Look up an ldap entry for a user
558
#
559
sub getLdapEntry {
560
    my $ldapurl = shift;
561
    my $base = shift;
562
    my $username = shift;
563
    my $org = shift;
564
565
    my $entry = "";
566
    my $mesg;
567 3177 tao
    my $ldap;
568
    print("ldap server ", $ldapurl, "\n");
569
    if ($ldapurl =~ $mainldapurl){
570
        #if main ldap server is down, a html file containing warning message will be returned
571
        $ldap = Net::LDAP->new($ldapurl, timeout => $TIMEOUT) or handleGeneralServerFailure($mainldapdownmessage);
572
    }
573
    else{
574
        #if a referral ldap server is down, we will ignore it silently
575
        $ldap = Net::LDAP->new($ldapurl, timeout => $TIMEOUT) or return;
576
    }
577 2972 jones
    $ldap->start_tls( verify => 'none');
578 2341 sgarg
    my $bindresult = $ldap->bind;
579
    if ($bindresult->code) {
580
        return $entry;
581
    }
582
583
    if($config_filter->{$org}){
584 2972 jones
        $mesg = $ldap->search ( base   => $base,
585
                filter => "(&(uid=$username)($config_filter->{$org}))");
586 2341 sgarg
    } else {
587 2972 jones
        $mesg = $ldap->search ( base   => $base, filter => "(uid=$username)");
588 2341 sgarg
    }
589 3177 tao
590 2341 sgarg
    if ($mesg->count > 0) {
591
        $entry = $mesg->pop_entry;
592
        $ldap->unbind;   # take down session
593
    } else {
594
        $ldap->unbind;   # take down session
595
        # Follow references by recursive call to self
596
        my @references = $mesg->references();
597
        for (my $i = 0; $i <= $#references; $i++) {
598
            my $uri = URI->new($references[$i]);
599
            my $host = $uri->host();
600
            my $path = $uri->path();
601
            $path =~ s/^\///;
602
            $entry = &getLdapEntry($host, $path, $username, $org);
603
            if ($entry) {
604
                return $entry;
605
            }
606
        }
607
    }
608
    return $entry;
609
}
610
611
#
612
# send an email message notifying the user of the pw change
613
#
614
sub sendPasswordNotification {
615
    my $username = shift;
616
    my $org = shift;
617
    my $newPass = shift;
618
    my $recipient = shift;
619 2972 jones
    my $cfg = shift;
620 2341 sgarg
621
    my $errorMessage = "";
622
    if ($recipient) {
623
        # Send the email message to them
624
        my $smtp = Net::SMTP->new($mailhost);
625
        $smtp->mail($sender);
626
        $smtp->to($recipient);
627
628
        my $message = <<"        ENDOFMESSAGE";
629
        To: $recipient
630
        From: $sender
631
        Subject: KNB Password Reset
632
633
        Somebody (hopefully you) requested that your KNB password be reset.
634
        This is generally done when somebody forgets their password.  Your
635
        password can be changed by visiting the following URL:
636
637 2972 jones
        @cgiurl@?stage=changepass&cfg=$cfg
638 2341 sgarg
639
            Username: $username
640
        Organization: $org
641
        New Password: $newPass
642
643
        Thanks,
644
            The KNB Development Team
645
646
        ENDOFMESSAGE
647
        $message =~ s/^[ \t\r\f]+//gm;
648
649
        $smtp->data($message);
650
        $smtp->quit;
651
    } else {
652
        $errorMessage = "Failed to send password because I " .
653
                        "couldn't find a valid email address.";
654
    }
655
    return $errorMessage;
656
}
657
658
#
659
# search the LDAP directory to see if a similar account already exists
660
#
661
sub findExistingAccounts {
662
    my $ldapurl = shift;
663
    my $base = shift;
664
    my $filter = shift;
665
    my $attref = shift;
666 3175 tao
    my $ldap;
667 2341 sgarg
668
    my $foundAccounts = 0;
669 3175 tao
    #print("the ldapurl in findExstingAccounts is ", $ldapurl, "\n");
670
    if ($ldapurl =~ $mainldapurl){
671
        #if main ldap server is down, a html file containing warning message will be returned
672 3177 tao
        $ldap = Net::LDAP->new($ldapurl, timeout => $TIMEOUT) or handleGeneralServerFailure($mainldapdownmessage);
673 3175 tao
    }
674
    else{
675
        #if a referral ldap server is down, we will ignore it silently
676
        $ldap = Net::LDAP->new($ldapurl, timeout => $TIMEOUT) or return;
677
    }
678 2972 jones
    $ldap->start_tls( verify => 'none');
679 2341 sgarg
    $ldap->bind( version => 3, anonymous => 1);
680
    my $mesg = $ldap->search (
681
        base   => $base,
682
        filter => $filter,
683
        attrs => @$attref,
684
    );
685
686
    if ($mesg->count() > 0) {
687
        $foundAccounts = "";
688
        my $entry;
689
        foreach $entry ($mesg->all_entries) {
690
            $foundAccounts .= "<p>\n<b><u>Account:</u> ";
691
            $foundAccounts .= $entry->dn();
692
            $foundAccounts .= "</b><br />\n";
693
            foreach my $attribute ($entry->attributes()) {
694
                $foundAccounts .= "$attribute: ";
695
                $foundAccounts .= $entry->get_value($attribute);
696
                $foundAccounts .= "<br />\n";
697
            }
698
            $foundAccounts .= "</p>\n";
699
        }
700
    }
701
    $ldap->unbind;   # take down session
702
703
    # Follow references
704
    my @references = $mesg->references();
705
    for (my $i = 0; $i <= $#references; $i++) {
706
        my $uri = URI->new($references[$i]);
707
        my $host = $uri->host();
708
        my $path = $uri->path();
709
        $path =~ s/^\///;
710
        my $refFound = &findExistingAccounts($host, $path, $filter, $attref);
711
        if ($refFound) {
712
            $foundAccounts .= $refFound;
713
        }
714
    }
715
716
    #print "<p>Checking referrals...</p>\n";
717
    #my @referrals = $mesg->referrals();
718
    #print "<p>Referrals count: ", scalar(@referrals), "</p>\n";
719
    #for (my $i = 0; $i <= $#referrals; $i++) {
720
        #print "<p>Referral: ", $referrals[$i], "</p>\n";
721
    #}
722
723
    return $foundAccounts;
724
}
725
726
#
727
# Validate that we have the proper set of input parameters
728
#
729
sub paramsAreValid {
730
    my @pnames = @_;
731
732
    my $allValid = 1;
733
    foreach my $parameter (@pnames) {
734
        if (!defined($query->param($parameter)) ||
735
            ! $query->param($parameter) ||
736
            $query->param($parameter) =~ /^\s+$/) {
737
            $allValid = 0;
738
        }
739
    }
740
741
    return $allValid;
742
}
743
744
#
745
# Bind to LDAP and create a new account using the information provided
746
# by the user
747
#
748
sub createAccount {
749
    my $allParams = shift;
750
751
    if ($query->param('o') =~ "LTER") {
752
        $template->process( "@defaultHeader@");
753
        $template->process( "@registerLter@");
754
        $template->process( "@defaultFooter@");
755
    } else {
756
757
        # Be sure the passwords match
758
        if ($query->param('userPassword') !~ $query->param('userPassword2')) {
759
            my $errorMessage = "The passwords do not match. Try again.";
760
            my $templateVars = { stage => "register",
761
                                 cfg => $cfg,
762
                                 allParams => $allParams,
763
                                 errorMessage => $errorMessage };
764 2972 jones
            $$templateVars{'orgList'} = \@orglist;
765 2341 sgarg
            $template->process( "@defaultHeader@", $templateVars);
766 2540 sgarg
            $template->process( "@registerFailed@", $templateVars);
767 2341 sgarg
            $template->process( "@register@", $templateVars);
768
            $template->process( "@defaultFooter@", $templateVars);
769
            exit(0);
770
        }
771
772 2972 jones
        my $o = $query->param('o');
773 2341 sgarg
774 2972 jones
        my $ldapurl = $config_ldapurl->{$o};
775
        my $root = $config_user->{$o};
776
        my $rootpw = $config_password->{$o};
777
        my $searchBase = $config_ldapsearchbase->{$o};
778
        my $dnBase = $config_dn->{$o};
779 2341 sgarg
780 3177 tao
781
        #if main ldap server is down, a html file containing warning message will be returned
782
        my $ldap = Net::LDAP->new($ldapurl, timeout => $TIMEOUT) or handleGeneralServerFailure("The ldap server " . $ldapurl . " is down!");
783
784
785 2972 jones
        $ldap->start_tls( verify => 'none');
786 2341 sgarg
        $ldap->bind( version => 3, dn => $root, password => $rootpw );
787
        #print "Inserting new entry...\n";
788
        my $dn = 'uid=' . $query->param('uid') . ',' . $dnBase;
789
790
        # Create a hashed version of the password
791
        my $shapass = createSeededPassHash($query->param('userPassword'));
792
793
        # Do the insertion
794
        my $additions = [
795
                'uid'   => $query->param('uid'),
796
                'o'   => $query->param('o'),
797
                'cn'   => join(" ", $query->param('givenName'),
798
                                    $query->param('sn')),
799
                'sn'   => $query->param('sn'),
800
                'givenName'   => $query->param('givenName'),
801
                'mail' => $query->param('mail'),
802
                'userPassword' => $shapass,
803
                'objectclass' => ['top', 'person', 'organizationalPerson',
804
                                'inetOrgPerson', 'uidObject' ]
805
            ];
806
        if (defined($query->param('telephoneNumber')) &&
807
            $query->param('telephoneNumber') &&
808
            ! $query->param('telephoneNumber') =~ /^\s+$/) {
809
            $$additions[$#$additions + 1] = 'telephoneNumber';
810
            $$additions[$#$additions + 1] = $query->param('telephoneNumber');
811
        }
812
        if (defined($query->param('title')) &&
813
            $query->param('title') &&
814
            ! $query->param('title') =~ /^\s+$/) {
815
            $$additions[$#$additions + 1] = 'title';
816
            $$additions[$#$additions + 1] = $query->param('title');
817
        }
818
        my $result = $ldap->add ( 'dn' => $dn, 'attr' => [ @$additions ]);
819
820
        if ($result->code()) {
821
            my $templateVars = { stage => "register",
822
                                 cfg => $cfg,
823
                                 allParams => $allParams,
824
                                 errorMessage => $result->error };
825 2972 jones
            $$templateVars{'orgList'} = \@orglist;
826 2414 sgarg
            $template->process( "@defaultHeader@", $templateVars);
827 2341 sgarg
            $template->process( "@registerFailed@", $templateVars);
828
            $templateVars    = { stage => "register",
829
                                 cfg => $cfg,
830
                                 allParams => $allParams };
831 2972 jones
            $$templateVars{'orgList'} = \@orglist;
832 2341 sgarg
            $template->process( "@register@", $templateVars);
833 2414 sgarg
            $template->process( "@defaultFooter@", $templateVars);
834 2341 sgarg
        } else {
835 2972 jones
            my $templateVars    = { cfg => $cfg };
836 2414 sgarg
            $template->process( "@defaultHeader@", $templateVars);
837
            $template->process( "@registerSuccess@", $templateVars);
838
            $template->process( "@defaultFooter@", $templateVars);
839 2341 sgarg
        }
840
841
        $ldap->unbind;   # take down session
842
    }
843
}
844
845
sub handleResponseMessage {
846
847
  print "Content-type: text/html\n\n";
848
  my $errorMessage = "You provided invalid input to the script. " .
849
                     "Try again please.";
850
  my $templateVars = { stage => "@defaultStage@",
851
                       cfg => $cfg,
852
                       errorMessage => $errorMessage };
853
  $$templateVars{'orgList'} = \@orglist;
854
  $template->process( "@defaultHeader@", $templateVars);
855
  $template->process( "@defaultFooter@", $templateVars);
856
  exit(0);
857
}
858
859
#
860
# perform a simple search against the LDAP database using
861
# a small subset of attributes of each dn and return it
862
# as a table to the calling browser.
863
#
864
sub handleSimpleSearch {
865
866
    my $o = $query->param('o');
867
868
    my $ldapurl = $config_ldapurl->{$o};
869
    my $searchBase = $config_ldapsearchbase->{$o};
870
871
    print "Content-type: text/html\n\n";
872
873
    my $allParams = {
874
                      'cn' => $query->param('cn'),
875
                      'sn' => $query->param('sn'),
876
                      'gn' => $query->param('gn'),
877
                      'o'  => $query->param('o'),
878
                      'facsimiletelephonenumber'
879
                      => $query->param('facsimiletelephonenumber'),
880
                      'mail' => $query->param('cmail'),
881
                      'telephonenumber' => $query->param('telephonenumber'),
882
                      'title' => $query->param('title'),
883
                      'uid' => $query->param('uid'),
884
                      'ou' => $query->param('ou'),
885
                    };
886
887
    # Search LDAP for matching entries that already exist
888
    my $filter = "(" .
889
                 $query->param('searchField') . "=" .
890
                 "*" .
891
                 $query->param('searchValue') .
892
                 "*" .
893
                 ")";
894
895
    my @attrs = [ 'sn',
896
                  'gn',
897
                  'cn',
898
                  'o',
899
                  'facsimiletelephonenumber',
900
                  'mail',
901
                  'telephoneNumber',
902
                  'title',
903
                  'uid',
904
                  'labeledURI',
905
                  'ou' ];
906
907
    my $found = searchDirectory($ldapurl, $searchBase, $filter, \@attrs);
908
909
    # Send back the search results
910
    if ($found) {
911
      my $templateVars = { stage => "searchResults",
912
                           cfg => $cfg,
913
                           allParams => $allParams,
914
                           foundAccounts => $found };
915
      $$templateVars{'orgList'} = \@orglist;
916
      $template->process( "@defaultHeader@", $templateVars);
917
      $template->process( "@searchResults@", $templateVars);
918
      $template->process( "@defaultFooter@", $templateVars);
919
920
    } else {
921
      $found = "No entries matched your criteria.  Please try again\n";
922
923
      my $templateVars = { stage => "searchResults",
924
                           cfg => $cfg,
925
                           allParams => $allParams,
926
                           foundAccounts => $found };
927
      $$templateVars{'orgList'} = \@orglist;
928
      $template->process( "@defaultHeader@", $templateVars);
929
      $template->process( "@searchResults@", $templateVars);
930
      $template->process( "@defaultFooter@", $templateVars);
931
932
    }
933
934
    exit();
935
}
936
937
#
938
# search the LDAP directory to see if a similar account already exists
939
#
940
sub searchDirectory {
941
    my $ldapurl = shift;
942
    my $base = shift;
943
    my $filter = shift;
944
    my $attref = shift;
945
946
    my $foundAccounts = 0;
947
948 3177 tao
949
950
    #if ldap server is down, a html file containing warning message will be returned
951
    my $ldap = Net::LDAP->new($ldapurl, timeout => $TIMEOUT) or handleGeneralServerFailure("The ldap server " . $ldapurl . " is down!");
952
953 2972 jones
    $ldap->start_tls( verify => 'none');
954 2341 sgarg
    $ldap->bind( version => 3, anonymous => 1);
955
    my $mesg = $ldap->search (
956
        base   => $base,
957
        filter => $filter,
958
        attrs => @$attref,
959
    );
960
961
    if ($mesg->count() > 0) {
962
        $foundAccounts = "";
963
        my $entry;
964
        foreach $entry ($mesg->sorted(['sn'])) {
965
          $foundAccounts .= "<tr>\n<td class=\"main\">\n";
966
          $foundAccounts .= "<a href=\"" unless
967
                    (!$entry->get_value('labeledURI'));
968
          $foundAccounts .= $entry->get_value('labeledURI') unless
969
                    (!$entry->get_value('labeledURI'));
970
          $foundAccounts .= "\">\n" unless
971
                    (!$entry->get_value('labeledURI'));
972
          $foundAccounts .= $entry->get_value('givenName');
973
          $foundAccounts .= "</a>\n" unless
974
                    (!$entry->get_value('labeledURI'));
975
          $foundAccounts .= "\n</td>\n<td class=\"main\">\n";
976
          $foundAccounts .= "<a href=\"" unless
977
                    (!$entry->get_value('labeledURI'));
978
          $foundAccounts .= $entry->get_value('labeledURI') unless
979
                    (!$entry->get_value('labeledURI'));
980
          $foundAccounts .= "\">\n" unless
981
                    (!$entry->get_value('labeledURI'));
982
          $foundAccounts .= $entry->get_value('sn');
983
          $foundAccounts .= "</a>\n";
984
          $foundAccounts .= "\n</td>\n<td class=\"main\">\n";
985
          $foundAccounts .= $entry->get_value('mail');
986
          $foundAccounts .= "\n</td>\n<td class=\"main\">\n";
987
          $foundAccounts .= $entry->get_value('telephonenumber');
988
          $foundAccounts .= "\n</td>\n<td class=\"main\">\n";
989
          $foundAccounts .= $entry->get_value('title');
990
          $foundAccounts .= "\n</td>\n<td class=\"main\">\n";
991
          $foundAccounts .= $entry->get_value('ou');
992
          $foundAccounts .= "\n</td>\n";
993
          $foundAccounts .= "</tr>\n";
994
        }
995
    }
996
    $ldap->unbind;   # take down session
997
    return $foundAccounts;
998
}
999
1000
sub debug {
1001
    my $msg = shift;
1002
1003
    if ($debug) {
1004
        print STDERR "$msg\n";
1005
    }
1006
}
1007 3175 tao
1008 3177 tao
sub handleGeneralServerFailure {
1009
    my $errorMessage = shift;
1010 3175 tao
    my $templateVars = { cfg => $cfg,
1011
                         errorMessage => $errorMessage };
1012
    $template->process( "@defaultHeader@", $templateVars);
1013
    $template->process( "@ldapMainServerFailure@", $templateVars);
1014
    $template->process( "@defaultFooter@", $templateVars);
1015
    exit(0);
1016
   }
1017