root/src/noit_rest.c

Revision 90efcaaaccf0ea7f45d7afcb8361f4df41d71b0f, 19.3 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 4 years ago)

allow deeper ACLs under the rest section

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2007, OmniTI Computer Consulting, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  *       notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  *       copyright notice, this list of conditions and the following
13  *       disclaimer in the documentation and/or other materials provided
14  *       with the distribution.
15  *     * Neither the name OmniTI Computer Consulting, Inc. nor the names
16  *       of its contributors may be used to endorse or promote products
17  *       derived from this software without specific prior written
18  *       permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include "noit_defines.h"
34 #include "noit_listener.h"
35 #include "noit_http.h"
36 #include "noit_rest.h"
37 #include "noit_conf.h"
38
39 #include <pcre.h>
40 #include <unistd.h>
41 #include <fcntl.h>
42 #include <sys/mman.h>
43 #include <errno.h>
44
45 struct rest_xml_payload {
46   char *buffer;
47   xmlDocPtr indoc;
48   int len;
49   int allocd;
50   int complete;
51 };
52
53 struct rest_url_dispatcher {
54   char *method;
55   pcre *expression;
56   pcre_extra *extra;
57   rest_request_handler handler;
58   rest_authorize_func_t auth;
59   /* Chain to the next one */
60   struct rest_url_dispatcher *next;
61 };
62
63 struct rule_container {
64   char *base;
65   struct rest_url_dispatcher *rules;
66   struct rest_url_dispatcher *rules_endptr;
67 };
68 noit_hash_table dispatch_points = NOIT_HASH_EMPTY;
69
70 struct noit_rest_acl_rule {
71   noit_boolean allow;
72   pcre *url;
73   pcre *cn;
74   struct noit_rest_acl_rule *next;
75 };
76 struct noit_rest_acl {
77   noit_boolean allow;
78   pcre *url;
79   pcre *cn;
80   struct noit_rest_acl_rule *rules;
81   struct noit_rest_acl *next;
82 };
83
84 static struct noit_rest_acl *global_rest_acls = NULL;
85
86 static int
87 noit_http_rest_permission_denied(noit_http_rest_closure_t *restc,
88                                  int npats, char **pats) {
89   noit_http_session_ctx *ctx = restc->http_ctx;
90   noit_http_response_standard(ctx, 403, "DENIED", "text/xml");
91   noit_http_response_end(ctx);
92   return 0;
93 }
94 static rest_request_handler
95 noit_http_get_handler(noit_http_rest_closure_t *restc) {
96   struct rule_container *cont = NULL;
97   struct rest_url_dispatcher *rule;
98   char *eoq, *eob;
99   eoq = strchr(restc->http_ctx->req.uri_str, '?');
100   if(!eoq)
101     eoq = restc->http_ctx->req.uri_str + strlen(restc->http_ctx->req.uri_str);
102   eob = eoq - 1;
103
104   /* find the right base */
105   while(1) {
106     void *vcont;
107     while(eob >= restc->http_ctx->req.uri_str && *eob != '/') eob--;
108     if(eob < restc->http_ctx->req.uri_str) break; /* off the front */
109     if(noit_hash_retrieve(&dispatch_points, restc->http_ctx->req.uri_str,
110                           eob - restc->http_ctx->req.uri_str + 1, &vcont)) {
111       cont = vcont;
112       eob++; /* move past the determined base */
113       break;
114     }
115     eob--;
116   }
117   if(!cont) return NULL;
118   for(rule = cont->rules; rule; rule = rule->next) {
119     int ovector[30];
120     int cnt;
121     if(strcmp(rule->method, restc->http_ctx->req.method_str)) continue;
122     if((cnt = pcre_exec(rule->expression, rule->extra, eob, eoq - eob, 0, 0,
123                         ovector, sizeof(ovector)/sizeof(*ovector))) > 0) {
124       /* We match, set 'er up */
125       restc->fastpath = rule->handler;
126       restc->nparams = cnt - 1;
127       if(restc->nparams) {
128         restc->params = calloc(restc->nparams, sizeof(*restc->params));
129         for(cnt = 0; cnt < restc->nparams; cnt++) {
130           int start = ovector[(cnt+1)*2];
131           int end = ovector[(cnt+1)*2+1];
132           restc->params[cnt] = malloc(end - start + 1);
133           memcpy(restc->params[cnt], eob + start, end - start);
134           restc->params[cnt][end - start] = '\0';
135         }
136       }
137       if(rule->auth && !rule->auth(restc, restc->nparams, restc->params))
138         return noit_http_rest_permission_denied;
139       return restc->fastpath;
140     }
141   }
142   return NULL;
143 }
144 noit_boolean
145 noit_http_rest_access(noit_http_rest_closure_t *restc,
146                       int npats, char **pats) {
147   struct noit_rest_acl *acl;
148   struct noit_rest_acl_rule *rule;
149   int ovector[30];
150
151   for(acl = global_rest_acls; acl; acl = acl->next) {
152     if(acl->cn && pcre_exec(acl->cn, NULL, "", 0, 0, 0,
153                             ovector, sizeof(ovector)/sizeof(*ovector)) <= 0)
154       continue;
155     if(acl->url && pcre_exec(acl->url, NULL, restc->http_ctx->req.uri_str,
156                              strlen(restc->http_ctx->req.uri_str), 0, 0,
157                              ovector, sizeof(ovector)/sizeof(*ovector)) <= 0)
158       continue;
159     for(rule = acl->rules; rule; rule = rule->next) {
160       if(rule->cn && pcre_exec(rule->cn, NULL, "", 0, 0, 0,
161                                ovector, sizeof(ovector)/sizeof(*ovector)) <= 0)
162         continue;
163       if(rule->url && pcre_exec(rule->url, NULL, restc->http_ctx->req.uri_str,
164                                 strlen(restc->http_ctx->req.uri_str), 0, 0,
165                                 ovector, sizeof(ovector)/sizeof(*ovector)) <= 0)
166         continue;
167       return rule->allow;
168     }
169     return acl->allow;
170   }
171   return noit_false;
172 }
173 noit_boolean
174 noit_http_rest_client_cert_auth(noit_http_rest_closure_t *restc,
175                                 int npats, char **pats) {
176   struct noit_rest_acl *acl;
177   struct noit_rest_acl_rule *rule;
178   int ovector[30];
179
180   if(!restc->remote_cn || !strlen(restc->remote_cn)) return noit_false;
181   for(acl = global_rest_acls; acl; acl = acl->next) {
182     if(acl->cn && pcre_exec(acl->cn, NULL, restc->remote_cn,
183                             strlen(restc->remote_cn), 0, 0,
184                             ovector, sizeof(ovector)/sizeof(*ovector)) <= 0)
185       continue;
186     if(acl->url && pcre_exec(acl->url, NULL, restc->http_ctx->req.uri_str,
187                              strlen(restc->http_ctx->req.uri_str), 0, 0,
188                              ovector, sizeof(ovector)/sizeof(*ovector)) <= 0)
189       continue;
190     for(rule = acl->rules; rule; rule = rule->next) {
191       if(rule->cn && pcre_exec(rule->cn, NULL, restc->remote_cn,
192                                strlen(restc->remote_cn), 0, 0,
193                                ovector, sizeof(ovector)/sizeof(*ovector)) <= 0)
194         continue;
195       if(rule->url && pcre_exec(rule->url, NULL, restc->http_ctx->req.uri_str,
196                                 strlen(restc->http_ctx->req.uri_str), 0, 0,
197                                 ovector, sizeof(ovector)/sizeof(*ovector)) <= 0)
198         continue;
199       return rule->allow;
200     }
201     return acl->allow;
202   }
203   return noit_false;
204 }
205 int
206 noit_http_rest_register(const char *method, const char *base,
207                         const char *expr, rest_request_handler f) {
208   return noit_http_rest_register_auth(method, base, expr, f, NULL);
209 }
210 int
211 noit_http_rest_register_auth(const char *method, const char *base,
212                              const char *expr, rest_request_handler f,
213                              rest_authorize_func_t auth) {
214   void *vcont;
215   struct rule_container *cont;
216   struct rest_url_dispatcher *rule;
217   const char *error;
218   int erroffset;
219   pcre *pcre_expr;
220   int blen = strlen(base);
221   /* base must end in a /, 'cause I said so */
222   if(blen == 0 || base[blen-1] != '/') return -1;
223   pcre_expr = pcre_compile(expr, 0, &error, &erroffset, NULL);
224   if(!pcre_expr) {
225     noitL(noit_error, "Error in rest expr(%s) '%s'@%d: %s\n",
226           base, expr, erroffset, error);
227     return -1;
228   }
229   rule = calloc(1, sizeof(*rule));
230   rule->method = strdup(method);
231   rule->expression = pcre_expr;
232   rule->extra = pcre_study(rule->expression, 0, &error);
233   rule->handler = f;
234   rule->auth = auth;
235
236   /* Make sure we have a container */
237   if(!noit_hash_retrieve(&dispatch_points, base, strlen(base), &vcont)) {
238     cont = calloc(1, sizeof(*cont));
239     cont->base = strdup(base);
240     noit_hash_store(&dispatch_points, cont->base, strlen(cont->base), cont);
241   }
242   else cont = vcont;
243
244   /* Append the rule */
245   if(cont->rules_endptr) {
246     cont->rules_endptr->next = rule;
247     cont->rules_endptr = cont->rules_endptr->next;
248   }
249   else
250     cont->rules = cont->rules_endptr = rule;
251   return 0;
252 }
253
254 static noit_http_rest_closure_t *
255 noit_http_rest_closure_alloc() {
256   noit_http_rest_closure_t *restc;
257   restc = calloc(1, sizeof(*restc));
258   return restc;
259 }
260 static void
261 noit_http_rest_clean_request(noit_http_rest_closure_t *restc) {
262   int i;
263   if(restc->nparams) {
264     for(i=0;i<restc->nparams;i++) free(restc->params[i]);
265     free(restc->params);
266   }
267   if(restc->call_closure_free) restc->call_closure_free(restc->call_closure);
268   restc->call_closure_free = NULL;
269   restc->call_closure = NULL;
270   restc->nparams = 0;
271   restc->params = NULL;
272   restc->fastpath = NULL;
273 }
274 void
275 noit_http_rest_closure_free(void *v) {
276   noit_http_rest_closure_t *restc = v;
277   free(restc->remote_cn);
278   noit_http_rest_clean_request(restc);
279   free(restc);
280 }
281
282 int
283 noit_rest_request_dispatcher(noit_http_session_ctx *ctx) {
284   noit_http_rest_closure_t *restc = ctx->dispatcher_closure;
285   rest_request_handler handler = restc->fastpath;
286   if(!handler) handler = noit_http_get_handler(restc);
287   if(handler) {
288     int rv;
289     rv = handler(restc, restc->nparams, restc->params);
290     if(ctx->res.closed) noit_http_rest_clean_request(restc);
291     return rv;
292   }
293   noit_http_response_status_set(ctx, 404, "NOT FOUND");
294   noit_http_response_option_set(ctx, NOIT_HTTP_CHUNKED);
295   noit_http_rest_clean_request(restc);
296   noit_http_response_end(ctx);
297   return 0;
298 }
299
300 int
301 noit_http_rest_handler(eventer_t e, int mask, void *closure,
302                        struct timeval *now) {
303   int newmask = EVENTER_READ | EVENTER_EXCEPTION, rv, done = 0;
304   acceptor_closure_t *ac = closure;
305   noit_http_rest_closure_t *restc = ac->service_ctx;
306
307   if(mask & EVENTER_EXCEPTION || (restc && restc->wants_shutdown)) {
308 socket_error:
309     /* Exceptions cause us to simply snip the connection */
310     eventer_remove_fd(e->fd);
311     e->opset->close(e->fd, &newmask, e);
312     if(ac) acceptor_closure_free(ac);
313     return 0;
314   }
315
316   if(!ac->service_ctx) {
317     const char *primer = "";
318     ac->service_ctx = restc = noit_http_rest_closure_alloc();
319     ac->service_ctx_free = noit_http_rest_closure_free;
320     restc->ac = ac;
321     restc->remote_cn = strdup(ac->remote_cn ? ac->remote_cn : "");
322     restc->http_ctx =
323         noit_http_session_ctx_new(noit_rest_request_dispatcher,
324                                   restc, e, ac);
325    
326     switch(ac->cmd) {
327       case NOIT_CONTROL_DELETE:
328         primer = "DELE";
329         break;
330       case NOIT_CONTROL_GET:
331         primer = "GET ";
332         break;
333       case NOIT_CONTROL_HEAD:
334         primer = "HEAD";
335         break;
336       case NOIT_CONTROL_POST:
337         primer = "POST";
338         break;
339       case NOIT_CONTROL_PUT:
340         primer = "PUT ";
341         break;
342       case NOIT_CONTROL_MERGE:
343         primer = "MERG";
344         break;
345       default:
346         goto socket_error;
347     }
348     noit_http_session_prime_input(restc->http_ctx, primer, 4);
349   }
350   rv = noit_http_session_drive(e, mask, restc->http_ctx, now, &done);
351   if(done) {
352     if(ac) acceptor_closure_free(ac);
353   }
354   return rv;
355 }
356
357 int
358 noit_http_rest_raw_handler(eventer_t e, int mask, void *closure,
359                            struct timeval *now) {
360   int newmask = EVENTER_READ | EVENTER_EXCEPTION, rv, done = 0;
361   acceptor_closure_t *ac = closure;
362   noit_http_rest_closure_t *restc = ac->service_ctx;
363
364   if(mask & EVENTER_EXCEPTION || (restc && restc->wants_shutdown)) {
365     /* Exceptions cause us to simply snip the connection */
366     eventer_remove_fd(e->fd);
367     e->opset->close(e->fd, &newmask, e);
368     if(ac) acceptor_closure_free(ac);
369     return 0;
370   }
371   if(!ac->service_ctx) {
372     ac->service_ctx = restc = noit_http_rest_closure_alloc();
373     ac->service_ctx_free = noit_http_rest_closure_free;
374     restc->ac = ac;
375     restc->http_ctx =
376         noit_http_session_ctx_new(noit_rest_request_dispatcher,
377                                   restc, e, ac);
378   }
379   rv = noit_http_session_drive(e, mask, restc->http_ctx, now, &done);
380   if(done) {
381     if(ac) acceptor_closure_free(ac);
382   }
383   return rv;
384 }
385
386 static void
387 rest_xml_payload_free(void *f) {
388   struct rest_xml_payload *xmlin = f;
389   if(xmlin->buffer) free(xmlin->buffer);
390   if(xmlin->indoc) xmlFreeDoc(xmlin->indoc);
391 }
392
393 xmlDocPtr
394 rest_get_xml_upload(noit_http_rest_closure_t *restc,
395                     int *mask, int *complete) {
396   struct rest_xml_payload *rxc;
397   if(restc->call_closure == NULL) {
398     restc->call_closure = calloc(1, sizeof(*rxc));
399     restc->call_closure_free = rest_xml_payload_free;
400   }
401   rxc = restc->call_closure;
402   while(!rxc->complete) {
403     int len;
404     if(rxc->len == rxc->allocd) {
405       char *b;
406       rxc->allocd += 32768;
407       b = rxc->buffer ? realloc(rxc->buffer, rxc->allocd) :
408                         malloc(rxc->allocd);
409       if(!b) {
410         *complete = 1;
411         return NULL;
412       }
413       rxc->buffer = b;
414     }
415     len = noit_http_session_req_consume(restc->http_ctx,
416                                         rxc->buffer + rxc->len,
417                                         rxc->allocd - rxc->len,
418                                         mask);
419     if(len > 0) rxc->len += len;
420     if(len < 0 && errno == EAGAIN) return NULL;
421     else if(len < 0) {
422       *complete = 1;
423       return NULL;
424     }
425     if(rxc->len == restc->http_ctx->req.content_length) {
426       rxc->indoc = xmlParseMemory(rxc->buffer, rxc->len);
427       rxc->complete = 1;
428     }
429   }
430
431   *complete = 1;
432   return rxc->indoc;
433 }
434 int
435 noit_rest_simple_file_handler(noit_http_rest_closure_t *restc,
436                               int npats, char **pats) {
437   int drlen = 0;
438   const char *document_root = NULL;
439   const char *index_file = NULL;
440   noit_http_session_ctx *ctx = restc->http_ctx;
441   char file[PATH_MAX], rfile[PATH_MAX];
442   struct stat st;
443   int fd;
444   void *contents = MAP_FAILED;
445
446   if(npats != 1 ||
447      !noit_hash_retr_str(restc->ac->config,
448                          "document_root", strlen("document_root"),
449                          &document_root)) {
450     goto not_found;
451   }
452   if(!noit_hash_retr_str(restc->ac->config,
453                          "index_file", strlen("index_file"),
454                          &index_file)) {
455     index_file = "index.html";
456   }
457   drlen = strlen(document_root);
458   snprintf(file, sizeof(file), "%s/%s", document_root, pats[0]);
459   if(file[strlen(file) - 1] == '/') {
460     snprintf(file + strlen(file), sizeof(file) - strlen(file),
461              "%s", index_file);
462   }
463   /* resolve */
464   if(realpath(file, rfile) == NULL) goto not_found;
465   /* restrict */
466   if(strncmp(rfile, document_root, drlen)) goto denied;
467   if(rfile[drlen] != '/' && rfile[drlen + 1] != '/') goto denied;
468   /* stat */
469   if(stat(rfile, &st) != 0) {
470     switch (errno) {
471       case EACCES: goto denied;
472       default: goto not_found;
473     }
474   }
475   /* open */
476   if(st.st_size > 0) {
477     fd = open(rfile, O_RDONLY);
478     if(fd < 0) goto not_found;
479     contents = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
480     close(fd);
481     if(contents == MAP_FAILED) goto not_found;
482   }
483   noit_http_response_ok(ctx, "text/html");
484   if(st.st_size > 0) {
485     noit_http_response_append(ctx, contents, st.st_size);
486     munmap(contents, st.st_size);
487   }
488   noit_http_response_end(ctx);
489   return 0;
490
491  denied:
492   noit_http_response_denied(ctx, "text/html");
493   noit_http_response_end(ctx);
494   return 0;
495  not_found:
496   noit_http_response_not_found(ctx, "text/html");
497   noit_http_response_end(ctx);
498   return 0;
499 }
500
501 void noit_http_rest_load_rules() {
502   int ai, cnt = 0;
503   noit_conf_section_t *acls;
504   char path[256];
505   struct noit_rest_acl *newhead = NULL, *oldacls, *remove_acl;
506   struct noit_rest_acl_rule *remove_rule;
507
508   snprintf(path, sizeof(path), "//rest//acl");
509   acls = noit_conf_get_sections(NULL, path, &cnt);
510   noitL(noit_stderr, "Found %d acl stanzas\n", cnt);
511   for(ai = cnt-1; ai>=0; ai--) {
512     char tbuff[32];
513     struct noit_rest_acl *newacl;
514     int ri, rcnt = 0;
515     noit_boolean default_allow = noit_false;
516     noit_conf_section_t *rules;
517
518     newacl = calloc(1, sizeof(*newacl));
519     newacl->next = newhead;
520     newhead = newacl;
521     if(noit_conf_get_stringbuf(acls[ai], "@type", tbuff, sizeof(tbuff)) &&
522        !strcmp(tbuff, "allow"))
523       newacl->allow = noit_true;
524
525 #define compile_re(node, cont, name) do { \
526   char buff[256]; \
527   if(noit_conf_get_stringbuf(node, "@" #name, buff, sizeof(buff))) { \
528     const char *error; \
529     int erroffset; \
530     cont->name = pcre_compile(buff, 0, &error, &erroffset, NULL); \
531   } \
532 } while(0)
533
534     newacl->allow = default_allow;
535     compile_re(acls[ai], newacl, cn);
536     compile_re(acls[ai], newacl, url);
537     rules = noit_conf_get_sections(acls[ai], "rule", &rcnt);
538     for(ri = rcnt - 1; ri >= 0; ri--) {
539       struct noit_rest_acl_rule *newacl_rule;
540       newacl_rule = calloc(1, sizeof(*newacl_rule));
541       newacl_rule->next = newacl->rules;
542       newacl->rules = newacl_rule;
543       if(noit_conf_get_stringbuf(rules[ri], "@type", tbuff, sizeof(tbuff)) &&
544          !strcmp(tbuff, "allow"))
545         newacl_rule->allow = noit_true;
546       compile_re(rules[ri], newacl_rule, cn);
547       compile_re(rules[ri], newacl_rule, url);
548     }
549     free(rules);
550   }
551   free(acls);
552
553   oldacls = global_rest_acls;
554   global_rest_acls = newhead;
555
556   while(oldacls) {
557     remove_acl = oldacls->next;
558     while(oldacls->rules) {
559       remove_rule = oldacls->rules->next;
560       if(oldacls->rules->cn) pcre_free(oldacls->rules->cn);
561       if(oldacls->rules->url) pcre_free(oldacls->rules->url);
562       free(oldacls->rules);
563       oldacls->rules = remove_rule;
564     }
565     if(oldacls->cn) pcre_free(oldacls->cn);
566     if(oldacls->url) pcre_free(oldacls->url);
567     free(oldacls);
568     oldacls = remove_acl;
569   }
570 }
571 void noit_http_rest_init() {
572   noit_http_init();
573   eventer_name_callback("noit_wire_rest_api/1.0", noit_http_rest_handler);
574   eventer_name_callback("http_rest_api", noit_http_rest_raw_handler);
575
576   noit_http_rest_load_rules();
577
578   noit_control_dispatch_delegate(noit_control_dispatch,
579                                  NOIT_CONTROL_DELETE,
580                                  noit_http_rest_handler);
581   noit_control_dispatch_delegate(noit_control_dispatch,
582                                  NOIT_CONTROL_MERGE,
583                                  noit_http_rest_handler);
584   noit_control_dispatch_delegate(noit_control_dispatch,
585                                  NOIT_CONTROL_GET,
586                                  noit_http_rest_handler);
587   noit_control_dispatch_delegate(noit_control_dispatch,
588                                  NOIT_CONTROL_HEAD,
589                                  noit_http_rest_handler);
590   noit_control_dispatch_delegate(noit_control_dispatch,
591                                  NOIT_CONTROL_POST,
592                                  noit_http_rest_handler);
593   noit_control_dispatch_delegate(noit_control_dispatch,
594                                  NOIT_CONTROL_PUT,
595                                  noit_http_rest_handler);
596 }
597
Note: See TracBrowser for help on using the browser.