1 /*
2  * This file Copyright (C) 2008-2014 Mnemosyne LLC
3  *
4  * It may be used under the GNU GPL versions 2 or 3
5  * or any future license endorsed by Mnemosyne LLC.
6  *
7  */
8 
9 #include <errno.h>
10 #include <string.h> /* memcpy */
11 
12 #include <zlib.h>
13 
14 #include <event2/buffer.h>
15 #include <event2/event.h>
16 #include <event2/http.h>
17 #include <event2/http_struct.h> /* TODO: eventually remove this */
18 
19 #include "transmission.h"
20 #include "crypto.h" /* tr_ssha1_matches() */
21 #include "crypto-utils.h" /* tr_rand_buffer() */
22 #include "error.h"
23 #include "fdlimit.h"
24 #include "list.h"
25 #include "log.h"
26 #include "net.h"
27 #include "platform.h" /* tr_getWebClientDir() */
28 #include "ptrarray.h"
29 #include "rpcimpl.h"
30 #include "rpc-server.h"
31 #include "session.h"
32 #include "session-id.h"
33 #include "tr-assert.h"
34 #include "trevent.h"
35 #include "utils.h"
36 #include "variant.h"
37 #include "web.h"
38 
39 /* session-id is used to make cross-site request forgery attacks difficult.
40  * Don't disable this feature unless you really know what you're doing!
41  * http://en.wikipedia.org/wiki/Cross-site_request_forgery
42  * http://shiflett.org/articles/cross-site-request-forgeries
43  * http://www.webappsec.org/lists/websecurity/archive/2008-04/msg00037.html */
44 #define REQUIRE_SESSION_ID
45 
46 #define MY_NAME "RPC Server"
47 #define MY_REALM "Transmission"
48 
49 struct tr_rpc_server
50 {
51     bool isEnabled;
52     bool isPasswordEnabled;
53     bool isWhitelistEnabled;
54     bool isHostWhitelistEnabled;
55     tr_port port;
56     char* url;
57     struct tr_address bindAddress;
58     struct evhttp* httpd;
59     struct event* start_retry_timer;
60     int start_retry_counter;
61     tr_session* session;
62     char* username;
63     char* password;
64     char* whitelistStr;
65     tr_list* whitelist;
66     tr_list* hostWhitelist;
67     int loginattempts;
68 
69     bool isStreamInitialized;
70     z_stream stream;
71 };
72 
73 #define dbgmsg(...) tr_logAddDeepNamed(MY_NAME, __VA_ARGS__)
74 
75 /***
76 ****
77 ***/
78 
get_current_session_id(struct tr_rpc_server * server)79 static char const* get_current_session_id(struct tr_rpc_server* server)
80 {
81     return tr_session_id_get_current(server->session->session_id);
82 }
83 
84 /**
85 ***
86 **/
87 
send_simple_response(struct evhttp_request * req,int code,char const * text)88 static void send_simple_response(struct evhttp_request* req, int code, char const* text)
89 {
90     char const* code_text = tr_webGetResponseStr(code);
91     struct evbuffer* body = evbuffer_new();
92 
93     evbuffer_add_printf(body, "<h1>%d: %s</h1>", code, code_text);
94 
95     if (text != NULL)
96     {
97         evbuffer_add_printf(body, "%s", text);
98     }
99 
100     evhttp_send_reply(req, code, code_text, body);
101 
102     evbuffer_free(body);
103 }
104 
105 struct tr_mimepart
106 {
107     char* headers;
108     size_t headers_len;
109     char* body;
110     size_t body_len;
111 };
112 
tr_mimepart_free(struct tr_mimepart * p)113 static void tr_mimepart_free(struct tr_mimepart* p)
114 {
115     tr_free(p->body);
116     tr_free(p->headers);
117     tr_free(p);
118 }
119 
extract_parts_from_multipart(struct evkeyvalq const * headers,struct evbuffer * body,tr_ptrArray * setme_parts)120 static void extract_parts_from_multipart(struct evkeyvalq const* headers, struct evbuffer* body, tr_ptrArray* setme_parts)
121 {
122     char const* content_type = evhttp_find_header(headers, "Content-Type");
123     char const* in = (char const*)evbuffer_pullup(body, -1);
124     size_t inlen = evbuffer_get_length(body);
125 
126     char const* boundary_key = "boundary=";
127     char const* boundary_key_begin = content_type != NULL ? strstr(content_type, boundary_key) : NULL;
128     char const* boundary_val = boundary_key_begin != NULL ? boundary_key_begin + strlen(boundary_key) : "arglebargle";
129     char* boundary = tr_strdup_printf("--%s", boundary_val);
130     size_t const boundary_len = strlen(boundary);
131 
132     char const* delim = tr_memmem(in, inlen, boundary, boundary_len);
133 
134     while (delim != NULL)
135     {
136         size_t part_len;
137         char const* part = delim + boundary_len;
138 
139         inlen -= part - in;
140         in = part;
141 
142         delim = tr_memmem(in, inlen, boundary, boundary_len);
143         part_len = delim != NULL ? (size_t)(delim - part) : inlen;
144 
145         if (part_len != 0)
146         {
147             char const* rnrn = tr_memmem(part, part_len, "\r\n\r\n", 4);
148 
149             if (rnrn != NULL)
150             {
151                 struct tr_mimepart* p = tr_new(struct tr_mimepart, 1);
152                 p->headers_len = (size_t)(rnrn - part);
153                 p->headers = tr_strndup(part, p->headers_len);
154                 p->body_len = (size_t)((part + part_len) - (rnrn + 4));
155                 p->body = tr_strndup(rnrn + 4, p->body_len);
156                 tr_ptrArrayAppend(setme_parts, p);
157             }
158         }
159     }
160 
161     tr_free(boundary);
162 }
163 
handle_upload(struct evhttp_request * req,struct tr_rpc_server * server)164 static void handle_upload(struct evhttp_request* req, struct tr_rpc_server* server)
165 {
166     if (req->type != EVHTTP_REQ_POST)
167     {
168         send_simple_response(req, 405, NULL);
169     }
170     else
171     {
172         int n;
173         bool hasSessionId = false;
174         tr_ptrArray parts = TR_PTR_ARRAY_INIT;
175 
176         char const* query = strchr(req->uri, '?');
177         bool const paused = query != NULL && strstr(query + 1, "paused=true") != NULL;
178 
179         extract_parts_from_multipart(req->input_headers, req->input_buffer, &parts);
180         n = tr_ptrArraySize(&parts);
181 
182         /* first look for the session id */
183         for (int i = 0; i < n; ++i)
184         {
185             struct tr_mimepart* p = tr_ptrArrayNth(&parts, i);
186 
187             if (tr_strcasestr(p->headers, TR_RPC_SESSION_ID_HEADER) != NULL)
188             {
189                 char const* ours = get_current_session_id(server);
190                 size_t const ourlen = strlen(ours);
191                 hasSessionId = ourlen <= p->body_len && memcmp(p->body, ours, ourlen) == 0;
192                 break;
193             }
194         }
195 
196         if (!hasSessionId)
197         {
198             int code = 409;
199             char const* codetext = tr_webGetResponseStr(code);
200             struct evbuffer* body = evbuffer_new();
201             evbuffer_add_printf(body, "%s", "{ \"success\": false, \"msg\": \"Bad Session-Id\" }");
202             evhttp_send_reply(req, code, codetext, body);
203             evbuffer_free(body);
204         }
205         else
206         {
207             for (int i = 0; i < n; ++i)
208             {
209                 struct tr_mimepart* p = tr_ptrArrayNth(&parts, i);
210                 size_t body_len = p->body_len;
211                 tr_variant top;
212                 tr_variant* args;
213                 tr_variant test;
214                 bool have_source = false;
215                 char* body = p->body;
216 
217                 if (body_len >= 2 && memcmp(&body[body_len - 2], "\r\n", 2) == 0)
218                 {
219                     body_len -= 2;
220                 }
221 
222                 tr_variantInitDict(&top, 2);
223                 tr_variantDictAddStr(&top, TR_KEY_method, "torrent-add");
224                 args = tr_variantDictAddDict(&top, TR_KEY_arguments, 2);
225                 tr_variantDictAddBool(args, TR_KEY_paused, paused);
226 
227                 if (tr_urlIsValid(body, body_len))
228                 {
229                     tr_variantDictAddRaw(args, TR_KEY_filename, body, body_len);
230                     have_source = true;
231                 }
232                 else if (tr_variantFromBenc(&test, body, body_len) == 0)
233                 {
234                     char* b64 = tr_base64_encode(body, body_len, NULL);
235                     tr_variantDictAddStr(args, TR_KEY_metainfo, b64);
236                     tr_free(b64);
237                     have_source = true;
238                 }
239 
240                 if (have_source)
241                 {
242                     tr_rpc_request_exec_json(server->session, &top, NULL, NULL);
243                 }
244 
245                 tr_variantFree(&top);
246             }
247         }
248 
249         tr_ptrArrayDestruct(&parts, (PtrArrayForeachFunc)tr_mimepart_free);
250 
251         /* send "success" response */
252         {
253             int code = HTTP_OK;
254             char const* codetext = tr_webGetResponseStr(code);
255             struct evbuffer* body = evbuffer_new();
256             evbuffer_add_printf(body, "%s", "{ \"success\": true, \"msg\": \"Torrent Added\" }");
257             evhttp_send_reply(req, code, codetext, body);
258             evbuffer_free(body);
259         }
260     }
261 }
262 
263 /***
264 ****
265 ***/
266 
mimetype_guess(char const * path)267 static char const* mimetype_guess(char const* path)
268 {
269     struct
270     {
271         char const* suffix;
272         char const* mime_type;
273     }
274     const types[] =
275     {
276         /* these are the ones we need for serving the web client's files... */
277         { "css", "text/css" },
278         { "gif", "image/gif" },
279         { "html", "text/html" },
280         { "ico", "image/vnd.microsoft.icon" },
281         { "js", "application/javascript" },
282         { "png", "image/png" }
283     };
284     char const* dot = strrchr(path, '.');
285 
286     for (unsigned int i = 0; dot != NULL && i < TR_N_ELEMENTS(types); ++i)
287     {
288         if (strcmp(dot + 1, types[i].suffix) == 0)
289         {
290             return types[i].mime_type;
291         }
292     }
293 
294     return "application/octet-stream";
295 }
296 
add_response(struct evhttp_request * req,struct tr_rpc_server * server,struct evbuffer * out,struct evbuffer * content)297 static void add_response(struct evhttp_request* req, struct tr_rpc_server* server, struct evbuffer* out,
298     struct evbuffer* content)
299 {
300     char const* key = "Accept-Encoding";
301     char const* encoding = evhttp_find_header(req->input_headers, key);
302     bool const do_compress = encoding != NULL && strstr(encoding, "gzip") != NULL;
303 
304     if (!do_compress)
305     {
306         evbuffer_add_buffer(out, content);
307     }
308     else
309     {
310         int state;
311         struct evbuffer_iovec iovec[1];
312         void* content_ptr = evbuffer_pullup(content, -1);
313         size_t const content_len = evbuffer_get_length(content);
314 
315         if (!server->isStreamInitialized)
316         {
317             int compressionLevel;
318 
319             server->isStreamInitialized = true;
320             server->stream.zalloc = (alloc_func)Z_NULL;
321             server->stream.zfree = (free_func)Z_NULL;
322             server->stream.opaque = (voidpf)Z_NULL;
323 
324             /* zlib's manual says: "Add 16 to windowBits to write a simple gzip header
325              * and trailer around the compressed data instead of a zlib wrapper." */
326 #ifdef TR_LIGHTWEIGHT
327             compressionLevel = Z_DEFAULT_COMPRESSION;
328 #else
329             compressionLevel = Z_BEST_COMPRESSION;
330 #endif
331             deflateInit2(&server->stream, compressionLevel, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY);
332         }
333 
334         server->stream.next_in = content_ptr;
335         server->stream.avail_in = content_len;
336 
337         /* allocate space for the raw data and call deflate() just once --
338          * we won't use the deflated data if it's longer than the raw data,
339          * so it's okay to let deflate() run out of output buffer space */
340         evbuffer_reserve_space(out, content_len, iovec, 1);
341         server->stream.next_out = iovec[0].iov_base;
342         server->stream.avail_out = iovec[0].iov_len;
343         state = deflate(&server->stream, Z_FINISH);
344 
345         if (state == Z_STREAM_END)
346         {
347             iovec[0].iov_len -= server->stream.avail_out;
348 
349 #if 0
350 
351             fprintf(stderr, "compressed response is %.2f of original (raw==%zu bytes; compressed==%zu)\n",
352                 (double)evbuffer_get_length(out) / content_len, content_len, evbuffer_get_length(out));
353 
354 #endif
355 
356             evhttp_add_header(req->output_headers, "Content-Encoding", "gzip");
357         }
358         else
359         {
360             memcpy(iovec[0].iov_base, content_ptr, content_len);
361             iovec[0].iov_len = content_len;
362         }
363 
364         evbuffer_commit_space(out, iovec, 1);
365         deflateReset(&server->stream);
366     }
367 }
368 
add_time_header(struct evkeyvalq * headers,char const * key,time_t value)369 static void add_time_header(struct evkeyvalq* headers, char const* key, time_t value)
370 {
371     /* According to RFC 2616 this must follow RFC 1123's date format,
372        so use gmtime instead of localtime... */
373     char buf[128];
374     struct tm tm = *gmtime(&value);
375     strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S GMT", &tm);
376     evhttp_add_header(headers, key, buf);
377 }
378 
evbuffer_ref_cleanup_tr_free(void const * data UNUSED,size_t datalen UNUSED,void * extra)379 static void evbuffer_ref_cleanup_tr_free(void const* data UNUSED, size_t datalen UNUSED, void* extra)
380 {
381     tr_free(extra);
382 }
383 
serve_file(struct evhttp_request * req,struct tr_rpc_server * server,char const * filename)384 static void serve_file(struct evhttp_request* req, struct tr_rpc_server* server, char const* filename)
385 {
386     if (req->type != EVHTTP_REQ_GET)
387     {
388         evhttp_add_header(req->output_headers, "Allow", "GET");
389         send_simple_response(req, 405, NULL);
390     }
391     else
392     {
393         void* file;
394         size_t file_len;
395         tr_error* error = NULL;
396 
397         file_len = 0;
398         file = tr_loadFile(filename, &file_len, &error);
399 
400         if (file == NULL)
401         {
402             char* tmp = tr_strdup_printf("%s (%s)", filename, error->message);
403             send_simple_response(req, HTTP_NOTFOUND, tmp);
404             tr_free(tmp);
405             tr_error_free(error);
406         }
407         else
408         {
409             struct evbuffer* content;
410             struct evbuffer* out;
411             time_t const now = tr_time();
412 
413             content = evbuffer_new();
414             evbuffer_add_reference(content, file, file_len, evbuffer_ref_cleanup_tr_free, file);
415 
416             out = evbuffer_new();
417             evhttp_add_header(req->output_headers, "Content-Type", mimetype_guess(filename));
418             add_time_header(req->output_headers, "Date", now);
419             add_time_header(req->output_headers, "Expires", now + (24 * 60 * 60));
420             add_response(req, server, out, content);
421             evhttp_send_reply(req, HTTP_OK, "OK", out);
422 
423             evbuffer_free(out);
424             evbuffer_free(content);
425         }
426     }
427 }
428 
handle_web_client(struct evhttp_request * req,struct tr_rpc_server * server)429 static void handle_web_client(struct evhttp_request* req, struct tr_rpc_server* server)
430 {
431     char const* webClientDir = tr_getWebClientDir(server->session);
432 
433     if (tr_str_is_empty(webClientDir))
434     {
435         send_simple_response(req, HTTP_NOTFOUND,
436             "<p>Couldn't find Transmission's web interface files!</p>"
437             "<p>Users: to tell Transmission where to look, "
438             "set the TRANSMISSION_WEB_HOME environment "
439             "variable to the folder where the web interface's "
440             "index.html is located.</p>"
441             "<p>Package Builders: to set a custom default at compile time, "
442             "#define PACKAGE_DATA_DIR in libtransmission/platform.c "
443             "or tweak tr_getClutchDir() by hand.</p>");
444     }
445     else
446     {
447         char* pch;
448         char* subpath;
449 
450         subpath = tr_strdup(req->uri + strlen(server->url) + 4);
451 
452         if ((pch = strchr(subpath, '?')) != NULL)
453         {
454             *pch = '\0';
455         }
456 
457         if (strstr(subpath, "..") != NULL)
458         {
459             send_simple_response(req, HTTP_NOTFOUND, "<p>Tsk, tsk.</p>");
460         }
461         else
462         {
463             char* filename = tr_strdup_printf("%s%s%s", webClientDir, TR_PATH_DELIMITER_STR,
464                 tr_str_is_empty(subpath) ? "index.html" : subpath);
465             serve_file(req, server, filename);
466             tr_free(filename);
467         }
468 
469         tr_free(subpath);
470     }
471 }
472 
473 struct rpc_response_data
474 {
475     struct evhttp_request* req;
476     struct tr_rpc_server* server;
477 };
478 
rpc_response_func(tr_session * session UNUSED,tr_variant * response,void * user_data)479 static void rpc_response_func(tr_session* session UNUSED, tr_variant* response, void* user_data)
480 {
481     struct rpc_response_data* data = user_data;
482     struct evbuffer* response_buf = tr_variantToBuf(response, TR_VARIANT_FMT_JSON_LEAN);
483     struct evbuffer* buf = evbuffer_new();
484 
485     add_response(data->req, data->server, buf, response_buf);
486     evhttp_add_header(data->req->output_headers, "Content-Type", "application/json; charset=UTF-8");
487     evhttp_send_reply(data->req, HTTP_OK, "OK", buf);
488 
489     evbuffer_free(buf);
490     evbuffer_free(response_buf);
491     tr_free(data);
492 }
493 
handle_rpc_from_json(struct evhttp_request * req,struct tr_rpc_server * server,char const * json,size_t json_len)494 static void handle_rpc_from_json(struct evhttp_request* req, struct tr_rpc_server* server, char const* json, size_t json_len)
495 {
496     tr_variant top;
497     bool have_content = tr_variantFromJson(&top, json, json_len) == 0;
498     struct rpc_response_data* data;
499 
500     data = tr_new0(struct rpc_response_data, 1);
501     data->req = req;
502     data->server = server;
503 
504     tr_rpc_request_exec_json(server->session, have_content ? &top : NULL, rpc_response_func, data);
505 
506     if (have_content)
507     {
508         tr_variantFree(&top);
509     }
510 }
511 
handle_rpc(struct evhttp_request * req,struct tr_rpc_server * server)512 static void handle_rpc(struct evhttp_request* req, struct tr_rpc_server* server)
513 {
514     if (req->type == EVHTTP_REQ_POST)
515     {
516         handle_rpc_from_json(req, server, (char const*)evbuffer_pullup(req->input_buffer, -1),
517             evbuffer_get_length(req->input_buffer));
518         return;
519     }
520 
521     if (req->type == EVHTTP_REQ_GET)
522     {
523         char const* q = strchr(req->uri, '?');
524 
525         if (q != NULL)
526         {
527             struct rpc_response_data* data = tr_new0(struct rpc_response_data, 1);
528             data->req = req;
529             data->server = server;
530             tr_rpc_request_exec_uri(server->session, q + 1, TR_BAD_SIZE, rpc_response_func, data);
531             return;
532         }
533     }
534 
535     send_simple_response(req, 405, NULL);
536 }
537 
isAddressAllowed(tr_rpc_server const * server,char const * address)538 static bool isAddressAllowed(tr_rpc_server const* server, char const* address)
539 {
540     if (!server->isWhitelistEnabled)
541     {
542         return true;
543     }
544 
545     for (tr_list* l = server->whitelist; l != NULL; l = l->next)
546     {
547         if (tr_wildmat(address, l->data))
548         {
549             return true;
550         }
551     }
552 
553     return false;
554 }
555 
isIPAddressWithOptionalPort(char const * host)556 static bool isIPAddressWithOptionalPort(char const* host)
557 {
558     struct sockaddr_storage address;
559     int address_len = sizeof(address);
560 
561     /* TODO: move to net.{c,h} */
562     return evutil_parse_sockaddr_port(host, (struct sockaddr*)&address, &address_len) != -1;
563 }
564 
isHostnameAllowed(tr_rpc_server const * server,struct evhttp_request * req)565 static bool isHostnameAllowed(tr_rpc_server const* server, struct evhttp_request* req)
566 {
567     /* If password auth is enabled, any hostname is permitted. */
568     if (server->isPasswordEnabled)
569     {
570         return true;
571     }
572 
573     /* If whitelist is disabled, no restrictions. */
574     if (!server->isHostWhitelistEnabled)
575     {
576         return true;
577     }
578 
579     char const* const host = evhttp_find_header(req->input_headers, "Host");
580 
581     /* No host header, invalid request. */
582     if (host == NULL)
583     {
584         return false;
585     }
586 
587     /* IP address is always acceptable. */
588     if (isIPAddressWithOptionalPort(host))
589     {
590         return true;
591     }
592 
593     /* Host header might include the port. */
594     char* const hostname = tr_strndup(host, strcspn(host, ":"));
595 
596     /* localhost is always acceptable. */
597     if (strcmp(hostname, "localhost") == 0 || strcmp(hostname, "localhost.") == 0)
598     {
599         tr_free(hostname);
600         return true;
601     }
602 
603     /* Otherwise, hostname must be whitelisted. */
604     for (tr_list* l = server->hostWhitelist; l != NULL; l = l->next)
605     {
606         if (tr_wildmat(hostname, l->data))
607         {
608             tr_free(hostname);
609             return true;
610         }
611     }
612 
613     tr_free(hostname);
614     return false;
615 }
616 
test_session_id(struct tr_rpc_server * server,struct evhttp_request * req)617 static bool test_session_id(struct tr_rpc_server* server, struct evhttp_request* req)
618 {
619     char const* ours = get_current_session_id(server);
620     char const* theirs = evhttp_find_header(req->input_headers, TR_RPC_SESSION_ID_HEADER);
621     bool const success = theirs != NULL && strcmp(theirs, ours) == 0;
622     return success;
623 }
624 
handle_request(struct evhttp_request * req,void * arg)625 static void handle_request(struct evhttp_request* req, void* arg)
626 {
627     struct tr_rpc_server* server = arg;
628 
629     if (req != NULL && req->evcon != NULL)
630     {
631         char const* auth;
632         char* user = NULL;
633         char* pass = NULL;
634 
635         evhttp_add_header(req->output_headers, "Server", MY_REALM);
636 
637         if (server->loginattempts == 100)
638         {
639             send_simple_response(req, 403, "<p>Too many unsuccessful login attempts. Please restart transmission-daemon.</p>");
640             return;
641         }
642 
643         if (!isAddressAllowed(server, req->remote_host))
644         {
645             send_simple_response(req, 403,
646                 "<p>Unauthorized IP Address.</p>"
647                 "<p>Either disable the IP address whitelist or add your address to it.</p>"
648                 "<p>If you're editing settings.json, see the 'rpc-whitelist' and 'rpc-whitelist-enabled' entries.</p>"
649                 "<p>If you're still using ACLs, use a whitelist instead. See the transmission-daemon manpage for details.</p>");
650             return;
651         }
652 
653         auth = evhttp_find_header(req->input_headers, "Authorization");
654 
655         if (auth != NULL && evutil_ascii_strncasecmp(auth, "basic ", 6) == 0)
656         {
657             char* p = tr_base64_decode_str(auth + 6, NULL);
658 
659             if (p != NULL)
660             {
661                 if ((pass = strchr(p, ':')) != NULL)
662                 {
663                     user = p;
664                     *pass++ = '\0';
665                 }
666                 else
667                 {
668                     tr_free(p);
669                 }
670             }
671         }
672 
673         if (server->isPasswordEnabled && (pass == NULL || user == NULL || strcmp(server->username, user) != 0 ||
674             !tr_ssha1_matches(server->password, pass)))
675         {
676             evhttp_add_header(req->output_headers, "WWW-Authenticate", "Basic realm=\"" MY_REALM "\"");
677             server->loginattempts++;
678             char* unauthuser = tr_strdup_printf("<p>Unauthorized User. %d unsuccessful login attempts.</p>",
679                 server->loginattempts);
680             send_simple_response(req, 401, unauthuser);
681             tr_free(unauthuser);
682             tr_free(user);
683             return;
684         }
685 
686         server->loginattempts = 0;
687 
688         if (strncmp(req->uri, server->url, strlen(server->url)) != 0)
689         {
690             char* location = tr_strdup_printf("%sweb/", server->url);
691             evhttp_add_header(req->output_headers, "Location", location);
692             send_simple_response(req, HTTP_MOVEPERM, NULL);
693             tr_free(location);
694         }
695         else if (strncmp(req->uri + strlen(server->url), "web/", 4) == 0)
696         {
697             handle_web_client(req, server);
698         }
699         else if (strcmp(req->uri + strlen(server->url), "upload") == 0)
700         {
701             handle_upload(req, server);
702         }
703         else if (!isHostnameAllowed(server, req))
704         {
705             char* const tmp = tr_strdup_printf(
706                 "<p>Transmission received your request, but the hostname was unrecognized.</p>"
707                 "<p>To fix this, choose one of the following options:"
708                 "<ul>"
709                 "<li>Enable password authentication, then any hostname is allowed.</li>"
710                 "<li>Add the hostname you want to use to the whitelist in settings.</li>"
711                 "</ul></p>"
712                 "<p>If you're editing settings.json, see the 'rpc-host-whitelist' and 'rpc-host-whitelist-enabled' entries.</p>"
713                 "<p>This requirement has been added to help prevent "
714                 "<a href=\"https://en.wikipedia.org/wiki/DNS_rebinding\">DNS Rebinding</a> "
715                 "attacks.</p>");
716             send_simple_response(req, 421, tmp);
717             tr_free(tmp);
718         }
719 
720 #ifdef REQUIRE_SESSION_ID
721 
722         else if (!test_session_id(server, req))
723         {
724             char const* sessionId = get_current_session_id(server);
725             char* tmp = tr_strdup_printf(
726                 "<p>Your request had an invalid session-id header.</p>"
727                 "<p>To fix this, follow these steps:"
728                 "<ol><li> When reading a response, get its X-Transmission-Session-Id header and remember it"
729                 "<li> Add the updated header to your outgoing requests"
730                 "<li> When you get this 409 error message, resend your request with the updated header"
731                 "</ol></p>"
732                 "<p>This requirement has been added to help prevent "
733                 "<a href=\"https://en.wikipedia.org/wiki/Cross-site_request_forgery\">CSRF</a> "
734                 "attacks.</p>"
735                 "<p><code>%s: %s</code></p>",
736                 TR_RPC_SESSION_ID_HEADER, sessionId);
737             evhttp_add_header(req->output_headers, TR_RPC_SESSION_ID_HEADER, sessionId);
738             send_simple_response(req, 409, tmp);
739             tr_free(tmp);
740         }
741 
742 #endif
743 
744         else if (strncmp(req->uri + strlen(server->url), "rpc", 3) == 0)
745         {
746             handle_rpc(req, server);
747         }
748         else
749         {
750             send_simple_response(req, HTTP_NOTFOUND, req->uri);
751         }
752 
753         tr_free(user);
754     }
755 }
756 
757 enum
758 {
759     SERVER_START_RETRY_COUNT = 10,
760     SERVER_START_RETRY_DELAY_STEP = 3,
761     SERVER_START_RETRY_DELAY_INCREMENT = 5,
762     SERVER_START_RETRY_MAX_DELAY = 60
763 };
764 
765 static void startServer(void* vserver);
766 
rpc_server_on_start_retry(evutil_socket_t fd UNUSED,short type UNUSED,void * context)767 static void rpc_server_on_start_retry(evutil_socket_t fd UNUSED, short type UNUSED, void* context)
768 {
769     startServer(context);
770 }
771 
rpc_server_start_retry(tr_rpc_server * server)772 static int rpc_server_start_retry(tr_rpc_server* server)
773 {
774     int retry_delay = (server->start_retry_counter / SERVER_START_RETRY_DELAY_STEP + 1) * SERVER_START_RETRY_DELAY_INCREMENT;
775     retry_delay = MIN(retry_delay, SERVER_START_RETRY_MAX_DELAY);
776 
777     if (server->start_retry_timer == NULL)
778     {
779         server->start_retry_timer = evtimer_new(server->session->event_base, rpc_server_on_start_retry, server);
780     }
781 
782     tr_timerAdd(server->start_retry_timer, retry_delay, 0);
783     ++server->start_retry_counter;
784 
785     return retry_delay;
786 }
787 
rpc_server_start_retry_cancel(tr_rpc_server * server)788 static void rpc_server_start_retry_cancel(tr_rpc_server* server)
789 {
790     if (server->start_retry_timer != NULL)
791     {
792         event_free(server->start_retry_timer);
793         server->start_retry_timer = NULL;
794     }
795 
796     server->start_retry_counter = 0;
797 }
798 
startServer(void * vserver)799 static void startServer(void* vserver)
800 {
801     tr_rpc_server* server = vserver;
802 
803     if (server->httpd != NULL)
804     {
805         return;
806     }
807 
808     struct evhttp* httpd = evhttp_new(server->session->event_base);
809 
810     char const* address = tr_rpcGetBindAddress(server);
811 
812     int const port = server->port;
813 
814     if (evhttp_bind_socket(httpd, address, port) == -1)
815     {
816         evhttp_free(httpd);
817 
818         if (server->start_retry_counter < SERVER_START_RETRY_COUNT)
819         {
820             int const retry_delay = rpc_server_start_retry(server);
821 
822             tr_logAddNamedDbg(MY_NAME, "Unable to bind to %s:%d, retrying in %d seconds", address, port, retry_delay);
823             return;
824         }
825 
826         tr_logAddNamedError(MY_NAME, "Unable to bind to %s:%d after %d attempts, giving up", address, port,
827             SERVER_START_RETRY_COUNT);
828     }
829     else
830     {
831         evhttp_set_gencb(httpd, handle_request, server);
832         server->httpd = httpd;
833 
834         tr_logAddNamedDbg(MY_NAME, "Started listening on %s:%d", address, port);
835     }
836 
837     rpc_server_start_retry_cancel(server);
838 }
839 
stopServer(tr_rpc_server * server)840 static void stopServer(tr_rpc_server* server)
841 {
842     rpc_server_start_retry_cancel(server);
843 
844     struct evhttp* httpd = server->httpd;
845 
846     if (httpd == NULL)
847     {
848         return;
849     }
850 
851     char const* address = tr_rpcGetBindAddress(server);
852     int const port = server->port;
853 
854     server->httpd = NULL;
855     evhttp_free(httpd);
856 
857     tr_logAddNamedDbg(MY_NAME, "Stopped listening on %s:%d", address, port);
858 }
859 
onEnabledChanged(void * vserver)860 static void onEnabledChanged(void* vserver)
861 {
862     tr_rpc_server* server = vserver;
863 
864     if (!server->isEnabled)
865     {
866         stopServer(server);
867     }
868     else
869     {
870         startServer(server);
871     }
872 }
873 
tr_rpcSetEnabled(tr_rpc_server * server,bool isEnabled)874 void tr_rpcSetEnabled(tr_rpc_server* server, bool isEnabled)
875 {
876     server->isEnabled = isEnabled;
877 
878     tr_runInEventThread(server->session, onEnabledChanged, server);
879 }
880 
tr_rpcIsEnabled(tr_rpc_server const * server)881 bool tr_rpcIsEnabled(tr_rpc_server const* server)
882 {
883     return server->isEnabled;
884 }
885 
restartServer(void * vserver)886 static void restartServer(void* vserver)
887 {
888     tr_rpc_server* server = vserver;
889 
890     if (server->isEnabled)
891     {
892         stopServer(server);
893         startServer(server);
894     }
895 }
896 
tr_rpcSetPort(tr_rpc_server * server,tr_port port)897 void tr_rpcSetPort(tr_rpc_server* server, tr_port port)
898 {
899     TR_ASSERT(server != NULL);
900 
901     if (server->port != port)
902     {
903         server->port = port;
904 
905         if (server->isEnabled)
906         {
907             tr_runInEventThread(server->session, restartServer, server);
908         }
909     }
910 }
911 
tr_rpcGetPort(tr_rpc_server const * server)912 tr_port tr_rpcGetPort(tr_rpc_server const* server)
913 {
914     return server->port;
915 }
916 
tr_rpcSetUrl(tr_rpc_server * server,char const * url)917 void tr_rpcSetUrl(tr_rpc_server* server, char const* url)
918 {
919     char* tmp = server->url;
920     server->url = tr_strdup(url);
921     dbgmsg("setting our URL to [%s]", server->url);
922     tr_free(tmp);
923 }
924 
tr_rpcGetUrl(tr_rpc_server const * server)925 char const* tr_rpcGetUrl(tr_rpc_server const* server)
926 {
927     return server->url != NULL ? server->url : "";
928 }
929 
tr_rpcSetList(char const * whitelistStr,tr_list ** list)930 static void tr_rpcSetList(char const* whitelistStr, tr_list** list)
931 {
932     void* tmp;
933 
934     /* clear out the old whitelist entries */
935     while ((tmp = tr_list_pop_front(list)) != NULL)
936     {
937         tr_free(tmp);
938     }
939 
940     /* build the new whitelist entries */
941     for (char const* walk = whitelistStr; !tr_str_is_empty(walk);)
942     {
943         char const* delimiters = " ,;";
944         size_t const len = strcspn(walk, delimiters);
945         char* token = tr_strndup(walk, len);
946 
947         tr_list_append(list, token);
948 
949         if (strcspn(token, "+-") < len)
950         {
951             tr_logAddNamedInfo(MY_NAME,
952                 "Adding address to whitelist: %s (And it has a '+' or '-'!  Are you using an old ACL by mistake?)", token);
953         }
954         else
955         {
956             tr_logAddNamedInfo(MY_NAME, "Adding address to whitelist: %s", token);
957         }
958 
959         walk += len;
960 
961         if (*walk == '\0')
962         {
963             break;
964         }
965 
966         ++walk;
967     }
968 }
969 
tr_rpcSetHostWhitelist(tr_rpc_server * server,char const * whitelistStr)970 void tr_rpcSetHostWhitelist(tr_rpc_server* server, char const* whitelistStr)
971 {
972     tr_rpcSetList(whitelistStr, &server->hostWhitelist);
973 }
974 
tr_rpcSetWhitelist(tr_rpc_server * server,char const * whitelistStr)975 void tr_rpcSetWhitelist(tr_rpc_server* server, char const* whitelistStr)
976 {
977     /* keep the string */
978     char* const tmp = server->whitelistStr;
979     server->whitelistStr = tr_strdup(whitelistStr);
980     tr_free(tmp);
981 
982     tr_rpcSetList(whitelistStr, &server->whitelist);
983 }
984 
tr_rpcGetWhitelist(tr_rpc_server const * server)985 char const* tr_rpcGetWhitelist(tr_rpc_server const* server)
986 {
987     return server->whitelistStr != NULL ? server->whitelistStr : "";
988 }
989 
tr_rpcSetWhitelistEnabled(tr_rpc_server * server,bool isEnabled)990 void tr_rpcSetWhitelistEnabled(tr_rpc_server* server, bool isEnabled)
991 {
992     server->isWhitelistEnabled = isEnabled;
993 }
994 
tr_rpcGetWhitelistEnabled(tr_rpc_server const * server)995 bool tr_rpcGetWhitelistEnabled(tr_rpc_server const* server)
996 {
997     return server->isWhitelistEnabled;
998 }
999 
tr_rpcSetHostWhitelistEnabled(tr_rpc_server * server,bool isEnabled)1000 void tr_rpcSetHostWhitelistEnabled(tr_rpc_server* server, bool isEnabled)
1001 {
1002     server->isHostWhitelistEnabled = isEnabled;
1003 }
1004 
1005 /****
1006 *****  PASSWORD
1007 ****/
1008 
tr_rpcSetUsername(tr_rpc_server * server,char const * username)1009 void tr_rpcSetUsername(tr_rpc_server* server, char const* username)
1010 {
1011     char* tmp = server->username;
1012     server->username = tr_strdup(username);
1013     dbgmsg("setting our Username to [%s]", server->username);
1014     tr_free(tmp);
1015 }
1016 
tr_rpcGetUsername(tr_rpc_server const * server)1017 char const* tr_rpcGetUsername(tr_rpc_server const* server)
1018 {
1019     return server->username != NULL ? server->username : "";
1020 }
1021 
tr_rpcSetPassword(tr_rpc_server * server,char const * password)1022 void tr_rpcSetPassword(tr_rpc_server* server, char const* password)
1023 {
1024     tr_free(server->password);
1025 
1026     if (*password != '{')
1027     {
1028         server->password = tr_ssha1(password);
1029     }
1030     else
1031     {
1032         server->password = strdup(password);
1033     }
1034 
1035     dbgmsg("setting our Password to [%s]", server->password);
1036 }
1037 
tr_rpcGetPassword(tr_rpc_server const * server)1038 char const* tr_rpcGetPassword(tr_rpc_server const* server)
1039 {
1040     return server->password != NULL ? server->password : "";
1041 }
1042 
tr_rpcSetPasswordEnabled(tr_rpc_server * server,bool isEnabled)1043 void tr_rpcSetPasswordEnabled(tr_rpc_server* server, bool isEnabled)
1044 {
1045     server->isPasswordEnabled = isEnabled;
1046     dbgmsg("setting 'password enabled' to %d", (int)isEnabled);
1047 }
1048 
tr_rpcIsPasswordEnabled(tr_rpc_server const * server)1049 bool tr_rpcIsPasswordEnabled(tr_rpc_server const* server)
1050 {
1051     return server->isPasswordEnabled;
1052 }
1053 
tr_rpcGetBindAddress(tr_rpc_server const * server)1054 char const* tr_rpcGetBindAddress(tr_rpc_server const* server)
1055 {
1056     return tr_address_to_string(&server->bindAddress);
1057 }
1058 
1059 /****
1060 *****  LIFE CYCLE
1061 ****/
1062 
closeServer(void * vserver)1063 static void closeServer(void* vserver)
1064 {
1065     void* tmp;
1066     tr_rpc_server* s = vserver;
1067 
1068     stopServer(s);
1069 
1070     while ((tmp = tr_list_pop_front(&s->whitelist)) != NULL)
1071     {
1072         tr_free(tmp);
1073     }
1074 
1075     if (s->isStreamInitialized)
1076     {
1077         deflateEnd(&s->stream);
1078     }
1079 
1080     tr_free(s->url);
1081     tr_free(s->whitelistStr);
1082     tr_free(s->username);
1083     tr_free(s->password);
1084     tr_free(s);
1085 }
1086 
tr_rpcClose(tr_rpc_server ** ps)1087 void tr_rpcClose(tr_rpc_server** ps)
1088 {
1089     tr_runInEventThread((*ps)->session, closeServer, *ps);
1090     *ps = NULL;
1091 }
1092 
missing_settings_key(tr_quark const q)1093 static void missing_settings_key(tr_quark const q)
1094 {
1095     char const* str = tr_quark_get_string(q, NULL);
1096     tr_logAddNamedError(MY_NAME, _("Couldn't find settings key \"%s\""), str);
1097 }
1098 
tr_rpcInit(tr_session * session,tr_variant * settings)1099 tr_rpc_server* tr_rpcInit(tr_session* session, tr_variant* settings)
1100 {
1101     tr_rpc_server* s;
1102     bool boolVal;
1103     int64_t i;
1104     char const* str;
1105     tr_quark key;
1106     tr_address address;
1107 
1108     s = tr_new0(tr_rpc_server, 1);
1109     s->session = session;
1110 
1111     key = TR_KEY_rpc_enabled;
1112 
1113     if (!tr_variantDictFindBool(settings, key, &boolVal))
1114     {
1115         missing_settings_key(key);
1116     }
1117     else
1118     {
1119         s->isEnabled = boolVal;
1120     }
1121 
1122     key = TR_KEY_rpc_port;
1123 
1124     if (!tr_variantDictFindInt(settings, key, &i))
1125     {
1126         missing_settings_key(key);
1127     }
1128     else
1129     {
1130         s->port = i;
1131     }
1132 
1133     key = TR_KEY_rpc_url;
1134 
1135     if (!tr_variantDictFindStr(settings, key, &str, NULL))
1136     {
1137         missing_settings_key(key);
1138     }
1139     else
1140     {
1141         s->url = tr_strdup(str);
1142     }
1143 
1144     key = TR_KEY_rpc_whitelist_enabled;
1145 
1146     if (!tr_variantDictFindBool(settings, key, &boolVal))
1147     {
1148         missing_settings_key(key);
1149     }
1150     else
1151     {
1152         tr_rpcSetWhitelistEnabled(s, boolVal);
1153     }
1154 
1155     key = TR_KEY_rpc_host_whitelist_enabled;
1156 
1157     if (!tr_variantDictFindBool(settings, key, &boolVal))
1158     {
1159         missing_settings_key(key);
1160     }
1161     else
1162     {
1163         tr_rpcSetHostWhitelistEnabled(s, boolVal);
1164     }
1165 
1166     key = TR_KEY_rpc_host_whitelist;
1167 
1168     if (!tr_variantDictFindStr(settings, key, &str, NULL) && str != NULL)
1169     {
1170         missing_settings_key(key);
1171     }
1172     else
1173     {
1174         tr_rpcSetHostWhitelist(s, str);
1175     }
1176 
1177     key = TR_KEY_rpc_authentication_required;
1178 
1179     if (!tr_variantDictFindBool(settings, key, &boolVal))
1180     {
1181         missing_settings_key(key);
1182     }
1183     else
1184     {
1185         tr_rpcSetPasswordEnabled(s, boolVal);
1186     }
1187 
1188     key = TR_KEY_rpc_whitelist;
1189 
1190     if (!tr_variantDictFindStr(settings, key, &str, NULL) && str != NULL)
1191     {
1192         missing_settings_key(key);
1193     }
1194     else
1195     {
1196         tr_rpcSetWhitelist(s, str);
1197     }
1198 
1199     key = TR_KEY_rpc_username;
1200 
1201     if (!tr_variantDictFindStr(settings, key, &str, NULL))
1202     {
1203         missing_settings_key(key);
1204     }
1205     else
1206     {
1207         tr_rpcSetUsername(s, str);
1208     }
1209 
1210     key = TR_KEY_rpc_password;
1211 
1212     if (!tr_variantDictFindStr(settings, key, &str, NULL))
1213     {
1214         missing_settings_key(key);
1215     }
1216     else
1217     {
1218         tr_rpcSetPassword(s, str);
1219     }
1220 
1221     key = TR_KEY_rpc_bind_address;
1222 
1223     if (!tr_variantDictFindStr(settings, key, &str, NULL))
1224     {
1225         missing_settings_key(key);
1226         address = tr_inaddr_any;
1227     }
1228     else if (!tr_address_from_string(&address, str))
1229     {
1230         tr_logAddNamedError(MY_NAME, _("%s is not a valid address"), str);
1231         address = tr_inaddr_any;
1232     }
1233     else if (address.type != TR_AF_INET && address.type != TR_AF_INET6)
1234     {
1235         tr_logAddNamedError(MY_NAME, _("%s is not an IPv4 or IPv6 address. RPC listeners must be IPv4 or IPv6"), str);
1236         address = tr_inaddr_any;
1237     }
1238 
1239     s->bindAddress = address;
1240 
1241     if (s->isEnabled)
1242     {
1243         tr_logAddNamedInfo(MY_NAME, _("Serving RPC and Web requests on %s:%d%s"), tr_rpcGetBindAddress(s), (int)s->port,
1244             s->url);
1245         tr_runInEventThread(session, startServer, s);
1246 
1247         if (s->isWhitelistEnabled)
1248         {
1249             tr_logAddNamedInfo(MY_NAME, "%s", _("Whitelist enabled"));
1250         }
1251 
1252         if (s->isPasswordEnabled)
1253         {
1254             tr_logAddNamedInfo(MY_NAME, "%s", _("Password required"));
1255         }
1256     }
1257 
1258     return s;
1259 }
1260