1 /*
2  * Copyright (c) 2015 DeNA Co., Ltd. Kazuho Oku
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to
6  * deal in the Software without restriction, including without limitation the
7  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8  * sell copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20  * IN THE SOFTWARE.
21  */
22 #include <arpa/inet.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <inttypes.h>
26 #include <netinet/in.h>
27 #include <netinet/tcp.h>
28 #include <pwd.h>
29 #include <stdlib.h>
30 #include <sys/stat.h>
31 #include <sys/un.h>
32 #include "h2o.h"
33 #include "h2o/configurator.h"
34 #include "h2o/serverutil.h"
35 
36 struct fastcgi_configurator_t {
37     h2o_configurator_t super;
38     h2o_fastcgi_config_vars_t *vars;
39     h2o_fastcgi_config_vars_t _vars_stack[H2O_CONFIGURATOR_NUM_LEVELS + 1];
40 };
41 
on_config_timeout_io(h2o_configurator_command_t * cmd,h2o_configurator_context_t * ctx,yoml_t * node)42 static int on_config_timeout_io(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node)
43 {
44     struct fastcgi_configurator_t *self = (void *)cmd->configurator;
45     return h2o_configurator_scanf(cmd, node, "%" SCNu64, &self->vars->io_timeout);
46 }
47 
on_config_timeout_keepalive(h2o_configurator_command_t * cmd,h2o_configurator_context_t * ctx,yoml_t * node)48 static int on_config_timeout_keepalive(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node)
49 {
50     struct fastcgi_configurator_t *self = (void *)cmd->configurator;
51     return h2o_configurator_scanf(cmd, node, "%" SCNu64, &self->vars->keepalive_timeout);
52 }
53 
on_config_document_root(h2o_configurator_command_t * cmd,h2o_configurator_context_t * ctx,yoml_t * node)54 static int on_config_document_root(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node)
55 {
56     struct fastcgi_configurator_t *self = (void *)cmd->configurator;
57 
58     if (node->data.scalar[0] == '\0') {
59         /* unset */
60         self->vars->document_root = h2o_iovec_init(NULL, 0);
61     } else if (node->data.scalar[0] == '/') {
62         /* set */
63         self->vars->document_root = h2o_iovec_init(node->data.scalar, strlen(node->data.scalar));
64     } else {
65         h2o_configurator_errprintf(cmd, node, "value does not start from `/`");
66         return -1;
67     }
68     return 0;
69 }
70 
on_config_send_delegated_uri(h2o_configurator_command_t * cmd,h2o_configurator_context_t * ctx,yoml_t * node)71 static int on_config_send_delegated_uri(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node)
72 {
73     struct fastcgi_configurator_t *self = (void *)cmd->configurator;
74     ssize_t v;
75 
76     if ((v = h2o_configurator_get_one_of(cmd, node, "OFF,ON")) == -1)
77         return -1;
78     self->vars->send_delegated_uri = (int)v;
79     return 0;
80 }
81 
on_config_connect(h2o_configurator_command_t * cmd,h2o_configurator_context_t * ctx,yoml_t * node)82 static int on_config_connect(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node)
83 {
84     struct fastcgi_configurator_t *self = (void *)cmd->configurator;
85     const char *hostname = "127.0.0.1", *servname = NULL, *type = "tcp";
86 
87     /* fetch servname (and hostname) */
88     switch (node->type) {
89     case YOML_TYPE_SCALAR:
90         servname = node->data.scalar;
91         break;
92     case YOML_TYPE_MAPPING: {
93         yoml_t **port_node, **host_node, **type_node;
94         if (h2o_configurator_parse_mapping(cmd, node, "port:s", "host:s,type:s", &port_node, &host_node, &type_node) != 0)
95             return -1;
96         servname = (*port_node)->data.scalar;
97         if (host_node != NULL)
98             hostname = (*host_node)->data.scalar;
99         if (type_node != NULL)
100             type = (*type_node)->data.scalar;
101     } break;
102     default:
103         h2o_configurator_errprintf(cmd, node,
104                                    "value must be a string or a mapping (with keys: `port` and optionally `host` and `type`)");
105         return -1;
106     }
107 
108     h2o_url_t upstream;
109 
110     if (strcmp(type, "unix") == 0) {
111         /* unix socket */
112         struct sockaddr_un sa;
113         if (strlen(servname) >= sizeof(sa.sun_path)) {
114             h2o_configurator_errprintf(cmd, node, "path:%s is too long as a unix socket name", servname);
115             return -1;
116         }
117         h2o_url_init_with_sun_path(&upstream, NULL, &H2O_URL_SCHEME_FASTCGI, h2o_iovec_init(servname, strlen(servname)),
118                                    h2o_iovec_init(H2O_STRLIT("/")));
119     } else if (strcmp(type, "tcp") == 0) {
120         /* tcp socket */
121         uint16_t port;
122         if (sscanf(servname, "%" SCNu16, &port) != 1) {
123             h2o_configurator_errprintf(cmd, node, "invalid port number:%s", servname);
124             return -1;
125         }
126         h2o_url_init_with_hostport(&upstream, NULL, &H2O_URL_SCHEME_FASTCGI, h2o_iovec_init(hostname, strlen(hostname)), port,
127                                    h2o_iovec_init(H2O_STRLIT("/")));
128     } else {
129         h2o_configurator_errprintf(cmd, node, "unknown listen type: %s", type);
130         return -1;
131     }
132 
133     h2o_fastcgi_register(ctx->pathconf, &upstream, self->vars);
134     free(upstream.authority.base);
135 
136     return 0;
137 }
138 
create_spawnproc(h2o_configurator_command_t * cmd,yoml_t * node,const char * dirname,char * const * argv,struct sockaddr_un * sa,struct passwd * pw)139 static int create_spawnproc(h2o_configurator_command_t *cmd, yoml_t *node, const char *dirname, char *const *argv,
140                             struct sockaddr_un *sa, struct passwd *pw)
141 {
142     int ret, listen_fd = -1, pipe_fds[2] = {-1, -1};
143 
144     /* build socket path */
145     sa->sun_family = AF_UNIX;
146     ret = snprintf(sa->sun_path, sizeof(sa->sun_path), "%s/_", dirname);
147     if (ret < 0 || ret >= sizeof(sa->sun_path)) {
148         h2o_configurator_errprintf(cmd, node, "unix socket path too long: %s", dirname);
149         goto Error;
150     }
151 
152     /* create socket */
153     if ((listen_fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
154         h2o_configurator_errprintf(cmd, node, "socket(2) failed: %s", strerror(errno));
155         goto Error;
156     }
157     if (bind(listen_fd, (void *)sa, sizeof(*sa)) != 0) {
158         h2o_configurator_errprintf(cmd, node, "bind(2) failed: %s", strerror(errno));
159         goto Error;
160     }
161     if (listen(listen_fd, H2O_SOMAXCONN) != 0) {
162         h2o_configurator_errprintf(cmd, node, "listen(2) failed: %s", strerror(errno));
163         goto Error;
164     }
165     /* change ownership of socket */
166     if (pw != NULL && chown(sa->sun_path, pw->pw_uid, pw->pw_gid) != 0) {
167         h2o_configurator_errprintf(cmd, node, "chown(2) failed to change ownership of socket:%s:%s", sa->sun_path, strerror(errno));
168         goto Error;
169     }
170 
171     /* create pipe which is used to notify the termination of the server */
172     if (pipe(pipe_fds) != 0) {
173         h2o_configurator_errprintf(cmd, node, "pipe(2) failed: %s", strerror(errno));
174         pipe_fds[0] = -1;
175         pipe_fds[1] = -1;
176         goto Error;
177     }
178     if (fcntl(pipe_fds[1], F_SETFD, FD_CLOEXEC) < 0)
179         goto Error;
180 
181     /* spawn */
182     int mapped_fds[] = {listen_fd, 0,   /* listen_fd to 0 */
183                         pipe_fds[0], 5, /* pipe_fds[0] to 5 */
184                         -1};
185     pid_t pid = h2o_spawnp(argv[0], argv, mapped_fds, 0);
186     if (pid == -1) {
187         h2o_error_printf("[lib/handler/fastcgi.c] failed to launch helper program %s:%s\n", argv[0], strerror(errno));
188         goto Error;
189     }
190 
191     close(listen_fd);
192     listen_fd = -1;
193     close(pipe_fds[0]);
194     pipe_fds[0] = -1;
195 
196     return pipe_fds[1];
197 
198 Error:
199     if (pipe_fds[0] != -1)
200         close(pipe_fds[0]);
201     if (pipe_fds[1])
202         close(pipe_fds[1]);
203     if (listen_fd != -1)
204         close(listen_fd);
205     unlink(sa->sun_path);
206     return -1;
207 }
208 
spawnproc_on_dispose(h2o_fastcgi_handler_t * handler,void * data)209 static void spawnproc_on_dispose(h2o_fastcgi_handler_t *handler, void *data)
210 {
211     int pipe_fd = (int)((char *)data - (char *)NULL);
212     close(pipe_fd);
213 }
214 
on_config_spawn(h2o_configurator_command_t * cmd,h2o_configurator_context_t * ctx,yoml_t * node)215 static int on_config_spawn(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node)
216 {
217     struct fastcgi_configurator_t *self = (void *)cmd->configurator;
218     char *spawn_user = ctx->globalconf->user, *spawn_cmd;
219     char *kill_on_close_cmd_path = NULL, *setuidgid_cmd_path = NULL;
220     char dirname[] = "/tmp/h2o.fcgisock.XXXXXX";
221     char *argv[10];
222     int spawner_fd;
223     struct sockaddr_un sa;
224     h2o_fastcgi_config_vars_t config_vars;
225     int ret = -1;
226     struct passwd h2o_user_pwbuf, *h2o_user_pw;
227     char h2o_user_buf[65536];
228 
229     memset(&sa, 0, sizeof(sa));
230 
231     switch (node->type) {
232     case YOML_TYPE_SCALAR:
233         spawn_cmd = node->data.scalar;
234         break;
235     case YOML_TYPE_MAPPING: {
236         yoml_t **command_node, **user_node;
237         if (h2o_configurator_parse_mapping(cmd, node, "command:s", "user:s", &command_node, &user_node) != 0)
238             return -1;
239         spawn_cmd = (*command_node)->data.scalar;
240         if (user_node != NULL)
241             spawn_user = (*user_node)->data.scalar;
242     } break;
243     default:
244         h2o_configurator_errprintf(cmd, node, "argument must be scalar or mapping");
245         return -1;
246     }
247 
248     /* obtain uid & gid of the client that connects to the FastCGI daemon (i.e. H2O after dropping privileges) */
249     if (ctx->globalconf->user != NULL) {
250         /* change ownership of temporary directory */
251         if (getpwnam_r(ctx->globalconf->user, &h2o_user_pwbuf, h2o_user_buf, sizeof(h2o_user_buf), &h2o_user_pw) != 0 ||
252             h2o_user_pw == NULL) {
253             h2o_configurator_errprintf(cmd, node, "getpwnam_r(3) failed to obtain uid of user:%s", ctx->globalconf->user);
254             goto Exit;
255         }
256     } else {
257         h2o_user_pw = NULL;
258     }
259 
260     { /* build args */
261         size_t i = 0;
262         argv[i++] = kill_on_close_cmd_path = h2o_configurator_get_cmd_path("share/h2o/kill-on-close");
263         argv[i++] = "--rm";
264         argv[i++] = dirname;
265         argv[i++] = "--";
266         if (spawn_user != NULL) {
267             argv[i++] = setuidgid_cmd_path = h2o_configurator_get_cmd_path("share/h2o/setuidgid");
268             argv[i++] = spawn_user;
269         }
270         argv[i++] = "/bin/sh";
271         argv[i++] = "-c";
272         argv[i++] = spawn_cmd;
273         argv[i++] = NULL;
274         assert(i <= sizeof(argv) / sizeof(argv[0]));
275     }
276 
277     if (ctx->dry_run) {
278         dirname[0] = '\0';
279         spawner_fd = -1;
280         sa.sun_family = AF_UNIX;
281         strcpy(sa.sun_path, "/dry-run.nonexistent");
282     } else {
283         /* create temporary directory */
284         if (mkdtemp(dirname) == NULL) {
285             h2o_configurator_errprintf(cmd, node, "mkdtemp(3) failed to create temporary directory:%s:%s", dirname,
286                                        strerror(errno));
287             dirname[0] = '\0';
288             goto Exit;
289         }
290         /* change ownership of temporary directory */
291         if (h2o_user_pw != NULL && chown(dirname, h2o_user_pw->pw_uid, h2o_user_pw->pw_gid) != 0) {
292             h2o_configurator_errprintf(cmd, node, "chown(2) failed to change ownership of temporary directory:%s:%s", dirname,
293                                        strerror(errno));
294             goto Exit;
295         }
296         /* launch spawnfcgi command */
297         if ((spawner_fd = create_spawnproc(cmd, node, dirname, argv, &sa, h2o_user_pw)) == -1) {
298             goto Exit;
299         }
300     }
301 
302     config_vars = *self->vars;
303     config_vars.callbacks.dispose = spawnproc_on_dispose;
304     config_vars.callbacks.data = (char *)NULL + spawner_fd;
305 
306     h2o_url_t upstream;
307     h2o_url_init_with_sun_path(&upstream, NULL, &H2O_URL_SCHEME_FASTCGI, h2o_iovec_init(sa.sun_path, strlen(sa.sun_path)),
308                                h2o_iovec_init(H2O_STRLIT("/")));
309     h2o_fastcgi_register(ctx->pathconf, &upstream, &config_vars);
310     free(upstream.authority.base);
311 
312     ret = 0;
313 Exit:
314     if (dirname[0] != '\0')
315         unlink(dirname);
316     free(kill_on_close_cmd_path);
317     free(setuidgid_cmd_path);
318     return ret;
319 }
320 
on_config_enter(h2o_configurator_t * _self,h2o_configurator_context_t * ctx,yoml_t * node)321 static int on_config_enter(h2o_configurator_t *_self, h2o_configurator_context_t *ctx, yoml_t *node)
322 {
323     struct fastcgi_configurator_t *self = (void *)_self;
324 
325     memcpy(self->vars + 1, self->vars, sizeof(*self->vars));
326     ++self->vars;
327     return 0;
328 }
329 
on_config_exit(h2o_configurator_t * _self,h2o_configurator_context_t * ctx,yoml_t * node)330 static int on_config_exit(h2o_configurator_t *_self, h2o_configurator_context_t *ctx, yoml_t *node)
331 {
332     struct fastcgi_configurator_t *self = (void *)_self;
333 
334     --self->vars;
335     return 0;
336 }
337 
h2o_fastcgi_register_configurator(h2o_globalconf_t * conf)338 void h2o_fastcgi_register_configurator(h2o_globalconf_t *conf)
339 {
340     struct fastcgi_configurator_t *c = (void *)h2o_configurator_create(conf, sizeof(*c));
341 
342     /* set default vars */
343     c->vars = c->_vars_stack;
344     c->vars->io_timeout = H2O_DEFAULT_FASTCGI_IO_TIMEOUT;
345     c->vars->keepalive_timeout = 0;
346 
347     /* setup handlers */
348     c->super.enter = on_config_enter;
349     c->super.exit = on_config_exit;
350 
351     h2o_configurator_define_command(&c->super, "fastcgi.connect",
352                                     H2O_CONFIGURATOR_FLAG_PATH | H2O_CONFIGURATOR_FLAG_EXTENSION | H2O_CONFIGURATOR_FLAG_DEFERRED,
353                                     on_config_connect);
354     h2o_configurator_define_command(&c->super, "fastcgi.spawn",
355                                     H2O_CONFIGURATOR_FLAG_PATH | H2O_CONFIGURATOR_FLAG_EXTENSION | H2O_CONFIGURATOR_FLAG_DEFERRED,
356                                     on_config_spawn);
357     h2o_configurator_define_command(&c->super, "fastcgi.timeout.io",
358                                     H2O_CONFIGURATOR_FLAG_ALL_LEVELS | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR, on_config_timeout_io);
359     h2o_configurator_define_command(&c->super, "fastcgi.timeout.keepalive",
360                                     H2O_CONFIGURATOR_FLAG_ALL_LEVELS | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR,
361                                     on_config_timeout_keepalive);
362     h2o_configurator_define_command(&c->super, "fastcgi.document_root",
363                                     H2O_CONFIGURATOR_FLAG_ALL_LEVELS | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR,
364                                     on_config_document_root);
365     h2o_configurator_define_command(&c->super, "fastcgi.send-delegated-uri",
366                                     H2O_CONFIGURATOR_FLAG_ALL_LEVELS | H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR,
367                                     on_config_send_delegated_uri);
368 }
369