RSA BSAFE Micro Edition Suite

Streamlined security for mobile and embedded devices

Search  Print

verify_cb.c

/* $Id: verify_cb.c,v 1.31.6.1 2005/11/22 00:30:04 lmalmborg Exp $ */
/*
 * Copyright (C) 2002 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 <time.h>

#include "verify_cb.h"
#define MAX_SSLCERT_NAME_BUF_LEN 128

#ifndef NO_VERIFICATION
/*
 * Include the Certification Authority (CA) list which contains only
 * the subject name and public key data of certificates as the full certificate
 * is not required for verification (to reduce data size).
 */
#ifdef TEST_CA
#include "tcalist.h"
#else
#include "calist.h"
#endif

static int CA_data_init = 0;

#endif /* !NO_VERIFICATION */

#ifdef SSLC_SMALL_CODE
#define NO_PRINT_SSLCERT_NAME
#endif

int v_cb_dummy(SSL * ssl, SSLCERT *x[], int num, char *arg,
    long *verify_result)
{
    /* Accept all certificates without checking */
    return (1);
}


#ifndef NO_VERIFICATION

int v_cb(SSL * ssl, SSLCERT *x[], int num, char *arg, long *verify_result)
{
    int ret = 0; /* Function return code, initialized to failure */
    int err;
    int i;
    int idx;
    unsigned int ca_idx;
    SSLCERT_NAME *xn;
    SSLCERT_NAME *sn;
    EVP_PKEY *pubkey = NULL;
    char *p;
#ifndef NO_STDIO
#ifndef NO_PRINT_SSLCERT_NAME
    char buf[MAX_SSLCERT_NAME_BUF_LEN] = {'\0'};
#endif /* NO_PRINT_SSLCERT_NAME */
#endif /* NO_STDIO */
#ifdef SSLC_SMALL_CODE
    unsigned char *certTime = NULL;
    long timeLen = 0;
    int result = 0;
    unsigned int time_encoding = SSLCERT_ENCODING_ASN1_UTCTIME;
    unsigned int now = 0; /* Current time in seconds since 1 Jan, 1970 */
#endif /* SSLC_SMALL_CODE */
    unsigned int critical;
    /* Maximum path length allowed */
    unsigned long path_constraint=10;
    int ca_constraint;

    /*
     * For this callback, the current_cert is undefined. get_cert returns the
     * 'leaf' certificate.
     */
    if (num == 0)
    {
#ifndef NO_STDIO
        fprintf(stderr, "No certificates\n");
        fflush(stderr);
#endif
        goto end;
    }

    /*
     * Convert the binary subject names into usable #SSLCERT_NAME objects so
     * they can be compared with the objects in the certificates
     */
    if (!CA_data_init)
    {
        for (ca_idx = 0; ca_idx < CA_DATA_NUM; ca_idx++)
        {
            unsigned char *pp;

            pp = CA_data[ca_idx].namedata;
            if (SSLCERT_NAME_from_binary(&CA_data[ca_idx].name, &pp,
                CA_data[ca_idx].namedata_len) == NULL)
            {
#ifndef NO_STDIO
                fprintf(stderr, "Error - failed to convert CA name data\n");
                fflush(stderr);
#endif
                return (0);
            }
        }
        CA_data_init = 1;
    }

#ifndef NO_STDIO
    fprintf(stderr, "Verify information\n");
    fprintf(stderr, "There are %d certs in the chain\n", num);
    fflush(stderr);
#endif

    /************************************************/
    /* VERIFICATION CODE */
    /************************************************/

    /* Retrieve the first certificate and attempt to find the issuer */
    idx = 0;

    for (;;)
    {
        xn = SSLCERT_get_issuer_name(x[idx]);
#ifndef NO_STDIO
#ifndef NO_PRINT_SSLCERT_NAME
        fprintf(stdout, "Index: %d \nIssuer: %s\n", idx,
           SSLCERT_NAME_oneline(xn,buf,MAX_SSLCERT_NAME_BUF_LEN));
        fprintf(stdout, "Subject: %s\n",
           SSLCERT_NAME_oneline(SSLCERT_get_subject_name(x[idx]),buf,
               MAX_SSLCERT_NAME_BUF_LEN));
        fflush(stdout);
#endif /* NO_PRINT_SSLCERT_NAME */
#endif /* NO_STDIO */
        /* Check for the issuer in the CA list */
        for (ca_idx = 0; ca_idx < CA_DATA_NUM; ca_idx++)
        {
            if (SSLCERT_NAME_cmp(CA_data[ca_idx].name, xn) == 0)
            {
                /* A CA was located in the list */
                goto verify;
            }
        }

        /*
         * Indicates the issuer was not found in the CA list. Check the next
         * certificate in the certificate chain.
         */
        if (idx + 1 >= num)
        {
            /*
             * The last certificate has been reached, and therefore the
             * certificates cannot be verified. Return.
             */
#ifndef NO_STDIO
            fprintf(stderr, "Unable to verify certificate\n");
            fflush(stderr);
#endif
            goto end;
        }
        else
        {
            sn = SSLCERT_get_subject_name(x[idx + 1]);
            if (SSLCERT_NAME_cmp(xn, sn) != 0)
            {
                /*
                 * The next certificate in the chain is not the signer of the
                 * previous one. As the SSL implementation was not correct it
                 * will fail at this point.
                 */
#ifndef NO_STDIO
                fprintf(stderr, "Certificate chain is non-standard\n");
                fflush(stderr);
#endif
                goto end;
            }

            /* Point at this new certificate */
            idx++;
        }
    }

verify:

    /*
     * At this point, certificates x[0] .. x[idx] need to verified.
     * The CA information is in CA_data[ca_idx]. Verify x[idx] with
     * CA_data[ca_idx], x[idx-1] with x[idx], x[idx-2] with x[idx-1]
     * ... x[0] with x[1].
     */
#ifndef NO_STDIO
    fprintf(stdout, "******* ca_idx=%d idx=%d\n", ca_idx, idx);
    fflush(stdout);
#endif

    /* Load the CA key */
    p = (char *) CA_data[ca_idx].pkey;
    if ((SSLCERT_PKEY_from_PUBKEY_binary(EVP_PKEY_RSA, &pubkey,
        (unsigned char **) &p, CA_data[ca_idx].pkey_len)) == NULL)
    {
        goto end;
    }

    err = SSLCERT_verify(x[idx], pubkey);
    SSLCERT_PKEY_free(pubkey);
    pubkey = NULL;

    if (!err)
    {
#ifndef NO_STDIO
        fprintf(stderr, "Failed to verify the certificate with CA key\n");
        fflush(stderr);
#endif
        goto end;
    }


    for (i = idx; i >= 0; i--)
    {

        /*
         * Check the basic constraints on all certificates in the chain
         * except for the trusted CA and the leaf certificate
         */

        if ((i > 0) && (SSLCERT_get_basic_constraints_int(x[i], &critical,
            &path_constraint, &ca_constraint) == 1))
        {
#ifndef NO_STDIO
            fprintf(stderr,"Basic Constraints: %s : %s : MaxLen:%ld\n",
                critical ? "critical" : "not crit",
                ca_constraint ? "ca" : "no ca", path_constraint);
            fflush(stderr);
#endif
            /* If the certificate is not a CA then verification fails */
            if (!ca_constraint)
            {
#ifndef NO_STDIO
                fprintf(stderr, "Certificate at index %d is not a CA\n", i);
                fflush(stderr);
#endif
                goto end;
            }
            /*
             * Otherwise if the number of certificates in the remainder of the
             * chain exceeds the allowed length given in the path_constraint
             * then verification fails. Note that a path length of zero
             * indicates the leaf certificate must be next.
             */

            else if (path_constraint + 1 < (unsigned int)i )
            {
#ifndef NO_STDIO
                fprintf(stderr,
                    "Path longer than allowed by CA (pathlen:%d)\n", i-1);
                fflush(stderr);
#endif
                goto end;
            }

        }
#ifndef NO_STDIO
#ifndef NO_PRINT_SSLCERT_NAME
        fprintf(stdout, "Index: %d, Issuer: %s\n", i,
           SSLCERT_NAME_oneline(SSLCERT_get_issuer_name(x[i]),buf,
               MAX_SSLCERT_NAME_BUF_LEN));
        fflush(stdout);
#endif /* NO_PRINT_SSLCERT_NAME */
#endif /* NO_STDIO */
        /*
         * Ensure that the next certificate in the chained is signed
         * correctly
         */
        if (i > 0)
        {
            if ((pubkey = SSLCERT_get_pubkey(x[i])) == NULL)
            {
                goto end;
            }
            if (!SSLCERT_verify(x[i - 1], pubkey))
            {
#ifndef NO_STDIO
                fprintf(stderr, "Verify of certificate failure\n");
                fflush(stderr);
#endif
                goto end;
            }
        }

#ifdef SSLC_SMALL_CODE
        {
        /*
         * Check the certificate for time validity. Ensure notAfter and
         * notBefore are satisfied.
         */

        now = time(NULL);

        /* First check notAfter */
        if (SSLCERT_get_notAfter(x[i], &time_encoding, &certTime,
            &timeLen) == 1)
        {
            /* Check if the notAfter date is valid */

            if (SSLCERT_compare_ASN1_time((int)time_encoding, certTime,
                now, &result) == 1)
            {
                if (result == 1)
                {
                    /* notAfter time is ok */
                }
                else
                {
#ifndef NO_STDIO
                    fprintf(stderr, "certificate %d has expired\n", i);
                    fflush(stderr);
#endif
                    goto end;
                }
            }
            else
            {
#ifndef NO_STDIO
                fprintf(stderr, "Error comparing notAfter time\n");
                fflush(stderr);
#endif
                goto end;
            }
        }
        else
        {
#ifndef NO_STDIO
            fprintf(stderr, "Error getting notAfter data, certificate: %d", i);
            fflush(stderr);
#endif
            goto end;
        }

        /* Check notBefore */
        if (SSLCERT_get_notBefore(x[i], &time_encoding, &certTime,
            &timeLen) == 1)
        {

            if (SSLCERT_compare_ASN1_time((int)time_encoding, certTime,
                now, &result) == 1)
            {
                if (result == -1)
                {
                    /* notBefore time is ok */
                }
                else
                {
#ifndef NO_STDIO
                    fprintf(stderr, "certificate %d is not yet valid\n", i);
                    fflush(stderr);
#endif
                    goto end;
                }
            }
            else
            {
#ifndef NO_STDIO
                fprintf(stderr, "Error comparing notBefore time\n");
                fflush(stderr);
#endif
                goto end;
            }
        }
        else
        {
#ifndef NO_STDIO
            fprintf(stderr, "Error getting notBefore data, certificate: %d", i);
            fflush(stderr);
#endif
            goto end;
        }

        }
#endif /* SSLC_SMALL_CODE */
    }
    /* Everything seems to be OK */

    ret = 1;

end:
    return(ret);
}

void v_cb_cleanup(void)
{
    unsigned int ca_idx;

    for (ca_idx = 0; ca_idx < CA_DATA_NUM; ca_idx++)
    {
        if (CA_data[ca_idx].name != NULL)
        {
            SSLCERT_NAME_free(CA_data[ca_idx].name);
        }
    }
}

#endif /* !NO_VERIFICATION */


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