Fix an infinite loop in the Apache module.
[itkacl] / libapache2-mod-authz-itkacl-0.5 / mod_authz_itkacl.c
1 /*
2  * ITKACL module, (C) 2004-2011 Steinar H. Gunderson
3  */
4
5 #define MODAUTHITKACL_VERSION "0.4"
6
7 #include "apr_strings.h"
8
9 #include "ap_config.h"
10 #include "httpd.h"
11 #include "http_config.h"
12 #include "http_core.h"
13 #include "http_log.h"
14 #include "http_protocol.h"
15 #include "http_request.h"
16
17 module AP_MODULE_DECLARE_DATA authz_itkacl_module;
18
19 extern int itkacl_check(const char * const realm, const char * const user,
20                         char *errmsg, size_t errmsg_size);
21
22 typedef struct {
23         int authoritative;
24 } authz_itkacl_config_rec;
25
26 static void *authz_itkacl_dir_create_config(apr_pool_t *p, char *d)
27 {
28         authz_itkacl_config_rec *conf = apr_palloc(p, sizeof(*conf));
29
30         conf->authoritative = 1; /* keep the fortress secure by default */
31         return conf;
32 }
33
34 static const command_rec authz_itkacl_cmds[] =
35 {
36         AP_INIT_FLAG("AuthzITKACLAuthoritative", ap_set_flag_slot,
37                 (void *)APR_OFFSETOF(authz_itkacl_config_rec, authoritative),
38                 OR_AUTHCFG,
39                 "Set to 'Off' to allow access control to be passed along to "
40                 "lower modules if the 'require user' or 'require valid-user' "
41                 "statement is not met. (default: On)."),
42         {NULL}
43 };
44
45 static int fail(request_rec *r, authz_itkacl_config_rec *conf)
46 {
47         if (conf->authoritative) {
48                 ap_note_auth_failure(r);
49                 return HTTP_UNAUTHORIZED;
50         } else {
51                 return DECLINED;
52         }
53 }
54
55 static int handle_require(request_rec *r, authz_itkacl_config_rec *conf, const char *username, const char *acl_path)
56 {
57         char errmsg[1024];
58         int ret;
59
60         ret = itkacl_check(acl_path, username, errmsg, 1024);
61         if (ret == 0) {
62                 return OK;
63         }
64
65         if (ret == -1) {
66                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
67                         "error during itkacl check for %s on %s: %s",
68                         username, acl_path, errmsg);
69         }
70
71         return HTTP_UNAUTHORIZED;
72 }
73
74 static int authz_itkacl_authorize_user(request_rec *r)
75 {
76         char *username, *ptr;
77         const apr_array_header_t *reqs_arr = ap_requires(r);
78         unsigned i;
79         require_line *reqs;
80         authz_itkacl_config_rec *conf = (authz_itkacl_config_rec *)
81                 ap_get_module_config(r->per_dir_config, &authz_itkacl_module);
82
83         if (reqs_arr == NULL)
84                 return DECLINED;
85         
86         /* strip the domain part (FIXME: use the alias module instead?) */
87         username = strdup(r->user);
88         ptr = strchr(username, '@');
89         if (ptr != NULL)
90                 ptr[0] = 0;
91
92         reqs = (require_line *)reqs_arr->elts;
93         for (i = 0; i < reqs_arr->nelts; ++i) {
94                 const char *t, *w;
95
96                 if (!(reqs[i].method_mask & (AP_METHOD_BIT << (r->method_number))))
97                         continue;
98
99                 t = reqs[i].requirement;
100                 w = ap_getword_white(r->pool, &t);
101
102                 if (strcasecmp(w, "valid-user") == 0) {
103                         free(username);
104                         return OK;
105                 }
106
107                 if (strcasecmp(w, "itkacl") == 0) {
108                         const char *acl_path;
109
110                         acl_path = ap_getword_conf(r->pool, &t);
111                         if (acl_path == NULL || strcmp(acl_path, "") == 0) {
112                                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
113                                                 "'Require itkacl' needs an argument");
114                                 return fail(r, conf);
115                         }
116
117                         if (strcasecmp(acl_path, "anyof") == 0) {
118                                 int num_seen = 0, ret;
119                                 while ((acl_path = ap_getword_conf(r->pool, &t)) != NULL &&
120                                        strcmp(acl_path, "") != 0) {
121                                         ret = handle_require(r, conf, username, acl_path);
122                                         if (ret == OK) {
123                                                 free(username);
124                                                 return ret;
125                                         }
126                                         ++num_seen;
127                                 }
128                                 if (num_seen == 0) {
129                                         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
130                                                 "Missing arguments after 'Require itkacl anyof'");
131                                 } else if (ret == HTTP_UNAUTHORIZED) {
132                                         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
133                                             "%s failed itkacl check for (multiple paths)",
134                                             username, acl_path);
135                                         ret = fail(r, conf);
136                                 }
137                                 free(username);
138                                 return ret;
139                         } else {
140                                 /* check that there are no more arguments */
141                                 w = ap_getword_conf(r->pool, &t);
142                                 if (w != NULL && strcmp(w, "") != 0) {
143                                         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
144                                                 "Excess arguments ('%s') after Require itkacl %s; "
145                                                 "did you mean 'Require itkacl anyof ...'?",
146                                                 w, acl_path);
147                                         return fail(r, conf);
148                                 }
149
150                                 int ret = handle_require(r, conf, username, acl_path);
151                                 if (ret == HTTP_UNAUTHORIZED) {
152                                         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
153                                             "%s failed itkacl check for %s",
154                                             username, acl_path);
155                                         ret = fail(r, conf);
156                                 }
157                                 free(username);
158                                 return ret;
159                         }
160                 }
161         }
162
163         free(username);
164         return DECLINED;
165 }
166
167 static int authz_itkacl_init_handler(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
168 {
169         ap_add_version_component(p, "mod_auth_itkacl/" MODAUTHITKACL_VERSION);
170         return OK;
171 }
172
173 void authz_itkacl_register_hooks(apr_pool_t *p)
174 {
175         ap_hook_post_config(authz_itkacl_init_handler, NULL, NULL, APR_HOOK_MIDDLE);
176         ap_hook_auth_checker(authz_itkacl_authorize_user, NULL, NULL, APR_HOOK_MIDDLE); 
177 }
178
179 module AP_MODULE_DECLARE_DATA authz_itkacl_module =
180 {
181         STANDARD20_MODULE_STUFF,
182         authz_itkacl_dir_create_config,
183         NULL,
184         NULL,
185         NULL,
186         authz_itkacl_cmds,
187         authz_itkacl_register_hooks
188 };