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