The GroupExternal cannot (yet?) be used with multiple external
authenticators.
+ * USE WITH MOD_AUTHN_SOCACHE
+
+ Mod_authnz_external version 3.3.1 and later can be used with the
+ Apache mod_authn_socache module, which caches authentications. If
+ you do this, then after a successful authentication, mod_socache
+ will remember the user for a settable time (5 minutes by default)
+ and not rerun the external authenticator again to check their password
+ until after the timeout. This can be a very substantial performance
+ improvement.
+
+ It can also be a very substantial security problem. One common use of
+ mod_authnz_external is to authenticate from databases that are not readable
+ by Apache, and should not be. For example, if you are authenticating out
+ of the a unix password file with pwauth, you don't want make the password
+ file readable to Apache because then an Apache bug would risk exposing
+ your entire password file to the net. But if you turn on caching with
+ mod_authn_socache, then the cache it builds up is essentially an
+ Apache-readable copy of the most sensitive data from your password file.
+ With some settings, it may even be stored on disk rather than on memory.
+ The only good thing you can say for it is that all the passwords in that
+ cache will be encrypted (even if you are dopey enough not to encrypt them
+ in your actual password database). But encryption is a pretty weak
+ defense all by itself.
+
+ So using mod_authnz_external with mod_authn_socache might be dumb, but,
+ what the heck, when have we passed up a chance to give you more rope to
+ hang yourself with?
+
+ One note: normally when you use mod_authn_socache with one of the standard
+ Apache modules, a cache entry is created everytime it looks up a user's
+ password in the database, even if the password they submitted wasn't
+ the correct one. With mod_authnz_external it only happens after
+ successful authentications. That's because mod_authnz_external doesn't
+ have direct access to the password database. After a successful
+ authentication we can fake-up something that looks to mod_authn_socache
+ like some credentials out of a database by simple encrypting the password
+ that the user sent us and pretending we got that out of a database. This
+ means we don't get quite the performance gains that mod_authn_socache
+ would give with something like mod_authn_dbd, but we get pretty close.
+
+ So here's how you do it. First you AuthBasicProvider statement should
+ list both 'socache' and 'external', and it's important that 'socache'
+ should be listed first, so that it tries to look up users in the cache
+ before mod_authnz_external runs the authenticator:
+
+ AuthBasicProvider socache external
+
+ Then you need to tell mod_authnz_external to start forging credentials
+ for mod_authn_socache:
+
+ AuthExternalProvideCache On
+
+ And you need to tell mod_authn_socache to accept credentials from
+ mod_authnz_external:
+
+ AuthnCacheProvideFor external
+
+ And that should do it. You should see many fewer runs of the external
+ authenticator, and perhaps a slight decline in your overall security.
+
* PASSING CONTEXT INFORMATION INTO AUTHENTICATORS:
If you want the authentication to work slightly differently in
#define APR_WANT_STRFUNC
#include "apr_want.h"
#include "apr_strings.h"
+#include "apr_sha1.h"
#include "httpd.h"
#include "http_config.h"
apr_array_header_t *auth_name; /* Auth keyword for current dir */
char *group_name; /* Group keyword for current dir */
char *context; /* Context string from AuthExternalContext */
- int groupsatonce; /* Check all groups in one call in this dir? */
+ int groupsatonce; /* Check all groups in one call? */
+ int providecache; /* Provide auth data to mod_authn_socache? */
} authnz_external_dir_config_rec;
} authnz_external_svr_config_rec;
-/* A handle for retrieving the requested file's group from mod_authnz_owner */
+/* mod_authz_owner's function for retrieving the requested file's group */
APR_DECLARE_OPTIONAL_FN(char*, authz_owner_get_file_group, (request_rec *r));
+APR_OPTIONAL_FN_TYPE(authz_owner_get_file_group) *authz_owner_get_file_group;
-/*
- * Creators for per-dir and server configurations. These are called
+/* mod_authn_socache's function for adding credentials to its cache */
+static APR_OPTIONAL_FN_TYPE(ap_authn_cache_store) *authn_cache_store = NULL;
+
+
+/* Creators for per-dir and server configurations. These are called
* via the hooks in the module declaration to allocate and initialize
* the per-directory and per-server configuration data structures declared
- * above.
- */
+ * above. */
static void *create_authnz_external_dir_config(apr_pool_t *p, char *d)
{
authnz_external_dir_config_rec *dir= (authnz_external_dir_config_rec *)
apr_palloc(p, sizeof(authnz_external_dir_config_rec));
- dir->auth_name= apr_array_make(p,2,sizeof(const char *)); /* no default */
+ dir->auth_name= apr_array_make(p,2,sizeof(const char *)); /* no default */
dir->group_name= NULL; /* no default */
dir->context= NULL; /* no default */
dir->groupsatonce= 1; /* default to on */
+ dir->providecache= 0; /* default to off */
return dir;
}
-
static void *create_authnz_external_svr_config( apr_pool_t *p, server_rec *s)
{
authnz_external_svr_config_rec *svr= (authnz_external_svr_config_rec *)
return (void *)svr;
}
-/*
- * Handler for a DefineExternalAuth server config line
- */
-
+/* Handler for a DefineExternalAuth server config line */
static const char *def_extauth(cmd_parms *cmd, void *dummy, const char *keyword,
const char *method, const char *path)
{
}
-/*
- * Handler for a DefineExternalGroup server config line
- */
-
+/* Handler for a DefineExternalGroup server config line */
static const char *def_extgroup(cmd_parms *cmd, void *dummy,
const char *keyword, const char *method, const char *path)
{
-/*
- * Handler for a AddExternalAuth server config line - add a external auth
- * type to the server configuration
- */
-
+/* Handler for a AddExternalAuth server config line - add a external auth
+ * type to the server configuration */
static const char *add_extauth(cmd_parms *cmd, void *dummy, const char *keyword,
const char *path)
{
}
-/*
- * Handler for a AddExternalGroup server config line - add a external group
- * type to the server configuration
- */
-
+/* Handler for a AddExternalGroup server config line - add a external group
+ * type to the server configuration */
static const char *add_extgroup(cmd_parms *cmd, void *dummy,
const char *keyword, const char *path)
{
return NULL;
}
-/*
- * Handler for a SetExternalAuthMethod server config line - change an external
- * auth method in the server configuration
- */
-
+/* Handler for a SetExternalAuthMethod server config line - change an external
+ * auth method in the server configuration */
static const char *set_authnz_external_method(cmd_parms *cmd, void *dummy,
- const char *keyword, const char *method)
+ const char *keyword, const char *method)
{
authnz_external_svr_config_rec *svr= (authnz_external_svr_config_rec *)
ap_get_module_config( cmd->server->module_config,
}
-/*
- * Handler for a SetExternalGroupMethod server config line - change an external
- * group method in the server configuration
- */
-
+/* Handler for a SetExternalGroupMethod server config line - change an external
+ * group method in the server configuration */
static const char *set_extgroup_method(cmd_parms *cmd, void *dummy,
- const char *keyword, const char *method)
+ const char *keyword, const char *method)
{
authnz_external_svr_config_rec *svr= (authnz_external_svr_config_rec *)
ap_get_module_config( cmd->server->module_config,
}
-/*
- * Config file commands that this module can handle
- */
-
+/* Config file directives for this module */
static const command_rec authnz_external_cmds[] =
{
AP_INIT_ITERATE("AuthExternal",
"An arbitrary context string to pass to the authenticator in the "
ENV_CONTEXT " environment variable"),
+ AP_INIT_FLAG("AuthExternalProvideCache",
+ ap_set_flag_slot,
+ (void *)APR_OFFSETOF(authnz_external_dir_config_rec, providecache),
+ OR_AUTHCFG,
+ "Should we forge authentication credentials for mod_authn_socache?"),
+
AP_INIT_FLAG("GroupExternalManyAtOnce",
ap_set_flag_slot,
(void *)APR_OFFSETOF(authnz_external_dir_config_rec, groupsatonce),
/* Called from apr_proc_create() if there are errors during launch of child
- * process. Mostly just lifted from mod_cgi.
- */
-
+ * process. Mostly just lifted from mod_cgi. */
static void extchilderr(apr_pool_t *p, apr_status_t err, const char *desc)
{
apr_file_t *stderr_log;
}
-/*
- * Run an external authentication program using the given method for passing
+/* Run an external authentication program using the given method for passing
* in the data. The login name is always passed in. Dataname is "GROUP" or
* "PASS" and data is the group list or password being checked. To launch
* a detached daemon, run this with extmethod=NULL.
* -4 apr_proc_wait() did not return a status code. Should never happen.
* -5 apr_proc_wait() returned before child finished. Should never happen.
*/
-
static int exec_external(const char *extpath, const char *extmethod,
const request_rec *r, const char *dataname, const char *data)
{
* into this source file, as well as inserting a call to them into this
* routine.
*/
-
static int exec_hardcode(const request_rec *r, const char *extpath,
const char *password)
{
}
+/* Handle a group check triggered by a 'Require external-group foo bar baz'
+ * directive. */
static authz_status externalgroup_check_authorization(request_rec *r,
const char *require_args, const void *parsed_require_args)
{
return AUTHZ_DENIED;
}
-APR_OPTIONAL_FN_TYPE(authz_owner_get_file_group) *authz_owner_get_file_group;
+/* Handle a group check triggered by a 'Require external-file-group'
+ * directive. */
static authz_status externalfilegroup_check_authorization(request_rec *r,
const char *require_args, const void *parsed_require_args)
{
}
+/* Mod_authn_socache wants us to pass it the username and the encrypted
+ * password from the user database to cache. But we have no access to the
+ * actual user database - only the external authenticator can see that -
+ * and chances are, the passwords there aren't encrypted in any way that
+ * mod_authn_socache would understand anyway. So instead, after successful
+ * authentications only, we take the user's plain text password, encrypt
+ * that using an algorithm mod_authn_socache will understand, and cache that
+ * as if we'd actually gotten it from a password database.
+ */
+void mock_turtle_cache(request_rec *r, const char *plainpw)
+{
+ char cryptpw[120];
+
+ /* Authn_cache_store will be null if mod_authn_socache does not exist.
+ * If it does exist, but is not set up to cache us, then
+ * authn_cache_store() will do nothing, which is why we turn this off
+ * with "AuthExternalProvideCache Off" to avoid doing the encryption
+ * for no reason. */
+ if (authn_cache_store != NULL)
+ {
+ apr_sha1_base64(plainpw,strlen(plainpw),cryptpw);
+ authn_cache_store(r, "external", r->user, NULL, cryptpw);
+ }
+}
+
+
/* Password checker for basic authentication - given a login/password,
* check if it is valid. Returns one of AUTH_DENIED, AUTH_GRANTED,
- * or AUTH_GENERAL_ERROR.
- */
+ * or AUTH_GENERAL_ERROR. */
static authn_status authn_external_check_password(request_rec *r,
const char *user, const char *password)
code= exec_external(extpath, extmethod, r, ENV_PASS, password);
/* If return code was zero, authentication succeeded */
- if (code == 0) return AUTH_GRANTED;
+ if (code == 0)
+ {
+ if (dir->providecache) mock_turtle_cache(r, password);
+ return AUTH_GRANTED;
+ }
/* Log a failed authentication */
errno= 0;
#if 0
/* Password checker for digest authentication - given a login/password,
* check if it is valid. Returns one of AUTH_USER_FOUND, AUTH_USER_NOT_FOUND,
- * or AUTH_GENERAL_ERROR. Not implemented at this time.
+ * or AUTH_GENERAL_ERROR. Not implemented at this time and probably not ever.
*/
auth_status *authn_external_get_realm_hash(request_rec *r, const char *user,
}
#endif
+/* This is called after all modules have been initialized to acquire pointers
+ * to some functions from other modules that we would like to use if they are
+ * available. */
+static void opt_retr(void)
+{
+ /* Get authn_cache_store from mod_authn_socache */
+ authn_cache_store=
+ APR_RETRIEVE_OPTIONAL_FN(ap_authn_cache_store);
+
+ /* Get authz_owner_get_file_group from mod_authz_owner */
+ authz_owner_get_file_group=
+ APR_RETRIEVE_OPTIONAL_FN(authz_owner_get_file_group);
+}
+/* This tells mod_auth_basic and mod_auth_digest what to call for
+ * authentication. */
static const authn_provider authn_external_provider =
{
&authn_external_check_password,
#if 0
&authn_external_get_realm_hash
#else
- NULL /* No support for digest authentication at this time */
+ NULL /* No support for digest authentication */
#endif
};
+/* This tells mod_auth_basic and mod_auth_digest what to call for
+ * access control with 'Require external-group' directives. */
static const authz_provider authz_externalgroup_provider =
{
&externalgroup_check_authorization,
NULL,
};
+/* This tells mod_auth_basic and mod_auth_digest what to call for
+ * access control with 'Require external-file-group' directives. */
static const authz_provider authz_externalfilegroup_provider =
{
&externalfilegroup_check_authorization,
NULL,
};
+/* Register this module with Apache */
static void register_hooks(apr_pool_t *p)
{
- /* Get a handle on mod_authz_owner */
- authz_owner_get_file_group = APR_RETRIEVE_OPTIONAL_FN(authz_owner_get_file_group);
-
/* Register authn provider */
ap_register_auth_provider(p, AUTHN_PROVIDER_GROUP, "external",
AUTHN_PROVIDER_VERSION,
ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "external-file-group",
AUTHZ_PROVIDER_VERSION,
&authz_externalfilegroup_provider, AP_AUTH_INTERNAL_PER_CONF);
+
+ /* Ask for opt_retr() to be called after all modules have registered */
+ ap_hook_optional_fn_retrieve(opt_retr, NULL, NULL, APR_HOOK_MIDDLE);
}