From 4294664aad69d9bdddaadc69148550c4f2ec532d Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Thu, 9 Oct 2008 11:10:28 +0000 Subject: [PATCH] ITS#5369 SASL/GSSAPi refactoring from Stefan Metzmacher and Rafal Szczeniak , with minor cleanups --- libraries/libldap/gssapi.c | 1016 ++++++++++++++++++++++++++++++++++++ 1 file changed, 1016 insertions(+) create mode 100644 libraries/libldap/gssapi.c diff --git a/libraries/libldap/gssapi.c b/libraries/libldap/gssapi.c new file mode 100644 index 0000000000..0edf6a53b2 --- /dev/null +++ b/libraries/libldap/gssapi.c @@ -0,0 +1,1016 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * Copyright 1998-2007 The OpenLDAP Foundation. + * All rights reserved. + * + * Author: Stefan Metzmacher + * + * 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 + * . + */ + +#include "portable.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_LIMITS_H +#include +#endif + +#include "ldap-int.h" + +#ifdef HAVE_GSSAPI + +#ifdef HAVE_GSSAPI_GSSAPI_H +#include +#else +#include +#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 neccessary */ + 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; + ber_len_t pkt_len; + + 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 neccessary */ + 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", + pkt_len ); + 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 ) +{ + OM_uint32 min2; + OM_uint32 msg_ctx = 0; + char msg[256]; + + Debug( LDAP_DEBUG_ANY, "%s\n", + gsserrstr( msg, sizeof(msg), mech, gss_rc, minor_status ), + NULL, NULL ); + + 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; + + Debug( LDAP_DEBUG_TRACE, "ldap_gssapi_get_rootdse_infos\n", 0, 0, 0 ); + + 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 = strlen(dnsHostName) + strlen(principal_fmt); + str = dnsHostName; + + } else { + principal_fmt = "ldap/%s"; + svc_principal_size = strlen(host) + strlen(principal_fmt); + str = host; + } + + svc_principal = (char*) ldap_memalloc(svc_principal_size * sizeof(char)); + if ( ret < 0 ) { + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + + ret = snprintf( svc_principal, svc_principal_size - 1, principal_fmt, str); + if (ret < 0 || ret >= svc_principal_size - 1) { + ld->ld_errno = LDAP_LOCAL_ERROR; + return ld->ld_errno; + } + + Debug( LDAP_DEBUG_TRACE, "principal for host[%s]: '%s'\n", + host, svc_principal, 0 ); + + input_name.value = svc_principal; + input_name.length = strlen( svc_principal ); + + 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; + +#ifdef LDAP_R_COMPILE + ldap_pvt_thread_mutex_lock( &ldap_int_gssapi_mutex ); +#endif + + /* 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.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: +#ifdef LDAP_R_COMPILE + ldap_pvt_thread_mutex_unlock( &ldap_int_gssapi_mutex ); +#endif + 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.gssapi_flags; + break; + + case LDAP_OPT_SIGN: + if ( ld->ld_options.gssapi_flags & GSS_C_INTEG_FLAG ) { + * (int *) arg = (int)-1; + } else { + * (int *) arg = (int)0; + } + break; + + case LDAP_OPT_ENCRYPT: + if ( ld->ld_options.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.gssapi_flags = * (unsigned *)arg; + } + break; + + case LDAP_OPT_SIGN: + if ( arg != LDAP_OPT_OFF ) { + ld->ld_options.gssapi_flags |= GSS_C_INTEG_FLAG; + } + break; + + case LDAP_OPT_ENCRYPT: + if ( arg != LDAP_OPT_OFF ) { + ld->ld_options.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); +} -- 2.39.5