6c648dd642
This is actually a fully functional build except: * All internal shared libraries are static linked to make sure there is no interference with ports (and to reduce build time). * It does not have the python/perl/etc plugin or API support. * By default, it installs as "svnlite" rather than "svn". * If WITH_SVN added in make.conf, you get "svn". * If WITHOUT_SVNLITE is in make.conf, this is completely disabled. To be absolutely clear, this is not intended for any use other than checking out freebsd source and committing, like we once did with cvs. It should be usable for small scale local repositories that don't need the python/perl plugin architecture.
327 lines
9.5 KiB
C
327 lines
9.5 KiB
C
/* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership.
|
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
|
* (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/*
|
|
* See the paper "On Randomness" by Ben Laurie for an explanation of this PRNG.
|
|
* http://www.apache-ssl.org/randomness.pdf
|
|
* XXX: Is there a formal proof of this PRNG? Couldn't we use the more popular
|
|
* Mersenne Twister PRNG (and BSD licensed)?
|
|
*/
|
|
|
|
#include "apr.h"
|
|
#include "apr_pools.h"
|
|
#include "apr_random.h"
|
|
#include "apr_thread_proc.h"
|
|
#include <assert.h>
|
|
|
|
#ifdef min
|
|
#undef min
|
|
#endif
|
|
#define min(a,b) ((a) < (b) ? (a) : (b))
|
|
|
|
#define APR_RANDOM_DEFAULT_POOLS 32
|
|
#define APR_RANDOM_DEFAULT_REHASH_SIZE 1024
|
|
#define APR_RANDOM_DEFAULT_RESEED_SIZE 32
|
|
#define APR_RANDOM_DEFAULT_HASH_SECRET_SIZE 32
|
|
#define APR_RANDOM_DEFAULT_G_FOR_INSECURE 32
|
|
#define APR_RANDOM_DEFAULT_G_FOR_SECURE 320
|
|
|
|
typedef struct apr_random_pool_t {
|
|
unsigned char *pool;
|
|
unsigned int bytes;
|
|
unsigned int pool_size;
|
|
} apr_random_pool_t;
|
|
|
|
#define hash_init(h) (h)->init(h)
|
|
#define hash_add(h,b,n) (h)->add(h,b,n)
|
|
#define hash_finish(h,r) (h)->finish(h,r)
|
|
|
|
#define hash(h,r,b,n) hash_init(h),hash_add(h,b,n),hash_finish(h,r)
|
|
|
|
#define crypt_setkey(c,k) (c)->set_key((c)->data,k)
|
|
#define crypt_crypt(c,out,in) (c)->crypt((c)->date,out,in)
|
|
|
|
struct apr_random_t {
|
|
apr_pool_t *apr_pool;
|
|
apr_crypto_hash_t *pool_hash;
|
|
unsigned int npools;
|
|
apr_random_pool_t *pools;
|
|
unsigned int next_pool;
|
|
unsigned int generation;
|
|
apr_size_t rehash_size;
|
|
apr_size_t reseed_size;
|
|
apr_crypto_hash_t *key_hash;
|
|
#define K_size(g) ((g)->key_hash->size)
|
|
apr_crypto_hash_t *prng_hash;
|
|
#define B_size(g) ((g)->prng_hash->size)
|
|
|
|
unsigned char *H;
|
|
unsigned char *H_waiting;
|
|
#define H_size(g) (B_size(g)+K_size(g))
|
|
#define H_current(g) (((g)->insecure_started && !(g)->secure_started) \
|
|
? (g)->H_waiting : (g)->H)
|
|
|
|
unsigned char *randomness;
|
|
apr_size_t random_bytes;
|
|
unsigned int g_for_insecure;
|
|
unsigned int g_for_secure;
|
|
unsigned int secure_base;
|
|
unsigned int insecure_started:1;
|
|
unsigned int secure_started:1;
|
|
|
|
apr_random_t *next;
|
|
};
|
|
|
|
static apr_random_t *all_random;
|
|
|
|
static apr_status_t random_cleanup(void *data)
|
|
{
|
|
apr_random_t *remove_this = data,
|
|
*cur = all_random,
|
|
**prev_ptr = &all_random;
|
|
while (cur) {
|
|
if (cur == remove_this) {
|
|
*prev_ptr = cur->next;
|
|
break;
|
|
}
|
|
prev_ptr = &cur->next;
|
|
cur = cur->next;
|
|
}
|
|
return APR_SUCCESS;
|
|
}
|
|
|
|
|
|
APR_DECLARE(void) apr_random_init(apr_random_t *g,apr_pool_t *p,
|
|
apr_crypto_hash_t *pool_hash,
|
|
apr_crypto_hash_t *key_hash,
|
|
apr_crypto_hash_t *prng_hash)
|
|
{
|
|
unsigned int n;
|
|
|
|
g->apr_pool = p;
|
|
|
|
g->pool_hash = pool_hash;
|
|
g->key_hash = key_hash;
|
|
g->prng_hash = prng_hash;
|
|
|
|
g->npools = APR_RANDOM_DEFAULT_POOLS;
|
|
g->pools = apr_palloc(p,g->npools*sizeof *g->pools);
|
|
for (n = 0; n < g->npools; ++n) {
|
|
g->pools[n].bytes = g->pools[n].pool_size = 0;
|
|
g->pools[n].pool = NULL;
|
|
}
|
|
g->next_pool = 0;
|
|
|
|
g->generation = 0;
|
|
|
|
g->rehash_size = APR_RANDOM_DEFAULT_REHASH_SIZE;
|
|
/* Ensure that the rehash size is twice the size of the pool hasher */
|
|
g->rehash_size = ((g->rehash_size+2*g->pool_hash->size-1)/g->pool_hash->size
|
|
/2)*g->pool_hash->size*2;
|
|
g->reseed_size = APR_RANDOM_DEFAULT_RESEED_SIZE;
|
|
|
|
g->H = apr_pcalloc(p,H_size(g));
|
|
g->H_waiting = apr_pcalloc(p,H_size(g));
|
|
|
|
g->randomness = apr_palloc(p,B_size(g));
|
|
g->random_bytes = 0;
|
|
|
|
g->g_for_insecure = APR_RANDOM_DEFAULT_G_FOR_INSECURE;
|
|
g->secure_base = 0;
|
|
g->g_for_secure = APR_RANDOM_DEFAULT_G_FOR_SECURE;
|
|
g->secure_started = g->insecure_started = 0;
|
|
|
|
g->next = all_random;
|
|
all_random = g;
|
|
apr_pool_cleanup_register(p, g, random_cleanup, apr_pool_cleanup_null);
|
|
}
|
|
|
|
static void mix_pid(apr_random_t *g,unsigned char *H,pid_t pid)
|
|
{
|
|
hash_init(g->key_hash);
|
|
hash_add(g->key_hash,H,H_size(g));
|
|
hash_add(g->key_hash,&pid,sizeof pid);
|
|
hash_finish(g->key_hash,H);
|
|
}
|
|
|
|
static void mixer(apr_random_t *g,pid_t pid)
|
|
{
|
|
unsigned char *H = H_current(g);
|
|
|
|
/* mix the PID into the current H */
|
|
mix_pid(g,H,pid);
|
|
/* if we are in waiting, then also mix into main H */
|
|
if (H != g->H)
|
|
mix_pid(g,g->H,pid);
|
|
/* change order of pool mixing for good measure - note that going
|
|
backwards is much better than going forwards */
|
|
--g->generation;
|
|
/* blow away any lingering randomness */
|
|
g->random_bytes = 0;
|
|
}
|
|
|
|
APR_DECLARE(void) apr_random_after_fork(apr_proc_t *proc)
|
|
{
|
|
apr_random_t *r;
|
|
|
|
for (r = all_random; r; r = r->next)
|
|
/*
|
|
* XXX Note: the pid does not provide sufficient entropy to
|
|
* actually call this secure. See Ben's paper referenced at
|
|
* the top of this file.
|
|
*/
|
|
mixer(r,proc->pid);
|
|
}
|
|
|
|
APR_DECLARE(apr_random_t *) apr_random_standard_new(apr_pool_t *p)
|
|
{
|
|
apr_random_t *r = apr_palloc(p,sizeof *r);
|
|
|
|
apr_random_init(r,p,apr_crypto_sha256_new(p),apr_crypto_sha256_new(p),
|
|
apr_crypto_sha256_new(p));
|
|
return r;
|
|
}
|
|
|
|
static void rekey(apr_random_t *g)
|
|
{
|
|
unsigned int n;
|
|
unsigned char *H = H_current(g);
|
|
|
|
hash_init(g->key_hash);
|
|
hash_add(g->key_hash,H,H_size(g));
|
|
for (n = 0 ; n < g->npools && (n == 0 || g->generation&(1 << (n-1)))
|
|
; ++n) {
|
|
hash_add(g->key_hash,g->pools[n].pool,g->pools[n].bytes);
|
|
g->pools[n].bytes = 0;
|
|
}
|
|
hash_finish(g->key_hash,H+B_size(g));
|
|
|
|
++g->generation;
|
|
if (!g->insecure_started && g->generation > g->g_for_insecure) {
|
|
g->insecure_started = 1;
|
|
if (!g->secure_started) {
|
|
memcpy(g->H_waiting,g->H,H_size(g));
|
|
g->secure_base = g->generation;
|
|
}
|
|
}
|
|
|
|
if (!g->secure_started && g->generation > g->secure_base+g->g_for_secure) {
|
|
g->secure_started = 1;
|
|
memcpy(g->H,g->H_waiting,H_size(g));
|
|
}
|
|
}
|
|
|
|
APR_DECLARE(void) apr_random_add_entropy(apr_random_t *g,const void *entropy_,
|
|
apr_size_t bytes)
|
|
{
|
|
unsigned int n;
|
|
const unsigned char *entropy = entropy_;
|
|
|
|
for (n = 0; n < bytes; ++n) {
|
|
apr_random_pool_t *p = &g->pools[g->next_pool];
|
|
|
|
if (++g->next_pool == g->npools)
|
|
g->next_pool = 0;
|
|
|
|
if (p->pool_size < p->bytes+1) {
|
|
unsigned char *np = apr_palloc(g->apr_pool,(p->bytes+1)*2);
|
|
|
|
memcpy(np,p->pool,p->bytes);
|
|
p->pool = np;
|
|
p->pool_size = (p->bytes+1)*2;
|
|
}
|
|
p->pool[p->bytes++] = entropy[n];
|
|
|
|
if (p->bytes == g->rehash_size) {
|
|
apr_size_t r;
|
|
|
|
for (r = 0; r < p->bytes/2; r+=g->pool_hash->size)
|
|
hash(g->pool_hash,p->pool+r,p->pool+r*2,g->pool_hash->size*2);
|
|
p->bytes/=2;
|
|
}
|
|
assert(p->bytes < g->rehash_size);
|
|
}
|
|
|
|
if (g->pools[0].bytes >= g->reseed_size)
|
|
rekey(g);
|
|
}
|
|
|
|
/* This will give g->B_size bytes of randomness */
|
|
static void apr_random_block(apr_random_t *g,unsigned char *random)
|
|
{
|
|
/* FIXME: in principle, these are different hashes */
|
|
hash(g->prng_hash,g->H,g->H,H_size(g));
|
|
hash(g->prng_hash,random,g->H,B_size(g));
|
|
}
|
|
|
|
static void apr_random_bytes(apr_random_t *g,unsigned char *random,
|
|
apr_size_t bytes)
|
|
{
|
|
apr_size_t n;
|
|
|
|
for (n = 0; n < bytes; ) {
|
|
apr_size_t l;
|
|
|
|
if (g->random_bytes == 0) {
|
|
apr_random_block(g,g->randomness);
|
|
g->random_bytes = B_size(g);
|
|
}
|
|
l = min(bytes-n,g->random_bytes);
|
|
memcpy(&random[n],g->randomness+B_size(g)-g->random_bytes,l);
|
|
g->random_bytes-=l;
|
|
n+=l;
|
|
}
|
|
}
|
|
|
|
APR_DECLARE(apr_status_t) apr_random_secure_bytes(apr_random_t *g,
|
|
void *random,
|
|
apr_size_t bytes)
|
|
{
|
|
if (!g->secure_started)
|
|
return APR_ENOTENOUGHENTROPY;
|
|
apr_random_bytes(g,random,bytes);
|
|
return APR_SUCCESS;
|
|
}
|
|
|
|
APR_DECLARE(apr_status_t) apr_random_insecure_bytes(apr_random_t *g,
|
|
void *random,
|
|
apr_size_t bytes)
|
|
{
|
|
if (!g->insecure_started)
|
|
return APR_ENOTENOUGHENTROPY;
|
|
apr_random_bytes(g,random,bytes);
|
|
return APR_SUCCESS;
|
|
}
|
|
|
|
APR_DECLARE(void) apr_random_barrier(apr_random_t *g)
|
|
{
|
|
g->secure_started = 0;
|
|
g->secure_base = g->generation;
|
|
}
|
|
|
|
APR_DECLARE(apr_status_t) apr_random_secure_ready(apr_random_t *r)
|
|
{
|
|
if (!r->secure_started)
|
|
return APR_ENOTENOUGHENTROPY;
|
|
return APR_SUCCESS;
|
|
}
|
|
|
|
APR_DECLARE(apr_status_t) apr_random_insecure_ready(apr_random_t *r)
|
|
{
|
|
if (!r->insecure_started)
|
|
return APR_ENOTENOUGHENTROPY;
|
|
return APR_SUCCESS;
|
|
}
|