📄 ldap.c
字号:
/* Unix SMB/CIFS implementation. ads (active directory) utility library Copyright (C) Andrew Tridgell 2001 Copyright (C) Remus Koos 2001 Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.*/#include "includes.h"#ifdef HAVE_LDAP/** * @file ldap.c * @brief basic ldap client-side routines for ads server communications * * The routines contained here should do the necessary ldap calls for * ads setups. * * Important note: attribute names passed into ads_ routines must * already be in UTF-8 format. We do not convert them because in almost * all cases, they are just ascii (which is represented with the same * codepoints in UTF-8). This may have to change at some point **/#define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"static SIG_ATOMIC_T gotalarm; /*************************************************************** Signal function to tell us we timed out.****************************************************************/ static void gotalarm_sig(void){ gotalarm = 1;} LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to){ LDAP *ldp = NULL; /* Setup timeout */ gotalarm = 0; CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig); alarm(to); /* End setup timeout. */ ldp = ldap_open(server, port); /* Teardown timeout. */ CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN); alarm(0); return ldp;}static int ldap_search_with_timeout(LDAP *ld, LDAP_CONST char *base, int scope, LDAP_CONST char *filter, char **attrs, int attrsonly, LDAPControl **sctrls, LDAPControl **cctrls, int sizelimit, LDAPMessage **res ){ struct timeval timeout; int result; /* Setup timeout for the ldap_search_ext_s call - local and remote. */ timeout.tv_sec = lp_ldap_timeout(); timeout.tv_usec = 0; /* Setup alarm timeout.... Do we need both of these ? JRA. */ gotalarm = 0; CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig); alarm(lp_ldap_timeout()); /* End setup timeout. */ result = ldap_search_ext_s(ld, base, scope, filter, attrs, attrsonly, sctrls, cctrls, &timeout, sizelimit, res); /* Teardown timeout. */ CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN); alarm(0); if (gotalarm != 0) return LDAP_TIMELIMIT_EXCEEDED; return result;}/* try a connection to a given ldap server, returning True and setting the servers IP in the ads struct if successful TODO : add a negative connection cache in here leveraged off of the one found in the rpc code. --jerry */BOOL ads_try_connect(ADS_STRUCT *ads, const char *server, unsigned port){ char *srv; if (!server || !*server) { return False; } DEBUG(5,("ads_try_connect: trying ldap server '%s' port %u\n", server, port)); /* this copes with inet_ntoa brokenness */ srv = SMB_STRDUP(server); ads->ld = ldap_open_with_timeout(srv, port, lp_ldap_timeout()); if (!ads->ld) { free(srv); return False; } ads->ldap_port = port; ads->ldap_ip = *interpret_addr2(srv); free(srv); return True;}/* try a connection to a given ldap server, based on URL, returning True if successful */static BOOL ads_try_connect_uri(ADS_STRUCT *ads){#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) DEBUG(5,("ads_try_connect: trying ldap server at URI '%s'\n", ads->server.ldap_uri)); if (ldap_initialize((LDAP**)&(ads->ld), ads->server.ldap_uri) == LDAP_SUCCESS) { return True; } DEBUG(0, ("ldap_initialize: %s\n", strerror(errno))); #else DEBUG(1, ("no URL support in LDAP libs!\n"));#endif return False;}/********************************************************************** Try to find an AD dc using our internal name resolution routines Try the realm first and then then workgroup name if netbios is not disabled**********************************************************************/static BOOL ads_find_dc(ADS_STRUCT *ads){ const char *c_realm; int count, i=0; struct ip_service *ip_list; pstring realm; BOOL got_realm = False; BOOL use_own_domain = False; /* if the realm and workgroup are both empty, assume they are ours */ /* realm */ c_realm = ads->server.realm; if ( !c_realm || !*c_realm ) { /* special case where no realm and no workgroup means our own */ if ( !ads->server.workgroup || !*ads->server.workgroup ) { use_own_domain = True; c_realm = lp_realm(); } } if (c_realm && *c_realm) got_realm = True; again: /* we need to try once with the realm name and fallback to the netbios domain name if we fail (if netbios has not been disabled */ if ( !got_realm && !lp_disable_netbios() ) { c_realm = ads->server.workgroup; if (!c_realm || !*c_realm) { if ( use_own_domain ) c_realm = lp_workgroup(); } if ( !c_realm || !*c_realm ) { DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n")); return False; } } pstrcpy( realm, c_realm ); DEBUG(6,("ads_find_dc: looking for %s '%s'\n", (got_realm ? "realm" : "domain"), realm)); if ( !get_sorted_dc_list(realm, &ip_list, &count, got_realm) ) { /* fall back to netbios if we can */ if ( got_realm && !lp_disable_netbios() ) { got_realm = False; goto again; } return False; } /* if we fail this loop, then giveup since all the IP addresses returned were dead */ for ( i=0; i<count; i++ ) { /* since this is an ads conection request, default to LDAP_PORT is not set */ int port = (ip_list[i].port!=PORT_NONE) ? ip_list[i].port : LDAP_PORT; fstring server; fstrcpy( server, inet_ntoa(ip_list[i].ip) ); if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) ) continue; if ( ads_try_connect(ads, server, port) ) { SAFE_FREE(ip_list); return True; } /* keep track of failures */ add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL ); } SAFE_FREE(ip_list); return False;}/** * Connect to the LDAP server * @param ads Pointer to an existing ADS_STRUCT * @return status of connection **/ADS_STATUS ads_connect(ADS_STRUCT *ads){ int version = LDAP_VERSION3; ADS_STATUS status; ads->last_attempt = time(NULL); ads->ld = NULL; /* try with a URL based server */ if (ads->server.ldap_uri && ads_try_connect_uri(ads)) { goto got_connection; } /* try with a user specified server */ if (ads->server.ldap_server && ads_try_connect(ads, ads->server.ldap_server, LDAP_PORT)) { goto got_connection; } if (ads_find_dc(ads)) { goto got_connection; } return ADS_ERROR_SYSTEM(errno?errno:ENOENT);got_connection: DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip))); status = ads_server_info(ads); if (!ADS_ERR_OK(status)) { DEBUG(1,("Failed to get ldap server info\n")); return status; } ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version); status = ADS_ERROR(smb_ldap_start_tls(ads->ld, version)); if (!ADS_ERR_OK(status)) { return status; } if (!ads->auth.user_name) { /* have to use the userPrincipalName value here and not servicePrincipalName; found by Guenther Deschner @ Sernet */ asprintf(&ads->auth.user_name, "host/%s", global_myname() ); } if (!ads->auth.realm) { ads->auth.realm = SMB_STRDUP(ads->config.realm); } if (!ads->auth.kdc_server) { ads->auth.kdc_server = SMB_STRDUP(inet_ntoa(ads->ldap_ip)); }#if KRB5_DNS_HACK /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch to MIT kerberos to work (tridge) */ { char *env; asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm); setenv(env, ads->auth.kdc_server, 1); free(env); }#endif if (ads->auth.flags & ADS_AUTH_NO_BIND) { return ADS_SUCCESS; } if (ads->auth.flags & ADS_AUTH_ANON_BIND) { return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL)); } if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) { return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password)); } return ads_sasl_bind(ads);}/* Duplicate a struct berval into talloc'ed memory */static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val){ struct berval *value; if (!in_val) return NULL; value = TALLOC_ZERO_P(ctx, struct berval); if (value == NULL) return NULL; if (in_val->bv_len == 0) return value; value->bv_len = in_val->bv_len; value->bv_val = TALLOC_MEMDUP(ctx, in_val->bv_val, in_val->bv_len); return value;}/* Make a values list out of an array of (struct berval *) */static struct berval **ads_dup_values(TALLOC_CTX *ctx, const struct berval **in_vals){ struct berval **values; int i; if (!in_vals) return NULL; for (i=0; in_vals[i]; i++) ; /* count values */ values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1); if (!values) return NULL; for (i=0; in_vals[i]; i++) { values[i] = dup_berval(ctx, in_vals[i]); } return values;}/* UTF8-encode a values list out of an array of (char *) */static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals){ char **values; int i; if (!in_vals) return NULL; for (i=0; in_vals[i]; i++) ; /* count values */ values = TALLOC_ZERO_ARRAY(ctx, char *, i+1); if (!values) return NULL; for (i=0; in_vals[i]; i++) { push_utf8_talloc(ctx, &values[i], in_vals[i]); } return values;}/* Pull a (char *) array out of a UTF8-encoded values list */static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals){ char **values; int i; if (!in_vals) return NULL; for (i=0; in_vals[i]; i++) ; /* count values */ values = TALLOC_ZERO_ARRAY(ctx, char *, i+1); if (!values) return NULL; for (i=0; in_vals[i]; i++) { pull_utf8_talloc(ctx, &values[i], in_vals[i]); } return values;}/** * Do a search with paged results. cookie must be null on the first * call, and then returned on each subsequent call. It will be null * again when the entire search is complete * @param ads connection to ads server * @param bind_path Base dn for the search * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE) * @param expr Search expression - specified in local charset * @param attrs Attributes to retrieve - specified in utf8 or ascii * @param res ** which will contain results - free res* with ads_msgfree() * @param count Number of entries retrieved on this page * @param cookie The paged results cookie to be returned on subsequent calls * @return status of search **/ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path, int scope, const char *expr, const char **attrs, void **res, int *count, void **cookie){ int rc, i, version; char *utf8_expr, *utf8_path, **search_attrs; LDAPControl PagedResults, NoReferrals, *controls[3], **rcontrols; BerElement *cookie_be = NULL; struct berval *cookie_bv= NULL; TALLOC_CTX *ctx; *res = NULL; if (!(ctx = talloc_init("ads_do_paged_search"))) return ADS_ERROR(LDAP_NO_MEMORY); /* 0 means the conversion worked but the result was empty so we only fail if it's -1. In any case, it always at least nulls out the dest */ if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) || (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) { rc = LDAP_NO_MEMORY; goto done; } if (!attrs || !(*attrs)) search_attrs = NULL; else { /* This would be the utf8-encoded version...*/ /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */ if (!(str_list_copy(&search_attrs, attrs))) { rc = LDAP_NO_MEMORY; goto done; } } /* Paged results only available on ldap v3 or later */ ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version); if (version < LDAP_VERSION3) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -