2 Unix SMB/CIFS implementation.
3 ads (active directory) utility library
4 Copyright (C) Andrew Tridgell 2001
5 Copyright (C) Remus Koos 2001
6 Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 * @brief basic ldap client-side routines for ads server communications
31 * The routines contained here should do the necessary ldap calls for
34 * Important note: attribute names passed into ads_ routines must
35 * already be in UTF-8 format. We do not convert them because in almost
36 * all cases, they are just ascii (which is represented with the same
37 * codepoints in UTF-8). This may have to change at some point
40 static SIG_ATOMIC_T gotalarm;
42 /***************************************************************
43 Signal function to tell us we timed out.
44 ****************************************************************/
46 static void gotalarm_sig(void)
51 LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
57 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
59 /* End setup timeout. */
61 ldp = ldap_open(server, port);
63 /* Teardown timeout. */
64 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
70 static int ldap_search_with_timeout(LDAP *ld,
71 LDAP_CONST char *base,
73 LDAP_CONST char *filter,
81 struct timeval timeout;
84 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
85 timeout.tv_sec = lp_ldap_timeout();
88 /* Setup alarm timeout.... Do we need both of these ? JRA. */
90 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
91 alarm(lp_ldap_timeout());
92 /* End setup timeout. */
94 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
95 attrsonly, sctrls, cctrls, &timeout,
98 /* Teardown timeout. */
99 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
103 return LDAP_TIMELIMIT_EXCEEDED;
109 try a connection to a given ldap server, returning True and setting the servers IP
110 in the ads struct if successful
112 TODO : add a negative connection cache in here leveraged off of the one
113 found in the rpc code. --jerry
115 BOOL ads_try_connect(ADS_STRUCT *ads, const char *server, unsigned port)
119 if (!server || !*server) {
123 DEBUG(5,("ads_try_connect: trying ldap server '%s' port %u\n", server, port));
125 /* this copes with inet_ntoa brokenness */
126 srv = SMB_STRDUP(server);
128 ads->ld = ldap_open_with_timeout(srv, port, lp_ldap_timeout());
133 ads->ldap_port = port;
134 ads->ldap_ip = *interpret_addr2(srv);
141 try a connection to a given ldap server, based on URL, returning True if successful
143 static BOOL ads_try_connect_uri(ADS_STRUCT *ads)
145 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
146 DEBUG(5,("ads_try_connect: trying ldap server at URI '%s'\n",
147 ads->server.ldap_uri));
150 if (ldap_initialize((LDAP**)&(ads->ld), ads->server.ldap_uri) == LDAP_SUCCESS) {
153 DEBUG(0, ("ldap_initialize: %s\n", strerror(errno)));
157 DEBUG(1, ("no URL support in LDAP libs!\n"));
163 /**********************************************************************
164 Try to find an AD dc using our internal name resolution routines
165 Try the realm first and then then workgroup name if netbios is not
167 **********************************************************************/
169 static BOOL ads_find_dc(ADS_STRUCT *ads)
173 struct ip_service *ip_list;
175 BOOL got_realm = False;
176 BOOL use_own_domain = False;
178 /* if the realm and workgroup are both empty, assume they are ours */
181 c_realm = ads->server.realm;
183 if ( !c_realm || !*c_realm ) {
184 /* special case where no realm and no workgroup means our own */
185 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
186 use_own_domain = True;
187 c_realm = lp_realm();
191 if (c_realm && *c_realm)
195 /* we need to try once with the realm name and fallback to the
196 netbios domain name if we fail (if netbios has not been disabled */
198 if ( !got_realm && !lp_disable_netbios() ) {
199 c_realm = ads->server.workgroup;
200 if (!c_realm || !*c_realm) {
201 if ( use_own_domain )
202 c_realm = lp_workgroup();
205 if ( !c_realm || !*c_realm ) {
206 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
211 pstrcpy( realm, c_realm );
213 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
214 (got_realm ? "realm" : "domain"), realm));
216 if ( !get_sorted_dc_list(realm, &ip_list, &count, got_realm) ) {
217 /* fall back to netbios if we can */
218 if ( got_realm && !lp_disable_netbios() ) {
226 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
227 for ( i=0; i<count; i++ ) {
228 /* since this is an ads conection request, default to LDAP_PORT is not set */
229 int port = (ip_list[i].port!=PORT_NONE) ? ip_list[i].port : LDAP_PORT;
232 fstrcpy( server, inet_ntoa(ip_list[i].ip) );
234 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
237 if ( ads_try_connect(ads, server, port) ) {
242 /* keep track of failures */
243 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
253 * Connect to the LDAP server
254 * @param ads Pointer to an existing ADS_STRUCT
255 * @return status of connection
257 ADS_STATUS ads_connect(ADS_STRUCT *ads)
259 int version = LDAP_VERSION3;
262 ads->last_attempt = time(NULL);
265 /* try with a URL based server */
267 if (ads->server.ldap_uri &&
268 ads_try_connect_uri(ads)) {
272 /* try with a user specified server */
273 if (ads->server.ldap_server &&
274 ads_try_connect(ads, ads->server.ldap_server, LDAP_PORT)) {
278 if (ads_find_dc(ads)) {
282 return ADS_ERROR_SYSTEM(errno?errno:ENOENT);
285 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
287 status = ads_server_info(ads);
288 if (!ADS_ERR_OK(status)) {
289 DEBUG(1,("Failed to get ldap server info\n"));
293 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
295 status = ADS_ERROR(smb_ldap_start_tls(ads->ld, version));
296 if (!ADS_ERR_OK(status)) {
300 if (!ads->auth.user_name) {
301 /* have to use the userPrincipalName value here and
302 not servicePrincipalName; found by Guenther Deschner @ Sernet */
304 asprintf(&ads->auth.user_name, "host/%s", global_myname() );
307 if (!ads->auth.realm) {
308 ads->auth.realm = SMB_STRDUP(ads->config.realm);
311 if (!ads->auth.kdc_server) {
312 ads->auth.kdc_server = SMB_STRDUP(inet_ntoa(ads->ldap_ip));
316 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
317 to MIT kerberos to work (tridge) */
320 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
321 setenv(env, ads->auth.kdc_server, 1);
326 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
330 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
331 return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
334 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
335 return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
338 return ads_sasl_bind(ads);
342 Duplicate a struct berval into talloc'ed memory
344 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
346 struct berval *value;
348 if (!in_val) return NULL;
350 value = TALLOC_ZERO_P(ctx, struct berval);
353 if (in_val->bv_len == 0) return value;
355 value->bv_len = in_val->bv_len;
356 value->bv_val = TALLOC_MEMDUP(ctx, in_val->bv_val, in_val->bv_len);
361 Make a values list out of an array of (struct berval *)
363 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
364 const struct berval **in_vals)
366 struct berval **values;
369 if (!in_vals) return NULL;
370 for (i=0; in_vals[i]; i++)
372 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
373 if (!values) return NULL;
375 for (i=0; in_vals[i]; i++) {
376 values[i] = dup_berval(ctx, in_vals[i]);
382 UTF8-encode a values list out of an array of (char *)
384 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
389 if (!in_vals) return NULL;
390 for (i=0; in_vals[i]; i++)
392 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
393 if (!values) return NULL;
395 for (i=0; in_vals[i]; i++) {
396 push_utf8_talloc(ctx, &values[i], in_vals[i]);
402 Pull a (char *) array out of a UTF8-encoded values list
404 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
409 if (!in_vals) return NULL;
410 for (i=0; in_vals[i]; i++)
412 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
413 if (!values) return NULL;
415 for (i=0; in_vals[i]; i++) {
416 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
422 * Do a search with paged results. cookie must be null on the first
423 * call, and then returned on each subsequent call. It will be null
424 * again when the entire search is complete
425 * @param ads connection to ads server
426 * @param bind_path Base dn for the search
427 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
428 * @param expr Search expression - specified in local charset
429 * @param attrs Attributes to retrieve - specified in utf8 or ascii
430 * @param res ** which will contain results - free res* with ads_msgfree()
431 * @param count Number of entries retrieved on this page
432 * @param cookie The paged results cookie to be returned on subsequent calls
433 * @return status of search
435 ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
436 int scope, const char *expr,
437 const char **attrs, void **res,
438 int *count, void **cookie)
441 char *utf8_expr, *utf8_path, **search_attrs;
442 LDAPControl PagedResults, NoReferrals, *controls[3], **rcontrols;
443 BerElement *cookie_be = NULL;
444 struct berval *cookie_bv= NULL;
449 if (!(ctx = talloc_init("ads_do_paged_search")))
450 return ADS_ERROR(LDAP_NO_MEMORY);
452 /* 0 means the conversion worked but the result was empty
453 so we only fail if it's -1. In any case, it always
454 at least nulls out the dest */
455 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
456 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
461 if (!attrs || !(*attrs))
464 /* This would be the utf8-encoded version...*/
465 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
466 if (!(str_list_copy(&search_attrs, attrs))) {
473 /* Paged results only available on ldap v3 or later */
474 ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
475 if (version < LDAP_VERSION3) {
476 rc = LDAP_NOT_SUPPORTED;
480 cookie_be = ber_alloc_t(LBER_USE_DER);
481 if (cookie && *cookie) {
482 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
483 ber_bvfree(*cookie); /* don't need it from last time */
486 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
488 ber_flatten(cookie_be, &cookie_bv);
489 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
490 PagedResults.ldctl_iscritical = (char) 1;
491 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
492 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
494 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
495 NoReferrals.ldctl_iscritical = (char) 0;
496 NoReferrals.ldctl_value.bv_len = 0;
497 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
500 controls[0] = &NoReferrals;
501 controls[1] = &PagedResults;
504 /* we need to disable referrals as the openldap libs don't
505 handle them and paged results at the same time. Using them
506 together results in the result record containing the server
507 page control being removed from the result list (tridge/jmcd)
509 leaving this in despite the control that says don't generate
510 referrals, in case the server doesn't support it (jmcd)
512 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
514 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
515 search_attrs, 0, controls,
517 (LDAPMessage **)res);
519 ber_free(cookie_be, 1);
520 ber_bvfree(cookie_bv);
523 DEBUG(3,("ads_do_paged_search: ldap_search_with_timeout(%s) -> %s\n", expr,
524 ldap_err2string(rc)));
528 rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
529 NULL, &rcontrols, 0);
535 for (i=0; rcontrols[i]; i++) {
536 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
537 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
538 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
540 /* the berval is the cookie, but must be freed when
542 if (cookie_bv->bv_len) /* still more to do */
543 *cookie=ber_bvdup(cookie_bv);
546 ber_bvfree(cookie_bv);
547 ber_free(cookie_be, 1);
551 ldap_controls_free(rcontrols);
555 /* if/when we decide to utf8-encode attrs, take out this next line */
556 str_list_free(&search_attrs);
558 return ADS_ERROR(rc);
563 * Get all results for a search. This uses ads_do_paged_search() to return
564 * all entries in a large search.
565 * @param ads connection to ads server
566 * @param bind_path Base dn for the search
567 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
568 * @param expr Search expression
569 * @param attrs Attributes to retrieve
570 * @param res ** which will contain results - free res* with ads_msgfree()
571 * @return status of search
573 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
574 int scope, const char *expr,
575 const char **attrs, void **res)
582 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, res,
585 if (!ADS_ERR_OK(status))
588 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
592 LDAPMessage *msg, *next;
594 status2 = ads_do_paged_search(ads, bind_path, scope, expr,
595 attrs, &res2, &count, &cookie);
597 if (!ADS_ERR_OK(status2)) break;
599 /* this relies on the way that ldap_add_result_entry() works internally. I hope
600 that this works on all ldap libs, but I have only tested with openldap */
601 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
602 next = ads_next_entry(ads, msg);
603 ldap_add_result_entry((LDAPMessage **)res, msg);
605 /* note that we do not free res2, as the memory is now
606 part of the main returned list */
609 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
610 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
617 * Run a function on all results for a search. Uses ads_do_paged_search() and
618 * runs the function as each page is returned, using ads_process_results()
619 * @param ads connection to ads server
620 * @param bind_path Base dn for the search
621 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
622 * @param expr Search expression - specified in local charset
623 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
624 * @param fn Function which takes attr name, values list, and data_area
625 * @param data_area Pointer which is passed to function on each call
626 * @return status of search
628 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
629 int scope, const char *expr, const char **attrs,
630 BOOL(*fn)(char *, void **, void *),
638 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
641 if (!ADS_ERR_OK(status)) return status;
643 ads_process_results(ads, res, fn, data_area);
644 ads_msgfree(ads, res);
647 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
648 &res, &count, &cookie);
650 if (!ADS_ERR_OK(status)) break;
652 ads_process_results(ads, res, fn, data_area);
653 ads_msgfree(ads, res);
660 * Do a search with a timeout.
661 * @param ads connection to ads server
662 * @param bind_path Base dn for the search
663 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
664 * @param expr Search expression
665 * @param attrs Attributes to retrieve
666 * @param res ** which will contain results - free res* with ads_msgfree()
667 * @return status of search
669 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
671 const char **attrs, void **res)
674 char *utf8_expr, *utf8_path, **search_attrs = NULL;
678 if (!(ctx = talloc_init("ads_do_search"))) {
679 DEBUG(1,("ads_do_search: talloc_init() failed!"));
680 return ADS_ERROR(LDAP_NO_MEMORY);
683 /* 0 means the conversion worked but the result was empty
684 so we only fail if it's negative. In any case, it always
685 at least nulls out the dest */
686 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
687 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
688 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
693 if (!attrs || !(*attrs))
696 /* This would be the utf8-encoded version...*/
697 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
698 if (!(str_list_copy(&search_attrs, attrs)))
700 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
706 /* see the note in ads_do_paged_search - we *must* disable referrals */
707 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
709 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
710 search_attrs, 0, NULL, NULL,
712 (LDAPMessage **)res);
714 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
715 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
721 /* if/when we decide to utf8-encode attrs, take out this next line */
722 str_list_free(&search_attrs);
723 return ADS_ERROR(rc);
726 * Do a general ADS search
727 * @param ads connection to ads server
728 * @param res ** which will contain results - free res* with ads_msgfree()
729 * @param expr Search expression
730 * @param attrs Attributes to retrieve
731 * @return status of search
733 ADS_STATUS ads_search(ADS_STRUCT *ads, void **res,
737 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
742 * Do a search on a specific DistinguishedName
743 * @param ads connection to ads server
744 * @param res ** which will contain results - free res* with ads_msgfree()
745 * @param dn DistinguishName to search
746 * @param attrs Attributes to retrieve
747 * @return status of search
749 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, void **res,
753 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res);
757 * Free up memory from a ads_search
758 * @param ads connection to ads server
759 * @param msg Search results to free
761 void ads_msgfree(ADS_STRUCT *ads, void *msg)
768 * Free up memory from various ads requests
769 * @param ads connection to ads server
770 * @param mem Area to free
772 void ads_memfree(ADS_STRUCT *ads, void *mem)
778 * Get a dn from search results
779 * @param ads connection to ads server
780 * @param msg Search result
783 char *ads_get_dn(ADS_STRUCT *ads, void *msg)
785 char *utf8_dn, *unix_dn;
787 utf8_dn = ldap_get_dn(ads->ld, msg);
790 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
794 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
795 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
799 ldap_memfree(utf8_dn);
804 * Find a machine account given a hostname
805 * @param ads connection to ads server
806 * @param res ** which will contain results - free res* with ads_msgfree()
807 * @param host Hostname to search for
808 * @return status of search
810 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *machine)
814 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
818 /* the easiest way to find a machine account anywhere in the tree
819 is to look for hostname$ */
820 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
821 DEBUG(1, ("asprintf failed!\n"));
822 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
825 status = ads_search(ads, res, expr, attrs);
831 * Initialize a list of mods to be used in a modify request
832 * @param ctx An initialized TALLOC_CTX
833 * @return allocated ADS_MODLIST
835 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
837 #define ADS_MODLIST_ALLOC_SIZE 10
840 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
841 /* -1 is safety to make sure we don't go over the end.
842 need to reset it to NULL before doing ldap modify */
843 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
845 return (ADS_MODLIST)mods;
850 add an attribute to the list, with values list already constructed
852 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
853 int mod_op, const char *name,
857 LDAPMod **modlist = (LDAPMod **) *mods;
858 struct berval **ber_values = NULL;
859 char **char_values = NULL;
862 mod_op = LDAP_MOD_DELETE;
864 if (mod_op & LDAP_MOD_BVALUES)
865 ber_values = ads_dup_values(ctx,
866 (const struct berval **)invals);
868 char_values = ads_push_strvals(ctx,
869 (const char **) invals);
872 /* find the first empty slot */
873 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
875 if (modlist[curmod] == (LDAPMod *) -1) {
876 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
877 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
878 return ADS_ERROR(LDAP_NO_MEMORY);
879 memset(&modlist[curmod], 0,
880 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
881 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
882 *mods = (ADS_MODLIST)modlist;
885 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
886 return ADS_ERROR(LDAP_NO_MEMORY);
887 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
888 if (mod_op & LDAP_MOD_BVALUES) {
889 modlist[curmod]->mod_bvalues = ber_values;
890 } else if (mod_op & LDAP_MOD_DELETE) {
891 modlist[curmod]->mod_values = NULL;
893 modlist[curmod]->mod_values = char_values;
896 modlist[curmod]->mod_op = mod_op;
897 return ADS_ERROR(LDAP_SUCCESS);
901 * Add a single string value to a mod list
902 * @param ctx An initialized TALLOC_CTX
903 * @param mods An initialized ADS_MODLIST
904 * @param name The attribute name to add
905 * @param val The value to add - NULL means DELETE
906 * @return ADS STATUS indicating success of add
908 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
909 const char *name, const char *val)
911 const char *values[2];
917 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
918 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name,
919 (const void **) values);
923 * Add an array of string values to a mod list
924 * @param ctx An initialized TALLOC_CTX
925 * @param mods An initialized ADS_MODLIST
926 * @param name The attribute name to add
927 * @param vals The array of string values to add - NULL means DELETE
928 * @return ADS STATUS indicating success of add
930 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
931 const char *name, const char **vals)
934 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
935 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
936 name, (const void **) vals);
940 * Add a single ber-encoded value to a mod list
941 * @param ctx An initialized TALLOC_CTX
942 * @param mods An initialized ADS_MODLIST
943 * @param name The attribute name to add
944 * @param val The value to add - NULL means DELETE
945 * @return ADS STATUS indicating success of add
947 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
948 const char *name, const struct berval *val)
950 const struct berval *values[2];
955 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
956 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
957 name, (const void **) values);
961 * Perform an ldap modify
962 * @param ads connection to ads server
963 * @param mod_dn DistinguishedName to modify
964 * @param mods list of modifications to perform
965 * @return status of modify
967 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
970 char *utf8_dn = NULL;
972 this control is needed to modify that contains a currently
973 non-existent attribute (but allowable for the object) to run
975 LDAPControl PermitModify = {
976 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
979 LDAPControl *controls[2];
981 controls[0] = &PermitModify;
984 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
985 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
988 /* find the end of the list, marked by NULL or -1 */
989 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
990 /* make sure the end of the list is NULL */
992 ret = ldap_modify_ext_s(ads->ld, utf8_dn,
993 (LDAPMod **) mods, controls, NULL);
995 return ADS_ERROR(ret);
999 * Perform an ldap add
1000 * @param ads connection to ads server
1001 * @param new_dn DistinguishedName to add
1002 * @param mods list of attributes and values for DN
1003 * @return status of add
1005 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1008 char *utf8_dn = NULL;
1010 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1011 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1012 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1015 /* find the end of the list, marked by NULL or -1 */
1016 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1017 /* make sure the end of the list is NULL */
1020 ret = ldap_add_s(ads->ld, utf8_dn, (LDAPMod**)mods);
1022 return ADS_ERROR(ret);
1026 * Delete a DistinguishedName
1027 * @param ads connection to ads server
1028 * @param new_dn DistinguishedName to delete
1029 * @return status of delete
1031 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1034 char *utf8_dn = NULL;
1035 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1036 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1037 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1040 ret = ldap_delete_s(ads->ld, utf8_dn);
1041 return ADS_ERROR(ret);
1045 * Build an org unit string
1046 * if org unit is Computers or blank then assume a container, otherwise
1047 * assume a \ separated list of organisational units
1048 * @param ads connection to ads server
1049 * @param org_unit Organizational unit
1050 * @return org unit string - caller must free
1052 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1056 if (!org_unit || !*org_unit) {
1058 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1060 /* samba4 might not yet respond to a wellknownobject-query */
1061 return ret ? ret : SMB_STRDUP("cn=Computers");
1064 if (strequal(org_unit, "Computers")) {
1065 return SMB_STRDUP("cn=Computers");
1068 return ads_build_path(org_unit, "\\/", "ou=", 1);
1072 * Get a org unit string for a well-known GUID
1073 * @param ads connection to ads server
1074 * @param wknguid Well known GUID
1075 * @return org unit string - caller must free
1077 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1081 char *base, *wkn_dn, *ret, **wkn_dn_exp, **bind_dn_exp;
1082 const char *attrs[] = {"distinguishedName", NULL};
1083 int new_ln, wkn_ln, bind_ln, i;
1085 if (wknguid == NULL) {
1089 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1090 DEBUG(1, ("asprintf failed!\n"));
1094 status = ads_search_dn(ads, &res, base, attrs);
1095 if (!ADS_ERR_OK(status)) {
1096 DEBUG(1,("Failed while searching for: %s\n", base));
1101 if (ads_count_replies(ads, res) != 1) {
1105 /* substitute the bind-path from the well-known-guid-search result */
1106 wkn_dn = ads_get_dn(ads, res);
1107 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1108 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1110 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1112 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1115 new_ln = wkn_ln - bind_ln;
1117 ret = wkn_dn_exp[0];
1119 for (i=1; i < new_ln; i++) {
1121 asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]);
1122 ret = SMB_STRDUP(s);
1130 * Adds (appends) an item to an attribute array, rather then
1131 * replacing the whole list
1132 * @param ctx An initialized TALLOC_CTX
1133 * @param mods An initialized ADS_MODLIST
1134 * @param name name of the ldap attribute to append to
1135 * @param vals an array of values to add
1136 * @return status of addition
1139 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1140 const char *name, const char **vals)
1142 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name, (const void **) vals);
1146 * Determines the computer account's current KVNO via an LDAP lookup
1147 * @param ads An initialized ADS_STRUCT
1148 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1149 * @return the kvno for the computer account, or -1 in case of a failure.
1152 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1154 LDAPMessage *res = NULL;
1155 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1157 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1158 char *dn_string = NULL;
1159 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1161 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1162 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1165 ret = ads_search(ads, (void**) &res, filter, attrs);
1167 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1168 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1169 ads_msgfree(ads, res);
1173 dn_string = ads_get_dn(ads, res);
1175 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1176 ads_msgfree(ads, res);
1179 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1180 ads_memfree(ads, dn_string);
1182 /* ---------------------------------------------------------
1183 * 0 is returned as a default KVNO from this point on...
1184 * This is done because Windows 2000 does not support key
1185 * version numbers. Chances are that a failure in the next
1186 * step is simply due to Windows 2000 being used for a
1187 * domain controller. */
1190 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1191 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1192 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1193 ads_msgfree(ads, res);
1198 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1199 ads_msgfree(ads, res);
1204 * This clears out all registered spn's for a given hostname
1205 * @param ads An initilaized ADS_STRUCT
1206 * @param machine_name the NetBIOS name of the computer.
1207 * @return 0 upon success, non-zero otherwise.
1210 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1213 LDAPMessage *res = NULL;
1215 const char *servicePrincipalName[1] = {NULL};
1216 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1217 char *dn_string = NULL;
1219 ret = ads_find_machine_acct(ads, (void **)&res, machine_name);
1220 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1221 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1222 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1223 ads_msgfree(ads, res);
1224 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1227 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1228 ctx = talloc_init("ads_clear_service_principal_names");
1230 ads_msgfree(ads, res);
1231 return ADS_ERROR(LDAP_NO_MEMORY);
1234 if (!(mods = ads_init_mods(ctx))) {
1235 talloc_destroy(ctx);
1236 ads_msgfree(ads, res);
1237 return ADS_ERROR(LDAP_NO_MEMORY);
1239 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1240 if (!ADS_ERR_OK(ret)) {
1241 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1242 ads_msgfree(ads, res);
1243 talloc_destroy(ctx);
1246 dn_string = ads_get_dn(ads, res);
1248 talloc_destroy(ctx);
1249 ads_msgfree(ads, res);
1250 return ADS_ERROR(LDAP_NO_MEMORY);
1252 ret = ads_gen_mod(ads, dn_string, mods);
1253 ads_memfree(ads,dn_string);
1254 if (!ADS_ERR_OK(ret)) {
1255 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1257 ads_msgfree(ads, res);
1258 talloc_destroy(ctx);
1262 ads_msgfree(ads, res);
1263 talloc_destroy(ctx);
1268 * This adds a service principal name to an existing computer account
1269 * (found by hostname) in AD.
1270 * @param ads An initialized ADS_STRUCT
1271 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1272 * @param spn A string of the service principal to add, i.e. 'host'
1273 * @return 0 upon sucess, or non-zero if a failure occurs
1276 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name, const char *spn)
1280 LDAPMessage *res = NULL;
1281 char *host_spn, *psp1, *psp2, *psp3;
1284 char *dn_string = NULL;
1285 const char *servicePrincipalName[4] = {NULL, NULL, NULL, NULL};
1287 ret = ads_find_machine_acct(ads, (void **)&res, machine_name);
1288 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1289 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1291 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1292 spn, machine_name, ads->config.realm));
1293 ads_msgfree(ads, res);
1294 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1297 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1298 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1299 ads_msgfree(ads, res);
1300 return ADS_ERROR(LDAP_NO_MEMORY);
1303 name_to_fqdn(my_fqdn, machine_name);
1304 strlower_m(my_fqdn);
1306 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", my_fqdn))) {
1307 talloc_destroy(ctx);
1308 ads_msgfree(ads, res);
1309 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1312 /* Add the extra principal */
1313 psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name);
1315 strlower_m(&psp1[strlen(spn)]);
1316 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp1, machine_name));
1317 servicePrincipalName[0] = psp1;
1318 psp2 = talloc_asprintf(ctx, "%s/%s.%s", spn, machine_name, ads->config.realm);
1320 strlower_m(&psp2[strlen(spn)]);
1321 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp2, machine_name));
1322 servicePrincipalName[1] = psp2;
1324 /* Add another principal in case the realm != the DNS domain, so that
1325 * the KDC doesn't send "server principal unknown" errors to clients
1326 * which use the DNS name in determining service principal names. */
1327 psp3 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn);
1329 strlower_m(&psp3[strlen(spn)]);
1330 if (strcmp(psp2, psp3) != 0) {
1331 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp3, machine_name));
1332 servicePrincipalName[2] = psp3;
1335 if (!(mods = ads_init_mods(ctx))) {
1336 talloc_destroy(ctx);
1337 ads_msgfree(ads, res);
1338 return ADS_ERROR(LDAP_NO_MEMORY);
1340 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1341 if (!ADS_ERR_OK(ret)) {
1342 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1343 talloc_destroy(ctx);
1344 ads_msgfree(ads, res);
1347 dn_string = ads_get_dn(ads, res);
1349 talloc_destroy(ctx);
1350 ads_msgfree(ads, res);
1351 return ADS_ERROR(LDAP_NO_MEMORY);
1353 ret = ads_gen_mod(ads, dn_string, mods);
1354 ads_memfree(ads,dn_string);
1355 if (!ADS_ERR_OK(ret)) {
1356 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1357 talloc_destroy(ctx);
1358 ads_msgfree(ads, res);
1362 talloc_destroy(ctx);
1363 ads_msgfree(ads, res);
1368 * adds a machine account to the ADS server
1369 * @param ads An intialized ADS_STRUCT
1370 * @param machine_name - the NetBIOS machine name of this account.
1371 * @param account_type A number indicating the type of account to create
1372 * @param org_unit The LDAP path in which to place this account
1373 * @return 0 upon success, or non-zero otherwise
1376 static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1377 uint32 account_type,
1378 const char *org_unit)
1380 ADS_STATUS ret, status;
1381 char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
1384 const char *objectClass[] = {"top", "person", "organizationalPerson",
1385 "user", "computer", NULL};
1386 const char *servicePrincipalName[7] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
1387 char *psp, *psp2, *psp3, *psp4;
1388 unsigned acct_control;
1391 LDAPMessage *res = NULL;
1394 if (!(ctx = talloc_init("ads_add_machine_acct")))
1395 return ADS_ERROR(LDAP_NO_MEMORY);
1397 ret = ADS_ERROR(LDAP_NO_MEMORY);
1399 name_to_fqdn(my_fqdn, machine_name);
1401 status = ads_find_machine_acct(ads, (void **)&res, machine_name);
1402 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1403 char *dn_string = ads_get_dn(ads, res);
1405 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
1408 new_dn = talloc_strdup(ctx, dn_string);
1409 ads_memfree(ads,dn_string);
1410 DEBUG(0, ("ads_add_machine_acct: Host account for %s already exists - modifying old account\n",
1414 char *ou_str = ads_ou_string(ads,org_unit);
1416 DEBUG(1, ("ads_add_machine_acct: ads_ou_string returned NULL (malloc failure?)\n"));
1419 new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", machine_name, ou_str,
1420 ads->config.bind_path);
1429 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", machine_name)))
1431 if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm)))
1433 servicePrincipalName[0] = talloc_asprintf(ctx, "HOST/%s", machine_name);
1434 psp = talloc_asprintf(ctx, "HOST/%s.%s",
1437 strlower_m(&psp[5]);
1438 servicePrincipalName[1] = psp;
1439 servicePrincipalName[2] = talloc_asprintf(ctx, "CIFS/%s", machine_name);
1440 psp2 = talloc_asprintf(ctx, "CIFS/%s.%s",
1443 strlower_m(&psp2[5]);
1444 servicePrincipalName[3] = psp2;
1446 /* Ensure servicePrincipalName[4] and [5] are unique. */
1447 strlower_m(my_fqdn);
1448 psp3 = talloc_asprintf(ctx, "CIFS/%s", my_fqdn);
1449 strlower_m(&psp3[5]);
1452 for (i = 0; i < next_spn; i++) {
1453 if (strequal(servicePrincipalName[i], psp3))
1456 if (i == next_spn) {
1457 servicePrincipalName[next_spn++] = psp3;
1460 psp4 = talloc_asprintf(ctx, "HOST/%s", my_fqdn);
1461 strlower_m(&psp4[5]);
1462 for (i = 0; i < next_spn; i++) {
1463 if (strequal(servicePrincipalName[i], psp4))
1466 if (i == next_spn) {
1467 servicePrincipalName[next_spn++] = psp4;
1470 if (!(samAccountName = talloc_asprintf(ctx, "%s$", machine_name))) {
1474 acct_control = account_type | UF_DONT_EXPIRE_PASSWD;
1475 #ifndef ENCTYPE_ARCFOUR_HMAC
1476 acct_control |= UF_USE_DES_KEY_ONLY;
1479 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1483 if (!(mods = ads_init_mods(ctx))) {
1488 ads_mod_str(ctx, &mods, "cn", machine_name);
1489 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1490 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1491 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1493 ads_mod_str(ctx, &mods, "dNSHostName", my_fqdn);
1494 ads_mod_str(ctx, &mods, "userPrincipalName", host_upn);
1495 ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1496 ads_mod_str(ctx, &mods, "operatingSystem", "Samba");
1497 ads_mod_str(ctx, &mods, "operatingSystemVersion", SAMBA_VERSION_STRING);
1500 ret = ads_gen_add(ads, new_dn, mods);
1502 ret = ads_gen_mod(ads, new_dn, mods);
1505 if (!ADS_ERR_OK(ret)) {
1509 /* Do not fail if we can't set security descriptor
1510 * it shouldn't be mandatory and probably we just
1511 * don't have enough rights to do it.
1514 status = ads_set_machine_sd(ads, machine_name, new_dn);
1516 if (!ADS_ERR_OK(status)) {
1517 DEBUG(0, ("Warning: ads_set_machine_sd: %s\n",
1518 ads_errstr(status)));
1522 ads_msgfree(ads, res);
1523 talloc_destroy(ctx);
1528 dump a binary result from ldap
1530 static void dump_binary(const char *field, struct berval **values)
1533 for (i=0; values[i]; i++) {
1534 printf("%s: ", field);
1535 for (j=0; j<values[i]->bv_len; j++) {
1536 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1542 static void dump_guid(const char *field, struct berval **values)
1546 for (i=0; values[i]; i++) {
1547 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1548 printf("%s: %s\n", field,
1549 smb_uuid_string_static(smb_uuid_unpack_static(guid)));
1554 dump a sid result from ldap
1556 static void dump_sid(const char *field, struct berval **values)
1559 for (i=0; values[i]; i++) {
1561 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1562 printf("%s: %s\n", field, sid_string_static(&sid));
1567 dump ntSecurityDescriptor
1569 static void dump_sd(const char *filed, struct berval **values)
1574 TALLOC_CTX *ctx = 0;
1576 if (!(ctx = talloc_init("sec_io_desc")))
1580 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1581 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1582 prs_set_offset(&ps,0);
1585 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1587 talloc_destroy(ctx);
1590 if (psd) ads_disp_sd(psd);
1593 talloc_destroy(ctx);
1597 dump a string result from ldap
1599 static void dump_string(const char *field, char **values)
1602 for (i=0; values[i]; i++) {
1603 printf("%s: %s\n", field, values[i]);
1608 dump a field from LDAP on stdout
1612 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1617 void (*handler)(const char *, struct berval **);
1619 {"objectGUID", False, dump_guid},
1620 {"nTSecurityDescriptor", False, dump_sd},
1621 {"dnsRecord", False, dump_binary},
1622 {"objectSid", False, dump_sid},
1623 {"tokenGroups", False, dump_sid},
1628 if (!field) { /* must be end of an entry */
1633 for (i=0; handlers[i].name; i++) {
1634 if (StrCaseCmp(handlers[i].name, field) == 0) {
1635 if (!values) /* first time, indicate string or not */
1636 return handlers[i].string;
1637 handlers[i].handler(field, (struct berval **) values);
1641 if (!handlers[i].name) {
1642 if (!values) /* first time, indicate string conversion */
1644 dump_string(field, (char **)values);
1650 * Dump a result from LDAP on stdout
1651 * used for debugging
1652 * @param ads connection to ads server
1653 * @param res Results to dump
1656 void ads_dump(ADS_STRUCT *ads, void *res)
1658 ads_process_results(ads, res, ads_dump_field, NULL);
1662 * Walk through results, calling a function for each entry found.
1663 * The function receives a field name, a berval * array of values,
1664 * and a data area passed through from the start. The function is
1665 * called once with null for field and values at the end of each
1667 * @param ads connection to ads server
1668 * @param res Results to process
1669 * @param fn Function for processing each result
1670 * @param data_area user-defined area to pass to function
1672 void ads_process_results(ADS_STRUCT *ads, void *res,
1673 BOOL(*fn)(char *, void **, void *),
1679 if (!(ctx = talloc_init("ads_process_results")))
1682 for (msg = ads_first_entry(ads, res); msg;
1683 msg = ads_next_entry(ads, msg)) {
1687 for (utf8_field=ldap_first_attribute(ads->ld,
1688 (LDAPMessage *)msg,&b);
1690 utf8_field=ldap_next_attribute(ads->ld,
1691 (LDAPMessage *)msg,b)) {
1692 struct berval **ber_vals;
1693 char **str_vals, **utf8_vals;
1697 pull_utf8_talloc(ctx, &field, utf8_field);
1698 string = fn(field, NULL, data_area);
1701 utf8_vals = ldap_get_values(ads->ld,
1702 (LDAPMessage *)msg, field);
1703 str_vals = ads_pull_strvals(ctx,
1704 (const char **) utf8_vals);
1705 fn(field, (void **) str_vals, data_area);
1706 ldap_value_free(utf8_vals);
1708 ber_vals = ldap_get_values_len(ads->ld,
1709 (LDAPMessage *)msg, field);
1710 fn(field, (void **) ber_vals, data_area);
1712 ldap_value_free_len(ber_vals);
1714 ldap_memfree(utf8_field);
1717 talloc_free_children(ctx);
1718 fn(NULL, NULL, data_area); /* completed an entry */
1721 talloc_destroy(ctx);
1725 * count how many replies are in a LDAPMessage
1726 * @param ads connection to ads server
1727 * @param res Results to count
1728 * @return number of replies
1730 int ads_count_replies(ADS_STRUCT *ads, void *res)
1732 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1736 * Join a machine to a realm
1737 * Creates the machine account and sets the machine password
1738 * @param ads connection to ads server
1739 * @param machine name of host to add
1740 * @param org_unit Organizational unit to place machine in
1741 * @return status of join
1743 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
1744 uint32 account_type, const char *org_unit)
1747 LDAPMessage *res = NULL;
1750 /* machine name must be lowercase */
1751 machine = SMB_STRDUP(machine_name);
1752 strlower_m(machine);
1755 status = ads_find_machine_acct(ads, (void **)&res, machine);
1756 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1757 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
1758 status = ads_leave_realm(ads, machine);
1759 if (!ADS_ERR_OK(status)) {
1760 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
1761 machine, ads->config.realm));
1767 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
1768 if (!ADS_ERR_OK(status)) {
1769 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
1774 status = ads_find_machine_acct(ads, (void **)&res, machine);
1775 if (!ADS_ERR_OK(status)) {
1776 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
1782 ads_msgfree(ads, res);
1788 * Delete a machine from the realm
1789 * @param ads connection to ads server
1790 * @param hostname Machine to remove
1791 * @return status of delete
1793 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
1797 char *hostnameDN, *host;
1800 /* hostname must be lowercase */
1801 host = SMB_STRDUP(hostname);
1804 status = ads_find_machine_acct(ads, &res, host);
1805 if (!ADS_ERR_OK(status)) {
1806 DEBUG(0, ("Host account for %s does not exist.\n", host));
1810 msg = ads_first_entry(ads, res);
1812 return ADS_ERROR_SYSTEM(ENOENT);
1815 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
1816 rc = ldap_delete_s(ads->ld, hostnameDN);
1817 ads_memfree(ads, hostnameDN);
1818 if (rc != LDAP_SUCCESS) {
1819 return ADS_ERROR(rc);
1822 status = ads_find_machine_acct(ads, &res, host);
1823 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1824 DEBUG(0, ("Failed to remove host account.\n"));
1834 * add machine account to existing security descriptor
1835 * @param ads connection to ads server
1836 * @param hostname machine to add
1837 * @param dn DN of security descriptor
1840 ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
1842 const char *attrs[] = {"nTSecurityDescriptor", "objectSid", 0};
1845 struct berval bval = {0, NULL};
1847 char *escaped_hostname = escape_ldap_string_alloc(hostname);
1849 LDAPMessage *res = 0;
1850 LDAPMessage *msg = 0;
1851 ADS_MODLIST mods = 0;
1856 SEC_DESC *psd = NULL;
1857 TALLOC_CTX *ctx = NULL;
1859 /* Avoid segmentation fault in prs_mem_free if
1860 * we have to bail out before prs_init */
1861 ps_wire.is_dynamic = False;
1863 if (!ads) return ADS_ERROR(LDAP_SERVER_DOWN);
1865 ret = ADS_ERROR(LDAP_SUCCESS);
1867 if (!escaped_hostname) {
1868 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1871 if (asprintf(&expr, "(samAccountName=%s$)", escaped_hostname) == -1) {
1872 DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n"));
1873 SAFE_FREE(escaped_hostname);
1874 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1877 SAFE_FREE(escaped_hostname);
1879 ret = ads_search(ads, (void *) &res, expr, attrs);
1881 if (!ADS_ERR_OK(ret)) return ret;
1883 if ( !(msg = ads_first_entry(ads, res) )) {
1884 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1885 goto ads_set_sd_error;
1888 if (!ads_pull_sid(ads, msg, attrs[1], &sid)) {
1889 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1890 goto ads_set_sd_error;
1893 if (!(ctx = talloc_init("sec_io_desc"))) {
1894 ret = ADS_ERROR(LDAP_NO_MEMORY);
1895 goto ads_set_sd_error;
1898 if (!ads_pull_sd(ads, ctx, msg, attrs[0], &psd)) {
1899 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1900 goto ads_set_sd_error;
1903 status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size);
1905 if (!NT_STATUS_IS_OK(status)) {
1906 ret = ADS_ERROR_NT(status);
1907 goto ads_set_sd_error;
1910 if (!prs_init(&ps_wire, sd_size, ctx, MARSHALL)) {
1911 ret = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1914 if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1)) {
1915 ret = ADS_ERROR(LDAP_NO_MEMORY);
1916 goto ads_set_sd_error;
1920 file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size);
1922 if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY);
1924 bval.bv_len = prs_offset(&ps_wire);
1925 bval.bv_val = TALLOC(ctx, bval.bv_len);
1927 ret = ADS_ERROR(LDAP_NO_MEMORY);
1928 goto ads_set_sd_error;
1931 prs_set_offset(&ps_wire, 0);
1933 if (!prs_copy_data_out(bval.bv_val, &ps_wire, bval.bv_len)) {
1934 ret = ADS_ERROR(LDAP_NO_MEMORY);
1935 goto ads_set_sd_error;
1938 ret = ads_mod_ber(ctx, &mods, attrs[0], &bval);
1939 if (ADS_ERR_OK(ret)) {
1940 ret = ads_gen_mod(ads, dn, mods);
1944 ads_msgfree(ads, res);
1945 prs_mem_free(&ps_wire);
1946 talloc_destroy(ctx);
1951 * pull the first entry from a ADS result
1952 * @param ads connection to ads server
1953 * @param res Results of search
1954 * @return first entry from result
1956 void *ads_first_entry(ADS_STRUCT *ads, void *res)
1958 return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
1962 * pull the next entry from a ADS result
1963 * @param ads connection to ads server
1964 * @param res Results of search
1965 * @return next entry from result
1967 void *ads_next_entry(ADS_STRUCT *ads, void *res)
1969 return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
1973 * pull a single string from a ADS result
1974 * @param ads connection to ads server
1975 * @param mem_ctx TALLOC_CTX to use for allocating result string
1976 * @param msg Results of search
1977 * @param field Attribute to retrieve
1978 * @return Result string in talloc context
1980 char *ads_pull_string(ADS_STRUCT *ads,
1981 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1988 values = ldap_get_values(ads->ld, msg, field);
1993 rc = pull_utf8_talloc(mem_ctx, &ux_string,
1995 if (rc != (size_t)-1)
1999 ldap_value_free(values);
2004 * pull an array of strings from a ADS result
2005 * @param ads connection to ads server
2006 * @param mem_ctx TALLOC_CTX to use for allocating result string
2007 * @param msg Results of search
2008 * @param field Attribute to retrieve
2009 * @return Result strings in talloc context
2011 char **ads_pull_strings(ADS_STRUCT *ads,
2012 TALLOC_CTX *mem_ctx, void *msg, const char *field,
2019 values = ldap_get_values(ads->ld, msg, field);
2023 *num_values = ldap_count_values(values);
2025 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2027 ldap_value_free(values);
2031 for (i=0;i<*num_values;i++) {
2032 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
2033 ldap_value_free(values);
2039 ldap_value_free(values);
2044 * pull an array of strings from a ADS result
2045 * (handle large multivalue attributes with range retrieval)
2046 * @param ads connection to ads server
2047 * @param mem_ctx TALLOC_CTX to use for allocating result string
2048 * @param msg Results of search
2049 * @param field Attribute to retrieve
2050 * @param current_strings strings returned by a previous call to this function
2051 * @param next_attribute The next query should ask for this attribute
2052 * @param num_values How many values did we get this time?
2053 * @param more_values Are there more values to get?
2054 * @return Result strings in talloc context
2056 char **ads_pull_strings_range(ADS_STRUCT *ads,
2057 TALLOC_CTX *mem_ctx,
2058 void *msg, const char *field,
2059 char **current_strings,
2060 const char **next_attribute,
2061 size_t *num_strings,
2065 char *expected_range_attrib, *range_attr;
2066 BerElement *ptr = NULL;
2069 size_t num_new_strings;
2070 unsigned long int range_start;
2071 unsigned long int range_end;
2073 /* we might have been given the whole lot anyway */
2074 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2075 *more_strings = False;
2079 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2081 /* look for Range result */
2082 for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr);
2084 attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
2085 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2086 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2094 /* nothing here - this field is just empty */
2095 *more_strings = False;
2099 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2100 &range_start, &range_end) == 2) {
2101 *more_strings = True;
2103 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2104 &range_start) == 1) {
2105 *more_strings = False;
2107 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2109 ldap_memfree(range_attr);
2110 *more_strings = False;
2115 if ((*num_strings) != range_start) {
2116 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2117 " - aborting range retreival\n",
2118 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2119 ldap_memfree(range_attr);
2120 *more_strings = False;
2124 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2126 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2127 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2128 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2129 range_attr, (unsigned long int)range_end - range_start + 1,
2130 (unsigned long int)num_new_strings));
2131 ldap_memfree(range_attr);
2132 *more_strings = False;
2136 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2137 *num_strings + num_new_strings);
2139 if (strings == NULL) {
2140 ldap_memfree(range_attr);
2141 *more_strings = False;
2145 memcpy(&strings[*num_strings], new_strings,
2146 sizeof(*new_strings) * num_new_strings);
2148 (*num_strings) += num_new_strings;
2150 if (*more_strings) {
2151 *next_attribute = talloc_asprintf(mem_ctx,
2156 if (!*next_attribute) {
2157 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2158 ldap_memfree(range_attr);
2159 *more_strings = False;
2164 ldap_memfree(range_attr);
2170 * pull a single uint32 from a ADS result
2171 * @param ads connection to ads server
2172 * @param msg Results of search
2173 * @param field Attribute to retrieve
2174 * @param v Pointer to int to store result
2175 * @return boolean inidicating success
2177 BOOL ads_pull_uint32(ADS_STRUCT *ads,
2178 void *msg, const char *field, uint32 *v)
2182 values = ldap_get_values(ads->ld, msg, field);
2186 ldap_value_free(values);
2190 *v = atoi(values[0]);
2191 ldap_value_free(values);
2196 * pull a single objectGUID from an ADS result
2197 * @param ads connection to ADS server
2198 * @param msg results of search
2199 * @param guid 37-byte area to receive text guid
2200 * @return boolean indicating success
2202 BOOL ads_pull_guid(ADS_STRUCT *ads,
2203 void *msg, struct uuid *guid)
2206 UUID_FLAT flat_guid;
2208 values = ldap_get_values(ads->ld, msg, "objectGUID");
2213 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2214 smb_uuid_unpack(flat_guid, guid);
2215 ldap_value_free(values);
2218 ldap_value_free(values);
2225 * pull a single DOM_SID from a ADS result
2226 * @param ads connection to ads server
2227 * @param msg Results of search
2228 * @param field Attribute to retrieve
2229 * @param sid Pointer to sid to store result
2230 * @return boolean inidicating success
2232 BOOL ads_pull_sid(ADS_STRUCT *ads,
2233 void *msg, const char *field, DOM_SID *sid)
2235 struct berval **values;
2238 values = ldap_get_values_len(ads->ld, msg, field);
2244 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2246 ldap_value_free_len(values);
2251 * pull an array of DOM_SIDs from a ADS result
2252 * @param ads connection to ads server
2253 * @param mem_ctx TALLOC_CTX for allocating sid array
2254 * @param msg Results of search
2255 * @param field Attribute to retrieve
2256 * @param sids pointer to sid array to allocate
2257 * @return the count of SIDs pulled
2259 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2260 void *msg, const char *field, DOM_SID **sids)
2262 struct berval **values;
2266 values = ldap_get_values_len(ads->ld, msg, field);
2271 for (i=0; values[i]; i++)
2274 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2276 ldap_value_free_len(values);
2281 for (i=0; values[i]; i++) {
2282 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2285 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2290 ldap_value_free_len(values);
2295 * pull a SEC_DESC from a ADS result
2296 * @param ads connection to ads server
2297 * @param mem_ctx TALLOC_CTX for allocating sid array
2298 * @param msg Results of search
2299 * @param field Attribute to retrieve
2300 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2301 * @return boolean inidicating success
2303 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2304 void *msg, const char *field, SEC_DESC **sd)
2306 struct berval **values;
2310 values = ldap_get_values_len(ads->ld, msg, field);
2312 if (!values) return False;
2315 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2316 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2317 prs_set_offset(&ps,0);
2319 ret = sec_io_desc("sd", sd, &ps, 1);
2322 ldap_value_free_len(values);
2327 * in order to support usernames longer than 21 characters we need to
2328 * use both the sAMAccountName and the userPrincipalName attributes
2329 * It seems that not all users have the userPrincipalName attribute set
2331 * @param ads connection to ads server
2332 * @param mem_ctx TALLOC_CTX for allocating sid array
2333 * @param msg Results of search
2334 * @return the username
2336 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
2341 /* lookup_name() only works on the sAMAccountName to
2342 returning the username portion of userPrincipalName
2343 breaks winbindd_getpwnam() */
2345 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2346 if (ret && (p = strchr_m(ret, '@'))) {
2351 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2356 * find the update serial number - this is the core of the ldap cache
2357 * @param ads connection to ads server
2358 * @param ads connection to ADS server
2359 * @param usn Pointer to retrieved update serial number
2360 * @return status of search
2362 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2364 const char *attrs[] = {"highestCommittedUSN", NULL};
2368 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2369 if (!ADS_ERR_OK(status))
2372 if (ads_count_replies(ads, res) != 1) {
2373 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2376 ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
2377 ads_msgfree(ads, res);
2381 /* parse a ADS timestring - typical string is
2382 '20020917091222.0Z0' which means 09:12.22 17th September
2384 static time_t ads_parse_time(const char *str)
2390 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2391 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2392 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2402 const char *ads_get_attrname_by_oid(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char * OID)
2408 const char *attrs[] = { "lDAPDisplayName", NULL };
2410 if (ads == NULL || mem_ctx == NULL || OID == NULL) {
2414 expr = talloc_asprintf(mem_ctx, "(attributeId=%s)", OID);
2419 rc = ads_do_search_retry(ads, ads->config.schema_path,
2420 LDAP_SCOPE_SUBTREE, expr, attrs, &res);
2421 if (!ADS_ERR_OK(rc)) {
2425 count = ads_count_replies(ads, res);
2426 if (count == 0 || !res) {
2430 return ads_pull_string(ads, mem_ctx, res, "lDAPDisplayName");
2433 DEBUG(0,("ads_get_attrname_by_oid: failed to retrieve name for oid: %s\n",
2440 * Find the servers name and realm - this can be done before authentication
2441 * The ldapServiceName field on w2k looks like this:
2442 * vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
2443 * @param ads connection to ads server
2444 * @return status of search
2446 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
2448 const char *attrs[] = {"ldapServiceName",
2450 "schemaNamingContext", NULL};
2459 if (!(ctx = talloc_init("ads_server_info"))) {
2460 return ADS_ERROR(LDAP_NO_MEMORY);
2463 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2464 if (!ADS_ERR_OK(status)) {
2465 talloc_destroy(ctx);
2469 value = ads_pull_string(ads, ctx, res, "ldapServiceName");
2471 ads_msgfree(ads, res);
2472 talloc_destroy(ctx);
2473 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2476 timestr = ads_pull_string(ads, ctx, res, "currentTime");
2478 ads_msgfree(ads, res);
2479 talloc_destroy(ctx);
2480 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2483 schema_path = ads_pull_string(ads, ctx, res, "schemaNamingContext");
2485 ads_msgfree(ads, res);
2486 talloc_destroy(ctx);
2487 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2490 SAFE_FREE(ads->config.schema_path);
2491 ads->config.schema_path = SMB_STRDUP(schema_path);
2493 ads_msgfree(ads, res);
2495 p = strchr(value, ':');
2497 talloc_destroy(ctx);
2498 DEBUG(1, ("ads_server_info: returned ldap server name did not contain a ':' "
2499 "so was deemed invalid\n"));
2500 return ADS_ERROR(LDAP_DECODING_ERROR);
2503 SAFE_FREE(ads->config.ldap_server_name);
2505 ads->config.ldap_server_name = SMB_STRDUP(p+1);
2506 p = strchr(ads->config.ldap_server_name, '$');
2507 if (!p || p[1] != '@') {
2508 talloc_destroy(ctx);
2509 DEBUG(1, ("ads_server_info: returned ldap server name (%s) does not contain '$@'"
2510 " so was deemed invalid\n", ads->config.ldap_server_name));
2511 SAFE_FREE(ads->config.ldap_server_name);
2512 return ADS_ERROR(LDAP_DECODING_ERROR);
2517 SAFE_FREE(ads->config.realm);
2518 SAFE_FREE(ads->config.bind_path);
2520 ads->config.realm = SMB_STRDUP(p+2);
2521 ads->config.bind_path = ads_build_dn(ads->config.realm);
2523 DEBUG(3,("got ldap server name %s@%s, using bind path: %s\n",
2524 ads->config.ldap_server_name, ads->config.realm,
2525 ads->config.bind_path));
2527 ads->config.current_time = ads_parse_time(timestr);
2529 if (ads->config.current_time != 0) {
2530 ads->auth.time_offset = ads->config.current_time - time(NULL);
2531 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2534 talloc_destroy(ctx);
2540 * Check for "Services for Unix"-Schema and load some attributes into the ADS_STRUCT
2541 * @param ads connection to ads server
2542 * @return BOOL status of search (False if one or more attributes couldn't be
2543 * found in Active Directory)
2545 BOOL ads_check_sfu_mapping(ADS_STRUCT *ads)
2548 TALLOC_CTX *ctx = NULL;
2549 const char *gidnumber, *uidnumber, *homedir, *shell;
2551 ctx = talloc_init("ads_check_sfu_mapping");
2555 gidnumber = ads_get_attrname_by_oid(ads, ctx, ADS_ATTR_SFU_GIDNUMBER_OID);
2556 if (gidnumber == NULL)
2558 ads->schema.sfu_gidnumber_attr = SMB_STRDUP(gidnumber);
2560 uidnumber = ads_get_attrname_by_oid(ads, ctx, ADS_ATTR_SFU_UIDNUMBER_OID);
2561 if (uidnumber == NULL)
2563 ads->schema.sfu_uidnumber_attr = SMB_STRDUP(uidnumber);
2565 homedir = ads_get_attrname_by_oid(ads, ctx, ADS_ATTR_SFU_HOMEDIR_OID);
2566 if (homedir == NULL)
2568 ads->schema.sfu_homedir_attr = SMB_STRDUP(homedir);
2570 shell = ads_get_attrname_by_oid(ads, ctx, ADS_ATTR_SFU_SHELL_OID);
2573 ads->schema.sfu_shell_attr = SMB_STRDUP(shell);
2578 talloc_destroy(ctx);
2584 * find the domain sid for our domain
2585 * @param ads connection to ads server
2586 * @param sid Pointer to domain sid
2587 * @return status of search
2589 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2591 const char *attrs[] = {"objectSid", NULL};
2595 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2597 if (!ADS_ERR_OK(rc)) return rc;
2598 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2599 ads_msgfree(ads, res);
2600 return ADS_ERROR_SYSTEM(ENOENT);
2602 ads_msgfree(ads, res);
2607 /* this is rather complex - we need to find the allternate (netbios) name
2608 for the domain, but there isn't a simple query to do this. Instead
2609 we look for the principle names on the DCs account and find one that has
2610 the right form, then extract the netbios name of the domain from that
2612 NOTE! better method is this:
2614 bin/net -Uadministrator%XXXXX ads search '(&(objectclass=crossref)(dnsroot=VNET3.HOME.SAMBA.ORG))' nETBIOSName
2616 but you need to force the bind path to match the configurationNamingContext from the rootDSE
2619 ADS_STATUS ads_workgroup_name(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **workgroup)
2628 const char *attrs[] = {"servicePrincipalName", NULL};
2629 size_t num_principals;
2631 (*workgroup) = NULL;
2633 asprintf(&expr, "(&(objectclass=computer)(dnshostname=%s.%s))",
2634 ads->config.ldap_server_name, ads->config.realm);
2636 ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2639 rc = ads_search(ads, &res, expr, attrs);
2642 if (!ADS_ERR_OK(rc)) {
2646 principles = ads_pull_strings(ads, mem_ctx, res,
2647 "servicePrincipalName", &num_principals);
2649 ads_msgfree(ads, res);
2652 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2655 asprintf(&prefix, "HOST/%s.%s/",
2656 ads->config.ldap_server_name,
2659 prefix_length = strlen(prefix);
2661 for (i=0;principles[i]; i++) {
2662 if (strnequal(principles[i], prefix, prefix_length) &&
2663 !strequal(ads->config.realm, principles[i]+prefix_length) &&
2664 !strchr(principles[i]+prefix_length, '.')) {
2665 /* found an alternate (short) name for the domain. */
2666 DEBUG(3,("Found alternate name '%s' for realm '%s'\n",
2667 principles[i]+prefix_length,
2668 ads->config.realm));
2669 (*workgroup) = talloc_strdup(mem_ctx, principles[i]+prefix_length);
2676 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);