Home | History | Annotate | Line # | Download | only in libldap
      1 /*	$NetBSD: gssapi.c,v 1.5 2021/08/19 12:13:37 christos Exp $	*/
      2 
      3 /* $OpenLDAP$ */
      4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
      5  *
      6  * Copyright 1998-2020 The OpenLDAP Foundation.
      7  * All rights reserved.
      8  *
      9  * Author: Stefan Metzmacher <metze (at) sernet.de>
     10  *
     11  * Redistribution and use in source and binary forms, with or without
     12  * modification, are permitted only as authorized by the OpenLDAP
     13  * Public License.
     14  *
     15  * A copy of this license is available in the file LICENSE in the
     16  * top-level directory of the distribution or, alternatively, at
     17  * <http://www.OpenLDAP.org/license.html>.
     18  */
     19 
     20 #include <sys/cdefs.h>
     21 __RCSID("$NetBSD: gssapi.c,v 1.5 2021/08/19 12:13:37 christos Exp $");
     22 
     23 #include "portable.h"
     24 
     25 #include <stdio.h>
     26 
     27 #include <ac/socket.h>
     28 #include <ac/stdlib.h>
     29 #include <ac/string.h>
     30 #include <ac/time.h>
     31 #include <ac/errno.h>
     32 #include <ac/ctype.h>
     33 #include <ac/unistd.h>
     34 
     35 #ifdef HAVE_LIMITS_H
     36 #include <limits.h>
     37 #endif
     38 
     39 #include "ldap-int.h"
     40 
     41 #ifdef HAVE_GSSAPI
     42 
     43 #ifdef HAVE_GSSAPI_GSSAPI_H
     44 #include <gssapi/gssapi.h>
     45 #else
     46 #include <gssapi.h>
     47 #endif
     48 
     49 static char *
     50 gsserrstr(
     51 	char *buf,
     52 	ber_len_t buf_len,
     53 	gss_OID mech,
     54 	int gss_rc,
     55 	OM_uint32 minor_status )
     56 {
     57 	OM_uint32 min2;
     58 	gss_buffer_desc mech_msg = GSS_C_EMPTY_BUFFER;
     59 	gss_buffer_desc gss_msg = GSS_C_EMPTY_BUFFER;
     60 	gss_buffer_desc minor_msg = GSS_C_EMPTY_BUFFER;
     61 	OM_uint32 msg_ctx = 0;
     62 
     63 	if (buf == NULL) {
     64 		return NULL;
     65 	}
     66 
     67 	if (buf_len == 0) {
     68 		return NULL;
     69 	}
     70 
     71 #ifdef HAVE_GSS_OID_TO_STR
     72 	gss_oid_to_str(&min2, mech, &mech_msg);
     73 #endif
     74 	gss_display_status(&min2, gss_rc, GSS_C_GSS_CODE,
     75 			   mech, &msg_ctx, &gss_msg);
     76 	gss_display_status(&min2, minor_status, GSS_C_MECH_CODE,
     77 			   mech, &msg_ctx, &minor_msg);
     78 
     79 	snprintf(buf, buf_len, "gss_rc[%d:%*s] mech[%*s] minor[%u:%*s]",
     80 		 gss_rc, (int)gss_msg.length,
     81 		 (const char *)(gss_msg.value?gss_msg.value:""),
     82 		 (int)mech_msg.length,
     83 		 (const char *)(mech_msg.value?mech_msg.value:""),
     84 		 minor_status, (int)minor_msg.length,
     85 		 (const char *)(minor_msg.value?minor_msg.value:""));
     86 
     87 	gss_release_buffer(&min2, &mech_msg);
     88 	gss_release_buffer(&min2, &gss_msg);
     89 	gss_release_buffer(&min2, &minor_msg);
     90 
     91 	buf[buf_len-1] = '\0';
     92 
     93 	return buf;
     94 }
     95 
     96 static void
     97 sb_sasl_gssapi_init(
     98 	struct sb_sasl_generic_data *p,
     99 	ber_len_t *min_send,
    100 	ber_len_t *max_send,
    101 	ber_len_t *max_recv )
    102 {
    103 	gss_ctx_id_t gss_ctx = (gss_ctx_id_t)p->ops_private;
    104 	int gss_rc;
    105 	OM_uint32 minor_status;
    106 	gss_OID ctx_mech = GSS_C_NO_OID;
    107 	OM_uint32 ctx_flags = 0;
    108 	int conf_req_flag = 0;
    109 	OM_uint32 max_input_size;
    110 
    111 	gss_inquire_context(&minor_status,
    112 			    gss_ctx,
    113 			    NULL,
    114 			    NULL,
    115 			    NULL,
    116 			    &ctx_mech,
    117 			    &ctx_flags,
    118 			    NULL,
    119 			    NULL);
    120 
    121 	if (ctx_flags & (GSS_C_CONF_FLAG)) {
    122 		conf_req_flag = 1;
    123 	}
    124 
    125 #if defined(HAVE_CYRUS_SASL)
    126 #define SEND_PREALLOC_SIZE	SASL_MIN_BUFF_SIZE
    127 #else
    128 #define SEND_PREALLOC_SIZE      4096
    129 #endif
    130 #define SEND_MAX_WIRE_SIZE	0x00A00000
    131 #define RECV_MAX_WIRE_SIZE	0x0FFFFFFF
    132 #define FALLBACK_SEND_MAX_SIZE	0x009FFFB8 /* from MIT 1.5.x */
    133 
    134 	gss_rc = gss_wrap_size_limit(&minor_status, gss_ctx,
    135 				     conf_req_flag, GSS_C_QOP_DEFAULT,
    136 				     SEND_MAX_WIRE_SIZE, &max_input_size);
    137 	if ( gss_rc != GSS_S_COMPLETE ) {
    138 		char msg[256];
    139 		ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
    140 				"%s: failed to wrap size limit: %s\n", __func__,
    141 				gsserrstr( msg, sizeof(msg), ctx_mech, gss_rc, minor_status ) );
    142 		ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
    143 				"%s: fallback to default wrap size limit\n",
    144 				__func__);
    145 		/*
    146 		 * some libgssglue/libgssapi versions
    147 		 * have a broken gss_wrap_size_limit()
    148 		 * implementation
    149 		 */
    150 		max_input_size = FALLBACK_SEND_MAX_SIZE;
    151 	}
    152 
    153 	*min_send = SEND_PREALLOC_SIZE;
    154 	*max_send = max_input_size;
    155 	*max_recv = RECV_MAX_WIRE_SIZE;
    156 }
    157 
    158 static ber_int_t
    159 sb_sasl_gssapi_encode(
    160 	struct sb_sasl_generic_data *p,
    161 	unsigned char *buf,
    162 	ber_len_t len,
    163 	Sockbuf_Buf *dst )
    164 {
    165 	gss_ctx_id_t gss_ctx = (gss_ctx_id_t)p->ops_private;
    166 	int gss_rc;
    167 	OM_uint32 minor_status;
    168 	gss_buffer_desc unwrapped, wrapped;
    169 	gss_OID ctx_mech = GSS_C_NO_OID;
    170 	OM_uint32 ctx_flags = 0;
    171 	int conf_req_flag = 0;
    172 	int conf_state;
    173 	unsigned char *b;
    174 	ber_len_t pkt_len;
    175 
    176 	unwrapped.value		= buf;
    177 	unwrapped.length	= len;
    178 
    179 	gss_inquire_context(&minor_status,
    180 			    gss_ctx,
    181 			    NULL,
    182 			    NULL,
    183 			    NULL,
    184 			    &ctx_mech,
    185 			    &ctx_flags,
    186 			    NULL,
    187 			    NULL);
    188 
    189 	if (ctx_flags & (GSS_C_CONF_FLAG)) {
    190 		conf_req_flag = 1;
    191 	}
    192 
    193 	gss_rc = gss_wrap(&minor_status, gss_ctx,
    194 			  conf_req_flag, GSS_C_QOP_DEFAULT,
    195 			  &unwrapped, &conf_state,
    196 			  &wrapped);
    197 	if ( gss_rc != GSS_S_COMPLETE ) {
    198 		char msg[256];
    199 		ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
    200 				"%s: failed to encode packet: %s\n", __func__,
    201 				gsserrstr( msg, sizeof(msg), ctx_mech, gss_rc, minor_status ) );
    202 		return -1;
    203 	}
    204 
    205 	if ( conf_req_flag && conf_state == 0 ) {
    206 		ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
    207 				"%s: GSS_C_CONF_FLAG was ignored by our gss_wrap()\n",
    208 				__func__);
    209 		return -1;
    210 	}
    211 
    212 	pkt_len = 4 + wrapped.length;
    213 
    214 	/* Grow the packet buffer if neccessary */
    215 	if ( dst->buf_size < pkt_len &&
    216 		ber_pvt_sb_grow_buffer( dst, pkt_len ) < 0 )
    217 	{
    218 		ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
    219 				"%s: failed to grow the buffer to %lu bytes\n",
    220 				__func__, pkt_len );
    221 		return -1;
    222 	}
    223 
    224 	dst->buf_end = pkt_len;
    225 
    226 	b = (unsigned char *)dst->buf_base;
    227 
    228 	b[0] = (unsigned char)(wrapped.length >> 24);
    229 	b[1] = (unsigned char)(wrapped.length >> 16);
    230 	b[2] = (unsigned char)(wrapped.length >>  8);
    231 	b[3] = (unsigned char)(wrapped.length >>  0);
    232 
    233 	/* copy the wrapped blob to the right location */
    234 	memcpy(b + 4, wrapped.value, wrapped.length);
    235 
    236 	gss_release_buffer(&minor_status, &wrapped);
    237 
    238 	return 0;
    239 }
    240 
    241 static ber_int_t
    242 sb_sasl_gssapi_decode(
    243 	struct sb_sasl_generic_data *p,
    244 	const Sockbuf_Buf *src,
    245 	Sockbuf_Buf *dst )
    246 {
    247 	gss_ctx_id_t gss_ctx = (gss_ctx_id_t)p->ops_private;
    248 	int gss_rc;
    249 	OM_uint32 minor_status;
    250 	gss_buffer_desc unwrapped, wrapped;
    251 	gss_OID ctx_mech = GSS_C_NO_OID;
    252 	OM_uint32 ctx_flags = 0;
    253 	int conf_req_flag = 0;
    254 	int conf_state;
    255 	unsigned char *b;
    256 
    257 	wrapped.value	= src->buf_base + 4;
    258 	wrapped.length	= src->buf_end - 4;
    259 
    260 	gss_inquire_context(&minor_status,
    261 			    gss_ctx,
    262 			    NULL,
    263 			    NULL,
    264 			    NULL,
    265 			    &ctx_mech,
    266 			    &ctx_flags,
    267 			    NULL,
    268 			    NULL);
    269 
    270 	if (ctx_flags & (GSS_C_CONF_FLAG)) {
    271 		conf_req_flag = 1;
    272 	}
    273 
    274 	gss_rc = gss_unwrap(&minor_status, gss_ctx,
    275 			    &wrapped, &unwrapped,
    276 			    &conf_state, GSS_C_QOP_DEFAULT);
    277 	if ( gss_rc != GSS_S_COMPLETE ) {
    278 		char msg[256];
    279 		ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
    280 				"%s: failed to decode packet: %s\n", __func__,
    281 				gsserrstr( msg, sizeof(msg), ctx_mech, gss_rc, minor_status ) );
    282 		return -1;
    283 	}
    284 
    285 	if ( conf_req_flag && conf_state == 0 ) {
    286 		ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
    287 				"%s: GSS_C_CONF_FLAG was ignored by our peer\n",
    288 				__func__);
    289 		return -1;
    290 	}
    291 
    292 	/* Grow the packet buffer if neccessary */
    293 	if ( dst->buf_size < unwrapped.length &&
    294 		ber_pvt_sb_grow_buffer( dst, unwrapped.length ) < 0 )
    295 	{
    296 		ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
    297 				"%s: failed to grow the buffer to %zu bytes\n",
    298 				__func__, unwrapped.length );
    299 		return -1;
    300 	}
    301 
    302 	dst->buf_end = unwrapped.length;
    303 
    304 	b = (unsigned char *)dst->buf_base;
    305 
    306 	/* copy the wrapped blob to the right location */
    307 	memcpy(b, unwrapped.value, unwrapped.length);
    308 
    309 	gss_release_buffer(&minor_status, &unwrapped);
    310 
    311 	return 0;
    312 }
    313 
    314 static void
    315 sb_sasl_gssapi_reset_buf(
    316 	struct sb_sasl_generic_data *p,
    317 	Sockbuf_Buf *buf )
    318 {
    319 	ber_pvt_sb_buf_destroy( buf );
    320 }
    321 
    322 static void
    323 sb_sasl_gssapi_fini( struct sb_sasl_generic_data *p )
    324 {
    325 }
    326 
    327 static const struct sb_sasl_generic_ops sb_sasl_gssapi_ops = {
    328 	sb_sasl_gssapi_init,
    329 	sb_sasl_gssapi_encode,
    330 	sb_sasl_gssapi_decode,
    331 	sb_sasl_gssapi_reset_buf,
    332 	sb_sasl_gssapi_fini
    333 };
    334 
    335 static int
    336 sb_sasl_gssapi_install(
    337 	Sockbuf *sb,
    338 	gss_ctx_id_t gss_ctx )
    339 {
    340 	struct sb_sasl_generic_install install_arg;
    341 
    342 	install_arg.ops		= &sb_sasl_gssapi_ops;
    343 	install_arg.ops_private = gss_ctx;
    344 
    345 	return ldap_pvt_sasl_generic_install( sb, &install_arg );
    346 }
    347 
    348 static void
    349 sb_sasl_gssapi_remove( Sockbuf *sb )
    350 {
    351 	ldap_pvt_sasl_generic_remove( sb );
    352 }
    353 
    354 static int
    355 map_gsserr2ldap(
    356 	LDAP *ld,
    357 	gss_OID mech,
    358 	int gss_rc,
    359 	OM_uint32 minor_status )
    360 {
    361 	char msg[256];
    362 
    363 	Debug1( LDAP_DEBUG_ANY, "%s\n",
    364 	       gsserrstr( msg, sizeof(msg), mech, gss_rc, minor_status ));
    365 
    366 	if (gss_rc == GSS_S_COMPLETE) {
    367 		ld->ld_errno = LDAP_SUCCESS;
    368 	} else if (GSS_CALLING_ERROR(gss_rc)) {
    369 		ld->ld_errno = LDAP_LOCAL_ERROR;
    370 	} else if (GSS_ROUTINE_ERROR(gss_rc)) {
    371 		ld->ld_errno = LDAP_INAPPROPRIATE_AUTH;
    372 	} else if (gss_rc == GSS_S_CONTINUE_NEEDED) {
    373 		ld->ld_errno = LDAP_SASL_BIND_IN_PROGRESS;
    374 	} else if (GSS_SUPPLEMENTARY_INFO(gss_rc)) {
    375 		ld->ld_errno = LDAP_AUTH_UNKNOWN;
    376 	} else if (GSS_ERROR(gss_rc)) {
    377 		ld->ld_errno = LDAP_AUTH_UNKNOWN;
    378 	} else {
    379 		ld->ld_errno = LDAP_OTHER;
    380 	}
    381 
    382 	return ld->ld_errno;
    383 }
    384 
    385 
    386 static int
    387 ldap_gssapi_get_rootdse_infos (
    388 	LDAP *ld,
    389 	char **pmechlist,
    390 	char **pldapServiceName,
    391 	char **pdnsHostName )
    392 {
    393 	/* we need to query the server for supported mechs anyway */
    394 	LDAPMessage *res, *e;
    395 	char *attrs[] = {
    396 		"supportedSASLMechanisms",
    397 		"ldapServiceName",
    398 		"dnsHostName",
    399 		NULL
    400 	};
    401 	char **values, *mechlist;
    402 	char *ldapServiceName = NULL;
    403 	char *dnsHostName = NULL;
    404 	int rc;
    405 
    406 	Debug0( LDAP_DEBUG_TRACE, "ldap_gssapi_get_rootdse_infos\n");
    407 
    408 	rc = ldap_search_s( ld, "", LDAP_SCOPE_BASE,
    409 		NULL, attrs, 0, &res );
    410 
    411 	if ( rc != LDAP_SUCCESS ) {
    412 		return ld->ld_errno;
    413 	}
    414 
    415 	e = ldap_first_entry( ld, res );
    416 	if ( e == NULL ) {
    417 		ldap_msgfree( res );
    418 		if ( ld->ld_errno == LDAP_SUCCESS ) {
    419 			ld->ld_errno = LDAP_NO_SUCH_OBJECT;
    420 		}
    421 		return ld->ld_errno;
    422 	}
    423 
    424 	values = ldap_get_values( ld, e, "supportedSASLMechanisms" );
    425 	if ( values == NULL ) {
    426 		ldap_msgfree( res );
    427 		ld->ld_errno = LDAP_NO_SUCH_ATTRIBUTE;
    428 		return ld->ld_errno;
    429 	}
    430 
    431 	mechlist = ldap_charray2str( values, " " );
    432 	if ( mechlist == NULL ) {
    433 		LDAP_VFREE( values );
    434 		ldap_msgfree( res );
    435 		ld->ld_errno = LDAP_NO_MEMORY;
    436 		return ld->ld_errno;
    437 	}
    438 
    439 	LDAP_VFREE( values );
    440 
    441 	values = ldap_get_values( ld, e, "ldapServiceName" );
    442 	if ( values == NULL ) {
    443 		goto get_dns_host_name;
    444 	}
    445 
    446 	ldapServiceName = ldap_charray2str( values, " " );
    447 	if ( ldapServiceName == NULL ) {
    448 		LDAP_FREE( mechlist );
    449 		LDAP_VFREE( values );
    450 		ldap_msgfree( res );
    451 		ld->ld_errno = LDAP_NO_MEMORY;
    452 		return ld->ld_errno;
    453 	}
    454 	LDAP_VFREE( values );
    455 
    456 get_dns_host_name:
    457 
    458 	values = ldap_get_values( ld, e, "dnsHostName" );
    459 	if ( values == NULL ) {
    460 		goto done;
    461 	}
    462 
    463 	dnsHostName = ldap_charray2str( values, " " );
    464 	if ( dnsHostName == NULL ) {
    465 		LDAP_FREE( mechlist );
    466 		LDAP_FREE( ldapServiceName );
    467 		LDAP_VFREE( values );
    468 		ldap_msgfree( res );
    469 		ld->ld_errno = LDAP_NO_MEMORY;
    470 		return ld->ld_errno;
    471 	}
    472 	LDAP_VFREE( values );
    473 
    474 done:
    475 	ldap_msgfree( res );
    476 
    477 	*pmechlist = mechlist;
    478 	*pldapServiceName = ldapServiceName;
    479 	*pdnsHostName = dnsHostName;
    480 
    481 	return LDAP_SUCCESS;
    482 }
    483 
    484 
    485 static int check_for_gss_spnego_support( LDAP *ld, const char *mechs_str )
    486 {
    487 	int rc;
    488 	char **mechs_list = NULL;
    489 
    490 	mechs_list = ldap_str2charray( mechs_str, " " );
    491 	if ( mechs_list == NULL ) {
    492 		ld->ld_errno = LDAP_NO_MEMORY;
    493 		return ld->ld_errno;
    494 	}
    495 
    496 	rc = ldap_charray_inlist( mechs_list, "GSS-SPNEGO" );
    497 	ldap_charray_free( mechs_list );
    498 	if ( rc != 1) {
    499 		ld->ld_errno = LDAP_STRONG_AUTH_NOT_SUPPORTED;
    500 		return ld->ld_errno;
    501 	}
    502 
    503 	return LDAP_SUCCESS;
    504 }
    505 
    506 static int
    507 guess_service_principal(
    508 	LDAP *ld,
    509 	const char *ldapServiceName,
    510 	const char *dnsHostName,
    511 	gss_name_t *principal )
    512 {
    513 	gss_buffer_desc input_name;
    514 	/* GSS_KRB5_NT_PRINCIPAL_NAME */
    515 	gss_OID_desc nt_principal =
    516 	{10, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x01"};
    517 	const char *host = ld->ld_defconn->lconn_server->lud_host;
    518 	OM_uint32 minor_status;
    519 	int gss_rc;
    520 	int ret;
    521 	size_t svc_principal_size;
    522 	char *svc_principal = NULL;
    523 	const char *principal_fmt = NULL;
    524 	const char *str = NULL;
    525 	const char *givenstr = NULL;
    526 	const char *ignore = "not_defined_in_RFC4178@please_ignore";
    527 	int allow_remote = 0;
    528 
    529 	if (ldapServiceName) {
    530 		givenstr = strchr(ldapServiceName, ':');
    531 		if (givenstr && givenstr[1]) {
    532 			givenstr++;
    533 			if (strcmp(givenstr, ignore) == 0) {
    534 				givenstr = NULL;
    535 			}
    536 		} else {
    537 			givenstr = NULL;
    538 		}
    539 	}
    540 
    541 	if ( ld->ld_options.ldo_gssapi_options & LDAP_GSSAPI_OPT_ALLOW_REMOTE_PRINCIPAL ) {
    542 		allow_remote = 1;
    543 	}
    544 
    545 	if (allow_remote && givenstr) {
    546 		principal_fmt = "%s";
    547 		svc_principal_size = strlen(givenstr) + 1;
    548 		str = givenstr;
    549 
    550 	} else if (allow_remote && dnsHostName) {
    551 		principal_fmt = "ldap/%s";
    552 		svc_principal_size = STRLENOF("ldap/") + strlen(dnsHostName) + 1;
    553 		str = dnsHostName;
    554 
    555 	} else {
    556 		principal_fmt = "ldap/%s";
    557 		svc_principal_size = STRLENOF("ldap/") + strlen(host) + 1;
    558 		str = host;
    559 	}
    560 
    561 	svc_principal = (char*) ldap_memalloc(svc_principal_size * sizeof(char));
    562 	if ( svc_principal == NULL ) {
    563 		ld->ld_errno = LDAP_NO_MEMORY;
    564 		return ld->ld_errno;
    565 	}
    566 
    567 	ret = snprintf( svc_principal, svc_principal_size, principal_fmt, str );
    568 	if (ret < 0 || (size_t)ret >= svc_principal_size) {
    569 		ld->ld_errno = LDAP_LOCAL_ERROR;
    570 		return ld->ld_errno;
    571 	}
    572 
    573 	Debug2( LDAP_DEBUG_TRACE, "principal for host[%s]: '%s'\n",
    574 	       host, svc_principal );
    575 
    576 	input_name.value  = svc_principal;
    577 	input_name.length = (size_t)ret;
    578 
    579 	gss_rc = gss_import_name( &minor_status, &input_name, &nt_principal, principal );
    580 	ldap_memfree( svc_principal );
    581 	if ( gss_rc != GSS_S_COMPLETE ) {
    582 		return map_gsserr2ldap( ld, GSS_C_NO_OID, gss_rc, minor_status );
    583 	}
    584 
    585 	return LDAP_SUCCESS;
    586 }
    587 
    588 void ldap_int_gssapi_close( LDAP *ld, LDAPConn *lc )
    589 {
    590 	if ( lc && lc->lconn_gss_ctx ) {
    591 		OM_uint32 minor_status;
    592 		OM_uint32 ctx_flags = 0;
    593 		gss_ctx_id_t old_gss_ctx = GSS_C_NO_CONTEXT;
    594 		old_gss_ctx = (gss_ctx_id_t)lc->lconn_gss_ctx;
    595 
    596 		gss_inquire_context(&minor_status,
    597 				    old_gss_ctx,
    598 				    NULL,
    599 				    NULL,
    600 				    NULL,
    601 				    NULL,
    602 				    &ctx_flags,
    603 				    NULL,
    604 				    NULL);
    605 
    606 		if (!( ld->ld_options.ldo_gssapi_options & LDAP_GSSAPI_OPT_DO_NOT_FREE_GSS_CONTEXT )) {
    607 			gss_delete_sec_context( &minor_status, &old_gss_ctx, GSS_C_NO_BUFFER );
    608 		}
    609 		lc->lconn_gss_ctx = GSS_C_NO_CONTEXT;
    610 
    611 		if (ctx_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG)) {
    612 			/* remove wrapping layer */
    613 			sb_sasl_gssapi_remove( lc->lconn_sb );
    614 		}
    615 	}
    616 }
    617 
    618 static void
    619 ldap_int_gssapi_setup(
    620 	LDAP *ld,
    621 	LDAPConn *lc,
    622 	gss_ctx_id_t gss_ctx)
    623 {
    624 	OM_uint32 minor_status;
    625 	OM_uint32 ctx_flags = 0;
    626 
    627 	ldap_int_gssapi_close( ld, lc );
    628 
    629 	gss_inquire_context(&minor_status,
    630 			    gss_ctx,
    631 			    NULL,
    632 			    NULL,
    633 			    NULL,
    634 			    NULL,
    635 			    &ctx_flags,
    636 			    NULL,
    637 			    NULL);
    638 
    639 	lc->lconn_gss_ctx = gss_ctx;
    640 
    641 	if (ctx_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG)) {
    642 		/* setup wrapping layer */
    643 		sb_sasl_gssapi_install( lc->lconn_sb, gss_ctx );
    644 	}
    645 }
    646 
    647 #ifdef LDAP_R_COMPILE
    648 ldap_pvt_thread_mutex_t ldap_int_gssapi_mutex;
    649 #endif
    650 
    651 static int
    652 ldap_int_gss_spnego_bind_s( LDAP *ld )
    653 {
    654 	int rc;
    655 	int gss_rc;
    656 	OM_uint32 minor_status;
    657 	char *mechlist = NULL;
    658 	char *ldapServiceName = NULL;
    659 	char *dnsHostName = NULL;
    660 	gss_OID_set supported_mechs = GSS_C_NO_OID_SET;
    661 	int spnego_support = 0;
    662 #define	__SPNEGO_OID_LENGTH 6
    663 #define	__SPNEGO_OID "\053\006\001\005\005\002"
    664 	gss_OID_desc spnego_oid = {__SPNEGO_OID_LENGTH, __SPNEGO_OID};
    665 	gss_OID req_mech = GSS_C_NO_OID;
    666 	gss_OID ret_mech = GSS_C_NO_OID;
    667 	gss_ctx_id_t gss_ctx = GSS_C_NO_CONTEXT;
    668 	gss_name_t principal = GSS_C_NO_NAME;
    669 	OM_uint32 req_flags;
    670 	OM_uint32 ret_flags;
    671 	gss_buffer_desc input_token, output_token = GSS_C_EMPTY_BUFFER;
    672 	struct berval cred, *scred = NULL;
    673 
    674 	LDAP_MUTEX_LOCK( &ldap_int_gssapi_mutex );
    675 
    676 	/* get information from RootDSE entry */
    677 	rc = ldap_gssapi_get_rootdse_infos ( ld, &mechlist,
    678 					     &ldapServiceName, &dnsHostName);
    679 	if ( rc != LDAP_SUCCESS ) {
    680 		return rc;
    681 	}
    682 
    683 	/* check that the server supports GSS-SPNEGO */
    684 	rc = check_for_gss_spnego_support( ld, mechlist );
    685 	if ( rc != LDAP_SUCCESS ) {
    686 		goto rc_error;
    687 	}
    688 
    689 	/* prepare new gss_ctx_id_t */
    690 	rc = guess_service_principal( ld, ldapServiceName, dnsHostName, &principal );
    691 	if ( rc != LDAP_SUCCESS ) {
    692 		goto rc_error;
    693 	}
    694 
    695 	/* see if our gssapi library supports spnego */
    696 	gss_rc = gss_indicate_mechs( &minor_status, &supported_mechs );
    697 	if ( gss_rc != GSS_S_COMPLETE ) {
    698 		goto gss_error;
    699 	}
    700 	gss_rc = gss_test_oid_set_member( &minor_status,
    701 		&spnego_oid, supported_mechs, &spnego_support);
    702 	gss_release_oid_set( &minor_status, &supported_mechs);
    703 	if ( gss_rc != GSS_S_COMPLETE ) {
    704 		goto gss_error;
    705 	}
    706 	if ( spnego_support != 0 ) {
    707 		req_mech = &spnego_oid;
    708 	}
    709 
    710 	req_flags = ld->ld_options.ldo_gssapi_flags;
    711 	req_flags |= GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG;
    712 
    713 	/*
    714 	 * loop around gss_init_sec_context() and ldap_sasl_bind_s()
    715 	 */
    716 	input_token.value = NULL;
    717 	input_token.length = 0;
    718 	gss_rc = gss_init_sec_context(&minor_status,
    719 				      GSS_C_NO_CREDENTIAL,
    720 				      &gss_ctx,
    721 				      principal,
    722 				      req_mech,
    723 				      req_flags,
    724 				      0,
    725 				      NULL,
    726 				      &input_token,
    727 				      &ret_mech,
    728 				      &output_token,
    729 				      &ret_flags,
    730 				      NULL);
    731 	if ( gss_rc == GSS_S_COMPLETE ) {
    732 		rc = LDAP_INAPPROPRIATE_AUTH;
    733 		goto rc_error;
    734 	}
    735 	if ( gss_rc != GSS_S_CONTINUE_NEEDED ) {
    736 		goto gss_error;
    737 	}
    738 	while (1) {
    739 		cred.bv_val = (char *)output_token.value;
    740 		cred.bv_len = output_token.length;
    741 		rc = ldap_sasl_bind_s( ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred );
    742 		gss_release_buffer( &minor_status, &output_token );
    743 		if ( rc != LDAP_SUCCESS && rc != LDAP_SASL_BIND_IN_PROGRESS ) {
    744 			goto rc_error;
    745 		}
    746 
    747 		if ( scred ) {
    748 			input_token.value = scred->bv_val;
    749 			input_token.length = scred->bv_len;
    750 		} else {
    751 			input_token.value = NULL;
    752 			input_token.length = 0;
    753 		}
    754 
    755 		gss_rc = gss_init_sec_context(&minor_status,
    756 					      GSS_C_NO_CREDENTIAL,
    757 					      &gss_ctx,
    758 					      principal,
    759 					      req_mech,
    760 					      req_flags,
    761 					      0,
    762 					      NULL,
    763 					      &input_token,
    764 					      &ret_mech,
    765 					      &output_token,
    766 					      &ret_flags,
    767 					      NULL);
    768 		if ( scred ) {
    769 			ber_bvfree( scred );
    770 		}
    771 		if ( gss_rc == GSS_S_COMPLETE ) {
    772 			gss_release_buffer( &minor_status, &output_token );
    773 			break;
    774 		}
    775 
    776 		if ( gss_rc != GSS_S_CONTINUE_NEEDED ) {
    777 			goto gss_error;
    778 		}
    779 	}
    780 
    781  	ldap_int_gssapi_setup( ld, ld->ld_defconn, gss_ctx);
    782 	gss_ctx = GSS_C_NO_CONTEXT;
    783 
    784 	rc = LDAP_SUCCESS;
    785 	goto rc_error;
    786 
    787 gss_error:
    788 	rc = map_gsserr2ldap( ld,
    789 			      (ret_mech != GSS_C_NO_OID ? ret_mech : req_mech ),
    790 			      gss_rc, minor_status );
    791 rc_error:
    792 	LDAP_MUTEX_UNLOCK( &ldap_int_gssapi_mutex );
    793 	LDAP_FREE( mechlist );
    794 	LDAP_FREE( ldapServiceName );
    795 	LDAP_FREE( dnsHostName );
    796 	gss_release_buffer( &minor_status, &output_token );
    797 	if ( gss_ctx != GSS_C_NO_CONTEXT ) {
    798 		gss_delete_sec_context( &minor_status, &gss_ctx, GSS_C_NO_BUFFER );
    799 	}
    800 	if ( principal != GSS_C_NO_NAME ) {
    801 		gss_release_name( &minor_status, &principal );
    802 	}
    803 	return rc;
    804 }
    805 
    806 int
    807 ldap_int_gssapi_config( struct ldapoptions *lo, int option, const char *arg )
    808 {
    809 	int ok = 0;
    810 
    811 	switch( option ) {
    812 	case LDAP_OPT_SIGN:
    813 
    814 		if (!arg) {
    815 		} else if (strcasecmp(arg, "on") == 0) {
    816 			ok = 1;
    817 		} else if (strcasecmp(arg, "yes") == 0) {
    818 			ok = 1;
    819 		} else if (strcasecmp(arg, "true") == 0) {
    820 			ok = 1;
    821 
    822 		}
    823 		if (ok) {
    824 			lo->ldo_gssapi_flags |= GSS_C_INTEG_FLAG;
    825 		}
    826 
    827 		return 0;
    828 
    829 	case LDAP_OPT_ENCRYPT:
    830 
    831 		if (!arg) {
    832 		} else if (strcasecmp(arg, "on") == 0) {
    833 			ok = 1;
    834 		} else if (strcasecmp(arg, "yes") == 0) {
    835 			ok = 1;
    836 		} else if (strcasecmp(arg, "true") == 0) {
    837 			ok = 1;
    838 		}
    839 
    840 		if (ok) {
    841 			lo->ldo_gssapi_flags |= GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG;
    842 		}
    843 
    844 		return 0;
    845 
    846 	case LDAP_OPT_X_GSSAPI_ALLOW_REMOTE_PRINCIPAL:
    847 
    848 		if (!arg) {
    849 		} else if (strcasecmp(arg, "on") == 0) {
    850 			ok = 1;
    851 		} else if (strcasecmp(arg, "yes") == 0) {
    852 			ok = 1;
    853 		} else if (strcasecmp(arg, "true") == 0) {
    854 			ok = 1;
    855 		}
    856 
    857 		if (ok) {
    858 			lo->ldo_gssapi_options |= LDAP_GSSAPI_OPT_ALLOW_REMOTE_PRINCIPAL;
    859 		}
    860 
    861 		return 0;
    862 	}
    863 
    864 	return -1;
    865 }
    866 
    867 int
    868 ldap_int_gssapi_get_option( LDAP *ld, int option, void *arg )
    869 {
    870 	if ( ld == NULL )
    871 		return -1;
    872 
    873 	switch ( option ) {
    874 	case LDAP_OPT_SSPI_FLAGS:
    875 		* (unsigned *) arg = (unsigned) ld->ld_options.ldo_gssapi_flags;
    876 		break;
    877 
    878 	case LDAP_OPT_SIGN:
    879 		if ( ld->ld_options.ldo_gssapi_flags & GSS_C_INTEG_FLAG ) {
    880 			* (int *) arg = (int)-1;
    881 		} else {
    882 			* (int *) arg = (int)0;
    883 		}
    884 		break;
    885 
    886 	case LDAP_OPT_ENCRYPT:
    887 		if ( ld->ld_options.ldo_gssapi_flags & GSS_C_CONF_FLAG ) {
    888 			* (int *) arg = (int)-1;
    889 		} else {
    890 			* (int *) arg = (int)0;
    891 		}
    892 		break;
    893 
    894 	case LDAP_OPT_SASL_METHOD:
    895 		* (char **) arg = LDAP_STRDUP("GSS-SPNEGO");
    896 		break;
    897 
    898 	case LDAP_OPT_SECURITY_CONTEXT:
    899 		if ( ld->ld_defconn && ld->ld_defconn->lconn_gss_ctx ) {
    900 			* (gss_ctx_id_t *) arg = (gss_ctx_id_t)ld->ld_defconn->lconn_gss_ctx;
    901 		} else {
    902 			* (gss_ctx_id_t *) arg = GSS_C_NO_CONTEXT;
    903 		}
    904 		break;
    905 
    906 	case LDAP_OPT_X_GSSAPI_DO_NOT_FREE_CONTEXT:
    907 		if ( ld->ld_options.ldo_gssapi_options & LDAP_GSSAPI_OPT_DO_NOT_FREE_GSS_CONTEXT ) {
    908 			* (int *) arg = (int)-1;
    909 		} else {
    910 			* (int *) arg = (int)0;
    911 		}
    912 		break;
    913 
    914 	case LDAP_OPT_X_GSSAPI_ALLOW_REMOTE_PRINCIPAL:
    915 		if ( ld->ld_options.ldo_gssapi_options & LDAP_GSSAPI_OPT_ALLOW_REMOTE_PRINCIPAL ) {
    916 			* (int *) arg = (int)-1;
    917 		} else {
    918 			* (int *) arg = (int)0;
    919 		}
    920 		break;
    921 
    922 	default:
    923 		return -1;
    924 	}
    925 
    926 	return 0;
    927 }
    928 
    929 int
    930 ldap_int_gssapi_set_option( LDAP *ld, int option, void *arg )
    931 {
    932 	if ( ld == NULL )
    933 		return -1;
    934 
    935 	switch ( option ) {
    936 	case LDAP_OPT_SSPI_FLAGS:
    937 		if ( arg != LDAP_OPT_OFF ) {
    938 			ld->ld_options.ldo_gssapi_flags = * (unsigned *)arg;
    939 		}
    940 		break;
    941 
    942 	case LDAP_OPT_SIGN:
    943 		if ( arg != LDAP_OPT_OFF ) {
    944 			ld->ld_options.ldo_gssapi_flags |= GSS_C_INTEG_FLAG;
    945 		}
    946 		break;
    947 
    948 	case LDAP_OPT_ENCRYPT:
    949 		if ( arg != LDAP_OPT_OFF ) {
    950 			ld->ld_options.ldo_gssapi_flags |= GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG;
    951 		}
    952 		break;
    953 
    954 	case LDAP_OPT_SASL_METHOD:
    955 		if ( arg != LDAP_OPT_OFF ) {
    956 			const char *m = (const char *)arg;
    957 			if ( strcmp( "GSS-SPNEGO", m ) != 0 ) {
    958 				/* we currently only support GSS-SPNEGO */
    959 				return -1;
    960 			}
    961 		}
    962 		break;
    963 
    964 	case LDAP_OPT_SECURITY_CONTEXT:
    965 		if ( arg != LDAP_OPT_OFF && ld->ld_defconn) {
    966 			ldap_int_gssapi_setup( ld, ld->ld_defconn,
    967 					       (gss_ctx_id_t) arg);
    968 		}
    969 		break;
    970 
    971 	case LDAP_OPT_X_GSSAPI_DO_NOT_FREE_CONTEXT:
    972 		if ( arg != LDAP_OPT_OFF ) {
    973 			ld->ld_options.ldo_gssapi_options |= LDAP_GSSAPI_OPT_DO_NOT_FREE_GSS_CONTEXT;
    974 		}
    975 		break;
    976 
    977 	case LDAP_OPT_X_GSSAPI_ALLOW_REMOTE_PRINCIPAL:
    978 		if ( arg != LDAP_OPT_OFF ) {
    979 			ld->ld_options.ldo_gssapi_options |= LDAP_GSSAPI_OPT_ALLOW_REMOTE_PRINCIPAL;
    980 		}
    981 		break;
    982 
    983 	default:
    984 		return -1;
    985 	}
    986 
    987 	return 0;
    988 }
    989 
    990 #else /* HAVE_GSSAPI */
    991 #define ldap_int_gss_spnego_bind_s(ld) LDAP_NOT_SUPPORTED
    992 #endif /* HAVE_GSSAPI */
    993 
    994 int
    995 ldap_gssapi_bind(
    996 	LDAP *ld,
    997 	LDAP_CONST char *dn,
    998 	LDAP_CONST char *creds )
    999 {
   1000 	return LDAP_NOT_SUPPORTED;
   1001 }
   1002 
   1003 int
   1004 ldap_gssapi_bind_s(
   1005 	LDAP *ld,
   1006 	LDAP_CONST char *dn,
   1007 	LDAP_CONST char *creds )
   1008 {
   1009 	if ( dn != NULL ) {
   1010 		return LDAP_NOT_SUPPORTED;
   1011 	}
   1012 
   1013 	if ( creds != NULL ) {
   1014 		return LDAP_NOT_SUPPORTED;
   1015 	}
   1016 
   1017 	return ldap_int_gss_spnego_bind_s(ld);
   1018 }
   1019