Back to index

openldap  2.4.31
gssapi.c
Go to the documentation of this file.
00001 /* $OpenLDAP$ */
00002 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
00003  *
00004  * Copyright 1998-2012 The OpenLDAP Foundation.
00005  * All rights reserved.
00006  *
00007  * Author: Stefan Metzmacher <metze@sernet.de>
00008  *
00009  * Redistribution and use in source and binary forms, with or without
00010  * modification, are permitted only as authorized by the OpenLDAP
00011  * Public License.
00012  *
00013  * A copy of this license is available in the file LICENSE in the
00014  * top-level directory of the distribution or, alternatively, at
00015  * <http://www.OpenLDAP.org/license.html>.
00016  */
00017 
00018 #include "portable.h"
00019 
00020 #include <stdio.h>
00021 
00022 #include <ac/socket.h>
00023 #include <ac/stdlib.h>
00024 #include <ac/string.h>
00025 #include <ac/time.h>
00026 #include <ac/errno.h>
00027 #include <ac/ctype.h>
00028 #include <ac/unistd.h>
00029 
00030 #ifdef HAVE_LIMITS_H
00031 #include <limits.h>
00032 #endif
00033 
00034 #include "ldap-int.h"
00035 
00036 #ifdef HAVE_GSSAPI
00037 
00038 #ifdef HAVE_GSSAPI_GSSAPI_H
00039 #include <gssapi/gssapi.h>
00040 #else
00041 #include <gssapi.h>
00042 #endif
00043 
00044 static char *
00045 gsserrstr(
00046        char *buf,
00047        ber_len_t buf_len,
00048        gss_OID mech,
00049        int gss_rc,
00050        OM_uint32 minor_status )
00051 {
00052        OM_uint32 min2;
00053        gss_buffer_desc mech_msg = GSS_C_EMPTY_BUFFER;
00054        gss_buffer_desc gss_msg = GSS_C_EMPTY_BUFFER;
00055        gss_buffer_desc minor_msg = GSS_C_EMPTY_BUFFER;
00056        OM_uint32 msg_ctx = 0;
00057 
00058        if (buf == NULL) {
00059               return NULL;
00060        }
00061 
00062        if (buf_len == 0) {
00063               return NULL;
00064        }
00065 
00066 #ifdef HAVE_GSS_OID_TO_STR
00067        gss_oid_to_str(&min2, mech, &mech_msg);
00068 #endif
00069        gss_display_status(&min2, gss_rc, GSS_C_GSS_CODE,
00070                         mech, &msg_ctx, &gss_msg);
00071        gss_display_status(&min2, minor_status, GSS_C_MECH_CODE,
00072                         mech, &msg_ctx, &minor_msg);
00073 
00074        snprintf(buf, buf_len, "gss_rc[%d:%*s] mech[%*s] minor[%u:%*s]",
00075                gss_rc, (int)gss_msg.length,
00076                (const char *)(gss_msg.value?gss_msg.value:""),
00077                (int)mech_msg.length,
00078                (const char *)(mech_msg.value?mech_msg.value:""),
00079                minor_status, (int)minor_msg.length,
00080                (const char *)(minor_msg.value?minor_msg.value:""));
00081 
00082        gss_release_buffer(&min2, &mech_msg);
00083        gss_release_buffer(&min2, &gss_msg);
00084        gss_release_buffer(&min2, &minor_msg);
00085 
00086        buf[buf_len-1] = '\0';
00087 
00088        return buf;
00089 }
00090 
00091 static void
00092 sb_sasl_gssapi_init(
00093        struct sb_sasl_generic_data *p,
00094        ber_len_t *min_send,
00095        ber_len_t *max_send,
00096        ber_len_t *max_recv )
00097 {
00098        gss_ctx_id_t gss_ctx = (gss_ctx_id_t)p->ops_private;
00099        int gss_rc;
00100        OM_uint32 minor_status;
00101        gss_OID ctx_mech = GSS_C_NO_OID;
00102        OM_uint32 ctx_flags = 0;
00103        int conf_req_flag = 0;
00104        OM_uint32 max_input_size;
00105 
00106        gss_inquire_context(&minor_status,
00107                          gss_ctx,
00108                          NULL,
00109                          NULL,
00110                          NULL,
00111                          &ctx_mech,
00112                          &ctx_flags,
00113                          NULL,
00114                          NULL);
00115 
00116        if (ctx_flags & (GSS_C_CONF_FLAG)) {
00117               conf_req_flag = 1;
00118        }
00119 
00120 #if defined(HAVE_CYRUS_SASL)
00121 #define SEND_PREALLOC_SIZE  SASL_MIN_BUFF_SIZE
00122 #else
00123 #define SEND_PREALLOC_SIZE      4096
00124 #endif
00125 #define SEND_MAX_WIRE_SIZE  0x00A00000
00126 #define RECV_MAX_WIRE_SIZE  0x0FFFFFFF
00127 #define FALLBACK_SEND_MAX_SIZE     0x009FFFB8 /* from MIT 1.5.x */
00128 
00129        gss_rc = gss_wrap_size_limit(&minor_status, gss_ctx,
00130                                  conf_req_flag, GSS_C_QOP_DEFAULT,
00131                                  SEND_MAX_WIRE_SIZE, &max_input_size);
00132        if ( gss_rc != GSS_S_COMPLETE ) {
00133               char msg[256];
00134               ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
00135                             "sb_sasl_gssapi_init: failed to wrap size limit: %s\n",
00136                             gsserrstr( msg, sizeof(msg), ctx_mech, gss_rc, minor_status ) );
00137               ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
00138                             "sb_sasl_gssapi_init: fallback to default wrap size limit\n");
00139               /*
00140                * some libgssglue/libgssapi versions
00141                * have a broken gss_wrap_size_limit()
00142                * implementation
00143                */
00144               max_input_size = FALLBACK_SEND_MAX_SIZE;
00145        }
00146 
00147        *min_send = SEND_PREALLOC_SIZE;
00148        *max_send = max_input_size;
00149        *max_recv = RECV_MAX_WIRE_SIZE;
00150 }
00151 
00152 static ber_int_t
00153 sb_sasl_gssapi_encode(
00154        struct sb_sasl_generic_data *p,
00155        unsigned char *buf,
00156        ber_len_t len,
00157        Sockbuf_Buf *dst )
00158 {
00159        gss_ctx_id_t gss_ctx = (gss_ctx_id_t)p->ops_private;
00160        int gss_rc;
00161        OM_uint32 minor_status;
00162        gss_buffer_desc unwrapped, wrapped;
00163        gss_OID ctx_mech = GSS_C_NO_OID;
00164        OM_uint32 ctx_flags = 0;
00165        int conf_req_flag = 0;
00166        int conf_state;
00167        unsigned char *b;
00168        ber_len_t pkt_len;
00169 
00170        unwrapped.value             = buf;
00171        unwrapped.length     = len;
00172 
00173        gss_inquire_context(&minor_status,
00174                          gss_ctx,
00175                          NULL,
00176                          NULL,
00177                          NULL,
00178                          &ctx_mech,
00179                          &ctx_flags,
00180                          NULL,
00181                          NULL);
00182 
00183        if (ctx_flags & (GSS_C_CONF_FLAG)) {
00184               conf_req_flag = 1;
00185        }
00186 
00187        gss_rc = gss_wrap(&minor_status, gss_ctx,
00188                        conf_req_flag, GSS_C_QOP_DEFAULT,
00189                        &unwrapped, &conf_state,
00190                        &wrapped);
00191        if ( gss_rc != GSS_S_COMPLETE ) {
00192               char msg[256];
00193               ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
00194                             "sb_sasl_gssapi_encode: failed to encode packet: %s\n",
00195                             gsserrstr( msg, sizeof(msg), ctx_mech, gss_rc, minor_status ) );
00196               return -1;
00197        }
00198 
00199        if ( conf_req_flag && conf_state == 0 ) {
00200               ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
00201                             "sb_sasl_gssapi_encode: GSS_C_CONF_FLAG was ignored by our gss_wrap()\n" );
00202               return -1;
00203        }
00204 
00205        pkt_len = 4 + wrapped.length;
00206 
00207        /* Grow the packet buffer if neccessary */
00208        if ( dst->buf_size < pkt_len &&
00209               ber_pvt_sb_grow_buffer( dst, pkt_len ) < 0 )
00210        {
00211               ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
00212                             "sb_sasl_gssapi_encode: failed to grow the buffer to %lu bytes\n",
00213                             pkt_len );
00214               return -1;
00215        }
00216 
00217        dst->buf_end = pkt_len;
00218 
00219        b = (unsigned char *)dst->buf_base;
00220 
00221        b[0] = (unsigned char)(wrapped.length >> 24);
00222        b[1] = (unsigned char)(wrapped.length >> 16);
00223        b[2] = (unsigned char)(wrapped.length >>  8);
00224        b[3] = (unsigned char)(wrapped.length >>  0);
00225 
00226        /* copy the wrapped blob to the right location */
00227        memcpy(b + 4, wrapped.value, wrapped.length);
00228 
00229        gss_release_buffer(&minor_status, &wrapped);
00230 
00231        return 0;
00232 }
00233 
00234 static ber_int_t
00235 sb_sasl_gssapi_decode(
00236        struct sb_sasl_generic_data *p,
00237        const Sockbuf_Buf *src,
00238        Sockbuf_Buf *dst )
00239 {
00240        gss_ctx_id_t gss_ctx = (gss_ctx_id_t)p->ops_private;
00241        int gss_rc;
00242        OM_uint32 minor_status;
00243        gss_buffer_desc unwrapped, wrapped;
00244        gss_OID ctx_mech = GSS_C_NO_OID;
00245        OM_uint32 ctx_flags = 0;
00246        int conf_req_flag = 0;
00247        int conf_state;
00248        unsigned char *b;
00249 
00250        wrapped.value = src->buf_base + 4;
00251        wrapped.length       = src->buf_end - 4;
00252 
00253        gss_inquire_context(&minor_status,
00254                          gss_ctx,
00255                          NULL,
00256                          NULL,
00257                          NULL,
00258                          &ctx_mech,
00259                          &ctx_flags,
00260                          NULL,
00261                          NULL);
00262 
00263        if (ctx_flags & (GSS_C_CONF_FLAG)) {
00264               conf_req_flag = 1;
00265        }
00266 
00267        gss_rc = gss_unwrap(&minor_status, gss_ctx,
00268                          &wrapped, &unwrapped,
00269                          &conf_state, GSS_C_QOP_DEFAULT);
00270        if ( gss_rc != GSS_S_COMPLETE ) {
00271               char msg[256];
00272               ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
00273                             "sb_sasl_gssapi_decode: failed to decode packet: %s\n",
00274                             gsserrstr( msg, sizeof(msg), ctx_mech, gss_rc, minor_status ) );
00275               return -1;
00276        }
00277 
00278        if ( conf_req_flag && conf_state == 0 ) {
00279               ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
00280                             "sb_sasl_gssapi_encode: GSS_C_CONF_FLAG was ignored by our peer\n" );
00281               return -1;
00282        }
00283 
00284        /* Grow the packet buffer if neccessary */
00285        if ( dst->buf_size < unwrapped.length &&
00286               ber_pvt_sb_grow_buffer( dst, unwrapped.length ) < 0 )
00287        {
00288               ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
00289                             "sb_sasl_gssapi_decode: failed to grow the buffer to %lu bytes\n",
00290                             unwrapped.length );
00291               return -1;
00292        }
00293 
00294        dst->buf_end = unwrapped.length;
00295 
00296        b = (unsigned char *)dst->buf_base;
00297 
00298        /* copy the wrapped blob to the right location */
00299        memcpy(b, unwrapped.value, unwrapped.length);
00300 
00301        gss_release_buffer(&minor_status, &unwrapped);
00302 
00303        return 0;
00304 }
00305 
00306 static void
00307 sb_sasl_gssapi_reset_buf(
00308        struct sb_sasl_generic_data *p,
00309        Sockbuf_Buf *buf )
00310 {
00311        ber_pvt_sb_buf_destroy( buf );
00312 }
00313 
00314 static void
00315 sb_sasl_gssapi_fini( struct sb_sasl_generic_data *p )
00316 {
00317 }
00318 
00319 static const struct sb_sasl_generic_ops sb_sasl_gssapi_ops = {
00320        sb_sasl_gssapi_init,
00321        sb_sasl_gssapi_encode,
00322        sb_sasl_gssapi_decode,
00323        sb_sasl_gssapi_reset_buf,
00324        sb_sasl_gssapi_fini
00325 };
00326 
00327 static int
00328 sb_sasl_gssapi_install(
00329        Sockbuf *sb,
00330        gss_ctx_id_t gss_ctx )
00331 {
00332        struct sb_sasl_generic_install install_arg;
00333 
00334        install_arg.ops             = &sb_sasl_gssapi_ops;
00335        install_arg.ops_private = gss_ctx;
00336 
00337        return ldap_pvt_sasl_generic_install( sb, &install_arg );
00338 }
00339 
00340 static void
00341 sb_sasl_gssapi_remove( Sockbuf *sb )
00342 {
00343        ldap_pvt_sasl_generic_remove( sb );
00344 }
00345 
00346 static int
00347 map_gsserr2ldap(
00348        LDAP *ld,
00349        gss_OID mech,
00350        int gss_rc,
00351        OM_uint32 minor_status )
00352 {
00353        char msg[256];
00354 
00355        Debug( LDAP_DEBUG_ANY, "%s\n",
00356               gsserrstr( msg, sizeof(msg), mech, gss_rc, minor_status ),
00357               NULL, NULL );
00358 
00359        if (gss_rc == GSS_S_COMPLETE) {
00360               ld->ld_errno = LDAP_SUCCESS;
00361        } else if (GSS_CALLING_ERROR(gss_rc)) {
00362               ld->ld_errno = LDAP_LOCAL_ERROR;
00363        } else if (GSS_ROUTINE_ERROR(gss_rc)) {
00364               ld->ld_errno = LDAP_INAPPROPRIATE_AUTH;
00365        } else if (gss_rc == GSS_S_CONTINUE_NEEDED) {
00366               ld->ld_errno = LDAP_SASL_BIND_IN_PROGRESS;
00367        } else if (GSS_SUPPLEMENTARY_INFO(gss_rc)) {
00368               ld->ld_errno = LDAP_AUTH_UNKNOWN;
00369        } else if (GSS_ERROR(gss_rc)) {
00370               ld->ld_errno = LDAP_AUTH_UNKNOWN;
00371        } else {
00372               ld->ld_errno = LDAP_OTHER;
00373        }
00374 
00375        return ld->ld_errno;
00376 }
00377 
00378 
00379 static int
00380 ldap_gssapi_get_rootdse_infos (
00381        LDAP *ld,
00382        char **pmechlist,
00383        char **pldapServiceName,
00384        char **pdnsHostName )
00385 {
00386        /* we need to query the server for supported mechs anyway */
00387        LDAPMessage *res, *e;
00388        char *attrs[] = {
00389               "supportedSASLMechanisms",
00390               "ldapServiceName",
00391               "dnsHostName",
00392               NULL
00393        };
00394        char **values, *mechlist;
00395        char *ldapServiceName = NULL;
00396        char *dnsHostName = NULL;
00397        int rc;
00398 
00399        Debug( LDAP_DEBUG_TRACE, "ldap_gssapi_get_rootdse_infos\n", 0, 0, 0 );
00400 
00401        rc = ldap_search_s( ld, "", LDAP_SCOPE_BASE,
00402               NULL, attrs, 0, &res );
00403 
00404        if ( rc != LDAP_SUCCESS ) {
00405               return ld->ld_errno;
00406        }
00407 
00408        e = ldap_first_entry( ld, res );
00409        if ( e == NULL ) {
00410               ldap_msgfree( res );
00411               if ( ld->ld_errno == LDAP_SUCCESS ) {
00412                      ld->ld_errno = LDAP_NO_SUCH_OBJECT;
00413               }
00414               return ld->ld_errno;
00415        }
00416 
00417        values = ldap_get_values( ld, e, "supportedSASLMechanisms" );
00418        if ( values == NULL ) {
00419               ldap_msgfree( res );
00420               ld->ld_errno = LDAP_NO_SUCH_ATTRIBUTE;
00421               return ld->ld_errno;
00422        }
00423 
00424        mechlist = ldap_charray2str( values, " " );
00425        if ( mechlist == NULL ) {
00426               LDAP_VFREE( values );
00427               ldap_msgfree( res );
00428               ld->ld_errno = LDAP_NO_MEMORY;
00429               return ld->ld_errno;
00430        }
00431 
00432        LDAP_VFREE( values );
00433 
00434        values = ldap_get_values( ld, e, "ldapServiceName" );
00435        if ( values == NULL ) {
00436               goto get_dns_host_name;
00437        }
00438 
00439        ldapServiceName = ldap_charray2str( values, " " );
00440        if ( ldapServiceName == NULL ) {
00441               LDAP_FREE( mechlist );
00442               LDAP_VFREE( values );
00443               ldap_msgfree( res );
00444               ld->ld_errno = LDAP_NO_MEMORY;
00445               return ld->ld_errno;
00446        }
00447        LDAP_VFREE( values );
00448 
00449 get_dns_host_name:
00450 
00451        values = ldap_get_values( ld, e, "dnsHostName" );
00452        if ( values == NULL ) {
00453               goto done;
00454        }
00455 
00456        dnsHostName = ldap_charray2str( values, " " );
00457        if ( dnsHostName == NULL ) {
00458               LDAP_FREE( mechlist );
00459               LDAP_FREE( ldapServiceName );
00460               LDAP_VFREE( values );
00461               ldap_msgfree( res );
00462               ld->ld_errno = LDAP_NO_MEMORY;
00463               return ld->ld_errno;
00464        }
00465        LDAP_VFREE( values );
00466 
00467 done:
00468        ldap_msgfree( res );
00469 
00470        *pmechlist = mechlist;
00471        *pldapServiceName = ldapServiceName;
00472        *pdnsHostName = dnsHostName;
00473 
00474        return LDAP_SUCCESS;
00475 }
00476 
00477 
00478 static int check_for_gss_spnego_support( LDAP *ld, const char *mechs_str )
00479 {
00480        int rc;
00481        char **mechs_list = NULL;
00482 
00483        mechs_list = ldap_str2charray( mechs_str, " " );
00484        if ( mechs_list == NULL ) {
00485               ld->ld_errno = LDAP_NO_MEMORY;
00486               return ld->ld_errno;
00487        }
00488 
00489        rc = ldap_charray_inlist( mechs_list, "GSS-SPNEGO" );
00490        ldap_charray_free( mechs_list );
00491        if ( rc != 1) {
00492               ld->ld_errno = LDAP_STRONG_AUTH_NOT_SUPPORTED;
00493               return ld->ld_errno;
00494        }
00495 
00496        return LDAP_SUCCESS;
00497 }
00498 
00499 static int
00500 guess_service_principal(
00501        LDAP *ld,
00502        const char *ldapServiceName,
00503        const char *dnsHostName,
00504        gss_name_t *principal )
00505 {
00506        gss_buffer_desc input_name;
00507        /* GSS_KRB5_NT_PRINCIPAL_NAME */
00508        gss_OID_desc nt_principal =
00509        {10, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x01"};
00510        const char *host = ld->ld_defconn->lconn_server->lud_host;
00511        OM_uint32 minor_status;
00512        int gss_rc;
00513        int ret;
00514        size_t svc_principal_size;
00515        char *svc_principal = NULL;
00516        const char *principal_fmt = NULL;
00517        const char *str = NULL;
00518        const char *givenstr = NULL;
00519        const char *ignore = "not_defined_in_RFC4178@please_ignore";
00520        int allow_remote = 0;
00521 
00522        if (ldapServiceName) {
00523               givenstr = strchr(ldapServiceName, ':');
00524               if (givenstr && givenstr[1]) {
00525                      givenstr++;
00526                      if (strcmp(givenstr, ignore) == 0) {
00527                             givenstr = NULL;
00528                      }
00529               } else {
00530                      givenstr = NULL;
00531               }
00532        }
00533 
00534        if ( ld->ld_options.ldo_gssapi_options & LDAP_GSSAPI_OPT_ALLOW_REMOTE_PRINCIPAL ) {
00535               allow_remote = 1;
00536        }
00537 
00538        if (allow_remote && givenstr) {
00539               principal_fmt = "%s";
00540               svc_principal_size = strlen(givenstr) + 1;
00541               str = givenstr;
00542 
00543        } else if (allow_remote && dnsHostName) {
00544               principal_fmt = "ldap/%s";
00545               svc_principal_size = STRLENOF("ldap/") + strlen(dnsHostName) + 1;
00546               str = dnsHostName;
00547 
00548        } else {
00549               principal_fmt = "ldap/%s";
00550               svc_principal_size = STRLENOF("ldap/") + strlen(host) + 1;
00551               str = host;
00552        }
00553 
00554        svc_principal = (char*) ldap_memalloc(svc_principal_size * sizeof(char));
00555        if ( svc_principal == NULL ) {
00556               ld->ld_errno = LDAP_NO_MEMORY;
00557               return ld->ld_errno;
00558        }
00559 
00560        ret = snprintf( svc_principal, svc_principal_size, principal_fmt, str );
00561        if (ret < 0 || (size_t)ret >= svc_principal_size) {
00562               ld->ld_errno = LDAP_LOCAL_ERROR;
00563               return ld->ld_errno;
00564        }
00565 
00566        Debug( LDAP_DEBUG_TRACE, "principal for host[%s]: '%s'\n",
00567               host, svc_principal, 0 );
00568 
00569        input_name.value  = svc_principal;
00570        input_name.length = (size_t)ret;
00571 
00572        gss_rc = gss_import_name( &minor_status, &input_name, &nt_principal, principal );
00573        ldap_memfree( svc_principal );
00574        if ( gss_rc != GSS_S_COMPLETE ) {
00575               return map_gsserr2ldap( ld, GSS_C_NO_OID, gss_rc, minor_status );
00576        }
00577 
00578        return LDAP_SUCCESS;
00579 }
00580 
00581 void ldap_int_gssapi_close( LDAP *ld, LDAPConn *lc )
00582 {
00583        if ( lc && lc->lconn_gss_ctx ) {
00584               OM_uint32 minor_status;
00585               OM_uint32 ctx_flags = 0;
00586               gss_ctx_id_t old_gss_ctx = GSS_C_NO_CONTEXT;
00587               old_gss_ctx = (gss_ctx_id_t)lc->lconn_gss_ctx;
00588 
00589               gss_inquire_context(&minor_status,
00590                                 old_gss_ctx,
00591                                 NULL,
00592                                 NULL,
00593                                 NULL,
00594                                 NULL,
00595                                 &ctx_flags,
00596                                 NULL,
00597                                 NULL);
00598 
00599               if (!( ld->ld_options.ldo_gssapi_options & LDAP_GSSAPI_OPT_DO_NOT_FREE_GSS_CONTEXT )) {
00600                      gss_delete_sec_context( &minor_status, &old_gss_ctx, GSS_C_NO_BUFFER );
00601               }
00602               lc->lconn_gss_ctx = GSS_C_NO_CONTEXT;
00603 
00604               if (ctx_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG)) {
00605                      /* remove wrapping layer */
00606                      sb_sasl_gssapi_remove( lc->lconn_sb );
00607               }
00608        }
00609 }
00610 
00611 static void
00612 ldap_int_gssapi_setup(
00613        LDAP *ld,
00614        LDAPConn *lc,
00615        gss_ctx_id_t gss_ctx)
00616 {
00617        OM_uint32 minor_status;
00618        OM_uint32 ctx_flags = 0;
00619 
00620        ldap_int_gssapi_close( ld, lc );
00621 
00622        gss_inquire_context(&minor_status,
00623                          gss_ctx,
00624                          NULL,
00625                          NULL,
00626                          NULL,
00627                          NULL,
00628                          &ctx_flags,
00629                          NULL,
00630                          NULL);
00631 
00632        lc->lconn_gss_ctx = gss_ctx;
00633 
00634        if (ctx_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG)) {
00635               /* setup wrapping layer */
00636               sb_sasl_gssapi_install( lc->lconn_sb, gss_ctx );
00637        }
00638 }
00639 
00640 #ifdef LDAP_R_COMPILE
00641 ldap_pvt_thread_mutex_t ldap_int_gssapi_mutex;
00642 #endif
00643 
00644 static int
00645 ldap_int_gss_spnego_bind_s( LDAP *ld )
00646 {
00647        int rc;
00648        int gss_rc;
00649        OM_uint32 minor_status;
00650        char *mechlist = NULL;
00651        char *ldapServiceName = NULL;
00652        char *dnsHostName = NULL;
00653        gss_OID_set supported_mechs = GSS_C_NO_OID_SET;
00654        int spnego_support = 0;
00655 #define       __SPNEGO_OID_LENGTH 6
00656 #define       __SPNEGO_OID "\053\006\001\005\005\002"
00657        gss_OID_desc spnego_oid = {__SPNEGO_OID_LENGTH, __SPNEGO_OID};
00658        gss_OID req_mech = GSS_C_NO_OID;
00659        gss_OID ret_mech = GSS_C_NO_OID;
00660        gss_ctx_id_t gss_ctx = GSS_C_NO_CONTEXT;
00661        gss_name_t principal = GSS_C_NO_NAME;
00662        OM_uint32 req_flags;
00663        OM_uint32 ret_flags;
00664        gss_buffer_desc input_token, output_token = GSS_C_EMPTY_BUFFER;
00665        struct berval cred, *scred = NULL;
00666 
00667        LDAP_MUTEX_LOCK( &ldap_int_gssapi_mutex );
00668 
00669        /* get information from RootDSE entry */
00670        rc = ldap_gssapi_get_rootdse_infos ( ld, &mechlist,
00671                                         &ldapServiceName, &dnsHostName);
00672        if ( rc != LDAP_SUCCESS ) {
00673               return rc;
00674        }
00675 
00676        /* check that the server supports GSS-SPNEGO */
00677        rc = check_for_gss_spnego_support( ld, mechlist );
00678        if ( rc != LDAP_SUCCESS ) {
00679               goto rc_error;
00680        }
00681 
00682        /* prepare new gss_ctx_id_t */
00683        rc = guess_service_principal( ld, ldapServiceName, dnsHostName, &principal );
00684        if ( rc != LDAP_SUCCESS ) {
00685               goto rc_error;
00686        }
00687 
00688        /* see if our gssapi library supports spnego */
00689        gss_rc = gss_indicate_mechs( &minor_status, &supported_mechs );
00690        if ( gss_rc != GSS_S_COMPLETE ) {
00691               goto gss_error;
00692        }
00693        gss_rc = gss_test_oid_set_member( &minor_status,
00694               &spnego_oid, supported_mechs, &spnego_support);
00695        gss_release_oid_set( &minor_status, &supported_mechs);
00696        if ( gss_rc != GSS_S_COMPLETE ) {
00697               goto gss_error;
00698        }
00699        if ( spnego_support != 0 ) {
00700               req_mech = &spnego_oid;
00701        }
00702 
00703        req_flags = ld->ld_options.gssapi_flags;
00704        req_flags |= GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG;
00705 
00706        /*
00707         * loop around gss_init_sec_context() and ldap_sasl_bind_s()
00708         */
00709        input_token.value = NULL;
00710        input_token.length = 0;
00711        gss_rc = gss_init_sec_context(&minor_status,
00712                                   GSS_C_NO_CREDENTIAL,
00713                                   &gss_ctx,
00714                                   principal,
00715                                   req_mech,
00716                                   req_flags,
00717                                   0,
00718                                   NULL,
00719                                   &input_token,
00720                                   &ret_mech,
00721                                   &output_token,
00722                                   &ret_flags,
00723                                   NULL);
00724        if ( gss_rc == GSS_S_COMPLETE ) {
00725               rc = LDAP_INAPPROPRIATE_AUTH;
00726               goto rc_error;
00727        }
00728        if ( gss_rc != GSS_S_CONTINUE_NEEDED ) {
00729               goto gss_error;
00730        }
00731        while (1) {
00732               cred.bv_val = (char *)output_token.value;
00733               cred.bv_len = output_token.length;
00734               rc = ldap_sasl_bind_s( ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred );
00735               gss_release_buffer( &minor_status, &output_token );
00736               if ( rc != LDAP_SUCCESS && rc != LDAP_SASL_BIND_IN_PROGRESS ) {
00737                      goto rc_error;
00738               }
00739 
00740               if ( scred ) {
00741                      input_token.value = scred->bv_val;
00742                      input_token.length = scred->bv_len;
00743               } else {
00744                      input_token.value = NULL;
00745                      input_token.length = 0;
00746               }
00747 
00748               gss_rc = gss_init_sec_context(&minor_status,
00749                                          GSS_C_NO_CREDENTIAL,
00750                                          &gss_ctx,
00751                                          principal,
00752                                          req_mech,
00753                                          req_flags,
00754                                          0,
00755                                          NULL,
00756                                          &input_token,
00757                                          &ret_mech,
00758                                          &output_token,
00759                                          &ret_flags,
00760                                          NULL);
00761               if ( scred ) {
00762                      ber_bvfree( scred );
00763               }
00764               if ( gss_rc == GSS_S_COMPLETE ) {
00765                      gss_release_buffer( &minor_status, &output_token );
00766                      break;
00767               }
00768 
00769               if ( gss_rc != GSS_S_CONTINUE_NEEDED ) {
00770                      goto gss_error;
00771               }
00772        }
00773 
00774        ldap_int_gssapi_setup( ld, ld->ld_defconn, gss_ctx);
00775        gss_ctx = GSS_C_NO_CONTEXT;
00776 
00777        rc = LDAP_SUCCESS;
00778        goto rc_error;
00779 
00780 gss_error:
00781        rc = map_gsserr2ldap( ld, 
00782                            (ret_mech != GSS_C_NO_OID ? ret_mech : req_mech ),
00783                            gss_rc, minor_status );
00784 rc_error:
00785        LDAP_MUTEX_UNLOCK( &ldap_int_gssapi_mutex );
00786        LDAP_FREE( mechlist );
00787        LDAP_FREE( ldapServiceName );
00788        LDAP_FREE( dnsHostName );
00789        gss_release_buffer( &minor_status, &output_token );
00790        if ( gss_ctx != GSS_C_NO_CONTEXT ) {
00791               gss_delete_sec_context( &minor_status, &gss_ctx, GSS_C_NO_BUFFER );
00792        }
00793        if ( principal != GSS_C_NO_NAME ) {
00794               gss_release_name( &minor_status, &principal );
00795        }
00796        return rc;
00797 }
00798 
00799 int
00800 ldap_int_gssapi_config( struct ldapoptions *lo, int option, const char *arg )
00801 {
00802        int ok = 0;
00803 
00804        switch( option ) {
00805        case LDAP_OPT_SIGN:
00806 
00807               if (!arg) {
00808               } else if (strcasecmp(arg, "on") == 0) {
00809                      ok = 1;
00810               } else if (strcasecmp(arg, "yes") == 0) {
00811                      ok = 1;
00812               } else if (strcasecmp(arg, "true") == 0) {
00813                      ok = 1;
00814 
00815               }
00816               if (ok) {
00817                      lo->ldo_gssapi_flags |= GSS_C_INTEG_FLAG;
00818               }
00819 
00820               return 0;
00821 
00822        case LDAP_OPT_ENCRYPT:
00823 
00824               if (!arg) {
00825               } else if (strcasecmp(arg, "on") == 0) {
00826                      ok = 1;
00827               } else if (strcasecmp(arg, "yes") == 0) {
00828                      ok = 1;
00829               } else if (strcasecmp(arg, "true") == 0) {
00830                      ok = 1;
00831               }
00832 
00833               if (ok) {
00834                      lo->ldo_gssapi_flags |= GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG;
00835               }
00836 
00837               return 0;
00838 
00839        case LDAP_OPT_X_GSSAPI_ALLOW_REMOTE_PRINCIPAL:
00840 
00841               if (!arg) {
00842               } else if (strcasecmp(arg, "on") == 0) {
00843                      ok = 1;
00844               } else if (strcasecmp(arg, "yes") == 0) {
00845                      ok = 1;
00846               } else if (strcasecmp(arg, "true") == 0) {
00847                      ok = 1;
00848               }
00849 
00850               if (ok) {
00851                      lo->ldo_gssapi_options |= LDAP_GSSAPI_OPT_ALLOW_REMOTE_PRINCIPAL;
00852               }
00853 
00854               return 0;
00855        }
00856 
00857        return -1;
00858 }
00859 
00860 int
00861 ldap_int_gssapi_get_option( LDAP *ld, int option, void *arg )
00862 {
00863        if ( ld == NULL )
00864               return -1;
00865 
00866        switch ( option ) {
00867        case LDAP_OPT_SSPI_FLAGS:
00868               * (unsigned *) arg = (unsigned) ld->ld_options.gssapi_flags;
00869               break;
00870 
00871        case LDAP_OPT_SIGN:
00872               if ( ld->ld_options.gssapi_flags & GSS_C_INTEG_FLAG ) {
00873                      * (int *) arg = (int)-1;
00874               } else {
00875                      * (int *) arg = (int)0;
00876               }
00877               break;
00878 
00879        case LDAP_OPT_ENCRYPT:
00880               if ( ld->ld_options.gssapi_flags & GSS_C_CONF_FLAG ) {
00881                      * (int *) arg = (int)-1;
00882               } else {
00883                      * (int *) arg = (int)0;
00884               }
00885               break;
00886 
00887        case LDAP_OPT_SASL_METHOD:
00888               * (char **) arg = LDAP_STRDUP("GSS-SPNEGO");
00889               break;
00890 
00891        case LDAP_OPT_SECURITY_CONTEXT:
00892               if ( ld->ld_defconn && ld->ld_defconn->lconn_gss_ctx ) {
00893                      * (gss_ctx_id_t *) arg = (gss_ctx_id_t)ld->ld_defconn->lconn_gss_ctx;
00894               } else {
00895                      * (gss_ctx_id_t *) arg = GSS_C_NO_CONTEXT;
00896               }
00897               break;
00898 
00899        case LDAP_OPT_X_GSSAPI_DO_NOT_FREE_CONTEXT:
00900               if ( ld->ld_options.ldo_gssapi_options & LDAP_GSSAPI_OPT_DO_NOT_FREE_GSS_CONTEXT ) {
00901                      * (int *) arg = (int)-1;
00902               } else {
00903                      * (int *) arg = (int)0;
00904               }
00905               break;
00906 
00907        case LDAP_OPT_X_GSSAPI_ALLOW_REMOTE_PRINCIPAL:
00908               if ( ld->ld_options.ldo_gssapi_options & LDAP_GSSAPI_OPT_ALLOW_REMOTE_PRINCIPAL ) {
00909                      * (int *) arg = (int)-1;
00910               } else {
00911                      * (int *) arg = (int)0;
00912               }
00913               break;
00914 
00915        default:
00916               return -1;
00917        }
00918 
00919        return 0;
00920 }
00921 
00922 int
00923 ldap_int_gssapi_set_option( LDAP *ld, int option, void *arg )
00924 {
00925        if ( ld == NULL )
00926               return -1;
00927 
00928        switch ( option ) {
00929        case LDAP_OPT_SSPI_FLAGS:
00930               if ( arg != LDAP_OPT_OFF ) {
00931                      ld->ld_options.gssapi_flags = * (unsigned *)arg;
00932               }
00933               break;
00934 
00935        case LDAP_OPT_SIGN:
00936               if ( arg != LDAP_OPT_OFF ) {
00937                      ld->ld_options.gssapi_flags |= GSS_C_INTEG_FLAG;
00938               }
00939               break;
00940 
00941        case LDAP_OPT_ENCRYPT:
00942               if ( arg != LDAP_OPT_OFF ) {
00943                      ld->ld_options.gssapi_flags |= GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG;
00944               }
00945               break;
00946 
00947        case LDAP_OPT_SASL_METHOD:
00948               if ( arg != LDAP_OPT_OFF ) {
00949                      const char *m = (const char *)arg;
00950                      if ( strcmp( "GSS-SPNEGO", m ) != 0 ) {
00951                             /* we currently only support GSS-SPNEGO */
00952                             return -1;
00953                      }
00954               }
00955               break;
00956 
00957        case LDAP_OPT_SECURITY_CONTEXT:
00958               if ( arg != LDAP_OPT_OFF && ld->ld_defconn) {
00959                      ldap_int_gssapi_setup( ld, ld->ld_defconn,
00960                                           (gss_ctx_id_t) arg);
00961               }
00962               break;
00963 
00964        case LDAP_OPT_X_GSSAPI_DO_NOT_FREE_CONTEXT:
00965               if ( arg != LDAP_OPT_OFF ) {
00966                      ld->ld_options.ldo_gssapi_options |= LDAP_GSSAPI_OPT_DO_NOT_FREE_GSS_CONTEXT;
00967               }
00968               break;
00969 
00970        case LDAP_OPT_X_GSSAPI_ALLOW_REMOTE_PRINCIPAL:
00971               if ( arg != LDAP_OPT_OFF ) {
00972                      ld->ld_options.ldo_gssapi_options |= LDAP_GSSAPI_OPT_ALLOW_REMOTE_PRINCIPAL;
00973               }
00974               break;
00975 
00976        default:
00977               return -1;
00978        }
00979 
00980        return 0;
00981 }
00982 
00983 #else /* HAVE_GSSAPI */
00984 #define ldap_int_gss_spnego_bind_s(ld) LDAP_NOT_SUPPORTED
00985 #endif /* HAVE_GSSAPI */
00986 
00987 int
00988 ldap_gssapi_bind(
00989        LDAP *ld,
00990        LDAP_CONST char *dn,
00991        LDAP_CONST char *creds )
00992 {
00993        return LDAP_NOT_SUPPORTED;
00994 }
00995 
00996 int
00997 ldap_gssapi_bind_s(
00998        LDAP *ld,
00999        LDAP_CONST char *dn,
01000        LDAP_CONST char *creds )
01001 {
01002        if ( dn != NULL ) {
01003               return LDAP_NOT_SUPPORTED;
01004        }
01005 
01006        if ( creds != NULL ) {
01007               return LDAP_NOT_SUPPORTED;
01008        }
01009 
01010        return ldap_int_gss_spnego_bind_s(ld);
01011 }