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
7 Copyright (C) Guenther Deschner 2005
8 Copyright (C) Gerald Carter 2006
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
31 * @brief basic ldap client-side routines for ads server communications
33 * The routines contained here should do the necessary ldap calls for
36 * Important note: attribute names passed into ads_ routines must
37 * already be in UTF-8 format. We do not convert them because in almost
38 * all cases, they are just ascii (which is represented with the same
39 * codepoints in UTF-8). This may have to change at some point
43 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
45 static SIG_ATOMIC_T gotalarm;
47 /***************************************************************
48 Signal function to tell us we timed out.
49 ****************************************************************/
51 static void gotalarm_sig(void)
56 LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
62 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
64 /* End setup timeout. */
66 ldp = ldap_open(server, port);
69 DEBUG(2,("Could not open LDAP connection to %s:%d: %s\n",
70 server, port, strerror(errno)));
73 /* Teardown timeout. */
74 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
80 static int ldap_search_with_timeout(LDAP *ld,
81 LDAP_CONST char *base,
83 LDAP_CONST char *filter,
91 struct timeval timeout;
94 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
95 timeout.tv_sec = lp_ldap_timeout();
98 /* Setup alarm timeout.... Do we need both of these ? JRA. */
100 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
101 alarm(lp_ldap_timeout());
102 /* End setup timeout. */
104 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
105 attrsonly, sctrls, cctrls, &timeout,
108 /* Teardown timeout. */
109 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
113 return LDAP_TIMELIMIT_EXCEEDED;
119 try a connection to a given ldap server, returning True and setting the servers IP
120 in the ads struct if successful
122 BOOL ads_try_connect(ADS_STRUCT *ads, const char *server )
125 struct cldap_netlogon_reply cldap_reply;
127 if (!server || !*server) {
131 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
132 server, ads->server.realm));
134 /* this copes with inet_ntoa brokenness */
136 srv = SMB_STRDUP(server);
138 ZERO_STRUCT( cldap_reply );
140 if ( !ads_cldap_netlogon( srv, ads->server.realm, &cldap_reply ) ) {
141 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
146 /* Check the CLDAP reply flags */
148 if ( !(cldap_reply.flags & ADS_LDAP) ) {
149 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
155 /* Fill in the ads->config values */
157 SAFE_FREE(ads->config.realm);
158 SAFE_FREE(ads->config.bind_path);
159 SAFE_FREE(ads->config.ldap_server_name);
160 SAFE_FREE(ads->server.workgroup);
162 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.hostname);
163 strupper_m(cldap_reply.domain);
164 ads->config.realm = SMB_STRDUP(cldap_reply.domain);
165 ads->config.bind_path = ads_build_dn(ads->config.realm);
166 ads->server.workgroup = SMB_STRDUP(cldap_reply.netbios_domain);
168 ads->ldap_port = LDAP_PORT;
169 ads->ldap_ip = *interpret_addr2(srv);
172 /* cache the successful connection */
174 saf_store( ads->server.workgroup, server );
179 /**********************************************************************
180 Try to find an AD dc using our internal name resolution routines
181 Try the realm first and then then workgroup name if netbios is not
183 **********************************************************************/
185 static BOOL ads_find_dc(ADS_STRUCT *ads)
189 struct ip_service *ip_list;
191 BOOL got_realm = False;
192 BOOL use_own_domain = False;
194 /* if the realm and workgroup are both empty, assume they are ours */
197 c_realm = ads->server.realm;
199 if ( !c_realm || !*c_realm ) {
200 /* special case where no realm and no workgroup means our own */
201 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
202 use_own_domain = True;
203 c_realm = lp_realm();
207 if (c_realm && *c_realm)
211 /* we need to try once with the realm name and fallback to the
212 netbios domain name if we fail (if netbios has not been disabled */
214 if ( !got_realm && !lp_disable_netbios() ) {
215 c_realm = ads->server.workgroup;
216 if (!c_realm || !*c_realm) {
217 if ( use_own_domain )
218 c_realm = lp_workgroup();
221 if ( !c_realm || !*c_realm ) {
222 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
227 pstrcpy( realm, c_realm );
229 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
230 (got_realm ? "realm" : "domain"), realm));
232 if ( !get_sorted_dc_list(realm, &ip_list, &count, got_realm) ) {
233 /* fall back to netbios if we can */
234 if ( got_realm && !lp_disable_netbios() ) {
242 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
243 for ( i=0; i<count; i++ ) {
246 fstrcpy( server, inet_ntoa(ip_list[i].ip) );
248 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
251 if ( ads_try_connect(ads, server) ) {
256 /* keep track of failures */
257 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
267 * Connect to the LDAP server
268 * @param ads Pointer to an existing ADS_STRUCT
269 * @return status of connection
271 ADS_STATUS ads_connect(ADS_STRUCT *ads)
273 int version = LDAP_VERSION3;
276 ads->last_attempt = time(NULL);
279 /* try with a user specified server */
281 if (ads->server.ldap_server &&
282 ads_try_connect(ads, ads->server.ldap_server)) {
286 if (ads_find_dc(ads)) {
290 return ADS_ERROR_NT(NT_STATUS_NO_LOGON_SERVERS);
293 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
295 if (!ads->auth.user_name) {
296 /* Must use the userPrincipalName value here or sAMAccountName
297 and not servicePrincipalName; found by Guenther Deschner */
299 asprintf(&ads->auth.user_name, "%s$", global_myname() );
302 if (!ads->auth.realm) {
303 ads->auth.realm = SMB_STRDUP(ads->config.realm);
306 if (!ads->auth.kdc_server) {
307 ads->auth.kdc_server = SMB_STRDUP(inet_ntoa(ads->ldap_ip));
311 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
312 to MIT kerberos to work (tridge) */
315 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
316 setenv(env, ads->auth.kdc_server, 1);
321 /* If the caller() requested no LDAP bind, then we are done */
323 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
327 /* Otherwise setup the TCP LDAP session */
329 if ( (ads->ld = ldap_open_with_timeout(ads->config.ldap_server_name,
330 LDAP_PORT, lp_ldap_timeout())) == NULL )
332 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
334 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
336 status = ADS_ERROR(smb_ldap_start_tls(ads->ld, version));
337 if (!ADS_ERR_OK(status)) {
341 /* fill in the current time and offsets */
343 status = ads_current_time( ads );
344 if ( !ADS_ERR_OK(status) ) {
348 /* Now do the bind */
350 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
351 return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
354 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
355 return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
358 return ads_sasl_bind(ads);
362 Duplicate a struct berval into talloc'ed memory
364 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
366 struct berval *value;
368 if (!in_val) return NULL;
370 value = TALLOC_ZERO_P(ctx, struct berval);
373 if (in_val->bv_len == 0) return value;
375 value->bv_len = in_val->bv_len;
376 value->bv_val = TALLOC_MEMDUP(ctx, in_val->bv_val, in_val->bv_len);
381 Make a values list out of an array of (struct berval *)
383 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
384 const struct berval **in_vals)
386 struct berval **values;
389 if (!in_vals) return NULL;
390 for (i=0; in_vals[i]; i++)
392 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
393 if (!values) return NULL;
395 for (i=0; in_vals[i]; i++) {
396 values[i] = dup_berval(ctx, in_vals[i]);
402 UTF8-encode a values list out of an array of (char *)
404 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
409 if (!in_vals) return NULL;
410 for (i=0; in_vals[i]; i++)
412 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
413 if (!values) return NULL;
415 for (i=0; in_vals[i]; i++) {
416 push_utf8_talloc(ctx, &values[i], in_vals[i]);
422 Pull a (char *) array out of a UTF8-encoded values list
424 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
429 if (!in_vals) return NULL;
430 for (i=0; in_vals[i]; i++)
432 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
433 if (!values) return NULL;
435 for (i=0; in_vals[i]; i++) {
436 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
442 * Do a search with paged results. cookie must be null on the first
443 * call, and then returned on each subsequent call. It will be null
444 * again when the entire search is complete
445 * @param ads connection to ads server
446 * @param bind_path Base dn for the search
447 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
448 * @param expr Search expression - specified in local charset
449 * @param attrs Attributes to retrieve - specified in utf8 or ascii
450 * @param res ** which will contain results - free res* with ads_msgfree()
451 * @param count Number of entries retrieved on this page
452 * @param cookie The paged results cookie to be returned on subsequent calls
453 * @return status of search
455 ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads, const char *bind_path,
456 int scope, const char *expr,
457 const char **attrs, void *args, void **res,
458 int *count, void **cookie)
461 char *utf8_expr, *utf8_path, **search_attrs;
462 LDAPControl PagedResults, NoReferrals, ExtendedDn, *controls[4], **rcontrols;
463 BerElement *cookie_be = NULL;
464 struct berval *cookie_bv= NULL;
465 BerElement *extdn_be = NULL;
466 struct berval *extdn_bv= NULL;
469 ads_control *external_control = (ads_control *) args;
473 if (!(ctx = talloc_init("ads_do_paged_search_args")))
474 return ADS_ERROR(LDAP_NO_MEMORY);
476 /* 0 means the conversion worked but the result was empty
477 so we only fail if it's -1. In any case, it always
478 at least nulls out the dest */
479 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
480 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
485 if (!attrs || !(*attrs))
488 /* This would be the utf8-encoded version...*/
489 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
490 if (!(str_list_copy(&search_attrs, attrs))) {
497 /* Paged results only available on ldap v3 or later */
498 ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
499 if (version < LDAP_VERSION3) {
500 rc = LDAP_NOT_SUPPORTED;
504 cookie_be = ber_alloc_t(LBER_USE_DER);
505 if (cookie && *cookie) {
506 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
507 ber_bvfree(*cookie); /* don't need it from last time */
510 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
512 ber_flatten(cookie_be, &cookie_bv);
513 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
514 PagedResults.ldctl_iscritical = (char) 1;
515 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
516 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
518 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
519 NoReferrals.ldctl_iscritical = (char) 0;
520 NoReferrals.ldctl_value.bv_len = 0;
521 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
523 if (external_control && strequal(external_control->control, ADS_EXTENDED_DN_OID)) {
525 ExtendedDn.ldctl_oid = CONST_DISCARD(char *, external_control->control);
526 ExtendedDn.ldctl_iscritical = (char) external_control->critical;
528 /* win2k does not accept a ldctl_value beeing passed in */
530 if (external_control->val != 0) {
532 if ((extdn_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
537 if ((ber_printf(extdn_be, "{i}", (ber_int_t) external_control->val)) == -1) {
541 if ((ber_flatten(extdn_be, &extdn_bv)) == -1) {
546 ExtendedDn.ldctl_value.bv_len = extdn_bv->bv_len;
547 ExtendedDn.ldctl_value.bv_val = extdn_bv->bv_val;
550 ExtendedDn.ldctl_value.bv_len = 0;
551 ExtendedDn.ldctl_value.bv_val = NULL;
554 controls[0] = &NoReferrals;
555 controls[1] = &PagedResults;
556 controls[2] = &ExtendedDn;
560 controls[0] = &NoReferrals;
561 controls[1] = &PagedResults;
565 /* we need to disable referrals as the openldap libs don't
566 handle them and paged results at the same time. Using them
567 together results in the result record containing the server
568 page control being removed from the result list (tridge/jmcd)
570 leaving this in despite the control that says don't generate
571 referrals, in case the server doesn't support it (jmcd)
573 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
575 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
576 search_attrs, 0, controls,
578 (LDAPMessage **)res);
580 ber_free(cookie_be, 1);
581 ber_bvfree(cookie_bv);
584 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
585 ldap_err2string(rc)));
589 rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
590 NULL, &rcontrols, 0);
596 for (i=0; rcontrols[i]; i++) {
597 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
598 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
599 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
601 /* the berval is the cookie, but must be freed when
603 if (cookie_bv->bv_len) /* still more to do */
604 *cookie=ber_bvdup(cookie_bv);
607 ber_bvfree(cookie_bv);
608 ber_free(cookie_be, 1);
612 ldap_controls_free(rcontrols);
618 ber_free(extdn_be, 1);
622 ber_bvfree(extdn_bv);
625 /* if/when we decide to utf8-encode attrs, take out this next line */
626 str_list_free(&search_attrs);
628 return ADS_ERROR(rc);
631 ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
632 int scope, const char *expr,
633 const char **attrs, void **res,
634 int *count, void **cookie)
636 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
641 * Get all results for a search. This uses ads_do_paged_search() to return
642 * all entries in a large search.
643 * @param ads connection to ads server
644 * @param bind_path Base dn for the search
645 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
646 * @param expr Search expression
647 * @param attrs Attributes to retrieve
648 * @param res ** which will contain results - free res* with ads_msgfree()
649 * @return status of search
651 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
652 int scope, const char *expr,
653 const char **attrs, void *args, void **res)
660 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
663 if (!ADS_ERR_OK(status))
666 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
670 LDAPMessage *msg, *next;
672 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
673 attrs, args, &res2, &count, &cookie);
675 if (!ADS_ERR_OK(status2)) break;
677 /* this relies on the way that ldap_add_result_entry() works internally. I hope
678 that this works on all ldap libs, but I have only tested with openldap */
679 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
680 next = ads_next_entry(ads, msg);
681 ldap_add_result_entry((LDAPMessage **)res, msg);
683 /* note that we do not free res2, as the memory is now
684 part of the main returned list */
687 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
688 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
694 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
695 int scope, const char *expr,
696 const char **attrs, void **res)
698 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
702 * Run a function on all results for a search. Uses ads_do_paged_search() and
703 * runs the function as each page is returned, using ads_process_results()
704 * @param ads connection to ads server
705 * @param bind_path Base dn for the search
706 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
707 * @param expr Search expression - specified in local charset
708 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
709 * @param fn Function which takes attr name, values list, and data_area
710 * @param data_area Pointer which is passed to function on each call
711 * @return status of search
713 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
714 int scope, const char *expr, const char **attrs,
715 BOOL(*fn)(char *, void **, void *),
723 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
726 if (!ADS_ERR_OK(status)) return status;
728 ads_process_results(ads, res, fn, data_area);
729 ads_msgfree(ads, res);
732 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
733 &res, &count, &cookie);
735 if (!ADS_ERR_OK(status)) break;
737 ads_process_results(ads, res, fn, data_area);
738 ads_msgfree(ads, res);
745 * Do a search with a timeout.
746 * @param ads connection to ads server
747 * @param bind_path Base dn for the search
748 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
749 * @param expr Search expression
750 * @param attrs Attributes to retrieve
751 * @param res ** which will contain results - free res* with ads_msgfree()
752 * @return status of search
754 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
756 const char **attrs, void **res)
759 char *utf8_expr, *utf8_path, **search_attrs = NULL;
763 if (!(ctx = talloc_init("ads_do_search"))) {
764 DEBUG(1,("ads_do_search: talloc_init() failed!"));
765 return ADS_ERROR(LDAP_NO_MEMORY);
768 /* 0 means the conversion worked but the result was empty
769 so we only fail if it's negative. In any case, it always
770 at least nulls out the dest */
771 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
772 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
773 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
778 if (!attrs || !(*attrs))
781 /* This would be the utf8-encoded version...*/
782 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
783 if (!(str_list_copy(&search_attrs, attrs)))
785 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
791 /* see the note in ads_do_paged_search - we *must* disable referrals */
792 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
794 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
795 search_attrs, 0, NULL, NULL,
797 (LDAPMessage **)res);
799 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
800 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
806 /* if/when we decide to utf8-encode attrs, take out this next line */
807 str_list_free(&search_attrs);
808 return ADS_ERROR(rc);
811 * Do a general ADS search
812 * @param ads connection to ads server
813 * @param res ** which will contain results - free res* with ads_msgfree()
814 * @param expr Search expression
815 * @param attrs Attributes to retrieve
816 * @return status of search
818 ADS_STATUS ads_search(ADS_STRUCT *ads, void **res,
822 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
827 * Do a search on a specific DistinguishedName
828 * @param ads connection to ads server
829 * @param res ** which will contain results - free res* with ads_msgfree()
830 * @param dn DistinguishName to search
831 * @param attrs Attributes to retrieve
832 * @return status of search
834 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, void *_res,
838 void **res = (void **)_res;
839 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res);
843 * Free up memory from a ads_search
844 * @param ads connection to ads server
845 * @param msg Search results to free
847 void ads_msgfree(ADS_STRUCT *ads, void *msg)
854 * Free up memory from various ads requests
855 * @param ads connection to ads server
856 * @param mem Area to free
858 void ads_memfree(ADS_STRUCT *ads, void *mem)
864 * Get a dn from search results
865 * @param ads connection to ads server
866 * @param msg Search result
869 char *ads_get_dn(ADS_STRUCT *ads, void *msg)
871 char *utf8_dn, *unix_dn;
873 utf8_dn = ldap_get_dn(ads->ld, msg);
876 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
880 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
881 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
885 ldap_memfree(utf8_dn);
890 * Get a canonical dn from search results
891 * @param ads connection to ads server
892 * @param msg Search result
895 char *ads_get_dn_canonical(ADS_STRUCT *ads, void *msg)
897 #ifdef HAVE_LDAP_DN2AD_CANONICAL
898 return ldap_dn2ad_canonical(ads_get_dn(ads, msg));
905 * Get the parent from a dn
906 * @param dn the dn to return the parent from
907 * @return parent dn string
909 char *ads_parent_dn(const char *dn)
927 * Find a machine account given a hostname
928 * @param ads connection to ads server
929 * @param res ** which will contain results - free res* with ads_msgfree()
930 * @param host Hostname to search for
931 * @return status of search
933 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *machine)
937 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
941 /* the easiest way to find a machine account anywhere in the tree
942 is to look for hostname$ */
943 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
944 DEBUG(1, ("asprintf failed!\n"));
945 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
948 status = ads_search(ads, res, expr, attrs);
954 * Initialize a list of mods to be used in a modify request
955 * @param ctx An initialized TALLOC_CTX
956 * @return allocated ADS_MODLIST
958 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
960 #define ADS_MODLIST_ALLOC_SIZE 10
963 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
964 /* -1 is safety to make sure we don't go over the end.
965 need to reset it to NULL before doing ldap modify */
966 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
968 return (ADS_MODLIST)mods;
973 add an attribute to the list, with values list already constructed
975 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
976 int mod_op, const char *name,
979 const void **invals = (const void **)_invals;
981 LDAPMod **modlist = (LDAPMod **) *mods;
982 struct berval **ber_values = NULL;
983 char **char_values = NULL;
986 mod_op = LDAP_MOD_DELETE;
988 if (mod_op & LDAP_MOD_BVALUES)
989 ber_values = ads_dup_values(ctx,
990 (const struct berval **)invals);
992 char_values = ads_push_strvals(ctx,
993 (const char **) invals);
996 /* find the first empty slot */
997 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
999 if (modlist[curmod] == (LDAPMod *) -1) {
1000 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1001 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1002 return ADS_ERROR(LDAP_NO_MEMORY);
1003 memset(&modlist[curmod], 0,
1004 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1005 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1006 *mods = (ADS_MODLIST)modlist;
1009 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1010 return ADS_ERROR(LDAP_NO_MEMORY);
1011 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1012 if (mod_op & LDAP_MOD_BVALUES) {
1013 modlist[curmod]->mod_bvalues = ber_values;
1014 } else if (mod_op & LDAP_MOD_DELETE) {
1015 modlist[curmod]->mod_values = NULL;
1017 modlist[curmod]->mod_values = char_values;
1020 modlist[curmod]->mod_op = mod_op;
1021 return ADS_ERROR(LDAP_SUCCESS);
1025 * Add a single string value to a mod list
1026 * @param ctx An initialized TALLOC_CTX
1027 * @param mods An initialized ADS_MODLIST
1028 * @param name The attribute name to add
1029 * @param val The value to add - NULL means DELETE
1030 * @return ADS STATUS indicating success of add
1032 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1033 const char *name, const char *val)
1035 const char *values[2];
1041 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1042 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1046 * Add an array of string values to a mod list
1047 * @param ctx An initialized TALLOC_CTX
1048 * @param mods An initialized ADS_MODLIST
1049 * @param name The attribute name to add
1050 * @param vals The array of string values to add - NULL means DELETE
1051 * @return ADS STATUS indicating success of add
1053 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1054 const char *name, const char **vals)
1057 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1058 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1059 name, (const void **) vals);
1064 * Add a single ber-encoded value to a mod list
1065 * @param ctx An initialized TALLOC_CTX
1066 * @param mods An initialized ADS_MODLIST
1067 * @param name The attribute name to add
1068 * @param val The value to add - NULL means DELETE
1069 * @return ADS STATUS indicating success of add
1071 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1072 const char *name, const struct berval *val)
1074 const struct berval *values[2];
1079 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1080 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1081 name, (const void **) values);
1086 * Perform an ldap modify
1087 * @param ads connection to ads server
1088 * @param mod_dn DistinguishedName to modify
1089 * @param mods list of modifications to perform
1090 * @return status of modify
1092 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1095 char *utf8_dn = NULL;
1097 this control is needed to modify that contains a currently
1098 non-existent attribute (but allowable for the object) to run
1100 LDAPControl PermitModify = {
1101 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1104 LDAPControl *controls[2];
1106 controls[0] = &PermitModify;
1109 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
1110 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1113 /* find the end of the list, marked by NULL or -1 */
1114 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1115 /* make sure the end of the list is NULL */
1117 ret = ldap_modify_ext_s(ads->ld, utf8_dn,
1118 (LDAPMod **) mods, controls, NULL);
1120 return ADS_ERROR(ret);
1124 * Perform an ldap add
1125 * @param ads connection to ads server
1126 * @param new_dn DistinguishedName to add
1127 * @param mods list of attributes and values for DN
1128 * @return status of add
1130 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1133 char *utf8_dn = NULL;
1135 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1136 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1137 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1140 /* find the end of the list, marked by NULL or -1 */
1141 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1142 /* make sure the end of the list is NULL */
1145 ret = ldap_add_s(ads->ld, utf8_dn, (LDAPMod**)mods);
1147 return ADS_ERROR(ret);
1151 * Delete a DistinguishedName
1152 * @param ads connection to ads server
1153 * @param new_dn DistinguishedName to delete
1154 * @return status of delete
1156 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1159 char *utf8_dn = NULL;
1160 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1161 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1162 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1165 ret = ldap_delete_s(ads->ld, utf8_dn);
1166 return ADS_ERROR(ret);
1170 * Build an org unit string
1171 * if org unit is Computers or blank then assume a container, otherwise
1172 * assume a / separated list of organisational units.
1173 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1174 * @param ads connection to ads server
1175 * @param org_unit Organizational unit
1176 * @return org unit string - caller must free
1178 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1182 if (!org_unit || !*org_unit) {
1184 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1186 /* samba4 might not yet respond to a wellknownobject-query */
1187 return ret ? ret : SMB_STRDUP("cn=Computers");
1190 if (strequal(org_unit, "Computers")) {
1191 return SMB_STRDUP("cn=Computers");
1194 /* jmcd: removed "\\" from the separation chars, because it is
1195 needed as an escape for chars like '#' which are valid in an
1197 return ads_build_path(org_unit, "/", "ou=", 1);
1201 * Get a org unit string for a well-known GUID
1202 * @param ads connection to ads server
1203 * @param wknguid Well known GUID
1204 * @return org unit string - caller must free
1206 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1210 char *base, *wkn_dn, *ret, **wkn_dn_exp, **bind_dn_exp;
1211 const char *attrs[] = {"distinguishedName", NULL};
1212 int new_ln, wkn_ln, bind_ln, i;
1214 if (wknguid == NULL) {
1218 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1219 DEBUG(1, ("asprintf failed!\n"));
1223 status = ads_search_dn(ads, &res, base, attrs);
1224 if (!ADS_ERR_OK(status)) {
1225 DEBUG(1,("Failed while searching for: %s\n", base));
1231 if (ads_count_replies(ads, res) != 1) {
1235 /* substitute the bind-path from the well-known-guid-search result */
1236 wkn_dn = ads_get_dn(ads, res);
1237 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1238 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1240 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1242 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1245 new_ln = wkn_ln - bind_ln;
1247 ret = wkn_dn_exp[0];
1249 for (i=1; i < new_ln; i++) {
1251 asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]);
1252 ret = SMB_STRDUP(s);
1256 ads_memfree(ads, wkn_dn);
1257 ldap_value_free(wkn_dn_exp);
1258 ldap_value_free(bind_dn_exp);
1264 * Adds (appends) an item to an attribute array, rather then
1265 * replacing the whole list
1266 * @param ctx An initialized TALLOC_CTX
1267 * @param mods An initialized ADS_MODLIST
1268 * @param name name of the ldap attribute to append to
1269 * @param vals an array of values to add
1270 * @return status of addition
1273 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1274 const char *name, const char **vals)
1276 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name, (const void **) vals);
1280 * Determines the computer account's current KVNO via an LDAP lookup
1281 * @param ads An initialized ADS_STRUCT
1282 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1283 * @return the kvno for the computer account, or -1 in case of a failure.
1286 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1288 LDAPMessage *res = NULL;
1289 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1291 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1292 char *dn_string = NULL;
1293 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1295 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1296 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1299 ret = ads_search(ads, (void**)(void *)&res, filter, attrs);
1301 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1302 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1303 ads_msgfree(ads, res);
1307 dn_string = ads_get_dn(ads, res);
1309 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1310 ads_msgfree(ads, res);
1313 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1314 ads_memfree(ads, dn_string);
1316 /* ---------------------------------------------------------
1317 * 0 is returned as a default KVNO from this point on...
1318 * This is done because Windows 2000 does not support key
1319 * version numbers. Chances are that a failure in the next
1320 * step is simply due to Windows 2000 being used for a
1321 * domain controller. */
1324 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1325 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1326 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1327 ads_msgfree(ads, res);
1332 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1333 ads_msgfree(ads, res);
1338 * This clears out all registered spn's for a given hostname
1339 * @param ads An initilaized ADS_STRUCT
1340 * @param machine_name the NetBIOS name of the computer.
1341 * @return 0 upon success, non-zero otherwise.
1344 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1347 LDAPMessage *res = NULL;
1349 const char *servicePrincipalName[1] = {NULL};
1350 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1351 char *dn_string = NULL;
1353 ret = ads_find_machine_acct(ads, (void **)(void *)&res, machine_name);
1354 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1355 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1356 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1357 ads_msgfree(ads, res);
1358 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1361 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1362 ctx = talloc_init("ads_clear_service_principal_names");
1364 ads_msgfree(ads, res);
1365 return ADS_ERROR(LDAP_NO_MEMORY);
1368 if (!(mods = ads_init_mods(ctx))) {
1369 talloc_destroy(ctx);
1370 ads_msgfree(ads, res);
1371 return ADS_ERROR(LDAP_NO_MEMORY);
1373 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1374 if (!ADS_ERR_OK(ret)) {
1375 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1376 ads_msgfree(ads, res);
1377 talloc_destroy(ctx);
1380 dn_string = ads_get_dn(ads, res);
1382 talloc_destroy(ctx);
1383 ads_msgfree(ads, res);
1384 return ADS_ERROR(LDAP_NO_MEMORY);
1386 ret = ads_gen_mod(ads, dn_string, mods);
1387 ads_memfree(ads,dn_string);
1388 if (!ADS_ERR_OK(ret)) {
1389 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1391 ads_msgfree(ads, res);
1392 talloc_destroy(ctx);
1396 ads_msgfree(ads, res);
1397 talloc_destroy(ctx);
1402 * This adds a service principal name to an existing computer account
1403 * (found by hostname) in AD.
1404 * @param ads An initialized ADS_STRUCT
1405 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1406 * @param my_fqdn The fully qualified DNS name of the machine
1407 * @param spn A string of the service principal to add, i.e. 'host'
1408 * @return 0 upon sucess, or non-zero if a failure occurs
1411 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1412 const char *my_fqdn, const char *spn)
1416 LDAPMessage *res = NULL;
1419 char *dn_string = NULL;
1420 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1422 ret = ads_find_machine_acct(ads, (void **)(void *)&res, machine_name);
1423 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1424 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1426 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1427 spn, machine_name, ads->config.realm));
1428 ads_msgfree(ads, res);
1429 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1432 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1433 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1434 ads_msgfree(ads, res);
1435 return ADS_ERROR(LDAP_NO_MEMORY);
1438 /* add short name spn */
1440 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1441 talloc_destroy(ctx);
1442 ads_msgfree(ads, res);
1443 return ADS_ERROR(LDAP_NO_MEMORY);
1446 strlower_m(&psp1[strlen(spn)]);
1447 servicePrincipalName[0] = psp1;
1449 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1450 psp1, machine_name));
1453 /* add fully qualified spn */
1455 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1456 ret = ADS_ERROR(LDAP_NO_MEMORY);
1460 strlower_m(&psp2[strlen(spn)]);
1461 servicePrincipalName[1] = psp2;
1463 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1464 psp2, machine_name));
1466 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1467 ret = ADS_ERROR(LDAP_NO_MEMORY);
1471 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1472 if (!ADS_ERR_OK(ret)) {
1473 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1477 if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1478 ret = ADS_ERROR(LDAP_NO_MEMORY);
1482 ret = ads_gen_mod(ads, dn_string, mods);
1483 ads_memfree(ads,dn_string);
1484 if (!ADS_ERR_OK(ret)) {
1485 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1491 ads_msgfree(ads, res);
1496 * adds a machine account to the ADS server
1497 * @param ads An intialized ADS_STRUCT
1498 * @param machine_name - the NetBIOS machine name of this account.
1499 * @param account_type A number indicating the type of account to create
1500 * @param org_unit The LDAP path in which to place this account
1501 * @return 0 upon success, or non-zero otherwise
1504 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1505 const char *org_unit)
1508 char *samAccountName, *controlstr;
1512 const char *objectClass[] = {"top", "person", "organizationalPerson",
1513 "user", "computer", NULL};
1514 LDAPMessage *res = NULL;
1515 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1516 UF_DONT_EXPIRE_PASSWD |\
1517 UF_ACCOUNTDISABLE );
1519 if (!(ctx = talloc_init("ads_add_machine_acct")))
1520 return ADS_ERROR(LDAP_NO_MEMORY);
1522 ret = ADS_ERROR(LDAP_NO_MEMORY);
1524 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_name, org_unit);
1525 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1527 if ( !new_dn || !samAccountName ) {
1531 #ifndef ENCTYPE_ARCFOUR_HMAC
1532 acct_control |= UF_USE_DES_KEY_ONLY;
1535 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1539 if (!(mods = ads_init_mods(ctx))) {
1543 ads_mod_str(ctx, &mods, "cn", machine_name);
1544 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1545 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1546 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1548 ret = ads_gen_add(ads, new_dn, mods);
1551 ads_msgfree(ads, res);
1552 talloc_destroy(ctx);
1558 dump a binary result from ldap
1560 static void dump_binary(const char *field, struct berval **values)
1563 for (i=0; values[i]; i++) {
1564 printf("%s: ", field);
1565 for (j=0; j<values[i]->bv_len; j++) {
1566 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1572 static void dump_guid(const char *field, struct berval **values)
1576 for (i=0; values[i]; i++) {
1577 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1578 printf("%s: %s\n", field,
1579 smb_uuid_string_static(smb_uuid_unpack_static(guid)));
1584 dump a sid result from ldap
1586 static void dump_sid(const char *field, struct berval **values)
1589 for (i=0; values[i]; i++) {
1591 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1592 printf("%s: %s\n", field, sid_string_static(&sid));
1597 dump ntSecurityDescriptor
1599 static void dump_sd(const char *filed, struct berval **values)
1604 TALLOC_CTX *ctx = 0;
1606 if (!(ctx = talloc_init("sec_io_desc")))
1610 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1611 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1612 prs_set_offset(&ps,0);
1615 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1617 talloc_destroy(ctx);
1620 if (psd) ads_disp_sd(psd);
1623 talloc_destroy(ctx);
1627 dump a string result from ldap
1629 static void dump_string(const char *field, char **values)
1632 for (i=0; values[i]; i++) {
1633 printf("%s: %s\n", field, values[i]);
1638 dump a field from LDAP on stdout
1642 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1647 void (*handler)(const char *, struct berval **);
1649 {"objectGUID", False, dump_guid},
1650 {"netbootGUID", False, dump_guid},
1651 {"nTSecurityDescriptor", False, dump_sd},
1652 {"dnsRecord", False, dump_binary},
1653 {"objectSid", False, dump_sid},
1654 {"tokenGroups", False, dump_sid},
1655 {"tokenGroupsNoGCAcceptable", False, dump_sid},
1656 {"tokengroupsGlobalandUniversal", False, dump_sid},
1661 if (!field) { /* must be end of an entry */
1666 for (i=0; handlers[i].name; i++) {
1667 if (StrCaseCmp(handlers[i].name, field) == 0) {
1668 if (!values) /* first time, indicate string or not */
1669 return handlers[i].string;
1670 handlers[i].handler(field, (struct berval **) values);
1674 if (!handlers[i].name) {
1675 if (!values) /* first time, indicate string conversion */
1677 dump_string(field, (char **)values);
1683 * Dump a result from LDAP on stdout
1684 * used for debugging
1685 * @param ads connection to ads server
1686 * @param res Results to dump
1689 void ads_dump(ADS_STRUCT *ads, void *res)
1691 ads_process_results(ads, res, ads_dump_field, NULL);
1695 * Walk through results, calling a function for each entry found.
1696 * The function receives a field name, a berval * array of values,
1697 * and a data area passed through from the start. The function is
1698 * called once with null for field and values at the end of each
1700 * @param ads connection to ads server
1701 * @param res Results to process
1702 * @param fn Function for processing each result
1703 * @param data_area user-defined area to pass to function
1705 void ads_process_results(ADS_STRUCT *ads, void *res,
1706 BOOL(*fn)(char *, void **, void *),
1712 if (!(ctx = talloc_init("ads_process_results")))
1715 for (msg = ads_first_entry(ads, res); msg;
1716 msg = ads_next_entry(ads, msg)) {
1720 for (utf8_field=ldap_first_attribute(ads->ld,
1721 (LDAPMessage *)msg,&b);
1723 utf8_field=ldap_next_attribute(ads->ld,
1724 (LDAPMessage *)msg,b)) {
1725 struct berval **ber_vals;
1726 char **str_vals, **utf8_vals;
1730 pull_utf8_talloc(ctx, &field, utf8_field);
1731 string = fn(field, NULL, data_area);
1734 utf8_vals = ldap_get_values(ads->ld,
1735 (LDAPMessage *)msg, field);
1736 str_vals = ads_pull_strvals(ctx,
1737 (const char **) utf8_vals);
1738 fn(field, (void **) str_vals, data_area);
1739 ldap_value_free(utf8_vals);
1741 ber_vals = ldap_get_values_len(ads->ld,
1742 (LDAPMessage *)msg, field);
1743 fn(field, (void **) ber_vals, data_area);
1745 ldap_value_free_len(ber_vals);
1747 ldap_memfree(utf8_field);
1750 talloc_free_children(ctx);
1751 fn(NULL, NULL, data_area); /* completed an entry */
1754 talloc_destroy(ctx);
1758 * count how many replies are in a LDAPMessage
1759 * @param ads connection to ads server
1760 * @param res Results to count
1761 * @return number of replies
1763 int ads_count_replies(ADS_STRUCT *ads, void *res)
1765 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1769 * pull the first entry from a ADS result
1770 * @param ads connection to ads server
1771 * @param res Results of search
1772 * @return first entry from result
1774 void *ads_first_entry(ADS_STRUCT *ads, void *res)
1776 return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
1780 * pull the next entry from a ADS result
1781 * @param ads connection to ads server
1782 * @param res Results of search
1783 * @return next entry from result
1785 void *ads_next_entry(ADS_STRUCT *ads, void *res)
1787 return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
1791 * pull a single string from a ADS result
1792 * @param ads connection to ads server
1793 * @param mem_ctx TALLOC_CTX to use for allocating result string
1794 * @param msg Results of search
1795 * @param field Attribute to retrieve
1796 * @return Result string in talloc context
1798 char *ads_pull_string(ADS_STRUCT *ads,
1799 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1806 values = ldap_get_values(ads->ld, msg, field);
1811 rc = pull_utf8_talloc(mem_ctx, &ux_string,
1813 if (rc != (size_t)-1)
1817 ldap_value_free(values);
1822 * pull an array of strings from a ADS result
1823 * @param ads connection to ads server
1824 * @param mem_ctx TALLOC_CTX to use for allocating result string
1825 * @param msg Results of search
1826 * @param field Attribute to retrieve
1827 * @return Result strings in talloc context
1829 char **ads_pull_strings(ADS_STRUCT *ads,
1830 TALLOC_CTX *mem_ctx, void *msg, const char *field,
1837 values = ldap_get_values(ads->ld, msg, field);
1841 *num_values = ldap_count_values(values);
1843 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
1845 ldap_value_free(values);
1849 for (i=0;i<*num_values;i++) {
1850 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
1851 ldap_value_free(values);
1857 ldap_value_free(values);
1862 * pull an array of strings from a ADS result
1863 * (handle large multivalue attributes with range retrieval)
1864 * @param ads connection to ads server
1865 * @param mem_ctx TALLOC_CTX to use for allocating result string
1866 * @param msg Results of search
1867 * @param field Attribute to retrieve
1868 * @param current_strings strings returned by a previous call to this function
1869 * @param next_attribute The next query should ask for this attribute
1870 * @param num_values How many values did we get this time?
1871 * @param more_values Are there more values to get?
1872 * @return Result strings in talloc context
1874 char **ads_pull_strings_range(ADS_STRUCT *ads,
1875 TALLOC_CTX *mem_ctx,
1876 void *msg, const char *field,
1877 char **current_strings,
1878 const char **next_attribute,
1879 size_t *num_strings,
1883 char *expected_range_attrib, *range_attr;
1884 BerElement *ptr = NULL;
1887 size_t num_new_strings;
1888 unsigned long int range_start;
1889 unsigned long int range_end;
1891 /* we might have been given the whole lot anyway */
1892 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
1893 *more_strings = False;
1897 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
1899 /* look for Range result */
1900 for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr);
1902 attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
1903 /* we ignore the fact that this is utf8, as all attributes are ascii... */
1904 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
1912 /* nothing here - this field is just empty */
1913 *more_strings = False;
1917 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
1918 &range_start, &range_end) == 2) {
1919 *more_strings = True;
1921 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
1922 &range_start) == 1) {
1923 *more_strings = False;
1925 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
1927 ldap_memfree(range_attr);
1928 *more_strings = False;
1933 if ((*num_strings) != range_start) {
1934 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
1935 " - aborting range retreival\n",
1936 range_attr, (unsigned int)(*num_strings) + 1, range_start));
1937 ldap_memfree(range_attr);
1938 *more_strings = False;
1942 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
1944 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
1945 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
1946 "strings in this bunch, but we only got %lu - aborting range retreival\n",
1947 range_attr, (unsigned long int)range_end - range_start + 1,
1948 (unsigned long int)num_new_strings));
1949 ldap_memfree(range_attr);
1950 *more_strings = False;
1954 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
1955 *num_strings + num_new_strings);
1957 if (strings == NULL) {
1958 ldap_memfree(range_attr);
1959 *more_strings = False;
1963 if (new_strings && num_new_strings) {
1964 memcpy(&strings[*num_strings], new_strings,
1965 sizeof(*new_strings) * num_new_strings);
1968 (*num_strings) += num_new_strings;
1970 if (*more_strings) {
1971 *next_attribute = talloc_asprintf(mem_ctx,
1976 if (!*next_attribute) {
1977 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
1978 ldap_memfree(range_attr);
1979 *more_strings = False;
1984 ldap_memfree(range_attr);
1990 * pull a single uint32 from a ADS result
1991 * @param ads connection to ads server
1992 * @param msg Results of search
1993 * @param field Attribute to retrieve
1994 * @param v Pointer to int to store result
1995 * @return boolean inidicating success
1997 BOOL ads_pull_uint32(ADS_STRUCT *ads,
1998 void *msg, const char *field, uint32 *v)
2002 values = ldap_get_values(ads->ld, msg, field);
2006 ldap_value_free(values);
2010 *v = atoi(values[0]);
2011 ldap_value_free(values);
2016 * pull a single objectGUID from an ADS result
2017 * @param ads connection to ADS server
2018 * @param msg results of search
2019 * @param guid 37-byte area to receive text guid
2020 * @return boolean indicating success
2022 BOOL ads_pull_guid(ADS_STRUCT *ads,
2023 void *msg, struct uuid *guid)
2026 UUID_FLAT flat_guid;
2028 values = ldap_get_values(ads->ld, msg, "objectGUID");
2033 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2034 smb_uuid_unpack(flat_guid, guid);
2035 ldap_value_free(values);
2038 ldap_value_free(values);
2045 * pull a single DOM_SID from a ADS result
2046 * @param ads connection to ads server
2047 * @param msg Results of search
2048 * @param field Attribute to retrieve
2049 * @param sid Pointer to sid to store result
2050 * @return boolean inidicating success
2052 BOOL ads_pull_sid(ADS_STRUCT *ads,
2053 void *msg, const char *field, DOM_SID *sid)
2055 struct berval **values;
2058 values = ldap_get_values_len(ads->ld, msg, field);
2064 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2066 ldap_value_free_len(values);
2071 * pull an array of DOM_SIDs from a ADS result
2072 * @param ads connection to ads server
2073 * @param mem_ctx TALLOC_CTX for allocating sid array
2074 * @param msg Results of search
2075 * @param field Attribute to retrieve
2076 * @param sids pointer to sid array to allocate
2077 * @return the count of SIDs pulled
2079 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2080 void *msg, const char *field, DOM_SID **sids)
2082 struct berval **values;
2086 values = ldap_get_values_len(ads->ld, msg, field);
2091 for (i=0; values[i]; i++)
2094 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2096 ldap_value_free_len(values);
2101 for (i=0; values[i]; i++) {
2102 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2105 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2110 ldap_value_free_len(values);
2115 * pull a SEC_DESC from a ADS result
2116 * @param ads connection to ads server
2117 * @param mem_ctx TALLOC_CTX for allocating sid array
2118 * @param msg Results of search
2119 * @param field Attribute to retrieve
2120 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2121 * @return boolean inidicating success
2123 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2124 void *msg, const char *field, SEC_DESC **sd)
2126 struct berval **values;
2130 values = ldap_get_values_len(ads->ld, msg, field);
2132 if (!values) return False;
2135 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2136 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2137 prs_set_offset(&ps,0);
2139 ret = sec_io_desc("sd", sd, &ps, 1);
2142 ldap_value_free_len(values);
2147 * in order to support usernames longer than 21 characters we need to
2148 * use both the sAMAccountName and the userPrincipalName attributes
2149 * It seems that not all users have the userPrincipalName attribute set
2151 * @param ads connection to ads server
2152 * @param mem_ctx TALLOC_CTX for allocating sid array
2153 * @param msg Results of search
2154 * @return the username
2156 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
2161 /* lookup_name() only works on the sAMAccountName to
2162 returning the username portion of userPrincipalName
2163 breaks winbindd_getpwnam() */
2165 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2166 if (ret && (p = strchr_m(ret, '@'))) {
2171 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2176 * find the update serial number - this is the core of the ldap cache
2177 * @param ads connection to ads server
2178 * @param ads connection to ADS server
2179 * @param usn Pointer to retrieved update serial number
2180 * @return status of search
2182 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2184 const char *attrs[] = {"highestCommittedUSN", NULL};
2188 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2189 if (!ADS_ERR_OK(status))
2192 if (ads_count_replies(ads, res) != 1) {
2193 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2196 ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
2197 ads_msgfree(ads, res);
2201 /* parse a ADS timestring - typical string is
2202 '20020917091222.0Z0' which means 09:12.22 17th September
2204 static time_t ads_parse_time(const char *str)
2210 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2211 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2212 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2221 /********************************************************************
2222 ********************************************************************/
2224 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2226 const char *attrs[] = {"currentTime", NULL};
2231 ADS_STRUCT *ads_s = ads;
2233 if (!(ctx = talloc_init("ads_current_time"))) {
2234 return ADS_ERROR(LDAP_NO_MEMORY);
2237 /* establish a new ldap tcp session if necessary */
2240 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2241 ads->server.ldap_server )) == NULL )
2245 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2246 status = ads_connect( ads_s );
2247 if ( !ADS_ERR_OK(status))
2251 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2252 if (!ADS_ERR_OK(status)) {
2256 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2258 ads_msgfree(ads, res);
2259 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2263 /* but save the time and offset in the original ADS_STRUCT */
2265 ads->config.current_time = ads_parse_time(timestr);
2267 if (ads->config.current_time != 0) {
2268 ads->auth.time_offset = ads->config.current_time - time(NULL);
2269 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2272 ads_msgfree(ads, res);
2274 status = ADS_SUCCESS;
2277 /* free any temporary ads connections */
2278 if ( ads_s != ads ) {
2279 ads_destroy( &ads_s );
2281 talloc_destroy(ctx);
2286 /********************************************************************
2287 ********************************************************************/
2289 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2291 const char *attrs[] = {"domainFunctionality", NULL};
2294 ADS_STRUCT *ads_s = ads;
2296 *val = DS_DOMAIN_FUNCTION_2000;
2298 /* establish a new ldap tcp session if necessary */
2301 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2302 ads->server.ldap_server )) == NULL )
2306 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2307 status = ads_connect( ads_s );
2308 if ( !ADS_ERR_OK(status))
2312 /* If the attribute does not exist assume it is a Windows 2000
2313 functional domain */
2315 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2316 if (!ADS_ERR_OK(status)) {
2317 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2318 status = ADS_SUCCESS;
2323 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2324 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2326 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2329 ads_msgfree(ads, res);
2332 /* free any temporary ads connections */
2333 if ( ads_s != ads ) {
2334 ads_destroy( &ads_s );
2341 * find the domain sid for our domain
2342 * @param ads connection to ads server
2343 * @param sid Pointer to domain sid
2344 * @return status of search
2346 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2348 const char *attrs[] = {"objectSid", NULL};
2352 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2354 if (!ADS_ERR_OK(rc)) return rc;
2355 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2356 ads_msgfree(ads, res);
2357 return ADS_ERROR_SYSTEM(ENOENT);
2359 ads_msgfree(ads, res);
2365 * find our site name
2366 * @param ads connection to ads server
2367 * @param mem_ctx Pointer to talloc context
2368 * @param site_name Pointer to the sitename
2369 * @return status of search
2371 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2375 const char *dn, *service_name;
2376 const char *attrs[] = { "dsServiceName", NULL };
2378 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2379 if (!ADS_ERR_OK(status)) {
2383 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2384 if (service_name == NULL) {
2385 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2388 /* go up three levels */
2389 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2391 return ADS_ERROR(LDAP_NO_MEMORY);
2394 *site_name = talloc_strdup(mem_ctx, dn);
2395 if (*site_name == NULL) {
2396 return ADS_ERROR(LDAP_NO_MEMORY);
2399 ads_msgfree(ads, res);
2403 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2408 * find the site dn where a machine resides
2409 * @param ads connection to ads server
2410 * @param mem_ctx Pointer to talloc context
2411 * @param computer_name name of the machine
2412 * @param site_name Pointer to the sitename
2413 * @return status of search
2415 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2419 const char *parent, *config_context, *filter;
2420 const char *attrs[] = { "configurationNamingContext", NULL };
2423 /* shortcut a query */
2424 if (strequal(computer_name, ads->config.ldap_server_name)) {
2425 return ads_site_dn(ads, mem_ctx, site_dn);
2428 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2429 if (!ADS_ERR_OK(status)) {
2433 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2434 if (config_context == NULL) {
2435 return ADS_ERROR(LDAP_NO_MEMORY);
2438 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2439 if (filter == NULL) {
2440 return ADS_ERROR(LDAP_NO_MEMORY);
2443 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, filter, NULL, &res);
2444 if (!ADS_ERR_OK(status)) {
2448 if (ads_count_replies(ads, res) != 1) {
2449 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2452 dn = ads_get_dn(ads, res);
2454 return ADS_ERROR(LDAP_NO_MEMORY);
2457 /* go up three levels */
2458 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2459 if (parent == NULL) {
2460 ads_memfree(ads, dn);
2461 return ADS_ERROR(LDAP_NO_MEMORY);
2464 *site_dn = talloc_strdup(mem_ctx, parent);
2465 if (*site_dn == NULL) {
2466 ads_memfree(ads, dn);
2467 ADS_ERROR(LDAP_NO_MEMORY);
2470 ads_memfree(ads, dn);
2471 ads_msgfree(ads, res);
2477 * get the upn suffixes for a domain
2478 * @param ads connection to ads server
2479 * @param mem_ctx Pointer to talloc context
2480 * @param suffixes Pointer to an array of suffixes
2481 * @param site_name Pointer to the number of suffixes
2482 * @return status of search
2484 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char **suffixes, size_t *num_suffixes)
2488 const char *config_context, *base;
2489 const char *attrs[] = { "configurationNamingContext", NULL };
2490 const char *attrs2[] = { "uPNSuffixes", NULL };
2492 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2493 if (!ADS_ERR_OK(status)) {
2497 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2498 if (config_context == NULL) {
2499 return ADS_ERROR(LDAP_NO_MEMORY);
2502 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2504 return ADS_ERROR(LDAP_NO_MEMORY);
2507 status = ads_search_dn(ads, &res, base, attrs2);
2508 if (!ADS_ERR_OK(status)) {
2512 if (ads_count_replies(ads, res) != 1) {
2513 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2516 suffixes = ads_pull_strings(ads, mem_ctx, &res, "uPNSuffixes", num_suffixes);
2517 if (suffixes == NULL) {
2518 ads_msgfree(ads, res);
2519 return ADS_ERROR(LDAP_NO_MEMORY);
2522 ads_msgfree(ads, res);
2528 * pull a DOM_SID from an extended dn string
2529 * @param mem_ctx TALLOC_CTX
2530 * @param flags string type of extended_dn
2531 * @param sid pointer to a DOM_SID
2532 * @return boolean inidicating success
2534 BOOL ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
2536 enum ads_extended_dn_flags flags,
2546 * ADS_EXTENDED_DN_HEX_STRING:
2547 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2549 * ADS_EXTENDED_DN_STRING (only with w2k3):
2550 <GUID=63198e23-39cb-4b0f-b032-ba0105525a29>;<SID=S-1-5-21-4257769659-666132843-1169174103-1110>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2553 p = strchr(dn, ';');
2558 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
2562 p += strlen(";<SID=");
2571 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
2575 case ADS_EXTENDED_DN_STRING:
2576 if (!string_to_sid(sid, p)) {
2580 case ADS_EXTENDED_DN_HEX_STRING: {
2584 buf_len = strhex_to_str(buf, strlen(p), p);
2589 if (!sid_parse(buf, buf_len, sid)) {
2590 DEBUG(10,("failed to parse sid\n"));
2596 DEBUG(10,("unknown extended dn format\n"));
2604 * pull an array of DOM_SIDs from a ADS result
2605 * @param ads connection to ads server
2606 * @param mem_ctx TALLOC_CTX for allocating sid array
2607 * @param msg Results of search
2608 * @param field Attribute to retrieve
2609 * @param flags string type of extended_dn
2610 * @param sids pointer to sid array to allocate
2611 * @return the count of SIDs pulled
2613 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
2614 TALLOC_CTX *mem_ctx,
2617 enum ads_extended_dn_flags flags,
2624 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
2625 &dn_count)) == NULL) {
2629 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
2631 TALLOC_FREE(dn_strings);
2635 for (i=0; i<dn_count; i++) {
2637 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
2638 flags, &(*sids)[i])) {
2640 TALLOC_FREE(dn_strings);
2645 TALLOC_FREE(dn_strings);
2650 /********************************************************************
2651 ********************************************************************/
2653 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2655 LDAPMessage *res = NULL;
2660 status = ads_find_machine_acct(ads, (void **)(void *)&res, global_myname());
2661 if (!ADS_ERR_OK(status)) {
2662 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2667 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2668 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2672 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
2673 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
2677 ads_msgfree(ads, res);
2682 /********************************************************************
2683 ********************************************************************/
2685 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2687 LDAPMessage *res = NULL;
2692 status = ads_find_machine_acct(ads, (void **)(void *)&res, global_myname());
2693 if (!ADS_ERR_OK(status)) {
2694 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
2699 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2700 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
2704 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
2705 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
2709 ads_msgfree(ads, res);
2714 /********************************************************************
2715 ********************************************************************/
2717 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2719 LDAPMessage *res = NULL;
2724 status = ads_find_machine_acct(ads, (void **)(void *)&res, global_myname());
2725 if (!ADS_ERR_OK(status)) {
2726 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2731 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2732 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2736 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
2737 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
2741 ads_msgfree(ads, res);