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 try a connection to a given ldap server, returning True and setting the servers IP
120 in the ads struct if successful
122 BOOL ads_try_connect(ADS_STRUCT *ads, const char *server )
125 struct cldap_netlogon_reply cldap_reply;
127 if (!server || !*server) {
131 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
132 server, ads->server.realm));
134 /* this copes with inet_ntoa brokenness */
136 srv = SMB_STRDUP(server);
138 ZERO_STRUCT( cldap_reply );
140 if ( !ads_cldap_netlogon( srv, ads->server.realm, &cldap_reply ) ) {
141 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
146 /* Check the CLDAP reply flags */
148 if ( !(cldap_reply.flags & ADS_LDAP) ) {
149 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
155 /* Fill in the ads->config values */
157 SAFE_FREE(ads->config.realm);
158 SAFE_FREE(ads->config.bind_path);
159 SAFE_FREE(ads->config.ldap_server_name);
160 SAFE_FREE(ads->server.workgroup);
162 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.hostname);
163 strupper_m(cldap_reply.domain);
164 ads->config.realm = SMB_STRDUP(cldap_reply.domain);
165 ads->config.bind_path = ads_build_dn(ads->config.realm);
166 ads->server.workgroup = SMB_STRDUP(cldap_reply.netbios_domain);
168 ads->ldap_port = LDAP_PORT;
169 ads->ldap_ip = *interpret_addr2(srv);
172 /* Store our site name. */
173 sitename_store( cldap_reply.client_site_name );
178 /**********************************************************************
179 Try to find an AD dc using our internal name resolution routines
180 Try the realm first and then then workgroup name if netbios is not
182 **********************************************************************/
184 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
188 struct ip_service *ip_list;
190 BOOL got_realm = False;
191 BOOL use_own_domain = False;
192 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
194 /* if the realm and workgroup are both empty, assume they are ours */
197 c_realm = ads->server.realm;
199 if ( !c_realm || !*c_realm ) {
200 /* special case where no realm and no workgroup means our own */
201 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
202 use_own_domain = True;
203 c_realm = lp_realm();
207 if (c_realm && *c_realm)
211 /* we need to try once with the realm name and fallback to the
212 netbios domain name if we fail (if netbios has not been disabled */
214 if ( !got_realm && !lp_disable_netbios() ) {
215 c_realm = ads->server.workgroup;
216 if (!c_realm || !*c_realm) {
217 if ( use_own_domain )
218 c_realm = lp_workgroup();
221 if ( !c_realm || !*c_realm ) {
222 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
223 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
227 pstrcpy( realm, c_realm );
229 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
230 (got_realm ? "realm" : "domain"), realm));
232 status = get_sorted_dc_list(realm, &ip_list, &count, got_realm);
233 if (!NT_STATUS_IS_OK(status)) {
234 /* fall back to netbios if we can */
235 if ( got_realm && !lp_disable_netbios() ) {
243 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
244 for ( i=0; i<count; i++ ) {
247 fstrcpy( server, inet_ntoa(ip_list[i].ip) );
249 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
252 if ( ads_try_connect(ads, server) ) {
257 /* keep track of failures */
258 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
263 return NT_STATUS_NO_LOGON_SERVERS;
268 * Connect to the LDAP server
269 * @param ads Pointer to an existing ADS_STRUCT
270 * @return status of connection
272 ADS_STATUS ads_connect(ADS_STRUCT *ads)
274 int version = LDAP_VERSION3;
278 ads->last_attempt = time(NULL);
281 /* try with a user specified server */
283 if (ads->server.ldap_server &&
284 ads_try_connect(ads, ads->server.ldap_server)) {
288 ntstatus = ads_find_dc(ads);
289 if (NT_STATUS_IS_OK(ntstatus)) {
293 return ADS_ERROR_NT(ntstatus);
296 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
298 if (!ads->auth.user_name) {
299 /* Must use the userPrincipalName value here or sAMAccountName
300 and not servicePrincipalName; found by Guenther Deschner */
302 asprintf(&ads->auth.user_name, "%s$", global_myname() );
305 if (!ads->auth.realm) {
306 ads->auth.realm = SMB_STRDUP(ads->config.realm);
309 if (!ads->auth.kdc_server) {
310 ads->auth.kdc_server = SMB_STRDUP(inet_ntoa(ads->ldap_ip));
314 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
315 to MIT kerberos to work (tridge) */
318 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
319 setenv(env, ads->auth.kdc_server, 1);
324 /* If the caller() requested no LDAP bind, then we are done */
326 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
330 /* Otherwise setup the TCP LDAP session */
332 if ( (ads->ld = ldap_open_with_timeout(ads->config.ldap_server_name,
333 LDAP_PORT, lp_ldap_timeout())) == NULL )
335 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
338 /* cache the successful connection */
339 saf_store( ads->server.workgroup, inet_ntoa(ads->ldap_ip));
341 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
343 status = ADS_ERROR(smb_ldap_start_tls(ads->ld, version));
344 if (!ADS_ERR_OK(status)) {
348 /* fill in the current time and offsets */
350 status = ads_current_time( ads );
351 if ( !ADS_ERR_OK(status) ) {
355 /* Now do the bind */
357 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
358 return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
361 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
362 return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
365 return ads_sasl_bind(ads);
369 Duplicate a struct berval into talloc'ed memory
371 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
373 struct berval *value;
375 if (!in_val) return NULL;
377 value = TALLOC_ZERO_P(ctx, struct berval);
380 if (in_val->bv_len == 0) return value;
382 value->bv_len = in_val->bv_len;
383 value->bv_val = TALLOC_MEMDUP(ctx, in_val->bv_val, in_val->bv_len);
388 Make a values list out of an array of (struct berval *)
390 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
391 const struct berval **in_vals)
393 struct berval **values;
396 if (!in_vals) return NULL;
397 for (i=0; in_vals[i]; i++)
399 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
400 if (!values) return NULL;
402 for (i=0; in_vals[i]; i++) {
403 values[i] = dup_berval(ctx, in_vals[i]);
409 UTF8-encode a values list out of an array of (char *)
411 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
416 if (!in_vals) return NULL;
417 for (i=0; in_vals[i]; i++)
419 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
420 if (!values) return NULL;
422 for (i=0; in_vals[i]; i++) {
423 push_utf8_talloc(ctx, &values[i], in_vals[i]);
429 Pull a (char *) array out of a UTF8-encoded values list
431 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
436 if (!in_vals) return NULL;
437 for (i=0; in_vals[i]; i++)
439 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
440 if (!values) return NULL;
442 for (i=0; in_vals[i]; i++) {
443 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
449 * Do a search with paged results. cookie must be null on the first
450 * call, and then returned on each subsequent call. It will be null
451 * again when the entire search is complete
452 * @param ads connection to ads server
453 * @param bind_path Base dn for the search
454 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
455 * @param expr Search expression - specified in local charset
456 * @param attrs Attributes to retrieve - specified in utf8 or ascii
457 * @param res ** which will contain results - free res* with ads_msgfree()
458 * @param count Number of entries retrieved on this page
459 * @param cookie The paged results cookie to be returned on subsequent calls
460 * @return status of search
462 ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads, const char *bind_path,
463 int scope, const char *expr,
464 const char **attrs, void *args, void **res,
465 int *count, void **cookie)
468 char *utf8_expr, *utf8_path, **search_attrs;
469 LDAPControl PagedResults, NoReferrals, ExtendedDn, *controls[4], **rcontrols;
470 BerElement *cookie_be = NULL;
471 struct berval *cookie_bv= NULL;
472 BerElement *extdn_be = NULL;
473 struct berval *extdn_bv= NULL;
476 ads_control *external_control = (ads_control *) args;
480 if (!(ctx = talloc_init("ads_do_paged_search_args")))
481 return ADS_ERROR(LDAP_NO_MEMORY);
483 /* 0 means the conversion worked but the result was empty
484 so we only fail if it's -1. In any case, it always
485 at least nulls out the dest */
486 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
487 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
492 if (!attrs || !(*attrs))
495 /* This would be the utf8-encoded version...*/
496 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
497 if (!(str_list_copy(&search_attrs, attrs))) {
504 /* Paged results only available on ldap v3 or later */
505 ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
506 if (version < LDAP_VERSION3) {
507 rc = LDAP_NOT_SUPPORTED;
511 cookie_be = ber_alloc_t(LBER_USE_DER);
513 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
514 ber_bvfree(*cookie); /* don't need it from last time */
517 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
519 ber_flatten(cookie_be, &cookie_bv);
520 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
521 PagedResults.ldctl_iscritical = (char) 1;
522 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
523 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
525 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
526 NoReferrals.ldctl_iscritical = (char) 0;
527 NoReferrals.ldctl_value.bv_len = 0;
528 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
530 if (external_control && strequal(external_control->control, ADS_EXTENDED_DN_OID)) {
532 ExtendedDn.ldctl_oid = CONST_DISCARD(char *, external_control->control);
533 ExtendedDn.ldctl_iscritical = (char) external_control->critical;
535 /* win2k does not accept a ldctl_value beeing passed in */
537 if (external_control->val != 0) {
539 if ((extdn_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
544 if ((ber_printf(extdn_be, "{i}", (ber_int_t) external_control->val)) == -1) {
548 if ((ber_flatten(extdn_be, &extdn_bv)) == -1) {
553 ExtendedDn.ldctl_value.bv_len = extdn_bv->bv_len;
554 ExtendedDn.ldctl_value.bv_val = extdn_bv->bv_val;
557 ExtendedDn.ldctl_value.bv_len = 0;
558 ExtendedDn.ldctl_value.bv_val = NULL;
561 controls[0] = &NoReferrals;
562 controls[1] = &PagedResults;
563 controls[2] = &ExtendedDn;
567 controls[0] = &NoReferrals;
568 controls[1] = &PagedResults;
572 /* we need to disable referrals as the openldap libs don't
573 handle them and paged results at the same time. Using them
574 together results in the result record containing the server
575 page control being removed from the result list (tridge/jmcd)
577 leaving this in despite the control that says don't generate
578 referrals, in case the server doesn't support it (jmcd)
580 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
582 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
583 search_attrs, 0, controls,
585 (LDAPMessage **)res);
587 ber_free(cookie_be, 1);
588 ber_bvfree(cookie_bv);
591 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
592 ldap_err2string(rc)));
596 rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
597 NULL, &rcontrols, 0);
603 for (i=0; rcontrols[i]; i++) {
604 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
605 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
606 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
608 /* the berval is the cookie, but must be freed when
610 if (cookie_bv->bv_len) /* still more to do */
611 *cookie=ber_bvdup(cookie_bv);
614 ber_bvfree(cookie_bv);
615 ber_free(cookie_be, 1);
619 ldap_controls_free(rcontrols);
625 ber_free(extdn_be, 1);
629 ber_bvfree(extdn_bv);
632 /* if/when we decide to utf8-encode attrs, take out this next line */
633 str_list_free(&search_attrs);
635 return ADS_ERROR(rc);
638 ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
639 int scope, const char *expr,
640 const char **attrs, void **res,
641 int *count, void **cookie)
643 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
648 * Get all results for a search. This uses ads_do_paged_search() to return
649 * all entries in a large search.
650 * @param ads connection to ads server
651 * @param bind_path Base dn for the search
652 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
653 * @param expr Search expression
654 * @param attrs Attributes to retrieve
655 * @param res ** which will contain results - free res* with ads_msgfree()
656 * @return status of search
658 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
659 int scope, const char *expr,
660 const char **attrs, void *args, void **res)
667 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
670 if (!ADS_ERR_OK(status))
673 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
677 LDAPMessage *msg, *next;
679 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
680 attrs, args, &res2, &count, &cookie);
682 if (!ADS_ERR_OK(status2)) break;
684 /* this relies on the way that ldap_add_result_entry() works internally. I hope
685 that this works on all ldap libs, but I have only tested with openldap */
686 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
687 next = ads_next_entry(ads, msg);
688 ldap_add_result_entry((LDAPMessage **)res, msg);
690 /* note that we do not free res2, as the memory is now
691 part of the main returned list */
694 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
695 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
701 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
702 int scope, const char *expr,
703 const char **attrs, void **res)
705 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
709 * Run a function on all results for a search. Uses ads_do_paged_search() and
710 * runs the function as each page is returned, using ads_process_results()
711 * @param ads connection to ads server
712 * @param bind_path Base dn for the search
713 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
714 * @param expr Search expression - specified in local charset
715 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
716 * @param fn Function which takes attr name, values list, and data_area
717 * @param data_area Pointer which is passed to function on each call
718 * @return status of search
720 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
721 int scope, const char *expr, const char **attrs,
722 BOOL(*fn)(char *, void **, void *),
730 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
733 if (!ADS_ERR_OK(status)) return status;
735 ads_process_results(ads, res, fn, data_area);
736 ads_msgfree(ads, res);
739 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
740 &res, &count, &cookie);
742 if (!ADS_ERR_OK(status)) break;
744 ads_process_results(ads, res, fn, data_area);
745 ads_msgfree(ads, res);
752 * Do a search with a timeout.
753 * @param ads connection to ads server
754 * @param bind_path Base dn for the search
755 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
756 * @param expr Search expression
757 * @param attrs Attributes to retrieve
758 * @param res ** which will contain results - free res* with ads_msgfree()
759 * @return status of search
761 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
763 const char **attrs, void **res)
766 char *utf8_expr, *utf8_path, **search_attrs = NULL;
770 if (!(ctx = talloc_init("ads_do_search"))) {
771 DEBUG(1,("ads_do_search: talloc_init() failed!"));
772 return ADS_ERROR(LDAP_NO_MEMORY);
775 /* 0 means the conversion worked but the result was empty
776 so we only fail if it's negative. In any case, it always
777 at least nulls out the dest */
778 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
779 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
780 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
785 if (!attrs || !(*attrs))
788 /* This would be the utf8-encoded version...*/
789 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
790 if (!(str_list_copy(&search_attrs, attrs)))
792 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
798 /* see the note in ads_do_paged_search - we *must* disable referrals */
799 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
801 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
802 search_attrs, 0, NULL, NULL,
804 (LDAPMessage **)res);
806 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
807 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
813 /* if/when we decide to utf8-encode attrs, take out this next line */
814 str_list_free(&search_attrs);
815 return ADS_ERROR(rc);
818 * Do a general ADS search
819 * @param ads connection to ads server
820 * @param res ** which will contain results - free res* with ads_msgfree()
821 * @param expr Search expression
822 * @param attrs Attributes to retrieve
823 * @return status of search
825 ADS_STATUS ads_search(ADS_STRUCT *ads, void **res,
829 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
834 * Do a search on a specific DistinguishedName
835 * @param ads connection to ads server
836 * @param res ** which will contain results - free res* with ads_msgfree()
837 * @param dn DistinguishName to search
838 * @param attrs Attributes to retrieve
839 * @return status of search
841 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, void *_res,
845 void **res = (void **)_res;
846 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res);
850 * Free up memory from a ads_search
851 * @param ads connection to ads server
852 * @param msg Search results to free
854 void ads_msgfree(ADS_STRUCT *ads, void *msg)
861 * Free up memory from various ads requests
862 * @param ads connection to ads server
863 * @param mem Area to free
865 void ads_memfree(ADS_STRUCT *ads, void *mem)
871 * Get a dn from search results
872 * @param ads connection to ads server
873 * @param msg Search result
876 char *ads_get_dn(ADS_STRUCT *ads, void *msg)
878 char *utf8_dn, *unix_dn;
880 utf8_dn = ldap_get_dn(ads->ld, msg);
883 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
887 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
888 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
892 ldap_memfree(utf8_dn);
897 * Get a canonical dn from search results
898 * @param ads connection to ads server
899 * @param msg Search result
902 char *ads_get_dn_canonical(ADS_STRUCT *ads, void *msg)
904 #ifdef HAVE_LDAP_DN2AD_CANONICAL
905 return ldap_dn2ad_canonical(ads_get_dn(ads, msg));
912 * Get the parent from a dn
913 * @param dn the dn to return the parent from
914 * @return parent dn string
916 char *ads_parent_dn(const char *dn)
934 * Find a machine account given a hostname
935 * @param ads connection to ads server
936 * @param res ** which will contain results - free res* with ads_msgfree()
937 * @param host Hostname to search for
938 * @return status of search
940 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *machine)
944 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
948 /* the easiest way to find a machine account anywhere in the tree
949 is to look for hostname$ */
950 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
951 DEBUG(1, ("asprintf failed!\n"));
952 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
955 status = ads_search(ads, res, expr, attrs);
961 * Initialize a list of mods to be used in a modify request
962 * @param ctx An initialized TALLOC_CTX
963 * @return allocated ADS_MODLIST
965 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
967 #define ADS_MODLIST_ALLOC_SIZE 10
970 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
971 /* -1 is safety to make sure we don't go over the end.
972 need to reset it to NULL before doing ldap modify */
973 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
975 return (ADS_MODLIST)mods;
980 add an attribute to the list, with values list already constructed
982 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
983 int mod_op, const char *name,
986 const void **invals = (const void **)_invals;
988 LDAPMod **modlist = (LDAPMod **) *mods;
989 struct berval **ber_values = NULL;
990 char **char_values = NULL;
993 mod_op = LDAP_MOD_DELETE;
995 if (mod_op & LDAP_MOD_BVALUES)
996 ber_values = ads_dup_values(ctx,
997 (const struct berval **)invals);
999 char_values = ads_push_strvals(ctx,
1000 (const char **) invals);
1003 /* find the first empty slot */
1004 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1006 if (modlist[curmod] == (LDAPMod *) -1) {
1007 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1008 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1009 return ADS_ERROR(LDAP_NO_MEMORY);
1010 memset(&modlist[curmod], 0,
1011 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1012 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1013 *mods = (ADS_MODLIST)modlist;
1016 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1017 return ADS_ERROR(LDAP_NO_MEMORY);
1018 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1019 if (mod_op & LDAP_MOD_BVALUES) {
1020 modlist[curmod]->mod_bvalues = ber_values;
1021 } else if (mod_op & LDAP_MOD_DELETE) {
1022 modlist[curmod]->mod_values = NULL;
1024 modlist[curmod]->mod_values = char_values;
1027 modlist[curmod]->mod_op = mod_op;
1028 return ADS_ERROR(LDAP_SUCCESS);
1032 * Add a single string value to a mod list
1033 * @param ctx An initialized TALLOC_CTX
1034 * @param mods An initialized ADS_MODLIST
1035 * @param name The attribute name to add
1036 * @param val The value to add - NULL means DELETE
1037 * @return ADS STATUS indicating success of add
1039 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1040 const char *name, const char *val)
1042 const char *values[2];
1048 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1049 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1053 * Add an array of string values to a mod list
1054 * @param ctx An initialized TALLOC_CTX
1055 * @param mods An initialized ADS_MODLIST
1056 * @param name The attribute name to add
1057 * @param vals The array of string values to add - NULL means DELETE
1058 * @return ADS STATUS indicating success of add
1060 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1061 const char *name, const char **vals)
1064 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1065 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1066 name, (const void **) vals);
1071 * Add a single ber-encoded value to a mod list
1072 * @param ctx An initialized TALLOC_CTX
1073 * @param mods An initialized ADS_MODLIST
1074 * @param name The attribute name to add
1075 * @param val The value to add - NULL means DELETE
1076 * @return ADS STATUS indicating success of add
1078 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1079 const char *name, const struct berval *val)
1081 const struct berval *values[2];
1086 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1087 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1088 name, (const void **) values);
1093 * Perform an ldap modify
1094 * @param ads connection to ads server
1095 * @param mod_dn DistinguishedName to modify
1096 * @param mods list of modifications to perform
1097 * @return status of modify
1099 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1102 char *utf8_dn = NULL;
1104 this control is needed to modify that contains a currently
1105 non-existent attribute (but allowable for the object) to run
1107 LDAPControl PermitModify = {
1108 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1111 LDAPControl *controls[2];
1113 controls[0] = &PermitModify;
1116 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
1117 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1120 /* find the end of the list, marked by NULL or -1 */
1121 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1122 /* make sure the end of the list is NULL */
1124 ret = ldap_modify_ext_s(ads->ld, utf8_dn,
1125 (LDAPMod **) mods, controls, NULL);
1127 return ADS_ERROR(ret);
1131 * Perform an ldap add
1132 * @param ads connection to ads server
1133 * @param new_dn DistinguishedName to add
1134 * @param mods list of attributes and values for DN
1135 * @return status of add
1137 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1140 char *utf8_dn = NULL;
1142 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1143 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1144 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1147 /* find the end of the list, marked by NULL or -1 */
1148 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1149 /* make sure the end of the list is NULL */
1152 ret = ldap_add_s(ads->ld, utf8_dn, (LDAPMod**)mods);
1154 return ADS_ERROR(ret);
1158 * Delete a DistinguishedName
1159 * @param ads connection to ads server
1160 * @param new_dn DistinguishedName to delete
1161 * @return status of delete
1163 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1166 char *utf8_dn = NULL;
1167 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1168 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1169 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1172 ret = ldap_delete_s(ads->ld, utf8_dn);
1173 return ADS_ERROR(ret);
1177 * Build an org unit string
1178 * if org unit is Computers or blank then assume a container, otherwise
1179 * assume a / separated list of organisational units.
1180 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1181 * @param ads connection to ads server
1182 * @param org_unit Organizational unit
1183 * @return org unit string - caller must free
1185 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1189 if (!org_unit || !*org_unit) {
1191 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1193 /* samba4 might not yet respond to a wellknownobject-query */
1194 return ret ? ret : SMB_STRDUP("cn=Computers");
1197 if (strequal(org_unit, "Computers")) {
1198 return SMB_STRDUP("cn=Computers");
1201 /* jmcd: removed "\\" from the separation chars, because it is
1202 needed as an escape for chars like '#' which are valid in an
1204 return ads_build_path(org_unit, "/", "ou=", 1);
1208 * Get a org unit string for a well-known GUID
1209 * @param ads connection to ads server
1210 * @param wknguid Well known GUID
1211 * @return org unit string - caller must free
1213 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1217 char *base, *wkn_dn, *ret, **wkn_dn_exp, **bind_dn_exp;
1218 const char *attrs[] = {"distinguishedName", NULL};
1219 int new_ln, wkn_ln, bind_ln, i;
1221 if (wknguid == NULL) {
1225 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1226 DEBUG(1, ("asprintf failed!\n"));
1230 status = ads_search_dn(ads, &res, base, attrs);
1231 if (!ADS_ERR_OK(status)) {
1232 DEBUG(1,("Failed while searching for: %s\n", base));
1238 if (ads_count_replies(ads, res) != 1) {
1242 /* substitute the bind-path from the well-known-guid-search result */
1243 wkn_dn = ads_get_dn(ads, res);
1244 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1245 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1247 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1249 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1252 new_ln = wkn_ln - bind_ln;
1254 ret = wkn_dn_exp[0];
1256 for (i=1; i < new_ln; i++) {
1258 asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]);
1259 ret = SMB_STRDUP(s);
1263 ads_memfree(ads, wkn_dn);
1264 ldap_value_free(wkn_dn_exp);
1265 ldap_value_free(bind_dn_exp);
1271 * Adds (appends) an item to an attribute array, rather then
1272 * replacing the whole list
1273 * @param ctx An initialized TALLOC_CTX
1274 * @param mods An initialized ADS_MODLIST
1275 * @param name name of the ldap attribute to append to
1276 * @param vals an array of values to add
1277 * @return status of addition
1280 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1281 const char *name, const char **vals)
1283 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name, (const void **) vals);
1287 * Determines the computer account's current KVNO via an LDAP lookup
1288 * @param ads An initialized ADS_STRUCT
1289 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1290 * @return the kvno for the computer account, or -1 in case of a failure.
1293 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1295 LDAPMessage *res = NULL;
1296 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1298 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1299 char *dn_string = NULL;
1300 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1302 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1303 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1306 ret = ads_search(ads, (void**)(void *)&res, filter, attrs);
1308 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1309 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1310 ads_msgfree(ads, res);
1314 dn_string = ads_get_dn(ads, res);
1316 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1317 ads_msgfree(ads, res);
1320 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1321 ads_memfree(ads, dn_string);
1323 /* ---------------------------------------------------------
1324 * 0 is returned as a default KVNO from this point on...
1325 * This is done because Windows 2000 does not support key
1326 * version numbers. Chances are that a failure in the next
1327 * step is simply due to Windows 2000 being used for a
1328 * domain controller. */
1331 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1332 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1333 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1334 ads_msgfree(ads, res);
1339 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1340 ads_msgfree(ads, res);
1345 * This clears out all registered spn's for a given hostname
1346 * @param ads An initilaized ADS_STRUCT
1347 * @param machine_name the NetBIOS name of the computer.
1348 * @return 0 upon success, non-zero otherwise.
1351 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1354 LDAPMessage *res = NULL;
1356 const char *servicePrincipalName[1] = {NULL};
1357 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1358 char *dn_string = NULL;
1360 ret = ads_find_machine_acct(ads, (void **)(void *)&res, machine_name);
1361 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1362 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1363 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1364 ads_msgfree(ads, res);
1365 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1368 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1369 ctx = talloc_init("ads_clear_service_principal_names");
1371 ads_msgfree(ads, res);
1372 return ADS_ERROR(LDAP_NO_MEMORY);
1375 if (!(mods = ads_init_mods(ctx))) {
1376 talloc_destroy(ctx);
1377 ads_msgfree(ads, res);
1378 return ADS_ERROR(LDAP_NO_MEMORY);
1380 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1381 if (!ADS_ERR_OK(ret)) {
1382 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1383 ads_msgfree(ads, res);
1384 talloc_destroy(ctx);
1387 dn_string = ads_get_dn(ads, res);
1389 talloc_destroy(ctx);
1390 ads_msgfree(ads, res);
1391 return ADS_ERROR(LDAP_NO_MEMORY);
1393 ret = ads_gen_mod(ads, dn_string, mods);
1394 ads_memfree(ads,dn_string);
1395 if (!ADS_ERR_OK(ret)) {
1396 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1398 ads_msgfree(ads, res);
1399 talloc_destroy(ctx);
1403 ads_msgfree(ads, res);
1404 talloc_destroy(ctx);
1409 * This adds a service principal name to an existing computer account
1410 * (found by hostname) in AD.
1411 * @param ads An initialized ADS_STRUCT
1412 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1413 * @param my_fqdn The fully qualified DNS name of the machine
1414 * @param spn A string of the service principal to add, i.e. 'host'
1415 * @return 0 upon sucess, or non-zero if a failure occurs
1418 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1419 const char *my_fqdn, const char *spn)
1423 LDAPMessage *res = NULL;
1426 char *dn_string = NULL;
1427 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1429 ret = ads_find_machine_acct(ads, (void **)(void *)&res, machine_name);
1430 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1431 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1433 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1434 spn, machine_name, ads->config.realm));
1435 ads_msgfree(ads, res);
1436 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1439 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1440 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1441 ads_msgfree(ads, res);
1442 return ADS_ERROR(LDAP_NO_MEMORY);
1445 /* add short name spn */
1447 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1448 talloc_destroy(ctx);
1449 ads_msgfree(ads, res);
1450 return ADS_ERROR(LDAP_NO_MEMORY);
1453 strlower_m(&psp1[strlen(spn)]);
1454 servicePrincipalName[0] = psp1;
1456 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1457 psp1, machine_name));
1460 /* add fully qualified spn */
1462 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1463 ret = ADS_ERROR(LDAP_NO_MEMORY);
1467 strlower_m(&psp2[strlen(spn)]);
1468 servicePrincipalName[1] = psp2;
1470 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1471 psp2, machine_name));
1473 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1474 ret = ADS_ERROR(LDAP_NO_MEMORY);
1478 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1479 if (!ADS_ERR_OK(ret)) {
1480 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1484 if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1485 ret = ADS_ERROR(LDAP_NO_MEMORY);
1489 ret = ads_gen_mod(ads, dn_string, mods);
1490 ads_memfree(ads,dn_string);
1491 if (!ADS_ERR_OK(ret)) {
1492 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1498 ads_msgfree(ads, res);
1503 * adds a machine account to the ADS server
1504 * @param ads An intialized ADS_STRUCT
1505 * @param machine_name - the NetBIOS machine name of this account.
1506 * @param account_type A number indicating the type of account to create
1507 * @param org_unit The LDAP path in which to place this account
1508 * @return 0 upon success, or non-zero otherwise
1511 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1512 const char *org_unit)
1515 char *samAccountName, *controlstr;
1519 const char *objectClass[] = {"top", "person", "organizationalPerson",
1520 "user", "computer", NULL};
1521 LDAPMessage *res = NULL;
1522 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1523 UF_DONT_EXPIRE_PASSWD |\
1524 UF_ACCOUNTDISABLE );
1526 if (!(ctx = talloc_init("ads_add_machine_acct")))
1527 return ADS_ERROR(LDAP_NO_MEMORY);
1529 ret = ADS_ERROR(LDAP_NO_MEMORY);
1531 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_name, org_unit);
1532 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1534 if ( !new_dn || !samAccountName ) {
1538 #ifndef ENCTYPE_ARCFOUR_HMAC
1539 acct_control |= UF_USE_DES_KEY_ONLY;
1542 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1546 if (!(mods = ads_init_mods(ctx))) {
1550 ads_mod_str(ctx, &mods, "cn", machine_name);
1551 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1552 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1553 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1555 ret = ads_gen_add(ads, new_dn, mods);
1558 ads_msgfree(ads, res);
1559 talloc_destroy(ctx);
1565 dump a binary result from ldap
1567 static void dump_binary(const char *field, struct berval **values)
1570 for (i=0; values[i]; i++) {
1571 printf("%s: ", field);
1572 for (j=0; j<values[i]->bv_len; j++) {
1573 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1579 static void dump_guid(const char *field, struct berval **values)
1583 for (i=0; values[i]; i++) {
1584 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1585 printf("%s: %s\n", field,
1586 smb_uuid_string_static(smb_uuid_unpack_static(guid)));
1591 dump a sid result from ldap
1593 static void dump_sid(const char *field, struct berval **values)
1596 for (i=0; values[i]; i++) {
1598 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1599 printf("%s: %s\n", field, sid_string_static(&sid));
1604 dump ntSecurityDescriptor
1606 static void dump_sd(const char *filed, struct berval **values)
1611 TALLOC_CTX *ctx = 0;
1613 if (!(ctx = talloc_init("sec_io_desc")))
1617 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1618 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1619 prs_set_offset(&ps,0);
1622 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1624 talloc_destroy(ctx);
1627 if (psd) ads_disp_sd(psd);
1630 talloc_destroy(ctx);
1634 dump a string result from ldap
1636 static void dump_string(const char *field, char **values)
1639 for (i=0; values[i]; i++) {
1640 printf("%s: %s\n", field, values[i]);
1645 dump a field from LDAP on stdout
1649 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1654 void (*handler)(const char *, struct berval **);
1656 {"objectGUID", False, dump_guid},
1657 {"netbootGUID", False, dump_guid},
1658 {"nTSecurityDescriptor", False, dump_sd},
1659 {"dnsRecord", False, dump_binary},
1660 {"objectSid", False, dump_sid},
1661 {"tokenGroups", False, dump_sid},
1662 {"tokenGroupsNoGCAcceptable", False, dump_sid},
1663 {"tokengroupsGlobalandUniversal", False, dump_sid},
1668 if (!field) { /* must be end of an entry */
1673 for (i=0; handlers[i].name; i++) {
1674 if (StrCaseCmp(handlers[i].name, field) == 0) {
1675 if (!values) /* first time, indicate string or not */
1676 return handlers[i].string;
1677 handlers[i].handler(field, (struct berval **) values);
1681 if (!handlers[i].name) {
1682 if (!values) /* first time, indicate string conversion */
1684 dump_string(field, (char **)values);
1690 * Dump a result from LDAP on stdout
1691 * used for debugging
1692 * @param ads connection to ads server
1693 * @param res Results to dump
1696 void ads_dump(ADS_STRUCT *ads, void *res)
1698 ads_process_results(ads, res, ads_dump_field, NULL);
1702 * Walk through results, calling a function for each entry found.
1703 * The function receives a field name, a berval * array of values,
1704 * and a data area passed through from the start. The function is
1705 * called once with null for field and values at the end of each
1707 * @param ads connection to ads server
1708 * @param res Results to process
1709 * @param fn Function for processing each result
1710 * @param data_area user-defined area to pass to function
1712 void ads_process_results(ADS_STRUCT *ads, void *res,
1713 BOOL(*fn)(char *, void **, void *),
1719 if (!(ctx = talloc_init("ads_process_results")))
1722 for (msg = ads_first_entry(ads, res); msg;
1723 msg = ads_next_entry(ads, msg)) {
1727 for (utf8_field=ldap_first_attribute(ads->ld,
1728 (LDAPMessage *)msg,&b);
1730 utf8_field=ldap_next_attribute(ads->ld,
1731 (LDAPMessage *)msg,b)) {
1732 struct berval **ber_vals;
1733 char **str_vals, **utf8_vals;
1737 pull_utf8_talloc(ctx, &field, utf8_field);
1738 string = fn(field, NULL, data_area);
1741 utf8_vals = ldap_get_values(ads->ld,
1742 (LDAPMessage *)msg, field);
1743 str_vals = ads_pull_strvals(ctx,
1744 (const char **) utf8_vals);
1745 fn(field, (void **) str_vals, data_area);
1746 ldap_value_free(utf8_vals);
1748 ber_vals = ldap_get_values_len(ads->ld,
1749 (LDAPMessage *)msg, field);
1750 fn(field, (void **) ber_vals, data_area);
1752 ldap_value_free_len(ber_vals);
1754 ldap_memfree(utf8_field);
1757 talloc_free_children(ctx);
1758 fn(NULL, NULL, data_area); /* completed an entry */
1761 talloc_destroy(ctx);
1765 * count how many replies are in a LDAPMessage
1766 * @param ads connection to ads server
1767 * @param res Results to count
1768 * @return number of replies
1770 int ads_count_replies(ADS_STRUCT *ads, void *res)
1772 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1776 * pull the first entry from a ADS result
1777 * @param ads connection to ads server
1778 * @param res Results of search
1779 * @return first entry from result
1781 void *ads_first_entry(ADS_STRUCT *ads, void *res)
1783 return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
1787 * pull the next entry from a ADS result
1788 * @param ads connection to ads server
1789 * @param res Results of search
1790 * @return next entry from result
1792 void *ads_next_entry(ADS_STRUCT *ads, void *res)
1794 return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
1798 * pull a single string from a ADS result
1799 * @param ads connection to ads server
1800 * @param mem_ctx TALLOC_CTX to use for allocating result string
1801 * @param msg Results of search
1802 * @param field Attribute to retrieve
1803 * @return Result string in talloc context
1805 char *ads_pull_string(ADS_STRUCT *ads,
1806 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1813 values = ldap_get_values(ads->ld, msg, field);
1818 rc = pull_utf8_talloc(mem_ctx, &ux_string,
1820 if (rc != (size_t)-1)
1824 ldap_value_free(values);
1829 * pull an array of strings from a ADS result
1830 * @param ads connection to ads server
1831 * @param mem_ctx TALLOC_CTX to use for allocating result string
1832 * @param msg Results of search
1833 * @param field Attribute to retrieve
1834 * @return Result strings in talloc context
1836 char **ads_pull_strings(ADS_STRUCT *ads,
1837 TALLOC_CTX *mem_ctx, void *msg, const char *field,
1844 values = ldap_get_values(ads->ld, msg, field);
1848 *num_values = ldap_count_values(values);
1850 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
1852 ldap_value_free(values);
1856 for (i=0;i<*num_values;i++) {
1857 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
1858 ldap_value_free(values);
1864 ldap_value_free(values);
1869 * pull an array of strings from a ADS result
1870 * (handle large multivalue attributes with range retrieval)
1871 * @param ads connection to ads server
1872 * @param mem_ctx TALLOC_CTX to use for allocating result string
1873 * @param msg Results of search
1874 * @param field Attribute to retrieve
1875 * @param current_strings strings returned by a previous call to this function
1876 * @param next_attribute The next query should ask for this attribute
1877 * @param num_values How many values did we get this time?
1878 * @param more_values Are there more values to get?
1879 * @return Result strings in talloc context
1881 char **ads_pull_strings_range(ADS_STRUCT *ads,
1882 TALLOC_CTX *mem_ctx,
1883 void *msg, const char *field,
1884 char **current_strings,
1885 const char **next_attribute,
1886 size_t *num_strings,
1890 char *expected_range_attrib, *range_attr;
1891 BerElement *ptr = NULL;
1894 size_t num_new_strings;
1895 unsigned long int range_start;
1896 unsigned long int range_end;
1898 /* we might have been given the whole lot anyway */
1899 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
1900 *more_strings = False;
1904 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
1906 /* look for Range result */
1907 for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr);
1909 attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
1910 /* we ignore the fact that this is utf8, as all attributes are ascii... */
1911 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
1919 /* nothing here - this field is just empty */
1920 *more_strings = False;
1924 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
1925 &range_start, &range_end) == 2) {
1926 *more_strings = True;
1928 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
1929 &range_start) == 1) {
1930 *more_strings = False;
1932 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
1934 ldap_memfree(range_attr);
1935 *more_strings = False;
1940 if ((*num_strings) != range_start) {
1941 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
1942 " - aborting range retreival\n",
1943 range_attr, (unsigned int)(*num_strings) + 1, range_start));
1944 ldap_memfree(range_attr);
1945 *more_strings = False;
1949 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
1951 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
1952 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
1953 "strings in this bunch, but we only got %lu - aborting range retreival\n",
1954 range_attr, (unsigned long int)range_end - range_start + 1,
1955 (unsigned long int)num_new_strings));
1956 ldap_memfree(range_attr);
1957 *more_strings = False;
1961 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
1962 *num_strings + num_new_strings);
1964 if (strings == NULL) {
1965 ldap_memfree(range_attr);
1966 *more_strings = False;
1970 if (new_strings && num_new_strings) {
1971 memcpy(&strings[*num_strings], new_strings,
1972 sizeof(*new_strings) * num_new_strings);
1975 (*num_strings) += num_new_strings;
1977 if (*more_strings) {
1978 *next_attribute = talloc_asprintf(mem_ctx,
1983 if (!*next_attribute) {
1984 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
1985 ldap_memfree(range_attr);
1986 *more_strings = False;
1991 ldap_memfree(range_attr);
1997 * pull a single uint32 from a ADS result
1998 * @param ads connection to ads server
1999 * @param msg Results of search
2000 * @param field Attribute to retrieve
2001 * @param v Pointer to int to store result
2002 * @return boolean inidicating success
2004 BOOL ads_pull_uint32(ADS_STRUCT *ads,
2005 void *msg, const char *field, uint32 *v)
2009 values = ldap_get_values(ads->ld, msg, field);
2013 ldap_value_free(values);
2017 *v = atoi(values[0]);
2018 ldap_value_free(values);
2023 * pull a single objectGUID from an ADS result
2024 * @param ads connection to ADS server
2025 * @param msg results of search
2026 * @param guid 37-byte area to receive text guid
2027 * @return boolean indicating success
2029 BOOL ads_pull_guid(ADS_STRUCT *ads,
2030 void *msg, struct uuid *guid)
2033 UUID_FLAT flat_guid;
2035 values = ldap_get_values(ads->ld, msg, "objectGUID");
2040 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2041 smb_uuid_unpack(flat_guid, guid);
2042 ldap_value_free(values);
2045 ldap_value_free(values);
2052 * pull a single DOM_SID from a ADS result
2053 * @param ads connection to ads server
2054 * @param msg Results of search
2055 * @param field Attribute to retrieve
2056 * @param sid Pointer to sid to store result
2057 * @return boolean inidicating success
2059 BOOL ads_pull_sid(ADS_STRUCT *ads,
2060 void *msg, const char *field, DOM_SID *sid)
2062 struct berval **values;
2065 values = ldap_get_values_len(ads->ld, msg, field);
2071 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2073 ldap_value_free_len(values);
2078 * pull an array of DOM_SIDs from a ADS result
2079 * @param ads connection to ads server
2080 * @param mem_ctx TALLOC_CTX for allocating sid array
2081 * @param msg Results of search
2082 * @param field Attribute to retrieve
2083 * @param sids pointer to sid array to allocate
2084 * @return the count of SIDs pulled
2086 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2087 void *msg, const char *field, DOM_SID **sids)
2089 struct berval **values;
2093 values = ldap_get_values_len(ads->ld, msg, field);
2098 for (i=0; values[i]; i++)
2101 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2103 ldap_value_free_len(values);
2108 for (i=0; values[i]; i++) {
2109 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2112 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2117 ldap_value_free_len(values);
2122 * pull a SEC_DESC from a ADS result
2123 * @param ads connection to ads server
2124 * @param mem_ctx TALLOC_CTX for allocating sid array
2125 * @param msg Results of search
2126 * @param field Attribute to retrieve
2127 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2128 * @return boolean inidicating success
2130 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2131 void *msg, const char *field, SEC_DESC **sd)
2133 struct berval **values;
2137 values = ldap_get_values_len(ads->ld, msg, field);
2139 if (!values) return False;
2142 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2143 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2144 prs_set_offset(&ps,0);
2146 ret = sec_io_desc("sd", sd, &ps, 1);
2149 ldap_value_free_len(values);
2154 * in order to support usernames longer than 21 characters we need to
2155 * use both the sAMAccountName and the userPrincipalName attributes
2156 * It seems that not all users have the userPrincipalName attribute set
2158 * @param ads connection to ads server
2159 * @param mem_ctx TALLOC_CTX for allocating sid array
2160 * @param msg Results of search
2161 * @return the username
2163 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
2168 /* lookup_name() only works on the sAMAccountName to
2169 returning the username portion of userPrincipalName
2170 breaks winbindd_getpwnam() */
2172 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2173 if (ret && (p = strchr_m(ret, '@'))) {
2178 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2183 * find the update serial number - this is the core of the ldap cache
2184 * @param ads connection to ads server
2185 * @param ads connection to ADS server
2186 * @param usn Pointer to retrieved update serial number
2187 * @return status of search
2189 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2191 const char *attrs[] = {"highestCommittedUSN", NULL};
2195 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2196 if (!ADS_ERR_OK(status))
2199 if (ads_count_replies(ads, res) != 1) {
2200 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2203 ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
2204 ads_msgfree(ads, res);
2208 /* parse a ADS timestring - typical string is
2209 '20020917091222.0Z0' which means 09:12.22 17th September
2211 static time_t ads_parse_time(const char *str)
2217 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2218 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2219 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2228 /********************************************************************
2229 ********************************************************************/
2231 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2233 const char *attrs[] = {"currentTime", NULL};
2238 ADS_STRUCT *ads_s = ads;
2240 if (!(ctx = talloc_init("ads_current_time"))) {
2241 return ADS_ERROR(LDAP_NO_MEMORY);
2244 /* establish a new ldap tcp session if necessary */
2247 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2248 ads->server.ldap_server )) == NULL )
2252 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2253 status = ads_connect( ads_s );
2254 if ( !ADS_ERR_OK(status))
2258 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2259 if (!ADS_ERR_OK(status)) {
2263 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2265 ads_msgfree(ads, res);
2266 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2270 /* but save the time and offset in the original ADS_STRUCT */
2272 ads->config.current_time = ads_parse_time(timestr);
2274 if (ads->config.current_time != 0) {
2275 ads->auth.time_offset = ads->config.current_time - time(NULL);
2276 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2279 ads_msgfree(ads, res);
2281 status = ADS_SUCCESS;
2284 /* free any temporary ads connections */
2285 if ( ads_s != ads ) {
2286 ads_destroy( &ads_s );
2288 talloc_destroy(ctx);
2293 /********************************************************************
2294 ********************************************************************/
2296 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2298 const char *attrs[] = {"domainFunctionality", NULL};
2301 ADS_STRUCT *ads_s = ads;
2303 *val = DS_DOMAIN_FUNCTION_2000;
2305 /* establish a new ldap tcp session if necessary */
2308 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2309 ads->server.ldap_server )) == NULL )
2313 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2314 status = ads_connect( ads_s );
2315 if ( !ADS_ERR_OK(status))
2319 /* If the attribute does not exist assume it is a Windows 2000
2320 functional domain */
2322 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2323 if (!ADS_ERR_OK(status)) {
2324 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2325 status = ADS_SUCCESS;
2330 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2331 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2333 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2336 ads_msgfree(ads, res);
2339 /* free any temporary ads connections */
2340 if ( ads_s != ads ) {
2341 ads_destroy( &ads_s );
2348 * find the domain sid for our domain
2349 * @param ads connection to ads server
2350 * @param sid Pointer to domain sid
2351 * @return status of search
2353 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2355 const char *attrs[] = {"objectSid", NULL};
2359 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2361 if (!ADS_ERR_OK(rc)) return rc;
2362 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2363 ads_msgfree(ads, res);
2364 return ADS_ERROR_SYSTEM(ENOENT);
2366 ads_msgfree(ads, res);
2372 * find our site name
2373 * @param ads connection to ads server
2374 * @param mem_ctx Pointer to talloc context
2375 * @param site_name Pointer to the sitename
2376 * @return status of search
2378 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2382 const char *dn, *service_name;
2383 const char *attrs[] = { "dsServiceName", NULL };
2385 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2386 if (!ADS_ERR_OK(status)) {
2390 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2391 if (service_name == NULL) {
2392 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2395 /* go up three levels */
2396 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2398 return ADS_ERROR(LDAP_NO_MEMORY);
2401 *site_name = talloc_strdup(mem_ctx, dn);
2402 if (*site_name == NULL) {
2403 return ADS_ERROR(LDAP_NO_MEMORY);
2406 ads_msgfree(ads, res);
2410 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2415 * find the site dn where a machine resides
2416 * @param ads connection to ads server
2417 * @param mem_ctx Pointer to talloc context
2418 * @param computer_name name of the machine
2419 * @param site_name Pointer to the sitename
2420 * @return status of search
2422 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2426 const char *parent, *config_context, *filter;
2427 const char *attrs[] = { "configurationNamingContext", NULL };
2430 /* shortcut a query */
2431 if (strequal(computer_name, ads->config.ldap_server_name)) {
2432 return ads_site_dn(ads, mem_ctx, site_dn);
2435 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2436 if (!ADS_ERR_OK(status)) {
2440 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2441 if (config_context == NULL) {
2442 return ADS_ERROR(LDAP_NO_MEMORY);
2445 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2446 if (filter == NULL) {
2447 return ADS_ERROR(LDAP_NO_MEMORY);
2450 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, filter, NULL, &res);
2451 if (!ADS_ERR_OK(status)) {
2455 if (ads_count_replies(ads, res) != 1) {
2456 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2459 dn = ads_get_dn(ads, res);
2461 return ADS_ERROR(LDAP_NO_MEMORY);
2464 /* go up three levels */
2465 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2466 if (parent == NULL) {
2467 ads_memfree(ads, dn);
2468 return ADS_ERROR(LDAP_NO_MEMORY);
2471 *site_dn = talloc_strdup(mem_ctx, parent);
2472 if (*site_dn == NULL) {
2473 ads_memfree(ads, dn);
2474 ADS_ERROR(LDAP_NO_MEMORY);
2477 ads_memfree(ads, dn);
2478 ads_msgfree(ads, res);
2484 * get the upn suffixes for a domain
2485 * @param ads connection to ads server
2486 * @param mem_ctx Pointer to talloc context
2487 * @param suffixes Pointer to an array of suffixes
2488 * @param site_name Pointer to the number of suffixes
2489 * @return status of search
2491 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char **suffixes, size_t *num_suffixes)
2495 const char *config_context, *base;
2496 const char *attrs[] = { "configurationNamingContext", NULL };
2497 const char *attrs2[] = { "uPNSuffixes", NULL };
2499 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2500 if (!ADS_ERR_OK(status)) {
2504 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2505 if (config_context == NULL) {
2506 return ADS_ERROR(LDAP_NO_MEMORY);
2509 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2511 return ADS_ERROR(LDAP_NO_MEMORY);
2514 status = ads_search_dn(ads, &res, base, attrs2);
2515 if (!ADS_ERR_OK(status)) {
2519 if (ads_count_replies(ads, res) != 1) {
2520 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2523 suffixes = ads_pull_strings(ads, mem_ctx, &res, "uPNSuffixes", num_suffixes);
2524 if (suffixes == NULL) {
2525 ads_msgfree(ads, res);
2526 return ADS_ERROR(LDAP_NO_MEMORY);
2529 ads_msgfree(ads, res);
2535 * pull a DOM_SID from an extended dn string
2536 * @param mem_ctx TALLOC_CTX
2537 * @param flags string type of extended_dn
2538 * @param sid pointer to a DOM_SID
2539 * @return boolean inidicating success
2541 BOOL ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
2543 enum ads_extended_dn_flags flags,
2553 * ADS_EXTENDED_DN_HEX_STRING:
2554 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2556 * ADS_EXTENDED_DN_STRING (only with w2k3):
2557 <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
2560 p = strchr(dn, ';');
2565 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
2569 p += strlen(";<SID=");
2578 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
2582 case ADS_EXTENDED_DN_STRING:
2583 if (!string_to_sid(sid, p)) {
2587 case ADS_EXTENDED_DN_HEX_STRING: {
2591 buf_len = strhex_to_str(buf, strlen(p), p);
2596 if (!sid_parse(buf, buf_len, sid)) {
2597 DEBUG(10,("failed to parse sid\n"));
2603 DEBUG(10,("unknown extended dn format\n"));
2611 * pull an array of DOM_SIDs from a ADS result
2612 * @param ads connection to ads server
2613 * @param mem_ctx TALLOC_CTX for allocating sid array
2614 * @param msg Results of search
2615 * @param field Attribute to retrieve
2616 * @param flags string type of extended_dn
2617 * @param sids pointer to sid array to allocate
2618 * @return the count of SIDs pulled
2620 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
2621 TALLOC_CTX *mem_ctx,
2624 enum ads_extended_dn_flags flags,
2631 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
2632 &dn_count)) == NULL) {
2636 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
2638 TALLOC_FREE(dn_strings);
2642 for (i=0; i<dn_count; i++) {
2644 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
2645 flags, &(*sids)[i])) {
2647 TALLOC_FREE(dn_strings);
2652 TALLOC_FREE(dn_strings);
2657 /********************************************************************
2658 ********************************************************************/
2660 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2662 LDAPMessage *res = NULL;
2667 status = ads_find_machine_acct(ads, (void **)(void *)&res, global_myname());
2668 if (!ADS_ERR_OK(status)) {
2669 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2674 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2675 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2679 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
2680 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
2684 ads_msgfree(ads, res);
2689 /********************************************************************
2690 ********************************************************************/
2692 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2694 LDAPMessage *res = NULL;
2699 status = ads_find_machine_acct(ads, (void **)(void *)&res, global_myname());
2700 if (!ADS_ERR_OK(status)) {
2701 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
2706 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2707 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
2711 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
2712 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
2716 ads_msgfree(ads, res);
2721 /********************************************************************
2722 ********************************************************************/
2724 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2726 LDAPMessage *res = NULL;
2731 status = ads_find_machine_acct(ads, (void **)(void *)&res, global_myname());
2732 if (!ADS_ERR_OK(status)) {
2733 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2738 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2739 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2743 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
2744 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
2748 ads_msgfree(ads, res);