2 Unix SMB/CIFS implementation.
3 ads (active directory) utility library
4 Copyright (C) Andrew Tridgell 2001
5 Copyright (C) Remus Koos 2001
6 Copyright (C) Jim McDonough 2002
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 * @brief basic ldap client-side routines for ads server communications
31 * The routines contained here should do the necessary ldap calls for
34 * Important note: attribute names passed into ads_ routines must
35 * already be in UTF-8 format. We do not convert them because in almost
36 * all cases, they are just ascii (which is represented with the same
37 * codepoints in UTF-8). This may have to change at some point
42 try a connection to a given ldap server, returning True and setting the servers IP
43 in the ads struct if successful
45 static BOOL ads_try_connect(ADS_STRUCT *ads, const char *server, unsigned port)
49 if (!server || !*server) {
53 DEBUG(5,("ads_try_connect: trying ldap server '%s' port %u\n", server, port));
55 /* this copes with inet_ntoa brokenness */
58 ads->ld = ldap_open(srv, port);
63 ads->ldap_port = port;
64 ads->ldap_ip = *interpret_addr2(srv);
71 try a connection to a given ldap server, based on URL, returning True if successful
73 static BOOL ads_try_connect_uri(ADS_STRUCT *ads)
75 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
76 DEBUG(5,("ads_try_connect: trying ldap server at URI '%s'\n",
77 ads->server.ldap_uri));
80 if (ldap_initialize((LDAP**)&(ads->ld), ads->server.ldap_uri) == LDAP_SUCCESS) {
83 DEBUG(0, ("ldap_initialize: %s\n", strerror(errno)));
87 DEBUG(1, ("no URL support in LDAP libs!\n"));
93 /* used by the IP comparison function */
99 /* compare 2 ldap IPs by nearness to our interfaces - used in qsort */
100 static int ldap_ip_compare(struct ldap_ip *ip1, struct ldap_ip *ip2)
102 return ip_compare(&ip1->ip, &ip2->ip);
105 /* try connecting to a ldap server via DNS */
106 static BOOL ads_try_dns(ADS_STRUCT *ads)
113 struct ldap_ip *ip_list;
116 c_realm = ads->server.realm;
117 if (!c_realm || !*c_realm) {
118 c_realm = lp_realm();
120 if (!c_realm || !*c_realm) {
121 c_realm = ads->server.workgroup;
123 if (!c_realm || !*c_realm) {
124 c_realm = lp_workgroup();
129 realm = smb_xstrdup(c_realm);
131 DEBUG(6,("ads_try_dns: looking for realm '%s'\n", realm));
132 if (ldap_domain2hostlist(realm, &list) != LDAP_SUCCESS) {
137 DEBUG(6,("ads_try_dns: ldap realm '%s' host list '%s'\n", realm, list));
140 count = count_chars(list, ' ') + 1;
141 ip_list = malloc(count * sizeof(struct ldap_ip));
147 while (next_token(&ptr, tok, " ", sizeof(tok))) {
148 unsigned port = LDAP_PORT;
149 char *p = strchr(tok, ':');
154 ip_list[i].ip = *interpret_addr2(tok);
155 ip_list[i].port = port;
156 if (!is_zero_ip(ip_list[i].ip)) {
164 /* we sort the list of addresses by closeness to our interfaces. This
165 tries to prevent us using a DC on the other side of the country */
167 qsort(ip_list, count, sizeof(struct ldap_ip),
168 QSORT_CAST ldap_ip_compare);
171 for (i=0;i<count;i++) {
172 if (ads_try_connect(ads, inet_ntoa(ip_list[i].ip), ip_list[i].port)) {
182 /* try connecting to a ldap server via netbios */
183 static BOOL ads_try_netbios(ADS_STRUCT *ads)
185 struct in_addr *ip_list, pdc_ip;
188 const char *workgroup = ads->server.workgroup;
192 workgroup = lp_workgroup();
195 DEBUG(6,("ads_try_netbios: looking for workgroup '%s'\n", workgroup));
197 /* try the PDC first */
198 if (get_pdc_ip(workgroup, &pdc_ip)) {
199 DEBUG(6,("ads_try_netbios: trying server '%s'\n",
201 if (ads_try_connect(ads, inet_ntoa(pdc_ip), LDAP_PORT))
205 /* now any DC, including backups */
206 if (get_dc_list(workgroup, &ip_list, &count, &list_ordered)) {
207 for (i=0;i<count;i++) {
208 DEBUG(6,("ads_try_netbios: trying server '%s'\n",
209 inet_ntoa(ip_list[i])));
210 if (ads_try_connect(ads, inet_ntoa(ip_list[i]), LDAP_PORT)) {
222 * Connect to the LDAP server
223 * @param ads Pointer to an existing ADS_STRUCT
224 * @return status of connection
226 ADS_STATUS ads_connect(ADS_STRUCT *ads)
228 int version = LDAP_VERSION3;
231 ads->last_attempt = time(NULL);
234 /* try with a URL based server */
236 if (ads->server.ldap_uri &&
237 ads_try_connect_uri(ads)) {
241 /* try with a user specified server */
242 if (ads->server.ldap_server &&
243 ads_try_connect(ads, ads->server.ldap_server, LDAP_PORT)) {
247 /* try with a smb.conf ads server setting if we are connecting
248 to the primary workgroup or realm */
249 if (!ads->server.foreign &&
250 ads_try_connect(ads, lp_ads_server(), LDAP_PORT)) {
255 if (ads_try_dns(ads)) {
259 /* try via netbios lookups */
260 if (!lp_disable_netbios() && ads_try_netbios(ads)) {
264 return ADS_ERROR_SYSTEM(errno?errno:ENOENT);
267 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
269 status = ads_server_info(ads);
270 if (!ADS_ERR_OK(status)) {
271 DEBUG(1,("Failed to get ldap server info\n"));
275 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
277 if (!ads->auth.user_name) {
278 /* by default use the machine account */
280 fstrcpy(myname, global_myname());
282 asprintf(&ads->auth.user_name, "HOST/%s", myname);
285 if (!ads->auth.realm) {
286 ads->auth.realm = strdup(ads->config.realm);
289 if (!ads->auth.kdc_server) {
290 ads->auth.kdc_server = strdup(inet_ntoa(ads->ldap_ip));
294 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
295 to MIT kerberos to work (tridge) */
298 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
299 setenv(env, ads->auth.kdc_server, 1);
304 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
308 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
309 return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
312 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
313 return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
316 return ads_sasl_bind(ads);
320 Duplicate a struct berval into talloc'ed memory
322 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
324 struct berval *value;
326 if (!in_val) return NULL;
328 value = talloc_zero(ctx, sizeof(struct berval));
331 if (in_val->bv_len == 0) return value;
333 value->bv_len = in_val->bv_len;
334 value->bv_val = talloc_memdup(ctx, in_val->bv_val, in_val->bv_len);
339 Make a values list out of an array of (struct berval *)
341 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
342 const struct berval **in_vals)
344 struct berval **values;
347 if (!in_vals) return NULL;
348 for (i=0; in_vals[i]; i++); /* count values */
349 values = (struct berval **) talloc_zero(ctx,
350 (i+1)*sizeof(struct berval *));
351 if (!values) return NULL;
353 for (i=0; in_vals[i]; i++) {
354 values[i] = dup_berval(ctx, in_vals[i]);
360 UTF8-encode a values list out of an array of (char *)
362 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
367 if (!in_vals) return NULL;
368 for (i=0; in_vals[i]; i++); /* count values */
369 values = (char ** ) talloc_zero(ctx, (i+1)*sizeof(char *));
370 if (!values) return NULL;
372 for (i=0; in_vals[i]; i++) {
373 push_utf8_talloc(ctx, &values[i], in_vals[i]);
379 Pull a (char *) array out of a UTF8-encoded values list
381 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
386 if (!in_vals) return NULL;
387 for (i=0; in_vals[i]; i++); /* count values */
388 values = (char **) talloc_zero(ctx, (i+1)*sizeof(char *));
389 if (!values) return NULL;
391 for (i=0; in_vals[i]; i++) {
392 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
398 * Do a search with paged results. cookie must be null on the first
399 * call, and then returned on each subsequent call. It will be null
400 * again when the entire search is complete
401 * @param ads connection to ads server
402 * @param bind_path Base dn for the search
403 * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
404 * @param expr Search expression - specified in local charset
405 * @param attrs Attributes to retrieve - specified in utf8 or ascii
406 * @param res ** which will contain results - free res* with ads_msgfree()
407 * @param count Number of entries retrieved on this page
408 * @param cookie The paged results cookie to be returned on subsequent calls
409 * @return status of search
411 ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
412 int scope, const char *expr,
413 const char **attrs, void **res,
414 int *count, void **cookie)
417 char *utf8_expr, *utf8_path, **search_attrs;
418 LDAPControl PagedResults, NoReferrals, *controls[3], **rcontrols;
419 BerElement *cookie_be = NULL;
420 struct berval *cookie_bv= NULL;
425 if (!(ctx = talloc_init("ads_do_paged_search")))
426 return ADS_ERROR(LDAP_NO_MEMORY);
428 /* 0 means the conversion worked but the result was empty
429 so we only fail if it's -1. In any case, it always
430 at least nulls out the dest */
431 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
432 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
437 if (!attrs || !(*attrs))
440 /* This would be the utf8-encoded version...*/
441 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
442 if (!(str_list_copy(&search_attrs, attrs))) {
449 /* Paged results only available on ldap v3 or later */
450 ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
451 if (version < LDAP_VERSION3) {
452 rc = LDAP_NOT_SUPPORTED;
456 cookie_be = ber_alloc_t(LBER_USE_DER);
457 if (cookie && *cookie) {
458 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
459 ber_bvfree(*cookie); /* don't need it from last time */
462 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
464 ber_flatten(cookie_be, &cookie_bv);
465 PagedResults.ldctl_oid = ADS_PAGE_CTL_OID;
466 PagedResults.ldctl_iscritical = (char) 1;
467 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
468 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
470 NoReferrals.ldctl_oid = ADS_NO_REFERRALS_OID;
471 NoReferrals.ldctl_iscritical = (char) 0;
472 NoReferrals.ldctl_value.bv_len = 0;
473 NoReferrals.ldctl_value.bv_val = "";
476 controls[0] = &NoReferrals;
477 controls[1] = &PagedResults;
482 /* we need to disable referrals as the openldap libs don't
483 handle them and paged results at the same time. Using them
484 together results in the result record containing the server
485 page control being removed from the result list (tridge/jmcd)
487 leaving this in despite the control that says don't generate
488 referrals, in case the server doesn't support it (jmcd)
490 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
492 rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_expr,
493 search_attrs, 0, controls,
494 NULL, NULL, LDAP_NO_LIMIT, (LDAPMessage **)res);
496 ber_free(cookie_be, 1);
497 ber_bvfree(cookie_bv);
500 DEBUG(3,("ldap_search_ext_s(%s) -> %s\n", expr, ldap_err2string(rc)));
504 rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
505 NULL, &rcontrols, 0);
511 for (i=0; rcontrols[i]; i++) {
512 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
513 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
514 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
516 /* the berval is the cookie, but must be freed when
518 if (cookie_bv->bv_len) /* still more to do */
519 *cookie=ber_bvdup(cookie_bv);
522 ber_bvfree(cookie_bv);
523 ber_free(cookie_be, 1);
527 ldap_controls_free(rcontrols);
531 /* if/when we decide to utf8-encode attrs, take out this next line */
532 str_list_free(&search_attrs);
534 return ADS_ERROR(rc);
539 * Get all results for a search. This uses ads_do_paged_search() to return
540 * all entries in a large search.
541 * @param ads connection to ads server
542 * @param bind_path Base dn for the search
543 * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
544 * @param expr Search expression
545 * @param attrs Attributes to retrieve
546 * @param res ** which will contain results - free res* with ads_msgfree()
547 * @return status of search
549 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
550 int scope, const char *expr,
551 const char **attrs, void **res)
557 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, res,
560 if (!ADS_ERR_OK(status)) return status;
565 LDAPMessage *msg, *next;
567 status2 = ads_do_paged_search(ads, bind_path, scope, expr,
568 attrs, &res2, &count, &cookie);
570 if (!ADS_ERR_OK(status2)) break;
572 /* this relies on the way that ldap_add_result_entry() works internally. I hope
573 that this works on all ldap libs, but I have only tested with openldap */
574 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
575 next = ads_next_entry(ads, msg);
576 ldap_add_result_entry((LDAPMessage **)res, msg);
578 /* note that we do not free res2, as the memory is now
579 part of the main returned list */
586 * Run a function on all results for a search. Uses ads_do_paged_search() and
587 * runs the function as each page is returned, using ads_process_results()
588 * @param ads connection to ads server
589 * @param bind_path Base dn for the search
590 * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
591 * @param expr Search expression - specified in local charset
592 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
593 * @param fn Function which takes attr name, values list, and data_area
594 * @param data_area Pointer which is passed to function on each call
595 * @return status of search
597 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
598 int scope, const char *expr, const char **attrs,
599 BOOL(*fn)(char *, void **, void *),
607 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
610 if (!ADS_ERR_OK(status)) return status;
612 ads_process_results(ads, res, fn, data_area);
613 ads_msgfree(ads, res);
616 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
617 &res, &count, &cookie);
619 if (!ADS_ERR_OK(status)) break;
621 ads_process_results(ads, res, fn, data_area);
622 ads_msgfree(ads, res);
629 * Do a search with a timeout.
630 * @param ads connection to ads server
631 * @param bind_path Base dn for the search
632 * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
633 * @param expr Search expression
634 * @param attrs Attributes to retrieve
635 * @param res ** which will contain results - free res* with ads_msgfree()
636 * @return status of search
638 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
640 const char **attrs, void **res)
642 struct timeval timeout;
644 char *utf8_expr, *utf8_path, **search_attrs = NULL;
647 if (!(ctx = talloc_init("ads_do_search"))) {
648 DEBUG(1,("ads_do_search: talloc_init() failed!"));
649 return ADS_ERROR(LDAP_NO_MEMORY);
652 /* 0 means the conversion worked but the result was empty
653 so we only fail if it's negative. In any case, it always
654 at least nulls out the dest */
655 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
656 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
657 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
662 if (!attrs || !(*attrs))
665 /* This would be the utf8-encoded version...*/
666 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
667 if (!(str_list_copy(&search_attrs, attrs)))
669 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
675 timeout.tv_sec = ADS_SEARCH_TIMEOUT;
679 /* see the note in ads_do_paged_search - we *must* disable referrals */
680 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
682 rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_expr,
683 search_attrs, 0, NULL, NULL,
684 &timeout, LDAP_NO_LIMIT, (LDAPMessage **)res);
686 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
687 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
693 /* if/when we decide to utf8-encode attrs, take out this next line */
694 str_list_free(&search_attrs);
695 return ADS_ERROR(rc);
698 * Do a general ADS search
699 * @param ads connection to ads server
700 * @param res ** which will contain results - free res* with ads_msgfree()
701 * @param expr Search expression
702 * @param attrs Attributes to retrieve
703 * @return status of search
705 ADS_STATUS ads_search(ADS_STRUCT *ads, void **res,
709 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
714 * Do a search on a specific DistinguishedName
715 * @param ads connection to ads server
716 * @param res ** which will contain results - free res* with ads_msgfree()
717 * @param dn DistinguishName to search
718 * @param attrs Attributes to retrieve
719 * @return status of search
721 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, void **res,
725 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res);
729 * Free up memory from a ads_search
730 * @param ads connection to ads server
731 * @param msg Search results to free
733 void ads_msgfree(ADS_STRUCT *ads, void *msg)
740 * Free up memory from various ads requests
741 * @param ads connection to ads server
742 * @param mem Area to free
744 void ads_memfree(ADS_STRUCT *ads, void *mem)
750 * Get a dn from search results
751 * @param ads connection to ads server
752 * @param res Search results
755 char *ads_get_dn(ADS_STRUCT *ads, void *res)
757 char *utf8_dn, *unix_dn;
759 utf8_dn = ldap_get_dn(ads->ld, res);
760 pull_utf8_allocate((void **) &unix_dn, utf8_dn);
761 ldap_memfree(utf8_dn);
766 * Find a machine account given a hostname
767 * @param ads connection to ads server
768 * @param res ** which will contain results - free res* with ads_msgfree()
769 * @param host Hostname to search for
770 * @return status of search
772 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *host)
776 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
778 /* the easiest way to find a machine account anywhere in the tree
779 is to look for hostname$ */
780 if (asprintf(&expr, "(samAccountName=%s$)", host) == -1) {
781 DEBUG(1, ("asprintf failed!\n"));
782 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
785 status = ads_search(ads, res, expr, attrs);
791 * Initialize a list of mods to be used in a modify request
792 * @param ctx An initialized TALLOC_CTX
793 * @return allocated ADS_MODLIST
795 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
797 #define ADS_MODLIST_ALLOC_SIZE 10
800 if ((mods = (LDAPMod **) talloc_zero(ctx, sizeof(LDAPMod *) *
801 (ADS_MODLIST_ALLOC_SIZE + 1))))
802 /* -1 is safety to make sure we don't go over the end.
803 need to reset it to NULL before doing ldap modify */
804 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
811 add an attribute to the list, with values list already constructed
813 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
814 int mod_op, const char *name,
818 LDAPMod **modlist = (LDAPMod **) *mods;
819 struct berval **ber_values = NULL;
820 char **char_values = NULL;
823 mod_op = LDAP_MOD_DELETE;
825 if (mod_op & LDAP_MOD_BVALUES)
826 ber_values = ads_dup_values(ctx,
827 (const struct berval **)invals);
829 char_values = ads_push_strvals(ctx,
830 (const char **) invals);
833 /* find the first empty slot */
834 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
836 if (modlist[curmod] == (LDAPMod *) -1) {
837 if (!(modlist = talloc_realloc(ctx, modlist,
838 (curmod+ADS_MODLIST_ALLOC_SIZE+1)*sizeof(LDAPMod *))))
839 return ADS_ERROR(LDAP_NO_MEMORY);
840 memset(&modlist[curmod], 0,
841 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
842 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
846 if (!(modlist[curmod] = talloc_zero(ctx, sizeof(LDAPMod))))
847 return ADS_ERROR(LDAP_NO_MEMORY);
848 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
849 if (mod_op & LDAP_MOD_BVALUES) {
850 modlist[curmod]->mod_bvalues = ber_values;
851 } else if (mod_op & LDAP_MOD_DELETE) {
852 modlist[curmod]->mod_values = NULL;
854 modlist[curmod]->mod_values = char_values;
857 modlist[curmod]->mod_op = mod_op;
858 return ADS_ERROR(LDAP_SUCCESS);
862 * Add a single string value to a mod list
863 * @param ctx An initialized TALLOC_CTX
864 * @param mods An initialized ADS_MODLIST
865 * @param name The attribute name to add
866 * @param val The value to add - NULL means DELETE
867 * @return ADS STATUS indicating success of add
869 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
870 const char *name, const char *val)
872 const char *values[2];
878 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
879 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name,
880 (const void **) values);
884 * Add an array of string values to a mod list
885 * @param ctx An initialized TALLOC_CTX
886 * @param mods An initialized ADS_MODLIST
887 * @param name The attribute name to add
888 * @param vals The array of string values to add - NULL means DELETE
889 * @return ADS STATUS indicating success of add
891 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
892 const char *name, const char **vals)
895 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
896 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
897 name, (const void **) vals);
901 * Add a single ber-encoded value to a mod list
902 * @param ctx An initialized TALLOC_CTX
903 * @param mods An initialized ADS_MODLIST
904 * @param name The attribute name to add
905 * @param val The value to add - NULL means DELETE
906 * @return ADS STATUS indicating success of add
908 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
909 const char *name, const struct berval *val)
911 const struct berval *values[2];
916 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
917 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
918 name, (const void **) values);
922 * Perform an ldap modify
923 * @param ads connection to ads server
924 * @param mod_dn DistinguishedName to modify
925 * @param mods list of modifications to perform
926 * @return status of modify
928 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
931 char *utf8_dn = NULL;
933 this control is needed to modify that contains a currently
934 non-existent attribute (but allowable for the object) to run
936 LDAPControl PermitModify = {
937 ADS_PERMIT_MODIFY_OID,
940 LDAPControl *controls[2];
942 controls[0] = &PermitModify;
945 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
946 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
949 /* find the end of the list, marked by NULL or -1 */
950 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
951 /* make sure the end of the list is NULL */
953 ret = ldap_modify_ext_s(ads->ld, utf8_dn,
954 (LDAPMod **) mods, controls, NULL);
956 return ADS_ERROR(ret);
960 * Perform an ldap add
961 * @param ads connection to ads server
962 * @param new_dn DistinguishedName to add
963 * @param mods list of attributes and values for DN
964 * @return status of add
966 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
969 char *utf8_dn = NULL;
971 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
972 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
973 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
976 /* find the end of the list, marked by NULL or -1 */
977 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
978 /* make sure the end of the list is NULL */
981 ret = ldap_add_s(ads->ld, utf8_dn, mods);
983 return ADS_ERROR(ret);
987 * Delete a DistinguishedName
988 * @param ads connection to ads server
989 * @param new_dn DistinguishedName to delete
990 * @return status of delete
992 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
995 char *utf8_dn = NULL;
996 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
997 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
998 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1001 ret = ldap_delete(ads->ld, utf8_dn);
1002 return ADS_ERROR(ret);
1006 * Build an org unit string
1007 * if org unit is Computers or blank then assume a container, otherwise
1008 * assume a \ separated list of organisational units
1009 * @param org_unit Organizational unit
1010 * @return org unit string - caller must free
1012 char *ads_ou_string(const char *org_unit)
1014 if (!org_unit || !*org_unit || strcasecmp(org_unit, "Computers") == 0) {
1015 return strdup("cn=Computers");
1018 return ads_build_path(org_unit, "\\/", "ou=", 1);
1024 add a machine account to the ADS server
1026 static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *hostname,
1027 uint32 account_type,
1028 const char *org_unit)
1030 ADS_STATUS ret, status;
1031 char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
1035 const char *objectClass[] = {"top", "person", "organizationalPerson",
1036 "user", "computer", NULL};
1037 const char *servicePrincipalName[5] = {NULL, NULL, NULL, NULL, NULL};
1039 unsigned acct_control;
1041 if (!(ctx = talloc_init("machine_account")))
1042 return ADS_ERROR(LDAP_NO_MEMORY);
1044 ret = ADS_ERROR(LDAP_NO_MEMORY);
1046 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", hostname)))
1048 if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm)))
1050 ou_str = ads_ou_string(org_unit);
1052 DEBUG(1, ("ads_ou_string returned NULL (malloc failure?)\n"));
1055 new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", hostname, ou_str,
1056 ads->config.bind_path);
1057 servicePrincipalName[0] = talloc_asprintf(ctx, "HOST/%s", hostname);
1058 psp = talloc_asprintf(ctx, "HOST/%s.%s",
1062 servicePrincipalName[1] = psp;
1063 servicePrincipalName[2] = talloc_asprintf(ctx, "CIFS/%s", hostname);
1064 psp2 = talloc_asprintf(ctx, "CIFS/%s.%s",
1068 servicePrincipalName[3] = psp2;
1074 if (!(samAccountName = talloc_asprintf(ctx, "%s$", hostname)))
1077 acct_control = account_type | UF_DONT_EXPIRE_PASSWD;
1078 #ifndef ENCTYPE_ARCFOUR_HMAC
1079 acct_control |= UF_USE_DES_KEY_ONLY;
1081 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control)))
1084 if (!(mods = ads_init_mods(ctx)))
1087 ads_mod_str(ctx, &mods, "cn", hostname);
1088 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1089 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1090 ads_mod_str(ctx, &mods, "userPrincipalName", host_upn);
1091 ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1092 ads_mod_str(ctx, &mods, "dNSHostName", hostname);
1093 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1094 ads_mod_str(ctx, &mods, "operatingSystem", "Samba");
1095 ads_mod_str(ctx, &mods, "operatingSystemVersion", VERSION);
1097 ret = ads_gen_add(ads, new_dn, mods);
1099 if (!ADS_ERR_OK(ret))
1102 /* Do not fail if we can't set security descriptor
1103 * it shouldn't be mandatory and probably we just
1104 * don't have enough rights to do it.
1106 status = ads_set_machine_sd(ads, hostname, new_dn);
1108 if (!ADS_ERR_OK(status)) {
1109 DEBUG(0, ("Warning: ads_set_machine_sd: %s\n",
1110 ads_errstr(status)));
1113 talloc_destroy(ctx);
1118 dump a binary result from ldap
1120 static void dump_binary(const char *field, struct berval **values)
1123 for (i=0; values[i]; i++) {
1124 printf("%s: ", field);
1125 for (j=0; j<values[i]->bv_len; j++) {
1126 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1139 static void dump_guid(const char *field, struct berval **values)
1143 for (i=0; values[i]; i++) {
1144 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1145 printf("%s: %s\n", field, uuid_string_static(guid));
1150 dump a sid result from ldap
1152 static void dump_sid(const char *field, struct berval **values)
1155 for (i=0; values[i]; i++) {
1157 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1158 printf("%s: %s\n", field, sid_string_static(&sid));
1163 dump ntSecurityDescriptor
1165 static void dump_sd(const char *filed, struct berval **values)
1170 TALLOC_CTX *ctx = 0;
1172 if (!(ctx = talloc_init("sec_io_desc")))
1176 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1177 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1178 prs_set_offset(&ps,0);
1181 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1183 talloc_destroy(ctx);
1186 if (psd) ads_disp_sd(psd);
1189 talloc_destroy(ctx);
1193 dump a string result from ldap
1195 static void dump_string(const char *field, char **values)
1198 for (i=0; values[i]; i++) {
1199 printf("%s: %s\n", field, values[i]);
1204 dump a field from LDAP on stdout
1208 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1213 void (*handler)(const char *, struct berval **);
1215 {"objectGUID", False, dump_guid},
1216 {"nTSecurityDescriptor", False, dump_sd},
1217 {"dnsRecord", False, dump_binary},
1218 {"objectSid", False, dump_sid},
1219 {"tokenGroups", False, dump_sid},
1224 if (!field) { /* must be end of an entry */
1229 for (i=0; handlers[i].name; i++) {
1230 if (StrCaseCmp(handlers[i].name, field) == 0) {
1231 if (!values) /* first time, indicate string or not */
1232 return handlers[i].string;
1233 handlers[i].handler(field, (struct berval **) values);
1237 if (!handlers[i].name) {
1238 if (!values) /* first time, indicate string conversion */
1240 dump_string(field, (char **)values);
1246 * Dump a result from LDAP on stdout
1247 * used for debugging
1248 * @param ads connection to ads server
1249 * @param res Results to dump
1252 void ads_dump(ADS_STRUCT *ads, void *res)
1254 ads_process_results(ads, res, ads_dump_field, NULL);
1258 * Walk through results, calling a function for each entry found.
1259 * The function receives a field name, a berval * array of values,
1260 * and a data area passed through from the start. The function is
1261 * called once with null for field and values at the end of each
1263 * @param ads connection to ads server
1264 * @param res Results to process
1265 * @param fn Function for processing each result
1266 * @param data_area user-defined area to pass to function
1268 void ads_process_results(ADS_STRUCT *ads, void *res,
1269 BOOL(*fn)(char *, void **, void *),
1275 if (!(ctx = talloc_init("ads_process_results")))
1278 for (msg = ads_first_entry(ads, res); msg;
1279 msg = ads_next_entry(ads, msg)) {
1283 for (utf8_field=ldap_first_attribute(ads->ld,
1284 (LDAPMessage *)msg,&b);
1286 utf8_field=ldap_next_attribute(ads->ld,
1287 (LDAPMessage *)msg,b)) {
1288 struct berval **ber_vals;
1289 char **str_vals, **utf8_vals;
1293 pull_utf8_talloc(ctx, &field, utf8_field);
1294 string = fn(field, NULL, data_area);
1297 utf8_vals = ldap_get_values(ads->ld,
1298 (LDAPMessage *)msg, field);
1299 str_vals = ads_pull_strvals(ctx,
1300 (const char **) utf8_vals);
1301 fn(field, (void **) str_vals, data_area);
1302 ldap_value_free(utf8_vals);
1304 ber_vals = ldap_get_values_len(ads->ld,
1305 (LDAPMessage *)msg, field);
1306 fn(field, (void **) ber_vals, data_area);
1308 ldap_value_free_len(ber_vals);
1310 ldap_memfree(utf8_field);
1313 talloc_destroy_pool(ctx);
1314 fn(NULL, NULL, data_area); /* completed an entry */
1317 talloc_destroy(ctx);
1321 * count how many replies are in a LDAPMessage
1322 * @param ads connection to ads server
1323 * @param res Results to count
1324 * @return number of replies
1326 int ads_count_replies(ADS_STRUCT *ads, void *res)
1328 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1332 * Join a machine to a realm
1333 * Creates the machine account and sets the machine password
1334 * @param ads connection to ads server
1335 * @param hostname name of host to add
1336 * @param org_unit Organizational unit to place machine in
1337 * @return status of join
1339 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *hostname,
1340 uint32 account_type, const char *org_unit)
1346 /* hostname must be lowercase */
1347 host = strdup(hostname);
1350 status = ads_find_machine_acct(ads, (void **)&res, host);
1351 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1352 DEBUG(0, ("Host account for %s already exists - deleting old account\n", host));
1353 status = ads_leave_realm(ads, host);
1354 if (!ADS_ERR_OK(status)) {
1355 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
1356 host, ads->config.realm));
1361 status = ads_add_machine_acct(ads, host, account_type, org_unit);
1362 if (!ADS_ERR_OK(status)) {
1363 DEBUG(0, ("ads_add_machine_acct: %s\n", ads_errstr(status)));
1367 status = ads_find_machine_acct(ads, (void **)&res, host);
1368 if (!ADS_ERR_OK(status)) {
1369 DEBUG(0, ("Host account test failed\n"));
1379 * Delete a machine from the realm
1380 * @param ads connection to ads server
1381 * @param hostname Machine to remove
1382 * @return status of delete
1384 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
1388 char *hostnameDN, *host;
1391 /* hostname must be lowercase */
1392 host = strdup(hostname);
1395 status = ads_find_machine_acct(ads, &res, host);
1396 if (!ADS_ERR_OK(status)) {
1397 DEBUG(0, ("Host account for %s does not exist.\n", host));
1401 hostnameDN = ads_get_dn(ads, (LDAPMessage *)res);
1402 rc = ldap_delete_s(ads->ld, hostnameDN);
1403 ads_memfree(ads, hostnameDN);
1404 if (rc != LDAP_SUCCESS) {
1405 return ADS_ERROR(rc);
1408 status = ads_find_machine_acct(ads, &res, host);
1409 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1410 DEBUG(0, ("Failed to remove host account.\n"));
1420 * add machine account to existing security descriptor
1421 * @param ads connection to ads server
1422 * @param hostname machine to add
1423 * @param dn DN of security descriptor
1426 ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
1428 const char *attrs[] = {"nTSecurityDescriptor", "objectSid", 0};
1431 struct berval bval = {0, NULL};
1433 char *escaped_hostname = escape_ldap_string_alloc(hostname);
1435 LDAPMessage *res = 0;
1436 LDAPMessage *msg = 0;
1437 ADS_MODLIST mods = 0;
1442 SEC_DESC *psd = NULL;
1443 TALLOC_CTX *ctx = NULL;
1445 /* Avoid segmentation fault in prs_mem_free if
1446 * we have to bail out before prs_init */
1447 ps_wire.is_dynamic = False;
1449 if (!ads) return ADS_ERROR(LDAP_SERVER_DOWN);
1451 ret = ADS_ERROR(LDAP_SUCCESS);
1453 if (!escaped_hostname) {
1454 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1457 if (asprintf(&expr, "(samAccountName=%s$)", escaped_hostname) == -1) {
1458 DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n"));
1459 SAFE_FREE(escaped_hostname);
1460 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1463 SAFE_FREE(escaped_hostname);
1465 ret = ads_search(ads, (void *) &res, expr, attrs);
1467 if (!ADS_ERR_OK(ret)) return ret;
1469 if ( !(msg = ads_first_entry(ads, res) )) {
1470 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1471 goto ads_set_sd_error;
1474 if (!ads_pull_sid(ads, msg, attrs[1], &sid)) {
1475 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1476 goto ads_set_sd_error;
1479 if (!(ctx = talloc_init("sec_io_desc"))) {
1480 ret = ADS_ERROR(LDAP_NO_MEMORY);
1481 goto ads_set_sd_error;
1484 if (!ads_pull_sd(ads, ctx, msg, attrs[0], &psd)) {
1485 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1486 goto ads_set_sd_error;
1489 status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size);
1491 if (!NT_STATUS_IS_OK(status)) {
1492 ret = ADS_ERROR_NT(status);
1493 goto ads_set_sd_error;
1496 if (!prs_init(&ps_wire, sd_size, ctx, MARSHALL)) {
1497 ret = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1500 if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1)) {
1501 ret = ADS_ERROR(LDAP_NO_MEMORY);
1502 goto ads_set_sd_error;
1506 file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size);
1508 if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY);
1510 bval.bv_len = prs_offset(&ps_wire);
1511 bval.bv_val = talloc(ctx, bval.bv_len);
1513 ret = ADS_ERROR(LDAP_NO_MEMORY);
1514 goto ads_set_sd_error;
1517 prs_set_offset(&ps_wire, 0);
1519 if (!prs_copy_data_out(bval.bv_val, &ps_wire, bval.bv_len)) {
1520 ret = ADS_ERROR(LDAP_NO_MEMORY);
1521 goto ads_set_sd_error;
1524 ret = ads_mod_ber(ctx, &mods, attrs[0], &bval);
1525 if (ADS_ERR_OK(ret)) {
1526 ret = ads_gen_mod(ads, dn, mods);
1530 ads_msgfree(ads, res);
1531 prs_mem_free(&ps_wire);
1532 talloc_destroy(ctx);
1537 * pull the first entry from a ADS result
1538 * @param ads connection to ads server
1539 * @param res Results of search
1540 * @return first entry from result
1542 void *ads_first_entry(ADS_STRUCT *ads, void *res)
1544 return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
1548 * pull the next entry from a ADS result
1549 * @param ads connection to ads server
1550 * @param res Results of search
1551 * @return next entry from result
1553 void *ads_next_entry(ADS_STRUCT *ads, void *res)
1555 return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
1559 * pull a single string from a ADS result
1560 * @param ads connection to ads server
1561 * @param mem_ctx TALLOC_CTX to use for allocating result string
1562 * @param msg Results of search
1563 * @param field Attribute to retrieve
1564 * @return Result string in talloc context
1566 char *ads_pull_string(ADS_STRUCT *ads,
1567 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1574 values = ldap_get_values(ads->ld, msg, field);
1579 rc = pull_utf8_talloc(mem_ctx, &ux_string,
1581 if (rc != (size_t)-1)
1585 ldap_value_free(values);
1590 * pull an array of strings from a ADS result
1591 * @param ads connection to ads server
1592 * @param mem_ctx TALLOC_CTX to use for allocating result string
1593 * @param msg Results of search
1594 * @param field Attribute to retrieve
1595 * @return Result strings in talloc context
1597 char **ads_pull_strings(ADS_STRUCT *ads,
1598 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1604 values = ldap_get_values(ads->ld, msg, field);
1608 for (i=0;values[i];i++)
1612 ret = talloc(mem_ctx, sizeof(char *) * (n+1));
1614 ldap_value_free(values);
1619 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
1620 ldap_value_free(values);
1626 ldap_value_free(values);
1632 * pull a single uint32 from a ADS result
1633 * @param ads connection to ads server
1634 * @param msg Results of search
1635 * @param field Attribute to retrieve
1636 * @param v Pointer to int to store result
1637 * @return boolean inidicating success
1639 BOOL ads_pull_uint32(ADS_STRUCT *ads,
1640 void *msg, const char *field, uint32 *v)
1644 values = ldap_get_values(ads->ld, msg, field);
1648 ldap_value_free(values);
1652 *v = atoi(values[0]);
1653 ldap_value_free(values);
1658 * pull a single objectGUID from an ADS result
1659 * @param ads connection to ADS server
1660 * @param msg results of search
1661 * @param guid 37-byte area to receive text guid
1662 * @return boolean indicating success
1664 BOOL ads_pull_guid(ADS_STRUCT *ads,
1665 void *msg, GUID *guid)
1669 values = ldap_get_values(ads->ld, msg, "objectGUID");
1674 memcpy(guid, values[0], sizeof(GUID));
1675 ldap_value_free(values);
1678 ldap_value_free(values);
1685 * pull a single DOM_SID from a ADS result
1686 * @param ads connection to ads server
1687 * @param msg Results of search
1688 * @param field Attribute to retrieve
1689 * @param sid Pointer to sid to store result
1690 * @return boolean inidicating success
1692 BOOL ads_pull_sid(ADS_STRUCT *ads,
1693 void *msg, const char *field, DOM_SID *sid)
1695 struct berval **values;
1698 values = ldap_get_values_len(ads->ld, msg, field);
1704 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
1706 ldap_value_free_len(values);
1711 * pull an array of DOM_SIDs from a ADS result
1712 * @param ads connection to ads server
1713 * @param mem_ctx TALLOC_CTX for allocating sid array
1714 * @param msg Results of search
1715 * @param field Attribute to retrieve
1716 * @param sids pointer to sid array to allocate
1717 * @return the count of SIDs pulled
1719 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1720 void *msg, const char *field, DOM_SID **sids)
1722 struct berval **values;
1726 values = ldap_get_values_len(ads->ld, msg, field);
1731 for (i=0; values[i]; i++)
1734 (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * i);
1736 ldap_value_free_len(values);
1741 for (i=0; values[i]; i++) {
1742 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
1745 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
1750 ldap_value_free_len(values);
1755 * pull a SEC_DESC from a ADS result
1756 * @param ads connection to ads server
1757 * @param mem_ctx TALLOC_CTX for allocating sid array
1758 * @param msg Results of search
1759 * @param field Attribute to retrieve
1760 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
1761 * @return boolean inidicating success
1763 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1764 void *msg, const char *field, SEC_DESC **sd)
1766 struct berval **values;
1770 values = ldap_get_values_len(ads->ld, msg, field);
1772 if (!values) return False;
1775 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
1776 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1777 prs_set_offset(&ps,0);
1779 ret = sec_io_desc("sd", sd, &ps, 1);
1782 ldap_value_free_len(values);
1787 * in order to support usernames longer than 21 characters we need to
1788 * use both the sAMAccountName and the userPrincipalName attributes
1789 * It seems that not all users have the userPrincipalName attribute set
1791 * @param ads connection to ads server
1792 * @param mem_ctx TALLOC_CTX for allocating sid array
1793 * @param msg Results of search
1794 * @return the username
1796 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
1800 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
1801 if (ret && (p = strchr(ret, '@'))) {
1805 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
1810 * find the update serial number - this is the core of the ldap cache
1811 * @param ads connection to ads server
1812 * @param ads connection to ADS server
1813 * @param usn Pointer to retrieved update serial number
1814 * @return status of search
1816 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
1818 const char *attrs[] = {"highestCommittedUSN", NULL};
1822 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1823 if (!ADS_ERR_OK(status)) return status;
1825 if (ads_count_replies(ads, res) != 1) {
1826 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1829 ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
1830 ads_msgfree(ads, res);
1834 /* parse a ADS timestring - typical string is
1835 '20020917091222.0Z0' which means 09:12.22 17th September
1837 static time_t ads_parse_time(const char *str)
1843 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
1844 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
1845 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
1856 * Find the servers name and realm - this can be done before authentication
1857 * The ldapServiceName field on w2k looks like this:
1858 * vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
1859 * @param ads connection to ads server
1860 * @return status of search
1862 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
1864 const char *attrs[] = {"ldapServiceName", "currentTime", NULL};
1872 if (!(ctx = talloc_init("ads_server_info"))) {
1873 return ADS_ERROR(LDAP_NO_MEMORY);
1876 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1877 if (!ADS_ERR_OK(status)) return status;
1879 value = ads_pull_string(ads, ctx, res, "ldapServiceName");
1881 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1884 timestr = ads_pull_string(ads, ctx, res, "currentTime");
1886 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1891 p = strchr(value, ':');
1893 talloc_destroy(ctx);
1894 DEBUG(1, ("ads_server_info: returned ldap server name did not contain a ':' so was deemed invalid\n"));
1895 return ADS_ERROR(LDAP_DECODING_ERROR);
1898 SAFE_FREE(ads->config.ldap_server_name);
1900 ads->config.ldap_server_name = strdup(p+1);
1901 p = strchr(ads->config.ldap_server_name, '$');
1902 if (!p || p[1] != '@') {
1903 talloc_destroy(ctx);
1904 DEBUG(1, ("ads_server_info: returned ldap server name (%s) does not contain '$@' so was deemed invalid\n", ads->config.ldap_server_name));
1905 SAFE_FREE(ads->config.ldap_server_name);
1906 return ADS_ERROR(LDAP_DECODING_ERROR);
1911 SAFE_FREE(ads->config.realm);
1912 SAFE_FREE(ads->config.bind_path);
1914 ads->config.realm = strdup(p+2);
1915 ads->config.bind_path = ads_build_dn(ads->config.realm);
1917 DEBUG(3,("got ldap server name %s@%s, using bind path: %s\n",
1918 ads->config.ldap_server_name, ads->config.realm,
1919 ads->config.bind_path));
1921 ads->config.current_time = ads_parse_time(timestr);
1923 if (ads->config.current_time != 0) {
1924 ads->auth.time_offset = ads->config.current_time - time(NULL);
1925 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
1928 talloc_destroy(ctx);
1935 * find the list of trusted domains
1936 * @param ads connection to ads server
1937 * @param mem_ctx TALLOC_CTX for allocating results
1938 * @param num_trusts pointer to number of trusts
1939 * @param names pointer to trusted domain name list
1940 * @param sids pointer to list of sids of trusted domains
1941 * @return the count of SIDs pulled
1943 ADS_STATUS ads_trusted_domains(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1949 const char *attrs[] = {"name", "flatname", "securityIdentifier",
1950 "trustDirection", NULL};
1957 status = ads_search(ads, &res, "(objectcategory=trustedDomain)", attrs);
1958 if (!ADS_ERR_OK(status)) return status;
1960 count = ads_count_replies(ads, res);
1962 ads_msgfree(ads, res);
1963 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1966 (*names) = talloc(mem_ctx, sizeof(char *) * count);
1967 (*alt_names) = talloc(mem_ctx, sizeof(char *) * count);
1968 (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * count);
1969 if (! *names || ! *sids) return ADS_ERROR(LDAP_NO_MEMORY);
1971 for (i=0, msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
1974 /* direction is a 2 bit bitfield, 1 means they trust us
1975 but we don't trust them, so we should not list them
1976 as users from that domain can't login */
1977 if (ads_pull_uint32(ads, msg, "trustDirection", &direction) &&
1982 (*names)[i] = ads_pull_string(ads, mem_ctx, msg, "name");
1983 (*alt_names)[i] = ads_pull_string(ads, mem_ctx, msg, "flatname");
1985 if ((*alt_names)[i] && (*alt_names)[i][0]) {
1986 /* we prefer the flatname as the primary name
1987 for consistency with RPC */
1988 char *name = (*alt_names)[i];
1989 (*alt_names)[i] = (*names)[i];
1992 if (ads_pull_sid(ads, msg, "securityIdentifier", &(*sids)[i])) {
1997 ads_msgfree(ads, res);
2005 * find the domain sid for our domain
2006 * @param ads connection to ads server
2007 * @param sid Pointer to domain sid
2008 * @return status of search
2010 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2012 const char *attrs[] = {"objectSid", NULL};
2016 rc = ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2018 if (!ADS_ERR_OK(rc)) return rc;
2019 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2020 return ADS_ERROR_SYSTEM(ENOENT);
2022 ads_msgfree(ads, res);
2027 /* this is rather complex - we need to find the allternate (netbios) name
2028 for the domain, but there isn't a simple query to do this. Instead
2029 we look for the principle names on the DCs account and find one that has
2030 the right form, then extract the netbios name of the domain from that
2032 NOTE! better method is this:
2034 bin/net -Uadministrator%XXXXX ads search '(&(objectclass=crossref)(dnsroot=VNET3.HOME.SAMBA.ORG))' nETBIOSName
2036 but you need to force the bind path to match the configurationNamingContext from the rootDSE
2039 ADS_STATUS ads_workgroup_name(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char **workgroup)
2048 const char *attrs[] = {"servicePrincipalName", NULL};
2050 (*workgroup) = NULL;
2052 asprintf(&expr, "(&(objectclass=computer)(dnshostname=%s.%s))",
2053 ads->config.ldap_server_name, ads->config.realm);
2054 rc = ads_search(ads, &res, expr, attrs);
2057 if (!ADS_ERR_OK(rc)) {
2061 principles = ads_pull_strings(ads, mem_ctx, res, "servicePrincipalName");
2063 ads_msgfree(ads, res);
2066 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2069 asprintf(&prefix, "HOST/%s.%s/",
2070 ads->config.ldap_server_name,
2073 prefix_length = strlen(prefix);
2075 for (i=0;principles[i]; i++) {
2076 if (strncasecmp(principles[i], prefix, prefix_length) == 0 &&
2077 strcasecmp(ads->config.realm, principles[i]+prefix_length) != 0 &&
2078 !strchr(principles[i]+prefix_length, '.')) {
2079 /* found an alternate (short) name for the domain. */
2080 DEBUG(3,("Found alternate name '%s' for realm '%s'\n",
2081 principles[i]+prefix_length,
2082 ads->config.realm));
2083 (*workgroup) = talloc_strdup(mem_ctx, principles[i]+prefix_length);
2090 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);