--- mod_auth_pwcheck.c Sat Jul 13 03:38:20 2002 +++ mod_auth_pwcheck.c Fri Apr 16 12:26:07 2004 @@ -14,6 +14,7 @@ #include #include #include +#include #include #include "httpd.h" @@ -21,8 +22,9 @@ #include "http_core.h" #include "http_log.h" #include "http_protocol.h" +#include "http_request.h" -#define MAX_RESPONSE 1024 +#define MAX_RESPONSE 256 #define SOCK_PATH "/var/pwcheck/pwcheck" @@ -30,47 +32,44 @@ /* internal configuration data */ typedef struct { - int auth_pwcheck_enabled; /* contains true/false */ - int auth_pwcheck_authoritative; /* contains true/false */ + int enabled; /* contains true/false */ + int authoritative; /* contains true/false */ int use_system_group_file; /* contains true/false */ -} auth_pwcheck_config_rec; +} config_rec; -static void* auth_pwcheck_create_config( pool*, char* ); -static const char* auth_pwcheck_command_handler( cmd_parms*, auth_pwcheck_config_rec*, int ); -static int auth_pwcheck_authenticate( request_rec* ); +static void* create_config(apr_pool_t*, char*); +static int authenticate(request_rec*); +static void register_hooks(apr_pool_t*); int retry_writev( int, struct iovec*, int ); -static int auth_pwcheck_authorize( request_rec* ); +static int authorize(request_rec*); /*-----------------------------------------------------------------------*/ /* config commands */ -static command_rec auth_pwcheck_config_cmds[] = { - { +static command_rec config_cmds[] = { + AP_INIT_FLAG( "AuthPwcheck", - auth_pwcheck_command_handler, - NULL, + ap_set_flag_slot, + (void *)APR_OFFSETOF(config_rec, enabled), OR_AUTHCFG, - FLAG, "authenticate user using pwcheck daemon" - }, - { + ), + AP_INIT_FLAG( "AuthPwcheckAuthoritative", - auth_pwcheck_command_handler, - NULL, + ap_set_flag_slot, + (void *)APR_OFFSETOF(config_rec, authoritative), OR_AUTHCFG, - FLAG, "If set to Off, access control is passed to subsequent auth modules " "when mod_auth_pwcheck cannot authenticate or authorize the user." - }, - { + ), + AP_INIT_FLAG( "UseSystemGroupFile", - auth_pwcheck_command_handler, - NULL, + ap_set_flag_slot, + (void *)APR_OFFSETOF(config_rec, use_system_group_file), OR_AUTHCFG, - FLAG, "whether to use system group file instead of AuthGroupFile" - }, + ), { NULL } @@ -78,78 +77,59 @@ /* module */ module auth_pwcheck_module = { - STANDARD_MODULE_STUFF, - NULL, /* module initializer */ - auth_pwcheck_create_config, /* dir config creator */ + STANDARD20_MODULE_STUFF, + create_config, /* dir config creator */ NULL, /* dir config merger */ NULL, /* server config creator */ NULL, /* server config merger */ - auth_pwcheck_config_cmds, /* config command table */ - NULL, /* response handlers table */ - NULL, /* filename-to-URI translation */ - auth_pwcheck_authenticate, /* check user id */ - auth_pwcheck_authorize, /* authorize user */ - NULL, /* check host access */ - NULL, /* check/set MIME type */ - NULL, /* fixups */ - NULL, /* logger */ - NULL, /* header parser */ - NULL, /* process initializer */ - NULL, /* process exit/cleanup */ - NULL /* post read_request handling */ + config_cmds, /* config command table */ + register_hooks, /* register hooks */ }; - /* config creator */ -static void *auth_pwcheck_create_config( pool *p, char *dir_name ) +static void * +create_config(apr_pool_t *p, char *dir_name) { - auth_pwcheck_config_rec *new_conf = ap_pcalloc( p, sizeof( auth_pwcheck_config_rec ) ); - new_conf->auth_pwcheck_authoritative = 1; - new_conf->auth_pwcheck_enabled = 0; + config_rec *new_conf = apr_palloc(p, sizeof(config_rec)); + new_conf->authoritative = 1; + new_conf->enabled = 0; new_conf->use_system_group_file = 0; return new_conf; } - -/* config command handler */ -static const char *auth_pwcheck_command_handler( cmd_parms *cmd, auth_pwcheck_config_rec *struct_ptr, int arg ) +/* register our two routines */ +static void +register_hooks(apr_pool_t *p) { - if( strcmp( cmd->cmd->name, "AuthPwcheck" ) == 0 ){ - struct_ptr->auth_pwcheck_enabled = arg; - }else if( strcmp( cmd->cmd->name, "AuthPwcheckAuthoritative" ) == 0 ){ - struct_ptr->auth_pwcheck_authoritative = arg; - }else{ - struct_ptr->use_system_group_file = arg; - } - return NULL; + ap_hook_check_user_id(authenticate, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_auth_checker(authorize, NULL, NULL, APR_HOOK_MIDDLE); } - /* Authenticates user (checks ID and password). Returns: - OK if authentication was successful, -- AUTH_REQUIRED, if authentication failed, -- SERVER_ERROR, if authentication is not possible, +- HTTP_UNAUTHORIZED, if authentication failed, +- HTTP_INTERNAL_SERVER_ERROR, if authentication is not possible, - DECLINED if the module is not turned on via the configuration. */ -static int auth_pwcheck_authenticate( request_rec *r ) +static int +authenticate(request_rec *r) { - auth_pwcheck_config_rec *dir_config; // our per-dir configuration - conn_rec *c = r->connection; // connection data - const char *sent_pw; // user-supplied password + const config_rec *dir_config; // our per-dir configuration int res; // ap_get_basic_auth return code int s; // socket descriptor struct sockaddr_un srvaddr; // socket address (in the communication space) - struct iovec iov[ 10 ]; // write buffers array - static char response[ MAX_RESPONSE ]; // pwcheck reply + struct iovec iov[2]; // write buffers array + char response[MAX_RESPONSE]; // pwcheck reply int start, n; // counters // obtaining password supplied by user: - if( ( res = ap_get_basic_auth_pw( r, &sent_pw ) ) != OK ){ + if((res = ap_get_basic_auth_pw(r, (const char **)&iov[1].iov_base)) != OK) { return res; } - dir_config = ( auth_pwcheck_config_rec * )ap_get_module_config( r->per_dir_config, &auth_pwcheck_module ); - if( dir_config->auth_pwcheck_enabled == 0 ){ + dir_config = (const config_rec *)ap_get_module_config(r->per_dir_config, + &auth_pwcheck_module); + if( dir_config->enabled == 0 ){ // AuthPwcheck is not turned on in this directory return DECLINED; } @@ -158,26 +138,25 @@ errno = 0; /* clear errno just in case */ s = socket(AF_UNIX, SOCK_STREAM, 0); if( s == -1 ){ - ap_log_rerror( APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, - "can't open socket for pwcheck server: %s", strerror( errno ) ); + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, + "can't open socket for pwcheck server: %s", strerror(errno)); ap_note_basic_auth_failure( r ); - return SERVER_ERROR; + return HTTP_INTERNAL_SERVER_ERROR; } memset( ( char * )&srvaddr, 0, sizeof( srvaddr ) ); srvaddr.sun_family = AF_UNIX; strcpy( srvaddr.sun_path, SOCK_PATH ); if( connect( s, ( struct sockaddr * )&srvaddr, sizeof( srvaddr ) ) == -1 ) { - ap_log_rerror( APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, - "can't connect to pwcheck server: %s", strerror( errno ) ); + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, + "can't connect to pwcheck server: %s", strerror(errno)); ap_note_basic_auth_failure( r ); - return SERVER_ERROR; + return HTTP_INTERNAL_SERVER_ERROR; } // initializing data to pass to pwcheck: - iov[0].iov_base = c->user; - iov[0].iov_len = strlen( c->user ) + 1; - iov[1].iov_base = ( char * )sent_pw; - iov[1].iov_len = strlen( sent_pw ) + 1; + iov[0].iov_base = r->user; + iov[0].iov_len = strlen( r->user ) + 1; + iov[1].iov_len = strlen(iov[1].iov_base) + 1; // passing data to pwcheck: retry_writev( s, iov, 2 ); @@ -194,12 +173,13 @@ if( strcmp( response, "OK" ) == 0 ){ // authentication succeeded - }else if( dir_config->auth_pwcheck_authoritative ){ + return OK; + }else if( dir_config->authoritative ){ // authentication failed - ap_log_rerror( APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, - "auth for user %s failed: %s", c->user, response ); + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, + "auth for user %s failed: %s", r->user, response); ap_note_basic_auth_failure( r ); - return AUTH_REQUIRED; + return HTTP_UNAUTHORIZED; }else{ return DECLINED; } @@ -249,19 +229,29 @@ Returns: - DECLINED if "required" keyword contains something else than system group, - OK if the user belongs to the required system group, -- AUTH_REQUIRED, if the user is not authorized to access. */ -static int auth_pwcheck_authorize( request_rec *r ){ +- HTTP_UNAUTHORIZED, if the user is not authorized to access. */ +static int +authorize(request_rec *r) +{ int i, j; // loop variables - const array_header *reqs_array; // array of auth requirements - require_line *reqs_array_elem; // reqs_array element + const apr_array_header_t *reqs_array; // array of auth requirements + const require_line *reqs_array_elem; // reqs_array element + int m = r->method_number; int method_restricted = 0; // flag: 1 if there's at least one // requirement for the given method - struct group *real_group; // group structure for the required group - const char *line, *word; // a text line and a word within it - auth_pwcheck_config_rec *dir_config; // our per-dir configuration + const struct group *real_group; // group structure for the required group + const struct passwd *real_user; // passwd structure for the required user + const char *line, *word; // a text line and a word within it + const config_rec *dir_config; // our per-dir configuration + char * const *member; // Group member(s) + enum { + FILE_GROUP, + FILE_OWNER + } file_req; // The kind of the file-* requirement - dir_config = ( auth_pwcheck_config_rec* )ap_get_module_config( r->per_dir_config, &auth_pwcheck_module ); + dir_config = (const config_rec *)ap_get_module_config(r->per_dir_config, + &auth_pwcheck_module); if( dir_config->use_system_group_file == 0 ){ // system group authorization is not required, // we leave other authorization types to @@ -277,9 +267,18 @@ for( i = 0; i < reqs_array->nelts; i++ ){ + if (!(reqs_array_elem[i].method_mask & (AP_METHOD_BIT << m))) { + continue; + } + + method_restricted = 1; + line = reqs_array_elem[ i ].requirement; word = ap_getword_white( r->pool, &line ); - if( strcmp( word, "group" ) == 0 ){ + switch (*word++) { + case 'g': + if (strcmp(word, "roup")) + continue; // searching user in all equired groups: while( line[ 0 ] != '\0' ){ word = ap_getword_conf( r->pool, &line ); @@ -290,35 +289,155 @@ }else{ // checking user against a group: for( j = 0; real_group->gr_mem[ j ] != NULL; j++ ){ - if( strcmp( real_group->gr_mem[ j ], r->connection->user ) == 0 ){ + if (strcmp(real_group->gr_mem[j], + r->user) == 0) { // authorization succeeded return OK; } } } } - }else if( strcmp( word, "user" ) ){ + continue; + + case 'u': + if (strcmp(word, "ser")) + continue; while( line[ 0 ] != '\0' ){ word = ap_getword_conf( r->pool, &line ); - if( strcmp( word, r->connection->user ) == 0 ){ + if (strcmp(word, r->user) == 0) { return OK; } } - }else if( strcmp( word, "valid-user" ) ){ - return OK; - }else if( dir_config->auth_pwcheck_authoritative ){ - ap_log_rerror( APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, - "%s: module mod_auth_pwcheck does not support \"%s\" require directive.", - r->uri, word ); + continue; + + case 'v': + if (strcmp(word, "alid-user") == 0) + return OK; + continue; + + case 'f': + if (strcmp(word, "ile-owner") == 0) + file_req = FILE_OWNER; + else if (strcmp(word, "ile-group") == 0) + file_req = FILE_GROUP; + else + continue; + + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, + 0, r, + "processing the %s requirement on %s for %s", + word - 1, r->user, r->filename); + + if (!r->finfo.valid) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, + 0, r, "no stat info for '%s'", r->filename); + continue; + } + + /* Need user's record in both cases */ + real_user = getpwuid(r->finfo.user); + if (real_user == NULL) { + ap_log_rerror(APLOG_MARK, + APLOG_NOERRNO|APLOG_ERR, + 0, r, "can't obtain the user record " + "uid %lu: %s", (unsigned long)r->finfo.user, + strerror(errno)); + return HTTP_INTERNAL_SERVER_ERROR; + } + + switch (file_req) { + case FILE_OWNER: + /* Handle the file-owner requirement */ + + if (strcmp(real_user->pw_name, r->user) == 0) + return OK; + continue; + + case FILE_GROUP: + /* Handle the file-group requirement */ + + if (r->finfo.group == real_user->pw_gid) { + /* + * We are lucky, the file belongs to + * the user's primary group + */ + return OK; + } + + /* + * Not so lucky, see if the file's group + * is among the user's secondary groups + */ + real_group = getgrgid(r->finfo.group); + if (real_group == NULL) { + if (errno) { + ap_log_rerror(APLOG_MARK, + APLOG_NOERRNO|APLOG_ERR, + 0, r, "can't obtain the " + "group record " + "gid %lu: %s", + (unsigned long)r->finfo.group, + strerror(errno)); + return HTTP_INTERNAL_SERVER_ERROR; + } + ap_log_rerror(APLOG_MARK, + APLOG_NOERRNO|APLOG_DEBUG, 0, r, + "%s: group id (%lu) unrecognizable", + r->filename, + (unsigned long)r->finfo.group); + continue; + } + + /* + * Not sure, if this is even supposed to happen... + */ + if (real_group->gr_mem == NULL) + continue; + + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, + 0, r, + "checking if %s is also a member of group %s (%lu)", + r->user, real_group->gr_name, + (unsigned long)r->finfo.group); + for (member = real_group->gr_mem; *member; member++) { + ap_log_rerror(APLOG_MARK, + APLOG_NOERRNO|APLOG_DEBUG, 0, r, + "comparing %s with %s", + r->user, *member); + if (strcmp(*member, r->user) == 0) + return OK; + } + + continue; + } + + default: + /* + * Comment from Apache's mod_auth.c: if we aren't + * authoritative, any require directive could be + * valid even if we don't grok it. However, if we + * are authoritative, we can warn the user they did + * something wrong. That something could be a missing + * "AuthAuthoritative off", but more likely is a typo in + * the require directive. + */ + if (!dir_config->authoritative) + continue; + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, + "%s: module %s does not support \"%s\" require " + "directive.", + r->uri, "mod_auth_pwcheck", word - 1); } } - if( dir_config->auth_pwcheck_authoritative ){ + if (!method_restricted) + return OK; + + if( dir_config->authoritative ){ // there was at least one requirement but it failed ap_note_basic_auth_failure( r ); - return AUTH_REQUIRED; + return HTTP_UNAUTHORIZED; }else{ return DECLINED; } } -