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