Skip to content

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

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
192 changes: 153 additions & 39 deletions src/mod_auth_gssapi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please prefix with mag_

Copy link
Contributor

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

Copy link
Author

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,

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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

} else {

please

Copy link
Author

Choose a reason for hiding this comment

The 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?

Copy link
Member

@frozencemetery frozencemetery Oct 23, 2017

Choose a reason for hiding this comment

The 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.

Copy link
Contributor

Choose a reason for hiding this comment

The 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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use for loop here please:
/* append to the end */
for (authn_provider_list *last = conf->providers; last->next; last = last->next);
last->next = newp;

Copy link
Author

Choose a reason for hiding this comment

The 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;
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);
Copy link
Contributor

@simo5 simo5 Sep 26, 2017

Choose a reason for hiding this comment

The 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 ....) {
ap_log_rerror(....)
skip_subreq = true;
}
if ((!skip_subreq) && (type != NULL) && (strcasecmp(type, "GSSAPI") == 0)) {

Copy link
Author

Choose a reason for hiding this comment

The 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.");
}
}

Expand Down Expand Up @@ -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;
}

Expand All @@ -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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

call this 'provider', initialize it to NULL

Copy link
Author

Choose a reason for hiding this comment

The 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;
Copy link
Contributor

Choose a reason for hiding this comment

The 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.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK


provider = current_provider->provider;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will segfault if cfg->providers == NULL

Copy link
Author

Choose a reason for hiding this comment

The 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) {
Copy link
Contributor

Choose a reason for hiding this comment

The 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,
Expand Down Expand Up @@ -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)));
Expand Down Expand Up @@ -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) {
Copy link
Contributor

Choose a reason for hiding this comment

The 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.
Rather than changing the meaning of GssapiBasicAuth, I would use the presence of basic auth providers to decide if forwarding should be done.

Copy link
Author

Choose a reason for hiding this comment

The 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
Expand Down Expand Up @@ -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,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

change to GssapiBasicAuthProviders

Copy link
Contributor

Choose a reason for hiding this comment

The 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 ?

Copy link
Author

Choose a reason for hiding this comment

The 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,
Expand Down Expand Up @@ -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"),
Expand Down
10 changes: 9 additions & 1 deletion src/mod_auth_gssapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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;
Copy link
Contributor

Choose a reason for hiding this comment

The 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.

Copy link
Author

Choose a reason for hiding this comment

The 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;
Expand All @@ -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 {
Expand Down