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, ×truct);
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