Back to index

nordugrid-arc-nox  1.1.0~rc6
CertUtil.cpp
Go to the documentation of this file.
00001 #include <iostream>
00002 #include <cstring>
00003 #include <arc/Logger.h>
00004 
00005 #include <openssl/err.h>
00006 
00007 #include "CertUtil.h"
00008 
00009 #define X509_CERT_DIR  "X509_CERT_DIR"
00010 
00011 #ifndef WIN32
00012 #define FILE_SEPERATOR "/"
00013 #else
00014 #define FILE_SEPERATOR "\\"
00015 #endif
00016 #define SIGNING_POLICY_FILE_EXTENSION   ".signing_policy"
00017 
00018 namespace ArcCredential {
00019 
00020   static Arc::Logger& logger = Arc::Logger::rootLogger;
00021 
00022 static int check_issued(X509_STORE_CTX*, X509* x, X509* issuer);
00023 static int verify_callback(int ok, X509_STORE_CTX* store_ctx);
00024 
00025 int verify_cert_chain(X509* cert, STACK_OF(X509)** certchain, cert_verify_context* vctx) {
00026   int i;
00027   int j;
00028   int retval = 0;
00029   X509_STORE* cert_store = NULL;
00030   X509_STORE_CTX* store_ctx = NULL;
00031   X509* cert_in_chain = NULL;
00032   X509* user_cert = NULL;
00033 
00034   user_cert = cert;
00035   cert_store = X509_STORE_new();
00036   X509_STORE_set_verify_cb_func(cert_store, verify_callback);
00037   if (*certchain != NULL) {
00038     for (i=0;i<sk_X509_num(*certchain);i++) {
00039       cert_in_chain = sk_X509_value(*certchain,i);
00040       if (!user_cert) {
00041         //Assume the first cert in cert chain is the user cert.
00042         user_cert = cert_in_chain;
00043       }
00044       else {
00045         j = X509_STORE_add_cert(cert_store, cert_in_chain);
00046         if (!j) {
00047           if ((ERR_GET_REASON(ERR_peek_error()) == X509_R_CERT_ALREADY_IN_HASH_TABLE)) {
00048             ERR_clear_error();
00049             break;
00050           }
00051           else { goto err; }
00052         }
00053       }
00054     }
00055   }
00056   if(user_cert == NULL) goto err;
00057 
00058   if (X509_STORE_load_locations(cert_store,
00059            vctx->ca_file.empty() ? NULL:vctx->ca_file.c_str(),
00060            vctx->ca_dir.empty() ? NULL:vctx->ca_dir.c_str())) {
00061     store_ctx = X509_STORE_CTX_new();
00062     X509_STORE_CTX_init(store_ctx, cert_store, user_cert, NULL);
00063     //Last parameter is "untrusted", probably related globus code is wrong.
00064 
00065 #if SSLEAY_VERSION_NUMBER >=  0x0090600fL
00066     /* override the check_issued with our version */
00067     store_ctx->check_issued = check_issued;
00068 #endif
00069 
00070     /*
00071      * If this is not set, OpenSSL-0.9.8 assumes the proxy cert
00072      * as an EEC and the next level cert in the chain as a CA cert
00073      * and throws an invalid CA error. If we set this, the callback
00074      * (verify_callback) gets called with
00075      * ok = 0 with an error "unhandled critical extension"
00076      * and "path length exceeded".
00077      * verify_callback will check the critical extension later.
00078      */
00079 #if (OPENSSL_VERSION_NUMBER >= 0x0090800fL)
00080     X509_STORE_CTX_set_flags(store_ctx, X509_V_FLAG_ALLOW_PROXY_CERTS);
00081 #endif
00082 
00083     if (!X509_STORE_CTX_set_ex_data(store_ctx, VERIFY_CTX_STORE_EX_DATA_IDX, (void *)vctx)) {
00084       logger.msg(Arc::ERROR,"Can not set the STORE_CTX for chain verification");
00085       goto err;
00086     }
00087 
00088     //X509_STORE_CTX_set_depth(store_ctx, 10);
00089 
00090     if(!X509_verify_cert(store_ctx)) { goto err; }
00091   }
00092 
00093   //Replace the trusted certificate chain after verification passed, the
00094   //trusted ca certificate is added
00095   if(*certchain) { sk_X509_pop_free(*certchain, X509_free); }
00096   *certchain = sk_X509_new_null();
00097 
00098   if(store_ctx != NULL) for (i=0; i < sk_X509_num(store_ctx->chain); i++) {
00099     X509* tmp = NULL; tmp = X509_dup(sk_X509_value(store_ctx->chain,i));
00100     sk_X509_insert(*certchain, tmp, i);
00101   }
00102 
00103   retval = 1;
00104 
00105 err:
00106   if(cert_store) { X509_STORE_free(cert_store); }
00107   if(store_ctx) { X509_STORE_CTX_free(store_ctx); }
00108 
00109   return retval;
00110 }
00111 
00112 static int verify_callback(int ok, X509_STORE_CTX* store_ctx) {
00113   cert_verify_context*      vctx;
00114   vctx = (cert_verify_context *) X509_STORE_CTX_get_ex_data(store_ctx, VERIFY_CTX_STORE_EX_DATA_IDX);
00115   //TODO get SSL object here, special for GSSAPI
00116   if(!vctx) { return (0);}
00117 
00118   /* Now check for some error conditions which can be disregarded. */
00119   if(!ok) {
00120     switch (store_ctx->error) {
00121     case X509_V_ERR_PATH_LENGTH_EXCEEDED:
00122       /*
00123       * Since OpenSSL does not know about proxies,
00124       * it will count them against the path length
00125       * So we will ignore the errors and do our
00126       * own checks later on, when we check the last
00127       * certificate in the chain we will check the chain.
00128       */
00129       logger.msg(Arc::DEBUG,"X509_V_ERR_PATH_LENGTH_EXCEEDED");
00130 
00131 #if (OPENSSL_VERSION_NUMBER >= 0x0090800fL)
00132       /*
00133       * OpenSSL-0.9.8 (because of proxy support) has this error
00134       *(0.9.7d did not have this, not proxy support still)
00135       * So we will ignore the errors now and do our checks later
00136       * on.
00137       */
00138     case X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED:
00139       logger.msg(Arc::DEBUG,"X509_V_ERR_PATH_LENGTH_EXCEEDED --- with proxy");
00140       ok = 1;
00141       break;
00142 #endif
00143 
00144 #if (OPENSSL_VERSION_NUMBER > 0x0090706fL)
00145       /*
00146       * In the later version (097g+) OpenSSL does know about
00147       * proxies, but not non-rfc compliant proxies, it will
00148       * count them as unhandled critical extensions.
00149       * So we will ignore the errors and do our
00150       * own checks later on, when we check the last
00151       * certificate in the chain we will check the chain.
00152       * As OpenSSL does not recognize legacy proxies (pre-RFC, and older fasion proxies)
00153       */
00154     case X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION:
00155       logger.msg(Arc::DEBUG,"X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION");
00156       /*
00157       * Setting this for 098 or later versions avoid the invalid
00158       * CA error but would result in proxy path len exceeded which
00159       * is handled above. For versions less than 098 and greater
00160       * than or equal to 097g causes a seg fault in
00161       * check_chain_extensions (line 498 in crypto/x509/x509_vfy.c)
00162       * If this flag is set, openssl assumes proxy extensions would
00163       * definitely be there and tries to access the extensions but
00164       * the extension is not there really, as it not recognized by
00165       * openssl. So openssl versions >= 097g and < 098 would
00166       * consider our proxy as an EEC and higher level proxy in the
00167       * cert chain (if any) or EEC as a CA cert and thus would throw
00168       * as invalid CA error. We handle that error below.
00169       */
00170   #if (OPENSSL_VERSION_NUMBER >= 0x0090800fL)
00171       store_ctx->current_cert->ex_flags |= EXFLAG_PROXY;
00172   #endif
00173       ok = 1;
00174       break;
00175     case X509_V_ERR_INVALID_PURPOSE:
00176       /*
00177       * Invalid purpose if we init sec context with a server that does
00178       * not have the SSL Server Netscape extension (occurs with 0.9.7
00179       * servers)
00180       */
00181       ok = 1;
00182       break;
00183 #endif
00184 
00185 #if (OPENSSL_VERSION_NUMBER > 0x0090706fL)
00186     case X509_V_ERR_INVALID_CA:
00187     {
00188       /*
00189       * If the previous cert in the chain is a proxy cert then
00190       * we get this error just because openssl does not recognize
00191       * our proxy and treats it as an EEC. And thus, it would
00192       * treat higher level proxies (if any) or EEC as CA cert
00193       * (which are not actually CA certs) and would throw this
00194       * error. As long as the previous cert in the chain is a
00195       * proxy cert, we ignore this error.
00196       */
00197       X509* prev_cert = sk_X509_value(store_ctx->chain, store_ctx->error_depth-1);
00198       certType type;
00199       if(check_cert_type(prev_cert, type)) { if(CERT_IS_PROXY(type)) ok = 1; }
00200       break;
00201     }
00202 #endif
00203     default:
00204       break;
00205     }
00206 
00207 
00208 
00209     //if failed, show the error message.
00210     if(!ok) {
00211       char * subject_name = X509_NAME_oneline(X509_get_subject_name(store_ctx->current_cert), 0, 0);
00212       unsigned long issuer_hash = X509_issuer_name_hash(store_ctx->current_cert);
00213 
00214       logger.msg(Arc::DEBUG,"Error number in store context: %i",(int)(store_ctx->error));
00215       if(sk_X509_num(store_ctx->chain) == 1) { logger.msg(Arc::VERBOSE,"Self-signed certificate"); }
00216 
00217       if (store_ctx->error == X509_V_ERR_CERT_NOT_YET_VALID) {
00218         logger.msg(Arc::INFO,"The certificate with subject %s  is not valid",subject_name);
00219       }
00220       else if(store_ctx->error == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY) {
00221         logger.msg(Arc::INFO,"Can not find issuer certificate for the certificate with subject %s and hash: %lu",subject_name,issuer_hash);
00222       }
00223       else if(store_ctx->error == X509_V_ERR_CERT_HAS_EXPIRED) {
00224         logger.msg(Arc::INFO,"Certificate with subject %s has expired",subject_name);
00225       }
00226       else if(store_ctx->error == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) {
00227         logger.msg(Arc::INFO,"Untrusted self-signed certificate in chain with "
00228                    "subject %s and hash: %lu",subject_name,issuer_hash);
00229       }
00230       else
00231         logger.msg(Arc::INFO,"Certificate verification error: %s",X509_verify_cert_error_string(store_ctx->error));
00232 
00233       if(subject_name) OPENSSL_free(subject_name);
00234 
00235       return ok;
00236     }
00237     store_ctx->error = 0;
00238     return ok;
00239   }
00240 
00241   /* All of the OpenSSL tests have passed and we now get to
00242    * look at the certificate to verify the proxy rules,
00243    * and ca-signing-policy rules. CRL checking will also be done.
00244    */
00245 
00246   /*
00247    * Test if the name ends in CN=proxy and if the issuer
00248    * name matches the subject without the final proxy.
00249    */
00250   certType type;
00251   bool ret = check_cert_type(store_ctx->current_cert,type);
00252   if(!ret) {
00253     logger.msg(Arc::ERROR,"Can not get the certificate type");
00254     return 0;
00255   }
00256   if(CERT_IS_PROXY(type)){
00257    /* it is a proxy */
00258         /* a legacy globus proxy may only be followed by another legacy globus
00259          * proxy or a limited legacy globus_proxy.
00260          * a limited legacy globus proxy may only be followed by another
00261          * limited legacy globus proxy
00262          * a draft compliant proxy may only be followed by another draft
00263          * compliant proxy
00264          * a draft compliant limited proxy may only be followed by another draft
00265          * compliant limited proxy or a draft compliant independent proxy
00266          */
00267 
00268     if((CERT_IS_GSI_2_PROXY(vctx->cert_type) && !CERT_IS_GSI_2_PROXY(type)) ||
00269          (CERT_IS_GSI_3_PROXY(vctx->cert_type) && !CERT_IS_GSI_3_PROXY(type)) ||
00270          (CERT_IS_RFC_PROXY(vctx->cert_type) && !CERT_IS_RFC_PROXY(type))) {
00271       logger.msg(Arc::ERROR,"The proxy to be signed should be compatible with the signing certificate: (%s) -> (%s)",certTypeToString(vctx->cert_type),certTypeToString(type));
00272       return (0);
00273     }
00274 
00275     if(CERT_IS_LIMITED_PROXY(vctx->cert_type) &&
00276        !(CERT_IS_LIMITED_PROXY(type) || CERT_IS_INDEPENDENT_PROXY(type))) {
00277       logger.msg(Arc::ERROR,"Can't sign a non-limited, non-independent proxy with a limited proxy");
00278       store_ctx->error = X509_V_ERR_CERT_SIGNATURE_FAILURE;
00279       return (0);
00280     }
00281 
00282     vctx->proxy_depth++;
00283     if(vctx->max_proxy_depth!=-1 && vctx->max_proxy_depth < vctx->proxy_depth) {
00284       logger.msg(Arc::ERROR,"The proxy depth %i is out of maximum limit %i",vctx->proxy_depth,vctx->max_proxy_depth);
00285       return (0);
00286     }
00287     vctx->cert_type=type;
00288   }
00289 
00293   if(vctx->cert_type == CERT_TYPE_EEC || vctx->cert_type == CERT_TYPE_CA) {
00294 #ifdef X509_V_ERR_CERT_REVOKED
00295         /*
00296          * SSLeay 0.9.0 handles CRLs but does not check them.
00297          * We will check the crl for this cert, if there
00298          * is a CRL in the store.
00299          * If we find the crl is not valid, we will fail,
00300          * as once the sysadmin indicates that CRLs are to
00301          * be checked, he best keep it upto date.
00302          *
00303          * When future versions of SSLeay support this better,
00304          * we can remove these tests.
00305          * we come through this code for each certificate,
00306          * starting with the CA's We will check for a CRL
00307          * each time, but only check the signature if the
00308          * subject name matches, and check for revoked
00309          * if the issuer name matches.
00310          * this allows the CA to revoke its own cert as well.
00311          */
00312     int i, n;
00313     X509_OBJECT     obj;
00314     X509_CRL *      crl = NULL;
00315     X509_CRL_INFO * crl_info = NULL;
00316     X509_REVOKED *  revoked = NULL;;
00317     EVP_PKEY *key = NULL;
00318 
00322     if (X509_STORE_get_by_subject(store_ctx, X509_LU_CRL, X509_get_subject_name(store_ctx->current_cert), &obj)) {
00323       if((crl=obj.data.crl) && (crl_info=crl->crl)) {
00324         /* verify the signature on this CRL */
00325         key = X509_get_pubkey(store_ctx->current_cert);
00326         if (X509_CRL_verify(crl, key) <= 0) {
00327           store_ctx->error = X509_V_ERR_CRL_SIGNATURE_FAILURE;
00328           // TODO: tell which crl failed
00329           logger.msg(Arc::ERROR,"Couldn't verify availability of CRL");
00330           EVP_PKEY_free(key); X509_OBJECT_free_contents(&obj); return (0);
00331         }
00332 
00333         /* Check date see if expired */
00334         i = X509_cmp_current_time(crl_info->lastUpdate);
00335         if (i == 0) {
00336           store_ctx->error = X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD;
00337           // TODO: tell which crl failed
00338           logger.msg(Arc::ERROR,"In the available CRL the lastUpdate field is not valid");
00339           EVP_PKEY_free(key); X509_OBJECT_free_contents(&obj); return (0);
00340         }
00341         if(i>0) {
00342           store_ctx->error = X509_V_ERR_CRL_NOT_YET_VALID;
00343           // TODO: tell which crl failed
00344           logger.msg(Arc::ERROR,"The available CRL is not yet valid");
00345           EVP_PKEY_free(key); X509_OBJECT_free_contents(&obj); return (0);
00346         }
00347 
00348         i = (crl_info->nextUpdate != NULL) ? X509_cmp_current_time(crl_info->nextUpdate) : 1;
00349         if (i == 0) {
00350           store_ctx->error = X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD;
00351           // TODO: tell which crl failed
00352           logger.msg(Arc::ERROR,"In the available CRL, the nextUpdate field is not valid");
00353           EVP_PKEY_free(key); X509_OBJECT_free_contents(&obj); return (0);
00354         }
00355 
00356         if (i < 0) {
00357           store_ctx->error = X509_V_ERR_CRL_HAS_EXPIRED;
00358           logger.msg(Arc::ERROR,"The available CRL has expired");
00359           EVP_PKEY_free(key); X509_OBJECT_free_contents(&obj); return (0);
00360         }
00361         EVP_PKEY_free(key);
00362       }
00363       X509_OBJECT_free_contents(&obj);
00364     }
00365 
00366     /* now check if the issuer has a CRL, and we are revoked */
00367     if (X509_STORE_get_by_subject(store_ctx, X509_LU_CRL, X509_get_issuer_name(store_ctx->current_cert), &obj)) {
00368       if((crl=obj.data.crl) && (crl_info=crl->crl)) {
00369         /* check if this cert is revoked */
00370         n = sk_X509_REVOKED_num(crl_info->revoked);
00371         for (i=0; i<n; i++) {
00372           revoked = (X509_REVOKED *)sk_X509_REVOKED_value(crl_info->revoked,i);
00373           if(!ASN1_INTEGER_cmp(revoked->serialNumber, X509_get_serialNumber(store_ctx->current_cert))) {
00374             long serial;
00375             char buf[256];
00376             char* subject_string;
00377             serial = ASN1_INTEGER_get(revoked->serialNumber);
00378             snprintf(buf, sizeof(buf), "%ld (0x%lX)",serial,serial);
00379             subject_string = X509_NAME_oneline(X509_get_subject_name(store_ctx->current_cert),NULL,0);
00380             logger.msg(Arc::ERROR,"Certificate with serial number %s and subject \"%s\" is revoked",buf,subject_string);
00381             store_ctx->error = X509_V_ERR_CERT_REVOKED;
00382             OPENSSL_free(subject_string);
00383             X509_OBJECT_free_contents(&obj); return (0);
00384           }
00385         }
00386       }
00387       X509_OBJECT_free_contents(&obj);
00388     }
00389 #endif /* X509_V_ERR_CERT_REVOKED */
00390 
00391 
00393     char* cadir = NULL;
00394     char* ca_policy_file_path = NULL;
00395     if (X509_NAME_cmp(X509_get_subject_name(store_ctx->current_cert), X509_get_issuer_name(store_ctx->current_cert))) {
00396       cadir = (char*)(vctx->ca_dir.c_str());
00397       if(!(*cadir)) {
00398         logger.msg(Arc::ERROR,"Directory of trusted CAs is not specified/found");
00399         return (0);
00400       }
00401 
00402       unsigned int buffer_len;
00403       unsigned long hash;
00404       hash = X509_NAME_hash(X509_get_issuer_name(store_ctx->current_cert));
00405 
00406       buffer_len = strlen(cadir) + strlen(FILE_SEPERATOR) + 8 /* hash */
00407         + strlen(SIGNING_POLICY_FILE_EXTENSION) + 1 /* NULL */;
00408       ca_policy_file_path = (char*) malloc(buffer_len);
00409       if(ca_policy_file_path == NULL) {
00410         logger.msg(Arc::ERROR,"Can't allocate memory for CA policy path");
00411         store_ctx->error = X509_V_ERR_APPLICATION_VERIFICATION;
00412         return (0);
00413       }
00414       snprintf(ca_policy_file_path,buffer_len,"%s%s%08lx%s", cadir, FILE_SEPERATOR, hash, SIGNING_POLICY_FILE_EXTENSION);
00415       ca_policy_file_path[buffer_len-1]=0;
00416 
00417       //TODO check the certificate against policy
00418 
00419       free(ca_policy_file_path);
00420     }
00421   }
00422 
00424   if(vctx->cert_chain == NULL) { vctx->cert_chain = sk_X509_new_null(); }
00425   sk_X509_push(vctx->cert_chain, X509_dup(store_ctx->current_cert));
00426   vctx->cert_depth++;
00427 
00429   STACK_OF(X509_EXTENSION)* extensions;
00430   X509_EXTENSION* ext;
00431   ASN1_OBJECT* extension_obj;
00432   extensions = store_ctx->current_cert->cert_info->extensions;
00433   int i;
00434   if(extensions) for (i=0;i<sk_X509_EXTENSION_num(extensions);i++) {
00435     ext = (X509_EXTENSION *) sk_X509_EXTENSION_value(extensions,i);
00436     if(X509_EXTENSION_get_critical(ext)) {
00437       extension_obj = X509_EXTENSION_get_object(ext);
00438       int nid = OBJ_obj2nid(extension_obj);
00439       if(nid != NID_basic_constraints &&
00440          nid != NID_key_usage &&
00441          nid != NID_ext_key_usage &&
00442          nid != NID_netscape_cert_type &&
00443          nid != NID_subject_key_identifier &&
00444          nid != NID_authority_key_identifier &&
00445          nid != OBJ_sn2nid("PROXYCERTINFO_V3") &&
00446          nid != OBJ_sn2nid("PROXYCERTINFO_V4") &&
00447          nid != OBJ_sn2nid("OLD_PROXYCERTINFO") &&
00448          nid != OBJ_sn2nid("PROXYCERTINFO")
00449 #if (OPENSSL_VERSION_NUMBER > 0x0090706fL)
00450          && nid != NID_proxyCertInfo
00451 #endif
00452         ) {
00453         store_ctx->error = X509_V_ERR_CERT_REJECTED;
00454         logger.msg(Arc::ERROR,"Certificate has unknown extension with numeric ID %u and SN %s",(unsigned int)nid,OBJ_nid2sn(nid));
00455         return (0);
00456       }
00457 
00458 // TODO: do not use openssl version - instead use result of check if
00459 // proxy extension is supported
00460 #if (OPENSSL_VERSION_NUMBER > 0x0090706fL) && (nid == NID_proxyCertInfo)
00461       /* If the openssl version >=097g (which means proxy cert info is
00462        * supported), and NID_proxyCertInfo can be got from the extension,
00463        * then we use the proxy cert info support from openssl itself.
00464        * Otherwise we have to use globus-customized proxy cert info support.
00465        */
00466       PROXY_CERT_INFO_EXTENSION*  proxycertinfo = NULL;
00467       proxycertinfo = (PROXY_CERT_INFO_EXTENSION*) X509V3_EXT_d2i(ext);
00468       if (proxycertinfo == NULL) {
00469         logger.msg(Arc::WARNING,"Can not convert DER encoded PROXY_CERT_INFO_EXTENSION extension to internal format");
00470       } else {
00471         int path_length = ASN1_INTEGER_get(proxycertinfo->pcPathLengthConstraint);
00472         /* ignore negative values */
00473         if(path_length > -1) {
00474           if(vctx->max_proxy_depth == -1 || vctx->max_proxy_depth > vctx->proxy_depth + path_length) {
00475             vctx->max_proxy_depth = vctx->proxy_depth + path_length;
00476             logger.msg(Arc::DEBUG,"proxy_depth: %i, path_length: %i",(int)(vctx->proxy_depth),(int)path_length);
00477           }
00478         }
00480         if(store_ctx->current_cert->ex_flags & EXFLAG_PROXY) {
00481           switch (OBJ_obj2nid(proxycertinfo->proxyPolicy->policyLanguage)) {
00482             case NID_Independent:
00483                /* Put whatever explicit policy here to this particular proxy certificate, usually by
00484                 * pulling them from some database. If there is none policy which need to be explicitly
00485                 * inserted here, clear all the policy storage (make this and any subsequent proxy certificate
00486                 * be void of any policy, because here the policylanguage is independent)
00487                 */
00488               vctx->proxy_policy.clear();
00489               break;
00490             case NID_id_ppl_inheritAll:
00491                /* This is basically a NOP */
00492               break;
00493             default:
00494               /* Here get the proxy policy */
00495               vctx->proxy_policy.clear();
00496               if((proxycertinfo->proxyPolicy) &&
00497                  (proxycertinfo->proxyPolicy->policy) &&
00498                  (proxycertinfo->proxyPolicy->policy->data)) {
00499                 vctx->proxy_policy.append(
00500                    proxycertinfo->proxyPolicy->policy->data,
00501                    proxycertinfo->proxyPolicy->policy->length);
00502               }
00503               /* Use : as seperator for policies parsed from different proxy certificate*/
00504               /* !!!! Taking int account previous proxy_policy.clear() !!!!
00505                  !!!! it seems to be impossible to have more than one    !!!!
00506                  !!!!  policy collected anyway !!!! */
00507               vctx->proxy_policy.append(":");
00508               break;
00509           }
00510         }
00511         PROXY_CERT_INFO_EXTENSION_free(proxycertinfo);
00512         proxycertinfo = NULL;
00513       }
00514 #else
00515       PROXYCERTINFO*  proxycertinfo = NULL;
00516       if(nid == OBJ_sn2nid("PROXYCERTINFO_V3") || nid == OBJ_sn2nid("PROXYCERTINFO_V4")) {
00517         proxycertinfo = (PROXYCERTINFO*) X509V3_EXT_d2i(ext);
00518         if (proxycertinfo == NULL) {
00519           logger.msg(Arc::WARNING,"Can not convert DER encoded PROXYCERTINFO extension to internal format");
00520         } else {
00521           int path_length = PROXYCERTINFO_get_path_length(proxycertinfo);
00522           /* ignore negative values */
00523           if(path_length > -1) {
00524             if(vctx->max_proxy_depth == -1 || vctx->max_proxy_depth > vctx->proxy_depth + path_length) {
00525               vctx->max_proxy_depth = vctx->proxy_depth + path_length;
00526               logger.msg(Arc::DEBUG,"proxy_depth: %i, path_length: %i",(int)(vctx->proxy_depth),(int)path_length);
00527             }
00528           }
00529         }
00530       }
00531 
00533       if(proxycertinfo != NULL) {
00534         int policynid = OBJ_obj2nid(PROXYPOLICY_get_policy_language(proxycertinfo->proxypolicy));
00535         if(policynid == OBJ_sn2nid(INDEPENDENT_PROXY_SN)) {
00536           /* Put whatever explicit policy here to this particular proxy certificate, usually by
00537            * pulling them from some database. If there is none policy which need to be explicitly
00538            * inserted here, clear all the policy storage (make this and any subsequent proxy certificate
00539            * be void of any policy, because here the policylanguage is independent)
00540            */
00541           vctx->proxy_policy.clear();
00542         }
00543         else if(policynid == OBJ_sn2nid(IMPERSONATION_PROXY_SN)) {
00544           /* This is basically a NOP */
00545         }
00546         else {
00547           /* Here get the proxy policy */
00548           vctx->proxy_policy.clear();
00549           if(proxycertinfo->proxypolicy) {
00550             int length;
00551             char* policy_string = NULL;
00552             policy_string = (char*)PROXYPOLICY_get_policy(proxycertinfo->proxypolicy, &length);
00553             if(policy_string && (length > 0)) {
00554               vctx->proxy_policy.append(policy_string, length);
00555               /* Use : as seperator for policies parsed from different
00556                  proxy certificate*/
00557               /* !!!! Taking int account previous proxy_policy.clear() !!!!
00558                  !!!! it seems to be impossible to have more than one    !!!!
00559                  !!!!  policy collected anyway !!!! */
00560               vctx->proxy_policy.append(":");
00561             }
00562             if(policy_string != NULL) free(policy_string);
00563           }
00564         }
00565         PROXYCERTINFO_free(proxycertinfo); proxycertinfo = NULL;
00566       }
00567 #endif
00568     }
00569   }
00570 
00571   /*
00572   * We ignored any path length restrictions above because
00573   * OpenSSL was counting proxies against the limit.
00574   * If we are on the last cert in the chain, we
00575   * know how many are proxies, so we can do the
00576   * path length check now.
00577   * See x509_vfy.c check_chain_purpose
00578   * all we do is substract off the proxy_dpeth
00579   */
00580   if(store_ctx->current_cert == store_ctx->cert) {
00581     if(store_ctx->chain) for (i=0; i < sk_X509_num(store_ctx->chain); i++) {
00582       X509* cert = sk_X509_value(store_ctx->chain,i);
00583       if (((i - vctx->proxy_depth) > 1) && (cert->ex_pathlen != -1)
00584                && ((i - vctx->proxy_depth) > (cert->ex_pathlen + 1))
00585                && (cert->ex_flags & EXFLAG_BCONS)) {
00586         store_ctx->current_cert = cert; /* point at failing cert */
00587         store_ctx->error = X509_V_ERR_PATH_LENGTH_EXCEEDED;
00588         return (0);
00589       }
00590     }
00591   }
00592 
00593   return (1);
00594 }
00595 
00596 bool check_cert_type(X509* cert, certType& type) {
00597   logger.msg(Arc::DEBUG, "Trying to check X509 cert with check_cert_type");
00598 
00599   bool ret = false;
00600   type = CERT_TYPE_EEC;
00601 
00602   ASN1_STRING* data;
00603   X509_EXTENSION* certinfo_ext;
00604   PROXYCERTINFO* certinfo = NULL;
00605   PROXYPOLICY* policy = NULL;
00606   ASN1_OBJECT* policylang = NULL;
00607   int policynid;
00608 
00609   int index = -1;
00610   int critical;
00611   BASIC_CONSTRAINTS* x509v3_bc = NULL;
00612   if(!cert) return false;
00613   if((x509v3_bc = (BASIC_CONSTRAINTS*) X509_get_ext_d2i(cert,
00614     NID_basic_constraints, &critical, &index)) && x509v3_bc->ca) {
00615     type = CERT_TYPE_CA;
00616     if(x509v3_bc) { BASIC_CONSTRAINTS_free(x509v3_bc); }
00617     return true;
00618   }
00619 
00620   X509_NAME* issuer = NULL;
00621   X509_NAME* subject = X509_get_subject_name(cert);
00622   X509_NAME_ENTRY * name_entry = NULL;
00623   if(!subject) goto err;
00624   name_entry = X509_NAME_get_entry(subject, X509_NAME_entry_count(subject)-1);
00625   if(!name_entry) goto err;
00626   if (!OBJ_cmp(name_entry->object,OBJ_nid2obj(NID_commonName))) {
00627     /* the name entry is of the type: common name */
00628     data = X509_NAME_ENTRY_get_data(name_entry);
00629     if(!data) goto err;
00630     if (data->length == 5 && !memcmp(data->data,"proxy",5)) { type = CERT_TYPE_GSI_2_PROXY; }
00631     else if(data->length == 13 && !memcmp(data->data,"limited proxy",13)) { type = CERT_TYPE_GSI_2_LIMITED_PROXY; }
00632     else if((index = X509_get_ext_by_NID(cert, OBJ_txt2nid(PROXYCERTINFO_V4), -1)) != -1) {
00633       certinfo_ext = X509_get_ext(cert,index);
00634       if(X509_EXTENSION_get_critical(certinfo_ext)) {
00635         if((certinfo = (PROXYCERTINFO *)X509V3_EXT_d2i(certinfo_ext)) == NULL) {
00636           logger.msg(Arc::ERROR,"Can't convert DER encoded PROXYCERTINFO extension to internal form");
00637           goto err;
00638         }
00639         if((policy = PROXYCERTINFO_get_proxypolicy(certinfo)) == NULL) {
00640           logger.msg(Arc::ERROR,"Can't get policy from PROXYCERTINFO extension");
00641           goto err;
00642         }
00643         if((policylang = PROXYPOLICY_get_policy_language(policy)) == NULL) {
00644           logger.msg(Arc::ERROR,"Can't get policy language from PROXYCERTINFO extension");
00645           goto err;
00646         }
00647         policynid = OBJ_obj2nid(policylang);
00648         if(policynid == OBJ_sn2nid(IMPERSONATION_PROXY_SN)) { type = CERT_TYPE_RFC_IMPERSONATION_PROXY; }
00649         else if(policynid == OBJ_sn2nid(INDEPENDENT_PROXY_SN)) { type = CERT_TYPE_RFC_INDEPENDENT_PROXY; }
00650         else if(policynid == OBJ_sn2nid(ANYLANGUAGE_PROXY_SN)) { type = CERT_TYPE_RFC_ANYLANGUAGE_PROXY; }
00651         else if(policynid == OBJ_sn2nid(LIMITED_PROXY_SN)) { type = CERT_TYPE_RFC_LIMITED_PROXY; }
00652         else { type = CERT_TYPE_RFC_RESTRICTED_PROXY; }
00653 
00654         if((index = X509_get_ext_by_NID(cert, OBJ_txt2nid(PROXYCERTINFO_V3), -1)) != -1) {
00655           logger.msg(Arc::ERROR,"Found more than one PCI extension");
00656           goto err;
00657         }
00658       }
00659     }
00660     else if((index = X509_get_ext_by_NID(cert, OBJ_txt2nid(PROXYCERTINFO_V3), -1)) != -1) {
00661       certinfo_ext = X509_get_ext(cert,index);
00662       if(X509_EXTENSION_get_critical(certinfo_ext)) {
00663         if((certinfo = (PROXYCERTINFO *)X509V3_EXT_d2i(certinfo_ext)) == NULL) {
00664           logger.msg(Arc::ERROR,"Can't convert DER encoded PROXYCERTINFO extension to internal form");
00665           goto err;
00666         }
00667         if((policy = PROXYCERTINFO_get_proxypolicy(certinfo)) == NULL) {
00668           logger.msg(Arc::ERROR,"Can't get policy from PROXYCERTINFO extension");
00669           goto err;
00670         }
00671         if((policylang = PROXYPOLICY_get_policy_language(policy)) == NULL) {
00672           logger.msg(Arc::ERROR,"Can't get policy language from PROXYCERTINFO extension");
00673           goto err;
00674         }
00675         policynid = OBJ_obj2nid(policylang);
00676         if(policynid == OBJ_sn2nid(IMPERSONATION_PROXY_SN)) { type = CERT_TYPE_GSI_3_IMPERSONATION_PROXY; }
00677         else if(policynid == OBJ_sn2nid(INDEPENDENT_PROXY_SN)){ type = CERT_TYPE_GSI_3_INDEPENDENT_PROXY; }
00678         else if(policynid == OBJ_sn2nid(LIMITED_PROXY_SN)) { type = CERT_TYPE_GSI_3_LIMITED_PROXY; }
00679         else {type = CERT_TYPE_GSI_3_RESTRICTED_PROXY; }
00680 
00681         if((index = X509_get_ext_by_NID(cert, OBJ_txt2nid(PROXYCERTINFO_V4), -1)) != -1) {
00682           logger.msg(Arc::ERROR,"Found more than one PCI extension");
00683           goto err;
00684         }
00685       }
00686     }
00687 
00688     /*Duplicate the issuer, and add the CN=proxy, or CN=limitedproxy, etc.
00689      * This should match the subject. i.e. proxy can only be signed by
00690      * the owner.  We do it this way, to double check all the ANS1 bits
00691      * as well.
00692      */
00693     X509_NAME_ENTRY* new_name_entry = NULL;
00694     if(ret != CERT_TYPE_EEC && ret != CERT_TYPE_CA) {
00695       issuer = X509_NAME_dup(X509_get_issuer_name(cert));
00696       new_name_entry = X509_NAME_ENTRY_create_by_NID(NULL, NID_commonName, V_ASN1_APP_CHOOSE, data->data, -1);
00697       if(!new_name_entry) goto err;
00698       X509_NAME_add_entry(issuer,new_name_entry,X509_NAME_entry_count(issuer),0);
00699       X509_NAME_ENTRY_free(new_name_entry);
00700       new_name_entry = NULL;
00701 
00702       if (X509_NAME_cmp(issuer, subject)) {
00703         /* Reject this certificate, only the user may sign the proxy */
00704         logger.msg(Arc::ERROR,"The subject does not match the issuer name + proxy CN entry");
00705         goto err;
00706       }
00707       X509_NAME_free(issuer);
00708       issuer = NULL;
00709     }
00710   }
00711   ret = true;
00712 
00713 err:
00714   if(issuer) { X509_NAME_free(issuer); }
00715   if(certinfo) {PROXYCERTINFO_free(certinfo);}
00716   if(x509v3_bc) { BASIC_CONSTRAINTS_free(x509v3_bc); }
00717 
00718   return ret;
00719 }
00720 
00721 #if SSLEAY_VERSION_NUMBER >=  0x0090600fL
00722 
00726 static int check_issued(X509_STORE_CTX*, X509* x, X509* issuer) {
00727   int  ret;
00728   int  ret_code = 1;
00729 
00730   ret = X509_check_issued(issuer, x);
00731   if (ret != X509_V_OK) {
00732     ret_code = 0;
00733     switch (ret) {
00734       case X509_V_ERR_AKID_SKID_MISMATCH:
00735             /*
00736              * If the proxy was created with a previous version of Globus
00737              * where the extensions where copied from the user certificate
00738              * This error could arise, as the akid will be the wrong key
00739              * So if its a proxy, we will ignore this error.
00740              * We should remove this in 12/2001
00741              * At which time we may want to add the akid extension to the proxy.
00742              */
00743       case X509_V_ERR_KEYUSAGE_NO_CERTSIGN:
00744             /*
00745              * If this is a proxy certificate then the issuer
00746              * does not need to have the key_usage set.
00747              * So check if its a proxy, and ignore
00748              * the error if so.
00749              */
00750         certType type;
00751         check_cert_type(x, type);
00752         if (CERT_IS_PROXY(type)) { ret_code = 1; }
00753         break;
00754       default:
00755         break;
00756       }
00757     }
00758     return ret_code;
00759 }
00760 #endif
00761 
00762 const char* certTypeToString(certType type) {
00763   switch(type) {
00764     case CERT_TYPE_EEC:
00765     case CERT_TYPE_CA:
00766       return "CA certificate";
00767     case CERT_TYPE_GSI_3_IMPERSONATION_PROXY:
00768       return "X.509 Proxy Certificate Profile (pre-RFC) compliant impersonation proxy";
00769     case CERT_TYPE_GSI_3_INDEPENDENT_PROXY:
00770       return "X.509 Proxy Certificate Profile (pre-RFC) compliant independent proxy";
00771     case CERT_TYPE_GSI_3_LIMITED_PROXY:
00772       return "X.509 Proxy Certificate Profile (pre-RFC) compliant limited proxy";
00773     case CERT_TYPE_GSI_3_RESTRICTED_PROXY:
00774       return "X.509 Proxy Certificate Profile (pre-RFC) compliant restricted proxy";
00775     case CERT_TYPE_GSI_2_PROXY:
00776       return "Legacy Globus impersonation proxy";
00777     case CERT_TYPE_GSI_2_LIMITED_PROXY:
00778       return "Legacy Globus limited impersonation proxy";
00779     case CERT_TYPE_RFC_IMPERSONATION_PROXY:
00780       return "X.509 Proxy Certificate Profile RFC compliant impersonation proxy - RFC inheritAll proxy";
00781     case CERT_TYPE_RFC_INDEPENDENT_PROXY:
00782       return "X.509 Proxy Certificate Profile RFC compliant independent proxy - RFC independent proxy";
00783     case CERT_TYPE_RFC_LIMITED_PROXY:
00784       return "X.509 Proxy Certificate Profile RFC compliant limited proxy";
00785     case CERT_TYPE_RFC_RESTRICTED_PROXY:
00786       return "X.509 Proxy Certificate Profile RFC compliant restricted proxy";
00787     case CERT_TYPE_RFC_ANYLANGUAGE_PROXY:
00788       return "RFC anyLanguage proxy";
00789     default:
00790       return "Unknown certificate type";
00791   }
00792 }
00793 
00794 } // namespace ArcCredential
00795