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
40 static SIG_ATOMIC_T gotalarm;
42 /***************************************************************
43 Signal function to tell us we timed out.
44 ****************************************************************/
46 static void gotalarm_sig(void)
51 LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
57 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
59 /* End setup timeout. */
61 ldp = ldap_open(server, port);
63 /* Teardown timeout. */
64 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
70 static int ldap_search_with_timeout(LDAP *ld,
71 LDAP_CONST char *base,
73 LDAP_CONST char *filter,
78 struct timeval *timeout,
86 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
87 alarm(lp_ldap_timeout());
88 /* End setup timeout. */
90 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
91 attrsonly, sctrls, cctrls, timeout,
94 /* Teardown timeout. */
95 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
99 return LDAP_TIMELIMIT_EXCEEDED;
105 try a connection to a given ldap server, returning True and setting the servers IP
106 in the ads struct if successful
108 TODO : add a negative connection cache in here leveraged off of the one
109 found in the rpc code. --jerry
111 static BOOL ads_try_connect(ADS_STRUCT *ads, const char *server, unsigned port)
115 if (!server || !*server) {
119 DEBUG(5,("ads_try_connect: trying ldap server '%s' port %u\n", server, port));
121 /* this copes with inet_ntoa brokenness */
122 srv = strdup(server);
124 ads->ld = ldap_open_with_timeout(srv, port, lp_ldap_timeout());
129 ads->ldap_port = port;
130 ads->ldap_ip = *interpret_addr2(srv);
137 try a connection to a given ldap server, based on URL, returning True if successful
139 static BOOL ads_try_connect_uri(ADS_STRUCT *ads)
141 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
142 DEBUG(5,("ads_try_connect: trying ldap server at URI '%s'\n",
143 ads->server.ldap_uri));
146 if (ldap_initialize((LDAP**)&(ads->ld), ads->server.ldap_uri) == LDAP_SUCCESS) {
149 DEBUG(0, ("ldap_initialize: %s\n", strerror(errno)));
153 DEBUG(1, ("no URL support in LDAP libs!\n"));
159 /**********************************************************************
160 Try to find an AD dc using our internal name resolution routines
161 Try the realm first and then then workgroup name if netbios is not
163 **********************************************************************/
165 static BOOL ads_find_dc(ADS_STRUCT *ads)
169 struct ip_service *ip_list;
171 BOOL got_realm = False;
172 BOOL use_own_domain = False;
174 /* if the realm and workgroup are both empty, assume they are ours */
177 c_realm = ads->server.realm;
179 if ( !c_realm || !*c_realm ) {
180 /* special case where no realm and no workgroup means our own */
181 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
182 use_own_domain = True;
183 c_realm = lp_realm();
187 if (c_realm && *c_realm)
191 /* we need to try once with the realm name and fallback to the
192 netbios domain name if we fail (if netbios has not been disabled */
194 if ( !got_realm && !lp_disable_netbios() ) {
195 c_realm = ads->server.workgroup;
196 if (!c_realm || !*c_realm) {
197 if ( use_own_domain )
198 c_realm = lp_workgroup();
201 if ( !c_realm || !*c_realm ) {
202 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
207 pstrcpy( realm, c_realm );
209 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
210 (got_realm ? "realm" : "domain"), realm));
212 if ( !get_sorted_dc_list(realm, &ip_list, &count, got_realm) ) {
213 /* fall back to netbios if we can */
214 if ( got_realm && !lp_disable_netbios() ) {
222 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
223 for ( i=0; i<count; i++ ) {
224 /* since this is an ads conection request, default to LDAP_PORT is not set */
225 int port = (ip_list[i].port!=PORT_NONE) ? ip_list[i].port : LDAP_PORT;
228 fstrcpy( server, inet_ntoa(ip_list[i].ip) );
230 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
233 if ( ads_try_connect(ads, server, port) ) {
238 /* keep track of failures */
239 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
249 * Connect to the LDAP server
250 * @param ads Pointer to an existing ADS_STRUCT
251 * @return status of connection
253 ADS_STATUS ads_connect(ADS_STRUCT *ads)
255 int version = LDAP_VERSION3;
258 ads->last_attempt = time(NULL);
261 /* try with a URL based server */
263 if (ads->server.ldap_uri &&
264 ads_try_connect_uri(ads)) {
268 /* try with a user specified server */
269 if (ads->server.ldap_server &&
270 ads_try_connect(ads, ads->server.ldap_server, LDAP_PORT)) {
274 if (ads_find_dc(ads)) {
278 return ADS_ERROR_SYSTEM(errno?errno:ENOENT);
281 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
283 status = ads_server_info(ads);
284 if (!ADS_ERR_OK(status)) {
285 DEBUG(1,("Failed to get ldap server info\n"));
289 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
291 if (!ads->auth.user_name) {
292 /* have to use the userPrincipalName value here and
293 not servicePrincipalName; found by Guenther Deschner @ Sernet */
295 asprintf(&ads->auth.user_name, "host/%s", global_myname() );
298 if (!ads->auth.realm) {
299 ads->auth.realm = strdup(ads->config.realm);
302 if (!ads->auth.kdc_server) {
303 ads->auth.kdc_server = strdup(inet_ntoa(ads->ldap_ip));
307 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
308 to MIT kerberos to work (tridge) */
311 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
312 setenv(env, ads->auth.kdc_server, 1);
317 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
321 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
322 return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
325 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
326 return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
329 return ads_sasl_bind(ads);
333 Duplicate a struct berval into talloc'ed memory
335 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
337 struct berval *value;
339 if (!in_val) return NULL;
341 value = talloc_zero(ctx, sizeof(struct berval));
344 if (in_val->bv_len == 0) return value;
346 value->bv_len = in_val->bv_len;
347 value->bv_val = talloc_memdup(ctx, in_val->bv_val, in_val->bv_len);
352 Make a values list out of an array of (struct berval *)
354 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
355 const struct berval **in_vals)
357 struct berval **values;
360 if (!in_vals) return NULL;
361 for (i=0; in_vals[i]; i++); /* count values */
362 values = (struct berval **) talloc_zero(ctx,
363 (i+1)*sizeof(struct berval *));
364 if (!values) return NULL;
366 for (i=0; in_vals[i]; i++) {
367 values[i] = dup_berval(ctx, in_vals[i]);
373 UTF8-encode a values list out of an array of (char *)
375 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
380 if (!in_vals) return NULL;
381 for (i=0; in_vals[i]; i++); /* count values */
382 values = (char ** ) talloc_zero(ctx, (i+1)*sizeof(char *));
383 if (!values) return NULL;
385 for (i=0; in_vals[i]; i++) {
386 push_utf8_talloc(ctx, &values[i], in_vals[i]);
392 Pull a (char *) array out of a UTF8-encoded values list
394 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
399 if (!in_vals) return NULL;
400 for (i=0; in_vals[i]; i++); /* count values */
401 values = (char **) talloc_zero(ctx, (i+1)*sizeof(char *));
402 if (!values) return NULL;
404 for (i=0; in_vals[i]; i++) {
405 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
411 * Do a search with paged results. cookie must be null on the first
412 * call, and then returned on each subsequent call. It will be null
413 * again when the entire search is complete
414 * @param ads connection to ads server
415 * @param bind_path Base dn for the search
416 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
417 * @param expr Search expression - specified in local charset
418 * @param attrs Attributes to retrieve - specified in utf8 or ascii
419 * @param res ** which will contain results - free res* with ads_msgfree()
420 * @param count Number of entries retrieved on this page
421 * @param cookie The paged results cookie to be returned on subsequent calls
422 * @return status of search
424 ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
425 int scope, const char *expr,
426 const char **attrs, void **res,
427 int *count, void **cookie)
430 char *utf8_expr, *utf8_path, **search_attrs;
431 LDAPControl PagedResults, NoReferrals, *controls[3], **rcontrols;
432 BerElement *cookie_be = NULL;
433 struct berval *cookie_bv= NULL;
438 if (!(ctx = talloc_init("ads_do_paged_search")))
439 return ADS_ERROR(LDAP_NO_MEMORY);
441 /* 0 means the conversion worked but the result was empty
442 so we only fail if it's -1. In any case, it always
443 at least nulls out the dest */
444 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
445 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
450 if (!attrs || !(*attrs))
453 /* This would be the utf8-encoded version...*/
454 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
455 if (!(str_list_copy(&search_attrs, attrs))) {
462 /* Paged results only available on ldap v3 or later */
463 ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
464 if (version < LDAP_VERSION3) {
465 rc = LDAP_NOT_SUPPORTED;
469 cookie_be = ber_alloc_t(LBER_USE_DER);
470 if (cookie && *cookie) {
471 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
472 ber_bvfree(*cookie); /* don't need it from last time */
475 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
477 ber_flatten(cookie_be, &cookie_bv);
478 PagedResults.ldctl_oid = ADS_PAGE_CTL_OID;
479 PagedResults.ldctl_iscritical = (char) 1;
480 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
481 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
483 NoReferrals.ldctl_oid = ADS_NO_REFERRALS_OID;
484 NoReferrals.ldctl_iscritical = (char) 0;
485 NoReferrals.ldctl_value.bv_len = 0;
486 NoReferrals.ldctl_value.bv_val = "";
489 controls[0] = &NoReferrals;
490 controls[1] = &PagedResults;
493 /* we need to disable referrals as the openldap libs don't
494 handle them and paged results at the same time. Using them
495 together results in the result record containing the server
496 page control being removed from the result list (tridge/jmcd)
498 leaving this in despite the control that says don't generate
499 referrals, in case the server doesn't support it (jmcd)
501 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
503 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
504 search_attrs, 0, controls,
505 NULL, NULL, LDAP_NO_LIMIT,
506 (LDAPMessage **)res);
508 ber_free(cookie_be, 1);
509 ber_bvfree(cookie_bv);
512 DEBUG(3,("ldap_search_with_timeout(%s) -> %s\n", expr,
513 ldap_err2string(rc)));
517 rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
518 NULL, &rcontrols, 0);
524 for (i=0; rcontrols[i]; i++) {
525 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
526 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
527 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
529 /* the berval is the cookie, but must be freed when
531 if (cookie_bv->bv_len) /* still more to do */
532 *cookie=ber_bvdup(cookie_bv);
535 ber_bvfree(cookie_bv);
536 ber_free(cookie_be, 1);
540 ldap_controls_free(rcontrols);
544 /* if/when we decide to utf8-encode attrs, take out this next line */
545 str_list_free(&search_attrs);
547 return ADS_ERROR(rc);
552 * Get all results for a search. This uses ads_do_paged_search() to return
553 * all entries in a large search.
554 * @param ads connection to ads server
555 * @param bind_path Base dn for the search
556 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
557 * @param expr Search expression
558 * @param attrs Attributes to retrieve
559 * @param res ** which will contain results - free res* with ads_msgfree()
560 * @return status of search
562 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
563 int scope, const char *expr,
564 const char **attrs, void **res)
571 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, res,
574 if (!ADS_ERR_OK(status)) return status;
579 LDAPMessage *msg, *next;
581 status2 = ads_do_paged_search(ads, bind_path, scope, expr,
582 attrs, &res2, &count, &cookie);
584 if (!ADS_ERR_OK(status2)) break;
586 /* this relies on the way that ldap_add_result_entry() works internally. I hope
587 that this works on all ldap libs, but I have only tested with openldap */
588 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
589 next = ads_next_entry(ads, msg);
590 ldap_add_result_entry((LDAPMessage **)res, msg);
592 /* note that we do not free res2, as the memory is now
593 part of the main returned list */
600 * Run a function on all results for a search. Uses ads_do_paged_search() and
601 * runs the function as each page is returned, using ads_process_results()
602 * @param ads connection to ads server
603 * @param bind_path Base dn for the search
604 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
605 * @param expr Search expression - specified in local charset
606 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
607 * @param fn Function which takes attr name, values list, and data_area
608 * @param data_area Pointer which is passed to function on each call
609 * @return status of search
611 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
612 int scope, const char *expr, const char **attrs,
613 BOOL(*fn)(char *, void **, void *),
621 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
624 if (!ADS_ERR_OK(status)) return status;
626 ads_process_results(ads, res, fn, data_area);
627 ads_msgfree(ads, res);
630 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
631 &res, &count, &cookie);
633 if (!ADS_ERR_OK(status)) break;
635 ads_process_results(ads, res, fn, data_area);
636 ads_msgfree(ads, res);
643 * Do a search with a timeout.
644 * @param ads connection to ads server
645 * @param bind_path Base dn for the search
646 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
647 * @param expr Search expression
648 * @param attrs Attributes to retrieve
649 * @param res ** which will contain results - free res* with ads_msgfree()
650 * @return status of search
652 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
654 const char **attrs, void **res)
656 struct timeval timeout;
658 char *utf8_expr, *utf8_path, **search_attrs = NULL;
662 if (!(ctx = talloc_init("ads_do_search"))) {
663 DEBUG(1,("ads_do_search: talloc_init() failed!"));
664 return ADS_ERROR(LDAP_NO_MEMORY);
667 /* 0 means the conversion worked but the result was empty
668 so we only fail if it's negative. In any case, it always
669 at least nulls out the dest */
670 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
671 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
672 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
677 if (!attrs || !(*attrs))
680 /* This would be the utf8-encoded version...*/
681 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
682 if (!(str_list_copy(&search_attrs, attrs)))
684 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
690 timeout.tv_sec = ADS_SEARCH_TIMEOUT;
693 /* see the note in ads_do_paged_search - we *must* disable referrals */
694 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
696 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
697 search_attrs, 0, NULL, NULL,
698 &timeout, LDAP_NO_LIMIT,
699 (LDAPMessage **)res);
701 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
702 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
708 /* if/when we decide to utf8-encode attrs, take out this next line */
709 str_list_free(&search_attrs);
710 return ADS_ERROR(rc);
713 * Do a general ADS search
714 * @param ads connection to ads server
715 * @param res ** which will contain results - free res* with ads_msgfree()
716 * @param expr Search expression
717 * @param attrs Attributes to retrieve
718 * @return status of search
720 ADS_STATUS ads_search(ADS_STRUCT *ads, void **res,
724 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
729 * Do a search on a specific DistinguishedName
730 * @param ads connection to ads server
731 * @param res ** which will contain results - free res* with ads_msgfree()
732 * @param dn DistinguishName to search
733 * @param attrs Attributes to retrieve
734 * @return status of search
736 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, void **res,
740 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res);
744 * Free up memory from a ads_search
745 * @param ads connection to ads server
746 * @param msg Search results to free
748 void ads_msgfree(ADS_STRUCT *ads, void *msg)
755 * Free up memory from various ads requests
756 * @param ads connection to ads server
757 * @param mem Area to free
759 void ads_memfree(ADS_STRUCT *ads, void *mem)
765 * Get a dn from search results
766 * @param ads connection to ads server
767 * @param msg Search result
770 char *ads_get_dn(ADS_STRUCT *ads, void *msg)
772 char *utf8_dn, *unix_dn;
774 utf8_dn = ldap_get_dn(ads->ld, msg);
777 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
781 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
782 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
786 ldap_memfree(utf8_dn);
791 * Find a machine account given a hostname
792 * @param ads connection to ads server
793 * @param res ** which will contain results - free res* with ads_msgfree()
794 * @param host Hostname to search for
795 * @return status of search
797 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *machine)
801 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
805 /* the easiest way to find a machine account anywhere in the tree
806 is to look for hostname$ */
807 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
808 DEBUG(1, ("asprintf failed!\n"));
809 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
812 status = ads_search(ads, res, expr, attrs);
818 * Initialize a list of mods to be used in a modify request
819 * @param ctx An initialized TALLOC_CTX
820 * @return allocated ADS_MODLIST
822 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
824 #define ADS_MODLIST_ALLOC_SIZE 10
827 if ((mods = (LDAPMod **) talloc_zero(ctx, sizeof(LDAPMod *) *
828 (ADS_MODLIST_ALLOC_SIZE + 1))))
829 /* -1 is safety to make sure we don't go over the end.
830 need to reset it to NULL before doing ldap modify */
831 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
838 add an attribute to the list, with values list already constructed
840 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
841 int mod_op, const char *name,
845 LDAPMod **modlist = (LDAPMod **) *mods;
846 struct berval **ber_values = NULL;
847 char **char_values = NULL;
850 mod_op = LDAP_MOD_DELETE;
852 if (mod_op & LDAP_MOD_BVALUES)
853 ber_values = ads_dup_values(ctx,
854 (const struct berval **)invals);
856 char_values = ads_push_strvals(ctx,
857 (const char **) invals);
860 /* find the first empty slot */
861 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
863 if (modlist[curmod] == (LDAPMod *) -1) {
864 if (!(modlist = talloc_realloc(ctx, modlist,
865 (curmod+ADS_MODLIST_ALLOC_SIZE+1)*sizeof(LDAPMod *))))
866 return ADS_ERROR(LDAP_NO_MEMORY);
867 memset(&modlist[curmod], 0,
868 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
869 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
873 if (!(modlist[curmod] = talloc_zero(ctx, sizeof(LDAPMod))))
874 return ADS_ERROR(LDAP_NO_MEMORY);
875 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
876 if (mod_op & LDAP_MOD_BVALUES) {
877 modlist[curmod]->mod_bvalues = ber_values;
878 } else if (mod_op & LDAP_MOD_DELETE) {
879 modlist[curmod]->mod_values = NULL;
881 modlist[curmod]->mod_values = char_values;
884 modlist[curmod]->mod_op = mod_op;
885 return ADS_ERROR(LDAP_SUCCESS);
889 * Add a single string value to a mod list
890 * @param ctx An initialized TALLOC_CTX
891 * @param mods An initialized ADS_MODLIST
892 * @param name The attribute name to add
893 * @param val The value to add - NULL means DELETE
894 * @return ADS STATUS indicating success of add
896 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
897 const char *name, const char *val)
899 const char *values[2];
905 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
906 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name,
907 (const void **) values);
911 * Add an array of string values to a mod list
912 * @param ctx An initialized TALLOC_CTX
913 * @param mods An initialized ADS_MODLIST
914 * @param name The attribute name to add
915 * @param vals The array of string values to add - NULL means DELETE
916 * @return ADS STATUS indicating success of add
918 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
919 const char *name, const char **vals)
922 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
923 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
924 name, (const void **) vals);
928 * Add a single ber-encoded value to a mod list
929 * @param ctx An initialized TALLOC_CTX
930 * @param mods An initialized ADS_MODLIST
931 * @param name The attribute name to add
932 * @param val The value to add - NULL means DELETE
933 * @return ADS STATUS indicating success of add
935 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
936 const char *name, const struct berval *val)
938 const struct berval *values[2];
943 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
944 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
945 name, (const void **) values);
949 * Perform an ldap modify
950 * @param ads connection to ads server
951 * @param mod_dn DistinguishedName to modify
952 * @param mods list of modifications to perform
953 * @return status of modify
955 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
958 char *utf8_dn = NULL;
960 this control is needed to modify that contains a currently
961 non-existent attribute (but allowable for the object) to run
963 LDAPControl PermitModify = {
964 ADS_PERMIT_MODIFY_OID,
967 LDAPControl *controls[2];
969 controls[0] = &PermitModify;
972 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
973 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
976 /* find the end of the list, marked by NULL or -1 */
977 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
978 /* make sure the end of the list is NULL */
980 ret = ldap_modify_ext_s(ads->ld, utf8_dn,
981 (LDAPMod **) mods, controls, NULL);
983 return ADS_ERROR(ret);
987 * Perform an ldap add
988 * @param ads connection to ads server
989 * @param new_dn DistinguishedName to add
990 * @param mods list of attributes and values for DN
991 * @return status of add
993 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
996 char *utf8_dn = NULL;
998 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
999 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1000 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1003 /* find the end of the list, marked by NULL or -1 */
1004 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1005 /* make sure the end of the list is NULL */
1008 ret = ldap_add_s(ads->ld, utf8_dn, mods);
1010 return ADS_ERROR(ret);
1014 * Delete a DistinguishedName
1015 * @param ads connection to ads server
1016 * @param new_dn DistinguishedName to delete
1017 * @return status of delete
1019 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1022 char *utf8_dn = NULL;
1023 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1024 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1025 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1028 ret = ldap_delete_s(ads->ld, utf8_dn);
1029 return ADS_ERROR(ret);
1033 * Build an org unit string
1034 * if org unit is Computers or blank then assume a container, otherwise
1035 * assume a \ separated list of organisational units
1036 * @param ads connection to ads server
1037 * @param org_unit Organizational unit
1038 * @return org unit string - caller must free
1040 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1044 if (!org_unit || !*org_unit) {
1046 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1048 /* samba4 might not yet respond to a wellknownobject-query */
1049 return ret ? ret : strdup("cn=Computers");
1052 if (strequal(org_unit, "Computers")) {
1053 return strdup("cn=Computers");
1056 return ads_build_path(org_unit, "\\/", "ou=", 1);
1060 * Get a org unit string for a well-known GUID
1061 * @param ads connection to ads server
1062 * @param wknguid Well known GUID
1063 * @return org unit string - caller must free
1065 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1069 char *base, *wkn_dn, *ret, **wkn_dn_exp, **bind_dn_exp;
1070 const char *attrs[] = {"distinguishedName", NULL};
1071 int new_ln, wkn_ln, bind_ln, i;
1073 if (wknguid == NULL) {
1077 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1078 DEBUG(1, ("asprintf failed!\n"));
1082 status = ads_search_dn(ads, &res, base, attrs);
1083 if (!ADS_ERR_OK(status)) {
1084 DEBUG(1,("Failed while searching for: %s\n", base));
1089 if (ads_count_replies(ads, res) != 1) {
1093 /* substitute the bind-path from the well-known-guid-search result */
1094 wkn_dn = ads_get_dn(ads, res);
1095 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1096 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1098 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1100 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1103 new_ln = wkn_ln - bind_ln;
1105 ret = wkn_dn_exp[0];
1107 for (i=1; i < new_ln; i++) {
1109 asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]);
1118 * Adds (appends) an item to an attribute array, rather then
1119 * replacing the whole list
1120 * @param ctx An initialized TALLOC_CTX
1121 * @param mods An initialized ADS_MODLIST
1122 * @param name name of the ldap attribute to append to
1123 * @param vals an array of values to add
1124 * @return status of addition
1127 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1128 const char *name, const char **vals)
1130 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name, (const void **) vals);
1134 * Determines the computer account's current KVNO via an LDAP lookup
1135 * @param ads An initialized ADS_STRUCT
1136 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1137 * @return the kvno for the computer account, or -1 in case of a failure.
1140 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1142 LDAPMessage *res = NULL;
1143 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1145 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1146 char *dn_string = NULL;
1147 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1149 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1150 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1153 ret = ads_search(ads, (void**) &res, filter, attrs);
1155 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1156 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1157 ads_msgfree(ads, res);
1161 dn_string = ads_get_dn(ads, res);
1163 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1164 ads_msgfree(ads, res);
1167 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1168 ads_memfree(ads, dn_string);
1170 /* ---------------------------------------------------------
1171 * 0 is returned as a default KVNO from this point on...
1172 * This is done because Windows 2000 does not support key
1173 * version numbers. Chances are that a failure in the next
1174 * step is simply due to Windows 2000 being used for a
1175 * domain controller. */
1178 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1179 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1180 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1181 ads_msgfree(ads, res);
1186 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1187 ads_msgfree(ads, res);
1192 * This clears out all registered spn's for a given hostname
1193 * @param ads An initilaized ADS_STRUCT
1194 * @param machine_name the NetBIOS name of the computer.
1195 * @return 0 upon success, non-zero otherwise.
1198 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1201 LDAPMessage *res = NULL;
1203 const char *servicePrincipalName[1] = {NULL};
1204 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1205 char *dn_string = NULL;
1207 ret = ads_find_machine_acct(ads, (void **)&res, machine_name);
1208 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1209 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1210 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1211 ads_msgfree(ads, res);
1212 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1215 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1216 ctx = talloc_init("ads_clear_service_principal_names");
1218 ads_msgfree(ads, res);
1219 return ADS_ERROR(LDAP_NO_MEMORY);
1222 if (!(mods = ads_init_mods(ctx))) {
1223 talloc_destroy(ctx);
1224 ads_msgfree(ads, res);
1225 return ADS_ERROR(LDAP_NO_MEMORY);
1227 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1228 if (!ADS_ERR_OK(ret)) {
1229 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1230 ads_msgfree(ads, res);
1231 talloc_destroy(ctx);
1234 dn_string = ads_get_dn(ads, res);
1236 talloc_destroy(ctx);
1237 ads_msgfree(ads, res);
1238 return ADS_ERROR(LDAP_NO_MEMORY);
1240 ret = ads_gen_mod(ads, dn_string, mods);
1241 ads_memfree(ads,dn_string);
1242 if (!ADS_ERR_OK(ret)) {
1243 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1245 ads_msgfree(ads, res);
1246 talloc_destroy(ctx);
1250 ads_msgfree(ads, res);
1251 talloc_destroy(ctx);
1256 * This adds a service principal name to an existing computer account
1257 * (found by hostname) in AD.
1258 * @param ads An initialized ADS_STRUCT
1259 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1260 * @param spn A string of the service principal to add, i.e. 'host'
1261 * @return 0 upon sucess, or non-zero if a failure occurs
1264 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name, const char *spn)
1268 LDAPMessage *res = NULL;
1269 char *host_spn, *host_upn, *psp1, *psp2, *psp3;
1272 char *dn_string = NULL;
1273 const char *servicePrincipalName[4] = {NULL, NULL, NULL, NULL};
1275 ret = ads_find_machine_acct(ads, (void **)&res, machine_name);
1276 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1277 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1279 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1280 spn, machine_name, ads->config.realm));
1281 ads_msgfree(ads, res);
1282 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1285 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1286 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1287 ads_msgfree(ads, res);
1288 return ADS_ERROR(LDAP_NO_MEMORY);
1291 name_to_fqdn(my_fqdn, machine_name);
1292 strlower_m(my_fqdn);
1294 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", my_fqdn))) {
1295 talloc_destroy(ctx);
1296 ads_msgfree(ads, res);
1297 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1299 if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm))) {
1300 talloc_destroy(ctx);
1301 ads_msgfree(ads, res);
1302 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1305 /* Add the extra principal */
1306 psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name);
1308 strlower_m(&psp1[strlen(spn)]);
1309 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp1, machine_name));
1310 servicePrincipalName[0] = psp1;
1311 psp2 = talloc_asprintf(ctx, "%s/%s.%s", spn, machine_name, ads->config.realm);
1313 strlower_m(&psp2[strlen(spn)]);
1314 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp2, machine_name));
1315 servicePrincipalName[1] = psp2;
1317 /* Add another principal in case the realm != the DNS domain, so that
1318 * the KDC doesn't send "server principal unknown" errors to clients
1319 * which use the DNS name in determining service principal names. */
1320 psp3 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn);
1322 strlower_m(&psp3[strlen(spn)]);
1323 if (strcmp(psp2, psp3) != 0) {
1324 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp3, machine_name));
1325 servicePrincipalName[2] = psp3;
1328 if (!(mods = ads_init_mods(ctx))) {
1329 talloc_destroy(ctx);
1330 ads_msgfree(ads, res);
1331 return ADS_ERROR(LDAP_NO_MEMORY);
1333 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1334 if (!ADS_ERR_OK(ret)) {
1335 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1336 talloc_destroy(ctx);
1337 ads_msgfree(ads, res);
1340 dn_string = ads_get_dn(ads, res);
1342 talloc_destroy(ctx);
1343 ads_msgfree(ads, res);
1344 return ADS_ERROR(LDAP_NO_MEMORY);
1346 ret = ads_gen_mod(ads, dn_string, mods);
1347 ads_memfree(ads,dn_string);
1348 if (!ADS_ERR_OK(ret)) {
1349 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1350 talloc_destroy(ctx);
1351 ads_msgfree(ads, res);
1355 talloc_destroy(ctx);
1356 ads_msgfree(ads, res);
1361 * adds a machine account to the ADS server
1362 * @param ads An intialized ADS_STRUCT
1363 * @param machine_name - the NetBIOS machine name of this account.
1364 * @param account_type A number indicating the type of account to create
1365 * @param org_unit The LDAP path in which to place this account
1366 * @return 0 upon success, or non-zero otherwise
1369 static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1370 uint32 account_type,
1371 const char *org_unit)
1373 ADS_STATUS ret, status;
1374 char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
1377 const char *objectClass[] = {"top", "person", "organizationalPerson",
1378 "user", "computer", NULL};
1379 const char *servicePrincipalName[7] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
1380 char *psp, *psp2, *psp3, *psp4;
1381 unsigned acct_control;
1384 LDAPMessage *res = NULL;
1387 if (!(ctx = talloc_init("ads_add_machine_acct")))
1388 return ADS_ERROR(LDAP_NO_MEMORY);
1390 ret = ADS_ERROR(LDAP_NO_MEMORY);
1392 name_to_fqdn(my_fqdn, machine_name);
1394 status = ads_find_machine_acct(ads, (void **)&res, machine_name);
1395 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1396 char *dn_string = ads_get_dn(ads, res);
1398 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
1401 new_dn = talloc_strdup(ctx, dn_string);
1402 ads_memfree(ads,dn_string);
1403 DEBUG(0, ("ads_add_machine_acct: Host account for %s already exists - modifying old account\n",
1407 char *ou_str = ads_ou_string(ads,org_unit);
1409 DEBUG(1, ("ads_add_machine_acct: ads_ou_string returned NULL (malloc failure?)\n"));
1412 new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", machine_name, ou_str,
1413 ads->config.bind_path);
1422 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", machine_name)))
1424 if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm)))
1426 servicePrincipalName[0] = talloc_asprintf(ctx, "HOST/%s", machine_name);
1427 psp = talloc_asprintf(ctx, "HOST/%s.%s",
1430 strlower_m(&psp[5]);
1431 servicePrincipalName[1] = psp;
1432 servicePrincipalName[2] = talloc_asprintf(ctx, "CIFS/%s", machine_name);
1433 psp2 = talloc_asprintf(ctx, "CIFS/%s.%s",
1436 strlower_m(&psp2[5]);
1437 servicePrincipalName[3] = psp2;
1439 /* Ensure servicePrincipalName[4] and [5] are unique. */
1440 strlower_m(my_fqdn);
1441 psp3 = talloc_asprintf(ctx, "CIFS/%s", my_fqdn);
1442 strlower_m(&psp3[5]);
1445 for (i = 0; i < next_spn; i++) {
1446 if (strequal(servicePrincipalName[i], psp3))
1449 if (i == next_spn) {
1450 servicePrincipalName[next_spn++] = psp3;
1453 psp4 = talloc_asprintf(ctx, "HOST/%s", my_fqdn);
1454 strlower_m(&psp4[5]);
1455 for (i = 0; i < next_spn; i++) {
1456 if (strequal(servicePrincipalName[i], psp3))
1459 if (i == next_spn) {
1460 servicePrincipalName[next_spn++] = psp4;
1463 if (!(samAccountName = talloc_asprintf(ctx, "%s$", machine_name))) {
1467 acct_control = account_type | UF_DONT_EXPIRE_PASSWD;
1468 #ifndef ENCTYPE_ARCFOUR_HMAC
1469 acct_control |= UF_USE_DES_KEY_ONLY;
1472 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1476 if (!(mods = ads_init_mods(ctx))) {
1481 ads_mod_str(ctx, &mods, "cn", machine_name);
1482 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1483 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1484 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1486 ads_mod_str(ctx, &mods, "dNSHostName", my_fqdn);
1487 ads_mod_str(ctx, &mods, "userPrincipalName", host_upn);
1488 ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1489 ads_mod_str(ctx, &mods, "operatingSystem", "Samba");
1490 ads_mod_str(ctx, &mods, "operatingSystemVersion", SAMBA_VERSION_STRING);
1493 ret = ads_gen_add(ads, new_dn, mods);
1495 ret = ads_gen_mod(ads, new_dn, mods);
1498 if (!ADS_ERR_OK(ret)) {
1502 /* Do not fail if we can't set security descriptor
1503 * it shouldn't be mandatory and probably we just
1504 * don't have enough rights to do it.
1507 status = ads_set_machine_sd(ads, machine_name, new_dn);
1509 if (!ADS_ERR_OK(status)) {
1510 DEBUG(0, ("Warning: ads_set_machine_sd: %s\n",
1511 ads_errstr(status)));
1515 ads_msgfree(ads, res);
1516 talloc_destroy(ctx);
1521 dump a binary result from ldap
1523 static void dump_binary(const char *field, struct berval **values)
1526 for (i=0; values[i]; i++) {
1527 printf("%s: ", field);
1528 for (j=0; j<values[i]->bv_len; j++) {
1529 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1535 static void dump_guid(const char *field, struct berval **values)
1539 for (i=0; values[i]; i++) {
1540 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1541 printf("%s: %s\n", field,
1542 smb_uuid_string_static(smb_uuid_unpack_static(guid)));
1547 dump a sid result from ldap
1549 static void dump_sid(const char *field, struct berval **values)
1552 for (i=0; values[i]; i++) {
1554 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1555 printf("%s: %s\n", field, sid_string_static(&sid));
1560 dump ntSecurityDescriptor
1562 static void dump_sd(const char *filed, struct berval **values)
1567 TALLOC_CTX *ctx = 0;
1569 if (!(ctx = talloc_init("sec_io_desc")))
1573 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1574 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1575 prs_set_offset(&ps,0);
1578 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1580 talloc_destroy(ctx);
1583 if (psd) ads_disp_sd(psd);
1586 talloc_destroy(ctx);
1590 dump a string result from ldap
1592 static void dump_string(const char *field, char **values)
1595 for (i=0; values[i]; i++) {
1596 printf("%s: %s\n", field, values[i]);
1601 dump a field from LDAP on stdout
1605 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1610 void (*handler)(const char *, struct berval **);
1612 {"objectGUID", False, dump_guid},
1613 {"nTSecurityDescriptor", False, dump_sd},
1614 {"dnsRecord", False, dump_binary},
1615 {"objectSid", False, dump_sid},
1616 {"tokenGroups", False, dump_sid},
1621 if (!field) { /* must be end of an entry */
1626 for (i=0; handlers[i].name; i++) {
1627 if (StrCaseCmp(handlers[i].name, field) == 0) {
1628 if (!values) /* first time, indicate string or not */
1629 return handlers[i].string;
1630 handlers[i].handler(field, (struct berval **) values);
1634 if (!handlers[i].name) {
1635 if (!values) /* first time, indicate string conversion */
1637 dump_string(field, (char **)values);
1643 * Dump a result from LDAP on stdout
1644 * used for debugging
1645 * @param ads connection to ads server
1646 * @param res Results to dump
1649 void ads_dump(ADS_STRUCT *ads, void *res)
1651 ads_process_results(ads, res, ads_dump_field, NULL);
1655 * Walk through results, calling a function for each entry found.
1656 * The function receives a field name, a berval * array of values,
1657 * and a data area passed through from the start. The function is
1658 * called once with null for field and values at the end of each
1660 * @param ads connection to ads server
1661 * @param res Results to process
1662 * @param fn Function for processing each result
1663 * @param data_area user-defined area to pass to function
1665 void ads_process_results(ADS_STRUCT *ads, void *res,
1666 BOOL(*fn)(char *, void **, void *),
1672 if (!(ctx = talloc_init("ads_process_results")))
1675 for (msg = ads_first_entry(ads, res); msg;
1676 msg = ads_next_entry(ads, msg)) {
1680 for (utf8_field=ldap_first_attribute(ads->ld,
1681 (LDAPMessage *)msg,&b);
1683 utf8_field=ldap_next_attribute(ads->ld,
1684 (LDAPMessage *)msg,b)) {
1685 struct berval **ber_vals;
1686 char **str_vals, **utf8_vals;
1690 pull_utf8_talloc(ctx, &field, utf8_field);
1691 string = fn(field, NULL, data_area);
1694 utf8_vals = ldap_get_values(ads->ld,
1695 (LDAPMessage *)msg, field);
1696 str_vals = ads_pull_strvals(ctx,
1697 (const char **) utf8_vals);
1698 fn(field, (void **) str_vals, data_area);
1699 ldap_value_free(utf8_vals);
1701 ber_vals = ldap_get_values_len(ads->ld,
1702 (LDAPMessage *)msg, field);
1703 fn(field, (void **) ber_vals, data_area);
1705 ldap_value_free_len(ber_vals);
1707 ldap_memfree(utf8_field);
1710 talloc_destroy_pool(ctx);
1711 fn(NULL, NULL, data_area); /* completed an entry */
1714 talloc_destroy(ctx);
1718 * count how many replies are in a LDAPMessage
1719 * @param ads connection to ads server
1720 * @param res Results to count
1721 * @return number of replies
1723 int ads_count_replies(ADS_STRUCT *ads, void *res)
1725 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1729 * Join a machine to a realm
1730 * Creates the machine account and sets the machine password
1731 * @param ads connection to ads server
1732 * @param machine name of host to add
1733 * @param org_unit Organizational unit to place machine in
1734 * @return status of join
1736 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
1737 uint32 account_type, const char *org_unit)
1740 LDAPMessage *res = NULL;
1743 /* machine name must be lowercase */
1744 machine = strdup(machine_name);
1745 strlower_m(machine);
1748 status = ads_find_machine_acct(ads, (void **)&res, machine);
1749 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1750 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
1751 status = ads_leave_realm(ads, machine);
1752 if (!ADS_ERR_OK(status)) {
1753 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
1754 machine, ads->config.realm));
1760 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
1761 if (!ADS_ERR_OK(status)) {
1762 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
1767 status = ads_find_machine_acct(ads, (void **)&res, machine);
1768 if (!ADS_ERR_OK(status)) {
1769 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
1775 ads_msgfree(ads, res);
1781 * Delete a machine from the realm
1782 * @param ads connection to ads server
1783 * @param hostname Machine to remove
1784 * @return status of delete
1786 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
1790 char *hostnameDN, *host;
1793 /* hostname must be lowercase */
1794 host = strdup(hostname);
1797 status = ads_find_machine_acct(ads, &res, host);
1798 if (!ADS_ERR_OK(status)) {
1799 DEBUG(0, ("Host account for %s does not exist.\n", host));
1803 msg = ads_first_entry(ads, res);
1805 return ADS_ERROR_SYSTEM(ENOENT);
1808 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
1809 rc = ldap_delete_s(ads->ld, hostnameDN);
1810 ads_memfree(ads, hostnameDN);
1811 if (rc != LDAP_SUCCESS) {
1812 return ADS_ERROR(rc);
1815 status = ads_find_machine_acct(ads, &res, host);
1816 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1817 DEBUG(0, ("Failed to remove host account.\n"));
1827 * add machine account to existing security descriptor
1828 * @param ads connection to ads server
1829 * @param hostname machine to add
1830 * @param dn DN of security descriptor
1833 ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
1835 const char *attrs[] = {"nTSecurityDescriptor", "objectSid", 0};
1838 struct berval bval = {0, NULL};
1840 char *escaped_hostname = escape_ldap_string_alloc(hostname);
1842 LDAPMessage *res = 0;
1843 LDAPMessage *msg = 0;
1844 ADS_MODLIST mods = 0;
1849 SEC_DESC *psd = NULL;
1850 TALLOC_CTX *ctx = NULL;
1852 /* Avoid segmentation fault in prs_mem_free if
1853 * we have to bail out before prs_init */
1854 ps_wire.is_dynamic = False;
1856 if (!ads) return ADS_ERROR(LDAP_SERVER_DOWN);
1858 ret = ADS_ERROR(LDAP_SUCCESS);
1860 if (!escaped_hostname) {
1861 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1864 if (asprintf(&expr, "(samAccountName=%s$)", escaped_hostname) == -1) {
1865 DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n"));
1866 SAFE_FREE(escaped_hostname);
1867 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1870 SAFE_FREE(escaped_hostname);
1872 ret = ads_search(ads, (void *) &res, expr, attrs);
1874 if (!ADS_ERR_OK(ret)) return ret;
1876 if ( !(msg = ads_first_entry(ads, res) )) {
1877 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1878 goto ads_set_sd_error;
1881 if (!ads_pull_sid(ads, msg, attrs[1], &sid)) {
1882 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1883 goto ads_set_sd_error;
1886 if (!(ctx = talloc_init("sec_io_desc"))) {
1887 ret = ADS_ERROR(LDAP_NO_MEMORY);
1888 goto ads_set_sd_error;
1891 if (!ads_pull_sd(ads, ctx, msg, attrs[0], &psd)) {
1892 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1893 goto ads_set_sd_error;
1896 status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size);
1898 if (!NT_STATUS_IS_OK(status)) {
1899 ret = ADS_ERROR_NT(status);
1900 goto ads_set_sd_error;
1903 if (!prs_init(&ps_wire, sd_size, ctx, MARSHALL)) {
1904 ret = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1907 if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1)) {
1908 ret = ADS_ERROR(LDAP_NO_MEMORY);
1909 goto ads_set_sd_error;
1913 file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size);
1915 if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY);
1917 bval.bv_len = prs_offset(&ps_wire);
1918 bval.bv_val = talloc(ctx, bval.bv_len);
1920 ret = ADS_ERROR(LDAP_NO_MEMORY);
1921 goto ads_set_sd_error;
1924 prs_set_offset(&ps_wire, 0);
1926 if (!prs_copy_data_out(bval.bv_val, &ps_wire, bval.bv_len)) {
1927 ret = ADS_ERROR(LDAP_NO_MEMORY);
1928 goto ads_set_sd_error;
1931 ret = ads_mod_ber(ctx, &mods, attrs[0], &bval);
1932 if (ADS_ERR_OK(ret)) {
1933 ret = ads_gen_mod(ads, dn, mods);
1937 ads_msgfree(ads, res);
1938 prs_mem_free(&ps_wire);
1939 talloc_destroy(ctx);
1944 * pull the first entry from a ADS result
1945 * @param ads connection to ads server
1946 * @param res Results of search
1947 * @return first entry from result
1949 void *ads_first_entry(ADS_STRUCT *ads, void *res)
1951 return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
1955 * pull the next entry from a ADS result
1956 * @param ads connection to ads server
1957 * @param res Results of search
1958 * @return next entry from result
1960 void *ads_next_entry(ADS_STRUCT *ads, void *res)
1962 return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
1966 * pull a single string from a ADS result
1967 * @param ads connection to ads server
1968 * @param mem_ctx TALLOC_CTX to use for allocating result string
1969 * @param msg Results of search
1970 * @param field Attribute to retrieve
1971 * @return Result string in talloc context
1973 char *ads_pull_string(ADS_STRUCT *ads,
1974 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1981 values = ldap_get_values(ads->ld, msg, field);
1986 rc = pull_utf8_talloc(mem_ctx, &ux_string,
1988 if (rc != (size_t)-1)
1992 ldap_value_free(values);
1997 * pull an array of strings from a ADS result
1998 * @param ads connection to ads server
1999 * @param mem_ctx TALLOC_CTX to use for allocating result string
2000 * @param msg Results of search
2001 * @param field Attribute to retrieve
2002 * @return Result strings in talloc context
2004 char **ads_pull_strings(ADS_STRUCT *ads,
2005 TALLOC_CTX *mem_ctx, void *msg, const char *field,
2012 values = ldap_get_values(ads->ld, msg, field);
2016 *num_values = ldap_count_values(values);
2018 ret = talloc(mem_ctx, sizeof(char *) * (*num_values+1));
2020 ldap_value_free(values);
2024 for (i=0;i<*num_values;i++) {
2025 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
2026 ldap_value_free(values);
2032 ldap_value_free(values);
2037 * pull an array of strings from a ADS result
2038 * (handle large multivalue attributes with range retrieval)
2039 * @param ads connection to ads server
2040 * @param mem_ctx TALLOC_CTX to use for allocating result string
2041 * @param msg Results of search
2042 * @param field Attribute to retrieve
2043 * @param current_strings strings returned by a previous call to this function
2044 * @param next_attribute The next query should ask for this attribute
2045 * @param num_values How many values did we get this time?
2046 * @param more_values Are there more values to get?
2047 * @return Result strings in talloc context
2049 char **ads_pull_strings_range(ADS_STRUCT *ads,
2050 TALLOC_CTX *mem_ctx,
2051 void *msg, const char *field,
2052 char **current_strings,
2053 const char **next_attribute,
2054 size_t *num_strings,
2058 char *expected_range_attrib, *range_attr;
2059 BerElement *ptr = NULL;
2062 size_t num_new_strings;
2063 unsigned long int range_start;
2064 unsigned long int range_end;
2066 /* we might have been given the whole lot anyway */
2067 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2068 *more_strings = False;
2072 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2074 /* look for Range result */
2075 for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr);
2077 attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
2078 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2079 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2087 /* nothing here - this field is just empty */
2088 *more_strings = False;
2092 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2093 &range_start, &range_end) == 2) {
2094 *more_strings = True;
2096 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2097 &range_start) == 1) {
2098 *more_strings = False;
2100 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2102 ldap_memfree(range_attr);
2103 *more_strings = False;
2108 if ((*num_strings) != range_start) {
2109 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2110 " - aborting range retreival\n",
2111 range_attr, *num_strings + 1, range_start));
2112 ldap_memfree(range_attr);
2113 *more_strings = False;
2117 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2119 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2120 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2121 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2122 range_attr, (unsigned long int)range_end - range_start + 1,
2123 (unsigned long int)num_new_strings));
2124 ldap_memfree(range_attr);
2125 *more_strings = False;
2129 strings = talloc_realloc(mem_ctx, current_strings,
2130 sizeof(*current_strings) *
2131 (*num_strings + num_new_strings));
2133 if (strings == NULL) {
2134 ldap_memfree(range_attr);
2135 *more_strings = False;
2139 memcpy(&strings[*num_strings], new_strings,
2140 sizeof(*new_strings) * num_new_strings);
2142 (*num_strings) += num_new_strings;
2144 if (*more_strings) {
2145 *next_attribute = talloc_asprintf(mem_ctx,
2150 if (!*next_attribute) {
2151 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2152 ldap_memfree(range_attr);
2153 *more_strings = False;
2158 ldap_memfree(range_attr);
2164 * pull a single uint32 from a ADS result
2165 * @param ads connection to ads server
2166 * @param msg Results of search
2167 * @param field Attribute to retrieve
2168 * @param v Pointer to int to store result
2169 * @return boolean inidicating success
2171 BOOL ads_pull_uint32(ADS_STRUCT *ads,
2172 void *msg, const char *field, uint32 *v)
2176 values = ldap_get_values(ads->ld, msg, field);
2180 ldap_value_free(values);
2184 *v = atoi(values[0]);
2185 ldap_value_free(values);
2190 * pull a single objectGUID from an ADS result
2191 * @param ads connection to ADS server
2192 * @param msg results of search
2193 * @param guid 37-byte area to receive text guid
2194 * @return boolean indicating success
2196 BOOL ads_pull_guid(ADS_STRUCT *ads,
2197 void *msg, struct uuid *guid)
2200 UUID_FLAT flat_guid;
2202 values = ldap_get_values(ads->ld, msg, "objectGUID");
2207 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2208 smb_uuid_unpack(flat_guid, guid);
2209 ldap_value_free(values);
2212 ldap_value_free(values);
2219 * pull a single DOM_SID from a ADS result
2220 * @param ads connection to ads server
2221 * @param msg Results of search
2222 * @param field Attribute to retrieve
2223 * @param sid Pointer to sid to store result
2224 * @return boolean inidicating success
2226 BOOL ads_pull_sid(ADS_STRUCT *ads,
2227 void *msg, const char *field, DOM_SID *sid)
2229 struct berval **values;
2232 values = ldap_get_values_len(ads->ld, msg, field);
2238 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2240 ldap_value_free_len(values);
2245 * pull an array of DOM_SIDs from a ADS result
2246 * @param ads connection to ads server
2247 * @param mem_ctx TALLOC_CTX for allocating sid array
2248 * @param msg Results of search
2249 * @param field Attribute to retrieve
2250 * @param sids pointer to sid array to allocate
2251 * @return the count of SIDs pulled
2253 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2254 void *msg, const char *field, DOM_SID **sids)
2256 struct berval **values;
2260 values = ldap_get_values_len(ads->ld, msg, field);
2265 for (i=0; values[i]; i++)
2268 (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * i);
2270 ldap_value_free_len(values);
2275 for (i=0; values[i]; i++) {
2276 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2279 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2284 ldap_value_free_len(values);
2289 * pull a SEC_DESC from a ADS result
2290 * @param ads connection to ads server
2291 * @param mem_ctx TALLOC_CTX for allocating sid array
2292 * @param msg Results of search
2293 * @param field Attribute to retrieve
2294 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2295 * @return boolean inidicating success
2297 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2298 void *msg, const char *field, SEC_DESC **sd)
2300 struct berval **values;
2304 values = ldap_get_values_len(ads->ld, msg, field);
2306 if (!values) return False;
2309 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2310 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2311 prs_set_offset(&ps,0);
2313 ret = sec_io_desc("sd", sd, &ps, 1);
2316 ldap_value_free_len(values);
2321 * in order to support usernames longer than 21 characters we need to
2322 * use both the sAMAccountName and the userPrincipalName attributes
2323 * It seems that not all users have the userPrincipalName attribute set
2325 * @param ads connection to ads server
2326 * @param mem_ctx TALLOC_CTX for allocating sid array
2327 * @param msg Results of search
2328 * @return the username
2330 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
2335 /* lookup_name() only works on the sAMAccountName to
2336 returning the username portion of userPrincipalName
2337 breaks winbindd_getpwnam() */
2339 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2340 if (ret && (p = strchr_m(ret, '@'))) {
2345 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2350 * find the update serial number - this is the core of the ldap cache
2351 * @param ads connection to ads server
2352 * @param ads connection to ADS server
2353 * @param usn Pointer to retrieved update serial number
2354 * @return status of search
2356 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2358 const char *attrs[] = {"highestCommittedUSN", NULL};
2362 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2363 if (!ADS_ERR_OK(status))
2366 if (ads_count_replies(ads, res) != 1) {
2367 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2370 ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
2371 ads_msgfree(ads, res);
2375 /* parse a ADS timestring - typical string is
2376 '20020917091222.0Z0' which means 09:12.22 17th September
2378 static time_t ads_parse_time(const char *str)
2384 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2385 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2386 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2397 * Find the servers name and realm - this can be done before authentication
2398 * The ldapServiceName field on w2k looks like this:
2399 * vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
2400 * @param ads connection to ads server
2401 * @return status of search
2403 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
2405 const char *attrs[] = {"ldapServiceName", "currentTime", NULL};
2413 if (!(ctx = talloc_init("ads_server_info"))) {
2414 return ADS_ERROR(LDAP_NO_MEMORY);
2417 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2418 if (!ADS_ERR_OK(status)) {
2419 talloc_destroy(ctx);
2423 value = ads_pull_string(ads, ctx, res, "ldapServiceName");
2425 ads_msgfree(ads, res);
2426 talloc_destroy(ctx);
2427 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2430 timestr = ads_pull_string(ads, ctx, res, "currentTime");
2432 ads_msgfree(ads, res);
2433 talloc_destroy(ctx);
2434 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2437 ads_msgfree(ads, res);
2439 p = strchr(value, ':');
2441 talloc_destroy(ctx);
2442 DEBUG(1, ("ads_server_info: returned ldap server name did not contain a ':' "
2443 "so was deemed invalid\n"));
2444 return ADS_ERROR(LDAP_DECODING_ERROR);
2447 SAFE_FREE(ads->config.ldap_server_name);
2449 ads->config.ldap_server_name = strdup(p+1);
2450 p = strchr(ads->config.ldap_server_name, '$');
2451 if (!p || p[1] != '@') {
2452 talloc_destroy(ctx);
2453 DEBUG(1, ("ads_server_info: returned ldap server name (%s) does not contain '$@'"
2454 " so was deemed invalid\n", ads->config.ldap_server_name));
2455 SAFE_FREE(ads->config.ldap_server_name);
2456 return ADS_ERROR(LDAP_DECODING_ERROR);
2461 SAFE_FREE(ads->config.realm);
2462 SAFE_FREE(ads->config.bind_path);
2464 ads->config.realm = strdup(p+2);
2465 ads->config.bind_path = ads_build_dn(ads->config.realm);
2467 DEBUG(3,("got ldap server name %s@%s, using bind path: %s\n",
2468 ads->config.ldap_server_name, ads->config.realm,
2469 ads->config.bind_path));
2471 ads->config.current_time = ads_parse_time(timestr);
2473 if (ads->config.current_time != 0) {
2474 ads->auth.time_offset = ads->config.current_time - time(NULL);
2475 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2478 talloc_destroy(ctx);
2484 * find the domain sid for our domain
2485 * @param ads connection to ads server
2486 * @param sid Pointer to domain sid
2487 * @return status of search
2489 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2491 const char *attrs[] = {"objectSid", NULL};
2495 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2497 if (!ADS_ERR_OK(rc)) return rc;
2498 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2499 ads_msgfree(ads, res);
2500 return ADS_ERROR_SYSTEM(ENOENT);
2502 ads_msgfree(ads, res);
2507 /* this is rather complex - we need to find the allternate (netbios) name
2508 for the domain, but there isn't a simple query to do this. Instead
2509 we look for the principle names on the DCs account and find one that has
2510 the right form, then extract the netbios name of the domain from that
2512 NOTE! better method is this:
2514 bin/net -Uadministrator%XXXXX ads search '(&(objectclass=crossref)(dnsroot=VNET3.HOME.SAMBA.ORG))' nETBIOSName
2516 but you need to force the bind path to match the configurationNamingContext from the rootDSE
2519 ADS_STATUS ads_workgroup_name(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **workgroup)
2528 const char *attrs[] = {"servicePrincipalName", NULL};
2531 (*workgroup) = NULL;
2533 asprintf(&expr, "(&(objectclass=computer)(dnshostname=%s.%s))",
2534 ads->config.ldap_server_name, ads->config.realm);
2536 ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2539 rc = ads_search(ads, &res, expr, attrs);
2542 if (!ADS_ERR_OK(rc)) {
2546 principles = ads_pull_strings(ads, mem_ctx, res,
2547 "servicePrincipalName", &num_principals);
2549 ads_msgfree(ads, res);
2552 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2555 asprintf(&prefix, "HOST/%s.%s/",
2556 ads->config.ldap_server_name,
2559 prefix_length = strlen(prefix);
2561 for (i=0;principles[i]; i++) {
2562 if (strnequal(principles[i], prefix, prefix_length) &&
2563 !strequal(ads->config.realm, principles[i]+prefix_length) &&
2564 !strchr(principles[i]+prefix_length, '.')) {
2565 /* found an alternate (short) name for the domain. */
2566 DEBUG(3,("Found alternate name '%s' for realm '%s'\n",
2567 principles[i]+prefix_length,
2568 ads->config.realm));
2569 (*workgroup) = talloc_strdup(mem_ctx, principles[i]+prefix_length);
2576 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);