|
|
f9ac4e |
From 96a8f82b0b9a84a7ba7ba84965978ab27674b802 Mon Sep 17 00:00:00 2001
|
|
|
f9ac4e |
From: Simo Sorce <simo@redhat.com>
|
|
|
f9ac4e |
Date: Mon, 24 Apr 2017 15:40:33 -0400
|
|
|
f9ac4e |
Subject: [PATCH] Allow admins to selectively suppress negotiation
|
|
|
f9ac4e |
|
|
|
f9ac4e |
If the admin sets the gssapi-no-negotiate requets enironemnt variable,
|
|
|
f9ac4e |
then we suppress the ability to send Negotiate headers.
|
|
|
f9ac4e |
This is useful to slectively send negotiate only to specific whielisted
|
|
|
f9ac4e |
or blacklisted browsers, clients, IP Addresses, etc... based on
|
|
|
f9ac4e |
directives like BrowserMatch or SetEnvIf.
|
|
|
f9ac4e |
|
|
|
f9ac4e |
Signed-off-by: Simo Sorce <simo@redhat.com>
|
|
|
f9ac4e |
Resolves #135
|
|
|
f9ac4e |
(cherry picked from commit 114e4408523ca4d06da32c265680b9faa74ad882)
|
|
|
f9ac4e |
---
|
|
|
f9ac4e |
src/mod_auth_gssapi.c | 13 ++++++++++---
|
|
|
f9ac4e |
tests/httpd.conf | 13 +++++++++++++
|
|
|
f9ac4e |
tests/magtests.py | 19 +++++++++++++++++++
|
|
|
f9ac4e |
tests/t_nonego.py | 29 +++++++++++++++++++++++++++++
|
|
|
f9ac4e |
4 files changed, 71 insertions(+), 3 deletions(-)
|
|
|
f9ac4e |
create mode 100755 tests/t_nonego.py
|
|
|
f9ac4e |
|
|
|
f9ac4e |
diff --git a/src/mod_auth_gssapi.c b/src/mod_auth_gssapi.c
|
|
|
f9ac4e |
index 755654d..59120d1 100644
|
|
|
f9ac4e |
--- a/src/mod_auth_gssapi.c
|
|
|
f9ac4e |
+++ b/src/mod_auth_gssapi.c
|
|
|
f9ac4e |
@@ -833,7 +833,7 @@ static int mag_auth(request_rec *req)
|
|
|
f9ac4e |
gss_OID_set desired_mechs = GSS_C_NO_OID_SET;
|
|
|
f9ac4e |
struct mag_conn *mc = NULL;
|
|
|
f9ac4e |
int i;
|
|
|
f9ac4e |
- bool send_auth_header = true;
|
|
|
f9ac4e |
+ bool send_nego_header = true;
|
|
|
f9ac4e |
|
|
|
f9ac4e |
type = ap_auth_type(req);
|
|
|
f9ac4e |
if ((type == NULL) || (strcasecmp(type, "GSSAPI") != 0)) {
|
|
|
f9ac4e |
@@ -907,6 +907,11 @@ static int mag_auth(request_rec *req)
|
|
|
f9ac4e |
}
|
|
|
f9ac4e |
}
|
|
|
f9ac4e |
|
|
|
f9ac4e |
+ /* check if admin wants to disable negotiate with this client */
|
|
|
f9ac4e |
+ if (apr_table_get(req->subprocess_env, "gssapi-no-negotiate")) {
|
|
|
f9ac4e |
+ send_nego_header = false;
|
|
|
f9ac4e |
+ }
|
|
|
f9ac4e |
+
|
|
|
f9ac4e |
if (cfg->ssl_only) {
|
|
|
f9ac4e |
if (!mag_conn_is_https(req->connection)) {
|
|
|
f9ac4e |
mag_post_error(req, cfg, MAG_AUTH_NOT_ALLOWED, 0, 0,
|
|
|
f9ac4e |
@@ -965,7 +970,9 @@ static int mag_auth(request_rec *req)
|
|
|
f9ac4e |
}
|
|
|
f9ac4e |
|
|
|
f9ac4e |
/* We got auth header, sending auth header would mean re-auth */
|
|
|
f9ac4e |
- send_auth_header = !cfg->negotiate_once;
|
|
|
f9ac4e |
+ if (cfg->negotiate_once) {
|
|
|
f9ac4e |
+ send_nego_header = false;
|
|
|
f9ac4e |
+ }
|
|
|
f9ac4e |
|
|
|
f9ac4e |
for (i = 0; auth_types[i] != NULL; i++) {
|
|
|
f9ac4e |
if (strcasecmp(auth_header_type, auth_types[i]) == 0) {
|
|
|
f9ac4e |
@@ -1126,7 +1133,7 @@ done:
|
|
|
f9ac4e |
apr_table_add(req->err_headers_out, req_cfg->rep_proto, reply);
|
|
|
f9ac4e |
}
|
|
|
f9ac4e |
} else if (ret == HTTP_UNAUTHORIZED) {
|
|
|
f9ac4e |
- if (send_auth_header) {
|
|
|
f9ac4e |
+ if (send_nego_header) {
|
|
|
f9ac4e |
apr_table_add(req->err_headers_out,
|
|
|
f9ac4e |
req_cfg->rep_proto, "Negotiate");
|
|
|
f9ac4e |
if (is_mech_allowed(desired_mechs, gss_mech_ntlmssp,
|
|
|
f9ac4e |
diff --git a/tests/httpd.conf b/tests/httpd.conf
|
|
|
f9ac4e |
index 7879727..e17cf0a 100644
|
|
|
f9ac4e |
--- a/tests/httpd.conf
|
|
|
f9ac4e |
+++ b/tests/httpd.conf
|
|
|
f9ac4e |
@@ -211,6 +211,19 @@ CoreDumpDirectory "${HTTPROOT}"
|
|
|
f9ac4e |
Require valid-user
|
|
|
f9ac4e |
</Location>
|
|
|
f9ac4e |
|
|
|
f9ac4e |
+<Location /nonego>
|
|
|
f9ac4e |
+ BrowserMatch NONEGO gssapi-no-negotiate
|
|
|
f9ac4e |
+ AuthType GSSAPI
|
|
|
f9ac4e |
+ AuthName "Login"
|
|
|
f9ac4e |
+ GssapiSSLonly Off
|
|
|
f9ac4e |
+ GssapiCredStore ccache:${HTTPROOT}/tmp/httpd_krb5_ccache
|
|
|
f9ac4e |
+ GssapiCredStore client_keytab:${HTTPROOT}/http.keytab
|
|
|
f9ac4e |
+ GssapiCredStore keytab:${HTTPROOT}/http.keytab
|
|
|
f9ac4e |
+ GssapiBasicAuth On
|
|
|
f9ac4e |
+ GssapiAllowedMech krb5
|
|
|
f9ac4e |
+ Require valid-user
|
|
|
f9ac4e |
+</Location>
|
|
|
f9ac4e |
+
|
|
|
f9ac4e |
<VirtualHost *:${PROXYPORT}>
|
|
|
f9ac4e |
ProxyRequests On
|
|
|
f9ac4e |
ProxyVia On
|
|
|
f9ac4e |
diff --git a/tests/magtests.py b/tests/magtests.py
|
|
|
f9ac4e |
index a008d81..4d276df 100755
|
|
|
f9ac4e |
--- a/tests/magtests.py
|
|
|
f9ac4e |
+++ b/tests/magtests.py
|
|
|
f9ac4e |
@@ -410,6 +410,23 @@ def test_bad_acceptor_name(testdir, testenv, testlog):
|
|
|
f9ac4e |
sys.stderr.write('BAD ACCEPTOR: FAILED\n')
|
|
|
f9ac4e |
|
|
|
f9ac4e |
|
|
|
f9ac4e |
+def test_no_negotiate(testdir, testenv, testlog):
|
|
|
f9ac4e |
+
|
|
|
f9ac4e |
+ nonego_dir = os.path.join(testdir, 'httpd', 'html', 'nonego')
|
|
|
f9ac4e |
+ os.mkdir(nonego_dir)
|
|
|
f9ac4e |
+ shutil.copy('tests/index.html', nonego_dir)
|
|
|
f9ac4e |
+
|
|
|
f9ac4e |
+ with (open(testlog, 'a')) as logfile:
|
|
|
f9ac4e |
+ spnego = subprocess.Popen(["tests/t_nonego.py"],
|
|
|
f9ac4e |
+ stdout=logfile, stderr=logfile,
|
|
|
f9ac4e |
+ env=testenv, preexec_fn=os.setsid)
|
|
|
f9ac4e |
+ spnego.wait()
|
|
|
f9ac4e |
+ if spnego.returncode != 0:
|
|
|
f9ac4e |
+ sys.stderr.write('NO Negotiate: FAILED\n')
|
|
|
f9ac4e |
+ else:
|
|
|
f9ac4e |
+ sys.stderr.write('NO Negotiate: SUCCESS\n')
|
|
|
f9ac4e |
+
|
|
|
f9ac4e |
+
|
|
|
f9ac4e |
if __name__ == '__main__':
|
|
|
f9ac4e |
|
|
|
f9ac4e |
args = parse_args()
|
|
|
f9ac4e |
@@ -454,6 +471,8 @@ if __name__ == '__main__':
|
|
|
f9ac4e |
testenv.update(kdcenv)
|
|
|
f9ac4e |
test_basic_auth_krb5(testdir, testenv, testlog)
|
|
|
f9ac4e |
|
|
|
f9ac4e |
+ test_no_negotiate(testdir, testenv, testlog)
|
|
|
f9ac4e |
+
|
|
|
f9ac4e |
finally:
|
|
|
f9ac4e |
with (open(testlog, 'a')) as logfile:
|
|
|
f9ac4e |
for name in processes:
|
|
|
f9ac4e |
diff --git a/tests/t_nonego.py b/tests/t_nonego.py
|
|
|
f9ac4e |
new file mode 100755
|
|
|
f9ac4e |
index 0000000..c4f2bdd
|
|
|
f9ac4e |
--- /dev/null
|
|
|
f9ac4e |
+++ b/tests/t_nonego.py
|
|
|
f9ac4e |
@@ -0,0 +1,29 @@
|
|
|
f9ac4e |
+#!/usr/bin/python
|
|
|
f9ac4e |
+# Copyright (C) 2015 - mod_auth_gssapi contributors, see COPYING for license.
|
|
|
f9ac4e |
+
|
|
|
f9ac4e |
+import os
|
|
|
f9ac4e |
+import requests
|
|
|
f9ac4e |
+
|
|
|
f9ac4e |
+
|
|
|
f9ac4e |
+if __name__ == '__main__':
|
|
|
f9ac4e |
+ url = 'http://%s/nonego/' % (os.environ['NSS_WRAPPER_HOSTNAME'])
|
|
|
f9ac4e |
+
|
|
|
f9ac4e |
+ # ensure a 401 with the appropriate WWW-Authenticate header is returned
|
|
|
f9ac4e |
+ # when no auth is provided
|
|
|
f9ac4e |
+ r = requests.get(url)
|
|
|
f9ac4e |
+ if r.status_code != 401:
|
|
|
f9ac4e |
+ raise ValueError('NO Negotiate failed - 401 expected')
|
|
|
f9ac4e |
+ if not (r.headers.get("WWW-Authenticate") and
|
|
|
f9ac4e |
+ r.headers.get("WWW-Authenticate").startswith("Negotiate")):
|
|
|
f9ac4e |
+ raise ValueError('NO Negotiate failed - WWW-Authenticate '
|
|
|
f9ac4e |
+ 'Negotiate header is absent')
|
|
|
f9ac4e |
+
|
|
|
f9ac4e |
+ # ensure a 401 with the WWW-Authenticate Negotiate header is absent
|
|
|
f9ac4e |
+ # when the special User-Agent is sent
|
|
|
f9ac4e |
+ r = requests.get(url, headers={'User-Agent': 'NONEGO'})
|
|
|
f9ac4e |
+ if r.status_code != 401:
|
|
|
f9ac4e |
+ raise ValueError('NO Negotiate failed - 401 expected')
|
|
|
f9ac4e |
+ if (r.headers.get("WWW-Authenticate") and
|
|
|
f9ac4e |
+ r.headers.get("WWW-Authenticate").startswith("Negotiate")):
|
|
|
f9ac4e |
+ raise ValueError('NO Negotiate failed - WWW-Authenticate '
|
|
|
f9ac4e |
+ 'Negotiate header is present, should be absent')
|