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