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) {
229 /* by default use the machine account */
231 fstrcpy(myname, global_myname());
233 asprintf(&ads->auth.user_name, "HOST/%s", myname);
236 if (!ads->auth.realm) {
237 ads->auth.realm = strdup(ads->config.realm);
240 if (!ads->auth.kdc_server) {
241 ads->auth.kdc_server = strdup(inet_ntoa(ads->ldap_ip));
245 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
246 to MIT kerberos to work (tridge) */
249 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
250 setenv(env, ads->auth.kdc_server, 1);
255 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
259 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
260 return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
263 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
264 return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
267 return ads_sasl_bind(ads);
271 Duplicate a struct berval into talloc'ed memory
273 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
275 struct berval *value;
277 if (!in_val) return NULL;
279 value = talloc_zero(ctx, sizeof(struct berval));
282 if (in_val->bv_len == 0) return value;
284 value->bv_len = in_val->bv_len;
285 value->bv_val = talloc_memdup(ctx, in_val->bv_val, in_val->bv_len);
290 Make a values list out of an array of (struct berval *)
292 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
293 const struct berval **in_vals)
295 struct berval **values;
298 if (!in_vals) return NULL;
299 for (i=0; in_vals[i]; i++); /* count values */
300 values = (struct berval **) talloc_zero(ctx,
301 (i+1)*sizeof(struct berval *));
302 if (!values) return NULL;
304 for (i=0; in_vals[i]; i++) {
305 values[i] = dup_berval(ctx, in_vals[i]);
311 UTF8-encode a values list out of an array of (char *)
313 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
318 if (!in_vals) return NULL;
319 for (i=0; in_vals[i]; i++); /* count values */
320 values = (char ** ) talloc_zero(ctx, (i+1)*sizeof(char *));
321 if (!values) return NULL;
323 for (i=0; in_vals[i]; i++) {
324 push_utf8_talloc(ctx, &values[i], in_vals[i]);
330 Pull a (char *) array out of a UTF8-encoded values list
332 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
337 if (!in_vals) return NULL;
338 for (i=0; in_vals[i]; i++); /* count values */
339 values = (char **) talloc_zero(ctx, (i+1)*sizeof(char *));
340 if (!values) return NULL;
342 for (i=0; in_vals[i]; i++) {
343 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
349 * Do a search with paged results. cookie must be null on the first
350 * call, and then returned on each subsequent call. It will be null
351 * again when the entire search is complete
352 * @param ads connection to ads server
353 * @param bind_path Base dn for the search
354 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
355 * @param expr Search expression - specified in local charset
356 * @param attrs Attributes to retrieve - specified in utf8 or ascii
357 * @param res ** which will contain results - free res* with ads_msgfree()
358 * @param count Number of entries retrieved on this page
359 * @param cookie The paged results cookie to be returned on subsequent calls
360 * @return status of search
362 ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
363 int scope, const char *expr,
364 const char **attrs, void **res,
365 int *count, void **cookie)
368 char *utf8_expr, *utf8_path, **search_attrs;
369 LDAPControl PagedResults, NoReferrals, *controls[3], **rcontrols;
370 BerElement *cookie_be = NULL;
371 struct berval *cookie_bv= NULL;
376 if (!(ctx = talloc_init("ads_do_paged_search")))
377 return ADS_ERROR(LDAP_NO_MEMORY);
379 /* 0 means the conversion worked but the result was empty
380 so we only fail if it's -1. In any case, it always
381 at least nulls out the dest */
382 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
383 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
388 if (!attrs || !(*attrs))
391 /* This would be the utf8-encoded version...*/
392 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
393 if (!(str_list_copy(&search_attrs, attrs))) {
400 /* Paged results only available on ldap v3 or later */
401 ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
402 if (version < LDAP_VERSION3) {
403 rc = LDAP_NOT_SUPPORTED;
407 cookie_be = ber_alloc_t(LBER_USE_DER);
408 if (cookie && *cookie) {
409 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
410 ber_bvfree(*cookie); /* don't need it from last time */
413 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
415 ber_flatten(cookie_be, &cookie_bv);
416 PagedResults.ldctl_oid = ADS_PAGE_CTL_OID;
417 PagedResults.ldctl_iscritical = (char) 1;
418 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
419 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
421 NoReferrals.ldctl_oid = ADS_NO_REFERRALS_OID;
422 NoReferrals.ldctl_iscritical = (char) 0;
423 NoReferrals.ldctl_value.bv_len = 0;
424 NoReferrals.ldctl_value.bv_val = "";
427 controls[0] = &NoReferrals;
428 controls[1] = &PagedResults;
433 /* we need to disable referrals as the openldap libs don't
434 handle them and paged results at the same time. Using them
435 together results in the result record containing the server
436 page control being removed from the result list (tridge/jmcd)
438 leaving this in despite the control that says don't generate
439 referrals, in case the server doesn't support it (jmcd)
441 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
443 rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_expr,
444 search_attrs, 0, controls,
445 NULL, NULL, LDAP_NO_LIMIT, (LDAPMessage **)res);
447 ber_free(cookie_be, 1);
448 ber_bvfree(cookie_bv);
451 DEBUG(3,("ldap_search_ext_s(%s) -> %s\n", expr, ldap_err2string(rc)));
455 rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
456 NULL, &rcontrols, 0);
462 for (i=0; rcontrols[i]; i++) {
463 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
464 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
465 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
467 /* the berval is the cookie, but must be freed when
469 if (cookie_bv->bv_len) /* still more to do */
470 *cookie=ber_bvdup(cookie_bv);
473 ber_bvfree(cookie_bv);
474 ber_free(cookie_be, 1);
478 ldap_controls_free(rcontrols);
482 /* if/when we decide to utf8-encode attrs, take out this next line */
483 str_list_free(&search_attrs);
485 return ADS_ERROR(rc);
490 * Get all results for a search. This uses ads_do_paged_search() to return
491 * all entries in a large search.
492 * @param ads connection to ads server
493 * @param bind_path Base dn for the search
494 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
495 * @param expr Search expression
496 * @param attrs Attributes to retrieve
497 * @param res ** which will contain results - free res* with ads_msgfree()
498 * @return status of search
500 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
501 int scope, const char *expr,
502 const char **attrs, void **res)
508 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, res,
511 if (!ADS_ERR_OK(status)) return status;
516 LDAPMessage *msg, *next;
518 status2 = ads_do_paged_search(ads, bind_path, scope, expr,
519 attrs, &res2, &count, &cookie);
521 if (!ADS_ERR_OK(status2)) break;
523 /* this relies on the way that ldap_add_result_entry() works internally. I hope
524 that this works on all ldap libs, but I have only tested with openldap */
525 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
526 next = ads_next_entry(ads, msg);
527 ldap_add_result_entry((LDAPMessage **)res, msg);
529 /* note that we do not free res2, as the memory is now
530 part of the main returned list */
537 * Run a function on all results for a search. Uses ads_do_paged_search() and
538 * runs the function as each page is returned, using ads_process_results()
539 * @param ads connection to ads server
540 * @param bind_path Base dn for the search
541 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
542 * @param expr Search expression - specified in local charset
543 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
544 * @param fn Function which takes attr name, values list, and data_area
545 * @param data_area Pointer which is passed to function on each call
546 * @return status of search
548 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
549 int scope, const char *expr, const char **attrs,
550 BOOL(*fn)(char *, void **, void *),
558 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
561 if (!ADS_ERR_OK(status)) return status;
563 ads_process_results(ads, res, fn, data_area);
564 ads_msgfree(ads, res);
567 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
568 &res, &count, &cookie);
570 if (!ADS_ERR_OK(status)) break;
572 ads_process_results(ads, res, fn, data_area);
573 ads_msgfree(ads, res);
580 * Do a search with a timeout.
581 * @param ads connection to ads server
582 * @param bind_path Base dn for the search
583 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
584 * @param expr Search expression
585 * @param attrs Attributes to retrieve
586 * @param res ** which will contain results - free res* with ads_msgfree()
587 * @return status of search
589 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
591 const char **attrs, void **res)
593 struct timeval timeout;
595 char *utf8_expr, *utf8_path, **search_attrs = NULL;
598 if (!(ctx = talloc_init("ads_do_search"))) {
599 DEBUG(1,("ads_do_search: talloc_init() failed!"));
600 return ADS_ERROR(LDAP_NO_MEMORY);
603 /* 0 means the conversion worked but the result was empty
604 so we only fail if it's negative. In any case, it always
605 at least nulls out the dest */
606 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
607 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
608 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
613 if (!attrs || !(*attrs))
616 /* This would be the utf8-encoded version...*/
617 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
618 if (!(str_list_copy(&search_attrs, attrs)))
620 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
626 timeout.tv_sec = ADS_SEARCH_TIMEOUT;
630 /* see the note in ads_do_paged_search - we *must* disable referrals */
631 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
633 rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_expr,
634 search_attrs, 0, NULL, NULL,
635 &timeout, LDAP_NO_LIMIT, (LDAPMessage **)res);
637 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
638 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
644 /* if/when we decide to utf8-encode attrs, take out this next line */
645 str_list_free(&search_attrs);
646 return ADS_ERROR(rc);
649 * Do a general ADS search
650 * @param ads connection to ads server
651 * @param res ** which will contain results - free res* with ads_msgfree()
652 * @param expr Search expression
653 * @param attrs Attributes to retrieve
654 * @return status of search
656 ADS_STATUS ads_search(ADS_STRUCT *ads, void **res,
660 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
665 * Do a search on a specific DistinguishedName
666 * @param ads connection to ads server
667 * @param res ** which will contain results - free res* with ads_msgfree()
668 * @param dn DistinguishName to search
669 * @param attrs Attributes to retrieve
670 * @return status of search
672 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, void **res,
676 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res);
680 * Free up memory from a ads_search
681 * @param ads connection to ads server
682 * @param msg Search results to free
684 void ads_msgfree(ADS_STRUCT *ads, void *msg)
691 * Free up memory from various ads requests
692 * @param ads connection to ads server
693 * @param mem Area to free
695 void ads_memfree(ADS_STRUCT *ads, void *mem)
701 * Get a dn from search results
702 * @param ads connection to ads server
703 * @param msg Search result
706 char *ads_get_dn(ADS_STRUCT *ads, void *msg)
708 char *utf8_dn, *unix_dn;
710 utf8_dn = ldap_get_dn(ads->ld, msg);
712 pull_utf8_allocate((void **) &unix_dn, utf8_dn);
713 ldap_memfree(utf8_dn);
718 * Find a machine account given a hostname
719 * @param ads connection to ads server
720 * @param res ** which will contain results - free res* with ads_msgfree()
721 * @param host Hostname to search for
722 * @return status of search
724 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *host)
728 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
730 /* the easiest way to find a machine account anywhere in the tree
731 is to look for hostname$ */
732 if (asprintf(&expr, "(samAccountName=%s$)", host) == -1) {
733 DEBUG(1, ("asprintf failed!\n"));
734 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
737 status = ads_search(ads, res, expr, attrs);
743 * Initialize a list of mods to be used in a modify request
744 * @param ctx An initialized TALLOC_CTX
745 * @return allocated ADS_MODLIST
747 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
749 #define ADS_MODLIST_ALLOC_SIZE 10
752 if ((mods = (LDAPMod **) talloc_zero(ctx, sizeof(LDAPMod *) *
753 (ADS_MODLIST_ALLOC_SIZE + 1))))
754 /* -1 is safety to make sure we don't go over the end.
755 need to reset it to NULL before doing ldap modify */
756 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
763 add an attribute to the list, with values list already constructed
765 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
766 int mod_op, const char *name,
770 LDAPMod **modlist = (LDAPMod **) *mods;
771 struct berval **ber_values = NULL;
772 char **char_values = NULL;
775 mod_op = LDAP_MOD_DELETE;
777 if (mod_op & LDAP_MOD_BVALUES)
778 ber_values = ads_dup_values(ctx,
779 (const struct berval **)invals);
781 char_values = ads_push_strvals(ctx,
782 (const char **) invals);
785 /* find the first empty slot */
786 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
788 if (modlist[curmod] == (LDAPMod *) -1) {
789 if (!(modlist = talloc_realloc(ctx, modlist,
790 (curmod+ADS_MODLIST_ALLOC_SIZE+1)*sizeof(LDAPMod *))))
791 return ADS_ERROR(LDAP_NO_MEMORY);
792 memset(&modlist[curmod], 0,
793 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
794 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
798 if (!(modlist[curmod] = talloc_zero(ctx, sizeof(LDAPMod))))
799 return ADS_ERROR(LDAP_NO_MEMORY);
800 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
801 if (mod_op & LDAP_MOD_BVALUES) {
802 modlist[curmod]->mod_bvalues = ber_values;
803 } else if (mod_op & LDAP_MOD_DELETE) {
804 modlist[curmod]->mod_values = NULL;
806 modlist[curmod]->mod_values = char_values;
809 modlist[curmod]->mod_op = mod_op;
810 return ADS_ERROR(LDAP_SUCCESS);
814 * Add a single string value to a mod list
815 * @param ctx An initialized TALLOC_CTX
816 * @param mods An initialized ADS_MODLIST
817 * @param name The attribute name to add
818 * @param val The value to add - NULL means DELETE
819 * @return ADS STATUS indicating success of add
821 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
822 const char *name, const char *val)
824 const char *values[2];
830 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
831 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name,
832 (const void **) values);
836 * Add an array of string values to a mod list
837 * @param ctx An initialized TALLOC_CTX
838 * @param mods An initialized ADS_MODLIST
839 * @param name The attribute name to add
840 * @param vals The array of string values to add - NULL means DELETE
841 * @return ADS STATUS indicating success of add
843 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
844 const char *name, const char **vals)
847 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
848 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
849 name, (const void **) vals);
853 * Add a single ber-encoded value to a mod list
854 * @param ctx An initialized TALLOC_CTX
855 * @param mods An initialized ADS_MODLIST
856 * @param name The attribute name to add
857 * @param val The value to add - NULL means DELETE
858 * @return ADS STATUS indicating success of add
860 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
861 const char *name, const struct berval *val)
863 const struct berval *values[2];
868 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
869 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
870 name, (const void **) values);
874 * Perform an ldap modify
875 * @param ads connection to ads server
876 * @param mod_dn DistinguishedName to modify
877 * @param mods list of modifications to perform
878 * @return status of modify
880 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
883 char *utf8_dn = NULL;
885 this control is needed to modify that contains a currently
886 non-existent attribute (but allowable for the object) to run
888 LDAPControl PermitModify = {
889 ADS_PERMIT_MODIFY_OID,
892 LDAPControl *controls[2];
894 controls[0] = &PermitModify;
897 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
898 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
901 /* find the end of the list, marked by NULL or -1 */
902 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
903 /* make sure the end of the list is NULL */
905 ret = ldap_modify_ext_s(ads->ld, utf8_dn,
906 (LDAPMod **) mods, controls, NULL);
908 return ADS_ERROR(ret);
912 * Perform an ldap add
913 * @param ads connection to ads server
914 * @param new_dn DistinguishedName to add
915 * @param mods list of attributes and values for DN
916 * @return status of add
918 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
921 char *utf8_dn = NULL;
923 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
924 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
925 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
928 /* find the end of the list, marked by NULL or -1 */
929 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
930 /* make sure the end of the list is NULL */
933 ret = ldap_add_s(ads->ld, utf8_dn, mods);
935 return ADS_ERROR(ret);
939 * Delete a DistinguishedName
940 * @param ads connection to ads server
941 * @param new_dn DistinguishedName to delete
942 * @return status of delete
944 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
947 char *utf8_dn = NULL;
948 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
949 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
950 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
953 ret = ldap_delete_s(ads->ld, utf8_dn);
954 return ADS_ERROR(ret);
958 * Build an org unit string
959 * if org unit is Computers or blank then assume a container, otherwise
960 * assume a \ separated list of organisational units
961 * @param org_unit Organizational unit
962 * @return org unit string - caller must free
964 char *ads_ou_string(const char *org_unit)
966 if (!org_unit || !*org_unit || strcasecmp(org_unit, "Computers") == 0) {
967 return strdup("cn=Computers");
970 return ads_build_path(org_unit, "\\/", "ou=", 1);
976 add a machine account to the ADS server
978 static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *hostname,
980 const char *org_unit)
982 ADS_STATUS ret, status;
983 char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
987 const char *objectClass[] = {"top", "person", "organizationalPerson",
988 "user", "computer", NULL};
989 const char *servicePrincipalName[5] = {NULL, NULL, NULL, NULL, NULL};
991 unsigned acct_control;
993 if (!(ctx = talloc_init("machine_account")))
994 return ADS_ERROR(LDAP_NO_MEMORY);
996 ret = ADS_ERROR(LDAP_NO_MEMORY);
998 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", hostname)))
1000 if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm)))
1002 ou_str = ads_ou_string(org_unit);
1004 DEBUG(1, ("ads_ou_string returned NULL (malloc failure?)\n"));
1007 new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", hostname, ou_str,
1008 ads->config.bind_path);
1009 servicePrincipalName[0] = talloc_asprintf(ctx, "HOST/%s", hostname);
1010 psp = talloc_asprintf(ctx, "HOST/%s.%s",
1013 strlower_m(&psp[5]);
1014 servicePrincipalName[1] = psp;
1015 servicePrincipalName[2] = talloc_asprintf(ctx, "CIFS/%s", hostname);
1016 psp2 = talloc_asprintf(ctx, "CIFS/%s.%s",
1019 strlower_m(&psp2[5]);
1020 servicePrincipalName[3] = psp2;
1026 if (!(samAccountName = talloc_asprintf(ctx, "%s$", hostname)))
1029 acct_control = account_type | UF_DONT_EXPIRE_PASSWD;
1030 #ifndef ENCTYPE_ARCFOUR_HMAC
1031 acct_control |= UF_USE_DES_KEY_ONLY;
1034 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control)))
1037 if (!(mods = ads_init_mods(ctx)))
1040 ads_mod_str(ctx, &mods, "cn", hostname);
1041 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1042 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1043 ads_mod_str(ctx, &mods, "userPrincipalName", host_upn);
1044 ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1045 ads_mod_str(ctx, &mods, "dNSHostName", hostname);
1046 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1047 ads_mod_str(ctx, &mods, "operatingSystem", "Samba");
1048 ads_mod_str(ctx, &mods, "operatingSystemVersion", SAMBA_VERSION_STRING);
1050 ret = ads_gen_add(ads, new_dn, mods);
1052 if (!ADS_ERR_OK(ret))
1055 /* Do not fail if we can't set security descriptor
1056 * it shouldn't be mandatory and probably we just
1057 * don't have enough rights to do it.
1059 status = ads_set_machine_sd(ads, hostname, new_dn);
1061 if (!ADS_ERR_OK(status)) {
1062 DEBUG(0, ("Warning: ads_set_machine_sd: %s\n",
1063 ads_errstr(status)));
1066 talloc_destroy(ctx);
1071 dump a binary result from ldap
1073 static void dump_binary(const char *field, struct berval **values)
1076 for (i=0; values[i]; i++) {
1077 printf("%s: ", field);
1078 for (j=0; j<values[i]->bv_len; j++) {
1079 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1092 static void dump_guid(const char *field, struct berval **values)
1096 for (i=0; values[i]; i++) {
1097 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1098 printf("%s: %s\n", field, smb_uuid_string_static(guid));
1103 dump a sid result from ldap
1105 static void dump_sid(const char *field, struct berval **values)
1108 for (i=0; values[i]; i++) {
1110 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1111 printf("%s: %s\n", field, sid_string_static(&sid));
1116 dump ntSecurityDescriptor
1118 static void dump_sd(const char *filed, struct berval **values)
1123 TALLOC_CTX *ctx = 0;
1125 if (!(ctx = talloc_init("sec_io_desc")))
1129 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1130 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1131 prs_set_offset(&ps,0);
1134 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1136 talloc_destroy(ctx);
1139 if (psd) ads_disp_sd(psd);
1142 talloc_destroy(ctx);
1146 dump a string result from ldap
1148 static void dump_string(const char *field, char **values)
1151 for (i=0; values[i]; i++) {
1152 printf("%s: %s\n", field, values[i]);
1157 dump a field from LDAP on stdout
1161 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1166 void (*handler)(const char *, struct berval **);
1168 {"objectGUID", False, dump_guid},
1169 {"nTSecurityDescriptor", False, dump_sd},
1170 {"dnsRecord", False, dump_binary},
1171 {"objectSid", False, dump_sid},
1172 {"tokenGroups", False, dump_sid},
1177 if (!field) { /* must be end of an entry */
1182 for (i=0; handlers[i].name; i++) {
1183 if (StrCaseCmp(handlers[i].name, field) == 0) {
1184 if (!values) /* first time, indicate string or not */
1185 return handlers[i].string;
1186 handlers[i].handler(field, (struct berval **) values);
1190 if (!handlers[i].name) {
1191 if (!values) /* first time, indicate string conversion */
1193 dump_string(field, (char **)values);
1199 * Dump a result from LDAP on stdout
1200 * used for debugging
1201 * @param ads connection to ads server
1202 * @param res Results to dump
1205 void ads_dump(ADS_STRUCT *ads, void *res)
1207 ads_process_results(ads, res, ads_dump_field, NULL);
1211 * Walk through results, calling a function for each entry found.
1212 * The function receives a field name, a berval * array of values,
1213 * and a data area passed through from the start. The function is
1214 * called once with null for field and values at the end of each
1216 * @param ads connection to ads server
1217 * @param res Results to process
1218 * @param fn Function for processing each result
1219 * @param data_area user-defined area to pass to function
1221 void ads_process_results(ADS_STRUCT *ads, void *res,
1222 BOOL(*fn)(char *, void **, void *),
1228 if (!(ctx = talloc_init("ads_process_results")))
1231 for (msg = ads_first_entry(ads, res); msg;
1232 msg = ads_next_entry(ads, msg)) {
1236 for (utf8_field=ldap_first_attribute(ads->ld,
1237 (LDAPMessage *)msg,&b);
1239 utf8_field=ldap_next_attribute(ads->ld,
1240 (LDAPMessage *)msg,b)) {
1241 struct berval **ber_vals;
1242 char **str_vals, **utf8_vals;
1246 pull_utf8_talloc(ctx, &field, utf8_field);
1247 string = fn(field, NULL, data_area);
1250 utf8_vals = ldap_get_values(ads->ld,
1251 (LDAPMessage *)msg, field);
1252 str_vals = ads_pull_strvals(ctx,
1253 (const char **) utf8_vals);
1254 fn(field, (void **) str_vals, data_area);
1255 ldap_value_free(utf8_vals);
1257 ber_vals = ldap_get_values_len(ads->ld,
1258 (LDAPMessage *)msg, field);
1259 fn(field, (void **) ber_vals, data_area);
1261 ldap_value_free_len(ber_vals);
1263 ldap_memfree(utf8_field);
1266 talloc_destroy_pool(ctx);
1267 fn(NULL, NULL, data_area); /* completed an entry */
1270 talloc_destroy(ctx);
1274 * count how many replies are in a LDAPMessage
1275 * @param ads connection to ads server
1276 * @param res Results to count
1277 * @return number of replies
1279 int ads_count_replies(ADS_STRUCT *ads, void *res)
1281 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1285 * Join a machine to a realm
1286 * Creates the machine account and sets the machine password
1287 * @param ads connection to ads server
1288 * @param hostname name of host to add
1289 * @param org_unit Organizational unit to place machine in
1290 * @return status of join
1292 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *hostname,
1293 uint32 account_type, const char *org_unit)
1299 /* hostname must be lowercase */
1300 host = strdup(hostname);
1303 status = ads_find_machine_acct(ads, (void **)&res, host);
1304 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1305 DEBUG(0, ("Host account for %s already exists - deleting old account\n", host));
1306 status = ads_leave_realm(ads, host);
1307 if (!ADS_ERR_OK(status)) {
1308 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
1309 host, ads->config.realm));
1314 status = ads_add_machine_acct(ads, host, account_type, org_unit);
1315 if (!ADS_ERR_OK(status)) {
1316 DEBUG(0, ("ads_add_machine_acct: %s\n", ads_errstr(status)));
1320 status = ads_find_machine_acct(ads, (void **)&res, host);
1321 if (!ADS_ERR_OK(status)) {
1322 DEBUG(0, ("Host account test failed\n"));
1332 * Delete a machine from the realm
1333 * @param ads connection to ads server
1334 * @param hostname Machine to remove
1335 * @return status of delete
1337 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
1341 char *hostnameDN, *host;
1344 /* hostname must be lowercase */
1345 host = strdup(hostname);
1348 status = ads_find_machine_acct(ads, &res, host);
1349 if (!ADS_ERR_OK(status)) {
1350 DEBUG(0, ("Host account for %s does not exist.\n", host));
1354 msg = ads_first_entry(ads, res);
1356 return ADS_ERROR_SYSTEM(ENOENT);
1359 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
1360 rc = ldap_delete_s(ads->ld, hostnameDN);
1361 ads_memfree(ads, hostnameDN);
1362 if (rc != LDAP_SUCCESS) {
1363 return ADS_ERROR(rc);
1366 status = ads_find_machine_acct(ads, &res, host);
1367 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1368 DEBUG(0, ("Failed to remove host account.\n"));
1378 * add machine account to existing security descriptor
1379 * @param ads connection to ads server
1380 * @param hostname machine to add
1381 * @param dn DN of security descriptor
1384 ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
1386 const char *attrs[] = {"nTSecurityDescriptor", "objectSid", 0};
1389 struct berval bval = {0, NULL};
1391 char *escaped_hostname = escape_ldap_string_alloc(hostname);
1393 LDAPMessage *res = 0;
1394 LDAPMessage *msg = 0;
1395 ADS_MODLIST mods = 0;
1400 SEC_DESC *psd = NULL;
1401 TALLOC_CTX *ctx = NULL;
1403 /* Avoid segmentation fault in prs_mem_free if
1404 * we have to bail out before prs_init */
1405 ps_wire.is_dynamic = False;
1407 if (!ads) return ADS_ERROR(LDAP_SERVER_DOWN);
1409 ret = ADS_ERROR(LDAP_SUCCESS);
1411 if (!escaped_hostname) {
1412 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1415 if (asprintf(&expr, "(samAccountName=%s$)", escaped_hostname) == -1) {
1416 DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n"));
1417 SAFE_FREE(escaped_hostname);
1418 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1421 SAFE_FREE(escaped_hostname);
1423 ret = ads_search(ads, (void *) &res, expr, attrs);
1425 if (!ADS_ERR_OK(ret)) return ret;
1427 if ( !(msg = ads_first_entry(ads, res) )) {
1428 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1429 goto ads_set_sd_error;
1432 if (!ads_pull_sid(ads, msg, attrs[1], &sid)) {
1433 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1434 goto ads_set_sd_error;
1437 if (!(ctx = talloc_init("sec_io_desc"))) {
1438 ret = ADS_ERROR(LDAP_NO_MEMORY);
1439 goto ads_set_sd_error;
1442 if (!ads_pull_sd(ads, ctx, msg, attrs[0], &psd)) {
1443 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1444 goto ads_set_sd_error;
1447 status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size);
1449 if (!NT_STATUS_IS_OK(status)) {
1450 ret = ADS_ERROR_NT(status);
1451 goto ads_set_sd_error;
1454 if (!prs_init(&ps_wire, sd_size, ctx, MARSHALL)) {
1455 ret = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1458 if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1)) {
1459 ret = ADS_ERROR(LDAP_NO_MEMORY);
1460 goto ads_set_sd_error;
1464 file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size);
1466 if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY);
1468 bval.bv_len = prs_offset(&ps_wire);
1469 bval.bv_val = talloc(ctx, bval.bv_len);
1471 ret = ADS_ERROR(LDAP_NO_MEMORY);
1472 goto ads_set_sd_error;
1475 prs_set_offset(&ps_wire, 0);
1477 if (!prs_copy_data_out(bval.bv_val, &ps_wire, bval.bv_len)) {
1478 ret = ADS_ERROR(LDAP_NO_MEMORY);
1479 goto ads_set_sd_error;
1482 ret = ads_mod_ber(ctx, &mods, attrs[0], &bval);
1483 if (ADS_ERR_OK(ret)) {
1484 ret = ads_gen_mod(ads, dn, mods);
1488 ads_msgfree(ads, res);
1489 prs_mem_free(&ps_wire);
1490 talloc_destroy(ctx);
1495 * pull the first entry from a ADS result
1496 * @param ads connection to ads server
1497 * @param res Results of search
1498 * @return first entry from result
1500 void *ads_first_entry(ADS_STRUCT *ads, void *res)
1502 return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
1506 * pull the next entry from a ADS result
1507 * @param ads connection to ads server
1508 * @param res Results of search
1509 * @return next entry from result
1511 void *ads_next_entry(ADS_STRUCT *ads, void *res)
1513 return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
1517 * pull a single string from a ADS result
1518 * @param ads connection to ads server
1519 * @param mem_ctx TALLOC_CTX to use for allocating result string
1520 * @param msg Results of search
1521 * @param field Attribute to retrieve
1522 * @return Result string in talloc context
1524 char *ads_pull_string(ADS_STRUCT *ads,
1525 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1532 values = ldap_get_values(ads->ld, msg, field);
1537 rc = pull_utf8_talloc(mem_ctx, &ux_string,
1539 if (rc != (size_t)-1)
1543 ldap_value_free(values);
1548 * pull an array of strings from a ADS result
1549 * @param ads connection to ads server
1550 * @param mem_ctx TALLOC_CTX to use for allocating result string
1551 * @param msg Results of search
1552 * @param field Attribute to retrieve
1553 * @return Result strings in talloc context
1555 char **ads_pull_strings(ADS_STRUCT *ads,
1556 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1562 values = ldap_get_values(ads->ld, msg, field);
1566 for (i=0;values[i];i++)
1570 ret = talloc(mem_ctx, sizeof(char *) * (n+1));
1572 ldap_value_free(values);
1577 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
1578 ldap_value_free(values);
1584 ldap_value_free(values);
1590 * pull a single uint32 from a ADS result
1591 * @param ads connection to ads server
1592 * @param msg Results of search
1593 * @param field Attribute to retrieve
1594 * @param v Pointer to int to store result
1595 * @return boolean inidicating success
1597 BOOL ads_pull_uint32(ADS_STRUCT *ads,
1598 void *msg, const char *field, uint32 *v)
1602 values = ldap_get_values(ads->ld, msg, field);
1606 ldap_value_free(values);
1610 *v = atoi(values[0]);
1611 ldap_value_free(values);
1616 * pull a single objectGUID from an ADS result
1617 * @param ads connection to ADS server
1618 * @param msg results of search
1619 * @param guid 37-byte area to receive text guid
1620 * @return boolean indicating success
1622 BOOL ads_pull_guid(ADS_STRUCT *ads,
1623 void *msg, GUID *guid)
1627 values = ldap_get_values(ads->ld, msg, "objectGUID");
1632 memcpy(guid, values[0], sizeof(GUID));
1633 ldap_value_free(values);
1636 ldap_value_free(values);
1643 * pull a single DOM_SID from a ADS result
1644 * @param ads connection to ads server
1645 * @param msg Results of search
1646 * @param field Attribute to retrieve
1647 * @param sid Pointer to sid to store result
1648 * @return boolean inidicating success
1650 BOOL ads_pull_sid(ADS_STRUCT *ads,
1651 void *msg, const char *field, DOM_SID *sid)
1653 struct berval **values;
1656 values = ldap_get_values_len(ads->ld, msg, field);
1662 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
1664 ldap_value_free_len(values);
1669 * pull an array of DOM_SIDs from a ADS result
1670 * @param ads connection to ads server
1671 * @param mem_ctx TALLOC_CTX for allocating sid array
1672 * @param msg Results of search
1673 * @param field Attribute to retrieve
1674 * @param sids pointer to sid array to allocate
1675 * @return the count of SIDs pulled
1677 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1678 void *msg, const char *field, DOM_SID **sids)
1680 struct berval **values;
1684 values = ldap_get_values_len(ads->ld, msg, field);
1689 for (i=0; values[i]; i++)
1692 (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * i);
1694 ldap_value_free_len(values);
1699 for (i=0; values[i]; i++) {
1700 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
1703 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
1708 ldap_value_free_len(values);
1713 * pull a SEC_DESC from a ADS result
1714 * @param ads connection to ads server
1715 * @param mem_ctx TALLOC_CTX for allocating sid array
1716 * @param msg Results of search
1717 * @param field Attribute to retrieve
1718 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
1719 * @return boolean inidicating success
1721 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1722 void *msg, const char *field, SEC_DESC **sd)
1724 struct berval **values;
1728 values = ldap_get_values_len(ads->ld, msg, field);
1730 if (!values) return False;
1733 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
1734 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1735 prs_set_offset(&ps,0);
1737 ret = sec_io_desc("sd", sd, &ps, 1);
1740 ldap_value_free_len(values);
1745 * in order to support usernames longer than 21 characters we need to
1746 * use both the sAMAccountName and the userPrincipalName attributes
1747 * It seems that not all users have the userPrincipalName attribute set
1749 * @param ads connection to ads server
1750 * @param mem_ctx TALLOC_CTX for allocating sid array
1751 * @param msg Results of search
1752 * @return the username
1754 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
1758 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
1759 if (ret && (p = strchr(ret, '@'))) {
1763 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
1768 * find the update serial number - this is the core of the ldap cache
1769 * @param ads connection to ads server
1770 * @param ads connection to ADS server
1771 * @param usn Pointer to retrieved update serial number
1772 * @return status of search
1774 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
1776 const char *attrs[] = {"highestCommittedUSN", NULL};
1780 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1781 if (!ADS_ERR_OK(status)) return status;
1783 if (ads_count_replies(ads, res) != 1) {
1784 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1787 ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
1788 ads_msgfree(ads, res);
1792 /* parse a ADS timestring - typical string is
1793 '20020917091222.0Z0' which means 09:12.22 17th September
1795 static time_t ads_parse_time(const char *str)
1801 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
1802 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
1803 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
1814 * Find the servers name and realm - this can be done before authentication
1815 * The ldapServiceName field on w2k looks like this:
1816 * vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
1817 * @param ads connection to ads server
1818 * @return status of search
1820 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
1822 const char *attrs[] = {"ldapServiceName", "currentTime", NULL};
1830 if (!(ctx = talloc_init("ads_server_info"))) {
1831 return ADS_ERROR(LDAP_NO_MEMORY);
1834 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1835 if (!ADS_ERR_OK(status)) return status;
1837 value = ads_pull_string(ads, ctx, res, "ldapServiceName");
1839 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1842 timestr = ads_pull_string(ads, ctx, res, "currentTime");
1844 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1849 p = strchr(value, ':');
1851 talloc_destroy(ctx);
1852 DEBUG(1, ("ads_server_info: returned ldap server name did not contain a ':' so was deemed invalid\n"));
1853 return ADS_ERROR(LDAP_DECODING_ERROR);
1856 SAFE_FREE(ads->config.ldap_server_name);
1858 ads->config.ldap_server_name = strdup(p+1);
1859 p = strchr(ads->config.ldap_server_name, '$');
1860 if (!p || p[1] != '@') {
1861 talloc_destroy(ctx);
1862 DEBUG(1, ("ads_server_info: returned ldap server name (%s) does not contain '$@' so was deemed invalid\n", ads->config.ldap_server_name));
1863 SAFE_FREE(ads->config.ldap_server_name);
1864 return ADS_ERROR(LDAP_DECODING_ERROR);
1869 SAFE_FREE(ads->config.realm);
1870 SAFE_FREE(ads->config.bind_path);
1872 ads->config.realm = strdup(p+2);
1873 ads->config.bind_path = ads_build_dn(ads->config.realm);
1875 DEBUG(3,("got ldap server name %s@%s, using bind path: %s\n",
1876 ads->config.ldap_server_name, ads->config.realm,
1877 ads->config.bind_path));
1879 ads->config.current_time = ads_parse_time(timestr);
1881 if (ads->config.current_time != 0) {
1882 ads->auth.time_offset = ads->config.current_time - time(NULL);
1883 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
1886 talloc_destroy(ctx);
1892 * find the domain sid for our domain
1893 * @param ads connection to ads server
1894 * @param sid Pointer to domain sid
1895 * @return status of search
1897 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
1899 const char *attrs[] = {"objectSid", NULL};
1903 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
1905 if (!ADS_ERR_OK(rc)) return rc;
1906 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
1907 return ADS_ERROR_SYSTEM(ENOENT);
1909 ads_msgfree(ads, res);
1914 /* this is rather complex - we need to find the allternate (netbios) name
1915 for the domain, but there isn't a simple query to do this. Instead
1916 we look for the principle names on the DCs account and find one that has
1917 the right form, then extract the netbios name of the domain from that
1919 NOTE! better method is this:
1921 bin/net -Uadministrator%XXXXX ads search '(&(objectclass=crossref)(dnsroot=VNET3.HOME.SAMBA.ORG))' nETBIOSName
1923 but you need to force the bind path to match the configurationNamingContext from the rootDSE
1926 ADS_STATUS ads_workgroup_name(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char **workgroup)
1935 const char *attrs[] = {"servicePrincipalName", NULL};
1937 (*workgroup) = NULL;
1939 asprintf(&expr, "(&(objectclass=computer)(dnshostname=%s.%s))",
1940 ads->config.ldap_server_name, ads->config.realm);
1941 rc = ads_search(ads, &res, expr, attrs);
1944 if (!ADS_ERR_OK(rc)) {
1948 principles = ads_pull_strings(ads, mem_ctx, res, "servicePrincipalName");
1950 ads_msgfree(ads, res);
1953 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1956 asprintf(&prefix, "HOST/%s.%s/",
1957 ads->config.ldap_server_name,
1960 prefix_length = strlen(prefix);
1962 for (i=0;principles[i]; i++) {
1963 if (strncasecmp(principles[i], prefix, prefix_length) == 0 &&
1964 strcasecmp(ads->config.realm, principles[i]+prefix_length) != 0 &&
1965 !strchr(principles[i]+prefix_length, '.')) {
1966 /* found an alternate (short) name for the domain. */
1967 DEBUG(3,("Found alternate name '%s' for realm '%s'\n",
1968 principles[i]+prefix_length,
1969 ads->config.realm));
1970 (*workgroup) = talloc_strdup(mem_ctx, principles[i]+prefix_length);
1977 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);