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