Branch data Line data Source code
1 : : // $Id: SSLInterpreter.cc 5988 2008-07-19 07:02:12Z vern $
2 : :
3 : : #include "SSLInterpreter.h"
4 : : #include "SSLv2.h"
5 : :
6 : : #include "X509.h"
7 : :
8 : : #include <sys/socket.h>
9 : : #include <netinet/in.h>
10 : : #include <arpa/inet.h>
11 : :
12 [ # # ][ - + ]: 6 : declare(PDict, CertStore);
13 : 6 : PDict(CertStore) cert_states;
14 : :
15 : : // --- Initalization of static variables --------------------------------------
16 : :
17 : : uint32 SSL_Interpreter::analyzedCertificates = 0;
18 : : uint32 SSL_Interpreter::verifiedCertificates = 0;
19 : : uint32 SSL_Interpreter::failedCertificates = 0;
20 : : uint32 SSL_Interpreter::certificateChains = 0;
21 : :
22 : : // --- SSL_Interpreter --------------------------------------------------------
23 : :
24 : : /*!
25 : : * The constructor.
26 : : *
27 : : * \param proxy Pointer to the SSLProxy_Analyzer which created this instance.
28 : : */
29 : 0 : SSL_Interpreter::SSL_Interpreter(SSLProxy_Analyzer* proxy)
30 : : {
31 : 0 : this->proxy = proxy;
32 : 0 : }
33 : :
34 : : /*!
35 : : * The destructor.
36 : : */
37 : 0 : SSL_Interpreter::~SSL_Interpreter()
38 : : {
39 [ # # ][ # # ]: 0 : delete orig;
[ # # ]
40 [ # # ][ # # ]: 0 : delete resp;
[ # # ]
41 [ # # ][ # # ]: 0 : }
[ # # ]
42 : :
43 : : /*!
44 : : * Analogous to TCP_Connection::Init(), this method calls
45 : : * BuildInterpreterEndpoints() to create the corresponding endpoints.
46 : : */
47 : 0 : void SSL_Interpreter::Init()
48 : : {
49 : 0 : BuildInterpreterEndpoints();
50 : 0 : orig->SetPeer(resp);
51 : 0 : resp->SetPeer(orig);
52 : 0 : }
53 : :
54 : : /*!
55 : : * This method analyzes a given certificate (chain), using the OpenSSL library.
56 : : *
57 : : * \param s Pointer to the SSL_InterpreterEndpoint which received the
58 : : * cerificate (chain).
59 : : * \param data Pointer to the data block which contains the certificate (chain).
60 : : * \param length Size of the data block.
61 : : * \param type the certificate type
62 : : * \param isChain false if data is pointing to a single certificate,
63 : : * true if data is pointing to a certificate chain
64 : : * mod by scott in:
65 : : * uint32 ip_address = *(s->proxyEndpoint->Endpoint()->dst_addr);
66 : : * uint16 port = (uint16) s->proxyEndpoint->Endpoint()->conn->RespPort();
67 : : * inserting endpoint
68 : : */
69 : : void SSL_Interpreter::analyzeCertificate(SSL_InterpreterEndpoint* s,
70 : 0 : const u_char* data, int length, uint8 type, bool isChain)
71 : : {
72 : : // See if we should continue with this certificate.
73 [ # # ]: 0 : if ( ssl_certificate_seen )
74 : : {
75 : 0 : val_list* vl = new val_list;
76 : 0 : vl->append(proxy->BuildConnVal());
77 : 0 : vl->append(new Val(! s->IsOrig(), TYPE_BOOL));
78 : 0 : proxy->ConnectionEvent(ssl_certificate_seen, vl);
79 : : }
80 : :
81 : 0 : ++analyzedCertificates;
82 : :
83 : 0 : const u_char* pCert = data;
84 : 0 : uint32 certLength = length;
85 : 0 : uint certCount = 0;
86 : :
87 [ # # ]: 0 : if ( isChain )
88 : : {
89 : 0 : ++certificateChains;
90 : :
91 : : // Sum of all cert sizes has to match certListLength.
92 : 0 : int tempLength = 0;
93 [ # # ]: 0 : while ( tempLength < length )
94 : : {
95 : 0 : ++certCount;
96 : : uint32 certLength =
97 : : uint32((data[tempLength + 0] << 16) |
98 : : data[tempLength + 1] << 8) |
99 : 0 : data[tempLength + 2];
100 : :
101 : 0 : tempLength += certLength + 3;
102 : : }
103 : :
104 [ # # ]: 0 : if ( tempLength > length )
105 : : {
106 : 0 : Weird( "SSLv3x: sum of size of certificates doesn't match size of certificate chain" );
107 : 0 : return;
108 : : }
109 : :
110 : : // Get the first certificate.
111 : 0 : pCert = data + 3;
112 : 0 : certLength = uint32((data[0] << 16) | data[1] << 8) | data[2];
113 : : }
114 : :
115 : : // Create a hashsum of the current certificate.
116 : 0 : hash_t hashsum = HashKey::HashBytes(pCert, certLength);
117 : :
118 [ # # ]: 0 : if ( ! proxy->TCP() )
119 : 0 : return;
120 : :
121 [ # # ]: 0 : TCP_Endpoint* endp = s->IsOrig() ? proxy->TCP()->Orig() : proxy->TCP()->Resp();
122 : :
123 : : // Check if we've seen a certificate from this addr/port before.
124 : : uint8 key[6];
125 : : // ### Won't work for IPv6.
126 : 0 : uint32 ip_address = *(endp->dst_addr);
127 : 0 : uint16 port = uint16(proxy->Conn()->RespPort());
128 : 0 : memcpy(key, &ip_address, 4);
129 : 0 : memcpy(&key[4], &port, 2);
130 : :
131 : 0 : HashKey h(key, sizeof(key));
132 : 0 : CertStore* pCertState = 0;
133 : 0 : pCertState = (CertStore*) cert_states.Lookup(&h);
134 [ # # ]: 0 : if ( ! pCertState )
135 : : { // new address
136 : 0 : pCertState = new CertStore(ip_address, port, hashsum, certLength);
137 : 0 : cert_states.Insert(&h, pCertState);
138 : : }
139 : : else
140 : : {
141 : : // We've seen this address/certificate before. Check if
142 : : // certificate changed.
143 [ # # ]: 0 : if ( ! pCertState->isSameCert(hashsum, certLength) )
144 : : {
145 : : // This shouldn't happen; ### make a stronger error.
146 : 0 : Weird("SSL: Certificate changed for this ip+port !!!");
147 : :
148 : : // Update status.
149 : 0 : ++pCertState->changes;
150 : 0 : pCertState->certHash = hashsum;
151 : 0 : pCertState->certSize = certLength;
152 : 0 : pCertState->isValid = -1;
153 : : }
154 : : else
155 : : { // cert didn't change
156 : 0 : if ( pCertState->isValid == 0 )
157 : : {
158 : : // This is an invalid cert, but we
159 : : // warned before.
160 : : }
161 : :
162 : : // Save time - don't analyze it any further.
163 : 0 : return;
164 : : }
165 : : }
166 : :
167 : : // Certificate verification.
168 [ # # ]: 0 : if ( ssl_verify_certificates != 0 )
169 : : {
170 : 0 : ++verifiedCertificates;
171 : 0 : int invalid = 0;
172 [ # # ]: 0 : switch ( type ) {
173 : : case SSLv2_CT_X509_CERTIFICATE:
174 [ # # ]: 0 : if ( ! isChain )
175 : : invalid = X509_Cert::verify(s->GetProxyEndpoint(),
176 : 0 : pCert, certLength);
177 : : else
178 : : invalid = X509_Cert::verifyChain(s->GetProxyEndpoint(),
179 : 0 : data, length);
180 : 0 : break;
181 : :
182 : : default:
183 : 0 : Weird("SSL: Unknown CERTIFICATE-TYPE!");
184 : 0 : invalid = 1; // quick 'n dirty :)
185 : : break;
186 : : }
187 : :
188 [ # # ]: 0 : if ( invalid )
189 : : {
190 : 0 : proxy->Weak("SSL: Certificate check FAILED!");
191 : 0 : pCertState->isValid = 0;
192 : 0 : ++failedCertificates;
193 : : }
194 : : else
195 : 0 : pCertState->isValid = 1;
196 : : }
197 : :
198 : : // Store the certificate.
199 [ # # ]: 0 : if ( ssl_store_certificates != 0 )
200 : : {
201 : : // Let's hope the address is currently in network byte order!
202 : : in_addr addr;
203 : 0 : addr.s_addr = ip_address;
204 : 0 : char* pDummy = inet_ntoa(addr);
205 : : char sFileName[PATH_MAX];
206 : :
207 [ # # # # ]: 0 : if ( ssl_store_cert_path &&
[ # # ]
208 : : ssl_store_cert_path->AsString()->Len() > 0 )
209 : : {
210 : 0 : const BroString* pString = ssl_store_cert_path->AsString();
211 : : safe_snprintf(sFileName, PATH_MAX, "%s/cert.%s-server-c%i.der",
212 : 0 : pString->Bytes(), pDummy, pCertState->changes);
213 : : }
214 : : else
215 : : safe_snprintf(sFileName, PATH_MAX, "cert.%s-server-c%i.der",
216 : 0 : pDummy, pCertState->changes);
217 : :
218 : 0 : FILE* certFile = fopen(sFileName, "wb");
219 [ # # ]: 0 : if ( ! certFile )
220 : : {
221 : 0 : Weird(fmt("SSL_Interpreter::analyzeCertificate(): Error opening '%s'!\n", sFileName));
222 : : return;
223 : : }
224 : :
225 : 0 : fwrite(pCert, 1, certLength, certFile);
226 : 0 : fclose(certFile);
227 [ # # ]: 0 : }
228 : :
229 : : // TODO: test if cert is valid for the address we got it from.
230 : : }
231 : :
232 : :
233 : : /*!
234 : : * \return the originating SSL_InterpreterEndpoint
235 : : */
236 : 0 : SSL_InterpreterEndpoint* SSL_Interpreter::Orig() const
237 : : {
238 : 0 : return orig;
239 : : }
240 : :
241 : : /*!
242 : : * \return the responding SSL_InterpreterEndpoint
243 : : */
244 : 0 : SSL_InterpreterEndpoint* SSL_Interpreter::Resp() const
245 : : {
246 : 0 : return resp;
247 : : }
248 : :
249 : : /*!
250 : : * \param p Pointer to an SSL_InterpreterEndpoint to test
251 : : *
252 : : * \return true if p is the originating SSL_InterpreterEndpoint,
253 : : * false otherwise
254 : : */
255 : 0 : int SSL_Interpreter::Is_Orig(SSL_InterpreterEndpoint* p) const
256 : : {
257 : 0 : return p == orig;
258 : : }
259 : :
260 : : /*!
261 : : * \return the responding SSL_InterpreterEndpoint
262 : : */
263 : 0 : SSLProxy_Analyzer* SSL_Interpreter::Proxy() const
264 : : {
265 : 0 : return proxy;
266 : : }
267 : :
268 : : /*!
269 : : * This methods prints a string into the "weird" log file.
270 : : *
271 : : * \param name String to log into the "weird" file.
272 : : */
273 : 0 : void SSL_Interpreter::Weird(const char* name) const
274 : : {
275 : 0 : proxy->Weird(name);
276 : 0 : }
277 : :
278 : : /*!
279 : : * Prints some counters.
280 : : */
281 : 0 : void SSL_Interpreter::printStats()
282 : : {
283 : 0 : printf("SSL_Interpreter:\n");
284 : 0 : printf("analyzedCertificates = %u\n", analyzedCertificates);
285 : 0 : printf("verifiedCertificates = %u\n", verifiedCertificates);
286 : 0 : printf("failedCertificates = %u\n", failedCertificates);
287 : 0 : printf("certificateChains = %u\n", certificateChains);
288 : 0 : }
289 : :
290 : : /*!
291 : : * Wrapper function for the event ssl_conn_attempt.
292 : : *
293 : : * \param sslVersion the SSL version for which the event occured
294 : : *
295 : : * \see SSLProxy_Analyzer::SSL_Versions
296 : : */
297 : : void SSL_Interpreter::fire_ssl_conn_attempt(uint16 sslVersion,
298 : 0 : TableVal* currentCipherSuites)
299 : : {
300 : 0 : EventHandlerPtr event = ssl_conn_attempt;
301 [ # # ]: 0 : if ( event )
302 : : {
303 : 0 : val_list* vl = new val_list;
304 : 0 : vl->append(proxy->BuildConnVal());
305 : 0 : vl->append(new Val(sslVersion, TYPE_INT));
306 : 0 : vl->append(currentCipherSuites);
307 : :
308 : 0 : proxy->ConnectionEvent(event, vl);
309 : : }
310 : 0 : }
311 : :
312 : : /*!
313 : : * Wrapper function for the event ssl_conn_server_reply.
314 : : *
315 : : * \param sslVersion the SSL version for which the event occured
316 : : *
317 : : * \see SSLProxy_Analyzer::SSL_Versions
318 : : */
319 : : void SSL_Interpreter::fire_ssl_conn_server_reply(uint16 sslVersion,
320 : 0 : TableVal* currentCipherSuites)
321 : : {
322 : 0 : EventHandlerPtr event = ssl_conn_server_reply;
323 [ # # ]: 0 : if ( event )
324 : : {
325 : 0 : val_list* vl = new val_list;
326 : 0 : vl->append(proxy->BuildConnVal());
327 : 0 : vl->append(new Val(sslVersion, TYPE_INT));
328 : 0 : vl->append(currentCipherSuites);
329 : :
330 : 0 : proxy->ConnectionEvent(event, vl);
331 : : }
332 : 0 : }
333 : :
334 : : /*!
335 : : * Wrapper function for the event ssl_conn_established.
336 : : *
337 : : * \param sslVersion the SSL version for which the event occured
338 : : * \param cipherSuite constant indicating the used SSL cipher suite
339 : : *
340 : : * \see SSLProxy_Analyzer::SSL_Versions, SSLv2_CipherSpecs and SSL3_1_CipherSpec.
341 : : */
342 : : void SSL_Interpreter::fire_ssl_conn_established(uint16 sslVersion,
343 : 0 : uint32 cipherSuite)
344 : : {
345 : 0 : EventHandlerPtr event = ssl_conn_established;
346 [ # # ]: 0 : if ( event )
347 : : {
348 : 0 : val_list* vl = new val_list;
349 : 0 : vl->append(proxy->BuildConnVal());
350 : 0 : vl->append(new Val(sslVersion, TYPE_INT));
351 : 0 : vl->append(new Val(cipherSuite, TYPE_COUNT));
352 : :
353 : 0 : proxy->ConnectionEvent(event, vl);
354 : : }
355 : :
356 : 0 : }
357 : :
358 : : /*!
359 : : * Wrapper function for the event ssl_conn_reused
360 : : *
361 : : * \param pData Pointer to a SSL_DataBlock which contains the SSL session ID
362 : : * of the originating ssl session.
363 : : */
364 : 0 : void SSL_Interpreter::fire_ssl_conn_reused(const SSL_DataBlock* pData)
365 : : {
366 : 0 : EventHandlerPtr event = ssl_conn_reused;
367 [ # # ]: 0 : if ( event )
368 : : {
369 : 0 : val_list* vl = new val_list;
370 : 0 : vl->append(proxy->BuildConnVal());
371 : 0 : vl->append(MakeSessionID(pData->data, pData->len));
372 : 0 : proxy->ConnectionEvent(event, vl);
373 : : }
374 : 0 : }
375 : :
376 : : /*!
377 : : * Wrapper function for the event ssl_conn_alert
378 : : *
379 : : * \param sslVersion the SSL version for which the event occured
380 : : * \param level constant indicating the level of severity
381 : : * \param description constant indicating the type of alert/error
382 : : *
383 : : * \see SSLProxy_Analyzer::SSL_Versions, SSL3x_AlertLevel, SSL3_1_AlertDescription
384 : : * and SSLv2_ErrorCodes.
385 : : */
386 : : void SSL_Interpreter::fire_ssl_conn_alert(uint16 sslVersion, uint16 level,
387 : 0 : uint16 description)
388 : : {
389 [ # # ]: 0 : if ( ssl_conn_alert )
390 : : {
391 : 0 : EventHandlerPtr event = ssl_conn_alert;
392 [ # # ]: 0 : if ( event )
393 : : {
394 : 0 : val_list* vl = new val_list;
395 : 0 : vl->append(proxy->BuildConnVal());
396 : 0 : vl->append(new Val(sslVersion, TYPE_INT));
397 : 0 : vl->append(new Val(level, TYPE_COUNT));
398 : 0 : vl->append(new Val(description, TYPE_COUNT));
399 : :
400 : 0 : proxy->ConnectionEvent(event, vl);
401 : : }
402 : : }
403 : 0 : }
404 : :
405 : : // Generate a session ID table. Returns an empty table
406 : : // if len is zero.
407 : 0 : TableVal* SSL_Interpreter::MakeSessionID(const u_char* data, int len)
408 : : {
409 : 0 : TableVal* sessionIDTable = new TableVal(SSL_sessionID);
410 : :
411 [ # # ]: 0 : if ( ! len )
412 : 0 : return sessionIDTable;
413 : :
414 [ # # ]: 0 : for ( int i = 0; i < len; i += 4 )
415 : : {
416 : : uint32 temp = (data[i] << 24) | (data[i + 1] << 16) |
417 : 0 : (data[i + 2] << 8) | data[i + 3];
418 : :
419 : 0 : Val* index = new Val(i / 4, TYPE_COUNT);
420 : :
421 : 0 : sessionIDTable->Assign(index, new Val(temp, TYPE_COUNT));
422 : :
423 : 0 : Unref(index);
424 : : }
425 : :
426 : 0 : return sessionIDTable;
427 : : }
428 : :
429 : :
430 : : //--- SSL_InterpreterEndpoint -------------------------------------------------
431 : :
432 : : /*!
433 : : * The constructor.
434 : : *
435 : : * \param interpreter Pointer to the instance of an SSL_Interpreter to which
436 : : * this endpoint belongs to.
437 : : * \param is_orig true if this endpoint is the originator of the connection,
438 : : * false otherwise
439 : : * SC: an adjustment was made here since the endpoints are now assosciated with
440 : : * TCP_Contents base objects rather than TCP_Endpoint.
441 : : */
442 : : SSL_InterpreterEndpoint::SSL_InterpreterEndpoint(SSL_Interpreter* arg_interpreter,
443 : 0 : bool arg_is_orig )
444 : : {
445 : 0 : interpreter = arg_interpreter;
446 : 0 : is_orig = arg_is_orig;
447 : 0 : proxyEndpoint = new Contents_SSL(interpreter->Proxy()->Conn(), is_orig);
448 : 0 : ourProxyEndpoint = true;
449 : 0 : }
450 : :
451 : : /*!
452 : : * The destructor.
453 : : */
454 : 0 : SSL_InterpreterEndpoint::~SSL_InterpreterEndpoint()
455 : : {
456 : 0 : SetProxyEndpoint(0);
457 [ # # ][ # # ]: 0 : }
[ # # ]
458 : :
459 : : /*!
460 : : * \return true if there's currently data pending for this endpoint,
461 : : * false otherwise
462 : : */
463 : 0 : bool SSL_InterpreterEndpoint::isDataPending()
464 : : {
465 : 0 : return proxyEndpoint->isDataPending();
466 : : }
467 : :
468 : : /*!
469 : : * Sets the peer of this endpoint.
470 : : *
471 : : * \param p Pointer to an interpreter endpoint which will be set as the peer
472 : : * of this endpoint.
473 : : */
474 : 0 : void SSL_InterpreterEndpoint::SetPeer(SSL_InterpreterEndpoint* p)
475 : : {
476 : 0 : peer = p;
477 : 0 : }
478 : :
479 : : /*!
480 : : * Sets the proxy endpoint of this endpoint.
481 : : *
482 : : * \param p Pointer to a Contents_SSL analyzer which will be set as the proxy endpoint
483 : : * of this endpoint.
484 : : */
485 : 0 : void SSL_InterpreterEndpoint::SetProxyEndpoint(Contents_SSL* p)
486 : : {
487 [ # # ]: 0 : if ( ourProxyEndpoint )
488 : : {
489 : 0 : proxyEndpoint->Done();
490 [ # # ]: 0 : delete proxyEndpoint;
491 : 0 : ourProxyEndpoint = false;
492 : : }
493 : :
494 : 0 : proxyEndpoint = p;
495 : 0 : }
496 : :
497 : : /*!
498 : : * \return is_orig true if this endpoint is the originator of the connection,
499 : : * false otherwise
500 : : */
501 : 0 : int SSL_InterpreterEndpoint::IsOrig() const
502 : : {
503 : 0 : return is_orig;
504 : : }
505 : :
506 : : /*!
507 : : * \return the peer of this endpoint
508 : : */
509 : 0 : SSL_InterpreterEndpoint* SSL_InterpreterEndpoint::Peer() const
510 : : {
511 : 0 : return peer;
512 : : }
513 : :
514 : : /*!
515 : : * \return the interpreter of this endpoint
516 : : */
517 : 0 : SSL_Interpreter* SSL_InterpreterEndpoint::Interpreter() const
518 : : {
519 : 0 : return interpreter;
520 : : }
521 : :
522 : : // --- CertStore --------------------------------------------------------------
523 : :
524 : : /*
525 : : * The constructor.
526 : : *
527 : : * \param ip ip adress where this certificate came from
528 : : * \param port port number where this certificate came from
529 : : * \param hash hahssum for this certificate
530 : : * \param size of this certificate in bytes
531 : : */
532 : 0 : CertStore::CertStore(uint32 ip, uint32 arg_port, hash_t hash, int size)
533 : : {
534 : 0 : ip_addr = ip;
535 : 0 : certHash = hash;
536 : 0 : certSize = size;
537 : 0 : isValid = -1;
538 : 0 : changes = 0;
539 : 0 : port = arg_port;
540 : 0 : }
541 : :
542 : : /*
543 : : * This method can be used to compare certificates by certain criterias.
544 : : *
545 : : * \param hash hashsum of the certificate to compare
546 : : * \param size size of the certificate to compare
547 : : *
548 : : * \return true if the criterias match, false otherwise
549 : : */
550 : 0 : bool CertStore::isSameCert(hash_t hash, int length)
551 : : {
552 [ # # ][ # # ]: 0 : return hash == certHash && length == certSize;
553 [ + - ][ + - ]: 6 : }
|