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
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 * @brief basic ldap client-side routines for ads server communications
31 * The routines contained here should do the necessary ldap calls for
34 * Important note: attribute names passed into ads_ routines must
35 * already be in UTF-8 format. We do not convert them because in almost
36 * all cases, they are just ascii (which is represented with the same
37 * codepoints in UTF-8). This may have to change at some point
40 static SIG_ATOMIC_T gotalarm
;
42 /***************************************************************
43 Signal function to tell us we timed out.
44 ****************************************************************/
46 static void gotalarm_sig(void)
51 LDAP
*ldap_open_with_timeout(const char *server
, int port
, unsigned int to
)
57 CatchSignal(SIGALRM
, SIGNAL_CAST gotalarm_sig
);
59 /* End setup timeout. */
61 ldp
= ldap_open(server
, port
);
63 /* Teardown timeout. */
64 CatchSignal(SIGALRM
, SIGNAL_CAST SIG_IGN
);
71 try a connection to a given ldap server, returning True and setting the servers IP
72 in the ads struct if successful
74 TODO : add a negative connection cache in here leveraged off of the one
75 found in the rpc code. --jerry
77 static BOOL
ads_try_connect(ADS_STRUCT
*ads
, const char *server
, unsigned port
)
81 if (!server
|| !*server
) {
85 DEBUG(5,("ads_try_connect: trying ldap server '%s' port %u\n", server
, port
));
87 /* this copes with inet_ntoa brokenness */
90 ads
->ld
= ldap_open_with_timeout(srv
, port
, lp_ldap_timeout());
95 ads
->ldap_port
= port
;
96 ads
->ldap_ip
= *interpret_addr2(srv
);
103 try a connection to a given ldap server, based on URL, returning True if successful
105 static BOOL
ads_try_connect_uri(ADS_STRUCT
*ads
)
107 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
108 DEBUG(5,("ads_try_connect: trying ldap server at URI '%s'\n",
109 ads
->server
.ldap_uri
));
112 if (ldap_initialize((LDAP
**)&(ads
->ld
), ads
->server
.ldap_uri
) == LDAP_SUCCESS
) {
115 DEBUG(0, ("ldap_initialize: %s\n", strerror(errno
)));
119 DEBUG(1, ("no URL support in LDAP libs!\n"));
125 /**********************************************************************
126 Try to find an AD dc using our internal name resolution routines
127 Try the realm first and then then workgroup name if netbios is not
129 **********************************************************************/
131 static BOOL
ads_find_dc(ADS_STRUCT
*ads
)
135 struct ip_service
*ip_list
;
137 BOOL got_realm
= False
;
138 BOOL use_own_domain
= False
;
140 /* if the realm and workgroup are both empty, assume they are ours */
143 c_realm
= ads
->server
.realm
;
145 if ( !c_realm
|| !*c_realm
) {
146 /* special case where no realm and no workgroup means our own */
147 if ( !ads
->server
.workgroup
|| !*ads
->server
.workgroup
) {
148 use_own_domain
= True
;
149 c_realm
= lp_realm();
153 if (c_realm
&& *c_realm
)
157 /* we need to try once with the realm name and fallback to the
158 netbios domain name if we fail (if netbios has not been disabled */
160 if ( !got_realm
&& !lp_disable_netbios() ) {
161 c_realm
= ads
->server
.workgroup
;
162 if (!c_realm
|| !*c_realm
) {
163 if ( use_own_domain
)
164 c_realm
= lp_workgroup();
167 if ( !c_realm
|| !*c_realm
) {
168 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
173 pstrcpy( realm
, c_realm
);
175 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
176 (got_realm
? "realm" : "domain"), realm
));
178 if ( !get_sorted_dc_list(realm
, &ip_list
, &count
, got_realm
) ) {
179 /* fall back to netbios if we can */
180 if ( got_realm
&& !lp_disable_netbios() ) {
188 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
189 for ( i
=0; i
<count
; i
++ ) {
190 /* since this is an ads conection request, default to LDAP_PORT is not set */
191 int port
= (ip_list
[i
].port
!=PORT_NONE
) ? ip_list
[i
].port
: LDAP_PORT
;
194 fstrcpy( server
, inet_ntoa(ip_list
[i
].ip
) );
196 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm
, server
)) )
199 if ( ads_try_connect(ads
, server
, port
) ) {
204 /* keep track of failures */
205 add_failed_connection_entry( realm
, server
, NT_STATUS_UNSUCCESSFUL
);
215 * Connect to the LDAP server
216 * @param ads Pointer to an existing ADS_STRUCT
217 * @return status of connection
219 ADS_STATUS
ads_connect(ADS_STRUCT
*ads
)
221 int version
= LDAP_VERSION3
;
224 ads
->last_attempt
= time(NULL
);
227 /* try with a URL based server */
229 if (ads
->server
.ldap_uri
&&
230 ads_try_connect_uri(ads
)) {
234 /* try with a user specified server */
235 if (ads
->server
.ldap_server
&&
236 ads_try_connect(ads
, ads
->server
.ldap_server
, LDAP_PORT
)) {
240 if (ads_find_dc(ads
)) {
244 return ADS_ERROR_SYSTEM(errno
?errno
:ENOENT
);
247 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads
->ldap_ip
)));
249 status
= ads_server_info(ads
);
250 if (!ADS_ERR_OK(status
)) {
251 DEBUG(1,("Failed to get ldap server info\n"));
255 ldap_set_option(ads
->ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
257 if (!ads
->auth
.user_name
) {
258 /* have to use the userPrincipalName value here and
259 not servicePrincipalName; found by Guenther Deschner @ Sernet */
261 asprintf(&ads
->auth
.user_name
, "host/%s", global_myname() );
264 if (!ads
->auth
.realm
) {
265 ads
->auth
.realm
= strdup(ads
->config
.realm
);
268 if (!ads
->auth
.kdc_server
) {
269 ads
->auth
.kdc_server
= strdup(inet_ntoa(ads
->ldap_ip
));
273 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
274 to MIT kerberos to work (tridge) */
277 asprintf(&env
, "KRB5_KDC_ADDRESS_%s", ads
->config
.realm
);
278 setenv(env
, ads
->auth
.kdc_server
, 1);
283 if (ads
->auth
.flags
& ADS_AUTH_NO_BIND
) {
287 if (ads
->auth
.flags
& ADS_AUTH_ANON_BIND
) {
288 return ADS_ERROR(ldap_simple_bind_s( ads
->ld
, NULL
, NULL
));
291 if (ads
->auth
.flags
& ADS_AUTH_SIMPLE_BIND
) {
292 return ADS_ERROR(ldap_simple_bind_s( ads
->ld
, ads
->auth
.user_name
, ads
->auth
.password
));
295 return ads_sasl_bind(ads
);
299 Duplicate a struct berval into talloc'ed memory
301 static struct berval
*dup_berval(TALLOC_CTX
*ctx
, const struct berval
*in_val
)
303 struct berval
*value
;
305 if (!in_val
) return NULL
;
307 value
= talloc_zero(ctx
, sizeof(struct berval
));
310 if (in_val
->bv_len
== 0) return value
;
312 value
->bv_len
= in_val
->bv_len
;
313 value
->bv_val
= talloc_memdup(ctx
, in_val
->bv_val
, in_val
->bv_len
);
318 Make a values list out of an array of (struct berval *)
320 static struct berval
**ads_dup_values(TALLOC_CTX
*ctx
,
321 const struct berval
**in_vals
)
323 struct berval
**values
;
326 if (!in_vals
) return NULL
;
327 for (i
=0; in_vals
[i
]; i
++); /* count values */
328 values
= (struct berval
**) talloc_zero(ctx
,
329 (i
+1)*sizeof(struct berval
*));
330 if (!values
) return NULL
;
332 for (i
=0; in_vals
[i
]; i
++) {
333 values
[i
] = dup_berval(ctx
, in_vals
[i
]);
339 UTF8-encode a values list out of an array of (char *)
341 static char **ads_push_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
346 if (!in_vals
) return NULL
;
347 for (i
=0; in_vals
[i
]; i
++); /* count values */
348 values
= (char ** ) talloc_zero(ctx
, (i
+1)*sizeof(char *));
349 if (!values
) return NULL
;
351 for (i
=0; in_vals
[i
]; i
++) {
352 push_utf8_talloc(ctx
, &values
[i
], in_vals
[i
]);
358 Pull a (char *) array out of a UTF8-encoded values list
360 static char **ads_pull_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
365 if (!in_vals
) return NULL
;
366 for (i
=0; in_vals
[i
]; i
++); /* count values */
367 values
= (char **) talloc_zero(ctx
, (i
+1)*sizeof(char *));
368 if (!values
) return NULL
;
370 for (i
=0; in_vals
[i
]; i
++) {
371 pull_utf8_talloc(ctx
, &values
[i
], in_vals
[i
]);
377 * Do a search with paged results. cookie must be null on the first
378 * call, and then returned on each subsequent call. It will be null
379 * again when the entire search is complete
380 * @param ads connection to ads server
381 * @param bind_path Base dn for the search
382 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
383 * @param expr Search expression - specified in local charset
384 * @param attrs Attributes to retrieve - specified in utf8 or ascii
385 * @param res ** which will contain results - free res* with ads_msgfree()
386 * @param count Number of entries retrieved on this page
387 * @param cookie The paged results cookie to be returned on subsequent calls
388 * @return status of search
390 ADS_STATUS
ads_do_paged_search(ADS_STRUCT
*ads
, const char *bind_path
,
391 int scope
, const char *expr
,
392 const char **attrs
, void **res
,
393 int *count
, void **cookie
)
396 char *utf8_expr
, *utf8_path
, **search_attrs
;
397 LDAPControl PagedResults
, NoReferrals
, *controls
[3], **rcontrols
;
398 BerElement
*cookie_be
= NULL
;
399 struct berval
*cookie_bv
= NULL
;
404 if (!(ctx
= talloc_init("ads_do_paged_search")))
405 return ADS_ERROR(LDAP_NO_MEMORY
);
407 /* 0 means the conversion worked but the result was empty
408 so we only fail if it's -1. In any case, it always
409 at least nulls out the dest */
410 if ((push_utf8_talloc(ctx
, &utf8_expr
, expr
) == (size_t)-1) ||
411 (push_utf8_talloc(ctx
, &utf8_path
, bind_path
) == (size_t)-1)) {
416 if (!attrs
|| !(*attrs
))
419 /* This would be the utf8-encoded version...*/
420 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
421 if (!(str_list_copy(&search_attrs
, attrs
))) {
428 /* Paged results only available on ldap v3 or later */
429 ldap_get_option(ads
->ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
430 if (version
< LDAP_VERSION3
) {
431 rc
= LDAP_NOT_SUPPORTED
;
435 cookie_be
= ber_alloc_t(LBER_USE_DER
);
436 if (cookie
&& *cookie
) {
437 ber_printf(cookie_be
, "{iO}", (ber_int_t
) 1000, *cookie
);
438 ber_bvfree(*cookie
); /* don't need it from last time */
441 ber_printf(cookie_be
, "{io}", (ber_int_t
) 1000, "", 0);
443 ber_flatten(cookie_be
, &cookie_bv
);
444 PagedResults
.ldctl_oid
= ADS_PAGE_CTL_OID
;
445 PagedResults
.ldctl_iscritical
= (char) 1;
446 PagedResults
.ldctl_value
.bv_len
= cookie_bv
->bv_len
;
447 PagedResults
.ldctl_value
.bv_val
= cookie_bv
->bv_val
;
449 NoReferrals
.ldctl_oid
= ADS_NO_REFERRALS_OID
;
450 NoReferrals
.ldctl_iscritical
= (char) 0;
451 NoReferrals
.ldctl_value
.bv_len
= 0;
452 NoReferrals
.ldctl_value
.bv_val
= "";
455 controls
[0] = &NoReferrals
;
456 controls
[1] = &PagedResults
;
459 /* we need to disable referrals as the openldap libs don't
460 handle them and paged results at the same time. Using them
461 together results in the result record containing the server
462 page control being removed from the result list (tridge/jmcd)
464 leaving this in despite the control that says don't generate
465 referrals, in case the server doesn't support it (jmcd)
467 ldap_set_option(ads
->ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
469 rc
= ldap_search_ext_s(ads
->ld
, utf8_path
, scope
, utf8_expr
,
470 search_attrs
, 0, controls
,
471 NULL
, NULL
, LDAP_NO_LIMIT
, (LDAPMessage
**)res
);
473 ber_free(cookie_be
, 1);
474 ber_bvfree(cookie_bv
);
477 DEBUG(3,("ldap_search_ext_s(%s) -> %s\n", expr
, ldap_err2string(rc
)));
481 rc
= ldap_parse_result(ads
->ld
, *res
, NULL
, NULL
, NULL
,
482 NULL
, &rcontrols
, 0);
488 for (i
=0; rcontrols
[i
]; i
++) {
489 if (strcmp(ADS_PAGE_CTL_OID
, rcontrols
[i
]->ldctl_oid
) == 0) {
490 cookie_be
= ber_init(&rcontrols
[i
]->ldctl_value
);
491 ber_scanf(cookie_be
,"{iO}", (ber_int_t
*) count
,
493 /* the berval is the cookie, but must be freed when
495 if (cookie_bv
->bv_len
) /* still more to do */
496 *cookie
=ber_bvdup(cookie_bv
);
499 ber_bvfree(cookie_bv
);
500 ber_free(cookie_be
, 1);
504 ldap_controls_free(rcontrols
);
508 /* if/when we decide to utf8-encode attrs, take out this next line */
509 str_list_free(&search_attrs
);
511 return ADS_ERROR(rc
);
516 * Get all results for a search. This uses ads_do_paged_search() to return
517 * all entries in a large search.
518 * @param ads connection to ads server
519 * @param bind_path Base dn for the search
520 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
521 * @param expr Search expression
522 * @param attrs Attributes to retrieve
523 * @param res ** which will contain results - free res* with ads_msgfree()
524 * @return status of search
526 ADS_STATUS
ads_do_search_all(ADS_STRUCT
*ads
, const char *bind_path
,
527 int scope
, const char *expr
,
528 const char **attrs
, void **res
)
535 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
, res
,
538 if (!ADS_ERR_OK(status
)) return status
;
543 LDAPMessage
*msg
, *next
;
545 status2
= ads_do_paged_search(ads
, bind_path
, scope
, expr
,
546 attrs
, &res2
, &count
, &cookie
);
548 if (!ADS_ERR_OK(status2
)) break;
550 /* this relies on the way that ldap_add_result_entry() works internally. I hope
551 that this works on all ldap libs, but I have only tested with openldap */
552 for (msg
= ads_first_entry(ads
, res2
); msg
; msg
= next
) {
553 next
= ads_next_entry(ads
, msg
);
554 ldap_add_result_entry((LDAPMessage
**)res
, msg
);
556 /* note that we do not free res2, as the memory is now
557 part of the main returned list */
564 * Run a function on all results for a search. Uses ads_do_paged_search() and
565 * runs the function as each page is returned, using ads_process_results()
566 * @param ads connection to ads server
567 * @param bind_path Base dn for the search
568 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
569 * @param expr Search expression - specified in local charset
570 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
571 * @param fn Function which takes attr name, values list, and data_area
572 * @param data_area Pointer which is passed to function on each call
573 * @return status of search
575 ADS_STATUS
ads_do_search_all_fn(ADS_STRUCT
*ads
, const char *bind_path
,
576 int scope
, const char *expr
, const char **attrs
,
577 BOOL(*fn
)(char *, void **, void *),
585 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
, &res
,
588 if (!ADS_ERR_OK(status
)) return status
;
590 ads_process_results(ads
, res
, fn
, data_area
);
591 ads_msgfree(ads
, res
);
594 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
,
595 &res
, &count
, &cookie
);
597 if (!ADS_ERR_OK(status
)) break;
599 ads_process_results(ads
, res
, fn
, data_area
);
600 ads_msgfree(ads
, res
);
607 * Do a search with a timeout.
608 * @param ads connection to ads server
609 * @param bind_path Base dn for the search
610 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
611 * @param expr Search expression
612 * @param attrs Attributes to retrieve
613 * @param res ** which will contain results - free res* with ads_msgfree()
614 * @return status of search
616 ADS_STATUS
ads_do_search(ADS_STRUCT
*ads
, const char *bind_path
, int scope
,
618 const char **attrs
, void **res
)
620 struct timeval timeout
;
622 char *utf8_expr
, *utf8_path
, **search_attrs
= NULL
;
626 if (!(ctx
= talloc_init("ads_do_search"))) {
627 DEBUG(1,("ads_do_search: talloc_init() failed!"));
628 return ADS_ERROR(LDAP_NO_MEMORY
);
631 /* 0 means the conversion worked but the result was empty
632 so we only fail if it's negative. In any case, it always
633 at least nulls out the dest */
634 if ((push_utf8_talloc(ctx
, &utf8_expr
, expr
) == (size_t)-1) ||
635 (push_utf8_talloc(ctx
, &utf8_path
, bind_path
) == (size_t)-1)) {
636 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
641 if (!attrs
|| !(*attrs
))
644 /* This would be the utf8-encoded version...*/
645 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
646 if (!(str_list_copy(&search_attrs
, attrs
)))
648 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
654 timeout
.tv_sec
= ADS_SEARCH_TIMEOUT
;
657 /* see the note in ads_do_paged_search - we *must* disable referrals */
658 ldap_set_option(ads
->ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
660 rc
= ldap_search_ext_s(ads
->ld
, utf8_path
, scope
, utf8_expr
,
661 search_attrs
, 0, NULL
, NULL
,
662 &timeout
, LDAP_NO_LIMIT
, (LDAPMessage
**)res
);
664 if (rc
== LDAP_SIZELIMIT_EXCEEDED
) {
665 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
671 /* if/when we decide to utf8-encode attrs, take out this next line */
672 str_list_free(&search_attrs
);
673 return ADS_ERROR(rc
);
676 * Do a general ADS search
677 * @param ads connection to ads server
678 * @param res ** which will contain results - free res* with ads_msgfree()
679 * @param expr Search expression
680 * @param attrs Attributes to retrieve
681 * @return status of search
683 ADS_STATUS
ads_search(ADS_STRUCT
*ads
, void **res
,
687 return ads_do_search(ads
, ads
->config
.bind_path
, LDAP_SCOPE_SUBTREE
,
692 * Do a search on a specific DistinguishedName
693 * @param ads connection to ads server
694 * @param res ** which will contain results - free res* with ads_msgfree()
695 * @param dn DistinguishName to search
696 * @param attrs Attributes to retrieve
697 * @return status of search
699 ADS_STATUS
ads_search_dn(ADS_STRUCT
*ads
, void **res
,
703 return ads_do_search(ads
, dn
, LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, res
);
707 * Free up memory from a ads_search
708 * @param ads connection to ads server
709 * @param msg Search results to free
711 void ads_msgfree(ADS_STRUCT
*ads
, void *msg
)
718 * Free up memory from various ads requests
719 * @param ads connection to ads server
720 * @param mem Area to free
722 void ads_memfree(ADS_STRUCT
*ads
, void *mem
)
728 * Get a dn from search results
729 * @param ads connection to ads server
730 * @param msg Search result
733 char *ads_get_dn(ADS_STRUCT
*ads
, void *msg
)
735 char *utf8_dn
, *unix_dn
;
737 utf8_dn
= ldap_get_dn(ads
->ld
, msg
);
740 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
744 if (pull_utf8_allocate(&unix_dn
, utf8_dn
) == (size_t)-1) {
745 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
749 ldap_memfree(utf8_dn
);
754 * Find a machine account given a hostname
755 * @param ads connection to ads server
756 * @param res ** which will contain results - free res* with ads_msgfree()
757 * @param host Hostname to search for
758 * @return status of search
760 ADS_STATUS
ads_find_machine_acct(ADS_STRUCT
*ads
, void **res
, const char *machine
)
764 const char *attrs
[] = {"*", "nTSecurityDescriptor", NULL
};
768 /* the easiest way to find a machine account anywhere in the tree
769 is to look for hostname$ */
770 if (asprintf(&expr
, "(samAccountName=%s$)", machine
) == -1) {
771 DEBUG(1, ("asprintf failed!\n"));
772 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
775 status
= ads_search(ads
, res
, expr
, attrs
);
781 * Initialize a list of mods to be used in a modify request
782 * @param ctx An initialized TALLOC_CTX
783 * @return allocated ADS_MODLIST
785 ADS_MODLIST
ads_init_mods(TALLOC_CTX
*ctx
)
787 #define ADS_MODLIST_ALLOC_SIZE 10
790 if ((mods
= (LDAPMod
**) talloc_zero(ctx
, sizeof(LDAPMod
*) *
791 (ADS_MODLIST_ALLOC_SIZE
+ 1))))
792 /* -1 is safety to make sure we don't go over the end.
793 need to reset it to NULL before doing ldap modify */
794 mods
[ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
801 add an attribute to the list, with values list already constructed
803 static ADS_STATUS
ads_modlist_add(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
804 int mod_op
, const char *name
,
808 LDAPMod
**modlist
= (LDAPMod
**) *mods
;
809 struct berval
**ber_values
= NULL
;
810 char **char_values
= NULL
;
813 mod_op
= LDAP_MOD_DELETE
;
815 if (mod_op
& LDAP_MOD_BVALUES
)
816 ber_values
= ads_dup_values(ctx
,
817 (const struct berval
**)invals
);
819 char_values
= ads_push_strvals(ctx
,
820 (const char **) invals
);
823 /* find the first empty slot */
824 for (curmod
=0; modlist
[curmod
] && modlist
[curmod
] != (LDAPMod
*) -1;
826 if (modlist
[curmod
] == (LDAPMod
*) -1) {
827 if (!(modlist
= talloc_realloc(ctx
, modlist
,
828 (curmod
+ADS_MODLIST_ALLOC_SIZE
+1)*sizeof(LDAPMod
*))))
829 return ADS_ERROR(LDAP_NO_MEMORY
);
830 memset(&modlist
[curmod
], 0,
831 ADS_MODLIST_ALLOC_SIZE
*sizeof(LDAPMod
*));
832 modlist
[curmod
+ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
836 if (!(modlist
[curmod
] = talloc_zero(ctx
, sizeof(LDAPMod
))))
837 return ADS_ERROR(LDAP_NO_MEMORY
);
838 modlist
[curmod
]->mod_type
= talloc_strdup(ctx
, name
);
839 if (mod_op
& LDAP_MOD_BVALUES
) {
840 modlist
[curmod
]->mod_bvalues
= ber_values
;
841 } else if (mod_op
& LDAP_MOD_DELETE
) {
842 modlist
[curmod
]->mod_values
= NULL
;
844 modlist
[curmod
]->mod_values
= char_values
;
847 modlist
[curmod
]->mod_op
= mod_op
;
848 return ADS_ERROR(LDAP_SUCCESS
);
852 * Add a single string value to a mod list
853 * @param ctx An initialized TALLOC_CTX
854 * @param mods An initialized ADS_MODLIST
855 * @param name The attribute name to add
856 * @param val The value to add - NULL means DELETE
857 * @return ADS STATUS indicating success of add
859 ADS_STATUS
ads_mod_str(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
860 const char *name
, const char *val
)
862 const char *values
[2];
868 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
869 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
, name
,
870 (const void **) values
);
874 * Add an array of string values to a mod list
875 * @param ctx An initialized TALLOC_CTX
876 * @param mods An initialized ADS_MODLIST
877 * @param name The attribute name to add
878 * @param vals The array of string values to add - NULL means DELETE
879 * @return ADS STATUS indicating success of add
881 ADS_STATUS
ads_mod_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
882 const char *name
, const char **vals
)
885 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
886 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
,
887 name
, (const void **) vals
);
891 * Add a single ber-encoded value to a mod list
892 * @param ctx An initialized TALLOC_CTX
893 * @param mods An initialized ADS_MODLIST
894 * @param name The attribute name to add
895 * @param val The value to add - NULL means DELETE
896 * @return ADS STATUS indicating success of add
898 static ADS_STATUS
ads_mod_ber(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
899 const char *name
, const struct berval
*val
)
901 const struct berval
*values
[2];
906 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
907 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
|LDAP_MOD_BVALUES
,
908 name
, (const void **) values
);
912 * Perform an ldap modify
913 * @param ads connection to ads server
914 * @param mod_dn DistinguishedName to modify
915 * @param mods list of modifications to perform
916 * @return status of modify
918 ADS_STATUS
ads_gen_mod(ADS_STRUCT
*ads
, const char *mod_dn
, ADS_MODLIST mods
)
921 char *utf8_dn
= NULL
;
923 this control is needed to modify that contains a currently
924 non-existent attribute (but allowable for the object) to run
926 LDAPControl PermitModify
= {
927 ADS_PERMIT_MODIFY_OID
,
930 LDAPControl
*controls
[2];
932 controls
[0] = &PermitModify
;
935 if (push_utf8_allocate(&utf8_dn
, mod_dn
) == -1) {
936 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
939 /* find the end of the list, marked by NULL or -1 */
940 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
941 /* make sure the end of the list is NULL */
943 ret
= ldap_modify_ext_s(ads
->ld
, utf8_dn
,
944 (LDAPMod
**) mods
, controls
, NULL
);
946 return ADS_ERROR(ret
);
950 * Perform an ldap add
951 * @param ads connection to ads server
952 * @param new_dn DistinguishedName to add
953 * @param mods list of attributes and values for DN
954 * @return status of add
956 ADS_STATUS
ads_gen_add(ADS_STRUCT
*ads
, const char *new_dn
, ADS_MODLIST mods
)
959 char *utf8_dn
= NULL
;
961 if (push_utf8_allocate(&utf8_dn
, new_dn
) == -1) {
962 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
963 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
966 /* find the end of the list, marked by NULL or -1 */
967 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
968 /* make sure the end of the list is NULL */
971 ret
= ldap_add_s(ads
->ld
, utf8_dn
, mods
);
973 return ADS_ERROR(ret
);
977 * Delete a DistinguishedName
978 * @param ads connection to ads server
979 * @param new_dn DistinguishedName to delete
980 * @return status of delete
982 ADS_STATUS
ads_del_dn(ADS_STRUCT
*ads
, char *del_dn
)
985 char *utf8_dn
= NULL
;
986 if (push_utf8_allocate(&utf8_dn
, del_dn
) == -1) {
987 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
988 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
991 ret
= ldap_delete_s(ads
->ld
, utf8_dn
);
992 return ADS_ERROR(ret
);
996 * Build an org unit string
997 * if org unit is Computers or blank then assume a container, otherwise
998 * assume a \ separated list of organisational units
999 * @param ads connection to ads server
1000 * @param org_unit Organizational unit
1001 * @return org unit string - caller must free
1003 char *ads_ou_string(ADS_STRUCT
*ads
, const char *org_unit
)
1007 if (!org_unit
|| !*org_unit
) {
1009 ret
= ads_default_ou_string(ads
, WELL_KNOWN_GUID_COMPUTERS
);
1011 /* samba4 might not yet respond to a wellknownobject-query */
1012 return ret
? ret
: strdup("cn=Computers");
1015 if (strequal(org_unit
, "Computers")) {
1016 return strdup("cn=Computers");
1019 return ads_build_path(org_unit
, "\\/", "ou=", 1);
1023 * Get a org unit string for a well-known GUID
1024 * @param ads connection to ads server
1025 * @param wknguid Well known GUID
1026 * @return org unit string - caller must free
1028 char *ads_default_ou_string(ADS_STRUCT
*ads
, const char *wknguid
)
1032 char *base
, *wkn_dn
, *ret
, **wkn_dn_exp
, **bind_dn_exp
;
1033 const char *attrs
[] = {"distinguishedName", NULL
};
1034 int new_ln
, wkn_ln
, bind_ln
, i
;
1036 if (wknguid
== NULL
) {
1040 if (asprintf(&base
, "<WKGUID=%s,%s>", wknguid
, ads
->config
.bind_path
) == -1) {
1041 DEBUG(1, ("asprintf failed!\n"));
1045 status
= ads_search_dn(ads
, &res
, base
, attrs
);
1046 if (!ADS_ERR_OK(status
)) {
1047 DEBUG(1,("Failed while searching for: %s\n", base
));
1052 if (ads_count_replies(ads
, res
) != 1) {
1056 /* substitute the bind-path from the well-known-guid-search result */
1057 wkn_dn
= ads_get_dn(ads
, res
);
1058 wkn_dn_exp
= ldap_explode_dn(wkn_dn
, 0);
1059 bind_dn_exp
= ldap_explode_dn(ads
->config
.bind_path
, 0);
1061 for (wkn_ln
=0; wkn_dn_exp
[wkn_ln
]; wkn_ln
++)
1063 for (bind_ln
=0; bind_dn_exp
[bind_ln
]; bind_ln
++)
1066 new_ln
= wkn_ln
- bind_ln
;
1068 ret
= wkn_dn_exp
[0];
1070 for (i
=1; i
< new_ln
; i
++) {
1072 asprintf(&s
, "%s,%s", ret
, wkn_dn_exp
[i
]);
1081 * Adds (appends) an item to an attribute array, rather then
1082 * replacing the whole list
1083 * @param ctx An initialized TALLOC_CTX
1084 * @param mods An initialized ADS_MODLIST
1085 * @param name name of the ldap attribute to append to
1086 * @param vals an array of values to add
1087 * @return status of addition
1090 ADS_STATUS
ads_add_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1091 const char *name
, const char **vals
)
1093 return ads_modlist_add(ctx
, mods
, LDAP_MOD_ADD
, name
, (const void **) vals
);
1097 * Determines the computer account's current KVNO via an LDAP lookup
1098 * @param ads An initialized ADS_STRUCT
1099 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1100 * @return the kvno for the computer account, or -1 in case of a failure.
1103 uint32
ads_get_kvno(ADS_STRUCT
*ads
, const char *machine_name
)
1105 LDAPMessage
*res
= NULL
;
1106 uint32 kvno
= (uint32
)-1; /* -1 indicates a failure */
1108 const char *attrs
[] = {"msDS-KeyVersionNumber", NULL
};
1109 char *dn_string
= NULL
;
1110 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1112 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name
));
1113 if (asprintf(&filter
, "(samAccountName=%s$)", machine_name
) == -1) {
1116 ret
= ads_search(ads
, (void**) &res
, filter
, attrs
);
1118 if (!ADS_ERR_OK(ret
) && ads_count_replies(ads
, res
)) {
1119 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name
));
1120 ads_msgfree(ads
, res
);
1124 dn_string
= ads_get_dn(ads
, res
);
1126 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1127 ads_msgfree(ads
, res
);
1130 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string
));
1131 ads_memfree(ads
, dn_string
);
1133 /* ---------------------------------------------------------
1134 * 0 is returned as a default KVNO from this point on...
1135 * This is done because Windows 2000 does not support key
1136 * version numbers. Chances are that a failure in the next
1137 * step is simply due to Windows 2000 being used for a
1138 * domain controller. */
1141 if (!ads_pull_uint32(ads
, res
, "msDS-KeyVersionNumber", &kvno
)) {
1142 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1143 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1144 ads_msgfree(ads
, res
);
1149 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno
));
1150 ads_msgfree(ads
, res
);
1155 * This clears out all registered spn's for a given hostname
1156 * @param ads An initilaized ADS_STRUCT
1157 * @param machine_name the NetBIOS name of the computer.
1158 * @return 0 upon success, non-zero otherwise.
1161 ADS_STATUS
ads_clear_service_principal_names(ADS_STRUCT
*ads
, const char *machine_name
)
1164 LDAPMessage
*res
= NULL
;
1166 const char *servicePrincipalName
[1] = {NULL
};
1167 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1168 char *dn_string
= NULL
;
1170 ret
= ads_find_machine_acct(ads
, (void **)&res
, machine_name
);
1171 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1172 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name
));
1173 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name
));
1174 ads_msgfree(ads
, res
);
1175 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1178 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name
));
1179 ctx
= talloc_init("ads_clear_service_principal_names");
1181 ads_msgfree(ads
, res
);
1182 return ADS_ERROR(LDAP_NO_MEMORY
);
1185 if (!(mods
= ads_init_mods(ctx
))) {
1186 talloc_destroy(ctx
);
1187 ads_msgfree(ads
, res
);
1188 return ADS_ERROR(LDAP_NO_MEMORY
);
1190 ret
= ads_mod_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1191 if (!ADS_ERR_OK(ret
)) {
1192 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1193 ads_msgfree(ads
, res
);
1194 talloc_destroy(ctx
);
1197 dn_string
= ads_get_dn(ads
, res
);
1199 talloc_destroy(ctx
);
1200 ads_msgfree(ads
, res
);
1201 return ADS_ERROR(LDAP_NO_MEMORY
);
1203 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1204 ads_memfree(ads
,dn_string
);
1205 if (!ADS_ERR_OK(ret
)) {
1206 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1208 ads_msgfree(ads
, res
);
1209 talloc_destroy(ctx
);
1213 ads_msgfree(ads
, res
);
1214 talloc_destroy(ctx
);
1219 * This adds a service principal name to an existing computer account
1220 * (found by hostname) in AD.
1221 * @param ads An initialized ADS_STRUCT
1222 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1223 * @param spn A string of the service principal to add, i.e. 'host'
1224 * @return 0 upon sucess, or non-zero if a failure occurs
1227 ADS_STATUS
ads_add_service_principal_name(ADS_STRUCT
*ads
, const char *machine_name
, const char *spn
)
1231 LDAPMessage
*res
= NULL
;
1232 char *host_spn
, *host_upn
, *psp1
, *psp2
, *psp3
;
1235 char *dn_string
= NULL
;
1236 const char *servicePrincipalName
[4] = {NULL
, NULL
, NULL
, NULL
};
1238 ret
= ads_find_machine_acct(ads
, (void **)&res
, machine_name
);
1239 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1240 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1242 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1243 spn
, machine_name
, ads
->config
.realm
));
1244 ads_msgfree(ads
, res
);
1245 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1248 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name
));
1249 if (!(ctx
= talloc_init("ads_add_service_principal_name"))) {
1250 ads_msgfree(ads
, res
);
1251 return ADS_ERROR(LDAP_NO_MEMORY
);
1254 name_to_fqdn(my_fqdn
, machine_name
);
1255 strlower_m(my_fqdn
);
1257 if (!(host_spn
= talloc_asprintf(ctx
, "HOST/%s", my_fqdn
))) {
1258 talloc_destroy(ctx
);
1259 ads_msgfree(ads
, res
);
1260 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1262 if (!(host_upn
= talloc_asprintf(ctx
, "%s@%s", host_spn
, ads
->config
.realm
))) {
1263 talloc_destroy(ctx
);
1264 ads_msgfree(ads
, res
);
1265 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1268 /* Add the extra principal */
1269 psp1
= talloc_asprintf(ctx
, "%s/%s", spn
, machine_name
);
1271 strlower_m(&psp1
[strlen(spn
)]);
1272 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp1
, machine_name
));
1273 servicePrincipalName
[0] = psp1
;
1274 psp2
= talloc_asprintf(ctx
, "%s/%s.%s", spn
, machine_name
, ads
->config
.realm
);
1276 strlower_m(&psp2
[strlen(spn
)]);
1277 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp2
, machine_name
));
1278 servicePrincipalName
[1] = psp2
;
1280 /* Add another principal in case the realm != the DNS domain, so that
1281 * the KDC doesn't send "server principal unknown" errors to clients
1282 * which use the DNS name in determining service principal names. */
1283 psp3
= talloc_asprintf(ctx
, "%s/%s", spn
, my_fqdn
);
1285 strlower_m(&psp3
[strlen(spn
)]);
1286 if (strcmp(psp2
, psp3
) != 0) {
1287 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp3
, machine_name
));
1288 servicePrincipalName
[2] = psp3
;
1291 if (!(mods
= ads_init_mods(ctx
))) {
1292 talloc_destroy(ctx
);
1293 ads_msgfree(ads
, res
);
1294 return ADS_ERROR(LDAP_NO_MEMORY
);
1296 ret
= ads_add_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1297 if (!ADS_ERR_OK(ret
)) {
1298 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1299 talloc_destroy(ctx
);
1300 ads_msgfree(ads
, res
);
1303 dn_string
= ads_get_dn(ads
, res
);
1305 talloc_destroy(ctx
);
1306 ads_msgfree(ads
, res
);
1307 return ADS_ERROR(LDAP_NO_MEMORY
);
1309 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1310 ads_memfree(ads
,dn_string
);
1311 if (!ADS_ERR_OK(ret
)) {
1312 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1313 talloc_destroy(ctx
);
1314 ads_msgfree(ads
, res
);
1318 talloc_destroy(ctx
);
1319 ads_msgfree(ads
, res
);
1324 * adds a machine account to the ADS server
1325 * @param ads An intialized ADS_STRUCT
1326 * @param machine_name - the NetBIOS machine name of this account.
1327 * @param account_type A number indicating the type of account to create
1328 * @param org_unit The LDAP path in which to place this account
1329 * @return 0 upon success, or non-zero otherwise
1332 static ADS_STATUS
ads_add_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
1333 uint32 account_type
,
1334 const char *org_unit
)
1336 ADS_STATUS ret
, status
;
1337 char *host_spn
, *host_upn
, *new_dn
, *samAccountName
, *controlstr
;
1340 const char *objectClass
[] = {"top", "person", "organizationalPerson",
1341 "user", "computer", NULL
};
1342 const char *servicePrincipalName
[7] = {NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
};
1343 char *psp
, *psp2
, *psp3
, *psp4
;
1344 unsigned acct_control
;
1347 LDAPMessage
*res
= NULL
;
1350 if (!(ctx
= talloc_init("ads_add_machine_acct")))
1351 return ADS_ERROR(LDAP_NO_MEMORY
);
1353 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1355 name_to_fqdn(my_fqdn
, machine_name
);
1357 status
= ads_find_machine_acct(ads
, (void **)&res
, machine_name
);
1358 if (ADS_ERR_OK(status
) && ads_count_replies(ads
, res
) == 1) {
1359 char *dn_string
= ads_get_dn(ads
, res
);
1361 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
1364 new_dn
= talloc_strdup(ctx
, dn_string
);
1365 ads_memfree(ads
,dn_string
);
1366 DEBUG(0, ("ads_add_machine_acct: Host account for %s already exists - modifying old account\n",
1370 char *ou_str
= ads_ou_string(ads
,org_unit
);
1372 DEBUG(1, ("ads_add_machine_acct: ads_ou_string returned NULL (malloc failure?)\n"));
1375 new_dn
= talloc_asprintf(ctx
, "cn=%s,%s,%s", machine_name
, ou_str
,
1376 ads
->config
.bind_path
);
1385 if (!(host_spn
= talloc_asprintf(ctx
, "HOST/%s", machine_name
)))
1387 if (!(host_upn
= talloc_asprintf(ctx
, "%s@%s", host_spn
, ads
->config
.realm
)))
1389 servicePrincipalName
[0] = talloc_asprintf(ctx
, "HOST/%s", machine_name
);
1390 psp
= talloc_asprintf(ctx
, "HOST/%s.%s",
1393 strlower_m(&psp
[5]);
1394 servicePrincipalName
[1] = psp
;
1395 servicePrincipalName
[2] = talloc_asprintf(ctx
, "CIFS/%s", machine_name
);
1396 psp2
= talloc_asprintf(ctx
, "CIFS/%s.%s",
1399 strlower_m(&psp2
[5]);
1400 servicePrincipalName
[3] = psp2
;
1402 /* Ensure servicePrincipalName[4] and [5] are unique. */
1403 strlower_m(my_fqdn
);
1404 psp3
= talloc_asprintf(ctx
, "CIFS/%s", my_fqdn
);
1405 strlower_m(&psp3
[5]);
1408 for (i
= 0; i
< next_spn
; i
++) {
1409 if (strequal(servicePrincipalName
[i
], psp3
))
1412 if (i
== next_spn
) {
1413 servicePrincipalName
[next_spn
++] = psp3
;
1416 psp4
= talloc_asprintf(ctx
, "HOST/%s", my_fqdn
);
1417 strlower_m(&psp4
[5]);
1418 for (i
= 0; i
< next_spn
; i
++) {
1419 if (strequal(servicePrincipalName
[i
], psp3
))
1422 if (i
== next_spn
) {
1423 servicePrincipalName
[next_spn
++] = psp4
;
1426 if (!(samAccountName
= talloc_asprintf(ctx
, "%s$", machine_name
))) {
1430 acct_control
= account_type
| UF_DONT_EXPIRE_PASSWD
;
1431 #ifndef ENCTYPE_ARCFOUR_HMAC
1432 acct_control
|= UF_USE_DES_KEY_ONLY
;
1435 if (!(controlstr
= talloc_asprintf(ctx
, "%u", acct_control
))) {
1439 if (!(mods
= ads_init_mods(ctx
))) {
1444 ads_mod_str(ctx
, &mods
, "cn", machine_name
);
1445 ads_mod_str(ctx
, &mods
, "sAMAccountName", samAccountName
);
1446 ads_mod_str(ctx
, &mods
, "userAccountControl", controlstr
);
1447 ads_mod_strlist(ctx
, &mods
, "objectClass", objectClass
);
1449 ads_mod_str(ctx
, &mods
, "dNSHostName", my_fqdn
);
1450 ads_mod_str(ctx
, &mods
, "userPrincipalName", host_upn
);
1451 ads_mod_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1452 ads_mod_str(ctx
, &mods
, "operatingSystem", "Samba");
1453 ads_mod_str(ctx
, &mods
, "operatingSystemVersion", SAMBA_VERSION_STRING
);
1456 ret
= ads_gen_add(ads
, new_dn
, mods
);
1458 ret
= ads_gen_mod(ads
, new_dn
, mods
);
1461 if (!ADS_ERR_OK(ret
)) {
1465 /* Do not fail if we can't set security descriptor
1466 * it shouldn't be mandatory and probably we just
1467 * don't have enough rights to do it.
1470 status
= ads_set_machine_sd(ads
, machine_name
, new_dn
);
1472 if (!ADS_ERR_OK(status
)) {
1473 DEBUG(0, ("Warning: ads_set_machine_sd: %s\n",
1474 ads_errstr(status
)));
1478 ads_msgfree(ads
, res
);
1479 talloc_destroy(ctx
);
1484 dump a binary result from ldap
1486 static void dump_binary(const char *field
, struct berval
**values
)
1489 for (i
=0; values
[i
]; i
++) {
1490 printf("%s: ", field
);
1491 for (j
=0; j
<values
[i
]->bv_len
; j
++) {
1492 printf("%02X", (unsigned char)values
[i
]->bv_val
[j
]);
1498 static void dump_guid(const char *field
, struct berval
**values
)
1502 for (i
=0; values
[i
]; i
++) {
1503 memcpy(guid
.info
, values
[i
]->bv_val
, sizeof(guid
.info
));
1504 printf("%s: %s\n", field
,
1505 smb_uuid_string_static(smb_uuid_unpack_static(guid
)));
1510 dump a sid result from ldap
1512 static void dump_sid(const char *field
, struct berval
**values
)
1515 for (i
=0; values
[i
]; i
++) {
1517 sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &sid
);
1518 printf("%s: %s\n", field
, sid_string_static(&sid
));
1523 dump ntSecurityDescriptor
1525 static void dump_sd(const char *filed
, struct berval
**values
)
1530 TALLOC_CTX
*ctx
= 0;
1532 if (!(ctx
= talloc_init("sec_io_desc")))
1536 prs_init(&ps
, values
[0]->bv_len
, ctx
, UNMARSHALL
);
1537 prs_copy_data_in(&ps
, values
[0]->bv_val
, values
[0]->bv_len
);
1538 prs_set_offset(&ps
,0);
1541 if (!sec_io_desc("sd", &psd
, &ps
, 1)) {
1543 talloc_destroy(ctx
);
1546 if (psd
) ads_disp_sd(psd
);
1549 talloc_destroy(ctx
);
1553 dump a string result from ldap
1555 static void dump_string(const char *field
, char **values
)
1558 for (i
=0; values
[i
]; i
++) {
1559 printf("%s: %s\n", field
, values
[i
]);
1564 dump a field from LDAP on stdout
1568 static BOOL
ads_dump_field(char *field
, void **values
, void *data_area
)
1573 void (*handler
)(const char *, struct berval
**);
1575 {"objectGUID", False
, dump_guid
},
1576 {"nTSecurityDescriptor", False
, dump_sd
},
1577 {"dnsRecord", False
, dump_binary
},
1578 {"objectSid", False
, dump_sid
},
1579 {"tokenGroups", False
, dump_sid
},
1584 if (!field
) { /* must be end of an entry */
1589 for (i
=0; handlers
[i
].name
; i
++) {
1590 if (StrCaseCmp(handlers
[i
].name
, field
) == 0) {
1591 if (!values
) /* first time, indicate string or not */
1592 return handlers
[i
].string
;
1593 handlers
[i
].handler(field
, (struct berval
**) values
);
1597 if (!handlers
[i
].name
) {
1598 if (!values
) /* first time, indicate string conversion */
1600 dump_string(field
, (char **)values
);
1606 * Dump a result from LDAP on stdout
1607 * used for debugging
1608 * @param ads connection to ads server
1609 * @param res Results to dump
1612 void ads_dump(ADS_STRUCT
*ads
, void *res
)
1614 ads_process_results(ads
, res
, ads_dump_field
, NULL
);
1618 * Walk through results, calling a function for each entry found.
1619 * The function receives a field name, a berval * array of values,
1620 * and a data area passed through from the start. The function is
1621 * called once with null for field and values at the end of each
1623 * @param ads connection to ads server
1624 * @param res Results to process
1625 * @param fn Function for processing each result
1626 * @param data_area user-defined area to pass to function
1628 void ads_process_results(ADS_STRUCT
*ads
, void *res
,
1629 BOOL(*fn
)(char *, void **, void *),
1635 if (!(ctx
= talloc_init("ads_process_results")))
1638 for (msg
= ads_first_entry(ads
, res
); msg
;
1639 msg
= ads_next_entry(ads
, msg
)) {
1643 for (utf8_field
=ldap_first_attribute(ads
->ld
,
1644 (LDAPMessage
*)msg
,&b
);
1646 utf8_field
=ldap_next_attribute(ads
->ld
,
1647 (LDAPMessage
*)msg
,b
)) {
1648 struct berval
**ber_vals
;
1649 char **str_vals
, **utf8_vals
;
1653 pull_utf8_talloc(ctx
, &field
, utf8_field
);
1654 string
= fn(field
, NULL
, data_area
);
1657 utf8_vals
= ldap_get_values(ads
->ld
,
1658 (LDAPMessage
*)msg
, field
);
1659 str_vals
= ads_pull_strvals(ctx
,
1660 (const char **) utf8_vals
);
1661 fn(field
, (void **) str_vals
, data_area
);
1662 ldap_value_free(utf8_vals
);
1664 ber_vals
= ldap_get_values_len(ads
->ld
,
1665 (LDAPMessage
*)msg
, field
);
1666 fn(field
, (void **) ber_vals
, data_area
);
1668 ldap_value_free_len(ber_vals
);
1670 ldap_memfree(utf8_field
);
1673 talloc_destroy_pool(ctx
);
1674 fn(NULL
, NULL
, data_area
); /* completed an entry */
1677 talloc_destroy(ctx
);
1681 * count how many replies are in a LDAPMessage
1682 * @param ads connection to ads server
1683 * @param res Results to count
1684 * @return number of replies
1686 int ads_count_replies(ADS_STRUCT
*ads
, void *res
)
1688 return ldap_count_entries(ads
->ld
, (LDAPMessage
*)res
);
1692 * Join a machine to a realm
1693 * Creates the machine account and sets the machine password
1694 * @param ads connection to ads server
1695 * @param machine name of host to add
1696 * @param org_unit Organizational unit to place machine in
1697 * @return status of join
1699 ADS_STATUS
ads_join_realm(ADS_STRUCT
*ads
, const char *machine_name
,
1700 uint32 account_type
, const char *org_unit
)
1703 LDAPMessage
*res
= NULL
;
1706 /* machine name must be lowercase */
1707 machine
= strdup(machine_name
);
1708 strlower_m(machine
);
1711 status = ads_find_machine_acct(ads, (void **)&res, machine);
1712 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1713 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
1714 status = ads_leave_realm(ads, machine);
1715 if (!ADS_ERR_OK(status)) {
1716 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
1717 machine, ads->config.realm));
1723 status
= ads_add_machine_acct(ads
, machine
, account_type
, org_unit
);
1724 if (!ADS_ERR_OK(status
)) {
1725 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine
, ads_errstr(status
)));
1730 status
= ads_find_machine_acct(ads
, (void **)&res
, machine
);
1731 if (!ADS_ERR_OK(status
)) {
1732 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine
));
1738 ads_msgfree(ads
, res
);
1744 * Delete a machine from the realm
1745 * @param ads connection to ads server
1746 * @param hostname Machine to remove
1747 * @return status of delete
1749 ADS_STATUS
ads_leave_realm(ADS_STRUCT
*ads
, const char *hostname
)
1753 char *hostnameDN
, *host
;
1756 /* hostname must be lowercase */
1757 host
= strdup(hostname
);
1760 status
= ads_find_machine_acct(ads
, &res
, host
);
1761 if (!ADS_ERR_OK(status
)) {
1762 DEBUG(0, ("Host account for %s does not exist.\n", host
));
1766 msg
= ads_first_entry(ads
, res
);
1768 return ADS_ERROR_SYSTEM(ENOENT
);
1771 hostnameDN
= ads_get_dn(ads
, (LDAPMessage
*)msg
);
1772 rc
= ldap_delete_s(ads
->ld
, hostnameDN
);
1773 ads_memfree(ads
, hostnameDN
);
1774 if (rc
!= LDAP_SUCCESS
) {
1775 return ADS_ERROR(rc
);
1778 status
= ads_find_machine_acct(ads
, &res
, host
);
1779 if (ADS_ERR_OK(status
) && ads_count_replies(ads
, res
) == 1) {
1780 DEBUG(0, ("Failed to remove host account.\n"));
1790 * add machine account to existing security descriptor
1791 * @param ads connection to ads server
1792 * @param hostname machine to add
1793 * @param dn DN of security descriptor
1796 ADS_STATUS
ads_set_machine_sd(ADS_STRUCT
*ads
, const char *hostname
, char *dn
)
1798 const char *attrs
[] = {"nTSecurityDescriptor", "objectSid", 0};
1801 struct berval bval
= {0, NULL
};
1803 char *escaped_hostname
= escape_ldap_string_alloc(hostname
);
1805 LDAPMessage
*res
= 0;
1806 LDAPMessage
*msg
= 0;
1807 ADS_MODLIST mods
= 0;
1812 SEC_DESC
*psd
= NULL
;
1813 TALLOC_CTX
*ctx
= NULL
;
1815 /* Avoid segmentation fault in prs_mem_free if
1816 * we have to bail out before prs_init */
1817 ps_wire
.is_dynamic
= False
;
1819 if (!ads
) return ADS_ERROR(LDAP_SERVER_DOWN
);
1821 ret
= ADS_ERROR(LDAP_SUCCESS
);
1823 if (!escaped_hostname
) {
1824 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1827 if (asprintf(&expr
, "(samAccountName=%s$)", escaped_hostname
) == -1) {
1828 DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n"));
1829 SAFE_FREE(escaped_hostname
);
1830 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1833 SAFE_FREE(escaped_hostname
);
1835 ret
= ads_search(ads
, (void *) &res
, expr
, attrs
);
1837 if (!ADS_ERR_OK(ret
)) return ret
;
1839 if ( !(msg
= ads_first_entry(ads
, res
) )) {
1840 ret
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
1841 goto ads_set_sd_error
;
1844 if (!ads_pull_sid(ads
, msg
, attrs
[1], &sid
)) {
1845 ret
= ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
1846 goto ads_set_sd_error
;
1849 if (!(ctx
= talloc_init("sec_io_desc"))) {
1850 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1851 goto ads_set_sd_error
;
1854 if (!ads_pull_sd(ads
, ctx
, msg
, attrs
[0], &psd
)) {
1855 ret
= ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
1856 goto ads_set_sd_error
;
1859 status
= sec_desc_add_sid(ctx
, &psd
, &sid
, SEC_RIGHTS_FULL_CTRL
, &sd_size
);
1861 if (!NT_STATUS_IS_OK(status
)) {
1862 ret
= ADS_ERROR_NT(status
);
1863 goto ads_set_sd_error
;
1866 if (!prs_init(&ps_wire
, sd_size
, ctx
, MARSHALL
)) {
1867 ret
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1870 if (!sec_io_desc("sd_wire", &psd
, &ps_wire
, 1)) {
1871 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1872 goto ads_set_sd_error
;
1876 file_save("/tmp/sec_desc.new", ps_wire
.data_p
, sd_size
);
1878 if (!(mods
= ads_init_mods(ctx
))) return ADS_ERROR(LDAP_NO_MEMORY
);
1880 bval
.bv_len
= prs_offset(&ps_wire
);
1881 bval
.bv_val
= talloc(ctx
, bval
.bv_len
);
1883 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1884 goto ads_set_sd_error
;
1887 prs_set_offset(&ps_wire
, 0);
1889 if (!prs_copy_data_out(bval
.bv_val
, &ps_wire
, bval
.bv_len
)) {
1890 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1891 goto ads_set_sd_error
;
1894 ret
= ads_mod_ber(ctx
, &mods
, attrs
[0], &bval
);
1895 if (ADS_ERR_OK(ret
)) {
1896 ret
= ads_gen_mod(ads
, dn
, mods
);
1900 ads_msgfree(ads
, res
);
1901 prs_mem_free(&ps_wire
);
1902 talloc_destroy(ctx
);
1907 * pull the first entry from a ADS result
1908 * @param ads connection to ads server
1909 * @param res Results of search
1910 * @return first entry from result
1912 void *ads_first_entry(ADS_STRUCT
*ads
, void *res
)
1914 return (void *)ldap_first_entry(ads
->ld
, (LDAPMessage
*)res
);
1918 * pull the next entry from a ADS result
1919 * @param ads connection to ads server
1920 * @param res Results of search
1921 * @return next entry from result
1923 void *ads_next_entry(ADS_STRUCT
*ads
, void *res
)
1925 return (void *)ldap_next_entry(ads
->ld
, (LDAPMessage
*)res
);
1929 * pull a single string from a ADS result
1930 * @param ads connection to ads server
1931 * @param mem_ctx TALLOC_CTX to use for allocating result string
1932 * @param msg Results of search
1933 * @param field Attribute to retrieve
1934 * @return Result string in talloc context
1936 char *ads_pull_string(ADS_STRUCT
*ads
,
1937 TALLOC_CTX
*mem_ctx
, void *msg
, const char *field
)
1944 values
= ldap_get_values(ads
->ld
, msg
, field
);
1949 rc
= pull_utf8_talloc(mem_ctx
, &ux_string
,
1951 if (rc
!= (size_t)-1)
1955 ldap_value_free(values
);
1960 * pull an array of strings from a ADS result
1961 * @param ads connection to ads server
1962 * @param mem_ctx TALLOC_CTX to use for allocating result string
1963 * @param msg Results of search
1964 * @param field Attribute to retrieve
1965 * @return Result strings in talloc context
1967 char **ads_pull_strings(ADS_STRUCT
*ads
,
1968 TALLOC_CTX
*mem_ctx
, void *msg
, const char *field
,
1975 values
= ldap_get_values(ads
->ld
, msg
, field
);
1979 *num_values
= ldap_count_values(values
);
1981 ret
= talloc(mem_ctx
, sizeof(char *) * (*num_values
+1));
1983 ldap_value_free(values
);
1987 for (i
=0;i
<*num_values
;i
++) {
1988 if (pull_utf8_talloc(mem_ctx
, &ret
[i
], values
[i
]) == -1) {
1989 ldap_value_free(values
);
1995 ldap_value_free(values
);
2000 * pull an array of strings from a ADS result
2001 * (handle large multivalue attributes with range retrieval)
2002 * @param ads connection to ads server
2003 * @param mem_ctx TALLOC_CTX to use for allocating result string
2004 * @param msg Results of search
2005 * @param field Attribute to retrieve
2006 * @param current_strings strings returned by a previous call to this function
2007 * @param next_attribute The next query should ask for this attribute
2008 * @param num_values How many values did we get this time?
2009 * @param more_values Are there more values to get?
2010 * @return Result strings in talloc context
2012 char **ads_pull_strings_range(ADS_STRUCT
*ads
,
2013 TALLOC_CTX
*mem_ctx
,
2014 void *msg
, const char *field
,
2015 char **current_strings
,
2016 const char **next_attribute
,
2017 size_t *num_strings
,
2021 char *expected_range_attrib
, *range_attr
;
2022 BerElement
*ptr
= NULL
;
2025 size_t num_new_strings
;
2026 unsigned long int range_start
;
2027 unsigned long int range_end
;
2029 /* we might have been given the whole lot anyway */
2030 if ((strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
, num_strings
))) {
2031 *more_strings
= False
;
2035 expected_range_attrib
= talloc_asprintf(mem_ctx
, "%s;Range=", field
);
2037 /* look for Range result */
2038 for (attr
= ldap_first_attribute(ads
->ld
, (LDAPMessage
*)msg
, &ptr
);
2040 attr
= ldap_next_attribute(ads
->ld
, (LDAPMessage
*)msg
, ptr
)) {
2041 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2042 if (strnequal(attr
, expected_range_attrib
, strlen(expected_range_attrib
))) {
2050 /* nothing here - this field is just empty */
2051 *more_strings
= False
;
2055 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-%lu",
2056 &range_start
, &range_end
) == 2) {
2057 *more_strings
= True
;
2059 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-*",
2060 &range_start
) == 1) {
2061 *more_strings
= False
;
2063 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2065 ldap_memfree(range_attr
);
2066 *more_strings
= False
;
2071 if ((*num_strings
) != range_start
) {
2072 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2073 " - aborting range retreival\n",
2074 range_attr
, *num_strings
+ 1, range_start
));
2075 ldap_memfree(range_attr
);
2076 *more_strings
= False
;
2080 new_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, range_attr
, &num_new_strings
);
2082 if (*more_strings
&& ((*num_strings
+ num_new_strings
) != (range_end
+ 1))) {
2083 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2084 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2085 range_attr
, (unsigned long int)range_end
- range_start
+ 1,
2086 (unsigned long int)num_new_strings
));
2087 ldap_memfree(range_attr
);
2088 *more_strings
= False
;
2092 strings
= talloc_realloc(mem_ctx
, current_strings
,
2093 sizeof(*current_strings
) *
2094 (*num_strings
+ num_new_strings
));
2096 if (strings
== NULL
) {
2097 ldap_memfree(range_attr
);
2098 *more_strings
= False
;
2102 memcpy(&strings
[*num_strings
], new_strings
,
2103 sizeof(*new_strings
) * num_new_strings
);
2105 (*num_strings
) += num_new_strings
;
2107 if (*more_strings
) {
2108 *next_attribute
= talloc_asprintf(mem_ctx
,
2113 if (!*next_attribute
) {
2114 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2115 ldap_memfree(range_attr
);
2116 *more_strings
= False
;
2121 ldap_memfree(range_attr
);
2127 * pull a single uint32 from a ADS result
2128 * @param ads connection to ads server
2129 * @param msg Results of search
2130 * @param field Attribute to retrieve
2131 * @param v Pointer to int to store result
2132 * @return boolean inidicating success
2134 BOOL
ads_pull_uint32(ADS_STRUCT
*ads
,
2135 void *msg
, const char *field
, uint32
*v
)
2139 values
= ldap_get_values(ads
->ld
, msg
, field
);
2143 ldap_value_free(values
);
2147 *v
= atoi(values
[0]);
2148 ldap_value_free(values
);
2153 * pull a single objectGUID from an ADS result
2154 * @param ads connection to ADS server
2155 * @param msg results of search
2156 * @param guid 37-byte area to receive text guid
2157 * @return boolean indicating success
2159 BOOL
ads_pull_guid(ADS_STRUCT
*ads
,
2160 void *msg
, struct uuid
*guid
)
2163 UUID_FLAT flat_guid
;
2165 values
= ldap_get_values(ads
->ld
, msg
, "objectGUID");
2170 memcpy(&flat_guid
.info
, values
[0], sizeof(UUID_FLAT
));
2171 smb_uuid_unpack(flat_guid
, guid
);
2172 ldap_value_free(values
);
2175 ldap_value_free(values
);
2182 * pull a single DOM_SID from a ADS result
2183 * @param ads connection to ads server
2184 * @param msg Results of search
2185 * @param field Attribute to retrieve
2186 * @param sid Pointer to sid to store result
2187 * @return boolean inidicating success
2189 BOOL
ads_pull_sid(ADS_STRUCT
*ads
,
2190 void *msg
, const char *field
, DOM_SID
*sid
)
2192 struct berval
**values
;
2195 values
= ldap_get_values_len(ads
->ld
, msg
, field
);
2201 ret
= sid_parse(values
[0]->bv_val
, values
[0]->bv_len
, sid
);
2203 ldap_value_free_len(values
);
2208 * pull an array of DOM_SIDs from a ADS result
2209 * @param ads connection to ads server
2210 * @param mem_ctx TALLOC_CTX for allocating sid array
2211 * @param msg Results of search
2212 * @param field Attribute to retrieve
2213 * @param sids pointer to sid array to allocate
2214 * @return the count of SIDs pulled
2216 int ads_pull_sids(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2217 void *msg
, const char *field
, DOM_SID
**sids
)
2219 struct berval
**values
;
2223 values
= ldap_get_values_len(ads
->ld
, msg
, field
);
2228 for (i
=0; values
[i
]; i
++)
2231 (*sids
) = talloc(mem_ctx
, sizeof(DOM_SID
) * i
);
2233 ldap_value_free_len(values
);
2238 for (i
=0; values
[i
]; i
++) {
2239 ret
= sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &(*sids
)[count
]);
2242 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid
, &(*sids
)[count
])));
2247 ldap_value_free_len(values
);
2252 * pull a SEC_DESC from a ADS result
2253 * @param ads connection to ads server
2254 * @param mem_ctx TALLOC_CTX for allocating sid array
2255 * @param msg Results of search
2256 * @param field Attribute to retrieve
2257 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2258 * @return boolean inidicating success
2260 BOOL
ads_pull_sd(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2261 void *msg
, const char *field
, SEC_DESC
**sd
)
2263 struct berval
**values
;
2267 values
= ldap_get_values_len(ads
->ld
, msg
, field
);
2269 if (!values
) return False
;
2272 prs_init(&ps
, values
[0]->bv_len
, mem_ctx
, UNMARSHALL
);
2273 prs_copy_data_in(&ps
, values
[0]->bv_val
, values
[0]->bv_len
);
2274 prs_set_offset(&ps
,0);
2276 ret
= sec_io_desc("sd", sd
, &ps
, 1);
2279 ldap_value_free_len(values
);
2284 * in order to support usernames longer than 21 characters we need to
2285 * use both the sAMAccountName and the userPrincipalName attributes
2286 * It seems that not all users have the userPrincipalName attribute set
2288 * @param ads connection to ads server
2289 * @param mem_ctx TALLOC_CTX for allocating sid array
2290 * @param msg Results of search
2291 * @return the username
2293 char *ads_pull_username(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, void *msg
)
2298 /* lookup_name() only works on the sAMAccountName to
2299 returning the username portion of userPrincipalName
2300 breaks winbindd_getpwnam() */
2302 ret
= ads_pull_string(ads
, mem_ctx
, msg
, "userPrincipalName");
2303 if (ret
&& (p
= strchr_m(ret
, '@'))) {
2308 return ads_pull_string(ads
, mem_ctx
, msg
, "sAMAccountName");
2313 * find the update serial number - this is the core of the ldap cache
2314 * @param ads connection to ads server
2315 * @param ads connection to ADS server
2316 * @param usn Pointer to retrieved update serial number
2317 * @return status of search
2319 ADS_STATUS
ads_USN(ADS_STRUCT
*ads
, uint32
*usn
)
2321 const char *attrs
[] = {"highestCommittedUSN", NULL
};
2325 status
= ads_do_search_retry(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2326 if (!ADS_ERR_OK(status
))
2329 if (ads_count_replies(ads
, res
) != 1) {
2330 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2333 ads_pull_uint32(ads
, res
, "highestCommittedUSN", usn
);
2334 ads_msgfree(ads
, res
);
2338 /* parse a ADS timestring - typical string is
2339 '20020917091222.0Z0' which means 09:12.22 17th September
2341 static time_t ads_parse_time(const char *str
)
2347 if (sscanf(str
, "%4d%2d%2d%2d%2d%2d",
2348 &tm
.tm_year
, &tm
.tm_mon
, &tm
.tm_mday
,
2349 &tm
.tm_hour
, &tm
.tm_min
, &tm
.tm_sec
) != 6) {
2360 * Find the servers name and realm - this can be done before authentication
2361 * The ldapServiceName field on w2k looks like this:
2362 * vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
2363 * @param ads connection to ads server
2364 * @return status of search
2366 ADS_STATUS
ads_server_info(ADS_STRUCT
*ads
)
2368 const char *attrs
[] = {"ldapServiceName", "currentTime", NULL
};
2376 if (!(ctx
= talloc_init("ads_server_info"))) {
2377 return ADS_ERROR(LDAP_NO_MEMORY
);
2380 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2381 if (!ADS_ERR_OK(status
)) {
2382 talloc_destroy(ctx
);
2386 value
= ads_pull_string(ads
, ctx
, res
, "ldapServiceName");
2388 ads_msgfree(ads
, res
);
2389 talloc_destroy(ctx
);
2390 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2393 timestr
= ads_pull_string(ads
, ctx
, res
, "currentTime");
2395 ads_msgfree(ads
, res
);
2396 talloc_destroy(ctx
);
2397 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2400 ads_msgfree(ads
, res
);
2402 p
= strchr(value
, ':');
2404 talloc_destroy(ctx
);
2405 DEBUG(1, ("ads_server_info: returned ldap server name did not contain a ':' "
2406 "so was deemed invalid\n"));
2407 return ADS_ERROR(LDAP_DECODING_ERROR
);
2410 SAFE_FREE(ads
->config
.ldap_server_name
);
2412 ads
->config
.ldap_server_name
= strdup(p
+1);
2413 p
= strchr(ads
->config
.ldap_server_name
, '$');
2414 if (!p
|| p
[1] != '@') {
2415 talloc_destroy(ctx
);
2416 DEBUG(1, ("ads_server_info: returned ldap server name (%s) does not contain '$@'"
2417 " so was deemed invalid\n", ads
->config
.ldap_server_name
));
2418 SAFE_FREE(ads
->config
.ldap_server_name
);
2419 return ADS_ERROR(LDAP_DECODING_ERROR
);
2424 SAFE_FREE(ads
->config
.realm
);
2425 SAFE_FREE(ads
->config
.bind_path
);
2427 ads
->config
.realm
= strdup(p
+2);
2428 ads
->config
.bind_path
= ads_build_dn(ads
->config
.realm
);
2430 DEBUG(3,("got ldap server name %s@%s, using bind path: %s\n",
2431 ads
->config
.ldap_server_name
, ads
->config
.realm
,
2432 ads
->config
.bind_path
));
2434 ads
->config
.current_time
= ads_parse_time(timestr
);
2436 if (ads
->config
.current_time
!= 0) {
2437 ads
->auth
.time_offset
= ads
->config
.current_time
- time(NULL
);
2438 DEBUG(4,("time offset is %d seconds\n", ads
->auth
.time_offset
));
2441 talloc_destroy(ctx
);
2447 * find the domain sid for our domain
2448 * @param ads connection to ads server
2449 * @param sid Pointer to domain sid
2450 * @return status of search
2452 ADS_STATUS
ads_domain_sid(ADS_STRUCT
*ads
, DOM_SID
*sid
)
2454 const char *attrs
[] = {"objectSid", NULL
};
2458 rc
= ads_do_search_retry(ads
, ads
->config
.bind_path
, LDAP_SCOPE_BASE
, "(objectclass=*)",
2460 if (!ADS_ERR_OK(rc
)) return rc
;
2461 if (!ads_pull_sid(ads
, res
, "objectSid", sid
)) {
2462 ads_msgfree(ads
, res
);
2463 return ADS_ERROR_SYSTEM(ENOENT
);
2465 ads_msgfree(ads
, res
);
2470 /* this is rather complex - we need to find the allternate (netbios) name
2471 for the domain, but there isn't a simple query to do this. Instead
2472 we look for the principle names on the DCs account and find one that has
2473 the right form, then extract the netbios name of the domain from that
2475 NOTE! better method is this:
2477 bin/net -Uadministrator%XXXXX ads search '(&(objectclass=crossref)(dnsroot=VNET3.HOME.SAMBA.ORG))' nETBIOSName
2479 but you need to force the bind path to match the configurationNamingContext from the rootDSE
2482 ADS_STATUS
ads_workgroup_name(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char **workgroup
)
2491 const char *attrs
[] = {"servicePrincipalName", NULL
};
2494 (*workgroup
) = NULL
;
2496 asprintf(&expr
, "(&(objectclass=computer)(dnshostname=%s.%s))",
2497 ads
->config
.ldap_server_name
, ads
->config
.realm
);
2499 ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
2502 rc
= ads_search(ads
, &res
, expr
, attrs
);
2505 if (!ADS_ERR_OK(rc
)) {
2509 principles
= ads_pull_strings(ads
, mem_ctx
, res
,
2510 "servicePrincipalName", &num_principals
);
2512 ads_msgfree(ads
, res
);
2515 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2518 asprintf(&prefix
, "HOST/%s.%s/",
2519 ads
->config
.ldap_server_name
,
2522 prefix_length
= strlen(prefix
);
2524 for (i
=0;principles
[i
]; i
++) {
2525 if (strnequal(principles
[i
], prefix
, prefix_length
) &&
2526 !strequal(ads
->config
.realm
, principles
[i
]+prefix_length
) &&
2527 !strchr(principles
[i
]+prefix_length
, '.')) {
2528 /* found an alternate (short) name for the domain. */
2529 DEBUG(3,("Found alternate name '%s' for realm '%s'\n",
2530 principles
[i
]+prefix_length
,
2531 ads
->config
.realm
));
2532 (*workgroup
) = talloc_strdup(mem_ctx
, principles
[i
]+prefix_length
);
2539 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);