1 /*
2  *   stunnel       TLS offloading and load-balancing proxy
3  *   Copyright (C) 1998-2021 Michal Trojnara <Michal.Trojnara@stunnel.org>
4  *
5  *   This program is free software; you can redistribute it and/or modify it
6  *   under the terms of the GNU General Public License as published by the
7  *   Free Software Foundation; either version 2 of the License, or (at your
8  *   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.
13  *   See the GNU General Public License for more details.
14  *
15  *   You should have received a copy of the GNU General Public License along
16  *   with this program; if not, see <http://www.gnu.org/licenses>.
17  *
18  *   Linking stunnel statically or dynamically with other modules is making
19  *   a combined work based on stunnel. Thus, the terms and conditions of
20  *   the GNU General Public License cover the whole combination.
21  *
22  *   In addition, as a special exception, the copyright holder of stunnel
23  *   gives you permission to combine stunnel with free software programs or
24  *   libraries that are released under the GNU LGPL and with code included
25  *   in the standard release of OpenSSL under the OpenSSL License (or
26  *   modified versions of such code, with unchanged license). You may copy
27  *   and distribute such a system following the terms of the GNU GPL for
28  *   stunnel and the licenses of the other code concerned.
29  *
30  *   Note that people who make modified versions of stunnel are not obligated
31  *   to grant this special exception for their modified versions; it is their
32  *   choice whether to do so. The GNU General Public License gives permission
33  *   to release a modified version without this exception; this exception
34  *   also makes it possible to release a modified version which carries
35  *   forward this exception.
36  */
37 
38 #include "common.h"
39 #include "prototypes.h"
40 
41 NOEXPORT void log_queue(SERVICE_OPTIONS *, int, char *, char *, char *);
42 NOEXPORT void log_raw(SERVICE_OPTIONS *, int, char *, char *, char *);
43 NOEXPORT void safestring(char *);
44 
45 static DISK_FILE *outfile=NULL;
46 static struct LIST { /* single-linked list of log lines */
47     struct LIST *next;
48     SERVICE_OPTIONS *opt;
49     int level;
50     char *stamp, *id, *text;
51 } *head=NULL, *tail=NULL;
52 static LOG_MODE log_mode=LOG_MODE_BUFFER;
53 
54 #if !defined(USE_WIN32) && !defined(__vms)
55 
56 static int syslog_opened=0;
57 
syslog_open(void)58 NOEXPORT void syslog_open(void) {
59     if(global_options.option.log_syslog) {
60         static char *servname=NULL;
61         char *servname_old;
62 
63         /* openlog(3) requires a persistent copy of the "ident" parameter */
64         servname_old=servname;
65         servname=str_dup(service_options.servname);
66 #ifdef __ultrix__
67         openlog(servname, 0);
68 #else
69         openlog(servname, LOG_CONS|LOG_NDELAY, global_options.log_facility);
70 #endif /* __ultrix__ */
71         str_free(servname_old);
72     }
73     syslog_opened=1;
74 }
75 
syslog_close(void)76 NOEXPORT void syslog_close(void) {
77     if(syslog_opened) {
78         if(global_options.option.log_syslog)
79             closelog();
80         syslog_opened=0;
81     }
82 }
83 
84 #endif /* !defined(USE_WIN32) && !defined(__vms) */
85 
outfile_open(void)86 NOEXPORT int outfile_open(void) {
87     if(global_options.output_file) { /* 'output' option specified */
88         outfile=file_open(global_options.output_file,
89             global_options.log_file_mode);
90 #if defined(USE_WIN32) && !defined(_WIN32_WCE)
91         if(!outfile) {
92             char appdata[MAX_PATH], *path;
93             if(SHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA|CSIDL_FLAG_CREATE,
94                     NULL, 0, appdata)==S_OK) {
95                 path=str_printf("%s\\%s", appdata, global_options.output_file);
96                 outfile=file_open(path, global_options.log_file_mode);
97                 if(outfile)
98                     s_log(LOG_NOTICE, "Logging to %s", path);
99                 str_free(path);
100             }
101         }
102 #endif
103         if(!outfile) {
104             s_log(LOG_ERR, "Cannot open log file: %s",
105                 global_options.output_file);
106             return 1;
107         }
108     }
109     return 0;
110 }
111 
outfile_close(void)112 NOEXPORT void outfile_close(void) {
113     if(outfile) {
114         file_close(outfile);
115         outfile=NULL;
116     }
117 }
118 
log_open(int sink)119 int log_open(int sink) {
120 #if !defined(USE_WIN32) && !defined(__vms)
121     if(sink&SINK_SYSLOG)
122         syslog_open();
123 #endif
124     if(sink&SINK_OUTFILE && outfile_open())
125         return 1;
126     return 0;
127 }
128 
log_close(int sink)129 void log_close(int sink) {
130     /* prevent changing the mode while logging */
131     CRYPTO_THREAD_write_lock(stunnel_locks[LOCK_LOG_MODE]);
132 #if !defined(USE_WIN32) && !defined(__vms)
133     if(sink&SINK_SYSLOG)
134         syslog_close();
135 #endif
136     if(sink&SINK_OUTFILE)
137         outfile_close();
138     CRYPTO_THREAD_unlock(stunnel_locks[LOCK_LOG_MODE]);
139 }
140 
s_log(int level,const char * format,...)141 void s_log(int level, const char *format, ...) {
142     va_list ap;
143     char *text, *stamp, *id;
144 #ifdef USE_WIN32
145     DWORD libc_error;
146 #else
147     int libc_error;
148 #endif
149     int socket_error;
150     time_t gmt;
151     struct tm *timeptr;
152 #if defined(HAVE_LOCALTIME_R) && defined(_REENTRANT)
153     struct tm timestruct;
154 #endif
155     TLS_DATA *tls_data;
156 
157     libc_error=get_last_error();
158     socket_error=get_last_socket_error();
159 
160     tls_data=tls_get();
161     if(!tls_data) {
162         tls_data=tls_alloc(NULL, NULL, "log");
163         s_log(LOG_ERR, "INTERNAL ERROR: Uninitialized TLS at %s, line %d",
164             __FILE__, __LINE__);
165     }
166 
167     /* performance optimization: skip the trivial case early */
168     if(log_mode!=LOG_MODE_CONFIGURED || level<=tls_data->opt->log_level) {
169         /* format the id to be logged */
170         time(&gmt);
171 #if defined(HAVE_LOCALTIME_R) && defined(_REENTRANT)
172         timeptr=localtime_r(&gmt, &timestruct);
173 #else
174         timeptr=localtime(&gmt);
175 #endif
176         stamp=str_printf("%04d.%02d.%02d %02d:%02d:%02d",
177             timeptr->tm_year+1900, timeptr->tm_mon+1, timeptr->tm_mday,
178             timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec);
179         id=str_printf("LOG%d[%s]", level, tls_data->id);
180 
181         /* format the text to be logged */
182         va_start(ap, format);
183         text=str_vprintf(format, ap);
184         va_end(ap);
185         safestring(text);
186 
187         /* either log or queue for logging */
188         CRYPTO_THREAD_read_lock(stunnel_locks[LOCK_LOG_MODE]);
189         if(log_mode==LOG_MODE_BUFFER)
190             log_queue(tls_data->opt, level, stamp, id, text);
191         else
192             log_raw(tls_data->opt, level, stamp, id, text);
193         CRYPTO_THREAD_unlock(stunnel_locks[LOCK_LOG_MODE]);
194     }
195 
196     set_last_error(libc_error);
197     set_last_socket_error(socket_error);
198 }
199 
log_queue(SERVICE_OPTIONS * opt,int level,char * stamp,char * id,char * text)200 NOEXPORT void log_queue(SERVICE_OPTIONS *opt,
201         int level, char *stamp, char *id, char *text) {
202     struct LIST *tmp;
203 
204     /* make a new element */
205     tmp=str_alloc_detached(sizeof(struct LIST));
206     tmp->next=NULL;
207     tmp->opt=opt;
208     tmp->level=level;
209     tmp->stamp=stamp;
210     str_detach(tmp->stamp);
211     tmp->id=id;
212     str_detach(tmp->id);
213     tmp->text=text;
214     str_detach(tmp->text);
215 
216     /* append the new element to the list */
217     CRYPTO_THREAD_write_lock(stunnel_locks[LOCK_LOG_BUFFER]);
218     if(tail)
219         tail->next=tmp;
220     else
221         head=tmp;
222     tail=tmp;
223     if(stunnel_locks[LOCK_LOG_BUFFER])
224     CRYPTO_THREAD_unlock(stunnel_locks[LOCK_LOG_BUFFER]);
225 }
226 
log_flush(LOG_MODE new_mode)227 void log_flush(LOG_MODE new_mode) {
228     CRYPTO_THREAD_write_lock(stunnel_locks[LOCK_LOG_MODE]);
229 
230     log_mode=new_mode;
231 
232     /* emit the buffered logs (unless we just started buffering) */
233     if(new_mode!=LOG_MODE_BUFFER) {
234         /* log_raw() will use the new value of log_mode */
235         CRYPTO_THREAD_write_lock(stunnel_locks[LOCK_LOG_BUFFER]);
236         while(head) {
237             struct LIST *tmp=head;
238             head=head->next;
239             log_raw(tmp->opt, tmp->level, tmp->stamp, tmp->id, tmp->text);
240             str_free(tmp);
241         }
242         head=tail=NULL;
243         CRYPTO_THREAD_unlock(stunnel_locks[LOCK_LOG_BUFFER]);
244     }
245 
246     CRYPTO_THREAD_unlock(stunnel_locks[LOCK_LOG_MODE]);
247 }
248 
log_raw(SERVICE_OPTIONS * opt,int level,char * stamp,char * id,char * text)249 NOEXPORT void log_raw(SERVICE_OPTIONS *opt,
250         int level, char *stamp, char *id, char *text) {
251     char *line;
252 
253     /* NOTE: opt->log_level may have changed since s_log().
254      * It is important to use the new value and not the old one. */
255 
256     /* build the line and log it to syslog/file if configured */
257     switch(log_mode) {
258     case LOG_MODE_CONFIGURED:
259         line=str_printf("%s %s: %s", stamp, id, text);
260         if(level<=opt->log_level) {
261 #if !defined(USE_WIN32) && !defined(__vms)
262             if(global_options.option.log_syslog)
263                 syslog(level, "%s: %s", id, text);
264 #endif /* USE_WIN32, __vms */
265             if(outfile)
266                 file_putline(outfile, line);
267         }
268         break;
269     case LOG_MODE_ERROR:
270         /* don't log the id or the time stamp */
271         if(level>=0 && level<=7) /* just in case */
272             line=str_printf("[%c] %s", "***!:.  "[level], text);
273         else
274             line=str_printf("[?] %s", text);
275         break;
276     default: /* LOG_MODE_INFO */
277         /* don't log the level, the id or the time stamp */
278         line=str_dup(text);
279     }
280 
281     /* free the memory */
282     str_free(stamp);
283     str_free(id);
284     str_free(text);
285 
286     /* log the line to the UI (GUI, stderr, etc.) */
287     if(log_mode==LOG_MODE_ERROR ||
288             (log_mode==LOG_MODE_INFO && level<LOG_DEBUG) ||
289 #if defined(USE_WIN32) || defined(USE_JNI)
290             level<=opt->log_level
291 #else
292             (level<=opt->log_level &&
293             opt->option.log_stderr)
294 #endif
295             )
296         ui_new_log(line);
297 
298     str_free(line);
299 }
300 
301 #ifdef __GNUC__
302 #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
303 #pragma GCC diagnostic push
304 #endif /* __GNUC__>=4.6 */
305 #pragma GCC diagnostic ignored "-Wformat"
306 #pragma GCC diagnostic ignored "-Wformat-extra-args"
307 #endif /* __GNUC__ */
log_id(CLI * c)308 char *log_id(CLI *c) {
309     const char table[62]=
310         "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
311     unsigned char rnd[22];
312     char *uniq;
313     size_t i;
314     unsigned long tid;
315 
316     switch(c->opt->log_id) {
317     case LOG_ID_SEQUENTIAL:
318         return str_printf("%llu", c->seq);
319     case LOG_ID_UNIQUE:
320         memset(rnd, 0, sizeof rnd);
321         if(RAND_bytes(rnd, sizeof rnd)<=0) /* log2(62^22)=130.99 */
322             return str_dup("error");
323         for(i=0; i<sizeof rnd; ++i) {
324             rnd[i]&=63;
325             while(rnd[i]>=62) {
326                 if(RAND_bytes(rnd+i, 1)<=0)
327                     return str_dup("error");
328                 rnd[i]&=63;
329             }
330         }
331         uniq=str_alloc(sizeof rnd+1);
332         for(i=0; i<sizeof rnd; ++i)
333             uniq[i]=table[rnd[i]];
334         uniq[sizeof rnd]='\0';
335         return uniq;
336     case LOG_ID_THREAD:
337         tid=stunnel_thread_id();
338         if(!tid) /* currently USE_FORK */
339             tid=stunnel_process_id();
340         return str_printf("%lu", tid);
341     case LOG_ID_PROCESS:
342         return str_printf("%lu", stunnel_process_id());
343     }
344     return str_dup("error");
345 }
346 #ifdef __GNUC__
347 #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
348 #pragma GCC diagnostic pop
349 #endif /* __GNUC__>=4.6 */
350 #endif /* __GNUC__ */
351 
352 /* critical problem handling */
353 /* str.c functions are not safe to use here */
354 #ifdef __GNUC__
355 #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || defined(__clang__)
356 #pragma GCC diagnostic push
357 #pragma GCC diagnostic ignored "-Wunused-result"
358 #endif /* __GNUC__>=4.6 */
359 #endif /* __GNUC__ */
fatal_debug(char * txt,const char * file,int line)360 void fatal_debug(char *txt, const char *file, int line) {
361     char msg[80];
362 #ifdef USE_WIN32
363     DWORD num;
364 #ifdef UNICODE
365     TCHAR tmsg[80];
366 #endif
367 #endif /* USE_WIN32 */
368 
369     snprintf(msg, sizeof msg, /* with newline */
370         "INTERNAL ERROR: %s at %s, line %d\n", txt, file, line);
371 
372     if(outfile) {
373 #ifdef USE_WIN32
374         WriteFile(outfile->fh, msg, (DWORD)strlen(msg), &num, NULL);
375 #else /* USE_WIN32 */
376         /* no file -> write to stderr */
377         /* no meaningful way here to handle the result */
378         write(outfile ? outfile->fd : 2, msg, strlen(msg));
379 #endif /* USE_WIN32 */
380     }
381 
382 #ifndef USE_WIN32
383     if(log_mode!=LOG_MODE_CONFIGURED || global_options.option.log_stderr) {
384         fputs(msg, stderr);
385         fflush(stderr);
386     }
387 #endif /* !USE_WIN32 */
388 
389     snprintf(msg, sizeof msg, /* without newline */
390         "INTERNAL ERROR: %s at %s, line %d", txt, file, line);
391 
392 #if !defined(USE_WIN32) && !defined(__vms)
393     if(global_options.option.log_syslog)
394         syslog(LOG_CRIT, "%s", msg);
395 #endif /* USE_WIN32, __vms */
396 
397 #ifdef USE_WIN32
398 #ifdef UNICODE
399     if(MultiByteToWideChar(CP_UTF8, 0, msg, -1, tmsg, 80))
400         message_box(tmsg, MB_ICONERROR);
401 #else
402     message_box(msg, MB_ICONERROR);
403 #endif
404 #endif /* USE_WIN32 */
405 
406     abort();
407 }
408 #ifdef __GNUC__
409 #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
410 #pragma GCC diagnostic pop
411 #endif /* __GNUC__>=4.6 */
412 #endif /* __GNUC__ */
413 
ioerror(const char * txt)414 void ioerror(const char *txt) { /* input/output error */
415     log_error(LOG_ERR, (int)get_last_error(), txt);
416 }
417 
sockerror(const char * txt)418 void sockerror(const char *txt) { /* socket error */
419     log_error(LOG_ERR, get_last_socket_error(), txt);
420 }
421 
log_error(int level,int error,const char * txt)422 void log_error(int level, int error, const char *txt) { /* generic error */
423     s_log(level, "%s: %s (%d)", txt, s_strerror(error), error);
424 }
425 
s_strerror(int errnum)426 char *s_strerror(int errnum) {
427     switch(errnum) {
428 #ifdef USE_WIN32
429     case 10004:
430         return "Interrupted system call (WSAEINTR)";
431     case 10009:
432         return "Bad file number (WSAEBADF)";
433     case 10013:
434         return "Permission denied (WSAEACCES)";
435     case 10014:
436         return "Bad address (WSAEFAULT)";
437     case 10022:
438         return "Invalid argument (WSAEINVAL)";
439     case 10024:
440         return "Too many open files (WSAEMFILE)";
441     case 10035:
442         return "Operation would block (WSAEWOULDBLOCK)";
443     case 10036:
444         return "Operation now in progress (WSAEINPROGRESS)";
445     case 10037:
446         return "Operation already in progress (WSAEALREADY)";
447     case 10038:
448         return "Socket operation on non-socket (WSAENOTSOCK)";
449     case 10039:
450         return "Destination address required (WSAEDESTADDRREQ)";
451     case 10040:
452         return "Message too long (WSAEMSGSIZE)";
453     case 10041:
454         return "Protocol wrong type for socket (WSAEPROTOTYPE)";
455     case 10042:
456         return "Bad protocol option (WSAENOPROTOOPT)";
457     case 10043:
458         return "Protocol not supported (WSAEPROTONOSUPPORT)";
459     case 10044:
460         return "Socket type not supported (WSAESOCKTNOSUPPORT)";
461     case 10045:
462         return "Operation not supported on socket (WSAEOPNOTSUPP)";
463     case 10046:
464         return "Protocol family not supported (WSAEPFNOSUPPORT)";
465     case 10047:
466         return "Address family not supported by protocol family (WSAEAFNOSUPPORT)";
467     case 10048:
468         return "Address already in use (WSAEADDRINUSE)";
469     case 10049:
470         return "Can't assign requested address (WSAEADDRNOTAVAIL)";
471     case 10050:
472         return "Network is down (WSAENETDOWN)";
473     case 10051:
474         return "Network is unreachable (WSAENETUNREACH)";
475     case 10052:
476         return "Net dropped connection or reset (WSAENETRESET)";
477     case 10053:
478         return "Software caused connection abort (WSAECONNABORTED)";
479     case 10054:
480         return "Connection reset by peer (WSAECONNRESET)";
481     case 10055:
482         return "No buffer space available (WSAENOBUFS)";
483     case 10056:
484         return "Socket is already connected (WSAEISCONN)";
485     case 10057:
486         return "Socket is not connected (WSAENOTCONN)";
487     case 10058:
488         return "Can't send after socket shutdown (WSAESHUTDOWN)";
489     case 10059:
490         return "Too many references, can't splice (WSAETOOMANYREFS)";
491     case 10060:
492         return "Connection timed out (WSAETIMEDOUT)";
493     case 10061:
494         return "Connection refused (WSAECONNREFUSED)";
495     case 10062:
496         return "Too many levels of symbolic links (WSAELOOP)";
497     case 10063:
498         return "File name too long (WSAENAMETOOLONG)";
499     case 10064:
500         return "Host is down (WSAEHOSTDOWN)";
501     case 10065:
502         return "No Route to Host (WSAEHOSTUNREACH)";
503     case 10066:
504         return "Directory not empty (WSAENOTEMPTY)";
505     case 10067:
506         return "Too many processes (WSAEPROCLIM)";
507     case 10068:
508         return "Too many users (WSAEUSERS)";
509     case 10069:
510         return "Disc Quota Exceeded (WSAEDQUOT)";
511     case 10070:
512         return "Stale NFS file handle (WSAESTALE)";
513     case 10091:
514         return "Network SubSystem is unavailable (WSASYSNOTREADY)";
515     case 10092:
516         return "WINSOCK DLL Version out of range (WSAVERNOTSUPPORTED)";
517     case 10093:
518         return "Successful WSASTARTUP not yet performed (WSANOTINITIALISED)";
519     case 10071:
520         return "Too many levels of remote in path (WSAEREMOTE)";
521     case 11001:
522         return "Host not found (WSAHOST_NOT_FOUND)";
523     case 11002:
524         return "Non-Authoritative Host not found (WSATRY_AGAIN)";
525     case 11003:
526         return "Non-Recoverable errors: FORMERR, REFUSED, NOTIMP (WSANO_RECOVERY)";
527     case 11004:
528         return "Valid name, no data record of requested type (WSANO_DATA)";
529 #if 0
530     case 11004: /* typically, only WSANO_DATA is reported */
531         return "No address, look for MX record (WSANO_ADDRESS)";
532 #endif
533 #endif /* defined USE_WIN32 */
534     default:
535         return strerror(errnum);
536     }
537 }
538 
539 /* replace non-UTF-8 and non-printable control characters with '.' */
safestring(char * c)540 NOEXPORT void safestring(char *c) {
541     for(; *c; ++c)
542         if(!(*c&0x80 || isprint((int)*c)))
543             *c='.';
544 }
545 
546 /* provide hex string corresponding to the input string
547  * will be NULL terminated */
bin2hexstring(const unsigned char * in_data,size_t in_size,char * out_data,size_t out_size)548 void bin2hexstring(const unsigned char *in_data, size_t in_size, char *out_data, size_t out_size) {
549     const char hex[16]="0123456789ABCDEF";
550     size_t i;
551 
552     for(i=0; i<in_size && 2*i+2<out_size; ++i) {
553         out_data[2*i]=hex[in_data[i]>>4];
554         out_data[2*i+1]=hex[in_data[i]&0x0f];
555     }
556     out_data[2*i]='\0';
557 }
558 
559 /* end of log.c */
560