diff --git a/src/mod_auth_gssapi.c b/src/mod_auth_gssapi.c
index 763b625..b7664b2 100644
--- a/src/mod_auth_gssapi.c
+++ b/src/mod_auth_gssapi.c
@@ -292,8 +292,12 @@ static bool parse_auth_header(apr_pool_t *pool, const char **auth_header,
return true;
}
-static bool is_mech_allowed(gss_OID_set allowed_mechs, gss_const_OID mech)
+static bool is_mech_allowed(gss_OID_set allowed_mechs, gss_const_OID mech,
+ bool multi_step_supported)
{
+ if (!multi_step_supported && gss_oid_equal(&gss_mech_ntlmssp, mech))
+ return false;
+
if (allowed_mechs == GSS_C_NO_OID_SET) return true;
for (int i = 0; i < allowed_mechs->count; i++) {
@@ -615,11 +619,41 @@ static bool mag_auth_basic(request_rec *req,
return ret;
}
+struct mag_req_cfg *mag_init_cfg(request_rec *req)
+{
+ struct mag_req_cfg *req_cfg = apr_pcalloc(req->pool,
+ sizeof(struct mag_req_cfg));
+ req_cfg->cfg = ap_get_module_config(req->per_dir_config,
+ &auth_gssapi_module);
+
+ if (req_cfg->cfg->allowed_mechs) {
+ req_cfg->desired_mechs = req_cfg->cfg->allowed_mechs;
+ } else {
+ struct mag_server_config *scfg;
+ /* Try to fetch the default set if not explicitly configured */
+ scfg = ap_get_module_config(req->server->module_config,
+ &auth_gssapi_module);
+ req_cfg->desired_mechs = scfg->default_mechs;
+ }
+
+ if (req->proxyreq == PROXYREQ_PROXY) {
+ req_cfg->req_proto = "Proxy-Authorization";
+ req_cfg->rep_proto = "Proxy-Authenticate";
+ } else {
+ req_cfg->req_proto = "Authorization";
+ req_cfg->rep_proto = "WWW-Authenticate";
+ req_cfg->use_sessions = req_cfg->cfg->use_sessions;
+ req_cfg->send_persist = req_cfg->cfg->send_persist;
+ }
+
+ return req_cfg;
+}
static int mag_auth(request_rec *req)
{
const char *type;
int auth_type = -1;
+ struct mag_req_cfg *req_cfg;
struct mag_config *cfg;
const char *auth_header;
char *auth_header_type;
@@ -652,17 +686,11 @@ static int mag_auth(request_rec *req)
return DECLINED;
}
- cfg = ap_get_module_config(req->per_dir_config, &auth_gssapi_module);
+ req_cfg = mag_init_cfg(req);
- if (cfg->allowed_mechs) {
- desired_mechs = cfg->allowed_mechs;
- } else {
- struct mag_server_config *scfg;
- /* Try to fetch the default set if not explicitly configured */
- scfg = ap_get_module_config(req->server->module_config,
- &auth_gssapi_module);
- desired_mechs = scfg->default_mechs;
- }
+ cfg = req_cfg->cfg;
+
+ desired_mechs = req_cfg->desired_mechs;
/* implicit auth for subrequests if main auth already happened */
if (!ap_is_initial_req(req) && req->main != NULL) {
@@ -714,11 +742,11 @@ static int mag_auth(request_rec *req)
}
/* if available, session always supersedes connection bound data */
- if (cfg->use_sessions) {
+ if (req_cfg->use_sessions) {
mag_check_session(req, cfg, &mc);
}
- auth_header = apr_table_get(req->headers_in, "Authorization");
+ auth_header = apr_table_get(req->headers_in, req_cfg->req_proto);
if (mc) {
if (mc->established &&
@@ -785,7 +813,7 @@ static int mag_auth(request_rec *req)
break;
case AUTH_TYPE_RAW_NTLM:
- if (!is_mech_allowed(desired_mechs, &gss_mech_ntlmssp)) {
+ if (!is_mech_allowed(desired_mechs, &gss_mech_ntlmssp, (mc != NULL))) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
"NTLM Authentication is not allowed!");
goto done;
@@ -920,18 +948,19 @@ static int mag_auth(request_rec *req)
if (auth_type == AUTH_TYPE_BASIC) {
mag_basic_cache(cfg, mc, ba_user, ba_pwd);
}
- if (cfg->use_sessions) {
+ if (req_cfg->use_sessions) {
mag_attempt_session(req, cfg, mc);
}
}
- if (cfg->send_persist)
+ if (req_cfg->send_persist)
apr_table_set(req->headers_out, "Persistent-Auth",
cfg->gss_conn_ctx ? "true" : "false");
ret = OK;
done:
+
if ((auth_type != AUTH_TYPE_BASIC) && (output.length != 0)) {
int prefixlen = strlen(auth_types[auth_type]) + 1;
replen = apr_base64_encode_len(output.length) + 1;
@@ -940,17 +969,16 @@ static int mag_auth(request_rec *req)
memcpy(reply, auth_types[auth_type], prefixlen - 1);
reply[prefixlen - 1] = ' ';
apr_base64_encode(&reply[prefixlen], output.value, output.length);
- apr_table_add(req->err_headers_out,
- "WWW-Authenticate", reply);
+ apr_table_add(req->err_headers_out, req_cfg->rep_proto, reply);
}
} else if (ret == HTTP_UNAUTHORIZED) {
- apr_table_add(req->err_headers_out, "WWW-Authenticate", "Negotiate");
- if (is_mech_allowed(desired_mechs, &gss_mech_ntlmssp)) {
- apr_table_add(req->err_headers_out, "WWW-Authenticate", "NTLM");
+ apr_table_add(req->err_headers_out, req_cfg->rep_proto, "Negotiate");
+
+ if (is_mech_allowed(desired_mechs, &gss_mech_ntlmssp, (mc != NULL))) {
+ apr_table_add(req->err_headers_out, req_cfg->rep_proto, "NTLM");
}
if (cfg->use_basic_auth) {
- apr_table_add(req->err_headers_out,
- "WWW-Authenticate",
+ apr_table_add(req->err_headers_out, req_cfg->rep_proto,
apr_psprintf(req->pool, "Basic realm=\"%s\"",
ap_auth_name(req)));
}
diff --git a/src/mod_auth_gssapi.h b/src/mod_auth_gssapi.h
index 5beda2d..46e5c6a 100644
--- a/src/mod_auth_gssapi.h
+++ b/src/mod_auth_gssapi.h
@@ -65,6 +65,15 @@ struct mag_server_config {
gss_OID_set default_mechs;
};
+struct mag_req_cfg {
+ struct mag_config *cfg;
+ gss_OID_set desired_mechs;
+ bool use_sessions;
+ bool send_persist;
+ const char *req_proto;
+ const char *rep_proto;
+};
+
struct mag_conn {
apr_pool_t *pool;
gss_ctx_id_t ctx;
diff --git a/tests/httpd.conf b/tests/httpd.conf
index 77701f9..8c6370e 100644
--- a/tests/httpd.conf
+++ b/tests/httpd.conf
@@ -1,6 +1,7 @@
ServerRoot "${HTTPROOT}"
ServerName "${HTTPNAME}"
Listen ${HTTPADDR}:${HTTPPORT}
+Listen ${HTTPADDR}:${PROXYPORT}
LoadModule access_compat_module modules/mod_access_compat.so
LoadModule actions_module modules/mod_actions.so
@@ -62,13 +63,16 @@ LoadModule unixd_module modules/mod_unixd.so
LoadModule userdir_module modules/mod_userdir.so
LoadModule version_module modules/mod_version.so
LoadModule vhost_alias_module modules/mod_vhost_alias.so
-
LoadModule mpm_prefork_module modules/mod_mpm_prefork.so
+LoadModule proxy_module modules/mod_proxy.so
+LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule auth_gssapi_module mod_auth_gssapi.so
+ Options +Includes
+ AddOutputFilter INCLUDES .html
AllowOverride none
Require all denied
@@ -117,7 +121,10 @@ IncludeOptional conf.d/*.conf
CoreDumpDirectory /tmp
+
+ Options +Includes
+ AddOutputFilter INCLUDES .html
AuthType GSSAPI
AuthName "Login"
GssapiSSLonly Off
@@ -127,12 +134,14 @@ CoreDumpDirectory /tmp
GssapiCredStore ccache:${HTTPROOT}/tmp/httpd_krb5_ccache
GssapiCredStore client_keytab:${HTTPROOT}/http.keytab
GssapiCredStore keytab:${HTTPROOT}/http.keytab
- GssapiBasicAuth Off
+ GssapiBasicAuth On
GssapiAllowedMech krb5
Require valid-user
+ Options +Includes
+ AddOutputFilter INCLUDES .html
AuthType GSSAPI
AuthName "Password Login"
GssapiSSLonly Off
@@ -141,6 +150,21 @@ CoreDumpDirectory /tmp
GssapiCredStore keytab:${HTTPROOT}/http.keytab
GssapiBasicAuth On
GssapiBasicAuthMech krb5
+ GssapiConnectionBound On
Require valid-user
+
+ ProxyRequests On
+ ProxyVia On
+
+
+ AuthType GSSAPI
+ AuthName "Proxy Login"
+ GssapiCredStore ccache:${HTTPROOT}/tmp/httpd_krb5_ccache
+ GssapiCredStore client_keytab:${HTTPROOT}/http.keytab
+ GssapiCredStore keytab:${HTTPROOT}/http.keytab
+ GssapiBasicAuth On
+ Require valid-user
+
+
diff --git a/tests/index.html b/tests/index.html
index c5ad10e..9416405 100644
--- a/tests/index.html
+++ b/tests/index.html
@@ -1 +1 @@
-WORKS
+
diff --git a/tests/magtests.py b/tests/magtests.py
index 27f55f2..a38783d 100755
--- a/tests/magtests.py
+++ b/tests/magtests.py
@@ -11,6 +11,7 @@
import subprocess
import sys
import time
+import shlex
def parse_args():
@@ -23,6 +24,8 @@ def parse_args():
WRAP_HOSTNAME = "kdc.mag.dev"
WRAP_IPADDR = '127.0.0.9'
+WRAP_HTTP_PORT = '80'
+WRAP_PROXY_PORT = '8080'
def setup_wrappers(base):
@@ -47,6 +50,7 @@ def setup_wrappers(base):
wenv = {'LD_PRELOAD': 'libsocket_wrapper.so libnss_wrapper.so',
'SOCKET_WRAPPER_DIR': wrapdir,
'SOCKET_WRAPPER_DEFAULT_IFACE': '9',
+ 'WRAP_PROXY_PORT': WRAP_PROXY_PORT,
'NSS_WRAPPER_HOSTNAME': WRAP_HOSTNAME,
'NSS_WRAPPER_HOSTS': hosts_file}
@@ -73,8 +77,8 @@ def setup_wrappers(base):
}
[domain_realm]
- .mag.dev = MAG.DEV
- mag.dev = MAG.DEV
+ .mag.dev = ${TESTREALM}
+ mag.dev = ${TESTREALM}
[dbmodules]
${TESTREALM} = {
@@ -167,6 +171,8 @@ def kadmin_local(cmd, env, logfile):
USR_NAME = "maguser"
USR_PWD = "magpwd"
+USR_NAME_2 = "maguser2"
+USR_PWD_2 = "magpwd2"
SVC_KTNAME = "httpd/http.keytab"
KEY_TYPE = "aes256-cts-hmac-sha1-96:normal"
@@ -188,6 +194,10 @@ def setup_keys(tesdir, env):
with (open(testlog, 'a')) as logfile:
kadmin_local(cmd, env, logfile)
+ cmd = "addprinc -pw %s -e %s %s" % (USR_PWD_2, KEY_TYPE, USR_NAME_2)
+ with (open(testlog, 'a')) as logfile:
+ kadmin_local(cmd, env, logfile)
+
keys_env = { "KRB5_KTNAME": svc_keytab }
keys_env.update(env)
@@ -212,7 +222,8 @@ def setup_http(testdir, wrapenv):
text = t.substitute({'HTTPROOT': httpdir,
'HTTPNAME': WRAP_HOSTNAME,
'HTTPADDR': WRAP_IPADDR,
- 'HTTPPORT': '80'})
+ 'PROXYPORT': WRAP_PROXY_PORT,
+ 'HTTPPORT': WRAP_HTTP_PORT})
config = os.path.join(httpdir, 'httpd.conf')
with open(config, 'w+') as f:
f.write(text)
@@ -263,6 +274,20 @@ def test_spnego_auth(testdir, testenv, testlog):
else:
sys.stderr.write('SPNEGO: SUCCESS\n')
+ curl = "curl -vf http://%s:%s@%s/spnego/ -x http://%s:%s -U: --proxy-negotiate" % (
+ USR_NAME, USR_PWD, WRAP_HOSTNAME, WRAP_HOSTNAME, WRAP_PROXY_PORT)
+ curl = shlex.split(curl)
+
+ with (open(testlog, 'a')) as logfile:
+ spnego = subprocess.Popen(curl,
+ stdout=logfile, stderr=logfile,
+ env=testenv, preexec_fn=os.setsid)
+ spnego.wait()
+ if spnego.returncode != 0:
+ sys.stderr.write('SPNEGO Proxy Auth: FAILED\n')
+ else:
+ sys.stderr.write('SPNEGO Proxy Auth: SUCCESS\n')
+
def test_basic_auth_krb5(testdir, testenv, testlog):
@@ -280,6 +305,36 @@ def test_basic_auth_krb5(testdir, testenv, testlog):
else:
sys.stderr.write('BASIC-AUTH: SUCCESS\n')
+ with (open(testlog, 'a')) as logfile:
+ basick5 = subprocess.Popen(["tests/t_basic_k5_two_users.py"],
+ stdout=logfile, stderr=logfile,
+ env=testenv, preexec_fn=os.setsid)
+ basick5.wait()
+ if basick5.returncode != 0:
+ sys.stderr.write('BASIC-AUTH Two Users: FAILED\n')
+ else:
+ sys.stderr.write('BASIC-AUTH Two Users: SUCCESS\n')
+
+ with (open(testlog, 'a')) as logfile:
+ basick5 = subprocess.Popen(["tests/t_basic_k5_fail_second.py"],
+ stdout=logfile, stderr=logfile,
+ env=testenv, preexec_fn=os.setsid)
+ basick5.wait()
+ if basick5.returncode != 0:
+ sys.stderr.write('BASIC Fail Second User: FAILED\n')
+ else:
+ sys.stderr.write('BASIC Fail Secnond User: SUCCESS\n')
+
+ with (open(testlog, 'a')) as logfile:
+ basick5 = subprocess.Popen(["tests/t_basic_proxy.py"],
+ stdout=logfile, stderr=logfile,
+ env=testenv, preexec_fn=os.setsid)
+ basick5.wait()
+ if basick5.returncode != 0:
+ sys.stderr.write('BASIC Proxy Auth: FAILED\n')
+ else:
+ sys.stderr.write('BASIC Proxy Auth: SUCCESS\n')
+
if __name__ == '__main__':
@@ -310,7 +365,9 @@ def test_basic_auth_krb5(testdir, testenv, testlog):
testenv = {'MAG_USER_NAME': USR_NAME,
- 'MAG_USER_PASSWORD': USR_PWD}
+ 'MAG_USER_PASSWORD': USR_PWD,
+ 'MAG_USER_NAME_2': USR_NAME_2,
+ 'MAG_USER_PASSWORD_2': USR_PWD_2}
testenv.update(kdcenv)
test_basic_auth_krb5(testdir, testenv, testlog)
diff --git a/tests/t_basic_k5_fail_second.py b/tests/t_basic_k5_fail_second.py
new file mode 100755
index 0000000..cd78c4f
--- /dev/null
+++ b/tests/t_basic_k5_fail_second.py
@@ -0,0 +1,36 @@
+#!/usr/bin/python
+# Copyright (C) 2015 - mod_auth_gssapi contributors, see COPYING for license.
+
+import os
+import requests
+from requests.auth import HTTPBasicAuth
+
+
+if __name__ == '__main__':
+ s = requests.Session()
+
+ url = 'http://%s:%s@%s/basic_auth_krb5/' % (os.environ['MAG_USER_NAME'],
+ os.environ['MAG_USER_PASSWORD'],
+ os.environ['NSS_WRAPPER_HOSTNAME'])
+ r = s.get(url)
+ if r.status_code != 200:
+ raise ValueError('Basic Auth Failed')
+
+ url = 'http://%s:%s@%s/basic_auth_krb5/' % (os.environ['MAG_USER_NAME_2'],
+ os.environ['MAG_USER_PASSWORD'],
+ os.environ['NSS_WRAPPER_HOSTNAME'])
+ r = s.get(url)
+ if r.status_code == 200:
+ raise ValueError('Basic Auth fatal error')
+
+ url = 'http://%s:%s@%s/basic_auth_krb5/' % (os.environ['MAG_USER_NAME_2'],
+ os.environ['MAG_USER_PASSWORD_2'],
+ os.environ['NSS_WRAPPER_HOSTNAME'])
+ r = s.get(url)
+ if r.status_code != 200:
+ raise ValueError('Basic Auth Failed')
+
+ url = 'http://%s/basic_auth_krb5/' % os.environ['NSS_WRAPPER_HOSTNAME']
+ r = s.get(url)
+ if r.status_code == 200:
+ raise ValueError('Basic Auth fatal error')
diff --git a/tests/t_basic_k5_two_users.py b/tests/t_basic_k5_two_users.py
new file mode 100755
index 0000000..0d3d45b
--- /dev/null
+++ b/tests/t_basic_k5_two_users.py
@@ -0,0 +1,27 @@
+#!/usr/bin/python
+# Copyright (C) 2015 - mod_auth_gssapi contributors, see COPYING for license.
+
+import os
+import requests
+from requests.auth import HTTPBasicAuth
+
+
+if __name__ == '__main__':
+ s = requests.Session()
+
+ url = 'http://%s:%s@%s/basic_auth_krb5/' % (os.environ['MAG_USER_NAME'],
+ os.environ['MAG_USER_PASSWORD'],
+ os.environ['NSS_WRAPPER_HOSTNAME'])
+ r = s.get(url)
+ if r.status_code != 200:
+ raise ValueError('Basic Auth Failed')
+
+ url = 'http://%s:%s@%s/basic_auth_krb5/' % (os.environ['MAG_USER_NAME_2'],
+ os.environ['MAG_USER_PASSWORD_2'],
+ os.environ['NSS_WRAPPER_HOSTNAME'])
+ r2 = s.get(url)
+ if r2.status_code != 200:
+ raise ValueError('Basic Auth failed')
+
+ if r.text == r2.text:
+ raise ValueError('Basic Auth fatal error')
diff --git a/tests/t_basic_proxy.py b/tests/t_basic_proxy.py
new file mode 100755
index 0000000..4290695
--- /dev/null
+++ b/tests/t_basic_proxy.py
@@ -0,0 +1,20 @@
+#!/usr/bin/python
+# Copyright (C) 2015 - mod_auth_gssapi contributors, see COPYING for license.
+
+import os
+import requests
+from requests.auth import HTTPBasicAuth
+
+
+if __name__ == '__main__':
+ proxy = 'http://%s:%s@%s:%s' % (os.environ['MAG_USER_NAME'],
+ os.environ['MAG_USER_PASSWORD'],
+ os.environ['NSS_WRAPPER_HOSTNAME'],
+ os.environ['WRAP_PROXY_PORT'])
+ proxies = { "http": proxy, }
+ url = 'http://%s/basic_auth_krb5/' % os.environ['NSS_WRAPPER_HOSTNAME']
+ r = requests.get(url, proxies=proxies,
+ auth=HTTPBasicAuth(os.environ['MAG_USER_NAME_2'],
+ os.environ['MAG_USER_PASSWORD_2']))
+ if r.status_code != 200:
+ raise ValueError('Basic Proxy Auth Failed')