2e93e11603a61bcd15c1b1edb492a9841965e1bd
[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 - deleting for readd\n", host));
325                 status = ads_leave_realm(ads, host);
326                 if (!ADS_ERR_OK(status)) {
327                         DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n", 
328                                   host, ads->realm));
329                         return status;
330                 }
331         }
332
333         status = ads_add_machine_acct(ads, host, org_unit);
334         if (!ADS_ERR_OK(status)) {
335                 DEBUG(0, ("ads_add_machine_acct: %s\n", ads_errstr(status)));
336                 return status;
337         }
338
339         status = ads_find_machine_acct(ads, (void **)&res, host);
340         if (!ADS_ERR_OK(status)) {
341                 DEBUG(0, ("Host account test failed\n"));
342                 return status;
343         }
344
345         free(host);
346
347         return status;
348 }
349
350 /*
351   delete a machine from the realm
352 */
353 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
354 {
355         ADS_STATUS status;
356         void *res;
357         char *hostnameDN, *host; 
358         int rc;
359
360         /* hostname must be lowercase */
361         host = strdup(hostname);
362         strlower(host);
363
364         status = ads_find_machine_acct(ads, &res, host);
365         if (!ADS_ERR_OK(status)) {
366             DEBUG(0, ("Host account for %s does not exist.\n", host));
367             return status;
368         }
369
370         hostnameDN = ldap_get_dn(ads->ld, (LDAPMessage *)res);
371         rc = ldap_delete_s(ads->ld, hostnameDN);
372         ldap_memfree(hostnameDN);
373         if (rc != LDAP_SUCCESS) {
374                 return ADS_ERROR(rc);
375         }
376
377         status = ads_find_machine_acct(ads, &res, host);
378         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
379                 DEBUG(0, ("Failed to remove host account.\n"));
380                 return status;
381         }
382
383         free(host);
384
385         return status;
386 }
387
388
389 ADS_STATUS ads_set_machine_password(ADS_STRUCT *ads,
390                                     const char *hostname, 
391                                     const char *password)
392 {
393         ADS_STATUS status;
394         char *host = strdup(hostname);
395         char *principal; 
396
397         strlower(host);
398
399         asprintf(&principal, "%s@%s", host, ads->realm);
400         
401         status = krb5_set_password(ads->kdc_server, principal, password);
402         
403         free(host);
404         free(principal);
405
406         return status;
407 }
408
409 /*
410   pull the first entry from a ADS result
411 */
412 void *ads_first_entry(ADS_STRUCT *ads, void *res)
413 {
414         return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
415 }
416
417 /*
418   pull the next entry from a ADS result
419 */
420 void *ads_next_entry(ADS_STRUCT *ads, void *res)
421 {
422         return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
423 }
424
425 /*
426   pull a single string from a ADS result
427 */
428 char *ads_pull_string(ADS_STRUCT *ads, 
429                       TALLOC_CTX *mem_ctx, void *msg, const char *field)
430 {
431         char **values;
432         char *ret = NULL;
433
434         values = ldap_get_values(ads->ld, msg, field);
435         if (!values) return NULL;
436         
437         if (values[0]) {
438                 ret = talloc_strdup(mem_ctx, values[0]);
439         }
440         ldap_value_free(values);
441         return ret;
442 }
443
444 /*
445   pull a single uint32 from a ADS result
446 */
447 BOOL ads_pull_uint32(ADS_STRUCT *ads, 
448                      void *msg, const char *field, uint32 *v)
449 {
450         char **values;
451
452         values = ldap_get_values(ads->ld, msg, field);
453         if (!values) return False;
454         if (!values[0]) {
455                 ldap_value_free(values);
456                 return False;
457         }
458
459         *v = atoi(values[0]);
460         ldap_value_free(values);
461         return True;
462 }
463
464 /*
465   pull a single DOM_SID from a ADS result
466 */
467 BOOL ads_pull_sid(ADS_STRUCT *ads, 
468                   void *msg, const char *field, DOM_SID *sid)
469 {
470         struct berval **values;
471         BOOL ret = False;
472
473         values = ldap_get_values_len(ads->ld, msg, field);
474
475         if (!values) return False;
476
477         if (values[0]) {
478                 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
479         }
480         
481         ldap_value_free_len(values);
482         return ret;
483 }
484
485 /*
486   pull an array of DOM_SIDs from a ADS result
487   return the count of SIDs pulled
488 */
489 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
490                   void *msg, const char *field, DOM_SID **sids)
491 {
492         struct berval **values;
493         BOOL ret;
494         int count, i;
495
496         values = ldap_get_values_len(ads->ld, msg, field);
497
498         if (!values) return 0;
499
500         for (i=0; values[i]; i++) /* nop */ ;
501
502         (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * i);
503
504         count = 0;
505         for (i=0; values[i]; i++) {
506                 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
507                 if (ret) count++;
508         }
509         
510         ldap_value_free_len(values);
511         return count;
512 }
513
514
515 /* find the update serial number - this is the core of the ldap cache */
516 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
517 {
518         const char *attrs[] = {"highestCommittedUSN", NULL};
519         ADS_STATUS status;
520         void *res;
521
522         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
523         if (!ADS_ERR_OK(status)) return status;
524
525         if (ads_count_replies(ads, res) != 1) {
526                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
527         }
528
529         ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
530         ads_msgfree(ads, res);
531         return ADS_SUCCESS;
532 }
533
534
535 /* find the servers name and realm - this can be done before authentication 
536    The ldapServiceName field on w2k  looks like this:
537      vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
538 */
539 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
540 {
541         const char *attrs[] = {"ldapServiceName", NULL};
542         ADS_STATUS status;
543         void *res;
544         char **values;
545         char *p;
546
547         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
548         if (!ADS_ERR_OK(status)) return status;
549
550         values = ldap_get_values(ads->ld, res, "ldapServiceName");
551         if (!values || !values[0]) return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
552
553         p = strchr(values[0], ':');
554         if (!p) {
555                 ldap_value_free(values);
556                 ldap_msgfree(res);
557                 return ADS_ERROR(LDAP_DECODING_ERROR);
558         }
559
560         SAFE_FREE(ads->ldap_server_name);
561
562         ads->ldap_server_name = strdup(p+1);
563         p = strchr(ads->ldap_server_name, '$');
564         if (!p || p[1] != '@') {
565                 ldap_value_free(values);
566                 ldap_msgfree(res);
567                 SAFE_FREE(ads->ldap_server_name);
568                 return ADS_ERROR(LDAP_DECODING_ERROR);
569         }
570
571         *p = 0;
572
573         SAFE_FREE(ads->server_realm);
574         SAFE_FREE(ads->bind_path);
575
576         ads->server_realm = strdup(p+2);
577         ads->bind_path = ads_build_dn(ads->server_realm);
578
579         /* in case the realm isn't configured in smb.conf */
580         if (!ads->realm || !ads->realm[0]) {
581                 SAFE_FREE(ads->realm);
582                 ads->realm = strdup(ads->server_realm);
583         }
584
585         DEBUG(3,("got ldap server name %s@%s\n", 
586                  ads->ldap_server_name, ads->realm));
587
588         return ADS_SUCCESS;
589 }
590
591
592 /* 
593    find the list of trusted domains
594 */
595 ADS_STATUS ads_trusted_domains(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, 
596                                int *num_trusts, char ***names, DOM_SID **sids)
597 {
598         const char *attrs[] = {"flatName", "securityIdentifier", NULL};
599         ADS_STATUS status;
600         void *res, *msg;
601         int count, i;
602
603         *num_trusts = 0;
604
605         status = ads_search(ads, &res, "(objectcategory=trustedDomain)", attrs);
606         if (!ADS_ERR_OK(status)) return status;
607
608         count = ads_count_replies(ads, res);
609         if (count == 0) {
610                 ads_msgfree(ads, res);
611                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
612         }
613
614         (*names) = talloc(mem_ctx, sizeof(char *) * count);
615         (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * count);
616         if (! *names || ! *sids) return ADS_ERROR(LDAP_NO_MEMORY);
617
618         for (i=0, msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
619                 (*names)[i] = ads_pull_string(ads, mem_ctx, msg, "flatName");
620                 ads_pull_sid(ads, msg, "securityIdentifier", &(*sids)[i]);
621                 i++;
622         }
623
624         ads_msgfree(ads, res);
625
626         *num_trusts = i;
627
628         return ADS_SUCCESS;
629 }
630
631 /* find the domain sid for our domain */
632 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
633 {
634         const char *attrs[] = {"objectSid", NULL};
635         void *res;
636         ADS_STATUS rc;
637
638         rc = ads_do_search(ads, ads->bind_path, LDAP_SCOPE_BASE, "(objectclass=*)", 
639                            attrs, &res);
640         if (!ADS_ERR_OK(rc)) return rc;
641         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
642                 return ADS_ERROR_SYSTEM(ENOENT);
643         }
644         ads_msgfree(ads, res);
645         
646         return ADS_SUCCESS;
647 }
648
649 #endif