]> granicus.if.org Git - postgresql/commitdiff
Replace PostmasterRandom() with a stronger source, second attempt.
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>
Mon, 5 Dec 2016 11:42:59 +0000 (13:42 +0200)
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>
Mon, 5 Dec 2016 11:42:59 +0000 (13:42 +0200)
This adds a new routine, pg_strong_random() for generating random bytes,
for use in both frontend and backend. At the moment, it's only used in
the backend, but the upcoming SCRAM authentication patches need strong
random numbers in libpq as well.

pg_strong_random() is based on, and replaces, the existing implementation
in pgcrypto. It can acquire strong random numbers from a number of sources,
depending on what's available:

- OpenSSL RAND_bytes(), if built with OpenSSL
- On Windows, the native cryptographic functions are used
- /dev/urandom

Unlike the current pgcrypto function, the source is chosen by configure.
That makes it easier to test different implementations, and ensures that
we don't accidentally fall back to a less secure implementation, if the
primary source fails. All of those methods are quite reliable, it would be
pretty surprising for them to fail, so we'd rather find out by failing
hard.

If no strong random source is available, we fall back to using erand48(),
seeded from current timestamp, like PostmasterRandom() was. That isn't
cryptographically secure, but allows us to still work on platforms that
don't have any of the above stronger sources. Because it's not very secure,
the built-in implementation is only used if explicitly requested with
--disable-strong-random.

This replaces the more complicated Fortuna algorithm we used to have in
pgcrypto, which is unfortunate, but all modern platforms have /dev/urandom,
so it doesn't seem worth the maintenance effort to keep that. pgcrypto
functions that require strong random numbers will be disabled with
--disable-strong-random.

Original patch by Magnus Hagander, tons of further work by Michael Paquier
and me.

Discussion: https://www.postgresql.org/message-id/CAB7nPqRy3krN8quR9XujMVVHYtXJ0_60nqgVc6oUk8ygyVkZsA@mail.gmail.com
Discussion: https://www.postgresql.org/message-id/CAB7nPqRWkNYRRPJA7-cF+LfroYV10pvjdz6GNvxk-Eee9FypKA@mail.gmail.com

42 files changed:
configure
configure.in
contrib/pgcrypto/Makefile
contrib/pgcrypto/expected/pgp-compression_1.out [new file with mode: 0644]
contrib/pgcrypto/expected/pgp-decrypt_1.out [new file with mode: 0644]
contrib/pgcrypto/expected/pgp-encrypt_1.out [new file with mode: 0644]
contrib/pgcrypto/expected/pgp-pubkey-encrypt_1.out [new file with mode: 0644]
contrib/pgcrypto/fortuna.c [deleted file]
contrib/pgcrypto/fortuna.h [deleted file]
contrib/pgcrypto/internal.c
contrib/pgcrypto/openssl.c
contrib/pgcrypto/pgcrypto.c
contrib/pgcrypto/pgp-encrypt.c
contrib/pgcrypto/pgp-mpi-internal.c
contrib/pgcrypto/pgp-pgsql.c
contrib/pgcrypto/pgp-pubenc.c
contrib/pgcrypto/pgp-s2k.c
contrib/pgcrypto/px-crypt.c
contrib/pgcrypto/px.c
contrib/pgcrypto/px.h
contrib/pgcrypto/random.c [deleted file]
doc/src/sgml/installation.sgml
src/Makefile.global.in
src/backend/libpq/auth.c
src/backend/libpq/crypt.c
src/backend/postmaster/postmaster.c
src/backend/storage/ipc/ipci.c
src/backend/storage/lmgr/lwlocknames.txt
src/backend/utils/init/globals.c
src/backend/utils/misc/Makefile
src/backend/utils/misc/backend_random.c [new file with mode: 0644]
src/include/libpq/crypt.h
src/include/libpq/libpq-be.h
src/include/miscadmin.h
src/include/pg_config.h.in
src/include/pg_config.h.win32
src/include/port.h
src/include/utils/backend_random.h [new file with mode: 0644]
src/port/Makefile
src/port/erand48.c
src/port/pg_strong_random.c [new file with mode: 0644]
src/tools/msvc/Mkvcbuild.pm

index f4f2f8b7ce8b264e887db5a1d65c64b9003b9ca9..551fc720b7fb7cba33c36c32a451a84271181e46 100755 (executable)
--- a/configure
+++ b/configure
@@ -739,6 +739,7 @@ GENHTML
 LCOV
 GCOV
 enable_debug
+enable_strong_random
 enable_rpath
 default_port
 WANTED_LANGUAGES
@@ -806,6 +807,7 @@ with_pgport
 enable_rpath
 enable_spinlocks
 enable_atomics
+enable_strong_random
 enable_debug
 enable_profiling
 enable_coverage
@@ -1478,6 +1480,7 @@ Optional Features:
                           executables
   --disable-spinlocks     do not use spinlocks
   --disable-atomics       do not use atomic operations
+  --disable-strong-random do not use a strong random number source
   --enable-debug          build with debugging symbols (-g)
   --enable-profiling      build with profiling enabled
   --enable-coverage       build with coverage testing instrumentation
@@ -3192,6 +3195,34 @@ fi
 
 
 
+#
+# Random number generation
+#
+
+
+# Check whether --enable-strong-random was given.
+if test "${enable_strong_random+set}" = set; then :
+  enableval=$enable_strong_random;
+  case $enableval in
+    yes)
+      :
+      ;;
+    no)
+      :
+      ;;
+    *)
+      as_fn_error $? "no argument expected for --enable-strong-random option" "$LINENO" 5
+      ;;
+  esac
+
+else
+  enable_strong_random=yes
+
+fi
+
+
+
+
 #
 # --enable-debug adds -g to compiler flags
 #
@@ -14982,6 +15013,84 @@ $as_echo "#define USE_WIN32_SHARED_MEMORY 1" >>confdefs.h
   SHMEM_IMPLEMENTATION="src/backend/port/win32_shmem.c"
 fi
 
+# Select random number source
+#
+# You can override this logic by setting the appropriate USE_*RANDOM flag to 1
+# in the template or configure command line.
+
+# If not selected manually, try to select a source automatically.
+if test "$enable_strong_random" = "yes" && test x"$USE_OPENSSL_RANDOM" = x"" && test x"$USE_WIN32_RANDOM" = x"" && test x"$USE_DEV_URANDOM" = x"" ; then
+  if test x"$with_openssl" = x"yes" ; then
+    USE_OPENSSL_RANDOM=1
+  elif test "$PORTNAME" = x"win32" ; then
+    USE_WIN32_RANDOM=1
+  else
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for /dev/urandom" >&5
+$as_echo_n "checking for /dev/urandom... " >&6; }
+if ${ac_cv_file__dev_urandom+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  test "$cross_compiling" = yes &&
+  as_fn_error $? "cannot check for file existence when cross compiling" "$LINENO" 5
+if test -r "/dev/urandom"; then
+  ac_cv_file__dev_urandom=yes
+else
+  ac_cv_file__dev_urandom=no
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_file__dev_urandom" >&5
+$as_echo "$ac_cv_file__dev_urandom" >&6; }
+if test "x$ac_cv_file__dev_urandom" = xyes; then :
+
+fi
+
+
+    if test x"$ac_cv_file__dev_urandom" = x"yes" ; then
+      USE_DEV_URANDOM=1
+    fi
+  fi
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking which random number source to use" >&5
+$as_echo_n "checking which random number source to use... " >&6; }
+if test "$enable_strong_random" = yes ; then
+  if test x"$USE_OPENSSL_RANDOM" = x"1" ; then
+
+$as_echo "#define USE_OPENSSL_RANDOM 1" >>confdefs.h
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: OpenSSL" >&5
+$as_echo "OpenSSL" >&6; }
+  elif test x"$USE_WIN32_RANDOM" = x"1" ; then
+
+$as_echo "#define USE_WIN32_RANDOM 1" >>confdefs.h
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: Windows native" >&5
+$as_echo "Windows native" >&6; }
+  elif test x"$USE_DEV_URANDOM" = x"1" ; then
+
+$as_echo "#define USE_DEV_URANDOM 1" >>confdefs.h
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: /dev/urandom" >&5
+$as_echo "/dev/urandom" >&6; }
+  else
+    as_fn_error $? "
+no source of strong random numbers was found
+PostgreSQL can use OpenSSL or /dev/urandom as a source of random numbers,
+for authentication protocols. You can use --disable-strong-random to use of a built-in
+pseudo random number generator, but that may be insecure." "$LINENO" 5
+  fi
+
+$as_echo "#define HAVE_STRONG_RANDOM 1" >>confdefs.h
+
+else
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: weak builtin PRNG" >&5
+$as_echo "weak builtin PRNG" >&6; }
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING:
+*** Not using a strong random number source may be insecure." >&5
+$as_echo "$as_me: WARNING:
+*** Not using a strong random number source may be insecure." >&2;}
+fi
+
 # If not set in template file, set bytes to use libc memset()
 if test x"$MEMSET_LOOP_LIMIT" = x"" ; then
   MEMSET_LOOP_LIMIT=1024
index 9f7611caebe8f58100a28f8d9c2ac88c2f1c7767..595e047d0e29656c246f5828259f4f45d9aa7f56 100644 (file)
@@ -193,6 +193,13 @@ PGAC_ARG_BOOL(enable, spinlocks, yes,
 PGAC_ARG_BOOL(enable, atomics, yes,
               [do not use atomic operations])
 
+#
+# Random number generation
+#
+PGAC_ARG_BOOL(enable, strong-random, yes,
+              [do not use a strong random number source])
+AC_SUBST(enable_strong_random)
+
 #
 # --enable-debug adds -g to compiler flags
 #
@@ -1965,6 +1972,51 @@ else
   SHMEM_IMPLEMENTATION="src/backend/port/win32_shmem.c"
 fi
 
+# Select random number source
+#
+# You can override this logic by setting the appropriate USE_*RANDOM flag to 1
+# in the template or configure command line.
+
+# If not selected manually, try to select a source automatically.
+if test "$enable_strong_random" = "yes" && test x"$USE_OPENSSL_RANDOM" = x"" && test x"$USE_WIN32_RANDOM" = x"" && test x"$USE_DEV_URANDOM" = x"" ; then
+  if test x"$with_openssl" = x"yes" ; then
+    USE_OPENSSL_RANDOM=1
+  elif test "$PORTNAME" = x"win32" ; then
+    USE_WIN32_RANDOM=1
+  else
+    AC_CHECK_FILE([/dev/urandom], [], [])
+
+    if test x"$ac_cv_file__dev_urandom" = x"yes" ; then
+      USE_DEV_URANDOM=1
+    fi
+  fi
+fi
+
+AC_MSG_CHECKING([which random number source to use])
+if test "$enable_strong_random" = yes ; then
+  if test x"$USE_OPENSSL_RANDOM" = x"1" ; then
+    AC_DEFINE(USE_OPENSSL_RANDOM, 1, [Define to use OpenSSL for random number generation])
+    AC_MSG_RESULT([OpenSSL])
+  elif test x"$USE_WIN32_RANDOM" = x"1" ; then
+    AC_DEFINE(USE_WIN32_RANDOM, 1, [Define to use native Windows API for random number generation])
+    AC_MSG_RESULT([Windows native])
+  elif test x"$USE_DEV_URANDOM" = x"1" ; then
+    AC_DEFINE(USE_DEV_URANDOM, 1, [Define to use /dev/urandom for random number generation])
+    AC_MSG_RESULT([/dev/urandom])
+  else
+    AC_MSG_ERROR([
+no source of strong random numbers was found
+PostgreSQL can use OpenSSL or /dev/urandom as a source of random numbers,
+for authentication protocols. You can use --disable-strong-random to use of a built-in
+pseudo random number generator, but that may be insecure.])
+  fi
+  AC_DEFINE(HAVE_STRONG_RANDOM, 1, [Define to use have a strong random number source])
+else
+    AC_MSG_RESULT([weak builtin PRNG])
+    AC_MSG_WARN([
+*** Not using a strong random number source may be insecure.])
+fi
+
 # If not set in template file, set bytes to use libc memset()
 if test x"$MEMSET_LOOP_LIMIT" = x"" ; then
   MEMSET_LOOP_LIMIT=1024
index 805db7626b4ec519f5a2e41675d36513146f81ce..f65d84d1f35a95e5482c733959796d179302ebe3 100644 (file)
@@ -1,7 +1,7 @@
 # contrib/pgcrypto/Makefile
 
 INT_SRCS = md5.c sha1.c sha2.c internal.c internal-sha2.c blf.c rijndael.c \
-               fortuna.c random.c pgp-mpi-internal.c imath.c
+               pgp-mpi-internal.c imath.c
 INT_TESTS = sha2
 
 OSSL_SRCS = openssl.c pgp-mpi-openssl.c
diff --git a/contrib/pgcrypto/expected/pgp-compression_1.out b/contrib/pgcrypto/expected/pgp-compression_1.out
new file mode 100644 (file)
index 0000000..25d5c35
--- /dev/null
@@ -0,0 +1,42 @@
+--
+-- PGP compression support
+--
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+
+ww0ECQMCsci6AdHnELlh0kQB4jFcVwHMJg0Bulop7m3Mi36s15TAhBo0AnzIrRFrdLVCkKohsS6+
+DMcmR53SXfLoDJOv/M8uKj3QSq7oWNIp95pxfA==
+=tbSn
+-----END PGP MESSAGE-----
+'), 'key', 'expect-compress-algo=1');
+ pgp_sym_decrypt 
+-----------------
+ Secret message
+(1 row)
+
+select pgp_sym_decrypt(
+       pgp_sym_encrypt('Secret message', 'key', 'compress-algo=0'),
+       'key', 'expect-compress-algo=0');
+ERROR:  pg_random_bytes() is not supported by this build
+DETAIL:  This functionality requires a source of strong random numbers
+HINT:  You need to rebuild PostgreSQL using --enable-strong-random
+select pgp_sym_decrypt(
+       pgp_sym_encrypt('Secret message', 'key', 'compress-algo=1'),
+       'key', 'expect-compress-algo=1');
+ERROR:  pg_random_bytes() is not supported by this build
+DETAIL:  This functionality requires a source of strong random numbers
+HINT:  You need to rebuild PostgreSQL using --enable-strong-random
+select pgp_sym_decrypt(
+       pgp_sym_encrypt('Secret message', 'key', 'compress-algo=2'),
+       'key', 'expect-compress-algo=2');
+ERROR:  pg_random_bytes() is not supported by this build
+DETAIL:  This functionality requires a source of strong random numbers
+HINT:  You need to rebuild PostgreSQL using --enable-strong-random
+-- level=0 should turn compression off
+select pgp_sym_decrypt(
+       pgp_sym_encrypt('Secret message', 'key',
+                       'compress-algo=2, compress-level=0'),
+       'key', 'expect-compress-algo=0');
+ERROR:  pg_random_bytes() is not supported by this build
+DETAIL:  This functionality requires a source of strong random numbers
+HINT:  You need to rebuild PostgreSQL using --enable-strong-random
diff --git a/contrib/pgcrypto/expected/pgp-decrypt_1.out b/contrib/pgcrypto/expected/pgp-decrypt_1.out
new file mode 100644 (file)
index 0000000..d9e1e38
--- /dev/null
@@ -0,0 +1,424 @@
+--
+-- pgp_descrypt tests
+--
+--  Checking ciphers
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.blowfish.sha1.mdc.s2k3.z0
+
+jA0EBAMCfFNwxnvodX9g0jwB4n4s26/g5VmKzVab1bX1SmwY7gvgvlWdF3jKisvS
+yA6Ce1QTMK3KdL2MPfamsTUSAML8huCJMwYQFfE=
+=JcP+
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt 
+-----------------
+ Secret message.
+(1 row)
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.sha1.mdc.s2k3.z0
+
+jA0EBwMCci97v0Q6Z0Zg0kQBsVf5Oe3iC+FBzUmuMV9KxmAyOMyjCc/5i8f1Eest
+UTAsG35A1vYs02VARKzGz6xI2UHwFUirP+brPBg3Ee7muOx8pA==
+=XtrP
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt 
+-----------------
+ Secret message.
+(1 row)
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes192.sha1.mdc.s2k3.z0
+
+jA0ECAMCI7YQpWqp3D1g0kQBCjB7GlX7+SQeXNleXeXQ78ZAPNliquGDq9u378zI
+5FPTqAhIB2/2fjY8QEIs1ai00qphjX2NitxV/3Wn+6dufB4Q4g==
+=rCZt
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt 
+-----------------
+ Secret message.
+(1 row)
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes256.sha1.mdc.s2k3.z0
+
+jA0ECQMC4f/5djqCC1Rg0kQBTHEPsD+Sw7biBsM2er3vKyGPAQkuTBGKC5ie7hT/
+lceMfQdbAg6oTFyJpk/wH18GzRDphCofg0X8uLgkAKMrpcmgog==
+=fB6S
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt 
+-----------------
+ Secret message.
+(1 row)
+
+-- Checking MDC modes
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.sha1.nomdc.s2k3.z0
+
+jA0EBwMCnv07rlXqWctgyS2Dm2JfOKCRL4sLSLJUC8RS2cH7cIhKSuLitOtyquB+
+u9YkgfJfsuRJmgQ9tmo=
+=60ui
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt 
+-----------------
+ Secret message.
+(1 row)
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.sha1.mdc.s2k3.z0
+
+jA0EBwMCEeP3idNjQ1Bg0kQBf4G0wX+2QNzLh2YNwYkQgQkfYhn/hLXjV4nK9nsE
+8Ex1Dsdt5UPvOz8W8VKQRS6loOfOe+yyXil8W3IYFwUpdDUi+Q==
+=moGf
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt 
+-----------------
+ Secret message.
+(1 row)
+
+-- Checking hashes
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.md5.mdc.s2k3.z0
+
+jA0EBwMClrXXtOXetohg0kQBn0Kl1ymevQZRHkdoYRHgzCwSQEiss7zYff2UNzgO
+KyRrHf7zEBuZiZ2AG34jNVMOLToj1jJUg5zTSdecUzQVCykWTA==
+=NyLk
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt 
+-----------------
+ Secret message.
+(1 row)
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.sha1.mdc.s2k3.z0
+
+jA0EBwMCApbdlrURoWJg0kQBzHM/E0o7djY82bNuspjxjAcPFrrtp0uvDdMQ4z2m
+/PM8jhgI5vxFYfNQjLl8y3fHYIomk9YflN9K/Q13iq8A8sjeTw==
+=FxbQ
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt 
+-----------------
+ Secret message.
+(1 row)
+
+-- Checking S2K modes
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.sha1.mdc.s2k0.z0
+
+jAQEBwAC0kQBKTaLAKE3xzps+QIZowqRNb2eAdzBw2LxEW2YD5PgNlbhJdGg+dvw
+Ah9GXjGS1TVALzTImJbz1uHUZRfhJlFbc5yGQw==
+=YvkV
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt 
+-----------------
+ Secret message.
+(1 row)
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.sha1.mdc.s2k1.z0
+
+jAwEBwEC/QTByBLI3b/SRAHPxKzI6SZBo5lAEOD+EsvKQWO4adL9tDY+++Iqy1xK
+4IaWXVKEj9R2Lr2xntWWMGZtcKtjD2lFFRXXd9dZp1ZThNDz
+=dbXm
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt 
+-----------------
+ Secret message.
+(1 row)
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.sha1.mdc.s2k3.z0
+
+jA0EBwMCEq4Su3ZqNEJg0kQB4QG5jBTKF0i04xtH+avzmLhstBNRxvV3nsmB3cwl
+z+9ZaA/XdSx5ZiFnMym8P6r8uY9rLjjNptvvRHlxIReF+p9MNg==
+=VJKg
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt 
+-----------------
+ Secret message.
+(1 row)
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes192.sha1.mdc.s2k0.z0
+
+jAQECAAC0kQBBDnQWkgsx9YFaqDfWmpsiyAJ6y2xG/sBvap1dySYEMuZ+wJTXQ9E
+Cr3i2M7TgVZ0M4jp4QL0adG1lpN5iK7aQeOwMw==
+=cg+i
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt 
+-----------------
+ Secret message.
+(1 row)
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes192.sha1.mdc.s2k1.z0
+
+jAwECAECruOfyNDFiTnSRAEVoGXm4A9UZKkWljdzjEO/iaE7mIraltIpQMkiqCh9
+7h8uZ2u9uRBOv222fZodGvc6bvq/4R4hAa/6qSHtm8mdmvGt
+=aHmC
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt 
+-----------------
+ Secret message.
+(1 row)
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes192.sha1.mdc.s2k3.z0
+
+jA0ECAMCjFn6SRi3SONg0kQBqtSHPaD0m7rXfDAhCWU/ypAsI93GuHGRyM99cvMv
+q6eF6859ZVnli3BFSDSk3a4e/pXhglxmDYCfjAXkozKNYLo6yw==
+=K0LS
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt 
+-----------------
+ Secret message.
+(1 row)
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes256.sha1.mdc.s2k0.z0
+
+jAQECQAC0kQB4L1eMbani07XF2ZYiXNK9LW3v8w41oUPl7dStmrJPQFwsdxmrDHu
+rQr3WbdKdY9ufjOE5+mXI+EFkSPrF9rL9NCq6w==
+=RGts
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt 
+-----------------
+ Secret message.
+(1 row)
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes256.sha1.mdc.s2k1.z0
+
+jAwECQECKHhrou7ZOIXSRAHWIVP+xjVQcjAVBTt+qh9SNzYe248xFTwozkwev3mO
++KVJW0qhk0An+Y2KF99/bYFl9cL5D3Tl43fC8fXGl3x3m7pR
+=SUrU
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt 
+-----------------
+ Secret message.
+(1 row)
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes256.sha1.mdc.s2k3.z0
+
+jA0ECQMCjc8lwZu8Fz1g0kQBkEzjImi21liep5jj+3dAJ2aZFfUkohi8b3n9z+7+
+4+NRzL7cMW2RLAFnJbiqXDlRHMwleeuLN1up2WIxsxtYYuaBjA==
+=XZrG
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt 
+-----------------
+ Secret message.
+(1 row)
+
+-- Checking longer passwords
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.sha1.mdc.s2k3.z0
+
+jA0EBwMCx6dBiuqrYNRg0kQBEo63AvA1SCslxP7ayanLf1H0/hlk2nONVhTwVEWi
+tTGup1mMz6Cfh1uDRErUuXpx9A0gdMu7zX0o5XjrL7WGDAZdSw==
+=XKKG
+-----END PGP MESSAGE-----
+'), '0123456789abcdefghij');
+ pgp_sym_decrypt 
+-----------------
+ Secret message.
+(1 row)
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.sha1.mdc.s2k3.z0
+
+jA0EBwMCBDvYuS990iFg0kQBW31UK5OiCjWf5x6KJ8qNNT2HZWQCjCBZMU0XsOC6
+CMxFKadf144H/vpoV9GA0f22keQgCl0EsTE4V4lweVOPTKCMJg==
+=gWDh
+-----END PGP MESSAGE-----
+'), '0123456789abcdefghij2jk4h5g2j54khg23h54g2kh54g2khj54g23hj54');
+ pgp_sym_decrypt 
+-----------------
+ Secret message.
+(1 row)
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.sha1.mdc.s2k3.z0
+
+jA0EBwMCqXbFafC+ofVg0kQBejyiPqH0QMERVGfmPOjtAxvyG5KDIJPYojTgVSDt
+FwsDabdQUz5O7bgNSnxfmyw1OifGF+W2bIn/8W+0rDf8u3+O+Q==
+=OxOF
+-----END PGP MESSAGE-----
+'), 'x');
+ pgp_sym_decrypt 
+-----------------
+ Secret message.
+(1 row)
+
+-- Checking various data
+select encode(digest(pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.sha1.mdc.s2k3.z0
+
+jA0EBwMCGJ+SpuOysINg0kQBJfSjzsW0x4OVcAyr17O7FBvMTwIGeGcJd99oTQU8
+Xtx3kDqnhUq9Z1fS3qPbi5iNP2A9NxOBxPWz2JzxhydANlgbxg==
+=W/ik
+-----END PGP MESSAGE-----
+'), '0123456789abcdefghij'), 'sha1'), 'hex');
+                  encode                  
+------------------------------------------
+ 0225e3ede6f2587b076d021a189ff60aad67e066
+(1 row)
+
+-- expected: 0225e3ede6f2587b076d021a189ff60aad67e066
+select encode(digest(pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat2.aes.sha1.mdc.s2k3.z0
+
+jA0EBwMCvdpDvidNzMxg0jUBvj8eS2+1t/9/zgemxvhtc0fvdKGGbjH7dleaTJRB
+SaV9L04ky1qECNDx3XjnoKLC+H7IOQ==
+=Fxen
+-----END PGP MESSAGE-----
+'), '0123456789abcdefghij'), 'sha1'), 'hex');
+                  encode                  
+------------------------------------------
+ da39a3ee5e6b4b0d3255bfef95601890afd80709
+(1 row)
+
+-- expected: da39a3ee5e6b4b0d3255bfef95601890afd80709
+select encode(digest(pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat3.aes.sha1.mdc.s2k3.z0
+
+jA0EBwMCxQvxJZ3G/HRg0lgBeYmTa7/uDAjPyFwSX4CYBgpZWVn/JS8JzILrcWF8
+gFnkUKIE0PSaYFp+Yi1VlRfUtRQ/X/LYNGa7tWZS+4VQajz2Xtz4vUeAEiYFYPXk
+73Hb8m1yRhQK
+=ivrD
+-----END PGP MESSAGE-----
+'), '0123456789abcdefghij'), 'sha1'), 'hex');
+                  encode                  
+------------------------------------------
+ 5e5c135efc0dd00633efc6dfd6e731ea408a5b4c
+(1 row)
+
+-- expected: 5e5c135efc0dd00633efc6dfd6e731ea408a5b4c
+-- Checking CRLF
+select encode(digest(pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: crlf mess
+
+ww0ECQMCt7VAtby6l4Bi0lgB5KMIZiiF/b3CfMfUyY0eDncsGXtkbu1X+l9brjpMP8eJnY79Amms
+a3nsOzKTXUfS9VyaXo8IrncM6n7fdaXpwba/3tNsAhJG4lDv1k4g9v8Ix2dfv6Rs
+=mBP9
+-----END PGP MESSAGE-----
+'), 'key', 'convert-crlf=0'), 'sha1'), 'hex');
+                  encode                  
+------------------------------------------
+ 9353062be7720f1446d30b9e75573a4833886784
+(1 row)
+
+-- expected: 9353062be7720f1446d30b9e75573a4833886784
+select encode(digest(pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: crlf mess
+
+ww0ECQMCt7VAtby6l4Bi0lgB5KMIZiiF/b3CfMfUyY0eDncsGXtkbu1X+l9brjpMP8eJnY79Amms
+a3nsOzKTXUfS9VyaXo8IrncM6n7fdaXpwba/3tNsAhJG4lDv1k4g9v8Ix2dfv6Rs
+=mBP9
+-----END PGP MESSAGE-----
+'), 'key', 'convert-crlf=1'), 'sha1'), 'hex');
+                  encode                  
+------------------------------------------
+ 7efefcab38467f7484d6fa43dc86cf5281bd78e2
+(1 row)
+
+-- expected: 7efefcab38467f7484d6fa43dc86cf5281bd78e2
+-- check BUG #11905, problem with messages 6 less than a power of 2.
+select pgp_sym_decrypt(pgp_sym_encrypt(repeat('x',65530),'1'),'1') = repeat('x',65530);
+ERROR:  pg_random_bytes() is not supported by this build
+DETAIL:  This functionality requires a source of strong random numbers
+HINT:  You need to rebuild PostgreSQL using --enable-strong-random
+-- expected: true
+-- Negative tests
+-- Decryption with a certain incorrect key yields an apparent Literal Data
+-- packet reporting its content to be binary data.  Ciphertext source:
+-- iterative pgp_sym_encrypt('secret', 'key') until the random prefix gave
+-- rise to that property.
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+
+ww0EBwMCxf8PTrQBmJdl0jcB6y2joE7GSLKRv7trbNsF5Z8ou5NISLUg31llVH/S0B2wl4bvzZjV
+VsxxqLSPzNLAeIspJk5G
+=mSd/
+-----END PGP MESSAGE-----
+'), 'wrong-key', 'debug=1');
+NOTICE:  dbg: prefix_init: corrupt prefix
+NOTICE:  dbg: parse_literal_data: data type=b
+NOTICE:  dbg: mdcbuf_finish: bad MDC pkt hdr
+ERROR:  Wrong key or corrupt data
+-- Routine text/binary mismatch.
+select pgp_sym_decrypt(pgp_sym_encrypt_bytea('P', 'key'), 'key', 'debug=1');
+ERROR:  pg_random_bytes() is not supported by this build
+DETAIL:  This functionality requires a source of strong random numbers
+HINT:  You need to rebuild PostgreSQL using --enable-strong-random
+-- Decryption with a certain incorrect key yields an apparent BZip2-compressed
+-- plaintext.  Ciphertext source: iterative pgp_sym_encrypt('secret', 'key')
+-- until the random prefix gave rise to that property.
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+
+ww0EBwMC9rK/dMkF5Zlt0jcBlzAQ1mQY2qYbKYbw8h3EZ5Jk0K2IiY92R82TRhWzBIF/8cmXDPtP
+GXsd65oYJZp3Khz0qfyn
+=Nmpq
+-----END PGP MESSAGE-----
+'), 'wrong-key', 'debug=1');
+NOTICE:  dbg: prefix_init: corrupt prefix
+NOTICE:  dbg: parse_compressed_data: bzip2 unsupported
+NOTICE:  dbg: mdcbuf_finish: bad MDC pkt hdr
+ERROR:  Wrong key or corrupt data
+-- Routine use of BZip2 compression.  Ciphertext source:
+-- echo x | gpg --homedir /nonexistent --personal-compress-preferences bzip2 \
+--      --personal-cipher-preferences aes --no-emit-version --batch \
+--      --symmetric --passphrase key --armor
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+
+jA0EBwMCRhFrAKNcLVJg0mMBLJG1cCASNk/x/3dt1zJ+2eo7jHfjgg3N6wpB3XIe
+QCwkWJwlBG5pzbO5gu7xuPQN+TbPJ7aQ2sLx3bAHhtYb0i3vV9RO10Gw++yUyd4R
+UCAAw2JRIISttRHMfDpDuZJpvYo=
+=AZ9M
+-----END PGP MESSAGE-----
+'), 'key', 'debug=1');
+NOTICE:  dbg: parse_compressed_data: bzip2 unsupported
+ERROR:  Unsupported compression algorithm
diff --git a/contrib/pgcrypto/expected/pgp-encrypt_1.out b/contrib/pgcrypto/expected/pgp-encrypt_1.out
new file mode 100644 (file)
index 0000000..2291e66
--- /dev/null
@@ -0,0 +1,161 @@
+--
+-- PGP encrypt
+--
+-- ensure consistent test output regardless of the default bytea format
+SET bytea_output TO escape;
+select pgp_sym_decrypt(pgp_sym_encrypt('Secret.', 'key'), 'key');
+ERROR:  pg_random_bytes() is not supported by this build
+DETAIL:  This functionality requires a source of strong random numbers
+HINT:  You need to rebuild PostgreSQL using --enable-strong-random
+-- check whether the defaults are ok
+select pgp_sym_decrypt(pgp_sym_encrypt('Secret.', 'key'),
+       'key', 'expect-cipher-algo=aes128,
+               expect-disable-mdc=0,
+               expect-sess-key=0,
+               expect-s2k-mode=3,
+               expect-s2k-digest-algo=sha1,
+               expect-compress-algo=0
+               ');
+ERROR:  pg_random_bytes() is not supported by this build
+DETAIL:  This functionality requires a source of strong random numbers
+HINT:  You need to rebuild PostgreSQL using --enable-strong-random
+-- maybe the expect- stuff simply does not work
+select pgp_sym_decrypt(pgp_sym_encrypt('Secret.', 'key'),
+       'key', 'expect-cipher-algo=bf,
+               expect-disable-mdc=1,
+               expect-sess-key=1,
+               expect-s2k-mode=0,
+               expect-s2k-digest-algo=md5,
+               expect-compress-algo=1
+               ');
+ERROR:  pg_random_bytes() is not supported by this build
+DETAIL:  This functionality requires a source of strong random numbers
+HINT:  You need to rebuild PostgreSQL using --enable-strong-random
+-- bytea as text
+select pgp_sym_decrypt(pgp_sym_encrypt_bytea('Binary', 'baz'), 'baz');
+ERROR:  pg_random_bytes() is not supported by this build
+DETAIL:  This functionality requires a source of strong random numbers
+HINT:  You need to rebuild PostgreSQL using --enable-strong-random
+-- text as bytea
+select pgp_sym_decrypt_bytea(pgp_sym_encrypt('Text', 'baz'), 'baz');
+ERROR:  pg_random_bytes() is not supported by this build
+DETAIL:  This functionality requires a source of strong random numbers
+HINT:  You need to rebuild PostgreSQL using --enable-strong-random
+-- algorithm change
+select pgp_sym_decrypt(
+       pgp_sym_encrypt('Secret.', 'key', 'cipher-algo=bf'),
+       'key', 'expect-cipher-algo=bf');
+ERROR:  pg_random_bytes() is not supported by this build
+DETAIL:  This functionality requires a source of strong random numbers
+HINT:  You need to rebuild PostgreSQL using --enable-strong-random
+select pgp_sym_decrypt(
+       pgp_sym_encrypt('Secret.', 'key', 'cipher-algo=aes'),
+       'key', 'expect-cipher-algo=aes128');
+ERROR:  pg_random_bytes() is not supported by this build
+DETAIL:  This functionality requires a source of strong random numbers
+HINT:  You need to rebuild PostgreSQL using --enable-strong-random
+select pgp_sym_decrypt(
+       pgp_sym_encrypt('Secret.', 'key', 'cipher-algo=aes192'),
+       'key', 'expect-cipher-algo=aes192');
+ERROR:  pg_random_bytes() is not supported by this build
+DETAIL:  This functionality requires a source of strong random numbers
+HINT:  You need to rebuild PostgreSQL using --enable-strong-random
+-- s2k change
+select pgp_sym_decrypt(
+       pgp_sym_encrypt('Secret.', 'key', 's2k-mode=0'),
+       'key', 'expect-s2k-mode=0');
+ERROR:  pg_random_bytes() is not supported by this build
+DETAIL:  This functionality requires a source of strong random numbers
+HINT:  You need to rebuild PostgreSQL using --enable-strong-random
+select pgp_sym_decrypt(
+       pgp_sym_encrypt('Secret.', 'key', 's2k-mode=1'),
+       'key', 'expect-s2k-mode=1');
+ERROR:  pg_random_bytes() is not supported by this build
+DETAIL:  This functionality requires a source of strong random numbers
+HINT:  You need to rebuild PostgreSQL using --enable-strong-random
+select pgp_sym_decrypt(
+       pgp_sym_encrypt('Secret.', 'key', 's2k-mode=3'),
+       'key', 'expect-s2k-mode=3');
+ERROR:  pg_random_bytes() is not supported by this build
+DETAIL:  This functionality requires a source of strong random numbers
+HINT:  You need to rebuild PostgreSQL using --enable-strong-random
+-- s2k count change
+select pgp_sym_decrypt(
+       pgp_sym_encrypt('Secret.', 'key', 's2k-count=1024'),
+       'key', 'expect-s2k-count=1024');
+ERROR:  pg_random_bytes() is not supported by this build
+DETAIL:  This functionality requires a source of strong random numbers
+HINT:  You need to rebuild PostgreSQL using --enable-strong-random
+-- s2k_count rounds up
+select pgp_sym_decrypt(
+       pgp_sym_encrypt('Secret.', 'key', 's2k-count=65000000'),
+       'key', 'expect-s2k-count=65000000');
+ERROR:  pg_random_bytes() is not supported by this build
+DETAIL:  This functionality requires a source of strong random numbers
+HINT:  You need to rebuild PostgreSQL using --enable-strong-random
+-- s2k digest change
+select pgp_sym_decrypt(
+       pgp_sym_encrypt('Secret.', 'key', 's2k-digest-algo=md5'),
+       'key', 'expect-s2k-digest-algo=md5');
+ERROR:  pg_random_bytes() is not supported by this build
+DETAIL:  This functionality requires a source of strong random numbers
+HINT:  You need to rebuild PostgreSQL using --enable-strong-random
+select pgp_sym_decrypt(
+               pgp_sym_encrypt('Secret.', 'key', 's2k-digest-algo=sha1'),
+       'key', 'expect-s2k-digest-algo=sha1');
+ERROR:  pg_random_bytes() is not supported by this build
+DETAIL:  This functionality requires a source of strong random numbers
+HINT:  You need to rebuild PostgreSQL using --enable-strong-random
+-- sess key
+select pgp_sym_decrypt(
+       pgp_sym_encrypt('Secret.', 'key', 'sess-key=0'),
+       'key', 'expect-sess-key=0');
+ERROR:  pg_random_bytes() is not supported by this build
+DETAIL:  This functionality requires a source of strong random numbers
+HINT:  You need to rebuild PostgreSQL using --enable-strong-random
+select pgp_sym_decrypt(
+       pgp_sym_encrypt('Secret.', 'key', 'sess-key=1'),
+       'key', 'expect-sess-key=1');
+ERROR:  pg_random_bytes() is not supported by this build
+DETAIL:  This functionality requires a source of strong random numbers
+HINT:  You need to rebuild PostgreSQL using --enable-strong-random
+select pgp_sym_decrypt(
+       pgp_sym_encrypt('Secret.', 'key', 'sess-key=1, cipher-algo=bf'),
+       'key', 'expect-sess-key=1, expect-cipher-algo=bf');
+ERROR:  pg_random_bytes() is not supported by this build
+DETAIL:  This functionality requires a source of strong random numbers
+HINT:  You need to rebuild PostgreSQL using --enable-strong-random
+select pgp_sym_decrypt(
+       pgp_sym_encrypt('Secret.', 'key', 'sess-key=1, cipher-algo=aes192'),
+       'key', 'expect-sess-key=1, expect-cipher-algo=aes192');
+ERROR:  pg_random_bytes() is not supported by this build
+DETAIL:  This functionality requires a source of strong random numbers
+HINT:  You need to rebuild PostgreSQL using --enable-strong-random
+select pgp_sym_decrypt(
+       pgp_sym_encrypt('Secret.', 'key', 'sess-key=1, cipher-algo=aes256'),
+       'key', 'expect-sess-key=1, expect-cipher-algo=aes256');
+ERROR:  pg_random_bytes() is not supported by this build
+DETAIL:  This functionality requires a source of strong random numbers
+HINT:  You need to rebuild PostgreSQL using --enable-strong-random
+-- no mdc
+select pgp_sym_decrypt(
+               pgp_sym_encrypt('Secret.', 'key', 'disable-mdc=1'),
+       'key', 'expect-disable-mdc=1');
+ERROR:  pg_random_bytes() is not supported by this build
+DETAIL:  This functionality requires a source of strong random numbers
+HINT:  You need to rebuild PostgreSQL using --enable-strong-random
+-- crlf
+select encode(pgp_sym_decrypt_bytea(
+       pgp_sym_encrypt(E'1\n2\n3\r\n', 'key', 'convert-crlf=1'),
+       'key'), 'hex');
+ERROR:  pg_random_bytes() is not supported by this build
+DETAIL:  This functionality requires a source of strong random numbers
+HINT:  You need to rebuild PostgreSQL using --enable-strong-random
+-- conversion should be lossless
+select encode(digest(pgp_sym_decrypt(
+  pgp_sym_encrypt(E'\r\n0\n1\r\r\n\n2\r', 'key', 'convert-crlf=1'),
+       'key', 'convert-crlf=1'), 'sha1'), 'hex') as result,
+  encode(digest(E'\r\n0\n1\r\r\n\n2\r', 'sha1'), 'hex') as expect;
+ERROR:  pg_random_bytes() is not supported by this build
+DETAIL:  This functionality requires a source of strong random numbers
+HINT:  You need to rebuild PostgreSQL using --enable-strong-random
diff --git a/contrib/pgcrypto/expected/pgp-pubkey-encrypt_1.out b/contrib/pgcrypto/expected/pgp-pubkey-encrypt_1.out
new file mode 100644 (file)
index 0000000..3b1822e
--- /dev/null
@@ -0,0 +1,62 @@
+--
+-- PGP Public Key Encryption
+--
+-- ensure consistent test output regardless of the default bytea format
+SET bytea_output TO escape;
+-- successful encrypt/decrypt
+select pgp_pub_decrypt(
+       pgp_pub_encrypt('Secret msg', dearmor(pubkey)),
+       dearmor(seckey))
+from keytbl where keytbl.id=1;
+ERROR:  pg_random_bytes() is not supported by this build
+DETAIL:  This functionality requires a source of strong random numbers
+HINT:  You need to rebuild PostgreSQL using --enable-strong-random
+select pgp_pub_decrypt(
+               pgp_pub_encrypt('Secret msg', dearmor(pubkey)),
+               dearmor(seckey))
+from keytbl where keytbl.id=2;
+ERROR:  pg_random_bytes() is not supported by this build
+DETAIL:  This functionality requires a source of strong random numbers
+HINT:  You need to rebuild PostgreSQL using --enable-strong-random
+select pgp_pub_decrypt(
+               pgp_pub_encrypt('Secret msg', dearmor(pubkey)),
+               dearmor(seckey))
+from keytbl where keytbl.id=3;
+ERROR:  pg_random_bytes() is not supported by this build
+DETAIL:  This functionality requires a source of strong random numbers
+HINT:  You need to rebuild PostgreSQL using --enable-strong-random
+select pgp_pub_decrypt(
+               pgp_pub_encrypt('Secret msg', dearmor(pubkey)),
+               dearmor(seckey))
+from keytbl where keytbl.id=6;
+ERROR:  pg_random_bytes() is not supported by this build
+DETAIL:  This functionality requires a source of strong random numbers
+HINT:  You need to rebuild PostgreSQL using --enable-strong-random
+-- try with rsa-sign only
+select pgp_pub_decrypt(
+               pgp_pub_encrypt('Secret msg', dearmor(pubkey)),
+               dearmor(seckey))
+from keytbl where keytbl.id=4;
+ERROR:  No encryption key found
+-- try with secret key
+select pgp_pub_decrypt(
+               pgp_pub_encrypt('Secret msg', dearmor(seckey)),
+               dearmor(seckey))
+from keytbl where keytbl.id=1;
+ERROR:  Refusing to encrypt with secret key
+-- does text-to-bytea works
+select pgp_pub_decrypt_bytea(
+               pgp_pub_encrypt('Secret msg', dearmor(pubkey)),
+               dearmor(seckey))
+from keytbl where keytbl.id=1;
+ERROR:  pg_random_bytes() is not supported by this build
+DETAIL:  This functionality requires a source of strong random numbers
+HINT:  You need to rebuild PostgreSQL using --enable-strong-random
+-- and bytea-to-text?
+select pgp_pub_decrypt(
+               pgp_pub_encrypt_bytea('Secret msg', dearmor(pubkey)),
+               dearmor(seckey))
+from keytbl where keytbl.id=1;
+ERROR:  pg_random_bytes() is not supported by this build
+DETAIL:  This functionality requires a source of strong random numbers
+HINT:  You need to rebuild PostgreSQL using --enable-strong-random
diff --git a/contrib/pgcrypto/fortuna.c b/contrib/pgcrypto/fortuna.c
deleted file mode 100644 (file)
index 5028203..0000000
+++ /dev/null
@@ -1,463 +0,0 @@
-/*
- * fortuna.c
- *             Fortuna-like PRNG.
- *
- * Copyright (c) 2005 Marko Kreen
- * 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.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS 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 AUTHOR OR 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.
- *
- * contrib/pgcrypto/fortuna.c
- */
-
-#include "postgres.h"
-
-#include <sys/time.h>
-#include <time.h>
-
-#include "px.h"
-#include "rijndael.h"
-#include "sha2.h"
-#include "fortuna.h"
-
-
-/*
- * Why Fortuna-like: There does not seem to be any definitive reference
- * on Fortuna in the net.  Instead this implementation is based on
- * following references:
- *
- * http://en.wikipedia.org/wiki/Fortuna_(PRNG)
- *      - Wikipedia article
- * http://jlcooke.ca/random/
- *      - Jean-Luc Cooke Fortuna-based /dev/random driver for Linux.
- */
-
-/*
- * There is some confusion about whether and how to carry forward
- * the state of the pools.  Seems like original Fortuna does not
- * do it, resetting hash after each request.  I guess expecting
- * feeding to happen more often that requesting.   This is absolutely
- * unsuitable for pgcrypto, as nothing asynchronous happens here.
- *
- * J.L. Cooke fixed this by feeding previous hash to new re-initialized
- * hash context.
- *
- * Fortuna predecessor Yarrow requires ability to query intermediate
- * 'final result' from hash, without affecting it.
- *
- * This implementation uses the Yarrow method - asking intermediate
- * results, but continuing with old state.
- */
-
-
-/*
- * Algorithm parameters
- */
-
-/*
- * How many pools.
- *
- * Original Fortuna uses 32 pools, that means 32'th pool is
- * used not earlier than in 13th year.  This is a waste in
- * pgcrypto, as we have very low-frequancy seeding.  Here
- * is preferable to have all entropy usable in reasonable time.
- *
- * With 23 pools, 23th pool is used after 9 days which seems
- * more sane.
- *
- * In our case the minimal cycle time would be bit longer
- * than the system-randomness feeding frequency.
- */
-#define NUM_POOLS              23
-
-/* in microseconds */
-#define RESEED_INTERVAL 100000 /* 0.1 sec */
-
-/* for one big request, reseed after this many bytes */
-#define RESEED_BYTES   (1024*1024)
-
-/*
- * Skip reseed if pool 0 has less than this many
- * bytes added since last reseed.
- */
-#define POOL0_FILL             (256/8)
-
-/*
- * Algorithm constants
- */
-
-/* Both cipher key size and hash result size */
-#define BLOCK                  32
-
-/* cipher block size */
-#define CIPH_BLOCK             16
-
-/* for internal wrappers */
-#define MD_CTX                 SHA256_CTX
-#define CIPH_CTX               rijndael_ctx
-
-struct fortuna_state
-{
-       uint8           counter[CIPH_BLOCK];
-       uint8           result[CIPH_BLOCK];
-       uint8           key[BLOCK];
-       MD_CTX          pool[NUM_POOLS];
-       CIPH_CTX        ciph;
-       unsigned        reseed_count;
-       struct timeval last_reseed_time;
-       unsigned        pool0_bytes;
-       unsigned        rnd_pos;
-       int                     tricks_done;
-};
-typedef struct fortuna_state FState;
-
-
-/*
- * Use our own wrappers here.
- * - Need to get intermediate result from digest, without affecting it.
- * - Need re-set key on a cipher context.
- * - Algorithms are guaranteed to exist.
- * - No memory allocations.
- */
-
-static void
-ciph_init(CIPH_CTX * ctx, const uint8 *key, int klen)
-{
-       rijndael_set_key(ctx, (const uint32 *) key, klen, 1);
-}
-
-static void
-ciph_encrypt(CIPH_CTX * ctx, const uint8 *in, uint8 *out)
-{
-       rijndael_encrypt(ctx, (const uint32 *) in, (uint32 *) out);
-}
-
-static void
-md_init(MD_CTX * ctx)
-{
-       SHA256_Init(ctx);
-}
-
-static void
-md_update(MD_CTX * ctx, const uint8 *data, int len)
-{
-       SHA256_Update(ctx, data, len);
-}
-
-static void
-md_result(MD_CTX * ctx, uint8 *dst)
-{
-       SHA256_CTX      tmp;
-
-       memcpy(&tmp, ctx, sizeof(*ctx));
-       SHA256_Final(dst, &tmp);
-       px_memset(&tmp, 0, sizeof(tmp));
-}
-
-/*
- * initialize state
- */
-static void
-init_state(FState *st)
-{
-       int                     i;
-
-       memset(st, 0, sizeof(*st));
-       for (i = 0; i < NUM_POOLS; i++)
-               md_init(&st->pool[i]);
-}
-
-/*
- * Endianess does not matter.
- * It just needs to change without repeating.
- */
-static void
-inc_counter(FState *st)
-{
-       uint32     *val = (uint32 *) st->counter;
-
-       if (++val[0])
-               return;
-       if (++val[1])
-               return;
-       if (++val[2])
-               return;
-       ++val[3];
-}
-
-/*
- * This is called 'cipher in counter mode'.
- */
-static void
-encrypt_counter(FState *st, uint8 *dst)
-{
-       ciph_encrypt(&st->ciph, st->counter, dst);
-       inc_counter(st);
-}
-
-
-/*
- * The time between reseed must be at least RESEED_INTERVAL
- * microseconds.
- */
-static int
-enough_time_passed(FState *st)
-{
-       int                     ok;
-       struct timeval tv;
-       struct timeval *last = &st->last_reseed_time;
-
-       gettimeofday(&tv, NULL);
-
-       /* check how much time has passed */
-       ok = 0;
-       if (tv.tv_sec > last->tv_sec + 1)
-               ok = 1;
-       else if (tv.tv_sec == last->tv_sec + 1)
-       {
-               if (1000000 + tv.tv_usec - last->tv_usec >= RESEED_INTERVAL)
-                       ok = 1;
-       }
-       else if (tv.tv_usec - last->tv_usec >= RESEED_INTERVAL)
-               ok = 1;
-
-       /* reseed will happen, update last_reseed_time */
-       if (ok)
-               memcpy(last, &tv, sizeof(tv));
-
-       px_memset(&tv, 0, sizeof(tv));
-
-       return ok;
-}
-
-/*
- * generate new key from all the pools
- */
-static void
-reseed(FState *st)
-{
-       unsigned        k;
-       unsigned        n;
-       MD_CTX          key_md;
-       uint8           buf[BLOCK];
-
-       /* set pool as empty */
-       st->pool0_bytes = 0;
-
-       /*
-        * Both #0 and #1 reseed would use only pool 0. Just skip #0 then.
-        */
-       n = ++st->reseed_count;
-
-       /*
-        * The goal: use k-th pool only 1/(2^k) of the time.
-        */
-       md_init(&key_md);
-       for (k = 0; k < NUM_POOLS; k++)
-       {
-               md_result(&st->pool[k], buf);
-               md_update(&key_md, buf, BLOCK);
-
-               if (n & 1 || !n)
-                       break;
-               n >>= 1;
-       }
-
-       /* add old key into mix too */
-       md_update(&key_md, st->key, BLOCK);
-
-       /* now we have new key */
-       md_result(&key_md, st->key);
-
-       /* use new key */
-       ciph_init(&st->ciph, st->key, BLOCK);
-
-       px_memset(&key_md, 0, sizeof(key_md));
-       px_memset(buf, 0, BLOCK);
-}
-
-/*
- * Pick a random pool.  This uses key bytes as random source.
- */
-static unsigned
-get_rand_pool(FState *st)
-{
-       unsigned        rnd;
-
-       /*
-        * This slightly prefers lower pools - that is OK.
-        */
-       rnd = st->key[st->rnd_pos] % NUM_POOLS;
-
-       st->rnd_pos++;
-       if (st->rnd_pos >= BLOCK)
-               st->rnd_pos = 0;
-
-       return rnd;
-}
-
-/*
- * update pools
- */
-static void
-add_entropy(FState *st, const uint8 *data, unsigned len)
-{
-       unsigned        pos;
-       uint8           hash[BLOCK];
-       MD_CTX          md;
-
-       /* hash given data */
-       md_init(&md);
-       md_update(&md, data, len);
-       md_result(&md, hash);
-
-       /*
-        * Make sure the pool 0 is initialized, then update randomly.
-        */
-       if (st->reseed_count == 0)
-               pos = 0;
-       else
-               pos = get_rand_pool(st);
-       md_update(&st->pool[pos], hash, BLOCK);
-
-       if (pos == 0)
-               st->pool0_bytes += len;
-
-       px_memset(hash, 0, BLOCK);
-       px_memset(&md, 0, sizeof(md));
-}
-
-/*
- * Just take 2 next blocks as new key
- */
-static void
-rekey(FState *st)
-{
-       encrypt_counter(st, st->key);
-       encrypt_counter(st, st->key + CIPH_BLOCK);
-       ciph_init(&st->ciph, st->key, BLOCK);
-}
-
-/*
- * Hide public constants. (counter, pools > 0)
- *
- * This can also be viewed as spreading the startup
- * entropy over all of the components.
- */
-static void
-startup_tricks(FState *st)
-{
-       int                     i;
-       uint8           buf[BLOCK];
-
-       /* Use next block as counter. */
-       encrypt_counter(st, st->counter);
-
-       /* Now shuffle pools, excluding #0 */
-       for (i = 1; i < NUM_POOLS; i++)
-       {
-               encrypt_counter(st, buf);
-               encrypt_counter(st, buf + CIPH_BLOCK);
-               md_update(&st->pool[i], buf, BLOCK);
-       }
-       px_memset(buf, 0, BLOCK);
-
-       /* Hide the key. */
-       rekey(st);
-
-       /* This can be done only once. */
-       st->tricks_done = 1;
-}
-
-static void
-extract_data(FState *st, unsigned count, uint8 *dst)
-{
-       unsigned        n;
-       unsigned        block_nr = 0;
-
-       /* Should we reseed? */
-       if (st->pool0_bytes >= POOL0_FILL || st->reseed_count == 0)
-               if (enough_time_passed(st))
-                       reseed(st);
-
-       /* Do some randomization on first call */
-       if (!st->tricks_done)
-               startup_tricks(st);
-
-       while (count > 0)
-       {
-               /* produce bytes */
-               encrypt_counter(st, st->result);
-
-               /* copy result */
-               if (count > CIPH_BLOCK)
-                       n = CIPH_BLOCK;
-               else
-                       n = count;
-               memcpy(dst, st->result, n);
-               dst += n;
-               count -= n;
-
-               /* must not give out too many bytes with one key */
-               block_nr++;
-               if (block_nr > (RESEED_BYTES / CIPH_BLOCK))
-               {
-                       rekey(st);
-                       block_nr = 0;
-               }
-       }
-       /* Set new key for next request. */
-       rekey(st);
-}
-
-/*
- * public interface
- */
-
-static FState main_state;
-static int     init_done = 0;
-
-void
-fortuna_add_entropy(const uint8 *data, unsigned len)
-{
-       if (!init_done)
-       {
-               init_state(&main_state);
-               init_done = 1;
-       }
-       if (!data || !len)
-               return;
-       add_entropy(&main_state, data, len);
-}
-
-void
-fortuna_get_bytes(unsigned len, uint8 *dst)
-{
-       if (!init_done)
-       {
-               init_state(&main_state);
-               init_done = 1;
-       }
-       if (!dst || !len)
-               return;
-       extract_data(&main_state, len, dst);
-}
diff --git a/contrib/pgcrypto/fortuna.h b/contrib/pgcrypto/fortuna.h
deleted file mode 100644 (file)
index bf9f476..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * fortuna.c
- *             Fortuna PRNG.
- *
- * Copyright (c) 2005 Marko Kreen
- * 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.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS 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 AUTHOR OR 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.
- *
- * contrib/pgcrypto/fortuna.h
- */
-
-#ifndef __FORTUNA_H
-#define __FORTUNA_H
-
-void           fortuna_get_bytes(unsigned len, uint8 *dst);
-void           fortuna_add_entropy(const uint8 *data, unsigned len);
-
-#endif
index 02ff976c25a6e89c929208ececccfdf1b698a66c..2516092ba4ec4db85ebe4568b5fcaf836c0c7548 100644 (file)
@@ -38,7 +38,6 @@
 #include "sha1.h"
 #include "blf.h"
 #include "rijndael.h"
-#include "fortuna.h"
 
 /*
  * System reseeds should be separated at least this much.
@@ -615,65 +614,3 @@ px_find_cipher(const char *name, PX_Cipher **res)
        *res = c;
        return 0;
 }
-
-/*
- * Randomness provider
- */
-
-static time_t seed_time = 0;
-static time_t check_time = 0;
-
-static void
-system_reseed(void)
-{
-       uint8           buf[1024];
-       int                     n;
-       time_t          t;
-       int                     skip = 1;
-
-       t = time(NULL);
-
-       if (seed_time == 0)
-               skip = 0;
-       else if ((t - seed_time) < SYSTEM_RESEED_MIN)
-               skip = 1;
-       else if ((t - seed_time) > SYSTEM_RESEED_MAX)
-               skip = 0;
-       else if (check_time == 0 ||
-                        (t - check_time) > SYSTEM_RESEED_CHECK_TIME)
-       {
-               check_time = t;
-
-               /* roll dice */
-               px_get_random_bytes(buf, 1);
-               skip = buf[0] >= SYSTEM_RESEED_CHANCE;
-       }
-       /* clear 1 byte */
-       px_memset(buf, 0, sizeof(buf));
-
-       if (skip)
-               return;
-
-       n = px_acquire_system_randomness(buf);
-       if (n > 0)
-               fortuna_add_entropy(buf, n);
-
-       seed_time = t;
-       px_memset(buf, 0, sizeof(buf));
-}
-
-int
-px_get_random_bytes(uint8 *dst, unsigned count)
-{
-       system_reseed();
-       fortuna_get_bytes(count, dst);
-       return 0;
-}
-
-int
-px_add_entropy(const uint8 *data, unsigned count)
-{
-       system_reseed();
-       fortuna_add_entropy(data, count);
-       return 0;
-}
index f3e3a924867686204fbcff5cf49b69846df92f57..1d3e58d925d9df25cf395e2e6bed5ef930c9a5b6 100644 (file)
@@ -712,49 +712,3 @@ px_find_cipher(const char *name, PX_Cipher **res)
        *res = c;
        return 0;
 }
-
-
-static int     openssl_random_init = 0;
-
-/*
- * OpenSSL random should re-feeded occasionally. From /dev/urandom
- * preferably.
- */
-static void
-init_openssl_rand(void)
-{
-       if (RAND_get_rand_method() == NULL)
-       {
-#ifdef HAVE_RAND_OPENSSL
-               RAND_set_rand_method(RAND_OpenSSL());
-#else
-               RAND_set_rand_method(RAND_SSLeay());
-#endif
-       }
-       openssl_random_init = 1;
-}
-
-int
-px_get_random_bytes(uint8 *dst, unsigned count)
-{
-       int                     res;
-
-       if (!openssl_random_init)
-               init_openssl_rand();
-
-       res = RAND_bytes(dst, count);
-       if (res == 1)
-               return count;
-
-       return PXE_OSSL_RAND_ERROR;
-}
-
-int
-px_add_entropy(const uint8 *data, unsigned count)
-{
-       /*
-        * estimate 0 bits
-        */
-       RAND_add(data, count, 0);
-       return 0;
-}
index 27b96c7cc4a6022842d58ebf697e9aabad505368..d815de30734f5782073cf712427288185b15ae6a 100644 (file)
@@ -34,6 +34,7 @@
 #include <ctype.h>
 
 #include "parser/scansup.h"
+#include "utils/backend_random.h"
 #include "utils/builtins.h"
 #include "utils/uuid.h"
 
@@ -422,7 +423,7 @@ PG_FUNCTION_INFO_V1(pg_random_bytes);
 Datum
 pg_random_bytes(PG_FUNCTION_ARGS)
 {
-       int                     err;
+#ifdef HAVE_STRONG_RANDOM
        int                     len = PG_GETARG_INT32(0);
        bytea      *res;
 
@@ -435,13 +436,13 @@ pg_random_bytes(PG_FUNCTION_ARGS)
        SET_VARSIZE(res, VARHDRSZ + len);
 
        /* generate result */
-       err = px_get_random_bytes((uint8 *) VARDATA(res), len);
-       if (err < 0)
-               ereport(ERROR,
-                               (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
-                                errmsg("Random generator error: %s", px_strerror(err))));
+       if (!pg_strong_random(VARDATA(res), len))
+               px_THROW_ERROR(PXE_NO_RANDOM);
 
        PG_RETURN_BYTEA_P(res);
+#else
+       px_THROW_ERROR(PXE_NO_RANDOM);
+#endif
 }
 
 /* SQL function: gen_random_uuid() returns uuid */
@@ -451,14 +452,14 @@ Datum
 pg_random_uuid(PG_FUNCTION_ARGS)
 {
        uint8      *buf = (uint8 *) palloc(UUID_LEN);
-       int                     err;
 
-       /* generate random bits */
-       err = px_get_random_bytes(buf, UUID_LEN);
-       if (err < 0)
-               ereport(ERROR,
-                               (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
-                                errmsg("Random generator error: %s", px_strerror(err))));
+       /*
+        * Generate random bits. pg_backend_random() will do here, we don't
+        * promis UUIDs to be cryptographically random, when built with
+        * --disable-strong-random.
+        */
+       if (!pg_backend_random((char *) buf, UUID_LEN))
+               px_THROW_ERROR(PXE_NO_RANDOM);
 
        /*
         * Set magic numbers for a "version 4" (pseudorandom) UUID, see
index c9148fd2fc6d0f630ba1116a5d753157791d9d85..be933bf86c1a4e77029ef15be50ecb201528eaa2 100644 (file)
@@ -37,6 +37,8 @@
 #include "px.h"
 #include "pgp.h"
 
+#include "utils/backend_random.h"
+
 
 #define MDC_DIGEST_LEN 20
 #define STREAM_ID 0xE0
@@ -477,14 +479,14 @@ init_encdata_packet(PushFilter **pf_res, PGP_Context *ctx, PushFilter *dst)
 static int
 write_prefix(PGP_Context *ctx, PushFilter *dst)
 {
+#ifdef HAVE_STRONG_RANDOM
        uint8           prefix[PGP_MAX_BLOCK + 2];
        int                     res,
                                bs;
 
        bs = pgp_get_cipher_block_size(ctx->cipher_algo);
-       res = px_get_random_bytes(prefix, bs);
-       if (res < 0)
-               return res;
+       if (!pg_backend_random((char *) prefix, bs))
+               return PXE_NO_RANDOM;
 
        prefix[bs + 0] = prefix[bs - 2];
        prefix[bs + 1] = prefix[bs - 1];
@@ -492,6 +494,9 @@ write_prefix(PGP_Context *ctx, PushFilter *dst)
        res = pushf_write(dst, prefix, bs + 2);
        px_memset(prefix, 0, bs + 2);
        return res < 0 ? res : 0;
+#else
+       return PXE_NO_RANDOM;
+#endif
 }
 
 /*
@@ -578,14 +583,15 @@ init_s2k_key(PGP_Context *ctx)
 static int
 init_sess_key(PGP_Context *ctx)
 {
-       int                     res;
-
        if (ctx->use_sess_key || ctx->pub_key)
        {
+#ifdef HAVE_STRONG_RANDOM
                ctx->sess_key_len = pgp_get_cipher_key_size(ctx->cipher_algo);
-               res = px_get_random_bytes(ctx->sess_key, ctx->sess_key_len);
-               if (res < 0)
-                       return res;
+               if (!pg_strong_random((char *) ctx->sess_key, ctx->sess_key_len))
+                       return PXE_NO_RANDOM;
+#else
+               return PXE_NO_RANDOM;
+#endif
        }
        else
        {
index be95f2d092690a4dd8378e4b21ea24dddd37f57c..cb70fcba6cc13d0d5ee08c5a99d6ed7abf1852c3 100644 (file)
@@ -57,17 +57,16 @@ mp_clear_free(mpz_t *a)
 static int
 mp_px_rand(uint32 bits, mpz_t *res)
 {
-       int                     err;
+#ifdef HAVE_STRONG_RANDOM
        unsigned        bytes = (bits + 7) / 8;
        int                     last_bits = bits & 7;
        uint8      *buf;
 
        buf = px_alloc(bytes);
-       err = px_get_random_bytes(buf, bytes);
-       if (err < 0)
+       if (!pg_strong_random((char *) buf, bytes))
        {
                px_free(buf);
-               return err;
+               return PXE_NO_RANDOM;
        }
 
        /* clear unnecessary bits and set last bit to one */
@@ -84,6 +83,9 @@ mp_px_rand(uint32 bits, mpz_t *res)
        px_free(buf);
 
        return 0;
+#else
+       return PXE_NO_RANDOM;
+#endif
 }
 
 static void
index 1f65b667cab3747cf2af2216b577284f31e3051c..ce16db71d878c279df9dc40bb930534d4a135a0b 100644 (file)
@@ -61,65 +61,6 @@ PG_FUNCTION_INFO_V1(pg_armor);
 PG_FUNCTION_INFO_V1(pg_dearmor);
 PG_FUNCTION_INFO_V1(pgp_armor_headers);
 
-/*
- * Mix a block of data into RNG.
- */
-static void
-add_block_entropy(PX_MD *md, text *data)
-{
-       uint8           sha1[20];
-
-       px_md_reset(md);
-       px_md_update(md, (uint8 *) VARDATA(data), VARSIZE(data) - VARHDRSZ);
-       px_md_finish(md, sha1);
-
-       px_add_entropy(sha1, 20);
-
-       px_memset(sha1, 0, 20);
-}
-
-/*
- * Mix user data into RNG.  It is for user own interests to have
- * RNG state shuffled.
- */
-static void
-add_entropy(text *data1, text *data2, text *data3)
-{
-       PX_MD      *md;
-       uint8           rnd[3];
-
-       if (!data1 && !data2 && !data3)
-               return;
-
-       if (px_get_random_bytes(rnd, 3) < 0)
-               return;
-
-       if (px_find_digest("sha1", &md) < 0)
-               return;
-
-       /*
-        * Try to make the feeding unpredictable.
-        *
-        * Prefer data over keys, as it's rather likely that key is same in
-        * several calls.
-        */
-
-       /* chance: 7/8 */
-       if (data1 && rnd[0] >= 32)
-               add_block_entropy(md, data1);
-
-       /* chance: 5/8 */
-       if (data2 && rnd[1] >= 160)
-               add_block_entropy(md, data2);
-
-       /* chance: 5/8 */
-       if (data3 && rnd[2] >= 160)
-               add_block_entropy(md, data3);
-
-       px_md_free(md);
-       px_memset(rnd, 0, sizeof(rnd));
-}
-
 /*
  * returns src in case of no conversion or error
  */
@@ -432,11 +373,7 @@ init_work(PGP_Context **ctx_p, int is_text,
                                                 VARSIZE(args) - VARHDRSZ, ex);
 
        if (err)
-       {
-               ereport(ERROR,
-                               (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
-                                errmsg("%s", px_strerror(err))));
-       }
+               px_THROW_ERROR(err);
 
        if (ex->debug)
                px_set_debug_handler(show_debug);
@@ -459,11 +396,6 @@ encrypt_internal(int is_pubenc, int is_text,
        struct debug_expect ex;
        text       *tmp_data = NULL;
 
-       /*
-        * Add data and key info RNG.
-        */
-       add_entropy(data, key, NULL);
-
        init_work(&ctx, is_text, args, &ex);
 
        if (is_text && pgp_get_unicode_mode(ctx))
@@ -516,9 +448,7 @@ encrypt_internal(int is_pubenc, int is_text,
                pgp_free(ctx);
                mbuf_free(src);
                mbuf_free(dst);
-               ereport(ERROR,
-                               (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
-                                errmsg("%s", px_strerror(err))));
+               px_THROW_ERROR(err);
        }
 
        /* res_len includes VARHDRSZ */
@@ -605,9 +535,7 @@ decrypt_internal(int is_pubenc, int need_text, text *data,
        {
                px_set_debug_handler(NULL);
                mbuf_free(dst);
-               ereport(ERROR,
-                               (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
-                                errmsg("%s", px_strerror(err))));
+               px_THROW_ERROR(err);
        }
 
        res_len = mbuf_steal_data(dst, &restmp);
@@ -629,11 +557,6 @@ decrypt_internal(int is_pubenc, int need_text, text *data,
        }
        px_set_debug_handler(NULL);
 
-       /*
-        * add successful decryptions also into RNG
-        */
-       add_entropy(res, key, keypsw);
-
        return res;
 }
 
@@ -985,9 +908,7 @@ pg_dearmor(PG_FUNCTION_ARGS)
 
        ret = pgp_armor_decode((uint8 *) VARDATA(data), data_len, &buf);
        if (ret < 0)
-               ereport(ERROR,
-                               (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
-                                errmsg("%s", px_strerror(ret))));
+               px_THROW_ERROR(ret);
        res = palloc(VARHDRSZ + buf.len);
        SET_VARSIZE(res, VARHDRSZ + buf.len);
        memcpy(VARDATA(res), buf.data, buf.len);
@@ -1041,9 +962,7 @@ pgp_armor_headers(PG_FUNCTION_ARGS)
                                                                                &state->nheaders, &state->keys,
                                                                                &state->values);
                if (res < 0)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
-                                        errmsg("%s", px_strerror(res))));
+                       px_THROW_ERROR(res);
 
                MemoryContextSwitchTo(oldcontext);
                funcctx->user_fctx = state;
@@ -1092,9 +1011,7 @@ pgp_key_id_w(PG_FUNCTION_ARGS)
        res_len = pgp_get_keyid(buf, VARDATA(res));
        mbuf_free(buf);
        if (res_len < 0)
-               ereport(ERROR,
-                               (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
-                                errmsg("%s", px_strerror(res_len))));
+               px_THROW_ERROR(res_len);
        SET_VARSIZE(res, VARHDRSZ + res_len);
 
        PG_FREE_IF_COPY(data, 0);
index 3b43bb61c05a02d183a00103acb9baa75f41f6ce..443987666431f9c0b3ce9ffbc78b11738449a4c4 100644 (file)
@@ -39,7 +39,7 @@
 static int
 pad_eme_pkcs1_v15(uint8 *data, int data_len, int res_len, uint8 **res_p)
 {
-       int                     res;
+#ifdef HAVE_STRONG_RANDOM
        uint8      *buf,
                           *p;
        int                     pad_len = res_len - 2 - data_len;
@@ -49,11 +49,11 @@ pad_eme_pkcs1_v15(uint8 *data, int data_len, int res_len, uint8 **res_p)
 
        buf = px_alloc(res_len);
        buf[0] = 0x02;
-       res = px_get_random_bytes(buf + 1, pad_len);
-       if (res < 0)
+
+       if (!pg_strong_random((char *) buf + 1, pad_len))
        {
                px_free(buf);
-               return res;
+               return PXE_NO_RANDOM;
        }
 
        /* pad must not contain zero bytes */
@@ -62,26 +62,26 @@ pad_eme_pkcs1_v15(uint8 *data, int data_len, int res_len, uint8 **res_p)
        {
                if (*p == 0)
                {
-                       res = px_get_random_bytes(p, 1);
-                       if (res < 0)
+                       if (!pg_strong_random((char *) p, 1))
+                       {
+                               px_memset(buf, 0, res_len);
+                               px_free(buf);
                                break;
+                       }
                }
                if (*p != 0)
                        p++;
        }
 
-       if (res < 0)
-       {
-               px_memset(buf, 0, res_len);
-               px_free(buf);
-               return res;
-       }
-
        buf[pad_len + 1] = 0;
        memcpy(buf + pad_len + 2, data, data_len);
        *res_p = buf;
 
        return 0;
+
+#else
+       return PXE_NO_RANDOM;
+#endif
 }
 
 static int
index 3551d44d62bd3494d63b1a604445a848bfba6071..a0fd8969efe2c7203f5320dff20036bfa61384b1 100644 (file)
@@ -34,6 +34,8 @@
 #include "px.h"
 #include "pgp.h"
 
+#include "utils/backend_random.h"
+
 static int
 calc_s2k_simple(PGP_S2K *s2k, PX_MD *md, const uint8 *key,
                                unsigned key_len)
@@ -233,15 +235,14 @@ pgp_s2k_fill(PGP_S2K *s2k, int mode, int digest_algo, int count)
                case PGP_S2K_SIMPLE:
                        break;
                case PGP_S2K_SALTED:
-                       res = px_get_random_bytes(s2k->salt, PGP_S2K_SALT);
+                       if (!pg_backend_random((char *) s2k->salt, PGP_S2K_SALT))
+                               return PXE_NO_RANDOM;
                        break;
                case PGP_S2K_ISALTED:
-                       res = px_get_random_bytes(s2k->salt, PGP_S2K_SALT);
-                       if (res < 0)
-                               break;
-                       res = px_get_random_bytes(&tmp, 1);
-                       if (res < 0)
-                               break;
+                       if (!pg_backend_random((char *) s2k->salt, PGP_S2K_SALT))
+                               return PXE_NO_RANDOM;
+                       if (!pg_backend_random((char *) &tmp, 1))
+                               return PXE_NO_RANDOM;
                        s2k->iter = decide_s2k_iter(tmp, count);
                        break;
                default:
index 3d423938502721b633ed75c9dc5612549bab3c53..6c72c4ae8304d97fa99dd7757aa79f99ba0ed9d4 100644 (file)
@@ -34,6 +34,7 @@
 #include "px.h"
 #include "px-crypt.h"
 
+#include "utils/backend_random.h"
 
 static char *
 run_crypt_des(const char *psw, const char *salt,
@@ -132,7 +133,6 @@ static struct generator gen_list[] = {
 int
 px_gen_salt(const char *salt_type, char *buf, int rounds)
 {
-       int                     res;
        struct generator *g;
        char       *p;
        char            rbuf[16];
@@ -153,9 +153,8 @@ px_gen_salt(const char *salt_type, char *buf, int rounds)
                        return PXE_BAD_SALT_ROUNDS;
        }
 
-       res = px_get_random_bytes((uint8 *) rbuf, g->input_len);
-       if (res < 0)
-               return res;
+       if (!pg_backend_random(rbuf, g->input_len))
+               return PXE_NO_RANDOM;
 
        p = g->gen(rounds, rbuf, g->input_len, buf, PX_MAX_SALT_LEN);
        px_memset(rbuf, 0, sizeof(rbuf));
index b01701ea7509eb8217d9c8c1dfbc55968a3bffa4..a5c02f3612d3b673c9c70330ea400e210ca457d1 100644 (file)
@@ -51,7 +51,6 @@ static const struct error_desc px_err_list[] = {
        {PXE_CIPHER_INIT, "Cipher cannot be initialized ?"},
        {PXE_HASH_UNUSABLE_FOR_HMAC, "This hash algorithm is unusable for HMAC"},
        {PXE_DEV_READ_ERROR, "Error reading from random device"},
-       {PXE_OSSL_RAND_ERROR, "OpenSSL PRNG error"},
        {PXE_BUG, "pgcrypto bug"},
        {PXE_ARGUMENT_ERROR, "Illegal argument to function"},
        {PXE_UNKNOWN_SALT_ALGO, "Unknown salt algorithm"},
@@ -86,6 +85,39 @@ static const struct error_desc px_err_list[] = {
        {0, NULL},
 };
 
+/*
+ * Call ereport(ERROR, ...), with an error code and message corresponding to
+ * the PXE_* error code given as argument.
+ *
+ * This is similar to px_strerror(err), but for some errors, we fill in the
+ * error code and detail fields more appropriately.
+ */
+void
+px_THROW_ERROR(int err)
+{
+       if (err == PXE_NO_RANDOM)
+       {
+#ifdef HAVE_STRONG_RANDOM
+               ereport(ERROR,
+                               (errcode(ERRCODE_INTERNAL_ERROR),
+                                errmsg("could not generate a random number")));
+#else
+               ereport(ERROR,
+                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                errmsg("pg_random_bytes() is not supported by this build"),
+                                errdetail("This functionality requires a source of strong random numbers"),
+                                errhint("You need to rebuild PostgreSQL using --enable-strong-random")));
+#endif
+       }
+       else
+       {
+               /* For other errors, use the message from the above list. */
+               ereport(ERROR,
+                               (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
+                                errmsg("%s", px_strerror(err))));
+       }
+}
+
 const char *
 px_strerror(int err)
 {
index 800c552bf6089dfd77ecc48faa0f7b0e94832bff..00fc6f0c015285fe81427a5069b615a4de09335e 100644 (file)
@@ -71,7 +71,6 @@ void          px_free(void *p);
 #define PXE_CIPHER_INIT                                -8
 #define PXE_HASH_UNUSABLE_FOR_HMAC     -9
 #define PXE_DEV_READ_ERROR                     -10
-#define PXE_OSSL_RAND_ERROR                    -11
 #define PXE_BUG                                                -12
 #define PXE_ARGUMENT_ERROR                     -13
 #define PXE_UNKNOWN_SALT_ALGO          -14
@@ -189,11 +188,7 @@ int                        px_find_hmac(const char *name, PX_HMAC **res);
 int                    px_find_cipher(const char *name, PX_Cipher **res);
 int                    px_find_combo(const char *name, PX_Combo **res);
 
-int                    px_get_random_bytes(uint8 *dst, unsigned count);
-int                    px_add_entropy(const uint8 *data, unsigned count);
-
-unsigned       px_acquire_system_randomness(uint8 *dst);
-
+void           px_THROW_ERROR(int err) pg_attribute_noreturn();
 const char *px_strerror(int err);
 
 const char *px_resolve_alias(const PX_Alias *aliases, const char *name);
diff --git a/contrib/pgcrypto/random.c b/contrib/pgcrypto/random.c
deleted file mode 100644 (file)
index d72679e..0000000
+++ /dev/null
@@ -1,247 +0,0 @@
-/*
- * random.c
- *             Acquire randomness from system.  For seeding RNG.
- *
- * Copyright (c) 2001 Marko Kreen
- * 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.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS 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 AUTHOR OR 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.
- *
- * contrib/pgcrypto/random.c
- */
-
-#include "postgres.h"
-
-#include "px.h"
-#include "utils/memdebug.h"
-
-/* how many bytes to ask from system random provider */
-#define RND_BYTES  32
-
-/*
- * Try to read from /dev/urandom or /dev/random on these OS'es.
- *
- * The list can be pretty liberal, as the device not existing
- * is expected event.
- */
-#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) \
-       || defined(__NetBSD__) || defined(__DragonFly__) \
-       || defined(__darwin__) || defined(__SOLARIS__) \
-       || defined(__hpux) || defined(__HPUX__) \
-       || defined(__CYGWIN__) || defined(_AIX)
-
-#define TRY_DEV_RANDOM
-
-#include <fcntl.h>
-#include <unistd.h>
-
-static int
-safe_read(int fd, void *buf, size_t count)
-{
-       int                     done = 0;
-       char       *p = buf;
-       int                     res;
-
-       while (count)
-       {
-               res = read(fd, p, count);
-               if (res <= 0)
-               {
-                       if (errno == EINTR)
-                               continue;
-                       return PXE_DEV_READ_ERROR;
-               }
-               p += res;
-               done += res;
-               count -= res;
-       }
-       return done;
-}
-
-static uint8 *
-try_dev_random(uint8 *dst)
-{
-       int                     fd;
-       int                     res;
-
-       fd = open("/dev/urandom", O_RDONLY, 0);
-       if (fd == -1)
-       {
-               fd = open("/dev/random", O_RDONLY, 0);
-               if (fd == -1)
-                       return dst;
-       }
-       res = safe_read(fd, dst, RND_BYTES);
-       close(fd);
-       if (res > 0)
-               dst += res;
-       return dst;
-}
-#endif
-
-/*
- * Try to find randomness on Windows
- */
-#ifdef WIN32
-
-#define TRY_WIN32_GENRAND
-#define TRY_WIN32_PERFC
-
-#include <windows.h>
-#include <wincrypt.h>
-
-/*
- * this function is from libtomcrypt
- *
- * try to use Microsoft crypto API
- */
-static uint8 *
-try_win32_genrand(uint8 *dst)
-{
-       int                     res;
-       HCRYPTPROV      h = 0;
-
-       res = CryptAcquireContext(&h, NULL, MS_DEF_PROV, PROV_RSA_FULL,
-                                                         (CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET));
-       if (!res)
-               res = CryptAcquireContext(&h, NULL, MS_DEF_PROV, PROV_RSA_FULL,
-                          CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET | CRYPT_NEWKEYSET);
-       if (!res)
-               return dst;
-
-       res = CryptGenRandom(h, RND_BYTES, dst);
-       if (res == TRUE)
-               dst += RND_BYTES;
-
-       CryptReleaseContext(h, 0);
-       return dst;
-}
-
-static uint8 *
-try_win32_perfc(uint8 *dst)
-{
-       int                     res;
-       LARGE_INTEGER time;
-
-       res = QueryPerformanceCounter(&time);
-       if (!res)
-               return dst;
-
-       memcpy(dst, &time, sizeof(time));
-       return dst + sizeof(time);
-}
-#endif   /* WIN32 */
-
-
-/*
- * If we are not on Windows, then hopefully we are
- * on a unix-like system.  Use the usual suspects
- * for randomness.
- */
-#ifndef WIN32
-
-#define TRY_UNIXSTD
-
-#include <sys/types.h>
-#include <sys/time.h>
-#include <time.h>
-#include <unistd.h>
-
-/*
- * Everything here is predictible, only needs some patience.
- *
- * But there is a chance that the system-specific functions
- * did not work.  So keep faith and try to slow the attacker down.
- */
-static uint8 *
-try_unix_std(uint8 *dst)
-{
-       pid_t           pid;
-       int                     x;
-       PX_MD      *md;
-       struct timeval tv;
-       int                     res;
-
-       /* process id */
-       pid = getpid();
-       memcpy(dst, (uint8 *) &pid, sizeof(pid));
-       dst += sizeof(pid);
-
-       /* time */
-       gettimeofday(&tv, NULL);
-       memcpy(dst, (uint8 *) &tv, sizeof(tv));
-       dst += sizeof(tv);
-
-       /* pointless, but should not hurt */
-       x = random();
-       memcpy(dst, (uint8 *) &x, sizeof(x));
-       dst += sizeof(x);
-
-       /* hash of uninitialized stack and heap allocations */
-       res = px_find_digest("sha1", &md);
-       if (res >= 0)
-       {
-               uint8      *ptr;
-               uint8           stack[8192];
-               int                     alloc = 32 * 1024;
-
-               VALGRIND_MAKE_MEM_DEFINED(stack, sizeof(stack));
-               px_md_update(md, stack, sizeof(stack));
-               ptr = px_alloc(alloc);
-               VALGRIND_MAKE_MEM_DEFINED(ptr, alloc);
-               px_md_update(md, ptr, alloc);
-               px_free(ptr);
-
-               px_md_finish(md, dst);
-               px_md_free(md);
-
-               dst += 20;
-       }
-
-       return dst;
-}
-#endif
-
-/*
- * try to extract some randomness for initial seeding
- *
- * dst should have room for 1024 bytes.
- */
-unsigned
-px_acquire_system_randomness(uint8 *dst)
-{
-       uint8      *p = dst;
-
-#ifdef TRY_DEV_RANDOM
-       p = try_dev_random(p);
-#endif
-#ifdef TRY_WIN32_GENRAND
-       p = try_win32_genrand(p);
-#endif
-#ifdef TRY_WIN32_PERFC
-       p = try_win32_perfc(p);
-#endif
-#ifdef TRY_UNIXSTD
-       p = try_unix_std(p);
-#endif
-       return p - dst;
-}
index 296611d4256b06171eb3aa4f34a08bb4383fe819..98594a487e6ed6c38fc6191ba1eab36f763ca904 100644 (file)
@@ -1096,6 +1096,23 @@ su - postgres
        </listitem>
       </varlistentry>
 
+      <varlistentry>
+       <term><option>--disable-strong-random</option></term>
+       <listitem>
+        <para>
+         Allow the build to succeed even if <productname>PostgreSQL</>
+         has no support for strong random numbers on the platform.
+         A source of random numbers is needed for some authentication
+         protocols, as well as some routines in <xref linkend="pgcrypto">
+         module. --disable-strong-random disables functionality that
+         requires cryptographically strong random numbers, and substitutes
+         a weak pseudo-random-number-generator for the generation of
+         authentication salt values and query cancel keys. It may make
+         authentication less secure.
+        </para>
+       </listitem>
+      </varlistentry>
+
       <varlistentry>
        <term><option>--disable-thread-safety</option></term>
        <listitem>
index aa1fa658ed0e2c6468426fdb6ff94003b12e509c..d39d6ca8670f6676aa898ac4d2ae3c6979808d6e 100644 (file)
@@ -197,6 +197,7 @@ enable_dtrace       = @enable_dtrace@
 enable_coverage        = @enable_coverage@
 enable_tap_tests       = @enable_tap_tests@
 enable_thread_safety   = @enable_thread_safety@
+enable_strong_random   = @enable_strong_random@
 
 python_includespec     = @python_includespec@
 python_libdir          = @python_libdir@
index 0ba85301149a03be96f127b455d179645b23d95a..5d166db5744515a6e08b1a7937e27af780eb61c6 100644 (file)
@@ -33,6 +33,7 @@
 #include "miscadmin.h"
 #include "replication/walsender.h"
 #include "storage/ipc.h"
+#include "utils/backend_random.h"
 
 
 /*----------------------------------------------------------------
@@ -43,9 +44,21 @@ static void sendAuthRequest(Port *port, AuthRequest areq, char *extradata,
                                int extralen);
 static void auth_failed(Port *port, int status, char *logdetail);
 static char *recv_password_packet(Port *port);
-static int     recv_and_check_password_packet(Port *port, char **logdetail);
 
 
+/*----------------------------------------------------------------
+ * MD5 authentication
+ *----------------------------------------------------------------
+ */
+static int CheckMD5Auth(Port *port, char **logdetail);
+
+/*----------------------------------------------------------------
+ * Plaintext password authentication
+ *----------------------------------------------------------------
+ */
+
+static int CheckPasswordAuth(Port *port, char **logdetail);
+
 /*----------------------------------------------------------------
  * Ident authentication
  *----------------------------------------------------------------
@@ -536,13 +549,11 @@ ClientAuthentication(Port *port)
                                                (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
                                                 errmsg("MD5 authentication is not supported when \"db_user_namespace\" is enabled")));
                        /* include the salt to use for computing the response */
-                       sendAuthRequest(port, AUTH_REQ_MD5, port->md5Salt, 4);
-                       status = recv_and_check_password_packet(port, &logdetail);
+                       status = CheckMD5Auth(port, &logdetail);
                        break;
 
                case uaPassword:
-                       sendAuthRequest(port, AUTH_REQ_PASSWORD, NULL, 0);
-                       status = recv_and_check_password_packet(port, &logdetail);
+                       status = CheckPasswordAuth(port, &logdetail);
                        break;
 
                case uaPAM:
@@ -696,23 +707,48 @@ recv_password_packet(Port *port)
  *----------------------------------------------------------------
  */
 
-/*
- * Called when we have sent an authorization request for a password.
- * Get the response and check it.
- * On error, optionally store a detail string at *logdetail.
+static int
+CheckMD5Auth(Port *port, char **logdetail)
+{
+       char            md5Salt[4];             /* Password salt */
+       char       *passwd;
+       int                     result;
+
+       pg_backend_random(md5Salt, 4);
+
+       sendAuthRequest(port, AUTH_REQ_MD5, md5Salt, 4);
+
+       passwd = recv_password_packet(port);
+
+       if (passwd == NULL)
+               return STATUS_EOF;              /* client wouldn't send password */
+
+       result = md5_crypt_verify(port, port->user_name, passwd, md5Salt, 4, logdetail);
+
+       pfree(passwd);
+
+       return result;
+}
+
+/*----------------------------------------------------------------
+ * Plaintext password authentication
+ *----------------------------------------------------------------
  */
+
 static int
-recv_and_check_password_packet(Port *port, char **logdetail)
+CheckPasswordAuth(Port *port, char **logdetail)
 {
        char       *passwd;
        int                     result;
 
+       sendAuthRequest(port, AUTH_REQ_PASSWORD, NULL, 0);
+
        passwd = recv_password_packet(port);
 
        if (passwd == NULL)
                return STATUS_EOF;              /* client wouldn't send password */
 
-       result = md5_crypt_verify(port, port->user_name, passwd, logdetail);
+       result = md5_crypt_verify(port, port->user_name, passwd, NULL, 0, logdetail);
 
        pfree(passwd);
 
@@ -920,7 +956,7 @@ pg_GSS_recvauth(Port *port)
                                 (unsigned int) port->gss->outbuf.length);
 
                        sendAuthRequest(port, AUTH_REQ_GSS_CONT,
-                                                       port->gss->outbuf.value, port->gss->outbuf.length);
+                                                 port->gss->outbuf.value, port->gss->outbuf.length);
 
                        gss_release_buffer(&lmin_s, &port->gss->outbuf);
                }
@@ -1166,7 +1202,7 @@ pg_SSPI_recvauth(Port *port)
                        port->gss->outbuf.value = outbuf.pBuffers[0].pvBuffer;
 
                        sendAuthRequest(port, AUTH_REQ_GSS_CONT,
-                                                       port->gss->outbuf.value, port->gss->outbuf.length);
+                                                 port->gss->outbuf.value, port->gss->outbuf.length);
 
                        FreeContextBuffer(outbuf.pBuffers[0].pvBuffer);
                }
index d84a18033048698ff288eb46b5f6f19ae64d955f..35b657adbbe2e14f965d36fc608e2c759da21234 100644 (file)
@@ -36,7 +36,7 @@
  */
 int
 md5_crypt_verify(const Port *port, const char *role, char *client_pass,
-                                char **logdetail)
+                                char *md5_salt, int md5_salt_len, char **logdetail)
 {
        int                     retval = STATUS_ERROR;
        char       *shadow_pass,
@@ -91,13 +91,14 @@ md5_crypt_verify(const Port *port, const char *role, char *client_pass,
        switch (port->hba->auth_method)
        {
                case uaMD5:
+                       Assert(md5_salt != NULL && md5_salt_len > 0);
                        crypt_pwd = palloc(MD5_PASSWD_LEN + 1);
                        if (isMD5(shadow_pass))
                        {
                                /* stored password already encrypted, only do salt */
                                if (!pg_md5_encrypt(shadow_pass + strlen("md5"),
-                                                                       port->md5Salt,
-                                                                       sizeof(port->md5Salt), crypt_pwd))
+                                                                       md5_salt, md5_salt_len,
+                                                                       crypt_pwd))
                                {
                                        pfree(crypt_pwd);
                                        return STATUS_ERROR;
@@ -118,8 +119,7 @@ md5_crypt_verify(const Port *port, const char *role, char *client_pass,
                                        return STATUS_ERROR;
                                }
                                if (!pg_md5_encrypt(crypt_pwd2 + strlen("md5"),
-                                                                       port->md5Salt,
-                                                                       sizeof(port->md5Salt),
+                                                                       md5_salt, md5_salt_len,
                                                                        crypt_pwd))
                                {
                                        pfree(crypt_pwd);
index 24add74512fa2086d0044c6fc12dfbdbe7d776a3..f0ed5233711b66c67d44352cee819f86a6af21c2 100644 (file)
 typedef struct bkend
 {
        pid_t           pid;                    /* process id of backend */
-       long            cancel_key;             /* cancel key for cancels for this backend */
+       int32           cancel_key;             /* cancel key for cancels for this backend */
        int                     child_slot;             /* PMChildSlot for this backend, if any */
 
        /*
@@ -358,13 +358,15 @@ static volatile bool avlauncher_needs_signal = false;
 static volatile bool StartWorkerNeeded = true;
 static volatile bool HaveCrashedWorker = false;
 
+#ifndef HAVE_STRONG_RANDOM
 /*
- * State for assigning random salts and cancel keys.
+ * State for assigning cancel keys.
  * Also, the global MyCancelKey passes the cancel key assigned to a given
  * backend from the postmaster to that backend (via fork).
  */
 static unsigned int random_seed = 0;
 static struct timeval random_start_time;
+#endif
 
 #ifdef USE_BONJOUR
 static DNSServiceRef bonjour_sdref = NULL;
@@ -403,8 +405,7 @@ static void processCancelRequest(Port *port, void *pkt);
 static int     initMasks(fd_set *rmask);
 static void report_fork_failure_to_client(Port *port, int errnum);
 static CAC_state canAcceptConnections(void);
-static long PostmasterRandom(void);
-static void RandomSalt(char *salt, int len);
+static bool RandomCancelKey(int32 *cancel_key);
 static void signal_child(pid_t pid, int signal);
 static bool SignalSomeChildren(int signal, int targets);
 static void TerminateChildren(int signal);
@@ -471,7 +472,7 @@ typedef struct
        InheritableSocket portsocket;
        char            DataDir[MAXPGPATH];
        pgsocket        ListenSocket[MAXLISTEN];
-       long            MyCancelKey;
+       int32           MyCancelKey;
        int                     MyPMChildSlot;
 #ifndef WIN32
        unsigned long UsedShmemSegID;
@@ -1292,8 +1293,10 @@ PostmasterMain(int argc, char *argv[])
         * Remember postmaster startup time
         */
        PgStartTime = GetCurrentTimestamp();
-       /* PostmasterRandom wants its own copy */
+#ifndef HAVE_STRONG_RANDOM
+       /* RandomCancelKey wants its own copy */
        gettimeofday(&random_start_time, NULL);
+#endif
 
        /*
         * We're ready to rock and roll...
@@ -2344,15 +2347,6 @@ ConnCreate(int serverFd)
                return NULL;
        }
 
-       /*
-        * Precompute password salt values to use for this connection. It's
-        * slightly annoying to do this long in advance of knowing whether we'll
-        * need 'em or not, but we must do the random() calls before we fork, not
-        * after.  Else the postmaster's random sequence won't get advanced, and
-        * all backends would end up using the same salt...
-        */
-       RandomSalt(port->md5Salt, sizeof(port->md5Salt));
-
        /*
         * Allocate GSSAPI specific state struct
         */
@@ -3905,7 +3899,14 @@ BackendStartup(Port *port)
         * backend will have its own copy in the forked-off process' value of
         * MyCancelKey, so that it can transmit the key to the frontend.
         */
-       MyCancelKey = PostmasterRandom();
+       if (!RandomCancelKey(&MyCancelKey))
+       {
+               ereport(LOG,
+                               (errcode(ERRCODE_OUT_OF_MEMORY),
+                                errmsg("could not acquire random number")));
+               return STATUS_ERROR;
+       }
+
        bn->cancel_key = MyCancelKey;
 
        /* Pass down canAcceptConnections state */
@@ -4218,8 +4219,10 @@ BackendRun(Port *port)
         * generator state.  We have to clobber the static random_seed *and* start
         * a new random sequence in the random() library function.
         */
+#ifndef HAVE_STRONG_RANDOM
        random_seed = 0;
        random_start_time.tv_usec = 0;
+#endif
        /* slightly hacky way to convert timestamptz into integers */
        TimestampDifference(0, port->SessionStartTime, &secs, &usecs);
        srandom((unsigned int) (MyProcPid ^ (usecs << 12) ^ secs));
@@ -5068,63 +5071,42 @@ StartupPacketTimeoutHandler(void)
 
 
 /*
- * RandomSalt
+ * Generate a random cancel key.
  */
-static void
-RandomSalt(char *salt, int len)
+static bool
+RandomCancelKey(int32 *cancel_key)
 {
-       long            rand;
-       int                     i;
-
+#ifdef HAVE_STRONG_RANDOM
+       return pg_strong_random((char *) cancel_key, sizeof(int32));
+#else
        /*
-        * We use % 255, sacrificing one possible byte value, so as to ensure that
-        * all bits of the random() value participate in the result. While at it,
-        * add one to avoid generating any null bytes.
+        * If built with --disable-strong-random, use plain old erand48.
+        *
+        * We cannot use pg_backend_random() in postmaster, because it stores
+        * its state in shared memory.
         */
-       for (i = 0; i < len; i++)
-       {
-               rand = PostmasterRandom();
-               salt[i] = (rand % 255) + 1;
-       }
-}
+       static unsigned short seed[3];
 
-/*
- * PostmasterRandom
- *
- * Caution: use this only for values needed during connection-request
- * processing.  Otherwise, the intended property of having an unpredictable
- * delay between random_start_time and random_stop_time will be broken.
- */
-static long
-PostmasterRandom(void)
-{
        /*
         * Select a random seed at the time of first receiving a request.
         */
        if (random_seed == 0)
        {
-               do
-               {
-                       struct timeval random_stop_time;
+               struct timeval random_stop_time;
 
-                       gettimeofday(&random_stop_time, NULL);
+               gettimeofday(&random_stop_time, NULL);
 
-                       /*
-                        * We are not sure how much precision is in tv_usec, so we swap
-                        * the high and low 16 bits of 'random_stop_time' and XOR them
-                        * with 'random_start_time'. On the off chance that the result is
-                        * 0, we loop until it isn't.
-                        */
-                       random_seed = random_start_time.tv_usec ^
-                               ((random_stop_time.tv_usec << 16) |
-                                ((random_stop_time.tv_usec >> 16) & 0xffff));
-               }
-               while (random_seed == 0);
+               seed[0] = (unsigned short) random_start_time.tv_usec;
+               seed[1] = (unsigned short) (random_stop_time.tv_usec) ^ (random_start_time.tv_usec >> 16);
+               seed[2] = (unsigned short) (random_stop_time.tv_usec >> 16);
 
-               srandom(random_seed);
+               random_seed = 1;
        }
 
-       return random();
+       *cancel_key = pg_jrand48(seed);
+
+       return true;
+#endif
 }
 
 /*
@@ -5295,16 +5277,23 @@ StartAutovacuumWorker(void)
         */
        if (canAcceptConnections() == CAC_OK)
        {
+               /*
+                * Compute the cancel key that will be assigned to this session.
+                * We probably don't need cancel keys for autovac workers, but
+                * we'd better have something random in the field to prevent
+                * unfriendly people from sending cancels to them.
+                */
+               if (!RandomCancelKey(&MyCancelKey))
+               {
+                       ereport(LOG,
+                                       (errcode(ERRCODE_INTERNAL_ERROR),
+                                        errmsg("could not acquire random number")));
+                       return;
+               }
+
                bn = (Backend *) malloc(sizeof(Backend));
                if (bn)
                {
-                       /*
-                        * Compute the cancel key that will be assigned to this session.
-                        * We probably don't need cancel keys for autovac workers, but
-                        * we'd better have something random in the field to prevent
-                        * unfriendly people from sending cancels to them.
-                        */
-                       MyCancelKey = PostmasterRandom();
                        bn->cancel_key = MyCancelKey;
 
                        /* Autovac workers are not dead_end and need a child slot */
@@ -5592,8 +5581,25 @@ bgworker_should_start_now(BgWorkerStartTime start_time)
 static bool
 assign_backendlist_entry(RegisteredBgWorker *rw)
 {
-       Backend    *bn = malloc(sizeof(Backend));
+       Backend    *bn;
 
+       /*
+        * Compute the cancel key that will be assigned to this session. We
+        * probably don't need cancel keys for background workers, but we'd better
+        * have something random in the field to prevent unfriendly people from
+        * sending cancels to them.
+        */
+       if (!RandomCancelKey(&MyCancelKey))
+       {
+               ereport(LOG,
+                               (errcode(ERRCODE_INTERNAL_ERROR),
+                                errmsg("could not acquire random number")));
+
+               rw->rw_crashed_at = GetCurrentTimestamp();
+               return false;
+       }
+
+       bn = malloc(sizeof(Backend));
        if (bn == NULL)
        {
                ereport(LOG,
@@ -5610,15 +5616,7 @@ assign_backendlist_entry(RegisteredBgWorker *rw)
                return false;
        }
 
-       /*
-        * Compute the cancel key that will be assigned to this session. We
-        * probably don't need cancel keys for background workers, but we'd better
-        * have something random in the field to prevent unfriendly people from
-        * sending cancels to them.
-        */
-       MyCancelKey = PostmasterRandom();
        bn->cancel_key = MyCancelKey;
-
        bn->child_slot = MyPMChildSlot = AssignPostmasterChildSlot();
        bn->bkend_type = BACKEND_TYPE_BGWORKER;
        bn->dead_end = false;
index c04b17fa8ead59f8190a3fa7d880197e9cff9235..01bddcea16c4471af840db90deb2d21dbeeb017d 100644 (file)
@@ -43,6 +43,7 @@
 #include "storage/procsignal.h"
 #include "storage/sinvaladt.h"
 #include "storage/spin.h"
+#include "utils/backend_random.h"
 #include "utils/snapmgr.h"
 
 
@@ -141,6 +142,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port)
                size = add_size(size, BTreeShmemSize());
                size = add_size(size, SyncScanShmemSize());
                size = add_size(size, AsyncShmemSize());
+               size = add_size(size, BackendRandomShmemSize());
 #ifdef EXEC_BACKEND
                size = add_size(size, ShmemBackendArraySize());
 #endif
@@ -253,6 +255,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port)
        BTreeShmemInit();
        SyncScanShmemInit();
        AsyncShmemInit();
+       BackendRandomShmemInit();
 
 #ifdef EXEC_BACKEND
 
index f8996cd21a552089d08978936cd7418f9232aee9..0dcf7effd417ec85c9b383b8f65c65e637cdacd6 100644 (file)
@@ -47,3 +47,4 @@ CommitTsLock                                          39
 ReplicationOriginLock                          40
 MultiXactTruncationLock                                41
 OldSnapshotTimeMapLock                         42
+BackendRandomLock                              43
index c564ae396dac5d06cb08db7e90bcb57eb948c691..6ab03cea17034334a7341268d469c64e0730ee14 100644 (file)
@@ -38,7 +38,7 @@ volatile uint32 CritSectionCount = 0;
 int                    MyProcPid;
 pg_time_t      MyStartTime;
 struct Port *MyProcPort;
-long           MyCancelKey;
+int32          MyCancelKey;
 int                    MyPMChildSlot;
 
 /*
index a5b487d0b64de0e169423e721d897b748fe3e2df..0ad1b8b59540f1e006abf30be27cd854e2da39d8 100644 (file)
@@ -14,8 +14,9 @@ include $(top_builddir)/src/Makefile.global
 
 override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
 
-OBJS = guc.o help_config.o pg_config.o pg_controldata.o pg_rusage.o \
-       ps_status.o rls.o sampling.o superuser.o timeout.o tzparser.o
+OBJS = backend_random.o guc.o help_config.o pg_config.o pg_controldata.o \
+       pg_rusage.o ps_status.o rls.o sampling.o superuser.o timeout.o \
+       tzparser.o
 
 # This location might depend on the installation directories. Therefore
 # we can't subsitute it into pg_config.h.
diff --git a/src/backend/utils/misc/backend_random.c b/src/backend/utils/misc/backend_random.c
new file mode 100644 (file)
index 0000000..1bc239d
--- /dev/null
@@ -0,0 +1,158 @@
+/*-------------------------------------------------------------------------
+ *
+ * backend_random.c
+ *       Backend random number generation routine.
+ *
+ * pg_backend_random() function fills a buffer with random bytes. Normally,
+ * it is just a thin wrapper around pg_strong_random(), but when compiled
+ * with --disable-strong-random, we provide a built-in implementation.
+ *
+ * This function is used for generating nonces in authentication, and for
+ * random salt generation in pgcrypto. The built-in implementation is not
+ * cryptographically strong, but if the user asked for it, we'll go ahead
+ * and use it anyway.
+ *
+ * The built-in implementation uses the standard erand48 algorithm, with
+ * a seed shared between all backends.
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *       src/backend/utils/misc/backend_random.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include <sys/time.h>
+
+#include "miscadmin.h"
+#include "storage/lwlock.h"
+#include "storage/shmem.h"
+#include "utils/backend_random.h"
+#include "utils/timestamp.h"
+
+#ifdef HAVE_STRONG_RANDOM
+
+Size
+BackendRandomShmemSize(void)
+{
+       return 0;
+}
+
+void
+BackendRandomShmemInit(void)
+{
+       /* do nothing */
+}
+
+bool
+pg_backend_random(char *dst, int len)
+{
+       /* should not be called in postmaster */
+       Assert (IsUnderPostmaster || !IsPostmasterEnvironment);
+
+       return pg_strong_random(dst, len);
+}
+
+#else
+
+/*
+ * Seed for the PRNG, stored in shared memory.
+ *
+ * Protected by BackendRandomLock.
+ */
+typedef struct
+{
+       bool            initialized;
+       unsigned short seed[3];
+} BackendRandomShmemStruct;
+
+static BackendRandomShmemStruct *BackendRandomShmem;
+
+Size
+BackendRandomShmemSize(void)
+{
+       return sizeof(BackendRandomShmemStruct);
+}
+
+void
+BackendRandomShmemInit(void)
+{
+       bool            found;
+
+       BackendRandomShmem = (BackendRandomShmemStruct *)
+               ShmemInitStruct("Backend PRNG state",
+                                               BackendRandomShmemSize(),
+                                               &found);
+
+       if (!IsUnderPostmaster)
+       {
+               Assert(!found);
+
+               BackendRandomShmem->initialized = false;
+       }
+       else
+               Assert(found);
+}
+
+bool
+pg_backend_random(char *dst, int len)
+{
+       int                     i;
+       char       *end = dst + len;
+
+       /* should not be called in postmaster */
+       Assert (IsUnderPostmaster || !IsPostmasterEnvironment);
+
+       LWLockAcquire(BackendRandomLock, LW_EXCLUSIVE);
+
+       /*
+        * Seed the PRNG on the first use.
+        */
+       if (!BackendRandomShmem->initialized)
+       {
+               struct timeval now;
+
+               gettimeofday(&now, NULL);
+
+               BackendRandomShmem->seed[0] = now.tv_sec;
+               BackendRandomShmem->seed[1] = (unsigned short) (now.tv_usec);
+               BackendRandomShmem->seed[2] = (unsigned short) (now.tv_usec >> 16);
+
+               /*
+                * Mix in the cancel key, generated by the postmaster. This adds
+                * what little entropy the postmaster had to the seed.
+                */
+               BackendRandomShmem->seed[0] ^= (MyCancelKey);
+               BackendRandomShmem->seed[1] ^= (MyCancelKey >> 16);
+
+               BackendRandomShmem->initialized = true;
+       }
+
+       for (i = 0; dst < end; i++)
+       {
+               uint32          r;
+               int                     j;
+
+               /*
+                * pg_jrand48 returns a 32-bit integer. Fill the next 4 bytes from it.
+                */
+               r  = (uint32) pg_jrand48(BackendRandomShmem->seed);
+
+               for (j = 0; j < 4 && dst < end; j++)
+               {
+                       *(dst++) = (char) (r & 0xFF);
+                       r >>= 8;
+               }
+       }
+       LWLockRelease(BackendRandomLock);
+
+       return true;
+}
+
+
+#endif /* HAVE_STRONG_RANDOM */
index 5725bb409e82dc350a2aa317d074ea0a655b8705..f51e0fd46b68609b891ddf668dfc0cd927dcd2d6 100644 (file)
@@ -16,6 +16,6 @@
 #include "libpq/libpq-be.h"
 
 extern int md5_crypt_verify(const Port *port, const char *role,
-                                char *client_pass, char **logdetail);
+                                char *client_pass, char *md5_salt, int md5_salt_len, char **logdetail);
 
 #endif
index b91eca5b2c1400616682a307e4d82d03bb713d58..66647ad003260c01d7ab7af61dcf6b56f88361cb 100644 (file)
@@ -144,7 +144,6 @@ typedef struct Port
         * Information that needs to be held during the authentication cycle.
         */
        HbaLine    *hba;
-       char            md5Salt[4];             /* Password salt */
 
        /*
         * Information that really has no business at all being in struct Port,
index d06eca54b46fea36df5b5252b063359d955cf36a..999440fdec10bbb49651cfaa0ea8834c4349df0b 100644 (file)
@@ -163,7 +163,7 @@ extern PGDLLIMPORT int MyProcPid;
 extern PGDLLIMPORT pg_time_t MyStartTime;
 extern PGDLLIMPORT struct Port *MyProcPort;
 extern PGDLLIMPORT struct Latch *MyLatch;
-extern long MyCancelKey;
+extern int32 MyCancelKey;
 extern int     MyPMChildSlot;
 
 extern char OutputFileName[];
index 7dbfa90bf4971f2739448d370a8fdce3f8d766d1..42a3fc862e9665e1e4f5af66353ee73b3434a81a 100644 (file)
 /* Define to 1 if you have the `strlcpy' function. */
 #undef HAVE_STRLCPY
 
+/* Define to use have a strong random number source */
+#undef HAVE_STRONG_RANDOM
+
 /* Define to 1 if you have the `strtoll' function. */
 #undef HAVE_STRTOLL
 
 /* Define to 1 to build with BSD Authentication support. (--with-bsd-auth) */
 #undef USE_BSD_AUTH
 
+/* Define to use /dev/urandom for random number generation */
+#undef USE_DEV_URANDOM
+
 /* Define to 1 if you want float4 values to be passed by value.
    (--enable-float4-byval) */
 #undef USE_FLOAT4_BYVAL
 /* Define to build with OpenSSL support. (--with-openssl) */
 #undef USE_OPENSSL
 
+/* Define to use OpenSSL for random number generation */
+#undef USE_OPENSSL_RANDOM
+
 /* Define to 1 to build with PAM support. (--with-pam) */
 #undef USE_PAM
 
 /* Define to select unnamed POSIX semaphores. */
 #undef USE_UNNAMED_POSIX_SEMAPHORES
 
+/* Define to use native Windows API for random number generation */
+#undef USE_WIN32_RANDOM
+
 /* Define to select Win32-style semaphores. */
 #undef USE_WIN32_SEMAPHORES
 
index 8892c3cb4fa453b614cdc5a35db202952d5072e9..ceb8b7956eee08d40aa105a6cb4ddaa2b84369dc 100644 (file)
 /* Define to 1 if you have the <string.h> header file. */
 #define HAVE_STRING_H 1
 
+/* Define to use have a strong random number source */
+#define HAVE_STRONG_RANDOM 1
+
 /* Define to 1 if you have the `strtoll' function. */
 //#define HAVE_STRTOLL 1
 
 /* Define to 1 to build with BSD Authentication support. (--with-bsd-auth) */
 /* #undef USE_BSD_AUTH */
 
+/* Define to use /dev/urandom for random number generation */
+/* #undef USE_DEV_URANDOM */
+
 /* Define to 1 if you want 64-bit integer timestamp and interval support.
    (--enable-integer-datetimes) */
 /* #undef USE_INTEGER_DATETIMES */
 /* Define to build with OpenSSL support. (--with-openssl) */
 /* #undef USE_OPENSSL */
 
+/* Define to use OpenSSL for random number generation */
+/* #undef USE_OPENSSL_RANDOM */
+
 /* Define to 1 to build with PAM support. (--with-pam) */
 /* #undef USE_PAM */
 
 /* Define to select unnamed POSIX semaphores. */
 /* #undef USE_UNNAMED_POSIX_SEMAPHORES */
 
+/* Define to use native Windows API for random number generation */
+#define USE_WIN32_RANDOM 1
+
 /* Define to select Win32-style semaphores. */
 #define USE_WIN32_SEMAPHORES 1
 
index 8a63958535b39963cfc410ac1cfede1c5b215468..f2b9882b7b76ccdd94a5fc5f08da72014ea4c9d4 100644 (file)
@@ -361,6 +361,7 @@ extern off_t ftello(FILE *stream);
 
 extern double pg_erand48(unsigned short xseed[3]);
 extern long pg_lrand48(void);
+extern long pg_jrand48(unsigned short xseed[3]);
 extern void pg_srand48(long seed);
 
 #ifndef HAVE_FLS
@@ -454,6 +455,11 @@ extern int pg_codepage_to_encoding(UINT cp);
 extern char *inet_net_ntop(int af, const void *src, int bits,
                          char *dst, size_t size);
 
+/* port/pg_strong_random.c */
+#ifdef HAVE_STRONG_RANDOM
+extern bool pg_strong_random(void *buf, size_t len);
+#endif
+
 /* port/pgcheckdir.c */
 extern int     pg_check_dir(const char *dir);
 
diff --git a/src/include/utils/backend_random.h b/src/include/utils/backend_random.h
new file mode 100644 (file)
index 0000000..16a6a26
--- /dev/null
@@ -0,0 +1,19 @@
+/*-------------------------------------------------------------------------
+ *
+ * backend_random.h
+ *             Declarations for backend random number generation
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ *
+ *       src/include/utils/backend_random.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef BACKEND_RANDOM_H
+#define BACKEND_RANDOM_H
+
+extern Size BackendRandomShmemSize(void);
+extern void BackendRandomShmemInit(void);
+extern bool pg_backend_random(char *dst, int len);
+
+#endif   /* BACKEND_RANDOM_H */
index bc9b63add0479459d146550c0338e1a22a478400..81f01b25bbc6949df930de17a198107282ccd375 100644 (file)
@@ -35,6 +35,10 @@ OBJS = $(LIBOBJS) $(PG_CRC32C_OBJS) chklocale.o erand48.o inet_net_ntop.o \
        pgstrcasecmp.o pqsignal.o \
        qsort.o qsort_arg.o quotes.o sprompt.o tar.o thread.o
 
+ifeq ($(enable_strong_random), yes)
+OBJS += pg_strong_random.o
+endif
+
 # foo_srv.o and foo.o are both built from foo.c, but only foo.o has -DFRONTEND
 OBJS_SRV = $(OBJS:%.o=%_srv.o)
 
index 9d471197c354056c8903a5a9e1c6b0023419f1d1..716816bc361ba3ee5e89822e2c8c59a6b4fdf959 100644 (file)
@@ -91,6 +91,13 @@ pg_lrand48(void)
        return ((long) _rand48_seed[2] << 15) + ((long) _rand48_seed[1] >> 1);
 }
 
+long
+pg_jrand48(unsigned short xseed[3])
+{
+       _dorand48(xseed);
+       return ((long) xseed[2] << 16) + ((long) xseed[1]);
+}
+
 void
 pg_srand48(long seed)
 {
diff --git a/src/port/pg_strong_random.c b/src/port/pg_strong_random.c
new file mode 100644 (file)
index 0000000..6d3aa38
--- /dev/null
@@ -0,0 +1,149 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_strong_random.c
+ *       generate a cryptographically secure random number
+ *
+ * Our definition of "strong" is that it's suitable for generating random
+ * salts and query cancellation keys, during authentication.
+ *
+ * Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *       src/port/pg_strong_random.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef FRONTEND
+#include "postgres.h"
+#else
+#include "postgres_fe.h"
+#endif
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+#ifdef USE_OPENSSL
+#include <openssl/rand.h>
+#endif
+#ifdef WIN32
+#include <Wincrypt.h>
+#endif
+
+#ifdef WIN32
+/*
+ * Cache a global crypto provider that only gets freed when the process
+ * exits, in case we need random numbers more than once.
+ */
+static HCRYPTPROV hProvider = 0;
+#endif
+
+#if defined(USE_DEV_URANDOM)
+/*
+ * Read (random) bytes from a file.
+ */
+static bool
+random_from_file(char *filename, void *buf, size_t len)
+{
+       int                     f;
+       char       *p = buf;
+       ssize_t         res;
+
+       f = open(filename, O_RDONLY, 0);
+       if (f == -1)
+               return false;
+
+       while (len)
+       {
+               res = read(f, p, len);
+               if (res <= 0)
+               {
+                       if (errno == EINTR)
+                               continue;               /* interrupted by signal, just retry */
+
+                       close(f);
+                       return false;
+               }
+
+               p += res;
+               len -= res;
+       }
+
+       close(f);
+       return true;
+}
+#endif
+
+/*
+ * pg_strong_random
+ *
+ * Generate requested number of random bytes. The returned bytes are
+ * cryptographically secure, suitable for use e.g. in authentication.
+ *
+ * We rely on system facilities for actually generating the numbers.
+ * We support a number of sources:
+ *
+ * 1. OpenSSL's RAND_bytes()
+ * 2. Windows' CryptGenRandom() function
+ * 3. /dev/urandom
+ *
+ * The configure script will choose which one to use, and set
+ * a USE_*_RANDOM flag accordingly.
+ *
+ * Returns true on success, and false if none of the sources
+ * were available. NB: It is important to check the return value!
+ * Proceeding with key generation when no random data was available
+ * would lead to predictable keys and security issues.
+ */
+bool
+pg_strong_random(void *buf, size_t len)
+{
+       /*
+        * When built with OpenSSL, use OpenSSL's RAND_bytes function.
+        */
+#if defined(USE_OPENSSL_RANDOM)
+       if (RAND_bytes(buf, len) == 1)
+               return true;
+       return false;
+
+       /*
+        * Windows has CryptoAPI for strong cryptographic numbers.
+        */
+#elif defined(USE_WIN32_RANDOM)
+       if (hProvider == 0)
+       {
+               if (!CryptAcquireContext(&hProvider,
+                                                                NULL,
+                                                                MS_DEF_PROV,
+                                                                PROV_RSA_FULL,
+                                                                CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
+               {
+                       /*
+                        * On failure, set back to 0 in case the value was for some reason
+                        * modified.
+                        */
+                       hProvider = 0;
+               }
+       }
+       /* Re-check in case we just retrieved the provider */
+       if (hProvider != 0)
+       {
+               if (CryptGenRandom(hProvider, len, buf))
+                       return true;
+       }
+       return false;
+
+       /*
+        * Read /dev/urandom ourselves.
+        */
+#elif defined(USE_DEV_URANDOM)
+       if (random_from_file("/dev/urandom", buf, len))
+               return true;
+       return false;
+
+#else
+       /* The autoconf script should not have allowed this */
+#error no source of random numbers configured
+#endif
+}
index de764dd4d44b28a37e18357f1ce99544a43cceff..db566f9c88ca69157a48c53143dadf246e994770 100644 (file)
@@ -91,8 +91,8 @@ sub mkvcbuild
          chklocale.c crypt.c fls.c fseeko.c getrusage.c inet_aton.c random.c
          srandom.c getaddrinfo.c gettimeofday.c inet_net_ntop.c kill.c open.c
          erand48.c snprintf.c strlcat.c strlcpy.c dirmod.c noblock.c path.c
-         pgcheckdir.c pgmkdirp.c pgsleep.c pgstrcasecmp.c pqsignal.c
-         mkdtemp.c qsort.c qsort_arg.c quotes.c system.c
+         pg_strong_random.c pgcheckdir.c pgmkdirp.c pgsleep.c pgstrcasecmp.c
+         pqsignal.c mkdtemp.c qsort.c qsort_arg.c quotes.c system.c
          sprompt.c tar.c thread.c getopt.c getopt_long.c dirent.c
          win32env.c win32error.c win32security.c win32setlocale.c);
 
@@ -425,7 +425,6 @@ sub mkvcbuild
                        'sha1.c',             'sha2.c',
                        'internal.c',         'internal-sha2.c',
                        'blf.c',              'rijndael.c',
-                       'fortuna.c',          'random.c',
                        'pgp-mpi-internal.c', 'imath.c');
        }
        $pgcrypto->AddReference($postgres);