mirror of
https://git.openldap.org/openldap/openldap.git
synced 2024-12-21 03:10:25 +08:00
1011 lines
22 KiB
C
1011 lines
22 KiB
C
/* $OpenLDAP$ */
|
|
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
|
|
*
|
|
* Copyright 1998-2020 The OpenLDAP Foundation.
|
|
* All rights reserved.
|
|
*
|
|
* Author: Stefan Metzmacher <metze@sernet.de>
|
|
*
|
|
* 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>.
|
|
*/
|
|
|
|
#include "portable.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <ac/socket.h>
|
|
#include <ac/stdlib.h>
|
|
#include <ac/string.h>
|
|
#include <ac/time.h>
|
|
#include <ac/errno.h>
|
|
#include <ac/ctype.h>
|
|
#include <ac/unistd.h>
|
|
|
|
#ifdef HAVE_LIMITS_H
|
|
#include <limits.h>
|
|
#endif
|
|
|
|
#include "ldap-int.h"
|
|
|
|
#ifdef HAVE_GSSAPI
|
|
|
|
#ifdef HAVE_GSSAPI_GSSAPI_H
|
|
#include <gssapi/gssapi.h>
|
|
#else
|
|
#include <gssapi.h>
|
|
#endif
|
|
|
|
static char *
|
|
gsserrstr(
|
|
char *buf,
|
|
ber_len_t buf_len,
|
|
gss_OID mech,
|
|
int gss_rc,
|
|
OM_uint32 minor_status )
|
|
{
|
|
OM_uint32 min2;
|
|
gss_buffer_desc mech_msg = GSS_C_EMPTY_BUFFER;
|
|
gss_buffer_desc gss_msg = GSS_C_EMPTY_BUFFER;
|
|
gss_buffer_desc minor_msg = GSS_C_EMPTY_BUFFER;
|
|
OM_uint32 msg_ctx = 0;
|
|
|
|
if (buf == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
if (buf_len == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
#ifdef HAVE_GSS_OID_TO_STR
|
|
gss_oid_to_str(&min2, mech, &mech_msg);
|
|
#endif
|
|
gss_display_status(&min2, gss_rc, GSS_C_GSS_CODE,
|
|
mech, &msg_ctx, &gss_msg);
|
|
gss_display_status(&min2, minor_status, GSS_C_MECH_CODE,
|
|
mech, &msg_ctx, &minor_msg);
|
|
|
|
snprintf(buf, buf_len, "gss_rc[%d:%*s] mech[%*s] minor[%u:%*s]",
|
|
gss_rc, (int)gss_msg.length,
|
|
(const char *)(gss_msg.value?gss_msg.value:""),
|
|
(int)mech_msg.length,
|
|
(const char *)(mech_msg.value?mech_msg.value:""),
|
|
minor_status, (int)minor_msg.length,
|
|
(const char *)(minor_msg.value?minor_msg.value:""));
|
|
|
|
gss_release_buffer(&min2, &mech_msg);
|
|
gss_release_buffer(&min2, &gss_msg);
|
|
gss_release_buffer(&min2, &minor_msg);
|
|
|
|
buf[buf_len-1] = '\0';
|
|
|
|
return buf;
|
|
}
|
|
|
|
static void
|
|
sb_sasl_gssapi_init(
|
|
struct sb_sasl_generic_data *p,
|
|
ber_len_t *min_send,
|
|
ber_len_t *max_send,
|
|
ber_len_t *max_recv )
|
|
{
|
|
gss_ctx_id_t gss_ctx = (gss_ctx_id_t)p->ops_private;
|
|
int gss_rc;
|
|
OM_uint32 minor_status;
|
|
gss_OID ctx_mech = GSS_C_NO_OID;
|
|
OM_uint32 ctx_flags = 0;
|
|
int conf_req_flag = 0;
|
|
OM_uint32 max_input_size;
|
|
|
|
gss_inquire_context(&minor_status,
|
|
gss_ctx,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&ctx_mech,
|
|
&ctx_flags,
|
|
NULL,
|
|
NULL);
|
|
|
|
if (ctx_flags & (GSS_C_CONF_FLAG)) {
|
|
conf_req_flag = 1;
|
|
}
|
|
|
|
#if defined(HAVE_CYRUS_SASL)
|
|
#define SEND_PREALLOC_SIZE SASL_MIN_BUFF_SIZE
|
|
#else
|
|
#define SEND_PREALLOC_SIZE 4096
|
|
#endif
|
|
#define SEND_MAX_WIRE_SIZE 0x00A00000
|
|
#define RECV_MAX_WIRE_SIZE 0x0FFFFFFF
|
|
#define FALLBACK_SEND_MAX_SIZE 0x009FFFB8 /* from MIT 1.5.x */
|
|
|
|
gss_rc = gss_wrap_size_limit(&minor_status, gss_ctx,
|
|
conf_req_flag, GSS_C_QOP_DEFAULT,
|
|
SEND_MAX_WIRE_SIZE, &max_input_size);
|
|
if ( gss_rc != GSS_S_COMPLETE ) {
|
|
char msg[256];
|
|
ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
|
|
"sb_sasl_gssapi_init: failed to wrap size limit: %s\n",
|
|
gsserrstr( msg, sizeof(msg), ctx_mech, gss_rc, minor_status ) );
|
|
ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
|
|
"sb_sasl_gssapi_init: fallback to default wrap size limit\n");
|
|
/*
|
|
* some libgssglue/libgssapi versions
|
|
* have a broken gss_wrap_size_limit()
|
|
* implementation
|
|
*/
|
|
max_input_size = FALLBACK_SEND_MAX_SIZE;
|
|
}
|
|
|
|
*min_send = SEND_PREALLOC_SIZE;
|
|
*max_send = max_input_size;
|
|
*max_recv = RECV_MAX_WIRE_SIZE;
|
|
}
|
|
|
|
static ber_int_t
|
|
sb_sasl_gssapi_encode(
|
|
struct sb_sasl_generic_data *p,
|
|
unsigned char *buf,
|
|
ber_len_t len,
|
|
Sockbuf_Buf *dst )
|
|
{
|
|
gss_ctx_id_t gss_ctx = (gss_ctx_id_t)p->ops_private;
|
|
int gss_rc;
|
|
OM_uint32 minor_status;
|
|
gss_buffer_desc unwrapped, wrapped;
|
|
gss_OID ctx_mech = GSS_C_NO_OID;
|
|
OM_uint32 ctx_flags = 0;
|
|
int conf_req_flag = 0;
|
|
int conf_state;
|
|
unsigned char *b;
|
|
ber_len_t pkt_len;
|
|
|
|
unwrapped.value = buf;
|
|
unwrapped.length = len;
|
|
|
|
gss_inquire_context(&minor_status,
|
|
gss_ctx,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&ctx_mech,
|
|
&ctx_flags,
|
|
NULL,
|
|
NULL);
|
|
|
|
if (ctx_flags & (GSS_C_CONF_FLAG)) {
|
|
conf_req_flag = 1;
|
|
}
|
|
|
|
gss_rc = gss_wrap(&minor_status, gss_ctx,
|
|
conf_req_flag, GSS_C_QOP_DEFAULT,
|
|
&unwrapped, &conf_state,
|
|
&wrapped);
|
|
if ( gss_rc != GSS_S_COMPLETE ) {
|
|
char msg[256];
|
|
ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
|
|
"sb_sasl_gssapi_encode: failed to encode packet: %s\n",
|
|
gsserrstr( msg, sizeof(msg), ctx_mech, gss_rc, minor_status ) );
|
|
return -1;
|
|
}
|
|
|
|
if ( conf_req_flag && conf_state == 0 ) {
|
|
ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
|
|
"sb_sasl_gssapi_encode: GSS_C_CONF_FLAG was ignored by our gss_wrap()\n" );
|
|
return -1;
|
|
}
|
|
|
|
pkt_len = 4 + wrapped.length;
|
|
|
|
/* Grow the packet buffer if necessary */
|
|
if ( dst->buf_size < pkt_len &&
|
|
ber_pvt_sb_grow_buffer( dst, pkt_len ) < 0 )
|
|
{
|
|
ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
|
|
"sb_sasl_gssapi_encode: failed to grow the buffer to %lu bytes\n",
|
|
pkt_len );
|
|
return -1;
|
|
}
|
|
|
|
dst->buf_end = pkt_len;
|
|
|
|
b = (unsigned char *)dst->buf_base;
|
|
|
|
b[0] = (unsigned char)(wrapped.length >> 24);
|
|
b[1] = (unsigned char)(wrapped.length >> 16);
|
|
b[2] = (unsigned char)(wrapped.length >> 8);
|
|
b[3] = (unsigned char)(wrapped.length >> 0);
|
|
|
|
/* copy the wrapped blob to the right location */
|
|
memcpy(b + 4, wrapped.value, wrapped.length);
|
|
|
|
gss_release_buffer(&minor_status, &wrapped);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static ber_int_t
|
|
sb_sasl_gssapi_decode(
|
|
struct sb_sasl_generic_data *p,
|
|
const Sockbuf_Buf *src,
|
|
Sockbuf_Buf *dst )
|
|
{
|
|
gss_ctx_id_t gss_ctx = (gss_ctx_id_t)p->ops_private;
|
|
int gss_rc;
|
|
OM_uint32 minor_status;
|
|
gss_buffer_desc unwrapped, wrapped;
|
|
gss_OID ctx_mech = GSS_C_NO_OID;
|
|
OM_uint32 ctx_flags = 0;
|
|
int conf_req_flag = 0;
|
|
int conf_state;
|
|
unsigned char *b;
|
|
|
|
wrapped.value = src->buf_base + 4;
|
|
wrapped.length = src->buf_end - 4;
|
|
|
|
gss_inquire_context(&minor_status,
|
|
gss_ctx,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&ctx_mech,
|
|
&ctx_flags,
|
|
NULL,
|
|
NULL);
|
|
|
|
if (ctx_flags & (GSS_C_CONF_FLAG)) {
|
|
conf_req_flag = 1;
|
|
}
|
|
|
|
gss_rc = gss_unwrap(&minor_status, gss_ctx,
|
|
&wrapped, &unwrapped,
|
|
&conf_state, GSS_C_QOP_DEFAULT);
|
|
if ( gss_rc != GSS_S_COMPLETE ) {
|
|
char msg[256];
|
|
ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
|
|
"sb_sasl_gssapi_decode: failed to decode packet: %s\n",
|
|
gsserrstr( msg, sizeof(msg), ctx_mech, gss_rc, minor_status ) );
|
|
return -1;
|
|
}
|
|
|
|
if ( conf_req_flag && conf_state == 0 ) {
|
|
ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
|
|
"sb_sasl_gssapi_encode: GSS_C_CONF_FLAG was ignored by our peer\n" );
|
|
return -1;
|
|
}
|
|
|
|
/* Grow the packet buffer if necessary */
|
|
if ( dst->buf_size < unwrapped.length &&
|
|
ber_pvt_sb_grow_buffer( dst, unwrapped.length ) < 0 )
|
|
{
|
|
ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
|
|
"sb_sasl_gssapi_decode: failed to grow the buffer to %lu bytes\n",
|
|
unwrapped.length );
|
|
return -1;
|
|
}
|
|
|
|
dst->buf_end = unwrapped.length;
|
|
|
|
b = (unsigned char *)dst->buf_base;
|
|
|
|
/* copy the wrapped blob to the right location */
|
|
memcpy(b, unwrapped.value, unwrapped.length);
|
|
|
|
gss_release_buffer(&minor_status, &unwrapped);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
sb_sasl_gssapi_reset_buf(
|
|
struct sb_sasl_generic_data *p,
|
|
Sockbuf_Buf *buf )
|
|
{
|
|
ber_pvt_sb_buf_destroy( buf );
|
|
}
|
|
|
|
static void
|
|
sb_sasl_gssapi_fini( struct sb_sasl_generic_data *p )
|
|
{
|
|
}
|
|
|
|
static const struct sb_sasl_generic_ops sb_sasl_gssapi_ops = {
|
|
sb_sasl_gssapi_init,
|
|
sb_sasl_gssapi_encode,
|
|
sb_sasl_gssapi_decode,
|
|
sb_sasl_gssapi_reset_buf,
|
|
sb_sasl_gssapi_fini
|
|
};
|
|
|
|
static int
|
|
sb_sasl_gssapi_install(
|
|
Sockbuf *sb,
|
|
gss_ctx_id_t gss_ctx )
|
|
{
|
|
struct sb_sasl_generic_install install_arg;
|
|
|
|
install_arg.ops = &sb_sasl_gssapi_ops;
|
|
install_arg.ops_private = gss_ctx;
|
|
|
|
return ldap_pvt_sasl_generic_install( sb, &install_arg );
|
|
}
|
|
|
|
static void
|
|
sb_sasl_gssapi_remove( Sockbuf *sb )
|
|
{
|
|
ldap_pvt_sasl_generic_remove( sb );
|
|
}
|
|
|
|
static int
|
|
map_gsserr2ldap(
|
|
LDAP *ld,
|
|
gss_OID mech,
|
|
int gss_rc,
|
|
OM_uint32 minor_status )
|
|
{
|
|
char msg[256];
|
|
|
|
Debug1( LDAP_DEBUG_ANY, "%s\n",
|
|
gsserrstr( msg, sizeof(msg), mech, gss_rc, minor_status ) );
|
|
|
|
if (gss_rc == GSS_S_COMPLETE) {
|
|
ld->ld_errno = LDAP_SUCCESS;
|
|
} else if (GSS_CALLING_ERROR(gss_rc)) {
|
|
ld->ld_errno = LDAP_LOCAL_ERROR;
|
|
} else if (GSS_ROUTINE_ERROR(gss_rc)) {
|
|
ld->ld_errno = LDAP_INAPPROPRIATE_AUTH;
|
|
} else if (gss_rc == GSS_S_CONTINUE_NEEDED) {
|
|
ld->ld_errno = LDAP_SASL_BIND_IN_PROGRESS;
|
|
} else if (GSS_SUPPLEMENTARY_INFO(gss_rc)) {
|
|
ld->ld_errno = LDAP_AUTH_UNKNOWN;
|
|
} else if (GSS_ERROR(gss_rc)) {
|
|
ld->ld_errno = LDAP_AUTH_UNKNOWN;
|
|
} else {
|
|
ld->ld_errno = LDAP_OTHER;
|
|
}
|
|
|
|
return ld->ld_errno;
|
|
}
|
|
|
|
|
|
static int
|
|
ldap_gssapi_get_rootdse_infos (
|
|
LDAP *ld,
|
|
char **pmechlist,
|
|
char **pldapServiceName,
|
|
char **pdnsHostName )
|
|
{
|
|
/* we need to query the server for supported mechs anyway */
|
|
LDAPMessage *res, *e;
|
|
char *attrs[] = {
|
|
"supportedSASLMechanisms",
|
|
"ldapServiceName",
|
|
"dnsHostName",
|
|
NULL
|
|
};
|
|
char **values, *mechlist;
|
|
char *ldapServiceName = NULL;
|
|
char *dnsHostName = NULL;
|
|
int rc;
|
|
|
|
Debug0( LDAP_DEBUG_TRACE, "ldap_gssapi_get_rootdse_infos\n" );
|
|
|
|
rc = ldap_search_s( ld, "", LDAP_SCOPE_BASE,
|
|
NULL, attrs, 0, &res );
|
|
|
|
if ( rc != LDAP_SUCCESS ) {
|
|
return ld->ld_errno;
|
|
}
|
|
|
|
e = ldap_first_entry( ld, res );
|
|
if ( e == NULL ) {
|
|
ldap_msgfree( res );
|
|
if ( ld->ld_errno == LDAP_SUCCESS ) {
|
|
ld->ld_errno = LDAP_NO_SUCH_OBJECT;
|
|
}
|
|
return ld->ld_errno;
|
|
}
|
|
|
|
values = ldap_get_values( ld, e, "supportedSASLMechanisms" );
|
|
if ( values == NULL ) {
|
|
ldap_msgfree( res );
|
|
ld->ld_errno = LDAP_NO_SUCH_ATTRIBUTE;
|
|
return ld->ld_errno;
|
|
}
|
|
|
|
mechlist = ldap_charray2str( values, " " );
|
|
if ( mechlist == NULL ) {
|
|
LDAP_VFREE( values );
|
|
ldap_msgfree( res );
|
|
ld->ld_errno = LDAP_NO_MEMORY;
|
|
return ld->ld_errno;
|
|
}
|
|
|
|
LDAP_VFREE( values );
|
|
|
|
values = ldap_get_values( ld, e, "ldapServiceName" );
|
|
if ( values == NULL ) {
|
|
goto get_dns_host_name;
|
|
}
|
|
|
|
ldapServiceName = ldap_charray2str( values, " " );
|
|
if ( ldapServiceName == NULL ) {
|
|
LDAP_FREE( mechlist );
|
|
LDAP_VFREE( values );
|
|
ldap_msgfree( res );
|
|
ld->ld_errno = LDAP_NO_MEMORY;
|
|
return ld->ld_errno;
|
|
}
|
|
LDAP_VFREE( values );
|
|
|
|
get_dns_host_name:
|
|
|
|
values = ldap_get_values( ld, e, "dnsHostName" );
|
|
if ( values == NULL ) {
|
|
goto done;
|
|
}
|
|
|
|
dnsHostName = ldap_charray2str( values, " " );
|
|
if ( dnsHostName == NULL ) {
|
|
LDAP_FREE( mechlist );
|
|
LDAP_FREE( ldapServiceName );
|
|
LDAP_VFREE( values );
|
|
ldap_msgfree( res );
|
|
ld->ld_errno = LDAP_NO_MEMORY;
|
|
return ld->ld_errno;
|
|
}
|
|
LDAP_VFREE( values );
|
|
|
|
done:
|
|
ldap_msgfree( res );
|
|
|
|
*pmechlist = mechlist;
|
|
*pldapServiceName = ldapServiceName;
|
|
*pdnsHostName = dnsHostName;
|
|
|
|
return LDAP_SUCCESS;
|
|
}
|
|
|
|
|
|
static int check_for_gss_spnego_support( LDAP *ld, const char *mechs_str )
|
|
{
|
|
int rc;
|
|
char **mechs_list = NULL;
|
|
|
|
mechs_list = ldap_str2charray( mechs_str, " " );
|
|
if ( mechs_list == NULL ) {
|
|
ld->ld_errno = LDAP_NO_MEMORY;
|
|
return ld->ld_errno;
|
|
}
|
|
|
|
rc = ldap_charray_inlist( mechs_list, "GSS-SPNEGO" );
|
|
ldap_charray_free( mechs_list );
|
|
if ( rc != 1) {
|
|
ld->ld_errno = LDAP_STRONG_AUTH_NOT_SUPPORTED;
|
|
return ld->ld_errno;
|
|
}
|
|
|
|
return LDAP_SUCCESS;
|
|
}
|
|
|
|
static int
|
|
guess_service_principal(
|
|
LDAP *ld,
|
|
const char *ldapServiceName,
|
|
const char *dnsHostName,
|
|
gss_name_t *principal )
|
|
{
|
|
gss_buffer_desc input_name;
|
|
/* GSS_KRB5_NT_PRINCIPAL_NAME */
|
|
gss_OID_desc nt_principal =
|
|
{10, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x01"};
|
|
const char *host = ld->ld_defconn->lconn_server->lud_host;
|
|
OM_uint32 minor_status;
|
|
int gss_rc;
|
|
int ret;
|
|
size_t svc_principal_size;
|
|
char *svc_principal = NULL;
|
|
const char *principal_fmt = NULL;
|
|
const char *str = NULL;
|
|
const char *givenstr = NULL;
|
|
const char *ignore = "not_defined_in_RFC4178@please_ignore";
|
|
int allow_remote = 0;
|
|
|
|
if (ldapServiceName) {
|
|
givenstr = strchr(ldapServiceName, ':');
|
|
if (givenstr && givenstr[1]) {
|
|
givenstr++;
|
|
if (strcmp(givenstr, ignore) == 0) {
|
|
givenstr = NULL;
|
|
}
|
|
} else {
|
|
givenstr = NULL;
|
|
}
|
|
}
|
|
|
|
if ( ld->ld_options.ldo_gssapi_options & LDAP_GSSAPI_OPT_ALLOW_REMOTE_PRINCIPAL ) {
|
|
allow_remote = 1;
|
|
}
|
|
|
|
if (allow_remote && givenstr) {
|
|
principal_fmt = "%s";
|
|
svc_principal_size = strlen(givenstr) + 1;
|
|
str = givenstr;
|
|
|
|
} else if (allow_remote && dnsHostName) {
|
|
principal_fmt = "ldap/%s";
|
|
svc_principal_size = STRLENOF("ldap/") + strlen(dnsHostName) + 1;
|
|
str = dnsHostName;
|
|
|
|
} else {
|
|
principal_fmt = "ldap/%s";
|
|
svc_principal_size = STRLENOF("ldap/") + strlen(host) + 1;
|
|
str = host;
|
|
}
|
|
|
|
svc_principal = (char*) ldap_memalloc(svc_principal_size * sizeof(char));
|
|
if ( svc_principal == NULL ) {
|
|
ld->ld_errno = LDAP_NO_MEMORY;
|
|
return ld->ld_errno;
|
|
}
|
|
|
|
ret = snprintf( svc_principal, svc_principal_size, principal_fmt, str );
|
|
if (ret < 0 || (size_t)ret >= svc_principal_size) {
|
|
ld->ld_errno = LDAP_LOCAL_ERROR;
|
|
return ld->ld_errno;
|
|
}
|
|
|
|
Debug2( LDAP_DEBUG_TRACE, "principal for host[%s]: '%s'\n",
|
|
host, svc_principal );
|
|
|
|
input_name.value = svc_principal;
|
|
input_name.length = (size_t)ret;
|
|
|
|
gss_rc = gss_import_name( &minor_status, &input_name, &nt_principal, principal );
|
|
ldap_memfree( svc_principal );
|
|
if ( gss_rc != GSS_S_COMPLETE ) {
|
|
return map_gsserr2ldap( ld, GSS_C_NO_OID, gss_rc, minor_status );
|
|
}
|
|
|
|
return LDAP_SUCCESS;
|
|
}
|
|
|
|
void ldap_int_gssapi_close( LDAP *ld, LDAPConn *lc )
|
|
{
|
|
if ( lc && lc->lconn_gss_ctx ) {
|
|
OM_uint32 minor_status;
|
|
OM_uint32 ctx_flags = 0;
|
|
gss_ctx_id_t old_gss_ctx = GSS_C_NO_CONTEXT;
|
|
old_gss_ctx = (gss_ctx_id_t)lc->lconn_gss_ctx;
|
|
|
|
gss_inquire_context(&minor_status,
|
|
old_gss_ctx,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&ctx_flags,
|
|
NULL,
|
|
NULL);
|
|
|
|
if (!( ld->ld_options.ldo_gssapi_options & LDAP_GSSAPI_OPT_DO_NOT_FREE_GSS_CONTEXT )) {
|
|
gss_delete_sec_context( &minor_status, &old_gss_ctx, GSS_C_NO_BUFFER );
|
|
}
|
|
lc->lconn_gss_ctx = GSS_C_NO_CONTEXT;
|
|
|
|
if (ctx_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG)) {
|
|
/* remove wrapping layer */
|
|
sb_sasl_gssapi_remove( lc->lconn_sb );
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
ldap_int_gssapi_setup(
|
|
LDAP *ld,
|
|
LDAPConn *lc,
|
|
gss_ctx_id_t gss_ctx)
|
|
{
|
|
OM_uint32 minor_status;
|
|
OM_uint32 ctx_flags = 0;
|
|
|
|
ldap_int_gssapi_close( ld, lc );
|
|
|
|
gss_inquire_context(&minor_status,
|
|
gss_ctx,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&ctx_flags,
|
|
NULL,
|
|
NULL);
|
|
|
|
lc->lconn_gss_ctx = gss_ctx;
|
|
|
|
if (ctx_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG)) {
|
|
/* setup wrapping layer */
|
|
sb_sasl_gssapi_install( lc->lconn_sb, gss_ctx );
|
|
}
|
|
}
|
|
|
|
#ifdef LDAP_R_COMPILE
|
|
ldap_pvt_thread_mutex_t ldap_int_gssapi_mutex;
|
|
#endif
|
|
|
|
static int
|
|
ldap_int_gss_spnego_bind_s( LDAP *ld )
|
|
{
|
|
int rc;
|
|
int gss_rc;
|
|
OM_uint32 minor_status;
|
|
char *mechlist = NULL;
|
|
char *ldapServiceName = NULL;
|
|
char *dnsHostName = NULL;
|
|
gss_OID_set supported_mechs = GSS_C_NO_OID_SET;
|
|
int spnego_support = 0;
|
|
#define __SPNEGO_OID_LENGTH 6
|
|
#define __SPNEGO_OID "\053\006\001\005\005\002"
|
|
gss_OID_desc spnego_oid = {__SPNEGO_OID_LENGTH, __SPNEGO_OID};
|
|
gss_OID req_mech = GSS_C_NO_OID;
|
|
gss_OID ret_mech = GSS_C_NO_OID;
|
|
gss_ctx_id_t gss_ctx = GSS_C_NO_CONTEXT;
|
|
gss_name_t principal = GSS_C_NO_NAME;
|
|
OM_uint32 req_flags;
|
|
OM_uint32 ret_flags;
|
|
gss_buffer_desc input_token, output_token = GSS_C_EMPTY_BUFFER;
|
|
struct berval cred, *scred = NULL;
|
|
|
|
LDAP_MUTEX_LOCK( &ldap_int_gssapi_mutex );
|
|
|
|
/* get information from RootDSE entry */
|
|
rc = ldap_gssapi_get_rootdse_infos ( ld, &mechlist,
|
|
&ldapServiceName, &dnsHostName);
|
|
if ( rc != LDAP_SUCCESS ) {
|
|
return rc;
|
|
}
|
|
|
|
/* check that the server supports GSS-SPNEGO */
|
|
rc = check_for_gss_spnego_support( ld, mechlist );
|
|
if ( rc != LDAP_SUCCESS ) {
|
|
goto rc_error;
|
|
}
|
|
|
|
/* prepare new gss_ctx_id_t */
|
|
rc = guess_service_principal( ld, ldapServiceName, dnsHostName, &principal );
|
|
if ( rc != LDAP_SUCCESS ) {
|
|
goto rc_error;
|
|
}
|
|
|
|
/* see if our gssapi library supports spnego */
|
|
gss_rc = gss_indicate_mechs( &minor_status, &supported_mechs );
|
|
if ( gss_rc != GSS_S_COMPLETE ) {
|
|
goto gss_error;
|
|
}
|
|
gss_rc = gss_test_oid_set_member( &minor_status,
|
|
&spnego_oid, supported_mechs, &spnego_support);
|
|
gss_release_oid_set( &minor_status, &supported_mechs);
|
|
if ( gss_rc != GSS_S_COMPLETE ) {
|
|
goto gss_error;
|
|
}
|
|
if ( spnego_support != 0 ) {
|
|
req_mech = &spnego_oid;
|
|
}
|
|
|
|
req_flags = ld->ld_options.ldo_gssapi_flags;
|
|
req_flags |= GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG;
|
|
|
|
/*
|
|
* loop around gss_init_sec_context() and ldap_sasl_bind_s()
|
|
*/
|
|
input_token.value = NULL;
|
|
input_token.length = 0;
|
|
gss_rc = gss_init_sec_context(&minor_status,
|
|
GSS_C_NO_CREDENTIAL,
|
|
&gss_ctx,
|
|
principal,
|
|
req_mech,
|
|
req_flags,
|
|
0,
|
|
NULL,
|
|
&input_token,
|
|
&ret_mech,
|
|
&output_token,
|
|
&ret_flags,
|
|
NULL);
|
|
if ( gss_rc == GSS_S_COMPLETE ) {
|
|
rc = LDAP_INAPPROPRIATE_AUTH;
|
|
goto rc_error;
|
|
}
|
|
if ( gss_rc != GSS_S_CONTINUE_NEEDED ) {
|
|
goto gss_error;
|
|
}
|
|
while (1) {
|
|
cred.bv_val = (char *)output_token.value;
|
|
cred.bv_len = output_token.length;
|
|
rc = ldap_sasl_bind_s( ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred );
|
|
gss_release_buffer( &minor_status, &output_token );
|
|
if ( rc != LDAP_SUCCESS && rc != LDAP_SASL_BIND_IN_PROGRESS ) {
|
|
goto rc_error;
|
|
}
|
|
|
|
if ( scred ) {
|
|
input_token.value = scred->bv_val;
|
|
input_token.length = scred->bv_len;
|
|
} else {
|
|
input_token.value = NULL;
|
|
input_token.length = 0;
|
|
}
|
|
|
|
gss_rc = gss_init_sec_context(&minor_status,
|
|
GSS_C_NO_CREDENTIAL,
|
|
&gss_ctx,
|
|
principal,
|
|
req_mech,
|
|
req_flags,
|
|
0,
|
|
NULL,
|
|
&input_token,
|
|
&ret_mech,
|
|
&output_token,
|
|
&ret_flags,
|
|
NULL);
|
|
if ( scred ) {
|
|
ber_bvfree( scred );
|
|
}
|
|
if ( gss_rc == GSS_S_COMPLETE ) {
|
|
gss_release_buffer( &minor_status, &output_token );
|
|
break;
|
|
}
|
|
|
|
if ( gss_rc != GSS_S_CONTINUE_NEEDED ) {
|
|
goto gss_error;
|
|
}
|
|
}
|
|
|
|
ldap_int_gssapi_setup( ld, ld->ld_defconn, gss_ctx);
|
|
gss_ctx = GSS_C_NO_CONTEXT;
|
|
|
|
rc = LDAP_SUCCESS;
|
|
goto rc_error;
|
|
|
|
gss_error:
|
|
rc = map_gsserr2ldap( ld,
|
|
(ret_mech != GSS_C_NO_OID ? ret_mech : req_mech ),
|
|
gss_rc, minor_status );
|
|
rc_error:
|
|
LDAP_MUTEX_UNLOCK( &ldap_int_gssapi_mutex );
|
|
LDAP_FREE( mechlist );
|
|
LDAP_FREE( ldapServiceName );
|
|
LDAP_FREE( dnsHostName );
|
|
gss_release_buffer( &minor_status, &output_token );
|
|
if ( gss_ctx != GSS_C_NO_CONTEXT ) {
|
|
gss_delete_sec_context( &minor_status, &gss_ctx, GSS_C_NO_BUFFER );
|
|
}
|
|
if ( principal != GSS_C_NO_NAME ) {
|
|
gss_release_name( &minor_status, &principal );
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
ldap_int_gssapi_config( struct ldapoptions *lo, int option, const char *arg )
|
|
{
|
|
int ok = 0;
|
|
|
|
switch( option ) {
|
|
case LDAP_OPT_SIGN:
|
|
|
|
if (!arg) {
|
|
} else if (strcasecmp(arg, "on") == 0) {
|
|
ok = 1;
|
|
} else if (strcasecmp(arg, "yes") == 0) {
|
|
ok = 1;
|
|
} else if (strcasecmp(arg, "true") == 0) {
|
|
ok = 1;
|
|
|
|
}
|
|
if (ok) {
|
|
lo->ldo_gssapi_flags |= GSS_C_INTEG_FLAG;
|
|
}
|
|
|
|
return 0;
|
|
|
|
case LDAP_OPT_ENCRYPT:
|
|
|
|
if (!arg) {
|
|
} else if (strcasecmp(arg, "on") == 0) {
|
|
ok = 1;
|
|
} else if (strcasecmp(arg, "yes") == 0) {
|
|
ok = 1;
|
|
} else if (strcasecmp(arg, "true") == 0) {
|
|
ok = 1;
|
|
}
|
|
|
|
if (ok) {
|
|
lo->ldo_gssapi_flags |= GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG;
|
|
}
|
|
|
|
return 0;
|
|
|
|
case LDAP_OPT_X_GSSAPI_ALLOW_REMOTE_PRINCIPAL:
|
|
|
|
if (!arg) {
|
|
} else if (strcasecmp(arg, "on") == 0) {
|
|
ok = 1;
|
|
} else if (strcasecmp(arg, "yes") == 0) {
|
|
ok = 1;
|
|
} else if (strcasecmp(arg, "true") == 0) {
|
|
ok = 1;
|
|
}
|
|
|
|
if (ok) {
|
|
lo->ldo_gssapi_options |= LDAP_GSSAPI_OPT_ALLOW_REMOTE_PRINCIPAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
ldap_int_gssapi_get_option( LDAP *ld, int option, void *arg )
|
|
{
|
|
if ( ld == NULL )
|
|
return -1;
|
|
|
|
switch ( option ) {
|
|
case LDAP_OPT_SSPI_FLAGS:
|
|
* (unsigned *) arg = (unsigned) ld->ld_options.ldo_gssapi_flags;
|
|
break;
|
|
|
|
case LDAP_OPT_SIGN:
|
|
if ( ld->ld_options.ldo_gssapi_flags & GSS_C_INTEG_FLAG ) {
|
|
* (int *) arg = (int)-1;
|
|
} else {
|
|
* (int *) arg = (int)0;
|
|
}
|
|
break;
|
|
|
|
case LDAP_OPT_ENCRYPT:
|
|
if ( ld->ld_options.ldo_gssapi_flags & GSS_C_CONF_FLAG ) {
|
|
* (int *) arg = (int)-1;
|
|
} else {
|
|
* (int *) arg = (int)0;
|
|
}
|
|
break;
|
|
|
|
case LDAP_OPT_SASL_METHOD:
|
|
* (char **) arg = LDAP_STRDUP("GSS-SPNEGO");
|
|
break;
|
|
|
|
case LDAP_OPT_SECURITY_CONTEXT:
|
|
if ( ld->ld_defconn && ld->ld_defconn->lconn_gss_ctx ) {
|
|
* (gss_ctx_id_t *) arg = (gss_ctx_id_t)ld->ld_defconn->lconn_gss_ctx;
|
|
} else {
|
|
* (gss_ctx_id_t *) arg = GSS_C_NO_CONTEXT;
|
|
}
|
|
break;
|
|
|
|
case LDAP_OPT_X_GSSAPI_DO_NOT_FREE_CONTEXT:
|
|
if ( ld->ld_options.ldo_gssapi_options & LDAP_GSSAPI_OPT_DO_NOT_FREE_GSS_CONTEXT ) {
|
|
* (int *) arg = (int)-1;
|
|
} else {
|
|
* (int *) arg = (int)0;
|
|
}
|
|
break;
|
|
|
|
case LDAP_OPT_X_GSSAPI_ALLOW_REMOTE_PRINCIPAL:
|
|
if ( ld->ld_options.ldo_gssapi_options & LDAP_GSSAPI_OPT_ALLOW_REMOTE_PRINCIPAL ) {
|
|
* (int *) arg = (int)-1;
|
|
} else {
|
|
* (int *) arg = (int)0;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
ldap_int_gssapi_set_option( LDAP *ld, int option, void *arg )
|
|
{
|
|
if ( ld == NULL )
|
|
return -1;
|
|
|
|
switch ( option ) {
|
|
case LDAP_OPT_SSPI_FLAGS:
|
|
if ( arg != LDAP_OPT_OFF ) {
|
|
ld->ld_options.ldo_gssapi_flags = * (unsigned *)arg;
|
|
}
|
|
break;
|
|
|
|
case LDAP_OPT_SIGN:
|
|
if ( arg != LDAP_OPT_OFF ) {
|
|
ld->ld_options.ldo_gssapi_flags |= GSS_C_INTEG_FLAG;
|
|
}
|
|
break;
|
|
|
|
case LDAP_OPT_ENCRYPT:
|
|
if ( arg != LDAP_OPT_OFF ) {
|
|
ld->ld_options.ldo_gssapi_flags |= GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG;
|
|
}
|
|
break;
|
|
|
|
case LDAP_OPT_SASL_METHOD:
|
|
if ( arg != LDAP_OPT_OFF ) {
|
|
const char *m = (const char *)arg;
|
|
if ( strcmp( "GSS-SPNEGO", m ) != 0 ) {
|
|
/* we currently only support GSS-SPNEGO */
|
|
return -1;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case LDAP_OPT_SECURITY_CONTEXT:
|
|
if ( arg != LDAP_OPT_OFF && ld->ld_defconn) {
|
|
ldap_int_gssapi_setup( ld, ld->ld_defconn,
|
|
(gss_ctx_id_t) arg);
|
|
}
|
|
break;
|
|
|
|
case LDAP_OPT_X_GSSAPI_DO_NOT_FREE_CONTEXT:
|
|
if ( arg != LDAP_OPT_OFF ) {
|
|
ld->ld_options.ldo_gssapi_options |= LDAP_GSSAPI_OPT_DO_NOT_FREE_GSS_CONTEXT;
|
|
}
|
|
break;
|
|
|
|
case LDAP_OPT_X_GSSAPI_ALLOW_REMOTE_PRINCIPAL:
|
|
if ( arg != LDAP_OPT_OFF ) {
|
|
ld->ld_options.ldo_gssapi_options |= LDAP_GSSAPI_OPT_ALLOW_REMOTE_PRINCIPAL;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#else /* HAVE_GSSAPI */
|
|
#define ldap_int_gss_spnego_bind_s(ld) LDAP_NOT_SUPPORTED
|
|
#endif /* HAVE_GSSAPI */
|
|
|
|
int
|
|
ldap_gssapi_bind(
|
|
LDAP *ld,
|
|
LDAP_CONST char *dn,
|
|
LDAP_CONST char *creds )
|
|
{
|
|
return LDAP_NOT_SUPPORTED;
|
|
}
|
|
|
|
int
|
|
ldap_gssapi_bind_s(
|
|
LDAP *ld,
|
|
LDAP_CONST char *dn,
|
|
LDAP_CONST char *creds )
|
|
{
|
|
if ( dn != NULL ) {
|
|
return LDAP_NOT_SUPPORTED;
|
|
}
|
|
|
|
if ( creds != NULL ) {
|
|
return LDAP_NOT_SUPPORTED;
|
|
}
|
|
|
|
return ldap_int_gss_spnego_bind_s(ld);
|
|
}
|