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#define APR_WANT_STRFUNC 18#include "apr_want.h" 19#include "apr_strings.h" 20#include "apr_dbm.h" 21#include "apr_md5.h" 22 23#include "httpd.h" 24#include "http_config.h" 25#include "ap_provider.h" 26#include "http_core.h" 27#include "http_log.h" 28#include "http_protocol.h" 29#include "http_request.h" /* for ap_hook_(check_user_id | auth_checker)*/ 30 31#include "mod_auth.h" 32 33typedef struct { 34 const char *grpfile; 35 const char *dbmtype; 36} authz_dbm_config_rec; 37 38APR_DECLARE_OPTIONAL_FN(char*, authz_owner_get_file_group, (request_rec *r)); 39 40 41/* This should go into APR; perhaps with some nice 42 * caching/locking/flocking of the open dbm file. 43 */ 44static char *get_dbm_entry_as_str(apr_pool_t *pool, apr_dbm_t *f, char *key) 45{ 46 apr_datum_t d, q; 47 q.dptr = key; 48 49#ifndef NETSCAPE_DBM_COMPAT 50 q.dsize = strlen(q.dptr); 51#else 52 q.dsize = strlen(q.dptr) + 1; 53#endif 54 55 if (apr_dbm_fetch(f, q, &d) == APR_SUCCESS && d.dptr) { 56 return apr_pstrmemdup(pool, d.dptr, d.dsize); 57 } 58 59 return NULL; 60} 61 62static void *create_authz_dbm_dir_config(apr_pool_t *p, char *d) 63{ 64 authz_dbm_config_rec *conf = apr_palloc(p, sizeof(*conf)); 65 66 conf->grpfile = NULL; 67 conf->dbmtype = "default"; 68 69 return conf; 70} 71 72static const command_rec authz_dbm_cmds[] = 73{ 74 AP_INIT_TAKE1("AuthDBMGroupFile", ap_set_file_slot, 75 (void *)APR_OFFSETOF(authz_dbm_config_rec, grpfile), 76 OR_AUTHCFG, "database file containing group names and member user IDs"), 77 AP_INIT_TAKE1("AuthzDBMType", ap_set_string_slot, 78 (void *)APR_OFFSETOF(authz_dbm_config_rec, dbmtype), 79 OR_AUTHCFG, "what type of DBM file the group file is"), 80 {NULL} 81}; 82 83module AP_MODULE_DECLARE_DATA authz_dbm_module; 84 85/* We do something strange with the group file. If the group file 86 * contains any : we assume the format is 87 * key=username value=":"groupname [":"anything here is ignored] 88 * otherwise we now (0.8.14+) assume that the format is 89 * key=username value=groupname 90 * The first allows the password and group files to be the same 91 * physical DBM file; key=username value=password":"groupname[":"anything] 92 * 93 * mark@telescope.org, 22Sep95 94 */ 95 96static apr_status_t get_dbm_grp(request_rec *r, char *key1, char *key2, 97 const char *dbmgrpfile, const char *dbtype, 98 const char ** out) 99{ 100 char *grp_colon, *val; 101 apr_status_t retval; 102 apr_dbm_t *f; 103 104 retval = apr_dbm_open_ex(&f, dbtype, dbmgrpfile, APR_DBM_READONLY, 105 APR_OS_DEFAULT, r->pool); 106 107 if (retval != APR_SUCCESS) { 108 return retval; 109 } 110 111 /* Try key2 only if key1 failed */ 112 if (!(val = get_dbm_entry_as_str(r->pool, f, key1))) { 113 val = get_dbm_entry_as_str(r->pool, f, key2); 114 } 115 116 apr_dbm_close(f); 117 118 if (val && (grp_colon = ap_strchr(val, ':')) != NULL) { 119 char *grp_colon2 = ap_strchr(++grp_colon, ':'); 120 121 if (grp_colon2) { 122 *grp_colon2 = '\0'; 123 } 124 *out = grp_colon; 125 } 126 else { 127 *out = val; 128 } 129 130 return retval; 131} 132 133static authz_status dbmgroup_check_authorization(request_rec *r, 134 const char *require_args, 135 const void *parsed_require_args) 136{ 137 authz_dbm_config_rec *conf = ap_get_module_config(r->per_dir_config, 138 &authz_dbm_module); 139 char *user = r->user; 140 141 const char *err = NULL; 142 const ap_expr_info_t *expr = parsed_require_args; 143 const char *require; 144 145 const char *t; 146 char *w; 147 const char *orig_groups = NULL; 148 const char *realm = ap_auth_name(r); 149 const char *groups; 150 char *v; 151 152 if (!user) { 153 return AUTHZ_DENIED_NO_USER; 154 } 155 156 if (!conf->grpfile) { 157 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01798) 158 "No group file was specified in the configuration"); 159 return AUTHZ_DENIED; 160 } 161 162 /* fetch group data from dbm file only once. */ 163 if (!orig_groups) { 164 apr_status_t status; 165 166 status = get_dbm_grp(r, apr_pstrcat(r->pool, user, ":", realm, NULL), 167 user, conf->grpfile, conf->dbmtype, &groups); 168 169 if (status != APR_SUCCESS) { 170 ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01799) 171 "could not open dbm (type %s) group access " 172 "file: %s", conf->dbmtype, conf->grpfile); 173 return AUTHZ_GENERAL_ERROR; 174 } 175 176 if (groups == NULL) { 177 /* no groups available, so exit immediately */ 178 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01800) 179 "Authorization of user %s to access %s failed, reason: " 180 "user doesn't appear in DBM group file (%s).", 181 r->user, r->uri, conf->grpfile); 182 return AUTHZ_DENIED; 183 } 184 185 orig_groups = groups; 186 } 187 188 require = ap_expr_str_exec(r, expr, &err); 189 if (err) { 190 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02591) 191 "authz_dbm authorize: require dbm-group: Can't " 192 "evaluate require expression: %s", err); 193 return AUTHZ_DENIED; 194 } 195 196 t = require; 197 while ((w = ap_getword_white(r->pool, &t)) && w[0]) { 198 groups = orig_groups; 199 while (groups[0]) { 200 v = ap_getword(r->pool, &groups, ','); 201 if (!strcmp(v, w)) { 202 return AUTHZ_GRANTED; 203 } 204 } 205 } 206 207 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01801) 208 "Authorization of user %s to access %s failed, reason: " 209 "user is not part of the 'require'ed group(s).", 210 r->user, r->uri); 211 212 return AUTHZ_DENIED; 213} 214 215APR_OPTIONAL_FN_TYPE(authz_owner_get_file_group) *authz_owner_get_file_group; 216 217static authz_status dbmfilegroup_check_authorization(request_rec *r, 218 const char *require_args, 219 const void *parsed_require_args) 220{ 221 authz_dbm_config_rec *conf = ap_get_module_config(r->per_dir_config, 222 &authz_dbm_module); 223 char *user = r->user; 224 const char *realm = ap_auth_name(r); 225 const char *filegroup = NULL; 226 apr_status_t status; 227 const char *groups; 228 char *v; 229 230 if (!user) { 231 return AUTHZ_DENIED_NO_USER; 232 } 233 234 if (!conf->grpfile) { 235 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01802) 236 "No group file was specified in the configuration"); 237 return AUTHZ_DENIED; 238 } 239 240 /* fetch group data from dbm file. */ 241 status = get_dbm_grp(r, apr_pstrcat(r->pool, user, ":", realm, NULL), 242 user, conf->grpfile, conf->dbmtype, &groups); 243 244 if (status != APR_SUCCESS) { 245 ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01803) 246 "could not open dbm (type %s) group access " 247 "file: %s", conf->dbmtype, conf->grpfile); 248 return AUTHZ_DENIED; 249 } 250 251 if (groups == NULL) { 252 /* no groups available, so exit immediately */ 253 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01804) 254 "Authorization of user %s to access %s failed, reason: " 255 "user doesn't appear in DBM group file (%s).", 256 r->user, r->uri, conf->grpfile); 257 return AUTHZ_DENIED; 258 } 259 260 filegroup = authz_owner_get_file_group(r); 261 262 if (filegroup) { 263 while (groups[0]) { 264 v = ap_getword(r->pool, &groups, ','); 265 if (!strcmp(v, filegroup)) { 266 return AUTHZ_GRANTED; 267 } 268 } 269 } 270 271 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01805) 272 "Authorization of user %s to access %s failed, reason: " 273 "user is not part of the 'require'ed group(s).", 274 r->user, r->uri); 275 276 return AUTHZ_DENIED; 277} 278 279static const char *dbm_parse_config(cmd_parms *cmd, const char *require_line, 280 const void **parsed_require_line) 281{ 282 const char *expr_err = NULL; 283 ap_expr_info_t *expr; 284 285 expr = ap_expr_parse_cmd(cmd, require_line, AP_EXPR_FLAG_STRING_RESULT, 286 &expr_err, NULL); 287 288 if (expr_err) 289 return apr_pstrcat(cmd->temp_pool, 290 "Cannot parse expression in require line: ", 291 expr_err, NULL); 292 293 *parsed_require_line = expr; 294 295 return NULL; 296} 297 298static const authz_provider authz_dbmgroup_provider = 299{ 300 &dbmgroup_check_authorization, 301 &dbm_parse_config, 302}; 303 304static const authz_provider authz_dbmfilegroup_provider = 305{ 306 &dbmfilegroup_check_authorization, 307 NULL, 308}; 309 310 311static void register_hooks(apr_pool_t *p) 312{ 313 authz_owner_get_file_group = APR_RETRIEVE_OPTIONAL_FN(authz_owner_get_file_group); 314 315 ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "dbm-group", 316 AUTHZ_PROVIDER_VERSION, 317 &authz_dbmgroup_provider, 318 AP_AUTH_INTERNAL_PER_CONF); 319 ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "dbm-file-group", 320 AUTHZ_PROVIDER_VERSION, 321 &authz_dbmfilegroup_provider, 322 AP_AUTH_INTERNAL_PER_CONF); 323} 324 325AP_DECLARE_MODULE(authz_dbm) = 326{ 327 STANDARD20_MODULE_STUFF, 328 create_authz_dbm_dir_config, /* dir config creater */ 329 NULL, /* dir merger --- default is to override */ 330 NULL, /* server config */ 331 NULL, /* merge server config */ 332 authz_dbm_cmds, /* command apr_table_t */ 333 register_hooks /* register hooks */ 334}; 335