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) {
95 if (!realm || !*realm) {
97 realm = ads->server.workgroup;
99 if (!realm || !*realm) {
101 realm = lp_workgroup();
107 DEBUG(6,("ads_try_dns: looking for realm '%s'\n", realm));
108 if (ldap_domain2hostlist(realm, &list) != LDAP_SUCCESS) {
112 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 if (!get_dc_list(True, workgroup, &ip_list, &count) &&
171 !get_dc_list(False, workgroup, &ip_list, &count)) {
175 for (i=0;i<count;i++) {
176 DEBUG(6,("ads_try_netbios: trying server '%s'\n", inet_ntoa(ip_list[i])));
177 if (ads_try_connect(ads, inet_ntoa(ip_list[i]), LDAP_PORT)) {
188 * Connect to the LDAP server
189 * @param ads Pointer to an existing ADS_STRUCT
190 * @return status of connection
192 ADS_STATUS ads_connect(ADS_STRUCT *ads)
194 int version = LDAP_VERSION3;
198 ads->last_attempt = time(NULL);
201 /* try with a user specified server */
202 if (ads->server.ldap_server &&
203 ads_try_connect(ads, ads->server.ldap_server, LDAP_PORT)) {
207 /* try with a smb.conf ads server setting if we are connecting
208 to the primary workgroup or realm */
209 if (!ads->server.foreign &&
210 ads_try_connect(ads, lp_ads_server(), LDAP_PORT)) {
215 if (ads_try_dns(ads)) {
219 /* try via netbios lookups */
220 if (!lp_disable_netbios() && ads_try_netbios(ads)) {
224 return ADS_ERROR_SYSTEM(errno?errno:ENOENT);
227 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
229 status = ads_server_info(ads);
230 if (!ADS_ERR_OK(status)) {
231 DEBUG(1,("Failed to get ldap server info\n"));
235 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
237 if (!ads->auth.user_name) {
238 /* by default use the machine account */
239 extern pstring global_myname;
241 fstrcpy(myname, global_myname);
243 asprintf(&ads->auth.user_name, "HOST/%s", myname);
246 if (!ads->auth.realm) {
247 ads->auth.realm = strdup(ads->config.realm);
250 if (!ads->auth.kdc_server) {
251 ads->auth.kdc_server = strdup(inet_ntoa(ads->ldap_ip));
255 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
256 to MIT kerberos to work (tridge) */
259 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
260 setenv(env, ads->auth.kdc_server, 1);
265 if (ads->auth.password) {
266 if ((code = ads_kinit_password(ads)))
267 return ADS_ERROR_KRB5(code);
270 if (ads->auth.no_bind) {
274 return ads_sasl_bind(ads);
278 Duplicate a struct berval into talloc'ed memory
280 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
282 struct berval *value;
284 if (!in_val) return NULL;
286 value = talloc_zero(ctx, sizeof(struct berval));
287 if (in_val->bv_len == 0) return value;
289 value->bv_len = in_val->bv_len;
290 value->bv_val = talloc_memdup(ctx, in_val->bv_val, in_val->bv_len);
295 Make a values list out of an array of (struct berval *)
297 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
298 const struct berval **in_vals)
300 struct berval **values;
303 if (!in_vals) return NULL;
304 for (i=0; in_vals[i]; i++); /* count values */
305 values = (struct berval **) talloc_zero(ctx,
306 (i+1)*sizeof(struct berval *));
307 if (!values) return NULL;
309 for (i=0; in_vals[i]; i++) {
310 values[i] = dup_berval(ctx, in_vals[i]);
316 UTF8-encode a values list out of an array of (char *)
318 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
323 if (!in_vals) return NULL;
324 for (i=0; in_vals[i]; i++); /* count values */
325 values = (char ** ) talloc_zero(ctx, (i+1)*sizeof(char *));
326 if (!values) return NULL;
328 for (i=0; in_vals[i]; i++) {
329 push_utf8_talloc(ctx, &values[i], in_vals[i]);
335 Pull a (char *) array out of a UTF8-encoded values list
337 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
342 if (!in_vals) return NULL;
343 for (i=0; in_vals[i]; i++); /* count values */
344 values = (char **) talloc_zero(ctx, (i+1)*sizeof(char *));
345 if (!values) return NULL;
347 for (i=0; in_vals[i]; i++) {
348 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
354 * Do a search with paged results. cookie must be null on the first
355 * call, and then returned on each subsequent call. It will be null
356 * again when the entire search is complete
357 * @param ads connection to ads server
358 * @param bind_path Base dn for the search
359 * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
360 * @param exp Search expression - specified in local charset
361 * @param attrs Attributes to retrieve - specified in utf8 or ascii
362 * @param res ** which will contain results - free res* with ads_msgfree()
363 * @param count Number of entries retrieved on this page
364 * @param cookie The paged results cookie to be returned on subsequent calls
365 * @return status of search
367 ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
368 int scope, const char *exp,
369 const char **attrs, void **res,
370 int *count, void **cookie)
373 char *utf8_exp, *utf8_path, **search_attrs;
374 LDAPControl PagedResults, NoReferrals, *controls[3], **rcontrols;
375 BerElement *cookie_be = NULL;
376 struct berval *cookie_bv= NULL;
381 if (!(ctx = talloc_init()))
382 return ADS_ERROR(LDAP_NO_MEMORY);
384 /* 0 means the conversion worked but the result was empty
385 so we only fail if it's negative. In any case, it always
386 at least nulls out the dest */
387 if ((push_utf8_talloc(ctx, &utf8_exp, exp) < 0) ||
388 (push_utf8_talloc(ctx, &utf8_path, bind_path) < 0)) {
393 if (!attrs || !(*attrs))
396 /* This would be the utf8-encoded version...*/
397 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
398 if (!(str_list_copy(&search_attrs, attrs)))
406 /* Paged results only available on ldap v3 or later */
407 ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
408 if (version < LDAP_VERSION3) {
409 rc = LDAP_NOT_SUPPORTED;
413 cookie_be = ber_alloc_t(LBER_USE_DER);
414 if (cookie && *cookie) {
415 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
416 ber_bvfree(*cookie); /* don't need it from last time */
419 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
421 ber_flatten(cookie_be, &cookie_bv);
422 PagedResults.ldctl_oid = ADS_PAGE_CTL_OID;
423 PagedResults.ldctl_iscritical = (char) 1;
424 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
425 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
427 NoReferrals.ldctl_oid = ADS_NO_REFERRALS_OID;
428 NoReferrals.ldctl_iscritical = (char) 0;
429 NoReferrals.ldctl_value.bv_len = 0;
430 NoReferrals.ldctl_value.bv_val = "";
433 controls[0] = &NoReferrals;
434 controls[1] = &PagedResults;
439 /* we need to disable referrals as the openldap libs don't
440 handle them and paged results at the same time. Using them
441 together results in the result record containing the server
442 page control being removed from the result list (tridge/jmcd)
444 leaving this in despite the control that says don't generate
445 referrals, in case the server doesn't support it (jmcd)
447 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
449 rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_exp,
450 search_attrs, 0, controls,
451 NULL, NULL, LDAP_NO_LIMIT, (LDAPMessage **)res);
453 ber_free(cookie_be, 1);
454 ber_bvfree(cookie_bv);
457 DEBUG(3,("ldap_search_ext_s(%s) -> %s\n", exp, ldap_err2string(rc)));
461 rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
462 NULL, &rcontrols, 0);
468 for (i=0; rcontrols[i]; i++) {
469 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
470 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
471 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
473 /* the berval is the cookie, but must be freed when
475 if (cookie_bv->bv_len) /* still more to do */
476 *cookie=ber_bvdup(cookie_bv);
479 ber_bvfree(cookie_bv);
480 ber_free(cookie_be, 1);
484 ldap_controls_free(rcontrols);
488 /* if/when we decide to utf8-encode attrs, take out this next line */
489 str_list_free(&search_attrs);
491 return ADS_ERROR(rc);
496 * Get all results for a search. This uses ads_do_paged_search() to return
497 * all entries in a large search.
498 * @param ads connection to ads server
499 * @param bind_path Base dn for the search
500 * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
501 * @param exp Search expression
502 * @param attrs Attributes to retrieve
503 * @param res ** which will contain results - free res* with ads_msgfree()
504 * @return status of search
506 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
507 int scope, const char *exp,
508 const char **attrs, void **res)
514 status = ads_do_paged_search(ads, bind_path, scope, exp, attrs, res,
517 if (!ADS_ERR_OK(status)) return status;
522 LDAPMessage *msg, *next;
524 status2 = ads_do_paged_search(ads, bind_path, scope, exp,
525 attrs, &res2, &count, &cookie);
527 if (!ADS_ERR_OK(status2)) break;
529 /* this relies on the way that ldap_add_result_entry() works internally. I hope
530 that this works on all ldap libs, but I have only tested with openldap */
531 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
532 next = ads_next_entry(ads, msg);
533 ldap_add_result_entry((LDAPMessage **)res, msg);
535 /* note that we do not free res2, as the memory is now
536 part of the main returned list */
543 * Run a function on all results for a search. Uses ads_do_paged_search() and
544 * runs the function as each page is returned, using ads_process_results()
545 * @param ads connection to ads server
546 * @param bind_path Base dn for the search
547 * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
548 * @param exp Search expression - specified in local charset
549 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
550 * @param fn Function which takes attr name, values list, and data_area
551 * @param data_area Pointer which is passed to function on each call
552 * @return status of search
554 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
555 int scope, const char *exp, const char **attrs,
556 BOOL(*fn)(char *, void **, void *),
564 status = ads_do_paged_search(ads, bind_path, scope, exp, attrs, &res,
567 if (!ADS_ERR_OK(status)) return status;
569 ads_process_results(ads, res, fn, data_area);
570 ads_msgfree(ads, res);
573 status = ads_do_paged_search(ads, bind_path, scope, exp, attrs,
574 &res, &count, &cookie);
576 if (!ADS_ERR_OK(status)) break;
578 ads_process_results(ads, res, fn, data_area);
579 ads_msgfree(ads, res);
586 * Do a search with a timeout.
587 * @param ads connection to ads server
588 * @param bind_path Base dn for the search
589 * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
590 * @param exp Search expression
591 * @param attrs Attributes to retrieve
592 * @param res ** which will contain results - free res* with ads_msgfree()
593 * @return status of search
595 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
597 const char **attrs, void **res)
599 struct timeval timeout;
601 char *utf8_exp, *utf8_path, **search_attrs = NULL;
604 if (!(ctx = talloc_init()))
605 return ADS_ERROR(LDAP_NO_MEMORY);
607 /* 0 means the conversion worked but the result was empty
608 so we only fail if it's negative. In any case, it always
609 at least nulls out the dest */
610 if ((push_utf8_talloc(ctx, &utf8_exp, exp) < 0) ||
611 (push_utf8_talloc(ctx, &utf8_path, bind_path) < 0)) {
616 if (!attrs || !(*attrs))
619 /* This would be the utf8-encoded version...*/
620 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
621 if (!(str_list_copy(&search_attrs, attrs)))
628 timeout.tv_sec = ADS_SEARCH_TIMEOUT;
632 /* see the note in ads_do_paged_search - we *must* disable referrals */
633 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
635 rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_exp,
636 search_attrs, 0, NULL, NULL,
637 &timeout, LDAP_NO_LIMIT, (LDAPMessage **)res);
639 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
640 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
646 /* if/when we decide to utf8-encode attrs, take out this next line */
647 str_list_free(&search_attrs);
648 return ADS_ERROR(rc);
651 * Do a general ADS search
652 * @param ads connection to ads server
653 * @param res ** which will contain results - free res* with ads_msgfree()
654 * @param exp Search expression
655 * @param attrs Attributes to retrieve
656 * @return status of search
658 ADS_STATUS ads_search(ADS_STRUCT *ads, void **res,
662 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
667 * Do a search on a specific DistinguishedName
668 * @param ads connection to ads server
669 * @param res ** which will contain results - free res* with ads_msgfree()
670 * @param dn DistinguishName to search
671 * @param attrs Attributes to retrieve
672 * @return status of search
674 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, void **res,
678 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res);
682 * Free up memory from a ads_search
683 * @param ads connection to ads server
684 * @param msg Search results to free
686 void ads_msgfree(ADS_STRUCT *ads, void *msg)
693 * Free up memory from various ads requests
694 * @param ads connection to ads server
695 * @param mem Area to free
697 void ads_memfree(ADS_STRUCT *ads, void *mem)
703 * Get a dn from search results
704 * @param ads connection to ads server
705 * @param res Search results
708 char *ads_get_dn(ADS_STRUCT *ads, void *res)
710 char *utf8_dn, *unix_dn;
712 utf8_dn = ldap_get_dn(ads->ld, res);
713 pull_utf8_allocate((void **) &unix_dn, utf8_dn);
714 ldap_memfree(utf8_dn);
719 * Find a machine account given a hostname
720 * @param ads connection to ads server
721 * @param res ** which will contain results - free res* with ads_msgfree()
722 * @param host Hostname to search for
723 * @return status of search
725 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *host)
729 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
731 /* the easiest way to find a machine account anywhere in the tree
732 is to look for hostname$ */
733 asprintf(&exp, "(samAccountName=%s$)", host);
734 status = ads_search(ads, res, exp, attrs);
740 * Initialize a list of mods to be used in a modify request
741 * @param ctx An initialized TALLOC_CTX
742 * @return allocated ADS_MODLIST
744 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
746 #define ADS_MODLIST_ALLOC_SIZE 10
749 if ((mods = (LDAPMod **) talloc_zero(ctx, sizeof(LDAPMod *) *
750 (ADS_MODLIST_ALLOC_SIZE + 1))))
751 /* -1 is safety to make sure we don't go over the end.
752 need to reset it to NULL before doing ldap modify */
753 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
760 add an attribute to the list, with values list already constructed
762 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
763 int mod_op, const char *name,
767 LDAPMod **modlist = (LDAPMod **) *mods;
772 mod_op = LDAP_MOD_DELETE;
774 if (mod_op & LDAP_MOD_BVALUES)
775 values = (void **) ads_dup_values(ctx,
776 (const struct berval **)invals);
778 values = (void **) ads_push_strvals(ctx,
779 (const char **) invals);
782 /* find the first empty slot */
783 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
785 if (modlist[curmod] == (LDAPMod *) -1) {
786 if (!(modlist = talloc_realloc(ctx, modlist,
787 (curmod+ADS_MODLIST_ALLOC_SIZE+1)*sizeof(LDAPMod *))))
788 return ADS_ERROR(LDAP_NO_MEMORY);
789 memset(&modlist[curmod], 0,
790 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
791 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
795 if (!(modlist[curmod] = talloc_zero(ctx, sizeof(LDAPMod))))
796 return ADS_ERROR(LDAP_NO_MEMORY);
797 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
798 if (mod_op & LDAP_MOD_BVALUES)
799 modlist[curmod]->mod_bvalues = (struct berval **) values;
801 modlist[curmod]->mod_values = (char **) values;
802 modlist[curmod]->mod_op = mod_op;
803 return ADS_ERROR(LDAP_SUCCESS);
807 * Add a single string value to a mod list
808 * @param ctx An initialized TALLOC_CTX
809 * @param mods An initialized ADS_MODLIST
810 * @param name The attribute name to add
811 * @param val The value to add - NULL means DELETE
812 * @return ADS STATUS indicating success of add
814 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
815 const char *name, const char *val)
817 const char *values[2] = {val, NULL};
819 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
820 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name,
821 (const void **) values);
825 * Add an array of string values to a mod list
826 * @param ctx An initialized TALLOC_CTX
827 * @param mods An initialized ADS_MODLIST
828 * @param name The attribute name to add
829 * @param vals The array of string values to add - NULL means DELETE
830 * @return ADS STATUS indicating success of add
832 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
833 const char *name, const char **vals)
836 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
837 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
838 name, (const void **) vals);
842 * Add a single ber-encoded value to a mod list
843 * @param ctx An initialized TALLOC_CTX
844 * @param mods An initialized ADS_MODLIST
845 * @param name The attribute name to add
846 * @param val The value to add - NULL means DELETE
847 * @return ADS STATUS indicating success of add
849 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
850 const char *name, const struct berval *val)
852 const struct berval *values[2] = {val, NULL};
854 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
855 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
856 name, (const void **) values);
860 * Perform an ldap modify
861 * @param ads connection to ads server
862 * @param mod_dn DistinguishedName to modify
863 * @param mods list of modifications to perform
864 * @return status of modify
866 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
869 char *utf8_dn = NULL;
871 this control is needed to modify that contains a currently
872 non-existent attribute (but allowable for the object) to run
874 LDAPControl PermitModify = {
875 "1.2.840.113556.1.4.1413",
878 LDAPControl *controls[2];
880 controls[0] = &PermitModify;
883 push_utf8_allocate((void **) &utf8_dn, mod_dn);
885 /* find the end of the list, marked by NULL or -1 */
886 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
887 /* make sure the end of the list is NULL */
889 ret = ldap_modify_ext_s(ads->ld, utf8_dn ? utf8_dn : mod_dn,
890 (LDAPMod **) mods, controls, NULL);
892 return ADS_ERROR(ret);
896 * Perform an ldap add
897 * @param ads connection to ads server
898 * @param new_dn DistinguishedName to add
899 * @param mods list of attributes and values for DN
900 * @return status of add
902 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
905 char *utf8_dn = NULL;
907 push_utf8_allocate((void **) &utf8_dn, new_dn);
909 /* find the end of the list, marked by NULL or -1 */
910 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
911 /* make sure the end of the list is NULL */
914 ret = ldap_add_s(ads->ld, utf8_dn ? utf8_dn : new_dn, mods);
916 return ADS_ERROR(ret);
920 * Delete a DistinguishedName
921 * @param ads connection to ads server
922 * @param new_dn DistinguishedName to delete
923 * @return status of delete
925 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
928 char *utf8_dn = NULL;
929 push_utf8_allocate((void **) &utf8_dn, del_dn);
930 ret = ldap_delete(ads->ld, utf8_dn ? utf8_dn : del_dn);
931 return ADS_ERROR(ret);
935 * Build an org unit string
936 * if org unit is Computers or blank then assume a container, otherwise
937 * assume a \ separated list of organisational units
938 * @param org_unit Organizational unit
939 * @return org unit string - caller must free
941 char *ads_ou_string(const char *org_unit)
943 if (!org_unit || !*org_unit || strcasecmp(org_unit, "Computers") == 0) {
944 return strdup("cn=Computers");
947 return ads_build_path(org_unit, "\\/", "ou=", 1);
953 add a machine account to the ADS server
955 static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *hostname,
956 const char *org_unit)
959 char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
963 const char *objectClass[] = {"top", "person", "organizationalPerson",
964 "user", "computer", NULL};
966 if (!(ctx = talloc_init_named("machine_account")))
967 return ADS_ERROR(LDAP_NO_MEMORY);
969 ret = ADS_ERROR(LDAP_NO_MEMORY);
971 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", hostname)))
973 if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm)))
975 ou_str = ads_ou_string(org_unit);
976 new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", hostname, ou_str,
977 ads->config.bind_path);
982 if (!(samAccountName = talloc_asprintf(ctx, "%s$", hostname)))
984 if (!(controlstr = talloc_asprintf(ctx, "%u",
985 UF_DONT_EXPIRE_PASSWD | UF_WORKSTATION_TRUST_ACCOUNT |
986 UF_TRUSTED_FOR_DELEGATION | UF_USE_DES_KEY_ONLY)))
989 if (!(mods = ads_init_mods(ctx)))
992 ads_mod_str(ctx, &mods, "cn", hostname);
993 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
994 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
995 ads_mod_str(ctx, &mods, "userPrincipalName", host_upn);
996 ads_mod_str(ctx, &mods, "servicePrincipalName", host_spn);
997 ads_mod_str(ctx, &mods, "dNSHostName", hostname);
998 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
999 ads_mod_str(ctx, &mods, "operatingSystem", "Samba");
1000 ads_mod_str(ctx, &mods, "operatingSystemVersion", VERSION);
1002 ads_gen_add(ads, new_dn, mods);
1003 ret = ads_set_machine_sd(ads, hostname, new_dn);
1006 talloc_destroy(ctx);
1011 dump a binary result from ldap
1013 static void dump_binary(const char *field, struct berval **values)
1016 for (i=0; values[i]; i++) {
1017 printf("%s: ", field);
1018 for (j=0; j<values[i]->bv_len; j++) {
1019 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1026 dump a sid result from ldap
1028 static void dump_sid(const char *field, struct berval **values)
1031 for (i=0; values[i]; i++) {
1033 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1034 printf("%s: %s\n", field, sid_string_static(&sid));
1039 dump ntSecurityDescriptor
1041 static void dump_sd(const char *filed, struct berval **values)
1046 TALLOC_CTX *ctx = 0;
1048 if (!(ctx = talloc_init_named("sec_io_desc")))
1052 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1053 prs_append_data(&ps, values[0]->bv_val, values[0]->bv_len);
1057 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1059 talloc_destroy(ctx);
1062 if (psd) ads_disp_sd(psd);
1065 talloc_destroy(ctx);
1069 dump a string result from ldap
1071 static void dump_string(const char *field, char **values)
1074 for (i=0; values[i]; i++) {
1075 printf("%s: %s\n", field, values[i]);
1080 dump a field from LDAP on stdout
1084 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1089 void (*handler)(const char *, struct berval **);
1091 {"objectGUID", False, dump_binary},
1092 {"nTSecurityDescriptor", False, dump_sd},
1093 {"dnsRecord", False, dump_binary},
1094 {"objectSid", False, dump_sid},
1099 if (!field) { /* must be end of an entry */
1104 for (i=0; handlers[i].name; i++) {
1105 if (StrCaseCmp(handlers[i].name, field) == 0) {
1106 if (!values) /* first time, indicate string or not */
1107 return handlers[i].string;
1108 handlers[i].handler(field, (struct berval **) values);
1112 if (!handlers[i].name) {
1113 if (!values) /* first time, indicate string conversion */
1115 dump_string(field, (char **)values);
1121 * Dump a result from LDAP on stdout
1122 * used for debugging
1123 * @param ads connection to ads server
1124 * @param res Results to dump
1127 void ads_dump(ADS_STRUCT *ads, void *res)
1129 ads_process_results(ads, res, ads_dump_field, NULL);
1133 * Walk through results, calling a function for each entry found.
1134 * The function receives a field name, a berval * array of values,
1135 * and a data area passed through from the start. The function is
1136 * called once with null for field and values at the end of each
1138 * @param ads connection to ads server
1139 * @param res Results to process
1140 * @param fn Function for processing each result
1141 * @param data_area user-defined area to pass to function
1143 void ads_process_results(ADS_STRUCT *ads, void *res,
1144 BOOL(*fn)(char *, void **, void *),
1150 if (!(ctx = talloc_init()))
1153 for (msg = ads_first_entry(ads, res); msg;
1154 msg = ads_next_entry(ads, msg)) {
1158 for (utf8_field=ldap_first_attribute(ads->ld,
1159 (LDAPMessage *)msg,&b);
1161 utf8_field=ldap_next_attribute(ads->ld,
1162 (LDAPMessage *)msg,b)) {
1163 struct berval **ber_vals;
1164 char **str_vals, **utf8_vals;
1168 pull_utf8_talloc(ctx, &field, utf8_field);
1169 string = fn(field, NULL, data_area);
1172 utf8_vals = ldap_get_values(ads->ld,
1173 (LDAPMessage *)msg, field);
1174 str_vals = ads_pull_strvals(ctx,
1175 (const char **) utf8_vals);
1176 fn(field, (void **) str_vals, data_area);
1177 ldap_value_free(utf8_vals);
1179 ber_vals = ldap_get_values_len(ads->ld,
1180 (LDAPMessage *)msg, field);
1181 fn(field, (void **) ber_vals, data_area);
1183 ldap_value_free_len(ber_vals);
1185 ldap_memfree(utf8_field);
1188 talloc_destroy_pool(ctx);
1189 fn(NULL, NULL, data_area); /* completed an entry */
1192 talloc_destroy(ctx);
1196 * count how many replies are in a LDAPMessage
1197 * @param ads connection to ads server
1198 * @param res Results to count
1199 * @return number of replies
1201 int ads_count_replies(ADS_STRUCT *ads, void *res)
1203 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1207 * Join a machine to a realm
1208 * Creates the machine account and sets the machine password
1209 * @param ads connection to ads server
1210 * @param hostname name of host to add
1211 * @param org_unit Organizational unit to place machine in
1212 * @return status of join
1214 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *hostname, const char *org_unit)
1220 /* hostname must be lowercase */
1221 host = strdup(hostname);
1224 status = ads_find_machine_acct(ads, (void **)&res, host);
1225 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1226 DEBUG(0, ("Host account for %s already exists - deleting old account\n", host));
1227 status = ads_leave_realm(ads, host);
1228 if (!ADS_ERR_OK(status)) {
1229 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
1230 host, ads->config.realm));
1235 status = ads_add_machine_acct(ads, host, org_unit);
1236 if (!ADS_ERR_OK(status)) {
1237 DEBUG(0, ("ads_add_machine_acct: %s\n", ads_errstr(status)));
1241 status = ads_find_machine_acct(ads, (void **)&res, host);
1242 if (!ADS_ERR_OK(status)) {
1243 DEBUG(0, ("Host account test failed\n"));
1253 * Delete a machine from the realm
1254 * @param ads connection to ads server
1255 * @param hostname Machine to remove
1256 * @return status of delete
1258 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
1262 char *hostnameDN, *host;
1265 /* hostname must be lowercase */
1266 host = strdup(hostname);
1269 status = ads_find_machine_acct(ads, &res, host);
1270 if (!ADS_ERR_OK(status)) {
1271 DEBUG(0, ("Host account for %s does not exist.\n", host));
1275 hostnameDN = ads_get_dn(ads, (LDAPMessage *)res);
1276 rc = ldap_delete_s(ads->ld, hostnameDN);
1277 ads_memfree(ads, hostnameDN);
1278 if (rc != LDAP_SUCCESS) {
1279 return ADS_ERROR(rc);
1282 status = ads_find_machine_acct(ads, &res, host);
1283 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1284 DEBUG(0, ("Failed to remove host account.\n"));
1294 * add machine account to existing security descriptor
1295 * @param ads connection to ads server
1296 * @param hostname machine to add
1297 * @param dn DN of security descriptor
1300 ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
1302 const char *attrs[] = {"ntSecurityDescriptor", "objectSid", 0};
1305 struct berval **bvals = 0;
1306 struct berval bval = {0, NULL};
1310 LDAPMessage *res = 0;
1311 LDAPMessage *msg = 0;
1312 ADS_MODLIST mods = 0;
1318 TALLOC_CTX *ctx = 0;
1320 if (!ads) return ADS_ERROR(LDAP_SERVER_DOWN);
1322 ret = ADS_ERROR(LDAP_SUCCESS);
1324 asprintf(&exp, "(samAccountName=%s$)", hostname);
1325 ret = ads_search(ads, (void *) &res, exp, attrs);
1327 if (!ADS_ERR_OK(ret)) return ret;
1329 msg = ads_first_entry(ads, res);
1330 bvals = ldap_get_values_len(ads->ld, msg, attrs[0]);
1331 ads_pull_sid(ads, msg, attrs[1], &sid);
1332 ads_msgfree(ads, res);
1334 file_save("/tmp/sec_desc.old", bvals[0]->bv_val, bvals[0]->bv_len);
1336 if (!(ctx = talloc_init_named("sec_io_desc")))
1337 return ADS_ERROR(LDAP_NO_MEMORY);
1339 prs_init(&ps, bvals[0]->bv_len, ctx, UNMARSHALL);
1340 prs_append_data(&ps, bvals[0]->bv_val, bvals[0]->bv_len);
1342 ldap_value_free_len(bvals);
1344 if (!sec_io_desc("sd", &psd, &ps, 1))
1345 goto ads_set_sd_error;
1347 status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size);
1349 if (!NT_STATUS_IS_OK(status))
1350 goto ads_set_sd_error;
1352 prs_init(&ps_wire, sd_size, ctx, MARSHALL);
1353 if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1))
1354 goto ads_set_sd_error;
1357 file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size);
1359 if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY);
1361 bval.bv_len = sd_size;
1362 bval.bv_val = prs_data_p(&ps_wire);
1363 ads_mod_ber(ctx, &mods, attrs[0], &bval);
1364 ret = ads_gen_mod(ads, dn, mods);
1367 prs_mem_free(&ps_wire);
1368 talloc_destroy(ctx);
1373 prs_mem_free(&ps_wire);
1374 talloc_destroy(ctx);
1375 return ADS_ERROR(LDAP_NO_MEMORY);
1379 * Set the machine account password
1380 * @param ads connection to ads server
1381 * @param hostname machine whose password is being set
1382 * @param password new password
1383 * @return status of password change
1385 ADS_STATUS ads_set_machine_password(ADS_STRUCT *ads,
1386 const char *hostname,
1387 const char *password)
1390 char *host = strdup(hostname);
1396 we need to use the '$' form of the name here, as otherwise the
1397 server might end up setting the password for a user instead
1399 asprintf(&principal, "%s$@%s", host, ads->auth.realm);
1401 status = krb5_set_password(ads->auth.kdc_server, principal, password);
1410 * pull the first entry from a ADS result
1411 * @param ads connection to ads server
1412 * @param res Results of search
1413 * @return first entry from result
1415 void *ads_first_entry(ADS_STRUCT *ads, void *res)
1417 return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
1421 * pull the next entry from a ADS result
1422 * @param ads connection to ads server
1423 * @param res Results of search
1424 * @return next entry from result
1426 void *ads_next_entry(ADS_STRUCT *ads, void *res)
1428 return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
1432 * pull a single string from a ADS result
1433 * @param ads connection to ads server
1434 * @param mem_ctx TALLOC_CTX to use for allocating result string
1435 * @param msg Results of search
1436 * @param field Attribute to retrieve
1437 * @return Result string in talloc context
1439 char *ads_pull_string(ADS_STRUCT *ads,
1440 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1447 values = ldap_get_values(ads->ld, msg, field);
1448 if (!values) return NULL;
1451 rc = pull_utf8_talloc(mem_ctx, &ux_string,
1457 ldap_value_free(values);
1462 * pull an array of strings from a ADS result
1463 * @param ads connection to ads server
1464 * @param mem_ctx TALLOC_CTX to use for allocating result string
1465 * @param msg Results of search
1466 * @param field Attribute to retrieve
1467 * @return Result strings in talloc context
1469 char **ads_pull_strings(ADS_STRUCT *ads,
1470 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1476 values = ldap_get_values(ads->ld, msg, field);
1477 if (!values) return NULL;
1479 for (i=0;values[i];i++) /* noop */ ;
1482 ret = talloc(mem_ctx, sizeof(char *) * (n+1));
1485 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
1491 ldap_value_free(values);
1497 * pull a single uint32 from a ADS result
1498 * @param ads connection to ads server
1499 * @param msg Results of search
1500 * @param field Attribute to retrieve
1501 * @param v Pointer to int to store result
1502 * @return boolean inidicating success
1504 BOOL ads_pull_uint32(ADS_STRUCT *ads,
1505 void *msg, const char *field, uint32 *v)
1509 values = ldap_get_values(ads->ld, msg, field);
1510 if (!values) return False;
1512 ldap_value_free(values);
1516 *v = atoi(values[0]);
1517 ldap_value_free(values);
1522 * pull a single DOM_SID from a ADS result
1523 * @param ads connection to ads server
1524 * @param msg Results of search
1525 * @param field Attribute to retrieve
1526 * @param sid Pointer to sid to store result
1527 * @return boolean inidicating success
1529 BOOL ads_pull_sid(ADS_STRUCT *ads,
1530 void *msg, const char *field, DOM_SID *sid)
1532 struct berval **values;
1535 values = ldap_get_values_len(ads->ld, msg, field);
1537 if (!values) return False;
1540 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
1543 ldap_value_free_len(values);
1548 * pull an array of DOM_SIDs from a ADS result
1549 * @param ads connection to ads server
1550 * @param mem_ctx TALLOC_CTX for allocating sid array
1551 * @param msg Results of search
1552 * @param field Attribute to retrieve
1553 * @param sids pointer to sid array to allocate
1554 * @return the count of SIDs pulled
1556 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1557 void *msg, const char *field, DOM_SID **sids)
1559 struct berval **values;
1563 values = ldap_get_values_len(ads->ld, msg, field);
1565 if (!values) return 0;
1567 for (i=0; values[i]; i++) /* nop */ ;
1569 (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * i);
1572 for (i=0; values[i]; i++) {
1573 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
1577 ldap_value_free_len(values);
1583 * find the update serial number - this is the core of the ldap cache
1584 * @param ads connection to ads server
1585 * @param ads connection to ADS server
1586 * @param usn Pointer to retrieved update serial number
1587 * @return status of search
1589 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
1591 const char *attrs[] = {"highestCommittedUSN", NULL};
1595 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1596 if (!ADS_ERR_OK(status)) return status;
1598 if (ads_count_replies(ads, res) != 1) {
1599 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1602 ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
1603 ads_msgfree(ads, res);
1609 * Find the servers name and realm - this can be done before authentication
1610 * The ldapServiceName field on w2k looks like this:
1611 * vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
1612 * @param ads connection to ads server
1613 * @return status of search
1615 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
1617 const char *attrs[] = {"ldapServiceName", NULL};
1623 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1624 if (!ADS_ERR_OK(status)) return status;
1626 values = ldap_get_values(ads->ld, res, "ldapServiceName");
1627 if (!values || !values[0]) return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1629 p = strchr(values[0], ':');
1631 ldap_value_free(values);
1633 return ADS_ERROR(LDAP_DECODING_ERROR);
1636 SAFE_FREE(ads->config.ldap_server_name);
1638 ads->config.ldap_server_name = strdup(p+1);
1639 p = strchr(ads->config.ldap_server_name, '$');
1640 if (!p || p[1] != '@') {
1641 ldap_value_free(values);
1643 SAFE_FREE(ads->config.ldap_server_name);
1644 return ADS_ERROR(LDAP_DECODING_ERROR);
1649 SAFE_FREE(ads->config.realm);
1650 SAFE_FREE(ads->config.bind_path);
1652 ads->config.realm = strdup(p+2);
1653 ads->config.bind_path = ads_build_dn(ads->config.realm);
1655 DEBUG(3,("got ldap server name %s@%s\n",
1656 ads->config.ldap_server_name, ads->config.realm));
1663 * find the list of trusted domains
1664 * @param ads connection to ads server
1665 * @param mem_ctx TALLOC_CTX for allocating results
1666 * @param num_trusts pointer to number of trusts
1667 * @param names pointer to trusted domain name list
1668 * @param sids pointer to list of sids of trusted domains
1669 * @return the count of SIDs pulled
1671 ADS_STATUS ads_trusted_domains(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1677 const char *attrs[] = {"name", "flatname", "securityIdentifier",
1678 "trustDirection", NULL};
1685 status = ads_search(ads, &res, "(objectcategory=trustedDomain)", attrs);
1686 if (!ADS_ERR_OK(status)) return status;
1688 count = ads_count_replies(ads, res);
1690 ads_msgfree(ads, res);
1691 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1694 (*names) = talloc(mem_ctx, sizeof(char *) * count);
1695 (*alt_names) = talloc(mem_ctx, sizeof(char *) * count);
1696 (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * count);
1697 if (! *names || ! *sids) return ADS_ERROR(LDAP_NO_MEMORY);
1699 for (i=0, msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
1702 /* direction is a 2 bit bitfield, 1 means they trust us
1703 but we don't trust them, so we should not list them
1704 as users from that domain can't login */
1705 if (ads_pull_uint32(ads, msg, "trustDirection", &direction) &&
1710 (*names)[i] = ads_pull_string(ads, mem_ctx, msg, "name");
1711 (*alt_names)[i] = ads_pull_string(ads, mem_ctx, msg, "flatname");
1713 if ((*alt_names)[i] && (*alt_names)[i][0]) {
1714 /* we prefer the flatname as the primary name
1715 for consistency with RPC */
1716 char *name = (*alt_names)[i];
1717 (*alt_names)[i] = (*names)[i];
1720 if (ads_pull_sid(ads, msg, "securityIdentifier", &(*sids)[i])) {
1725 ads_msgfree(ads, res);
1733 * find the domain sid for our domain
1734 * @param ads connection to ads server
1735 * @param sid Pointer to domain sid
1736 * @return status of search
1738 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
1740 const char *attrs[] = {"objectSid", NULL};
1744 rc = ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
1746 if (!ADS_ERR_OK(rc)) return rc;
1747 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
1748 return ADS_ERROR_SYSTEM(ENOENT);
1750 ads_msgfree(ads, res);
1755 /* this is rather complex - we need to find the allternate (netbios) name
1756 for the domain, but there isn't a simple query to do this. Instead
1757 we look for the principle names on the DCs account and find one that has
1758 the right form, then extract the netbios name of the domain from that
1760 ADS_STATUS ads_workgroup_name(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char **workgroup)
1769 const char *attrs[] = {"servicePrincipalName", NULL};
1771 (*workgroup) = NULL;
1773 asprintf(&exp, "(&(objectclass=computer)(dnshostname=%s.%s))",
1774 ads->config.ldap_server_name, ads->config.realm);
1775 rc = ads_search(ads, &res, exp, attrs);
1778 if (!ADS_ERR_OK(rc)) {
1782 principles = ads_pull_strings(ads, mem_ctx, res, "servicePrincipalName");
1784 ads_msgfree(ads, res);
1787 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1790 asprintf(&prefix, "HOST/%s.%s/",
1791 ads->config.ldap_server_name,
1794 prefix_length = strlen(prefix);
1796 for (i=0;principles[i]; i++) {
1797 if (strncasecmp(principles[i], prefix, prefix_length) == 0 &&
1798 strcasecmp(ads->config.realm, principles[i]+prefix_length) != 0 &&
1799 !strchr(principles[i]+prefix_length, '.')) {
1800 /* found an alternate (short) name for the domain. */
1801 DEBUG(3,("Found alternate name '%s' for realm '%s'\n",
1802 principles[i]+prefix_length,
1803 ads->config.realm));
1804 (*workgroup) = talloc_strdup(mem_ctx, principles[i]+prefix_length);
1811 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);