[apache2] 06/07: CVE-2018-1312: mod_auth_digest: Weak Digest auth nonce generation
Stefan Fritsch
sf at moszumanska.debian.org
Sat Mar 31 09:46:39 UTC 2018
This is an automated email from the git hooks/post-receive script.
sf pushed a commit to branch jessie
in repository apache2.
commit 3c823942b5b3cce4a14da855179c1422606c4419
Author: Stefan Fritsch <sf at sfritsch.de>
Date: Fri Mar 30 16:48:52 2018 +0200
CVE-2018-1312: mod_auth_digest: Weak Digest auth nonce generation
---
debian/changelog | 1 +
.../CVE-2018-1312-mod_auth_digest-nonce.diff | 399 +++++++++++++++++++++
debian/patches/series | 1 +
3 files changed, 401 insertions(+)
diff --git a/debian/changelog b/debian/changelog
index 25a8c2b..ca3af2f 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -12,6 +12,7 @@ apache2 (2.4.10-10+deb8u12) UNRELEASED; urgency=medium
* CVE-2018-1301: Possible out of bound access after failure in reading the
HTTP request
* CVE-2018-1303: Possible out of bound read in mod_cache_socache
+ * CVE-2018-1312: mod_auth_digest: Weak Digest auth nonce generation
-- Stefan Fritsch <sf at debian.org> Sat, 31 Mar 2018 11:24:46 +0200
diff --git a/debian/patches/CVE-2018-1312-mod_auth_digest-nonce.diff b/debian/patches/CVE-2018-1312-mod_auth_digest-nonce.diff
new file mode 100644
index 0000000..1c14320
--- /dev/null
+++ b/debian/patches/CVE-2018-1312-mod_auth_digest-nonce.diff
@@ -0,0 +1,399 @@
+# https://svn.apache.org/r1824481
+# CVE-2018-1312
+--- apache2.orig/modules/aaa/mod_auth_digest.c
++++ apache2/modules/aaa/mod_auth_digest.c
+@@ -26,20 +26,13 @@
+ * reports to the Apache bug-database, or send them directly to me
+ * at ronald at innovation.ch.
+ *
+- * Requires either /dev/random (or equivalent) or the truerand library,
+- * available for instance from
+- * ftp://research.att.com/dist/mab/librand.shar
+- *
+ * Open Issues:
+ * - qop=auth-int (when streams and trailer support available)
+ * - nonce-format configurability
+ * - Proxy-Authorization-Info header is set by this module, but is
+ * currently ignored by mod_proxy (needs patch to mod_proxy)
+- * - generating the secret takes a while (~ 8 seconds) if using the
+- * truerand library
+ * - The source of the secret should be run-time directive (with server
+- * scope: RSRC_CONF). However, that could be tricky when trying to
+- * choose truerand vs. file...
++ * scope: RSRC_CONF)
+ * - shared-mem not completely tested yet. Seems to work ok for me,
+ * but... (definitely won't work on Windoze)
+ * - Sharing a realm among multiple servers has following problems:
+@@ -52,6 +45,8 @@
+ * captures a packet sent to one server and sends it to another
+ * one. Should we add "AuthDigestNcCheck Strict"?
+ * - expired nonces give amaya fits.
++ * - MD5-sess and auth-int are not yet implemented. An incomplete
++ * implementation has been removed and can be retrieved from svn history.
+ */
+
+ #include "apr_sha1.h"
+@@ -94,7 +89,6 @@ typedef struct digest_config_struct {
+ apr_array_header_t *qop_list;
+ apr_sha1_ctx_t nonce_ctx;
+ apr_time_t nonce_lifetime;
+- const char *nonce_format;
+ int check_nc;
+ const char *algorithm;
+ char *uri_list;
+@@ -112,7 +106,8 @@ typedef struct digest_config_struct {
+ #define NONCE_HASH_LEN (2*APR_SHA1_DIGESTSIZE)
+ #define NONCE_LEN (int )(NONCE_TIME_LEN + NONCE_HASH_LEN)
+
+-#define SECRET_LEN 20
++#define SECRET_LEN 20
++#define RETAINED_DATA_ID "mod_auth_digest"
+
+
+ /* client list definitions */
+@@ -121,7 +116,6 @@ typedef struct hash_entry {
+ unsigned long key; /* the key for this entry */
+ struct hash_entry *next; /* next entry in the bucket */
+ unsigned long nonce_count; /* for nonce-count checking */
+- char ha1[2*APR_MD5_DIGESTSIZE+1]; /* for algorithm=MD5-sess */
+ char last_nonce[NONCE_LEN+1]; /* for one-time nonce's */
+ } client_entry;
+
+@@ -170,7 +164,7 @@ typedef union time_union {
+ unsigned char arr[sizeof(apr_time_t)];
+ } time_rec;
+
+-static unsigned char secret[SECRET_LEN];
++static unsigned char *secret;
+
+ /* client-list, opaque, and one-time-nonce stuff */
+
+@@ -228,35 +222,11 @@ static apr_status_t cleanup_tables(void
+ return APR_SUCCESS;
+ }
+
+-static apr_status_t initialize_secret(server_rec *s)
+-{
+- apr_status_t status;
+-
+- ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, APLOGNO(01757)
+- "generating secret for digest authentication ...");
+-
+-#if APR_HAS_RANDOM
+- status = apr_generate_random_bytes(secret, sizeof(secret));
+-#else
+-#error APR random number support is missing; you probably need to install the truerand library.
+-#endif
+-
+- if (status != APR_SUCCESS) {
+- ap_log_error(APLOG_MARK, APLOG_CRIT, status, s, APLOGNO(01758)
+- "error generating secret");
+- return status;
+- }
+-
+- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01759) "done");
+-
+- return APR_SUCCESS;
+-}
+-
+ static void log_error_and_cleanup(char *msg, apr_status_t sts, server_rec *s)
+ {
+ ap_log_error(APLOG_MARK, APLOG_ERR, sts, s, APLOGNO(01760)
+- "%s - all nonce-count checking, one-time nonces, and "
+- "MD5-sess algorithm disabled", msg);
++ "%s - all nonce-count checking and one-time nonces"
++ "disabled", msg);
+
+ cleanup_tables(NULL);
+ }
+@@ -377,16 +347,32 @@ static int initialize_tables(server_rec
+ static int pre_init(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp)
+ {
+ apr_status_t rv;
++ void *retained;
+
+ rv = ap_mutex_register(pconf, client_mutex_type, NULL, APR_LOCK_DEFAULT, 0);
+- if (rv == APR_SUCCESS) {
+- rv = ap_mutex_register(pconf, opaque_mutex_type, NULL, APR_LOCK_DEFAULT,
+- 0);
+- }
+- if (rv != APR_SUCCESS) {
+- return rv;
+- }
++ if (rv != APR_SUCCESS)
++ return !OK;
++ rv = ap_mutex_register(pconf, opaque_mutex_type, NULL, APR_LOCK_DEFAULT, 0);
++ if (rv != APR_SUCCESS)
++ return !OK;
+
++ retained = ap_retained_data_get(RETAINED_DATA_ID);
++ if (retained == NULL) {
++ retained = ap_retained_data_create(RETAINED_DATA_ID, SECRET_LEN);
++ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, APLOGNO(01757)
++ "generating secret for digest authentication");
++#if APR_HAS_RANDOM
++ rv = apr_generate_random_bytes(retained, SECRET_LEN);
++#else
++#error APR random number support is missing
++#endif
++ if (rv != APR_SUCCESS) {
++ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL, APLOGNO(01758)
++ "error generating secret");
++ return !OK;
++ }
++ }
++ secret = retained;
+ return OK;
+ }
+
+@@ -399,10 +385,6 @@ static int initialize_module(apr_pool_t
+ if (ap_state_query(AP_SQ_MAIN_STATE) == AP_SQ_MS_CREATE_PRE_CONFIG)
+ return OK;
+
+- if (initialize_secret(s) != APR_SUCCESS) {
+- return !OK;
+- }
+-
+ #if APR_HAS_SHARED_MEMORY
+ /* Note: this stuff is currently fixed for the lifetime of the server,
+ * i.e. even across restarts. This means that A) any shmem-size
+@@ -483,6 +465,16 @@ static void *create_digest_dir_config(ap
+ static const char *set_realm(cmd_parms *cmd, void *config, const char *realm)
+ {
+ digest_config_rec *conf = (digest_config_rec *) config;
++#ifdef AP_DEBUG
++ int i;
++
++ /* check that we got random numbers */
++ for (i = 0; i < SECRET_LEN; i++) {
++ if (secret[i] != 0)
++ break;
++ }
++ ap_assert(i < SECRET_LEN);
++#endif
+
+ /* The core already handles the realm, but it's just too convenient to
+ * grab it ourselves too and cache some setups. However, we need to
+@@ -496,7 +488,7 @@ static const char *set_realm(cmd_parms *
+ * and directives outside a virtual host section)
+ */
+ apr_sha1_init(&conf->nonce_ctx);
+- apr_sha1_update_binary(&conf->nonce_ctx, secret, sizeof(secret));
++ apr_sha1_update_binary(&conf->nonce_ctx, secret, SECRET_LEN);
+ apr_sha1_update_binary(&conf->nonce_ctx, (const unsigned char *) realm,
+ strlen(realm));
+
+@@ -590,8 +582,7 @@ static const char *set_nonce_lifetime(cm
+ static const char *set_nonce_format(cmd_parms *cmd, void *config,
+ const char *fmt)
+ {
+- ((digest_config_rec *) config)->nonce_format = fmt;
+- return "AuthDigestNonceFormat is not implemented (yet)";
++ return "AuthDigestNonceFormat is not implemented";
+ }
+
+ static const char *set_nc_check(cmd_parms *cmd, void *config, int flag)
+@@ -612,7 +603,7 @@ static const char *set_algorithm(cmd_par
+ {
+ if (!strcasecmp(alg, "MD5-sess")) {
+ return "AuthDigestAlgorithm: ERROR: algorithm `MD5-sess' "
+- "is not fully implemented";
++ "is not implemented";
+ }
+ else if (strcasecmp(alg, "MD5")) {
+ return apr_pstrcat(cmd->pool, "Invalid algorithm in AuthDigestAlgorithm: ", alg, NULL);
+@@ -1138,7 +1129,7 @@ static const char *gen_nonce(apr_pool_t
+ static client_entry *gen_client(const request_rec *r)
+ {
+ unsigned long op;
+- client_entry new_entry = { 0, NULL, 0, "", "" }, *entry;
++ client_entry new_entry = { 0, NULL, 0, "" }, *entry;
+
+ if (!opaque_cntr) {
+ return NULL;
+@@ -1159,92 +1150,6 @@ static client_entry *gen_client(const re
+
+
+ /*
+- * MD5-sess code.
+- *
+- * If you want to use algorithm=MD5-sess you must write get_userpw_hash()
+- * yourself (see below). The dummy provided here just uses the hash from
+- * the auth-file, i.e. it is only useful for testing client implementations
+- * of MD5-sess .
+- */
+-
+-/*
+- * get_userpw_hash() will be called each time a new session needs to be
+- * generated and is expected to return the equivalent of
+- *
+- * h_urp = ap_md5(r->pool,
+- * apr_pstrcat(r->pool, username, ":", ap_auth_name(r), ":", passwd))
+- * ap_md5(r->pool,
+- * (unsigned char *) apr_pstrcat(r->pool, h_urp, ":", resp->nonce, ":",
+- * resp->cnonce, NULL));
+- *
+- * or put differently, it must return
+- *
+- * MD5(MD5(username ":" realm ":" password) ":" nonce ":" cnonce)
+- *
+- * If something goes wrong, the failure must be logged and NULL returned.
+- *
+- * You must implement this yourself, which will probably consist of code
+- * contacting the password server with the necessary information (typically
+- * the username, realm, nonce, and cnonce) and receiving the hash from it.
+- *
+- * TBD: This function should probably be in a separate source file so that
+- * people need not modify mod_auth_digest.c each time they install a new
+- * version of apache.
+- */
+-static const char *get_userpw_hash(const request_rec *r,
+- const digest_header_rec *resp,
+- const digest_config_rec *conf)
+-{
+- return ap_md5(r->pool,
+- (unsigned char *) apr_pstrcat(r->pool, conf->ha1, ":", resp->nonce,
+- ":", resp->cnonce, NULL));
+-}
+-
+-
+-/* Retrieve current session H(A1). If there is none and "generate" is
+- * true then a new session for MD5-sess is generated and stored in the
+- * client struct; if generate is false, or a new session could not be
+- * generated then NULL is returned (in case of failure to generate the
+- * failure reason will have been logged already).
+- */
+-static const char *get_session_HA1(const request_rec *r,
+- digest_header_rec *resp,
+- const digest_config_rec *conf,
+- int generate)
+-{
+- const char *ha1 = NULL;
+-
+- /* return the current sessions if there is one */
+- if (resp->opaque && resp->client && resp->client->ha1[0]) {
+- return resp->client->ha1;
+- }
+- else if (!generate) {
+- return NULL;
+- }
+-
+- /* generate a new session */
+- if (!resp->client) {
+- resp->client = gen_client(r);
+- }
+- if (resp->client) {
+- ha1 = get_userpw_hash(r, resp, conf);
+- if (ha1) {
+- memcpy(resp->client->ha1, ha1, sizeof(resp->client->ha1));
+- }
+- }
+-
+- return ha1;
+-}
+-
+-
+-static void clear_session(const digest_header_rec *resp)
+-{
+- if (resp->client) {
+- resp->client->ha1[0] = '\0';
+- }
+-}
+-
+-/*
+ * Authorization challenge generation code (for WWW-Authenticate)
+ */
+
+@@ -1282,8 +1187,7 @@ static void note_digest_auth_failure(req
+
+ if (resp->opaque == NULL) {
+ /* new client */
+- if ((conf->check_nc || conf->nonce_lifetime == 0
+- || !strcasecmp(conf->algorithm, "MD5-sess"))
++ if ((conf->check_nc || conf->nonce_lifetime == 0)
+ && (resp->client = gen_client(r)) != NULL) {
+ opaque = ltox(r->pool, resp->client->key);
+ }
+@@ -1323,15 +1227,6 @@ static void note_digest_auth_failure(req
+ memcpy(resp->client->last_nonce, nonce, NONCE_LEN+1);
+ }
+
+- /* Setup MD5-sess stuff. Note that we just clear out the session
+- * info here, since we can't generate a new session until the request
+- * from the client comes in with the cnonce.
+- */
+-
+- if (!strcasecmp(conf->algorithm, "MD5-sess")) {
+- clear_session(resp);
+- }
+-
+ /* setup domain attribute. We want to send this attribute wherever
+ * possible so that the client won't send the Authorization header
+ * unnecessarily (it's usually > 200 bytes!).
+@@ -1597,24 +1492,9 @@ static const char *new_digest(const requ
+ {
+ const char *ha1, *ha2, *a2;
+
+- if (resp->algorithm && !strcasecmp(resp->algorithm, "MD5-sess")) {
+- ha1 = get_session_HA1(r, resp, conf, 1);
+- if (!ha1) {
+- return NULL;
+- }
+- }
+- else {
+- ha1 = conf->ha1;
+- }
++ ha1 = conf->ha1;
+
+- if (resp->message_qop && !strcasecmp(resp->message_qop, "auth-int")) {
+- a2 = apr_pstrcat(r->pool, resp->method, ":", resp->uri, ":",
+- ap_md5(r->pool, (const unsigned char*) ""), NULL);
+- /* TBD */
+- }
+- else {
+- a2 = apr_pstrcat(r->pool, resp->method, ":", resp->uri, NULL);
+- }
++ a2 = apr_pstrcat(r->pool, resp->method, ":", resp->uri, NULL);
+ ha2 = ap_md5(r->pool, (const unsigned char *)a2);
+
+ return ap_md5(r->pool,
+@@ -1862,8 +1742,7 @@ static int authenticate_digest_user(requ
+ }
+
+ if (resp->algorithm != NULL
+- && strcasecmp(resp->algorithm, "MD5")
+- && strcasecmp(resp->algorithm, "MD5-sess")) {
++ && strcasecmp(resp->algorithm, "MD5")) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01789)
+ "unknown algorithm `%s' received: %s",
+ resp->algorithm, r->uri);
+@@ -2015,27 +1894,9 @@ static int add_auth_info(request_rec *r)
+
+ /* calculate rspauth attribute
+ */
+- if (resp->algorithm && !strcasecmp(resp->algorithm, "MD5-sess")) {
+- ha1 = get_session_HA1(r, resp, conf, 0);
+- if (!ha1) {
+- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01795)
+- "internal error: couldn't find session "
+- "info for user %s", resp->username);
+- return !OK;
+- }
+- }
+- else {
+- ha1 = conf->ha1;
+- }
++ ha1 = conf->ha1;
+
+- if (resp->message_qop && !strcasecmp(resp->message_qop, "auth-int")) {
+- a2 = apr_pstrcat(r->pool, ":", resp->uri, ":",
+- ap_md5(r->pool,(const unsigned char *) ""), NULL);
+- /* TBD */
+- }
+- else {
+- a2 = apr_pstrcat(r->pool, ":", resp->uri, NULL);
+- }
++ a2 = apr_pstrcat(r->pool, ":", resp->uri, NULL);
+ ha2 = ap_md5(r->pool, (const unsigned char *)a2);
+
+ resp_dig = ap_md5(r->pool,
diff --git a/debian/patches/series b/debian/patches/series
index 24e3f68..bdd4606 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -37,3 +37,4 @@ CVE-2017-15715-regex-line-endings.diff
CVE-2018-1283-mod_session.diff
CVE-2018-1301-HTTP-request-read-out-of-bounds.diff
CVE-2018-1303-mod_cache_socache-oob.diff
+CVE-2018-1312-mod_auth_digest-nonce.diff
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-apache/apache2.git
More information about the Pkg-apache-commits
mailing list