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)
113 struct ldap_ip *ip_list;
116 c_realm = ads->server.realm;
117 if (!c_realm || !*c_realm) {
118 c_realm = lp_realm();
120 if (!c_realm || !*c_realm) {
121 c_realm = ads->server.workgroup;
123 if (!c_realm || !*c_realm) {
124 c_realm = lp_workgroup();
129 realm = smb_xstrdup(c_realm);
131 DEBUG(6,("ads_try_dns: looking for realm '%s'\n", realm));
132 if (ldap_domain2hostlist(realm, &list) != LDAP_SUCCESS) {
137 DEBUG(6,("ads_try_dns: ldap realm '%s' host list '%s'\n", realm, list));
140 count = count_chars(list, ' ') + 1;
141 ip_list = malloc(count * sizeof(struct ldap_ip));
147 while (next_token(&ptr, tok, " ", sizeof(tok))) {
148 unsigned port = LDAP_PORT;
149 char *p = strchr(tok, ':');
154 ip_list[i].ip = *interpret_addr2(tok);
155 ip_list[i].port = port;
156 if (!is_zero_ip(ip_list[i].ip)) {
164 /* we sort the list of addresses by closeness to our interfaces. This
165 tries to prevent us using a DC on the other side of the country */
167 qsort(ip_list, count, sizeof(struct ldap_ip),
168 QSORT_CAST ldap_ip_compare);
171 for (i=0;i<count;i++) {
172 if (ads_try_connect(ads, inet_ntoa(ip_list[i].ip), ip_list[i].port)) {
182 /* try connecting to a ldap server via netbios */
183 static BOOL ads_try_netbios(ADS_STRUCT *ads)
185 struct in_addr *ip_list, pdc_ip;
188 const char *workgroup = ads->server.workgroup;
191 workgroup = lp_workgroup();
194 DEBUG(6,("ads_try_netbios: looking for workgroup '%s'\n", workgroup));
196 /* try the PDC first */
197 if (get_pdc_ip(workgroup, &pdc_ip)) {
198 DEBUG(6,("ads_try_netbios: trying server '%s'\n",
200 if (ads_try_connect(ads, inet_ntoa(pdc_ip), LDAP_PORT))
204 /* now any DC, including backups */
205 if (get_dc_list(workgroup, &ip_list, &count)) {
206 for (i=0;i<count;i++) {
207 DEBUG(6,("ads_try_netbios: trying server '%s'\n",
208 inet_ntoa(ip_list[i])));
209 if (ads_try_connect(ads, inet_ntoa(ip_list[i]), LDAP_PORT)) {
221 * Connect to the LDAP server
222 * @param ads Pointer to an existing ADS_STRUCT
223 * @return status of connection
225 ADS_STATUS ads_connect(ADS_STRUCT *ads)
227 int version = LDAP_VERSION3;
230 ads->last_attempt = time(NULL);
233 /* try with a URL based server */
235 if (ads->server.ldap_uri &&
236 ads_try_connect_uri(ads)) {
240 /* try with a user specified server */
241 if (ads->server.ldap_server &&
242 ads_try_connect(ads, ads->server.ldap_server, LDAP_PORT)) {
246 /* try with a smb.conf ads server setting if we are connecting
247 to the primary workgroup or realm */
248 if (!ads->server.foreign &&
249 ads_try_connect(ads, lp_ads_server(), LDAP_PORT)) {
254 if (ads_try_dns(ads)) {
258 /* try via netbios lookups */
259 if (!lp_disable_netbios() && ads_try_netbios(ads)) {
263 return ADS_ERROR_SYSTEM(errno?errno:ENOENT);
266 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
268 status = ads_server_info(ads);
269 if (!ADS_ERR_OK(status)) {
270 DEBUG(1,("Failed to get ldap server info\n"));
274 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
276 if (!ads->auth.user_name) {
277 /* by default use the machine account */
279 fstrcpy(myname, global_myname());
281 asprintf(&ads->auth.user_name, "HOST/%s", myname);
284 if (!ads->auth.realm) {
285 ads->auth.realm = strdup(ads->config.realm);
288 if (!ads->auth.kdc_server) {
289 ads->auth.kdc_server = strdup(inet_ntoa(ads->ldap_ip));
293 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
294 to MIT kerberos to work (tridge) */
297 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
298 setenv(env, ads->auth.kdc_server, 1);
303 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
307 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
308 return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
311 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
312 return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
315 return ads_sasl_bind(ads);
319 Duplicate a struct berval into talloc'ed memory
321 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
323 struct berval *value;
325 if (!in_val) return NULL;
327 value = talloc_zero(ctx, sizeof(struct berval));
328 if (in_val->bv_len == 0) return value;
330 value->bv_len = in_val->bv_len;
331 value->bv_val = talloc_memdup(ctx, in_val->bv_val, in_val->bv_len);
336 Make a values list out of an array of (struct berval *)
338 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
339 const struct berval **in_vals)
341 struct berval **values;
344 if (!in_vals) return NULL;
345 for (i=0; in_vals[i]; i++); /* count values */
346 values = (struct berval **) talloc_zero(ctx,
347 (i+1)*sizeof(struct berval *));
348 if (!values) return NULL;
350 for (i=0; in_vals[i]; i++) {
351 values[i] = dup_berval(ctx, in_vals[i]);
357 UTF8-encode a values list out of an array of (char *)
359 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
364 if (!in_vals) return NULL;
365 for (i=0; in_vals[i]; i++); /* count values */
366 values = (char ** ) talloc_zero(ctx, (i+1)*sizeof(char *));
367 if (!values) return NULL;
369 for (i=0; in_vals[i]; i++) {
370 push_utf8_talloc(ctx, &values[i], in_vals[i]);
376 Pull a (char *) array out of a UTF8-encoded values list
378 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
383 if (!in_vals) return NULL;
384 for (i=0; in_vals[i]; i++); /* count values */
385 values = (char **) talloc_zero(ctx, (i+1)*sizeof(char *));
386 if (!values) return NULL;
388 for (i=0; in_vals[i]; i++) {
389 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
395 * Do a search with paged results. cookie must be null on the first
396 * call, and then returned on each subsequent call. It will be null
397 * again when the entire search is complete
398 * @param ads connection to ads server
399 * @param bind_path Base dn for the search
400 * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
401 * @param exp Search expression - specified in local charset
402 * @param attrs Attributes to retrieve - specified in utf8 or ascii
403 * @param res ** which will contain results - free res* with ads_msgfree()
404 * @param count Number of entries retrieved on this page
405 * @param cookie The paged results cookie to be returned on subsequent calls
406 * @return status of search
408 ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
409 int scope, const char *exp,
410 const char **attrs, void **res,
411 int *count, void **cookie)
414 char *utf8_exp, *utf8_path, **search_attrs;
415 LDAPControl PagedResults, NoReferrals, *controls[3], **rcontrols;
416 BerElement *cookie_be = NULL;
417 struct berval *cookie_bv= NULL;
422 if (!(ctx = talloc_init()))
423 return ADS_ERROR(LDAP_NO_MEMORY);
425 /* 0 means the conversion worked but the result was empty
426 so we only fail if it's negative. In any case, it always
427 at least nulls out the dest */
428 if ((push_utf8_talloc(ctx, &utf8_exp, exp) < 0) ||
429 (push_utf8_talloc(ctx, &utf8_path, bind_path) < 0)) {
434 if (!attrs || !(*attrs))
437 /* This would be the utf8-encoded version...*/
438 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
439 if (!(str_list_copy(&search_attrs, attrs))) {
446 /* Paged results only available on ldap v3 or later */
447 ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
448 if (version < LDAP_VERSION3) {
449 rc = LDAP_NOT_SUPPORTED;
453 cookie_be = ber_alloc_t(LBER_USE_DER);
454 if (cookie && *cookie) {
455 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
456 ber_bvfree(*cookie); /* don't need it from last time */
459 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
461 ber_flatten(cookie_be, &cookie_bv);
462 PagedResults.ldctl_oid = ADS_PAGE_CTL_OID;
463 PagedResults.ldctl_iscritical = (char) 1;
464 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
465 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
467 NoReferrals.ldctl_oid = ADS_NO_REFERRALS_OID;
468 NoReferrals.ldctl_iscritical = (char) 0;
469 NoReferrals.ldctl_value.bv_len = 0;
470 NoReferrals.ldctl_value.bv_val = "";
473 controls[0] = &NoReferrals;
474 controls[1] = &PagedResults;
479 /* we need to disable referrals as the openldap libs don't
480 handle them and paged results at the same time. Using them
481 together results in the result record containing the server
482 page control being removed from the result list (tridge/jmcd)
484 leaving this in despite the control that says don't generate
485 referrals, in case the server doesn't support it (jmcd)
487 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
489 rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_exp,
490 search_attrs, 0, controls,
491 NULL, NULL, LDAP_NO_LIMIT, (LDAPMessage **)res);
493 ber_free(cookie_be, 1);
494 ber_bvfree(cookie_bv);
497 DEBUG(3,("ldap_search_ext_s(%s) -> %s\n", exp, ldap_err2string(rc)));
501 rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
502 NULL, &rcontrols, 0);
508 for (i=0; rcontrols[i]; i++) {
509 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
510 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
511 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
513 /* the berval is the cookie, but must be freed when
515 if (cookie_bv->bv_len) /* still more to do */
516 *cookie=ber_bvdup(cookie_bv);
519 ber_bvfree(cookie_bv);
520 ber_free(cookie_be, 1);
524 ldap_controls_free(rcontrols);
528 /* if/when we decide to utf8-encode attrs, take out this next line */
529 str_list_free(&search_attrs);
531 return ADS_ERROR(rc);
536 * Get all results for a search. This uses ads_do_paged_search() to return
537 * all entries in a large search.
538 * @param ads connection to ads server
539 * @param bind_path Base dn for the search
540 * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
541 * @param exp Search expression
542 * @param attrs Attributes to retrieve
543 * @param res ** which will contain results - free res* with ads_msgfree()
544 * @return status of search
546 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
547 int scope, const char *exp,
548 const char **attrs, void **res)
554 status = ads_do_paged_search(ads, bind_path, scope, exp, attrs, res,
557 if (!ADS_ERR_OK(status)) return status;
562 LDAPMessage *msg, *next;
564 status2 = ads_do_paged_search(ads, bind_path, scope, exp,
565 attrs, &res2, &count, &cookie);
567 if (!ADS_ERR_OK(status2)) break;
569 /* this relies on the way that ldap_add_result_entry() works internally. I hope
570 that this works on all ldap libs, but I have only tested with openldap */
571 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
572 next = ads_next_entry(ads, msg);
573 ldap_add_result_entry((LDAPMessage **)res, msg);
575 /* note that we do not free res2, as the memory is now
576 part of the main returned list */
583 * Run a function on all results for a search. Uses ads_do_paged_search() and
584 * runs the function as each page is returned, using ads_process_results()
585 * @param ads connection to ads server
586 * @param bind_path Base dn for the search
587 * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
588 * @param exp Search expression - specified in local charset
589 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
590 * @param fn Function which takes attr name, values list, and data_area
591 * @param data_area Pointer which is passed to function on each call
592 * @return status of search
594 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
595 int scope, const char *exp, const char **attrs,
596 BOOL(*fn)(char *, void **, void *),
604 status = ads_do_paged_search(ads, bind_path, scope, exp, attrs, &res,
607 if (!ADS_ERR_OK(status)) return status;
609 ads_process_results(ads, res, fn, data_area);
610 ads_msgfree(ads, res);
613 status = ads_do_paged_search(ads, bind_path, scope, exp, attrs,
614 &res, &count, &cookie);
616 if (!ADS_ERR_OK(status)) break;
618 ads_process_results(ads, res, fn, data_area);
619 ads_msgfree(ads, res);
626 * Do a search with a timeout.
627 * @param ads connection to ads server
628 * @param bind_path Base dn for the search
629 * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
630 * @param exp Search expression
631 * @param attrs Attributes to retrieve
632 * @param res ** which will contain results - free res* with ads_msgfree()
633 * @return status of search
635 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
637 const char **attrs, void **res)
639 struct timeval timeout;
641 char *utf8_exp, *utf8_path, **search_attrs = NULL;
644 if (!(ctx = talloc_init())) {
645 DEBUG(1,("ads_do_search: talloc_init() failed!"));
646 return ADS_ERROR(LDAP_NO_MEMORY);
649 /* 0 means the conversion worked but the result was empty
650 so we only fail if it's negative. In any case, it always
651 at least nulls out the dest */
652 if ((push_utf8_talloc(ctx, &utf8_exp, exp) < 0) ||
653 (push_utf8_talloc(ctx, &utf8_path, bind_path) < 0)) {
654 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
659 if (!attrs || !(*attrs))
662 /* This would be the utf8-encoded version...*/
663 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
664 if (!(str_list_copy(&search_attrs, attrs)))
666 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
672 timeout.tv_sec = ADS_SEARCH_TIMEOUT;
676 /* see the note in ads_do_paged_search - we *must* disable referrals */
677 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
679 rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_exp,
680 search_attrs, 0, NULL, NULL,
681 &timeout, LDAP_NO_LIMIT, (LDAPMessage **)res);
683 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
684 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
690 /* if/when we decide to utf8-encode attrs, take out this next line */
691 str_list_free(&search_attrs);
692 return ADS_ERROR(rc);
695 * Do a general ADS search
696 * @param ads connection to ads server
697 * @param res ** which will contain results - free res* with ads_msgfree()
698 * @param exp Search expression
699 * @param attrs Attributes to retrieve
700 * @return status of search
702 ADS_STATUS ads_search(ADS_STRUCT *ads, void **res,
706 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
711 * Do a search on a specific DistinguishedName
712 * @param ads connection to ads server
713 * @param res ** which will contain results - free res* with ads_msgfree()
714 * @param dn DistinguishName to search
715 * @param attrs Attributes to retrieve
716 * @return status of search
718 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, void **res,
722 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res);
726 * Free up memory from a ads_search
727 * @param ads connection to ads server
728 * @param msg Search results to free
730 void ads_msgfree(ADS_STRUCT *ads, void *msg)
737 * Free up memory from various ads requests
738 * @param ads connection to ads server
739 * @param mem Area to free
741 void ads_memfree(ADS_STRUCT *ads, void *mem)
747 * Get a dn from search results
748 * @param ads connection to ads server
749 * @param res Search results
752 char *ads_get_dn(ADS_STRUCT *ads, void *res)
754 char *utf8_dn, *unix_dn;
756 utf8_dn = ldap_get_dn(ads->ld, res);
757 pull_utf8_allocate((void **) &unix_dn, utf8_dn);
758 ldap_memfree(utf8_dn);
763 * Find a machine account given a hostname
764 * @param ads connection to ads server
765 * @param res ** which will contain results - free res* with ads_msgfree()
766 * @param host Hostname to search for
767 * @return status of search
769 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *host)
773 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
775 /* the easiest way to find a machine account anywhere in the tree
776 is to look for hostname$ */
777 if (asprintf(&exp, "(samAccountName=%s$)", host) == -1) {
778 DEBUG(1, ("asprintf failed!\n"));
779 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
782 status = ads_search(ads, res, exp, attrs);
788 * Initialize a list of mods to be used in a modify request
789 * @param ctx An initialized TALLOC_CTX
790 * @return allocated ADS_MODLIST
792 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
794 #define ADS_MODLIST_ALLOC_SIZE 10
797 if ((mods = (LDAPMod **) talloc_zero(ctx, sizeof(LDAPMod *) *
798 (ADS_MODLIST_ALLOC_SIZE + 1))))
799 /* -1 is safety to make sure we don't go over the end.
800 need to reset it to NULL before doing ldap modify */
801 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
808 add an attribute to the list, with values list already constructed
810 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
811 int mod_op, const char *name,
815 LDAPMod **modlist = (LDAPMod **) *mods;
820 mod_op = LDAP_MOD_DELETE;
822 if (mod_op & LDAP_MOD_BVALUES)
823 values = (void **) ads_dup_values(ctx,
824 (const struct berval **)invals);
826 values = (void **) ads_push_strvals(ctx,
827 (const char **) invals);
830 /* find the first empty slot */
831 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
833 if (modlist[curmod] == (LDAPMod *) -1) {
834 if (!(modlist = talloc_realloc(ctx, modlist,
835 (curmod+ADS_MODLIST_ALLOC_SIZE+1)*sizeof(LDAPMod *))))
836 return ADS_ERROR(LDAP_NO_MEMORY);
837 memset(&modlist[curmod], 0,
838 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
839 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
843 if (!(modlist[curmod] = talloc_zero(ctx, sizeof(LDAPMod))))
844 return ADS_ERROR(LDAP_NO_MEMORY);
845 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
846 if (mod_op & LDAP_MOD_BVALUES)
847 modlist[curmod]->mod_bvalues = (struct berval **) values;
849 modlist[curmod]->mod_values = (char **) values;
850 modlist[curmod]->mod_op = mod_op;
851 return ADS_ERROR(LDAP_SUCCESS);
855 * Add a single string value to a mod list
856 * @param ctx An initialized TALLOC_CTX
857 * @param mods An initialized ADS_MODLIST
858 * @param name The attribute name to add
859 * @param val The value to add - NULL means DELETE
860 * @return ADS STATUS indicating success of add
862 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
863 const char *name, const char *val)
865 const char *values[2];
871 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
872 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name,
873 (const void **) values);
877 * Add an array of string values to a mod list
878 * @param ctx An initialized TALLOC_CTX
879 * @param mods An initialized ADS_MODLIST
880 * @param name The attribute name to add
881 * @param vals The array of string values to add - NULL means DELETE
882 * @return ADS STATUS indicating success of add
884 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
885 const char *name, const char **vals)
888 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
889 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
890 name, (const void **) vals);
894 * Add a single ber-encoded value to a mod list
895 * @param ctx An initialized TALLOC_CTX
896 * @param mods An initialized ADS_MODLIST
897 * @param name The attribute name to add
898 * @param val The value to add - NULL means DELETE
899 * @return ADS STATUS indicating success of add
901 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
902 const char *name, const struct berval *val)
904 const struct berval *values[2];
909 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
910 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
911 name, (const void **) values);
915 * Perform an ldap modify
916 * @param ads connection to ads server
917 * @param mod_dn DistinguishedName to modify
918 * @param mods list of modifications to perform
919 * @return status of modify
921 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
924 char *utf8_dn = NULL;
926 this control is needed to modify that contains a currently
927 non-existent attribute (but allowable for the object) to run
929 LDAPControl PermitModify = {
930 ADS_PERMIT_MODIFY_OID,
933 LDAPControl *controls[2];
935 controls[0] = &PermitModify;
938 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
939 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
942 /* find the end of the list, marked by NULL or -1 */
943 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
944 /* make sure the end of the list is NULL */
946 ret = ldap_modify_ext_s(ads->ld, utf8_dn,
947 (LDAPMod **) mods, controls, NULL);
949 return ADS_ERROR(ret);
953 * Perform an ldap add
954 * @param ads connection to ads server
955 * @param new_dn DistinguishedName to add
956 * @param mods list of attributes and values for DN
957 * @return status of add
959 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
962 char *utf8_dn = NULL;
964 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
965 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
966 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
969 /* find the end of the list, marked by NULL or -1 */
970 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
971 /* make sure the end of the list is NULL */
974 ret = ldap_add_s(ads->ld, utf8_dn ? utf8_dn : new_dn, mods);
976 return ADS_ERROR(ret);
980 * Delete a DistinguishedName
981 * @param ads connection to ads server
982 * @param new_dn DistinguishedName to delete
983 * @return status of delete
985 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
988 char *utf8_dn = NULL;
989 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
990 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
991 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
994 ret = ldap_delete(ads->ld, utf8_dn ? utf8_dn : del_dn);
995 return ADS_ERROR(ret);
999 * Build an org unit string
1000 * if org unit is Computers or blank then assume a container, otherwise
1001 * assume a \ separated list of organisational units
1002 * @param org_unit Organizational unit
1003 * @return org unit string - caller must free
1005 char *ads_ou_string(const char *org_unit)
1007 if (!org_unit || !*org_unit || strcasecmp(org_unit, "Computers") == 0) {
1008 return strdup("cn=Computers");
1011 return ads_build_path(org_unit, "\\/", "ou=", 1);
1017 add a machine account to the ADS server
1019 static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *hostname,
1020 const char *org_unit)
1023 char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
1027 const char *objectClass[] = {"top", "person", "organizationalPerson",
1028 "user", "computer", NULL};
1029 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1031 unsigned acct_control;
1033 if (!(ctx = talloc_init_named("machine_account")))
1034 return ADS_ERROR(LDAP_NO_MEMORY);
1036 ret = ADS_ERROR(LDAP_NO_MEMORY);
1038 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", hostname)))
1040 if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm)))
1042 ou_str = ads_ou_string(org_unit);
1044 DEBUG(1, ("ads_ou_string returned NULL (malloc failure?)\n"));
1047 new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", hostname, ou_str,
1048 ads->config.bind_path);
1049 servicePrincipalName[0] = talloc_asprintf(ctx, "HOST/%s", hostname);
1050 psp = talloc_asprintf(ctx, "HOST/%s.%s",
1054 servicePrincipalName[1] = psp;
1060 if (!(samAccountName = talloc_asprintf(ctx, "%s$", hostname)))
1063 acct_control = UF_WORKSTATION_TRUST_ACCOUNT | UF_DONT_EXPIRE_PASSWD;
1064 #ifndef ENCTYPE_ARCFOUR_HMAC
1065 acct_control |= UF_USE_DES_KEY_ONLY;
1067 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control)))
1070 if (!(mods = ads_init_mods(ctx)))
1073 ads_mod_str(ctx, &mods, "cn", hostname);
1074 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1075 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1076 ads_mod_str(ctx, &mods, "userPrincipalName", host_upn);
1077 ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1078 ads_mod_str(ctx, &mods, "dNSHostName", hostname);
1079 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1080 ads_mod_str(ctx, &mods, "operatingSystem", "Samba");
1081 ads_mod_str(ctx, &mods, "operatingSystemVersion", VERSION);
1083 ads_gen_add(ads, new_dn, mods);
1084 ret = ads_set_machine_sd(ads, hostname, new_dn);
1087 talloc_destroy(ctx);
1092 dump a binary result from ldap
1094 static void dump_binary(const char *field, struct berval **values)
1097 for (i=0; values[i]; i++) {
1098 printf("%s: ", field);
1099 for (j=0; j<values[i]->bv_len; j++) {
1100 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1113 static void dump_guid(const char *field, struct berval **values)
1117 for (i=0; values[i]; i++) {
1118 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1119 printf("%s: %s\n", field, uuid_string_static(guid));
1124 dump a sid result from ldap
1126 static void dump_sid(const char *field, struct berval **values)
1129 for (i=0; values[i]; i++) {
1131 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1132 printf("%s: %s\n", field, sid_string_static(&sid));
1137 dump ntSecurityDescriptor
1139 static void dump_sd(const char *filed, struct berval **values)
1144 TALLOC_CTX *ctx = 0;
1146 if (!(ctx = talloc_init_named("sec_io_desc")))
1150 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1151 prs_append_data(&ps, values[0]->bv_val, values[0]->bv_len);
1155 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1157 talloc_destroy(ctx);
1160 if (psd) ads_disp_sd(psd);
1163 talloc_destroy(ctx);
1167 dump a string result from ldap
1169 static void dump_string(const char *field, char **values)
1172 for (i=0; values[i]; i++) {
1173 printf("%s: %s\n", field, values[i]);
1178 dump a field from LDAP on stdout
1182 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1187 void (*handler)(const char *, struct berval **);
1189 {"objectGUID", False, dump_guid},
1190 {"nTSecurityDescriptor", False, dump_sd},
1191 {"dnsRecord", False, dump_binary},
1192 {"objectSid", False, dump_sid},
1197 if (!field) { /* must be end of an entry */
1202 for (i=0; handlers[i].name; i++) {
1203 if (StrCaseCmp(handlers[i].name, field) == 0) {
1204 if (!values) /* first time, indicate string or not */
1205 return handlers[i].string;
1206 handlers[i].handler(field, (struct berval **) values);
1210 if (!handlers[i].name) {
1211 if (!values) /* first time, indicate string conversion */
1213 dump_string(field, (char **)values);
1219 * Dump a result from LDAP on stdout
1220 * used for debugging
1221 * @param ads connection to ads server
1222 * @param res Results to dump
1225 void ads_dump(ADS_STRUCT *ads, void *res)
1227 ads_process_results(ads, res, ads_dump_field, NULL);
1231 * Walk through results, calling a function for each entry found.
1232 * The function receives a field name, a berval * array of values,
1233 * and a data area passed through from the start. The function is
1234 * called once with null for field and values at the end of each
1236 * @param ads connection to ads server
1237 * @param res Results to process
1238 * @param fn Function for processing each result
1239 * @param data_area user-defined area to pass to function
1241 void ads_process_results(ADS_STRUCT *ads, void *res,
1242 BOOL(*fn)(char *, void **, void *),
1248 if (!(ctx = talloc_init()))
1251 for (msg = ads_first_entry(ads, res); msg;
1252 msg = ads_next_entry(ads, msg)) {
1256 for (utf8_field=ldap_first_attribute(ads->ld,
1257 (LDAPMessage *)msg,&b);
1259 utf8_field=ldap_next_attribute(ads->ld,
1260 (LDAPMessage *)msg,b)) {
1261 struct berval **ber_vals;
1262 char **str_vals, **utf8_vals;
1266 pull_utf8_talloc(ctx, &field, utf8_field);
1267 string = fn(field, NULL, data_area);
1270 utf8_vals = ldap_get_values(ads->ld,
1271 (LDAPMessage *)msg, field);
1272 str_vals = ads_pull_strvals(ctx,
1273 (const char **) utf8_vals);
1274 fn(field, (void **) str_vals, data_area);
1275 ldap_value_free(utf8_vals);
1277 ber_vals = ldap_get_values_len(ads->ld,
1278 (LDAPMessage *)msg, field);
1279 fn(field, (void **) ber_vals, data_area);
1281 ldap_value_free_len(ber_vals);
1283 ldap_memfree(utf8_field);
1286 talloc_destroy_pool(ctx);
1287 fn(NULL, NULL, data_area); /* completed an entry */
1290 talloc_destroy(ctx);
1294 * count how many replies are in a LDAPMessage
1295 * @param ads connection to ads server
1296 * @param res Results to count
1297 * @return number of replies
1299 int ads_count_replies(ADS_STRUCT *ads, void *res)
1301 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1305 * Join a machine to a realm
1306 * Creates the machine account and sets the machine password
1307 * @param ads connection to ads server
1308 * @param hostname name of host to add
1309 * @param org_unit Organizational unit to place machine in
1310 * @return status of join
1312 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *hostname, const char *org_unit)
1318 /* hostname must be lowercase */
1319 host = strdup(hostname);
1322 status = ads_find_machine_acct(ads, (void **)&res, host);
1323 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1324 DEBUG(0, ("Host account for %s already exists - deleting old account\n", host));
1325 status = ads_leave_realm(ads, host);
1326 if (!ADS_ERR_OK(status)) {
1327 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
1328 host, ads->config.realm));
1333 status = ads_add_machine_acct(ads, host, org_unit);
1334 if (!ADS_ERR_OK(status)) {
1335 DEBUG(0, ("ads_add_machine_acct: %s\n", ads_errstr(status)));
1339 status = ads_find_machine_acct(ads, (void **)&res, host);
1340 if (!ADS_ERR_OK(status)) {
1341 DEBUG(0, ("Host account test failed\n"));
1351 * Delete a machine from the realm
1352 * @param ads connection to ads server
1353 * @param hostname Machine to remove
1354 * @return status of delete
1356 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
1360 char *hostnameDN, *host;
1363 /* hostname must be lowercase */
1364 host = strdup(hostname);
1367 status = ads_find_machine_acct(ads, &res, host);
1368 if (!ADS_ERR_OK(status)) {
1369 DEBUG(0, ("Host account for %s does not exist.\n", host));
1373 hostnameDN = ads_get_dn(ads, (LDAPMessage *)res);
1374 rc = ldap_delete_s(ads->ld, hostnameDN);
1375 ads_memfree(ads, hostnameDN);
1376 if (rc != LDAP_SUCCESS) {
1377 return ADS_ERROR(rc);
1380 status = ads_find_machine_acct(ads, &res, host);
1381 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1382 DEBUG(0, ("Failed to remove host account.\n"));
1392 * add machine account to existing security descriptor
1393 * @param ads connection to ads server
1394 * @param hostname machine to add
1395 * @param dn DN of security descriptor
1398 ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
1400 const char *attrs[] = {"ntSecurityDescriptor", "objectSid", 0};
1403 struct berval bval = {0, NULL};
1406 LDAPMessage *res = 0;
1407 LDAPMessage *msg = 0;
1408 ADS_MODLIST mods = 0;
1414 TALLOC_CTX *ctx = 0;
1416 if (!ads) return ADS_ERROR(LDAP_SERVER_DOWN);
1418 ret = ADS_ERROR(LDAP_SUCCESS);
1420 if (asprintf(&exp, "(samAccountName=%s$)", hostname) == -1) {
1421 DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n"));
1422 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1425 ret = ads_search(ads, (void *) &res, exp, attrs);
1427 if (!ADS_ERR_OK(ret)) return ret;
1429 msg = ads_first_entry(ads, res);
1430 ads_pull_sid(ads, msg, attrs[1], &sid);
1431 if (!(ctx = talloc_init_named("sec_io_desc"))) {
1432 ret = ADS_ERROR(LDAP_NO_MEMORY);
1433 goto ads_set_sd_error;
1436 if (!ads_pull_sd(ads, ctx, msg, attrs[0], &psd)) {
1437 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1438 goto ads_set_sd_error;
1441 status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size);
1443 if (!NT_STATUS_IS_OK(status)) {
1444 ret = ADS_ERROR_NT(status);
1445 goto ads_set_sd_error;
1448 prs_init(&ps_wire, sd_size, ctx, MARSHALL);
1449 if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1)) {
1450 ret = ADS_ERROR(LDAP_NO_MEMORY);
1451 goto ads_set_sd_error;
1455 file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size);
1457 if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY);
1459 bval.bv_len = sd_size;
1460 bval.bv_val = prs_data_p(&ps_wire);
1461 ads_mod_ber(ctx, &mods, attrs[0], &bval);
1462 ret = ads_gen_mod(ads, dn, mods);
1465 ads_msgfree(ads, res);
1466 prs_mem_free(&ps_wire);
1467 talloc_destroy(ctx);
1472 * pull the first entry from a ADS result
1473 * @param ads connection to ads server
1474 * @param res Results of search
1475 * @return first entry from result
1477 void *ads_first_entry(ADS_STRUCT *ads, void *res)
1479 return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
1483 * pull the next entry from a ADS result
1484 * @param ads connection to ads server
1485 * @param res Results of search
1486 * @return next entry from result
1488 void *ads_next_entry(ADS_STRUCT *ads, void *res)
1490 return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
1494 * pull a single string from a ADS result
1495 * @param ads connection to ads server
1496 * @param mem_ctx TALLOC_CTX to use for allocating result string
1497 * @param msg Results of search
1498 * @param field Attribute to retrieve
1499 * @return Result string in talloc context
1501 char *ads_pull_string(ADS_STRUCT *ads,
1502 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1509 values = ldap_get_values(ads->ld, msg, field);
1510 if (!values) return NULL;
1513 rc = pull_utf8_talloc(mem_ctx, &ux_string,
1519 ldap_value_free(values);
1524 * pull an array of strings from a ADS result
1525 * @param ads connection to ads server
1526 * @param mem_ctx TALLOC_CTX to use for allocating result string
1527 * @param msg Results of search
1528 * @param field Attribute to retrieve
1529 * @return Result strings in talloc context
1531 char **ads_pull_strings(ADS_STRUCT *ads,
1532 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1538 values = ldap_get_values(ads->ld, msg, field);
1539 if (!values) return NULL;
1541 for (i=0;values[i];i++) /* noop */ ;
1544 ret = talloc(mem_ctx, sizeof(char *) * (n+1));
1547 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
1553 ldap_value_free(values);
1559 * pull a single uint32 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 v Pointer to int to store result
1564 * @return boolean inidicating success
1566 BOOL ads_pull_uint32(ADS_STRUCT *ads,
1567 void *msg, const char *field, uint32 *v)
1571 values = ldap_get_values(ads->ld, msg, field);
1572 if (!values) return False;
1574 ldap_value_free(values);
1578 *v = atoi(values[0]);
1579 ldap_value_free(values);
1584 * pull a single DOM_SID from a ADS result
1585 * @param ads connection to ads server
1586 * @param msg Results of search
1587 * @param field Attribute to retrieve
1588 * @param sid Pointer to sid to store result
1589 * @return boolean inidicating success
1591 BOOL ads_pull_sid(ADS_STRUCT *ads,
1592 void *msg, const char *field, DOM_SID *sid)
1594 struct berval **values;
1597 values = ldap_get_values_len(ads->ld, msg, field);
1599 if (!values) return False;
1602 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
1605 ldap_value_free_len(values);
1610 * pull an array of DOM_SIDs from a ADS result
1611 * @param ads connection to ads server
1612 * @param mem_ctx TALLOC_CTX for allocating sid array
1613 * @param msg Results of search
1614 * @param field Attribute to retrieve
1615 * @param sids pointer to sid array to allocate
1616 * @return the count of SIDs pulled
1618 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1619 void *msg, const char *field, DOM_SID **sids)
1621 struct berval **values;
1625 values = ldap_get_values_len(ads->ld, msg, field);
1627 if (!values) return 0;
1629 for (i=0; values[i]; i++) /* nop */ ;
1631 (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * i);
1634 for (i=0; values[i]; i++) {
1635 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
1639 ldap_value_free_len(values);
1644 * pull a SEC_DESC from a ADS result
1645 * @param ads connection to ads server
1646 * @param mem_ctx TALLOC_CTX for allocating sid array
1647 * @param msg Results of search
1648 * @param field Attribute to retrieve
1649 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
1650 * @return boolean inidicating success
1652 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1653 void *msg, const char *field, SEC_DESC **sd)
1655 struct berval **values;
1659 values = ldap_get_values_len(ads->ld, msg, field);
1661 if (!values) return False;
1664 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
1665 prs_append_data(&ps, values[0]->bv_val, values[0]->bv_len);
1668 ret = sec_io_desc("sd", sd, &ps, 1);
1671 ldap_value_free_len(values);
1676 * in order to support usernames longer than 21 characters we need to
1677 * use both the sAMAccountName and the userPrincipalName attributes
1678 * It seems that not all users have the userPrincipalName attribute set
1680 * @param ads connection to ads server
1681 * @param mem_ctx TALLOC_CTX for allocating sid array
1682 * @param msg Results of search
1683 * @return the username
1685 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
1689 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
1690 if (ret && (p = strchr(ret, '@'))) {
1694 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
1699 * find the update serial number - this is the core of the ldap cache
1700 * @param ads connection to ads server
1701 * @param ads connection to ADS server
1702 * @param usn Pointer to retrieved update serial number
1703 * @return status of search
1705 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
1707 const char *attrs[] = {"highestCommittedUSN", NULL};
1711 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1712 if (!ADS_ERR_OK(status)) return status;
1714 if (ads_count_replies(ads, res) != 1) {
1715 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1718 ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
1719 ads_msgfree(ads, res);
1723 /* parse a ADS timestring - typical string is
1724 '20020917091222.0Z0' which means 09:12.22 17th September
1726 static time_t ads_parse_time(const char *str)
1732 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
1733 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
1734 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
1745 * Find the servers name and realm - this can be done before authentication
1746 * The ldapServiceName field on w2k looks like this:
1747 * vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
1748 * @param ads connection to ads server
1749 * @return status of search
1751 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
1753 const char *attrs[] = {"ldapServiceName", "currentTime", NULL};
1761 if (!(ctx = talloc_init())) {
1762 return ADS_ERROR(LDAP_NO_MEMORY);
1765 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1766 if (!ADS_ERR_OK(status)) return status;
1768 value = ads_pull_string(ads, ctx, res, "ldapServiceName");
1770 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1773 timestr = ads_pull_string(ads, ctx, res, "currentTime");
1775 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1780 p = strchr(value, ':');
1782 talloc_destroy(ctx);
1783 DEBUG(1, ("ads_server_info: returned ldap server name did not contain a ':' so was deemed invalid\n"));
1784 return ADS_ERROR(LDAP_DECODING_ERROR);
1787 SAFE_FREE(ads->config.ldap_server_name);
1789 ads->config.ldap_server_name = strdup(p+1);
1790 p = strchr(ads->config.ldap_server_name, '$');
1791 if (!p || p[1] != '@') {
1792 talloc_destroy(ctx);
1793 DEBUG(1, ("ads_server_info: returned ldap server name (%s) does not contain '$@' so was deemed invalid\n", ads->config.ldap_server_name));
1794 SAFE_FREE(ads->config.ldap_server_name);
1795 return ADS_ERROR(LDAP_DECODING_ERROR);
1800 SAFE_FREE(ads->config.realm);
1801 SAFE_FREE(ads->config.bind_path);
1803 ads->config.realm = strdup(p+2);
1804 ads->config.bind_path = ads_build_dn(ads->config.realm);
1806 DEBUG(3,("got ldap server name %s@%s, using bind path: %s\n",
1807 ads->config.ldap_server_name, ads->config.realm,
1808 ads->config.bind_path));
1810 ads->config.current_time = ads_parse_time(timestr);
1812 if (ads->config.current_time != 0) {
1813 ads->auth.time_offset = ads->config.current_time - time(NULL);
1814 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
1817 talloc_destroy(ctx);
1824 * find the list of trusted domains
1825 * @param ads connection to ads server
1826 * @param mem_ctx TALLOC_CTX for allocating results
1827 * @param num_trusts pointer to number of trusts
1828 * @param names pointer to trusted domain name list
1829 * @param sids pointer to list of sids of trusted domains
1830 * @return the count of SIDs pulled
1832 ADS_STATUS ads_trusted_domains(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1838 const char *attrs[] = {"name", "flatname", "securityIdentifier",
1839 "trustDirection", NULL};
1846 status = ads_search(ads, &res, "(objectcategory=trustedDomain)", attrs);
1847 if (!ADS_ERR_OK(status)) return status;
1849 count = ads_count_replies(ads, res);
1851 ads_msgfree(ads, res);
1852 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1855 (*names) = talloc(mem_ctx, sizeof(char *) * count);
1856 (*alt_names) = talloc(mem_ctx, sizeof(char *) * count);
1857 (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * count);
1858 if (! *names || ! *sids) return ADS_ERROR(LDAP_NO_MEMORY);
1860 for (i=0, msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
1863 /* direction is a 2 bit bitfield, 1 means they trust us
1864 but we don't trust them, so we should not list them
1865 as users from that domain can't login */
1866 if (ads_pull_uint32(ads, msg, "trustDirection", &direction) &&
1871 (*names)[i] = ads_pull_string(ads, mem_ctx, msg, "name");
1872 (*alt_names)[i] = ads_pull_string(ads, mem_ctx, msg, "flatname");
1874 if ((*alt_names)[i] && (*alt_names)[i][0]) {
1875 /* we prefer the flatname as the primary name
1876 for consistency with RPC */
1877 char *name = (*alt_names)[i];
1878 (*alt_names)[i] = (*names)[i];
1881 if (ads_pull_sid(ads, msg, "securityIdentifier", &(*sids)[i])) {
1886 ads_msgfree(ads, res);
1894 * find the domain sid for our domain
1895 * @param ads connection to ads server
1896 * @param sid Pointer to domain sid
1897 * @return status of search
1899 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
1901 const char *attrs[] = {"objectSid", NULL};
1905 rc = ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
1907 if (!ADS_ERR_OK(rc)) return rc;
1908 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
1909 return ADS_ERROR_SYSTEM(ENOENT);
1911 ads_msgfree(ads, res);
1916 /* this is rather complex - we need to find the allternate (netbios) name
1917 for the domain, but there isn't a simple query to do this. Instead
1918 we look for the principle names on the DCs account and find one that has
1919 the right form, then extract the netbios name of the domain from that
1921 ADS_STATUS ads_workgroup_name(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char **workgroup)
1930 const char *attrs[] = {"servicePrincipalName", NULL};
1932 (*workgroup) = NULL;
1934 asprintf(&exp, "(&(objectclass=computer)(dnshostname=%s.%s))",
1935 ads->config.ldap_server_name, ads->config.realm);
1936 rc = ads_search(ads, &res, exp, attrs);
1939 if (!ADS_ERR_OK(rc)) {
1943 principles = ads_pull_strings(ads, mem_ctx, res, "servicePrincipalName");
1945 ads_msgfree(ads, res);
1948 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1951 asprintf(&prefix, "HOST/%s.%s/",
1952 ads->config.ldap_server_name,
1955 prefix_length = strlen(prefix);
1957 for (i=0;principles[i]; i++) {
1958 if (strncasecmp(principles[i], prefix, prefix_length) == 0 &&
1959 strcasecmp(ads->config.realm, principles[i]+prefix_length) != 0 &&
1960 !strchr(principles[i]+prefix_length, '.')) {
1961 /* found an alternate (short) name for the domain. */
1962 DEBUG(3,("Found alternate name '%s' for realm '%s'\n",
1963 principles[i]+prefix_length,
1964 ads->config.realm));
1965 (*workgroup) = talloc_strdup(mem_ctx, principles[i]+prefix_length);
1972 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);