1/* Licensed to the Apache Software Foundation (ASF) under one or more 2 * contributor license agreements. See the NOTICE file distributed with 3 * this work for additional information regarding copyright ownership. 4 * The ASF licenses this file to You under the Apache License, Version 2.0 5 * (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17/* This module is triggered by an 18 * 19 * AuthGroupFile standard /path/to/file 20 * 21 * and the presense of a 22 * 23 * require group <list-of-groups> 24 * 25 * In an applicable limit/directory block for that method. 26 * 27 * If there are no AuthGroupFile directives valid for 28 * the request; we DECLINED. 29 * 30 * If the AuthGroupFile is defined; but somehow not 31 * accessible: we SERVER_ERROR (was DECLINED). 32 * 33 * If there are no 'require ' directives defined for 34 * this request then we DECLINED (was OK). 35 * 36 * If there are no 'require ' directives valid for 37 * this request method then we DECLINED. (was OK) 38 * 39 * If there are any 'require group' blocks and we 40 * are not in any group - we HTTP_UNAUTHORIZE 41 * 42 */ 43 44#include "apr_strings.h" 45#include "apr_lib.h" /* apr_isspace */ 46 47#include "ap_config.h" 48#include "ap_provider.h" 49#include "httpd.h" 50#include "http_config.h" 51#include "http_core.h" 52#include "http_log.h" 53#include "http_protocol.h" 54#include "http_request.h" 55#include "util_varbuf.h" 56 57#include "mod_auth.h" 58 59typedef struct { 60 char *groupfile; 61} authz_groupfile_config_rec; 62 63APR_DECLARE_OPTIONAL_FN(char*, authz_owner_get_file_group, (request_rec *r)); 64 65static void *create_authz_groupfile_dir_config(apr_pool_t *p, char *d) 66{ 67 authz_groupfile_config_rec *conf = apr_palloc(p, sizeof(*conf)); 68 69 conf->groupfile = NULL; 70 return conf; 71} 72 73static const command_rec authz_groupfile_cmds[] = 74{ 75 AP_INIT_TAKE1("AuthGroupFile", ap_set_file_slot, 76 (void *)APR_OFFSETOF(authz_groupfile_config_rec, groupfile), 77 OR_AUTHCFG, 78 "text file containing group names and member user IDs"), 79 {NULL} 80}; 81 82module AP_MODULE_DECLARE_DATA authz_groupfile_module; 83 84#define VARBUF_INIT_LEN 512 85#define VARBUF_MAX_LEN (16*1024*1024) 86static apr_status_t groups_for_user(apr_pool_t *p, char *user, char *grpfile, 87 apr_table_t ** out) 88{ 89 ap_configfile_t *f; 90 apr_table_t *grps = apr_table_make(p, 15); 91 apr_pool_t *sp; 92 struct ap_varbuf vb; 93 const char *group_name, *ll, *w; 94 apr_status_t status; 95 apr_size_t group_len; 96 97 if ((status = ap_pcfg_openfile(&f, p, grpfile)) != APR_SUCCESS) { 98 return status ; 99 } 100 101 apr_pool_create(&sp, p); 102 ap_varbuf_init(p, &vb, VARBUF_INIT_LEN); 103 104 while (!(ap_varbuf_cfg_getline(&vb, f, VARBUF_MAX_LEN))) { 105 if ((vb.buf[0] == '#') || (!vb.buf[0])) { 106 continue; 107 } 108 ll = vb.buf; 109 apr_pool_clear(sp); 110 111 group_name = ap_getword(sp, &ll, ':'); 112 group_len = strlen(group_name); 113 114 while (group_len && apr_isspace(*(group_name + group_len - 1))) { 115 --group_len; 116 } 117 118 while (ll[0]) { 119 w = ap_getword_conf(sp, &ll); 120 if (!strcmp(w, user)) { 121 apr_table_setn(grps, apr_pstrmemdup(p, group_name, group_len), 122 "in"); 123 break; 124 } 125 } 126 } 127 ap_cfg_closefile(f); 128 apr_pool_destroy(sp); 129 ap_varbuf_free(&vb); 130 131 *out = grps; 132 return APR_SUCCESS; 133} 134 135static authz_status group_check_authorization(request_rec *r, 136 const char *require_args, 137 const void *parsed_require_args) 138{ 139 authz_groupfile_config_rec *conf = ap_get_module_config(r->per_dir_config, 140 &authz_groupfile_module); 141 char *user = r->user; 142 143 const char *err = NULL; 144 const ap_expr_info_t *expr = parsed_require_args; 145 const char *require; 146 147 const char *t, *w; 148 apr_table_t *grpstatus = NULL; 149 apr_status_t status; 150 151 if (!user) { 152 return AUTHZ_DENIED_NO_USER; 153 } 154 155 /* If there is no group file - then we are not 156 * configured. So decline. 157 */ 158 if (!(conf->groupfile)) { 159 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01664) 160 "No group file was specified in the configuration"); 161 return AUTHZ_DENIED; 162 } 163 164 status = groups_for_user(r->pool, user, conf->groupfile, 165 &grpstatus); 166 167 if (status != APR_SUCCESS) { 168 ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01665) 169 "Could not open group file: %s", 170 conf->groupfile); 171 return AUTHZ_DENIED; 172 } 173 174 if (apr_is_empty_table(grpstatus)) { 175 /* no groups available, so exit immediately */ 176 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01666) 177 "Authorization of user %s to access %s failed, reason: " 178 "user doesn't appear in group file (%s).", 179 r->user, r->uri, conf->groupfile); 180 return AUTHZ_DENIED; 181 } 182 183 require = ap_expr_str_exec(r, expr, &err); 184 if (err) { 185 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02592) 186 "authz_groupfile authorize: require group: Can't " 187 "evaluate require expression: %s", err); 188 return AUTHZ_DENIED; 189 } 190 191 t = require; 192 while ((w = ap_getword_conf(r->pool, &t)) && w[0]) { 193 if (apr_table_get(grpstatus, w)) { 194 return AUTHZ_GRANTED; 195 } 196 } 197 198 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01667) 199 "Authorization of user %s to access %s failed, reason: " 200 "user is not part of the 'require'ed group(s).", 201 r->user, r->uri); 202 203 return AUTHZ_DENIED; 204} 205 206APR_OPTIONAL_FN_TYPE(authz_owner_get_file_group) *authz_owner_get_file_group; 207 208static authz_status filegroup_check_authorization(request_rec *r, 209 const char *require_args, 210 const void *parsed_require_args) 211{ 212 authz_groupfile_config_rec *conf = ap_get_module_config(r->per_dir_config, 213 &authz_groupfile_module); 214 char *user = r->user; 215 apr_table_t *grpstatus = NULL; 216 apr_status_t status; 217 const char *filegroup = NULL; 218 219 if (!user) { 220 return AUTHZ_DENIED_NO_USER; 221 } 222 223 /* If there is no group file - then we are not 224 * configured. So decline. 225 */ 226 if (!(conf->groupfile)) { 227 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01668) 228 "No group file was specified in the configuration"); 229 return AUTHZ_DENIED; 230 } 231 232 status = groups_for_user(r->pool, user, conf->groupfile, 233 &grpstatus); 234 if (status != APR_SUCCESS) { 235 ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01669) 236 "Could not open group file: %s", 237 conf->groupfile); 238 return AUTHZ_DENIED; 239 } 240 241 if (apr_is_empty_table(grpstatus)) { 242 /* no groups available, so exit immediately */ 243 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01670) 244 "Authorization of user %s to access %s failed, reason: " 245 "user doesn't appear in group file (%s).", 246 r->user, r->uri, conf->groupfile); 247 return AUTHZ_DENIED; 248 } 249 250 filegroup = authz_owner_get_file_group(r); 251 252 if (filegroup) { 253 if (apr_table_get(grpstatus, filegroup)) { 254 return AUTHZ_GRANTED; 255 } 256 } 257 else { 258 /* No need to emit a error log entry because the call 259 to authz_owner_get_file_group already did it 260 for us. 261 */ 262 return AUTHZ_DENIED; 263 } 264 265 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01671) 266 "Authorization of user %s to access %s failed, reason: " 267 "user is not part of the 'require'ed file group.", 268 r->user, r->uri); 269 270 return AUTHZ_DENIED; 271} 272 273static const char *groupfile_parse_config(cmd_parms *cmd, const char *require_line, 274 const void **parsed_require_line) 275{ 276 const char *expr_err = NULL; 277 ap_expr_info_t *expr; 278 279 expr = ap_expr_parse_cmd(cmd, require_line, AP_EXPR_FLAG_STRING_RESULT, 280 &expr_err, NULL); 281 282 if (expr_err) 283 return apr_pstrcat(cmd->temp_pool, 284 "Cannot parse expression in require line: ", 285 expr_err, NULL); 286 287 *parsed_require_line = expr; 288 289 return NULL; 290} 291 292static const authz_provider authz_group_provider = 293{ 294 &group_check_authorization, 295 &groupfile_parse_config, 296}; 297 298static const authz_provider authz_filegroup_provider = 299{ 300 &filegroup_check_authorization, 301 NULL, 302}; 303 304static void register_hooks(apr_pool_t *p) 305{ 306 authz_owner_get_file_group = APR_RETRIEVE_OPTIONAL_FN(authz_owner_get_file_group); 307 308 ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "group", 309 AUTHZ_PROVIDER_VERSION, 310 &authz_group_provider, 311 AP_AUTH_INTERNAL_PER_CONF); 312 ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "file-group", 313 AUTHZ_PROVIDER_VERSION, 314 &authz_filegroup_provider, 315 AP_AUTH_INTERNAL_PER_CONF); 316} 317 318AP_DECLARE_MODULE(authz_groupfile) = 319{ 320 STANDARD20_MODULE_STUFF, 321 create_authz_groupfile_dir_config,/* dir config creater */ 322 NULL, /* dir merger -- default is to override */ 323 NULL, /* server config */ 324 NULL, /* merge server config */ 325 authz_groupfile_cmds, /* command apr_table_t */ 326 register_hooks /* register hooks */ 327}; 328