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.flags = cldap_reply.flags;
163 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.hostname);
164 strupper_m(cldap_reply.domain);
165 ads->config.realm = SMB_STRDUP(cldap_reply.domain);
166 ads->config.bind_path = ads_build_dn(ads->config.realm);
167 ads->server.workgroup = SMB_STRDUP(cldap_reply.netbios_domain);
169 ads->ldap_port = LDAP_PORT;
170 ads->ldap_ip = *interpret_addr2(srv);
173 /* Store our site name. */
174 sitename_store( cldap_reply.client_site_name );
179 /**********************************************************************
180 Try to find an AD dc using our internal name resolution routines
181 Try the realm first and then then workgroup name if netbios is not
183 **********************************************************************/
185 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
189 struct ip_service *ip_list;
191 BOOL got_realm = False;
192 BOOL use_own_domain = False;
193 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
195 /* if the realm and workgroup are both empty, assume they are ours */
198 c_realm = ads->server.realm;
200 if ( !c_realm || !*c_realm ) {
201 /* special case where no realm and no workgroup means our own */
202 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
203 use_own_domain = True;
204 c_realm = lp_realm();
208 if (c_realm && *c_realm)
212 /* we need to try once with the realm name and fallback to the
213 netbios domain name if we fail (if netbios has not been disabled */
215 if ( !got_realm && !lp_disable_netbios() ) {
216 c_realm = ads->server.workgroup;
217 if (!c_realm || !*c_realm) {
218 if ( use_own_domain )
219 c_realm = lp_workgroup();
222 if ( !c_realm || !*c_realm ) {
223 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
224 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
228 pstrcpy( realm, c_realm );
230 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
231 (got_realm ? "realm" : "domain"), realm));
233 status = get_sorted_dc_list(realm, &ip_list, &count, got_realm);
234 if (!NT_STATUS_IS_OK(status)) {
235 /* fall back to netbios if we can */
236 if ( got_realm && !lp_disable_netbios() ) {
244 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
245 for ( i=0; i<count; i++ ) {
248 fstrcpy( server, inet_ntoa(ip_list[i].ip) );
250 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
253 if ( ads_try_connect(ads, server) ) {
258 /* keep track of failures */
259 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
264 return NT_STATUS_NO_LOGON_SERVERS;
269 * Connect to the LDAP server
270 * @param ads Pointer to an existing ADS_STRUCT
271 * @return status of connection
273 ADS_STATUS ads_connect(ADS_STRUCT *ads)
275 int version = LDAP_VERSION3;
279 ads->last_attempt = time(NULL);
282 /* try with a user specified server */
284 if (ads->server.ldap_server &&
285 ads_try_connect(ads, ads->server.ldap_server)) {
289 ntstatus = ads_find_dc(ads);
290 if (NT_STATUS_IS_OK(ntstatus)) {
294 return ADS_ERROR_NT(ntstatus);
297 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
299 if (!ads->auth.user_name) {
300 /* Must use the userPrincipalName value here or sAMAccountName
301 and not servicePrincipalName; found by Guenther Deschner */
303 asprintf(&ads->auth.user_name, "%s$", global_myname() );
306 if (!ads->auth.realm) {
307 ads->auth.realm = SMB_STRDUP(ads->config.realm);
310 if (!ads->auth.kdc_server) {
311 ads->auth.kdc_server = SMB_STRDUP(inet_ntoa(ads->ldap_ip));
315 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
316 to MIT kerberos to work (tridge) */
319 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
320 setenv(env, ads->auth.kdc_server, 1);
325 /* If the caller() requested no LDAP bind, then we are done */
327 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
331 /* Otherwise setup the TCP LDAP session */
333 if ( (ads->ld = ldap_open_with_timeout(ads->config.ldap_server_name,
334 LDAP_PORT, lp_ldap_timeout())) == NULL )
336 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
339 /* cache the successful connection */
340 saf_store( ads->server.workgroup, inet_ntoa(ads->ldap_ip));
342 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
344 status = ADS_ERROR(smb_ldap_start_tls(ads->ld, version));
345 if (!ADS_ERR_OK(status)) {
349 /* fill in the current time and offsets */
351 status = ads_current_time( ads );
352 if ( !ADS_ERR_OK(status) ) {
356 /* Now do the bind */
358 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
359 return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
362 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
363 return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
366 return ads_sasl_bind(ads);
370 Duplicate a struct berval into talloc'ed memory
372 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
374 struct berval *value;
376 if (!in_val) return NULL;
378 value = TALLOC_ZERO_P(ctx, struct berval);
381 if (in_val->bv_len == 0) return value;
383 value->bv_len = in_val->bv_len;
384 value->bv_val = TALLOC_MEMDUP(ctx, in_val->bv_val, in_val->bv_len);
389 Make a values list out of an array of (struct berval *)
391 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
392 const struct berval **in_vals)
394 struct berval **values;
397 if (!in_vals) return NULL;
398 for (i=0; in_vals[i]; i++)
400 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
401 if (!values) return NULL;
403 for (i=0; in_vals[i]; i++) {
404 values[i] = dup_berval(ctx, in_vals[i]);
410 UTF8-encode a values list out of an array of (char *)
412 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
417 if (!in_vals) return NULL;
418 for (i=0; in_vals[i]; i++)
420 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
421 if (!values) return NULL;
423 for (i=0; in_vals[i]; i++) {
424 push_utf8_talloc(ctx, &values[i], in_vals[i]);
430 Pull a (char *) array out of a UTF8-encoded values list
432 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
437 if (!in_vals) return NULL;
438 for (i=0; in_vals[i]; i++)
440 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
441 if (!values) return NULL;
443 for (i=0; in_vals[i]; i++) {
444 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
450 * Do a search with paged results. cookie must be null on the first
451 * call, and then returned on each subsequent call. It will be null
452 * again when the entire search is complete
453 * @param ads connection to ads server
454 * @param bind_path Base dn for the search
455 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
456 * @param expr Search expression - specified in local charset
457 * @param attrs Attributes to retrieve - specified in utf8 or ascii
458 * @param res ** which will contain results - free res* with ads_msgfree()
459 * @param count Number of entries retrieved on this page
460 * @param cookie The paged results cookie to be returned on subsequent calls
461 * @return status of search
463 ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads, const char *bind_path,
464 int scope, const char *expr,
465 const char **attrs, void *args, void **res,
466 int *count, void **cookie)
469 char *utf8_expr, *utf8_path, **search_attrs;
470 LDAPControl PagedResults, NoReferrals, ExtendedDn, *controls[4], **rcontrols;
471 BerElement *cookie_be = NULL;
472 struct berval *cookie_bv= NULL;
473 BerElement *extdn_be = NULL;
474 struct berval *extdn_bv= NULL;
477 ads_control *external_control = (ads_control *) args;
481 if (!(ctx = talloc_init("ads_do_paged_search_args")))
482 return ADS_ERROR(LDAP_NO_MEMORY);
484 /* 0 means the conversion worked but the result was empty
485 so we only fail if it's -1. In any case, it always
486 at least nulls out the dest */
487 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
488 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
493 if (!attrs || !(*attrs))
496 /* This would be the utf8-encoded version...*/
497 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
498 if (!(str_list_copy(&search_attrs, attrs))) {
505 /* Paged results only available on ldap v3 or later */
506 ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
507 if (version < LDAP_VERSION3) {
508 rc = LDAP_NOT_SUPPORTED;
512 cookie_be = ber_alloc_t(LBER_USE_DER);
514 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
515 ber_bvfree(*cookie); /* don't need it from last time */
518 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
520 ber_flatten(cookie_be, &cookie_bv);
521 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
522 PagedResults.ldctl_iscritical = (char) 1;
523 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
524 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
526 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
527 NoReferrals.ldctl_iscritical = (char) 0;
528 NoReferrals.ldctl_value.bv_len = 0;
529 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
531 if (external_control && strequal(external_control->control, ADS_EXTENDED_DN_OID)) {
533 ExtendedDn.ldctl_oid = CONST_DISCARD(char *, external_control->control);
534 ExtendedDn.ldctl_iscritical = (char) external_control->critical;
536 /* win2k does not accept a ldctl_value beeing passed in */
538 if (external_control->val != 0) {
540 if ((extdn_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
545 if ((ber_printf(extdn_be, "{i}", (ber_int_t) external_control->val)) == -1) {
549 if ((ber_flatten(extdn_be, &extdn_bv)) == -1) {
554 ExtendedDn.ldctl_value.bv_len = extdn_bv->bv_len;
555 ExtendedDn.ldctl_value.bv_val = extdn_bv->bv_val;
558 ExtendedDn.ldctl_value.bv_len = 0;
559 ExtendedDn.ldctl_value.bv_val = NULL;
562 controls[0] = &NoReferrals;
563 controls[1] = &PagedResults;
564 controls[2] = &ExtendedDn;
568 controls[0] = &NoReferrals;
569 controls[1] = &PagedResults;
573 /* we need to disable referrals as the openldap libs don't
574 handle them and paged results at the same time. Using them
575 together results in the result record containing the server
576 page control being removed from the result list (tridge/jmcd)
578 leaving this in despite the control that says don't generate
579 referrals, in case the server doesn't support it (jmcd)
581 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
583 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
584 search_attrs, 0, controls,
586 (LDAPMessage **)res);
588 ber_free(cookie_be, 1);
589 ber_bvfree(cookie_bv);
592 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
593 ldap_err2string(rc)));
597 rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
598 NULL, &rcontrols, 0);
604 for (i=0; rcontrols[i]; i++) {
605 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
606 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
607 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
609 /* the berval is the cookie, but must be freed when
611 if (cookie_bv->bv_len) /* still more to do */
612 *cookie=ber_bvdup(cookie_bv);
615 ber_bvfree(cookie_bv);
616 ber_free(cookie_be, 1);
620 ldap_controls_free(rcontrols);
626 ber_free(extdn_be, 1);
630 ber_bvfree(extdn_bv);
633 /* if/when we decide to utf8-encode attrs, take out this next line */
634 str_list_free(&search_attrs);
636 return ADS_ERROR(rc);
639 ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
640 int scope, const char *expr,
641 const char **attrs, void **res,
642 int *count, void **cookie)
644 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
649 * Get all results for a search. This uses ads_do_paged_search() to return
650 * all entries in a large search.
651 * @param ads connection to ads server
652 * @param bind_path Base dn for the search
653 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
654 * @param expr Search expression
655 * @param attrs Attributes to retrieve
656 * @param res ** which will contain results - free res* with ads_msgfree()
657 * @return status of search
659 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
660 int scope, const char *expr,
661 const char **attrs, void *args, void **res)
668 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
671 if (!ADS_ERR_OK(status))
674 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
678 LDAPMessage *msg, *next;
680 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
681 attrs, args, &res2, &count, &cookie);
683 if (!ADS_ERR_OK(status2)) break;
685 /* this relies on the way that ldap_add_result_entry() works internally. I hope
686 that this works on all ldap libs, but I have only tested with openldap */
687 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
688 next = ads_next_entry(ads, msg);
689 ldap_add_result_entry((LDAPMessage **)res, msg);
691 /* note that we do not free res2, as the memory is now
692 part of the main returned list */
695 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
696 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
702 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
703 int scope, const char *expr,
704 const char **attrs, void **res)
706 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
710 * Run a function on all results for a search. Uses ads_do_paged_search() and
711 * runs the function as each page is returned, using ads_process_results()
712 * @param ads connection to ads server
713 * @param bind_path Base dn for the search
714 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
715 * @param expr Search expression - specified in local charset
716 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
717 * @param fn Function which takes attr name, values list, and data_area
718 * @param data_area Pointer which is passed to function on each call
719 * @return status of search
721 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
722 int scope, const char *expr, const char **attrs,
723 BOOL(*fn)(char *, void **, void *),
731 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
734 if (!ADS_ERR_OK(status)) return status;
736 ads_process_results(ads, res, fn, data_area);
737 ads_msgfree(ads, res);
740 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
741 &res, &count, &cookie);
743 if (!ADS_ERR_OK(status)) break;
745 ads_process_results(ads, res, fn, data_area);
746 ads_msgfree(ads, res);
753 * Do a search with a timeout.
754 * @param ads connection to ads server
755 * @param bind_path Base dn for the search
756 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
757 * @param expr Search expression
758 * @param attrs Attributes to retrieve
759 * @param res ** which will contain results - free res* with ads_msgfree()
760 * @return status of search
762 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
764 const char **attrs, void **res)
767 char *utf8_expr, *utf8_path, **search_attrs = NULL;
771 if (!(ctx = talloc_init("ads_do_search"))) {
772 DEBUG(1,("ads_do_search: talloc_init() failed!"));
773 return ADS_ERROR(LDAP_NO_MEMORY);
776 /* 0 means the conversion worked but the result was empty
777 so we only fail if it's negative. In any case, it always
778 at least nulls out the dest */
779 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
780 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
781 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
786 if (!attrs || !(*attrs))
789 /* This would be the utf8-encoded version...*/
790 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
791 if (!(str_list_copy(&search_attrs, attrs)))
793 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
799 /* see the note in ads_do_paged_search - we *must* disable referrals */
800 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
802 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
803 search_attrs, 0, NULL, NULL,
805 (LDAPMessage **)res);
807 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
808 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
814 /* if/when we decide to utf8-encode attrs, take out this next line */
815 str_list_free(&search_attrs);
816 return ADS_ERROR(rc);
819 * Do a general ADS search
820 * @param ads connection to ads server
821 * @param res ** which will contain results - free res* with ads_msgfree()
822 * @param expr Search expression
823 * @param attrs Attributes to retrieve
824 * @return status of search
826 ADS_STATUS ads_search(ADS_STRUCT *ads, void **res,
830 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
835 * Do a search on a specific DistinguishedName
836 * @param ads connection to ads server
837 * @param res ** which will contain results - free res* with ads_msgfree()
838 * @param dn DistinguishName to search
839 * @param attrs Attributes to retrieve
840 * @return status of search
842 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, void *_res,
846 void **res = (void **)_res;
847 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res);
851 * Free up memory from a ads_search
852 * @param ads connection to ads server
853 * @param msg Search results to free
855 void ads_msgfree(ADS_STRUCT *ads, void *msg)
862 * Free up memory from various ads requests
863 * @param ads connection to ads server
864 * @param mem Area to free
866 void ads_memfree(ADS_STRUCT *ads, void *mem)
872 * Get a dn from search results
873 * @param ads connection to ads server
874 * @param msg Search result
877 char *ads_get_dn(ADS_STRUCT *ads, void *msg)
879 char *utf8_dn, *unix_dn;
881 utf8_dn = ldap_get_dn(ads->ld, msg);
884 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
888 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
889 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
893 ldap_memfree(utf8_dn);
898 * Get a canonical dn from search results
899 * @param ads connection to ads server
900 * @param msg Search result
903 char *ads_get_dn_canonical(ADS_STRUCT *ads, void *msg)
905 #ifdef HAVE_LDAP_DN2AD_CANONICAL
906 return ldap_dn2ad_canonical(ads_get_dn(ads, msg));
913 * Get the parent from a dn
914 * @param dn the dn to return the parent from
915 * @return parent dn string
917 char *ads_parent_dn(const char *dn)
935 * Find a machine account given a hostname
936 * @param ads connection to ads server
937 * @param res ** which will contain results - free res* with ads_msgfree()
938 * @param host Hostname to search for
939 * @return status of search
941 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *machine)
945 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
949 /* the easiest way to find a machine account anywhere in the tree
950 is to look for hostname$ */
951 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
952 DEBUG(1, ("asprintf failed!\n"));
953 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
956 status = ads_search(ads, res, expr, attrs);
962 * Initialize a list of mods to be used in a modify request
963 * @param ctx An initialized TALLOC_CTX
964 * @return allocated ADS_MODLIST
966 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
968 #define ADS_MODLIST_ALLOC_SIZE 10
971 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
972 /* -1 is safety to make sure we don't go over the end.
973 need to reset it to NULL before doing ldap modify */
974 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
976 return (ADS_MODLIST)mods;
981 add an attribute to the list, with values list already constructed
983 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
984 int mod_op, const char *name,
987 const void **invals = (const void **)_invals;
989 LDAPMod **modlist = (LDAPMod **) *mods;
990 struct berval **ber_values = NULL;
991 char **char_values = NULL;
994 mod_op = LDAP_MOD_DELETE;
996 if (mod_op & LDAP_MOD_BVALUES)
997 ber_values = ads_dup_values(ctx,
998 (const struct berval **)invals);
1000 char_values = ads_push_strvals(ctx,
1001 (const char **) invals);
1004 /* find the first empty slot */
1005 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1007 if (modlist[curmod] == (LDAPMod *) -1) {
1008 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1009 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1010 return ADS_ERROR(LDAP_NO_MEMORY);
1011 memset(&modlist[curmod], 0,
1012 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1013 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1014 *mods = (ADS_MODLIST)modlist;
1017 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1018 return ADS_ERROR(LDAP_NO_MEMORY);
1019 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1020 if (mod_op & LDAP_MOD_BVALUES) {
1021 modlist[curmod]->mod_bvalues = ber_values;
1022 } else if (mod_op & LDAP_MOD_DELETE) {
1023 modlist[curmod]->mod_values = NULL;
1025 modlist[curmod]->mod_values = char_values;
1028 modlist[curmod]->mod_op = mod_op;
1029 return ADS_ERROR(LDAP_SUCCESS);
1033 * Add a single string value to a mod list
1034 * @param ctx An initialized TALLOC_CTX
1035 * @param mods An initialized ADS_MODLIST
1036 * @param name The attribute name to add
1037 * @param val The value to add - NULL means DELETE
1038 * @return ADS STATUS indicating success of add
1040 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1041 const char *name, const char *val)
1043 const char *values[2];
1049 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1050 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1054 * Add an array of string values to a mod list
1055 * @param ctx An initialized TALLOC_CTX
1056 * @param mods An initialized ADS_MODLIST
1057 * @param name The attribute name to add
1058 * @param vals The array of string values to add - NULL means DELETE
1059 * @return ADS STATUS indicating success of add
1061 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1062 const char *name, const char **vals)
1065 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1066 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1067 name, (const void **) vals);
1072 * Add a single ber-encoded value to a mod list
1073 * @param ctx An initialized TALLOC_CTX
1074 * @param mods An initialized ADS_MODLIST
1075 * @param name The attribute name to add
1076 * @param val The value to add - NULL means DELETE
1077 * @return ADS STATUS indicating success of add
1079 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1080 const char *name, const struct berval *val)
1082 const struct berval *values[2];
1087 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1088 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1089 name, (const void **) values);
1094 * Perform an ldap modify
1095 * @param ads connection to ads server
1096 * @param mod_dn DistinguishedName to modify
1097 * @param mods list of modifications to perform
1098 * @return status of modify
1100 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1103 char *utf8_dn = NULL;
1105 this control is needed to modify that contains a currently
1106 non-existent attribute (but allowable for the object) to run
1108 LDAPControl PermitModify = {
1109 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1112 LDAPControl *controls[2];
1114 controls[0] = &PermitModify;
1117 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
1118 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1121 /* find the end of the list, marked by NULL or -1 */
1122 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1123 /* make sure the end of the list is NULL */
1125 ret = ldap_modify_ext_s(ads->ld, utf8_dn,
1126 (LDAPMod **) mods, controls, NULL);
1128 return ADS_ERROR(ret);
1132 * Perform an ldap add
1133 * @param ads connection to ads server
1134 * @param new_dn DistinguishedName to add
1135 * @param mods list of attributes and values for DN
1136 * @return status of add
1138 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1141 char *utf8_dn = NULL;
1143 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1144 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1145 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1148 /* find the end of the list, marked by NULL or -1 */
1149 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1150 /* make sure the end of the list is NULL */
1153 ret = ldap_add_s(ads->ld, utf8_dn, (LDAPMod**)mods);
1155 return ADS_ERROR(ret);
1159 * Delete a DistinguishedName
1160 * @param ads connection to ads server
1161 * @param new_dn DistinguishedName to delete
1162 * @return status of delete
1164 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1167 char *utf8_dn = NULL;
1168 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1169 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1170 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1173 ret = ldap_delete_s(ads->ld, utf8_dn);
1174 return ADS_ERROR(ret);
1178 * Build an org unit string
1179 * if org unit is Computers or blank then assume a container, otherwise
1180 * assume a / separated list of organisational units.
1181 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1182 * @param ads connection to ads server
1183 * @param org_unit Organizational unit
1184 * @return org unit string - caller must free
1186 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1190 if (!org_unit || !*org_unit) {
1192 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1194 /* samba4 might not yet respond to a wellknownobject-query */
1195 return ret ? ret : SMB_STRDUP("cn=Computers");
1198 if (strequal(org_unit, "Computers")) {
1199 return SMB_STRDUP("cn=Computers");
1202 /* jmcd: removed "\\" from the separation chars, because it is
1203 needed as an escape for chars like '#' which are valid in an
1205 return ads_build_path(org_unit, "/", "ou=", 1);
1209 * Get a org unit string for a well-known GUID
1210 * @param ads connection to ads server
1211 * @param wknguid Well known GUID
1212 * @return org unit string - caller must free
1214 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1218 char *base, *wkn_dn, *ret, **wkn_dn_exp, **bind_dn_exp;
1219 const char *attrs[] = {"distinguishedName", NULL};
1220 int new_ln, wkn_ln, bind_ln, i;
1222 if (wknguid == NULL) {
1226 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1227 DEBUG(1, ("asprintf failed!\n"));
1231 status = ads_search_dn(ads, &res, base, attrs);
1232 if (!ADS_ERR_OK(status)) {
1233 DEBUG(1,("Failed while searching for: %s\n", base));
1239 if (ads_count_replies(ads, res) != 1) {
1243 /* substitute the bind-path from the well-known-guid-search result */
1244 wkn_dn = ads_get_dn(ads, res);
1245 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1246 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1248 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1250 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1253 new_ln = wkn_ln - bind_ln;
1255 ret = wkn_dn_exp[0];
1257 for (i=1; i < new_ln; i++) {
1259 asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]);
1260 ret = SMB_STRDUP(s);
1264 ads_memfree(ads, wkn_dn);
1265 ldap_value_free(wkn_dn_exp);
1266 ldap_value_free(bind_dn_exp);
1272 * Adds (appends) an item to an attribute array, rather then
1273 * replacing the whole list
1274 * @param ctx An initialized TALLOC_CTX
1275 * @param mods An initialized ADS_MODLIST
1276 * @param name name of the ldap attribute to append to
1277 * @param vals an array of values to add
1278 * @return status of addition
1281 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1282 const char *name, const char **vals)
1284 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name, (const void **) vals);
1288 * Determines the computer account's current KVNO via an LDAP lookup
1289 * @param ads An initialized ADS_STRUCT
1290 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1291 * @return the kvno for the computer account, or -1 in case of a failure.
1294 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1296 LDAPMessage *res = NULL;
1297 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1299 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1300 char *dn_string = NULL;
1301 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1303 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1304 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1307 ret = ads_search(ads, (void**)(void *)&res, filter, attrs);
1309 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1310 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1311 ads_msgfree(ads, res);
1315 dn_string = ads_get_dn(ads, res);
1317 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1318 ads_msgfree(ads, res);
1321 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1322 ads_memfree(ads, dn_string);
1324 /* ---------------------------------------------------------
1325 * 0 is returned as a default KVNO from this point on...
1326 * This is done because Windows 2000 does not support key
1327 * version numbers. Chances are that a failure in the next
1328 * step is simply due to Windows 2000 being used for a
1329 * domain controller. */
1332 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1333 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1334 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1335 ads_msgfree(ads, res);
1340 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1341 ads_msgfree(ads, res);
1346 * This clears out all registered spn's for a given hostname
1347 * @param ads An initilaized ADS_STRUCT
1348 * @param machine_name the NetBIOS name of the computer.
1349 * @return 0 upon success, non-zero otherwise.
1352 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1355 LDAPMessage *res = NULL;
1357 const char *servicePrincipalName[1] = {NULL};
1358 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1359 char *dn_string = NULL;
1361 ret = ads_find_machine_acct(ads, (void **)(void *)&res, machine_name);
1362 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1363 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1364 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1365 ads_msgfree(ads, res);
1366 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1369 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1370 ctx = talloc_init("ads_clear_service_principal_names");
1372 ads_msgfree(ads, res);
1373 return ADS_ERROR(LDAP_NO_MEMORY);
1376 if (!(mods = ads_init_mods(ctx))) {
1377 talloc_destroy(ctx);
1378 ads_msgfree(ads, res);
1379 return ADS_ERROR(LDAP_NO_MEMORY);
1381 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1382 if (!ADS_ERR_OK(ret)) {
1383 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1384 ads_msgfree(ads, res);
1385 talloc_destroy(ctx);
1388 dn_string = ads_get_dn(ads, res);
1390 talloc_destroy(ctx);
1391 ads_msgfree(ads, res);
1392 return ADS_ERROR(LDAP_NO_MEMORY);
1394 ret = ads_gen_mod(ads, dn_string, mods);
1395 ads_memfree(ads,dn_string);
1396 if (!ADS_ERR_OK(ret)) {
1397 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1399 ads_msgfree(ads, res);
1400 talloc_destroy(ctx);
1404 ads_msgfree(ads, res);
1405 talloc_destroy(ctx);
1410 * This adds a service principal name to an existing computer account
1411 * (found by hostname) in AD.
1412 * @param ads An initialized ADS_STRUCT
1413 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1414 * @param my_fqdn The fully qualified DNS name of the machine
1415 * @param spn A string of the service principal to add, i.e. 'host'
1416 * @return 0 upon sucess, or non-zero if a failure occurs
1419 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1420 const char *my_fqdn, const char *spn)
1424 LDAPMessage *res = NULL;
1427 char *dn_string = NULL;
1428 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1430 ret = ads_find_machine_acct(ads, (void **)(void *)&res, machine_name);
1431 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1432 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1434 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1435 spn, machine_name, ads->config.realm));
1436 ads_msgfree(ads, res);
1437 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1440 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1441 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1442 ads_msgfree(ads, res);
1443 return ADS_ERROR(LDAP_NO_MEMORY);
1446 /* add short name spn */
1448 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1449 talloc_destroy(ctx);
1450 ads_msgfree(ads, res);
1451 return ADS_ERROR(LDAP_NO_MEMORY);
1454 strlower_m(&psp1[strlen(spn)]);
1455 servicePrincipalName[0] = psp1;
1457 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1458 psp1, machine_name));
1461 /* add fully qualified spn */
1463 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1464 ret = ADS_ERROR(LDAP_NO_MEMORY);
1468 strlower_m(&psp2[strlen(spn)]);
1469 servicePrincipalName[1] = psp2;
1471 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1472 psp2, machine_name));
1474 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1475 ret = ADS_ERROR(LDAP_NO_MEMORY);
1479 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1480 if (!ADS_ERR_OK(ret)) {
1481 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1485 if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1486 ret = ADS_ERROR(LDAP_NO_MEMORY);
1490 ret = ads_gen_mod(ads, dn_string, mods);
1491 ads_memfree(ads,dn_string);
1492 if (!ADS_ERR_OK(ret)) {
1493 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1499 ads_msgfree(ads, res);
1504 * adds a machine account to the ADS server
1505 * @param ads An intialized ADS_STRUCT
1506 * @param machine_name - the NetBIOS machine name of this account.
1507 * @param account_type A number indicating the type of account to create
1508 * @param org_unit The LDAP path in which to place this account
1509 * @return 0 upon success, or non-zero otherwise
1512 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1513 const char *org_unit)
1516 char *samAccountName, *controlstr;
1520 const char *objectClass[] = {"top", "person", "organizationalPerson",
1521 "user", "computer", NULL};
1522 LDAPMessage *res = NULL;
1523 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1524 UF_DONT_EXPIRE_PASSWD |\
1525 UF_ACCOUNTDISABLE );
1527 if (!(ctx = talloc_init("ads_add_machine_acct")))
1528 return ADS_ERROR(LDAP_NO_MEMORY);
1530 ret = ADS_ERROR(LDAP_NO_MEMORY);
1532 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_name, org_unit);
1533 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1535 if ( !new_dn || !samAccountName ) {
1539 #ifndef ENCTYPE_ARCFOUR_HMAC
1540 acct_control |= UF_USE_DES_KEY_ONLY;
1543 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1547 if (!(mods = ads_init_mods(ctx))) {
1551 ads_mod_str(ctx, &mods, "cn", machine_name);
1552 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1553 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1554 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1556 ret = ads_gen_add(ads, new_dn, mods);
1559 ads_msgfree(ads, res);
1560 talloc_destroy(ctx);
1566 dump a binary result from ldap
1568 static void dump_binary(const char *field, struct berval **values)
1571 for (i=0; values[i]; i++) {
1572 printf("%s: ", field);
1573 for (j=0; j<values[i]->bv_len; j++) {
1574 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1580 static void dump_guid(const char *field, struct berval **values)
1584 for (i=0; values[i]; i++) {
1585 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1586 printf("%s: %s\n", field,
1587 smb_uuid_string_static(smb_uuid_unpack_static(guid)));
1592 dump a sid result from ldap
1594 static void dump_sid(const char *field, struct berval **values)
1597 for (i=0; values[i]; i++) {
1599 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1600 printf("%s: %s\n", field, sid_string_static(&sid));
1605 dump ntSecurityDescriptor
1607 static void dump_sd(const char *filed, struct berval **values)
1612 TALLOC_CTX *ctx = 0;
1614 if (!(ctx = talloc_init("sec_io_desc")))
1618 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1619 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1620 prs_set_offset(&ps,0);
1623 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1625 talloc_destroy(ctx);
1628 if (psd) ads_disp_sd(psd);
1631 talloc_destroy(ctx);
1635 dump a string result from ldap
1637 static void dump_string(const char *field, char **values)
1640 for (i=0; values[i]; i++) {
1641 printf("%s: %s\n", field, values[i]);
1646 dump a field from LDAP on stdout
1650 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1655 void (*handler)(const char *, struct berval **);
1657 {"objectGUID", False, dump_guid},
1658 {"netbootGUID", False, dump_guid},
1659 {"nTSecurityDescriptor", False, dump_sd},
1660 {"dnsRecord", False, dump_binary},
1661 {"objectSid", False, dump_sid},
1662 {"tokenGroups", False, dump_sid},
1663 {"tokenGroupsNoGCAcceptable", False, dump_sid},
1664 {"tokengroupsGlobalandUniversal", False, dump_sid},
1669 if (!field) { /* must be end of an entry */
1674 for (i=0; handlers[i].name; i++) {
1675 if (StrCaseCmp(handlers[i].name, field) == 0) {
1676 if (!values) /* first time, indicate string or not */
1677 return handlers[i].string;
1678 handlers[i].handler(field, (struct berval **) values);
1682 if (!handlers[i].name) {
1683 if (!values) /* first time, indicate string conversion */
1685 dump_string(field, (char **)values);
1691 * Dump a result from LDAP on stdout
1692 * used for debugging
1693 * @param ads connection to ads server
1694 * @param res Results to dump
1697 void ads_dump(ADS_STRUCT *ads, void *res)
1699 ads_process_results(ads, res, ads_dump_field, NULL);
1703 * Walk through results, calling a function for each entry found.
1704 * The function receives a field name, a berval * array of values,
1705 * and a data area passed through from the start. The function is
1706 * called once with null for field and values at the end of each
1708 * @param ads connection to ads server
1709 * @param res Results to process
1710 * @param fn Function for processing each result
1711 * @param data_area user-defined area to pass to function
1713 void ads_process_results(ADS_STRUCT *ads, void *res,
1714 BOOL(*fn)(char *, void **, void *),
1720 if (!(ctx = talloc_init("ads_process_results")))
1723 for (msg = ads_first_entry(ads, res); msg;
1724 msg = ads_next_entry(ads, msg)) {
1728 for (utf8_field=ldap_first_attribute(ads->ld,
1729 (LDAPMessage *)msg,&b);
1731 utf8_field=ldap_next_attribute(ads->ld,
1732 (LDAPMessage *)msg,b)) {
1733 struct berval **ber_vals;
1734 char **str_vals, **utf8_vals;
1738 pull_utf8_talloc(ctx, &field, utf8_field);
1739 string = fn(field, NULL, data_area);
1742 utf8_vals = ldap_get_values(ads->ld,
1743 (LDAPMessage *)msg, field);
1744 str_vals = ads_pull_strvals(ctx,
1745 (const char **) utf8_vals);
1746 fn(field, (void **) str_vals, data_area);
1747 ldap_value_free(utf8_vals);
1749 ber_vals = ldap_get_values_len(ads->ld,
1750 (LDAPMessage *)msg, field);
1751 fn(field, (void **) ber_vals, data_area);
1753 ldap_value_free_len(ber_vals);
1755 ldap_memfree(utf8_field);
1758 talloc_free_children(ctx);
1759 fn(NULL, NULL, data_area); /* completed an entry */
1762 talloc_destroy(ctx);
1766 * count how many replies are in a LDAPMessage
1767 * @param ads connection to ads server
1768 * @param res Results to count
1769 * @return number of replies
1771 int ads_count_replies(ADS_STRUCT *ads, void *res)
1773 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1777 * pull the first entry from a ADS result
1778 * @param ads connection to ads server
1779 * @param res Results of search
1780 * @return first entry from result
1782 void *ads_first_entry(ADS_STRUCT *ads, void *res)
1784 return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
1788 * pull the next entry from a ADS result
1789 * @param ads connection to ads server
1790 * @param res Results of search
1791 * @return next entry from result
1793 void *ads_next_entry(ADS_STRUCT *ads, void *res)
1795 return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
1799 * pull a single string from a ADS result
1800 * @param ads connection to ads server
1801 * @param mem_ctx TALLOC_CTX to use for allocating result string
1802 * @param msg Results of search
1803 * @param field Attribute to retrieve
1804 * @return Result string in talloc context
1806 char *ads_pull_string(ADS_STRUCT *ads,
1807 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1814 values = ldap_get_values(ads->ld, msg, field);
1819 rc = pull_utf8_talloc(mem_ctx, &ux_string,
1821 if (rc != (size_t)-1)
1825 ldap_value_free(values);
1830 * pull an array of strings from a ADS result
1831 * @param ads connection to ads server
1832 * @param mem_ctx TALLOC_CTX to use for allocating result string
1833 * @param msg Results of search
1834 * @param field Attribute to retrieve
1835 * @return Result strings in talloc context
1837 char **ads_pull_strings(ADS_STRUCT *ads,
1838 TALLOC_CTX *mem_ctx, void *msg, const char *field,
1845 values = ldap_get_values(ads->ld, msg, field);
1849 *num_values = ldap_count_values(values);
1851 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
1853 ldap_value_free(values);
1857 for (i=0;i<*num_values;i++) {
1858 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
1859 ldap_value_free(values);
1865 ldap_value_free(values);
1870 * pull an array of strings from a ADS result
1871 * (handle large multivalue attributes with range retrieval)
1872 * @param ads connection to ads server
1873 * @param mem_ctx TALLOC_CTX to use for allocating result string
1874 * @param msg Results of search
1875 * @param field Attribute to retrieve
1876 * @param current_strings strings returned by a previous call to this function
1877 * @param next_attribute The next query should ask for this attribute
1878 * @param num_values How many values did we get this time?
1879 * @param more_values Are there more values to get?
1880 * @return Result strings in talloc context
1882 char **ads_pull_strings_range(ADS_STRUCT *ads,
1883 TALLOC_CTX *mem_ctx,
1884 void *msg, const char *field,
1885 char **current_strings,
1886 const char **next_attribute,
1887 size_t *num_strings,
1891 char *expected_range_attrib, *range_attr;
1892 BerElement *ptr = NULL;
1895 size_t num_new_strings;
1896 unsigned long int range_start;
1897 unsigned long int range_end;
1899 /* we might have been given the whole lot anyway */
1900 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
1901 *more_strings = False;
1905 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
1907 /* look for Range result */
1908 for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr);
1910 attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
1911 /* we ignore the fact that this is utf8, as all attributes are ascii... */
1912 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
1920 /* nothing here - this field is just empty */
1921 *more_strings = False;
1925 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
1926 &range_start, &range_end) == 2) {
1927 *more_strings = True;
1929 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
1930 &range_start) == 1) {
1931 *more_strings = False;
1933 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
1935 ldap_memfree(range_attr);
1936 *more_strings = False;
1941 if ((*num_strings) != range_start) {
1942 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
1943 " - aborting range retreival\n",
1944 range_attr, (unsigned int)(*num_strings) + 1, range_start));
1945 ldap_memfree(range_attr);
1946 *more_strings = False;
1950 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
1952 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
1953 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
1954 "strings in this bunch, but we only got %lu - aborting range retreival\n",
1955 range_attr, (unsigned long int)range_end - range_start + 1,
1956 (unsigned long int)num_new_strings));
1957 ldap_memfree(range_attr);
1958 *more_strings = False;
1962 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
1963 *num_strings + num_new_strings);
1965 if (strings == NULL) {
1966 ldap_memfree(range_attr);
1967 *more_strings = False;
1971 if (new_strings && num_new_strings) {
1972 memcpy(&strings[*num_strings], new_strings,
1973 sizeof(*new_strings) * num_new_strings);
1976 (*num_strings) += num_new_strings;
1978 if (*more_strings) {
1979 *next_attribute = talloc_asprintf(mem_ctx,
1984 if (!*next_attribute) {
1985 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
1986 ldap_memfree(range_attr);
1987 *more_strings = False;
1992 ldap_memfree(range_attr);
1998 * pull a single uint32 from a ADS result
1999 * @param ads connection to ads server
2000 * @param msg Results of search
2001 * @param field Attribute to retrieve
2002 * @param v Pointer to int to store result
2003 * @return boolean inidicating success
2005 BOOL ads_pull_uint32(ADS_STRUCT *ads,
2006 void *msg, const char *field, uint32 *v)
2010 values = ldap_get_values(ads->ld, msg, field);
2014 ldap_value_free(values);
2018 *v = atoi(values[0]);
2019 ldap_value_free(values);
2024 * pull a single objectGUID from an ADS result
2025 * @param ads connection to ADS server
2026 * @param msg results of search
2027 * @param guid 37-byte area to receive text guid
2028 * @return boolean indicating success
2030 BOOL ads_pull_guid(ADS_STRUCT *ads,
2031 void *msg, struct uuid *guid)
2034 UUID_FLAT flat_guid;
2036 values = ldap_get_values(ads->ld, msg, "objectGUID");
2041 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2042 smb_uuid_unpack(flat_guid, guid);
2043 ldap_value_free(values);
2046 ldap_value_free(values);
2053 * pull a single DOM_SID from a ADS result
2054 * @param ads connection to ads server
2055 * @param msg Results of search
2056 * @param field Attribute to retrieve
2057 * @param sid Pointer to sid to store result
2058 * @return boolean inidicating success
2060 BOOL ads_pull_sid(ADS_STRUCT *ads,
2061 void *msg, const char *field, DOM_SID *sid)
2063 struct berval **values;
2066 values = ldap_get_values_len(ads->ld, msg, field);
2072 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2074 ldap_value_free_len(values);
2079 * pull an array of DOM_SIDs from a ADS result
2080 * @param ads connection to ads server
2081 * @param mem_ctx TALLOC_CTX for allocating sid array
2082 * @param msg Results of search
2083 * @param field Attribute to retrieve
2084 * @param sids pointer to sid array to allocate
2085 * @return the count of SIDs pulled
2087 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2088 void *msg, const char *field, DOM_SID **sids)
2090 struct berval **values;
2094 values = ldap_get_values_len(ads->ld, msg, field);
2099 for (i=0; values[i]; i++)
2102 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2104 ldap_value_free_len(values);
2109 for (i=0; values[i]; i++) {
2110 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2113 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2118 ldap_value_free_len(values);
2123 * pull a SEC_DESC from a ADS result
2124 * @param ads connection to ads server
2125 * @param mem_ctx TALLOC_CTX for allocating sid array
2126 * @param msg Results of search
2127 * @param field Attribute to retrieve
2128 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2129 * @return boolean inidicating success
2131 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2132 void *msg, const char *field, SEC_DESC **sd)
2134 struct berval **values;
2138 values = ldap_get_values_len(ads->ld, msg, field);
2140 if (!values) return False;
2143 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2144 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2145 prs_set_offset(&ps,0);
2147 ret = sec_io_desc("sd", sd, &ps, 1);
2150 ldap_value_free_len(values);
2155 * in order to support usernames longer than 21 characters we need to
2156 * use both the sAMAccountName and the userPrincipalName attributes
2157 * It seems that not all users have the userPrincipalName attribute set
2159 * @param ads connection to ads server
2160 * @param mem_ctx TALLOC_CTX for allocating sid array
2161 * @param msg Results of search
2162 * @return the username
2164 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
2169 /* lookup_name() only works on the sAMAccountName to
2170 returning the username portion of userPrincipalName
2171 breaks winbindd_getpwnam() */
2173 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2174 if (ret && (p = strchr_m(ret, '@'))) {
2179 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2184 * find the update serial number - this is the core of the ldap cache
2185 * @param ads connection to ads server
2186 * @param ads connection to ADS server
2187 * @param usn Pointer to retrieved update serial number
2188 * @return status of search
2190 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2192 const char *attrs[] = {"highestCommittedUSN", NULL};
2196 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2197 if (!ADS_ERR_OK(status))
2200 if (ads_count_replies(ads, res) != 1) {
2201 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2204 ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
2205 ads_msgfree(ads, res);
2209 /* parse a ADS timestring - typical string is
2210 '20020917091222.0Z0' which means 09:12.22 17th September
2212 static time_t ads_parse_time(const char *str)
2218 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2219 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2220 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2229 /********************************************************************
2230 ********************************************************************/
2232 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2234 const char *attrs[] = {"currentTime", NULL};
2239 ADS_STRUCT *ads_s = ads;
2241 if (!(ctx = talloc_init("ads_current_time"))) {
2242 return ADS_ERROR(LDAP_NO_MEMORY);
2245 /* establish a new ldap tcp session if necessary */
2248 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2249 ads->server.ldap_server )) == NULL )
2253 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2254 status = ads_connect( ads_s );
2255 if ( !ADS_ERR_OK(status))
2259 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2260 if (!ADS_ERR_OK(status)) {
2264 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2266 ads_msgfree(ads, res);
2267 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2271 /* but save the time and offset in the original ADS_STRUCT */
2273 ads->config.current_time = ads_parse_time(timestr);
2275 if (ads->config.current_time != 0) {
2276 ads->auth.time_offset = ads->config.current_time - time(NULL);
2277 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2280 ads_msgfree(ads, res);
2282 status = ADS_SUCCESS;
2285 /* free any temporary ads connections */
2286 if ( ads_s != ads ) {
2287 ads_destroy( &ads_s );
2289 talloc_destroy(ctx);
2294 /********************************************************************
2295 ********************************************************************/
2297 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2299 const char *attrs[] = {"domainFunctionality", NULL};
2302 ADS_STRUCT *ads_s = ads;
2304 *val = DS_DOMAIN_FUNCTION_2000;
2306 /* establish a new ldap tcp session if necessary */
2309 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2310 ads->server.ldap_server )) == NULL )
2314 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2315 status = ads_connect( ads_s );
2316 if ( !ADS_ERR_OK(status))
2320 /* If the attribute does not exist assume it is a Windows 2000
2321 functional domain */
2323 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2324 if (!ADS_ERR_OK(status)) {
2325 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2326 status = ADS_SUCCESS;
2331 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2332 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2334 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2337 ads_msgfree(ads, res);
2340 /* free any temporary ads connections */
2341 if ( ads_s != ads ) {
2342 ads_destroy( &ads_s );
2349 * find the domain sid for our domain
2350 * @param ads connection to ads server
2351 * @param sid Pointer to domain sid
2352 * @return status of search
2354 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2356 const char *attrs[] = {"objectSid", NULL};
2360 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2362 if (!ADS_ERR_OK(rc)) return rc;
2363 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2364 ads_msgfree(ads, res);
2365 return ADS_ERROR_SYSTEM(ENOENT);
2367 ads_msgfree(ads, res);
2373 * find our site name
2374 * @param ads connection to ads server
2375 * @param mem_ctx Pointer to talloc context
2376 * @param site_name Pointer to the sitename
2377 * @return status of search
2379 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2383 const char *dn, *service_name;
2384 const char *attrs[] = { "dsServiceName", NULL };
2386 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2387 if (!ADS_ERR_OK(status)) {
2391 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2392 if (service_name == NULL) {
2393 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2396 /* go up three levels */
2397 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2399 return ADS_ERROR(LDAP_NO_MEMORY);
2402 *site_name = talloc_strdup(mem_ctx, dn);
2403 if (*site_name == NULL) {
2404 return ADS_ERROR(LDAP_NO_MEMORY);
2407 ads_msgfree(ads, res);
2411 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2416 * find the site dn where a machine resides
2417 * @param ads connection to ads server
2418 * @param mem_ctx Pointer to talloc context
2419 * @param computer_name name of the machine
2420 * @param site_name Pointer to the sitename
2421 * @return status of search
2423 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2427 const char *parent, *config_context, *filter;
2428 const char *attrs[] = { "configurationNamingContext", NULL };
2431 /* shortcut a query */
2432 if (strequal(computer_name, ads->config.ldap_server_name)) {
2433 return ads_site_dn(ads, mem_ctx, site_dn);
2436 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2437 if (!ADS_ERR_OK(status)) {
2441 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2442 if (config_context == NULL) {
2443 return ADS_ERROR(LDAP_NO_MEMORY);
2446 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2447 if (filter == NULL) {
2448 return ADS_ERROR(LDAP_NO_MEMORY);
2451 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, filter, NULL, &res);
2452 if (!ADS_ERR_OK(status)) {
2456 if (ads_count_replies(ads, res) != 1) {
2457 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2460 dn = ads_get_dn(ads, res);
2462 return ADS_ERROR(LDAP_NO_MEMORY);
2465 /* go up three levels */
2466 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2467 if (parent == NULL) {
2468 ads_memfree(ads, dn);
2469 return ADS_ERROR(LDAP_NO_MEMORY);
2472 *site_dn = talloc_strdup(mem_ctx, parent);
2473 if (*site_dn == NULL) {
2474 ads_memfree(ads, dn);
2475 ADS_ERROR(LDAP_NO_MEMORY);
2478 ads_memfree(ads, dn);
2479 ads_msgfree(ads, res);
2485 * get the upn suffixes for a domain
2486 * @param ads connection to ads server
2487 * @param mem_ctx Pointer to talloc context
2488 * @param suffixes Pointer to an array of suffixes
2489 * @param site_name Pointer to the number of suffixes
2490 * @return status of search
2492 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char **suffixes, size_t *num_suffixes)
2496 const char *config_context, *base;
2497 const char *attrs[] = { "configurationNamingContext", NULL };
2498 const char *attrs2[] = { "uPNSuffixes", NULL };
2500 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2501 if (!ADS_ERR_OK(status)) {
2505 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2506 if (config_context == NULL) {
2507 return ADS_ERROR(LDAP_NO_MEMORY);
2510 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2512 return ADS_ERROR(LDAP_NO_MEMORY);
2515 status = ads_search_dn(ads, &res, base, attrs2);
2516 if (!ADS_ERR_OK(status)) {
2520 if (ads_count_replies(ads, res) != 1) {
2521 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2524 suffixes = ads_pull_strings(ads, mem_ctx, &res, "uPNSuffixes", num_suffixes);
2525 if (suffixes == NULL) {
2526 ads_msgfree(ads, res);
2527 return ADS_ERROR(LDAP_NO_MEMORY);
2530 ads_msgfree(ads, res);
2536 * pull a DOM_SID from an extended dn string
2537 * @param mem_ctx TALLOC_CTX
2538 * @param flags string type of extended_dn
2539 * @param sid pointer to a DOM_SID
2540 * @return boolean inidicating success
2542 BOOL ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
2544 enum ads_extended_dn_flags flags,
2554 * ADS_EXTENDED_DN_HEX_STRING:
2555 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2557 * ADS_EXTENDED_DN_STRING (only with w2k3):
2558 <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
2561 p = strchr(dn, ';');
2566 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
2570 p += strlen(";<SID=");
2579 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
2583 case ADS_EXTENDED_DN_STRING:
2584 if (!string_to_sid(sid, p)) {
2588 case ADS_EXTENDED_DN_HEX_STRING: {
2592 buf_len = strhex_to_str(buf, strlen(p), p);
2597 if (!sid_parse(buf, buf_len, sid)) {
2598 DEBUG(10,("failed to parse sid\n"));
2604 DEBUG(10,("unknown extended dn format\n"));
2612 * pull an array of DOM_SIDs from a ADS result
2613 * @param ads connection to ads server
2614 * @param mem_ctx TALLOC_CTX for allocating sid array
2615 * @param msg Results of search
2616 * @param field Attribute to retrieve
2617 * @param flags string type of extended_dn
2618 * @param sids pointer to sid array to allocate
2619 * @return the count of SIDs pulled
2621 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
2622 TALLOC_CTX *mem_ctx,
2625 enum ads_extended_dn_flags flags,
2632 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
2633 &dn_count)) == NULL) {
2637 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
2639 TALLOC_FREE(dn_strings);
2643 for (i=0; i<dn_count; i++) {
2645 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
2646 flags, &(*sids)[i])) {
2648 TALLOC_FREE(dn_strings);
2653 TALLOC_FREE(dn_strings);
2658 /********************************************************************
2659 ********************************************************************/
2661 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2663 LDAPMessage *res = NULL;
2668 status = ads_find_machine_acct(ads, (void **)(void *)&res, global_myname());
2669 if (!ADS_ERR_OK(status)) {
2670 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2675 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2676 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2680 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
2681 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
2685 ads_msgfree(ads, res);
2690 /********************************************************************
2691 ********************************************************************/
2693 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2695 LDAPMessage *res = NULL;
2700 status = ads_find_machine_acct(ads, (void **)(void *)&res, global_myname());
2701 if (!ADS_ERR_OK(status)) {
2702 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
2707 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2708 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
2712 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
2713 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
2717 ads_msgfree(ads, res);
2722 /********************************************************************
2723 ********************************************************************/
2725 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2727 LDAPMessage *res = NULL;
2732 status = ads_find_machine_acct(ads, (void **)(void *)&res, global_myname());
2733 if (!ADS_ERR_OK(status)) {
2734 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2739 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2740 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2744 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
2745 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
2749 ads_msgfree(ads, res);