Commit 2e3f0abc authored by Leigh B. Stoller's avatar Leigh B. Stoller

Commit my changes that use the UUID in the certificate, rather then the

uid of the user. Not sure this change will survive in its present form
since this was done for protogeni and the spec will probably call for
something different. But this is what it is for now. Note that the server
side is backwards compatable with old certs, at least for now.
parent 54749a00
#!/usr/bin/perl -wT #!/usr/bin/perl -wT
# #
# EMULAB-COPYRIGHT # EMULAB-COPYRIGHT
# Copyright (c) 2000-2004, 2006, 2007 University of Utah and the Flux Group. # Copyright (c) 2000-2008 University of Utah and the Flux Group.
# All rights reserved. # All rights reserved.
# #
use strict;
use English; use English;
use Getopt::Std; use Getopt::Std;
use Fcntl ':flock';
# #
# Load the Testbed support stuff. # Load the Testbed support stuff.
...@@ -23,13 +22,14 @@ use User; ...@@ -23,13 +22,14 @@ use User;
# #
sub usage() sub usage()
{ {
print("Usage: mkusercert [-d] [-o] [-p password] <user>\n"); print("Usage: mkusercert [-d] [-o] [-g] [-p password] <user>\n");
exit(-1); exit(-1);
} }
my $optlist = "dp:o"; my $optlist = "dp:og";
my $debug = 0; my $debug = 0;
my $output = 0; my $output = 0;
my $password; my $password = "";
my $geniflag = 0;
# #
# Configure variables # Configure variables
...@@ -40,6 +40,7 @@ my $TBLOGS = "@TBLOGSEMAIL@"; ...@@ -40,6 +40,7 @@ my $TBLOGS = "@TBLOGSEMAIL@";
my $OURDOMAIN = "@OURDOMAIN@"; my $OURDOMAIN = "@OURDOMAIN@";
my $CONTROL = "@USERNODE@"; my $CONTROL = "@USERNODE@";
my $BOSSNODE = "@BOSSNODE@"; my $BOSSNODE = "@BOSSNODE@";
my $OU = "sslxmlrpc"; # orgunit
# Locals # Locals
my $USERDIR = USERROOT(); my $USERDIR = USERROOT();
...@@ -52,6 +53,11 @@ my $OPENSSL = "/usr/bin/openssl"; ...@@ -52,6 +53,11 @@ my $OPENSSL = "/usr/bin/openssl";
my $WORKDIR = "$TB/ssl"; my $WORKDIR = "$TB/ssl";
my $SAVEUID = $UID; my $SAVEUID = $UID;
# Locals
my $encrypted = 0;
my $db_password = "''";
my $sh_password = "";
# #
# We don't want to run this script unless its the real version. # We don't want to run this script unless its the real version.
# #
...@@ -89,7 +95,7 @@ sub fatal($); ...@@ -89,7 +95,7 @@ sub fatal($);
# Rewrite audit version of ARGV to prevent password in mail logs. # Rewrite audit version of ARGV to prevent password in mail logs.
# #
my @NEWARGV = @ARGV; my @NEWARGV = @ARGV;
for ($i = 0; $i < scalar(@NEWARGV); $i++) { for (my $i = 0; $i < scalar(@NEWARGV); $i++) {
if ($NEWARGV[$i] eq "-p") { if ($NEWARGV[$i] eq "-p") {
$NEWARGV[$i + 1] = "**********"; $NEWARGV[$i + 1] = "**********";
} }
...@@ -100,13 +106,17 @@ AuditSetARGV(@NEWARGV); ...@@ -100,13 +106,17 @@ AuditSetARGV(@NEWARGV);
# Parse command arguments. Once we return from getopts, all that should be # Parse command arguments. Once we return from getopts, all that should be
# left are the required arguments. # left are the required arguments.
# #
%options = (); my %options = ();
if (! getopts($optlist, \%options)) { if (! getopts($optlist, \%options)) {
usage(); usage();
} }
if (defined($options{"d"})) { if (defined($options{"d"})) {
$debug = 1; $debug = 1;
} }
if (defined($options{"g"})) {
$OU = "geni";
$geniflag = 1;
}
if (defined($options{"p"})) { if (defined($options{"p"})) {
$password = $options{"p"}; $password = $options{"p"};
...@@ -119,12 +129,18 @@ if (defined($options{"p"})) { ...@@ -119,12 +129,18 @@ if (defined($options{"p"})) {
else { else {
die("Tainted argument: $password\n"); die("Tainted argument: $password\n");
} }
$password =~ s/\'/\'\\\'\'/g; $db_password = DBQuoteSpecial($password);
$password = "'$password'"; $sh_password = $password;
$sh_password =~ s/\'/\'\\\'\'/g;
$sh_password = "$sh_password";
$encrypted = 1;
} }
if (@ARGV != 1) { if (@ARGV != 1) {
usage(); usage();
} }
if ($geniflag && !$encrypted) {
fatal("GENI certs must be encrypted (use -p password).");
}
my $user = $ARGV[0]; my $user = $ARGV[0];
# #
...@@ -172,9 +188,7 @@ TBScriptLock("mkusercert") == 0 or ...@@ -172,9 +188,7 @@ TBScriptLock("mkusercert") == 0 or
# #
# Get the user info (the user being operated on). # Get the user info (the user being operated on).
# #
my $fullname = $target_user->name(); my $user_uuid = $target_user->uuid();
my $user_email = $target_user->email();
my $usr_admin = $target_user->admin();
my $user_number = $target_user->unix_uid(); my $user_number = $target_user->unix_uid();
my $user_uid = $target_user->uid(); my $user_uid = $target_user->uid();
my $user_dbid = $target_user->dbid(); my $user_dbid = $target_user->dbid();
...@@ -200,6 +214,27 @@ else { ...@@ -200,6 +214,27 @@ else {
(undef,undef,$default_groupgid,undef) = getgrnam("guest"); (undef,undef,$default_groupgid,undef) = getgrnam("guest");
} }
#
# Need an index file, which is the openssl version of the DB.
#
if (! -e "index.txt") {
open(IND, ">index.txt")
or fatal("Could not create index.txt");
close(IND);
}
#
# We have to figure out what the next serial number will be and write
# that into the file. We could let "ca' keep track, but with devel
# trees, we might end up with duplicate serial numbers.
#
my $serial = TBGetUniqueIndex("user_sslcerts");
open(SER, ">serial")
or fatal("Could not create new serial file");
printf SER "%08x\n", $serial;
close(SER);
# #
# Create a template conf file. We tack on the DN record based on the # Create a template conf file. We tack on the DN record based on the
# user particulars. # user particulars.
...@@ -210,9 +245,8 @@ system("cp -f $TEMPLATE usercert.cnf") == 0 ...@@ -210,9 +245,8 @@ system("cp -f $TEMPLATE usercert.cnf") == 0
open(TEMP, ">>usercert.cnf") open(TEMP, ">>usercert.cnf")
or fatal("Could not open $TEMPLATE for append: $!"); or fatal("Could not open $TEMPLATE for append: $!");
print TEMP "OU\t\t= sslxmlrpc\n"; print TEMP "OU\t\t= $OU\n";
print TEMP "CN\t\t= $user_uid\n"; print TEMP "CN\t\t= $user_uuid\n";
print TEMP "emailAddress\t= $user_uid" . "\@" . "$OURDOMAIN\n";
close(TEMP) close(TEMP)
or fatal("Could not close usercert.cnf: $!"); or fatal("Could not close usercert.cnf: $!");
...@@ -220,53 +254,35 @@ close(TEMP) ...@@ -220,53 +254,35 @@ close(TEMP)
# Create a client side private key and certificate request. # Create a client side private key and certificate request.
# #
system("$OPENSSL req -new -config usercert.cnf ". system("$OPENSSL req -new -config usercert.cnf ".
(defined($password) ? " -passout pass:${password} " : " -nodes ") . ($encrypted ? " -passout 'pass:${sh_password}' " : " -nodes ") .
" -keyout usercert_key.pem -out usercert_req.pem") == 0 " -keyout usercert_key.pem -out usercert_req.pem") == 0
or fatal("Could not create certificate request"); or fatal("Could not create certificate request");
#
# Remove the index file. We keep track of things ourselves. We also have to
# figure out what the next serial number will be and write that into the
# file. We could let "ca' keep track, but with devel trees, we might end
# up with duplicate serial numbers.
#
open(IND, ">index.txt")
or fatal("Could not clear index.txt");
close(IND);
my $curidx = TBGetUniqueIndex("user_sslcerts");
open(SER, ">serial")
or fatal("Could not create new serial file");
printf SER "%08x\n", $curidx;
close(SER);
# #
# Sign the client cert request, creating a client certificate. # Sign the client cert request, creating a client certificate.
# #
$UID = 0; $UID = 0;
system("$OPENSSL ca -batch -policy policy_sslxmlrpc -config $CACONFIG ". system("$OPENSSL ca -batch -policy policy_sslxmlrpc ".
" -name CA_usercerts ". " -name CA_usercerts -config $CACONFIG ".
" -out usercert_cert.pem -cert $EMULAB_CERT -keyfile $EMULAB_KEY ". " -out usercert_cert.pem -cert $EMULAB_CERT -keyfile $EMULAB_KEY ".
" -infiles usercert_req.pem") == 0 " -infiles usercert_req.pem") == 0
or fatal("Could not sign certificate request"); or fatal("Could not sign certificate request");
$UID = $SAVEUID; $UID = $SAVEUID;
# #
# For now, there can be just one cert of each kind (encrypted, and # We save all of the certs in the DB, but we are not worrying about
# unencrypted). Might change that at some point. # revocation yet. By saving them, we can eventually do that. But we do
# have to set the status bit to revoked though or else we will not know
# later (okay, we can probably figure it out if we had to).
# #
DBQueryFatal("delete from user_sslcerts ". DBQueryFatal("update user_sslcerts set ".
"where uid_idx='$user_dbid' and ". " status='revoked',revoked=now() ".
" encrypted=" . (defined($password) ? 1 : 0)); "where uid_idx='$user_dbid' and encrypted=$encrypted");
#
# Create a new entry in the table.
#
DBQueryFatal("insert into user_sslcerts ". DBQueryFatal("insert into user_sslcerts ".
"(uid, uid_idx, idx, created, encrypted) values ". "(uid,uid_idx,idx,created,encrypted,orgunit,status,password) ".
"('$user_uid', '$user_dbid', $curidx, now(), ". "values ('$user_uid', '$user_dbid', $serial, now(), ".
(defined($password) ? 1 : 0) . ")"); " $encrypted, '$OU', 'valid', $db_password)");
# #
# Grab the cert path and strip off the header goo, then insert into # Grab the cert path and strip off the header goo, then insert into
...@@ -301,12 +317,11 @@ close(PKEY); ...@@ -301,12 +317,11 @@ close(PKEY);
$pkeystring = DBQuoteSpecial($pkeystring); $pkeystring = DBQuoteSpecial($pkeystring);
$certstring = DBQuoteSpecial($certstring); $certstring = DBQuoteSpecial($certstring);
DBQueryFatal("update user_sslcerts set cert=$certstring,privkey=$pkeystring ". DBQueryFatal("update user_sslcerts set cert=$certstring,privkey=$pkeystring ".
"where uid_idx='$user_dbid' and idx=$curidx"); "where uid_idx='$user_dbid' and idx=$serial");
# #
# Combine the key and the certificate into one file which is installed # Combine the key and the certificate into one file which is
# on each remote node and used by tmcc. Installed on boss too so # installed in the users home directory.
# we can test tmcc there.
# #
system("cat usercert_key.pem usercert_cert.pem > usercert.pem") == 0 system("cat usercert_key.pem usercert_cert.pem > usercert.pem") == 0
or fatal("Could not combine cert and key into one file"); or fatal("Could not combine cert and key into one file");
...@@ -325,7 +340,7 @@ if (! -d $ssldir) { ...@@ -325,7 +340,7 @@ if (! -d $ssldir) {
my $target; my $target;
if (defined($password)) { if ($encrypted) {
$target = "$ssldir/encrypted.pem"; $target = "$ssldir/encrypted.pem";
} }
else { else {
...@@ -338,6 +353,25 @@ system("cp -f usercert.pem $target") == 0 ...@@ -338,6 +353,25 @@ system("cp -f usercert.pem $target") == 0
chown($user_number, $default_groupgid, "$target") chown($user_number, $default_groupgid, "$target")
or fatal("Could not chown $target: $!"); or fatal("Could not chown $target: $!");
if ($encrypted) {
#
# Convert to pkcs12 format, strictly for the geni xmlrpc code, whichs
# does not provide a way to give the passphrase for encrypted x509 keys.
#
system("$OPENSSL pkcs12 -export -in usercert.pem -des ".
"-passin 'pass:${sh_password}' -passout 'pass:${sh_password}' ".
"-out usercert.p12 -rand ./.rnd")
== 0 or fatal("Could not create usercert.p12");
$target = "$ssldir/encrypted.p12";
system("cp -f usercert.p12 $target") == 0
or fatal("Could not copy usercert.p12 to $target");
chown($user_number, $default_groupgid, "$target")
or fatal("Could not chown $target: $!");
}
TBScriptUnlock(); TBScriptUnlock();
exit(0); exit(0);
......
#!/usr/local/bin/python #!/usr/local/bin/python
# #
# EMULAB-COPYRIGHT
# Copyright (c) 2005-2008 University of Utah and the Flux Group. # Copyright (c) 2005-2008 University of Utah and the Flux Group.
# All rights reserved. # All rights reserved.
# #
...@@ -221,24 +220,26 @@ class MyServer(SSL.ForkingSSLServer, SimpleXMLRPCDispatcher): ...@@ -221,24 +220,26 @@ class MyServer(SSL.ForkingSSLServer, SimpleXMLRPCDispatcher):
# #
# Get the unix_uid for the user. User must be active. # Get the unix_uid for the user. User must be active.
# #
def getuserid(self, uid): def getuserid(self, uuid):
userQuery = DBQueryFatal("select unix_uid,status from users " userQuery = DBQueryFatal("select uid,uid_idx,unix_uid,status "
"where uid=%s", " from users "
(uid,)) "where uid_uuid=%s or uid=%s",
(uuid, uuid))
if len(userQuery) == 0: if len(userQuery) == 0:
return 0 return (None, None, 0);
if (userQuery[0][1] != "active"): if (userQuery[0][3] != "active"):
return -1 return (None, None, -1);
return int(userQuery[0][0]) return (userQuery[0][0], int(userQuery[0][1]), int(userQuery[0][2]))
# #
# Check if the user is an stud. # Check if the user is an stud.
# #
def isstuduser(self, uid): def isstuduser(self, uid_idx):
res = DBQueryFatal("select stud from users where uid=%s", (uid,)) res = DBQueryFatal("select stud from users where uid_idx=%s",
(str(uid_idx),))
if len(res) == 0: if len(res) == 0:
return 0 return 0
...@@ -248,26 +249,26 @@ class MyServer(SSL.ForkingSSLServer, SimpleXMLRPCDispatcher): ...@@ -248,26 +249,26 @@ class MyServer(SSL.ForkingSSLServer, SimpleXMLRPCDispatcher):
# #
# Check the certificate serial number. # Check the certificate serial number.
# #
def checkcert(self, uid, serial): def checkcert(self, uid_idx, serial):
res = DBQueryFatal("select idx from user_sslcerts " res = DBQueryFatal("select idx from user_sslcerts "
"where uid=%s and idx=%s", "where uid_idx=%s and idx=%s and status='valid'",
(uid, serial)) (str(uid_idx), serial))
return len(res) return len(res)
# #
# Get the group list for the user. # Get the group list for the user.
# #
def getusergroups(self, user): def getusergroups(self, uid_idx):
result = [] result = []
res = DBQueryFatal("select distinct g.gid,g.unix_gid " res = DBQueryFatal("select distinct g.gid,g.unix_gid "
" from group_membership as m " " from group_membership as m "
"left join groups as g on " "left join groups as g on "
" g.pid=m.pid and g.gid=m.gid " " g.pid_idx=m.pid_idx and g.gid_idx=m.gid_idx "
"where m.uid=%s " "where m.uid_idx=%s "
"order by date_approved asc ", "order by date_approved asc ",
(user,)) (str(uid_idx),))
for group in res: for group in res:
result.append(int(group[1])); result.append(int(group[1]));
...@@ -283,22 +284,28 @@ class MyServer(SSL.ForkingSSLServer, SimpleXMLRPCDispatcher): ...@@ -283,22 +284,28 @@ class MyServer(SSL.ForkingSSLServer, SimpleXMLRPCDispatcher):
if self.debug: if self.debug:
self.logit(str(subject)) self.logit(str(subject))
pass pass
#
# The CN might look like UUID,serial so split it up.
#
cnwords = getattr(subject, "CN").split(",")
self.uuid = cnwords[0]
self.user = getattr(subject, "CN");
# #
# Must be a valid and non-zero unix_uid from the DB. # Must be a valid and non-zero unix_uid from the DB.
# #
self.uid = self.getuserid(self.user) (self.uid,self.uid_idx,self.unix_uid) = self.getuserid(self.uuid)
if self.uid == 0: if self.unix_uid == 0:
self.logit('No such user: "%s"' % self.user) self.logit('No such user: "%s"' % self.uuid)
raise Exception('No such user: "%s"' % self.user) raise Exception('No such user: "%s"' % self.uuid)
if self.uid == -1: if self.unix_uid == -1:
self.logit('User "%s" is not active' % self.user) self.logit('User "%s,%d" is not active' % (self.uid,self.uid_idx))
raise Exception('User "%s" is not active' % self.user) raise Exception('User "%s,%d" is not active' %
(self.uid,self.uid_idx))
self.stud = self.isstuduser(self.user) self.stud = self.isstuduser(self.uid_idx)
if self.stud: if self.stud:
try: try:
ALLOWED_PATHS.extend(map(lambda x: ALLOWED_PATHS.extend(map(lambda x:
...@@ -309,13 +316,14 @@ class MyServer(SSL.ForkingSSLServer, SimpleXMLRPCDispatcher): ...@@ -309,13 +316,14 @@ class MyServer(SSL.ForkingSSLServer, SimpleXMLRPCDispatcher):
pass pass
pass pass
self.glist = self.getusergroups(self.user); self.glist = self.getusergroups(self.uid_idx);
if len(self.glist) == 0: if len(self.glist) == 0:
self.logit('No groups for user: "%s"' % self.user) self.logit('No groups for user: "%s,%d"' % (self.uid,self.uid_idx))
raise Exception('No groups for user: "%s"' % self.user) raise Exception('No groups for user: "%s,%d"' %
(self.uid,self.uid_idx))
self.logit("Connect from %s: %s %s" % self.logit("Connect from %s: %s,%d %s" %
(client[0], self.user, str(self.glist))) (client[0], self.uid, self.uid_idx, str(self.glist)))
# #
# Check the certificate serial number. At the moment, the serial # Check the certificate serial number. At the moment, the serial
...@@ -324,19 +332,19 @@ class MyServer(SSL.ForkingSSLServer, SimpleXMLRPCDispatcher): ...@@ -324,19 +332,19 @@ class MyServer(SSL.ForkingSSLServer, SimpleXMLRPCDispatcher):
# #
serial = request.get_peer_cert().get_serial_number() serial = request.get_peer_cert().get_serial_number()
if self.checkcert(self.user, serial) == 0: if self.checkcert(self.uid_idx, serial) == 0:
self.logit('No such cert with serial "%s"' % serial) self.logit('No such cert with serial "%s"' % serial)
raise Exception('No such cert with serial "%s"' % serial) raise Exception('No such cert with serial "%s"' % serial)
try: try:
os.setgid(self.glist[0]) os.setgid(self.glist[0])
os.setgroups(self.glist) os.setgroups(self.glist)
os.setuid(self.uid) os.setuid(self.unix_uid)
pwddb = pwd.getpwuid(self.uid); pwddb = pwd.getpwuid(self.unix_uid);
os.environ["HOME"] = pwddb[5]; os.environ["HOME"] = pwddb[5];
os.environ["USER"] = self.user; os.environ["USER"] = self.uid;
os.environ["LOGNAME"] = self.user; os.environ["LOGNAME"] = self.uid;
pass pass
except: except:
traceback.print_exc() traceback.print_exc()
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment