]> granicus.if.org Git - apache/blob - modules/aaa/mod_authz_dbd.c
update license header text
[apache] / modules / aaa / mod_authz_dbd.c
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 #include "httpd.h"
18 #include "http_log.h"
19 #include "http_config.h"
20 #include "ap_provider.h"
21 #include "http_request.h"
22 #include "http_protocol.h"
23 #include "http_core.h"
24 #include "apr_dbd.h"
25 #include "mod_dbd.h"
26 #include "apr_strings.h"
27 #include "mod_authz_dbd.h"
28
29 #include "mod_auth.h"
30
31
32 module AP_MODULE_DECLARE_DATA authz_dbd_module;
33
34 /* Export a hook for modules that manage clientside sessions
35  * (e.g. mod_auth_cookie)
36  * to deal with those when we successfully login/logout at the server
37  */
38 APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(authz_dbd, AP, int, client_login,
39                             (request_rec *r, int code, const char *action),
40                             (r, code, action), OK, DECLINED)
41
42
43 typedef struct {
44     const char *query;
45     const char *redir_query;
46     int redirect;
47 } authz_dbd_cfg ;
48
49 static ap_dbd_t *(*dbd_handle)(request_rec*) = NULL;
50 static void (*dbd_prepare)(server_rec*, const char*, const char*) = NULL;
51
52 static const char *const noerror = "???";
53
54 static void *authz_dbd_cr_cfg(apr_pool_t *pool, char *dummy)
55 {
56     authz_dbd_cfg *ret = apr_pcalloc(pool, sizeof(authz_dbd_cfg));
57     ret->redirect = -1;
58     return ret;
59 }
60 static void *authz_dbd_merge_cfg(apr_pool_t *pool, void *BASE, void *ADD)
61 {
62     authz_dbd_cfg *base = BASE;
63     authz_dbd_cfg *add = ADD;
64     authz_dbd_cfg *ret = apr_palloc(pool, sizeof(authz_dbd_cfg));
65
66     ret->query = (add->query == NULL) ? base->query : add->query;
67     ret->redir_query = (add->redir_query == NULL)
68                             ? base->redir_query : add->redir_query;
69     ret->redirect = (add->redirect == -1) ? base->redirect : add->redirect;
70     return ret;
71 }
72 static const char *authz_dbd_prepare(cmd_parms *cmd, void *cfg,
73                                      const char *query)
74 {
75     static unsigned int label_num = 0;
76     char *label;
77
78     if (dbd_prepare == NULL) {
79         dbd_prepare = APR_RETRIEVE_OPTIONAL_FN(ap_dbd_prepare);
80         if (dbd_prepare == NULL) {
81             return "You must load mod_dbd to enable AuthzDBD functions";
82         }
83         dbd_handle = APR_RETRIEVE_OPTIONAL_FN(ap_dbd_acquire);
84     }
85     label = apr_psprintf(cmd->pool, "authz_dbd_%d", ++label_num);
86
87     dbd_prepare(cmd->server, query, label);
88
89     /* save the label here for our own use */
90     return ap_set_string_slot(cmd, cfg, label);
91 }
92 static const command_rec authz_dbd_cmds[] = {
93     AP_INIT_FLAG("AuthzDBDLoginToReferer", ap_set_flag_slot,
94                  (void*)APR_OFFSETOF(authz_dbd_cfg, redirect), ACCESS_CONF,
95                  "Whether to redirect to referer on successful login"),
96     AP_INIT_TAKE1("AuthzDBDQuery", authz_dbd_prepare,
97                   (void*)APR_OFFSETOF(authz_dbd_cfg, query), ACCESS_CONF,
98                   "SQL query for DBD Authz or login"),
99     AP_INIT_TAKE1("AuthzDBDRedirectQuery", authz_dbd_prepare,
100                   (void*)APR_OFFSETOF(authz_dbd_cfg, redir_query), ACCESS_CONF,
101                   "SQL query to get per-user redirect URL after login"),
102     {NULL}
103 };
104
105 static int authz_dbd_login(request_rec *r, authz_dbd_cfg *cfg,
106                            const char *action)
107 {
108     int rv;
109     const char *newuri = NULL;
110     int nrows;
111     const char *message;
112     ap_dbd_t *dbd = dbd_handle(r);
113     apr_dbd_prepared_t *query;
114     apr_dbd_results_t *res = NULL;
115     apr_dbd_row_t *row = NULL;
116
117     if (cfg->query == NULL) {
118         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
119                       "No query configured for %s!", action);
120         return HTTP_INTERNAL_SERVER_ERROR;
121     }
122     query = apr_hash_get(dbd->prepared, cfg->query, APR_HASH_KEY_STRING);
123     if (query == NULL) {
124         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
125                       "Error retrieving Query for %s!", action);
126         return HTTP_INTERNAL_SERVER_ERROR;
127     }
128
129     rv = apr_dbd_pvquery(dbd->driver, r->pool, dbd->handle, &nrows,
130                          query, r->user, NULL);
131     if (rv == 0) {
132         if (nrows != 1) {
133             ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
134                           "authz_dbd: %s of user %s updated %d rows",
135                           action, r->user, nrows);
136         }
137     }
138     else {
139         message = apr_dbd_error(dbd->driver, dbd->handle, rv);
140         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
141                       "authz_dbd: query for %s failed; user %s [%s]",
142                       action, r->user, message?message:noerror);
143         return HTTP_INTERNAL_SERVER_ERROR;
144     }
145
146     if (cfg->redirect == 1) {
147         newuri = apr_table_get(r->headers_in, "Referer");
148     }
149
150     if (!newuri && cfg->redir_query) {
151         query = apr_hash_get(dbd->prepared, cfg->redir_query,
152                              APR_HASH_KEY_STRING);
153         if (query == NULL) {
154             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
155                           "authz_dbd: no redirect query!");
156             /* OK, this is non-critical; we can just not-redirect */
157         }
158         else if (apr_dbd_pvselect(dbd->driver, r->pool, dbd->handle, &res,
159                                   query, 0, r->user, NULL) == 0) {
160             for (rv = apr_dbd_get_row(dbd->driver, r->pool, res, &row, -1);
161                  rv != -1;
162                  rv = apr_dbd_get_row(dbd->driver, r->pool, res, &row, -1)) {
163                 if (rv == 0) {
164                     newuri = apr_dbd_get_entry(dbd->driver, row, 0);
165                 }
166                 else {
167                     message = apr_dbd_error(dbd->driver, dbd->handle, rv);
168                     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
169                           "authz_dbd in get_row; action=%s user=%s [%s]",
170                           action, r->user, message?message:noerror);
171                 }
172             }
173         }
174         else {
175             message = apr_dbd_error(dbd->driver, dbd->handle, rv);
176             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
177                           "authz_dbd/redirect for %s of %s [%s]",
178                           action, r->user, message?message:noerror);
179         }
180     }
181     if (newuri != NULL) {
182         r->status = HTTP_MOVED_TEMPORARILY;
183         apr_table_set(r->err_headers_out, "Location", newuri);
184         rv = HTTP_MOVED_TEMPORARILY;
185     }
186     else {
187         rv = OK;
188     }
189     authz_dbd_run_client_login(r, rv, action);
190     return rv;
191 }
192
193 static int authz_dbd_group_query(request_rec *r, authz_dbd_cfg *cfg,
194                                  apr_array_header_t *groups)
195 {
196     /* SELECT group FROM authz WHERE user = %s */
197     int rv;
198     const char *message;
199     ap_dbd_t *dbd = dbd_handle(r);
200     apr_dbd_prepared_t *query;
201     apr_dbd_results_t *res = NULL;
202     apr_dbd_row_t *row = NULL;
203     const char **group;
204
205     if (cfg->query == NULL) {
206         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
207                       "No query configured for dbd-group!");
208         return HTTP_INTERNAL_SERVER_ERROR;
209     }
210     query = apr_hash_get(dbd->prepared, cfg->query, APR_HASH_KEY_STRING);
211     if (query == NULL) {
212         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
213                       "Error retrieving query for dbd-group!");
214         return HTTP_INTERNAL_SERVER_ERROR;
215     }
216     rv = apr_dbd_pvselect(dbd->driver, r->pool, dbd->handle, &res,
217                           query, 0, r->user, NULL);
218     if (rv == 0) {
219         for (rv = apr_dbd_get_row(dbd->driver, r->pool, res, &row, -1);
220              rv != -1;
221              rv = apr_dbd_get_row(dbd->driver, r->pool, res, &row, -1)) {
222             if (rv == 0) {
223                 group = apr_array_push(groups);
224                 *group = apr_dbd_get_entry(dbd->driver, row, 0);
225             }
226             else {
227                 message = apr_dbd_error(dbd->driver, dbd->handle, rv);
228                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
229                         "authz_dbd in get_row; group query for user=%s [%s]",
230                         r->user, message?message:noerror);
231                 return HTTP_INTERNAL_SERVER_ERROR;
232             }
233         }
234     }
235     else {
236         message = apr_dbd_error(dbd->driver, dbd->handle, rv);
237         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
238                       "authz_dbd, in groups query for %s [%s]",
239                       r->user, message?message:noerror);
240         return HTTP_INTERNAL_SERVER_ERROR;
241     }
242     return OK;
243 }
244
245 static authz_status dbdgroup_check_authorization(request_rec *r,
246                                               const char *require_args)
247 {
248     int i, rv;
249     const char *w;
250     apr_array_header_t *groups = NULL;
251     const char *t;
252     authz_dbd_cfg *cfg = ap_get_module_config(r->per_dir_config,
253                                               &authz_dbd_module);
254
255     if (groups == NULL) {
256         groups = apr_array_make(r->pool, 4, sizeof(const char*));
257         rv = authz_dbd_group_query(r, cfg, groups);
258         if (rv != OK) {
259             return AUTHZ_GENERAL_ERROR;
260         }
261     }
262
263     t = require_args;
264     while (t[0]) {
265         w = ap_getword_white(r->pool, &t);
266         for (i=0; i < groups->nelts; ++i) {
267             if (!strcmp(w, ((const char**)groups->elts)[i])) {
268                 return AUTHZ_GRANTED;
269             }
270         }
271     }
272
273     return AUTHZ_DENIED;
274 }
275
276 static authz_status dbdlogin_check_authorization(request_rec *r,
277                                               const char *require_args)
278 {
279     authz_dbd_cfg *cfg = ap_get_module_config(r->per_dir_config,
280                                               &authz_dbd_module);
281
282     return (authz_dbd_login(r, cfg, "login") == OK ? AUTHZ_GRANTED : AUTHZ_DENIED);
283 }
284
285 static authz_status dbdlogout_check_authorization(request_rec *r,
286                                               const char *require_args)
287 {
288     authz_dbd_cfg *cfg = ap_get_module_config(r->per_dir_config,
289                                               &authz_dbd_module);
290
291     return (authz_dbd_login(r, cfg, "logout") == OK ? AUTHZ_GRANTED : AUTHZ_DENIED);
292 }
293
294 static const authz_provider authz_dbdgroup_provider =
295 {
296     &dbdgroup_check_authorization,
297 };
298
299 static const authz_provider authz_dbdlogin_provider =
300 {
301     &dbdlogin_check_authorization,
302 };
303
304
305 static const authz_provider authz_dbdlogout_provider =
306 {
307     &dbdlogout_check_authorization,
308 };
309
310 static void authz_dbd_hooks(apr_pool_t *p)
311 {
312     ap_register_provider(p, AUTHZ_PROVIDER_GROUP, "dbd-group", "0",
313                          &authz_dbdgroup_provider);
314     ap_register_provider(p, AUTHZ_PROVIDER_GROUP, "dbd-login", "0",
315                          &authz_dbdlogin_provider);
316     ap_register_provider(p, AUTHZ_PROVIDER_GROUP, "dbd-logout", "0",
317                          &authz_dbdlogout_provider);
318 }
319
320 module AP_MODULE_DECLARE_DATA authz_dbd_module =
321 {
322     STANDARD20_MODULE_STUFF,
323     authz_dbd_cr_cfg,
324     authz_dbd_merge_cfg,
325     NULL,
326     NULL,
327     authz_dbd_cmds,
328     authz_dbd_hooks
329 };
330