Backport of:

From 78f0f0b6585f13ec1175c7020ee01cd0237fc1ba Mon Sep 17 00:00:00 2001
From: Jim Jagielski <jim@apache.org>
Date: Tue, 30 May 2017 12:27:41 +0000
Subject: [PATCH] Merge r1796348 from trunk:

core: deprecate and replace ap_get_basic_auth_pw

  *) core: Deprecate ap_get_basic_auth_pw() and add
    ap_get_basic_auth_components().

Submitted By: Emmanuel Dreyfus <manu netbsd.org>, Jacob Champion, Eric Covener



Submitted by: covener
Reviewed by: covener, ylavic, jim


git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1796855 13f79535-47bb-0310-9956-ffa450edef68
---
 CHANGES                 |  4 ++++
 STATUS                  |  6 ------
 include/ap_mmn.h        |  4 +++-
 include/http_protocol.h | 25 ++++++++++++++++++++++++-
 server/protocol.c       | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
 server/request.c        | 17 ++++++++++++++---
 6 files changed, 93 insertions(+), 11 deletions(-)

#diff --git a/CHANGES b/CHANGES
#index 1b815557a37..9d7fe8e379b 100644
#--- a/CHANGES
#+++ b/CHANGES
#@@ -2,6 +2,10 @@
# 
# Changes with Apache 2.4.26
# 
#+  *) core: Deprecate ap_get_basic_auth_pw() and add
#+     ap_get_basic_auth_components().
#+     [Emmanuel Dreyfus <manu netbsd.org>, Jacob Champion, Eric Covener]
#+
#   *) mod_rewrite: When a substitution is a fully qualified URL, and the 
#      scheme/host/port matches the current virtual host, stop interpreting the 
#      path component as a local path just because the first component of the 
#diff --git a/STATUS b/STATUS
#index 1ce64c6b43b..456596a6160 100644
#--- a/STATUS
#+++ b/STATUS
#@@ -120,12 +120,6 @@ RELEASE SHOWSTOPPERS:
# PATCHES ACCEPTED TO BACKPORT FROM TRUNK:
#   [ start all new proposals below, under PATCHES PROPOSED. ]
# 
#-  *) core: Deprecate ap_get_basic_auth_pw() and add 
#-     ap_get_basic_auth_components(). 
#-     trunk patch: http://svn.apache.org/r1796348
#-     2.4.x patch: http://people.apache.org/~covener/patches/httpd-2.4.x-ap_get_basic_auth_pw.diff + CHANGES
#-     +1: covener, ylavic, jim
#-
#   *) core: Terminate token processing on NULL.
#      trunk patch: http://svn.apache.org/r1796350
#      2.4.x patch: svn merge -c 1796350 ^/httpd/httpd/trunk .
Index: apache2-2.4.7/include/ap_mmn.h
===================================================================
--- apache2-2.4.7.orig/include/ap_mmn.h	2017-06-26 07:58:36.697632178 -0400
+++ apache2-2.4.7/include/ap_mmn.h	2017-06-26 07:58:56.297842089 -0400
@@ -422,6 +422,8 @@
  * 20120211.25 (2.4.7-dev) Add conn_sense_e
  * 20120211.26 (2.4.7-dev) Add util_fcgi.h, FastCGI protocol support
  * 20120211.27 (2.4.7-dev) Add ap_podx_restart_t and ap_mpm_podx_*
+ * 20120211.68 (2.4.26-dev) Add ap_get_basic_auth_components() and deprecate
+ *                          ap_get_basic_auth_pw()
  */
 
 #define MODULE_MAGIC_COOKIE 0x41503234UL /* "AP24" */
Index: apache2-2.4.7/include/http_protocol.h
===================================================================
--- apache2-2.4.7.orig/include/http_protocol.h	2017-06-26 07:58:36.697632178 -0400
+++ apache2-2.4.7/include/http_protocol.h	2017-06-26 07:58:36.653631707 -0400
@@ -541,7 +541,11 @@ AP_DECLARE(void) ap_note_digest_auth_fai
 AP_DECLARE_HOOK(int, note_auth_failure, (request_rec *r, const char *auth_type))
 
 /**
- * Get the password from the request headers
+ * Get the password from the request headers. This function has multiple side
+ * effects due to its prior use in the old authentication framework.
+ * ap_get_basic_auth_components() should be preferred.
+ *
+ * @deprecated @see ap_get_basic_auth_components
  * @param r The current request
  * @param pw The password as set in the headers
  * @return 0 (OK) if it set the 'pw' argument (and assured
@@ -554,6 +558,25 @@ AP_DECLARE_HOOK(int, note_auth_failure,
  */
 AP_DECLARE(int) ap_get_basic_auth_pw(request_rec *r, const char **pw);
 
+#define AP_GET_BASIC_AUTH_PW_NOTE "AP_GET_BASIC_AUTH_PW_NOTE"
+
+/**
+ * Get the username and/or password from the request's Basic authentication
+ * headers. Unlike ap_get_basic_auth_pw(), calling this function has no side
+ * effects on the passed request_rec.
+ *
+ * @param r The current request
+ * @param username If not NULL, set to the username sent by the client
+ * @param password If not NULL, set to the password sent by the client
+ * @return APR_SUCCESS if the credentials were successfully parsed and returned;
+ *         APR_EINVAL if there was no authentication header sent or if the
+ *         client was not using the Basic authentication scheme. username and
+ *         password are unchanged on failure.
+ */
+AP_DECLARE(apr_status_t) ap_get_basic_auth_components(const request_rec *r,
+                                                      const char **username,
+                                                      const char **password);
+
 /**
  * parse_uri: break apart the uri
  * @warning Side Effects:
Index: apache2-2.4.7/server/protocol.c
===================================================================
--- apache2-2.4.7.orig/server/protocol.c	2017-06-26 07:58:36.697632178 -0400
+++ apache2-2.4.7/server/protocol.c	2017-06-26 07:58:36.657631749 -0400
@@ -1564,6 +1564,7 @@ AP_DECLARE(int) ap_get_basic_auth_pw(req
 
     t = ap_pbase64decode(r->pool, auth_line);
     r->user = ap_getword_nulls (r->pool, &t, ':');
+    apr_table_setn(r->notes, AP_GET_BASIC_AUTH_PW_NOTE, "1");
     r->ap_auth_type = "Basic";
 
     *pw = t;
@@ -1571,6 +1572,53 @@ AP_DECLARE(int) ap_get_basic_auth_pw(req
     return OK;
 }
 
+AP_DECLARE(apr_status_t) ap_get_basic_auth_components(const request_rec *r,
+                                                      const char **username,
+                                                      const char **password)
+{
+    const char *auth_header;
+    const char *credentials;
+    const char *decoded;
+    const char *user;
+
+    auth_header = (PROXYREQ_PROXY == r->proxyreq) ? "Proxy-Authorization"
+                                                  : "Authorization";
+    credentials = apr_table_get(r->headers_in, auth_header);
+
+    if (!credentials) {
+        /* No auth header. */
+        return APR_EINVAL;
+    }
+
+    if (strcasecmp(ap_getword(r->pool, &credentials, ' '), "Basic")) {
+        /* These aren't Basic credentials. */
+        return APR_EINVAL;
+    }
+
+    while (*credentials == ' ' || *credentials == '\t') {
+        credentials++;
+    }
+
+    /* XXX Our base64 decoding functions don't actually error out if the string
+     * we give it isn't base64; they'll just silently stop and hand us whatever
+     * they've parsed up to that point.
+     *
+     * Since this function is supposed to be a drop-in replacement for the
+     * deprecated ap_get_basic_auth_pw(), don't fix this for 2.4.x.
+     */
+    decoded = ap_pbase64decode(r->pool, credentials);
+    user = ap_getword_nulls(r->pool, &decoded, ':');
+
+    if (username) {
+        *username = user;
+    }
+    if (password) {
+        *password = decoded;
+    }
+
+    return APR_SUCCESS;
+}
+
 struct content_length_ctx {
     int data_sent;  /* true if the C-L filter has already sent at
                      * least one bucket on to the next output filter
Index: apache2-2.4.7/server/request.c
===================================================================
--- apache2-2.4.7.orig/server/request.c	2017-06-26 07:58:36.697632178 -0400
+++ apache2-2.4.7/server/request.c	2017-06-26 07:58:36.661631792 -0400
@@ -124,6 +124,8 @@ static int decl_die(int status, const ch
 AP_DECLARE(int) ap_some_authn_required(request_rec *r)
 {
     int access_status;
+    char *olduser = r->user;
+    int rv = FALSE;
 
     switch (ap_satisfies(r)) {
     case SATISFY_ALL:
@@ -134,7 +136,7 @@ AP_DECLARE(int) ap_some_authn_required(r
 
         access_status = ap_run_access_checker_ex(r);
         if (access_status == DECLINED) {
-            return TRUE;
+            rv = TRUE;
         }
 
         break;
@@ -145,13 +147,14 @@ AP_DECLARE(int) ap_some_authn_required(r
 
         access_status = ap_run_access_checker_ex(r);
         if (access_status == DECLINED) {
-            return TRUE;
+            rv = TRUE;
         }
 
         break;
     }
 
-    return FALSE;
+    r->user = olduser;
+    return rv;
 }
 
 /* This is the master logic for processing requests.  Do NOT duplicate
@@ -259,6 +262,14 @@ AP_DECLARE(int) ap_process_request_inter
         r->ap_auth_type = r->main->ap_auth_type;
     }
     else {
+        /* A module using a confusing API (ap_get_basic_auth_pw) caused
+        ** r->user to be filled out prior to check_authn hook. We treat
+        ** it is inadvertent.
+        */
+        if (r->user && apr_table_get(r->notes, AP_GET_BASIC_AUTH_PW_NOTE)) { 
+            r->user = NULL;
+        }
+
         switch (ap_satisfies(r)) {
         case SATISFY_ALL:
         case SATISFY_NOSPEC:
