xref: /netbsd-src/external/bsd/openldap/dist/libraries/libldap/gssapi.c (revision 646988131bf73e1f9b70c3ffdb75155bdbd5dffb)
1*64698813Schristos /*	$NetBSD: gssapi.c,v 1.5 2021/08/19 12:13:37 christos Exp $	*/
2476be171Schristos 
3476be171Schristos /* $OpenLDAP$ */
4476be171Schristos /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5476be171Schristos  *
6476be171Schristos  * Copyright 1998-2020 The OpenLDAP Foundation.
7476be171Schristos  * All rights reserved.
8476be171Schristos  *
9476be171Schristos  * Author: Stefan Metzmacher <metze@sernet.de>
10476be171Schristos  *
11476be171Schristos  * Redistribution and use in source and binary forms, with or without
12476be171Schristos  * modification, are permitted only as authorized by the OpenLDAP
13476be171Schristos  * Public License.
14476be171Schristos  *
15476be171Schristos  * A copy of this license is available in the file LICENSE in the
16476be171Schristos  * top-level directory of the distribution or, alternatively, at
17476be171Schristos  * <http://www.OpenLDAP.org/license.html>.
18476be171Schristos  */
19476be171Schristos 
20476be171Schristos #include <sys/cdefs.h>
21*64698813Schristos __RCSID("$NetBSD: gssapi.c,v 1.5 2021/08/19 12:13:37 christos Exp $");
22476be171Schristos 
23476be171Schristos #include "portable.h"
24476be171Schristos 
25476be171Schristos #include <stdio.h>
26476be171Schristos 
27476be171Schristos #include <ac/socket.h>
28476be171Schristos #include <ac/stdlib.h>
29476be171Schristos #include <ac/string.h>
30476be171Schristos #include <ac/time.h>
31476be171Schristos #include <ac/errno.h>
32476be171Schristos #include <ac/ctype.h>
33476be171Schristos #include <ac/unistd.h>
34476be171Schristos 
35476be171Schristos #ifdef HAVE_LIMITS_H
36476be171Schristos #include <limits.h>
37476be171Schristos #endif
38476be171Schristos 
39476be171Schristos #include "ldap-int.h"
40476be171Schristos 
41476be171Schristos #ifdef HAVE_GSSAPI
42476be171Schristos 
43476be171Schristos #ifdef HAVE_GSSAPI_GSSAPI_H
44476be171Schristos #include <gssapi/gssapi.h>
45476be171Schristos #else
46476be171Schristos #include <gssapi.h>
47476be171Schristos #endif
48476be171Schristos 
49476be171Schristos static char *
gsserrstr(char * buf,ber_len_t buf_len,gss_OID mech,int gss_rc,OM_uint32 minor_status)50476be171Schristos gsserrstr(
51476be171Schristos 	char *buf,
52476be171Schristos 	ber_len_t buf_len,
53476be171Schristos 	gss_OID mech,
54476be171Schristos 	int gss_rc,
55476be171Schristos 	OM_uint32 minor_status )
56476be171Schristos {
57476be171Schristos 	OM_uint32 min2;
58476be171Schristos 	gss_buffer_desc mech_msg = GSS_C_EMPTY_BUFFER;
59476be171Schristos 	gss_buffer_desc gss_msg = GSS_C_EMPTY_BUFFER;
60476be171Schristos 	gss_buffer_desc minor_msg = GSS_C_EMPTY_BUFFER;
61476be171Schristos 	OM_uint32 msg_ctx = 0;
62476be171Schristos 
63476be171Schristos 	if (buf == NULL) {
64476be171Schristos 		return NULL;
65476be171Schristos 	}
66476be171Schristos 
67476be171Schristos 	if (buf_len == 0) {
68476be171Schristos 		return NULL;
69476be171Schristos 	}
70476be171Schristos 
71476be171Schristos #ifdef HAVE_GSS_OID_TO_STR
72476be171Schristos 	gss_oid_to_str(&min2, mech, &mech_msg);
73476be171Schristos #endif
74476be171Schristos 	gss_display_status(&min2, gss_rc, GSS_C_GSS_CODE,
75476be171Schristos 			   mech, &msg_ctx, &gss_msg);
76476be171Schristos 	gss_display_status(&min2, minor_status, GSS_C_MECH_CODE,
77476be171Schristos 			   mech, &msg_ctx, &minor_msg);
78476be171Schristos 
79476be171Schristos 	snprintf(buf, buf_len, "gss_rc[%d:%*s] mech[%*s] minor[%u:%*s]",
80476be171Schristos 		 gss_rc, (int)gss_msg.length,
81476be171Schristos 		 (const char *)(gss_msg.value?gss_msg.value:""),
82476be171Schristos 		 (int)mech_msg.length,
83476be171Schristos 		 (const char *)(mech_msg.value?mech_msg.value:""),
84476be171Schristos 		 minor_status, (int)minor_msg.length,
85476be171Schristos 		 (const char *)(minor_msg.value?minor_msg.value:""));
86476be171Schristos 
87476be171Schristos 	gss_release_buffer(&min2, &mech_msg);
88476be171Schristos 	gss_release_buffer(&min2, &gss_msg);
89476be171Schristos 	gss_release_buffer(&min2, &minor_msg);
90476be171Schristos 
91476be171Schristos 	buf[buf_len-1] = '\0';
92476be171Schristos 
93476be171Schristos 	return buf;
94476be171Schristos }
95476be171Schristos 
96476be171Schristos static void
sb_sasl_gssapi_init(struct sb_sasl_generic_data * p,ber_len_t * min_send,ber_len_t * max_send,ber_len_t * max_recv)97476be171Schristos sb_sasl_gssapi_init(
98476be171Schristos 	struct sb_sasl_generic_data *p,
99476be171Schristos 	ber_len_t *min_send,
100476be171Schristos 	ber_len_t *max_send,
101476be171Schristos 	ber_len_t *max_recv )
102476be171Schristos {
103476be171Schristos 	gss_ctx_id_t gss_ctx = (gss_ctx_id_t)p->ops_private;
104476be171Schristos 	int gss_rc;
105476be171Schristos 	OM_uint32 minor_status;
106476be171Schristos 	gss_OID ctx_mech = GSS_C_NO_OID;
107476be171Schristos 	OM_uint32 ctx_flags = 0;
108476be171Schristos 	int conf_req_flag = 0;
109476be171Schristos 	OM_uint32 max_input_size;
110476be171Schristos 
111476be171Schristos 	gss_inquire_context(&minor_status,
112476be171Schristos 			    gss_ctx,
113476be171Schristos 			    NULL,
114476be171Schristos 			    NULL,
115476be171Schristos 			    NULL,
116476be171Schristos 			    &ctx_mech,
117476be171Schristos 			    &ctx_flags,
118476be171Schristos 			    NULL,
119476be171Schristos 			    NULL);
120476be171Schristos 
121476be171Schristos 	if (ctx_flags & (GSS_C_CONF_FLAG)) {
122476be171Schristos 		conf_req_flag = 1;
123476be171Schristos 	}
124476be171Schristos 
125476be171Schristos #if defined(HAVE_CYRUS_SASL)
126476be171Schristos #define SEND_PREALLOC_SIZE	SASL_MIN_BUFF_SIZE
127476be171Schristos #else
128476be171Schristos #define SEND_PREALLOC_SIZE      4096
129476be171Schristos #endif
130476be171Schristos #define SEND_MAX_WIRE_SIZE	0x00A00000
131476be171Schristos #define RECV_MAX_WIRE_SIZE	0x0FFFFFFF
132476be171Schristos #define FALLBACK_SEND_MAX_SIZE	0x009FFFB8 /* from MIT 1.5.x */
133476be171Schristos 
134476be171Schristos 	gss_rc = gss_wrap_size_limit(&minor_status, gss_ctx,
135476be171Schristos 				     conf_req_flag, GSS_C_QOP_DEFAULT,
136476be171Schristos 				     SEND_MAX_WIRE_SIZE, &max_input_size);
137476be171Schristos 	if ( gss_rc != GSS_S_COMPLETE ) {
138476be171Schristos 		char msg[256];
139476be171Schristos 		ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
140*64698813Schristos 				"%s: failed to wrap size limit: %s\n", __func__,
141476be171Schristos 				gsserrstr( msg, sizeof(msg), ctx_mech, gss_rc, minor_status ) );
142476be171Schristos 		ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
143*64698813Schristos 				"%s: fallback to default wrap size limit\n",
144*64698813Schristos 				__func__);
145476be171Schristos 		/*
146476be171Schristos 		 * some libgssglue/libgssapi versions
147476be171Schristos 		 * have a broken gss_wrap_size_limit()
148476be171Schristos 		 * implementation
149476be171Schristos 		 */
150476be171Schristos 		max_input_size = FALLBACK_SEND_MAX_SIZE;
151476be171Schristos 	}
152476be171Schristos 
153476be171Schristos 	*min_send = SEND_PREALLOC_SIZE;
154476be171Schristos 	*max_send = max_input_size;
155476be171Schristos 	*max_recv = RECV_MAX_WIRE_SIZE;
156476be171Schristos }
157476be171Schristos 
158476be171Schristos static ber_int_t
sb_sasl_gssapi_encode(struct sb_sasl_generic_data * p,unsigned char * buf,ber_len_t len,Sockbuf_Buf * dst)159476be171Schristos sb_sasl_gssapi_encode(
160476be171Schristos 	struct sb_sasl_generic_data *p,
161476be171Schristos 	unsigned char *buf,
162476be171Schristos 	ber_len_t len,
163476be171Schristos 	Sockbuf_Buf *dst )
164476be171Schristos {
165476be171Schristos 	gss_ctx_id_t gss_ctx = (gss_ctx_id_t)p->ops_private;
166476be171Schristos 	int gss_rc;
167476be171Schristos 	OM_uint32 minor_status;
168476be171Schristos 	gss_buffer_desc unwrapped, wrapped;
169476be171Schristos 	gss_OID ctx_mech = GSS_C_NO_OID;
170476be171Schristos 	OM_uint32 ctx_flags = 0;
171476be171Schristos 	int conf_req_flag = 0;
172476be171Schristos 	int conf_state;
173476be171Schristos 	unsigned char *b;
174476be171Schristos 	ber_len_t pkt_len;
175476be171Schristos 
176476be171Schristos 	unwrapped.value		= buf;
177476be171Schristos 	unwrapped.length	= len;
178476be171Schristos 
179476be171Schristos 	gss_inquire_context(&minor_status,
180476be171Schristos 			    gss_ctx,
181476be171Schristos 			    NULL,
182476be171Schristos 			    NULL,
183476be171Schristos 			    NULL,
184476be171Schristos 			    &ctx_mech,
185476be171Schristos 			    &ctx_flags,
186476be171Schristos 			    NULL,
187476be171Schristos 			    NULL);
188476be171Schristos 
189476be171Schristos 	if (ctx_flags & (GSS_C_CONF_FLAG)) {
190476be171Schristos 		conf_req_flag = 1;
191476be171Schristos 	}
192476be171Schristos 
193476be171Schristos 	gss_rc = gss_wrap(&minor_status, gss_ctx,
194476be171Schristos 			  conf_req_flag, GSS_C_QOP_DEFAULT,
195476be171Schristos 			  &unwrapped, &conf_state,
196476be171Schristos 			  &wrapped);
197476be171Schristos 	if ( gss_rc != GSS_S_COMPLETE ) {
198476be171Schristos 		char msg[256];
199476be171Schristos 		ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
200*64698813Schristos 				"%s: failed to encode packet: %s\n", __func__,
201476be171Schristos 				gsserrstr( msg, sizeof(msg), ctx_mech, gss_rc, minor_status ) );
202476be171Schristos 		return -1;
203476be171Schristos 	}
204476be171Schristos 
205476be171Schristos 	if ( conf_req_flag && conf_state == 0 ) {
206476be171Schristos 		ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
207*64698813Schristos 				"%s: GSS_C_CONF_FLAG was ignored by our gss_wrap()\n",
208*64698813Schristos 				__func__);
209476be171Schristos 		return -1;
210476be171Schristos 	}
211476be171Schristos 
212476be171Schristos 	pkt_len = 4 + wrapped.length;
213476be171Schristos 
214476be171Schristos 	/* Grow the packet buffer if neccessary */
215476be171Schristos 	if ( dst->buf_size < pkt_len &&
216476be171Schristos 		ber_pvt_sb_grow_buffer( dst, pkt_len ) < 0 )
217476be171Schristos 	{
218476be171Schristos 		ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
219*64698813Schristos 				"%s: failed to grow the buffer to %lu bytes\n",
220*64698813Schristos 				__func__, pkt_len );
221476be171Schristos 		return -1;
222476be171Schristos 	}
223476be171Schristos 
224476be171Schristos 	dst->buf_end = pkt_len;
225476be171Schristos 
226476be171Schristos 	b = (unsigned char *)dst->buf_base;
227476be171Schristos 
228476be171Schristos 	b[0] = (unsigned char)(wrapped.length >> 24);
229476be171Schristos 	b[1] = (unsigned char)(wrapped.length >> 16);
230476be171Schristos 	b[2] = (unsigned char)(wrapped.length >>  8);
231476be171Schristos 	b[3] = (unsigned char)(wrapped.length >>  0);
232476be171Schristos 
233476be171Schristos 	/* copy the wrapped blob to the right location */
234476be171Schristos 	memcpy(b + 4, wrapped.value, wrapped.length);
235476be171Schristos 
236476be171Schristos 	gss_release_buffer(&minor_status, &wrapped);
237476be171Schristos 
238476be171Schristos 	return 0;
239476be171Schristos }
240476be171Schristos 
241476be171Schristos static ber_int_t
sb_sasl_gssapi_decode(struct sb_sasl_generic_data * p,const Sockbuf_Buf * src,Sockbuf_Buf * dst)242476be171Schristos sb_sasl_gssapi_decode(
243476be171Schristos 	struct sb_sasl_generic_data *p,
244476be171Schristos 	const Sockbuf_Buf *src,
245476be171Schristos 	Sockbuf_Buf *dst )
246476be171Schristos {
247476be171Schristos 	gss_ctx_id_t gss_ctx = (gss_ctx_id_t)p->ops_private;
248476be171Schristos 	int gss_rc;
249476be171Schristos 	OM_uint32 minor_status;
250476be171Schristos 	gss_buffer_desc unwrapped, wrapped;
251476be171Schristos 	gss_OID ctx_mech = GSS_C_NO_OID;
252476be171Schristos 	OM_uint32 ctx_flags = 0;
253476be171Schristos 	int conf_req_flag = 0;
254476be171Schristos 	int conf_state;
255476be171Schristos 	unsigned char *b;
256476be171Schristos 
257476be171Schristos 	wrapped.value	= src->buf_base + 4;
258476be171Schristos 	wrapped.length	= src->buf_end - 4;
259476be171Schristos 
260476be171Schristos 	gss_inquire_context(&minor_status,
261476be171Schristos 			    gss_ctx,
262476be171Schristos 			    NULL,
263476be171Schristos 			    NULL,
264476be171Schristos 			    NULL,
265476be171Schristos 			    &ctx_mech,
266476be171Schristos 			    &ctx_flags,
267476be171Schristos 			    NULL,
268476be171Schristos 			    NULL);
269476be171Schristos 
270476be171Schristos 	if (ctx_flags & (GSS_C_CONF_FLAG)) {
271476be171Schristos 		conf_req_flag = 1;
272476be171Schristos 	}
273476be171Schristos 
274476be171Schristos 	gss_rc = gss_unwrap(&minor_status, gss_ctx,
275476be171Schristos 			    &wrapped, &unwrapped,
276476be171Schristos 			    &conf_state, GSS_C_QOP_DEFAULT);
277476be171Schristos 	if ( gss_rc != GSS_S_COMPLETE ) {
278476be171Schristos 		char msg[256];
279476be171Schristos 		ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
280*64698813Schristos 				"%s: failed to decode packet: %s\n", __func__,
281476be171Schristos 				gsserrstr( msg, sizeof(msg), ctx_mech, gss_rc, minor_status ) );
282476be171Schristos 		return -1;
283476be171Schristos 	}
284476be171Schristos 
285476be171Schristos 	if ( conf_req_flag && conf_state == 0 ) {
286476be171Schristos 		ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
287*64698813Schristos 				"%s: GSS_C_CONF_FLAG was ignored by our peer\n",
288*64698813Schristos 				__func__);
289476be171Schristos 		return -1;
290476be171Schristos 	}
291476be171Schristos 
292476be171Schristos 	/* Grow the packet buffer if neccessary */
293476be171Schristos 	if ( dst->buf_size < unwrapped.length &&
294476be171Schristos 		ber_pvt_sb_grow_buffer( dst, unwrapped.length ) < 0 )
295476be171Schristos 	{
296476be171Schristos 		ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
297*64698813Schristos 				"%s: failed to grow the buffer to %zu bytes\n",
298*64698813Schristos 				__func__, unwrapped.length );
299476be171Schristos 		return -1;
300476be171Schristos 	}
301476be171Schristos 
302476be171Schristos 	dst->buf_end = unwrapped.length;
303476be171Schristos 
304476be171Schristos 	b = (unsigned char *)dst->buf_base;
305476be171Schristos 
306476be171Schristos 	/* copy the wrapped blob to the right location */
307476be171Schristos 	memcpy(b, unwrapped.value, unwrapped.length);
308476be171Schristos 
309476be171Schristos 	gss_release_buffer(&minor_status, &unwrapped);
310476be171Schristos 
311476be171Schristos 	return 0;
312476be171Schristos }
313476be171Schristos 
314476be171Schristos static void
sb_sasl_gssapi_reset_buf(struct sb_sasl_generic_data * p,Sockbuf_Buf * buf)315476be171Schristos sb_sasl_gssapi_reset_buf(
316476be171Schristos 	struct sb_sasl_generic_data *p,
317476be171Schristos 	Sockbuf_Buf *buf )
318476be171Schristos {
319476be171Schristos 	ber_pvt_sb_buf_destroy( buf );
320476be171Schristos }
321476be171Schristos 
322476be171Schristos static void
sb_sasl_gssapi_fini(struct sb_sasl_generic_data * p)323476be171Schristos sb_sasl_gssapi_fini( struct sb_sasl_generic_data *p )
324476be171Schristos {
325476be171Schristos }
326476be171Schristos 
327476be171Schristos static const struct sb_sasl_generic_ops sb_sasl_gssapi_ops = {
328476be171Schristos 	sb_sasl_gssapi_init,
329476be171Schristos 	sb_sasl_gssapi_encode,
330476be171Schristos 	sb_sasl_gssapi_decode,
331476be171Schristos 	sb_sasl_gssapi_reset_buf,
332476be171Schristos 	sb_sasl_gssapi_fini
333476be171Schristos };
334476be171Schristos 
335476be171Schristos static int
sb_sasl_gssapi_install(Sockbuf * sb,gss_ctx_id_t gss_ctx)336476be171Schristos sb_sasl_gssapi_install(
337476be171Schristos 	Sockbuf *sb,
338476be171Schristos 	gss_ctx_id_t gss_ctx )
339476be171Schristos {
340476be171Schristos 	struct sb_sasl_generic_install install_arg;
341476be171Schristos 
342476be171Schristos 	install_arg.ops		= &sb_sasl_gssapi_ops;
343476be171Schristos 	install_arg.ops_private = gss_ctx;
344476be171Schristos 
345476be171Schristos 	return ldap_pvt_sasl_generic_install( sb, &install_arg );
346476be171Schristos }
347476be171Schristos 
348476be171Schristos static void
sb_sasl_gssapi_remove(Sockbuf * sb)349476be171Schristos sb_sasl_gssapi_remove( Sockbuf *sb )
350476be171Schristos {
351476be171Schristos 	ldap_pvt_sasl_generic_remove( sb );
352476be171Schristos }
353476be171Schristos 
354476be171Schristos static int
map_gsserr2ldap(LDAP * ld,gss_OID mech,int gss_rc,OM_uint32 minor_status)355476be171Schristos map_gsserr2ldap(
356476be171Schristos 	LDAP *ld,
357476be171Schristos 	gss_OID mech,
358476be171Schristos 	int gss_rc,
359476be171Schristos 	OM_uint32 minor_status )
360476be171Schristos {
361476be171Schristos 	char msg[256];
362476be171Schristos 
363476be171Schristos 	Debug1( LDAP_DEBUG_ANY, "%s\n",
364476be171Schristos 	       gsserrstr( msg, sizeof(msg), mech, gss_rc, minor_status ));
365476be171Schristos 
366476be171Schristos 	if (gss_rc == GSS_S_COMPLETE) {
367476be171Schristos 		ld->ld_errno = LDAP_SUCCESS;
368476be171Schristos 	} else if (GSS_CALLING_ERROR(gss_rc)) {
369476be171Schristos 		ld->ld_errno = LDAP_LOCAL_ERROR;
370476be171Schristos 	} else if (GSS_ROUTINE_ERROR(gss_rc)) {
371476be171Schristos 		ld->ld_errno = LDAP_INAPPROPRIATE_AUTH;
372476be171Schristos 	} else if (gss_rc == GSS_S_CONTINUE_NEEDED) {
373476be171Schristos 		ld->ld_errno = LDAP_SASL_BIND_IN_PROGRESS;
374476be171Schristos 	} else if (GSS_SUPPLEMENTARY_INFO(gss_rc)) {
375476be171Schristos 		ld->ld_errno = LDAP_AUTH_UNKNOWN;
376476be171Schristos 	} else if (GSS_ERROR(gss_rc)) {
377476be171Schristos 		ld->ld_errno = LDAP_AUTH_UNKNOWN;
378476be171Schristos 	} else {
379476be171Schristos 		ld->ld_errno = LDAP_OTHER;
380476be171Schristos 	}
381476be171Schristos 
382476be171Schristos 	return ld->ld_errno;
383476be171Schristos }
384476be171Schristos 
385476be171Schristos 
386476be171Schristos static int
ldap_gssapi_get_rootdse_infos(LDAP * ld,char ** pmechlist,char ** pldapServiceName,char ** pdnsHostName)387476be171Schristos ldap_gssapi_get_rootdse_infos (
388476be171Schristos 	LDAP *ld,
389476be171Schristos 	char **pmechlist,
390476be171Schristos 	char **pldapServiceName,
391476be171Schristos 	char **pdnsHostName )
392476be171Schristos {
393476be171Schristos 	/* we need to query the server for supported mechs anyway */
394476be171Schristos 	LDAPMessage *res, *e;
395476be171Schristos 	char *attrs[] = {
396476be171Schristos 		"supportedSASLMechanisms",
397476be171Schristos 		"ldapServiceName",
398476be171Schristos 		"dnsHostName",
399476be171Schristos 		NULL
400476be171Schristos 	};
401476be171Schristos 	char **values, *mechlist;
402476be171Schristos 	char *ldapServiceName = NULL;
403476be171Schristos 	char *dnsHostName = NULL;
404476be171Schristos 	int rc;
405476be171Schristos 
406476be171Schristos 	Debug0( LDAP_DEBUG_TRACE, "ldap_gssapi_get_rootdse_infos\n");
407476be171Schristos 
408476be171Schristos 	rc = ldap_search_s( ld, "", LDAP_SCOPE_BASE,
409476be171Schristos 		NULL, attrs, 0, &res );
410476be171Schristos 
411476be171Schristos 	if ( rc != LDAP_SUCCESS ) {
412476be171Schristos 		return ld->ld_errno;
413476be171Schristos 	}
414476be171Schristos 
415476be171Schristos 	e = ldap_first_entry( ld, res );
416476be171Schristos 	if ( e == NULL ) {
417476be171Schristos 		ldap_msgfree( res );
418476be171Schristos 		if ( ld->ld_errno == LDAP_SUCCESS ) {
419476be171Schristos 			ld->ld_errno = LDAP_NO_SUCH_OBJECT;
420476be171Schristos 		}
421476be171Schristos 		return ld->ld_errno;
422476be171Schristos 	}
423476be171Schristos 
424476be171Schristos 	values = ldap_get_values( ld, e, "supportedSASLMechanisms" );
425476be171Schristos 	if ( values == NULL ) {
426476be171Schristos 		ldap_msgfree( res );
427476be171Schristos 		ld->ld_errno = LDAP_NO_SUCH_ATTRIBUTE;
428476be171Schristos 		return ld->ld_errno;
429476be171Schristos 	}
430476be171Schristos 
431476be171Schristos 	mechlist = ldap_charray2str( values, " " );
432476be171Schristos 	if ( mechlist == NULL ) {
433476be171Schristos 		LDAP_VFREE( values );
434476be171Schristos 		ldap_msgfree( res );
435476be171Schristos 		ld->ld_errno = LDAP_NO_MEMORY;
436476be171Schristos 		return ld->ld_errno;
437476be171Schristos 	}
438476be171Schristos 
439476be171Schristos 	LDAP_VFREE( values );
440476be171Schristos 
441476be171Schristos 	values = ldap_get_values( ld, e, "ldapServiceName" );
442476be171Schristos 	if ( values == NULL ) {
443476be171Schristos 		goto get_dns_host_name;
444476be171Schristos 	}
445476be171Schristos 
446476be171Schristos 	ldapServiceName = ldap_charray2str( values, " " );
447476be171Schristos 	if ( ldapServiceName == NULL ) {
448476be171Schristos 		LDAP_FREE( mechlist );
449476be171Schristos 		LDAP_VFREE( values );
450476be171Schristos 		ldap_msgfree( res );
451476be171Schristos 		ld->ld_errno = LDAP_NO_MEMORY;
452476be171Schristos 		return ld->ld_errno;
453476be171Schristos 	}
454476be171Schristos 	LDAP_VFREE( values );
455476be171Schristos 
456476be171Schristos get_dns_host_name:
457476be171Schristos 
458476be171Schristos 	values = ldap_get_values( ld, e, "dnsHostName" );
459476be171Schristos 	if ( values == NULL ) {
460476be171Schristos 		goto done;
461476be171Schristos 	}
462476be171Schristos 
463476be171Schristos 	dnsHostName = ldap_charray2str( values, " " );
464476be171Schristos 	if ( dnsHostName == NULL ) {
465476be171Schristos 		LDAP_FREE( mechlist );
466476be171Schristos 		LDAP_FREE( ldapServiceName );
467476be171Schristos 		LDAP_VFREE( values );
468476be171Schristos 		ldap_msgfree( res );
469476be171Schristos 		ld->ld_errno = LDAP_NO_MEMORY;
470476be171Schristos 		return ld->ld_errno;
471476be171Schristos 	}
472476be171Schristos 	LDAP_VFREE( values );
473476be171Schristos 
474476be171Schristos done:
475476be171Schristos 	ldap_msgfree( res );
476476be171Schristos 
477476be171Schristos 	*pmechlist = mechlist;
478476be171Schristos 	*pldapServiceName = ldapServiceName;
479476be171Schristos 	*pdnsHostName = dnsHostName;
480476be171Schristos 
481476be171Schristos 	return LDAP_SUCCESS;
482476be171Schristos }
483476be171Schristos 
484476be171Schristos 
check_for_gss_spnego_support(LDAP * ld,const char * mechs_str)485476be171Schristos static int check_for_gss_spnego_support( LDAP *ld, const char *mechs_str )
486476be171Schristos {
487476be171Schristos 	int rc;
488476be171Schristos 	char **mechs_list = NULL;
489476be171Schristos 
490476be171Schristos 	mechs_list = ldap_str2charray( mechs_str, " " );
491476be171Schristos 	if ( mechs_list == NULL ) {
492476be171Schristos 		ld->ld_errno = LDAP_NO_MEMORY;
493476be171Schristos 		return ld->ld_errno;
494476be171Schristos 	}
495476be171Schristos 
496476be171Schristos 	rc = ldap_charray_inlist( mechs_list, "GSS-SPNEGO" );
497476be171Schristos 	ldap_charray_free( mechs_list );
498476be171Schristos 	if ( rc != 1) {
499476be171Schristos 		ld->ld_errno = LDAP_STRONG_AUTH_NOT_SUPPORTED;
500476be171Schristos 		return ld->ld_errno;
501476be171Schristos 	}
502476be171Schristos 
503476be171Schristos 	return LDAP_SUCCESS;
504476be171Schristos }
505476be171Schristos 
506476be171Schristos static int
guess_service_principal(LDAP * ld,const char * ldapServiceName,const char * dnsHostName,gss_name_t * principal)507476be171Schristos guess_service_principal(
508476be171Schristos 	LDAP *ld,
509476be171Schristos 	const char *ldapServiceName,
510476be171Schristos 	const char *dnsHostName,
511476be171Schristos 	gss_name_t *principal )
512476be171Schristos {
513476be171Schristos 	gss_buffer_desc input_name;
514476be171Schristos 	/* GSS_KRB5_NT_PRINCIPAL_NAME */
515476be171Schristos 	gss_OID_desc nt_principal =
516476be171Schristos 	{10, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x01"};
517476be171Schristos 	const char *host = ld->ld_defconn->lconn_server->lud_host;
518476be171Schristos 	OM_uint32 minor_status;
519476be171Schristos 	int gss_rc;
520476be171Schristos 	int ret;
521476be171Schristos 	size_t svc_principal_size;
522476be171Schristos 	char *svc_principal = NULL;
523476be171Schristos 	const char *principal_fmt = NULL;
524476be171Schristos 	const char *str = NULL;
525476be171Schristos 	const char *givenstr = NULL;
526476be171Schristos 	const char *ignore = "not_defined_in_RFC4178@please_ignore";
527476be171Schristos 	int allow_remote = 0;
528476be171Schristos 
529476be171Schristos 	if (ldapServiceName) {
530476be171Schristos 		givenstr = strchr(ldapServiceName, ':');
531476be171Schristos 		if (givenstr && givenstr[1]) {
532476be171Schristos 			givenstr++;
533476be171Schristos 			if (strcmp(givenstr, ignore) == 0) {
534476be171Schristos 				givenstr = NULL;
535476be171Schristos 			}
536476be171Schristos 		} else {
537476be171Schristos 			givenstr = NULL;
538476be171Schristos 		}
539476be171Schristos 	}
540476be171Schristos 
541476be171Schristos 	if ( ld->ld_options.ldo_gssapi_options & LDAP_GSSAPI_OPT_ALLOW_REMOTE_PRINCIPAL ) {
542476be171Schristos 		allow_remote = 1;
543476be171Schristos 	}
544476be171Schristos 
545476be171Schristos 	if (allow_remote && givenstr) {
546476be171Schristos 		principal_fmt = "%s";
547476be171Schristos 		svc_principal_size = strlen(givenstr) + 1;
548476be171Schristos 		str = givenstr;
549476be171Schristos 
550476be171Schristos 	} else if (allow_remote && dnsHostName) {
551476be171Schristos 		principal_fmt = "ldap/%s";
552476be171Schristos 		svc_principal_size = STRLENOF("ldap/") + strlen(dnsHostName) + 1;
553476be171Schristos 		str = dnsHostName;
554476be171Schristos 
555476be171Schristos 	} else {
556476be171Schristos 		principal_fmt = "ldap/%s";
557476be171Schristos 		svc_principal_size = STRLENOF("ldap/") + strlen(host) + 1;
558476be171Schristos 		str = host;
559476be171Schristos 	}
560476be171Schristos 
561476be171Schristos 	svc_principal = (char*) ldap_memalloc(svc_principal_size * sizeof(char));
562476be171Schristos 	if ( svc_principal == NULL ) {
563476be171Schristos 		ld->ld_errno = LDAP_NO_MEMORY;
564476be171Schristos 		return ld->ld_errno;
565476be171Schristos 	}
566476be171Schristos 
567476be171Schristos 	ret = snprintf( svc_principal, svc_principal_size, principal_fmt, str );
568476be171Schristos 	if (ret < 0 || (size_t)ret >= svc_principal_size) {
569476be171Schristos 		ld->ld_errno = LDAP_LOCAL_ERROR;
570476be171Schristos 		return ld->ld_errno;
571476be171Schristos 	}
572476be171Schristos 
573476be171Schristos 	Debug2( LDAP_DEBUG_TRACE, "principal for host[%s]: '%s'\n",
574476be171Schristos 	       host, svc_principal );
575476be171Schristos 
576476be171Schristos 	input_name.value  = svc_principal;
577476be171Schristos 	input_name.length = (size_t)ret;
578476be171Schristos 
579476be171Schristos 	gss_rc = gss_import_name( &minor_status, &input_name, &nt_principal, principal );
580476be171Schristos 	ldap_memfree( svc_principal );
581476be171Schristos 	if ( gss_rc != GSS_S_COMPLETE ) {
582476be171Schristos 		return map_gsserr2ldap( ld, GSS_C_NO_OID, gss_rc, minor_status );
583476be171Schristos 	}
584476be171Schristos 
585476be171Schristos 	return LDAP_SUCCESS;
586476be171Schristos }
587476be171Schristos 
ldap_int_gssapi_close(LDAP * ld,LDAPConn * lc)588476be171Schristos void ldap_int_gssapi_close( LDAP *ld, LDAPConn *lc )
589476be171Schristos {
590476be171Schristos 	if ( lc && lc->lconn_gss_ctx ) {
591476be171Schristos 		OM_uint32 minor_status;
592476be171Schristos 		OM_uint32 ctx_flags = 0;
593476be171Schristos 		gss_ctx_id_t old_gss_ctx = GSS_C_NO_CONTEXT;
594476be171Schristos 		old_gss_ctx = (gss_ctx_id_t)lc->lconn_gss_ctx;
595476be171Schristos 
596476be171Schristos 		gss_inquire_context(&minor_status,
597476be171Schristos 				    old_gss_ctx,
598476be171Schristos 				    NULL,
599476be171Schristos 				    NULL,
600476be171Schristos 				    NULL,
601476be171Schristos 				    NULL,
602476be171Schristos 				    &ctx_flags,
603476be171Schristos 				    NULL,
604476be171Schristos 				    NULL);
605476be171Schristos 
606476be171Schristos 		if (!( ld->ld_options.ldo_gssapi_options & LDAP_GSSAPI_OPT_DO_NOT_FREE_GSS_CONTEXT )) {
607476be171Schristos 			gss_delete_sec_context( &minor_status, &old_gss_ctx, GSS_C_NO_BUFFER );
608476be171Schristos 		}
609476be171Schristos 		lc->lconn_gss_ctx = GSS_C_NO_CONTEXT;
610476be171Schristos 
611476be171Schristos 		if (ctx_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG)) {
612476be171Schristos 			/* remove wrapping layer */
613476be171Schristos 			sb_sasl_gssapi_remove( lc->lconn_sb );
614476be171Schristos 		}
615476be171Schristos 	}
616476be171Schristos }
617476be171Schristos 
618476be171Schristos static void
ldap_int_gssapi_setup(LDAP * ld,LDAPConn * lc,gss_ctx_id_t gss_ctx)619476be171Schristos ldap_int_gssapi_setup(
620476be171Schristos 	LDAP *ld,
621476be171Schristos 	LDAPConn *lc,
622476be171Schristos 	gss_ctx_id_t gss_ctx)
623476be171Schristos {
624476be171Schristos 	OM_uint32 minor_status;
625476be171Schristos 	OM_uint32 ctx_flags = 0;
626476be171Schristos 
627476be171Schristos 	ldap_int_gssapi_close( ld, lc );
628476be171Schristos 
629476be171Schristos 	gss_inquire_context(&minor_status,
630476be171Schristos 			    gss_ctx,
631476be171Schristos 			    NULL,
632476be171Schristos 			    NULL,
633476be171Schristos 			    NULL,
634476be171Schristos 			    NULL,
635476be171Schristos 			    &ctx_flags,
636476be171Schristos 			    NULL,
637476be171Schristos 			    NULL);
638476be171Schristos 
639476be171Schristos 	lc->lconn_gss_ctx = gss_ctx;
640476be171Schristos 
641476be171Schristos 	if (ctx_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG)) {
642476be171Schristos 		/* setup wrapping layer */
643476be171Schristos 		sb_sasl_gssapi_install( lc->lconn_sb, gss_ctx );
644476be171Schristos 	}
645476be171Schristos }
646476be171Schristos 
647476be171Schristos #ifdef LDAP_R_COMPILE
648476be171Schristos ldap_pvt_thread_mutex_t ldap_int_gssapi_mutex;
649476be171Schristos #endif
650476be171Schristos 
651476be171Schristos static int
ldap_int_gss_spnego_bind_s(LDAP * ld)652476be171Schristos ldap_int_gss_spnego_bind_s( LDAP *ld )
653476be171Schristos {
654476be171Schristos 	int rc;
655476be171Schristos 	int gss_rc;
656476be171Schristos 	OM_uint32 minor_status;
657476be171Schristos 	char *mechlist = NULL;
658476be171Schristos 	char *ldapServiceName = NULL;
659476be171Schristos 	char *dnsHostName = NULL;
660476be171Schristos 	gss_OID_set supported_mechs = GSS_C_NO_OID_SET;
661476be171Schristos 	int spnego_support = 0;
662476be171Schristos #define	__SPNEGO_OID_LENGTH 6
663476be171Schristos #define	__SPNEGO_OID "\053\006\001\005\005\002"
664476be171Schristos 	gss_OID_desc spnego_oid = {__SPNEGO_OID_LENGTH, __SPNEGO_OID};
665476be171Schristos 	gss_OID req_mech = GSS_C_NO_OID;
666476be171Schristos 	gss_OID ret_mech = GSS_C_NO_OID;
667476be171Schristos 	gss_ctx_id_t gss_ctx = GSS_C_NO_CONTEXT;
668476be171Schristos 	gss_name_t principal = GSS_C_NO_NAME;
669476be171Schristos 	OM_uint32 req_flags;
670476be171Schristos 	OM_uint32 ret_flags;
671476be171Schristos 	gss_buffer_desc input_token, output_token = GSS_C_EMPTY_BUFFER;
672476be171Schristos 	struct berval cred, *scred = NULL;
673476be171Schristos 
674476be171Schristos 	LDAP_MUTEX_LOCK( &ldap_int_gssapi_mutex );
675476be171Schristos 
676476be171Schristos 	/* get information from RootDSE entry */
677476be171Schristos 	rc = ldap_gssapi_get_rootdse_infos ( ld, &mechlist,
678476be171Schristos 					     &ldapServiceName, &dnsHostName);
679476be171Schristos 	if ( rc != LDAP_SUCCESS ) {
680476be171Schristos 		return rc;
681476be171Schristos 	}
682476be171Schristos 
683476be171Schristos 	/* check that the server supports GSS-SPNEGO */
684476be171Schristos 	rc = check_for_gss_spnego_support( ld, mechlist );
685476be171Schristos 	if ( rc != LDAP_SUCCESS ) {
686476be171Schristos 		goto rc_error;
687476be171Schristos 	}
688476be171Schristos 
689476be171Schristos 	/* prepare new gss_ctx_id_t */
690476be171Schristos 	rc = guess_service_principal( ld, ldapServiceName, dnsHostName, &principal );
691476be171Schristos 	if ( rc != LDAP_SUCCESS ) {
692476be171Schristos 		goto rc_error;
693476be171Schristos 	}
694476be171Schristos 
695476be171Schristos 	/* see if our gssapi library supports spnego */
696476be171Schristos 	gss_rc = gss_indicate_mechs( &minor_status, &supported_mechs );
697476be171Schristos 	if ( gss_rc != GSS_S_COMPLETE ) {
698476be171Schristos 		goto gss_error;
699476be171Schristos 	}
700476be171Schristos 	gss_rc = gss_test_oid_set_member( &minor_status,
701476be171Schristos 		&spnego_oid, supported_mechs, &spnego_support);
702476be171Schristos 	gss_release_oid_set( &minor_status, &supported_mechs);
703476be171Schristos 	if ( gss_rc != GSS_S_COMPLETE ) {
704476be171Schristos 		goto gss_error;
705476be171Schristos 	}
706476be171Schristos 	if ( spnego_support != 0 ) {
707476be171Schristos 		req_mech = &spnego_oid;
708476be171Schristos 	}
709476be171Schristos 
710476be171Schristos 	req_flags = ld->ld_options.ldo_gssapi_flags;
711476be171Schristos 	req_flags |= GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG;
712476be171Schristos 
713476be171Schristos 	/*
714476be171Schristos 	 * loop around gss_init_sec_context() and ldap_sasl_bind_s()
715476be171Schristos 	 */
716476be171Schristos 	input_token.value = NULL;
717476be171Schristos 	input_token.length = 0;
718476be171Schristos 	gss_rc = gss_init_sec_context(&minor_status,
719476be171Schristos 				      GSS_C_NO_CREDENTIAL,
720476be171Schristos 				      &gss_ctx,
721476be171Schristos 				      principal,
722476be171Schristos 				      req_mech,
723476be171Schristos 				      req_flags,
724476be171Schristos 				      0,
725476be171Schristos 				      NULL,
726476be171Schristos 				      &input_token,
727476be171Schristos 				      &ret_mech,
728476be171Schristos 				      &output_token,
729476be171Schristos 				      &ret_flags,
730476be171Schristos 				      NULL);
731476be171Schristos 	if ( gss_rc == GSS_S_COMPLETE ) {
732476be171Schristos 		rc = LDAP_INAPPROPRIATE_AUTH;
733476be171Schristos 		goto rc_error;
734476be171Schristos 	}
735476be171Schristos 	if ( gss_rc != GSS_S_CONTINUE_NEEDED ) {
736476be171Schristos 		goto gss_error;
737476be171Schristos 	}
738476be171Schristos 	while (1) {
739476be171Schristos 		cred.bv_val = (char *)output_token.value;
740476be171Schristos 		cred.bv_len = output_token.length;
741476be171Schristos 		rc = ldap_sasl_bind_s( ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred );
742476be171Schristos 		gss_release_buffer( &minor_status, &output_token );
743476be171Schristos 		if ( rc != LDAP_SUCCESS && rc != LDAP_SASL_BIND_IN_PROGRESS ) {
744476be171Schristos 			goto rc_error;
745476be171Schristos 		}
746476be171Schristos 
747476be171Schristos 		if ( scred ) {
748476be171Schristos 			input_token.value = scred->bv_val;
749476be171Schristos 			input_token.length = scred->bv_len;
750476be171Schristos 		} else {
751476be171Schristos 			input_token.value = NULL;
752476be171Schristos 			input_token.length = 0;
753476be171Schristos 		}
754476be171Schristos 
755476be171Schristos 		gss_rc = gss_init_sec_context(&minor_status,
756476be171Schristos 					      GSS_C_NO_CREDENTIAL,
757476be171Schristos 					      &gss_ctx,
758476be171Schristos 					      principal,
759476be171Schristos 					      req_mech,
760476be171Schristos 					      req_flags,
761476be171Schristos 					      0,
762476be171Schristos 					      NULL,
763476be171Schristos 					      &input_token,
764476be171Schristos 					      &ret_mech,
765476be171Schristos 					      &output_token,
766476be171Schristos 					      &ret_flags,
767476be171Schristos 					      NULL);
768476be171Schristos 		if ( scred ) {
769476be171Schristos 			ber_bvfree( scred );
770476be171Schristos 		}
771476be171Schristos 		if ( gss_rc == GSS_S_COMPLETE ) {
772476be171Schristos 			gss_release_buffer( &minor_status, &output_token );
773476be171Schristos 			break;
774476be171Schristos 		}
775476be171Schristos 
776476be171Schristos 		if ( gss_rc != GSS_S_CONTINUE_NEEDED ) {
777476be171Schristos 			goto gss_error;
778476be171Schristos 		}
779476be171Schristos 	}
780476be171Schristos 
781476be171Schristos  	ldap_int_gssapi_setup( ld, ld->ld_defconn, gss_ctx);
782476be171Schristos 	gss_ctx = GSS_C_NO_CONTEXT;
783476be171Schristos 
784476be171Schristos 	rc = LDAP_SUCCESS;
785476be171Schristos 	goto rc_error;
786476be171Schristos 
787476be171Schristos gss_error:
788476be171Schristos 	rc = map_gsserr2ldap( ld,
789476be171Schristos 			      (ret_mech != GSS_C_NO_OID ? ret_mech : req_mech ),
790476be171Schristos 			      gss_rc, minor_status );
791476be171Schristos rc_error:
792476be171Schristos 	LDAP_MUTEX_UNLOCK( &ldap_int_gssapi_mutex );
793476be171Schristos 	LDAP_FREE( mechlist );
794476be171Schristos 	LDAP_FREE( ldapServiceName );
795476be171Schristos 	LDAP_FREE( dnsHostName );
796476be171Schristos 	gss_release_buffer( &minor_status, &output_token );
797476be171Schristos 	if ( gss_ctx != GSS_C_NO_CONTEXT ) {
798476be171Schristos 		gss_delete_sec_context( &minor_status, &gss_ctx, GSS_C_NO_BUFFER );
799476be171Schristos 	}
800476be171Schristos 	if ( principal != GSS_C_NO_NAME ) {
801476be171Schristos 		gss_release_name( &minor_status, &principal );
802476be171Schristos 	}
803476be171Schristos 	return rc;
804476be171Schristos }
805476be171Schristos 
806476be171Schristos int
ldap_int_gssapi_config(struct ldapoptions * lo,int option,const char * arg)807476be171Schristos ldap_int_gssapi_config( struct ldapoptions *lo, int option, const char *arg )
808476be171Schristos {
809476be171Schristos 	int ok = 0;
810476be171Schristos 
811476be171Schristos 	switch( option ) {
812476be171Schristos 	case LDAP_OPT_SIGN:
813476be171Schristos 
814476be171Schristos 		if (!arg) {
815476be171Schristos 		} else if (strcasecmp(arg, "on") == 0) {
816476be171Schristos 			ok = 1;
817476be171Schristos 		} else if (strcasecmp(arg, "yes") == 0) {
818476be171Schristos 			ok = 1;
819476be171Schristos 		} else if (strcasecmp(arg, "true") == 0) {
820476be171Schristos 			ok = 1;
821476be171Schristos 
822476be171Schristos 		}
823476be171Schristos 		if (ok) {
824476be171Schristos 			lo->ldo_gssapi_flags |= GSS_C_INTEG_FLAG;
825476be171Schristos 		}
826476be171Schristos 
827476be171Schristos 		return 0;
828476be171Schristos 
829476be171Schristos 	case LDAP_OPT_ENCRYPT:
830476be171Schristos 
831476be171Schristos 		if (!arg) {
832476be171Schristos 		} else if (strcasecmp(arg, "on") == 0) {
833476be171Schristos 			ok = 1;
834476be171Schristos 		} else if (strcasecmp(arg, "yes") == 0) {
835476be171Schristos 			ok = 1;
836476be171Schristos 		} else if (strcasecmp(arg, "true") == 0) {
837476be171Schristos 			ok = 1;
838476be171Schristos 		}
839476be171Schristos 
840476be171Schristos 		if (ok) {
841476be171Schristos 			lo->ldo_gssapi_flags |= GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG;
842476be171Schristos 		}
843476be171Schristos 
844476be171Schristos 		return 0;
845476be171Schristos 
846476be171Schristos 	case LDAP_OPT_X_GSSAPI_ALLOW_REMOTE_PRINCIPAL:
847476be171Schristos 
848476be171Schristos 		if (!arg) {
849476be171Schristos 		} else if (strcasecmp(arg, "on") == 0) {
850476be171Schristos 			ok = 1;
851476be171Schristos 		} else if (strcasecmp(arg, "yes") == 0) {
852476be171Schristos 			ok = 1;
853476be171Schristos 		} else if (strcasecmp(arg, "true") == 0) {
854476be171Schristos 			ok = 1;
855476be171Schristos 		}
856476be171Schristos 
857476be171Schristos 		if (ok) {
858476be171Schristos 			lo->ldo_gssapi_options |= LDAP_GSSAPI_OPT_ALLOW_REMOTE_PRINCIPAL;
859476be171Schristos 		}
860476be171Schristos 
861476be171Schristos 		return 0;
862476be171Schristos 	}
863476be171Schristos 
864476be171Schristos 	return -1;
865476be171Schristos }
866476be171Schristos 
867476be171Schristos int
ldap_int_gssapi_get_option(LDAP * ld,int option,void * arg)868476be171Schristos ldap_int_gssapi_get_option( LDAP *ld, int option, void *arg )
869476be171Schristos {
870476be171Schristos 	if ( ld == NULL )
871476be171Schristos 		return -1;
872476be171Schristos 
873476be171Schristos 	switch ( option ) {
874476be171Schristos 	case LDAP_OPT_SSPI_FLAGS:
875476be171Schristos 		* (unsigned *) arg = (unsigned) ld->ld_options.ldo_gssapi_flags;
876476be171Schristos 		break;
877476be171Schristos 
878476be171Schristos 	case LDAP_OPT_SIGN:
879476be171Schristos 		if ( ld->ld_options.ldo_gssapi_flags & GSS_C_INTEG_FLAG ) {
880476be171Schristos 			* (int *) arg = (int)-1;
881476be171Schristos 		} else {
882476be171Schristos 			* (int *) arg = (int)0;
883476be171Schristos 		}
884476be171Schristos 		break;
885476be171Schristos 
886476be171Schristos 	case LDAP_OPT_ENCRYPT:
887476be171Schristos 		if ( ld->ld_options.ldo_gssapi_flags & GSS_C_CONF_FLAG ) {
888476be171Schristos 			* (int *) arg = (int)-1;
889476be171Schristos 		} else {
890476be171Schristos 			* (int *) arg = (int)0;
891476be171Schristos 		}
892476be171Schristos 		break;
893476be171Schristos 
894476be171Schristos 	case LDAP_OPT_SASL_METHOD:
895476be171Schristos 		* (char **) arg = LDAP_STRDUP("GSS-SPNEGO");
896476be171Schristos 		break;
897476be171Schristos 
898476be171Schristos 	case LDAP_OPT_SECURITY_CONTEXT:
899476be171Schristos 		if ( ld->ld_defconn && ld->ld_defconn->lconn_gss_ctx ) {
900476be171Schristos 			* (gss_ctx_id_t *) arg = (gss_ctx_id_t)ld->ld_defconn->lconn_gss_ctx;
901476be171Schristos 		} else {
902476be171Schristos 			* (gss_ctx_id_t *) arg = GSS_C_NO_CONTEXT;
903476be171Schristos 		}
904476be171Schristos 		break;
905476be171Schristos 
906476be171Schristos 	case LDAP_OPT_X_GSSAPI_DO_NOT_FREE_CONTEXT:
907476be171Schristos 		if ( ld->ld_options.ldo_gssapi_options & LDAP_GSSAPI_OPT_DO_NOT_FREE_GSS_CONTEXT ) {
908476be171Schristos 			* (int *) arg = (int)-1;
909476be171Schristos 		} else {
910476be171Schristos 			* (int *) arg = (int)0;
911476be171Schristos 		}
912476be171Schristos 		break;
913476be171Schristos 
914476be171Schristos 	case LDAP_OPT_X_GSSAPI_ALLOW_REMOTE_PRINCIPAL:
915476be171Schristos 		if ( ld->ld_options.ldo_gssapi_options & LDAP_GSSAPI_OPT_ALLOW_REMOTE_PRINCIPAL ) {
916476be171Schristos 			* (int *) arg = (int)-1;
917476be171Schristos 		} else {
918476be171Schristos 			* (int *) arg = (int)0;
919476be171Schristos 		}
920476be171Schristos 		break;
921476be171Schristos 
922476be171Schristos 	default:
923476be171Schristos 		return -1;
924476be171Schristos 	}
925476be171Schristos 
926476be171Schristos 	return 0;
927476be171Schristos }
928476be171Schristos 
929476be171Schristos int
ldap_int_gssapi_set_option(LDAP * ld,int option,void * arg)930476be171Schristos ldap_int_gssapi_set_option( LDAP *ld, int option, void *arg )
931476be171Schristos {
932476be171Schristos 	if ( ld == NULL )
933476be171Schristos 		return -1;
934476be171Schristos 
935476be171Schristos 	switch ( option ) {
936476be171Schristos 	case LDAP_OPT_SSPI_FLAGS:
937476be171Schristos 		if ( arg != LDAP_OPT_OFF ) {
938476be171Schristos 			ld->ld_options.ldo_gssapi_flags = * (unsigned *)arg;
939476be171Schristos 		}
940476be171Schristos 		break;
941476be171Schristos 
942476be171Schristos 	case LDAP_OPT_SIGN:
943476be171Schristos 		if ( arg != LDAP_OPT_OFF ) {
944476be171Schristos 			ld->ld_options.ldo_gssapi_flags |= GSS_C_INTEG_FLAG;
945476be171Schristos 		}
946476be171Schristos 		break;
947476be171Schristos 
948476be171Schristos 	case LDAP_OPT_ENCRYPT:
949476be171Schristos 		if ( arg != LDAP_OPT_OFF ) {
950476be171Schristos 			ld->ld_options.ldo_gssapi_flags |= GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG;
951476be171Schristos 		}
952476be171Schristos 		break;
953476be171Schristos 
954476be171Schristos 	case LDAP_OPT_SASL_METHOD:
955476be171Schristos 		if ( arg != LDAP_OPT_OFF ) {
956476be171Schristos 			const char *m = (const char *)arg;
957476be171Schristos 			if ( strcmp( "GSS-SPNEGO", m ) != 0 ) {
958476be171Schristos 				/* we currently only support GSS-SPNEGO */
959476be171Schristos 				return -1;
960476be171Schristos 			}
961476be171Schristos 		}
962476be171Schristos 		break;
963476be171Schristos 
964476be171Schristos 	case LDAP_OPT_SECURITY_CONTEXT:
965476be171Schristos 		if ( arg != LDAP_OPT_OFF && ld->ld_defconn) {
966476be171Schristos 			ldap_int_gssapi_setup( ld, ld->ld_defconn,
967476be171Schristos 					       (gss_ctx_id_t) arg);
968476be171Schristos 		}
969476be171Schristos 		break;
970476be171Schristos 
971476be171Schristos 	case LDAP_OPT_X_GSSAPI_DO_NOT_FREE_CONTEXT:
972476be171Schristos 		if ( arg != LDAP_OPT_OFF ) {
973476be171Schristos 			ld->ld_options.ldo_gssapi_options |= LDAP_GSSAPI_OPT_DO_NOT_FREE_GSS_CONTEXT;
974476be171Schristos 		}
975476be171Schristos 		break;
976476be171Schristos 
977476be171Schristos 	case LDAP_OPT_X_GSSAPI_ALLOW_REMOTE_PRINCIPAL:
978476be171Schristos 		if ( arg != LDAP_OPT_OFF ) {
979476be171Schristos 			ld->ld_options.ldo_gssapi_options |= LDAP_GSSAPI_OPT_ALLOW_REMOTE_PRINCIPAL;
980476be171Schristos 		}
981476be171Schristos 		break;
982476be171Schristos 
983476be171Schristos 	default:
984476be171Schristos 		return -1;
985476be171Schristos 	}
986476be171Schristos 
987476be171Schristos 	return 0;
988476be171Schristos }
989476be171Schristos 
990476be171Schristos #else /* HAVE_GSSAPI */
991476be171Schristos #define ldap_int_gss_spnego_bind_s(ld) LDAP_NOT_SUPPORTED
992476be171Schristos #endif /* HAVE_GSSAPI */
993476be171Schristos 
994476be171Schristos int
ldap_gssapi_bind(LDAP * ld,LDAP_CONST char * dn,LDAP_CONST char * creds)995476be171Schristos ldap_gssapi_bind(
996476be171Schristos 	LDAP *ld,
997476be171Schristos 	LDAP_CONST char *dn,
998476be171Schristos 	LDAP_CONST char *creds )
999476be171Schristos {
1000476be171Schristos 	return LDAP_NOT_SUPPORTED;
1001476be171Schristos }
1002476be171Schristos 
1003476be171Schristos int
ldap_gssapi_bind_s(LDAP * ld,LDAP_CONST char * dn,LDAP_CONST char * creds)1004476be171Schristos ldap_gssapi_bind_s(
1005476be171Schristos 	LDAP *ld,
1006476be171Schristos 	LDAP_CONST char *dn,
1007476be171Schristos 	LDAP_CONST char *creds )
1008476be171Schristos {
1009476be171Schristos 	if ( dn != NULL ) {
1010476be171Schristos 		return LDAP_NOT_SUPPORTED;
1011476be171Schristos 	}
1012476be171Schristos 
1013476be171Schristos 	if ( creds != NULL ) {
1014476be171Schristos 		return LDAP_NOT_SUPPORTED;
1015476be171Schristos 	}
1016476be171Schristos 
1017476be171Schristos 	return ldap_int_gss_spnego_bind_s(ld);
1018476be171Schristos }
1019