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, pdc_ip;
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_pdc_ip(workgroup, &pdc_ip)) {
196 DEBUG(6,("ads_try_netbios: trying server '%s'\n",
198 if (ads_try_connect(ads, inet_ntoa(pdc_ip), LDAP_PORT))
202 /* now any DC, including backups */
203 if (get_dc_list(workgroup, &ip_list, &count)) {
204 for (i=0;i<count;i++) {
205 DEBUG(6,("ads_try_netbios: trying server '%s'\n",
206 inet_ntoa(ip_list[i])));
207 if (ads_try_connect(ads, inet_ntoa(ip_list[i]), LDAP_PORT)) {
219 * Connect to the LDAP server
220 * @param ads Pointer to an existing ADS_STRUCT
221 * @return status of connection
223 ADS_STATUS ads_connect(ADS_STRUCT *ads)
225 int version = LDAP_VERSION3;
228 ads->last_attempt = time(NULL);
231 /* try with a URL based server */
233 if (ads->server.ldap_uri &&
234 ads_try_connect_uri(ads)) {
238 /* try with a user specified server */
239 if (ads->server.ldap_server &&
240 ads_try_connect(ads, ads->server.ldap_server, LDAP_PORT)) {
244 /* try with a smb.conf ads server setting if we are connecting
245 to the primary workgroup or realm */
246 if (!ads->server.foreign &&
247 ads_try_connect(ads, lp_ads_server(), LDAP_PORT)) {
252 if (ads_try_dns(ads)) {
256 /* try via netbios lookups */
257 if (!lp_disable_netbios() && ads_try_netbios(ads)) {
261 return ADS_ERROR_SYSTEM(errno?errno:ENOENT);
264 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
266 status = ads_server_info(ads);
267 if (!ADS_ERR_OK(status)) {
268 DEBUG(1,("Failed to get ldap server info\n"));
272 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
274 if (!ads->auth.user_name) {
275 /* by default use the machine account */
276 extern pstring global_myname;
278 fstrcpy(myname, global_myname);
280 asprintf(&ads->auth.user_name, "HOST/%s", myname);
283 if (!ads->auth.realm) {
284 ads->auth.realm = strdup(ads->config.realm);
287 if (!ads->auth.kdc_server) {
288 ads->auth.kdc_server = strdup(inet_ntoa(ads->ldap_ip));
292 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
293 to MIT kerberos to work (tridge) */
296 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
297 setenv(env, ads->auth.kdc_server, 1);
302 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
306 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
307 return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
310 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
311 return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
314 return ads_sasl_bind(ads);
318 Duplicate a struct berval into talloc'ed memory
320 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
322 struct berval *value;
324 if (!in_val) return NULL;
326 value = talloc_zero(ctx, sizeof(struct berval));
327 if (in_val->bv_len == 0) return value;
329 value->bv_len = in_val->bv_len;
330 value->bv_val = talloc_memdup(ctx, in_val->bv_val, in_val->bv_len);
335 Make a values list out of an array of (struct berval *)
337 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
338 const struct berval **in_vals)
340 struct berval **values;
343 if (!in_vals) return NULL;
344 for (i=0; in_vals[i]; i++); /* count values */
345 values = (struct berval **) talloc_zero(ctx,
346 (i+1)*sizeof(struct berval *));
347 if (!values) return NULL;
349 for (i=0; in_vals[i]; i++) {
350 values[i] = dup_berval(ctx, in_vals[i]);
356 UTF8-encode a values list out of an array of (char *)
358 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
363 if (!in_vals) return NULL;
364 for (i=0; in_vals[i]; i++); /* count values */
365 values = (char ** ) talloc_zero(ctx, (i+1)*sizeof(char *));
366 if (!values) return NULL;
368 for (i=0; in_vals[i]; i++) {
369 push_utf8_talloc(ctx, &values[i], in_vals[i]);
375 Pull a (char *) array out of a UTF8-encoded values list
377 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
382 if (!in_vals) return NULL;
383 for (i=0; in_vals[i]; i++); /* count values */
384 values = (char **) talloc_zero(ctx, (i+1)*sizeof(char *));
385 if (!values) return NULL;
387 for (i=0; in_vals[i]; i++) {
388 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
394 * Do a search with paged results. cookie must be null on the first
395 * call, and then returned on each subsequent call. It will be null
396 * again when the entire search is complete
397 * @param ads connection to ads server
398 * @param bind_path Base dn for the search
399 * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
400 * @param exp Search expression - specified in local charset
401 * @param attrs Attributes to retrieve - specified in utf8 or ascii
402 * @param res ** which will contain results - free res* with ads_msgfree()
403 * @param count Number of entries retrieved on this page
404 * @param cookie The paged results cookie to be returned on subsequent calls
405 * @return status of search
407 ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
408 int scope, const char *exp,
409 const char **attrs, void **res,
410 int *count, void **cookie)
413 char *utf8_exp, *utf8_path, **search_attrs;
414 LDAPControl PagedResults, NoReferrals, *controls[3], **rcontrols;
415 BerElement *cookie_be = NULL;
416 struct berval *cookie_bv= NULL;
421 if (!(ctx = talloc_init()))
422 return ADS_ERROR(LDAP_NO_MEMORY);
424 /* 0 means the conversion worked but the result was empty
425 so we only fail if it's negative. In any case, it always
426 at least nulls out the dest */
427 if ((push_utf8_talloc(ctx, &utf8_exp, exp) < 0) ||
428 (push_utf8_talloc(ctx, &utf8_path, bind_path) < 0)) {
433 if (!attrs || !(*attrs))
436 /* This would be the utf8-encoded version...*/
437 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
438 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 char *servicePrincipalName[3] = {NULL, NULL, NULL};
1030 unsigned acct_control;
1032 if (!(ctx = talloc_init_named("machine_account")))
1033 return ADS_ERROR(LDAP_NO_MEMORY);
1035 ret = ADS_ERROR(LDAP_NO_MEMORY);
1037 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", hostname)))
1039 if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm)))
1041 ou_str = ads_ou_string(org_unit);
1043 DEBUG(1, ("ads_ou_string returned NULL (malloc failure?)\n"));
1046 new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", hostname, ou_str,
1047 ads->config.bind_path);
1048 servicePrincipalName[0] = talloc_asprintf(ctx, "HOST/%s", hostname);
1049 servicePrincipalName[1] = talloc_asprintf(ctx, "HOST/%s.%s",
1052 strlower(&servicePrincipalName[1][5]);
1058 if (!(samAccountName = talloc_asprintf(ctx, "%s$", hostname)))
1061 acct_control = UF_WORKSTATION_TRUST_ACCOUNT | UF_DONT_EXPIRE_PASSWD;
1062 #ifndef ENCTYPE_ARCFOUR_HMAC
1063 acct_control |= UF_USE_DES_KEY_ONLY;
1065 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control)))
1068 if (!(mods = ads_init_mods(ctx)))
1071 ads_mod_str(ctx, &mods, "cn", hostname);
1072 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1073 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1074 ads_mod_str(ctx, &mods, "userPrincipalName", host_upn);
1075 ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1076 ads_mod_str(ctx, &mods, "dNSHostName", hostname);
1077 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1078 ads_mod_str(ctx, &mods, "operatingSystem", "Samba");
1079 ads_mod_str(ctx, &mods, "operatingSystemVersion", VERSION);
1081 ads_gen_add(ads, new_dn, mods);
1082 ret = ads_set_machine_sd(ads, hostname, new_dn);
1085 talloc_destroy(ctx);
1090 dump a binary result from ldap
1092 static void dump_binary(const char *field, struct berval **values)
1095 for (i=0; values[i]; i++) {
1096 printf("%s: ", field);
1097 for (j=0; j<values[i]->bv_len; j++) {
1098 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1111 static void dump_guid(const char *field, struct berval **values)
1115 for (i=0; values[i]; i++) {
1116 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1117 printf("%s: %s\n", field, uuid_string_static(guid));
1122 dump a sid result from ldap
1124 static void dump_sid(const char *field, struct berval **values)
1127 for (i=0; values[i]; i++) {
1129 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1130 printf("%s: %s\n", field, sid_string_static(&sid));
1135 dump ntSecurityDescriptor
1137 static void dump_sd(const char *filed, struct berval **values)
1142 TALLOC_CTX *ctx = 0;
1144 if (!(ctx = talloc_init_named("sec_io_desc")))
1148 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1149 prs_append_data(&ps, values[0]->bv_val, values[0]->bv_len);
1153 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1155 talloc_destroy(ctx);
1158 if (psd) ads_disp_sd(psd);
1161 talloc_destroy(ctx);
1165 dump a string result from ldap
1167 static void dump_string(const char *field, char **values)
1170 for (i=0; values[i]; i++) {
1171 printf("%s: %s\n", field, values[i]);
1176 dump a field from LDAP on stdout
1180 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1185 void (*handler)(const char *, struct berval **);
1187 {"objectGUID", False, dump_guid},
1188 {"nTSecurityDescriptor", False, dump_sd},
1189 {"dnsRecord", False, dump_binary},
1190 {"objectSid", False, dump_sid},
1195 if (!field) { /* must be end of an entry */
1200 for (i=0; handlers[i].name; i++) {
1201 if (StrCaseCmp(handlers[i].name, field) == 0) {
1202 if (!values) /* first time, indicate string or not */
1203 return handlers[i].string;
1204 handlers[i].handler(field, (struct berval **) values);
1208 if (!handlers[i].name) {
1209 if (!values) /* first time, indicate string conversion */
1211 dump_string(field, (char **)values);
1217 * Dump a result from LDAP on stdout
1218 * used for debugging
1219 * @param ads connection to ads server
1220 * @param res Results to dump
1223 void ads_dump(ADS_STRUCT *ads, void *res)
1225 ads_process_results(ads, res, ads_dump_field, NULL);
1229 * Walk through results, calling a function for each entry found.
1230 * The function receives a field name, a berval * array of values,
1231 * and a data area passed through from the start. The function is
1232 * called once with null for field and values at the end of each
1234 * @param ads connection to ads server
1235 * @param res Results to process
1236 * @param fn Function for processing each result
1237 * @param data_area user-defined area to pass to function
1239 void ads_process_results(ADS_STRUCT *ads, void *res,
1240 BOOL(*fn)(char *, void **, void *),
1246 if (!(ctx = talloc_init()))
1249 for (msg = ads_first_entry(ads, res); msg;
1250 msg = ads_next_entry(ads, msg)) {
1254 for (utf8_field=ldap_first_attribute(ads->ld,
1255 (LDAPMessage *)msg,&b);
1257 utf8_field=ldap_next_attribute(ads->ld,
1258 (LDAPMessage *)msg,b)) {
1259 struct berval **ber_vals;
1260 char **str_vals, **utf8_vals;
1264 pull_utf8_talloc(ctx, &field, utf8_field);
1265 string = fn(field, NULL, data_area);
1268 utf8_vals = ldap_get_values(ads->ld,
1269 (LDAPMessage *)msg, field);
1270 str_vals = ads_pull_strvals(ctx,
1271 (const char **) utf8_vals);
1272 fn(field, (void **) str_vals, data_area);
1273 ldap_value_free(utf8_vals);
1275 ber_vals = ldap_get_values_len(ads->ld,
1276 (LDAPMessage *)msg, field);
1277 fn(field, (void **) ber_vals, data_area);
1279 ldap_value_free_len(ber_vals);
1281 ldap_memfree(utf8_field);
1284 talloc_destroy_pool(ctx);
1285 fn(NULL, NULL, data_area); /* completed an entry */
1288 talloc_destroy(ctx);
1292 * count how many replies are in a LDAPMessage
1293 * @param ads connection to ads server
1294 * @param res Results to count
1295 * @return number of replies
1297 int ads_count_replies(ADS_STRUCT *ads, void *res)
1299 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1303 * Join a machine to a realm
1304 * Creates the machine account and sets the machine password
1305 * @param ads connection to ads server
1306 * @param hostname name of host to add
1307 * @param org_unit Organizational unit to place machine in
1308 * @return status of join
1310 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *hostname, const char *org_unit)
1316 /* hostname must be lowercase */
1317 host = strdup(hostname);
1320 status = ads_find_machine_acct(ads, (void **)&res, host);
1321 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1322 DEBUG(0, ("Host account for %s already exists - deleting old account\n", host));
1323 status = ads_leave_realm(ads, host);
1324 if (!ADS_ERR_OK(status)) {
1325 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
1326 host, ads->config.realm));
1331 status = ads_add_machine_acct(ads, host, org_unit);
1332 if (!ADS_ERR_OK(status)) {
1333 DEBUG(0, ("ads_add_machine_acct: %s\n", ads_errstr(status)));
1337 status = ads_find_machine_acct(ads, (void **)&res, host);
1338 if (!ADS_ERR_OK(status)) {
1339 DEBUG(0, ("Host account test failed\n"));
1349 * Delete a machine from the realm
1350 * @param ads connection to ads server
1351 * @param hostname Machine to remove
1352 * @return status of delete
1354 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
1358 char *hostnameDN, *host;
1361 /* hostname must be lowercase */
1362 host = strdup(hostname);
1365 status = ads_find_machine_acct(ads, &res, host);
1366 if (!ADS_ERR_OK(status)) {
1367 DEBUG(0, ("Host account for %s does not exist.\n", host));
1371 hostnameDN = ads_get_dn(ads, (LDAPMessage *)res);
1372 rc = ldap_delete_s(ads->ld, hostnameDN);
1373 ads_memfree(ads, hostnameDN);
1374 if (rc != LDAP_SUCCESS) {
1375 return ADS_ERROR(rc);
1378 status = ads_find_machine_acct(ads, &res, host);
1379 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1380 DEBUG(0, ("Failed to remove host account.\n"));
1390 * add machine account to existing security descriptor
1391 * @param ads connection to ads server
1392 * @param hostname machine to add
1393 * @param dn DN of security descriptor
1396 ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
1398 const char *attrs[] = {"ntSecurityDescriptor", "objectSid", 0};
1401 struct berval bval = {0, NULL};
1404 LDAPMessage *res = 0;
1405 LDAPMessage *msg = 0;
1406 ADS_MODLIST mods = 0;
1412 TALLOC_CTX *ctx = 0;
1414 if (!ads) return ADS_ERROR(LDAP_SERVER_DOWN);
1416 ret = ADS_ERROR(LDAP_SUCCESS);
1418 if (asprintf(&exp, "(samAccountName=%s$)", hostname) == -1) {
1419 DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n"));
1420 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1423 ret = ads_search(ads, (void *) &res, exp, attrs);
1425 if (!ADS_ERR_OK(ret)) return ret;
1427 msg = ads_first_entry(ads, res);
1428 ads_pull_sid(ads, msg, attrs[1], &sid);
1429 if (!(ctx = talloc_init_named("sec_io_desc"))) {
1430 ret = ADS_ERROR(LDAP_NO_MEMORY);
1431 goto ads_set_sd_error;
1434 if (!ads_pull_sd(ads, ctx, msg, attrs[0], &psd)) {
1435 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1436 goto ads_set_sd_error;
1439 status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size);
1441 if (!NT_STATUS_IS_OK(status)) {
1442 ret = ADS_ERROR_NT(status);
1443 goto ads_set_sd_error;
1446 prs_init(&ps_wire, sd_size, ctx, MARSHALL);
1447 if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1)) {
1448 ret = ADS_ERROR(LDAP_NO_MEMORY);
1449 goto ads_set_sd_error;
1453 file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size);
1455 if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY);
1457 bval.bv_len = sd_size;
1458 bval.bv_val = prs_data_p(&ps_wire);
1459 ads_mod_ber(ctx, &mods, attrs[0], &bval);
1460 ret = ads_gen_mod(ads, dn, mods);
1463 ads_msgfree(ads, res);
1464 prs_mem_free(&ps_wire);
1465 talloc_destroy(ctx);
1470 * pull the first entry from a ADS result
1471 * @param ads connection to ads server
1472 * @param res Results of search
1473 * @return first entry from result
1475 void *ads_first_entry(ADS_STRUCT *ads, void *res)
1477 return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
1481 * pull the next entry from a ADS result
1482 * @param ads connection to ads server
1483 * @param res Results of search
1484 * @return next entry from result
1486 void *ads_next_entry(ADS_STRUCT *ads, void *res)
1488 return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
1492 * pull a single string from a ADS result
1493 * @param ads connection to ads server
1494 * @param mem_ctx TALLOC_CTX to use for allocating result string
1495 * @param msg Results of search
1496 * @param field Attribute to retrieve
1497 * @return Result string in talloc context
1499 char *ads_pull_string(ADS_STRUCT *ads,
1500 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1507 values = ldap_get_values(ads->ld, msg, field);
1508 if (!values) return NULL;
1511 rc = pull_utf8_talloc(mem_ctx, &ux_string,
1517 ldap_value_free(values);
1522 * pull an array of strings from a ADS result
1523 * @param ads connection to ads server
1524 * @param mem_ctx TALLOC_CTX to use for allocating result string
1525 * @param msg Results of search
1526 * @param field Attribute to retrieve
1527 * @return Result strings in talloc context
1529 char **ads_pull_strings(ADS_STRUCT *ads,
1530 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1536 values = ldap_get_values(ads->ld, msg, field);
1537 if (!values) return NULL;
1539 for (i=0;values[i];i++) /* noop */ ;
1542 ret = talloc(mem_ctx, sizeof(char *) * (n+1));
1545 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
1551 ldap_value_free(values);
1557 * pull a single uint32 from a ADS result
1558 * @param ads connection to ads server
1559 * @param msg Results of search
1560 * @param field Attribute to retrieve
1561 * @param v Pointer to int to store result
1562 * @return boolean inidicating success
1564 BOOL ads_pull_uint32(ADS_STRUCT *ads,
1565 void *msg, const char *field, uint32 *v)
1569 values = ldap_get_values(ads->ld, msg, field);
1570 if (!values) return False;
1572 ldap_value_free(values);
1576 *v = atoi(values[0]);
1577 ldap_value_free(values);
1582 * pull a single DOM_SID from a ADS result
1583 * @param ads connection to ads server
1584 * @param msg Results of search
1585 * @param field Attribute to retrieve
1586 * @param sid Pointer to sid to store result
1587 * @return boolean inidicating success
1589 BOOL ads_pull_sid(ADS_STRUCT *ads,
1590 void *msg, const char *field, DOM_SID *sid)
1592 struct berval **values;
1595 values = ldap_get_values_len(ads->ld, msg, field);
1597 if (!values) return False;
1600 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
1603 ldap_value_free_len(values);
1608 * pull an array of DOM_SIDs from a ADS result
1609 * @param ads connection to ads server
1610 * @param mem_ctx TALLOC_CTX for allocating sid array
1611 * @param msg Results of search
1612 * @param field Attribute to retrieve
1613 * @param sids pointer to sid array to allocate
1614 * @return the count of SIDs pulled
1616 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1617 void *msg, const char *field, DOM_SID **sids)
1619 struct berval **values;
1623 values = ldap_get_values_len(ads->ld, msg, field);
1625 if (!values) return 0;
1627 for (i=0; values[i]; i++) /* nop */ ;
1629 (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * i);
1632 for (i=0; values[i]; i++) {
1633 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
1637 ldap_value_free_len(values);
1642 * pull a SEC_DESC from a ADS result
1643 * @param ads connection to ads server
1644 * @param mem_ctx TALLOC_CTX for allocating sid array
1645 * @param msg Results of search
1646 * @param field Attribute to retrieve
1647 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
1648 * @return boolean inidicating success
1650 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1651 void *msg, const char *field, SEC_DESC **sd)
1653 struct berval **values;
1657 values = ldap_get_values_len(ads->ld, msg, field);
1659 if (!values) return False;
1662 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
1663 prs_append_data(&ps, values[0]->bv_val, values[0]->bv_len);
1666 ret = sec_io_desc("sd", sd, &ps, 1);
1669 ldap_value_free_len(values);
1674 * in order to support usernames longer than 21 characters we need to
1675 * use both the sAMAccountName and the userPrincipalName attributes
1676 * It seems that not all users have the userPrincipalName attribute set
1678 * @param ads connection to ads server
1679 * @param mem_ctx TALLOC_CTX for allocating sid array
1680 * @param msg Results of search
1681 * @return the username
1683 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
1687 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
1688 if (ret && (p = strchr(ret, '@'))) {
1692 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
1697 * find the update serial number - this is the core of the ldap cache
1698 * @param ads connection to ads server
1699 * @param ads connection to ADS server
1700 * @param usn Pointer to retrieved update serial number
1701 * @return status of search
1703 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
1705 const char *attrs[] = {"highestCommittedUSN", NULL};
1709 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1710 if (!ADS_ERR_OK(status)) return status;
1712 if (ads_count_replies(ads, res) != 1) {
1713 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1716 ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
1717 ads_msgfree(ads, res);
1721 /* parse a ADS timestring - typical string is
1722 '20020917091222.0Z0' which means 09:12.22 17th September
1724 static time_t ads_parse_time(const char *str)
1730 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
1731 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
1732 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
1743 * Find the servers name and realm - this can be done before authentication
1744 * The ldapServiceName field on w2k looks like this:
1745 * vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
1746 * @param ads connection to ads server
1747 * @return status of search
1749 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
1751 const char *attrs[] = {"ldapServiceName", "currentTime", NULL};
1759 if (!(ctx = talloc_init())) {
1760 return ADS_ERROR(LDAP_NO_MEMORY);
1763 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1764 if (!ADS_ERR_OK(status)) return status;
1766 value = ads_pull_string(ads, ctx, res, "ldapServiceName");
1768 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1771 timestr = ads_pull_string(ads, ctx, res, "currentTime");
1773 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1778 p = strchr(value, ':');
1780 talloc_destroy(ctx);
1781 DEBUG(1, ("ads_server_info: returned ldap server name did not contain a ':' so was deemed invalid\n"));
1782 return ADS_ERROR(LDAP_DECODING_ERROR);
1785 SAFE_FREE(ads->config.ldap_server_name);
1787 ads->config.ldap_server_name = strdup(p+1);
1788 p = strchr(ads->config.ldap_server_name, '$');
1789 if (!p || p[1] != '@') {
1790 talloc_destroy(ctx);
1791 SAFE_FREE(ads->config.ldap_server_name);
1792 DEBUG(1, ("ads_server_info: returned ldap server name did not contain '$@' so was deemed invalid\n"));
1793 return ADS_ERROR(LDAP_DECODING_ERROR);
1798 SAFE_FREE(ads->config.realm);
1799 SAFE_FREE(ads->config.bind_path);
1801 ads->config.realm = strdup(p+2);
1802 ads->config.bind_path = ads_build_dn(ads->config.realm);
1804 DEBUG(3,("got ldap server name %s@%s, using bind path: %s\n",
1805 ads->config.ldap_server_name, ads->config.realm,
1806 ads->config.bind_path));
1808 ads->config.current_time = ads_parse_time(timestr);
1810 if (ads->config.current_time != 0) {
1811 ads->auth.time_offset = ads->config.current_time - time(NULL);
1812 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
1815 talloc_destroy(ctx);
1822 * find the list of trusted domains
1823 * @param ads connection to ads server
1824 * @param mem_ctx TALLOC_CTX for allocating results
1825 * @param num_trusts pointer to number of trusts
1826 * @param names pointer to trusted domain name list
1827 * @param sids pointer to list of sids of trusted domains
1828 * @return the count of SIDs pulled
1830 ADS_STATUS ads_trusted_domains(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1836 const char *attrs[] = {"name", "flatname", "securityIdentifier",
1837 "trustDirection", NULL};
1844 status = ads_search(ads, &res, "(objectcategory=trustedDomain)", attrs);
1845 if (!ADS_ERR_OK(status)) return status;
1847 count = ads_count_replies(ads, res);
1849 ads_msgfree(ads, res);
1850 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1853 (*names) = talloc(mem_ctx, sizeof(char *) * count);
1854 (*alt_names) = talloc(mem_ctx, sizeof(char *) * count);
1855 (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * count);
1856 if (! *names || ! *sids) return ADS_ERROR(LDAP_NO_MEMORY);
1858 for (i=0, msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
1861 /* direction is a 2 bit bitfield, 1 means they trust us
1862 but we don't trust them, so we should not list them
1863 as users from that domain can't login */
1864 if (ads_pull_uint32(ads, msg, "trustDirection", &direction) &&
1869 (*names)[i] = ads_pull_string(ads, mem_ctx, msg, "name");
1870 (*alt_names)[i] = ads_pull_string(ads, mem_ctx, msg, "flatname");
1872 if ((*alt_names)[i] && (*alt_names)[i][0]) {
1873 /* we prefer the flatname as the primary name
1874 for consistency with RPC */
1875 char *name = (*alt_names)[i];
1876 (*alt_names)[i] = (*names)[i];
1879 if (ads_pull_sid(ads, msg, "securityIdentifier", &(*sids)[i])) {
1884 ads_msgfree(ads, res);
1892 * find the domain sid for our domain
1893 * @param ads connection to ads server
1894 * @param sid Pointer to domain sid
1895 * @return status of search
1897 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
1899 const char *attrs[] = {"objectSid", NULL};
1903 rc = ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
1905 if (!ADS_ERR_OK(rc)) return rc;
1906 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
1907 return ADS_ERROR_SYSTEM(ENOENT);
1909 ads_msgfree(ads, res);
1914 /* this is rather complex - we need to find the allternate (netbios) name
1915 for the domain, but there isn't a simple query to do this. Instead
1916 we look for the principle names on the DCs account and find one that has
1917 the right form, then extract the netbios name of the domain from that
1919 ADS_STATUS ads_workgroup_name(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char **workgroup)
1928 const char *attrs[] = {"servicePrincipalName", NULL};
1930 (*workgroup) = NULL;
1932 asprintf(&exp, "(&(objectclass=computer)(dnshostname=%s.%s))",
1933 ads->config.ldap_server_name, ads->config.realm);
1934 rc = ads_search(ads, &res, exp, attrs);
1937 if (!ADS_ERR_OK(rc)) {
1941 principles = ads_pull_strings(ads, mem_ctx, res, "servicePrincipalName");
1943 ads_msgfree(ads, res);
1946 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1949 asprintf(&prefix, "HOST/%s.%s/",
1950 ads->config.ldap_server_name,
1953 prefix_length = strlen(prefix);
1955 for (i=0;principles[i]; i++) {
1956 if (strncasecmp(principles[i], prefix, prefix_length) == 0 &&
1957 strcasecmp(ads->config.realm, principles[i]+prefix_length) != 0 &&
1958 !strchr(principles[i]+prefix_length, '.')) {
1959 /* found an alternate (short) name for the domain. */
1960 DEBUG(3,("Found alternate name '%s' for realm '%s'\n",
1961 principles[i]+prefix_length,
1962 ads->config.realm));
1963 (*workgroup) = talloc_strdup(mem_ctx, principles[i]+prefix_length);
1970 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);