--- /dev/null
+/*
+ Copyright (c) 2002-10, Andrew McNab, University of Manchester
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, are permitted provided that the following
+ conditions are met:
+
+ o Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the following
+ disclaimer.
+ o Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+ ---------------------------------------------------------------
+ For more information about GridSite: http://www.gridsite.org/
+ ---------------------------------------------------------------
+*/
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <time.h>
+#include <stdarg.h>
+#include <dirent.h>
+#include <string.h>
+#include <pwd.h>
+#include <errno.h>
+#include <getopt.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifndef GRST_NO_OPENSSL
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+
+#include <openssl/rsa.h>
+#include <openssl/pem.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/bio.h>
+#include <openssl/des.h>
+#include <openssl/rand.h>
+#endif
+
+#include <canl.h>
+#include <canl_cred.h>
+
+#include "gridsite.h"
+
+#define GRST_KEYSIZE 512
+#define GRST_PROXYCACHE "/../proxycache/"
+#define GRST_MAX_CHAIN_LEN 9
+#define GRST_BACKDATE_SECONDS 300
+
+/// Compare X509 Distinguished Name strings
+int GRSTx509NameCmp(char *a, char *b)
+///
+/// This function attempts to do with string representations what
+/// would ideally be done with OIDs/values. In particular, we equate
+/// "/Email=" == "/emailAddress=" to deal with this important change
+/// between OpenSSL 0.9.6 and 0.9.7.
+/// Other than that, it is currently the same as ordinary strcasecmp(3)
+/// (for consistency with EDG/LCG/EGEE gridmapdir case insensitivity.)
+{
+ int ret;
+ char *aa, *bb, *p;
+
+ if ((a == NULL) || (b == NULL)) return 1; /* NULL never matches */
+
+ aa = strdup(a);
+ while ((p = strstr(aa, "/emailAddress=")) != NULL)
+ {
+ memmove(&p[6], &p[13], strlen(&p[13]) + 1);
+ p[1] = 'E';
+ }
+
+ bb = strdup(b);
+ while ((p = strstr(bb, "/emailAddress=")) != NULL)
+ {
+ memmove(&p[6], &p[13], strlen(&p[13]) + 1);
+ p[1] = 'E';
+ }
+
+ ret = strcasecmp(aa, bb);
+
+ free(aa);
+ free(bb);
+
+ return ret;
+}
+
+
+/// Check critical extensions
+int GRSTx509KnownCriticalExts(X509 *cert)
+///
+/// Returning GRST_RET_OK if all of extensions are known to us or
+/// OpenSSL; GRST_REF_FAILED otherwise.
+///
+/// Since this function relies on functionality (X509_supported_extension)
+/// introduced in 0.9.7, then we do nothing and report an error
+/// (GRST_RET_FAILED) if one of the associated defines
+/// (X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION) is absent.
+{
+ int i;
+ char s[80];
+ X509_EXTENSION *ex;
+
+#ifdef X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION
+ for (i = 0; i < X509_get_ext_count(cert); ++i)
+ {
+ ex = X509_get_ext(cert, i);
+
+ if (X509_EXTENSION_get_critical(ex) &&
+ !X509_supported_extension(ex))
+ {
+ OBJ_obj2txt(s, sizeof(s), X509_EXTENSION_get_object(ex), 1);
+
+ if ((strcmp(s, GRST_PROXYCERTINFO_OID) != 0) &&
+ (strcmp(s, GRST_PROXYCERTINFO_OLD_OID) != 0))
+ return GRST_RET_FAILED;
+ }
+ }
+
+ return GRST_RET_OK;
+#else
+ return GRST_RET_FAILED;
+#endif
+}
+
+/// Check if certificate can be used as a CA to sign standard X509 certs
+int GRSTx509IsCA(X509 *cert)
+///
+/// Return GRST_RET_OK if true; GRST_RET_FAILED if not.
+{
+ int idret, purpose_id;
+
+ purpose_id = X509_PURPOSE_get_by_sname("sslclient");
+
+ /* final argument to X509_check_purpose() is whether to check for CAness */
+
+ if (X509_check_purpose(cert, purpose_id + X509_PURPOSE_MIN, 1))
+ return GRST_RET_OK;
+ else return GRST_RET_FAILED;
+}
+
+int GRSTx509ChainFree(GRSTx509Chain *chain)
+{
+ GRSTx509Cert *grst_cert, *next_grst_cert;
+
+ if (chain == NULL) return GRST_RET_OK;
+
+ next_grst_cert = chain->firstcert;
+
+ while (next_grst_cert != NULL)
+ {
+ grst_cert = next_grst_cert;
+
+ if (grst_cert->issuer != NULL) free(grst_cert->issuer);
+ if (grst_cert->dn != NULL) free(grst_cert->dn);
+ if (grst_cert->value != NULL) free(grst_cert->value);
+ if (grst_cert->ocsp != NULL) free(grst_cert->ocsp);
+
+ next_grst_cert = grst_cert->next;
+ free(grst_cert);
+ }
+
+ free(chain);
+
+ return GRST_RET_OK;
+}
+
+/// Check a specific signature against a specific (VOMS) cert
+static int GRSTx509VerifySig(time_t *time1_time, time_t *time2_time,
+ unsigned char *txt, int txt_len,
+ unsigned char *sig, int sig_len,
+ X509 *cert, EVP_MD *md_type)
+///
+/// Returns GRST_RET_OK if signature is ok, other values if not.
+{
+ int ret;
+ EVP_PKEY *prvkey;
+ EVP_MD_CTX ctx;
+ time_t voms_service_time1, voms_service_time2;
+
+ prvkey = X509_extract_key(cert);
+ if (prvkey == NULL) return GRST_RET_FAILED;
+
+ OpenSSL_add_all_digests();
+#if OPENSSL_VERSION_NUMBER >= 0x0090701fL
+ EVP_MD_CTX_init(&ctx);
+ EVP_VerifyInit_ex(&ctx, md_type, NULL);
+#else
+ EVP_VerifyInit(&ctx, md_type);
+#endif
+
+ EVP_VerifyUpdate(&ctx, txt, txt_len);
+
+ ret = EVP_VerifyFinal(&ctx, sig, sig_len, prvkey);
+
+#if OPENSSL_VERSION_NUMBER >= 0x0090701fL
+ EVP_MD_CTX_cleanup(&ctx);
+#endif
+ EVP_PKEY_free(prvkey);
+
+ if (ret != 1) return GRST_RET_FAILED;
+
+ voms_service_time1 =
+ GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notBefore(cert)),0);
+ if (voms_service_time1 > *time1_time)
+ *time1_time = voms_service_time1;
+
+ voms_service_time2 =
+ GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notAfter(cert)),0);
+ if (voms_service_time2 < *time1_time)
+ *time2_time = voms_service_time2;
+
+ return GRST_RET_OK ; /* verified */
+}
+
+/// Check the signature of the VOMS attributes
+static int GRSTx509VerifyVomsSig(time_t *time1_time, time_t *time2_time,
+ unsigned char *asn1string,
+ struct GRSTasn1TagList taglist[],
+ int lasttag,
+ char *vomsdir, int acnumber)
+///
+/// Returns GRST_RET_OK if signature is ok, other values if not.
+{
+#define GRST_ASN1_COORDS_VOMS_DN "-1-1-%d-1-3-1-1-1-%%d-1-%%d"
+#define GRST_ASN1_COORDS_VOMS_INFO "-1-1-%d-1"
+#define GRST_ASN1_COORDS_VOMS_HASH "-1-1-%d-2-1"
+#define GRST_ASN1_COORDS_VOMS_SIG "-1-1-%d-3"
+ int ret, ihash, isig, iinfo;
+ char *certpath, *certpath2, acvomsdn[200], dn_coords[200],
+ info_coords[200], sig_coords[200], hash_coords[200],
+ *p;
+ unsigned char *q;
+ DIR *vomsDIR, *vomsDIR2;
+ struct dirent *vomsdirent, *vomsdirent2;
+ X509 *cert;
+ EVP_PKEY *prvkey;
+ FILE *fp;
+ EVP_MD_CTX ctx;
+ EVP_MD *md_type = NULL;
+ struct stat statbuf;
+ time_t voms_service_time1 = GRST_MAX_TIME_T, voms_service_time2 = 0,
+ tmp_time1, tmp_time2;
+ ASN1_OBJECT *hash_obj = NULL;
+
+ if ((vomsdir == NULL) || (vomsdir[0] == '\0')) return GRST_RET_FAILED;
+
+ snprintf(dn_coords, sizeof(dn_coords),
+ GRST_ASN1_COORDS_VOMS_DN, acnumber);
+
+ if (GRSTasn1GetX509Name(acvomsdn, sizeof(acvomsdn), dn_coords,
+ asn1string, taglist, lasttag) != GRST_RET_OK) return GRST_RET_FAILED;
+
+ snprintf(info_coords, sizeof(info_coords),
+ GRST_ASN1_COORDS_VOMS_INFO, acnumber);
+ iinfo = GRSTasn1SearchTaglist(taglist, lasttag, info_coords);
+
+ snprintf(hash_coords, sizeof(hash_coords),
+ GRST_ASN1_COORDS_VOMS_HASH, acnumber);
+ ihash = GRSTasn1SearchTaglist(taglist, lasttag, hash_coords);
+
+ snprintf(sig_coords, sizeof(sig_coords),
+ GRST_ASN1_COORDS_VOMS_SIG, acnumber);
+ isig = GRSTasn1SearchTaglist(taglist, lasttag, sig_coords);
+
+ if ((iinfo < 0) || (ihash < 0) || (isig < 0)) return GRST_RET_FAILED;
+
+ /* determine hash algorithm's type */
+
+ p = &asn1string[taglist[ihash].start];
+
+ d2i_ASN1_OBJECT(&hash_obj, &p,
+ taglist[ihash].length+taglist[ihash].headerlength);
+
+ md_type = EVP_get_digestbyname(OBJ_nid2sn(OBJ_obj2nid(hash_obj)));
+
+ if (md_type == NULL) return GRST_RET_FAILED;
+
+
+ vomsDIR = opendir(vomsdir);
+ if (vomsDIR == NULL) return GRST_RET_FAILED;
+
+ while ((vomsdirent = readdir(vomsDIR)) != NULL)
+ {
+ if (vomsdirent->d_name[0] == '.') continue;
+
+ asprintf(&certpath, "%s/%s", vomsdir, vomsdirent->d_name);
+ stat(certpath, &statbuf);
+
+ if (S_ISDIR(statbuf.st_mode))
+ {
+ vomsDIR2 = opendir(certpath);
+ GRSTerrorLog(GRST_LOG_DEBUG,
+ "Descend VOMS subdirectory %s", certpath);
+ free(certpath);
+
+ if (vomsDIR2 == NULL) continue;
+
+ while ((vomsdirent2 = readdir(vomsDIR2)) != NULL)
+ {
+ if (vomsdirent2->d_name[0] == '.') continue;
+
+ asprintf(&certpath2, "%s/%s/%s",
+ vomsdir, vomsdirent->d_name, vomsdirent2->d_name);
+
+ fp = fopen(certpath2, "r");
+ GRSTerrorLog(GRST_LOG_DEBUG,
+ "Examine VOMS cert %s", certpath2);
+ free(certpath2);
+ if (fp == NULL) continue;
+
+ cert = PEM_read_X509(fp, NULL, NULL, NULL);
+ fclose(fp);
+ if (cert == NULL) continue;
+
+ tmp_time1 = 0;
+ tmp_time2 = GRST_MAX_TIME_T;
+
+ if (GRSTx509VerifySig(&tmp_time1, &tmp_time2,
+ &asn1string[taglist[iinfo].start],
+ taglist[iinfo].length+taglist[iinfo].headerlength,
+ &asn1string[taglist[isig].start+
+ taglist[isig].headerlength+1],
+ taglist[isig].length - 1,
+ cert, md_type) == GRST_RET_OK)
+ {
+ GRSTerrorLog(GRST_LOG_DEBUG, "Matched VOMS cert file %s", vomsdirent2->d_name);
+
+ /* Store more permissive time ranges for now */
+
+ if (tmp_time1 < voms_service_time1)
+ voms_service_time1 = tmp_time1;
+
+ if (tmp_time2 > voms_service_time2)
+ voms_service_time2 = tmp_time2;
+ }
+
+ X509_free(cert);
+ }
+
+ closedir(vomsDIR2);
+ }
+ else
+ {
+ fp = fopen(certpath, "r");
+ GRSTerrorLog(GRST_LOG_DEBUG, "Examine VOMS cert %s", certpath);
+ free(certpath);
+ if (fp == NULL) continue;
+
+ cert = PEM_read_X509(fp, NULL, NULL, NULL);
+ fclose(fp);
+ if (cert == NULL) continue;
+
+ tmp_time1 = 0;
+ tmp_time2 = GRST_MAX_TIME_T;
+
+ if (GRSTx509VerifySig(&tmp_time1, &tmp_time2,
+ &asn1string[taglist[iinfo].start],
+ taglist[iinfo].length+taglist[iinfo].headerlength,
+ &asn1string[taglist[isig].start+
+ taglist[isig].headerlength+1],
+ taglist[isig].length - 1,
+ cert, md_type) == GRST_RET_OK)
+ {
+ GRSTerrorLog(GRST_LOG_DEBUG, "Matched VOMS cert file %s", vomsdirent->d_name);
+
+ /* Store more permissive time ranges for now */
+
+ if (tmp_time1 < voms_service_time1)
+ voms_service_time1 = tmp_time1;
+
+ if (tmp_time2 > voms_service_time2)
+ voms_service_time2 = tmp_time2;
+ }
+
+ X509_free(cert);
+ }
+ }
+
+ closedir(vomsDIR);
+
+ if ((voms_service_time1 == GRST_MAX_TIME_T) || (voms_service_time2 == 0))
+ return GRST_RET_FAILED;
+
+ /* now we tighten up the VOMS AC time range using the most permissive
+ pair of VOMS certificate ranges (in case of multiple, possibly
+ overlapping, matching certificates in the VOMS certs store) */
+
+ if (voms_service_time1 > *time1_time) *time1_time = voms_service_time1;
+
+ if (voms_service_time2 < *time2_time) *time2_time = voms_service_time2;
+
+ return GRST_RET_OK;
+}
+
+/// Check the signature of the VOMS attributes using the LSC file cert
+static int GRSTx509VerifyVomsSigCert(time_t *time1_time, time_t *time2_time,
+ unsigned char *asn1string,
+ struct GRSTasn1TagList taglist[],
+ int lasttag,
+ char *vomsdir, int acnumber,
+ int ivomscert,
+ char *capath,
+ char *acvomsdn,
+ char *voname)
+///
+/// Returns GRST_RET_OK if signature is ok, other values if not.
+{
+#define GRST_ASN1_COORDS_VOMS_DN "-1-1-%d-1-3-1-1-1-%%d-1-%%d"
+#define GRST_ASN1_COORDS_VOMS_INFO "-1-1-%d-1"
+#define GRST_ASN1_COORDS_VOMS_SIG "-1-1-%d-3"
+ int ret, isig, iinfo, chain_errors = GRST_RET_OK,
+ cadn_len, vomsdn_len, lsc_found = 0, ihash;
+ char *lscpath, dn_coords[200],
+ info_coords[200], sig_coords[200], *p, *cacertpath,
+ *vodir, *vomscert_cadn, *vomscert_vomsdn,
+ *lsc_cadn, *lsc_vomsdn;
+ unsigned char *q;
+ unsigned long issuerhash = 0;
+ DIR *voDIR;
+ struct dirent *vodirent;
+ X509 *cacert = NULL, *vomscert = NULL;
+ EVP_PKEY *prvkey;
+ FILE *fp;
+ EVP_MD_CTX ctx;
+ struct stat statbuf;
+ time_t tmp_time;
+ ASN1_OBJECT *hash_obj = NULL;
+ char coords[200];
+ EVP_MD *md_type = NULL;
+ time_t voms_service_time1, voms_service_time2;
+
+ if ((vomsdir == NULL) || (vomsdir[0] == '\0')) return GRST_RET_FAILED;
+
+ q = &asn1string[taglist[ivomscert].start + 12];
+
+ vomscert = d2i_X509(NULL, (const unsigned char **) &q,
+ taglist[ivomscert].length - 8);
+
+ if (vomscert == NULL)
+ {
+ GRSTerrorLog(GRST_LOG_DEBUG, "Failed to read included VOMS cert in GRSTx509VerifyVomsSigCert()");
+ return GRST_RET_FAILED;
+ }
+
+ GRSTerrorLog(GRST_LOG_DEBUG, "Found included VOMS cert in GRSTx509VerifyVomsSigCert()");
+
+ snprintf(coords, sizeof(coords), GRST_ASN1_COORDS_VOMS_INFO, acnumber);
+ iinfo = GRSTasn1SearchTaglist(taglist, lasttag, coords);
+
+ snprintf(coords, sizeof(coords), GRST_ASN1_COORDS_VOMS_HASH, acnumber);
+ ihash = GRSTasn1SearchTaglist(taglist, lasttag, coords);
+
+ snprintf(coords, sizeof(coords), GRST_ASN1_COORDS_VOMS_SIG, acnumber);
+ isig = GRSTasn1SearchTaglist(taglist, lasttag, coords);
+
+ if ((iinfo < 0) || (ihash < 0) || (isig < 0)) return GRST_RET_FAILED;
+
+ q = &asn1string[taglist[ihash].start];
+ d2i_ASN1_OBJECT(&hash_obj, &q,
+ taglist[ihash].length+taglist[ihash].headerlength);
+
+ md_type = EVP_get_digestbyname(OBJ_nid2sn(OBJ_obj2nid(hash_obj)));
+ if (md_type == NULL) return GRST_RET_FAILED;
+
+ /* check issuer CA certificate */
+
+ issuerhash = X509_NAME_hash(X509_get_issuer_name(vomscert));
+ asprintf(&cacertpath, "%s/%.8x.0", capath, issuerhash);
+
+ /* check voms cert DN matches DN from AC */
+
+ vomscert_vomsdn = X509_NAME_oneline(X509_get_subject_name(vomscert),NULL,0);
+
+ if (GRSTx509NameCmp(vomscert_vomsdn, acvomsdn) != 0)
+ {
+ free(vomscert_vomsdn);
+
+ GRSTerrorLog(GRST_LOG_DEBUG, "Included VOMS cert DN does not match AC issuer DN!");
+ return GRST_RET_FAILED;
+ }
+
+ free(vomscert_vomsdn);
+
+ GRSTerrorLog(GRST_LOG_DEBUG, "Look for CA root file %s", cacertpath);
+
+ fp = fopen(cacertpath, "r");
+ free(cacertpath);
+
+ if (fp == NULL) return GRST_RET_FAILED;
+ else
+ {
+ cacert = PEM_read_X509(fp, NULL, NULL, NULL);
+ fclose(fp);
+ if (cacert != NULL)
+ {
+ GRSTerrorLog(GRST_LOG_DEBUG, " Loaded CA root cert from file");
+ }
+ else
+ {
+ GRSTerrorLog(GRST_LOG_DEBUG, " Failed to load CA root cert file");
+ return GRST_RET_FAILED;
+ }
+ }
+
+ /* check times CA cert times, and reject if necessary */
+
+ tmp_time = GRSTasn1TimeToTimeT(
+ ASN1_STRING_data(X509_get_notBefore(cacert)), 0);
+ if (tmp_time > *time1_time) chain_errors |= GRST_CERT_BAD_TIME;
+
+ tmp_time = GRSTasn1TimeToTimeT(
+ ASN1_STRING_data(X509_get_notAfter(cacert)), 0);
+ if (tmp_time < *time2_time) chain_errors |= GRST_CERT_BAD_TIME;
+
+ /* check times VOMS cert times, and tighten if necessary */
+
+ tmp_time = GRSTasn1TimeToTimeT(
+ ASN1_STRING_data(X509_get_notBefore(vomscert)), 0);
+ if (tmp_time > *time1_time) chain_errors |= GRST_CERT_BAD_TIME;
+
+ tmp_time = GRSTasn1TimeToTimeT(
+ ASN1_STRING_data(X509_get_notAfter(vomscert)), 0);
+ if (tmp_time < *time2_time) chain_errors |= GRST_CERT_BAD_TIME;
+
+ ret = X509_check_issued(cacert, vomscert);
+ GRSTerrorLog(GRST_LOG_DEBUG, "X509_check_issued returns %d", ret);
+
+ vomscert_cadn = X509_NAME_oneline(X509_get_issuer_name(vomscert),NULL,0);
+
+
+ if (ret != X509_V_OK) return (chain_errors | GRST_CERT_BAD_SIG);
+
+ asprintf(&vodir, "%s/%s", vomsdir, voname);
+
+ voDIR = opendir(vodir);
+ if (voDIR == NULL) return GRST_RET_FAILED;
+
+ cadn_len = strlen(vomscert_cadn);
+ vomsdn_len = strlen(acvomsdn);
+
+ lsc_cadn = malloc(cadn_len + 2);
+ lsc_vomsdn = malloc(vomsdn_len + 2);
+
+ while (((vodirent = readdir(voDIR)) != NULL) && !lsc_found)
+ {
+ if (vodirent->d_name[0] == '.') continue;
+
+ if (strlen(vodirent->d_name) < 5) continue;
+
+ if (strcmp(&(vodirent->d_name[strlen(vodirent->d_name)-4]), ".lsc") != 0) continue;
+
+ asprintf(&lscpath, "%s/%s", vodir, vodirent->d_name);
+ stat(lscpath, &statbuf);
+
+ GRSTerrorLog(GRST_LOG_DEBUG, "Check LSC file %s for %s,%s",
+ lscpath, acvomsdn, vomscert_cadn);
+
+ if ((fp = fopen(lscpath, "r")) != NULL)
+ {
+ lsc_cadn[0] = '\0';
+ lsc_vomsdn[0] = '\0';
+
+ if ((fgets(lsc_vomsdn, vomsdn_len + 2, fp) != NULL)
+ && (fgets(lsc_cadn, cadn_len + 2, fp) != NULL))
+ {
+ if ((p = index(lsc_cadn, '\n')) != NULL) *p = '\0';
+
+ if ((p = index(lsc_vomsdn, '\n')) != NULL) *p = '\0';
+
+ if ((GRSTx509NameCmp(lsc_cadn, vomscert_cadn) == 0) &&
+ (GRSTx509NameCmp(lsc_vomsdn, acvomsdn) == 0))
+ {
+ GRSTerrorLog(GRST_LOG_DEBUG, "Matched LSC file %s", lscpath);
+ lsc_found = 1;
+ }
+ }
+
+ fclose(fp);
+ }
+
+ free(lscpath);
+ }
+
+ closedir(voDIR);
+ free(vodir);
+ free(vomscert_cadn);
+ free(lsc_cadn);
+ free(lsc_vomsdn);
+
+ if (!lsc_found) {
+ chain_errors |= GRST_CERT_BAD_SIG;
+ goto end;
+ }
+
+ ret = GRSTx509VerifySig(&voms_service_time1, &voms_service_time2,
+ &asn1string[taglist[iinfo].start],
+ taglist[iinfo].length+taglist[iinfo].headerlength,
+ &asn1string[taglist[isig].start+
+ taglist[isig].headerlength+1],
+ taglist[isig].length - 1,
+ vomscert, md_type);
+ if (ret != GRST_RET_OK) {
+ chain_errors |= GRST_CERT_BAD_SIG;
+ goto end;
+ }
+
+ if (voms_service_time1 > *time1_time) *time1_time = voms_service_time1;
+ if (voms_service_time2 < *time2_time) *time2_time = voms_service_time2;
+
+end:
+ if (cacert)
+ X509_free(cacert);
+ if (vomscert)
+ X509_free(vomscert);
+
+ return (chain_errors ? GRST_RET_FAILED : GRST_RET_OK);
+}
+
+/// Get the VOMS attributes in the given extension
+static int GRSTx509ChainVomsAdd(GRSTx509Cert **grst_cert,
+ time_t time1_time, time_t time2_time,
+ int delegation,
+ X509_EXTENSION *ex,
+ GRSTx509Cert *user_cert, char *vomsdir, char *capath)
+///
+/// Add any VOMS credentials found into the chain. Always returns GRST_RET_OK
+/// - even for invalid credentials, which are flagged in errors field
+{
+#define MAXTAG 500
+#define GRST_ASN1_COORDS_FQAN "-1-1-%d-1-7-1-2-1-2-%d"
+#define GRST_ASN1_COORDS_ISSUER_DN "-1-1-%d-1-2-1-1-1-1-%%d-1-%%d"
+#define GRST_ASN1_COORDS_ISSUER_SERIAL "-1-1-%d-1-2-1-2"
+#define GRST_ASN1_COORDS_VOMS_DN "-1-1-%d-1-3-1-1-1-%%d-1-%%d"
+#define GRST_ASN1_COORDS_TIME1 "-1-1-%d-1-6-1"
+#define GRST_ASN1_COORDS_TIME2 "-1-1-%d-1-6-2"
+#define GRST_ASN1_COORDS_VOMSCERT "-1-1-%d-1-8-%%d-%%d"
+
+ ASN1_OCTET_STRING *asn1data;
+ char *asn1string, acissuerdn[200], acvomsdn[200],
+ dn_coords[200], fqan_coords[200], time1_coords[200],
+ time2_coords[200], vomscert_coords[200], *voname = NULL,
+ serial_coords[200];
+ long asn1length;
+ int lasttag=-1, itag, i, j, acnumber = 1, chain_errors = 0,
+ ivomscert, tmp_chain_errors, ret;
+ char *acissuerserial = NULL;
+ struct GRSTasn1TagList taglist[MAXTAG+1];
+ time_t actime1 = 0, actime2 = 0, time_now,
+ tmp_time1, tmp_time2;
+ ASN1_INTEGER acissuerserialASN1;
+
+ asn1data = X509_EXTENSION_get_data(ex);
+ asn1string = ASN1_STRING_data(asn1data);
+ asn1length = ASN1_STRING_length(asn1data);
+
+ GRSTasn1ParseDump(NULL, asn1string, asn1length, taglist, MAXTAG, &lasttag);
+
+ for (acnumber = 1; ; ++acnumber) /* go through ACs one by one */
+ {
+ chain_errors = 0;
+
+ snprintf(dn_coords, sizeof(dn_coords), GRST_ASN1_COORDS_ISSUER_DN, acnumber);
+ if (GRSTasn1GetX509Name(acissuerdn, sizeof(acissuerdn), dn_coords,
+ asn1string, taglist, lasttag) != GRST_RET_OK)
+ break;
+
+ snprintf(dn_coords, sizeof(dn_coords), GRST_ASN1_COORDS_VOMS_DN, acnumber);
+ if (GRSTasn1GetX509Name(acvomsdn, sizeof(acvomsdn), dn_coords,
+ asn1string, taglist, lasttag) != GRST_RET_OK)
+ break;
+
+ if ((GRSTx509NameCmp(user_cert->dn, acissuerdn) != 0) && /* old */
+ (GRSTx509NameCmp(user_cert->issuer, acissuerdn) != 0)) /* new */
+ chain_errors |= GRST_CERT_BAD_CHAIN;
+
+ /* check serial numbers */
+
+ snprintf(serial_coords, sizeof(serial_coords),
+ GRST_ASN1_COORDS_ISSUER_SERIAL, acnumber);
+
+ itag = GRSTasn1SearchTaglist(taglist, lasttag, serial_coords);
+
+ if (itag > -1)
+ {
+ acissuerserialASN1.length = taglist[itag].length;
+ acissuerserialASN1.type = V_ASN1_INTEGER;
+ acissuerserialASN1.data = &asn1string[taglist[itag].start+taglist[itag].headerlength];
+
+ acissuerserial = i2s_ASN1_INTEGER(NULL, &acissuerserialASN1);
+/*
+ p = &asn1string[taglist[itag].start+taglist[itag].headerlength];
+
+ if (taglist[itag].length == 2)
+ acissuerserial = p[1] + p[0] * 0x100;
+ else if (taglist[itag].length == 3)
+ acissuerserial = p[2] + p[1] * 0x100 + p[0] * 0x10000;
+ else if (taglist[itag].length == 4)
+ acissuerserial = p[3] + p[2] * 0x100 + p[1] * 0x10000 +
+ p[0] * 0x1000000;
+*/
+ }
+
+ if (strcmp(acissuerserial, user_cert->serial) != 0)
+ chain_errors |= GRST_CERT_BAD_CHAIN;
+
+ /* get times */
+
+ snprintf(time1_coords, sizeof(time1_coords), GRST_ASN1_COORDS_TIME1, acnumber);
+ itag = GRSTasn1SearchTaglist(taglist, lasttag, time1_coords);
+
+ if (itag > -1) actime1 = GRSTasn1TimeToTimeT(
+ &asn1string[taglist[itag].start+
+ taglist[itag].headerlength],
+ taglist[itag].length);
+ else actime1 = 0;
+
+ snprintf(time2_coords, sizeof(time2_coords), GRST_ASN1_COORDS_TIME2, acnumber);
+ itag = GRSTasn1SearchTaglist(taglist, lasttag, time2_coords);
+
+ if (itag > -1) actime2 = GRSTasn1TimeToTimeT(
+ &asn1string[taglist[itag].start+
+ taglist[itag].headerlength],
+ taglist[itag].length);
+ else actime2 = 0;
+
+ if (actime1 > time1_time) time1_time = actime1;
+ if (actime2 < time2_time) time2_time = actime2;
+
+ time(&time_now);
+ if ((time1_time > time_now + 300) || (time2_time < time_now))
+ chain_errors |= GRST_CERT_BAD_TIME;
+
+ /* get first FQAN and use to get VO name */
+
+ snprintf(fqan_coords, sizeof(fqan_coords), GRST_ASN1_COORDS_FQAN, acnumber, 1);
+ itag = GRSTasn1SearchTaglist(taglist, lasttag, fqan_coords);
+
+ if ((itag > -1) &&
+ (asn1string[taglist[itag].start+taglist[itag].headerlength] == '/'))
+ {
+ for (j=1;
+ (asn1string[taglist[itag].start+taglist[itag].headerlength+j] != '/') &&
+ (j < taglist[itag].length);
+ ++j) ;
+
+ asprintf(&voname, "%.*s", j-1,
+ &(asn1string[taglist[itag].start+taglist[itag].headerlength+1]));
+ }
+
+ snprintf(vomscert_coords, sizeof(vomscert_coords),
+ GRST_ASN1_COORDS_VOMSCERT, acnumber);
+ ret = GRSTasn1FindField(GRST_VOMS_PK_CERT_LIST_OID, vomscert_coords, asn1string,
+ taglist, lasttag, &ivomscert);
+
+ /* try using internal VOMS issuer cert */
+ tmp_chain_errors = GRST_CERT_BAD_SIG;
+ tmp_time1 = time1_time;
+ tmp_time2 = time2_time;
+ if ((ivomscert > -1) &&
+ (voname != NULL) &&
+ (GRSTx509VerifyVomsSigCert(&tmp_time1, &tmp_time2,
+ asn1string, taglist, lasttag, vomsdir, acnumber,
+ ivomscert, capath, acvomsdn,
+ voname) == GRST_RET_OK))
+ {
+ tmp_chain_errors = 0;
+ time1_time = tmp_time1;
+ time2_time = tmp_time2;
+ }
+
+ if (voname != NULL)
+ {
+ free(voname);
+ voname = NULL;
+ }
+
+ if ((tmp_chain_errors != 0) &&
+ (GRSTx509VerifyVomsSig(&time1_time, &time2_time,
+ asn1string, taglist, lasttag, vomsdir, acnumber)
+ != GRST_RET_OK))
+ chain_errors |= GRST_CERT_BAD_SIG;
+
+ for (i=1; ; ++i) /* now go through FQANs */
+ {
+ snprintf(fqan_coords, sizeof(fqan_coords), GRST_ASN1_COORDS_FQAN, acnumber, i);
+ itag = GRSTasn1SearchTaglist(taglist, lasttag, fqan_coords);
+
+ if (itag > -1)
+ {
+ (*grst_cert)->next = malloc(sizeof(GRSTx509Cert));
+ *grst_cert = (*grst_cert)->next;
+ bzero(*grst_cert, sizeof(GRSTx509Cert));
+
+ (*grst_cert)->notbefore = time1_time;
+ (*grst_cert)->notafter = time2_time;
+ asprintf(&((*grst_cert)->value), "%.*s",
+ taglist[itag].length,
+ &asn1string[taglist[itag].start+
+ taglist[itag].headerlength]);
+
+ (*grst_cert)->errors = chain_errors; /* ie may be invalid */
+ (*grst_cert)->type = GRST_CERT_TYPE_VOMS;
+ (*grst_cert)->issuer = strdup(acvomsdn);
+ (*grst_cert)->dn = strdup(user_cert->dn);
+ (*grst_cert)->delegation = delegation;
+ }
+ else break;
+ }
+ }
+
+ return GRST_RET_OK;
+}
+
+/// Check certificate chain for GSI proxy acceptability.
+int GRSTx509ChainLoadCheck(GRSTx509Chain **chain,
+ STACK_OF(X509) *certstack, X509 *lastcert,
+ char *capath, char *vomsdir)
+///
+/// Returns GRST_RET_OK if valid; OpenSSL X509 errors otherwise.
+///
+/// The GridSite version handles old and new style Globus proxies, and
+/// proxies derived from user certificates issued with "X509v3 Basic
+/// Constraints: CA:FALSE" (eg UK e-Science CA)
+///
+/// TODO: we do not yet check ProxyCertInfo and ProxyCertPolicy extensions
+/// (although via GRSTx509KnownCriticalExts() we can accept them.)
+{
+ X509 *cert; /* Points to the current cert in the loop */
+ X509 *cacert = NULL; /* The CA root cert */
+ int depth = 0; /* Depth of cert chain */
+ int chain_errors = 0; /* records previous errors */
+ int first_non_ca; /* number of the EEC issued to user by CA */
+ size_t len,len2; /* Lengths of issuer and cert DN */
+ int IsCA; /* Holds whether cert is allowed to sign */
+ int prevIsCA; /* Holds whether previous cert in chain is
+ allowed to sign */
+ int prevIsLimited; /* previous cert was proxy and limited */
+ int i,j,ret; /* Iteration/temp variables */
+ char *proxy_part_DN; /* Pointer to end part of current-cert-in-chain
+ maybe eg "/CN=proxy" */
+ char s[80], *p;
+ char *cacertpath;
+ unsigned long subjecthash = 0; /* hash of the name of first cert */
+ unsigned long issuerhash = 0; /* hash of issuer name of first cert */
+ FILE *fp;
+ X509_EXTENSION *ex;
+ time_t now;
+ GRSTx509Cert *grst_cert, *new_grst_cert, *user_cert = NULL;
+
+ GRSTerrorLog(GRST_LOG_DEBUG, "GRSTx509ChainLoadCheck() starts");
+
+ time(&now);
+
+ first_non_ca = 0; /* set to something predictable if things fail */
+
+ /* Set necessary preliminary values */
+ IsCA = TRUE; /* =prevIsCA - start from a CA */
+ prevIsLimited = 0;
+
+ /* Get the client cert chain */
+ if (certstack != NULL)
+ depth = sk_X509_num(certstack); /* How deep is that chain? */
+
+ if ((depth == 0) && (lastcert == NULL))
+ {
+ *chain = NULL;
+ return GRST_RET_FAILED;
+ }
+
+ cert = sk_X509_value(certstack, depth - 1);
+ subjecthash = X509_NAME_hash(X509_get_subject_name(cert));
+ issuerhash = X509_NAME_hash(X509_get_issuer_name(cert));
+ asprintf(&cacertpath, "%s/%.8x.0", capath, issuerhash);
+
+ GRSTerrorLog(GRST_LOG_DEBUG, "Look for CA root file %s", cacertpath);
+
+ fp = fopen(cacertpath, "r");
+ free(cacertpath);
+
+ if (fp == NULL) chain_errors |= GRST_CERT_BAD_CHAIN;
+ else
+ {
+ cacert = PEM_read_X509(fp, NULL, NULL, NULL);
+ fclose(fp);
+ if (cacert != NULL)
+ GRSTerrorLog(GRST_LOG_DEBUG, " Loaded CA root cert from file");
+ else
+ GRSTerrorLog(GRST_LOG_DEBUG, " Failed to load CA root cert file");
+ }
+
+ *chain = malloc(sizeof(GRSTx509Chain));
+ bzero(*chain, sizeof(GRSTx509Chain));
+
+ /* Check the client chain */
+ for (i = depth - ((subjecthash == issuerhash) ? 1 : 0);
+ i >= ((lastcert == NULL) ? 0 : -1);
+ --i)
+ /* loop through client-presented chain starting at CA end */
+ {
+ GRSTerrorLog(GRST_LOG_DEBUG, "Process cert at depth %d in chain", i);
+
+ prevIsCA=IsCA;
+
+ new_grst_cert = malloc(sizeof(GRSTx509Cert));
+ bzero(new_grst_cert, sizeof(GRSTx509Cert));
+ new_grst_cert->errors = chain_errors;
+
+ if ((*chain)->firstcert == NULL)
+ {
+ GRSTerrorLog(GRST_LOG_DEBUG, "Initialise chain");
+ (*chain)->firstcert = new_grst_cert;
+ }
+ else grst_cert->next = new_grst_cert;
+
+ grst_cert = new_grst_cert;
+
+ /* Choose X509 certificate and point to it with 'cert' */
+ if (i < 0) cert = lastcert;
+ else if (i == depth)
+ cert = cacert; /* the self-signed CA from the store*/
+ else if ((i == depth - 1) && (subjecthash == issuerhash))
+ cert = cacert; /* ie claims to be a copy of a self-signed CA */
+ else cert = sk_X509_value(certstack, i);
+
+ if (cert != NULL)
+ {
+ if ((i == depth - 1) && (subjecthash != issuerhash))
+ {
+ /* if first cert does not claim to be a self-signed copy
+ of a CA root cert in the store, we check the signature */
+
+ if (cacert == NULL)
+ {
+ chain_errors |= GRST_CERT_BAD_CHAIN;
+ ret = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT;
+ }
+ else
+ {
+ ret = X509_check_issued(cacert, cert);
+
+ GRSTerrorLog(GRST_LOG_DEBUG,
+ "Cert sig check %d returns %d", i, ret);
+
+ if (ret != X509_V_OK)
+ new_grst_cert->errors |= GRST_CERT_BAD_SIG;
+ }
+ }
+ else if ((i == depth - 2) && (subjecthash == issuerhash))
+ {
+ /* first cert claimed to be a self-signed copy of a CA root
+ cert in the store, we check the signature of the second
+ cert, using OUR copy of the CA cert DIRECT from the store */
+
+ if (cacert == NULL)
+ {
+ chain_errors |= GRST_CERT_BAD_CHAIN;
+ ret = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT;
+ }
+ else
+ {
+ ret = X509_check_issued(cacert, cert);
+
+ GRSTerrorLog(GRST_LOG_DEBUG,
+ "Cert sig check %d returns %d", i, ret);
+
+ if (ret != X509_V_OK)
+ new_grst_cert->errors |= GRST_CERT_BAD_SIG;
+ }
+ }
+ else if (i < depth - 1)
+ {
+ /* otherwise a normal part of the chain: note that if the
+ first cert claims to be a self-signed copy of a CA root
+ cert in the store, we never use it for sig checking */
+
+ ret = X509_check_issued(sk_X509_value(certstack, i + 1), cert);
+
+ GRSTerrorLog(GRST_LOG_DEBUG,
+ "Cert sig check %d returns %d", i, ret);
+
+ if ((ret != X509_V_OK) &&
+ (ret != X509_V_ERR_KEYUSAGE_NO_CERTSIGN))
+ new_grst_cert->errors |= GRST_CERT_BAD_SIG;
+
+ /* NO_CERTSIGN can still be ok due to Proxy Certificates */
+ }
+
+ p = i2s_ASN1_INTEGER(NULL, X509_get_serialNumber(cert));
+ strncpy(new_grst_cert->serial, p, GRST_X509_SERIAL_DIGITS);
+ new_grst_cert->serial[GRST_X509_SERIAL_DIGITS] = '\0';
+ free(p);
+
+ new_grst_cert->notbefore = GRSTasn1TimeToTimeT(
+ ASN1_STRING_data(X509_get_notBefore(cert)), 0);
+ new_grst_cert->notafter = GRSTasn1TimeToTimeT(
+ ASN1_STRING_data(X509_get_notAfter(cert)), 0);
+
+ /* we check times and record if invalid */
+
+ if (now < new_grst_cert->notbefore)
+ new_grst_cert->errors |= GRST_CERT_BAD_TIME;
+
+ if (now > new_grst_cert->notafter)
+ new_grst_cert->errors |= GRST_CERT_BAD_TIME;
+
+ new_grst_cert->dn = X509_NAME_oneline(X509_get_subject_name(cert),NULL,0);
+ new_grst_cert->issuer = X509_NAME_oneline(X509_get_issuer_name(cert),NULL,0);
+ len = strlen(new_grst_cert->dn);
+ len2 = strlen(new_grst_cert->issuer);
+
+ /* always treat a first cert from the CA files as a
+ CA: this is really for lousy CAs that dont create
+ proper v3 root certificates */
+
+ if (i == depth) IsCA == TRUE;
+ else IsCA = (GRSTx509IsCA(cert) == GRST_RET_OK);
+
+ /* If any forebear certificate is not allowed to sign we must
+ assume all decendents are proxies and cannot sign either */
+ if (prevIsCA)
+ {
+ if (IsCA)
+ {
+ new_grst_cert->type = GRST_CERT_TYPE_CA;
+ }
+ else
+ {
+ new_grst_cert->type = GRST_CERT_TYPE_EEC;
+ first_non_ca = i;
+ user_cert = new_grst_cert;
+ new_grst_cert->delegation
+ = (lastcert == NULL) ? i : i + 1;
+ }
+ }
+ else
+ {
+ new_grst_cert->type = GRST_CERT_TYPE_PROXY;
+
+ IsCA = FALSE;
+ /* Force proxy check next iteration. Important because I can
+ sign any CA I create! */
+
+ new_grst_cert->delegation = (lastcert == NULL) ? i : i + 1;
+ }
+
+ if (!prevIsCA)
+ {
+ /* issuer didn't have CA status, so this is (at best) a proxy:
+ check for bad proxy extension*/
+
+ if (prevIsLimited) /* we reject proxies of limited proxies! */
+ {
+ new_grst_cert->errors |= GRST_CERT_BAD_CHAIN;
+ chain_errors |= GRST_CERT_BAD_CHAIN;
+ }
+
+ /* User not allowed to sign shortened DN */
+ if (len2 > len)
+ {
+ new_grst_cert->errors |= GRST_CERT_BAD_CHAIN;
+ chain_errors |= GRST_CERT_BAD_CHAIN;
+ }
+
+ /* Proxy subject must begin with issuer. */
+ if (strncmp(new_grst_cert->dn, new_grst_cert->issuer, len2) != 0)
+ {
+ new_grst_cert->errors |= GRST_CERT_BAD_CHAIN;
+ chain_errors |= GRST_CERT_BAD_CHAIN;
+ }
+
+ /* Set pointer to end of base DN in cert_DN */
+ proxy_part_DN = &(new_grst_cert->dn[len2]);
+
+ /* First attempt at support for Old and New style GSI
+ proxies: /CN=anything is ok for now */
+ if (strncmp(proxy_part_DN, "/CN=", 4) != 0)
+ {
+ new_grst_cert->errors |= GRST_CERT_BAD_CHAIN;
+ chain_errors |= GRST_CERT_BAD_CHAIN;
+ }
+
+ if (strncmp(proxy_part_DN, "/CN=limited proxy", 17) == 0)
+ prevIsLimited = 1; /* ready for next cert ... */
+
+ for (j=0; j < X509_get_ext_count(cert); ++j)
+ {
+ ex = X509_get_ext(cert, j);
+ OBJ_obj2txt(s,sizeof(s),X509_EXTENSION_get_object(ex),1);
+
+ if (strcmp(s, GRST_VOMS_OID) == 0) /* a VOMS extension */
+ {
+ GRSTx509ChainVomsAdd(&grst_cert,
+ new_grst_cert->notbefore,
+ new_grst_cert->notafter,
+ (lastcert == NULL) ? i : i+1,
+ ex,
+ user_cert,
+ vomsdir,
+ capath);
+ }
+ }
+ }
+ }
+
+
+ } /* end of for loop */
+
+ if (cacert != NULL) X509_free(cacert);
+
+ return GRST_RET_OK;
+}
+
+/// Check certificate chain for GSI proxy acceptability.
+int GRSTx509CheckChain(int *first_non_ca, X509_STORE_CTX *ctx)
+///
+/// Returns X509_V_OK/GRST_RET_OK if valid; OpenSSL X509 errors otherwise.
+///
+/// Inspired by GSIcheck written by Mike Jones, SVE, Manchester Computing,
+/// The University of Manchester.
+///
+/// The GridSite version handles old and new style Globus proxies, and
+/// proxies derived from user certificates issued with "X509v3 Basic
+/// Constraints: CA:FALSE" (eg UK e-Science CA)
+///
+/// We do not check chain links between certs here: this is done by
+/// GRST_check_issued/X509_check_issued in mod_ssl's ssl_engine_init.c
+///
+/// TODO: we do not yet check ProxyCertInfo and ProxyCertPolicy extensions
+/// (although via GRSTx509KnownCriticalExts() we can accept them.)
+{
+ STACK_OF(X509) *certstack; /* Points to the client's cert chain */
+ X509 *cert; /* Points to the client's cert */
+ int depth; /* Depth of cert chain */
+ size_t len,len2; /* Lengths of issuer and cert DN */
+ int IsCA; /* Holds whether cert is allowed to sign */
+ int prevIsCA; /* Holds whether previous cert in chain is
+ allowed to sign */
+ int prevIsLimited; /* previous cert was proxy and limited */
+ int i,j; /* Iteration variables */
+ char *cert_DN; /* Pointer to current-certificate-in-chain's
+ DN */
+ char *issuer_DN; /* Pointer to
+ issuer-of-current-cert-in-chain's DN */
+ char *proxy_part_DN; /* Pointer to end part of current-cert-in-chain
+ maybe eg "/CN=proxy" */
+ time_t now;
+
+ time(&now);
+
+ *first_non_ca = 0; /* set to something predictable if things fail */
+
+ /* Check for context */
+ if (!ctx) return X509_V_ERR_INVALID_CA;
+ /* Can't GSI-verify if there is no context. Here and throughout this
+ function we report all errors as X509_V_ERR_INVALID_CA. */
+
+ /* Set necessary preliminary values */
+ IsCA = TRUE; /* =prevIsCA - start from a CA */
+ prevIsLimited = 0;
+
+ /* Get the client cert chain */
+ certstack = X509_STORE_CTX_get_chain(ctx); /* Get the client's chain */
+ depth = sk_X509_num(certstack); /* How deep is that chain? */
+
+ /* Check the client chain */
+ for (i=depth-1; i >= 0; --i)
+ /* loop through client-presented chain starting at CA end */
+ {
+ prevIsCA=IsCA;
+
+ /* Check for X509 certificate and point to it with 'cert' */
+ if (cert = sk_X509_value(certstack, i))
+ {
+ /* we check times and reject immediately if invalid */
+
+ if (now <
+ GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notBefore(cert)),0))
+ return X509_V_ERR_INVALID_CA;
+
+ if (now >
+ GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notAfter(cert)),0))
+ return X509_V_ERR_INVALID_CA;
+
+ /* If any forebear certificate is not allowed to sign we must
+ assume all decendents are proxies and cannot sign either */
+ if (prevIsCA)
+ {
+ /* always treat the first cert (from the CA files) as a CA */
+ if (i == depth-1) IsCA = TRUE;
+ /* check if this cert is valid CA for signing certs */
+ else IsCA = (GRSTx509IsCA(cert) == GRST_RET_OK);
+
+ if (!IsCA) *first_non_ca = i;
+ }
+ else
+ {
+ IsCA = FALSE;
+ /* Force proxy check next iteration. Important because I can
+ sign any CA I create! */
+ }
+
+ cert_DN = X509_NAME_oneline(X509_get_subject_name(cert),NULL,0);
+ issuer_DN = X509_NAME_oneline(X509_get_issuer_name(cert),NULL,0);
+ len = strlen(cert_DN);
+ len2 = strlen(issuer_DN);
+
+ /* issuer didn't have CA status, so this is (at best) a proxy:
+ check for bad proxy extension*/
+
+ if (!prevIsCA)
+ {
+ if (prevIsLimited) /* we reject proxies of limited proxies! */
+ return X509_V_ERR_INVALID_CA;
+
+ /* User not allowed to sign shortened DN */
+ if (len2 > len) return X509_V_ERR_INVALID_CA;
+
+ /* Proxy subject must begin with issuer. */
+ if (strncmp(cert_DN, issuer_DN, len2) != 0)
+ return X509_V_ERR_INVALID_CA;
+
+ /* Set pointer to end of base DN in cert_DN */
+ proxy_part_DN = &cert_DN[len2];
+
+ /* First attempt at support for Old and New style GSI
+ proxies: /CN=anything is ok for now */
+ if (strncmp(proxy_part_DN, "/CN=", 4) != 0)
+ return X509_V_ERR_INVALID_CA;
+
+ if ((strncmp(proxy_part_DN, "/CN=limited proxy", 17) == 0) &&
+ (i > 0)) prevIsLimited = 1; /* ready for next cert ... */
+ }
+ }
+ }
+
+ /* Check cert whose private key is being used by client. If previous in
+ chain is not allowed to be a CA then need to check this final cert for
+ valid proxy-icity too */
+ if (!prevIsCA)
+ {
+ if (prevIsLimited) return X509_V_ERR_INVALID_CA;
+ /* we do not accept proxies signed by limited proxies */
+
+ if (cert = sk_X509_value(certstack, 0))
+ {
+ /* Load DN & length of DN and either its issuer or the
+ first-bad-issuer-in-chain */
+ cert_DN = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0);
+ issuer_DN = X509_NAME_oneline(X509_get_issuer_name(cert), NULL, 0);
+ len = strlen(cert_DN);
+ len2 = strlen(issuer_DN);
+
+ /* issuer didn't have CA status, check for bad proxy extension */
+
+ if (len2 > len) return X509_V_ERR_INVALID_CA;
+ /* User not allowed to sign shortened DN */
+
+ if (strncmp(cert_DN, issuer_DN, len2) != 0)
+ return X509_V_ERR_INVALID_CA;
+ /* Proxy subject must begin with issuer. */
+
+ proxy_part_DN = &cert_DN[len2];
+ /* Set pointer to end of DN base in cert_DN */
+
+ /* Remander of subject must be either "/CN=proxy" or
+ "/CN=limited proxy" (or /CN=XYZ for New style GSI) */
+
+ /* First attempt at support for Old and New style GSI
+ proxies: /CN=anything is ok for now. */
+ if (strncmp(proxy_part_DN, "/CN=", 4) != 0)
+ return X509_V_ERR_INVALID_CA;
+ }
+ }
+
+ return X509_V_OK; /* this is also GRST_RET_OK, of course - by choice */
+}
+
+/// Example VerifyCallback routine
+int GRSTx509VerifyCallback (int ok, X509_STORE_CTX *ctx)
+{
+ int errnum = X509_STORE_CTX_get_error(ctx);
+ int errdepth = X509_STORE_CTX_get_error_depth(ctx);
+ int first_non_ca;
+
+#ifndef X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION
+#define X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION 34
+#endif
+
+ if (errnum == X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION)
+ {
+ if (GRSTx509KnownCriticalExts(X509_STORE_CTX_get_current_cert(ctx))
+ == GRST_RET_OK)
+ {
+ ok = TRUE;
+ errnum = X509_V_OK;
+ X509_STORE_CTX_set_error(ctx, errnum);
+ }
+ }
+ else if ((errdepth == 0) &&
+ (errnum == X509_V_OK) &&
+ (GRSTx509CheckChain(&first_non_ca, ctx) != X509_V_OK)) ok = FALSE;
+
+
+ return ok;
+
+// check this
+
+// if (ok) return GRST_RET_OK;
+// else return GRST_RET_FAILED;
+}
+
+/// Get the VOMS attributes in the given extension
+int GRSTx509ParseVomsExt(int *lastcred, int maxcreds, size_t credlen,
+ char *creds, time_t time1_time, time_t time2_time,
+ X509_EXTENSION *ex,
+ char *ucuserdn, char *ucissuerdn, char *ucserial,
+ char *vomsdir)
+///
+/// Puts any VOMS credentials found into the Compact Creds string array
+/// starting at *creds. Always returns GRST_RET_OK - even for invalid
+/// credentials, which are just ignored.
+{
+#define MAXTAG 500
+#define GRST_ASN1_COORDS_FQAN "-1-1-%d-1-7-1-2-1-2-%d"
+#define GRST_ASN1_COORDS_ISSUER_DN "-1-1-%d-1-2-1-1-1-1-%%d-1-%%d"
+#define GRST_ASN1_COORDS_TIME1 "-1-1-%d-1-6-1"
+#define GRST_ASN1_COORDS_TIME2 "-1-1-%d-1-6-2"
+ ASN1_OCTET_STRING *asn1data;
+ char *asn1string, acissuerdn[200], acvomsdn[200],
+ dn_coords[200], fqan_coords[200], time1_coords[200],
+ time2_coords[200], serial_coords[200];
+ unsigned char *p;
+ long asn1length;
+ int lasttag=-1, itag, i, acnumber = 1;
+ char *acissuerserial = NULL;
+ struct GRSTasn1TagList taglist[MAXTAG+1];
+ time_t actime1, actime2, time_now;
+ ASN1_INTEGER acissuerserialASN1;
+
+ asn1data = X509_EXTENSION_get_data(ex);
+ asn1string = ASN1_STRING_data(asn1data);
+ asn1length = ASN1_STRING_length(asn1data);
+
+ GRSTasn1ParseDump(NULL, asn1string, asn1length, taglist, MAXTAG, &lasttag);
+
+ for (acnumber = 1; ; ++acnumber) /* go through ACs one by one */
+ {
+ /* check names */
+
+ snprintf(dn_coords, sizeof(dn_coords), GRST_ASN1_COORDS_ISSUER_DN, acnumber);
+ if (GRSTasn1GetX509Name(acissuerdn, sizeof(acissuerdn), dn_coords,
+ asn1string, taglist, lasttag) != GRST_RET_OK) break;
+
+ if ((GRSTx509NameCmp(ucuserdn, acissuerdn) != 0) && /* old */
+ (GRSTx509NameCmp(ucissuerdn, acissuerdn) != 0)) /* new */
+ continue;
+
+ /* check serial numbers */
+
+ snprintf(serial_coords, sizeof(serial_coords),
+ GRST_ASN1_COORDS_ISSUER_SERIAL, acnumber);
+
+ itag = GRSTasn1SearchTaglist(taglist, lasttag, serial_coords);
+
+ if (itag > -1)
+ {
+ acissuerserialASN1.length = taglist[itag].length;
+ acissuerserialASN1.type = V_ASN1_INTEGER;
+ acissuerserialASN1.data = &asn1string[taglist[itag].start+taglist[itag].headerlength];
+
+ acissuerserial = i2s_ASN1_INTEGER(NULL, &acissuerserialASN1);
+/*
+ p = &asn1string[taglist[itag].start+taglist[itag].headerlength];
+
+ if (taglist[itag].length == 2)
+ acissuerserial = p[1] + p[0] * 0x100;
+ else if (taglist[itag].length == 3)
+ acissuerserial = p[2] + p[1] * 0x100 + p[0] * 0x10000;
+ else if (taglist[itag].length == 4)
+ acissuerserial = p[3] + p[2] * 0x100 + p[1] * 0x10000 +
+ p[0] * 0x1000000;
+*/
+ }
+
+ if (strcmp(acissuerserial, ucserial) != 0) continue;
+
+ if (GRSTx509VerifyVomsSig(&time1_time, &time2_time,
+ asn1string, taglist, lasttag, vomsdir, acnumber)
+ != GRST_RET_OK) continue;
+
+ snprintf(time1_coords, sizeof(time1_coords), GRST_ASN1_COORDS_TIME1, acnumber);
+ itag = GRSTasn1SearchTaglist(taglist, lasttag, time1_coords);
+ actime1 = GRSTasn1TimeToTimeT(&asn1string[taglist[itag].start+
+ taglist[itag].headerlength],
+ taglist[itag].length);
+ if (actime1 > time1_time) time1_time = actime1;
+
+ snprintf(time2_coords, sizeof(time2_coords), GRST_ASN1_COORDS_TIME2, acnumber);
+ itag = GRSTasn1SearchTaglist(taglist, lasttag, time2_coords);
+ actime2 = GRSTasn1TimeToTimeT(&asn1string[taglist[itag].start+
+ taglist[itag].headerlength],
+ taglist[itag].length);
+ if (actime2 < time2_time) time2_time = actime2;
+
+ time(&time_now);
+ if ((time1_time > time_now + 300) || (time2_time < time_now))
+ continue; /* expiration isnt invalidity ...? */
+
+ for (i=1; ; ++i)
+ {
+ snprintf(fqan_coords, sizeof(fqan_coords), GRST_ASN1_COORDS_FQAN, acnumber, i);
+ itag = GRSTasn1SearchTaglist(taglist, lasttag, fqan_coords);
+
+ if (itag > -1)
+ {
+ if (*lastcred < maxcreds - 1)
+ {
+ ++(*lastcred);
+ snprintf(&creds[*lastcred * (credlen + 1)], credlen+1,
+ "VOMS %010lu %010lu 0 %.*s",
+ time1_time, time2_time,
+ taglist[itag].length,
+ &asn1string[taglist[itag].start+
+ taglist[itag].headerlength]);
+ }
+ }
+ else break;
+ }
+ }
+
+ return GRST_RET_OK;
+}
+
+/// Get the VOMS attributes in the extensions to the given cert stack
+int GRSTx509GetVomsCreds(int *lastcred, int maxcreds, size_t credlen,
+ char *creds, X509 *usercert, STACK_OF(X509) *certstack,
+ char *vomsdir)
+///
+/// Puts any VOMS credentials found into the Compact Creds string array
+/// starting at *creds. Always returns GRST_RET_OK.
+{
+ int i, j;
+ char s[80], *ucserial;
+ unsigned char *ucuser, *ucissuer;
+ X509_EXTENSION *ex;
+ ASN1_STRING *asn1str;
+ X509 *cert;
+ time_t time1_time = 0, time2_time = 0, uctime1_time, uctime2_time;
+
+ uctime1_time =
+ GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notBefore(usercert)),0);
+ uctime2_time =
+ GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notAfter(usercert)),0);
+ ucuser =
+ X509_NAME_oneline(X509_get_subject_name(usercert), NULL, 0);
+ ucissuer =
+ X509_NAME_oneline(X509_get_issuer_name(usercert), NULL, 0);
+ ucserial = i2s_ASN1_INTEGER(NULL, X509_get_serialNumber(usercert));
+
+ for (j=sk_X509_num(certstack)-1; j >= 0; --j)
+ {
+ cert = sk_X509_value(certstack, j);
+
+ time1_time =
+ GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notBefore(cert)),0);
+ uctime1_time = (time1_time > uctime1_time) ? time1_time:uctime1_time;
+
+ time2_time =
+ GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notAfter(cert)),0);
+ uctime2_time = (time2_time < uctime2_time) ? time2_time:uctime2_time;
+
+ for (i=0; i < X509_get_ext_count(cert); ++i)
+ {
+ ex = X509_get_ext(cert, i);
+ OBJ_obj2txt(s, sizeof(s), X509_EXTENSION_get_object(ex), 1);
+
+ if (strcmp(s, GRST_VOMS_OID) == 0) /* a VOMS extension */
+ {
+ GRSTx509ParseVomsExt(lastcred, maxcreds, credlen, creds,
+ uctime1_time, uctime2_time,
+ ex, ucuser, ucissuer, ucserial,
+ vomsdir);
+ }
+ }
+ }
+
+ return GRST_RET_OK;
+}
+
+/// Turn a Compact Cred line into a GRSTgaclCred object
+GRSTgaclCred *GRSTx509CompactToCred(char *grst_cred)
+///
+/// Returns pointer to created GRSTgaclCred or NULL or failure.
+{
+ int delegation;
+ char *p, *encoded;
+ time_t now, notbefore, notafter;
+ GRSTgaclCred *cred = NULL;
+
+ time(&now);
+
+ if (grst_cred == NULL) return NULL; /* just in case */
+
+ if (strncmp(grst_cred, "X509USER ", 9) == 0)
+ {
+ if ((sscanf(grst_cred, "X509USER %lu %lu %d",
+ ¬before, ¬after, &delegation) == 3)
+ && (now >= notbefore)
+ && (now <= notafter)
+ && (p = index(grst_cred, ' '))
+ && (p = index(++p, ' '))
+ && (p = index(++p, ' '))
+ && (p = index(++p, ' ')))
+ {
+ encoded = GRSThttpUrlMildencode(&p[1]);
+ cred = GRSTgaclCredCreate("dn:", encoded);
+ free(encoded);
+ GRSTgaclCredSetDelegation(cred, delegation);
+ }
+
+ return cred;
+ }
+
+ if (strncmp(grst_cred, "VOMS ", 5) == 0)
+ {
+ if ((sscanf(grst_cred, "VOMS %lu %lu %d",
+ ¬before, ¬after, &delegation) == 3)
+ && (now >= notbefore)
+ && (now <= notafter)
+ && (p = index(grst_cred, ' '))
+ && (p = index(++p, ' '))
+ && (p = index(++p, ' '))
+ && (p = index(++p, ' ')))
+ {
+ /* include /VO/group/subgroup/Role=role/Capability=cap */
+
+ if (p[1] != '/') return NULL; /* must begin with / */
+
+ encoded = GRSThttpUrlMildencode(&p[1]);
+ cred = GRSTgaclCredCreate("fqan:", encoded);
+ free(encoded);
+ GRSTgaclCredSetDelegation(cred, delegation);
+ }
+
+ return cred;
+ }
+
+ return NULL; /* dont recognise this credential type */
+}
+
+/// Get the credentials in an X509 cert/GSI proxy, including any VOMS
+int GRSTx509CompactCreds(int *lastcred, int maxcreds, size_t credlen,
+ char *creds, STACK_OF(X509) *certstack, char *vomsdir,
+ X509 *peercert)
+///
+/// Credentials are placed in Compact Creds string array at *creds.
+///
+/// Function returns GRST_RET_OK on success, or GRST_RET_FAILED if
+/// some inconsistency found in certificate.
+{
+ int i, j, delegation = 0;
+ char credtemp[credlen+1];
+ X509 *cert, *usercert = NULL, *gsiproxycert = NULL;
+
+ *lastcred = -1;
+
+ for (i = sk_X509_num(certstack) - 1; i >= 0; --i)
+ {
+ cert = sk_X509_value(certstack, i);
+
+ if (usercert != NULL)
+ { /* found a (GSI proxy) cert after the user cert */
+ gsiproxycert = cert;
+ ++delegation;
+ }
+
+ if ((usercert == NULL) &&
+ (i < sk_X509_num(certstack) - 1) &&
+ (GRSTx509IsCA(cert) != GRST_RET_OK)) usercert = cert;
+ /* found the 1st non-CA cert */
+ }
+
+ if (peercert != NULL)
+ {
+ if (usercert != NULL) /* found a (GSI proxy) cert after user cert */
+ {
+ gsiproxycert = peercert;
+ ++delegation;
+ }
+
+ if ((usercert == NULL) &&
+ (GRSTx509IsCA(peercert) != GRST_RET_OK)) usercert = peercert;
+ /* found the 1st non-CA cert */
+ }
+
+ if ((usercert == NULL) /* if no usercert ("EEC"), we're not interested */
+ ||
+ (snprintf(credtemp, credlen+1, "X509USER %010lu %010lu %d %s",
+ GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notBefore(usercert)),0),
+ GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notAfter(usercert)),0),
+ delegation,
+ X509_NAME_oneline(X509_get_subject_name(usercert), NULL, 0)) >= credlen+1)
+ ||
+ (*lastcred >= maxcreds-1))
+ {
+ *lastcred = -1; /* just in case the caller looks at it */
+ return GRST_RET_FAILED; /* tell caller that things didn't work out */
+ }
+
+ ++(*lastcred);
+ strcpy(&creds[*lastcred * (credlen + 1)], credtemp);
+
+ if ((gsiproxycert != NULL)
+ &&
+ (snprintf(credtemp, credlen+1, "GSIPROXY %010lu %010lu %d %s",
+ GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notBefore(gsiproxycert)),0),
+ GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notAfter(gsiproxycert)),0),
+ delegation,
+ X509_NAME_oneline(X509_get_subject_name(gsiproxycert), NULL, 0)) < credlen+1)
+ &&
+ (*lastcred < maxcreds-1))
+ {
+ ++(*lastcred);
+ strcpy(&creds[*lastcred * (credlen + 1)], credtemp);
+
+ GRSTx509GetVomsCreds(lastcred, maxcreds, credlen, creds,
+ usercert, certstack, vomsdir);
+
+ }
+
+ return GRST_RET_OK;
+}
+
+/// Find proxy file name of the current user
+char *GRSTx509FindProxyFileName(void)
+///
+/// Return a string with the proxy file name or NULL if not present.
+/// This function does not check if the proxy has expired.
+{
+ char *p;
+
+ p = getenv("X509_USER_PROXY");
+
+ if (p != NULL) return strdup(p);
+
+ p = malloc(sizeof("/tmp/x509up_uXYYYXXXYYY"));
+
+ sprintf(p, "/tmp/x509up_u%d", getuid());
+
+ return p;
+}
+
+static void mpcerror(FILE *debugfp, char *msg)
+{
+ if (debugfp != NULL)
+ {
+ fputs(msg, debugfp);
+ ERR_print_errors_fp(debugfp);
+ }
+}
+
+/// Make a GSI Proxy chain from a request, certificate and private key
+int GRSTx509MakeProxyCert(char **proxychain, FILE *debugfp,
+ char *reqtxt, char *cert, char *key, int minutes)
+///
+/// The proxy chain is returned in *proxychain. If debugfp is non-NULL,
+/// errors are output to that file pointer. The proxy will expired in
+/// the given number of minutes starting from the current time.
+{
+ char *ptr, *certchain, s[41];
+ static unsigned char pci_str[] = { 0x30, 0x0c, 0x30, 0x0a, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x15, 0x01, 0 },
+ kyu_str[] = { 0x03, 0x02, 0x03,
+ X509v3_KU_DIGITAL_SIGNATURE |
+ X509v3_KU_KEY_ENCIPHERMENT |
+ X509v3_KU_KEY_AGREEMENT,
+ 0 };
+ int i, ncerts, any_rfc_proxies = 0;
+ long serial = 1234, ptrlen;
+ EVP_PKEY *pkey, *CApkey;
+ const EVP_MD *digest;
+ X509 *certs[GRST_MAX_CHAIN_LEN];
+ X509_REQ *req;
+ X509_NAME *name, *CAsubject, *newsubject;
+ X509_NAME_ENTRY *ent;
+ ASN1_OBJECT *pci_obj = NULL, *kyu_obj;
+ ASN1_OCTET_STRING *pci_oct, *kyu_oct;
+ X509_EXTENSION *pci_ex, *kyu_ex;
+ FILE *fp;
+ BIO *reqmem, *certmem;
+ time_t notAfter;
+
+ /* read in the request */
+ reqmem = BIO_new(BIO_s_mem());
+ BIO_puts(reqmem, reqtxt);
+
+ if (!(req = PEM_read_bio_X509_REQ(reqmem, NULL, NULL, NULL)))
+ {
+ mpcerror(debugfp,
+ "GRSTx509MakeProxyCert(): error reading request from BIO memory\n");
+ BIO_free(reqmem);
+ return GRST_RET_FAILED;
+ }
+
+ BIO_free(reqmem);
+
+ /* verify signature on the request */
+ if (!(pkey = X509_REQ_get_pubkey(req)))
+ {
+ mpcerror(debugfp,
+ "GRSTx509MakeProxyCert(): error getting public key from request\n");
+
+ X509_REQ_free(req);
+ return GRST_RET_FAILED;
+ }
+
+ if (X509_REQ_verify(req, pkey) != 1)
+ {
+ mpcerror(debugfp,
+ "GRSTx509MakeProxyCert(): error verifying signature on certificate\n");
+
+ X509_REQ_free(req);
+ return GRST_RET_FAILED;
+ }
+
+ /* read in the signing certificate */
+ if (!(fp = fopen(cert, "r")))
+ {
+ mpcerror(debugfp,
+ "GRSTx509MakeProxyCert(): error opening signing certificate file\n");
+
+ X509_REQ_free(req);
+ return GRST_RET_FAILED;
+ }
+
+ for (ncerts = 1; ncerts < GRST_MAX_CHAIN_LEN; ++ncerts)
+ if ((certs[ncerts] = PEM_read_X509(fp, NULL, NULL, NULL)) == NULL) break;
+
+ if (ncerts == 1) /* zeroth cert with be new proxy cert */
+ {
+ mpcerror(debugfp,
+ "GRSTx509MakeProxyCert(): error reading signing certificate file\n");
+
+ X509_REQ_free(req);
+ return GRST_RET_FAILED;
+ }
+
+ fclose(fp);
+
+ CAsubject = X509_get_subject_name(certs[1]);
+
+ /* read in the CA private key */
+ if (!(fp = fopen(key, "r")))
+ {
+ mpcerror(debugfp,
+ "GRSTx509MakeProxyCert(): error reading signing private key file\n");
+
+ X509_REQ_free(req);
+ return GRST_RET_FAILED;
+ }
+
+ if (!(CApkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL)))
+ {
+ mpcerror(debugfp,
+ "GRSTx509MakeProxyCert(): error reading signing private key in file\n");
+
+ X509_REQ_free(req);
+ return GRST_RET_FAILED;
+ }
+
+ fclose(fp);
+
+ /* get subject name */
+ if (!(name = X509_REQ_get_subject_name(req)))
+ {
+ mpcerror(debugfp,
+ "GRSTx509MakeProxyCert(): error getting subject name from request\n");
+
+ X509_REQ_free(req);
+ return GRST_RET_FAILED;
+ }
+
+ /* create new certificate */
+ if (!(certs[0] = X509_new()))
+ {
+ mpcerror(debugfp,
+ "GRSTx509MakeProxyCert(): error creating X509 object\n");
+
+ X509_REQ_free(req);
+ return GRST_RET_FAILED;
+ }
+
+ /* set version number for the certificate (X509v3) and the serial number
+
+ We now use 2 = v3 for the GSI proxy, rather than the old Globus
+ behaviour of 3 = v4. See Savannah Bug #53721 */
+
+ if (X509_set_version(certs[0], 2L) != 1)
+ {
+ mpcerror(debugfp,
+ "GRSTx509MakeProxyCert(): error setting certificate version\n");
+
+ X509_REQ_free(req);
+ return GRST_RET_FAILED;
+ }
+
+ ASN1_INTEGER_set(X509_get_serialNumber(certs[0]), (long) time(NULL));
+
+ if (!(name = X509_get_subject_name(certs[1])))
+ {
+ mpcerror(debugfp,
+ "GRSTx509MakeProxyCert(): error getting subject name from CA certificate\n");
+
+ X509_REQ_free(req);
+ return GRST_RET_FAILED;
+ }
+
+ if (X509_set_issuer_name(certs[0], name) != 1)
+ {
+ mpcerror(debugfp,
+ "GRSTx509MakeProxyCert(): error setting issuer name of certificate\n");
+
+ X509_REQ_free(req);
+ return GRST_RET_FAILED;
+ }
+
+ /* set public key in the certificate */
+ if (X509_set_pubkey(certs[0], pkey) != 1)
+ {
+ mpcerror(debugfp,
+ "GRSTx509MakeProxyCert(): error setting public key of the certificate\n");
+
+ X509_REQ_free(req);
+ return GRST_RET_FAILED;
+ }
+
+ /* set duration for the certificate */
+ if (!(X509_gmtime_adj(X509_get_notBefore(certs[0]), -GRST_BACKDATE_SECONDS)))
+ {
+ mpcerror(debugfp,
+ "GRSTx509MakeProxyCert(): error setting beginning time of the certificate\n");
+
+ X509_REQ_free(req);
+ return GRST_RET_FAILED;
+ }
+
+ if (!(X509_gmtime_adj(X509_get_notAfter(certs[0]), 60 * minutes)))
+ {
+ mpcerror(debugfp,
+ "GRSTx509MakeProxyCert(): error setting ending time of the certificate\n");
+
+ X509_REQ_free(req);
+ return GRST_RET_FAILED;
+ }
+
+ /* go through chain making sure this proxy is not longer lived */
+
+ pci_obj = OBJ_txt2obj(GRST_PROXYCERTINFO_OID, 0);
+
+ notAfter =
+ GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notAfter(certs[0])), 0);
+
+ for (i=1; i < ncerts; ++i)
+ {
+ if (notAfter >
+ GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notAfter(certs[i])),
+ 0))
+ {
+ notAfter =
+ GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notAfter(certs[i])),
+ 0);
+
+ ASN1_UTCTIME_set(X509_get_notAfter(certs[0]), notAfter);
+ }
+
+ if (X509_get_ext_by_OBJ(certs[i], pci_obj, -1) > 0)
+ any_rfc_proxies = 1;
+ }
+
+ /* if any earlier proxies are RFC 3820, then new proxy must be
+ an RFC 3820 proxy too with the required extensions */
+ if (any_rfc_proxies)
+ {
+ /* key usage */
+ kyu_obj = OBJ_txt2obj(GRST_KEYUSAGE_OID, 0);
+ kyu_ex = X509_EXTENSION_new();
+
+ X509_EXTENSION_set_object(kyu_ex, kyu_obj);
+ ASN1_OBJECT_free(kyu_obj);
+ X509_EXTENSION_set_critical(kyu_ex, 1);
+
+ kyu_oct = ASN1_OCTET_STRING_new();
+ ASN1_OCTET_STRING_set(kyu_oct, kyu_str, strlen(kyu_str));
+ X509_EXTENSION_set_data(kyu_ex, kyu_oct);
+ ASN1_OCTET_STRING_free(kyu_oct);
+
+ X509_add_ext(certs[0], kyu_ex, -1);
+ X509_EXTENSION_free(kyu_ex);
+
+ /* proxy certificate info */
+ pci_ex = X509_EXTENSION_new();
+
+ X509_EXTENSION_set_object(pci_ex, pci_obj);
+ X509_EXTENSION_set_critical(pci_ex, 1);
+
+ pci_oct = ASN1_OCTET_STRING_new();
+ ASN1_OCTET_STRING_set(pci_oct, pci_str, strlen(pci_str));
+ X509_EXTENSION_set_data(pci_ex, pci_oct);
+ ASN1_OCTET_STRING_free(pci_oct);
+
+ X509_add_ext(certs[0], pci_ex, -1);
+ X509_EXTENSION_free(pci_ex);
+ }
+ ASN1_OBJECT_free(pci_obj);
+
+ /* set issuer and subject name of the cert from the req and the CA */
+
+ if (any_rfc_proxies) /* user CN=number rather than CN=proxy */
+ {
+ snprintf(s, sizeof(s), "%ld", (long) time(NULL));
+ ent = X509_NAME_ENTRY_create_by_NID(NULL, OBJ_txt2nid("commonName"),
+ MBSTRING_ASC, s, -1);
+ }
+ else ent = X509_NAME_ENTRY_create_by_NID(NULL, OBJ_txt2nid("commonName"),
+ MBSTRING_ASC, "proxy", -1);
+
+ newsubject = X509_NAME_dup(CAsubject);
+
+ X509_NAME_add_entry(newsubject, ent, -1, 0);
+
+ if (X509_set_subject_name(certs[0], newsubject) != 1)
+ {
+ mpcerror(debugfp,
+ "GRSTx509MakeProxyCert(): error setting subject name of certificate\n");
+
+ X509_REQ_free(req);
+ return GRST_RET_FAILED;
+ }
+
+ X509_NAME_free(newsubject);
+ X509_NAME_ENTRY_free(ent);
+
+ /* sign the certificate with the signing private key */
+ if (EVP_PKEY_type(CApkey->type) == EVP_PKEY_RSA)
+ digest = EVP_md5();
+ else
+ {
+ mpcerror(debugfp,
+ "GRSTx509MakeProxyCert(): error checking signing private key for a valid digest\n");
+
+ X509_REQ_free(req);
+ return GRST_RET_FAILED;
+ }
+
+ if (!(X509_sign(certs[0], CApkey, digest)))
+ {
+ mpcerror(debugfp,
+ "GRSTx509MakeProxyCert(): error signing certificate\n");
+
+ X509_REQ_free(req);
+ return GRST_RET_FAILED;
+ }
+
+ /* store the completed certificate chain */
+
+ certchain = strdup("");
+
+ for (i=0; i < ncerts; ++i)
+ {
+ certmem = BIO_new(BIO_s_mem());
+
+ if (PEM_write_bio_X509(certmem, certs[i]) != 1)
+ {
+ mpcerror(debugfp,
+ "GRSTx509MakeProxyCert(): error writing certificate to memory BIO\n");
+
+ X509_REQ_free(req);
+ return GRST_RET_FAILED;
+ }
+
+ ptrlen = BIO_get_mem_data(certmem, &ptr);
+
+ certchain = realloc(certchain, strlen(certchain) + ptrlen + 1);
+
+ strncat(certchain, ptr, ptrlen);
+
+ BIO_free(certmem);
+ X509_free(certs[i]);
+ }
+
+ EVP_PKEY_free(pkey);
+ EVP_PKEY_free(CApkey);
+ X509_REQ_free(req);
+
+ *proxychain = certchain;
+ return GRST_RET_OK;
+}
+
+/// Find a proxy file in the proxy cache
+char *GRSTx509CachedProxyFind(char *proxydir, char *delegation_id,
+ char *user_dn)
+///
+/// Returns the full path and file name of proxy file associated
+/// with given delegation ID and user DN.
+///
+/// Return a pointer to a malloc'd string with the full path of the
+/// proxy file corresponding to the given delegation_id, or NULL
+/// if not found.
+{
+ char *user_dn_enc, *proxyfile;
+ struct stat statbuf;
+
+ user_dn_enc = GRSThttpUrlEncode(user_dn);
+
+ asprintf(&proxyfile, "%s/%s/%s/userproxy.pem",
+ proxydir, user_dn_enc, delegation_id);
+
+ free(user_dn_enc);
+
+ if ((stat(proxyfile, &statbuf) != 0) || !S_ISREG(statbuf.st_mode))
+ {
+ free(proxyfile);
+ return NULL;
+ }
+
+ return proxyfile;
+}
+
+/// Find a temporary proxy private key file in the proxy cache
+char *GRSTx509CachedProxyKeyFind(char *proxydir, char *delegation_id,
+ char *user_dn)
+///
+/// Returns the full path and file name of the private key file associated
+/// with given delegation ID and user DN.
+///
+/// Return a pointer to a malloc'd string with the full path of the
+/// private proxy key corresponding to the given delegation_id, or NULL
+/// if not found.
+{
+ char *user_dn_enc, *prvkeyfile;
+ struct stat statbuf;
+
+ user_dn_enc = GRSThttpUrlEncode(user_dn);
+
+ asprintf(&prvkeyfile, "%s/cache/%s/%s/userkey.pem",
+ proxydir, user_dn_enc, delegation_id);
+
+ free(user_dn_enc);
+
+ if ((stat(prvkeyfile, &statbuf) != 0) || !S_ISREG(statbuf.st_mode))
+ {
+ free(prvkeyfile);
+ return NULL;
+ }
+
+ return prvkeyfile;
+}
+
+static void mkdir_printf(mode_t mode, char *fmt, ...)
+{
+ int ret;
+ char *path;
+ va_list ap;
+
+ va_start(ap, fmt);
+ vasprintf(&path, fmt, ap);
+ va_end(ap);
+
+ ret = mkdir(path, mode);
+
+ free(path);
+}
+
+/// Create a X.509 request for a GSI proxy and its private key
+int GRSTx509CreateProxyRequest(char **reqtxt, char **keytxt, char *ocspurl)
+///
+/// Returns GRST_RET_OK on success, non-zero otherwise. Request string
+/// and private key are PEM encoded strings
+{
+ int i;
+ char *ptr;
+ size_t ptrlen;
+ RSA *keypair;
+ X509_NAME *subject;
+ X509_NAME_ENTRY *ent;
+ EVP_PKEY *pkey;
+ X509_REQ *certreq;
+ BIO *reqmem, *keymem;
+ const EVP_MD *digest;
+ struct stat statbuf;
+
+ /* create key pair and put it in a PEM string */
+
+ if ((keypair = RSA_generate_key(GRST_KEYSIZE, 65537, NULL, NULL)) == NULL)
+ return 1;
+
+ keymem = BIO_new(BIO_s_mem());
+ if (!PEM_write_bio_RSAPrivateKey(keymem, keypair, NULL, NULL, 0, NULL, NULL))
+ {
+ BIO_free(keymem);
+ return 3;
+ }
+
+ ptrlen = BIO_get_mem_data(keymem, &ptr);
+
+ *keytxt = malloc(ptrlen + 1);
+ memcpy(*keytxt, ptr, ptrlen);
+ (*keytxt)[ptrlen] = '\0';
+
+ BIO_free(keymem);
+
+ /* now create the certificate request */
+
+ certreq = X509_REQ_new();
+
+ OpenSSL_add_all_algorithms();
+
+ pkey = EVP_PKEY_new();
+ EVP_PKEY_assign_RSA(pkey, keypair);
+
+ X509_REQ_set_pubkey(certreq, pkey);
+
+ subject = X509_NAME_new();
+ ent = X509_NAME_ENTRY_create_by_NID(NULL, OBJ_txt2nid("organizationName"),
+ MBSTRING_ASC, "Dummy", -1);
+ X509_NAME_add_entry (subject, ent, -1, 0);
+ X509_REQ_set_subject_name (certreq, subject);
+
+ digest = EVP_md5();
+ X509_REQ_sign(certreq, pkey, digest);
+
+ reqmem = BIO_new(BIO_s_mem());
+ PEM_write_bio_X509_REQ(reqmem, certreq);
+ ptrlen = BIO_get_mem_data(reqmem, &ptr);
+
+ *reqtxt = malloc(ptrlen + 1);
+ memcpy(*reqtxt, ptr, ptrlen);
+ (*reqtxt)[ptrlen] = '\0';
+
+ BIO_free(reqmem);
+
+ X509_REQ_free(certreq);
+
+ return 0;
+}
+
+/// Make and store a X.509 request for a GSI proxy
+int GRSTx509MakeProxyRequest(char **reqtxt, char *proxydir,
+ char *delegation_id, char *user_dn)
+///
+/// Returns GRST_RET_OK on success, non-zero otherwise. Request string
+/// is PEM encoded, and the key is stored in the temporary cache under
+/// proxydir
+{
+ int i = 0;
+ char *docroot = NULL, *prvkeyfile = NULL, *ptr = NULL, *user_dn_enc = NULL;
+ size_t ptrlen = 0;
+ FILE *fp = NULL;
+ RSA *keypair = NULL;
+ BIO *reqmem = NULL;
+ canl_ctx c_ctx = NULL;
+ canl_cred proxy_bob = NULL;
+ X509_REQ *req = NULL;
+ int retval = GRST_RET_OK;
+
+ if (strcmp(user_dn, "cache") == 0)
+ return GRST_RET_FAILED;
+
+ user_dn_enc = GRSThttpUrlEncode(user_dn);
+
+ /* create directories if necessary */
+
+ mkdir_printf(S_IRUSR | S_IWUSR | S_IXUSR,
+ "%s/cache", proxydir);
+ mkdir_printf(S_IRUSR | S_IWUSR | S_IXUSR,
+ "%s/cache/%s", proxydir, user_dn_enc);
+ mkdir_printf(S_IRUSR | S_IWUSR | S_IXUSR,
+ "%s/cache/%s/%s", proxydir, user_dn_enc, delegation_id);
+
+ /* make the new proxy private key */
+
+ asprintf(&prvkeyfile, "%s/cache/%s/%s/userkey.pem",
+ proxydir, user_dn_enc, delegation_id);
+
+ if (prvkeyfile == NULL){
+ free(user_dn_enc);
+ return GRST_RET_FAILED;
+ }
+
+ /*Make new canl_ctx, TODO MP how to initialize it only once?*/
+ c_ctx = canl_create_ctx();
+ if (c_ctx == NULL) {
+ free(prvkeyfile);
+ return 10; /* TODO MP we can use caNl error codes now */
+ }
+
+ ret = canl_cred_new(c_ctx, &proxy_bob);
+ if (ret){
+ retval = 11;
+ goto end;
+ }
+
+ /*use caNl to generate X509 request*/
+ ret = canl_cred_new_req(c_ctx, proxy_bob, GRST_KEYSIZE);
+ if (ret) {
+ retval = 12;
+ goto end;
+ }
+
+ ret = canl_cred_save_req(c_ctx, proxy_bob, &req);
+ if (ret) {
+ retval = 13;
+ goto end;
+ }
+
+ if ((fp = fopen(prvkeyfile, "w")) == NULL)
+ {
+ retval = 14;
+ goto end;
+ }
+
+ /*Save private key in cache*/
+ chmod(prvkeyfile, S_IRUSR | S_IWUSR);
+ free(prvkeyfile);
+ prvkeyfile = NULL;
+ free(user_dn_enc);
+ user_dn_enc = NULL;
+
+ keypair = canl_cred_get_keypair();
+ if (!keypair) {
+ retval = 15;
+ goto end;
+ }
+
+ if (!PEM_write_RSAPrivateKey(fp, keypair, NULL, NULL, 0, NULL, NULL)) {
+ retval = 3;
+ goto end;
+ }
+
+ if (fclose(fp) != 0){
+ retval = 4;
+ goto end;
+ }
+
+ /* TODO MP ask. "Dummy" vs "proxy" in sslutils.c from Voms
+ ent=X509_NAME_ENTRY_create_by_NID(NULL, OBJ_txt2nid("organizationName"),
+ MBSTRING_ASC, "Dummy", -1);
+ TODO MD5 vs SHA1 defaults.
+ */
+
+ /*Convert request into string*/
+ reqmem = BIO_new(BIO_s_mem());
+ PEM_write_bio_X509_REQ(reqmem, req);
+ ptrlen = BIO_get_mem_data(reqmem, &ptr);
+
+ *reqtxt = malloc(ptrlen + 1);
+ memcpy(*reqtxt, ptr, ptrlen);
+ (*reqtxt)[ptrlen] = '\0';
+end:
+ if (proxy_bob)
+ canl_cred_free(ctx, proxy_bob);
+ if (reqmem)
+ BIO_free(reqmem);
+ if (req)
+ X509_REQ_free(req);
+ if (prvkeyfile)
+ free(prvkeyfile);
+ if (user_dn_enc)
+ free(user_dn_enc);
+ if (c_ctx)
+ canl_free_ctx(c_ctx);
+
+ return retval;
+}
+
+/// Destroy stored GSI proxy files
+int GRSTx509ProxyDestroy(char *proxydir, char *delegation_id, char *user_dn)
+///
+/// Returns GRST_RET_OK on success, non-zero otherwise.
+/// (Including GRST_RET_NO_SUCH_FILE if the private key or cert chain
+/// were not found.)
+{
+ int ret = GRST_RET_OK;
+ char *docroot, *filename, *user_dn_enc;
+
+ if (strcmp(user_dn, "cache") == 0) return GRST_RET_FAILED;
+
+ user_dn_enc = GRSThttpUrlEncode(user_dn);
+
+ /* proxy file */
+
+ asprintf(&filename, "%s/%s/%s/userproxy.pem",
+ proxydir, user_dn_enc, delegation_id);
+
+ if (filename == NULL)
+ {
+ free(user_dn_enc);
+ return GRST_RET_FAILED;
+ }
+
+ if (unlink(filename) != 0) ret = GRST_RET_NO_SUCH_FILE;
+ free(filename);
+
+ /* voms file */
+
+ asprintf(&filename, "%s/%s/%s/voms.attributes",
+ proxydir, user_dn_enc, delegation_id);
+
+ if (filename == NULL)
+ {
+ free(user_dn_enc);
+ return GRST_RET_FAILED;
+ }
+
+ unlink(filename);
+ free(filename);
+
+ return ret;
+}
+
+/// Get start and finish validity times of stored GSI proxy file
+int GRSTx509ProxyGetTimes(char *proxydir, char *delegation_id, char *user_dn,
+ time_t *start, time_t *finish)
+///
+/// Returns GRST_RET_OK on success, non-zero otherwise.
+/// (Including GRST_RET_NO_SUCH_FILE if the cert chain was not found.)
+{
+ char *docroot, *filename, *user_dn_enc;
+ FILE *fp;
+ X509 *cert;
+
+ if (strcmp(user_dn, "cache") == 0) return GRST_RET_FAILED;
+
+ user_dn_enc = GRSThttpUrlEncode(user_dn);
+
+ asprintf(&filename, "%s/%s/%s/userproxy.pem",
+ proxydir, user_dn_enc, delegation_id);
+
+ free(user_dn_enc);
+
+ if (filename == NULL) return GRST_RET_FAILED;
+
+ fp = fopen(filename, "r");
+ free(filename);
+
+ if (fp == NULL) return GRST_RET_NO_SUCH_FILE;
+
+ cert = PEM_read_X509(fp, NULL, NULL, NULL); /* first cert is X.509 PC */
+
+ fclose(fp);
+
+ *start = GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notBefore(cert)),0);
+ *finish = GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notAfter(cert)),0);
+
+ X509_free(cert);
+
+ return GRST_RET_OK;
+}
+
+/// Create a stack of X509 certificate from a PEM-encoded string
+int GRSTx509StringToChain(STACK_OF(X509) **certstack, char *certstring)
+///
+/// Creates a dynamically allocated stack of X509 certificate objects
+/// by walking through the PEM-encoded X509 certificates.
+///
+/// Returns GRST_RET_OK on success, non-zero otherwise.
+{
+ STACK_OF(X509_INFO) *sk=NULL;
+ BIO *certbio;
+ X509_INFO *xi;
+
+ *certstack = sk_X509_new_null();
+ if (*certstack == NULL) return GRST_RET_FAILED;
+
+ certbio = BIO_new_mem_buf(certstring, -1);
+
+ if (!(sk=PEM_X509_INFO_read_bio(certbio, NULL, NULL, NULL)))
+ {
+ BIO_free(certbio);
+ sk_X509_INFO_free(sk);
+ sk_X509_free(*certstack);
+ return GRST_RET_FAILED;
+ }
+
+ while (sk_X509_INFO_num(sk))
+ {
+ xi=sk_X509_INFO_shift(sk);
+ if (xi->x509 != NULL)
+ {
+ sk_X509_push(*certstack, xi->x509);
+ xi->x509=NULL;
+ }
+ X509_INFO_free(xi);
+ }
+
+ if (!sk_X509_num(*certstack))
+ {
+ BIO_free(certbio);
+ sk_X509_INFO_free(sk);
+ sk_X509_free(*certstack);
+ return GRST_RET_FAILED;
+ }
+
+ BIO_free(certbio);
+ sk_X509_INFO_free(sk);
+
+ return GRST_RET_OK;
+}
+
+/// Returns a Delegation ID based on hash of GRST_CRED_0, ...
+char *GRSTx509MakeDelegationID(void)
+///
+/// Returns a malloc'd string with Delegation ID made by SHA1-hashing the
+/// values of the compact credentials exported by mod_gridsite
+{
+ unsigned char hash_delegation_id[EVP_MAX_MD_SIZE];
+ int size_needed = 0, i, delegation_id_len;
+ char cred_name[14], *cred_value, *delegation_id;
+ const EVP_MD *m;
+ EVP_MD_CTX ctx;
+
+ OpenSSL_add_all_digests();
+
+ m = EVP_sha1();
+ if (m == NULL) return NULL;
+
+ EVP_DigestInit(&ctx, m);
+
+ for (i=0; i <= 999; ++i)
+ {
+ snprintf(cred_name, sizeof(cred_name), "GRST_CRED_%d", i);
+ if ((cred_value = getenv(cred_name)) == NULL) break;
+
+ EVP_DigestUpdate(&ctx, cred_value, strlen(cred_value));
+ }
+
+ EVP_DigestFinal(&ctx, hash_delegation_id, &delegation_id_len);
+
+ delegation_id = malloc(17);
+
+ for (i=0; i <=7; ++i)
+ sprintf(&delegation_id[i*2], "%02x", hash_delegation_id[i]);
+
+ delegation_id[16] = '\0';
+
+ return delegation_id;
+}
+
+#if 0
+/// Return the short file name for the given delegation_id and user_dn
+char *GRSTx509MakeProxyFileName(char *delegation_id,
+ STACK_OF(X509) *certstack)
+///
+/// Returns a malloc'd string with the short file name (no paths) that
+/// derived from the hashed delegation_id and user_dn
+///
+/// File name is SHA1_HASH(DelegationID)+"-"+SHA1_HASH(DN) where DN
+/// is DER encoded version of user_dn with any trailing CN=proxy removed
+/// Hashes are the most significant 8 bytes, in lowercase hexadecimal.
+{
+ int i, depth, prevIsCA = 1, IsCA, hash_name_len, delegation_id_len,
+ der_name_len;
+ unsigned char *der_name, *buf, hash_name[EVP_MAX_MD_SIZE],
+ hash_delegation_id[EVP_MAX_MD_SIZE],
+ filename[34];
+ X509_NAME *subject_name;
+ X509 *cert;
+ const EVP_MD *m;
+ EVP_MD_CTX ctx;
+
+ depth = sk_X509_num(certstack);
+
+ for (i=depth-1; i >= 0; --i)
+ /* loop through the proxy chain starting at CA end */
+ {
+ if (cert = sk_X509_value(certstack, i))
+ {
+ IsCA = (GRSTx509IsCA(cert) == GRST_RET_OK);
+
+ if (prevIsCA && !IsCA) /* the full certificate of the user */
+ {
+ break;
+ }
+ }
+ }
+
+ if (i < 0) return NULL; /* not found: something wrong with the chain */
+
+ if ((subject_name = X509_get_subject_name(cert)) == NULL) return NULL;
+
+ der_name_len = i2d_X509_NAME(X509_get_subject_name(cert), NULL);
+ if (der_name_len == 0) return NULL;
+
+ buf = OPENSSL_malloc(der_name_len);
+ der_name = buf;
+
+
+ if (!i2d_X509_NAME(X509_get_subject_name(cert), &der_name))
+ {
+ OPENSSL_free(der_name);
+ return NULL;
+ }
+
+ OpenSSL_add_all_digests();
+
+ m = EVP_sha1();
+ if (m == NULL)
+ {
+ OPENSSL_free(der_name);
+ return NULL;
+ }
+
+
+ EVP_DigestInit(&ctx, m);
+ EVP_DigestUpdate(&ctx, delegation_id, strlen(delegation_id));
+ EVP_DigestFinal(&ctx, hash_delegation_id, &delegation_id_len);
+
+ /* lots of nasty hard coded numbers:
+ "8bytes/16chars delegation ID" + "-" + "8bytes/16chars DN" */
+
+ for (i=0; i <=7; ++i)
+ sprintf(&filename[i*2], "%02x", hash_delegation_id[i]);
+
+ filename[16] = '-';
+
+ EVP_DigestInit(&ctx, m);
+ EVP_DigestUpdate(&ctx, buf, der_name_len);
+ EVP_DigestFinal(&ctx, hash_name, &hash_name_len);
+
+ for (i=0; i <=7; ++i)
+ sprintf(&filename[17 + i*2], "%02x", hash_name[i]);
+
+ return strdup(filename);
+}
+#endif
+
+/// Store a GSI proxy chain in the proxy cache, along with the private key
+int GRSTx509CacheProxy(char *proxydir, char *delegation_id,
+ char *user_dn, char *proxychain)
+///
+/// Returns GRST_RET_OK on success, non-zero otherwise. The existing
+/// private key with the same delegation ID and user DN is moved out of
+/// the temporary cache.
+{
+ int ret = 0;
+ int retval = GRST_RET_FAILED;
+ char *user_dn_enc, *prvkeyfile, *proxyfile;
+ STACK_OF(X509) *certstack;
+ FILE *ifp = NULL;
+ canl_ctx c_ctx = NULL;
+ canl_cred proxy_bob = NULL;
+
+ if (strcmp(user_dn, "cache") == 0)
+ return GRST_RET_FAILED;
+
+
+ /* get the X509 stack */
+ if (GRSTx509StringToChain(&certstack, proxychain) != GRST_RET_OK){
+ return GRST_RET_FAILED;/* TODO MP we can use caNl error codes now */
+ }
+
+ /*Make new canl_ctx, TODO MP how to initialize it only once?*/
+ c_ctx = canl_create_ctx();
+ if (c_ctx == NULL)
+ goto end;
+
+ ret = canl_cred_new(c_ctx, &proxy_bob);
+ if (ret){
+ goto end; /* TODO MP we can use caNl error codes now */
+ }
+
+ /*Use caNl to load certstack into proxy_bob*/
+ ret = canl_cred_load_chain(c_ctx, proxy_bob, certstack);
+ if (ret){
+ goto end;
+ }
+
+ /* create directories if necessary, and set proxy filename */
+ user_dn_enc = GRSThttpUrlEncode(user_dn);
+
+ mkdir_printf(S_IRUSR | S_IWUSR | S_IXUSR,
+ "%s/%s", proxydir, user_dn_enc);
+ mkdir_printf(S_IRUSR | S_IWUSR | S_IXUSR,
+ "%s/%s/%s", proxydir, user_dn_enc, delegation_id);
+
+ asprintf(&proxyfile, "%s/%s/%s/userproxy.pem",
+ proxydir, user_dn_enc, delegation_id);
+ free(user_dn_enc);
+ user_dn_enc = NULL;
+
+ /* find the existing private key file */
+ prvkeyfile = GRSTx509CachedProxyKeyFind(proxydir, delegation_id, user_dn);
+ if (prvkeyfile == NULL)
+ goto end;
+ if ((ifp = fopen(prvkeyfile, "r")) == NULL)
+ goto end;
+
+ /* insert proxy private key into canl structure,
+ read from private key file */
+ ret = canl_cred_load_priv_key_file(c_ctx, proxy_bob, ifp);
+ if (ret)
+ goto end;
+ if (fclose(ifp) !=0)
+ goto end;
+ ifp = NULL;
+ unlink(prvkeyfile);
+ free(prvkeyfile);
+ prvkeyfile = NULL;
+
+ ret = canl_cred_save_proxyfile(c_ctx, proxy_bob, proxyfile);
+ if (ret)
+ goto end;
+
+ retval = GRST_RET_OK;
+
+end:
+ if (proxyfile)
+ free(proxyfile);
+ if (proxy_bob)
+ canl_cred_free(ctx, proxy_bob);
+ if (certstack)
+ sk_X509_free(certstack);
+ if (prvkeyfile)
+ free(prvkeyfile);
+ if (user_dn_enc)
+ free(user_dn_enc);
+ if (c_ctx)
+ canl_free_ctx(c_ctx);
+ if (ifp)
+ fclose(ifp);
+
+ return retval;
+}