From b3a754cd4561e908ee117849b82ef7bedffb4046 Mon Sep 17 00:00:00 2001
From: Gary Wong <gtw@cs.utah.edu>
Date: Fri, 15 May 2009 21:57:46 +0000
Subject: [PATCH] Checkpointing work on URN support in certificates and
 credentials.  URNs are now generated for new certificates.  However, UUIDs
 and old HRNs are still used, for compatibility.

---
 account/mkusercert.in               |  4 ++++
 protogeni/etc/protogeni.sql         |  1 +
 protogeni/lib/GeniCertificate.pm.in | 33 +++++++++++++++++++++++------
 protogeni/lib/GeniCredential.pm.in  |  2 ++
 protogeni/test/showcredential.py    | 16 +++++++-------
 protogeni/updates/3                 | 18 ++++++++++++++++
 ssl/usercert.cnf.in                 |  1 +
 7 files changed, 60 insertions(+), 15 deletions(-)
 create mode 100644 protogeni/updates/3

diff --git a/account/mkusercert.in b/account/mkusercert.in
index 4e95030373..3755dad62b 100644
--- a/account/mkusercert.in
+++ b/account/mkusercert.in
@@ -256,6 +256,10 @@ else {
 }
 print TEMP "CN\t\t= $user_uuid\n";
 print TEMP "emailAddress\t= $user_uid" . "\@" . "$OURDOMAIN\n";
+
+print TEMP "\n[ req_altname ]\nURI=urn:publicid:IDN+$OURDOMAIN" .
+    "+user+$user_uid\nemail=$user_uid" . "\@" . "$OURDOMAIN\n";
+
 close(TEMP)
     or fatal("Could not close usercert.cnf: $!");
 
diff --git a/protogeni/etc/protogeni.sql b/protogeni/etc/protogeni.sql
index fa014af3ee..9bc3cfe8fb 100644
--- a/protogeni/etc/protogeni.sql
+++ b/protogeni/etc/protogeni.sql
@@ -148,6 +148,7 @@ CREATE TABLE `geni_certificates` (
   `cert` text,
   `DN` text,
   `privkey` text,
+  `uri` text,
   PRIMARY KEY  (`uuid`)
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1;
 
diff --git a/protogeni/lib/GeniCertificate.pm.in b/protogeni/lib/GeniCertificate.pm.in
index f249587c69..899b259875 100644
--- a/protogeni/lib/GeniCertificate.pm.in
+++ b/protogeni/lib/GeniCertificate.pm.in
@@ -66,7 +66,6 @@ sub Lookup($$)
 
     my $self          = {};
     $self->{'CERT'}   = $query_result->fetchrow_hashref();
-    $self->{'url'}    = undef;
     $self->{'stored'} = 1;
     bless($self, $class);
     my $cert = $self->cert();
@@ -98,6 +97,7 @@ sub DN($)		{ return field($_[0], "DN"); }
 sub privkey($)		{ return field($_[0], "privkey"); }
 sub revoked($)		{ return field($_[0], "revoked"); }
 sub certfile($)		{ return field($_[0], "certfile"); }
+sub uri($)              { return field($_[0], "uri"); }
 sub GetCertificate($)   { return $_[0]; }
 
 #
@@ -315,7 +315,7 @@ sub LoadFromFile($$)
     $self->{'CERT'}->{'revoked'}   = undef;
     $self->{'CERT'}->{'created'}   = undef;
     $self->{'CERT'}->{'certfile'}  = $filename;
-    $self->{'url'}                 = $url;
+    $self->{'CERT'}->{'uri'}       = $url;
     return $self;
 }
 
@@ -336,6 +336,8 @@ sub Store($)
     push(@inserts, "DN=" . DBQuoteSpecial($self->DN()));
     push(@inserts, "privkey=" . DBQuoteSpecial($self->privkey()))
 	if (defined($self->privkey()));
+    push(@inserts, "uri=" . DBQuoteSpecial($self->uri()))
+	if (defined($self->uri()));
 
     return -1
 	if (!DBQueryWarn("replace into geni_certificates set ".
@@ -374,7 +376,7 @@ sub WriteToFile($;$)
 sub URL($)
 {
     my ($self) = @_;
-    my $url    = $self->{'url'};
+    my $url    = $self->uri();
 
     return $url
 	if (defined($url));
@@ -384,10 +386,21 @@ sub URL($)
 	print STDERR "Could not start $OPENSSL on $filename\n";
 	return undef;
     }
+    my $altname = 0;
     while (<X509>) {
-	if ($_ =~ /^\s+URI:([-\w\.\/:]+)$/) {
-	    $url = $1;
-	    chomp($url);
+	if( /^\s+x509v3 Subject Alternative Name:\s*$/ ) {
+	    $altname = 1;
+	} elsif( $altname ) {
+	    # Gah!  OpenSSL is horrible.  Apparently the text output format
+	    # for the subject alternative name is fixed, and neither
+	    # -nameopt nor -certopt will help us.  Worse still, the
+	    # directory entries (e.g. URI, email) are comma separated...
+	    # but commas are legal characters in URIs (see RFC 3986, section
+	    # 2.2)!  We'll have to assume the delimiter is the ", " (comma,
+	    # space) pair...
+	    m'^\s*URI:([-!#$%()*+,./0-9:;=?@A-Z_a-z~]+)\s*$' and $url = $1
+		foreach split( /, / );
+	    $altname = 0;
 	}
     }
     if (!close(X509) || !defined($url)) {
@@ -395,7 +408,7 @@ sub URL($)
 	return undef;
     }
     unlink($filename);
-    $self->{'url'} = $url;
+    $self->{'CERT'}->{'uri'} = $url;
     return $url;
 }
 
@@ -456,9 +469,13 @@ sub StoreCRL($$$)
 # Wrapper for local users.
 #
 package GeniCertificate::LocalUser;
+use GeniHRN;
 use English;
 use emdb;
 
+# Configure variables
+my $OURDOMAIN      = "@OURDOMAIN@";
+
 #
 # Create a wrapper, with the same access names.
 #
@@ -477,6 +494,8 @@ sub Create($$)
 
     my $self           = {};
     $self->{'CERT'}    = $query_result->fetchrow_hashref();
+    $self->{'CERT'}->{'uri'} = GeniHRN::Generate( $OURDOMAIN, "user",
+						  $self->{'CERT'}->{'uid'} );
     $self->{'stored'}  = 1;
     bless($self, $class);
 
diff --git a/protogeni/lib/GeniCredential.pm.in b/protogeni/lib/GeniCredential.pm.in
index 4c2b68c184..560448f097 100644
--- a/protogeni/lib/GeniCredential.pm.in
+++ b/protogeni/lib/GeniCredential.pm.in
@@ -123,6 +123,8 @@ sub extensions($)	{ return field($_[0], "extensions"); }
 sub owner_cert($)	{ return $_[0]->{"owner_cert"}; }
 sub target_cert($)	{ return $_[0]->{"target_cert"}; }
 sub hrn($)		{ return $_[0]->{"target_cert"}->hrn(); }
+sub target_uri($)       { return $_[0]->{"target_cert"}->uri(); }
+sub owner_uri($)        { return $_[0]->{"owner_cert"}->uri(); }
 
 #
 # Stringify for output.
diff --git a/protogeni/test/showcredential.py b/protogeni/test/showcredential.py
index e7b219f9bf..71af339bf7 100755
--- a/protogeni/test/showcredential.py
+++ b/protogeni/test/showcredential.py
@@ -60,6 +60,12 @@ def Decode( gid ):
     f.close()
     return s
 
+def SubjectName( cert ):
+    return ( re.search( r"X509v3 Subject Alternative Name:[ \t]*\n[ \t]*.*URI:"
+                        "(urn:publicid:[-!$%()*+.0-9:;=?@A-Z_a-z~]+)", \
+                        cert ) or \
+             re.search( r"Subject: .*OU=([-\w.]+)", cert ) ).group( 1 )
+
 def ShowCredential( cred, level ):
 
     if level == 0:
@@ -79,14 +85,8 @@ def ShowCredential( cred, level ):
     owner = Decode( Text( Lookup( cred, "owner_gid" ) ) )
     target = Decode( Text( Lookup( cred, "target_gid" ) ) )
 
-    print "    Owner:"
-    print "        Subject:", re.search( r"Subject: (.+)", owner ).group( 1 )
-    print "        Issuer:", re.search( r"Issuer: (.+)", owner ).group( 1 )
-
-    print "    Target:"
-    print "        Subject:", re.search( r"Subject: (.+)", target ).group( 1 )
-    print "        Issuer:", re.search( r"Issuer: (.+)", target ).group( 1 )
-
+    print "    Owner: " + SubjectName( owner )
+    print "    Target: " + SubjectName( target )
     print "    UUID: " + Text( Lookup( cred, "uuid" ) )
     print "    Expires: " + Text( Lookup( cred, "expires" ) )
 
diff --git a/protogeni/updates/3 b/protogeni/updates/3
new file mode 100644
index 0000000000..5d4e090d00
--- /dev/null
+++ b/protogeni/updates/3
@@ -0,0 +1,18 @@
+#
+# Add a URI column to the certificate table, for storing new GENI identifiers.
+#
+use strict;
+use GeniDB;
+
+sub DoUpdate($$$)
+{
+    my ($dbhandle, $dbname, $version) = @_;
+
+    DBSetDefault( $dbhandle );
+
+    DBQueryFatal( "ALTER TABLE `geni_certificates` ADD `uri` text" )
+	unless DBSlotExists( "geni_certificates", "uri" );
+    
+    return 0;
+}
+1;
diff --git a/ssl/usercert.cnf.in b/ssl/usercert.cnf.in
index 140bbe8472..5e3ad42a24 100644
--- a/ssl/usercert.cnf.in
+++ b/ssl/usercert.cnf.in
@@ -11,6 +11,7 @@ string_mask             = nombstr
 [ request_extensions ]
 basicConstraints=critical,CA:TRUE
 subjectKeyIdentifier=hash
+subjectAltName=@req_altname
 
 # This will be appended to by mkusercert.
 [ req_distinguished_name ]
-- 
GitLab