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)
         {
 
 int GRSTx509ChainFree(GRSTx509Chain *chain)
 {
-   GRSTx509Cert *grst_cert;
+   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);        
+      }
    
-// delete the various stuff in the chain members....      
+   free(chain);
 
    return GRST_RET_OK;
 }
                              unsigned char *sig, int sig_len, 
                              X509 *cert)
 {   
-   int            ret, isig, iinfo;
-   char           acvomsdn[200], dn_coords[200],
-                  info_coords[200], sig_coords[200];
-   unsigned char *q;
+   int            ret;
    EVP_PKEY      *prvkey;
-   FILE          *fp;
    EVP_MD_CTX     ctx;
    time_t         voms_service_time1, voms_service_time2;
 
-   if (GRSTx509NameCmp(acvomsdn, 
-                   X509_NAME_oneline(X509_get_subject_name(cert),NULL,0)) != 0)
-     {
-       return GRST_RET_FAILED;
-     }
-
    prvkey = X509_extract_key(cert);
    if (prvkey == 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;
 
                             &asn1string[taglist[iinfo].start], 
                             taglist[iinfo].length+taglist[iinfo].headerlength,
                             &asn1string[taglist[isig].start+
-                                                taglist[isig].headerlength]+1,
+                                                taglist[isig].headerlength+1],
                             taglist[isig].length - 1,
                             cert) == GRST_RET_OK)
                     {
+                      GRSTerrorLog(GRST_LOG_DEBUG, " VOMS cert signature match");
                       X509_free(cert);
                       closedir(vomsDIR2);
                       closedir(vomsDIR);
           else
             {
               fp = fopen(certpath, "r");
+              GRSTerrorLog(GRST_LOG_DEBUG, "Examine VOMS cert %s", certpath);
               free(certpath);
               if (fp == NULL) continue;
 
                             &asn1string[taglist[iinfo].start], 
                             taglist[iinfo].length+taglist[iinfo].headerlength,
                             &asn1string[taglist[isig].start+
-                                                taglist[isig].headerlength]+1,
+                                                taglist[isig].headerlength+1],
                             taglist[isig].length - 1,
                             cert) == GRST_RET_OK)
                 {
  *  - even for invalid credentials, which are flagged in errors field
  */
 
-int GRSTx509ChainVomsAdd(GRSTx509Cert **grst_cert, 
+static int GRSTx509ChainVomsAdd(GRSTx509Cert **grst_cert, 
                          time_t time1_time, time_t time2_time,
                          X509_EXTENSION *ex, 
-                         char *ucuserdn, char *uccadn, char *vomsdir)
+                         char *ucuserdn, char *vomsdir)
 {
 #define MAXTAG 500
 #define GRST_ASN1_COORDS_FQAN    "-1-1-%d-1-7-1-2-1-2-%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, acuserdn[200], acvomsdn[200],
+   char              *asn1string, acuserdn[200], accadn[200], acvomsdn[200],
                       dn_coords[200], fqan_coords[200], time1_coords[200],
                       time2_coords[200];
    long               asn1length;
         if (GRSTx509NameCmp(ucuserdn, acuserdn) != 0)
                              chain_errors |= GRST_CERT_BAD_CHAIN;
 
-// also check CA names match
-
         if (GRSTx509VerifyVomsSig(&time1_time, &time2_time,
                               asn1string, taglist, lasttag, vomsdir, acnumber)
                               != GRST_RET_OK)
                                                taglist[itag].headerlength],
                                    taglist[itag].length);
         else actime2 = 0;
-
-        GRSTerrorLog(GRST_LOG_DEBUG, "actime1=%lu actime2=%lu\n", actime1, actime2);
         
         if (actime1 > time1_time) time1_time = actime1;
         if (actime2 < time2_time) time2_time = actime2;
                                       
                  (*grst_cert)->errors = chain_errors; /* ie may be invalid */
                  (*grst_cert)->type = GRST_CERT_TYPE_VOMS;
-                 (*grst_cert)->ca = strdup(acvomsdn);
+                 (*grst_cert)->issuer = strdup(acvomsdn);
                  (*grst_cert)->dn = strdup(acuserdn);
-//                 (*grst_cert)->serial = ???;
                }
              else break;
            }
                            char *capath, char *vomsdir)
 {
    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;
+   int first_non_ca;           /* number of the EEC issued to user by CA */
+   char *ucuserdn = NULL;      /* DN of 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;                     /* Iteration variables */
+   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];
+   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;
        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");
+     }
+
    *chain = malloc(sizeof(GRSTx509Chain));
    bzero(*chain, sizeof(GRSTx509Chain));
        
    /* Check the client chain */
-   for (i = depth - 1; i >= ((lastcert == NULL) ? 0 : -1); --i) 
+   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 (i == depth - 1) 
-         (*chain)->firstcert = new_grst_cert;
+        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;
 
-        /* Check for X509 certificate and point to it with '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 */
+              
+                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 */
+              
+                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 */
+              }
+
             new_grst_cert->serial = (int) ASN1_INTEGER_get(
                                X509_get_serialNumber(cert));
             new_grst_cert->start  = GRSTasn1TimeToTimeT(
             if (now > new_grst_cert->finish)
                  new_grst_cert->errors |= GRST_CERT_BAD_TIME;
 
-            IsCA = (GRSTx509IsCA(cert) == GRST_RET_OK);
+            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)
               {
-                /* always treat the 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 - 1) IsCA = TRUE;
-                
                 if (IsCA)
                   {               
                     new_grst_cert->type = GRST_CERT_TYPE_CA;
                   {
                     new_grst_cert->type = GRST_CERT_TYPE_EEC;
                     first_non_ca = i;
+                    ucuserdn = new_grst_cert->dn;
                   }
               } 
             else 
                    sign any CA I create! */
               }
 
-            new_grst_cert->dn = X509_NAME_oneline(X509_get_subject_name(cert),NULL,0);
-            new_grst_cert->ca = X509_NAME_oneline(X509_get_issuer_name(cert),NULL,0);
-            len       = strlen(new_grst_cert->dn);
-            len2      = strlen(new_grst_cert->ca);
-
             if (!prevIsCA)
               {
                 /* issuer didn't have CA status, so this is (at best) a proxy:
                   }
                   
                 /* Proxy subject must begin with issuer. */
-                if (strncmp(new_grst_cert->dn, new_grst_cert->ca, len2) != 0) 
+                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;
                                               new_grst_cert->start,
                                               new_grst_cert->finish,                                              
                                               ex,
-                                              new_grst_cert->dn, 
-                                              new_grst_cert->ca,
+                                              ucuserdn, 
                                               vomsdir);
                        }
                    }
           
 
       } /* end of for loop */
+
+   if (cacert != NULL) X509_free(cacert);
  
    return GRST_RET_OK;
 }