@@ -102,6 +102,9 @@ char *ssl_crl_file;
102
102
int ssl_renegotiation_limit ;
103
103
104
104
#ifdef USE_SSL
105
+ /* are we in the middle of a renegotiation? */
106
+ static bool in_ssl_renegotiation = false;
107
+
105
108
static SSL_CTX * SSL_context = NULL ;
106
109
static bool ssl_loaded_verify_locations = false;
107
110
#endif
@@ -295,6 +298,7 @@ secure_read(Port *port, void *ptr, size_t len)
295
298
(errcode (ERRCODE_PROTOCOL_VIOLATION ),
296
299
errmsg ("unrecognized SSL error code: %d" ,
297
300
err )));
301
+ errno = ECONNRESET ;
298
302
n = -1 ;
299
303
break ;
300
304
}
@@ -326,29 +330,55 @@ secure_write(Port *port, void *ptr, size_t len)
326
330
int err ;
327
331
unsigned long ecode ;
328
332
329
- if (ssl_renegotiation_limit && port -> count > ssl_renegotiation_limit * 1024L )
333
+ /*
334
+ * If SSL renegotiations are enabled and we're getting close to the
335
+ * limit, start one now; but avoid it if there's one already in
336
+ * progress. Request the renegotiation 1kB before the limit has
337
+ * actually expired.
338
+ */
339
+ if (ssl_renegotiation_limit && !in_ssl_renegotiation &&
340
+ port -> count > (ssl_renegotiation_limit - 1 ) * 1024L )
330
341
{
342
+ in_ssl_renegotiation = true;
343
+
344
+ /*
345
+ * The way we determine that a renegotiation has completed is by
346
+ * observing OpenSSL's internal renegotiation counter. Make sure
347
+ * we start out at zero, and assume that the renegotiation is
348
+ * complete when the counter advances.
349
+ *
350
+ * OpenSSL provides SSL_renegotiation_pending(), but this doesn't
351
+ * seem to work in testing.
352
+ */
353
+ SSL_clear_num_renegotiations (port -> ssl );
354
+
331
355
SSL_set_session_id_context (port -> ssl , (void * ) & SSL_context ,
332
356
sizeof (SSL_context ));
333
357
if (SSL_renegotiate (port -> ssl ) <= 0 )
334
358
ereport (COMMERROR ,
335
359
(errcode (ERRCODE_PROTOCOL_VIOLATION ),
336
- errmsg ("SSL renegotiation failure" )));
337
- if (SSL_do_handshake (port -> ssl ) <= 0 )
338
- ereport (COMMERROR ,
339
- (errcode (ERRCODE_PROTOCOL_VIOLATION ),
340
- errmsg ("SSL renegotiation failure" )));
341
- if (port -> ssl -> state != SSL_ST_OK )
342
- ereport (COMMERROR ,
343
- (errcode (ERRCODE_PROTOCOL_VIOLATION ),
344
- errmsg ("SSL failed to send renegotiation request" )));
345
- port -> ssl -> state |= SSL_ST_ACCEPT ;
346
- SSL_do_handshake (port -> ssl );
347
- if (port -> ssl -> state != SSL_ST_OK )
348
- ereport (COMMERROR ,
349
- (errcode (ERRCODE_PROTOCOL_VIOLATION ),
350
- errmsg ("SSL renegotiation failure" )));
351
- port -> count = 0 ;
360
+ errmsg ("SSL failure during renegotiation start" )));
361
+ else
362
+ {
363
+ int retries ;
364
+
365
+ /*
366
+ * A handshake can fail, so be prepared to retry it, but only
367
+ * a few times.
368
+ */
369
+ for (retries = 0 ;; retries ++ )
370
+ {
371
+ if (SSL_do_handshake (port -> ssl ) > 0 )
372
+ break ; /* done */
373
+ ereport (COMMERROR ,
374
+ (errcode (ERRCODE_PROTOCOL_VIOLATION ),
375
+ errmsg ("SSL handshake failure on renegotiation, retrying" )));
376
+ if (retries >= 20 )
377
+ ereport (FATAL ,
378
+ (errcode (ERRCODE_PROTOCOL_VIOLATION ),
379
+ errmsg ("could not complete SSL handshake on renegotiation, too many failures" )));
380
+ }
381
+ }
352
382
}
353
383
354
384
wloop :
@@ -393,9 +423,32 @@ secure_write(Port *port, void *ptr, size_t len)
393
423
(errcode (ERRCODE_PROTOCOL_VIOLATION ),
394
424
errmsg ("unrecognized SSL error code: %d" ,
395
425
err )));
426
+ errno = ECONNRESET ;
396
427
n = -1 ;
397
428
break ;
398
429
}
430
+
431
+ if (n >= 0 )
432
+ {
433
+ /* is renegotiation complete? */
434
+ if (in_ssl_renegotiation &&
435
+ SSL_num_renegotiations (port -> ssl ) >= 1 )
436
+ {
437
+ in_ssl_renegotiation = false;
438
+ port -> count = 0 ;
439
+ }
440
+
441
+ /*
442
+ * if renegotiation is still ongoing, and we've gone beyond the
443
+ * limit, kill the connection now -- continuing to use it can be
444
+ * considered a security problem.
445
+ */
446
+ if (in_ssl_renegotiation &&
447
+ port -> count > ssl_renegotiation_limit * 1024L )
448
+ ereport (FATAL ,
449
+ (errcode (ERRCODE_PROTOCOL_VIOLATION ),
450
+ errmsg ("SSL failed to renegotiate connection before limit expired" )));
451
+ }
399
452
}
400
453
else
401
454
#endif
0 commit comments