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
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
30 * @brief basic ldap client-side routines for ads server communications
32 * The routines contained here should do the necessary ldap calls for
35 * Important note: attribute names passed into ads_ routines must
36 * already be in UTF-8 format. We do not convert them because in almost
37 * all cases, they are just ascii (which is represented with the same
38 * codepoints in UTF-8). This may have to change at some point
42 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
44 static SIG_ATOMIC_T gotalarm
;
46 /***************************************************************
47 Signal function to tell us we timed out.
48 ****************************************************************/
50 static void gotalarm_sig(void)
55 LDAP
*ldap_open_with_timeout(const char *server
, int port
, unsigned int to
)
61 CatchSignal(SIGALRM
, SIGNAL_CAST gotalarm_sig
);
63 /* End setup timeout. */
65 ldp
= ldap_open(server
, port
);
67 /* Teardown timeout. */
68 CatchSignal(SIGALRM
, SIGNAL_CAST SIG_IGN
);
74 static int ldap_search_with_timeout(LDAP
*ld
,
75 LDAP_CONST
char *base
,
77 LDAP_CONST
char *filter
,
85 struct timeval timeout
;
88 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
89 timeout
.tv_sec
= lp_ldap_timeout();
92 /* Setup alarm timeout.... Do we need both of these ? JRA. */
94 CatchSignal(SIGALRM
, SIGNAL_CAST gotalarm_sig
);
95 alarm(lp_ldap_timeout());
96 /* End setup timeout. */
98 result
= ldap_search_ext_s(ld
, base
, scope
, filter
, attrs
,
99 attrsonly
, sctrls
, cctrls
, &timeout
,
102 /* Teardown timeout. */
103 CatchSignal(SIGALRM
, SIGNAL_CAST SIG_IGN
);
107 return LDAP_TIMELIMIT_EXCEEDED
;
113 try a connection to a given ldap server, returning True and setting the servers IP
114 in the ads struct if successful
116 BOOL
ads_try_connect(ADS_STRUCT
*ads
, const char *server
)
119 struct cldap_netlogon_reply cldap_reply
;
121 if (!server
|| !*server
) {
125 DEBUG(5,("ads_try_connect: sending CLDAP request to %s\n", server
));
127 /* this copes with inet_ntoa brokenness */
129 srv
= SMB_STRDUP(server
);
131 ZERO_STRUCT( cldap_reply
);
133 if ( !ads_cldap_netlogon( srv
, ads
->server
.realm
, &cldap_reply
) ) {
134 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv
));
138 /* Check the CLDAP reply flags */
140 if ( !(cldap_reply
.flags
& ADS_LDAP
) ) {
141 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
147 /* Fill in the ads->config values */
149 SAFE_FREE(ads
->config
.realm
);
150 SAFE_FREE(ads
->config
.bind_path
);
151 SAFE_FREE(ads
->config
.ldap_server_name
);
153 ads
->config
.ldap_server_name
= SMB_STRDUP(cldap_reply
.hostname
);
154 strupper_m(cldap_reply
.domain
);
155 ads
->config
.realm
= SMB_STRDUP(cldap_reply
.domain
);
156 ads
->config
.bind_path
= ads_build_dn(ads
->config
.realm
);
158 ads
->ldap_port
= LDAP_PORT
;
159 ads
->ldap_ip
= *interpret_addr2(srv
);
162 /* cache the successful connection */
164 saf_store( ads
->server
.workgroup
, server
);
169 /**********************************************************************
170 Try to find an AD dc using our internal name resolution routines
171 Try the realm first and then then workgroup name if netbios is not
173 **********************************************************************/
175 static BOOL
ads_find_dc(ADS_STRUCT
*ads
)
179 struct ip_service
*ip_list
;
181 BOOL got_realm
= False
;
182 BOOL use_own_domain
= False
;
184 /* if the realm and workgroup are both empty, assume they are ours */
187 c_realm
= ads
->server
.realm
;
189 if ( !c_realm
|| !*c_realm
) {
190 /* special case where no realm and no workgroup means our own */
191 if ( !ads
->server
.workgroup
|| !*ads
->server
.workgroup
) {
192 use_own_domain
= True
;
193 c_realm
= lp_realm();
197 if (c_realm
&& *c_realm
)
201 /* we need to try once with the realm name and fallback to the
202 netbios domain name if we fail (if netbios has not been disabled */
204 if ( !got_realm
&& !lp_disable_netbios() ) {
205 c_realm
= ads
->server
.workgroup
;
206 if (!c_realm
|| !*c_realm
) {
207 if ( use_own_domain
)
208 c_realm
= lp_workgroup();
211 if ( !c_realm
|| !*c_realm
) {
212 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
217 pstrcpy( realm
, c_realm
);
219 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
220 (got_realm
? "realm" : "domain"), realm
));
222 if ( !get_sorted_dc_list(realm
, &ip_list
, &count
, got_realm
) ) {
223 /* fall back to netbios if we can */
224 if ( got_realm
&& !lp_disable_netbios() ) {
232 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
233 for ( i
=0; i
<count
; i
++ ) {
236 fstrcpy( server
, inet_ntoa(ip_list
[i
].ip
) );
238 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm
, server
)) )
241 if ( ads_try_connect(ads
, server
) ) {
246 /* keep track of failures */
247 add_failed_connection_entry( realm
, server
, NT_STATUS_UNSUCCESSFUL
);
257 * Connect to the LDAP server
258 * @param ads Pointer to an existing ADS_STRUCT
259 * @return status of connection
261 ADS_STATUS
ads_connect(ADS_STRUCT
*ads
)
263 int version
= LDAP_VERSION3
;
266 ads
->last_attempt
= time(NULL
);
269 /* try with a user specified server */
271 if (ads
->server
.ldap_server
&&
272 ads_try_connect(ads
, ads
->server
.ldap_server
)) {
276 if (ads_find_dc(ads
)) {
280 return ADS_ERROR_SYSTEM(errno
?errno
:ENOENT
);
283 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads
->ldap_ip
)));
285 if (!ads
->auth
.user_name
) {
286 /* have to use the userPrincipalName value here and
287 not servicePrincipalName; found by Guenther Deschner @ Sernet.
289 Is this still correct? The comment does not match
296 asprintf(&ads
->auth
.user_name
, "%s$", global_myname() );
299 if (!ads
->auth
.realm
) {
300 ads
->auth
.realm
= SMB_STRDUP(ads
->config
.realm
);
303 if (!ads
->auth
.kdc_server
) {
304 ads
->auth
.kdc_server
= SMB_STRDUP(inet_ntoa(ads
->ldap_ip
));
308 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
309 to MIT kerberos to work (tridge) */
312 asprintf(&env
, "KRB5_KDC_ADDRESS_%s", ads
->config
.realm
);
313 setenv(env
, ads
->auth
.kdc_server
, 1);
318 /* If the caller() requested no LDAP bind, then we are done */
320 if (ads
->auth
.flags
& ADS_AUTH_NO_BIND
) {
324 /* Otherwise setup the TCP LDAP session */
326 if ( (ads
->ld
= ldap_open_with_timeout(ads
->config
.ldap_server_name
,
327 LDAP_PORT
, lp_ldap_timeout())) == NULL
)
329 return ADS_ERROR(LDAP_OPERATIONS_ERROR
);
331 ldap_set_option(ads
->ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
333 status
= ADS_ERROR(smb_ldap_start_tls(ads
->ld
, version
));
334 if (!ADS_ERR_OK(status
)) {
338 /* fill in the current time and offsets */
340 status
= ads_current_time( ads
);
341 if ( !ADS_ERR_OK(status
) ) {
345 /* Now do the bind */
347 if (ads
->auth
.flags
& ADS_AUTH_ANON_BIND
) {
348 return ADS_ERROR(ldap_simple_bind_s( ads
->ld
, NULL
, NULL
));
351 if (ads
->auth
.flags
& ADS_AUTH_SIMPLE_BIND
) {
352 return ADS_ERROR(ldap_simple_bind_s( ads
->ld
, ads
->auth
.user_name
, ads
->auth
.password
));
355 return ads_sasl_bind(ads
);
359 Duplicate a struct berval into talloc'ed memory
361 static struct berval
*dup_berval(TALLOC_CTX
*ctx
, const struct berval
*in_val
)
363 struct berval
*value
;
365 if (!in_val
) return NULL
;
367 value
= TALLOC_ZERO_P(ctx
, struct berval
);
370 if (in_val
->bv_len
== 0) return value
;
372 value
->bv_len
= in_val
->bv_len
;
373 value
->bv_val
= TALLOC_MEMDUP(ctx
, in_val
->bv_val
, in_val
->bv_len
);
378 Make a values list out of an array of (struct berval *)
380 static struct berval
**ads_dup_values(TALLOC_CTX
*ctx
,
381 const struct berval
**in_vals
)
383 struct berval
**values
;
386 if (!in_vals
) return NULL
;
387 for (i
=0; in_vals
[i
]; i
++)
389 values
= TALLOC_ZERO_ARRAY(ctx
, struct berval
*, i
+1);
390 if (!values
) return NULL
;
392 for (i
=0; in_vals
[i
]; i
++) {
393 values
[i
] = dup_berval(ctx
, in_vals
[i
]);
399 UTF8-encode a values list out of an array of (char *)
401 static char **ads_push_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
406 if (!in_vals
) return NULL
;
407 for (i
=0; in_vals
[i
]; i
++)
409 values
= TALLOC_ZERO_ARRAY(ctx
, char *, i
+1);
410 if (!values
) return NULL
;
412 for (i
=0; in_vals
[i
]; i
++) {
413 push_utf8_talloc(ctx
, &values
[i
], in_vals
[i
]);
419 Pull a (char *) array out of a UTF8-encoded values list
421 static char **ads_pull_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
426 if (!in_vals
) return NULL
;
427 for (i
=0; in_vals
[i
]; i
++)
429 values
= TALLOC_ZERO_ARRAY(ctx
, char *, i
+1);
430 if (!values
) return NULL
;
432 for (i
=0; in_vals
[i
]; i
++) {
433 pull_utf8_talloc(ctx
, &values
[i
], in_vals
[i
]);
439 * Do a search with paged results. cookie must be null on the first
440 * call, and then returned on each subsequent call. It will be null
441 * again when the entire search is complete
442 * @param ads connection to ads server
443 * @param bind_path Base dn for the search
444 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
445 * @param expr Search expression - specified in local charset
446 * @param attrs Attributes to retrieve - specified in utf8 or ascii
447 * @param res ** which will contain results - free res* with ads_msgfree()
448 * @param count Number of entries retrieved on this page
449 * @param cookie The paged results cookie to be returned on subsequent calls
450 * @return status of search
452 ADS_STATUS
ads_do_paged_search_args(ADS_STRUCT
*ads
, const char *bind_path
,
453 int scope
, const char *expr
,
454 const char **attrs
, void *args
, void **res
,
455 int *count
, void **cookie
)
458 char *utf8_expr
, *utf8_path
, **search_attrs
;
459 LDAPControl PagedResults
, NoReferrals
, ExtendedDn
, *controls
[4], **rcontrols
;
460 BerElement
*cookie_be
= NULL
;
461 struct berval
*cookie_bv
= NULL
;
462 BerElement
*extdn_be
= NULL
;
463 struct berval
*extdn_bv
= NULL
;
466 ads_control
*external_control
= (ads_control
*) args
;
470 if (!(ctx
= talloc_init("ads_do_paged_search_args")))
471 return ADS_ERROR(LDAP_NO_MEMORY
);
473 /* 0 means the conversion worked but the result was empty
474 so we only fail if it's -1. In any case, it always
475 at least nulls out the dest */
476 if ((push_utf8_talloc(ctx
, &utf8_expr
, expr
) == (size_t)-1) ||
477 (push_utf8_talloc(ctx
, &utf8_path
, bind_path
) == (size_t)-1)) {
482 if (!attrs
|| !(*attrs
))
485 /* This would be the utf8-encoded version...*/
486 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
487 if (!(str_list_copy(&search_attrs
, attrs
))) {
494 /* Paged results only available on ldap v3 or later */
495 ldap_get_option(ads
->ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
496 if (version
< LDAP_VERSION3
) {
497 rc
= LDAP_NOT_SUPPORTED
;
501 cookie_be
= ber_alloc_t(LBER_USE_DER
);
502 if (cookie
&& *cookie
) {
503 ber_printf(cookie_be
, "{iO}", (ber_int_t
) 1000, *cookie
);
504 ber_bvfree(*cookie
); /* don't need it from last time */
507 ber_printf(cookie_be
, "{io}", (ber_int_t
) 1000, "", 0);
509 ber_flatten(cookie_be
, &cookie_bv
);
510 PagedResults
.ldctl_oid
= CONST_DISCARD(char *, ADS_PAGE_CTL_OID
);
511 PagedResults
.ldctl_iscritical
= (char) 1;
512 PagedResults
.ldctl_value
.bv_len
= cookie_bv
->bv_len
;
513 PagedResults
.ldctl_value
.bv_val
= cookie_bv
->bv_val
;
515 NoReferrals
.ldctl_oid
= CONST_DISCARD(char *, ADS_NO_REFERRALS_OID
);
516 NoReferrals
.ldctl_iscritical
= (char) 0;
517 NoReferrals
.ldctl_value
.bv_len
= 0;
518 NoReferrals
.ldctl_value
.bv_val
= CONST_DISCARD(char *, "");
520 if (external_control
&& strequal(external_control
->control
, ADS_EXTENDED_DN_OID
)) {
522 ExtendedDn
.ldctl_oid
= CONST_DISCARD(char *, external_control
->control
);
523 ExtendedDn
.ldctl_iscritical
= (char) external_control
->critical
;
525 /* win2k does not accept a ldctl_value beeing passed in */
527 if (external_control
->val
!= 0) {
529 if ((extdn_be
= ber_alloc_t(LBER_USE_DER
)) == NULL
) {
534 if ((ber_printf(extdn_be
, "{i}", (ber_int_t
) external_control
->val
)) == -1) {
538 if ((ber_flatten(extdn_be
, &extdn_bv
)) == -1) {
543 ExtendedDn
.ldctl_value
.bv_len
= extdn_bv
->bv_len
;
544 ExtendedDn
.ldctl_value
.bv_val
= extdn_bv
->bv_val
;
547 ExtendedDn
.ldctl_value
.bv_len
= 0;
548 ExtendedDn
.ldctl_value
.bv_val
= NULL
;
551 controls
[0] = &NoReferrals
;
552 controls
[1] = &PagedResults
;
553 controls
[2] = &ExtendedDn
;
557 controls
[0] = &NoReferrals
;
558 controls
[1] = &PagedResults
;
562 /* we need to disable referrals as the openldap libs don't
563 handle them and paged results at the same time. Using them
564 together results in the result record containing the server
565 page control being removed from the result list (tridge/jmcd)
567 leaving this in despite the control that says don't generate
568 referrals, in case the server doesn't support it (jmcd)
570 ldap_set_option(ads
->ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
572 rc
= ldap_search_with_timeout(ads
->ld
, utf8_path
, scope
, utf8_expr
,
573 search_attrs
, 0, controls
,
575 (LDAPMessage
**)res
);
577 ber_free(cookie_be
, 1);
578 ber_bvfree(cookie_bv
);
581 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr
,
582 ldap_err2string(rc
)));
586 rc
= ldap_parse_result(ads
->ld
, *res
, NULL
, NULL
, NULL
,
587 NULL
, &rcontrols
, 0);
593 for (i
=0; rcontrols
[i
]; i
++) {
594 if (strcmp(ADS_PAGE_CTL_OID
, rcontrols
[i
]->ldctl_oid
) == 0) {
595 cookie_be
= ber_init(&rcontrols
[i
]->ldctl_value
);
596 ber_scanf(cookie_be
,"{iO}", (ber_int_t
*) count
,
598 /* the berval is the cookie, but must be freed when
600 if (cookie_bv
->bv_len
) /* still more to do */
601 *cookie
=ber_bvdup(cookie_bv
);
604 ber_bvfree(cookie_bv
);
605 ber_free(cookie_be
, 1);
609 ldap_controls_free(rcontrols
);
615 ber_free(extdn_be
, 1);
619 ber_bvfree(extdn_bv
);
622 /* if/when we decide to utf8-encode attrs, take out this next line */
623 str_list_free(&search_attrs
);
625 return ADS_ERROR(rc
);
628 ADS_STATUS
ads_do_paged_search(ADS_STRUCT
*ads
, const char *bind_path
,
629 int scope
, const char *expr
,
630 const char **attrs
, void **res
,
631 int *count
, void **cookie
)
633 return ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
, count
, cookie
);
638 * Get all results for a search. This uses ads_do_paged_search() to return
639 * all entries in a large search.
640 * @param ads connection to ads server
641 * @param bind_path Base dn for the search
642 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
643 * @param expr Search expression
644 * @param attrs Attributes to retrieve
645 * @param res ** which will contain results - free res* with ads_msgfree()
646 * @return status of search
648 ADS_STATUS
ads_do_search_all_args(ADS_STRUCT
*ads
, const char *bind_path
,
649 int scope
, const char *expr
,
650 const char **attrs
, void *args
, void **res
)
657 status
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, args
, res
,
660 if (!ADS_ERR_OK(status
))
663 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
667 LDAPMessage
*msg
, *next
;
669 status2
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
,
670 attrs
, args
, &res2
, &count
, &cookie
);
672 if (!ADS_ERR_OK(status2
)) break;
674 /* this relies on the way that ldap_add_result_entry() works internally. I hope
675 that this works on all ldap libs, but I have only tested with openldap */
676 for (msg
= ads_first_entry(ads
, res2
); msg
; msg
= next
) {
677 next
= ads_next_entry(ads
, msg
);
678 ldap_add_result_entry((LDAPMessage
**)res
, msg
);
680 /* note that we do not free res2, as the memory is now
681 part of the main returned list */
684 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
685 status
= ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL
);
691 ADS_STATUS
ads_do_search_all(ADS_STRUCT
*ads
, const char *bind_path
,
692 int scope
, const char *expr
,
693 const char **attrs
, void **res
)
695 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
);
699 * Run a function on all results for a search. Uses ads_do_paged_search() and
700 * runs the function as each page is returned, using ads_process_results()
701 * @param ads connection to ads server
702 * @param bind_path Base dn for the search
703 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
704 * @param expr Search expression - specified in local charset
705 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
706 * @param fn Function which takes attr name, values list, and data_area
707 * @param data_area Pointer which is passed to function on each call
708 * @return status of search
710 ADS_STATUS
ads_do_search_all_fn(ADS_STRUCT
*ads
, const char *bind_path
,
711 int scope
, const char *expr
, const char **attrs
,
712 BOOL(*fn
)(char *, void **, void *),
720 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
, &res
,
723 if (!ADS_ERR_OK(status
)) return status
;
725 ads_process_results(ads
, res
, fn
, data_area
);
726 ads_msgfree(ads
, res
);
729 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
,
730 &res
, &count
, &cookie
);
732 if (!ADS_ERR_OK(status
)) break;
734 ads_process_results(ads
, res
, fn
, data_area
);
735 ads_msgfree(ads
, res
);
742 * Do a search with a timeout.
743 * @param ads connection to ads server
744 * @param bind_path Base dn for the search
745 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
746 * @param expr Search expression
747 * @param attrs Attributes to retrieve
748 * @param res ** which will contain results - free res* with ads_msgfree()
749 * @return status of search
751 ADS_STATUS
ads_do_search(ADS_STRUCT
*ads
, const char *bind_path
, int scope
,
753 const char **attrs
, void **res
)
756 char *utf8_expr
, *utf8_path
, **search_attrs
= NULL
;
760 if (!(ctx
= talloc_init("ads_do_search"))) {
761 DEBUG(1,("ads_do_search: talloc_init() failed!"));
762 return ADS_ERROR(LDAP_NO_MEMORY
);
765 /* 0 means the conversion worked but the result was empty
766 so we only fail if it's negative. In any case, it always
767 at least nulls out the dest */
768 if ((push_utf8_talloc(ctx
, &utf8_expr
, expr
) == (size_t)-1) ||
769 (push_utf8_talloc(ctx
, &utf8_path
, bind_path
) == (size_t)-1)) {
770 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
775 if (!attrs
|| !(*attrs
))
778 /* This would be the utf8-encoded version...*/
779 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
780 if (!(str_list_copy(&search_attrs
, attrs
)))
782 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
788 /* see the note in ads_do_paged_search - we *must* disable referrals */
789 ldap_set_option(ads
->ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
791 rc
= ldap_search_with_timeout(ads
->ld
, utf8_path
, scope
, utf8_expr
,
792 search_attrs
, 0, NULL
, NULL
,
794 (LDAPMessage
**)res
);
796 if (rc
== LDAP_SIZELIMIT_EXCEEDED
) {
797 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
803 /* if/when we decide to utf8-encode attrs, take out this next line */
804 str_list_free(&search_attrs
);
805 return ADS_ERROR(rc
);
808 * Do a general ADS search
809 * @param ads connection to ads server
810 * @param res ** which will contain results - free res* with ads_msgfree()
811 * @param expr Search expression
812 * @param attrs Attributes to retrieve
813 * @return status of search
815 ADS_STATUS
ads_search(ADS_STRUCT
*ads
, void **res
,
819 return ads_do_search(ads
, ads
->config
.bind_path
, LDAP_SCOPE_SUBTREE
,
824 * Do a search on a specific DistinguishedName
825 * @param ads connection to ads server
826 * @param res ** which will contain results - free res* with ads_msgfree()
827 * @param dn DistinguishName to search
828 * @param attrs Attributes to retrieve
829 * @return status of search
831 ADS_STATUS
ads_search_dn(ADS_STRUCT
*ads
, void **res
,
835 return ads_do_search(ads
, dn
, LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, res
);
839 * Free up memory from a ads_search
840 * @param ads connection to ads server
841 * @param msg Search results to free
843 void ads_msgfree(ADS_STRUCT
*ads
, void *msg
)
850 * Free up memory from various ads requests
851 * @param ads connection to ads server
852 * @param mem Area to free
854 void ads_memfree(ADS_STRUCT
*ads
, void *mem
)
860 * Get a dn from search results
861 * @param ads connection to ads server
862 * @param msg Search result
865 char *ads_get_dn(ADS_STRUCT
*ads
, void *msg
)
867 char *utf8_dn
, *unix_dn
;
869 utf8_dn
= ldap_get_dn(ads
->ld
, msg
);
872 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
876 if (pull_utf8_allocate(&unix_dn
, utf8_dn
) == (size_t)-1) {
877 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
881 ldap_memfree(utf8_dn
);
886 * Get a canonical dn from search results
887 * @param ads connection to ads server
888 * @param msg Search result
891 char *ads_get_dn_canonical(ADS_STRUCT
*ads
, void *msg
)
893 #ifdef HAVE_LDAP_DN2AD_CANONICAL
894 return ldap_dn2ad_canonical(ads_get_dn(ads
, msg
));
901 * Get the parent from a dn
902 * @param dn the dn to return the parent from
903 * @return parent dn string
905 char *ads_parent_dn(const char *dn
)
907 char *p
= strchr(dn
, ',');
917 * Find a machine account given a hostname
918 * @param ads connection to ads server
919 * @param res ** which will contain results - free res* with ads_msgfree()
920 * @param host Hostname to search for
921 * @return status of search
923 ADS_STATUS
ads_find_machine_acct(ADS_STRUCT
*ads
, void **res
, const char *machine
)
927 const char *attrs
[] = {"*", "nTSecurityDescriptor", NULL
};
931 /* the easiest way to find a machine account anywhere in the tree
932 is to look for hostname$ */
933 if (asprintf(&expr
, "(samAccountName=%s$)", machine
) == -1) {
934 DEBUG(1, ("asprintf failed!\n"));
935 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
938 status
= ads_search(ads
, res
, expr
, attrs
);
944 * Initialize a list of mods to be used in a modify request
945 * @param ctx An initialized TALLOC_CTX
946 * @return allocated ADS_MODLIST
948 ADS_MODLIST
ads_init_mods(TALLOC_CTX
*ctx
)
950 #define ADS_MODLIST_ALLOC_SIZE 10
953 if ((mods
= TALLOC_ZERO_ARRAY(ctx
, LDAPMod
*, ADS_MODLIST_ALLOC_SIZE
+ 1)))
954 /* -1 is safety to make sure we don't go over the end.
955 need to reset it to NULL before doing ldap modify */
956 mods
[ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
958 return (ADS_MODLIST
)mods
;
963 add an attribute to the list, with values list already constructed
965 static ADS_STATUS
ads_modlist_add(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
966 int mod_op
, const char *name
,
970 LDAPMod
**modlist
= (LDAPMod
**) *mods
;
971 struct berval
**ber_values
= NULL
;
972 char **char_values
= NULL
;
975 mod_op
= LDAP_MOD_DELETE
;
977 if (mod_op
& LDAP_MOD_BVALUES
)
978 ber_values
= ads_dup_values(ctx
,
979 (const struct berval
**)invals
);
981 char_values
= ads_push_strvals(ctx
,
982 (const char **) invals
);
985 /* find the first empty slot */
986 for (curmod
=0; modlist
[curmod
] && modlist
[curmod
] != (LDAPMod
*) -1;
988 if (modlist
[curmod
] == (LDAPMod
*) -1) {
989 if (!(modlist
= TALLOC_REALLOC_ARRAY(ctx
, modlist
, LDAPMod
*,
990 curmod
+ADS_MODLIST_ALLOC_SIZE
+1)))
991 return ADS_ERROR(LDAP_NO_MEMORY
);
992 memset(&modlist
[curmod
], 0,
993 ADS_MODLIST_ALLOC_SIZE
*sizeof(LDAPMod
*));
994 modlist
[curmod
+ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
995 *mods
= (ADS_MODLIST
)modlist
;
998 if (!(modlist
[curmod
] = TALLOC_ZERO_P(ctx
, LDAPMod
)))
999 return ADS_ERROR(LDAP_NO_MEMORY
);
1000 modlist
[curmod
]->mod_type
= talloc_strdup(ctx
, name
);
1001 if (mod_op
& LDAP_MOD_BVALUES
) {
1002 modlist
[curmod
]->mod_bvalues
= ber_values
;
1003 } else if (mod_op
& LDAP_MOD_DELETE
) {
1004 modlist
[curmod
]->mod_values
= NULL
;
1006 modlist
[curmod
]->mod_values
= char_values
;
1009 modlist
[curmod
]->mod_op
= mod_op
;
1010 return ADS_ERROR(LDAP_SUCCESS
);
1014 * Add a single string value to a mod list
1015 * @param ctx An initialized TALLOC_CTX
1016 * @param mods An initialized ADS_MODLIST
1017 * @param name The attribute name to add
1018 * @param val The value to add - NULL means DELETE
1019 * @return ADS STATUS indicating success of add
1021 ADS_STATUS
ads_mod_str(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1022 const char *name
, const char *val
)
1024 const char *values
[2];
1030 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1031 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
, name
,
1032 (const void **) values
);
1036 * Add an array of string values to a mod list
1037 * @param ctx An initialized TALLOC_CTX
1038 * @param mods An initialized ADS_MODLIST
1039 * @param name The attribute name to add
1040 * @param vals The array of string values to add - NULL means DELETE
1041 * @return ADS STATUS indicating success of add
1043 ADS_STATUS
ads_mod_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1044 const char *name
, const char **vals
)
1047 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1048 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
,
1049 name
, (const void **) vals
);
1054 * Add a single ber-encoded value to a mod list
1055 * @param ctx An initialized TALLOC_CTX
1056 * @param mods An initialized ADS_MODLIST
1057 * @param name The attribute name to add
1058 * @param val The value to add - NULL means DELETE
1059 * @return ADS STATUS indicating success of add
1061 static ADS_STATUS
ads_mod_ber(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1062 const char *name
, const struct berval
*val
)
1064 const struct berval
*values
[2];
1069 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1070 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
|LDAP_MOD_BVALUES
,
1071 name
, (const void **) values
);
1076 * Perform an ldap modify
1077 * @param ads connection to ads server
1078 * @param mod_dn DistinguishedName to modify
1079 * @param mods list of modifications to perform
1080 * @return status of modify
1082 ADS_STATUS
ads_gen_mod(ADS_STRUCT
*ads
, const char *mod_dn
, ADS_MODLIST mods
)
1085 char *utf8_dn
= NULL
;
1087 this control is needed to modify that contains a currently
1088 non-existent attribute (but allowable for the object) to run
1090 LDAPControl PermitModify
= {
1091 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID
),
1094 LDAPControl
*controls
[2];
1096 controls
[0] = &PermitModify
;
1099 if (push_utf8_allocate(&utf8_dn
, mod_dn
) == -1) {
1100 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1103 /* find the end of the list, marked by NULL or -1 */
1104 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1105 /* make sure the end of the list is NULL */
1107 ret
= ldap_modify_ext_s(ads
->ld
, utf8_dn
,
1108 (LDAPMod
**) mods
, controls
, NULL
);
1110 return ADS_ERROR(ret
);
1114 * Perform an ldap add
1115 * @param ads connection to ads server
1116 * @param new_dn DistinguishedName to add
1117 * @param mods list of attributes and values for DN
1118 * @return status of add
1120 ADS_STATUS
ads_gen_add(ADS_STRUCT
*ads
, const char *new_dn
, ADS_MODLIST mods
)
1123 char *utf8_dn
= NULL
;
1125 if (push_utf8_allocate(&utf8_dn
, new_dn
) == -1) {
1126 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1127 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1130 /* find the end of the list, marked by NULL or -1 */
1131 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1132 /* make sure the end of the list is NULL */
1135 ret
= ldap_add_s(ads
->ld
, utf8_dn
, (LDAPMod
**)mods
);
1137 return ADS_ERROR(ret
);
1141 * Delete a DistinguishedName
1142 * @param ads connection to ads server
1143 * @param new_dn DistinguishedName to delete
1144 * @return status of delete
1146 ADS_STATUS
ads_del_dn(ADS_STRUCT
*ads
, char *del_dn
)
1149 char *utf8_dn
= NULL
;
1150 if (push_utf8_allocate(&utf8_dn
, del_dn
) == -1) {
1151 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1152 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1155 ret
= ldap_delete_s(ads
->ld
, utf8_dn
);
1156 return ADS_ERROR(ret
);
1160 * Build an org unit string
1161 * if org unit is Computers or blank then assume a container, otherwise
1162 * assume a / separated list of organisational units.
1163 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1164 * @param ads connection to ads server
1165 * @param org_unit Organizational unit
1166 * @return org unit string - caller must free
1168 char *ads_ou_string(ADS_STRUCT
*ads
, const char *org_unit
)
1172 if (!org_unit
|| !*org_unit
) {
1174 ret
= ads_default_ou_string(ads
, WELL_KNOWN_GUID_COMPUTERS
);
1176 /* samba4 might not yet respond to a wellknownobject-query */
1177 return ret
? ret
: SMB_STRDUP("cn=Computers");
1180 if (strequal(org_unit
, "Computers")) {
1181 return SMB_STRDUP("cn=Computers");
1184 /* jmcd: removed "\\" from the separation chars, because it is
1185 needed as an escape for chars like '#' which are valid in an
1187 return ads_build_path(org_unit
, "/", "ou=", 1);
1191 * Get a org unit string for a well-known GUID
1192 * @param ads connection to ads server
1193 * @param wknguid Well known GUID
1194 * @return org unit string - caller must free
1196 char *ads_default_ou_string(ADS_STRUCT
*ads
, const char *wknguid
)
1200 char *base
, *wkn_dn
, *ret
, **wkn_dn_exp
, **bind_dn_exp
;
1201 const char *attrs
[] = {"distinguishedName", NULL
};
1202 int new_ln
, wkn_ln
, bind_ln
, i
;
1204 if (wknguid
== NULL
) {
1208 if (asprintf(&base
, "<WKGUID=%s,%s>", wknguid
, ads
->config
.bind_path
) == -1) {
1209 DEBUG(1, ("asprintf failed!\n"));
1213 status
= ads_search_dn(ads
, &res
, base
, attrs
);
1214 if (!ADS_ERR_OK(status
)) {
1215 DEBUG(1,("Failed while searching for: %s\n", base
));
1221 if (ads_count_replies(ads
, res
) != 1) {
1225 /* substitute the bind-path from the well-known-guid-search result */
1226 wkn_dn
= ads_get_dn(ads
, res
);
1227 wkn_dn_exp
= ldap_explode_dn(wkn_dn
, 0);
1228 bind_dn_exp
= ldap_explode_dn(ads
->config
.bind_path
, 0);
1230 for (wkn_ln
=0; wkn_dn_exp
[wkn_ln
]; wkn_ln
++)
1232 for (bind_ln
=0; bind_dn_exp
[bind_ln
]; bind_ln
++)
1235 new_ln
= wkn_ln
- bind_ln
;
1237 ret
= wkn_dn_exp
[0];
1239 for (i
=1; i
< new_ln
; i
++) {
1241 asprintf(&s
, "%s,%s", ret
, wkn_dn_exp
[i
]);
1242 ret
= SMB_STRDUP(s
);
1246 ads_memfree(ads
, wkn_dn
);
1247 ldap_value_free(wkn_dn_exp
);
1248 ldap_value_free(bind_dn_exp
);
1254 * Adds (appends) an item to an attribute array, rather then
1255 * replacing the whole list
1256 * @param ctx An initialized TALLOC_CTX
1257 * @param mods An initialized ADS_MODLIST
1258 * @param name name of the ldap attribute to append to
1259 * @param vals an array of values to add
1260 * @return status of addition
1263 ADS_STATUS
ads_add_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1264 const char *name
, const char **vals
)
1266 return ads_modlist_add(ctx
, mods
, LDAP_MOD_ADD
, name
, (const void **) vals
);
1270 * Determines the computer account's current KVNO via an LDAP lookup
1271 * @param ads An initialized ADS_STRUCT
1272 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1273 * @return the kvno for the computer account, or -1 in case of a failure.
1276 uint32
ads_get_kvno(ADS_STRUCT
*ads
, const char *machine_name
)
1278 LDAPMessage
*res
= NULL
;
1279 uint32 kvno
= (uint32
)-1; /* -1 indicates a failure */
1281 const char *attrs
[] = {"msDS-KeyVersionNumber", NULL
};
1282 char *dn_string
= NULL
;
1283 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1285 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name
));
1286 if (asprintf(&filter
, "(samAccountName=%s$)", machine_name
) == -1) {
1289 ret
= ads_search(ads
, (void**)(void *)&res
, filter
, attrs
);
1291 if (!ADS_ERR_OK(ret
) && ads_count_replies(ads
, res
)) {
1292 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name
));
1293 ads_msgfree(ads
, res
);
1297 dn_string
= ads_get_dn(ads
, res
);
1299 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1300 ads_msgfree(ads
, res
);
1303 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string
));
1304 ads_memfree(ads
, dn_string
);
1306 /* ---------------------------------------------------------
1307 * 0 is returned as a default KVNO from this point on...
1308 * This is done because Windows 2000 does not support key
1309 * version numbers. Chances are that a failure in the next
1310 * step is simply due to Windows 2000 being used for a
1311 * domain controller. */
1314 if (!ads_pull_uint32(ads
, res
, "msDS-KeyVersionNumber", &kvno
)) {
1315 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1316 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1317 ads_msgfree(ads
, res
);
1322 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno
));
1323 ads_msgfree(ads
, res
);
1328 * This clears out all registered spn's for a given hostname
1329 * @param ads An initilaized ADS_STRUCT
1330 * @param machine_name the NetBIOS name of the computer.
1331 * @return 0 upon success, non-zero otherwise.
1334 ADS_STATUS
ads_clear_service_principal_names(ADS_STRUCT
*ads
, const char *machine_name
)
1337 LDAPMessage
*res
= NULL
;
1339 const char *servicePrincipalName
[1] = {NULL
};
1340 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1341 char *dn_string
= NULL
;
1343 ret
= ads_find_machine_acct(ads
, (void **)(void *)&res
, machine_name
);
1344 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1345 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name
));
1346 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name
));
1347 ads_msgfree(ads
, res
);
1348 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1351 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name
));
1352 ctx
= talloc_init("ads_clear_service_principal_names");
1354 ads_msgfree(ads
, res
);
1355 return ADS_ERROR(LDAP_NO_MEMORY
);
1358 if (!(mods
= ads_init_mods(ctx
))) {
1359 talloc_destroy(ctx
);
1360 ads_msgfree(ads
, res
);
1361 return ADS_ERROR(LDAP_NO_MEMORY
);
1363 ret
= ads_mod_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1364 if (!ADS_ERR_OK(ret
)) {
1365 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1366 ads_msgfree(ads
, res
);
1367 talloc_destroy(ctx
);
1370 dn_string
= ads_get_dn(ads
, res
);
1372 talloc_destroy(ctx
);
1373 ads_msgfree(ads
, res
);
1374 return ADS_ERROR(LDAP_NO_MEMORY
);
1376 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1377 ads_memfree(ads
,dn_string
);
1378 if (!ADS_ERR_OK(ret
)) {
1379 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1381 ads_msgfree(ads
, res
);
1382 talloc_destroy(ctx
);
1386 ads_msgfree(ads
, res
);
1387 talloc_destroy(ctx
);
1392 * This adds a service principal name to an existing computer account
1393 * (found by hostname) in AD.
1394 * @param ads An initialized ADS_STRUCT
1395 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1396 * @param spn A string of the service principal to add, i.e. 'host'
1397 * @return 0 upon sucess, or non-zero if a failure occurs
1400 ADS_STATUS
ads_add_service_principal_name(ADS_STRUCT
*ads
, const char *machine_name
, const char *spn
)
1404 LDAPMessage
*res
= NULL
;
1405 char *host_spn
, *psp1
, *psp2
, *psp3
;
1408 char *dn_string
= NULL
;
1409 const char *servicePrincipalName
[4] = {NULL
, NULL
, NULL
, NULL
};
1411 ret
= ads_find_machine_acct(ads
, (void **)(void *)&res
, machine_name
);
1412 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1413 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1415 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1416 spn
, machine_name
, ads
->config
.realm
));
1417 ads_msgfree(ads
, res
);
1418 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1421 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name
));
1422 if (!(ctx
= talloc_init("ads_add_service_principal_name"))) {
1423 ads_msgfree(ads
, res
);
1424 return ADS_ERROR(LDAP_NO_MEMORY
);
1427 name_to_fqdn(my_fqdn
, machine_name
);
1428 strlower_m(my_fqdn
);
1430 if (!(host_spn
= talloc_asprintf(ctx
, "HOST/%s", my_fqdn
))) {
1431 talloc_destroy(ctx
);
1432 ads_msgfree(ads
, res
);
1433 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1436 /* Add the extra principal */
1437 psp1
= talloc_asprintf(ctx
, "%s/%s", spn
, machine_name
);
1439 strlower_m(&psp1
[strlen(spn
)]);
1440 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp1
, machine_name
));
1441 servicePrincipalName
[0] = psp1
;
1442 psp2
= talloc_asprintf(ctx
, "%s/%s.%s", spn
, machine_name
, ads
->config
.realm
);
1444 strlower_m(&psp2
[strlen(spn
)]);
1445 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp2
, machine_name
));
1446 servicePrincipalName
[1] = psp2
;
1448 /* Add another principal in case the realm != the DNS domain, so that
1449 * the KDC doesn't send "server principal unknown" errors to clients
1450 * which use the DNS name in determining service principal names. */
1451 psp3
= talloc_asprintf(ctx
, "%s/%s", spn
, my_fqdn
);
1453 strlower_m(&psp3
[strlen(spn
)]);
1454 if (strcmp(psp2
, psp3
) != 0) {
1455 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp3
, machine_name
));
1456 servicePrincipalName
[2] = psp3
;
1459 if (!(mods
= ads_init_mods(ctx
))) {
1460 talloc_destroy(ctx
);
1461 ads_msgfree(ads
, res
);
1462 return 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"));
1467 talloc_destroy(ctx
);
1468 ads_msgfree(ads
, res
);
1471 dn_string
= ads_get_dn(ads
, res
);
1473 talloc_destroy(ctx
);
1474 ads_msgfree(ads
, res
);
1475 return ADS_ERROR(LDAP_NO_MEMORY
);
1477 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1478 ads_memfree(ads
,dn_string
);
1479 if (!ADS_ERR_OK(ret
)) {
1480 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1481 talloc_destroy(ctx
);
1482 ads_msgfree(ads
, res
);
1486 talloc_destroy(ctx
);
1487 ads_msgfree(ads
, res
);
1492 * adds a machine account to the ADS server
1493 * @param ads An intialized ADS_STRUCT
1494 * @param machine_name - the NetBIOS machine name of this account.
1495 * @param account_type A number indicating the type of account to create
1496 * @param org_unit The LDAP path in which to place this account
1497 * @return 0 upon success, or non-zero otherwise
1500 ADS_STATUS
ads_create_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
1501 const char *org_unit
)
1504 char *samAccountName
, *controlstr
;
1508 const char *objectClass
[] = {"top", "person", "organizationalPerson",
1509 "user", "computer", NULL
};
1510 LDAPMessage
*res
= NULL
;
1511 uint32 acct_control
= ( UF_WORKSTATION_TRUST_ACCOUNT
|\
1512 UF_DONT_EXPIRE_PASSWD
|\
1513 UF_ACCOUNTDISABLE
);
1515 if (!(ctx
= talloc_init("ads_add_machine_acct")))
1516 return ADS_ERROR(LDAP_NO_MEMORY
);
1518 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1520 new_dn
= talloc_asprintf(ctx
, "cn=%s,%s", machine_name
, org_unit
);
1521 samAccountName
= talloc_asprintf(ctx
, "%s$", machine_name
);
1523 if ( !new_dn
|| !samAccountName
) {
1527 #ifndef ENCTYPE_ARCFOUR_HMAC
1528 acct_control
|= UF_USE_DES_KEY_ONLY
;
1531 if (!(controlstr
= talloc_asprintf(ctx
, "%u", acct_control
))) {
1535 if (!(mods
= ads_init_mods(ctx
))) {
1539 ads_mod_str(ctx
, &mods
, "cn", machine_name
);
1540 ads_mod_str(ctx
, &mods
, "sAMAccountName", samAccountName
);
1541 ads_mod_strlist(ctx
, &mods
, "objectClass", objectClass
);
1542 ads_mod_str(ctx
, &mods
, "userAccountControl", controlstr
);
1544 ret
= ads_gen_add(ads
, new_dn
, mods
);
1547 ads_msgfree(ads
, res
);
1548 talloc_destroy(ctx
);
1554 dump a binary result from ldap
1556 static void dump_binary(const char *field
, struct berval
**values
)
1559 for (i
=0; values
[i
]; i
++) {
1560 printf("%s: ", field
);
1561 for (j
=0; j
<values
[i
]->bv_len
; j
++) {
1562 printf("%02X", (unsigned char)values
[i
]->bv_val
[j
]);
1568 static void dump_guid(const char *field
, struct berval
**values
)
1572 for (i
=0; values
[i
]; i
++) {
1573 memcpy(guid
.info
, values
[i
]->bv_val
, sizeof(guid
.info
));
1574 printf("%s: %s\n", field
,
1575 smb_uuid_string_static(smb_uuid_unpack_static(guid
)));
1580 dump a sid result from ldap
1582 static void dump_sid(const char *field
, struct berval
**values
)
1585 for (i
=0; values
[i
]; i
++) {
1587 sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &sid
);
1588 printf("%s: %s\n", field
, sid_string_static(&sid
));
1593 dump ntSecurityDescriptor
1595 static void dump_sd(const char *filed
, struct berval
**values
)
1600 TALLOC_CTX
*ctx
= 0;
1602 if (!(ctx
= talloc_init("sec_io_desc")))
1606 prs_init(&ps
, values
[0]->bv_len
, ctx
, UNMARSHALL
);
1607 prs_copy_data_in(&ps
, values
[0]->bv_val
, values
[0]->bv_len
);
1608 prs_set_offset(&ps
,0);
1611 if (!sec_io_desc("sd", &psd
, &ps
, 1)) {
1613 talloc_destroy(ctx
);
1616 if (psd
) ads_disp_sd(psd
);
1619 talloc_destroy(ctx
);
1623 dump a string result from ldap
1625 static void dump_string(const char *field
, char **values
)
1628 for (i
=0; values
[i
]; i
++) {
1629 printf("%s: %s\n", field
, values
[i
]);
1634 dump a field from LDAP on stdout
1638 static BOOL
ads_dump_field(char *field
, void **values
, void *data_area
)
1643 void (*handler
)(const char *, struct berval
**);
1645 {"objectGUID", False
, dump_guid
},
1646 {"netbootGUID", False
, dump_guid
},
1647 {"nTSecurityDescriptor", False
, dump_sd
},
1648 {"dnsRecord", False
, dump_binary
},
1649 {"objectSid", False
, dump_sid
},
1650 {"tokenGroups", False
, dump_sid
},
1651 {"tokenGroupsNoGCAcceptable", False
, dump_sid
},
1652 {"tokengroupsGlobalandUniversal", False
, dump_sid
},
1657 if (!field
) { /* must be end of an entry */
1662 for (i
=0; handlers
[i
].name
; i
++) {
1663 if (StrCaseCmp(handlers
[i
].name
, field
) == 0) {
1664 if (!values
) /* first time, indicate string or not */
1665 return handlers
[i
].string
;
1666 handlers
[i
].handler(field
, (struct berval
**) values
);
1670 if (!handlers
[i
].name
) {
1671 if (!values
) /* first time, indicate string conversion */
1673 dump_string(field
, (char **)values
);
1679 * Dump a result from LDAP on stdout
1680 * used for debugging
1681 * @param ads connection to ads server
1682 * @param res Results to dump
1685 void ads_dump(ADS_STRUCT
*ads
, void *res
)
1687 ads_process_results(ads
, res
, ads_dump_field
, NULL
);
1691 * Walk through results, calling a function for each entry found.
1692 * The function receives a field name, a berval * array of values,
1693 * and a data area passed through from the start. The function is
1694 * called once with null for field and values at the end of each
1696 * @param ads connection to ads server
1697 * @param res Results to process
1698 * @param fn Function for processing each result
1699 * @param data_area user-defined area to pass to function
1701 void ads_process_results(ADS_STRUCT
*ads
, void *res
,
1702 BOOL(*fn
)(char *, void **, void *),
1708 if (!(ctx
= talloc_init("ads_process_results")))
1711 for (msg
= ads_first_entry(ads
, res
); msg
;
1712 msg
= ads_next_entry(ads
, msg
)) {
1716 for (utf8_field
=ldap_first_attribute(ads
->ld
,
1717 (LDAPMessage
*)msg
,&b
);
1719 utf8_field
=ldap_next_attribute(ads
->ld
,
1720 (LDAPMessage
*)msg
,b
)) {
1721 struct berval
**ber_vals
;
1722 char **str_vals
, **utf8_vals
;
1726 pull_utf8_talloc(ctx
, &field
, utf8_field
);
1727 string
= fn(field
, NULL
, data_area
);
1730 utf8_vals
= ldap_get_values(ads
->ld
,
1731 (LDAPMessage
*)msg
, field
);
1732 str_vals
= ads_pull_strvals(ctx
,
1733 (const char **) utf8_vals
);
1734 fn(field
, (void **) str_vals
, data_area
);
1735 ldap_value_free(utf8_vals
);
1737 ber_vals
= ldap_get_values_len(ads
->ld
,
1738 (LDAPMessage
*)msg
, field
);
1739 fn(field
, (void **) ber_vals
, data_area
);
1741 ldap_value_free_len(ber_vals
);
1743 ldap_memfree(utf8_field
);
1746 talloc_free_children(ctx
);
1747 fn(NULL
, NULL
, data_area
); /* completed an entry */
1750 talloc_destroy(ctx
);
1754 * count how many replies are in a LDAPMessage
1755 * @param ads connection to ads server
1756 * @param res Results to count
1757 * @return number of replies
1759 int ads_count_replies(ADS_STRUCT
*ads
, void *res
)
1761 return ldap_count_entries(ads
->ld
, (LDAPMessage
*)res
);
1765 * pull the first entry from a ADS result
1766 * @param ads connection to ads server
1767 * @param res Results of search
1768 * @return first entry from result
1770 void *ads_first_entry(ADS_STRUCT
*ads
, void *res
)
1772 return (void *)ldap_first_entry(ads
->ld
, (LDAPMessage
*)res
);
1776 * pull the next entry from a ADS result
1777 * @param ads connection to ads server
1778 * @param res Results of search
1779 * @return next entry from result
1781 void *ads_next_entry(ADS_STRUCT
*ads
, void *res
)
1783 return (void *)ldap_next_entry(ads
->ld
, (LDAPMessage
*)res
);
1787 * pull a single string from a ADS result
1788 * @param ads connection to ads server
1789 * @param mem_ctx TALLOC_CTX to use for allocating result string
1790 * @param msg Results of search
1791 * @param field Attribute to retrieve
1792 * @return Result string in talloc context
1794 char *ads_pull_string(ADS_STRUCT
*ads
,
1795 TALLOC_CTX
*mem_ctx
, void *msg
, const char *field
)
1802 values
= ldap_get_values(ads
->ld
, msg
, field
);
1807 rc
= pull_utf8_talloc(mem_ctx
, &ux_string
,
1809 if (rc
!= (size_t)-1)
1813 ldap_value_free(values
);
1818 * pull an array of strings from a ADS result
1819 * @param ads connection to ads server
1820 * @param mem_ctx TALLOC_CTX to use for allocating result string
1821 * @param msg Results of search
1822 * @param field Attribute to retrieve
1823 * @return Result strings in talloc context
1825 char **ads_pull_strings(ADS_STRUCT
*ads
,
1826 TALLOC_CTX
*mem_ctx
, void *msg
, const char *field
,
1833 values
= ldap_get_values(ads
->ld
, msg
, field
);
1837 *num_values
= ldap_count_values(values
);
1839 ret
= TALLOC_ARRAY(mem_ctx
, char *, *num_values
+ 1);
1841 ldap_value_free(values
);
1845 for (i
=0;i
<*num_values
;i
++) {
1846 if (pull_utf8_talloc(mem_ctx
, &ret
[i
], values
[i
]) == -1) {
1847 ldap_value_free(values
);
1853 ldap_value_free(values
);
1858 * pull an array of strings from a ADS result
1859 * (handle large multivalue attributes with range retrieval)
1860 * @param ads connection to ads server
1861 * @param mem_ctx TALLOC_CTX to use for allocating result string
1862 * @param msg Results of search
1863 * @param field Attribute to retrieve
1864 * @param current_strings strings returned by a previous call to this function
1865 * @param next_attribute The next query should ask for this attribute
1866 * @param num_values How many values did we get this time?
1867 * @param more_values Are there more values to get?
1868 * @return Result strings in talloc context
1870 char **ads_pull_strings_range(ADS_STRUCT
*ads
,
1871 TALLOC_CTX
*mem_ctx
,
1872 void *msg
, const char *field
,
1873 char **current_strings
,
1874 const char **next_attribute
,
1875 size_t *num_strings
,
1879 char *expected_range_attrib
, *range_attr
;
1880 BerElement
*ptr
= NULL
;
1883 size_t num_new_strings
;
1884 unsigned long int range_start
;
1885 unsigned long int range_end
;
1887 /* we might have been given the whole lot anyway */
1888 if ((strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
, num_strings
))) {
1889 *more_strings
= False
;
1893 expected_range_attrib
= talloc_asprintf(mem_ctx
, "%s;Range=", field
);
1895 /* look for Range result */
1896 for (attr
= ldap_first_attribute(ads
->ld
, (LDAPMessage
*)msg
, &ptr
);
1898 attr
= ldap_next_attribute(ads
->ld
, (LDAPMessage
*)msg
, ptr
)) {
1899 /* we ignore the fact that this is utf8, as all attributes are ascii... */
1900 if (strnequal(attr
, expected_range_attrib
, strlen(expected_range_attrib
))) {
1908 /* nothing here - this field is just empty */
1909 *more_strings
= False
;
1913 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-%lu",
1914 &range_start
, &range_end
) == 2) {
1915 *more_strings
= True
;
1917 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-*",
1918 &range_start
) == 1) {
1919 *more_strings
= False
;
1921 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
1923 ldap_memfree(range_attr
);
1924 *more_strings
= False
;
1929 if ((*num_strings
) != range_start
) {
1930 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
1931 " - aborting range retreival\n",
1932 range_attr
, (unsigned int)(*num_strings
) + 1, range_start
));
1933 ldap_memfree(range_attr
);
1934 *more_strings
= False
;
1938 new_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, range_attr
, &num_new_strings
);
1940 if (*more_strings
&& ((*num_strings
+ num_new_strings
) != (range_end
+ 1))) {
1941 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
1942 "strings in this bunch, but we only got %lu - aborting range retreival\n",
1943 range_attr
, (unsigned long int)range_end
- range_start
+ 1,
1944 (unsigned long int)num_new_strings
));
1945 ldap_memfree(range_attr
);
1946 *more_strings
= False
;
1950 strings
= TALLOC_REALLOC_ARRAY(mem_ctx
, current_strings
, char *,
1951 *num_strings
+ num_new_strings
);
1953 if (strings
== NULL
) {
1954 ldap_memfree(range_attr
);
1955 *more_strings
= False
;
1959 if (new_strings
&& num_new_strings
) {
1960 memcpy(&strings
[*num_strings
], new_strings
,
1961 sizeof(*new_strings
) * num_new_strings
);
1964 (*num_strings
) += num_new_strings
;
1966 if (*more_strings
) {
1967 *next_attribute
= talloc_asprintf(mem_ctx
,
1972 if (!*next_attribute
) {
1973 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
1974 ldap_memfree(range_attr
);
1975 *more_strings
= False
;
1980 ldap_memfree(range_attr
);
1986 * pull a single uint32 from a ADS result
1987 * @param ads connection to ads server
1988 * @param msg Results of search
1989 * @param field Attribute to retrieve
1990 * @param v Pointer to int to store result
1991 * @return boolean inidicating success
1993 BOOL
ads_pull_uint32(ADS_STRUCT
*ads
,
1994 void *msg
, const char *field
, uint32
*v
)
1998 values
= ldap_get_values(ads
->ld
, msg
, field
);
2002 ldap_value_free(values
);
2006 *v
= atoi(values
[0]);
2007 ldap_value_free(values
);
2012 * pull a single objectGUID from an ADS result
2013 * @param ads connection to ADS server
2014 * @param msg results of search
2015 * @param guid 37-byte area to receive text guid
2016 * @return boolean indicating success
2018 BOOL
ads_pull_guid(ADS_STRUCT
*ads
,
2019 void *msg
, struct uuid
*guid
)
2022 UUID_FLAT flat_guid
;
2024 values
= ldap_get_values(ads
->ld
, msg
, "objectGUID");
2029 memcpy(&flat_guid
.info
, values
[0], sizeof(UUID_FLAT
));
2030 smb_uuid_unpack(flat_guid
, guid
);
2031 ldap_value_free(values
);
2034 ldap_value_free(values
);
2041 * pull a single DOM_SID from a ADS result
2042 * @param ads connection to ads server
2043 * @param msg Results of search
2044 * @param field Attribute to retrieve
2045 * @param sid Pointer to sid to store result
2046 * @return boolean inidicating success
2048 BOOL
ads_pull_sid(ADS_STRUCT
*ads
,
2049 void *msg
, const char *field
, DOM_SID
*sid
)
2051 struct berval
**values
;
2054 values
= ldap_get_values_len(ads
->ld
, msg
, field
);
2060 ret
= sid_parse(values
[0]->bv_val
, values
[0]->bv_len
, sid
);
2062 ldap_value_free_len(values
);
2067 * pull an array of DOM_SIDs from a ADS result
2068 * @param ads connection to ads server
2069 * @param mem_ctx TALLOC_CTX for allocating sid array
2070 * @param msg Results of search
2071 * @param field Attribute to retrieve
2072 * @param sids pointer to sid array to allocate
2073 * @return the count of SIDs pulled
2075 int ads_pull_sids(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2076 void *msg
, const char *field
, DOM_SID
**sids
)
2078 struct berval
**values
;
2082 values
= ldap_get_values_len(ads
->ld
, msg
, field
);
2087 for (i
=0; values
[i
]; i
++)
2090 (*sids
) = TALLOC_ARRAY(mem_ctx
, DOM_SID
, i
);
2092 ldap_value_free_len(values
);
2097 for (i
=0; values
[i
]; i
++) {
2098 ret
= sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &(*sids
)[count
]);
2101 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid
, &(*sids
)[count
])));
2106 ldap_value_free_len(values
);
2111 * pull a SEC_DESC from a ADS result
2112 * @param ads connection to ads server
2113 * @param mem_ctx TALLOC_CTX for allocating sid array
2114 * @param msg Results of search
2115 * @param field Attribute to retrieve
2116 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2117 * @return boolean inidicating success
2119 BOOL
ads_pull_sd(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2120 void *msg
, const char *field
, SEC_DESC
**sd
)
2122 struct berval
**values
;
2126 values
= ldap_get_values_len(ads
->ld
, msg
, field
);
2128 if (!values
) return False
;
2131 prs_init(&ps
, values
[0]->bv_len
, mem_ctx
, UNMARSHALL
);
2132 prs_copy_data_in(&ps
, values
[0]->bv_val
, values
[0]->bv_len
);
2133 prs_set_offset(&ps
,0);
2135 ret
= sec_io_desc("sd", sd
, &ps
, 1);
2138 ldap_value_free_len(values
);
2143 * in order to support usernames longer than 21 characters we need to
2144 * use both the sAMAccountName and the userPrincipalName attributes
2145 * It seems that not all users have the userPrincipalName attribute set
2147 * @param ads connection to ads server
2148 * @param mem_ctx TALLOC_CTX for allocating sid array
2149 * @param msg Results of search
2150 * @return the username
2152 char *ads_pull_username(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, void *msg
)
2157 /* lookup_name() only works on the sAMAccountName to
2158 returning the username portion of userPrincipalName
2159 breaks winbindd_getpwnam() */
2161 ret
= ads_pull_string(ads
, mem_ctx
, msg
, "userPrincipalName");
2162 if (ret
&& (p
= strchr_m(ret
, '@'))) {
2167 return ads_pull_string(ads
, mem_ctx
, msg
, "sAMAccountName");
2172 * find the update serial number - this is the core of the ldap cache
2173 * @param ads connection to ads server
2174 * @param ads connection to ADS server
2175 * @param usn Pointer to retrieved update serial number
2176 * @return status of search
2178 ADS_STATUS
ads_USN(ADS_STRUCT
*ads
, uint32
*usn
)
2180 const char *attrs
[] = {"highestCommittedUSN", NULL
};
2184 status
= ads_do_search_retry(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2185 if (!ADS_ERR_OK(status
))
2188 if (ads_count_replies(ads
, res
) != 1) {
2189 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2192 ads_pull_uint32(ads
, res
, "highestCommittedUSN", usn
);
2193 ads_msgfree(ads
, res
);
2197 /* parse a ADS timestring - typical string is
2198 '20020917091222.0Z0' which means 09:12.22 17th September
2200 static time_t ads_parse_time(const char *str
)
2206 if (sscanf(str
, "%4d%2d%2d%2d%2d%2d",
2207 &tm
.tm_year
, &tm
.tm_mon
, &tm
.tm_mday
,
2208 &tm
.tm_hour
, &tm
.tm_min
, &tm
.tm_sec
) != 6) {
2218 * Find the servers name and realm - this can be done before authentication
2219 * The ldapServiceName field on w2k looks like this:
2220 * vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
2221 * @param ads connection to ads server
2222 * @return status of search
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_server_info"))) {
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
)) {
2253 talloc_destroy(ctx
);
2257 timestr
= ads_pull_string(ads_s
, ctx
, res
, "currentTime");
2259 ads_msgfree(ads
, res
);
2260 talloc_destroy(ctx
);
2261 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2265 /* but save the time and offset in the original ADS_STRUCT */
2267 ads
->config
.current_time
= ads_parse_time(timestr
);
2269 if (ads
->config
.current_time
!= 0) {
2270 ads
->auth
.time_offset
= ads
->config
.current_time
- time(NULL
);
2271 DEBUG(4,("time offset is %d seconds\n", ads
->auth
.time_offset
));
2274 ads_msgfree(ads
, res
);
2276 status
= ADS_SUCCESS
;
2279 /* free any temporary ads connections */
2280 if ( ads_s
!= ads
) {
2281 ads_destroy( &ads_s
);
2283 talloc_destroy(ctx
);
2289 * find the domain sid for our domain
2290 * @param ads connection to ads server
2291 * @param sid Pointer to domain sid
2292 * @return status of search
2294 ADS_STATUS
ads_domain_sid(ADS_STRUCT
*ads
, DOM_SID
*sid
)
2296 const char *attrs
[] = {"objectSid", NULL
};
2300 rc
= ads_do_search_retry(ads
, ads
->config
.bind_path
, LDAP_SCOPE_BASE
, "(objectclass=*)",
2302 if (!ADS_ERR_OK(rc
)) return rc
;
2303 if (!ads_pull_sid(ads
, res
, "objectSid", sid
)) {
2304 ads_msgfree(ads
, res
);
2305 return ADS_ERROR_SYSTEM(ENOENT
);
2307 ads_msgfree(ads
, res
);
2313 * find our site name
2314 * @param ads connection to ads server
2315 * @param mem_ctx Pointer to talloc context
2316 * @param site_name Pointer to the sitename
2317 * @return status of search
2319 ADS_STATUS
ads_site_dn(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char **site_name
)
2323 const char *dn
, *service_name
;
2324 const char *attrs
[] = { "dsServiceName", NULL
};
2326 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2327 if (!ADS_ERR_OK(status
)) {
2331 service_name
= ads_pull_string(ads
, mem_ctx
, res
, "dsServiceName");
2332 if (service_name
== NULL
) {
2333 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2336 /* go up three levels */
2337 dn
= ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name
)));
2339 return ADS_ERROR(LDAP_NO_MEMORY
);
2342 *site_name
= talloc_strdup(mem_ctx
, dn
);
2343 if (*site_name
== NULL
) {
2344 return ADS_ERROR(LDAP_NO_MEMORY
);
2347 ads_msgfree(ads
, res
);
2351 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2356 * find the site dn where a machine resides
2357 * @param ads connection to ads server
2358 * @param mem_ctx Pointer to talloc context
2359 * @param computer_name name of the machine
2360 * @param site_name Pointer to the sitename
2361 * @return status of search
2363 ADS_STATUS
ads_site_dn_for_machine(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char *computer_name
, const char **site_dn
)
2367 const char *parent
, *config_context
, *filter
;
2368 const char *attrs
[] = { "configurationNamingContext", NULL
};
2371 /* shortcut a query */
2372 if (strequal(computer_name
, ads
->config
.ldap_server_name
)) {
2373 return ads_site_dn(ads
, mem_ctx
, site_dn
);
2376 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2377 if (!ADS_ERR_OK(status
)) {
2381 config_context
= ads_pull_string(ads
, mem_ctx
, res
, "configurationNamingContext");
2382 if (config_context
== NULL
) {
2383 return ADS_ERROR(LDAP_NO_MEMORY
);
2386 filter
= talloc_asprintf(mem_ctx
, "(cn=%s)", computer_name
);
2387 if (filter
== NULL
) {
2388 return ADS_ERROR(LDAP_NO_MEMORY
);
2391 status
= ads_do_search(ads
, config_context
, LDAP_SCOPE_SUBTREE
, filter
, NULL
, &res
);
2392 if (!ADS_ERR_OK(status
)) {
2396 if (ads_count_replies(ads
, res
) != 1) {
2397 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2400 dn
= ads_get_dn(ads
, res
);
2402 return ADS_ERROR(LDAP_NO_MEMORY
);
2405 /* go up three levels */
2406 parent
= ads_parent_dn(ads_parent_dn(ads_parent_dn(dn
)));
2407 if (parent
== NULL
) {
2408 ads_memfree(ads
, dn
);
2409 return ADS_ERROR(LDAP_NO_MEMORY
);
2412 *site_dn
= talloc_strdup(mem_ctx
, parent
);
2413 if (*site_dn
== NULL
) {
2414 ads_memfree(ads
, dn
);
2415 ADS_ERROR(LDAP_NO_MEMORY
);
2418 ads_memfree(ads
, dn
);
2419 ads_msgfree(ads
, res
);
2425 * get the upn suffixes for a domain
2426 * @param ads connection to ads server
2427 * @param mem_ctx Pointer to talloc context
2428 * @param suffixes Pointer to an array of suffixes
2429 * @param site_name Pointer to the number of suffixes
2430 * @return status of search
2432 ADS_STATUS
ads_upn_suffixes(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, char **suffixes
, size_t *num_suffixes
)
2436 const char *config_context
, *base
;
2437 const char *attrs
[] = { "configurationNamingContext", NULL
};
2438 const char *attrs2
[] = { "uPNSuffixes", NULL
};
2440 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2441 if (!ADS_ERR_OK(status
)) {
2445 config_context
= ads_pull_string(ads
, mem_ctx
, res
, "configurationNamingContext");
2446 if (config_context
== NULL
) {
2447 return ADS_ERROR(LDAP_NO_MEMORY
);
2450 base
= talloc_asprintf(mem_ctx
, "cn=Partitions,%s", config_context
);
2452 return ADS_ERROR(LDAP_NO_MEMORY
);
2455 status
= ads_search_dn(ads
, &res
, base
, attrs2
);
2456 if (!ADS_ERR_OK(status
)) {
2460 if (ads_count_replies(ads
, res
) != 1) {
2461 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2464 suffixes
= ads_pull_strings(ads
, mem_ctx
, &res
, "uPNSuffixes", num_suffixes
);
2465 if (suffixes
== NULL
) {
2466 ads_msgfree(ads
, res
);
2467 return ADS_ERROR(LDAP_NO_MEMORY
);
2470 ads_msgfree(ads
, res
);
2476 * pull a DOM_SID from an extended dn string
2477 * @param mem_ctx TALLOC_CTX
2478 * @param flags string type of extended_dn
2479 * @param sid pointer to a DOM_SID
2480 * @return boolean inidicating success
2482 BOOL
ads_get_sid_from_extended_dn(TALLOC_CTX
*mem_ctx
,
2484 enum ads_extended_dn_flags flags
,
2494 * ADS_EXTENDED_DN_HEX_STRING:
2495 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2497 * ADS_EXTENDED_DN_STRING (only with w2k3):
2498 <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
2501 p
= strchr(dn
, ';');
2506 if (strncmp(p
, ";<SID=", strlen(";<SID=")) != 0) {
2510 p
+= strlen(";<SID=");
2519 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p
));
2523 case ADS_EXTENDED_DN_STRING
:
2524 if (!string_to_sid(sid
, p
)) {
2528 case ADS_EXTENDED_DN_HEX_STRING
: {
2532 buf_len
= strhex_to_str(buf
, strlen(p
), p
);
2537 if (!sid_parse(buf
, buf_len
, sid
)) {
2538 DEBUG(10,("failed to parse sid\n"));
2544 DEBUG(10,("unknown extended dn format\n"));
2552 * pull an array of DOM_SIDs from a ADS result
2553 * @param ads connection to ads server
2554 * @param mem_ctx TALLOC_CTX for allocating sid array
2555 * @param msg Results of search
2556 * @param field Attribute to retrieve
2557 * @param flags string type of extended_dn
2558 * @param sids pointer to sid array to allocate
2559 * @return the count of SIDs pulled
2561 int ads_pull_sids_from_extendeddn(ADS_STRUCT
*ads
,
2562 TALLOC_CTX
*mem_ctx
,
2565 enum ads_extended_dn_flags flags
,
2572 if ((dn_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
,
2573 &dn_count
)) == NULL
) {
2577 (*sids
) = TALLOC_ZERO_ARRAY(mem_ctx
, DOM_SID
, dn_count
+ 1);
2579 TALLOC_FREE(dn_strings
);
2583 for (i
=0; i
<dn_count
; i
++) {
2585 if (!ads_get_sid_from_extended_dn(mem_ctx
, dn_strings
[i
],
2586 flags
, &(*sids
)[i
])) {
2588 TALLOC_FREE(dn_strings
);
2593 TALLOC_FREE(dn_strings
);