From d0cfcfc20ac3ae005faa7c5582364ad0e27cf5ad Mon Sep 17 00:00:00 2001 From: Leigh B Stoller <stoller@flux.utah.edu> Date: Fri, 22 Mar 2013 09:12:56 -0600 Subject: [PATCH] Changes to allow passing password hash from the portal master to the portal peers. Also add locking between the portal daemon and manageremote. --- account/dumpuser.in | 4 +-- account/manageremote.in | 51 ++++++++++++++++++++++++---------- account/newuser.in | 10 +++++-- account/tbacct.in | 6 ++++ backend/moduserinfo.in | 4 ++- protogeni/lib/GeniEmulab.pm.in | 50 +++++++++++++++++++++++++++++++++ tbsetup/portal_daemon.in | 12 ++++++-- 7 files changed, 115 insertions(+), 22 deletions(-) diff --git a/account/dumpuser.in b/account/dumpuser.in index d5ced425a5..1233571820 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 f0b07d8dbd..528d9b4350 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 1c39350258..4555ceb844 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 d2d5ff9943..5a10283158 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 164614ac7a..16aca96d50 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 1ff58e7beb..c58f75759c 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 b537b8c091..95e61bac1e 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); -- GitLab