Commit 4b7eb173 authored by Quanah Gibson-Mount's avatar Quanah Gibson-Mount
Browse files

Use modular TLS

ITS#5655
ITS#5887
ITS#5462
ITS#5789
parent 85950000
......@@ -2,19 +2,23 @@ OpenLDAP 2.4 Change Log
OpenLDAP 2.4.14 Engineering
Added libldap option to disable SASL host canonicalization (ITS#5812)
Added libldap TLS_PROTOCOL_MIN (ITS#5655)
Added libldap GnuTLS support for TLS_CIPHER_SUITE (ITS#5887)
Added libldap GnuTLS setting random file (ITS#5462)
Fixed libldap deref handling (ITS#5768)
Fixed libldap peer cert memory leak (ITS#5849)
Fixed libldap interaction with GnuTLS CN IP-based matches (ITS#5789)
Fixed libldap intermediate response behavior (ITS#5896)
Fixed libldap_r deref building (ITS#5768)
Fixed libldap_r slapd lockup when paused during shutdown (ITS#5841)
Added slapd syncrepl default retry setting (ITS#5825)
Added slapd val.regex expansion (ITS#5804)
Added slapo-rwm newRDN rewriting (ITS#5834)
Added slapd TLS_PROTOCOL_MIN (ITS#5655)
Added slapd slapi_pw_find (ITS#2615,ITS#4359)
Fixed slapd bconfig to return error codes (ITS#5867)
Fixed slapd bconfig encoding incorrectly (ITS#5897)
Fixed slapd connection assert (ITS#5835)
Fixed slapd epoll handling (ITS#5886)
Added slapd slapi_pw_find (ITS#2615,ITS#4359)
Fixed slapd syncrepl rename handling (ITS#5809)
Fixed slapd syncrepl MMR when adding new server (ITS#5850)
Fixed slapd syncrepl MMR with deleted entries (ITS#5843)
......@@ -35,6 +39,7 @@ OpenLDAP 2.4.14 Engineering
Fixed slapo-pcache filter sorting (ITS#5756)
Fixed slapo-ppolicy to not be global (ITS#5858)
Fixed slapo-rwm with back-config (ITS#5906)
Added slapo-rwm newRDN rewriting (ITS#5834)
Updated contrib/addpartial module (ITS#5764)
Added contrib/cloak module (ITS#5872)
Added contrib/smbk5pwd gcrypt support (ITS#5410)
......
......@@ -144,7 +144,7 @@ LDAP_BEGIN_DECL
#define LDAP_OPT_X_TLS_CERTFILE 0x6004
#define LDAP_OPT_X_TLS_KEYFILE 0x6005
#define LDAP_OPT_X_TLS_REQUIRE_CERT 0x6006
/* #define LDAP_OPT_X_TLS_PROTOCOL 0x6007 */
#define LDAP_OPT_X_TLS_PROTOCOL_MIN 0x6007
#define LDAP_OPT_X_TLS_CIPHER_SUITE 0x6008
#define LDAP_OPT_X_TLS_RANDOM_FILE 0x6009
#define LDAP_OPT_X_TLS_SSL_CTX 0x600a /* OpenSSL SSL* */
......@@ -165,6 +165,14 @@ LDAP_BEGIN_DECL
#define LDAP_OPT_X_TLS_CRL_PEER 1
#define LDAP_OPT_X_TLS_CRL_ALL 2
/* for LDAP_OPT_X_TLS_PROTOCOL_MIN */
#define LDAP_OPT_X_TLS_PROTOCOL(maj,min) (((maj) << 8) + (min))
#define LDAP_OPT_X_TLS_PROTOCOL_SSL2 (2 << 8)
#define LDAP_OPT_X_TLS_PROTOCOL_SSL3 (3 << 8)
#define LDAP_OPT_X_TLS_PROTOCOL_TLS1_0 ((3 << 8) + 1)
#define LDAP_OPT_X_TLS_PROTOCOL_TLS1_1 ((3 << 8) + 2)
#define LDAP_OPT_X_TLS_PROTOCOL_TLS1_2 ((3 << 8) + 3)
/* OpenLDAP SASL options */
#define LDAP_OPT_X_SASL_MECH 0x6100
#define LDAP_OPT_X_SASL_REALM 0x6101
......
......@@ -25,7 +25,8 @@ SRCS = bind.c open.c result.c error.c compare.c search.c \
getdn.c getentry.c getattr.c getvalues.c addentry.c \
request.c os-ip.c url.c pagectrl.c sortctrl.c vlvctrl.c \
init.c options.c print.c string.c util-int.c schema.c \
charray.c tls.c os-local.c dnssrv.c utf-8.c utf-8-conv.c \
charray.c os-local.c dnssrv.c utf-8.c utf-8-conv.c \
tls2.c tls_o.c tls_g.c tls_m.c \
turn.c ppolicy.c dds.c txn.c ldap_sync.c stctrl.c \
assertion.c deref.c
......@@ -37,7 +38,8 @@ OBJS = bind.lo open.lo result.lo error.lo compare.lo search.lo \
getdn.lo getentry.lo getattr.lo getvalues.lo addentry.lo \
request.lo os-ip.lo url.lo pagectrl.lo sortctrl.lo vlvctrl.lo \
init.lo options.lo print.lo string.lo util-int.lo schema.lo \
charray.lo tls.lo os-local.lo dnssrv.lo utf-8.lo utf-8-conv.lo \
charray.lo os-local.lo dnssrv.lo utf-8.lo utf-8-conv.lo \
tls2.lo tls_o.lo tls_g.lo tls_m.lo \
turn.lo ppolicy.lo dds.lo txn.lo ldap_sync.lo stctrl.lo \
assertion.lo deref.lo
......
......@@ -123,6 +123,7 @@ static const struct ol_attribute {
{0, ATTR_TLS, "TLS_REQCERT", NULL, LDAP_OPT_X_TLS_REQUIRE_CERT},
{0, ATTR_TLS, "TLS_RANDFILE", NULL, LDAP_OPT_X_TLS_RANDOM_FILE},
{0, ATTR_TLS, "TLS_CIPHER_SUITE", NULL, LDAP_OPT_X_TLS_CIPHER_SUITE},
{0, ATTR_TLS, "TLS_PROTOCOL_MIN", NULL, LDAP_OPT_X_TLS_PROTOCOL_MIN},
#ifdef HAVE_OPENSSL_CRL
{0, ATTR_TLS, "TLS_CRLCHECK", NULL, LDAP_OPT_X_TLS_CRLCHECK},
......
......@@ -155,9 +155,9 @@ struct ldaptls {
char *lt_cacertfile;
char *lt_cacertdir;
char *lt_ciphersuite;
#ifdef HAVE_GNUTLS
char *lt_crlfile;
#endif
char *lt_randfile; /* OpenSSL only */
int lt_protocol_min;
};
#endif
......@@ -205,9 +205,12 @@ struct ldapoptions {
#define ldo_tls_cacertfile ldo_tls_info.lt_cacertfile
#define ldo_tls_cacertdir ldo_tls_info.lt_cacertdir
#define ldo_tls_ciphersuite ldo_tls_info.lt_ciphersuite
#define ldo_tls_protocol_min ldo_tls_info.lt_protocol_min
#define ldo_tls_crlfile ldo_tls_info.lt_crlfile
#define ldo_tls_randfile ldo_tls_info.lt_randfile
int ldo_tls_mode;
int ldo_tls_require_cert;
int ldo_tls_impl;
#ifdef HAVE_OPENSSL_CRL
int ldo_tls_crlcheck;
#endif
......
/* ldap-tls.h - TLS defines & prototypes internal to the LDAP library */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
* Copyright 2008-2009 The OpenLDAP Foundation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted only as authorized by the OpenLDAP
* Public License.
*
* A copy of this license is available in the file LICENSE in the
* top-level directory of the distribution or, alternatively, at
* <http://www.OpenLDAP.org/license.html>.
*/
#ifndef _LDAP_TLS_H
#define _LDAP_TLS_H 1
struct tls_impl;
struct tls_ctx;
struct tls_session;
typedef struct tls_ctx tls_ctx;
typedef struct tls_session tls_session;
typedef int (TI_tls_init)(void);
typedef void (TI_tls_destroy)(void);
typedef tls_ctx *(TI_ctx_new)(struct ldapoptions *lo);
typedef void (TI_ctx_ref)(tls_ctx *ctx);
typedef void (TI_ctx_free)(tls_ctx *ctx);
typedef int (TI_ctx_init)(struct ldapoptions *lo, struct ldaptls *lt, int is_server);
typedef tls_session *(TI_session_new)(tls_ctx *ctx, int is_server);
typedef int (TI_session_connect)(LDAP *ld, tls_session *s);
typedef int (TI_session_accept)(tls_session *s);
typedef int (TI_session_upflags)(Sockbuf *sb, tls_session *s, int rc);
typedef char *(TI_session_errmsg)(int rc, char *buf, size_t len );
typedef int (TI_session_dn)(tls_session *sess, struct berval *dn);
typedef int (TI_session_chkhost)(LDAP *ld, tls_session *s, const char *name_in);
typedef int (TI_session_strength)(tls_session *sess);
typedef void (TI_thr_init)(void);
typedef struct tls_impl {
const char *ti_name;
TI_tls_init *ti_tls_init; /* library initialization */
TI_tls_destroy *ti_tls_destroy;
TI_ctx_new *ti_ctx_new;
TI_ctx_ref *ti_ctx_ref;
TI_ctx_free *ti_ctx_free;
TI_ctx_init *ti_ctx_init;
TI_session_new *ti_session_new;
TI_session_connect *ti_session_connect;
TI_session_accept *ti_session_accept;
TI_session_upflags *ti_session_upflags;
TI_session_errmsg *ti_session_errmsg;
TI_session_dn *ti_session_my_dn;
TI_session_dn *ti_session_peer_dn;
TI_session_chkhost *ti_session_chkhost;
TI_session_strength *ti_session_strength;
Sockbuf_IO *ti_sbio;
TI_thr_init *ti_thr_init;
int ti_inited;
} tls_impl;
extern tls_impl ldap_int_tls_impl;
#endif /* _LDAP_TLS_H */
This diff is collapsed.
/* tls_m.c - Handle tls/ssl using Mozilla NSS. */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
* Copyright 2008-2009 The OpenLDAP Foundation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted only as authorized by the OpenLDAP
* Public License.
*
* A copy of this license is available in the file LICENSE in the
* top-level directory of the distribution or, alternatively, at
* <http://www.OpenLDAP.org/license.html>.
*/
/* ACKNOWLEDGEMENTS: written by Howard Chu.
*/
#include "portable.h"
#ifdef HAVE_MOZNSS
#include "ldap_config.h"
#include <stdio.h>
#include <ac/stdlib.h>
#include <ac/errno.h>
#include <ac/socket.h>
#include <ac/string.h>
#include <ac/ctype.h>
#include <ac/time.h>
#include <ac/unistd.h>
#include <ac/param.h>
#include <ac/dirent.h>
#include "ldap-int.h"
#include "ldap-tls.h"
#ifdef LDAP_R_COMPILE
#include <ldap_pvt_thread.h>
#endif
#include <nspr.h>
#include <nss.h>
#include <ssl.h>
typedef struct tlsm_ctx {
PRFileDesc *tc_model;
int tc_refcnt;
#ifdef LDAP_R_COMPILE
ldap_pvt_thread_mutex_t tc_refmutex;
#endif
} tlsm_ctx;
typedef PRFileDesc tlsm_session;
static PRDescIdentity tlsm_layer_id;
static const PRIOMethods tlsm_PR_methods;
extern tls_impl ldap_int_tls_impl;
#ifdef LDAP_R_COMPILE
static void
tlsm_thr_init( void )
{
}
#endif /* LDAP_R_COMPILE */
/*
* Initialize TLS subsystem. Should be called only once.
*/
static int
tlsm_init( void )
{
PR_Init(0, 0, 0);
tlsm_layer_id = PR_GetUniqueIdentity("OpenLDAP");
if ( !NSS_IsInitialized() ) {
NSS_NoDB_Init("");
NSS_SetDomesticPolicy();
}
/* No cipher suite handling for now */
return 0;
}
/*
* Tear down the TLS subsystem. Should only be called once.
*/
static void
tlsm_destroy( void )
{
NSS_Shutdown();
PR_Cleanup();
}
static tls_ctx *
tlsm_ctx_new ( struct ldapoptions *lo )
{
tlsm_ctx *ctx;
ctx = LDAP_MALLOC( sizeof (*ctx) );
if ( ctx ) {
PRFileDesc *fd = PR_CreateIOLayerStub(tlsm_layer_id, &tlsm_PR_methods);
if ( fd ) {
ctx->tc_model = SSL_ImportFD( NULL, fd );
if ( ctx->tc_model ) {
ctx->tc_refcnt = 1;
#ifdef LDAP_R_COMPILE
ldap_pvt_thread_mutex_init( &ctx->tc_refmutex );
#endif
} else {
PR_DELETE( fd );
LDAP_FREE( ctx );
ctx = NULL;
}
} else {
LDAP_FREE( ctx );
ctx = NULL;
}
}
return (tls_ctx *)ctx;
}
static void
tlsm_ctx_ref( tls_ctx *ctx )
{
tlsm_ctx *c = (tlsm_ctx *)ctx;
#ifdef LDAP_R_COMPILE
ldap_pvt_thread_mutex_lock( &c->tc_refmutex );
#endif
c->tc_refcnt++;
#ifdef LDAP_R_COMPILE
ldap_pvt_thread_mutex_unlock( &c->tc_refmutex );
#endif
}
static void
tlsm_ctx_free ( tls_ctx *ctx )
{
tlsm_ctx *c = (tlsm_ctx *)ctx;
int refcount;
if ( !c ) return;
#ifdef LDAP_R_COMPILE
ldap_pvt_thread_mutex_lock( &c->tc_refmutex );
#endif
refcount = --c->tc_refcnt;
#ifdef LDAP_R_COMPILE
ldap_pvt_thread_mutex_unlock( &c->tc_refmutex );
#endif
if ( refcount )
return;
PR_Close( c->tc_model );
#ifdef LDAP_R_COMPILE
ldap_pvt_thread_mutex_destroy( &c->tc_refmutex );
#endif
LDAP_FREE( c );
}
/*
* initialize a new TLS context
*/
static int
tlsm_ctx_init( struct ldapoptions *lo, struct ldaptls *lt, int is_server )
{
tlsm_ctx *ctx = lo->ldo_tls_ctx;
int rc;
SSL_OptionSet( ctx->tc_model, SSL_SECURITY, PR_TRUE );
SSL_OptionSet( ctx->tc_model, SSL_HANDSHAKE_AS_CLIENT, !is_server );
SSL_OptionSet( ctx->tc_model, SSL_HANDSHAKE_AS_SERVER, is_server );
/* See SECMOD_OpenUserDB() */
#if 0
if ( lo->ldo_tls_ciphersuite &&
tlsm_parse_ciphers( ctx, lt->lt_ciphersuite )) {
Debug( LDAP_DEBUG_ANY,
"TLS: could not set cipher list %s.\n",
lo->ldo_tls_ciphersuite, 0, 0 );
return -1;
}
if (lo->ldo_tls_cacertdir != NULL) {
Debug( LDAP_DEBUG_ANY,
"TLS: warning: cacertdir not implemented for gnutls\n",
NULL, NULL, NULL );
}
if (lo->ldo_tls_cacertfile != NULL) {
rc = gnutls_certificate_set_x509_trust_file(
ctx->cred,
lt->lt_cacertfile,
GNUTLS_X509_FMT_PEM );
if ( rc < 0 ) return -1;
}
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_FMT_PEM );
if ( rc ) return -1;
} else if ( lo->ldo_tls_certfile || lo->ldo_tls_keyfile ) {
Debug( LDAP_DEBUG_ANY,
"TLS: only one of certfile and keyfile specified\n",
NULL, NULL, NULL );
return -1;
}
if ( lo->ldo_tls_dhfile ) {
Debug( LDAP_DEBUG_ANY,
"TLS: warning: ignoring dhfile\n",
NULL, NULL, NULL );
}
if ( lo->ldo_tls_crlfile ) {
rc = gnutls_certificate_set_x509_crl_file(
ctx->cred,
lt->lt_crlfile,
GNUTLS_X509_FMT_PEM );
if ( rc < 0 ) return -1;
rc = 0;
}
if ( is_server ) {
gnutls_dh_params_init(&ctx->dh_params);
gnutls_dh_params_generate2(ctx->dh_params, DH_BITS);
}
#endif
return 0;
}
static tls_session *
tlsm_session_new ( tls_ctx * ctx, int is_server )
{
tlsm_ctx *c = (tlsm_ctx *)ctx;
tlsm_session *session;
PRFileDesc *fd;
fd = PR_CreateIOLayerStub(tlsm_layer_id, &tlsm_PR_methods);
if ( !fd ) {
return NULL;
}
session = SSL_ImportFD( c->tc_model, fd );
if ( !session ) {
PR_DELETE( fd );
return NULL;
}
SSL_ResetHandshake( session, is_server );
return (tls_session *)session;
}
static int
tlsm_session_accept( tls_session *session )
{
tlsm_session *s = (tlsm_session *)session;
return SSL_ForceHandshake( s );
}
static int
tlsm_session_connect( LDAP *ld, tls_session *session )
{
tlsm_session *s = (tlsm_session *)session;
int rc;
/* By default, NSS checks the cert hostname for us */
rc = SSL_SetURL( s, ld->ld_options.ldo_defludp->lud_host );
return SSL_ForceHandshake( s );
}
static int
tlsm_session_upflags( Sockbuf *sb, tls_session *session, int rc )
{
/* Should never happen */
rc = PR_GetError();
if ( rc != PR_PENDING_INTERRUPT_ERROR && rc != PR_WOULD_BLOCK_ERROR )
return 0;
return 0;
}
static char *
tlsm_session_errmsg( int rc, char *buf, size_t len )
{
int i;
rc = PR_GetError();
i = PR_GetErrorTextLength();
if ( i > len ) {
char *msg = LDAP_MALLOC( i+1 );
PR_GetErrorText( msg );
memcpy( buf, msg, len );
LDAP_FREE( msg );
} else if ( i ) {
PR_GetErrorText( buf );
}
return i ? buf : NULL;
}
static int
tlsm_session_my_dn( tls_session *session, struct berval *der_dn )
{
tlsm_session *s = (tlsm_session *)session;
CERTCertificate *cert;
cert = SSL_LocalCertificate( s );
if (!cert) return LDAP_INVALID_CREDENTIALS;
der_dn->bv_val = cert->derSubject.data;
der_dn->bv_len = cert->derSubject.len;
CERT_DestroyCertificate( cert );
return 0;
}
static int
tlsm_session_peer_dn( tls_session *session, struct berval *der_dn )
{
tlsm_session *s = (tlsm_session *)session;
CERTCertificate *cert;
cert = SSL_PeerCertificate( s );
if (!cert) return LDAP_INVALID_CREDENTIALS;
der_dn->bv_val = cert->derSubject.data;
der_dn->bv_len = cert->derSubject.len;
CERT_DestroyCertificate( cert );
return 0;
}
/* what kind of hostname were we given? */
#define IS_DNS 0
#define IS_IP4 1
#define IS_IP6 2
static int
tlsm_session_chkhost( LDAP *ld, tls_session *session, const char *name_in )
{
/* NSS already does a hostname check */
#if 0
int i, ret;
const gnutls_datum_t *peer_cert_list;
int list_size;
struct berval bv;
char altname[NI_MAXHOST];
size_t altnamesize;
gnutls_x509_crt_t cert;
gnutls_datum_t *x;
const char *name;
char *ptr;
char *domain = NULL;
#ifdef LDAP_PF_INET6
struct in6_addr addr;
#else
struct in_addr addr;
#endif
int n, len1 = 0, len2 = 0;
int ntype = IS_DNS;
time_t now = time(0);
if( ldap_int_hostname &&
( !name_in || !strcasecmp( name_in, "localhost" ) ) )
{
name = ldap_int_hostname;
} else {
name = name_in;
}
peer_cert_list = gnutls_certificate_get_peers( session->session,
&list_size );
if ( !peer_cert_list ) {
Debug( LDAP_DEBUG_ANY,
"TLS: unable to get peer certificate.\n",
0, 0, 0 );
/* If this was a fatal condition, things would have
* aborted long before now.
*/
return LDAP_SUCCESS;
}
ret = gnutls_x509_crt_init( &cert );
if ( ret < 0 )
return LDAP_LOCAL_ERROR;
ret = gnutls_x509_crt_import( cert, peer_cert_list, GNUTLS_X509_FMT_DER );
if ( ret ) {
gnutls_x509_crt_deinit( cert );
return LDAP_LOCAL_ERROR;
}
#ifdef LDAP_PF_INET6
if (name[0] == '[' && strchr(name, ']')) {
char *n2 = ldap_strdup(name+1);
*strchr(n2, ']') = 2;
if (inet_pton(AF_INET6, n2, &addr))
ntype = IS_IP6;
LDAP_FREE(n2);
} else
#endif
if ((ptr = strrchr(name, '.')) && isdigit((unsigned char)ptr[1])) {
if (inet_aton(name, (struct in_addr *)&addr)) ntype = IS_IP4;
}
if (ntype == IS_DNS) {
len1 = strlen(name);
domain = strchr(name, '.');
if (domain) {
len2 = len1 - (domain-name);
}
}
for ( i=0, ret=0; ret >= 0; i++ ) {
altnamesize = sizeof(altname);
ret = gnutls_x509_crt_get_subject_alt_name( cert, i,
altname, &altnamesize, NULL );
if ( ret < 0 ) break;
/* ignore empty */
if ( altnamesize == 0 ) continue;
if ( ret == GNUTLS_SAN_DNSNAME ) {
if (ntype != IS_DNS) continue;