diff --git a/.gitignore b/.gitignore index 260a2d6..9969f1d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ SOURCES/httpd-2.4.6.tar.bz2 -SOURCES/centos-noindex.tar.gz diff --git a/.httpd.metadata b/.httpd.metadata index 17ede1b..d335a99 100644 --- a/.httpd.metadata +++ b/.httpd.metadata @@ -1,2 +1 @@ 16d8ec72535ded65d035122b0d944b0e64eaa2a2 SOURCES/httpd-2.4.6.tar.bz2 -6ce5ab3c765b9efeceb2e636e32373bc6e6ed489 SOURCES/centos-noindex.tar.gz diff --git a/SOURCES/httpd-2.4.6-CVE-2018-1312.patch b/SOURCES/httpd-2.4.6-CVE-2018-1312.patch new file mode 100644 index 0000000..4c694a5 --- /dev/null +++ b/SOURCES/httpd-2.4.6-CVE-2018-1312.patch @@ -0,0 +1,399 @@ +diff --git a/modules/aaa/mod_auth_digest.c b/modules/aaa/mod_auth_digest.c +index cbb4434..b50bcf9 100644 +--- a/modules/aaa/mod_auth_digest.c ++++ b/modules/aaa/mod_auth_digest.c +@@ -26,20 +26,13 @@ + * reports to the Apache bug-database, or send them directly to me + * at ronald@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 *not_used) + 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 *s, apr_pool_t *ctx) + 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 *p, apr_pool_t *plog, + 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(apr_pool_t *p, char *dir) + 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 *cmd, void *config, const char *realm) + * 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(cmd_parms *cmd, void *config, + 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_parms *cmd, void *config, const char *alg) + { + 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 *p, apr_time_t now, const char *opaque, + 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; +@@ -1158,92 +1149,6 @@ static client_entry *gen_client(const request_rec *r) + } + + +-/* +- * 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(request_rec *r, + + 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(request_rec *r, + 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 request_rec *r, + { + 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, +@@ -1854,8 +1734,7 @@ static int authenticate_digest_user(request_rec *r) + } + + 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); +@@ -2007,27 +1886,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/SOURCES/welcome.conf b/SOURCES/welcome.conf index c1b6c11..5d1e452 100644 --- a/SOURCES/welcome.conf +++ b/SOURCES/welcome.conf @@ -16,7 +16,3 @@ Alias /.noindex.html /usr/share/httpd/noindex/index.html -Alias /noindex/css/bootstrap.min.css /usr/share/httpd/noindex/css/bootstrap.min.css -Alias /noindex/css/open-sans.css /usr/share/httpd/noindex/css/open-sans.css -Alias /images/apache_pb.gif /usr/share/httpd/noindex/images/apache_pb.gif -Alias /images/poweredby.png /usr/share/httpd/noindex/images/poweredby.png diff --git a/SPECS/httpd.spec b/SPECS/httpd.spec index 928ee7c..38eab8f 100644 --- a/SPECS/httpd.spec +++ b/SPECS/httpd.spec @@ -4,7 +4,7 @@ %define mmn 20120211 %define oldmmnisa %{mmn}-%{__isa_name}-%{__isa_bits} %define mmnisa %{mmn}%{__isa_name}%{__isa_bits} -%define vstring CentOS +%define vstring %(source /etc/os-release; echo ${REDHAT_SUPPORT_PRODUCT}) # Drop automatic provides for module DSOs %{?filter_setup: @@ -15,10 +15,10 @@ Summary: Apache HTTP Server Name: httpd Version: 2.4.6 -Release: 89%{?dist} +Release: 89%{?dist}.1 URL: http://httpd.apache.org/ Source0: http://www.apache.org/dist/httpd/httpd-%{version}.tar.bz2 -Source1: centos-noindex.tar.gz +Source1: index.html Source2: httpd.logrotate Source3: httpd.sysconf Source4: httpd-ssl-pass-dialog @@ -214,6 +214,7 @@ Patch217: httpd-2.4.6-CVE-2017-7668.patch Patch218: httpd-2.4.6-CVE-2017-7679.patch Patch219: httpd-2.4.6-CVE-2017-9788.patch Patch220: httpd-2.4.6-CVE-2017-9798.patch +Patch221: httpd-2.4.6-CVE-2018-1312.patch License: ASL 2.0 Group: System Environment/Daemons @@ -458,6 +459,7 @@ rm modules/ssl/ssl_engine_dh.c %patch218 -p1 -b .cve7679 %patch219 -p1 -b .cve9788 %patch220 -p1 -b .cve9798 +%patch221 -p1 -b .cve1312 # Patch in the vendor string and the release string sed -i '/^#define PLATFORM/s/Unix/%{vstring}/' os/unix/os.h @@ -611,10 +613,8 @@ EOF # Handle contentdir mkdir $RPM_BUILD_ROOT%{contentdir}/noindex -tar xzf $RPM_SOURCE_DIR/centos-noindex.tar.gz \ - -C $RPM_BUILD_ROOT%{contentdir}/noindex/ \ - --strip-components=1 - +install -m 644 -p $RPM_SOURCE_DIR/index.html \ + $RPM_BUILD_ROOT%{contentdir}/noindex/index.html rm -rf %{contentdir}/htdocs # remove manual sources @@ -637,7 +637,7 @@ rm -v $RPM_BUILD_ROOT%{docroot}/html/*.html \ $RPM_BUILD_ROOT%{docroot}/cgi-bin/* # Symlink for the powered-by-$DISTRO image: -ln -s ../noindex/images/poweredby.png \ +ln -s ../../pixmaps/poweredby.png \ $RPM_BUILD_ROOT%{contentdir}/icons/poweredby.png # symlinks for /etc/httpd @@ -823,7 +823,7 @@ rm -rf $RPM_BUILD_ROOT %{contentdir}/error/README %{contentdir}/error/*.var %{contentdir}/error/include/*.html -%{contentdir}/noindex/* +%{contentdir}/noindex/index.html %dir %{docroot} %dir %{docroot}/cgi-bin @@ -889,11 +889,9 @@ rm -rf $RPM_BUILD_ROOT %{_sysconfdir}/rpm/macros.httpd %changelog -* Wed Apr 24 2019 CentOS Sources - 2.4.6-89.el7.centos -- Remove index.html, add centos-noindex.tar.gz -- change vstring -- change symlink for poweredby.png -- update welcome.conf with proper aliases +* Tue Jun 25 2019 Lubos Uhliarik - 2.4.6-89.1 +- Resolves: #1719722 - CVE-2018-1312 httpd: Weak Digest auth nonce generation + in mod_auth_digest * Fri Mar 15 2019 Joe Orton - 2.4.6-89 - fix per-request leak of bucket brigade structure (#1583218)