RSA BSAFE Micro Edition Suite

Streamlined security for mobile and embedded devices

Search  Print

vfy_bc.c

/* $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);
}



Copyright (c) 1999-2005 RSA Security Inc. All rights reserved. 072-001001-2100-001-000 - 2.1