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 <jmcd@us.ibm.com> 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 TODO : add a negative connection cache in here leveraged off of the one
46 found in the rpc code. --jerry
48 static BOOL ads_try_connect(ADS_STRUCT *ads, const char *server, unsigned port)
52 if (!server || !*server) {
56 DEBUG(5,("ads_try_connect: trying ldap server '%s' port %u\n", server, port));
58 /* this copes with inet_ntoa brokenness */
61 ads->ld = ldap_open(srv, port);
66 ads->ldap_port = port;
67 ads->ldap_ip = *interpret_addr2(srv);
74 try a connection to a given ldap server, based on URL, returning True if successful
76 static BOOL ads_try_connect_uri(ADS_STRUCT *ads)
78 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
79 DEBUG(5,("ads_try_connect: trying ldap server at URI '%s'\n",
80 ads->server.ldap_uri));
83 if (ldap_initialize((LDAP**)&(ads->ld), ads->server.ldap_uri) == LDAP_SUCCESS) {
86 DEBUG(0, ("ldap_initialize: %s\n", strerror(errno)));
90 DEBUG(1, ("no URL support in LDAP libs!\n"));
96 /**********************************************************************
97 Try to find an AD dc using our internal name resolution routines
98 Try the realm first and then then workgroup name if netbios is not
100 **********************************************************************/
102 static BOOL ads_find_dc(ADS_STRUCT *ads)
106 struct ip_service *ip_list;
108 BOOL got_realm = False;
109 BOOL use_own_domain = False;
111 /* if the realm and workgroup are both empty, assume they are ours */
114 c_realm = ads->server.realm;
116 if ( !c_realm || !*c_realm ) {
117 /* special case where no realm and no workgroup means our own */
118 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
119 use_own_domain = True;
120 c_realm = lp_realm();
124 if (c_realm && *c_realm)
128 /* we need to try once with the realm name and fallback to the
129 netbios domain name if we fail (if netbios has not been disabled */
131 if ( !got_realm && !lp_disable_netbios() ) {
132 c_realm = ads->server.workgroup;
133 if (!c_realm || !*c_realm) {
134 if ( use_own_domain )
135 c_realm = lp_workgroup();
138 if ( !c_realm || !*c_realm ) {
139 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
144 pstrcpy( realm, c_realm );
146 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
147 (got_realm ? "realm" : "domain"), realm));
149 if ( !get_sorted_dc_list(realm, &ip_list, &count, got_realm) ) {
150 /* fall back to netbios if we can */
151 if ( got_realm && !lp_disable_netbios() ) {
159 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
160 for ( i=0; i<count; i++ ) {
161 /* since this is an ads conection request, default to LDAP_PORT is not set */
162 int port = (ip_list[i].port!=PORT_NONE) ? ip_list[i].port : LDAP_PORT;
165 fstrcpy( server, inet_ntoa(ip_list[i].ip) );
167 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
170 if ( ads_try_connect(ads, server, port) ) {
175 /* keep track of failures */
176 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
186 * Connect to the LDAP server
187 * @param ads Pointer to an existing ADS_STRUCT
188 * @return status of connection
190 ADS_STATUS ads_connect(ADS_STRUCT *ads)
192 int version = LDAP_VERSION3;
195 ads->last_attempt = time(NULL);
198 /* try with a URL based server */
200 if (ads->server.ldap_uri &&
201 ads_try_connect_uri(ads)) {
205 /* try with a user specified server */
206 if (ads->server.ldap_server &&
207 ads_try_connect(ads, ads->server.ldap_server, LDAP_PORT)) {
211 if (ads_find_dc(ads)) {
215 return ADS_ERROR_SYSTEM(errno?errno:ENOENT);
218 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
220 status = ads_server_info(ads);
221 if (!ADS_ERR_OK(status)) {
222 DEBUG(1,("Failed to get ldap server info\n"));
226 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
228 if (!ads->auth.user_name) {
230 name_to_fqdn(my_fqdn, global_myname());
232 asprintf(&ads->auth.user_name, "host/%s", my_fqdn);
235 if (!ads->auth.realm) {
236 ads->auth.realm = strdup(ads->config.realm);
239 if (!ads->auth.kdc_server) {
240 ads->auth.kdc_server = strdup(inet_ntoa(ads->ldap_ip));
244 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
245 to MIT kerberos to work (tridge) */
248 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
249 setenv(env, ads->auth.kdc_server, 1);
254 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
258 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
259 return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
262 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
263 return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
266 return ads_sasl_bind(ads);
270 Duplicate a struct berval into talloc'ed memory
272 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
274 struct berval *value;
276 if (!in_val) return NULL;
278 value = talloc_zero(ctx, sizeof(struct berval));
281 if (in_val->bv_len == 0) return value;
283 value->bv_len = in_val->bv_len;
284 value->bv_val = talloc_memdup(ctx, in_val->bv_val, in_val->bv_len);
289 Make a values list out of an array of (struct berval *)
291 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
292 const struct berval **in_vals)
294 struct berval **values;
297 if (!in_vals) return NULL;
298 for (i=0; in_vals[i]; i++); /* count values */
299 values = (struct berval **) talloc_zero(ctx,
300 (i+1)*sizeof(struct berval *));
301 if (!values) return NULL;
303 for (i=0; in_vals[i]; i++) {
304 values[i] = dup_berval(ctx, in_vals[i]);
310 UTF8-encode a values list out of an array of (char *)
312 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
317 if (!in_vals) return NULL;
318 for (i=0; in_vals[i]; i++); /* count values */
319 values = (char ** ) talloc_zero(ctx, (i+1)*sizeof(char *));
320 if (!values) return NULL;
322 for (i=0; in_vals[i]; i++) {
323 push_utf8_talloc(ctx, &values[i], in_vals[i]);
329 Pull a (char *) array out of a UTF8-encoded values list
331 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
336 if (!in_vals) return NULL;
337 for (i=0; in_vals[i]; i++); /* count values */
338 values = (char **) talloc_zero(ctx, (i+1)*sizeof(char *));
339 if (!values) return NULL;
341 for (i=0; in_vals[i]; i++) {
342 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
348 * Do a search with paged results. cookie must be null on the first
349 * call, and then returned on each subsequent call. It will be null
350 * again when the entire search is complete
351 * @param ads connection to ads server
352 * @param bind_path Base dn for the search
353 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
354 * @param expr Search expression - specified in local charset
355 * @param attrs Attributes to retrieve - specified in utf8 or ascii
356 * @param res ** which will contain results - free res* with ads_msgfree()
357 * @param count Number of entries retrieved on this page
358 * @param cookie The paged results cookie to be returned on subsequent calls
359 * @return status of search
361 ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
362 int scope, const char *expr,
363 const char **attrs, void **res,
364 int *count, void **cookie)
367 char *utf8_expr, *utf8_path, **search_attrs;
368 LDAPControl PagedResults, NoReferrals, *controls[3], **rcontrols;
369 BerElement *cookie_be = NULL;
370 struct berval *cookie_bv= NULL;
375 if (!(ctx = talloc_init("ads_do_paged_search")))
376 return ADS_ERROR(LDAP_NO_MEMORY);
378 /* 0 means the conversion worked but the result was empty
379 so we only fail if it's -1. In any case, it always
380 at least nulls out the dest */
381 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
382 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
387 if (!attrs || !(*attrs))
390 /* This would be the utf8-encoded version...*/
391 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
392 if (!(str_list_copy(&search_attrs, attrs))) {
399 /* Paged results only available on ldap v3 or later */
400 ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
401 if (version < LDAP_VERSION3) {
402 rc = LDAP_NOT_SUPPORTED;
406 cookie_be = ber_alloc_t(LBER_USE_DER);
407 if (cookie && *cookie) {
408 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
409 ber_bvfree(*cookie); /* don't need it from last time */
412 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
414 ber_flatten(cookie_be, &cookie_bv);
415 PagedResults.ldctl_oid = ADS_PAGE_CTL_OID;
416 PagedResults.ldctl_iscritical = (char) 1;
417 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
418 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
420 NoReferrals.ldctl_oid = ADS_NO_REFERRALS_OID;
421 NoReferrals.ldctl_iscritical = (char) 0;
422 NoReferrals.ldctl_value.bv_len = 0;
423 NoReferrals.ldctl_value.bv_val = "";
426 controls[0] = &NoReferrals;
427 controls[1] = &PagedResults;
432 /* we need to disable referrals as the openldap libs don't
433 handle them and paged results at the same time. Using them
434 together results in the result record containing the server
435 page control being removed from the result list (tridge/jmcd)
437 leaving this in despite the control that says don't generate
438 referrals, in case the server doesn't support it (jmcd)
440 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
442 rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_expr,
443 search_attrs, 0, controls,
444 NULL, NULL, LDAP_NO_LIMIT, (LDAPMessage **)res);
446 ber_free(cookie_be, 1);
447 ber_bvfree(cookie_bv);
450 DEBUG(3,("ldap_search_ext_s(%s) -> %s\n", expr, ldap_err2string(rc)));
454 rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
455 NULL, &rcontrols, 0);
461 for (i=0; rcontrols[i]; i++) {
462 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
463 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
464 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
466 /* the berval is the cookie, but must be freed when
468 if (cookie_bv->bv_len) /* still more to do */
469 *cookie=ber_bvdup(cookie_bv);
472 ber_bvfree(cookie_bv);
473 ber_free(cookie_be, 1);
477 ldap_controls_free(rcontrols);
481 /* if/when we decide to utf8-encode attrs, take out this next line */
482 str_list_free(&search_attrs);
484 return ADS_ERROR(rc);
489 * Get all results for a search. This uses ads_do_paged_search() to return
490 * all entries in a large search.
491 * @param ads connection to ads server
492 * @param bind_path Base dn for the search
493 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
494 * @param expr Search expression
495 * @param attrs Attributes to retrieve
496 * @param res ** which will contain results - free res* with ads_msgfree()
497 * @return status of search
499 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
500 int scope, const char *expr,
501 const char **attrs, void **res)
507 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, res,
510 if (!ADS_ERR_OK(status)) return status;
515 LDAPMessage *msg, *next;
517 status2 = ads_do_paged_search(ads, bind_path, scope, expr,
518 attrs, &res2, &count, &cookie);
520 if (!ADS_ERR_OK(status2)) break;
522 /* this relies on the way that ldap_add_result_entry() works internally. I hope
523 that this works on all ldap libs, but I have only tested with openldap */
524 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
525 next = ads_next_entry(ads, msg);
526 ldap_add_result_entry((LDAPMessage **)res, msg);
528 /* note that we do not free res2, as the memory is now
529 part of the main returned list */
536 * Run a function on all results for a search. Uses ads_do_paged_search() and
537 * runs the function as each page is returned, using ads_process_results()
538 * @param ads connection to ads server
539 * @param bind_path Base dn for the search
540 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
541 * @param expr Search expression - specified in local charset
542 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
543 * @param fn Function which takes attr name, values list, and data_area
544 * @param data_area Pointer which is passed to function on each call
545 * @return status of search
547 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
548 int scope, const char *expr, const char **attrs,
549 BOOL(*fn)(char *, void **, void *),
557 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
560 if (!ADS_ERR_OK(status)) return status;
562 ads_process_results(ads, res, fn, data_area);
563 ads_msgfree(ads, res);
566 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
567 &res, &count, &cookie);
569 if (!ADS_ERR_OK(status)) break;
571 ads_process_results(ads, res, fn, data_area);
572 ads_msgfree(ads, res);
579 * Do a search with a timeout.
580 * @param ads connection to ads server
581 * @param bind_path Base dn for the search
582 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
583 * @param expr Search expression
584 * @param attrs Attributes to retrieve
585 * @param res ** which will contain results - free res* with ads_msgfree()
586 * @return status of search
588 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
590 const char **attrs, void **res)
592 struct timeval timeout;
594 char *utf8_expr, *utf8_path, **search_attrs = NULL;
597 if (!(ctx = talloc_init("ads_do_search"))) {
598 DEBUG(1,("ads_do_search: talloc_init() failed!"));
599 return ADS_ERROR(LDAP_NO_MEMORY);
602 /* 0 means the conversion worked but the result was empty
603 so we only fail if it's negative. In any case, it always
604 at least nulls out the dest */
605 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
606 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
607 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
612 if (!attrs || !(*attrs))
615 /* This would be the utf8-encoded version...*/
616 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
617 if (!(str_list_copy(&search_attrs, attrs)))
619 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
625 timeout.tv_sec = ADS_SEARCH_TIMEOUT;
629 /* see the note in ads_do_paged_search - we *must* disable referrals */
630 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
632 rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_expr,
633 search_attrs, 0, NULL, NULL,
634 &timeout, LDAP_NO_LIMIT, (LDAPMessage **)res);
636 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
637 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
643 /* if/when we decide to utf8-encode attrs, take out this next line */
644 str_list_free(&search_attrs);
645 return ADS_ERROR(rc);
648 * Do a general ADS search
649 * @param ads connection to ads server
650 * @param res ** which will contain results - free res* with ads_msgfree()
651 * @param expr Search expression
652 * @param attrs Attributes to retrieve
653 * @return status of search
655 ADS_STATUS ads_search(ADS_STRUCT *ads, void **res,
659 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
664 * Do a search on a specific DistinguishedName
665 * @param ads connection to ads server
666 * @param res ** which will contain results - free res* with ads_msgfree()
667 * @param dn DistinguishName to search
668 * @param attrs Attributes to retrieve
669 * @return status of search
671 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, void **res,
675 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res);
679 * Free up memory from a ads_search
680 * @param ads connection to ads server
681 * @param msg Search results to free
683 void ads_msgfree(ADS_STRUCT *ads, void *msg)
690 * Free up memory from various ads requests
691 * @param ads connection to ads server
692 * @param mem Area to free
694 void ads_memfree(ADS_STRUCT *ads, void *mem)
700 * Get a dn from search results
701 * @param ads connection to ads server
702 * @param msg Search result
705 char *ads_get_dn(ADS_STRUCT *ads, void *msg)
707 char *utf8_dn, *unix_dn;
709 utf8_dn = ldap_get_dn(ads->ld, msg);
712 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
716 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
717 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
721 ldap_memfree(utf8_dn);
726 * Find a machine account given a hostname
727 * @param ads connection to ads server
728 * @param res ** which will contain results - free res* with ads_msgfree()
729 * @param host Hostname to search for
730 * @return status of search
732 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *machine)
736 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
738 /* the easiest way to find a machine account anywhere in the tree
739 is to look for hostname$ */
740 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
741 DEBUG(1, ("asprintf failed!\n"));
742 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
745 status = ads_search(ads, res, expr, attrs);
751 * Initialize a list of mods to be used in a modify request
752 * @param ctx An initialized TALLOC_CTX
753 * @return allocated ADS_MODLIST
755 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
757 #define ADS_MODLIST_ALLOC_SIZE 10
760 if ((mods = (LDAPMod **) talloc_zero(ctx, sizeof(LDAPMod *) *
761 (ADS_MODLIST_ALLOC_SIZE + 1))))
762 /* -1 is safety to make sure we don't go over the end.
763 need to reset it to NULL before doing ldap modify */
764 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
771 add an attribute to the list, with values list already constructed
773 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
774 int mod_op, const char *name,
778 LDAPMod **modlist = (LDAPMod **) *mods;
779 struct berval **ber_values = NULL;
780 char **char_values = NULL;
783 mod_op = LDAP_MOD_DELETE;
785 if (mod_op & LDAP_MOD_BVALUES)
786 ber_values = ads_dup_values(ctx,
787 (const struct berval **)invals);
789 char_values = ads_push_strvals(ctx,
790 (const char **) invals);
793 /* find the first empty slot */
794 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
796 if (modlist[curmod] == (LDAPMod *) -1) {
797 if (!(modlist = talloc_realloc(ctx, modlist,
798 (curmod+ADS_MODLIST_ALLOC_SIZE+1)*sizeof(LDAPMod *))))
799 return ADS_ERROR(LDAP_NO_MEMORY);
800 memset(&modlist[curmod], 0,
801 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
802 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
806 if (!(modlist[curmod] = talloc_zero(ctx, sizeof(LDAPMod))))
807 return ADS_ERROR(LDAP_NO_MEMORY);
808 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
809 if (mod_op & LDAP_MOD_BVALUES) {
810 modlist[curmod]->mod_bvalues = ber_values;
811 } else if (mod_op & LDAP_MOD_DELETE) {
812 modlist[curmod]->mod_values = NULL;
814 modlist[curmod]->mod_values = char_values;
817 modlist[curmod]->mod_op = mod_op;
818 return ADS_ERROR(LDAP_SUCCESS);
822 * Add a single string value to a mod list
823 * @param ctx An initialized TALLOC_CTX
824 * @param mods An initialized ADS_MODLIST
825 * @param name The attribute name to add
826 * @param val The value to add - NULL means DELETE
827 * @return ADS STATUS indicating success of add
829 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
830 const char *name, const char *val)
832 const char *values[2];
838 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
839 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name,
840 (const void **) values);
844 * Add an array of string values to a mod list
845 * @param ctx An initialized TALLOC_CTX
846 * @param mods An initialized ADS_MODLIST
847 * @param name The attribute name to add
848 * @param vals The array of string values to add - NULL means DELETE
849 * @return ADS STATUS indicating success of add
851 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
852 const char *name, const char **vals)
855 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
856 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
857 name, (const void **) vals);
861 * Add a single ber-encoded value to a mod list
862 * @param ctx An initialized TALLOC_CTX
863 * @param mods An initialized ADS_MODLIST
864 * @param name The attribute name to add
865 * @param val The value to add - NULL means DELETE
866 * @return ADS STATUS indicating success of add
868 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
869 const char *name, const struct berval *val)
871 const struct berval *values[2];
876 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
877 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
878 name, (const void **) values);
882 * Perform an ldap modify
883 * @param ads connection to ads server
884 * @param mod_dn DistinguishedName to modify
885 * @param mods list of modifications to perform
886 * @return status of modify
888 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
891 char *utf8_dn = NULL;
893 this control is needed to modify that contains a currently
894 non-existent attribute (but allowable for the object) to run
896 LDAPControl PermitModify = {
897 ADS_PERMIT_MODIFY_OID,
900 LDAPControl *controls[2];
902 controls[0] = &PermitModify;
905 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
906 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
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 */
913 ret = ldap_modify_ext_s(ads->ld, utf8_dn,
914 (LDAPMod **) mods, controls, NULL);
916 return ADS_ERROR(ret);
920 * Perform an ldap add
921 * @param ads connection to ads server
922 * @param new_dn DistinguishedName to add
923 * @param mods list of attributes and values for DN
924 * @return status of add
926 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
929 char *utf8_dn = NULL;
931 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
932 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
933 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
936 /* find the end of the list, marked by NULL or -1 */
937 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
938 /* make sure the end of the list is NULL */
941 ret = ldap_add_s(ads->ld, utf8_dn, mods);
943 return ADS_ERROR(ret);
947 * Delete a DistinguishedName
948 * @param ads connection to ads server
949 * @param new_dn DistinguishedName to delete
950 * @return status of delete
952 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
955 char *utf8_dn = NULL;
956 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
957 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
958 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
961 ret = ldap_delete_s(ads->ld, utf8_dn);
962 return ADS_ERROR(ret);
966 * Build an org unit string
967 * if org unit is Computers or blank then assume a container, otherwise
968 * assume a \ separated list of organisational units
969 * @param org_unit Organizational unit
970 * @return org unit string - caller must free
972 char *ads_ou_string(const char *org_unit)
974 if (!org_unit || !*org_unit || strequal(org_unit, "Computers")) {
975 return strdup("cn=Computers");
978 return ads_build_path(org_unit, "\\/", "ou=", 1);
982 * Adds (appends) an item to an attribute array, rather then
983 * replacing the whole list
984 * @param ctx An initialized TALLOC_CTX
985 * @param mods An initialized ADS_MODLIST
986 * @param name name of the ldap attribute to append to
987 * @param vals an array of values to add
988 * @return status of addition
991 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
992 const char *name, const char **vals)
994 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name, (const void **) vals);
998 * Determines the computer account's current KVNO via an LDAP lookup
999 * @param ads An initialized ADS_STRUCT
1000 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1001 * @return the kvno for the computer account, or -1 in case of a failure.
1004 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1007 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1009 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1010 char *dn_string = NULL;
1011 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1013 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1014 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1017 ret = ads_search(ads, (void**) &res, filter, attrs);
1019 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1020 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1024 dn_string = ads_get_dn(ads, res);
1026 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1029 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1030 ads_memfree(ads, dn_string);
1032 /* ---------------------------------------------------------
1033 * 0 is returned as a default KVNO from this point on...
1034 * This is done because Windows 2000 does not support key
1035 * version numbers. Chances are that a failure in the next
1036 * step is simply due to Windows 2000 being used for a
1037 * domain controller. */
1040 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1041 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1042 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1047 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1052 * This clears out all registered spn's for a given hostname
1053 * @param ads An initilaized ADS_STRUCT
1054 * @param machine_name the NetBIOS name of the computer.
1055 * @return 0 upon success, non-zero otherwise.
1058 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1063 const char *servicePrincipalName[1] = {NULL};
1064 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1065 char *dn_string = NULL;
1067 ret = ads_find_machine_acct(ads, (void **)&res, machine_name);
1068 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1069 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1070 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1071 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1074 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1075 ctx = talloc_init("ads_clear_service_principal_names");
1077 return ADS_ERROR(LDAP_NO_MEMORY);
1080 if (!(mods = ads_init_mods(ctx))) {
1081 talloc_destroy(ctx);
1082 return ADS_ERROR(LDAP_NO_MEMORY);
1084 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1085 if (!ADS_ERR_OK(ret)) {
1086 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1087 talloc_destroy(ctx);
1090 dn_string = ads_get_dn(ads, res);
1092 talloc_destroy(ctx);
1093 return ADS_ERROR(LDAP_NO_MEMORY);
1095 ret = ads_gen_mod(ads, dn_string, mods);
1096 ads_memfree(ads,dn_string);
1097 if (!ADS_ERR_OK(ret)) {
1098 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1100 talloc_destroy(ctx);
1104 talloc_destroy(ctx);
1109 * This adds a service principal name to an existing computer account
1110 * (found by hostname) in AD.
1111 * @param ads An initialized ADS_STRUCT
1112 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1113 * @param spn A string of the service principal to add, i.e. 'host'
1114 * @return 0 upon sucess, or non-zero if a failure occurs
1117 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name, const char *spn)
1122 char *host_spn, *host_upn, *psp1, *psp2;
1125 char *dn_string = NULL;
1126 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1128 ret = ads_find_machine_acct(ads, (void **)&res, machine_name);
1129 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1130 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1132 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1133 spn, machine_name, ads->config.realm));
1134 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1137 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1138 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1139 return ADS_ERROR(LDAP_NO_MEMORY);
1142 name_to_fqdn(my_fqdn, machine_name);
1143 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", my_fqdn))) {
1144 talloc_destroy(ctx);
1145 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1147 if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm))) {
1148 talloc_destroy(ctx);
1149 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1152 /* Add the extra principal */
1153 psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name);
1155 strlower_m(&psp1[strlen(spn)]);
1156 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp1, machine_name));
1157 servicePrincipalName[0] = psp1;
1158 psp2 = talloc_asprintf(ctx, "%s/%s.%s", spn, machine_name, ads->config.realm);
1160 strlower_m(&psp2[strlen(spn)]);
1161 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp2, machine_name));
1162 servicePrincipalName[1] = psp2;
1164 if (!(mods = ads_init_mods(ctx))) {
1165 talloc_destroy(ctx);
1166 return ADS_ERROR(LDAP_NO_MEMORY);
1168 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1169 if (!ADS_ERR_OK(ret)) {
1170 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1171 talloc_destroy(ctx);
1174 dn_string = ads_get_dn(ads, res);
1176 talloc_destroy(ctx);
1177 return ADS_ERROR(LDAP_NO_MEMORY);
1179 ret = ads_gen_mod(ads, ads_get_dn(ads, res), mods);
1180 ads_memfree(ads,dn_string);
1181 if (!ADS_ERR_OK(ret)) {
1182 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1183 talloc_destroy(ctx);
1187 talloc_destroy(ctx);
1192 * adds a machine account to the ADS server
1193 * @param ads An intialized ADS_STRUCT
1194 * @param machine_name - the NetBIOS machine name of this account.
1195 * @param account_type A number indicating the type of account to create
1196 * @param org_unit The LDAP path in which to place this account
1197 * @return 0 upon success, or non-zero otherwise
1200 static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1201 uint32 account_type,
1202 const char *org_unit)
1204 ADS_STATUS ret, status;
1205 char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
1208 const char *objectClass[] = {"top", "person", "organizationalPerson",
1209 "user", "computer", NULL};
1210 const char *servicePrincipalName[5] = {NULL, NULL, NULL, NULL, NULL};
1212 unsigned acct_control;
1217 if (!(ctx = talloc_init("ads_add_machine_acct")))
1218 return ADS_ERROR(LDAP_NO_MEMORY);
1220 ret = ADS_ERROR(LDAP_NO_MEMORY);
1222 name_to_fqdn(my_fqdn, machine_name);
1224 status = ads_find_machine_acct(ads, (void **)&res, machine_name);
1225 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1226 char *dn_string = ads_get_dn(ads, res);
1228 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
1231 new_dn = talloc_strdup(ctx, dn_string);
1232 ads_memfree(ads,dn_string);
1233 DEBUG(0, ("ads_add_machine_acct: Host account for %s already exists - modifying old account\n",
1237 char *ou_str = ads_ou_string(org_unit);
1239 DEBUG(1, ("ads_add_machine_acct: ads_ou_string returned NULL (malloc failure?)\n"));
1242 new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", machine_name, ou_str,
1243 ads->config.bind_path);
1252 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", machine_name)))
1254 if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm)))
1256 servicePrincipalName[0] = talloc_asprintf(ctx, "HOST/%s", machine_name);
1257 psp = talloc_asprintf(ctx, "HOST/%s.%s",
1260 strlower_m(&psp[5]);
1261 servicePrincipalName[1] = psp;
1262 servicePrincipalName[2] = talloc_asprintf(ctx, "CIFS/%s", machine_name);
1263 psp2 = talloc_asprintf(ctx, "CIFS/%s.%s",
1266 strlower_m(&psp2[5]);
1267 servicePrincipalName[3] = psp2;
1269 if (!(samAccountName = talloc_asprintf(ctx, "%s$", machine_name))) {
1273 acct_control = account_type | UF_DONT_EXPIRE_PASSWD;
1274 #ifndef ENCTYPE_ARCFOUR_HMAC
1275 acct_control |= UF_USE_DES_KEY_ONLY;
1278 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1282 if (!(mods = ads_init_mods(ctx))) {
1287 ads_mod_str(ctx, &mods, "cn", machine_name);
1288 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1289 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1290 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1292 ads_mod_str(ctx, &mods, "dNSHostName", my_fqdn);
1293 ads_mod_str(ctx, &mods, "userPrincipalName", host_upn);
1294 ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1295 ads_mod_str(ctx, &mods, "operatingSystem", "Samba");
1296 ads_mod_str(ctx, &mods, "operatingSystemVersion", SAMBA_VERSION_STRING);
1299 ret = ads_gen_add(ads, new_dn, mods);
1301 ret = ads_gen_mod(ads, new_dn, mods);
1304 if (!ADS_ERR_OK(ret)) {
1308 /* Do not fail if we can't set security descriptor
1309 * it shouldn't be mandatory and probably we just
1310 * don't have enough rights to do it.
1313 status = ads_set_machine_sd(ads, machine_name, new_dn);
1315 if (!ADS_ERR_OK(status)) {
1316 DEBUG(0, ("Warning: ads_set_machine_sd: %s\n",
1317 ads_errstr(status)));
1321 talloc_destroy(ctx);
1326 dump a binary result from ldap
1328 static void dump_binary(const char *field, struct berval **values)
1331 for (i=0; values[i]; i++) {
1332 printf("%s: ", field);
1333 for (j=0; j<values[i]->bv_len; j++) {
1334 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1340 static void dump_guid(const char *field, struct berval **values)
1344 for (i=0; values[i]; i++) {
1345 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1346 printf("%s: %s\n", field,
1347 smb_uuid_string_static(smb_uuid_unpack_static(guid)));
1352 dump a sid result from ldap
1354 static void dump_sid(const char *field, struct berval **values)
1357 for (i=0; values[i]; i++) {
1359 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1360 printf("%s: %s\n", field, sid_string_static(&sid));
1365 dump ntSecurityDescriptor
1367 static void dump_sd(const char *filed, struct berval **values)
1372 TALLOC_CTX *ctx = 0;
1374 if (!(ctx = talloc_init("sec_io_desc")))
1378 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1379 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1380 prs_set_offset(&ps,0);
1383 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1385 talloc_destroy(ctx);
1388 if (psd) ads_disp_sd(psd);
1391 talloc_destroy(ctx);
1395 dump a string result from ldap
1397 static void dump_string(const char *field, char **values)
1400 for (i=0; values[i]; i++) {
1401 printf("%s: %s\n", field, values[i]);
1406 dump a field from LDAP on stdout
1410 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1415 void (*handler)(const char *, struct berval **);
1417 {"objectGUID", False, dump_guid},
1418 {"nTSecurityDescriptor", False, dump_sd},
1419 {"dnsRecord", False, dump_binary},
1420 {"objectSid", False, dump_sid},
1421 {"tokenGroups", False, dump_sid},
1426 if (!field) { /* must be end of an entry */
1431 for (i=0; handlers[i].name; i++) {
1432 if (StrCaseCmp(handlers[i].name, field) == 0) {
1433 if (!values) /* first time, indicate string or not */
1434 return handlers[i].string;
1435 handlers[i].handler(field, (struct berval **) values);
1439 if (!handlers[i].name) {
1440 if (!values) /* first time, indicate string conversion */
1442 dump_string(field, (char **)values);
1448 * Dump a result from LDAP on stdout
1449 * used for debugging
1450 * @param ads connection to ads server
1451 * @param res Results to dump
1454 void ads_dump(ADS_STRUCT *ads, void *res)
1456 ads_process_results(ads, res, ads_dump_field, NULL);
1460 * Walk through results, calling a function for each entry found.
1461 * The function receives a field name, a berval * array of values,
1462 * and a data area passed through from the start. The function is
1463 * called once with null for field and values at the end of each
1465 * @param ads connection to ads server
1466 * @param res Results to process
1467 * @param fn Function for processing each result
1468 * @param data_area user-defined area to pass to function
1470 void ads_process_results(ADS_STRUCT *ads, void *res,
1471 BOOL(*fn)(char *, void **, void *),
1477 if (!(ctx = talloc_init("ads_process_results")))
1480 for (msg = ads_first_entry(ads, res); msg;
1481 msg = ads_next_entry(ads, msg)) {
1485 for (utf8_field=ldap_first_attribute(ads->ld,
1486 (LDAPMessage *)msg,&b);
1488 utf8_field=ldap_next_attribute(ads->ld,
1489 (LDAPMessage *)msg,b)) {
1490 struct berval **ber_vals;
1491 char **str_vals, **utf8_vals;
1495 pull_utf8_talloc(ctx, &field, utf8_field);
1496 string = fn(field, NULL, data_area);
1499 utf8_vals = ldap_get_values(ads->ld,
1500 (LDAPMessage *)msg, field);
1501 str_vals = ads_pull_strvals(ctx,
1502 (const char **) utf8_vals);
1503 fn(field, (void **) str_vals, data_area);
1504 ldap_value_free(utf8_vals);
1506 ber_vals = ldap_get_values_len(ads->ld,
1507 (LDAPMessage *)msg, field);
1508 fn(field, (void **) ber_vals, data_area);
1510 ldap_value_free_len(ber_vals);
1512 ldap_memfree(utf8_field);
1515 talloc_destroy_pool(ctx);
1516 fn(NULL, NULL, data_area); /* completed an entry */
1519 talloc_destroy(ctx);
1523 * count how many replies are in a LDAPMessage
1524 * @param ads connection to ads server
1525 * @param res Results to count
1526 * @return number of replies
1528 int ads_count_replies(ADS_STRUCT *ads, void *res)
1530 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1534 * Join a machine to a realm
1535 * Creates the machine account and sets the machine password
1536 * @param ads connection to ads server
1537 * @param machine name of host to add
1538 * @param org_unit Organizational unit to place machine in
1539 * @return status of join
1541 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
1542 uint32 account_type, const char *org_unit)
1548 /* machine name must be lowercase */
1549 machine = strdup(machine_name);
1550 strlower_m(machine);
1553 status = ads_find_machine_acct(ads, (void **)&res, machine);
1554 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1555 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
1556 status = ads_leave_realm(ads, machine);
1557 if (!ADS_ERR_OK(status)) {
1558 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
1559 machine, ads->config.realm));
1565 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
1566 if (!ADS_ERR_OK(status)) {
1567 DEBUG(0, ("ads_add_machine_acct (%s): %s\n", machine, ads_errstr(status)));
1572 status = ads_find_machine_acct(ads, (void **)&res, machine);
1573 if (!ADS_ERR_OK(status)) {
1574 DEBUG(0, ("Host account test failed for machine %s\n", machine));
1585 * Delete a machine from the realm
1586 * @param ads connection to ads server
1587 * @param hostname Machine to remove
1588 * @return status of delete
1590 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
1594 char *hostnameDN, *host;
1597 /* hostname must be lowercase */
1598 host = strdup(hostname);
1601 status = ads_find_machine_acct(ads, &res, host);
1602 if (!ADS_ERR_OK(status)) {
1603 DEBUG(0, ("Host account for %s does not exist.\n", host));
1607 msg = ads_first_entry(ads, res);
1609 return ADS_ERROR_SYSTEM(ENOENT);
1612 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
1613 rc = ldap_delete_s(ads->ld, hostnameDN);
1614 ads_memfree(ads, hostnameDN);
1615 if (rc != LDAP_SUCCESS) {
1616 return ADS_ERROR(rc);
1619 status = ads_find_machine_acct(ads, &res, host);
1620 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1621 DEBUG(0, ("Failed to remove host account.\n"));
1631 * add machine account to existing security descriptor
1632 * @param ads connection to ads server
1633 * @param hostname machine to add
1634 * @param dn DN of security descriptor
1637 ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
1639 const char *attrs[] = {"nTSecurityDescriptor", "objectSid", 0};
1642 struct berval bval = {0, NULL};
1644 char *escaped_hostname = escape_ldap_string_alloc(hostname);
1646 LDAPMessage *res = 0;
1647 LDAPMessage *msg = 0;
1648 ADS_MODLIST mods = 0;
1653 SEC_DESC *psd = NULL;
1654 TALLOC_CTX *ctx = NULL;
1656 /* Avoid segmentation fault in prs_mem_free if
1657 * we have to bail out before prs_init */
1658 ps_wire.is_dynamic = False;
1660 if (!ads) return ADS_ERROR(LDAP_SERVER_DOWN);
1662 ret = ADS_ERROR(LDAP_SUCCESS);
1664 if (!escaped_hostname) {
1665 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1668 if (asprintf(&expr, "(samAccountName=%s$)", escaped_hostname) == -1) {
1669 DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n"));
1670 SAFE_FREE(escaped_hostname);
1671 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1674 SAFE_FREE(escaped_hostname);
1676 ret = ads_search(ads, (void *) &res, expr, attrs);
1678 if (!ADS_ERR_OK(ret)) return ret;
1680 if ( !(msg = ads_first_entry(ads, res) )) {
1681 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1682 goto ads_set_sd_error;
1685 if (!ads_pull_sid(ads, msg, attrs[1], &sid)) {
1686 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1687 goto ads_set_sd_error;
1690 if (!(ctx = talloc_init("sec_io_desc"))) {
1691 ret = ADS_ERROR(LDAP_NO_MEMORY);
1692 goto ads_set_sd_error;
1695 if (!ads_pull_sd(ads, ctx, msg, attrs[0], &psd)) {
1696 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1697 goto ads_set_sd_error;
1700 status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size);
1702 if (!NT_STATUS_IS_OK(status)) {
1703 ret = ADS_ERROR_NT(status);
1704 goto ads_set_sd_error;
1707 if (!prs_init(&ps_wire, sd_size, ctx, MARSHALL)) {
1708 ret = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1711 if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1)) {
1712 ret = ADS_ERROR(LDAP_NO_MEMORY);
1713 goto ads_set_sd_error;
1717 file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size);
1719 if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY);
1721 bval.bv_len = prs_offset(&ps_wire);
1722 bval.bv_val = talloc(ctx, bval.bv_len);
1724 ret = ADS_ERROR(LDAP_NO_MEMORY);
1725 goto ads_set_sd_error;
1728 prs_set_offset(&ps_wire, 0);
1730 if (!prs_copy_data_out(bval.bv_val, &ps_wire, bval.bv_len)) {
1731 ret = ADS_ERROR(LDAP_NO_MEMORY);
1732 goto ads_set_sd_error;
1735 ret = ads_mod_ber(ctx, &mods, attrs[0], &bval);
1736 if (ADS_ERR_OK(ret)) {
1737 ret = ads_gen_mod(ads, dn, mods);
1741 ads_msgfree(ads, res);
1742 prs_mem_free(&ps_wire);
1743 talloc_destroy(ctx);
1748 * pull the first entry from a ADS result
1749 * @param ads connection to ads server
1750 * @param res Results of search
1751 * @return first entry from result
1753 void *ads_first_entry(ADS_STRUCT *ads, void *res)
1755 return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
1759 * pull the next entry from a ADS result
1760 * @param ads connection to ads server
1761 * @param res Results of search
1762 * @return next entry from result
1764 void *ads_next_entry(ADS_STRUCT *ads, void *res)
1766 return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
1770 * pull a single string from a ADS result
1771 * @param ads connection to ads server
1772 * @param mem_ctx TALLOC_CTX to use for allocating result string
1773 * @param msg Results of search
1774 * @param field Attribute to retrieve
1775 * @return Result string in talloc context
1777 char *ads_pull_string(ADS_STRUCT *ads,
1778 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1785 values = ldap_get_values(ads->ld, msg, field);
1790 rc = pull_utf8_talloc(mem_ctx, &ux_string,
1792 if (rc != (size_t)-1)
1796 ldap_value_free(values);
1801 * pull an array of strings from a ADS result
1802 * @param ads connection to ads server
1803 * @param mem_ctx TALLOC_CTX to use for allocating result string
1804 * @param msg Results of search
1805 * @param field Attribute to retrieve
1806 * @return Result strings in talloc context
1808 char **ads_pull_strings(ADS_STRUCT *ads,
1809 TALLOC_CTX *mem_ctx, void *msg, const char *field,
1816 values = ldap_get_values(ads->ld, msg, field);
1820 *num_values = ldap_count_values(values);
1822 ret = talloc(mem_ctx, sizeof(char *) * (*num_values+1));
1824 ldap_value_free(values);
1828 for (i=0;i<*num_values;i++) {
1829 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
1830 ldap_value_free(values);
1836 ldap_value_free(values);
1841 * pull an array of strings from a ADS result
1842 * (handle large multivalue attributes with range retrieval)
1843 * @param ads connection to ads server
1844 * @param mem_ctx TALLOC_CTX to use for allocating result string
1845 * @param msg Results of search
1846 * @param field Attribute to retrieve
1847 * @param current_strings strings returned by a previous call to this function
1848 * @param next_attribute The next query should ask for this attribute
1849 * @param num_values How many values did we get this time?
1850 * @param more_values Are there more values to get?
1851 * @return Result strings in talloc context
1853 char **ads_pull_strings_range(ADS_STRUCT *ads,
1854 TALLOC_CTX *mem_ctx,
1855 void *msg, const char *field,
1856 char **current_strings,
1857 const char **next_attribute,
1858 size_t *num_strings,
1862 char *expected_range_attrib, *range_attr;
1863 BerElement *ptr = NULL;
1866 size_t num_new_strings;
1867 unsigned long int range_start;
1868 unsigned long int range_end;
1870 /* we might have been given the whole lot anyway */
1871 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
1872 *more_strings = False;
1876 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
1878 /* look for Range result */
1879 for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr);
1881 attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
1882 /* we ignore the fact that this is utf8, as all attributes are ascii... */
1883 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
1891 /* nothing here - this field is just empty */
1892 *more_strings = False;
1896 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
1897 &range_start, &range_end) == 2) {
1898 *more_strings = True;
1900 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
1901 &range_start) == 1) {
1902 *more_strings = False;
1904 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
1906 ldap_memfree(range_attr);
1907 *more_strings = False;
1912 if ((*num_strings) != range_start) {
1913 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
1914 " - aborting range retreival\n",
1915 range_attr, *num_strings + 1, range_start));
1916 ldap_memfree(range_attr);
1917 *more_strings = False;
1921 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
1923 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
1924 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
1925 "strings in this bunch, but we only got %lu - aborting range retreival\n",
1926 range_attr, (unsigned long int)range_end - range_start + 1,
1927 (unsigned long int)num_new_strings));
1928 ldap_memfree(range_attr);
1929 *more_strings = False;
1933 strings = talloc_realloc(mem_ctx, current_strings,
1934 sizeof(*current_strings) *
1935 (*num_strings + num_new_strings));
1937 if (strings == NULL) {
1938 ldap_memfree(range_attr);
1939 *more_strings = False;
1943 memcpy(&strings[*num_strings], new_strings,
1944 sizeof(*new_strings) * num_new_strings);
1946 (*num_strings) += num_new_strings;
1948 if (*more_strings) {
1949 *next_attribute = talloc_asprintf(mem_ctx,
1954 if (!*next_attribute) {
1955 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
1956 ldap_memfree(range_attr);
1957 *more_strings = False;
1962 ldap_memfree(range_attr);
1968 * pull a single uint32 from a ADS result
1969 * @param ads connection to ads server
1970 * @param msg Results of search
1971 * @param field Attribute to retrieve
1972 * @param v Pointer to int to store result
1973 * @return boolean inidicating success
1975 BOOL ads_pull_uint32(ADS_STRUCT *ads,
1976 void *msg, const char *field, uint32 *v)
1980 values = ldap_get_values(ads->ld, msg, field);
1984 ldap_value_free(values);
1988 *v = atoi(values[0]);
1989 ldap_value_free(values);
1994 * pull a single objectGUID from an ADS result
1995 * @param ads connection to ADS server
1996 * @param msg results of search
1997 * @param guid 37-byte area to receive text guid
1998 * @return boolean indicating success
2000 BOOL ads_pull_guid(ADS_STRUCT *ads,
2001 void *msg, struct uuid *guid)
2004 UUID_FLAT flat_guid;
2006 values = ldap_get_values(ads->ld, msg, "objectGUID");
2011 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2012 smb_uuid_unpack(flat_guid, guid);
2013 ldap_value_free(values);
2016 ldap_value_free(values);
2023 * pull a single DOM_SID from a ADS result
2024 * @param ads connection to ads server
2025 * @param msg Results of search
2026 * @param field Attribute to retrieve
2027 * @param sid Pointer to sid to store result
2028 * @return boolean inidicating success
2030 BOOL ads_pull_sid(ADS_STRUCT *ads,
2031 void *msg, const char *field, DOM_SID *sid)
2033 struct berval **values;
2036 values = ldap_get_values_len(ads->ld, msg, field);
2042 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2044 ldap_value_free_len(values);
2049 * pull an array of DOM_SIDs from a ADS result
2050 * @param ads connection to ads server
2051 * @param mem_ctx TALLOC_CTX for allocating sid array
2052 * @param msg Results of search
2053 * @param field Attribute to retrieve
2054 * @param sids pointer to sid array to allocate
2055 * @return the count of SIDs pulled
2057 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2058 void *msg, const char *field, DOM_SID **sids)
2060 struct berval **values;
2064 values = ldap_get_values_len(ads->ld, msg, field);
2069 for (i=0; values[i]; i++)
2072 (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * i);
2074 ldap_value_free_len(values);
2079 for (i=0; values[i]; i++) {
2080 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2083 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2088 ldap_value_free_len(values);
2093 * pull a SEC_DESC from a ADS result
2094 * @param ads connection to ads server
2095 * @param mem_ctx TALLOC_CTX for allocating sid array
2096 * @param msg Results of search
2097 * @param field Attribute to retrieve
2098 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2099 * @return boolean inidicating success
2101 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2102 void *msg, const char *field, SEC_DESC **sd)
2104 struct berval **values;
2108 values = ldap_get_values_len(ads->ld, msg, field);
2110 if (!values) return False;
2113 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2114 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2115 prs_set_offset(&ps,0);
2117 ret = sec_io_desc("sd", sd, &ps, 1);
2120 ldap_value_free_len(values);
2125 * in order to support usernames longer than 21 characters we need to
2126 * use both the sAMAccountName and the userPrincipalName attributes
2127 * It seems that not all users have the userPrincipalName attribute set
2129 * @param ads connection to ads server
2130 * @param mem_ctx TALLOC_CTX for allocating sid array
2131 * @param msg Results of search
2132 * @return the username
2134 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
2138 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2139 if (ret && (p = strchr(ret, '@'))) {
2143 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2148 * find the update serial number - this is the core of the ldap cache
2149 * @param ads connection to ads server
2150 * @param ads connection to ADS server
2151 * @param usn Pointer to retrieved update serial number
2152 * @return status of search
2154 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2156 const char *attrs[] = {"highestCommittedUSN", NULL};
2160 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2161 if (!ADS_ERR_OK(status))
2164 if (ads_count_replies(ads, res) != 1) {
2165 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2168 ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
2169 ads_msgfree(ads, res);
2173 /* parse a ADS timestring - typical string is
2174 '20020917091222.0Z0' which means 09:12.22 17th September
2176 static time_t ads_parse_time(const char *str)
2182 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2183 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2184 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2195 * Find the servers name and realm - this can be done before authentication
2196 * The ldapServiceName field on w2k looks like this:
2197 * vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
2198 * @param ads connection to ads server
2199 * @return status of search
2201 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
2203 const char *attrs[] = {"ldapServiceName", "currentTime", NULL};
2211 if (!(ctx = talloc_init("ads_server_info"))) {
2212 return ADS_ERROR(LDAP_NO_MEMORY);
2215 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2216 if (!ADS_ERR_OK(status)) {
2217 talloc_destroy(ctx);
2221 value = ads_pull_string(ads, ctx, res, "ldapServiceName");
2223 ads_msgfree(ads, res);
2224 talloc_destroy(ctx);
2225 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2228 timestr = ads_pull_string(ads, ctx, res, "currentTime");
2230 ads_msgfree(ads, res);
2231 talloc_destroy(ctx);
2232 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2235 ads_msgfree(ads, res);
2237 p = strchr(value, ':');
2239 talloc_destroy(ctx);
2240 DEBUG(1, ("ads_server_info: returned ldap server name did not contain a ':' "
2241 "so was deemed invalid\n"));
2242 return ADS_ERROR(LDAP_DECODING_ERROR);
2245 SAFE_FREE(ads->config.ldap_server_name);
2247 ads->config.ldap_server_name = strdup(p+1);
2248 p = strchr(ads->config.ldap_server_name, '$');
2249 if (!p || p[1] != '@') {
2250 talloc_destroy(ctx);
2251 DEBUG(1, ("ads_server_info: returned ldap server name (%s) does not contain '$@'"
2252 " so was deemed invalid\n", ads->config.ldap_server_name));
2253 SAFE_FREE(ads->config.ldap_server_name);
2254 return ADS_ERROR(LDAP_DECODING_ERROR);
2259 SAFE_FREE(ads->config.realm);
2260 SAFE_FREE(ads->config.bind_path);
2262 ads->config.realm = strdup(p+2);
2263 ads->config.bind_path = ads_build_dn(ads->config.realm);
2265 DEBUG(3,("got ldap server name %s@%s, using bind path: %s\n",
2266 ads->config.ldap_server_name, ads->config.realm,
2267 ads->config.bind_path));
2269 ads->config.current_time = ads_parse_time(timestr);
2271 if (ads->config.current_time != 0) {
2272 ads->auth.time_offset = ads->config.current_time - time(NULL);
2273 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2276 talloc_destroy(ctx);
2282 * find the domain sid for our domain
2283 * @param ads connection to ads server
2284 * @param sid Pointer to domain sid
2285 * @return status of search
2287 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2289 const char *attrs[] = {"objectSid", NULL};
2293 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2295 if (!ADS_ERR_OK(rc)) return rc;
2296 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2297 ads_msgfree(ads, res);
2298 return ADS_ERROR_SYSTEM(ENOENT);
2300 ads_msgfree(ads, res);
2305 /* this is rather complex - we need to find the allternate (netbios) name
2306 for the domain, but there isn't a simple query to do this. Instead
2307 we look for the principle names on the DCs account and find one that has
2308 the right form, then extract the netbios name of the domain from that
2310 NOTE! better method is this:
2312 bin/net -Uadministrator%XXXXX ads search '(&(objectclass=crossref)(dnsroot=VNET3.HOME.SAMBA.ORG))' nETBIOSName
2314 but you need to force the bind path to match the configurationNamingContext from the rootDSE
2317 ADS_STATUS ads_workgroup_name(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **workgroup)
2326 const char *attrs[] = {"servicePrincipalName", NULL};
2329 (*workgroup) = NULL;
2331 asprintf(&expr, "(&(objectclass=computer)(dnshostname=%s.%s))",
2332 ads->config.ldap_server_name, ads->config.realm);
2333 rc = ads_search(ads, &res, expr, attrs);
2336 if (!ADS_ERR_OK(rc)) {
2340 principles = ads_pull_strings(ads, mem_ctx, res,
2341 "servicePrincipalName", &num_principals);
2343 ads_msgfree(ads, res);
2346 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2349 asprintf(&prefix, "HOST/%s.%s/",
2350 ads->config.ldap_server_name,
2353 prefix_length = strlen(prefix);
2355 for (i=0;principles[i]; i++) {
2356 if (strnequal(principles[i], prefix, prefix_length) &&
2357 !strequal(ads->config.realm, principles[i]+prefix_length) &&
2358 !strchr(principles[i]+prefix_length, '.')) {
2359 /* found an alternate (short) name for the domain. */
2360 DEBUG(3,("Found alternate name '%s' for realm '%s'\n",
2361 principles[i]+prefix_length,
2362 ads->config.realm));
2363 (*workgroup) = talloc_strdup(mem_ctx, principles[i]+prefix_length);
2370 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);