2 Unix SMB/CIFS implementation.
3 ads (active directory) utility library
4 Copyright (C) Andrew Tridgell 2001
5 Copyright (C) Remus Koos 2001
6 Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
7 Copyright (C) Guenther Deschner 2005
8 Copyright (C) Gerald Carter 2006
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
31 * @brief basic ldap client-side routines for ads server communications
33 * The routines contained here should do the necessary ldap calls for
36 * Important note: attribute names passed into ads_ routines must
37 * already be in UTF-8 format. We do not convert them because in almost
38 * all cases, they are just ascii (which is represented with the same
39 * codepoints in UTF-8). This may have to change at some point
43 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
45 static SIG_ATOMIC_T gotalarm;
47 /***************************************************************
48 Signal function to tell us we timed out.
49 ****************************************************************/
51 static void gotalarm_sig(void)
56 LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
62 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
64 /* End setup timeout. */
66 ldp = ldap_open(server, port);
69 DEBUG(2,("Could not open LDAP connection to %s:%d: %s\n",
70 server, port, strerror(errno)));
73 /* Teardown timeout. */
74 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
80 static int ldap_search_with_timeout(LDAP *ld,
81 LDAP_CONST char *base,
83 LDAP_CONST char *filter,
91 struct timeval timeout;
94 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
95 timeout.tv_sec = lp_ldap_timeout();
98 /* Setup alarm timeout.... Do we need both of these ? JRA. */
100 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
101 alarm(lp_ldap_timeout());
102 /* End setup timeout. */
104 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
105 attrsonly, sctrls, cctrls, &timeout,
108 /* Teardown timeout. */
109 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
113 return LDAP_TIMELIMIT_EXCEEDED;
119 /**********************************************
120 Do client and server sitename match ?
121 **********************************************/
123 BOOL ads_sitename_match(ADS_STRUCT *ads)
125 if (ads->config.server_site_name == NULL &&
126 ads->config.client_site_name == NULL ) {
127 DEBUG(10,("ads_sitename_match: both null\n"));
130 if (ads->config.server_site_name &&
131 ads->config.client_site_name &&
132 strequal(ads->config.server_site_name,
133 ads->config.client_site_name)) {
134 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
137 DEBUG(10,("ads_sitename_match: no match %s %s\n",
138 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
139 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
145 try a connection to a given ldap server, returning True and setting the servers IP
146 in the ads struct if successful
148 BOOL ads_try_connect(ADS_STRUCT *ads, const char *server )
151 struct cldap_netlogon_reply cldap_reply;
153 if (!server || !*server) {
157 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
158 server, ads->server.realm));
160 /* this copes with inet_ntoa brokenness */
162 srv = SMB_STRDUP(server);
164 ZERO_STRUCT( cldap_reply );
166 if ( !ads_cldap_netlogon( srv, ads->server.realm, &cldap_reply ) ) {
167 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
172 /* Check the CLDAP reply flags */
174 if ( !(cldap_reply.flags & ADS_LDAP) ) {
175 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
181 /* Fill in the ads->config values */
183 SAFE_FREE(ads->config.realm);
184 SAFE_FREE(ads->config.bind_path);
185 SAFE_FREE(ads->config.ldap_server_name);
186 SAFE_FREE(ads->config.server_site_name);
187 SAFE_FREE(ads->config.client_site_name);
188 SAFE_FREE(ads->server.workgroup);
190 ads->config.flags = cldap_reply.flags;
191 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.hostname);
192 strupper_m(cldap_reply.domain);
193 ads->config.realm = SMB_STRDUP(cldap_reply.domain);
194 ads->config.bind_path = ads_build_dn(ads->config.realm);
195 if (*cldap_reply.server_site_name) {
196 ads->config.server_site_name =
197 SMB_STRDUP(cldap_reply.server_site_name);
199 if (*cldap_reply.client_site_name) {
200 ads->config.client_site_name =
201 SMB_STRDUP(cldap_reply.client_site_name);
204 ads->server.workgroup = SMB_STRDUP(cldap_reply.netbios_domain);
206 ads->ldap_port = LDAP_PORT;
207 ads->ldap_ip = *interpret_addr2(srv);
210 /* Store our site name. */
211 sitename_store( cldap_reply.client_site_name );
216 /**********************************************************************
217 Try to find an AD dc using our internal name resolution routines
218 Try the realm first and then then workgroup name if netbios is not
220 **********************************************************************/
222 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
226 struct ip_service *ip_list;
228 BOOL got_realm = False;
229 BOOL use_own_domain = False;
230 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
232 /* if the realm and workgroup are both empty, assume they are ours */
235 c_realm = ads->server.realm;
237 if ( !c_realm || !*c_realm ) {
238 /* special case where no realm and no workgroup means our own */
239 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
240 use_own_domain = True;
241 c_realm = lp_realm();
245 if (c_realm && *c_realm)
249 /* we need to try once with the realm name and fallback to the
250 netbios domain name if we fail (if netbios has not been disabled */
252 if ( !got_realm && !lp_disable_netbios() ) {
253 c_realm = ads->server.workgroup;
254 if (!c_realm || !*c_realm) {
255 if ( use_own_domain )
256 c_realm = lp_workgroup();
259 if ( !c_realm || !*c_realm ) {
260 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
261 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
265 pstrcpy( realm, c_realm );
267 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
268 (got_realm ? "realm" : "domain"), realm));
270 status = get_sorted_dc_list(realm, &ip_list, &count, got_realm);
271 if (!NT_STATUS_IS_OK(status)) {
272 /* fall back to netbios if we can */
273 if ( got_realm && !lp_disable_netbios() ) {
281 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
282 for ( i=0; i<count; i++ ) {
285 fstrcpy( server, inet_ntoa(ip_list[i].ip) );
287 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
290 if ( ads_try_connect(ads, server) ) {
295 /* keep track of failures */
296 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
301 return NT_STATUS_NO_LOGON_SERVERS;
306 * Connect to the LDAP server
307 * @param ads Pointer to an existing ADS_STRUCT
308 * @return status of connection
310 ADS_STATUS ads_connect(ADS_STRUCT *ads)
312 int version = LDAP_VERSION3;
316 ads->last_attempt = time(NULL);
319 /* try with a user specified server */
321 if (ads->server.ldap_server &&
322 ads_try_connect(ads, ads->server.ldap_server)) {
326 ntstatus = ads_find_dc(ads);
327 if (NT_STATUS_IS_OK(ntstatus)) {
331 return ADS_ERROR_NT(ntstatus);
334 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
336 if (!ads->auth.user_name) {
337 /* Must use the userPrincipalName value here or sAMAccountName
338 and not servicePrincipalName; found by Guenther Deschner */
340 asprintf(&ads->auth.user_name, "%s$", global_myname() );
343 if (!ads->auth.realm) {
344 ads->auth.realm = SMB_STRDUP(ads->config.realm);
347 if (!ads->auth.kdc_server) {
348 ads->auth.kdc_server = SMB_STRDUP(inet_ntoa(ads->ldap_ip));
352 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
353 to MIT kerberos to work (tridge) */
356 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
357 setenv(env, ads->auth.kdc_server, 1);
362 /* If the caller() requested no LDAP bind, then we are done */
364 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
368 /* Otherwise setup the TCP LDAP session */
370 if ( (ads->ld = ldap_open_with_timeout(ads->config.ldap_server_name,
371 LDAP_PORT, lp_ldap_timeout())) == NULL )
373 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
376 /* cache the successful connection */
377 saf_store( ads->server.workgroup, inet_ntoa(ads->ldap_ip));
379 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
381 status = ADS_ERROR(smb_ldap_start_tls(ads->ld, version));
382 if (!ADS_ERR_OK(status)) {
386 /* fill in the current time and offsets */
388 status = ads_current_time( ads );
389 if ( !ADS_ERR_OK(status) ) {
393 /* Now do the bind */
395 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
396 return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
399 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
400 return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
403 return ads_sasl_bind(ads);
407 Duplicate a struct berval into talloc'ed memory
409 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
411 struct berval *value;
413 if (!in_val) return NULL;
415 value = TALLOC_ZERO_P(ctx, struct berval);
418 if (in_val->bv_len == 0) return value;
420 value->bv_len = in_val->bv_len;
421 value->bv_val = TALLOC_MEMDUP(ctx, in_val->bv_val, in_val->bv_len);
426 Make a values list out of an array of (struct berval *)
428 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
429 const struct berval **in_vals)
431 struct berval **values;
434 if (!in_vals) return NULL;
435 for (i=0; in_vals[i]; i++)
437 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
438 if (!values) return NULL;
440 for (i=0; in_vals[i]; i++) {
441 values[i] = dup_berval(ctx, in_vals[i]);
447 UTF8-encode a values list out of an array of (char *)
449 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
454 if (!in_vals) return NULL;
455 for (i=0; in_vals[i]; i++)
457 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
458 if (!values) return NULL;
460 for (i=0; in_vals[i]; i++) {
461 push_utf8_talloc(ctx, &values[i], in_vals[i]);
467 Pull a (char *) array out of a UTF8-encoded values list
469 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
474 if (!in_vals) return NULL;
475 for (i=0; in_vals[i]; i++)
477 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
478 if (!values) return NULL;
480 for (i=0; in_vals[i]; i++) {
481 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
487 * Do a search with paged results. cookie must be null on the first
488 * call, and then returned on each subsequent call. It will be null
489 * again when the entire search is complete
490 * @param ads connection to ads server
491 * @param bind_path Base dn for the search
492 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
493 * @param expr Search expression - specified in local charset
494 * @param attrs Attributes to retrieve - specified in utf8 or ascii
495 * @param res ** which will contain results - free res* with ads_msgfree()
496 * @param count Number of entries retrieved on this page
497 * @param cookie The paged results cookie to be returned on subsequent calls
498 * @return status of search
500 ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads, const char *bind_path,
501 int scope, const char *expr,
502 const char **attrs, void *args, void **res,
503 int *count, void **cookie)
506 char *utf8_expr, *utf8_path, **search_attrs;
507 LDAPControl PagedResults, NoReferrals, ExtendedDn, *controls[4], **rcontrols;
508 BerElement *cookie_be = NULL;
509 struct berval *cookie_bv= NULL;
510 BerElement *extdn_be = NULL;
511 struct berval *extdn_bv= NULL;
514 ads_control *external_control = (ads_control *) args;
518 if (!(ctx = talloc_init("ads_do_paged_search_args")))
519 return ADS_ERROR(LDAP_NO_MEMORY);
521 /* 0 means the conversion worked but the result was empty
522 so we only fail if it's -1. In any case, it always
523 at least nulls out the dest */
524 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
525 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
530 if (!attrs || !(*attrs))
533 /* This would be the utf8-encoded version...*/
534 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
535 if (!(str_list_copy(&search_attrs, attrs))) {
542 /* Paged results only available on ldap v3 or later */
543 ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
544 if (version < LDAP_VERSION3) {
545 rc = LDAP_NOT_SUPPORTED;
549 cookie_be = ber_alloc_t(LBER_USE_DER);
551 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
552 ber_bvfree(*cookie); /* don't need it from last time */
555 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
557 ber_flatten(cookie_be, &cookie_bv);
558 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
559 PagedResults.ldctl_iscritical = (char) 1;
560 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
561 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
563 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
564 NoReferrals.ldctl_iscritical = (char) 0;
565 NoReferrals.ldctl_value.bv_len = 0;
566 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
568 if (external_control && strequal(external_control->control, ADS_EXTENDED_DN_OID)) {
570 ExtendedDn.ldctl_oid = CONST_DISCARD(char *, external_control->control);
571 ExtendedDn.ldctl_iscritical = (char) external_control->critical;
573 /* win2k does not accept a ldctl_value beeing passed in */
575 if (external_control->val != 0) {
577 if ((extdn_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
582 if ((ber_printf(extdn_be, "{i}", (ber_int_t) external_control->val)) == -1) {
586 if ((ber_flatten(extdn_be, &extdn_bv)) == -1) {
591 ExtendedDn.ldctl_value.bv_len = extdn_bv->bv_len;
592 ExtendedDn.ldctl_value.bv_val = extdn_bv->bv_val;
595 ExtendedDn.ldctl_value.bv_len = 0;
596 ExtendedDn.ldctl_value.bv_val = NULL;
599 controls[0] = &NoReferrals;
600 controls[1] = &PagedResults;
601 controls[2] = &ExtendedDn;
605 controls[0] = &NoReferrals;
606 controls[1] = &PagedResults;
610 /* we need to disable referrals as the openldap libs don't
611 handle them and paged results at the same time. Using them
612 together results in the result record containing the server
613 page control being removed from the result list (tridge/jmcd)
615 leaving this in despite the control that says don't generate
616 referrals, in case the server doesn't support it (jmcd)
618 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
620 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
621 search_attrs, 0, controls,
623 (LDAPMessage **)res);
625 ber_free(cookie_be, 1);
626 ber_bvfree(cookie_bv);
629 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
630 ldap_err2string(rc)));
634 rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
635 NULL, &rcontrols, 0);
641 for (i=0; rcontrols[i]; i++) {
642 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
643 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
644 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
646 /* the berval is the cookie, but must be freed when
648 if (cookie_bv->bv_len) /* still more to do */
649 *cookie=ber_bvdup(cookie_bv);
652 ber_bvfree(cookie_bv);
653 ber_free(cookie_be, 1);
657 ldap_controls_free(rcontrols);
663 ber_free(extdn_be, 1);
667 ber_bvfree(extdn_bv);
670 /* if/when we decide to utf8-encode attrs, take out this next line */
671 str_list_free(&search_attrs);
673 return ADS_ERROR(rc);
676 ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
677 int scope, const char *expr,
678 const char **attrs, void **res,
679 int *count, void **cookie)
681 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
686 * Get all results for a search. This uses ads_do_paged_search() to return
687 * all entries in a large search.
688 * @param ads connection to ads server
689 * @param bind_path Base dn for the search
690 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
691 * @param expr Search expression
692 * @param attrs Attributes to retrieve
693 * @param res ** which will contain results - free res* with ads_msgfree()
694 * @return status of search
696 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
697 int scope, const char *expr,
698 const char **attrs, void *args, void **res)
705 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
708 if (!ADS_ERR_OK(status))
711 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
715 LDAPMessage *msg, *next;
717 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
718 attrs, args, &res2, &count, &cookie);
720 if (!ADS_ERR_OK(status2)) break;
722 /* this relies on the way that ldap_add_result_entry() works internally. I hope
723 that this works on all ldap libs, but I have only tested with openldap */
724 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
725 next = ads_next_entry(ads, msg);
726 ldap_add_result_entry((LDAPMessage **)res, msg);
728 /* note that we do not free res2, as the memory is now
729 part of the main returned list */
732 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
733 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
739 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
740 int scope, const char *expr,
741 const char **attrs, void **res)
743 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
747 * Run a function on all results for a search. Uses ads_do_paged_search() and
748 * runs the function as each page is returned, using ads_process_results()
749 * @param ads connection to ads server
750 * @param bind_path Base dn for the search
751 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
752 * @param expr Search expression - specified in local charset
753 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
754 * @param fn Function which takes attr name, values list, and data_area
755 * @param data_area Pointer which is passed to function on each call
756 * @return status of search
758 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
759 int scope, const char *expr, const char **attrs,
760 BOOL(*fn)(char *, void **, void *),
768 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
771 if (!ADS_ERR_OK(status)) return status;
773 ads_process_results(ads, res, fn, data_area);
774 ads_msgfree(ads, res);
777 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
778 &res, &count, &cookie);
780 if (!ADS_ERR_OK(status)) break;
782 ads_process_results(ads, res, fn, data_area);
783 ads_msgfree(ads, res);
790 * Do a search with a timeout.
791 * @param ads connection to ads server
792 * @param bind_path Base dn for the search
793 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
794 * @param expr Search expression
795 * @param attrs Attributes to retrieve
796 * @param res ** which will contain results - free res* with ads_msgfree()
797 * @return status of search
799 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
801 const char **attrs, void **res)
804 char *utf8_expr, *utf8_path, **search_attrs = NULL;
808 if (!(ctx = talloc_init("ads_do_search"))) {
809 DEBUG(1,("ads_do_search: talloc_init() failed!"));
810 return ADS_ERROR(LDAP_NO_MEMORY);
813 /* 0 means the conversion worked but the result was empty
814 so we only fail if it's negative. In any case, it always
815 at least nulls out the dest */
816 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
817 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
818 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
823 if (!attrs || !(*attrs))
826 /* This would be the utf8-encoded version...*/
827 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
828 if (!(str_list_copy(&search_attrs, attrs)))
830 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
836 /* see the note in ads_do_paged_search - we *must* disable referrals */
837 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
839 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
840 search_attrs, 0, NULL, NULL,
842 (LDAPMessage **)res);
844 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
845 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
851 /* if/when we decide to utf8-encode attrs, take out this next line */
852 str_list_free(&search_attrs);
853 return ADS_ERROR(rc);
856 * Do a general ADS search
857 * @param ads connection to ads server
858 * @param res ** which will contain results - free res* with ads_msgfree()
859 * @param expr Search expression
860 * @param attrs Attributes to retrieve
861 * @return status of search
863 ADS_STATUS ads_search(ADS_STRUCT *ads, void **res,
867 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
872 * Do a search on a specific DistinguishedName
873 * @param ads connection to ads server
874 * @param res ** which will contain results - free res* with ads_msgfree()
875 * @param dn DistinguishName to search
876 * @param attrs Attributes to retrieve
877 * @return status of search
879 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, void *_res,
883 void **res = (void **)_res;
884 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res);
888 * Free up memory from a ads_search
889 * @param ads connection to ads server
890 * @param msg Search results to free
892 void ads_msgfree(ADS_STRUCT *ads, void *msg)
899 * Free up memory from various ads requests
900 * @param ads connection to ads server
901 * @param mem Area to free
903 void ads_memfree(ADS_STRUCT *ads, void *mem)
909 * Get a dn from search results
910 * @param ads connection to ads server
911 * @param msg Search result
914 char *ads_get_dn(ADS_STRUCT *ads, void *msg)
916 char *utf8_dn, *unix_dn;
918 utf8_dn = ldap_get_dn(ads->ld, msg);
921 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
925 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
926 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
930 ldap_memfree(utf8_dn);
935 * Get a canonical dn from search results
936 * @param ads connection to ads server
937 * @param msg Search result
940 char *ads_get_dn_canonical(ADS_STRUCT *ads, void *msg)
942 #ifdef HAVE_LDAP_DN2AD_CANONICAL
943 return ldap_dn2ad_canonical(ads_get_dn(ads, msg));
950 * Get the parent from a dn
951 * @param dn the dn to return the parent from
952 * @return parent dn string
954 char *ads_parent_dn(const char *dn)
972 * Find a machine account given a hostname
973 * @param ads connection to ads server
974 * @param res ** which will contain results - free res* with ads_msgfree()
975 * @param host Hostname to search for
976 * @return status of search
978 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *machine)
982 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
986 /* the easiest way to find a machine account anywhere in the tree
987 is to look for hostname$ */
988 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
989 DEBUG(1, ("asprintf failed!\n"));
990 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
993 status = ads_search(ads, res, expr, attrs);
999 * Initialize a list of mods to be used in a modify request
1000 * @param ctx An initialized TALLOC_CTX
1001 * @return allocated ADS_MODLIST
1003 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1005 #define ADS_MODLIST_ALLOC_SIZE 10
1008 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1009 /* -1 is safety to make sure we don't go over the end.
1010 need to reset it to NULL before doing ldap modify */
1011 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1013 return (ADS_MODLIST)mods;
1018 add an attribute to the list, with values list already constructed
1020 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1021 int mod_op, const char *name,
1022 const void *_invals)
1024 const void **invals = (const void **)_invals;
1026 LDAPMod **modlist = (LDAPMod **) *mods;
1027 struct berval **ber_values = NULL;
1028 char **char_values = NULL;
1031 mod_op = LDAP_MOD_DELETE;
1033 if (mod_op & LDAP_MOD_BVALUES)
1034 ber_values = ads_dup_values(ctx,
1035 (const struct berval **)invals);
1037 char_values = ads_push_strvals(ctx,
1038 (const char **) invals);
1041 /* find the first empty slot */
1042 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1044 if (modlist[curmod] == (LDAPMod *) -1) {
1045 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1046 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1047 return ADS_ERROR(LDAP_NO_MEMORY);
1048 memset(&modlist[curmod], 0,
1049 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1050 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1051 *mods = (ADS_MODLIST)modlist;
1054 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1055 return ADS_ERROR(LDAP_NO_MEMORY);
1056 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1057 if (mod_op & LDAP_MOD_BVALUES) {
1058 modlist[curmod]->mod_bvalues = ber_values;
1059 } else if (mod_op & LDAP_MOD_DELETE) {
1060 modlist[curmod]->mod_values = NULL;
1062 modlist[curmod]->mod_values = char_values;
1065 modlist[curmod]->mod_op = mod_op;
1066 return ADS_ERROR(LDAP_SUCCESS);
1070 * Add a single string value to a mod list
1071 * @param ctx An initialized TALLOC_CTX
1072 * @param mods An initialized ADS_MODLIST
1073 * @param name The attribute name to add
1074 * @param val The value to add - NULL means DELETE
1075 * @return ADS STATUS indicating success of add
1077 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1078 const char *name, const char *val)
1080 const char *values[2];
1086 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1087 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1091 * Add an array of string values to a mod list
1092 * @param ctx An initialized TALLOC_CTX
1093 * @param mods An initialized ADS_MODLIST
1094 * @param name The attribute name to add
1095 * @param vals The array of string values to add - NULL means DELETE
1096 * @return ADS STATUS indicating success of add
1098 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1099 const char *name, const char **vals)
1102 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1103 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1104 name, (const void **) vals);
1109 * Add a single ber-encoded value to a mod list
1110 * @param ctx An initialized TALLOC_CTX
1111 * @param mods An initialized ADS_MODLIST
1112 * @param name The attribute name to add
1113 * @param val The value to add - NULL means DELETE
1114 * @return ADS STATUS indicating success of add
1116 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1117 const char *name, const struct berval *val)
1119 const struct berval *values[2];
1124 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1125 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1126 name, (const void **) values);
1131 * Perform an ldap modify
1132 * @param ads connection to ads server
1133 * @param mod_dn DistinguishedName to modify
1134 * @param mods list of modifications to perform
1135 * @return status of modify
1137 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1140 char *utf8_dn = NULL;
1142 this control is needed to modify that contains a currently
1143 non-existent attribute (but allowable for the object) to run
1145 LDAPControl PermitModify = {
1146 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1149 LDAPControl *controls[2];
1151 controls[0] = &PermitModify;
1154 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
1155 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1158 /* find the end of the list, marked by NULL or -1 */
1159 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1160 /* make sure the end of the list is NULL */
1162 ret = ldap_modify_ext_s(ads->ld, utf8_dn,
1163 (LDAPMod **) mods, controls, NULL);
1165 return ADS_ERROR(ret);
1169 * Perform an ldap add
1170 * @param ads connection to ads server
1171 * @param new_dn DistinguishedName to add
1172 * @param mods list of attributes and values for DN
1173 * @return status of add
1175 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1178 char *utf8_dn = NULL;
1180 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1181 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1182 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1185 /* find the end of the list, marked by NULL or -1 */
1186 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1187 /* make sure the end of the list is NULL */
1190 ret = ldap_add_s(ads->ld, utf8_dn, (LDAPMod**)mods);
1192 return ADS_ERROR(ret);
1196 * Delete a DistinguishedName
1197 * @param ads connection to ads server
1198 * @param new_dn DistinguishedName to delete
1199 * @return status of delete
1201 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1204 char *utf8_dn = NULL;
1205 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1206 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1207 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1210 ret = ldap_delete_s(ads->ld, utf8_dn);
1211 return ADS_ERROR(ret);
1215 * Build an org unit string
1216 * if org unit is Computers or blank then assume a container, otherwise
1217 * assume a / separated list of organisational units.
1218 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1219 * @param ads connection to ads server
1220 * @param org_unit Organizational unit
1221 * @return org unit string - caller must free
1223 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1227 if (!org_unit || !*org_unit) {
1229 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1231 /* samba4 might not yet respond to a wellknownobject-query */
1232 return ret ? ret : SMB_STRDUP("cn=Computers");
1235 if (strequal(org_unit, "Computers")) {
1236 return SMB_STRDUP("cn=Computers");
1239 /* jmcd: removed "\\" from the separation chars, because it is
1240 needed as an escape for chars like '#' which are valid in an
1242 return ads_build_path(org_unit, "/", "ou=", 1);
1246 * Get a org unit string for a well-known GUID
1247 * @param ads connection to ads server
1248 * @param wknguid Well known GUID
1249 * @return org unit string - caller must free
1251 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1255 char *base, *wkn_dn, *ret, **wkn_dn_exp, **bind_dn_exp;
1256 const char *attrs[] = {"distinguishedName", NULL};
1257 int new_ln, wkn_ln, bind_ln, i;
1259 if (wknguid == NULL) {
1263 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1264 DEBUG(1, ("asprintf failed!\n"));
1268 status = ads_search_dn(ads, &res, base, attrs);
1269 if (!ADS_ERR_OK(status)) {
1270 DEBUG(1,("Failed while searching for: %s\n", base));
1276 if (ads_count_replies(ads, res) != 1) {
1280 /* substitute the bind-path from the well-known-guid-search result */
1281 wkn_dn = ads_get_dn(ads, res);
1282 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1283 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1285 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1287 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1290 new_ln = wkn_ln - bind_ln;
1292 ret = wkn_dn_exp[0];
1294 for (i=1; i < new_ln; i++) {
1296 asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]);
1297 ret = SMB_STRDUP(s);
1301 ads_memfree(ads, wkn_dn);
1302 ldap_value_free(wkn_dn_exp);
1303 ldap_value_free(bind_dn_exp);
1309 * Adds (appends) an item to an attribute array, rather then
1310 * replacing the whole list
1311 * @param ctx An initialized TALLOC_CTX
1312 * @param mods An initialized ADS_MODLIST
1313 * @param name name of the ldap attribute to append to
1314 * @param vals an array of values to add
1315 * @return status of addition
1318 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1319 const char *name, const char **vals)
1321 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name, (const void **) vals);
1325 * Determines the computer account's current KVNO via an LDAP lookup
1326 * @param ads An initialized ADS_STRUCT
1327 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1328 * @return the kvno for the computer account, or -1 in case of a failure.
1331 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1333 LDAPMessage *res = NULL;
1334 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1336 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1337 char *dn_string = NULL;
1338 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1340 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1341 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1344 ret = ads_search(ads, (void**)(void *)&res, filter, attrs);
1346 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1347 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1348 ads_msgfree(ads, res);
1352 dn_string = ads_get_dn(ads, res);
1354 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1355 ads_msgfree(ads, res);
1358 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1359 ads_memfree(ads, dn_string);
1361 /* ---------------------------------------------------------
1362 * 0 is returned as a default KVNO from this point on...
1363 * This is done because Windows 2000 does not support key
1364 * version numbers. Chances are that a failure in the next
1365 * step is simply due to Windows 2000 being used for a
1366 * domain controller. */
1369 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1370 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1371 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1372 ads_msgfree(ads, res);
1377 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1378 ads_msgfree(ads, res);
1383 * This clears out all registered spn's for a given hostname
1384 * @param ads An initilaized ADS_STRUCT
1385 * @param machine_name the NetBIOS name of the computer.
1386 * @return 0 upon success, non-zero otherwise.
1389 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1392 LDAPMessage *res = NULL;
1394 const char *servicePrincipalName[1] = {NULL};
1395 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1396 char *dn_string = NULL;
1398 ret = ads_find_machine_acct(ads, (void **)(void *)&res, machine_name);
1399 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1400 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1401 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1402 ads_msgfree(ads, res);
1403 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1406 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1407 ctx = talloc_init("ads_clear_service_principal_names");
1409 ads_msgfree(ads, res);
1410 return ADS_ERROR(LDAP_NO_MEMORY);
1413 if (!(mods = ads_init_mods(ctx))) {
1414 talloc_destroy(ctx);
1415 ads_msgfree(ads, res);
1416 return ADS_ERROR(LDAP_NO_MEMORY);
1418 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1419 if (!ADS_ERR_OK(ret)) {
1420 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1421 ads_msgfree(ads, res);
1422 talloc_destroy(ctx);
1425 dn_string = ads_get_dn(ads, res);
1427 talloc_destroy(ctx);
1428 ads_msgfree(ads, res);
1429 return ADS_ERROR(LDAP_NO_MEMORY);
1431 ret = ads_gen_mod(ads, dn_string, mods);
1432 ads_memfree(ads,dn_string);
1433 if (!ADS_ERR_OK(ret)) {
1434 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1436 ads_msgfree(ads, res);
1437 talloc_destroy(ctx);
1441 ads_msgfree(ads, res);
1442 talloc_destroy(ctx);
1447 * This adds a service principal name to an existing computer account
1448 * (found by hostname) in AD.
1449 * @param ads An initialized ADS_STRUCT
1450 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1451 * @param my_fqdn The fully qualified DNS name of the machine
1452 * @param spn A string of the service principal to add, i.e. 'host'
1453 * @return 0 upon sucess, or non-zero if a failure occurs
1456 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1457 const char *my_fqdn, const char *spn)
1461 LDAPMessage *res = NULL;
1464 char *dn_string = NULL;
1465 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1467 ret = ads_find_machine_acct(ads, (void **)(void *)&res, machine_name);
1468 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1469 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1471 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1472 spn, machine_name, ads->config.realm));
1473 ads_msgfree(ads, res);
1474 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1477 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1478 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1479 ads_msgfree(ads, res);
1480 return ADS_ERROR(LDAP_NO_MEMORY);
1483 /* add short name spn */
1485 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1486 talloc_destroy(ctx);
1487 ads_msgfree(ads, res);
1488 return ADS_ERROR(LDAP_NO_MEMORY);
1491 strlower_m(&psp1[strlen(spn)]);
1492 servicePrincipalName[0] = psp1;
1494 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1495 psp1, machine_name));
1498 /* add fully qualified spn */
1500 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1501 ret = ADS_ERROR(LDAP_NO_MEMORY);
1505 strlower_m(&psp2[strlen(spn)]);
1506 servicePrincipalName[1] = psp2;
1508 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1509 psp2, machine_name));
1511 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1512 ret = ADS_ERROR(LDAP_NO_MEMORY);
1516 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1517 if (!ADS_ERR_OK(ret)) {
1518 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1522 if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1523 ret = ADS_ERROR(LDAP_NO_MEMORY);
1527 ret = ads_gen_mod(ads, dn_string, mods);
1528 ads_memfree(ads,dn_string);
1529 if (!ADS_ERR_OK(ret)) {
1530 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1536 ads_msgfree(ads, res);
1541 * adds a machine account to the ADS server
1542 * @param ads An intialized ADS_STRUCT
1543 * @param machine_name - the NetBIOS machine name of this account.
1544 * @param account_type A number indicating the type of account to create
1545 * @param org_unit The LDAP path in which to place this account
1546 * @return 0 upon success, or non-zero otherwise
1549 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1550 const char *org_unit)
1553 char *samAccountName, *controlstr;
1557 const char *objectClass[] = {"top", "person", "organizationalPerson",
1558 "user", "computer", NULL};
1559 LDAPMessage *res = NULL;
1560 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1561 UF_DONT_EXPIRE_PASSWD |\
1562 UF_ACCOUNTDISABLE );
1564 if (!(ctx = talloc_init("ads_add_machine_acct")))
1565 return ADS_ERROR(LDAP_NO_MEMORY);
1567 ret = ADS_ERROR(LDAP_NO_MEMORY);
1569 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_name, org_unit);
1570 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1572 if ( !new_dn || !samAccountName ) {
1576 #ifndef ENCTYPE_ARCFOUR_HMAC
1577 acct_control |= UF_USE_DES_KEY_ONLY;
1580 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1584 if (!(mods = ads_init_mods(ctx))) {
1588 ads_mod_str(ctx, &mods, "cn", machine_name);
1589 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1590 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1591 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1593 ret = ads_gen_add(ads, new_dn, mods);
1596 ads_msgfree(ads, res);
1597 talloc_destroy(ctx);
1603 dump a binary result from ldap
1605 static void dump_binary(const char *field, struct berval **values)
1608 for (i=0; values[i]; i++) {
1609 printf("%s: ", field);
1610 for (j=0; j<values[i]->bv_len; j++) {
1611 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1617 static void dump_guid(const char *field, struct berval **values)
1621 for (i=0; values[i]; i++) {
1622 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1623 printf("%s: %s\n", field,
1624 smb_uuid_string_static(smb_uuid_unpack_static(guid)));
1629 dump a sid result from ldap
1631 static void dump_sid(const char *field, struct berval **values)
1634 for (i=0; values[i]; i++) {
1636 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1637 printf("%s: %s\n", field, sid_string_static(&sid));
1642 dump ntSecurityDescriptor
1644 static void dump_sd(const char *filed, struct berval **values)
1649 TALLOC_CTX *ctx = 0;
1651 if (!(ctx = talloc_init("sec_io_desc")))
1655 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1656 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1657 prs_set_offset(&ps,0);
1660 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1662 talloc_destroy(ctx);
1665 if (psd) ads_disp_sd(psd);
1668 talloc_destroy(ctx);
1672 dump a string result from ldap
1674 static void dump_string(const char *field, char **values)
1677 for (i=0; values[i]; i++) {
1678 printf("%s: %s\n", field, values[i]);
1683 dump a field from LDAP on stdout
1687 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1692 void (*handler)(const char *, struct berval **);
1694 {"objectGUID", False, dump_guid},
1695 {"netbootGUID", False, dump_guid},
1696 {"nTSecurityDescriptor", False, dump_sd},
1697 {"dnsRecord", False, dump_binary},
1698 {"objectSid", False, dump_sid},
1699 {"tokenGroups", False, dump_sid},
1700 {"tokenGroupsNoGCAcceptable", False, dump_sid},
1701 {"tokengroupsGlobalandUniversal", False, dump_sid},
1706 if (!field) { /* must be end of an entry */
1711 for (i=0; handlers[i].name; i++) {
1712 if (StrCaseCmp(handlers[i].name, field) == 0) {
1713 if (!values) /* first time, indicate string or not */
1714 return handlers[i].string;
1715 handlers[i].handler(field, (struct berval **) values);
1719 if (!handlers[i].name) {
1720 if (!values) /* first time, indicate string conversion */
1722 dump_string(field, (char **)values);
1728 * Dump a result from LDAP on stdout
1729 * used for debugging
1730 * @param ads connection to ads server
1731 * @param res Results to dump
1734 void ads_dump(ADS_STRUCT *ads, void *res)
1736 ads_process_results(ads, res, ads_dump_field, NULL);
1740 * Walk through results, calling a function for each entry found.
1741 * The function receives a field name, a berval * array of values,
1742 * and a data area passed through from the start. The function is
1743 * called once with null for field and values at the end of each
1745 * @param ads connection to ads server
1746 * @param res Results to process
1747 * @param fn Function for processing each result
1748 * @param data_area user-defined area to pass to function
1750 void ads_process_results(ADS_STRUCT *ads, void *res,
1751 BOOL(*fn)(char *, void **, void *),
1757 if (!(ctx = talloc_init("ads_process_results")))
1760 for (msg = ads_first_entry(ads, res); msg;
1761 msg = ads_next_entry(ads, msg)) {
1765 for (utf8_field=ldap_first_attribute(ads->ld,
1766 (LDAPMessage *)msg,&b);
1768 utf8_field=ldap_next_attribute(ads->ld,
1769 (LDAPMessage *)msg,b)) {
1770 struct berval **ber_vals;
1771 char **str_vals, **utf8_vals;
1775 pull_utf8_talloc(ctx, &field, utf8_field);
1776 string = fn(field, NULL, data_area);
1779 utf8_vals = ldap_get_values(ads->ld,
1780 (LDAPMessage *)msg, field);
1781 str_vals = ads_pull_strvals(ctx,
1782 (const char **) utf8_vals);
1783 fn(field, (void **) str_vals, data_area);
1784 ldap_value_free(utf8_vals);
1786 ber_vals = ldap_get_values_len(ads->ld,
1787 (LDAPMessage *)msg, field);
1788 fn(field, (void **) ber_vals, data_area);
1790 ldap_value_free_len(ber_vals);
1792 ldap_memfree(utf8_field);
1795 talloc_free_children(ctx);
1796 fn(NULL, NULL, data_area); /* completed an entry */
1799 talloc_destroy(ctx);
1803 * count how many replies are in a LDAPMessage
1804 * @param ads connection to ads server
1805 * @param res Results to count
1806 * @return number of replies
1808 int ads_count_replies(ADS_STRUCT *ads, void *res)
1810 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1814 * pull the first entry from a ADS result
1815 * @param ads connection to ads server
1816 * @param res Results of search
1817 * @return first entry from result
1819 void *ads_first_entry(ADS_STRUCT *ads, void *res)
1821 return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
1825 * pull the next entry from a ADS result
1826 * @param ads connection to ads server
1827 * @param res Results of search
1828 * @return next entry from result
1830 void *ads_next_entry(ADS_STRUCT *ads, void *res)
1832 return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
1836 * pull a single string from a ADS result
1837 * @param ads connection to ads server
1838 * @param mem_ctx TALLOC_CTX to use for allocating result string
1839 * @param msg Results of search
1840 * @param field Attribute to retrieve
1841 * @return Result string in talloc context
1843 char *ads_pull_string(ADS_STRUCT *ads,
1844 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1851 values = ldap_get_values(ads->ld, msg, field);
1856 rc = pull_utf8_talloc(mem_ctx, &ux_string,
1858 if (rc != (size_t)-1)
1862 ldap_value_free(values);
1867 * pull an array of strings from a ADS result
1868 * @param ads connection to ads server
1869 * @param mem_ctx TALLOC_CTX to use for allocating result string
1870 * @param msg Results of search
1871 * @param field Attribute to retrieve
1872 * @return Result strings in talloc context
1874 char **ads_pull_strings(ADS_STRUCT *ads,
1875 TALLOC_CTX *mem_ctx, void *msg, const char *field,
1882 values = ldap_get_values(ads->ld, msg, field);
1886 *num_values = ldap_count_values(values);
1888 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
1890 ldap_value_free(values);
1894 for (i=0;i<*num_values;i++) {
1895 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
1896 ldap_value_free(values);
1902 ldap_value_free(values);
1907 * pull an array of strings from a ADS result
1908 * (handle large multivalue attributes with range retrieval)
1909 * @param ads connection to ads server
1910 * @param mem_ctx TALLOC_CTX to use for allocating result string
1911 * @param msg Results of search
1912 * @param field Attribute to retrieve
1913 * @param current_strings strings returned by a previous call to this function
1914 * @param next_attribute The next query should ask for this attribute
1915 * @param num_values How many values did we get this time?
1916 * @param more_values Are there more values to get?
1917 * @return Result strings in talloc context
1919 char **ads_pull_strings_range(ADS_STRUCT *ads,
1920 TALLOC_CTX *mem_ctx,
1921 void *msg, const char *field,
1922 char **current_strings,
1923 const char **next_attribute,
1924 size_t *num_strings,
1928 char *expected_range_attrib, *range_attr;
1929 BerElement *ptr = NULL;
1932 size_t num_new_strings;
1933 unsigned long int range_start;
1934 unsigned long int range_end;
1936 /* we might have been given the whole lot anyway */
1937 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
1938 *more_strings = False;
1942 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
1944 /* look for Range result */
1945 for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr);
1947 attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
1948 /* we ignore the fact that this is utf8, as all attributes are ascii... */
1949 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
1957 /* nothing here - this field is just empty */
1958 *more_strings = False;
1962 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
1963 &range_start, &range_end) == 2) {
1964 *more_strings = True;
1966 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
1967 &range_start) == 1) {
1968 *more_strings = False;
1970 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
1972 ldap_memfree(range_attr);
1973 *more_strings = False;
1978 if ((*num_strings) != range_start) {
1979 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
1980 " - aborting range retreival\n",
1981 range_attr, (unsigned int)(*num_strings) + 1, range_start));
1982 ldap_memfree(range_attr);
1983 *more_strings = False;
1987 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
1989 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
1990 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
1991 "strings in this bunch, but we only got %lu - aborting range retreival\n",
1992 range_attr, (unsigned long int)range_end - range_start + 1,
1993 (unsigned long int)num_new_strings));
1994 ldap_memfree(range_attr);
1995 *more_strings = False;
1999 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2000 *num_strings + num_new_strings);
2002 if (strings == NULL) {
2003 ldap_memfree(range_attr);
2004 *more_strings = False;
2008 if (new_strings && num_new_strings) {
2009 memcpy(&strings[*num_strings], new_strings,
2010 sizeof(*new_strings) * num_new_strings);
2013 (*num_strings) += num_new_strings;
2015 if (*more_strings) {
2016 *next_attribute = talloc_asprintf(mem_ctx,
2021 if (!*next_attribute) {
2022 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2023 ldap_memfree(range_attr);
2024 *more_strings = False;
2029 ldap_memfree(range_attr);
2035 * pull a single uint32 from a ADS result
2036 * @param ads connection to ads server
2037 * @param msg Results of search
2038 * @param field Attribute to retrieve
2039 * @param v Pointer to int to store result
2040 * @return boolean inidicating success
2042 BOOL ads_pull_uint32(ADS_STRUCT *ads,
2043 void *msg, const char *field, uint32 *v)
2047 values = ldap_get_values(ads->ld, msg, field);
2051 ldap_value_free(values);
2055 *v = atoi(values[0]);
2056 ldap_value_free(values);
2061 * pull a single objectGUID from an ADS result
2062 * @param ads connection to ADS server
2063 * @param msg results of search
2064 * @param guid 37-byte area to receive text guid
2065 * @return boolean indicating success
2067 BOOL ads_pull_guid(ADS_STRUCT *ads,
2068 void *msg, struct uuid *guid)
2071 UUID_FLAT flat_guid;
2073 values = ldap_get_values(ads->ld, msg, "objectGUID");
2078 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2079 smb_uuid_unpack(flat_guid, guid);
2080 ldap_value_free(values);
2083 ldap_value_free(values);
2090 * pull a single DOM_SID from a ADS result
2091 * @param ads connection to ads server
2092 * @param msg Results of search
2093 * @param field Attribute to retrieve
2094 * @param sid Pointer to sid to store result
2095 * @return boolean inidicating success
2097 BOOL ads_pull_sid(ADS_STRUCT *ads,
2098 void *msg, const char *field, DOM_SID *sid)
2100 struct berval **values;
2103 values = ldap_get_values_len(ads->ld, msg, field);
2109 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2111 ldap_value_free_len(values);
2116 * pull an array of DOM_SIDs from a ADS result
2117 * @param ads connection to ads server
2118 * @param mem_ctx TALLOC_CTX for allocating sid array
2119 * @param msg Results of search
2120 * @param field Attribute to retrieve
2121 * @param sids pointer to sid array to allocate
2122 * @return the count of SIDs pulled
2124 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2125 void *msg, const char *field, DOM_SID **sids)
2127 struct berval **values;
2131 values = ldap_get_values_len(ads->ld, msg, field);
2136 for (i=0; values[i]; i++)
2139 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2141 ldap_value_free_len(values);
2146 for (i=0; values[i]; i++) {
2147 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2150 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2155 ldap_value_free_len(values);
2160 * pull a SEC_DESC from a ADS result
2161 * @param ads connection to ads server
2162 * @param mem_ctx TALLOC_CTX for allocating sid array
2163 * @param msg Results of search
2164 * @param field Attribute to retrieve
2165 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2166 * @return boolean inidicating success
2168 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2169 void *msg, const char *field, SEC_DESC **sd)
2171 struct berval **values;
2175 values = ldap_get_values_len(ads->ld, msg, field);
2177 if (!values) return False;
2180 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2181 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2182 prs_set_offset(&ps,0);
2184 ret = sec_io_desc("sd", sd, &ps, 1);
2187 ldap_value_free_len(values);
2192 * in order to support usernames longer than 21 characters we need to
2193 * use both the sAMAccountName and the userPrincipalName attributes
2194 * It seems that not all users have the userPrincipalName attribute set
2196 * @param ads connection to ads server
2197 * @param mem_ctx TALLOC_CTX for allocating sid array
2198 * @param msg Results of search
2199 * @return the username
2201 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
2206 /* lookup_name() only works on the sAMAccountName to
2207 returning the username portion of userPrincipalName
2208 breaks winbindd_getpwnam() */
2210 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2211 if (ret && (p = strchr_m(ret, '@'))) {
2216 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2221 * find the update serial number - this is the core of the ldap cache
2222 * @param ads connection to ads server
2223 * @param ads connection to ADS server
2224 * @param usn Pointer to retrieved update serial number
2225 * @return status of search
2227 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2229 const char *attrs[] = {"highestCommittedUSN", NULL};
2233 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2234 if (!ADS_ERR_OK(status))
2237 if (ads_count_replies(ads, res) != 1) {
2238 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2241 ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
2242 ads_msgfree(ads, res);
2246 /* parse a ADS timestring - typical string is
2247 '20020917091222.0Z0' which means 09:12.22 17th September
2249 static time_t ads_parse_time(const char *str)
2255 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2256 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2257 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2266 /********************************************************************
2267 ********************************************************************/
2269 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2271 const char *attrs[] = {"currentTime", NULL};
2276 ADS_STRUCT *ads_s = ads;
2278 if (!(ctx = talloc_init("ads_current_time"))) {
2279 return ADS_ERROR(LDAP_NO_MEMORY);
2282 /* establish a new ldap tcp session if necessary */
2285 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2286 ads->server.ldap_server )) == NULL )
2290 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2291 status = ads_connect( ads_s );
2292 if ( !ADS_ERR_OK(status))
2296 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2297 if (!ADS_ERR_OK(status)) {
2301 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2303 ads_msgfree(ads, res);
2304 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2308 /* but save the time and offset in the original ADS_STRUCT */
2310 ads->config.current_time = ads_parse_time(timestr);
2312 if (ads->config.current_time != 0) {
2313 ads->auth.time_offset = ads->config.current_time - time(NULL);
2314 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2317 ads_msgfree(ads, res);
2319 status = ADS_SUCCESS;
2322 /* free any temporary ads connections */
2323 if ( ads_s != ads ) {
2324 ads_destroy( &ads_s );
2326 talloc_destroy(ctx);
2331 /********************************************************************
2332 ********************************************************************/
2334 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2336 const char *attrs[] = {"domainFunctionality", NULL};
2339 ADS_STRUCT *ads_s = ads;
2341 *val = DS_DOMAIN_FUNCTION_2000;
2343 /* establish a new ldap tcp session if necessary */
2346 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2347 ads->server.ldap_server )) == NULL )
2351 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2352 status = ads_connect( ads_s );
2353 if ( !ADS_ERR_OK(status))
2357 /* If the attribute does not exist assume it is a Windows 2000
2358 functional domain */
2360 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2361 if (!ADS_ERR_OK(status)) {
2362 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2363 status = ADS_SUCCESS;
2368 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2369 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2371 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2374 ads_msgfree(ads, res);
2377 /* free any temporary ads connections */
2378 if ( ads_s != ads ) {
2379 ads_destroy( &ads_s );
2386 * find the domain sid for our domain
2387 * @param ads connection to ads server
2388 * @param sid Pointer to domain sid
2389 * @return status of search
2391 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2393 const char *attrs[] = {"objectSid", NULL};
2397 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2399 if (!ADS_ERR_OK(rc)) return rc;
2400 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2401 ads_msgfree(ads, res);
2402 return ADS_ERROR_SYSTEM(ENOENT);
2404 ads_msgfree(ads, res);
2410 * find our site name
2411 * @param ads connection to ads server
2412 * @param mem_ctx Pointer to talloc context
2413 * @param site_name Pointer to the sitename
2414 * @return status of search
2416 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2420 const char *dn, *service_name;
2421 const char *attrs[] = { "dsServiceName", NULL };
2423 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2424 if (!ADS_ERR_OK(status)) {
2428 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2429 if (service_name == NULL) {
2430 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2433 /* go up three levels */
2434 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2436 return ADS_ERROR(LDAP_NO_MEMORY);
2439 *site_name = talloc_strdup(mem_ctx, dn);
2440 if (*site_name == NULL) {
2441 return ADS_ERROR(LDAP_NO_MEMORY);
2444 ads_msgfree(ads, res);
2448 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2453 * find the site dn where a machine resides
2454 * @param ads connection to ads server
2455 * @param mem_ctx Pointer to talloc context
2456 * @param computer_name name of the machine
2457 * @param site_name Pointer to the sitename
2458 * @return status of search
2460 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2464 const char *parent, *config_context, *filter;
2465 const char *attrs[] = { "configurationNamingContext", NULL };
2468 /* shortcut a query */
2469 if (strequal(computer_name, ads->config.ldap_server_name)) {
2470 return ads_site_dn(ads, mem_ctx, site_dn);
2473 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2474 if (!ADS_ERR_OK(status)) {
2478 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2479 if (config_context == NULL) {
2480 return ADS_ERROR(LDAP_NO_MEMORY);
2483 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2484 if (filter == NULL) {
2485 return ADS_ERROR(LDAP_NO_MEMORY);
2488 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, filter, NULL, &res);
2489 if (!ADS_ERR_OK(status)) {
2493 if (ads_count_replies(ads, res) != 1) {
2494 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2497 dn = ads_get_dn(ads, res);
2499 return ADS_ERROR(LDAP_NO_MEMORY);
2502 /* go up three levels */
2503 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2504 if (parent == NULL) {
2505 ads_memfree(ads, dn);
2506 return ADS_ERROR(LDAP_NO_MEMORY);
2509 *site_dn = talloc_strdup(mem_ctx, parent);
2510 if (*site_dn == NULL) {
2511 ads_memfree(ads, dn);
2512 ADS_ERROR(LDAP_NO_MEMORY);
2515 ads_memfree(ads, dn);
2516 ads_msgfree(ads, res);
2522 * get the upn suffixes for a domain
2523 * @param ads connection to ads server
2524 * @param mem_ctx Pointer to talloc context
2525 * @param suffixes Pointer to an array of suffixes
2526 * @param site_name Pointer to the number of suffixes
2527 * @return status of search
2529 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char **suffixes, size_t *num_suffixes)
2533 const char *config_context, *base;
2534 const char *attrs[] = { "configurationNamingContext", NULL };
2535 const char *attrs2[] = { "uPNSuffixes", NULL };
2537 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2538 if (!ADS_ERR_OK(status)) {
2542 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2543 if (config_context == NULL) {
2544 return ADS_ERROR(LDAP_NO_MEMORY);
2547 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2549 return ADS_ERROR(LDAP_NO_MEMORY);
2552 status = ads_search_dn(ads, &res, base, attrs2);
2553 if (!ADS_ERR_OK(status)) {
2557 if (ads_count_replies(ads, res) != 1) {
2558 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2561 suffixes = ads_pull_strings(ads, mem_ctx, &res, "uPNSuffixes", num_suffixes);
2562 if (suffixes == NULL) {
2563 ads_msgfree(ads, res);
2564 return ADS_ERROR(LDAP_NO_MEMORY);
2567 ads_msgfree(ads, res);
2573 * pull a DOM_SID from an extended dn string
2574 * @param mem_ctx TALLOC_CTX
2575 * @param flags string type of extended_dn
2576 * @param sid pointer to a DOM_SID
2577 * @return boolean inidicating success
2579 BOOL ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
2581 enum ads_extended_dn_flags flags,
2591 * ADS_EXTENDED_DN_HEX_STRING:
2592 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2594 * ADS_EXTENDED_DN_STRING (only with w2k3):
2595 <GUID=63198e23-39cb-4b0f-b032-ba0105525a29>;<SID=S-1-5-21-4257769659-666132843-1169174103-1110>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2598 p = strchr(dn, ';');
2603 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
2607 p += strlen(";<SID=");
2616 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
2620 case ADS_EXTENDED_DN_STRING:
2621 if (!string_to_sid(sid, p)) {
2625 case ADS_EXTENDED_DN_HEX_STRING: {
2629 buf_len = strhex_to_str(buf, strlen(p), p);
2634 if (!sid_parse(buf, buf_len, sid)) {
2635 DEBUG(10,("failed to parse sid\n"));
2641 DEBUG(10,("unknown extended dn format\n"));
2649 * pull an array of DOM_SIDs from a ADS result
2650 * @param ads connection to ads server
2651 * @param mem_ctx TALLOC_CTX for allocating sid array
2652 * @param msg Results of search
2653 * @param field Attribute to retrieve
2654 * @param flags string type of extended_dn
2655 * @param sids pointer to sid array to allocate
2656 * @return the count of SIDs pulled
2658 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
2659 TALLOC_CTX *mem_ctx,
2662 enum ads_extended_dn_flags flags,
2669 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
2670 &dn_count)) == NULL) {
2674 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
2676 TALLOC_FREE(dn_strings);
2680 for (i=0; i<dn_count; i++) {
2682 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
2683 flags, &(*sids)[i])) {
2685 TALLOC_FREE(dn_strings);
2690 TALLOC_FREE(dn_strings);
2695 /********************************************************************
2696 ********************************************************************/
2698 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2700 LDAPMessage *res = NULL;
2705 status = ads_find_machine_acct(ads, (void **)(void *)&res, global_myname());
2706 if (!ADS_ERR_OK(status)) {
2707 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2712 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2713 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2717 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
2718 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
2722 ads_msgfree(ads, res);
2727 /********************************************************************
2728 ********************************************************************/
2730 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2732 LDAPMessage *res = NULL;
2737 status = ads_find_machine_acct(ads, (void **)(void *)&res, global_myname());
2738 if (!ADS_ERR_OK(status)) {
2739 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
2744 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2745 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
2749 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
2750 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
2754 ads_msgfree(ads, res);
2759 /********************************************************************
2760 ********************************************************************/
2762 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2764 LDAPMessage *res = NULL;
2769 status = ads_find_machine_acct(ads, (void **)(void *)&res, global_myname());
2770 if (!ADS_ERR_OK(status)) {
2771 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2776 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2777 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2781 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
2782 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
2786 ads_msgfree(ads, res);