added nTSecurityDescriptor field to host acct dump
[ira/wip.git] / source3 / libads / ldap.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 3.0
4    ads (active directory) utility library
5    Copyright (C) Andrew Tridgell 2001
6    Copyright (C) Remus Koos 2001
7    
8    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 #include "includes.h"
25
26 #ifdef HAVE_ADS
27
28 /*
29   connect to the LDAP server
30 */
31 ADS_STATUS ads_connect(ADS_STRUCT *ads)
32 {
33         int version = LDAP_VERSION3;
34         ADS_STATUS status;
35
36         ads->last_attempt = time(NULL);
37
38         ads->ld = ldap_open(ads->ldap_server, ads->ldap_port);
39         if (!ads->ld) {
40                 return ADS_ERROR_SYSTEM(errno)
41         }
42         status = ads_server_info(ads);
43         if (!ADS_ERR_OK(status)) {
44                 DEBUG(1,("Failed to get ldap server info\n"));
45                 return status;
46         }
47
48         ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
49
50         if (ads->password) {
51                 ads_kinit_password(ads);
52         }
53
54         return ads_sasl_bind(ads);
55 }
56
57 /*
58   do a search with a timeout
59 */
60 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope, 
61                          const char *exp,
62                          const char **attrs, void **res)
63 {
64         struct timeval timeout;
65         int rc;
66
67         timeout.tv_sec = ADS_SEARCH_TIMEOUT;
68         timeout.tv_usec = 0;
69         *res = NULL;
70
71         rc = ldap_search_ext_s(ads->ld, 
72                                bind_path, scope,
73                                exp, attrs, 0, NULL, NULL, 
74                                &timeout, LDAP_NO_LIMIT, (LDAPMessage **)res);
75         return ADS_ERROR(rc);
76 }
77 /*
78   do a general ADS search
79 */
80 ADS_STATUS ads_search(ADS_STRUCT *ads, void **res, 
81                       const char *exp, 
82                       const char **attrs)
83 {
84         return ads_do_search(ads, ads->bind_path, LDAP_SCOPE_SUBTREE, 
85                              exp, attrs, res);
86 }
87
88 /*
89   do a search on a specific DistinguishedName
90 */
91 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, void **res, 
92                          const char *dn, 
93                          const char **attrs)
94 {
95         return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res);
96 }
97
98 /*
99   free up memory from a ads_search
100 */
101 void ads_msgfree(ADS_STRUCT *ads, void *msg)
102 {
103         if (!msg) return;
104         ldap_msgfree(msg);
105 }
106
107 /*
108   find a machine account given a hostname 
109 */
110 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *host)
111 {
112         ADS_STATUS status;
113         char *exp;
114         const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
115
116         /* the easiest way to find a machine account anywhere in the tree
117            is to look for hostname$ */
118         asprintf(&exp, "(samAccountName=%s$)", host);
119         status = ads_search(ads, res, exp, attrs);
120         free(exp);
121         return status;
122 }
123
124
125 /*
126   a convenient routine for adding a generic LDAP record 
127 */
128 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ...)
129 {
130         int i;
131         va_list ap;
132         LDAPMod **mods;
133         char *name, *value;
134         int ret;
135 #define MAX_MOD_VALUES 10
136         
137         /* count the number of attributes */
138         va_start(ap, new_dn);
139         for (i=0; va_arg(ap, char *); i++) {
140                 /* skip the values */
141                 while (va_arg(ap, char *)) ;
142         }
143         va_end(ap);
144
145         mods = malloc(sizeof(LDAPMod *) * (i+1));
146
147         va_start(ap, new_dn);
148         for (i=0; (name=va_arg(ap, char *)); i++) {
149                 char **values;
150                 int j;
151                 values = (char **)malloc(sizeof(char *) * (MAX_MOD_VALUES+1));
152                 for (j=0; (value=va_arg(ap, char *)) && j < MAX_MOD_VALUES; j++) {
153                         values[j] = value;
154                 }
155                 values[j] = NULL;
156                 mods[i] = malloc(sizeof(LDAPMod));
157                 mods[i]->mod_type = name;
158                 mods[i]->mod_op = LDAP_MOD_ADD;
159                 mods[i]->mod_values = values;
160         }
161         mods[i] = NULL;
162         va_end(ap);
163
164         ret = ldap_add_s(ads->ld, new_dn, mods);
165
166         for (i=0; mods[i]; i++) {
167                 free(mods[i]->mod_values);
168                 free(mods[i]);
169         }
170         free(mods);
171         
172         return ADS_ERROR(ret);
173 }
174
175 /*
176   add a machine account to the ADS server
177 */
178 static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *hostname, 
179                                        const char *org_unit)
180 {
181         ADS_STATUS ret;
182         char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
183
184         asprintf(&host_spn, "HOST/%s", hostname);
185         asprintf(&host_upn, "%s@%s", host_spn, ads->realm);
186         asprintf(&new_dn, "cn=%s,cn=%s,%s", hostname, org_unit, ads->bind_path);
187         asprintf(&samAccountName, "%s$", hostname);
188         asprintf(&controlstr, "%u", 
189                 UF_DONT_EXPIRE_PASSWD | UF_WORKSTATION_TRUST_ACCOUNT | 
190                 UF_TRUSTED_FOR_DELEGATION | UF_USE_DES_KEY_ONLY);
191     
192         ret = ads_gen_add(ads, new_dn,
193                            "cn", hostname, NULL,
194                            "sAMAccountName", samAccountName, NULL,
195                            "objectClass", 
196                               "top", "person", "organizationalPerson", 
197                               "user", "computer", NULL,
198                            "userPrincipalName", host_upn, NULL, 
199                            "servicePrincipalName", host_spn, NULL,
200                            "dNSHostName", hostname, NULL,
201                            "userAccountControl", controlstr, NULL,
202                            "operatingSystem", "Samba", NULL,
203                            "operatingSystemVersion", VERSION, NULL,
204                            NULL);
205
206         free(host_spn);
207         free(host_upn);
208         free(new_dn);
209         free(samAccountName);
210         free(controlstr);
211
212         return ret;
213 }
214
215 /*
216   dump a binary result from ldap
217 */
218 static void dump_binary(const char *field, struct berval **values)
219 {
220         int i, j;
221         for (i=0; values[i]; i++) {
222                 printf("%s: ", field);
223                 for (j=0; j<values[i]->bv_len; j++) {
224                         printf("%02X", (unsigned char)values[i]->bv_val[j]);
225                 }
226                 printf("\n");
227         }
228 }
229
230 /*
231   dump a sid result from ldap
232 */
233 static void dump_sid(const char *field, struct berval **values)
234 {
235         int i;
236         for (i=0; values[i]; i++) {
237                 DOM_SID sid;
238                 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
239                 printf("%s: %s\n", field, sid_string_static(&sid));
240         }
241 }
242
243 /*
244   dump a string result from ldap
245 */
246 static void dump_string(const char *field, struct berval **values)
247 {
248         int i;
249         for (i=0; values[i]; i++) {
250                 printf("%s: %s\n", field, values[i]->bv_val);
251         }
252 }
253
254 /*
255   dump a record from LDAP on stdout
256   used for debugging
257 */
258 void ads_dump(ADS_STRUCT *ads, void *res)
259 {
260         char *field;
261         void *msg;
262         BerElement *b;
263         struct {
264                 char *name;
265                 void (*handler)(const char *, struct berval **);
266         } handlers[] = {
267                 {"objectGUID", dump_binary},
268                 {"nTSecurityDescriptor", dump_binary},
269                 {"objectSid", dump_sid},
270                 {NULL, NULL}
271         };
272     
273         for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
274                 for (field = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &b); 
275                      field;
276                      field = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, b)) {
277                         struct berval **values;
278                         int i;
279
280                         values = ldap_get_values_len(ads->ld, (LDAPMessage *)msg, field);
281
282                         for (i=0; handlers[i].name; i++) {
283                                 if (StrCaseCmp(handlers[i].name, field) == 0) {
284                                         handlers[i].handler(field, values);
285                                         break;
286                                 }
287                         }
288                         if (!handlers[i].name) {
289                                 dump_string(field, values);
290                         }
291                         ldap_value_free_len(values);
292                         ldap_memfree(field);
293                 }
294
295                 ber_free(b, 1);
296                 printf("\n");
297         }
298 }
299
300 /*
301   count how many replies are in a LDAPMessage
302 */
303 int ads_count_replies(ADS_STRUCT *ads, void *res)
304 {
305         return ldap_count_entries(ads->ld, (LDAPMessage *)res);
306 }
307
308 /*
309   join a machine to a realm, creating the machine account
310   and setting the machine password
311 */
312 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *hostname, const char *org_unit)
313 {
314         ADS_STATUS status;
315         LDAPMessage *res;
316         char *host;
317
318         /* hostname must be lowercase */
319         host = strdup(hostname);
320         strlower(host);
321
322         status = ads_find_machine_acct(ads, (void **)&res, host);
323         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
324                 DEBUG(0, ("Host account for %s already exists\n", host));
325                 return ADS_SUCCESS;
326         }
327
328         status = ads_add_machine_acct(ads, host, org_unit);
329         if (!ADS_ERR_OK(status)) {
330                 DEBUG(0, ("ads_add_machine_acct: %s\n", ads_errstr(status)));
331                 return status;
332         }
333
334         status = ads_find_machine_acct(ads, (void **)&res, host);
335         if (!ADS_ERR_OK(status)) {
336                 DEBUG(0, ("Host account test failed\n"));
337                 return status;
338         }
339
340         free(host);
341
342         return status;
343 }
344
345 /*
346   delete a machine from the realm
347 */
348 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
349 {
350         ADS_STATUS status;
351         void *res;
352         char *hostnameDN, *host; 
353         int rc;
354
355         /* hostname must be lowercase */
356         host = strdup(hostname);
357         strlower(host);
358
359         status = ads_find_machine_acct(ads, &res, host);
360         if (!ADS_ERR_OK(status)) {
361             DEBUG(0, ("Host account for %s does not exist.\n", host));
362             return status;
363         }
364
365         hostnameDN = ldap_get_dn(ads->ld, (LDAPMessage *)res);
366         rc = ldap_delete_s(ads->ld, hostnameDN);
367         ldap_memfree(hostnameDN);
368         if (rc != LDAP_SUCCESS) {
369                 return ADS_ERROR(rc);
370         }
371
372         status = ads_find_machine_acct(ads, &res, host);
373         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
374                 DEBUG(0, ("Failed to remove host account.\n"));
375                 return status;
376         }
377
378         free(host);
379
380         return status;
381 }
382
383
384 ADS_STATUS ads_set_machine_password(ADS_STRUCT *ads,
385                                     const char *hostname, 
386                                     const char *password)
387 {
388         ADS_STATUS status;
389         char *host = strdup(hostname);
390         char *principal; 
391
392         strlower(host);
393
394         asprintf(&principal, "%s@%s", host, ads->realm);
395         
396         status = krb5_set_password(ads->kdc_server, principal, password);
397         
398         free(host);
399         free(principal);
400
401         return status;
402 }
403
404 /*
405   pull the first entry from a ADS result
406 */
407 void *ads_first_entry(ADS_STRUCT *ads, void *res)
408 {
409         return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
410 }
411
412 /*
413   pull the next entry from a ADS result
414 */
415 void *ads_next_entry(ADS_STRUCT *ads, void *res)
416 {
417         return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
418 }
419
420 /*
421   pull a single string from a ADS result
422 */
423 char *ads_pull_string(ADS_STRUCT *ads, 
424                       TALLOC_CTX *mem_ctx, void *msg, const char *field)
425 {
426         char **values;
427         char *ret = NULL;
428
429         values = ldap_get_values(ads->ld, msg, field);
430         if (!values) return NULL;
431         
432         if (values[0]) {
433                 ret = talloc_strdup(mem_ctx, values[0]);
434         }
435         ldap_value_free(values);
436         return ret;
437 }
438
439 /*
440   pull a single uint32 from a ADS result
441 */
442 BOOL ads_pull_uint32(ADS_STRUCT *ads, 
443                      void *msg, const char *field, uint32 *v)
444 {
445         char **values;
446
447         values = ldap_get_values(ads->ld, msg, field);
448         if (!values) return False;
449         if (!values[0]) {
450                 ldap_value_free(values);
451                 return False;
452         }
453
454         *v = atoi(values[0]);
455         ldap_value_free(values);
456         return True;
457 }
458
459 /*
460   pull a single DOM_SID from a ADS result
461 */
462 BOOL ads_pull_sid(ADS_STRUCT *ads, 
463                   void *msg, const char *field, DOM_SID *sid)
464 {
465         struct berval **values;
466         BOOL ret = False;
467
468         values = ldap_get_values_len(ads->ld, msg, field);
469
470         if (!values) return False;
471
472         if (values[0]) {
473                 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
474         }
475         
476         ldap_value_free_len(values);
477         return ret;
478 }
479
480 /*
481   pull an array of DOM_SIDs from a ADS result
482   return the count of SIDs pulled
483 */
484 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
485                   void *msg, const char *field, DOM_SID **sids)
486 {
487         struct berval **values;
488         BOOL ret;
489         int count, i;
490
491         values = ldap_get_values_len(ads->ld, msg, field);
492
493         if (!values) return 0;
494
495         for (i=0; values[i]; i++) /* nop */ ;
496
497         (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * i);
498
499         count = 0;
500         for (i=0; values[i]; i++) {
501                 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
502                 if (ret) count++;
503         }
504         
505         ldap_value_free_len(values);
506         return count;
507 }
508
509
510 /* find the update serial number - this is the core of the ldap cache */
511 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
512 {
513         const char *attrs[] = {"highestCommittedUSN", NULL};
514         ADS_STATUS status;
515         void *res;
516
517         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
518         if (!ADS_ERR_OK(status)) return status;
519
520         if (ads_count_replies(ads, res) != 1) {
521                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
522         }
523
524         ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
525         ads_msgfree(ads, res);
526         return ADS_SUCCESS;
527 }
528
529
530 /* find the servers name and realm - this can be done before authentication 
531    The ldapServiceName field on w2k  looks like this:
532      vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
533 */
534 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
535 {
536         const char *attrs[] = {"ldapServiceName", NULL};
537         ADS_STATUS status;
538         void *res;
539         char **values;
540         char *p;
541
542         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
543         if (!ADS_ERR_OK(status)) return status;
544
545         values = ldap_get_values(ads->ld, res, "ldapServiceName");
546         if (!values || !values[0]) return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
547
548         p = strchr(values[0], ':');
549         if (!p) {
550                 ldap_value_free(values);
551                 ldap_msgfree(res);
552                 return ADS_ERROR(LDAP_DECODING_ERROR);
553         }
554
555         SAFE_FREE(ads->ldap_server_name);
556
557         ads->ldap_server_name = strdup(p+1);
558         p = strchr(ads->ldap_server_name, '$');
559         if (!p || p[1] != '@') {
560                 ldap_value_free(values);
561                 ldap_msgfree(res);
562                 SAFE_FREE(ads->ldap_server_name);
563                 return ADS_ERROR(LDAP_DECODING_ERROR);
564         }
565
566         *p = 0;
567
568         SAFE_FREE(ads->server_realm);
569         SAFE_FREE(ads->bind_path);
570
571         ads->server_realm = strdup(p+2);
572         ads->bind_path = ads_build_dn(ads->server_realm);
573
574         /* in case the realm isn't configured in smb.conf */
575         if (!ads->realm || !ads->realm[0]) {
576                 SAFE_FREE(ads->realm);
577                 ads->realm = strdup(ads->server_realm);
578         }
579
580         DEBUG(3,("got ldap server name %s@%s\n", 
581                  ads->ldap_server_name, ads->realm));
582
583         return ADS_SUCCESS;
584 }
585
586
587 /* 
588    find the list of trusted domains
589 */
590 ADS_STATUS ads_trusted_domains(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, 
591                                int *num_trusts, char ***names, DOM_SID **sids)
592 {
593         const char *attrs[] = {"flatName", "securityIdentifier", NULL};
594         ADS_STATUS status;
595         void *res, *msg;
596         int count, i;
597
598         *num_trusts = 0;
599
600         status = ads_search(ads, &res, "(objectcategory=trustedDomain)", attrs);
601         if (!ADS_ERR_OK(status)) return status;
602
603         count = ads_count_replies(ads, res);
604         if (count == 0) {
605                 ads_msgfree(ads, res);
606                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
607         }
608
609         (*names) = talloc(mem_ctx, sizeof(char *) * count);
610         (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * count);
611         if (! *names || ! *sids) return ADS_ERROR(LDAP_NO_MEMORY);
612
613         for (i=0, msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
614                 (*names)[i] = ads_pull_string(ads, mem_ctx, msg, "flatName");
615                 ads_pull_sid(ads, msg, "securityIdentifier", &(*sids)[i]);
616                 i++;
617         }
618
619         ads_msgfree(ads, res);
620
621         *num_trusts = i;
622
623         return ADS_SUCCESS;
624 }
625
626 /* find the domain sid for our domain */
627 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
628 {
629         const char *attrs[] = {"objectSid", NULL};
630         void *res;
631         ADS_STATUS rc;
632
633         rc = ads_do_search(ads, ads->bind_path, LDAP_SCOPE_BASE, "(objectclass=*)", 
634                            attrs, &res);
635         if (!ADS_ERR_OK(rc)) return rc;
636         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
637                 return ADS_ERROR_SYSTEM(ENOENT);
638         }
639         ads_msgfree(ads, res);
640         
641         return ADS_SUCCESS;
642 }
643
644 #endif