1111 *
1212 *
1313 * IDENTIFICATION
14- * $Header: /cvsroot/pgsql/src/backend/libpq/be-secure.c,v 1.1 2002/06/14 04:23:17 momjian Exp $
15- *
14+ * $Header: /cvsroot/pgsql/src/backend/libpq/be-secure.c,v 1.2 2002/06/14 04:31:49 momjian Exp $
15+ *
16+ * Since the server static private key ($DataDir/server.key)
17+ * will normally be stored unencrypted so that the database
18+ * backend can restart automatically, it is important that
19+ * we select an algorithm that continues to provide confidentiality
20+ * even if the attacker has the server's private key. Empheral
21+ * DH (EDH) keys provide this, and in fact provide Perfect Forward
22+ * Secrecy (PFS) except for situations where the session can
23+ * be hijacked during a periodic handshake/renegotiation.
24+ * Even that backdoor can be closed if client certificates
25+ * are used (since the imposter will be unable to successfully
26+ * complete renegotiation).
27+ *
28+ * N.B., the static private key should still be protected to
29+ * the largest extent possible, to minimize the risk of
30+ * impersonations.
31+ *
32+ * Another benefit of EDH is that it allows the backend and
33+ * clients to use DSA keys. DSA keys can only provide digital
34+ * signatures, not encryption, and are often acceptable in
35+ * jurisdictions where RSA keys are unacceptable.
36+ *
37+ * The downside to EDH is that it makes it impossible to
38+ * use ssldump(1) if there's a problem establishing an SSL
39+ * session. In this case you'll need to temporarily disable
40+ * EDH by commenting out the callback.
41+ *
1642 * PATCH LEVEL
1743 * milestone 1: fix basic coding errors
1844 * [*] existing SSL code pulled out of existing files.
2551 *
2652 * milestone 3: improve confidentially, support perfect forward secrecy
2753 * [ ] use 'random' file, read from '/dev/urandom?'
28- * [ ] emphermal DH keys, default values
54+ * [* ] emphermal DH keys, default values
2955 * [ ] periodic renegotiation
56+ * [ ] private key permissions
3057 *
3158 * milestone 4: provide endpoint authentication (client)
3259 * [ ] server verifies client certificates
74101#ifdef USE_SSL
75102#include <openssl/ssl.h>
76103#include <openssl/e_os.h>
104+ #include <openssl/dh.h>
77105#endif
78106
79107extern void ExitPostmaster (int );
@@ -87,6 +115,9 @@ ssize_t secure_read(Port *, void *ptr, size_t len);
87115ssize_t secure_write (Port * ,const void * ptr ,size_t len );
88116
89117#ifdef USE_SSL
118+ static DH * load_dh_file (int keylength );
119+ static DH * load_dh_buffer (const char * ,size_t );
120+ static DH * tmp_dh_cb (SSL * s ,int is_export ,int keylength );
90121static int initialize_SSL (void );
91122static void destroy_SSL (void );
92123static int open_server_SSL (Port * );
@@ -98,6 +129,70 @@ static const char *SSLerrmessage(void);
98129static SSL_CTX * SSL_context = NULL ;
99130#endif
100131
132+ /* ------------------------------------------------------------ */
133+ /* Hardcoded values */
134+ /* ------------------------------------------------------------ */
135+
136+ /*
137+ *Hardcoded DH parameters, used in empheral DH keying.
138+ *As discussed above, EDH protects the confidentiality of
139+ *sessions even if the static private key is compromised,
140+ *so we are *highly* motivated to ensure that we can use
141+ *EDH even if the DBA... or an attacker... deletes the
142+ *$DataDir/dh*.pem files.
143+ *
144+ *We could refuse SSL connections unless a good DH parameter
145+ *file exists, but some clients may quietly renegotiate an
146+ *unsecured connection without fully informing the user.
147+ *Very uncool.
148+ *
149+ *Alternately, the backend could attempt to load these files
150+ *on startup if SSL is enabled - and refuse to start if any
151+ *do not exist - but this would tend to piss off DBAs.
152+ *
153+ *If you want to create your own hardcoded DH parameters
154+ *for fun and profit, review "Assigned Number for SKIP
155+ *Protocols" (http://www.skip-vpn.org/spec/numbers.html)
156+ *for suggestions.
157+ */
158+ static const char file_dh512 []=
159+ "-----BEGIN DH PARAMETERS-----\n\
160+ MEYCQQD1Kv884bEpQBgRjXyEpwpy1obEAxnIByl6ypUM2Zafq9AKUJsCRtMIPWak\n\
161+ XUGfnHy9iUsiGSa6q6Jew1XpKgVfAgEC\n\
162+ -----END DH PARAMETERS-----\n" ;
163+
164+ static const char file_dh1024 []=
165+ "-----BEGIN DH PARAMETERS-----\n\
166+ MIGHAoGBAPSI/VhOSdvNILSd5JEHNmszbDgNRR0PfIizHHxbLY7288kjwEPwpVsY\n\
167+ jY67VYy4XTjTNP18F1dDox0YbN4zISy1Kv884bEpQBgRjXyEpwpy1obEAxnIByl6\n\
168+ ypUM2Zafq9AKUJsCRtMIPWakXUGfnHy9iUsiGSa6q6Jew1XpL3jHAgEC\n\
169+ -----END DH PARAMETERS-----\n" ;
170+
171+ static const char file_dh2048 []=
172+ "-----BEGIN DH PARAMETERS-----\n\
173+ MIIBCAKCAQEA9kJXtwh/CBdyorrWqULzBej5UxE5T7bxbrlLOCDaAadWoxTpj0BV\n\
174+ 89AHxstDqZSt90xkhkn4DIO9ZekX1KHTUPj1WV/cdlJPPT2N286Z4VeSWc39uK50\n\
175+ T8X8dryDxUcwYc58yWb/Ffm7/ZFexwGq01uejaClcjrUGvC/RgBYK+X0iP1YTknb\n\
176+ zSC0neSRBzZrM2w4DUUdD3yIsxx8Wy2O9vPJI8BD8KVbGI2Ou1WMuF040zT9fBdX\n\
177+ Q6MdGGzeMyEstSr/POGxKUAYEY18hKcKctaGxAMZyAcpesqVDNmWn6vQClCbAkbT\n\
178+ CD1mpF1Bn5x8vYlLIhkmuquiXsNV6TILOwIBAg==\n\
179+ -----END DH PARAMETERS-----\n" ;
180+
181+ static const char file_dh4096 []=
182+ "-----BEGIN DH PARAMETERS-----\n\
183+ MIICCAKCAgEA+hRyUsFN4VpJ1O8JLcCo/VWr19k3BCgJ4uk+d+KhehjdRqNDNyOQ\n\
184+ l/MOyQNQfWXPeGKmOmIig6Ev/nm6Nf9Z2B1h3R4hExf+zTiHnvVPeRBhjdQi81rt\n\
185+ Xeoh6TNrSBIKIHfUJWBh3va0TxxjQIs6IZOLeVNRLMqzeylWqMf49HsIXqbcokUS\n\
186+ Vt1BkvLdW48j8PPv5DsKRN3tloTxqDJGo9tKvj1Fuk74A+Xda1kNhB7KFlqMyN98\n\
187+ VETEJ6c7KpfOo30mnK30wqw3S8OtaIR/maYX72tGOno2ehFDkq3pnPtEbD2CScxc\n\
188+ alJC+EL7RPk5c/tgeTvCngvc1KZn92Y//EI7G9tPZtylj2b56sHtMftIoYJ9+ODM\n\
189+ sccD5Piz/rejE3Ome8EOOceUSCYAhXn8b3qvxVI1ddd1pED6FHRhFvLrZxFvBEM9\n\
190+ ERRMp5QqOaHJkM+Dxv8Cj6MqrCbfC4u+ZErxodzuusgDgvZiLF22uxMZbobFWyte\n\
191+ OvOzKGtwcTqO/1wV5gKkzu1ZVswVUQd5Gg8lJicwqRWyyNRczDDoG9jVDxmogKTH\n\
192+ AaqLulO7R8Ifa1SwF2DteSGVtgWEN8gDpN3RBmmPTDngyF2DHb5qmpnznwtFKdTL\n\
193+ KWbuHn491xNO25CQWMtem80uKw+pTnisBRF/454n1Jnhub144YRBoN8CAQI=\n\
194+ -----END DH PARAMETERS-----\n" ;
195+
101196/* ------------------------------------------------------------ */
102197/* Procedures common to all secure sessions */
103198/* ------------------------------------------------------------ */
@@ -245,6 +340,161 @@ secure_write (Port *port, const void *ptr, size_t len)
245340/* SSL specific code */
246341/* ------------------------------------------------------------ */
247342#ifdef USE_SSL
343+ /*
344+ *Load precomputed DH parameters.
345+ *
346+ *To prevent "downgrade" attacks, we perform a number of checks
347+ *to verify that the DBA-generated DH parameters file contains
348+ *what we expect it to contain.
349+ */
350+ static DH *
351+ load_dh_file (int keylength )
352+ {
353+ FILE * fp ;
354+ char fnbuf [2048 ];
355+ DH * dh = NULL ;
356+ int codes ;
357+
358+ /* attempt to open file. It's not an error if it doesn't exist. */
359+ snprintf (fnbuf ,sizeof fnbuf ,"%s/dh%d.pem" ,DataDir ,keylength );
360+ if ((fp = fopen (fnbuf ,"r" ))== NULL )
361+ return NULL ;
362+
363+ /*flock(fileno(fp), LOCK_SH); */
364+ dh = PEM_read_DHparams (fp ,NULL ,NULL ,NULL );
365+ /*flock(fileno(fp), LOCK_UN); */
366+ fclose (fp );
367+
368+ /* is the prime the correct size? */
369+ if (dh != NULL && 8 * DH_size (dh )< keylength )
370+ {
371+ elog (DEBUG ,"DH errors (%s): %d bits expected, %d bits found" ,
372+ fnbuf ,keylength ,8 * DH_size (dh ));
373+ dh = NULL ;
374+ }
375+
376+ /* make sure the DH parameters are usable */
377+ if (dh != NULL )
378+ {
379+ if (DH_check (dh ,& codes ))
380+ {
381+ elog (DEBUG ,"DH_check error (%s): %s" ,fnbuf ,SSLerrmessage ());
382+ return NULL ;
383+ }
384+ if (codes & DH_CHECK_P_NOT_PRIME )
385+ {
386+ elog (DEBUG ,"DH error (%s): p is not prime" ,fnbuf );
387+ return NULL ;
388+ }
389+ if ((codes & DH_NOT_SUITABLE_GENERATOR )&&
390+ (codes & DH_CHECK_P_NOT_SAFE_PRIME ))
391+ {
392+ elog (DEBUG ,
393+ "DH error (%s): neither suitable generator or safe prime" ,
394+ fnbuf );
395+ return NULL ;
396+ }
397+ }
398+
399+ return dh ;
400+ }
401+
402+ /*
403+ *Load hardcoded DH parameters.
404+ *
405+ *To prevent problems if the DH parameters files don't even
406+ *exist, we can load DH parameters hardcoded into this file.
407+ */
408+ static DH *
409+ load_dh_buffer (const char * buffer ,size_t len )
410+ {
411+ BIO * bio ;
412+ DH * dh = NULL ;
413+
414+ bio = BIO_new_mem_buf ((char * )buffer ,len );
415+ if (bio == NULL )
416+ return NULL ;
417+ dh = PEM_read_bio_DHparams (bio ,NULL ,NULL ,NULL );
418+ if (dh == NULL )
419+ elog (DEBUG ,"DH load buffer: %s" ,SSLerrmessage ());
420+ BIO_free (bio );
421+
422+ return dh ;
423+ }
424+
425+ /*
426+ *Generate an empheral DH key. Because this can take a long
427+ *time to compute, we can use precomputed parameters of the
428+ *common key sizes.
429+ *
430+ *Since few sites will bother to precompute these parameter
431+ *files, we also provide a fallback to the parameters provided
432+ *by the OpenSSL project.
433+ *
434+ *These values can be static (once loaded or computed) since
435+ *the OpenSSL library can efficiently generate random keys from
436+ *the information provided.
437+ */
438+ static DH *
439+ tmp_dh_cb (SSL * s ,int is_export ,int keylength )
440+ {
441+ DH * r = NULL ;
442+ static DH * dh = NULL ;
443+ static DH * dh512 = NULL ;
444+ static DH * dh1024 = NULL ;
445+ static DH * dh2048 = NULL ;
446+ static DH * dh4096 = NULL ;
447+
448+ switch (keylength )
449+ {
450+ case 512 :
451+ if (dh512 == NULL )
452+ dh512 = load_dh_file (keylength );
453+ if (dh512 == NULL )
454+ dh512 = load_dh_buffer (file_dh512 ,sizeof file_dh512 );
455+ r = dh512 ;
456+ break ;
457+
458+ case 1024 :
459+ if (dh1024 == NULL )
460+ dh1024 = load_dh_file (keylength );
461+ if (dh1024 == NULL )
462+ dh1024 = load_dh_buffer (file_dh1024 ,sizeof file_dh1024 );
463+ r = dh1024 ;
464+ break ;
465+
466+ case 2048 :
467+ if (dh2048 == NULL )
468+ dh2048 = load_dh_file (keylength );
469+ if (dh2048 == NULL )
470+ dh2048 = load_dh_buffer (file_dh2048 ,sizeof file_dh2048 );
471+ r = dh2048 ;
472+ break ;
473+
474+ case 4096 :
475+ if (dh4096 == NULL )
476+ dh4096 = load_dh_file (keylength );
477+ if (dh4096 == NULL )
478+ dh4096 = load_dh_buffer (file_dh4096 ,sizeof file_dh4096 );
479+ r = dh4096 ;
480+ break ;
481+
482+ default :
483+ if (dh == NULL )
484+ dh = load_dh_file (keylength );
485+ r = dh ;
486+ }
487+
488+ /* this may take a long time, but it may be necessary... */
489+ if (r == NULL || 8 * DH_size (r )< keylength )
490+ {
491+ elog (DEBUG ,"DH: generating parameters (%d bits)...." ,keylength );
492+ r = DH_generate_parameters (keylength ,DH_GENERATOR_2 ,NULL ,NULL );
493+ }
494+
495+ return r ;
496+ }
497+
248498/*
249499 *Initialize global SSL context.
250500 */
@@ -290,6 +540,10 @@ initialize_SSL (void)
290540}
291541}
292542
543+ /* set up empheral DH keys */
544+ SSL_CTX_set_tmp_dh_callback (SSL_context ,tmp_dh_cb );
545+ SSL_CTX_set_options (SSL_context ,SSL_OP_SINGLE_DH_USE );
546+
293547return 0 ;
294548}
295549