mirror of
https://github.com/lammertb/libhttp.git
synced 2025-12-22 04:02:04 +03:00
Use 64 bit random number nonce in http digest auth
This commit is contained in:
124
src/civetweb.c
124
src/civetweb.c
@@ -149,12 +149,12 @@ clock_gettime(int clk_id, struct timespec *t)
|
||||
return 0;
|
||||
|
||||
} else if (clk_id == CLOCK_MONOTONIC) {
|
||||
static uint64_t start_time = 0;
|
||||
static uint64_t clock_start_time = 0;
|
||||
static mach_timebase_info_data_t timebase_ifo = {0, 0};
|
||||
|
||||
uint64_t now = mach_absolute_time();
|
||||
|
||||
if (start_time == 0) {
|
||||
if (clock_start_time == 0) {
|
||||
kern_return_t mach_status = mach_timebase_info(&timebase_ifo);
|
||||
#if defined(DEBUG)
|
||||
assert(mach_status == KERN_SUCCESS);
|
||||
@@ -162,10 +162,11 @@ clock_gettime(int clk_id, struct timespec *t)
|
||||
/* appease "unused variable" warning for release builds */
|
||||
(void)mach_status;
|
||||
#endif
|
||||
start_time = now;
|
||||
clock_start_time = now;
|
||||
}
|
||||
|
||||
now = (uint64_t)((double)(now - start_time) * (double)timebase_ifo.numer
|
||||
now = (uint64_t)((double)(now - clock_start_time)
|
||||
* (double)timebase_ifo.numer
|
||||
/ (double)timebase_ifo.denom);
|
||||
|
||||
t->tv_sec = now / 1000000000;
|
||||
@@ -270,6 +271,7 @@ typedef long off_t;
|
||||
#endif /* !EWOULDBLOCK */
|
||||
#define _POSIX_
|
||||
#define INT64_FMT "I64d"
|
||||
#define UINT64_FMT "I64u"
|
||||
|
||||
#define WINCDECL __cdecl
|
||||
#define SHUT_RD (0)
|
||||
@@ -426,6 +428,7 @@ typedef unsigned short int in_port_t;
|
||||
#define ERRNO (errno)
|
||||
#define INVALID_SOCKET (-1)
|
||||
#define INT64_FMT PRId64
|
||||
#define UINT64_FMT PRIu64
|
||||
typedef int SOCKET;
|
||||
#define WINCDECL
|
||||
|
||||
@@ -1160,7 +1163,8 @@ struct mg_context {
|
||||
cfg_worker_threads; /* The number of configured worker threads. */
|
||||
pthread_t *workerthreadids; /* The worker thread IDs */
|
||||
|
||||
unsigned long start_time; /* Server start time, used for authentication */
|
||||
time_t start_time; /* Server start time, used for authentication */
|
||||
uint64_t auth_nonce_mask; /* Mask for all nonce values */
|
||||
pthread_mutex_t nonce_mutex; /* Protects nonce_count */
|
||||
unsigned long nonce_count; /* Used nonces, used for authentication */
|
||||
|
||||
@@ -1556,6 +1560,7 @@ mg_vsnprintf(const struct mg_connection *conn,
|
||||
buf[n] = '\0';
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
mg_snprintf(const struct mg_connection *conn,
|
||||
int *truncated,
|
||||
@@ -1572,39 +1577,6 @@ mg_snprintf(const struct mg_connection *conn,
|
||||
}
|
||||
|
||||
|
||||
static int64_t
|
||||
get_random(void)
|
||||
{
|
||||
static uint64_t lfsr = 0; /* Linear feedback shift register */
|
||||
static uint64_t lcg = 0; /* Linear congruential generator */
|
||||
struct timespec now;
|
||||
|
||||
memset(&now, 0, sizeof(now));
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
|
||||
if (lfsr == 0) {
|
||||
/* lfsr will be only 0 if has not been initialized,
|
||||
* so this code is called only once. */
|
||||
lfsr = (((uint64_t)now.tv_sec) << 21) ^ ((uint64_t)now.tv_nsec)
|
||||
^ ((uint64_t)(ptrdiff_t)&now) ^ ((uint64_t)pthread_self())
|
||||
^ (((uint64_t)time(NULL)) << 33);
|
||||
lcg = (((uint64_t)now.tv_sec) << 25) + (uint64_t)now.tv_nsec
|
||||
+ (uint64_t)(ptrdiff_t)&now;
|
||||
} else {
|
||||
/* Get the next step of both random number generators. */
|
||||
lfsr = (lfsr >> 1)
|
||||
| ((((lfsr >> 0) ^ (lfsr >> 1) ^ (lfsr >> 3) ^ (lfsr >> 4)) & 1)
|
||||
<< 63);
|
||||
lcg = lcg * 6364136223846793005 + 1442695040888963407;
|
||||
}
|
||||
|
||||
/* Combining two pseudo-random number generators and a high resolution part
|
||||
* of the current server time will make it hard (impossible?) to guess the
|
||||
* next number. */
|
||||
return (lfsr ^ lcg ^ now.tv_nsec);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
get_option_index(const char *name)
|
||||
{
|
||||
@@ -1618,6 +1590,7 @@ get_option_index(const char *name)
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
const char *
|
||||
mg_get_option(const struct mg_context *ctx, const char *name)
|
||||
{
|
||||
@@ -1631,18 +1604,21 @@ mg_get_option(const struct mg_context *ctx, const char *name)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct mg_context *
|
||||
mg_get_context(const struct mg_connection *conn)
|
||||
{
|
||||
return (conn == NULL) ? (struct mg_context *)NULL : (conn->ctx);
|
||||
}
|
||||
|
||||
|
||||
void *
|
||||
mg_get_user_data(const struct mg_context *ctx)
|
||||
{
|
||||
return (ctx == NULL) ? NULL : ctx->user_data;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
mg_set_user_connection_data(const struct mg_connection *conn, void *data)
|
||||
{
|
||||
@@ -1651,6 +1627,7 @@ mg_set_user_connection_data(const struct mg_connection *conn, void *data)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void *
|
||||
mg_get_user_connection_data(const struct mg_connection *conn)
|
||||
{
|
||||
@@ -1660,6 +1637,7 @@ mg_get_user_connection_data(const struct mg_connection *conn)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
size_t
|
||||
mg_get_ports(const struct mg_context *ctx, size_t size, int *ports, int *ssl)
|
||||
{
|
||||
@@ -1674,6 +1652,7 @@ mg_get_ports(const struct mg_context *ctx, size_t size, int *ports, int *ssl)
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
mg_get_server_ports(const struct mg_context *ctx,
|
||||
int size,
|
||||
@@ -1712,6 +1691,7 @@ mg_get_server_ports(const struct mg_context *ctx,
|
||||
return cnt;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
sockaddr_to_string(char *buf, size_t len, const union usa *usa)
|
||||
{
|
||||
@@ -1743,6 +1723,7 @@ sockaddr_to_string(char *buf, size_t len, const union usa *usa)
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* Convert time_t to a string. According to RFC2616, Sec 14.18, this must be
|
||||
* included in all responses other than 100, 101, 5xx. */
|
||||
static void
|
||||
@@ -1759,6 +1740,7 @@ gmt_time_string(char *buf, size_t buf_len, time_t *t)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* difftime for struct timespec. Return value is in seconds. */
|
||||
static double
|
||||
mg_difftimespec(const struct timespec *ts_now, const struct timespec *ts_before)
|
||||
@@ -1767,6 +1749,7 @@ mg_difftimespec(const struct timespec *ts_now, const struct timespec *ts_before)
|
||||
+ (double)(ts_now->tv_sec - ts_before->tv_sec);
|
||||
}
|
||||
|
||||
|
||||
/* Print error message to the opened error log stream. */
|
||||
void
|
||||
mg_cry(const struct mg_connection *conn, const char *fmt, ...)
|
||||
@@ -1815,6 +1798,7 @@ mg_cry(const struct mg_connection *conn, const char *fmt, ...)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Return fake connection structure. Used for logging, if connection
|
||||
* is not applicable at the moment of logging. */
|
||||
static struct mg_connection *
|
||||
@@ -1825,12 +1809,14 @@ fc(struct mg_context *ctx)
|
||||
return &fake_connection;
|
||||
}
|
||||
|
||||
|
||||
const char *
|
||||
mg_version(void)
|
||||
{
|
||||
return CIVETWEB_VERSION;
|
||||
}
|
||||
|
||||
|
||||
const struct mg_request_info *
|
||||
mg_get_request_info(const struct mg_connection *conn)
|
||||
{
|
||||
@@ -1840,6 +1826,7 @@ mg_get_request_info(const struct mg_connection *conn)
|
||||
return &conn->request_info;
|
||||
}
|
||||
|
||||
|
||||
/* Skip the characters until one of the delimiters characters found.
|
||||
* 0-terminate resulting word. Skip the delimiter and following whitespaces.
|
||||
* Advance pointer to buffer to the next word. Return found 0-terminated word.
|
||||
@@ -1894,6 +1881,7 @@ skip_quoted(char **buf,
|
||||
return begin_word;
|
||||
}
|
||||
|
||||
|
||||
/* Simplified version of skip_quoted without quote char
|
||||
* and whitespace == delimiters */
|
||||
static char *
|
||||
@@ -1902,6 +1890,7 @@ skip(char **buf, const char *delimiters)
|
||||
return skip_quoted(buf, delimiters, delimiters, 0);
|
||||
}
|
||||
|
||||
|
||||
/* Return HTTP header value, or NULL if not found. */
|
||||
static const char *
|
||||
get_header(const struct mg_request_info *ri, const char *name)
|
||||
@@ -1918,6 +1907,7 @@ get_header(const struct mg_request_info *ri, const char *name)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
const char *
|
||||
mg_get_header(const struct mg_connection *conn, const char *name)
|
||||
{
|
||||
@@ -1928,6 +1918,7 @@ mg_get_header(const struct mg_connection *conn, const char *name)
|
||||
return get_header(&conn->request_info, name);
|
||||
}
|
||||
|
||||
|
||||
/* A helper function for traversing a comma separated list of values.
|
||||
* It returns a list pointer shifted to the next value, or NULL if the end
|
||||
* of the list found.
|
||||
@@ -3355,6 +3346,42 @@ set_non_blocking_mode(SOCKET sock)
|
||||
return 0;
|
||||
}
|
||||
#endif /* _WIN32 */
|
||||
/* End of initial operating system specific define block. */
|
||||
|
||||
|
||||
/* Get a random number (independent of C rand function) */
|
||||
static int64_t
|
||||
get_random(void)
|
||||
{
|
||||
static uint64_t lfsr = 0; /* Linear feedback shift register */
|
||||
static uint64_t lcg = 0; /* Linear congruential generator */
|
||||
struct timespec now;
|
||||
|
||||
memset(&now, 0, sizeof(now));
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
|
||||
if (lfsr == 0) {
|
||||
/* lfsr will be only 0 if has not been initialized,
|
||||
* so this code is called only once. */
|
||||
lfsr = (((uint64_t)now.tv_sec) << 21) ^ ((uint64_t)now.tv_nsec)
|
||||
^ ((uint64_t)(ptrdiff_t)&now) ^ ((uint64_t)pthread_self())
|
||||
^ (((uint64_t)time(NULL)) << 33);
|
||||
lcg = (((uint64_t)now.tv_sec) << 25) + (uint64_t)now.tv_nsec
|
||||
+ (uint64_t)(ptrdiff_t)&now;
|
||||
} else {
|
||||
/* Get the next step of both random number generators. */
|
||||
lfsr = (lfsr >> 1)
|
||||
| ((((lfsr >> 0) ^ (lfsr >> 1) ^ (lfsr >> 3) ^ (lfsr >> 4)) & 1)
|
||||
<< 63);
|
||||
lcg = lcg * 6364136223846793005 + 1442695040888963407;
|
||||
}
|
||||
|
||||
/* Combining two pseudo-random number generators and a high resolution part
|
||||
* of the current server time will make it hard (impossible?) to guess the
|
||||
* next number. */
|
||||
return (lfsr ^ lcg ^ (uint64_t)now.tv_nsec);
|
||||
}
|
||||
|
||||
|
||||
/* Write data to the IO channel - opened file descriptor, socket or SSL
|
||||
* descriptor. Return number of bytes written. */
|
||||
@@ -4841,7 +4868,7 @@ parse_auth_header(struct mg_connection *conn,
|
||||
{
|
||||
char *name, *value, *s;
|
||||
const char *auth_header;
|
||||
unsigned long nonce;
|
||||
uint64_t nonce;
|
||||
|
||||
if (!ah || !conn) {
|
||||
return 0;
|
||||
@@ -4902,13 +4929,13 @@ parse_auth_header(struct mg_connection *conn,
|
||||
return 0;
|
||||
}
|
||||
s = NULL;
|
||||
nonce = strtoul(ah->nonce, &s, 10);
|
||||
nonce = strtoull(ah->nonce, &s, 10);
|
||||
if ((s == NULL) || (*s != 0)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Convert the nonce from the client to a number. */
|
||||
nonce ^= (uintptr_t)(conn->ctx);
|
||||
nonce ^= conn->ctx->auth_nonce_mask;
|
||||
|
||||
/* The converted number corresponds to the time the nounce has been
|
||||
* created. This should not be earlier than the server start. */
|
||||
@@ -4918,14 +4945,14 @@ parse_auth_header(struct mg_connection *conn,
|
||||
/* However, the reasonable default is to not accept a nonce from a
|
||||
* previous start, so if anyone changed the access rights between
|
||||
* two restarts, a new login is required. */
|
||||
if (nonce < conn->ctx->start_time) {
|
||||
if (nonce < (uint64_t)conn->ctx->start_time) {
|
||||
/* nonce is from a previous start of the server and no longer valid
|
||||
* (replay attack?) */
|
||||
return 0;
|
||||
}
|
||||
/* Check if the nonce is too high, so it has not (yet) been used by the
|
||||
* server. */
|
||||
if (nonce >= conn->ctx->start_time + conn->ctx->nonce_count) {
|
||||
if (nonce >= ((uint64_t)conn->ctx->start_time + conn->ctx->nonce_count)) {
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@@ -5155,14 +5182,14 @@ send_authorization_request(struct mg_connection *conn)
|
||||
time_t curtime = time(NULL);
|
||||
|
||||
if (conn && conn->ctx) {
|
||||
unsigned long nonce = (unsigned long)(conn->ctx->start_time);
|
||||
uint64_t nonce = (uint64_t)(conn->ctx->start_time);
|
||||
|
||||
(void)pthread_mutex_lock(&conn->ctx->nonce_mutex);
|
||||
nonce += conn->ctx->nonce_count;
|
||||
++conn->ctx->nonce_count;
|
||||
(void)pthread_mutex_unlock(&conn->ctx->nonce_mutex);
|
||||
|
||||
nonce ^= (uintptr_t)(conn->ctx);
|
||||
nonce ^= conn->ctx->auth_nonce_mask;
|
||||
conn->status_code = 401;
|
||||
conn->must_close = 1;
|
||||
|
||||
@@ -5174,7 +5201,7 @@ send_authorization_request(struct mg_connection *conn)
|
||||
"Connection: %s\r\n"
|
||||
"Content-Length: 0\r\n"
|
||||
"WWW-Authenticate: Digest qop=\"auth\", realm=\"%s\", "
|
||||
"nonce=\"%lu\"\r\n\r\n",
|
||||
"nonce=\"%" UINT64_FMT "\"\r\n\r\n",
|
||||
date,
|
||||
suggest_connection_header(conn),
|
||||
conn->ctx->config[AUTHENTICATION_DOMAIN],
|
||||
@@ -11607,7 +11634,7 @@ master_thread_run(void *thread_func_param)
|
||||
pthread_setspecific(sTlsKey, &tls);
|
||||
|
||||
/* Server starts *now* */
|
||||
ctx->start_time = (unsigned long)time(NULL);
|
||||
ctx->start_time = time(NULL);
|
||||
|
||||
/* Allocate memory for the listening sockets, and start the server */
|
||||
pfd =
|
||||
@@ -11867,7 +11894,8 @@ mg_start(const struct mg_callbacks *callbacks,
|
||||
}
|
||||
|
||||
/* Random number generator will initialize at the first call */
|
||||
(void)get_random();
|
||||
ctx->auth_nonce_mask =
|
||||
(uint64_t)get_random() ^ (uint64_t)(ptrdiff_t)(options);
|
||||
|
||||
if (mg_atomic_inc(&sTlsInit) == 1) {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user