Skip to content

Commit 114e440

Browse files
committed
Allow admins to selectively suppress negotiation
If the admin sets the gssapi-no-negotiate requets enironemnt variable, then we suppress the ability to send Negotiate headers. This is useful to slectively send negotiate only to specific whielisted or blacklisted browsers, clients, IP Addresses, etc... based on directives like BrowserMatch or SetEnvIf. Signed-off-by: Simo Sorce <simo@redhat.com> Resolves #135
1 parent 12ebe14 commit 114e440

File tree

4 files changed

+71
-3
lines changed

4 files changed

+71
-3
lines changed

src/mod_auth_gssapi.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -833,7 +833,7 @@ static int mag_auth(request_rec *req)
833833
gss_OID_set desired_mechs = GSS_C_NO_OID_SET;
834834
struct mag_conn *mc = NULL;
835835
int i;
836-
bool send_auth_header = true;
836+
bool send_nego_header = true;
837837

838838
type = ap_auth_type(req);
839839
if ((type == NULL) || (strcasecmp(type, "GSSAPI") != 0)) {
@@ -907,6 +907,11 @@ static int mag_auth(request_rec *req)
907907
}
908908
}
909909

910+
/* check if admin wants to disable negotiate with this client */
911+
if (apr_table_get(req->subprocess_env, "gssapi-no-negotiate")) {
912+
send_nego_header = false;
913+
}
914+
910915
if (cfg->ssl_only) {
911916
if (!mag_conn_is_https(req->connection)) {
912917
mag_post_error(req, cfg, MAG_AUTH_NOT_ALLOWED, 0, 0,
@@ -965,7 +970,9 @@ static int mag_auth(request_rec *req)
965970
}
966971

967972
/* We got auth header, sending auth header would mean re-auth */
968-
send_auth_header = !cfg->negotiate_once;
973+
if (cfg->negotiate_once) {
974+
send_nego_header = false;
975+
}
969976

970977
for (i = 0; auth_types[i] != NULL; i++) {
971978
if (strcasecmp(auth_header_type, auth_types[i]) == 0) {
@@ -1126,7 +1133,7 @@ static int mag_auth(request_rec *req)
11261133
apr_table_add(req->err_headers_out, req_cfg->rep_proto, reply);
11271134
}
11281135
} else if (ret == HTTP_UNAUTHORIZED) {
1129-
if (send_auth_header) {
1136+
if (send_nego_header) {
11301137
apr_table_add(req->err_headers_out,
11311138
req_cfg->rep_proto, "Negotiate");
11321139
if (is_mech_allowed(desired_mechs, gss_mech_ntlmssp,

tests/httpd.conf

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,19 @@ CoreDumpDirectory "${HTTPROOT}"
211211
Require valid-user
212212
</Location>
213213

214+
<Location /nonego>
215+
BrowserMatch NONEGO gssapi-no-negotiate
216+
AuthType GSSAPI
217+
AuthName "Login"
218+
GssapiSSLonly Off
219+
GssapiCredStore ccache:${HTTPROOT}/tmp/httpd_krb5_ccache
220+
GssapiCredStore client_keytab:${HTTPROOT}/http.keytab
221+
GssapiCredStore keytab:${HTTPROOT}/http.keytab
222+
GssapiBasicAuth On
223+
GssapiAllowedMech krb5
224+
Require valid-user
225+
</Location>
226+
214227
<VirtualHost *:${PROXYPORT}>
215228
ProxyRequests On
216229
ProxyVia On

tests/magtests.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,23 @@ def test_bad_acceptor_name(testdir, testenv, testlog):
410410
sys.stderr.write('BAD ACCEPTOR: FAILED\n')
411411

412412

413+
def test_no_negotiate(testdir, testenv, testlog):
414+
415+
nonego_dir = os.path.join(testdir, 'httpd', 'html', 'nonego')
416+
os.mkdir(nonego_dir)
417+
shutil.copy('tests/index.html', nonego_dir)
418+
419+
with (open(testlog, 'a')) as logfile:
420+
spnego = subprocess.Popen(["tests/t_nonego.py"],
421+
stdout=logfile, stderr=logfile,
422+
env=testenv, preexec_fn=os.setsid)
423+
spnego.wait()
424+
if spnego.returncode != 0:
425+
sys.stderr.write('NO Negotiate: FAILED\n')
426+
else:
427+
sys.stderr.write('NO Negotiate: SUCCESS\n')
428+
429+
413430
if __name__ == '__main__':
414431

415432
args = parse_args()
@@ -454,6 +471,8 @@ def test_bad_acceptor_name(testdir, testenv, testlog):
454471
testenv.update(kdcenv)
455472
test_basic_auth_krb5(testdir, testenv, testlog)
456473

474+
test_no_negotiate(testdir, testenv, testlog)
475+
457476
finally:
458477
with (open(testlog, 'a')) as logfile:
459478
for name in processes:

tests/t_nonego.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#!/usr/bin/python
2+
# Copyright (C) 2015 - mod_auth_gssapi contributors, see COPYING for license.
3+
4+
import os
5+
import requests
6+
7+
8+
if __name__ == '__main__':
9+
url = 'http://%s/nonego/' % (os.environ['NSS_WRAPPER_HOSTNAME'])
10+
11+
# ensure a 401 with the appropriate WWW-Authenticate header is returned
12+
# when no auth is provided
13+
r = requests.get(url)
14+
if r.status_code != 401:
15+
raise ValueError('NO Negotiate failed - 401 expected')
16+
if not (r.headers.get("WWW-Authenticate") and
17+
r.headers.get("WWW-Authenticate").startswith("Negotiate")):
18+
raise ValueError('NO Negotiate failed - WWW-Authenticate '
19+
'Negotiate header is absent')
20+
21+
# ensure a 401 with the WWW-Authenticate Negotiate header is absent
22+
# when the special User-Agent is sent
23+
r = requests.get(url, headers={'User-Agent': 'NONEGO'})
24+
if r.status_code != 401:
25+
raise ValueError('NO Negotiate failed - 401 expected')
26+
if (r.headers.get("WWW-Authenticate") and
27+
r.headers.get("WWW-Authenticate").startswith("Negotiate")):
28+
raise ValueError('NO Negotiate failed - WWW-Authenticate '
29+
'Negotiate header is present, should be absent')

0 commit comments

Comments
 (0)