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