1 /*
2 * nghttp2 - HTTP/2 C Library
3 *
4 * Copyright (c) 2015 Tatsuhiro Tsujikawa
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 */
25 #include "shrpx_worker_process.h"
26
27 #include <sys/types.h>
28 #ifdef HAVE_UNISTD_H
29 # include <unistd.h>
30 #endif // HAVE_UNISTD_H
31 #include <sys/resource.h>
32 #include <sys/wait.h>
33 #include <grp.h>
34
35 #include <cinttypes>
36 #include <cstdlib>
37
38 #include <openssl/rand.h>
39
40 #include <ev.h>
41
42 #include <ares.h>
43
44 #include "shrpx_config.h"
45 #include "shrpx_connection_handler.h"
46 #include "shrpx_log_config.h"
47 #include "shrpx_worker.h"
48 #include "shrpx_accept_handler.h"
49 #include "shrpx_http2_upstream.h"
50 #include "shrpx_http2_session.h"
51 #include "shrpx_memcached_dispatcher.h"
52 #include "shrpx_memcached_request.h"
53 #include "shrpx_process.h"
54 #include "shrpx_tls.h"
55 #include "shrpx_log.h"
56 #include "util.h"
57 #include "app_helper.h"
58 #include "template.h"
59 #include "xsi_strerror.h"
60
61 using namespace nghttp2;
62
63 namespace shrpx {
64
65 namespace {
drop_privileges(neverbleed_t * nb)66 void drop_privileges(
67 #ifdef HAVE_NEVERBLEED
68 neverbleed_t *nb
69 #endif // HAVE_NEVERBLEED
70 ) {
71 std::array<char, STRERROR_BUFSIZE> errbuf;
72 auto config = get_config();
73
74 if (getuid() == 0 && config->uid != 0) {
75 #ifdef HAVE_NEVERBLEED
76 if (nb) {
77 neverbleed_setuidgid(nb, config->user.c_str(), 1);
78 }
79 #endif // HAVE_NEVERBLEED
80
81 if (initgroups(config->user.c_str(), config->gid) != 0) {
82 auto error = errno;
83 LOG(FATAL) << "Could not change supplementary groups: "
84 << xsi_strerror(error, errbuf.data(), errbuf.size());
85 exit(EXIT_FAILURE);
86 }
87 if (setgid(config->gid) != 0) {
88 auto error = errno;
89 LOG(FATAL) << "Could not change gid: "
90 << xsi_strerror(error, errbuf.data(), errbuf.size());
91 exit(EXIT_FAILURE);
92 }
93 if (setuid(config->uid) != 0) {
94 auto error = errno;
95 LOG(FATAL) << "Could not change uid: "
96 << xsi_strerror(error, errbuf.data(), errbuf.size());
97 exit(EXIT_FAILURE);
98 }
99 if (setuid(0) != -1) {
100 LOG(FATAL) << "Still have root privileges?";
101 exit(EXIT_FAILURE);
102 }
103 }
104 }
105 } // namespace
106
107 namespace {
graceful_shutdown(ConnectionHandler * conn_handler)108 void graceful_shutdown(ConnectionHandler *conn_handler) {
109 if (conn_handler->get_graceful_shutdown()) {
110 return;
111 }
112
113 LOG(NOTICE) << "Graceful shutdown signal received";
114
115 conn_handler->set_graceful_shutdown(true);
116
117 // TODO What happens for the connections not established in the
118 // kernel?
119 conn_handler->accept_pending_connection();
120 conn_handler->delete_acceptor();
121
122 conn_handler->graceful_shutdown_worker();
123
124 auto single_worker = conn_handler->get_single_worker();
125 if (single_worker) {
126 if (single_worker->get_worker_stat()->num_connections == 0) {
127 ev_break(conn_handler->get_loop());
128 }
129
130 return;
131 }
132 }
133 } // namespace
134
135 namespace {
reopen_log(ConnectionHandler * conn_handler)136 void reopen_log(ConnectionHandler *conn_handler) {
137 LOG(NOTICE) << "Reopening log files: worker process (thread main)";
138
139 auto config = get_config();
140 auto &loggingconf = config->logging;
141
142 (void)reopen_log_files(loggingconf);
143 redirect_stderr_to_errorlog(loggingconf);
144
145 conn_handler->worker_reopen_log_files();
146 }
147 } // namespace
148
149 namespace {
ipc_readcb(struct ev_loop * loop,ev_io * w,int revents)150 void ipc_readcb(struct ev_loop *loop, ev_io *w, int revents) {
151 auto conn_handler = static_cast<ConnectionHandler *>(w->data);
152 std::array<uint8_t, 1024> buf;
153 ssize_t nread;
154 while ((nread = read(w->fd, buf.data(), buf.size())) == -1 && errno == EINTR)
155 ;
156 if (nread == -1) {
157 auto error = errno;
158 LOG(ERROR) << "Failed to read data from ipc channel: errno=" << error;
159 return;
160 }
161
162 if (nread == 0) {
163 // IPC socket closed. Perform immediate shutdown.
164 LOG(FATAL) << "IPC socket is closed. Perform immediate shutdown.";
165 nghttp2_Exit(EXIT_FAILURE);
166 }
167
168 for (ssize_t i = 0; i < nread; ++i) {
169 switch (buf[i]) {
170 case SHRPX_IPC_GRACEFUL_SHUTDOWN:
171 graceful_shutdown(conn_handler);
172 break;
173 case SHRPX_IPC_REOPEN_LOG:
174 reopen_log(conn_handler);
175 break;
176 }
177 }
178 }
179 } // namespace
180
181 namespace {
generate_ticket_key(TicketKey & ticket_key)182 int generate_ticket_key(TicketKey &ticket_key) {
183 ticket_key.cipher = get_config()->tls.ticket.cipher;
184 ticket_key.hmac = EVP_sha256();
185 ticket_key.hmac_keylen = EVP_MD_size(ticket_key.hmac);
186
187 assert(static_cast<size_t>(EVP_CIPHER_key_length(ticket_key.cipher)) <=
188 ticket_key.data.enc_key.size());
189 assert(ticket_key.hmac_keylen <= ticket_key.data.hmac_key.size());
190
191 if (LOG_ENABLED(INFO)) {
192 LOG(INFO) << "enc_keylen=" << EVP_CIPHER_key_length(ticket_key.cipher)
193 << ", hmac_keylen=" << ticket_key.hmac_keylen;
194 }
195
196 if (RAND_bytes(reinterpret_cast<unsigned char *>(&ticket_key.data),
197 sizeof(ticket_key.data)) == 0) {
198 return -1;
199 }
200
201 return 0;
202 }
203 } // namespace
204
205 namespace {
renew_ticket_key_cb(struct ev_loop * loop,ev_timer * w,int revents)206 void renew_ticket_key_cb(struct ev_loop *loop, ev_timer *w, int revents) {
207 auto conn_handler = static_cast<ConnectionHandler *>(w->data);
208 const auto &old_ticket_keys = conn_handler->get_ticket_keys();
209
210 auto ticket_keys = std::make_shared<TicketKeys>();
211 LOG(NOTICE) << "Renew new ticket keys";
212
213 // If old_ticket_keys is not empty, it should contain at least 2
214 // keys: one for encryption, and last one for the next encryption
215 // key but decryption only. The keys in between are old keys and
216 // decryption only. The next key is provided to ensure to mitigate
217 // possible problem when one worker encrypt new key, but one worker,
218 // which did not take the that key yet, and cannot decrypt it.
219 //
220 // We keep keys for get_config()->tls_session_timeout seconds. The
221 // default is 12 hours. Thus the maximum ticket vector size is 12.
222 if (old_ticket_keys) {
223 auto &old_keys = old_ticket_keys->keys;
224 auto &new_keys = ticket_keys->keys;
225
226 assert(!old_keys.empty());
227
228 auto max_tickets =
229 static_cast<size_t>(std::chrono::duration_cast<std::chrono::hours>(
230 get_config()->tls.session_timeout)
231 .count());
232
233 new_keys.resize(std::min(max_tickets, old_keys.size() + 1));
234 std::copy_n(std::begin(old_keys), new_keys.size() - 1,
235 std::begin(new_keys) + 1);
236 } else {
237 ticket_keys->keys.resize(1);
238 }
239
240 auto &new_key = ticket_keys->keys[0];
241
242 if (generate_ticket_key(new_key) != 0) {
243 if (LOG_ENABLED(INFO)) {
244 LOG(INFO) << "failed to generate ticket key";
245 }
246 conn_handler->set_ticket_keys(nullptr);
247 conn_handler->set_ticket_keys_to_worker(nullptr);
248 return;
249 }
250
251 if (LOG_ENABLED(INFO)) {
252 LOG(INFO) << "ticket keys generation done";
253 assert(ticket_keys->keys.size() >= 1);
254 LOG(INFO) << 0 << " enc+dec: "
255 << util::format_hex(ticket_keys->keys[0].data.name);
256 for (size_t i = 1; i < ticket_keys->keys.size(); ++i) {
257 auto &key = ticket_keys->keys[i];
258 LOG(INFO) << i << " dec: " << util::format_hex(key.data.name);
259 }
260 }
261
262 conn_handler->set_ticket_keys(ticket_keys);
263 conn_handler->set_ticket_keys_to_worker(ticket_keys);
264 }
265 } // namespace
266
267 namespace {
memcached_get_ticket_key_cb(struct ev_loop * loop,ev_timer * w,int revents)268 void memcached_get_ticket_key_cb(struct ev_loop *loop, ev_timer *w,
269 int revents) {
270 auto conn_handler = static_cast<ConnectionHandler *>(w->data);
271 auto dispatcher = conn_handler->get_tls_ticket_key_memcached_dispatcher();
272
273 auto req = std::make_unique<MemcachedRequest>();
274 req->key = "nghttpx:tls-ticket-key";
275 req->op = MemcachedOp::GET;
276 req->cb = [conn_handler, w](MemcachedRequest *req, MemcachedResult res) {
277 switch (res.status_code) {
278 case MemcachedStatusCode::NO_ERROR:
279 break;
280 case MemcachedStatusCode::EXT_NETWORK_ERROR:
281 conn_handler->on_tls_ticket_key_network_error(w);
282 return;
283 default:
284 conn_handler->on_tls_ticket_key_not_found(w);
285 return;
286 }
287
288 // |version (4bytes)|len (2bytes)|key (variable length)|...
289 // (len, key) pairs are repeated as necessary.
290
291 auto &value = res.value;
292 if (value.size() < 4) {
293 LOG(WARN) << "Memcached: tls ticket key value is too small: got "
294 << value.size();
295 conn_handler->on_tls_ticket_key_not_found(w);
296 return;
297 }
298 auto p = value.data();
299 auto version = util::get_uint32(p);
300 // Currently supported version is 1.
301 if (version != 1) {
302 LOG(WARN) << "Memcached: tls ticket key version: want 1, got " << version;
303 conn_handler->on_tls_ticket_key_not_found(w);
304 return;
305 }
306
307 auto end = p + value.size();
308 p += 4;
309
310 auto &ticketconf = get_config()->tls.ticket;
311
312 size_t expectedlen;
313 size_t enc_keylen;
314 size_t hmac_keylen;
315 if (ticketconf.cipher == EVP_aes_128_cbc()) {
316 expectedlen = 48;
317 enc_keylen = 16;
318 hmac_keylen = 16;
319 } else if (ticketconf.cipher == EVP_aes_256_cbc()) {
320 expectedlen = 80;
321 enc_keylen = 32;
322 hmac_keylen = 32;
323 } else {
324 return;
325 }
326
327 auto ticket_keys = std::make_shared<TicketKeys>();
328
329 for (; p != end;) {
330 if (end - p < 2) {
331 LOG(WARN) << "Memcached: tls ticket key data is too small";
332 conn_handler->on_tls_ticket_key_not_found(w);
333 return;
334 }
335 auto len = util::get_uint16(p);
336 p += 2;
337 if (len != expectedlen) {
338 LOG(WARN) << "Memcached: wrong tls ticket key size: want "
339 << expectedlen << ", got " << len;
340 conn_handler->on_tls_ticket_key_not_found(w);
341 return;
342 }
343 if (p + len > end) {
344 LOG(WARN) << "Memcached: too short tls ticket key payload: want " << len
345 << ", got " << (end - p);
346 conn_handler->on_tls_ticket_key_not_found(w);
347 return;
348 }
349 auto key = TicketKey();
350 key.cipher = ticketconf.cipher;
351 key.hmac = EVP_sha256();
352 key.hmac_keylen = hmac_keylen;
353
354 std::copy_n(p, key.data.name.size(), std::begin(key.data.name));
355 p += key.data.name.size();
356
357 std::copy_n(p, enc_keylen, std::begin(key.data.enc_key));
358 p += enc_keylen;
359
360 std::copy_n(p, hmac_keylen, std::begin(key.data.hmac_key));
361 p += hmac_keylen;
362
363 ticket_keys->keys.push_back(std::move(key));
364 }
365
366 conn_handler->on_tls_ticket_key_get_success(ticket_keys, w);
367 };
368
369 if (LOG_ENABLED(INFO)) {
370 LOG(INFO) << "Memcached: tls ticket key get request sent";
371 }
372
373 dispatcher->add_request(std::move(req));
374 }
375
376 } // namespace
377
378 #ifdef HAVE_NEVERBLEED
379 namespace {
nb_child_cb(struct ev_loop * loop,ev_child * w,int revents)380 void nb_child_cb(struct ev_loop *loop, ev_child *w, int revents) {
381 log_chld(w->rpid, w->rstatus, "neverbleed process");
382
383 ev_child_stop(loop, w);
384
385 LOG(FATAL) << "neverbleed process exitted; aborting now";
386
387 nghttp2_Exit(EXIT_FAILURE);
388 }
389 } // namespace
390 #endif // HAVE_NEVERBLEED
391
worker_process_event_loop(WorkerProcessConfig * wpconf)392 int worker_process_event_loop(WorkerProcessConfig *wpconf) {
393 int rv;
394 std::array<char, STRERROR_BUFSIZE> errbuf;
395 (void)errbuf;
396
397 auto config = get_config();
398
399 if (reopen_log_files(config->logging) != 0) {
400 LOG(FATAL) << "Failed to open log file";
401 return -1;
402 }
403
404 rv = ares_library_init(ARES_LIB_INIT_ALL);
405 if (rv != 0) {
406 LOG(FATAL) << "ares_library_init failed: " << ares_strerror(rv);
407 return -1;
408 }
409
410 auto loop = EV_DEFAULT;
411
412 auto gen = util::make_mt19937();
413
414 auto conn_handler = std::make_unique<ConnectionHandler>(loop, gen);
415
416 for (auto &addr : config->conn.listener.addrs) {
417 conn_handler->add_acceptor(
418 std::make_unique<AcceptHandler>(&addr, conn_handler.get()));
419 }
420
421 #ifdef HAVE_NEVERBLEED
422 std::array<char, NEVERBLEED_ERRBUF_SIZE> nb_errbuf;
423 auto nb = std::make_unique<neverbleed_t>();
424 if (neverbleed_init(nb.get(), nb_errbuf.data()) != 0) {
425 LOG(FATAL) << "neverbleed_init failed: " << nb_errbuf.data();
426 return -1;
427 }
428
429 LOG(NOTICE) << "neverbleed process [" << nb->daemon_pid << "] spawned";
430
431 conn_handler->set_neverbleed(nb.get());
432
433 ev_child nb_childev;
434
435 ev_child_init(&nb_childev, nb_child_cb, nb->daemon_pid, 0);
436 nb_childev.data = nullptr;
437 ev_child_start(loop, &nb_childev);
438 #endif // HAVE_NEVERBLEED
439
440 MemchunkPool mcpool;
441
442 ev_timer renew_ticket_key_timer;
443 if (tls::upstream_tls_enabled(config->conn)) {
444 auto &ticketconf = config->tls.ticket;
445 auto &memcachedconf = ticketconf.memcached;
446
447 if (!memcachedconf.host.empty()) {
448 SSL_CTX *ssl_ctx = nullptr;
449
450 if (memcachedconf.tls) {
451 ssl_ctx = conn_handler->create_tls_ticket_key_memcached_ssl_ctx();
452 }
453
454 conn_handler->set_tls_ticket_key_memcached_dispatcher(
455 std::make_unique<MemcachedDispatcher>(
456 &ticketconf.memcached.addr, loop, ssl_ctx,
457 StringRef{memcachedconf.host}, &mcpool, gen));
458
459 ev_timer_init(&renew_ticket_key_timer, memcached_get_ticket_key_cb, 0.,
460 0.);
461 renew_ticket_key_timer.data = conn_handler.get();
462 // Get first ticket keys.
463 memcached_get_ticket_key_cb(loop, &renew_ticket_key_timer, 0);
464 } else {
465 bool auto_tls_ticket_key = true;
466 if (!ticketconf.files.empty()) {
467 if (!ticketconf.cipher_given) {
468 LOG(WARN)
469 << "It is strongly recommended to specify "
470 "--tls-ticket-key-cipher=aes-128-cbc (or "
471 "tls-ticket-key-cipher=aes-128-cbc in configuration file) "
472 "when --tls-ticket-key-file is used for the smooth "
473 "transition when the default value of --tls-ticket-key-cipher "
474 "becomes aes-256-cbc";
475 }
476 auto ticket_keys = read_tls_ticket_key_file(
477 ticketconf.files, ticketconf.cipher, EVP_sha256());
478 if (!ticket_keys) {
479 LOG(WARN) << "Use internal session ticket key generator";
480 } else {
481 conn_handler->set_ticket_keys(std::move(ticket_keys));
482 auto_tls_ticket_key = false;
483 }
484 }
485 if (auto_tls_ticket_key) {
486 // Generate new ticket key every 1hr.
487 ev_timer_init(&renew_ticket_key_timer, renew_ticket_key_cb, 0., 1_h);
488 renew_ticket_key_timer.data = conn_handler.get();
489 ev_timer_again(loop, &renew_ticket_key_timer);
490
491 // Generate first session ticket key before running workers.
492 renew_ticket_key_cb(loop, &renew_ticket_key_timer, 0);
493 }
494 }
495 }
496
497 if (config->single_thread) {
498 rv = conn_handler->create_single_worker();
499 if (rv != 0) {
500 return -1;
501 }
502 } else {
503 #ifndef NOTHREADS
504 sigset_t set;
505 sigemptyset(&set);
506 sigaddset(&set, SIGCHLD);
507
508 rv = pthread_sigmask(SIG_BLOCK, &set, nullptr);
509 if (rv != 0) {
510 LOG(ERROR) << "Blocking SIGCHLD failed: "
511 << xsi_strerror(rv, errbuf.data(), errbuf.size());
512 return -1;
513 }
514 #endif // !NOTHREADS
515
516 rv = conn_handler->create_worker_thread(config->num_worker);
517 if (rv != 0) {
518 return -1;
519 }
520
521 #ifndef NOTHREADS
522 rv = pthread_sigmask(SIG_UNBLOCK, &set, nullptr);
523 if (rv != 0) {
524 LOG(ERROR) << "Unblocking SIGCHLD failed: "
525 << xsi_strerror(rv, errbuf.data(), errbuf.size());
526 return -1;
527 }
528 #endif // !NOTHREADS
529 }
530
531 drop_privileges(
532 #ifdef HAVE_NEVERBLEED
533 nb.get()
534 #endif // HAVE_NEVERBLEED
535 );
536
537 ev_io ipcev;
538 ev_io_init(&ipcev, ipc_readcb, wpconf->ipc_fd, EV_READ);
539 ipcev.data = conn_handler.get();
540 ev_io_start(loop, &ipcev);
541
542 if (tls::upstream_tls_enabled(config->conn) && !config->tls.ocsp.disabled) {
543 if (config->tls.ocsp.startup) {
544 conn_handler->set_enable_acceptor_on_ocsp_completion(true);
545 conn_handler->disable_acceptor();
546 }
547
548 conn_handler->proceed_next_cert_ocsp();
549 }
550
551 if (LOG_ENABLED(INFO)) {
552 LOG(INFO) << "Entering event loop";
553 }
554
555 ev_run(loop, 0);
556
557 conn_handler->cancel_ocsp_update();
558
559 // Destroy SSL_CTX held in conn_handler before killing neverbleed
560 // daemon. Otherwise priv_rsa_finish yields "write error" and
561 // worker process aborts.
562 conn_handler.reset();
563
564 #ifdef HAVE_NEVERBLEED
565 assert(nb->daemon_pid > 0);
566
567 rv = kill(nb->daemon_pid, SIGTERM);
568 if (rv != 0) {
569 auto error = errno;
570 LOG(ERROR) << "Could not send signal to neverbleed daemon: errno=" << error;
571 }
572
573 while ((rv = waitpid(nb->daemon_pid, nullptr, 0)) == -1 && errno == EINTR)
574 ;
575 if (rv == -1) {
576 auto error = errno;
577 LOG(ERROR) << "Error occurred while we were waiting for the completion "
578 "of neverbleed process: errno="
579 << error;
580 }
581 #endif // HAVE_NEVERBLEED
582
583 ares_library_cleanup();
584
585 return 0;
586 }
587
588 } // namespace shrpx
589