11
11
*
12
12
*
13
13
* IDENTIFICATION
14
- * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-secure.c,v 1.2 2002/06/14 04:31:49 momjian Exp $
14
+ * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-secure.c,v 1.3 2002/06/14 04:36:58 momjian Exp $
15
15
*
16
16
* NOTES
17
17
* The client *requires* a valid server certificate. Since
52
52
* should normally be stored encrypted. However we still
53
53
* support EPH since it's useful for other reasons.
54
54
*
55
+ * ...
56
+ *
57
+ * Client certificates are supported, if the server requests
58
+ * or requires them. Client certificates can be used for
59
+ * authentication, to prevent sessions from being hijacked,
60
+ * or to allow "road warriors" to access the database while
61
+ * keeping it closed to everyone else.
62
+ *
63
+ * The user's certificate and private key are located in
64
+ * $HOME/.postgresql/postgresql.crt
65
+ * and
66
+ * $HOME/.postgresql/postgresql.key
67
+ * respectively.
68
+ *
55
69
* OS DEPENDENCIES
56
70
* The code currently assumes a POSIX password entry. How should
57
71
* Windows and Mac users be handled?
71
85
* [*] emphermal DH keys, default values
72
86
*
73
87
* milestone 4: provide endpoint authentication (client)
74
- * [ ] server verifies client certificates
88
+ * [* ] server verifies client certificates
75
89
*
76
90
* milestone 5: provide informational callbacks
77
91
* [ ] provide informational callbacks
@@ -135,6 +149,7 @@ static int verify_peer(PGconn *);
135
149
static DH * load_dh_file (int keylength );
136
150
static DH * load_dh_buffer (const char * , size_t );
137
151
static DH * tmp_dh_cb (SSL * s , int is_export , int keylength );
152
+ static int client_cert_cb (SSL * , X509 * * , EVP_PKEY * * );
138
153
static int initialize_SSL (PGconn * );
139
154
static void destroy_SSL (void );
140
155
static int open_client_SSL (PGconn * );
@@ -614,6 +629,101 @@ tmp_dh_cb (SSL *s, int is_export, int keylength)
614
629
return r ;
615
630
}
616
631
632
+ /*
633
+ * Callback used by SSL to load client cert and key.
634
+ * This callback is only called when the server wants a
635
+ * client cert.
636
+ *
637
+ * Returns 1 on success, 0 on no data, -1 on error.
638
+ */
639
+ static int
640
+ client_cert_cb (SSL * ssl , X509 * * x509 , EVP_PKEY * * pkey )
641
+ {
642
+ struct passwd * pwd ;
643
+ struct stat buf , buf2 ;
644
+ char fnbuf [2048 ];
645
+ FILE * fp ;
646
+ PGconn * conn = (PGconn * ) SSL_get_app_data (ssl );
647
+ int (* cb )() = NULL ; /* how to read user password */
648
+
649
+ if ((pwd = getpwuid (getuid ())) == NULL )
650
+ {
651
+ printfPQExpBuffer (& conn -> errorMessage ,
652
+ libpq_gettext ("unable to get user information\n" ));
653
+ return -1 ;
654
+ }
655
+
656
+ /* read the user certificate */
657
+ snprintf (fnbuf , sizeof fnbuf , "%s/.postgresql/postgresql.crt" ,
658
+ pwd -> pw_dir );
659
+ if (stat (fnbuf , & buf ) == -1 )
660
+ return 0 ;
661
+ if ((fp = fopen (fnbuf , "r" )) == NULL )
662
+ {
663
+ printfPQExpBuffer (& conn -> errorMessage ,
664
+ libpq_gettext ("unable to open certificate (%s): %s\n" ),
665
+ fnbuf , strerror (errno ));
666
+ return -1 ;
667
+ }
668
+ if (PEM_read_X509 (fp , x509 , NULL , NULL ) == NULL )
669
+ {
670
+ printfPQExpBuffer (& conn -> errorMessage ,
671
+ libpq_gettext ("unable to read certificate (%s): %s\n" ),
672
+ fnbuf , SSLerrmessage ());
673
+ fclose (fp );
674
+ return -1 ;
675
+ }
676
+ fclose (fp );
677
+
678
+ /* read the user key */
679
+ snprintf (fnbuf , sizeof fnbuf , "%s/.postgresql/postgresql.key" ,
680
+ pwd -> pw_dir );
681
+ if (stat (fnbuf , & buf ) == -1 )
682
+ {
683
+ printfPQExpBuffer (& conn -> errorMessage ,
684
+ libpq_gettext ("certificate present, but not private key (%s)\n" ),
685
+ fnbuf );
686
+ X509_free (* x509 );
687
+ return 0 ;
688
+ }
689
+ if (!S_ISREG (buf .st_mode ) || (buf .st_mode & 0077 ) ||
690
+ buf .st_uid != getuid ())
691
+ {
692
+ printfPQExpBuffer (& conn -> errorMessage ,
693
+ libpq_gettext ("private key has bad permissions (%s)\n" ), fnbuf );
694
+ X509_free (* x509 );
695
+ return -1 ;
696
+ }
697
+ if ((fp = fopen (fnbuf , "r" )) == NULL )
698
+ {
699
+ printfPQExpBuffer (& conn -> errorMessage ,
700
+ libpq_gettext ("unable to open private key file (%s): %s\n" ),
701
+ fnbuf , strerror (errno ));
702
+ X509_free (* x509 );
703
+ return -1 ;
704
+ }
705
+ if (fstat (fileno (fp ), & buf2 ) == -1 ||
706
+ buf .st_dev != buf2 .st_dev || buf .st_ino != buf2 .st_ino )
707
+ {
708
+ printfPQExpBuffer (& conn -> errorMessage ,
709
+ libpq_gettext ("private key changed under us (%s)\n" ), fnbuf );
710
+ X509_free (* x509 );
711
+ return -1 ;
712
+ }
713
+ if (PEM_read_PrivateKey (fp , pkey , cb , NULL ) == NULL )
714
+ {
715
+ printfPQExpBuffer (& conn -> errorMessage ,
716
+ libpq_gettext ("unable to read private key (%s): %s\n" ),
717
+ fnbuf , SSLerrmessage ());
718
+ X509_free (* x509 );
719
+ fclose (fp );
720
+ return -1 ;
721
+ }
722
+ fclose (fp );
723
+
724
+ return 1 ;
725
+ }
726
+
617
727
/*
618
728
* Initialize global SSL context.
619
729
*/
@@ -666,6 +776,9 @@ initialize_SSL (PGconn *conn)
666
776
SSL_CTX_set_tmp_dh_callback (SSL_context , tmp_dh_cb );
667
777
SSL_CTX_set_options (SSL_context , SSL_OP_SINGLE_DH_USE );
668
778
779
+ /* set up mechanism to provide client certificate, if available */
780
+ SSL_CTX_set_client_cert_cb (SSL_context , client_cert_cb );
781
+
669
782
return 0 ;
670
783
}
671
784
@@ -691,6 +804,7 @@ open_client_SSL (PGconn *conn)
691
804
int r ;
692
805
693
806
if (!(conn -> ssl = SSL_new (SSL_context )) ||
807
+ !SSL_set_app_data (conn -> ssl , conn ) ||
694
808
!SSL_set_fd (conn -> ssl , conn -> sock ) ||
695
809
SSL_connect (conn -> ssl ) <= 0 )
696
810
{
0 commit comments