Skip to content

Commit 92e4025

Browse files
committed
Allow sourcing the acceptor_name from httpd
The hostname the client uses to connect to apache is stored in the request structure for modules to use. This patch adds an option to specify that the acceptor name to be used should be constructed on a per-request basis from this name. This is simply achieved by specifying the literal string {HOSTNAME} to the existing GssapiAcceptorName option insted of specifying a full GSSAPI name. Signed-off-by: Simo Sorce <simo@redhat.com> Reviewed-by: Robbie Harwood <rharwood@redhat.com> Reviewed-by: Jan Pazdziora <jpazdziora@redhat.com> Resolves #138 Closes #139
1 parent 114e440 commit 92e4025

File tree

6 files changed

+117
-5
lines changed

6 files changed

+117
-5
lines changed

README

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,10 @@ This option is used to force the server to accept only for a specific name.
407407
This allows, for example to select to use a specific credential when multiple
408408
keys are provided in a keytab.
409409

410+
A special value of {HOSTNAME} will make the code use the name apache sees in
411+
the httpd request to select the correct name to use. This may be useful to
412+
allow multiple names and multiple keys to be used on the same apache instance.
413+
410414
Note: By default no name is set and any name in a keytab or mechanism specific
411415
acceptor credential will be allowed.
412416

src/mod_auth_gssapi.c

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -185,26 +185,54 @@ static bool mag_acquire_creds(request_rec *req,
185185
gss_cred_id_t *creds,
186186
gss_OID_set *actual_mechs)
187187
{
188+
gss_name_t acceptor_name = GSS_C_NO_NAME;
188189
uint32_t maj, min;
190+
bool ret;
191+
192+
if (cfg->acceptor_name_from_req) {
193+
gss_buffer_desc bufnam;
194+
195+
bufnam.value = apr_psprintf(req->pool, "HTTP@%s", req->hostname);
196+
bufnam.length = strlen(bufnam.value);
197+
198+
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req, "GSS Server Name: %s",
199+
(char *)bufnam.value);
200+
201+
maj = gss_import_name(&min, &bufnam, GSS_C_NT_HOSTBASED_SERVICE,
202+
&acceptor_name);
203+
if (GSS_ERROR(maj)) {
204+
mag_post_error(req, cfg, MAG_GSS_ERR, maj, min,
205+
"gss_import_name() failed to import hostnname");
206+
return false;
207+
}
208+
} else {
209+
acceptor_name = cfg->acceptor_name;
210+
}
211+
189212
#ifdef HAVE_CRED_STORE
190213
gss_const_key_value_set_t store = cfg->cred_store;
191214

192-
maj = gss_acquire_cred_from(&min, cfg->acceptor_name, GSS_C_INDEFINITE,
215+
maj = gss_acquire_cred_from(&min, acceptor_name, GSS_C_INDEFINITE,
193216
desired_mechs, cred_usage, store, creds,
194217
actual_mechs, NULL);
195218
#else
196-
maj = gss_acquire_cred(&min, cfg->acceptor_name, GSS_C_INDEFINITE,
219+
maj = gss_acquire_cred(&min, acceptor_name, GSS_C_INDEFINITE,
197220
desired_mechs, cred_usage, creds,
198221
actual_mechs, NULL);
199222
#endif
200223

201224
if (GSS_ERROR(maj)) {
202225
mag_post_error(req, cfg, MAG_GSS_ERR, maj, min,
203226
"gss_acquire_cred[_from]() failed to get server creds");
204-
return false;
227+
ret = false;
228+
} else {
229+
ret = true;
205230
}
206231

207-
return true;
232+
if (cfg->acceptor_name_from_req) {
233+
gss_release_name(&min, &acceptor_name);
234+
}
235+
return ret;
208236
}
209237

210238
#ifdef HAVE_CRED_STORE
@@ -1720,6 +1748,11 @@ static const char *mag_acceptor_name(cmd_parms *parms, void *mconfig,
17201748
gss_buffer_desc bufnam = { strlen(w), (void *)w };
17211749
uint32_t maj, min;
17221750

1751+
if (strcmp(w, "{HOSTNAME}") == 0) {
1752+
cfg->acceptor_name_from_req = true;
1753+
return NULL;
1754+
}
1755+
17231756
maj = gss_import_name(&min, &bufnam, GSS_C_NT_HOSTBASED_SERVICE,
17241757
&cfg->acceptor_name);
17251758
if (GSS_ERROR(maj)) {

src/mod_auth_gssapi.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ struct mag_config {
9393
struct mag_name_attributes *name_attributes;
9494
bool enverrs;
9595
gss_name_t acceptor_name;
96+
bool acceptor_name_from_req;
9697
};
9798

9899
struct mag_server_config {

tests/httpd.conf

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

227+
<Location /hostname_acceptor>
228+
AuthType GSSAPI
229+
AuthName "Login"
230+
GssapiSSLonly Off
231+
GssapiCredStore ccache:${HTTPROOT}/tmp/httpd_krb5_ccache
232+
GssapiCredStore client_keytab:${HTTPROOT}/http.keytab
233+
GssapiCredStore keytab:${HTTPROOT}/http.keytab
234+
GssapiBasicAuth Off
235+
GssapiAllowedMech krb5
236+
GssapiAcceptorName {HOSTNAME}
237+
Require valid-user
238+
</Location>
239+
227240
<VirtualHost *:${PROXYPORT}>
228241
ProxyRequests On
229242
ProxyVia On

tests/magtests.py

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ def parse_args():
2828

2929

3030
WRAP_HOSTNAME = "kdc.mag.dev"
31+
WRAP_ALIASNAME = "alias.mag.dev"
32+
WRAP_FAILNAME = "fail.mag.dev"
3133
WRAP_IPADDR = '127.0.0.9'
3234
WRAP_HTTP_PORT = '80'
3335
WRAP_PROXY_PORT = '8080'
@@ -50,7 +52,9 @@ def setup_wrappers(base):
5052

5153
hosts_file = os.path.join(testdir, 'hosts')
5254
with open(hosts_file, 'w+') as f:
53-
f.write('%s %s' % (WRAP_IPADDR, WRAP_HOSTNAME))
55+
f.write('%s %s\n' % (WRAP_IPADDR, WRAP_HOSTNAME))
56+
f.write('%s %s\n' % (WRAP_IPADDR, WRAP_ALIASNAME))
57+
f.write('%s %s\n' % (WRAP_IPADDR, WRAP_FAILNAME))
5458

5559
wenv = {'LD_PRELOAD': 'libsocket_wrapper.so libnss_wrapper.so',
5660
'SOCKET_WRAPPER_DIR': wrapdir,
@@ -203,6 +207,15 @@ def setup_keys(tesdir, env):
203207
with (open(testlog, 'a')) as logfile:
204208
kadmin_local(cmd, env, logfile)
205209

210+
# alias for multinamed hosts testing
211+
alias_name = "HTTP/%s" % WRAP_ALIASNAME
212+
cmd = "addprinc -randkey -e %s %s" % (KEY_TYPE, alias_name)
213+
with (open(testlog, 'a')) as logfile:
214+
kadmin_local(cmd, env, logfile)
215+
cmd = "ktadd -k %s -e %s %s" % (svc_keytab, KEY_TYPE, alias_name)
216+
with (open(testlog, 'a')) as logfile:
217+
kadmin_local(cmd, env, logfile)
218+
206219
keys_env = { "KRB5_KTNAME": svc_keytab }
207220
keys_env.update(env)
208221

@@ -427,6 +440,36 @@ def test_no_negotiate(testdir, testenv, testlog):
427440
sys.stderr.write('NO Negotiate: SUCCESS\n')
428441

429442

443+
def test_hostname_acceptor(testdir, testenv, testlog):
444+
445+
hdir = os.path.join(testdir, 'httpd', 'html', 'hostname_acceptor')
446+
os.mkdir(hdir)
447+
shutil.copy('tests/index.html', hdir)
448+
449+
with (open(testlog, 'a')) as logfile:
450+
failed = False
451+
for (name, fail) in [(WRAP_HOSTNAME, False),
452+
(WRAP_ALIASNAME,False),
453+
(WRAP_FAILNAME, True)]:
454+
res = subprocess.Popen(["tests/t_hostname_acceptor.py", name],
455+
stdout=logfile, stderr=logfile,
456+
env=testenv, preexec_fn=os.setsid)
457+
res.wait()
458+
if fail:
459+
if res.returncode == 0:
460+
failed = True
461+
else:
462+
if res.returncode != 0:
463+
failed = True
464+
if failed:
465+
break
466+
467+
if failed:
468+
sys.stderr.write('HOSTNAME ACCEPTOR: FAILED\n')
469+
else:
470+
sys.stderr.write('HOSTNAME ACCEPTOR: SUCCESS\n')
471+
472+
430473
if __name__ == '__main__':
431474

432475
args = parse_args()
@@ -462,6 +505,8 @@ def test_no_negotiate(testdir, testenv, testlog):
462505

463506
test_spnego_negotiate_once(testdir, testenv, testlog)
464507

508+
test_hostname_acceptor(testdir, testenv, testlog)
509+
465510
test_bad_acceptor_name(testdir, testenv, testlog)
466511

467512
testenv = {'MAG_USER_NAME': USR_NAME,

tests/t_hostname_acceptor.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#!/usr/bin/python
2+
# Copyright (C) 2017 - mod_auth_gssapi contributors, see COPYING for license.
3+
4+
import os
5+
import requests
6+
import sys
7+
from stat import ST_MODE
8+
from requests_kerberos import HTTPKerberosAuth, OPTIONAL
9+
10+
11+
if __name__ == '__main__':
12+
sess = requests.Session()
13+
url = 'http://%s/hostname_acceptor/' % sys.argv[1]
14+
r = sess.get(url, auth=HTTPKerberosAuth(delegate=True))
15+
if r.status_code != 200:
16+
raise ValueError('Hostname-based acceptor failed')

0 commit comments

Comments
 (0)