Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * ProFTPD - FTP server daemon |
3 | | * Copyright (c) 1997, 1998 Public Flood Software |
4 | | * Copyright (c) 1999, 2000 MacGyver aka Habeeb J. Dihu <macgyver@tos.net> |
5 | | * Copyright (c) 2001-2023 The ProFTPD Project team |
6 | | * |
7 | | * This program is free software; you can redistribute it and/or modify |
8 | | * it under the terms of the GNU General Public License as published by |
9 | | * the Free Software Foundation; either version 2 of the License, or |
10 | | * (at your option) any later version. |
11 | | * |
12 | | * This program is distributed in the hope that it will be useful, |
13 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | | * GNU General Public License for more details. |
16 | | * |
17 | | * You should have received a copy of the GNU General Public License |
18 | | * along with this program; if not, write to the Free Software |
19 | | * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. |
20 | | * |
21 | | * As a special exemption, Public Flood Software/MacGyver aka Habeeb J. Dihu |
22 | | * and other respective copyright holders give permission to link this program |
23 | | * with OpenSSL, and distribute the resulting executable, without including |
24 | | * the source code for OpenSSL in the source distribution. |
25 | | */ |
26 | | |
27 | | /* House initialization and main program loop */ |
28 | | |
29 | | #include "conf.h" |
30 | | |
31 | | #ifdef HAVE_GETOPT_H |
32 | | # include <getopt.h> |
33 | | #endif |
34 | | |
35 | | #ifdef HAVE_LIBUTIL_H |
36 | | # include <libutil.h> |
37 | | #endif /* HAVE_LIBUTIL_H */ |
38 | | |
39 | | #ifdef HAVE_UNAME |
40 | | # include <sys/utsname.h> |
41 | | #endif |
42 | | |
43 | | #include "privs.h" |
44 | | |
45 | | #ifdef PR_USE_OPENSSL |
46 | | # include <openssl/opensslv.h> |
47 | | #endif /* PR_USE_OPENSSL */ |
48 | | |
49 | | int (*cmd_auth_chk)(cmd_rec *); |
50 | | void (*cmd_handler)(server_rec *, conn_t *); |
51 | | |
52 | | /* From modules/module_glue.c */ |
53 | | extern module *static_modules[]; |
54 | | |
55 | | extern int have_dead_child; |
56 | | extern xaset_t *server_list; |
57 | | |
58 | | unsigned long max_connects = 0UL; |
59 | | unsigned int max_connect_interval = 1; |
60 | | |
61 | | session_t session; |
62 | | |
63 | | /* Is this process the master standalone daemon process? */ |
64 | | unsigned char is_master = TRUE; |
65 | | |
66 | | pid_t mpid = 0; /* Master pid */ |
67 | | |
68 | | uid_t daemon_uid; |
69 | | gid_t daemon_gid; |
70 | | array_header *daemon_gids; |
71 | | |
72 | | static time_t shut = 0, deny = 0, disc = 0; |
73 | | static char shutmsg[81] = {'\0'}; |
74 | | |
75 | | /* The default command buffer size SHOULD be large enough to handle the |
76 | | * maximum path length, plus 4 bytes for the FTP command, plus 1 for the |
77 | | * whitespace separating command from path, and 2 for the terminating CRLF. |
78 | | */ |
79 | 0 | #define PR_DEFAULT_CMD_BUFSZ (PR_TUNABLE_PATH_MAX + 7) |
80 | | |
81 | | /* From response.c */ |
82 | | extern pr_response_t *resp_list, *resp_err_list; |
83 | | |
84 | | int nodaemon = 0; |
85 | | |
86 | | static int no_forking = FALSE; |
87 | | static int quiet = 0; |
88 | | static int shutting_down = FALSE; |
89 | | static int syntax_check = 0; |
90 | | |
91 | | /* Command handling */ |
92 | | static void cmd_loop(server_rec *s, conn_t *conn); |
93 | | |
94 | | static cmd_rec *make_ftp_cmd(pool *p, char *buf, size_t buflen, int flags); |
95 | | |
96 | | static const char *config_filename = PR_CONFIG_FILE_PATH; |
97 | | |
98 | | /* Add child semaphore fds into the rfd for selecting */ |
99 | 0 | static int semaphore_fds(fd_set *rfd, int maxfd) { |
100 | 0 | if (child_count()) { |
101 | 0 | pr_child_t *ch; |
102 | |
|
103 | 0 | for (ch = child_get(NULL); ch; ch = child_get(ch)) { |
104 | 0 | pr_signals_handle_without_delay(); |
105 | |
|
106 | 0 | if (ch->ch_pipefd != -1) { |
107 | 0 | FD_SET(ch->ch_pipefd, rfd); |
108 | 0 | if (ch->ch_pipefd > maxfd) { |
109 | 0 | maxfd = ch->ch_pipefd; |
110 | 0 | } |
111 | 0 | } |
112 | 0 | } |
113 | 0 | } |
114 | |
|
115 | 0 | return maxfd; |
116 | 0 | } |
117 | | |
118 | 0 | void set_auth_check(int (*chk)(cmd_rec*)) { |
119 | 0 | cmd_auth_chk = chk; |
120 | 0 | } |
121 | | |
122 | 0 | void pr_cmd_set_handler(void (*handler)(server_rec *, conn_t *)) { |
123 | 0 | if (handler == NULL) { |
124 | 0 | cmd_handler = cmd_loop; |
125 | |
|
126 | 0 | } else { |
127 | 0 | cmd_handler = handler; |
128 | 0 | } |
129 | 0 | } |
130 | | |
131 | 0 | void session_exit(int pri, void *lv, int exitval, void *dummy) { |
132 | 0 | char *msg = (char *) lv; |
133 | |
|
134 | 0 | pr_log_pri(pri, "%s", msg); |
135 | |
|
136 | 0 | if (ServerType == SERVER_STANDALONE && |
137 | 0 | is_master) { |
138 | 0 | pr_log_pri(PR_LOG_NOTICE, "ProFTPD " PROFTPD_VERSION_TEXT |
139 | 0 | " standalone mode SHUTDOWN"); |
140 | |
|
141 | 0 | PRIVS_ROOT |
142 | 0 | pr_delete_scoreboard(); |
143 | 0 | if (nodaemon == FALSE) { |
144 | 0 | pr_pidfile_remove(); |
145 | 0 | } |
146 | 0 | PRIVS_RELINQUISH |
147 | 0 | } |
148 | |
|
149 | 0 | pr_session_end(0); |
150 | 0 | } |
151 | | |
152 | 0 | void shutdown_end_session(void *d1, void *d2, void *d3, void *d4) { |
153 | 0 | pool *p = permanent_pool; |
154 | |
|
155 | 0 | if (check_shutmsg(p, PR_SHUTMSG_PATH, &shut, &deny, &disc, shutmsg, |
156 | 0 | sizeof(shutmsg)) == 1) { |
157 | 0 | const char *user; |
158 | 0 | time_t now; |
159 | 0 | const char *msg, *serveraddress; |
160 | 0 | config_rec *c = NULL; |
161 | 0 | unsigned char *authenticated = get_param_ptr(main_server->conf, |
162 | 0 | "authenticated", FALSE); |
163 | |
|
164 | 0 | serveraddress = (session.c && session.c->local_addr) ? |
165 | 0 | pr_netaddr_get_ipstr(session.c->local_addr) : |
166 | 0 | main_server->ServerAddress; |
167 | |
|
168 | 0 | c = find_config(main_server->conf, CONF_PARAM, "MasqueradeAddress", FALSE); |
169 | 0 | if (c != NULL) { |
170 | 0 | pr_netaddr_t *masq_addr = NULL; |
171 | |
|
172 | 0 | if (c->argv[0] != NULL) { |
173 | 0 | masq_addr = c->argv[0]; |
174 | 0 | } |
175 | |
|
176 | 0 | if (masq_addr != NULL) { |
177 | 0 | serveraddress = pr_netaddr_get_ipstr(masq_addr); |
178 | 0 | } |
179 | 0 | } |
180 | |
|
181 | 0 | time(&now); |
182 | 0 | if (authenticated && *authenticated == TRUE) { |
183 | 0 | user = pr_table_get(session.notes, "mod_auth.orig-user", NULL); |
184 | |
|
185 | 0 | } else { |
186 | 0 | user = "NONE"; |
187 | 0 | } |
188 | |
|
189 | 0 | msg = sreplace(p, shutmsg, |
190 | 0 | "%s", pstrdup(p, pr_strtime3(p, shut, FALSE)), |
191 | 0 | "%r", pstrdup(p, pr_strtime3(p, deny, FALSE)), |
192 | 0 | "%d", pstrdup(p, pr_strtime3(p, disc, FALSE)), |
193 | 0 | "%C", (session.cwd[0] ? session.cwd : "(none)"), |
194 | 0 | "%L", serveraddress, |
195 | 0 | "%R", (session.c && session.c->remote_name ? |
196 | 0 | session.c->remote_name : "(unknown)"), |
197 | 0 | "%T", pstrdup(p, pr_strtime3(p, now, FALSE)), |
198 | 0 | "%U", user, |
199 | 0 | "%V", main_server->ServerName, |
200 | 0 | NULL ); |
201 | |
|
202 | 0 | pr_response_send_async(R_421, _("FTP server shutting down - %s"), msg); |
203 | |
|
204 | 0 | pr_log_pri(PR_LOG_NOTICE, "%s", msg); |
205 | 0 | pr_session_disconnect(NULL, PR_SESS_DISCONNECT_SERVER_SHUTDOWN, NULL); |
206 | 0 | } |
207 | |
|
208 | 0 | if (signal(SIGUSR1, pr_signals_handle_disconnect) == SIG_ERR) { |
209 | 0 | pr_log_pri(PR_LOG_NOTICE, |
210 | 0 | "unable to install SIGUSR1 (signal %d) handler: %s", SIGUSR1, |
211 | 0 | strerror(errno)); |
212 | 0 | } |
213 | 0 | } |
214 | | |
215 | 0 | static int get_command_class(const char *name) { |
216 | 0 | int idx = -1; |
217 | 0 | unsigned int hash = 0; |
218 | 0 | cmdtable *c; |
219 | |
|
220 | 0 | c = pr_stash_get_symbol2(PR_SYM_CMD, name, NULL, &idx, &hash); |
221 | 0 | while (c && c->cmd_type != CMD) { |
222 | 0 | pr_signals_handle(); |
223 | 0 | c = pr_stash_get_symbol2(PR_SYM_CMD, name, c, &idx, &hash); |
224 | 0 | } |
225 | | |
226 | | /* By default, every command has a class of CL_ALL. This insures that |
227 | | * any configured ExtendedLogs that default to "all" will log the command. |
228 | | */ |
229 | 0 | return (c ? c->cmd_class : CL_ALL); |
230 | 0 | } |
231 | | |
232 | 0 | static int _dispatch(cmd_rec *cmd, int cmd_type, int validate, char *match) { |
233 | 0 | const char *cmdargstr = NULL; |
234 | 0 | cmdtable *c; |
235 | 0 | modret_t *mr; |
236 | 0 | int success = 0, xerrno = 0; |
237 | 0 | int send_error = 0; |
238 | 0 | static int match_index_cache = -1; |
239 | 0 | static unsigned int match_hash_cache = 0; |
240 | 0 | static char *last_match = NULL; |
241 | 0 | int *index_cache = NULL; |
242 | 0 | unsigned int *hash_cache = NULL; |
243 | |
|
244 | 0 | send_error = (cmd_type == PRE_CMD || cmd_type == CMD || |
245 | 0 | cmd_type == POST_CMD_ERR); |
246 | |
|
247 | 0 | if (!match) { |
248 | 0 | match = cmd->argv[0]; |
249 | 0 | index_cache = &cmd->stash_index; |
250 | 0 | hash_cache = &cmd->stash_hash; |
251 | |
|
252 | 0 | } else { |
253 | 0 | if (last_match != match) { |
254 | 0 | match_index_cache = -1; |
255 | 0 | last_match = match; |
256 | 0 | } |
257 | |
|
258 | 0 | index_cache = &match_index_cache; |
259 | 0 | hash_cache = &match_hash_cache; |
260 | 0 | } |
261 | |
|
262 | 0 | c = pr_stash_get_symbol2(PR_SYM_CMD, match, NULL, index_cache, hash_cache); |
263 | |
|
264 | 0 | while (c && !success) { |
265 | 0 | size_t cmdargstrlen = 0; |
266 | |
|
267 | 0 | pr_signals_handle(); |
268 | |
|
269 | 0 | session.curr_cmd = cmd->argv[0]; |
270 | 0 | session.curr_cmd_id = cmd->cmd_id; |
271 | 0 | session.curr_cmd_rec = cmd; |
272 | 0 | session.curr_phase = cmd_type; |
273 | |
|
274 | 0 | if (c->cmd_type == cmd_type) { |
275 | 0 | if (c->group) { |
276 | 0 | cmd->group = pstrdup(cmd->pool, c->group); |
277 | 0 | } |
278 | |
|
279 | 0 | if (c->requires_auth && |
280 | 0 | cmd_auth_chk && |
281 | 0 | !cmd_auth_chk(cmd)) { |
282 | 0 | pr_trace_msg("command", 8, |
283 | 0 | "command '%s' failed 'requires_auth' check for mod_%s.c", |
284 | 0 | (char *) cmd->argv[0], c->m->name); |
285 | 0 | errno = EACCES; |
286 | 0 | return -1; |
287 | 0 | } |
288 | | |
289 | 0 | if (cmd->tmp_pool == NULL) { |
290 | 0 | cmd->tmp_pool = make_sub_pool(cmd->pool); |
291 | 0 | pr_pool_tag(cmd->tmp_pool, "cmd_rec tmp pool"); |
292 | 0 | } |
293 | |
|
294 | 0 | cmdargstr = pr_cmd_get_displayable_str(cmd, &cmdargstrlen); |
295 | |
|
296 | 0 | if (cmd_type == CMD) { |
297 | | |
298 | | /* The client has successfully authenticated... */ |
299 | 0 | if (session.user) { |
300 | 0 | char *args = NULL; |
301 | | |
302 | | /* Be defensive, and check whether cmdargstrlen has a value. |
303 | | * If it's zero, assume we need to use strchr(3), rather than |
304 | | * memchr(2); see Bug#3714. |
305 | | */ |
306 | 0 | if (cmdargstrlen > 0) { |
307 | 0 | args = memchr(cmdargstr, ' ', cmdargstrlen); |
308 | |
|
309 | 0 | } else { |
310 | 0 | args = strchr(cmdargstr, ' '); |
311 | 0 | } |
312 | |
|
313 | 0 | pr_scoreboard_entry_update(session.pid, |
314 | 0 | PR_SCORE_CMD, "%s", cmd->argv[0], NULL, NULL); |
315 | 0 | pr_scoreboard_entry_update(session.pid, |
316 | 0 | PR_SCORE_CMD_ARG, "%s", args ? (args + 1) : "", NULL, NULL); |
317 | |
|
318 | 0 | pr_proctitle_set("%s - %s: %s", session.user, session.proc_prefix, |
319 | 0 | cmdargstr); |
320 | | |
321 | | /* ...else the client has not yet authenticated */ |
322 | 0 | } else { |
323 | 0 | pr_proctitle_set("%s:%d: %s", session.c->remote_addr ? |
324 | 0 | pr_netaddr_get_ipstr(session.c->remote_addr) : "?", |
325 | 0 | session.c->remote_port ? session.c->remote_port : 0, cmdargstr); |
326 | 0 | } |
327 | 0 | } |
328 | | |
329 | | /* Skip logging the internal CONNECT/DISCONNECT commands. */ |
330 | 0 | if (!(cmd->cmd_class & CL_CONNECT) && |
331 | 0 | !(cmd->cmd_class & CL_DISCONNECT)) { |
332 | |
|
333 | 0 | pr_log_debug(DEBUG4, "dispatching %s command '%s' to mod_%s", |
334 | 0 | (cmd_type == PRE_CMD ? "PRE_CMD" : |
335 | 0 | cmd_type == CMD ? "CMD" : |
336 | 0 | cmd_type == POST_CMD ? "POST_CMD" : |
337 | 0 | cmd_type == POST_CMD_ERR ? "POST_CMD_ERR" : |
338 | 0 | cmd_type == LOG_CMD ? "LOG_CMD" : |
339 | 0 | cmd_type == LOG_CMD_ERR ? "LOG_CMD_ERR" : |
340 | 0 | "(unknown)"), |
341 | 0 | cmdargstr, c->m->name); |
342 | |
|
343 | 0 | pr_trace_msg("command", 7, "dispatching %s command '%s' to mod_%s.c", |
344 | 0 | (cmd_type == PRE_CMD ? "PRE_CMD" : |
345 | 0 | cmd_type == CMD ? "CMD" : |
346 | 0 | cmd_type == POST_CMD ? "POST_CMD" : |
347 | 0 | cmd_type == POST_CMD_ERR ? "POST_CMD_ERR" : |
348 | 0 | cmd_type == LOG_CMD ? "LOG_CMD" : |
349 | 0 | cmd_type == LOG_CMD_ERR ? "LOG_CMD_ERR" : |
350 | 0 | "(unknown)"), |
351 | 0 | cmdargstr, c->m->name); |
352 | 0 | } |
353 | |
|
354 | 0 | cmd->cmd_class |= c->cmd_class; |
355 | | |
356 | | /* KLUDGE: disable umask() for not G_WRITE operations. Config/ |
357 | | * Directory walking code will be completely redesigned in 1.3, |
358 | | * this is only necessary for performance reasons in 1.1/1.2 |
359 | | */ |
360 | |
|
361 | 0 | if (c->group == NULL || |
362 | 0 | strcmp(c->group, G_WRITE) != 0) { |
363 | 0 | kludge_disable_umask(); |
364 | 0 | } |
365 | |
|
366 | 0 | mr = pr_module_call(c->m, c->handler, cmd); |
367 | 0 | kludge_enable_umask(); |
368 | |
|
369 | 0 | if (MODRET_ISHANDLED(mr)) { |
370 | 0 | success = 1; |
371 | |
|
372 | 0 | } else if (MODRET_ISERROR(mr)) { |
373 | 0 | xerrno = errno; |
374 | 0 | success = -1; |
375 | |
|
376 | 0 | if (cmd_type == POST_CMD || |
377 | 0 | cmd_type == LOG_CMD || |
378 | 0 | cmd_type == LOG_CMD_ERR) { |
379 | 0 | if (MODRET_ERRMSG(mr)) { |
380 | 0 | pr_log_pri(PR_LOG_NOTICE, "%s", MODRET_ERRMSG(mr)); |
381 | 0 | } |
382 | | |
383 | | /* Even though we normally want to return a negative value |
384 | | * for success (indicating lack of success), for |
385 | | * LOG_CMD/LOG_CMD_ERR handlers, we always want to handle |
386 | | * errors as a success value of zero (meaning "keep looking"). |
387 | | * |
388 | | * This will allow the cmd_rec to continue to be dispatched to |
389 | | * the other interested handlers (Bug#3633). |
390 | | */ |
391 | 0 | if (cmd_type == LOG_CMD || |
392 | 0 | cmd_type == LOG_CMD_ERR) { |
393 | 0 | success = 0; |
394 | 0 | } |
395 | |
|
396 | 0 | } else if (send_error) { |
397 | 0 | if (MODRET_ERRNUM(mr) && |
398 | 0 | MODRET_ERRMSG(mr)) { |
399 | 0 | pr_response_add_err(MODRET_ERRNUM(mr), "%s", MODRET_ERRMSG(mr)); |
400 | |
|
401 | 0 | } else if (MODRET_ERRMSG(mr)) { |
402 | 0 | pr_response_send_raw("%s", MODRET_ERRMSG(mr)); |
403 | 0 | } |
404 | 0 | } |
405 | |
|
406 | 0 | errno = xerrno; |
407 | 0 | } |
408 | |
|
409 | 0 | if (session.user && |
410 | 0 | !(session.sf_flags & SF_XFER) && |
411 | 0 | cmd_type == CMD) { |
412 | 0 | pr_session_set_idle(); |
413 | 0 | } |
414 | |
|
415 | 0 | destroy_pool(cmd->tmp_pool); |
416 | 0 | cmd->tmp_pool = NULL; |
417 | 0 | } |
418 | | |
419 | 0 | if (!success) { |
420 | 0 | c = pr_stash_get_symbol2(PR_SYM_CMD, match, c, index_cache, hash_cache); |
421 | 0 | } |
422 | 0 | } |
423 | | |
424 | | /* Note: validate is only TRUE for the CMD phase, for specific handlers |
425 | | * (as opposed to any C_ANY handlers). |
426 | | */ |
427 | | |
428 | 0 | if (!c && |
429 | 0 | !success && |
430 | 0 | validate) { |
431 | 0 | char *method; |
432 | | |
433 | | /* Prettify the command method, if need be. */ |
434 | 0 | if (strchr(cmd->argv[0], '_') == NULL) { |
435 | 0 | method = cmd->argv[0]; |
436 | |
|
437 | 0 | } else { |
438 | 0 | register unsigned int i; |
439 | |
|
440 | 0 | method = pstrdup(cmd->pool, cmd->argv[0]); |
441 | 0 | for (i = 0; method[i]; i++) { |
442 | 0 | if (method[i] == '_') { |
443 | 0 | method[i] = ' '; |
444 | 0 | } |
445 | 0 | } |
446 | 0 | } |
447 | |
|
448 | 0 | pr_event_generate("core.unhandled-command", cmd); |
449 | |
|
450 | 0 | pr_response_add_err(R_500, _("%s not understood"), method); |
451 | 0 | success = -1; |
452 | 0 | } |
453 | |
|
454 | 0 | return success; |
455 | 0 | } |
456 | | |
457 | | /* Returns the appropriate maximum buffer size to use for FTP commands |
458 | | * from the client. |
459 | | */ |
460 | 0 | static size_t get_max_cmd_sz(void) { |
461 | 0 | size_t res; |
462 | 0 | size_t *bufsz = NULL; |
463 | |
|
464 | 0 | bufsz = get_param_ptr(main_server->conf, "CommandBufferSize", FALSE); |
465 | 0 | if (bufsz == NULL) { |
466 | 0 | res = PR_DEFAULT_CMD_BUFSZ; |
467 | |
|
468 | 0 | } else { |
469 | 0 | pr_log_debug(DEBUG1, "setting CommandBufferSize to %lu", |
470 | 0 | (unsigned long) *bufsz); |
471 | 0 | res = *bufsz; |
472 | 0 | } |
473 | |
|
474 | 0 | return res; |
475 | 0 | } |
476 | | |
477 | 0 | int pr_cmd_read(cmd_rec **res) { |
478 | 0 | static long cmd_bufsz = -1; |
479 | 0 | static char *cmd_buf = NULL; |
480 | 0 | int cmd_buflen; |
481 | 0 | unsigned int too_large_count = 0; |
482 | 0 | char *ptr; |
483 | |
|
484 | 0 | if (res == NULL) { |
485 | 0 | errno = EINVAL; |
486 | 0 | return -1; |
487 | 0 | } |
488 | | |
489 | 0 | if (cmd_bufsz == -1) { |
490 | 0 | cmd_bufsz = get_max_cmd_sz(); |
491 | 0 | } |
492 | |
|
493 | 0 | if (cmd_buf == NULL) { |
494 | 0 | cmd_buf = pcalloc(session.pool, cmd_bufsz + 1); |
495 | 0 | } |
496 | |
|
497 | 0 | while (TRUE) { |
498 | 0 | pr_signals_handle(); |
499 | |
|
500 | 0 | memset(cmd_buf, '\0', cmd_bufsz); |
501 | |
|
502 | 0 | cmd_buflen = pr_netio_telnet_gets2(cmd_buf, cmd_bufsz, session.c->instrm, |
503 | 0 | session.c->outstrm); |
504 | 0 | if (cmd_buflen < 0) { |
505 | 0 | if (errno == E2BIG) { |
506 | | /* The client sent a too-long command which was ignored; give |
507 | | * them a few more chances, with minor delays? |
508 | | */ |
509 | 0 | too_large_count++; |
510 | 0 | pr_timer_usleep(250 * 1000); |
511 | |
|
512 | 0 | if (too_large_count > 3) { |
513 | 0 | return -1; |
514 | 0 | } |
515 | | |
516 | 0 | continue; |
517 | 0 | } |
518 | | |
519 | 0 | if (session.c->instrm->strm_errno == 0) { |
520 | 0 | pr_trace_msg("command", 6, |
521 | 0 | "client sent EOF, closing control connection"); |
522 | 0 | } |
523 | |
|
524 | 0 | return -1; |
525 | 0 | } |
526 | | |
527 | 0 | break; |
528 | 0 | } |
529 | | |
530 | | /* If the read length is less than the cmd_bufsz, then there is no need to |
531 | | * truncate the buffer by inserting a NUL. |
532 | | */ |
533 | 0 | if (cmd_buflen > cmd_bufsz) { |
534 | 0 | pr_log_debug(DEBUG0, "truncating incoming command length (%d bytes) to " |
535 | 0 | "CommandBufferSize %lu; use the CommandBufferSize directive to increase " |
536 | 0 | "the allowed command length", cmd_buflen, (unsigned long) cmd_bufsz); |
537 | 0 | cmd_buf[cmd_bufsz-1] = '\0'; |
538 | 0 | } |
539 | |
|
540 | 0 | if (cmd_buflen > 0 && |
541 | 0 | (cmd_buf[cmd_buflen-1] == '\n' || cmd_buf[cmd_buflen-1] == '\r')) { |
542 | 0 | cmd_buf[cmd_buflen-1] = '\0'; |
543 | 0 | cmd_buflen--; |
544 | |
|
545 | 0 | if (cmd_buflen > 0 && |
546 | 0 | (cmd_buf[cmd_buflen-1] == '\n' || cmd_buf[cmd_buflen-1] =='\r')) { |
547 | 0 | cmd_buf[cmd_buflen-1] = '\0'; |
548 | 0 | cmd_buflen--; |
549 | 0 | } |
550 | 0 | } |
551 | |
|
552 | 0 | ptr = cmd_buf; |
553 | 0 | if (*ptr == '\r') { |
554 | 0 | ptr++; |
555 | 0 | } |
556 | |
|
557 | 0 | if (*ptr) { |
558 | 0 | int flags = 0; |
559 | 0 | cmd_rec *cmd; |
560 | | |
561 | | /* If this is a SITE command, preserve embedded whitespace in the |
562 | | * command parameters, in order to handle file names that have multiple |
563 | | * spaces in the names. Arguably this should be handled in the SITE |
564 | | * command handlers themselves, via cmd->arg. This small hack |
565 | | * reduces the burden on SITE module developers, however. |
566 | | */ |
567 | 0 | if (strncasecmp(ptr, C_SITE, 4) == 0) { |
568 | 0 | flags |= PR_STR_FL_PRESERVE_WHITESPACE; |
569 | 0 | } |
570 | |
|
571 | 0 | cmd = make_ftp_cmd(session.pool, ptr, cmd_buflen, flags); |
572 | 0 | if (cmd != NULL) { |
573 | 0 | *res = cmd; |
574 | |
|
575 | 0 | if (pr_cmd_is_http(cmd) == TRUE) { |
576 | 0 | cmd->is_ftp = FALSE; |
577 | 0 | cmd->protocol = "HTTP"; |
578 | |
|
579 | 0 | } else if (pr_cmd_is_ssh2(cmd) == TRUE) { |
580 | 0 | cmd->is_ftp = FALSE; |
581 | 0 | cmd->protocol = "SSH2"; |
582 | |
|
583 | 0 | } else if (pr_cmd_is_smtp(cmd) == TRUE) { |
584 | 0 | cmd->is_ftp = FALSE; |
585 | 0 | cmd->protocol = "SMTP"; |
586 | |
|
587 | 0 | } else { |
588 | | /* Assume that the client is sending valid FTP commands. */ |
589 | 0 | cmd->is_ftp = TRUE; |
590 | 0 | cmd->protocol = "FTP"; |
591 | 0 | } |
592 | 0 | } |
593 | 0 | } |
594 | |
|
595 | 0 | return 0; |
596 | 0 | } |
597 | | |
598 | 0 | static int set_cmd_start_ms(cmd_rec *cmd) { |
599 | 0 | void *v; |
600 | 0 | uint64_t start_ms; |
601 | |
|
602 | 0 | if (cmd->notes == NULL) { |
603 | 0 | return 0; |
604 | 0 | } |
605 | | |
606 | 0 | v = (void *) pr_table_get(cmd->notes, "start_ms", NULL); |
607 | 0 | if (v != NULL) { |
608 | 0 | return 0; |
609 | 0 | } |
610 | | |
611 | 0 | if (pr_gettimeofday_millis(&start_ms) < 0) { |
612 | 0 | return -1; |
613 | 0 | } |
614 | | |
615 | 0 | v = palloc(cmd->pool, sizeof(uint64_t)); |
616 | 0 | memcpy(v, &start_ms, sizeof(uint64_t)); |
617 | |
|
618 | 0 | return pr_table_add(cmd->notes, "start_ms", v, sizeof(uint64_t)); |
619 | 0 | } |
620 | | |
621 | 0 | int pr_cmd_dispatch_phase(cmd_rec *cmd, int phase, int flags) { |
622 | 0 | char *cp = NULL; |
623 | 0 | int success = 0, xerrno = 0; |
624 | 0 | pool *resp_pool = NULL; |
625 | |
|
626 | 0 | if (cmd == NULL) { |
627 | 0 | errno = EINVAL; |
628 | 0 | return -1; |
629 | 0 | } |
630 | | |
631 | 0 | cmd->server = main_server; |
632 | |
|
633 | 0 | if (flags & PR_CMD_DISPATCH_FL_CLEAR_RESPONSE) { |
634 | | /* Skip logging the internal CONNECT/DISCONNECT commands. */ |
635 | 0 | if (!(cmd->cmd_class & CL_CONNECT) && |
636 | 0 | !(cmd->cmd_class & CL_DISCONNECT)) { |
637 | 0 | pr_trace_msg("response", 9, |
638 | 0 | "clearing response lists before dispatching command '%s'", |
639 | 0 | (char *) cmd->argv[0]); |
640 | 0 | } |
641 | 0 | pr_response_clear(&resp_list); |
642 | 0 | pr_response_clear(&resp_err_list); |
643 | 0 | } |
644 | | |
645 | | /* Get any previous pool that may be being used by the Response API. |
646 | | * |
647 | | * In most cases, this will be NULL. However, if proftpd is in the |
648 | | * midst of a data transfer when a command comes in on the control |
649 | | * connection, then the pool in use will be that of the data transfer |
650 | | * instigating command. We want to stash that pool, so that after this |
651 | | * command is dispatched, we can return the pool of the old command. |
652 | | * Otherwise, Bad Things (segfaults) happen. |
653 | | */ |
654 | 0 | resp_pool = pr_response_get_pool(); |
655 | | |
656 | | /* Set the pool used by the Response API for this command. */ |
657 | 0 | pr_response_set_pool(cmd->pool); |
658 | |
|
659 | 0 | for (cp = cmd->argv[0]; *cp; cp++) { |
660 | 0 | *cp = toupper((int) *cp); |
661 | 0 | } |
662 | |
|
663 | 0 | if (cmd->cmd_class == 0) { |
664 | 0 | cmd->cmd_class = get_command_class(cmd->argv[0]); |
665 | 0 | } |
666 | |
|
667 | 0 | if (cmd->cmd_id == 0) { |
668 | 0 | cmd->cmd_id = pr_cmd_get_id(cmd->argv[0]); |
669 | 0 | } |
670 | |
|
671 | 0 | set_cmd_start_ms(cmd); |
672 | |
|
673 | 0 | if (phase == 0) { |
674 | | /* First, dispatch to wildcard PRE_CMD handlers. */ |
675 | 0 | success = _dispatch(cmd, PRE_CMD, FALSE, C_ANY); |
676 | 0 | if (success == 0) { |
677 | | /* No success yet? Run other PRE_CMD phase handlers. */ |
678 | 0 | success = _dispatch(cmd, PRE_CMD, FALSE, NULL); |
679 | 0 | } |
680 | |
|
681 | 0 | if (success < 0) { |
682 | | /* Dispatch to POST_CMD_ERR handlers as well. */ |
683 | |
|
684 | 0 | _dispatch(cmd, POST_CMD_ERR, FALSE, C_ANY); |
685 | 0 | _dispatch(cmd, POST_CMD_ERR, FALSE, NULL); |
686 | |
|
687 | 0 | _dispatch(cmd, LOG_CMD_ERR, FALSE, C_ANY); |
688 | 0 | _dispatch(cmd, LOG_CMD_ERR, FALSE, NULL); |
689 | |
|
690 | 0 | xerrno = errno; |
691 | 0 | pr_trace_msg("response", 9, "flushing error response list for '%s'", |
692 | 0 | (char *) cmd->argv[0]); |
693 | 0 | pr_response_flush(&resp_err_list); |
694 | | |
695 | | /* Restore any previous pool to the Response API. */ |
696 | 0 | pr_response_set_pool(resp_pool); |
697 | |
|
698 | 0 | errno = xerrno; |
699 | 0 | return success; |
700 | 0 | } |
701 | | |
702 | 0 | success = _dispatch(cmd, CMD, FALSE, C_ANY); |
703 | 0 | if (success == 0) { |
704 | 0 | success = _dispatch(cmd, CMD, TRUE, NULL); |
705 | 0 | } |
706 | |
|
707 | 0 | if (success == 1) { |
708 | 0 | success = _dispatch(cmd, POST_CMD, FALSE, C_ANY); |
709 | 0 | if (success == 0) { |
710 | 0 | success = _dispatch(cmd, POST_CMD, FALSE, NULL); |
711 | 0 | } |
712 | |
|
713 | 0 | _dispatch(cmd, LOG_CMD, FALSE, C_ANY); |
714 | 0 | _dispatch(cmd, LOG_CMD, FALSE, NULL); |
715 | |
|
716 | 0 | xerrno = errno; |
717 | 0 | pr_trace_msg("response", 9, "flushing response list for '%s'", |
718 | 0 | (char *) cmd->argv[0]); |
719 | 0 | pr_response_flush(&resp_list); |
720 | |
|
721 | 0 | errno = xerrno; |
722 | |
|
723 | 0 | } else if (success < 0) { |
724 | | /* Allow for non-logging command handlers to be run if CMD fails. */ |
725 | |
|
726 | 0 | success = _dispatch(cmd, POST_CMD_ERR, FALSE, C_ANY); |
727 | 0 | if (success == 0) { |
728 | 0 | success = _dispatch(cmd, POST_CMD_ERR, FALSE, NULL); |
729 | 0 | } |
730 | |
|
731 | 0 | _dispatch(cmd, LOG_CMD_ERR, FALSE, C_ANY); |
732 | 0 | _dispatch(cmd, LOG_CMD_ERR, FALSE, NULL); |
733 | |
|
734 | 0 | xerrno = errno; |
735 | 0 | pr_trace_msg("response", 9, "flushing error response list for '%s'", |
736 | 0 | (char *) cmd->argv[0]); |
737 | 0 | pr_response_flush(&resp_err_list); |
738 | |
|
739 | 0 | errno = xerrno; |
740 | 0 | } |
741 | |
|
742 | 0 | } else { |
743 | 0 | switch (phase) { |
744 | 0 | case PRE_CMD: |
745 | 0 | case POST_CMD: |
746 | 0 | case POST_CMD_ERR: |
747 | 0 | success = _dispatch(cmd, phase, FALSE, C_ANY); |
748 | 0 | if (success == 0) { |
749 | 0 | success = _dispatch(cmd, phase, FALSE, NULL); |
750 | 0 | xerrno = errno; |
751 | 0 | } |
752 | 0 | break; |
753 | | |
754 | 0 | case CMD: |
755 | 0 | success = _dispatch(cmd, phase, FALSE, C_ANY); |
756 | 0 | if (success == 0) { |
757 | 0 | success = _dispatch(cmd, phase, TRUE, NULL); |
758 | 0 | } |
759 | 0 | break; |
760 | | |
761 | 0 | case LOG_CMD: |
762 | 0 | case LOG_CMD_ERR: |
763 | 0 | (void) _dispatch(cmd, phase, FALSE, C_ANY); |
764 | 0 | (void) _dispatch(cmd, phase, FALSE, NULL); |
765 | 0 | break; |
766 | | |
767 | 0 | default: |
768 | | /* Restore any previous pool to the Response API. */ |
769 | 0 | pr_response_set_pool(resp_pool); |
770 | |
|
771 | 0 | errno = EINVAL; |
772 | 0 | return -1; |
773 | 0 | } |
774 | | |
775 | 0 | if (flags & PR_CMD_DISPATCH_FL_SEND_RESPONSE) { |
776 | 0 | xerrno = errno; |
777 | |
|
778 | 0 | if (success == 1) { |
779 | 0 | pr_trace_msg("response", 9, "flushing response list for '%s'", |
780 | 0 | (char *) cmd->argv[0]); |
781 | 0 | pr_response_flush(&resp_list); |
782 | |
|
783 | 0 | } else if (success < 0) { |
784 | 0 | pr_trace_msg("response", 9, "flushing error response list for '%s'", |
785 | 0 | (char *) cmd->argv[0]); |
786 | 0 | pr_response_flush(&resp_err_list); |
787 | 0 | } |
788 | |
|
789 | 0 | errno = xerrno; |
790 | 0 | } |
791 | 0 | } |
792 | | |
793 | | /* Restore any previous pool to the Response API. */ |
794 | 0 | pr_response_set_pool(resp_pool); |
795 | |
|
796 | 0 | errno = xerrno; |
797 | 0 | return success; |
798 | 0 | } |
799 | | |
800 | 0 | int pr_cmd_dispatch(cmd_rec *cmd) { |
801 | 0 | return pr_cmd_dispatch_phase(cmd, 0, |
802 | 0 | PR_CMD_DISPATCH_FL_SEND_RESPONSE|PR_CMD_DISPATCH_FL_CLEAR_RESPONSE); |
803 | 0 | } |
804 | | |
805 | 0 | static cmd_rec *make_ftp_cmd(pool *p, char *buf, size_t buflen, int flags) { |
806 | 0 | register unsigned int i, j; |
807 | 0 | char *arg, *ptr, *wrd; |
808 | 0 | size_t arg_len; |
809 | 0 | cmd_rec *cmd; |
810 | 0 | pool *subpool; |
811 | 0 | array_header *tarr; |
812 | 0 | int have_crnul = FALSE, str_flags = PR_STR_FL_PRESERVE_COMMENTS|flags; |
813 | | |
814 | | /* Be pedantic (and RFC-compliant) by not allowing leading whitespace |
815 | | * in an issued FTP command. Will this cause troubles with many clients? |
816 | | */ |
817 | 0 | if (PR_ISSPACE(buf[0])) { |
818 | 0 | pr_trace_msg("ctrl", 5, |
819 | 0 | "command '%s' has illegal leading whitespace, rejecting", buf); |
820 | 0 | errno = EINVAL; |
821 | 0 | return NULL; |
822 | 0 | } |
823 | | |
824 | | /* By default, pr_str_get_word will handle quotes and backslashes for |
825 | | * escaping characters. This can produce words which are shorter, use |
826 | | * fewer bytes than the corresponding input buffer. |
827 | | * |
828 | | * In this particular situation, we use the length of this initial word |
829 | | * for determining the length of the remaining buffer bytes, assumed to |
830 | | * contain the FTP command arguments. If this initial word is thus |
831 | | * unexpectedly "shorter", due to nonconformant FTP text, it can lead |
832 | | * the subsequent buffer scan, looking for CRNUL sequencees, to access |
833 | | * unexpected memory addresses (Issue #1683). |
834 | | * |
835 | | * Thus for this particular situation, we tell the function to ignore/skip |
836 | | * such quote/backslash semantics, and treat them as any other character |
837 | | * using the IGNORE_QUOTES flag. |
838 | | */ |
839 | | |
840 | 0 | ptr = buf; |
841 | 0 | wrd = pr_str_get_word(&ptr, str_flags|PR_STR_FL_IGNORE_QUOTES); |
842 | 0 | if (wrd == NULL) { |
843 | | /* Nothing there...bail out. */ |
844 | 0 | pr_trace_msg("ctrl", 5, "command '%s' is empty, ignoring", buf); |
845 | 0 | errno = ENOENT; |
846 | 0 | return NULL; |
847 | 0 | } |
848 | | |
849 | | /* Note that this first word is the FTP command. This is why we make |
850 | | * use of the ptr buffer, which advances through the input buffer as |
851 | | * we read words from the buffer. |
852 | | */ |
853 | | |
854 | 0 | subpool = make_sub_pool(p); |
855 | 0 | pr_pool_tag(subpool, "make_ftp_cmd pool"); |
856 | 0 | cmd = pcalloc(subpool, sizeof(cmd_rec)); |
857 | 0 | cmd->pool = subpool; |
858 | 0 | cmd->tmp_pool = NULL; |
859 | 0 | cmd->stash_index = -1; |
860 | 0 | cmd->stash_hash = 0; |
861 | |
|
862 | 0 | tarr = make_array(cmd->pool, 2, sizeof(char *)); |
863 | |
|
864 | 0 | *((char **) push_array(tarr)) = pstrdup(cmd->pool, wrd); |
865 | 0 | cmd->argc++; |
866 | | |
867 | | /* Make a copy of the command argument; we need to scan through it, |
868 | | * looking for any CR+NUL sequences, per RFC 2640, Section 3.1. |
869 | | * |
870 | | * Note for future readers that this scanning may cause problems for |
871 | | * commands such as ADAT, ENC, and MIC. Per RFC 2228, the arguments for |
872 | | * these commands are base64-encoded Telnet strings, thus there is no |
873 | | * chance of them containing CRNUL sequences. Any modules which implement |
874 | | * the translating of those arguments, e.g. mod_gss, will need to ensure |
875 | | * it does the proper handling of CRNUL sequences itself. |
876 | | */ |
877 | 0 | arg_len = buflen - strlen(wrd); |
878 | 0 | arg = pcalloc(cmd->pool, arg_len + 1); |
879 | | |
880 | | /* Remember that ptr here is advanced past the first word. */ |
881 | 0 | for (i = 0, j = 0; i < arg_len; i++) { |
882 | 0 | pr_signals_handle(); |
883 | 0 | if (i > 1 && |
884 | 0 | ptr[i] == '\0' && |
885 | 0 | ptr[i-1] == '\r') { |
886 | | |
887 | | /* Strip out the NUL by simply not copying it into the new buffer. */ |
888 | 0 | have_crnul = TRUE; |
889 | |
|
890 | 0 | } else { |
891 | 0 | arg[j++] = ptr[i]; |
892 | 0 | } |
893 | 0 | } |
894 | |
|
895 | 0 | if (have_crnul == TRUE) { |
896 | 0 | char *dup_arg; |
897 | | |
898 | | /* Now make a copy of the stripped argument; this is what we need to |
899 | | * tokenize into words, for further command dispatching/processing. |
900 | | */ |
901 | 0 | dup_arg = pstrdup(cmd->pool, arg); |
902 | 0 | ptr = dup_arg; |
903 | 0 | } |
904 | |
|
905 | 0 | cmd->arg = arg; |
906 | | |
907 | | /* Now we can read the remamining words, as command arguments, from the |
908 | | * input buffer. |
909 | | */ |
910 | 0 | while ((wrd = pr_str_get_word(&ptr, str_flags)) != NULL) { |
911 | 0 | pr_signals_handle(); |
912 | 0 | *((char **) push_array(tarr)) = pstrdup(cmd->pool, wrd); |
913 | 0 | cmd->argc++; |
914 | 0 | } |
915 | |
|
916 | 0 | *((char **) push_array(tarr)) = NULL; |
917 | 0 | cmd->argv = tarr->elts; |
918 | 0 | pr_pool_tag(cmd->pool, cmd->argv[0]); |
919 | | |
920 | | /* This table will not contain that many entries, so a low number |
921 | | * of chains should suffice. |
922 | | */ |
923 | 0 | cmd->notes = pr_table_nalloc(cmd->pool, 0, 8); |
924 | |
|
925 | 0 | return cmd; |
926 | 0 | } |
927 | | |
928 | 0 | static void cmd_loop(server_rec *server, conn_t *c) { |
929 | |
|
930 | 0 | while (TRUE) { |
931 | 0 | int res = 0; |
932 | 0 | cmd_rec *cmd = NULL; |
933 | |
|
934 | 0 | pr_signals_handle(); |
935 | |
|
936 | 0 | res = pr_cmd_read(&cmd); |
937 | 0 | if (res < 0) { |
938 | 0 | if (PR_NETIO_ERRNO(session.c->instrm) == EINTR) { |
939 | | /* Simple interrupted syscall */ |
940 | 0 | continue; |
941 | 0 | } |
942 | | |
943 | 0 | #ifndef PR_DEVEL_NO_DAEMON |
944 | | /* Otherwise, EOF */ |
945 | 0 | pr_session_disconnect(NULL, PR_SESS_DISCONNECT_CLIENT_EOF, NULL); |
946 | | #else |
947 | | return; |
948 | | #endif /* PR_DEVEL_NO_DAEMON */ |
949 | 0 | } |
950 | | |
951 | | /* Data received, reset idle timer */ |
952 | 0 | if (pr_data_get_timeout(PR_DATA_TIMEOUT_IDLE) > 0) { |
953 | 0 | pr_timer_reset(PR_TIMER_IDLE, ANY_MODULE); |
954 | 0 | } |
955 | |
|
956 | 0 | if (cmd != NULL) { |
957 | | /* Detect known commands for other protocols; if found, drop the |
958 | | * connection, lest we be used as part of an attack on a different |
959 | | * protocol server (Bug#4143). |
960 | | */ |
961 | 0 | if (cmd->is_ftp == FALSE) { |
962 | 0 | pr_log_pri(PR_LOG_WARNING, |
963 | 0 | "client sent %s command '%s', disconnecting", cmd->protocol, |
964 | 0 | (char *) cmd->argv[0]); |
965 | 0 | pr_event_generate("core.bad-protocol", cmd); |
966 | 0 | pr_session_disconnect(NULL, PR_SESS_DISCONNECT_BAD_PROTOCOL, |
967 | 0 | cmd->protocol); |
968 | 0 | } |
969 | |
|
970 | 0 | pr_cmd_dispatch(cmd); |
971 | 0 | destroy_pool(cmd->pool); |
972 | 0 | session.curr_cmd = NULL; |
973 | 0 | session.curr_cmd_id = 0; |
974 | 0 | session.curr_cmd_rec = NULL; |
975 | |
|
976 | 0 | } else { |
977 | 0 | pr_event_generate("core.invalid-command", NULL); |
978 | 0 | pr_response_send(R_500, _("Invalid command: try being more creative")); |
979 | 0 | } |
980 | | |
981 | | /* Release any working memory allocated in inet */ |
982 | 0 | pr_inet_clear(); |
983 | 0 | } |
984 | 0 | } |
985 | | |
986 | 0 | void restart_daemon(void *d1, void *d2, void *d3, void *d4) { |
987 | 0 | int maxfd, res, xerrno; |
988 | 0 | fd_set childfds; |
989 | 0 | struct timeval restart_start, restart_finish; |
990 | 0 | long restart_elapsed = 0; |
991 | |
|
992 | 0 | if (is_master == FALSE || |
993 | 0 | !mpid) { |
994 | | /* Child process -- cannot restart, log error */ |
995 | 0 | pr_log_pri(PR_LOG_ERR, "received SIGHUP, cannot restart child process"); |
996 | 0 | return; |
997 | 0 | } |
998 | | |
999 | 0 | pr_log_pri(PR_LOG_NOTICE, |
1000 | 0 | "received SIGHUP -- master server reparsing configuration file"); |
1001 | |
|
1002 | 0 | gettimeofday(&restart_start, NULL); |
1003 | | |
1004 | | /* Make sure none of our children haven't completed start up */ |
1005 | 0 | FD_ZERO(&childfds); |
1006 | 0 | maxfd = -1; |
1007 | |
|
1008 | 0 | maxfd = semaphore_fds(&childfds, maxfd); |
1009 | 0 | if (maxfd > -1) { |
1010 | 0 | pr_log_pri(PR_LOG_NOTICE, "waiting for child processes to complete " |
1011 | 0 | "initialization"); |
1012 | |
|
1013 | 0 | while (maxfd != -1) { |
1014 | 0 | int i; |
1015 | |
|
1016 | 0 | i = select(maxfd + 1, &childfds, NULL, NULL, NULL); |
1017 | 0 | if (i > 0) { |
1018 | 0 | pr_child_t *ch; |
1019 | |
|
1020 | 0 | for (ch = child_get(NULL); ch; ch = child_get(ch)) { |
1021 | 0 | if (ch->ch_pipefd != -1 && |
1022 | 0 | FD_ISSET(ch->ch_pipefd, &childfds)) { |
1023 | 0 | (void) close(ch->ch_pipefd); |
1024 | 0 | ch->ch_pipefd = -1; |
1025 | 0 | } |
1026 | 0 | } |
1027 | 0 | } |
1028 | |
|
1029 | 0 | FD_ZERO(&childfds); |
1030 | 0 | maxfd = -1; |
1031 | 0 | maxfd = semaphore_fds(&childfds, maxfd); |
1032 | 0 | } |
1033 | 0 | } |
1034 | |
|
1035 | 0 | free_bindings(); |
1036 | | |
1037 | | /* Run through the list of registered restart callbacks. */ |
1038 | 0 | pr_event_generate("core.restart", NULL); |
1039 | |
|
1040 | 0 | init_log(); |
1041 | 0 | init_netaddr(); |
1042 | 0 | init_class(); |
1043 | 0 | init_config(); |
1044 | 0 | init_dirtree(); |
1045 | |
|
1046 | | #ifdef PR_USE_NLS |
1047 | | encode_free(); |
1048 | | #endif /* PR_USE_NLS */ |
1049 | |
|
1050 | 0 | pr_netaddr_clear_cache(); |
1051 | 0 | pr_parser_prepare(NULL, NULL); |
1052 | 0 | pr_event_generate("core.preparse", NULL); |
1053 | |
|
1054 | 0 | PRIVS_ROOT |
1055 | 0 | res = pr_parser_parse_file(NULL, config_filename, NULL, 0); |
1056 | 0 | xerrno = errno; |
1057 | 0 | PRIVS_RELINQUISH |
1058 | |
|
1059 | 0 | if (res < 0) { |
1060 | | /* Note: EPERM is used to indicate the presence of unrecognized |
1061 | | * configuration directives in the parsed file(s). |
1062 | | */ |
1063 | 0 | if (xerrno != EPERM) { |
1064 | 0 | pr_log_pri(PR_LOG_WARNING, |
1065 | 0 | "fatal: unable to read configuration file '%s': %s", config_filename, |
1066 | 0 | strerror(xerrno)); |
1067 | 0 | } |
1068 | |
|
1069 | 0 | pr_session_end(0); |
1070 | 0 | } |
1071 | |
|
1072 | 0 | if (pr_parser_cleanup() < 0) { |
1073 | 0 | pr_log_pri(PR_LOG_WARNING, |
1074 | 0 | "fatal: error processing configuration file '%s': " |
1075 | 0 | "unclosed configuration section", config_filename); |
1076 | 0 | pr_session_end(0); |
1077 | 0 | } |
1078 | |
|
1079 | | #ifdef PR_USE_NLS |
1080 | | encode_init(); |
1081 | | #endif /* PR_USE_NLS */ |
1082 | | |
1083 | | /* After configuration is complete, make sure that passwd, group |
1084 | | * aren't held open (unnecessary fds for master daemon) |
1085 | | */ |
1086 | 0 | endpwent(); |
1087 | 0 | endgrent(); |
1088 | |
|
1089 | 0 | if (fixup_servers(server_list) < 0) { |
1090 | 0 | pr_log_pri(PR_LOG_WARNING, |
1091 | 0 | "fatal: error processing configuration file '%s'", config_filename); |
1092 | 0 | pr_session_end(0); |
1093 | 0 | } |
1094 | |
|
1095 | 0 | pr_event_generate("core.postparse", NULL); |
1096 | | |
1097 | | /* Recreate the listen connection. Can an inetd-spawned server accept |
1098 | | * and process HUP? |
1099 | | */ |
1100 | 0 | init_bindings(); |
1101 | |
|
1102 | 0 | gettimeofday(&restart_finish, NULL); |
1103 | |
|
1104 | 0 | restart_elapsed = ((restart_finish.tv_sec - restart_start.tv_sec) * 1000L) + |
1105 | 0 | ((restart_finish.tv_usec - restart_start.tv_usec) / 1000L); |
1106 | 0 | pr_trace_msg("config", 12, "restart took %ld millisecs", restart_elapsed); |
1107 | 0 | } |
1108 | | |
1109 | 0 | static void set_server_privs(void) { |
1110 | 0 | uid_t *uid, server_uid, current_euid; |
1111 | 0 | gid_t *gid, server_gid, current_egid; |
1112 | 0 | unsigned char switch_server_id = FALSE; |
1113 | |
|
1114 | 0 | current_euid = geteuid(); |
1115 | 0 | current_egid = getegid(); |
1116 | |
|
1117 | 0 | uid = get_param_ptr(main_server->conf, "UserID", FALSE); |
1118 | 0 | if (uid != NULL) { |
1119 | 0 | server_uid = *uid; |
1120 | 0 | switch_server_id = TRUE; |
1121 | |
|
1122 | 0 | } else { |
1123 | 0 | server_uid = current_euid; |
1124 | 0 | } |
1125 | |
|
1126 | 0 | gid = get_param_ptr(main_server->conf, "GroupID", FALSE); |
1127 | 0 | if (gid != NULL) { |
1128 | 0 | server_gid = *gid; |
1129 | 0 | switch_server_id = TRUE; |
1130 | |
|
1131 | 0 | } else { |
1132 | 0 | server_gid = current_egid; |
1133 | 0 | } |
1134 | |
|
1135 | 0 | if (switch_server_id) { |
1136 | 0 | PRIVS_ROOT |
1137 | | |
1138 | | /* Note: will it be necessary to double check this switch, as is done |
1139 | | * in elsewhere in this file? |
1140 | | */ |
1141 | 0 | PRIVS_SETUP(server_uid, server_gid); |
1142 | 0 | } |
1143 | 0 | } |
1144 | | |
1145 | 0 | static void fork_server(int fd, conn_t *l, unsigned char no_fork) { |
1146 | 0 | conn_t *conn = NULL; |
1147 | 0 | int i, rev; |
1148 | 0 | int semfds[2] = { -1, -1 }; |
1149 | 0 | int xerrno = 0; |
1150 | |
|
1151 | 0 | #ifndef PR_DEVEL_NO_FORK |
1152 | 0 | pid_t pid; |
1153 | 0 | sigset_t sig_set; |
1154 | |
|
1155 | 0 | if (no_fork == FALSE) { |
1156 | | |
1157 | | /* A race condition exists on heavily loaded servers where the parent |
1158 | | * catches SIGHUP and attempts to close/re-open the main listening |
1159 | | * socket(s), however the children haven't finished closing them |
1160 | | * (EADDRINUSE). We use a semaphore pipe here to flag the parent once |
1161 | | * the child has closed all former listening sockets. |
1162 | | */ |
1163 | |
|
1164 | 0 | if (pipe(semfds) == -1) { |
1165 | 0 | pr_log_pri(PR_LOG_ALERT, "pipe(2) failed: %s", strerror(errno)); |
1166 | 0 | (void) close(fd); |
1167 | 0 | return; |
1168 | 0 | } |
1169 | | |
1170 | | /* Need to make sure the child (writer) end of the pipe isn't |
1171 | | * < 2 (stdio/stdout/stderr) as this will cause problems later. |
1172 | | */ |
1173 | 0 | semfds[1] = pr_fs_get_usable_fd(semfds[1]); |
1174 | | |
1175 | | /* Make sure we set the close-on-exec flag for the parent's read side |
1176 | | * of the pipe. |
1177 | | */ |
1178 | 0 | (void) fcntl(semfds[0], F_SETFD, FD_CLOEXEC); |
1179 | | |
1180 | | /* We block SIGCHLD to prevent a race condition if the child |
1181 | | * dies before we can record it's pid. Also block SIGTERM to |
1182 | | * prevent sig_terminate() from examining the child list |
1183 | | */ |
1184 | |
|
1185 | 0 | sigemptyset(&sig_set); |
1186 | 0 | sigaddset(&sig_set, SIGTERM); |
1187 | 0 | sigaddset(&sig_set, SIGCHLD); |
1188 | 0 | sigaddset(&sig_set, SIGUSR1); |
1189 | 0 | sigaddset(&sig_set, SIGUSR2); |
1190 | |
|
1191 | 0 | if (sigprocmask(SIG_BLOCK, &sig_set, NULL) < 0) { |
1192 | 0 | pr_log_pri(PR_LOG_NOTICE, |
1193 | 0 | "unable to block signal set: %s", strerror(errno)); |
1194 | 0 | } |
1195 | |
|
1196 | 0 | pid = fork(); |
1197 | 0 | xerrno = errno; |
1198 | |
|
1199 | 0 | switch (pid) { |
1200 | | |
1201 | 0 | case 0: /* child */ |
1202 | | /* No longer the master process. */ |
1203 | 0 | is_master = FALSE; |
1204 | 0 | if (sigprocmask(SIG_UNBLOCK, &sig_set, NULL) < 0) { |
1205 | 0 | pr_log_pri(PR_LOG_NOTICE, |
1206 | 0 | "unable to unblock signal set: %s", strerror(errno)); |
1207 | 0 | } |
1208 | | |
1209 | | /* No longer need the read side of the semaphore pipe. */ |
1210 | 0 | (void) close(semfds[0]); |
1211 | 0 | break; |
1212 | | |
1213 | 0 | case -1: |
1214 | 0 | if (sigprocmask(SIG_UNBLOCK, &sig_set, NULL) < 0) { |
1215 | 0 | pr_log_pri(PR_LOG_NOTICE, |
1216 | 0 | "unable to unblock signal set: %s", strerror(errno)); |
1217 | 0 | } |
1218 | |
|
1219 | 0 | pr_log_pri(PR_LOG_ALERT, "unable to fork(): %s", strerror(xerrno)); |
1220 | | |
1221 | | /* The parent doesn't need the socket open. */ |
1222 | 0 | (void) close(fd); |
1223 | 0 | (void) close(semfds[0]); |
1224 | 0 | (void) close(semfds[1]); |
1225 | |
|
1226 | 0 | return; |
1227 | | |
1228 | 0 | default: /* parent */ |
1229 | | /* The parent doesn't need the socket open */ |
1230 | 0 | (void) close(fd); |
1231 | |
|
1232 | 0 | child_add(pid, semfds[0]); |
1233 | 0 | (void) close(semfds[1]); |
1234 | | |
1235 | | /* Unblock the signals now as sig_child() will catch |
1236 | | * an "immediate" death and remove the pid from the children list |
1237 | | */ |
1238 | 0 | if (sigprocmask(SIG_UNBLOCK, &sig_set, NULL) < 0) { |
1239 | 0 | pr_log_pri(PR_LOG_NOTICE, |
1240 | 0 | "unable to unblock signal set: %s", strerror(errno)); |
1241 | 0 | } |
1242 | |
|
1243 | 0 | return; |
1244 | 0 | } |
1245 | 0 | } |
1246 | | |
1247 | 0 | session.pid = getpid(); |
1248 | | |
1249 | | /* No longer need any listening fds. */ |
1250 | 0 | pr_ipbind_close_listeners(); |
1251 | | |
1252 | | /* There would appear to be no useful purpose behind setting the process |
1253 | | * group of the newly forked child. In daemon/inetd mode, we should have no |
1254 | | * controlling tty and either have the process group of the parent or of |
1255 | | * inetd. In non-daemon mode (-n), doing this may cause SIGTTOU to be |
1256 | | * raised on output to the terminal (stderr logging). |
1257 | | * |
1258 | | * #ifdef HAVE_SETPGID |
1259 | | * setpgid(0,getpid()); |
1260 | | * #else |
1261 | | * # ifdef SETPGRP_VOID |
1262 | | * setpgrp(); |
1263 | | * # else |
1264 | | * setpgrp(0,getpid()); |
1265 | | * # endif |
1266 | | * #endif |
1267 | | * |
1268 | | */ |
1269 | | |
1270 | | /* Reseed pseudo-randoms */ |
1271 | 0 | pr_random_init(); |
1272 | |
|
1273 | 0 | #endif /* PR_DEVEL_NO_FORK */ |
1274 | | |
1275 | | /* Child is running here */ |
1276 | 0 | if (signal(SIGUSR1, pr_signals_handle_disconnect) == SIG_ERR) { |
1277 | 0 | pr_log_pri(PR_LOG_NOTICE, |
1278 | 0 | "unable to install SIGUSR1 (signal %d) handler: %s", SIGUSR1, |
1279 | 0 | strerror(errno)); |
1280 | 0 | } |
1281 | |
|
1282 | 0 | if (signal(SIGUSR2, pr_signals_handle_event) == SIG_ERR) { |
1283 | 0 | pr_log_pri(PR_LOG_NOTICE, |
1284 | 0 | "unable to install SIGUSR2 (signal %d) handler: %s", SIGUSR2, |
1285 | 0 | strerror(errno)); |
1286 | 0 | } |
1287 | |
|
1288 | 0 | if (signal(SIGCHLD, SIG_DFL) == SIG_ERR) { |
1289 | 0 | pr_log_pri(PR_LOG_NOTICE, |
1290 | 0 | "unable to install SIGCHLD (signal %d) handler: %s", SIGCHLD, |
1291 | 0 | strerror(errno)); |
1292 | 0 | } |
1293 | |
|
1294 | 0 | if (signal(SIGHUP, SIG_IGN) == SIG_ERR) { |
1295 | 0 | pr_log_pri(PR_LOG_NOTICE, |
1296 | 0 | "unable to install SIGHUP (signal %d) handler: %s", SIGHUP, |
1297 | 0 | strerror(errno)); |
1298 | 0 | } |
1299 | | |
1300 | | /* From this point on, syslog stays open. We close it first so that the |
1301 | | * logger will pick up our new PID. |
1302 | | * |
1303 | | * We have to delay calling log_opensyslog() until after pr_inet_openrw() |
1304 | | * is called, otherwise the potential exists for the syslog FD to |
1305 | | * be overwritten and the user to see logging information. |
1306 | | * |
1307 | | * This isn't that big of a deal because the logging functions will |
1308 | | * just open it dynamically if they need to. |
1309 | | */ |
1310 | 0 | log_closesyslog(); |
1311 | | |
1312 | | /* Specifically DO NOT perform reverse DNS at this point, to alleviate |
1313 | | * the race condition mentioned above. Instead we do it after closing |
1314 | | * all former listening sockets. |
1315 | | */ |
1316 | 0 | conn = pr_inet_openrw(permanent_pool, l, NULL, PR_NETIO_STRM_CTRL, fd, |
1317 | 0 | STDIN_FILENO, STDOUT_FILENO, FALSE); |
1318 | | |
1319 | | /* Capture errno here, if necessary. */ |
1320 | 0 | if (conn == NULL) { |
1321 | 0 | xerrno = errno; |
1322 | 0 | } |
1323 | | |
1324 | | /* Now do the permanent syslog open |
1325 | | */ |
1326 | 0 | pr_signals_block(); |
1327 | 0 | PRIVS_ROOT |
1328 | |
|
1329 | 0 | log_opensyslog(NULL); |
1330 | |
|
1331 | 0 | PRIVS_RELINQUISH |
1332 | 0 | pr_signals_unblock(); |
1333 | |
|
1334 | 0 | if (conn == NULL) { |
1335 | | /* There are some errors, e.g. ENOTCONN ("Transport endpoint is not |
1336 | | * connected") which can easily happen, as during scans/TCP |
1337 | | * probes/healthchecks, commonly done by load balancers, firewalls, and |
1338 | | * other clients. By the time proftpd reaches the point of looking up |
1339 | | * the peer data for that connection, the client has disconnected. |
1340 | | * |
1341 | | * These are normal errors, and thus should not be logged as fatal |
1342 | | * conditions. |
1343 | | */ |
1344 | 0 | if (xerrno == ENOTCONN || |
1345 | 0 | xerrno == ECONNABORTED || |
1346 | 0 | xerrno == ECONNRESET) { |
1347 | 0 | pr_log_pri(PR_LOG_DEBUG, "unable to open incoming connection: %s", |
1348 | 0 | strerror(xerrno)); |
1349 | |
|
1350 | 0 | } else { |
1351 | 0 | pr_log_pri(PR_LOG_ERR, "fatal: unable to open incoming connection: %s", |
1352 | 0 | strerror(xerrno)); |
1353 | 0 | } |
1354 | |
|
1355 | 0 | exit(1); |
1356 | 0 | } |
1357 | | |
1358 | 0 | pr_gettimeofday_millis(&session.connect_time_ms); |
1359 | 0 | pr_event_generate("core.connect", conn); |
1360 | | |
1361 | | /* Find the server for this connection. */ |
1362 | 0 | main_server = pr_ipbind_get_server(conn->local_addr, conn->local_port); |
1363 | | |
1364 | | /* Make sure we allocate a session pool, even if this connection will |
1365 | | * dropped soon. |
1366 | | */ |
1367 | 0 | session.pool = make_sub_pool(permanent_pool); |
1368 | 0 | pr_pool_tag(session.pool, "Session Pool"); |
1369 | |
|
1370 | 0 | session.c = conn; |
1371 | 0 | session.data_port = conn->remote_port - 1; |
1372 | 0 | session.sf_flags = 0; |
1373 | 0 | session.sp_flags = 0; |
1374 | 0 | session.proc_prefix = "(connecting)"; |
1375 | | |
1376 | | /* If no server is configured to handle the addr the user is connected to, |
1377 | | * drop them. |
1378 | | */ |
1379 | 0 | if (main_server == NULL) { |
1380 | 0 | pr_log_debug(DEBUG2, "No server configuration found for IP address %s", |
1381 | 0 | pr_netaddr_get_ipstr(conn->local_addr)); |
1382 | 0 | pr_log_debug(DEBUG2, "Use the DefaultServer directive to designate " |
1383 | 0 | "a default server configuration to handle requests like this"); |
1384 | |
|
1385 | 0 | pr_response_send(R_500, |
1386 | 0 | _("Sorry, no server available to handle request on %s"), |
1387 | 0 | pr_netaddr_get_dnsstr(conn->local_addr)); |
1388 | 0 | exit(0); |
1389 | 0 | } |
1390 | | |
1391 | 0 | pr_inet_set_proto_opts(permanent_pool, conn, 0, 1, IPTOS_LOWDELAY, 0); |
1392 | | |
1393 | | /* Close the write side of the semaphore pipe to tell the parent |
1394 | | * we are all grown up and have finished housekeeping (closing |
1395 | | * former listen sockets). |
1396 | | */ |
1397 | 0 | (void) close(semfds[1]); |
1398 | | |
1399 | | /* Now perform reverse DNS lookups. */ |
1400 | 0 | if (ServerUseReverseDNS) { |
1401 | 0 | rev = pr_netaddr_set_reverse_dns(ServerUseReverseDNS); |
1402 | |
|
1403 | 0 | if (conn->remote_addr) { |
1404 | 0 | conn->remote_name = pr_netaddr_get_dnsstr(conn->remote_addr); |
1405 | 0 | } |
1406 | |
|
1407 | 0 | pr_netaddr_set_reverse_dns(rev); |
1408 | 0 | } |
1409 | |
|
1410 | 0 | pr_netaddr_set_sess_addrs(); |
1411 | | |
1412 | | /* Check and see if we are shutting down. */ |
1413 | 0 | if (shutting_down == TRUE) { |
1414 | 0 | time_t now; |
1415 | |
|
1416 | 0 | time(&now); |
1417 | 0 | if (!deny || deny <= now) { |
1418 | 0 | pool *tmp_pool; |
1419 | 0 | config_rec *c = NULL; |
1420 | 0 | const char *reason = NULL, *serveraddress; |
1421 | |
|
1422 | 0 | serveraddress = (session.c && session.c->local_addr) ? |
1423 | 0 | pr_netaddr_get_ipstr(session.c->local_addr) : |
1424 | 0 | main_server->ServerAddress; |
1425 | |
|
1426 | 0 | c = find_config(main_server->conf, CONF_PARAM, "MasqueradeAddress", |
1427 | 0 | FALSE); |
1428 | 0 | if (c != NULL) { |
1429 | 0 | pr_netaddr_t *masq_addr = NULL; |
1430 | |
|
1431 | 0 | if (c->argv[0] != NULL) { |
1432 | 0 | masq_addr = c->argv[0]; |
1433 | 0 | } |
1434 | |
|
1435 | 0 | if (masq_addr != NULL) { |
1436 | 0 | serveraddress = pr_netaddr_get_ipstr(masq_addr); |
1437 | 0 | } |
1438 | 0 | } |
1439 | |
|
1440 | 0 | tmp_pool = make_sub_pool(permanent_pool); |
1441 | 0 | pr_pool_tag(tmp_pool, "shutmsg check pool"); |
1442 | |
|
1443 | 0 | reason = sreplace(tmp_pool, shutmsg, |
1444 | 0 | "%s", pstrdup(tmp_pool, pr_strtime3(tmp_pool, shut, FALSE)), |
1445 | 0 | "%r", pstrdup(tmp_pool, pr_strtime3(tmp_pool, deny, FALSE)), |
1446 | 0 | "%d", pstrdup(tmp_pool, pr_strtime3(tmp_pool, disc, FALSE)), |
1447 | 0 | "%C", (session.cwd[0] ? session.cwd : "(none)"), |
1448 | 0 | "%L", serveraddress, |
1449 | 0 | "%R", (session.c && session.c->remote_name ? |
1450 | 0 | session.c->remote_name : "(unknown)"), |
1451 | 0 | "%T", pstrdup(tmp_pool, pr_strtime3(tmp_pool, now, FALSE)), |
1452 | 0 | "%U", "NONE", |
1453 | 0 | "%V", main_server->ServerName, |
1454 | 0 | NULL ); |
1455 | |
|
1456 | 0 | pr_log_auth(PR_LOG_NOTICE, "connection refused (%s) from %s [%s]", |
1457 | 0 | reason, session.c->remote_name, |
1458 | 0 | pr_netaddr_get_ipstr(session.c->remote_addr)); |
1459 | 0 | pr_response_send(R_500, |
1460 | 0 | _("FTP server shut down (%s) -- please try again later"), reason); |
1461 | |
|
1462 | 0 | destroy_pool(tmp_pool); |
1463 | 0 | exit(0); |
1464 | 0 | } |
1465 | 0 | } |
1466 | | |
1467 | 0 | if (main_server->listen) { |
1468 | 0 | if (main_server->listen->listen_fd == conn->rfd || |
1469 | 0 | main_server->listen->listen_fd == conn->wfd) { |
1470 | 0 | main_server->listen->listen_fd = -1; |
1471 | 0 | } |
1472 | |
|
1473 | 0 | main_server->listen = NULL; |
1474 | 0 | } |
1475 | | |
1476 | | /* Set the ID/privs for the User/Group in this server */ |
1477 | 0 | set_server_privs(); |
1478 | | |
1479 | | /* Find the class for this session. */ |
1480 | 0 | session.conn_class = pr_class_match_addr(session.c->remote_addr); |
1481 | 0 | if (session.conn_class != NULL) { |
1482 | 0 | pr_log_debug(DEBUG2, "session requested from client in '%s' class", |
1483 | 0 | session.conn_class->cls_name); |
1484 | |
|
1485 | 0 | } else { |
1486 | 0 | pr_log_debug(DEBUG5, "session requested from client in unknown class"); |
1487 | 0 | } |
1488 | | |
1489 | | /* Check config tree for <Limit LOGIN> directives. Do not perform |
1490 | | * this check until after the class of the session has been determined, |
1491 | | * in order to properly handle any AllowClass/DenyClass directives |
1492 | | * within the <Limit> section. |
1493 | | */ |
1494 | 0 | if (!login_check_limits(main_server->conf, TRUE, FALSE, &i)) { |
1495 | 0 | pr_log_pri(PR_LOG_NOTICE, "Connection from %s [%s] denied", |
1496 | 0 | session.c->remote_name, |
1497 | 0 | pr_netaddr_get_ipstr(session.c->remote_addr)); |
1498 | | |
1499 | | /* XXX Send DisplayConnect here? No chroot to worry about; modules have |
1500 | | * NOT been initialized, so generating an event would not work as |
1501 | | * expected. |
1502 | | */ |
1503 | |
|
1504 | 0 | pr_session_disconnect(NULL, PR_SESS_DISCONNECT_CONFIG_ACL, |
1505 | 0 | "Blocked by <Limit LOGIN>"); |
1506 | 0 | } |
1507 | | |
1508 | | /* Create a table for modules to use. */ |
1509 | 0 | session.notes = pr_table_alloc(session.pool, 0); |
1510 | 0 | if (session.notes == NULL) { |
1511 | 0 | pr_log_debug(DEBUG3, "error creating session.notes table: %s", |
1512 | 0 | strerror(errno)); |
1513 | 0 | } |
1514 | | |
1515 | | /* Prepare the Timers API. */ |
1516 | 0 | timers_init(); |
1517 | | |
1518 | | /* Inform all the modules that we are now a child */ |
1519 | 0 | pr_log_debug(DEBUG7, "performing module session initializations"); |
1520 | 0 | if (modules_session_init() < 0) { |
1521 | 0 | pr_session_disconnect(NULL, PR_SESS_DISCONNECT_SESSION_INIT_FAILED, NULL); |
1522 | 0 | } |
1523 | |
|
1524 | 0 | pr_event_generate("core.connected", conn); |
1525 | |
|
1526 | 0 | pr_log_debug(DEBUG4, "connected - local : %s:%d", |
1527 | 0 | pr_netaddr_get_ipstr(session.c->local_addr), session.c->local_port); |
1528 | 0 | pr_log_debug(DEBUG4, "connected - remote : %s:%d", |
1529 | 0 | pr_netaddr_get_ipstr(session.c->remote_addr), session.c->remote_port); |
1530 | |
|
1531 | 0 | pr_proctitle_set("connected: %s (%s:%d)", |
1532 | 0 | session.c->remote_name ? session.c->remote_name : "?", |
1533 | 0 | session.c->remote_addr ? pr_netaddr_get_ipstr(session.c->remote_addr) : "?", |
1534 | 0 | session.c->remote_port ? session.c->remote_port : 0); |
1535 | |
|
1536 | 0 | pr_log_pri(PR_LOG_INFO, "%s session opened.", |
1537 | 0 | pr_session_get_protocol(PR_SESS_PROTO_FL_LOGOUT)); |
1538 | | |
1539 | | /* Make sure we can receive OOB data */ |
1540 | 0 | pr_inet_set_async(session.pool, session.c); |
1541 | |
|
1542 | 0 | pr_session_send_banner(main_server, |
1543 | 0 | PR_DISPLAY_FL_NO_EOM|PR_DISPLAY_FL_SEND_NOW); |
1544 | |
|
1545 | 0 | cmd_handler(main_server, conn); |
1546 | |
|
1547 | | #ifdef PR_DEVEL_NO_DAEMON |
1548 | | /* Cleanup */ |
1549 | | pr_session_end(PR_SESS_END_FL_NOEXIT); |
1550 | | main_server = NULL; |
1551 | | free_pools(); |
1552 | | pr_proctitle_free(); |
1553 | | #endif /* PR_DEVEL_NO_DAEMON */ |
1554 | 0 | } |
1555 | | |
1556 | 0 | static void disc_children(void) { |
1557 | |
|
1558 | 0 | if (disc && disc <= time(NULL) && child_count()) { |
1559 | 0 | sigset_t sig_set; |
1560 | |
|
1561 | 0 | sigemptyset(&sig_set); |
1562 | 0 | sigaddset(&sig_set, SIGTERM); |
1563 | 0 | sigaddset(&sig_set, SIGCHLD); |
1564 | 0 | sigaddset(&sig_set, SIGUSR1); |
1565 | 0 | sigaddset(&sig_set, SIGUSR2); |
1566 | |
|
1567 | 0 | if (sigprocmask(SIG_BLOCK, &sig_set, NULL) < 0) { |
1568 | 0 | pr_log_pri(PR_LOG_NOTICE, |
1569 | 0 | "unable to block signal set: %s", strerror(errno)); |
1570 | 0 | } |
1571 | |
|
1572 | 0 | PRIVS_ROOT |
1573 | 0 | child_signal(SIGUSR1); |
1574 | 0 | PRIVS_RELINQUISH |
1575 | |
|
1576 | 0 | if (sigprocmask(SIG_UNBLOCK, &sig_set, NULL) < 0) { |
1577 | 0 | pr_log_pri(PR_LOG_NOTICE, |
1578 | 0 | "unable to unblock signal set: %s", strerror(errno)); |
1579 | 0 | } |
1580 | 0 | } |
1581 | 0 | } |
1582 | | |
1583 | 0 | static void daemon_loop(void) { |
1584 | 0 | static int running = 0; |
1585 | 0 | fd_set listenfds; |
1586 | 0 | conn_t *listen_conn; |
1587 | 0 | int i, err_count = 0, fd, xerrno = 0; |
1588 | 0 | unsigned long nconnects = 0UL; |
1589 | 0 | time_t last_error; |
1590 | 0 | struct timeval tv; |
1591 | |
|
1592 | 0 | pr_proctitle_set("(accepting connections)"); |
1593 | |
|
1594 | 0 | time(&last_error); |
1595 | |
|
1596 | 0 | while (TRUE) { |
1597 | 0 | int maxfd, res; |
1598 | |
|
1599 | 0 | run_schedule(); |
1600 | |
|
1601 | 0 | FD_ZERO(&listenfds); |
1602 | 0 | maxfd = pr_ipbind_listen(&listenfds); |
1603 | | |
1604 | | /* Monitor children pipes */ |
1605 | 0 | maxfd = semaphore_fds(&listenfds, maxfd); |
1606 | | |
1607 | | /* Check for ftp shutdown message file */ |
1608 | 0 | res = check_shutmsg(permanent_pool, PR_SHUTMSG_PATH, &shut, &deny, &disc, |
1609 | 0 | shutmsg, sizeof(shutmsg)); |
1610 | 0 | if (res == 1) { |
1611 | 0 | if (shutting_down == FALSE) { |
1612 | 0 | disc_children(); |
1613 | 0 | } |
1614 | 0 | shutting_down = TRUE; |
1615 | |
|
1616 | 0 | } else { |
1617 | 0 | shutting_down = FALSE; |
1618 | 0 | deny = disc = (time_t) 0; |
1619 | 0 | } |
1620 | |
|
1621 | 0 | if (shutting_down == TRUE) { |
1622 | 0 | tv.tv_sec = 5L; |
1623 | 0 | tv.tv_usec = 0L; |
1624 | |
|
1625 | 0 | } else { |
1626 | 0 | tv.tv_sec = PR_TUNABLE_SELECT_TIMEOUT; |
1627 | 0 | tv.tv_usec = 0L; |
1628 | 0 | } |
1629 | | |
1630 | | /* If running (a flag signaling whether proftpd is just starting up) |
1631 | | * AND shutting_down (a flag signalling the present of /etc/shutmsg) are |
1632 | | * true, then log an error stating this -- but don't stop the server. |
1633 | | */ |
1634 | 0 | if (shutting_down == TRUE && |
1635 | 0 | !running) { |
1636 | | |
1637 | | /* Check the value of the deny time_t struct w/ the current time. |
1638 | | * If the deny time has passed, log that all incoming connections |
1639 | | * will be refused. If not, note the date at which they will be |
1640 | | * refused in the future. |
1641 | | */ |
1642 | 0 | time_t now = time(NULL); |
1643 | |
|
1644 | 0 | if (difftime(deny, now) < 0.0) { |
1645 | 0 | pr_log_pri(PR_LOG_WARNING, PR_SHUTMSG_PATH |
1646 | 0 | " present: all incoming connections will be refused"); |
1647 | |
|
1648 | 0 | } else { |
1649 | 0 | #if defined(HAVE_CTIME_R) |
1650 | 0 | char deny_ts[32]; |
1651 | |
|
1652 | 0 | memset(deny_ts, '\0', sizeof(deny_ts)); |
1653 | 0 | (void) ctime_r(&deny, deny_ts); |
1654 | | #else |
1655 | | char *deny_ts = NULL; |
1656 | | deny_ts = ctime(&deny); |
1657 | | #endif /* HAVE_CTIME_R */ |
1658 | |
|
1659 | 0 | pr_log_pri(PR_LOG_NOTICE, |
1660 | 0 | PR_SHUTMSG_PATH " present: incoming connections " |
1661 | 0 | "will be denied starting %s", CHOP(deny_ts)); |
1662 | 0 | } |
1663 | 0 | } |
1664 | |
|
1665 | 0 | running = 1; |
1666 | 0 | xerrno = errno = 0; |
1667 | |
|
1668 | 0 | PR_DEVEL_CLOCK(i = select(maxfd + 1, &listenfds, NULL, NULL, &tv)); |
1669 | 0 | xerrno = errno; |
1670 | |
|
1671 | 0 | if (i < 0 && |
1672 | 0 | xerrno == EINTR) { |
1673 | 0 | errno = xerrno; |
1674 | 0 | pr_signals_handle_without_delay(); |
1675 | | |
1676 | | /* We handled our signal; clear errno. */ |
1677 | 0 | xerrno = errno = 0; |
1678 | 0 | continue; |
1679 | 0 | } |
1680 | | |
1681 | 0 | if (have_dead_child) { |
1682 | 0 | sigset_t sig_set; |
1683 | |
|
1684 | 0 | sigemptyset(&sig_set); |
1685 | 0 | sigaddset(&sig_set, SIGCHLD); |
1686 | 0 | sigaddset(&sig_set, SIGTERM); |
1687 | 0 | pr_alarms_block(); |
1688 | 0 | if (sigprocmask(SIG_BLOCK, &sig_set, NULL) < 0) { |
1689 | 0 | pr_log_pri(PR_LOG_NOTICE, |
1690 | 0 | "unable to block signal set: %s", strerror(errno)); |
1691 | 0 | } |
1692 | |
|
1693 | 0 | have_dead_child = FALSE; |
1694 | 0 | child_update(); |
1695 | |
|
1696 | 0 | if (sigprocmask(SIG_UNBLOCK, &sig_set, NULL) < 0) { |
1697 | 0 | pr_log_pri(PR_LOG_NOTICE, |
1698 | 0 | "unable to unblock signal set: %s", strerror(errno)); |
1699 | 0 | } |
1700 | |
|
1701 | 0 | pr_alarms_unblock(); |
1702 | 0 | } |
1703 | |
|
1704 | 0 | if (i == -1) { |
1705 | 0 | time_t this_error; |
1706 | |
|
1707 | 0 | time(&this_error); |
1708 | |
|
1709 | 0 | if ((this_error - last_error) <= 5 && err_count++ > 10) { |
1710 | 0 | pr_log_pri(PR_LOG_ERR, "fatal: select(2) failing repeatedly, shutting " |
1711 | 0 | "down"); |
1712 | 0 | exit(1); |
1713 | |
|
1714 | 0 | } else if ((this_error - last_error) > 5) { |
1715 | 0 | last_error = this_error; |
1716 | 0 | err_count = 0; |
1717 | 0 | } |
1718 | | |
1719 | 0 | pr_log_pri(PR_LOG_WARNING, "select(2) failed in daemon_loop(): %s", |
1720 | 0 | strerror(xerrno)); |
1721 | 0 | } |
1722 | | |
1723 | 0 | if (i == 0) { |
1724 | 0 | continue; |
1725 | 0 | } |
1726 | | |
1727 | | /* Reset the connection counter. Take into account this current |
1728 | | * connection, which does not (yet) have an entry in the child list. |
1729 | | */ |
1730 | 0 | nconnects = 1UL; |
1731 | | |
1732 | | /* See if child semaphore pipes have signaled */ |
1733 | 0 | if (child_count()) { |
1734 | 0 | pr_child_t *ch; |
1735 | 0 | time_t now = time(NULL); |
1736 | |
|
1737 | 0 | for (ch = child_get(NULL); ch; ch = child_get(ch)) { |
1738 | 0 | if (ch->ch_pipefd != -1 && |
1739 | 0 | FD_ISSET(ch->ch_pipefd, &listenfds)) { |
1740 | 0 | (void) close(ch->ch_pipefd); |
1741 | 0 | ch->ch_pipefd = -1; |
1742 | 0 | } |
1743 | | |
1744 | | /* While we're looking, tally up the number of children forked in |
1745 | | * the past interval. |
1746 | | */ |
1747 | 0 | if (ch->ch_when >= (time_t) (now - (long) max_connect_interval)) { |
1748 | 0 | nconnects++; |
1749 | 0 | } |
1750 | 0 | } |
1751 | 0 | } |
1752 | |
|
1753 | 0 | pr_signals_handle_without_delay(); |
1754 | |
|
1755 | 0 | if (i < 0) { |
1756 | 0 | continue; |
1757 | 0 | } |
1758 | | |
1759 | | /* Accept the connection. */ |
1760 | 0 | listen_conn = pr_ipbind_accept_conn(&listenfds, &fd); |
1761 | | |
1762 | | /* Fork off servers to handle each connection our job is to get back to |
1763 | | * answering connections ASAP, so leave the work of determining which |
1764 | | * server the connection is for to our child. |
1765 | | */ |
1766 | |
|
1767 | 0 | if (listen_conn != NULL) { |
1768 | | |
1769 | | /* Check for exceeded MaxInstances. */ |
1770 | 0 | if (ServerMaxInstances > 0 && |
1771 | 0 | child_count() >= ServerMaxInstances) { |
1772 | 0 | pr_event_generate("core.max-instances", NULL); |
1773 | |
|
1774 | 0 | pr_log_pri(PR_LOG_WARNING, |
1775 | 0 | "MaxInstances (%lu) reached, new connection denied", |
1776 | 0 | ServerMaxInstances); |
1777 | 0 | close(fd); |
1778 | | |
1779 | | /* Check for exceeded MaxConnectionRate. */ |
1780 | 0 | } else if (max_connects && (nconnects > max_connects)) { |
1781 | 0 | pr_event_generate("core.max-connection-rate", NULL); |
1782 | |
|
1783 | 0 | pr_log_pri(PR_LOG_WARNING, |
1784 | 0 | "MaxConnectionRate (%lu/%u secs) reached, new connection denied", |
1785 | 0 | max_connects, max_connect_interval); |
1786 | 0 | close(fd); |
1787 | | |
1788 | | /* Fork off a child to handle the connection. */ |
1789 | 0 | } else { |
1790 | 0 | PR_DEVEL_CLOCK(fork_server(fd, listen_conn, no_forking)); |
1791 | 0 | } |
1792 | 0 | } |
1793 | | #ifdef PR_DEVEL_NO_DAEMON |
1794 | | /* Do not continue the while() loop here if not daemonizing. */ |
1795 | | break; |
1796 | | #endif /* PR_DEVEL_NO_DAEMON */ |
1797 | 0 | } |
1798 | 0 | } |
1799 | | |
1800 | | /* Returns 1 for the background daemon process, 0 for the foreground process, |
1801 | | * and -1 if there was an error. |
1802 | | */ |
1803 | 0 | static int daemonize(void) { |
1804 | | #ifndef HAVE_SETSID |
1805 | | int ttyfd; |
1806 | | #endif |
1807 | 0 | pid_t pid; |
1808 | | |
1809 | | /* Fork off and have parent exit. |
1810 | | */ |
1811 | 0 | pid = fork(); |
1812 | 0 | switch (pid) { |
1813 | 0 | case -1: |
1814 | 0 | perror("fork(2) error"); |
1815 | 0 | return -1; |
1816 | | |
1817 | 0 | case 0: |
1818 | | /* Child process; keep going. */ |
1819 | 0 | break; |
1820 | | |
1821 | 0 | default: |
1822 | | /* Parent process; we're done. */ |
1823 | 0 | pr_log_pri(PR_LOG_DEBUG, "forked daemon process (PID %lu)", |
1824 | 0 | (unsigned long) pid); |
1825 | 0 | return 0; |
1826 | 0 | } |
1827 | | |
1828 | 0 | #ifdef HAVE_SETSID |
1829 | | /* setsid() is the preferred way to disassociate from the |
1830 | | * controlling terminal |
1831 | | */ |
1832 | 0 | setsid(); |
1833 | | #else |
1834 | | /* Open /dev/tty to access our controlling tty (if any) */ |
1835 | | if ((ttyfd = open("/dev/tty", O_RDWR)) != -1) { |
1836 | | if (ioctl(ttyfd, TIOCNOTTY, NULL) == -1) { |
1837 | | perror("ioctl"); |
1838 | | exit(1); |
1839 | | } |
1840 | | |
1841 | | close(ttyfd); |
1842 | | } |
1843 | | #endif /* HAVE_SETSID */ |
1844 | | |
1845 | | /* Close the three big boys */ |
1846 | 0 | close(fileno(stdin)); |
1847 | 0 | close(fileno(stdout)); |
1848 | 0 | close(fileno(stderr)); |
1849 | | |
1850 | | /* Portable way to prevent re-acquiring a tty in the future */ |
1851 | |
|
1852 | 0 | #ifdef HAVE_SETPGID |
1853 | 0 | setpgid(0, getpid()); |
1854 | | #else |
1855 | | # ifdef SETPGRP_VOID |
1856 | | setpgrp(); |
1857 | | # else |
1858 | | setpgrp(0, getpid()); |
1859 | | # endif |
1860 | | #endif |
1861 | | |
1862 | | /* Reset the cached "master PID" value to that of the daemon process; |
1863 | | * there are places in the code which check this value to see if they |
1864 | | * are the daemon process, e.g. at shutdown. |
1865 | | */ |
1866 | 0 | mpid = getpid(); |
1867 | |
|
1868 | 0 | pr_fsio_chdir("/", 0); |
1869 | 0 | return 1; |
1870 | 0 | } |
1871 | | |
1872 | 0 | static void inetd_main(void) { |
1873 | 0 | int res = 0; |
1874 | | |
1875 | | /* Make sure the scoreboard file exists. */ |
1876 | 0 | PRIVS_ROOT |
1877 | 0 | res = pr_open_scoreboard(O_RDWR); |
1878 | 0 | if (res < 0) { |
1879 | 0 | PRIVS_RELINQUISH |
1880 | |
|
1881 | 0 | switch (res) { |
1882 | 0 | case PR_SCORE_ERR_BAD_MAGIC: |
1883 | 0 | pr_log_pri(PR_LOG_ERR, "error opening scoreboard: bad/corrupted file"); |
1884 | 0 | return; |
1885 | | |
1886 | 0 | case PR_SCORE_ERR_OLDER_VERSION: |
1887 | 0 | case PR_SCORE_ERR_NEWER_VERSION: |
1888 | 0 | pr_log_pri(PR_LOG_ERR, "error opening scoreboard: wrong version, " |
1889 | 0 | "writing new scoreboard"); |
1890 | | |
1891 | | /* Delete the scoreboard, then open it again. */ |
1892 | 0 | PRIVS_ROOT |
1893 | 0 | pr_delete_scoreboard(); |
1894 | 0 | if (pr_open_scoreboard(O_RDWR) < 0) { |
1895 | 0 | int xerrno = errno; |
1896 | |
|
1897 | 0 | PRIVS_RELINQUISH |
1898 | 0 | pr_log_pri(PR_LOG_ERR, "error opening scoreboard: %s", |
1899 | 0 | strerror(xerrno)); |
1900 | 0 | return; |
1901 | 0 | } |
1902 | 0 | break; |
1903 | | |
1904 | 0 | default: |
1905 | 0 | pr_log_pri(PR_LOG_ERR, "error opening scoreboard: %s", |
1906 | 0 | strerror(errno)); |
1907 | 0 | return; |
1908 | 0 | } |
1909 | 0 | } |
1910 | 0 | PRIVS_RELINQUISH |
1911 | 0 | pr_close_scoreboard(FALSE); |
1912 | |
|
1913 | 0 | pr_event_generate("core.startup", NULL); |
1914 | |
|
1915 | 0 | init_bindings(); |
1916 | | |
1917 | | /* Check our shutdown status */ |
1918 | 0 | if (check_shutmsg(permanent_pool, PR_SHUTMSG_PATH, &shut, &deny, &disc, |
1919 | 0 | shutmsg, sizeof(shutmsg)) == 1) { |
1920 | 0 | shutting_down = TRUE; |
1921 | 0 | } |
1922 | | |
1923 | | /* Finally, call right into fork_server() to start servicing the |
1924 | | * connection immediately. |
1925 | | */ |
1926 | 0 | fork_server(STDIN_FILENO, main_server->listen, TRUE); |
1927 | 0 | } |
1928 | | |
1929 | 0 | static void standalone_main(void) { |
1930 | 0 | int res = 0; |
1931 | |
|
1932 | 0 | if (nodaemon) { |
1933 | 0 | log_stderr(quiet ? FALSE : TRUE); |
1934 | 0 | close(fileno(stdin)); |
1935 | 0 | close(fileno(stdout)); |
1936 | |
|
1937 | 0 | } else { |
1938 | 0 | log_stderr(FALSE); |
1939 | 0 | res = daemonize(); |
1940 | 0 | if (res != 1) { |
1941 | | /* We're either the foreground process, or there was an error. Either |
1942 | | * way, we're done. |
1943 | | */ |
1944 | 0 | return; |
1945 | 0 | } |
1946 | 0 | } |
1947 | | |
1948 | 0 | PRIVS_ROOT |
1949 | 0 | pr_delete_scoreboard(); |
1950 | 0 | res = pr_open_scoreboard(O_RDWR); |
1951 | 0 | if (res < 0) { |
1952 | 0 | PRIVS_RELINQUISH |
1953 | |
|
1954 | 0 | switch (res) { |
1955 | 0 | case PR_SCORE_ERR_BAD_MAGIC: |
1956 | 0 | pr_log_pri(PR_LOG_ERR, |
1957 | 0 | "error opening scoreboard: bad/corrupted file"); |
1958 | 0 | return; |
1959 | | |
1960 | 0 | case PR_SCORE_ERR_OLDER_VERSION: |
1961 | 0 | pr_log_pri(PR_LOG_ERR, |
1962 | 0 | "error opening scoreboard: bad version (too old)"); |
1963 | 0 | return; |
1964 | | |
1965 | 0 | case PR_SCORE_ERR_NEWER_VERSION: |
1966 | 0 | pr_log_pri(PR_LOG_ERR, |
1967 | 0 | "error opening scoreboard: bad version (too new)"); |
1968 | 0 | return; |
1969 | | |
1970 | 0 | default: |
1971 | 0 | pr_log_pri(PR_LOG_ERR, "error opening scoreboard: %s", strerror(errno)); |
1972 | 0 | return; |
1973 | 0 | } |
1974 | 0 | } |
1975 | 0 | PRIVS_RELINQUISH |
1976 | 0 | pr_close_scoreboard(TRUE); |
1977 | |
|
1978 | 0 | pr_event_generate("core.startup", NULL); |
1979 | |
|
1980 | 0 | init_bindings(); |
1981 | |
|
1982 | 0 | if (pr_pidfile_write() < 0) { |
1983 | 0 | pr_log_pri(PR_LOG_ERR, "error writing PidFile '%s': %s", pr_pidfile_get(), |
1984 | 0 | strerror(errno)); |
1985 | 0 | exit(1); |
1986 | 0 | } |
1987 | | |
1988 | 0 | pr_log_pri(PR_LOG_NOTICE, "ProFTPD %s (built %s) standalone mode STARTUP", |
1989 | 0 | PROFTPD_VERSION_TEXT " " PR_STATUS, BUILD_STAMP); |
1990 | |
|
1991 | 0 | daemon_loop(); |
1992 | 0 | } |
1993 | | |
1994 | 0 | static int conftab_cmp(const void *a, const void *b) { |
1995 | 0 | const conftable *tab1, *tab2; |
1996 | |
|
1997 | 0 | tab1 = *((conftable **) a); |
1998 | 0 | tab2 = *((conftable **) b); |
1999 | 0 | return strcmp(tab1->directive, tab2->directive); |
2000 | 0 | } |
2001 | | |
2002 | | /* Similar to the `get_all_directives` function in src/parser.c, except |
2003 | | * that we sort the directives, and display their associated/implementing |
2004 | | * modules. |
2005 | | */ |
2006 | 0 | static void list_directives(void) { |
2007 | 0 | register unsigned int i; |
2008 | 0 | pool *tmp_pool; |
2009 | 0 | array_header *directives; |
2010 | 0 | conftable *tab; |
2011 | 0 | int idx; |
2012 | 0 | unsigned int hash; |
2013 | |
|
2014 | 0 | tmp_pool = make_sub_pool(permanent_pool); |
2015 | 0 | directives = make_array(tmp_pool, 1, sizeof(conftable **)); |
2016 | |
|
2017 | 0 | idx = -1; |
2018 | 0 | hash = 0; |
2019 | 0 | tab = pr_stash_get_symbol2(PR_SYM_CONF, NULL, NULL, &idx, &hash); |
2020 | 0 | while (idx != -1) { |
2021 | 0 | pr_signals_handle(); |
2022 | |
|
2023 | 0 | if (tab != NULL) { |
2024 | 0 | *((conftable **) push_array(directives)) = tab; |
2025 | |
|
2026 | 0 | } else { |
2027 | 0 | idx++; |
2028 | 0 | } |
2029 | |
|
2030 | 0 | tab = pr_stash_get_symbol2(PR_SYM_CONF, NULL, tab, &idx, &hash); |
2031 | 0 | } |
2032 | |
|
2033 | 0 | qsort((void *) directives->elts, directives->nelts, sizeof(conftable **), |
2034 | 0 | conftab_cmp); |
2035 | |
|
2036 | 0 | printf("Configuration Directives:\n"); |
2037 | 0 | for (i = 0; i < directives->nelts; i++) { |
2038 | 0 | conftable *conftab; |
2039 | |
|
2040 | 0 | conftab = ((conftable **) directives->elts)[i]; |
2041 | 0 | printf(" %s (from mod_%s)\n", conftab->directive, conftab->m->name); |
2042 | 0 | } |
2043 | |
|
2044 | 0 | destroy_pool(tmp_pool); |
2045 | 0 | } |
2046 | | |
2047 | | extern char *optarg; |
2048 | | extern int optind, opterr, optopt; |
2049 | | |
2050 | | #if defined(HAVE_GETOPT_LONG) |
2051 | | static struct option opts[] = { |
2052 | | { "nocollision", 0, NULL, 'N' }, |
2053 | | { "nodaemon", 0, NULL, 'n' }, |
2054 | | { "quiet", 0, NULL, 'q' }, |
2055 | | { "debug", 1, NULL, 'd' }, |
2056 | | { "define", 1, NULL, 'D' }, |
2057 | | { "config", 1, NULL, 'c' }, |
2058 | | { "persistent", 1, NULL, 'p' }, |
2059 | | { "list", 0, NULL, 'l' }, |
2060 | | { "version", 0, NULL, 'v' }, |
2061 | | { "settings", 0, NULL, 'V' }, |
2062 | | { "version-status", 0, NULL, 1 }, |
2063 | | { "configtest", 0, NULL, 't' }, |
2064 | | { "help", 0, NULL, 'h' }, |
2065 | | { "ipv4", 0, NULL, '4' }, |
2066 | | { "ipv6", 0, NULL, '6' }, |
2067 | | { NULL, 0, NULL, 0 } |
2068 | | }; |
2069 | | #endif /* HAVE_GETOPT_LONG */ |
2070 | | |
2071 | | /* If there is an /etc/os-release file, display its contents; see: |
2072 | | * https://www.freedesktop.org/software/systemd/man/os-release.html |
2073 | | */ |
2074 | 0 | static void show_os_release(void) { |
2075 | 0 | const char *os_release_path = "/etc/os-release"; |
2076 | 0 | FILE *fh; |
2077 | 0 | char *line = NULL; |
2078 | 0 | size_t linelen = 0; |
2079 | 0 | ssize_t nread = 0; |
2080 | |
|
2081 | 0 | fh = fopen(os_release_path, "r"); |
2082 | 0 | if (fh == NULL) { |
2083 | 0 | return; |
2084 | 0 | } |
2085 | | |
2086 | 0 | printf("%s", " OS/Release:\n"); |
2087 | |
|
2088 | 0 | nread = getline(&line, &linelen, fh); |
2089 | 0 | while (nread >= 0) { |
2090 | 0 | int skip_line = FALSE; |
2091 | |
|
2092 | 0 | pr_signals_handle(); |
2093 | | |
2094 | | /* Skip any lines containing uninteresting info. */ |
2095 | 0 | if (strstr(line, "COLOR") != NULL || |
2096 | 0 | strstr(line, "LOGO") != NULL || |
2097 | 0 | strstr(line, "_URL") != NULL) { |
2098 | 0 | skip_line = TRUE; |
2099 | 0 | } |
2100 | |
|
2101 | 0 | if (skip_line == TRUE) { |
2102 | 0 | nread = getline(&line, &linelen, fh); |
2103 | 0 | continue; |
2104 | 0 | } |
2105 | | |
2106 | 0 | printf(" %s", line); |
2107 | 0 | nread = getline(&line, &linelen, fh); |
2108 | 0 | } |
2109 | |
|
2110 | 0 | if (line != NULL) { |
2111 | 0 | free(line); |
2112 | 0 | } |
2113 | |
|
2114 | 0 | (void) fclose(fh); |
2115 | 0 | } |
2116 | | |
2117 | 0 | static void show_settings(void) { |
2118 | 0 | #if defined(HAVE_UNAME) |
2119 | 0 | int res; |
2120 | 0 | struct utsname uts; |
2121 | 0 | #endif /* !HAVE_UNAME */ |
2122 | |
|
2123 | 0 | printf("%s", "Compile-time Settings:\n"); |
2124 | 0 | printf("%s", " Version: " PROFTPD_VERSION_TEXT " " PR_STATUS "\n"); |
2125 | |
|
2126 | 0 | #if defined(HAVE_UNAME) |
2127 | | /* We use uname(2) to get the 'machine', which will tell us whether |
2128 | | * we're a 32- or 64-bit machine. |
2129 | | */ |
2130 | 0 | res = uname(&uts); |
2131 | 0 | if (res < 0) { |
2132 | 0 | printf("%s", " Platform: " PR_PLATFORM " [unavailable]\n"); |
2133 | |
|
2134 | 0 | } else { |
2135 | 0 | printf(" Platform: " PR_PLATFORM " [%s %s %s]\n", uts.sysname, |
2136 | 0 | uts.release, uts.machine); |
2137 | 0 | } |
2138 | | #else |
2139 | | printf("%s", " Platform: " PR_PLATFORM " [unknown]\n"); |
2140 | | #endif /* !HAVE_UNAME */ |
2141 | |
|
2142 | 0 | show_os_release(); |
2143 | |
|
2144 | 0 | printf("%s", " Built: " BUILD_STAMP "\n"); |
2145 | 0 | printf("%s", " Built With:\n configure " PR_BUILD_OPTS "\n\n"); |
2146 | |
|
2147 | 0 | printf("%s", " CFLAGS: " PR_BUILD_CFLAGS "\n"); |
2148 | 0 | printf("%s", " LDFLAGS: " PR_BUILD_LDFLAGS "\n"); |
2149 | 0 | printf("%s", " LIBS: " PR_BUILD_LIBS "\n"); |
2150 | | |
2151 | | /* Files/paths */ |
2152 | 0 | printf("%s", "\n Files:\n"); |
2153 | 0 | printf("%s", " Configuration File:\n"); |
2154 | 0 | printf("%s", " " PR_CONFIG_FILE_PATH "\n"); |
2155 | 0 | printf("%s", " Pid File:\n"); |
2156 | 0 | printf("%s", " " PR_PID_FILE_PATH "\n"); |
2157 | 0 | printf("%s", " Scoreboard File:\n"); |
2158 | 0 | printf("%s", " " PR_RUN_DIR "/proftpd.scoreboard\n"); |
2159 | | #ifdef PR_USE_DSO |
2160 | | printf("%s", " Header Directory:\n"); |
2161 | | printf("%s", " " PR_INCLUDE_DIR "/proftpd\n"); |
2162 | | printf("%s", " Shared Module Directory:\n"); |
2163 | | printf("%s", " " PR_LIBEXEC_DIR "\n"); |
2164 | | #endif /* PR_USE_DSO */ |
2165 | | |
2166 | | /* Informational */ |
2167 | 0 | printf("%s", "\n Info:\n"); |
2168 | 0 | #if SIZEOF_UID_T == SIZEOF_INT |
2169 | 0 | printf(" + Max supported UID: %u\n", UINT_MAX); |
2170 | | #elif SIZEOF_UID_T == SIZEOF_LONG |
2171 | | printf(" + Max supported UID: %lu\n", ULONG_MAX); |
2172 | | #elif SIZEOF_UID_T == SIZEOF_LONG_LONG |
2173 | | printf(" + Max supported UID: %llu\n", ULLONG_MAX); |
2174 | | #endif |
2175 | |
|
2176 | 0 | #if SIZEOF_GID_T == SIZEOF_INT |
2177 | 0 | printf(" + Max supported GID: %u\n", UINT_MAX); |
2178 | | #elif SIZEOF_GID_T == SIZEOF_LONG |
2179 | | printf(" + Max supported GID: %lu\n", ULONG_MAX); |
2180 | | #elif SIZEOF_GID_T == SIZEOF_LONG_LONG |
2181 | | printf(" + Max supported GID: %llu\n", ULLONG_MAX); |
2182 | | #endif |
2183 | | |
2184 | | /* Feature settings */ |
2185 | 0 | printf("%s", "\n Features:\n"); |
2186 | | #ifdef PR_USE_AUTO_SHADOW |
2187 | | printf("%s", " + Autoshadow support\n"); |
2188 | | #else |
2189 | 0 | printf("%s", " - Autoshadow support\n"); |
2190 | 0 | #endif /* PR_USE_AUTO_SHADOW */ |
2191 | |
|
2192 | 0 | #ifdef PR_USE_CTRLS |
2193 | 0 | printf("%s", " + Controls support\n"); |
2194 | | #else |
2195 | | printf("%s", " - Controls support\n"); |
2196 | | #endif /* PR_USE_CTRLS */ |
2197 | |
|
2198 | | #if defined(PR_USE_CURSES) && defined(HAVE_LIBCURSES) |
2199 | | printf("%s", " + curses support\n"); |
2200 | | #else |
2201 | 0 | printf("%s", " - curses support\n"); |
2202 | 0 | #endif /* PR_USE_CURSES && HAVE_LIBCURSES */ |
2203 | |
|
2204 | | #ifdef PR_USE_DEVEL |
2205 | | printf("%s", " + Developer support\n"); |
2206 | | #else |
2207 | 0 | printf("%s", " - Developer support\n"); |
2208 | 0 | #endif /* PR_USE_DEVEL */ |
2209 | |
|
2210 | | #ifdef PR_USE_DSO |
2211 | | printf("%s", " + DSO support\n"); |
2212 | | #else |
2213 | 0 | printf("%s", " - DSO support\n"); |
2214 | 0 | #endif /* PR_USE_DSO */ |
2215 | |
|
2216 | 0 | #ifdef PR_USE_IPV6 |
2217 | 0 | printf("%s", " + IPv6 support\n"); |
2218 | | #else |
2219 | | printf("%s", " - IPv6 support\n"); |
2220 | | #endif /* PR_USE_IPV6 */ |
2221 | |
|
2222 | 0 | #ifdef PR_USE_LARGEFILES |
2223 | 0 | printf("%s", " + Largefile support\n"); |
2224 | | #else |
2225 | | printf("%s", " - Largefile support\n"); |
2226 | | #endif /* PR_USE_LARGEFILES */ |
2227 | |
|
2228 | | #ifdef PR_USE_LASTLOG |
2229 | | printf("%s", " + Lastlog support\n"); |
2230 | | #else |
2231 | 0 | printf("%s", " - Lastlog support\n"); |
2232 | 0 | #endif /* PR_USE_LASTLOG */ |
2233 | |
|
2234 | | #ifdef PR_USE_MEMCACHE |
2235 | | printf("%s", " + Memcache support\n"); |
2236 | | #else |
2237 | 0 | printf("%s", " - Memcache support\n"); |
2238 | 0 | #endif /* PR_USE_MEMCACHE */ |
2239 | |
|
2240 | | #if defined(PR_USE_NCURSESW) && defined(HAVE_LIBNCURSESW) |
2241 | | printf("%s", " + ncursesw support\n"); |
2242 | | #elif defined(PR_USE_NCURSES) && defined(HAVE_LIBNCURSES) |
2243 | | printf("%s", " + ncurses support\n"); |
2244 | | #else |
2245 | 0 | printf("%s", " - ncurses support\n"); |
2246 | 0 | #endif |
2247 | |
|
2248 | | #ifdef PR_USE_NLS |
2249 | | printf("%s", " + NLS support\n"); |
2250 | | #else |
2251 | 0 | printf("%s", " - NLS support\n"); |
2252 | 0 | #endif /* PR_USE_NLS */ |
2253 | |
|
2254 | | #ifdef PR_USE_OPENSSL |
2255 | | # ifdef PR_USE_OPENSSL_FIPS |
2256 | | printf(" + OpenSSL support (%s, FIPS enabled)\n", OPENSSL_VERSION_TEXT); |
2257 | | # else |
2258 | | # ifdef LIBRESSL_VERSION_NUMBER |
2259 | | printf(" + OpenSSL support (%s, LibreSSL)\n", OPENSSL_VERSION_TEXT); |
2260 | | # else |
2261 | | printf(" + OpenSSL support (%s)\n", OPENSSL_VERSION_TEXT); |
2262 | | # endif /* Have LibreSSL */ |
2263 | | # endif /* PR_USE_OPENSSL_FIPS */ |
2264 | | #else |
2265 | 0 | printf("%s", " - OpenSSL support\n"); |
2266 | 0 | #endif /* PR_USE_OPENSSL */ |
2267 | |
|
2268 | | #ifdef PR_USE_PCRE |
2269 | | printf("%s", " + PCRE support\n"); |
2270 | | #else |
2271 | 0 | printf("%s", " - PCRE support\n"); |
2272 | 0 | #endif /* PR_USE_PCRE */ |
2273 | |
|
2274 | | #ifdef PR_USE_PCRE2 |
2275 | | printf("%s", " + PCRE2 support\n"); |
2276 | | #else |
2277 | 0 | printf("%s", " - PCRE2 support\n"); |
2278 | 0 | #endif /* PR_USE_PCRE2 */ |
2279 | |
|
2280 | | #ifdef PR_USE_FACL |
2281 | | printf("%s", " + POSIX ACL support\n"); |
2282 | | #else |
2283 | 0 | printf("%s", " - POSIX ACL support\n"); |
2284 | 0 | #endif /* PR_USE_FACL */ |
2285 | |
|
2286 | | #ifdef PR_USE_REDIS |
2287 | | printf("%s", " + Redis support\n"); |
2288 | | #else |
2289 | 0 | printf("%s", " - Redis support\n"); |
2290 | 0 | #endif /* PR_USE_REDIS */ |
2291 | |
|
2292 | 0 | #ifdef PR_USE_SENDFILE |
2293 | 0 | printf("%s", " + Sendfile support\n"); |
2294 | | #else |
2295 | | printf("%s", " - Sendfile support\n"); |
2296 | | #endif /* PR_USE_SENDFILE */ |
2297 | |
|
2298 | 0 | #ifdef PR_USE_SHADOW |
2299 | 0 | printf("%s", " + Shadow file support\n"); |
2300 | | #else |
2301 | | printf("%s", " - Shadow file support\n"); |
2302 | | #endif /* PR_USE_SHADOW */ |
2303 | |
|
2304 | | #ifdef PR_USE_SODIUM |
2305 | | printf("%s", " + Sodium support\n"); |
2306 | | #else |
2307 | 0 | printf("%s", " - Sodium support\n"); |
2308 | 0 | #endif /* PR_USE_SODIUM */ |
2309 | |
|
2310 | 0 | #ifdef PR_USE_TRACE |
2311 | 0 | printf("%s", " + Trace support\n"); |
2312 | | #else |
2313 | | printf("%s", " - Trace support\n"); |
2314 | | #endif /* PR_USE_TRACE */ |
2315 | |
|
2316 | 0 | #ifdef PR_USE_XATTR |
2317 | 0 | printf("%s", " + xattr support\n"); |
2318 | | #else |
2319 | | printf("%s", " - xattr support\n"); |
2320 | | #endif /* PR_USE_XATTR */ |
2321 | | |
2322 | | /* Tunable settings */ |
2323 | 0 | printf("%s", "\n Tunable Options:\n"); |
2324 | 0 | printf(" PR_TUNABLE_BUFFER_SIZE = %u\n", PR_TUNABLE_BUFFER_SIZE); |
2325 | 0 | printf(" PR_TUNABLE_DEFAULT_RCVBUFSZ = %u\n", PR_TUNABLE_DEFAULT_RCVBUFSZ); |
2326 | 0 | printf(" PR_TUNABLE_DEFAULT_SNDBUFSZ = %u\n", PR_TUNABLE_DEFAULT_SNDBUFSZ); |
2327 | 0 | printf(" PR_TUNABLE_ENV_MAX = %u\n", PR_TUNABLE_ENV_MAX); |
2328 | 0 | printf(" PR_TUNABLE_GLOBBING_MAX_MATCHES = %lu\n", PR_TUNABLE_GLOBBING_MAX_MATCHES); |
2329 | 0 | printf(" PR_TUNABLE_GLOBBING_MAX_RECURSION = %u\n", PR_TUNABLE_GLOBBING_MAX_RECURSION); |
2330 | 0 | printf(" PR_TUNABLE_HASH_TABLE_SIZE = %u\n", PR_TUNABLE_HASH_TABLE_SIZE); |
2331 | 0 | printf(" PR_TUNABLE_LOGIN_MAX = %u\n", PR_TUNABLE_LOGIN_MAX); |
2332 | 0 | printf(" PR_TUNABLE_NEW_POOL_SIZE = %u\n", PR_TUNABLE_NEW_POOL_SIZE); |
2333 | 0 | printf(" PR_TUNABLE_PATH_MAX = %u\n", PR_TUNABLE_PATH_MAX); |
2334 | 0 | printf(" PR_TUNABLE_SCOREBOARD_BUFFER_SIZE = %u\n", |
2335 | 0 | PR_TUNABLE_SCOREBOARD_BUFFER_SIZE); |
2336 | 0 | printf(" PR_TUNABLE_SCOREBOARD_SCRUB_TIMER = %u\n", |
2337 | 0 | PR_TUNABLE_SCOREBOARD_SCRUB_TIMER); |
2338 | 0 | printf(" PR_TUNABLE_SELECT_TIMEOUT = %u\n", PR_TUNABLE_SELECT_TIMEOUT); |
2339 | 0 | printf(" PR_TUNABLE_TIMEOUTIDENT = %u\n", PR_TUNABLE_TIMEOUTIDENT); |
2340 | 0 | printf(" PR_TUNABLE_TIMEOUTIDLE = %u\n", PR_TUNABLE_TIMEOUTIDLE); |
2341 | 0 | printf(" PR_TUNABLE_TIMEOUTLINGER = %u\n", PR_TUNABLE_TIMEOUTLINGER); |
2342 | 0 | printf(" PR_TUNABLE_TIMEOUTLOGIN = %u\n", PR_TUNABLE_TIMEOUTLOGIN); |
2343 | 0 | printf(" PR_TUNABLE_TIMEOUTNOXFER = %u\n", PR_TUNABLE_TIMEOUTNOXFER); |
2344 | 0 | printf(" PR_TUNABLE_TIMEOUTSTALLED = %u\n", PR_TUNABLE_TIMEOUTSTALLED); |
2345 | 0 | printf(" PR_TUNABLE_XFER_SCOREBOARD_UPDATES = %u\n\n", |
2346 | 0 | PR_TUNABLE_XFER_SCOREBOARD_UPDATES); |
2347 | 0 | } |
2348 | | |
2349 | | static struct option_help { |
2350 | | const char *long_opt, *short_opt, *desc; |
2351 | | |
2352 | | } opts_help[] = { |
2353 | | { "--help", "-h", |
2354 | | "Display proftpd usage"}, |
2355 | | |
2356 | | { "--nocollision", "-N", |
2357 | | "Disable address/port collision checking" }, |
2358 | | |
2359 | | { "--nodaemon", "-n", |
2360 | | "Disable background daemon mode (and send all output to stderr)" }, |
2361 | | |
2362 | | { "--quiet", "-q", |
2363 | | "Don't send output to stderr when running with -n or --nodaemon" }, |
2364 | | |
2365 | | { "--debug", "-d [level]", |
2366 | | "Set debugging level (0-10, 10 = most debugging)" }, |
2367 | | |
2368 | | { "--define", "-D [definition]", |
2369 | | "Set arbitrary IfDefine definition" }, |
2370 | | |
2371 | | { "--config", "-c [config-file]", |
2372 | | "Specify alternate configuration file" }, |
2373 | | |
2374 | | { "--persistent", "-p [0|1]", |
2375 | | "Enable/disable default persistent passwd support" }, |
2376 | | |
2377 | | { "--list", "-l", |
2378 | | "List all compiled-in modules" }, |
2379 | | |
2380 | | { "--serveraddr", "-S", |
2381 | | "Specify IP address for server config" }, |
2382 | | |
2383 | | { "--configtest", "-t", |
2384 | | "Test the syntax of the specified config" }, |
2385 | | |
2386 | | { "--settings", "-V", |
2387 | | "Print compile-time settings and exit" }, |
2388 | | |
2389 | | { "--version", "-v", |
2390 | | "Print version number and exit" }, |
2391 | | |
2392 | | { "--version-status", "-vv", |
2393 | | "Print extended version information and exit" }, |
2394 | | |
2395 | | { "--nofork", "-X", |
2396 | | "Non-forking debug mode; exits after one session" }, |
2397 | | |
2398 | | { "--ipv4", "-4", |
2399 | | "Support IPv4 connections only" }, |
2400 | | |
2401 | | { "--ipv6", "-6", |
2402 | | "Support IPv6 connections" }, |
2403 | | |
2404 | | { NULL, NULL, NULL } |
2405 | | }; |
2406 | | |
2407 | 0 | static void show_usage(int exit_code) { |
2408 | 0 | struct option_help *h; |
2409 | |
|
2410 | 0 | printf("%s", "usage: proftpd [options]\n"); |
2411 | 0 | for (h = opts_help; h->long_opt; h++) { |
2412 | 0 | #ifdef HAVE_GETOPT_LONG |
2413 | 0 | printf(" %s, %s\n ", h->short_opt, h->long_opt); |
2414 | | #else /* HAVE_GETOPT_LONG */ |
2415 | | printf(" %s\n", h->short_opt); |
2416 | | #endif /* HAVE_GETOPT_LONG */ |
2417 | 0 | printf(" %s\n", h->desc); |
2418 | 0 | } |
2419 | |
|
2420 | 0 | exit(exit_code); |
2421 | 0 | } |
2422 | | |
2423 | 0 | int main2(int argc, char *argv[], char **envp) { |
2424 | 0 | int optc, show_version = 0; |
2425 | 0 | const char *cmdopts = "D:NVc:d:hlnp:qS:tvX46"; |
2426 | 0 | mode_t *main_umask = NULL; |
2427 | 0 | socklen_t peerlen; |
2428 | 0 | struct sockaddr peer; |
2429 | | #if defined(PR_USE_NLS) && defined(HAVE_LOCALE_H) |
2430 | | const char *env_lang = NULL, *env_locale = NULL; |
2431 | | #endif |
2432 | |
|
2433 | | #ifdef HAVE_SET_AUTH_PARAMETERS |
2434 | | (void) set_auth_parameters(argc, argv); |
2435 | | #endif |
2436 | |
|
2437 | 0 | #ifdef HAVE_TZSET |
2438 | | /* Preserve timezone information in jailed environments. |
2439 | | */ |
2440 | 0 | tzset(); |
2441 | 0 | #endif |
2442 | |
|
2443 | 0 | memset(&session, 0, sizeof(session)); |
2444 | |
|
2445 | 0 | pr_fs_close_extra_fds(); |
2446 | 0 | pr_proctitle_init(argc, argv, envp); |
2447 | | |
2448 | | /* Seed rand */ |
2449 | 0 | pr_random_init(); |
2450 | | |
2451 | | /* getpeername() fails if the fd isn't a socket */ |
2452 | 0 | peerlen = sizeof(peer); |
2453 | 0 | memset(&peer, 0, peerlen); |
2454 | 0 | if (getpeername(fileno(stdin), &peer, &peerlen) != -1) { |
2455 | 0 | log_stderr(FALSE); |
2456 | 0 | } |
2457 | | |
2458 | | /* Open the syslog */ |
2459 | 0 | log_opensyslog(NULL); |
2460 | | |
2461 | | /* Initialize the memory subsystem here */ |
2462 | 0 | init_pools(); |
2463 | | |
2464 | | /* Command line options supported: |
2465 | | * |
2466 | | * -D parameter set run-time configuration parameter |
2467 | | * --define parameter |
2468 | | * -V |
2469 | | * --settings report compile-time settings |
2470 | | * -c path set the configuration path |
2471 | | * --config path |
2472 | | * -d n set the debug level |
2473 | | * --debug n |
2474 | | * -q quiet mode; don't log to stderr when not daemonized |
2475 | | * --quiet |
2476 | | * -N disable address/port collision checks |
2477 | | * --nocollision |
2478 | | * -n standalone server does not daemonize, all logging |
2479 | | * --nodaemon redirected to stderr |
2480 | | * -S specify the IP address for the 'server config', |
2481 | | * --serveraddr rather than using DNS on the hostname |
2482 | | * -t syntax check of the configuration file |
2483 | | * --configtest |
2484 | | * -v report version number |
2485 | | * --version |
2486 | | * -X |
2487 | | * --nofork debug/non-fork mode |
2488 | | * -4 support IPv4 connections only |
2489 | | * --ipv4 |
2490 | | * -6 support IPv6 connections |
2491 | | * --ipv6 |
2492 | | */ |
2493 | |
|
2494 | 0 | opterr = 0; |
2495 | 0 | while ((optc = |
2496 | 0 | #ifdef HAVE_GETOPT_LONG |
2497 | 0 | getopt_long(argc, argv, cmdopts, opts, NULL) |
2498 | | #else /* HAVE_GETOPT_LONG */ |
2499 | | getopt(argc, argv, cmdopts) |
2500 | | #endif /* HAVE_GETOPT_LONG */ |
2501 | 0 | ) != -1) { |
2502 | 0 | switch (optc) { |
2503 | | |
2504 | 0 | case 'D': |
2505 | 0 | if (!optarg) { |
2506 | 0 | pr_log_pri(PR_LOG_WARNING, "fatal: -D requires definition parameter"); |
2507 | 0 | exit(1); |
2508 | 0 | } |
2509 | | |
2510 | 0 | pr_define_add(optarg, TRUE); |
2511 | 0 | break; |
2512 | | |
2513 | 0 | case 'V': |
2514 | 0 | show_settings(); |
2515 | 0 | exit(0); |
2516 | 0 | break; |
2517 | | |
2518 | 0 | case 'N': |
2519 | 0 | AddressCollisionCheck = FALSE; |
2520 | 0 | break; |
2521 | | |
2522 | 0 | case 'n': |
2523 | 0 | nodaemon++; |
2524 | | #ifdef PR_USE_DEVEL |
2525 | | pr_pool_debug_set_flags(PR_POOL_DEBUG_FL_OOM_DUMP_POOLS); |
2526 | | #endif |
2527 | 0 | break; |
2528 | | |
2529 | 0 | case 'q': |
2530 | 0 | quiet++; |
2531 | 0 | break; |
2532 | | |
2533 | 0 | case 'd': |
2534 | 0 | if (!optarg) { |
2535 | 0 | pr_log_pri(PR_LOG_WARNING, "fatal: -d requires debug level parameter"); |
2536 | 0 | exit(1); |
2537 | 0 | } |
2538 | 0 | pr_log_setdebuglevel(atoi(optarg)); |
2539 | | |
2540 | | /* If the admin uses -d on the command-line, they explicitly WANT |
2541 | | * debug logging, thus make sure the default SyslogLevel is set to |
2542 | | * DEBUG (rather than NOTICE); see Bug#3983. |
2543 | | */ |
2544 | 0 | pr_log_setdefaultlevel(PR_LOG_DEBUG); |
2545 | 0 | break; |
2546 | | |
2547 | 0 | case 'c': |
2548 | 0 | if (!optarg) { |
2549 | 0 | pr_log_pri(PR_LOG_WARNING, |
2550 | 0 | "fatal: -c requires configuration path parameter"); |
2551 | 0 | exit(1); |
2552 | 0 | } |
2553 | | |
2554 | | /* Note: we delay sanity-checking the given path until after the FSIO |
2555 | | * layer has been initialized. |
2556 | | */ |
2557 | 0 | config_filename = strdup(optarg); |
2558 | 0 | break; |
2559 | | |
2560 | 0 | case 'l': |
2561 | 0 | modules_list2(NULL, PR_MODULES_LIST_FL_SHOW_STATIC); |
2562 | 0 | exit(0); |
2563 | 0 | break; |
2564 | | |
2565 | 0 | case 'S': |
2566 | 0 | if (!optarg) { |
2567 | 0 | pr_log_pri(PR_LOG_WARNING, "fatal: -S requires IP address parameter"); |
2568 | 0 | exit(1); |
2569 | 0 | } |
2570 | | |
2571 | 0 | if (pr_netaddr_set_localaddr_str(optarg) < 0) { |
2572 | 0 | pr_log_pri(PR_LOG_WARNING, |
2573 | 0 | "fatal: unable to use '%s' as server address: %s", optarg, |
2574 | 0 | strerror(errno)); |
2575 | 0 | exit(1); |
2576 | 0 | } |
2577 | 0 | break; |
2578 | | |
2579 | 0 | case 't': |
2580 | 0 | syntax_check = 1; |
2581 | 0 | printf("%s", "Checking syntax of configuration file\n"); |
2582 | 0 | fflush(stdout); |
2583 | 0 | break; |
2584 | | |
2585 | | /* Note: This is now unused, and should be deprecated in the next release. |
2586 | | * See Bug#3952 for details. |
2587 | | */ |
2588 | 0 | case 'p': { |
2589 | 0 | if (!optarg || |
2590 | 0 | (atoi(optarg) != 1 && atoi(optarg) != 0)) { |
2591 | 0 | pr_log_pri(PR_LOG_WARNING, |
2592 | 0 | "fatal: -p requires Boolean (0|1) parameter"); |
2593 | 0 | exit(1); |
2594 | 0 | } |
2595 | | |
2596 | 0 | break; |
2597 | 0 | } |
2598 | | |
2599 | 0 | case 'v': |
2600 | 0 | show_version++; |
2601 | 0 | break; |
2602 | | |
2603 | 0 | case 'X': |
2604 | 0 | no_forking = TRUE; |
2605 | 0 | break; |
2606 | | |
2607 | 0 | case 1: |
2608 | 0 | show_version = 2; |
2609 | 0 | break; |
2610 | | |
2611 | 0 | case 'h': |
2612 | 0 | show_usage(0); |
2613 | 0 | break; |
2614 | | |
2615 | 0 | case '4': |
2616 | 0 | pr_netaddr_disable_ipv6(); |
2617 | 0 | break; |
2618 | | |
2619 | 0 | case '6': |
2620 | 0 | pr_netaddr_enable_ipv6(); |
2621 | 0 | break; |
2622 | | |
2623 | 0 | case '?': |
2624 | 0 | pr_log_pri(PR_LOG_WARNING, "unknown option: %c", (char) optopt); |
2625 | 0 | show_usage(1); |
2626 | 0 | break; |
2627 | 0 | } |
2628 | 0 | } |
2629 | | |
2630 | | /* If we have any leftover parameters, it's an error. */ |
2631 | 0 | if (argv[optind]) { |
2632 | 0 | pr_log_pri(PR_LOG_WARNING, "fatal: unknown parameter: '%s'", argv[optind]); |
2633 | 0 | exit(1); |
2634 | 0 | } |
2635 | | |
2636 | 0 | if (show_version == 1) { |
2637 | 0 | printf("%s", "ProFTPD Version " PROFTPD_VERSION_TEXT "\n"); |
2638 | 0 | exit(0); |
2639 | 0 | } |
2640 | | |
2641 | 0 | mpid = getpid(); |
2642 | | |
2643 | | /* Initialize sub-systems */ |
2644 | 0 | init_signals(); |
2645 | 0 | init_pools(); |
2646 | 0 | init_privs(); |
2647 | 0 | init_log(); |
2648 | 0 | init_regexp(); |
2649 | 0 | init_inet(); |
2650 | 0 | init_netio(); |
2651 | 0 | init_netaddr(); |
2652 | 0 | init_fs(); |
2653 | 0 | init_class(); |
2654 | 0 | free_bindings(); |
2655 | 0 | init_config(); |
2656 | 0 | init_dirtree(); |
2657 | 0 | init_stash(); |
2658 | 0 | init_json(); |
2659 | |
|
2660 | 0 | #ifdef PR_USE_CTRLS |
2661 | 0 | init_ctrls(); |
2662 | 0 | #endif /* PR_USE_CTRLS */ |
2663 | |
|
2664 | 0 | var_init(); |
2665 | |
|
2666 | | #if defined(PR_USE_NLS) |
2667 | | # if defined(HAVE_LOCALE_H) |
2668 | | /* Initialize the locale based on environment variables. */ |
2669 | | env_lang = pr_env_get(permanent_pool, "LANG"); |
2670 | | |
2671 | | env_locale = setlocale(LC_ALL, ""); |
2672 | | if (env_locale == NULL) { |
2673 | | pr_log_pri(PR_LOG_WARNING, "warning: unknown/unsupported LANG environment " |
2674 | | "variable '%s', ignoring", env_lang != NULL ? env_lang : "(null)"); |
2675 | | (void) setlocale(LC_ALL, "C"); |
2676 | | |
2677 | | } else { |
2678 | | pr_log_debug(DEBUG9, "using '%s' locale based on LANG=%s environment " |
2679 | | "variable", env_locale, env_lang != NULL ? env_lang : "(null)"); |
2680 | | |
2681 | | /* Make sure that LC_NUMERIC is always set to "C", so as not to interfere |
2682 | | * with formatting of strings (like printing out floats in SQL query |
2683 | | * strings). |
2684 | | */ |
2685 | | (void) setlocale(LC_NUMERIC, "C"); |
2686 | | } |
2687 | | # endif /* !HAVE_LOCALE_H */ |
2688 | | |
2689 | | encode_init(); |
2690 | | #endif /* PR_USE_NLS */ |
2691 | | |
2692 | | /* Note that modules MUST be initialized AFTER the locale, so that we |
2693 | | * are consistent in handling of strings via _e.g._ tolower(3); see Bug#4466. |
2694 | | */ |
2695 | 0 | modules_init(); |
2696 | | |
2697 | | /* Now, once the modules have had a chance to initialize themselves |
2698 | | * but before the configuration stream is actually parsed, check |
2699 | | * that the given configuration path is valid. |
2700 | | */ |
2701 | 0 | if (pr_fs_valid_path(config_filename) < 0) { |
2702 | 0 | pr_log_pri(PR_LOG_WARNING, "fatal: -c requires an absolute path"); |
2703 | 0 | exit(1); |
2704 | 0 | } |
2705 | | |
2706 | 0 | pr_parser_prepare(NULL, NULL); |
2707 | |
|
2708 | 0 | pr_event_generate("core.preparse", NULL); |
2709 | |
|
2710 | 0 | if (pr_parser_parse_file(NULL, config_filename, NULL, 0) < 0) { |
2711 | | /* Note: EPERM is used to indicate the presence of unrecognized |
2712 | | * configuration directives in the parsed file(s). |
2713 | | */ |
2714 | 0 | if (errno != EPERM) { |
2715 | 0 | pr_log_pri(PR_LOG_WARNING, |
2716 | 0 | "fatal: unable to read configuration file '%s': %s", config_filename, |
2717 | 0 | strerror(errno)); |
2718 | 0 | } |
2719 | |
|
2720 | 0 | exit(1); |
2721 | 0 | } |
2722 | | |
2723 | 0 | if (pr_parser_cleanup() < 0) { |
2724 | 0 | pr_log_pri(PR_LOG_WARNING, |
2725 | 0 | "fatal: error processing configuration file '%s': " |
2726 | 0 | "unclosed configuration section", config_filename); |
2727 | 0 | exit(1); |
2728 | 0 | } |
2729 | | |
2730 | 0 | if (fixup_servers(server_list) < 0) { |
2731 | 0 | pr_log_pri(PR_LOG_WARNING, |
2732 | 0 | "fatal: error processing configuration file '%s'", config_filename); |
2733 | 0 | exit(1); |
2734 | 0 | } |
2735 | | |
2736 | 0 | pr_event_generate("core.postparse", NULL); |
2737 | |
|
2738 | 0 | if (show_version >= 2) { |
2739 | 0 | printf("ProFTPD Version: %s", PROFTPD_VERSION_TEXT " " PR_STATUS "\n"); |
2740 | 0 | printf(" Scoreboard Version: %08x\n", PR_SCOREBOARD_VERSION); |
2741 | 0 | printf(" Built: %s\n\n", BUILD_STAMP); |
2742 | |
|
2743 | 0 | modules_list2(NULL, PR_MODULES_LIST_FL_SHOW_VERSION); |
2744 | |
|
2745 | 0 | if (show_version >= 3) { |
2746 | 0 | printf("\n"); |
2747 | 0 | list_directives(); |
2748 | 0 | } |
2749 | |
|
2750 | 0 | exit(0); |
2751 | 0 | } |
2752 | | |
2753 | | /* We're only doing a syntax check of the configuration file. */ |
2754 | 0 | if (syntax_check) { |
2755 | 0 | printf("%s", "Syntax check complete.\n"); |
2756 | 0 | pr_session_end(PR_SESS_END_FL_SYNTAX_CHECK); |
2757 | 0 | } |
2758 | | |
2759 | | /* Security */ |
2760 | 0 | { |
2761 | 0 | uid_t *uid = (uid_t *) get_param_ptr(main_server->conf, "UserID", FALSE); |
2762 | 0 | gid_t *gid = (gid_t *) get_param_ptr(main_server->conf, "GroupID", FALSE); |
2763 | |
|
2764 | 0 | daemon_uid = (uid != NULL ? *uid : PR_ROOT_UID); |
2765 | 0 | daemon_gid = (gid != NULL ? *gid : PR_ROOT_GID); |
2766 | 0 | } |
2767 | |
|
2768 | 0 | if (daemon_uid != PR_ROOT_UID) { |
2769 | 0 | pr_log_debug(DEBUG9, "ignoring supplemental groups for non-root UID %lu", |
2770 | 0 | (unsigned long) daemon_uid); |
2771 | 0 | } |
2772 | | |
2773 | | /* After configuration is complete, make sure that passwd, group |
2774 | | * aren't held open (unnecessary fds for master daemon) |
2775 | | */ |
2776 | 0 | endpwent(); |
2777 | 0 | endgrent(); |
2778 | |
|
2779 | 0 | main_umask = get_param_ptr(main_server->conf, "Umask", FALSE); |
2780 | 0 | if (main_umask == NULL) { |
2781 | 0 | umask((mode_t) 0022); |
2782 | |
|
2783 | 0 | } else { |
2784 | 0 | umask(*main_umask); |
2785 | 0 | } |
2786 | | |
2787 | | /* Give up root and save our uid/gid for later use (if supported) |
2788 | | * If we aren't currently root, PRIVS_SETUP will get rid of setuid |
2789 | | * granted root and prevent further uid switching from being attempted. |
2790 | | */ |
2791 | |
|
2792 | 0 | PRIVS_SETUP(daemon_uid, daemon_gid) |
2793 | |
|
2794 | 0 | #ifndef PR_DEVEL_COREDUMP |
2795 | | /* Test to make sure that our uid/gid is correct. Try to do this in |
2796 | | * a portable fashion *gah!* |
2797 | | */ |
2798 | |
|
2799 | 0 | if (geteuid() != daemon_uid) { |
2800 | 0 | pr_log_pri(PR_LOG_ERR, "unable to set UID to %s, current UID: %s", |
2801 | 0 | pr_uid2str(permanent_pool, daemon_uid), |
2802 | 0 | pr_uid2str(permanent_pool, geteuid())); |
2803 | 0 | exit(1); |
2804 | 0 | } |
2805 | | |
2806 | 0 | if (getegid() != daemon_gid) { |
2807 | 0 | pr_log_pri(PR_LOG_ERR, "unable to set GID to %s, current GID: %s", |
2808 | 0 | pr_gid2str(permanent_pool, daemon_gid), |
2809 | 0 | pr_gid2str(permanent_pool, getegid())); |
2810 | 0 | exit(1); |
2811 | 0 | } |
2812 | 0 | #endif /* PR_DEVEL_COREDUMP */ |
2813 | | |
2814 | 0 | switch (ServerType) { |
2815 | 0 | case SERVER_STANDALONE: |
2816 | 0 | standalone_main(); |
2817 | 0 | break; |
2818 | | |
2819 | 0 | case SERVER_INETD: |
2820 | | /* Reset the variable containing the pid of the master/daemon process; |
2821 | | * it should only be non-zero in the case of standalone daemons. |
2822 | | */ |
2823 | 0 | mpid = 0; |
2824 | 0 | inetd_main(); |
2825 | 0 | break; |
2826 | 0 | } |
2827 | | |
2828 | | #ifdef PR_DEVEL_NO_DAEMON |
2829 | | PRIVS_ROOT |
2830 | | chdir(PR_RUN_DIR); |
2831 | | #endif /* PR_DEVEL_NO_DAEMON */ |
2832 | | |
2833 | 0 | return 0; |
2834 | 0 | } |