/*
 * PKCS #11 FIPS Power-Up Self Test.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* $Id: fipstest.c,v 1.31 2012/06/28 17:55:06 rrelyea%redhat.com Exp $ */

#include "seccomon.h"
#include "lgdb.h"
#include "blapi.h"

/*
 * different platforms have different ways of calling and initial entry point
 * when the dll/.so is loaded. Most platforms support either a posix pragma
 * or the GCC attribute. Some platforms suppor a pre-defined name, and some
 * platforms have a link line way of invoking this function.
 */

/* The pragma */
#if defined(USE_INIT_PRAGMA)
#pragma init(lg_startup_tests)
#endif

/* GCC Attribute */
#if defined(__GNUC__) && !defined(NSS_NO_INIT_SUPPORT)
#define INIT_FUNCTION __attribute__((constructor))
#else
#define INIT_FUNCTION
#endif

static void INIT_FUNCTION lg_startup_tests(void);

/* Windows pre-defined entry */
#if defined(XP_WIN) && !defined(NSS_NO_INIT_SUPPORT)
#include <windows.h>

BOOL WINAPI DllMain(
    HINSTANCE hinstDLL, // handle to DLL module
    DWORD fdwReason,    // reason for calling function
    LPVOID lpReserved)  // reserved
{
    // Perform actions based on the reason for calling.
    switch (fdwReason) {
        case DLL_PROCESS_ATTACH:
            // Initialize once for each new process.
            // Return FALSE to fail DLL load.
            lg_startup_tests();
            break;

        case DLL_THREAD_ATTACH:
            // Do thread-specific initialization.
            break;

        case DLL_THREAD_DETACH:
            // Do thread-specific cleanup.
            break;

        case DLL_PROCESS_DETACH:
            // Perform any necessary cleanup.
            break;
    }
    return TRUE; // Successful DLL_PROCESS_ATTACH.
}
#endif

static PRBool lg_self_tests_ran = PR_FALSE;
static PRBool lg_self_tests_success = PR_FALSE;

static void
lg_local_function(void)
{
}

/*
 * This function is called at dll load time, the code tha makes this
 * happen is platform specific on defined above.
 */
static void
lg_startup_tests(void)
{
    const char *libraryName = LG_LIB_NAME;

    PORT_Assert(!lg_self_tests_ran);
    PORT_Assert(!lg_self_tests_success);
    lg_self_tests_ran = PR_TRUE;
    lg_self_tests_success = PR_FALSE; /* just in case */

    /* no self tests required for the legacy db, only the integrity check */
    /* check the integrity of our shared library */
    if (!BLAPI_SHVerify(libraryName, (PRFuncPtr)&lg_local_function)) {
        /* something is wrong with the library, fail without enabling
         * the fips token */
        return;
    }
    /* FIPS product has been installed and is functioning, allow
     * the module to operate in fips mode */
    lg_self_tests_success = PR_TRUE;
}

PRBool
lg_FIPSEntryOK()
{
#ifdef NSS_NO_INIT_SUPPORT
    /* this should only be set on platforms that can't handle one of the INIT
     * schemes.  This code allows those platforms to continue to function,
     * though they don't meet the strict NIST requirements. If NO_INIT_SUPPORT
     * is not set, and init support has not been properly enabled, softken
     * will always fail because of the test below */
    if (!lg_self_tests_ran) {
        lg_startup_tests();
    }
#endif
    return lg_self_tests_success;
}
