From: jan@unixpapa.com Date: Fri, 15 May 2009 13:38:56 +0000 (+0000) Subject: initial import X-Git-Tag: mod_authz_unixgroup-1.0.1~1 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=5c4099eedfaf287cc4694973c90d1c3d7edb6712;p=apache-authnz-external initial import --- diff --git a/mod_authz_unixgroup/CHANGES b/mod_authz_unixgroup/CHANGES new file mode 100644 index 0000000..98e6c70 --- /dev/null +++ b/mod_authz_unixgroup/CHANGES @@ -0,0 +1,19 @@ +v1.0.1 (Jan Wolter - Aug 6, 2008) +------------------------------------ + * Delete various logging statements that were really just there for + debugging and should have been removed sooner. + + * If there is an '@' in the user's login name, strip off that and anything + after it. An '@' sign is never legal in an unix login name, and some + authentication modules, like mod_auth_kerb, append an "@domain" to the + user's login name. + + Both of the above modifications are inspired by patches made by Ken Lalonde + . + + * Included "http_request.h" header file to surpress a harmless compile-time + warning. + +v1.0.0 (Jan Wolter - Feb 19, 2006) +------------------------------------ + * Original release diff --git a/mod_authz_unixgroup/INSTALL b/mod_authz_unixgroup/INSTALL new file mode 100644 index 0000000..97fbd09 --- /dev/null +++ b/mod_authz_unixgroup/INSTALL @@ -0,0 +1,145 @@ +How to install mod_authz_unixgroup.c into Apache: + +NOTES: + + * There are two ways of installing mod_authz_unixgroup. + + (1) You can statically link it with Apache. This requires rebuilding + Apache in such a way that mod_authz_unixgroup will be compiled in. + + (2) You can make mod_authz_unixgroup a dynamically loaded module. If + your Apache has been built to support dynamically loaded modules + you can do this without rebuilding Apache, so it is pretty easy. + Performance may be slightly worse with this option. For information + on dynamically loaded modules see http://www.apache.org/docs/dso.html + + Instructions for both options are given here. + + * There is also documentation in the README file. If you find this document + unclear, reading that may help. + + +INSTALL METHOD A: Dynamically Linking Mod_authz_unixgroup using apxs: +--------------------------------------------------------------------- + +Step 1: + Ensure that your Apache server is configured to handle dynamically + loaded modules. To check this, run Apache server with the -l command + flag, like + + httpd -l + + If mod_so.c is one of the compiled-in modules, then you are ready + to go. + +Step 2: + Compile the module using the following command in the + mod_authz_unixgroup distribution directory: + + apxs -c mod_authz_unixgroup.c + + 'Apxs' is the Apache extension tool. It is part of the standard + Apache installation. If you don't have it, then your Apache server + is probably not set up for handling dynamically loaded modules. + This should create a file named 'mod_authz_unixgroup.so'. + +Step 3: + Install the module. Apxs can do this for you too. Do the following + command (as root so you can write to Apache's directories and config + files): + + apxs -i -a mod_authz_unixgroup.la + + This will create mod_authz_unixgroup.so and copy it into the proper + place, and add appropriate AddModule and LoadModule commands to the + configuration files. (Actually, it may get the LoadModule command + wrong. See below.) + +Step 4: + Go to the CONFIGURATION instructions below. + + +INSTALL METHOD B: Statically Linking +------------------------------------ + +Step 1: + Read the instructions on how to configure the Apache server in the + INSTALL file provided with the Apache source. + +Step 2: + When you run the ./configure script, include an --with-module flag, + giving the full pathname to the mod_authz_unixgroup.c file in this + distribution. For example, if you have unpacked this distribution + in /usr/local/src/mod_authz_unixgroup and are building Apache for + installation in /usr/local/apache, you might do: + + ./configure --prefix=/usr/local/apache \ + --with-module=aaa:/usr/local/src/mod_authz_unixgroup/mod_authz_unixgroup.c + + This will copy the mod_authz_unixgroup.c file into the correct place in + the Apache source tree and set things up to link it in. + +Step 3: + Type "make" to compile Apache and "make install" to install it. + +Step 4: + Go to the CONFIGURATION instructions below. + + +CONFIGURATION: +-------------- + +Mod_authz_unixgroup is pretty simple to use. First, you need to enable it +for whatever directory you want to use it in, by inserting the following +directive either in a .htaccess file in the directory or a block +in the httpd.conf file: + + AuthzUnixgroup on + +Second, you will need a require directive like + + Require group admin +or + Require group students teachers staff + +Obviously this only makes sense in a directory where you are doing +authentication. This could be any kind of authentication, but it makes +most sense if you are using it in combination with authentication out of +the unix password file, perhaps using mod_auth_external together with +pwauth, or mod_auth_shadow. The "Require group" directive will then +cause mod_authz_unixgroup to check if the user is in one of the groups +listed, and reject the authentication if they are not. A user is considered +to be in a group if either (1) the group is the user's primary group +identified by it's gid number in /etc/passwd, or (2) the group is listed +in /etc/group and the user id is listed as a member of that group. + +If you are authenticating out of something other than the unix password +database, then this can be used, but the effect is a bit odd. To pass +the "Require group" test, there must (1) exist a unix account with the same +name as the account the user authenticated in, and (2) that unix account must +be in one of the unix groups listed on the Require line. + +It is also possible to list groups by gid number instead of name, like + + Require group 10 + +would be equivalent to "Require group admin" if the gid listed for the group +admin in /etc/group is 10. + +If mod_authz_owner is enabled in your httpd, then that will work with +mod_authz_unixgroup to check access based on file groups. For example if +we do: + + AuthzUnixgroup on + Require file-group + +Then a user will be able to access a file if and only if that file is owned +by a group of which the user is a member. + +By default, mod_authz_unixgroup is authoritative. If you want to use more +than one group checker, like mod_authz_unixgroup together with +mod_authz_groupfile or mod_authz_dbm, then you'll want to make them non- +authoritative, so that if one fails, the other will be tried. You can +make mod_authz_unixgroup non-authoritative by saying: + + AuthzUnixgroupAuthoritative off diff --git a/mod_authz_unixgroup/README b/mod_authz_unixgroup/README new file mode 100644 index 0000000..5666e12 --- /dev/null +++ b/mod_authz_unixgroup/README @@ -0,0 +1,61 @@ + Mod_Authz_Unixgroup version 1.0.1 + + Author: Jan Wolter + Website: http://www.unixpapa.com/mod_authz_unixgroup/ + Requires: Apache 2.1 or later on a Unix server + +Mod_Authz_Unixgroup is a unix group access control modules for Apache 2.1 and +later. If you are having users authenticate with real Unix login ID over the +net, using something like my mod_authnz_external/pwauth combination, and you +want to do access control based on unix group membership, then +mod_authz_unixgroup is exactly what you need. + +Let's say that you were using this with mod_authnz_external and pwauth. Your +.htaccess file for a protected directory would probably start with the +following directives: + + AuthType Basic + AuthName mysite + AuthBasicProvider external + AuthExternal pwauth + +That would cause mod_auth_basic and mod_authnz_external to do authentication +based on the Unix passwd database. Mod_Authz_Unixgroup would come into play +if you wanted to further restrict access to specific Unix groups. You might +append the following directives: + + AuthzUnixgroup on + Require group staff admin + +This would allow only access to accounts in the 'staff' or 'admin' unix groups. +You can alternately specify groups by their gid numbers instead of their names. + +Or you could use mod_authz_unixgroup together with the standard apache module +mod_authz_owner to do something like: + + Require file-group + +This would allow access to the page, only the user was a member of the unix +group that owns the file. + +Though it makes the most sense to use this with unix passwd authentication, +it can be used with other databases. In that case it would grant access if, +(1) the name the user authenticated with exactly matched the name of a real +unix account on the server, and (2) that real unix account was in one of the +required groups. However, I think this would be a pretty senseless way to +use this module. I expect that it will really only be used by user of +mod_authnz_external/pwauth. + +Some authentication modules, like mod_auth_kerb, use usernames that have +domains appended to them, like "whomever@krb.ncsu.edu". In such cases, +mod_authz_unixgroup will take the part before the @-sign as the username +and ignore the rest. + +Mod_authnz_external is available from: + http://www.unixpapa.com/mod_auth_external/ + +Pwauth is available from: + http://www.unixpapa.com/pwauth/ + +It might also be possible to use this with mod_auth_shadow, expecially if a +authn/authz version of that is ever released. diff --git a/mod_authz_unixgroup/mod_authz_unixgroup.c b/mod_authz_unixgroup/mod_authz_unixgroup.c new file mode 100644 index 0000000..765d25c --- /dev/null +++ b/mod_authz_unixgroup/mod_authz_unixgroup.c @@ -0,0 +1,251 @@ +#include "apr_lib.h" + +#include "ap_config.h" +#include "ap_provider.h" +#include "mod_auth.h" + +#define APR_WANT_STRFUNC +#include "apr_want.h" +#include "apr_strings.h" + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_log.h" +#include "http_protocol.h" +#include "http_request.h" /* for ap_hook_(check_user_id | auth_checker)*/ +#if HAVE_PWD_H +#include +#endif +#if HAVE_GRP_H +#include +#endif +#if APR_HAVE_UNISTD_H +#include +#endif + +/* + * Structure for the module itself. The actual definition of this structure + * is at the end of the file. + */ +module AP_MODULE_DECLARE_DATA authz_unixgroup_module; + +/* + * Data type for per-directory configuration + */ + +typedef struct +{ + int enabled; + int authoritative; + +} authz_unixgroup_dir_config_rec; + + +/* + * Creator for per-dir configurations. This is called via the hook in the + * module declaration to allocate and initialize the per-directory + * configuration data structures declared above. + */ + +static void *create_authz_unixgroup_dir_config(apr_pool_t *p, char *d) +{ + authz_unixgroup_dir_config_rec *dir= (authz_unixgroup_dir_config_rec *) + apr_palloc(p, sizeof(authz_unixgroup_dir_config_rec)); + + dir->enabled= 0; + dir->authoritative= 1; /* strong by default */ + + return dir; +} + + +/* + * Config file commands that this module can handle + */ + +static const command_rec authz_unixgroup_cmds[] = +{ + AP_INIT_FLAG("AuthzUnixgroup", + ap_set_flag_slot, + (void *)APR_OFFSETOF(authz_unixgroup_dir_config_rec, enabled), + OR_AUTHCFG, + "Set to 'on' to enable unix group checking"), + + AP_INIT_FLAG("AuthzUnixgroupAuthoritative", + ap_set_flag_slot, + (void *)APR_OFFSETOF(authz_unixgroup_dir_config_rec, authoritative), + OR_AUTHCFG, + "Set to 'off' to allow access control to be passed along to lower " + "modules if this module can't confirm access rights" ), + + { NULL } +}; + + +/* Check if the named user is in the given list of groups. The list of + * groups is a string with groups separated by white space. Group ids + * can either be unix group names or numeric group id numbers. There must + * be a unix login corresponding to the named user. + */ + +static int check_unix_group(request_rec *r, const char *grouplist) +{ + char **p; + struct group *grp; + char *user= r->user; + char *w, *at; + + /* Strip @ sign and anything following it from the username. Some + * authentication modules, like mod_auth_kerb like appending such + * stuff to user names, but an @ sign is never legal in a unix login + * name, so it should be safe to always discard such stuff. + */ + if ((at= strchr(user, '@')) != NULL) *at= '\0'; + + /* Get info about login */ + struct passwd *pwd= getpwnam(user); + if (pwd == NULL) + { + /* No such user - forget it */ + if (at != NULL) *at= '@'; + return 0; + } + + /* Loop through list of groups passed in */ + while (*grouplist != '\0') + { + w= ap_getword_white(r->pool, &grouplist); + if (apr_isdigit(w[0])) + { + /* Numeric group id */ + int gid= atoi(w); + + /* Check if it matches the user's primary group */ + if (gid == pwd->pw_gid) + { + if (at != NULL) *at= '@'; + return 1; + } + + /* Get list of group members for numeric group id */ + grp= getgrgid(gid); + } + else + { + /* Get gid and list of group members for group name */ + grp= getgrnam(w); + /* Check if gid of this group matches user's primary gid */ + if (grp != NULL && grp->gr_gid == pwd->pw_gid) + { + if (at != NULL) *at= '@'; + return 1; + } + } + + /* Walk through list of members, seeing if any match user login */ + if (grp != NULL) + for (p= grp->gr_mem; *p != NULL; p++) + { + if (!strcmp(user, *p)) + { + if (at != NULL) *at= '@'; + return 1; + } + } + } + + /* Didn't find any matches, flunk him */ + if (at != NULL) *at= '@'; + return 0; +} + + +static int authz_unixgroup_check_user_access(request_rec *r) +{ + authz_unixgroup_dir_config_rec *dir= (authz_unixgroup_dir_config_rec *) + ap_get_module_config(r->per_dir_config, &authz_unixgroup_module); + + int m= r->method_number; + int required_group= 0; + register int x; + const char *t, *w; + const apr_array_header_t *reqs_arr= ap_requires(r); + const char *filegroup= NULL; + require_line *reqs; + + /* If not enabled, pass */ + if ( !dir->enabled ) return DECLINED; + + /* If there are no Require arguments, pass */ + if (!reqs_arr) return DECLINED; + reqs= (require_line *)reqs_arr->elts; + + /* Loop through the "Require" argument list */ + for(x= 0; x < reqs_arr->nelts; x++) + { + if (!(reqs[x].method_mask & (AP_METHOD_BIT << m))) continue; + + t= reqs[x].requirement; + w= ap_getword_white(r->pool, &t); + + /* The 'file-group' directive causes mod_authz_owner to store the + * group name of the file we are trying to access in a note attached + * to the request. It's our job to decide if the user actually is + * in that group. If the note is missing, we just ignore it. + * Probably mod_authz_owner is not installed. + */ + if ( !strcasecmp(w, "file-group")) + { + filegroup= apr_table_get(r->notes, AUTHZ_GROUP_NOTE); + if (filegroup == NULL) continue; + } + + if ( !strcmp(w,"group") || filegroup != NULL) + { + required_group= 1; + + if (filegroup) + { + /* Check if user is in the group that owns the file */ + if (check_unix_group(r,filegroup)) + return OK; + } + else if (t[0]) + { + /* Pass rest of require line to authenticator */ + if (check_unix_group(r,t)) + return OK; + } + } + } + + /* If we didn't see a 'require group' or aren't authoritive, decline */ + if (!required_group || !dir->authoritative) + return DECLINED; + + /* Authentication failed and we are authoritive, declare unauthorized */ + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "access to %s failed, reason: user %s not allowed access", + r->uri, r->user); + + ap_note_basic_auth_failure(r); + return HTTP_UNAUTHORIZED; +} + +static void authz_unixgroup_register_hooks(apr_pool_t *p) +{ + ap_hook_auth_checker(authz_unixgroup_check_user_access, NULL, NULL, + APR_HOOK_MIDDLE); +} + + +module AP_MODULE_DECLARE_DATA authz_unixgroup_module = { + STANDARD20_MODULE_STUFF, + create_authz_unixgroup_dir_config, /* create per-dir config */ + NULL, /* merge per-dir config */ + NULL, /* create per-server config */ + NULL, /* merge per-server config */ + authz_unixgroup_cmds, /* command apr_table_t */ + authz_unixgroup_register_hooks /* register hooks */ +};