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.
30 * @brief basic ldap client-side routines for ads server communications
32 * The routines contained here should do the necessary ldap calls for
35 * Important note: attribute names passed into ads_ routines must
36 * already be in UTF-8 format. We do not convert them because in almost
37 * all cases, they are just ascii (which is represented with the same
38 * codepoints in UTF-8). This may have to change at some point
43 try a connection to a given ldap server, returning True and setting the servers IP
44 in the ads struct if successful
46 TODO : add a negative connection cache in here leveraged off of the one
47 found in the rpc code. --jerry
49 static BOOL
ads_try_connect(ADS_STRUCT
*ads
, const char *server
, uint_t port
)
53 if (!server
|| !*server
) {
57 DEBUG(5,("ads_try_connect: trying ldap server '%s' port %u\n", server
, port
));
59 /* this copes with inet_ntoa brokenness */
62 ads
->ld
= ldap_open(srv
, port
);
67 ads
->ldap_port
= port
;
68 ads
->ldap_ip
= interpret_addr2(srv
);
75 try a connection to a given ldap server, based on URL, returning True if successful
77 static BOOL
ads_try_connect_uri(ADS_STRUCT
*ads
)
79 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
80 DEBUG(5,("ads_try_connect: trying ldap server at URI '%s'\n",
81 ads
->server
.ldap_uri
));
84 if (ldap_initialize((LDAP
**)&(ads
->ld
), ads
->server
.ldap_uri
) == LDAP_SUCCESS
) {
87 DEBUG(0, ("ldap_initialize: %s\n", strerror(errno
)));
91 DEBUG(1, ("no URL support in LDAP libs!\n"));
97 /**********************************************************************
98 Try to find an AD dc using our internal name resolution routines
99 Try the realm first and then then workgroup name if netbios is not
101 **********************************************************************/
103 static BOOL
ads_find_dc(ADS_STRUCT
*ads
)
107 struct ip_service
*ip_list
;
109 BOOL got_realm
= False
;
110 BOOL use_own_domain
= False
;
112 /* if the realm and workgroup are both empty, assume they are ours */
115 c_realm
= ads
->server
.realm
;
117 if ( !c_realm
|| !*c_realm
) {
118 /* special case where no realm and no workgroup means our own */
119 if ( !ads
->server
.workgroup
|| !*ads
->server
.workgroup
) {
120 use_own_domain
= True
;
121 c_realm
= lp_realm();
125 if (c_realm
&& *c_realm
)
129 /* we need to try once with the realm name and fallback to the
130 netbios domain name if we fail (if netbios has not been disabled */
132 if ( !got_realm
&& !lp_disable_netbios() ) {
133 c_realm
= ads
->server
.workgroup
;
134 if (!c_realm
|| !*c_realm
) {
135 if ( use_own_domain
)
136 c_realm
= lp_workgroup();
139 if ( !c_realm
|| !*c_realm
) {
140 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
145 pstrcpy( realm
, c_realm
);
147 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
148 (got_realm
? "realm" : "domain"), realm
));
150 if ( !get_sorted_dc_list(realm
, &ip_list
, &count
, got_realm
) ) {
151 /* fall back to netbios if we can */
152 if ( got_realm
&& !lp_disable_netbios() ) {
160 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
161 for ( i
=0; i
<count
; i
++ ) {
162 /* since this is an ads conection request, default to LDAP_PORT is not set */
163 int port
= (ip_list
[i
].port
!=PORT_NONE
) ? ip_list
[i
].port
: LDAP_PORT
;
166 fstrcpy( server
, inet_ntoa(ip_list
[i
].ip
) );
168 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm
, server
)) )
171 if ( ads_try_connect(ads
, server
, port
) ) {
176 /* keep track of failures */
177 add_failed_connection_entry( realm
, server
, NT_STATUS_UNSUCCESSFUL
);
187 * Connect to the LDAP server
188 * @param ads Pointer to an existing ADS_STRUCT
189 * @return status of connection
191 ADS_STATUS
ads_connect(ADS_STRUCT
*ads
)
193 int version
= LDAP_VERSION3
;
196 ads
->last_attempt
= time(NULL
);
199 /* try with a URL based server */
201 if (ads
->server
.ldap_uri
&&
202 ads_try_connect_uri(ads
)) {
206 /* try with a user specified server */
207 if (ads
->server
.ldap_server
&&
208 ads_try_connect(ads
, ads
->server
.ldap_server
, LDAP_PORT
)) {
212 if (ads_find_dc(ads
)) {
216 return ADS_ERROR_SYSTEM(errno
?errno
:ENOENT
);
219 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads
->ldap_ip
)));
221 status
= ads_server_info(ads
);
222 if (!ADS_ERR_OK(status
)) {
223 DEBUG(1,("Failed to get ldap server info\n"));
227 ldap_set_option(ads
->ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
229 if (!ads
->auth
.user_name
) {
230 /* by default use the machine account */
232 fstrcpy(myname
, lp_netbios_name());
234 asprintf(&ads
->auth
.user_name
, "HOST/%s", myname
);
237 if (!ads
->auth
.realm
) {
238 ads
->auth
.realm
= strdup(ads
->config
.realm
);
241 if (!ads
->auth
.kdc_server
) {
242 ads
->auth
.kdc_server
= strdup(inet_ntoa(ads
->ldap_ip
));
246 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
247 to MIT kerberos to work (tridge) */
250 asprintf(&env
, "KRB5_KDC_ADDRESS_%s", ads
->config
.realm
);
251 setenv(env
, ads
->auth
.kdc_server
, 1);
256 if (ads
->auth
.flags
& ADS_AUTH_NO_BIND
) {
260 if (ads
->auth
.flags
& ADS_AUTH_ANON_BIND
) {
261 return ADS_ERROR(ldap_simple_bind_s( ads
->ld
, NULL
, NULL
));
264 if (ads
->auth
.flags
& ADS_AUTH_SIMPLE_BIND
) {
265 return ADS_ERROR(ldap_simple_bind_s( ads
->ld
, ads
->auth
.user_name
, ads
->auth
.password
));
268 return ads_sasl_bind(ads
);
272 Duplicate a struct berval into talloc'ed memory
274 static struct berval
*dup_berval(TALLOC_CTX
*ctx
, const struct berval
*in_val
)
276 struct berval
*value
;
278 if (!in_val
) return NULL
;
280 value
= talloc_zero(ctx
, struct berval
);
283 if (in_val
->bv_len
== 0) return value
;
285 value
->bv_len
= in_val
->bv_len
;
286 value
->bv_val
= talloc_memdup(ctx
, in_val
->bv_val
, in_val
->bv_len
);
291 Make a values list out of an array of (struct berval *)
293 static struct berval
**ads_dup_values(TALLOC_CTX
*ctx
,
294 const struct berval
**in_vals
)
296 struct berval
**values
;
299 if (!in_vals
) return NULL
;
300 for (i
=0; in_vals
[i
]; i
++); /* count values */
301 values
= (struct berval
**) talloc_zero(ctx
,
302 (i
+1)*sizeof(struct berval
*));
303 if (!values
) return NULL
;
305 for (i
=0; in_vals
[i
]; i
++) {
306 values
[i
] = dup_berval(ctx
, in_vals
[i
]);
312 UTF8-encode a values list out of an array of (char *)
314 static char **ads_push_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
319 if (!in_vals
) return NULL
;
320 for (i
=0; in_vals
[i
]; i
++); /* count values */
321 values
= talloc_zero_array_p(ctx
, char *, i
+1);
322 if (!values
) return NULL
;
324 for (i
=0; in_vals
[i
]; i
++) {
325 push_utf8_talloc(ctx
, &values
[i
], in_vals
[i
]);
331 Pull a (char *) array out of a UTF8-encoded values list
333 static char **ads_pull_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
338 if (!in_vals
) return NULL
;
339 for (i
=0; in_vals
[i
]; i
++); /* count values */
340 values
= talloc_zero_array_p(ctx
, char *, i
+1);
341 if (!values
) return NULL
;
343 for (i
=0; in_vals
[i
]; i
++) {
344 pull_utf8_talloc(ctx
, &values
[i
], in_vals
[i
]);
350 * Do a search with paged results. cookie must be null on the first
351 * call, and then returned on each subsequent call. It will be null
352 * again when the entire search is complete
353 * @param ads connection to ads server
354 * @param bind_path Base dn for the search
355 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
356 * @param expr Search expression - specified in local charset
357 * @param attrs Attributes to retrieve - specified in utf8 or ascii
358 * @param res ** which will contain results - free res* with ads_msgfree()
359 * @param count Number of entries retrieved on this page
360 * @param cookie The paged results cookie to be returned on subsequent calls
361 * @return status of search
363 ADS_STATUS
ads_do_paged_search(ADS_STRUCT
*ads
, const char *bind_path
,
364 int scope
, const char *expr
,
365 const char **attrs
, void **res
,
366 int *count
, void **cookie
)
369 char *utf8_expr
, *utf8_path
, **search_attrs
;
370 LDAPControl PagedResults
, NoReferrals
, *controls
[3], **rcontrols
;
371 BerElement
*cookie_be
= NULL
;
372 struct berval
*cookie_bv
= NULL
;
377 if (!(ctx
= talloc_init("ads_do_paged_search")))
378 return ADS_ERROR(LDAP_NO_MEMORY
);
380 /* 0 means the conversion worked but the result was empty
381 so we only fail if it's -1. In any case, it always
382 at least nulls out the dest */
383 if ((push_utf8_talloc(ctx
, &utf8_expr
, expr
) == (size_t)-1) ||
384 (push_utf8_talloc(ctx
, &utf8_path
, bind_path
) == (size_t)-1)) {
389 if (!attrs
|| !(*attrs
))
392 /* This would be the utf8-encoded version...*/
393 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
394 search_attrs
= str_list_copy(ctx
, attrs
);
395 if (search_attrs
== NULL
) {
402 /* Paged results only available on ldap v3 or later */
403 ldap_get_option(ads
->ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
404 if (version
< LDAP_VERSION3
) {
405 rc
= LDAP_NOT_SUPPORTED
;
409 cookie_be
= ber_alloc_t(LBER_USE_DER
);
410 if (cookie
&& *cookie
) {
411 ber_printf(cookie_be
, "{iO}", (ber_int_t
) 1000, *cookie
);
412 ber_bvfree(*cookie
); /* don't need it from last time */
415 ber_printf(cookie_be
, "{io}", (ber_int_t
) 1000, "", 0);
417 ber_flatten(cookie_be
, &cookie_bv
);
418 PagedResults
.ldctl_oid
= ADS_PAGE_CTL_OID
;
419 PagedResults
.ldctl_iscritical
= (char) 1;
420 PagedResults
.ldctl_value
.bv_len
= cookie_bv
->bv_len
;
421 PagedResults
.ldctl_value
.bv_val
= cookie_bv
->bv_val
;
423 NoReferrals
.ldctl_oid
= ADS_NO_REFERRALS_OID
;
424 NoReferrals
.ldctl_iscritical
= (char) 0;
425 NoReferrals
.ldctl_value
.bv_len
= 0;
426 NoReferrals
.ldctl_value
.bv_val
= "";
429 controls
[0] = &NoReferrals
;
430 controls
[1] = &PagedResults
;
435 /* we need to disable referrals as the openldap libs don't
436 handle them and paged results at the same time. Using them
437 together results in the result record containing the server
438 page control being removed from the result list (tridge/jmcd)
440 leaving this in despite the control that says don't generate
441 referrals, in case the server doesn't support it (jmcd)
443 ldap_set_option(ads
->ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
445 rc
= ldap_search_ext_s(ads
->ld
, utf8_path
, scope
, utf8_expr
,
446 search_attrs
, 0, controls
,
447 NULL
, NULL
, LDAP_NO_LIMIT
, (LDAPMessage
**)res
);
449 ber_free(cookie_be
, 1);
450 ber_bvfree(cookie_bv
);
453 DEBUG(3,("ldap_search_ext_s(%s) -> %s\n", expr
, ldap_err2string(rc
)));
457 rc
= ldap_parse_result(ads
->ld
, *res
, NULL
, NULL
, NULL
,
458 NULL
, &rcontrols
, 0);
464 for (i
=0; rcontrols
[i
]; i
++) {
465 if (strcmp(ADS_PAGE_CTL_OID
, rcontrols
[i
]->ldctl_oid
) == 0) {
466 cookie_be
= ber_init(&rcontrols
[i
]->ldctl_value
);
467 ber_scanf(cookie_be
,"{iO}", (ber_int_t
*) count
,
469 /* the berval is the cookie, but must be freed when
471 if (cookie_bv
->bv_len
) /* still more to do */
472 *cookie
=ber_bvdup(cookie_bv
);
475 ber_bvfree(cookie_bv
);
476 ber_free(cookie_be
, 1);
480 ldap_controls_free(rcontrols
);
484 /* if/when we decide to utf8-encode attrs, take out this next line */
485 str_list_free(&search_attrs
);
487 return ADS_ERROR(rc
);
492 * Get all results for a search. This uses ads_do_paged_search() to return
493 * all entries in a large search.
494 * @param ads connection to ads server
495 * @param bind_path Base dn for the search
496 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
497 * @param expr Search expression
498 * @param attrs Attributes to retrieve
499 * @param res ** which will contain results - free res* with ads_msgfree()
500 * @return status of search
502 ADS_STATUS
ads_do_search_all(ADS_STRUCT
*ads
, const char *bind_path
,
503 int scope
, const char *expr
,
504 const char **attrs
, void **res
)
510 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
, res
,
513 if (!ADS_ERR_OK(status
)) return status
;
518 LDAPMessage
*msg
, *next
;
520 status2
= ads_do_paged_search(ads
, bind_path
, scope
, expr
,
521 attrs
, &res2
, &count
, &cookie
);
523 if (!ADS_ERR_OK(status2
)) break;
525 /* this relies on the way that ldap_add_result_entry() works internally. I hope
526 that this works on all ldap libs, but I have only tested with openldap */
527 for (msg
= ads_first_entry(ads
, res2
); msg
; msg
= next
) {
528 next
= ads_next_entry(ads
, msg
);
529 ldap_add_result_entry((LDAPMessage
**)res
, msg
);
531 /* note that we do not free res2, as the memory is now
532 part of the main returned list */
539 * Run a function on all results for a search. Uses ads_do_paged_search() and
540 * runs the function as each page is returned, using ads_process_results()
541 * @param ads connection to ads server
542 * @param bind_path Base dn for the search
543 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
544 * @param expr Search expression - specified in local charset
545 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
546 * @param fn Function which takes attr name, values list, and data_area
547 * @param data_area Pointer which is passed to function on each call
548 * @return status of search
550 ADS_STATUS
ads_do_search_all_fn(ADS_STRUCT
*ads
, const char *bind_path
,
551 int scope
, const char *expr
, const char **attrs
,
552 BOOL(*fn
)(char *, void **, void *),
560 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
, &res
,
563 if (!ADS_ERR_OK(status
)) return status
;
565 ads_process_results(ads
, res
, fn
, data_area
);
566 ads_msgfree(ads
, res
);
569 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
,
570 &res
, &count
, &cookie
);
572 if (!ADS_ERR_OK(status
)) break;
574 ads_process_results(ads
, res
, fn
, data_area
);
575 ads_msgfree(ads
, res
);
582 * Do a search with a timeout.
583 * @param ads connection to ads server
584 * @param bind_path Base dn for the search
585 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
586 * @param expr Search expression
587 * @param attrs Attributes to retrieve
588 * @param res ** which will contain results - free res* with ads_msgfree()
589 * @return status of search
591 ADS_STATUS
ads_do_search(ADS_STRUCT
*ads
, const char *bind_path
, int scope
,
593 const char **attrs
, void **res
)
595 struct timeval timeout
;
597 char *utf8_expr
, *utf8_path
, **search_attrs
= NULL
;
600 if (!(ctx
= talloc_init("ads_do_search"))) {
601 DEBUG(1,("ads_do_search: talloc_init() failed!"));
602 return ADS_ERROR(LDAP_NO_MEMORY
);
605 /* 0 means the conversion worked but the result was empty
606 so we only fail if it's negative. In any case, it always
607 at least nulls out the dest */
608 if ((push_utf8_talloc(ctx
, &utf8_expr
, expr
) == (size_t)-1) ||
609 (push_utf8_talloc(ctx
, &utf8_path
, bind_path
) == (size_t)-1)) {
610 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
615 if (!attrs
|| !(*attrs
))
618 /* This would be the utf8-encoded version...*/
619 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
620 search_attrs
= str_list_copy(ctx
, attrs
);
621 if (search_attrs
== NULL
) {
622 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
628 timeout
.tv_sec
= ADS_SEARCH_TIMEOUT
;
632 /* see the note in ads_do_paged_search - we *must* disable referrals */
633 ldap_set_option(ads
->ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
635 rc
= ldap_search_ext_s(ads
->ld
, utf8_path
, scope
, utf8_expr
,
636 search_attrs
, 0, NULL
, NULL
,
637 &timeout
, LDAP_NO_LIMIT
, (LDAPMessage
**)res
);
639 if (rc
== LDAP_SIZELIMIT_EXCEEDED
) {
640 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
646 /* if/when we decide to utf8-encode attrs, take out this next line */
647 str_list_free(&search_attrs
);
648 return ADS_ERROR(rc
);
651 * Do a general ADS search
652 * @param ads connection to ads server
653 * @param res ** which will contain results - free res* with ads_msgfree()
654 * @param expr Search expression
655 * @param attrs Attributes to retrieve
656 * @return status of search
658 ADS_STATUS
ads_search(ADS_STRUCT
*ads
, void **res
,
662 return ads_do_search(ads
, ads
->config
.bind_path
, LDAP_SCOPE_SUBTREE
,
667 * Do a search on a specific DistinguishedName
668 * @param ads connection to ads server
669 * @param res ** which will contain results - free res* with ads_msgfree()
670 * @param dn DistinguishName to search
671 * @param attrs Attributes to retrieve
672 * @return status of search
674 ADS_STATUS
ads_search_dn(ADS_STRUCT
*ads
, void **res
,
678 return ads_do_search(ads
, dn
, LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, res
);
682 * Free up memory from a ads_search
683 * @param ads connection to ads server
684 * @param msg Search results to free
686 void ads_msgfree(ADS_STRUCT
*ads
, void *msg
)
693 * Free up memory from various ads requests
694 * @param ads connection to ads server
695 * @param mem Area to free
697 void ads_memfree(ADS_STRUCT
*ads
, void *mem
)
703 * Get a dn from search results
704 * @param ads connection to ads server
705 * @param msg Search result
708 char *ads_get_dn(ADS_STRUCT
*ads
, void *msg
)
710 char *utf8_dn
, *unix_dn
;
712 utf8_dn
= ldap_get_dn(ads
->ld
, msg
);
715 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
719 if (pull_utf8_allocate(&unix_dn
, utf8_dn
) == (size_t)-1) {
720 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
724 ldap_memfree(utf8_dn
);
729 * Find a machine account given a hostname
730 * @param ads connection to ads server
731 * @param res ** which will contain results - free res* with ads_msgfree()
732 * @param host Hostname to search for
733 * @return status of search
735 ADS_STATUS
ads_find_machine_acct(ADS_STRUCT
*ads
, void **res
, const char *host
)
739 const char *attrs
[] = {"*", "nTSecurityDescriptor", NULL
};
741 /* the easiest way to find a machine account anywhere in the tree
742 is to look for hostname$ */
743 if (asprintf(&expr
, "(samAccountName=%s$)", host
) == -1) {
744 DEBUG(1, ("asprintf failed!\n"));
745 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
748 status
= ads_search(ads
, res
, expr
, attrs
);
754 * Initialize a list of mods to be used in a modify request
755 * @param ctx An initialized TALLOC_CTX
756 * @return allocated ADS_MODLIST
758 ADS_MODLIST
ads_init_mods(TALLOC_CTX
*ctx
)
760 #define ADS_MODLIST_ALLOC_SIZE 10
763 if ((mods
= talloc_zero_array_p(ctx
, LDAPMod
*, ADS_MODLIST_ALLOC_SIZE
+ 1))) {
764 /* -1 is safety to make sure we don't go over the end.
765 need to reset it to NULL before doing ldap modify */
766 mods
[ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
774 add an attribute to the list, with values list already constructed
776 static ADS_STATUS
ads_modlist_add(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
777 int mod_op
, const char *name
,
781 LDAPMod
**modlist
= (LDAPMod
**) *mods
;
782 struct berval
**ber_values
= NULL
;
783 char **char_values
= NULL
;
786 mod_op
= LDAP_MOD_DELETE
;
788 if (mod_op
& LDAP_MOD_BVALUES
)
789 ber_values
= ads_dup_values(ctx
,
790 (const struct berval
**)invals
);
792 char_values
= ads_push_strvals(ctx
,
793 (const char **) invals
);
796 /* find the first empty slot */
797 for (curmod
=0; modlist
[curmod
] && modlist
[curmod
] != (LDAPMod
*) -1;
799 if (modlist
[curmod
] == (LDAPMod
*) -1) {
800 if (!(modlist
= talloc_realloc(modlist
,
801 (curmod
+ADS_MODLIST_ALLOC_SIZE
+1)*sizeof(LDAPMod
*))))
802 return ADS_ERROR(LDAP_NO_MEMORY
);
803 memset(&modlist
[curmod
], 0,
804 ADS_MODLIST_ALLOC_SIZE
*sizeof(LDAPMod
*));
805 modlist
[curmod
+ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
809 if (!(modlist
[curmod
] = talloc_zero(ctx
, LDAPMod
)))
810 return ADS_ERROR(LDAP_NO_MEMORY
);
811 modlist
[curmod
]->mod_type
= talloc_strdup(ctx
, name
);
812 if (mod_op
& LDAP_MOD_BVALUES
) {
813 modlist
[curmod
]->mod_bvalues
= ber_values
;
814 } else if (mod_op
& LDAP_MOD_DELETE
) {
815 modlist
[curmod
]->mod_values
= NULL
;
817 modlist
[curmod
]->mod_values
= char_values
;
820 modlist
[curmod
]->mod_op
= mod_op
;
821 return ADS_ERROR(LDAP_SUCCESS
);
825 * Add a single string value to a mod list
826 * @param ctx An initialized TALLOC_CTX
827 * @param mods An initialized ADS_MODLIST
828 * @param name The attribute name to add
829 * @param val The value to add - NULL means DELETE
830 * @return ADS STATUS indicating success of add
832 ADS_STATUS
ads_mod_str(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
833 const char *name
, const char *val
)
835 const char *values
[2];
841 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
842 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
, name
,
843 (const void **) values
);
847 * Add an array of string values to a mod list
848 * @param ctx An initialized TALLOC_CTX
849 * @param mods An initialized ADS_MODLIST
850 * @param name The attribute name to add
851 * @param vals The array of string values to add - NULL means DELETE
852 * @return ADS STATUS indicating success of add
854 ADS_STATUS
ads_mod_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
855 const char *name
, const char **vals
)
858 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
859 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
,
860 name
, (const void **) vals
);
864 * Add a single ber-encoded value to a mod list
865 * @param ctx An initialized TALLOC_CTX
866 * @param mods An initialized ADS_MODLIST
867 * @param name The attribute name to add
868 * @param val The value to add - NULL means DELETE
869 * @return ADS STATUS indicating success of add
871 static ADS_STATUS
ads_mod_ber(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
872 const char *name
, const struct berval
*val
)
874 const struct berval
*values
[2];
879 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
880 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
|LDAP_MOD_BVALUES
,
881 name
, (const void **) values
);
885 * Perform an ldap modify
886 * @param ads connection to ads server
887 * @param mod_dn DistinguishedName to modify
888 * @param mods list of modifications to perform
889 * @return status of modify
891 ADS_STATUS
ads_gen_mod(ADS_STRUCT
*ads
, const char *mod_dn
, ADS_MODLIST mods
)
894 char *utf8_dn
= NULL
;
896 this control is needed to modify that contains a currently
897 non-existent attribute (but allowable for the object) to run
899 LDAPControl PermitModify
= {
900 ADS_PERMIT_MODIFY_OID
,
903 LDAPControl
*controls
[2];
905 controls
[0] = &PermitModify
;
908 if (push_utf8_allocate(&utf8_dn
, mod_dn
) == -1) {
909 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
912 /* find the end of the list, marked by NULL or -1 */
913 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
914 /* make sure the end of the list is NULL */
916 ret
= ldap_modify_ext_s(ads
->ld
, utf8_dn
,
917 (LDAPMod
**) mods
, controls
, NULL
);
919 return ADS_ERROR(ret
);
923 * Perform an ldap add
924 * @param ads connection to ads server
925 * @param new_dn DistinguishedName to add
926 * @param mods list of attributes and values for DN
927 * @return status of add
929 ADS_STATUS
ads_gen_add(ADS_STRUCT
*ads
, const char *new_dn
, ADS_MODLIST mods
)
932 char *utf8_dn
= NULL
;
934 if (push_utf8_allocate(&utf8_dn
, new_dn
) == -1) {
935 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
936 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
939 /* find the end of the list, marked by NULL or -1 */
940 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
941 /* make sure the end of the list is NULL */
944 ret
= ldap_add_s(ads
->ld
, utf8_dn
, mods
);
946 return ADS_ERROR(ret
);
950 * Delete a DistinguishedName
951 * @param ads connection to ads server
952 * @param new_dn DistinguishedName to delete
953 * @return status of delete
955 ADS_STATUS
ads_del_dn(ADS_STRUCT
*ads
, char *del_dn
)
958 char *utf8_dn
= NULL
;
959 if (push_utf8_allocate(&utf8_dn
, del_dn
) == -1) {
960 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
961 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
964 ret
= ldap_delete_s(ads
->ld
, utf8_dn
);
965 return ADS_ERROR(ret
);
969 * Build an org unit string
970 * if org unit is Computers or blank then assume a container, otherwise
971 * assume a \ separated list of organisational units
972 * @param org_unit Organizational unit
973 * @return org unit string - caller must free
975 char *ads_ou_string(const char *org_unit
)
977 if (!org_unit
|| !*org_unit
|| strequal(org_unit
, "Computers")) {
978 return strdup("cn=Computers");
981 return ads_build_path(org_unit
, "\\/", "ou=", 1);
987 add a machine account to the ADS server
989 static ADS_STATUS
ads_add_machine_acct(ADS_STRUCT
*ads
, const char *hostname
,
990 uint32_t account_type
,
991 const char *org_unit
)
993 ADS_STATUS ret
, status
;
994 char *host_spn
, *host_upn
, *new_dn
, *samAccountName
, *controlstr
;
998 const char *objectClass
[] = {"top", "person", "organizationalPerson",
999 "user", "computer", NULL
};
1000 const char *servicePrincipalName
[5] = {NULL
, NULL
, NULL
, NULL
, NULL
};
1002 uint_t acct_control
;
1006 status
= ads_find_machine_acct(ads
, (void **)&res
, hostname
);
1007 if (ADS_ERR_OK(status
) && ads_count_replies(ads
, res
) == 1) {
1008 DEBUG(0, ("Host account for %s already exists - modifying old account\n", hostname
));
1012 if (!(ctx
= talloc_init("machine_account")))
1013 return ADS_ERROR(LDAP_NO_MEMORY
);
1015 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1017 if (!(host_spn
= talloc_asprintf(ctx
, "HOST/%s", hostname
)))
1019 if (!(host_upn
= talloc_asprintf(ctx
, "%s@%s", host_spn
, ads
->config
.realm
)))
1021 ou_str
= ads_ou_string(org_unit
);
1023 DEBUG(1, ("ads_ou_string returned NULL (malloc failure?)\n"));
1026 new_dn
= talloc_asprintf(ctx
, "cn=%s,%s,%s", hostname
, ou_str
,
1027 ads
->config
.bind_path
);
1028 servicePrincipalName
[0] = talloc_asprintf(ctx
, "HOST/%s", hostname
);
1029 psp
= talloc_asprintf(ctx
, "HOST/%s.%s",
1032 strlower_m(&psp
[5]);
1033 servicePrincipalName
[1] = psp
;
1034 servicePrincipalName
[2] = talloc_asprintf(ctx
, "CIFS/%s", hostname
);
1035 psp2
= talloc_asprintf(ctx
, "CIFS/%s.%s",
1038 strlower_m(&psp2
[5]);
1039 servicePrincipalName
[3] = psp2
;
1045 if (!(samAccountName
= talloc_asprintf(ctx
, "%s$", hostname
)))
1048 acct_control
= account_type
| UF_DONT_EXPIRE_PASSWD
;
1049 #ifndef ENCTYPE_ARCFOUR_HMAC
1050 acct_control
|= UF_USE_DES_KEY_ONLY
;
1053 if (!(controlstr
= talloc_asprintf(ctx
, "%u", acct_control
)))
1056 if (!(mods
= ads_init_mods(ctx
)))
1060 ads_mod_str(ctx
, &mods
, "cn", hostname
);
1061 ads_mod_str(ctx
, &mods
, "sAMAccountName", samAccountName
);
1062 ads_mod_str(ctx
, &mods
, "userAccountControl", controlstr
);
1063 ads_mod_strlist(ctx
, &mods
, "objectClass", objectClass
);
1065 ads_mod_str(ctx
, &mods
, "dNSHostName", hostname
);
1066 ads_mod_str(ctx
, &mods
, "userPrincipalName", host_upn
);
1067 ads_mod_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1068 ads_mod_str(ctx
, &mods
, "operatingSystem", "Samba");
1069 ads_mod_str(ctx
, &mods
, "operatingSystemVersion", SAMBA_VERSION_STRING
);
1072 ret
= ads_gen_add(ads
, new_dn
, mods
);
1074 ret
= ads_gen_mod(ads
, new_dn
, mods
);
1076 if (!ADS_ERR_OK(ret
))
1079 /* Do not fail if we can't set security descriptor
1080 * it shouldn't be mandatory and probably we just
1081 * don't have enough rights to do it.
1084 status
= ads_set_machine_sd(ads
, hostname
, new_dn
);
1086 if (!ADS_ERR_OK(status
)) {
1087 DEBUG(0, ("Warning: ads_set_machine_sd: %s\n",
1088 ads_errstr(status
)));
1097 dump a binary result from ldap
1099 static void dump_binary(const char *field
, struct berval
**values
)
1102 for (i
=0; values
[i
]; i
++) {
1103 printf("%s: ", field
);
1104 for (j
=0; j
<values
[i
]->bv_len
; j
++) {
1105 printf("%02X", (uint8_t)values
[i
]->bv_val
[j
]);
1118 static void dump_guid(const char *field
, struct berval
**values
)
1122 for (i
=0; values
[i
]; i
++) {
1123 memcpy(guid
.info
, values
[i
]->bv_val
, sizeof(guid
.info
));
1124 printf("%s: %s\n", field
, smb_uuid_string_static(guid
));
1129 dump a sid result from ldap
1131 static void dump_sid(const char *field
, struct berval
**values
)
1134 for (i
=0; values
[i
]; i
++) {
1136 sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &sid
);
1137 printf("%s: %s\n", field
, sid_string_static(&sid
));
1142 dump ntSecurityDescriptor
1144 static void dump_sd(const char *filed
, struct berval
**values
)
1149 TALLOC_CTX
*ctx
= 0;
1151 if (!(ctx
= talloc_init("sec_io_desc")))
1155 prs_init(&ps
, values
[0]->bv_len
, ctx
, UNMARSHALL
);
1156 prs_copy_data_in(&ps
, values
[0]->bv_val
, values
[0]->bv_len
);
1157 prs_set_offset(&ps
,0);
1160 if (!sec_io_desc("sd", &psd
, &ps
, 1)) {
1165 if (psd
) ads_disp_sd(psd
);
1172 dump a string result from ldap
1174 static void dump_string(const char *field
, char **values
)
1177 for (i
=0; values
[i
]; i
++) {
1178 printf("%s: %s\n", field
, values
[i
]);
1183 dump a field from LDAP on stdout
1187 static BOOL
ads_dump_field(char *field
, void **values
, void *data_area
)
1192 void (*handler
)(const char *, struct berval
**);
1194 {"objectGUID", False
, dump_guid
},
1195 {"nTSecurityDescriptor", False
, dump_sd
},
1196 {"dnsRecord", False
, dump_binary
},
1197 {"objectSid", False
, dump_sid
},
1198 {"tokenGroups", False
, dump_sid
},
1203 if (!field
) { /* must be end of an entry */
1208 for (i
=0; handlers
[i
].name
; i
++) {
1209 if (StrCaseCmp(handlers
[i
].name
, field
) == 0) {
1210 if (!values
) /* first time, indicate string or not */
1211 return handlers
[i
].string
;
1212 handlers
[i
].handler(field
, (struct berval
**) values
);
1216 if (!handlers
[i
].name
) {
1217 if (!values
) /* first time, indicate string conversion */
1219 dump_string(field
, (char **)values
);
1225 * Dump a result from LDAP on stdout
1226 * used for debugging
1227 * @param ads connection to ads server
1228 * @param res Results to dump
1231 void ads_dump(ADS_STRUCT
*ads
, void *res
)
1233 ads_process_results(ads
, res
, ads_dump_field
, NULL
);
1237 * Walk through results, calling a function for each entry found.
1238 * The function receives a field name, a berval * array of values,
1239 * and a data area passed through from the start. The function is
1240 * called once with null for field and values at the end of each
1242 * @param ads connection to ads server
1243 * @param res Results to process
1244 * @param fn Function for processing each result
1245 * @param data_area user-defined area to pass to function
1247 void ads_process_results(ADS_STRUCT
*ads
, void *res
,
1248 BOOL(*fn
)(char *, void **, void *),
1254 if (!(ctx
= talloc_init("ads_process_results")))
1257 for (msg
= ads_first_entry(ads
, res
); msg
;
1258 msg
= ads_next_entry(ads
, msg
)) {
1262 for (utf8_field
=ldap_first_attribute(ads
->ld
,
1263 (LDAPMessage
*)msg
,&b
);
1265 utf8_field
=ldap_next_attribute(ads
->ld
,
1266 (LDAPMessage
*)msg
,b
)) {
1267 struct berval
**ber_vals
;
1268 char **str_vals
, **utf8_vals
;
1272 pull_utf8_talloc(ctx
, &field
, utf8_field
);
1273 string
= fn(field
, NULL
, data_area
);
1276 utf8_vals
= ldap_get_values(ads
->ld
,
1277 (LDAPMessage
*)msg
, field
);
1278 str_vals
= ads_pull_strvals(ctx
,
1279 (const char **) utf8_vals
);
1280 fn(field
, (void **) str_vals
, data_area
);
1281 ldap_value_free(utf8_vals
);
1283 ber_vals
= ldap_get_values_len(ads
->ld
,
1284 (LDAPMessage
*)msg
, field
);
1285 fn(field
, (void **) ber_vals
, data_area
);
1287 ldap_value_free_len(ber_vals
);
1289 ldap_memfree(utf8_field
);
1292 talloc_destroy_pool(ctx
);
1293 fn(NULL
, NULL
, data_area
); /* completed an entry */
1300 * count how many replies are in a LDAPMessage
1301 * @param ads connection to ads server
1302 * @param res Results to count
1303 * @return number of replies
1305 int ads_count_replies(ADS_STRUCT
*ads
, void *res
)
1307 return ldap_count_entries(ads
->ld
, (LDAPMessage
*)res
);
1311 * Join a machine to a realm
1312 * Creates the machine account and sets the machine password
1313 * @param ads connection to ads server
1314 * @param hostname name of host to add
1315 * @param org_unit Organizational unit to place machine in
1316 * @return status of join
1318 ADS_STATUS
ads_join_realm(ADS_STRUCT
*ads
, const char *hostname
,
1319 uint32_t account_type
, const char *org_unit
)
1325 /* hostname must be lowercase */
1326 host
= strdup(hostname
);
1330 status = ads_find_machine_acct(ads, (void **)&res, host);
1331 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1332 DEBUG(0, ("Host account for %s already exists - deleting old account\n", host));
1333 status = ads_leave_realm(ads, host);
1334 if (!ADS_ERR_OK(status)) {
1335 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
1336 host, ads->config.realm));
1342 status
= ads_add_machine_acct(ads
, host
, account_type
, org_unit
);
1343 if (!ADS_ERR_OK(status
)) {
1344 DEBUG(0, ("ads_add_machine_acct: %s\n", ads_errstr(status
)));
1348 status
= ads_find_machine_acct(ads
, (void **)&res
, host
);
1349 if (!ADS_ERR_OK(status
)) {
1350 DEBUG(0, ("Host account test failed\n"));
1360 * Delete a machine from the realm
1361 * @param ads connection to ads server
1362 * @param hostname Machine to remove
1363 * @return status of delete
1365 ADS_STATUS
ads_leave_realm(ADS_STRUCT
*ads
, const char *hostname
)
1369 char *hostnameDN
, *host
;
1372 /* hostname must be lowercase */
1373 host
= strdup(hostname
);
1376 status
= ads_find_machine_acct(ads
, &res
, host
);
1377 if (!ADS_ERR_OK(status
)) {
1378 DEBUG(0, ("Host account for %s does not exist.\n", host
));
1382 msg
= ads_first_entry(ads
, res
);
1384 return ADS_ERROR_SYSTEM(ENOENT
);
1387 hostnameDN
= ads_get_dn(ads
, (LDAPMessage
*)msg
);
1388 rc
= ldap_delete_s(ads
->ld
, hostnameDN
);
1389 ads_memfree(ads
, hostnameDN
);
1390 if (rc
!= LDAP_SUCCESS
) {
1391 return ADS_ERROR(rc
);
1394 status
= ads_find_machine_acct(ads
, &res
, host
);
1395 if (ADS_ERR_OK(status
) && ads_count_replies(ads
, res
) == 1) {
1396 DEBUG(0, ("Failed to remove host account.\n"));
1406 * add machine account to existing security descriptor
1407 * @param ads connection to ads server
1408 * @param hostname machine to add
1409 * @param dn DN of security descriptor
1412 ADS_STATUS
ads_set_machine_sd(ADS_STRUCT
*ads
, const char *hostname
, char *dn
)
1414 const char *attrs
[] = {"nTSecurityDescriptor", "objectSid", 0};
1417 struct berval bval
= {0, NULL
};
1419 char *escaped_hostname
= escape_ldap_string_alloc(hostname
);
1421 LDAPMessage
*res
= 0;
1422 LDAPMessage
*msg
= 0;
1423 ADS_MODLIST mods
= 0;
1428 SEC_DESC
*psd
= NULL
;
1429 TALLOC_CTX
*ctx
= NULL
;
1431 /* Avoid segmentation fault in prs_mem_free if
1432 * we have to bail out before prs_init */
1433 ps_wire
.is_dynamic
= False
;
1435 if (!ads
) return ADS_ERROR(LDAP_SERVER_DOWN
);
1437 ret
= ADS_ERROR(LDAP_SUCCESS
);
1439 if (!escaped_hostname
) {
1440 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1443 if (asprintf(&expr
, "(samAccountName=%s$)", escaped_hostname
) == -1) {
1444 DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n"));
1445 SAFE_FREE(escaped_hostname
);
1446 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1449 SAFE_FREE(escaped_hostname
);
1451 ret
= ads_search(ads
, (void *) &res
, expr
, attrs
);
1453 if (!ADS_ERR_OK(ret
)) return ret
;
1455 if ( !(msg
= ads_first_entry(ads
, res
) )) {
1456 ret
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
1457 goto ads_set_sd_error
;
1460 if (!ads_pull_sid(ads
, msg
, attrs
[1], &sid
)) {
1461 ret
= ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
1462 goto ads_set_sd_error
;
1465 if (!(ctx
= talloc_init("sec_io_desc"))) {
1466 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1467 goto ads_set_sd_error
;
1470 if (!ads_pull_sd(ads
, ctx
, msg
, attrs
[0], &psd
)) {
1471 ret
= ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
1472 goto ads_set_sd_error
;
1475 status
= sec_desc_add_sid(ctx
, &psd
, &sid
, SEC_RIGHTS_FULL_CTRL
, &sd_size
);
1477 if (!NT_STATUS_IS_OK(status
)) {
1478 ret
= ADS_ERROR_NT(status
);
1479 goto ads_set_sd_error
;
1482 if (!prs_init(&ps_wire
, sd_size
, ctx
, MARSHALL
)) {
1483 ret
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1486 if (!sec_io_desc("sd_wire", &psd
, &ps_wire
, 1)) {
1487 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1488 goto ads_set_sd_error
;
1492 file_save("/tmp/sec_desc.new", ps_wire
.data_p
, sd_size
);
1494 if (!(mods
= ads_init_mods(ctx
))) return ADS_ERROR(LDAP_NO_MEMORY
);
1496 bval
.bv_len
= prs_offset(&ps_wire
);
1497 bval
.bv_val
= talloc(ctx
, bval
.bv_len
);
1499 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1500 goto ads_set_sd_error
;
1503 prs_set_offset(&ps_wire
, 0);
1505 if (!prs_copy_data_out(bval
.bv_val
, &ps_wire
, bval
.bv_len
)) {
1506 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1507 goto ads_set_sd_error
;
1510 ret
= ads_mod_ber(ctx
, &mods
, attrs
[0], &bval
);
1511 if (ADS_ERR_OK(ret
)) {
1512 ret
= ads_gen_mod(ads
, dn
, mods
);
1516 ads_msgfree(ads
, res
);
1517 prs_mem_free(&ps_wire
);
1523 * pull the first entry from a ADS result
1524 * @param ads connection to ads server
1525 * @param res Results of search
1526 * @return first entry from result
1528 void *ads_first_entry(ADS_STRUCT
*ads
, void *res
)
1530 return (void *)ldap_first_entry(ads
->ld
, (LDAPMessage
*)res
);
1534 * pull the next entry from a ADS result
1535 * @param ads connection to ads server
1536 * @param res Results of search
1537 * @return next entry from result
1539 void *ads_next_entry(ADS_STRUCT
*ads
, void *res
)
1541 return (void *)ldap_next_entry(ads
->ld
, (LDAPMessage
*)res
);
1545 * pull a single string from a ADS result
1546 * @param ads connection to ads server
1547 * @param mem_ctx TALLOC_CTX to use for allocating result string
1548 * @param msg Results of search
1549 * @param field Attribute to retrieve
1550 * @return Result string in talloc context
1552 char *ads_pull_string(ADS_STRUCT
*ads
,
1553 TALLOC_CTX
*mem_ctx
, void *msg
, const char *field
)
1560 values
= ldap_get_values(ads
->ld
, msg
, field
);
1565 rc
= pull_utf8_talloc(mem_ctx
, &ux_string
,
1567 if (rc
!= (size_t)-1)
1571 ldap_value_free(values
);
1576 * pull an array of strings from a ADS result
1577 * @param ads connection to ads server
1578 * @param mem_ctx TALLOC_CTX to use for allocating result string
1579 * @param msg Results of search
1580 * @param field Attribute to retrieve
1581 * @return Result strings in talloc context
1583 char **ads_pull_strings(ADS_STRUCT
*ads
,
1584 TALLOC_CTX
*mem_ctx
, void *msg
, const char *field
,
1591 values
= ldap_get_values(ads
->ld
, msg
, field
);
1595 *num_values
= ldap_count_values(values
);
1597 ret
= talloc_array(mem_ctx
, char *, *num_values
+1);
1599 ldap_value_free(values
);
1603 for (i
=0;i
<*num_values
;i
++) {
1604 if (pull_utf8_talloc(mem_ctx
, &ret
[i
], values
[i
]) == -1) {
1605 ldap_value_free(values
);
1611 ldap_value_free(values
);
1616 * pull an array of strings from a ADS result
1617 * (handle large multivalue attributes with range retrieval)
1618 * @param ads connection to ads server
1619 * @param mem_ctx TALLOC_CTX to use for allocating result string
1620 * @param msg Results of search
1621 * @param field Attribute to retrieve
1622 * @param current_strings strings returned by a previous call to this function
1623 * @param next_attribute The next query should ask for this attribute
1624 * @param num_values How many values did we get this time?
1625 * @param more_values Are there more values to get?
1626 * @return Result strings in talloc context
1628 char **ads_pull_strings_range(ADS_STRUCT
*ads
,
1629 TALLOC_CTX
*mem_ctx
,
1630 void *msg
, const char *field
,
1631 char **current_strings
,
1632 const char **next_attribute
,
1633 size_t *num_strings
,
1637 char *expected_range_attrib
, *range_attr
;
1638 BerElement
*ptr
= NULL
;
1641 size_t num_new_strings
;
1642 unsigned long int range_start
;
1643 unsigned long int range_end
;
1645 /* we might have been given the whole lot anyway */
1646 if ((strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
, num_strings
))) {
1647 *more_strings
= False
;
1651 expected_range_attrib
= talloc_asprintf(mem_ctx
, "%s;Range=", field
);
1653 /* look for Range result */
1654 for (attr
= ldap_first_attribute(ads
->ld
, (LDAPMessage
*)msg
, &ptr
);
1656 attr
= ldap_next_attribute(ads
->ld
, (LDAPMessage
*)msg
, ptr
)) {
1657 /* we ignore the fact that this is utf8, as all attributes are ascii... */
1658 if (strncasecmp(attr
, expected_range_attrib
, strlen(expected_range_attrib
)) == 0) {
1666 /* nothing here - this field is just empty */
1667 *more_strings
= False
;
1671 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-%lu",
1672 &range_start
, &range_end
) == 2) {
1673 *more_strings
= True
;
1675 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-*",
1676 &range_start
) == 1) {
1677 *more_strings
= False
;
1679 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
1681 ldap_memfree(range_attr
);
1682 *more_strings
= False
;
1687 if ((*num_strings
) != range_start
) {
1688 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
1689 " - aborting range retreival\n",
1690 range_attr
, *num_strings
+ 1, range_start
));
1691 ldap_memfree(range_attr
);
1692 *more_strings
= False
;
1696 new_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, range_attr
, &num_new_strings
);
1698 if (*more_strings
&& ((*num_strings
+ num_new_strings
) != (range_end
+ 1))) {
1699 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
1700 "strings in this bunch, but we only got %lu - aborting range retreival\n",
1701 range_attr
, (unsigned long int)range_end
- range_start
+ 1,
1702 (unsigned long int)num_new_strings
));
1703 ldap_memfree(range_attr
);
1704 *more_strings
= False
;
1708 strings
= talloc_realloc(current_strings
,
1709 sizeof(*current_strings
) *
1710 (*num_strings
+ num_new_strings
));
1712 if (strings
== NULL
) {
1713 ldap_memfree(range_attr
);
1714 *more_strings
= False
;
1718 memcpy(&strings
[*num_strings
], new_strings
,
1719 sizeof(*new_strings
) * num_new_strings
);
1721 (*num_strings
) += num_new_strings
;
1723 if (*more_strings
) {
1724 *next_attribute
= talloc_asprintf(mem_ctx
,
1725 "member;range=%d-*",
1728 if (!*next_attribute
) {
1729 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
1730 ldap_memfree(range_attr
);
1731 *more_strings
= False
;
1736 ldap_memfree(range_attr
);
1742 * pull a single uint32_t from a ADS result
1743 * @param ads connection to ads server
1744 * @param msg Results of search
1745 * @param field Attribute to retrieve
1746 * @param v Pointer to int to store result
1747 * @return boolean inidicating success
1749 BOOL
ads_pull_uint32_t(ADS_STRUCT
*ads
,
1750 void *msg
, const char *field
, uint32_t *v
)
1754 values
= ldap_get_values(ads
->ld
, msg
, field
);
1758 ldap_value_free(values
);
1762 *v
= atoi(values
[0]);
1763 ldap_value_free(values
);
1768 * pull a single objectGUID from an ADS result
1769 * @param ads connection to ADS server
1770 * @param msg results of search
1771 * @param guid 37-byte area to receive text guid
1772 * @return boolean indicating success
1774 BOOL
ads_pull_guid(ADS_STRUCT
*ads
,
1775 void *msg
, GUID
*guid
)
1779 values
= ldap_get_values(ads
->ld
, msg
, "objectGUID");
1784 memcpy(guid
, values
[0], sizeof(GUID
));
1785 ldap_value_free(values
);
1788 ldap_value_free(values
);
1795 * pull a single DOM_SID from a ADS result
1796 * @param ads connection to ads server
1797 * @param msg Results of search
1798 * @param field Attribute to retrieve
1799 * @param sid Pointer to sid to store result
1800 * @return boolean inidicating success
1802 BOOL
ads_pull_sid(ADS_STRUCT
*ads
,
1803 void *msg
, const char *field
, DOM_SID
*sid
)
1805 struct berval
**values
;
1808 values
= ldap_get_values_len(ads
->ld
, msg
, field
);
1814 ret
= sid_parse(values
[0]->bv_val
, values
[0]->bv_len
, sid
);
1816 ldap_value_free_len(values
);
1821 * pull an array of DOM_SIDs from a ADS result
1822 * @param ads connection to ads server
1823 * @param mem_ctx TALLOC_CTX for allocating sid array
1824 * @param msg Results of search
1825 * @param field Attribute to retrieve
1826 * @param sids pointer to sid array to allocate
1827 * @return the count of SIDs pulled
1829 int ads_pull_sids(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
1830 void *msg
, const char *field
, DOM_SID
**sids
)
1832 struct berval
**values
;
1836 values
= ldap_get_values_len(ads
->ld
, msg
, field
);
1841 for (i
=0; values
[i
]; i
++)
1844 (*sids
) = talloc_array(mem_ctx
, DOM_SID
, i
);
1846 ldap_value_free_len(values
);
1851 for (i
=0; values
[i
]; i
++) {
1852 ret
= sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &(*sids
)[count
]);
1855 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid
, &(*sids
)[count
])));
1860 ldap_value_free_len(values
);
1865 * pull a SEC_DESC from a ADS result
1866 * @param ads connection to ads server
1867 * @param mem_ctx TALLOC_CTX for allocating sid array
1868 * @param msg Results of search
1869 * @param field Attribute to retrieve
1870 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
1871 * @return boolean inidicating success
1873 BOOL
ads_pull_sd(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
1874 void *msg
, const char *field
, SEC_DESC
**sd
)
1876 struct berval
**values
;
1880 values
= ldap_get_values_len(ads
->ld
, msg
, field
);
1882 if (!values
) return False
;
1885 prs_init(&ps
, values
[0]->bv_len
, mem_ctx
, UNMARSHALL
);
1886 prs_copy_data_in(&ps
, values
[0]->bv_val
, values
[0]->bv_len
);
1887 prs_set_offset(&ps
,0);
1889 ret
= sec_io_desc("sd", sd
, &ps
, 1);
1892 ldap_value_free_len(values
);
1897 * in order to support usernames longer than 21 characters we need to
1898 * use both the sAMAccountName and the userPrincipalName attributes
1899 * It seems that not all users have the userPrincipalName attribute set
1901 * @param ads connection to ads server
1902 * @param mem_ctx TALLOC_CTX for allocating sid array
1903 * @param msg Results of search
1904 * @return the username
1906 char *ads_pull_username(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, void *msg
)
1910 ret
= ads_pull_string(ads
, mem_ctx
, msg
, "userPrincipalName");
1911 if (ret
&& (p
= strchr(ret
, '@'))) {
1915 return ads_pull_string(ads
, mem_ctx
, msg
, "sAMAccountName");
1920 * find the update serial number - this is the core of the ldap cache
1921 * @param ads connection to ads server
1922 * @param ads connection to ADS server
1923 * @param usn Pointer to retrieved update serial number
1924 * @return status of search
1926 ADS_STATUS
ads_USN(ADS_STRUCT
*ads
, uint32_t *usn
)
1928 const char *attrs
[] = {"highestCommittedUSN", NULL
};
1932 status
= ads_do_search_retry(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
1933 if (!ADS_ERR_OK(status
))
1936 if (ads_count_replies(ads
, res
) != 1) {
1937 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
1940 ads_pull_uint32_t(ads
, res
, "highestCommittedUSN", usn
);
1941 ads_msgfree(ads
, res
);
1945 /* parse a ADS timestring - typical string is
1946 '20020917091222.0Z0' which means 09:12.22 17th September
1948 static time_t ads_parse_time(const char *str
)
1954 if (sscanf(str
, "%4d%2d%2d%2d%2d%2d",
1955 &tm
.tm_year
, &tm
.tm_mon
, &tm
.tm_mday
,
1956 &tm
.tm_hour
, &tm
.tm_min
, &tm
.tm_sec
) != 6) {
1967 * Find the servers name and realm - this can be done before authentication
1968 * The ldapServiceName field on w2k looks like this:
1969 * vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
1970 * @param ads connection to ads server
1971 * @return status of search
1973 ADS_STATUS
ads_server_info(ADS_STRUCT
*ads
)
1975 const char *attrs
[] = {"ldapServiceName", "currentTime", NULL
};
1983 if (!(ctx
= talloc_init("ads_server_info"))) {
1984 return ADS_ERROR(LDAP_NO_MEMORY
);
1987 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
1988 if (!ADS_ERR_OK(status
)) return status
;
1990 value
= ads_pull_string(ads
, ctx
, res
, "ldapServiceName");
1992 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
1995 timestr
= ads_pull_string(ads
, ctx
, res
, "currentTime");
1997 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2002 p
= strchr(value
, ':');
2005 DEBUG(1, ("ads_server_info: returned ldap server name did not contain a ':' "
2006 "so was deemed invalid\n"));
2007 return ADS_ERROR(LDAP_DECODING_ERROR
);
2010 SAFE_FREE(ads
->config
.ldap_server_name
);
2012 ads
->config
.ldap_server_name
= strdup(p
+1);
2013 p
= strchr(ads
->config
.ldap_server_name
, '$');
2014 if (!p
|| p
[1] != '@') {
2016 DEBUG(1, ("ads_server_info: returned ldap server name (%s) does not contain '$@'"
2017 " so was deemed invalid\n", ads
->config
.ldap_server_name
));
2018 SAFE_FREE(ads
->config
.ldap_server_name
);
2019 return ADS_ERROR(LDAP_DECODING_ERROR
);
2024 SAFE_FREE(ads
->config
.realm
);
2025 SAFE_FREE(ads
->config
.bind_path
);
2027 ads
->config
.realm
= strdup(p
+2);
2028 ads
->config
.bind_path
= ads_build_dn(ads
->config
.realm
);
2030 DEBUG(3,("got ldap server name %s@%s, using bind path: %s\n",
2031 ads
->config
.ldap_server_name
, ads
->config
.realm
,
2032 ads
->config
.bind_path
));
2034 ads
->config
.current_time
= ads_parse_time(timestr
);
2036 if (ads
->config
.current_time
!= 0) {
2037 ads
->auth
.time_offset
= ads
->config
.current_time
- time(NULL
);
2038 DEBUG(4,("time offset is %d seconds\n", ads
->auth
.time_offset
));
2047 * find the domain sid for our domain
2048 * @param ads connection to ads server
2049 * @param sid Pointer to domain sid
2050 * @return status of search
2052 ADS_STATUS
ads_domain_sid(ADS_STRUCT
*ads
, DOM_SID
*sid
)
2054 const char *attrs
[] = {"objectSid", NULL
};
2058 rc
= ads_do_search_retry(ads
, ads
->config
.bind_path
, LDAP_SCOPE_BASE
, "(objectclass=*)",
2060 if (!ADS_ERR_OK(rc
)) return rc
;
2061 if (!ads_pull_sid(ads
, res
, "objectSid", sid
)) {
2062 return ADS_ERROR_SYSTEM(ENOENT
);
2064 ads_msgfree(ads
, res
);
2069 /* this is rather complex - we need to find the allternate (netbios) name
2070 for the domain, but there isn't a simple query to do this. Instead
2071 we look for the principle names on the DCs account and find one that has
2072 the right form, then extract the netbios name of the domain from that
2074 NOTE! better method is this:
2076 bin/net -Uadministrator%XXXXX ads search '(&(objectclass=crossref)(dnsroot=VNET3.HOME.SAMBA.ORG))' nETBIOSName
2078 but you need to force the bind path to match the configurationNamingContext from the rootDSE
2081 ADS_STATUS
ads_workgroup_name(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char **workgroup
)
2090 const char *attrs
[] = {"servicePrincipalName", NULL
};
2093 (*workgroup
) = NULL
;
2095 asprintf(&expr
, "(&(objectclass=computer)(dnshostname=%s.%s))",
2096 ads
->config
.ldap_server_name
, ads
->config
.realm
);
2097 rc
= ads_search(ads
, &res
, expr
, attrs
);
2100 if (!ADS_ERR_OK(rc
)) {
2104 principles
= ads_pull_strings(ads
, mem_ctx
, res
,
2105 "servicePrincipalName", &num_principals
);
2107 ads_msgfree(ads
, res
);
2110 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2113 asprintf(&prefix
, "HOST/%s.%s/",
2114 ads
->config
.ldap_server_name
,
2117 prefix_length
= strlen(prefix
);
2119 for (i
=0;principles
[i
]; i
++) {
2120 if (strnequal(principles
[i
], prefix
, prefix_length
) &&
2121 !strequal(ads
->config
.realm
, principles
[i
]+prefix_length
) &&
2122 !strchr(principles
[i
]+prefix_length
, '.')) {
2123 /* found an alternate (short) name for the domain. */
2124 DEBUG(3,("Found alternate name '%s' for realm '%s'\n",
2125 principles
[i
]+prefix_length
,
2126 ads
->config
.realm
));
2127 (*workgroup
) = talloc_strdup(mem_ctx
, principles
[i
]+prefix_length
);
2134 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);