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
41 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
43 static SIG_ATOMIC_T gotalarm;
45 /***************************************************************
46 Signal function to tell us we timed out.
47 ****************************************************************/
49 static void gotalarm_sig(void)
54 LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
60 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
62 /* End setup timeout. */
64 ldp = ldap_open(server, port);
66 /* Teardown timeout. */
67 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
73 static int ldap_search_with_timeout(LDAP *ld,
74 LDAP_CONST char *base,
76 LDAP_CONST char *filter,
84 struct timeval timeout;
87 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
88 timeout.tv_sec = lp_ldap_timeout();
91 /* Setup alarm timeout.... Do we need both of these ? JRA. */
93 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
94 alarm(lp_ldap_timeout());
95 /* End setup timeout. */
97 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
98 attrsonly, sctrls, cctrls, &timeout,
101 /* Teardown timeout. */
102 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
106 return LDAP_TIMELIMIT_EXCEEDED;
112 try a connection to a given ldap server, returning True and setting the servers IP
113 in the ads struct if successful
115 TODO : add a negative connection cache in here leveraged off of the one
116 found in the rpc code. --jerry
118 BOOL ads_try_connect(ADS_STRUCT *ads, const char *server, unsigned port)
122 if (!server || !*server) {
126 DEBUG(5,("ads_try_connect: trying ldap server '%s' port %u\n", server, port));
128 /* this copes with inet_ntoa brokenness */
129 srv = SMB_STRDUP(server);
131 ads->ld = ldap_open_with_timeout(srv, port, lp_ldap_timeout());
136 ads->ldap_port = port;
137 ads->ldap_ip = *interpret_addr2(srv);
144 try a connection to a given ldap server, based on URL, returning True if successful
146 static BOOL ads_try_connect_uri(ADS_STRUCT *ads)
148 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
149 DEBUG(5,("ads_try_connect: trying ldap server at URI '%s'\n",
150 ads->server.ldap_uri));
153 if (ldap_initialize((LDAP**)&(ads->ld), ads->server.ldap_uri) == LDAP_SUCCESS) {
156 DEBUG(0, ("ldap_initialize: %s\n", strerror(errno)));
160 DEBUG(1, ("no URL support in LDAP libs!\n"));
166 /**********************************************************************
167 Try to find an AD dc using our internal name resolution routines
168 Try the realm first and then then workgroup name if netbios is not
170 **********************************************************************/
172 static BOOL ads_find_dc(ADS_STRUCT *ads)
176 struct ip_service *ip_list;
178 BOOL got_realm = False;
179 BOOL use_own_domain = False;
181 /* if the realm and workgroup are both empty, assume they are ours */
184 c_realm = ads->server.realm;
186 if ( !c_realm || !*c_realm ) {
187 /* special case where no realm and no workgroup means our own */
188 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
189 use_own_domain = True;
190 c_realm = lp_realm();
194 if (c_realm && *c_realm)
198 /* we need to try once with the realm name and fallback to the
199 netbios domain name if we fail (if netbios has not been disabled */
201 if ( !got_realm && !lp_disable_netbios() ) {
202 c_realm = ads->server.workgroup;
203 if (!c_realm || !*c_realm) {
204 if ( use_own_domain )
205 c_realm = lp_workgroup();
208 if ( !c_realm || !*c_realm ) {
209 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
214 pstrcpy( realm, c_realm );
216 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
217 (got_realm ? "realm" : "domain"), realm));
219 if ( !get_sorted_dc_list(realm, &ip_list, &count, got_realm) ) {
220 /* fall back to netbios if we can */
221 if ( got_realm && !lp_disable_netbios() ) {
229 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
230 for ( i=0; i<count; i++ ) {
231 /* since this is an ads conection request, default to LDAP_PORT is not set */
232 int port = (ip_list[i].port!=PORT_NONE) ? ip_list[i].port : LDAP_PORT;
235 fstrcpy( server, inet_ntoa(ip_list[i].ip) );
237 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
240 if ( ads_try_connect(ads, server, port) ) {
245 /* keep track of failures */
246 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
256 * Connect to the LDAP server
257 * @param ads Pointer to an existing ADS_STRUCT
258 * @return status of connection
260 ADS_STATUS ads_connect(ADS_STRUCT *ads)
262 int version = LDAP_VERSION3;
265 ads->last_attempt = time(NULL);
268 /* try with a URL based server */
270 if (ads->server.ldap_uri &&
271 ads_try_connect_uri(ads)) {
275 /* try with a user specified server */
276 if (ads->server.ldap_server &&
277 ads_try_connect(ads, ads->server.ldap_server, LDAP_PORT)) {
281 if (ads_find_dc(ads)) {
285 return ADS_ERROR_SYSTEM(errno?errno:ENOENT);
288 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
290 status = ads_server_info(ads);
291 if (!ADS_ERR_OK(status)) {
292 DEBUG(1,("Failed to get ldap server info\n"));
296 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
298 status = ADS_ERROR(smb_ldap_start_tls(ads->ld, version));
299 if (!ADS_ERR_OK(status)) {
303 if (!ads->auth.user_name) {
304 /* have to use the userPrincipalName value here and
305 not servicePrincipalName; found by Guenther Deschner @ Sernet */
307 asprintf(&ads->auth.user_name, "host/%s", global_myname() );
310 if (!ads->auth.realm) {
311 ads->auth.realm = SMB_STRDUP(ads->config.realm);
314 if (!ads->auth.kdc_server) {
315 ads->auth.kdc_server = SMB_STRDUP(inet_ntoa(ads->ldap_ip));
319 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
320 to MIT kerberos to work (tridge) */
323 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
324 setenv(env, ads->auth.kdc_server, 1);
329 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
333 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
334 return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
337 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
338 return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
341 return ads_sasl_bind(ads);
345 Duplicate a struct berval into talloc'ed memory
347 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
349 struct berval *value;
351 if (!in_val) return NULL;
353 value = TALLOC_ZERO_P(ctx, struct berval);
356 if (in_val->bv_len == 0) return value;
358 value->bv_len = in_val->bv_len;
359 value->bv_val = TALLOC_MEMDUP(ctx, in_val->bv_val, in_val->bv_len);
364 Make a values list out of an array of (struct berval *)
366 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
367 const struct berval **in_vals)
369 struct berval **values;
372 if (!in_vals) return NULL;
373 for (i=0; in_vals[i]; i++)
375 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
376 if (!values) return NULL;
378 for (i=0; in_vals[i]; i++) {
379 values[i] = dup_berval(ctx, in_vals[i]);
385 UTF8-encode a values list out of an array of (char *)
387 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
392 if (!in_vals) return NULL;
393 for (i=0; in_vals[i]; i++)
395 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
396 if (!values) return NULL;
398 for (i=0; in_vals[i]; i++) {
399 push_utf8_talloc(ctx, &values[i], in_vals[i]);
405 Pull a (char *) array out of a UTF8-encoded values list
407 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
412 if (!in_vals) return NULL;
413 for (i=0; in_vals[i]; i++)
415 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
416 if (!values) return NULL;
418 for (i=0; in_vals[i]; i++) {
419 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
425 * Do a search with paged results. cookie must be null on the first
426 * call, and then returned on each subsequent call. It will be null
427 * again when the entire search is complete
428 * @param ads connection to ads server
429 * @param bind_path Base dn for the search
430 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
431 * @param expr Search expression - specified in local charset
432 * @param attrs Attributes to retrieve - specified in utf8 or ascii
433 * @param res ** which will contain results - free res* with ads_msgfree()
434 * @param count Number of entries retrieved on this page
435 * @param cookie The paged results cookie to be returned on subsequent calls
436 * @return status of search
438 ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
439 int scope, const char *expr,
440 const char **attrs, void **res,
441 int *count, void **cookie)
444 char *utf8_expr, *utf8_path, **search_attrs;
445 LDAPControl PagedResults, NoReferrals, *controls[3], **rcontrols;
446 BerElement *cookie_be = NULL;
447 struct berval *cookie_bv= NULL;
452 if (!(ctx = talloc_init("ads_do_paged_search")))
453 return ADS_ERROR(LDAP_NO_MEMORY);
455 /* 0 means the conversion worked but the result was empty
456 so we only fail if it's -1. In any case, it always
457 at least nulls out the dest */
458 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
459 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
464 if (!attrs || !(*attrs))
467 /* This would be the utf8-encoded version...*/
468 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
469 if (!(str_list_copy(&search_attrs, attrs))) {
476 /* Paged results only available on ldap v3 or later */
477 ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
478 if (version < LDAP_VERSION3) {
479 rc = LDAP_NOT_SUPPORTED;
483 cookie_be = ber_alloc_t(LBER_USE_DER);
484 if (cookie && *cookie) {
485 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
486 ber_bvfree(*cookie); /* don't need it from last time */
489 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
491 ber_flatten(cookie_be, &cookie_bv);
492 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
493 PagedResults.ldctl_iscritical = (char) 1;
494 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
495 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
497 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
498 NoReferrals.ldctl_iscritical = (char) 0;
499 NoReferrals.ldctl_value.bv_len = 0;
500 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
503 controls[0] = &NoReferrals;
504 controls[1] = &PagedResults;
507 /* we need to disable referrals as the openldap libs don't
508 handle them and paged results at the same time. Using them
509 together results in the result record containing the server
510 page control being removed from the result list (tridge/jmcd)
512 leaving this in despite the control that says don't generate
513 referrals, in case the server doesn't support it (jmcd)
515 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
517 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
518 search_attrs, 0, controls,
520 (LDAPMessage **)res);
522 ber_free(cookie_be, 1);
523 ber_bvfree(cookie_bv);
526 DEBUG(3,("ads_do_paged_search: ldap_search_with_timeout(%s) -> %s\n", expr,
527 ldap_err2string(rc)));
531 rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
532 NULL, &rcontrols, 0);
538 for (i=0; rcontrols[i]; i++) {
539 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
540 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
541 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
543 /* the berval is the cookie, but must be freed when
545 if (cookie_bv->bv_len) /* still more to do */
546 *cookie=ber_bvdup(cookie_bv);
549 ber_bvfree(cookie_bv);
550 ber_free(cookie_be, 1);
554 ldap_controls_free(rcontrols);
558 /* if/when we decide to utf8-encode attrs, take out this next line */
559 str_list_free(&search_attrs);
561 return ADS_ERROR(rc);
566 * Get all results for a search. This uses ads_do_paged_search() to return
567 * all entries in a large search.
568 * @param ads connection to ads server
569 * @param bind_path Base dn for the search
570 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
571 * @param expr Search expression
572 * @param attrs Attributes to retrieve
573 * @param res ** which will contain results - free res* with ads_msgfree()
574 * @return status of search
576 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
577 int scope, const char *expr,
578 const char **attrs, void **res)
585 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, res,
588 if (!ADS_ERR_OK(status))
591 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
595 LDAPMessage *msg, *next;
597 status2 = ads_do_paged_search(ads, bind_path, scope, expr,
598 attrs, &res2, &count, &cookie);
600 if (!ADS_ERR_OK(status2)) break;
602 /* this relies on the way that ldap_add_result_entry() works internally. I hope
603 that this works on all ldap libs, but I have only tested with openldap */
604 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
605 next = ads_next_entry(ads, msg);
606 ldap_add_result_entry((LDAPMessage **)res, msg);
608 /* note that we do not free res2, as the memory is now
609 part of the main returned list */
612 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
613 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
620 * Run a function on all results for a search. Uses ads_do_paged_search() and
621 * runs the function as each page is returned, using ads_process_results()
622 * @param ads connection to ads server
623 * @param bind_path Base dn for the search
624 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
625 * @param expr Search expression - specified in local charset
626 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
627 * @param fn Function which takes attr name, values list, and data_area
628 * @param data_area Pointer which is passed to function on each call
629 * @return status of search
631 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
632 int scope, const char *expr, const char **attrs,
633 BOOL(*fn)(char *, void **, void *),
641 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
644 if (!ADS_ERR_OK(status)) return status;
646 ads_process_results(ads, res, fn, data_area);
647 ads_msgfree(ads, res);
650 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
651 &res, &count, &cookie);
653 if (!ADS_ERR_OK(status)) break;
655 ads_process_results(ads, res, fn, data_area);
656 ads_msgfree(ads, res);
663 * Do a search with a timeout.
664 * @param ads connection to ads server
665 * @param bind_path Base dn for the search
666 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
667 * @param expr Search expression
668 * @param attrs Attributes to retrieve
669 * @param res ** which will contain results - free res* with ads_msgfree()
670 * @return status of search
672 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
674 const char **attrs, void **res)
677 char *utf8_expr, *utf8_path, **search_attrs = NULL;
681 if (!(ctx = talloc_init("ads_do_search"))) {
682 DEBUG(1,("ads_do_search: talloc_init() failed!"));
683 return ADS_ERROR(LDAP_NO_MEMORY);
686 /* 0 means the conversion worked but the result was empty
687 so we only fail if it's negative. In any case, it always
688 at least nulls out the dest */
689 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
690 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
691 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
696 if (!attrs || !(*attrs))
699 /* This would be the utf8-encoded version...*/
700 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
701 if (!(str_list_copy(&search_attrs, attrs)))
703 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
709 /* see the note in ads_do_paged_search - we *must* disable referrals */
710 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
712 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
713 search_attrs, 0, NULL, NULL,
715 (LDAPMessage **)res);
717 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
718 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
724 /* if/when we decide to utf8-encode attrs, take out this next line */
725 str_list_free(&search_attrs);
726 return ADS_ERROR(rc);
729 * Do a general ADS search
730 * @param ads connection to ads server
731 * @param res ** which will contain results - free res* with ads_msgfree()
732 * @param expr Search expression
733 * @param attrs Attributes to retrieve
734 * @return status of search
736 ADS_STATUS ads_search(ADS_STRUCT *ads, void **res,
740 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
745 * Do a search on a specific DistinguishedName
746 * @param ads connection to ads server
747 * @param res ** which will contain results - free res* with ads_msgfree()
748 * @param dn DistinguishName to search
749 * @param attrs Attributes to retrieve
750 * @return status of search
752 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, void **res,
756 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res);
760 * Free up memory from a ads_search
761 * @param ads connection to ads server
762 * @param msg Search results to free
764 void ads_msgfree(ADS_STRUCT *ads, void *msg)
771 * Free up memory from various ads requests
772 * @param ads connection to ads server
773 * @param mem Area to free
775 void ads_memfree(ADS_STRUCT *ads, void *mem)
781 * Get a dn from search results
782 * @param ads connection to ads server
783 * @param msg Search result
786 char *ads_get_dn(ADS_STRUCT *ads, void *msg)
788 char *utf8_dn, *unix_dn;
790 utf8_dn = ldap_get_dn(ads->ld, msg);
793 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
797 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
798 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
802 ldap_memfree(utf8_dn);
807 * Find a machine account given a hostname
808 * @param ads connection to ads server
809 * @param res ** which will contain results - free res* with ads_msgfree()
810 * @param host Hostname to search for
811 * @return status of search
813 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *machine)
817 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
821 /* the easiest way to find a machine account anywhere in the tree
822 is to look for hostname$ */
823 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
824 DEBUG(1, ("asprintf failed!\n"));
825 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
828 status = ads_search(ads, res, expr, attrs);
834 * Initialize a list of mods to be used in a modify request
835 * @param ctx An initialized TALLOC_CTX
836 * @return allocated ADS_MODLIST
838 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
840 #define ADS_MODLIST_ALLOC_SIZE 10
843 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
844 /* -1 is safety to make sure we don't go over the end.
845 need to reset it to NULL before doing ldap modify */
846 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
848 return (ADS_MODLIST)mods;
853 add an attribute to the list, with values list already constructed
855 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
856 int mod_op, const char *name,
860 LDAPMod **modlist = (LDAPMod **) *mods;
861 struct berval **ber_values = NULL;
862 char **char_values = NULL;
865 mod_op = LDAP_MOD_DELETE;
867 if (mod_op & LDAP_MOD_BVALUES)
868 ber_values = ads_dup_values(ctx,
869 (const struct berval **)invals);
871 char_values = ads_push_strvals(ctx,
872 (const char **) invals);
875 /* find the first empty slot */
876 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
878 if (modlist[curmod] == (LDAPMod *) -1) {
879 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
880 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
881 return ADS_ERROR(LDAP_NO_MEMORY);
882 memset(&modlist[curmod], 0,
883 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
884 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
885 *mods = (ADS_MODLIST)modlist;
888 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
889 return ADS_ERROR(LDAP_NO_MEMORY);
890 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
891 if (mod_op & LDAP_MOD_BVALUES) {
892 modlist[curmod]->mod_bvalues = ber_values;
893 } else if (mod_op & LDAP_MOD_DELETE) {
894 modlist[curmod]->mod_values = NULL;
896 modlist[curmod]->mod_values = char_values;
899 modlist[curmod]->mod_op = mod_op;
900 return ADS_ERROR(LDAP_SUCCESS);
904 * Add a single string value to a mod list
905 * @param ctx An initialized TALLOC_CTX
906 * @param mods An initialized ADS_MODLIST
907 * @param name The attribute name to add
908 * @param val The value to add - NULL means DELETE
909 * @return ADS STATUS indicating success of add
911 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
912 const char *name, const char *val)
914 const char *values[2];
920 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
921 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name,
922 (const void **) values);
926 * Add an array of string values to a mod list
927 * @param ctx An initialized TALLOC_CTX
928 * @param mods An initialized ADS_MODLIST
929 * @param name The attribute name to add
930 * @param vals The array of string values to add - NULL means DELETE
931 * @return ADS STATUS indicating success of add
933 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
934 const char *name, const char **vals)
937 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
938 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
939 name, (const void **) vals);
943 * Add a single ber-encoded value to a mod list
944 * @param ctx An initialized TALLOC_CTX
945 * @param mods An initialized ADS_MODLIST
946 * @param name The attribute name to add
947 * @param val The value to add - NULL means DELETE
948 * @return ADS STATUS indicating success of add
950 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
951 const char *name, const struct berval *val)
953 const struct berval *values[2];
958 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
959 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
960 name, (const void **) values);
964 * Perform an ldap modify
965 * @param ads connection to ads server
966 * @param mod_dn DistinguishedName to modify
967 * @param mods list of modifications to perform
968 * @return status of modify
970 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
973 char *utf8_dn = NULL;
975 this control is needed to modify that contains a currently
976 non-existent attribute (but allowable for the object) to run
978 LDAPControl PermitModify = {
979 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
982 LDAPControl *controls[2];
984 controls[0] = &PermitModify;
987 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
988 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
991 /* find the end of the list, marked by NULL or -1 */
992 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
993 /* make sure the end of the list is NULL */
995 ret = ldap_modify_ext_s(ads->ld, utf8_dn,
996 (LDAPMod **) mods, controls, NULL);
998 return ADS_ERROR(ret);
1002 * Perform an ldap add
1003 * @param ads connection to ads server
1004 * @param new_dn DistinguishedName to add
1005 * @param mods list of attributes and values for DN
1006 * @return status of add
1008 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1011 char *utf8_dn = NULL;
1013 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1014 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1015 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1018 /* find the end of the list, marked by NULL or -1 */
1019 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1020 /* make sure the end of the list is NULL */
1023 ret = ldap_add_s(ads->ld, utf8_dn, (LDAPMod**)mods);
1025 return ADS_ERROR(ret);
1029 * Delete a DistinguishedName
1030 * @param ads connection to ads server
1031 * @param new_dn DistinguishedName to delete
1032 * @return status of delete
1034 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1037 char *utf8_dn = NULL;
1038 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1039 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1040 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1043 ret = ldap_delete_s(ads->ld, utf8_dn);
1044 return ADS_ERROR(ret);
1048 * Build an org unit string
1049 * if org unit is Computers or blank then assume a container, otherwise
1050 * assume a \ separated list of organisational units
1051 * @param ads connection to ads server
1052 * @param org_unit Organizational unit
1053 * @return org unit string - caller must free
1055 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1059 if (!org_unit || !*org_unit) {
1061 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1063 /* samba4 might not yet respond to a wellknownobject-query */
1064 return ret ? ret : SMB_STRDUP("cn=Computers");
1067 if (strequal(org_unit, "Computers")) {
1068 return SMB_STRDUP("cn=Computers");
1071 return ads_build_path(org_unit, "\\/", "ou=", 1);
1075 * Get a org unit string for a well-known GUID
1076 * @param ads connection to ads server
1077 * @param wknguid Well known GUID
1078 * @return org unit string - caller must free
1080 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1084 char *base, *wkn_dn, *ret, **wkn_dn_exp, **bind_dn_exp;
1085 const char *attrs[] = {"distinguishedName", NULL};
1086 int new_ln, wkn_ln, bind_ln, i;
1088 if (wknguid == NULL) {
1092 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1093 DEBUG(1, ("asprintf failed!\n"));
1097 status = ads_search_dn(ads, &res, base, attrs);
1098 if (!ADS_ERR_OK(status)) {
1099 DEBUG(1,("Failed while searching for: %s\n", base));
1104 if (ads_count_replies(ads, res) != 1) {
1108 /* substitute the bind-path from the well-known-guid-search result */
1109 wkn_dn = ads_get_dn(ads, res);
1110 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1111 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1113 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1115 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1118 new_ln = wkn_ln - bind_ln;
1120 ret = wkn_dn_exp[0];
1122 for (i=1; i < new_ln; i++) {
1124 asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]);
1125 ret = SMB_STRDUP(s);
1133 * Adds (appends) an item to an attribute array, rather then
1134 * replacing the whole list
1135 * @param ctx An initialized TALLOC_CTX
1136 * @param mods An initialized ADS_MODLIST
1137 * @param name name of the ldap attribute to append to
1138 * @param vals an array of values to add
1139 * @return status of addition
1142 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1143 const char *name, const char **vals)
1145 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name, (const void **) vals);
1149 * Determines the computer account's current KVNO via an LDAP lookup
1150 * @param ads An initialized ADS_STRUCT
1151 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1152 * @return the kvno for the computer account, or -1 in case of a failure.
1155 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1157 LDAPMessage *res = NULL;
1158 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1160 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1161 char *dn_string = NULL;
1162 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1164 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1165 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1168 ret = ads_search(ads, (void**)(void *)&res, filter, attrs);
1170 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1171 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1172 ads_msgfree(ads, res);
1176 dn_string = ads_get_dn(ads, res);
1178 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1179 ads_msgfree(ads, res);
1182 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1183 ads_memfree(ads, dn_string);
1185 /* ---------------------------------------------------------
1186 * 0 is returned as a default KVNO from this point on...
1187 * This is done because Windows 2000 does not support key
1188 * version numbers. Chances are that a failure in the next
1189 * step is simply due to Windows 2000 being used for a
1190 * domain controller. */
1193 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1194 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1195 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1196 ads_msgfree(ads, res);
1201 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1202 ads_msgfree(ads, res);
1207 * This clears out all registered spn's for a given hostname
1208 * @param ads An initilaized ADS_STRUCT
1209 * @param machine_name the NetBIOS name of the computer.
1210 * @return 0 upon success, non-zero otherwise.
1213 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1216 LDAPMessage *res = NULL;
1218 const char *servicePrincipalName[1] = {NULL};
1219 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1220 char *dn_string = NULL;
1222 ret = ads_find_machine_acct(ads, (void **)(void *)&res, machine_name);
1223 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1224 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1225 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1226 ads_msgfree(ads, res);
1227 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1230 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1231 ctx = talloc_init("ads_clear_service_principal_names");
1233 ads_msgfree(ads, res);
1234 return ADS_ERROR(LDAP_NO_MEMORY);
1237 if (!(mods = ads_init_mods(ctx))) {
1238 talloc_destroy(ctx);
1239 ads_msgfree(ads, res);
1240 return ADS_ERROR(LDAP_NO_MEMORY);
1242 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1243 if (!ADS_ERR_OK(ret)) {
1244 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1245 ads_msgfree(ads, res);
1246 talloc_destroy(ctx);
1249 dn_string = ads_get_dn(ads, res);
1251 talloc_destroy(ctx);
1252 ads_msgfree(ads, res);
1253 return ADS_ERROR(LDAP_NO_MEMORY);
1255 ret = ads_gen_mod(ads, dn_string, mods);
1256 ads_memfree(ads,dn_string);
1257 if (!ADS_ERR_OK(ret)) {
1258 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1260 ads_msgfree(ads, res);
1261 talloc_destroy(ctx);
1265 ads_msgfree(ads, res);
1266 talloc_destroy(ctx);
1271 * This adds a service principal name to an existing computer account
1272 * (found by hostname) in AD.
1273 * @param ads An initialized ADS_STRUCT
1274 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1275 * @param spn A string of the service principal to add, i.e. 'host'
1276 * @return 0 upon sucess, or non-zero if a failure occurs
1279 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name, const char *spn)
1283 LDAPMessage *res = NULL;
1284 char *host_spn, *psp1, *psp2, *psp3;
1287 char *dn_string = NULL;
1288 const char *servicePrincipalName[4] = {NULL, NULL, NULL, NULL};
1290 ret = ads_find_machine_acct(ads, (void **)(void *)&res, machine_name);
1291 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1292 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1294 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1295 spn, machine_name, ads->config.realm));
1296 ads_msgfree(ads, res);
1297 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1300 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1301 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1302 ads_msgfree(ads, res);
1303 return ADS_ERROR(LDAP_NO_MEMORY);
1306 name_to_fqdn(my_fqdn, machine_name);
1307 strlower_m(my_fqdn);
1309 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", my_fqdn))) {
1310 talloc_destroy(ctx);
1311 ads_msgfree(ads, res);
1312 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1315 /* Add the extra principal */
1316 psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name);
1318 strlower_m(&psp1[strlen(spn)]);
1319 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp1, machine_name));
1320 servicePrincipalName[0] = psp1;
1321 psp2 = talloc_asprintf(ctx, "%s/%s.%s", spn, machine_name, ads->config.realm);
1323 strlower_m(&psp2[strlen(spn)]);
1324 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp2, machine_name));
1325 servicePrincipalName[1] = psp2;
1327 /* Add another principal in case the realm != the DNS domain, so that
1328 * the KDC doesn't send "server principal unknown" errors to clients
1329 * which use the DNS name in determining service principal names. */
1330 psp3 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn);
1332 strlower_m(&psp3[strlen(spn)]);
1333 if (strcmp(psp2, psp3) != 0) {
1334 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp3, machine_name));
1335 servicePrincipalName[2] = psp3;
1338 if (!(mods = ads_init_mods(ctx))) {
1339 talloc_destroy(ctx);
1340 ads_msgfree(ads, res);
1341 return ADS_ERROR(LDAP_NO_MEMORY);
1343 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1344 if (!ADS_ERR_OK(ret)) {
1345 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1346 talloc_destroy(ctx);
1347 ads_msgfree(ads, res);
1350 dn_string = ads_get_dn(ads, res);
1352 talloc_destroy(ctx);
1353 ads_msgfree(ads, res);
1354 return ADS_ERROR(LDAP_NO_MEMORY);
1356 ret = ads_gen_mod(ads, dn_string, mods);
1357 ads_memfree(ads,dn_string);
1358 if (!ADS_ERR_OK(ret)) {
1359 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1360 talloc_destroy(ctx);
1361 ads_msgfree(ads, res);
1365 talloc_destroy(ctx);
1366 ads_msgfree(ads, res);
1371 * adds a machine account to the ADS server
1372 * @param ads An intialized ADS_STRUCT
1373 * @param machine_name - the NetBIOS machine name of this account.
1374 * @param account_type A number indicating the type of account to create
1375 * @param org_unit The LDAP path in which to place this account
1376 * @return 0 upon success, or non-zero otherwise
1379 static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1380 uint32 account_type,
1381 const char *org_unit)
1383 ADS_STATUS ret, status;
1384 char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
1387 const char *objectClass[] = {"top", "person", "organizationalPerson",
1388 "user", "computer", NULL};
1389 const char *servicePrincipalName[7] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
1390 char *psp, *psp2, *psp3, *psp4;
1391 unsigned acct_control;
1394 LDAPMessage *res = NULL;
1397 if (!(ctx = talloc_init("ads_add_machine_acct")))
1398 return ADS_ERROR(LDAP_NO_MEMORY);
1400 ret = ADS_ERROR(LDAP_NO_MEMORY);
1402 name_to_fqdn(my_fqdn, machine_name);
1404 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine_name);
1405 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1406 char *dn_string = ads_get_dn(ads, res);
1408 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
1411 new_dn = talloc_strdup(ctx, dn_string);
1412 ads_memfree(ads,dn_string);
1413 DEBUG(0, ("ads_add_machine_acct: Host account for %s already exists - modifying old account\n",
1417 char *ou_str = ads_ou_string(ads,org_unit);
1419 DEBUG(1, ("ads_add_machine_acct: ads_ou_string returned NULL (malloc failure?)\n"));
1422 new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", machine_name, ou_str,
1423 ads->config.bind_path);
1432 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", machine_name)))
1434 if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm)))
1436 servicePrincipalName[0] = talloc_asprintf(ctx, "HOST/%s", machine_name);
1437 psp = talloc_asprintf(ctx, "HOST/%s.%s",
1440 strlower_m(&psp[5]);
1441 servicePrincipalName[1] = psp;
1442 servicePrincipalName[2] = talloc_asprintf(ctx, "CIFS/%s", machine_name);
1443 psp2 = talloc_asprintf(ctx, "CIFS/%s.%s",
1446 strlower_m(&psp2[5]);
1447 servicePrincipalName[3] = psp2;
1449 /* Ensure servicePrincipalName[4] and [5] are unique. */
1450 strlower_m(my_fqdn);
1451 psp3 = talloc_asprintf(ctx, "CIFS/%s", my_fqdn);
1452 strlower_m(&psp3[5]);
1455 for (i = 0; i < next_spn; i++) {
1456 if (strequal(servicePrincipalName[i], psp3))
1459 if (i == next_spn) {
1460 servicePrincipalName[next_spn++] = psp3;
1463 psp4 = talloc_asprintf(ctx, "HOST/%s", my_fqdn);
1464 strlower_m(&psp4[5]);
1465 for (i = 0; i < next_spn; i++) {
1466 if (strequal(servicePrincipalName[i], psp4))
1469 if (i == next_spn) {
1470 servicePrincipalName[next_spn++] = psp4;
1473 if (!(samAccountName = talloc_asprintf(ctx, "%s$", machine_name))) {
1477 acct_control = account_type | UF_DONT_EXPIRE_PASSWD;
1478 #ifndef ENCTYPE_ARCFOUR_HMAC
1479 acct_control |= UF_USE_DES_KEY_ONLY;
1482 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1486 if (!(mods = ads_init_mods(ctx))) {
1491 ads_mod_str(ctx, &mods, "cn", machine_name);
1492 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1493 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1494 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1496 ads_mod_str(ctx, &mods, "dNSHostName", my_fqdn);
1497 ads_mod_str(ctx, &mods, "userPrincipalName", host_upn);
1498 ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1499 ads_mod_str(ctx, &mods, "operatingSystem", "Samba");
1500 ads_mod_str(ctx, &mods, "operatingSystemVersion", SAMBA_VERSION_STRING);
1503 ret = ads_gen_add(ads, new_dn, mods);
1505 ret = ads_gen_mod(ads, new_dn, mods);
1508 if (!ADS_ERR_OK(ret)) {
1512 /* Do not fail if we can't set security descriptor
1513 * it shouldn't be mandatory and probably we just
1514 * don't have enough rights to do it.
1517 status = ads_set_machine_sd(ads, machine_name, new_dn);
1519 if (!ADS_ERR_OK(status)) {
1520 DEBUG(0, ("Warning: ads_set_machine_sd: %s\n",
1521 ads_errstr(status)));
1525 ads_msgfree(ads, res);
1526 talloc_destroy(ctx);
1531 dump a binary result from ldap
1533 static void dump_binary(const char *field, struct berval **values)
1536 for (i=0; values[i]; i++) {
1537 printf("%s: ", field);
1538 for (j=0; j<values[i]->bv_len; j++) {
1539 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1545 static void dump_guid(const char *field, struct berval **values)
1549 for (i=0; values[i]; i++) {
1550 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1551 printf("%s: %s\n", field,
1552 smb_uuid_string_static(smb_uuid_unpack_static(guid)));
1557 dump a sid result from ldap
1559 static void dump_sid(const char *field, struct berval **values)
1562 for (i=0; values[i]; i++) {
1564 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1565 printf("%s: %s\n", field, sid_string_static(&sid));
1570 dump ntSecurityDescriptor
1572 static void dump_sd(const char *filed, struct berval **values)
1577 TALLOC_CTX *ctx = 0;
1579 if (!(ctx = talloc_init("sec_io_desc")))
1583 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1584 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1585 prs_set_offset(&ps,0);
1588 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1590 talloc_destroy(ctx);
1593 if (psd) ads_disp_sd(psd);
1596 talloc_destroy(ctx);
1600 dump a string result from ldap
1602 static void dump_string(const char *field, char **values)
1605 for (i=0; values[i]; i++) {
1606 printf("%s: %s\n", field, values[i]);
1611 dump a field from LDAP on stdout
1615 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1620 void (*handler)(const char *, struct berval **);
1622 {"objectGUID", False, dump_guid},
1623 {"nTSecurityDescriptor", False, dump_sd},
1624 {"dnsRecord", False, dump_binary},
1625 {"objectSid", False, dump_sid},
1626 {"tokenGroups", False, dump_sid},
1631 if (!field) { /* must be end of an entry */
1636 for (i=0; handlers[i].name; i++) {
1637 if (StrCaseCmp(handlers[i].name, field) == 0) {
1638 if (!values) /* first time, indicate string or not */
1639 return handlers[i].string;
1640 handlers[i].handler(field, (struct berval **) values);
1644 if (!handlers[i].name) {
1645 if (!values) /* first time, indicate string conversion */
1647 dump_string(field, (char **)values);
1653 * Dump a result from LDAP on stdout
1654 * used for debugging
1655 * @param ads connection to ads server
1656 * @param res Results to dump
1659 void ads_dump(ADS_STRUCT *ads, void *res)
1661 ads_process_results(ads, res, ads_dump_field, NULL);
1665 * Walk through results, calling a function for each entry found.
1666 * The function receives a field name, a berval * array of values,
1667 * and a data area passed through from the start. The function is
1668 * called once with null for field and values at the end of each
1670 * @param ads connection to ads server
1671 * @param res Results to process
1672 * @param fn Function for processing each result
1673 * @param data_area user-defined area to pass to function
1675 void ads_process_results(ADS_STRUCT *ads, void *res,
1676 BOOL(*fn)(char *, void **, void *),
1682 if (!(ctx = talloc_init("ads_process_results")))
1685 for (msg = ads_first_entry(ads, res); msg;
1686 msg = ads_next_entry(ads, msg)) {
1690 for (utf8_field=ldap_first_attribute(ads->ld,
1691 (LDAPMessage *)msg,&b);
1693 utf8_field=ldap_next_attribute(ads->ld,
1694 (LDAPMessage *)msg,b)) {
1695 struct berval **ber_vals;
1696 char **str_vals, **utf8_vals;
1700 pull_utf8_talloc(ctx, &field, utf8_field);
1701 string = fn(field, NULL, data_area);
1704 utf8_vals = ldap_get_values(ads->ld,
1705 (LDAPMessage *)msg, field);
1706 str_vals = ads_pull_strvals(ctx,
1707 (const char **) utf8_vals);
1708 fn(field, (void **) str_vals, data_area);
1709 ldap_value_free(utf8_vals);
1711 ber_vals = ldap_get_values_len(ads->ld,
1712 (LDAPMessage *)msg, field);
1713 fn(field, (void **) ber_vals, data_area);
1715 ldap_value_free_len(ber_vals);
1717 ldap_memfree(utf8_field);
1720 talloc_free_children(ctx);
1721 fn(NULL, NULL, data_area); /* completed an entry */
1724 talloc_destroy(ctx);
1728 * count how many replies are in a LDAPMessage
1729 * @param ads connection to ads server
1730 * @param res Results to count
1731 * @return number of replies
1733 int ads_count_replies(ADS_STRUCT *ads, void *res)
1735 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1739 * Join a machine to a realm
1740 * Creates the machine account and sets the machine password
1741 * @param ads connection to ads server
1742 * @param machine name of host to add
1743 * @param org_unit Organizational unit to place machine in
1744 * @return status of join
1746 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
1747 uint32 account_type, const char *org_unit)
1750 LDAPMessage *res = NULL;
1753 /* machine name must be lowercase */
1754 machine = SMB_STRDUP(machine_name);
1755 strlower_m(machine);
1758 status = ads_find_machine_acct(ads, (void **)&res, machine);
1759 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1760 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
1761 status = ads_leave_realm(ads, machine);
1762 if (!ADS_ERR_OK(status)) {
1763 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
1764 machine, ads->config.realm));
1770 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
1771 if (!ADS_ERR_OK(status)) {
1772 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
1777 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
1778 if (!ADS_ERR_OK(status)) {
1779 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
1785 ads_msgfree(ads, res);
1791 * Delete a machine from the realm
1792 * @param ads connection to ads server
1793 * @param hostname Machine to remove
1794 * @return status of delete
1796 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
1800 char *hostnameDN, *host;
1802 LDAPControl ldap_control;
1803 LDAPControl * pldap_control[2] = {NULL, NULL};
1805 pldap_control[0] = &ldap_control;
1806 memset(&ldap_control, 0, sizeof(LDAPControl));
1807 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
1809 /* hostname must be lowercase */
1810 host = SMB_STRDUP(hostname);
1813 status = ads_find_machine_acct(ads, &res, host);
1814 if (!ADS_ERR_OK(status)) {
1815 DEBUG(0, ("Host account for %s does not exist.\n", host));
1819 msg = ads_first_entry(ads, res);
1821 return ADS_ERROR_SYSTEM(ENOENT);
1824 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
1827 rc = ldap_delete_ext_s(ads->ld, hostnameDN, pldap_control, NULL);
1829 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
1831 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
1834 ads_memfree(ads, hostnameDN);
1835 if (rc != LDAP_SUCCESS) {
1836 return ADS_ERROR(rc);
1839 status = ads_find_machine_acct(ads, &res, host);
1840 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1841 DEBUG(0, ("Failed to remove host account.\n"));
1851 * add machine account to existing security descriptor
1852 * @param ads connection to ads server
1853 * @param hostname machine to add
1854 * @param dn DN of security descriptor
1857 ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
1859 const char *attrs[] = {"nTSecurityDescriptor", "objectSid", 0};
1862 struct berval bval = {0, NULL};
1864 char *escaped_hostname = escape_ldap_string_alloc(hostname);
1866 LDAPMessage *res = 0;
1867 LDAPMessage *msg = 0;
1868 ADS_MODLIST mods = 0;
1873 SEC_DESC *psd = NULL;
1874 TALLOC_CTX *ctx = NULL;
1876 /* Avoid segmentation fault in prs_mem_free if
1877 * we have to bail out before prs_init */
1878 ps_wire.is_dynamic = False;
1880 if (!ads) return ADS_ERROR(LDAP_SERVER_DOWN);
1882 ret = ADS_ERROR(LDAP_SUCCESS);
1884 if (!escaped_hostname) {
1885 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1888 if (asprintf(&expr, "(samAccountName=%s$)", escaped_hostname) == -1) {
1889 DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n"));
1890 SAFE_FREE(escaped_hostname);
1891 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1894 SAFE_FREE(escaped_hostname);
1896 ret = ads_search(ads, (void *) &res, expr, attrs);
1898 if (!ADS_ERR_OK(ret)) return ret;
1900 if ( !(msg = ads_first_entry(ads, res) )) {
1901 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1902 goto ads_set_sd_error;
1905 if (!ads_pull_sid(ads, msg, attrs[1], &sid)) {
1906 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1907 goto ads_set_sd_error;
1910 if (!(ctx = talloc_init("sec_io_desc"))) {
1911 ret = ADS_ERROR(LDAP_NO_MEMORY);
1912 goto ads_set_sd_error;
1915 if (!ads_pull_sd(ads, ctx, msg, attrs[0], &psd)) {
1916 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1917 goto ads_set_sd_error;
1920 status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size);
1922 if (!NT_STATUS_IS_OK(status)) {
1923 ret = ADS_ERROR_NT(status);
1924 goto ads_set_sd_error;
1927 if (!prs_init(&ps_wire, sd_size, ctx, MARSHALL)) {
1928 ret = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1931 if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1)) {
1932 ret = ADS_ERROR(LDAP_NO_MEMORY);
1933 goto ads_set_sd_error;
1937 file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size);
1939 if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY);
1941 bval.bv_len = prs_offset(&ps_wire);
1942 bval.bv_val = TALLOC(ctx, bval.bv_len);
1944 ret = ADS_ERROR(LDAP_NO_MEMORY);
1945 goto ads_set_sd_error;
1948 prs_set_offset(&ps_wire, 0);
1950 if (!prs_copy_data_out(bval.bv_val, &ps_wire, bval.bv_len)) {
1951 ret = ADS_ERROR(LDAP_NO_MEMORY);
1952 goto ads_set_sd_error;
1955 ret = ads_mod_ber(ctx, &mods, attrs[0], &bval);
1956 if (ADS_ERR_OK(ret)) {
1957 ret = ads_gen_mod(ads, dn, mods);
1961 ads_msgfree(ads, res);
1962 prs_mem_free(&ps_wire);
1963 talloc_destroy(ctx);
1968 * pull the first entry from a ADS result
1969 * @param ads connection to ads server
1970 * @param res Results of search
1971 * @return first entry from result
1973 void *ads_first_entry(ADS_STRUCT *ads, void *res)
1975 return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
1979 * pull the next entry from a ADS result
1980 * @param ads connection to ads server
1981 * @param res Results of search
1982 * @return next entry from result
1984 void *ads_next_entry(ADS_STRUCT *ads, void *res)
1986 return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
1990 * pull a single string from a ADS result
1991 * @param ads connection to ads server
1992 * @param mem_ctx TALLOC_CTX to use for allocating result string
1993 * @param msg Results of search
1994 * @param field Attribute to retrieve
1995 * @return Result string in talloc context
1997 char *ads_pull_string(ADS_STRUCT *ads,
1998 TALLOC_CTX *mem_ctx, void *msg, const char *field)
2005 values = ldap_get_values(ads->ld, msg, field);
2010 rc = pull_utf8_talloc(mem_ctx, &ux_string,
2012 if (rc != (size_t)-1)
2016 ldap_value_free(values);
2021 * pull an array of strings from a ADS result
2022 * @param ads connection to ads server
2023 * @param mem_ctx TALLOC_CTX to use for allocating result string
2024 * @param msg Results of search
2025 * @param field Attribute to retrieve
2026 * @return Result strings in talloc context
2028 char **ads_pull_strings(ADS_STRUCT *ads,
2029 TALLOC_CTX *mem_ctx, void *msg, const char *field,
2036 values = ldap_get_values(ads->ld, msg, field);
2040 *num_values = ldap_count_values(values);
2042 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2044 ldap_value_free(values);
2048 for (i=0;i<*num_values;i++) {
2049 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
2050 ldap_value_free(values);
2056 ldap_value_free(values);
2061 * pull an array of strings from a ADS result
2062 * (handle large multivalue attributes with range retrieval)
2063 * @param ads connection to ads server
2064 * @param mem_ctx TALLOC_CTX to use for allocating result string
2065 * @param msg Results of search
2066 * @param field Attribute to retrieve
2067 * @param current_strings strings returned by a previous call to this function
2068 * @param next_attribute The next query should ask for this attribute
2069 * @param num_values How many values did we get this time?
2070 * @param more_values Are there more values to get?
2071 * @return Result strings in talloc context
2073 char **ads_pull_strings_range(ADS_STRUCT *ads,
2074 TALLOC_CTX *mem_ctx,
2075 void *msg, const char *field,
2076 char **current_strings,
2077 const char **next_attribute,
2078 size_t *num_strings,
2082 char *expected_range_attrib, *range_attr;
2083 BerElement *ptr = NULL;
2086 size_t num_new_strings;
2087 unsigned long int range_start;
2088 unsigned long int range_end;
2090 /* we might have been given the whole lot anyway */
2091 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2092 *more_strings = False;
2096 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2098 /* look for Range result */
2099 for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr);
2101 attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
2102 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2103 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2111 /* nothing here - this field is just empty */
2112 *more_strings = False;
2116 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2117 &range_start, &range_end) == 2) {
2118 *more_strings = True;
2120 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2121 &range_start) == 1) {
2122 *more_strings = False;
2124 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2126 ldap_memfree(range_attr);
2127 *more_strings = False;
2132 if ((*num_strings) != range_start) {
2133 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2134 " - aborting range retreival\n",
2135 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2136 ldap_memfree(range_attr);
2137 *more_strings = False;
2141 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2143 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2144 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2145 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2146 range_attr, (unsigned long int)range_end - range_start + 1,
2147 (unsigned long int)num_new_strings));
2148 ldap_memfree(range_attr);
2149 *more_strings = False;
2153 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2154 *num_strings + num_new_strings);
2156 if (strings == NULL) {
2157 ldap_memfree(range_attr);
2158 *more_strings = False;
2162 memcpy(&strings[*num_strings], new_strings,
2163 sizeof(*new_strings) * num_new_strings);
2165 (*num_strings) += num_new_strings;
2167 if (*more_strings) {
2168 *next_attribute = talloc_asprintf(mem_ctx,
2173 if (!*next_attribute) {
2174 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2175 ldap_memfree(range_attr);
2176 *more_strings = False;
2181 ldap_memfree(range_attr);
2187 * pull a single uint32 from a ADS result
2188 * @param ads connection to ads server
2189 * @param msg Results of search
2190 * @param field Attribute to retrieve
2191 * @param v Pointer to int to store result
2192 * @return boolean inidicating success
2194 BOOL ads_pull_uint32(ADS_STRUCT *ads,
2195 void *msg, const char *field, uint32 *v)
2199 values = ldap_get_values(ads->ld, msg, field);
2203 ldap_value_free(values);
2207 *v = atoi(values[0]);
2208 ldap_value_free(values);
2213 * pull a single objectGUID from an ADS result
2214 * @param ads connection to ADS server
2215 * @param msg results of search
2216 * @param guid 37-byte area to receive text guid
2217 * @return boolean indicating success
2219 BOOL ads_pull_guid(ADS_STRUCT *ads,
2220 void *msg, struct uuid *guid)
2223 UUID_FLAT flat_guid;
2225 values = ldap_get_values(ads->ld, msg, "objectGUID");
2230 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2231 smb_uuid_unpack(flat_guid, guid);
2232 ldap_value_free(values);
2235 ldap_value_free(values);
2242 * pull a single DOM_SID from a ADS result
2243 * @param ads connection to ads server
2244 * @param msg Results of search
2245 * @param field Attribute to retrieve
2246 * @param sid Pointer to sid to store result
2247 * @return boolean inidicating success
2249 BOOL ads_pull_sid(ADS_STRUCT *ads,
2250 void *msg, const char *field, DOM_SID *sid)
2252 struct berval **values;
2255 values = ldap_get_values_len(ads->ld, msg, field);
2261 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2263 ldap_value_free_len(values);
2268 * pull an array of DOM_SIDs from a ADS result
2269 * @param ads connection to ads server
2270 * @param mem_ctx TALLOC_CTX for allocating sid array
2271 * @param msg Results of search
2272 * @param field Attribute to retrieve
2273 * @param sids pointer to sid array to allocate
2274 * @return the count of SIDs pulled
2276 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2277 void *msg, const char *field, DOM_SID **sids)
2279 struct berval **values;
2283 values = ldap_get_values_len(ads->ld, msg, field);
2288 for (i=0; values[i]; i++)
2291 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2293 ldap_value_free_len(values);
2298 for (i=0; values[i]; i++) {
2299 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2302 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2307 ldap_value_free_len(values);
2312 * pull a SEC_DESC from a ADS result
2313 * @param ads connection to ads server
2314 * @param mem_ctx TALLOC_CTX for allocating sid array
2315 * @param msg Results of search
2316 * @param field Attribute to retrieve
2317 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2318 * @return boolean inidicating success
2320 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2321 void *msg, const char *field, SEC_DESC **sd)
2323 struct berval **values;
2327 values = ldap_get_values_len(ads->ld, msg, field);
2329 if (!values) return False;
2332 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2333 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2334 prs_set_offset(&ps,0);
2336 ret = sec_io_desc("sd", sd, &ps, 1);
2339 ldap_value_free_len(values);
2344 * in order to support usernames longer than 21 characters we need to
2345 * use both the sAMAccountName and the userPrincipalName attributes
2346 * It seems that not all users have the userPrincipalName attribute set
2348 * @param ads connection to ads server
2349 * @param mem_ctx TALLOC_CTX for allocating sid array
2350 * @param msg Results of search
2351 * @return the username
2353 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
2358 /* lookup_name() only works on the sAMAccountName to
2359 returning the username portion of userPrincipalName
2360 breaks winbindd_getpwnam() */
2362 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2363 if (ret && (p = strchr_m(ret, '@'))) {
2368 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2373 * find the update serial number - this is the core of the ldap cache
2374 * @param ads connection to ads server
2375 * @param ads connection to ADS server
2376 * @param usn Pointer to retrieved update serial number
2377 * @return status of search
2379 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2381 const char *attrs[] = {"highestCommittedUSN", NULL};
2385 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2386 if (!ADS_ERR_OK(status))
2389 if (ads_count_replies(ads, res) != 1) {
2390 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2393 ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
2394 ads_msgfree(ads, res);
2398 /* parse a ADS timestring - typical string is
2399 '20020917091222.0Z0' which means 09:12.22 17th September
2401 static time_t ads_parse_time(const char *str)
2407 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2408 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2409 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2419 const char *ads_get_attrname_by_oid(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char * OID)
2425 const char *attrs[] = { "lDAPDisplayName", NULL };
2427 if (ads == NULL || mem_ctx == NULL || OID == NULL) {
2431 expr = talloc_asprintf(mem_ctx, "(attributeId=%s)", OID);
2436 rc = ads_do_search_retry(ads, ads->config.schema_path,
2437 LDAP_SCOPE_SUBTREE, expr, attrs, &res);
2438 if (!ADS_ERR_OK(rc)) {
2442 count = ads_count_replies(ads, res);
2443 if (count == 0 || !res) {
2447 return ads_pull_string(ads, mem_ctx, res, "lDAPDisplayName");
2450 DEBUG(0,("ads_get_attrname_by_oid: failed to retrieve name for oid: %s\n",
2457 * Find the servers name and realm - this can be done before authentication
2458 * The ldapServiceName field on w2k looks like this:
2459 * vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
2460 * @param ads connection to ads server
2461 * @return status of search
2463 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
2465 const char *attrs[] = {"ldapServiceName",
2467 "schemaNamingContext", NULL};
2476 if (!(ctx = talloc_init("ads_server_info"))) {
2477 return ADS_ERROR(LDAP_NO_MEMORY);
2480 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2481 if (!ADS_ERR_OK(status)) {
2482 talloc_destroy(ctx);
2486 value = ads_pull_string(ads, ctx, res, "ldapServiceName");
2488 ads_msgfree(ads, res);
2489 talloc_destroy(ctx);
2490 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2493 timestr = ads_pull_string(ads, ctx, res, "currentTime");
2495 ads_msgfree(ads, res);
2496 talloc_destroy(ctx);
2497 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2500 schema_path = ads_pull_string(ads, ctx, res, "schemaNamingContext");
2502 ads_msgfree(ads, res);
2503 talloc_destroy(ctx);
2504 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2507 SAFE_FREE(ads->config.schema_path);
2508 ads->config.schema_path = SMB_STRDUP(schema_path);
2510 ads_msgfree(ads, res);
2512 p = strchr(value, ':');
2514 talloc_destroy(ctx);
2515 DEBUG(1, ("ads_server_info: returned ldap server name did not contain a ':' "
2516 "so was deemed invalid\n"));
2517 return ADS_ERROR(LDAP_DECODING_ERROR);
2520 SAFE_FREE(ads->config.ldap_server_name);
2522 ads->config.ldap_server_name = SMB_STRDUP(p+1);
2523 p = strchr(ads->config.ldap_server_name, '$');
2524 if (!p || p[1] != '@') {
2525 talloc_destroy(ctx);
2526 DEBUG(1, ("ads_server_info: returned ldap server name (%s) does not contain '$@'"
2527 " so was deemed invalid\n", ads->config.ldap_server_name));
2528 SAFE_FREE(ads->config.ldap_server_name);
2529 return ADS_ERROR(LDAP_DECODING_ERROR);
2534 SAFE_FREE(ads->config.realm);
2535 SAFE_FREE(ads->config.bind_path);
2537 ads->config.realm = SMB_STRDUP(p+2);
2538 ads->config.bind_path = ads_build_dn(ads->config.realm);
2540 DEBUG(3,("got ldap server name %s@%s, using bind path: %s\n",
2541 ads->config.ldap_server_name, ads->config.realm,
2542 ads->config.bind_path));
2544 ads->config.current_time = ads_parse_time(timestr);
2546 if (ads->config.current_time != 0) {
2547 ads->auth.time_offset = ads->config.current_time - time(NULL);
2548 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2551 talloc_destroy(ctx);
2557 * Check for "Services for Unix"-Schema and load some attributes into the ADS_STRUCT
2558 * @param ads connection to ads server
2559 * @return BOOL status of search (False if one or more attributes couldn't be
2560 * found in Active Directory)
2562 BOOL ads_check_sfu_mapping(ADS_STRUCT *ads)
2565 TALLOC_CTX *ctx = NULL;
2566 const char *gidnumber, *uidnumber, *homedir, *shell, *gecos;
2568 ctx = talloc_init("ads_check_sfu_mapping");
2572 gidnumber = ads_get_attrname_by_oid(ads, ctx, ADS_ATTR_SFU_GIDNUMBER_OID);
2573 if (gidnumber == NULL)
2575 ads->schema.sfu_gidnumber_attr = SMB_STRDUP(gidnumber);
2577 uidnumber = ads_get_attrname_by_oid(ads, ctx, ADS_ATTR_SFU_UIDNUMBER_OID);
2578 if (uidnumber == NULL)
2580 ads->schema.sfu_uidnumber_attr = SMB_STRDUP(uidnumber);
2582 homedir = ads_get_attrname_by_oid(ads, ctx, ADS_ATTR_SFU_HOMEDIR_OID);
2583 if (homedir == NULL)
2585 ads->schema.sfu_homedir_attr = SMB_STRDUP(homedir);
2587 shell = ads_get_attrname_by_oid(ads, ctx, ADS_ATTR_SFU_SHELL_OID);
2590 ads->schema.sfu_shell_attr = SMB_STRDUP(shell);
2592 gecos = ads_get_attrname_by_oid(ads, ctx, ADS_ATTR_SFU_GECOS_OID);
2595 ads->schema.sfu_gecos_attr = SMB_STRDUP(gecos);
2600 talloc_destroy(ctx);
2606 * find the domain sid for our domain
2607 * @param ads connection to ads server
2608 * @param sid Pointer to domain sid
2609 * @return status of search
2611 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2613 const char *attrs[] = {"objectSid", NULL};
2617 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2619 if (!ADS_ERR_OK(rc)) return rc;
2620 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2621 ads_msgfree(ads, res);
2622 return ADS_ERROR_SYSTEM(ENOENT);
2624 ads_msgfree(ads, res);
2629 /* this is rather complex - we need to find the allternate (netbios) name
2630 for the domain, but there isn't a simple query to do this. Instead
2631 we look for the principle names on the DCs account and find one that has
2632 the right form, then extract the netbios name of the domain from that
2634 NOTE! better method is this:
2636 bin/net -Uadministrator%XXXXX ads search '(&(objectclass=crossref)(dnsroot=VNET3.HOME.SAMBA.ORG))' nETBIOSName
2638 but you need to force the bind path to match the configurationNamingContext from the rootDSE
2641 ADS_STATUS ads_workgroup_name(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **workgroup)
2650 const char *attrs[] = {"servicePrincipalName", NULL};
2651 size_t num_principals;
2653 (*workgroup) = NULL;
2655 asprintf(&expr, "(&(objectclass=computer)(dnshostname=%s.%s))",
2656 ads->config.ldap_server_name, ads->config.realm);
2658 ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2661 rc = ads_search(ads, &res, expr, attrs);
2664 if (!ADS_ERR_OK(rc)) {
2668 principles = ads_pull_strings(ads, mem_ctx, res,
2669 "servicePrincipalName", &num_principals);
2671 ads_msgfree(ads, res);
2674 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2677 asprintf(&prefix, "HOST/%s.%s/",
2678 ads->config.ldap_server_name,
2681 prefix_length = strlen(prefix);
2683 for (i=0;principles[i]; i++) {
2684 if (strnequal(principles[i], prefix, prefix_length) &&
2685 !strequal(ads->config.realm, principles[i]+prefix_length) &&
2686 !strchr(principles[i]+prefix_length, '.')) {
2687 /* found an alternate (short) name for the domain. */
2688 DEBUG(3,("Found alternate name '%s' for realm '%s'\n",
2689 principles[i]+prefix_length,
2690 ads->config.realm));
2691 (*workgroup) = talloc_strdup(mem_ctx, principles[i]+prefix_length);
2698 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);