1 /*
2 * ufdbchkport.c - URLfilterDB
3 *
4 * ufdbGuard is copyrighted (C) 2005-2020 by URLfilterDB with all rights reserved.
5 *
6 * Parts of the ufdbGuard daemon are based on squidGuard.
7 * This module is NOT based on squidGuard.
8 *
9 * RCS $Id: ufdbchkport.c,v 1.123 2020/08/20 09:56:10 root Exp root $
10 */
11
12 /* gcc 4.8.5 has an optimizer bug with -O3 which generates incorrect code for the function UFDBsslPeekServer
13 * where inlined code overwrites the parameter "hostname" that is stored in register rbx.
14 * gcc 4.8.5 with -O2 does not have this bug since it does not inline.
15 * gcc 5.2.0 does not have this bug.
16 */
17 #if __GNUC__ && !__clang__
18
19 #if __GNUC__ <= 4
20 #pragma GCC optimize ("no-inline")
21 #define GCC_NO_INLINE __attribute__ ((noinline))
22 #else
23 #define GCC_NO_INLINE /**/
24 #endif
25
26 #else
27 #define GCC_NO_INLINE /**/
28 #endif
29
30 #include "ufdb.h"
31 #include "ufdblib.h"
32 #include "ufdblocks.h"
33 #include "ufdbdb.h"
34 #include "ufdbchkport.h"
35 #include "httpsQueue.h"
36
37 #include "ufdbHashtable.h"
38
39 #if !UFDB_SSL_SUPPORT
40 #error "UFDB_SSL_SUPPORT is not set"
41 #endif
42 #if !UFDB_PTHREAD_SUPPORT
43 #error "UFDB_PTHREAD_SUPPORT is not set"
44 #endif
45
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <strings.h>
49 #include <string.h>
50 #include <ctype.h>
51 #include <unistd.h>
52 #include <fcntl.h>
53 #include <errno.h>
54 #include <signal.h>
55 #include <syslog.h>
56 #include <sys/types.h>
57 #include <sys/stat.h>
58 #include <sys/socket.h>
59 #include <netinet/tcp.h>
60 #ifdef _POSIX_PRIORITY_SCHEDULING
61 #include <sched.h>
62 #endif
63
64 #include <sys/select.h>
65
66 #include <openssl/ssl.h>
67 #include <openssl/conf.h>
68 #include <openssl/crypto.h>
69 #include <openssl/engine.h>
70 #include <openssl/x509v3.h>
71 #include <openssl/err.h>
72
73
74 #ifdef __cplusplus
75 extern "C" {
76 #endif
77
78 #undef UFDB_HTTPS_CACHE_DEBUG
79 #if defined(UFDB_DEBUG)
80 #define UFDB_HTTPS_CACHE_DEBUG 1
81 #endif
82
83 #if UFDB_DO_DEBUG || 0
84 #define DEBUG(x) fprintf x
85 #else
86 #define DEBUG(x)
87 #endif
88
89
90 static volatile int tls_inited = 0;
91 static ufdb_mutex init_mutex = ufdb_mutex_initializer;
92 static SSL_CTX * ssl_ctx = NULL;
93
94 #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
95 static int num_static_ssl_locks = 0;
96 static ufdb_mutex * crypto_mutexes = NULL;
97 #endif
98
99 static struct UFDBhashtable * myht = NULL;
100 static int ufdbCacertsLoaded = 0;
101 static time_t lastHTTPScachePurgeTime = 0;
102 static time_t OldestInHTTPScache = 0;
103
104 static int UFDBverifyPortHasHTTPS( const char * hostname, int portnumber, int flags );
105 static int lookupHTTPScache( const char * hostname, int portnumber, int keepLockForInsert,
106 struct httpsInfo ** hinfo );
107 static void updateHTTPScache( const char * hostname, int portnumber, int status, int lockSetBySearch,
108 struct httpsInfo ** hinfo );
109 static void insertHTTPScache( const char * hostname, int portnumber, int status, int lockSetBySearch,
110 struct httpsInfo ** hinfo );
111
112 #ifdef UFDB_LOAD_CERT_CHAIN
113 static int UFDBloadIntermediateCertificates( X509 * cert );
114 #endif
115
116 #if 0 || defined(UFDB_DEBUG)
117
118 static ufdb_mutex https_mutex = ufdb_mutex_initializer;
119
120 #define SSL_MUTEX_FN_INIT int _mutex_retval;
121 #define SSL_MUTEX_LOCK(fn) \
122 { \
123 _mutex_retval = ufdb_mutex_lock( &https_mutex ); \
124 if (_mutex_retval != 0) \
125 ufdbLogError( fn ": mutex_lock failed with code %d", _mutex_retval ); \
126 }
127 #define SSL_MUTEX_UNLOCK(fn) \
128 { \
129 _mutex_retval = ufdb_mutex_unlock( &https_mutex ); \
130 if (_mutex_retval != 0) \
131 ufdbLogError( fn ": mutex_unlock failed with code %d", _mutex_retval ); \
132 }
133
134 #else
135 #define SSL_MUTEX_FN_INIT
136 #define SSL_MUTEX_LOCK(fn)
137 #define SSL_MUTEX_UNLOCK(fn)
138 #endif
139
140
141 #define UFDB_DEBUG_PROBES 1
142
143
144 /*
145 * Parsed OpenSSL version number.
146 */
147 typedef struct {
148 int major;
149 int minor;
150 int micro;
151 int patch;
152 int status;
153 } TLS_VINFO;
154
155 static int ssl_check_certificate( SSL * ssl, const char * hostname, int portnumber, char * cn );
156
157
hostnameIsIP(const char * hostname)158 static int hostnameIsIP( const char * hostname )
159 {
160 const char * h;
161
162 if (hostname == NULL)
163 return 0;
164
165 /* check IPv4 */
166 h = hostname;
167 while (*h != '\0')
168 {
169 if (*h != '.' && !isdigit(*h))
170 {
171 /* check for IPv6 */
172 h = hostname;
173 while (*h != '\0')
174 {
175 if (*h != ':' && !isxdigit(*h))
176 return 0;
177 h++;
178 }
179 return 1;
180 }
181 h++;
182 }
183
184 return 1;
185 }
186
187
188 #if UFDB_SSL_SUPPORT
detectSkypeSN(const char * hostname,int portnumber)189 static int detectSkypeSN(
190 const char * hostname,
191 int portnumber )
192 {
193 int n;
194 int s;
195 struct timeval tv;
196 unsigned char snbuffer[100] = {
197 0x80, 0x46, 0x01, 0x03, 0x01, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x05, 0x00, 0x00,
198 0x04, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, 0x00, 0x64, 0x00, 0x00, 0x62, 0x00, 0x00, 0x08,
199 0x00, 0x00, 0x03, 0x00, 0x00, 0x06, 0x01, 0x00, 0x80, 0x07, 0x00, 0xc0, 0x03, 0x00, 0x80, 0x06,
200 0x00, 0x40, 0x02, 0x00, 0x80, 0x04, 0x00, 0x80,
201 0x16, 0x03, 0x01, 0x00, 0x00, 0x06, 0xa3, 0x66, 0x09, 0x33, 0x5d, 0x3f, 0xf2, 0xb4, 0xca, 0x44
202 };
203 unsigned char expected[11] = {
204 0x16, 0x03, 0x01, 0x00, 0x4a, 0x02, 0x00, 0x00, 0x46, 0x03, 0x01 /* , 0x4d, 0xbe, 0xde */
205 };
206
207 if (ufdbGV.debug || ufdbGV.debugSkype)
208 ufdbLogMessage( " probing for Skype node on %s:%d ...", hostname, portnumber );
209
210 s = UFDBopenSocket( hostname, portnumber );
211 if (s < 0)
212 {
213 if (ufdbGV.debugSkype)
214 ufdbLogMessage( " socket open failed for %s:%d", hostname, portnumber );
215 return UFDB_API_ERR_SOCKET;
216 }
217
218 tv.tv_sec = 3;
219 tv.tv_usec = 0;
220 setsockopt( s, SOL_SOCKET, SO_RCVTIMEO, (void *) &tv, sizeof(tv) );
221 tv.tv_sec = 3;
222 tv.tv_usec = 0;
223 setsockopt( s, SOL_SOCKET, SO_SNDTIMEO, (void *) &tv, sizeof(tv) );
224
225 /* Skype node detection */
226
227 if (72 != write( s, snbuffer, 72 ))
228 {
229 if (ufdbGV.debug || ufdbGV.debugSkype)
230 ufdbLogMessage( " write failed for %s:%d: %s", hostname, portnumber, strerror(errno) );
231 close( s );
232 return UFDB_API_ERR_READ;
233 }
234
235 n = read( s, snbuffer, 50 );
236 close( s );
237
238 #if UFDB_DEBUG_PROBES
239 if (ufdbGV.debugSkype)
240 {
241 int i;
242 if (ufdbGV.debug == 0)
243 if (n > 20)
244 n = 20;
245 for (i = 0; i < n; i++)
246 ufdbLogMessage( " skype node probe reply byte %2d, 0x%02x %c", i, snbuffer[i], (snbuffer[i] & 0x7f)|0x20 );
247 }
248 #endif
249 if (strncmp( (char *) snbuffer, "SSH", 3 ) == 0)
250 {
251 ufdbLogMessage( "%s:%d responded with SSH so it has a TUNNEL.", hostname, portnumber );
252 return UFDB_API_ERR_TUNNEL;
253 }
254
255 if (n >= (int) sizeof(expected) && memcmp( snbuffer, expected, sizeof(expected) ) == 0)
256 {
257 if (ufdbGV.debug || ufdbGV.debugSkype)
258 ufdbLogMessage( " received Skype Node Reply Message from %s:%d", hostname, portnumber );
259 return UFDB_API_ERR_IS_SKYPE;
260 }
261
262 if (n < 0)
263 {
264 if (ufdbGV.debugSkype)
265 ufdbLogMessage( " read failed from %s:%d: n=%d, %s", hostname, portnumber, n, strerror(errno) );
266 return UFDB_API_ERR_READ;
267 }
268 else
269 {
270 if (ufdbGV.debugSkype)
271 ufdbLogMessage( " got reply of %d bytes to Skype Node probe from %s:%d", n, hostname, portnumber );
272 #if UFDB_DEBUG_PROBES
273 if (ufdbGV.debugSkype)
274 {
275 int i;
276 if (ufdbGV.debug == 0)
277 if (n > 20)
278 n = 20;
279 for (i = 0; i < n; i++)
280 ufdbLogMessage( " reply byte %2d, 0x%02x %c", i, snbuffer[i], (snbuffer[i] & 0x7f)|0x20 );
281 }
282 #endif
283 }
284
285 return UFDB_API_OK;
286 }
287 #endif
288
289
TLSalert2string(int alertCode)290 static const char * TLSalert2string( int alertCode )
291 {
292 switch (alertCode)
293 {
294 case 0: return "close notify";
295 case 10: return "unexpected message";
296 case 20: return "bad record mac";
297 case 21: return "decryption failed";
298 case 22: return "record overflow";
299 case 30: return "decompression failure";
300 case 40: return "handshake failure";
301 case 41: return "no certificate";
302 case 42: return "bad certificate";
303 case 43: return "unsupported certificate";
304 case 44: return "certificate revoked";
305 case 45: return "certificate expired";
306 case 46: return "certificate unknown";
307 case 47: return "illegal parameter";
308 case 48: return "unknown ca";
309 case 49: return "access denied";
310 case 50: return "decode error";
311 case 51: return "decrypt error";
312 case 60: return "export restriction";
313 case 70: return "protocol version";
314 case 71: return "insufficient security";
315 case 80: return "internal error";
316 case 90: return "user canceled";
317 case 100: return "no renegotiation";
318 case 110: return "unsupported extension";
319 case 111: return "certificate unobtainable";
320 case 112: return "unrecognized name";
321 case 113: return "bad certificate status response";
322 case 114: return "bad certificate hash value";
323 }
324 return "unknown";
325 }
326
327
328 /*
329 * Detect Gtalk protocol for hostname:port.
330 * example of gtalk server on port 443: 209.85.157.126
331 * returns UFDB_API_OK, UFBD_API_ERR_IS_GTALK, UFDB_API_ERR_TUNNEL or UFDB_API_ERR_SOCKET.
332 */
333 #if UFDB_SSL_SUPPORT
UFDBdetectGtalk(int worker,const char * hostname,int portnumber)334 int UFDBdetectGtalk(
335 int worker,
336 const char * hostname,
337 int portnumber )
338 {
339 int s;
340 int n;
341 struct timeval tv;
342 unsigned char loginbuffer[] = {
343 0x80, 0x46, 0x01, 0x03, 0x01, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x10, 0x01, 0x00, 0x80, 0x03, 0x00,
344 0x80, 0x07, 0x00, 0xc0, 0x06, 0x00, 0x40, 0x02, 0x00, 0x80, 0x04, 0x00, 0x80, 0x00, 0x00, 0x04,
345 0x00, 0xfe, 0xff, 0x00, 0x00, 0x0a, 0x00, 0xfe, 0xfe, 0x00, 0x00, 0x09, 0x00, 0x00, 0x64, 0x00,
346 0x00, 0x62, 0x00, 0x00, 0x03, 0x00, 0x00, 0x06,
347 0x1f, 0x17, 0x0c, 0xa6, 0x2f, 0x00, 0x78, 0xfc, 0x46, 0x55, 0x2e, 0xb1, 0x83, 0x39, 0xf1, 0xea
348 };
349 unsigned char expected[30] = {
350 0x16, 0x03, 0x01, 0x00, 0x4a, 0x02, 0x00, 0x00, 0x46, 0x03, 0x01, 0x42, 0x85, 0x45, 0xa7, 0x27,
351 0xa9, 0x5d, 0xa0, 0xb3, 0xc5, 0xe7, 0x53, 0xda, 0x48, 0x2b, 0x3f, 0xc6, 0x5a, 0xca
352 };
353 unsigned char TLSalert[6] = {
354 0x15, 0x03, 0x01, 0x00, 0x02, 0x02
355 };
356 unsigned char reply[300];
357
358 if (ufdbGV.debug || ufdbGV.debugGtalk)
359 ufdbLogMessage( "W%03d: probing for Gtalk on %s:%d ...", worker, hostname, portnumber );
360
361 s = UFDBopenSocket( hostname, portnumber );
362 if (s < 0)
363 {
364 if (ufdbGV.debug || ufdbGV.debugGtalk)
365 ufdbLogMessage( "W%03d: socket open failed for %s:%d", worker, hostname, portnumber );
366 return UFDB_API_ERR_SOCKET;
367 }
368
369 tv.tv_sec = 3;
370 tv.tv_usec = 0;
371 setsockopt( s, SOL_SOCKET, SO_RCVTIMEO, (void *) &tv, sizeof(tv) );
372 tv.tv_sec = 3;
373 tv.tv_usec = 0;
374 setsockopt( s, SOL_SOCKET, SO_SNDTIMEO, (void *) &tv, sizeof(tv) );
375
376 if (72 != write( s, loginbuffer, 72 ))
377 {
378 if (ufdbGV.debug || ufdbGV.debugGtalk)
379 ufdbLogMessage( "W%03d: write to %s:%d failed: %s", worker, hostname, portnumber, strerror(errno) );
380 close( s );
381 return UFDB_API_ERR_SOCKET;
382 }
383
384 n = read( s, reply, sizeof(reply) );
385 close( s );
386
387 if (n >= (int) sizeof(expected) && memcmp( reply, expected, sizeof(expected) ) == 0)
388 {
389 if (ufdbGV.debug || ufdbGV.debugGtalk)
390 ufdbLogMessage( "W%03d: received Gtalk Reply Message from %s:%d", worker, hostname, portnumber );
391 return UFDB_API_ERR_IS_GTALK;
392 }
393
394 if (n >= (int) sizeof(TLSalert)+1 && memcmp( reply, TLSalert, sizeof(TLSalert) ) == 0)
395 {
396 ufdbLogMessage( "W%03d: received a TLS protocol alert %d (%s) from %s:%d",
397 worker, (int) reply[6], TLSalert2string((int) reply[6]), hostname, portnumber );
398 return UFDB_API_ERR_TLS;
399 }
400
401 if (n < 0)
402 {
403 if (ufdbGV.debugGtalk)
404 ufdbLogMessage( "W%03d: read from %s:%d failed: n=%d, %s",
405 worker, hostname, portnumber, n, strerror(errno) );
406 return UFDB_API_ERR_READ;
407 }
408 else
409 {
410 if (ufdbGV.debugGtalk)
411 ufdbLogMessage( "W%03d: Got reply of %d bytes to Gtalk probe message from %s:%d.",
412 worker, n, hostname, portnumber );
413
414 if (strncmp( (char *) reply, "HTTP/", 5 ) == 0 && (reply[5] >= '0' && reply[5] <= '2'))
415 {
416 ufdbLogMessage( "W%03d: received a plain HTTP message from %s:%d", worker, hostname, portnumber );
417 return UFDB_API_ERR_TLS;
418 }
419 else if (strncmp( (char *) reply, "SSH", 3 ) == 0)
420 {
421 ufdbLogMessage( "W%03d: %s:%d responded with SSH so it has a TUNNEL.", worker, hostname, portnumber );
422 return UFDB_API_ERR_TUNNEL;
423 }
424 else if (n > 80)
425 {
426 reply[ sizeof(reply)-1 ] = '\0';
427 if (strncasecmp( (char*) reply, "<?xml", 5 ) == 0 &&
428 strstr( (char*) reply, "<stream:stream" ) != NULL &&
429 strstr( (char*) reply, "from=\"chat.facebook.com" ) != NULL)
430 {
431 if (ufdbGV.debug || ufdbGV.debugFBchat)
432 ufdbLogMessage( "W%03d: received Facebook Chat Reply Message from %s:%d",
433 worker, hostname, portnumber );
434 return UFDB_API_ERR_IS_FBCHAT;
435 }
436 }
437 #if UFDB_DEBUG_PROBES
438 else
439 {
440 /* it is not a Gtalk server, print the reply */
441 if (ufdbGV.debugGtalk)
442 {
443 int i;
444 if (ufdbGV.debug == 0)
445 if (n > 200)
446 n = 200;
447 for (i = 0; i < n; i++)
448 ufdbLogMessage( "W%03d: Gtalk probe reply byte %2d, 0x%02x %c",
449 worker, i, reply[i], (reply[i] & 0x7f)|0x20 );
450 }
451 }
452 #endif
453 }
454
455 return UFDB_API_OK;
456 }
457 #endif
458
459
460 /*
461 * Detect Skype protocol for hostname:port.
462 * returns UFDB_API_OK, UFBD_API_ERR_IS_SKYPE, UFDB_API_ERR_TUNNEL or UFDB_API_ERR_SOCKET.
463 */
464 #if UFDB_SSL_SUPPORT
UFDBdetectSkype(int worker,const char * hostname,int portnumber)465 int UFDBdetectSkype(
466 int worker,
467 const char * hostname,
468 int portnumber )
469 {
470 #if UFDB_PROBE_SKYPE_LOGIN_NODE
471 int n;
472 int s;
473 struct timeval tv;
474 unsigned char loginbuffer[100] = {
475 0x16, 0x03, 0x01, 0x00, 0x00,
476 0x80, 0x46, 0x01, 0x03, 0x01, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x05, 0x00, 0x00,
477 0x04, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, 0x00, 0x64, 0x00, 0x00, 0x62, 0x00, 0x00, 0x08,
478 0x00, 0x00, 0x03, 0x00, 0x00, 0x06, 0x01, 0x00, 0x80, 0x07, 0x00, 0xc0, 0x03, 0x00, 0x80, 0x06,
479 0x00, 0x40, 0x02, 0x00, 0x80, 0x04, 0x00, 0x80,
480 0x16, 0x03, 0x01, 0x00, 0x00, 0x06, 0xa3, 0x66, 0x09, 0x33, 0x5d, 0x3f, 0xf2, 0xb4, 0xca, 0x44
481 };
482 unsigned char expected[5] = {
483 0x17, 0x03, 0x01, 0x00, 0x00
484 };
485
486 if (ufdbGV.debug || ufdbGV.debugSkype)
487 ufdbLogMessage( "W%03d: probing for Skype login on %s:%d ...", worker, hostname, portnumber );
488
489 s = UFDBopenSocket( hostname, portnumber );
490 if (s < 0)
491 {
492 if (ufdbGV.debug || ufdbGV.debugSkype)
493 ufdbLogMessage( "W%03d: socket open failed for %s:%d", worker, hostname, portnumber );
494 return UFDB_API_ERR_SOCKET;
495 }
496
497 tv.tv_sec = 3;
498 tv.tv_usec = 0;
499 setsockopt( s, SOL_SOCKET, SO_RCVTIMEO, (void *) &tv, sizeof(tv) );
500 tv.tv_sec = 3;
501 tv.tv_usec = 0;
502 setsockopt( s, SOL_SOCKET, SO_SNDTIMEO, (void *) &tv, sizeof(tv) );
503
504 /* Skype login server detection */
505
506 if (72 != write( s, loginbuffer, 72 ))
507 {
508 if (ufdbGV.debug || ufdbGV.debugSkype)
509 ufdbLogMessage( "W%03d: write to %s:%d failed: %s",
510 worker, hostname, portnumber, strerror(errno) );
511 close( s );
512 return UFDB_API_ERR_READ;
513 }
514
515 n = read( s, loginbuffer, 30 );
516 close( s );
517
518 #if UFDB_DEBUG_PROBES
519 if (ufdbGV.debugSkype)
520 {
521 int i;
522 if (ufdbGV.debug == 0)
523 if (n > 20)
524 n = 20;
525 for (i = 0; i < n; i++)
526 ufdbLogMessage( "W%03d: skype server probe reply byte %2d, 0x%02x %c",
527 worker, i, loginbuffer[i], (loginbuffer[i] & 0x7f)|0x20 );
528 }
529 #endif
530
531 if (n >= (int) sizeof(expected) && memcmp( loginbuffer, expected, sizeof(expected) ) == 0)
532 {
533 if (ufdbGV.debug || ufdbGV.debugSkype)
534 ufdbLogMessage( "W%03d: received Skype Login Reply Message from %s:%d", worker, hostname, portnumber );
535 return UFDB_API_ERR_IS_SKYPE;
536 }
537
538 if (n < 0)
539 {
540 if (ufdbGV.debugSkype)
541 ufdbLogMessage( "W%03d: read from %s:%d failed: n=%d, %s",
542 worker, hostname, portnumber, n, strerror(errno) );
543 return UFDB_API_ERR_READ;
544 }
545 else
546 {
547 if (ufdbGV.debugSkype)
548 ufdbLogMessage( "W%03d: Got reply of %d bytes to Skype login probe from %s:%d.",
549 worker, n, hostname, portnumber );
550
551 if (strncmp( (char *) loginbuffer, "SSH", 3 ) == 0)
552 {
553 ufdbLogMessage( "W%03d: %s:%d responded with SSH so it has a TUNNEL.", worker, hostname, portnumber );
554 return UFDB_API_ERR_TUNNEL;
555 }
556 else
557 {
558 /* it is not a Skype login server, try to detect a Skype node */
559 return detectSkypeSN( hostname, portnumber );
560 }
561 }
562
563 #else
564 /* !UFDB_PROBE_SKYPE_LOGIN_NODE */
565 if (worker) // prevent compiler warning
566 { ; }
567 return detectSkypeSN( hostname, portnumber );
568 #endif
569
570 return UFDB_API_OK;
571 }
572 #endif
573
574
575 #if UFDB_SSL_SUPPORT
UFDBdetectSSH(int worker,const char * hostname,int portnumber)576 int UFDBdetectSSH(
577 int worker,
578 const char * hostname,
579 int portnumber )
580 {
581 int n;
582 int s;
583 struct timeval tv;
584 char loginbuffer[8];
585
586 if (ufdbGV.debug)
587 ufdbLogMessage( "W%03d: probing for SSH server on %s:%d ...", worker, hostname, portnumber );
588
589 s = UFDBopenSocket( hostname, portnumber );
590 if (s < 0)
591 {
592 if (ufdbGV.debug)
593 ufdbLogMessage( "W%03d: opening of socket failed for %s:%d", worker, hostname, portnumber );
594 return UFDB_API_ERR_SOCKET;
595 }
596
597 tv.tv_sec = 3;
598 tv.tv_usec = 0;
599 setsockopt( s, SOL_SOCKET, SO_RCVTIMEO, (void *) &tv, sizeof(tv) );
600 tv.tv_sec = 3;
601 tv.tv_usec = 0;
602 setsockopt( s, SOL_SOCKET, SO_SNDTIMEO, (void *) &tv, sizeof(tv) );
603
604 /* SSH server detection */
605
606 n = read( s, loginbuffer, 4 );
607 close( s );
608
609 if (n >= 3 && strncmp( loginbuffer, "SSH", 3 ) == 0)
610 {
611 if (ufdbGV.debug)
612 ufdbLogMessage( "W%03d: received SSH Server Message from %s:%d", worker, hostname, portnumber );
613 return UFDB_API_ERR_TUNNEL;
614 }
615
616 return UFDB_API_OK;
617 }
618 #endif
619
620
621 UFDB_GCC_INLINE
sslerr2str(int errnum)622 static const char * sslerr2str( int errnum )
623 {
624 switch (errnum)
625 {
626 case SSL_ERROR_NONE: return "SSL_ERROR_NONE";
627 case SSL_ERROR_SSL: return "SSL_ERROR_SSL";
628 case SSL_ERROR_WANT_READ: return "SSL_ERROR_WANT_READ";
629 case SSL_ERROR_WANT_WRITE: return "SSL_ERROR_WANT_WRITE";
630 case SSL_ERROR_WANT_X509_LOOKUP: return "SSL_ERROR_WANT_X509_LOOKUP";
631 case SSL_ERROR_SYSCALL: return "SSL_ERROR_SYSCALL";
632 case SSL_ERROR_ZERO_RETURN: return "SSL_ERROR_ZERO_RETURN";
633 case SSL_ERROR_WANT_CONNECT: return "SSL_ERROR_WANT_CONNECT";
634 case SSL_ERROR_WANT_ACCEPT: return "SSL_ERROR_WANT_ACCEPT";
635 }
636 return "unknown";
637 }
638
639
640 #if UFDB_SSL_SUPPORT
probePort(int worker,const char * hostname,int portnumber)641 GCC_NO_INLINE static int probePort(
642 int worker,
643 const char * hostname,
644 int portnumber )
645 {
646 int connect_status;
647 int otherStat;
648
649 if (ufdbGV.debug)
650 ufdbLogMessage( "W%03d: probePort %s:%d", worker, hostname, portnumber );
651
652 otherStat = UFDB_API_OK;
653
654 if (hostnameIsIP(hostname))
655 {
656 otherStat = UFDBdetectGtalk( worker, hostname, portnumber );
657 if (otherStat == UFDB_API_ERR_IS_GTALK)
658 {
659 connect_status = otherStat;
660 ufdbLogMessage( "W%03d: Gtalk detected on %s:%d", worker, hostname, portnumber );
661 goto done;
662 }
663 else if (otherStat == UFDB_API_ERR_IS_FBCHAT)
664 {
665 connect_status = otherStat;
666 ufdbLogMessage( "W%03d: Facebook Chat detected on %s:%d", worker, hostname, portnumber );
667 goto done;
668 }
669 else if (otherStat == UFDB_API_ERR_TUNNEL)
670 {
671 connect_status = otherStat;
672 ufdbLogMessage( "W%03d: Tunnel detected on %s:%d", worker, hostname, portnumber );
673 goto done;
674 }
675 else if (otherStat == UFDB_API_ERR_TLS)
676 {
677 connect_status = otherStat;
678 goto done;
679 }
680
681 if (otherStat == UFDB_API_ERR_SOCKET)
682 {
683 connect_status = otherStat;
684 goto done;
685 }
686 else
687 {
688 if (ufdbGV.reconfig || ufdbGV.terminating)
689 {
690 ufdbLogMessage( "W%03d: aborting HTTPS probe because ufdbguardd is reloading its configuration",
691 worker );
692 connect_status = UFDB_API_ERR_SOCKET;
693 goto done;
694 }
695
696 otherStat = UFDBdetectSkype( worker, hostname, portnumber );
697 if (otherStat == UFDB_API_ERR_IS_SKYPE)
698 {
699 connect_status = otherStat;
700 ufdbLogMessage( "W%03d: Skype detected on %s:%d", worker, hostname, portnumber );
701 goto done;
702 }
703 else if (otherStat == UFDB_API_ERR_TUNNEL)
704 {
705 connect_status = otherStat;
706 ufdbLogMessage( "W%03d: Tunnel detected on %s:%d", worker, hostname, portnumber );
707 goto done;
708 }
709 }
710 }
711
712 /* NOTE: UFDBdetectSkype() and UFDBdetectGtalk() also detect SSH tunnels,
713 * so we only call UFDBdetectSSH() when hostname is not an IP address.
714 */
715 if (otherStat != UFDB_API_ERR_SOCKET &&
716 !hostnameIsIP(hostname) &&
717 !ufdbGV.reconfig && !ufdbGV.terminating &&
718 UFDBdetectSSH( worker, hostname, portnumber ) == UFDB_API_ERR_TUNNEL)
719 {
720 ufdbLogMessage( "W%03d: SSH detected on %s:%d", worker, hostname, portnumber );
721 connect_status = UFDB_API_ERR_TUNNEL;
722 goto done;
723 }
724
725 /* we have an unknown protocol. It is dealt with below */
726
727 if (ufdbGV.unknownProtocolOverHttps)
728 {
729 connect_status = UFDB_API_ERR_UNKNOWN_PROTOCOL;
730 ufdbLogMessage( "W%03d: Unknown Protocol is used on %s:%d", worker, hostname, portnumber );
731 goto done;
732 }
733 else
734 {
735 /* There is an unknown protocol and the option allow-unknown-protocol-over-https is off */
736 connect_status = UFDB_API_ERR_TUNNEL;
737 ufdbLogMessage( "W%03d: forbidden Unknown Protocol used on %s:%d so it is flagged as a TUNNEL *****",
738 worker, hostname, portnumber );
739 goto done;
740 }
741
742 return UFDB_API_OK;
743
744 done:
745 return connect_status;
746 }
747 #endif
748
749
UFDBopenssl_read(char * buf,int bufsize,SSL * ssl)750 GCC_NO_INLINE int UFDBopenssl_read( char * buf, int bufsize, SSL * ssl )
751 {
752 SSL_MUTEX_FN_INIT
753 int ret;
754
755 SSL_MUTEX_LOCK( "UFDBopenssl_read" )
756
757 errno = 0;
758 ret = -2;
759 do
760 {
761 ret = SSL_read( ssl, buf, bufsize );
762 }
763 while (ret == -1 &&
764 SSL_get_error(ssl, ret) == SSL_ERROR_SYSCALL &&
765 errno == EINTR);
766
767 SSL_MUTEX_UNLOCK( "UFDBopenssl_read" )
768
769 return ret;
770 }
771
772
UFDBopenssl_write(char * buf,int bufsize,SSL * ssl)773 GCC_NO_INLINE int UFDBopenssl_write( char * buf, int bufsize, SSL * ssl )
774 {
775 SSL_MUTEX_FN_INIT
776 int ret;
777
778 SSL_MUTEX_LOCK( "UFDBopenssl_write" )
779
780 errno = 0;
781 do
782 {
783 ret = SSL_write( ssl, buf, bufsize );
784 }
785 while (ret == -1 &&
786 SSL_get_error(ssl,ret) == SSL_ERROR_SYSCALL &&
787 errno == EINTR);
788
789 SSL_MUTEX_UNLOCK( "UFDBopenssl_write" )
790
791 return ret;
792 }
793
794
UFDBopenssl_close(SSL * ssl)795 GCC_NO_INLINE void UFDBopenssl_close( SSL * ssl )
796 {
797 SSL_MUTEX_FN_INIT
798
799 if (ssl == NULL)
800 return;
801
802 SSL_MUTEX_LOCK( "openssl_close" )
803
804 #if 0
805 ERR_clear_error();
806 #endif
807 if (SSL_shutdown( ssl ) < 0)
808 ERR_clear_error();
809 SSL_free( ssl );
810
811 SSL_MUTEX_UNLOCK( "openssl_close" )
812 }
813
814
print_errors(void)815 static void print_errors( void )
816 {
817 unsigned long sslerr;
818 const char * file;
819 int line;
820 const char * data;
821 int flags;
822 int header = 0;
823 char errstring[256];
824
825 header = 0;
826 while ((sslerr = ERR_get_error_line_data( &file, &line, &data, &flags )) != 0)
827 {
828 if (!header)
829 {
830 ufdbLogError( "TLS/SSL connection failed with OpenSSL error:" );
831 header = 1;
832 }
833
834 ERR_error_string_n( sslerr, errstring, sizeof(errstring) );
835 ufdbLogMessage( " SSL error: %s", errstring );
836 }
837 }
838
839
UFDBloadAllowedHTTPSsites(char * filename)840 int UFDBloadAllowedHTTPSsites(
841 char * filename )
842 {
843 if (filename == NULL) // prevent compiler warning
844 { ; }
845
846 return UFDB_API_OK;
847 }
848
849
purgeHTTPSinfo(const void * key,const void * value)850 static int purgeHTTPSinfo( const void * key, const void * value )
851 {
852 struct httpsInfo * info;
853
854 info = (struct httpsInfo *) value;
855 if (info->t < OldestInHTTPScache)
856 {
857 if (ufdbGV.debug > 1)
858 ufdbLogMessage( " key %s purged", (char*) key );
859 ufdbFree( info->content );
860 /* caller frees key and value */
861 return 1;
862 }
863
864 return 0;
865 }
866
867
868 #ifdef UFDB_LOAD_CERT_CHAIN
UFDBread(int fd,char * buffer,int max)869 static int UFDBread(
870 int fd,
871 char * buffer,
872 int max )
873 {
874 int n;
875 int total;
876
877 total = 0;
878 while ((n = read( fd, buffer, max )) > 0)
879 {
880 total += n;
881 buffer += n;
882 max -= n;
883 }
884 if (total == 0)
885 return n;
886 return total;
887 }
888 #endif
889
890
hostname2hash(const void * key)891 static unsigned int hostname2hash(
892 const void * key )
893 {
894 unsigned int hash;
895 const char * hostname = (const char *) key;
896
897 hash = 0x55;
898 while (*hostname != '\0')
899 {
900 hash = ((hash << 4) ^ ((unsigned int) (*hostname))) - 17;
901 hostname++;
902 }
903
904 return hash & 0x7FFFFFFF;
905 }
906
907
mystrcmp(const void * a,const void * b)908 static int mystrcmp( const void * a, const void * b )
909 {
910 if (a == NULL || b == NULL)
911 {
912 ufdbLogError( "mystrcmp: a and/or b is NULL" );
913 return 0;
914 }
915 return strcmp( (const char *) a, (const char *) b ) == 0;
916 }
917
918
919 #if 0
920 void UFDBsetTunnelCheckMethod( int method )
921 {
922 ufdbGV.tunnelCheckMethod = method;
923 }
924 #endif
925
926
927 #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
ufdb_crypto_locking_cb(int mode,int type,const char * file,int line)928 static void ufdb_crypto_locking_cb(
929 int mode,
930 int type,
931 const char * file,
932 int line )
933 {
934 int ret; /* must ignore warning about variable set but not used */
935
936 #if 0
937 if (ufdbGV.debug)
938 ufdbLogMessage( " CRYPTO lock %6s %s%s %2d",
939 (mode & CRYPTO_LOCK) ? "lock" : "unlock",
940 (mode & CRYPTO_READ) ? "read" : "",
941 (mode & CRYPTO_WRITE) ? "write" : "",
942 type );
943 #endif
944
945 if (type < 0 || type > num_static_ssl_locks)
946 {
947 ufdbLogMessage( "CRYPTO lock type %d out of range (max=%d) *****", type, num_static_ssl_locks );
948 return;
949 }
950
951 if (mode & CRYPTO_LOCK)
952 {
953 ret = ufdb_mutex_lock( &crypto_mutexes[type] );
954 if (ret != 0)
955 #ifdef UFDB_DEBUG
956 ufdbLogError( "ufdb_crypto_locking_cb: mutex_lock[%d] failed with code %d", type, ret );
957 #else
958 { ; }
959 #endif
960 }
961 else if (mode & CRYPTO_UNLOCK)
962 {
963 ret = ufdb_mutex_unlock( &crypto_mutexes[type] );
964 if (ret != 0)
965 #ifdef UFDB_DEBUG
966 ufdbLogError( "ufdb_crypto_locking_cb: mutex_unlock[%d] failed with code %d", type, ret );
967 #else
968 { ; }
969 #endif
970 }
971 else
972 ufdbLogError( "ufdb_crypto_locking_cb: no LOCK|UNLOCK for type %d", type );
973 }
974 #endif
975
976
977 #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
ufdb_pthread_id_callback(void)978 static unsigned long ufdb_pthread_id_callback( void )
979 {
980 unsigned long id = (unsigned long) pthread_self();
981
982 return id;
983 }
984 #endif
985
986
987 #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
988
989 typedef struct CRYPTO_dynlock_value {
990 pthread_mutex_t lock;
991 } CRYPTO_dynlock_value;
992
993
ufdb_ssl_dyn_create(const char * file,int line)994 static CRYPTO_dynlock_value * ufdb_ssl_dyn_create(
995 const char * file,
996 int line )
997 {
998 CRYPTO_dynlock_value * lp;
999
1000 lp = (CRYPTO_dynlock_value *) ufdbMalloc( sizeof(CRYPTO_dynlock_value) );
1001 pthread_mutex_init( &(lp->lock), NULL );
1002 return lp;
1003 }
1004
1005
ufdb_ssl_dyn_lock(int mode,CRYPTO_dynlock_value * lp,const char * file,int line)1006 static void ufdb_ssl_dyn_lock(
1007 int mode,
1008 CRYPTO_dynlock_value * lp,
1009 const char * file,
1010 int line )
1011 {
1012 int ret; /* must ignore warning about variable set but not used */
1013
1014 #if 0
1015 if (ufdbGV.debug)
1016 {
1017 ufdbLogMessage( " ufdb_ssl_dyn_lock %6s %s%s",
1018 (mode & CRYPTO_LOCK) ? "lock" : "unlock",
1019 (mode & CRYPTO_READ) ? "read" : "",
1020 (mode & CRYPTO_WRITE) ? "write" : "" );
1021 }
1022 #endif
1023
1024 if (mode & CRYPTO_LOCK)
1025 {
1026 ret = pthread_mutex_lock( &(lp->lock) );
1027 if (ret != 0)
1028 #ifdef UFDB_DEBUG
1029 ufdbLogError( "ufdb_ssl_dyn_lock: mutex_lock failed with code %d", ret );
1030 #else
1031 { ; }
1032 #endif
1033 }
1034 else if (mode & CRYPTO_UNLOCK)
1035 {
1036 ret = pthread_mutex_unlock( &(lp->lock) );
1037 if (ret != 0)
1038 #ifdef UFDB_DEBUG
1039 ufdbLogError( "ufdb_ssl_dyn_lock: mutex_unlock failed with code %d", ret );
1040 #else
1041 { ; }
1042 #endif
1043 }
1044 else
1045 ufdbLogError( "ufdb_ssl_dyn_lock: no LOCK|UNLOCK in mode" );
1046 }
1047
1048
ufdb_ssl_dyn_destroy(CRYPTO_dynlock_value * lp,const char * file,int line)1049 static void ufdb_ssl_dyn_destroy(
1050 CRYPTO_dynlock_value * lp,
1051 const char * file,
1052 int line )
1053 {
1054 pthread_mutex_destroy( &(lp->lock) );
1055 }
1056 #endif
1057
1058
1059 #if UFDB_USE_EXPORT_CIPHERS
1060 /* tmp_rsa_cb - call-back to generate ephemeral RSA key */
tmp_rsa_cb(SSL * unused_ssl,int unused_export,int keylength)1061 static RSA * tmp_rsa_cb(
1062 SSL * unused_ssl,
1063 int unused_export,
1064 int keylength )
1065 {
1066 static RSA * rsa_tmp;
1067
1068 /* Code adapted from OpenSSL apps/s_cb.c */
1069
1070 if (rsa_tmp == 0)
1071 rsa_tmp = RSA_generate_key( keylength, RSA_F4, NULL, NULL );
1072 return rsa_tmp;
1073 }
1074 #endif
1075
1076
1077 /* openssl_bug_bits - SSL bug compatibility bits for this OpenSSL version */
openssl_bug_bits(void)1078 static long openssl_bug_bits( void )
1079 {
1080 long bits = SSL_OP_ALL; /* Work around all known bugs */
1081
1082 return bits;
1083 }
1084
1085
1086 /* tls_version_split - Split OpenSSL version number into major, minor, ... */
1087
tls_version_split(long version,TLS_VINFO * info)1088 static void tls_version_split(
1089 long version,
1090 TLS_VINFO * info )
1091 {
1092 /*
1093 * OPENSSL_VERSION_NUMBER(3):
1094 *
1095 * OPENSSL_VERSION_NUMBER is a numeric release version identifier:
1096 *
1097 * MMNNFFPPS: major minor fix patch status
1098 *
1099 * The status nibble has one of the values 0 for development, 1 to e for
1100 * betas 1 to 14, and f for release. Parsed OpenSSL version number. for
1101 * example
1102 *
1103 * 0x000906000 == 0.9.6 dev
1104 * 0x000906023 == 0.9.6b beta 3
1105 * 0x00090605f == 0.9.6e release
1106 *
1107 * Versions prior to 0.9.3 have identifiers < 0x0930. Versions between
1108 * 0.9.3 and 0.9.5 had a version identifier with this interpretation:
1109 *
1110 * MMNNFFRBB major minor fix final beta/patch
1111 *
1112 * for example
1113 *
1114 * 0x000904100 == 0.9.4 release
1115 * 0x000905000 == 0.9.5 dev
1116 *
1117 * Version 0.9.5a had an interim interpretation that is like the current
1118 * one, except the patch level got the highest bit set, to keep continu-
1119 * ity. The number was therefore 0x0090581f.
1120 */
1121
1122 if (version < 0x0930) {
1123 info->status = 0;
1124 info->patch = version & 0x0f;
1125 version >>= 4;
1126 info->micro = version & 0x0f;
1127 version >>= 4;
1128 info->minor = version & 0x0f;
1129 version >>= 4;
1130 info->major = version & 0x0f;
1131 } else if (version < 0x00905800L) {
1132 info->patch = version & 0xff;
1133 version >>= 8;
1134 info->status = version & 0xf;
1135 version >>= 4;
1136 info->micro = version & 0xff;
1137 version >>= 8;
1138 info->minor = version & 0xff;
1139 version >>= 8;
1140 info->major = version & 0xff;
1141 } else {
1142 info->status = version & 0xf;
1143 version >>= 4;
1144 info->patch = version & 0xff;
1145 version >>= 8;
1146 info->micro = version & 0xff;
1147 version >>= 8;
1148 info->minor = version & 0xff;
1149 version >>= 8;
1150 info->major = version & 0xff;
1151 if (version < 0x00906000L)
1152 info->patch &= ~0x80;
1153 }
1154 }
1155
1156
OpenSSLversionCheck(void)1157 static void OpenSSLversionCheck( void )
1158 {
1159 TLS_VINFO hdr_info;
1160 TLS_VINFO lib_info;
1161
1162 tls_version_split( OPENSSL_VERSION_NUMBER, &hdr_info );
1163 tls_version_split( SSLeay(), &lib_info );
1164
1165 if ( lib_info.major != hdr_info.major ||
1166 lib_info.minor != hdr_info.minor ||
1167 lib_info.micro != hdr_info.micro)
1168 {
1169 ufdbLogMessage( "WARNING: TLS/SSL run-time library vs. compile-time header version mismatch: *****\n"
1170 "OpenSSL %d.%d.%d may not be compatible with OpenSSL %d.%d.%d %s",
1171 lib_info.major, lib_info.minor, lib_info.micro,
1172 hdr_info.major, hdr_info.minor, hdr_info.micro, OPENSSL_VERSION_TEXT );
1173 }
1174 else
1175 {
1176 char status[16];
1177 char rev[2];
1178
1179 if (lib_info.status == 0x0f)
1180 strcpy( status, "R" );
1181 else
1182 sprintf( status, "beta %d", lib_info.status );
1183 if (lib_info.patch == 0)
1184 rev[0] = '\0';
1185 else
1186 {
1187 rev[0] = lib_info.patch - 1 + 'a';
1188 rev[1] = '\0';
1189 }
1190
1191 ufdbLogMessage( "using OpenSSL library %d.%d.%d%s %s (%s)",
1192 lib_info.major, lib_info.minor, lib_info.micro, rev, status, OPENSSL_VERSION_TEXT );
1193 }
1194 }
1195
1196
waitForTLSinitilisation(void)1197 GCC_NO_INLINE static void waitForTLSinitilisation( void )
1198 {
1199 static volatile int didwait = 0;
1200 int i;
1201
1202 if (didwait)
1203 return;
1204
1205 if (ufdbGV.debug > 1)
1206 ufdbLogMessage( "waitForTLSinitilisation: tls_inited is %d ssl_ctx is %sNULL ...",
1207 tls_inited, ssl_ctx != NULL ? "not " : "" );
1208
1209 if (tls_inited >= 3)
1210 {
1211 didwait = 1;
1212 return;
1213 }
1214
1215 if (tls_inited == 2 && ssl_ctx == NULL)
1216 {
1217 didwait = 1;
1218 return;
1219 }
1220
1221 /* the HTTPS initialisation may be delayed by the sgReadConfig() which may take several minutes to complete */
1222 /* the crash report uploader may be faster than the TLS/SSL initialisation so we sleep max 30 seconds */
1223 for (i = 0; i < 30; i++)
1224 {
1225 sleep( 1 );
1226 if (tls_inited >= 3)
1227 break;
1228 }
1229 didwait = 1;
1230
1231 if (ssl_ctx == NULL)
1232 {
1233 ufdbLogError( "waitForTLSinitilisation: after waiting: tls_inited is %d, ssl_ctx is NULL *****\n"
1234 "No HTTPS connections can be set up yet.",
1235 tls_inited );
1236 }
1237 }
1238
1239
UFDBinitHTTPSchecker(void)1240 int UFDBinitHTTPSchecker( void )
1241 {
1242 struct stat statres;
1243
1244 if (tls_inited == 0)
1245 {
1246 char * dbdirectory;
1247
1248 ufdb_mutex_lock( &init_mutex ); // >====================================
1249 if (tls_inited != 0)
1250 {
1251 ufdb_mutex_unlock( &init_mutex ); // <==============
1252 usleep( 10000 );
1253 return UFDB_API_OK;
1254 }
1255 tls_inited = 1;
1256
1257 if (ufdbGV.debug)
1258 ufdbLogMessage( "UFDBinitHTTPSchecker started" );
1259
1260 myht = UFDBcreateHashtable( 317, hostname2hash, mystrcmp );
1261 lastHTTPScachePurgeTime = time( NULL );
1262
1263 srandom( ((getpid() << 8) ^ lastHTTPScachePurgeTime) + ((unsigned long)myht << 26) );
1264
1265 OpenSSLversionCheck();
1266
1267 SSL_load_error_strings();
1268 SSL_library_init();
1269 /*
1270 * The SSL_library_init man page does not mention which "old" version...
1271 * But some "old" versions need to call OpenSSL_add_all_algorithms()
1272 */
1273 OpenSSL_add_all_algorithms();
1274 ENGINE_load_builtin_engines();
1275 CONF_modules_load( NULL, NULL, 0 );
1276
1277 #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
1278 {
1279 int i;
1280 CRYPTO_set_id_callback( ufdb_pthread_id_callback );
1281 num_static_ssl_locks = CRYPTO_num_locks();
1282 crypto_mutexes = (ufdb_mutex*) ufdbMalloc( (num_static_ssl_locks+1) * sizeof(ufdb_mutex) );
1283 for (i = 0; i <= num_static_ssl_locks; i++)
1284 ufdb_mutex_init( &crypto_mutexes[i] );
1285 CRYPTO_set_locking_callback( ufdb_crypto_locking_cb );
1286
1287 CRYPTO_set_dynlock_create_callback( ufdb_ssl_dyn_create );
1288 CRYPTO_set_dynlock_lock_callback( ufdb_ssl_dyn_lock );
1289 CRYPTO_set_dynlock_destroy_callback( ufdb_ssl_dyn_destroy );
1290 }
1291 #endif
1292
1293 ssl_ctx = SSL_CTX_new( SSLv23_client_method() );
1294 SSL_CTX_set_options( ssl_ctx, SSL_OP_NO_SSLv2 );
1295 SSL_CTX_set_options( ssl_ctx, SSL_OP_NO_SSLv3 );
1296 SSL_CTX_set_options( ssl_ctx, SSL_OP_NO_TLSv1 );
1297 #ifdef SSL_OP_NO_TICKET
1298 SSL_CTX_set_options( ssl_ctx, SSL_OP_NO_TICKET );
1299 #endif
1300
1301 #if 0
1302 if (ufdbGV.peek)
1303 {
1304 SSL_CTX_set_options( ssl_ctx, SSL_OP_NO_TLSv1 );
1305 SSL_CTX_set_options( ssl_ctx, SSL_OP_NO_TLSv1_1 );
1306 }
1307 #endif
1308
1309 if (ssl_ctx != NULL)
1310 {
1311 SSL_CTX_set_options( ssl_ctx, openssl_bug_bits() );
1312 SSL_CTX_set_session_cache_mode( ssl_ctx, SSL_SESS_CACHE_OFF );
1313 SSL_CTX_set_timeout( ssl_ctx, 120 );
1314 SSL_CTX_set_default_verify_paths( ssl_ctx );
1315 SSL_CTX_set_verify_depth( ssl_ctx, 12 );
1316 tls_inited = 2;
1317 }
1318
1319 if (ufdbGV.debug)
1320 ufdbLogMessage( "UFDBinitHTTPSchecker: tls_inited is %d", tls_inited );
1321
1322 if (ssl_ctx == NULL)
1323 {
1324 ufdbGV.httpsOfficialCertificate = 0;
1325 tls_inited = 5;
1326 errno = EINVAL;
1327 ufdb_mutex_unlock( &init_mutex ); // <====================
1328 return UFDB_API_ERR_ERRNO;
1329 }
1330
1331 if (ufdbGV.CAcertsFile[0] == '\0' && ufdbGV.CAcertsDir[0] == '\0')
1332 {
1333 ufdbLogMessage( "CA certificates are not defined, using default file" );
1334 #if !UFDB_GEN_API
1335 dbdirectory = ufdbGV.databaseDirectory;
1336 if (dbdirectory[0] == '\0')
1337 #endif
1338 dbdirectory = (char *) DEFAULT_DBHOME;
1339 strcpy( ufdbGV.CAcertsFile, dbdirectory );
1340 strcat( ufdbGV.CAcertsFile, "/security/cacerts" );
1341 }
1342 if (ufdbGV.debug)
1343 ufdbLogMessage( "UFDBinitHTTPSchecker: CA certficates are in file \"%s\" and/or directory \"%s\"",
1344 ufdbGV.CAcertsFile, ufdbGV.CAcertsDir );
1345 if (ufdbGV.CAcertsFile[0] != '\0' && stat( ufdbGV.CAcertsFile, &statres ) < 0)
1346 {
1347 ufdbLogError( "SSL library initialisation failed because of a problem with the CA certificate file: *****\n"
1348 "file: \"%s\", error: %s", ufdbGV.CAcertsFile, strerror(errno) );
1349 if (ufdbGV.httpsOfficialCertificate)
1350 {
1351 ufdbLogFatalError( "Cannot perform mandatory check of TLS/SSL certificates." );
1352 ufdbLogMessage( "Fix the problem ***** " );
1353 ufdbLogMessage( "In case that there is no cacerts file, option enforce-https-official-certificate must be \"off\"" );
1354 }
1355 tls_inited = 6;
1356 ufdb_mutex_unlock( &init_mutex ); // <====================
1357 errno = ENOENT;
1358 return UFDB_API_ERR_ERRNO;
1359 }
1360
1361 ufdbCacertsLoaded = SSL_CTX_load_verify_locations( ssl_ctx,
1362 ufdbGV.CAcertsFile[0] == '\0' ? NULL : ufdbGV.CAcertsFile,
1363 ufdbGV.CAcertsDir[0] == '\0' ? NULL : ufdbGV.CAcertsDir );
1364 if (ufdbCacertsLoaded != 1)
1365 {
1366 ufdbLogError( "Failure to load the CA database; TLS/SSL certificates cannot be verified *****\n"
1367 "CA file is \"%s\" and CA directory is \"%s\"",
1368 ufdbGV.CAcertsFile[0] == '\0' ? "none" : ufdbGV.CAcertsFile,
1369 ufdbGV.CAcertsDir[0] == '\0' ? "none" : ufdbGV.CAcertsDir );
1370 if (ufdbGV.httpsOfficialCertificate)
1371 {
1372 ufdbLogFatalError( "Cannot perform mandatory check of TLS/SSL certificates." );
1373 ufdbLogMessage( "ABORTING. Fix the problem and start ufdbguardd again." );
1374 ufdbLogMessage( "In case that there is no cacerts file, option enforce-https-official-certificate must be \"off\"" );
1375 }
1376 tls_inited = 7;
1377 ufdb_mutex_unlock( &init_mutex ); // <====================
1378 errno = ENOENT;
1379 return UFDB_API_ERR_ERRNO;
1380 }
1381 else
1382 {
1383 ufdbLogMessage( "HTTPS/SSL verification with trusted certificates from file \"%s\" and directory \"%s\"",
1384 ufdbGV.CAcertsFile[0] == '\0' ? "none" : ufdbGV.CAcertsFile,
1385 ufdbGV.CAcertsDir[0] == '\0' ? "none" : ufdbGV.CAcertsDir );
1386 }
1387
1388 #if UFDB_USE_EXPORT_CIPHERS
1389 /*
1390 * According to the OpenSSL documentation, temporary RSA key is needed
1391 * when export ciphers are in use.
1392 */
1393 SSL_CTX_set_tmp_rsa_callback( ssl_ctx, tmp_rsa_cb );
1394 #endif
1395
1396 /* use SSL_VERIFY_NONE and verify the certificate in ssl_check_certificate */
1397 SSL_CTX_set_verify( ssl_ctx, SSL_VERIFY_NONE, NULL );
1398
1399 #if 0
1400 SSL_CTX_set_mode( ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE );
1401 #endif
1402 SSL_CTX_set_mode( ssl_ctx, SSL_MODE_AUTO_RETRY );
1403
1404 tls_inited = 3;
1405 if (ufdbGV.debug)
1406 ufdbLogMessage( "UFDBinitHTTPSchecker finished: tls_inited is %d", tls_inited );
1407
1408 ufdb_mutex_unlock( &init_mutex ); // <==========================================
1409 }
1410
1411 return UFDB_API_OK;
1412 }
1413
1414
sslver2string(int version)1415 static const char * sslver2string(
1416 int version )
1417 {
1418 switch (version)
1419 {
1420 case SSL2_VERSION: return "SSLv2";
1421 case SSL3_VERSION: return "SSLv3";
1422 case TLS1_VERSION: return "TLSv1.0";
1423
1424 #ifdef TLS1_1_VERSION
1425 case TLS1_1_VERSION: return "TLSv1.1";
1426 #endif
1427
1428 #ifdef TLS1_2_VERSION
1429 case TLS1_2_VERSION: return "TLSv1.2";
1430 #endif
1431
1432 #ifdef TLS1_3_VERSION
1433 case TLS1_3_VERSION: return "TLSv1.3";
1434 #endif
1435
1436 #ifdef TLS1_4_VERSION
1437 case TLS1_4_VERSION: return "TLSv1.4";
1438 #endif
1439
1440 #ifdef TLS2_0_VERSION
1441 case TLS2_0_VERSION: return "TLSv2.0";
1442 #endif
1443
1444 #ifdef DTLS1_VERSION
1445 case DTLS1_VERSION: return "DTLSv1";
1446 #endif
1447 }
1448
1449 return "TLSv0";
1450 }
1451
1452
1453 /* status return value is one of
1454 * UFDB_API_OK
1455 * UFDB_API_ERR_INVALID_CERT
1456 * UFDB_API_ERR_IS_AIM UFDB_API_ERR_IS_YAHOOMSG UFDB_API_ERR_IS_SKYPE UFDB_API_ERR_IS_GTALK UFDB_API_ERR_IS_FBCHAT
1457 * UFDB_API_ERR_IS_CITRIXONLINE
1458 * UFDB_API_ERR_IS_TUNNEL
1459 * UFDB_API_ERR_UNKNOWN_PROTOCOL
1460 * UFDB_API_ERR_NULL
1461 * UFDB_API_ERR_SOCKET
1462 * UFDB_API_ERR_IP_ADDRESS
1463 */
httpsGETroot(int worker,int s,const char * hostname,int portnumber,int * certErrors,char * cn,int * status)1464 GCC_NO_INLINE static char * httpsGETroot(
1465 int worker,
1466 int s,
1467 const char * hostname,
1468 int portnumber,
1469 int * certErrors,
1470 char * cn,
1471 int * status )
1472 {
1473 int n;
1474 int m;
1475 const char * method;
1476 SSL_MUTEX_FN_INIT
1477 SSL * ssl;
1478 char request[2048];
1479 char reply[32*1024+4];
1480
1481 /*******************************
1482 GET / HTTP/1.0
1483 User-Agent: Mozilla/4.0 (xxx) Gecko/20100722 Firefox/3.6.8
1484 Host: www.urlfilterdb.com:9443
1485 Accept: * / *
1486 Connection: Keep-Alive
1487 ********************************/
1488
1489 if (hostname == NULL)
1490 {
1491 *status = UFDB_API_ERR_FATAL;
1492 ufdbLogError( "httpsGETroot: hostname is NULL" );
1493 return NULL;
1494 }
1495
1496 waitForTLSinitilisation(); /* wait for UFDBinitHTTPSchecker() to finish */
1497 if (ssl_ctx == NULL)
1498 {
1499 *status = UFDB_API_ERR_FATAL;
1500 ufdbLogError( "W%03d: httpsGETroot: ssl_ctx is NULL", worker );
1501 return NULL;
1502 }
1503
1504 /* UFDBopenssl_connect returns OK (have SSL), SKYPE (detected Skype), or UNKNOWN_PROTOCOL or TUNNEL */
1505 request[0] = '\0';
1506 reply[0] = '\0';
1507 ssl = NULL;
1508 n = UFDBopenssl_connect( hostname, portnumber, s, &ssl );
1509 if (n != UFDB_API_OK)
1510 {
1511 if (n == UFDB_API_ERR_TLS && !ufdbGV.reconfig)
1512 {
1513 n = probePort( worker, hostname, portnumber );
1514 if (n == UFDB_API_OK)
1515 n = UFDB_API_ERR_TLS;
1516 }
1517
1518 ufdbLogMessage( "W%03d: SSL connect failure to %s:%d. The server %s.",
1519 worker, hostname, portnumber,
1520 (n==UFDB_API_ERR_IS_AIM) ? "has AIM" :
1521 (n==UFDB_API_ERR_IS_GTALK) ? "has Google Talk" :
1522 (n==UFDB_API_ERR_IS_SKYPE) ? "has Skype" :
1523 (n==UFDB_API_ERR_IS_YAHOOMSG) ? "has Yahoo IM" :
1524 (n==UFDB_API_ERR_IS_FBCHAT) ? "has Facebook Chat" :
1525 (n==UFDB_API_ERR_IS_CITRIXONLINE) ? "has CitrixOnline" :
1526 (n==UFDB_API_ERR_IS_ANYDESK) ? "has AnyDesk" :
1527 (n==UFDB_API_ERR_IS_TEAMVIEWER) ? "has Teamviewer" :
1528 (n==UFDB_API_ERR_UNKNOWN_PROTOCOL) ? "uses an unknown protocol" :
1529 (n==UFDB_API_ERR_NULL) ? "has a NULL connection" :
1530 (n==UFDB_API_ERR_TUNNEL) ? "has a tunnel" :
1531 (n==UFDB_API_ERR_SOCKET) ? "has a connection error" :
1532 (n==UFDB_API_ERR_TLS) ? "does not accept a TLS connection" :
1533 (n==UFDB_API_ERR_FATAL) ? "has a fatal connection error" :
1534 (n==UFDB_API_ERR_IP_ADDRESS) ? "has no FQDN but an IP address" :
1535 "does not speak SSL+HTTP" );
1536 *status = n;
1537 return NULL;
1538 }
1539
1540 method = sslver2string( SSL_version(ssl) );
1541 if (ufdbGV.debug)
1542 {
1543 int cipherbits;
1544 const SSL_CIPHER * cipher;
1545 const char * ciphername;
1546
1547 cipher = SSL_get_current_cipher( ssl );
1548 ciphername = SSL_CIPHER_get_name( (SSL_CIPHER *) cipher ); /* cast is required for Solaris 10 */
1549 cipherbits = SSL_CIPHER_get_bits( (SSL_CIPHER *) cipher, &m ); /* idem */
1550 ufdbLogMessage( "W%03d: TLS/SSL connection to %s:%d established method %s cipher %s %d-bits",
1551 worker, hostname, portnumber, method, ciphername, cipherbits );
1552 }
1553
1554 if (ufdbGV.reconfig)
1555 {
1556 ufdbLogMessage( "W%03d: certificate check of %s connection to %s:%d is aborted due to reconfiguration in process",
1557 worker, method, hostname, portnumber );
1558 UFDBopenssl_close( ssl );
1559 *status = UFDB_API_ERR_OUTDATED;
1560 return NULL;
1561 }
1562
1563 n = ssl_check_certificate( ssl, hostname, portnumber, cn );
1564 if (n == UFDB_API_ERR_INVALID_CERT)
1565 *certErrors = UFDB_API_ERR_INVALID_CERT;
1566 #if 1
1567 /* n = OK|ERR_INVALID_CERT|ERR_IS_YAHOOMSG|ERR_IS_AIM */
1568 if (!ufdbGV.httpsOfficialCertificate)
1569 {
1570 if (n == UFDB_API_ERR_INVALID_CERT)
1571 {
1572 ufdbLogMessage( "W%03d: %s certificate of %s:%d has an issue but enforce-https-official-certificate is off",
1573 worker, method, hostname, portnumber );
1574 n = UFDB_API_OK;
1575 }
1576 }
1577 #endif
1578
1579 if (n == UFDB_API_ERR_IS_AIM)
1580 {
1581 ufdbLogMessage( "W%03d: %s connection to %s:%d has AIM", worker, method, hostname, portnumber );
1582 UFDBopenssl_close( ssl );
1583 *status = n;
1584 return NULL;
1585 }
1586
1587 if (n == UFDB_API_ERR_IS_YAHOOMSG)
1588 {
1589 ufdbLogMessage( "W%03d: %s connection to %s:%d has Yahoo IM", worker, method, hostname, portnumber );
1590 UFDBopenssl_close( ssl );
1591 *status = n;
1592 return NULL;
1593 }
1594
1595 if (n == UFDB_API_ERR_IS_SKYPE)
1596 {
1597 ufdbLogMessage( "W%03d: %s connection to %s:%d has Skype", worker, method, hostname, portnumber );
1598 UFDBopenssl_close( ssl );
1599 *status = n;
1600 return NULL;
1601 }
1602
1603 if (n == UFDB_API_ERR_IS_GTALK)
1604 {
1605 ufdbLogMessage( "W%03d: %s connection to %s:%d has Google Talk", worker, method, hostname, portnumber );
1606 UFDBopenssl_close( ssl );
1607 *status = n;
1608 return NULL;
1609 }
1610
1611 if (n == UFDB_API_ERR_IS_FBCHAT)
1612 {
1613 ufdbLogMessage( "W%03d: %s connection to %s:%d has Facebook Chat", worker, method, hostname, portnumber );
1614 UFDBopenssl_close( ssl );
1615 *status = n;
1616 return NULL;
1617 }
1618
1619 if (n == UFDB_API_ERR_IS_CITRIXONLINE)
1620 {
1621 ufdbLogMessage( "W%03d: %s connection to %s:%d has CitrixOnline", worker, method, hostname, portnumber );
1622 UFDBopenssl_close( ssl );
1623 *status = n;
1624 return NULL;
1625 }
1626
1627 if (n == UFDB_API_ERR_IS_ANYDESK)
1628 {
1629 ufdbLogMessage( "W%03d: %s connection to %s:%d has Anydesk", worker, method, hostname, portnumber );
1630 UFDBopenssl_close( ssl );
1631 *status = n;
1632 return NULL;
1633 }
1634
1635 if (n == UFDB_API_ERR_IS_TEAMVIEWER)
1636 {
1637 ufdbLogMessage( "W%03d: %s connection to %s:%d has Teamviewer", worker, method, hostname, portnumber );
1638 UFDBopenssl_close( ssl );
1639 *status = n;
1640 return NULL;
1641 }
1642
1643 if (n == UFDB_API_ERR_TUNNEL)
1644 {
1645 ufdbLogMessage( "W%03d: %s connection to %s:%d has a tunnel", worker, method, hostname, portnumber );
1646 UFDBopenssl_close( ssl );
1647 *status = n;
1648 return NULL;
1649 }
1650
1651 if (n != UFDB_API_OK && !ufdbGV.httpsEnforceContentPeek)
1652 {
1653 ufdbLogMessage( "W%03d: %s connection to %s:%d has error code %d. It is marked as a TLS/SSL certificate issue",
1654 worker, method, hostname, portnumber, n );
1655 UFDBopenssl_close( ssl );
1656 *status = UFDB_API_ERR_INVALID_CERT;
1657 return NULL;
1658 }
1659
1660 if (ufdbGV.reconfig)
1661 {
1662 ufdbLogMessage( "W%03d: content check of %s connection to %s:%d is aborted due to reconfiguration in process",
1663 worker, method, hostname, portnumber );
1664 UFDBopenssl_close( ssl );
1665 *status = UFDB_API_ERR_OUTDATED;
1666 return NULL;
1667 }
1668
1669 *status = n;
1670
1671 /* TO-DO: if there is a proxy, "CONNECT" must be used */
1672 sprintf( request, "GET / HTTP/1.1\r\n"
1673 "User-Agent: " UFDB_USER_AGENT "\r\n"
1674 "Host: %s:%d\r\n"
1675 "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
1676 "Accept-Language: en-US,en;q=0.5\r\n"
1677 "DNT: 1\r\n"
1678 "Upgrade-Insecure-Requests: 1\r\n"
1679 // "Connection: Close\r\n"
1680 "\r\n",
1681 hostname, portnumber
1682 );
1683 n = UFDBopenssl_write( request, strlen(request), ssl );
1684 if (n < 0)
1685 {
1686 if (n == UFDB_API_OK)
1687 *status = UFDB_API_ERR_SOCKET;
1688 strcpy( reply, "failed to send GET / with TLS/SSL" );
1689 }
1690 else
1691 {
1692 reply[0] = '\0';
1693 n = UFDBopenssl_read( reply, sizeof(reply)-4, ssl );
1694 if (n > 0)
1695 {
1696 reply[n] = '\0';
1697 #if UFDB_VALGRINDING
1698 reply[n+1] = '\0'; /* to make valgrind happy */
1699 reply[n+2] = '\0';
1700 reply[n+3] = '\0';
1701 #endif
1702 if ((ufdbGV.debug > 1 || ufdbGV.peek) &&
1703 strstr( reply, " 200 " ) != NULL && // HTTP code 200 must be present
1704 strstr( reply, "Content-Length: 0" ) == NULL)
1705 {
1706 /* in many cases we only got the header but we also want to see some content */
1707 // TODO: to not upset some web servers who block bad behaving clients, all content (up to sizeof(reply)) should be read
1708 // TODO: set read timeout to 2 seconds.
1709 usleep( 150000 );
1710 m = UFDBopenssl_read( reply+n, sizeof(reply)-4-n, ssl );
1711 ufdbLogMessage( "httpsGETroot: read %d + %d bytes of content from %s:%d",
1712 n, m, hostname, portnumber );
1713 if (m > 0)
1714 {
1715 n += m;
1716 reply[n] = '\0';
1717 #if UFDB_VALGRINDING
1718 reply[n+1] = '\0'; /* to make valgrind happy */
1719 reply[n+2] = '\0';
1720 reply[n+3] = '\0';
1721 #endif
1722 }
1723 }
1724 }
1725 else
1726 {
1727 if (n == UFDB_API_OK)
1728 *status = UFDB_API_ERR_SOCKET;
1729 #if UFDB_VALGRINDING
1730 reply[0+1] = '\0'; /* to make valgrind happy */
1731 reply[0+2] = '\0';
1732 reply[0+3] = '\0';
1733 #endif
1734 }
1735 }
1736
1737 UFDBopenssl_close( ssl );
1738
1739 return ufdbStrdup( reply );
1740 }
1741
1742
1743 /* Check for a tunnel. *** used by ufdbpeek and the API ***
1744 *
1745 * Valid flags are:
1746 * UFDB_API_ALLOW_QUEUING
1747 * UFDB_API_VERBOSE_OUTPUT
1748 *
1749 * return values are:
1750 * UFDB_API_OK: regular https traffic
1751 * UFDB_API_REQ_QUEUED: request is queued for an other thread
1752 * UFDB_API_ERR_TUNNEL: https channel is tunneled
1753 * UFDB_API_ERR_UNKNOWN_PROTOCOL: https channel is tunneled
1754 * UFDB_API_ERR_IS_SKYPE: https channel is used by Skype
1755 * UFDB_API_ERR_IS_GTALK: https channel is used by Gtalk
1756 * UFDB_API_ERR_IS_FBCHAT: https channel is used by Facebook Chat
1757 * UFDB_API_ERR_IS_CITRIXONLINE: https channel is used by CitrixOnline
1758 * UFDB_API_ERR_IS_ANYDESK: https channel is used by Anydesk
1759 * UFDB_API_ERR_IS_TEAMVIEWER: https channel is used by Teamviewer
1760 * TODO: implement UFDB_API_ERR_IS_PANDO: https channel is used by Pando Client
1761 * UFDB_API_ERR_CERTIFICATE: TLS/SSL certificate is self-signed or has wrong common name
1762 * UFDB_API_ERR_IP_ADDRESS: hostname is an IP address (only when flag UFDB_API_NO_IP is used)
1763 * UFDB_API_ERR_NULL: tunnel verification is OFF.
1764 */
1765 #if UFDB_SSL_SUPPORT
UFDBcheckForHTTPStunnel(const char * hostname,int portnumber,int flags)1766 int UFDBcheckForHTTPStunnel(
1767 const char * hostname,
1768 int portnumber,
1769 int flags )
1770 {
1771 int ret;
1772 struct httpsInfo * info;
1773
1774 if (ufdbGV.tunnelCheckMethod == UFDB_API_HTTPS_CHECK_OFF)
1775 return UFDB_API_ERR_NULL;
1776
1777 if (ufdbGV.debug)
1778 ufdbLogMessage( "UFDBcheckForHTTPStunnel %s:%d %04x", hostname, portnumber, flags );
1779
1780 if (flags & UFDB_API_ALLOW_QUEUING)
1781 {
1782 int status;
1783 struct httpsInfo * hinfo;
1784 char key[1024+16];
1785
1786 status = lookupHTTPScache( hostname, portnumber, 1, &hinfo );
1787 if (status == UFDB_API_ERR_NULL)
1788 {
1789 /* The hostname:port is entered in the hash *NOW* so that future calls
1790 * know that it is already queued.
1791 */
1792 sprintf( key, "%s:%d", hostname, portnumber );
1793 info = (struct httpsInfo *) ufdbMalloc( sizeof(struct httpsInfo) );
1794 info->t = time( NULL );
1795 info->status = UFDB_API_REQ_QUEUED;
1796 info->content = NULL;
1797 info->category[0] = '\0';
1798 info->cn[0] = '\0';
1799 UFDBinsertHashtable( myht, ufdbStrdup(key), (void *) info, 1 );
1800
1801 ret = ufdbHttpsQueueRequest( hostname, portnumber );
1802 if (ret != UFDB_API_OK)
1803 {
1804 if (ret == UFDB_API_ERR_FULL)
1805 {
1806 /* The queue is full. The status in the hashtable is updated to "socket error"
1807 * because this forces a removal from the cache in 5 minutes and a retry.
1808 */
1809 info->status = UFDB_API_ERR_SOCKET;
1810 return UFDB_API_ERR_SOCKET;
1811 }
1812 else if (ret == UFDB_API_REQ_QUEUED)
1813 {
1814 /* The HTTP verification request was already queued... */
1815 return UFDB_API_BEING_VERIFIED;
1816 }
1817 else
1818 ufdbLogError( "UFDBcheckForHTTPStunnel: ufdbHttpsQueueRequest( %s:%d ) returned with code %d",
1819 hostname, portnumber, ret );
1820 }
1821
1822 /* We just queued a request for a verification thread. Be nice and yield the CPU. */
1823 #ifdef _POSIX_PRIORITY_SCHEDULING
1824 sched_yield();
1825 #endif
1826 return UFDB_API_BEING_VERIFIED;
1827 }
1828
1829 /* TODO: deal with status == UFDB_API_ERR_OUTDATED */
1830 return status;
1831 }
1832
1833 /* requests are not queued, so we perform the verification ourselves */
1834 return UFDBverifyPortHasHTTPS( hostname, portnumber, flags );
1835 }
1836 #else
UFDBcheckForHTTPStunnel(const char * hostname,int portnumber,int flags)1837 int UFDBcheckForHTTPStunnel(
1838 const char * hostname,
1839 int portnumber,
1840 int flags )
1841 {
1842 return UFDB_API_ERR_NULL;
1843 }
1844 #endif
1845
1846
1847 /*
1848 * in case that the caller of UFDBcheckForHTTPStunnel uses the UFDB_API_ALLOW_QUEUING flag,
1849 * the caller MUST have created 1-14 threads with UFDBhttpsTunnelVerifier() as main.
1850 */
UFDBhttpsTunnelVerifier(void * ptr)1851 void * UFDBhttpsTunnelVerifier(
1852 void * ptr )
1853 {
1854 #if UFDB_PTHREAD_SUPPORT
1855 sigset_t signals;
1856
1857 /*
1858 * All signals must be blocked.
1859 */
1860 sigemptyset( &signals );
1861 sigaddset( &signals, SIGHUP );
1862 sigaddset( &signals, SIGUSR1 );
1863 sigaddset( &signals, SIGCHLD );
1864 sigaddset( &signals, SIGTERM );
1865 sigaddset( &signals, SIGINT );
1866 pthread_sigmask( SIG_BLOCK, &signals, NULL );
1867
1868 #if !UFDB_SSL_SUPPORT
1869 while (1)
1870 sleep( 1 );
1871 #else
1872 waitForTLSinitilisation(); /* wait for UFDBinitHTTPSchecker() to finish */
1873 int portnumber;
1874 char hostname[1028];
1875
1876 while (!ufdbGV.terminating)
1877 {
1878 pthread_testcancel();
1879
1880 ufdbGetHttpsRequest( hostname, &portnumber );
1881
1882 if (((volatile int) ufdbGV.tunnelCheckMethod) == UFDB_API_HTTPS_CHECK_OFF)
1883 continue;
1884
1885 pthread_testcancel();
1886
1887 if (ufdbGV.debug)
1888 ufdbLogMessage( "T%02ld: UFDBhttpsTunnelVerifier: start TLS/SSL verification %s:%d ...",
1889 (long) ptr, hostname, portnumber );
1890 (void) UFDBverifyPortHasHTTPS( hostname, portnumber, 0 );
1891
1892 if (ufdbGV.terminating)
1893 pthread_exit( (void *) 0 );
1894
1895 while (ufdbGV.reconfig)
1896 {
1897 pthread_testcancel();
1898 usleep( 400000 );
1899 }
1900 }
1901 #endif
1902
1903 #endif
1904 return NULL;
1905 }
1906
1907
1908 /* Probe a port (often 443) to see what protocol is used.
1909 *
1910 * NOTE: This function may take up to 22 seconds to complete !
1911 */
1912 GCC_NO_INLINE
UFDBverifyPortHasHTTPS(const char * hostname,int portnumber,int flags)1913 static int UFDBverifyPortHasHTTPS( /* TODO: GET RID OF UFDBverifyPortHasHTTPS() and use UFDBsslPeekServer() instead *****************************************/
1914 const char * hostname,
1915 int portnumber,
1916 int flags )
1917 {
1918 int status;
1919
1920 #if UFDB_SSL_SUPPORT
1921 /* When a HTTPS port is used, we need to check the following:
1922 * if SSL+HTTP is spoken
1923 * if not SSL, is XMPP/Jabber/Google Talk
1924 * if not SSL, is Skype or an unknown protocol
1925 * if SSL has a valid certificate
1926 * if SSL+HTTP, detect known tunnels
1927 * TODO: if SSL+HHTP, detect proxy with content analysis (NEW)
1928 */
1929 int s;
1930 char * content;
1931 struct timeval tv;
1932 struct httpsInfo * hinfo;
1933 int certErrors;
1934 char cn[1024];
1935
1936 if (ufdbGV.debug || flags & UFDB_API_VERBOSE_OUTPUT)
1937 ufdbLogMessage( "UFDBverifyPortHasHTTPS( %s, %d )", hostname, portnumber );
1938
1939 /* aargghh!! with Squid interception mode, youtube-education and google sites now also use HTTPS with IPv4 */
1940 if (ufdbGV.httpsWithHostname &&
1941 !ufdbGV.SquidUsesActiveBumping &&
1942 !ufdbGV.unknownProtocolOverHttps &&
1943 !ufdbGV.SkypeOverHttps && !ufdbGV.GtalkOverHttps && !ufdbGV.FBchatOverHttps &&
1944 !ufdbGV.YahooMsgOverHttps && !ufdbGV.AimOverHttps &&
1945 !ufdbGV.CitrixOnlineOverHttps &&
1946 hostnameIsIP(hostname) )
1947 {
1948 ufdbLogError( "IP address in URL is not allowed for the HTTPS protocol. IP=%s", hostname );
1949 status = UFDB_API_ERR_IP_ADDRESS;
1950 updateHTTPScache( hostname, portnumber, status, 0, &hinfo );
1951 return status;
1952 }
1953
1954 if (ufdbGV.reconfig || ufdbGV.terminating)
1955 return UFDB_API_ERR_SOCKET; /* try again in 5 minutes */
1956
1957 status = lookupHTTPScache( hostname, portnumber, 1, &hinfo );
1958 if (status == UFDB_API_ERR_OUTDATED)
1959 {
1960 /* this thread is going to do the verification now */
1961 updateHTTPScache( hostname, portnumber, UFDB_API_BEING_VERIFIED, 0, &hinfo );
1962 }
1963 else
1964 if (status == UFDB_API_ERR_NULL) /* not found in cache, fresh entry */
1965 {
1966 /* this thread is going to do the verification now */
1967 insertHTTPScache( hostname, portnumber, UFDB_API_BEING_VERIFIED, 1, &hinfo );
1968 }
1969 else if (status == UFDB_API_REQ_QUEUED) /* found in cache queue */
1970 {
1971 /* this thread is going to do the verification now */
1972 updateHTTPScache( hostname, portnumber, UFDB_API_BEING_VERIFIED, 0, &hinfo );
1973 }
1974 else
1975 {
1976 if (status != UFDB_API_BEING_VERIFIED) /* found in cache, probe is concluded */
1977 return status;
1978
1979 /* found in cache, being verified at this moment */
1980 #ifdef UFDB_HTTPS_CACHE_DEBUG
1981 ufdbLogMessage( " UFDBverifyPortHasHTTPS %s:%d status is BEING_VERIFIED", hostname, portnumber );
1982 #endif
1983
1984 /* The status is UFDB_API_BEING_VERIFIED...
1985 * When the global mode is "aggressive" we use an idle-wait loop waiting
1986 * for the other thread to finish its job, otherwise the flags (UFDB_API_ALLOW_QUEUING)
1987 * may indicate if UFDB_API_BEING_VERIFIED is returned or the idle-wait loop is done.
1988 */
1989 if (ufdbGV.tunnelCheckMethod != UFDB_API_HTTPS_CHECK_AGGRESSIVE &&
1990 (flags & UFDB_API_ALLOW_QUEUING) != 0)
1991 {
1992 if (ufdbGV.tunnelCheckMethod == UFDB_API_HTTPS_CHECK_QUEUE_CHECKS)
1993 usleep( 100000 ); /* give 0.1 sec CPU to other threads */
1994 return UFDB_API_BEING_VERIFIED;
1995 }
1996
1997 /* The method is aggressive so this thread is going to wait for another thread to terminate its probe */
1998
1999 usleep( 35000 + (random() % 19999) );
2000 s = 0;
2001
2002 while (status == UFDB_API_BEING_VERIFIED)
2003 {
2004 if (ufdbGV.reconfig)
2005 return UFDB_API_ERR_SOCKET;
2006
2007 status = lookupHTTPScache( hostname, portnumber, 0, &hinfo );
2008 if (status == UFDB_API_BEING_VERIFIED)
2009 {
2010 usleep( 80000 + (random() % 27777) );
2011 s++;
2012 if (s == 10)
2013 {
2014 if (ufdbGV.debug > 1 || flags & UFDB_API_VERBOSE_OUTPUT)
2015 ufdbLogMessage( " UFDBverifyPortHasHTTPS: waiting for other thread on status for %s:%d",
2016 hostname, portnumber );
2017 }
2018 }
2019 else
2020 {
2021 if (s >= 10)
2022 {
2023 if (ufdbGV.debug > 1 || flags & UFDB_API_VERBOSE_OUTPUT)
2024 ufdbLogMessage( " UFDBverifyPortHasHTTPS: finished waiting %d intervals for other thread on %s:%d",
2025 s, hostname, portnumber );
2026 }
2027 return status;
2028 }
2029
2030 if (s == 150) /* Aaaargh. I am tired of waiting! */
2031 {
2032 if (ufdbGV.debug > 1 || flags & UFDB_API_VERBOSE_OUTPUT)
2033 ufdbLogMessage( " UFDBverifyPortHasHTTPS: waited too long for other thread on status for %s:%d",
2034 hostname, portnumber );
2035 return UFDB_API_BEING_VERIFIED;
2036 }
2037 }
2038 }
2039
2040 #ifdef UFDB_HTTPS_CACHE_DEBUG
2041 if (ufdbGV.debug || (flags & UFDB_API_VERBOSE_OUTPUT))
2042 ufdbLogMessage( " UFDBverifyPortHasHTTPS: %s:%d opening socket...", hostname, portnumber );
2043 #endif
2044
2045 /************************** THE ACTUAL HTTPS PORT PROBE STARTS HERE **********************************/
2046
2047 s = UFDBopenSocket( hostname, portnumber );
2048 if (s < 0)
2049 {
2050 ufdbLogError( "HTTPS protocol verification for %s:%d FAILED: cannot open communication socket",
2051 hostname, portnumber );
2052 updateHTTPScache( hostname, portnumber, UFDB_API_ERR_SOCKET, 0, &hinfo );
2053 return UFDB_API_ERR_SOCKET;
2054 }
2055
2056 if (ufdbGV.reconfig)
2057 {
2058 close( s );
2059 return UFDB_API_ERR_SOCKET;
2060 }
2061
2062 if (ufdbGV.debug || (flags & UFDB_API_VERBOSE_OUTPUT) ||
2063 ufdbGV.debugAim || ufdbGV.debugGtalk || ufdbGV.debugSkype || ufdbGV.debugYahooMsg ||
2064 ufdbGV.debugFBchat || ufdbGV.debugCitrixOnline)
2065 {
2066 ufdbLogMessage( "UFDBverifyPortHasHTTPS: socket to %s:%d is opened successfully. fd=%d",
2067 hostname, portnumber, s );
2068 }
2069
2070 content = NULL;
2071 status = UFDB_API_OK;
2072
2073 if (status == UFDB_API_OK)
2074 {
2075 /* TODO: check for vtunnel */
2076
2077 tv.tv_sec = 3; /* NOTE: ufdbgclient times out in 21 seconds! but we also have Skype,Gtalk,SSH probes */
2078 tv.tv_usec = 500000;
2079 (void) setsockopt( s, SOL_SOCKET, SO_RCVTIMEO, (void *) &tv, sizeof(tv) );
2080 (void) setsockopt( s, SOL_SOCKET, SO_SNDTIMEO, (void *) &tv, sizeof(tv) );
2081
2082 /* Setup a TLS connection to connect to the HTTPS port, do a "GET /",
2083 * and see what the server has to say. An error is returned if
2084 * the other end does not speak SSL.
2085 */
2086 status = UFDB_API_OK;
2087 content = httpsGETroot( 0, s, hostname, portnumber, &certErrors, cn, &status );
2088 if (ufdbGV.debug || (flags & UFDB_API_VERBOSE_OUTPUT) ||
2089 ufdbGV.debugAim || ufdbGV.debugGtalk || ufdbGV.debugSkype || ufdbGV.debugYahooMsg ||
2090 ufdbGV.debugFBchat || ufdbGV.debugCitrixOnline)
2091 {
2092 ufdbLogMessage( " httpsGETroot for %s:%d returned status %s, content is %sNULL",
2093 hostname, portnumber, ufdbAPIstatusString(status), content==NULL ? "" : "not " );
2094 }
2095
2096 if (content == NULL || *content == '\0')
2097 {
2098 if (status == UFDB_API_OK)
2099 {
2100 /* We did not read anything from the server... so we cannot draw a conclusion.
2101 * Therefore we return "OK" and hope that the next check gives an answer.
2102 */
2103 ufdbLogMessage( "HTTPS server %s:%d has an empty or no response.", hostname, portnumber );
2104 close( s );
2105 if (content != NULL)
2106 ufdbFree( content );
2107 return UFDB_API_OK;
2108 }
2109 }
2110 else
2111 if (status != UFDB_API_OK)
2112 {
2113 // we have already an error condition or ERR_IS_AIM|GTALK|SKYPE|YAHOOMSG|FBCHAT ; do no more checks
2114 ;
2115 }
2116 else
2117 if (strncmp( content, "HTTP/", 5 ) == 0 ||
2118 strncmp( content, "<?xml", 5 ) == 0) /* detect known tunnels */
2119 {
2120 if (ufdbGV.debug > 1)
2121 {
2122 int i, j;
2123 char debuginfo[2600];
2124 /* TO-DO: put this debug code in a separate function */
2125
2126 for (j = 0, i = 0; content[i] != '\0' && i < 2599; i++)
2127 {
2128 if (content[i] == '\r' || content[i] == '\n')
2129 debuginfo[j++] = '_';
2130 else if (isprint(content[i]))
2131 debuginfo[j++] = content[i];
2132 else
2133 debuginfo[j++] = '.';
2134 }
2135 debuginfo[j] = '\0';
2136 ufdbLogMessage( " HTTPS protocol reply for %s:%d:\n %s", hostname, portnumber, debuginfo );
2137 }
2138
2139 /* TODO: if header has PIWIK_SESSID= the category must be "ads" */
2140 #if 0
2141 if (strstr( content, "X-Kazaa-" ) != NULL)
2142 {
2143 ufdbLogError( "HTTPS protocol on %s:%d uses Kazaa P2P",
2144 hostname, portnumber );
2145 status = UFDB_API_ERR_P2P;
2146 }
2147 #endif
2148 /* TODO: investigate nomachine.com */
2149 if (strstr( content, "logmein.com/" ) != NULL ||
2150 strstr( content, "hamachi.cc/" ) != NULL)
2151 {
2152 ufdbLogError( "HTTPS protocol on %s:%d uses a hamachi/logmein TUNNEL",
2153 hostname, portnumber );
2154 status = UFDB_API_ERR_TUNNEL;
2155 }
2156 else
2157 if (strstr( content, "Set-Cookie: SSLX_SSESHID=" ) != NULL)
2158 {
2159 ufdbLogError( "HTTPS protocol on %s:%d uses an SSL-Explorer TUNNEL",
2160 hostname, portnumber );
2161 status = UFDB_API_ERR_TUNNEL;
2162 }
2163 else
2164 if (strstr( content, "BarracudaServer.com" ) != NULL ||
2165 strstr( content, "barracudaserver.com" ) != NULL ||
2166 strstr( content, "BarracudaDrive" ) != NULL)
2167 {
2168 ufdbLogError( "HTTPS protocol on %s:%d uses a BARRACUDA proxy TUNNEL",
2169 hostname, portnumber );
2170 status = UFDB_API_ERR_TUNNEL;
2171 }
2172 else
2173 if (strstr( content, " index.vnc -" ) != NULL ||
2174 strstr( content, " used with Xvnc" ) != NULL ||
2175 strstr( content, "TightVNC Java viewer applet" ) != NULL)
2176 {
2177 ufdbLogError( "HTTPS protocol on %s:%d uses a VNC proxy TUNNEL",
2178 hostname, portnumber );
2179 status = UFDB_API_ERR_TUNNEL;
2180 }
2181 else
2182 if (strstr( content, "X-Webtunnel-Status" ) != NULL ||
2183 strstr( content, "X-webtunnel-status" ) != NULL ||
2184 strstr( content, "X-webtunnel-version" ) != NULL ||
2185 strstr( content, "X-Webtunnel-Version" ) != NULL)
2186 {
2187 ufdbLogError( "HTTPS protocol on %s:%d uses Webtunnel TUNNEL",
2188 hostname, portnumber );
2189 status = UFDB_API_ERR_TUNNEL;
2190 }
2191 }
2192 else /* server does not speak HTTP */
2193 {
2194 int i,j;
2195 char debuginfo[2400];
2196
2197 ufdbLogMessage( "HTTPS protocol on %s:%d encapsulates a non-HTTP protocol %s",
2198 hostname, portnumber,
2199 ufdbGV.unknownProtocolOverHttps ? "and an unknown protocol is allowed" :
2200 "and is considered a PROXY TUNNEL because allow-unknown-protocol-over-https is OFF"
2201 );
2202 if (ufdbGV.debug > 1)
2203 {
2204 /* TO-DO: put this code in a separate function */
2205 for (j = 0, i = 0; content[i] != '\0' && i < 2399; i++)
2206 {
2207 if (content[i] == '\r' || content[i] == '\n')
2208 debuginfo[j++] = '_';
2209 else if (isprint(content[i]))
2210 debuginfo[j++] = content[i];
2211 else
2212 debuginfo[j++] = '.';
2213 }
2214 debuginfo[j] = '\0';
2215 ufdbLogMessage( " HTTPS protocol reply for %s:%d:\n %s", hostname, portnumber, debuginfo );
2216 }
2217
2218 status = ufdbGV.unknownProtocolOverHttps ? UFDB_API_ERR_UNKNOWN_PROTOCOL : UFDB_API_ERR_TUNNEL;
2219 }
2220 }
2221
2222 close( s );
2223
2224 if (ufdbGV.debug || ufdbGV.logAllRequests ||
2225 ufdbGV.debugAim || ufdbGV.debugGtalk || ufdbGV.debugSkype || ufdbGV.debugYahooMsg ||
2226 ufdbGV.debugFBchat || ufdbGV.debugCitrixOnline)
2227 {
2228 ufdbLogMessage( "HTTPS protocol on %s:%d has been verified and status is %s",
2229 hostname, portnumber, ufdbAPIstatusString(status) );
2230 }
2231
2232 if (ufdbGV.tunnelCheckMethod == UFDB_API_HTTPS_LOG_ONLY)
2233 status = UFDB_API_OK;
2234
2235 updateHTTPScache( hostname, portnumber, status, 0, &hinfo );
2236
2237 ufdbFree( content );
2238 #else
2239 status = UFDB_API_OK;
2240 #endif
2241
2242 return status;
2243 }
2244
2245
2246 /*
2247 * return values:
2248 * UFDB_API_ERR_NULL
2249 * UFDB_API_ERR_OUTDATED
2250 * status found in cache
2251 */
2252 GCC_NO_INLINE
lookupHTTPScache(const char * hostname,int portnumber,int keepLockForInsert,struct httpsInfo ** hinfo)2253 static int lookupHTTPScache(
2254 const char * hostname,
2255 int portnumber,
2256 int keepLockForInsert,
2257 struct httpsInfo ** hinfo )
2258 {
2259 struct httpsInfo * info;
2260 int status;
2261 time_t now;
2262 char key[1024+16];
2263
2264 sprintf( key, "%s:%d", hostname, portnumber );
2265
2266 info = (struct httpsInfo *) UFDBsearchHashtable( myht, (void *) key, keepLockForInsert );
2267 if (info == NULL)
2268 {
2269 #ifdef UFDB_HTTPS_CACHE_DEBUG
2270 ufdbLogMessage( " lookupHTTPScache %s%s is NULL", key, keepLockForInsert ? " keeplock" : "" );
2271 #endif
2272 return UFDB_API_ERR_NULL;
2273 }
2274
2275 now = time( NULL );
2276 status = info->status;
2277 *hinfo = info;
2278
2279 if (info->t < ufdbGV.databaseLoadTime)
2280 {
2281 #ifdef UFDB_HTTPS_CACHE_DEBUG
2282 ufdbLogMessage( " lookupHTTPScache %s%s is removed from hash table because the configuration changed",
2283 key, keepLockForInsert ? " keeplock" : "" );
2284 #endif
2285 return UFDB_API_ERR_OUTDATED;
2286 }
2287 else if (status == UFDB_API_ERR_SOCKET)
2288 {
2289 if (info->t < now - 1800 ||
2290 (info->t < now - 600 && UFDBhttpsVerificationQueued() < UFDB_NUM_HTTPS_VERIFIERS && UFDBgetTunnelCheckMethod() != UFDB_API_HTTPS_CHECK_AGGRESSIVE))
2291 {
2292 #ifdef UFDB_HTTPS_CACHE_DEBUG
2293 ufdbLogMessage( " lookupHTTPScache %s%s is removed from hash table because it has aged",
2294 key, keepLockForInsert ? " keeplock" : "" );
2295 #endif
2296 return UFDB_API_ERR_OUTDATED;
2297 }
2298 }
2299
2300 info->t = now;
2301
2302 #ifdef UFDB_HTTPS_CACHE_DEBUG
2303 ufdbLogMessage( " lookupHTTPScache %s%s is %d",
2304 key, keepLockForInsert ? " keeplock" : "", status );
2305 #endif
2306
2307 return status;
2308 }
2309
2310
2311 GCC_NO_INLINE
updateHTTPScache(const char * hostname,int portnumber,int status,int lockSetBySearch,struct httpsInfo ** hinfo)2312 static void updateHTTPScache(
2313 const char * hostname,
2314 int portnumber,
2315 int status,
2316 int lockSetBySearch,
2317 struct httpsInfo ** hinfo )
2318 {
2319 time_t now;
2320 struct httpsInfo * linfo;
2321 char key[1024+16];
2322
2323 sprintf( key, "%s:%d", hostname, portnumber );
2324
2325 #ifdef UFDB_HTTPS_CACHE_DEBUG
2326 ufdbLogMessage( " updateHTTPScache %s %d", key, status );
2327 #endif
2328
2329 /* The hostname:portnumber is most likely already in the hashlist with status QUEUED.
2330 * So we only have to update the status...
2331 * Unless the cache was rebuilt, we need to add the entry to the hash.
2332 */
2333 if (lockSetBySearch)
2334 UFDBunlockHashtable( myht );
2335
2336 now = time( NULL );
2337 linfo = (struct httpsInfo *) UFDBsearchHashtable( myht, (void *) key, 1 );
2338 if (linfo == NULL)
2339 {
2340 linfo = (struct httpsInfo *) ufdbMalloc( sizeof(struct httpsInfo) );
2341 linfo->t = now;
2342 linfo->status = status;
2343 linfo->content = NULL;
2344 linfo->category[0] = '\0';
2345 linfo->cn[0] = '\0';
2346 UFDBinsertHashtable( myht, (void *) ufdbStrdup(key), (void *) linfo, 1 );
2347 }
2348 else
2349 {
2350 /* note that the hashtable is not locked any more but that is allright since linfo will not change */
2351 linfo->status = status;
2352 linfo->t = now;
2353 }
2354 *hinfo = linfo;
2355 }
2356
2357
2358 /*
2359 *
2360 */
2361 GCC_NO_INLINE
insertHTTPScache(const char * hostname,int portnumber,int status,int lockSetbySearch,struct httpsInfo ** hinfo)2362 static void insertHTTPScache(
2363 const char * hostname,
2364 int portnumber,
2365 int status,
2366 int lockSetbySearch, /* always 1 */
2367 struct httpsInfo ** hinfo )
2368 {
2369 time_t now;
2370 struct httpsInfo * info;
2371 char key[1024+16];
2372
2373 sprintf( key, "%s:%d", hostname, portnumber );
2374
2375 #ifdef UFDB_HTTPS_CACHE_DEBUG
2376 ufdbLogMessage( " insertHTTPScache %s %d %s", key, status, lockSetbySearch ? "lockSetBySearch" : "" );
2377 #endif
2378
2379 now = time( NULL );
2380 info = (struct httpsInfo *) ufdbMalloc( sizeof(struct httpsInfo) );
2381 info->t = now;
2382 info->status = status;
2383 info->content = NULL;
2384 info->category[0] = '\0';
2385 info->cn[0] = '\0';
2386 *hinfo = info;
2387
2388 /* before calling UFDBinsertHashtable, check if the cache needs a purge and
2389 * take advantage of the hash table already being locked.
2390 */
2391 if ((myht->nEntries > 15000 && now > lastHTTPScachePurgeTime + 60*60) || (now > lastHTTPScachePurgeTime + 3*60*60))
2392 {
2393 int oldnEntries;
2394
2395 oldnEntries = myht->nEntries;
2396 if (oldnEntries > 15000)
2397 OldestInHTTPScache = now - 2 * 60 * 60; /* 2 hours */
2398 else
2399 OldestInHTTPScache = now - 3 * 60 * 60; /* 3 hours */
2400 UFDBpurgeHashtable( myht, purgeHTTPSinfo );
2401 lastHTTPScachePurgeTime = now;
2402 if (ufdbGV.debug)
2403 ufdbLogMessage( " insertHTTPScache: HTTPS cache %08lx was purged: %d of %d entries were removed",
2404 (unsigned long) myht, oldnEntries - myht->nEntries, oldnEntries );
2405 }
2406
2407 UFDBinsertHashtable( myht, (void *) ufdbStrdup(key), (void *) info, lockSetbySearch );
2408 }
2409
2410
2411 #if 0
2412 int select_fd(
2413 int fd,
2414 double maxtime,
2415 int wait_for )
2416 {
2417 int result;
2418 fd_set fdset;
2419 fd_set * rd;
2420 fd_set * wr;
2421 struct timeval tmout;
2422
2423 FD_ZERO( &fdset );
2424 FD_SET( fd, &fdset );
2425 rd = &fdset;
2426 wr = &fdset;
2427
2428 tmout.tv_sec = (long) maxtime;
2429 tmout.tv_usec = 1000000 * (maxtime - (long) maxtime);
2430
2431 errno = 0;
2432 do
2433 result = select( fd+1, rd, wr, NULL, &tmout );
2434 while (result < 0 && errno == EINTR);
2435
2436 return result;
2437 }
2438 #endif
2439
2440
2441 /* Perform the SSL handshake on file descriptor FD, which is assumed
2442 to be connected to an SSL server. The SSL handle provided by
2443 OpenSSL is registered with the file descriptor FD using
2444 fd_register_transport, so that subsequent calls to fd_read,
2445 fd_write, etc., will use the corresponding SSL functions.
2446
2447 Returns UFDB_API_OK on success */
2448
2449 GCC_NO_INLINE
UFDBopenssl_connect(const char * hostname,int portnumber,int fd,SSL ** ssl)2450 int UFDBopenssl_connect(
2451 const char * hostname,
2452 int portnumber,
2453 int fd,
2454 SSL ** ssl )
2455 {
2456 int ret;
2457 int saved_errno;
2458 int SSL_error;
2459 long state;
2460 char syserrstring[256];
2461 char errstring[256];
2462
2463 if (ufdbGV.debug)
2464 ufdbLogMessage( "UFDBopenssl_connect %s %d %d ...", hostname, portnumber, fd );
2465
2466 waitForTLSinitilisation();
2467
2468 ERR_clear_error();
2469 errno = 0;
2470
2471 *ssl = SSL_new( ssl_ctx );
2472 if (*ssl == NULL)
2473 {
2474 ufdbLogError( "UFDBopenssl_connect: SSL_new failed fd=%d", fd );
2475 print_errors();
2476 return UFDB_API_ERR_FATAL;
2477 }
2478
2479 #if 0
2480 (void) SSL_set_options( *ssl, SSL_OP_SINGLE_DH_USE|SSL_OP_CIPHER_SERVER_PREFERENCE|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3 );
2481 #endif
2482
2483 SSL_set_read_ahead( *ssl, 1 );
2484
2485 ret = SSL_set_fd( *ssl, fd );
2486 if (ret == 0)
2487 {
2488 ufdbLogError( "UFDBopenssl_connect: SSL_set_fd failed fd=%d", fd );
2489 print_errors();
2490 UFDBopenssl_close( *ssl );
2491 return UFDB_API_ERR_FATAL;
2492 }
2493
2494 SSL_set_connect_state( *ssl );
2495
2496 if (ufdbGV.peek && ufdbGV.peekServerName[0] != '\0' && hostnameIsIP(hostname))
2497 hostname = ufdbGV.peekServerName;
2498
2499 /* NOTE: also call SSL_set_tlsext_host_name() if the hostname is an IPv4 or IPv6 address */
2500 if (!SSL_set_tlsext_host_name( *ssl, hostname ))
2501 {
2502 ufdbLogMessage( "UFDBopenssl_connect: SSL_set_tlsext_host_name failed for '%s'", hostname );
2503 }
2504
2505 errno = 0;
2506 ret = SSL_connect( *ssl );
2507 saved_errno = errno;
2508
2509 if (saved_errno == ECONNRESET && hostnameIsIP(hostname))
2510 {
2511 hostname = (char*) "www.google.com";
2512 if (!SSL_set_tlsext_host_name( *ssl, hostname ))
2513 ufdbLogMessage( "UFDBopenssl_connect: SSL_set_tlsext_host_name failed for '%s'", hostname );
2514 // try again to connect but now with a hostname as SNI instead of an IP address
2515 errno = 0;
2516 ret = SSL_connect( *ssl );
2517 saved_errno = errno;
2518 }
2519
2520 state = SSL_get_state( *ssl );
2521
2522 #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
2523 if (ret > 0 && state == SSL_ST_OK)
2524 #else
2525 if (ret > 0 && state == TLS_ST_OK)
2526 #endif
2527 {
2528 int version;
2529 version = SSL_version( *ssl );
2530 if (version == SSL2_VERSION && ufdbGV.httpsNoSSLv2)
2531 {
2532 ufdbLogError( "HTTPS/SSL connection for %s:%d has an insecure SSLv2 protocol. It is blocked. *****",
2533 hostname, portnumber );
2534 UFDBopenssl_close( *ssl );
2535 return UFDB_API_ERR_INVALID_CERT;
2536 }
2537 if (version == SSL3_VERSION && ufdbGV.httpsNoSSLv3)
2538 {
2539 ufdbLogError( "HTTPS/SSL connection for %s:%d has an insecure SSLv3 protocol. It is blocked. *****",
2540 hostname, portnumber );
2541 UFDBopenssl_close( *ssl );
2542 return UFDB_API_ERR_INVALID_CERT;
2543 }
2544 return UFDB_API_OK;
2545 }
2546
2547 if (saved_errno == 0)
2548 strcpy( syserrstring, "no error" );
2549 else
2550 strerror_r( saved_errno, syserrstring, sizeof(syserrstring) );
2551 SSL_error = SSL_get_error( *ssl, ret );
2552 ERR_error_string_n( SSL_error, errstring, sizeof(errstring) );
2553 ufdbLogMessage( "UFDBopenssl_connect: SSL_connect to %s:%d failed:\n"
2554 "ret=%d errno=%d(%s) SSLerr=%d(%s) state=0x%08lx %s",
2555 hostname, portnumber, ret, saved_errno, syserrstring, SSL_error, sslerr2str(SSL_error),
2556 (long) state, errstring );
2557 print_errors();
2558 UFDBopenssl_close( *ssl );
2559
2560 /* do not call probePort if the server reset the connection */
2561 if (saved_errno == ECONNRESET)
2562 return UFDB_API_ERR_FATAL;
2563
2564 /* SSL_connect failed */
2565 return UFDB_API_ERR_TLS;
2566 }
2567
2568
2569 /* Return 1 if hostname (case-insensitively) matches common, 0
2570 otherwise. The recognized wildcard character is "*", which matches
2571 any character in common except ".".
2572
2573 This is used to match of hosts as indicated in rfc2818: "Names may
2574 contain the wildcard character * which is considered to match any
2575 single domain name component or component fragment. E.g., *.a.com
2576 matches foo.a.com but not bar.foo.a.com. f*.com matches foo.com but
2577 not bar.com [or foo.bar.com]."
2578
2579 If the pattern contain no wildcards, matchHostname(a,b) is
2580 equivalent to !strcasecmp(a,b)
2581 */
2582
2583 GCC_NO_INLINE
matchHostname(const char * common,const char * hostname)2584 static int matchHostname(
2585 const char * common,
2586 const char * hostname )
2587 {
2588 const char * c = common;
2589 const char * h = hostname;
2590 char ch;
2591
2592 #if 0
2593 if (ufdbGV.debug)
2594 ufdbLogMessage( " matchHostname( %s, %s )", common, hostname );
2595 #endif
2596
2597 for (; (ch = tolower(*c)) != '\0'; h++)
2598 {
2599 if (ch == '*')
2600 {
2601 c++;
2602 for (ch = tolower(*c); ch == '*'; c++, ch = tolower(*c))
2603 ;
2604 for (; *h != '\0'; h++)
2605 {
2606 if (tolower(*h) == ch && matchHostname(c,h)) /* TO-DO: make non-recursive */
2607 return 1;
2608 else if (*h == '.')
2609 return 0;
2610 }
2611 return ch == '\0';
2612 }
2613 else
2614 {
2615 if (ch != tolower(*h))
2616 return 0;
2617 c++;
2618 }
2619 }
2620
2621 return (*h == '\0');
2622 }
2623
2624
2625 #ifdef UFDB_LOAD_CERT_CHAIN
2626
convert_bytes_to_cert(unsigned char * bytes,int nbytes)2627 static X509 * convert_bytes_to_cert(
2628 unsigned char * bytes,
2629 int nbytes )
2630 {
2631 X509 * cert;
2632
2633 if (ufdbGV.debug)
2634 ufdbLogMessage( " convert_bytes_to_cert: certificate has %d bytes", nbytes );
2635
2636 #if 0
2637 BIO * in;
2638 if (!(in = BIO_new_mem_buf( bytes, nbytes ))
2639 {
2640 ufdbLogError( "cannot convert bytes to certificate. BIO_new_mem_buf failed" );
2641 return NULL;
2642 }
2643 #endif
2644
2645 /* certificate format is FORMAT_ASN1 or FORMAT_PEM or FORMAT_NETSCAPE */
2646 if (bytes[0] == '-' && bytes[1] == '-') /* FORMAT_PEM */
2647 {
2648 /* x = PEM_read_bio_X509_AUX(cert,NULL, (pem_password_cb *)password_callback, NULL); */
2649 ufdbLogError( "cannot convert bytes to certificate. PEM_read_bio_X509_AUX not yet implemented" );
2650 }
2651 else /* FORMAT_ASN1 (DER) */
2652 {
2653 /* d2i = DER/BER to internal */
2654 cert = d2i_X509( NULL, (unsigned char **) &bytes, nbytes );
2655 if (cert == NULL)
2656 {
2657 ufdbLogError( "cannot convert bytes to certificate. d2i_X509 failed" );
2658 print_errors();
2659 return NULL;
2660 }
2661 return cert;
2662 }
2663
2664 {
2665 int i;
2666 if (nbytes > 100) nbytes = 100;
2667 for (i = 0; i < nbytes; i++)
2668 ufdbLogMessage( " byte %2d, 0x%02x %c", i, bytes[i], (bytes[i] & 0x7f)|0x20 );
2669 }
2670
2671 ufdbLogError( "cannot convert bytes to certificate" );
2672 return NULL;
2673 }
2674 #endif
2675
2676
2677 /* see also https://en.wikipedia.org/wiki/Extended_Validation_Certificate */
2678 static const char * knownEVcertIssuerOIDTable[] =
2679 {
2680 "0.4.0.2042.1.4", /* ETSI */
2681 "0.4.0.2042.1.5", /* ETSI */
2682 "1.2.40.0.17.1.22", /* A-Trust */
2683 "1.2.392.200091.100.721.1", /* SECOM Trust Systems */
2684 "1.3.6.1.4.1.311.60.2.1.3", /* Symantec */
2685 "1.3.6.1.4.1.782.1.2.1.8.1", /* Network Solutions */
2686 "1.3.6.1.4.1.4146.1.1", /* GlobalSign */
2687 "1.3.6.1.4.1.6449.1.2.1.5.1", /* Comodo */
2688 "1.3.6.1.4.1.6334.1.100.1", /* Verizon Business (was: Cybertrust) */
2689 "1.3.6.1.4.1.8024.0.2.100.1.2", /* QuoVadis */
2690 "1.3.6.1.4.1.13177.10.1.3.10", /* Firmaprofesional */
2691 "1.3.6.1.4.1.14370.1.6", /* GeoTrust */
2692 "1.3.6.1.4.1.14777.6.1.1", /* Izenpe */
2693 "1.3.6.1.4.1.17326.10.14.2.1.2", /* Camerfirma */
2694 "1.3.6.1.4.1.17326.10.8.12.1.2", /* Camerfirma */
2695 "1.3.6.1.4.1.22234.2.5.2.3.1", /* Keynectis */
2696 "1.3.6.1.4.1.23223.2", /* Startcom Certification Authority */
2697 "1.3.6.1.4.1.23223.1.1.1", /* Startcom Certification Authority */
2698 "1.3.6.1.4.1.34697.2.1", /* AffirmTrust */
2699 "1.3.6.1.4.1.34697.2.2", /* AffirmTrust */
2700 "1.3.6.1.4.1.34697.2.3", /* AffirmTrust */
2701 "1.3.6.1.4.1.34697.2.4", /* AffirmTrust */
2702 "1.3.6.1.4.1.36305.2", /* Wosign */
2703 "2.16.578.1.26.1.3.3", /* BuyPass */
2704 "2.16.756.1.83.21.0", /* Swisscom */
2705 "2.16.756.1.89.1.2.1.1", /* SwissSign */
2706 "2.16.792.1.2.1.1.5.7.1.9", /* Kamu Sertifikasyon Merkezi */
2707 "2.16.792.3.0.4.1.1.4", /* E-Tugra */
2708 "2.16.840.1.114412.1.3.0.2", /* DigiCert */
2709 "2.16.840.1.114412.2.1", /* DigiCert */
2710 "2.16.840.1.114028.10.1.2", /* Entrust */
2711 "2.16.840.1.114413.1.7.23.3", /* Go Daddy */
2712 "2.16.840.1.114414.1.7.23.3", /* Starfield Technologies */
2713 "2.16.840.1.113733.1.7.48.1", /* Thawte */
2714 "2.16.840.1.113733.1.7.23.6", /* VeriSign */
2715 "2.16.840.1.114171.500.9", /* Wells Fargo */
2716 "2.16.840.1.114404.1.1.2.4.1", /* Trustwave */
2717 NULL
2718 };
2719
2720
2721 #if UFDB_SSL_SUPPORT
certificateIsEV(X509 * cert)2722 static int certificateIsEV(
2723 X509 * cert )
2724 {
2725 POLICYINFO * pinfo;
2726 STACK_OF(POLICYINFO) * policies;
2727 char * oid;
2728 int idlen;
2729 int i, j;
2730
2731 /* EV certificates are certificates with a well-known OID in the "Certificate Policies"
2732 * extension field.
2733 */
2734
2735 if ((policies = (STACK_OF(POLICYINFO) *) X509_get_ext_d2i( cert, NID_certificate_policies, NULL, NULL)) == NULL)
2736 {
2737 return 0;
2738 }
2739
2740 for (i = 0; i < sk_POLICYINFO_num(policies); i++)
2741 {
2742 if ((pinfo = sk_POLICYINFO_value(policies,i)) == NULL)
2743 {
2744 continue;
2745 }
2746
2747 if ((idlen = i2t_ASN1_OBJECT( NULL, 0, pinfo->policyid )) <= 0)
2748 {
2749 continue;
2750 }
2751
2752 oid = (char *) ufdbMalloc( idlen + 1 );
2753 if (i2t_ASN1_OBJECT( oid, idlen + 1, pinfo->policyid ) != idlen)
2754 {
2755 ufdbLogError( "cannot convert certificate OID" );
2756 }
2757
2758 if (ufdbGV.debug > 2)
2759 ufdbLogMessage( "X.509 policy extension OID = %s", oid );
2760
2761 for (j = 0; knownEVcertIssuerOIDTable[j] != NULL; j++)
2762 {
2763 if( strcmp( oid, knownEVcertIssuerOIDTable[j] ) == 0)
2764 {
2765 if (ufdbGV.debug > 1)
2766 ufdbLogMessage( "Certificate is EV. Issuer is %s", oid );
2767 ufdbFree( oid );
2768 sk_POLICYINFO_pop_free( policies, POLICYINFO_free );
2769 return 1;
2770 }
2771 }
2772
2773 ufdbFree( oid );
2774 }
2775
2776 sk_POLICYINFO_pop_free( policies, POLICYINFO_free );
2777 return 0;
2778 }
2779 #endif
2780
2781
2782 #ifdef UFDB_LOAD_CERT_CHAIN
2783
LoadCertificateByURL(char * URL)2784 static X509 * LoadCertificateByURL(
2785 char * URL )
2786 {
2787 int s;
2788 int n;
2789 int port;
2790 char * path;
2791 char * p;
2792 X509 * cert;
2793 char protocol[16];
2794 char domain[1024];
2795 char strippedUrl[UFDB_MAX_URL_LENGTH];
2796 char request[1024+UFDB_MAX_URL_LENGTH];
2797 char certbuffer[1024*16];
2798
2799 port = 80;
2800 UFDBstripURL( URL, strippedUrl, domain, protocol, &port );
2801 if (strcmp(protocol,"http") != 0 && port != 80)
2802 {
2803 ufdbLogError( "cannot load certificate of %s since it has no HTTP protocol", URL );
2804 return NULL;
2805 }
2806
2807 s = UFDBopenSocket( domain, port );
2808 if (s < 0)
2809 {
2810 ufdbLogError( "cannot load certificate of %s", URL );
2811 return NULL;
2812 }
2813
2814 path = strchr( URL, ':' ); /* strip http:// */
2815 if (path == NULL)
2816 path = URL;
2817 else
2818 path += 3;
2819 path = strchr( path, '/' ); /* strip domainname */
2820 if (path == NULL)
2821 path = "/";
2822
2823 /*******************************
2824 GET / HTTP/1.0
2825 User-Agent: Mozilla/5.0 (xxx) Gecko/20100722 Firefox/3.6.8
2826 Host: www.urlfilterdb.com:9443
2827 Accept: * / *
2828 Connection: Keep-Alive
2829 ********************************/
2830
2831 if (ufdbGV.debug)
2832 ufdbLogMessage( " LoadCertificateByURL %s %s", domain, path );
2833
2834 /* TO-DO: if there is a proxy, "CONNECT" must be used */
2835 sprintf( request, "GET %s HTTP/1.1\r\n"
2836 "User-Agent: " UFDB_USER_AGENT "\r\n"
2837 "Host: %s:%d\r\n"
2838 "Accept: */*\r\n"
2839 "Connection: Close\r\n"
2840 "\r\n",
2841 path,
2842 domain, port );
2843 n = strlen( request );
2844 if (write( s, request, n ) != n)
2845 {
2846 ufdbLogError( "cannot retrieve certificate for %s: write failed: %s", URL, strerror(errno) );
2847 close( s );
2848 return NULL;
2849 }
2850
2851 n = UFDBread( s, certbuffer, sizeof(certbuffer) );
2852 if (n < 0)
2853 {
2854 ufdbLogError( "cannot retrieve certificate for %s: read failed: %s", URL, strerror(errno) );
2855 close( s );
2856 return NULL;
2857 }
2858 close( s );
2859 p = strstr( certbuffer, "\r\n\r\n" );
2860 if (p != NULL)
2861 {
2862 if (ufdbGV.debug)
2863 ufdbLogMessage( " downloaded file from %s has no <CR><LF<CR><LF> separator", URL );
2864 n -= (p - certbuffer) - 4;
2865 p += 4;
2866 }
2867 else
2868 p = certbuffer;
2869 if (ufdbGV.debug)
2870 ufdbLogMessage( " certificate has %d bytes", n );
2871
2872 /* And now begins the real magic: add the certificate to the certificate list.
2873 * It is a recursive process since a EV chain usually adds more than one certificate.
2874 */
2875 cert = convert_bytes_to_cert( (unsigned char *) p, n );
2876 if (cert == NULL)
2877 return cert;
2878
2879 if (ufdbGV.debug)
2880 {
2881 char issuer[1024];
2882 char subject[1024];
2883
2884 issuer[0] = '\0';
2885 (void) X509_NAME_oneline( X509_get_issuer_name(cert), issuer, 1023 );
2886 issuer[1023] = '\0';
2887
2888 subject[0] = '\0';
2889 (void) X509_NAME_oneline( X509_get_subject_name(cert), subject, 1023 );
2890 subject[1023] = '\0';
2891
2892 ufdbLogMessage( " issuer is %s\nsubject is %s", issuer, subject );
2893 }
2894 UFDBloadIntermediateCertificates( cert );
2895
2896 return cert;
2897 /* TO-DO: after validation: add URL to a cache to prevent multiple downloads */
2898 }
2899 #endif
2900
2901
2902 #ifdef UFDB_LOAD_CERT_CHAIN
2903
CAaddURL(STACK_OF (STRING)** list,ASN1_IA5STRING * url)2904 static void CAaddURL(
2905 STACK_OF(STRING) ** list,
2906 ASN1_IA5STRING * url )
2907 {
2908 X509 * cert;
2909 char * tmp;
2910
2911 if (url->type != V_ASN1_IA5STRING)
2912 return;
2913 if (!url->data || !url->length)
2914 return;
2915
2916 #ifdef UFDB_STORE_X509_URIS
2917 if (!*list)
2918 *list = sk_ASN1_STRING_TABLE_new( *list );
2919 if (!*list)
2920 return;
2921
2922 /* prevent duplicates */
2923 if (sk_ASN1_STRING_TABLE_find( *list, (char *) url->data ) != -1)
2924 return;
2925 tmp = ufdbStrdup( (char *) url->data );
2926 #else
2927 tmp = (char *) url->data;
2928 #endif
2929
2930 if (ufdbGV.debug)
2931 ufdbLogMessage( " CAaddURL %s", tmp );
2932
2933 if (!ufdbCacertsLoaded)
2934 return;
2935
2936 #if 1
2937 return; /* just for now: do not load the certificate */
2938 #endif
2939
2940 cert = LoadCertificateByURL( tmp );
2941 if (cert == NULL)
2942 {
2943 ufdbLogMessage( "error parsing certificate from %s", tmp );
2944 }
2945 else
2946 {
2947 X509_free( cert );
2948 }
2949
2950 #ifdef UFDB_STORE_X509_URIS
2951 if (!tmp || !sk_ASN1_STRING_TABLE_push(*list, tmp))
2952 {
2953 X509_email_free( *list );
2954 *list = NULL;
2955 }
2956 #endif
2957 }
2958
2959 #endif
2960
2961
2962 #ifdef UFDB_LOAD_CERT_CHAIN
2963
UFDBloadIntermediateCertificates(X509 * cert)2964 static int UFDBloadIntermediateCertificates(
2965 X509 * cert )
2966 {
2967 AUTHORITY_INFO_ACCESS * aia_data;
2968 ACCESS_DESCRIPTION * aia_descr;
2969 STACK_OF(STRING) * ocsp_list;
2970 int i;
2971 int is_ev;
2972
2973 aia_data = X509_get_ext_d2i( cert, NID_info_access, NULL, NULL );
2974
2975 if (ufdbGV.debug)
2976 ufdbLogMessage( "UFDBloadIntermediateCertificates: aia_data %s", (aia_data==NULL?"is NULL":"has data") );
2977
2978 if (aia_data == NULL)
2979 return 0;
2980
2981 is_ev = certificateIsEV( cert );
2982 ocsp_list = NULL;
2983 if (ufdbGV.debug)
2984 ufdbLogMessage( " #aia_data = %d", sk_ACCESS_DESCRIPTION_num(aia_data) );
2985 for (i = 0; i < sk_ACCESS_DESCRIPTION_num(aia_data); i++)
2986 {
2987 int nid;
2988 aia_descr = sk_ACCESS_DESCRIPTION_value( aia_data, i );
2989 nid = OBJ_obj2nid(aia_descr->method);
2990 #if 0
2991 if (ufdbGV.debug && nid != NID_ad_OCSP && nid != NID_ad_ca_issuers)
2992 ufdbLogMessage( " OBJ_obj2nid = %d", nid );
2993 #endif
2994 /* We do not do OCSP. This is left for the browser. */
2995 if (nid == NID_ad_ca_issuers)
2996 {
2997 if (aia_descr->location->type == GEN_URI)
2998 {
2999 if (ufdbGV.debug)
3000 ufdbLogMessage( " certificate AIA CA-ISSUERS URI:" );
3001 CAaddURL( &ocsp_list, aia_descr->location->d.uniformResourceIdentifier );
3002 is_ev = 1;
3003 }
3004 }
3005 }
3006
3007 /* TODO: download and verify intermediate certificates: UFDB_STORE_X509_URIS SSL_CTX_use_certificate() */
3008 #ifdef UFDB_STORE_X509_URIS
3009 X509_email_free( ocsp_list );
3010 #endif
3011
3012 return is_ev;
3013 }
3014
3015 #endif
3016
3017
3018 #if UFDB_LOOK_AT_AIA || 1
3019
3020 #if UFDB_SSL_SUPPORT
3021 GCC_NO_INLINE
CertificatesHasAIA(X509 * cert)3022 static int CertificatesHasAIA(
3023 X509 * cert )
3024 {
3025 AUTHORITY_INFO_ACCESS * aia_data;
3026 ACCESS_DESCRIPTION * aia_descr;
3027 int i;
3028 int has_uri;
3029
3030 aia_data = (AUTHORITY_INFO_ACCESS *) X509_get_ext_d2i( cert, NID_info_access, NULL, NULL );
3031
3032 if (ufdbGV.debug > 2)
3033 ufdbLogMessage( " CertificatesHasAIA: aia_data %s", (aia_data==NULL?"is NULL":"has data") );
3034
3035 if (aia_data == NULL)
3036 return 0;
3037
3038 has_uri = 0;
3039 for (i = 0; i < sk_ACCESS_DESCRIPTION_num(aia_data); i++)
3040 {
3041 int nid;
3042 aia_descr = sk_ACCESS_DESCRIPTION_value( aia_data, i );
3043 nid = OBJ_obj2nid(aia_descr->method);
3044 if (nid == NID_ad_ca_issuers)
3045 {
3046 if (aia_descr->location->type == GEN_URI)
3047 {
3048 if (ufdbGV.peek || ufdbGV.debug > 1)
3049 ufdbLogMessage( " AIA CA Issuers URI: %s", aia_descr->location->d.uniformResourceIdentifier->data );
3050 if (strncasecmp( "http", (char*) aia_descr->location->d.uniformResourceIdentifier->data, 4 ) == 0)
3051 has_uri = 1;
3052 }
3053 }
3054 }
3055
3056 sk_ACCESS_DESCRIPTION_pop_free( aia_data, ACCESS_DESCRIPTION_free );
3057
3058 return has_uri;
3059 }
3060 #endif
3061 #endif
3062
3063
3064 #if 0
3065 static int my_verify_cert(
3066 X509_STORE * trustedCertStore,
3067 X509 * cert,
3068 STACK_OF(X509) * certStack )
3069 {
3070 X509_STORE_CTX * csc;
3071 int retval;
3072
3073 if (ufdbGV.debug)
3074 ufdbLogMessage( "my_verify_cert" );
3075
3076 csc = X509_STORE_CTX_new();
3077 if (csc == NULL)
3078 {
3079 ufdbLogError( "cannot verify certificate. X509_STORE_CTX_new failed" );
3080 /* we simply don't know if the certificate is OK or not... OK */
3081 return X509_V_OK;
3082 }
3083 if (!X509_STORE_CTX_init( csc, trustedCertStore, cert, certStack ))
3084 {
3085 ufdbLogError( "cannot verify certificate. X509_STORE_CTX_init failed" );
3086 /* we simply don't know if the certificate is OK or not... OK */
3087 return X509_V_OK;
3088 }
3089 /* X509_VERIFY_PARAM_set_flags( X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL ); */
3090 X509_STORE_CTX_set_purpose( csc, X509_PURPOSE_SSL_SERVER ); /* or ??? X509_PURPOSE_SSL_SERVER */
3091
3092 #if 0
3093 X509_STORE_CTX_set_verify_cb( csc, certVerifycallback );
3094 #endif
3095
3096 retval = X509_verify_cert( csc );
3097 if (retval > 0)
3098 {
3099 if (ufdbGV.debug)
3100 ufdbLogMessage( "my_verify_cert: certificate is OK" );
3101 retval = X509_V_OK;
3102 }
3103 else
3104 {
3105 retval = X509_STORE_CTX_get_error( csc );
3106 if (ufdbGV.debug)
3107 {
3108 ufdbLogError( "my_verify_cert: certificate verification failed\n"
3109 "certificate error code is %d",
3110 retval );
3111 print_errors();
3112 }
3113 }
3114 X509_STORE_CTX_free( csc );
3115
3116 return retval;
3117 }
3118 #endif
3119
3120
3121 /* ssl_dns_name - Extract valid DNS name from subjectAltName value */
3122 #if UFDB_SSL_SUPPORT
ssl_dns_name(const GENERAL_NAME * gn)3123 static const char * ssl_dns_name(
3124 const GENERAL_NAME * gn )
3125 {
3126 const char * dnsname;
3127 int len;
3128
3129 /*
3130 * We expect the OpenSSL library to construct GEN_DNS extension objects as
3131 * ASN1_IA5STRING values. Check we got the right union member.
3132 */
3133 if (ASN1_STRING_type(gn->d.ia5) != V_ASN1_IA5STRING)
3134 {
3135 ufdbLogError( "ssl_dns_name: invalid ASN1 value type in subjectAltName of TLS/SSL certificate" );
3136 return NULL;
3137 }
3138
3139 #define TRIM0(s, l) do { while ((l) > 0 && (s)[(l)-1] == 0) --(l); } while (0)
3140 /*
3141 * Safe to treat as an ASCII string possibly holding a DNS name
3142 */
3143 dnsname = (char *) ASN1_STRING_get0_data( gn->d.ia5 );
3144 len = ASN1_STRING_length( gn->d.ia5 );
3145 TRIM0( dnsname, len );
3146
3147 /*
3148 * Per Dr. Steven Henson of the OpenSSL development team, ASN1_IA5STRING
3149 * values can have internal ASCII NUL values in this context because
3150 * their length is taken from the decoded ASN1 buffer, a trailing NUL is
3151 * always appended to make sure that the string is terminated, but the
3152 * ASN.1 length may differ from strlen().
3153 */
3154 if (len != (int) strlen(dnsname))
3155 {
3156 ufdbLogError( "ssl_dns_name: internal NUL in subjectAltName of TLS/SSL certificate" );
3157 return "error-converting-name";
3158 }
3159
3160 return dnsname;
3161 }
3162 #endif
3163
3164
3165 /* return values: UFDB_API_OK
3166 * UFDB_API_ERR_INVALID_CERT
3167 * UFDB_API_ERR_IS_YAHOOMSG
3168 * UFDB_API_ERR_IS_AIM
3169 * UFDB_API_ERR_IS_TUNNEL (ssh, tor, logme.in)
3170 */
3171 #if UFDB_SSL_SUPPORT
3172 GCC_NO_INLINE
ssl_check_certificate(SSL * ssl,const char * hostname,int portnumber,char * cn)3173 static int ssl_check_certificate(
3174 SSL * ssl,
3175 const char * hostname,
3176 int portnumber,
3177 char * cn )
3178 {
3179 const char * altPtr;
3180 char * matchedAltPtr;
3181 X509 * cert;
3182 long vresult;
3183 int success;
3184 int cert_stack_size;
3185 int is_chained;
3186 int is_ev;
3187 int is_ip;
3188 int match;
3189 int altNameSeen;
3190 char * _cneq;
3191 const char * protocol;
3192 char issuer[1024];
3193 char subject[1024];
3194 STACK_OF(X509) * certStack;
3195 STACK_OF(GENERAL_NAME) * altNames;
3196
3197 success = 2;
3198 issuer[0] = '\0';
3199 subject[0] = '\0';
3200
3201 protocol = sslver2string( SSL_version(ssl) );
3202 cert = SSL_get_peer_certificate( ssl );
3203 certStack = SSL_get_peer_cert_chain( ssl );
3204
3205 if (hostname == NULL)
3206 {
3207 success = 0;
3208 ufdbLogError( "ssl_check_certificate: hostname=NULL" );
3209 goto no_cert; /* must bail out since hostname is NULL */
3210 }
3211
3212 if (cert == NULL)
3213 {
3214 success = 0;
3215 ufdbLogError( "site %s:%d has NO TLS/SSL certificate", hostname, portnumber );
3216 goto no_cert; /* must bail out since CERT is NULL */
3217 }
3218
3219 (void) X509_NAME_oneline( X509_get_issuer_name(cert), issuer, 1023 );
3220 issuer[1023] = '\0';
3221 if (issuer[0] == '\0')
3222 {
3223
3224 strcpy( issuer, "--no-issuer-found-in-certificate--" );
3225 success = 0;
3226 ufdbLogError( "site %s:%d certificate has no issuer", hostname, portnumber );
3227 }
3228 (void) X509_NAME_oneline( X509_get_subject_name(cert), subject, 1023 );
3229 subject[1023] = '\0';
3230 if (subject[0] == '\0')
3231 {
3232 strcpy( subject, "--no-subject-found-in-certificate--" );
3233 success = 0;
3234 ufdbLogError( "site %s:%d certificate has no subject", hostname, portnumber );
3235 }
3236
3237 #if 0
3238 UFDBloadIntermediateCertificates( cert );
3239 #endif
3240
3241 if (ufdbGV.debug > 1 || ufdbGV.peek)
3242 CertificatesHasAIA( cert );
3243
3244 is_ev = certificateIsEV( cert );
3245 cert_stack_size = sk_X509_num( certStack );
3246 is_chained = (cert_stack_size > 1);
3247 is_ip = hostnameIsIP( hostname );
3248
3249 #if 0
3250 if (ufdbGV.debug || ufdbGV.debugAim)
3251 {
3252 ufdbLogMessage( " issuer: %s\nhostnameIsIP(%s): %d", issuer, hostname, is_ip );
3253 }
3254 #endif
3255
3256 /* Yuck! AIM connects with a (chained) certificate but does not allow HTTP/1.1 */
3257 /* TODO: investigate client.web.aol.com */
3258 if (strstr( issuer, "/O=America Online Inc./CN=AOL Member CA" ) != NULL)
3259 {
3260 ufdbLogMessage( "%s certificate on %s:%d is used by AOL or AIM since issuer is %s",
3261 protocol, hostname, portnumber, issuer );
3262 X509_free( cert );
3263 return UFDB_API_ERR_IS_AIM;
3264 }
3265
3266 #if 0
3267 if (certStack != NULL)
3268 {
3269 AUTHORITY_INFO_ACCESS * aia_data;
3270
3271 /* the server uses a chain of certificates ? */
3272 if (ufdbGV.debug)
3273 {
3274 int i;
3275 ufdbLogMessage( "size of stack of certificates is %d", sk_X509_num(certStack) );
3276 for (i = 0; i < sk_X509_num(certStack); i++)
3277 {
3278 X509 * x;
3279 char buffer[512];
3280
3281 x = sk_X509_value( certStack, i );
3282 (void) X509_NAME_oneline( X509_get_subject_name(x), buffer, sizeof(buffer) );
3283 ufdbLogMessage( " %s", buffer );
3284 }
3285 }
3286 aia_data = X509_get_ext_d2i( cert, NID_info_access, NULL, NULL );
3287 is_ev = (aia_data != NULL);
3288 }
3289 #endif
3290
3291 _cneq = strstr( subject, "/CN=" );
3292 if (_cneq != NULL)
3293 {
3294 ufdbStrncpy( cn, _cneq+4, 1023 );
3295 }
3296 else
3297 {
3298 if (subject[0] == 'C' && subject[1] == 'N' && subject[2] == '=')
3299 {
3300 _cneq = subject;
3301 ufdbStrncpy( cn, _cneq+3, 1023 );
3302 }
3303 }
3304 if (_cneq != NULL)
3305 {
3306 char * sep;
3307 sep = strchr( cn, '/' );
3308 if (sep != NULL)
3309 *sep = '\0';
3310 }
3311 else
3312 strcpy( cn, "certificate subject has no CommonName (/CN=)" );
3313
3314 if (ufdbGV.debug)
3315 {
3316 if (cert_stack_size > 1)
3317 ufdbLogMessage( " received %d certificates from %s", cert_stack_size, hostname );
3318 ufdbLogMessage( " %s%s%s certificate for %s has issuer '%s'",
3319 is_chained ? "chained " : "",
3320 is_ev ? "EV " : "",
3321 protocol,
3322 hostname, issuer );
3323 if (ufdbGV.debug > 1)
3324 {
3325 ufdbLogMessage( " %s%s%s certificate for %s has subject '%s' (category=%s)",
3326 is_chained ? "chained " : "",
3327 is_ev ? "EV " : "",
3328 protocol,
3329 hostname, subject,
3330 _cneq != NULL && *cn != '\0' ? ufdbCategoryName(cn) : "unknown" );
3331 }
3332 else
3333 ufdbLogMessage( " %s%s%s certificate for %s has subject '%s'",
3334 is_chained ? "chained " : "",
3335 is_ev ? "EV " : "",
3336 protocol,
3337 hostname, subject );
3338 }
3339
3340 if (!ufdbCacertsLoaded)
3341 {
3342 ufdbLogMessage( "No CA certificates are loaded. Cannot verify signature of "
3343 "%s%scertificate for %s:%d. Marking it as invalid.",
3344 is_chained ? "chained " : "",
3345 is_ev ? "EV " : "",
3346 hostname, portnumber );
3347 /* TODO: investigate free cert chain */
3348 X509_free( cert );
3349 return UFDB_API_ERR_INVALID_CERT;
3350 }
3351
3352 if (strcmp( cn, "*.uc.cn" ) == 0 || strcmp( cn, "*.ucweb.com" ) == 0)
3353 {
3354 ufdbLogMessage( "%s certificate for %s:%d: has a certificate with \"ucweb.com\" or \"uc.cn\" and "
3355 "is considered a proxy *****\n"
3356 "issuer: %s\n"
3357 "subject: %s",
3358 protocol,
3359 hostname, portnumber, issuer, subject );
3360 X509_free( cert );
3361 return UFDB_API_ERR_TUNNEL;
3362 }
3363
3364 if (_cneq != NULL)
3365 {
3366 if (strstr( _cneq, ".logme.in" ) != NULL || strstr( _cneq, ".logmein.com" ) != NULL)
3367 {
3368 ufdbLogMessage( "%s certificate for %s:%d: has a certificate with \"logme.in\" and "
3369 "is considered a proxy *****\n"
3370 "issuer: %s\n"
3371 "subject: %s",
3372 protocol,
3373 hostname, portnumber, issuer, subject );
3374 X509_free( cert );
3375 return UFDB_API_ERR_TUNNEL;
3376 }
3377 }
3378
3379 /* TODO: check certificate for p2p sites and return new status code UFDB_API_ERR_P2P */
3380 /* TODO: dediseedbox.com ... */
3381
3382 matchedAltPtr = NULL;
3383 vresult = SSL_get_verify_result( ssl );
3384
3385 if (vresult == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY)
3386 {
3387 if (ufdbCacertsLoaded)
3388 {
3389 if (is_ev || is_chained)
3390 {
3391 success = 1;
3392 if (ufdbGV.debug || ufdbGV.httpsOfficialCertificate)
3393 ufdbLogMessage( "%s%s%s certificate signature of %s:%d cannot be verified, assuming it is OK",
3394 is_chained ? "chained " : "",
3395 is_ev ? "EV " : "",
3396 protocol,
3397 hostname, portnumber );
3398 }
3399 else
3400 {
3401 /* Tor uses certificates where the issuer only has /CN=www.BOGUS.net and the subject also only has /CN=www.BOGUS.net
3402 * So when the Country and Organization are missing from the issuer and subject, it is marked as a Tor Tunnel.
3403 */
3404 if (strstr( issuer, "/C=" ) == NULL &&
3405 strstr( issuer, "/O=" ) == NULL &&
3406 strstr( issuer, "/DC=" ) == NULL &&
3407 strstr( subject, "/C=" ) == NULL &&
3408 strstr( subject, "/OU=" ) == NULL &&
3409 strstr( subject, "/O=" ) == NULL &&
3410 strstr( issuer, "/CN=" ) != NULL &&
3411 strstr( subject, "/CN=" ) != NULL &&
3412 issuer[0] != '-' &&
3413 strchr( subject, '.' ) != NULL &&
3414 strchr( issuer, '.' ) != NULL)
3415 {
3416 ufdbLogMessage( "%s certificate for %s:%d: has an UNRECOGNISED ISSUER and "
3417 "has the characteristics of a Tor proxy *****\n"
3418 "issuer: %s\n"
3419 "subject: %s",
3420 protocol, hostname, portnumber, issuer, subject );
3421 X509_free( cert );
3422 return UFDB_API_ERR_TUNNEL;
3423 }
3424
3425 if (ufdbGV.debug || ufdbGV.httpsOfficialCertificate)
3426 ufdbLogMessage( "%s%s%s certificate for %s:%d: UNRECOGNISED ISSUER (maybe a certificate chain issue) *****\nissuer: %s\nsubject: %s",
3427 is_chained ? "chained " : "",
3428 is_ev ? "EV " : "",
3429 protocol, hostname, portnumber,
3430 issuer, subject );
3431 success = 0;
3432 }
3433 }
3434 else
3435 {
3436 ufdbLogError( "SSL_get_verify_result() for %s:%d is X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY (ignored)\n"
3437 "Check the existence and file permissions of %s",
3438 hostname, portnumber,
3439 ufdbGV.CAcertsFile );
3440 }
3441 }
3442 else if (vresult == X509_V_OK)
3443 {
3444 if (strstr( issuer, "/DC=microsoft/" ) != NULL &&
3445 strstr( subject, "skype.com" ) != NULL)
3446 {
3447 ufdbLogMessage( "%s certificate for %s:%d: has a certificate for Skype *****\n"
3448 "issuer: %s\n"
3449 "subject: %s",
3450 protocol, hostname, portnumber, issuer, subject );
3451 X509_free( cert );
3452 return UFDB_API_ERR_IS_SKYPE;
3453 }
3454
3455 if (is_chained &&
3456 strstr( subject, "citrixonline.com" ) != NULL)
3457 {
3458 ufdbLogMessage( "%s certificate for %s:%d: has a certificate for CitrixOnline *****\n"
3459 "issuer: %s\n"
3460 "subject: %s",
3461 protocol, hostname, portnumber, issuer, subject );
3462 X509_free( cert );
3463 return UFDB_API_ERR_IS_CITRIXONLINE;
3464 }
3465 }
3466 else
3467 {
3468 success = 0;
3469 switch (vresult)
3470 {
3471 case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
3472 if (strstr( subject, "/CN=AnyNet Relay" ) != NULL)
3473 {
3474 ufdbLogMessage( "%s certificate for %s:%d: has a certificate for AnyDesk *****\n"
3475 "issuer: %s\nsubject: %s",
3476 protocol, hostname, portnumber, issuer, subject );
3477 X509_free( cert );
3478 return UFDB_API_ERR_IS_ANYDESK;
3479 }
3480 else
3481 if (is_chained &&
3482 strstr( subject, "citrixonline.com" ) != NULL)
3483 {
3484 ufdbLogMessage( "%s certificate for %s:%d: has a certificate for CitrixOnline *****\n"
3485 "issuer: %s\nsubject: %s",
3486 protocol, hostname, portnumber, issuer, subject );
3487 X509_free( cert );
3488 return UFDB_API_ERR_IS_CITRIXONLINE;
3489 }
3490 ufdbLogError( "%s certificate for %s:%d has a SELF-SIGNED certificate in chain *****\nissuer: %s",
3491 protocol, hostname, portnumber, issuer );
3492 break;
3493 case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
3494 /* Yuck! Yahoo IM connect to addresses like <IP>:443 with a self-signed certificate. */
3495 if (strstr( issuer, "/O=Yahoo/OU=Messenger/CN=undermine.corp/" ) != NULL)
3496 {
3497 ufdbLogMessage( "%s certificate on %s:%d is used for Yahoo IM since issuer is %s",
3498 protocol, hostname, portnumber, issuer );
3499 X509_free( cert );
3500 return UFDB_API_ERR_IS_YAHOOMSG;
3501 }
3502 else
3503 {
3504 ufdbLogError( "%s certificate for %s:%d has a DEPTH-ZERO SELF-SIGNED certificate *****\nissuer: %s",
3505 protocol, hostname, portnumber, issuer );
3506 }
3507 break;
3508 case X509_V_ERR_CERT_NOT_YET_VALID:
3509 ufdbLogError( "%s certificate for %s:%d has a NOT YET VALID DATE *****",
3510 protocol, hostname, portnumber );
3511 break;
3512 case X509_V_ERR_CERT_HAS_EXPIRED:
3513 ufdbLogError( "%s certificate for %s:%d has EXPIRED *****", protocol, hostname, portnumber );
3514 break;
3515 case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
3516 ufdbLogError( "%s certificate for %s:%d has an UNRECOGNISED ISSUER ?!? *****\nissuer: %s",
3517 protocol, hostname, portnumber, issuer );
3518 break;
3519 case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
3520 ufdbLogMessage( "%s%s%s certificate for %s:%d cannot be verified.",
3521 is_chained ? "chained " : "",
3522 is_ev ? "EV " : "",
3523 protocol, hostname, portnumber );
3524 break;
3525 case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
3526 ufdbLogMessage( "%s%s%s certificate for %s:%d CANNOT DECRYPT SIGNATURE.",
3527 is_chained ? "chained " : "",
3528 is_ev ? "EV " : "",
3529 protocol, hostname, portnumber );
3530 break;
3531 case X509_V_ERR_CERT_SIGNATURE_FAILURE:
3532 if (is_chained || is_ev)
3533 {
3534 success = 1;
3535 ufdbLogMessage( "%s%s%s certificate for %s:%d has an unresolvable certificate signature failure.\n"
3536 "assuming that the signature is OK.",
3537 is_chained ? "chained " : "",
3538 is_ev ? "EV " : "",
3539 protocol, hostname, portnumber );
3540 }
3541 else
3542 ufdbLogMessage( "%s%s%s certificate for %s:%d CERTIFICATE SIGNATURE FAILURE.",
3543 is_chained ? "chained " : "",
3544 is_ev ? "EV " : "",
3545 protocol, hostname, portnumber );
3546 break;
3547 default:
3548 ufdbLogError( "%s certificate VERIFICATION ERROR for %s:%d %ld %s *****",
3549 protocol, hostname, portnumber, vresult, X509_verify_cert_error_string(vresult) );
3550 }
3551 /* Fall through */
3552 }
3553
3554 if (success>1 && ufdbGV.debug)
3555 ufdbLogMessage( " %s%s%s certificate for %s:%d is signed by a CA (OK)",
3556 is_chained ? "chained " : "",
3557 is_ev ? "EV " : "",
3558 protocol, hostname, portnumber );
3559
3560 /*
3561 * Check that hostname matches the common name in the certificate.
3562 * rfc2818:
3563 * "If a subjectAltName extension of type dNSName is present,
3564 * that MUST be used as the identity."
3565 * If not, we will use the commonName as the identity.
3566 */
3567 match = 0;
3568
3569 altNameSeen = 0;
3570
3571 altNames = (STACK_OF(GENERAL_NAME) *) X509_get_ext_d2i( cert, NID_subject_alt_name, NULL, NULL );
3572 if (altNames)
3573 {
3574 const GENERAL_NAME * check;
3575 int numAltNames;
3576 int i;
3577
3578 altPtr = "-";
3579 numAltNames = sk_GENERAL_NAME_num( altNames );
3580 for (i = 0; i < numAltNames; i++)
3581 {
3582 check = sk_GENERAL_NAME_value( altNames, i );
3583 if (check->type == GEN_DNS)
3584 {
3585 const char * d;
3586
3587 altPtr = ssl_dns_name( check );
3588 altNameSeen = 1;
3589
3590 /* google uses a very large array of altNames for google and youtube */
3591
3592 if (ufdbGV.debug > 1)
3593 ufdbLogMessage( " %s certificate for %s:%d has subjectAltName %s (category=%s)",
3594 protocol, hostname, portnumber, altPtr, ufdbCategoryName(altPtr) );
3595 else if (ufdbGV.debug)
3596 ufdbLogMessage( " %s certificate for %s:%d has subjectAltName %s",
3597 protocol, hostname, portnumber, altPtr );
3598
3599 if (is_ip &&
3600 (ufdbGV.YoutubeEdufilter || ufdbGV.allowGoogleHTTPSusingIP) &&
3601 strstr( issuer, "/O=Google Inc/" ) != NULL &&
3602 ((d = strstr( altPtr, "google.com" )) != NULL && strcmp( d, "google.com" ) == 0))
3603 {
3604 if (ufdbGV.debug)
3605 ufdbLogMessage( "%s certificate for %s:%d is used by %s (allowed)",
3606 protocol, hostname, portnumber, altPtr );
3607 sk_GENERAL_NAME_pop_free( altNames, GENERAL_NAME_free );
3608 X509_free( cert );
3609 return UFDB_API_OK;
3610 }
3611
3612 if (matchHostname( altPtr, hostname ))
3613 {
3614 match = 1;
3615 if (!ufdbGV.debug)
3616 break;
3617 matchedAltPtr = ufdbStrdup( altPtr );
3618 }
3619 }
3620 }
3621
3622 if (altNameSeen)
3623 {
3624 if (!match)
3625 {
3626 if (ufdbGV.debug || ufdbGV.httpsOfficialCertificate)
3627 ufdbLogMessage( "%s certificate has subjectAltName \"%s\" which does NOT MATCH hostname \"%s\". Certificate is invalid.",
3628 protocol, altPtr, hostname );
3629 success = 0;
3630 }
3631 else
3632 if (ufdbGV.debug)
3633 ufdbLogMessage( "%s certificate with subjectAltName \"%s\" matches hostname \"%s\"",
3634 protocol, matchedAltPtr, hostname );
3635 }
3636 sk_GENERAL_NAME_pop_free( altNames, GENERAL_NAME_free );
3637 }
3638
3639 if (matchedAltPtr != NULL)
3640 ufdbFree( matchedAltPtr );
3641
3642 if (!altNameSeen)
3643 {
3644 int i, j;
3645 X509_NAME * name;
3646 unsigned char * nulstr = (unsigned char *) "";
3647 unsigned char * commonName = nulstr;
3648
3649 if (ufdbGV.debug)
3650 ufdbLogMessage( "%s certificate for %s:%d has no subjectAltName", protocol, hostname, portnumber );
3651
3652 i = -1;
3653 name = X509_get_subject_name( cert );
3654 if (name != NULL)
3655 while ((j=X509_NAME_get_index_by_NID(name,NID_commonName,i)) >= 0)
3656 i = j;
3657
3658 /* now we have the name entry and convert it to a string */
3659 if (i >= 0)
3660 {
3661 ASN1_STRING * tmp;
3662
3663 tmp = X509_NAME_ENTRY_get_data( X509_NAME_get_entry(name,i) );
3664
3665 /* In OpenSSL 0.9.7d and earlier ASN1_STRING_to_UTF8 fails if string is already UTF8 :-) */
3666 #if defined(OPENSSL_VERSION_NUMBER)
3667 #if OPENSSL_VERSION_NUMBER <= 0x0090704fL
3668 if (ufdbGV.debug)
3669 ufdbLogMessage( " OpenSSL library version is %08X", OPENSSL_VERSION_NUMBER );
3670 if (tmp != NULL && ASN1_STRING_type(tmp) == V_ASN1_UTF8STRING)
3671 {
3672 j = ASN1_STRING_length( tmp );
3673 if (j >= 0)
3674 {
3675 commonName = ufdbMalloc( j+1 );
3676 memcpy( commonName, ASN1_STRING_data(tmp), j );
3677 commonName[j] = '\0';
3678 }
3679 }
3680 else
3681 {
3682 j = ASN1_STRING_to_UTF8( &commonName, tmp );
3683 }
3684 #else
3685 j = ASN1_STRING_to_UTF8( &commonName, tmp );
3686 #endif
3687 #else
3688 j = ASN1_STRING_to_UTF8( &commonName, tmp );
3689 #endif
3690 }
3691
3692 if (commonName == nulstr)
3693 commonName = NULL;
3694 if (commonName == NULL)
3695 {
3696 ufdbLogError( "%s certificate for %s:%d has no CommonName (CN). Certificate is invalid.",
3697 protocol, hostname, portnumber );
3698 success = 0;
3699 }
3700 else
3701 {
3702 if ( !matchHostname( (char *) commonName, hostname ))
3703 {
3704 ufdbLogError( "%s certificate CommonName '%s' does NOT match hostname `%s'. Certificate is invalid.",
3705 protocol, commonName, hostname );
3706 success = 0;
3707 }
3708 OPENSSL_free( commonName );
3709 }
3710 }
3711
3712 print_errors();
3713
3714 if (success && ufdbGV.debug)
3715 ufdbLogMessage( "%s certificate matches hostname %s", protocol, hostname );
3716
3717 /* TODO: investigate free cert chain */
3718 X509_free( cert );
3719
3720 no_cert:
3721 return success ? UFDB_API_OK : UFDB_API_ERR_INVALID_CERT;
3722 }
3723 #endif
3724
3725
3726 /*
3727 * Probe a port (often 443) to see what protocol is used.
3728 *
3729 * NOTE: This function may take up to 15 seconds to complete !
3730 *
3731 * return values:
3732 * function: UFDB_API_OK / UFDB_API_ERR_SOCKET / UFDB_API_BEING_VERIFIED / status(UFDB_API_ERR_TUNNEL etc)
3733 * cn: CN of the TLS/SSL certificate
3734 * certErrors: UFDB_API_OK / UFDB_API_ERR_INVALID_CERT
3735 * content: caller must free() it.
3736 *
3737 */
3738 #if UFDB_SSL_SUPPORT
UFDBsslPeekServer(const char * hostname,int portnumber,char * cn,int * certErrors,char ** content,int worker)3739 int UFDBsslPeekServer(
3740 const char * hostname,
3741 int portnumber,
3742 char * cn,
3743 int * certErrors,
3744 char ** content,
3745 int worker )
3746 {
3747 struct httpsInfo * info;
3748 int s;
3749 int status;
3750 int n;
3751 struct timeval tv;
3752
3753 *cn = '\0';
3754 *certErrors = UFDB_API_OK;
3755 *content = NULL;
3756 s = -9999;
3757
3758 if (ufdbGV.tunnelCheckMethod == UFDB_API_HTTPS_CHECK_OFF)
3759 return UFDB_API_OK;
3760
3761 if (ufdbGV.reconfig || ufdbGV.terminating)
3762 return UFDB_API_OK;
3763 /* note that after a database reload this result is removed from the cache */
3764
3765 if (ufdbGV.debug > 1)
3766 ufdbLogMessage( "W%03d: UFDBsslPeekServer %s:%d start", worker, hostname, portnumber );
3767
3768 /* When a HTTPS port is used, we need to check the following:
3769 * if SSL+HTTP is spoken
3770 * if not SSL, is XMPP/Jabber/Google Talk
3771 * if not SSL, is Skype or an unknown protocol
3772 * if SSL has a valid certificate
3773 * detect categories based on CommonName and/or patterns in the certificate
3774 * if SSL+HTTP, detect known tunnels based on content
3775 */
3776
3777 info = NULL;
3778 status = lookupHTTPScache( hostname, portnumber, 1, &info );
3779
3780 if (ufdbGV.debug > 1)
3781 ufdbLogMessage( "W%03d: UFDBsslPeekServer %s:%d cached status %s",
3782 worker, hostname, portnumber, ufdbAPIstatusString(status) );
3783
3784 if (status == UFDB_API_ERR_OUTDATED)
3785 {
3786 /* this thread is going to do the verification now */
3787 updateHTTPScache( hostname, portnumber, UFDB_API_BEING_VERIFIED, 0, &info );
3788 info->category[0] = '\0';
3789 info->cn[0] = '\0';
3790 ufdbFree( info->content );
3791 info->content = NULL;
3792 }
3793 else
3794 if (status == UFDB_API_ERR_NULL) /* not found in cache, fresh entry */
3795 {
3796 /* this thread is going to do the verification now */
3797 insertHTTPScache( hostname, portnumber, UFDB_API_BEING_VERIFIED, 1, &info );
3798 }
3799 else if (status == UFDB_API_REQ_QUEUED) /* found in cache queue */
3800 {
3801 /* this thread is going to do the verification now */
3802 updateHTTPScache( hostname, portnumber, UFDB_API_BEING_VERIFIED, 0, &info );
3803 }
3804 else
3805 {
3806 if (status != UFDB_API_BEING_VERIFIED) /* found in cache, probe is concluded */
3807 return status;
3808
3809 /* found in cache, being verified at this moment */
3810 #ifdef UFDB_HTTPS_CACHE_DEBUG
3811 ufdbLogMessage( "W%03d: UFDBsslPeekServer %s:%d status is BEING_VERIFIED", worker, hostname, portnumber );
3812 #endif
3813
3814 /* UFDBsslPeekServer cannot return UFDB_API_BEING_VERIFIED, so wait for the other thread to terminate its probe
3815 * and retrieve the probe results from the cache.
3816 */
3817
3818 usleep( 35000 + (random() % 19999) );
3819 s = 0;
3820
3821 while (status == UFDB_API_BEING_VERIFIED)
3822 {
3823 status = lookupHTTPScache( hostname, portnumber, 0, &info );
3824 if (status == UFDB_API_BEING_VERIFIED)
3825 {
3826 usleep( 80000 + (random() % 27777) );
3827 s++;
3828 if (s == 10)
3829 {
3830 if (ufdbGV.debug > 1)
3831 ufdbLogMessage( "W%03d: UFDBsslPeekServer: waiting for other thread on status for %s:%d",
3832 worker, hostname, portnumber );
3833 }
3834 }
3835 else
3836 {
3837 if (s >= 10)
3838 {
3839 if (ufdbGV.debug > 1)
3840 ufdbLogMessage( "W%03d: UFDBsslPeekServer: finished waiting %d intervals for other thread on %s:%d status %d",
3841 worker, s, hostname, portnumber, status );
3842 }
3843 return status;
3844 }
3845
3846 if (ufdbGV.reconfig || ufdbGV.terminating) /* this thread must release the readlock asap */
3847 {
3848 if (ufdbGV.debug > 1)
3849 ufdbLogMessage( "W%03d: UFDBsslPeekServer: stopped waiting for other thread peeking %s:%d "
3850 "since reconfig=%d",
3851 worker, hostname, portnumber, ufdbGV.reconfig );
3852 return UFDB_API_BEING_VERIFIED;
3853 }
3854
3855 if (s == 200) /* 200 * 0.08+ sec = 16+ sec. Aaaargh. I am tired of waiting! */
3856 {
3857 if (ufdbGV.debug)
3858 ufdbLogMessage( "W%03d: UFDBsslPeekServer: waited too long for other thread on status for %s:%d",
3859 worker, hostname, portnumber );
3860 return UFDB_API_BEING_VERIFIED;
3861 }
3862 }
3863 }
3864
3865 /************************** THE ACTUAL HTTPS PORT PROBE STARTS HERE **********************************/
3866
3867 #ifdef UFDB_HTTPS_CACHE_DEBUG
3868 if (ufdbGV.debug || (flags & UFDB_API_VERBOSE_OUTPUT))
3869 ufdbLogMessage( "W%03d: UFDBsslPeekServer: %s:%d opening socket...", worker, hostname, portnumber );
3870 #endif
3871
3872 /* TO-DO: UFDBopenSocket may take a long time doing hostname lookups and trying to open a socket on various addresses */
3873 s = UFDBopenSocket( hostname, portnumber );
3874 if (s < 0)
3875 {
3876 ufdbLogMessage( "W%03d: HTTPS protocol verification for %s:%d FAILED for peek-server: "
3877 "cannot open communication socket",
3878 worker, hostname, portnumber );
3879 updateHTTPScache( hostname, portnumber, UFDB_API_ERR_SOCKET, 0, &info );
3880 return UFDB_API_ERR_SOCKET;
3881 }
3882
3883 if (ufdbGV.debug > 1)
3884 ufdbLogMessage( "W%03d: UFDBsslPeekServer: socket to %s:%d is opened successfully. fd=%d",
3885 worker, hostname, portnumber, s );
3886
3887 if (ufdbGV.reconfig != UFDB_RECONFIGR_NONE) /* this thread must release the readlock asap */
3888 {
3889 if (ufdbGV.debug)
3890 ufdbLogMessage( "W%03d: UFDBsslPeekServer: interrupted peeking %s:%d since reconfig=%d",
3891 worker, hostname, portnumber, ufdbGV.reconfig );
3892 close( s );
3893 updateHTTPScache( hostname, portnumber, UFDB_API_ERR_OUTDATED, 0, &info );
3894 return UFDB_API_BEING_VERIFIED;
3895 }
3896
3897 *content = NULL;
3898 status = UFDB_API_OK;
3899
3900 if (status == UFDB_API_OK)
3901 {
3902 /* TODO: check for vtunnel */
3903
3904 tv.tv_sec = 3; /* NOTE: ufdbgclient times out in 21 seconds! but we also have Skype,Gtalk,SSH probes */
3905 tv.tv_usec = 500000;
3906 n = setsockopt( s, SOL_SOCKET, SO_RCVTIMEO, (void *) &tv, sizeof(tv) );
3907 #if 0
3908 if (n < 0)
3909 ufdbLogError( "UFDBsslPeekServer: cannot set socket timeout to %ld seconds: %s",
3910 (long) tv.tv_sec, strerror(errno) );
3911 #else
3912 if (n < 0)
3913 { ; }
3914 #endif
3915 (void) setsockopt( s, SOL_SOCKET, SO_SNDTIMEO, (void *) &tv, sizeof(tv) );
3916
3917 /* Setup a TLS connection to connect to the HTTPS port, do a "GET /",
3918 * and see what the server has to say. An error is returned if
3919 * the other end does not speak SSL nor speaks one of the protocols that we are able to detect.
3920 */
3921
3922 status = UFDB_API_OK;
3923 *content = httpsGETroot( worker, s, hostname, portnumber, certErrors, cn, &status );
3924 if (ufdbGV.debug ||
3925 ufdbGV.debugAim || ufdbGV.debugGtalk || ufdbGV.debugSkype || ufdbGV.debugYahooMsg ||
3926 ufdbGV.debugFBchat || ufdbGV.debugCitrixOnline)
3927 {
3928 ufdbLogMessage( "W%03d: UFDBsslPeekServer: httpsGETroot for %s:%d returned status %s, content is %sNULL, CN is \"%s\"",
3929 worker, hostname, portnumber, ufdbAPIstatusString(status), *content==NULL ? "" : "not ", cn );
3930 }
3931
3932 if (*content == NULL || (*content)[0] == '\0')
3933 {
3934 if (status == UFDB_API_OK)
3935 {
3936 /* We did not read anything from the server... so we cannot draw a conclusion.
3937 * Therefore we return "OK" and hope that the next check gives an answer.
3938 */
3939 ufdbLogMessage( "W%03d: HTTPS server %s:%d did not send any content.",
3940 worker, hostname, portnumber );
3941 close( s );
3942 if (*content != NULL)
3943 ufdbFree( *content );
3944 *content = NULL;
3945 return UFDB_API_OK;
3946 }
3947
3948 if (status == UFDB_API_ERR_NULL)
3949 /* there was some kind of error. Use UFDB_API_ERR_SOCKET instead of UFDB_API_ERR_NULL */
3950 status = UFDB_API_ERR_SOCKET;
3951 }
3952 else
3953 if (status != UFDB_API_OK)
3954 {
3955 /* we have already an error condition or ERR_IS_AIM|GTALK|SKYPE|YAHOOMSG|FBCHAT ; do no more checks */
3956 if (status == UFDB_API_ERR_NULL)
3957 status = UFDB_API_ERR_SOCKET;
3958 }
3959 else
3960 if (strncmp( *content, "HTTP/", 5 ) == 0 ||
3961 strncmp( *content, "<?xml", 5 ) == 0) /* detect known tunnels */
3962 {
3963 if (ufdbGV.debug > 1)
3964 {
3965 int i, j;
3966 char * c;
3967 char debuginfo[2600];
3968 /* TO-DO: put this debug code in a separate function */
3969
3970 c = *content;
3971 for (j = 0, i = 0; c[i] != '\0' && i < 2599; i++)
3972 {
3973 if (c[i] == '\r' || c[i] == '\n')
3974 debuginfo[j++] = '_';
3975 else if (isprint(c[i]))
3976 debuginfo[j++] = c[i];
3977 else
3978 debuginfo[j++] = '.';
3979 }
3980 debuginfo[j] = '\0';
3981 ufdbLogMessage( "W%03d: HTTPS protocol reply for %s:%d:\n %s",
3982 worker, hostname, portnumber, debuginfo );
3983 }
3984
3985 #if 0
3986 if (strstr( *content, "X-Kazaa-" ) != NULL) /* TO-DO */
3987 {
3988 ufdbLogError( "HTTPS protocol on %s:%d uses Kazaa P2P",
3989 hostname, portnumber );
3990 status = UFDB_API_ERR_P2P;
3991 }
3992 #endif
3993 /* TODO: investigate nomachine.com */
3994 if (strstr( *content, "logmein.com/" ) != NULL ||
3995 strstr( *content, "hamachi.cc/" ) != NULL)
3996 {
3997 ufdbLogMessage( "W%03d: HTTPS protocol on %s:%d uses a hamachi/logmein TUNNEL",
3998 worker, hostname, portnumber );
3999 status = UFDB_API_ERR_TUNNEL;
4000 }
4001 else
4002 if (strstr( *content, "Set-Cookie: SSLX_SSESHID=" ) != NULL)
4003 {
4004 ufdbLogMessage( "W%03d: HTTPS protocol on %s:%d uses an SSL-Explorer TUNNEL",
4005 worker, hostname, portnumber );
4006 status = UFDB_API_ERR_TUNNEL;
4007 }
4008 else
4009 if (strstr( *content, "BarracudaServer.com" ) != NULL ||
4010 strstr( *content, "barracudaserver.com" ) != NULL ||
4011 strstr( *content, "BarracudaDrive" ) != NULL)
4012 {
4013 ufdbLogMessage( "W%03d: HTTPS protocol on %s:%d uses a BARRACUDA proxy TUNNEL",
4014 worker, hostname, portnumber );
4015 status = UFDB_API_ERR_TUNNEL;
4016 }
4017 else
4018 if (strstr( *content, " index.vnc -" ) != NULL ||
4019 strstr( *content, " used with Xvnc" ) != NULL ||
4020 strstr( *content, "TightVNC Java viewer applet" ) != NULL)
4021 {
4022 ufdbLogMessage( "W%03d: HTTPS protocol on %s:%d uses a VNC proxy TUNNEL",
4023 worker, hostname, portnumber );
4024 status = UFDB_API_ERR_TUNNEL;
4025 }
4026 else
4027 if (strstr( *content, "X-Webtunnel-Status" ) != NULL ||
4028 strstr( *content, "X-webtunnel-status" ) != NULL ||
4029 strstr( *content, "X-webtunnel-version" ) != NULL ||
4030 strstr( *content, "X-Webtunnel-Version" ) != NULL)
4031 {
4032 ufdbLogMessage( "W%03d: HTTPS protocol on %s:%d uses Webtunnel TUNNEL",
4033 worker, hostname, portnumber );
4034 status = UFDB_API_ERR_TUNNEL;
4035 }
4036 }
4037 else /* server does not speak HTTP */
4038 {
4039 int i,j;
4040 char * c;
4041 char debuginfo[2400];
4042
4043 ufdbLogMessage( "W%03d: HTTPS protocol on %s:%d encapsulates a non-HTTP protocol %s",
4044 worker, hostname, portnumber,
4045 ufdbGV.unknownProtocolOverHttps ? "and an unknown protocol is allowed" :
4046 "and is considered a PROXY TUNNEL because allow-unknown-protocol-over-https is OFF"
4047 );
4048 if (ufdbGV.debug > 1)
4049 {
4050 /* TO-DO: put this code in a separate function */
4051 c = *content;
4052 for (j = 0, i = 0; c[i] != '\0' && i < 2399; i++)
4053 {
4054 if (c[i] == '\r' || c[i] == '\n')
4055 debuginfo[j++] = '_';
4056 else if (isprint(c[i]))
4057 debuginfo[j++] = c[i];
4058 else
4059 debuginfo[j++] = '.';
4060 }
4061 debuginfo[j] = '\0';
4062 ufdbLogMessage( "W%03d: HTTPS protocol reply for %s:%d:\n%s",
4063 worker, hostname, portnumber, debuginfo );
4064 }
4065
4066 status = ufdbGV.unknownProtocolOverHttps ? UFDB_API_ERR_UNKNOWN_PROTOCOL : UFDB_API_ERR_TUNNEL;
4067 }
4068 }
4069
4070 (void) close( s );
4071
4072 if (ufdbGV.debug || ufdbGV.logAllRequests ||
4073 ufdbGV.debugAim || ufdbGV.debugGtalk || ufdbGV.debugSkype || ufdbGV.debugYahooMsg ||
4074 ufdbGV.debugFBchat || ufdbGV.debugCitrixOnline)
4075 {
4076 ufdbLogMessage( "W%03d: HTTPS protocol on %s:%d has been verified and status is %s",
4077 worker, hostname, portnumber, ufdbAPIstatusString(status) );
4078 }
4079
4080 if (ufdbGV.tunnelCheckMethod == UFDB_API_HTTPS_LOG_ONLY)
4081 status = UFDB_API_OK;
4082
4083 if (ufdbGV.debug > 1)
4084 ufdbLogMessage( "W%03d: UFDBsslPeekServer %s:%d status updated to %s",
4085 worker, hostname, portnumber, ufdbAPIstatusString(status) );
4086
4087 updateHTTPScache( hostname, portnumber, status, 0, &info );
4088
4089 return status;
4090 }
4091 #endif
4092
4093
4094 #ifdef __cplusplus
4095 }
4096 #endif
4097