]> git.sur5r.net Git - openldap/blob - libraries/libldap/gssapi.c
Merge remote-tracking branch 'origin/mdb.master'
[openldap] / libraries / libldap / gssapi.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1998-2012 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
250         wrapped.value   = src->buf_base + 4;
251         wrapped.length  = src->buf_end - 4;
252
253         gss_inquire_context(&minor_status,
254                             gss_ctx,
255                             NULL,
256                             NULL,
257                             NULL,
258                             &ctx_mech,
259                             &ctx_flags,
260                             NULL,
261                             NULL);
262
263         if (ctx_flags & (GSS_C_CONF_FLAG)) {
264                 conf_req_flag = 1;
265         }
266
267         gss_rc = gss_unwrap(&minor_status, gss_ctx,
268                             &wrapped, &unwrapped,
269                             &conf_state, GSS_C_QOP_DEFAULT);
270         if ( gss_rc != GSS_S_COMPLETE ) {
271                 char msg[256];
272                 ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
273                                 "sb_sasl_gssapi_decode: failed to decode packet: %s\n",
274                                 gsserrstr( msg, sizeof(msg), ctx_mech, gss_rc, minor_status ) );
275                 return -1;
276         }
277
278         if ( conf_req_flag && conf_state == 0 ) {
279                 ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
280                                 "sb_sasl_gssapi_encode: GSS_C_CONF_FLAG was ignored by our peer\n" );
281                 return -1;
282         }
283
284         /* Grow the packet buffer if neccessary */
285         if ( dst->buf_size < unwrapped.length &&
286                 ber_pvt_sb_grow_buffer( dst, unwrapped.length ) < 0 )
287         {
288                 ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
289                                 "sb_sasl_gssapi_decode: failed to grow the buffer to %lu bytes\n",
290                                 unwrapped.length );
291                 return -1;
292         }
293
294         dst->buf_end = unwrapped.length;
295
296         b = (unsigned char *)dst->buf_base;
297
298         /* copy the wrapped blob to the right location */
299         memcpy(b, unwrapped.value, unwrapped.length);
300
301         gss_release_buffer(&minor_status, &unwrapped);
302
303         return 0;
304 }
305
306 static void
307 sb_sasl_gssapi_reset_buf(
308         struct sb_sasl_generic_data *p,
309         Sockbuf_Buf *buf )
310 {
311         ber_pvt_sb_buf_destroy( buf );
312 }
313
314 static void
315 sb_sasl_gssapi_fini( struct sb_sasl_generic_data *p )
316 {
317 }
318
319 static const struct sb_sasl_generic_ops sb_sasl_gssapi_ops = {
320         sb_sasl_gssapi_init,
321         sb_sasl_gssapi_encode,
322         sb_sasl_gssapi_decode,
323         sb_sasl_gssapi_reset_buf,
324         sb_sasl_gssapi_fini
325 };
326
327 static int
328 sb_sasl_gssapi_install(
329         Sockbuf *sb,
330         gss_ctx_id_t gss_ctx )
331 {
332         struct sb_sasl_generic_install install_arg;
333
334         install_arg.ops         = &sb_sasl_gssapi_ops;
335         install_arg.ops_private = gss_ctx;
336
337         return ldap_pvt_sasl_generic_install( sb, &install_arg );
338 }
339
340 static void
341 sb_sasl_gssapi_remove( Sockbuf *sb )
342 {
343         ldap_pvt_sasl_generic_remove( sb );
344 }
345
346 static int
347 map_gsserr2ldap(
348         LDAP *ld,
349         gss_OID mech,
350         int gss_rc,
351         OM_uint32 minor_status )
352 {
353         char msg[256];
354
355         Debug( LDAP_DEBUG_ANY, "%s\n",
356                gsserrstr( msg, sizeof(msg), mech, gss_rc, minor_status ),
357                NULL, NULL );
358
359         if (gss_rc == GSS_S_COMPLETE) {
360                 ld->ld_errno = LDAP_SUCCESS;
361         } else if (GSS_CALLING_ERROR(gss_rc)) {
362                 ld->ld_errno = LDAP_LOCAL_ERROR;
363         } else if (GSS_ROUTINE_ERROR(gss_rc)) {
364                 ld->ld_errno = LDAP_INAPPROPRIATE_AUTH;
365         } else if (gss_rc == GSS_S_CONTINUE_NEEDED) {
366                 ld->ld_errno = LDAP_SASL_BIND_IN_PROGRESS;
367         } else if (GSS_SUPPLEMENTARY_INFO(gss_rc)) {
368                 ld->ld_errno = LDAP_AUTH_UNKNOWN;
369         } else if (GSS_ERROR(gss_rc)) {
370                 ld->ld_errno = LDAP_AUTH_UNKNOWN;
371         } else {
372                 ld->ld_errno = LDAP_OTHER;
373         }
374
375         return ld->ld_errno;
376 }
377
378
379 static int
380 ldap_gssapi_get_rootdse_infos (
381         LDAP *ld,
382         char **pmechlist,
383         char **pldapServiceName,
384         char **pdnsHostName )
385 {
386         /* we need to query the server for supported mechs anyway */
387         LDAPMessage *res, *e;
388         char *attrs[] = {
389                 "supportedSASLMechanisms",
390                 "ldapServiceName",
391                 "dnsHostName",
392                 NULL
393         };
394         char **values, *mechlist;
395         char *ldapServiceName = NULL;
396         char *dnsHostName = NULL;
397         int rc;
398
399         Debug( LDAP_DEBUG_TRACE, "ldap_gssapi_get_rootdse_infos\n", 0, 0, 0 );
400
401         rc = ldap_search_s( ld, "", LDAP_SCOPE_BASE,
402                 NULL, attrs, 0, &res );
403
404         if ( rc != LDAP_SUCCESS ) {
405                 return ld->ld_errno;
406         }
407
408         e = ldap_first_entry( ld, res );
409         if ( e == NULL ) {
410                 ldap_msgfree( res );
411                 if ( ld->ld_errno == LDAP_SUCCESS ) {
412                         ld->ld_errno = LDAP_NO_SUCH_OBJECT;
413                 }
414                 return ld->ld_errno;
415         }
416
417         values = ldap_get_values( ld, e, "supportedSASLMechanisms" );
418         if ( values == NULL ) {
419                 ldap_msgfree( res );
420                 ld->ld_errno = LDAP_NO_SUCH_ATTRIBUTE;
421                 return ld->ld_errno;
422         }
423
424         mechlist = ldap_charray2str( values, " " );
425         if ( mechlist == NULL ) {
426                 LDAP_VFREE( values );
427                 ldap_msgfree( res );
428                 ld->ld_errno = LDAP_NO_MEMORY;
429                 return ld->ld_errno;
430         }
431
432         LDAP_VFREE( values );
433
434         values = ldap_get_values( ld, e, "ldapServiceName" );
435         if ( values == NULL ) {
436                 goto get_dns_host_name;
437         }
438
439         ldapServiceName = ldap_charray2str( values, " " );
440         if ( ldapServiceName == NULL ) {
441                 LDAP_FREE( mechlist );
442                 LDAP_VFREE( values );
443                 ldap_msgfree( res );
444                 ld->ld_errno = LDAP_NO_MEMORY;
445                 return ld->ld_errno;
446         }
447         LDAP_VFREE( values );
448
449 get_dns_host_name:
450
451         values = ldap_get_values( ld, e, "dnsHostName" );
452         if ( values == NULL ) {
453                 goto done;
454         }
455
456         dnsHostName = ldap_charray2str( values, " " );
457         if ( dnsHostName == NULL ) {
458                 LDAP_FREE( mechlist );
459                 LDAP_FREE( ldapServiceName );
460                 LDAP_VFREE( values );
461                 ldap_msgfree( res );
462                 ld->ld_errno = LDAP_NO_MEMORY;
463                 return ld->ld_errno;
464         }
465         LDAP_VFREE( values );
466
467 done:
468         ldap_msgfree( res );
469
470         *pmechlist = mechlist;
471         *pldapServiceName = ldapServiceName;
472         *pdnsHostName = dnsHostName;
473
474         return LDAP_SUCCESS;
475 }
476
477
478 static int check_for_gss_spnego_support( LDAP *ld, const char *mechs_str )
479 {
480         int rc;
481         char **mechs_list = NULL;
482
483         mechs_list = ldap_str2charray( mechs_str, " " );
484         if ( mechs_list == NULL ) {
485                 ld->ld_errno = LDAP_NO_MEMORY;
486                 return ld->ld_errno;
487         }
488
489         rc = ldap_charray_inlist( mechs_list, "GSS-SPNEGO" );
490         ldap_charray_free( mechs_list );
491         if ( rc != 1) {
492                 ld->ld_errno = LDAP_STRONG_AUTH_NOT_SUPPORTED;
493                 return ld->ld_errno;
494         }
495
496         return LDAP_SUCCESS;
497 }
498
499 static int
500 guess_service_principal(
501         LDAP *ld,
502         const char *ldapServiceName,
503         const char *dnsHostName,
504         gss_name_t *principal )
505 {
506         gss_buffer_desc input_name;
507         /* GSS_KRB5_NT_PRINCIPAL_NAME */
508         gss_OID_desc nt_principal =
509         {10, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x01"};
510         const char *host = ld->ld_defconn->lconn_server->lud_host;
511         OM_uint32 minor_status;
512         int gss_rc;
513         int ret;
514         size_t svc_principal_size;
515         char *svc_principal = NULL;
516         const char *principal_fmt = NULL;
517         const char *str = NULL;
518         const char *givenstr = NULL;
519         const char *ignore = "not_defined_in_RFC4178@please_ignore";
520         int allow_remote = 0;
521
522         if (ldapServiceName) {
523                 givenstr = strchr(ldapServiceName, ':');
524                 if (givenstr && givenstr[1]) {
525                         givenstr++;
526                         if (strcmp(givenstr, ignore) == 0) {
527                                 givenstr = NULL;
528                         }
529                 } else {
530                         givenstr = NULL;
531                 }
532         }
533
534         if ( ld->ld_options.ldo_gssapi_options & LDAP_GSSAPI_OPT_ALLOW_REMOTE_PRINCIPAL ) {
535                 allow_remote = 1;
536         }
537
538         if (allow_remote && givenstr) {
539                 principal_fmt = "%s";
540                 svc_principal_size = strlen(givenstr) + 1;
541                 str = givenstr;
542
543         } else if (allow_remote && dnsHostName) {
544                 principal_fmt = "ldap/%s";
545                 svc_principal_size = STRLENOF("ldap/") + strlen(dnsHostName) + 1;
546                 str = dnsHostName;
547
548         } else {
549                 principal_fmt = "ldap/%s";
550                 svc_principal_size = STRLENOF("ldap/") + strlen(host) + 1;
551                 str = host;
552         }
553
554         svc_principal = (char*) ldap_memalloc(svc_principal_size * sizeof(char));
555         if ( svc_principal == NULL ) {
556                 ld->ld_errno = LDAP_NO_MEMORY;
557                 return ld->ld_errno;
558         }
559
560         ret = snprintf( svc_principal, svc_principal_size, principal_fmt, str );
561         if (ret < 0 || (size_t)ret >= svc_principal_size) {
562                 ld->ld_errno = LDAP_LOCAL_ERROR;
563                 return ld->ld_errno;
564         }
565
566         Debug( LDAP_DEBUG_TRACE, "principal for host[%s]: '%s'\n",
567                host, svc_principal, 0 );
568
569         input_name.value  = svc_principal;
570         input_name.length = (size_t)ret;
571
572         gss_rc = gss_import_name( &minor_status, &input_name, &nt_principal, principal );
573         ldap_memfree( svc_principal );
574         if ( gss_rc != GSS_S_COMPLETE ) {
575                 return map_gsserr2ldap( ld, GSS_C_NO_OID, gss_rc, minor_status );
576         }
577
578         return LDAP_SUCCESS;
579 }
580
581 void ldap_int_gssapi_close( LDAP *ld, LDAPConn *lc )
582 {
583         if ( lc && lc->lconn_gss_ctx ) {
584                 OM_uint32 minor_status;
585                 OM_uint32 ctx_flags = 0;
586                 gss_ctx_id_t old_gss_ctx = GSS_C_NO_CONTEXT;
587                 old_gss_ctx = (gss_ctx_id_t)lc->lconn_gss_ctx;
588
589                 gss_inquire_context(&minor_status,
590                                     old_gss_ctx,
591                                     NULL,
592                                     NULL,
593                                     NULL,
594                                     NULL,
595                                     &ctx_flags,
596                                     NULL,
597                                     NULL);
598
599                 if (!( ld->ld_options.ldo_gssapi_options & LDAP_GSSAPI_OPT_DO_NOT_FREE_GSS_CONTEXT )) {
600                         gss_delete_sec_context( &minor_status, &old_gss_ctx, GSS_C_NO_BUFFER );
601                 }
602                 lc->lconn_gss_ctx = GSS_C_NO_CONTEXT;
603
604                 if (ctx_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG)) {
605                         /* remove wrapping layer */
606                         sb_sasl_gssapi_remove( lc->lconn_sb );
607                 }
608         }
609 }
610
611 static void
612 ldap_int_gssapi_setup(
613         LDAP *ld,
614         LDAPConn *lc,
615         gss_ctx_id_t gss_ctx)
616 {
617         OM_uint32 minor_status;
618         OM_uint32 ctx_flags = 0;
619
620         ldap_int_gssapi_close( ld, lc );
621
622         gss_inquire_context(&minor_status,
623                             gss_ctx,
624                             NULL,
625                             NULL,
626                             NULL,
627                             NULL,
628                             &ctx_flags,
629                             NULL,
630                             NULL);
631
632         lc->lconn_gss_ctx = gss_ctx;
633
634         if (ctx_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG)) {
635                 /* setup wrapping layer */
636                 sb_sasl_gssapi_install( lc->lconn_sb, gss_ctx );
637         }
638 }
639
640 #ifdef LDAP_R_COMPILE
641 ldap_pvt_thread_mutex_t ldap_int_gssapi_mutex;
642 #endif
643
644 static int
645 ldap_int_gss_spnego_bind_s( LDAP *ld )
646 {
647         int rc;
648         int gss_rc;
649         OM_uint32 minor_status;
650         char *mechlist = NULL;
651         char *ldapServiceName = NULL;
652         char *dnsHostName = NULL;
653         gss_OID_set supported_mechs = GSS_C_NO_OID_SET;
654         int spnego_support = 0;
655 #define __SPNEGO_OID_LENGTH 6
656 #define __SPNEGO_OID "\053\006\001\005\005\002"
657         gss_OID_desc spnego_oid = {__SPNEGO_OID_LENGTH, __SPNEGO_OID};
658         gss_OID req_mech = GSS_C_NO_OID;
659         gss_OID ret_mech = GSS_C_NO_OID;
660         gss_ctx_id_t gss_ctx = GSS_C_NO_CONTEXT;
661         gss_name_t principal = GSS_C_NO_NAME;
662         OM_uint32 req_flags;
663         OM_uint32 ret_flags;
664         gss_buffer_desc input_token, output_token = GSS_C_EMPTY_BUFFER;
665         struct berval cred, *scred = NULL;
666
667         LDAP_MUTEX_LOCK( &ldap_int_gssapi_mutex );
668
669         /* get information from RootDSE entry */
670         rc = ldap_gssapi_get_rootdse_infos ( ld, &mechlist,
671                                              &ldapServiceName, &dnsHostName);
672         if ( rc != LDAP_SUCCESS ) {
673                 return rc;
674         }
675
676         /* check that the server supports GSS-SPNEGO */
677         rc = check_for_gss_spnego_support( ld, mechlist );
678         if ( rc != LDAP_SUCCESS ) {
679                 goto rc_error;
680         }
681
682         /* prepare new gss_ctx_id_t */
683         rc = guess_service_principal( ld, ldapServiceName, dnsHostName, &principal );
684         if ( rc != LDAP_SUCCESS ) {
685                 goto rc_error;
686         }
687
688         /* see if our gssapi library supports spnego */
689         gss_rc = gss_indicate_mechs( &minor_status, &supported_mechs );
690         if ( gss_rc != GSS_S_COMPLETE ) {
691                 goto gss_error;
692         }
693         gss_rc = gss_test_oid_set_member( &minor_status,
694                 &spnego_oid, supported_mechs, &spnego_support);
695         gss_release_oid_set( &minor_status, &supported_mechs);
696         if ( gss_rc != GSS_S_COMPLETE ) {
697                 goto gss_error;
698         }
699         if ( spnego_support != 0 ) {
700                 req_mech = &spnego_oid;
701         }
702
703         req_flags = ld->ld_options.ldo_gssapi_flags;
704         req_flags |= GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG;
705
706         /*
707          * loop around gss_init_sec_context() and ldap_sasl_bind_s()
708          */
709         input_token.value = NULL;
710         input_token.length = 0;
711         gss_rc = gss_init_sec_context(&minor_status,
712                                       GSS_C_NO_CREDENTIAL,
713                                       &gss_ctx,
714                                       principal,
715                                       req_mech,
716                                       req_flags,
717                                       0,
718                                       NULL,
719                                       &input_token,
720                                       &ret_mech,
721                                       &output_token,
722                                       &ret_flags,
723                                       NULL);
724         if ( gss_rc == GSS_S_COMPLETE ) {
725                 rc = LDAP_INAPPROPRIATE_AUTH;
726                 goto rc_error;
727         }
728         if ( gss_rc != GSS_S_CONTINUE_NEEDED ) {
729                 goto gss_error;
730         }
731         while (1) {
732                 cred.bv_val = (char *)output_token.value;
733                 cred.bv_len = output_token.length;
734                 rc = ldap_sasl_bind_s( ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred );
735                 gss_release_buffer( &minor_status, &output_token );
736                 if ( rc != LDAP_SUCCESS && rc != LDAP_SASL_BIND_IN_PROGRESS ) {
737                         goto rc_error;
738                 }
739
740                 if ( scred ) {
741                         input_token.value = scred->bv_val;
742                         input_token.length = scred->bv_len;
743                 } else {
744                         input_token.value = NULL;
745                         input_token.length = 0;
746                 }
747
748                 gss_rc = gss_init_sec_context(&minor_status,
749                                               GSS_C_NO_CREDENTIAL,
750                                               &gss_ctx,
751                                               principal,
752                                               req_mech,
753                                               req_flags,
754                                               0,
755                                               NULL,
756                                               &input_token,
757                                               &ret_mech,
758                                               &output_token,
759                                               &ret_flags,
760                                               NULL);
761                 if ( scred ) {
762                         ber_bvfree( scred );
763                 }
764                 if ( gss_rc == GSS_S_COMPLETE ) {
765                         gss_release_buffer( &minor_status, &output_token );
766                         break;
767                 }
768
769                 if ( gss_rc != GSS_S_CONTINUE_NEEDED ) {
770                         goto gss_error;
771                 }
772         }
773
774         ldap_int_gssapi_setup( ld, ld->ld_defconn, gss_ctx);
775         gss_ctx = GSS_C_NO_CONTEXT;
776
777         rc = LDAP_SUCCESS;
778         goto rc_error;
779
780 gss_error:
781         rc = map_gsserr2ldap( ld, 
782                               (ret_mech != GSS_C_NO_OID ? ret_mech : req_mech ),
783                               gss_rc, minor_status );
784 rc_error:
785         LDAP_MUTEX_UNLOCK( &ldap_int_gssapi_mutex );
786         LDAP_FREE( mechlist );
787         LDAP_FREE( ldapServiceName );
788         LDAP_FREE( dnsHostName );
789         gss_release_buffer( &minor_status, &output_token );
790         if ( gss_ctx != GSS_C_NO_CONTEXT ) {
791                 gss_delete_sec_context( &minor_status, &gss_ctx, GSS_C_NO_BUFFER );
792         }
793         if ( principal != GSS_C_NO_NAME ) {
794                 gss_release_name( &minor_status, &principal );
795         }
796         return rc;
797 }
798
799 int
800 ldap_int_gssapi_config( struct ldapoptions *lo, int option, const char *arg )
801 {
802         int ok = 0;
803
804         switch( option ) {
805         case LDAP_OPT_SIGN:
806
807                 if (!arg) {
808                 } else if (strcasecmp(arg, "on") == 0) {
809                         ok = 1;
810                 } else if (strcasecmp(arg, "yes") == 0) {
811                         ok = 1;
812                 } else if (strcasecmp(arg, "true") == 0) {
813                         ok = 1;
814
815                 }
816                 if (ok) {
817                         lo->ldo_gssapi_flags |= GSS_C_INTEG_FLAG;
818                 }
819
820                 return 0;
821
822         case LDAP_OPT_ENCRYPT:
823
824                 if (!arg) {
825                 } else if (strcasecmp(arg, "on") == 0) {
826                         ok = 1;
827                 } else if (strcasecmp(arg, "yes") == 0) {
828                         ok = 1;
829                 } else if (strcasecmp(arg, "true") == 0) {
830                         ok = 1;
831                 }
832
833                 if (ok) {
834                         lo->ldo_gssapi_flags |= GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG;
835                 }
836
837                 return 0;
838
839         case LDAP_OPT_X_GSSAPI_ALLOW_REMOTE_PRINCIPAL:
840
841                 if (!arg) {
842                 } else if (strcasecmp(arg, "on") == 0) {
843                         ok = 1;
844                 } else if (strcasecmp(arg, "yes") == 0) {
845                         ok = 1;
846                 } else if (strcasecmp(arg, "true") == 0) {
847                         ok = 1;
848                 }
849
850                 if (ok) {
851                         lo->ldo_gssapi_options |= LDAP_GSSAPI_OPT_ALLOW_REMOTE_PRINCIPAL;
852                 }
853
854                 return 0;
855         }
856
857         return -1;
858 }
859
860 int
861 ldap_int_gssapi_get_option( LDAP *ld, int option, void *arg )
862 {
863         if ( ld == NULL )
864                 return -1;
865
866         switch ( option ) {
867         case LDAP_OPT_SSPI_FLAGS:
868                 * (unsigned *) arg = (unsigned) ld->ld_options.ldo_gssapi_flags;
869                 break;
870
871         case LDAP_OPT_SIGN:
872                 if ( ld->ld_options.ldo_gssapi_flags & GSS_C_INTEG_FLAG ) {
873                         * (int *) arg = (int)-1;
874                 } else {
875                         * (int *) arg = (int)0;
876                 }
877                 break;
878
879         case LDAP_OPT_ENCRYPT:
880                 if ( ld->ld_options.ldo_gssapi_flags & GSS_C_CONF_FLAG ) {
881                         * (int *) arg = (int)-1;
882                 } else {
883                         * (int *) arg = (int)0;
884                 }
885                 break;
886
887         case LDAP_OPT_SASL_METHOD:
888                 * (char **) arg = LDAP_STRDUP("GSS-SPNEGO");
889                 break;
890
891         case LDAP_OPT_SECURITY_CONTEXT:
892                 if ( ld->ld_defconn && ld->ld_defconn->lconn_gss_ctx ) {
893                         * (gss_ctx_id_t *) arg = (gss_ctx_id_t)ld->ld_defconn->lconn_gss_ctx;
894                 } else {
895                         * (gss_ctx_id_t *) arg = GSS_C_NO_CONTEXT;
896                 }
897                 break;
898
899         case LDAP_OPT_X_GSSAPI_DO_NOT_FREE_CONTEXT:
900                 if ( ld->ld_options.ldo_gssapi_options & LDAP_GSSAPI_OPT_DO_NOT_FREE_GSS_CONTEXT ) {
901                         * (int *) arg = (int)-1;
902                 } else {
903                         * (int *) arg = (int)0;
904                 }
905                 break;
906
907         case LDAP_OPT_X_GSSAPI_ALLOW_REMOTE_PRINCIPAL:
908                 if ( ld->ld_options.ldo_gssapi_options & LDAP_GSSAPI_OPT_ALLOW_REMOTE_PRINCIPAL ) {
909                         * (int *) arg = (int)-1;
910                 } else {
911                         * (int *) arg = (int)0;
912                 }
913                 break;
914
915         default:
916                 return -1;
917         }
918
919         return 0;
920 }
921
922 int
923 ldap_int_gssapi_set_option( LDAP *ld, int option, void *arg )
924 {
925         if ( ld == NULL )
926                 return -1;
927
928         switch ( option ) {
929         case LDAP_OPT_SSPI_FLAGS:
930                 if ( arg != LDAP_OPT_OFF ) {
931                         ld->ld_options.ldo_gssapi_flags = * (unsigned *)arg;
932                 }
933                 break;
934
935         case LDAP_OPT_SIGN:
936                 if ( arg != LDAP_OPT_OFF ) {
937                         ld->ld_options.ldo_gssapi_flags |= GSS_C_INTEG_FLAG;
938                 }
939                 break;
940
941         case LDAP_OPT_ENCRYPT:
942                 if ( arg != LDAP_OPT_OFF ) {
943                         ld->ld_options.ldo_gssapi_flags |= GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG;
944                 }
945                 break;
946
947         case LDAP_OPT_SASL_METHOD:
948                 if ( arg != LDAP_OPT_OFF ) {
949                         const char *m = (const char *)arg;
950                         if ( strcmp( "GSS-SPNEGO", m ) != 0 ) {
951                                 /* we currently only support GSS-SPNEGO */
952                                 return -1;
953                         }
954                 }
955                 break;
956
957         case LDAP_OPT_SECURITY_CONTEXT:
958                 if ( arg != LDAP_OPT_OFF && ld->ld_defconn) {
959                         ldap_int_gssapi_setup( ld, ld->ld_defconn,
960                                                (gss_ctx_id_t) arg);
961                 }
962                 break;
963
964         case LDAP_OPT_X_GSSAPI_DO_NOT_FREE_CONTEXT:
965                 if ( arg != LDAP_OPT_OFF ) {
966                         ld->ld_options.ldo_gssapi_options |= LDAP_GSSAPI_OPT_DO_NOT_FREE_GSS_CONTEXT;
967                 }
968                 break;
969
970         case LDAP_OPT_X_GSSAPI_ALLOW_REMOTE_PRINCIPAL:
971                 if ( arg != LDAP_OPT_OFF ) {
972                         ld->ld_options.ldo_gssapi_options |= LDAP_GSSAPI_OPT_ALLOW_REMOTE_PRINCIPAL;
973                 }
974                 break;
975
976         default:
977                 return -1;
978         }
979
980         return 0;
981 }
982
983 #else /* HAVE_GSSAPI */
984 #define ldap_int_gss_spnego_bind_s(ld) LDAP_NOT_SUPPORTED
985 #endif /* HAVE_GSSAPI */
986
987 int
988 ldap_gssapi_bind(
989         LDAP *ld,
990         LDAP_CONST char *dn,
991         LDAP_CONST char *creds )
992 {
993         return LDAP_NOT_SUPPORTED;
994 }
995
996 int
997 ldap_gssapi_bind_s(
998         LDAP *ld,
999         LDAP_CONST char *dn,
1000         LDAP_CONST char *creds )
1001 {
1002         if ( dn != NULL ) {
1003                 return LDAP_NOT_SUPPORTED;
1004         }
1005
1006         if ( creds != NULL ) {
1007                 return LDAP_NOT_SUPPORTED;
1008         }
1009
1010         return ldap_int_gss_spnego_bind_s(ld);
1011 }