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 TODO : add a negative connection cache in here leveraged off of the one
117 found in the rpc code. --jerry
119 BOOL
ads_try_connect(ADS_STRUCT
*ads
, const char *server
, unsigned port
)
123 if (!server
|| !*server
) {
127 DEBUG(5,("ads_try_connect: trying ldap server '%s' port %u\n", server
, port
));
129 /* this copes with inet_ntoa brokenness */
130 srv
= SMB_STRDUP(server
);
132 ads
->ld
= ldap_open_with_timeout(srv
, port
, lp_ldap_timeout());
137 ads
->ldap_port
= port
;
138 ads
->ldap_ip
= *interpret_addr2(srv
);
141 /* cache the successful connection */
143 saf_store( ads
->server
.workgroup
, server
);
149 try a connection to a given ldap server, based on URL, returning True if successful
151 static BOOL
ads_try_connect_uri(ADS_STRUCT
*ads
)
153 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
154 DEBUG(5,("ads_try_connect: trying ldap server at URI '%s'\n",
155 ads
->server
.ldap_uri
));
158 if (ldap_initialize((LDAP
**)&(ads
->ld
), ads
->server
.ldap_uri
) == LDAP_SUCCESS
) {
161 DEBUG(0, ("ldap_initialize: %s\n", strerror(errno
)));
165 DEBUG(1, ("no URL support in LDAP libs!\n"));
171 /**********************************************************************
172 Try to find an AD dc using our internal name resolution routines
173 Try the realm first and then then workgroup name if netbios is not
175 **********************************************************************/
177 static BOOL
ads_find_dc(ADS_STRUCT
*ads
)
181 struct ip_service
*ip_list
;
183 BOOL got_realm
= False
;
184 BOOL use_own_domain
= False
;
186 /* if the realm and workgroup are both empty, assume they are ours */
189 c_realm
= ads
->server
.realm
;
191 if ( !c_realm
|| !*c_realm
) {
192 /* special case where no realm and no workgroup means our own */
193 if ( !ads
->server
.workgroup
|| !*ads
->server
.workgroup
) {
194 use_own_domain
= True
;
195 c_realm
= lp_realm();
199 if (c_realm
&& *c_realm
)
203 /* we need to try once with the realm name and fallback to the
204 netbios domain name if we fail (if netbios has not been disabled */
206 if ( !got_realm
&& !lp_disable_netbios() ) {
207 c_realm
= ads
->server
.workgroup
;
208 if (!c_realm
|| !*c_realm
) {
209 if ( use_own_domain
)
210 c_realm
= lp_workgroup();
213 if ( !c_realm
|| !*c_realm
) {
214 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
219 pstrcpy( realm
, c_realm
);
221 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
222 (got_realm
? "realm" : "domain"), realm
));
224 if ( !get_sorted_dc_list(realm
, &ip_list
, &count
, got_realm
) ) {
225 /* fall back to netbios if we can */
226 if ( got_realm
&& !lp_disable_netbios() ) {
234 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
235 for ( i
=0; i
<count
; i
++ ) {
236 /* since this is an ads conection request, default to LDAP_PORT is not set */
237 int port
= (ip_list
[i
].port
!=PORT_NONE
) ? ip_list
[i
].port
: LDAP_PORT
;
240 fstrcpy( server
, inet_ntoa(ip_list
[i
].ip
) );
242 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm
, server
)) )
245 if ( ads_try_connect(ads
, server
, port
) ) {
250 /* keep track of failures */
251 add_failed_connection_entry( realm
, server
, NT_STATUS_UNSUCCESSFUL
);
261 * Connect to the LDAP server
262 * @param ads Pointer to an existing ADS_STRUCT
263 * @return status of connection
265 ADS_STATUS
ads_connect(ADS_STRUCT
*ads
)
267 int version
= LDAP_VERSION3
;
270 ads
->last_attempt
= time(NULL
);
273 /* try with a URL based server */
275 if (ads
->server
.ldap_uri
&&
276 ads_try_connect_uri(ads
)) {
280 /* try with a user specified server */
281 if (ads
->server
.ldap_server
&&
282 ads_try_connect(ads
, ads
->server
.ldap_server
, LDAP_PORT
)) {
286 if (ads_find_dc(ads
)) {
290 return ADS_ERROR_SYSTEM(errno
?errno
:ENOENT
);
293 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads
->ldap_ip
)));
295 status
= ads_server_info(ads
);
296 if (!ADS_ERR_OK(status
)) {
297 DEBUG(1,("Failed to get ldap server info\n"));
301 ldap_set_option(ads
->ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
303 status
= ADS_ERROR(smb_ldap_start_tls(ads
->ld
, version
));
304 if (!ADS_ERR_OK(status
)) {
308 if (!ads
->auth
.user_name
) {
309 /* have to use the userPrincipalName value here and
310 not servicePrincipalName; found by Guenther Deschner @ Sernet */
312 asprintf(&ads
->auth
.user_name
, "host/%s", global_myname() );
315 if (!ads
->auth
.realm
) {
316 ads
->auth
.realm
= SMB_STRDUP(ads
->config
.realm
);
319 if (!ads
->auth
.kdc_server
) {
320 ads
->auth
.kdc_server
= SMB_STRDUP(inet_ntoa(ads
->ldap_ip
));
324 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
325 to MIT kerberos to work (tridge) */
328 asprintf(&env
, "KRB5_KDC_ADDRESS_%s", ads
->config
.realm
);
329 setenv(env
, ads
->auth
.kdc_server
, 1);
334 if (ads
->auth
.flags
& ADS_AUTH_NO_BIND
) {
338 if (ads
->auth
.flags
& ADS_AUTH_ANON_BIND
) {
339 return ADS_ERROR(ldap_simple_bind_s( ads
->ld
, NULL
, NULL
));
342 if (ads
->auth
.flags
& ADS_AUTH_SIMPLE_BIND
) {
343 return ADS_ERROR(ldap_simple_bind_s( ads
->ld
, ads
->auth
.user_name
, ads
->auth
.password
));
346 return ads_sasl_bind(ads
);
350 Duplicate a struct berval into talloc'ed memory
352 static struct berval
*dup_berval(TALLOC_CTX
*ctx
, const struct berval
*in_val
)
354 struct berval
*value
;
356 if (!in_val
) return NULL
;
358 value
= TALLOC_ZERO_P(ctx
, struct berval
);
361 if (in_val
->bv_len
== 0) return value
;
363 value
->bv_len
= in_val
->bv_len
;
364 value
->bv_val
= TALLOC_MEMDUP(ctx
, in_val
->bv_val
, in_val
->bv_len
);
369 Make a values list out of an array of (struct berval *)
371 static struct berval
**ads_dup_values(TALLOC_CTX
*ctx
,
372 const struct berval
**in_vals
)
374 struct berval
**values
;
377 if (!in_vals
) return NULL
;
378 for (i
=0; in_vals
[i
]; i
++)
380 values
= TALLOC_ZERO_ARRAY(ctx
, struct berval
*, i
+1);
381 if (!values
) return NULL
;
383 for (i
=0; in_vals
[i
]; i
++) {
384 values
[i
] = dup_berval(ctx
, in_vals
[i
]);
390 UTF8-encode a values list out of an array of (char *)
392 static char **ads_push_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
397 if (!in_vals
) return NULL
;
398 for (i
=0; in_vals
[i
]; i
++)
400 values
= TALLOC_ZERO_ARRAY(ctx
, char *, i
+1);
401 if (!values
) return NULL
;
403 for (i
=0; in_vals
[i
]; i
++) {
404 push_utf8_talloc(ctx
, &values
[i
], in_vals
[i
]);
410 Pull a (char *) array out of a UTF8-encoded values list
412 static char **ads_pull_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
417 if (!in_vals
) return NULL
;
418 for (i
=0; in_vals
[i
]; i
++)
420 values
= TALLOC_ZERO_ARRAY(ctx
, char *, i
+1);
421 if (!values
) return NULL
;
423 for (i
=0; in_vals
[i
]; i
++) {
424 pull_utf8_talloc(ctx
, &values
[i
], in_vals
[i
]);
430 * Do a search with paged results. cookie must be null on the first
431 * call, and then returned on each subsequent call. It will be null
432 * again when the entire search is complete
433 * @param ads connection to ads server
434 * @param bind_path Base dn for the search
435 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
436 * @param expr Search expression - specified in local charset
437 * @param attrs Attributes to retrieve - specified in utf8 or ascii
438 * @param res ** which will contain results - free res* with ads_msgfree()
439 * @param count Number of entries retrieved on this page
440 * @param cookie The paged results cookie to be returned on subsequent calls
441 * @return status of search
443 ADS_STATUS
ads_do_paged_search(ADS_STRUCT
*ads
, const char *bind_path
,
444 int scope
, const char *expr
,
445 const char **attrs
, void **res
,
446 int *count
, void **cookie
)
449 char *utf8_expr
, *utf8_path
, **search_attrs
;
450 LDAPControl PagedResults
, NoReferrals
, *controls
[3], **rcontrols
;
451 BerElement
*cookie_be
= NULL
;
452 struct berval
*cookie_bv
= NULL
;
457 if (!(ctx
= talloc_init("ads_do_paged_search")))
458 return ADS_ERROR(LDAP_NO_MEMORY
);
460 /* 0 means the conversion worked but the result was empty
461 so we only fail if it's -1. In any case, it always
462 at least nulls out the dest */
463 if ((push_utf8_talloc(ctx
, &utf8_expr
, expr
) == (size_t)-1) ||
464 (push_utf8_talloc(ctx
, &utf8_path
, bind_path
) == (size_t)-1)) {
469 if (!attrs
|| !(*attrs
))
472 /* This would be the utf8-encoded version...*/
473 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
474 if (!(str_list_copy(&search_attrs
, attrs
))) {
481 /* Paged results only available on ldap v3 or later */
482 ldap_get_option(ads
->ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
483 if (version
< LDAP_VERSION3
) {
484 rc
= LDAP_NOT_SUPPORTED
;
488 cookie_be
= ber_alloc_t(LBER_USE_DER
);
489 if (cookie
&& *cookie
) {
490 ber_printf(cookie_be
, "{iO}", (ber_int_t
) 1000, *cookie
);
491 ber_bvfree(*cookie
); /* don't need it from last time */
494 ber_printf(cookie_be
, "{io}", (ber_int_t
) 1000, "", 0);
496 ber_flatten(cookie_be
, &cookie_bv
);
497 PagedResults
.ldctl_oid
= CONST_DISCARD(char *, ADS_PAGE_CTL_OID
);
498 PagedResults
.ldctl_iscritical
= (char) 1;
499 PagedResults
.ldctl_value
.bv_len
= cookie_bv
->bv_len
;
500 PagedResults
.ldctl_value
.bv_val
= cookie_bv
->bv_val
;
502 NoReferrals
.ldctl_oid
= CONST_DISCARD(char *, ADS_NO_REFERRALS_OID
);
503 NoReferrals
.ldctl_iscritical
= (char) 0;
504 NoReferrals
.ldctl_value
.bv_len
= 0;
505 NoReferrals
.ldctl_value
.bv_val
= CONST_DISCARD(char *, "");
508 controls
[0] = &NoReferrals
;
509 controls
[1] = &PagedResults
;
512 /* we need to disable referrals as the openldap libs don't
513 handle them and paged results at the same time. Using them
514 together results in the result record containing the server
515 page control being removed from the result list (tridge/jmcd)
517 leaving this in despite the control that says don't generate
518 referrals, in case the server doesn't support it (jmcd)
520 ldap_set_option(ads
->ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
522 rc
= ldap_search_with_timeout(ads
->ld
, utf8_path
, scope
, utf8_expr
,
523 search_attrs
, 0, controls
,
525 (LDAPMessage
**)res
);
527 ber_free(cookie_be
, 1);
528 ber_bvfree(cookie_bv
);
531 DEBUG(3,("ads_do_paged_search: ldap_search_with_timeout(%s) -> %s\n", expr
,
532 ldap_err2string(rc
)));
536 rc
= ldap_parse_result(ads
->ld
, *res
, NULL
, NULL
, NULL
,
537 NULL
, &rcontrols
, 0);
543 for (i
=0; rcontrols
[i
]; i
++) {
544 if (strcmp(ADS_PAGE_CTL_OID
, rcontrols
[i
]->ldctl_oid
) == 0) {
545 cookie_be
= ber_init(&rcontrols
[i
]->ldctl_value
);
546 ber_scanf(cookie_be
,"{iO}", (ber_int_t
*) count
,
548 /* the berval is the cookie, but must be freed when
550 if (cookie_bv
->bv_len
) /* still more to do */
551 *cookie
=ber_bvdup(cookie_bv
);
554 ber_bvfree(cookie_bv
);
555 ber_free(cookie_be
, 1);
559 ldap_controls_free(rcontrols
);
563 /* if/when we decide to utf8-encode attrs, take out this next line */
564 str_list_free(&search_attrs
);
566 return ADS_ERROR(rc
);
571 * Get all results for a search. This uses ads_do_paged_search() to return
572 * all entries in a large search.
573 * @param ads connection to ads server
574 * @param bind_path Base dn for the search
575 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
576 * @param expr Search expression
577 * @param attrs Attributes to retrieve
578 * @param res ** which will contain results - free res* with ads_msgfree()
579 * @return status of search
581 ADS_STATUS
ads_do_search_all(ADS_STRUCT
*ads
, const char *bind_path
,
582 int scope
, const char *expr
,
583 const char **attrs
, void **res
)
590 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
, res
,
593 if (!ADS_ERR_OK(status
))
596 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
600 LDAPMessage
*msg
, *next
;
602 status2
= ads_do_paged_search(ads
, bind_path
, scope
, expr
,
603 attrs
, &res2
, &count
, &cookie
);
605 if (!ADS_ERR_OK(status2
)) break;
607 /* this relies on the way that ldap_add_result_entry() works internally. I hope
608 that this works on all ldap libs, but I have only tested with openldap */
609 for (msg
= ads_first_entry(ads
, res2
); msg
; msg
= next
) {
610 next
= ads_next_entry(ads
, msg
);
611 ldap_add_result_entry((LDAPMessage
**)res
, msg
);
613 /* note that we do not free res2, as the memory is now
614 part of the main returned list */
617 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
618 status
= ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL
);
625 * Run a function on all results for a search. Uses ads_do_paged_search() and
626 * runs the function as each page is returned, using ads_process_results()
627 * @param ads connection to ads server
628 * @param bind_path Base dn for the search
629 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
630 * @param expr Search expression - specified in local charset
631 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
632 * @param fn Function which takes attr name, values list, and data_area
633 * @param data_area Pointer which is passed to function on each call
634 * @return status of search
636 ADS_STATUS
ads_do_search_all_fn(ADS_STRUCT
*ads
, const char *bind_path
,
637 int scope
, const char *expr
, const char **attrs
,
638 BOOL(*fn
)(char *, void **, void *),
646 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
, &res
,
649 if (!ADS_ERR_OK(status
)) return status
;
651 ads_process_results(ads
, res
, fn
, data_area
);
652 ads_msgfree(ads
, res
);
655 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
,
656 &res
, &count
, &cookie
);
658 if (!ADS_ERR_OK(status
)) break;
660 ads_process_results(ads
, res
, fn
, data_area
);
661 ads_msgfree(ads
, res
);
668 * Do a search with a timeout.
669 * @param ads connection to ads server
670 * @param bind_path Base dn for the search
671 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
672 * @param expr Search expression
673 * @param attrs Attributes to retrieve
674 * @param res ** which will contain results - free res* with ads_msgfree()
675 * @return status of search
677 ADS_STATUS
ads_do_search(ADS_STRUCT
*ads
, const char *bind_path
, int scope
,
679 const char **attrs
, void **res
)
682 char *utf8_expr
, *utf8_path
, **search_attrs
= NULL
;
686 if (!(ctx
= talloc_init("ads_do_search"))) {
687 DEBUG(1,("ads_do_search: talloc_init() failed!"));
688 return ADS_ERROR(LDAP_NO_MEMORY
);
691 /* 0 means the conversion worked but the result was empty
692 so we only fail if it's negative. In any case, it always
693 at least nulls out the dest */
694 if ((push_utf8_talloc(ctx
, &utf8_expr
, expr
) == (size_t)-1) ||
695 (push_utf8_talloc(ctx
, &utf8_path
, bind_path
) == (size_t)-1)) {
696 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
701 if (!attrs
|| !(*attrs
))
704 /* This would be the utf8-encoded version...*/
705 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
706 if (!(str_list_copy(&search_attrs
, attrs
)))
708 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
714 /* see the note in ads_do_paged_search - we *must* disable referrals */
715 ldap_set_option(ads
->ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
717 rc
= ldap_search_with_timeout(ads
->ld
, utf8_path
, scope
, utf8_expr
,
718 search_attrs
, 0, NULL
, NULL
,
720 (LDAPMessage
**)res
);
722 if (rc
== LDAP_SIZELIMIT_EXCEEDED
) {
723 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
729 /* if/when we decide to utf8-encode attrs, take out this next line */
730 str_list_free(&search_attrs
);
731 return ADS_ERROR(rc
);
734 * Do a general ADS search
735 * @param ads connection to ads server
736 * @param res ** which will contain results - free res* with ads_msgfree()
737 * @param expr Search expression
738 * @param attrs Attributes to retrieve
739 * @return status of search
741 ADS_STATUS
ads_search(ADS_STRUCT
*ads
, void **res
,
745 return ads_do_search(ads
, ads
->config
.bind_path
, LDAP_SCOPE_SUBTREE
,
750 * Do a search on a specific DistinguishedName
751 * @param ads connection to ads server
752 * @param res ** which will contain results - free res* with ads_msgfree()
753 * @param dn DistinguishName to search
754 * @param attrs Attributes to retrieve
755 * @return status of search
757 ADS_STATUS
ads_search_dn(ADS_STRUCT
*ads
, void **res
,
761 return ads_do_search(ads
, dn
, LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, res
);
765 * Free up memory from a ads_search
766 * @param ads connection to ads server
767 * @param msg Search results to free
769 void ads_msgfree(ADS_STRUCT
*ads
, void *msg
)
776 * Free up memory from various ads requests
777 * @param ads connection to ads server
778 * @param mem Area to free
780 void ads_memfree(ADS_STRUCT
*ads
, void *mem
)
786 * Get a dn from search results
787 * @param ads connection to ads server
788 * @param msg Search result
791 char *ads_get_dn(ADS_STRUCT
*ads
, void *msg
)
793 char *utf8_dn
, *unix_dn
;
795 utf8_dn
= ldap_get_dn(ads
->ld
, msg
);
798 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
802 if (pull_utf8_allocate(&unix_dn
, utf8_dn
) == (size_t)-1) {
803 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
807 ldap_memfree(utf8_dn
);
812 * Get a canonical dn from search results
813 * @param ads connection to ads server
814 * @param msg Search result
817 char *ads_get_dn_canonical(ADS_STRUCT
*ads
, void *msg
)
819 #ifdef HAVE_LDAP_DN2AD_CANONICAL
820 return ldap_dn2ad_canonical(ads_get_dn(ads
, msg
));
828 * Get the parent dn from a search result
829 * @param ads connection to ads server
830 * @param msg Search result
831 * @return parent dn string
833 char *ads_get_parent_dn(ADS_STRUCT
*ads
, void *msg
)
837 dn
= ads_get_dn(ads
, msg
);
843 ads_memfree(ads
, dn
);
845 p
= strchr(mydn
, ',');
855 * Get the parent from a dn
856 * @param dn the dn to return the parent from
857 * @return parent dn string
859 char *ads_parent_dn(const char *dn
)
861 char *p
= strchr(dn
, ',');
871 * Find a machine account given a hostname
872 * @param ads connection to ads server
873 * @param res ** which will contain results - free res* with ads_msgfree()
874 * @param host Hostname to search for
875 * @return status of search
877 ADS_STATUS
ads_find_machine_acct(ADS_STRUCT
*ads
, void **res
, const char *machine
)
881 const char *attrs
[] = {"*", "nTSecurityDescriptor", NULL
};
885 /* the easiest way to find a machine account anywhere in the tree
886 is to look for hostname$ */
887 if (asprintf(&expr
, "(samAccountName=%s$)", machine
) == -1) {
888 DEBUG(1, ("asprintf failed!\n"));
889 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
892 status
= ads_search(ads
, res
, expr
, attrs
);
898 * Initialize a list of mods to be used in a modify request
899 * @param ctx An initialized TALLOC_CTX
900 * @return allocated ADS_MODLIST
902 ADS_MODLIST
ads_init_mods(TALLOC_CTX
*ctx
)
904 #define ADS_MODLIST_ALLOC_SIZE 10
907 if ((mods
= TALLOC_ZERO_ARRAY(ctx
, LDAPMod
*, ADS_MODLIST_ALLOC_SIZE
+ 1)))
908 /* -1 is safety to make sure we don't go over the end.
909 need to reset it to NULL before doing ldap modify */
910 mods
[ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
912 return (ADS_MODLIST
)mods
;
917 add an attribute to the list, with values list already constructed
919 static ADS_STATUS
ads_modlist_add(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
920 int mod_op
, const char *name
,
924 LDAPMod
**modlist
= (LDAPMod
**) *mods
;
925 struct berval
**ber_values
= NULL
;
926 char **char_values
= NULL
;
929 mod_op
= LDAP_MOD_DELETE
;
931 if (mod_op
& LDAP_MOD_BVALUES
)
932 ber_values
= ads_dup_values(ctx
,
933 (const struct berval
**)invals
);
935 char_values
= ads_push_strvals(ctx
,
936 (const char **) invals
);
939 /* find the first empty slot */
940 for (curmod
=0; modlist
[curmod
] && modlist
[curmod
] != (LDAPMod
*) -1;
942 if (modlist
[curmod
] == (LDAPMod
*) -1) {
943 if (!(modlist
= TALLOC_REALLOC_ARRAY(ctx
, modlist
, LDAPMod
*,
944 curmod
+ADS_MODLIST_ALLOC_SIZE
+1)))
945 return ADS_ERROR(LDAP_NO_MEMORY
);
946 memset(&modlist
[curmod
], 0,
947 ADS_MODLIST_ALLOC_SIZE
*sizeof(LDAPMod
*));
948 modlist
[curmod
+ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
949 *mods
= (ADS_MODLIST
)modlist
;
952 if (!(modlist
[curmod
] = TALLOC_ZERO_P(ctx
, LDAPMod
)))
953 return ADS_ERROR(LDAP_NO_MEMORY
);
954 modlist
[curmod
]->mod_type
= talloc_strdup(ctx
, name
);
955 if (mod_op
& LDAP_MOD_BVALUES
) {
956 modlist
[curmod
]->mod_bvalues
= ber_values
;
957 } else if (mod_op
& LDAP_MOD_DELETE
) {
958 modlist
[curmod
]->mod_values
= NULL
;
960 modlist
[curmod
]->mod_values
= char_values
;
963 modlist
[curmod
]->mod_op
= mod_op
;
964 return ADS_ERROR(LDAP_SUCCESS
);
968 * Add a single string value to a mod list
969 * @param ctx An initialized TALLOC_CTX
970 * @param mods An initialized ADS_MODLIST
971 * @param name The attribute name to add
972 * @param val The value to add - NULL means DELETE
973 * @return ADS STATUS indicating success of add
975 ADS_STATUS
ads_mod_str(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
976 const char *name
, const char *val
)
978 const char *values
[2];
984 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
985 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
, name
,
986 (const void **) values
);
990 * Add an array of string values to a mod list
991 * @param ctx An initialized TALLOC_CTX
992 * @param mods An initialized ADS_MODLIST
993 * @param name The attribute name to add
994 * @param vals The array of string values to add - NULL means DELETE
995 * @return ADS STATUS indicating success of add
997 ADS_STATUS
ads_mod_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
998 const char *name
, const char **vals
)
1001 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1002 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
,
1003 name
, (const void **) vals
);
1007 * Add a single ber-encoded value to a mod list
1008 * @param ctx An initialized TALLOC_CTX
1009 * @param mods An initialized ADS_MODLIST
1010 * @param name The attribute name to add
1011 * @param val The value to add - NULL means DELETE
1012 * @return ADS STATUS indicating success of add
1014 static ADS_STATUS
ads_mod_ber(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1015 const char *name
, const struct berval
*val
)
1017 const struct berval
*values
[2];
1022 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1023 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
|LDAP_MOD_BVALUES
,
1024 name
, (const void **) values
);
1028 * Perform an ldap modify
1029 * @param ads connection to ads server
1030 * @param mod_dn DistinguishedName to modify
1031 * @param mods list of modifications to perform
1032 * @return status of modify
1034 ADS_STATUS
ads_gen_mod(ADS_STRUCT
*ads
, const char *mod_dn
, ADS_MODLIST mods
)
1037 char *utf8_dn
= NULL
;
1039 this control is needed to modify that contains a currently
1040 non-existent attribute (but allowable for the object) to run
1042 LDAPControl PermitModify
= {
1043 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID
),
1046 LDAPControl
*controls
[2];
1048 controls
[0] = &PermitModify
;
1051 if (push_utf8_allocate(&utf8_dn
, mod_dn
) == -1) {
1052 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1055 /* find the end of the list, marked by NULL or -1 */
1056 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1057 /* make sure the end of the list is NULL */
1059 ret
= ldap_modify_ext_s(ads
->ld
, utf8_dn
,
1060 (LDAPMod
**) mods
, controls
, NULL
);
1062 return ADS_ERROR(ret
);
1066 * Perform an ldap add
1067 * @param ads connection to ads server
1068 * @param new_dn DistinguishedName to add
1069 * @param mods list of attributes and values for DN
1070 * @return status of add
1072 ADS_STATUS
ads_gen_add(ADS_STRUCT
*ads
, const char *new_dn
, ADS_MODLIST mods
)
1075 char *utf8_dn
= NULL
;
1077 if (push_utf8_allocate(&utf8_dn
, new_dn
) == -1) {
1078 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1079 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1082 /* find the end of the list, marked by NULL or -1 */
1083 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1084 /* make sure the end of the list is NULL */
1087 ret
= ldap_add_s(ads
->ld
, utf8_dn
, (LDAPMod
**)mods
);
1089 return ADS_ERROR(ret
);
1093 * Delete a DistinguishedName
1094 * @param ads connection to ads server
1095 * @param new_dn DistinguishedName to delete
1096 * @return status of delete
1098 ADS_STATUS
ads_del_dn(ADS_STRUCT
*ads
, char *del_dn
)
1101 char *utf8_dn
= NULL
;
1102 if (push_utf8_allocate(&utf8_dn
, del_dn
) == -1) {
1103 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1104 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1107 ret
= ldap_delete_s(ads
->ld
, utf8_dn
);
1108 return ADS_ERROR(ret
);
1112 * Build an org unit string
1113 * if org unit is Computers or blank then assume a container, otherwise
1114 * assume a \ separated list of organisational units
1115 * @param ads connection to ads server
1116 * @param org_unit Organizational unit
1117 * @return org unit string - caller must free
1119 char *ads_ou_string(ADS_STRUCT
*ads
, const char *org_unit
)
1123 if (!org_unit
|| !*org_unit
) {
1125 ret
= ads_default_ou_string(ads
, WELL_KNOWN_GUID_COMPUTERS
);
1127 /* samba4 might not yet respond to a wellknownobject-query */
1128 return ret
? ret
: SMB_STRDUP("cn=Computers");
1131 if (strequal(org_unit
, "Computers")) {
1132 return SMB_STRDUP("cn=Computers");
1135 return ads_build_path(org_unit
, "\\/", "ou=", 1);
1139 * Get a org unit string for a well-known GUID
1140 * @param ads connection to ads server
1141 * @param wknguid Well known GUID
1142 * @return org unit string - caller must free
1144 char *ads_default_ou_string(ADS_STRUCT
*ads
, const char *wknguid
)
1148 char *base
, *wkn_dn
, *ret
, **wkn_dn_exp
, **bind_dn_exp
;
1149 const char *attrs
[] = {"distinguishedName", NULL
};
1150 int new_ln
, wkn_ln
, bind_ln
, i
;
1152 if (wknguid
== NULL
) {
1156 if (asprintf(&base
, "<WKGUID=%s,%s>", wknguid
, ads
->config
.bind_path
) == -1) {
1157 DEBUG(1, ("asprintf failed!\n"));
1161 status
= ads_search_dn(ads
, &res
, base
, attrs
);
1162 if (!ADS_ERR_OK(status
)) {
1163 DEBUG(1,("Failed while searching for: %s\n", base
));
1168 if (ads_count_replies(ads
, res
) != 1) {
1172 /* substitute the bind-path from the well-known-guid-search result */
1173 wkn_dn
= ads_get_dn(ads
, res
);
1174 wkn_dn_exp
= ldap_explode_dn(wkn_dn
, 0);
1175 bind_dn_exp
= ldap_explode_dn(ads
->config
.bind_path
, 0);
1177 for (wkn_ln
=0; wkn_dn_exp
[wkn_ln
]; wkn_ln
++)
1179 for (bind_ln
=0; bind_dn_exp
[bind_ln
]; bind_ln
++)
1182 new_ln
= wkn_ln
- bind_ln
;
1184 ret
= wkn_dn_exp
[0];
1186 for (i
=1; i
< new_ln
; i
++) {
1188 asprintf(&s
, "%s,%s", ret
, wkn_dn_exp
[i
]);
1189 ret
= SMB_STRDUP(s
);
1197 * Adds (appends) an item to an attribute array, rather then
1198 * replacing the whole list
1199 * @param ctx An initialized TALLOC_CTX
1200 * @param mods An initialized ADS_MODLIST
1201 * @param name name of the ldap attribute to append to
1202 * @param vals an array of values to add
1203 * @return status of addition
1206 ADS_STATUS
ads_add_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1207 const char *name
, const char **vals
)
1209 return ads_modlist_add(ctx
, mods
, LDAP_MOD_ADD
, name
, (const void **) vals
);
1213 * Determines the computer account's current KVNO via an LDAP lookup
1214 * @param ads An initialized ADS_STRUCT
1215 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1216 * @return the kvno for the computer account, or -1 in case of a failure.
1219 uint32
ads_get_kvno(ADS_STRUCT
*ads
, const char *machine_name
)
1221 LDAPMessage
*res
= NULL
;
1222 uint32 kvno
= (uint32
)-1; /* -1 indicates a failure */
1224 const char *attrs
[] = {"msDS-KeyVersionNumber", NULL
};
1225 char *dn_string
= NULL
;
1226 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1228 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name
));
1229 if (asprintf(&filter
, "(samAccountName=%s$)", machine_name
) == -1) {
1232 ret
= ads_search(ads
, (void**)(void *)&res
, filter
, attrs
);
1234 if (!ADS_ERR_OK(ret
) && ads_count_replies(ads
, res
)) {
1235 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name
));
1236 ads_msgfree(ads
, res
);
1240 dn_string
= ads_get_dn(ads
, res
);
1242 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1243 ads_msgfree(ads
, res
);
1246 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string
));
1247 ads_memfree(ads
, dn_string
);
1249 /* ---------------------------------------------------------
1250 * 0 is returned as a default KVNO from this point on...
1251 * This is done because Windows 2000 does not support key
1252 * version numbers. Chances are that a failure in the next
1253 * step is simply due to Windows 2000 being used for a
1254 * domain controller. */
1257 if (!ads_pull_uint32(ads
, res
, "msDS-KeyVersionNumber", &kvno
)) {
1258 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1259 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1260 ads_msgfree(ads
, res
);
1265 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno
));
1266 ads_msgfree(ads
, res
);
1271 * This clears out all registered spn's for a given hostname
1272 * @param ads An initilaized ADS_STRUCT
1273 * @param machine_name the NetBIOS name of the computer.
1274 * @return 0 upon success, non-zero otherwise.
1277 ADS_STATUS
ads_clear_service_principal_names(ADS_STRUCT
*ads
, const char *machine_name
)
1280 LDAPMessage
*res
= NULL
;
1282 const char *servicePrincipalName
[1] = {NULL
};
1283 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1284 char *dn_string
= NULL
;
1286 ret
= ads_find_machine_acct(ads
, (void **)(void *)&res
, machine_name
);
1287 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1288 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name
));
1289 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name
));
1290 ads_msgfree(ads
, res
);
1291 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1294 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name
));
1295 ctx
= talloc_init("ads_clear_service_principal_names");
1297 ads_msgfree(ads
, res
);
1298 return ADS_ERROR(LDAP_NO_MEMORY
);
1301 if (!(mods
= ads_init_mods(ctx
))) {
1302 talloc_destroy(ctx
);
1303 ads_msgfree(ads
, res
);
1304 return ADS_ERROR(LDAP_NO_MEMORY
);
1306 ret
= ads_mod_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1307 if (!ADS_ERR_OK(ret
)) {
1308 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1309 ads_msgfree(ads
, res
);
1310 talloc_destroy(ctx
);
1313 dn_string
= ads_get_dn(ads
, res
);
1315 talloc_destroy(ctx
);
1316 ads_msgfree(ads
, res
);
1317 return ADS_ERROR(LDAP_NO_MEMORY
);
1319 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1320 ads_memfree(ads
,dn_string
);
1321 if (!ADS_ERR_OK(ret
)) {
1322 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1324 ads_msgfree(ads
, res
);
1325 talloc_destroy(ctx
);
1329 ads_msgfree(ads
, res
);
1330 talloc_destroy(ctx
);
1335 * This adds a service principal name to an existing computer account
1336 * (found by hostname) in AD.
1337 * @param ads An initialized ADS_STRUCT
1338 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1339 * @param spn A string of the service principal to add, i.e. 'host'
1340 * @return 0 upon sucess, or non-zero if a failure occurs
1343 ADS_STATUS
ads_add_service_principal_name(ADS_STRUCT
*ads
, const char *machine_name
, const char *spn
)
1347 LDAPMessage
*res
= NULL
;
1348 char *host_spn
, *psp1
, *psp2
, *psp3
;
1351 char *dn_string
= NULL
;
1352 const char *servicePrincipalName
[4] = {NULL
, NULL
, NULL
, NULL
};
1354 ret
= ads_find_machine_acct(ads
, (void **)(void *)&res
, machine_name
);
1355 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1356 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1358 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1359 spn
, machine_name
, ads
->config
.realm
));
1360 ads_msgfree(ads
, res
);
1361 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1364 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name
));
1365 if (!(ctx
= talloc_init("ads_add_service_principal_name"))) {
1366 ads_msgfree(ads
, res
);
1367 return ADS_ERROR(LDAP_NO_MEMORY
);
1370 name_to_fqdn(my_fqdn
, machine_name
);
1371 strlower_m(my_fqdn
);
1373 if (!(host_spn
= talloc_asprintf(ctx
, "HOST/%s", my_fqdn
))) {
1374 talloc_destroy(ctx
);
1375 ads_msgfree(ads
, res
);
1376 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1379 /* Add the extra principal */
1380 psp1
= talloc_asprintf(ctx
, "%s/%s", spn
, machine_name
);
1382 strlower_m(&psp1
[strlen(spn
)]);
1383 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp1
, machine_name
));
1384 servicePrincipalName
[0] = psp1
;
1385 psp2
= talloc_asprintf(ctx
, "%s/%s.%s", spn
, machine_name
, ads
->config
.realm
);
1387 strlower_m(&psp2
[strlen(spn
)]);
1388 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp2
, machine_name
));
1389 servicePrincipalName
[1] = psp2
;
1391 /* Add another principal in case the realm != the DNS domain, so that
1392 * the KDC doesn't send "server principal unknown" errors to clients
1393 * which use the DNS name in determining service principal names. */
1394 psp3
= talloc_asprintf(ctx
, "%s/%s", spn
, my_fqdn
);
1396 strlower_m(&psp3
[strlen(spn
)]);
1397 if (strcmp(psp2
, psp3
) != 0) {
1398 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp3
, machine_name
));
1399 servicePrincipalName
[2] = psp3
;
1402 if (!(mods
= ads_init_mods(ctx
))) {
1403 talloc_destroy(ctx
);
1404 ads_msgfree(ads
, res
);
1405 return ADS_ERROR(LDAP_NO_MEMORY
);
1407 ret
= ads_add_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1408 if (!ADS_ERR_OK(ret
)) {
1409 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1410 talloc_destroy(ctx
);
1411 ads_msgfree(ads
, res
);
1414 dn_string
= ads_get_dn(ads
, res
);
1416 talloc_destroy(ctx
);
1417 ads_msgfree(ads
, res
);
1418 return ADS_ERROR(LDAP_NO_MEMORY
);
1420 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1421 ads_memfree(ads
,dn_string
);
1422 if (!ADS_ERR_OK(ret
)) {
1423 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1424 talloc_destroy(ctx
);
1425 ads_msgfree(ads
, res
);
1429 talloc_destroy(ctx
);
1430 ads_msgfree(ads
, res
);
1435 * adds a machine account to the ADS server
1436 * @param ads An intialized ADS_STRUCT
1437 * @param machine_name - the NetBIOS machine name of this account.
1438 * @param account_type A number indicating the type of account to create
1439 * @param org_unit The LDAP path in which to place this account
1440 * @return 0 upon success, or non-zero otherwise
1443 static ADS_STATUS
ads_add_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
1444 uint32 account_type
,
1445 const char *org_unit
)
1447 ADS_STATUS ret
, status
;
1448 char *host_spn
, *host_upn
, *new_dn
, *samAccountName
, *controlstr
;
1451 const char *objectClass
[] = {"top", "person", "organizationalPerson",
1452 "user", "computer", NULL
};
1453 const char *servicePrincipalName
[7] = {NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
};
1454 char *psp
, *psp2
, *psp3
, *psp4
;
1455 unsigned acct_control
;
1458 LDAPMessage
*res
= NULL
;
1461 if (!(ctx
= talloc_init("ads_add_machine_acct")))
1462 return ADS_ERROR(LDAP_NO_MEMORY
);
1464 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1466 name_to_fqdn(my_fqdn
, machine_name
);
1468 status
= ads_find_machine_acct(ads
, (void **)(void *)&res
, machine_name
);
1469 if (ADS_ERR_OK(status
) && ads_count_replies(ads
, res
) == 1) {
1470 char *dn_string
= ads_get_dn(ads
, res
);
1472 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
1475 new_dn
= talloc_strdup(ctx
, dn_string
);
1476 ads_memfree(ads
,dn_string
);
1477 DEBUG(0, ("ads_add_machine_acct: Host account for %s already exists - modifying old account\n",
1481 char *ou_str
= ads_ou_string(ads
,org_unit
);
1483 DEBUG(1, ("ads_add_machine_acct: ads_ou_string returned NULL (malloc failure?)\n"));
1486 new_dn
= talloc_asprintf(ctx
, "cn=%s,%s,%s", machine_name
, ou_str
,
1487 ads
->config
.bind_path
);
1496 if (!(host_spn
= talloc_asprintf(ctx
, "HOST/%s", machine_name
)))
1498 if (!(host_upn
= talloc_asprintf(ctx
, "%s@%s", host_spn
, ads
->config
.realm
)))
1500 servicePrincipalName
[0] = talloc_asprintf(ctx
, "HOST/%s", machine_name
);
1501 psp
= talloc_asprintf(ctx
, "HOST/%s.%s",
1504 strlower_m(&psp
[5]);
1505 servicePrincipalName
[1] = psp
;
1506 servicePrincipalName
[2] = talloc_asprintf(ctx
, "CIFS/%s", machine_name
);
1507 psp2
= talloc_asprintf(ctx
, "CIFS/%s.%s",
1510 strlower_m(&psp2
[5]);
1511 servicePrincipalName
[3] = psp2
;
1513 /* Ensure servicePrincipalName[4] and [5] are unique. */
1514 strlower_m(my_fqdn
);
1515 psp3
= talloc_asprintf(ctx
, "CIFS/%s", my_fqdn
);
1516 strlower_m(&psp3
[5]);
1519 for (i
= 0; i
< next_spn
; i
++) {
1520 if (strequal(servicePrincipalName
[i
], psp3
))
1523 if (i
== next_spn
) {
1524 servicePrincipalName
[next_spn
++] = psp3
;
1527 psp4
= talloc_asprintf(ctx
, "HOST/%s", my_fqdn
);
1528 strlower_m(&psp4
[5]);
1529 for (i
= 0; i
< next_spn
; i
++) {
1530 if (strequal(servicePrincipalName
[i
], psp4
))
1533 if (i
== next_spn
) {
1534 servicePrincipalName
[next_spn
++] = psp4
;
1537 if (!(samAccountName
= talloc_asprintf(ctx
, "%s$", machine_name
))) {
1541 acct_control
= account_type
| UF_DONT_EXPIRE_PASSWD
;
1542 #ifndef ENCTYPE_ARCFOUR_HMAC
1543 acct_control
|= UF_USE_DES_KEY_ONLY
;
1546 if (!(controlstr
= talloc_asprintf(ctx
, "%u", acct_control
))) {
1550 if (!(mods
= ads_init_mods(ctx
))) {
1555 ads_mod_str(ctx
, &mods
, "cn", machine_name
);
1556 ads_mod_str(ctx
, &mods
, "sAMAccountName", samAccountName
);
1557 ads_mod_str(ctx
, &mods
, "userAccountControl", controlstr
);
1558 ads_mod_strlist(ctx
, &mods
, "objectClass", objectClass
);
1560 ads_mod_str(ctx
, &mods
, "dNSHostName", my_fqdn
);
1561 ads_mod_str(ctx
, &mods
, "userPrincipalName", host_upn
);
1562 ads_mod_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1563 ads_mod_str(ctx
, &mods
, "operatingSystem", "Samba");
1564 ads_mod_str(ctx
, &mods
, "operatingSystemVersion", SAMBA_VERSION_STRING
);
1567 ret
= ads_gen_add(ads
, new_dn
, mods
);
1569 ret
= ads_gen_mod(ads
, new_dn
, mods
);
1572 if (!ADS_ERR_OK(ret
)) {
1576 /* Do not fail if we can't set security descriptor
1577 * it shouldn't be mandatory and probably we just
1578 * don't have enough rights to do it.
1581 status
= ads_set_machine_sd(ads
, machine_name
, new_dn
);
1583 if (!ADS_ERR_OK(status
)) {
1584 DEBUG(0, ("Warning: ads_set_machine_sd: %s\n",
1585 ads_errstr(status
)));
1589 ads_msgfree(ads
, res
);
1590 talloc_destroy(ctx
);
1595 dump a binary result from ldap
1597 static void dump_binary(const char *field
, struct berval
**values
)
1600 for (i
=0; values
[i
]; i
++) {
1601 printf("%s: ", field
);
1602 for (j
=0; j
<values
[i
]->bv_len
; j
++) {
1603 printf("%02X", (unsigned char)values
[i
]->bv_val
[j
]);
1609 static void dump_guid(const char *field
, struct berval
**values
)
1613 for (i
=0; values
[i
]; i
++) {
1614 memcpy(guid
.info
, values
[i
]->bv_val
, sizeof(guid
.info
));
1615 printf("%s: %s\n", field
,
1616 smb_uuid_string_static(smb_uuid_unpack_static(guid
)));
1621 dump a sid result from ldap
1623 static void dump_sid(const char *field
, struct berval
**values
)
1626 for (i
=0; values
[i
]; i
++) {
1628 sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &sid
);
1629 printf("%s: %s\n", field
, sid_string_static(&sid
));
1634 dump ntSecurityDescriptor
1636 static void dump_sd(const char *filed
, struct berval
**values
)
1641 TALLOC_CTX
*ctx
= 0;
1643 if (!(ctx
= talloc_init("sec_io_desc")))
1647 prs_init(&ps
, values
[0]->bv_len
, ctx
, UNMARSHALL
);
1648 prs_copy_data_in(&ps
, values
[0]->bv_val
, values
[0]->bv_len
);
1649 prs_set_offset(&ps
,0);
1652 if (!sec_io_desc("sd", &psd
, &ps
, 1)) {
1654 talloc_destroy(ctx
);
1657 if (psd
) ads_disp_sd(psd
);
1660 talloc_destroy(ctx
);
1664 dump a string result from ldap
1666 static void dump_string(const char *field
, char **values
)
1669 for (i
=0; values
[i
]; i
++) {
1670 printf("%s: %s\n", field
, values
[i
]);
1675 dump a field from LDAP on stdout
1679 static BOOL
ads_dump_field(char *field
, void **values
, void *data_area
)
1684 void (*handler
)(const char *, struct berval
**);
1686 {"objectGUID", False
, dump_guid
},
1687 {"netbootGUID", False
, dump_guid
},
1688 {"nTSecurityDescriptor", False
, dump_sd
},
1689 {"dnsRecord", False
, dump_binary
},
1690 {"objectSid", False
, dump_sid
},
1691 {"tokenGroups", False
, dump_sid
},
1696 if (!field
) { /* must be end of an entry */
1701 for (i
=0; handlers
[i
].name
; i
++) {
1702 if (StrCaseCmp(handlers
[i
].name
, field
) == 0) {
1703 if (!values
) /* first time, indicate string or not */
1704 return handlers
[i
].string
;
1705 handlers
[i
].handler(field
, (struct berval
**) values
);
1709 if (!handlers
[i
].name
) {
1710 if (!values
) /* first time, indicate string conversion */
1712 dump_string(field
, (char **)values
);
1718 * Dump a result from LDAP on stdout
1719 * used for debugging
1720 * @param ads connection to ads server
1721 * @param res Results to dump
1724 void ads_dump(ADS_STRUCT
*ads
, void *res
)
1726 ads_process_results(ads
, res
, ads_dump_field
, NULL
);
1730 * Walk through results, calling a function for each entry found.
1731 * The function receives a field name, a berval * array of values,
1732 * and a data area passed through from the start. The function is
1733 * called once with null for field and values at the end of each
1735 * @param ads connection to ads server
1736 * @param res Results to process
1737 * @param fn Function for processing each result
1738 * @param data_area user-defined area to pass to function
1740 void ads_process_results(ADS_STRUCT
*ads
, void *res
,
1741 BOOL(*fn
)(char *, void **, void *),
1747 if (!(ctx
= talloc_init("ads_process_results")))
1750 for (msg
= ads_first_entry(ads
, res
); msg
;
1751 msg
= ads_next_entry(ads
, msg
)) {
1755 for (utf8_field
=ldap_first_attribute(ads
->ld
,
1756 (LDAPMessage
*)msg
,&b
);
1758 utf8_field
=ldap_next_attribute(ads
->ld
,
1759 (LDAPMessage
*)msg
,b
)) {
1760 struct berval
**ber_vals
;
1761 char **str_vals
, **utf8_vals
;
1765 pull_utf8_talloc(ctx
, &field
, utf8_field
);
1766 string
= fn(field
, NULL
, data_area
);
1769 utf8_vals
= ldap_get_values(ads
->ld
,
1770 (LDAPMessage
*)msg
, field
);
1771 str_vals
= ads_pull_strvals(ctx
,
1772 (const char **) utf8_vals
);
1773 fn(field
, (void **) str_vals
, data_area
);
1774 ldap_value_free(utf8_vals
);
1776 ber_vals
= ldap_get_values_len(ads
->ld
,
1777 (LDAPMessage
*)msg
, field
);
1778 fn(field
, (void **) ber_vals
, data_area
);
1780 ldap_value_free_len(ber_vals
);
1782 ldap_memfree(utf8_field
);
1785 talloc_free_children(ctx
);
1786 fn(NULL
, NULL
, data_area
); /* completed an entry */
1789 talloc_destroy(ctx
);
1793 * count how many replies are in a LDAPMessage
1794 * @param ads connection to ads server
1795 * @param res Results to count
1796 * @return number of replies
1798 int ads_count_replies(ADS_STRUCT
*ads
, void *res
)
1800 return ldap_count_entries(ads
->ld
, (LDAPMessage
*)res
);
1804 * Join a machine to a realm
1805 * Creates the machine account and sets the machine password
1806 * @param ads connection to ads server
1807 * @param machine name of host to add
1808 * @param org_unit Organizational unit to place machine in
1809 * @return status of join
1811 ADS_STATUS
ads_join_realm(ADS_STRUCT
*ads
, const char *machine_name
,
1812 uint32 account_type
, const char *org_unit
)
1815 LDAPMessage
*res
= NULL
;
1818 /* machine name must be lowercase */
1819 machine
= SMB_STRDUP(machine_name
);
1820 strlower_m(machine
);
1823 status = ads_find_machine_acct(ads, (void **)&res, machine);
1824 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1825 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
1826 status = ads_leave_realm(ads, machine);
1827 if (!ADS_ERR_OK(status)) {
1828 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
1829 machine, ads->config.realm));
1835 status
= ads_add_machine_acct(ads
, machine
, account_type
, org_unit
);
1836 if (!ADS_ERR_OK(status
)) {
1837 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine
, ads_errstr(status
)));
1842 status
= ads_find_machine_acct(ads
, (void **)(void *)&res
, machine
);
1843 if (!ADS_ERR_OK(status
)) {
1844 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine
));
1850 ads_msgfree(ads
, res
);
1856 * Delete a machine from the realm
1857 * @param ads connection to ads server
1858 * @param hostname Machine to remove
1859 * @return status of delete
1861 ADS_STATUS
ads_leave_realm(ADS_STRUCT
*ads
, const char *hostname
)
1865 char *hostnameDN
, *host
;
1867 LDAPControl ldap_control
;
1868 LDAPControl
* pldap_control
[2] = {NULL
, NULL
};
1870 pldap_control
[0] = &ldap_control
;
1871 memset(&ldap_control
, 0, sizeof(LDAPControl
));
1872 ldap_control
.ldctl_oid
= (char *)LDAP_SERVER_TREE_DELETE_OID
;
1874 /* hostname must be lowercase */
1875 host
= SMB_STRDUP(hostname
);
1878 status
= ads_find_machine_acct(ads
, &res
, host
);
1879 if (!ADS_ERR_OK(status
)) {
1880 DEBUG(0, ("Host account for %s does not exist.\n", host
));
1884 msg
= ads_first_entry(ads
, res
);
1886 return ADS_ERROR_SYSTEM(ENOENT
);
1889 hostnameDN
= ads_get_dn(ads
, (LDAPMessage
*)msg
);
1892 rc
= ldap_delete_ext_s(ads
->ld
, hostnameDN
, pldap_control
, NULL
);
1894 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc
));
1896 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc
));
1899 ads_memfree(ads
, hostnameDN
);
1900 if (rc
!= LDAP_SUCCESS
) {
1901 return ADS_ERROR(rc
);
1904 status
= ads_find_machine_acct(ads
, &res
, host
);
1905 if (ADS_ERR_OK(status
) && ads_count_replies(ads
, res
) == 1) {
1906 DEBUG(0, ("Failed to remove host account.\n"));
1916 * add machine account to existing security descriptor
1917 * @param ads connection to ads server
1918 * @param hostname machine to add
1919 * @param dn DN of security descriptor
1922 ADS_STATUS
ads_set_machine_sd(ADS_STRUCT
*ads
, const char *hostname
, char *dn
)
1924 const char *attrs
[] = {"nTSecurityDescriptor", "objectSid", 0};
1927 struct berval bval
= {0, NULL
};
1929 char *escaped_hostname
= escape_ldap_string_alloc(hostname
);
1931 LDAPMessage
*res
= 0;
1932 LDAPMessage
*msg
= 0;
1933 ADS_MODLIST mods
= 0;
1938 SEC_DESC
*psd
= NULL
;
1939 TALLOC_CTX
*ctx
= NULL
;
1941 /* Avoid segmentation fault in prs_mem_free if
1942 * we have to bail out before prs_init */
1943 ps_wire
.is_dynamic
= False
;
1945 if (!ads
) return ADS_ERROR(LDAP_SERVER_DOWN
);
1947 ret
= ADS_ERROR(LDAP_SUCCESS
);
1949 if (!escaped_hostname
) {
1950 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1953 if (asprintf(&expr
, "(samAccountName=%s$)", escaped_hostname
) == -1) {
1954 DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n"));
1955 SAFE_FREE(escaped_hostname
);
1956 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1959 SAFE_FREE(escaped_hostname
);
1961 ret
= ads_search(ads
, (void *) &res
, expr
, attrs
);
1963 if (!ADS_ERR_OK(ret
)) return ret
;
1965 if ( !(msg
= ads_first_entry(ads
, res
) )) {
1966 ret
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
1967 goto ads_set_sd_error
;
1970 if (!ads_pull_sid(ads
, msg
, attrs
[1], &sid
)) {
1971 ret
= ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
1972 goto ads_set_sd_error
;
1975 if (!(ctx
= talloc_init("sec_io_desc"))) {
1976 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1977 goto ads_set_sd_error
;
1980 if (!ads_pull_sd(ads
, ctx
, msg
, attrs
[0], &psd
)) {
1981 ret
= ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
1982 goto ads_set_sd_error
;
1985 status
= sec_desc_add_sid(ctx
, &psd
, &sid
, SEC_RIGHTS_FULL_CTRL
, &sd_size
);
1987 if (!NT_STATUS_IS_OK(status
)) {
1988 ret
= ADS_ERROR_NT(status
);
1989 goto ads_set_sd_error
;
1992 if (!prs_init(&ps_wire
, sd_size
, ctx
, MARSHALL
)) {
1993 ret
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1996 if (!sec_io_desc("sd_wire", &psd
, &ps_wire
, 1)) {
1997 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1998 goto ads_set_sd_error
;
2002 file_save("/tmp/sec_desc.new", ps_wire
.data_p
, sd_size
);
2004 if (!(mods
= ads_init_mods(ctx
))) return ADS_ERROR(LDAP_NO_MEMORY
);
2006 bval
.bv_len
= prs_offset(&ps_wire
);
2007 bval
.bv_val
= TALLOC(ctx
, bval
.bv_len
);
2009 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2010 goto ads_set_sd_error
;
2013 prs_set_offset(&ps_wire
, 0);
2015 if (!prs_copy_data_out(bval
.bv_val
, &ps_wire
, bval
.bv_len
)) {
2016 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2017 goto ads_set_sd_error
;
2020 ret
= ads_mod_ber(ctx
, &mods
, attrs
[0], &bval
);
2021 if (ADS_ERR_OK(ret
)) {
2022 ret
= ads_gen_mod(ads
, dn
, mods
);
2026 ads_msgfree(ads
, res
);
2027 prs_mem_free(&ps_wire
);
2028 talloc_destroy(ctx
);
2033 * pull the first entry from a ADS result
2034 * @param ads connection to ads server
2035 * @param res Results of search
2036 * @return first entry from result
2038 void *ads_first_entry(ADS_STRUCT
*ads
, void *res
)
2040 return (void *)ldap_first_entry(ads
->ld
, (LDAPMessage
*)res
);
2044 * pull the next entry from a ADS result
2045 * @param ads connection to ads server
2046 * @param res Results of search
2047 * @return next entry from result
2049 void *ads_next_entry(ADS_STRUCT
*ads
, void *res
)
2051 return (void *)ldap_next_entry(ads
->ld
, (LDAPMessage
*)res
);
2055 * pull a single string from a ADS result
2056 * @param ads connection to ads server
2057 * @param mem_ctx TALLOC_CTX to use for allocating result string
2058 * @param msg Results of search
2059 * @param field Attribute to retrieve
2060 * @return Result string in talloc context
2062 char *ads_pull_string(ADS_STRUCT
*ads
,
2063 TALLOC_CTX
*mem_ctx
, void *msg
, const char *field
)
2070 values
= ldap_get_values(ads
->ld
, msg
, field
);
2075 rc
= pull_utf8_talloc(mem_ctx
, &ux_string
,
2077 if (rc
!= (size_t)-1)
2081 ldap_value_free(values
);
2086 * pull an array of strings from a ADS result
2087 * @param ads connection to ads server
2088 * @param mem_ctx TALLOC_CTX to use for allocating result string
2089 * @param msg Results of search
2090 * @param field Attribute to retrieve
2091 * @return Result strings in talloc context
2093 char **ads_pull_strings(ADS_STRUCT
*ads
,
2094 TALLOC_CTX
*mem_ctx
, void *msg
, const char *field
,
2101 values
= ldap_get_values(ads
->ld
, msg
, field
);
2105 *num_values
= ldap_count_values(values
);
2107 ret
= TALLOC_ARRAY(mem_ctx
, char *, *num_values
+ 1);
2109 ldap_value_free(values
);
2113 for (i
=0;i
<*num_values
;i
++) {
2114 if (pull_utf8_talloc(mem_ctx
, &ret
[i
], values
[i
]) == -1) {
2115 ldap_value_free(values
);
2121 ldap_value_free(values
);
2126 * pull an array of strings from a ADS result
2127 * (handle large multivalue attributes with range retrieval)
2128 * @param ads connection to ads server
2129 * @param mem_ctx TALLOC_CTX to use for allocating result string
2130 * @param msg Results of search
2131 * @param field Attribute to retrieve
2132 * @param current_strings strings returned by a previous call to this function
2133 * @param next_attribute The next query should ask for this attribute
2134 * @param num_values How many values did we get this time?
2135 * @param more_values Are there more values to get?
2136 * @return Result strings in talloc context
2138 char **ads_pull_strings_range(ADS_STRUCT
*ads
,
2139 TALLOC_CTX
*mem_ctx
,
2140 void *msg
, const char *field
,
2141 char **current_strings
,
2142 const char **next_attribute
,
2143 size_t *num_strings
,
2147 char *expected_range_attrib
, *range_attr
;
2148 BerElement
*ptr
= NULL
;
2151 size_t num_new_strings
;
2152 unsigned long int range_start
;
2153 unsigned long int range_end
;
2155 /* we might have been given the whole lot anyway */
2156 if ((strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
, num_strings
))) {
2157 *more_strings
= False
;
2161 expected_range_attrib
= talloc_asprintf(mem_ctx
, "%s;Range=", field
);
2163 /* look for Range result */
2164 for (attr
= ldap_first_attribute(ads
->ld
, (LDAPMessage
*)msg
, &ptr
);
2166 attr
= ldap_next_attribute(ads
->ld
, (LDAPMessage
*)msg
, ptr
)) {
2167 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2168 if (strnequal(attr
, expected_range_attrib
, strlen(expected_range_attrib
))) {
2176 /* nothing here - this field is just empty */
2177 *more_strings
= False
;
2181 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-%lu",
2182 &range_start
, &range_end
) == 2) {
2183 *more_strings
= True
;
2185 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-*",
2186 &range_start
) == 1) {
2187 *more_strings
= False
;
2189 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2191 ldap_memfree(range_attr
);
2192 *more_strings
= False
;
2197 if ((*num_strings
) != range_start
) {
2198 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2199 " - aborting range retreival\n",
2200 range_attr
, (unsigned int)(*num_strings
) + 1, range_start
));
2201 ldap_memfree(range_attr
);
2202 *more_strings
= False
;
2206 new_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, range_attr
, &num_new_strings
);
2208 if (*more_strings
&& ((*num_strings
+ num_new_strings
) != (range_end
+ 1))) {
2209 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2210 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2211 range_attr
, (unsigned long int)range_end
- range_start
+ 1,
2212 (unsigned long int)num_new_strings
));
2213 ldap_memfree(range_attr
);
2214 *more_strings
= False
;
2218 strings
= TALLOC_REALLOC_ARRAY(mem_ctx
, current_strings
, char *,
2219 *num_strings
+ num_new_strings
);
2221 if (strings
== NULL
) {
2222 ldap_memfree(range_attr
);
2223 *more_strings
= False
;
2227 memcpy(&strings
[*num_strings
], new_strings
,
2228 sizeof(*new_strings
) * num_new_strings
);
2230 (*num_strings
) += num_new_strings
;
2232 if (*more_strings
) {
2233 *next_attribute
= talloc_asprintf(mem_ctx
,
2238 if (!*next_attribute
) {
2239 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2240 ldap_memfree(range_attr
);
2241 *more_strings
= False
;
2246 ldap_memfree(range_attr
);
2252 * pull a single uint32 from a ADS result
2253 * @param ads connection to ads server
2254 * @param msg Results of search
2255 * @param field Attribute to retrieve
2256 * @param v Pointer to int to store result
2257 * @return boolean inidicating success
2259 BOOL
ads_pull_uint32(ADS_STRUCT
*ads
,
2260 void *msg
, const char *field
, uint32
*v
)
2264 values
= ldap_get_values(ads
->ld
, msg
, field
);
2268 ldap_value_free(values
);
2272 *v
= atoi(values
[0]);
2273 ldap_value_free(values
);
2278 * pull a single objectGUID from an ADS result
2279 * @param ads connection to ADS server
2280 * @param msg results of search
2281 * @param guid 37-byte area to receive text guid
2282 * @return boolean indicating success
2284 BOOL
ads_pull_guid(ADS_STRUCT
*ads
,
2285 void *msg
, struct uuid
*guid
)
2288 UUID_FLAT flat_guid
;
2290 values
= ldap_get_values(ads
->ld
, msg
, "objectGUID");
2295 memcpy(&flat_guid
.info
, values
[0], sizeof(UUID_FLAT
));
2296 smb_uuid_unpack(flat_guid
, guid
);
2297 ldap_value_free(values
);
2300 ldap_value_free(values
);
2307 * pull a single DOM_SID from a ADS result
2308 * @param ads connection to ads server
2309 * @param msg Results of search
2310 * @param field Attribute to retrieve
2311 * @param sid Pointer to sid to store result
2312 * @return boolean inidicating success
2314 BOOL
ads_pull_sid(ADS_STRUCT
*ads
,
2315 void *msg
, const char *field
, DOM_SID
*sid
)
2317 struct berval
**values
;
2320 values
= ldap_get_values_len(ads
->ld
, msg
, field
);
2326 ret
= sid_parse(values
[0]->bv_val
, values
[0]->bv_len
, sid
);
2328 ldap_value_free_len(values
);
2333 * pull an array of DOM_SIDs from a ADS result
2334 * @param ads connection to ads server
2335 * @param mem_ctx TALLOC_CTX for allocating sid array
2336 * @param msg Results of search
2337 * @param field Attribute to retrieve
2338 * @param sids pointer to sid array to allocate
2339 * @return the count of SIDs pulled
2341 int ads_pull_sids(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2342 void *msg
, const char *field
, DOM_SID
**sids
)
2344 struct berval
**values
;
2348 values
= ldap_get_values_len(ads
->ld
, msg
, field
);
2353 for (i
=0; values
[i
]; i
++)
2356 (*sids
) = TALLOC_ARRAY(mem_ctx
, DOM_SID
, i
);
2358 ldap_value_free_len(values
);
2363 for (i
=0; values
[i
]; i
++) {
2364 ret
= sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &(*sids
)[count
]);
2367 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid
, &(*sids
)[count
])));
2372 ldap_value_free_len(values
);
2377 * pull a SEC_DESC from a ADS result
2378 * @param ads connection to ads server
2379 * @param mem_ctx TALLOC_CTX for allocating sid array
2380 * @param msg Results of search
2381 * @param field Attribute to retrieve
2382 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2383 * @return boolean inidicating success
2385 BOOL
ads_pull_sd(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2386 void *msg
, const char *field
, SEC_DESC
**sd
)
2388 struct berval
**values
;
2392 values
= ldap_get_values_len(ads
->ld
, msg
, field
);
2394 if (!values
) return False
;
2397 prs_init(&ps
, values
[0]->bv_len
, mem_ctx
, UNMARSHALL
);
2398 prs_copy_data_in(&ps
, values
[0]->bv_val
, values
[0]->bv_len
);
2399 prs_set_offset(&ps
,0);
2401 ret
= sec_io_desc("sd", sd
, &ps
, 1);
2404 ldap_value_free_len(values
);
2409 * in order to support usernames longer than 21 characters we need to
2410 * use both the sAMAccountName and the userPrincipalName attributes
2411 * It seems that not all users have the userPrincipalName attribute set
2413 * @param ads connection to ads server
2414 * @param mem_ctx TALLOC_CTX for allocating sid array
2415 * @param msg Results of search
2416 * @return the username
2418 char *ads_pull_username(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, void *msg
)
2423 /* lookup_name() only works on the sAMAccountName to
2424 returning the username portion of userPrincipalName
2425 breaks winbindd_getpwnam() */
2427 ret
= ads_pull_string(ads
, mem_ctx
, msg
, "userPrincipalName");
2428 if (ret
&& (p
= strchr_m(ret
, '@'))) {
2433 return ads_pull_string(ads
, mem_ctx
, msg
, "sAMAccountName");
2438 * find the update serial number - this is the core of the ldap cache
2439 * @param ads connection to ads server
2440 * @param ads connection to ADS server
2441 * @param usn Pointer to retrieved update serial number
2442 * @return status of search
2444 ADS_STATUS
ads_USN(ADS_STRUCT
*ads
, uint32
*usn
)
2446 const char *attrs
[] = {"highestCommittedUSN", NULL
};
2450 status
= ads_do_search_retry(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2451 if (!ADS_ERR_OK(status
))
2454 if (ads_count_replies(ads
, res
) != 1) {
2455 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2458 ads_pull_uint32(ads
, res
, "highestCommittedUSN", usn
);
2459 ads_msgfree(ads
, res
);
2463 /* parse a ADS timestring - typical string is
2464 '20020917091222.0Z0' which means 09:12.22 17th September
2466 static time_t ads_parse_time(const char *str
)
2472 if (sscanf(str
, "%4d%2d%2d%2d%2d%2d",
2473 &tm
.tm_year
, &tm
.tm_mon
, &tm
.tm_mday
,
2474 &tm
.tm_hour
, &tm
.tm_min
, &tm
.tm_sec
) != 6) {
2484 const char *ads_get_attrname_by_oid(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char * OID
)
2490 const char *attrs
[] = { "lDAPDisplayName", NULL
};
2492 if (ads
== NULL
|| mem_ctx
== NULL
|| OID
== NULL
) {
2496 expr
= talloc_asprintf(mem_ctx
, "(attributeId=%s)", OID
);
2501 rc
= ads_do_search_retry(ads
, ads
->config
.schema_path
,
2502 LDAP_SCOPE_SUBTREE
, expr
, attrs
, &res
);
2503 if (!ADS_ERR_OK(rc
)) {
2507 count
= ads_count_replies(ads
, res
);
2508 if (count
== 0 || !res
) {
2512 return ads_pull_string(ads
, mem_ctx
, res
, "lDAPDisplayName");
2515 DEBUG(0,("ads_get_attrname_by_oid: failed to retrieve name for oid: %s\n",
2522 * Find the servers name and realm - this can be done before authentication
2523 * The ldapServiceName field on w2k looks like this:
2524 * vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
2525 * @param ads connection to ads server
2526 * @return status of search
2528 ADS_STATUS
ads_server_info(ADS_STRUCT
*ads
)
2530 const char *attrs
[] = {"ldapServiceName",
2532 "schemaNamingContext", NULL
};
2541 if (!(ctx
= talloc_init("ads_server_info"))) {
2542 return ADS_ERROR(LDAP_NO_MEMORY
);
2545 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2546 if (!ADS_ERR_OK(status
)) {
2547 talloc_destroy(ctx
);
2551 value
= ads_pull_string(ads
, ctx
, res
, "ldapServiceName");
2553 ads_msgfree(ads
, res
);
2554 talloc_destroy(ctx
);
2555 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2558 timestr
= ads_pull_string(ads
, ctx
, res
, "currentTime");
2560 ads_msgfree(ads
, res
);
2561 talloc_destroy(ctx
);
2562 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2565 schema_path
= ads_pull_string(ads
, ctx
, res
, "schemaNamingContext");
2567 ads_msgfree(ads
, res
);
2568 talloc_destroy(ctx
);
2569 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2572 SAFE_FREE(ads
->config
.schema_path
);
2573 ads
->config
.schema_path
= SMB_STRDUP(schema_path
);
2575 ads_msgfree(ads
, res
);
2577 p
= strchr(value
, ':');
2579 talloc_destroy(ctx
);
2580 DEBUG(1, ("ads_server_info: returned ldap server name did not contain a ':' "
2581 "so was deemed invalid\n"));
2582 return ADS_ERROR(LDAP_DECODING_ERROR
);
2585 SAFE_FREE(ads
->config
.ldap_server_name
);
2587 ads
->config
.ldap_server_name
= SMB_STRDUP(p
+1);
2588 p
= strchr(ads
->config
.ldap_server_name
, '$');
2589 if (!p
|| p
[1] != '@') {
2590 talloc_destroy(ctx
);
2591 DEBUG(1, ("ads_server_info: returned ldap server name (%s) does not contain '$@'"
2592 " so was deemed invalid\n", ads
->config
.ldap_server_name
));
2593 SAFE_FREE(ads
->config
.ldap_server_name
);
2594 return ADS_ERROR(LDAP_DECODING_ERROR
);
2599 SAFE_FREE(ads
->config
.realm
);
2600 SAFE_FREE(ads
->config
.bind_path
);
2602 ads
->config
.realm
= SMB_STRDUP(p
+2);
2603 ads
->config
.bind_path
= ads_build_dn(ads
->config
.realm
);
2605 DEBUG(3,("got ldap server name %s@%s, using bind path: %s\n",
2606 ads
->config
.ldap_server_name
, ads
->config
.realm
,
2607 ads
->config
.bind_path
));
2609 ads
->config
.current_time
= ads_parse_time(timestr
);
2611 if (ads
->config
.current_time
!= 0) {
2612 ads
->auth
.time_offset
= ads
->config
.current_time
- time(NULL
);
2613 DEBUG(4,("time offset is %d seconds\n", ads
->auth
.time_offset
));
2616 talloc_destroy(ctx
);
2622 * Check for "Services for Unix"-Schema and load some attributes into the ADS_STRUCT
2623 * @param ads connection to ads server
2624 * @return BOOL status of search (False if one or more attributes couldn't be
2625 * found in Active Directory)
2627 BOOL
ads_check_sfu_mapping(ADS_STRUCT
*ads
)
2630 TALLOC_CTX
*ctx
= NULL
;
2631 const char *gidnumber
, *uidnumber
, *homedir
, *shell
, *gecos
;
2633 ctx
= talloc_init("ads_check_sfu_mapping");
2637 gidnumber
= ads_get_attrname_by_oid(ads
, ctx
, ADS_ATTR_SFU_GIDNUMBER_OID
);
2638 if (gidnumber
== NULL
)
2640 ads
->schema
.sfu_gidnumber_attr
= SMB_STRDUP(gidnumber
);
2642 uidnumber
= ads_get_attrname_by_oid(ads
, ctx
, ADS_ATTR_SFU_UIDNUMBER_OID
);
2643 if (uidnumber
== NULL
)
2645 ads
->schema
.sfu_uidnumber_attr
= SMB_STRDUP(uidnumber
);
2647 homedir
= ads_get_attrname_by_oid(ads
, ctx
, ADS_ATTR_SFU_HOMEDIR_OID
);
2648 if (homedir
== NULL
)
2650 ads
->schema
.sfu_homedir_attr
= SMB_STRDUP(homedir
);
2652 shell
= ads_get_attrname_by_oid(ads
, ctx
, ADS_ATTR_SFU_SHELL_OID
);
2655 ads
->schema
.sfu_shell_attr
= SMB_STRDUP(shell
);
2657 gecos
= ads_get_attrname_by_oid(ads
, ctx
, ADS_ATTR_SFU_GECOS_OID
);
2660 ads
->schema
.sfu_gecos_attr
= SMB_STRDUP(gecos
);
2665 talloc_destroy(ctx
);
2671 * find the domain sid for our domain
2672 * @param ads connection to ads server
2673 * @param sid Pointer to domain sid
2674 * @return status of search
2676 ADS_STATUS
ads_domain_sid(ADS_STRUCT
*ads
, DOM_SID
*sid
)
2678 const char *attrs
[] = {"objectSid", NULL
};
2682 rc
= ads_do_search_retry(ads
, ads
->config
.bind_path
, LDAP_SCOPE_BASE
, "(objectclass=*)",
2684 if (!ADS_ERR_OK(rc
)) return rc
;
2685 if (!ads_pull_sid(ads
, res
, "objectSid", sid
)) {
2686 ads_msgfree(ads
, res
);
2687 return ADS_ERROR_SYSTEM(ENOENT
);
2689 ads_msgfree(ads
, res
);
2694 /* this is rather complex - we need to find the allternate (netbios) name
2695 for the domain, but there isn't a simple query to do this. Instead
2696 we look for the principle names on the DCs account and find one that has
2697 the right form, then extract the netbios name of the domain from that
2699 NOTE! better method is this:
2701 bin/net -Uadministrator%XXXXX ads search '(&(objectclass=crossref)(dnsroot=VNET3.HOME.SAMBA.ORG))' nETBIOSName
2703 but you need to force the bind path to match the configurationNamingContext from the rootDSE
2706 ADS_STATUS
ads_workgroup_name(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char **workgroup
)
2715 const char *attrs
[] = {"servicePrincipalName", NULL
};
2716 size_t num_principals
;
2718 (*workgroup
) = NULL
;
2720 asprintf(&expr
, "(&(objectclass=computer)(dnshostname=%s.%s))",
2721 ads
->config
.ldap_server_name
, ads
->config
.realm
);
2723 ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
2726 rc
= ads_search(ads
, &res
, expr
, attrs
);
2729 if (!ADS_ERR_OK(rc
)) {
2733 principles
= ads_pull_strings(ads
, mem_ctx
, res
,
2734 "servicePrincipalName", &num_principals
);
2736 ads_msgfree(ads
, res
);
2739 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2742 asprintf(&prefix
, "HOST/%s.%s/",
2743 ads
->config
.ldap_server_name
,
2746 prefix_length
= strlen(prefix
);
2748 for (i
=0;principles
[i
]; i
++) {
2749 if (strnequal(principles
[i
], prefix
, prefix_length
) &&
2750 !strequal(ads
->config
.realm
, principles
[i
]+prefix_length
) &&
2751 !strchr(principles
[i
]+prefix_length
, '.')) {
2752 /* found an alternate (short) name for the domain. */
2753 DEBUG(3,("Found alternate name '%s' for realm '%s'\n",
2754 principles
[i
]+prefix_length
,
2755 ads
->config
.realm
));
2756 (*workgroup
) = talloc_strdup(mem_ctx
, principles
[i
]+prefix_length
);
2763 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2770 * find our site name
2771 * @param ads connection to ads server
2772 * @param mem_ctx Pointer to talloc context
2773 * @param site_name Pointer to the sitename
2774 * @return status of search
2776 ADS_STATUS
ads_site_dn(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char **site_name
)
2780 const char *dn
, *service_name
;
2781 const char *attrs
[] = { "dsServiceName", NULL
};
2783 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2784 if (!ADS_ERR_OK(status
)) {
2788 service_name
= ads_pull_string(ads
, mem_ctx
, res
, "dsServiceName");
2789 if (service_name
== NULL
) {
2790 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2793 /* go up three levels */
2794 dn
= ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name
)));
2796 return ADS_ERROR(LDAP_NO_MEMORY
);
2799 *site_name
= talloc_strdup(mem_ctx
, dn
);
2800 if (*site_name
== NULL
) {
2801 return ADS_ERROR(LDAP_NO_MEMORY
);
2804 ads_msgfree(ads
, res
);
2808 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2813 * find the site dn where a machine resides
2814 * @param ads connection to ads server
2815 * @param mem_ctx Pointer to talloc context
2816 * @param computer_name name of the machine
2817 * @param site_name Pointer to the sitename
2818 * @return status of search
2820 ADS_STATUS
ads_site_dn_for_machine(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char *computer_name
, const char **site_dn
)
2824 const char *parent
, *config_context
, *filter
;
2825 const char *attrs
[] = { "configurationNamingContext", NULL
};
2828 /* shortcut a query */
2829 if (strequal(computer_name
, ads
->config
.ldap_server_name
)) {
2830 return ads_site_dn(ads
, mem_ctx
, site_dn
);
2833 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2834 if (!ADS_ERR_OK(status
)) {
2838 config_context
= ads_pull_string(ads
, mem_ctx
, res
, "configurationNamingContext");
2839 if (config_context
== NULL
) {
2840 return ADS_ERROR(LDAP_NO_MEMORY
);
2843 filter
= talloc_asprintf(mem_ctx
, "(cn=%s)", computer_name
);
2844 if (filter
== NULL
) {
2845 return ADS_ERROR(LDAP_NO_MEMORY
);
2848 status
= ads_do_search(ads
, config_context
, LDAP_SCOPE_SUBTREE
, filter
, NULL
, &res
);
2849 if (!ADS_ERR_OK(status
)) {
2853 if (ads_count_replies(ads
, res
) != 1) {
2854 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2857 dn
= ads_get_dn(ads
, res
);
2859 return ADS_ERROR(LDAP_NO_MEMORY
);
2862 /* go up three levels */
2863 parent
= ads_parent_dn(ads_parent_dn(ads_parent_dn(dn
)));
2864 if (parent
== NULL
) {
2865 ads_memfree(ads
, dn
);
2866 return ADS_ERROR(LDAP_NO_MEMORY
);
2869 *site_dn
= talloc_strdup(mem_ctx
, parent
);
2870 if (*site_dn
== NULL
) {
2871 ads_memfree(ads
, dn
);
2872 ADS_ERROR(LDAP_NO_MEMORY
);
2875 ads_memfree(ads
, dn
);
2876 ads_msgfree(ads
, res
);
2882 * get the upn suffixes for a domain
2883 * @param ads connection to ads server
2884 * @param mem_ctx Pointer to talloc context
2885 * @param suffixes Pointer to an array of suffixes
2886 * @param site_name Pointer to the number of suffixes
2887 * @return status of search
2889 ADS_STATUS
ads_upn_suffixes(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, char **suffixes
, size_t *num_suffixes
)
2893 const char *config_context
, *base
;
2894 const char *attrs
[] = { "configurationNamingContext", NULL
};
2895 const char *attrs2
[] = { "uPNSuffixes", NULL
};
2897 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2898 if (!ADS_ERR_OK(status
)) {
2902 config_context
= ads_pull_string(ads
, mem_ctx
, res
, "configurationNamingContext");
2903 if (config_context
== NULL
) {
2904 return ADS_ERROR(LDAP_NO_MEMORY
);
2907 base
= talloc_asprintf(mem_ctx
, "cn=Partitions,%s", config_context
);
2909 return ADS_ERROR(LDAP_NO_MEMORY
);
2912 status
= ads_search_dn(ads
, &res
, base
, attrs2
);
2913 if (!ADS_ERR_OK(status
)) {
2917 if (ads_count_replies(ads
, res
) != 1) {
2918 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2921 suffixes
= ads_pull_strings(ads
, mem_ctx
, &res
, "uPNSuffixes", num_suffixes
);
2922 if (suffixes
== NULL
) {
2923 ads_msgfree(ads
, res
);
2924 return ADS_ERROR(LDAP_NO_MEMORY
);
2927 ads_msgfree(ads
, res
);