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)
1006 LDAPMessage *res = NULL;
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));
1021 ads_msgfree(ads, res);
1025 dn_string = ads_get_dn(ads, res);
1027 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1028 ads_msgfree(ads, res);
1031 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1032 ads_memfree(ads, dn_string);
1034 /* ---------------------------------------------------------
1035 * 0 is returned as a default KVNO from this point on...
1036 * This is done because Windows 2000 does not support key
1037 * version numbers. Chances are that a failure in the next
1038 * step is simply due to Windows 2000 being used for a
1039 * domain controller. */
1042 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1043 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1044 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1045 ads_msgfree(ads, res);
1050 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1051 ads_msgfree(ads, res);
1056 * This clears out all registered spn's for a given hostname
1057 * @param ads An initilaized ADS_STRUCT
1058 * @param machine_name the NetBIOS name of the computer.
1059 * @return 0 upon success, non-zero otherwise.
1062 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1065 LDAPMessage *res = NULL;
1067 const char *servicePrincipalName[1] = {NULL};
1068 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1069 char *dn_string = NULL;
1071 ret = ads_find_machine_acct(ads, (void **)&res, machine_name);
1072 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1073 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1074 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1075 ads_msgfree(ads, res);
1076 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1079 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1080 ctx = talloc_init("ads_clear_service_principal_names");
1082 ads_msgfree(ads, res);
1083 return ADS_ERROR(LDAP_NO_MEMORY);
1086 if (!(mods = ads_init_mods(ctx))) {
1087 talloc_destroy(ctx);
1088 ads_msgfree(ads, res);
1089 return ADS_ERROR(LDAP_NO_MEMORY);
1091 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1092 if (!ADS_ERR_OK(ret)) {
1093 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1094 ads_msgfree(ads, res);
1095 talloc_destroy(ctx);
1098 dn_string = ads_get_dn(ads, res);
1100 talloc_destroy(ctx);
1101 ads_msgfree(ads, res);
1102 return ADS_ERROR(LDAP_NO_MEMORY);
1104 ret = ads_gen_mod(ads, dn_string, mods);
1105 ads_memfree(ads,dn_string);
1106 if (!ADS_ERR_OK(ret)) {
1107 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1109 ads_msgfree(ads, res);
1110 talloc_destroy(ctx);
1114 ads_msgfree(ads, res);
1115 talloc_destroy(ctx);
1120 * This adds a service principal name to an existing computer account
1121 * (found by hostname) in AD.
1122 * @param ads An initialized ADS_STRUCT
1123 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1124 * @param spn A string of the service principal to add, i.e. 'host'
1125 * @return 0 upon sucess, or non-zero if a failure occurs
1128 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name, const char *spn)
1132 LDAPMessage *res = NULL;
1133 char *host_spn, *host_upn, *psp1, *psp2;
1136 char *dn_string = NULL;
1137 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1139 ret = ads_find_machine_acct(ads, (void **)&res, machine_name);
1140 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1141 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1143 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1144 spn, machine_name, ads->config.realm));
1145 ads_msgfree(ads, res);
1146 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1149 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1150 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1151 ads_msgfree(ads, res);
1152 return ADS_ERROR(LDAP_NO_MEMORY);
1155 name_to_fqdn(my_fqdn, machine_name);
1156 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", my_fqdn))) {
1157 talloc_destroy(ctx);
1158 ads_msgfree(ads, res);
1159 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1161 if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm))) {
1162 talloc_destroy(ctx);
1163 ads_msgfree(ads, res);
1164 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1167 /* Add the extra principal */
1168 psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name);
1170 strlower_m(&psp1[strlen(spn)]);
1171 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp1, machine_name));
1172 servicePrincipalName[0] = psp1;
1173 psp2 = talloc_asprintf(ctx, "%s/%s.%s", spn, machine_name, ads->config.realm);
1175 strlower_m(&psp2[strlen(spn)]);
1176 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp2, machine_name));
1177 servicePrincipalName[1] = psp2;
1179 if (!(mods = ads_init_mods(ctx))) {
1180 talloc_destroy(ctx);
1181 ads_msgfree(ads, res);
1182 return ADS_ERROR(LDAP_NO_MEMORY);
1184 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1185 if (!ADS_ERR_OK(ret)) {
1186 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1187 talloc_destroy(ctx);
1188 ads_msgfree(ads, res);
1191 dn_string = ads_get_dn(ads, res);
1193 talloc_destroy(ctx);
1194 ads_msgfree(ads, res);
1195 return ADS_ERROR(LDAP_NO_MEMORY);
1197 ret = ads_gen_mod(ads, dn_string, mods);
1198 ads_memfree(ads,dn_string);
1199 if (!ADS_ERR_OK(ret)) {
1200 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1201 talloc_destroy(ctx);
1202 ads_msgfree(ads, res);
1206 talloc_destroy(ctx);
1207 ads_msgfree(ads, res);
1212 * adds a machine account to the ADS server
1213 * @param ads An intialized ADS_STRUCT
1214 * @param machine_name - the NetBIOS machine name of this account.
1215 * @param account_type A number indicating the type of account to create
1216 * @param org_unit The LDAP path in which to place this account
1217 * @return 0 upon success, or non-zero otherwise
1220 static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1221 uint32 account_type,
1222 const char *org_unit)
1224 ADS_STATUS ret, status;
1225 char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
1228 const char *objectClass[] = {"top", "person", "organizationalPerson",
1229 "user", "computer", NULL};
1230 const char *servicePrincipalName[5] = {NULL, NULL, NULL, NULL, NULL};
1232 unsigned acct_control;
1235 LDAPMessage *res = NULL;
1237 if (!(ctx = talloc_init("ads_add_machine_acct")))
1238 return ADS_ERROR(LDAP_NO_MEMORY);
1240 ret = ADS_ERROR(LDAP_NO_MEMORY);
1242 name_to_fqdn(my_fqdn, machine_name);
1244 status = ads_find_machine_acct(ads, (void **)&res, machine_name);
1245 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1246 char *dn_string = ads_get_dn(ads, res);
1248 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
1251 new_dn = talloc_strdup(ctx, dn_string);
1252 ads_memfree(ads,dn_string);
1253 DEBUG(0, ("ads_add_machine_acct: Host account for %s already exists - modifying old account\n",
1257 char *ou_str = ads_ou_string(org_unit);
1259 DEBUG(1, ("ads_add_machine_acct: ads_ou_string returned NULL (malloc failure?)\n"));
1262 new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", machine_name, ou_str,
1263 ads->config.bind_path);
1272 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", machine_name)))
1274 if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm)))
1276 servicePrincipalName[0] = talloc_asprintf(ctx, "HOST/%s", machine_name);
1277 psp = talloc_asprintf(ctx, "HOST/%s.%s",
1280 strlower_m(&psp[5]);
1281 servicePrincipalName[1] = psp;
1282 servicePrincipalName[2] = talloc_asprintf(ctx, "CIFS/%s", machine_name);
1283 psp2 = talloc_asprintf(ctx, "CIFS/%s.%s",
1286 strlower_m(&psp2[5]);
1287 servicePrincipalName[3] = psp2;
1289 if (!(samAccountName = talloc_asprintf(ctx, "%s$", machine_name))) {
1293 acct_control = account_type | UF_DONT_EXPIRE_PASSWD;
1294 #ifndef ENCTYPE_ARCFOUR_HMAC
1295 acct_control |= UF_USE_DES_KEY_ONLY;
1298 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1302 if (!(mods = ads_init_mods(ctx))) {
1307 ads_mod_str(ctx, &mods, "cn", machine_name);
1308 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1309 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1310 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1312 ads_mod_str(ctx, &mods, "dNSHostName", my_fqdn);
1313 ads_mod_str(ctx, &mods, "userPrincipalName", host_upn);
1314 ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1315 ads_mod_str(ctx, &mods, "operatingSystem", "Samba");
1316 ads_mod_str(ctx, &mods, "operatingSystemVersion", SAMBA_VERSION_STRING);
1319 ret = ads_gen_add(ads, new_dn, mods);
1321 ret = ads_gen_mod(ads, new_dn, mods);
1324 if (!ADS_ERR_OK(ret)) {
1328 /* Do not fail if we can't set security descriptor
1329 * it shouldn't be mandatory and probably we just
1330 * don't have enough rights to do it.
1333 status = ads_set_machine_sd(ads, machine_name, new_dn);
1335 if (!ADS_ERR_OK(status)) {
1336 DEBUG(0, ("Warning: ads_set_machine_sd: %s\n",
1337 ads_errstr(status)));
1341 ads_msgfree(ads, res);
1342 talloc_destroy(ctx);
1347 dump a binary result from ldap
1349 static void dump_binary(const char *field, struct berval **values)
1352 for (i=0; values[i]; i++) {
1353 printf("%s: ", field);
1354 for (j=0; j<values[i]->bv_len; j++) {
1355 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1361 static void dump_guid(const char *field, struct berval **values)
1365 for (i=0; values[i]; i++) {
1366 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1367 printf("%s: %s\n", field,
1368 smb_uuid_string_static(smb_uuid_unpack_static(guid)));
1373 dump a sid result from ldap
1375 static void dump_sid(const char *field, struct berval **values)
1378 for (i=0; values[i]; i++) {
1380 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1381 printf("%s: %s\n", field, sid_string_static(&sid));
1386 dump ntSecurityDescriptor
1388 static void dump_sd(const char *filed, struct berval **values)
1393 TALLOC_CTX *ctx = 0;
1395 if (!(ctx = talloc_init("sec_io_desc")))
1399 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1400 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1401 prs_set_offset(&ps,0);
1404 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1406 talloc_destroy(ctx);
1409 if (psd) ads_disp_sd(psd);
1412 talloc_destroy(ctx);
1416 dump a string result from ldap
1418 static void dump_string(const char *field, char **values)
1421 for (i=0; values[i]; i++) {
1422 printf("%s: %s\n", field, values[i]);
1427 dump a field from LDAP on stdout
1431 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1436 void (*handler)(const char *, struct berval **);
1438 {"objectGUID", False, dump_guid},
1439 {"nTSecurityDescriptor", False, dump_sd},
1440 {"dnsRecord", False, dump_binary},
1441 {"objectSid", False, dump_sid},
1442 {"tokenGroups", False, dump_sid},
1447 if (!field) { /* must be end of an entry */
1452 for (i=0; handlers[i].name; i++) {
1453 if (StrCaseCmp(handlers[i].name, field) == 0) {
1454 if (!values) /* first time, indicate string or not */
1455 return handlers[i].string;
1456 handlers[i].handler(field, (struct berval **) values);
1460 if (!handlers[i].name) {
1461 if (!values) /* first time, indicate string conversion */
1463 dump_string(field, (char **)values);
1469 * Dump a result from LDAP on stdout
1470 * used for debugging
1471 * @param ads connection to ads server
1472 * @param res Results to dump
1475 void ads_dump(ADS_STRUCT *ads, void *res)
1477 ads_process_results(ads, res, ads_dump_field, NULL);
1481 * Walk through results, calling a function for each entry found.
1482 * The function receives a field name, a berval * array of values,
1483 * and a data area passed through from the start. The function is
1484 * called once with null for field and values at the end of each
1486 * @param ads connection to ads server
1487 * @param res Results to process
1488 * @param fn Function for processing each result
1489 * @param data_area user-defined area to pass to function
1491 void ads_process_results(ADS_STRUCT *ads, void *res,
1492 BOOL(*fn)(char *, void **, void *),
1498 if (!(ctx = talloc_init("ads_process_results")))
1501 for (msg = ads_first_entry(ads, res); msg;
1502 msg = ads_next_entry(ads, msg)) {
1506 for (utf8_field=ldap_first_attribute(ads->ld,
1507 (LDAPMessage *)msg,&b);
1509 utf8_field=ldap_next_attribute(ads->ld,
1510 (LDAPMessage *)msg,b)) {
1511 struct berval **ber_vals;
1512 char **str_vals, **utf8_vals;
1516 pull_utf8_talloc(ctx, &field, utf8_field);
1517 string = fn(field, NULL, data_area);
1520 utf8_vals = ldap_get_values(ads->ld,
1521 (LDAPMessage *)msg, field);
1522 str_vals = ads_pull_strvals(ctx,
1523 (const char **) utf8_vals);
1524 fn(field, (void **) str_vals, data_area);
1525 ldap_value_free(utf8_vals);
1527 ber_vals = ldap_get_values_len(ads->ld,
1528 (LDAPMessage *)msg, field);
1529 fn(field, (void **) ber_vals, data_area);
1531 ldap_value_free_len(ber_vals);
1533 ldap_memfree(utf8_field);
1536 talloc_destroy_pool(ctx);
1537 fn(NULL, NULL, data_area); /* completed an entry */
1540 talloc_destroy(ctx);
1544 * count how many replies are in a LDAPMessage
1545 * @param ads connection to ads server
1546 * @param res Results to count
1547 * @return number of replies
1549 int ads_count_replies(ADS_STRUCT *ads, void *res)
1551 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1555 * Join a machine to a realm
1556 * Creates the machine account and sets the machine password
1557 * @param ads connection to ads server
1558 * @param machine name of host to add
1559 * @param org_unit Organizational unit to place machine in
1560 * @return status of join
1562 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
1563 uint32 account_type, const char *org_unit)
1566 LDAPMessage *res = NULL;
1569 /* machine name must be lowercase */
1570 machine = strdup(machine_name);
1571 strlower_m(machine);
1574 status = ads_find_machine_acct(ads, (void **)&res, machine);
1575 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1576 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
1577 status = ads_leave_realm(ads, machine);
1578 if (!ADS_ERR_OK(status)) {
1579 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
1580 machine, ads->config.realm));
1586 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
1587 if (!ADS_ERR_OK(status)) {
1588 DEBUG(0, ("ads_add_machine_acct (%s): %s\n", machine, ads_errstr(status)));
1593 status = ads_find_machine_acct(ads, (void **)&res, machine);
1594 if (!ADS_ERR_OK(status)) {
1595 DEBUG(0, ("Host account test failed for machine %s\n", machine));
1601 ads_msgfree(ads, res);
1607 * Delete a machine from the realm
1608 * @param ads connection to ads server
1609 * @param hostname Machine to remove
1610 * @return status of delete
1612 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
1616 char *hostnameDN, *host;
1619 /* hostname must be lowercase */
1620 host = strdup(hostname);
1623 status = ads_find_machine_acct(ads, &res, host);
1624 if (!ADS_ERR_OK(status)) {
1625 DEBUG(0, ("Host account for %s does not exist.\n", host));
1629 msg = ads_first_entry(ads, res);
1631 return ADS_ERROR_SYSTEM(ENOENT);
1634 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
1635 rc = ldap_delete_s(ads->ld, hostnameDN);
1636 ads_memfree(ads, hostnameDN);
1637 if (rc != LDAP_SUCCESS) {
1638 return ADS_ERROR(rc);
1641 status = ads_find_machine_acct(ads, &res, host);
1642 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1643 DEBUG(0, ("Failed to remove host account.\n"));
1653 * add machine account to existing security descriptor
1654 * @param ads connection to ads server
1655 * @param hostname machine to add
1656 * @param dn DN of security descriptor
1659 ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
1661 const char *attrs[] = {"nTSecurityDescriptor", "objectSid", 0};
1664 struct berval bval = {0, NULL};
1666 char *escaped_hostname = escape_ldap_string_alloc(hostname);
1668 LDAPMessage *res = 0;
1669 LDAPMessage *msg = 0;
1670 ADS_MODLIST mods = 0;
1675 SEC_DESC *psd = NULL;
1676 TALLOC_CTX *ctx = NULL;
1678 /* Avoid segmentation fault in prs_mem_free if
1679 * we have to bail out before prs_init */
1680 ps_wire.is_dynamic = False;
1682 if (!ads) return ADS_ERROR(LDAP_SERVER_DOWN);
1684 ret = ADS_ERROR(LDAP_SUCCESS);
1686 if (!escaped_hostname) {
1687 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1690 if (asprintf(&expr, "(samAccountName=%s$)", escaped_hostname) == -1) {
1691 DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n"));
1692 SAFE_FREE(escaped_hostname);
1693 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1696 SAFE_FREE(escaped_hostname);
1698 ret = ads_search(ads, (void *) &res, expr, attrs);
1700 if (!ADS_ERR_OK(ret)) return ret;
1702 if ( !(msg = ads_first_entry(ads, res) )) {
1703 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1704 goto ads_set_sd_error;
1707 if (!ads_pull_sid(ads, msg, attrs[1], &sid)) {
1708 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1709 goto ads_set_sd_error;
1712 if (!(ctx = talloc_init("sec_io_desc"))) {
1713 ret = ADS_ERROR(LDAP_NO_MEMORY);
1714 goto ads_set_sd_error;
1717 if (!ads_pull_sd(ads, ctx, msg, attrs[0], &psd)) {
1718 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1719 goto ads_set_sd_error;
1722 status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size);
1724 if (!NT_STATUS_IS_OK(status)) {
1725 ret = ADS_ERROR_NT(status);
1726 goto ads_set_sd_error;
1729 if (!prs_init(&ps_wire, sd_size, ctx, MARSHALL)) {
1730 ret = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1733 if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1)) {
1734 ret = ADS_ERROR(LDAP_NO_MEMORY);
1735 goto ads_set_sd_error;
1739 file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size);
1741 if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY);
1743 bval.bv_len = prs_offset(&ps_wire);
1744 bval.bv_val = talloc(ctx, bval.bv_len);
1746 ret = ADS_ERROR(LDAP_NO_MEMORY);
1747 goto ads_set_sd_error;
1750 prs_set_offset(&ps_wire, 0);
1752 if (!prs_copy_data_out(bval.bv_val, &ps_wire, bval.bv_len)) {
1753 ret = ADS_ERROR(LDAP_NO_MEMORY);
1754 goto ads_set_sd_error;
1757 ret = ads_mod_ber(ctx, &mods, attrs[0], &bval);
1758 if (ADS_ERR_OK(ret)) {
1759 ret = ads_gen_mod(ads, dn, mods);
1763 ads_msgfree(ads, res);
1764 prs_mem_free(&ps_wire);
1765 talloc_destroy(ctx);
1770 * pull the first entry from a ADS result
1771 * @param ads connection to ads server
1772 * @param res Results of search
1773 * @return first entry from result
1775 void *ads_first_entry(ADS_STRUCT *ads, void *res)
1777 return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
1781 * pull the next entry from a ADS result
1782 * @param ads connection to ads server
1783 * @param res Results of search
1784 * @return next entry from result
1786 void *ads_next_entry(ADS_STRUCT *ads, void *res)
1788 return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
1792 * pull a single string from a ADS result
1793 * @param ads connection to ads server
1794 * @param mem_ctx TALLOC_CTX to use for allocating result string
1795 * @param msg Results of search
1796 * @param field Attribute to retrieve
1797 * @return Result string in talloc context
1799 char *ads_pull_string(ADS_STRUCT *ads,
1800 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1807 values = ldap_get_values(ads->ld, msg, field);
1812 rc = pull_utf8_talloc(mem_ctx, &ux_string,
1814 if (rc != (size_t)-1)
1818 ldap_value_free(values);
1823 * pull an array of strings from a ADS result
1824 * @param ads connection to ads server
1825 * @param mem_ctx TALLOC_CTX to use for allocating result string
1826 * @param msg Results of search
1827 * @param field Attribute to retrieve
1828 * @return Result strings in talloc context
1830 char **ads_pull_strings(ADS_STRUCT *ads,
1831 TALLOC_CTX *mem_ctx, void *msg, const char *field,
1838 values = ldap_get_values(ads->ld, msg, field);
1842 *num_values = ldap_count_values(values);
1844 ret = talloc(mem_ctx, sizeof(char *) * (*num_values+1));
1846 ldap_value_free(values);
1850 for (i=0;i<*num_values;i++) {
1851 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
1852 ldap_value_free(values);
1858 ldap_value_free(values);
1863 * pull an array of strings from a ADS result
1864 * (handle large multivalue attributes with range retrieval)
1865 * @param ads connection to ads server
1866 * @param mem_ctx TALLOC_CTX to use for allocating result string
1867 * @param msg Results of search
1868 * @param field Attribute to retrieve
1869 * @param current_strings strings returned by a previous call to this function
1870 * @param next_attribute The next query should ask for this attribute
1871 * @param num_values How many values did we get this time?
1872 * @param more_values Are there more values to get?
1873 * @return Result strings in talloc context
1875 char **ads_pull_strings_range(ADS_STRUCT *ads,
1876 TALLOC_CTX *mem_ctx,
1877 void *msg, const char *field,
1878 char **current_strings,
1879 const char **next_attribute,
1880 size_t *num_strings,
1884 char *expected_range_attrib, *range_attr;
1885 BerElement *ptr = NULL;
1888 size_t num_new_strings;
1889 unsigned long int range_start;
1890 unsigned long int range_end;
1892 /* we might have been given the whole lot anyway */
1893 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
1894 *more_strings = False;
1898 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
1900 /* look for Range result */
1901 for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr);
1903 attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
1904 /* we ignore the fact that this is utf8, as all attributes are ascii... */
1905 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
1913 /* nothing here - this field is just empty */
1914 *more_strings = False;
1918 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
1919 &range_start, &range_end) == 2) {
1920 *more_strings = True;
1922 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
1923 &range_start) == 1) {
1924 *more_strings = False;
1926 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
1928 ldap_memfree(range_attr);
1929 *more_strings = False;
1934 if ((*num_strings) != range_start) {
1935 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
1936 " - aborting range retreival\n",
1937 range_attr, *num_strings + 1, range_start));
1938 ldap_memfree(range_attr);
1939 *more_strings = False;
1943 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
1945 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
1946 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
1947 "strings in this bunch, but we only got %lu - aborting range retreival\n",
1948 range_attr, (unsigned long int)range_end - range_start + 1,
1949 (unsigned long int)num_new_strings));
1950 ldap_memfree(range_attr);
1951 *more_strings = False;
1955 strings = talloc_realloc(mem_ctx, current_strings,
1956 sizeof(*current_strings) *
1957 (*num_strings + num_new_strings));
1959 if (strings == NULL) {
1960 ldap_memfree(range_attr);
1961 *more_strings = False;
1965 memcpy(&strings[*num_strings], new_strings,
1966 sizeof(*new_strings) * num_new_strings);
1968 (*num_strings) += num_new_strings;
1970 if (*more_strings) {
1971 *next_attribute = talloc_asprintf(mem_ctx,
1976 if (!*next_attribute) {
1977 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
1978 ldap_memfree(range_attr);
1979 *more_strings = False;
1984 ldap_memfree(range_attr);
1990 * pull a single uint32 from a ADS result
1991 * @param ads connection to ads server
1992 * @param msg Results of search
1993 * @param field Attribute to retrieve
1994 * @param v Pointer to int to store result
1995 * @return boolean inidicating success
1997 BOOL ads_pull_uint32(ADS_STRUCT *ads,
1998 void *msg, const char *field, uint32 *v)
2002 values = ldap_get_values(ads->ld, msg, field);
2006 ldap_value_free(values);
2010 *v = atoi(values[0]);
2011 ldap_value_free(values);
2016 * pull a single objectGUID from an ADS result
2017 * @param ads connection to ADS server
2018 * @param msg results of search
2019 * @param guid 37-byte area to receive text guid
2020 * @return boolean indicating success
2022 BOOL ads_pull_guid(ADS_STRUCT *ads,
2023 void *msg, struct uuid *guid)
2026 UUID_FLAT flat_guid;
2028 values = ldap_get_values(ads->ld, msg, "objectGUID");
2033 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2034 smb_uuid_unpack(flat_guid, guid);
2035 ldap_value_free(values);
2038 ldap_value_free(values);
2045 * pull a single DOM_SID from a ADS result
2046 * @param ads connection to ads server
2047 * @param msg Results of search
2048 * @param field Attribute to retrieve
2049 * @param sid Pointer to sid to store result
2050 * @return boolean inidicating success
2052 BOOL ads_pull_sid(ADS_STRUCT *ads,
2053 void *msg, const char *field, DOM_SID *sid)
2055 struct berval **values;
2058 values = ldap_get_values_len(ads->ld, msg, field);
2064 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2066 ldap_value_free_len(values);
2071 * pull an array of DOM_SIDs from a ADS result
2072 * @param ads connection to ads server
2073 * @param mem_ctx TALLOC_CTX for allocating sid array
2074 * @param msg Results of search
2075 * @param field Attribute to retrieve
2076 * @param sids pointer to sid array to allocate
2077 * @return the count of SIDs pulled
2079 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2080 void *msg, const char *field, DOM_SID **sids)
2082 struct berval **values;
2086 values = ldap_get_values_len(ads->ld, msg, field);
2091 for (i=0; values[i]; i++)
2094 (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * i);
2096 ldap_value_free_len(values);
2101 for (i=0; values[i]; i++) {
2102 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2105 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2110 ldap_value_free_len(values);
2115 * pull a SEC_DESC from a ADS result
2116 * @param ads connection to ads server
2117 * @param mem_ctx TALLOC_CTX for allocating sid array
2118 * @param msg Results of search
2119 * @param field Attribute to retrieve
2120 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2121 * @return boolean inidicating success
2123 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2124 void *msg, const char *field, SEC_DESC **sd)
2126 struct berval **values;
2130 values = ldap_get_values_len(ads->ld, msg, field);
2132 if (!values) return False;
2135 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2136 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2137 prs_set_offset(&ps,0);
2139 ret = sec_io_desc("sd", sd, &ps, 1);
2142 ldap_value_free_len(values);
2147 * in order to support usernames longer than 21 characters we need to
2148 * use both the sAMAccountName and the userPrincipalName attributes
2149 * It seems that not all users have the userPrincipalName attribute set
2151 * @param ads connection to ads server
2152 * @param mem_ctx TALLOC_CTX for allocating sid array
2153 * @param msg Results of search
2154 * @return the username
2156 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
2160 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2161 if (ret && (p = strchr(ret, '@'))) {
2165 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2170 * find the update serial number - this is the core of the ldap cache
2171 * @param ads connection to ads server
2172 * @param ads connection to ADS server
2173 * @param usn Pointer to retrieved update serial number
2174 * @return status of search
2176 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2178 const char *attrs[] = {"highestCommittedUSN", NULL};
2182 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2183 if (!ADS_ERR_OK(status))
2186 if (ads_count_replies(ads, res) != 1) {
2187 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2190 ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
2191 ads_msgfree(ads, res);
2195 /* parse a ADS timestring - typical string is
2196 '20020917091222.0Z0' which means 09:12.22 17th September
2198 static time_t ads_parse_time(const char *str)
2204 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2205 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2206 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2217 * Find the servers name and realm - this can be done before authentication
2218 * The ldapServiceName field on w2k looks like this:
2219 * vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
2220 * @param ads connection to ads server
2221 * @return status of search
2223 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
2225 const char *attrs[] = {"ldapServiceName", "currentTime", NULL};
2233 if (!(ctx = talloc_init("ads_server_info"))) {
2234 return ADS_ERROR(LDAP_NO_MEMORY);
2237 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2238 if (!ADS_ERR_OK(status)) {
2239 talloc_destroy(ctx);
2243 value = ads_pull_string(ads, ctx, res, "ldapServiceName");
2245 ads_msgfree(ads, res);
2246 talloc_destroy(ctx);
2247 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2250 timestr = ads_pull_string(ads, ctx, res, "currentTime");
2252 ads_msgfree(ads, res);
2253 talloc_destroy(ctx);
2254 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2257 ads_msgfree(ads, res);
2259 p = strchr(value, ':');
2261 talloc_destroy(ctx);
2262 DEBUG(1, ("ads_server_info: returned ldap server name did not contain a ':' "
2263 "so was deemed invalid\n"));
2264 return ADS_ERROR(LDAP_DECODING_ERROR);
2267 SAFE_FREE(ads->config.ldap_server_name);
2269 ads->config.ldap_server_name = strdup(p+1);
2270 p = strchr(ads->config.ldap_server_name, '$');
2271 if (!p || p[1] != '@') {
2272 talloc_destroy(ctx);
2273 DEBUG(1, ("ads_server_info: returned ldap server name (%s) does not contain '$@'"
2274 " so was deemed invalid\n", ads->config.ldap_server_name));
2275 SAFE_FREE(ads->config.ldap_server_name);
2276 return ADS_ERROR(LDAP_DECODING_ERROR);
2281 SAFE_FREE(ads->config.realm);
2282 SAFE_FREE(ads->config.bind_path);
2284 ads->config.realm = strdup(p+2);
2285 ads->config.bind_path = ads_build_dn(ads->config.realm);
2287 DEBUG(3,("got ldap server name %s@%s, using bind path: %s\n",
2288 ads->config.ldap_server_name, ads->config.realm,
2289 ads->config.bind_path));
2291 ads->config.current_time = ads_parse_time(timestr);
2293 if (ads->config.current_time != 0) {
2294 ads->auth.time_offset = ads->config.current_time - time(NULL);
2295 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2298 talloc_destroy(ctx);
2304 * find the domain sid for our domain
2305 * @param ads connection to ads server
2306 * @param sid Pointer to domain sid
2307 * @return status of search
2309 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2311 const char *attrs[] = {"objectSid", NULL};
2315 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2317 if (!ADS_ERR_OK(rc)) return rc;
2318 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2319 ads_msgfree(ads, res);
2320 return ADS_ERROR_SYSTEM(ENOENT);
2322 ads_msgfree(ads, res);
2327 /* this is rather complex - we need to find the allternate (netbios) name
2328 for the domain, but there isn't a simple query to do this. Instead
2329 we look for the principle names on the DCs account and find one that has
2330 the right form, then extract the netbios name of the domain from that
2332 NOTE! better method is this:
2334 bin/net -Uadministrator%XXXXX ads search '(&(objectclass=crossref)(dnsroot=VNET3.HOME.SAMBA.ORG))' nETBIOSName
2336 but you need to force the bind path to match the configurationNamingContext from the rootDSE
2339 ADS_STATUS ads_workgroup_name(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **workgroup)
2348 const char *attrs[] = {"servicePrincipalName", NULL};
2351 (*workgroup) = NULL;
2353 asprintf(&expr, "(&(objectclass=computer)(dnshostname=%s.%s))",
2354 ads->config.ldap_server_name, ads->config.realm);
2355 rc = ads_search(ads, &res, expr, attrs);
2358 if (!ADS_ERR_OK(rc)) {
2362 principles = ads_pull_strings(ads, mem_ctx, res,
2363 "servicePrincipalName", &num_principals);
2365 ads_msgfree(ads, res);
2368 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2371 asprintf(&prefix, "HOST/%s.%s/",
2372 ads->config.ldap_server_name,
2375 prefix_length = strlen(prefix);
2377 for (i=0;principles[i]; i++) {
2378 if (strnequal(principles[i], prefix, prefix_length) &&
2379 !strequal(ads->config.realm, principles[i]+prefix_length) &&
2380 !strchr(principles[i]+prefix_length, '.')) {
2381 /* found an alternate (short) name for the domain. */
2382 DEBUG(3,("Found alternate name '%s' for realm '%s'\n",
2383 principles[i]+prefix_length,
2384 ads->config.realm));
2385 (*workgroup) = talloc_strdup(mem_ctx, principles[i]+prefix_length);
2392 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);