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