much better support for organisational units in ADS join
[tprouty/samba.git] / source / 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   build an org unit string
177   if org unit is Computers or blank then assume a container, otherwise
178   assume a \ separated list of organisational units
179   caller must free
180 */
181 char *ads_ou_string(const char *org_unit)
182 {       
183         if (!org_unit || !*org_unit || strcasecmp(org_unit, "Computers") == 0) {
184                 return strdup("cn=Computers");
185         }
186
187         return ads_build_path(org_unit, "\\/", "ou=", 1);
188 }
189
190
191
192 /*
193   add a machine account to the ADS server
194 */
195 static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *hostname, 
196                                        const char *org_unit)
197 {
198         ADS_STATUS ret;
199         char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
200         char *ou_str;
201
202         asprintf(&host_spn, "HOST/%s", hostname);
203         asprintf(&host_upn, "%s@%s", host_spn, ads->realm);
204         ou_str = ads_ou_string(org_unit);
205         asprintf(&new_dn, "cn=%s,%s,%s", hostname, ou_str, ads->bind_path);
206         free(ou_str);
207         asprintf(&samAccountName, "%s$", hostname);
208         asprintf(&controlstr, "%u", 
209                 UF_DONT_EXPIRE_PASSWD | UF_WORKSTATION_TRUST_ACCOUNT | 
210                 UF_TRUSTED_FOR_DELEGATION | UF_USE_DES_KEY_ONLY);
211     
212         ret = ads_gen_add(ads, new_dn,
213                            "cn", hostname, NULL,
214                            "sAMAccountName", samAccountName, NULL,
215                            "objectClass", 
216                               "top", "person", "organizationalPerson", 
217                               "user", "computer", NULL,
218                            "userPrincipalName", host_upn, NULL, 
219                            "servicePrincipalName", host_spn, NULL,
220                            "dNSHostName", hostname, NULL,
221                            "userAccountControl", controlstr, NULL,
222                            "operatingSystem", "Samba", NULL,
223                            "operatingSystemVersion", VERSION, NULL,
224                            NULL);
225
226         free(host_spn);
227         free(host_upn);
228         free(new_dn);
229         free(samAccountName);
230         free(controlstr);
231
232         return ret;
233 }
234
235 /*
236   dump a binary result from ldap
237 */
238 static void dump_binary(const char *field, struct berval **values)
239 {
240         int i, j;
241         for (i=0; values[i]; i++) {
242                 printf("%s: ", field);
243                 for (j=0; j<values[i]->bv_len; j++) {
244                         printf("%02X", (unsigned char)values[i]->bv_val[j]);
245                 }
246                 printf("\n");
247         }
248 }
249
250 /*
251   dump a sid result from ldap
252 */
253 static void dump_sid(const char *field, struct berval **values)
254 {
255         int i;
256         for (i=0; values[i]; i++) {
257                 DOM_SID sid;
258                 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
259                 printf("%s: %s\n", field, sid_string_static(&sid));
260         }
261 }
262
263 /*
264   dump a string result from ldap
265 */
266 static void dump_string(const char *field, struct berval **values)
267 {
268         int i;
269         for (i=0; values[i]; i++) {
270                 printf("%s: %s\n", field, values[i]->bv_val);
271         }
272 }
273
274 /*
275   dump a record from LDAP on stdout
276   used for debugging
277 */
278 void ads_dump(ADS_STRUCT *ads, void *res)
279 {
280         char *field;
281         void *msg;
282         BerElement *b;
283         struct {
284                 char *name;
285                 void (*handler)(const char *, struct berval **);
286         } handlers[] = {
287                 {"objectGUID", dump_binary},
288                 {"nTSecurityDescriptor", dump_binary},
289                 {"objectSid", dump_sid},
290                 {NULL, NULL}
291         };
292     
293         for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
294                 for (field = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &b); 
295                      field;
296                      field = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, b)) {
297                         struct berval **values;
298                         int i;
299
300                         values = ldap_get_values_len(ads->ld, (LDAPMessage *)msg, field);
301
302                         for (i=0; handlers[i].name; i++) {
303                                 if (StrCaseCmp(handlers[i].name, field) == 0) {
304                                         handlers[i].handler(field, values);
305                                         break;
306                                 }
307                         }
308                         if (!handlers[i].name) {
309                                 dump_string(field, values);
310                         }
311                         ldap_value_free_len(values);
312                         ldap_memfree(field);
313                 }
314
315                 ber_free(b, 1);
316                 printf("\n");
317         }
318 }
319
320 /*
321   count how many replies are in a LDAPMessage
322 */
323 int ads_count_replies(ADS_STRUCT *ads, void *res)
324 {
325         return ldap_count_entries(ads->ld, (LDAPMessage *)res);
326 }
327
328 /*
329   join a machine to a realm, creating the machine account
330   and setting the machine password
331 */
332 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *hostname, const char *org_unit)
333 {
334         ADS_STATUS status;
335         LDAPMessage *res;
336         char *host;
337
338         /* hostname must be lowercase */
339         host = strdup(hostname);
340         strlower(host);
341
342         status = ads_find_machine_acct(ads, (void **)&res, host);
343         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
344                 DEBUG(0, ("Host account for %s already exists - deleting for readd\n", host));
345                 status = ads_leave_realm(ads, host);
346                 if (!ADS_ERR_OK(status)) {
347                         DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n", 
348                                   host, ads->realm));
349                         return status;
350                 }
351         }
352
353         status = ads_add_machine_acct(ads, host, org_unit);
354         if (!ADS_ERR_OK(status)) {
355                 DEBUG(0, ("ads_add_machine_acct: %s\n", ads_errstr(status)));
356                 return status;
357         }
358
359         status = ads_find_machine_acct(ads, (void **)&res, host);
360         if (!ADS_ERR_OK(status)) {
361                 DEBUG(0, ("Host account test failed\n"));
362                 return status;
363         }
364
365         free(host);
366
367         return status;
368 }
369
370 /*
371   delete a machine from the realm
372 */
373 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
374 {
375         ADS_STATUS status;
376         void *res;
377         char *hostnameDN, *host; 
378         int rc;
379
380         /* hostname must be lowercase */
381         host = strdup(hostname);
382         strlower(host);
383
384         status = ads_find_machine_acct(ads, &res, host);
385         if (!ADS_ERR_OK(status)) {
386             DEBUG(0, ("Host account for %s does not exist.\n", host));
387             return status;
388         }
389
390         hostnameDN = ldap_get_dn(ads->ld, (LDAPMessage *)res);
391         rc = ldap_delete_s(ads->ld, hostnameDN);
392         ldap_memfree(hostnameDN);
393         if (rc != LDAP_SUCCESS) {
394                 return ADS_ERROR(rc);
395         }
396
397         status = ads_find_machine_acct(ads, &res, host);
398         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
399                 DEBUG(0, ("Failed to remove host account.\n"));
400                 return status;
401         }
402
403         free(host);
404
405         return status;
406 }
407
408
409 ADS_STATUS ads_set_machine_password(ADS_STRUCT *ads,
410                                     const char *hostname, 
411                                     const char *password)
412 {
413         ADS_STATUS status;
414         char *host = strdup(hostname);
415         char *principal; 
416
417         strlower(host);
418
419         asprintf(&principal, "%s@%s", host, ads->realm);
420         
421         status = krb5_set_password(ads->kdc_server, principal, password);
422         
423         free(host);
424         free(principal);
425
426         return status;
427 }
428
429 /*
430   pull the first entry from a ADS result
431 */
432 void *ads_first_entry(ADS_STRUCT *ads, void *res)
433 {
434         return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
435 }
436
437 /*
438   pull the next entry from a ADS result
439 */
440 void *ads_next_entry(ADS_STRUCT *ads, void *res)
441 {
442         return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
443 }
444
445 /*
446   pull a single string from a ADS result
447 */
448 char *ads_pull_string(ADS_STRUCT *ads, 
449                       TALLOC_CTX *mem_ctx, void *msg, const char *field)
450 {
451         char **values;
452         char *ret = NULL;
453
454         values = ldap_get_values(ads->ld, msg, field);
455         if (!values) return NULL;
456         
457         if (values[0]) {
458                 ret = talloc_strdup(mem_ctx, values[0]);
459         }
460         ldap_value_free(values);
461         return ret;
462 }
463
464 /*
465   pull a single uint32 from a ADS result
466 */
467 BOOL ads_pull_uint32(ADS_STRUCT *ads, 
468                      void *msg, const char *field, uint32 *v)
469 {
470         char **values;
471
472         values = ldap_get_values(ads->ld, msg, field);
473         if (!values) return False;
474         if (!values[0]) {
475                 ldap_value_free(values);
476                 return False;
477         }
478
479         *v = atoi(values[0]);
480         ldap_value_free(values);
481         return True;
482 }
483
484 /*
485   pull a single DOM_SID from a ADS result
486 */
487 BOOL ads_pull_sid(ADS_STRUCT *ads, 
488                   void *msg, const char *field, DOM_SID *sid)
489 {
490         struct berval **values;
491         BOOL ret = False;
492
493         values = ldap_get_values_len(ads->ld, msg, field);
494
495         if (!values) return False;
496
497         if (values[0]) {
498                 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
499         }
500         
501         ldap_value_free_len(values);
502         return ret;
503 }
504
505 /*
506   pull an array of DOM_SIDs from a ADS result
507   return the count of SIDs pulled
508 */
509 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
510                   void *msg, const char *field, DOM_SID **sids)
511 {
512         struct berval **values;
513         BOOL ret;
514         int count, i;
515
516         values = ldap_get_values_len(ads->ld, msg, field);
517
518         if (!values) return 0;
519
520         for (i=0; values[i]; i++) /* nop */ ;
521
522         (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * i);
523
524         count = 0;
525         for (i=0; values[i]; i++) {
526                 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
527                 if (ret) count++;
528         }
529         
530         ldap_value_free_len(values);
531         return count;
532 }
533
534
535 /* find the update serial number - this is the core of the ldap cache */
536 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
537 {
538         const char *attrs[] = {"highestCommittedUSN", NULL};
539         ADS_STATUS status;
540         void *res;
541
542         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
543         if (!ADS_ERR_OK(status)) return status;
544
545         if (ads_count_replies(ads, res) != 1) {
546                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
547         }
548
549         ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
550         ads_msgfree(ads, res);
551         return ADS_SUCCESS;
552 }
553
554
555 /* find the servers name and realm - this can be done before authentication 
556    The ldapServiceName field on w2k  looks like this:
557      vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
558 */
559 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
560 {
561         const char *attrs[] = {"ldapServiceName", NULL};
562         ADS_STATUS status;
563         void *res;
564         char **values;
565         char *p;
566
567         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
568         if (!ADS_ERR_OK(status)) return status;
569
570         values = ldap_get_values(ads->ld, res, "ldapServiceName");
571         if (!values || !values[0]) return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
572
573         p = strchr(values[0], ':');
574         if (!p) {
575                 ldap_value_free(values);
576                 ldap_msgfree(res);
577                 return ADS_ERROR(LDAP_DECODING_ERROR);
578         }
579
580         SAFE_FREE(ads->ldap_server_name);
581
582         ads->ldap_server_name = strdup(p+1);
583         p = strchr(ads->ldap_server_name, '$');
584         if (!p || p[1] != '@') {
585                 ldap_value_free(values);
586                 ldap_msgfree(res);
587                 SAFE_FREE(ads->ldap_server_name);
588                 return ADS_ERROR(LDAP_DECODING_ERROR);
589         }
590
591         *p = 0;
592
593         SAFE_FREE(ads->server_realm);
594         SAFE_FREE(ads->bind_path);
595
596         ads->server_realm = strdup(p+2);
597         ads->bind_path = ads_build_dn(ads->server_realm);
598
599         /* in case the realm isn't configured in smb.conf */
600         if (!ads->realm || !ads->realm[0]) {
601                 SAFE_FREE(ads->realm);
602                 ads->realm = strdup(ads->server_realm);
603         }
604
605         DEBUG(3,("got ldap server name %s@%s\n", 
606                  ads->ldap_server_name, ads->realm));
607
608         return ADS_SUCCESS;
609 }
610
611
612 /* 
613    find the list of trusted domains
614 */
615 ADS_STATUS ads_trusted_domains(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, 
616                                int *num_trusts, char ***names, DOM_SID **sids)
617 {
618         const char *attrs[] = {"flatName", "securityIdentifier", NULL};
619         ADS_STATUS status;
620         void *res, *msg;
621         int count, i;
622
623         *num_trusts = 0;
624
625         status = ads_search(ads, &res, "(objectcategory=trustedDomain)", attrs);
626         if (!ADS_ERR_OK(status)) return status;
627
628         count = ads_count_replies(ads, res);
629         if (count == 0) {
630                 ads_msgfree(ads, res);
631                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
632         }
633
634         (*names) = talloc(mem_ctx, sizeof(char *) * count);
635         (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * count);
636         if (! *names || ! *sids) return ADS_ERROR(LDAP_NO_MEMORY);
637
638         for (i=0, msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
639                 (*names)[i] = ads_pull_string(ads, mem_ctx, msg, "flatName");
640                 ads_pull_sid(ads, msg, "securityIdentifier", &(*sids)[i]);
641                 i++;
642         }
643
644         ads_msgfree(ads, res);
645
646         *num_trusts = i;
647
648         return ADS_SUCCESS;
649 }
650
651 /* find the domain sid for our domain */
652 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
653 {
654         const char *attrs[] = {"objectSid", NULL};
655         void *res;
656         ADS_STATUS rc;
657
658         rc = ads_do_search(ads, ads->bind_path, LDAP_SCOPE_BASE, "(objectclass=*)", 
659                            attrs, &res);
660         if (!ADS_ERR_OK(rc)) return rc;
661         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
662                 return ADS_ERROR_SYSTEM(ENOENT);
663         }
664         ads_msgfree(ads, res);
665         
666         return ADS_SUCCESS;
667 }
668
669 #endif