[openssl-dev] [openssl.org #4235] Crash on ssleay_rand_bytes - global variable is not protected

Alexandre PAQUE via RT rt at openssl.org
Thu Jan 14 13:37:24 UTC 2016


*​Context:​*

openssl 1.0.2e
Windows 2012
gsoap 2.8.7 - WSSE
Application multithread


*crash dump: *

d:\opensource\openssl-1.0.2e\crypto\sha\sha_locl.h (320):
sha1_block_data_order
d:\opensource\openssl-1.0.2e\tmp32\md32_common.h (343): SHA1_Update
d:\opensource\openssl-1.0.2e\crypto\evp\m_sha1.c (78): update
d:\opensource\openssl-1.0.2e\crypto\rand\md_rand.c (496): ssleay_rand_bytes
d:\opensource\openssl-1.0.2e\crypto\rand\md_rand.c (544):
ssleay_rand_pseudo_bytes
d:\dev\3rdparty\gsoap-2.8\gsoap\plugin\wsseapi.cpp (3545): calc_nonce
d:\dev\3rdparty\gsoap-2.8\gsoap\plugin\wsseapi.cpp (1718):
soap_wsse_add_UsernameTokenDigest

*Analyze:*

I think this issue occurs because of the global static variable "state" is
not protected into the functions (md_rand.c).

sample:

int ssleay_rand_bytes(unsigned char *buf, int num, int pseudo, int lock)
{
...

*if (lock)*
*        CRYPTO_w_lock(CRYPTO_LOCK_RAND);*

*    /* prevent ssleay_rand_bytes() from trying to obtain the lock again */*
*    CRYPTO_w_lock(CRYPTO_LOCK_RAND2);*
*    CRYPTO_THREADID_current(&locking_threadid);*
*    CRYPTO_w_unlock(CRYPTO_LOCK_RAND2);*
*    crypto_lock_rand = 1;*

    if (!initialized) {
        RAND_poll();
        initialized = 1;
    }

    if (!stirred_pool)
        do_stir_pool = 1;

    ok = (entropy >= ENTROPY_NEEDED);
    if (!ok) {
        /*
         * If the PRNG state is not yet unpredictable, then seeing the PRNG
         * output may help attackers to determine the new state; thus we
have
         * to decrease the entropy estimate. Once we've had enough initial
         * seeding we don't bother to adjust the entropy count, though,
         * because we're not ambitious to provide *information-theoretic*
         * randomness. NOTE: This approach fails if the program forks before
         * we have enough entropy. Entropy should be collected in a separate
         * input pool and be transferred to the output pool only when the
         * entropy limit has been reached.
         */
        entropy -= num;
        if (entropy < 0)
            entropy = 0;
    }

    if (do_stir_pool) {
        /*
         * In the output function only half of 'md' remains secret, so we
         * better make sure that the required entropy gets 'evenly
         * distributed' through 'state', our randomness pool. The input
         * function (ssleay_rand_add) chains all of 'md', which makes it
more
         * suitable for this purpose.
         */

        int n = STATE_SIZE;     /* so that the complete pool gets accessed
*/
        while (n > 0) {
#if MD_DIGEST_LENGTH > 20
# error "Please adjust DUMMY_SEED."
#endif
#define DUMMY_SEED "...................." /* at least MD_DIGEST_LENGTH */
            /*
             * Note that the seed does not matter, it's just that
             * ssleay_rand_add expects to have something to hash.
             */
            ssleay_rand_add(DUMMY_SEED, MD_DIGEST_LENGTH, 0.0);
            n -= MD_DIGEST_LENGTH;
        }
        if (ok)
            stirred_pool = 1;
    }

    st_idx = state_index;
    st_num = state_num;
    md_c[0] = md_count[0];
    md_c[1] = md_count[1];
    memcpy(local_md, md, sizeof md);

    state_index += num_ceil;
    if (state_index > state_num)
        state_index %= state_num;

    /*
     * state[st_idx], ..., state[(st_idx + num_ceil - 1) % st_num] are now
     * ours (but other threads may use them too)
     */

    md_count[0] += 1;

    /* before unlocking, we must clear 'crypto_lock_rand' */
    crypto_lock_rand = 0;
 *   if (lock)*
*        CRYPTO_w_unlock(CRYPTO_LOCK_RAND);*

    while (num > 0) {
        /* num_ceil -= MD_DIGEST_LENGTH/2 */
        j = (num >= MD_DIGEST_LENGTH / 2) ? MD_DIGEST_LENGTH / 2 : num;
        num -= j;
        MD_Init(&m);
#ifndef GETPID_IS_MEANINGLESS
        if (curr_pid) {         /* just in the first iteration to save time
*/
            MD_Update(&m, (unsigned char *)&curr_pid, sizeof curr_pid);
            curr_pid = 0;
        }
#endif
        MD_Update(&m, local_md, MD_DIGEST_LENGTH);
        MD_Update(&m, (unsigned char *)&(md_c[0]), sizeof(md_c));

#ifndef PURIFY                  /* purify complains */
        /*
         * The following line uses the supplied buffer as a small source of
         * entropy: since this buffer is often uninitialised it may cause
         * programs such as purify or valgrind to complain. So for those
         * builds it is not used: the removal of such a small source of
         * entropy has negligible impact on security.
         */
        MD_Update(&m, buf, j);
#endif

// no lock to protect "state" global variable

        k = (st_idx + MD_DIGEST_LENGTH / 2) - st_num;
        if (k > 0) {
            MD_Update(&m, &(state[st_idx]), MD_DIGEST_LENGTH / 2 - k);
         *   MD_Update(&m, &(state[0]), k); // CRASH*
        } else
            MD_Update(&m, &(state[st_idx]), MD_DIGEST_LENGTH / 2);
        MD_Final(&m, local_md);

        for (i = 0; i < MD_DIGEST_LENGTH / 2; i++) {
            /* may compete with other threads */
            state[st_idx++] ^= local_md[i];
            if (st_idx >= st_num)
                st_idx = 0;
            if (i < j)
                *(buf++) = local_md[i + MD_DIGEST_LENGTH / 2];
        }
    }

    MD_Init(&m);
    MD_Update(&m, (unsigned char *)&(md_c[0]), sizeof(md_c));
    MD_Update(&m, local_md, MD_DIGEST_LENGTH);
*    if (lock)*
*        CRYPTO_w_lock(CRYPTO_LOCK_RAND);*
    MD_Update(&m, md, MD_DIGEST_LENGTH);
    MD_Final(&m, md);
 *   if (lock)*
*        CRYPTO_w_unlock(CRYPTO_LOCK_RAND);*

    EVP_MD_CTX_cleanup(&m);



Best Regards,
Alex

-------------- next part --------------
_______________________________________________
openssl-bugs-mod mailing list
openssl-bugs-mod at openssl.org
https://mta.openssl.org/mailman/listinfo/openssl-bugs-mod


More information about the openssl-dev mailing list