tbacct.in 19.3 KB
Newer Older
1
2
3
4
#!/usr/bin/perl -wT

#
# EMULAB-COPYRIGHT
5
# Copyright (c) 2000-2006 University of Utah and the Flux Group.
6
7
8
9
# All rights reserved.
#
use English;
use Getopt::Std;
10

11
12
13
14
15
16
17
18
19
#
# Deal with user accounts. This script does not deal with group stuff.
# Just add/del/mod/passwd/freeze/thaw/ stuff. We do give users an
# initial group of course, which will be guest if not in any groups.
#
# This script is setuid. We farm stuff out to subscripts though, and need
# to be wary of what the UID/EUID is when those scripts are invoked. The
# subscripts are not generally setuid, but of course the web interface
# allows users to do things on behalf of other users, and we want to track
20
# that in the audit log.
21
22
#
# This script always does the right thing ...
23
#
24
25
sub usage()
{
26
    print("Usage: tbacct [-f] [-b] ".
27
	  "<add|del|mod|passwd|wpasswd|freeze|thaw> <user>\n");
28
29
    exit(-1);
}
30
31
32
my $optlist = "fb";
my $force   = 0;
my $batch   = 0;
33
34
35
36
37
38
39
40
41
42

#
# Configure variables
#
my $TB		= "@prefix@";
my $TBOPS	= "@TBOPSEMAIL@";
my $TBLOGS	= "@TBLOGSEMAIL@";
my $CONTROL	= "@USERNODE@";
my $BOSSNODE	= "@BOSSNODE@";
my $WITHSFS	= @SFSSUPPORT@;
43
my $WIKISUPPORT = @WIKISUPPORT@;
44
my $BUGDBSUPPORT= @BUGDBSUPPORT@;
45
my $OPSDBSUPPORT= @OPSDBSUPPORT@;
46
my $CHATSUPPORT = @CHATSUPPORT@;
47
my $MAILMANSUPPORT= @MAILMANSUPPORT@;
48
my $PROTOUSER   = 'elabman';
49

50
51
52
my $SAMBANODE	= "fs";  # DNS makes this do the right thing in E-in-E.
my $SMBPASSWD	= "/usr/local/bin/smbpasswd";

53
54
55
56
57
58
59
my $USERPATH	= "$TB/bin";
my $ADDKEY	= "$TB/sbin/addpubkey";
my $USERADD	= "/usr/sbin/pw useradd";
my $USERDEL	= "/usr/sbin/pw userdel";
my $USERMOD	= "/usr/sbin/pw usermod";
my $CHPASS	= "/usr/bin/chpass";
my $SFSKEYGEN	= "/usr/local/bin/sfskey gen";
60
my $GENELISTS	= "$TB/sbin/genelists";
61
my $MKUSERCERT	= "$TB/sbin/mkusercert";
62
my $SFSUPDATE	= "$TB/sbin/sfskey_update";
63
my $PBAG	= "$TB/sbin/paperbag";
64
my $EXPORTSSETUP= "$TB/sbin/exports_setup";
65
66
my $ADDWIKIUSER = "$TB/sbin/addwikiuser";
my $DELWIKIUSER = "$TB/sbin/delwikiuser";
67
68
my $ADDBUGDBUSER= "$TB/sbin/addbugdbuser";
my $DELBUGDBUSER= "$TB/sbin/delbugdbuser";
69
70
my $ADDCHATUSER = "$TB/sbin/addjabberuser";
my $DELCHATUSER = "$TB/sbin/deljabberuser";
71
72
73
my $MMMODIFYUSER= "$TB/sbin/mmmodifymember";
my $ADDMMUSER   = "$TB/sbin/addmmuser";
my $DELMMUSER   = "$TB/sbin/delmmuser";
74
my $OPSDBCONTROL= "$TB/sbin/opsdb_control";
75
my $NOLOGIN	= "/sbin/nologin";
76
my $SSH		= "$TB/bin/sshtb";
77
78
79
my $SAVEUID	= $UID;
my $NOSUCHUSER  = 67;
my $USEREXISTS  = 65;
80
81
82
# Nasty. Should do this with /etc/pw.conf shellpath.
my %shellpaths  = ("csh"  => "/bin/csh", "sh" => "/bin/sh",
		   "tcsh" => "/bin/tcsh", "bash" => "/usr/local/bin/bash");
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99

my $errors      = 0;
my $sfsupdate   = 0;
my @row;
my $query_result;

#
# We don't want to run this script unless its the real version.
#
if ($EUID != 0) {
    die("*** $0:\n".
	"    Must be setuid! Maybe its a development version?\n");
}

#
# This script is setuid, so please do not run it as root. Hard to track
# what has happened.
100
#
101
102
103
104
105
106
107
if ($UID == 0) {
    die("*** $0:\n".
	"    Please do not run this as root! Its already setuid!\n");
}

#
# Untaint the path
108
#
109
110
111
112
113
114
115
116
117
$ENV{'PATH'} = "$TB/bin:$TB/sbin:/bin:/usr/bin:/usr/bin:/usr/sbin";
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};

#
# Turn off line buffering on output
#
$| = 1;

#
118
# Load the Testbed support stuff.
119
120
121
122
123
124
#
use lib "@prefix@/lib";
use libaudit;
use libdb;
use libtestbed;

125
126
127
128
129
130
131
132
133
134
135
136
137
138
#
# Function prototypes
#
sub AddUser();
sub DelUser();
sub UpdatePassword();
sub UpdateWindowsPassword();
sub UpdateUser(;$);
sub FreezeUser();
sub ThawUser();
sub CheckDotFiles();
sub GenerateSFSKey();
sub fatal($);

139
140
my $HOMEDIR	= USERROOT();

141
142
143
144
145
146
147
148
149
150
151
#
# Parse command arguments. Once we return from getopts, all that should be
# left are the required arguments.
#
%options = ();
if (! getopts($optlist, \%options)) {
    usage();
}
if (defined($options{"f"})) {
    $force = 1;
}
152
153
154
if (defined($options{"b"})) {
    $batch = 1;
}
155
156
157
if (@ARGV != 2) {
    usage();
}
158
159
my $cmd  = $ARGV[0];
my $user = $ARGV[1];
160
161
162
163

#
# Untaint the arguments.
#
164
if ($user =~ /^([-\w]+)$/i) {
165
166
167
168
169
    $user = $1;
}
else {
    die("Tainted argument: $user\n");
}
170
if ($cmd =~ /^(add|del|mod|freeze|passwd|wpasswd|thaw)$/) {
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
    $cmd = $1;
}
else {
    usage();
}

# Only admins can use force mode.
if ($force && ! TBAdmin($UID)) {
    fatal("Only admins can use force mode!");
}

#
# This script is always audited. Mail is sent automatically upon exit.
#
if (AuditStart(0)) {
    #
    # Parent exits normally
    #
    exit(0);
}

#
# Get the user info (the user being operated on).
#
$query_result =
    DBQueryFatal("select u.usr_pswd,u.unix_uid,u.usr_name, ".
197
198
		 " u.usr_email,u.status,u.webonly,u.usr_shell,admin, ".
		 " u.usr_w_pswd,u.wikionly ".
199
		 "from users as u ".
200
201
202
203
204
205
206
		 "where u.uid='$user'");

if ($query_result->numrows == 0) {
    fatal("$user is not in the DB. This is bad.\n");
}
@row            = $query_result->fetchrow_array();
my $pswd        = $row[0];
207
my $user_number = $row[1];
208
209
210
211
my $fullname    = $row[2];
my $user_email  = $row[3];
my $status      = $row[4];
my $webonly     = $row[5];
212
my $usr_shell   = $row[6];
213
my $usr_admin   = $row[7];
214
my $wpswd       = $row[8];
215
my $wikionly    = $row[9];
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237

#
# Get the users earliest project membership to use as the default group
# for the case that the account is being (re)created. We convert that to
# the unix info.
#
my $default_groupname;
my $default_groupgid;

$query_result =
    DBQueryFatal("select m.pid from group_membership as m ".
		 "where m.uid='$user' and m.pid=m.gid and m.trust!='none' ".
		 "order by date_approved asc limit 1");

if (my ($defpid) = $query_result->fetchrow_array) {
    if (! TBGroupUnixInfo($defpid, $defpid,
			  \$default_groupgid, \$default_groupname)) {
	fatal("No info for default project $defpid!");
    }
}
else {
    print "No group membership for $user; using the guest group!\n";
238

239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
    ($default_groupname,undef,$default_groupgid,undef) = getgrnam("guest");
}

#
# Now dispatch operation.
#
SWITCH: for ($cmd) {
    /^add$/ && do {
	AddUser();
	last SWITCH;
    };
    /^del$/ && do {
	DelUser();
	last SWITCH;
    };
    /^passwd$/ && do {
	UpdatePassword();
	last SWITCH;
    };
258
259
260
261
    /^wpasswd$/ && do {
	UpdateWindowsPassword();
	last SWITCH;
    };
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
    /^mod$/ && do {
	UpdateUser();
	last SWITCH;
    };
    /^freeze$/ && do {
	FreezeUser();
	last SWITCH;
    };
    /^thaw$/ && do {
	ThawUser();
	last SWITCH;
    };
}

# Always do this!
CheckDotFiles();

#
# Invoke as real user for auditing (and cause of perl).
#
282
if ($WITHSFS && $sfsupdate) {
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
    $EUID = $UID;
    system($SFSUPDATE) == 0
	or fatal("$SFSUPDATE failed!");
    $EUID = 0;
}

#
# Now schedule account updates on all the nodes that this person has
# an account on.
#
TBNodeUpdateAccountsByUID($user);

exit(0);

#
# Add new user.
#
sub AddUser()
{
    #
    # Check status. Only active users get accounts built.
    #
305
    if ($webonly || $wikionly || $status ne USERSTATUS_ACTIVE) {
306
307
308
	if ($webonly) {
	    return 0;
	}
309
310
311
312
313
314
315
	if ($wikionly) {
	    $EUID = $UID;

	    # And to the wiki if enabled.
	    system("$ADDWIKIUSER $user")
		if ($WIKISUPPORT && !$batch);
	    
316
317
318
319
	    # And to the bugdb if enabled.
	    system("$ADDBUGDBUSER $user")
		if ($BUGDBSUPPORT && !$batch);
	    
320
321
322
	    $EUID = 0;
	    return 0;
	}
323
324
	fatal("$user is not active! Cannot build an account!");
    }
325

326
327
328
    $UID = 0;
    if (system("egrep -q -s '^${user}:' /etc/passwd")) {
	print "Adding user $user ($user_number) to local node.\n";
329

330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
	if (system("$USERADD $user -u $user_number -c \"$fullname\" ".
		   "-k /usr/share/skel -h - -m -d $HOMEDIR/$user ".
		   "-g $default_groupname -s $PBAG")) {
	    fatal("Could not add user $user to local node.");
	}
    }

    #
    # Quote special chars for ssh and the shell on the other side
    #
    $fullname =~ s/\"/\'/g;
    $fullname =~ s/([^\\])([\'\"\(\)])/$1\\$2/g;

    if ($CONTROL ne $BOSSNODE) {
	print "Adding user $user ($user_number) to $CONTROL.\n";

	if (system("$SSH -host $CONTROL ".
347
		   "'$USERADD $user -u $user_number -c \"$fullname\" ".
348
349
		   "-k /usr/share/skel -h - -m -d $HOMEDIR/$user ".
		   "-g $default_groupname -s /bin/tcsh'")) {
350
	    if (($? >> 8) != $USEREXISTS) {
351
352
353
354
355
356
357
358
359
360
361
362
363
		fatal("Could not add user $user ($user_number) to $CONTROL.");
	    }
	}
    }
    $UID = $SAVEUID;

    #
    # Do the ssh thing. Invoke as real user for auditing.
    #
    $EUID = $UID;
    if (system("$ADDKEY -i $user")) {
	fatal("Could not generate initial ssh key for $user");
    }
364
    # Add to elists.
365
366
    system("$GENELISTS -u $user")
	if (! $batch);
367

368
369
    # And to the wiki if enabled.
    system("$ADDWIKIUSER $user")
370
	if ($WIKISUPPORT && $user ne $PROTOUSER);
371

372
    # And to the bugdb if enabled.
373
    system("$ADDBUGDBUSER $user")
374
	if ($BUGDBSUPPORT && $user ne $PROTOUSER);
375

376
377
378
379
    # And to the OPS db if enabled.
    system("$OPSDBCONTROL adduser $user")
	if ($OPSDBSUPPORT && $user ne $PROTOUSER);

380
381
    # And to the chat server if enabled.
    system("$ADDCHATUSER $user")
382
	if ($CHATSUPPORT && $user ne $PROTOUSER);
383

384
385
386
387
    # And the mailman lists if enabled.
    system("$ADDMMUSER $user")
	if ($MAILMANSUPPORT);
    
388
389
390
    # Generate the SSL cert for the user.
    system("$MKUSERCERT $user");

391
392
393
394
395
396
397
398
399
    #
    # Must update the exports file or else nodes will complain.  There
    # is a bit of race in here since this update happens after the
    # user is marked "active", and in that time a node could suck over
    # the account info, but not be able to mount the directory. Short
    # window though. Do not worry about the exit value. Note that this
    # could hang for a while if another update is in progress. Hmm, I
    # do not like this.
    #
400
401
402
403
    if (! $batch) {
	print "Updating exports file.\n";
	system("$EXPORTSSETUP");
    }
404
405
406
407
408
409
    $EUID = 0;

    # SFS key.
    if ($CONTROL ne $BOSSNODE) {
	GenerateSFSKey();
    }
410

411
412
413
414
415
416
417
418
419
    return UpdatePassword();
}

#
# Delete a user.
#
sub DelUser()
{
    #
420
    # Only admin people can do this.
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
    #
    if (! TBAdmin($UID)) {
	fatal("You do not have permission to delete user $user.");
    }
    #
    # Check status. Active indicates something is wrong.
    #
    if (!$force && $status eq USERSTATUS_ACTIVE) {
	fatal("$user is still active! Cannot delete the account!");
    }

    print "Deleting user $user ($user_number) from local node.\n";

    $UID = 0;

    if (system("$USERDEL $user")) {
	if (($? >> 8) != $NOSUCHUSER) {
	    fatal("Could not remove user $user from local node.");
	}
    }

    if ($CONTROL ne $BOSSNODE) {
	print "Removing user $user from $CONTROL\n";
444

445
446
447
448
449
450
451
	if (system("$SSH -host $CONTROL '$USERDEL $user'")) {
	    if (($? >> 8) != $NOSUCHUSER) {
		fatal("Could not remove user $user from $CONTROL.");
	    }
	}
    }
    $UID = $SAVEUID;
452
453

    $EUID = $UID;
454
455
456
457
458
459
460
    #
    # Must update the exports file or else nodes will complain.  Note
    # that this could hang for a while if another update is in progress. 
    #
    print "Updating exports file.\n";
    system("$EXPORTSSETUP");

461
    # Remove from elists.
462
    system("$GENELISTS -u $user");
463
464
465
466
467

    # And to the wiki if enabled.
    system("$DELWIKIUSER $user")
	if ($WIKISUPPORT);
    
468
469
470
471
    # And the chat server if enabled.
    system("$DELCHATUSER $user")
	if ($CHATSUPPORT);
    
472
473
474
475
    # And the mailman lists if enabled.
    system("$DELMMUSER $user")
	if ($MAILMANSUPPORT);
    
476
    $EUID = 0;
477

478
479
480
481
482
483
    $sfsupdate = 1;
    return 0;
}

#
# Change a password for the user on the control node. The local password
484
# is not touched!
485
486
487
488
#
sub UpdatePassword()
{
    # shell escape.
489
    $pswd     =~ s/\$/\\\$/g;
490
    $pswd     =~ s/\*/\\\*/g;
491

492
493
494
495
496
497
498
499
    #
    # Check status. Ignore if user is not active.
    #
    if ($status ne USERSTATUS_ACTIVE) {
	print("$user is not active! Not updating the password!\n");
	return 0;
    }

500
501
502
503
504
505
506
507
    if (! $wikionly) {
	$UID = 0;
	if ($CONTROL ne $BOSSNODE) {
	    print "Updating user $user password on $CONTROL.\n";
	    
	    if (system("$SSH -host $CONTROL $CHPASS -p '$pswd' $user")) {
		fatal("Could not change password for user $user on $CONTROL!");
	    }
508
	}
509
	$UID = $SAVEUID;
510
    }
511
    
512
513
514
    $EUID = $UID;
    # And to the wiki if enabled.
    system("$ADDWIKIUSER -u $user")
515
	if ($WIKISUPPORT && $user ne $PROTOUSER);
516
517
518
519

    # And to the bugdb if enabled.
    system("$ADDBUGDBUSER -m $user")
	if ($BUGDBSUPPORT && $user ne $PROTOUSER);
520
521
522
523
524

    # And to the OPS db if enabled.
    system("$OPSDBCONTROL adduser $user")
	if ($OPSDBSUPPORT && $user ne $PROTOUSER);
  
525
526
    $EUID = 0;
    
527
528
529
    return 0;
}

530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
#
# Change a Windows password for the user on the Samba server node.
# The local password is not touched!
#
sub UpdateWindowsPassword()
{
    # shell escape.
    $wpswd     =~ s/\$/\\\$/g;

    #
    # Check status. Ignore if user is not active.
    #
    if ($status ne USERSTATUS_ACTIVE) {
	print("$user is not active! Not updating the password!\n");
	return 0;
    }

    $UID = 0;
    print "Updating user $user Samba password on $SAMBANODE.\n";
    # -s = silent, -a = add user if necessary.
    open( SPCMD, "| $SSH -host $SAMBANODE $SMBPASSWD -s -a $user")
        || fatal("Opening $SMBPASSWD pipe, user $user on $SAMBANODE: $! $?");
    local $SIG{PIPE} = sub { die "smbpasswd spooler pipe broke" };
    print SPCMD "$wpswd\n$wpswd\n";
    close SPCMD 
	|| fatal("Closing $SMBPASSWD pipe, user $user on $SAMBANODE: $! $?");

    $UID = $SAVEUID;
    return 0;
}

561
562
563
564
565
566
567
568
569
570
#
# Update user info. Allow for optional shell change for freeze/thaw.
#
sub UpdateUser(;$)
{
    my ($freezeopt) = @_;
    my $locshellarg = "";
    my $remshellarg = "";

    #
571
    # Sanity check.
572
573
574
575
576
    #
    if ($webonly) {
	return 0;
    }
    if (!defined($freezeopt) && ($status ne USERSTATUS_ACTIVE)) {
577
578
579
580
581
582
583
584
	#
	# If doing a modification to a frozen user, then just ignore
	# it; the modification will happen later when the user is thawed.
	#
	if ($status eq USERSTATUS_FROZEN) {
	    print "Ignoring update of frozen user $user\n";
	    return 0;
	}
585
	fatal("$user is not active! Cannot update the account!");
586
587
588
    }

    # Shell is different on local vs control node.
589
    if ((defined($freezeopt) && $freezeopt) || $user eq $PROTOUSER) {
590
591
592
593
	$locshellarg = "-s $NOLOGIN";
	$remshellarg = "-s $NOLOGIN";
    }
    else {
594
595
596
	# Leave local shell alone if an admin.
	$locshellarg = "-s $PBAG"
	    if (!$usr_admin);
597
598
599
600

	if (!defined($usr_shell) ||
	    !exists($shellpaths{$usr_shell})) {
	    $remshellarg = "-s " . $shellpaths{"tcsh"};
601
	}
602
603
	else  {
	    $remshellarg = "-s " . $shellpaths{$usr_shell};
604
605
606
	}
    }
    print "Updating user $user ($user_number) on local node.\n";
607

608
609
610
611
    $UID = 0;
    if (system("$USERMOD $user $locshellarg -c \"$fullname\" ")) {
	fatal("Could not modify user $user on local node.");
    }
612

613
614
615
616
617
618
619
620
    #
    # Quote special chars for ssh and the shell on the other side
    #
    $fullname =~ s/\"/\'/g;
    $fullname =~ s/([^\\])([\'\"\(\)])/$1\\$2/g;

    if ($CONTROL ne $BOSSNODE) {
	print "Updating user $user ($user_number) on $CONTROL\n";
621

622
	if (system("$SSH -host $CONTROL ".
623
		   "'$USERMOD $user $remshellarg -c \"$fullname\"'")) {
624
625
626
627
	    fatal("Could not modify user $user record on $CONTROL.");
	}
    }
    $UID = $SAVEUID;
628
629

    $EUID = $UID;
630
631
632
633
    # Update elists in case email changed.
    system("$MMMODIFYUSER $user")
	if ($MAILMANSUPPORT && !$batch);
    
634
    # Update elists in case email changed.
635
    system("$GENELISTS -m -u $user");
636
    $EUID = 0;
637

638
639
640
641
642
643
644
645
646
    return 0;
}

#
# Freeze a user.
#
sub FreezeUser()
{
    #
647
    # Only admin people can do this.
648
649
650
651
652
653
654
655
656
657
658
    #
    if (! TBAdmin($UID)) {
	fatal("You do not have permission to freeze user $user.");
    }
    #
    # Check status.
    #
    if ($status ne USERSTATUS_FROZEN) {
	fatal("$user is still active! Cannot freeze the account!");
    }
    $sfsupdate = 1;
659

660
661
662
663
664
665
666
667
668
    return UpdateUser(1);
}

#
# Thaw a user.
#
sub ThawUser()
{
    #
669
    # Only admin people can do this.
670
671
672
673
674
675
676
677
678
679
680
    #
    if (! TBAdmin($UID)) {
	fatal("You do not have permission to thaw user $user.");
    }
    #
    # Check status.
    #
    if ($status ne USERSTATUS_ACTIVE) {
	fatal("$user is not active! Cannot thaw the account!");
    }
    $sfsupdate = 1;
681

682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
    return UpdateUser(0);
}

#
# Check dot files. We do this over and over ...
#
sub CheckDotFiles()
{
    my $forward = "$HOMEDIR/$user/.forward";
    my $cshrc   = "$HOMEDIR/$user/.cshrc";
    my $profile = "$HOMEDIR/$user/.profile";

    if (! -d "$HOMEDIR/$user") {
	return 0;
    }

    # As the user.
    $UID = $user_number;

    #
    # Set up a .forward file so that any email to them gets forwarded off.
    #
    if (! -e $forward) {
	print "Setting up .forward file for $user.\n";

	if (system("echo \"$user_email\" > $forward")) {
		fatal("Could not create $forward!");
	}
	chmod(0644, "$HOMEDIR/$user/.forward") or
		fatal("Could not chmod $forward: $!");
712
713
714
715
716
717
718
719
720
	$fileowner= (stat($forward))[4];
	$dochown=0;
	if ($fileowner==0) {
	    chown($user_number,$default_groupgid,"$HOMEDIR/$user/.forward") or
	      do {
		  warn("Could not chown $forward: $!");
		  $dochown=1;
	      };
	}
721
722
723
724
    }

    #
    # Add testbed path to .cshrc and .profile.
725
    # Plus a conditional Cygwin section for the Windows system path.
726
    #
727
728
729
730
    my $cpathstr = "set path = ($USERPATH \$path)\n" .
    'if ( `uname -s` =~ CYGWIN* ) then' . "\n" .
    '    setenv PATH "${PATH}:/cygdrive/c/WINDOWS/system32:/cygdrive/c/WINDOWS"' . "\n" .
    'endif';
731
732
733
734
    if (-e $cshrc && system("egrep -q -s '$USERPATH' $cshrc")) {
	system("echo '$cpathstr' >> $cshrc");
    }

735
736
737
738
    my $spathstr = "PATH=$USERPATH:\$PATH\n" .
    'if [[ `uname -s` == CYGWIN* ]]; then' . "\n" .
    '    PATH="$PATH":/cygdrive/c/WINDOWS/system32:/cygdrive/c/WINDOWS' . "\n" .
    'fi';
739
740
741
742
743
    if (-e $profile && system("egrep -q -s '$USERPATH' $profile")) {
	system("echo '$spathstr' >> $profile");
    }
    $UID = $SAVEUID;

744
745
746
747
748
    if (defined($dochown) && $dochown!=0) {
	chown($user_number,$default_groupgid,"$HOMEDIR/$user/.forward") or
	  warn("Could not chown $forward: $!");
    }

749
750
751
752
753
754
755
756
757
    return 0;
}

#
# Do SFS stuff. Might move this out to its own script at some point.
#
sub GenerateSFSKey()
{
    my $sfsdir  = "$HOMEDIR/$user/.sfs";
758

759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
    #
    # Set up the sfs key, but only if not done so already.
    # This has to be done from root because the sfs_users file needs
    # to be updated (and "sfskey register" won't work because it
    # prompts for the user's UNIX password if not run from root.)
    #
    if ($WITHSFS && ! -e "$sfsdir/identity") {
	if (! -e "$sfsdir" ) {
	    print "Setting up sfs configuration for $user.\n";

	    mkdir("$sfsdir", 0700) or
		fatal("Could not mkdir $sfsdir: $!");
	    chown($user_number, $default_groupgid, "$sfsdir") or
		fatal("Could not chown $sfsdir: $!");
	}

	print "Generating sfs key\n";
	$UID = 0;
777
778
 	if (system("$SSH -host $CONTROL '$SFSKEYGEN -KPn ".
 		   "$user\@ops.emulab.net $sfsdir/identity'")) {
779
	    fatal("Failure in sfskey gen!");
780
781
782
783
784
785
	}
	# Version 7 stuff for later.
	#if (system("$SSH -host $CONTROL '$SFSKEYGEN -KP ".
	#	    "-l $user\@ops.emulab.net $sfsdir/identity'")) {
	#    fatal("Failure in sfskey gen!");
	#}
786
	$UID = $SAVEUID;
787

788
789
790
791
	chown($user_number, $default_groupgid, "$sfsdir/identity") or
	    fatal("Could not chown $sfsdir/identity: $!");
	chmod(0600, "$sfsdir/identity") or
	    fatal("Could not chmod $sfsdir/identity: $!");
792

793
794
795
796
797
798
799
	#
	# Grab a copy for the DB. Causes an SFS update key to run so
	# that key is inserted into the files.
	#
	my $ident = `cat $sfsdir/identity`;

	if ($ident =~ /.*,.*,.*,(.*),(.*)/) {
800
801
802
803
804
805
806
	    # Version 6
	    DBQueryFatal("replace into user_sfskeys ".
			 "values ('$user', '$2', '${user}:${1}:${user}::', ".
			 "now())");
	}
	elsif ($ident =~ /.*:.*:.*:(.*):(.*)/) {
	    # Version 7
807
808
809
810
811
812
813
814
815
816
817
	    DBQueryFatal("replace into user_sfskeys ".
			 "values ('$user', '$2', '${user}:${1}:${user}::', ".
			 "now())");
	}
	else {
	    warn("*** $0:\n".
		 "    Bad emulab SFS public key\n");
	}
	$sfsupdate = 1;
    }
    return 0;
818
}
819
820
821
822
823
824
825

sub fatal($) {
    my($mesg) = $_[0];

    die("*** $0:\n".
	"    $mesg\n");
}