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