-
Notifications
You must be signed in to change notification settings - Fork 41
mod_auth_gssapi: implement basic auth forwarding #151
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -835,6 +835,51 @@ static gss_OID_set mag_get_negotiate_mechs(apr_pool_t *p, gss_OID_set desired) | |
} | ||
} | ||
|
||
|
||
static const char *add_authn_provider(cmd_parms *cmd, void *config, | ||
const char *arg) | ||
{ | ||
struct mag_config *conf = (struct mag_config *)config; | ||
authn_provider_list *newp; | ||
|
||
newp = apr_pcalloc(cmd->pool, sizeof(authn_provider_list)); | ||
newp->provider_name = arg; | ||
|
||
/* lookup and cache the actual provider now */ | ||
newp->provider = ap_lookup_provider(AUTHN_PROVIDER_GROUP, | ||
newp->provider_name, | ||
AUTHN_PROVIDER_VERSION); | ||
|
||
if (newp->provider == NULL) { | ||
/* by the time they use it, the provider should be loaded and | ||
registered with us. */ | ||
return apr_psprintf(cmd->pool, | ||
"Unknown Authn provider: %s", | ||
newp->provider_name); | ||
} | ||
|
||
if (!newp->provider->check_password) { | ||
/* if it doesn't provide the appropriate function, reject it */ | ||
return apr_psprintf(cmd->pool, | ||
"The '%s' Authn provider doesn't support " | ||
"Basic Authentication", newp->provider_name); | ||
} | ||
|
||
/* Add it to the list now. */ | ||
if (!conf->providers) { | ||
conf->providers = newp; | ||
} | ||
else { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. } else { please There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. copied from the original code - so you use a different coding style than upstream apache? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Replying to just this comment, but please read it as a reply to several of yours: yes we have our own coding style, and yes it doesn't match upstream apache. It is much closer to the krb5 and freeipa style for reasons you can probably imagine. In general, consistency within the project (i.e., within m_a_g) is paramount, since it makes maintaining the codebase so much easier. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See @frozencemetery's comment, if there is some value in keeping the original it would have to be stored in it's own file, with prominent headers that state exactly where it comes from and what is the license on that piece of code (asuming it is compatible, otherwise we cannot keep a copy) so that people can keep it up to date according to the original source. It would still require a wrapper function named mag_something in that case as you would have to make the funciton public and we definitelyt do not allow non-namespaced function as public. |
||
authn_provider_list *last = conf->providers; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. use for loop here please: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here: "copied from the original code" |
||
|
||
while (last->next) { | ||
last = last->next; | ||
} | ||
last->next = newp; | ||
} | ||
return NULL; | ||
} | ||
|
||
static int mag_auth(request_rec *req) | ||
{ | ||
const char *type; | ||
|
@@ -868,6 +913,8 @@ static int mag_auth(request_rec *req) | |
if ((type == NULL) || (strcasecmp(type, "GSSAPI") != 0)) { | ||
return DECLINED; | ||
} | ||
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req, | ||
"URI: %s: type is: %s mag_auth", req->uri ?: "no-uri", type); | ||
|
||
req_cfg = mag_init_cfg(req); | ||
|
||
|
@@ -897,42 +944,47 @@ static int mag_auth(request_rec *req) | |
main_req = main_req->prev; | ||
|
||
type = ap_auth_type(main_req); | ||
if ((type != NULL) && (strcasecmp(type, "GSSAPI") == 0)) { | ||
/* warn if the subrequest location and the main request | ||
* location have different configs */ | ||
if (cfg != ap_get_module_config(main_req->per_dir_config, | ||
&auth_gssapi_module)) { | ||
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, | ||
req, "Subrequest authentication bypass on " | ||
"location with different configuration!"); | ||
} | ||
if (main_req->user) { | ||
apr_table_t *env; | ||
|
||
req->user = apr_pstrdup(req->pool, main_req->user); | ||
req->ap_auth_type = main_req->ap_auth_type; | ||
|
||
env = ap_get_module_config(main_req->request_config, | ||
&auth_gssapi_module); | ||
if (!env) { | ||
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, req, | ||
"Failed to lookup env table in subrequest"); | ||
} else | ||
mag_export_req_env(req, env); | ||
|
||
return OK; | ||
if (apr_table_get(main_req->notes, "AUTH_BASIC_FORWARD") != NULL) { | ||
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, | ||
req, "auth_type: %s user: %s main req used auth basic.", type, main_req->user); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This reflow nests too much add a variable to skip subreq handling like: if (apr_table_get(main ....) { There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK will do so |
||
} else { | ||
if ((type != NULL) && (strcasecmp(type, "GSSAPI") == 0)) { | ||
/* warn if the subrequest location and the main request | ||
* location have different configs */ | ||
if (cfg != ap_get_module_config(main_req->per_dir_config, | ||
&auth_gssapi_module)) { | ||
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, | ||
req, "Subrequest authentication bypass on " | ||
"location with different configuration!"); | ||
} | ||
if (main_req->user) { | ||
apr_table_t *env; | ||
|
||
req->user = apr_pstrdup(req->pool, main_req->user); | ||
req->ap_auth_type = main_req->ap_auth_type; | ||
|
||
env = ap_get_module_config(main_req->request_config, | ||
&auth_gssapi_module); | ||
if (!env) { | ||
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, req, | ||
"Failed to lookup env table in subrequest"); | ||
} else | ||
mag_export_req_env(req, env); | ||
|
||
return OK; | ||
} else { | ||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, | ||
"The main request is tasked to establish the " | ||
"security context, can't proceed!"); | ||
return HTTP_UNAUTHORIZED; | ||
} | ||
} else { | ||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, | ||
"The main request is tasked to establish the " | ||
"security context, can't proceed!"); | ||
return HTTP_UNAUTHORIZED; | ||
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req, | ||
"Subrequest GSSAPI auth with no auth on the main " | ||
"request. This operation may fail if other " | ||
"subrequests already established a context or the " | ||
"mechanism requires multiple roundtrips."); | ||
} | ||
} else { | ||
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req, | ||
"Subrequest GSSAPI auth with no auth on the main " | ||
"request. This operation may fail if other " | ||
"subrequests already established a context or the " | ||
"mechanism requires multiple roundtrips."); | ||
} | ||
} | ||
|
||
|
@@ -1024,7 +1076,7 @@ static int mag_auth(request_rec *req) | |
} | ||
break; | ||
case AUTH_TYPE_BASIC: | ||
if (!cfg->use_basic_auth) { | ||
if (cfg->use_basic_auth == BA_OFF) { | ||
goto done; | ||
} | ||
|
||
|
@@ -1043,6 +1095,59 @@ static int mag_auth(request_rec *req) | |
ba_user.length = strlen(ba_user.value); | ||
ba_pwd.length = strlen(ba_pwd.value); | ||
|
||
if (cfg->use_basic_auth == BA_FORWARD) { | ||
authn_provider_list *current_provider; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. call this 'provider', initialize it to NULL There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK |
||
authn_status auth_result = AUTHZ_GENERAL_ERROR; | ||
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req, | ||
"BA_FORWARD: forward auth to providers"); | ||
|
||
// overwrite AUTH_TYPE_BASIC => it's basic now | ||
auth_type = AUTH_TYPE_BASIC; | ||
req->ap_auth_type = (char *) mag_str_auth_type(auth_type); | ||
req->user = apr_pstrdup(req->pool, ba_user.value); | ||
|
||
current_provider = cfg->providers; | ||
do { | ||
const authn_provider *provider; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do not use this variable, you will use provider->provider once you rename as indicated above. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK |
||
|
||
provider = current_provider->provider; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this will segfault if cfg->providers == NULL There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sure ok |
||
apr_table_setn(req->notes, AUTHN_PROVIDER_NAME_NOTE, current_provider->provider_name); | ||
|
||
auth_result = provider->check_password(req, ba_user.value, ba_pwd.value); | ||
|
||
apr_table_unset(req->notes, AUTHN_PROVIDER_NAME_NOTE); | ||
|
||
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req, | ||
"BA_FORWARD: provider: %s result: %d", current_provider->provider_name, auth_result); | ||
|
||
/* Something occurred. Stop checking. */ | ||
if (auth_result != AUTH_USER_NOT_FOUND) { | ||
break; | ||
} | ||
|
||
/* If we're not really configured for providers, stop now. */ | ||
if (!cfg->providers) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. too late to check this here, you already segfaulted above. |
||
break; | ||
} | ||
|
||
current_provider = current_provider->next; | ||
} while (current_provider); | ||
|
||
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req, | ||
"BA_FORWARD: End of loop provider: %s result: %d", current_provider->provider_name, auth_result); | ||
|
||
apr_table_setn(req->notes, "AUTH_BASIC_FORWARD", "1"); | ||
|
||
if (auth_result == AUTH_GRANTED) { | ||
ret = OK; | ||
goto done; | ||
} | ||
|
||
ret = HTTP_UNAUTHORIZED; | ||
goto done; | ||
} | ||
|
||
|
||
if (mc->is_preserved && mc->established && | ||
mag_basic_check(req_cfg, mc, ba_user, ba_pwd)) { | ||
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req, | ||
|
@@ -1171,7 +1276,7 @@ static int mag_auth(request_rec *req) | |
"NTLM"); | ||
} | ||
} | ||
if (cfg->use_basic_auth) { | ||
if (cfg->use_basic_auth != BA_OFF) { | ||
apr_table_add(req->err_headers_out, req_cfg->rep_proto, | ||
apr_psprintf(req->pool, "Basic realm=\"%s\"", | ||
ap_auth_name(req))); | ||
|
@@ -1598,11 +1703,18 @@ static const char *mag_deleg_ccache_perms(cmd_parms *parms, void *mconfig, | |
#endif | ||
|
||
#ifdef HAVE_GSS_ACQUIRE_CRED_WITH_PASSWORD | ||
static const char *mag_use_basic_auth(cmd_parms *parms, void *mconfig, int on) | ||
static const char *mag_use_basic_auth(cmd_parms *parms, void *mconfig, | ||
const char *value) | ||
{ | ||
struct mag_config *cfg = (struct mag_config *)mconfig; | ||
|
||
cfg->use_basic_auth = on ? true : false; | ||
if (strcasecmp(value, "on") == 0) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not backwards compatible, you would need to handle all ways to "turn on" a boolean value, or existing configurations may break. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK i wasn't sure if this is a good idea and may break existing installations where gssapi suddenly starts forwarding auth. |
||
cfg->use_basic_auth = BA_ON; | ||
} else if (strcasecmp(value, "forward") == 0) { | ||
cfg->use_basic_auth = BA_FORWARD; | ||
} else { | ||
cfg->use_basic_auth = BA_OFF; | ||
} | ||
return NULL; | ||
} | ||
#endif | ||
|
@@ -1821,6 +1933,8 @@ static void *mag_create_server_config(apr_pool_t *p, server_rec *s) | |
} | ||
|
||
static const command_rec mag_commands[] = { | ||
AP_INIT_ITERATE("AuthBasicProvider", add_authn_provider, NULL, OR_AUTHCFG, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. change to GssapiBasicAuthProviders There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh I see this is an actual variable that is implemented for mod_auth_basic ... is this the canonical way to handle it, parsing it in each module that wants to deelgate basic auth to another provider ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure but as mounted out above all original modules implement add_authn_provider and use a specific one. Also ldap itself needs this variable. |
||
"specify the auth providers for a directory or location"), | ||
AP_INIT_FLAG("GssapiSSLonly", mag_ssl_only, NULL, OR_AUTHCFG, | ||
"Work only if connection is SSL Secured"), | ||
AP_INIT_FLAG("GssapiLocalName", mag_map_to_local, NULL, OR_AUTHCFG, | ||
|
@@ -1853,7 +1967,7 @@ static const command_rec mag_commands[] = { | |
"based on already authentication username"), | ||
#endif | ||
#ifdef HAVE_GSS_ACQUIRE_CRED_WITH_PASSWORD | ||
AP_INIT_FLAG("GssapiBasicAuth", mag_use_basic_auth, NULL, OR_AUTHCFG, | ||
AP_INIT_TAKE1("GssapiBasicAuth", mag_use_basic_auth, NULL, OR_AUTHCFG, | ||
"Allows use of Basic Auth for authentication"), | ||
AP_INIT_ITERATE("GssapiBasicAuthMech", mag_basic_auth_mechs, NULL, | ||
OR_AUTHCFG, "Mechanisms to use for basic auth"), | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,6 +19,9 @@ | |
#include <mod_session.h> | ||
#include <mod_ssl.h> | ||
|
||
#include <ap_provider.h> | ||
#include <mod_auth.h> | ||
|
||
/* apache's httpd.h drags in empty PACKAGE_* variables. | ||
* undefine them to avoid annoying compile warnings as they | ||
* are re-defined in config.h */ | ||
|
@@ -86,7 +89,11 @@ struct mag_config { | |
#endif | ||
struct seal_key *mag_skey; | ||
|
||
bool use_basic_auth; | ||
enum { | ||
BA_OFF = 0, | ||
BA_FORWARD = 1, | ||
BA_ON = 2 | ||
} use_basic_auth; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. drop this enum and just use the providers list below to decide whether forwarding will be done or not. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK |
||
gss_OID_set_desc *allowed_mechs; | ||
gss_OID_set_desc *basic_mechs; | ||
bool negotiate_once; | ||
|
@@ -95,6 +102,7 @@ struct mag_config { | |
bool enverrs; | ||
gss_name_t acceptor_name; | ||
bool acceptor_name_from_req; | ||
authn_provider_list *providers; | ||
}; | ||
|
||
struct mag_server_config { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please prefix with mag_
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also move this function below, among the functions that deal with configuration
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function was copy and pasted from the apache modules/aaa/mod_auth_basic.c file. The idea was to keep it 1:1 as the original function.
Currently each mod_auth_ module has implemented this function:
git grep add_authn_provider
modules/aaa/mod_auth_basic.c:static const char *add_authn_provider(cmd_parms *cmd, void *config,
modules/aaa/mod_auth_basic.c: AP_INIT_ITERATE("AuthBasicProvider", add_authn_provider, NULL, OR_AUTHCFG,
modules/aaa/mod_auth_digest.c:static const char *add_authn_provider(cmd_parms *cmd, void *config,
modules/aaa/mod_auth_digest.c: AP_INIT_ITERATE("AuthDigestProvider", add_authn_provider, NULL, OR_AUTHCFG,
modules/aaa/mod_auth_form.c:static const char *add_authn_provider(cmd_parms * cmd, void *config,
modules/aaa/mod_auth_form.c: AP_INIT_ITERATE("AuthFormProvider", add_authn_provider, NULL, OR_AUTHCFG,