wp/wp-includes/random_compat/random.php
changeset 9 177826044cd9
parent 7 cf61fcea0001
child 19 3d72ae0968f4
equal deleted inserted replaced
8:c7c34916027a 9:177826044cd9
     1 <?php
     1 <?php
     2 /**
     2 /**
     3  * Random_* Compatibility Library
     3  * Random_* Compatibility Library
     4  * for using the new PHP 7 random_* API in PHP 5 projects
     4  * for using the new PHP 7 random_* API in PHP 5 projects
     5  *
     5  *
     6  * @version 1.2.1
     6  * @version 2.0.10
     7  * @released 2016-02-29
     7  * @released 2017-03-13
     8  *
     8  *
     9  * The MIT License (MIT)
     9  * The MIT License (MIT)
    10  *
    10  *
    11  * Copyright (c) 2015 Paragon Initiative Enterprises
    11  * Copyright (c) 2015 - 2017 Paragon Initiative Enterprises
    12  *
    12  *
    13  * Permission is hereby granted, free of charge, to any person obtaining a copy
    13  * Permission is hereby granted, free of charge, to any person obtaining a copy
    14  * of this software and associated documentation files (the "Software"), to deal
    14  * of this software and associated documentation files (the "Software"), to deal
    15  * in the Software without restriction, including without limitation the rights
    15  * in the Software without restriction, including without limitation the rights
    16  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    16  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    29  * SOFTWARE.
    29  * SOFTWARE.
    30  */
    30  */
    31 
    31 
    32 if (!defined('PHP_VERSION_ID')) {
    32 if (!defined('PHP_VERSION_ID')) {
    33     // This constant was introduced in PHP 5.2.7
    33     // This constant was introduced in PHP 5.2.7
    34     $RandomCompatversion = explode('.', PHP_VERSION);
    34     $RandomCompatversion = array_map('intval', explode('.', PHP_VERSION));
    35     define(
    35     define(
    36         'PHP_VERSION_ID',
    36         'PHP_VERSION_ID',
    37         $RandomCompatversion[0] * 10000
    37         $RandomCompatversion[0] * 10000
    38         + $RandomCompatversion[1] * 100
    38         + $RandomCompatversion[1] * 100
    39         + $RandomCompatversion[2]
    39         + $RandomCompatversion[2]
    40     );
    40     );
    41     $RandomCompatversion = null;
    41     $RandomCompatversion = null;
    42 }
    42 }
    43 
    43 
    44 if (PHP_VERSION_ID < 70000) {
    44 /**
    45 
    45  * PHP 7.0.0 and newer have these functions natively.
    46     if (!defined('RANDOM_COMPAT_READ_BUFFER')) {
    46  */
    47         define('RANDOM_COMPAT_READ_BUFFER', 8);
    47 if (PHP_VERSION_ID >= 70000) {
    48     }
    48     return;
    49 
    49 }
    50     $RandomCompatDIR = dirname(__FILE__);
    50 
    51 
    51 if (!defined('RANDOM_COMPAT_READ_BUFFER')) {
    52     require_once $RandomCompatDIR.'/byte_safe_strings.php';
    52     define('RANDOM_COMPAT_READ_BUFFER', 8);
    53     require_once $RandomCompatDIR.'/cast_to_int.php';
    53 }
    54     require_once $RandomCompatDIR.'/error_polyfill.php';
    54 
    55 
    55 $RandomCompatDIR = dirname(__FILE__);
    56     if (!function_exists('random_bytes')) {
    56 
       
    57 require_once $RandomCompatDIR . '/byte_safe_strings.php';
       
    58 require_once $RandomCompatDIR . '/cast_to_int.php';
       
    59 require_once $RandomCompatDIR . '/error_polyfill.php';
       
    60 
       
    61 if (!is_callable('random_bytes')) {
       
    62     /**
       
    63      * PHP 5.2.0 - 5.6.x way to implement random_bytes()
       
    64      *
       
    65      * We use conditional statements here to define the function in accordance
       
    66      * to the operating environment. It's a micro-optimization.
       
    67      *
       
    68      * In order of preference:
       
    69      *   1. Use libsodium if available.
       
    70      *   2. fread() /dev/urandom if available (never on Windows)
       
    71      *   3. mcrypt_create_iv($bytes, MCRYPT_DEV_URANDOM)
       
    72      *   4. COM('CAPICOM.Utilities.1')->GetRandom()
       
    73      *
       
    74      * See RATIONALE.md for our reasoning behind this particular order
       
    75      */
       
    76     if (extension_loaded('libsodium')) {
       
    77         // See random_bytes_libsodium.php
       
    78         if (PHP_VERSION_ID >= 50300 && is_callable('\\Sodium\\randombytes_buf')) {
       
    79             require_once $RandomCompatDIR . '/random_bytes_libsodium.php';
       
    80         } elseif (method_exists('Sodium', 'randombytes_buf')) {
       
    81             require_once $RandomCompatDIR . '/random_bytes_libsodium_legacy.php';
       
    82         }
       
    83     }
       
    84 
       
    85     /**
       
    86      * Reading directly from /dev/urandom:
       
    87      */
       
    88     if (DIRECTORY_SEPARATOR === '/') {
       
    89         // DIRECTORY_SEPARATOR === '/' on Unix-like OSes -- this is a fast
       
    90         // way to exclude Windows.
       
    91         $RandomCompatUrandom = true;
       
    92         $RandomCompat_basedir = ini_get('open_basedir');
       
    93 
       
    94         if (!empty($RandomCompat_basedir)) {
       
    95             $RandomCompat_open_basedir = explode(
       
    96                 PATH_SEPARATOR,
       
    97                 strtolower($RandomCompat_basedir)
       
    98             );
       
    99             $RandomCompatUrandom = (array() !== array_intersect(
       
   100                 array('/dev', '/dev/', '/dev/urandom'),
       
   101                 $RandomCompat_open_basedir
       
   102             ));
       
   103             $RandomCompat_open_basedir = null;
       
   104         }
       
   105 
       
   106         if (
       
   107             !is_callable('random_bytes')
       
   108             &&
       
   109             $RandomCompatUrandom
       
   110             &&
       
   111             @is_readable('/dev/urandom')
       
   112         ) {
       
   113             // Error suppression on is_readable() in case of an open_basedir
       
   114             // or safe_mode failure. All we care about is whether or not we
       
   115             // can read it at this point. If the PHP environment is going to
       
   116             // panic over trying to see if the file can be read in the first
       
   117             // place, that is not helpful to us here.
       
   118 
       
   119             // See random_bytes_dev_urandom.php
       
   120             require_once $RandomCompatDIR . '/random_bytes_dev_urandom.php';
       
   121         }
       
   122         // Unset variables after use
       
   123         $RandomCompat_basedir = null;
       
   124     } else {
       
   125         $RandomCompatUrandom = false;
       
   126     }
       
   127 
       
   128     /**
       
   129      * mcrypt_create_iv()
       
   130      *
       
   131      * We only want to use mcypt_create_iv() if:
       
   132      *
       
   133      * - random_bytes() hasn't already been defined
       
   134      * - the mcrypt extensions is loaded
       
   135      * - One of these two conditions is true:
       
   136      *   - We're on Windows (DIRECTORY_SEPARATOR !== '/')
       
   137      *   - We're not on Windows and /dev/urandom is readabale
       
   138      *     (i.e. we're not in a chroot jail)
       
   139      * - Special case:
       
   140      *   - If we're not on Windows, but the PHP version is between
       
   141      *     5.6.10 and 5.6.12, we don't want to use mcrypt. It will
       
   142      *     hang indefinitely. This is bad.
       
   143      *   - If we're on Windows, we want to use PHP >= 5.3.7 or else
       
   144      *     we get insufficient entropy errors.
       
   145      */
       
   146     if (
       
   147         !is_callable('random_bytes')
       
   148         &&
       
   149         // Windows on PHP < 5.3.7 is broken, but non-Windows is not known to be.
       
   150         (DIRECTORY_SEPARATOR === '/' || PHP_VERSION_ID >= 50307)
       
   151         &&
       
   152         // Prevent this code from hanging indefinitely on non-Windows;
       
   153         // see https://bugs.php.net/bug.php?id=69833
       
   154         (
       
   155             DIRECTORY_SEPARATOR !== '/' ||
       
   156             (PHP_VERSION_ID <= 50609 || PHP_VERSION_ID >= 50613)
       
   157         )
       
   158         &&
       
   159         extension_loaded('mcrypt')
       
   160     ) {
       
   161         // See random_bytes_mcrypt.php
       
   162         require_once $RandomCompatDIR . '/random_bytes_mcrypt.php';
       
   163     }
       
   164     $RandomCompatUrandom = null;
       
   165 
       
   166     /**
       
   167      * This is a Windows-specific fallback, for when the mcrypt extension
       
   168      * isn't loaded.
       
   169      */
       
   170     if (
       
   171         !is_callable('random_bytes')
       
   172         &&
       
   173         extension_loaded('com_dotnet')
       
   174         &&
       
   175         class_exists('COM')
       
   176     ) {
       
   177         $RandomCompat_disabled_classes = preg_split(
       
   178             '#\s*,\s*#',
       
   179             strtolower(ini_get('disable_classes'))
       
   180         );
       
   181 
       
   182         if (!in_array('com', $RandomCompat_disabled_classes)) {
       
   183             try {
       
   184                 $RandomCompatCOMtest = new COM('CAPICOM.Utilities.1');
       
   185                 if (method_exists($RandomCompatCOMtest, 'GetRandom')) {
       
   186                     // See random_bytes_com_dotnet.php
       
   187                     require_once $RandomCompatDIR . '/random_bytes_com_dotnet.php';
       
   188                 }
       
   189             } catch (com_exception $e) {
       
   190                 // Don't try to use it.
       
   191             }
       
   192         }
       
   193         $RandomCompat_disabled_classes = null;
       
   194         $RandomCompatCOMtest = null;
       
   195     }
       
   196 
       
   197     /**
       
   198      * throw new Exception
       
   199      */
       
   200     if (!is_callable('random_bytes')) {
    57         /**
   201         /**
    58          * PHP 5.2.0 - 5.6.x way to implement random_bytes()
   202          * We don't have any more options, so let's throw an exception right now
       
   203          * and hope the developer won't let it fail silently.
    59          *
   204          *
    60          * We use conditional statements here to define the function in accordance
   205          * @param mixed $length
    61          * to the operating environment. It's a micro-optimization.
   206          * @return void
    62          *
   207          * @throws Exception
    63          * In order of preference:
       
    64          *   1. Use libsodium if available.
       
    65          *   2. fread() /dev/urandom if available (never on Windows)
       
    66          *   3. mcrypt_create_iv($bytes, MCRYPT_DEV_URANDOM)
       
    67          *   4. COM('CAPICOM.Utilities.1')->GetRandom()
       
    68          *   5. openssl_random_pseudo_bytes() (absolute last resort)
       
    69          *
       
    70          * See ERRATA.md for our reasoning behind this particular order
       
    71          */
   208          */
    72         if (extension_loaded('libsodium')) {
   209         function random_bytes($length)
    73             // See random_bytes_libsodium.php
   210         {
    74             if (PHP_VERSION_ID >= 50300 && function_exists('\\Sodium\\randombytes_buf')) {
   211             unset($length); // Suppress "variable not used" warnings.
    75                 require_once $RandomCompatDIR.'/random_bytes_libsodium.php';
   212             throw new Exception(
    76             } elseif (method_exists('Sodium', 'randombytes_buf')) {
   213                 'There is no suitable CSPRNG installed on your system'
    77                 require_once $RandomCompatDIR.'/random_bytes_libsodium_legacy.php';
       
    78             }
       
    79         }
       
    80 
       
    81         /**
       
    82          * Reading directly from /dev/urandom:
       
    83          */
       
    84         if (DIRECTORY_SEPARATOR === '/') {
       
    85             // DIRECTORY_SEPARATOR === '/' on Unix-like OSes -- this is a fast
       
    86             // way to exclude Windows.
       
    87             $RandomCompatUrandom = true;
       
    88             $RandomCompat_basedir = ini_get('open_basedir');
       
    89 
       
    90             if (!empty($RandomCompat_basedir)) {
       
    91                 $RandomCompat_open_basedir = explode(
       
    92                     PATH_SEPARATOR,
       
    93                     strtolower($RandomCompat_basedir)
       
    94                 );
       
    95                 $RandomCompatUrandom = in_array(
       
    96                     '/dev',
       
    97                     $RandomCompat_open_basedir
       
    98                 );
       
    99                 $RandomCompat_open_basedir = null;
       
   100             }
       
   101 
       
   102             if (
       
   103                 !function_exists('random_bytes')
       
   104                 &&
       
   105                 $RandomCompatUrandom
       
   106                 &&
       
   107                 @is_readable('/dev/urandom')
       
   108             ) {
       
   109                 // Error suppression on is_readable() in case of an open_basedir
       
   110                 // or safe_mode failure. All we care about is whether or not we
       
   111                 // can read it at this point. If the PHP environment is going to
       
   112                 // panic over trying to see if the file can be read in the first
       
   113                 // place, that is not helpful to us here.
       
   114 
       
   115                 // See random_bytes_dev_urandom.php
       
   116                 require_once $RandomCompatDIR.'/random_bytes_dev_urandom.php';
       
   117             }
       
   118             // Unset variables after use
       
   119             $RandomCompat_basedir = null;
       
   120             $RandomCompatUrandom = null;
       
   121         }
       
   122 
       
   123         /**
       
   124          * mcrypt_create_iv()
       
   125          */
       
   126         if (
       
   127             !function_exists('random_bytes')
       
   128             &&
       
   129             PHP_VERSION_ID >= 50307
       
   130             &&
       
   131             extension_loaded('mcrypt')
       
   132         ) {
       
   133             // Prevent this code from hanging indefinitely on non-Windows;
       
   134             // see https://bugs.php.net/bug.php?id=69833
       
   135             if (
       
   136                 DIRECTORY_SEPARATOR !== '/' ||
       
   137                 (PHP_VERSION_ID <= 50609 || PHP_VERSION_ID >= 50613)
       
   138             ) {
       
   139                 // See random_bytes_mcrypt.php
       
   140                 require_once $RandomCompatDIR.'/random_bytes_mcrypt.php';
       
   141             }
       
   142         }
       
   143 
       
   144         if (
       
   145             !function_exists('random_bytes')
       
   146             &&
       
   147             extension_loaded('com_dotnet')
       
   148             &&
       
   149             class_exists('COM')
       
   150         ) {
       
   151             $RandomCompat_disabled_classes = preg_split(
       
   152                 '#\s*,\s*#',
       
   153                 strtolower(ini_get('disable_classes'))
       
   154             );
   214             );
   155 
   215         }
   156             if (!in_array('com', $RandomCompat_disabled_classes)) {
   216     }
   157                 try {
   217 }
   158                     $RandomCompatCOMtest = new COM('CAPICOM.Utilities.1');
   218 
   159                     if (method_exists($RandomCompatCOMtest, 'GetRandom')) {
   219 if (!is_callable('random_int')) {
   160                         // See random_bytes_com_dotnet.php
   220     require_once $RandomCompatDIR . '/random_int.php';
   161                         require_once $RandomCompatDIR.'/random_bytes_com_dotnet.php';
   221 }
   162                     }
   222 
   163                 } catch (com_exception $e) {
   223 $RandomCompatDIR = null;
   164                     // Don't try to use it.
       
   165                 }
       
   166             }
       
   167             $RandomCompat_disabled_classes = null;
       
   168             $RandomCompatCOMtest = null;
       
   169         }
       
   170 
       
   171         /**
       
   172          * openssl_random_pseudo_bytes()
       
   173          */
       
   174         if (
       
   175             (
       
   176                 // Unix-like with PHP >= 5.3.0 or
       
   177                 (
       
   178                     DIRECTORY_SEPARATOR === '/'
       
   179                     &&
       
   180                     PHP_VERSION_ID >= 50300
       
   181                 )
       
   182                 ||
       
   183                 // Windows with PHP >= 5.4.1
       
   184                 PHP_VERSION_ID >= 50401
       
   185             )
       
   186             &&
       
   187             !function_exists('random_bytes')
       
   188             &&
       
   189             extension_loaded('openssl')
       
   190         ) {
       
   191             // See random_bytes_openssl.php
       
   192             require_once $RandomCompatDIR.'/random_bytes_openssl.php';
       
   193         }
       
   194 
       
   195         /**
       
   196          * throw new Exception
       
   197          */
       
   198         if (!function_exists('random_bytes')) {
       
   199             /**
       
   200              * We don't have any more options, so let's throw an exception right now
       
   201              * and hope the developer won't let it fail silently.
       
   202              */
       
   203             function random_bytes($length)
       
   204             {
       
   205                 throw new Exception(
       
   206                     'There is no suitable CSPRNG installed on your system'
       
   207                 );
       
   208             }
       
   209         }
       
   210     }
       
   211 
       
   212     if (!function_exists('random_int')) {
       
   213         require_once $RandomCompatDIR.'/random_int.php';
       
   214     }
       
   215 
       
   216     $RandomCompatDIR = null;
       
   217 }