mod_entropy.c 4.23 KB
/**
 * \file
 *
 * this filter generates a sha1 from the current microtime and request
 * useses this to fill the linux random source.
 *
 * inspired by timed_entropyd.
 *
 * ATTENTION: This module is not portable right now as i don't know
 * howto fill the random source for other systems. It is linux only.
 *
 * Most time was spend in figuring out how to write apache modules.
 *
 * \author  Georg Hopp <georg@steffers.org
 *
 * \copyright
 * Copyright © 2012  Georg Hopp
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#define _POSIX_C_SOURCE 199309L

#include "httpd.h"
#include "http_core.h"
#include "http_connection.h"
#include "http_config.h"
#include "http_core.h"
#include "http_log.h"
#include "apu.h"
#include "apr_general.h"
#include "apr_sha1.h"

#include <time.h>


int add_entropy(const unsigned char *, size_t);

module AP_MODULE_DECLARE_DATA entropy_module;


/**
 * add header values to sha1
 */
static
int
header_do_print(void * rec, const char * key, const char * value)
{
	apr_sha1_ctx_t * sha1_ctx = rec;

	apr_sha1_update(sha1_ctx, value, strlen(value));

	return 1;
}

static
apr_status_t
entropy_filter_in(
		ap_filter_t        * filter,
		apr_bucket_brigade * brigade, 
		ap_input_mode_t      mode,
		apr_read_type_e      block,
		apr_off_t            readbytes)
{
	apr_bucket     * bucket;
	apr_status_t     status;
	request_rec    * request    = filter->r;
	conn_rec       * connection = filter->c;
	apr_sha1_ctx_t   sha1_ctx;
	unsigned char    digest[APR_SHA1_DIGESTSIZE];
	ap_version_t     ap_version;
	char           * remote_ip;

	struct timespec ts;

	ap_get_server_revision(&ap_version);

	clock_gettime(CLOCK_REALTIME, &ts);

	apr_sha1_init(&sha1_ctx);

	/**
	 * add current microtime to sha1
	 */
	apr_sha1_update_binary(
			&sha1_ctx,
			(const unsigned char *)&ts,
			sizeof(ts));

	/**
	 * add client ip to sha1
	 */
	remote_ip = (ap_version.minor < 4)?
		connection->remote_ip:
		connection->client_ip;

	apr_sha1_update(&sha1_ctx, remote_ip, strlen(remote_ip));

	/**
	 * add request line to sha1
	 */
	apr_sha1_update(
			&sha1_ctx,
			request->the_request,
			strlen(request->the_request));

	/**
	 * add all header values to sha1
	 */
	apr_table_do(header_do_print, &sha1_ctx, request->headers_in, NULL);

	/**
	 * get the request body and add it to the sha1
	 */
	status = ap_get_brigade(filter->next, brigade, mode, block, readbytes);

	if (status == APR_SUCCESS) {
		for (
				bucket  = APR_BRIGADE_FIRST(brigade);
				bucket != APR_BRIGADE_SENTINEL(brigade);
				bucket  = APR_BUCKET_NEXT(bucket)) {

			if (!(APR_BUCKET_IS_METADATA(bucket))) {
				const char      * buffer;
				apr_size_t        nbuffer;

				status = apr_bucket_read(
						bucket,
						&buffer,
						&nbuffer,
						APR_BLOCK_READ);

				if (status == APR_SUCCESS) {
					apr_sha1_update(&sha1_ctx, buffer, nbuffer);
				}
			}
		}
	}

	/**
	 * get the sha1 digest
	 */
	apr_sha1_final(digest, &sha1_ctx);

	/**
	 * fill /dev/random with sha1 from current request
	 */
	add_entropy(digest, APR_SHA1_DIGESTSIZE);

	/**
	 * after we are done remove us from filter queue
	 */
	ap_remove_input_filter(filter);

	return status;
}

/**
 * apache module initialization
 */
static
void
entropy_register_hook(apr_pool_t *p)
{
	ap_register_input_filter(
			"ENTROPY",
			entropy_filter_in,
			NULL,
			AP_FTYPE_CONTENT_SET);
}

module AP_MODULE_DECLARE_DATA entropy_module = {
	STANDARD20_MODULE_STUFF,
	NULL,				/* create per-directory config structure */
	NULL,				/* merge per-directory config structures */
	NULL,				/* create per-server config structure */
	NULL,				/* merge per-server config structures */
	NULL,				/* command apr_table_t */
	entropy_register_hook	/* register hooks */
};

// vim: set ts=4 sw=4: