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 2002
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 * @brief basic ldap client-side routines for ads server communications
31 * The routines contained here should do the necessary ldap calls for
34 * Important note: attribute names passed into ads_ routines must
35 * already be in UTF-8 format. We do not convert them because in almost
36 * all cases, they are just ascii (which is represented with the same
37 * codepoints in UTF-8). This may have to change at some point
42 try a connection to a given ldap server, returning True and setting the servers IP
43 in the ads struct if successful
45 static BOOL ads_try_connect(ADS_STRUCT *ads, const char *server, unsigned port)
49 if (!server || !*server) {
53 DEBUG(5,("ads_try_connect: trying ldap server '%s' port %u\n", server, port));
55 /* this copes with inet_ntoa brokenness */
58 ads->ld = ldap_open(srv, port);
63 ads->ldap_port = port;
64 ads->ldap_ip = *interpret_addr2(srv);
71 try a connection to a given ldap server, based on URL, returning True if successful
73 static BOOL ads_try_connect_uri(ADS_STRUCT *ads)
75 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
76 DEBUG(5,("ads_try_connect: trying ldap server at URI '%s'\n",
77 ads->server.ldap_uri));
80 if (ldap_initialize((LDAP**)&(ads->ld), ads->server.ldap_uri) == LDAP_SUCCESS) {
83 DEBUG(0, ("ldap_initialize: %s\n", strerror(errno)));
87 DEBUG(1, ("no URL support in LDAP libs!\n"));
93 /* used by the IP comparison function */
99 /* compare 2 ldap IPs by nearness to our interfaces - used in qsort */
100 static int ldap_ip_compare(struct ldap_ip *ip1, struct ldap_ip *ip2)
102 return ip_compare(&ip1->ip, &ip2->ip);
105 /* try connecting to a ldap server via DNS */
106 static BOOL ads_try_dns(ADS_STRUCT *ads)
111 struct ldap_ip *ip_list;
114 realm = ads->server.realm;
115 if (!realm || !*realm) {
118 if (!realm || !*realm) {
119 realm = ads->server.workgroup;
121 if (!realm || !*realm) {
122 realm = lp_workgroup();
127 realm = smb_xstrdup(realm);
129 DEBUG(6,("ads_try_dns: looking for realm '%s'\n", realm));
130 if (ldap_domain2hostlist(realm, &list) != LDAP_SUCCESS) {
135 DEBUG(6,("ads_try_dns: ldap realm '%s' host list '%s'\n", realm, list));
138 count = count_chars(list, ' ') + 1;
139 ip_list = malloc(count * sizeof(struct ldap_ip));
145 while (next_token(&ptr, tok, " ", sizeof(tok))) {
146 unsigned port = LDAP_PORT;
147 char *p = strchr(tok, ':');
152 ip_list[i].ip = *interpret_addr2(tok);
153 ip_list[i].port = port;
154 if (!is_zero_ip(ip_list[i].ip)) {
162 /* we sort the list of addresses by closeness to our interfaces. This
163 tries to prevent us using a DC on the other side of the country */
165 qsort(ip_list, count, sizeof(struct ldap_ip),
166 QSORT_CAST ldap_ip_compare);
169 for (i=0;i<count;i++) {
170 if (ads_try_connect(ads, inet_ntoa(ip_list[i].ip), ip_list[i].port)) {
180 /* try connecting to a ldap server via netbios */
181 static BOOL ads_try_netbios(ADS_STRUCT *ads)
183 struct in_addr *ip_list;
186 char *workgroup = ads->server.workgroup;
189 workgroup = lp_workgroup();
192 DEBUG(6,("ads_try_netbios: looking for workgroup '%s'\n", workgroup));
194 /* try the PDC first */
195 if (get_dc_list(True, workgroup, &ip_list, &count)) {
196 for (i=0;i<count;i++) {
197 DEBUG(6,("ads_try_netbios: trying server '%s'\n",
198 inet_ntoa(ip_list[i])));
199 if (ads_try_connect(ads, inet_ntoa(ip_list[i]), LDAP_PORT)) {
207 /* now any DC, including backups */
208 if (get_dc_list(False, workgroup, &ip_list, &count)) {
209 for (i=0;i<count;i++) {
210 DEBUG(6,("ads_try_netbios: trying server '%s'\n",
211 inet_ntoa(ip_list[i])));
212 if (ads_try_connect(ads, inet_ntoa(ip_list[i]), LDAP_PORT)) {
224 * Connect to the LDAP server
225 * @param ads Pointer to an existing ADS_STRUCT
226 * @return status of connection
228 ADS_STATUS ads_connect(ADS_STRUCT *ads)
230 int version = LDAP_VERSION3;
233 ads->last_attempt = time(NULL);
236 /* try with a URL based server */
238 if (ads->server.ldap_uri &&
239 ads_try_connect_uri(ads)) {
243 /* try with a user specified server */
244 if (ads->server.ldap_server &&
245 ads_try_connect(ads, ads->server.ldap_server, LDAP_PORT)) {
249 /* try with a smb.conf ads server setting if we are connecting
250 to the primary workgroup or realm */
251 if (!ads->server.foreign &&
252 ads_try_connect(ads, lp_ads_server(), LDAP_PORT)) {
257 if (ads_try_dns(ads)) {
261 /* try via netbios lookups */
262 if (!lp_disable_netbios() && ads_try_netbios(ads)) {
266 return ADS_ERROR_SYSTEM(errno?errno:ENOENT);
269 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
271 status = ads_server_info(ads);
272 if (!ADS_ERR_OK(status)) {
273 DEBUG(1,("Failed to get ldap server info\n"));
277 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
279 if (!ads->auth.user_name) {
280 /* by default use the machine account */
281 extern pstring global_myname;
283 fstrcpy(myname, global_myname);
285 asprintf(&ads->auth.user_name, "HOST/%s", myname);
288 if (!ads->auth.realm) {
289 ads->auth.realm = strdup(ads->config.realm);
292 if (!ads->auth.kdc_server) {
293 ads->auth.kdc_server = strdup(inet_ntoa(ads->ldap_ip));
297 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
298 to MIT kerberos to work (tridge) */
301 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
302 setenv(env, ads->auth.kdc_server, 1);
307 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
311 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
312 return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
315 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
316 return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
319 return ads_sasl_bind(ads);
323 Duplicate a struct berval into talloc'ed memory
325 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
327 struct berval *value;
329 if (!in_val) return NULL;
331 value = talloc_zero(ctx, sizeof(struct berval));
332 if (in_val->bv_len == 0) return value;
334 value->bv_len = in_val->bv_len;
335 value->bv_val = talloc_memdup(ctx, in_val->bv_val, in_val->bv_len);
340 Make a values list out of an array of (struct berval *)
342 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
343 const struct berval **in_vals)
345 struct berval **values;
348 if (!in_vals) return NULL;
349 for (i=0; in_vals[i]; i++); /* count values */
350 values = (struct berval **) talloc_zero(ctx,
351 (i+1)*sizeof(struct berval *));
352 if (!values) return NULL;
354 for (i=0; in_vals[i]; i++) {
355 values[i] = dup_berval(ctx, in_vals[i]);
361 UTF8-encode a values list out of an array of (char *)
363 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
368 if (!in_vals) return NULL;
369 for (i=0; in_vals[i]; i++); /* count values */
370 values = (char ** ) talloc_zero(ctx, (i+1)*sizeof(char *));
371 if (!values) return NULL;
373 for (i=0; in_vals[i]; i++) {
374 push_utf8_talloc(ctx, &values[i], in_vals[i]);
380 Pull a (char *) array out of a UTF8-encoded values list
382 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
387 if (!in_vals) return NULL;
388 for (i=0; in_vals[i]; i++); /* count values */
389 values = (char **) talloc_zero(ctx, (i+1)*sizeof(char *));
390 if (!values) return NULL;
392 for (i=0; in_vals[i]; i++) {
393 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
399 * Do a search with paged results. cookie must be null on the first
400 * call, and then returned on each subsequent call. It will be null
401 * again when the entire search is complete
402 * @param ads connection to ads server
403 * @param bind_path Base dn for the search
404 * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
405 * @param exp Search expression - specified in local charset
406 * @param attrs Attributes to retrieve - specified in utf8 or ascii
407 * @param res ** which will contain results - free res* with ads_msgfree()
408 * @param count Number of entries retrieved on this page
409 * @param cookie The paged results cookie to be returned on subsequent calls
410 * @return status of search
412 ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
413 int scope, const char *exp,
414 const char **attrs, void **res,
415 int *count, void **cookie)
418 char *utf8_exp, *utf8_path, **search_attrs;
419 LDAPControl PagedResults, NoReferrals, *controls[3], **rcontrols;
420 BerElement *cookie_be = NULL;
421 struct berval *cookie_bv= NULL;
426 if (!(ctx = talloc_init()))
427 return ADS_ERROR(LDAP_NO_MEMORY);
429 /* 0 means the conversion worked but the result was empty
430 so we only fail if it's negative. In any case, it always
431 at least nulls out the dest */
432 if ((push_utf8_talloc(ctx, &utf8_exp, exp) < 0) ||
433 (push_utf8_talloc(ctx, &utf8_path, bind_path) < 0)) {
438 if (!attrs || !(*attrs))
441 /* This would be the utf8-encoded version...*/
442 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
443 if (!(str_list_copy(&search_attrs, attrs)))
451 /* Paged results only available on ldap v3 or later */
452 ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
453 if (version < LDAP_VERSION3) {
454 rc = LDAP_NOT_SUPPORTED;
458 cookie_be = ber_alloc_t(LBER_USE_DER);
459 if (cookie && *cookie) {
460 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
461 ber_bvfree(*cookie); /* don't need it from last time */
464 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
466 ber_flatten(cookie_be, &cookie_bv);
467 PagedResults.ldctl_oid = ADS_PAGE_CTL_OID;
468 PagedResults.ldctl_iscritical = (char) 1;
469 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
470 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
472 NoReferrals.ldctl_oid = ADS_NO_REFERRALS_OID;
473 NoReferrals.ldctl_iscritical = (char) 0;
474 NoReferrals.ldctl_value.bv_len = 0;
475 NoReferrals.ldctl_value.bv_val = "";
478 controls[0] = &NoReferrals;
479 controls[1] = &PagedResults;
484 /* we need to disable referrals as the openldap libs don't
485 handle them and paged results at the same time. Using them
486 together results in the result record containing the server
487 page control being removed from the result list (tridge/jmcd)
489 leaving this in despite the control that says don't generate
490 referrals, in case the server doesn't support it (jmcd)
492 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
494 rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_exp,
495 search_attrs, 0, controls,
496 NULL, NULL, LDAP_NO_LIMIT, (LDAPMessage **)res);
498 ber_free(cookie_be, 1);
499 ber_bvfree(cookie_bv);
502 DEBUG(3,("ldap_search_ext_s(%s) -> %s\n", exp, ldap_err2string(rc)));
506 rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
507 NULL, &rcontrols, 0);
513 for (i=0; rcontrols[i]; i++) {
514 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
515 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
516 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
518 /* the berval is the cookie, but must be freed when
520 if (cookie_bv->bv_len) /* still more to do */
521 *cookie=ber_bvdup(cookie_bv);
524 ber_bvfree(cookie_bv);
525 ber_free(cookie_be, 1);
529 ldap_controls_free(rcontrols);
533 /* if/when we decide to utf8-encode attrs, take out this next line */
534 str_list_free(&search_attrs);
536 return ADS_ERROR(rc);
541 * Get all results for a search. This uses ads_do_paged_search() to return
542 * all entries in a large search.
543 * @param ads connection to ads server
544 * @param bind_path Base dn for the search
545 * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
546 * @param exp Search expression
547 * @param attrs Attributes to retrieve
548 * @param res ** which will contain results - free res* with ads_msgfree()
549 * @return status of search
551 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
552 int scope, const char *exp,
553 const char **attrs, void **res)
559 status = ads_do_paged_search(ads, bind_path, scope, exp, attrs, res,
562 if (!ADS_ERR_OK(status)) return status;
567 LDAPMessage *msg, *next;
569 status2 = ads_do_paged_search(ads, bind_path, scope, exp,
570 attrs, &res2, &count, &cookie);
572 if (!ADS_ERR_OK(status2)) break;
574 /* this relies on the way that ldap_add_result_entry() works internally. I hope
575 that this works on all ldap libs, but I have only tested with openldap */
576 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
577 next = ads_next_entry(ads, msg);
578 ldap_add_result_entry((LDAPMessage **)res, msg);
580 /* note that we do not free res2, as the memory is now
581 part of the main returned list */
588 * Run a function on all results for a search. Uses ads_do_paged_search() and
589 * runs the function as each page is returned, using ads_process_results()
590 * @param ads connection to ads server
591 * @param bind_path Base dn for the search
592 * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
593 * @param exp Search expression - specified in local charset
594 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
595 * @param fn Function which takes attr name, values list, and data_area
596 * @param data_area Pointer which is passed to function on each call
597 * @return status of search
599 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
600 int scope, const char *exp, const char **attrs,
601 BOOL(*fn)(char *, void **, void *),
609 status = ads_do_paged_search(ads, bind_path, scope, exp, attrs, &res,
612 if (!ADS_ERR_OK(status)) return status;
614 ads_process_results(ads, res, fn, data_area);
615 ads_msgfree(ads, res);
618 status = ads_do_paged_search(ads, bind_path, scope, exp, attrs,
619 &res, &count, &cookie);
621 if (!ADS_ERR_OK(status)) break;
623 ads_process_results(ads, res, fn, data_area);
624 ads_msgfree(ads, res);
631 * Do a search with a timeout.
632 * @param ads connection to ads server
633 * @param bind_path Base dn for the search
634 * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
635 * @param exp Search expression
636 * @param attrs Attributes to retrieve
637 * @param res ** which will contain results - free res* with ads_msgfree()
638 * @return status of search
640 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
642 const char **attrs, void **res)
644 struct timeval timeout;
646 char *utf8_exp, *utf8_path, **search_attrs = NULL;
649 if (!(ctx = talloc_init())) {
650 DEBUG(1,("ads_do_search: talloc_init() failed!"));
651 return ADS_ERROR(LDAP_NO_MEMORY);
654 /* 0 means the conversion worked but the result was empty
655 so we only fail if it's negative. In any case, it always
656 at least nulls out the dest */
657 if ((push_utf8_talloc(ctx, &utf8_exp, exp) < 0) ||
658 (push_utf8_talloc(ctx, &utf8_path, bind_path) < 0)) {
659 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
664 if (!attrs || !(*attrs))
667 /* This would be the utf8-encoded version...*/
668 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
669 if (!(str_list_copy(&search_attrs, attrs)))
671 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
677 timeout.tv_sec = ADS_SEARCH_TIMEOUT;
681 /* see the note in ads_do_paged_search - we *must* disable referrals */
682 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
684 rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_exp,
685 search_attrs, 0, NULL, NULL,
686 &timeout, LDAP_NO_LIMIT, (LDAPMessage **)res);
688 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
689 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
695 /* if/when we decide to utf8-encode attrs, take out this next line */
696 str_list_free(&search_attrs);
697 return ADS_ERROR(rc);
700 * Do a general ADS search
701 * @param ads connection to ads server
702 * @param res ** which will contain results - free res* with ads_msgfree()
703 * @param exp Search expression
704 * @param attrs Attributes to retrieve
705 * @return status of search
707 ADS_STATUS ads_search(ADS_STRUCT *ads, void **res,
711 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
716 * Do a search on a specific DistinguishedName
717 * @param ads connection to ads server
718 * @param res ** which will contain results - free res* with ads_msgfree()
719 * @param dn DistinguishName to search
720 * @param attrs Attributes to retrieve
721 * @return status of search
723 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, void **res,
727 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res);
731 * Free up memory from a ads_search
732 * @param ads connection to ads server
733 * @param msg Search results to free
735 void ads_msgfree(ADS_STRUCT *ads, void *msg)
742 * Free up memory from various ads requests
743 * @param ads connection to ads server
744 * @param mem Area to free
746 void ads_memfree(ADS_STRUCT *ads, void *mem)
752 * Get a dn from search results
753 * @param ads connection to ads server
754 * @param res Search results
757 char *ads_get_dn(ADS_STRUCT *ads, void *res)
759 char *utf8_dn, *unix_dn;
761 utf8_dn = ldap_get_dn(ads->ld, res);
762 pull_utf8_allocate((void **) &unix_dn, utf8_dn);
763 ldap_memfree(utf8_dn);
768 * Find a machine account given a hostname
769 * @param ads connection to ads server
770 * @param res ** which will contain results - free res* with ads_msgfree()
771 * @param host Hostname to search for
772 * @return status of search
774 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *host)
778 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
780 /* the easiest way to find a machine account anywhere in the tree
781 is to look for hostname$ */
782 if (asprintf(&exp, "(samAccountName=%s$)", host) == -1) {
783 DEBUG(1, ("asprintf failed!\n"));
784 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
787 status = ads_search(ads, res, exp, attrs);
793 * Initialize a list of mods to be used in a modify request
794 * @param ctx An initialized TALLOC_CTX
795 * @return allocated ADS_MODLIST
797 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
799 #define ADS_MODLIST_ALLOC_SIZE 10
802 if ((mods = (LDAPMod **) talloc_zero(ctx, sizeof(LDAPMod *) *
803 (ADS_MODLIST_ALLOC_SIZE + 1))))
804 /* -1 is safety to make sure we don't go over the end.
805 need to reset it to NULL before doing ldap modify */
806 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
813 add an attribute to the list, with values list already constructed
815 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
816 int mod_op, const char *name,
820 LDAPMod **modlist = (LDAPMod **) *mods;
825 mod_op = LDAP_MOD_DELETE;
827 if (mod_op & LDAP_MOD_BVALUES)
828 values = (void **) ads_dup_values(ctx,
829 (const struct berval **)invals);
831 values = (void **) ads_push_strvals(ctx,
832 (const char **) invals);
835 /* find the first empty slot */
836 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
838 if (modlist[curmod] == (LDAPMod *) -1) {
839 if (!(modlist = talloc_realloc(ctx, modlist,
840 (curmod+ADS_MODLIST_ALLOC_SIZE+1)*sizeof(LDAPMod *))))
841 return ADS_ERROR(LDAP_NO_MEMORY);
842 memset(&modlist[curmod], 0,
843 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
844 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
848 if (!(modlist[curmod] = talloc_zero(ctx, sizeof(LDAPMod))))
849 return ADS_ERROR(LDAP_NO_MEMORY);
850 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
851 if (mod_op & LDAP_MOD_BVALUES)
852 modlist[curmod]->mod_bvalues = (struct berval **) values;
854 modlist[curmod]->mod_values = (char **) values;
855 modlist[curmod]->mod_op = mod_op;
856 return ADS_ERROR(LDAP_SUCCESS);
860 * Add a single string value to a mod list
861 * @param ctx An initialized TALLOC_CTX
862 * @param mods An initialized ADS_MODLIST
863 * @param name The attribute name to add
864 * @param val The value to add - NULL means DELETE
865 * @return ADS STATUS indicating success of add
867 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
868 const char *name, const char *val)
870 const char *values[2];
876 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
877 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name,
878 (const void **) values);
882 * Add an array of string values to a mod list
883 * @param ctx An initialized TALLOC_CTX
884 * @param mods An initialized ADS_MODLIST
885 * @param name The attribute name to add
886 * @param vals The array of string values to add - NULL means DELETE
887 * @return ADS STATUS indicating success of add
889 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
890 const char *name, const char **vals)
893 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
894 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
895 name, (const void **) vals);
899 * Add a single ber-encoded value to a mod list
900 * @param ctx An initialized TALLOC_CTX
901 * @param mods An initialized ADS_MODLIST
902 * @param name The attribute name to add
903 * @param val The value to add - NULL means DELETE
904 * @return ADS STATUS indicating success of add
906 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
907 const char *name, const struct berval *val)
909 const struct berval *values[2];
914 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
915 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
916 name, (const void **) values);
920 * Perform an ldap modify
921 * @param ads connection to ads server
922 * @param mod_dn DistinguishedName to modify
923 * @param mods list of modifications to perform
924 * @return status of modify
926 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
929 char *utf8_dn = NULL;
931 this control is needed to modify that contains a currently
932 non-existent attribute (but allowable for the object) to run
934 LDAPControl PermitModify = {
935 ADS_PERMIT_MODIFY_OID,
938 LDAPControl *controls[2];
940 controls[0] = &PermitModify;
943 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
944 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
947 /* find the end of the list, marked by NULL or -1 */
948 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
949 /* make sure the end of the list is NULL */
951 ret = ldap_modify_ext_s(ads->ld, utf8_dn,
952 (LDAPMod **) mods, controls, NULL);
954 return ADS_ERROR(ret);
958 * Perform an ldap add
959 * @param ads connection to ads server
960 * @param new_dn DistinguishedName to add
961 * @param mods list of attributes and values for DN
962 * @return status of add
964 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
967 char *utf8_dn = NULL;
969 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
970 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
971 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
974 /* find the end of the list, marked by NULL or -1 */
975 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
976 /* make sure the end of the list is NULL */
979 ret = ldap_add_s(ads->ld, utf8_dn ? utf8_dn : new_dn, mods);
981 return ADS_ERROR(ret);
985 * Delete a DistinguishedName
986 * @param ads connection to ads server
987 * @param new_dn DistinguishedName to delete
988 * @return status of delete
990 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
993 char *utf8_dn = NULL;
994 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
995 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
996 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
999 ret = ldap_delete(ads->ld, utf8_dn ? utf8_dn : del_dn);
1000 return ADS_ERROR(ret);
1004 * Build an org unit string
1005 * if org unit is Computers or blank then assume a container, otherwise
1006 * assume a \ separated list of organisational units
1007 * @param org_unit Organizational unit
1008 * @return org unit string - caller must free
1010 char *ads_ou_string(const char *org_unit)
1012 if (!org_unit || !*org_unit || strcasecmp(org_unit, "Computers") == 0) {
1013 return strdup("cn=Computers");
1016 return ads_build_path(org_unit, "\\/", "ou=", 1);
1022 add a machine account to the ADS server
1024 static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *hostname,
1025 const char *org_unit)
1028 char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
1032 const char *objectClass[] = {"top", "person", "organizationalPerson",
1033 "user", "computer", NULL};
1035 if (!(ctx = talloc_init_named("machine_account")))
1036 return ADS_ERROR(LDAP_NO_MEMORY);
1038 ret = ADS_ERROR(LDAP_NO_MEMORY);
1040 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", hostname)))
1042 if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm)))
1044 ou_str = ads_ou_string(org_unit);
1046 DEBUG(1, ("ads_ou_string returned NULL (malloc failure?)\n"));
1049 new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", hostname, ou_str,
1050 ads->config.bind_path);
1055 if (!(samAccountName = talloc_asprintf(ctx, "%s$", hostname)))
1057 if (!(controlstr = talloc_asprintf(ctx, "%u",
1058 UF_DONT_EXPIRE_PASSWD | UF_WORKSTATION_TRUST_ACCOUNT |
1059 UF_TRUSTED_FOR_DELEGATION | UF_USE_DES_KEY_ONLY)))
1062 if (!(mods = ads_init_mods(ctx)))
1065 ads_mod_str(ctx, &mods, "cn", hostname);
1066 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1067 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1068 ads_mod_str(ctx, &mods, "userPrincipalName", host_upn);
1069 ads_mod_str(ctx, &mods, "servicePrincipalName", host_spn);
1070 ads_mod_str(ctx, &mods, "dNSHostName", hostname);
1071 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1072 ads_mod_str(ctx, &mods, "operatingSystem", "Samba");
1073 ads_mod_str(ctx, &mods, "operatingSystemVersion", VERSION);
1075 ads_gen_add(ads, new_dn, mods);
1076 ret = ads_set_machine_sd(ads, hostname, new_dn);
1079 talloc_destroy(ctx);
1084 dump a binary result from ldap
1086 static void dump_binary(const char *field, struct berval **values)
1089 for (i=0; values[i]; i++) {
1090 printf("%s: ", field);
1091 for (j=0; j<values[i]->bv_len; j++) {
1092 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1099 dump a sid result from ldap
1101 static void dump_sid(const char *field, struct berval **values)
1104 for (i=0; values[i]; i++) {
1106 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1107 printf("%s: %s\n", field, sid_string_static(&sid));
1112 dump ntSecurityDescriptor
1114 static void dump_sd(const char *filed, struct berval **values)
1119 TALLOC_CTX *ctx = 0;
1121 if (!(ctx = talloc_init_named("sec_io_desc")))
1125 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1126 prs_append_data(&ps, values[0]->bv_val, values[0]->bv_len);
1130 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1132 talloc_destroy(ctx);
1135 if (psd) ads_disp_sd(psd);
1138 talloc_destroy(ctx);
1142 dump a string result from ldap
1144 static void dump_string(const char *field, char **values)
1147 for (i=0; values[i]; i++) {
1148 printf("%s: %s\n", field, values[i]);
1153 dump a field from LDAP on stdout
1157 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1162 void (*handler)(const char *, struct berval **);
1164 {"objectGUID", False, dump_binary},
1165 {"nTSecurityDescriptor", False, dump_sd},
1166 {"dnsRecord", False, dump_binary},
1167 {"objectSid", False, dump_sid},
1172 if (!field) { /* must be end of an entry */
1177 for (i=0; handlers[i].name; i++) {
1178 if (StrCaseCmp(handlers[i].name, field) == 0) {
1179 if (!values) /* first time, indicate string or not */
1180 return handlers[i].string;
1181 handlers[i].handler(field, (struct berval **) values);
1185 if (!handlers[i].name) {
1186 if (!values) /* first time, indicate string conversion */
1188 dump_string(field, (char **)values);
1194 * Dump a result from LDAP on stdout
1195 * used for debugging
1196 * @param ads connection to ads server
1197 * @param res Results to dump
1200 void ads_dump(ADS_STRUCT *ads, void *res)
1202 ads_process_results(ads, res, ads_dump_field, NULL);
1206 * Walk through results, calling a function for each entry found.
1207 * The function receives a field name, a berval * array of values,
1208 * and a data area passed through from the start. The function is
1209 * called once with null for field and values at the end of each
1211 * @param ads connection to ads server
1212 * @param res Results to process
1213 * @param fn Function for processing each result
1214 * @param data_area user-defined area to pass to function
1216 void ads_process_results(ADS_STRUCT *ads, void *res,
1217 BOOL(*fn)(char *, void **, void *),
1223 if (!(ctx = talloc_init()))
1226 for (msg = ads_first_entry(ads, res); msg;
1227 msg = ads_next_entry(ads, msg)) {
1231 for (utf8_field=ldap_first_attribute(ads->ld,
1232 (LDAPMessage *)msg,&b);
1234 utf8_field=ldap_next_attribute(ads->ld,
1235 (LDAPMessage *)msg,b)) {
1236 struct berval **ber_vals;
1237 char **str_vals, **utf8_vals;
1241 pull_utf8_talloc(ctx, &field, utf8_field);
1242 string = fn(field, NULL, data_area);
1245 utf8_vals = ldap_get_values(ads->ld,
1246 (LDAPMessage *)msg, field);
1247 str_vals = ads_pull_strvals(ctx,
1248 (const char **) utf8_vals);
1249 fn(field, (void **) str_vals, data_area);
1250 ldap_value_free(utf8_vals);
1252 ber_vals = ldap_get_values_len(ads->ld,
1253 (LDAPMessage *)msg, field);
1254 fn(field, (void **) ber_vals, data_area);
1256 ldap_value_free_len(ber_vals);
1258 ldap_memfree(utf8_field);
1261 talloc_destroy_pool(ctx);
1262 fn(NULL, NULL, data_area); /* completed an entry */
1265 talloc_destroy(ctx);
1269 * count how many replies are in a LDAPMessage
1270 * @param ads connection to ads server
1271 * @param res Results to count
1272 * @return number of replies
1274 int ads_count_replies(ADS_STRUCT *ads, void *res)
1276 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1280 * Join a machine to a realm
1281 * Creates the machine account and sets the machine password
1282 * @param ads connection to ads server
1283 * @param hostname name of host to add
1284 * @param org_unit Organizational unit to place machine in
1285 * @return status of join
1287 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *hostname, const char *org_unit)
1293 /* hostname must be lowercase */
1294 host = strdup(hostname);
1297 status = ads_find_machine_acct(ads, (void **)&res, host);
1298 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1299 DEBUG(0, ("Host account for %s already exists - deleting old account\n", host));
1300 status = ads_leave_realm(ads, host);
1301 if (!ADS_ERR_OK(status)) {
1302 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
1303 host, ads->config.realm));
1308 status = ads_add_machine_acct(ads, host, org_unit);
1309 if (!ADS_ERR_OK(status)) {
1310 DEBUG(0, ("ads_add_machine_acct: %s\n", ads_errstr(status)));
1314 status = ads_find_machine_acct(ads, (void **)&res, host);
1315 if (!ADS_ERR_OK(status)) {
1316 DEBUG(0, ("Host account test failed\n"));
1326 * Delete a machine from the realm
1327 * @param ads connection to ads server
1328 * @param hostname Machine to remove
1329 * @return status of delete
1331 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
1335 char *hostnameDN, *host;
1338 /* hostname must be lowercase */
1339 host = strdup(hostname);
1342 status = ads_find_machine_acct(ads, &res, host);
1343 if (!ADS_ERR_OK(status)) {
1344 DEBUG(0, ("Host account for %s does not exist.\n", host));
1348 hostnameDN = ads_get_dn(ads, (LDAPMessage *)res);
1349 rc = ldap_delete_s(ads->ld, hostnameDN);
1350 ads_memfree(ads, hostnameDN);
1351 if (rc != LDAP_SUCCESS) {
1352 return ADS_ERROR(rc);
1355 status = ads_find_machine_acct(ads, &res, host);
1356 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1357 DEBUG(0, ("Failed to remove host account.\n"));
1367 * add machine account to existing security descriptor
1368 * @param ads connection to ads server
1369 * @param hostname machine to add
1370 * @param dn DN of security descriptor
1373 ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
1375 const char *attrs[] = {"ntSecurityDescriptor", "objectSid", 0};
1378 struct berval bval = {0, NULL};
1381 LDAPMessage *res = 0;
1382 LDAPMessage *msg = 0;
1383 ADS_MODLIST mods = 0;
1389 TALLOC_CTX *ctx = 0;
1391 if (!ads) return ADS_ERROR(LDAP_SERVER_DOWN);
1393 ret = ADS_ERROR(LDAP_SUCCESS);
1395 if (asprintf(&exp, "(samAccountName=%s$)", hostname) == -1) {
1396 DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n"));
1397 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1400 ret = ads_search(ads, (void *) &res, exp, attrs);
1402 if (!ADS_ERR_OK(ret)) return ret;
1404 msg = ads_first_entry(ads, res);
1405 ads_pull_sid(ads, msg, attrs[1], &sid);
1406 if (!(ctx = talloc_init_named("sec_io_desc"))) {
1407 ret = ADS_ERROR(LDAP_NO_MEMORY);
1408 goto ads_set_sd_error;
1411 if (!ads_pull_sd(ads, ctx, msg, attrs[0], &psd)) {
1412 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1413 goto ads_set_sd_error;
1416 status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size);
1418 if (!NT_STATUS_IS_OK(status)) {
1419 ret = ADS_ERROR_NT(status);
1420 goto ads_set_sd_error;
1423 prs_init(&ps_wire, sd_size, ctx, MARSHALL);
1424 if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1)) {
1425 ret = ADS_ERROR(LDAP_NO_MEMORY);
1426 goto ads_set_sd_error;
1430 file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size);
1432 if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY);
1434 bval.bv_len = sd_size;
1435 bval.bv_val = prs_data_p(&ps_wire);
1436 ads_mod_ber(ctx, &mods, attrs[0], &bval);
1437 ret = ads_gen_mod(ads, dn, mods);
1440 ads_msgfree(ads, res);
1441 prs_mem_free(&ps_wire);
1442 talloc_destroy(ctx);
1447 * pull the first entry from a ADS result
1448 * @param ads connection to ads server
1449 * @param res Results of search
1450 * @return first entry from result
1452 void *ads_first_entry(ADS_STRUCT *ads, void *res)
1454 return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
1458 * pull the next entry from a ADS result
1459 * @param ads connection to ads server
1460 * @param res Results of search
1461 * @return next entry from result
1463 void *ads_next_entry(ADS_STRUCT *ads, void *res)
1465 return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
1469 * pull a single string from a ADS result
1470 * @param ads connection to ads server
1471 * @param mem_ctx TALLOC_CTX to use for allocating result string
1472 * @param msg Results of search
1473 * @param field Attribute to retrieve
1474 * @return Result string in talloc context
1476 char *ads_pull_string(ADS_STRUCT *ads,
1477 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1484 values = ldap_get_values(ads->ld, msg, field);
1485 if (!values) return NULL;
1488 rc = pull_utf8_talloc(mem_ctx, &ux_string,
1494 ldap_value_free(values);
1499 * pull an array of strings from a ADS result
1500 * @param ads connection to ads server
1501 * @param mem_ctx TALLOC_CTX to use for allocating result string
1502 * @param msg Results of search
1503 * @param field Attribute to retrieve
1504 * @return Result strings in talloc context
1506 char **ads_pull_strings(ADS_STRUCT *ads,
1507 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1513 values = ldap_get_values(ads->ld, msg, field);
1514 if (!values) return NULL;
1516 for (i=0;values[i];i++) /* noop */ ;
1519 ret = talloc(mem_ctx, sizeof(char *) * (n+1));
1522 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
1528 ldap_value_free(values);
1534 * pull a single uint32 from a ADS result
1535 * @param ads connection to ads server
1536 * @param msg Results of search
1537 * @param field Attribute to retrieve
1538 * @param v Pointer to int to store result
1539 * @return boolean inidicating success
1541 BOOL ads_pull_uint32(ADS_STRUCT *ads,
1542 void *msg, const char *field, uint32 *v)
1546 values = ldap_get_values(ads->ld, msg, field);
1547 if (!values) return False;
1549 ldap_value_free(values);
1553 *v = atoi(values[0]);
1554 ldap_value_free(values);
1559 * pull a single DOM_SID from a ADS result
1560 * @param ads connection to ads server
1561 * @param msg Results of search
1562 * @param field Attribute to retrieve
1563 * @param sid Pointer to sid to store result
1564 * @return boolean inidicating success
1566 BOOL ads_pull_sid(ADS_STRUCT *ads,
1567 void *msg, const char *field, DOM_SID *sid)
1569 struct berval **values;
1572 values = ldap_get_values_len(ads->ld, msg, field);
1574 if (!values) return False;
1577 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
1580 ldap_value_free_len(values);
1585 * pull an array of DOM_SIDs from a ADS result
1586 * @param ads connection to ads server
1587 * @param mem_ctx TALLOC_CTX for allocating sid array
1588 * @param msg Results of search
1589 * @param field Attribute to retrieve
1590 * @param sids pointer to sid array to allocate
1591 * @return the count of SIDs pulled
1593 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1594 void *msg, const char *field, DOM_SID **sids)
1596 struct berval **values;
1600 values = ldap_get_values_len(ads->ld, msg, field);
1602 if (!values) return 0;
1604 for (i=0; values[i]; i++) /* nop */ ;
1606 (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * i);
1609 for (i=0; values[i]; i++) {
1610 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
1614 ldap_value_free_len(values);
1619 * pull a SEC_DESC from a ADS result
1620 * @param ads connection to ads server
1621 * @param mem_ctx TALLOC_CTX for allocating sid array
1622 * @param msg Results of search
1623 * @param field Attribute to retrieve
1624 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
1625 * @return boolean inidicating success
1627 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1628 void *msg, const char *field, SEC_DESC **sd)
1630 struct berval **values;
1634 values = ldap_get_values_len(ads->ld, msg, field);
1636 if (!values) return False;
1639 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
1640 prs_append_data(&ps, values[0]->bv_val, values[0]->bv_len);
1643 ret = sec_io_desc("sd", sd, &ps, 1);
1646 ldap_value_free_len(values);
1651 * in order to support usernames longer than 21 characters we need to
1652 * use both the sAMAccountName and the userPrincipalName attributes
1653 * It seems that not all users have the userPrincipalName attribute set
1655 * @param ads connection to ads server
1656 * @param mem_ctx TALLOC_CTX for allocating sid array
1657 * @param msg Results of search
1658 * @return the username
1660 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
1664 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
1665 if (ret && (p = strchr(ret, '@'))) {
1669 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
1674 * find the update serial number - this is the core of the ldap cache
1675 * @param ads connection to ads server
1676 * @param ads connection to ADS server
1677 * @param usn Pointer to retrieved update serial number
1678 * @return status of search
1680 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
1682 const char *attrs[] = {"highestCommittedUSN", NULL};
1686 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1687 if (!ADS_ERR_OK(status)) return status;
1689 if (ads_count_replies(ads, res) != 1) {
1690 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1693 ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
1694 ads_msgfree(ads, res);
1698 /* parse a ADS timestring - typical string is
1699 '20020917091222.0Z0' which means 09:12.22 17th September
1701 static time_t ads_parse_time(const char *str)
1707 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
1708 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
1709 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
1720 * Find the servers name and realm - this can be done before authentication
1721 * The ldapServiceName field on w2k looks like this:
1722 * vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
1723 * @param ads connection to ads server
1724 * @return status of search
1726 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
1728 const char *attrs[] = {"ldapServiceName", "currentTime", NULL};
1736 if (!(ctx = talloc_init())) {
1737 return ADS_ERROR(LDAP_NO_MEMORY);
1740 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1741 if (!ADS_ERR_OK(status)) return status;
1743 value = ads_pull_string(ads, ctx, res, "ldapServiceName");
1745 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1748 timestr = ads_pull_string(ads, ctx, res, "currentTime");
1750 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1755 p = strchr(value, ':');
1757 talloc_destroy(ctx);
1758 DEBUG(1, ("ads_server_info: returned ldap server name did not contain a ':' so was deemed invalid\n"));
1759 return ADS_ERROR(LDAP_DECODING_ERROR);
1762 SAFE_FREE(ads->config.ldap_server_name);
1764 ads->config.ldap_server_name = strdup(p+1);
1765 p = strchr(ads->config.ldap_server_name, '$');
1766 if (!p || p[1] != '@') {
1767 talloc_destroy(ctx);
1768 SAFE_FREE(ads->config.ldap_server_name);
1769 DEBUG(1, ("ads_server_info: returned ldap server name did not contain '$@' so was deemed invalid\n"));
1770 return ADS_ERROR(LDAP_DECODING_ERROR);
1775 SAFE_FREE(ads->config.realm);
1776 SAFE_FREE(ads->config.bind_path);
1778 ads->config.realm = strdup(p+2);
1779 ads->config.bind_path = ads_build_dn(ads->config.realm);
1781 DEBUG(3,("got ldap server name %s@%s, using bind path: %s\n",
1782 ads->config.ldap_server_name, ads->config.realm,
1783 ads->config.bind_path));
1785 ads->config.current_time = ads_parse_time(timestr);
1787 if (ads->config.current_time != 0) {
1788 ads->auth.time_offset = ads->config.current_time - time(NULL);
1789 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
1792 talloc_destroy(ctx);
1799 * find the list of trusted domains
1800 * @param ads connection to ads server
1801 * @param mem_ctx TALLOC_CTX for allocating results
1802 * @param num_trusts pointer to number of trusts
1803 * @param names pointer to trusted domain name list
1804 * @param sids pointer to list of sids of trusted domains
1805 * @return the count of SIDs pulled
1807 ADS_STATUS ads_trusted_domains(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1813 const char *attrs[] = {"name", "flatname", "securityIdentifier",
1814 "trustDirection", NULL};
1821 status = ads_search(ads, &res, "(objectcategory=trustedDomain)", attrs);
1822 if (!ADS_ERR_OK(status)) return status;
1824 count = ads_count_replies(ads, res);
1826 ads_msgfree(ads, res);
1827 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1830 (*names) = talloc(mem_ctx, sizeof(char *) * count);
1831 (*alt_names) = talloc(mem_ctx, sizeof(char *) * count);
1832 (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * count);
1833 if (! *names || ! *sids) return ADS_ERROR(LDAP_NO_MEMORY);
1835 for (i=0, msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
1838 /* direction is a 2 bit bitfield, 1 means they trust us
1839 but we don't trust them, so we should not list them
1840 as users from that domain can't login */
1841 if (ads_pull_uint32(ads, msg, "trustDirection", &direction) &&
1846 (*names)[i] = ads_pull_string(ads, mem_ctx, msg, "name");
1847 (*alt_names)[i] = ads_pull_string(ads, mem_ctx, msg, "flatname");
1849 if ((*alt_names)[i] && (*alt_names)[i][0]) {
1850 /* we prefer the flatname as the primary name
1851 for consistency with RPC */
1852 char *name = (*alt_names)[i];
1853 (*alt_names)[i] = (*names)[i];
1856 if (ads_pull_sid(ads, msg, "securityIdentifier", &(*sids)[i])) {
1861 ads_msgfree(ads, res);
1869 * find the domain sid for our domain
1870 * @param ads connection to ads server
1871 * @param sid Pointer to domain sid
1872 * @return status of search
1874 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
1876 const char *attrs[] = {"objectSid", NULL};
1880 rc = ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
1882 if (!ADS_ERR_OK(rc)) return rc;
1883 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
1884 return ADS_ERROR_SYSTEM(ENOENT);
1886 ads_msgfree(ads, res);
1891 /* this is rather complex - we need to find the allternate (netbios) name
1892 for the domain, but there isn't a simple query to do this. Instead
1893 we look for the principle names on the DCs account and find one that has
1894 the right form, then extract the netbios name of the domain from that
1896 ADS_STATUS ads_workgroup_name(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char **workgroup)
1905 const char *attrs[] = {"servicePrincipalName", NULL};
1907 (*workgroup) = NULL;
1909 asprintf(&exp, "(&(objectclass=computer)(dnshostname=%s.%s))",
1910 ads->config.ldap_server_name, ads->config.realm);
1911 rc = ads_search(ads, &res, exp, attrs);
1914 if (!ADS_ERR_OK(rc)) {
1918 principles = ads_pull_strings(ads, mem_ctx, res, "servicePrincipalName");
1920 ads_msgfree(ads, res);
1923 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1926 asprintf(&prefix, "HOST/%s.%s/",
1927 ads->config.ldap_server_name,
1930 prefix_length = strlen(prefix);
1932 for (i=0;principles[i]; i++) {
1933 if (strncasecmp(principles[i], prefix, prefix_length) == 0 &&
1934 strcasecmp(ads->config.realm, principles[i]+prefix_length) != 0 &&
1935 !strchr(principles[i]+prefix_length, '.')) {
1936 /* found an alternate (short) name for the domain. */
1937 DEBUG(3,("Found alternate name '%s' for realm '%s'\n",
1938 principles[i]+prefix_length,
1939 ads->config.realm));
1940 (*workgroup) = talloc_strdup(mem_ctx, principles[i]+prefix_length);
1947 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);