source: branches/samba-3.3.x/source/libads/ldap.c@ 206

Last change on this file since 206 was 206, checked in by Herwig Bauernfeind, 16 years ago

Import Samba 3.3 branch at 3.0.0 level (psmedley's port)

File size: 98.5 KB
Line 
1/*
2 Unix SMB/CIFS implementation.
3 ads (active directory) utility library
4 Copyright (C) Andrew Tridgell 2001
5 Copyright (C) Remus Koos 2001
6 Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
7 Copyright (C) Guenther Deschner 2005
8 Copyright (C) Gerald Carter 2006
9
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
22*/
23
24#include "includes.h"
25#include "lib/ldb/include/includes.h"
26
27#ifdef HAVE_LDAP
28
29/**
30 * @file ldap.c
31 * @brief basic ldap client-side routines for ads server communications
32 *
33 * The routines contained here should do the necessary ldap calls for
34 * ads setups.
35 *
36 * Important note: attribute names passed into ads_ routines must
37 * already be in UTF-8 format. We do not convert them because in almost
38 * all cases, they are just ascii (which is represented with the same
39 * codepoints in UTF-8). This may have to change at some point
40 **/
41
42
43#define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
44
45static SIG_ATOMIC_T gotalarm;
46
47/***************************************************************
48 Signal function to tell us we timed out.
49****************************************************************/
50
51static void gotalarm_sig(void)
52{
53 gotalarm = 1;
54}
55
56 LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
57{
58 LDAP *ldp = NULL;
59
60
61 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
62 "%u seconds\n", server, port, to));
63
64 /* Setup timeout */
65 gotalarm = 0;
66 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
67 alarm(to);
68 /* End setup timeout. */
69
70 ldp = ldap_open(server, port);
71
72 if (ldp == NULL) {
73 DEBUG(2,("Could not open connection to LDAP server %s:%d: %s\n",
74 server, port, strerror(errno)));
75 } else {
76 DEBUG(10, ("Connected to LDAP server '%s:%d'\n", server, port));
77 }
78
79 /* Teardown timeout. */
80 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
81 alarm(0);
82
83 return ldp;
84}
85
86static int ldap_search_with_timeout(LDAP *ld,
87 LDAP_CONST char *base,
88 int scope,
89 LDAP_CONST char *filter,
90 char **attrs,
91 int attrsonly,
92 LDAPControl **sctrls,
93 LDAPControl **cctrls,
94 int sizelimit,
95 LDAPMessage **res )
96{
97 struct timeval timeout;
98 int result;
99
100 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
101 timeout.tv_sec = lp_ldap_timeout();
102 timeout.tv_usec = 0;
103
104 /* Setup alarm timeout.... Do we need both of these ? JRA. */
105 gotalarm = 0;
106 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
107 alarm(lp_ldap_timeout());
108 /* End setup timeout. */
109
110 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
111 attrsonly, sctrls, cctrls, &timeout,
112 sizelimit, res);
113
114 /* Teardown timeout. */
115 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
116 alarm(0);
117
118 if (gotalarm != 0)
119 return LDAP_TIMELIMIT_EXCEEDED;
120
121 return result;
122}
123
124/**********************************************
125 Do client and server sitename match ?
126**********************************************/
127
128bool ads_sitename_match(ADS_STRUCT *ads)
129{
130 if (ads->config.server_site_name == NULL &&
131 ads->config.client_site_name == NULL ) {
132 DEBUG(10,("ads_sitename_match: both null\n"));
133 return True;
134 }
135 if (ads->config.server_site_name &&
136 ads->config.client_site_name &&
137 strequal(ads->config.server_site_name,
138 ads->config.client_site_name)) {
139 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
140 return True;
141 }
142 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
143 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
144 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
145 return False;
146}
147
148/**********************************************
149 Is this the closest DC ?
150**********************************************/
151
152bool ads_closest_dc(ADS_STRUCT *ads)
153{
154 if (ads->config.flags & NBT_SERVER_CLOSEST) {
155 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
156 return True;
157 }
158
159 /* not sure if this can ever happen */
160 if (ads_sitename_match(ads)) {
161 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
162 return True;
163 }
164
165 if (ads->config.client_site_name == NULL) {
166 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
167 return True;
168 }
169
170 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
171 ads->config.ldap_server_name));
172
173 return False;
174}
175
176
177/*
178 try a connection to a given ldap server, returning True and setting the servers IP
179 in the ads struct if successful
180 */
181static bool ads_try_connect(ADS_STRUCT *ads, const char *server, bool gc)
182{
183 char *srv;
184 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply;
185 TALLOC_CTX *mem_ctx = NULL;
186 bool ret = false;
187
188 if (!server || !*server) {
189 return False;
190 }
191
192 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
193 server, ads->server.realm));
194
195 mem_ctx = talloc_init("ads_try_connect");
196 if (!mem_ctx) {
197 DEBUG(0,("out of memory\n"));
198 return false;
199 }
200
201 /* this copes with inet_ntoa brokenness */
202
203 srv = SMB_STRDUP(server);
204
205 ZERO_STRUCT( cldap_reply );
206
207 if ( !ads_cldap_netlogon_5(mem_ctx, srv, ads->server.realm, &cldap_reply ) ) {
208 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
209 ret = false;
210 goto out;
211 }
212
213 /* Check the CLDAP reply flags */
214
215 if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
216 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
217 srv));
218 ret = false;
219 goto out;
220 }
221
222 /* Fill in the ads->config values */
223
224 SAFE_FREE(ads->config.realm);
225 SAFE_FREE(ads->config.bind_path);
226 SAFE_FREE(ads->config.ldap_server_name);
227 SAFE_FREE(ads->config.server_site_name);
228 SAFE_FREE(ads->config.client_site_name);
229 SAFE_FREE(ads->server.workgroup);
230
231 ads->config.flags = cldap_reply.server_type;
232 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.pdc_dns_name);
233 ads->config.realm = SMB_STRDUP(cldap_reply.dns_domain);
234 strupper_m(ads->config.realm);
235 ads->config.bind_path = ads_build_dn(ads->config.realm);
236 if (*cldap_reply.server_site) {
237 ads->config.server_site_name =
238 SMB_STRDUP(cldap_reply.server_site);
239 }
240 if (*cldap_reply.client_site) {
241 ads->config.client_site_name =
242 SMB_STRDUP(cldap_reply.client_site);
243 }
244 ads->server.workgroup = SMB_STRDUP(cldap_reply.domain);
245
246 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
247 if (!interpret_string_addr(&ads->ldap.ss, srv, 0)) {
248 DEBUG(1,("ads_try_connect: unable to convert %s "
249 "to an address\n",
250 srv));
251 ret = false;
252 goto out;
253 }
254
255 /* Store our site name. */
256 sitename_store( cldap_reply.domain, cldap_reply.client_site);
257 sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
258
259 ret = true;
260 out:
261 SAFE_FREE(srv);
262 TALLOC_FREE(mem_ctx);
263
264 return ret;
265}
266
267/**********************************************************************
268 Try to find an AD dc using our internal name resolution routines
269 Try the realm first and then then workgroup name if netbios is not
270 disabled
271**********************************************************************/
272
273static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
274{
275 const char *c_domain;
276 const char *c_realm;
277 int count, i=0;
278 struct ip_service *ip_list;
279 const char *realm;
280 const char *domain;
281 bool got_realm = False;
282 bool use_own_domain = False;
283 char *sitename;
284 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
285
286 /* if the realm and workgroup are both empty, assume they are ours */
287
288 /* realm */
289 c_realm = ads->server.realm;
290
291 if ( !c_realm || !*c_realm ) {
292 /* special case where no realm and no workgroup means our own */
293 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
294 use_own_domain = True;
295 c_realm = lp_realm();
296 }
297 }
298
299 if (c_realm && *c_realm)
300 got_realm = True;
301
302 /* we need to try once with the realm name and fallback to the
303 netbios domain name if we fail (if netbios has not been disabled */
304
305 if ( !got_realm && !lp_disable_netbios() ) {
306 c_realm = ads->server.workgroup;
307 if (!c_realm || !*c_realm) {
308 if ( use_own_domain )
309 c_realm = lp_workgroup();
310 }
311 }
312
313 if ( !c_realm || !*c_realm ) {
314 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
315 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
316 }
317
318 if ( use_own_domain ) {
319 c_domain = lp_workgroup();
320 } else {
321 c_domain = ads->server.workgroup;
322 }
323
324 realm = c_realm;
325 domain = c_domain;
326
327 /*
328 * In case of LDAP we use get_dc_name() as that
329 * creates the custom krb5.conf file
330 */
331 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
332 fstring srv_name;
333 struct sockaddr_storage ip_out;
334
335 DEBUG(6,("ads_find_dc: (ldap) looking for %s '%s'\n",
336 (got_realm ? "realm" : "domain"), realm));
337
338 if (get_dc_name(domain, realm, srv_name, &ip_out)) {
339 /*
340 * we call ads_try_connect() to fill in the
341 * ads->config details
342 */
343 if (ads_try_connect(ads, srv_name, false)) {
344 return NT_STATUS_OK;
345 }
346 }
347
348 return NT_STATUS_NO_LOGON_SERVERS;
349 }
350
351 sitename = sitename_fetch(realm);
352
353 again:
354
355 DEBUG(6,("ads_find_dc: (cldap) looking for %s '%s'\n",
356 (got_realm ? "realm" : "domain"), realm));
357
358 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
359 if (!NT_STATUS_IS_OK(status)) {
360 /* fall back to netbios if we can */
361 if ( got_realm && !lp_disable_netbios() ) {
362 got_realm = False;
363 goto again;
364 }
365
366 SAFE_FREE(sitename);
367 return status;
368 }
369
370 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
371 for ( i=0; i<count; i++ ) {
372 char server[INET6_ADDRSTRLEN];
373
374 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
375
376 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
377 continue;
378
379 if (!got_realm) {
380 /* realm in this case is a workgroup name. We need
381 to ignore any IP addresses in the negative connection
382 cache that match ip addresses returned in the ad realm
383 case. It sucks that I have to reproduce the logic above... */
384 c_realm = ads->server.realm;
385 if ( !c_realm || !*c_realm ) {
386 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
387 c_realm = lp_realm();
388 }
389 }
390 if (c_realm && *c_realm &&
391 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
392 /* Ensure we add the workgroup name for this
393 IP address as negative too. */
394 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
395 continue;
396 }
397 }
398
399 if ( ads_try_connect(ads, server, false) ) {
400 SAFE_FREE(ip_list);
401 SAFE_FREE(sitename);
402 return NT_STATUS_OK;
403 }
404
405 /* keep track of failures */
406 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
407 }
408
409 SAFE_FREE(ip_list);
410
411 /* In case we failed to contact one of our closest DC on our site we
412 * need to try to find another DC, retry with a site-less SRV DNS query
413 * - Guenther */
414
415 if (sitename) {
416 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
417 "trying to find another DC\n", sitename));
418 SAFE_FREE(sitename);
419 namecache_delete(realm, 0x1C);
420 goto again;
421 }
422
423 return NT_STATUS_NO_LOGON_SERVERS;
424}
425
426/*********************************************************************
427 *********************************************************************/
428
429static NTSTATUS ads_lookup_site(void)
430{
431 ADS_STRUCT *ads = NULL;
432 ADS_STATUS ads_status;
433 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
434
435 ads = ads_init(lp_realm(), NULL, NULL);
436 if (!ads) {
437 return NT_STATUS_NO_MEMORY;
438 }
439
440 /* The NO_BIND here will find a DC and set the client site
441 but not establish the TCP connection */
442
443 ads->auth.flags = ADS_AUTH_NO_BIND;
444 ads_status = ads_connect(ads);
445 if (!ADS_ERR_OK(ads_status)) {
446 DEBUG(4, ("ads_lookup_site: ads_connect to our realm failed! (%s)\n",
447 ads_errstr(ads_status)));
448 }
449 nt_status = ads_ntstatus(ads_status);
450
451 if (ads) {
452 ads_destroy(&ads);
453 }
454
455 return nt_status;
456}
457
458/*********************************************************************
459 *********************************************************************/
460
461static const char* host_dns_domain(const char *fqdn)
462{
463 const char *p = fqdn;
464
465 /* go to next char following '.' */
466
467 if ((p = strchr_m(fqdn, '.')) != NULL) {
468 p++;
469 }
470
471 return p;
472}
473
474
475/**
476 * Connect to the Global Catalog server
477 * @param ads Pointer to an existing ADS_STRUCT
478 * @return status of connection
479 *
480 * Simple wrapper around ads_connect() that fills in the
481 * GC ldap server information
482 **/
483
484ADS_STATUS ads_connect_gc(ADS_STRUCT *ads)
485{
486 TALLOC_CTX *frame = talloc_stackframe();
487 struct dns_rr_srv *gcs_list;
488 int num_gcs;
489 char *realm = ads->server.realm;
490 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
491 ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
492 int i;
493 bool done = false;
494 char *sitename = NULL;
495
496 if (!realm)
497 realm = lp_realm();
498
499 if ((sitename = sitename_fetch(realm)) == NULL) {
500 ads_lookup_site();
501 sitename = sitename_fetch(realm);
502 }
503
504 do {
505 /* We try once with a sitename and once without
506 (unless we don't have a sitename and then we're
507 done */
508
509 if (sitename == NULL)
510 done = true;
511
512 nt_status = ads_dns_query_gcs(frame, realm, sitename,
513 &gcs_list, &num_gcs);
514
515 SAFE_FREE(sitename);
516
517 if (!NT_STATUS_IS_OK(nt_status)) {
518 ads_status = ADS_ERROR_NT(nt_status);
519 goto done;
520 }
521
522 /* Loop until we get a successful connection or have gone
523 through them all. When connecting a GC server, make sure that
524 the realm is the server's DNS name and not the forest root */
525
526 for (i=0; i<num_gcs; i++) {
527 ads->server.gc = true;
528 ads->server.ldap_server = SMB_STRDUP(gcs_list[i].hostname);
529 ads->server.realm = SMB_STRDUP(host_dns_domain(ads->server.ldap_server));
530 ads_status = ads_connect(ads);
531 if (ADS_ERR_OK(ads_status)) {
532 /* Reset the bind_dn to "". A Global Catalog server
533 may host multiple domain trees in a forest.
534 Windows 2003 GC server will accept "" as the search
535 path to imply search all domain trees in the forest */
536
537 SAFE_FREE(ads->config.bind_path);
538 ads->config.bind_path = SMB_STRDUP("");
539
540
541 goto done;
542 }
543 SAFE_FREE(ads->server.ldap_server);
544 SAFE_FREE(ads->server.realm);
545 }
546
547 TALLOC_FREE(gcs_list);
548 num_gcs = 0;
549 } while (!done);
550
551done:
552 SAFE_FREE(sitename);
553 talloc_destroy(frame);
554
555 return ads_status;
556}
557
558
559/**
560 * Connect to the LDAP server
561 * @param ads Pointer to an existing ADS_STRUCT
562 * @return status of connection
563 **/
564ADS_STATUS ads_connect(ADS_STRUCT *ads)
565{
566 int version = LDAP_VERSION3;
567 ADS_STATUS status;
568 NTSTATUS ntstatus;
569 char addr[INET6_ADDRSTRLEN];
570
571 ZERO_STRUCT(ads->ldap);
572 ads->ldap.last_attempt = time(NULL);
573 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
574
575 /* try with a user specified server */
576
577 if (DEBUGLEVEL >= 11) {
578 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
579 DEBUG(11,("ads_connect: entering\n"));
580 DEBUGADD(11,("%s\n", s));
581 TALLOC_FREE(s);
582 }
583
584 if (ads->server.ldap_server)
585 {
586 if (ads_try_connect(ads, ads->server.ldap_server, ads->server.gc)) {
587 goto got_connection;
588 }
589
590 /* The choice of which GC use is handled one level up in
591 ads_connect_gc(). If we continue on from here with
592 ads_find_dc() we will get GC searches on port 389 which
593 doesn't work. --jerry */
594
595 if (ads->server.gc == true) {
596 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
597 }
598 }
599
600 ntstatus = ads_find_dc(ads);
601 if (NT_STATUS_IS_OK(ntstatus)) {
602 goto got_connection;
603 }
604
605 status = ADS_ERROR_NT(ntstatus);
606 goto out;
607
608got_connection:
609
610 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
611 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
612
613 if (!ads->auth.user_name) {
614 /* Must use the userPrincipalName value here or sAMAccountName
615 and not servicePrincipalName; found by Guenther Deschner */
616
617 if (asprintf(&ads->auth.user_name, "%s$", global_myname() ) == -1) {
618 DEBUG(0,("ads_connect: asprintf fail.\n"));
619 ads->auth.user_name = NULL;
620 }
621 }
622
623 if (!ads->auth.realm) {
624 ads->auth.realm = SMB_STRDUP(ads->config.realm);
625 }
626
627 if (!ads->auth.kdc_server) {
628 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
629 ads->auth.kdc_server = SMB_STRDUP(addr);
630 }
631
632#if KRB5_DNS_HACK
633 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
634 to MIT kerberos to work (tridge) */
635 {
636 char *env = NULL;
637 if (asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm) > 0) {
638 setenv(env, ads->auth.kdc_server, 1);
639 free(env);
640 }
641 }
642#endif
643
644 /* If the caller() requested no LDAP bind, then we are done */
645
646 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
647 status = ADS_SUCCESS;
648 goto out;
649 }
650
651 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
652 if (!ads->ldap.mem_ctx) {
653 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
654 goto out;
655 }
656
657 /* Otherwise setup the TCP LDAP session */
658
659 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
660 ads->ldap.port, lp_ldap_timeout());
661 if (ads->ldap.ld == NULL) {
662 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
663 goto out;
664 }
665 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
666
667 /* cache the successful connection for workgroup and realm */
668 if (ads_closest_dc(ads)) {
669 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
670 saf_store( ads->server.realm, ads->config.ldap_server_name);
671 }
672
673 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
674
675 status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
676 if (!ADS_ERR_OK(status)) {
677 goto out;
678 }
679
680 /* fill in the current time and offsets */
681
682 status = ads_current_time( ads );
683 if ( !ADS_ERR_OK(status) ) {
684 goto out;
685 }
686
687 /* Now do the bind */
688
689 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
690 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
691 goto out;
692 }
693
694 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
695 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
696 goto out;
697 }
698
699 status = ads_sasl_bind(ads);
700
701 out:
702 if (DEBUGLEVEL >= 11) {
703 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
704 DEBUG(11,("ads_connect: leaving with: %s\n",
705 ads_errstr(status)));
706 DEBUGADD(11,("%s\n", s));
707 TALLOC_FREE(s);
708 }
709
710 return status;
711}
712
713/**
714 * Connect to the LDAP server using given credentials
715 * @param ads Pointer to an existing ADS_STRUCT
716 * @return status of connection
717 **/
718ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
719{
720 ads->auth.flags |= ADS_AUTH_USER_CREDS;
721
722 return ads_connect(ads);
723}
724
725/**
726 * Disconnect the LDAP server
727 * @param ads Pointer to an existing ADS_STRUCT
728 **/
729void ads_disconnect(ADS_STRUCT *ads)
730{
731 if (ads->ldap.ld) {
732 ldap_unbind(ads->ldap.ld);
733 ads->ldap.ld = NULL;
734 }
735 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
736 ads->ldap.wrap_ops->disconnect(ads);
737 }
738 if (ads->ldap.mem_ctx) {
739 talloc_free(ads->ldap.mem_ctx);
740 }
741 ZERO_STRUCT(ads->ldap);
742}
743
744/*
745 Duplicate a struct berval into talloc'ed memory
746 */
747static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
748{
749 struct berval *value;
750
751 if (!in_val) return NULL;
752
753 value = TALLOC_ZERO_P(ctx, struct berval);
754 if (value == NULL)
755 return NULL;
756 if (in_val->bv_len == 0) return value;
757
758 value->bv_len = in_val->bv_len;
759 value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
760 in_val->bv_len);
761 return value;
762}
763
764/*
765 Make a values list out of an array of (struct berval *)
766 */
767static struct berval **ads_dup_values(TALLOC_CTX *ctx,
768 const struct berval **in_vals)
769{
770 struct berval **values;
771 int i;
772
773 if (!in_vals) return NULL;
774 for (i=0; in_vals[i]; i++)
775 ; /* count values */
776 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
777 if (!values) return NULL;
778
779 for (i=0; in_vals[i]; i++) {
780 values[i] = dup_berval(ctx, in_vals[i]);
781 }
782 return values;
783}
784
785/*
786 UTF8-encode a values list out of an array of (char *)
787 */
788static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
789{
790 char **values;
791 int i;
792 size_t size;
793
794 if (!in_vals) return NULL;
795 for (i=0; in_vals[i]; i++)
796 ; /* count values */
797 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
798 if (!values) return NULL;
799
800 for (i=0; in_vals[i]; i++) {
801 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
802 TALLOC_FREE(values);
803 return NULL;
804 }
805 }
806 return values;
807}
808
809/*
810 Pull a (char *) array out of a UTF8-encoded values list
811 */
812static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
813{
814 char **values;
815 int i;
816 size_t converted_size;
817
818 if (!in_vals) return NULL;
819 for (i=0; in_vals[i]; i++)
820 ; /* count values */
821 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
822 if (!values) return NULL;
823
824 for (i=0; in_vals[i]; i++) {
825 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
826 &converted_size)) {
827 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
828 "%s", strerror(errno)));
829 }
830 }
831 return values;
832}
833
834/**
835 * Do a search with paged results. cookie must be null on the first
836 * call, and then returned on each subsequent call. It will be null
837 * again when the entire search is complete
838 * @param ads connection to ads server
839 * @param bind_path Base dn for the search
840 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
841 * @param expr Search expression - specified in local charset
842 * @param attrs Attributes to retrieve - specified in utf8 or ascii
843 * @param res ** which will contain results - free res* with ads_msgfree()
844 * @param count Number of entries retrieved on this page
845 * @param cookie The paged results cookie to be returned on subsequent calls
846 * @return status of search
847 **/
848static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
849 const char *bind_path,
850 int scope, const char *expr,
851 const char **attrs, void *args,
852 LDAPMessage **res,
853 int *count, struct berval **cookie)
854{
855 int rc, i, version;
856 char *utf8_expr, *utf8_path, **search_attrs;
857 size_t converted_size;
858 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
859 BerElement *cookie_be = NULL;
860 struct berval *cookie_bv= NULL;
861 BerElement *ext_be = NULL;
862 struct berval *ext_bv= NULL;
863
864 TALLOC_CTX *ctx;
865 ads_control *external_control = (ads_control *) args;
866
867 *res = NULL;
868
869 if (!(ctx = talloc_init("ads_do_paged_search_args")))
870 return ADS_ERROR(LDAP_NO_MEMORY);
871
872 /* 0 means the conversion worked but the result was empty
873 so we only fail if it's -1. In any case, it always
874 at least nulls out the dest */
875 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
876 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
877 {
878 rc = LDAP_NO_MEMORY;
879 goto done;
880 }
881
882 if (!attrs || !(*attrs))
883 search_attrs = NULL;
884 else {
885 /* This would be the utf8-encoded version...*/
886 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
887 if (!(str_list_copy(talloc_tos(), &search_attrs, attrs))) {
888 rc = LDAP_NO_MEMORY;
889 goto done;
890 }
891 }
892
893 /* Paged results only available on ldap v3 or later */
894 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
895 if (version < LDAP_VERSION3) {
896 rc = LDAP_NOT_SUPPORTED;
897 goto done;
898 }
899
900 cookie_be = ber_alloc_t(LBER_USE_DER);
901 if (*cookie) {
902 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
903 ber_bvfree(*cookie); /* don't need it from last time */
904 *cookie = NULL;
905 } else {
906 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
907 }
908 ber_flatten(cookie_be, &cookie_bv);
909 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
910 PagedResults.ldctl_iscritical = (char) 1;
911 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
912 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
913
914 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
915 NoReferrals.ldctl_iscritical = (char) 0;
916 NoReferrals.ldctl_value.bv_len = 0;
917 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
918
919 if (external_control &&
920 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
921 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
922
923 ExternalCtrl.ldctl_oid = CONST_DISCARD(char *, external_control->control);
924 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
925
926 /* win2k does not accept a ldctl_value beeing passed in */
927
928 if (external_control->val != 0) {
929
930 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
931 rc = LDAP_NO_MEMORY;
932 goto done;
933 }
934
935 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
936 rc = LDAP_NO_MEMORY;
937 goto done;
938 }
939 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
940 rc = LDAP_NO_MEMORY;
941 goto done;
942 }
943
944 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
945 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
946
947 } else {
948 ExternalCtrl.ldctl_value.bv_len = 0;
949 ExternalCtrl.ldctl_value.bv_val = NULL;
950 }
951
952 controls[0] = &NoReferrals;
953 controls[1] = &PagedResults;
954 controls[2] = &ExternalCtrl;
955 controls[3] = NULL;
956
957 } else {
958 controls[0] = &NoReferrals;
959 controls[1] = &PagedResults;
960 controls[2] = NULL;
961 }
962
963 /* we need to disable referrals as the openldap libs don't
964 handle them and paged results at the same time. Using them
965 together results in the result record containing the server
966 page control being removed from the result list (tridge/jmcd)
967
968 leaving this in despite the control that says don't generate
969 referrals, in case the server doesn't support it (jmcd)
970 */
971 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
972
973 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
974 search_attrs, 0, controls,
975 NULL, LDAP_NO_LIMIT,
976 (LDAPMessage **)res);
977
978 ber_free(cookie_be, 1);
979 ber_bvfree(cookie_bv);
980
981 if (rc) {
982 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
983 ldap_err2string(rc)));
984 goto done;
985 }
986
987 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
988 NULL, &rcontrols, 0);
989
990 if (!rcontrols) {
991 goto done;
992 }
993
994 for (i=0; rcontrols[i]; i++) {
995 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
996 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
997 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
998 &cookie_bv);
999 /* the berval is the cookie, but must be freed when
1000 it is all done */
1001 if (cookie_bv->bv_len) /* still more to do */
1002 *cookie=ber_bvdup(cookie_bv);
1003 else
1004 *cookie=NULL;
1005 ber_bvfree(cookie_bv);
1006 ber_free(cookie_be, 1);
1007 break;
1008 }
1009 }
1010 ldap_controls_free(rcontrols);
1011
1012done:
1013 talloc_destroy(ctx);
1014
1015 if (ext_be) {
1016 ber_free(ext_be, 1);
1017 }
1018
1019 if (ext_bv) {
1020 ber_bvfree(ext_bv);
1021 }
1022
1023 /* if/when we decide to utf8-encode attrs, take out this next line */
1024 TALLOC_FREE(search_attrs);
1025
1026 return ADS_ERROR(rc);
1027}
1028
1029static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1030 int scope, const char *expr,
1031 const char **attrs, LDAPMessage **res,
1032 int *count, struct berval **cookie)
1033{
1034 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1035}
1036
1037
1038/**
1039 * Get all results for a search. This uses ads_do_paged_search() to return
1040 * all entries in a large search.
1041 * @param ads connection to ads server
1042 * @param bind_path Base dn for the search
1043 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1044 * @param expr Search expression
1045 * @param attrs Attributes to retrieve
1046 * @param res ** which will contain results - free res* with ads_msgfree()
1047 * @return status of search
1048 **/
1049 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1050 int scope, const char *expr,
1051 const char **attrs, void *args,
1052 LDAPMessage **res)
1053{
1054 struct berval *cookie = NULL;
1055 int count = 0;
1056 ADS_STATUS status;
1057
1058 *res = NULL;
1059 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1060 &count, &cookie);
1061
1062 if (!ADS_ERR_OK(status))
1063 return status;
1064
1065#ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1066 while (cookie) {
1067 LDAPMessage *res2 = NULL;
1068 ADS_STATUS status2;
1069 LDAPMessage *msg, *next;
1070
1071 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
1072 attrs, args, &res2, &count, &cookie);
1073
1074 if (!ADS_ERR_OK(status2)) break;
1075
1076 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1077 that this works on all ldap libs, but I have only tested with openldap */
1078 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1079 next = ads_next_message(ads, msg);
1080 ldap_add_result_entry((LDAPMessage **)res, msg);
1081 }
1082 /* note that we do not free res2, as the memory is now
1083 part of the main returned list */
1084 }
1085#else
1086 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1087 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1088#endif
1089
1090 return status;
1091}
1092
1093 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1094 int scope, const char *expr,
1095 const char **attrs, LDAPMessage **res)
1096{
1097 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1098}
1099
1100 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1101 int scope, const char *expr,
1102 const char **attrs, uint32 sd_flags,
1103 LDAPMessage **res)
1104{
1105 ads_control args;
1106
1107 args.control = ADS_SD_FLAGS_OID;
1108 args.val = sd_flags;
1109 args.critical = True;
1110
1111 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1112}
1113
1114
1115/**
1116 * Run a function on all results for a search. Uses ads_do_paged_search() and
1117 * runs the function as each page is returned, using ads_process_results()
1118 * @param ads connection to ads server
1119 * @param bind_path Base dn for the search
1120 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1121 * @param expr Search expression - specified in local charset
1122 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1123 * @param fn Function which takes attr name, values list, and data_area
1124 * @param data_area Pointer which is passed to function on each call
1125 * @return status of search
1126 **/
1127ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1128 int scope, const char *expr, const char **attrs,
1129 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1130 void *data_area)
1131{
1132 struct berval *cookie = NULL;
1133 int count = 0;
1134 ADS_STATUS status;
1135 LDAPMessage *res;
1136
1137 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1138 &count, &cookie);
1139
1140 if (!ADS_ERR_OK(status)) return status;
1141
1142 ads_process_results(ads, res, fn, data_area);
1143 ads_msgfree(ads, res);
1144
1145 while (cookie) {
1146 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1147 &res, &count, &cookie);
1148
1149 if (!ADS_ERR_OK(status)) break;
1150
1151 ads_process_results(ads, res, fn, data_area);
1152 ads_msgfree(ads, res);
1153 }
1154
1155 return status;
1156}
1157
1158/**
1159 * Do a search with a timeout.
1160 * @param ads connection to ads server
1161 * @param bind_path Base dn for the search
1162 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1163 * @param expr Search expression
1164 * @param attrs Attributes to retrieve
1165 * @param res ** which will contain results - free res* with ads_msgfree()
1166 * @return status of search
1167 **/
1168 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1169 const char *expr,
1170 const char **attrs, LDAPMessage **res)
1171{
1172 int rc;
1173 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1174 size_t converted_size;
1175 TALLOC_CTX *ctx;
1176
1177 *res = NULL;
1178 if (!(ctx = talloc_init("ads_do_search"))) {
1179 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1180 return ADS_ERROR(LDAP_NO_MEMORY);
1181 }
1182
1183 /* 0 means the conversion worked but the result was empty
1184 so we only fail if it's negative. In any case, it always
1185 at least nulls out the dest */
1186 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1187 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1188 {
1189 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1190 rc = LDAP_NO_MEMORY;
1191 goto done;
1192 }
1193
1194 if (!attrs || !(*attrs))
1195 search_attrs = NULL;
1196 else {
1197 /* This would be the utf8-encoded version...*/
1198 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1199 if (!(str_list_copy(talloc_tos(), &search_attrs, attrs)))
1200 {
1201 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1202 rc = LDAP_NO_MEMORY;
1203 goto done;
1204 }
1205 }
1206
1207 /* see the note in ads_do_paged_search - we *must* disable referrals */
1208 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1209
1210 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1211 search_attrs, 0, NULL, NULL,
1212 LDAP_NO_LIMIT,
1213 (LDAPMessage **)res);
1214
1215 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1216 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1217 rc = 0;
1218 }
1219
1220 done:
1221 talloc_destroy(ctx);
1222 /* if/when we decide to utf8-encode attrs, take out this next line */
1223 TALLOC_FREE(search_attrs);
1224 return ADS_ERROR(rc);
1225}
1226/**
1227 * Do a general ADS search
1228 * @param ads connection to ads server
1229 * @param res ** which will contain results - free res* with ads_msgfree()
1230 * @param expr Search expression
1231 * @param attrs Attributes to retrieve
1232 * @return status of search
1233 **/
1234 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1235 const char *expr, const char **attrs)
1236{
1237 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1238 expr, attrs, res);
1239}
1240
1241/**
1242 * Do a search on a specific DistinguishedName
1243 * @param ads connection to ads server
1244 * @param res ** which will contain results - free res* with ads_msgfree()
1245 * @param dn DistinguishName to search
1246 * @param attrs Attributes to retrieve
1247 * @return status of search
1248 **/
1249 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1250 const char *dn, const char **attrs)
1251{
1252 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1253 attrs, res);
1254}
1255
1256/**
1257 * Free up memory from a ads_search
1258 * @param ads connection to ads server
1259 * @param msg Search results to free
1260 **/
1261 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1262{
1263 if (!msg) return;
1264 ldap_msgfree(msg);
1265}
1266
1267/**
1268 * Free up memory from various ads requests
1269 * @param ads connection to ads server
1270 * @param mem Area to free
1271 **/
1272void ads_memfree(ADS_STRUCT *ads, void *mem)
1273{
1274 SAFE_FREE(mem);
1275}
1276
1277/**
1278 * Get a dn from search results
1279 * @param ads connection to ads server
1280 * @param msg Search result
1281 * @return dn string
1282 **/
1283 char *ads_get_dn(ADS_STRUCT *ads, LDAPMessage *msg)
1284{
1285 char *utf8_dn, *unix_dn;
1286 size_t converted_size;
1287
1288 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1289
1290 if (!utf8_dn) {
1291 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1292 return NULL;
1293 }
1294
1295 if (!pull_utf8_allocate(&unix_dn, utf8_dn, &converted_size)) {
1296 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1297 utf8_dn ));
1298 return NULL;
1299 }
1300 ldap_memfree(utf8_dn);
1301 return unix_dn;
1302}
1303
1304/**
1305 * Get the parent from a dn
1306 * @param dn the dn to return the parent from
1307 * @return parent dn string
1308 **/
1309char *ads_parent_dn(const char *dn)
1310{
1311 char *p;
1312
1313 if (dn == NULL) {
1314 return NULL;
1315 }
1316
1317 p = strchr(dn, ',');
1318
1319 if (p == NULL) {
1320 return NULL;
1321 }
1322
1323 return p+1;
1324}
1325
1326/**
1327 * Find a machine account given a hostname
1328 * @param ads connection to ads server
1329 * @param res ** which will contain results - free res* with ads_msgfree()
1330 * @param host Hostname to search for
1331 * @return status of search
1332 **/
1333 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1334 const char *machine)
1335{
1336 ADS_STATUS status;
1337 char *expr;
1338 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1339
1340 *res = NULL;
1341
1342 /* the easiest way to find a machine account anywhere in the tree
1343 is to look for hostname$ */
1344 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1345 DEBUG(1, ("asprintf failed!\n"));
1346 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1347 }
1348
1349 status = ads_search(ads, res, expr, attrs);
1350 SAFE_FREE(expr);
1351 return status;
1352}
1353
1354/**
1355 * Initialize a list of mods to be used in a modify request
1356 * @param ctx An initialized TALLOC_CTX
1357 * @return allocated ADS_MODLIST
1358 **/
1359ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1360{
1361#define ADS_MODLIST_ALLOC_SIZE 10
1362 LDAPMod **mods;
1363
1364 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1365 /* -1 is safety to make sure we don't go over the end.
1366 need to reset it to NULL before doing ldap modify */
1367 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1368
1369 return (ADS_MODLIST)mods;
1370}
1371
1372
1373/*
1374 add an attribute to the list, with values list already constructed
1375*/
1376static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1377 int mod_op, const char *name,
1378 const void *_invals)
1379{
1380 const void **invals = (const void **)_invals;
1381 int curmod;
1382 LDAPMod **modlist = (LDAPMod **) *mods;
1383 struct berval **ber_values = NULL;
1384 char **char_values = NULL;
1385
1386 if (!invals) {
1387 mod_op = LDAP_MOD_DELETE;
1388 } else {
1389 if (mod_op & LDAP_MOD_BVALUES)
1390 ber_values = ads_dup_values(ctx,
1391 (const struct berval **)invals);
1392 else
1393 char_values = ads_push_strvals(ctx,
1394 (const char **) invals);
1395 }
1396
1397 /* find the first empty slot */
1398 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1399 curmod++);
1400 if (modlist[curmod] == (LDAPMod *) -1) {
1401 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1402 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1403 return ADS_ERROR(LDAP_NO_MEMORY);
1404 memset(&modlist[curmod], 0,
1405 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1406 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1407 *mods = (ADS_MODLIST)modlist;
1408 }
1409
1410 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1411 return ADS_ERROR(LDAP_NO_MEMORY);
1412 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1413 if (mod_op & LDAP_MOD_BVALUES) {
1414 modlist[curmod]->mod_bvalues = ber_values;
1415 } else if (mod_op & LDAP_MOD_DELETE) {
1416 modlist[curmod]->mod_values = NULL;
1417 } else {
1418 modlist[curmod]->mod_values = char_values;
1419 }
1420
1421 modlist[curmod]->mod_op = mod_op;
1422 return ADS_ERROR(LDAP_SUCCESS);
1423}
1424
1425/**
1426 * Add a single string value to a mod list
1427 * @param ctx An initialized TALLOC_CTX
1428 * @param mods An initialized ADS_MODLIST
1429 * @param name The attribute name to add
1430 * @param val The value to add - NULL means DELETE
1431 * @return ADS STATUS indicating success of add
1432 **/
1433ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1434 const char *name, const char *val)
1435{
1436 const char *values[2];
1437
1438 values[0] = val;
1439 values[1] = NULL;
1440
1441 if (!val)
1442 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1443 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1444}
1445
1446/**
1447 * Add an array of string values to a mod list
1448 * @param ctx An initialized TALLOC_CTX
1449 * @param mods An initialized ADS_MODLIST
1450 * @param name The attribute name to add
1451 * @param vals The array of string values to add - NULL means DELETE
1452 * @return ADS STATUS indicating success of add
1453 **/
1454ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1455 const char *name, const char **vals)
1456{
1457 if (!vals)
1458 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1459 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1460 name, (const void **) vals);
1461}
1462
1463#if 0
1464/**
1465 * Add a single ber-encoded value to a mod list
1466 * @param ctx An initialized TALLOC_CTX
1467 * @param mods An initialized ADS_MODLIST
1468 * @param name The attribute name to add
1469 * @param val The value to add - NULL means DELETE
1470 * @return ADS STATUS indicating success of add
1471 **/
1472static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1473 const char *name, const struct berval *val)
1474{
1475 const struct berval *values[2];
1476
1477 values[0] = val;
1478 values[1] = NULL;
1479 if (!val)
1480 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1481 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1482 name, (const void **) values);
1483}
1484#endif
1485
1486/**
1487 * Perform an ldap modify
1488 * @param ads connection to ads server
1489 * @param mod_dn DistinguishedName to modify
1490 * @param mods list of modifications to perform
1491 * @return status of modify
1492 **/
1493ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1494{
1495 int ret,i;
1496 char *utf8_dn = NULL;
1497 size_t converted_size;
1498 /*
1499 this control is needed to modify that contains a currently
1500 non-existent attribute (but allowable for the object) to run
1501 */
1502 LDAPControl PermitModify = {
1503 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1504 {0, NULL},
1505 (char) 1};
1506 LDAPControl *controls[2];
1507
1508 controls[0] = &PermitModify;
1509 controls[1] = NULL;
1510
1511 if (!push_utf8_allocate(&utf8_dn, mod_dn, &converted_size)) {
1512 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1513 }
1514
1515 /* find the end of the list, marked by NULL or -1 */
1516 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1517 /* make sure the end of the list is NULL */
1518 mods[i] = NULL;
1519 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1520 (LDAPMod **) mods, controls, NULL);
1521 SAFE_FREE(utf8_dn);
1522 return ADS_ERROR(ret);
1523}
1524
1525/**
1526 * Perform an ldap add
1527 * @param ads connection to ads server
1528 * @param new_dn DistinguishedName to add
1529 * @param mods list of attributes and values for DN
1530 * @return status of add
1531 **/
1532ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1533{
1534 int ret, i;
1535 char *utf8_dn = NULL;
1536 size_t converted_size;
1537
1538 if (!push_utf8_allocate(&utf8_dn, new_dn, &converted_size)) {
1539 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1540 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1541 }
1542
1543 /* find the end of the list, marked by NULL or -1 */
1544 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1545 /* make sure the end of the list is NULL */
1546 mods[i] = NULL;
1547
1548 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1549 SAFE_FREE(utf8_dn);
1550 return ADS_ERROR(ret);
1551}
1552
1553/**
1554 * Delete a DistinguishedName
1555 * @param ads connection to ads server
1556 * @param new_dn DistinguishedName to delete
1557 * @return status of delete
1558 **/
1559ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1560{
1561 int ret;
1562 char *utf8_dn = NULL;
1563 size_t converted_size;
1564 if (!push_utf8_allocate(&utf8_dn, del_dn, &converted_size)) {
1565 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1566 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1567 }
1568
1569 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1570 SAFE_FREE(utf8_dn);
1571 return ADS_ERROR(ret);
1572}
1573
1574/**
1575 * Build an org unit string
1576 * if org unit is Computers or blank then assume a container, otherwise
1577 * assume a / separated list of organisational units.
1578 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1579 * @param ads connection to ads server
1580 * @param org_unit Organizational unit
1581 * @return org unit string - caller must free
1582 **/
1583char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1584{
1585 char *ret = NULL;
1586
1587 if (!org_unit || !*org_unit) {
1588
1589 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1590
1591 /* samba4 might not yet respond to a wellknownobject-query */
1592 return ret ? ret : SMB_STRDUP("cn=Computers");
1593 }
1594
1595 if (strequal(org_unit, "Computers")) {
1596 return SMB_STRDUP("cn=Computers");
1597 }
1598
1599 /* jmcd: removed "\\" from the separation chars, because it is
1600 needed as an escape for chars like '#' which are valid in an
1601 OU name */
1602 return ads_build_path(org_unit, "/", "ou=", 1);
1603}
1604
1605/**
1606 * Get a org unit string for a well-known GUID
1607 * @param ads connection to ads server
1608 * @param wknguid Well known GUID
1609 * @return org unit string - caller must free
1610 **/
1611char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1612{
1613 ADS_STATUS status;
1614 LDAPMessage *res = NULL;
1615 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1616 **bind_dn_exp = NULL;
1617 const char *attrs[] = {"distinguishedName", NULL};
1618 int new_ln, wkn_ln, bind_ln, i;
1619
1620 if (wknguid == NULL) {
1621 return NULL;
1622 }
1623
1624 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1625 DEBUG(1, ("asprintf failed!\n"));
1626 return NULL;
1627 }
1628
1629 status = ads_search_dn(ads, &res, base, attrs);
1630 if (!ADS_ERR_OK(status)) {
1631 DEBUG(1,("Failed while searching for: %s\n", base));
1632 goto out;
1633 }
1634
1635 if (ads_count_replies(ads, res) != 1) {
1636 goto out;
1637 }
1638
1639 /* substitute the bind-path from the well-known-guid-search result */
1640 wkn_dn = ads_get_dn(ads, res);
1641 if (!wkn_dn) {
1642 goto out;
1643 }
1644
1645 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1646 if (!wkn_dn_exp) {
1647 goto out;
1648 }
1649
1650 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1651 if (!bind_dn_exp) {
1652 goto out;
1653 }
1654
1655 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1656 ;
1657 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1658 ;
1659
1660 new_ln = wkn_ln - bind_ln;
1661
1662 ret = SMB_STRDUP(wkn_dn_exp[0]);
1663 if (!ret) {
1664 goto out;
1665 }
1666
1667 for (i=1; i < new_ln; i++) {
1668 char *s = NULL;
1669
1670 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1671 SAFE_FREE(ret);
1672 goto out;
1673 }
1674
1675 SAFE_FREE(ret);
1676 ret = SMB_STRDUP(s);
1677 free(s);
1678 if (!ret) {
1679 goto out;
1680 }
1681 }
1682
1683 out:
1684 SAFE_FREE(base);
1685 ads_msgfree(ads, res);
1686 ads_memfree(ads, wkn_dn);
1687 if (wkn_dn_exp) {
1688 ldap_value_free(wkn_dn_exp);
1689 }
1690 if (bind_dn_exp) {
1691 ldap_value_free(bind_dn_exp);
1692 }
1693
1694 return ret;
1695}
1696
1697/**
1698 * Adds (appends) an item to an attribute array, rather then
1699 * replacing the whole list
1700 * @param ctx An initialized TALLOC_CTX
1701 * @param mods An initialized ADS_MODLIST
1702 * @param name name of the ldap attribute to append to
1703 * @param vals an array of values to add
1704 * @return status of addition
1705 **/
1706
1707ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1708 const char *name, const char **vals)
1709{
1710 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1711 (const void *) vals);
1712}
1713
1714/**
1715 * Determines the an account's current KVNO via an LDAP lookup
1716 * @param ads An initialized ADS_STRUCT
1717 * @param account_name the NT samaccountname.
1718 * @return the kvno for the account, or -1 in case of a failure.
1719 **/
1720
1721uint32 ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1722{
1723 LDAPMessage *res = NULL;
1724 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1725 char *filter;
1726 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1727 char *dn_string = NULL;
1728 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1729
1730 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1731 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1732 return kvno;
1733 }
1734 ret = ads_search(ads, &res, filter, attrs);
1735 SAFE_FREE(filter);
1736 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1737 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1738 ads_msgfree(ads, res);
1739 return kvno;
1740 }
1741
1742 dn_string = ads_get_dn(ads, res);
1743 if (!dn_string) {
1744 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1745 ads_msgfree(ads, res);
1746 return kvno;
1747 }
1748 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1749 ads_memfree(ads, dn_string);
1750
1751 /* ---------------------------------------------------------
1752 * 0 is returned as a default KVNO from this point on...
1753 * This is done because Windows 2000 does not support key
1754 * version numbers. Chances are that a failure in the next
1755 * step is simply due to Windows 2000 being used for a
1756 * domain controller. */
1757 kvno = 0;
1758
1759 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1760 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1761 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1762 ads_msgfree(ads, res);
1763 return kvno;
1764 }
1765
1766 /* Success */
1767 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1768 ads_msgfree(ads, res);
1769 return kvno;
1770}
1771
1772/**
1773 * Determines the computer account's current KVNO via an LDAP lookup
1774 * @param ads An initialized ADS_STRUCT
1775 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1776 * @return the kvno for the computer account, or -1 in case of a failure.
1777 **/
1778
1779uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1780{
1781 char *computer_account = NULL;
1782 uint32_t kvno = -1;
1783
1784 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1785 return kvno;
1786 }
1787
1788 kvno = ads_get_kvno(ads, computer_account);
1789 free(computer_account);
1790
1791 return kvno;
1792}
1793
1794/**
1795 * This clears out all registered spn's for a given hostname
1796 * @param ads An initilaized ADS_STRUCT
1797 * @param machine_name the NetBIOS name of the computer.
1798 * @return 0 upon success, non-zero otherwise.
1799 **/
1800
1801ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1802{
1803 TALLOC_CTX *ctx;
1804 LDAPMessage *res = NULL;
1805 ADS_MODLIST mods;
1806 const char *servicePrincipalName[1] = {NULL};
1807 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1808 char *dn_string = NULL;
1809
1810 ret = ads_find_machine_acct(ads, &res, machine_name);
1811 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1812 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1813 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1814 ads_msgfree(ads, res);
1815 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1816 }
1817
1818 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1819 ctx = talloc_init("ads_clear_service_principal_names");
1820 if (!ctx) {
1821 ads_msgfree(ads, res);
1822 return ADS_ERROR(LDAP_NO_MEMORY);
1823 }
1824
1825 if (!(mods = ads_init_mods(ctx))) {
1826 talloc_destroy(ctx);
1827 ads_msgfree(ads, res);
1828 return ADS_ERROR(LDAP_NO_MEMORY);
1829 }
1830 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1831 if (!ADS_ERR_OK(ret)) {
1832 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1833 ads_msgfree(ads, res);
1834 talloc_destroy(ctx);
1835 return ret;
1836 }
1837 dn_string = ads_get_dn(ads, res);
1838 if (!dn_string) {
1839 talloc_destroy(ctx);
1840 ads_msgfree(ads, res);
1841 return ADS_ERROR(LDAP_NO_MEMORY);
1842 }
1843 ret = ads_gen_mod(ads, dn_string, mods);
1844 ads_memfree(ads,dn_string);
1845 if (!ADS_ERR_OK(ret)) {
1846 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1847 machine_name));
1848 ads_msgfree(ads, res);
1849 talloc_destroy(ctx);
1850 return ret;
1851 }
1852
1853 ads_msgfree(ads, res);
1854 talloc_destroy(ctx);
1855 return ret;
1856}
1857
1858/**
1859 * This adds a service principal name to an existing computer account
1860 * (found by hostname) in AD.
1861 * @param ads An initialized ADS_STRUCT
1862 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1863 * @param my_fqdn The fully qualified DNS name of the machine
1864 * @param spn A string of the service principal to add, i.e. 'host'
1865 * @return 0 upon sucess, or non-zero if a failure occurs
1866 **/
1867
1868ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1869 const char *my_fqdn, const char *spn)
1870{
1871 ADS_STATUS ret;
1872 TALLOC_CTX *ctx;
1873 LDAPMessage *res = NULL;
1874 char *psp1, *psp2;
1875 ADS_MODLIST mods;
1876 char *dn_string = NULL;
1877 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1878
1879 ret = ads_find_machine_acct(ads, &res, machine_name);
1880 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1881 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1882 machine_name));
1883 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1884 spn, machine_name, ads->config.realm));
1885 ads_msgfree(ads, res);
1886 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1887 }
1888
1889 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1890 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1891 ads_msgfree(ads, res);
1892 return ADS_ERROR(LDAP_NO_MEMORY);
1893 }
1894
1895 /* add short name spn */
1896
1897 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1898 talloc_destroy(ctx);
1899 ads_msgfree(ads, res);
1900 return ADS_ERROR(LDAP_NO_MEMORY);
1901 }
1902 strupper_m(psp1);
1903 strlower_m(&psp1[strlen(spn)]);
1904 servicePrincipalName[0] = psp1;
1905
1906 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1907 psp1, machine_name));
1908
1909
1910 /* add fully qualified spn */
1911
1912 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1913 ret = ADS_ERROR(LDAP_NO_MEMORY);
1914 goto out;
1915 }
1916 strupper_m(psp2);
1917 strlower_m(&psp2[strlen(spn)]);
1918 servicePrincipalName[1] = psp2;
1919
1920 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1921 psp2, machine_name));
1922
1923 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1924 ret = ADS_ERROR(LDAP_NO_MEMORY);
1925 goto out;
1926 }
1927
1928 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1929 if (!ADS_ERR_OK(ret)) {
1930 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1931 goto out;
1932 }
1933
1934 if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1935 ret = ADS_ERROR(LDAP_NO_MEMORY);
1936 goto out;
1937 }
1938
1939 ret = ads_gen_mod(ads, dn_string, mods);
1940 ads_memfree(ads,dn_string);
1941 if (!ADS_ERR_OK(ret)) {
1942 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1943 goto out;
1944 }
1945
1946 out:
1947 TALLOC_FREE( ctx );
1948 ads_msgfree(ads, res);
1949 return ret;
1950}
1951
1952/**
1953 * adds a machine account to the ADS server
1954 * @param ads An intialized ADS_STRUCT
1955 * @param machine_name - the NetBIOS machine name of this account.
1956 * @param account_type A number indicating the type of account to create
1957 * @param org_unit The LDAP path in which to place this account
1958 * @return 0 upon success, or non-zero otherwise
1959**/
1960
1961ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1962 const char *org_unit)
1963{
1964 ADS_STATUS ret;
1965 char *samAccountName, *controlstr;
1966 TALLOC_CTX *ctx;
1967 ADS_MODLIST mods;
1968 char *machine_escaped = NULL;
1969 char *new_dn;
1970 const char *objectClass[] = {"top", "person", "organizationalPerson",
1971 "user", "computer", NULL};
1972 LDAPMessage *res = NULL;
1973 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1974 UF_DONT_EXPIRE_PASSWD |\
1975 UF_ACCOUNTDISABLE );
1976
1977 if (!(ctx = talloc_init("ads_add_machine_acct")))
1978 return ADS_ERROR(LDAP_NO_MEMORY);
1979
1980 ret = ADS_ERROR(LDAP_NO_MEMORY);
1981
1982 machine_escaped = escape_rdn_val_string_alloc(machine_name);
1983 if (!machine_escaped) {
1984 goto done;
1985 }
1986
1987 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
1988 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1989
1990 if ( !new_dn || !samAccountName ) {
1991 goto done;
1992 }
1993
1994#ifndef ENCTYPE_ARCFOUR_HMAC
1995 acct_control |= UF_USE_DES_KEY_ONLY;
1996#endif
1997
1998 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1999 goto done;
2000 }
2001
2002 if (!(mods = ads_init_mods(ctx))) {
2003 goto done;
2004 }
2005
2006 ads_mod_str(ctx, &mods, "cn", machine_name);
2007 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2008 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2009 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2010
2011 ret = ads_gen_add(ads, new_dn, mods);
2012
2013done:
2014 SAFE_FREE(machine_escaped);
2015 ads_msgfree(ads, res);
2016 talloc_destroy(ctx);
2017
2018 return ret;
2019}
2020
2021/**
2022 * move a machine account to another OU on the ADS server
2023 * @param ads - An intialized ADS_STRUCT
2024 * @param machine_name - the NetBIOS machine name of this account.
2025 * @param org_unit - The LDAP path in which to place this account
2026 * @param moved - whether we moved the machine account (optional)
2027 * @return 0 upon success, or non-zero otherwise
2028**/
2029
2030ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2031 const char *org_unit, bool *moved)
2032{
2033 ADS_STATUS rc;
2034 int ldap_status;
2035 LDAPMessage *res = NULL;
2036 char *filter = NULL;
2037 char *computer_dn = NULL;
2038 char *parent_dn;
2039 char *computer_rdn = NULL;
2040 bool need_move = False;
2041
2042 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2043 rc = ADS_ERROR(LDAP_NO_MEMORY);
2044 goto done;
2045 }
2046
2047 /* Find pre-existing machine */
2048 rc = ads_search(ads, &res, filter, NULL);
2049 if (!ADS_ERR_OK(rc)) {
2050 goto done;
2051 }
2052
2053 computer_dn = ads_get_dn(ads, res);
2054 if (!computer_dn) {
2055 rc = ADS_ERROR(LDAP_NO_MEMORY);
2056 goto done;
2057 }
2058
2059 parent_dn = ads_parent_dn(computer_dn);
2060 if (strequal(parent_dn, org_unit)) {
2061 goto done;
2062 }
2063
2064 need_move = True;
2065
2066 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2067 rc = ADS_ERROR(LDAP_NO_MEMORY);
2068 goto done;
2069 }
2070
2071 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2072 org_unit, 1, NULL, NULL);
2073 rc = ADS_ERROR(ldap_status);
2074
2075done:
2076 ads_msgfree(ads, res);
2077 SAFE_FREE(filter);
2078 SAFE_FREE(computer_dn);
2079 SAFE_FREE(computer_rdn);
2080
2081 if (!ADS_ERR_OK(rc)) {
2082 need_move = False;
2083 }
2084
2085 if (moved) {
2086 *moved = need_move;
2087 }
2088
2089 return rc;
2090}
2091
2092/*
2093 dump a binary result from ldap
2094*/
2095static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2096{
2097 int i, j;
2098 for (i=0; values[i]; i++) {
2099 printf("%s: ", field);
2100 for (j=0; j<values[i]->bv_len; j++) {
2101 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2102 }
2103 printf("\n");
2104 }
2105}
2106
2107static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2108{
2109 int i;
2110 for (i=0; values[i]; i++) {
2111
2112 UUID_FLAT guid;
2113 struct GUID tmp;
2114
2115 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
2116 smb_uuid_unpack(guid, &tmp);
2117 printf("%s: %s\n", field, smb_uuid_string(talloc_tos(), tmp));
2118 }
2119}
2120
2121/*
2122 dump a sid result from ldap
2123*/
2124static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2125{
2126 int i;
2127 for (i=0; values[i]; i++) {
2128 DOM_SID sid;
2129 fstring tmp;
2130 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
2131 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2132 }
2133}
2134
2135/*
2136 dump ntSecurityDescriptor
2137*/
2138static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2139{
2140 TALLOC_CTX *frame = talloc_stackframe();
2141 struct security_descriptor *psd;
2142 NTSTATUS status;
2143
2144 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
2145 values[0]->bv_len, &psd);
2146 if (!NT_STATUS_IS_OK(status)) {
2147 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2148 nt_errstr(status)));
2149 TALLOC_FREE(frame);
2150 return;
2151 }
2152
2153 if (psd) {
2154 ads_disp_sd(ads, talloc_tos(), psd);
2155 }
2156
2157 TALLOC_FREE(frame);
2158}
2159
2160/*
2161 dump a string result from ldap
2162*/
2163static void dump_string(const char *field, char **values)
2164{
2165 int i;
2166 for (i=0; values[i]; i++) {
2167 printf("%s: %s\n", field, values[i]);
2168 }
2169}
2170
2171/*
2172 dump a field from LDAP on stdout
2173 used for debugging
2174*/
2175
2176static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2177{
2178 const struct {
2179 const char *name;
2180 bool string;
2181 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2182 } handlers[] = {
2183 {"objectGUID", False, dump_guid},
2184 {"netbootGUID", False, dump_guid},
2185 {"nTSecurityDescriptor", False, dump_sd},
2186 {"dnsRecord", False, dump_binary},
2187 {"objectSid", False, dump_sid},
2188 {"tokenGroups", False, dump_sid},
2189 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2190 {"tokengroupsGlobalandUniversal", False, dump_sid},
2191 {"mS-DS-CreatorSID", False, dump_sid},
2192 {"msExchMailboxGuid", False, dump_guid},
2193 {NULL, True, NULL}
2194 };
2195 int i;
2196
2197 if (!field) { /* must be end of an entry */
2198 printf("\n");
2199 return False;
2200 }
2201
2202 for (i=0; handlers[i].name; i++) {
2203 if (StrCaseCmp(handlers[i].name, field) == 0) {
2204 if (!values) /* first time, indicate string or not */
2205 return handlers[i].string;
2206 handlers[i].handler(ads, field, (struct berval **) values);
2207 break;
2208 }
2209 }
2210 if (!handlers[i].name) {
2211 if (!values) /* first time, indicate string conversion */
2212 return True;
2213 dump_string(field, (char **)values);
2214 }
2215 return False;
2216}
2217
2218/**
2219 * Dump a result from LDAP on stdout
2220 * used for debugging
2221 * @param ads connection to ads server
2222 * @param res Results to dump
2223 **/
2224
2225 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2226{
2227 ads_process_results(ads, res, ads_dump_field, NULL);
2228}
2229
2230/**
2231 * Walk through results, calling a function for each entry found.
2232 * The function receives a field name, a berval * array of values,
2233 * and a data area passed through from the start. The function is
2234 * called once with null for field and values at the end of each
2235 * entry.
2236 * @param ads connection to ads server
2237 * @param res Results to process
2238 * @param fn Function for processing each result
2239 * @param data_area user-defined area to pass to function
2240 **/
2241 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2242 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2243 void *data_area)
2244{
2245 LDAPMessage *msg;
2246 TALLOC_CTX *ctx;
2247 size_t converted_size;
2248
2249 if (!(ctx = talloc_init("ads_process_results")))
2250 return;
2251
2252 for (msg = ads_first_entry(ads, res); msg;
2253 msg = ads_next_entry(ads, msg)) {
2254 char *utf8_field;
2255 BerElement *b;
2256
2257 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2258 (LDAPMessage *)msg,&b);
2259 utf8_field;
2260 utf8_field=ldap_next_attribute(ads->ldap.ld,
2261 (LDAPMessage *)msg,b)) {
2262 struct berval **ber_vals;
2263 char **str_vals, **utf8_vals;
2264 char *field;
2265 bool string;
2266
2267 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2268 &converted_size))
2269 {
2270 DEBUG(0,("ads_process_results: "
2271 "pull_utf8_talloc failed: %s",
2272 strerror(errno)));
2273 }
2274
2275 string = fn(ads, field, NULL, data_area);
2276
2277 if (string) {
2278 utf8_vals = ldap_get_values(ads->ldap.ld,
2279 (LDAPMessage *)msg, field);
2280 str_vals = ads_pull_strvals(ctx,
2281 (const char **) utf8_vals);
2282 fn(ads, field, (void **) str_vals, data_area);
2283 ldap_value_free(utf8_vals);
2284 } else {
2285 ber_vals = ldap_get_values_len(ads->ldap.ld,
2286 (LDAPMessage *)msg, field);
2287 fn(ads, field, (void **) ber_vals, data_area);
2288
2289 ldap_value_free_len(ber_vals);
2290 }
2291 ldap_memfree(utf8_field);
2292 }
2293 ber_free(b, 0);
2294 talloc_free_children(ctx);
2295 fn(ads, NULL, NULL, data_area); /* completed an entry */
2296
2297 }
2298 talloc_destroy(ctx);
2299}
2300
2301/**
2302 * count how many replies are in a LDAPMessage
2303 * @param ads connection to ads server
2304 * @param res Results to count
2305 * @return number of replies
2306 **/
2307int ads_count_replies(ADS_STRUCT *ads, void *res)
2308{
2309 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2310}
2311
2312/**
2313 * pull the first entry from a ADS result
2314 * @param ads connection to ads server
2315 * @param res Results of search
2316 * @return first entry from result
2317 **/
2318 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2319{
2320 return ldap_first_entry(ads->ldap.ld, res);
2321}
2322
2323/**
2324 * pull the next entry from a ADS result
2325 * @param ads connection to ads server
2326 * @param res Results of search
2327 * @return next entry from result
2328 **/
2329 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2330{
2331 return ldap_next_entry(ads->ldap.ld, res);
2332}
2333
2334/**
2335 * pull the first message from a ADS result
2336 * @param ads connection to ads server
2337 * @param res Results of search
2338 * @return first message from result
2339 **/
2340 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2341{
2342 return ldap_first_message(ads->ldap.ld, res);
2343}
2344
2345/**
2346 * pull the next message from a ADS result
2347 * @param ads connection to ads server
2348 * @param res Results of search
2349 * @return next message from result
2350 **/
2351 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2352{
2353 return ldap_next_message(ads->ldap.ld, res);
2354}
2355
2356/**
2357 * pull a single string from a ADS result
2358 * @param ads connection to ads server
2359 * @param mem_ctx TALLOC_CTX to use for allocating result string
2360 * @param msg Results of search
2361 * @param field Attribute to retrieve
2362 * @return Result string in talloc context
2363 **/
2364 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2365 const char *field)
2366{
2367 char **values;
2368 char *ret = NULL;
2369 char *ux_string;
2370 size_t converted_size;
2371
2372 values = ldap_get_values(ads->ldap.ld, msg, field);
2373 if (!values)
2374 return NULL;
2375
2376 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2377 &converted_size))
2378 {
2379 ret = ux_string;
2380 }
2381 ldap_value_free(values);
2382 return ret;
2383}
2384
2385/**
2386 * pull an array of strings from a ADS result
2387 * @param ads connection to ads server
2388 * @param mem_ctx TALLOC_CTX to use for allocating result string
2389 * @param msg Results of search
2390 * @param field Attribute to retrieve
2391 * @return Result strings in talloc context
2392 **/
2393 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2394 LDAPMessage *msg, const char *field,
2395 size_t *num_values)
2396{
2397 char **values;
2398 char **ret = NULL;
2399 int i;
2400 size_t converted_size;
2401
2402 values = ldap_get_values(ads->ldap.ld, msg, field);
2403 if (!values)
2404 return NULL;
2405
2406 *num_values = ldap_count_values(values);
2407
2408 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2409 if (!ret) {
2410 ldap_value_free(values);
2411 return NULL;
2412 }
2413
2414 for (i=0;i<*num_values;i++) {
2415 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2416 &converted_size))
2417 {
2418 ldap_value_free(values);
2419 return NULL;
2420 }
2421 }
2422 ret[i] = NULL;
2423
2424 ldap_value_free(values);
2425 return ret;
2426}
2427
2428/**
2429 * pull an array of strings from a ADS result
2430 * (handle large multivalue attributes with range retrieval)
2431 * @param ads connection to ads server
2432 * @param mem_ctx TALLOC_CTX to use for allocating result string
2433 * @param msg Results of search
2434 * @param field Attribute to retrieve
2435 * @param current_strings strings returned by a previous call to this function
2436 * @param next_attribute The next query should ask for this attribute
2437 * @param num_values How many values did we get this time?
2438 * @param more_values Are there more values to get?
2439 * @return Result strings in talloc context
2440 **/
2441 char **ads_pull_strings_range(ADS_STRUCT *ads,
2442 TALLOC_CTX *mem_ctx,
2443 LDAPMessage *msg, const char *field,
2444 char **current_strings,
2445 const char **next_attribute,
2446 size_t *num_strings,
2447 bool *more_strings)
2448{
2449 char *attr;
2450 char *expected_range_attrib, *range_attr;
2451 BerElement *ptr = NULL;
2452 char **strings;
2453 char **new_strings;
2454 size_t num_new_strings;
2455 unsigned long int range_start;
2456 unsigned long int range_end;
2457
2458 /* we might have been given the whole lot anyway */
2459 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2460 *more_strings = False;
2461 return strings;
2462 }
2463
2464 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2465
2466 /* look for Range result */
2467 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2468 attr;
2469 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2470 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2471 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2472 range_attr = attr;
2473 break;
2474 }
2475 ldap_memfree(attr);
2476 }
2477 if (!attr) {
2478 ber_free(ptr, 0);
2479 /* nothing here - this field is just empty */
2480 *more_strings = False;
2481 return NULL;
2482 }
2483
2484 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2485 &range_start, &range_end) == 2) {
2486 *more_strings = True;
2487 } else {
2488 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2489 &range_start) == 1) {
2490 *more_strings = False;
2491 } else {
2492 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2493 range_attr));
2494 ldap_memfree(range_attr);
2495 *more_strings = False;
2496 return NULL;
2497 }
2498 }
2499
2500 if ((*num_strings) != range_start) {
2501 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2502 " - aborting range retreival\n",
2503 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2504 ldap_memfree(range_attr);
2505 *more_strings = False;
2506 return NULL;
2507 }
2508
2509 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2510
2511 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2512 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2513 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2514 range_attr, (unsigned long int)range_end - range_start + 1,
2515 (unsigned long int)num_new_strings));
2516 ldap_memfree(range_attr);
2517 *more_strings = False;
2518 return NULL;
2519 }
2520
2521 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2522 *num_strings + num_new_strings);
2523
2524 if (strings == NULL) {
2525 ldap_memfree(range_attr);
2526 *more_strings = False;
2527 return NULL;
2528 }
2529
2530 if (new_strings && num_new_strings) {
2531 memcpy(&strings[*num_strings], new_strings,
2532 sizeof(*new_strings) * num_new_strings);
2533 }
2534
2535 (*num_strings) += num_new_strings;
2536
2537 if (*more_strings) {
2538 *next_attribute = talloc_asprintf(mem_ctx,
2539 "%s;range=%d-*",
2540 field,
2541 (int)*num_strings);
2542
2543 if (!*next_attribute) {
2544 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2545 ldap_memfree(range_attr);
2546 *more_strings = False;
2547 return NULL;
2548 }
2549 }
2550
2551 ldap_memfree(range_attr);
2552
2553 return strings;
2554}
2555
2556/**
2557 * pull a single uint32 from a ADS result
2558 * @param ads connection to ads server
2559 * @param msg Results of search
2560 * @param field Attribute to retrieve
2561 * @param v Pointer to int to store result
2562 * @return boolean inidicating success
2563*/
2564 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2565 uint32 *v)
2566{
2567 char **values;
2568
2569 values = ldap_get_values(ads->ldap.ld, msg, field);
2570 if (!values)
2571 return False;
2572 if (!values[0]) {
2573 ldap_value_free(values);
2574 return False;
2575 }
2576
2577 *v = atoi(values[0]);
2578 ldap_value_free(values);
2579 return True;
2580}
2581
2582/**
2583 * pull a single objectGUID from an ADS result
2584 * @param ads connection to ADS server
2585 * @param msg results of search
2586 * @param guid 37-byte area to receive text guid
2587 * @return boolean indicating success
2588 **/
2589 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2590{
2591 char **values;
2592 UUID_FLAT flat_guid;
2593
2594 values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2595 if (!values)
2596 return False;
2597
2598 if (values[0]) {
2599 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2600 smb_uuid_unpack(flat_guid, guid);
2601 ldap_value_free(values);
2602 return True;
2603 }
2604 ldap_value_free(values);
2605 return False;
2606
2607}
2608
2609
2610/**
2611 * pull a single DOM_SID from a ADS result
2612 * @param ads connection to ads server
2613 * @param msg Results of search
2614 * @param field Attribute to retrieve
2615 * @param sid Pointer to sid to store result
2616 * @return boolean inidicating success
2617*/
2618 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2619 DOM_SID *sid)
2620{
2621 struct berval **values;
2622 bool ret = False;
2623
2624 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2625
2626 if (!values)
2627 return False;
2628
2629 if (values[0])
2630 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2631
2632 ldap_value_free_len(values);
2633 return ret;
2634}
2635
2636/**
2637 * pull an array of DOM_SIDs from a ADS result
2638 * @param ads connection to ads server
2639 * @param mem_ctx TALLOC_CTX for allocating sid array
2640 * @param msg Results of search
2641 * @param field Attribute to retrieve
2642 * @param sids pointer to sid array to allocate
2643 * @return the count of SIDs pulled
2644 **/
2645 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2646 LDAPMessage *msg, const char *field, DOM_SID **sids)
2647{
2648 struct berval **values;
2649 bool ret;
2650 int count, i;
2651
2652 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2653
2654 if (!values)
2655 return 0;
2656
2657 for (i=0; values[i]; i++)
2658 /* nop */ ;
2659
2660 if (i) {
2661 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2662 if (!(*sids)) {
2663 ldap_value_free_len(values);
2664 return 0;
2665 }
2666 } else {
2667 (*sids) = NULL;
2668 }
2669
2670 count = 0;
2671 for (i=0; values[i]; i++) {
2672 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2673 if (ret) {
2674 DEBUG(10, ("pulling SID: %s\n",
2675 sid_string_dbg(&(*sids)[count])));
2676 count++;
2677 }
2678 }
2679
2680 ldap_value_free_len(values);
2681 return count;
2682}
2683
2684/**
2685 * pull a SEC_DESC from a ADS result
2686 * @param ads connection to ads server
2687 * @param mem_ctx TALLOC_CTX for allocating sid array
2688 * @param msg Results of search
2689 * @param field Attribute to retrieve
2690 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2691 * @return boolean inidicating success
2692*/
2693 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2694 LDAPMessage *msg, const char *field, SEC_DESC **sd)
2695{
2696 struct berval **values;
2697 bool ret = true;
2698
2699 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2700
2701 if (!values) return false;
2702
2703 if (values[0]) {
2704 NTSTATUS status;
2705 status = unmarshall_sec_desc(mem_ctx,
2706 (uint8 *)values[0]->bv_val,
2707 values[0]->bv_len, sd);
2708 if (!NT_STATUS_IS_OK(status)) {
2709 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2710 nt_errstr(status)));
2711 ret = false;
2712 }
2713 }
2714
2715 ldap_value_free_len(values);
2716 return ret;
2717}
2718
2719/*
2720 * in order to support usernames longer than 21 characters we need to
2721 * use both the sAMAccountName and the userPrincipalName attributes
2722 * It seems that not all users have the userPrincipalName attribute set
2723 *
2724 * @param ads connection to ads server
2725 * @param mem_ctx TALLOC_CTX for allocating sid array
2726 * @param msg Results of search
2727 * @return the username
2728 */
2729 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2730 LDAPMessage *msg)
2731{
2732#if 0 /* JERRY */
2733 char *ret, *p;
2734
2735 /* lookup_name() only works on the sAMAccountName to
2736 returning the username portion of userPrincipalName
2737 breaks winbindd_getpwnam() */
2738
2739 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2740 if (ret && (p = strchr_m(ret, '@'))) {
2741 *p = 0;
2742 return ret;
2743 }
2744#endif
2745 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2746}
2747
2748
2749/**
2750 * find the update serial number - this is the core of the ldap cache
2751 * @param ads connection to ads server
2752 * @param ads connection to ADS server
2753 * @param usn Pointer to retrieved update serial number
2754 * @return status of search
2755 **/
2756ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2757{
2758 const char *attrs[] = {"highestCommittedUSN", NULL};
2759 ADS_STATUS status;
2760 LDAPMessage *res;
2761
2762 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2763 if (!ADS_ERR_OK(status))
2764 return status;
2765
2766 if (ads_count_replies(ads, res) != 1) {
2767 ads_msgfree(ads, res);
2768 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2769 }
2770
2771 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2772 ads_msgfree(ads, res);
2773 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2774 }
2775
2776 ads_msgfree(ads, res);
2777 return ADS_SUCCESS;
2778}
2779
2780/* parse a ADS timestring - typical string is
2781 '20020917091222.0Z0' which means 09:12.22 17th September
2782 2002, timezone 0 */
2783static time_t ads_parse_time(const char *str)
2784{
2785 struct tm tm;
2786
2787 ZERO_STRUCT(tm);
2788
2789 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2790 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2791 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2792 return 0;
2793 }
2794 tm.tm_year -= 1900;
2795 tm.tm_mon -= 1;
2796
2797 return timegm(&tm);
2798}
2799
2800/********************************************************************
2801********************************************************************/
2802
2803ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2804{
2805 const char *attrs[] = {"currentTime", NULL};
2806 ADS_STATUS status;
2807 LDAPMessage *res;
2808 char *timestr;
2809 TALLOC_CTX *ctx;
2810 ADS_STRUCT *ads_s = ads;
2811
2812 if (!(ctx = talloc_init("ads_current_time"))) {
2813 return ADS_ERROR(LDAP_NO_MEMORY);
2814 }
2815
2816 /* establish a new ldap tcp session if necessary */
2817
2818 if ( !ads->ldap.ld ) {
2819 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2820 ads->server.ldap_server )) == NULL )
2821 {
2822 goto done;
2823 }
2824 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2825 status = ads_connect( ads_s );
2826 if ( !ADS_ERR_OK(status))
2827 goto done;
2828 }
2829
2830 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2831 if (!ADS_ERR_OK(status)) {
2832 goto done;
2833 }
2834
2835 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2836 if (!timestr) {
2837 ads_msgfree(ads_s, res);
2838 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2839 goto done;
2840 }
2841
2842 /* but save the time and offset in the original ADS_STRUCT */
2843
2844 ads->config.current_time = ads_parse_time(timestr);
2845
2846 if (ads->config.current_time != 0) {
2847 ads->auth.time_offset = ads->config.current_time - time(NULL);
2848 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2849 }
2850
2851 ads_msgfree(ads, res);
2852
2853 status = ADS_SUCCESS;
2854
2855done:
2856 /* free any temporary ads connections */
2857 if ( ads_s != ads ) {
2858 ads_destroy( &ads_s );
2859 }
2860 talloc_destroy(ctx);
2861
2862 return status;
2863}
2864
2865/********************************************************************
2866********************************************************************/
2867
2868ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2869{
2870 const char *attrs[] = {"domainFunctionality", NULL};
2871 ADS_STATUS status;
2872 LDAPMessage *res;
2873 ADS_STRUCT *ads_s = ads;
2874
2875 *val = DS_DOMAIN_FUNCTION_2000;
2876
2877 /* establish a new ldap tcp session if necessary */
2878
2879 if ( !ads->ldap.ld ) {
2880 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2881 ads->server.ldap_server )) == NULL )
2882 {
2883 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2884 goto done;
2885 }
2886 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2887 status = ads_connect( ads_s );
2888 if ( !ADS_ERR_OK(status))
2889 goto done;
2890 }
2891
2892 /* If the attribute does not exist assume it is a Windows 2000
2893 functional domain */
2894
2895 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2896 if (!ADS_ERR_OK(status)) {
2897 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2898 status = ADS_SUCCESS;
2899 }
2900 goto done;
2901 }
2902
2903 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2904 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2905 }
2906 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2907
2908
2909 ads_msgfree(ads, res);
2910
2911done:
2912 /* free any temporary ads connections */
2913 if ( ads_s != ads ) {
2914 ads_destroy( &ads_s );
2915 }
2916
2917 return status;
2918}
2919
2920/**
2921 * find the domain sid for our domain
2922 * @param ads connection to ads server
2923 * @param sid Pointer to domain sid
2924 * @return status of search
2925 **/
2926ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2927{
2928 const char *attrs[] = {"objectSid", NULL};
2929 LDAPMessage *res;
2930 ADS_STATUS rc;
2931
2932 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2933 attrs, &res);
2934 if (!ADS_ERR_OK(rc)) return rc;
2935 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2936 ads_msgfree(ads, res);
2937 return ADS_ERROR_SYSTEM(ENOENT);
2938 }
2939 ads_msgfree(ads, res);
2940
2941 return ADS_SUCCESS;
2942}
2943
2944/**
2945 * find our site name
2946 * @param ads connection to ads server
2947 * @param mem_ctx Pointer to talloc context
2948 * @param site_name Pointer to the sitename
2949 * @return status of search
2950 **/
2951ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2952{
2953 ADS_STATUS status;
2954 LDAPMessage *res;
2955 const char *dn, *service_name;
2956 const char *attrs[] = { "dsServiceName", NULL };
2957
2958 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2959 if (!ADS_ERR_OK(status)) {
2960 return status;
2961 }
2962
2963 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2964 if (service_name == NULL) {
2965 ads_msgfree(ads, res);
2966 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2967 }
2968
2969 ads_msgfree(ads, res);
2970
2971 /* go up three levels */
2972 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2973 if (dn == NULL) {
2974 return ADS_ERROR(LDAP_NO_MEMORY);
2975 }
2976
2977 *site_name = talloc_strdup(mem_ctx, dn);
2978 if (*site_name == NULL) {
2979 return ADS_ERROR(LDAP_NO_MEMORY);
2980 }
2981
2982 return status;
2983 /*
2984 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2985 */
2986}
2987
2988/**
2989 * find the site dn where a machine resides
2990 * @param ads connection to ads server
2991 * @param mem_ctx Pointer to talloc context
2992 * @param computer_name name of the machine
2993 * @param site_name Pointer to the sitename
2994 * @return status of search
2995 **/
2996ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2997{
2998 ADS_STATUS status;
2999 LDAPMessage *res;
3000 const char *parent, *filter;
3001 char *config_context = NULL;
3002 char *dn;
3003
3004 /* shortcut a query */
3005 if (strequal(computer_name, ads->config.ldap_server_name)) {
3006 return ads_site_dn(ads, mem_ctx, site_dn);
3007 }
3008
3009 status = ads_config_path(ads, mem_ctx, &config_context);
3010 if (!ADS_ERR_OK(status)) {
3011 return status;
3012 }
3013
3014 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3015 if (filter == NULL) {
3016 return ADS_ERROR(LDAP_NO_MEMORY);
3017 }
3018
3019 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3020 filter, NULL, &res);
3021 if (!ADS_ERR_OK(status)) {
3022 return status;
3023 }
3024
3025 if (ads_count_replies(ads, res) != 1) {
3026 ads_msgfree(ads, res);
3027 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3028 }
3029
3030 dn = ads_get_dn(ads, res);
3031 if (dn == NULL) {
3032 ads_msgfree(ads, res);
3033 return ADS_ERROR(LDAP_NO_MEMORY);
3034 }
3035
3036 /* go up three levels */
3037 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3038 if (parent == NULL) {
3039 ads_msgfree(ads, res);
3040 ads_memfree(ads, dn);
3041 return ADS_ERROR(LDAP_NO_MEMORY);
3042 }
3043
3044 *site_dn = talloc_strdup(mem_ctx, parent);
3045 if (*site_dn == NULL) {
3046 ads_msgfree(ads, res);
3047 ads_memfree(ads, dn);
3048 return ADS_ERROR(LDAP_NO_MEMORY);
3049 }
3050
3051 ads_memfree(ads, dn);
3052 ads_msgfree(ads, res);
3053
3054 return status;
3055}
3056
3057/**
3058 * get the upn suffixes for a domain
3059 * @param ads connection to ads server
3060 * @param mem_ctx Pointer to talloc context
3061 * @param suffixes Pointer to an array of suffixes
3062 * @param num_suffixes Pointer to the number of suffixes
3063 * @return status of search
3064 **/
3065ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3066{
3067 ADS_STATUS status;
3068 LDAPMessage *res;
3069 const char *base;
3070 char *config_context = NULL;
3071 const char *attrs[] = { "uPNSuffixes", NULL };
3072
3073 status = ads_config_path(ads, mem_ctx, &config_context);
3074 if (!ADS_ERR_OK(status)) {
3075 return status;
3076 }
3077
3078 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3079 if (base == NULL) {
3080 return ADS_ERROR(LDAP_NO_MEMORY);
3081 }
3082
3083 status = ads_search_dn(ads, &res, base, attrs);
3084 if (!ADS_ERR_OK(status)) {
3085 return status;
3086 }
3087
3088 if (ads_count_replies(ads, res) != 1) {
3089 ads_msgfree(ads, res);
3090 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3091 }
3092
3093 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3094 if ((*suffixes) == NULL) {
3095 ads_msgfree(ads, res);
3096 return ADS_ERROR(LDAP_NO_MEMORY);
3097 }
3098
3099 ads_msgfree(ads, res);
3100
3101 return status;
3102}
3103
3104/**
3105 * get the joinable ous for a domain
3106 * @param ads connection to ads server
3107 * @param mem_ctx Pointer to talloc context
3108 * @param ous Pointer to an array of ous
3109 * @param num_ous Pointer to the number of ous
3110 * @return status of search
3111 **/
3112ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3113 TALLOC_CTX *mem_ctx,
3114 char ***ous,
3115 size_t *num_ous)
3116{
3117 ADS_STATUS status;
3118 LDAPMessage *res = NULL;
3119 LDAPMessage *msg = NULL;
3120 const char *attrs[] = { "dn", NULL };
3121 int count = 0;
3122
3123 status = ads_search(ads, &res,
3124 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3125 attrs);
3126 if (!ADS_ERR_OK(status)) {
3127 return status;
3128 }
3129
3130 count = ads_count_replies(ads, res);
3131 if (count < 1) {
3132 ads_msgfree(ads, res);
3133 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3134 }
3135
3136 for (msg = ads_first_entry(ads, res); msg;
3137 msg = ads_next_entry(ads, msg)) {
3138
3139 char *dn = NULL;
3140
3141 dn = ads_get_dn(ads, msg);
3142 if (!dn) {
3143 ads_msgfree(ads, res);
3144 return ADS_ERROR(LDAP_NO_MEMORY);
3145 }
3146
3147 if (!add_string_to_array(mem_ctx, dn,
3148 (const char ***)ous,
3149 (int *)num_ous)) {
3150 ads_memfree(ads, dn);
3151 ads_msgfree(ads, res);
3152 return ADS_ERROR(LDAP_NO_MEMORY);
3153 }
3154
3155 ads_memfree(ads, dn);
3156 }
3157
3158 ads_msgfree(ads, res);
3159
3160 return status;
3161}
3162
3163
3164/**
3165 * pull a DOM_SID from an extended dn string
3166 * @param mem_ctx TALLOC_CTX
3167 * @param extended_dn string
3168 * @param flags string type of extended_dn
3169 * @param sid pointer to a DOM_SID
3170 * @return NT_STATUS_OK on success,
3171 * NT_INVALID_PARAMETER on error,
3172 * NT_STATUS_NOT_FOUND if no SID present
3173 **/
3174ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3175 const char *extended_dn,
3176 enum ads_extended_dn_flags flags,
3177 DOM_SID *sid)
3178{
3179 char *p, *q, *dn;
3180
3181 if (!extended_dn) {
3182 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3183 }
3184
3185 /* otherwise extended_dn gets stripped off */
3186 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3187 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3188 }
3189 /*
3190 * ADS_EXTENDED_DN_HEX_STRING:
3191 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3192 *
3193 * ADS_EXTENDED_DN_STRING (only with w2k3):
3194 * <GUID=63198e23-39cb-4b0f-b032-ba0105525a29>;<SID=S-1-5-21-4257769659-666132843-1169174103-1110>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3195 *
3196 * Object with no SID, such as an Exchange Public Folder
3197 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3198 */
3199
3200 p = strchr(dn, ';');
3201 if (!p) {
3202 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3203 }
3204
3205 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3206 DEBUG(5,("No SID present in extended dn\n"));
3207 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3208 }
3209
3210 p += strlen(";<SID=");
3211
3212 q = strchr(p, '>');
3213 if (!q) {
3214 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3215 }
3216
3217 *q = '\0';
3218
3219 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3220
3221 switch (flags) {
3222
3223 case ADS_EXTENDED_DN_STRING:
3224 if (!string_to_sid(sid, p)) {
3225 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3226 }
3227 break;
3228 case ADS_EXTENDED_DN_HEX_STRING: {
3229 fstring buf;
3230 size_t buf_len;
3231
3232 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3233 if (buf_len == 0) {
3234 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3235 }
3236
3237 if (!sid_parse(buf, buf_len, sid)) {
3238 DEBUG(10,("failed to parse sid\n"));
3239 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3240 }
3241 break;
3242 }
3243 default:
3244 DEBUG(10,("unknown extended dn format\n"));
3245 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3246 }
3247
3248 return ADS_ERROR_NT(NT_STATUS_OK);
3249}
3250
3251/**
3252 * pull an array of DOM_SIDs from a ADS result
3253 * @param ads connection to ads server
3254 * @param mem_ctx TALLOC_CTX for allocating sid array
3255 * @param msg Results of search
3256 * @param field Attribute to retrieve
3257 * @param flags string type of extended_dn
3258 * @param sids pointer to sid array to allocate
3259 * @return the count of SIDs pulled
3260 **/
3261 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
3262 TALLOC_CTX *mem_ctx,
3263 LDAPMessage *msg,
3264 const char *field,
3265 enum ads_extended_dn_flags flags,
3266 DOM_SID **sids)
3267{
3268 int i;
3269 ADS_STATUS rc;
3270 size_t dn_count, ret_count = 0;
3271 char **dn_strings;
3272
3273 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
3274 &dn_count)) == NULL) {
3275 return 0;
3276 }
3277
3278 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
3279 if (!(*sids)) {
3280 TALLOC_FREE(dn_strings);
3281 return 0;
3282 }
3283
3284 for (i=0; i<dn_count; i++) {
3285 rc = ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
3286 flags, &(*sids)[i]);
3287 if (!ADS_ERR_OK(rc)) {
3288 if (NT_STATUS_EQUAL(ads_ntstatus(rc),
3289 NT_STATUS_NOT_FOUND)) {
3290 continue;
3291 }
3292 else {
3293 TALLOC_FREE(*sids);
3294 TALLOC_FREE(dn_strings);
3295 return 0;
3296 }
3297 }
3298 ret_count++;
3299 }
3300
3301 TALLOC_FREE(dn_strings);
3302
3303 return ret_count;
3304}
3305
3306/********************************************************************
3307********************************************************************/
3308
3309char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3310{
3311 LDAPMessage *res = NULL;
3312 ADS_STATUS status;
3313 int count = 0;
3314 char *name = NULL;
3315
3316 status = ads_find_machine_acct(ads, &res, global_myname());
3317 if (!ADS_ERR_OK(status)) {
3318 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3319 global_myname()));
3320 goto out;
3321 }
3322
3323 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3324 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3325 goto out;
3326 }
3327
3328 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3329 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3330 }
3331
3332out:
3333 ads_msgfree(ads, res);
3334
3335 return name;
3336}
3337
3338/********************************************************************
3339********************************************************************/
3340
3341char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3342{
3343 LDAPMessage *res = NULL;
3344 ADS_STATUS status;
3345 int count = 0;
3346 char *name = NULL;
3347
3348 status = ads_find_machine_acct(ads, &res, machine_name);
3349 if (!ADS_ERR_OK(status)) {
3350 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3351 global_myname()));
3352 goto out;
3353 }
3354
3355 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3356 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3357 goto out;
3358 }
3359
3360 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3361 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3362 }
3363
3364out:
3365 ads_msgfree(ads, res);
3366
3367 return name;
3368}
3369
3370/********************************************************************
3371********************************************************************/
3372
3373char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3374{
3375 LDAPMessage *res = NULL;
3376 ADS_STATUS status;
3377 int count = 0;
3378 char *name = NULL;
3379
3380 status = ads_find_machine_acct(ads, &res, global_myname());
3381 if (!ADS_ERR_OK(status)) {
3382 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3383 global_myname()));
3384 goto out;
3385 }
3386
3387 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3388 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3389 goto out;
3390 }
3391
3392 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3393 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3394 }
3395
3396out:
3397 ads_msgfree(ads, res);
3398
3399 return name;
3400}
3401
3402#if 0
3403
3404 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3405
3406/**
3407 * Join a machine to a realm
3408 * Creates the machine account and sets the machine password
3409 * @param ads connection to ads server
3410 * @param machine name of host to add
3411 * @param org_unit Organizational unit to place machine in
3412 * @return status of join
3413 **/
3414ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3415 uint32 account_type, const char *org_unit)
3416{
3417 ADS_STATUS status;
3418 LDAPMessage *res = NULL;
3419 char *machine;
3420
3421 /* machine name must be lowercase */
3422 machine = SMB_STRDUP(machine_name);
3423 strlower_m(machine);
3424
3425 /*
3426 status = ads_find_machine_acct(ads, (void **)&res, machine);
3427 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3428 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3429 status = ads_leave_realm(ads, machine);
3430 if (!ADS_ERR_OK(status)) {
3431 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3432 machine, ads->config.realm));
3433 return status;
3434 }
3435 }
3436 */
3437 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3438 if (!ADS_ERR_OK(status)) {
3439 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3440 SAFE_FREE(machine);
3441 return status;
3442 }
3443
3444 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3445 if (!ADS_ERR_OK(status)) {
3446 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3447 SAFE_FREE(machine);
3448 return status;
3449 }
3450
3451 SAFE_FREE(machine);
3452 ads_msgfree(ads, res);
3453
3454 return status;
3455}
3456#endif
3457
3458/**
3459 * Delete a machine from the realm
3460 * @param ads connection to ads server
3461 * @param hostname Machine to remove
3462 * @return status of delete
3463 **/
3464ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3465{
3466 ADS_STATUS status;
3467 void *msg;
3468 LDAPMessage *res;
3469 char *hostnameDN, *host;
3470 int rc;
3471 LDAPControl ldap_control;
3472 LDAPControl * pldap_control[2] = {NULL, NULL};
3473
3474 pldap_control[0] = &ldap_control;
3475 memset(&ldap_control, 0, sizeof(LDAPControl));
3476 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3477
3478 /* hostname must be lowercase */
3479 host = SMB_STRDUP(hostname);
3480 strlower_m(host);
3481
3482 status = ads_find_machine_acct(ads, &res, host);
3483 if (!ADS_ERR_OK(status)) {
3484 DEBUG(0, ("Host account for %s does not exist.\n", host));
3485 SAFE_FREE(host);
3486 return status;
3487 }
3488
3489 msg = ads_first_entry(ads, res);
3490 if (!msg) {
3491 SAFE_FREE(host);
3492 return ADS_ERROR_SYSTEM(ENOENT);
3493 }
3494
3495 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
3496
3497 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3498 if (rc) {
3499 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3500 }else {
3501 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3502 }
3503
3504 if (rc != LDAP_SUCCESS) {
3505 const char *attrs[] = { "cn", NULL };
3506 LDAPMessage *msg_sub;
3507
3508 /* we only search with scope ONE, we do not expect any further
3509 * objects to be created deeper */
3510
3511 status = ads_do_search_retry(ads, hostnameDN,
3512 LDAP_SCOPE_ONELEVEL,
3513 "(objectclass=*)", attrs, &res);
3514
3515 if (!ADS_ERR_OK(status)) {
3516 SAFE_FREE(host);
3517 ads_memfree(ads, hostnameDN);
3518 return status;
3519 }
3520
3521 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3522 msg_sub = ads_next_entry(ads, msg_sub)) {
3523
3524 char *dn = NULL;
3525
3526 if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3527 SAFE_FREE(host);
3528 ads_memfree(ads, hostnameDN);
3529 return ADS_ERROR(LDAP_NO_MEMORY);
3530 }
3531
3532 status = ads_del_dn(ads, dn);
3533 if (!ADS_ERR_OK(status)) {
3534 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3535 SAFE_FREE(host);
3536 ads_memfree(ads, dn);
3537 ads_memfree(ads, hostnameDN);
3538 return status;
3539 }
3540
3541 ads_memfree(ads, dn);
3542 }
3543
3544 /* there should be no subordinate objects anymore */
3545 status = ads_do_search_retry(ads, hostnameDN,
3546 LDAP_SCOPE_ONELEVEL,
3547 "(objectclass=*)", attrs, &res);
3548
3549 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3550 SAFE_FREE(host);
3551 ads_memfree(ads, hostnameDN);
3552 return status;
3553 }
3554
3555 /* delete hostnameDN now */
3556 status = ads_del_dn(ads, hostnameDN);
3557 if (!ADS_ERR_OK(status)) {
3558 SAFE_FREE(host);
3559 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3560 ads_memfree(ads, hostnameDN);
3561 return status;
3562 }
3563 }
3564
3565 ads_memfree(ads, hostnameDN);
3566
3567 status = ads_find_machine_acct(ads, &res, host);
3568 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3569 DEBUG(3, ("Failed to remove host account.\n"));
3570 SAFE_FREE(host);
3571 return status;
3572 }
3573
3574 SAFE_FREE(host);
3575 return status;
3576}
3577
3578/**
3579 * pull all token-sids from an LDAP dn
3580 * @param ads connection to ads server
3581 * @param mem_ctx TALLOC_CTX for allocating sid array
3582 * @param dn of LDAP object
3583 * @param user_sid pointer to DOM_SID (objectSid)
3584 * @param primary_group_sid pointer to DOM_SID (self composed)
3585 * @param sids pointer to sid array to allocate
3586 * @param num_sids counter of SIDs pulled
3587 * @return status of token query
3588 **/
3589 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3590 TALLOC_CTX *mem_ctx,
3591 const char *dn,
3592 DOM_SID *user_sid,
3593 DOM_SID *primary_group_sid,
3594 DOM_SID **sids,
3595 size_t *num_sids)
3596{
3597 ADS_STATUS status;
3598 LDAPMessage *res = NULL;
3599 int count = 0;
3600 size_t tmp_num_sids;
3601 DOM_SID *tmp_sids;
3602 DOM_SID tmp_user_sid;
3603 DOM_SID tmp_primary_group_sid;
3604 uint32 pgid;
3605 const char *attrs[] = {
3606 "objectSid",
3607 "tokenGroups",
3608 "primaryGroupID",
3609 NULL
3610 };
3611
3612 status = ads_search_retry_dn(ads, &res, dn, attrs);
3613 if (!ADS_ERR_OK(status)) {
3614 return status;
3615 }
3616
3617 count = ads_count_replies(ads, res);
3618 if (count != 1) {
3619 ads_msgfree(ads, res);
3620 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3621 }
3622
3623 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3624 ads_msgfree(ads, res);
3625 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3626 }
3627
3628 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3629 ads_msgfree(ads, res);
3630 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3631 }
3632
3633 {
3634 /* hack to compose the primary group sid without knowing the
3635 * domsid */
3636
3637 DOM_SID domsid;
3638 uint32 dummy_rid;
3639
3640 sid_copy(&domsid, &tmp_user_sid);
3641
3642 if (!sid_split_rid(&domsid, &dummy_rid)) {
3643 ads_msgfree(ads, res);
3644 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3645 }
3646
3647 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3648 ads_msgfree(ads, res);
3649 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3650 }
3651 }
3652
3653 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3654
3655 if (tmp_num_sids == 0 || !tmp_sids) {
3656 ads_msgfree(ads, res);
3657 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3658 }
3659
3660 if (num_sids) {
3661 *num_sids = tmp_num_sids;
3662 }
3663
3664 if (sids) {
3665 *sids = tmp_sids;
3666 }
3667
3668 if (user_sid) {
3669 *user_sid = tmp_user_sid;
3670 }
3671
3672 if (primary_group_sid) {
3673 *primary_group_sid = tmp_primary_group_sid;
3674 }
3675
3676 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3677
3678 ads_msgfree(ads, res);
3679 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3680}
3681
3682/**
3683 * Find a sAMAccoutName in LDAP
3684 * @param ads connection to ads server
3685 * @param mem_ctx TALLOC_CTX for allocating sid array
3686 * @param samaccountname to search
3687 * @param uac_ret uint32 pointer userAccountControl attribute value
3688 * @param dn_ret pointer to dn
3689 * @return status of token query
3690 **/
3691ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3692 TALLOC_CTX *mem_ctx,
3693 const char *samaccountname,
3694 uint32 *uac_ret,
3695 const char **dn_ret)
3696{
3697 ADS_STATUS status;
3698 const char *attrs[] = { "userAccountControl", NULL };
3699 const char *filter;
3700 LDAPMessage *res = NULL;
3701 char *dn = NULL;
3702 uint32 uac = 0;
3703
3704 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3705 samaccountname);
3706 if (filter == NULL) {
3707 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3708 goto out;
3709 }
3710
3711 status = ads_do_search_all(ads, ads->config.bind_path,
3712 LDAP_SCOPE_SUBTREE,
3713 filter, attrs, &res);
3714
3715 if (!ADS_ERR_OK(status)) {
3716 goto out;
3717 }
3718
3719 if (ads_count_replies(ads, res) != 1) {
3720 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3721 goto out;
3722 }
3723
3724 dn = ads_get_dn(ads, res);
3725 if (dn == NULL) {
3726 status = ADS_ERROR(LDAP_NO_MEMORY);
3727 goto out;
3728 }
3729
3730 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3731 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3732 goto out;
3733 }
3734
3735 if (uac_ret) {
3736 *uac_ret = uac;
3737 }
3738
3739 if (dn_ret) {
3740 *dn_ret = talloc_strdup(mem_ctx, dn);
3741 if (!*dn_ret) {
3742 status = ADS_ERROR(LDAP_NO_MEMORY);
3743 goto out;
3744 }
3745 }
3746 out:
3747 ads_memfree(ads, dn);
3748 ads_msgfree(ads, res);
3749
3750 return status;
3751}
3752
3753/**
3754 * find our configuration path
3755 * @param ads connection to ads server
3756 * @param mem_ctx Pointer to talloc context
3757 * @param config_path Pointer to the config path
3758 * @return status of search
3759 **/
3760ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3761 TALLOC_CTX *mem_ctx,
3762 char **config_path)
3763{
3764 ADS_STATUS status;
3765 LDAPMessage *res = NULL;
3766 const char *config_context = NULL;
3767 const char *attrs[] = { "configurationNamingContext", NULL };
3768
3769 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3770 "(objectclass=*)", attrs, &res);
3771 if (!ADS_ERR_OK(status)) {
3772 return status;
3773 }
3774
3775 config_context = ads_pull_string(ads, mem_ctx, res,
3776 "configurationNamingContext");
3777 ads_msgfree(ads, res);
3778 if (!config_context) {
3779 return ADS_ERROR(LDAP_NO_MEMORY);
3780 }
3781
3782 if (config_path) {
3783 *config_path = talloc_strdup(mem_ctx, config_context);
3784 if (!*config_path) {
3785 return ADS_ERROR(LDAP_NO_MEMORY);
3786 }
3787 }
3788
3789 return ADS_ERROR(LDAP_SUCCESS);
3790}
3791
3792/**
3793 * find the displayName of an extended right
3794 * @param ads connection to ads server
3795 * @param config_path The config path
3796 * @param mem_ctx Pointer to talloc context
3797 * @param GUID struct of the rightsGUID
3798 * @return status of search
3799 **/
3800const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3801 const char *config_path,
3802 TALLOC_CTX *mem_ctx,
3803 const struct GUID *rights_guid)
3804{
3805 ADS_STATUS rc;
3806 LDAPMessage *res = NULL;
3807 char *expr = NULL;
3808 const char *attrs[] = { "displayName", NULL };
3809 const char *result = NULL;
3810 const char *path;
3811
3812 if (!ads || !mem_ctx || !rights_guid) {
3813 goto done;
3814 }
3815
3816 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3817 smb_uuid_string(mem_ctx, *rights_guid));
3818 if (!expr) {
3819 goto done;
3820 }
3821
3822 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3823 if (!path) {
3824 goto done;
3825 }
3826
3827 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3828 expr, attrs, &res);
3829 if (!ADS_ERR_OK(rc)) {
3830 goto done;
3831 }
3832
3833 if (ads_count_replies(ads, res) != 1) {
3834 goto done;
3835 }
3836
3837 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3838
3839 done:
3840 ads_msgfree(ads, res);
3841 return result;
3842
3843}
3844
3845/**
3846 * verify or build and verify an account ou
3847 * @param mem_ctx Pointer to talloc context
3848 * @param ads connection to ads server
3849 * @param account_ou
3850 * @return status of search
3851 **/
3852
3853ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3854 ADS_STRUCT *ads,
3855 const char **account_ou)
3856{
3857 struct ldb_dn *name_dn = NULL;
3858 const char *name = NULL;
3859 char *ou_string = NULL;
3860
3861 name_dn = ldb_dn_explode(mem_ctx, *account_ou);
3862 if (name_dn) {
3863 return ADS_SUCCESS;
3864 }
3865
3866 ou_string = ads_ou_string(ads, *account_ou);
3867 if (!ou_string) {
3868 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3869 }
3870
3871 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3872 ads->config.bind_path);
3873 SAFE_FREE(ou_string);
3874 if (!name) {
3875 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3876 }
3877
3878 name_dn = ldb_dn_explode(mem_ctx, name);
3879 if (!name_dn) {
3880 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3881 }
3882
3883 *account_ou = talloc_strdup(mem_ctx, name);
3884 if (!*account_ou) {
3885 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3886 }
3887
3888 return ADS_SUCCESS;
3889}
3890
3891#endif
Note: See TracBrowser for help on using the repository browser.