diff --git a/account/dumpuser.in b/account/dumpuser.in index d5ced425a5d7f2ebdb8746c3ee9c169fb1eee2da..1233571820315fe219b0098d9d8d2026dccbfa51 100644 --- a/account/dumpuser.in +++ b/account/dumpuser.in @@ -1,6 +1,6 @@ #!/usr/bin/perl -w # -# Copyright (c) 2010-2011 University of Utah and the Flux Group. +# Copyright (c) 2010-2013 University of Utah and the Flux Group. # # {{{EMULAB-LICENSE # @@ -121,7 +121,7 @@ sub DumpUser($) "optional" => 0 }, "email" => {"tag" => "email", "optional" => 0 }, - "pswd" => {"tag" => "password", + "pswd" => {"tag" => "passhash", "optional" => 0 }, "uid" => {"tag" => "uid", "optional" => 0 }, diff --git a/account/manageremote.in b/account/manageremote.in index f0b07d8dbdf072995abc27ac95ff755638f30232..528d9b4350aeff36be03d94d392075874789162d 100644 --- a/account/manageremote.in +++ b/account/manageremote.in @@ -49,10 +49,12 @@ sub usage() print " manageremote addgroup <remote> <gid>\n"; exit(1); } -my $optlist = "dnf"; +my $optlist = "dnfp"; my $debug = 0; my $force = 0; my $impotent = 0; +my $locked = 0; +my $fromdaemon = 0; # # Function prototypes @@ -121,8 +123,11 @@ if (defined($options{"f"})) { if (defined($options{"n"})) { $impotent = 1; } +if (defined($options{"p"})) { + $fromdaemon = 1; +} usage() - if (@ARGV < 2 || @ARGV > 4); + if (@ARGV < 2 || @ARGV > 5); my $cmd = shift(@ARGV); my $peername = shift(@ARGV); @@ -170,26 +175,36 @@ my $credential = GeniCredential->GetSelfCredential($me); if (!defined($credential)) { fatal("Could not create self credential for $me"); } +my $authority; # # All operations other then AddPeer require that the peer be # in the DB. # -if ($cmd eq "addpeer") { - AddPeer(); - exit(0); -} -else { +if ($cmd ne "addpeer") { my $query_result = DBQueryFatal("select name,urn from emulab_peers ". "where name='$peername' or urn='$peername'"); fatal("Unknown peer") if (!$query_result->numrows); ($peername,$peerurn) = $query_result->fetchrow_array(); + + $authority = GeniAuthority->CreateFromRegistry("sa", $peerurn); + if (!defined($authority)) { + fatal("Could not locate authority for $peername"); + } } -my $authority = GeniAuthority->CreateFromRegistry("sa", $peerurn); -if (!defined($authority)) { - fatal("Could not locate authority for $peername"); + +# +# All operations other then xlogin require locking to avoid a +# race with the portal_daemon. +# +if ($cmd ne "xlogin" && !$fromdaemon) { + while (TBScriptLock("portal_op", 0, 5) != TBSCRIPTLOCK_OKAY()) { + print "Could not get the lock; trying again ... ^C to stop trying.\n"; + next; + } + $locked = 1; } # @@ -204,6 +219,10 @@ SWITCH: for ($cmd) { AddUser(); last SWITCH; }; + /^addpeer$/ && do { + AddPeer(); + last SWITCH; + }; /^deluser$/ && do { DeleteUser(); last SWITCH; @@ -230,8 +249,12 @@ SWITCH: for ($cmd) { }; # Default + TBScriptUnlock() + if ($locked); usage(); } +TBScriptUnlock() + if ($locked); exit(0); # @@ -315,7 +338,7 @@ sub AddUser(;$) } my $urn = GeniHRN::Generate($OURDOMAIN, "user", $user->uid()); - my $xmlgoo = emutil::ExecQuiet("$DUMPUSER -p $uid"); + my $xmlgoo = emutil::ExecQuiet("$DUMPUSER $uid"); if ($?) { fatal("$DUMPUSER failed"); } @@ -393,7 +416,7 @@ sub ModifyUser() } my $urn = GeniHRN::Generate($OURDOMAIN, "user", $user->uid()); - my $xmlgoo = emutil::ExecQuiet("$DUMPUSER -p $uid"); + my $xmlgoo = emutil::ExecQuiet("$DUMPUSER $uid"); if ($?) { fatal("$DUMPUSER failed"); } @@ -568,9 +591,7 @@ sub AddProject() " exported=now(), updated=now(), ". " peer='$peername'"); - if (!$leader_result->numrows) { - SetGroups($leader_idx); - } + SetGroups($leader_idx); return 0; } diff --git a/account/newuser.in b/account/newuser.in index 1c3935025880dbcd9eb453105e7053f9b05a9e38..4555ceb8447c73a2c2a06810c873f6c475776698 100644 --- a/account/newuser.in +++ b/account/newuser.in @@ -1,6 +1,6 @@ #!/usr/bin/perl -w # -# Copyright (c) 2000-2012 University of Utah and the Flux Group. +# Copyright (c) 2000-2013 University of Utah and the Flux Group. # # {{{EMULAB-LICENSE # @@ -35,11 +35,12 @@ sub usage() print("Usage: newuser [-s] -t <type> <xmlfile>\n"); exit(-1); } -my $optlist = "dt:ns"; +my $optlist = "dt:nsp"; my $debug = 0; my $impotent= 0; my $type = ""; my $silent = 0; +my $portal = 0; my @keyfiles = (); # @@ -213,6 +214,11 @@ foreach my $key (keys(%required)) { fatal("Missing required attribute '$key'") if (! exists($xmlparse->{'attribute'}->{"$key"})); } +# +# Always delete this. Used by the portal code but we ignore it. +# +delete($xmlparse->{'attribute'}->{"passhash"}) + if (exists($xmlparse->{'attribute'}->{"passhash"})); # # We build up an array of arguments to pass to User->Create() as we check diff --git a/account/tbacct.in b/account/tbacct.in index d2d5ff9943db8af70dd4bfcf215cc4704a052ed1..5a1028315805ad24131994ce9eef518a86e8dfd4 100644 --- a/account/tbacct.in +++ b/account/tbacct.in @@ -735,6 +735,9 @@ sub UpdatePassword() # return 0 if (getpwuid($UID) eq "nobody"); + + return 0 + if ($isnonlocal || $nocollabtools); $EUID = $UID; # And the wiki if enabled. @@ -882,6 +885,9 @@ sub UpdateUser(;$) } $UID = $SAVEUID; + return 0 + if ($isnonlocal || $nocollabtools); + $EUID = $UID; # Update elists in case email changed. system("$MMMODIFYUSER $user") diff --git a/backend/moduserinfo.in b/backend/moduserinfo.in index 164614ac7a03585ae8ad25db0805c9f65bf5f13f..16aca96d500401119f8613591ca683e3e0f81722 100644 --- a/backend/moduserinfo.in +++ b/backend/moduserinfo.in @@ -1,6 +1,6 @@ #!/usr/bin/perl -wT # -# Copyright (c) 2000-2012 University of Utah and the Flux Group. +# Copyright (c) 2000-2013 University of Utah and the Flux Group. # # {{{EMULAB-LICENSE # @@ -183,6 +183,8 @@ my %xmlfields = "user_interface" => ["user_interface", $SLOT_OPTIONAL], "pubkeys" => ["pubkeys", $SLOT_SKIP], "wikiname" => ["pubkeys", $SLOT_SKIP], + # The portal code sets this, we ignore it here. + "passhash" => ["passhash", $SLOT_SKIP], # These are alternates. "name" => ["usr_name", $SLOT_OPTIONAL], "title" => ["usr_title", $SLOT_OPTIONAL], diff --git a/protogeni/lib/GeniEmulab.pm.in b/protogeni/lib/GeniEmulab.pm.in index 1ff58e7beb90897784321f60d4eca1d7e3446967..c58f75759c4b5afb11516401bf4b6adbedf982ca 100755 --- a/protogeni/lib/GeniEmulab.pm.in +++ b/protogeni/lib/GeniEmulab.pm.in @@ -69,6 +69,7 @@ my $PGENIDOMAIN = "@PROTOGENI_DOMAIN@"; my $ELABINELAB = "@ELABINELAB@"; my $NEWUSER = "$TB/sbin/newuser"; my $RMUSER = "$TB/sbin/rmuser"; +my $CHPASS = "$TB/sbin/tbacct passwd"; my $ADDUSER = "$TB/sbin/tbacct add"; my $MODUSER = "$TB/bin/moduserinfo"; my $NEWPROJ = "$TB/sbin/newproj"; @@ -100,6 +101,7 @@ sub AddUser($) my $credentials = $argref->{'credentials'}; my $xmlgoo = $argref->{'xmlstring'}; my $urn = $argref->{'urn'}; + my $passhash; if (! (defined($credentials) && defined($xmlgoo) && defined($urn))) { return GeniResponse->MalformedArgsResponse("Missing arguments"); @@ -135,6 +137,15 @@ sub AddUser($) return GeniResponse->Create(GENIRESPONSE_BADARGS) if (! User->ValidUID($login)); + if (exists($xmlparse->{'attribute'}->{"passhash"})) { + $passhash = $xmlparse->{'attribute'}->{"passhash"}->{'value'}; + + return GeniResponse->Create(GENIRESPONSE_BADARGS, undef, + "Invalid characters in password hash") + if (! ($passhash =~ /^\$\d\$\w*\$[\w\/\.]*$/ || + $passhash =~ /^[\w\/\.]*$/)); + } + my $user = User->Lookup($login); if (defined($user)) { return GeniResponse->Create(GENIRESPONSE_ALREADYEXISTS, undef, @@ -182,6 +193,14 @@ sub AddUser($) $user->SetStatus($User::USERSTATUS_ACTIVE); $user->Update({"nocollabtools" => "1", "manager_urn" => $credential->owner_urn()}); + + if (defined($passhash) && + $user->SetPassword($passhash) != 0) { + GeniUtil::FlipToGeniUser(); + return GeniResponse->Create(GENIRESPONSE_ERROR, undef, + "Internal error setting user password"); + } + $output = GeniUtil::ExecQuiet("$WAP $ADDUSER $login"); if ($?) { $user->Delete(); @@ -314,6 +333,37 @@ sub ModifyUser($) "Internal error modifying user"); } unlink($filename); + + # + # Look for password change. Need special treatment cause its a hash. + # + my $xmlparse = eval { XMLin($xmlgoo, + ForceArray => ["pubkeys"], + VarAttr => 'name', + ContentKey => '-content', + SuppressEmpty => undef); }; + + return GeniResponse->Create(GENIRESPONSE_ERROR, undef, "$@") + if ($@); + + if (exists($xmlparse->{'attribute'}->{"passhash"})) { + my $hash = $xmlparse->{'attribute'}->{"passhash"}->{'value'}; + + return GeniResponse->Create(GENIRESPONSE_BADARGS, undef, + "Invalid characters in password hash") + if (! ($hash =~ /^\$\d\$\w*\$[\w\/\.]*$/ || + $hash =~ /^[\w\/\.]*$/)); + + my $uid = $user->uid(); + my $safe_encoding = User::escapeshellarg($hash); + $output = GeniUtil::ExecQuiet("$WAP $CHPASS $uid $safe_encoding"); + if ($?) { + GeniUtil::FlipToGeniUser(); + print STDERR $output; + return GeniResponse->Create(GENIRESPONSE_ERROR, undef, + "Internal error modifying user password"); + } + } GeniUtil::FlipToGeniUser(); return GeniResponse->Create(GENIRESPONSE_SUCCESS); diff --git a/tbsetup/portal_daemon.in b/tbsetup/portal_daemon.in index b537b8c09188be5eec9788d8bd08ac3b002524e9..95e61bac1e8e8da75d4894b068bdad03a68a3fed 100644 --- a/tbsetup/portal_daemon.in +++ b/tbsetup/portal_daemon.in @@ -1,6 +1,6 @@ #!/usr/bin/perl -w # -# Copyright (c) 2009-2011 University of Utah and the Flux Group. +# Copyright (c) 2009-2013 University of Utah and the Flux Group. # # {{{EMULAB-LICENSE # @@ -49,7 +49,7 @@ my $PIDFILE = "/var/run/portal_daemon.pid"; my $SUDO = "/usr/local/bin/sudo"; my $PROTOUSER = "elabman"; my $WAP = "$TB/sbin/withadminprivs"; -my $MANAGEREMOTE = "$TB/sbin/manageremote"; +my $MANAGEREMOTE = "$TB/sbin/manageremote -p"; my $PORTAL_ENABLE = @PORTAL_ENABLE@; my $PORTAL_PRIMARY= @PORTAL_ISPRIMARY@; @@ -377,9 +377,17 @@ while (1) next; } logit("Running"); + # + # Lock, to avoid race with command line tool. + # + if (TBScriptLock("portal_op", 0, 30) != TBSCRIPTLOCK_OKAY()) { + logit("Could not get the lock after a long time. Trying again ...\n"); + next; + } ExportUsers(); ExportGroups(); UpdateUsers(); + TBScriptUnlock(); last if ($oneshot);