1 /*
2 * ProFTPD - FTP server daemon
3 * Copyright (c) 2006-2020 The ProFTPD Project team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18 *
19 * As a special exemption, the ProFTPD Project and other respective copyright
20 * holders give permission to link this program with OpenSSL, and distribute
21 * the resulting executable, without including the source code for OpenSSL
22 * in the source distribution.
23 */
24
25 /* Trace functions. */
26
27 #include "conf.h"
28 #include "privs.h"
29
30 #ifdef PR_USE_TRACE
31
32 #define TRACE_BUFFER_SIZE (PR_TUNABLE_BUFFER_SIZE * 4)
33
34 static int trace_logfd = -1;
35 static unsigned long trace_opts = PR_TRACE_OPT_DEFAULT;
36 static pool *trace_pool = NULL;
37 static pr_table_t *trace_tab = NULL;
38
39 struct trace_levels {
40 int min_level;
41 int max_level;
42 };
43
44 static const char *trace_channels[] = {
45 "auth",
46 "binding",
47 "command",
48 "config",
49 "ctrls",
50 "data",
51 "delay",
52 "dns",
53 "dso",
54 "encode",
55 "event",
56 "facl",
57 "fsio",
58 "ident",
59 "inet",
60 "lock",
61 "log",
62 "module",
63 "netacl",
64 "netio",
65 "pam",
66 "pool",
67 "regexp",
68 "response",
69 "scoreboard",
70 "signal",
71 "site",
72 "timer",
73 "var",
74 "xfer",
75 NULL
76 };
77
trace_restart_ev(const void * event_data,void * user_data)78 static void trace_restart_ev(const void *event_data, void *user_data) {
79 trace_opts = PR_TRACE_OPT_DEFAULT;
80
81 close(trace_logfd);
82 trace_logfd = -1;
83
84 if (trace_pool) {
85 destroy_pool(trace_pool);
86 trace_pool = NULL;
87 trace_tab = NULL;
88
89 pr_event_unregister(NULL, "core.restart", trace_restart_ev);
90 }
91
92 return;
93 }
94
trace_write(const char * channel,int level,const char * msg,int discard)95 static int trace_write(const char *channel, int level, const char *msg,
96 int discard) {
97 pool *tmp_pool;
98 char buf[TRACE_BUFFER_SIZE];
99 size_t buflen = 0, len = 0;
100 struct tm *tm;
101 int use_conn_ips = FALSE;
102
103 if (trace_logfd < 0) {
104 return 0;
105 }
106
107 memset(buf, '\0', sizeof(buf));
108 tmp_pool = make_sub_pool(trace_pool);
109 pr_pool_tag(tmp_pool, "Trace message pool");
110
111 if (trace_opts & PR_TRACE_OPT_USE_TIMESTAMP) {
112 if (trace_opts & PR_TRACE_OPT_USE_TIMESTAMP_MILLIS) {
113 struct timeval now;
114 unsigned long millis;
115
116 gettimeofday(&now, NULL);
117 tm = pr_localtime(tmp_pool, (const time_t *) &(now.tv_sec));
118
119 len = strftime(buf, sizeof(buf)-1, "%Y-%m-%d %H:%M:%S", tm);
120 buflen = len;
121
122 /* Convert microsecs to millisecs. */
123 millis = now.tv_usec / 1000;
124
125 len = pr_snprintf(buf + buflen, sizeof(buf) - buflen, ",%03lu ", millis);
126 buflen += len;
127
128 } else {
129 time_t now;
130
131 now = time(NULL);
132 tm = pr_localtime(tmp_pool, &now);
133
134 len = strftime(buf, sizeof(buf)-1, "%Y-%m-%d %H:%M:%S ", tm);
135 buflen = len;
136 }
137 }
138
139 if ((trace_opts & PR_TRACE_OPT_LOG_CONN_IPS) &&
140 session.c != NULL) {
141 /* We can only support the "+ConnIPs" TraceOption if there actually
142 * is a client connected in this process. We might be the daemon
143 * process, in which there is no client.
144 */
145 use_conn_ips = TRUE;
146 }
147
148 if (use_conn_ips == FALSE) {
149 len = pr_snprintf(buf + buflen, sizeof(buf) - buflen, "[%u] <%s:%d>: %s",
150 (unsigned int) (session.pid ? session.pid : getpid()), channel, level,
151 msg);
152 buflen += len;
153
154 } else {
155 const char *client_ip, *server_ip;
156 int server_port;
157
158 client_ip = pr_netaddr_get_ipstr(session.c->remote_addr);
159 server_ip = pr_netaddr_get_ipstr(session.c->local_addr);
160 server_port = pr_netaddr_get_port(session.c->local_addr);
161
162 len = pr_snprintf(buf + buflen, sizeof(buf) - buflen,
163 "[%u] (client %s, server %s:%d) <%s:%d>: %s",
164 (unsigned int) (session.pid ? session.pid : getpid()),
165 client_ip != NULL ? client_ip : "none",
166 server_ip != NULL ? server_ip : "none", server_port, channel, level, msg);
167 buflen += len;
168 }
169
170 buf[sizeof(buf)-1] = '\0';
171
172 if (buflen < (sizeof(buf) - 1)) {
173 buf[buflen] = '\n';
174 buflen++;
175
176 } else {
177 buf[sizeof(buf)-5] = '.';
178 buf[sizeof(buf)-4] = '.';
179 buf[sizeof(buf)-3] = '.';
180 buf[sizeof(buf)-2] = '.';
181 buflen = sizeof(buf)-1;
182 }
183
184 pr_log_event_generate(PR_LOG_TYPE_TRACELOG, trace_logfd, level, buf, buflen);
185
186 if (discard) {
187 /* This log message would not have been written to disk, so just discard
188 * it. The discard value is TRUE when there's a log listener for
189 * TraceLog logging events, and the Trace log level configuration would
190 * otherwise have filtered out this log message.
191 */
192 return 0;
193 }
194
195 destroy_pool(tmp_pool);
196 return write(trace_logfd, buf, buflen);
197 }
198
pr_trace_get_table(void)199 pr_table_t *pr_trace_get_table(void) {
200 if (trace_tab == NULL) {
201 errno = ENOENT;
202 return NULL;
203 }
204
205 return trace_tab;
206 }
207
trace_get_levels(const char * channel)208 static const struct trace_levels *trace_get_levels(const char *channel) {
209 const void *value;
210
211 if (channel == NULL) {
212 errno = EINVAL;
213 return NULL;
214 }
215
216 if (trace_tab == NULL ||
217 trace_logfd < 0) {
218 errno = EPERM;
219 return NULL;
220 }
221
222 value = pr_table_get(trace_tab, channel, NULL);
223 if (value == NULL) {
224 errno = ENOENT;
225 return NULL;
226 }
227
228 return value;
229 }
230
pr_trace_get_level(const char * channel)231 int pr_trace_get_level(const char *channel) {
232 return pr_trace_get_max_level(channel);
233 }
234
pr_trace_get_max_level(const char * channel)235 int pr_trace_get_max_level(const char *channel) {
236 const struct trace_levels *levels;
237
238 levels = trace_get_levels(channel);
239 if (levels == NULL) {
240 return -1;
241 }
242
243 return levels->max_level;
244 }
245
pr_trace_get_min_level(const char * channel)246 int pr_trace_get_min_level(const char *channel) {
247 const struct trace_levels *levels;
248
249 levels = trace_get_levels(channel);
250 if (levels == NULL) {
251 return -1;
252 }
253
254 return levels->min_level;
255 }
256
pr_trace_parse_levels(char * str,int * min_level,int * max_level)257 int pr_trace_parse_levels(char *str, int *min_level, int *max_level) {
258 int low = 1, high = -1;
259 char *ptr = NULL, *tmp = NULL;
260
261 if (str == NULL ||
262 min_level == NULL ||
263 max_level == NULL) {
264 errno = EINVAL;
265 return -1;
266 }
267
268 /* Watch for blank strings for levels (i.e. misconfigured/typo in config). */
269 if (*str == '\0') {
270 errno = EINVAL;
271 return -1;
272 }
273
274 /* Check for a value range. */
275 if (*str == '-') {
276 errno = EINVAL;
277 return -1;
278 }
279
280 ptr = strchr(str, '-');
281 if (ptr == NULL) {
282 /* Just a single value. */
283 errno = 0;
284 high = (int) strtol(str, &ptr, 10);
285 if (errno == ERANGE) {
286 errno = EINVAL;
287 return -1;
288 }
289
290 if (ptr && *ptr) {
291 errno = EINVAL;
292 return -1;
293 }
294
295 if (high < 0) {
296 errno = EINVAL;
297 return -1;
298 }
299
300 /* A special case is where the single value is zero. If this is the
301 * case, we make sure that the min value is the same.
302 */
303 if (high != 0) {
304 *min_level = 1;
305
306 } else {
307 *min_level = 0;
308 }
309
310 *max_level = high;
311 return 0;
312 }
313
314 /* We have a range of values. */
315 *ptr = '\0';
316
317 low = (int) strtol(str, &tmp, 10);
318 if (errno == ERANGE) {
319 errno = EINVAL;
320 return -1;
321 }
322
323 if (tmp && *tmp) {
324 *ptr = '-';
325 errno = EINVAL;
326 return -1;
327 }
328 *ptr = '-';
329
330 if (low < 0) {
331 errno = EINVAL;
332 return -1;
333 }
334
335 tmp = NULL;
336 high = (int) strtol(ptr + 1, &tmp, 10);
337 if (errno == ERANGE) {
338 errno = EINVAL;
339 return -1;
340 }
341
342 if (tmp && *tmp) {
343 errno = EINVAL;
344 return -1;
345 }
346
347 if (high < 0) {
348 errno = EINVAL;
349 return -1;
350 }
351
352 if (high < low) {
353 errno = EINVAL;
354 return -1;
355 }
356
357 *min_level = low;
358 *max_level = high;
359 return 0;
360 }
361
pr_trace_set_file(const char * path)362 int pr_trace_set_file(const char *path) {
363 int res, xerrno;
364
365 if (path == NULL) {
366 if (trace_logfd < 0) {
367 errno = EINVAL;
368 return -1;
369 }
370
371 (void) close(trace_logfd);
372 trace_logfd = -1;
373 return 0;
374 }
375
376 pr_signals_block();
377 PRIVS_ROOT
378 res = pr_log_openfile(path, &trace_logfd, 0660);
379 xerrno = errno;
380 PRIVS_RELINQUISH
381 pr_signals_unblock();
382
383 if (res < 0) {
384 if (res == -1) {
385 pr_log_debug(DEBUG1, "unable to open TraceLog '%s': %s", path,
386 strerror(xerrno));
387 errno = xerrno;
388
389 } else if (res == PR_LOG_WRITABLE_DIR) {
390 pr_log_debug(DEBUG1,
391 "unable to open TraceLog '%s': parent directory is world-writable",
392 path);
393 errno = EPERM;
394
395 } else if (res == PR_LOG_SYMLINK) {
396 pr_log_debug(DEBUG1,
397 "unable to open TraceLog '%s': cannot log to a symbolic link",
398 path);
399 errno = EPERM;
400 }
401
402 return res;
403 }
404
405 return 0;
406 }
407
pr_trace_set_levels(const char * channel,int min_level,int max_level)408 int pr_trace_set_levels(const char *channel, int min_level, int max_level) {
409
410 if (channel == NULL) {
411 if (trace_tab == NULL) {
412 errno = EINVAL;
413 return -1;
414 }
415
416 return 0;
417 }
418
419 if (min_level > max_level) {
420 errno = EINVAL;
421 return -1;
422 }
423
424 if (trace_tab == NULL &&
425 min_level < 0) {
426 return 0;
427 }
428
429 if (trace_pool == NULL) {
430 trace_pool = make_sub_pool(permanent_pool);
431 pr_pool_tag(trace_pool, "Trace API");
432
433 trace_tab = pr_table_alloc(trace_pool, 0);
434
435 /* Register a handler for churning the log pool during HUP. */
436 pr_event_register(NULL, "core.restart", trace_restart_ev, NULL);
437 }
438
439 if (min_level >= 0) {
440 struct trace_levels *levels;
441
442 levels = pcalloc(trace_pool, sizeof(struct trace_levels));
443 levels->min_level = min_level;
444 levels->max_level = max_level;
445
446 if (strcmp(channel, PR_TRACE_DEFAULT_CHANNEL) != 0) {
447 int count;
448
449 count = pr_table_exists(trace_tab, channel);
450 if (count <= 0) {
451 if (pr_table_add(trace_tab, pstrdup(trace_pool, channel), levels,
452 sizeof(struct trace_levels)) < 0) {
453 return -1;
454 }
455
456 } else {
457 if (pr_table_set(trace_tab, pstrdup(trace_pool, channel), levels,
458 sizeof(struct trace_levels)) < 0) {
459 return -1;
460 }
461 }
462
463 } else {
464 register unsigned int i;
465
466 for (i = 0; trace_channels[i]; i++) {
467 (void) pr_trace_set_levels(trace_channels[i], min_level, max_level);
468 }
469 }
470
471 } else {
472 if (strcmp(channel, PR_TRACE_DEFAULT_CHANNEL) != 0) {
473 (void) pr_table_remove(trace_tab, channel, NULL);
474
475 } else {
476 register unsigned int i;
477
478 for (i = 0; trace_channels[i]; i++) {
479 (void) pr_table_remove(trace_tab, trace_channels[i], NULL);
480 }
481 }
482 }
483
484 return 0;
485 }
486
pr_trace_set_options(unsigned long opts)487 int pr_trace_set_options(unsigned long opts) {
488 trace_opts = opts;
489 return 0;
490 }
491
pr_trace_use_stderr(int use_stderr)492 int pr_trace_use_stderr(int use_stderr) {
493 if (use_stderr) {
494 int res;
495
496 res = dup(STDERR_FILENO);
497 if (res < 0) {
498 return -1;
499 }
500
501 /* Avoid a file descriptor leak by closing any existing fd. */
502 (void) close(trace_logfd);
503 trace_logfd = res;
504
505 } else {
506 (void) close(trace_logfd);
507 trace_logfd = -1;
508 }
509
510 return 0;
511 }
512
pr_trace_msg(const char * channel,int level,const char * fmt,...)513 int pr_trace_msg(const char *channel, int level, const char *fmt, ...) {
514 int res;
515 va_list msg;
516
517 if (channel == NULL ||
518 fmt == NULL ||
519 level <= 0) {
520 errno = EINVAL;
521 return -1;
522 }
523
524 /* If no one's listening... */
525 if (trace_logfd < 0 &&
526 pr_log_event_listening(PR_LOG_TYPE_TRACELOG) <= 0) {
527 return 0;
528 }
529
530 va_start(msg, fmt);
531 res = pr_trace_vmsg(channel, level, fmt, msg);
532 va_end(msg);
533
534 return res;
535 }
536
pr_trace_vmsg(const char * channel,int level,const char * fmt,va_list msg)537 int pr_trace_vmsg(const char *channel, int level, const char *fmt,
538 va_list msg) {
539 char buf[TRACE_BUFFER_SIZE];
540 const struct trace_levels *levels;
541 int buflen, discard = FALSE, listening;
542
543 /* Writing a trace message at level zero is NOT helpful; this makes it
544 * impossible to quell messages to that trace channel by setting the level
545 * filter to zero. That being the case, treat level of zero as an invalid
546 * level.
547 */
548
549 if (channel == NULL ||
550 fmt == NULL ||
551 level <= 0) {
552 errno = EINVAL;
553 return -1;
554 }
555
556 if (trace_tab == NULL) {
557 errno = EPERM;
558 return -1;
559 }
560
561 /* If no one's listening... */
562 if (trace_logfd < 0) {
563 return 0;
564 }
565
566 listening = pr_log_event_listening(PR_LOG_TYPE_TRACELOG);
567
568 levels = trace_get_levels(channel);
569 if (levels == NULL) {
570 discard = TRUE;
571
572 if (listening <= 0) {
573 return 0;
574 }
575 }
576
577 if (discard == FALSE &&
578 level < levels->min_level) {
579 discard = TRUE;
580
581 if (listening <= 0) {
582 return 0;
583 }
584 }
585
586 if (discard == FALSE &&
587 level > levels->max_level) {
588 discard = TRUE;
589
590 if (listening <= 0) {
591 return 0;
592 }
593 }
594
595 buflen = pr_vsnprintf(buf, sizeof(buf)-1, fmt, msg);
596
597 /* Always make sure the buffer is NUL-terminated. */
598 buf[sizeof(buf)-1] = '\0';
599
600 if (buflen > 0 &&
601 buflen < sizeof(buf)) {
602 buf[buflen] = '\0';
603
604 } else {
605 /* Note that vsnprintf() returns the number of characters _that would have
606 * been printed if buffer were unlimited_. Be careful of this.
607 */
608 buflen = sizeof(buf)-1;
609 }
610
611 /* Trim trailing newlines. */
612 while (buflen >= 1 &&
613 buf[buflen-1] == '\n') {
614 pr_signals_handle();
615 buf[buflen-1] = '\0';
616 buflen--;
617 }
618
619 return trace_write(channel, level, buf, discard);
620 }
621
622 #else
623
pr_trace_get_table(void)624 pr_table_t *pr_trace_get_table(void) {
625 errno = ENOSYS;
626 return NULL;
627 }
628
pr_trace_get_level(const char * channel)629 int pr_trace_get_level(const char *channel) {
630 errno = ENOSYS;
631 return -1;
632 }
633
pr_trace_get_max_level(const char * channel)634 int pr_trace_get_max_level(const char *channel) {
635 errno = ENOSYS;
636 return -1;
637 }
638
pr_trace_get_min_level(const char * channel)639 int pr_trace_get_min_level(const char *channel) {
640 errno = ENOSYS;
641 return -1;
642 }
643
pr_trace_parse_levels(char * str,int * min_level,int * max_level)644 int pr_trace_parse_levels(char *str, int *min_level, int *max_level) {
645 errno = ENOSYS;
646 return -1;
647 }
648
pr_trace_set_file(const char * path)649 int pr_trace_set_file(const char *path) {
650 errno = ENOSYS;
651 return -1;
652 }
653
pr_trace_set_levels(const char * channel,int min_level,int max_level)654 int pr_trace_set_levels(const char *channel, int min_level, int max_level) {
655 errno = ENOSYS;
656 return -1;
657 }
658
pr_trace_set_options(unsigned long opts)659 int pr_trace_set_options(unsigned long opts) {
660 errno = ENOSYS;
661 return -1;
662 }
663
pr_trace_msg(const char * channel,int level,const char * fmt,...)664 int pr_trace_msg(const char *channel, int level, const char *fmt, ...) {
665 errno = ENOSYS;
666 return -1;
667 }
668
pr_trace_vmsg(const char * channel,int level,const char * fmt,va_list vargs)669 int pr_trace_vmsg(const char *channel, int level, const char *fmt,
670 va_list vargs) {
671 errno = ENOSYS;
672 return -1;
673 }
674
675 #endif /* PR_USE_TRACE */
676