Author: Neil Gorsuch (ngorsuch@ncsa.uiuc.edu) Last Update: October 10, 2007
#!/bin/sh # has to be just one command line input argument if ! echo "$1" | egrep '^[A-Za-z][A-Za-z0-9_-]*$' >/dev/null ; then echo extapp error bad input parameter \'$1\' >&2 exit 1 fi # make a temprary directory for intermediate stage files tmpdir=/tmp/`basename $0`.$$ trap "cd ; rm -rf $tmpdir ; exit 1" 1 2 3 15 if ! mkdir $tmpdir ; then echo extapp error cannot create temporary directory \'$tmpdir\' >&2 exit 1 fi cat <<EOF | ... mysql commands to retrieve a user's group ... ... names with the user name denoted by "%USER%" ... ... for more information on the actual command ... ... see the mysql queries sub-section of the ... ... "MaeViz Group Authorization" section of the cet-wiki ... EOF sed "s,%USER%,$1,g" | \ mysql --host=mysql-server --user=mysql-user --password=mysql-password | \ egrep -v '^TITLE$' > $tmpdir/groups.txt # now we have a text file with each group as a string on it's own line: # groupA # groupB # time to convert the group strings to something like this: # http://groupA http://groupb # note that the spacing between each http'ed group string HAS to be # a single tab character!! cat $tmpdir/groups.txt | \ sed 's,^,http://,g' | \ sed 's,$,/,g' | \ tr '\012' '\t' | \ sed 's,\t$,,g' > $tmpdir/groups.str # now we make a partial openssl configuration file that includes # the information that the GridShib SAML Tools needs to generate # a SAML assertion samlconfig=$tmpdir/saml.config cat <<EOF | NameID.Format=urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified NameID.Format.template=%PRINCIPAL% Attribute.isMemberOf.Namespace=urn:mace:shibboleth:1.0:attributeNamespace:unspecified Attribute.isMemberOf.Name=urn:oid:1.3.6.1.4.1.5923.1.5.1.1 Attribute.isMemberOf.Value=%ISMEMBEROF% Attribute.countryName.Namespace=urn:mace:shibboleth:1.0:attributeNamespace:uri Attribute.countryName.Name=urn:oid:2.5.4.6 Attribute.countryName.Value=USUSUS certLocation=file:///etc/grid-security/hostcert.pem keyLocation=file:///etc/grid-security/hostkey.pem EOF sed "s,%USER%,$1,g" | \ sed "s,%ISMEMBEROF%,`cat $tmpdir/groups.str`,g" >$samlconfig # now use the GridSHib SAML Tools to make the SAML assertion string # note these environmental variables should be correctly defined: # GRIDSHIB_HOME # GLOBUS_PATH # JAVA_HOME # PATH echo $1 > /tmp/user #/usr/local/gridshib-saml-tools-0_2_1-alpha/bin/gridshib-saml-issuer --user $1 --config=file\ ://$samlconfig --der > $tmpdir/assertions.der /usr/local/gridshib-saml-tools-0_2_1-alpha/bin/gridshib-saml-issuer --user $1 --config=file:\ //$samlconfig --saml > $tmpdir/assertions.der # now encode the SAML assertion string: # # into hex-encoded like this: # 3c:41:73:73:65:72:74:...:69:6f:6e:3e:0a /usr/local/sbin/hexderit < $tmpdir/assertions.der > $tmpdir/assertions.hexder printenv > /tmp/env # and then output it as an openssl config file fragment like this to standard output: # 1.3.6.1.4.1.3536.1.1.1.10=DER:3c:41:73:73:65:72:74:...:69:6f:6e:3e:0a if [ `wc -c < $tmpdir/assertions.hexder 2>/dev/null` -gt 0 ] ; then echo 1.3.6.1.4.1.3536.1.1.1.10=DER:`cat $tmpdir/assertions.hexder` >$tmpdir/ssc.txt cat $tmpdir/ssc.txt fi rm -rf $tmpdir exit 0
#!/usr/bin/perl my $input = <>; foreach my $index ( 0 .. ((length $input) - 1) ) { print ":" if $index > 0; printf "%02x", 0x7f & ord substr( $input, $index, 1 ); }
certificate_extapp /usr/local/sbin/myproxy-maeviz-groups-extapp
cd /usr/local wget http://gridshib.globus.org/downloads/gridshib-saml-tools-0_2_0-src.tar.gz tar xpzf gridshib-saml-tools-0_2_0-src.tar.gz cd gridshib-saml-tools-0_2_0 ant install
/usr/local/gridshib-saml-tools-0_2_0/build.xml
<target name="compile-gimo" depends="create-log4j-props, create-jars"> <echo message="Running test of GroupsIsMemberOf"/> <echo message="...Complete."/> </target>
/usr/local/gridshib-saml-tools-0_2_0/tests/org/globus/gridshib/security/GroupsIsMemberOf.java
/* * Copyright 2006-2007 University of Illinois * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy * of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ package org.globus.gridshib.security; import java.io.File; import java.io.IOException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.security.auth.Subject; import javax.security.auth.x500.X500Principal; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.globus.gridshib.common.mapper.EntityMap; import org.globus.gridshib.common.mapper.GridShibEntityMapper; import org.globus.gridshib.security.SAMLSecurityContext; import org.globus.gridshib.security.SecurityContextFactory; import org.globus.gridshib.security.saml.SelfIssuedAssertion; import org.globus.gridshib.security.saml.SimpleAttribute; import org.globus.gridshib.security.util.CertUtil; import org.globus.gridshib.security.util.GSIUtil; import org.globus.gridshib.security.util.SAMLUtil; import org.globus.gridshib.tool.GridShibToolConfigException; import org.globus.gridshib.tool.saml.SAMLToolConfig; import org.globus.gridshib.tool.saml.SAMLToolConfigLoader; import org.globus.gsi.GlobusCredential; import org.globus.gsi.GlobusCredentialException; import org.globus.opensaml11.md.common.Constants; import org.globus.opensaml11.saml.SAMLAuthenticationStatement; import org.globus.opensaml11.saml.SAMLException; import org.globus.opensaml11.saml.SAMLSubjectAssertion; /** * A test application that illustrates the use of the * standalone GridShib Security Framework. */ public class GroupsIsMemberOf { private static Log logger = LogFactory.getLog(GroupsIsMemberOf.class.getName()); // the issuing credential (EEC or proxy): private static GlobusCredential credential = null; // a mapping from SAML entities to X.509 entities: private static DifferentTrivialEntityMap entityMap = new DifferentTrivialEntityMap(); public static void main(String[] args) { /* The SAML Assertion Issuer Tool has a config file * that specifies a log file and a default credential * (among other things). The SAMLToolConfig * class is not common code, but is useful here to * bootstrap the config of this test application. */ SAMLToolConfig config = null; try { config = SAMLToolConfigLoader.getToolConfig(); } catch (GridShibToolConfigException e) { String msg = "Unable to get the config"; } catch (GridShibToolConfigException e) { String msg = "Unable to get the config"; logger.error(msg, e); System.err.println(msg); System.exit(1); } /* There should be one command line argument, use it as * a combined cert and key file name, and try to open * the certificate from that. */ if ( args.length > 2 || args.length < 1 ) { System.err.println("usage: " + args[0] + " CredentialFile [ KeyFile ]"); System.exit(1); } String certPath = args[0]; String keyPath = (args.length > 1) ? args[1] : args[0]; //System.out.println("cert path: " + certPath); //System.out.println("key path: " + certPath); logger.debug("cert path: " + certPath); logger.debug("key path: " + keyPath); File certFile = new File(certPath); File keyFile = new File(keyPath); try { credential = GSIUtil.getCredential(certFile, keyFile); } catch (GlobusCredentialException e2) { String msg = "Unable to obtain credential from " + args[0] + ((args.length > 1) ? args[1] : ""); logger.error(msg, e2); System.err.println(msg); System.exit(1); } //System.out.println("Issuing credential (chain length " + // credential.getCertificateChain().length + "):"); //System.out.println(credential.toString()); initializeEntityMapping(); consumeX509BoundSAML(); } /** * Initializes an entity mapping, that is, a mapping of SAML * issuers to X.509 issuers. For each bound SAML assertion, * add a map from the issuer of the SAML assertion to the * issuer of the containing certificate (which may be an * EEC or a proxy certificate). * * Note: In practice, a consumer depends on a static entity * map configured into the runtime environment. In GridShib * for GT, for example, entity mappings are stored in the * file system and loaded when the runtime initializes. */ private static void initializeEntityMapping() { /* Determine the proxy issuer, which is the EEC subject, * by definition. */ X509Certificate[] certs = credential.getCertificateChain(); X509Certificate eec = null; try { logger.debug("Getting EEC..."); eec = CertUtil.getEEC(certs); } catch (CertificateException e) { String msg = "Unable to determine if certificate is an " + "impersonation proxy"; logger.error(msg, e); System.err.println(msg); System.exit(1); } if (eec == null) { String msg = "Unable to find end entity certificate"; logger.error(msg); System.err.println(msg); System.exit(1); } logger.debug("End entity cert: " + eec.toString()); X500Principal proxyIssuer = eec.getSubjectX500Principal(); String proxyIssuerDN = proxyIssuer.getName(X500Principal.RFC2253); logger.debug("Proxy issuer: " + proxyIssuerDN); /* Traverse the certificate chain and add an entity * map for each bound SAML assertion. */ for (int i = 0; i < certs.length; i++) { logger.debug("Processing certificate " + i + ": " + certs[i].toString()); String entityID = null; SAMLSubjectAssertion assertion = null; try { assertion = SAMLUtil.getSAMLAssertion(certs[i]); } catch (IOException e) { String msg = "Unable to decode certificate extension"; logger.error(msg, e); System.err.println(msg); System.exit(1); } catch (SAMLException e) { String msg = "Unable to convert extension to SAMLAssertion"; logger.error(msg, e); System.err.println(msg); System.exit(1); } if (assertion == null) { logger.debug("Certificate " + i + " does not contain a SAML assertion"); } else { logger.debug("Bound SAML assertion: " + assertion.toString()); entityID = assertion.getIssuer(); } try { if (!CertUtil.isImpersonationProxy(certs[i])) { if (assertion != null) { assert (entityID != null); // map the SAML issuer to the certificate issuer: X500Principal certIssuer = certs[i].getIssuerX500Principal(); String dn = certIssuer.getName(X500Principal.RFC2253); logger.debug("Mapping SAML issuer to " + "certificate issuer: " + dn); entityMap.addMapping(entityID, dn); } logger.debug("All certificates processed"); break; } else { if (assertion != null) { assert (entityID != null); // map the SAML issuer to the proxy issuer: logger.debug("Mapping SAML issuer to " + "proxy issuer: " + proxyIssuerDN); entityMap.addMapping(entityID, proxyIssuerDN); } continue; } } catch (CertificateException e) { String msg = "Unable to determine if certificate is an " + "impersonation proxy"; logger.error(msg, e); System.err.println(msg); System.exit(1); } } GridShibEntityMapper.register(entityMap); } /** * Creates a security context from the SAML assertions bound * to the certificate chain. */ private static void consumeX509BoundSAML() { /* Get a security context for the subject and * add the certificate chain of the proxy to the * security context. */ Subject subject = new Subject(); SAMLSecurityContext secCtx = (SAMLSecurityContext)SecurityContextFactory.getInstance(subject); assert (secCtx != null); secCtx.addCertificateChain(credential.getCertificateChain()); try { SAMLUtil.consumeSAMLAssertions(subject); /* Now get the attributes from the certificate. */ BasicAttribute[] rawAttributes = secCtx.getRawAttributes(); assert (rawAttributes != null); //System.out.println("Found " + rawAttributes.length + " raw attribute(s)"); for (int i = 0; i < rawAttributes.length; i++) { String entityID = rawAttributes[i].getIssuer(); String name = rawAttributes[i].getName(); String[] values = rawAttributes[i].getValues(); //System.out.println("attribute #" + (i+1) + " entityID=" + entityID); //System.out.println("attribute #" + (i+1) + " name=" + name); //System.out.println("attribute #" + (i+1) + " values:"); for (int j = 0; j < values.length; j++) { String value = values[j]; //System.out.println("attribute #" + (i+1) + " " + name + " value #" + \ if (name.compareTo("urn:oid:1.3.6.1.4.1.5923.1.5.1.1") == 0) { value = value.replaceFirst("^http://", ""); value = value.replaceFirst("/$", ""); System.out.println( value ); } } } } catch (IOException e) { String msg = "Unable to decode extension"; logger.error(msg, e); System.err.println(msg); System.exit(1); } catch (SAMLException e) { String msg = "Unable to convert extension to SAMLAssertion"; logger.error(msg, e); System.err.println(msg); System.exit(1); } catch (CertificateException e) { String msg = "Unable to determine if certificate is an " + "impersonation proxy"; logger.error(msg, e); System.err.println(msg); System.exit(1); } //System.out.println("secCtx: " + secCtx.toString()); } }
SAMLToolConfig
To build and install our extraction program, do this:
cd /usr/local/gridshib-saml-tools-0_2_0 ant compile ant install
/usr/local/gridshib-saml-tools-0_2_0/bin/groups-is-member-of
#!/bin/sh ## the GridShib installation directory if [ ! -n "$GRIDSHIB_HOME" ] ; then echo "Error: GRIDSHIB_HOME is not defined." exit 1 fi $GRIDSHIB_HOME/bin/java org.globus.gridshib.security.GroupsIsMemberOf "$@"
echo MyProxy-password | myproxy-login --stdin_pass -s host-fqdn -l MyProxy-login-name -o certificate-file
MAEviz My Workspace
openssl x509 -noout -text -in certificate-file
Certificate: Data: Version: 3 (0x2) Serial Number: 206 (0xce) Signature Algorithm: sha1WithRSAEncryption Issuer: C=US, O=host-fqdn, OU=MAEviz, CN=MAEviz Simple CA Validity Not Before: Oct 29 07:32:22 2007 GMT Not After : Oct 29 19:37:22 2007 GMT Subject: C=US, O=host-fqdn, OU=MAEviz, CN=MyProxy-login-name Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public Key: (1024 bit) Modulus (1024 bit): 00:a6:8d:af:15:1c:d1:94:d3:47:fc:68:45:1d:6f: 07:0a:bc:e1:a5:7f:e6:eb:c7:d5:e2:55:f4:d3:3c: 45:1c:d3:14:1e:4c:15:3b:82:67:e9:28:32:4b:ed: f7:99:40:73:ea:1d:28:66:dd:9e:4c:dc:cf:eb:01: 80:b1:f2:90:f6:61:0e:74:34:6c:61:3b:ad:c6:c1: 59:67:b0:7d:72:57:d0:07:51:7c:e9:19:d2:b5:a7: c0:4d:46:48:df:d1:1b:51:c7:13:df:4d:ab:98:f5: 99:70:74:95:c2:a0:71:30:8a:56:15:96:c9:7b:ee: d1:c7:e0:ff:eb:b8:aa:eb:d5 Exponent: 65537 (0x10001) X509v3 extensions: 1.3.6.1.4.1.3536.1.1.1.10: <Assertion xmlns="urn:oasis:names:tc:SAML:1.0:assertion" \ xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion" \ xmlns:samlp="urn:oasis:names:tc:SAML:1.0:protocol" \ xmlns:xsd="http://www.w3.org/2001/XMLSchema" \ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" \ AssertionID="_57c94d23b4d16ca258d8f83438d8c0af" \ IssueInstant="2007-10-29T07:37:25.025Z" \ Issuer="CN=host/host-fqdn,OU=MAEviz,O=host-fqdn,C=US" MajorVersion="1" MinorVersion="1"><AttributeStatement><Subject><NameIdentifier \ Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"> \ MyProxy-login-name</NameIdentifier></Subject><Attribute \ AttributeName="urn:oid:2.5.4.6" \ AttributeNamespace="urn:mace:shibboleth:1.0:attributeNamespace:uri"> \ <AttributeValue xsi:type="xsd:normalizedString">US</AttributeValue></Attribute> \ <Attribute AttributeName="urn:oid:1.3.6.1.4.1.5923.1.5.1.1" \ AttributeNamespace="urn:mace:shibboleth:1.0:attributeNamespace:unspecified"> \ \ <AttributeValue xsi:type="xsd:normalizedString">http://MAEviz/</AttributeValue> \ <AttributeValue xsi:type="xsd:normalizedString">http://My Workspace/</AttributeValue></Attribute> \ </AttributeStatement></Assertion> Signature Algorithm: sha1WithRSAEncryption 1e:da:7b:fd:6c:a1:09:cd:0a:7d:33:e2:11:e9:f0:df:91:fc: b2:2f:a0:c9:8f:c1:2c:17:55:fe:d7:0e:ad:93:3d:b7:63:ce: 8b:da:63:ce:94:87:bd:cb:2c:9f:ea:a2:03:ed:0e:04:8a:b3: 07:5d:49:5a:db:46:47:a1:51:bf:14:b7:66:fb:ea:44:a7:44: 9c:3e:43:e1:37:a7:c4:d4:f7:85:b7:e5:04:fc:a2:86:29:dc: b6:d4:be:95:a2:bf:69:ef:43:e5:31:7d:c5:bc:75:2c:28:f5: 94:2e:a2:b9:a4:4d:75:de:31:46:62:a4:00:12:49:56:5f:93: 27:a2
/usr/local/gridshib-saml-tools-0_2_0/bin/groups-is-member-of certificate-file
myproxy-init -s host-fqdn -l MyProxy-login-name -o second-certificate-file