Browse Source

Use 64 bit random number nonce in http digest auth

bel 9 years ago
parent
commit
f3b5d0f35d
1 changed files with 76 additions and 48 deletions
  1. 76 48
      src/civetweb.c

+ 76 - 48
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) {