/* ==================================================================== * Copyright (c) 2003 The OpenSSL Project. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. All advertising materials mentioning features or use of this * software must display the following acknowledgment: * "This product includes software developed by the OpenSSL Project * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" * * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to * endorse or promote products derived from this software without * prior written permission. For written permission, please contact * openssl-core@openssl.org. * * 5. Products derived from this software may not be called "OpenSSL" * nor may "OpenSSL" appear in their names without prior written * permission of the OpenSSL Project. * * 6. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by the OpenSSL Project * for use in the OpenSSL Toolkit (http://www.openssl.org/)" * * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fips_locl.h" #ifdef OPENSSL_FIPS # include # include "internal/thread_once.h" # include "crypto/rand.h" # ifndef PATH_MAX # define PATH_MAX 1024 # endif static int fips_selftest_fail = 0; static int fips_mode = 0; static int fips_started = 0; static int fips_post = 0; static int fips_is_owning_thread(void); static int fips_set_owning_thread(void); static int fips_clear_owning_thread(void); static CRYPTO_RWLOCK *fips_lock = NULL; static CRYPTO_RWLOCK *fips_owning_lock = NULL; static CRYPTO_ONCE fips_lock_init = CRYPTO_ONCE_STATIC_INIT; DEFINE_RUN_ONCE_STATIC(do_fips_lock_init) { fips_lock = CRYPTO_THREAD_lock_new(); fips_owning_lock = CRYPTO_THREAD_lock_new(); return fips_lock != NULL && fips_owning_lock != NULL; } # define fips_w_lock() CRYPTO_THREAD_write_lock(fips_lock) # define fips_w_unlock() CRYPTO_THREAD_unlock(fips_lock) # define fips_r_lock() CRYPTO_THREAD_read_lock(fips_lock) # define fips_r_unlock() CRYPTO_THREAD_unlock(fips_lock) static void fips_set_mode(int onoff) { int owning_thread = fips_is_owning_thread(); if (fips_started) { if (!owning_thread) fips_w_lock(); fips_mode = onoff; if (!owning_thread) fips_w_unlock(); } } int FIPS_module_mode(void) { int ret = 0; int owning_thread = fips_is_owning_thread(); if (fips_started) { if (!owning_thread) fips_r_lock(); ret = fips_mode; if (!owning_thread) fips_r_unlock(); } return ret; } /* just a compat symbol - return NULL */ int FIPS_selftest_failed(void) { int ret = 0; if (fips_started) { int owning_thread = fips_is_owning_thread(); if (!owning_thread) fips_r_lock(); ret = fips_selftest_fail; if (!owning_thread) fips_r_unlock(); } return ret; } /* Selftest failure fatal exit routine. This will be called * during *any* cryptographic operation. It has the minimum * overhead possible to avoid too big a performance hit. */ void FIPS_selftest_check(void) { if (fips_selftest_fail) { OpenSSLDie(__FILE__, __LINE__, "FATAL FIPS SELFTEST FAILURE"); } } void fips_set_selftest_fail(void) { fips_selftest_fail = 1; } int fips_in_post(void) { return fips_post; } /* we implement what libfipscheck does ourselves */ static int get_library_path(const char *libname, const char *symbolname, char *path, size_t pathlen) { Dl_info info; void *dl, *sym; int rv = -1; dl = dlopen(libname, RTLD_LAZY); if (dl == NULL) { return -1; } sym = dlsym(dl, symbolname); if (sym != NULL && dladdr(sym, &info)) { strncpy(path, info.dli_fname, pathlen - 1); path[pathlen - 1] = '\0'; rv = 0; } dlclose(dl); return rv; } static const char conv[] = "0123456789abcdef"; static char *bin2hex(void *buf, size_t len) { char *hex, *p; unsigned char *src = buf; hex = malloc(len * 2 + 1); if (hex == NULL) return NULL; p = hex; while (len > 0) { unsigned c; c = *src; src++; *p = conv[c >> 4]; ++p; *p = conv[c & 0x0f]; ++p; --len; } *p = '\0'; return hex; } # define HMAC_PREFIX "." # ifndef HMAC_SUFFIX # define HMAC_SUFFIX ".hmac" # endif # define READ_BUFFER_LENGTH 16384 static char *make_hmac_path(const char *origpath) { char *path, *p; const char *fn; path = malloc(sizeof(HMAC_PREFIX) + sizeof(HMAC_SUFFIX) + strlen(origpath)); if (path == NULL) { return NULL; } fn = strrchr(origpath, '/'); if (fn == NULL) { fn = origpath; } else { ++fn; } strncpy(path, origpath, fn - origpath); p = path + (fn - origpath); p = stpcpy(p, HMAC_PREFIX); p = stpcpy(p, fn); p = stpcpy(p, HMAC_SUFFIX); return path; } static const char hmackey[] = "orboDeJITITejsirpADONivirpUkvarP"; static int compute_file_hmac(const char *path, void **buf, size_t *hmaclen) { FILE *f = NULL; int rv = -1; unsigned char rbuf[READ_BUFFER_LENGTH]; size_t len; unsigned int hlen; HMAC_CTX *c; c = HMAC_CTX_new(); if (c == NULL) return rv; f = fopen(path, "r"); if (f == NULL) { goto end; } if (HMAC_Init_ex(c, hmackey, sizeof(hmackey) - 1, EVP_sha256(), NULL) <= 0) { goto end; } while ((len = fread(rbuf, 1, sizeof(rbuf), f)) != 0) { if (HMAC_Update(c, rbuf, len) <= 0) { goto end; } } len = sizeof(rbuf); /* reuse rbuf for hmac */ if (HMAC_Final(c, rbuf, &hlen) <= 0) { goto end; } *buf = malloc(hlen); if (*buf == NULL) { goto end; } *hmaclen = hlen; memcpy(*buf, rbuf, hlen); rv = 0; end: HMAC_CTX_free(c); if (f) fclose(f); return rv; } static int FIPSCHECK_verify(const char *path) { int rv = 0; FILE *hf; char *hmacpath, *p; char *hmac = NULL; size_t n; hmacpath = make_hmac_path(path); if (hmacpath == NULL) return 0; hf = fopen(hmacpath, "r"); if (hf == NULL) { free(hmacpath); return 0; } if (getline(&hmac, &n, hf) > 0) { void *buf; size_t hmaclen; char *hex; if ((p = strchr(hmac, '\n')) != NULL) *p = '\0'; if (compute_file_hmac(path, &buf, &hmaclen) < 0) { rv = -4; goto end; } if ((hex = bin2hex(buf, hmaclen)) == NULL) { free(buf); rv = -5; goto end; } if (strcmp(hex, hmac) != 0) { rv = -1; } free(buf); free(hex); } else { rv = -1; } end: free(hmac); free(hmacpath); fclose(hf); if (rv < 0) return 0; /* check successful */ return 1; } static int verify_checksums(void) { int rv; char path[PATH_MAX + 1]; char *p; /* we need to avoid dlopening libssl, assume both libcrypto and libssl are in the same directory */ rv = get_library_path("libcrypto.so." SHLIB_VERSION_NUMBER, "FIPS_mode_set", path, sizeof(path)); if (rv < 0) return 0; rv = FIPSCHECK_verify(path); if (!rv) return 0; /* replace libcrypto with libssl */ while ((p = strstr(path, "libcrypto.so")) != NULL) { p = stpcpy(p, "libssl"); memmove(p, p + 3, strlen(p + 2)); } rv = FIPSCHECK_verify(path); if (!rv) return 0; return 1; } # ifndef FIPS_MODULE_PATH # define FIPS_MODULE_PATH "/etc/system-fips" # endif int FIPS_module_installed(void) { int rv; rv = access(FIPS_MODULE_PATH, F_OK); if (rv < 0 && errno != ENOENT) rv = 0; /* Installed == true */ return !rv || FIPS_module_mode(); } int FIPS_module_mode_set(int onoff) { int ret = 0; if (!RUN_ONCE(&fips_lock_init, do_fips_lock_init)) return 0; fips_w_lock(); fips_started = 1; fips_set_owning_thread(); if (onoff) { fips_selftest_fail = 0; /* Don't go into FIPS mode twice, just so we can do automagic seeding */ if (FIPS_module_mode()) { FIPSerr(FIPS_F_FIPS_MODULE_MODE_SET, FIPS_R_FIPS_MODE_ALREADY_SET); fips_selftest_fail = 1; ret = 0; goto end; } # ifdef OPENSSL_IA32_SSE2 { extern unsigned int OPENSSL_ia32cap_P[2]; if ((OPENSSL_ia32cap_P[0] & (1 << 25 | 1 << 26)) != (1 << 25 | 1 << 26)) { FIPSerr(FIPS_F_FIPS_MODULE_MODE_SET, FIPS_R_UNSUPPORTED_PLATFORM); fips_selftest_fail = 1; ret = 0; goto end; } } # endif fips_post = 1; if (!FIPS_selftest()) { fips_selftest_fail = 1; ret = 0; goto end; } if (!verify_checksums()) { FIPSerr(FIPS_F_FIPS_MODULE_MODE_SET, FIPS_R_FINGERPRINT_DOES_NOT_MATCH); fips_selftest_fail = 1; ret = 0; goto end; } fips_post = 0; fips_set_mode(onoff); /* force RNG reseed with entropy from getrandom() on next call */ rand_force_reseed(); ret = 1; goto end; } fips_set_mode(0); fips_selftest_fail = 0; ret = 1; end: fips_clear_owning_thread(); fips_w_unlock(); return ret; } static CRYPTO_THREAD_ID fips_threadid; static int fips_thread_set = 0; static int fips_is_owning_thread(void) { int ret = 0; if (fips_started) { CRYPTO_THREAD_read_lock(fips_owning_lock); if (fips_thread_set) { CRYPTO_THREAD_ID cur = CRYPTO_THREAD_get_current_id(); if (CRYPTO_THREAD_compare_id(fips_threadid, cur)) ret = 1; } CRYPTO_THREAD_unlock(fips_owning_lock); } return ret; } int fips_set_owning_thread(void) { int ret = 0; if (fips_started) { CRYPTO_THREAD_write_lock(fips_owning_lock); if (!fips_thread_set) { fips_threadid = CRYPTO_THREAD_get_current_id(); ret = 1; fips_thread_set = 1; } CRYPTO_THREAD_unlock(fips_owning_lock); } return ret; } int fips_clear_owning_thread(void) { int ret = 0; if (fips_started) { CRYPTO_THREAD_write_lock(fips_owning_lock); if (fips_thread_set) { CRYPTO_THREAD_ID cur = CRYPTO_THREAD_get_current_id(); if (CRYPTO_THREAD_compare_id(fips_threadid, cur)) fips_thread_set = 0; } CRYPTO_THREAD_unlock(fips_owning_lock); } return ret; } #endif