summary refs log tree commit diff stats
path: root/libotr/libgcrypt-1.8.7/src/fips.c
diff options
context:
space:
mode:
Diffstat (limited to 'libotr/libgcrypt-1.8.7/src/fips.c')
-rw-r--r--libotr/libgcrypt-1.8.7/src/fips.c867
1 files changed, 867 insertions, 0 deletions
diff --git a/libotr/libgcrypt-1.8.7/src/fips.c b/libotr/libgcrypt-1.8.7/src/fips.c
new file mode 100644
index 0000000..af3fe2c
--- /dev/null
+++ b/libotr/libgcrypt-1.8.7/src/fips.c
@@ -0,0 +1,867 @@
+/* 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 "cipher-proto.h"
+#include "hmac256.h"
+
+
+/* The name of the file used to force 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.  */
+GPGRT_LOCK_DEFINE (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 accidentally 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 = gpgrt_lock_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",
+                    gpg_strerror (err));
+#ifdef HAVE_SYSLOG
+          syslog (LOG_USER|LOG_ERR, "Libgcrypt error: "
+                  "creating FSM lock failed: %s - abort",
+                  gpg_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 = gpgrt_lock_lock (&fsm_lock);
+  if (err)
+    {
+      log_info ("FATAL: failed to acquire the FSM lock in libgrypt: %s\n",
+                gpg_strerror (err));
+#ifdef HAVE_SYSLOG
+      syslog (LOG_USER|LOG_ERR, "Libgcrypt error: "
+              "acquiring FSM lock failed: %s - abort",
+              gpg_strerror (err));
+#endif /*HAVE_SYSLOG*/
+      abort ();
+    }
+}
+
+static void
+unlock_fsm (void)
+{
+  gpg_error_t err;
+
+  err = gpgrt_lock_unlock (&fsm_lock);
+  if (err)
+    {
+      log_info ("FATAL: failed to release the FSM lock in libgrypt: %s\n",
+                gpg_strerror (err));
+#ifdef HAVE_SYSLOG
+      syslog (LOG_USER|LOG_ERR, "Libgcrypt error: "
+              "releasing FSM lock failed: %s - abort",
+              gpg_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)
+{
+  if (!_gcry_fips_mode ())
+    return 0;
+  return enforced_fips_mode;
+}
+
+/* Set a flag telling whether we are in the enforced fips mode.  */
+void
+_gcry_set_enforced_fips_mode (void)
+{
+  enforced_fips_mode = 1;
+}
+
+/* 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 several 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,
+      GCRY_MD_SHA3_224,
+      GCRY_MD_SHA3_256,
+      GCRY_MD_SHA3_384,
+      GCRY_MD_SHA3_512,
+      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_ECC,
+      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 = xtrymalloc (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
+                     with an optional trailing linefeed or optional
+                     with two trailing spaces.  The latter format
+                     allows the use of the usual sha1sum format.  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')
+                      || (n == 66 && buffer[64] == ' ' && buffer[65] == ' '))
+                    {
+                      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*/
+  xfree (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*/
+}