Skip to content

Commit 7407b64

Browse files
committed
Add support for handling Basic Auth
Support either passing Basic Auth Through to another module, or handling it directly through gss_acquire_cred_with_password() Fixes #8
1 parent 2411072 commit 7407b64

File tree

4 files changed

+182
-13
lines changed

4 files changed

+182
-13
lines changed

README

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,3 +172,13 @@ ticket by the application.
172172
Example:
173173
GssapiUseS4U2Proxy On
174174
GssapiDelegCcacheDir = /var/run/httpd/clientcaches
175+
176+
177+
### GssapiBasicAuth
178+
Allows the use of Basic Auth in conjunction with Negotiate.
179+
Two modes are supported, direct usage of the received username and password
180+
to try to acquire credentials via GSSAPI, or forwarding to following apache
181+
module.
182+
183+
Example:
184+
GssapiBasicAuth Forward

configure.ac

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ AC_CHECK_LIB([gssapi_krb5], [gss_accept_sec_context], [],
6565
[AC_MSG_ERROR([GSSAPI library check failed])])
6666
AC_CHECK_FUNCS(gss_acquire_cred_from)
6767
AC_CHECK_FUNCS(gss_store_cred_into)
68+
AC_CHECK_FUNCS(gss_acquire_cred_with_password)
6869

6970
AC_SUBST([GSSAPI_CFLAGS])
7071
AC_SUBST([GSSAPI_LIBS])

src/mod_auth_gssapi.c

Lines changed: 165 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ static void mag_store_deleg_creds(request_rec *req,
155155
static int mag_auth(request_rec *req)
156156
{
157157
const char *type;
158+
const char *auth_type;
158159
struct mag_config *cfg;
159160
const char *auth_header;
160161
char *auth_header_type;
@@ -166,6 +167,7 @@ static int mag_auth(request_rec *req)
166167
gss_buffer_desc output = GSS_C_EMPTY_BUFFER;
167168
gss_buffer_desc name = GSS_C_EMPTY_BUFFER;
168169
gss_name_t client = GSS_C_NO_NAME;
170+
gss_cred_id_t user_cred = GSS_C_NO_CREDENTIAL;
169171
gss_cred_id_t acquired_cred = GSS_C_NO_CREDENTIAL;
170172
gss_cred_id_t delegated_cred = GSS_C_NO_CREDENTIAL;
171173
gss_cred_usage_t cred_usage = GSS_C_ACCEPT;
@@ -178,6 +180,9 @@ static int mag_auth(request_rec *req)
178180
gss_OID mech_type = GSS_C_NO_OID;
179181
gss_buffer_desc lname = GSS_C_EMPTY_BUFFER;
180182
struct mag_conn *mc = NULL;
183+
bool is_basic = false;
184+
gss_ctx_id_t user_ctx = GSS_C_NO_CONTEXT;
185+
gss_name_t server = GSS_C_NO_NAME;
181186

182187
type = ap_auth_type(req);
183188
if ((type == NULL) || (strcasecmp(type, "GSSAPI") != 0)) {
@@ -225,7 +230,7 @@ static int mag_auth(request_rec *req)
225230
ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, req,
226231
"Already established context found!");
227232
apr_table_set(req->subprocess_env, "GSS_NAME", mc->gss_name);
228-
req->ap_auth_type = apr_pstrdup(req->pool, "Negotiate");
233+
req->ap_auth_type = apr_pstrdup(req->pool, mc->auth_type);
229234
req->user = apr_pstrdup(req->pool, mc->user_name);
230235
ret = OK;
231236
goto done;
@@ -241,21 +246,81 @@ static int mag_auth(request_rec *req)
241246
auth_header_type = ap_getword_white(req->pool, &auth_header);
242247
if (!auth_header_type) goto done;
243248

244-
if (strcasecmp(auth_header_type, "Negotiate") != 0) goto done;
249+
if (strcasecmp(auth_header_type, "Negotiate") == 0) {
250+
auth_type = "Negotiate";
251+
252+
auth_header_value = ap_getword_white(req->pool, &auth_header);
253+
if (!auth_header_value) goto done;
254+
input.length = apr_base64_decode_len(auth_header_value) + 1;
255+
input.value = apr_pcalloc(req->pool, input.length);
256+
if (!input.value) goto done;
257+
input.length = apr_base64_decode(input.value, auth_header_value);
258+
} else if (strcasecmp(auth_header_type, "Basic") == 0) {
259+
auth_type = "Basic";
260+
is_basic = true;
261+
262+
gss_buffer_desc ba_user;
263+
gss_buffer_desc ba_pwd;
264+
265+
switch (cfg->basic_auth) {
266+
case BA_ON:
267+
/* handle directly */
268+
break;
269+
case BA_FORWARD:
270+
/* decline to handle ourselves, let other modules do it */
271+
ret = DECLINED;
272+
goto done;
273+
case BA_OFF:
274+
goto done;
275+
default:
276+
goto done;
277+
}
278+
ba_pwd.value = ap_pbase64decode(req->pool, auth_header);
279+
if (!ba_pwd.value) goto done;
280+
ba_user.value = ap_getword_nulls_nc(req->pool,
281+
(char **)&ba_pwd.value, ':');
282+
if (!ba_user.value) goto done;
283+
if (((char *)ba_user.value)[0] == '\0' ||
284+
((char *)ba_pwd.value)[0] == '\0') {
285+
ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
286+
"Invalid empty user or password for Basic Auth");
287+
goto done;
288+
}
289+
ba_user.length = strlen(ba_user.value);
290+
ba_pwd.length = strlen(ba_pwd.value);
291+
maj = gss_import_name(&min, &ba_user, GSS_C_NT_USER_NAME, &client);
292+
if (GSS_ERROR(maj)) {
293+
ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
294+
"In Basic Auth, %s",
295+
mag_error(req, "gss_import_name() failed",
296+
maj, min));
297+
goto done;
298+
}
299+
maj = gss_acquire_cred_with_password(&min, client, &ba_pwd,
300+
GSS_C_INDEFINITE,
301+
GSS_C_NO_OID_SET,
302+
GSS_C_INITIATE,
303+
&user_cred, NULL, NULL);
304+
if (GSS_ERROR(maj)) {
305+
ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
306+
"In Basic Auth, %s",
307+
mag_error(req, "gss_acquire_cred_with_password() "
308+
"failed", maj, min));
309+
goto done;
310+
}
311+
gss_release_name(&min, &client);
312+
} else {
313+
goto done;
314+
}
245315

246-
auth_header_value = ap_getword_white(req->pool, &auth_header);
247-
if (!auth_header_value) goto done;
248-
input.length = apr_base64_decode_len(auth_header_value) + 1;
249-
input.value = apr_pcalloc(req->pool, input.length);
250-
if (!input.value) goto done;
251-
input.length = apr_base64_decode(input.value, auth_header_value);
316+
req->ap_auth_type = apr_pstrdup(req->pool, auth_type);
252317

253318
#ifdef HAVE_GSS_ACQUIRE_CRED_FROM
254319
if (cfg->use_s4u2proxy) {
255320
cred_usage = GSS_C_BOTH;
256321
}
257322
if (cfg->cred_store) {
258-
maj = gss_acquire_cred_from(&min, GSS_C_NO_NAME, 0,
323+
maj = gss_acquire_cred_from(&min, GSS_C_NO_NAME, GSS_C_INDEFINITE,
259324
GSS_C_NO_OID_SET, cred_usage,
260325
cfg->cred_store, &acquired_cred,
261326
NULL, NULL);
@@ -268,6 +333,40 @@ static int mag_auth(request_rec *req)
268333
}
269334
#endif
270335

336+
if (is_basic) {
337+
if (!acquired_cred) {
338+
/* Try to acquire default creds */
339+
maj = gss_acquire_cred(&min, GSS_C_NO_NAME, GSS_C_INDEFINITE,
340+
GSS_C_NO_OID_SET, cred_usage,
341+
&acquired_cred, NULL, NULL);
342+
if (GSS_ERROR(maj)) {
343+
ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
344+
"%s", mag_error(req, "gss_acquire_cred_from()"
345+
" failed", maj, min));
346+
goto done;
347+
}
348+
}
349+
maj = gss_inquire_cred(&min, acquired_cred, &server,
350+
NULL, NULL, NULL);
351+
if (GSS_ERROR(maj)) {
352+
ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
353+
"%s", mag_error(req, "gss_inquired_cred_() "
354+
"failed", maj, min));
355+
goto done;
356+
}
357+
/* output and input are inverted here, this is intentional */
358+
maj = gss_init_sec_context(&min, user_cred, &user_ctx, server,
359+
GSS_C_NO_OID, 0, 300,
360+
GSS_C_NO_CHANNEL_BINDINGS, &output,
361+
NULL, &input, NULL, NULL);
362+
if (GSS_ERROR(maj)) {
363+
ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
364+
"%s", mag_error(req, "gss_init_sec_context() "
365+
"failed", maj, min));
366+
goto done;
367+
}
368+
}
369+
271370
maj = gss_accept_sec_context(&min, pctx, acquired_cred,
272371
&input, GSS_C_NO_CHANNEL_BINDINGS,
273372
&client, &mech_type, &output, &flags, &vtime,
@@ -278,8 +377,33 @@ static int mag_auth(request_rec *req)
278377
maj, min));
279378
goto done;
280379
}
281-
282-
if (maj == GSS_S_CONTINUE_NEEDED) {
380+
if (is_basic) {
381+
while (maj == GSS_S_CONTINUE_NEEDED) {
382+
gss_release_buffer(&min, &input);
383+
/* output and input are inverted here, this is intentional */
384+
maj = gss_init_sec_context(&min, user_cred, &user_ctx, server,
385+
GSS_C_NO_OID, 0, 300,
386+
GSS_C_NO_CHANNEL_BINDINGS, &output,
387+
NULL, &input, NULL, NULL);
388+
if (GSS_ERROR(maj)) {
389+
ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
390+
"%s", mag_error(req, "gss_init_sec_context() "
391+
"failed", maj, min));
392+
goto done;
393+
}
394+
gss_release_buffer(&min, &output);
395+
maj = gss_accept_sec_context(&min, pctx, acquired_cred,
396+
&input, GSS_C_NO_CHANNEL_BINDINGS,
397+
&client, &mech_type, &output, &flags,
398+
&vtime, &delegated_cred);
399+
if (GSS_ERROR(maj)) {
400+
ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
401+
"%s", mag_error(req, "gss_accept_sec_context()"
402+
" failed", maj, min));
403+
goto done;
404+
}
405+
}
406+
} else if (maj == GSS_S_CONTINUE_NEEDED) {
283407
if (!mc) {
284408
ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
285409
"Mechanism needs continuation but neither "
@@ -293,8 +417,6 @@ static int mag_auth(request_rec *req)
293417
goto done;
294418
}
295419

296-
req->ap_auth_type = apr_pstrdup(req->pool, "Negotiate");
297-
298420
/* Always set the GSS name in an env var */
299421
maj = gss_display_name(&min, client, &name, NULL);
300422
if (GSS_ERROR(maj)) {
@@ -342,6 +464,7 @@ static int mag_auth(request_rec *req)
342464
if (cfg->use_sessions) {
343465
mag_attempt_session(req, cfg, mc);
344466
}
467+
mc->auth_type = auth_type;
345468
}
346469

347470
ret = OK;
@@ -360,12 +483,21 @@ static int mag_auth(request_rec *req)
360483
} else {
361484
apr_table_add(req->err_headers_out,
362485
"WWW-Authenticate", "Negotiate");
486+
if (cfg->basic_auth != BA_OFF) {
487+
apr_table_add(req->err_headers_out,
488+
"WWW-Authenticate",
489+
apr_psprintf(req->pool, "Basic realm=\"%s\"",
490+
ap_auth_name(req)));
491+
}
363492
}
364493
}
494+
gss_delete_sec_context(&min, &user_ctx, &output);
495+
gss_release_cred(&min, &user_cred);
365496
gss_release_cred(&min, &acquired_cred);
366497
gss_release_cred(&min, &delegated_cred);
367498
gss_release_buffer(&min, &output);
368499
gss_release_name(&min, &client);
500+
gss_release_name(&min, &server);
369501
gss_release_buffer(&min, &name);
370502
gss_release_buffer(&min, &lname);
371503
return ret;
@@ -542,6 +674,22 @@ static const char *mag_deleg_ccache_dir(cmd_parms *parms, void *mconfig,
542674
return NULL;
543675
}
544676

677+
static const char *mag_use_basic_auth(cmd_parms *parms, void *mconfig,
678+
const char *value)
679+
{
680+
struct mag_config *cfg = (struct mag_config *)mconfig;
681+
682+
if (strcasecmp(value, "on") == 0) {
683+
cfg->basic_auth = BA_ON;
684+
} else if (strcasecmp(value, "forward") == 0) {
685+
cfg->basic_auth = BA_FORWARD;
686+
} else {
687+
cfg->basic_auth = BA_OFF;
688+
}
689+
690+
return NULL;
691+
}
692+
545693
static const command_rec mag_commands[] = {
546694
AP_INIT_FLAG("GssapiSSLonly", mag_ssl_only, NULL, OR_AUTHCFG,
547695
"Work only if connection is SSL Secured"),
@@ -562,6 +710,10 @@ static const command_rec mag_commands[] = {
562710
"Credential Store"),
563711
AP_INIT_RAW_ARGS("GssapiDelegCcacheDir", mag_deleg_ccache_dir, NULL,
564712
OR_AUTHCFG, "Directory to store delegated credentials"),
713+
#endif
714+
#ifdef HAVE_GSS_ACQUIRE_CRED_WITH_PASSWORD
715+
AP_INIT_TAKE1("GssapiBasicAuth", mag_use_basic_auth, NULL, OR_AUTHCFG,
716+
"Allows use of Basic Auth for authentication"),
565717
#endif
566718
{ NULL }
567719
};

src/mod_auth_gssapi.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ struct mag_config {
4343
char *deleg_ccache_dir;
4444
gss_key_value_set_desc *cred_store;
4545
struct seal_key *mag_skey;
46+
enum {
47+
BA_OFF = 0,
48+
BA_FORWARD = 1,
49+
BA_ON = 2
50+
} basic_auth;
4651
};
4752

4853
struct mag_conn {
@@ -52,4 +57,5 @@ struct mag_conn {
5257
const char *user_name;
5358
const char *gss_name;
5459
time_t expiration;
60+
const char *auth_type;
5561
};

0 commit comments

Comments
 (0)