--- openldap-2.4.40/include/ldap.h 2014-09-18 18:48:49.000000000 -0700 +++ openldap-2.4.40-1/include/ldap.h 2015-01-21 15:59:24.000000000 -0800 @@ -196,6 +196,7 @@ LDAP_BEGIN_DECL /* OpenLDAP GSSAPI options */ #define LDAP_OPT_X_GSSAPI_DO_NOT_FREE_CONTEXT 0x6200 #define LDAP_OPT_X_GSSAPI_ALLOW_REMOTE_PRINCIPAL 0x6201 +#define LDAP_OPT_X_GSSAPI_CREDENTIAL_HANDLE 0x6202 /* * OpenLDAP per connection tcp-keepalive settings @@ -1238,6 +1239,15 @@ ldap_parse_sasl_bind_result LDAP_P(( struct berval **servercredp, int freeit )); +/* + * in gssapi.c: + */ +LDAP_F( int ) +ldap_gssapi_bind_s LDAP_P(( + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *creds)); + #if LDAP_DEPRECATED /* * in bind.c: diff -rupN openldap-2.4.40/libraries/libldap/gssapi.c openldap-2.4.40-1/libraries/libldap/gssapi.c --- openldap-2.4.40/libraries/libldap/gssapi.c 2014-09-18 18:48:49.000000000 -0700 +++ openldap-2.4.40-1/libraries/libldap/gssapi.c 2015-01-21 17:32:47.000000000 -0800 @@ -199,6 +199,7 @@ sb_sasl_gssapi_encode( if ( conf_req_flag && conf_state == 0 ) { ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug, "sb_sasl_gssapi_encode: GSS_C_CONF_FLAG was ignored by our gss_wrap()\n" ); + gss_release_buffer(&minor_status, &wrapped); return -1; } @@ -211,6 +212,7 @@ sb_sasl_gssapi_encode( ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug, "sb_sasl_gssapi_encode: failed to grow the buffer to %lu bytes\n", pkt_len ); + gss_release_buffer(&minor_status, &wrapped); return -1; } @@ -240,12 +242,13 @@ sb_sasl_gssapi_decode( gss_ctx_id_t gss_ctx = (gss_ctx_id_t)p->ops_private; int gss_rc; OM_uint32 minor_status; - gss_buffer_desc unwrapped, wrapped; + gss_buffer_desc unwrapped = {0}, wrapped; gss_OID ctx_mech = GSS_C_NO_OID; OM_uint32 ctx_flags = 0; int conf_req_flag = 0; int conf_state; unsigned char *b; + ber_int_t result = 0; wrapped.value = src->buf_base + 4; wrapped.length = src->buf_end - 4; @@ -272,13 +275,15 @@ sb_sasl_gssapi_decode( ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug, "sb_sasl_gssapi_decode: failed to decode packet: %s\n", gsserrstr( msg, sizeof(msg), ctx_mech, gss_rc, minor_status ) ); - return -1; + result = -1; + goto cleanup; } if ( conf_req_flag && conf_state == 0 ) { ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug, "sb_sasl_gssapi_encode: GSS_C_CONF_FLAG was ignored by our peer\n" ); - return -1; + result = -1; + goto cleanup; } /* Grow the packet buffer if neccessary */ @@ -288,7 +293,8 @@ sb_sasl_gssapi_decode( ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug, "sb_sasl_gssapi_decode: failed to grow the buffer to %lu bytes\n", unwrapped.length ); - return -1; + result = -1; + goto cleanup; } dst->buf_end = unwrapped.length; @@ -298,9 +304,11 @@ sb_sasl_gssapi_decode( /* copy the wrapped blob to the right location */ memcpy(b, unwrapped.value, unwrapped.length); +cleanup: + gss_release_buffer(&minor_status, &unwrapped); - return 0; + return result; } static void @@ -512,15 +520,28 @@ guess_service_principal( int gss_rc; int ret; size_t svc_principal_size; + size_t dns_domain_name_size; char *svc_principal = NULL; const char *principal_fmt = NULL; - const char *str = NULL; const char *givenstr = NULL; + char *dns_domain_name = NULL; + char *name = NULL; const char *ignore = "not_defined_in_RFC4178@please_ignore"; int allow_remote = 0; if (ldapServiceName) { givenstr = strchr(ldapServiceName, ':'); + + dns_domain_name_size = (size_t)((givenstr - ldapServiceName) + 1); + dns_domain_name = (char*) ldap_memalloc(dns_domain_name_size * sizeof(char)); + if (!dns_domain_name) { + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + + strncpy(dns_domain_name, ldapServiceName, (dns_domain_name_size - 1)); + dns_domain_name[dns_domain_name_size - 1] = '\0'; + if (givenstr && givenstr[1]) { givenstr++; if (strcmp(givenstr, ignore) == 0) { @@ -535,20 +556,42 @@ guess_service_principal( allow_remote = 1; } + /* Try to figure out correct service principal form given + available information */ if (allow_remote && givenstr) { principal_fmt = "%s"; svc_principal_size = strlen(givenstr) + 1; - str = givenstr; + name = strdup(givenstr); + if (!name) { + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } - } else if (allow_remote && dnsHostName) { + } else if (dnsHostName) { principal_fmt = "ldap/%s"; - svc_principal_size = STRLENOF("ldap/") + strlen(dnsHostName) + 1; - str = dnsHostName; + svc_principal_size = strlen(dnsHostName) + strlen(dns_domain_name) + + strlen(principal_fmt); + + /* svc_principal_size is actually a bit more than really needed, but + let's use it to avoid calculating yet another size */ + name = (char*) ldap_memalloc(svc_principal_size * sizeof(char)); + if (!name) { + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + + snprintf(name, svc_principal_size, "%s/%s", + dnsHostName, dns_domain_name); } else { principal_fmt = "ldap/%s"; - svc_principal_size = STRLENOF("ldap/") + strlen(host) + 1; - str = host; + svc_principal_size = strlen(dns_domain_name) + strlen(principal_fmt); + name = strdup(dns_domain_name); + if (!name) { + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + } svc_principal = (char*) ldap_memalloc(svc_principal_size * sizeof(char)); @@ -557,8 +600,8 @@ guess_service_principal( return ld->ld_errno; } - ret = snprintf( svc_principal, svc_principal_size, principal_fmt, str ); - if (ret < 0 || (size_t)ret >= svc_principal_size) { + ret = snprintf( svc_principal, svc_principal_size, principal_fmt, name ); + if (ret < 0 || (size_t)(ret+1) >= svc_principal_size) { ld->ld_errno = LDAP_LOCAL_ERROR; return ld->ld_errno; } @@ -571,6 +614,8 @@ guess_service_principal( gss_rc = gss_import_name( &minor_status, &input_name, &nt_principal, principal ); ldap_memfree( svc_principal ); + ldap_memfree(dns_domain_name); + ldap_memfree(name); if ( gss_rc != GSS_S_COMPLETE ) { return map_gsserr2ldap( ld, GSS_C_NO_OID, gss_rc, minor_status ); } @@ -658,6 +703,7 @@ ldap_int_gss_spnego_bind_s( LDAP *ld ) gss_OID req_mech = GSS_C_NO_OID; gss_OID ret_mech = GSS_C_NO_OID; gss_ctx_id_t gss_ctx = GSS_C_NO_CONTEXT; + gss_cred_id_t gss_cred = GSS_C_NO_CREDENTIAL; gss_name_t principal = GSS_C_NO_NAME; OM_uint32 req_flags; OM_uint32 ret_flags; @@ -670,7 +716,7 @@ ldap_int_gss_spnego_bind_s( LDAP *ld ) rc = ldap_gssapi_get_rootdse_infos ( ld, &mechlist, &ldapServiceName, &dnsHostName); if ( rc != LDAP_SUCCESS ) { - return rc; + goto rc_error; } /* check that the server supports GSS-SPNEGO */ @@ -708,8 +754,16 @@ ldap_int_gss_spnego_bind_s( LDAP *ld ) */ input_token.value = NULL; input_token.length = 0; + + /* + * Set credentials handle if it's been set (e.g. for gss-ntlm authentication) + */ + if (ld->ld_options.gssapi_cred_handle) { + gss_cred = (gss_cred_id_t)ld->ld_options.gssapi_cred_handle; + } + gss_rc = gss_init_sec_context(&minor_status, - GSS_C_NO_CREDENTIAL, + gss_cred, &gss_ctx, principal, req_mech, @@ -746,7 +800,7 @@ ldap_int_gss_spnego_bind_s( LDAP *ld ) } gss_rc = gss_init_sec_context(&minor_status, - GSS_C_NO_CREDENTIAL, + gss_cred, &gss_ctx, principal, req_mech, @@ -896,6 +950,14 @@ ldap_int_gssapi_get_option( LDAP *ld, in } break; + case LDAP_OPT_X_GSSAPI_CREDENTIAL_HANDLE: + if ( ld->ld_options.gssapi_cred_handle ) { + * (void**)arg = ld->ld_options.gssapi_cred_handle; + } else { + * (void**)arg = GSS_C_NO_CREDENTIAL; + } + break; + case LDAP_OPT_X_GSSAPI_DO_NOT_FREE_CONTEXT: if ( ld->ld_options.ldo_gssapi_options & LDAP_GSSAPI_OPT_DO_NOT_FREE_GSS_CONTEXT ) { * (int *) arg = (int)-1; @@ -961,6 +1023,12 @@ ldap_int_gssapi_set_option( LDAP *ld, in } break; + case LDAP_OPT_X_GSSAPI_CREDENTIAL_HANDLE: + if ( arg != LDAP_OPT_OFF) { + ld->ld_options.gssapi_cred_handle = arg; + } + break; + case LDAP_OPT_X_GSSAPI_DO_NOT_FREE_CONTEXT: if ( arg != LDAP_OPT_OFF ) { ld->ld_options.ldo_gssapi_options |= LDAP_GSSAPI_OPT_DO_NOT_FREE_GSS_CONTEXT; diff -rupN openldap-2.4.40/libraries/libldap/ldap-int.h openldap-2.4.40-1/libraries/libldap/ldap-int.h --- openldap-2.4.40/libraries/libldap/ldap-int.h 2014-09-18 18:48:49.000000000 -0700 +++ openldap-2.4.40-1/libraries/libldap/ldap-int.h 2015-01-21 16:03:49.000000000 -0800 @@ -283,6 +283,7 @@ struct ldapoptions { #define LDAP_GSSAPI_OPT_DO_NOT_FREE_GSS_CONTEXT 0x0001 #define LDAP_GSSAPI_OPT_ALLOW_REMOTE_PRINCIPAL 0x0002 unsigned ldo_gssapi_options; + void *gssapi_cred_handle; #define LDAP_LDO_GSSAPI_NULLARG ,0,0 #else #define LDAP_LDO_GSSAPI_NULLARG diff -rupN openldap-2.4.40/libraries/libldap/os-ip.c openldap-2.4.40-1/libraries/libldap/os-ip.c --- openldap-2.4.40/libraries/libldap/os-ip.c 2014-09-18 18:48:49.000000000 -0700 +++ openldap-2.4.40-1/libraries/libldap/os-ip.c 2015-01-21 16:07:47.000000000 -0800 @@ -550,6 +550,7 @@ ldap_connect_to_host(LDAP *ld, Sockbuf * char serv[7]; int err; struct addrinfo hints, *res, *sai; + int retry; #else int i; int use_hp = 0; @@ -605,7 +606,22 @@ ldap_connect_to_host(LDAP *ld, Sockbuf * /* most getaddrinfo(3) use non-threadsafe resolver libraries */ LDAP_MUTEX_LOCK(&ldap_int_resolv_mutex); + /* The hostname may be an IP address, depending on what is stored in + * the configuration file. To avoid issuing any unnecessary network + * traffic, try looking it up as a numerical host first. + */ + hints.ai_flags |= AI_NUMERICHOST; err = getaddrinfo( host, serv, &hints, &res ); + retry = (err == EAI_NONAME); +#ifdef EAI_NODATA + if (err == EAI_NODATA) { + retry = 1; + } +#endif + if (retry) { + hints.ai_flags &= ~AI_NUMERICHOST; + err = getaddrinfo( host, serv, &hints, &res ); + } LDAP_MUTEX_UNLOCK(&ldap_int_resolv_mutex); diff -rupN openldap-2.4.40/libraries/libldap/request.c openldap-2.4.40-1/libraries/libldap/request.c --- openldap-2.4.40/libraries/libldap/request.c 2014-09-18 18:48:49.000000000 -0700 +++ openldap-2.4.40-1/libraries/libldap/request.c 2015-01-21 16:19:26.000000000 -0800 @@ -779,6 +779,7 @@ ldap_free_connection( LDAP *ld, LDAPConn if ( lc->lconn_ber != NULL ) { ber_free( lc->lconn_ber, 1 ); + lc->lconn_ber = NULL; } ldap_int_sasl_close( ld, lc ); diff -rupN openldap-2.4.40/libraries/libldap/result.c openldap-2.4.40-1/libraries/libldap/result.c --- openldap-2.4.40/libraries/libldap/result.c 2014-09-18 18:48:49.000000000 -0700 +++ openldap-2.4.40-1/libraries/libldap/result.c 2015-01-21 16:16:46.000000000 -0800 @@ -692,6 +692,8 @@ nextresp2: char *lr_res_error = NULL; tmpber = *ber; /* struct copy */ + lr->lr_res_matched = NULL; + lr_res_error = NULL; if ( ber_scanf( &tmpber, "{eAA", &lderr, &lr->lr_res_matched, &lr_res_error ) != LBER_ERROR ) @@ -789,6 +791,12 @@ nextresp2: lr->lr_res_errno = LDAP_PARTIAL_RESULTS; } } + else + { + /* Free lr_res_matched in case it was allocated before + * ber_scanf found an error */ + LDAP_FREE(lr->lr_res_matched); + } /* in any case, don't leave any lr_res_error 'round */ if ( lr_res_error ) { @@ -881,6 +889,9 @@ nextresp2: if ( lr != &dummy_lr ) { ldap_return_request( ld, lr, 1 ); + } else { + LDAP_FREE(lr->lr_res_error); + lr->lr_res_error = NULL; } lr = NULL; } @@ -962,7 +973,8 @@ nextresp2: /* need to return -1, because otherwise * a valid result is expected */ - ld->ld_errno = lderr; + ber_free(ber, 1); + ld->ld_errno = LDAP_CONNECT_ERROR; return -1; } }