853 lines
22 KiB
C
853 lines
22 KiB
C
|
/* fips.c - FIPS mode management
|
|||
|
* Copyright (C) 2008 Free Software Foundation, Inc.
|
|||
|
*
|
|||
|
* This file is part of Libgcrypt.
|
|||
|
*
|
|||
|
* Libgcrypt is free software; you can redistribute it and/or modify
|
|||
|
* it under the terms of the GNU Lesser General Public License as
|
|||
|
* published by the Free Software Foundation; either version 2.1 of
|
|||
|
* the License, or (at your option) any later version.
|
|||
|
*
|
|||
|
* Libgcrypt is distributed in the hope that it will be useful,
|
|||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|||
|
* GNU Lesser General Public License for more details.
|
|||
|
*
|
|||
|
* You should have received a copy of the GNU Lesser General Public
|
|||
|
* License along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|||
|
*/
|
|||
|
|
|||
|
#include <config.h>
|
|||
|
#include <stdio.h>
|
|||
|
#include <stdlib.h>
|
|||
|
#include <errno.h>
|
|||
|
#include <unistd.h>
|
|||
|
#include <string.h>
|
|||
|
#ifdef ENABLE_HMAC_BINARY_CHECK
|
|||
|
# include <dlfcn.h>
|
|||
|
#endif
|
|||
|
#ifdef HAVE_SYSLOG
|
|||
|
# include <syslog.h>
|
|||
|
#endif /*HAVE_SYSLOG*/
|
|||
|
|
|||
|
#include "g10lib.h"
|
|||
|
#include "ath.h"
|
|||
|
#include "cipher-proto.h"
|
|||
|
#include "hmac256.h"
|
|||
|
|
|||
|
|
|||
|
/* The name of the file used to foce libgcrypt into fips mode. */
|
|||
|
#define FIPS_FORCE_FILE "/etc/gcrypt/fips_enabled"
|
|||
|
|
|||
|
|
|||
|
/* The states of the finite state machine used in fips mode. */
|
|||
|
enum module_states
|
|||
|
{
|
|||
|
/* POWEROFF cannot be represented. */
|
|||
|
STATE_POWERON = 0,
|
|||
|
STATE_INIT,
|
|||
|
STATE_SELFTEST,
|
|||
|
STATE_OPERATIONAL,
|
|||
|
STATE_ERROR,
|
|||
|
STATE_FATALERROR,
|
|||
|
STATE_SHUTDOWN
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/* Flag telling whether we are in fips mode. It uses inverse logic so
|
|||
|
that fips mode is the default unless changed by the initialization
|
|||
|
code. To check whether fips mode is enabled, use the function
|
|||
|
fips_mode()! */
|
|||
|
static int no_fips_mode_required;
|
|||
|
|
|||
|
/* Flag to indicate that we are in the enforced FIPS mode. */
|
|||
|
static int enforced_fips_mode;
|
|||
|
|
|||
|
/* If this flag is set, the application may no longer assume that the
|
|||
|
process is running in FIPS mode. This flag is protected by the
|
|||
|
FSM_LOCK. */
|
|||
|
static int inactive_fips_mode;
|
|||
|
|
|||
|
/* This is the lock we use to protect the FSM. */
|
|||
|
static ath_mutex_t fsm_lock;
|
|||
|
|
|||
|
/* The current state of the FSM. The whole state machinery is only
|
|||
|
used while in fips mode. Change this only while holding fsm_lock. */
|
|||
|
static enum module_states current_state;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
static void fips_new_state (enum module_states new_state);
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/* Convert lowercase hex digits; assumes valid hex digits. */
|
|||
|
#define loxtoi_1(p) (*(p) <= '9'? (*(p)- '0'): (*(p)-'a'+10))
|
|||
|
#define loxtoi_2(p) ((loxtoi_1(p) * 16) + loxtoi_1((p)+1))
|
|||
|
|
|||
|
/* Returns true if P points to a lowercase hex digit. */
|
|||
|
#define loxdigit_p(p) !!strchr ("01234567890abcdef", *(p))
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/* Check whether the OS is in FIPS mode and record that in a module
|
|||
|
local variable. If FORCE is passed as true, fips mode will be
|
|||
|
enabled anyway. Note: This function is not thread-safe and should
|
|||
|
be called before any threads are created. This function may only
|
|||
|
be called once. */
|
|||
|
void
|
|||
|
_gcry_initialize_fips_mode (int force)
|
|||
|
{
|
|||
|
static int done;
|
|||
|
gpg_error_t err;
|
|||
|
|
|||
|
/* Make sure we are not accidently called twice. */
|
|||
|
if (done)
|
|||
|
{
|
|||
|
if ( fips_mode () )
|
|||
|
{
|
|||
|
fips_new_state (STATE_FATALERROR);
|
|||
|
fips_noreturn ();
|
|||
|
}
|
|||
|
/* If not in fips mode an assert is sufficient. */
|
|||
|
gcry_assert (!done);
|
|||
|
}
|
|||
|
done = 1;
|
|||
|
|
|||
|
/* If the calling application explicitly requested fipsmode, do so. */
|
|||
|
if (force)
|
|||
|
{
|
|||
|
gcry_assert (!no_fips_mode_required);
|
|||
|
goto leave;
|
|||
|
}
|
|||
|
|
|||
|
/* For testing the system it is useful to override the system
|
|||
|
provided detection of the FIPS mode and force FIPS mode using a
|
|||
|
file. The filename is hardwired so that there won't be any
|
|||
|
confusion on whether /etc/gcrypt/ or /usr/local/etc/gcrypt/ is
|
|||
|
actually used. The file itself may be empty. */
|
|||
|
if ( !access (FIPS_FORCE_FILE, F_OK) )
|
|||
|
{
|
|||
|
gcry_assert (!no_fips_mode_required);
|
|||
|
goto leave;
|
|||
|
}
|
|||
|
|
|||
|
/* Checking based on /proc file properties. */
|
|||
|
{
|
|||
|
static const char procfname[] = "/proc/sys/crypto/fips_enabled";
|
|||
|
FILE *fp;
|
|||
|
int saved_errno;
|
|||
|
|
|||
|
fp = fopen (procfname, "r");
|
|||
|
if (fp)
|
|||
|
{
|
|||
|
char line[256];
|
|||
|
|
|||
|
if (fgets (line, sizeof line, fp) && atoi (line))
|
|||
|
{
|
|||
|
/* System is in fips mode. */
|
|||
|
fclose (fp);
|
|||
|
gcry_assert (!no_fips_mode_required);
|
|||
|
goto leave;
|
|||
|
}
|
|||
|
fclose (fp);
|
|||
|
}
|
|||
|
else if ((saved_errno = errno) != ENOENT
|
|||
|
&& saved_errno != EACCES
|
|||
|
&& !access ("/proc/version", F_OK) )
|
|||
|
{
|
|||
|
/* Problem reading the fips file despite that we have the proc
|
|||
|
file system. We better stop right away. */
|
|||
|
log_info ("FATAL: error reading `%s' in libgcrypt: %s\n",
|
|||
|
procfname, strerror (saved_errno));
|
|||
|
#ifdef HAVE_SYSLOG
|
|||
|
syslog (LOG_USER|LOG_ERR, "Libgcrypt error: "
|
|||
|
"reading `%s' failed: %s - abort",
|
|||
|
procfname, strerror (saved_errno));
|
|||
|
#endif /*HAVE_SYSLOG*/
|
|||
|
abort ();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Fips not not requested, set flag. */
|
|||
|
no_fips_mode_required = 1;
|
|||
|
|
|||
|
leave:
|
|||
|
if (!no_fips_mode_required)
|
|||
|
{
|
|||
|
/* Yes, we are in FIPS mode. */
|
|||
|
FILE *fp;
|
|||
|
|
|||
|
/* Intitialize the lock to protect the FSM. */
|
|||
|
err = ath_mutex_init (&fsm_lock);
|
|||
|
if (err)
|
|||
|
{
|
|||
|
/* If that fails we can't do anything but abort the
|
|||
|
process. We need to use log_info so that the FSM won't
|
|||
|
get involved. */
|
|||
|
log_info ("FATAL: failed to create the FSM lock in libgcrypt: %s\n",
|
|||
|
strerror (err));
|
|||
|
#ifdef HAVE_SYSLOG
|
|||
|
syslog (LOG_USER|LOG_ERR, "Libgcrypt error: "
|
|||
|
"creating FSM lock failed: %s - abort",
|
|||
|
strerror (err));
|
|||
|
#endif /*HAVE_SYSLOG*/
|
|||
|
abort ();
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* If the FIPS force files exists, is readable and has a number
|
|||
|
!= 0 on its first line, we enable the enforced fips mode. */
|
|||
|
fp = fopen (FIPS_FORCE_FILE, "r");
|
|||
|
if (fp)
|
|||
|
{
|
|||
|
char line[256];
|
|||
|
|
|||
|
if (fgets (line, sizeof line, fp) && atoi (line))
|
|||
|
enforced_fips_mode = 1;
|
|||
|
fclose (fp);
|
|||
|
}
|
|||
|
|
|||
|
/* Now get us into the INIT state. */
|
|||
|
fips_new_state (STATE_INIT);
|
|||
|
|
|||
|
}
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
static void
|
|||
|
lock_fsm (void)
|
|||
|
{
|
|||
|
gpg_error_t err;
|
|||
|
|
|||
|
err = ath_mutex_lock (&fsm_lock);
|
|||
|
if (err)
|
|||
|
{
|
|||
|
log_info ("FATAL: failed to acquire the FSM lock in libgrypt: %s\n",
|
|||
|
strerror (err));
|
|||
|
#ifdef HAVE_SYSLOG
|
|||
|
syslog (LOG_USER|LOG_ERR, "Libgcrypt error: "
|
|||
|
"acquiring FSM lock failed: %s - abort",
|
|||
|
strerror (err));
|
|||
|
#endif /*HAVE_SYSLOG*/
|
|||
|
abort ();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
static void
|
|||
|
unlock_fsm (void)
|
|||
|
{
|
|||
|
gpg_error_t err;
|
|||
|
|
|||
|
err = ath_mutex_unlock (&fsm_lock);
|
|||
|
if (err)
|
|||
|
{
|
|||
|
log_info ("FATAL: failed to release the FSM lock in libgrypt: %s\n",
|
|||
|
strerror (err));
|
|||
|
#ifdef HAVE_SYSLOG
|
|||
|
syslog (LOG_USER|LOG_ERR, "Libgcrypt error: "
|
|||
|
"releasing FSM lock failed: %s - abort",
|
|||
|
strerror (err));
|
|||
|
#endif /*HAVE_SYSLOG*/
|
|||
|
abort ();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* This function returns true if fips mode is enabled. This is
|
|||
|
independent of the fips required finite state machine and only used
|
|||
|
to enable fips specific code. Please use the fips_mode macro
|
|||
|
instead of calling this function directly. */
|
|||
|
int
|
|||
|
_gcry_fips_mode (void)
|
|||
|
{
|
|||
|
/* No locking is required because we have the requirement that this
|
|||
|
variable is only initialized once with no other threads
|
|||
|
existing. */
|
|||
|
return !no_fips_mode_required;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* Return a flag telling whether we are in the enforced fips mode. */
|
|||
|
int
|
|||
|
_gcry_enforced_fips_mode (void)
|
|||
|
{
|
|||
|
return enforced_fips_mode;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* If we do not want to enforce the fips mode, we can set a flag so
|
|||
|
that the application may check whether it is still in fips mode.
|
|||
|
TEXT will be printed as part of a syslog message. This function
|
|||
|
may only be be called if in fips mode. */
|
|||
|
void
|
|||
|
_gcry_inactivate_fips_mode (const char *text)
|
|||
|
{
|
|||
|
gcry_assert (_gcry_fips_mode ());
|
|||
|
|
|||
|
if (_gcry_enforced_fips_mode () )
|
|||
|
{
|
|||
|
/* Get us into the error state. */
|
|||
|
fips_signal_error (text);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
lock_fsm ();
|
|||
|
if (!inactive_fips_mode)
|
|||
|
{
|
|||
|
inactive_fips_mode = 1;
|
|||
|
unlock_fsm ();
|
|||
|
#ifdef HAVE_SYSLOG
|
|||
|
syslog (LOG_USER|LOG_WARNING, "Libgcrypt warning: "
|
|||
|
"%s - FIPS mode inactivated", text);
|
|||
|
#endif /*HAVE_SYSLOG*/
|
|||
|
}
|
|||
|
else
|
|||
|
unlock_fsm ();
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* Return the FIPS mode inactive flag. If it is true the FIPS mode is
|
|||
|
not anymore active. */
|
|||
|
int
|
|||
|
_gcry_is_fips_mode_inactive (void)
|
|||
|
{
|
|||
|
int flag;
|
|||
|
|
|||
|
if (!_gcry_fips_mode ())
|
|||
|
return 0;
|
|||
|
lock_fsm ();
|
|||
|
flag = inactive_fips_mode;
|
|||
|
unlock_fsm ();
|
|||
|
return flag;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
static const char *
|
|||
|
state2str (enum module_states state)
|
|||
|
{
|
|||
|
const char *s;
|
|||
|
|
|||
|
switch (state)
|
|||
|
{
|
|||
|
case STATE_POWERON: s = "Power-On"; break;
|
|||
|
case STATE_INIT: s = "Init"; break;
|
|||
|
case STATE_SELFTEST: s = "Self-Test"; break;
|
|||
|
case STATE_OPERATIONAL: s = "Operational"; break;
|
|||
|
case STATE_ERROR: s = "Error"; break;
|
|||
|
case STATE_FATALERROR: s = "Fatal-Error"; break;
|
|||
|
case STATE_SHUTDOWN: s = "Shutdown"; break;
|
|||
|
default: s = "?"; break;
|
|||
|
}
|
|||
|
return s;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* Return true if the library is in the operational state. */
|
|||
|
int
|
|||
|
_gcry_fips_is_operational (void)
|
|||
|
{
|
|||
|
int result;
|
|||
|
|
|||
|
if (!fips_mode ())
|
|||
|
result = 1;
|
|||
|
else
|
|||
|
{
|
|||
|
lock_fsm ();
|
|||
|
if (current_state == STATE_INIT)
|
|||
|
{
|
|||
|
/* If we are still in the INIT state, we need to run the
|
|||
|
selftests so that the FSM can eventually get into
|
|||
|
operational state. Given that we would need a 2-phase
|
|||
|
initialization of libgcrypt, but that has traditionally
|
|||
|
not been enforced, we use this on demand self-test
|
|||
|
checking. Note that Proper applications would do the
|
|||
|
application specific libgcrypt initialization between a
|
|||
|
gcry_check_version() and gcry_control
|
|||
|
(GCRYCTL_INITIALIZATION_FINISHED) where the latter will
|
|||
|
run the selftests. The drawback of these on-demand
|
|||
|
self-tests are a small chance that self-tests are
|
|||
|
performed by severeal threads; that is no problem because
|
|||
|
our FSM make sure that we won't oversee any error. */
|
|||
|
unlock_fsm ();
|
|||
|
_gcry_fips_run_selftests (0);
|
|||
|
lock_fsm ();
|
|||
|
}
|
|||
|
|
|||
|
result = (current_state == STATE_OPERATIONAL);
|
|||
|
unlock_fsm ();
|
|||
|
}
|
|||
|
return result;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* This is test on whether the library is in the operational state. In
|
|||
|
contrast to _gcry_fips_is_operational this function won't do a
|
|||
|
state transition on the fly. */
|
|||
|
int
|
|||
|
_gcry_fips_test_operational (void)
|
|||
|
{
|
|||
|
int result;
|
|||
|
|
|||
|
if (!fips_mode ())
|
|||
|
result = 1;
|
|||
|
else
|
|||
|
{
|
|||
|
lock_fsm ();
|
|||
|
result = (current_state == STATE_OPERATIONAL);
|
|||
|
unlock_fsm ();
|
|||
|
}
|
|||
|
return result;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* This is a test on whether the library is in the error or
|
|||
|
operational state. */
|
|||
|
int
|
|||
|
_gcry_fips_test_error_or_operational (void)
|
|||
|
{
|
|||
|
int result;
|
|||
|
|
|||
|
if (!fips_mode ())
|
|||
|
result = 1;
|
|||
|
else
|
|||
|
{
|
|||
|
lock_fsm ();
|
|||
|
result = (current_state == STATE_OPERATIONAL
|
|||
|
|| current_state == STATE_ERROR);
|
|||
|
unlock_fsm ();
|
|||
|
}
|
|||
|
return result;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
static void
|
|||
|
reporter (const char *domain, int algo, const char *what, const char *errtxt)
|
|||
|
{
|
|||
|
if (!errtxt && !_gcry_log_verbosity (2))
|
|||
|
return;
|
|||
|
|
|||
|
log_info ("libgcrypt selftest: %s %s%s (%d): %s%s%s%s\n",
|
|||
|
!strcmp (domain, "hmac")? "digest":domain,
|
|||
|
!strcmp (domain, "hmac")? "HMAC-":"",
|
|||
|
!strcmp (domain, "cipher")? _gcry_cipher_algo_name (algo) :
|
|||
|
!strcmp (domain, "digest")? _gcry_md_algo_name (algo) :
|
|||
|
!strcmp (domain, "hmac")? _gcry_md_algo_name (algo) :
|
|||
|
!strcmp (domain, "pubkey")? _gcry_pk_algo_name (algo) : "",
|
|||
|
algo, errtxt? errtxt:"Okay",
|
|||
|
what?" (":"", what? what:"", what?")":"");
|
|||
|
}
|
|||
|
|
|||
|
/* Run self-tests for all required cipher algorithms. Return 0 on
|
|||
|
success. */
|
|||
|
static int
|
|||
|
run_cipher_selftests (int extended)
|
|||
|
{
|
|||
|
static int algos[] =
|
|||
|
{
|
|||
|
GCRY_CIPHER_3DES,
|
|||
|
GCRY_CIPHER_AES128,
|
|||
|
GCRY_CIPHER_AES192,
|
|||
|
GCRY_CIPHER_AES256,
|
|||
|
0
|
|||
|
};
|
|||
|
int idx;
|
|||
|
gpg_error_t err;
|
|||
|
int anyerr = 0;
|
|||
|
|
|||
|
for (idx=0; algos[idx]; idx++)
|
|||
|
{
|
|||
|
err = _gcry_cipher_selftest (algos[idx], extended, reporter);
|
|||
|
reporter ("cipher", algos[idx], NULL,
|
|||
|
err? gpg_strerror (err):NULL);
|
|||
|
if (err)
|
|||
|
anyerr = 1;
|
|||
|
}
|
|||
|
return anyerr;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* Run self-tests for all required hash algorithms. Return 0 on
|
|||
|
success. */
|
|||
|
static int
|
|||
|
run_digest_selftests (int extended)
|
|||
|
{
|
|||
|
static int algos[] =
|
|||
|
{
|
|||
|
GCRY_MD_SHA1,
|
|||
|
GCRY_MD_SHA224,
|
|||
|
GCRY_MD_SHA256,
|
|||
|
GCRY_MD_SHA384,
|
|||
|
GCRY_MD_SHA512,
|
|||
|
0
|
|||
|
};
|
|||
|
int idx;
|
|||
|
gpg_error_t err;
|
|||
|
int anyerr = 0;
|
|||
|
|
|||
|
for (idx=0; algos[idx]; idx++)
|
|||
|
{
|
|||
|
err = _gcry_md_selftest (algos[idx], extended, reporter);
|
|||
|
reporter ("digest", algos[idx], NULL,
|
|||
|
err? gpg_strerror (err):NULL);
|
|||
|
if (err)
|
|||
|
anyerr = 1;
|
|||
|
}
|
|||
|
return anyerr;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* Run self-tests for all HMAC algorithms. Return 0 on success. */
|
|||
|
static int
|
|||
|
run_hmac_selftests (int extended)
|
|||
|
{
|
|||
|
static int algos[] =
|
|||
|
{
|
|||
|
GCRY_MD_SHA1,
|
|||
|
GCRY_MD_SHA224,
|
|||
|
GCRY_MD_SHA256,
|
|||
|
GCRY_MD_SHA384,
|
|||
|
GCRY_MD_SHA512,
|
|||
|
0
|
|||
|
};
|
|||
|
int idx;
|
|||
|
gpg_error_t err;
|
|||
|
int anyerr = 0;
|
|||
|
|
|||
|
for (idx=0; algos[idx]; idx++)
|
|||
|
{
|
|||
|
err = _gcry_hmac_selftest (algos[idx], extended, reporter);
|
|||
|
reporter ("hmac", algos[idx], NULL,
|
|||
|
err? gpg_strerror (err):NULL);
|
|||
|
if (err)
|
|||
|
anyerr = 1;
|
|||
|
}
|
|||
|
return anyerr;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* Run self-tests for all required public key algorithms. Return 0 on
|
|||
|
success. */
|
|||
|
static int
|
|||
|
run_pubkey_selftests (int extended)
|
|||
|
{
|
|||
|
static int algos[] =
|
|||
|
{
|
|||
|
GCRY_PK_RSA,
|
|||
|
GCRY_PK_DSA,
|
|||
|
/* GCRY_PK_ECDSA is not enabled in fips mode. */
|
|||
|
0
|
|||
|
};
|
|||
|
int idx;
|
|||
|
gpg_error_t err;
|
|||
|
int anyerr = 0;
|
|||
|
|
|||
|
for (idx=0; algos[idx]; idx++)
|
|||
|
{
|
|||
|
err = _gcry_pk_selftest (algos[idx], extended, reporter);
|
|||
|
reporter ("pubkey", algos[idx], NULL,
|
|||
|
err? gpg_strerror (err):NULL);
|
|||
|
if (err)
|
|||
|
anyerr = 1;
|
|||
|
}
|
|||
|
return anyerr;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* Run self-tests for the random number generator. Returns 0 on
|
|||
|
success. */
|
|||
|
static int
|
|||
|
run_random_selftests (void)
|
|||
|
{
|
|||
|
gpg_error_t err;
|
|||
|
|
|||
|
err = _gcry_random_selftest (reporter);
|
|||
|
reporter ("random", 0, NULL, err? gpg_strerror (err):NULL);
|
|||
|
|
|||
|
return !!err;
|
|||
|
}
|
|||
|
|
|||
|
/* Run an integrity check on the binary. Returns 0 on success. */
|
|||
|
static int
|
|||
|
check_binary_integrity (void)
|
|||
|
{
|
|||
|
#ifdef ENABLE_HMAC_BINARY_CHECK
|
|||
|
gpg_error_t err;
|
|||
|
Dl_info info;
|
|||
|
unsigned char digest[32];
|
|||
|
int dlen;
|
|||
|
char *fname = NULL;
|
|||
|
const char key[] = "What am I, a doctor or a moonshuttle conductor?";
|
|||
|
|
|||
|
if (!dladdr ("gcry_check_version", &info))
|
|||
|
err = gpg_error_from_syserror ();
|
|||
|
else
|
|||
|
{
|
|||
|
dlen = _gcry_hmac256_file (digest, sizeof digest, info.dli_fname,
|
|||
|
key, strlen (key));
|
|||
|
if (dlen < 0)
|
|||
|
err = gpg_error_from_syserror ();
|
|||
|
else if (dlen != 32)
|
|||
|
err = gpg_error (GPG_ERR_INTERNAL);
|
|||
|
else
|
|||
|
{
|
|||
|
fname = gcry_malloc (strlen (info.dli_fname) + 1 + 5 + 1 );
|
|||
|
if (!fname)
|
|||
|
err = gpg_error_from_syserror ();
|
|||
|
else
|
|||
|
{
|
|||
|
FILE *fp;
|
|||
|
char *p;
|
|||
|
|
|||
|
/* Prefix the basename with a dot. */
|
|||
|
strcpy (fname, info.dli_fname);
|
|||
|
p = strrchr (fname, '/');
|
|||
|
if (p)
|
|||
|
p++;
|
|||
|
else
|
|||
|
p = fname;
|
|||
|
memmove (p+1, p, strlen (p)+1);
|
|||
|
*p = '.';
|
|||
|
strcat (fname, ".hmac");
|
|||
|
|
|||
|
/* Open the file. */
|
|||
|
fp = fopen (fname, "r");
|
|||
|
if (!fp)
|
|||
|
err = gpg_error_from_syserror ();
|
|||
|
else
|
|||
|
{
|
|||
|
/* A buffer of 64 bytes plus one for a LF and one to
|
|||
|
detect garbage. */
|
|||
|
unsigned char buffer[64+1+1];
|
|||
|
const unsigned char *s;
|
|||
|
int n;
|
|||
|
|
|||
|
/* The HMAC files consists of lowercase hex digits
|
|||
|
only with an optional trailing linefeed. Fail if
|
|||
|
there is any garbage. */
|
|||
|
err = gpg_error (GPG_ERR_SELFTEST_FAILED);
|
|||
|
n = fread (buffer, 1, sizeof buffer, fp);
|
|||
|
if (n == 64 || (n == 65 && buffer[64] == '\n'))
|
|||
|
{
|
|||
|
buffer[64] = 0;
|
|||
|
for (n=0, s= buffer;
|
|||
|
n < 32 && loxdigit_p (s) && loxdigit_p (s+1);
|
|||
|
n++, s += 2)
|
|||
|
buffer[n] = loxtoi_2 (s);
|
|||
|
if ( n == 32 && !memcmp (digest, buffer, 32) )
|
|||
|
err = 0;
|
|||
|
}
|
|||
|
fclose (fp);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
reporter ("binary", 0, fname, err? gpg_strerror (err):NULL);
|
|||
|
#ifdef HAVE_SYSLOG
|
|||
|
if (err)
|
|||
|
syslog (LOG_USER|LOG_ERR, "Libgcrypt error: "
|
|||
|
"integrity check using `%s' failed: %s",
|
|||
|
fname? fname:"[?]", gpg_strerror (err));
|
|||
|
#endif /*HAVE_SYSLOG*/
|
|||
|
gcry_free (fname);
|
|||
|
return !!err;
|
|||
|
#else
|
|||
|
return 0;
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* Run the self-tests. If EXTENDED is true, extended versions of the
|
|||
|
selftest are run, that is more tests than required by FIPS. */
|
|||
|
gpg_err_code_t
|
|||
|
_gcry_fips_run_selftests (int extended)
|
|||
|
{
|
|||
|
enum module_states result = STATE_ERROR;
|
|||
|
gcry_err_code_t ec = GPG_ERR_SELFTEST_FAILED;
|
|||
|
|
|||
|
if (fips_mode ())
|
|||
|
fips_new_state (STATE_SELFTEST);
|
|||
|
|
|||
|
if (run_cipher_selftests (extended))
|
|||
|
goto leave;
|
|||
|
|
|||
|
if (run_digest_selftests (extended))
|
|||
|
goto leave;
|
|||
|
|
|||
|
if (run_hmac_selftests (extended))
|
|||
|
goto leave;
|
|||
|
|
|||
|
/* Run random tests before the pubkey tests because the latter
|
|||
|
require random. */
|
|||
|
if (run_random_selftests ())
|
|||
|
goto leave;
|
|||
|
|
|||
|
if (run_pubkey_selftests (extended))
|
|||
|
goto leave;
|
|||
|
|
|||
|
/* Now check the integrity of the binary. We do this this after
|
|||
|
having checked the HMAC code. */
|
|||
|
if (check_binary_integrity ())
|
|||
|
goto leave;
|
|||
|
|
|||
|
/* All selftests passed. */
|
|||
|
result = STATE_OPERATIONAL;
|
|||
|
ec = 0;
|
|||
|
|
|||
|
leave:
|
|||
|
if (fips_mode ())
|
|||
|
fips_new_state (result);
|
|||
|
|
|||
|
return ec;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* This function is used to tell the FSM about errors in the library.
|
|||
|
The FSM will be put into an error state. This function should not
|
|||
|
be called directly but by one of the macros
|
|||
|
|
|||
|
fips_signal_error (description)
|
|||
|
fips_signal_fatal_error (description)
|
|||
|
|
|||
|
where DESCRIPTION is a string describing the error. */
|
|||
|
void
|
|||
|
_gcry_fips_signal_error (const char *srcfile, int srcline, const char *srcfunc,
|
|||
|
int is_fatal, const char *description)
|
|||
|
{
|
|||
|
if (!fips_mode ())
|
|||
|
return; /* Not required. */
|
|||
|
|
|||
|
/* Set new state before printing an error. */
|
|||
|
fips_new_state (is_fatal? STATE_FATALERROR : STATE_ERROR);
|
|||
|
|
|||
|
/* Print error. */
|
|||
|
log_info ("%serror in libgcrypt, file %s, line %d%s%s: %s\n",
|
|||
|
is_fatal? "fatal ":"",
|
|||
|
srcfile, srcline,
|
|||
|
srcfunc? ", function ":"", srcfunc? srcfunc:"",
|
|||
|
description? description : "no description available");
|
|||
|
#ifdef HAVE_SYSLOG
|
|||
|
syslog (LOG_USER|LOG_ERR, "Libgcrypt error: "
|
|||
|
"%serror in file %s, line %d%s%s: %s",
|
|||
|
is_fatal? "fatal ":"",
|
|||
|
srcfile, srcline,
|
|||
|
srcfunc? ", function ":"", srcfunc? srcfunc:"",
|
|||
|
description? description : "no description available");
|
|||
|
#endif /*HAVE_SYSLOG*/
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* Perform a state transition to NEW_STATE. If this is an invalid
|
|||
|
transition, the module will go into a fatal error state. */
|
|||
|
static void
|
|||
|
fips_new_state (enum module_states new_state)
|
|||
|
{
|
|||
|
int ok = 0;
|
|||
|
enum module_states last_state;
|
|||
|
|
|||
|
lock_fsm ();
|
|||
|
|
|||
|
last_state = current_state;
|
|||
|
switch (current_state)
|
|||
|
{
|
|||
|
case STATE_POWERON:
|
|||
|
if (new_state == STATE_INIT
|
|||
|
|| new_state == STATE_ERROR
|
|||
|
|| new_state == STATE_FATALERROR)
|
|||
|
ok = 1;
|
|||
|
break;
|
|||
|
|
|||
|
case STATE_INIT:
|
|||
|
if (new_state == STATE_SELFTEST
|
|||
|
|| new_state == STATE_ERROR
|
|||
|
|| new_state == STATE_FATALERROR)
|
|||
|
ok = 1;
|
|||
|
break;
|
|||
|
|
|||
|
case STATE_SELFTEST:
|
|||
|
if (new_state == STATE_OPERATIONAL
|
|||
|
|| new_state == STATE_ERROR
|
|||
|
|| new_state == STATE_FATALERROR)
|
|||
|
ok = 1;
|
|||
|
break;
|
|||
|
|
|||
|
case STATE_OPERATIONAL:
|
|||
|
if (new_state == STATE_SHUTDOWN
|
|||
|
|| new_state == STATE_SELFTEST
|
|||
|
|| new_state == STATE_ERROR
|
|||
|
|| new_state == STATE_FATALERROR)
|
|||
|
ok = 1;
|
|||
|
break;
|
|||
|
|
|||
|
case STATE_ERROR:
|
|||
|
if (new_state == STATE_SHUTDOWN
|
|||
|
|| new_state == STATE_ERROR
|
|||
|
|| new_state == STATE_FATALERROR
|
|||
|
|| new_state == STATE_SELFTEST)
|
|||
|
ok = 1;
|
|||
|
break;
|
|||
|
|
|||
|
case STATE_FATALERROR:
|
|||
|
if (new_state == STATE_SHUTDOWN )
|
|||
|
ok = 1;
|
|||
|
break;
|
|||
|
|
|||
|
case STATE_SHUTDOWN:
|
|||
|
/* We won't see any transition *from* Shutdown because the only
|
|||
|
allowed new state is Power-Off and that one can't be
|
|||
|
represented. */
|
|||
|
break;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
if (ok)
|
|||
|
{
|
|||
|
current_state = new_state;
|
|||
|
}
|
|||
|
|
|||
|
unlock_fsm ();
|
|||
|
|
|||
|
if (!ok || _gcry_log_verbosity (2))
|
|||
|
log_info ("libgcrypt state transition %s => %s %s\n",
|
|||
|
state2str (last_state), state2str (new_state),
|
|||
|
ok? "granted":"denied");
|
|||
|
|
|||
|
if (!ok)
|
|||
|
{
|
|||
|
/* Invalid state transition. Halting library. */
|
|||
|
#ifdef HAVE_SYSLOG
|
|||
|
syslog (LOG_USER|LOG_ERR,
|
|||
|
"Libgcrypt error: invalid state transition %s => %s",
|
|||
|
state2str (last_state), state2str (new_state));
|
|||
|
#endif /*HAVE_SYSLOG*/
|
|||
|
fips_noreturn ();
|
|||
|
}
|
|||
|
else if (new_state == STATE_ERROR || new_state == STATE_FATALERROR)
|
|||
|
{
|
|||
|
#ifdef HAVE_SYSLOG
|
|||
|
syslog (LOG_USER|LOG_WARNING,
|
|||
|
"Libgcrypt notice: state transition %s => %s",
|
|||
|
state2str (last_state), state2str (new_state));
|
|||
|
#endif /*HAVE_SYSLOG*/
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/* This function should be called to ensure that the execution shall
|
|||
|
not continue. */
|
|||
|
void
|
|||
|
_gcry_fips_noreturn (void)
|
|||
|
{
|
|||
|
#ifdef HAVE_SYSLOG
|
|||
|
syslog (LOG_USER|LOG_ERR, "Libgcrypt terminated the application");
|
|||
|
#endif /*HAVE_SYSLOG*/
|
|||
|
fflush (NULL);
|
|||
|
abort ();
|
|||
|
/*NOTREACHED*/
|
|||
|
}
|