xref: /netbsd-src/external/bsd/openldap/dist/libraries/libldap/gssapi.c (revision 646988131bf73e1f9b70c3ffdb75155bdbd5dffb)
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 
49 static char *
gsserrstr(char * buf,ber_len_t buf_len,gss_OID mech,int gss_rc,OM_uint32 minor_status)50 gsserrstr(
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 
96 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)97 sb_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 
158 static ber_int_t
sb_sasl_gssapi_encode(struct sb_sasl_generic_data * p,unsigned char * buf,ber_len_t len,Sockbuf_Buf * dst)159 sb_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 
241 static ber_int_t
sb_sasl_gssapi_decode(struct sb_sasl_generic_data * p,const Sockbuf_Buf * src,Sockbuf_Buf * dst)242 sb_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 
314 static void
sb_sasl_gssapi_reset_buf(struct sb_sasl_generic_data * p,Sockbuf_Buf * buf)315 sb_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 
322 static void
sb_sasl_gssapi_fini(struct sb_sasl_generic_data * p)323 sb_sasl_gssapi_fini( struct sb_sasl_generic_data *p )
324 {
325 }
326 
327 static 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 
335 static int
sb_sasl_gssapi_install(Sockbuf * sb,gss_ctx_id_t gss_ctx)336 sb_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 
348 static void
sb_sasl_gssapi_remove(Sockbuf * sb)349 sb_sasl_gssapi_remove( Sockbuf *sb )
350 {
351 	ldap_pvt_sasl_generic_remove( sb );
352 }
353 
354 static int
map_gsserr2ldap(LDAP * ld,gss_OID mech,int gss_rc,OM_uint32 minor_status)355 map_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 
386 static int
ldap_gssapi_get_rootdse_infos(LDAP * ld,char ** pmechlist,char ** pldapServiceName,char ** pdnsHostName)387 ldap_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 
456 get_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 
474 done:
475 	ldap_msgfree( res );
476 
477 	*pmechlist = mechlist;
478 	*pldapServiceName = ldapServiceName;
479 	*pdnsHostName = dnsHostName;
480 
481 	return LDAP_SUCCESS;
482 }
483 
484 
check_for_gss_spnego_support(LDAP * ld,const char * mechs_str)485 static 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 
506 static int
guess_service_principal(LDAP * ld,const char * ldapServiceName,const char * dnsHostName,gss_name_t * principal)507 guess_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 
ldap_int_gssapi_close(LDAP * ld,LDAPConn * lc)588 void 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 
618 static void
ldap_int_gssapi_setup(LDAP * ld,LDAPConn * lc,gss_ctx_id_t gss_ctx)619 ldap_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
648 ldap_pvt_thread_mutex_t ldap_int_gssapi_mutex;
649 #endif
650 
651 static int
ldap_int_gss_spnego_bind_s(LDAP * ld)652 ldap_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 
787 gss_error:
788 	rc = map_gsserr2ldap( ld,
789 			      (ret_mech != GSS_C_NO_OID ? ret_mech : req_mech ),
790 			      gss_rc, minor_status );
791 rc_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 
806 int
ldap_int_gssapi_config(struct ldapoptions * lo,int option,const char * arg)807 ldap_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 
867 int
ldap_int_gssapi_get_option(LDAP * ld,int option,void * arg)868 ldap_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 
929 int
ldap_int_gssapi_set_option(LDAP * ld,int option,void * arg)930 ldap_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 
994 int
ldap_gssapi_bind(LDAP * ld,LDAP_CONST char * dn,LDAP_CONST char * creds)995 ldap_gssapi_bind(
996 	LDAP *ld,
997 	LDAP_CONST char *dn,
998 	LDAP_CONST char *creds )
999 {
1000 	return LDAP_NOT_SUPPORTED;
1001 }
1002 
1003 int
ldap_gssapi_bind_s(LDAP * ld,LDAP_CONST char * dn,LDAP_CONST char * creds)1004 ldap_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