/** * Copyright (c) Members of the EGEE Collaboration. 2004-2010. * See http://www.eu-egee.org/partners/ for details on the copyright * holders. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * * Authors: * 2004- * Oscar Koeroo * NIKHEF Amsterdam, the Netherlands * * */ /* Needed for snprintf */ #define _XOPEN_SOURCE 500 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "_verify_log.h" #include "_verify_proxy_certinfo.h" #include "_verify_x509.h" #include "verify_x509.h" #include "verify_x509_datatypes.h" /* OpenSSL uses a default depth of 9. * To cope with Subordinate CAs we have to extend the verification depth to be * able to hold the certificate chain (could contain a lot of delegations) and * all the CA certificate, which might not be added to the certificate chain * itself but would still be lingering in the X509 CA directory lookup functions */ #define VERIFICATION_CHAIN_DEPTH_EXTENSION 9 #define USE_STRICT_PATH_VALIDATION #define OBJ_TEXT_LEN 80 #define MY_ERR_MAX 13 #if OPENSSL_VERSION_NUMBER < 0x030000000L # define MY_FUNC_MAX 5 /* function part only for OpenSSL < 3.0 */ #endif #define MY_LIB (ERR_LIB_USER+64) #define CA_TEXT "CA" #define EEC_TEXT "EEC" #define GT2_TEXT "GT2/old-style" #define GT3_TEXT "GT3/pre-RFC" #define RFC_TEXT "RFC3820" #define ANY_PROXY_TEXT "any type of" #define IMPERSONATION_TEXT " Proxy" #define LIMITED_TEXT " Limited Proxy" #define INDEPENDENT_TEXT " Independent Proxy" #define ANYLANG_TEXT " AnyLanguage Proxy" #define RESTRICTED_TEXT " Restricted Proxy" #define ALL_LANGUAGE_TEXT " proxy of any language" /*****************************************************************************/ /* Internal variables */ /*****************************************************************************/ /** * library number to be set by ERR_get_next_error_library(). Can be initialized * using verify_init_library() and read out by verify_get_library(). */ static int initialized=0; static int library=-1; static int chain_verify=0; /** * Input array containing the library name and errors. * Use only the reason as first argument, we'll do the proper ERR_PACK(library, * 0, reason) in verify_init_library() when using them to fill the actual * errstr[] */ static ERR_STRING_DATA my_errstr[MY_ERR_MAX] = { { 0, "Proxy Verification library"}, { VER_R_NO_CACERT, "No CA certificate directory specified"}, { VER_R_CERTSTACK_EMPTY, "No certificate chain presented"}, { VER_R_PARAMETER_EMPTY, "Mandatory parameter is empty"}, { VER_R_LIMITED_DISABLED, "Limited proxies are disallowed by configuration"}, { VER_R_NOPRIVATEKEY_DISABLED, "Absence of private key disallowed by configuration"}, { VER_R_X509_VERIFY_CERT_FAILURE, "Certificate verification failed"}, { VER_R_X509_PARAMS_CONTAINER_FAILURE, "Parameter object is unset or empty"}, { VER_R_X509_PARAMS_ALREADY_SET, "Parameter is set multiple times"}, { VER_R_X509_PARAMS_DATA_EMPTY, "Parameter is set but value is empty"}, { VER_R_X509_PARAMS_ACCESS_FAILURE, "Parameter value cannot be accessed (I/O error)"}, { VER_R_X509_PARAMS_UNSUPPORTED_DATATYPE, "Unknown parameter type specified"}, { 0, NULL }, }; #if OPENSSL_VERSION_NUMBER < 0x030000000L /* Function part is deprecated in OpenSSL 3.0: therefore should not try to set * it via ERR_PACK() or we'll overwrite our library part */ /** * Input array containing the functions. * Use only the reason as first argument, we'll do the proper ERR_PACK(library, * 0, reason) in verify_init_library() when using them to fill the actual * errstr[] */ static ERR_STRING_DATA my_funcstr[MY_FUNC_MAX] = { { VER_F_VERIFY_X509_VERIFY, "verify_x509_verify()"}, { VER_F_PROCESS_INTERNAL, "process_internal_verify_data()"}, { VER_F_GRID_VERIFYCERT, "verify_verifyCert()"}, { VER_F_SET_PARAM, "verify_X509_setParameter()"}, { 0, NULL } }; #endif /*****************************************************************************/ /* Internal prototypes */ /*****************************************************************************/ /** * Callback function for PEM_read_bio_PrivateKey() and friends. See * PEM_read_bio_PrivateKey(3ssl) */ static int grid_X509_empty_password_callback(char *buf, int buf_size, int verify, void *cb_tmp); /** * Callback function for OpenSSL to put the errors * Parameters: ok, X509_STORE_CTX * Returns: 1 on success, 0 on error */ static int grid_X509_verify_callback(int ok, X509_STORE_CTX *ctx); /** * Checks the certificate chain on CA based (RFC5280) and RFC3820 Proxy based * Path Length Constraints. In addition it does a number of other proxy tests. * Returns X509_V_OK on successful verification or X509_V_ERR_CERT_REJECTED on * any error. */ static int grid_verifyChain(STACK_OF(X509) * chain); /** * Returns proxy pathlength constraint for GT3 and RFC type proxies or -1 when * none. */ static long get_proxy_pathlength(X509 *cert, proxy_type_t proxy_type); /** * Checks to see if one of the unsupported critical extensions is a valid proxy * type. * Returns X509_V_OK on success or X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION on * error */ static int grid_X509_knownCriticalExts(X509 *cert); /** * Check if we can safely discards errors about the issuer * Returns 1 when issuer is fine, 0 when no. */ static int grid_X509_check_issued_wrapper(X509_STORE_CTX *ctx,X509 *x,X509 *issuer); /** * Print and verify the key strength for given certificate, on L_WARN for * smaller than given mininum keylength. For length == 0, use default 1024 * Returns -1 on error (cannot retrieve pubkey or keylength) */ static int grid_check_keylength(X509 *cert, int minlength); /** * Print the signature algorithm for given certificate, on L_WARN for deprecated * algorithms * Returns 0 when still acceptable, -1 when too old (currently not used) or * unknown */ static int grid_check_sigalg(X509 *cert); /** * Converts the ASN1_INTEGER serial into a string * Returns a pointer to a buffer-to-be-freed or NULL on error */ static char *grid_get_serialStr(ASN1_INTEGER *); /** * Constructs string printing the expected types of certificates in human * readable form. * Parameters: * int expected_proxy_type : bitfield of types of certificates that we expected. * Returns: * char *message, MUST be free'd, NULL on error */ static char *grid_get_expected_proxy_string(int expected_proxy_type); /*****************************************************************************/ /* Function definitions */ /*****************************************************************************/ /** * Converts an internal library 'reason' to an ERR_get_error() compatible code * and pushes the corresponding error on the stack. * Returns the error code * */ unsigned long verify_errval(int func, verify_x509_error_t reason, const char *file, int line) { /* Make sure the library is initialized */ if (!initialized) verify_init_library(); ERR_put_error(library, func, (int)reason, file, line); /* Although manpage says arguments are int, there are combined as unsigned. * Better convert them now */ return ERR_PACK((unsigned int)library, (unsigned int)func, (unsigned int)reason); } /** * Converts an internal library 'reason' to an ERR_get_error() compatible code * and pushes the corresponding error on the stack. * Returns the reason code * */ verify_x509_error_t verify_reasonval(int func, verify_x509_error_t reason, const char *file, int line) { /* Make sure the library is initialized */ if (!initialized) verify_init_library(); ERR_put_error(library, func, (int)reason, file, line); return reason; } /** * Initializes the library, by creating a new library ID and loading the error * and function strings. * Returns library number */ int verify_init_library(void) { const char *oper = "verify_init_library"; const char *buf=NULL; int i; ERR_STRING_DATA *errstr=NULL; #if OPENSSL_VERSION_NUMBER < 0x030000000L ERR_STRING_DATA *funcstr=NULL; /* function part only for OpenSSL < 3.0 */ #endif ASN1_OBJECT *obj=NULL; int nid; /* Although in an ideal world we should use ERR_get_next_error_library(), * since VOMS is not using it, better set it by hand to a hopefully unused * value. */ /* library=ERR_get_next_error_library();*/ library=MY_LIB; /* Check whether we already have loaded the error and function strings */ buf=ERR_lib_error_string(ERR_PACK(library,0,0)); if (buf!=NULL) { verify_log( L_DEBUG, "Strings have already been loaded for lib %d: name: %s\n", library, buf); } else { verify_log( L_DEBUG, "Loading strings for lib %d\n", library); /* We need to either strdup the strings or guarantee that they are * unloaded using (undocumented) ERR_unload_strings() explicitly before * the plugin is dlclosed since they are used by OpenSSL as is (i.e. not * strdupped). We prefer to strdup them here and never unload them, to * prevent any potential problem when they would still be referenced * after the dlclose, e.g. when one of the errors would still be on the * error stack (current OpenSSL implementation seems to protect against * that, but we prefer not to rely on that). * Result is in a small but fixed amount of 'still reachable' memory. */ /* Allocate dynamically the library and error array itself */ errstr = (ERR_STRING_DATA*)calloc(sizeof(ERR_STRING_DATA), MY_ERR_MAX); if (errstr == NULL) { verify_error(oper, "out of memory while allocating errstr[]\n"); } else { /* Now copy in the data from the input array to the array that will be * passed to ERR_load_strings(). The first element is the name of the * library itself */ for (i=0; i0) string */ if ( (errstr[i].string=strdup(my_errstr[i].string)) == NULL ) { verify_error(oper, "out of memory while duplicating errorstring %s\n", my_errstr[i].string); /* can safely break out of for-loop: we effectively truncate * the list of errors earlier */ break; } /* Function code ==0 and reason !=0 sets the reasons */ errstr[i].error=ERR_PACK(library, 0, my_errstr[i].error); } /* Store the library and error strings */ ERR_load_strings(library,errstr); } #if OPENSSL_VERSION_NUMBER < 0x030000000L /* Function part is deprecated in OpenSSL 3.0: therefore should not try * to set it via ERR_PACK() or we'll overwrite our library part */ /* Allocate dynamically the array itself */ funcstr = (ERR_STRING_DATA*)calloc(sizeof(ERR_STRING_DATA), MY_FUNC_MAX); if (funcstr == NULL) { verify_error(oper, "out of memory while allocating funcstr[]\n"); } else { /* Now copy in the data from the input array to the array that will be * passed to ERR_load_strings() */ for (i=0; ix509 != NULL) { sk_X509_push(*certstack, xi->x509); xi->x509=NULL; } X509_INFO_free(xi); } if (!sk_X509_num(*certstack)) { err = ERR_peek_error(); verify_error( oper, "No certificates found" ); BIO_free_all(certbio); sk_X509_INFO_free(sk); sk_X509_free(*certstack); *certstack=NULL; return err; } BIO_free_all(certbio); sk_X509_INFO_free(sk); return 0; } /** * Verifies a privatekey with the first cert in the certstack * returns 0 on success, or appropriate value from ERR_peek_error(). */ unsigned long verify_verifyPrivateKey( STACK_OF(X509) *certstack, EVP_PKEY *pkey ) { const char *oper="verify_verifyPrivateKey"; X509 *cert = NULL; verify_log( L_DEBUG, "--- Welcome to the %s function ---", oper); if ( pkey == NULL ) { verify_log( L_WARN, "No private key available." ); return 0; } /* Check for X509 certificate and point to it with 'cert' */ if ((cert = sk_X509_value(certstack, 0)) != NULL) { verify_log( L_DEBUG, "X509_check_private_key" ); if ( X509_check_private_key( cert, pkey ) != 1 ) { return ERR_peek_error(); } } return 0; } /** * Validates a certificate with the CRL list and the signing CA * Return 0 on success or result of ERR_peek_error() on error (to be fixed) */ unsigned long verify_verifyCert( char * CA_DIR, STACK_OF(X509) *certstack, unsigned int verifyatnotbefore) { const char *oper = "Verifying certificate chain"; X509_STORE *store = NULL; X509_LOOKUP *lookup = NULL; X509_STORE_CTX *verify_ctx = NULL; X509 *cert = NULL; char * cert_DN = NULL; char * issuer_DN = NULL; int i = 0; int depth = 0; int fail_err; int fail_depth; X509 * fail_cert = NULL; char * fail_dn = NULL; unsigned long rc = 0; time_t verificationtime; char timebuf[30]; #if OPENSSL_VERSION_NUMBER >= 0x00908000L proxy_type_t cert_type = NONE; #endif verify_log( L_DEBUG, "--- Welcome to the %s function ---", __func__); /* This is very important! We need to initialize at each invocation of * verify_verifyCert() or we get problems in a service such as the SCAS */ chain_verify=0; /* Initials must be good */ if ( CA_DIR == NULL ) { verify_error( oper, "No CA certificate directory specified." ); return VERIFY_errval(VER_F_GRID_VERIFYCERT, VER_R_NO_CACERT); } if ( certstack == NULL ) { verify_error( oper, "Certificate stack is empty." ); return VERIFY_errval(VER_F_GRID_VERIFYCERT, VER_R_CERTSTACK_EMPTY); } verify_log( L_DEBUG, "Using CA Directory: %s", CA_DIR); verify_log( L_DEBUG, "X509_STORE_new"); if (!(store = X509_STORE_new())) { verify_error( oper, "Could not create a X509 STORE." ); return ERR_peek_error(); } verify_log( L_DEBUG, "X509_STORE_set_verify_cb_func"); #if OPENSSL_VERSION_NUMBER < 0x010000000L X509_STORE_set_verify_cb_func (store, grid_X509_verify_callback); #else X509_STORE_set_verify_cb (store, grid_X509_verify_callback); #endif #if 0 /* NOTE: the following two calls are defined in openssl ./crypto/x509/x509_d2.c * (at least for openssl-1.0.0f) * Also note that the X509_STORE_set_default_paths adds X509_LOOKUP_file (among * others) which is effectively the file pointed at by the env variable * SSL_CERT_FILE */ /* Executing the lookups to the CA and CRL files */ verify_log( L_DEBUG, "X509_STORE_load_locations"); if (X509_STORE_load_locations (store, NULL, CA_DIR) != 1) { verify_error( oper, "Could not load the CA directory."); return ERR_get_error(); } verify_log( L_DEBUG, "X509_STORE_set_default_paths"); if (X509_STORE_set_default_paths(store) != 1) { verify_error( oper, "Could not load the system wide CA certificates."); return ERR_get_error(); } #endif /* Add CA_DIR, note: the next lines will effectively call dir_ctrl() from * ./crypto/x509/by_dir.c with type X509_FILETYPE_PEM */ verify_log( L_DEBUG, "X509_STORE_add_lookup"); if (!(lookup = X509_STORE_add_lookup (store, X509_LOOKUP_hash_dir()))) { verify_error( oper, "Could not create X509_LOOKUP object."); rc=ERR_peek_error(); goto cleanup; } verify_log( L_DEBUG, "X509_LOOKUP_add_dir"); i=X509_LOOKUP_add_dir (lookup, CA_DIR, X509_FILETYPE_PEM); if (!i) { verify_error( oper, "Could not add CA_DIR."); rc=ERR_peek_error(); goto cleanup; } verify_log( L_DEBUG, "X509_STORE_set_flags"); #if OPENSSL_VERSION_NUMBER < 0x010100000L store->check_issued = grid_X509_check_issued_wrapper; #else X509_STORE_set_check_issued(store, grid_X509_check_issued_wrapper); #endif #if OPENSSL_VERSION_NUMBER < 0x00908000L X509_STORE_set_flags( store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL ); #else X509_STORE_set_flags( store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL | X509_V_FLAG_ALLOW_PROXY_CERTS ); #endif /* Use last (proxy) certificate in chain */ cert = sk_X509_value( certstack, 0 ); /* Log the certificate subject and issuer DN. The X509_NAME_oneline() result * without a provided buffer must be free'd */ cert_DN = X509_NAME_oneline( X509_get_subject_name( cert ) , NULL, 0 ); issuer_DN = X509_NAME_oneline( X509_get_issuer_name( cert ) , NULL, 0 ); verify_log( L_DEBUG, "Certificate to verify:" ); verify_log( L_DEBUG, " DN: \"%s\"", cert_DN ? cert_DN : "(NULL)"); verify_log( L_DEBUG, " Issuer DN: \"%s\"", issuer_DN ? issuer_DN : "(NULL)"); free(cert_DN); free(issuer_DN); /* Creating a verification context and init it */ verify_log( L_DEBUG, "X509_STORE_CTX_new"); if (!(verify_ctx = X509_STORE_CTX_new())) { verify_error( oper, "Could not create a X509 STORE CTX (context)."); rc=ERR_peek_error(); goto cleanup; } verify_log( L_DEBUG, "X509_STORE_CTX_init" ); if ( X509_STORE_CTX_init( verify_ctx, store, cert, certstack) != 1 ) { verify_error( oper, "Could not initialize verification context."); rc=ERR_peek_error(); goto cleanup; } if ( verifyatnotbefore ) { verificationtime=verify_asn1TimeToTimeT(X509_get_notBefore(cert)); /* Add 5 minutes to verificationtime since many but not all tools * backdate proxy certificates. Note that POSIX specifies that time_t is * an integer or real representing seconds. */ verificationtime+=5*60; if (strftime(timebuf, 30, "%F %T %Z", localtime(&verificationtime))==0){ verify_error(oper, "timebuf too small for verificationtime."); verify_log( L_INFO, "Verifying at 'notBefore' time"); } else verify_log( L_INFO, "Verifying at 'notBefore' time: %s", timebuf); X509_VERIFY_PARAM_set_time(X509_STORE_CTX_get0_param(verify_ctx), verificationtime); } else verify_log( L_DEBUG, "Verifying at current time"); X509_STORE_CTX_set_purpose( verify_ctx, X509_PURPOSE_SSL_CLIENT ); #if OPENSSL_VERSION_NUMBER >= 0x00908000L cert_type=verify_type_of_proxy(cert); if ( CERTISTYPE(cert_type, EEC) || CERTISTYPE(cert_type, CA) ) { verify_log( L_DEBUG, "Cert is not a proxy, NOT setting proxy flag"); } else { verify_log( L_DEBUG, "Setting proxy flag"); #if OPENSSL_VERSION_NUMBER < 0x010100000L cert->ex_flags |= EXFLAG_PROXY; #else X509_set_proxy_flag(cert); #endif } #endif /* Alter verification depth to fit the certificate chain, sub-CAs and root * CA */ depth = sk_X509_num (certstack); verify_log( L_DEBUG, "The certificate chain has a depth of %d. " "For verification the depth is extended to fit the chain and " "(subordinate) CAs to %d", depth, depth + VERIFICATION_CHAIN_DEPTH_EXTENSION); X509_STORE_CTX_set_depth (verify_ctx, depth + VERIFICATION_CHAIN_DEPTH_EXTENSION); verify_log( L_DEBUG, "X509_verify_cert"); if ( X509_verify_cert(verify_ctx) != 1) { /* Note: can also use verify_ctx->error, ->error_depth and * ->current_cert, but is cleaner to use API */ fail_err=X509_STORE_CTX_get_error(verify_ctx); fail_depth=X509_STORE_CTX_get_error_depth(verify_ctx); if ( (fail_cert=X509_STORE_CTX_get_current_cert(verify_ctx)) ) fail_dn=X509_NAME_oneline(X509_get_subject_name(fail_cert),NULL,0); verify_error("Verifying certificate chain", "error %d: %s", fail_err, X509_verify_cert_error_string(fail_err)); verify_error("Failed at depth", "%d, DN: %s", fail_depth, fail_dn ? fail_dn : "Not applicable"); free(fail_dn); /* Note: X509_verify_cert() already pushes a error on the error stack * using X509err() (wrapper around ERR_PUT_error() defined in * openssl/err.h), and we have just printed the X509_V_* error code, so * we can suffice with returning that the verification failed */ rc=VERIFY_errval(VER_F_GRID_VERIFYCERT, VER_R_X509_VERIFY_CERT_FAILURE); } else { verify_log( L_INFO, "The verification of the certificate has succeeded."); rc=0; } cleanup: if ( verify_ctx ) X509_STORE_CTX_free( verify_ctx ); if ( store ) X509_STORE_free( store ); return rc; } /** * converts proxy_type_t certificate to human readable string */ const char *verify_certificate_type_str(proxy_type_t cert_type) { /* CA cert? */ if (CERTISTYPE(cert_type, CA)) return CA_TEXT; /* EEC cert? */ if (CERTISTYPE(cert_type, EEC)) return EEC_TEXT; /* Different proxy types? */ if (CERTISTYPE(cert_type, GT2_TYPE)) { if (CERTISTYPE(cert_type, IMPERSONATION)) return GT2_TEXT IMPERSONATION_TEXT; if (CERTISTYPE(cert_type, LIMITED)) return GT2_TEXT LIMITED_TEXT; } else if (CERTISTYPE(cert_type, GT3_TYPE)) { if (CERTISTYPE(cert_type, IMPERSONATION)) return GT3_TEXT IMPERSONATION_TEXT; if (CERTISTYPE(cert_type, LIMITED)) return GT3_TEXT LIMITED_TEXT; if (CERTISTYPE(cert_type, INDEPENDENT)) return GT3_TEXT INDEPENDENT_TEXT; if (CERTISTYPE(cert_type, ANYLANG)) return GT3_TEXT ANYLANG_TEXT; if (CERTISTYPE(cert_type, RESTRICTED)) return GT3_TEXT RESTRICTED_TEXT; } else if (CERTISTYPE(cert_type, RFC_TYPE)) { if (CERTISTYPE(cert_type, IMPERSONATION)) return RFC_TEXT IMPERSONATION_TEXT; if (CERTISTYPE(cert_type, LIMITED)) return RFC_TEXT LIMITED_TEXT; if (CERTISTYPE(cert_type, INDEPENDENT)) return RFC_TEXT INDEPENDENT_TEXT; if (CERTISTYPE(cert_type, ANYLANG)) return RFC_TEXT ANYLANG_TEXT; if (CERTISTYPE(cert_type, RESTRICTED)) return RFC_TEXT RESTRICTED_TEXT; } /* No known certificate type */ return "Unknown"; } /****************************************************************************** * Internal functions *****************************************************************************/ /** * Callback function for PEM_read_bio_PrivateKey() and friends. See * PEM_read_bio_PrivateKey(3ssl) */ static int grid_X509_empty_password_callback(char *buf, int buf_size, int verify, void *cb_tmp) { if ( buf_size > 0 ) *buf = '\0'; return 0; } /** * Callback function for OpenSSL to put the errors * Parameters: ok, X509_STORE_CTX * Returns: 1 on success, 0 on error */ static int grid_X509_verify_callback(int ok, X509_STORE_CTX *ctx) { int errnum = X509_STORE_CTX_get_error(ctx); int errdepth = X509_STORE_CTX_get_error_depth(ctx); verify_log( L_DEBUG, "--- Welcome to the %s function ---", __func__); /* When not ok... */ if (ok != 1) { verify_log( L_DEBUG, "verification callback indicated \'ok = %d\', error number = %d (depth %d):", ok, errnum, errdepth); if (errnum == X509_V_ERR_INVALID_CA) { verify_log( L_INFO, "Invalid CA at depth %d.", errdepth); ok=1; } if (errnum == X509_V_ERR_UNABLE_TO_GET_CRL) { verify_log( L_INFO, "Unable to get CRL at depth %d.", errdepth); ok=1; } #if OPENSSL_VERSION_NUMBER >= 0x00908000L /* I don't want to do this, really, but I have yet to figure out how to get openssl 0.9.8 to accept old-style proxy certificates... */ if (errnum == X509_V_ERR_INVALID_PURPOSE) { verify_log( L_INFO, "Invalid purpose at depth %d.", errdepth); ok=1; } #endif if (errnum == X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION) { verify_log( L_DEBUG, "Unhandled critical extension at depth %d, running grid_X509_knownCriticalExts() to check if we know it.", errdepth); errnum = grid_X509_knownCriticalExts(X509_STORE_CTX_get_current_cert(ctx)); X509_STORE_CTX_set_error(ctx, errnum); if (errnum == X509_V_OK) ok=1; } /* Path length exceeded for the CA (should never happen in OpenSSL - famous last words) */ if (X509_STORE_CTX_get_error(ctx) == X509_V_ERR_PATH_LENGTH_EXCEEDED) { verify_log( L_DEBUG, "Shallow Error X509_V_ERR_PATH_LENGTH_EXCEEDED: Running alternative RFC5280 and RFC3820 compliance tests."); #if OPENSSL_VERSION_NUMBER < 0x010100000L errnum = grid_verifyChain(X509_STORE_CTX_get_chain(ctx)); #else errnum = grid_verifyChain(X509_STORE_CTX_get0_chain(ctx)); #endif X509_STORE_CTX_set_error(ctx, errnum); if (errnum != X509_V_OK) goto failure; /* It's ok */ ok=1; } /* Path length exceeded for the Proxy! -> Override and continue */ /* This is NOT about X509_V_ERR_PATH_LENGTH_EXCEEDED */ #if OPENSSL_VERSION_NUMBER >= 0x00908000L if (X509_STORE_CTX_get_error(ctx) == X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED) { verify_log( L_DEBUG, "Shallow Error X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED: Running alternative RFC5280 and RFC3820 compliance tests."); #if OPENSSL_VERSION_NUMBER < 0x010100000L errnum = grid_verifyChain(X509_STORE_CTX_get_chain(ctx)); #else errnum = grid_verifyChain(X509_STORE_CTX_get0_chain(ctx)); #endif X509_STORE_CTX_set_error(ctx, errnum); if (errnum != X509_V_OK) goto failure; /* It's ok */ ok=1; } #endif } /* * We've now got the last certificate - the identity being used for * this connection. At this point we check the whole chain for valid * CAs or, failing that, GSI-proxy validity using grid_verifyProxy */ if ((errdepth == 0) && (ok == 1)) { /* I'm in the OK state - Let's see if the chain is really well * formed:\ */ #if OPENSSL_VERSION_NUMBER < 0x010100000L errnum = grid_verifyChain(X509_STORE_CTX_get_chain(ctx)); #else errnum = grid_verifyChain(X509_STORE_CTX_get0_chain(ctx)); #endif X509_STORE_CTX_set_error(ctx, errnum); if (errnum != X509_V_OK) goto failure; } /* Ready and probably ok */ return ok; failure: /* Indicate failure */ ok = 0; errnum = X509_STORE_CTX_get_error(ctx); verify_log( L_INFO, "grid_verify_callback: error code: %d, message: \"%s\"", errnum, X509_verify_cert_error_string(errnum)); return ok; } /** * Checks the certificate chain on CA based (RFC5280) and RFC3820 Proxy based * Path Length Constraints. * Returns X509_V_OK on successful verification or a suitable error code on * any error (default X509_V_ERR_CERT_REJECTED). */ static int grid_verifyChain(STACK_OF(X509) * chain) { const char *oper = "grid_verifyChain"; X509 *cert = NULL; int i, depth, proxylevel=-1; proxy_type_t curr_cert_type = NONE; /* Note: cannot define expe_cert_type as proxy_type_t since it's a * combination of them */ int expe_cert_type = CA|EEC|ANY_PROXY_VERSION|ALL_LANGUAGE; int found_EEC = 0; char *cert_subjectdn = NULL, *cert_issuerdn = NULL; size_t len_subject, len_issuer; char *expected_sub_msg = NULL; EVP_PKEY *ca_key=NULL; int result; ASN1_OBJECT *vomsobj=NULL; int voms_proxy_pos = -1; long cert_ex_pathlen = -1; long ca_path_len_countdown = -1; long proxy_path_len_countdown = -1; long ex_pcpathlen = -1; char *serialstr; unsigned long hash; GENERAL_NAMES *sANs; GENERAL_NAME *sAN; CERTIFICATEPOLICIES *pols; POLICYINFO *pol; unsigned char *utext; char buffer[OBJ_TEXT_LEN]; int j, num; time_t now = time((time_t *)NULL); ASN1_INTEGER *cert_Serial=NULL, *issuer_Serial=NULL; int rc=X509_V_ERR_CERT_REJECTED; verify_log( L_DEBUG, "--- Welcome to the %s function, run %d ---", oper, chain_verify); /* Note: the callback is called once for each error, so if we have multiple * errors at level 0, we would be called here multiple times with the same * chain: if we have already verified succesfully, just reuse the result */ if (chain_verify) { chain_verify++; return X509_V_OK; } /* No chain, no game */ if (!chain) { verify_error( oper, "No certificate chain detected."); goto failure; } /* Go through the list, from the CA(s) down through the EEC to the final * delegation */ depth = sk_X509_num (chain); for (i=depth-1; i >= 0; --i) { if ((cert = sk_X509_value(chain, i))) { /* Init to None, indicating not to have identified it yet */ curr_cert_type = NONE; /* Extract Subject DN - Needs free */ if (!(cert_subjectdn = X509_NAME_oneline (X509_get_subject_name (cert), NULL, 0))) { verify_error (oper, "Couldn't get the subject DN from the certificate at depth %d", depth); goto failure; } /* verify_log (L_DEBUG, "\tCert here is: %s", cert_subjectdn); */ curr_cert_type = verify_type_of_proxy(cert); if (curr_cert_type == NONE) { verify_error (oper, "Couldn't classify certificate at depth %d with subject DN \"%s\"", depth, cert_subjectdn); goto failure; } /* Mark that we've found an EEC - When we see it again, it's a * failure, this would otherwise also be caught by the 'expectation * management tests' below */ if (CERTISTYPE(curr_cert_type, EEC) && found_EEC == 0) { found_EEC = 1; } else if (CERTISTYPE(curr_cert_type, EEC) && found_EEC == 1) { verify_error (oper, "Found another EEC certificate in the same chain at depth %d of %d with subject DN \"%s\"", i, depth, cert_subjectdn); goto failure; } /* Expectation management */ if ( !CERTISTYPE(expe_cert_type, (int)curr_cert_type) ) { /* Failed to comply with the expectations! */ expected_sub_msg=grid_get_expected_proxy_string(expe_cert_type); #ifdef USE_STRICT_PATH_VALIDATION verify_error(oper, "Certificate chain not build in the right order: Got %s certificate, but expected %s. Cert at depth %d of %d with Subject DN: %s", verify_certificate_type_str(curr_cert_type), expected_sub_msg, i, depth, cert_subjectdn); free(expected_sub_msg); goto failure; #else verify_log(L_WARN, "%s: Certificate chain not build in the right order: Got %s certificate, but expected %s. Cert at depth %d of %d with Subject DN: %s. Cert at depth %d of %d with Subject DN: %s", oper, verify_certificate_type_str(curr_cert_type), expected_sub_msg, i, depth, cert_subjectdn); free(expected_sub_msg); #endif } if (CERTISTYPE(curr_cert_type, CA)) { /* Expected next certificate type is: CA or EEC certificate */ expe_cert_type = CA|EEC; /* Are we a root CA? Check whether we are self-signed: first get * public key */ if ( (ca_key = X509_get_pubkey(cert)) ==NULL ) { verify_error(oper, "cannot get public key from CA cert"); goto failure; } /* Check that certificate is self-signed: * returns 1 on success, <= 0 for error */ result = X509_verify(cert, ca_key); /* Free memory */ EVP_PKEY_free(ca_key); if (result == 1) { /* signature check succeeded */ /* root CA, log, don't check sig alg (no point) */ verify_log (L_INFO, "Cert at depth %d is a root CA: \"%s\"", i, cert_subjectdn); } else if (result == 0) { /* signature check failed */ /* Clear error stack (the signature failed) */ while(ERR_get_error()); verify_log (L_INFO, "Cert at depth %d is a CA: \"%s\"", i, cert_subjectdn); /* Check signature algorithm */ if (grid_check_sigalg(cert)<0) { rc=X509_V_ERR_CERT_SIGNATURE_FAILURE; goto failure; } } else { /* signature couldn't be checked because it was invalid or some other error occurred */ verify_error(oper, "Error while trying to verify CA cert with CA key"); rc=X509_V_ERR_CERT_SIGNATURE_FAILURE; goto failure; } /* Now check the public key strength */ grid_check_keylength(cert, 2048); /* Exceeded CA Path Length ? */ if (ca_path_len_countdown == 0) { verify_error(oper, "CA Path Length Constraint exceeded on depth %d for certificate \"%s\". No CA certificates were expected at this stage.", i, cert_subjectdn); rc=X509_V_ERR_PATH_LENGTH_EXCEEDED; goto failure; } /* Store pathlen, override when small, otherwise keep the * smallest */ #if OPENSSL_VERSION_NUMBER < 0x010100000L cert_ex_pathlen = cert->ex_pathlen; #else cert_ex_pathlen = X509_get_pathlen(cert); #endif if (cert_ex_pathlen != -1) { /* Update when ca_path_len_countdown is the initial value * or when the PathLenConstraint is smaller then the * remembered ca_path_len_countdown */ if ((ca_path_len_countdown == -1) || (cert_ex_pathlen < ca_path_len_countdown)) { ca_path_len_countdown = cert_ex_pathlen; } else { /* If a path length was already issued, lower * ca_path_len_countdown */ if (ca_path_len_countdown != -1) ca_path_len_countdown--; } } else { /* If a path length was already issued, lower * ca_path_len_countdown */ if (ca_path_len_countdown != -1) ca_path_len_countdown--; } /* continue to next cert in chain */ free(cert_subjectdn); cert_subjectdn = NULL; continue; } else if (CERTISTYPE(curr_cert_type, EEC)) { /* Expected next certificate type is any type of proxy */ expe_cert_type = ANY_PROXY_VERSION | ALL_LANGUAGE; /* Log certificate DN */ verify_log (L_INFO, "Cert at depth %d is an EEC: \"%s\"", i, cert_subjectdn); /* Check signature algorithm */ if (grid_check_sigalg(cert)<0) { rc=X509_V_ERR_CERT_SIGNATURE_FAILURE; goto failure; } /* Now check the public key strength */ grid_check_keylength(cert, 2048); /* Get the serial string and hash */ serialstr=grid_get_serialStr(X509_get_serialNumber(cert)); hash=X509_NAME_hash(X509_get_issuer_name(cert)); /* Log certificate CA hash and serial number */ verify_log (L_INFO, " CA hash: %lx, serial: %s", hash, serialstr); free(serialstr); /* Get Subject Alternative Names */ sANs = X509_get_ext_d2i( cert, NID_subject_alt_name, 0, 0 ); if (sANs) { num = sk_GENERAL_NAME_num( sANs ); for (j=0; jtype ) { /* dNSName */ ASN1_STRING_to_UTF8( &utext, sAN->d.dNSName ); verify_log(L_INFO, " subjAltName dNSName: %s", utext); OPENSSL_free(utext); } else if (GEN_EMAIL == sAN->type ) { /* email */ ASN1_STRING_to_UTF8( &utext, sAN->d.rfc822Name ); verify_log(L_INFO, " subjAltName rfc822Name: %s", utext); OPENSSL_free(utext); } } GENERAL_NAMES_free(sANs); } /* Get Certificate Policies */ pols = X509_get_ext_d2i( cert, NID_certificate_policies, 0, 0); if (pols) { /* Get number of policy OIDs */ num = sk_POLICYINFO_num( pols ); for (j=0; jpolicyid, 0); verify_log(L_INFO, " policy OID: %s", buffer); } CERTIFICATEPOLICIES_free(pols); } /* continue to next cert in chain */ free(cert_subjectdn); cert_subjectdn=NULL; continue; } /* Remainder are proxies */ /* Set expected type of next proxy */ /* First map onto the basic type: expected type must be same as the * current one */ expe_cert_type = (curr_cert_type & ANY_PROXY_VERSION); /* For a limited proxy, the next one must be limited, otherwise, any * proxy language is fine */ if ( CERTISTYPE(curr_cert_type, LIMITED) ) expe_cert_type |= LIMITED; else expe_cert_type |= ALL_LANGUAGE; /* Check whether we see a VOMS extension */ vomsobj=OBJ_txt2obj(VOMS_AC_OID,0); voms_proxy_pos=X509_get_ext_by_OBJ(cert, vomsobj, -1); ASN1_OBJECT_free(vomsobj); verify_log (L_INFO, "Cert at depth %d (proxylevel %d) is a %s%s: \"%s\"", i, ++proxylevel, (voms_proxy_pos==-1 ? "" : "VOMS "), verify_certificate_type_str(curr_cert_type), cert_subjectdn); /* Check signature algorithm */ if (grid_check_sigalg(cert)<0) { rc=X509_V_ERR_CERT_SIGNATURE_FAILURE; goto failure; } /* Now check the public key strength */ grid_check_keylength(cert, 0); /* Check expire time */ time(&now); if (now < verify_asn1TimeToTimeT(X509_get_notBefore(cert))) { verify_error( oper, "Proxy certificate at proxy-level %d is not yet valid.", proxylevel); /* error will be caught later by x509_verify: don't do yet to get also more * serious errors.*/ /* rc=X509_V_ERR_CERT_NOT_YET_VALID; goto failure;*/ } if (now > verify_asn1TimeToTimeT(X509_get_notAfter(cert))) { verify_error( oper, "Proxy certificate at proxy-level %d expired.", proxylevel); /* error will be caught later by x509_verify: don't do yet to get also more * serious errors.*/ /* return X509_V_ERR_CERT_HAS_EXPIRED; * goto failure;*/ } /* Get issuer to check subject matches it */ if (!(cert_issuerdn = X509_NAME_oneline( X509_get_issuer_name( cert ), NULL, 0))) { verify_error( oper, "Couldn't get the issuer DN at proxy-level %d.", proxylevel); rc=X509_V_ERR_OUT_OF_MEM; goto failure; } len_subject = strlen( cert_subjectdn ); len_issuer = strlen( cert_issuerdn ); if (len_issuer>=len_subject || strncmp(cert_subjectdn, cert_issuerdn, len_issuer) != 0) { verify_error( oper, "Proxy subject must begin with the issuer, DN \"%s\", Issuer \"%s\"", cert_subjectdn, cert_issuerdn); rc=X509_V_ERR_SUBJECT_ISSUER_MISMATCH; goto failure; } /* Do all proxy Path Length checks */ if ( CERTISTYPE(curr_cert_type, RFC_TYPE) || CERTISTYPE(curr_cert_type, GT3_TYPE) ) { /* Exceeded CA Path Length ? */ if (proxy_path_len_countdown == 0) { verify_error(oper, "Proxy Path Length Constraint exceeded on depth %d of %d for certificate \"%s\". No Proxy certificates were expected at this stage.", i, depth, cert_subjectdn); #if OPENSSL_VERSION_NUMBER >= 0x00908000L rc=X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED; #endif goto failure; } /* Store pathlen, override when small, otherwise keep the smallest */ ex_pcpathlen=get_proxy_pathlength(cert, curr_cert_type); if (ex_pcpathlen != -1) { /* Update when proxy_path_len_countdown is the initial value * or when the PathLenConstraint is smaller then the * remembered proxy_path_len_countdown */ if ((proxy_path_len_countdown == -1) || (ex_pcpathlen < proxy_path_len_countdown)) { proxy_path_len_countdown = ex_pcpathlen; verify_log (L_DEBUG, "Cert here is: %s -> Setting proxy path len constraint to: %ld", cert_subjectdn, ex_pcpathlen); } else { /* If a path length was already issuesd, lower ca_path_len_countdown */ if (proxy_path_len_countdown != -1) proxy_path_len_countdown--; verify_log (L_DEBUG, "Cert here is: %s -> Countdown is at %ld", cert_subjectdn, proxy_path_len_countdown); } } else { /* If a path length was already issued, lower ca_path_len_countdown */ if (proxy_path_len_countdown != -1) { proxy_path_len_countdown--; verify_log (L_DEBUG, "Cert here is: %s -> Countdown is at %ld", cert_subjectdn, proxy_path_len_countdown); } } } else { /* i.e. legacy proxy */ /* Check serial numbers match for old style proxy */ cert_Serial =X509_get_serialNumber(cert); issuer_Serial=X509_get_serialNumber(sk_X509_value(chain, i+1)); if ( (serialstr=grid_get_serialStr(cert_Serial)) ) { verify_log( L_DEBUG, "Serial number: %s", serialstr); free(serialstr); } if ( (serialstr=grid_get_serialStr(issuer_Serial)) ) { verify_log( L_DEBUG, "Issuer serial number: %s", serialstr); free(serialstr); } if ( ASN1_INTEGER_cmp( cert_Serial, issuer_Serial ) ) verify_log( L_WARN, "Serial numbers do not match." ); } /* Free memory during each cycle */ free(cert_subjectdn); free(cert_issuerdn); cert_subjectdn = cert_issuerdn = NULL; } } /* When we're here we should have ended with either a proxy or an EEC */ /* If we're now a CA there's something wrong */ if (CERTISTYPE(curr_cert_type, CA)) { verify_error( oper, "No personal certificate (neither proxy or user certificate) found in the certficiate stack." ); goto failure; } /* If we're now an EEC we don't have any proxies */ if (CERTISTYPE(curr_cert_type, EEC)) verify_log( L_WARN, "No proxy certificate in certificate stack to check." ); /*success:*/ /* Return an OK (thumbs up) in the grid_X509_verify_callback() */ /* keep track of successful verifications */ ++chain_verify; return X509_V_OK; failure: free(cert_subjectdn); free(cert_issuerdn); return rc; } /** * Obtains proxy pathlength constraint for GT3 and RFC type proxies */ static long get_proxy_pathlength(X509 *cert, proxy_type_t proxy_type) { #if OPENSSL_VERSION_NUMBER < 0x00908000L PROXYCERTINFO *info_rfc=NULL; #endif PROXYCERTINFO_GT3 *info_gt3=NULL; long ex_pcpathlen=-1; if ( CERTISTYPE(proxy_type, GT3_TYPE) ) { info_gt3=X509_get_ext_d2i( cert, OBJ_txt2nid(GLOBUS_PROXY_V3_OID), NULL, NULL); if (info_gt3 && info_gt3->path_length) ex_pcpathlen=ASN1_INTEGER_get(info_gt3->path_length); PROXYCERTINFO_GT3_free(info_gt3); } else if ( CERTISTYPE(proxy_type, RFC_TYPE) ) { /* OpenSSL pre-0.9.8 has no proxy support and hence no ex_pcpathlen in X509, * then we use our own */ #if OPENSSL_VERSION_NUMBER < 0x00908000L info_rfc=X509_get_ext_d2i( cert, OBJ_txt2nid(RFC_PROXY_OID), NULL, NULL); if (info_rfc && info_rfc->path_length) ex_pcpathlen=ASN1_INTEGER_get(info_rfc->path_length); PROXYCERTINFO_free(info_rfc); #elif OPENSSL_VERSION_NUMBER < 0x010100000L ex_pcpathlen=cert->ex_pcpathlen; #else ex_pcpathlen=X509_get_proxy_pathlen(cert); #endif } /* Other types do not have proxy pathlength */ return ex_pcpathlen; } /** * Checks to see if one of the unsupported critical extensions is a valid proxy * type. * Returns X509_V_OK on success or X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION on * error */ static int grid_X509_knownCriticalExts(X509 *cert) { int i; char s[OBJ_TEXT_LEN]; X509_EXTENSION *ex; 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, OBJ_TEXT_LEN, X509_EXTENSION_get_object(ex), 1); verify_log( L_DEBUG, "Critical extension found: %s", s ); if (strcmp(s, RFC_PROXY_OID) == 0 || strcmp(s, GLOBUS_PROXY_V3_OID) == 0) return X509_V_OK; return X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION; } } return X509_V_OK; } /** * Check if we can safely discards errors about the issuer * Returns 1 when issuer is fine, 0 when no. */ static int grid_X509_check_issued_wrapper(X509_STORE_CTX *ctx,X509 *x,X509 *issuer) /* We change the default callback to use our wrapper and discard errors due to GSI proxy chains (ie where users certs act as CAs) */ { int ret; unsigned long flags; #if OPENSSL_VERSION_NUMBER >= 0x010100000L X509_STORE_CTX_verify_cb callback; #endif ret = X509_check_issued(issuer, x); if (ret == X509_V_OK) return 1; /* Non self-signed certs without signing are ok if they passed the other checks inside X509_check_issued. Is this enough? */ if ((ret == X509_V_ERR_KEYUSAGE_NO_CERTSIGN) && (X509_NAME_cmp(X509_get_subject_name(issuer), X509_get_subject_name(x)) != 0)) return 1; #if OPENSSL_VERSION_NUMBER < 0x00908000L /* If we haven't asked for issuer errors don't set ctx */ flags = ctx->flags; #else flags = X509_VERIFY_PARAM_get_flags(X509_STORE_CTX_get0_param(ctx)); #endif if (!(flags & X509_V_FLAG_CB_ISSUER_CHECK)) return 0; X509_STORE_CTX_set_error(ctx, ret); #if OPENSSL_VERSION_NUMBER < 0x010100000L ctx->current_cert = x; /* Not clear if next one is necessary... */ ctx->current_issuer = issuer; /* verify_cb() returns 1 on ok. */ return ctx->verify_cb(0, ctx); #else X509_STORE_CTX_set_current_cert(ctx, x); /* verify_cb() returns 1 on ok. */ callback = X509_STORE_CTX_get_verify_cb(ctx); return callback(0, ctx); #endif } /** * Print and verify the key strength for given certificate, on L_WARN for * smaller than given mininum keylength. For length == 0, use default 1024 * Returns -1 on error (cannot retrieve pubkey or keylength) */ static int grid_check_keylength(X509 *cert, int minlength) { const char *oper = "grid_check_keylength"; EVP_PKEY *pkey = NULL; int key_strength; int min_strength=(minlength ? minlength : 1024); int rc=-1; if ( (pkey=X509_get_pubkey(cert)) == NULL ) { verify_error( oper, "Cannot retrieve public key."); rc=-1; } else { #if OPENSSL_VERSION_NUMBER < 0x010100000L /* EVP_PKEY_id already exists since 0.9.9 but this makes it easier */ if (pkey->type == EVP_PKEY_RSA) { key_strength = BN_num_bits(pkey->pkey.rsa->n); #else if (EVP_PKEY_id(pkey) == EVP_PKEY_RSA) { key_strength = RSA_bits(EVP_PKEY_get0_RSA(pkey)); #endif if (key_strength < min_strength) verify_log(L_WARN, " Key strength too low: %d (<%d)", key_strength, min_strength); else verify_log( L_INFO, " Key strength: %d", key_strength ); rc=0; } else { verify_log( L_DEBUG, "No RSA public key found?!?" ); rc=-1; } /* Cleanup memory */ EVP_PKEY_free(pkey); } return rc; } /** * Print the signature algorithm for given certificate, on L_WARN for deprecated * algorithms * Returns 0 when still acceptable, -1 when too old or unknown */ static int grid_check_sigalg(X509 *cert) { const char *oper = "grid_check_sigalg"; const ASN1_OBJECT *algorithm; char buffer[OBJ_TEXT_LEN]; int nid; const char *sigalg_text=""; #if OPENSSL_VERSION_NUMBER < 0x010100000L algorithm = cert -> sig_alg -> algorithm; #else /* Try to get the OID */ const X509_ALGOR *x509_sig_alg; X509_get0_signature(NULL, &x509_sig_alg, cert); X509_ALGOR_get0(&algorithm, NULL, NULL, x509_sig_alg); #endif /* Get the OID for the algorithm */ OBJ_obj2txt(buffer, OBJ_TEXT_LEN, algorithm, 1); /* Try to get signature algorithm and convert to long name, * for > 1.1 can use X509_get_signature_nid(cert) */ if ( (nid = OBJ_obj2nid(algorithm)) != NID_undef ) sigalg_text=OBJ_nid2ln(nid); switch (nid) { case NID_undef: verify_error(oper, " Unknown signature algorithm %s", buffer); return -1; case NID_md2WithRSAEncryption: case NID_md4WithRSAEncryption: case NID_shaWithRSAEncryption: verify_error(oper, " Ancient signature algorithm: %s (=%s)", sigalg_text, buffer); return -1; case NID_md5WithRSAEncryption: case NID_sha1WithRSAEncryption: verify_log(L_WARN, " Deprecated signature algorithm: %s (=%s)", sigalg_text, buffer); break; default: verify_log(L_INFO, " signature algorithm: %s (=%s)", sigalg_text, buffer); break; } return 0; } /** * Converts the ASN1_INTEGER serial into a string * Returns a pointer to a buffer-to-be-freed or NULL on error */ static char *grid_get_serialStr(ASN1_INTEGER *serial) { const char *oper = "grid_get_serialStr"; BIGNUM *bn_serial; char *serialStr; if ( serial == NULL || (bn_serial = ASN1_INTEGER_to_BN(serial, NULL)) == NULL || (serialStr=BN_bn2hex(bn_serial)) == NULL ) { verify_error( oper, "Cannot convert ASN1_INTEGER serial to char *"); return NULL; } BN_clear_free(bn_serial); return serialStr; } /** * Constructs string printing the expected types of certificates in human * readable form. * Parameters: * int expected_proxy_type : bitfield of types of certificates that we * expected. * Returns: * char *message, MUST be free'd, NULL on error */ static char *grid_get_expected_proxy_string(int expected_proxy_type) { const char *oper = __func__; int prev=0, proxy=0, type=0; int len; size_t buflen=0; char *buf=NULL, *pos=NULL; if ( CERTISTYPE(expected_proxy_type, CA) ) { buflen+=strlen(CA_TEXT); prev=1; } if ( CERTISTYPE(expected_proxy_type, EEC) ) { buflen+=(prev ? 4+strlen(EEC_TEXT) : strlen(EEC_TEXT)); prev=1; } /* Handle all proxy version specially */ if ( CERTISTYPE(expected_proxy_type, ANY_PROXY_VERSION) ) { buflen+=(prev ? 4+strlen(ANY_PROXY_TEXT) : strlen(ANY_PROXY_TEXT)); prev=proxy=1; } else { /* Now check individual proxy versions */ if ( CERTISTYPE(expected_proxy_type, GT2_TYPE) ) { buflen+=(prev ? 4+strlen(GT2_TEXT) : strlen(GT2_TEXT)); prev=proxy=1; } if ( CERTISTYPE(expected_proxy_type, GT3_TYPE) ) { buflen+=(prev ? 4+strlen(GT3_TEXT) : strlen(GT3_TEXT)); prev=proxy=1; } if ( CERTISTYPE(expected_proxy_type, RFC_TYPE) ) { buflen+=(prev ? 4+strlen(RFC_TEXT) : strlen(RFC_TEXT)); prev=proxy=1; } /* Check whether we matched any type of cert */ if (prev==0) { verify_error(oper, "Cannot determine basic certificate type"); return NULL; } } if (proxy==1) { /* First handle all proxy languages combined */ if ( CERTISTYPE(expected_proxy_type, ALL_LANGUAGE) ) { buflen+=strlen(ALL_LANGUAGE_TEXT); type=1; } else { /* Now check individual proxy languages */ if ( CERTISTYPE(expected_proxy_type, IMPERSONATION) ) { buflen+=strlen(IMPERSONATION_TEXT); type=1; } if ( CERTISTYPE(expected_proxy_type, LIMITED) ) { buflen+=strlen(LIMITED_TEXT) + (type ? 3 : 0); type=1; } if ( CERTISTYPE(expected_proxy_type, INDEPENDENT) ) { buflen+=strlen(INDEPENDENT_TEXT) + (type ? 3 : 0); type=1; } if ( CERTISTYPE(expected_proxy_type, ANYLANG) ) { buflen+=strlen(ANYLANG_TEXT) + (type ? 3 : 0); type=1; } if ( CERTISTYPE(expected_proxy_type, RESTRICTED) ) { buflen+=strlen(RESTRICTED_TEXT) + (type ? 3 : 0); type=1; } /* Check whether we matched any type of language */ if (type==0) { verify_error(oper, "Cannot determine proxy language for proxy"); return NULL; } } } /* Add one for the \0 byte */ buflen++; if ( (buf=malloc(buflen)) == NULL ) { verify_error(oper, "Out of memory"); return NULL; } pos=buf; /* reset prev, proxy and type */ prev=proxy=type=0; if ( CERTISTYPE(expected_proxy_type, CA) ) { len=snprintf(pos, buflen, "%s", CA_TEXT); buflen-=(size_t)len; pos+=(size_t)len; prev=1; } if ( CERTISTYPE(expected_proxy_type, EEC) ) { len=snprintf(pos, buflen, prev ? " or %s" : "%s", EEC_TEXT); buflen-=(size_t)len; pos+=(size_t)len; prev=1; } /* Handle all proxy version specially */ if ( CERTISTYPE(expected_proxy_type, ANY_PROXY_VERSION) ) { len=snprintf(pos, buflen, prev ? " or %s" : "%s",ANY_PROXY_TEXT); buflen-=(size_t)len; pos+=(size_t)len; prev=proxy=1; } else { /* Now check individual proxy versions */ if ( CERTISTYPE(expected_proxy_type, GT2_TYPE) ) { len=snprintf(pos, buflen, prev ? " or %s" : "%s", GT2_TEXT); buflen-=(size_t)len; pos+=(size_t)len; prev=proxy=1; } if ( CERTISTYPE(expected_proxy_type, GT3_TYPE) ) { len=snprintf(pos, buflen, prev ? " or %s" : "%s", GT3_TEXT); buflen-=(size_t)len; pos+=(size_t)len; prev=proxy=1; } if ( CERTISTYPE(expected_proxy_type, RFC_TYPE) ) { len=snprintf(pos, buflen, prev ? " or %s" : "%s", RFC_TEXT); buflen-=(size_t)len; pos+=(size_t)len; prev=proxy=1; } } if (proxy==1) { /* First handle all proxy languages combined */ if ( CERTISTYPE(expected_proxy_type, ALL_LANGUAGE) ) { len=snprintf(pos, buflen, "%s", ALL_LANGUAGE_TEXT); buflen-=(size_t)len; pos+=(size_t)len; type=1; } else { /* Now check individual proxy languages */ if ( CERTISTYPE(expected_proxy_type, IMPERSONATION) ) { len=snprintf(pos, buflen, "%s", IMPERSONATION_TEXT); buflen-=(size_t)len; pos+=(size_t)len; type=1; } if ( CERTISTYPE(expected_proxy_type, LIMITED) ) { len=snprintf(pos, buflen, type ? " or%s" : "%s", LIMITED_TEXT); buflen-=(size_t)len; pos+=(size_t)len; type=1; } if ( CERTISTYPE(expected_proxy_type, INDEPENDENT) ) { len=snprintf(pos, buflen, type ? " or%s" : "%s", INDEPENDENT_TEXT); buflen-=(size_t)len; pos+=(size_t)len; type=1; } if ( CERTISTYPE(expected_proxy_type, ANYLANG) ) { len=snprintf(pos, buflen, type ? " or%s" : "%s", ANYLANG_TEXT); buflen-=(size_t)len; pos+=(size_t)len; type=1; } if ( CERTISTYPE(expected_proxy_type, RESTRICTED) ) { len=snprintf(pos, buflen, type ? " or%s" : "%s", RESTRICTED_TEXT); buflen-=(size_t)len; pos+=(size_t)len; type=1; } } } return buf; }