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
292 asprintf(&ads
->auth
.user_name
, "host/%s", global_myname() );
295 if (!ads
->auth
.realm
) {
296 ads
->auth
.realm
= SMB_STRDUP(ads
->config
.realm
);
299 if (!ads
->auth
.kdc_server
) {
300 ads
->auth
.kdc_server
= SMB_STRDUP(inet_ntoa(ads
->ldap_ip
));
304 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
305 to MIT kerberos to work (tridge) */
308 asprintf(&env
, "KRB5_KDC_ADDRESS_%s", ads
->config
.realm
);
309 setenv(env
, ads
->auth
.kdc_server
, 1);
314 /* If the caller() requested no LDAP bind, then we are done */
316 if (ads
->auth
.flags
& ADS_AUTH_NO_BIND
) {
320 /* Otherwise setup the TCP LDAP session */
322 if ( (ads
->ld
= ldap_open_with_timeout(ads
->config
.ldap_server_name
,
323 LDAP_PORT
, lp_ldap_timeout())) == NULL
)
325 return ADS_ERROR(LDAP_OPERATIONS_ERROR
);
327 ldap_set_option(ads
->ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
329 status
= ADS_ERROR(smb_ldap_start_tls(ads
->ld
, version
));
330 if (!ADS_ERR_OK(status
)) {
334 /* fill in the current time and offsets */
336 status
= ads_current_time( ads
);
337 if ( !ADS_ERR_OK(status
) ) {
341 /* Now do the bind */
343 if (ads
->auth
.flags
& ADS_AUTH_ANON_BIND
) {
344 return ADS_ERROR(ldap_simple_bind_s( ads
->ld
, NULL
, NULL
));
347 if (ads
->auth
.flags
& ADS_AUTH_SIMPLE_BIND
) {
348 return ADS_ERROR(ldap_simple_bind_s( ads
->ld
, ads
->auth
.user_name
, ads
->auth
.password
));
351 return ads_sasl_bind(ads
);
355 Duplicate a struct berval into talloc'ed memory
357 static struct berval
*dup_berval(TALLOC_CTX
*ctx
, const struct berval
*in_val
)
359 struct berval
*value
;
361 if (!in_val
) return NULL
;
363 value
= TALLOC_ZERO_P(ctx
, struct berval
);
366 if (in_val
->bv_len
== 0) return value
;
368 value
->bv_len
= in_val
->bv_len
;
369 value
->bv_val
= TALLOC_MEMDUP(ctx
, in_val
->bv_val
, in_val
->bv_len
);
374 Make a values list out of an array of (struct berval *)
376 static struct berval
**ads_dup_values(TALLOC_CTX
*ctx
,
377 const struct berval
**in_vals
)
379 struct berval
**values
;
382 if (!in_vals
) return NULL
;
383 for (i
=0; in_vals
[i
]; i
++)
385 values
= TALLOC_ZERO_ARRAY(ctx
, struct berval
*, i
+1);
386 if (!values
) return NULL
;
388 for (i
=0; in_vals
[i
]; i
++) {
389 values
[i
] = dup_berval(ctx
, in_vals
[i
]);
395 UTF8-encode a values list out of an array of (char *)
397 static char **ads_push_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
402 if (!in_vals
) return NULL
;
403 for (i
=0; in_vals
[i
]; i
++)
405 values
= TALLOC_ZERO_ARRAY(ctx
, char *, i
+1);
406 if (!values
) return NULL
;
408 for (i
=0; in_vals
[i
]; i
++) {
409 push_utf8_talloc(ctx
, &values
[i
], in_vals
[i
]);
415 Pull a (char *) array out of a UTF8-encoded values list
417 static char **ads_pull_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
422 if (!in_vals
) return NULL
;
423 for (i
=0; in_vals
[i
]; i
++)
425 values
= TALLOC_ZERO_ARRAY(ctx
, char *, i
+1);
426 if (!values
) return NULL
;
428 for (i
=0; in_vals
[i
]; i
++) {
429 pull_utf8_talloc(ctx
, &values
[i
], in_vals
[i
]);
435 * Do a search with paged results. cookie must be null on the first
436 * call, and then returned on each subsequent call. It will be null
437 * again when the entire search is complete
438 * @param ads connection to ads server
439 * @param bind_path Base dn for the search
440 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
441 * @param expr Search expression - specified in local charset
442 * @param attrs Attributes to retrieve - specified in utf8 or ascii
443 * @param res ** which will contain results - free res* with ads_msgfree()
444 * @param count Number of entries retrieved on this page
445 * @param cookie The paged results cookie to be returned on subsequent calls
446 * @return status of search
448 ADS_STATUS
ads_do_paged_search(ADS_STRUCT
*ads
, const char *bind_path
,
449 int scope
, const char *expr
,
450 const char **attrs
, void **res
,
451 int *count
, void **cookie
)
454 char *utf8_expr
, *utf8_path
, **search_attrs
;
455 LDAPControl PagedResults
, NoReferrals
, *controls
[3], **rcontrols
;
456 BerElement
*cookie_be
= NULL
;
457 struct berval
*cookie_bv
= NULL
;
462 if (!(ctx
= talloc_init("ads_do_paged_search")))
463 return ADS_ERROR(LDAP_NO_MEMORY
);
465 /* 0 means the conversion worked but the result was empty
466 so we only fail if it's -1. In any case, it always
467 at least nulls out the dest */
468 if ((push_utf8_talloc(ctx
, &utf8_expr
, expr
) == (size_t)-1) ||
469 (push_utf8_talloc(ctx
, &utf8_path
, bind_path
) == (size_t)-1)) {
474 if (!attrs
|| !(*attrs
))
477 /* This would be the utf8-encoded version...*/
478 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
479 if (!(str_list_copy(&search_attrs
, attrs
))) {
486 /* Paged results only available on ldap v3 or later */
487 ldap_get_option(ads
->ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
488 if (version
< LDAP_VERSION3
) {
489 rc
= LDAP_NOT_SUPPORTED
;
493 cookie_be
= ber_alloc_t(LBER_USE_DER
);
494 if (cookie
&& *cookie
) {
495 ber_printf(cookie_be
, "{iO}", (ber_int_t
) 1000, *cookie
);
496 ber_bvfree(*cookie
); /* don't need it from last time */
499 ber_printf(cookie_be
, "{io}", (ber_int_t
) 1000, "", 0);
501 ber_flatten(cookie_be
, &cookie_bv
);
502 PagedResults
.ldctl_oid
= CONST_DISCARD(char *, ADS_PAGE_CTL_OID
);
503 PagedResults
.ldctl_iscritical
= (char) 1;
504 PagedResults
.ldctl_value
.bv_len
= cookie_bv
->bv_len
;
505 PagedResults
.ldctl_value
.bv_val
= cookie_bv
->bv_val
;
507 NoReferrals
.ldctl_oid
= CONST_DISCARD(char *, ADS_NO_REFERRALS_OID
);
508 NoReferrals
.ldctl_iscritical
= (char) 0;
509 NoReferrals
.ldctl_value
.bv_len
= 0;
510 NoReferrals
.ldctl_value
.bv_val
= CONST_DISCARD(char *, "");
513 controls
[0] = &NoReferrals
;
514 controls
[1] = &PagedResults
;
517 /* we need to disable referrals as the openldap libs don't
518 handle them and paged results at the same time. Using them
519 together results in the result record containing the server
520 page control being removed from the result list (tridge/jmcd)
522 leaving this in despite the control that says don't generate
523 referrals, in case the server doesn't support it (jmcd)
525 ldap_set_option(ads
->ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
527 rc
= ldap_search_with_timeout(ads
->ld
, utf8_path
, scope
, utf8_expr
,
528 search_attrs
, 0, controls
,
530 (LDAPMessage
**)res
);
532 ber_free(cookie_be
, 1);
533 ber_bvfree(cookie_bv
);
536 DEBUG(3,("ads_do_paged_search: ldap_search_with_timeout(%s) -> %s\n", expr
,
537 ldap_err2string(rc
)));
541 rc
= ldap_parse_result(ads
->ld
, *res
, NULL
, NULL
, NULL
,
542 NULL
, &rcontrols
, 0);
548 for (i
=0; rcontrols
[i
]; i
++) {
549 if (strcmp(ADS_PAGE_CTL_OID
, rcontrols
[i
]->ldctl_oid
) == 0) {
550 cookie_be
= ber_init(&rcontrols
[i
]->ldctl_value
);
551 ber_scanf(cookie_be
,"{iO}", (ber_int_t
*) count
,
553 /* the berval is the cookie, but must be freed when
555 if (cookie_bv
->bv_len
) /* still more to do */
556 *cookie
=ber_bvdup(cookie_bv
);
559 ber_bvfree(cookie_bv
);
560 ber_free(cookie_be
, 1);
564 ldap_controls_free(rcontrols
);
568 /* if/when we decide to utf8-encode attrs, take out this next line */
569 str_list_free(&search_attrs
);
571 return ADS_ERROR(rc
);
576 * Get all results for a search. This uses ads_do_paged_search() to return
577 * all entries in a large search.
578 * @param ads connection to ads server
579 * @param bind_path Base dn for the search
580 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
581 * @param expr Search expression
582 * @param attrs Attributes to retrieve
583 * @param res ** which will contain results - free res* with ads_msgfree()
584 * @return status of search
586 ADS_STATUS
ads_do_search_all(ADS_STRUCT
*ads
, const char *bind_path
,
587 int scope
, const char *expr
,
588 const char **attrs
, void **res
)
595 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
, res
,
598 if (!ADS_ERR_OK(status
))
601 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
605 LDAPMessage
*msg
, *next
;
607 status2
= ads_do_paged_search(ads
, bind_path
, scope
, expr
,
608 attrs
, &res2
, &count
, &cookie
);
610 if (!ADS_ERR_OK(status2
)) break;
612 /* this relies on the way that ldap_add_result_entry() works internally. I hope
613 that this works on all ldap libs, but I have only tested with openldap */
614 for (msg
= ads_first_entry(ads
, res2
); msg
; msg
= next
) {
615 next
= ads_next_entry(ads
, msg
);
616 ldap_add_result_entry((LDAPMessage
**)res
, msg
);
618 /* note that we do not free res2, as the memory is now
619 part of the main returned list */
622 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
623 status
= ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL
);
630 * Run a function on all results for a search. Uses ads_do_paged_search() and
631 * runs the function as each page is returned, using ads_process_results()
632 * @param ads connection to ads server
633 * @param bind_path Base dn for the search
634 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
635 * @param expr Search expression - specified in local charset
636 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
637 * @param fn Function which takes attr name, values list, and data_area
638 * @param data_area Pointer which is passed to function on each call
639 * @return status of search
641 ADS_STATUS
ads_do_search_all_fn(ADS_STRUCT
*ads
, const char *bind_path
,
642 int scope
, const char *expr
, const char **attrs
,
643 BOOL(*fn
)(char *, void **, void *),
651 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
, &res
,
654 if (!ADS_ERR_OK(status
)) return status
;
656 ads_process_results(ads
, res
, fn
, data_area
);
657 ads_msgfree(ads
, res
);
660 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
,
661 &res
, &count
, &cookie
);
663 if (!ADS_ERR_OK(status
)) break;
665 ads_process_results(ads
, res
, fn
, data_area
);
666 ads_msgfree(ads
, res
);
673 * Do a search with a timeout.
674 * @param ads connection to ads server
675 * @param bind_path Base dn for the search
676 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
677 * @param expr Search expression
678 * @param attrs Attributes to retrieve
679 * @param res ** which will contain results - free res* with ads_msgfree()
680 * @return status of search
682 ADS_STATUS
ads_do_search(ADS_STRUCT
*ads
, const char *bind_path
, int scope
,
684 const char **attrs
, void **res
)
687 char *utf8_expr
, *utf8_path
, **search_attrs
= NULL
;
691 if (!(ctx
= talloc_init("ads_do_search"))) {
692 DEBUG(1,("ads_do_search: talloc_init() failed!"));
693 return ADS_ERROR(LDAP_NO_MEMORY
);
696 /* 0 means the conversion worked but the result was empty
697 so we only fail if it's negative. In any case, it always
698 at least nulls out the dest */
699 if ((push_utf8_talloc(ctx
, &utf8_expr
, expr
) == (size_t)-1) ||
700 (push_utf8_talloc(ctx
, &utf8_path
, bind_path
) == (size_t)-1)) {
701 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
706 if (!attrs
|| !(*attrs
))
709 /* This would be the utf8-encoded version...*/
710 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
711 if (!(str_list_copy(&search_attrs
, attrs
)))
713 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
719 /* see the note in ads_do_paged_search - we *must* disable referrals */
720 ldap_set_option(ads
->ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
722 rc
= ldap_search_with_timeout(ads
->ld
, utf8_path
, scope
, utf8_expr
,
723 search_attrs
, 0, NULL
, NULL
,
725 (LDAPMessage
**)res
);
727 if (rc
== LDAP_SIZELIMIT_EXCEEDED
) {
728 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
734 /* if/when we decide to utf8-encode attrs, take out this next line */
735 str_list_free(&search_attrs
);
736 return ADS_ERROR(rc
);
739 * Do a general ADS search
740 * @param ads connection to ads server
741 * @param res ** which will contain results - free res* with ads_msgfree()
742 * @param expr Search expression
743 * @param attrs Attributes to retrieve
744 * @return status of search
746 ADS_STATUS
ads_search(ADS_STRUCT
*ads
, void **res
,
750 return ads_do_search(ads
, ads
->config
.bind_path
, LDAP_SCOPE_SUBTREE
,
755 * Do a search on a specific DistinguishedName
756 * @param ads connection to ads server
757 * @param res ** which will contain results - free res* with ads_msgfree()
758 * @param dn DistinguishName to search
759 * @param attrs Attributes to retrieve
760 * @return status of search
762 ADS_STATUS
ads_search_dn(ADS_STRUCT
*ads
, void **res
,
766 return ads_do_search(ads
, dn
, LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, res
);
770 * Free up memory from a ads_search
771 * @param ads connection to ads server
772 * @param msg Search results to free
774 void ads_msgfree(ADS_STRUCT
*ads
, void *msg
)
781 * Free up memory from various ads requests
782 * @param ads connection to ads server
783 * @param mem Area to free
785 void ads_memfree(ADS_STRUCT
*ads
, void *mem
)
791 * Get a dn from search results
792 * @param ads connection to ads server
793 * @param msg Search result
796 char *ads_get_dn(ADS_STRUCT
*ads
, void *msg
)
798 char *utf8_dn
, *unix_dn
;
800 utf8_dn
= ldap_get_dn(ads
->ld
, msg
);
803 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
807 if (pull_utf8_allocate(&unix_dn
, utf8_dn
) == (size_t)-1) {
808 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
812 ldap_memfree(utf8_dn
);
817 * Get a canonical dn from search results
818 * @param ads connection to ads server
819 * @param msg Search result
822 char *ads_get_dn_canonical(ADS_STRUCT
*ads
, void *msg
)
824 #ifdef HAVE_LDAP_DN2AD_CANONICAL
825 return ldap_dn2ad_canonical(ads_get_dn(ads
, msg
));
832 * Get the parent from a dn
833 * @param dn the dn to return the parent from
834 * @return parent dn string
836 char *ads_parent_dn(const char *dn
)
838 char *p
= strchr(dn
, ',');
848 * Find a machine account given a hostname
849 * @param ads connection to ads server
850 * @param res ** which will contain results - free res* with ads_msgfree()
851 * @param host Hostname to search for
852 * @return status of search
854 ADS_STATUS
ads_find_machine_acct(ADS_STRUCT
*ads
, void **res
, const char *machine
)
858 const char *attrs
[] = {"*", "nTSecurityDescriptor", NULL
};
862 /* the easiest way to find a machine account anywhere in the tree
863 is to look for hostname$ */
864 if (asprintf(&expr
, "(samAccountName=%s$)", machine
) == -1) {
865 DEBUG(1, ("asprintf failed!\n"));
866 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
869 status
= ads_search(ads
, res
, expr
, attrs
);
875 * Initialize a list of mods to be used in a modify request
876 * @param ctx An initialized TALLOC_CTX
877 * @return allocated ADS_MODLIST
879 ADS_MODLIST
ads_init_mods(TALLOC_CTX
*ctx
)
881 #define ADS_MODLIST_ALLOC_SIZE 10
884 if ((mods
= TALLOC_ZERO_ARRAY(ctx
, LDAPMod
*, ADS_MODLIST_ALLOC_SIZE
+ 1)))
885 /* -1 is safety to make sure we don't go over the end.
886 need to reset it to NULL before doing ldap modify */
887 mods
[ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
889 return (ADS_MODLIST
)mods
;
894 add an attribute to the list, with values list already constructed
896 static ADS_STATUS
ads_modlist_add(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
897 int mod_op
, const char *name
,
901 LDAPMod
**modlist
= (LDAPMod
**) *mods
;
902 struct berval
**ber_values
= NULL
;
903 char **char_values
= NULL
;
906 mod_op
= LDAP_MOD_DELETE
;
908 if (mod_op
& LDAP_MOD_BVALUES
)
909 ber_values
= ads_dup_values(ctx
,
910 (const struct berval
**)invals
);
912 char_values
= ads_push_strvals(ctx
,
913 (const char **) invals
);
916 /* find the first empty slot */
917 for (curmod
=0; modlist
[curmod
] && modlist
[curmod
] != (LDAPMod
*) -1;
919 if (modlist
[curmod
] == (LDAPMod
*) -1) {
920 if (!(modlist
= TALLOC_REALLOC_ARRAY(ctx
, modlist
, LDAPMod
*,
921 curmod
+ADS_MODLIST_ALLOC_SIZE
+1)))
922 return ADS_ERROR(LDAP_NO_MEMORY
);
923 memset(&modlist
[curmod
], 0,
924 ADS_MODLIST_ALLOC_SIZE
*sizeof(LDAPMod
*));
925 modlist
[curmod
+ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
926 *mods
= (ADS_MODLIST
)modlist
;
929 if (!(modlist
[curmod
] = TALLOC_ZERO_P(ctx
, LDAPMod
)))
930 return ADS_ERROR(LDAP_NO_MEMORY
);
931 modlist
[curmod
]->mod_type
= talloc_strdup(ctx
, name
);
932 if (mod_op
& LDAP_MOD_BVALUES
) {
933 modlist
[curmod
]->mod_bvalues
= ber_values
;
934 } else if (mod_op
& LDAP_MOD_DELETE
) {
935 modlist
[curmod
]->mod_values
= NULL
;
937 modlist
[curmod
]->mod_values
= char_values
;
940 modlist
[curmod
]->mod_op
= mod_op
;
941 return ADS_ERROR(LDAP_SUCCESS
);
945 * Add a single string value to a mod list
946 * @param ctx An initialized TALLOC_CTX
947 * @param mods An initialized ADS_MODLIST
948 * @param name The attribute name to add
949 * @param val The value to add - NULL means DELETE
950 * @return ADS STATUS indicating success of add
952 ADS_STATUS
ads_mod_str(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
953 const char *name
, const char *val
)
955 const char *values
[2];
961 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
962 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
, name
,
963 (const void **) values
);
967 * Add an array of string values to a mod list
968 * @param ctx An initialized TALLOC_CTX
969 * @param mods An initialized ADS_MODLIST
970 * @param name The attribute name to add
971 * @param vals The array of string values to add - NULL means DELETE
972 * @return ADS STATUS indicating success of add
974 ADS_STATUS
ads_mod_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
975 const char *name
, const char **vals
)
978 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
979 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
,
980 name
, (const void **) vals
);
985 * Add a single ber-encoded value to a mod list
986 * @param ctx An initialized TALLOC_CTX
987 * @param mods An initialized ADS_MODLIST
988 * @param name The attribute name to add
989 * @param val The value to add - NULL means DELETE
990 * @return ADS STATUS indicating success of add
992 static ADS_STATUS
ads_mod_ber(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
993 const char *name
, const struct berval
*val
)
995 const struct berval
*values
[2];
1000 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1001 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
|LDAP_MOD_BVALUES
,
1002 name
, (const void **) values
);
1007 * Perform an ldap modify
1008 * @param ads connection to ads server
1009 * @param mod_dn DistinguishedName to modify
1010 * @param mods list of modifications to perform
1011 * @return status of modify
1013 ADS_STATUS
ads_gen_mod(ADS_STRUCT
*ads
, const char *mod_dn
, ADS_MODLIST mods
)
1016 char *utf8_dn
= NULL
;
1018 this control is needed to modify that contains a currently
1019 non-existent attribute (but allowable for the object) to run
1021 LDAPControl PermitModify
= {
1022 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID
),
1025 LDAPControl
*controls
[2];
1027 controls
[0] = &PermitModify
;
1030 if (push_utf8_allocate(&utf8_dn
, mod_dn
) == -1) {
1031 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1034 /* find the end of the list, marked by NULL or -1 */
1035 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1036 /* make sure the end of the list is NULL */
1038 ret
= ldap_modify_ext_s(ads
->ld
, utf8_dn
,
1039 (LDAPMod
**) mods
, controls
, NULL
);
1041 return ADS_ERROR(ret
);
1045 * Perform an ldap add
1046 * @param ads connection to ads server
1047 * @param new_dn DistinguishedName to add
1048 * @param mods list of attributes and values for DN
1049 * @return status of add
1051 ADS_STATUS
ads_gen_add(ADS_STRUCT
*ads
, const char *new_dn
, ADS_MODLIST mods
)
1054 char *utf8_dn
= NULL
;
1056 if (push_utf8_allocate(&utf8_dn
, new_dn
) == -1) {
1057 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1058 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1061 /* find the end of the list, marked by NULL or -1 */
1062 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1063 /* make sure the end of the list is NULL */
1066 ret
= ldap_add_s(ads
->ld
, utf8_dn
, (LDAPMod
**)mods
);
1068 return ADS_ERROR(ret
);
1072 * Delete a DistinguishedName
1073 * @param ads connection to ads server
1074 * @param new_dn DistinguishedName to delete
1075 * @return status of delete
1077 ADS_STATUS
ads_del_dn(ADS_STRUCT
*ads
, char *del_dn
)
1080 char *utf8_dn
= NULL
;
1081 if (push_utf8_allocate(&utf8_dn
, del_dn
) == -1) {
1082 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1083 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1086 ret
= ldap_delete_s(ads
->ld
, utf8_dn
);
1087 return ADS_ERROR(ret
);
1091 * Build an org unit string
1092 * if org unit is Computers or blank then assume a container, otherwise
1093 * assume a / separated list of organisational units.
1094 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1095 * @param ads connection to ads server
1096 * @param org_unit Organizational unit
1097 * @return org unit string - caller must free
1099 char *ads_ou_string(ADS_STRUCT
*ads
, const char *org_unit
)
1103 if (!org_unit
|| !*org_unit
) {
1105 ret
= ads_default_ou_string(ads
, WELL_KNOWN_GUID_COMPUTERS
);
1107 /* samba4 might not yet respond to a wellknownobject-query */
1108 return ret
? ret
: SMB_STRDUP("cn=Computers");
1111 if (strequal(org_unit
, "Computers")) {
1112 return SMB_STRDUP("cn=Computers");
1115 /* jmcd: removed "\\" from the separation chars, because it is
1116 needed as an escape for chars like '#' which are valid in an
1118 return ads_build_path(org_unit
, "/", "ou=", 1);
1122 * Get a org unit string for a well-known GUID
1123 * @param ads connection to ads server
1124 * @param wknguid Well known GUID
1125 * @return org unit string - caller must free
1127 char *ads_default_ou_string(ADS_STRUCT
*ads
, const char *wknguid
)
1131 char *base
, *wkn_dn
, *ret
, **wkn_dn_exp
, **bind_dn_exp
;
1132 const char *attrs
[] = {"distinguishedName", NULL
};
1133 int new_ln
, wkn_ln
, bind_ln
, i
;
1135 if (wknguid
== NULL
) {
1139 if (asprintf(&base
, "<WKGUID=%s,%s>", wknguid
, ads
->config
.bind_path
) == -1) {
1140 DEBUG(1, ("asprintf failed!\n"));
1144 status
= ads_search_dn(ads
, &res
, base
, attrs
);
1145 if (!ADS_ERR_OK(status
)) {
1146 DEBUG(1,("Failed while searching for: %s\n", base
));
1151 if (ads_count_replies(ads
, res
) != 1) {
1155 /* substitute the bind-path from the well-known-guid-search result */
1156 wkn_dn
= ads_get_dn(ads
, res
);
1157 wkn_dn_exp
= ldap_explode_dn(wkn_dn
, 0);
1158 bind_dn_exp
= ldap_explode_dn(ads
->config
.bind_path
, 0);
1160 for (wkn_ln
=0; wkn_dn_exp
[wkn_ln
]; wkn_ln
++)
1162 for (bind_ln
=0; bind_dn_exp
[bind_ln
]; bind_ln
++)
1165 new_ln
= wkn_ln
- bind_ln
;
1167 ret
= wkn_dn_exp
[0];
1169 for (i
=1; i
< new_ln
; i
++) {
1171 asprintf(&s
, "%s,%s", ret
, wkn_dn_exp
[i
]);
1172 ret
= SMB_STRDUP(s
);
1180 * Adds (appends) an item to an attribute array, rather then
1181 * replacing the whole list
1182 * @param ctx An initialized TALLOC_CTX
1183 * @param mods An initialized ADS_MODLIST
1184 * @param name name of the ldap attribute to append to
1185 * @param vals an array of values to add
1186 * @return status of addition
1189 ADS_STATUS
ads_add_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1190 const char *name
, const char **vals
)
1192 return ads_modlist_add(ctx
, mods
, LDAP_MOD_ADD
, name
, (const void **) vals
);
1196 * Determines the computer account's current KVNO via an LDAP lookup
1197 * @param ads An initialized ADS_STRUCT
1198 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1199 * @return the kvno for the computer account, or -1 in case of a failure.
1202 uint32
ads_get_kvno(ADS_STRUCT
*ads
, const char *machine_name
)
1204 LDAPMessage
*res
= NULL
;
1205 uint32 kvno
= (uint32
)-1; /* -1 indicates a failure */
1207 const char *attrs
[] = {"msDS-KeyVersionNumber", NULL
};
1208 char *dn_string
= NULL
;
1209 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1211 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name
));
1212 if (asprintf(&filter
, "(samAccountName=%s$)", machine_name
) == -1) {
1215 ret
= ads_search(ads
, (void**)(void *)&res
, filter
, attrs
);
1217 if (!ADS_ERR_OK(ret
) && ads_count_replies(ads
, res
)) {
1218 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name
));
1219 ads_msgfree(ads
, res
);
1223 dn_string
= ads_get_dn(ads
, res
);
1225 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1226 ads_msgfree(ads
, res
);
1229 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string
));
1230 ads_memfree(ads
, dn_string
);
1232 /* ---------------------------------------------------------
1233 * 0 is returned as a default KVNO from this point on...
1234 * This is done because Windows 2000 does not support key
1235 * version numbers. Chances are that a failure in the next
1236 * step is simply due to Windows 2000 being used for a
1237 * domain controller. */
1240 if (!ads_pull_uint32(ads
, res
, "msDS-KeyVersionNumber", &kvno
)) {
1241 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1242 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1243 ads_msgfree(ads
, res
);
1248 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno
));
1249 ads_msgfree(ads
, res
);
1254 * This clears out all registered spn's for a given hostname
1255 * @param ads An initilaized ADS_STRUCT
1256 * @param machine_name the NetBIOS name of the computer.
1257 * @return 0 upon success, non-zero otherwise.
1260 ADS_STATUS
ads_clear_service_principal_names(ADS_STRUCT
*ads
, const char *machine_name
)
1263 LDAPMessage
*res
= NULL
;
1265 const char *servicePrincipalName
[1] = {NULL
};
1266 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1267 char *dn_string
= NULL
;
1269 ret
= ads_find_machine_acct(ads
, (void **)(void *)&res
, machine_name
);
1270 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1271 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name
));
1272 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name
));
1273 ads_msgfree(ads
, res
);
1274 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1277 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name
));
1278 ctx
= talloc_init("ads_clear_service_principal_names");
1280 ads_msgfree(ads
, res
);
1281 return ADS_ERROR(LDAP_NO_MEMORY
);
1284 if (!(mods
= ads_init_mods(ctx
))) {
1285 talloc_destroy(ctx
);
1286 ads_msgfree(ads
, res
);
1287 return ADS_ERROR(LDAP_NO_MEMORY
);
1289 ret
= ads_mod_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1290 if (!ADS_ERR_OK(ret
)) {
1291 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1292 ads_msgfree(ads
, res
);
1293 talloc_destroy(ctx
);
1296 dn_string
= ads_get_dn(ads
, res
);
1298 talloc_destroy(ctx
);
1299 ads_msgfree(ads
, res
);
1300 return ADS_ERROR(LDAP_NO_MEMORY
);
1302 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1303 ads_memfree(ads
,dn_string
);
1304 if (!ADS_ERR_OK(ret
)) {
1305 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1307 ads_msgfree(ads
, res
);
1308 talloc_destroy(ctx
);
1312 ads_msgfree(ads
, res
);
1313 talloc_destroy(ctx
);
1318 * This adds a service principal name to an existing computer account
1319 * (found by hostname) in AD.
1320 * @param ads An initialized ADS_STRUCT
1321 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1322 * @param spn A string of the service principal to add, i.e. 'host'
1323 * @return 0 upon sucess, or non-zero if a failure occurs
1326 ADS_STATUS
ads_add_service_principal_name(ADS_STRUCT
*ads
, const char *machine_name
, const char *spn
)
1330 LDAPMessage
*res
= NULL
;
1331 char *host_spn
, *psp1
, *psp2
, *psp3
;
1334 char *dn_string
= NULL
;
1335 const char *servicePrincipalName
[4] = {NULL
, NULL
, NULL
, NULL
};
1337 ret
= ads_find_machine_acct(ads
, (void **)(void *)&res
, machine_name
);
1338 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1339 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1341 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1342 spn
, machine_name
, ads
->config
.realm
));
1343 ads_msgfree(ads
, res
);
1344 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1347 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name
));
1348 if (!(ctx
= talloc_init("ads_add_service_principal_name"))) {
1349 ads_msgfree(ads
, res
);
1350 return ADS_ERROR(LDAP_NO_MEMORY
);
1353 name_to_fqdn(my_fqdn
, machine_name
);
1354 strlower_m(my_fqdn
);
1356 if (!(host_spn
= talloc_asprintf(ctx
, "HOST/%s", my_fqdn
))) {
1357 talloc_destroy(ctx
);
1358 ads_msgfree(ads
, res
);
1359 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1362 /* Add the extra principal */
1363 psp1
= talloc_asprintf(ctx
, "%s/%s", spn
, machine_name
);
1365 strlower_m(&psp1
[strlen(spn
)]);
1366 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp1
, machine_name
));
1367 servicePrincipalName
[0] = psp1
;
1368 psp2
= talloc_asprintf(ctx
, "%s/%s.%s", spn
, machine_name
, ads
->config
.realm
);
1370 strlower_m(&psp2
[strlen(spn
)]);
1371 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp2
, machine_name
));
1372 servicePrincipalName
[1] = psp2
;
1374 /* Add another principal in case the realm != the DNS domain, so that
1375 * the KDC doesn't send "server principal unknown" errors to clients
1376 * which use the DNS name in determining service principal names. */
1377 psp3
= talloc_asprintf(ctx
, "%s/%s", spn
, my_fqdn
);
1379 strlower_m(&psp3
[strlen(spn
)]);
1380 if (strcmp(psp2
, psp3
) != 0) {
1381 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp3
, machine_name
));
1382 servicePrincipalName
[2] = psp3
;
1385 if (!(mods
= ads_init_mods(ctx
))) {
1386 talloc_destroy(ctx
);
1387 ads_msgfree(ads
, res
);
1388 return ADS_ERROR(LDAP_NO_MEMORY
);
1390 ret
= ads_add_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1391 if (!ADS_ERR_OK(ret
)) {
1392 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1393 talloc_destroy(ctx
);
1394 ads_msgfree(ads
, res
);
1397 dn_string
= ads_get_dn(ads
, res
);
1399 talloc_destroy(ctx
);
1400 ads_msgfree(ads
, res
);
1401 return ADS_ERROR(LDAP_NO_MEMORY
);
1403 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1404 ads_memfree(ads
,dn_string
);
1405 if (!ADS_ERR_OK(ret
)) {
1406 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1407 talloc_destroy(ctx
);
1408 ads_msgfree(ads
, res
);
1412 talloc_destroy(ctx
);
1413 ads_msgfree(ads
, res
);
1418 * adds a machine account to the ADS server
1419 * @param ads An intialized ADS_STRUCT
1420 * @param machine_name - the NetBIOS machine name of this account.
1421 * @param account_type A number indicating the type of account to create
1422 * @param org_unit The LDAP path in which to place this account
1423 * @return 0 upon success, or non-zero otherwise
1426 ADS_STATUS
ads_create_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
1427 const char *org_unit
)
1430 char *samAccountName
, *controlstr
;
1434 const char *objectClass
[] = {"top", "person", "organizationalPerson",
1435 "user", "computer", NULL
};
1436 LDAPMessage
*res
= NULL
;
1437 uint32 acct_control
= ( UF_WORKSTATION_TRUST_ACCOUNT
|\
1438 UF_DONT_EXPIRE_PASSWD
|\
1439 UF_ACCOUNTDISABLE
);
1441 if (!(ctx
= talloc_init("ads_add_machine_acct")))
1442 return ADS_ERROR(LDAP_NO_MEMORY
);
1444 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1446 new_dn
= talloc_asprintf(ctx
, "cn=%s,%s", machine_name
, org_unit
);
1447 samAccountName
= talloc_asprintf(ctx
, "%s$", machine_name
);
1449 if ( !new_dn
|| !samAccountName
) {
1453 #ifndef ENCTYPE_ARCFOUR_HMAC
1454 acct_control
|= UF_USE_DES_KEY_ONLY
;
1457 if (!(controlstr
= talloc_asprintf(ctx
, "%u", acct_control
))) {
1461 if (!(mods
= ads_init_mods(ctx
))) {
1465 ads_mod_str(ctx
, &mods
, "cn", machine_name
);
1466 ads_mod_str(ctx
, &mods
, "sAMAccountName", samAccountName
);
1467 ads_mod_strlist(ctx
, &mods
, "objectClass", objectClass
);
1468 ads_mod_str(ctx
, &mods
, "userAccountControl", controlstr
);
1470 ret
= ads_gen_add(ads
, new_dn
, mods
);
1473 ads_msgfree(ads
, res
);
1474 talloc_destroy(ctx
);
1480 dump a binary result from ldap
1482 static void dump_binary(const char *field
, struct berval
**values
)
1485 for (i
=0; values
[i
]; i
++) {
1486 printf("%s: ", field
);
1487 for (j
=0; j
<values
[i
]->bv_len
; j
++) {
1488 printf("%02X", (unsigned char)values
[i
]->bv_val
[j
]);
1494 static void dump_guid(const char *field
, struct berval
**values
)
1498 for (i
=0; values
[i
]; i
++) {
1499 memcpy(guid
.info
, values
[i
]->bv_val
, sizeof(guid
.info
));
1500 printf("%s: %s\n", field
,
1501 smb_uuid_string_static(smb_uuid_unpack_static(guid
)));
1506 dump a sid result from ldap
1508 static void dump_sid(const char *field
, struct berval
**values
)
1511 for (i
=0; values
[i
]; i
++) {
1513 sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &sid
);
1514 printf("%s: %s\n", field
, sid_string_static(&sid
));
1519 dump ntSecurityDescriptor
1521 static void dump_sd(const char *filed
, struct berval
**values
)
1526 TALLOC_CTX
*ctx
= 0;
1528 if (!(ctx
= talloc_init("sec_io_desc")))
1532 prs_init(&ps
, values
[0]->bv_len
, ctx
, UNMARSHALL
);
1533 prs_copy_data_in(&ps
, values
[0]->bv_val
, values
[0]->bv_len
);
1534 prs_set_offset(&ps
,0);
1537 if (!sec_io_desc("sd", &psd
, &ps
, 1)) {
1539 talloc_destroy(ctx
);
1542 if (psd
) ads_disp_sd(psd
);
1545 talloc_destroy(ctx
);
1549 dump a string result from ldap
1551 static void dump_string(const char *field
, char **values
)
1554 for (i
=0; values
[i
]; i
++) {
1555 printf("%s: %s\n", field
, values
[i
]);
1560 dump a field from LDAP on stdout
1564 static BOOL
ads_dump_field(char *field
, void **values
, void *data_area
)
1569 void (*handler
)(const char *, struct berval
**);
1571 {"objectGUID", False
, dump_guid
},
1572 {"netbootGUID", False
, dump_guid
},
1573 {"nTSecurityDescriptor", False
, dump_sd
},
1574 {"dnsRecord", False
, dump_binary
},
1575 {"objectSid", False
, dump_sid
},
1576 {"tokenGroups", False
, dump_sid
},
1577 {"tokenGroupsNoGCAcceptable", False
, dump_sid
},
1578 {"tokengroupsGlobalandUniversal", False
, dump_sid
},
1583 if (!field
) { /* must be end of an entry */
1588 for (i
=0; handlers
[i
].name
; i
++) {
1589 if (StrCaseCmp(handlers
[i
].name
, field
) == 0) {
1590 if (!values
) /* first time, indicate string or not */
1591 return handlers
[i
].string
;
1592 handlers
[i
].handler(field
, (struct berval
**) values
);
1596 if (!handlers
[i
].name
) {
1597 if (!values
) /* first time, indicate string conversion */
1599 dump_string(field
, (char **)values
);
1605 * Dump a result from LDAP on stdout
1606 * used for debugging
1607 * @param ads connection to ads server
1608 * @param res Results to dump
1611 void ads_dump(ADS_STRUCT
*ads
, void *res
)
1613 ads_process_results(ads
, res
, ads_dump_field
, NULL
);
1617 * Walk through results, calling a function for each entry found.
1618 * The function receives a field name, a berval * array of values,
1619 * and a data area passed through from the start. The function is
1620 * called once with null for field and values at the end of each
1622 * @param ads connection to ads server
1623 * @param res Results to process
1624 * @param fn Function for processing each result
1625 * @param data_area user-defined area to pass to function
1627 void ads_process_results(ADS_STRUCT
*ads
, void *res
,
1628 BOOL(*fn
)(char *, void **, void *),
1634 if (!(ctx
= talloc_init("ads_process_results")))
1637 for (msg
= ads_first_entry(ads
, res
); msg
;
1638 msg
= ads_next_entry(ads
, msg
)) {
1642 for (utf8_field
=ldap_first_attribute(ads
->ld
,
1643 (LDAPMessage
*)msg
,&b
);
1645 utf8_field
=ldap_next_attribute(ads
->ld
,
1646 (LDAPMessage
*)msg
,b
)) {
1647 struct berval
**ber_vals
;
1648 char **str_vals
, **utf8_vals
;
1652 pull_utf8_talloc(ctx
, &field
, utf8_field
);
1653 string
= fn(field
, NULL
, data_area
);
1656 utf8_vals
= ldap_get_values(ads
->ld
,
1657 (LDAPMessage
*)msg
, field
);
1658 str_vals
= ads_pull_strvals(ctx
,
1659 (const char **) utf8_vals
);
1660 fn(field
, (void **) str_vals
, data_area
);
1661 ldap_value_free(utf8_vals
);
1663 ber_vals
= ldap_get_values_len(ads
->ld
,
1664 (LDAPMessage
*)msg
, field
);
1665 fn(field
, (void **) ber_vals
, data_area
);
1667 ldap_value_free_len(ber_vals
);
1669 ldap_memfree(utf8_field
);
1672 talloc_free_children(ctx
);
1673 fn(NULL
, NULL
, data_area
); /* completed an entry */
1676 talloc_destroy(ctx
);
1680 * count how many replies are in a LDAPMessage
1681 * @param ads connection to ads server
1682 * @param res Results to count
1683 * @return number of replies
1685 int ads_count_replies(ADS_STRUCT
*ads
, void *res
)
1687 return ldap_count_entries(ads
->ld
, (LDAPMessage
*)res
);
1691 * Delete a machine from the realm
1692 * @param ads connection to ads server
1693 * @param hostname Machine to remove
1694 * @return status of delete
1696 ADS_STATUS
ads_leave_realm(ADS_STRUCT
*ads
, const char *hostname
)
1700 char *hostnameDN
, *host
;
1702 LDAPControl ldap_control
;
1703 LDAPControl
* pldap_control
[2] = {NULL
, NULL
};
1705 pldap_control
[0] = &ldap_control
;
1706 memset(&ldap_control
, 0, sizeof(LDAPControl
));
1707 ldap_control
.ldctl_oid
= (char *)LDAP_SERVER_TREE_DELETE_OID
;
1709 /* hostname must be lowercase */
1710 host
= SMB_STRDUP(hostname
);
1713 status
= ads_find_machine_acct(ads
, &res
, host
);
1714 if (!ADS_ERR_OK(status
)) {
1715 DEBUG(0, ("Host account for %s does not exist.\n", host
));
1719 msg
= ads_first_entry(ads
, res
);
1721 return ADS_ERROR_SYSTEM(ENOENT
);
1724 hostnameDN
= ads_get_dn(ads
, (LDAPMessage
*)msg
);
1727 rc
= ldap_delete_ext_s(ads
->ld
, hostnameDN
, pldap_control
, NULL
);
1729 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc
));
1731 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc
));
1734 ads_memfree(ads
, hostnameDN
);
1735 if (rc
!= LDAP_SUCCESS
) {
1736 return ADS_ERROR(rc
);
1739 status
= ads_find_machine_acct(ads
, &res
, host
);
1740 if (ADS_ERR_OK(status
) && ads_count_replies(ads
, res
) == 1) {
1741 DEBUG(0, ("Failed to remove host account.\n"));
1752 * add machine account to existing security descriptor
1753 * @param ads connection to ads server
1754 * @param hostname machine to add
1755 * @param dn DN of security descriptor
1758 static ADS_STATUS
ads_set_machine_sd(ADS_STRUCT
*ads
, const char *hostname
, char *dn
)
1760 const char *attrs
[] = {"nTSecurityDescriptor", "objectSid", 0};
1763 struct berval bval
= {0, NULL
};
1765 char *escaped_hostname
= escape_ldap_string_alloc(hostname
);
1767 LDAPMessage
*res
= 0;
1768 LDAPMessage
*msg
= 0;
1769 ADS_MODLIST mods
= 0;
1774 SEC_DESC
*psd
= NULL
;
1775 TALLOC_CTX
*ctx
= NULL
;
1777 /* Avoid segmentation fault in prs_mem_free if
1778 * we have to bail out before prs_init */
1779 ps_wire
.is_dynamic
= False
;
1782 SAFE_FREE(escaped_hostname
);
1783 return ADS_ERROR(LDAP_SERVER_DOWN
);
1786 ret
= ADS_ERROR(LDAP_SUCCESS
);
1788 if (!escaped_hostname
) {
1789 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1792 if (asprintf(&expr
, "(samAccountName=%s$)", escaped_hostname
) == -1) {
1793 DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n"));
1794 SAFE_FREE(escaped_hostname
);
1795 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1798 SAFE_FREE(escaped_hostname
);
1800 ret
= ads_search(ads
, (void *) &res
, expr
, attrs
);
1804 if (!ADS_ERR_OK(ret
)) return ret
;
1806 if ( !(msg
= ads_first_entry(ads
, res
) )) {
1807 ret
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
1808 goto ads_set_sd_error
;
1811 if (!ads_pull_sid(ads
, msg
, attrs
[1], &sid
)) {
1812 ret
= ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
1813 goto ads_set_sd_error
;
1816 if (!(ctx
= talloc_init("sec_io_desc"))) {
1817 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1818 goto ads_set_sd_error
;
1821 if (!ads_pull_sd(ads
, ctx
, msg
, attrs
[0], &psd
)) {
1822 ret
= ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
1823 goto ads_set_sd_error
;
1826 status
= sec_desc_add_sid(ctx
, &psd
, &sid
, SEC_RIGHTS_FULL_CTRL
, &sd_size
);
1828 if (!NT_STATUS_IS_OK(status
)) {
1829 ret
= ADS_ERROR_NT(status
);
1830 goto ads_set_sd_error
;
1833 if (!prs_init(&ps_wire
, sd_size
, ctx
, MARSHALL
)) {
1834 ret
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1837 if (!sec_io_desc("sd_wire", &psd
, &ps_wire
, 1)) {
1838 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1839 goto ads_set_sd_error
;
1843 file_save("/tmp/sec_desc.new", ps_wire
.data_p
, sd_size
);
1845 if (!(mods
= ads_init_mods(ctx
))) return ADS_ERROR(LDAP_NO_MEMORY
);
1847 bval
.bv_len
= prs_offset(&ps_wire
);
1848 bval
.bv_val
= TALLOC(ctx
, bval
.bv_len
);
1850 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1851 goto ads_set_sd_error
;
1854 prs_set_offset(&ps_wire
, 0);
1856 if (!prs_copy_data_out(bval
.bv_val
, &ps_wire
, bval
.bv_len
)) {
1857 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1858 goto ads_set_sd_error
;
1861 ret
= ads_mod_ber(ctx
, &mods
, attrs
[0], &bval
);
1862 if (ADS_ERR_OK(ret
)) {
1863 ret
= ads_gen_mod(ads
, dn
, mods
);
1867 ads_msgfree(ads
, res
);
1868 prs_mem_free(&ps_wire
);
1869 talloc_destroy(ctx
);
1875 * pull the first entry from a ADS result
1876 * @param ads connection to ads server
1877 * @param res Results of search
1878 * @return first entry from result
1880 void *ads_first_entry(ADS_STRUCT
*ads
, void *res
)
1882 return (void *)ldap_first_entry(ads
->ld
, (LDAPMessage
*)res
);
1886 * pull the next entry from a ADS result
1887 * @param ads connection to ads server
1888 * @param res Results of search
1889 * @return next entry from result
1891 void *ads_next_entry(ADS_STRUCT
*ads
, void *res
)
1893 return (void *)ldap_next_entry(ads
->ld
, (LDAPMessage
*)res
);
1897 * pull a single string from a ADS result
1898 * @param ads connection to ads server
1899 * @param mem_ctx TALLOC_CTX to use for allocating result string
1900 * @param msg Results of search
1901 * @param field Attribute to retrieve
1902 * @return Result string in talloc context
1904 char *ads_pull_string(ADS_STRUCT
*ads
,
1905 TALLOC_CTX
*mem_ctx
, void *msg
, const char *field
)
1912 values
= ldap_get_values(ads
->ld
, msg
, field
);
1917 rc
= pull_utf8_talloc(mem_ctx
, &ux_string
,
1919 if (rc
!= (size_t)-1)
1923 ldap_value_free(values
);
1928 * pull an array of strings from a ADS result
1929 * @param ads connection to ads server
1930 * @param mem_ctx TALLOC_CTX to use for allocating result string
1931 * @param msg Results of search
1932 * @param field Attribute to retrieve
1933 * @return Result strings in talloc context
1935 char **ads_pull_strings(ADS_STRUCT
*ads
,
1936 TALLOC_CTX
*mem_ctx
, void *msg
, const char *field
,
1943 values
= ldap_get_values(ads
->ld
, msg
, field
);
1947 *num_values
= ldap_count_values(values
);
1949 ret
= TALLOC_ARRAY(mem_ctx
, char *, *num_values
+ 1);
1951 ldap_value_free(values
);
1955 for (i
=0;i
<*num_values
;i
++) {
1956 if (pull_utf8_talloc(mem_ctx
, &ret
[i
], values
[i
]) == -1) {
1957 ldap_value_free(values
);
1963 ldap_value_free(values
);
1968 * pull an array of strings from a ADS result
1969 * (handle large multivalue attributes with range retrieval)
1970 * @param ads connection to ads server
1971 * @param mem_ctx TALLOC_CTX to use for allocating result string
1972 * @param msg Results of search
1973 * @param field Attribute to retrieve
1974 * @param current_strings strings returned by a previous call to this function
1975 * @param next_attribute The next query should ask for this attribute
1976 * @param num_values How many values did we get this time?
1977 * @param more_values Are there more values to get?
1978 * @return Result strings in talloc context
1980 char **ads_pull_strings_range(ADS_STRUCT
*ads
,
1981 TALLOC_CTX
*mem_ctx
,
1982 void *msg
, const char *field
,
1983 char **current_strings
,
1984 const char **next_attribute
,
1985 size_t *num_strings
,
1989 char *expected_range_attrib
, *range_attr
;
1990 BerElement
*ptr
= NULL
;
1993 size_t num_new_strings
;
1994 unsigned long int range_start
;
1995 unsigned long int range_end
;
1997 /* we might have been given the whole lot anyway */
1998 if ((strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
, num_strings
))) {
1999 *more_strings
= False
;
2003 expected_range_attrib
= talloc_asprintf(mem_ctx
, "%s;Range=", field
);
2005 /* look for Range result */
2006 for (attr
= ldap_first_attribute(ads
->ld
, (LDAPMessage
*)msg
, &ptr
);
2008 attr
= ldap_next_attribute(ads
->ld
, (LDAPMessage
*)msg
, ptr
)) {
2009 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2010 if (strnequal(attr
, expected_range_attrib
, strlen(expected_range_attrib
))) {
2018 /* nothing here - this field is just empty */
2019 *more_strings
= False
;
2023 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-%lu",
2024 &range_start
, &range_end
) == 2) {
2025 *more_strings
= True
;
2027 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-*",
2028 &range_start
) == 1) {
2029 *more_strings
= False
;
2031 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2033 ldap_memfree(range_attr
);
2034 *more_strings
= False
;
2039 if ((*num_strings
) != range_start
) {
2040 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2041 " - aborting range retreival\n",
2042 range_attr
, (unsigned int)(*num_strings
) + 1, range_start
));
2043 ldap_memfree(range_attr
);
2044 *more_strings
= False
;
2048 new_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, range_attr
, &num_new_strings
);
2050 if (*more_strings
&& ((*num_strings
+ num_new_strings
) != (range_end
+ 1))) {
2051 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2052 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2053 range_attr
, (unsigned long int)range_end
- range_start
+ 1,
2054 (unsigned long int)num_new_strings
));
2055 ldap_memfree(range_attr
);
2056 *more_strings
= False
;
2060 strings
= TALLOC_REALLOC_ARRAY(mem_ctx
, current_strings
, char *,
2061 *num_strings
+ num_new_strings
);
2063 if (strings
== NULL
) {
2064 ldap_memfree(range_attr
);
2065 *more_strings
= False
;
2069 memcpy(&strings
[*num_strings
], new_strings
,
2070 sizeof(*new_strings
) * num_new_strings
);
2072 (*num_strings
) += num_new_strings
;
2074 if (*more_strings
) {
2075 *next_attribute
= talloc_asprintf(mem_ctx
,
2080 if (!*next_attribute
) {
2081 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2082 ldap_memfree(range_attr
);
2083 *more_strings
= False
;
2088 ldap_memfree(range_attr
);
2094 * pull a single uint32 from a ADS result
2095 * @param ads connection to ads server
2096 * @param msg Results of search
2097 * @param field Attribute to retrieve
2098 * @param v Pointer to int to store result
2099 * @return boolean inidicating success
2101 BOOL
ads_pull_uint32(ADS_STRUCT
*ads
,
2102 void *msg
, const char *field
, uint32
*v
)
2106 values
= ldap_get_values(ads
->ld
, msg
, field
);
2110 ldap_value_free(values
);
2114 *v
= atoi(values
[0]);
2115 ldap_value_free(values
);
2120 * pull a single objectGUID from an ADS result
2121 * @param ads connection to ADS server
2122 * @param msg results of search
2123 * @param guid 37-byte area to receive text guid
2124 * @return boolean indicating success
2126 BOOL
ads_pull_guid(ADS_STRUCT
*ads
,
2127 void *msg
, struct uuid
*guid
)
2130 UUID_FLAT flat_guid
;
2132 values
= ldap_get_values(ads
->ld
, msg
, "objectGUID");
2137 memcpy(&flat_guid
.info
, values
[0], sizeof(UUID_FLAT
));
2138 smb_uuid_unpack(flat_guid
, guid
);
2139 ldap_value_free(values
);
2142 ldap_value_free(values
);
2149 * pull a single DOM_SID from a ADS result
2150 * @param ads connection to ads server
2151 * @param msg Results of search
2152 * @param field Attribute to retrieve
2153 * @param sid Pointer to sid to store result
2154 * @return boolean inidicating success
2156 BOOL
ads_pull_sid(ADS_STRUCT
*ads
,
2157 void *msg
, const char *field
, DOM_SID
*sid
)
2159 struct berval
**values
;
2162 values
= ldap_get_values_len(ads
->ld
, msg
, field
);
2168 ret
= sid_parse(values
[0]->bv_val
, values
[0]->bv_len
, sid
);
2170 ldap_value_free_len(values
);
2175 * pull an array of DOM_SIDs from a ADS result
2176 * @param ads connection to ads server
2177 * @param mem_ctx TALLOC_CTX for allocating sid array
2178 * @param msg Results of search
2179 * @param field Attribute to retrieve
2180 * @param sids pointer to sid array to allocate
2181 * @return the count of SIDs pulled
2183 int ads_pull_sids(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2184 void *msg
, const char *field
, DOM_SID
**sids
)
2186 struct berval
**values
;
2190 values
= ldap_get_values_len(ads
->ld
, msg
, field
);
2195 for (i
=0; values
[i
]; i
++)
2198 (*sids
) = TALLOC_ARRAY(mem_ctx
, DOM_SID
, i
);
2200 ldap_value_free_len(values
);
2205 for (i
=0; values
[i
]; i
++) {
2206 ret
= sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &(*sids
)[count
]);
2209 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid
, &(*sids
)[count
])));
2214 ldap_value_free_len(values
);
2219 * pull a SEC_DESC from a ADS result
2220 * @param ads connection to ads server
2221 * @param mem_ctx TALLOC_CTX for allocating sid array
2222 * @param msg Results of search
2223 * @param field Attribute to retrieve
2224 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2225 * @return boolean inidicating success
2227 BOOL
ads_pull_sd(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2228 void *msg
, const char *field
, SEC_DESC
**sd
)
2230 struct berval
**values
;
2234 values
= ldap_get_values_len(ads
->ld
, msg
, field
);
2236 if (!values
) return False
;
2239 prs_init(&ps
, values
[0]->bv_len
, mem_ctx
, UNMARSHALL
);
2240 prs_copy_data_in(&ps
, values
[0]->bv_val
, values
[0]->bv_len
);
2241 prs_set_offset(&ps
,0);
2243 ret
= sec_io_desc("sd", sd
, &ps
, 1);
2246 ldap_value_free_len(values
);
2251 * in order to support usernames longer than 21 characters we need to
2252 * use both the sAMAccountName and the userPrincipalName attributes
2253 * It seems that not all users have the userPrincipalName attribute set
2255 * @param ads connection to ads server
2256 * @param mem_ctx TALLOC_CTX for allocating sid array
2257 * @param msg Results of search
2258 * @return the username
2260 char *ads_pull_username(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, void *msg
)
2265 /* lookup_name() only works on the sAMAccountName to
2266 returning the username portion of userPrincipalName
2267 breaks winbindd_getpwnam() */
2269 ret
= ads_pull_string(ads
, mem_ctx
, msg
, "userPrincipalName");
2270 if (ret
&& (p
= strchr_m(ret
, '@'))) {
2275 return ads_pull_string(ads
, mem_ctx
, msg
, "sAMAccountName");
2280 * find the update serial number - this is the core of the ldap cache
2281 * @param ads connection to ads server
2282 * @param ads connection to ADS server
2283 * @param usn Pointer to retrieved update serial number
2284 * @return status of search
2286 ADS_STATUS
ads_USN(ADS_STRUCT
*ads
, uint32
*usn
)
2288 const char *attrs
[] = {"highestCommittedUSN", NULL
};
2292 status
= ads_do_search_retry(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2293 if (!ADS_ERR_OK(status
))
2296 if (ads_count_replies(ads
, res
) != 1) {
2297 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2300 ads_pull_uint32(ads
, res
, "highestCommittedUSN", usn
);
2301 ads_msgfree(ads
, res
);
2305 /* parse a ADS timestring - typical string is
2306 '20020917091222.0Z0' which means 09:12.22 17th September
2308 static time_t ads_parse_time(const char *str
)
2314 if (sscanf(str
, "%4d%2d%2d%2d%2d%2d",
2315 &tm
.tm_year
, &tm
.tm_mon
, &tm
.tm_mday
,
2316 &tm
.tm_hour
, &tm
.tm_min
, &tm
.tm_sec
) != 6) {
2326 const char *ads_get_attrname_by_oid(ADS_STRUCT
*ads
, const char *schema_path
, TALLOC_CTX
*mem_ctx
, const char * OID
)
2332 const char *attrs
[] = { "lDAPDisplayName", NULL
};
2335 if (ads
== NULL
|| mem_ctx
== NULL
|| OID
== NULL
) {
2339 expr
= talloc_asprintf(mem_ctx
, "(attributeId=%s)", OID
);
2344 rc
= ads_do_search_retry(ads
, schema_path
, LDAP_SCOPE_SUBTREE
,
2346 if (!ADS_ERR_OK(rc
)) {
2350 count
= ads_count_replies(ads
, res
);
2351 if (count
== 0 || !res
) {
2355 result
= ads_pull_string(ads
, mem_ctx
, res
, "lDAPDisplayName");
2356 ads_msgfree(ads
, res
);
2361 DEBUG(0,("ads_get_attrname_by_oid: failed to retrieve name for oid: %s\n",
2364 ads_msgfree(ads
, res
);
2369 * Find the servers name and realm - this can be done before authentication
2370 * The ldapServiceName field on w2k looks like this:
2371 * vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
2372 * @param ads connection to ads server
2373 * @return status of search
2375 ADS_STATUS
ads_current_time(ADS_STRUCT
*ads
)
2377 const char *attrs
[] = {"currentTime", NULL
};
2382 ADS_STRUCT
*ads_s
= ads
;
2384 if (!(ctx
= talloc_init("ads_server_info"))) {
2385 return ADS_ERROR(LDAP_NO_MEMORY
);
2388 /* establish a new ldap tcp session if necessary */
2391 if ( (ads_s
= ads_init( ads
->server
.realm
, ads
->server
.workgroup
,
2392 ads
->server
.ldap_server
)) == NULL
)
2396 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
2397 status
= ads_connect( ads_s
);
2398 if ( !ADS_ERR_OK(status
))
2402 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2403 if (!ADS_ERR_OK(status
)) {
2404 talloc_destroy(ctx
);
2408 timestr
= ads_pull_string(ads_s
, ctx
, res
, "currentTime");
2410 ads_msgfree(ads
, res
);
2411 talloc_destroy(ctx
);
2412 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2416 /* but save the time and offset in the original ADS_STRUCT */
2418 ads
->config
.current_time
= ads_parse_time(timestr
);
2420 if (ads
->config
.current_time
!= 0) {
2421 ads
->auth
.time_offset
= ads
->config
.current_time
- time(NULL
);
2422 DEBUG(4,("time offset is %d seconds\n", ads
->auth
.time_offset
));
2425 status
= ADS_SUCCESS
;
2428 /* free any temporary ads connections */
2429 if ( ads_s
!= ads
) {
2430 ads_destroy( &ads_s
);
2432 talloc_destroy(ctx
);
2437 /*********************************************************************
2438 *********************************************************************/
2440 static ADS_STATUS
ads_schema_path(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, char **schema_path
)
2445 const char *attrs
[] = { "schemaNamingContext", NULL
};
2447 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2448 if (!ADS_ERR_OK(status
)) {
2452 if ( (schema
= ads_pull_string(ads
, mem_ctx
, res
, "schemaNamingContext")) == NULL
) {
2453 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2456 if ( (*schema_path
= talloc_strdup(mem_ctx
, schema
)) == NULL
) {
2457 return ADS_ERROR(LDAP_NO_MEMORY
);
2460 ads_msgfree(ads
, res
);
2466 * Check for "Services for Unix"-Schema and load some attributes into the ADS_STRUCT
2467 * @param ads connection to ads server
2468 * @return BOOL status of search (False if one or more attributes couldn't be
2469 * found in Active Directory)
2471 BOOL
ads_check_sfu_mapping(ADS_STRUCT
*ads
)
2474 TALLOC_CTX
*ctx
= NULL
;
2475 const char *gidnumber
, *uidnumber
, *homedir
, *shell
, *gecos
;
2476 char *schema_path
= NULL
;
2477 ADS_STRUCT
*ads_s
= ads
;
2480 if ( (ctx
= talloc_init("ads_check_sfu_mapping")) == NULL
) {
2484 /* establish a new ldap tcp session if necessary */
2487 if ( (ads_s
= ads_init( ads
->server
.realm
, ads
->server
.workgroup
,
2488 ads
->server
.ldap_server
)) == NULL
)
2493 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
2494 status
= ads_connect( ads_s
);
2495 if ( !ADS_ERR_OK(status
))
2499 status
= ads_schema_path( ads
, ctx
, &schema_path
);
2500 if ( !ADS_ERR_OK(status
) ) {
2501 DEBUG(3,("ads_check_sfu_mapping: Unable to retrieve schema DN!\n"));
2505 gidnumber
= ads_get_attrname_by_oid(ads_s
, schema_path
, ctx
, ADS_ATTR_SFU_GIDNUMBER_OID
);
2506 if (gidnumber
== NULL
)
2508 ads
->schema
.sfu_gidnumber_attr
= SMB_STRDUP(gidnumber
);
2510 uidnumber
= ads_get_attrname_by_oid(ads_s
, schema_path
, ctx
, ADS_ATTR_SFU_UIDNUMBER_OID
);
2511 if (uidnumber
== NULL
)
2513 ads
->schema
.sfu_uidnumber_attr
= SMB_STRDUP(uidnumber
);
2515 homedir
= ads_get_attrname_by_oid(ads_s
, schema_path
, ctx
, ADS_ATTR_SFU_HOMEDIR_OID
);
2516 if (homedir
== NULL
)
2518 ads
->schema
.sfu_homedir_attr
= SMB_STRDUP(homedir
);
2520 shell
= ads_get_attrname_by_oid(ads_s
, schema_path
, ctx
, ADS_ATTR_SFU_SHELL_OID
);
2523 ads
->schema
.sfu_shell_attr
= SMB_STRDUP(shell
);
2525 gecos
= ads_get_attrname_by_oid(ads_s
, schema_path
, ctx
, ADS_ATTR_SFU_GECOS_OID
);
2528 ads
->schema
.sfu_gecos_attr
= SMB_STRDUP(gecos
);
2532 /* free any temporary ads connections */
2533 if ( ads_s
!= ads
) {
2534 ads_destroy( &ads_s
);
2538 talloc_destroy(ctx
);
2545 * find the domain sid for our domain
2546 * @param ads connection to ads server
2547 * @param sid Pointer to domain sid
2548 * @return status of search
2550 ADS_STATUS
ads_domain_sid(ADS_STRUCT
*ads
, DOM_SID
*sid
)
2552 const char *attrs
[] = {"objectSid", NULL
};
2556 rc
= ads_do_search_retry(ads
, ads
->config
.bind_path
, LDAP_SCOPE_BASE
, "(objectclass=*)",
2558 if (!ADS_ERR_OK(rc
)) return rc
;
2559 if (!ads_pull_sid(ads
, res
, "objectSid", sid
)) {
2560 ads_msgfree(ads
, res
);
2561 return ADS_ERROR_SYSTEM(ENOENT
);
2563 ads_msgfree(ads
, res
);
2569 * find our site name
2570 * @param ads connection to ads server
2571 * @param mem_ctx Pointer to talloc context
2572 * @param site_name Pointer to the sitename
2573 * @return status of search
2575 ADS_STATUS
ads_site_dn(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char **site_name
)
2579 const char *dn
, *service_name
;
2580 const char *attrs
[] = { "dsServiceName", NULL
};
2582 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2583 if (!ADS_ERR_OK(status
)) {
2587 service_name
= ads_pull_string(ads
, mem_ctx
, res
, "dsServiceName");
2588 if (service_name
== NULL
) {
2589 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2592 /* go up three levels */
2593 dn
= ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name
)));
2595 return ADS_ERROR(LDAP_NO_MEMORY
);
2598 *site_name
= talloc_strdup(mem_ctx
, dn
);
2599 if (*site_name
== NULL
) {
2600 return ADS_ERROR(LDAP_NO_MEMORY
);
2603 ads_msgfree(ads
, res
);
2607 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2612 * find the site dn where a machine resides
2613 * @param ads connection to ads server
2614 * @param mem_ctx Pointer to talloc context
2615 * @param computer_name name of the machine
2616 * @param site_name Pointer to the sitename
2617 * @return status of search
2619 ADS_STATUS
ads_site_dn_for_machine(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char *computer_name
, const char **site_dn
)
2623 const char *parent
, *config_context
, *filter
;
2624 const char *attrs
[] = { "configurationNamingContext", NULL
};
2627 /* shortcut a query */
2628 if (strequal(computer_name
, ads
->config
.ldap_server_name
)) {
2629 return ads_site_dn(ads
, mem_ctx
, site_dn
);
2632 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2633 if (!ADS_ERR_OK(status
)) {
2637 config_context
= ads_pull_string(ads
, mem_ctx
, res
, "configurationNamingContext");
2638 if (config_context
== NULL
) {
2639 return ADS_ERROR(LDAP_NO_MEMORY
);
2642 filter
= talloc_asprintf(mem_ctx
, "(cn=%s)", computer_name
);
2643 if (filter
== NULL
) {
2644 return ADS_ERROR(LDAP_NO_MEMORY
);
2647 status
= ads_do_search(ads
, config_context
, LDAP_SCOPE_SUBTREE
, filter
, NULL
, &res
);
2648 if (!ADS_ERR_OK(status
)) {
2652 if (ads_count_replies(ads
, res
) != 1) {
2653 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2656 dn
= ads_get_dn(ads
, res
);
2658 return ADS_ERROR(LDAP_NO_MEMORY
);
2661 /* go up three levels */
2662 parent
= ads_parent_dn(ads_parent_dn(ads_parent_dn(dn
)));
2663 if (parent
== NULL
) {
2664 ads_memfree(ads
, dn
);
2665 return ADS_ERROR(LDAP_NO_MEMORY
);
2668 *site_dn
= talloc_strdup(mem_ctx
, parent
);
2669 if (*site_dn
== NULL
) {
2670 ads_memfree(ads
, dn
);
2671 ADS_ERROR(LDAP_NO_MEMORY
);
2674 ads_memfree(ads
, dn
);
2675 ads_msgfree(ads
, res
);
2681 * get the upn suffixes for a domain
2682 * @param ads connection to ads server
2683 * @param mem_ctx Pointer to talloc context
2684 * @param suffixes Pointer to an array of suffixes
2685 * @param site_name Pointer to the number of suffixes
2686 * @return status of search
2688 ADS_STATUS
ads_upn_suffixes(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, char **suffixes
, size_t *num_suffixes
)
2692 const char *config_context
, *base
;
2693 const char *attrs
[] = { "configurationNamingContext", NULL
};
2694 const char *attrs2
[] = { "uPNSuffixes", NULL
};
2696 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2697 if (!ADS_ERR_OK(status
)) {
2701 config_context
= ads_pull_string(ads
, mem_ctx
, res
, "configurationNamingContext");
2702 if (config_context
== NULL
) {
2703 return ADS_ERROR(LDAP_NO_MEMORY
);
2706 base
= talloc_asprintf(mem_ctx
, "cn=Partitions,%s", config_context
);
2708 return ADS_ERROR(LDAP_NO_MEMORY
);
2711 status
= ads_search_dn(ads
, &res
, base
, attrs2
);
2712 if (!ADS_ERR_OK(status
)) {
2716 if (ads_count_replies(ads
, res
) != 1) {
2717 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2720 suffixes
= ads_pull_strings(ads
, mem_ctx
, &res
, "uPNSuffixes", num_suffixes
);
2721 if (suffixes
== NULL
) {
2722 ads_msgfree(ads
, res
);
2723 return ADS_ERROR(LDAP_NO_MEMORY
);
2726 ads_msgfree(ads
, res
);