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@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
49static char *
50gsserrstr(
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
96static void
97sb_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
158static ber_int_t
159sb_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
241static ber_int_t
242sb_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
314static void
315sb_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
322static void
323sb_sasl_gssapi_fini( struct sb_sasl_generic_data *p )
324{
325}
326
327static 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
335static int
336sb_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
348static void
349sb_sasl_gssapi_remove( Sockbuf *sb )
350{
351	ldap_pvt_sasl_generic_remove( sb );
352}
353
354static int
355map_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
386static int
387ldap_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
456get_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
474done:
475	ldap_msgfree( res );
476
477	*pmechlist = mechlist;
478	*pldapServiceName = ldapServiceName;
479	*pdnsHostName = dnsHostName;
480
481	return LDAP_SUCCESS;
482}
483
484
485static 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
506static int
507guess_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
588void 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
618static void
619ldap_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
648ldap_pvt_thread_mutex_t ldap_int_gssapi_mutex;
649#endif
650
651static int
652ldap_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
787gss_error:
788	rc = map_gsserr2ldap( ld,
789			      (ret_mech != GSS_C_NO_OID ? ret_mech : req_mech ),
790			      gss_rc, minor_status );
791rc_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
806int
807ldap_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
867int
868ldap_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
929int
930ldap_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
994int
995ldap_gssapi_bind(
996	LDAP *ld,
997	LDAP_CONST char *dn,
998	LDAP_CONST char *creds )
999{
1000	return LDAP_NOT_SUPPORTED;
1001}
1002
1003int
1004ldap_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