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);
713 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
717 if (pull_utf8_allocate((void **) &unix_dn, utf8_dn) == (size_t)-1) {
718 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
722 ldap_memfree(utf8_dn);
727 * Find a machine account given a hostname
728 * @param ads connection to ads server
729 * @param res ** which will contain results - free res* with ads_msgfree()
730 * @param host Hostname to search for
731 * @return status of search
733 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *host)
737 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
739 /* the easiest way to find a machine account anywhere in the tree
740 is to look for hostname$ */
741 if (asprintf(&expr, "(samAccountName=%s$)", host) == -1) {
742 DEBUG(1, ("asprintf failed!\n"));
743 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
746 status = ads_search(ads, res, expr, attrs);
752 * Initialize a list of mods to be used in a modify request
753 * @param ctx An initialized TALLOC_CTX
754 * @return allocated ADS_MODLIST
756 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
758 #define ADS_MODLIST_ALLOC_SIZE 10
761 if ((mods = (LDAPMod **) talloc_zero(ctx, sizeof(LDAPMod *) *
762 (ADS_MODLIST_ALLOC_SIZE + 1))))
763 /* -1 is safety to make sure we don't go over the end.
764 need to reset it to NULL before doing ldap modify */
765 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
772 add an attribute to the list, with values list already constructed
774 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
775 int mod_op, const char *name,
779 LDAPMod **modlist = (LDAPMod **) *mods;
780 struct berval **ber_values = NULL;
781 char **char_values = NULL;
784 mod_op = LDAP_MOD_DELETE;
786 if (mod_op & LDAP_MOD_BVALUES)
787 ber_values = ads_dup_values(ctx,
788 (const struct berval **)invals);
790 char_values = ads_push_strvals(ctx,
791 (const char **) invals);
794 /* find the first empty slot */
795 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
797 if (modlist[curmod] == (LDAPMod *) -1) {
798 if (!(modlist = talloc_realloc(ctx, modlist,
799 (curmod+ADS_MODLIST_ALLOC_SIZE+1)*sizeof(LDAPMod *))))
800 return ADS_ERROR(LDAP_NO_MEMORY);
801 memset(&modlist[curmod], 0,
802 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
803 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
807 if (!(modlist[curmod] = talloc_zero(ctx, sizeof(LDAPMod))))
808 return ADS_ERROR(LDAP_NO_MEMORY);
809 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
810 if (mod_op & LDAP_MOD_BVALUES) {
811 modlist[curmod]->mod_bvalues = ber_values;
812 } else if (mod_op & LDAP_MOD_DELETE) {
813 modlist[curmod]->mod_values = NULL;
815 modlist[curmod]->mod_values = char_values;
818 modlist[curmod]->mod_op = mod_op;
819 return ADS_ERROR(LDAP_SUCCESS);
823 * Add a single string value to a mod list
824 * @param ctx An initialized TALLOC_CTX
825 * @param mods An initialized ADS_MODLIST
826 * @param name The attribute name to add
827 * @param val The value to add - NULL means DELETE
828 * @return ADS STATUS indicating success of add
830 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
831 const char *name, const char *val)
833 const char *values[2];
839 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
840 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name,
841 (const void **) values);
845 * Add an array of string values to a mod list
846 * @param ctx An initialized TALLOC_CTX
847 * @param mods An initialized ADS_MODLIST
848 * @param name The attribute name to add
849 * @param vals The array of string values to add - NULL means DELETE
850 * @return ADS STATUS indicating success of add
852 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
853 const char *name, const char **vals)
856 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
857 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
858 name, (const void **) vals);
862 * Add a single ber-encoded value to a mod list
863 * @param ctx An initialized TALLOC_CTX
864 * @param mods An initialized ADS_MODLIST
865 * @param name The attribute name to add
866 * @param val The value to add - NULL means DELETE
867 * @return ADS STATUS indicating success of add
869 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
870 const char *name, const struct berval *val)
872 const struct berval *values[2];
877 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
878 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
879 name, (const void **) values);
883 * Perform an ldap modify
884 * @param ads connection to ads server
885 * @param mod_dn DistinguishedName to modify
886 * @param mods list of modifications to perform
887 * @return status of modify
889 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
892 char *utf8_dn = NULL;
894 this control is needed to modify that contains a currently
895 non-existent attribute (but allowable for the object) to run
897 LDAPControl PermitModify = {
898 ADS_PERMIT_MODIFY_OID,
901 LDAPControl *controls[2];
903 controls[0] = &PermitModify;
906 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
907 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
910 /* find the end of the list, marked by NULL or -1 */
911 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
912 /* make sure the end of the list is NULL */
914 ret = ldap_modify_ext_s(ads->ld, utf8_dn,
915 (LDAPMod **) mods, controls, NULL);
917 return ADS_ERROR(ret);
921 * Perform an ldap add
922 * @param ads connection to ads server
923 * @param new_dn DistinguishedName to add
924 * @param mods list of attributes and values for DN
925 * @return status of add
927 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
930 char *utf8_dn = NULL;
932 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
933 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
934 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
937 /* find the end of the list, marked by NULL or -1 */
938 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
939 /* make sure the end of the list is NULL */
942 ret = ldap_add_s(ads->ld, utf8_dn, mods);
944 return ADS_ERROR(ret);
948 * Delete a DistinguishedName
949 * @param ads connection to ads server
950 * @param new_dn DistinguishedName to delete
951 * @return status of delete
953 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
956 char *utf8_dn = NULL;
957 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
958 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
959 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
962 ret = ldap_delete_s(ads->ld, utf8_dn);
963 return ADS_ERROR(ret);
967 * Build an org unit string
968 * if org unit is Computers or blank then assume a container, otherwise
969 * assume a \ separated list of organisational units
970 * @param org_unit Organizational unit
971 * @return org unit string - caller must free
973 char *ads_ou_string(const char *org_unit)
975 if (!org_unit || !*org_unit || strequal(org_unit, "Computers")) {
976 return strdup("cn=Computers");
979 return ads_build_path(org_unit, "\\/", "ou=", 1);
985 add a machine account to the ADS server
987 static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *hostname,
989 const char *org_unit)
991 ADS_STATUS ret, status;
992 char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
996 const char *objectClass[] = {"top", "person", "organizationalPerson",
997 "user", "computer", NULL};
998 const char *servicePrincipalName[5] = {NULL, NULL, NULL, NULL, NULL};
1000 unsigned acct_control;
1002 if (!(ctx = talloc_init("machine_account")))
1003 return ADS_ERROR(LDAP_NO_MEMORY);
1005 ret = ADS_ERROR(LDAP_NO_MEMORY);
1007 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", hostname)))
1009 if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm)))
1011 ou_str = ads_ou_string(org_unit);
1013 DEBUG(1, ("ads_ou_string returned NULL (malloc failure?)\n"));
1016 new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", hostname, ou_str,
1017 ads->config.bind_path);
1018 servicePrincipalName[0] = talloc_asprintf(ctx, "HOST/%s", hostname);
1019 psp = talloc_asprintf(ctx, "HOST/%s.%s",
1022 strlower_m(&psp[5]);
1023 servicePrincipalName[1] = psp;
1024 servicePrincipalName[2] = talloc_asprintf(ctx, "CIFS/%s", hostname);
1025 psp2 = talloc_asprintf(ctx, "CIFS/%s.%s",
1028 strlower_m(&psp2[5]);
1029 servicePrincipalName[3] = psp2;
1035 if (!(samAccountName = talloc_asprintf(ctx, "%s$", hostname)))
1038 acct_control = account_type | UF_DONT_EXPIRE_PASSWD;
1039 #ifndef ENCTYPE_ARCFOUR_HMAC
1040 acct_control |= UF_USE_DES_KEY_ONLY;
1043 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control)))
1046 if (!(mods = ads_init_mods(ctx)))
1049 ads_mod_str(ctx, &mods, "cn", hostname);
1050 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1051 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1052 ads_mod_str(ctx, &mods, "userPrincipalName", host_upn);
1053 ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1054 ads_mod_str(ctx, &mods, "dNSHostName", hostname);
1055 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1056 ads_mod_str(ctx, &mods, "operatingSystem", "Samba");
1057 ads_mod_str(ctx, &mods, "operatingSystemVersion", SAMBA_VERSION_STRING);
1059 ret = ads_gen_add(ads, new_dn, mods);
1061 if (!ADS_ERR_OK(ret))
1064 /* Do not fail if we can't set security descriptor
1065 * it shouldn't be mandatory and probably we just
1066 * don't have enough rights to do it.
1068 status = ads_set_machine_sd(ads, hostname, new_dn);
1070 if (!ADS_ERR_OK(status)) {
1071 DEBUG(0, ("Warning: ads_set_machine_sd: %s\n",
1072 ads_errstr(status)));
1075 talloc_destroy(ctx);
1080 dump a binary result from ldap
1082 static void dump_binary(const char *field, struct berval **values)
1085 for (i=0; values[i]; i++) {
1086 printf("%s: ", field);
1087 for (j=0; j<values[i]->bv_len; j++) {
1088 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1094 static void dump_guid(const char *field, struct berval **values)
1098 for (i=0; values[i]; i++) {
1099 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1100 printf("%s: %s\n", field,
1101 smb_uuid_string_static(smb_uuid_unpack_static(guid)));
1106 dump a sid result from ldap
1108 static void dump_sid(const char *field, struct berval **values)
1111 for (i=0; values[i]; i++) {
1113 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1114 printf("%s: %s\n", field, sid_string_static(&sid));
1119 dump ntSecurityDescriptor
1121 static void dump_sd(const char *filed, struct berval **values)
1126 TALLOC_CTX *ctx = 0;
1128 if (!(ctx = talloc_init("sec_io_desc")))
1132 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1133 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1134 prs_set_offset(&ps,0);
1137 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1139 talloc_destroy(ctx);
1142 if (psd) ads_disp_sd(psd);
1145 talloc_destroy(ctx);
1149 dump a string result from ldap
1151 static void dump_string(const char *field, char **values)
1154 for (i=0; values[i]; i++) {
1155 printf("%s: %s\n", field, values[i]);
1160 dump a field from LDAP on stdout
1164 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1169 void (*handler)(const char *, struct berval **);
1171 {"objectGUID", False, dump_guid},
1172 {"nTSecurityDescriptor", False, dump_sd},
1173 {"dnsRecord", False, dump_binary},
1174 {"objectSid", False, dump_sid},
1175 {"tokenGroups", False, dump_sid},
1180 if (!field) { /* must be end of an entry */
1185 for (i=0; handlers[i].name; i++) {
1186 if (StrCaseCmp(handlers[i].name, field) == 0) {
1187 if (!values) /* first time, indicate string or not */
1188 return handlers[i].string;
1189 handlers[i].handler(field, (struct berval **) values);
1193 if (!handlers[i].name) {
1194 if (!values) /* first time, indicate string conversion */
1196 dump_string(field, (char **)values);
1202 * Dump a result from LDAP on stdout
1203 * used for debugging
1204 * @param ads connection to ads server
1205 * @param res Results to dump
1208 void ads_dump(ADS_STRUCT *ads, void *res)
1210 ads_process_results(ads, res, ads_dump_field, NULL);
1214 * Walk through results, calling a function for each entry found.
1215 * The function receives a field name, a berval * array of values,
1216 * and a data area passed through from the start. The function is
1217 * called once with null for field and values at the end of each
1219 * @param ads connection to ads server
1220 * @param res Results to process
1221 * @param fn Function for processing each result
1222 * @param data_area user-defined area to pass to function
1224 void ads_process_results(ADS_STRUCT *ads, void *res,
1225 BOOL(*fn)(char *, void **, void *),
1231 if (!(ctx = talloc_init("ads_process_results")))
1234 for (msg = ads_first_entry(ads, res); msg;
1235 msg = ads_next_entry(ads, msg)) {
1239 for (utf8_field=ldap_first_attribute(ads->ld,
1240 (LDAPMessage *)msg,&b);
1242 utf8_field=ldap_next_attribute(ads->ld,
1243 (LDAPMessage *)msg,b)) {
1244 struct berval **ber_vals;
1245 char **str_vals, **utf8_vals;
1249 pull_utf8_talloc(ctx, &field, utf8_field);
1250 string = fn(field, NULL, data_area);
1253 utf8_vals = ldap_get_values(ads->ld,
1254 (LDAPMessage *)msg, field);
1255 str_vals = ads_pull_strvals(ctx,
1256 (const char **) utf8_vals);
1257 fn(field, (void **) str_vals, data_area);
1258 ldap_value_free(utf8_vals);
1260 ber_vals = ldap_get_values_len(ads->ld,
1261 (LDAPMessage *)msg, field);
1262 fn(field, (void **) ber_vals, data_area);
1264 ldap_value_free_len(ber_vals);
1266 ldap_memfree(utf8_field);
1269 talloc_destroy_pool(ctx);
1270 fn(NULL, NULL, data_area); /* completed an entry */
1273 talloc_destroy(ctx);
1277 * count how many replies are in a LDAPMessage
1278 * @param ads connection to ads server
1279 * @param res Results to count
1280 * @return number of replies
1282 int ads_count_replies(ADS_STRUCT *ads, void *res)
1284 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1288 * Join a machine to a realm
1289 * Creates the machine account and sets the machine password
1290 * @param ads connection to ads server
1291 * @param hostname name of host to add
1292 * @param org_unit Organizational unit to place machine in
1293 * @return status of join
1295 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *hostname,
1296 uint32 account_type, const char *org_unit)
1302 /* hostname must be lowercase */
1303 host = strdup(hostname);
1306 status = ads_find_machine_acct(ads, (void **)&res, host);
1307 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1308 DEBUG(0, ("Host account for %s already exists - deleting old account\n", host));
1309 status = ads_leave_realm(ads, host);
1310 if (!ADS_ERR_OK(status)) {
1311 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
1312 host, ads->config.realm));
1317 status = ads_add_machine_acct(ads, host, account_type, org_unit);
1318 if (!ADS_ERR_OK(status)) {
1319 DEBUG(0, ("ads_add_machine_acct: %s\n", ads_errstr(status)));
1323 status = ads_find_machine_acct(ads, (void **)&res, host);
1324 if (!ADS_ERR_OK(status)) {
1325 DEBUG(0, ("Host account test failed\n"));
1335 * Delete a machine from the realm
1336 * @param ads connection to ads server
1337 * @param hostname Machine to remove
1338 * @return status of delete
1340 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
1344 char *hostnameDN, *host;
1347 /* hostname must be lowercase */
1348 host = strdup(hostname);
1351 status = ads_find_machine_acct(ads, &res, host);
1352 if (!ADS_ERR_OK(status)) {
1353 DEBUG(0, ("Host account for %s does not exist.\n", host));
1357 msg = ads_first_entry(ads, res);
1359 return ADS_ERROR_SYSTEM(ENOENT);
1362 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
1363 rc = ldap_delete_s(ads->ld, hostnameDN);
1364 ads_memfree(ads, hostnameDN);
1365 if (rc != LDAP_SUCCESS) {
1366 return ADS_ERROR(rc);
1369 status = ads_find_machine_acct(ads, &res, host);
1370 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1371 DEBUG(0, ("Failed to remove host account.\n"));
1381 * add machine account to existing security descriptor
1382 * @param ads connection to ads server
1383 * @param hostname machine to add
1384 * @param dn DN of security descriptor
1387 ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
1389 const char *attrs[] = {"nTSecurityDescriptor", "objectSid", 0};
1392 struct berval bval = {0, NULL};
1394 char *escaped_hostname = escape_ldap_string_alloc(hostname);
1396 LDAPMessage *res = 0;
1397 LDAPMessage *msg = 0;
1398 ADS_MODLIST mods = 0;
1403 SEC_DESC *psd = NULL;
1404 TALLOC_CTX *ctx = NULL;
1406 /* Avoid segmentation fault in prs_mem_free if
1407 * we have to bail out before prs_init */
1408 ps_wire.is_dynamic = False;
1410 if (!ads) return ADS_ERROR(LDAP_SERVER_DOWN);
1412 ret = ADS_ERROR(LDAP_SUCCESS);
1414 if (!escaped_hostname) {
1415 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1418 if (asprintf(&expr, "(samAccountName=%s$)", escaped_hostname) == -1) {
1419 DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n"));
1420 SAFE_FREE(escaped_hostname);
1421 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1424 SAFE_FREE(escaped_hostname);
1426 ret = ads_search(ads, (void *) &res, expr, attrs);
1428 if (!ADS_ERR_OK(ret)) return ret;
1430 if ( !(msg = ads_first_entry(ads, res) )) {
1431 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1432 goto ads_set_sd_error;
1435 if (!ads_pull_sid(ads, msg, attrs[1], &sid)) {
1436 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1437 goto ads_set_sd_error;
1440 if (!(ctx = talloc_init("sec_io_desc"))) {
1441 ret = ADS_ERROR(LDAP_NO_MEMORY);
1442 goto ads_set_sd_error;
1445 if (!ads_pull_sd(ads, ctx, msg, attrs[0], &psd)) {
1446 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1447 goto ads_set_sd_error;
1450 status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size);
1452 if (!NT_STATUS_IS_OK(status)) {
1453 ret = ADS_ERROR_NT(status);
1454 goto ads_set_sd_error;
1457 if (!prs_init(&ps_wire, sd_size, ctx, MARSHALL)) {
1458 ret = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1461 if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1)) {
1462 ret = ADS_ERROR(LDAP_NO_MEMORY);
1463 goto ads_set_sd_error;
1467 file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size);
1469 if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY);
1471 bval.bv_len = prs_offset(&ps_wire);
1472 bval.bv_val = talloc(ctx, bval.bv_len);
1474 ret = ADS_ERROR(LDAP_NO_MEMORY);
1475 goto ads_set_sd_error;
1478 prs_set_offset(&ps_wire, 0);
1480 if (!prs_copy_data_out(bval.bv_val, &ps_wire, bval.bv_len)) {
1481 ret = ADS_ERROR(LDAP_NO_MEMORY);
1482 goto ads_set_sd_error;
1485 ret = ads_mod_ber(ctx, &mods, attrs[0], &bval);
1486 if (ADS_ERR_OK(ret)) {
1487 ret = ads_gen_mod(ads, dn, mods);
1491 ads_msgfree(ads, res);
1492 prs_mem_free(&ps_wire);
1493 talloc_destroy(ctx);
1498 * pull the first entry from a ADS result
1499 * @param ads connection to ads server
1500 * @param res Results of search
1501 * @return first entry from result
1503 void *ads_first_entry(ADS_STRUCT *ads, void *res)
1505 return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
1509 * pull the next entry from a ADS result
1510 * @param ads connection to ads server
1511 * @param res Results of search
1512 * @return next entry from result
1514 void *ads_next_entry(ADS_STRUCT *ads, void *res)
1516 return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
1520 * pull a single string from a ADS result
1521 * @param ads connection to ads server
1522 * @param mem_ctx TALLOC_CTX to use for allocating result string
1523 * @param msg Results of search
1524 * @param field Attribute to retrieve
1525 * @return Result string in talloc context
1527 char *ads_pull_string(ADS_STRUCT *ads,
1528 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1535 values = ldap_get_values(ads->ld, msg, field);
1540 rc = pull_utf8_talloc(mem_ctx, &ux_string,
1542 if (rc != (size_t)-1)
1546 ldap_value_free(values);
1551 * pull an array of strings from a ADS result
1552 * @param ads connection to ads server
1553 * @param mem_ctx TALLOC_CTX to use for allocating result string
1554 * @param msg Results of search
1555 * @param field Attribute to retrieve
1556 * @return Result strings in talloc context
1558 char **ads_pull_strings(ADS_STRUCT *ads,
1559 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1565 values = ldap_get_values(ads->ld, msg, field);
1569 for (i=0;values[i];i++)
1573 ret = talloc(mem_ctx, sizeof(char *) * (n+1));
1575 ldap_value_free(values);
1580 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
1581 ldap_value_free(values);
1587 ldap_value_free(values);
1593 * pull a single uint32 from a ADS result
1594 * @param ads connection to ads server
1595 * @param msg Results of search
1596 * @param field Attribute to retrieve
1597 * @param v Pointer to int to store result
1598 * @return boolean inidicating success
1600 BOOL ads_pull_uint32(ADS_STRUCT *ads,
1601 void *msg, const char *field, uint32 *v)
1605 values = ldap_get_values(ads->ld, msg, field);
1609 ldap_value_free(values);
1613 *v = atoi(values[0]);
1614 ldap_value_free(values);
1619 * pull a single objectGUID from an ADS result
1620 * @param ads connection to ADS server
1621 * @param msg results of search
1622 * @param guid 37-byte area to receive text guid
1623 * @return boolean indicating success
1625 BOOL ads_pull_guid(ADS_STRUCT *ads,
1626 void *msg, struct uuid *guid)
1629 UUID_FLAT flat_guid;
1631 values = ldap_get_values(ads->ld, msg, "objectGUID");
1636 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
1637 smb_uuid_unpack(flat_guid, guid);
1638 ldap_value_free(values);
1641 ldap_value_free(values);
1648 * pull a single DOM_SID from a ADS result
1649 * @param ads connection to ads server
1650 * @param msg Results of search
1651 * @param field Attribute to retrieve
1652 * @param sid Pointer to sid to store result
1653 * @return boolean inidicating success
1655 BOOL ads_pull_sid(ADS_STRUCT *ads,
1656 void *msg, const char *field, DOM_SID *sid)
1658 struct berval **values;
1661 values = ldap_get_values_len(ads->ld, msg, field);
1667 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
1669 ldap_value_free_len(values);
1674 * pull an array of DOM_SIDs from a ADS result
1675 * @param ads connection to ads server
1676 * @param mem_ctx TALLOC_CTX for allocating sid array
1677 * @param msg Results of search
1678 * @param field Attribute to retrieve
1679 * @param sids pointer to sid array to allocate
1680 * @return the count of SIDs pulled
1682 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1683 void *msg, const char *field, DOM_SID **sids)
1685 struct berval **values;
1689 values = ldap_get_values_len(ads->ld, msg, field);
1694 for (i=0; values[i]; i++)
1697 (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * i);
1699 ldap_value_free_len(values);
1704 for (i=0; values[i]; i++) {
1705 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
1708 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
1713 ldap_value_free_len(values);
1718 * pull a SEC_DESC from a ADS result
1719 * @param ads connection to ads server
1720 * @param mem_ctx TALLOC_CTX for allocating sid array
1721 * @param msg Results of search
1722 * @param field Attribute to retrieve
1723 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
1724 * @return boolean inidicating success
1726 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1727 void *msg, const char *field, SEC_DESC **sd)
1729 struct berval **values;
1733 values = ldap_get_values_len(ads->ld, msg, field);
1735 if (!values) return False;
1738 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
1739 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1740 prs_set_offset(&ps,0);
1742 ret = sec_io_desc("sd", sd, &ps, 1);
1745 ldap_value_free_len(values);
1750 * in order to support usernames longer than 21 characters we need to
1751 * use both the sAMAccountName and the userPrincipalName attributes
1752 * It seems that not all users have the userPrincipalName attribute set
1754 * @param ads connection to ads server
1755 * @param mem_ctx TALLOC_CTX for allocating sid array
1756 * @param msg Results of search
1757 * @return the username
1759 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
1763 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
1764 if (ret && (p = strchr(ret, '@'))) {
1768 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
1773 * find the update serial number - this is the core of the ldap cache
1774 * @param ads connection to ads server
1775 * @param ads connection to ADS server
1776 * @param usn Pointer to retrieved update serial number
1777 * @return status of search
1779 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
1781 const char *attrs[] = {"highestCommittedUSN", NULL};
1785 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1786 if (!ADS_ERR_OK(status))
1789 if (ads_count_replies(ads, res) != 1) {
1790 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1793 ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
1794 ads_msgfree(ads, res);
1798 /* parse a ADS timestring - typical string is
1799 '20020917091222.0Z0' which means 09:12.22 17th September
1801 static time_t ads_parse_time(const char *str)
1807 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
1808 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
1809 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
1820 * Find the servers name and realm - this can be done before authentication
1821 * The ldapServiceName field on w2k looks like this:
1822 * vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
1823 * @param ads connection to ads server
1824 * @return status of search
1826 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
1828 const char *attrs[] = {"ldapServiceName", "currentTime", NULL};
1836 if (!(ctx = talloc_init("ads_server_info"))) {
1837 return ADS_ERROR(LDAP_NO_MEMORY);
1840 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1841 if (!ADS_ERR_OK(status)) return status;
1843 value = ads_pull_string(ads, ctx, res, "ldapServiceName");
1845 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1848 timestr = ads_pull_string(ads, ctx, res, "currentTime");
1850 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1855 p = strchr(value, ':');
1857 talloc_destroy(ctx);
1858 DEBUG(1, ("ads_server_info: returned ldap server name did not contain a ':' so was deemed invalid\n"));
1859 return ADS_ERROR(LDAP_DECODING_ERROR);
1862 SAFE_FREE(ads->config.ldap_server_name);
1864 ads->config.ldap_server_name = strdup(p+1);
1865 p = strchr(ads->config.ldap_server_name, '$');
1866 if (!p || p[1] != '@') {
1867 talloc_destroy(ctx);
1868 DEBUG(1, ("ads_server_info: returned ldap server name (%s) does not contain '$@' so was deemed invalid\n", ads->config.ldap_server_name));
1869 SAFE_FREE(ads->config.ldap_server_name);
1870 return ADS_ERROR(LDAP_DECODING_ERROR);
1875 SAFE_FREE(ads->config.realm);
1876 SAFE_FREE(ads->config.bind_path);
1878 ads->config.realm = strdup(p+2);
1879 ads->config.bind_path = ads_build_dn(ads->config.realm);
1881 DEBUG(3,("got ldap server name %s@%s, using bind path: %s\n",
1882 ads->config.ldap_server_name, ads->config.realm,
1883 ads->config.bind_path));
1885 ads->config.current_time = ads_parse_time(timestr);
1887 if (ads->config.current_time != 0) {
1888 ads->auth.time_offset = ads->config.current_time - time(NULL);
1889 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
1892 talloc_destroy(ctx);
1898 * find the domain sid for our domain
1899 * @param ads connection to ads server
1900 * @param sid Pointer to domain sid
1901 * @return status of search
1903 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
1905 const char *attrs[] = {"objectSid", NULL};
1909 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
1911 if (!ADS_ERR_OK(rc)) return rc;
1912 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
1913 return ADS_ERROR_SYSTEM(ENOENT);
1915 ads_msgfree(ads, res);
1920 /* this is rather complex - we need to find the allternate (netbios) name
1921 for the domain, but there isn't a simple query to do this. Instead
1922 we look for the principle names on the DCs account and find one that has
1923 the right form, then extract the netbios name of the domain from that
1925 NOTE! better method is this:
1927 bin/net -Uadministrator%XXXXX ads search '(&(objectclass=crossref)(dnsroot=VNET3.HOME.SAMBA.ORG))' nETBIOSName
1929 but you need to force the bind path to match the configurationNamingContext from the rootDSE
1932 ADS_STATUS ads_workgroup_name(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **workgroup)
1941 const char *attrs[] = {"servicePrincipalName", NULL};
1943 (*workgroup) = NULL;
1945 asprintf(&expr, "(&(objectclass=computer)(dnshostname=%s.%s))",
1946 ads->config.ldap_server_name, ads->config.realm);
1947 rc = ads_search(ads, &res, expr, attrs);
1950 if (!ADS_ERR_OK(rc)) {
1954 principles = ads_pull_strings(ads, mem_ctx, res, "servicePrincipalName");
1956 ads_msgfree(ads, res);
1959 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1962 asprintf(&prefix, "HOST/%s.%s/",
1963 ads->config.ldap_server_name,
1966 prefix_length = strlen(prefix);
1968 for (i=0;principles[i]; i++) {
1969 if (strnequal(principles[i], prefix, prefix_length) &&
1970 !strequal(ads->config.realm, principles[i]+prefix_length) &&
1971 !strchr(principles[i]+prefix_length, '.')) {
1972 /* found an alternate (short) name for the domain. */
1973 DEBUG(3,("Found alternate name '%s' for realm '%s'\n",
1974 principles[i]+prefix_length,
1975 ads->config.realm));
1976 (*workgroup) = talloc_strdup(mem_ctx, principles[i]+prefix_length);
1983 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);