#include <ac/unistd.h>
#include <ac/param.h>
#include <ac/dirent.h>
+#include <sys/stat.h>
+#include <fcntl.h>
#include "ldap-int.h"
#include "ldap-tls.h"
ber_memfree ( c );
}
+static int
+tlsg_getfile( const char *path, gnutls_datum_t *buf )
+{
+ int rc = -1, fd;
+ struct stat st;
+
+ fd = open( path, O_RDONLY );
+ if ( fd >= 0 && fstat( fd, &st ) == 0 ) {
+ buf->size = st.st_size;
+ buf->data = LDAP_MALLOC( st.st_size + 1 );
+ if ( buf->data ) {
+ rc = read( fd, buf->data, st.st_size );
+ close( fd );
+ if ( rc < st.st_size )
+ rc = -1;
+ else
+ rc = 0;
+ }
+ }
+ return rc;
+}
+
+/* This is the GnuTLS default */
+#define VERIFY_DEPTH 6
+
/*
* initialize a new TLS context
*/
}
if ( lo->ldo_tls_certfile && lo->ldo_tls_keyfile ) {
- rc = gnutls_certificate_set_x509_key_file(
- ctx->cred,
- lt->lt_certfile,
- lt->lt_keyfile,
+ gnutls_x509_privkey_t key;
+ gnutls_datum_t buf;
+ gnutls_x509_crt_t certs[VERIFY_DEPTH];
+ unsigned int max = VERIFY_DEPTH;
+
+ rc = gnutls_x509_privkey_init( &key );
+ if ( rc ) return -1;
+
+ /* OpenSSL builds the cert chain for us, but GnuTLS
+ * expects it to be present in the certfile. If it's
+ * not, we have to build it ourselves. So we have to
+ * do some special checks here...
+ */
+ rc = tlsg_getfile( lt->lt_keyfile, &buf );
+ if ( rc ) return -1;
+ rc = gnutls_x509_privkey_import( key, &buf,
GNUTLS_X509_FMT_PEM );
+ LDAP_FREE( buf.data );
+ if ( rc < 0 ) return rc;
+
+ rc = tlsg_getfile( lt->lt_certfile, &buf );
+ if ( rc ) return -1;
+ rc = gnutls_x509_crt_list_import( certs, &max, &buf,
+ GNUTLS_X509_FMT_PEM, 0 );
+ LDAP_FREE( buf.data );
+ if ( rc < 0 ) return rc;
+
+ /* If there's only one cert and it's not self-signed,
+ * then we have to build the cert chain.
+ */
+ if ( max == 1 && !gnutls_x509_crt_check_issuer( certs[0], certs[0] )) {
+ gnutls_x509_crt_t *cas;
+ unsigned int i, j, ncas;
+
+ gnutls_certificate_get_x509_cas( ctx->cred, &cas, &ncas );
+ for ( i = 1; i<VERIFY_DEPTH; i++ ) {
+ for ( j = 0; j<ncas; j++ ) {
+ if ( gnutls_x509_crt_check_issuer( certs[i-1], cas[j] )) {
+ certs[i] = cas[j];
+ max++;
+ /* If this CA is self-signed, we're done */
+ if ( gnutls_x509_crt_check_issuer( cas[j], cas[j] ))
+ j = ncas;
+ break;
+ }
+ }
+ /* only continue if we found a CA and it was not self-signed */
+ if ( j == ncas )
+ break;
+ }
+ }
+ rc = gnutls_certificate_set_x509_key( ctx->cred, certs, max, key );
if ( rc ) return -1;
} else if ( lo->ldo_tls_certfile || lo->ldo_tls_keyfile ) {
Debug( LDAP_DEBUG_ANY,
if ( rc < 0 ) return -1;
rc = 0;
}
+
+ /* FIXME: ITS#5992 - this should go be configurable,
+ * and V1 CA certs should be phased out ASAP.
+ */
+ gnutls_certificate_set_verify_flags( ctx->cred,
+ GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT );
+
if ( is_server ) {
gnutls_dh_params_init(&ctx->dh_params);
gnutls_dh_params_generate2(ctx->dh_params, DH_BITS);
rc = gnutls_handshake( s->session );
if ( rc == 0 && s->ctx->lo->ldo_tls_require_cert != LDAP_OPT_X_TLS_NEVER ) {
- rc = tlsg_cert_verify( s );
- if ( rc && s->ctx->lo->ldo_tls_require_cert == LDAP_OPT_X_TLS_ALLOW )
+ const gnutls_datum_t *peer_cert_list;
+ unsigned int list_size;
+
+ peer_cert_list = gnutls_certificate_get_peers( s->session,
+ &list_size );
+ if ( !peer_cert_list && s->ctx->lo->ldo_tls_require_cert == LDAP_OPT_X_TLS_TRY )
rc = 0;
+ else {
+ rc = tlsg_cert_verify( s );
+ if ( rc && s->ctx->lo->ldo_tls_require_cert == LDAP_OPT_X_TLS_ALLOW )
+ rc = 0;
+ }
}
return rc;
}
if (!x) return LDAP_INVALID_CREDENTIALS;
- bv.bv_val = x->data;
+ bv.bv_val = (char *) x->data;
bv.bv_len = x->size;
tlsg_x509_cert_dn( &bv, der_dn, 1 );
tlsg_session *s = (tlsg_session *)session;
if ( !s->peer_der_dn.bv_val ) {
const gnutls_datum_t *peer_cert_list;
- int list_size;
+ unsigned int list_size;
struct berval bv;
peer_cert_list = gnutls_certificate_get_peers( s->session,
if ( !peer_cert_list ) return LDAP_INVALID_CREDENTIALS;
bv.bv_len = peer_cert_list->size;
- bv.bv_val = peer_cert_list->data;
+ bv.bv_val = (char *) peer_cert_list->data;
tlsg_x509_cert_dn( &bv, &s->peer_der_dn, 1 );
}
tlsg_session *s = (tlsg_session *)session;
int i, ret;
const gnutls_datum_t *peer_cert_list;
- int list_size;
- struct berval bv;
+ unsigned int list_size;
char altname[NI_MAXHOST];
size_t altnamesize;
gnutls_x509_crt_t cert;
- gnutls_datum_t *x;
const char *name;
char *ptr;
char *domain = NULL;
#else
struct in_addr addr;
#endif
- int n, len1 = 0, len2 = 0;
+ int len1 = 0, len2 = 0;
int ntype = IS_DNS;
- time_t now = time(0);
if( ldap_int_hostname &&
( !name_in || !strcasecmp( name_in, "localhost" ) ) )
if ( ret >= 0 ) {
ret = LDAP_SUCCESS;
} else {
- altnamesize = sizeof(altname);
- ret = gnutls_x509_crt_get_dn_by_oid( cert, CN_OID,
- 0, 0, altname, &altnamesize );
+ /* find the last CN */
+ i=0;
+ do {
+ altnamesize = 0;
+ ret = gnutls_x509_crt_get_dn_by_oid( cert, CN_OID,
+ i, 1, altname, &altnamesize );
+ if ( ret == GNUTLS_E_SHORT_MEMORY_BUFFER )
+ i++;
+ else
+ break;
+ } while ( 1 );
+
+ if ( i ) {
+ altnamesize = sizeof(altname);
+ ret = gnutls_x509_crt_get_dn_by_oid( cert, CN_OID,
+ i-1, 0, altname, &altnamesize );
+ }
+
if ( ret < 0 ) {
Debug( LDAP_DEBUG_ANY,
"TLS: unable to get common name from peer certificate.\n",
{
struct tls_data *p;
ber_slen_t ret;
- int err;
assert( sbiod != NULL );
assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
{
struct tls_data *p;
ber_slen_t ret;
- int err;
assert( sbiod != NULL );
assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
unsigned int status = 0;
int err;
time_t now = time(0);
+ time_t peertime;
err = gnutls_certificate_verify_peers2( ssl->session, &status );
if ( err < 0 ) {
status, 0,0 );
return -1;
}
- if ( gnutls_certificate_expiration_time_peers( ssl->session ) < now ) {
+ peertime = gnutls_certificate_expiration_time_peers( ssl->session );
+ if ( peertime == (time_t) -1 ) {
+ Debug( LDAP_DEBUG_ANY, "TLS: gnutls_certificate_expiration_time_peers failed\n",
+ 0, 0, 0 );
+ return -1;
+ }
+ if ( peertime < now ) {
Debug( LDAP_DEBUG_ANY, "TLS: peer certificate is expired\n",
0, 0, 0 );
return -1;
}
- if ( gnutls_certificate_activation_time_peers( ssl->session ) > now ) {
+ peertime = gnutls_certificate_activation_time_peers( ssl->session );
+ if ( peertime == (time_t) -1 ) {
+ Debug( LDAP_DEBUG_ANY, "TLS: gnutls_certificate_activation_time_peers failed\n",
+ 0, 0, 0 );
+ return -1;
+ }
+ if ( peertime > now ) {
Debug( LDAP_DEBUG_ANY, "TLS: peer certificate not yet active\n",
0, 0, 0 );
return -1;