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);
69 /* used by the IP comparison function */
75 /* compare 2 ldap IPs by nearness to our interfaces - used in qsort */
76 static int ldap_ip_compare(struct ldap_ip *ip1, struct ldap_ip *ip2)
78 return ip_compare(&ip1->ip, &ip2->ip);
81 /* try connecting to a ldap server via DNS */
82 static BOOL ads_try_dns(ADS_STRUCT *ads)
87 struct ldap_ip *ip_list;
90 realm = ads->server.realm;
91 if (!realm || !*realm) {
94 if (!realm || !*realm) {
95 realm = ads->server.workgroup;
97 if (!realm || !*realm) {
98 realm = lp_workgroup();
103 realm = smb_xstrdup(realm);
105 DEBUG(6,("ads_try_dns: looking for realm '%s'\n", realm));
106 if (ldap_domain2hostlist(realm, &list) != LDAP_SUCCESS) {
111 DEBUG(6,("ads_try_dns: ldap realm '%s' host list '%s'\n", realm, list));
114 count = count_chars(list, ' ') + 1;
115 ip_list = malloc(count * sizeof(struct ldap_ip));
121 while (next_token(&ptr, tok, " ", sizeof(tok))) {
122 unsigned port = LDAP_PORT;
123 char *p = strchr(tok, ':');
128 ip_list[i].ip = *interpret_addr2(tok);
129 ip_list[i].port = port;
130 if (!is_zero_ip(ip_list[i].ip)) {
138 /* we sort the list of addresses by closeness to our interfaces. This
139 tries to prevent us using a DC on the other side of the country */
141 qsort(ip_list, count, sizeof(struct ldap_ip),
142 QSORT_CAST ldap_ip_compare);
145 for (i=0;i<count;i++) {
146 if (ads_try_connect(ads, inet_ntoa(ip_list[i].ip), ip_list[i].port)) {
156 /* try connecting to a ldap server via netbios */
157 static BOOL ads_try_netbios(ADS_STRUCT *ads)
159 struct in_addr *ip_list;
162 char *workgroup = ads->server.workgroup;
165 workgroup = lp_workgroup();
168 DEBUG(6,("ads_try_netbios: looking for workgroup '%s'\n", workgroup));
170 /* try the PDC first */
171 if (get_dc_list(True, workgroup, &ip_list, &count)) {
172 for (i=0;i<count;i++) {
173 DEBUG(6,("ads_try_netbios: trying server '%s'\n",
174 inet_ntoa(ip_list[i])));
175 if (ads_try_connect(ads, inet_ntoa(ip_list[i]), LDAP_PORT)) {
183 /* now any DC, including backups */
184 if (get_dc_list(False, workgroup, &ip_list, &count)) {
185 for (i=0;i<count;i++) {
186 DEBUG(6,("ads_try_netbios: trying server '%s'\n",
187 inet_ntoa(ip_list[i])));
188 if (ads_try_connect(ads, inet_ntoa(ip_list[i]), LDAP_PORT)) {
200 * Connect to the LDAP server
201 * @param ads Pointer to an existing ADS_STRUCT
202 * @return status of connection
204 ADS_STATUS ads_connect(ADS_STRUCT *ads)
206 int version = LDAP_VERSION3;
210 ads->last_attempt = time(NULL);
213 /* try with a user specified server */
214 if (ads->server.ldap_server &&
215 ads_try_connect(ads, ads->server.ldap_server, LDAP_PORT)) {
219 /* try with a smb.conf ads server setting if we are connecting
220 to the primary workgroup or realm */
221 if (!ads->server.foreign &&
222 ads_try_connect(ads, lp_ads_server(), LDAP_PORT)) {
227 if (ads_try_dns(ads)) {
231 /* try via netbios lookups */
232 if (!lp_disable_netbios() && ads_try_netbios(ads)) {
236 return ADS_ERROR_SYSTEM(errno?errno:ENOENT);
239 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
241 status = ads_server_info(ads);
242 if (!ADS_ERR_OK(status)) {
243 DEBUG(1,("Failed to get ldap server info\n"));
247 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
249 if (!ads->auth.user_name) {
250 /* by default use the machine account */
251 extern pstring global_myname;
253 fstrcpy(myname, global_myname);
255 asprintf(&ads->auth.user_name, "HOST/%s", myname);
258 if (!ads->auth.realm) {
259 ads->auth.realm = strdup(ads->config.realm);
262 if (!ads->auth.kdc_server) {
263 ads->auth.kdc_server = strdup(inet_ntoa(ads->ldap_ip));
267 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
268 to MIT kerberos to work (tridge) */
271 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
272 setenv(env, ads->auth.kdc_server, 1);
277 if (ads->auth.password) {
278 if ((code = ads_kinit_password(ads)))
279 return ADS_ERROR_KRB5(code);
282 if (ads->auth.no_bind) {
286 return ads_sasl_bind(ads);
290 Duplicate a struct berval into talloc'ed memory
292 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
294 struct berval *value;
296 if (!in_val) return NULL;
298 value = talloc_zero(ctx, sizeof(struct berval));
299 if (in_val->bv_len == 0) return value;
301 value->bv_len = in_val->bv_len;
302 value->bv_val = talloc_memdup(ctx, in_val->bv_val, in_val->bv_len);
307 Make a values list out of an array of (struct berval *)
309 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
310 const struct berval **in_vals)
312 struct berval **values;
315 if (!in_vals) return NULL;
316 for (i=0; in_vals[i]; i++); /* count values */
317 values = (struct berval **) talloc_zero(ctx,
318 (i+1)*sizeof(struct berval *));
319 if (!values) return NULL;
321 for (i=0; in_vals[i]; i++) {
322 values[i] = dup_berval(ctx, in_vals[i]);
328 UTF8-encode a values list out of an array of (char *)
330 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
335 if (!in_vals) return NULL;
336 for (i=0; in_vals[i]; i++); /* count values */
337 values = (char ** ) talloc_zero(ctx, (i+1)*sizeof(char *));
338 if (!values) return NULL;
340 for (i=0; in_vals[i]; i++) {
341 push_utf8_talloc(ctx, &values[i], in_vals[i]);
347 Pull a (char *) array out of a UTF8-encoded values list
349 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
354 if (!in_vals) return NULL;
355 for (i=0; in_vals[i]; i++); /* count values */
356 values = (char **) talloc_zero(ctx, (i+1)*sizeof(char *));
357 if (!values) return NULL;
359 for (i=0; in_vals[i]; i++) {
360 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
366 * Do a search with paged results. cookie must be null on the first
367 * call, and then returned on each subsequent call. It will be null
368 * again when the entire search is complete
369 * @param ads connection to ads server
370 * @param bind_path Base dn for the search
371 * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
372 * @param exp Search expression - specified in local charset
373 * @param attrs Attributes to retrieve - specified in utf8 or ascii
374 * @param res ** which will contain results - free res* with ads_msgfree()
375 * @param count Number of entries retrieved on this page
376 * @param cookie The paged results cookie to be returned on subsequent calls
377 * @return status of search
379 ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
380 int scope, const char *exp,
381 const char **attrs, void **res,
382 int *count, void **cookie)
385 char *utf8_exp, *utf8_path, **search_attrs;
386 LDAPControl PagedResults, NoReferrals, *controls[3], **rcontrols;
387 BerElement *cookie_be = NULL;
388 struct berval *cookie_bv= NULL;
393 if (!(ctx = talloc_init()))
394 return ADS_ERROR(LDAP_NO_MEMORY);
396 /* 0 means the conversion worked but the result was empty
397 so we only fail if it's negative. In any case, it always
398 at least nulls out the dest */
399 if ((push_utf8_talloc(ctx, &utf8_exp, exp) < 0) ||
400 (push_utf8_talloc(ctx, &utf8_path, bind_path) < 0)) {
405 if (!attrs || !(*attrs))
408 /* This would be the utf8-encoded version...*/
409 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
410 if (!(str_list_copy(&search_attrs, attrs)))
418 /* Paged results only available on ldap v3 or later */
419 ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
420 if (version < LDAP_VERSION3) {
421 rc = LDAP_NOT_SUPPORTED;
425 cookie_be = ber_alloc_t(LBER_USE_DER);
426 if (cookie && *cookie) {
427 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
428 ber_bvfree(*cookie); /* don't need it from last time */
431 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
433 ber_flatten(cookie_be, &cookie_bv);
434 PagedResults.ldctl_oid = ADS_PAGE_CTL_OID;
435 PagedResults.ldctl_iscritical = (char) 1;
436 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
437 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
439 NoReferrals.ldctl_oid = ADS_NO_REFERRALS_OID;
440 NoReferrals.ldctl_iscritical = (char) 0;
441 NoReferrals.ldctl_value.bv_len = 0;
442 NoReferrals.ldctl_value.bv_val = "";
445 controls[0] = &NoReferrals;
446 controls[1] = &PagedResults;
451 /* we need to disable referrals as the openldap libs don't
452 handle them and paged results at the same time. Using them
453 together results in the result record containing the server
454 page control being removed from the result list (tridge/jmcd)
456 leaving this in despite the control that says don't generate
457 referrals, in case the server doesn't support it (jmcd)
459 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
461 rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_exp,
462 search_attrs, 0, controls,
463 NULL, NULL, LDAP_NO_LIMIT, (LDAPMessage **)res);
465 ber_free(cookie_be, 1);
466 ber_bvfree(cookie_bv);
469 DEBUG(3,("ldap_search_ext_s(%s) -> %s\n", exp, ldap_err2string(rc)));
473 rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
474 NULL, &rcontrols, 0);
480 for (i=0; rcontrols[i]; i++) {
481 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
482 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
483 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
485 /* the berval is the cookie, but must be freed when
487 if (cookie_bv->bv_len) /* still more to do */
488 *cookie=ber_bvdup(cookie_bv);
491 ber_bvfree(cookie_bv);
492 ber_free(cookie_be, 1);
496 ldap_controls_free(rcontrols);
500 /* if/when we decide to utf8-encode attrs, take out this next line */
501 str_list_free(&search_attrs);
503 return ADS_ERROR(rc);
508 * Get all results for a search. This uses ads_do_paged_search() to return
509 * all entries in a large search.
510 * @param ads connection to ads server
511 * @param bind_path Base dn for the search
512 * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
513 * @param exp Search expression
514 * @param attrs Attributes to retrieve
515 * @param res ** which will contain results - free res* with ads_msgfree()
516 * @return status of search
518 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
519 int scope, const char *exp,
520 const char **attrs, void **res)
526 status = ads_do_paged_search(ads, bind_path, scope, exp, attrs, res,
529 if (!ADS_ERR_OK(status)) return status;
534 LDAPMessage *msg, *next;
536 status2 = ads_do_paged_search(ads, bind_path, scope, exp,
537 attrs, &res2, &count, &cookie);
539 if (!ADS_ERR_OK(status2)) break;
541 /* this relies on the way that ldap_add_result_entry() works internally. I hope
542 that this works on all ldap libs, but I have only tested with openldap */
543 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
544 next = ads_next_entry(ads, msg);
545 ldap_add_result_entry((LDAPMessage **)res, msg);
547 /* note that we do not free res2, as the memory is now
548 part of the main returned list */
555 * Run a function on all results for a search. Uses ads_do_paged_search() and
556 * runs the function as each page is returned, using ads_process_results()
557 * @param ads connection to ads server
558 * @param bind_path Base dn for the search
559 * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
560 * @param exp Search expression - specified in local charset
561 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
562 * @param fn Function which takes attr name, values list, and data_area
563 * @param data_area Pointer which is passed to function on each call
564 * @return status of search
566 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
567 int scope, const char *exp, const char **attrs,
568 BOOL(*fn)(char *, void **, void *),
576 status = ads_do_paged_search(ads, bind_path, scope, exp, attrs, &res,
579 if (!ADS_ERR_OK(status)) return status;
581 ads_process_results(ads, res, fn, data_area);
582 ads_msgfree(ads, res);
585 status = ads_do_paged_search(ads, bind_path, scope, exp, attrs,
586 &res, &count, &cookie);
588 if (!ADS_ERR_OK(status)) break;
590 ads_process_results(ads, res, fn, data_area);
591 ads_msgfree(ads, res);
598 * Do a search with a timeout.
599 * @param ads connection to ads server
600 * @param bind_path Base dn for the search
601 * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
602 * @param exp Search expression
603 * @param attrs Attributes to retrieve
604 * @param res ** which will contain results - free res* with ads_msgfree()
605 * @return status of search
607 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
609 const char **attrs, void **res)
611 struct timeval timeout;
613 char *utf8_exp, *utf8_path, **search_attrs = NULL;
616 if (!(ctx = talloc_init()))
617 return ADS_ERROR(LDAP_NO_MEMORY);
619 /* 0 means the conversion worked but the result was empty
620 so we only fail if it's negative. In any case, it always
621 at least nulls out the dest */
622 if ((push_utf8_talloc(ctx, &utf8_exp, exp) < 0) ||
623 (push_utf8_talloc(ctx, &utf8_path, bind_path) < 0)) {
628 if (!attrs || !(*attrs))
631 /* This would be the utf8-encoded version...*/
632 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
633 if (!(str_list_copy(&search_attrs, attrs)))
640 timeout.tv_sec = ADS_SEARCH_TIMEOUT;
644 /* see the note in ads_do_paged_search - we *must* disable referrals */
645 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
647 rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_exp,
648 search_attrs, 0, NULL, NULL,
649 &timeout, LDAP_NO_LIMIT, (LDAPMessage **)res);
651 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
652 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
658 /* if/when we decide to utf8-encode attrs, take out this next line */
659 str_list_free(&search_attrs);
660 return ADS_ERROR(rc);
663 * Do a general ADS search
664 * @param ads connection to ads server
665 * @param res ** which will contain results - free res* with ads_msgfree()
666 * @param exp Search expression
667 * @param attrs Attributes to retrieve
668 * @return status of search
670 ADS_STATUS ads_search(ADS_STRUCT *ads, void **res,
674 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
679 * Do a search on a specific DistinguishedName
680 * @param ads connection to ads server
681 * @param res ** which will contain results - free res* with ads_msgfree()
682 * @param dn DistinguishName to search
683 * @param attrs Attributes to retrieve
684 * @return status of search
686 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, void **res,
690 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res);
694 * Free up memory from a ads_search
695 * @param ads connection to ads server
696 * @param msg Search results to free
698 void ads_msgfree(ADS_STRUCT *ads, void *msg)
705 * Free up memory from various ads requests
706 * @param ads connection to ads server
707 * @param mem Area to free
709 void ads_memfree(ADS_STRUCT *ads, void *mem)
715 * Get a dn from search results
716 * @param ads connection to ads server
717 * @param res Search results
720 char *ads_get_dn(ADS_STRUCT *ads, void *res)
722 char *utf8_dn, *unix_dn;
724 utf8_dn = ldap_get_dn(ads->ld, res);
725 pull_utf8_allocate((void **) &unix_dn, utf8_dn);
726 ldap_memfree(utf8_dn);
731 * Find a machine account given a hostname
732 * @param ads connection to ads server
733 * @param res ** which will contain results - free res* with ads_msgfree()
734 * @param host Hostname to search for
735 * @return status of search
737 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *host)
741 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
743 /* the easiest way to find a machine account anywhere in the tree
744 is to look for hostname$ */
745 asprintf(&exp, "(samAccountName=%s$)", host);
746 status = ads_search(ads, res, exp, attrs);
752 * Initialize a list of mods to be used in a modify request
753 * @param ctx An initialized TALLOC_CTX
754 * @return allocated ADS_MODLIST
756 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
758 #define ADS_MODLIST_ALLOC_SIZE 10
761 if ((mods = (LDAPMod **) talloc_zero(ctx, sizeof(LDAPMod *) *
762 (ADS_MODLIST_ALLOC_SIZE + 1))))
763 /* -1 is safety to make sure we don't go over the end.
764 need to reset it to NULL before doing ldap modify */
765 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
772 add an attribute to the list, with values list already constructed
774 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
775 int mod_op, const char *name,
779 LDAPMod **modlist = (LDAPMod **) *mods;
784 mod_op = LDAP_MOD_DELETE;
786 if (mod_op & LDAP_MOD_BVALUES)
787 values = (void **) ads_dup_values(ctx,
788 (const struct berval **)invals);
790 values = (void **) ads_push_strvals(ctx,
791 (const char **) invals);
794 /* find the first empty slot */
795 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
797 if (modlist[curmod] == (LDAPMod *) -1) {
798 if (!(modlist = talloc_realloc(ctx, modlist,
799 (curmod+ADS_MODLIST_ALLOC_SIZE+1)*sizeof(LDAPMod *))))
800 return ADS_ERROR(LDAP_NO_MEMORY);
801 memset(&modlist[curmod], 0,
802 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
803 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
807 if (!(modlist[curmod] = talloc_zero(ctx, sizeof(LDAPMod))))
808 return ADS_ERROR(LDAP_NO_MEMORY);
809 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
810 if (mod_op & LDAP_MOD_BVALUES)
811 modlist[curmod]->mod_bvalues = (struct berval **) values;
813 modlist[curmod]->mod_values = (char **) values;
814 modlist[curmod]->mod_op = mod_op;
815 return ADS_ERROR(LDAP_SUCCESS);
819 * Add a single string value to a mod list
820 * @param ctx An initialized TALLOC_CTX
821 * @param mods An initialized ADS_MODLIST
822 * @param name The attribute name to add
823 * @param val The value to add - NULL means DELETE
824 * @return ADS STATUS indicating success of add
826 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
827 const char *name, const char *val)
829 const char *values[2] = {val, NULL};
831 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
832 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name,
833 (const void **) values);
837 * Add an array of string values to a mod list
838 * @param ctx An initialized TALLOC_CTX
839 * @param mods An initialized ADS_MODLIST
840 * @param name The attribute name to add
841 * @param vals The array of string values to add - NULL means DELETE
842 * @return ADS STATUS indicating success of add
844 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
845 const char *name, const char **vals)
848 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
849 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
850 name, (const void **) vals);
854 * Add a single ber-encoded value to a mod list
855 * @param ctx An initialized TALLOC_CTX
856 * @param mods An initialized ADS_MODLIST
857 * @param name The attribute name to add
858 * @param val The value to add - NULL means DELETE
859 * @return ADS STATUS indicating success of add
861 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
862 const char *name, const struct berval *val)
864 const struct berval *values[2] = {val, NULL};
866 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
867 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
868 name, (const void **) values);
872 * Perform an ldap modify
873 * @param ads connection to ads server
874 * @param mod_dn DistinguishedName to modify
875 * @param mods list of modifications to perform
876 * @return status of modify
878 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
881 char *utf8_dn = NULL;
883 this control is needed to modify that contains a currently
884 non-existent attribute (but allowable for the object) to run
886 LDAPControl PermitModify = {
887 "1.2.840.113556.1.4.1413",
890 LDAPControl *controls[2];
892 controls[0] = &PermitModify;
895 push_utf8_allocate((void **) &utf8_dn, mod_dn);
897 /* find the end of the list, marked by NULL or -1 */
898 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
899 /* make sure the end of the list is NULL */
901 ret = ldap_modify_ext_s(ads->ld, utf8_dn ? utf8_dn : mod_dn,
902 (LDAPMod **) mods, controls, NULL);
904 return ADS_ERROR(ret);
908 * Perform an ldap add
909 * @param ads connection to ads server
910 * @param new_dn DistinguishedName to add
911 * @param mods list of attributes and values for DN
912 * @return status of add
914 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
917 char *utf8_dn = NULL;
919 push_utf8_allocate((void **) &utf8_dn, new_dn);
921 /* find the end of the list, marked by NULL or -1 */
922 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
923 /* make sure the end of the list is NULL */
926 ret = ldap_add_s(ads->ld, utf8_dn ? utf8_dn : new_dn, mods);
928 return ADS_ERROR(ret);
932 * Delete a DistinguishedName
933 * @param ads connection to ads server
934 * @param new_dn DistinguishedName to delete
935 * @return status of delete
937 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
940 char *utf8_dn = NULL;
941 push_utf8_allocate((void **) &utf8_dn, del_dn);
942 ret = ldap_delete(ads->ld, utf8_dn ? utf8_dn : del_dn);
943 return ADS_ERROR(ret);
947 * Build an org unit string
948 * if org unit is Computers or blank then assume a container, otherwise
949 * assume a \ separated list of organisational units
950 * @param org_unit Organizational unit
951 * @return org unit string - caller must free
953 char *ads_ou_string(const char *org_unit)
955 if (!org_unit || !*org_unit || strcasecmp(org_unit, "Computers") == 0) {
956 return strdup("cn=Computers");
959 return ads_build_path(org_unit, "\\/", "ou=", 1);
965 add a machine account to the ADS server
967 static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *hostname,
968 const char *org_unit)
971 char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
975 const char *objectClass[] = {"top", "person", "organizationalPerson",
976 "user", "computer", NULL};
978 if (!(ctx = talloc_init_named("machine_account")))
979 return ADS_ERROR(LDAP_NO_MEMORY);
981 ret = ADS_ERROR(LDAP_NO_MEMORY);
983 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", hostname)))
985 if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm)))
987 ou_str = ads_ou_string(org_unit);
988 new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", hostname, ou_str,
989 ads->config.bind_path);
994 if (!(samAccountName = talloc_asprintf(ctx, "%s$", hostname)))
996 if (!(controlstr = talloc_asprintf(ctx, "%u",
997 UF_DONT_EXPIRE_PASSWD | UF_WORKSTATION_TRUST_ACCOUNT |
998 UF_TRUSTED_FOR_DELEGATION | UF_USE_DES_KEY_ONLY)))
1001 if (!(mods = ads_init_mods(ctx)))
1004 ads_mod_str(ctx, &mods, "cn", hostname);
1005 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1006 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1007 ads_mod_str(ctx, &mods, "userPrincipalName", host_upn);
1008 ads_mod_str(ctx, &mods, "servicePrincipalName", host_spn);
1009 ads_mod_str(ctx, &mods, "dNSHostName", hostname);
1010 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1011 ads_mod_str(ctx, &mods, "operatingSystem", "Samba");
1012 ads_mod_str(ctx, &mods, "operatingSystemVersion", VERSION);
1014 ads_gen_add(ads, new_dn, mods);
1015 ret = ads_set_machine_sd(ads, hostname, new_dn);
1018 talloc_destroy(ctx);
1023 dump a binary result from ldap
1025 static void dump_binary(const char *field, struct berval **values)
1028 for (i=0; values[i]; i++) {
1029 printf("%s: ", field);
1030 for (j=0; j<values[i]->bv_len; j++) {
1031 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1038 dump a sid result from ldap
1040 static void dump_sid(const char *field, struct berval **values)
1043 for (i=0; values[i]; i++) {
1045 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1046 printf("%s: %s\n", field, sid_string_static(&sid));
1051 dump ntSecurityDescriptor
1053 static void dump_sd(const char *filed, struct berval **values)
1058 TALLOC_CTX *ctx = 0;
1060 if (!(ctx = talloc_init_named("sec_io_desc")))
1064 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1065 prs_append_data(&ps, values[0]->bv_val, values[0]->bv_len);
1069 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1071 talloc_destroy(ctx);
1074 if (psd) ads_disp_sd(psd);
1077 talloc_destroy(ctx);
1081 dump a string result from ldap
1083 static void dump_string(const char *field, char **values)
1086 for (i=0; values[i]; i++) {
1087 printf("%s: %s\n", field, values[i]);
1092 dump a field from LDAP on stdout
1096 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1101 void (*handler)(const char *, struct berval **);
1103 {"objectGUID", False, dump_binary},
1104 {"nTSecurityDescriptor", False, dump_sd},
1105 {"dnsRecord", False, dump_binary},
1106 {"objectSid", False, dump_sid},
1111 if (!field) { /* must be end of an entry */
1116 for (i=0; handlers[i].name; i++) {
1117 if (StrCaseCmp(handlers[i].name, field) == 0) {
1118 if (!values) /* first time, indicate string or not */
1119 return handlers[i].string;
1120 handlers[i].handler(field, (struct berval **) values);
1124 if (!handlers[i].name) {
1125 if (!values) /* first time, indicate string conversion */
1127 dump_string(field, (char **)values);
1133 * Dump a result from LDAP on stdout
1134 * used for debugging
1135 * @param ads connection to ads server
1136 * @param res Results to dump
1139 void ads_dump(ADS_STRUCT *ads, void *res)
1141 ads_process_results(ads, res, ads_dump_field, NULL);
1145 * Walk through results, calling a function for each entry found.
1146 * The function receives a field name, a berval * array of values,
1147 * and a data area passed through from the start. The function is
1148 * called once with null for field and values at the end of each
1150 * @param ads connection to ads server
1151 * @param res Results to process
1152 * @param fn Function for processing each result
1153 * @param data_area user-defined area to pass to function
1155 void ads_process_results(ADS_STRUCT *ads, void *res,
1156 BOOL(*fn)(char *, void **, void *),
1162 if (!(ctx = talloc_init()))
1165 for (msg = ads_first_entry(ads, res); msg;
1166 msg = ads_next_entry(ads, msg)) {
1170 for (utf8_field=ldap_first_attribute(ads->ld,
1171 (LDAPMessage *)msg,&b);
1173 utf8_field=ldap_next_attribute(ads->ld,
1174 (LDAPMessage *)msg,b)) {
1175 struct berval **ber_vals;
1176 char **str_vals, **utf8_vals;
1180 pull_utf8_talloc(ctx, &field, utf8_field);
1181 string = fn(field, NULL, data_area);
1184 utf8_vals = ldap_get_values(ads->ld,
1185 (LDAPMessage *)msg, field);
1186 str_vals = ads_pull_strvals(ctx,
1187 (const char **) utf8_vals);
1188 fn(field, (void **) str_vals, data_area);
1189 ldap_value_free(utf8_vals);
1191 ber_vals = ldap_get_values_len(ads->ld,
1192 (LDAPMessage *)msg, field);
1193 fn(field, (void **) ber_vals, data_area);
1195 ldap_value_free_len(ber_vals);
1197 ldap_memfree(utf8_field);
1200 talloc_destroy_pool(ctx);
1201 fn(NULL, NULL, data_area); /* completed an entry */
1204 talloc_destroy(ctx);
1208 * count how many replies are in a LDAPMessage
1209 * @param ads connection to ads server
1210 * @param res Results to count
1211 * @return number of replies
1213 int ads_count_replies(ADS_STRUCT *ads, void *res)
1215 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1219 * Join a machine to a realm
1220 * Creates the machine account and sets the machine password
1221 * @param ads connection to ads server
1222 * @param hostname name of host to add
1223 * @param org_unit Organizational unit to place machine in
1224 * @return status of join
1226 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *hostname, const char *org_unit)
1232 /* hostname must be lowercase */
1233 host = strdup(hostname);
1236 status = ads_find_machine_acct(ads, (void **)&res, host);
1237 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1238 DEBUG(0, ("Host account for %s already exists - deleting old account\n", host));
1239 status = ads_leave_realm(ads, host);
1240 if (!ADS_ERR_OK(status)) {
1241 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
1242 host, ads->config.realm));
1247 status = ads_add_machine_acct(ads, host, org_unit);
1248 if (!ADS_ERR_OK(status)) {
1249 DEBUG(0, ("ads_add_machine_acct: %s\n", ads_errstr(status)));
1253 status = ads_find_machine_acct(ads, (void **)&res, host);
1254 if (!ADS_ERR_OK(status)) {
1255 DEBUG(0, ("Host account test failed\n"));
1265 * Delete a machine from the realm
1266 * @param ads connection to ads server
1267 * @param hostname Machine to remove
1268 * @return status of delete
1270 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
1274 char *hostnameDN, *host;
1277 /* hostname must be lowercase */
1278 host = strdup(hostname);
1281 status = ads_find_machine_acct(ads, &res, host);
1282 if (!ADS_ERR_OK(status)) {
1283 DEBUG(0, ("Host account for %s does not exist.\n", host));
1287 hostnameDN = ads_get_dn(ads, (LDAPMessage *)res);
1288 rc = ldap_delete_s(ads->ld, hostnameDN);
1289 ads_memfree(ads, hostnameDN);
1290 if (rc != LDAP_SUCCESS) {
1291 return ADS_ERROR(rc);
1294 status = ads_find_machine_acct(ads, &res, host);
1295 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1296 DEBUG(0, ("Failed to remove host account.\n"));
1306 * add machine account to existing security descriptor
1307 * @param ads connection to ads server
1308 * @param hostname machine to add
1309 * @param dn DN of security descriptor
1312 ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
1314 const char *attrs[] = {"ntSecurityDescriptor", "objectSid", 0};
1317 struct berval **bvals = 0;
1318 struct berval bval = {0, NULL};
1322 LDAPMessage *res = 0;
1323 LDAPMessage *msg = 0;
1324 ADS_MODLIST mods = 0;
1330 TALLOC_CTX *ctx = 0;
1332 if (!ads) return ADS_ERROR(LDAP_SERVER_DOWN);
1334 ret = ADS_ERROR(LDAP_SUCCESS);
1336 asprintf(&exp, "(samAccountName=%s$)", hostname);
1337 ret = ads_search(ads, (void *) &res, exp, attrs);
1339 if (!ADS_ERR_OK(ret)) return ret;
1341 msg = ads_first_entry(ads, res);
1342 bvals = ldap_get_values_len(ads->ld, msg, attrs[0]);
1343 ads_pull_sid(ads, msg, attrs[1], &sid);
1344 ads_msgfree(ads, res);
1346 file_save("/tmp/sec_desc.old", bvals[0]->bv_val, bvals[0]->bv_len);
1348 if (!(ctx = talloc_init_named("sec_io_desc")))
1349 return ADS_ERROR(LDAP_NO_MEMORY);
1351 prs_init(&ps, bvals[0]->bv_len, ctx, UNMARSHALL);
1352 prs_append_data(&ps, bvals[0]->bv_val, bvals[0]->bv_len);
1354 ldap_value_free_len(bvals);
1356 if (!sec_io_desc("sd", &psd, &ps, 1))
1357 goto ads_set_sd_error;
1359 status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size);
1361 if (!NT_STATUS_IS_OK(status))
1362 goto ads_set_sd_error;
1364 prs_init(&ps_wire, sd_size, ctx, MARSHALL);
1365 if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1))
1366 goto ads_set_sd_error;
1369 file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size);
1371 if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY);
1373 bval.bv_len = sd_size;
1374 bval.bv_val = prs_data_p(&ps_wire);
1375 ads_mod_ber(ctx, &mods, attrs[0], &bval);
1376 ret = ads_gen_mod(ads, dn, mods);
1379 prs_mem_free(&ps_wire);
1380 talloc_destroy(ctx);
1385 prs_mem_free(&ps_wire);
1386 talloc_destroy(ctx);
1387 return ADS_ERROR(LDAP_NO_MEMORY);
1391 * Set the machine account password
1392 * @param ads connection to ads server
1393 * @param hostname machine whose password is being set
1394 * @param password new password
1395 * @return status of password change
1397 ADS_STATUS ads_set_machine_password(ADS_STRUCT *ads,
1398 const char *hostname,
1399 const char *password)
1402 char *host = strdup(hostname);
1408 we need to use the '$' form of the name here, as otherwise the
1409 server might end up setting the password for a user instead
1411 asprintf(&principal, "%s$@%s", host, ads->auth.realm);
1413 status = krb5_set_password(ads->auth.kdc_server, principal, password);
1422 * pull the first entry from a ADS result
1423 * @param ads connection to ads server
1424 * @param res Results of search
1425 * @return first entry from result
1427 void *ads_first_entry(ADS_STRUCT *ads, void *res)
1429 return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
1433 * pull the next entry from a ADS result
1434 * @param ads connection to ads server
1435 * @param res Results of search
1436 * @return next entry from result
1438 void *ads_next_entry(ADS_STRUCT *ads, void *res)
1440 return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
1444 * pull a single string from a ADS result
1445 * @param ads connection to ads server
1446 * @param mem_ctx TALLOC_CTX to use for allocating result string
1447 * @param msg Results of search
1448 * @param field Attribute to retrieve
1449 * @return Result string in talloc context
1451 char *ads_pull_string(ADS_STRUCT *ads,
1452 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1459 values = ldap_get_values(ads->ld, msg, field);
1460 if (!values) return NULL;
1463 rc = pull_utf8_talloc(mem_ctx, &ux_string,
1469 ldap_value_free(values);
1474 * pull an array of strings from a ADS result
1475 * @param ads connection to ads server
1476 * @param mem_ctx TALLOC_CTX to use for allocating result string
1477 * @param msg Results of search
1478 * @param field Attribute to retrieve
1479 * @return Result strings in talloc context
1481 char **ads_pull_strings(ADS_STRUCT *ads,
1482 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1488 values = ldap_get_values(ads->ld, msg, field);
1489 if (!values) return NULL;
1491 for (i=0;values[i];i++) /* noop */ ;
1494 ret = talloc(mem_ctx, sizeof(char *) * (n+1));
1497 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
1503 ldap_value_free(values);
1509 * pull a single uint32 from a ADS result
1510 * @param ads connection to ads server
1511 * @param msg Results of search
1512 * @param field Attribute to retrieve
1513 * @param v Pointer to int to store result
1514 * @return boolean inidicating success
1516 BOOL ads_pull_uint32(ADS_STRUCT *ads,
1517 void *msg, const char *field, uint32 *v)
1521 values = ldap_get_values(ads->ld, msg, field);
1522 if (!values) return False;
1524 ldap_value_free(values);
1528 *v = atoi(values[0]);
1529 ldap_value_free(values);
1534 * pull a single DOM_SID from a ADS result
1535 * @param ads connection to ads server
1536 * @param msg Results of search
1537 * @param field Attribute to retrieve
1538 * @param sid Pointer to sid to store result
1539 * @return boolean inidicating success
1541 BOOL ads_pull_sid(ADS_STRUCT *ads,
1542 void *msg, const char *field, DOM_SID *sid)
1544 struct berval **values;
1547 values = ldap_get_values_len(ads->ld, msg, field);
1549 if (!values) return False;
1552 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
1555 ldap_value_free_len(values);
1560 * pull an array of DOM_SIDs from a ADS result
1561 * @param ads connection to ads server
1562 * @param mem_ctx TALLOC_CTX for allocating sid array
1563 * @param msg Results of search
1564 * @param field Attribute to retrieve
1565 * @param sids pointer to sid array to allocate
1566 * @return the count of SIDs pulled
1568 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1569 void *msg, const char *field, DOM_SID **sids)
1571 struct berval **values;
1575 values = ldap_get_values_len(ads->ld, msg, field);
1577 if (!values) return 0;
1579 for (i=0; values[i]; i++) /* nop */ ;
1581 (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * i);
1584 for (i=0; values[i]; i++) {
1585 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
1589 ldap_value_free_len(values);
1595 * find the update serial number - this is the core of the ldap cache
1596 * @param ads connection to ads server
1597 * @param ads connection to ADS server
1598 * @param usn Pointer to retrieved update serial number
1599 * @return status of search
1601 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
1603 const char *attrs[] = {"highestCommittedUSN", NULL};
1607 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1608 if (!ADS_ERR_OK(status)) return status;
1610 if (ads_count_replies(ads, res) != 1) {
1611 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1614 ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
1615 ads_msgfree(ads, res);
1621 * Find the servers name and realm - this can be done before authentication
1622 * The ldapServiceName field on w2k looks like this:
1623 * vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
1624 * @param ads connection to ads server
1625 * @return status of search
1627 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
1629 const char *attrs[] = {"ldapServiceName", NULL};
1635 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1636 if (!ADS_ERR_OK(status)) return status;
1638 values = ldap_get_values(ads->ld, res, "ldapServiceName");
1639 if (!values || !values[0]) return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1641 p = strchr(values[0], ':');
1643 ldap_value_free(values);
1645 return ADS_ERROR(LDAP_DECODING_ERROR);
1648 SAFE_FREE(ads->config.ldap_server_name);
1650 ads->config.ldap_server_name = strdup(p+1);
1651 p = strchr(ads->config.ldap_server_name, '$');
1652 if (!p || p[1] != '@') {
1653 ldap_value_free(values);
1655 SAFE_FREE(ads->config.ldap_server_name);
1656 return ADS_ERROR(LDAP_DECODING_ERROR);
1661 SAFE_FREE(ads->config.realm);
1662 SAFE_FREE(ads->config.bind_path);
1664 ads->config.realm = strdup(p+2);
1665 ads->config.bind_path = ads_build_dn(ads->config.realm);
1667 DEBUG(3,("got ldap server name %s@%s\n",
1668 ads->config.ldap_server_name, ads->config.realm));
1675 * find the list of trusted domains
1676 * @param ads connection to ads server
1677 * @param mem_ctx TALLOC_CTX for allocating results
1678 * @param num_trusts pointer to number of trusts
1679 * @param names pointer to trusted domain name list
1680 * @param sids pointer to list of sids of trusted domains
1681 * @return the count of SIDs pulled
1683 ADS_STATUS ads_trusted_domains(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1689 const char *attrs[] = {"name", "flatname", "securityIdentifier",
1690 "trustDirection", NULL};
1697 status = ads_search(ads, &res, "(objectcategory=trustedDomain)", attrs);
1698 if (!ADS_ERR_OK(status)) return status;
1700 count = ads_count_replies(ads, res);
1702 ads_msgfree(ads, res);
1703 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1706 (*names) = talloc(mem_ctx, sizeof(char *) * count);
1707 (*alt_names) = talloc(mem_ctx, sizeof(char *) * count);
1708 (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * count);
1709 if (! *names || ! *sids) return ADS_ERROR(LDAP_NO_MEMORY);
1711 for (i=0, msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
1714 /* direction is a 2 bit bitfield, 1 means they trust us
1715 but we don't trust them, so we should not list them
1716 as users from that domain can't login */
1717 if (ads_pull_uint32(ads, msg, "trustDirection", &direction) &&
1722 (*names)[i] = ads_pull_string(ads, mem_ctx, msg, "name");
1723 (*alt_names)[i] = ads_pull_string(ads, mem_ctx, msg, "flatname");
1725 if ((*alt_names)[i] && (*alt_names)[i][0]) {
1726 /* we prefer the flatname as the primary name
1727 for consistency with RPC */
1728 char *name = (*alt_names)[i];
1729 (*alt_names)[i] = (*names)[i];
1732 if (ads_pull_sid(ads, msg, "securityIdentifier", &(*sids)[i])) {
1737 ads_msgfree(ads, res);
1745 * find the domain sid for our domain
1746 * @param ads connection to ads server
1747 * @param sid Pointer to domain sid
1748 * @return status of search
1750 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
1752 const char *attrs[] = {"objectSid", NULL};
1756 rc = ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
1758 if (!ADS_ERR_OK(rc)) return rc;
1759 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
1760 return ADS_ERROR_SYSTEM(ENOENT);
1762 ads_msgfree(ads, res);
1767 /* this is rather complex - we need to find the allternate (netbios) name
1768 for the domain, but there isn't a simple query to do this. Instead
1769 we look for the principle names on the DCs account and find one that has
1770 the right form, then extract the netbios name of the domain from that
1772 ADS_STATUS ads_workgroup_name(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char **workgroup)
1781 const char *attrs[] = {"servicePrincipalName", NULL};
1783 (*workgroup) = NULL;
1785 asprintf(&exp, "(&(objectclass=computer)(dnshostname=%s.%s))",
1786 ads->config.ldap_server_name, ads->config.realm);
1787 rc = ads_search(ads, &res, exp, attrs);
1790 if (!ADS_ERR_OK(rc)) {
1794 principles = ads_pull_strings(ads, mem_ctx, res, "servicePrincipalName");
1796 ads_msgfree(ads, res);
1799 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1802 asprintf(&prefix, "HOST/%s.%s/",
1803 ads->config.ldap_server_name,
1806 prefix_length = strlen(prefix);
1808 for (i=0;principles[i]; i++) {
1809 if (strncasecmp(principles[i], prefix, prefix_length) == 0 &&
1810 strcasecmp(ads->config.realm, principles[i]+prefix_length) != 0 &&
1811 !strchr(principles[i]+prefix_length, '.')) {
1812 /* found an alternate (short) name for the domain. */
1813 DEBUG(3,("Found alternate name '%s' for realm '%s'\n",
1814 principles[i]+prefix_length,
1815 ads->config.realm));
1816 (*workgroup) = talloc_strdup(mem_ctx, principles[i]+prefix_length);
1823 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);