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 */
88 srv
= SMB_STRDUP(server
);
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
= SMB_STRDUP(ads
->config
.realm
);
268 if (!ads
->auth
.kdc_server
) {
269 ads
->auth
.kdc_server
= SMB_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_P(ctx
, 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
++)
329 values
= TALLOC_ZERO_ARRAY(ctx
, struct berval
*, i
+1);
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
++)
349 values
= TALLOC_ZERO_ARRAY(ctx
, char *, i
+1);
350 if (!values
) return NULL
;
352 for (i
=0; in_vals
[i
]; i
++) {
353 push_utf8_talloc(ctx
, &values
[i
], in_vals
[i
]);
359 Pull a (char *) array out of a UTF8-encoded values list
361 static char **ads_pull_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
366 if (!in_vals
) return NULL
;
367 for (i
=0; in_vals
[i
]; i
++)
369 values
= TALLOC_ZERO_ARRAY(ctx
, char *, i
+1);
370 if (!values
) return NULL
;
372 for (i
=0; in_vals
[i
]; i
++) {
373 pull_utf8_talloc(ctx
, &values
[i
], in_vals
[i
]);
379 * Do a search with paged results. cookie must be null on the first
380 * call, and then returned on each subsequent call. It will be null
381 * again when the entire search is complete
382 * @param ads connection to ads server
383 * @param bind_path Base dn for the search
384 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
385 * @param expr Search expression - specified in local charset
386 * @param attrs Attributes to retrieve - specified in utf8 or ascii
387 * @param res ** which will contain results - free res* with ads_msgfree()
388 * @param count Number of entries retrieved on this page
389 * @param cookie The paged results cookie to be returned on subsequent calls
390 * @return status of search
392 ADS_STATUS
ads_do_paged_search(ADS_STRUCT
*ads
, const char *bind_path
,
393 int scope
, const char *expr
,
394 const char **attrs
, void **res
,
395 int *count
, void **cookie
)
398 char *utf8_expr
, *utf8_path
, **search_attrs
;
399 LDAPControl PagedResults
, NoReferrals
, *controls
[3], **rcontrols
;
400 BerElement
*cookie_be
= NULL
;
401 struct berval
*cookie_bv
= NULL
;
406 if (!(ctx
= talloc_init("ads_do_paged_search")))
407 return ADS_ERROR(LDAP_NO_MEMORY
);
409 /* 0 means the conversion worked but the result was empty
410 so we only fail if it's -1. In any case, it always
411 at least nulls out the dest */
412 if ((push_utf8_talloc(ctx
, &utf8_expr
, expr
) == (size_t)-1) ||
413 (push_utf8_talloc(ctx
, &utf8_path
, bind_path
) == (size_t)-1)) {
418 if (!attrs
|| !(*attrs
))
421 /* This would be the utf8-encoded version...*/
422 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
423 if (!(str_list_copy(&search_attrs
, attrs
))) {
430 /* Paged results only available on ldap v3 or later */
431 ldap_get_option(ads
->ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
432 if (version
< LDAP_VERSION3
) {
433 rc
= LDAP_NOT_SUPPORTED
;
437 cookie_be
= ber_alloc_t(LBER_USE_DER
);
438 if (cookie
&& *cookie
) {
439 ber_printf(cookie_be
, "{iO}", (ber_int_t
) 1000, *cookie
);
440 ber_bvfree(*cookie
); /* don't need it from last time */
443 ber_printf(cookie_be
, "{io}", (ber_int_t
) 1000, "", 0);
445 ber_flatten(cookie_be
, &cookie_bv
);
446 PagedResults
.ldctl_oid
= ADS_PAGE_CTL_OID
;
447 PagedResults
.ldctl_iscritical
= (char) 1;
448 PagedResults
.ldctl_value
.bv_len
= cookie_bv
->bv_len
;
449 PagedResults
.ldctl_value
.bv_val
= cookie_bv
->bv_val
;
451 NoReferrals
.ldctl_oid
= ADS_NO_REFERRALS_OID
;
452 NoReferrals
.ldctl_iscritical
= (char) 0;
453 NoReferrals
.ldctl_value
.bv_len
= 0;
454 NoReferrals
.ldctl_value
.bv_val
= "";
457 controls
[0] = &NoReferrals
;
458 controls
[1] = &PagedResults
;
461 /* we need to disable referrals as the openldap libs don't
462 handle them and paged results at the same time. Using them
463 together results in the result record containing the server
464 page control being removed from the result list (tridge/jmcd)
466 leaving this in despite the control that says don't generate
467 referrals, in case the server doesn't support it (jmcd)
469 ldap_set_option(ads
->ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
471 rc
= ldap_search_ext_s(ads
->ld
, utf8_path
, scope
, utf8_expr
,
472 search_attrs
, 0, controls
,
473 NULL
, NULL
, LDAP_NO_LIMIT
, (LDAPMessage
**)res
);
475 ber_free(cookie_be
, 1);
476 ber_bvfree(cookie_bv
);
479 DEBUG(3,("ldap_search_ext_s(%s) -> %s\n", expr
, ldap_err2string(rc
)));
483 rc
= ldap_parse_result(ads
->ld
, *res
, NULL
, NULL
, NULL
,
484 NULL
, &rcontrols
, 0);
490 for (i
=0; rcontrols
[i
]; i
++) {
491 if (strcmp(ADS_PAGE_CTL_OID
, rcontrols
[i
]->ldctl_oid
) == 0) {
492 cookie_be
= ber_init(&rcontrols
[i
]->ldctl_value
);
493 ber_scanf(cookie_be
,"{iO}", (ber_int_t
*) count
,
495 /* the berval is the cookie, but must be freed when
497 if (cookie_bv
->bv_len
) /* still more to do */
498 *cookie
=ber_bvdup(cookie_bv
);
501 ber_bvfree(cookie_bv
);
502 ber_free(cookie_be
, 1);
506 ldap_controls_free(rcontrols
);
510 /* if/when we decide to utf8-encode attrs, take out this next line */
511 str_list_free(&search_attrs
);
513 return ADS_ERROR(rc
);
518 * Get all results for a search. This uses ads_do_paged_search() to return
519 * all entries in a large search.
520 * @param ads connection to ads server
521 * @param bind_path Base dn for the search
522 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
523 * @param expr Search expression
524 * @param attrs Attributes to retrieve
525 * @param res ** which will contain results - free res* with ads_msgfree()
526 * @return status of search
528 ADS_STATUS
ads_do_search_all(ADS_STRUCT
*ads
, const char *bind_path
,
529 int scope
, const char *expr
,
530 const char **attrs
, void **res
)
537 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
, res
,
540 if (!ADS_ERR_OK(status
)) return status
;
545 LDAPMessage
*msg
, *next
;
547 status2
= ads_do_paged_search(ads
, bind_path
, scope
, expr
,
548 attrs
, &res2
, &count
, &cookie
);
550 if (!ADS_ERR_OK(status2
)) break;
552 /* this relies on the way that ldap_add_result_entry() works internally. I hope
553 that this works on all ldap libs, but I have only tested with openldap */
554 for (msg
= ads_first_entry(ads
, res2
); msg
; msg
= next
) {
555 next
= ads_next_entry(ads
, msg
);
556 ldap_add_result_entry((LDAPMessage
**)res
, msg
);
558 /* note that we do not free res2, as the memory is now
559 part of the main returned list */
566 * Run a function on all results for a search. Uses ads_do_paged_search() and
567 * runs the function as each page is returned, using ads_process_results()
568 * @param ads connection to ads server
569 * @param bind_path Base dn for the search
570 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
571 * @param expr Search expression - specified in local charset
572 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
573 * @param fn Function which takes attr name, values list, and data_area
574 * @param data_area Pointer which is passed to function on each call
575 * @return status of search
577 ADS_STATUS
ads_do_search_all_fn(ADS_STRUCT
*ads
, const char *bind_path
,
578 int scope
, const char *expr
, const char **attrs
,
579 BOOL(*fn
)(char *, void **, void *),
587 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
, &res
,
590 if (!ADS_ERR_OK(status
)) return status
;
592 ads_process_results(ads
, res
, fn
, data_area
);
593 ads_msgfree(ads
, res
);
596 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
,
597 &res
, &count
, &cookie
);
599 if (!ADS_ERR_OK(status
)) break;
601 ads_process_results(ads
, res
, fn
, data_area
);
602 ads_msgfree(ads
, res
);
609 * Do a search with a timeout.
610 * @param ads connection to ads server
611 * @param bind_path Base dn for the search
612 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
613 * @param expr Search expression
614 * @param attrs Attributes to retrieve
615 * @param res ** which will contain results - free res* with ads_msgfree()
616 * @return status of search
618 ADS_STATUS
ads_do_search(ADS_STRUCT
*ads
, const char *bind_path
, int scope
,
620 const char **attrs
, void **res
)
622 struct timeval timeout
;
624 char *utf8_expr
, *utf8_path
, **search_attrs
= NULL
;
628 if (!(ctx
= talloc_init("ads_do_search"))) {
629 DEBUG(1,("ads_do_search: talloc_init() failed!"));
630 return ADS_ERROR(LDAP_NO_MEMORY
);
633 /* 0 means the conversion worked but the result was empty
634 so we only fail if it's negative. In any case, it always
635 at least nulls out the dest */
636 if ((push_utf8_talloc(ctx
, &utf8_expr
, expr
) == (size_t)-1) ||
637 (push_utf8_talloc(ctx
, &utf8_path
, bind_path
) == (size_t)-1)) {
638 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
643 if (!attrs
|| !(*attrs
))
646 /* This would be the utf8-encoded version...*/
647 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
648 if (!(str_list_copy(&search_attrs
, attrs
)))
650 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
656 timeout
.tv_sec
= ADS_SEARCH_TIMEOUT
;
659 /* see the note in ads_do_paged_search - we *must* disable referrals */
660 ldap_set_option(ads
->ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
662 rc
= ldap_search_ext_s(ads
->ld
, utf8_path
, scope
, utf8_expr
,
663 search_attrs
, 0, NULL
, NULL
,
664 &timeout
, LDAP_NO_LIMIT
, (LDAPMessage
**)res
);
666 if (rc
== LDAP_SIZELIMIT_EXCEEDED
) {
667 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
673 /* if/when we decide to utf8-encode attrs, take out this next line */
674 str_list_free(&search_attrs
);
675 return ADS_ERROR(rc
);
678 * Do a general ADS search
679 * @param ads connection to ads server
680 * @param res ** which will contain results - free res* with ads_msgfree()
681 * @param expr Search expression
682 * @param attrs Attributes to retrieve
683 * @return status of search
685 ADS_STATUS
ads_search(ADS_STRUCT
*ads
, void **res
,
689 return ads_do_search(ads
, ads
->config
.bind_path
, LDAP_SCOPE_SUBTREE
,
694 * Do a search on a specific DistinguishedName
695 * @param ads connection to ads server
696 * @param res ** which will contain results - free res* with ads_msgfree()
697 * @param dn DistinguishName to search
698 * @param attrs Attributes to retrieve
699 * @return status of search
701 ADS_STATUS
ads_search_dn(ADS_STRUCT
*ads
, void **res
,
705 return ads_do_search(ads
, dn
, LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, res
);
709 * Free up memory from a ads_search
710 * @param ads connection to ads server
711 * @param msg Search results to free
713 void ads_msgfree(ADS_STRUCT
*ads
, void *msg
)
720 * Free up memory from various ads requests
721 * @param ads connection to ads server
722 * @param mem Area to free
724 void ads_memfree(ADS_STRUCT
*ads
, void *mem
)
730 * Get a dn from search results
731 * @param ads connection to ads server
732 * @param msg Search result
735 char *ads_get_dn(ADS_STRUCT
*ads
, void *msg
)
737 char *utf8_dn
, *unix_dn
;
739 utf8_dn
= ldap_get_dn(ads
->ld
, msg
);
742 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
746 if (pull_utf8_allocate(&unix_dn
, utf8_dn
) == (size_t)-1) {
747 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
751 ldap_memfree(utf8_dn
);
756 * Find a machine account given a hostname
757 * @param ads connection to ads server
758 * @param res ** which will contain results - free res* with ads_msgfree()
759 * @param host Hostname to search for
760 * @return status of search
762 ADS_STATUS
ads_find_machine_acct(ADS_STRUCT
*ads
, void **res
, const char *machine
)
766 const char *attrs
[] = {"*", "nTSecurityDescriptor", NULL
};
770 /* the easiest way to find a machine account anywhere in the tree
771 is to look for hostname$ */
772 if (asprintf(&expr
, "(samAccountName=%s$)", machine
) == -1) {
773 DEBUG(1, ("asprintf failed!\n"));
774 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
777 status
= ads_search(ads
, res
, expr
, attrs
);
783 * Initialize a list of mods to be used in a modify request
784 * @param ctx An initialized TALLOC_CTX
785 * @return allocated ADS_MODLIST
787 ADS_MODLIST
ads_init_mods(TALLOC_CTX
*ctx
)
789 #define ADS_MODLIST_ALLOC_SIZE 10
792 if ((mods
= TALLOC_ZERO_ARRAY(ctx
, LDAPMod
*, ADS_MODLIST_ALLOC_SIZE
+ 1)))
793 /* -1 is safety to make sure we don't go over the end.
794 need to reset it to NULL before doing ldap modify */
795 mods
[ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
802 add an attribute to the list, with values list already constructed
804 static ADS_STATUS
ads_modlist_add(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
805 int mod_op
, const char *name
,
809 LDAPMod
**modlist
= (LDAPMod
**) *mods
;
810 struct berval
**ber_values
= NULL
;
811 char **char_values
= NULL
;
814 mod_op
= LDAP_MOD_DELETE
;
816 if (mod_op
& LDAP_MOD_BVALUES
)
817 ber_values
= ads_dup_values(ctx
,
818 (const struct berval
**)invals
);
820 char_values
= ads_push_strvals(ctx
,
821 (const char **) invals
);
824 /* find the first empty slot */
825 for (curmod
=0; modlist
[curmod
] && modlist
[curmod
] != (LDAPMod
*) -1;
827 if (modlist
[curmod
] == (LDAPMod
*) -1) {
828 if (!(modlist
= TALLOC_REALLOC_ARRAY(ctx
, modlist
, LDAPMod
*,
829 curmod
+ADS_MODLIST_ALLOC_SIZE
+1)))
830 return ADS_ERROR(LDAP_NO_MEMORY
);
831 memset(&modlist
[curmod
], 0,
832 ADS_MODLIST_ALLOC_SIZE
*sizeof(LDAPMod
*));
833 modlist
[curmod
+ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
837 if (!(modlist
[curmod
] = TALLOC_ZERO_P(ctx
, LDAPMod
)))
838 return ADS_ERROR(LDAP_NO_MEMORY
);
839 modlist
[curmod
]->mod_type
= talloc_strdup(ctx
, name
);
840 if (mod_op
& LDAP_MOD_BVALUES
) {
841 modlist
[curmod
]->mod_bvalues
= ber_values
;
842 } else if (mod_op
& LDAP_MOD_DELETE
) {
843 modlist
[curmod
]->mod_values
= NULL
;
845 modlist
[curmod
]->mod_values
= char_values
;
848 modlist
[curmod
]->mod_op
= mod_op
;
849 return ADS_ERROR(LDAP_SUCCESS
);
853 * Add a single string value to a mod list
854 * @param ctx An initialized TALLOC_CTX
855 * @param mods An initialized ADS_MODLIST
856 * @param name The attribute name to add
857 * @param val The value to add - NULL means DELETE
858 * @return ADS STATUS indicating success of add
860 ADS_STATUS
ads_mod_str(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
861 const char *name
, const char *val
)
863 const char *values
[2];
869 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
870 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
, name
,
871 (const void **) values
);
875 * Add an array of string values to a mod list
876 * @param ctx An initialized TALLOC_CTX
877 * @param mods An initialized ADS_MODLIST
878 * @param name The attribute name to add
879 * @param vals The array of string values to add - NULL means DELETE
880 * @return ADS STATUS indicating success of add
882 ADS_STATUS
ads_mod_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
883 const char *name
, const char **vals
)
886 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
887 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
,
888 name
, (const void **) vals
);
892 * Add a single ber-encoded value to a mod list
893 * @param ctx An initialized TALLOC_CTX
894 * @param mods An initialized ADS_MODLIST
895 * @param name The attribute name to add
896 * @param val The value to add - NULL means DELETE
897 * @return ADS STATUS indicating success of add
899 static ADS_STATUS
ads_mod_ber(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
900 const char *name
, const struct berval
*val
)
902 const struct berval
*values
[2];
907 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
908 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
|LDAP_MOD_BVALUES
,
909 name
, (const void **) values
);
913 * Perform an ldap modify
914 * @param ads connection to ads server
915 * @param mod_dn DistinguishedName to modify
916 * @param mods list of modifications to perform
917 * @return status of modify
919 ADS_STATUS
ads_gen_mod(ADS_STRUCT
*ads
, const char *mod_dn
, ADS_MODLIST mods
)
922 char *utf8_dn
= NULL
;
924 this control is needed to modify that contains a currently
925 non-existent attribute (but allowable for the object) to run
927 LDAPControl PermitModify
= {
928 ADS_PERMIT_MODIFY_OID
,
931 LDAPControl
*controls
[2];
933 controls
[0] = &PermitModify
;
936 if (push_utf8_allocate(&utf8_dn
, mod_dn
) == -1) {
937 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
940 /* find the end of the list, marked by NULL or -1 */
941 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
942 /* make sure the end of the list is NULL */
944 ret
= ldap_modify_ext_s(ads
->ld
, utf8_dn
,
945 (LDAPMod
**) mods
, controls
, NULL
);
947 return ADS_ERROR(ret
);
951 * Perform an ldap add
952 * @param ads connection to ads server
953 * @param new_dn DistinguishedName to add
954 * @param mods list of attributes and values for DN
955 * @return status of add
957 ADS_STATUS
ads_gen_add(ADS_STRUCT
*ads
, const char *new_dn
, ADS_MODLIST mods
)
960 char *utf8_dn
= NULL
;
962 if (push_utf8_allocate(&utf8_dn
, new_dn
) == -1) {
963 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
964 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
967 /* find the end of the list, marked by NULL or -1 */
968 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
969 /* make sure the end of the list is NULL */
972 ret
= ldap_add_s(ads
->ld
, utf8_dn
, mods
);
974 return ADS_ERROR(ret
);
978 * Delete a DistinguishedName
979 * @param ads connection to ads server
980 * @param new_dn DistinguishedName to delete
981 * @return status of delete
983 ADS_STATUS
ads_del_dn(ADS_STRUCT
*ads
, char *del_dn
)
986 char *utf8_dn
= NULL
;
987 if (push_utf8_allocate(&utf8_dn
, del_dn
) == -1) {
988 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
989 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
992 ret
= ldap_delete_s(ads
->ld
, utf8_dn
);
993 return ADS_ERROR(ret
);
997 * Build an org unit string
998 * if org unit is Computers or blank then assume a container, otherwise
999 * assume a \ separated list of organisational units
1000 * @param ads connection to ads server
1001 * @param org_unit Organizational unit
1002 * @return org unit string - caller must free
1004 char *ads_ou_string(ADS_STRUCT
*ads
, const char *org_unit
)
1008 if (!org_unit
|| !*org_unit
) {
1010 ret
= ads_default_ou_string(ads
, WELL_KNOWN_GUID_COMPUTERS
);
1012 /* samba4 might not yet respond to a wellknownobject-query */
1013 return ret
? ret
: SMB_STRDUP("cn=Computers");
1016 if (strequal(org_unit
, "Computers")) {
1017 return SMB_STRDUP("cn=Computers");
1020 return ads_build_path(org_unit
, "\\/", "ou=", 1);
1024 * Get a org unit string for a well-known GUID
1025 * @param ads connection to ads server
1026 * @param wknguid Well known GUID
1027 * @return org unit string - caller must free
1029 char *ads_default_ou_string(ADS_STRUCT
*ads
, const char *wknguid
)
1033 char *base
, *wkn_dn
, *ret
, **wkn_dn_exp
, **bind_dn_exp
;
1034 const char *attrs
[] = {"distinguishedName", NULL
};
1035 int new_ln
, wkn_ln
, bind_ln
, i
;
1037 if (wknguid
== NULL
) {
1041 if (asprintf(&base
, "<WKGUID=%s,%s>", wknguid
, ads
->config
.bind_path
) == -1) {
1042 DEBUG(1, ("asprintf failed!\n"));
1046 status
= ads_search_dn(ads
, &res
, base
, attrs
);
1047 if (!ADS_ERR_OK(status
)) {
1048 DEBUG(1,("Failed while searching for: %s\n", base
));
1053 if (ads_count_replies(ads
, res
) != 1) {
1057 /* substitute the bind-path from the well-known-guid-search result */
1058 wkn_dn
= ads_get_dn(ads
, res
);
1059 wkn_dn_exp
= ldap_explode_dn(wkn_dn
, 0);
1060 bind_dn_exp
= ldap_explode_dn(ads
->config
.bind_path
, 0);
1062 for (wkn_ln
=0; wkn_dn_exp
[wkn_ln
]; wkn_ln
++)
1064 for (bind_ln
=0; bind_dn_exp
[bind_ln
]; bind_ln
++)
1067 new_ln
= wkn_ln
- bind_ln
;
1069 ret
= wkn_dn_exp
[0];
1071 for (i
=1; i
< new_ln
; i
++) {
1073 asprintf(&s
, "%s,%s", ret
, wkn_dn_exp
[i
]);
1074 ret
= SMB_STRDUP(s
);
1082 * Adds (appends) an item to an attribute array, rather then
1083 * replacing the whole list
1084 * @param ctx An initialized TALLOC_CTX
1085 * @param mods An initialized ADS_MODLIST
1086 * @param name name of the ldap attribute to append to
1087 * @param vals an array of values to add
1088 * @return status of addition
1091 ADS_STATUS
ads_add_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1092 const char *name
, const char **vals
)
1094 return ads_modlist_add(ctx
, mods
, LDAP_MOD_ADD
, name
, (const void **) vals
);
1098 * Determines the computer account's current KVNO via an LDAP lookup
1099 * @param ads An initialized ADS_STRUCT
1100 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1101 * @return the kvno for the computer account, or -1 in case of a failure.
1104 uint32
ads_get_kvno(ADS_STRUCT
*ads
, const char *machine_name
)
1106 LDAPMessage
*res
= NULL
;
1107 uint32 kvno
= (uint32
)-1; /* -1 indicates a failure */
1109 const char *attrs
[] = {"msDS-KeyVersionNumber", NULL
};
1110 char *dn_string
= NULL
;
1111 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1113 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name
));
1114 if (asprintf(&filter
, "(samAccountName=%s$)", machine_name
) == -1) {
1117 ret
= ads_search(ads
, (void**) &res
, filter
, attrs
);
1119 if (!ADS_ERR_OK(ret
) && ads_count_replies(ads
, res
)) {
1120 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name
));
1121 ads_msgfree(ads
, res
);
1125 dn_string
= ads_get_dn(ads
, res
);
1127 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1128 ads_msgfree(ads
, res
);
1131 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string
));
1132 ads_memfree(ads
, dn_string
);
1134 /* ---------------------------------------------------------
1135 * 0 is returned as a default KVNO from this point on...
1136 * This is done because Windows 2000 does not support key
1137 * version numbers. Chances are that a failure in the next
1138 * step is simply due to Windows 2000 being used for a
1139 * domain controller. */
1142 if (!ads_pull_uint32(ads
, res
, "msDS-KeyVersionNumber", &kvno
)) {
1143 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1144 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1145 ads_msgfree(ads
, res
);
1150 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno
));
1151 ads_msgfree(ads
, res
);
1156 * This clears out all registered spn's for a given hostname
1157 * @param ads An initilaized ADS_STRUCT
1158 * @param machine_name the NetBIOS name of the computer.
1159 * @return 0 upon success, non-zero otherwise.
1162 ADS_STATUS
ads_clear_service_principal_names(ADS_STRUCT
*ads
, const char *machine_name
)
1165 LDAPMessage
*res
= NULL
;
1167 const char *servicePrincipalName
[1] = {NULL
};
1168 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1169 char *dn_string
= NULL
;
1171 ret
= ads_find_machine_acct(ads
, (void **)&res
, machine_name
);
1172 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1173 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name
));
1174 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name
));
1175 ads_msgfree(ads
, res
);
1176 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1179 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name
));
1180 ctx
= talloc_init("ads_clear_service_principal_names");
1182 ads_msgfree(ads
, res
);
1183 return ADS_ERROR(LDAP_NO_MEMORY
);
1186 if (!(mods
= ads_init_mods(ctx
))) {
1187 talloc_destroy(ctx
);
1188 ads_msgfree(ads
, res
);
1189 return ADS_ERROR(LDAP_NO_MEMORY
);
1191 ret
= ads_mod_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1192 if (!ADS_ERR_OK(ret
)) {
1193 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1194 ads_msgfree(ads
, res
);
1195 talloc_destroy(ctx
);
1198 dn_string
= ads_get_dn(ads
, res
);
1200 talloc_destroy(ctx
);
1201 ads_msgfree(ads
, res
);
1202 return ADS_ERROR(LDAP_NO_MEMORY
);
1204 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1205 ads_memfree(ads
,dn_string
);
1206 if (!ADS_ERR_OK(ret
)) {
1207 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1209 ads_msgfree(ads
, res
);
1210 talloc_destroy(ctx
);
1214 ads_msgfree(ads
, res
);
1215 talloc_destroy(ctx
);
1220 * This adds a service principal name to an existing computer account
1221 * (found by hostname) in AD.
1222 * @param ads An initialized ADS_STRUCT
1223 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1224 * @param spn A string of the service principal to add, i.e. 'host'
1225 * @return 0 upon sucess, or non-zero if a failure occurs
1228 ADS_STATUS
ads_add_service_principal_name(ADS_STRUCT
*ads
, const char *machine_name
, const char *spn
)
1232 LDAPMessage
*res
= NULL
;
1233 char *host_spn
, *host_upn
, *psp1
, *psp2
, *psp3
;
1236 char *dn_string
= NULL
;
1237 const char *servicePrincipalName
[4] = {NULL
, NULL
, NULL
, NULL
};
1239 ret
= ads_find_machine_acct(ads
, (void **)&res
, machine_name
);
1240 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1241 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1243 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1244 spn
, machine_name
, ads
->config
.realm
));
1245 ads_msgfree(ads
, res
);
1246 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1249 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name
));
1250 if (!(ctx
= talloc_init("ads_add_service_principal_name"))) {
1251 ads_msgfree(ads
, res
);
1252 return ADS_ERROR(LDAP_NO_MEMORY
);
1255 name_to_fqdn(my_fqdn
, machine_name
);
1256 strlower_m(my_fqdn
);
1258 if (!(host_spn
= talloc_asprintf(ctx
, "HOST/%s", my_fqdn
))) {
1259 talloc_destroy(ctx
);
1260 ads_msgfree(ads
, res
);
1261 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1263 if (!(host_upn
= talloc_asprintf(ctx
, "%s@%s", host_spn
, ads
->config
.realm
))) {
1264 talloc_destroy(ctx
);
1265 ads_msgfree(ads
, res
);
1266 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1269 /* Add the extra principal */
1270 psp1
= talloc_asprintf(ctx
, "%s/%s", spn
, machine_name
);
1272 strlower_m(&psp1
[strlen(spn
)]);
1273 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp1
, machine_name
));
1274 servicePrincipalName
[0] = psp1
;
1275 psp2
= talloc_asprintf(ctx
, "%s/%s.%s", spn
, machine_name
, ads
->config
.realm
);
1277 strlower_m(&psp2
[strlen(spn
)]);
1278 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp2
, machine_name
));
1279 servicePrincipalName
[1] = psp2
;
1281 /* Add another principal in case the realm != the DNS domain, so that
1282 * the KDC doesn't send "server principal unknown" errors to clients
1283 * which use the DNS name in determining service principal names. */
1284 psp3
= talloc_asprintf(ctx
, "%s/%s", spn
, my_fqdn
);
1286 strlower_m(&psp3
[strlen(spn
)]);
1287 if (strcmp(psp2
, psp3
) != 0) {
1288 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp3
, machine_name
));
1289 servicePrincipalName
[2] = psp3
;
1292 if (!(mods
= ads_init_mods(ctx
))) {
1293 talloc_destroy(ctx
);
1294 ads_msgfree(ads
, res
);
1295 return ADS_ERROR(LDAP_NO_MEMORY
);
1297 ret
= ads_add_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1298 if (!ADS_ERR_OK(ret
)) {
1299 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1300 talloc_destroy(ctx
);
1301 ads_msgfree(ads
, res
);
1304 dn_string
= ads_get_dn(ads
, res
);
1306 talloc_destroy(ctx
);
1307 ads_msgfree(ads
, res
);
1308 return ADS_ERROR(LDAP_NO_MEMORY
);
1310 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1311 ads_memfree(ads
,dn_string
);
1312 if (!ADS_ERR_OK(ret
)) {
1313 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1314 talloc_destroy(ctx
);
1315 ads_msgfree(ads
, res
);
1319 talloc_destroy(ctx
);
1320 ads_msgfree(ads
, res
);
1325 * adds a machine account to the ADS server
1326 * @param ads An intialized ADS_STRUCT
1327 * @param machine_name - the NetBIOS machine name of this account.
1328 * @param account_type A number indicating the type of account to create
1329 * @param org_unit The LDAP path in which to place this account
1330 * @return 0 upon success, or non-zero otherwise
1333 static ADS_STATUS
ads_add_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
1334 uint32 account_type
,
1335 const char *org_unit
)
1337 ADS_STATUS ret
, status
;
1338 char *host_spn
, *host_upn
, *new_dn
, *samAccountName
, *controlstr
;
1341 const char *objectClass
[] = {"top", "person", "organizationalPerson",
1342 "user", "computer", NULL
};
1343 const char *servicePrincipalName
[7] = {NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
};
1344 char *psp
, *psp2
, *psp3
, *psp4
;
1345 unsigned acct_control
;
1348 LDAPMessage
*res
= NULL
;
1351 if (!(ctx
= talloc_init("ads_add_machine_acct")))
1352 return ADS_ERROR(LDAP_NO_MEMORY
);
1354 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1356 name_to_fqdn(my_fqdn
, machine_name
);
1358 status
= ads_find_machine_acct(ads
, (void **)&res
, machine_name
);
1359 if (ADS_ERR_OK(status
) && ads_count_replies(ads
, res
) == 1) {
1360 char *dn_string
= ads_get_dn(ads
, res
);
1362 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
1365 new_dn
= talloc_strdup(ctx
, dn_string
);
1366 ads_memfree(ads
,dn_string
);
1367 DEBUG(0, ("ads_add_machine_acct: Host account for %s already exists - modifying old account\n",
1371 char *ou_str
= ads_ou_string(ads
,org_unit
);
1373 DEBUG(1, ("ads_add_machine_acct: ads_ou_string returned NULL (malloc failure?)\n"));
1376 new_dn
= talloc_asprintf(ctx
, "cn=%s,%s,%s", machine_name
, ou_str
,
1377 ads
->config
.bind_path
);
1386 if (!(host_spn
= talloc_asprintf(ctx
, "HOST/%s", machine_name
)))
1388 if (!(host_upn
= talloc_asprintf(ctx
, "%s@%s", host_spn
, ads
->config
.realm
)))
1390 servicePrincipalName
[0] = talloc_asprintf(ctx
, "HOST/%s", machine_name
);
1391 psp
= talloc_asprintf(ctx
, "HOST/%s.%s",
1394 strlower_m(&psp
[5]);
1395 servicePrincipalName
[1] = psp
;
1396 servicePrincipalName
[2] = talloc_asprintf(ctx
, "CIFS/%s", machine_name
);
1397 psp2
= talloc_asprintf(ctx
, "CIFS/%s.%s",
1400 strlower_m(&psp2
[5]);
1401 servicePrincipalName
[3] = psp2
;
1403 /* Ensure servicePrincipalName[4] and [5] are unique. */
1404 strlower_m(my_fqdn
);
1405 psp3
= talloc_asprintf(ctx
, "CIFS/%s", my_fqdn
);
1406 strlower_m(&psp3
[5]);
1409 for (i
= 0; i
< next_spn
; i
++) {
1410 if (strequal(servicePrincipalName
[i
], psp3
))
1413 if (i
== next_spn
) {
1414 servicePrincipalName
[next_spn
++] = psp3
;
1417 psp4
= talloc_asprintf(ctx
, "HOST/%s", my_fqdn
);
1418 strlower_m(&psp4
[5]);
1419 for (i
= 0; i
< next_spn
; i
++) {
1420 if (strequal(servicePrincipalName
[i
], psp3
))
1423 if (i
== next_spn
) {
1424 servicePrincipalName
[next_spn
++] = psp4
;
1427 if (!(samAccountName
= talloc_asprintf(ctx
, "%s$", machine_name
))) {
1431 acct_control
= account_type
| UF_DONT_EXPIRE_PASSWD
;
1432 #ifndef ENCTYPE_ARCFOUR_HMAC
1433 acct_control
|= UF_USE_DES_KEY_ONLY
;
1436 if (!(controlstr
= talloc_asprintf(ctx
, "%u", acct_control
))) {
1440 if (!(mods
= ads_init_mods(ctx
))) {
1445 ads_mod_str(ctx
, &mods
, "cn", machine_name
);
1446 ads_mod_str(ctx
, &mods
, "sAMAccountName", samAccountName
);
1447 ads_mod_str(ctx
, &mods
, "userAccountControl", controlstr
);
1448 ads_mod_strlist(ctx
, &mods
, "objectClass", objectClass
);
1450 ads_mod_str(ctx
, &mods
, "dNSHostName", my_fqdn
);
1451 ads_mod_str(ctx
, &mods
, "userPrincipalName", host_upn
);
1452 ads_mod_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1453 ads_mod_str(ctx
, &mods
, "operatingSystem", "Samba");
1454 ads_mod_str(ctx
, &mods
, "operatingSystemVersion", SAMBA_VERSION_STRING
);
1457 ret
= ads_gen_add(ads
, new_dn
, mods
);
1459 ret
= ads_gen_mod(ads
, new_dn
, mods
);
1462 if (!ADS_ERR_OK(ret
)) {
1466 /* Do not fail if we can't set security descriptor
1467 * it shouldn't be mandatory and probably we just
1468 * don't have enough rights to do it.
1471 status
= ads_set_machine_sd(ads
, machine_name
, new_dn
);
1473 if (!ADS_ERR_OK(status
)) {
1474 DEBUG(0, ("Warning: ads_set_machine_sd: %s\n",
1475 ads_errstr(status
)));
1479 ads_msgfree(ads
, res
);
1480 talloc_destroy(ctx
);
1485 dump a binary result from ldap
1487 static void dump_binary(const char *field
, struct berval
**values
)
1490 for (i
=0; values
[i
]; i
++) {
1491 printf("%s: ", field
);
1492 for (j
=0; j
<values
[i
]->bv_len
; j
++) {
1493 printf("%02X", (unsigned char)values
[i
]->bv_val
[j
]);
1499 static void dump_guid(const char *field
, struct berval
**values
)
1503 for (i
=0; values
[i
]; i
++) {
1504 memcpy(guid
.info
, values
[i
]->bv_val
, sizeof(guid
.info
));
1505 printf("%s: %s\n", field
,
1506 smb_uuid_string_static(smb_uuid_unpack_static(guid
)));
1511 dump a sid result from ldap
1513 static void dump_sid(const char *field
, struct berval
**values
)
1516 for (i
=0; values
[i
]; i
++) {
1518 sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &sid
);
1519 printf("%s: %s\n", field
, sid_string_static(&sid
));
1524 dump ntSecurityDescriptor
1526 static void dump_sd(const char *filed
, struct berval
**values
)
1531 TALLOC_CTX
*ctx
= 0;
1533 if (!(ctx
= talloc_init("sec_io_desc")))
1537 prs_init(&ps
, values
[0]->bv_len
, ctx
, UNMARSHALL
);
1538 prs_copy_data_in(&ps
, values
[0]->bv_val
, values
[0]->bv_len
);
1539 prs_set_offset(&ps
,0);
1542 if (!sec_io_desc("sd", &psd
, &ps
, 1)) {
1544 talloc_destroy(ctx
);
1547 if (psd
) ads_disp_sd(psd
);
1550 talloc_destroy(ctx
);
1554 dump a string result from ldap
1556 static void dump_string(const char *field
, char **values
)
1559 for (i
=0; values
[i
]; i
++) {
1560 printf("%s: %s\n", field
, values
[i
]);
1565 dump a field from LDAP on stdout
1569 static BOOL
ads_dump_field(char *field
, void **values
, void *data_area
)
1574 void (*handler
)(const char *, struct berval
**);
1576 {"objectGUID", False
, dump_guid
},
1577 {"nTSecurityDescriptor", False
, dump_sd
},
1578 {"dnsRecord", False
, dump_binary
},
1579 {"objectSid", False
, dump_sid
},
1580 {"tokenGroups", False
, dump_sid
},
1585 if (!field
) { /* must be end of an entry */
1590 for (i
=0; handlers
[i
].name
; i
++) {
1591 if (StrCaseCmp(handlers
[i
].name
, field
) == 0) {
1592 if (!values
) /* first time, indicate string or not */
1593 return handlers
[i
].string
;
1594 handlers
[i
].handler(field
, (struct berval
**) values
);
1598 if (!handlers
[i
].name
) {
1599 if (!values
) /* first time, indicate string conversion */
1601 dump_string(field
, (char **)values
);
1607 * Dump a result from LDAP on stdout
1608 * used for debugging
1609 * @param ads connection to ads server
1610 * @param res Results to dump
1613 void ads_dump(ADS_STRUCT
*ads
, void *res
)
1615 ads_process_results(ads
, res
, ads_dump_field
, NULL
);
1619 * Walk through results, calling a function for each entry found.
1620 * The function receives a field name, a berval * array of values,
1621 * and a data area passed through from the start. The function is
1622 * called once with null for field and values at the end of each
1624 * @param ads connection to ads server
1625 * @param res Results to process
1626 * @param fn Function for processing each result
1627 * @param data_area user-defined area to pass to function
1629 void ads_process_results(ADS_STRUCT
*ads
, void *res
,
1630 BOOL(*fn
)(char *, void **, void *),
1636 if (!(ctx
= talloc_init("ads_process_results")))
1639 for (msg
= ads_first_entry(ads
, res
); msg
;
1640 msg
= ads_next_entry(ads
, msg
)) {
1644 for (utf8_field
=ldap_first_attribute(ads
->ld
,
1645 (LDAPMessage
*)msg
,&b
);
1647 utf8_field
=ldap_next_attribute(ads
->ld
,
1648 (LDAPMessage
*)msg
,b
)) {
1649 struct berval
**ber_vals
;
1650 char **str_vals
, **utf8_vals
;
1654 pull_utf8_talloc(ctx
, &field
, utf8_field
);
1655 string
= fn(field
, NULL
, data_area
);
1658 utf8_vals
= ldap_get_values(ads
->ld
,
1659 (LDAPMessage
*)msg
, field
);
1660 str_vals
= ads_pull_strvals(ctx
,
1661 (const char **) utf8_vals
);
1662 fn(field
, (void **) str_vals
, data_area
);
1663 ldap_value_free(utf8_vals
);
1665 ber_vals
= ldap_get_values_len(ads
->ld
,
1666 (LDAPMessage
*)msg
, field
);
1667 fn(field
, (void **) ber_vals
, data_area
);
1669 ldap_value_free_len(ber_vals
);
1671 ldap_memfree(utf8_field
);
1674 talloc_destroy_pool(ctx
);
1675 fn(NULL
, NULL
, data_area
); /* completed an entry */
1678 talloc_destroy(ctx
);
1682 * count how many replies are in a LDAPMessage
1683 * @param ads connection to ads server
1684 * @param res Results to count
1685 * @return number of replies
1687 int ads_count_replies(ADS_STRUCT
*ads
, void *res
)
1689 return ldap_count_entries(ads
->ld
, (LDAPMessage
*)res
);
1693 * Join a machine to a realm
1694 * Creates the machine account and sets the machine password
1695 * @param ads connection to ads server
1696 * @param machine name of host to add
1697 * @param org_unit Organizational unit to place machine in
1698 * @return status of join
1700 ADS_STATUS
ads_join_realm(ADS_STRUCT
*ads
, const char *machine_name
,
1701 uint32 account_type
, const char *org_unit
)
1704 LDAPMessage
*res
= NULL
;
1707 /* machine name must be lowercase */
1708 machine
= SMB_STRDUP(machine_name
);
1709 strlower_m(machine
);
1712 status = ads_find_machine_acct(ads, (void **)&res, machine);
1713 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1714 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
1715 status = ads_leave_realm(ads, machine);
1716 if (!ADS_ERR_OK(status)) {
1717 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
1718 machine, ads->config.realm));
1724 status
= ads_add_machine_acct(ads
, machine
, account_type
, org_unit
);
1725 if (!ADS_ERR_OK(status
)) {
1726 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine
, ads_errstr(status
)));
1731 status
= ads_find_machine_acct(ads
, (void **)&res
, machine
);
1732 if (!ADS_ERR_OK(status
)) {
1733 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine
));
1739 ads_msgfree(ads
, res
);
1745 * Delete a machine from the realm
1746 * @param ads connection to ads server
1747 * @param hostname Machine to remove
1748 * @return status of delete
1750 ADS_STATUS
ads_leave_realm(ADS_STRUCT
*ads
, const char *hostname
)
1754 char *hostnameDN
, *host
;
1757 /* hostname must be lowercase */
1758 host
= SMB_STRDUP(hostname
);
1761 status
= ads_find_machine_acct(ads
, &res
, host
);
1762 if (!ADS_ERR_OK(status
)) {
1763 DEBUG(0, ("Host account for %s does not exist.\n", host
));
1767 msg
= ads_first_entry(ads
, res
);
1769 return ADS_ERROR_SYSTEM(ENOENT
);
1772 hostnameDN
= ads_get_dn(ads
, (LDAPMessage
*)msg
);
1773 rc
= ldap_delete_s(ads
->ld
, hostnameDN
);
1774 ads_memfree(ads
, hostnameDN
);
1775 if (rc
!= LDAP_SUCCESS
) {
1776 return ADS_ERROR(rc
);
1779 status
= ads_find_machine_acct(ads
, &res
, host
);
1780 if (ADS_ERR_OK(status
) && ads_count_replies(ads
, res
) == 1) {
1781 DEBUG(0, ("Failed to remove host account.\n"));
1791 * add machine account to existing security descriptor
1792 * @param ads connection to ads server
1793 * @param hostname machine to add
1794 * @param dn DN of security descriptor
1797 ADS_STATUS
ads_set_machine_sd(ADS_STRUCT
*ads
, const char *hostname
, char *dn
)
1799 const char *attrs
[] = {"nTSecurityDescriptor", "objectSid", 0};
1802 struct berval bval
= {0, NULL
};
1804 char *escaped_hostname
= escape_ldap_string_alloc(hostname
);
1806 LDAPMessage
*res
= 0;
1807 LDAPMessage
*msg
= 0;
1808 ADS_MODLIST mods
= 0;
1813 SEC_DESC
*psd
= NULL
;
1814 TALLOC_CTX
*ctx
= NULL
;
1816 /* Avoid segmentation fault in prs_mem_free if
1817 * we have to bail out before prs_init */
1818 ps_wire
.is_dynamic
= False
;
1820 if (!ads
) return ADS_ERROR(LDAP_SERVER_DOWN
);
1822 ret
= ADS_ERROR(LDAP_SUCCESS
);
1824 if (!escaped_hostname
) {
1825 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1828 if (asprintf(&expr
, "(samAccountName=%s$)", escaped_hostname
) == -1) {
1829 DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n"));
1830 SAFE_FREE(escaped_hostname
);
1831 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1834 SAFE_FREE(escaped_hostname
);
1836 ret
= ads_search(ads
, (void *) &res
, expr
, attrs
);
1838 if (!ADS_ERR_OK(ret
)) return ret
;
1840 if ( !(msg
= ads_first_entry(ads
, res
) )) {
1841 ret
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
1842 goto ads_set_sd_error
;
1845 if (!ads_pull_sid(ads
, msg
, attrs
[1], &sid
)) {
1846 ret
= ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
1847 goto ads_set_sd_error
;
1850 if (!(ctx
= talloc_init("sec_io_desc"))) {
1851 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1852 goto ads_set_sd_error
;
1855 if (!ads_pull_sd(ads
, ctx
, msg
, attrs
[0], &psd
)) {
1856 ret
= ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
1857 goto ads_set_sd_error
;
1860 status
= sec_desc_add_sid(ctx
, &psd
, &sid
, SEC_RIGHTS_FULL_CTRL
, &sd_size
);
1862 if (!NT_STATUS_IS_OK(status
)) {
1863 ret
= ADS_ERROR_NT(status
);
1864 goto ads_set_sd_error
;
1867 if (!prs_init(&ps_wire
, sd_size
, ctx
, MARSHALL
)) {
1868 ret
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1871 if (!sec_io_desc("sd_wire", &psd
, &ps_wire
, 1)) {
1872 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1873 goto ads_set_sd_error
;
1877 file_save("/tmp/sec_desc.new", ps_wire
.data_p
, sd_size
);
1879 if (!(mods
= ads_init_mods(ctx
))) return ADS_ERROR(LDAP_NO_MEMORY
);
1881 bval
.bv_len
= prs_offset(&ps_wire
);
1882 bval
.bv_val
= TALLOC(ctx
, bval
.bv_len
);
1884 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1885 goto ads_set_sd_error
;
1888 prs_set_offset(&ps_wire
, 0);
1890 if (!prs_copy_data_out(bval
.bv_val
, &ps_wire
, bval
.bv_len
)) {
1891 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1892 goto ads_set_sd_error
;
1895 ret
= ads_mod_ber(ctx
, &mods
, attrs
[0], &bval
);
1896 if (ADS_ERR_OK(ret
)) {
1897 ret
= ads_gen_mod(ads
, dn
, mods
);
1901 ads_msgfree(ads
, res
);
1902 prs_mem_free(&ps_wire
);
1903 talloc_destroy(ctx
);
1908 * pull the first entry from a ADS result
1909 * @param ads connection to ads server
1910 * @param res Results of search
1911 * @return first entry from result
1913 void *ads_first_entry(ADS_STRUCT
*ads
, void *res
)
1915 return (void *)ldap_first_entry(ads
->ld
, (LDAPMessage
*)res
);
1919 * pull the next entry from a ADS result
1920 * @param ads connection to ads server
1921 * @param res Results of search
1922 * @return next entry from result
1924 void *ads_next_entry(ADS_STRUCT
*ads
, void *res
)
1926 return (void *)ldap_next_entry(ads
->ld
, (LDAPMessage
*)res
);
1930 * pull a single string from a ADS result
1931 * @param ads connection to ads server
1932 * @param mem_ctx TALLOC_CTX to use for allocating result string
1933 * @param msg Results of search
1934 * @param field Attribute to retrieve
1935 * @return Result string in talloc context
1937 char *ads_pull_string(ADS_STRUCT
*ads
,
1938 TALLOC_CTX
*mem_ctx
, void *msg
, const char *field
)
1945 values
= ldap_get_values(ads
->ld
, msg
, field
);
1950 rc
= pull_utf8_talloc(mem_ctx
, &ux_string
,
1952 if (rc
!= (size_t)-1)
1956 ldap_value_free(values
);
1961 * pull an array of strings from a ADS result
1962 * @param ads connection to ads server
1963 * @param mem_ctx TALLOC_CTX to use for allocating result string
1964 * @param msg Results of search
1965 * @param field Attribute to retrieve
1966 * @return Result strings in talloc context
1968 char **ads_pull_strings(ADS_STRUCT
*ads
,
1969 TALLOC_CTX
*mem_ctx
, void *msg
, const char *field
,
1976 values
= ldap_get_values(ads
->ld
, msg
, field
);
1980 *num_values
= ldap_count_values(values
);
1982 ret
= TALLOC_ARRAY(mem_ctx
, char *, *num_values
+ 1);
1984 ldap_value_free(values
);
1988 for (i
=0;i
<*num_values
;i
++) {
1989 if (pull_utf8_talloc(mem_ctx
, &ret
[i
], values
[i
]) == -1) {
1990 ldap_value_free(values
);
1996 ldap_value_free(values
);
2001 * pull an array of strings from a ADS result
2002 * (handle large multivalue attributes with range retrieval)
2003 * @param ads connection to ads server
2004 * @param mem_ctx TALLOC_CTX to use for allocating result string
2005 * @param msg Results of search
2006 * @param field Attribute to retrieve
2007 * @param current_strings strings returned by a previous call to this function
2008 * @param next_attribute The next query should ask for this attribute
2009 * @param num_values How many values did we get this time?
2010 * @param more_values Are there more values to get?
2011 * @return Result strings in talloc context
2013 char **ads_pull_strings_range(ADS_STRUCT
*ads
,
2014 TALLOC_CTX
*mem_ctx
,
2015 void *msg
, const char *field
,
2016 char **current_strings
,
2017 const char **next_attribute
,
2018 size_t *num_strings
,
2022 char *expected_range_attrib
, *range_attr
;
2023 BerElement
*ptr
= NULL
;
2026 size_t num_new_strings
;
2027 unsigned long int range_start
;
2028 unsigned long int range_end
;
2030 /* we might have been given the whole lot anyway */
2031 if ((strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
, num_strings
))) {
2032 *more_strings
= False
;
2036 expected_range_attrib
= talloc_asprintf(mem_ctx
, "%s;Range=", field
);
2038 /* look for Range result */
2039 for (attr
= ldap_first_attribute(ads
->ld
, (LDAPMessage
*)msg
, &ptr
);
2041 attr
= ldap_next_attribute(ads
->ld
, (LDAPMessage
*)msg
, ptr
)) {
2042 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2043 if (strnequal(attr
, expected_range_attrib
, strlen(expected_range_attrib
))) {
2051 /* nothing here - this field is just empty */
2052 *more_strings
= False
;
2056 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-%lu",
2057 &range_start
, &range_end
) == 2) {
2058 *more_strings
= True
;
2060 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-*",
2061 &range_start
) == 1) {
2062 *more_strings
= False
;
2064 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2066 ldap_memfree(range_attr
);
2067 *more_strings
= False
;
2072 if ((*num_strings
) != range_start
) {
2073 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2074 " - aborting range retreival\n",
2075 range_attr
, *num_strings
+ 1, range_start
));
2076 ldap_memfree(range_attr
);
2077 *more_strings
= False
;
2081 new_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, range_attr
, &num_new_strings
);
2083 if (*more_strings
&& ((*num_strings
+ num_new_strings
) != (range_end
+ 1))) {
2084 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2085 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2086 range_attr
, (unsigned long int)range_end
- range_start
+ 1,
2087 (unsigned long int)num_new_strings
));
2088 ldap_memfree(range_attr
);
2089 *more_strings
= False
;
2093 strings
= TALLOC_REALLOC_ARRAY(mem_ctx
, current_strings
, char *,
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_ARRAY(mem_ctx
, 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
= SMB_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
= SMB_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
);