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