On 03/09/2013 04:52 PM, Ian Pilcher wrote: > After looking at be-secure.c and investigating the way that OpenSSL > validates certificates, I do not believe that there is any way of > achieving the desired behavior with the current codebase. Test process: SET UP SERVER VERIFIED SSL (NO CLIENT CERTS) ------------------------------------------------------------------ Edited postgresql.conf and set: ssl=on ssl_cert_file = 'server.crt' ssl_key_file = 'server.key' ssl_ca_file='root.crt' Copied your samples: # cp $CERTS/postgres.crt server.crt # cp $CERTS/postgres.key server.key # cp $CERTS/client-ca.crt root.crt # chown postgres:postgres root.crt server.crt server.key # chmod 0600 server.crt server.key root.crt # systemctl restart postgresql-9.2.service $ psql "postgresql://localhost/?sslmode=require" SSL connection (cipher: DHE-RSA-AES256-SHA, bits: 256) (connects OK; expected since we aren't requiring client certs yet and we aren't validating the server cert). $ psql "postgresql://localhost/?sslmode=verify-ca" psql: root certificate file "/home/craig/.postgresql/root.crt" does not exist Either provide the file or change sslmode to disable server certificate verification. Again expected, though we really should be using the system SSL cert database. Anyway: $ mkdir .postgresql $ cp $CERTS/root-ca.crt ~/.postgresql/root.crt (This should be the trusted root, not server-ca or client-ca since we shouldn't have to keep copies of either to verify server trust). Now, test we can verify the server's identity: $ psql "postgresql://localhost/?sslmode=require" psql: SSL error: certificate verify failed Plonk, we can't. The reason for this is that the server has sent us its cert and we have the root cert, but we don't have the intermediate server-ca.crt . Append that to server.crt: # cat $CERTS/postgres.crt $CERTS/server-ca.crt > server.crt # systemctl restart postgresql-9.2.service $ psql "postgresql://localhost/?sslmode=require" SSL connection (cipher: DHE-RSA-AES256-SHA, bits: 256) OK, we're good now, the server is sending us the intermediate cert we require. Regular non-client-cert verified SSL is fine. Examination of the protocol chat shows that the server is sending a Server Hello with a Certificate message containing the server and intermdediate certificate DNs: id-at-commonName=postgres.example.com,id-at-organizationName=Ian Pilcher,id-at-localityName=Carrollton,id-at-stateOrProvinceName=Texas,id-at-countryName=US id-at-commonName=Server CA,id-at-organizationName=Ian Pilcher,id-at-localityName=Carrollton,id-at-stateOrProvinceName=Texas,id-at-countryName=US as expected. ENABLE CLIENT CERTIFICATE VERIFICATION --------------------------------------------------------- Now lets install our client certificates in the client. $ cp $CERTS/good-client.key ~/.postgresql/postgresql.key $ # Note that the order is important here, the client cert must appear first, followed by the chain cert(s) $ cat $CERTS/good-client.crt $CERTS/client-ca.crt > ~/.postgresql/postgresql.crt $ chmod 0600 .postgresql/postgresql.key $ psql "postgresql://localhost/?sslmode=verify-ca" psql: SSL error: tlsv1 alert unknown ca Examination of the handshake shows that the server is sending a request for client certificates signed by: Distinguished Name: (id-at-commonName=Client CA,id-at-organizationName=Ian Pilcher,id-at-localityName=Carrollton,id-at-stateOrProvinceName=Texas,id-at-countryName=US) and the client is sending in response: Certificate (id-at-commonName=Good Client,id-at-organizationName=Ian Pilcher,id-at-localityName=Carrollton,id-at-stateOrProvinceName=Texas,id-at-countryName=US) Certificate (id-at-commonName=Client CA,id-at-organizationName=Ian Pilcher,id-at-localityName=Carrollton,id-at-stateOrProvinceName=Texas,id-at-countryName=US) as expected, and good-client.crt is indeed signed by client-ca.crt . Again the issue appears to be that Pg can't find the root of trust, which is fair enough given that it is not present in Pg's root.crt or server.crt and it isn't installed system-wide either. We could add it to the server's root.crt but that'd cause the issues that started this thread: # cat $CERTS/client-ca.crt $CERTS/root-ca.crt > root.crt # systemctl restart postgresql-9.2.service the good client cert works now: $ psql "postgresql://localhost/?sslmode=verify-ca" SSL connection (cipher: DHE-RSA-AES256-SHA, bits: 256) PROBLEM VERIFIED -------------------------- ... but so does the one we don't want to trust, as per the problem report: $ cat $CERTS/bad-client.crt $CERTS/server-ca.crt > postgresql.crt $ cp $CERTS/bad-client.key postgresql.key $ chmod 600 postgresql.key $ psql "postgresql://localhost/?sslmode=verify-ca" SSL connection (cipher: DHE-RSA-AES256-SHA, bits: 256) So this problem is verified. THE SOLUTION -------------------- What we need to happen instead is for root.crt to contain only the trusted certificates and have a *separate* file or directory for intermediate certificates that OpenSSL can look up to get the intermediates it needs to validate client certs, like `ssl_ca_chain_file` or `ssl_ca_chain_path` if we want to support OpenSSL's hashed certificate directories. System wide installation of the root may allow OpenSSL to discover it and use it for verification back to the root without having to trust it to sign clients. I'll do some more checking to see if this is possible with how Pg uses OpenSSL but I'm inclined to doubt it. I thought you might be able to add the common root to the server.crt certificate chain to let OpenSSL discover it that way, but it looks like OpenSSL won't use certs it's seen in server.crt when verifying client cert trust paths. -- Craig Ringer http://www.2ndQuadrant.com/ PostgreSQL Development, 24x7 Support, Training & Services -- Sent via pgsql-general mailing list (pgsql-general@xxxxxxxxxxxxxx) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-general