Reduce compiler warnings.
[Samba/gebeck_regimport.git] / source / libads / ldap.c
blob22d7ed3ebfb7917082bc20bdd1a9591f4644ba79
1 /*
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 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.
23 #include "includes.h"
25 #ifdef HAVE_ADS
27 /**
28 * @file ldap.c
29 * @brief basic ldap client-side routines for ads server communications
31 * The routines contained here should do the necessary ldap calls for
32 * ads setups.
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
38 **/
40 /* This is used to get reduce other const warnings to just this fn */
41 static void * ads_unconst_ptr(const void *const_ptr)
43 return const_ptr;
46 /**
47 * Connect to the LDAP server
48 * @param ads Pointer to an existing ADS_STRUCT
49 * @return status of connection
50 **/
51 ADS_STATUS ads_connect(ADS_STRUCT *ads)
53 int version = LDAP_VERSION3;
54 int code;
55 ADS_STATUS status;
57 ads->last_attempt = time(NULL);
59 ads->ld = NULL;
61 if (ads->ldap_server) {
62 ads->ld = ldap_open(ads->ldap_server, ads->ldap_port);
65 /* if that failed then try each of the BDC's in turn */
66 if (!ads->ld) {
67 struct in_addr *ip_list;
68 int count;
70 if (get_dc_list(False, ads->workgroup, &ip_list, &count)) {
71 int i;
72 for (i=0;i<count;i++) {
73 ads->ld = ldap_open(inet_ntoa(ip_list[i]),
74 ads->ldap_port);
75 if (ads->ld) break;
77 if (ads->ld) {
78 SAFE_FREE(ads->ldap_server);
79 ads->ldap_server = strdup(inet_ntoa(ip_list[i]));
81 free(ip_list);
85 if (!ads->ld) {
86 return ADS_ERROR_SYSTEM(errno);
89 DEBUG(3,("Connected to LDAP server %s\n", ads->ldap_server));
91 status = ads_server_info(ads);
92 if (!ADS_ERR_OK(status)) {
93 DEBUG(1,("Failed to get ldap server info\n"));
94 return status;
97 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
99 #if KRB5_DNS_HACK
100 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
101 to MIT kerberos to work (tridge) */
103 char *env;
104 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->server_realm);
105 setenv(env, inet_ntoa(*interpret_addr2(ads->ldap_server)), 1);
106 free(env);
108 #endif
110 if (ads->password) {
111 if ((code = ads_kinit_password(ads)))
112 return ADS_ERROR_KRB5(code);
115 return ads_sasl_bind(ads);
120 * Do a search with paged results. cookie must be null on the first
121 * call, and then returned on each subsequent call. It will be null
122 * again when the entire search is complete
123 * @param ads connection to ads server
124 * @param bind_path Base dn for the search
125 * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
126 * @param exp Search expression - specified in local charset
127 * @param attrs Attributes to retrieve - specified in utf8 or ascii
128 * @param res ** which will contain results - free res* with ads_msgfree()
129 * @param count Number of entries retrieved on this page
130 * @param cookie The paged results cookie to be returned on subsequent calls
131 * @return status of search
133 ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
134 int scope, const char *exp,
135 const char **attrs, void **res,
136 int *count, void **cookie)
138 int rc, i, version;
139 char *utf8_exp, *utf8_path;
140 LDAPControl PagedResults, NoReferrals, *controls[3], **rcontrols;
141 BerElement *cookie_be = NULL;
142 struct berval *cookie_bv= NULL;
144 *res = NULL;
146 /* Paged results only available on ldap v3 or later */
147 ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
148 if (version < LDAP_VERSION3)
149 return ADS_ERROR(LDAP_NOT_SUPPORTED);
151 cookie_be = ber_alloc_t(LBER_USE_DER);
152 if (cookie && *cookie) {
153 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
154 ber_bvfree(*cookie); /* don't need it from last time */
155 *cookie = NULL;
156 } else {
157 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
159 ber_flatten(cookie_be, &cookie_bv);
160 PagedResults.ldctl_oid = ADS_PAGE_CTL_OID;
161 PagedResults.ldctl_iscritical = (char) 1;
162 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
163 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
165 NoReferrals.ldctl_oid = ADS_NO_REFERRALS_OID;
166 NoReferrals.ldctl_iscritical = (char) 0;
167 NoReferrals.ldctl_value.bv_len = 0;
168 NoReferrals.ldctl_value.bv_val = "";
171 controls[0] = &NoReferrals;
172 controls[1] = &PagedResults;
173 controls[2] = NULL;
175 *res = NULL;
177 /* we need to disable referrals as the openldap libs don't
178 handle them and paged results at the same time. Using them
179 together results in the result record containing the server
180 page control being removed from the result list (tridge/jmcd)
182 leaving this in despite the control that says don't generate
183 referrals, in case the server doesn't support it (jmcd)
185 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
187 if (!push_utf8_allocate((void **) &utf8_exp, exp))
188 utf8_exp = ads_unconst_ptr(exp);
189 if (!push_utf8_allocate((void **) &utf8_path, bind_path))
190 utf8_path = ads_unconst_ptr(bind_path);
192 rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_exp,
193 ads_unconst_ptr(attrs), 0, controls,
194 NULL, NULL, LDAP_NO_LIMIT, (LDAPMessage **)res);
196 if (utf8_exp != exp)
197 SAFE_FREE(utf8_exp);
198 if (utf8_path != bind_path)
199 SAFE_FREE(utf8_path);
200 ber_free(cookie_be, 1);
201 ber_bvfree(cookie_bv);
203 if (rc) {
204 DEBUG(3,("ldap_search_ext_s(%s) -> %s\n", exp, ldap_err2string(rc)));
205 return ADS_ERROR(rc);
208 rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
209 NULL, &rcontrols, 0);
211 if (!rcontrols) {
212 return ADS_ERROR(rc);
215 for (i=0; rcontrols[i]; i++) {
216 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
217 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
218 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
219 &cookie_bv);
220 /* the berval is the cookie, but must be freed when
221 it is all done */
222 if (cookie_bv->bv_len) /* still more to do */
223 *cookie=ber_bvdup(cookie_bv);
224 else
225 *cookie=NULL;
226 ber_bvfree(cookie_bv);
227 ber_free(cookie_be, 1);
228 break;
231 ldap_controls_free(rcontrols);
233 return ADS_ERROR(rc);
238 * Get all results for a search. This uses ads_do_paged_search() to return
239 * all entries in a large search.
240 * @param ads connection to ads server
241 * @param bind_path Base dn for the search
242 * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
243 * @param exp Search expression
244 * @param attrs Attributes to retrieve
245 * @param res ** which will contain results - free res* with ads_msgfree()
246 * @return status of search
248 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
249 int scope, const char *exp,
250 const char **attrs, void **res)
252 void *cookie = NULL;
253 int count = 0;
254 ADS_STATUS status;
256 status = ads_do_paged_search(ads, bind_path, scope, exp, attrs, res,
257 &count, &cookie);
259 if (!ADS_ERR_OK(status)) return status;
261 while (cookie) {
262 void *res2 = NULL;
263 ADS_STATUS status2;
264 LDAPMessage *msg, *next;
266 status2 = ads_do_paged_search(ads, bind_path, scope, exp,
267 attrs, &res2, &count, &cookie);
269 if (!ADS_ERR_OK(status2)) break;
271 /* this relies on the way that ldap_add_result_entry() works internally. I hope
272 that this works on all ldap libs, but I have only tested with openldap */
273 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
274 next = ads_next_entry(ads, msg);
275 ldap_add_result_entry((LDAPMessage **)res, msg);
277 /* note that we do not free res2, as the memory is now
278 part of the main returned list */
281 return status;
285 * Run a function on all results for a search. Uses ads_do_paged_search() and
286 * runs the function as each page is returned, using ads_process_results()
287 * @param ads connection to ads server
288 * @param bind_path Base dn for the search
289 * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
290 * @param exp Search expression - specified in local charset
291 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
292 * @param fn Function which takes attr name, values list, and data_area
293 * @param data_area Pointer which is passed to function on each call
294 * @return status of search
296 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
297 int scope, const char *exp, const char **attrs,
298 BOOL(*fn)(char *, void **, void *),
299 void *data_area)
301 void *cookie = NULL;
302 int count = 0;
303 ADS_STATUS status;
304 void *res;
306 status = ads_do_paged_search(ads, bind_path, scope, exp, attrs, &res,
307 &count, &cookie);
309 if (!ADS_ERR_OK(status)) return status;
311 ads_process_results(ads, res, fn, data_area);
312 ads_msgfree(ads, res);
314 while (cookie) {
315 status = ads_do_paged_search(ads, bind_path, scope, exp, attrs,
316 &res, &count, &cookie);
318 if (!ADS_ERR_OK(status)) break;
320 ads_process_results(ads, res, fn, data_area);
321 ads_msgfree(ads, res);
324 return status;
328 * Do a search with a timeout.
329 * @param ads connection to ads server
330 * @param bind_path Base dn for the search
331 * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
332 * @param exp Search expression
333 * @param attrs Attributes to retrieve
334 * @param res ** which will contain results - free res* with ads_msgfree()
335 * @return status of search
337 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
338 const char *exp,
339 const char **attrs, void **res)
341 struct timeval timeout;
342 int rc;
343 char *utf8_exp, *utf8_path;
345 timeout.tv_sec = ADS_SEARCH_TIMEOUT;
346 timeout.tv_usec = 0;
347 *res = NULL;
349 if (!push_utf8_allocate((void **) &utf8_exp, exp))
350 utf8_exp = ads_unconst_ptr(exp);
351 if (!push_utf8_allocate((void **) &utf8_path, bind_path))
352 utf8_path = ads_unconst_ptr(bind_path);
354 rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_exp,
355 ads_unconst_ptr(attrs), 0, NULL, NULL,
356 &timeout, LDAP_NO_LIMIT, (LDAPMessage **)res);
358 if (utf8_exp != exp)
359 SAFE_FREE(utf8_exp);
360 if (utf8_path != bind_path)
361 SAFE_FREE(utf8_path);
362 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
363 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
364 rc = 0;
367 return ADS_ERROR(rc);
370 * Do a general ADS search
371 * @param ads connection to ads server
372 * @param res ** which will contain results - free res* with ads_msgfree()
373 * @param exp Search expression
374 * @param attrs Attributes to retrieve
375 * @return status of search
377 ADS_STATUS ads_search(ADS_STRUCT *ads, void **res,
378 const char *exp,
379 const char **attrs)
381 return ads_do_search(ads, ads->bind_path, LDAP_SCOPE_SUBTREE,
382 exp, attrs, res);
386 * Do a search on a specific DistinguishedName
387 * @param ads connection to ads server
388 * @param res ** which will contain results - free res* with ads_msgfree()
389 * @param dn DistinguishName to search
390 * @param attrs Attributes to retrieve
391 * @return status of search
393 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, void **res,
394 const char *dn,
395 const char **attrs)
397 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res);
401 * Free up memory from a ads_search
402 * @param ads connection to ads server
403 * @param msg Search results to free
405 void ads_msgfree(ADS_STRUCT *ads, void *msg)
407 if (!msg) return;
408 ldap_msgfree(msg);
412 * Free up memory from various ads requests
413 * @param ads connection to ads server
414 * @param mem Area to free
416 void ads_memfree(ADS_STRUCT *ads, void *mem)
418 SAFE_FREE(mem);
422 * Get a dn from search results
423 * @param ads connection to ads server
424 * @param res Search results
425 * @return dn string
427 char *ads_get_dn(ADS_STRUCT *ads, void *res)
429 char *utf8_dn, *unix_dn;
431 utf8_dn = ldap_get_dn(ads->ld, res);
432 pull_utf8_allocate((void **) &unix_dn, utf8_dn);
433 ldap_memfree(utf8_dn);
434 return unix_dn;
438 * Find a machine account given a hostname
439 * @param ads connection to ads server
440 * @param res ** which will contain results - free res* with ads_msgfree()
441 * @param host Hostname to search for
442 * @return status of search
444 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *host)
446 ADS_STATUS status;
447 char *exp;
448 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
450 /* the easiest way to find a machine account anywhere in the tree
451 is to look for hostname$ */
452 asprintf(&exp, "(samAccountName=%s$)", host);
453 status = ads_search(ads, res, exp, attrs);
454 free(exp);
455 return status;
459 * Initialize a list of mods to be used in a modify request
460 * @param ctx An initialized TALLOC_CTX
461 * @return allocated ADS_MODLIST
463 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
465 #define ADS_MODLIST_ALLOC_SIZE 10
466 LDAPMod **mods;
468 if ((mods = (LDAPMod **) talloc_zero(ctx, sizeof(LDAPMod *) *
469 (ADS_MODLIST_ALLOC_SIZE + 1))))
470 /* -1 is safety to make sure we don't go over the end.
471 need to reset it to NULL before doing ldap modify */
472 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
474 return mods;
478 Duplicate a struct berval into talloc'ed memory
480 static struct berval *dup_berval(TALLOC_CTX *ctx, struct berval *in_val)
482 struct berval *value;
484 if (!in_val) return NULL;
486 value = talloc_zero(ctx, sizeof(struct berval));
487 if (in_val->bv_len == 0) return value;
489 value->bv_len = in_val->bv_len;
490 value->bv_val = talloc_memdup(ctx, in_val->bv_val, in_val->bv_len);
491 return value;
495 Make a values list out of an array of (struct berval *)
497 static struct berval **ads_dup_values(TALLOC_CTX *ctx, struct berval **in_vals)
499 struct berval **values;
500 int i;
502 if (!in_vals) return NULL;
503 for (i=0; in_vals[i]; i++); /* count values */
504 values = (struct berval **) talloc_zero(ctx,
505 (i+1)*sizeof(struct berval *));
506 if (!values) return NULL;
508 for (i=0; in_vals[i]; i++) {
509 values[i] = dup_berval(ctx, in_vals[i]);
511 return values;
515 UTF8-encode a values list out of an array of (char *)
517 static char **ads_push_strvals(TALLOC_CTX *ctx, char **in_vals)
519 char **values;
520 int i;
522 if (!in_vals) return NULL;
523 for (i=0; in_vals[i]; i++); /* count values */
524 values = (char ** ) talloc_zero(ctx, (i+1)*sizeof(char *));
525 if (!values) return NULL;
527 for (i=0; in_vals[i]; i++) {
528 push_utf8_talloc(ctx, (void **) &values[i], in_vals[i]);
530 return values;
534 Pull a (char *) array out of a UTF8-encoded values list
536 static char **ads_pull_strvals(TALLOC_CTX *ctx, char **in_vals)
538 char **values;
539 int i;
541 if (!in_vals) return NULL;
542 for (i=0; in_vals[i]; i++); /* count values */
543 values = (char **) talloc_zero(ctx, (i+1)*sizeof(char *));
544 if (!values) return NULL;
546 for (i=0; in_vals[i]; i++) {
547 pull_utf8_talloc(ctx, (void **) &values[i], in_vals[i]);
549 return values;
553 add an attribute to the list, with values list already constructed
555 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
556 int mod_op, const char *name, void **invals)
558 int curmod;
559 LDAPMod **modlist = (LDAPMod **) *mods;
560 void **values;
562 if (!invals) {
563 values = NULL;
564 mod_op = LDAP_MOD_DELETE;
565 } else {
566 if (mod_op & LDAP_MOD_BVALUES)
567 values = (void **) ads_dup_values(ctx,
568 (struct berval **)invals);
569 else
570 values = (void **) ads_push_strvals(ctx,
571 (char **) invals);
574 /* find the first empty slot */
575 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
576 curmod++);
577 if (modlist[curmod] == (LDAPMod *) -1) {
578 if (!(modlist = talloc_realloc(ctx, modlist,
579 (curmod+ADS_MODLIST_ALLOC_SIZE+1)*sizeof(LDAPMod *))))
580 return ADS_ERROR(LDAP_NO_MEMORY);
581 memset(&modlist[curmod], 0,
582 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
583 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
584 *mods = modlist;
587 if (!(modlist[curmod] = talloc_zero(ctx, sizeof(LDAPMod))))
588 return ADS_ERROR(LDAP_NO_MEMORY);
589 modlist[curmod]->mod_type = ads_unconst_ptr(name);
590 if (mod_op & LDAP_MOD_BVALUES)
591 modlist[curmod]->mod_bvalues = (struct berval **) values;
592 else
593 modlist[curmod]->mod_values = (char **) values;
594 modlist[curmod]->mod_op = mod_op;
595 return ADS_ERROR(LDAP_SUCCESS);
599 * Add a single string value to a mod list
600 * @param ctx An initialized TALLOC_CTX
601 * @param mods An initialized ADS_MODLIST
602 * @param name The attribute name to add
603 * @param val The value to add - NULL means DELETE
604 * @return ADS STATUS indicating success of add
606 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
607 const char *name, const char *val)
609 char *values[2] = {ads_unconst_ptr(val), NULL};
610 if (!val)
611 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
612 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name,
613 (void **) values);
617 * Add an array of string values to a mod list
618 * @param ctx An initialized TALLOC_CTX
619 * @param mods An initialized ADS_MODLIST
620 * @param name The attribute name to add
621 * @param vals The array of string values to add - NULL means DELETE
622 * @return ADS STATUS indicating success of add
624 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
625 const char *name, const char **vals)
627 if (!vals)
628 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
629 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
630 ads_unconst_ptr(name), ads_unconst_ptr(vals));
634 * Add a single ber-encoded value to a mod list
635 * @param ctx An initialized TALLOC_CTX
636 * @param mods An initialized ADS_MODLIST
637 * @param name The attribute name to add
638 * @param val The value to add - NULL means DELETE
639 * @return ADS STATUS indicating success of add
641 ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
642 const char *name, const struct berval *val)
644 struct berval *values[2] = {ads_unconst_ptr(val), NULL};
645 if (!val)
646 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
647 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
648 name, (void **) values);
652 * Perform an ldap modify
653 * @param ads connection to ads server
654 * @param mod_dn DistinguishedName to modify
655 * @param mods list of modifications to perform
656 * @return status of modify
658 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
660 int ret,i;
661 char *utf8_dn = NULL;
663 this control is needed to modify that contains a currently
664 non-existent attribute (but allowable for the object) to run
666 LDAPControl PermitModify = {
667 "1.2.840.113556.1.4.1413",
668 {0, NULL},
669 (char) 1};
670 LDAPControl *controls[2];
672 controls[0] = &PermitModify;
673 controls[1] = NULL;
675 push_utf8_allocate((void **) &utf8_dn, mod_dn);
677 /* find the end of the list, marked by NULL or -1 */
678 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
679 /* make sure the end of the list is NULL */
680 mods[i] = NULL;
681 ret = ldap_modify_ext_s(ads->ld, utf8_dn ? utf8_dn : mod_dn,
682 (LDAPMod **) mods, controls, NULL);
683 SAFE_FREE(utf8_dn);
684 return ADS_ERROR(ret);
688 * Perform an ldap add
689 * @param ads connection to ads server
690 * @param new_dn DistinguishedName to add
691 * @param mods list of attributes and values for DN
692 * @return status of add
694 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
696 int ret, i;
697 char *utf8_dn = NULL;
699 push_utf8_allocate((void **) &utf8_dn, new_dn);
701 /* find the end of the list, marked by NULL or -1 */
702 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
703 /* make sure the end of the list is NULL */
704 mods[i] = NULL;
706 ret = ldap_add_s(ads->ld, utf8_dn ? utf8_dn : new_dn, mods);
707 SAFE_FREE(utf8_dn);
708 return ADS_ERROR(ret);
712 * Delete a DistinguishedName
713 * @param ads connection to ads server
714 * @param new_dn DistinguishedName to delete
715 * @return status of delete
717 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
719 int ret;
720 char *utf8_dn = NULL;
721 push_utf8_allocate((void **) &utf8_dn, del_dn);
722 ret = ldap_delete(ads->ld, utf8_dn ? utf8_dn : del_dn);
723 return ADS_ERROR(ret);
727 * Build an org unit string
728 * if org unit is Computers or blank then assume a container, otherwise
729 * assume a \ separated list of organisational units
730 * @param org_unit Organizational unit
731 * @return org unit string - caller must free
733 char *ads_ou_string(const char *org_unit)
735 if (!org_unit || !*org_unit || strcasecmp(org_unit, "Computers") == 0) {
736 return strdup("cn=Computers");
739 return ads_build_path(org_unit, "\\/", "ou=", 1);
745 add a machine account to the ADS server
747 static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *hostname,
748 const char *org_unit)
750 ADS_STATUS ret;
751 char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
752 char *ou_str;
753 TALLOC_CTX *ctx;
754 ADS_MODLIST mods;
755 const char *objectClass[] = {"top", "person", "organizationalPerson",
756 "user", "computer", NULL};
758 if (!(ctx = talloc_init_named("machine_account")))
759 return ADS_ERROR(LDAP_NO_MEMORY);
761 ret = ADS_ERROR(LDAP_NO_MEMORY);
763 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", hostname)))
764 goto done;
765 if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->realm)))
766 goto done;
767 ou_str = ads_ou_string(org_unit);
768 new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", hostname, ou_str,
769 ads->bind_path);
770 free(ou_str);
771 if (!new_dn)
772 goto done;
774 if (!(samAccountName = talloc_asprintf(ctx, "%s$", hostname)))
775 goto done;
776 if (!(controlstr = talloc_asprintf(ctx, "%u",
777 UF_DONT_EXPIRE_PASSWD | UF_WORKSTATION_TRUST_ACCOUNT |
778 UF_TRUSTED_FOR_DELEGATION | UF_USE_DES_KEY_ONLY)))
779 goto done;
781 if (!(mods = ads_init_mods(ctx)))
782 goto done;
784 ads_mod_str(ctx, &mods, "cn", hostname);
785 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
786 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
787 ads_mod_str(ctx, &mods, "userPrincipalName", host_upn);
788 ads_mod_str(ctx, &mods, "servicePrincipalName", host_spn);
789 ads_mod_str(ctx, &mods, "dNSHostName", hostname);
790 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
791 ads_mod_str(ctx, &mods, "operatingSystem", "Samba");
792 ads_mod_str(ctx, &mods, "operatingSystemVersion", VERSION);
794 ads_gen_add(ads, new_dn, mods);
795 ret = ads_set_machine_sd(ads, hostname, new_dn);
797 done:
798 talloc_destroy(ctx);
799 return ret;
803 dump a binary result from ldap
805 static void dump_binary(const char *field, struct berval **values)
807 int i, j;
808 for (i=0; values[i]; i++) {
809 printf("%s: ", field);
810 for (j=0; j<values[i]->bv_len; j++) {
811 printf("%02X", (unsigned char)values[i]->bv_val[j]);
813 printf("\n");
818 dump a sid result from ldap
820 static void dump_sid(const char *field, struct berval **values)
822 int i;
823 for (i=0; values[i]; i++) {
824 DOM_SID sid;
825 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
826 printf("%s: %s\n", field, sid_string_static(&sid));
831 dump ntSecurityDescriptor
833 static void dump_sd(const char *filed, struct berval **values)
835 prs_struct ps;
837 SEC_DESC *psd = 0;
838 TALLOC_CTX *ctx = 0;
840 if (!(ctx = talloc_init_named("sec_io_desc")))
841 return;
843 /* prepare data */
844 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
845 prs_append_data(&ps, values[0]->bv_val, values[0]->bv_len);
846 ps.data_offset = 0;
848 /* parse secdesc */
849 if (!sec_io_desc("sd", &psd, &ps, 1)) {
850 prs_mem_free(&ps);
851 talloc_destroy(ctx);
852 return;
854 if (psd) ads_disp_sd(psd);
856 prs_mem_free(&ps);
857 talloc_destroy(ctx);
861 dump a string result from ldap
863 static void dump_string(const char *field, struct berval **values)
865 int i;
866 for (i=0; values[i]; i++) {
867 printf("%s: %s\n", field, values[i]->bv_val);
872 dump a field from LDAP on stdout
873 used for debugging
876 static BOOL ads_dump_field(char *field, void **values, void *data_area)
878 struct {
879 char *name;
880 BOOL string;
881 void (*handler)(const char *, struct berval **);
882 } handlers[] = {
883 {"objectGUID", False, dump_binary},
884 {"nTSecurityDescriptor", False, dump_sd},
885 {"objectSid", False, dump_sid},
886 {NULL, True, NULL}
888 int i;
890 if (!field) { /* must be end of an entry */
891 printf("\n");
892 return False;
895 for (i=0; handlers[i].name; i++) {
896 if (StrCaseCmp(handlers[i].name, field) == 0) {
897 if (!values) /* first time, indicate string or not */
898 return handlers[i].string;
899 handlers[i].handler(field, (struct berval **) values);
900 break;
903 if (!handlers[i].name) {
904 if (!values) /* first time, indicate string conversion */
905 return True;
906 dump_string(field, (struct berval **) values);
908 return False;
912 * Dump a result from LDAP on stdout
913 * used for debugging
914 * @param ads connection to ads server
915 * @param res Results to dump
918 void ads_dump(ADS_STRUCT *ads, void *res)
920 ads_process_results(ads, res, ads_dump_field, NULL);
924 * Walk through results, calling a function for each entry found.
925 * The function receives a field name, a berval * array of values,
926 * and a data area passed through from the start. The function is
927 * called once with null for field and values at the end of each
928 * entry.
929 * @param ads connection to ads server
930 * @param res Results to process
931 * @param fn Function for processing each result
932 * @param data_area user-defined area to pass to function
934 void ads_process_results(ADS_STRUCT *ads, void *res,
935 BOOL(*fn)(char *, void **, void *),
936 void *data_area)
938 void *msg;
939 TALLOC_CTX *ctx;
941 if (!(ctx = talloc_init()))
942 return;
944 for (msg = ads_first_entry(ads, res); msg;
945 msg = ads_next_entry(ads, msg)) {
946 char *utf8_field;
947 BerElement *b;
949 for (utf8_field=ldap_first_attribute(ads->ld,
950 (LDAPMessage *)msg,&b);
951 utf8_field;
952 utf8_field=ldap_next_attribute(ads->ld,
953 (LDAPMessage *)msg,b)) {
954 struct berval **ber_vals;
955 char **str_vals, **utf8_vals;
956 char *field;
957 BOOL string;
959 pull_utf8_talloc(ctx, (void **) &field, utf8_field);
960 string = fn(field, NULL, data_area);
962 if (string) {
963 utf8_vals = ldap_get_values(ads->ld,
964 (LDAPMessage *)msg, field);
965 str_vals = ads_pull_strvals(ctx, utf8_vals);
966 fn(field, (void **) str_vals, data_area);
967 ldap_value_free(utf8_vals);
968 } else {
969 ber_vals = ldap_get_values_len(ads->ld,
970 (LDAPMessage *)msg, field);
971 fn(field, (void **) ber_vals, data_area);
973 ldap_value_free_len(ber_vals);
975 ldap_memfree(utf8_field);
977 ber_free(b, 0);
978 talloc_destroy_pool(ctx);
979 fn(NULL, NULL, data_area); /* completed an entry */
982 talloc_destroy(ctx);
986 * count how many replies are in a LDAPMessage
987 * @param ads connection to ads server
988 * @param res Results to count
989 * @return number of replies
991 int ads_count_replies(ADS_STRUCT *ads, void *res)
993 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
997 * Join a machine to a realm
998 * Creates the machine account and sets the machine password
999 * @param ads connection to ads server
1000 * @param hostname name of host to add
1001 * @param org_unit Organizational unit to place machine in
1002 * @return status of join
1004 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *hostname, const char *org_unit)
1006 ADS_STATUS status;
1007 LDAPMessage *res;
1008 char *host;
1010 /* hostname must be lowercase */
1011 host = strdup(hostname);
1012 strlower(host);
1014 status = ads_find_machine_acct(ads, (void **)&res, host);
1015 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1016 DEBUG(0, ("Host account for %s already exists - deleting old account\n", host));
1017 status = ads_leave_realm(ads, host);
1018 if (!ADS_ERR_OK(status)) {
1019 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
1020 host, ads->realm));
1021 return status;
1025 status = ads_add_machine_acct(ads, host, org_unit);
1026 if (!ADS_ERR_OK(status)) {
1027 DEBUG(0, ("ads_add_machine_acct: %s\n", ads_errstr(status)));
1028 return status;
1031 status = ads_find_machine_acct(ads, (void **)&res, host);
1032 if (!ADS_ERR_OK(status)) {
1033 DEBUG(0, ("Host account test failed\n"));
1034 return status;
1037 free(host);
1039 return status;
1043 * Delete a machine from the realm
1044 * @param ads connection to ads server
1045 * @param hostname Machine to remove
1046 * @return status of delete
1048 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
1050 ADS_STATUS status;
1051 void *res;
1052 char *hostnameDN, *host;
1053 int rc;
1055 /* hostname must be lowercase */
1056 host = strdup(hostname);
1057 strlower(host);
1059 status = ads_find_machine_acct(ads, &res, host);
1060 if (!ADS_ERR_OK(status)) {
1061 DEBUG(0, ("Host account for %s does not exist.\n", host));
1062 return status;
1065 hostnameDN = ads_get_dn(ads, (LDAPMessage *)res);
1066 rc = ldap_delete_s(ads->ld, hostnameDN);
1067 ads_memfree(ads, hostnameDN);
1068 if (rc != LDAP_SUCCESS) {
1069 return ADS_ERROR(rc);
1072 status = ads_find_machine_acct(ads, &res, host);
1073 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1074 DEBUG(0, ("Failed to remove host account.\n"));
1075 return status;
1078 free(host);
1080 return status;
1084 * add machine account to existing security descriptor
1085 * @param ads connection to ads server
1086 * @param hostname machine to add
1087 * @param dn DN of security descriptor
1088 * @return status
1090 ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
1092 const char *attrs[] = {"ntSecurityDescriptor", "objectSid", 0};
1093 char *exp = 0;
1094 size_t sd_size = 0;
1095 struct berval **bvals = 0;
1096 struct berval bval = {0, NULL};
1097 prs_struct ps;
1098 prs_struct ps_wire;
1100 LDAPMessage *res = 0;
1101 LDAPMessage *msg = 0;
1102 ADS_MODLIST mods = 0;
1104 NTSTATUS status;
1105 ADS_STATUS ret;
1106 DOM_SID sid;
1107 SEC_DESC *psd = 0;
1108 TALLOC_CTX *ctx = 0;
1110 if (!ads) return ADS_ERROR(LDAP_SERVER_DOWN);
1112 ret = ADS_ERROR(LDAP_SUCCESS);
1114 asprintf(&exp, "(samAccountName=%s$)", hostname);
1115 ret = ads_search(ads, (void *) &res, exp, attrs);
1117 if (!ADS_ERR_OK(ret)) return ret;
1119 msg = ads_first_entry(ads, res);
1120 bvals = ldap_get_values_len(ads->ld, msg, attrs[0]);
1121 ads_pull_sid(ads, msg, attrs[1], &sid);
1122 ads_msgfree(ads, res);
1123 #if 0
1124 file_save("/tmp/sec_desc.old", bvals[0]->bv_val, bvals[0]->bv_len);
1125 #endif
1126 if (!(ctx = talloc_init_named("sec_io_desc")))
1127 return ADS_ERROR(LDAP_NO_MEMORY);
1129 prs_init(&ps, bvals[0]->bv_len, ctx, UNMARSHALL);
1130 prs_append_data(&ps, bvals[0]->bv_val, bvals[0]->bv_len);
1131 ps.data_offset = 0;
1132 ldap_value_free_len(bvals);
1134 if (!sec_io_desc("sd", &psd, &ps, 1))
1135 goto ads_set_sd_error;
1137 status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size);
1139 if (!NT_STATUS_IS_OK(status))
1140 goto ads_set_sd_error;
1142 prs_init(&ps_wire, sd_size, ctx, MARSHALL);
1143 if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1))
1144 goto ads_set_sd_error;
1146 #if 0
1147 file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size);
1148 #endif
1149 if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY);
1151 bval.bv_len = sd_size;
1152 bval.bv_val = prs_data_p(&ps_wire);
1153 ads_mod_ber(ctx, &mods, attrs[0], &bval);
1154 ret = ads_gen_mod(ads, dn, mods);
1156 prs_mem_free(&ps);
1157 prs_mem_free(&ps_wire);
1158 talloc_destroy(ctx);
1159 return ret;
1161 ads_set_sd_error:
1162 prs_mem_free(&ps);
1163 prs_mem_free(&ps_wire);
1164 talloc_destroy(ctx);
1165 return ADS_ERROR(LDAP_NO_MEMORY);
1169 * Set the machine account password
1170 * @param ads connection to ads server
1171 * @param hostname machine whose password is being set
1172 * @param password new password
1173 * @return status of password change
1175 ADS_STATUS ads_set_machine_password(ADS_STRUCT *ads,
1176 const char *hostname,
1177 const char *password)
1179 ADS_STATUS status;
1180 char *host = strdup(hostname);
1181 char *principal;
1183 if (!ads->kdc_server) {
1184 DEBUG(0, ("Unable to find KDC server\n"));
1185 return ADS_ERROR(LDAP_SERVER_DOWN);
1188 strlower(host);
1190 asprintf(&principal, "%s@%s", host, ads->realm);
1192 status = krb5_set_password(ads->kdc_server, principal, password);
1194 free(host);
1195 free(principal);
1197 return status;
1201 * pull the first entry from a ADS result
1202 * @param ads connection to ads server
1203 * @param res Results of search
1204 * @return first entry from result
1206 void *ads_first_entry(ADS_STRUCT *ads, void *res)
1208 return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
1212 * pull the next entry from a ADS result
1213 * @param ads connection to ads server
1214 * @param res Results of search
1215 * @return next entry from result
1217 void *ads_next_entry(ADS_STRUCT *ads, void *res)
1219 return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
1223 * pull a single string from a ADS result
1224 * @param ads connection to ads server
1225 * @param mem_ctx TALLOC_CTX to use for allocating result string
1226 * @param msg Results of search
1227 * @param field Attribute to retrieve
1228 * @return Result string in talloc context
1230 char *ads_pull_string(ADS_STRUCT *ads,
1231 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1233 char **values;
1234 char *ret = NULL;
1235 char *ux_string;
1236 int rc;
1238 values = ldap_get_values(ads->ld, msg, field);
1239 if (!values) return NULL;
1241 if (values[0]) {
1242 rc = pull_utf8_talloc(mem_ctx, (void **)&ux_string,
1243 values[0]);
1244 if (rc != -1)
1245 ret = ux_string;
1248 ldap_value_free(values);
1249 return ret;
1253 * pull a single uint32 from a ADS result
1254 * @param ads connection to ads server
1255 * @param msg Results of search
1256 * @param field Attribute to retrieve
1257 * @param v Pointer to int to store result
1258 * @return boolean inidicating success
1260 BOOL ads_pull_uint32(ADS_STRUCT *ads,
1261 void *msg, const char *field, uint32 *v)
1263 char **values;
1265 values = ldap_get_values(ads->ld, msg, field);
1266 if (!values) return False;
1267 if (!values[0]) {
1268 ldap_value_free(values);
1269 return False;
1272 *v = atoi(values[0]);
1273 ldap_value_free(values);
1274 return True;
1278 * pull a single DOM_SID from a ADS result
1279 * @param ads connection to ads server
1280 * @param msg Results of search
1281 * @param field Attribute to retrieve
1282 * @param sid Pointer to sid to store result
1283 * @return boolean inidicating success
1285 BOOL ads_pull_sid(ADS_STRUCT *ads,
1286 void *msg, const char *field, DOM_SID *sid)
1288 struct berval **values;
1289 BOOL ret = False;
1291 values = ldap_get_values_len(ads->ld, msg, field);
1293 if (!values) return False;
1295 if (values[0]) {
1296 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
1299 ldap_value_free_len(values);
1300 return ret;
1304 * pull an array of DOM_SIDs from a ADS result
1305 * @param ads connection to ads server
1306 * @param mem_ctx TALLOC_CTX for allocating sid array
1307 * @param msg Results of search
1308 * @param field Attribute to retrieve
1309 * @param sids pointer to sid array to allocate
1310 * @return the count of SIDs pulled
1312 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1313 void *msg, const char *field, DOM_SID **sids)
1315 struct berval **values;
1316 BOOL ret;
1317 int count, i;
1319 values = ldap_get_values_len(ads->ld, msg, field);
1321 if (!values) return 0;
1323 for (i=0; values[i]; i++) /* nop */ ;
1325 (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * i);
1327 count = 0;
1328 for (i=0; values[i]; i++) {
1329 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
1330 if (ret) count++;
1333 ldap_value_free_len(values);
1334 return count;
1339 * find the update serial number - this is the core of the ldap cache
1340 * @param ads connection to ads server
1341 * @param ads connection to ADS server
1342 * @param usn Pointer to retrieved update serial number
1343 * @return status of search
1345 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
1347 const char *attrs[] = {"highestCommittedUSN", NULL};
1348 ADS_STATUS status;
1349 void *res;
1351 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1352 if (!ADS_ERR_OK(status)) return status;
1354 if (ads_count_replies(ads, res) != 1) {
1355 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1358 ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
1359 ads_msgfree(ads, res);
1360 return ADS_SUCCESS;
1365 * Find the servers name and realm - this can be done before authentication
1366 * The ldapServiceName field on w2k looks like this:
1367 * vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
1368 * @param ads connection to ads server
1369 * @return status of search
1371 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
1373 const char *attrs[] = {"ldapServiceName", NULL};
1374 ADS_STATUS status;
1375 void *res;
1376 char **values;
1377 char *p;
1379 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1380 if (!ADS_ERR_OK(status)) return status;
1382 values = ldap_get_values(ads->ld, res, "ldapServiceName");
1383 if (!values || !values[0]) return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1385 p = strchr(values[0], ':');
1386 if (!p) {
1387 ldap_value_free(values);
1388 ldap_msgfree(res);
1389 return ADS_ERROR(LDAP_DECODING_ERROR);
1392 SAFE_FREE(ads->ldap_server_name);
1394 ads->ldap_server_name = strdup(p+1);
1395 p = strchr(ads->ldap_server_name, '$');
1396 if (!p || p[1] != '@') {
1397 ldap_value_free(values);
1398 ldap_msgfree(res);
1399 SAFE_FREE(ads->ldap_server_name);
1400 return ADS_ERROR(LDAP_DECODING_ERROR);
1403 *p = 0;
1405 SAFE_FREE(ads->server_realm);
1406 SAFE_FREE(ads->bind_path);
1408 ads->server_realm = strdup(p+2);
1409 ads->bind_path = ads_build_dn(ads->server_realm);
1411 /* in case the realm isn't configured in smb.conf */
1412 if (!ads->realm || !ads->realm[0]) {
1413 SAFE_FREE(ads->realm);
1414 ads->realm = strdup(ads->server_realm);
1417 DEBUG(3,("got ldap server name %s@%s\n",
1418 ads->ldap_server_name, ads->realm));
1420 return ADS_SUCCESS;
1425 * find the list of trusted domains
1426 * @param ads connection to ads server
1427 * @param mem_ctx TALLOC_CTX for allocating results
1428 * @param num_trusts pointer to number of trusts
1429 * @param names pointer to trusted domain name list
1430 * @param sids pointer to list of sids of trusted domains
1431 * @return the count of SIDs pulled
1433 ADS_STATUS ads_trusted_domains(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1434 int *num_trusts, char ***names, DOM_SID **sids)
1436 const char *attrs[] = {"flatName", "securityIdentifier", NULL};
1437 ADS_STATUS status;
1438 void *res, *msg;
1439 int count, i;
1441 *num_trusts = 0;
1443 status = ads_search(ads, &res, "(objectcategory=trustedDomain)", attrs);
1444 if (!ADS_ERR_OK(status)) return status;
1446 count = ads_count_replies(ads, res);
1447 if (count == 0) {
1448 ads_msgfree(ads, res);
1449 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1452 (*names) = talloc(mem_ctx, sizeof(char *) * count);
1453 (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * count);
1454 if (! *names || ! *sids) return ADS_ERROR(LDAP_NO_MEMORY);
1456 for (i=0, msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
1457 (*names)[i] = ads_pull_string(ads, mem_ctx, msg, "flatName");
1458 if (ads_pull_sid(ads, msg, "securityIdentifier", &(*sids)[i])) {
1459 i++;
1463 ads_msgfree(ads, res);
1465 *num_trusts = i;
1467 return ADS_SUCCESS;
1471 * find the domain sid for our domain
1472 * @param ads connection to ads server
1473 * @param sid Pointer to domain sid
1474 * @return status of search
1476 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
1478 const char *attrs[] = {"objectSid", NULL};
1479 void *res;
1480 ADS_STATUS rc;
1482 rc = ads_do_search(ads, ads->bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
1483 attrs, &res);
1484 if (!ADS_ERR_OK(rc)) return rc;
1485 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
1486 return ADS_ERROR_SYSTEM(ENOENT);
1488 ads_msgfree(ads, res);
1490 return ADS_SUCCESS;
1493 #endif