passdb: Use dom_sid_str_buf
[amitay/samba.git] / source4 / web_server / web_server.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    web server startup
5
6    Copyright (C) Andrew Tridgell 2005
7    Copyright (C) Jelmer Vernooij 2008
8    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "includes.h"
24 #include "web_server/web_server.h"
25 #include "../lib/util/dlinklist.h"
26 #include "lib/tls/tls.h"
27 #include "lib/events/events.h"
28 #include "lib/socket/netif.h"
29 #include "param/param.h"
30
31 NTSTATUS server_service_web_init(TALLOC_CTX *);
32
33 /* don't allow connections to hang around forever */
34 #define HTTP_TIMEOUT 120
35
36 /*
37   destroy a web connection
38 */
39 static int websrv_destructor(struct websrv_context *web)
40 {
41         return 0;
42 }
43
44 /*
45   called when a connection times out. This prevents a stuck connection
46   from hanging around forever
47 */
48 static void websrv_timeout(struct tevent_context *event_context, 
49                            struct tevent_timer *te, 
50                            struct timeval t, void *private_data)
51 {
52         struct websrv_context *web = talloc_get_type_abort(private_data, struct websrv_context);
53         struct stream_connection *conn = web->conn;
54         web->conn = NULL;
55         /* TODO: send a message to any running esp context on this connection
56            to stop running */
57         stream_terminate_connection(conn, "websrv_timeout: timed out"); 
58 }
59
60 /*
61   setup for a raw http level error
62 */
63 void http_error(struct websrv_context *web, const char *status, const char *info)
64 {
65         char *s;
66         s = talloc_asprintf(web,"<HTML><HEAD><TITLE>Error %s</TITLE></HEAD><BODY><H1>Error %s</H1><pre>%s</pre><p></BODY></HTML>\r\n\r\n", 
67                             status, status, info);
68         if (s == NULL) {
69                 stream_terminate_connection(web->conn, "http_error: out of memory");
70                 return;
71         }
72         websrv_output_headers(web, status, NULL);
73         websrv_output(web, s, strlen(s));
74 }
75
76 void websrv_output_headers(struct websrv_context *web, const char *status, struct http_header *headers)
77 {
78         char *s;
79         DATA_BLOB b;
80         struct http_header *hdr;
81
82         s = talloc_asprintf(web, "HTTP/1.0 %s\r\n", status);
83         if (s == NULL) return;
84         for (hdr = headers; hdr; hdr = hdr->next) {
85                 s = talloc_asprintf_append_buffer(s, "%s: %s\r\n", hdr->name, hdr->value);
86         }
87
88         s = talloc_asprintf_append_buffer(s, "\r\n");
89
90         b = web->output.content;
91         web->output.content = data_blob_string_const(s);
92         websrv_output(web, b.data, b.length);
93         data_blob_free(&b);
94 }
95
96 void websrv_output(struct websrv_context *web, const void *data, size_t length)
97 {
98         data_blob_append(web, &web->output.content, data, length);
99         TEVENT_FD_NOT_READABLE(web->conn->event.fde);
100         TEVENT_FD_WRITEABLE(web->conn->event.fde);
101         web->output.output_pending = true;
102 }
103
104
105 /*
106   parse one line of header input
107 */
108 NTSTATUS http_parse_header(struct websrv_context *web, const char *line)
109 {
110         if (line[0] == 0) {
111                 web->input.end_of_headers = true;
112         } else if (strncasecmp(line,"GET ", 4)==0) {
113                 web->input.url = talloc_strndup(web, &line[4], strcspn(&line[4], " \t"));
114         } else if (strncasecmp(line,"POST ", 5)==0) {
115                 web->input.post_request = true;
116                 web->input.url = talloc_strndup(web, &line[5], strcspn(&line[5], " \t"));
117         } else if (strchr(line, ':') == NULL) {
118                 http_error(web, "400 Bad request", "This server only accepts GET and POST requests");
119                 return NT_STATUS_INVALID_PARAMETER;
120         } else if (strncasecmp(line, "Content-Length: ", 16)==0) {
121                 web->input.content_length = strtoul(&line[16], NULL, 10);
122         } else {
123                 struct http_header *hdr = talloc_zero(web, struct http_header);
124                 char *colon = strchr(line, ':');
125                 if (colon == NULL) {
126                         http_error(web, "500 Internal Server Error", "invalidly formatted header");
127                         return NT_STATUS_INVALID_PARAMETER;
128                 }
129
130                 hdr->name = talloc_strndup(hdr, line, colon-line);
131                 hdr->value = talloc_strdup(hdr, colon+1);
132                 DLIST_ADD(web->input.headers, hdr);
133         }
134
135         /* ignore all other headers for now */
136         return NT_STATUS_OK;
137 }
138
139 /*
140   called when a web connection becomes readable
141 */
142 static void websrv_recv(struct stream_connection *conn, uint16_t flags)
143 {
144         struct web_server_data *wdata;
145         struct websrv_context *web = talloc_get_type_abort(conn->private_data,
146                                                            struct websrv_context);
147         NTSTATUS status;
148         uint8_t buf[1024];
149         size_t nread;
150         uint8_t *p;
151         DATA_BLOB b;
152
153         /* not the most efficient http parser ever, but good enough for us */
154         status = socket_recv(conn->socket, buf, sizeof(buf), &nread);
155         if (NT_STATUS_IS_ERR(status)) goto failed;
156         if (!NT_STATUS_IS_OK(status)) return;
157
158         if (!data_blob_append(web, &web->input.partial, buf, nread))
159                 goto failed;
160
161         /* parse any lines that are available */
162         b = web->input.partial;
163         while (!web->input.end_of_headers &&
164                (p=(uint8_t *)memchr(b.data, '\n', b.length))) {
165                 const char *line = (const char *)b.data;
166                 *p = 0;
167                 if (p != b.data && p[-1] == '\r') {
168                         p[-1] = 0;
169                 }
170                 status = http_parse_header(web, line);
171                 if (!NT_STATUS_IS_OK(status)) return;
172                 b.length -= (p - b.data) + 1;
173                 b.data = p+1;
174         }
175
176         /* keep any remaining bytes in web->input.partial */
177         if (b.length == 0) {
178                 b.data = NULL;
179         }
180         b = data_blob_talloc(web, b.data, b.length);
181         data_blob_free(&web->input.partial);
182         web->input.partial = b;
183
184         /* we finish when we have both the full headers (terminated by
185            a blank line) and any post data, as indicated by the
186            content_length */
187         if (web->input.end_of_headers &&
188             web->input.partial.length >= web->input.content_length) {
189                 if (web->input.partial.length > web->input.content_length) {
190                         web->input.partial.data[web->input.content_length] = 0;
191                 }
192                 TEVENT_FD_NOT_READABLE(web->conn->event.fde);
193
194                 /* the reference/unlink code here is quite subtle. It
195                  is needed because the rendering of the web-pages, and
196                  in particular the esp/ejs backend, is semi-async.  So
197                  we could well end up in the connection timeout code
198                  while inside http_process_input(), but we must not
199                  destroy the stack variables being used by that
200                  rendering process when we handle the timeout. */
201                 if (!talloc_reference(web->task, web)) goto failed;
202                 wdata = talloc_get_type_abort(web->task->private_data, struct web_server_data);
203                 if (wdata == NULL) goto failed;
204                 wdata->http_process_input(wdata, web);
205                 talloc_unlink(web->task, web);
206         }
207         return;
208
209 failed:
210         stream_terminate_connection(conn, "websrv_recv: failed");
211 }
212
213
214
215 /*
216   called when a web connection becomes writable
217 */
218 static void websrv_send(struct stream_connection *conn, uint16_t flags)
219 {
220         struct websrv_context *web = talloc_get_type_abort(conn->private_data,
221                                                            struct websrv_context);
222         NTSTATUS status;
223         size_t nsent;
224         DATA_BLOB b;
225
226         b = web->output.content;
227         b.data += web->output.nsent;
228         b.length -= web->output.nsent;
229
230         status = socket_send(conn->socket, &b, &nsent);
231         if (NT_STATUS_IS_ERR(status)) {
232                 stream_terminate_connection(web->conn, "socket_send: failed");
233                 return;
234         }
235         if (!NT_STATUS_IS_OK(status)) {
236                 return;
237         }
238
239         web->output.nsent += nsent;
240
241         if (web->output.content.length == web->output.nsent) {
242                 stream_terminate_connection(web->conn, "websrv_send: finished sending");
243         }
244 }
245
246 /*
247   establish a new connection to the web server
248 */
249 static void websrv_accept(struct stream_connection *conn)
250 {
251         struct web_server_data *wdata = talloc_get_type_abort(conn->private_data, struct web_server_data);
252         struct websrv_context *web;
253         struct socket_context *tls_socket;
254
255         web = talloc_zero(conn, struct websrv_context);
256         if (web == NULL) goto failed;
257
258         web->task = wdata->task;
259         web->conn = conn;
260         conn->private_data = web;
261         talloc_set_destructor(web, websrv_destructor);
262
263         tevent_add_timer(conn->event.ctx, web,
264                         timeval_current_ofs(HTTP_TIMEOUT, 0),
265                         websrv_timeout, web);
266
267         /* Overwrite the socket with a (possibly) TLS socket */
268         tls_socket = tls_init_server(wdata->tls_params, conn->socket, 
269                                      conn->event.fde, "GPHO");
270         /* We might not have TLS, or it might not have initilised */
271         if (tls_socket) {
272                 talloc_unlink(conn, conn->socket);
273                 talloc_steal(conn, tls_socket);
274                 conn->socket = tls_socket;
275         } else {
276                 DEBUG(3, ("TLS not available for web_server connections\n"));
277         }
278
279         return;
280
281 failed:
282         talloc_free(conn);
283 }
284
285
286 static const struct stream_server_ops web_stream_ops = {
287         .name                   = "web",
288         .accept_connection      = websrv_accept,
289         .recv_handler           = websrv_recv,
290         .send_handler           = websrv_send,
291 };
292
293 /*
294   startup the web server task
295 */
296 static NTSTATUS websrv_task_init(struct task_server *task)
297 {
298         NTSTATUS status;
299         uint16_t port = lpcfg_web_port(task->lp_ctx);
300         struct web_server_data *wdata;
301
302         task_server_set_title(task, "task[websrv]");
303
304         /* startup the Python processor - unfortunately we can't do this
305            per connection as that wouldn't allow for session variables */
306         wdata = talloc_zero(task, struct web_server_data);
307         if (wdata == NULL) {
308                 status = NT_STATUS_NO_MEMORY;
309                 goto failed;
310         }
311
312         wdata->task = task;
313         task->private_data = wdata;
314
315         if (lpcfg_interfaces(task->lp_ctx) && lpcfg_bind_interfaces_only(task->lp_ctx)) {
316                 int num_interfaces;
317                 int i;
318                 struct interface *ifaces;
319
320                 load_interface_list(NULL, task->lp_ctx, &ifaces);
321
322                 num_interfaces = iface_list_count(ifaces);
323                 for(i = 0; i < num_interfaces; i++) {
324                         const char *address = iface_list_n_ip(ifaces, i);
325                         status = stream_setup_socket(task,
326                                                      task->event_ctx,
327                                                      task->lp_ctx,
328                                                      task->model_ops,
329                                                      &web_stream_ops,
330                                                      "ip", address,
331                                                      &port,
332                                                      lpcfg_socket_options(task->lp_ctx),
333                                                      task,
334                                                      task->process_context);
335                         if (!NT_STATUS_IS_OK(status)) goto failed;
336                 }
337
338                 talloc_free(ifaces);
339         } else {
340                 char **wcard;
341                 int i;
342                 wcard = iface_list_wildcard(task);
343                 if (wcard == NULL) {
344                         DEBUG(0,("No wildcard addresses available\n"));
345                         status = NT_STATUS_UNSUCCESSFUL;
346                         goto failed;
347                 }
348                 for (i=0; wcard[i]; i++) {
349                         status = stream_setup_socket(task, task->event_ctx,
350                                                      task->lp_ctx,
351                                                      task->model_ops,
352                                                      &web_stream_ops,
353                                                      "ip", wcard[i],
354                                                      &port, lpcfg_socket_options(task->lp_ctx),
355                                                      wdata,
356                                                      task->process_context);
357                         if (!NT_STATUS_IS_OK(status)) goto failed;
358                 }
359                 talloc_free(wcard);
360         }
361
362         wdata->tls_params = tls_initialise(wdata, task->lp_ctx);
363         if (wdata->tls_params == NULL) {
364                 status = NT_STATUS_UNSUCCESSFUL;
365                 goto failed;
366         }
367
368         if (!wsgi_initialize(wdata)) {
369                 status = NT_STATUS_UNSUCCESSFUL;
370                 goto failed;
371         }
372
373
374         return NT_STATUS_OK;
375
376 failed:
377         task_server_terminate(task, "websrv_task_init: failed to startup web server task", true);
378         return status;
379 }
380
381
382 /* called at smbd startup - register ourselves as a server service */
383 NTSTATUS server_service_web_init(TALLOC_CTX *ctx)
384 {
385         static const struct service_details details = {
386                 .inhibit_fork_on_accept = true,
387                 .inhibit_pre_fork = true,
388                 .task_init = websrv_task_init,
389                 .post_fork = NULL
390         };
391         return register_server_service(ctx, "web", &details);
392 }