diff --git a/src/mod_auth_gssapi.c b/src/mod_auth_gssapi.c index 2883ed9..99de884 100644 --- a/src/mod_auth_gssapi.c +++ b/src/mod_auth_gssapi.c @@ -194,6 +194,9 @@ static bool mag_conn_is_https(conn_rec *c) return false; } +static char *get_ccache_name(request_rec *req, char *dir, const char *name, + bool use_unique, apr_pool_t *pool); + static bool mag_acquire_creds(request_rec *req, struct mag_config *cfg, gss_OID_set desired_mechs, @@ -226,7 +229,52 @@ static bool mag_acquire_creds(request_rec *req, } #ifdef HAVE_CRED_STORE - gss_const_key_value_set_t store = cfg->cred_store; + gss_const_key_value_set_t store = NULL; + + /* When using multiple names, we need to use individual separate ccaches + * for each principal or gss_acquire_cred() on the default ccache will + * fail when names don't match. This is needed only for the s4u2proxy + * case, where we try to acquire proxy credentials. The lucky thing is + * that in this case we require the use of a delegated creedntials + * directory, so we just use this directory to also hold permanent ccaches + * for individual acceptor names. */ + if (cfg->acceptor_name_from_req && cfg->use_s4u2proxy && + cfg->deleg_ccache_dir) { + + gss_key_value_set_desc *s; + bool add = true; + char *ccname; + char *special_name; + + special_name = apr_psprintf(req->pool, "acceptor_%s", req->hostname); + ccname = get_ccache_name(req, cfg->deleg_ccache_dir, special_name, + false, req->pool); + + s = apr_pcalloc(req->pool, sizeof(gss_key_value_set_desc)); + s->count = cfg->cred_store->count; + s->elements = apr_pcalloc(req->pool, + (s->count + 1) * + sizeof(gss_key_value_element_desc)); + for (size_t i = 0; i < s->count; i++) { + gss_key_value_element_desc *el = &cfg->cred_store->elements[i]; + s->elements[i].key = el->key; + if (strcmp(el->key, "ccache") == 0) { + s->elements[i].value = ccname; + add = false; + } else { + s->elements[i].value = el->value; + } + } + if (add) { + s->elements[s->count].key = "ccache"; + s->elements[s->count].value = ccname; + s->count++; + } + + store = s; + } else { + store = cfg->cred_store; + } maj = gss_acquire_cred_from(&min, acceptor_name, GSS_C_INDEFINITE, desired_mechs, cred_usage, store, creds, @@ -287,8 +335,8 @@ static char *escape(apr_pool_t *pool, const char *name, return escaped; } -static char *get_ccache_name(request_rec *req, char *dir, const char *gss_name, - bool use_unique, struct mag_conn *mc) +static char *get_ccache_name(request_rec *req, char *dir, const char *name, + bool use_unique, apr_pool_t *pool) { char *ccname, *escaped; int ccachefd; @@ -297,15 +345,15 @@ static char *get_ccache_name(request_rec *req, char *dir, const char *gss_name, /* We need to escape away '/', we can't have path separators in * a ccache file name */ /* first double escape the esacping char (~) if any */ - escaped = escape(req->pool, gss_name, '~', "~~"); + escaped = escape(req->pool, name, '~', "~~"); /* then escape away the separator (/) if any */ escaped = escape(req->pool, escaped, '/', "~"); if (use_unique == false) { - return apr_psprintf(mc->pool, "%s/%s", dir, escaped); + return apr_psprintf(pool, "%s/%s", dir, escaped); } - ccname = apr_psprintf(mc->pool, "%s/%s-XXXXXX", dir, escaped); + ccname = apr_psprintf(pool, "%s/%s-XXXXXX", dir, escaped); umask_save = umask(0177); ccachefd = mkstemp(ccname); @@ -1297,7 +1345,7 @@ static int mag_complete(struct mag_req_cfg *req_cfg, struct mag_conn *mc, "requester: %s", mc->gss_name); ccache_path = get_ccache_name(req, cfg->deleg_ccache_dir, mc->gss_name, - cfg->deleg_ccache_unique, mc); + cfg->deleg_ccache_unique, mc->pool); if (ccache_path == NULL) { goto done; } diff --git a/tests/httpd.conf b/tests/httpd.conf index 79be816..4672cde 100644 --- a/tests/httpd.conf +++ b/tests/httpd.conf @@ -238,6 +238,21 @@ CoreDumpDirectory "{HTTPROOT}" Require valid-user + + AuthType GSSAPI + AuthName "Login" + GssapiSSLonly Off + GssapiCredStore ccache:{HTTPROOT}/httpd_krb5_ccache + GssapiCredStore client_keytab:{HTTPROOT}/http.keytab + GssapiCredStore keytab:{HTTPROOT}/http.keytab + GssapiBasicAuth Off + GssapiAllowedMech krb5 + GssapiAcceptorName {{HOSTNAME}} + GssapiUseS4U2Proxy On + GssapiDelegCcacheDir {HTTPROOT}/delegccachedir + Require valid-user + + AuthType GSSAPI AuthName "Required Name Attributes" diff --git a/tests/magtests.py b/tests/magtests.py index ee17b5d..7316788 100755 --- a/tests/magtests.py +++ b/tests/magtests.py @@ -691,26 +691,32 @@ def test_no_negotiate(testdir, testenv, logfile): def test_hostname_acceptor(testdir, testenv, logfile): - hdir = os.path.join(testdir, 'httpd', 'html', 'hostname_acceptor') + plain_test_name = 'hostname_acceptor' + hdir = os.path.join(testdir, 'httpd', 'html', plain_test_name) os.mkdir(hdir) shutil.copy('tests/index.html', hdir) + proxy_test_name = 'hostname_proxy' + hdir = os.path.join(testdir, 'httpd', 'html', proxy_test_name) + os.mkdir(hdir) + shutil.copy('tests/index.html', hdir) + ddir = os.path.join(testdir, 'httpd', 'delegccachedir') + os.mkdir(ddir) + failed = False - for (name, fail) in [(WRAP_HOSTNAME, False), - (WRAP_ALIASNAME, False), - (WRAP_FAILNAME, True)]: - res = subprocess.Popen(["tests/t_hostname_acceptor.py", name], - stdout=logfile, stderr=logfile, - env=testenv, preexec_fn=os.setsid) - res.wait() - if fail: - if res.returncode == 0: - failed = True - else: - if res.returncode != 0: + for test_name in [plain_test_name, proxy_test_name]: + for (name, fail) in [(WRAP_HOSTNAME, False), + (WRAP_ALIASNAME, False), + (WRAP_FAILNAME, True)]: + res = subprocess.Popen(["tests/t_hostname_acceptor.py", + name, test_name], + stdout=logfile, stderr=logfile, + env=testenv, preexec_fn=os.setsid) + res.wait() + if (fail and res.returncode == 0) or \ + (not fail and res.returncode != 0): failed = True - if failed: - break + break if failed: sys.stderr.write('HOSTNAME ACCEPTOR: FAILED\n') diff --git a/tests/t_hostname_acceptor.py b/tests/t_hostname_acceptor.py index bb85700..0a07a0f 100755 --- a/tests/t_hostname_acceptor.py +++ b/tests/t_hostname_acceptor.py @@ -9,7 +9,7 @@ if __name__ == '__main__': sess = requests.Session() - url = 'http://%s/hostname_acceptor/' % sys.argv[1] + url = 'http://{}/{}/'.format(sys.argv[1], sys.argv[2]) r = sess.get(url, auth=HTTPKerberosAuth(delegate=True)) if r.status_code != 200: - raise ValueError('Hostname-based acceptor failed') + raise ValueError('Hostname acceptor ({}) failed'.format(sys.argv[2]))