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