diff --git a/configure b/configure index f4f2f8b7ce..551fc720b7 100755 --- 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 diff --git a/configure.in b/configure.in index 9f7611caeb..595e047d0e 100644 --- a/configure.in +++ b/configure.in @@ -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 diff --git a/contrib/pgcrypto/Makefile b/contrib/pgcrypto/Makefile index 805db7626b..f65d84d1f3 100644 --- a/contrib/pgcrypto/Makefile +++ b/contrib/pgcrypto/Makefile @@ -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 index 0000000000..25d5c35bf7 --- /dev/null +++ b/contrib/pgcrypto/expected/pgp-compression_1.out @@ -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 index 0000000000..d9e1e386a2 --- /dev/null +++ b/contrib/pgcrypto/expected/pgp-decrypt_1.out @@ -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 index 0000000000..2291e662ec --- /dev/null +++ b/contrib/pgcrypto/expected/pgp-encrypt_1.out @@ -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 index 0000000000..3b1822ed91 --- /dev/null +++ b/contrib/pgcrypto/expected/pgp-pubkey-encrypt_1.out @@ -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 index 5028203479..0000000000 --- a/contrib/pgcrypto/fortuna.c +++ /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 -#include - -#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 index bf9f4768d1..0000000000 --- a/contrib/pgcrypto/fortuna.h +++ /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 diff --git a/contrib/pgcrypto/internal.c b/contrib/pgcrypto/internal.c index 02ff976c25..2516092ba4 100644 --- a/contrib/pgcrypto/internal.c +++ b/contrib/pgcrypto/internal.c @@ -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; -} diff --git a/contrib/pgcrypto/openssl.c b/contrib/pgcrypto/openssl.c index f3e3a92486..1d3e58d925 100644 --- a/contrib/pgcrypto/openssl.c +++ b/contrib/pgcrypto/openssl.c @@ -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; -} diff --git a/contrib/pgcrypto/pgcrypto.c b/contrib/pgcrypto/pgcrypto.c index 27b96c7cc4..d815de3073 100644 --- a/contrib/pgcrypto/pgcrypto.c +++ b/contrib/pgcrypto/pgcrypto.c @@ -34,6 +34,7 @@ #include #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 diff --git a/contrib/pgcrypto/pgp-encrypt.c b/contrib/pgcrypto/pgp-encrypt.c index c9148fd2fc..be933bf86c 100644 --- a/contrib/pgcrypto/pgp-encrypt.c +++ b/contrib/pgcrypto/pgp-encrypt.c @@ -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 { diff --git a/contrib/pgcrypto/pgp-mpi-internal.c b/contrib/pgcrypto/pgp-mpi-internal.c index be95f2d092..cb70fcba6c 100644 --- a/contrib/pgcrypto/pgp-mpi-internal.c +++ b/contrib/pgcrypto/pgp-mpi-internal.c @@ -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 diff --git a/contrib/pgcrypto/pgp-pgsql.c b/contrib/pgcrypto/pgp-pgsql.c index 1f65b667ca..ce16db71d8 100644 --- a/contrib/pgcrypto/pgp-pgsql.c +++ b/contrib/pgcrypto/pgp-pgsql.c @@ -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); diff --git a/contrib/pgcrypto/pgp-pubenc.c b/contrib/pgcrypto/pgp-pubenc.c index 3b43bb61c0..4439876664 100644 --- a/contrib/pgcrypto/pgp-pubenc.c +++ b/contrib/pgcrypto/pgp-pubenc.c @@ -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 diff --git a/contrib/pgcrypto/pgp-s2k.c b/contrib/pgcrypto/pgp-s2k.c index 3551d44d62..a0fd8969ef 100644 --- a/contrib/pgcrypto/pgp-s2k.c +++ b/contrib/pgcrypto/pgp-s2k.c @@ -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: diff --git a/contrib/pgcrypto/px-crypt.c b/contrib/pgcrypto/px-crypt.c index 3d42393850..6c72c4ae83 100644 --- a/contrib/pgcrypto/px-crypt.c +++ b/contrib/pgcrypto/px-crypt.c @@ -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)); diff --git a/contrib/pgcrypto/px.c b/contrib/pgcrypto/px.c index b01701ea75..a5c02f3612 100644 --- a/contrib/pgcrypto/px.c +++ b/contrib/pgcrypto/px.c @@ -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) { diff --git a/contrib/pgcrypto/px.h b/contrib/pgcrypto/px.h index 800c552bf6..00fc6f0c01 100644 --- a/contrib/pgcrypto/px.h +++ b/contrib/pgcrypto/px.h @@ -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 index d72679e412..0000000000 --- a/contrib/pgcrypto/random.c +++ /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 -#include - -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 -#include - -/* - * 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 -#include -#include -#include - -/* - * 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; -} diff --git a/doc/src/sgml/installation.sgml b/doc/src/sgml/installation.sgml index 296611d425..98594a487e 100644 --- a/doc/src/sgml/installation.sgml +++ b/doc/src/sgml/installation.sgml @@ -1096,6 +1096,23 @@ su - postgres + + + + + Allow the build to succeed even if 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 + 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. + + + + diff --git a/src/Makefile.global.in b/src/Makefile.global.in index aa1fa658ed..d39d6ca867 100644 --- a/src/Makefile.global.in +++ b/src/Makefile.global.in @@ -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@ diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c index 0ba8530114..5d166db574 100644 --- a/src/backend/libpq/auth.c +++ b/src/backend/libpq/auth.c @@ -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 -recv_and_check_password_packet(Port *port, char **logdetail) +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, logdetail); + result = md5_crypt_verify(port, port->user_name, passwd, md5Salt, 4, logdetail); + + pfree(passwd); + + return result; +} + +/*---------------------------------------------------------------- + * Plaintext password authentication + *---------------------------------------------------------------- + */ + +static int +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, 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); } diff --git a/src/backend/libpq/crypt.c b/src/backend/libpq/crypt.c index d84a180330..35b657adbb 100644 --- a/src/backend/libpq/crypt.c +++ b/src/backend/libpq/crypt.c @@ -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); diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index 24add74512..f0ed523371 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -164,7 +164,7 @@ 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; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index c04b17fa8e..01bddcea16 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -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 diff --git a/src/backend/storage/lmgr/lwlocknames.txt b/src/backend/storage/lmgr/lwlocknames.txt index f8996cd21a..0dcf7effd4 100644 --- a/src/backend/storage/lmgr/lwlocknames.txt +++ b/src/backend/storage/lmgr/lwlocknames.txt @@ -47,3 +47,4 @@ CommitTsLock 39 ReplicationOriginLock 40 MultiXactTruncationLock 41 OldSnapshotTimeMapLock 42 +BackendRandomLock 43 diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c index c564ae396d..6ab03cea17 100644 --- a/src/backend/utils/init/globals.c +++ b/src/backend/utils/init/globals.c @@ -38,7 +38,7 @@ volatile uint32 CritSectionCount = 0; int MyProcPid; pg_time_t MyStartTime; struct Port *MyProcPort; -long MyCancelKey; +int32 MyCancelKey; int MyPMChildSlot; /* diff --git a/src/backend/utils/misc/Makefile b/src/backend/utils/misc/Makefile index a5b487d0b6..0ad1b8b595 100644 --- a/src/backend/utils/misc/Makefile +++ b/src/backend/utils/misc/Makefile @@ -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 index 0000000000..1bc239d1dd --- /dev/null +++ b/src/backend/utils/misc/backend_random.c @@ -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 + +#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 */ diff --git a/src/include/libpq/crypt.h b/src/include/libpq/crypt.h index 5725bb409e..f51e0fd46b 100644 --- a/src/include/libpq/crypt.h +++ b/src/include/libpq/crypt.h @@ -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 diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h index b91eca5b2c..66647ad003 100644 --- a/src/include/libpq/libpq-be.h +++ b/src/include/libpq/libpq-be.h @@ -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, diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index d06eca54b4..999440fdec 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -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[]; diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in index 7dbfa90bf4..42a3fc862e 100644 --- a/src/include/pg_config.h.in +++ b/src/include/pg_config.h.in @@ -497,6 +497,9 @@ /* 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 @@ -814,6 +817,9 @@ /* 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 @@ -842,6 +848,9 @@ /* 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 @@ -869,6 +878,9 @@ /* 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 diff --git a/src/include/pg_config.h.win32 b/src/include/pg_config.h.win32 index 8892c3cb4f..ceb8b7956e 100644 --- a/src/include/pg_config.h.win32 +++ b/src/include/pg_config.h.win32 @@ -348,6 +348,9 @@ /* Define to 1 if you have the 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 @@ -616,6 +619,9 @@ /* 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 */ @@ -629,6 +635,9 @@ /* 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 */ @@ -657,6 +666,9 @@ /* 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 diff --git a/src/include/port.h b/src/include/port.h index 8a63958535..f2b9882b7b 100644 --- a/src/include/port.h +++ b/src/include/port.h @@ -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 index 0000000000..16a6a26523 --- /dev/null +++ b/src/include/utils/backend_random.h @@ -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 */ diff --git a/src/port/Makefile b/src/port/Makefile index bc9b63add0..81f01b25bb 100644 --- a/src/port/Makefile +++ b/src/port/Makefile @@ -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) diff --git a/src/port/erand48.c b/src/port/erand48.c index 9d471197c3..716816bc36 100644 --- a/src/port/erand48.c +++ b/src/port/erand48.c @@ -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 index 0000000000..6d3aa38efd --- /dev/null +++ b/src/port/pg_strong_random.c @@ -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 +#include +#include + +#ifdef USE_OPENSSL +#include +#endif +#ifdef WIN32 +#include +#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 +} diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm index de764dd4d4..db566f9c88 100644 --- a/src/tools/msvc/Mkvcbuild.pm +++ b/src/tools/msvc/Mkvcbuild.pm @@ -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);