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
);
68 /* Teardown timeout. */
69 CatchSignal(SIGALRM
, SIGNAL_CAST SIG_IGN
);
75 static int ldap_search_with_timeout(LDAP
*ld
,
76 LDAP_CONST
char *base
,
78 LDAP_CONST
char *filter
,
86 struct timeval timeout
;
89 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
90 timeout
.tv_sec
= lp_ldap_timeout();
93 /* Setup alarm timeout.... Do we need both of these ? JRA. */
95 CatchSignal(SIGALRM
, SIGNAL_CAST gotalarm_sig
);
96 alarm(lp_ldap_timeout());
97 /* End setup timeout. */
99 result
= ldap_search_ext_s(ld
, base
, scope
, filter
, attrs
,
100 attrsonly
, sctrls
, cctrls
, &timeout
,
103 /* Teardown timeout. */
104 CatchSignal(SIGALRM
, SIGNAL_CAST SIG_IGN
);
108 return LDAP_TIMELIMIT_EXCEEDED
;
114 try a connection to a given ldap server, returning True and setting the servers IP
115 in the ads struct if successful
117 BOOL
ads_try_connect(ADS_STRUCT
*ads
, const char *server
)
120 struct cldap_netlogon_reply cldap_reply
;
122 if (!server
|| !*server
) {
126 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
127 server
, ads
->server
.realm
));
129 /* this copes with inet_ntoa brokenness */
131 srv
= SMB_STRDUP(server
);
133 ZERO_STRUCT( cldap_reply
);
135 if ( !ads_cldap_netlogon( srv
, ads
->server
.realm
, &cldap_reply
) ) {
136 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv
));
140 /* Check the CLDAP reply flags */
142 if ( !(cldap_reply
.flags
& ADS_LDAP
) ) {
143 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
149 /* Fill in the ads->config values */
151 SAFE_FREE(ads
->config
.realm
);
152 SAFE_FREE(ads
->config
.bind_path
);
153 SAFE_FREE(ads
->config
.ldap_server_name
);
154 SAFE_FREE(ads
->server
.workgroup
);
156 ads
->config
.ldap_server_name
= SMB_STRDUP(cldap_reply
.hostname
);
157 strupper_m(cldap_reply
.domain
);
158 ads
->config
.realm
= SMB_STRDUP(cldap_reply
.domain
);
159 ads
->config
.bind_path
= ads_build_dn(ads
->config
.realm
);
160 ads
->server
.workgroup
= SMB_STRDUP(cldap_reply
.netbios_domain
);
162 ads
->ldap_port
= LDAP_PORT
;
163 ads
->ldap_ip
= *interpret_addr2(srv
);
166 /* cache the successful connection */
168 saf_store( ads
->server
.workgroup
, server
);
173 /**********************************************************************
174 Try to find an AD dc using our internal name resolution routines
175 Try the realm first and then then workgroup name if netbios is not
177 **********************************************************************/
179 static BOOL
ads_find_dc(ADS_STRUCT
*ads
)
183 struct ip_service
*ip_list
;
185 BOOL got_realm
= False
;
186 BOOL use_own_domain
= False
;
188 /* if the realm and workgroup are both empty, assume they are ours */
191 c_realm
= ads
->server
.realm
;
193 if ( !c_realm
|| !*c_realm
) {
194 /* special case where no realm and no workgroup means our own */
195 if ( !ads
->server
.workgroup
|| !*ads
->server
.workgroup
) {
196 use_own_domain
= True
;
197 c_realm
= lp_realm();
201 if (c_realm
&& *c_realm
)
205 /* we need to try once with the realm name and fallback to the
206 netbios domain name if we fail (if netbios has not been disabled */
208 if ( !got_realm
&& !lp_disable_netbios() ) {
209 c_realm
= ads
->server
.workgroup
;
210 if (!c_realm
|| !*c_realm
) {
211 if ( use_own_domain
)
212 c_realm
= lp_workgroup();
215 if ( !c_realm
|| !*c_realm
) {
216 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
221 pstrcpy( realm
, c_realm
);
223 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
224 (got_realm
? "realm" : "domain"), realm
));
226 if ( !get_sorted_dc_list(realm
, &ip_list
, &count
, got_realm
) ) {
227 /* fall back to netbios if we can */
228 if ( got_realm
&& !lp_disable_netbios() ) {
236 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
237 for ( i
=0; i
<count
; i
++ ) {
240 fstrcpy( server
, inet_ntoa(ip_list
[i
].ip
) );
242 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm
, server
)) )
245 if ( ads_try_connect(ads
, server
) ) {
250 /* keep track of failures */
251 add_failed_connection_entry( realm
, server
, NT_STATUS_UNSUCCESSFUL
);
261 * Connect to the LDAP server
262 * @param ads Pointer to an existing ADS_STRUCT
263 * @return status of connection
265 ADS_STATUS
ads_connect(ADS_STRUCT
*ads
)
267 int version
= LDAP_VERSION3
;
270 ads
->last_attempt
= time(NULL
);
273 /* try with a user specified server */
275 if (ads
->server
.ldap_server
&&
276 ads_try_connect(ads
, ads
->server
.ldap_server
)) {
280 if (ads_find_dc(ads
)) {
284 return ADS_ERROR_SYSTEM(errno
?errno
:ENOENT
);
287 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads
->ldap_ip
)));
289 if (!ads
->auth
.user_name
) {
290 /* Must use the userPrincipalName value here or sAMAccountName
291 and not servicePrincipalName; found by Guenther Deschner */
293 asprintf(&ads
->auth
.user_name
, "%s$", global_myname() );
296 if (!ads
->auth
.realm
) {
297 ads
->auth
.realm
= SMB_STRDUP(ads
->config
.realm
);
300 if (!ads
->auth
.kdc_server
) {
301 ads
->auth
.kdc_server
= SMB_STRDUP(inet_ntoa(ads
->ldap_ip
));
305 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
306 to MIT kerberos to work (tridge) */
309 asprintf(&env
, "KRB5_KDC_ADDRESS_%s", ads
->config
.realm
);
310 setenv(env
, ads
->auth
.kdc_server
, 1);
315 /* If the caller() requested no LDAP bind, then we are done */
317 if (ads
->auth
.flags
& ADS_AUTH_NO_BIND
) {
321 /* Otherwise setup the TCP LDAP session */
323 if ( (ads
->ld
= ldap_open_with_timeout(ads
->config
.ldap_server_name
,
324 LDAP_PORT
, lp_ldap_timeout())) == NULL
)
326 return ADS_ERROR(LDAP_OPERATIONS_ERROR
);
328 ldap_set_option(ads
->ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
330 status
= ADS_ERROR(smb_ldap_start_tls(ads
->ld
, version
));
331 if (!ADS_ERR_OK(status
)) {
335 /* fill in the current time and offsets */
337 status
= ads_current_time( ads
);
338 if ( !ADS_ERR_OK(status
) ) {
342 /* Now do the bind */
344 if (ads
->auth
.flags
& ADS_AUTH_ANON_BIND
) {
345 return ADS_ERROR(ldap_simple_bind_s( ads
->ld
, NULL
, NULL
));
348 if (ads
->auth
.flags
& ADS_AUTH_SIMPLE_BIND
) {
349 return ADS_ERROR(ldap_simple_bind_s( ads
->ld
, ads
->auth
.user_name
, ads
->auth
.password
));
352 return ads_sasl_bind(ads
);
356 Duplicate a struct berval into talloc'ed memory
358 static struct berval
*dup_berval(TALLOC_CTX
*ctx
, const struct berval
*in_val
)
360 struct berval
*value
;
362 if (!in_val
) return NULL
;
364 value
= TALLOC_ZERO_P(ctx
, struct berval
);
367 if (in_val
->bv_len
== 0) return value
;
369 value
->bv_len
= in_val
->bv_len
;
370 value
->bv_val
= TALLOC_MEMDUP(ctx
, in_val
->bv_val
, in_val
->bv_len
);
375 Make a values list out of an array of (struct berval *)
377 static struct berval
**ads_dup_values(TALLOC_CTX
*ctx
,
378 const struct berval
**in_vals
)
380 struct berval
**values
;
383 if (!in_vals
) return NULL
;
384 for (i
=0; in_vals
[i
]; i
++)
386 values
= TALLOC_ZERO_ARRAY(ctx
, struct berval
*, i
+1);
387 if (!values
) return NULL
;
389 for (i
=0; in_vals
[i
]; i
++) {
390 values
[i
] = dup_berval(ctx
, in_vals
[i
]);
396 UTF8-encode a values list out of an array of (char *)
398 static char **ads_push_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
403 if (!in_vals
) return NULL
;
404 for (i
=0; in_vals
[i
]; i
++)
406 values
= TALLOC_ZERO_ARRAY(ctx
, char *, i
+1);
407 if (!values
) return NULL
;
409 for (i
=0; in_vals
[i
]; i
++) {
410 push_utf8_talloc(ctx
, &values
[i
], in_vals
[i
]);
416 Pull a (char *) array out of a UTF8-encoded values list
418 static char **ads_pull_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
423 if (!in_vals
) return NULL
;
424 for (i
=0; in_vals
[i
]; i
++)
426 values
= TALLOC_ZERO_ARRAY(ctx
, char *, i
+1);
427 if (!values
) return NULL
;
429 for (i
=0; in_vals
[i
]; i
++) {
430 pull_utf8_talloc(ctx
, &values
[i
], in_vals
[i
]);
436 * Do a search with paged results. cookie must be null on the first
437 * call, and then returned on each subsequent call. It will be null
438 * again when the entire search is complete
439 * @param ads connection to ads server
440 * @param bind_path Base dn for the search
441 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
442 * @param expr Search expression - specified in local charset
443 * @param attrs Attributes to retrieve - specified in utf8 or ascii
444 * @param res ** which will contain results - free res* with ads_msgfree()
445 * @param count Number of entries retrieved on this page
446 * @param cookie The paged results cookie to be returned on subsequent calls
447 * @return status of search
449 ADS_STATUS
ads_do_paged_search_args(ADS_STRUCT
*ads
, const char *bind_path
,
450 int scope
, const char *expr
,
451 const char **attrs
, void *args
, void **res
,
452 int *count
, void **cookie
)
455 char *utf8_expr
, *utf8_path
, **search_attrs
;
456 LDAPControl PagedResults
, NoReferrals
, ExtendedDn
, *controls
[4], **rcontrols
;
457 BerElement
*cookie_be
= NULL
;
458 struct berval
*cookie_bv
= NULL
;
459 BerElement
*extdn_be
= NULL
;
460 struct berval
*extdn_bv
= NULL
;
463 ads_control
*external_control
= (ads_control
*) args
;
467 if (!(ctx
= talloc_init("ads_do_paged_search_args")))
468 return ADS_ERROR(LDAP_NO_MEMORY
);
470 /* 0 means the conversion worked but the result was empty
471 so we only fail if it's -1. In any case, it always
472 at least nulls out the dest */
473 if ((push_utf8_talloc(ctx
, &utf8_expr
, expr
) == (size_t)-1) ||
474 (push_utf8_talloc(ctx
, &utf8_path
, bind_path
) == (size_t)-1)) {
479 if (!attrs
|| !(*attrs
))
482 /* This would be the utf8-encoded version...*/
483 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
484 if (!(str_list_copy(&search_attrs
, attrs
))) {
491 /* Paged results only available on ldap v3 or later */
492 ldap_get_option(ads
->ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
493 if (version
< LDAP_VERSION3
) {
494 rc
= LDAP_NOT_SUPPORTED
;
498 cookie_be
= ber_alloc_t(LBER_USE_DER
);
500 ber_printf(cookie_be
, "{iO}", (ber_int_t
) 1000, *cookie
);
501 ber_bvfree(*cookie
); /* don't need it from last time */
504 ber_printf(cookie_be
, "{io}", (ber_int_t
) 1000, "", 0);
506 ber_flatten(cookie_be
, &cookie_bv
);
507 PagedResults
.ldctl_oid
= CONST_DISCARD(char *, ADS_PAGE_CTL_OID
);
508 PagedResults
.ldctl_iscritical
= (char) 1;
509 PagedResults
.ldctl_value
.bv_len
= cookie_bv
->bv_len
;
510 PagedResults
.ldctl_value
.bv_val
= cookie_bv
->bv_val
;
512 NoReferrals
.ldctl_oid
= CONST_DISCARD(char *, ADS_NO_REFERRALS_OID
);
513 NoReferrals
.ldctl_iscritical
= (char) 0;
514 NoReferrals
.ldctl_value
.bv_len
= 0;
515 NoReferrals
.ldctl_value
.bv_val
= CONST_DISCARD(char *, "");
517 if (external_control
&& strequal(external_control
->control
, ADS_EXTENDED_DN_OID
)) {
519 ExtendedDn
.ldctl_oid
= CONST_DISCARD(char *, external_control
->control
);
520 ExtendedDn
.ldctl_iscritical
= (char) external_control
->critical
;
522 /* win2k does not accept a ldctl_value beeing passed in */
524 if (external_control
->val
!= 0) {
526 if ((extdn_be
= ber_alloc_t(LBER_USE_DER
)) == NULL
) {
531 if ((ber_printf(extdn_be
, "{i}", (ber_int_t
) external_control
->val
)) == -1) {
535 if ((ber_flatten(extdn_be
, &extdn_bv
)) == -1) {
540 ExtendedDn
.ldctl_value
.bv_len
= extdn_bv
->bv_len
;
541 ExtendedDn
.ldctl_value
.bv_val
= extdn_bv
->bv_val
;
544 ExtendedDn
.ldctl_value
.bv_len
= 0;
545 ExtendedDn
.ldctl_value
.bv_val
= NULL
;
548 controls
[0] = &NoReferrals
;
549 controls
[1] = &PagedResults
;
550 controls
[2] = &ExtendedDn
;
554 controls
[0] = &NoReferrals
;
555 controls
[1] = &PagedResults
;
559 /* we need to disable referrals as the openldap libs don't
560 handle them and paged results at the same time. Using them
561 together results in the result record containing the server
562 page control being removed from the result list (tridge/jmcd)
564 leaving this in despite the control that says don't generate
565 referrals, in case the server doesn't support it (jmcd)
567 ldap_set_option(ads
->ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
569 rc
= ldap_search_with_timeout(ads
->ld
, utf8_path
, scope
, utf8_expr
,
570 search_attrs
, 0, controls
,
572 (LDAPMessage
**)res
);
574 ber_free(cookie_be
, 1);
575 ber_bvfree(cookie_bv
);
578 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr
,
579 ldap_err2string(rc
)));
583 rc
= ldap_parse_result(ads
->ld
, *res
, NULL
, NULL
, NULL
,
584 NULL
, &rcontrols
, 0);
590 for (i
=0; rcontrols
[i
]; i
++) {
591 if (strcmp(ADS_PAGE_CTL_OID
, rcontrols
[i
]->ldctl_oid
) == 0) {
592 cookie_be
= ber_init(&rcontrols
[i
]->ldctl_value
);
593 ber_scanf(cookie_be
,"{iO}", (ber_int_t
*) count
,
595 /* the berval is the cookie, but must be freed when
597 if (cookie_bv
->bv_len
) /* still more to do */
598 *cookie
=ber_bvdup(cookie_bv
);
601 ber_bvfree(cookie_bv
);
602 ber_free(cookie_be
, 1);
606 ldap_controls_free(rcontrols
);
612 ber_free(extdn_be
, 1);
616 ber_bvfree(extdn_bv
);
619 /* if/when we decide to utf8-encode attrs, take out this next line */
620 str_list_free(&search_attrs
);
622 return ADS_ERROR(rc
);
625 ADS_STATUS
ads_do_paged_search(ADS_STRUCT
*ads
, const char *bind_path
,
626 int scope
, const char *expr
,
627 const char **attrs
, void **res
,
628 int *count
, void **cookie
)
630 return ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
, count
, cookie
);
635 * Get all results for a search. This uses ads_do_paged_search() to return
636 * all entries in a large search.
637 * @param ads connection to ads server
638 * @param bind_path Base dn for the search
639 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
640 * @param expr Search expression
641 * @param attrs Attributes to retrieve
642 * @param res ** which will contain results - free res* with ads_msgfree()
643 * @return status of search
645 ADS_STATUS
ads_do_search_all_args(ADS_STRUCT
*ads
, const char *bind_path
,
646 int scope
, const char *expr
,
647 const char **attrs
, void *args
, void **res
)
654 status
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, args
, res
,
657 if (!ADS_ERR_OK(status
))
660 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
664 LDAPMessage
*msg
, *next
;
666 status2
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
,
667 attrs
, args
, &res2
, &count
, &cookie
);
669 if (!ADS_ERR_OK(status2
)) break;
671 /* this relies on the way that ldap_add_result_entry() works internally. I hope
672 that this works on all ldap libs, but I have only tested with openldap */
673 for (msg
= ads_first_entry(ads
, res2
); msg
; msg
= next
) {
674 next
= ads_next_entry(ads
, msg
);
675 ldap_add_result_entry((LDAPMessage
**)res
, msg
);
677 /* note that we do not free res2, as the memory is now
678 part of the main returned list */
681 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
682 status
= ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL
);
688 ADS_STATUS
ads_do_search_all(ADS_STRUCT
*ads
, const char *bind_path
,
689 int scope
, const char *expr
,
690 const char **attrs
, void **res
)
692 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
);
696 * Run a function on all results for a search. Uses ads_do_paged_search() and
697 * runs the function as each page is returned, using ads_process_results()
698 * @param ads connection to ads server
699 * @param bind_path Base dn for the search
700 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
701 * @param expr Search expression - specified in local charset
702 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
703 * @param fn Function which takes attr name, values list, and data_area
704 * @param data_area Pointer which is passed to function on each call
705 * @return status of search
707 ADS_STATUS
ads_do_search_all_fn(ADS_STRUCT
*ads
, const char *bind_path
,
708 int scope
, const char *expr
, const char **attrs
,
709 BOOL(*fn
)(char *, void **, void *),
717 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
, &res
,
720 if (!ADS_ERR_OK(status
)) return status
;
722 ads_process_results(ads
, res
, fn
, data_area
);
723 ads_msgfree(ads
, res
);
726 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
,
727 &res
, &count
, &cookie
);
729 if (!ADS_ERR_OK(status
)) break;
731 ads_process_results(ads
, res
, fn
, data_area
);
732 ads_msgfree(ads
, res
);
739 * Do a search with a timeout.
740 * @param ads connection to ads server
741 * @param bind_path Base dn for the search
742 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
743 * @param expr Search expression
744 * @param attrs Attributes to retrieve
745 * @param res ** which will contain results - free res* with ads_msgfree()
746 * @return status of search
748 ADS_STATUS
ads_do_search(ADS_STRUCT
*ads
, const char *bind_path
, int scope
,
750 const char **attrs
, void **res
)
753 char *utf8_expr
, *utf8_path
, **search_attrs
= NULL
;
757 if (!(ctx
= talloc_init("ads_do_search"))) {
758 DEBUG(1,("ads_do_search: talloc_init() failed!"));
759 return ADS_ERROR(LDAP_NO_MEMORY
);
762 /* 0 means the conversion worked but the result was empty
763 so we only fail if it's negative. In any case, it always
764 at least nulls out the dest */
765 if ((push_utf8_talloc(ctx
, &utf8_expr
, expr
) == (size_t)-1) ||
766 (push_utf8_talloc(ctx
, &utf8_path
, bind_path
) == (size_t)-1)) {
767 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
772 if (!attrs
|| !(*attrs
))
775 /* This would be the utf8-encoded version...*/
776 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
777 if (!(str_list_copy(&search_attrs
, attrs
)))
779 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
785 /* see the note in ads_do_paged_search - we *must* disable referrals */
786 ldap_set_option(ads
->ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
788 rc
= ldap_search_with_timeout(ads
->ld
, utf8_path
, scope
, utf8_expr
,
789 search_attrs
, 0, NULL
, NULL
,
791 (LDAPMessage
**)res
);
793 if (rc
== LDAP_SIZELIMIT_EXCEEDED
) {
794 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
800 /* if/when we decide to utf8-encode attrs, take out this next line */
801 str_list_free(&search_attrs
);
802 return ADS_ERROR(rc
);
805 * Do a general ADS search
806 * @param ads connection to ads server
807 * @param res ** which will contain results - free res* with ads_msgfree()
808 * @param expr Search expression
809 * @param attrs Attributes to retrieve
810 * @return status of search
812 ADS_STATUS
ads_search(ADS_STRUCT
*ads
, void **res
,
816 return ads_do_search(ads
, ads
->config
.bind_path
, LDAP_SCOPE_SUBTREE
,
821 * Do a search on a specific DistinguishedName
822 * @param ads connection to ads server
823 * @param res ** which will contain results - free res* with ads_msgfree()
824 * @param dn DistinguishName to search
825 * @param attrs Attributes to retrieve
826 * @return status of search
828 ADS_STATUS
ads_search_dn(ADS_STRUCT
*ads
, void **res
,
832 return ads_do_search(ads
, dn
, LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, res
);
836 * Free up memory from a ads_search
837 * @param ads connection to ads server
838 * @param msg Search results to free
840 void ads_msgfree(ADS_STRUCT
*ads
, void *msg
)
847 * Free up memory from various ads requests
848 * @param ads connection to ads server
849 * @param mem Area to free
851 void ads_memfree(ADS_STRUCT
*ads
, void *mem
)
857 * Get a dn from search results
858 * @param ads connection to ads server
859 * @param msg Search result
862 char *ads_get_dn(ADS_STRUCT
*ads
, void *msg
)
864 char *utf8_dn
, *unix_dn
;
866 utf8_dn
= ldap_get_dn(ads
->ld
, msg
);
869 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
873 if (pull_utf8_allocate(&unix_dn
, utf8_dn
) == (size_t)-1) {
874 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
878 ldap_memfree(utf8_dn
);
883 * Get a canonical dn from search results
884 * @param ads connection to ads server
885 * @param msg Search result
888 char *ads_get_dn_canonical(ADS_STRUCT
*ads
, void *msg
)
890 #ifdef HAVE_LDAP_DN2AD_CANONICAL
891 return ldap_dn2ad_canonical(ads_get_dn(ads
, msg
));
898 * Get the parent from a dn
899 * @param dn the dn to return the parent from
900 * @return parent dn string
902 char *ads_parent_dn(const char *dn
)
920 * Find a machine account given a hostname
921 * @param ads connection to ads server
922 * @param res ** which will contain results - free res* with ads_msgfree()
923 * @param host Hostname to search for
924 * @return status of search
926 ADS_STATUS
ads_find_machine_acct(ADS_STRUCT
*ads
, void **res
, const char *machine
)
930 const char *attrs
[] = {"*", "nTSecurityDescriptor", NULL
};
934 /* the easiest way to find a machine account anywhere in the tree
935 is to look for hostname$ */
936 if (asprintf(&expr
, "(samAccountName=%s$)", machine
) == -1) {
937 DEBUG(1, ("asprintf failed!\n"));
938 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
941 status
= ads_search(ads
, res
, expr
, attrs
);
947 * Initialize a list of mods to be used in a modify request
948 * @param ctx An initialized TALLOC_CTX
949 * @return allocated ADS_MODLIST
951 ADS_MODLIST
ads_init_mods(TALLOC_CTX
*ctx
)
953 #define ADS_MODLIST_ALLOC_SIZE 10
956 if ((mods
= TALLOC_ZERO_ARRAY(ctx
, LDAPMod
*, ADS_MODLIST_ALLOC_SIZE
+ 1)))
957 /* -1 is safety to make sure we don't go over the end.
958 need to reset it to NULL before doing ldap modify */
959 mods
[ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
961 return (ADS_MODLIST
)mods
;
966 add an attribute to the list, with values list already constructed
968 static ADS_STATUS
ads_modlist_add(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
969 int mod_op
, const char *name
,
973 LDAPMod
**modlist
= (LDAPMod
**) *mods
;
974 struct berval
**ber_values
= NULL
;
975 char **char_values
= NULL
;
978 mod_op
= LDAP_MOD_DELETE
;
980 if (mod_op
& LDAP_MOD_BVALUES
)
981 ber_values
= ads_dup_values(ctx
,
982 (const struct berval
**)invals
);
984 char_values
= ads_push_strvals(ctx
,
985 (const char **) invals
);
988 /* find the first empty slot */
989 for (curmod
=0; modlist
[curmod
] && modlist
[curmod
] != (LDAPMod
*) -1;
991 if (modlist
[curmod
] == (LDAPMod
*) -1) {
992 if (!(modlist
= TALLOC_REALLOC_ARRAY(ctx
, modlist
, LDAPMod
*,
993 curmod
+ADS_MODLIST_ALLOC_SIZE
+1)))
994 return ADS_ERROR(LDAP_NO_MEMORY
);
995 memset(&modlist
[curmod
], 0,
996 ADS_MODLIST_ALLOC_SIZE
*sizeof(LDAPMod
*));
997 modlist
[curmod
+ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
998 *mods
= (ADS_MODLIST
)modlist
;
1001 if (!(modlist
[curmod
] = TALLOC_ZERO_P(ctx
, LDAPMod
)))
1002 return ADS_ERROR(LDAP_NO_MEMORY
);
1003 modlist
[curmod
]->mod_type
= talloc_strdup(ctx
, name
);
1004 if (mod_op
& LDAP_MOD_BVALUES
) {
1005 modlist
[curmod
]->mod_bvalues
= ber_values
;
1006 } else if (mod_op
& LDAP_MOD_DELETE
) {
1007 modlist
[curmod
]->mod_values
= NULL
;
1009 modlist
[curmod
]->mod_values
= char_values
;
1012 modlist
[curmod
]->mod_op
= mod_op
;
1013 return ADS_ERROR(LDAP_SUCCESS
);
1017 * Add a single string value to a mod list
1018 * @param ctx An initialized TALLOC_CTX
1019 * @param mods An initialized ADS_MODLIST
1020 * @param name The attribute name to add
1021 * @param val The value to add - NULL means DELETE
1022 * @return ADS STATUS indicating success of add
1024 ADS_STATUS
ads_mod_str(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1025 const char *name
, const char *val
)
1027 const char *values
[2];
1033 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1034 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
, name
,
1035 (const void **) values
);
1039 * Add an array of string values to a mod list
1040 * @param ctx An initialized TALLOC_CTX
1041 * @param mods An initialized ADS_MODLIST
1042 * @param name The attribute name to add
1043 * @param vals The array of string values to add - NULL means DELETE
1044 * @return ADS STATUS indicating success of add
1046 ADS_STATUS
ads_mod_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1047 const char *name
, const char **vals
)
1050 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1051 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
,
1052 name
, (const void **) vals
);
1057 * Add a single ber-encoded value to a mod list
1058 * @param ctx An initialized TALLOC_CTX
1059 * @param mods An initialized ADS_MODLIST
1060 * @param name The attribute name to add
1061 * @param val The value to add - NULL means DELETE
1062 * @return ADS STATUS indicating success of add
1064 static ADS_STATUS
ads_mod_ber(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1065 const char *name
, const struct berval
*val
)
1067 const struct berval
*values
[2];
1072 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1073 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
|LDAP_MOD_BVALUES
,
1074 name
, (const void **) values
);
1079 * Perform an ldap modify
1080 * @param ads connection to ads server
1081 * @param mod_dn DistinguishedName to modify
1082 * @param mods list of modifications to perform
1083 * @return status of modify
1085 ADS_STATUS
ads_gen_mod(ADS_STRUCT
*ads
, const char *mod_dn
, ADS_MODLIST mods
)
1088 char *utf8_dn
= NULL
;
1090 this control is needed to modify that contains a currently
1091 non-existent attribute (but allowable for the object) to run
1093 LDAPControl PermitModify
= {
1094 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID
),
1097 LDAPControl
*controls
[2];
1099 controls
[0] = &PermitModify
;
1102 if (push_utf8_allocate(&utf8_dn
, mod_dn
) == -1) {
1103 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1106 /* find the end of the list, marked by NULL or -1 */
1107 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1108 /* make sure the end of the list is NULL */
1110 ret
= ldap_modify_ext_s(ads
->ld
, utf8_dn
,
1111 (LDAPMod
**) mods
, controls
, NULL
);
1113 return ADS_ERROR(ret
);
1117 * Perform an ldap add
1118 * @param ads connection to ads server
1119 * @param new_dn DistinguishedName to add
1120 * @param mods list of attributes and values for DN
1121 * @return status of add
1123 ADS_STATUS
ads_gen_add(ADS_STRUCT
*ads
, const char *new_dn
, ADS_MODLIST mods
)
1126 char *utf8_dn
= NULL
;
1128 if (push_utf8_allocate(&utf8_dn
, new_dn
) == -1) {
1129 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1130 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1133 /* find the end of the list, marked by NULL or -1 */
1134 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1135 /* make sure the end of the list is NULL */
1138 ret
= ldap_add_s(ads
->ld
, utf8_dn
, (LDAPMod
**)mods
);
1140 return ADS_ERROR(ret
);
1144 * Delete a DistinguishedName
1145 * @param ads connection to ads server
1146 * @param new_dn DistinguishedName to delete
1147 * @return status of delete
1149 ADS_STATUS
ads_del_dn(ADS_STRUCT
*ads
, char *del_dn
)
1152 char *utf8_dn
= NULL
;
1153 if (push_utf8_allocate(&utf8_dn
, del_dn
) == -1) {
1154 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1155 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1158 ret
= ldap_delete_s(ads
->ld
, utf8_dn
);
1159 return ADS_ERROR(ret
);
1163 * Build an org unit string
1164 * if org unit is Computers or blank then assume a container, otherwise
1165 * assume a / separated list of organisational units.
1166 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1167 * @param ads connection to ads server
1168 * @param org_unit Organizational unit
1169 * @return org unit string - caller must free
1171 char *ads_ou_string(ADS_STRUCT
*ads
, const char *org_unit
)
1175 if (!org_unit
|| !*org_unit
) {
1177 ret
= ads_default_ou_string(ads
, WELL_KNOWN_GUID_COMPUTERS
);
1179 /* samba4 might not yet respond to a wellknownobject-query */
1180 return ret
? ret
: SMB_STRDUP("cn=Computers");
1183 if (strequal(org_unit
, "Computers")) {
1184 return SMB_STRDUP("cn=Computers");
1187 /* jmcd: removed "\\" from the separation chars, because it is
1188 needed as an escape for chars like '#' which are valid in an
1190 return ads_build_path(org_unit
, "/", "ou=", 1);
1194 * Get a org unit string for a well-known GUID
1195 * @param ads connection to ads server
1196 * @param wknguid Well known GUID
1197 * @return org unit string - caller must free
1199 char *ads_default_ou_string(ADS_STRUCT
*ads
, const char *wknguid
)
1203 char *base
, *wkn_dn
, *ret
, **wkn_dn_exp
, **bind_dn_exp
;
1204 const char *attrs
[] = {"distinguishedName", NULL
};
1205 int new_ln
, wkn_ln
, bind_ln
, i
;
1207 if (wknguid
== NULL
) {
1211 if (asprintf(&base
, "<WKGUID=%s,%s>", wknguid
, ads
->config
.bind_path
) == -1) {
1212 DEBUG(1, ("asprintf failed!\n"));
1216 status
= ads_search_dn(ads
, &res
, base
, attrs
);
1217 if (!ADS_ERR_OK(status
)) {
1218 DEBUG(1,("Failed while searching for: %s\n", base
));
1224 if (ads_count_replies(ads
, res
) != 1) {
1228 /* substitute the bind-path from the well-known-guid-search result */
1229 wkn_dn
= ads_get_dn(ads
, res
);
1230 wkn_dn_exp
= ldap_explode_dn(wkn_dn
, 0);
1231 bind_dn_exp
= ldap_explode_dn(ads
->config
.bind_path
, 0);
1233 for (wkn_ln
=0; wkn_dn_exp
[wkn_ln
]; wkn_ln
++)
1235 for (bind_ln
=0; bind_dn_exp
[bind_ln
]; bind_ln
++)
1238 new_ln
= wkn_ln
- bind_ln
;
1240 ret
= wkn_dn_exp
[0];
1242 for (i
=1; i
< new_ln
; i
++) {
1244 asprintf(&s
, "%s,%s", ret
, wkn_dn_exp
[i
]);
1245 ret
= SMB_STRDUP(s
);
1249 ads_memfree(ads
, wkn_dn
);
1250 ldap_value_free(wkn_dn_exp
);
1251 ldap_value_free(bind_dn_exp
);
1257 * Adds (appends) an item to an attribute array, rather then
1258 * replacing the whole list
1259 * @param ctx An initialized TALLOC_CTX
1260 * @param mods An initialized ADS_MODLIST
1261 * @param name name of the ldap attribute to append to
1262 * @param vals an array of values to add
1263 * @return status of addition
1266 ADS_STATUS
ads_add_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1267 const char *name
, const char **vals
)
1269 return ads_modlist_add(ctx
, mods
, LDAP_MOD_ADD
, name
, (const void **) vals
);
1273 * Determines the computer account's current KVNO via an LDAP lookup
1274 * @param ads An initialized ADS_STRUCT
1275 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1276 * @return the kvno for the computer account, or -1 in case of a failure.
1279 uint32
ads_get_kvno(ADS_STRUCT
*ads
, const char *machine_name
)
1281 LDAPMessage
*res
= NULL
;
1282 uint32 kvno
= (uint32
)-1; /* -1 indicates a failure */
1284 const char *attrs
[] = {"msDS-KeyVersionNumber", NULL
};
1285 char *dn_string
= NULL
;
1286 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1288 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name
));
1289 if (asprintf(&filter
, "(samAccountName=%s$)", machine_name
) == -1) {
1292 ret
= ads_search(ads
, (void**)(void *)&res
, filter
, attrs
);
1294 if (!ADS_ERR_OK(ret
) && ads_count_replies(ads
, res
)) {
1295 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name
));
1296 ads_msgfree(ads
, res
);
1300 dn_string
= ads_get_dn(ads
, res
);
1302 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1303 ads_msgfree(ads
, res
);
1306 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string
));
1307 ads_memfree(ads
, dn_string
);
1309 /* ---------------------------------------------------------
1310 * 0 is returned as a default KVNO from this point on...
1311 * This is done because Windows 2000 does not support key
1312 * version numbers. Chances are that a failure in the next
1313 * step is simply due to Windows 2000 being used for a
1314 * domain controller. */
1317 if (!ads_pull_uint32(ads
, res
, "msDS-KeyVersionNumber", &kvno
)) {
1318 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1319 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1320 ads_msgfree(ads
, res
);
1325 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno
));
1326 ads_msgfree(ads
, res
);
1331 * This clears out all registered spn's for a given hostname
1332 * @param ads An initilaized ADS_STRUCT
1333 * @param machine_name the NetBIOS name of the computer.
1334 * @return 0 upon success, non-zero otherwise.
1337 ADS_STATUS
ads_clear_service_principal_names(ADS_STRUCT
*ads
, const char *machine_name
)
1340 LDAPMessage
*res
= NULL
;
1342 const char *servicePrincipalName
[1] = {NULL
};
1343 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1344 char *dn_string
= NULL
;
1346 ret
= ads_find_machine_acct(ads
, (void **)(void *)&res
, machine_name
);
1347 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1348 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name
));
1349 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name
));
1350 ads_msgfree(ads
, res
);
1351 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1354 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name
));
1355 ctx
= talloc_init("ads_clear_service_principal_names");
1357 ads_msgfree(ads
, res
);
1358 return ADS_ERROR(LDAP_NO_MEMORY
);
1361 if (!(mods
= ads_init_mods(ctx
))) {
1362 talloc_destroy(ctx
);
1363 ads_msgfree(ads
, res
);
1364 return ADS_ERROR(LDAP_NO_MEMORY
);
1366 ret
= ads_mod_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1367 if (!ADS_ERR_OK(ret
)) {
1368 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1369 ads_msgfree(ads
, res
);
1370 talloc_destroy(ctx
);
1373 dn_string
= ads_get_dn(ads
, res
);
1375 talloc_destroy(ctx
);
1376 ads_msgfree(ads
, res
);
1377 return ADS_ERROR(LDAP_NO_MEMORY
);
1379 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1380 ads_memfree(ads
,dn_string
);
1381 if (!ADS_ERR_OK(ret
)) {
1382 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1384 ads_msgfree(ads
, res
);
1385 talloc_destroy(ctx
);
1389 ads_msgfree(ads
, res
);
1390 talloc_destroy(ctx
);
1395 * This adds a service principal name to an existing computer account
1396 * (found by hostname) in AD.
1397 * @param ads An initialized ADS_STRUCT
1398 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1399 * @param my_fqdn The fully qualified DNS name of the machine
1400 * @param spn A string of the service principal to add, i.e. 'host'
1401 * @return 0 upon sucess, or non-zero if a failure occurs
1404 ADS_STATUS
ads_add_service_principal_name(ADS_STRUCT
*ads
, const char *machine_name
,
1405 const char *my_fqdn
, const char *spn
)
1409 LDAPMessage
*res
= NULL
;
1412 char *dn_string
= NULL
;
1413 const char *servicePrincipalName
[3] = {NULL
, NULL
, NULL
};
1415 ret
= ads_find_machine_acct(ads
, (void **)(void *)&res
, machine_name
);
1416 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1417 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1419 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1420 spn
, machine_name
, ads
->config
.realm
));
1421 ads_msgfree(ads
, res
);
1422 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1425 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name
));
1426 if (!(ctx
= talloc_init("ads_add_service_principal_name"))) {
1427 ads_msgfree(ads
, res
);
1428 return ADS_ERROR(LDAP_NO_MEMORY
);
1431 /* add short name spn */
1433 if ( (psp1
= talloc_asprintf(ctx
, "%s/%s", spn
, machine_name
)) == NULL
) {
1434 talloc_destroy(ctx
);
1435 ads_msgfree(ads
, res
);
1436 return ADS_ERROR(LDAP_NO_MEMORY
);
1439 strlower_m(&psp1
[strlen(spn
)]);
1440 servicePrincipalName
[0] = psp1
;
1442 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1443 psp1
, machine_name
));
1446 /* add fully qualified spn */
1448 if ( (psp2
= talloc_asprintf(ctx
, "%s/%s", spn
, my_fqdn
)) == NULL
) {
1449 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1453 strlower_m(&psp2
[strlen(spn
)]);
1454 servicePrincipalName
[1] = psp2
;
1456 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1457 psp2
, machine_name
));
1459 if ( (mods
= ads_init_mods(ctx
)) == NULL
) {
1460 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1464 ret
= ads_add_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1465 if (!ADS_ERR_OK(ret
)) {
1466 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1470 if ( (dn_string
= ads_get_dn(ads
, res
)) == NULL
) {
1471 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1475 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1476 ads_memfree(ads
,dn_string
);
1477 if (!ADS_ERR_OK(ret
)) {
1478 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1484 ads_msgfree(ads
, res
);
1489 * adds a machine account to the ADS server
1490 * @param ads An intialized ADS_STRUCT
1491 * @param machine_name - the NetBIOS machine name of this account.
1492 * @param account_type A number indicating the type of account to create
1493 * @param org_unit The LDAP path in which to place this account
1494 * @return 0 upon success, or non-zero otherwise
1497 ADS_STATUS
ads_create_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
1498 const char *org_unit
)
1501 char *samAccountName
, *controlstr
;
1505 const char *objectClass
[] = {"top", "person", "organizationalPerson",
1506 "user", "computer", NULL
};
1507 LDAPMessage
*res
= NULL
;
1508 uint32 acct_control
= ( UF_WORKSTATION_TRUST_ACCOUNT
|\
1509 UF_DONT_EXPIRE_PASSWD
|\
1510 UF_ACCOUNTDISABLE
);
1512 if (!(ctx
= talloc_init("ads_add_machine_acct")))
1513 return ADS_ERROR(LDAP_NO_MEMORY
);
1515 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1517 new_dn
= talloc_asprintf(ctx
, "cn=%s,%s", machine_name
, org_unit
);
1518 samAccountName
= talloc_asprintf(ctx
, "%s$", machine_name
);
1520 if ( !new_dn
|| !samAccountName
) {
1524 #ifndef ENCTYPE_ARCFOUR_HMAC
1525 acct_control
|= UF_USE_DES_KEY_ONLY
;
1528 if (!(controlstr
= talloc_asprintf(ctx
, "%u", acct_control
))) {
1532 if (!(mods
= ads_init_mods(ctx
))) {
1536 ads_mod_str(ctx
, &mods
, "cn", machine_name
);
1537 ads_mod_str(ctx
, &mods
, "sAMAccountName", samAccountName
);
1538 ads_mod_strlist(ctx
, &mods
, "objectClass", objectClass
);
1539 ads_mod_str(ctx
, &mods
, "userAccountControl", controlstr
);
1541 ret
= ads_gen_add(ads
, new_dn
, mods
);
1544 ads_msgfree(ads
, res
);
1545 talloc_destroy(ctx
);
1551 dump a binary result from ldap
1553 static void dump_binary(const char *field
, struct berval
**values
)
1556 for (i
=0; values
[i
]; i
++) {
1557 printf("%s: ", field
);
1558 for (j
=0; j
<values
[i
]->bv_len
; j
++) {
1559 printf("%02X", (unsigned char)values
[i
]->bv_val
[j
]);
1565 static void dump_guid(const char *field
, struct berval
**values
)
1569 for (i
=0; values
[i
]; i
++) {
1570 memcpy(guid
.info
, values
[i
]->bv_val
, sizeof(guid
.info
));
1571 printf("%s: %s\n", field
,
1572 smb_uuid_string_static(smb_uuid_unpack_static(guid
)));
1577 dump a sid result from ldap
1579 static void dump_sid(const char *field
, struct berval
**values
)
1582 for (i
=0; values
[i
]; i
++) {
1584 sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &sid
);
1585 printf("%s: %s\n", field
, sid_string_static(&sid
));
1590 dump ntSecurityDescriptor
1592 static void dump_sd(const char *filed
, struct berval
**values
)
1597 TALLOC_CTX
*ctx
= 0;
1599 if (!(ctx
= talloc_init("sec_io_desc")))
1603 prs_init(&ps
, values
[0]->bv_len
, ctx
, UNMARSHALL
);
1604 prs_copy_data_in(&ps
, values
[0]->bv_val
, values
[0]->bv_len
);
1605 prs_set_offset(&ps
,0);
1608 if (!sec_io_desc("sd", &psd
, &ps
, 1)) {
1610 talloc_destroy(ctx
);
1613 if (psd
) ads_disp_sd(psd
);
1616 talloc_destroy(ctx
);
1620 dump a string result from ldap
1622 static void dump_string(const char *field
, char **values
)
1625 for (i
=0; values
[i
]; i
++) {
1626 printf("%s: %s\n", field
, values
[i
]);
1631 dump a field from LDAP on stdout
1635 static BOOL
ads_dump_field(char *field
, void **values
, void *data_area
)
1640 void (*handler
)(const char *, struct berval
**);
1642 {"objectGUID", False
, dump_guid
},
1643 {"netbootGUID", False
, dump_guid
},
1644 {"nTSecurityDescriptor", False
, dump_sd
},
1645 {"dnsRecord", False
, dump_binary
},
1646 {"objectSid", False
, dump_sid
},
1647 {"tokenGroups", False
, dump_sid
},
1648 {"tokenGroupsNoGCAcceptable", False
, dump_sid
},
1649 {"tokengroupsGlobalandUniversal", False
, dump_sid
},
1654 if (!field
) { /* must be end of an entry */
1659 for (i
=0; handlers
[i
].name
; i
++) {
1660 if (StrCaseCmp(handlers
[i
].name
, field
) == 0) {
1661 if (!values
) /* first time, indicate string or not */
1662 return handlers
[i
].string
;
1663 handlers
[i
].handler(field
, (struct berval
**) values
);
1667 if (!handlers
[i
].name
) {
1668 if (!values
) /* first time, indicate string conversion */
1670 dump_string(field
, (char **)values
);
1676 * Dump a result from LDAP on stdout
1677 * used for debugging
1678 * @param ads connection to ads server
1679 * @param res Results to dump
1682 void ads_dump(ADS_STRUCT
*ads
, void *res
)
1684 ads_process_results(ads
, res
, ads_dump_field
, NULL
);
1688 * Walk through results, calling a function for each entry found.
1689 * The function receives a field name, a berval * array of values,
1690 * and a data area passed through from the start. The function is
1691 * called once with null for field and values at the end of each
1693 * @param ads connection to ads server
1694 * @param res Results to process
1695 * @param fn Function for processing each result
1696 * @param data_area user-defined area to pass to function
1698 void ads_process_results(ADS_STRUCT
*ads
, void *res
,
1699 BOOL(*fn
)(char *, void **, void *),
1705 if (!(ctx
= talloc_init("ads_process_results")))
1708 for (msg
= ads_first_entry(ads
, res
); msg
;
1709 msg
= ads_next_entry(ads
, msg
)) {
1713 for (utf8_field
=ldap_first_attribute(ads
->ld
,
1714 (LDAPMessage
*)msg
,&b
);
1716 utf8_field
=ldap_next_attribute(ads
->ld
,
1717 (LDAPMessage
*)msg
,b
)) {
1718 struct berval
**ber_vals
;
1719 char **str_vals
, **utf8_vals
;
1723 pull_utf8_talloc(ctx
, &field
, utf8_field
);
1724 string
= fn(field
, NULL
, data_area
);
1727 utf8_vals
= ldap_get_values(ads
->ld
,
1728 (LDAPMessage
*)msg
, field
);
1729 str_vals
= ads_pull_strvals(ctx
,
1730 (const char **) utf8_vals
);
1731 fn(field
, (void **) str_vals
, data_area
);
1732 ldap_value_free(utf8_vals
);
1734 ber_vals
= ldap_get_values_len(ads
->ld
,
1735 (LDAPMessage
*)msg
, field
);
1736 fn(field
, (void **) ber_vals
, data_area
);
1738 ldap_value_free_len(ber_vals
);
1740 ldap_memfree(utf8_field
);
1743 talloc_free_children(ctx
);
1744 fn(NULL
, NULL
, data_area
); /* completed an entry */
1747 talloc_destroy(ctx
);
1751 * count how many replies are in a LDAPMessage
1752 * @param ads connection to ads server
1753 * @param res Results to count
1754 * @return number of replies
1756 int ads_count_replies(ADS_STRUCT
*ads
, void *res
)
1758 return ldap_count_entries(ads
->ld
, (LDAPMessage
*)res
);
1762 * pull the first entry from a ADS result
1763 * @param ads connection to ads server
1764 * @param res Results of search
1765 * @return first entry from result
1767 void *ads_first_entry(ADS_STRUCT
*ads
, void *res
)
1769 return (void *)ldap_first_entry(ads
->ld
, (LDAPMessage
*)res
);
1773 * pull the next entry from a ADS result
1774 * @param ads connection to ads server
1775 * @param res Results of search
1776 * @return next entry from result
1778 void *ads_next_entry(ADS_STRUCT
*ads
, void *res
)
1780 return (void *)ldap_next_entry(ads
->ld
, (LDAPMessage
*)res
);
1784 * pull a single string from a ADS result
1785 * @param ads connection to ads server
1786 * @param mem_ctx TALLOC_CTX to use for allocating result string
1787 * @param msg Results of search
1788 * @param field Attribute to retrieve
1789 * @return Result string in talloc context
1791 char *ads_pull_string(ADS_STRUCT
*ads
,
1792 TALLOC_CTX
*mem_ctx
, void *msg
, const char *field
)
1799 values
= ldap_get_values(ads
->ld
, msg
, field
);
1804 rc
= pull_utf8_talloc(mem_ctx
, &ux_string
,
1806 if (rc
!= (size_t)-1)
1810 ldap_value_free(values
);
1815 * pull an array of strings from a ADS result
1816 * @param ads connection to ads server
1817 * @param mem_ctx TALLOC_CTX to use for allocating result string
1818 * @param msg Results of search
1819 * @param field Attribute to retrieve
1820 * @return Result strings in talloc context
1822 char **ads_pull_strings(ADS_STRUCT
*ads
,
1823 TALLOC_CTX
*mem_ctx
, void *msg
, const char *field
,
1830 values
= ldap_get_values(ads
->ld
, msg
, field
);
1834 *num_values
= ldap_count_values(values
);
1836 ret
= TALLOC_ARRAY(mem_ctx
, char *, *num_values
+ 1);
1838 ldap_value_free(values
);
1842 for (i
=0;i
<*num_values
;i
++) {
1843 if (pull_utf8_talloc(mem_ctx
, &ret
[i
], values
[i
]) == -1) {
1844 ldap_value_free(values
);
1850 ldap_value_free(values
);
1855 * pull an array of strings from a ADS result
1856 * (handle large multivalue attributes with range retrieval)
1857 * @param ads connection to ads server
1858 * @param mem_ctx TALLOC_CTX to use for allocating result string
1859 * @param msg Results of search
1860 * @param field Attribute to retrieve
1861 * @param current_strings strings returned by a previous call to this function
1862 * @param next_attribute The next query should ask for this attribute
1863 * @param num_values How many values did we get this time?
1864 * @param more_values Are there more values to get?
1865 * @return Result strings in talloc context
1867 char **ads_pull_strings_range(ADS_STRUCT
*ads
,
1868 TALLOC_CTX
*mem_ctx
,
1869 void *msg
, const char *field
,
1870 char **current_strings
,
1871 const char **next_attribute
,
1872 size_t *num_strings
,
1876 char *expected_range_attrib
, *range_attr
;
1877 BerElement
*ptr
= NULL
;
1880 size_t num_new_strings
;
1881 unsigned long int range_start
;
1882 unsigned long int range_end
;
1884 /* we might have been given the whole lot anyway */
1885 if ((strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
, num_strings
))) {
1886 *more_strings
= False
;
1890 expected_range_attrib
= talloc_asprintf(mem_ctx
, "%s;Range=", field
);
1892 /* look for Range result */
1893 for (attr
= ldap_first_attribute(ads
->ld
, (LDAPMessage
*)msg
, &ptr
);
1895 attr
= ldap_next_attribute(ads
->ld
, (LDAPMessage
*)msg
, ptr
)) {
1896 /* we ignore the fact that this is utf8, as all attributes are ascii... */
1897 if (strnequal(attr
, expected_range_attrib
, strlen(expected_range_attrib
))) {
1905 /* nothing here - this field is just empty */
1906 *more_strings
= False
;
1910 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-%lu",
1911 &range_start
, &range_end
) == 2) {
1912 *more_strings
= True
;
1914 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-*",
1915 &range_start
) == 1) {
1916 *more_strings
= False
;
1918 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
1920 ldap_memfree(range_attr
);
1921 *more_strings
= False
;
1926 if ((*num_strings
) != range_start
) {
1927 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
1928 " - aborting range retreival\n",
1929 range_attr
, (unsigned int)(*num_strings
) + 1, range_start
));
1930 ldap_memfree(range_attr
);
1931 *more_strings
= False
;
1935 new_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, range_attr
, &num_new_strings
);
1937 if (*more_strings
&& ((*num_strings
+ num_new_strings
) != (range_end
+ 1))) {
1938 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
1939 "strings in this bunch, but we only got %lu - aborting range retreival\n",
1940 range_attr
, (unsigned long int)range_end
- range_start
+ 1,
1941 (unsigned long int)num_new_strings
));
1942 ldap_memfree(range_attr
);
1943 *more_strings
= False
;
1947 strings
= TALLOC_REALLOC_ARRAY(mem_ctx
, current_strings
, char *,
1948 *num_strings
+ num_new_strings
);
1950 if (strings
== NULL
) {
1951 ldap_memfree(range_attr
);
1952 *more_strings
= False
;
1956 if (new_strings
&& num_new_strings
) {
1957 memcpy(&strings
[*num_strings
], new_strings
,
1958 sizeof(*new_strings
) * num_new_strings
);
1961 (*num_strings
) += num_new_strings
;
1963 if (*more_strings
) {
1964 *next_attribute
= talloc_asprintf(mem_ctx
,
1969 if (!*next_attribute
) {
1970 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
1971 ldap_memfree(range_attr
);
1972 *more_strings
= False
;
1977 ldap_memfree(range_attr
);
1983 * pull a single uint32 from a ADS result
1984 * @param ads connection to ads server
1985 * @param msg Results of search
1986 * @param field Attribute to retrieve
1987 * @param v Pointer to int to store result
1988 * @return boolean inidicating success
1990 BOOL
ads_pull_uint32(ADS_STRUCT
*ads
,
1991 void *msg
, const char *field
, uint32
*v
)
1995 values
= ldap_get_values(ads
->ld
, msg
, field
);
1999 ldap_value_free(values
);
2003 *v
= atoi(values
[0]);
2004 ldap_value_free(values
);
2009 * pull a single objectGUID from an ADS result
2010 * @param ads connection to ADS server
2011 * @param msg results of search
2012 * @param guid 37-byte area to receive text guid
2013 * @return boolean indicating success
2015 BOOL
ads_pull_guid(ADS_STRUCT
*ads
,
2016 void *msg
, struct uuid
*guid
)
2019 UUID_FLAT flat_guid
;
2021 values
= ldap_get_values(ads
->ld
, msg
, "objectGUID");
2026 memcpy(&flat_guid
.info
, values
[0], sizeof(UUID_FLAT
));
2027 smb_uuid_unpack(flat_guid
, guid
);
2028 ldap_value_free(values
);
2031 ldap_value_free(values
);
2038 * pull a single DOM_SID from a ADS result
2039 * @param ads connection to ads server
2040 * @param msg Results of search
2041 * @param field Attribute to retrieve
2042 * @param sid Pointer to sid to store result
2043 * @return boolean inidicating success
2045 BOOL
ads_pull_sid(ADS_STRUCT
*ads
,
2046 void *msg
, const char *field
, DOM_SID
*sid
)
2048 struct berval
**values
;
2051 values
= ldap_get_values_len(ads
->ld
, msg
, field
);
2057 ret
= sid_parse(values
[0]->bv_val
, values
[0]->bv_len
, sid
);
2059 ldap_value_free_len(values
);
2064 * pull an array of DOM_SIDs from a ADS result
2065 * @param ads connection to ads server
2066 * @param mem_ctx TALLOC_CTX for allocating sid array
2067 * @param msg Results of search
2068 * @param field Attribute to retrieve
2069 * @param sids pointer to sid array to allocate
2070 * @return the count of SIDs pulled
2072 int ads_pull_sids(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2073 void *msg
, const char *field
, DOM_SID
**sids
)
2075 struct berval
**values
;
2079 values
= ldap_get_values_len(ads
->ld
, msg
, field
);
2084 for (i
=0; values
[i
]; i
++)
2087 (*sids
) = TALLOC_ARRAY(mem_ctx
, DOM_SID
, i
);
2089 ldap_value_free_len(values
);
2094 for (i
=0; values
[i
]; i
++) {
2095 ret
= sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &(*sids
)[count
]);
2098 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid
, &(*sids
)[count
])));
2103 ldap_value_free_len(values
);
2108 * pull a SEC_DESC from a ADS result
2109 * @param ads connection to ads server
2110 * @param mem_ctx TALLOC_CTX for allocating sid array
2111 * @param msg Results of search
2112 * @param field Attribute to retrieve
2113 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2114 * @return boolean inidicating success
2116 BOOL
ads_pull_sd(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2117 void *msg
, const char *field
, SEC_DESC
**sd
)
2119 struct berval
**values
;
2123 values
= ldap_get_values_len(ads
->ld
, msg
, field
);
2125 if (!values
) return False
;
2128 prs_init(&ps
, values
[0]->bv_len
, mem_ctx
, UNMARSHALL
);
2129 prs_copy_data_in(&ps
, values
[0]->bv_val
, values
[0]->bv_len
);
2130 prs_set_offset(&ps
,0);
2132 ret
= sec_io_desc("sd", sd
, &ps
, 1);
2135 ldap_value_free_len(values
);
2140 * in order to support usernames longer than 21 characters we need to
2141 * use both the sAMAccountName and the userPrincipalName attributes
2142 * It seems that not all users have the userPrincipalName attribute set
2144 * @param ads connection to ads server
2145 * @param mem_ctx TALLOC_CTX for allocating sid array
2146 * @param msg Results of search
2147 * @return the username
2149 char *ads_pull_username(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, void *msg
)
2154 /* lookup_name() only works on the sAMAccountName to
2155 returning the username portion of userPrincipalName
2156 breaks winbindd_getpwnam() */
2158 ret
= ads_pull_string(ads
, mem_ctx
, msg
, "userPrincipalName");
2159 if (ret
&& (p
= strchr_m(ret
, '@'))) {
2164 return ads_pull_string(ads
, mem_ctx
, msg
, "sAMAccountName");
2169 * find the update serial number - this is the core of the ldap cache
2170 * @param ads connection to ads server
2171 * @param ads connection to ADS server
2172 * @param usn Pointer to retrieved update serial number
2173 * @return status of search
2175 ADS_STATUS
ads_USN(ADS_STRUCT
*ads
, uint32
*usn
)
2177 const char *attrs
[] = {"highestCommittedUSN", NULL
};
2181 status
= ads_do_search_retry(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2182 if (!ADS_ERR_OK(status
))
2185 if (ads_count_replies(ads
, res
) != 1) {
2186 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2189 ads_pull_uint32(ads
, res
, "highestCommittedUSN", usn
);
2190 ads_msgfree(ads
, res
);
2194 /* parse a ADS timestring - typical string is
2195 '20020917091222.0Z0' which means 09:12.22 17th September
2197 static time_t ads_parse_time(const char *str
)
2203 if (sscanf(str
, "%4d%2d%2d%2d%2d%2d",
2204 &tm
.tm_year
, &tm
.tm_mon
, &tm
.tm_mday
,
2205 &tm
.tm_hour
, &tm
.tm_min
, &tm
.tm_sec
) != 6) {
2214 /********************************************************************
2215 ********************************************************************/
2217 ADS_STATUS
ads_current_time(ADS_STRUCT
*ads
)
2219 const char *attrs
[] = {"currentTime", NULL
};
2224 ADS_STRUCT
*ads_s
= ads
;
2226 if (!(ctx
= talloc_init("ads_current_time"))) {
2227 return ADS_ERROR(LDAP_NO_MEMORY
);
2230 /* establish a new ldap tcp session if necessary */
2233 if ( (ads_s
= ads_init( ads
->server
.realm
, ads
->server
.workgroup
,
2234 ads
->server
.ldap_server
)) == NULL
)
2238 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
2239 status
= ads_connect( ads_s
);
2240 if ( !ADS_ERR_OK(status
))
2244 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2245 if (!ADS_ERR_OK(status
)) {
2249 timestr
= ads_pull_string(ads_s
, ctx
, res
, "currentTime");
2251 ads_msgfree(ads
, res
);
2252 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2256 /* but save the time and offset in the original ADS_STRUCT */
2258 ads
->config
.current_time
= ads_parse_time(timestr
);
2260 if (ads
->config
.current_time
!= 0) {
2261 ads
->auth
.time_offset
= ads
->config
.current_time
- time(NULL
);
2262 DEBUG(4,("time offset is %d seconds\n", ads
->auth
.time_offset
));
2265 ads_msgfree(ads
, res
);
2267 status
= ADS_SUCCESS
;
2270 /* free any temporary ads connections */
2271 if ( ads_s
!= ads
) {
2272 ads_destroy( &ads_s
);
2274 talloc_destroy(ctx
);
2279 /********************************************************************
2280 ********************************************************************/
2282 ADS_STATUS
ads_domain_func_level(ADS_STRUCT
*ads
, uint32
*val
)
2284 const char *attrs
[] = {"domainFunctionality", NULL
};
2287 ADS_STRUCT
*ads_s
= ads
;
2289 *val
= DS_DOMAIN_FUNCTION_2000
;
2291 /* establish a new ldap tcp session if necessary */
2294 if ( (ads_s
= ads_init( ads
->server
.realm
, ads
->server
.workgroup
,
2295 ads
->server
.ldap_server
)) == NULL
)
2299 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
2300 status
= ads_connect( ads_s
);
2301 if ( !ADS_ERR_OK(status
))
2305 /* If the attribute does not exist assume it is a Windows 2000
2306 functional domain */
2308 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2309 if (!ADS_ERR_OK(status
)) {
2310 if ( status
.err
.rc
== LDAP_NO_SUCH_ATTRIBUTE
) {
2311 status
= ADS_SUCCESS
;
2316 if ( !ads_pull_uint32(ads_s
, res
, "domainFunctionality", val
) ) {
2317 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2319 DEBUG(3,("ads_domain_func_level: %d\n", *val
));
2322 ads_msgfree(ads
, res
);
2325 /* free any temporary ads connections */
2326 if ( ads_s
!= ads
) {
2327 ads_destroy( &ads_s
);
2334 * find the domain sid for our domain
2335 * @param ads connection to ads server
2336 * @param sid Pointer to domain sid
2337 * @return status of search
2339 ADS_STATUS
ads_domain_sid(ADS_STRUCT
*ads
, DOM_SID
*sid
)
2341 const char *attrs
[] = {"objectSid", NULL
};
2345 rc
= ads_do_search_retry(ads
, ads
->config
.bind_path
, LDAP_SCOPE_BASE
, "(objectclass=*)",
2347 if (!ADS_ERR_OK(rc
)) return rc
;
2348 if (!ads_pull_sid(ads
, res
, "objectSid", sid
)) {
2349 ads_msgfree(ads
, res
);
2350 return ADS_ERROR_SYSTEM(ENOENT
);
2352 ads_msgfree(ads
, res
);
2358 * find our site name
2359 * @param ads connection to ads server
2360 * @param mem_ctx Pointer to talloc context
2361 * @param site_name Pointer to the sitename
2362 * @return status of search
2364 ADS_STATUS
ads_site_dn(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char **site_name
)
2368 const char *dn
, *service_name
;
2369 const char *attrs
[] = { "dsServiceName", NULL
};
2371 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2372 if (!ADS_ERR_OK(status
)) {
2376 service_name
= ads_pull_string(ads
, mem_ctx
, res
, "dsServiceName");
2377 if (service_name
== NULL
) {
2378 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2381 /* go up three levels */
2382 dn
= ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name
)));
2384 return ADS_ERROR(LDAP_NO_MEMORY
);
2387 *site_name
= talloc_strdup(mem_ctx
, dn
);
2388 if (*site_name
== NULL
) {
2389 return ADS_ERROR(LDAP_NO_MEMORY
);
2392 ads_msgfree(ads
, res
);
2396 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2401 * find the site dn where a machine resides
2402 * @param ads connection to ads server
2403 * @param mem_ctx Pointer to talloc context
2404 * @param computer_name name of the machine
2405 * @param site_name Pointer to the sitename
2406 * @return status of search
2408 ADS_STATUS
ads_site_dn_for_machine(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char *computer_name
, const char **site_dn
)
2412 const char *parent
, *config_context
, *filter
;
2413 const char *attrs
[] = { "configurationNamingContext", NULL
};
2416 /* shortcut a query */
2417 if (strequal(computer_name
, ads
->config
.ldap_server_name
)) {
2418 return ads_site_dn(ads
, mem_ctx
, site_dn
);
2421 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2422 if (!ADS_ERR_OK(status
)) {
2426 config_context
= ads_pull_string(ads
, mem_ctx
, res
, "configurationNamingContext");
2427 if (config_context
== NULL
) {
2428 return ADS_ERROR(LDAP_NO_MEMORY
);
2431 filter
= talloc_asprintf(mem_ctx
, "(cn=%s)", computer_name
);
2432 if (filter
== NULL
) {
2433 return ADS_ERROR(LDAP_NO_MEMORY
);
2436 status
= ads_do_search(ads
, config_context
, LDAP_SCOPE_SUBTREE
, filter
, NULL
, &res
);
2437 if (!ADS_ERR_OK(status
)) {
2441 if (ads_count_replies(ads
, res
) != 1) {
2442 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2445 dn
= ads_get_dn(ads
, res
);
2447 return ADS_ERROR(LDAP_NO_MEMORY
);
2450 /* go up three levels */
2451 parent
= ads_parent_dn(ads_parent_dn(ads_parent_dn(dn
)));
2452 if (parent
== NULL
) {
2453 ads_memfree(ads
, dn
);
2454 return ADS_ERROR(LDAP_NO_MEMORY
);
2457 *site_dn
= talloc_strdup(mem_ctx
, parent
);
2458 if (*site_dn
== NULL
) {
2459 ads_memfree(ads
, dn
);
2460 ADS_ERROR(LDAP_NO_MEMORY
);
2463 ads_memfree(ads
, dn
);
2464 ads_msgfree(ads
, res
);
2470 * get the upn suffixes for a domain
2471 * @param ads connection to ads server
2472 * @param mem_ctx Pointer to talloc context
2473 * @param suffixes Pointer to an array of suffixes
2474 * @param site_name Pointer to the number of suffixes
2475 * @return status of search
2477 ADS_STATUS
ads_upn_suffixes(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, char **suffixes
, size_t *num_suffixes
)
2481 const char *config_context
, *base
;
2482 const char *attrs
[] = { "configurationNamingContext", NULL
};
2483 const char *attrs2
[] = { "uPNSuffixes", NULL
};
2485 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2486 if (!ADS_ERR_OK(status
)) {
2490 config_context
= ads_pull_string(ads
, mem_ctx
, res
, "configurationNamingContext");
2491 if (config_context
== NULL
) {
2492 return ADS_ERROR(LDAP_NO_MEMORY
);
2495 base
= talloc_asprintf(mem_ctx
, "cn=Partitions,%s", config_context
);
2497 return ADS_ERROR(LDAP_NO_MEMORY
);
2500 status
= ads_search_dn(ads
, &res
, base
, attrs2
);
2501 if (!ADS_ERR_OK(status
)) {
2505 if (ads_count_replies(ads
, res
) != 1) {
2506 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2509 suffixes
= ads_pull_strings(ads
, mem_ctx
, &res
, "uPNSuffixes", num_suffixes
);
2510 if (suffixes
== NULL
) {
2511 ads_msgfree(ads
, res
);
2512 return ADS_ERROR(LDAP_NO_MEMORY
);
2515 ads_msgfree(ads
, res
);
2521 * pull a DOM_SID from an extended dn string
2522 * @param mem_ctx TALLOC_CTX
2523 * @param flags string type of extended_dn
2524 * @param sid pointer to a DOM_SID
2525 * @return boolean inidicating success
2527 BOOL
ads_get_sid_from_extended_dn(TALLOC_CTX
*mem_ctx
,
2529 enum ads_extended_dn_flags flags
,
2539 * ADS_EXTENDED_DN_HEX_STRING:
2540 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2542 * ADS_EXTENDED_DN_STRING (only with w2k3):
2543 <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
2546 p
= strchr(dn
, ';');
2551 if (strncmp(p
, ";<SID=", strlen(";<SID=")) != 0) {
2555 p
+= strlen(";<SID=");
2564 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p
));
2568 case ADS_EXTENDED_DN_STRING
:
2569 if (!string_to_sid(sid
, p
)) {
2573 case ADS_EXTENDED_DN_HEX_STRING
: {
2577 buf_len
= strhex_to_str(buf
, strlen(p
), p
);
2582 if (!sid_parse(buf
, buf_len
, sid
)) {
2583 DEBUG(10,("failed to parse sid\n"));
2589 DEBUG(10,("unknown extended dn format\n"));
2597 * pull an array of DOM_SIDs from a ADS result
2598 * @param ads connection to ads server
2599 * @param mem_ctx TALLOC_CTX for allocating sid array
2600 * @param msg Results of search
2601 * @param field Attribute to retrieve
2602 * @param flags string type of extended_dn
2603 * @param sids pointer to sid array to allocate
2604 * @return the count of SIDs pulled
2606 int ads_pull_sids_from_extendeddn(ADS_STRUCT
*ads
,
2607 TALLOC_CTX
*mem_ctx
,
2610 enum ads_extended_dn_flags flags
,
2617 if ((dn_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
,
2618 &dn_count
)) == NULL
) {
2622 (*sids
) = TALLOC_ZERO_ARRAY(mem_ctx
, DOM_SID
, dn_count
+ 1);
2624 TALLOC_FREE(dn_strings
);
2628 for (i
=0; i
<dn_count
; i
++) {
2630 if (!ads_get_sid_from_extended_dn(mem_ctx
, dn_strings
[i
],
2631 flags
, &(*sids
)[i
])) {
2633 TALLOC_FREE(dn_strings
);
2638 TALLOC_FREE(dn_strings
);
2643 /********************************************************************
2644 ********************************************************************/
2646 char* ads_get_dnshostname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
2648 LDAPMessage
*res
= NULL
;
2653 status
= ads_find_machine_acct(ads
, (void **)(void *)&res
, global_myname());
2654 if (!ADS_ERR_OK(status
)) {
2655 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2660 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
2661 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count
));
2665 if ( (name
= ads_pull_string(ads
, ctx
, res
, "dNSHostName")) == NULL
) {
2666 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
2670 ads_msgfree(ads
, res
);
2675 /********************************************************************
2676 ********************************************************************/
2678 char* ads_get_upn( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
2680 LDAPMessage
*res
= NULL
;
2685 status
= ads_find_machine_acct(ads
, (void **)(void *)&res
, global_myname());
2686 if (!ADS_ERR_OK(status
)) {
2687 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2692 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
2693 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count
));
2697 if ( (name
= ads_pull_string(ads
, ctx
, res
, "userPrincipalName")) == NULL
) {
2698 DEBUG(0,("ads_get_dnshostname: No userPrincipalName attribute!\n"));
2702 ads_msgfree(ads
, res
);
2707 /********************************************************************
2708 ********************************************************************/
2710 char* ads_get_samaccountname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
2712 LDAPMessage
*res
= NULL
;
2717 status
= ads_find_machine_acct(ads
, (void **)(void *)&res
, global_myname());
2718 if (!ADS_ERR_OK(status
)) {
2719 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2724 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
2725 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count
));
2729 if ( (name
= ads_pull_string(ads
, ctx
, res
, "sAMAccountName")) == NULL
) {
2730 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
2734 ads_msgfree(ads
, res
);