| RSA BSAFE Micro Edition Suite |
Streamlined security for mobile and embedded devices |
 
![]() |
/* $Id: vfy_bc.c,v 1.22 2005/08/08 05:33:32 jlevander Exp $ */ /* * Copyright (C) 1998-2003 RSA Security Inc. * * This file shall only be used to demonstrate how to interface to an * RSA Security Inc. licensed development product. * * You have a royalty-free right to use, reproduce and distribute this * demonstration file, provided that you agree that RSA Security Inc. * has no warranty, implied or otherwise, or liability for this * demonstration file (including any modified version). This software * is provided "as is" without warranties or representations of any * kind. RSA Security disclaims all conditions and warranties, statutory * and otherwise, both express and implied, with respect to the software, * its quality and performance, including but not limited to, all * implied warranties of merchantability, fitness for a particular * purpose, title and noninfringement of third party rights. Without * limiting the foregoing, RSA Security does not warrant that the * software is error-free or that errors in the product will be * corrected. You agree that RSA Security shall not be liable for any * direct, indirect, incidental, special, consequential, punitive or * other damages whatsoever resulting from your use of this software * or any modified version. * * */ #include "r_prod.h" /* Usage help message. */ static char *vfy_bc_usage[] = { "usage: vfy_bc -trusted certs -untrusted certs\n", " -certtype encoding -certform format -bc check\n", "where:\n", " -trusted certs - List of trusted certificates (colon separated)\n", " -untrusted certs - List of untrusted certificates (colon separated)\n", " Peer cert first then its signer and so on.\n", " -certtype encoding - Cert encoding - one of X509 (default), WTLS\n", #ifdef NO_PEM " -certform format - Cert format (BIN only)\n", #else " -certform format - Cert format - one of BIN (default), PEM\n", #endif /* NO_PEM */ " -bc check - Enable basic constraints checking - one of\n", " on (default), off\n", " -time YYYYMMDDHHMMSS - Time to verify the chain with instead of the\n", " current system time.\n", #ifdef NO_SOFTWARE_CRYPTO " -no_fips140 - Use non FIPS140 crypto implementations\n", #endif /* NO_SOFTWARE_CRYPTO */ NULL }; int load_trusted_certs(BIO *bio_err, R_LIB_CTX *lib_ctx, char *certs, int cert_enc, R_FORMAT cert_form, R_CERT_CTX *cert_ctx, R_CERT_STORE *store); int load_untrusted_certs(BIO *bio_err, R_LIB_CTX *lib_ctx, char *certs, int cert_enc, R_FORMAT cert_form, R_CERT_CTX *cert_ctx, R_CERT **cert_chain, int *num_certs); /* The length of the reason for the verification failure string */ #define MAX_STR_LEN 30 /* * Main sample program entry point. * * @param argc [In] The number of arguments typed on the command line. * @param argv [In] The array of individual arguments from the command line. * * @returns R_ERROR_NONE indicates success.<br> * See @ref R_ERROR_IDS for valid values. */ int main(int argc, char **argv) { int ret = R_ERROR_NONE; /* The return value */ int res = R_VERIFY_R_NONE; /* The verify reason code */ R_RES_LIST *res_list = NULL; /* The resource list */ R_LIB_CTX *lib_ctx = NULL; /* The library context */ R_VERIFY_CTX *vfy_ctx = NULL; /* The verification context */ R_VERIFY_STATE *vfy_state = NULL; /* The verify state */ R_CERT_STORE_CTX *store_ctx = NULL; /* The certificate store * context */ R_CERT_STORE *store = NULL; /* The certificate store */ R_CERT_CTX *cert_ctx = NULL; /* The certificate context */ R_CERT *cert_chain[100]; /* The certificate chain array */ BIO *bio_err = NULL; /* The standard error output */ int verified; /* The verified certificates */ char str[MAX_STR_LEN]; /* The reason string length */ char *trusted_certs; /* The list of trusted * certificate file */ char *untrusted_certs; /* The list of untrusted * certificate files */ char *certtype; /* The type of certificates */ char *certform; /* The format of certificates */ int cert_enc; /* The identifier of the * certificate type */ R_FORMAT cert_form; /* The identifier of the * certificate format */ int num_untrusted_certs=0; /* The number of untrusted * certificates */ char *bc_str; /* The basic constraint check * parameter */ int check_bc; /* Indicates basic constraints */ /* The check required */ int bclen; /* The length of the -bc * parameter */ R_VERIFY_FLAG vfy_flag; /* The verify flag */ char *vfy_time = NULL; /* The verification time */ /* Set the defaults */ trusted_certs = NULL; untrusted_certs = NULL; certtype = "X509"; certform = "BIN"; bc_str = "on"; res_list = PRODUCT_DEFAULT_RESOURCE_LIST(); Memset(cert_chain, 0, sizeof(cert_chain)); /* Skip over program name */ argc--; argv++; /* Open standard error to report error messages */ if ((bio_err = BIO_new_fp(stderr, BIO_NOCLOSE)) == NULL) { ret = R_ERROR_ALLOC_FAILURE; goto err; } /* Process all command line arguments */ /* * This sample requires a trusted and untrusted certificate list as * arguments */ if (argc < 2) { goto bad; } while (argc >= 1) { /* The list of trusted certificates */ if (Strcmp(*argv, "-trusted") == 0) { if (--argc < 1) { BIO_printf(bio_err, "Missing list of trusted certificates\n"); goto bad; } trusted_certs = *(++argv); } /* The list of untrusted certificates */ else if (Strcmp(*argv, "-untrusted") == 0) { if (--argc < 1) { BIO_printf(bio_err, "Missing list of untrusted certificates\n"); goto bad; } untrusted_certs = *(++argv); } /* The type of certificates */ else if (Strcmp(*argv, "-certtype") == 0) { if (--argc < 1) { goto bad; } certtype = *(++argv); } /* The format of certificates */ else if (Strcmp(*argv, "-certform") == 0) { if (--argc < 1) { goto bad; } certform = *(++argv); } /* Indicate whether basic constraints checking is required */ else if (Strcmp(*argv, "-bc") == 0) { if (--argc < 1) { goto bad; } bc_str = *(++argv); } else if (Strcmp(*argv, "-time") == 0) { if (--argc < 1) { BIO_printf(bio_err, "Missing time value\n"); goto bad; } vfy_time = *(++argv); } #ifdef NO_SOFTWARE_CRYPTO /* Change to the non-FIPS resource list */ else if (Strcmp(*argv, "-no_fips140") == 0) { res_list = PRODUCT_NON_FIPS_140_MODE_RESOURCE_LIST(); } #endif /* NO_SOFTWARE_CRYPTO */ /* Unknown parameter */ else { goto bad; } argc--; argv++; } /* * Convert the string into a format type value.See @ref ENC_FORMAT for * valid values. */ if ((ret = R_CERT_TYPE_from_string(certtype, &cert_enc)) != R_ERROR_NONE) { BIO_printf(bio_err, "Unknown certificate type %s\n", certtype); goto bad; } /* Convert the string into a format type value */ if ((ret = R_FORMAT_from_string(certform, &cert_form)) != R_ERROR_NONE) { BIO_printf(bio_err, "Unknown certificate format %s\n", certform); goto bad; } /* * Convert the basic constraints check string to check string type * value */ bclen = Strlen(bc_str); if (Strncmp(bc_str, "on", bclen) == 0) { check_bc = 1; } else if (Strncmp(bc_str, "off", bclen) == 0) { check_bc = 0; } else { BIO_printf(bio_err, "Unknown basic constraints check parameter %s\n", bc_str); goto bad; } /* Display the help menu if an invalid command line option was entered */ if (0) { char **pp; bad: for (pp = vfy_bc_usage; (*pp != NULL); pp++) { BIO_printf(bio_err, *pp); } goto err; } /* * Create the library context. Add in a time verification callback to the * resource list and create a library context with the resource list. */ /* * Add the default certificate time verification callback to the resource * list so the verification routines have access to the callback */ if ((ret = R_VERIFY_set_time_verify_func(&res_list, R_VERIFY_DETAILS_get_def_time_verify_func)) != R_ERROR_NONE) { goto err; } /* * Create a library context to provide access to all configurable aspects * of the library */ if ((ret = PRODUCT_LIBRARY_NEW(res_list, 0, &lib_ctx)) != R_ERROR_NONE) { goto err; } /* * Create the verification state from a verification context. Set the * required verification options and the connection object. */ /* Create a new verification context */ if ((ret = R_VERIFY_CTX_new(lib_ctx, R_RES_FLAG_DEF, &vfy_ctx)) != R_ERROR_NONE) { goto err; } /* Set the default verification flags */ vfy_flag = R_VERIFY_FLAG_COMPLETE_CHAIN | R_VERIFY_FLAG_VERIFY_SIGNATURE | R_VERIFY_FLAG_VERIFY_FULL_CHAIN | R_VERIFY_FLAG_VERIFY_NOT_BEFORE | R_VERIFY_FLAG_SORT_CHAIN | R_VERIFY_FLAG_VERIFY_NOT_AFTER; if (check_bc) /* Set the basic constraints check flag if required */ { vfy_flag |= R_VERIFY_FLAG_VERIFY_BASIC_CONSTRAINTS; } if ((ret = R_VERIFY_CTX_set_flag(vfy_ctx, vfy_flag)) != R_ERROR_NONE) { goto err; } if (vfy_time != NULL) { if ((ret = R_VERIFY_CTX_set_info(vfy_ctx, R_VERIFY_CTX_INFO_TIME, vfy_time)) != R_ERROR_NONE) { goto err; } } /* * Create a new verification state. The state will hold the entire * verification process, including the certificates and the result. */ if ((ret = R_VERIFY_STATE_new(vfy_ctx, R_RES_FLAG_DEF, &vfy_state)) != R_ERROR_NONE) { goto err; } /* * Set the connection object so the state has a reference back to the * owner. In this case, there is no reference, so set the type to NONE. */ if ((ret = R_VERIFY_STATE_set_conn(vfy_state, R_VERIFY_CONN_TYPE_NONE, NULL, NULL)) != R_ERROR_NONE) { goto err; } /* * Create the trusted certificate store. Load the trusted certificate and * add it to the new certificate store. */ /* Create a new certificate store context */ if ((ret = R_CERT_STORE_CTX_new(lib_ctx, R_RES_FLAG_DEF, &store_ctx)) != R_ERROR_NONE) { BIO_printf(bio_err, "Store context new failure\n"); goto err; } /* Create a new certificate store */ if ((ret = R_CERT_STORE_new(store_ctx, &store)) != R_ERROR_NONE) { BIO_printf(bio_err, "Store object new failure\n"); goto err; } /* Create a context in order to create the X.509 certificates */ if ((ret = R_CERT_CTX_new(lib_ctx, R_RES_FLAG_DEF, cert_enc, &cert_ctx)) != R_ERROR_NONE) { BIO_printf(bio_err, "Certificate context new failure\n"); goto err; } /* Load the trusted certificates into the trusted certificate store */ if ((ret = load_trusted_certs(bio_err, lib_ctx, trusted_certs, cert_enc, cert_form, cert_ctx, store)) != R_ERROR_NONE) { BIO_printf(bio_err, "Load trusted certificates failure\n"); goto err; } /* * Create the untrusted certificate chain. Load the untrusted certificates * and create an ordered array, with the peer certificate first and then * its signer and so on. * For example: * - cert_chain[0] = The client certificate signed by the second CA. * - cert_chain[1] = The second CA certificate signed by the first CA. * - cert_chain[2] = The first CA certificate signed by Root. */ /* Load the untrusted certificates into the certificate chain */ if ((ret = load_untrusted_certs(bio_err, lib_ctx, untrusted_certs, cert_enc, cert_form, cert_ctx, cert_chain, &num_untrusted_certs)) != R_ERROR_NONE) { BIO_printf(bio_err, "Load untrusted certificates failure\n"); goto err; } /* * Verify the certificate chain. Populate the verification state with both * trusted and untrusted certificates and verify the chain. */ /* Populate the verification state object using the certificate chain */ ret = R_VERIFY_STATE_populate_with_R_CERT(vfy_state, store_ctx, cert_chain, num_untrusted_certs, R_VERIFY_STATE_MANAGE_DATA); Memset(cert_chain, 0, sizeof(cert_chain)); if (ret != R_ERROR_NONE) { goto err; } /* Verify the certificate chain */ if ((ret = R_VERIFY_STATE_verify(vfy_state, &verified)) != R_ERROR_NONE) { goto err; } if(!verified) { /* Check for verification errors */ if ((ret = R_VERIFY_STATE_get_reason(vfy_state, &res)) != R_ERROR_NONE) { BIO_printf(bio_err, "Verification error: reason unknown\n"); goto err; } if (res != R_VERIFY_R_NONE) { if ((ret = R_VERIFY_REASON_to_string(res, MAX_STR_LEN, str)) != R_ERROR_NONE) { BIO_printf(bio_err, "Verification error: reason unknown\n"); } else { BIO_printf(bio_err, "Verification error: reason %d (%s)\n", res, str); } ret=1; goto done; } } BIO_printf(bio_err, "\nVerified certificate\n"); ret = 0; goto done; err: /* * Clean up. Report errors if there is an output stream using both the * error and the string representation. Destroy the dynamically allocated * objects and return an exit code. */ if ((ret != R_ERROR_NONE) && (bio_err != NULL)) { BIO_printf(bio_err, "Error: (%d) %s\n", ret, R_LIB_CTX_get_error_string(lib_ctx, R_RES_MOD_ID_LIBRARY, ret)); } done: while(num_untrusted_certs-- > 0) { if (cert_chain[num_untrusted_certs] != NULL) { R_CERT_free(cert_chain[num_untrusted_certs]); } } if (store != NULL) { R_CERT_STORE_free(store); } if (store_ctx != NULL) { R_CERT_STORE_CTX_free(store_ctx); } if (vfy_state != NULL) { R_VERIFY_STATE_free(vfy_state); } /* * The certificate context cannot be freed until the store and the state * have been emptied as the certificates in the store and verification * state hold a reference back to the certificate context */ if (cert_ctx != NULL) { R_CERT_CTX_free(cert_ctx); } if (vfy_ctx != NULL) { R_VERIFY_CTX_free(vfy_ctx); } if (lib_ctx != NULL) { PRODUCT_LIBRARY_FREE(lib_ctx); } if (bio_err != NULL) { BIO_free(bio_err); } return(R_ERROR_EXIT_CODE(ret)); } /* * Creates and populates the certificate store if there are any certificates. * * @param bio_err [In] The BIO for error messages. * @param lib_ctx [In] The library context. * @param certs [In] A colon separated list of certificate files. * @param cert_enc [In] The certificate type. * @param cert_form [In] The certificate format. * @param cert_ctx [In] The certificate context. * @param store [In] The certificate store containing * any loaded certificates. * * @returns R_ERROR_NONE indicates success. * See @ref R_ERROR_IDS for valid values. */ int load_trusted_certs(BIO *bio_err, R_LIB_CTX *lib_ctx, char *certs, int cert_enc, R_FORMAT cert_form, R_CERT_CTX *cert_ctx, R_CERT_STORE *store_obj) { R_CERT *cert = NULL; char *str; int ret = R_ERROR_NONE; /* No action needs to be taken if no certificates have been passed in */ if (certs == NULL) { goto end; } /* Add all the certificates into the store */ str = strtok(certs, ":"); while (str != NULL) { /* Read the certificate from file */ if ((ret = R_CERT_read_file(cert_ctx, str, cert_enc, cert_form, &cert)) != R_ERROR_NONE) { BIO_printf(bio_err, "Certificate from file failure: %s\n", str); goto end; } if ((ret = R_CERT_STORE_set_cert(store_obj, cert, NULL)) != R_ERROR_NONE) { BIO_printf(bio_err, "Add certificate to store failure\n"); goto end; } /* The store object now owns the certificate. */ cert = NULL; /* * Set the trust level of the certificate. For example, all * certificates loaded are trusted and used for verification. */ if ((ret = R_CERT_STORE_set_cert_state(store_obj, R_CERT_STORE_STATE_TRUSTED_CERTIFICATE)) != R_ERROR_NONE) { BIO_printf(bio_err, "Set trust failure\n"); goto end; } if ((ret = R_CERT_STORE_add(store_obj)) != R_ERROR_NONE) { BIO_printf(bio_err, "Add certificate failure\n"); goto end; } /* * Add the certificate into the store and allow the store to manage * the data so it does not need to be freed by the application */ if ((ret = R_CERT_STORE_init(store_obj)) != R_ERROR_NONE) { BIO_printf(bio_err, "Initialization of store object failure\n"); goto end; } /* Retrieve the next file */ str = strtok(NULL, ":"); } end: if (cert != NULL) { R_CERT_free(cert); } return(ret); } /* * Adds the client certificate into the array followed by the CA * certificates. Ensure the chain is completed in the correct order. * * @param bio_err [In] The BIO for error messages. * @param lib_ctx [In] The library context. * @param certs [In] A colon separated list of certificate files. * @param cert_enc [In] The certificate type string. * @param cert_form [In] The certificate format string. * @param cert_ctx [In] The certificate context. * @param cert_chain [In] The certificate store context containing * any loaded certificates. * @param num_certs [Out] The number of loaded certificates. * * @returns R_ERROR_NONE indicates success. * See @ref R_ERROR_IDS for valid values. */ int load_untrusted_certs(BIO *bio_err, R_LIB_CTX *lib_ctx, char *certs, int cert_enc, R_FORMAT cert_form, R_CERT_CTX *cert_ctx, R_CERT **cert_chain, int *num_certs) { R_CERT *cert = NULL; char *str; int ret = R_ERROR_NONE; /* No action needs to be taken if no certificates have been passed in */ if (certs == NULL) { goto end; } /* Add all the certificates into the store */ *num_certs = 0; str = strtok(certs, ":"); while (str != NULL) { /* Read the certificate from file */ if ((ret = R_CERT_read_file(cert_ctx, str, cert_enc, cert_form, &cert)) != R_ERROR_NONE) { BIO_printf(bio_err, "Certificate from file failure: %s\n", str); goto end; } cert_chain[*num_certs] = cert; /* The chain now owns the certificate */ cert = NULL; /* Retrieve the next file */ str = strtok(NULL, ":"); (*num_certs)++; } end: if (cert != NULL) { R_CERT_free(cert); } return(ret); }