X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=source3%2Flibads%2Fldap.c;h=8e0ecb87569ef6780ca8ea0a6f2d26a3472a4b19;hb=e18610a197aab80a32cae8c1e09b96496679bbad;hp=ca5962cf3201347ddbcbc4c31d718eb8c684cb04;hpb=140bb288be426bf57fb46a3e4b012a07b6b60fc8;p=samba.git diff --git a/source3/libads/ldap.c b/source3/libads/ldap.c index ca5962cf320..8e0ecb87569 100644 --- a/source3/libads/ldap.c +++ b/source3/libads/ldap.c @@ -29,7 +29,9 @@ #include "../libds/common/flags.h" #include "smbldap.h" #include "../libcli/security/security.h" +#include "../librpc/gen_ndr/netlogon.h" #include "lib/param/loadparm.h" +#include "libsmb/namequery.h" #ifdef HAVE_LDAP @@ -65,42 +67,12 @@ static void gotalarm_sig(int signum) int port, unsigned int to) { LDAP *ldp = NULL; + int ldap_err; + char *uri; DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout " "%u seconds\n", server, port, to)); -#if defined(HAVE_LDAP_INIT_FD) && defined(SOCKET_WRAPPER) - /* Only use this private LDAP function if we are in make test, - * as this is the best way to get the emulated TCP socket into - * OpenLDAP */ - if (socket_wrapper_dir() != NULL) { - int fd, ldap_err; - NTSTATUS status; - char *uri; - - status = open_socket_out(ss, port, to, &fd); - - if (!NT_STATUS_IS_OK(status)) { - return NULL; - } - -#ifndef LDAP_PROTO_TCP -#define LDAP_PROTO_TCP 1 -#endif - uri = talloc_asprintf(talloc_tos(), "ldap://%s:%u", server, port); - if (uri == NULL) { - return NULL; - } - ldap_err = ldap_init_fd(fd, LDAP_PROTO_TCP, uri, &ldp); - talloc_free(uri); - - if (ldap_err != LDAP_SUCCESS) { - return NULL; - } - return ldp; - } -#endif - if (to) { /* Setup timeout */ gotalarm = 0; @@ -109,13 +81,32 @@ static void gotalarm_sig(int signum) /* End setup timeout. */ } - ldp = ldap_open(server, port); + if ( strchr_m(server, ':') ) { + /* IPv6 URI */ + uri = talloc_asprintf(talloc_tos(), "ldap://[%s]:%u", server, port); + } else { + /* IPv4 URI */ + uri = talloc_asprintf(talloc_tos(), "ldap://%s:%u", server, port); + } + if (uri == NULL) { + return NULL; + } - if (ldp == NULL) { - DEBUG(2,("Could not open connection to LDAP server %s:%d: %s\n", - server, port, strerror(errno))); +#ifdef HAVE_LDAP_INITIALIZE + ldap_err = ldap_initialize(&ldp, uri); +#else + ldp = ldap_open(server, port); + if (ldp != NULL) { + ldap_err = LDAP_SUCCESS; } else { - DEBUG(10, ("Connected to LDAP server '%s:%d'\n", server, port)); + ldap_err = LDAP_OTHER; + } +#endif + if (ldap_err != LDAP_SUCCESS) { + DEBUG(2,("Could not initialize connection for LDAP server '%s': %s\n", + uri, ldap_err2string(ldap_err))); + } else { + DEBUG(10, ("Initialized connection for LDAP server '%s'\n", uri)); } if (to) { @@ -245,33 +236,27 @@ bool ads_closest_dc(ADS_STRUCT *ads) try a connection to a given ldap server, returning True and setting the servers IP in the ads struct if successful */ -static bool ads_try_connect(ADS_STRUCT *ads, const char *server, bool gc) +static bool ads_try_connect(ADS_STRUCT *ads, bool gc, + struct sockaddr_storage *ss) { struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply; TALLOC_CTX *frame = talloc_stackframe(); bool ret = false; - struct sockaddr_storage ss; char addr[INET6_ADDRSTRLEN]; - if (!server || !*server) { + if (ss == NULL) { TALLOC_FREE(frame); return False; } - if (!resolve_name(server, &ss, 0x20, true)) { - DEBUG(5,("ads_try_connect: unable to resolve name %s\n", - server )); - TALLOC_FREE(frame); - return false; - } - print_sockaddr(addr, sizeof(addr), &ss); + print_sockaddr(addr, sizeof(addr), ss); DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n", addr, ads->server.realm)); ZERO_STRUCT( cldap_reply ); - if ( !ads_cldap_netlogon_5(frame, &ss, ads->server.realm, &cldap_reply ) ) { + if ( !ads_cldap_netlogon_5(frame, ss, ads->server.realm, &cldap_reply ) ) { DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", addr)); ret = false; goto out; @@ -295,7 +280,12 @@ static bool ads_try_connect(ADS_STRUCT *ads, const char *server, bool gc) SAFE_FREE(ads->config.client_site_name); SAFE_FREE(ads->server.workgroup); - ads->config.flags = cldap_reply.server_type; + if (!check_cldap_reply_required_flags(cldap_reply.server_type, + ads->config.flags)) { + ret = false; + goto out; + } + ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.pdc_dns_name); ads->config.realm = SMB_STRDUP(cldap_reply.dns_domain); if (!strupper_m(ads->config.realm)) { @@ -315,12 +305,15 @@ static bool ads_try_connect(ADS_STRUCT *ads, const char *server, bool gc) ads->server.workgroup = SMB_STRDUP(cldap_reply.domain_name); ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT; - ads->ldap.ss = ss; + ads->ldap.ss = *ss; /* Store our site name. */ sitename_store( cldap_reply.domain_name, cldap_reply.client_site); sitename_store( cldap_reply.dns_domain, cldap_reply.client_site); + /* Leave this until last so that the flags are not clobbered */ + ads->config.flags = cldap_reply.server_type; + ret = true; out: @@ -330,301 +323,248 @@ static bool ads_try_connect(ADS_STRUCT *ads, const char *server, bool gc) } /********************************************************************** - 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 + send a cldap ping to list of servers, one at a time, until one of + them answers it's an ldap server. Record success in the ADS_STRUCT. + Take note of and update negative connection cache. **********************************************************************/ -static NTSTATUS ads_find_dc(ADS_STRUCT *ads) +static NTSTATUS cldap_ping_list(ADS_STRUCT *ads,const char *domain, + struct ip_service *ip_list, int count) { - const char *c_domain; - const char *c_realm; - int count, i=0; - struct ip_service *ip_list; - const char *realm; - const char *domain; - bool got_realm = False; - bool use_own_domain = False; - char *sitename; - NTSTATUS status = NT_STATUS_UNSUCCESSFUL; - - /* if the realm and workgroup are both empty, assume they are ours */ + int i; + bool ok; - /* realm */ - c_realm = ads->server.realm; + for (i = 0; i < count; i++) { + char server[INET6_ADDRSTRLEN]; - 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(); - } - } + print_sockaddr(server, sizeof(server), &ip_list[i].ss); - if (c_realm && *c_realm) - got_realm = True; + if (!NT_STATUS_IS_OK( + check_negative_conn_cache(domain, server))) + continue; - /* 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 */ + /* Returns ok only if it matches the correct server type */ + ok = ads_try_connect(ads, false, &ip_list[i].ss); - 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 (ok) { + return NT_STATUS_OK; } - } - - if ( !c_realm || !*c_realm ) { - DEBUG(1, ("ads_find_dc: no realm or workgroup! Don't know " - "what to do\n")); - return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */ - } - if ( use_own_domain ) { - c_domain = lp_workgroup(); - } else { - c_domain = ads->server.workgroup; + /* keep track of failures */ + add_failed_connection_entry(domain, server, + NT_STATUS_UNSUCCESSFUL); } - realm = c_realm; - domain = c_domain; - - /* - * In case of LDAP we use get_dc_name() as that - * creates the custom krb5.conf file - */ - if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) { - fstring srv_name; - struct sockaddr_storage ip_out; - - DEBUG(6,("ads_find_dc: (ldap) looking for %s '%s'\n", - (got_realm ? "realm" : "domain"), realm)); - - if (get_dc_name(domain, realm, srv_name, &ip_out)) { - /* - * we call ads_try_connect() to fill in the - * ads->config details - */ - if (ads_try_connect(ads, srv_name, false)) { - return NT_STATUS_OK; - } - } - - return NT_STATUS_NO_LOGON_SERVERS; - } + return NT_STATUS_NO_LOGON_SERVERS; +} - sitename = sitename_fetch(realm); +/*************************************************************************** + resolve a name and perform an "ldap ping" using NetBIOS and related methods +****************************************************************************/ - again: +static NTSTATUS resolve_and_ping_netbios(ADS_STRUCT *ads, + const char *domain, const char *realm) +{ + int count, i; + struct ip_service *ip_list; + NTSTATUS status = NT_STATUS_UNSUCCESSFUL; - DEBUG(6,("ads_find_dc: (cldap) looking for %s '%s'\n", - (got_realm ? "realm" : "domain"), realm)); + DEBUG(6, ("resolve_and_ping_netbios: (cldap) looking for domain '%s'\n", + domain)); - status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm); + status = get_sorted_dc_list(domain, NULL, &ip_list, &count, + false); if (!NT_STATUS_IS_OK(status)) { - /* fall back to netbios if we can */ - if ( got_realm && !lp_disable_netbios() ) { - got_realm = False; - goto again; - } - - SAFE_FREE(sitename); return status; } - /* if we fail this loop, then giveup since all the IP addresses returned were dead */ - for ( i=0; iserver.realm; - if ( !c_realm || !*c_realm ) { - if ( !ads->server.workgroup || !*ads->server.workgroup ) { - c_realm = lp_realm(); - } - } - if (c_realm && *c_realm && - !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) { + if(!NT_STATUS_IS_OK( + check_negative_conn_cache(realm, server))) { /* Ensure we add the workgroup name for this IP address as negative too. */ - add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL ); - continue; + add_failed_connection_entry( + domain, server, + NT_STATUS_UNSUCCESSFUL); } } - - if ( ads_try_connect(ads, server, false) ) { - SAFE_FREE(ip_list); - SAFE_FREE(sitename); - return NT_STATUS_OK; - } - - /* keep track of failures */ - add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL ); } - SAFE_FREE(ip_list); - - /* In case we failed to contact one of our closest DC on our site we - * need to try to find another DC, retry with a site-less SRV DNS query - * - Guenther */ + status = cldap_ping_list(ads, domain, ip_list, count); - if (sitename) { - DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), " - "trying to find another DC\n", sitename)); - SAFE_FREE(sitename); - namecache_delete(realm, 0x1C); - goto again; - } + SAFE_FREE(ip_list); - return NT_STATUS_NO_LOGON_SERVERS; + return status; } -/********************************************************************* - *********************************************************************/ -static NTSTATUS ads_lookup_site(void) -{ - ADS_STRUCT *ads = NULL; - ADS_STATUS ads_status; - NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; +/********************************************************************** + resolve a name and perform an "ldap ping" using DNS +**********************************************************************/ - ads = ads_init(lp_realm(), NULL, NULL); - if (!ads) { - return NT_STATUS_NO_MEMORY; - } +static NTSTATUS resolve_and_ping_dns(ADS_STRUCT *ads, const char *sitename, + const char *realm) +{ + int count; + struct ip_service *ip_list = NULL; + NTSTATUS status = NT_STATUS_UNSUCCESSFUL; - /* The NO_BIND here will find a DC and set the client site - but not establish the TCP connection */ + DEBUG(6, ("resolve_and_ping_dns: (cldap) looking for realm '%s'\n", + realm)); - ads->auth.flags = ADS_AUTH_NO_BIND; - ads_status = ads_connect(ads); - if (!ADS_ERR_OK(ads_status)) { - DEBUG(4, ("ads_lookup_site: ads_connect to our realm failed! (%s)\n", - ads_errstr(ads_status))); + status = get_sorted_dc_list(realm, sitename, &ip_list, &count, + true); + if (!NT_STATUS_IS_OK(status)) { + SAFE_FREE(ip_list); + return status; } - nt_status = ads_ntstatus(ads_status); - if (ads) { - ads_destroy(&ads); - } + status = cldap_ping_list(ads, realm, ip_list, count); + + SAFE_FREE(ip_list); - return nt_status; + return status; } -/********************************************************************* - *********************************************************************/ +/********************************************************************** + 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 const char* host_dns_domain(const char *fqdn) +static NTSTATUS ads_find_dc(ADS_STRUCT *ads) { - const char *p = fqdn; - - /* go to next char following '.' */ + const char *c_domain = ""; + const char *c_realm; + bool use_own_domain = False; + char *sitename = NULL; + NTSTATUS status = NT_STATUS_UNSUCCESSFUL; + bool ok = false; - if ((p = strchr_m(fqdn, '.')) != NULL) { - p++; - } + /* if the realm and workgroup are both empty, assume they are ours */ - return p; -} + /* realm */ + c_realm = ads->server.realm; + if (c_realm == NULL) + c_realm = ""; -/** - * Connect to the Global Catalog server - * @param ads Pointer to an existing ADS_STRUCT - * @return status of connection - * - * Simple wrapper around ads_connect() that fills in the - * GC ldap server information - **/ - -ADS_STATUS ads_connect_gc(ADS_STRUCT *ads) -{ - TALLOC_CTX *frame = talloc_stackframe(); - struct dns_rr_srv *gcs_list; - int num_gcs; - const char *realm = ads->server.realm; - NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; - ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL); - int i; - bool done = false; - char *sitename = NULL; - const char *dns_hosts_file; + if (!*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 (!realm) - realm = lp_realm(); + if (!lp_disable_netbios()) { + if (use_own_domain) { + c_domain = lp_workgroup(); + } else { + c_domain = ads->server.workgroup; + if (!*c_realm && (!c_domain || !*c_domain)) { + c_domain = lp_workgroup(); + } + } - if ((sitename = sitename_fetch(realm)) == NULL) { - ads_lookup_site(); - sitename = sitename_fetch(realm); + if (!c_domain) { + c_domain = ""; + } } - dns_hosts_file = lp_parm_const_string(-1, "resolv", "host file", NULL); - do { - /* We try once with a sitename and once without - (unless we don't have a sitename and then we're - done */ - - if (sitename == NULL) - done = true; + if (!*c_realm && !*c_domain) { + DEBUG(0, ("ads_find_dc: no realm or workgroup! Don't know " + "what to do\n")); + return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */ + } - nt_status = ads_dns_query_gcs(frame, dns_hosts_file, - realm, sitename, - &gcs_list, &num_gcs); + /* + * In case of LDAP we use get_dc_name() as that + * creates the custom krb5.conf file + */ + if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) { + fstring srv_name; + struct sockaddr_storage ip_out; - SAFE_FREE(sitename); + DEBUG(6, ("ads_find_dc: (ldap) looking for realm '%s'" + " and falling back to domain '%s'\n", + c_realm, c_domain)); - if (!NT_STATUS_IS_OK(nt_status)) { - ads_status = ADS_ERROR_NT(nt_status); - goto done; + ok = get_dc_name(c_domain, c_realm, srv_name, &ip_out); + if (ok) { + /* + * we call ads_try_connect() to fill in the + * ads->config details + */ + ok = ads_try_connect(ads, false, &ip_out); + if (ok) { + return NT_STATUS_OK; + } } - /* Loop until we get a successful connection or have gone - through them all. When connecting a GC server, make sure that - the realm is the server's DNS name and not the forest root */ - - for (i=0; iserver.gc = true; - ads->server.ldap_server = SMB_STRDUP(gcs_list[i].hostname); - ads->server.realm = SMB_STRDUP(host_dns_domain(ads->server.ldap_server)); - ads_status = ads_connect(ads); - if (ADS_ERR_OK(ads_status)) { - /* Reset the bind_dn to "". A Global Catalog server - may host multiple domain trees in a forest. - Windows 2003 GC server will accept "" as the search - path to imply search all domain trees in the forest */ + return NT_STATUS_NO_LOGON_SERVERS; + } - SAFE_FREE(ads->config.bind_path); - ads->config.bind_path = SMB_STRDUP(""); + if (*c_realm) { + sitename = sitename_fetch(talloc_tos(), c_realm); + status = resolve_and_ping_dns(ads, sitename, c_realm); + if (NT_STATUS_IS_OK(status)) { + TALLOC_FREE(sitename); + return status; + } - goto done; + /* In case we failed to contact one of our closest DC on our + * site we + * need to try to find another DC, retry with a site-less SRV + * DNS query + * - Guenther */ + + if (sitename) { + DEBUG(3, ("ads_find_dc: failed to find a valid DC on " + "our site (%s), Trying to find another DC " + "for realm '%s' (domain '%s')\n", + sitename, c_realm, c_domain)); + namecache_delete(c_realm, 0x1C); + status = + resolve_and_ping_dns(ads, NULL, c_realm); + + if (NT_STATUS_IS_OK(status)) { + TALLOC_FREE(sitename); + return status; } - SAFE_FREE(ads->server.ldap_server); - SAFE_FREE(ads->server.realm); } - TALLOC_FREE(gcs_list); - num_gcs = 0; - } while (!done); - -done: - SAFE_FREE(sitename); - talloc_destroy(frame); + TALLOC_FREE(sitename); + } - return ads_status; -} + /* try netbios as fallback - if permitted, + or if configuration specifically requests it */ + if (*c_domain) { + if (*c_realm) { + DEBUG(3, ("ads_find_dc: falling back to netbios " + "name resolution for domain '%s' (realm '%s')\n", + c_domain, c_realm)); + } + status = resolve_and_ping_netbios(ads, c_domain, c_realm); + if (NT_STATUS_IS_OK(status)) { + return status; + } + } + DEBUG(1, ("ads_find_dc: " + "name resolution for realm '%s' (domain '%s') failed: %s\n", + c_realm, c_domain, nt_errstr(status))); + return status; +} /** * Connect to the LDAP server * @param ads Pointer to an existing ADS_STRUCT @@ -638,8 +578,9 @@ ADS_STATUS ads_connect(ADS_STRUCT *ads) char addr[INET6_ADDRSTRLEN]; ZERO_STRUCT(ads->ldap); + ZERO_STRUCT(ads->ldap_wrap_data); ads->ldap.last_attempt = time_mono(NULL); - ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN; + ads->ldap_wrap_data.wrap_type = ADS_SASLWRAP_TYPE_PLAIN; /* try with a user specified server */ @@ -650,9 +591,19 @@ ADS_STATUS ads_connect(ADS_STRUCT *ads) TALLOC_FREE(s); } - if (ads->server.ldap_server) - { - if (ads_try_connect(ads, ads->server.ldap_server, ads->server.gc)) { + if (ads->server.ldap_server) { + bool ok = false; + struct sockaddr_storage ss; + + ok = resolve_name(ads->server.ldap_server, &ss, 0x20, true); + if (!ok) { + DEBUG(5,("ads_connect: unable to resolve name %s\n", + ads->server.ldap_server)); + status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND); + goto out; + } + ok = ads_try_connect(ads, ads->server.gc, &ss); + if (ok) { goto got_connection; } @@ -664,6 +615,11 @@ ADS_STATUS ads_connect(ADS_STRUCT *ads) if (ads->server.gc == true) { return ADS_ERROR(LDAP_OPERATIONS_ERROR); } + + if (ads->server.no_fallback) { + status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND); + goto out; + } } ntstatus = ads_find_dc(ads); @@ -705,15 +661,15 @@ got_connection: goto out; } - ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory"); - if (!ads->ldap.mem_ctx) { + ads->ldap_wrap_data.mem_ctx = talloc_init("ads LDAP connection memory"); + if (!ads->ldap_wrap_data.mem_ctx) { status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY); goto out; } /* Otherwise setup the TCP LDAP session */ - ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name, + ads->ldap.ld = ldap_open_with_timeout(addr, &ads->ldap.ss, ads->ldap.port, lp_ldap_timeout()); if (ads->ldap.ld == NULL) { @@ -792,13 +748,15 @@ void ads_disconnect(ADS_STRUCT *ads) ldap_unbind(ads->ldap.ld); ads->ldap.ld = NULL; } - if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) { - ads->ldap.wrap_ops->disconnect(ads); + if (ads->ldap_wrap_data.wrap_ops && + ads->ldap_wrap_data.wrap_ops->disconnect) { + ads->ldap_wrap_data.wrap_ops->disconnect(&ads->ldap_wrap_data); } - if (ads->ldap.mem_ctx) { - talloc_free(ads->ldap.mem_ctx); + if (ads->ldap_wrap_data.mem_ctx) { + talloc_free(ads->ldap_wrap_data.mem_ctx); } ZERO_STRUCT(ads->ldap); + ZERO_STRUCT(ads->ldap_wrap_data); } /* @@ -1041,6 +999,24 @@ static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads, if (rc) { DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr, ldap_err2string(rc))); + if (rc == LDAP_OTHER) { + char *ldap_errmsg; + int ret; + + ret = ldap_parse_result(ads->ldap.ld, + *res, + NULL, + NULL, + &ldap_errmsg, + NULL, + NULL, + 0); + if (ret == LDAP_SUCCESS) { + DEBUG(3, ("ldap_search_with_timeout(%s) " + "error: %s\n", expr, ldap_errmsg)); + ldap_memfree(ldap_errmsg); + } + } goto done; } @@ -1080,6 +1056,11 @@ done: ber_bvfree(ext_bv); } + if (rc != LDAP_SUCCESS && *res != NULL) { + ads_msgfree(ads, *res); + *res = NULL; + } + /* if/when we decide to utf8-encode attrs, take out this next line */ TALLOC_FREE(search_attrs); @@ -1125,13 +1106,13 @@ static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path, #ifdef HAVE_LDAP_ADD_RESULT_ENTRY while (cookie) { LDAPMessage *res2 = NULL; - ADS_STATUS status2; LDAPMessage *msg, *next; - status2 = ads_do_paged_search_args(ads, bind_path, scope, expr, + status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, &res2, &count, &cookie); - - if (!ADS_ERR_OK(status2)) break; + if (!ADS_ERR_OK(status)) { + break; + } /* this relies on the way that ldap_add_result_entry() works internally. I hope that this works on all ldap libs, but I have only tested with openldap */ @@ -1159,7 +1140,7 @@ static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path, ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path, int scope, const char *expr, - const char **attrs, uint32 sd_flags, + const char **attrs, uint32_t sd_flags, LDAPMessage **res) { ads_control args; @@ -1385,7 +1366,7 @@ char *ads_parent_dn(const char *dn) { ADS_STATUS status; char *expr; - const char *attrs[] = {"*", "nTSecurityDescriptor", NULL}; + const char *attrs[] = {"*", "msDS-SupportedEncryptionTypes", "nTSecurityDescriptor", NULL}; *res = NULL; @@ -1427,21 +1408,23 @@ static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods, int mod_op, const char *name, const void *_invals) { - const void **invals = (const void **)_invals; int curmod; LDAPMod **modlist = (LDAPMod **) *mods; struct berval **ber_values = NULL; char **char_values = NULL; - if (!invals) { + if (!_invals) { mod_op = LDAP_MOD_DELETE; } else { - if (mod_op & LDAP_MOD_BVALUES) - ber_values = ads_dup_values(ctx, - (const struct berval **)invals); - else - char_values = ads_push_strvals(ctx, - (const char **) invals); + if (mod_op & LDAP_MOD_BVALUES) { + const struct berval **b; + b = discard_const_p(const struct berval *, _invals); + ber_values = ads_dup_values(ctx, b); + } else { + const char **c; + c = discard_const_p(const char *, _invals); + char_values = ads_push_strvals(ctx, c); + } } /* find the first empty slot */ @@ -1533,6 +1516,17 @@ static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods, } #endif +static void ads_print_error(int ret, LDAP *ld) +{ + if (ret != 0) { + char *ld_error = NULL; + ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &ld_error); + DEBUG(10,("AD LDAP failure %d (%s):\n%s\n", ret, + ldap_err2string(ret), ld_error)); + SAFE_FREE(ld_error); + } +} + /** * Perform an ldap modify * @param ads connection to ads server @@ -1568,6 +1562,7 @@ ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods) mods[i] = NULL; ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn, (LDAPMod **) mods, controls, NULL); + ads_print_error(ret, ads->ldap.ld); TALLOC_FREE(utf8_dn); return ADS_ERROR(ret); } @@ -1596,6 +1591,7 @@ ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods) mods[i] = NULL; ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods); + ads_print_error(ret, ads->ldap.ld); TALLOC_FREE(utf8_dn); return ADS_ERROR(ret); } @@ -1617,6 +1613,7 @@ ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn) } ret = ldap_delete_s(ads->ldap.ld, utf8_dn); + ads_print_error(ret, ads->ldap.ld); TALLOC_FREE(utf8_dn); return ADS_ERROR(ret); } @@ -1768,10 +1765,10 @@ ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods, * @return the kvno for the account, or -1 in case of a failure. **/ -uint32 ads_get_kvno(ADS_STRUCT *ads, const char *account_name) +uint32_t ads_get_kvno(ADS_STRUCT *ads, const char *account_name) { LDAPMessage *res = NULL; - uint32 kvno = (uint32)-1; /* -1 indicates a failure */ + uint32_t kvno = (uint32_t)-1; /* -1 indicates a failure */ char *filter; const char *attrs[] = {"msDS-KeyVersionNumber", NULL}; char *dn_string = NULL; @@ -1905,33 +1902,123 @@ ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machin return ret; } +/** + * @brief Search for an element in a string array. + * + * @param[in] el_array The string array to search. + * + * @param[in] num_el The number of elements in the string array. + * + * @param[in] el The string to search. + * + * @return True if found, false if not. + */ +bool ads_element_in_array(const char **el_array, size_t num_el, const char *el) +{ + size_t i; + + if (el_array == NULL || num_el == 0 || el == NULL) { + return false; + } + + for (i = 0; i < num_el && el_array[i] != NULL; i++) { + int cmp; + + cmp = strcasecmp_m(el_array[i], el); + if (cmp == 0) { + return true; + } + } + + return false; +} + +/** + * @brief This gets the service principal names of an existing computer account. + * + * @param[in] mem_ctx The memory context to use to allocate the spn array. + * + * @param[in] ads The ADS context to use. + * + * @param[in] machine_name The NetBIOS name of the computer, which is used to + * identify the computer account. + * + * @param[in] spn_array A pointer to store the array for SPNs. + * + * @param[in] num_spns The number of principals stored in the array. + * + * @return 0 on success, or a ADS error if a failure occurred. + */ +ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx, + ADS_STRUCT *ads, + const char *machine_name, + char ***spn_array, + size_t *num_spns) +{ + ADS_STATUS status; + LDAPMessage *res = NULL; + int count; + + status = ads_find_machine_acct(ads, + &res, + machine_name); + if (!ADS_ERR_OK(status)) { + DEBUG(1,("Host Account for %s not found... skipping operation.\n", + machine_name)); + return status; + } + + count = ads_count_replies(ads, res); + if (count != 1) { + status = ADS_ERROR(LDAP_NO_SUCH_OBJECT); + goto done; + } + + *spn_array = ads_pull_strings(ads, + mem_ctx, + res, + "servicePrincipalName", + num_spns); + if (*spn_array == NULL) { + DEBUG(1, ("Host account for %s does not have service principal " + "names.\n", + machine_name)); + status = ADS_ERROR(LDAP_NO_SUCH_OBJECT); + goto done; + } + +done: + ads_msgfree(ads, res); + + return status; +} + /** * This adds a service principal name to an existing computer account * (found by hostname) in AD. * @param ads An initialized ADS_STRUCT * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account. - * @param my_fqdn The fully qualified DNS name of the machine - * @param spn A string of the service principal to add, i.e. 'host' + * @param spns An array or strings for the service principals to add, + * i.e. 'cifs/machine_name', 'http/machine.full.domain.com' etc. * @return 0 upon sucess, or non-zero if a failure occurs **/ -ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name, - const char *my_fqdn, const char *spn) +ADS_STATUS ads_add_service_principal_names(ADS_STRUCT *ads, + const char *machine_name, + const char **spns) { ADS_STATUS ret; TALLOC_CTX *ctx; LDAPMessage *res = NULL; - char *psp1, *psp2; ADS_MODLIST mods; char *dn_string = NULL; - const char *servicePrincipalName[3] = {NULL, NULL, NULL}; + const char **servicePrincipalName = spns; ret = ads_find_machine_acct(ads, &res, machine_name); if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) { DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n", machine_name)); - DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n", - spn, machine_name, ads->config.realm)); + DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principals have NOT been added.\n")); ads_msgfree(ads, res); return ADS_ERROR(LDAP_NO_SUCH_OBJECT); } @@ -1942,54 +2029,24 @@ ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_n return ADS_ERROR(LDAP_NO_MEMORY); } - /* add short name spn */ - - if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) { - talloc_destroy(ctx); - ads_msgfree(ads, res); - return ADS_ERROR(LDAP_NO_MEMORY); - } - if (!strupper_m(psp1)) { - ret = ADS_ERROR(LDAP_NO_MEMORY); - goto out; - } - - if (!strlower_m(&psp1[strlen(spn)])) { - ret = ADS_ERROR(LDAP_NO_MEMORY); - goto out; - } - servicePrincipalName[0] = psp1; - - DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", - psp1, machine_name)); + DEBUG(5,("ads_add_service_principal_name: INFO: " + "Adding %s to host %s\n", + spns[0] ? "N/A" : spns[0], machine_name)); - /* add fully qualified spn */ - - if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) { - ret = ADS_ERROR(LDAP_NO_MEMORY); - goto out; - } - if (!strupper_m(psp2)) { - ret = ADS_ERROR(LDAP_NO_MEMORY); - goto out; - } - - if (!strlower_m(&psp2[strlen(spn)])) { - ret = ADS_ERROR(LDAP_NO_MEMORY); - goto out; - } - servicePrincipalName[1] = psp2; - - DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", - psp2, machine_name)); + DEBUG(5,("ads_add_service_principal_name: INFO: " + "Adding %s to host %s\n", + spns[1] ? "N/A" : spns[1], machine_name)); if ( (mods = ads_init_mods(ctx)) == NULL ) { ret = ADS_ERROR(LDAP_NO_MEMORY); goto out; } - ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName); + ret = ads_add_strlist(ctx, + &mods, + "servicePrincipalName", + servicePrincipalName); if (!ADS_ERR_OK(ret)) { DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n")); goto out; @@ -2021,8 +2078,10 @@ ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_n * @return 0 upon success, or non-zero otherwise **/ -ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name, - const char *org_unit) +ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, + const char *machine_name, + const char *org_unit, + uint32_t etype_list) { ADS_STATUS ret; char *samAccountName, *controlstr; @@ -2033,9 +2092,15 @@ ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name, const char *objectClass[] = {"top", "person", "organizationalPerson", "user", "computer", NULL}; LDAPMessage *res = NULL; - uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\ + uint32_t acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\ UF_DONT_EXPIRE_PASSWD |\ UF_ACCOUNTDISABLE ); + uint32_t func_level = 0; + + ret = ads_domain_func_level(ads, &func_level); + if (!ADS_ERR_OK(ret)) { + return ret; + } if (!(ctx = talloc_init("ads_add_machine_acct"))) return ADS_ERROR(LDAP_NO_MEMORY); @@ -2054,10 +2119,6 @@ ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name, goto done; } -#ifndef ENCTYPE_ARCFOUR_HMAC - acct_control |= UF_USE_DES_KEY_ONLY; -#endif - if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) { goto done; } @@ -2071,6 +2132,17 @@ ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name, ads_mod_strlist(ctx, &mods, "objectClass", objectClass); ads_mod_str(ctx, &mods, "userAccountControl", controlstr); + if (func_level >= DS_DOMAIN_FUNCTION_2008) { + const char *etype_list_str; + + etype_list_str = talloc_asprintf(ctx, "%d", (int)etype_list); + if (etype_list_str == NULL) { + goto done; + } + ads_mod_str(ctx, &mods, "msDS-SupportedEncryptionTypes", + etype_list_str); + } + ret = ads_gen_add(ads, new_dn, mods); done: @@ -2157,8 +2229,9 @@ done: */ static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values) { - int i, j; + size_t i; for (i=0; values[i]; i++) { + ber_len_t j; printf("%s: ", field); for (j=0; jbv_len; j++) { printf("%02X", (unsigned char)values[i]->bv_val[j]); @@ -2191,12 +2264,15 @@ static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values) { int i; for (i=0; values[i]; i++) { + struct sid_parse_ret ret; struct dom_sid sid; - fstring tmp; - if (!sid_parse(values[i]->bv_val, values[i]->bv_len, &sid)) { + struct dom_sid_buf tmp; + ret = sid_parse((const uint8_t *)values[i]->bv_val, + values[i]->bv_len, &sid); + if (ret.len == -1) { return; } - printf("%s: %s\n", field, sid_to_fstring(tmp, &sid)); + printf("%s: %s\n", field, dom_sid_str_buf(&sid, &tmp)); } } @@ -2209,7 +2285,7 @@ static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values) struct security_descriptor *psd; NTSTATUS status; - status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val, + status = unmarshall_sec_desc(talloc_tos(), (uint8_t *)values[0]->bv_val, values[0]->bv_len, &psd); if (!NT_STATUS_IS_OK(status)) { DEBUG(0, ("unmarshall_sec_desc failed: %s\n", @@ -2328,7 +2404,8 @@ static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *da utf8_field=ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg,b)) { struct berval **ber_vals; - char **str_vals, **utf8_vals; + char **str_vals; + char **utf8_vals; char *field; bool string; @@ -2343,10 +2420,12 @@ static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *da string = fn(ads, field, NULL, data_area); if (string) { + const char **p; + utf8_vals = ldap_get_values(ads->ldap.ld, (LDAPMessage *)msg, field); - str_vals = ads_pull_strvals(ctx, - (const char **) utf8_vals); + p = discard_const_p(const char *, utf8_vals); + str_vals = ads_pull_strvals(ctx, p); fn(ads, field, (void **) str_vals, data_area); ldap_value_free(utf8_vals); } else { @@ -2464,8 +2543,7 @@ int ads_count_replies(ADS_STRUCT *ads, void *res) { char **values; char **ret = NULL; - int i; - size_t converted_size; + size_t i, converted_size; values = ldap_get_values(ads->ldap.ld, msg, field); if (!values) @@ -2622,7 +2700,7 @@ int ads_count_replies(ADS_STRUCT *ads, void *res) } /** - * pull a single uint32 from a ADS result + * pull a single uint32_t from a ADS result * @param ads connection to ads server * @param msg Results of search * @param field Attribute to retrieve @@ -2630,7 +2708,7 @@ int ads_count_replies(ADS_STRUCT *ads, void *res) * @return boolean inidicating success */ bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field, - uint32 *v) + uint32_t *v) { char **values; @@ -2697,7 +2775,6 @@ int ads_count_replies(ADS_STRUCT *ads, void *res) LDAPMessage *msg, const char *field, struct dom_sid **sids) { struct berval **values; - bool ret; int count, i; values = ldap_get_values_len(ads->ldap.ld, msg, field); @@ -2720,10 +2797,13 @@ int ads_count_replies(ADS_STRUCT *ads, void *res) count = 0; for (i=0; values[i]; i++) { - ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]); - if (ret) { - DEBUG(10, ("pulling SID: %s\n", - sid_string_dbg(&(*sids)[count]))); + struct sid_parse_ret ret; + ret = sid_parse((const uint8_t *)values[i]->bv_val, + values[i]->bv_len, &(*sids)[count]); + if (ret.len != -1) { + struct dom_sid_buf buf; + DBG_DEBUG("pulling SID: %s\n", + dom_sid_str_buf(&(*sids)[count], &buf)); count++; } } @@ -2755,7 +2835,7 @@ int ads_count_replies(ADS_STRUCT *ads, void *res) if (values[0]) { NTSTATUS status; status = unmarshall_sec_desc(mem_ctx, - (uint8 *)values[0]->bv_val, + (uint8_t *)values[0]->bv_val, values[0]->bv_len, sd); if (!NT_STATUS_IS_OK(status)) { DEBUG(0, ("unmarshall_sec_desc failed: %s\n", @@ -2805,7 +2885,7 @@ int ads_count_replies(ADS_STRUCT *ads, void *res) * @param usn Pointer to retrieved update serial number * @return status of search **/ -ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn) +ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn) { const char *attrs[] = {"highestCommittedUSN", NULL}; ADS_STATUS status; @@ -2871,6 +2951,7 @@ ADS_STATUS ads_current_time(ADS_STRUCT *ads) if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup, ads->server.ldap_server )) == NULL ) { + status = ADS_ERROR(LDAP_NO_MEMORY); goto done; } ads_s->auth.flags = ADS_AUTH_ANON_BIND; @@ -2917,7 +2998,7 @@ done: /******************************************************************** ********************************************************************/ -ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val) +ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val) { const char *attrs[] = {"domainFunctionality", NULL}; ADS_STATUS status; @@ -3187,7 +3268,7 @@ ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads, for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) { - + const char **p = discard_const_p(const char *, *ous); char *dn = NULL; dn = ads_get_dn(ads, talloc_tos(), msg); @@ -3196,15 +3277,14 @@ ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads, return ADS_ERROR(LDAP_NO_MEMORY); } - if (!add_string_to_array(mem_ctx, dn, - (const char ***)ous, - (int *)num_ous)) { + if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) { TALLOC_FREE(dn); ads_msgfree(ads, res); return ADS_ERROR(LDAP_NO_MEMORY); } TALLOC_FREE(dn); + *ous = discard_const_p(char *, p); } ads_msgfree(ads, res); @@ -3278,6 +3358,7 @@ ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx, } break; case ADS_EXTENDED_DN_HEX_STRING: { + struct sid_parse_ret ret; fstring buf; size_t buf_len; @@ -3286,7 +3367,8 @@ ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx, return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER); } - if (!sid_parse(buf, buf_len, sid)) { + ret = sid_parse((const uint8_t *)buf, buf_len, sid); + if (ret.len == -1) { DEBUG(10,("failed to parse sid\n")); return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER); } @@ -3310,7 +3392,7 @@ char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine int count = 0; char *name = NULL; - status = ads_find_machine_acct(ads, &res, lp_netbios_name()); + status = ads_find_machine_acct(ads, &res, machine_name); if (!ADS_ERR_OK(status)) { DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n", lp_netbios_name())); @@ -3367,33 +3449,37 @@ out: /******************************************************************** ********************************************************************/ -char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name ) +bool ads_has_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name ) { LDAPMessage *res = NULL; ADS_STATUS status; int count = 0; char *name = NULL; + bool ok = false; - status = ads_find_machine_acct(ads, &res, lp_netbios_name()); + status = ads_find_machine_acct(ads, &res, machine_name); if (!ADS_ERR_OK(status)) { - DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n", + DEBUG(0,("ads_has_samaccountname: Failed to find account for %s\n", lp_netbios_name())); goto out; } if ( (count = ads_count_replies(ads, res)) != 1 ) { - DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count)); + DEBUG(1,("ads_has_samaccountname: %d entries returned!\n", count)); goto out; } if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) { - DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n")); + DEBUG(0,("ads_has_samaccountname: No sAMAccountName attribute!\n")); } out: ads_msgfree(ads, res); - - return name; + if (name != NULL) { + ok = (strlen(name) > 0); + } + TALLOC_FREE(name); + return ok; } #if 0 @@ -3409,7 +3495,7 @@ out: * @return status of join **/ ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name, - uint32 account_type, const char *org_unit) + uint32_t account_type, const char *org_unit) { ADS_STATUS status; LDAPMessage *res = NULL; @@ -3605,7 +3691,7 @@ ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname) struct dom_sid *tmp_sids; struct dom_sid tmp_user_sid; struct dom_sid tmp_primary_group_sid; - uint32 pgid; + uint32_t pgid; const char *attrs[] = { "objectSid", "tokenGroups", @@ -3687,14 +3773,14 @@ ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname) * @param ads connection to ads server * @param mem_ctx TALLOC_CTX for allocating sid array * @param samaccountname to search - * @param uac_ret uint32 pointer userAccountControl attribute value + * @param uac_ret uint32_t pointer userAccountControl attribute value * @param dn_ret pointer to dn * @return status of token query **/ ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *samaccountname, - uint32 *uac_ret, + uint32_t *uac_ret, const char **dn_ret) { ADS_STATUS status; @@ -3702,7 +3788,7 @@ ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads, const char *filter; LDAPMessage *res = NULL; char *dn = NULL; - uint32 uac = 0; + uint32_t uac = 0; filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))", samaccountname); @@ -3860,10 +3946,16 @@ ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx, const char *name; char *ou_string; - exploded_dn = ldap_explode_dn(*account_ou, 0); - if (exploded_dn) { - ldap_value_free(exploded_dn); - return ADS_SUCCESS; + if (account_ou == NULL) { + return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER); + } + + if (*account_ou != NULL) { + exploded_dn = ldap_explode_dn(*account_ou, 0); + if (exploded_dn) { + ldap_value_free(exploded_dn); + return ADS_SUCCESS; + } } ou_string = ads_ou_string(ads, *account_ou);