8ed6e7e2b009f7314a53f265f65d331aae830160
[jra/samba/.git] / source3 / nsswitch / winbindd_ads.c
1 /* 
2    Unix SMB/Netbios implementation.
3
4    Winbind ADS backend functions
5
6    Copyright (C) Andrew Tridgell 2001
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "winbindd.h"
24
25 #ifdef HAVE_ADS
26
27
28 /*
29   a wrapper around ldap_search_s that retries depending on the error code
30   this is supposed to catch dropped connections and auto-reconnect
31 */
32 int ads_do_search_retry(ADS_STRUCT *ads, const char *bind_path, int scope, 
33                         const char *exp,
34                         const char **attrs, void **res)
35 {
36         int rc = -1, rc2;
37         int count = 3;
38
39         if (!ads->ld &&
40             time(NULL) - ads->last_attempt < ADS_RECONNECT_TIME) {
41                 return LDAP_SERVER_DOWN;
42         }
43
44         while (count--) {
45                 rc = ads_do_search(ads, bind_path, scope, exp, attrs, res);
46                 if (rc == 0) return rc;
47
48                 if (*res) ads_msgfree(ads, *res);
49                 *res = NULL;
50                 DEBUG(1,("Reopening ads connection after error %s\n", ads_errstr(rc)));
51                 if (ads->ld) {
52                         /* we should unbind here, but that seems to trigger openldap bugs :(
53                            ldap_unbind(ads->ld); 
54                         */
55                 }
56                 ads->ld = NULL;
57                 rc2 = ads_connect(ads);
58                 if (rc2) {
59                         DEBUG(1,("ads_search_retry: failed to reconnect (%s)\n", ads_errstr(rc)));
60                         return rc2;
61                 }
62         }
63         DEBUG(1,("ads reopen failed after error %s\n", ads_errstr(rc)));
64         return rc;
65 }
66
67
68 int ads_search_retry(ADS_STRUCT *ads, void **res, 
69                      const char *exp, 
70                      const char **attrs)
71 {
72         return ads_do_search_retry(ads, ads->bind_path, LDAP_SCOPE_SUBTREE,
73                                    exp, attrs, res);
74 }
75
76 int ads_search_retry_dn(ADS_STRUCT *ads, void **res, 
77                         const char *dn, 
78                         const char **attrs)
79 {
80         return ads_do_search_retry(ads, dn, LDAP_SCOPE_BASE,
81                                    "(objectclass=*)", attrs, res);
82 }
83
84 /*
85   return our ads connections structure for a domain. We keep the connection
86   open to make things faster
87 */
88 static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain)
89 {
90         ADS_STRUCT *ads;
91         int rc;
92
93         if (domain->private) {
94                 return (ADS_STRUCT *)domain->private;
95         }
96
97         ads = ads_init(NULL, NULL, NULL, NULL);
98         if (!ads) {
99                 DEBUG(1,("ads_init for domain %s failed\n", domain->name));
100                 return NULL;
101         }
102
103         /* the machine acct password might have change - fetch it every time */
104         SAFE_FREE(ads->password);
105         ads->password = secrets_fetch_machine_password();
106
107         rc = ads_connect(ads);
108         if (rc) {
109                 DEBUG(1,("ads_connect for domain %s failed: %s\n", domain->name, ads_errstr(rc)));
110                 ads_destroy(&ads);
111                 return NULL;
112         }
113
114         domain->private = (void *)ads;
115         return ads;
116 }
117
118 /* useful utility */
119 static void sid_from_rid(struct winbindd_domain *domain, uint32 rid, DOM_SID *sid)
120 {
121         sid_copy(sid, &domain->sid);
122         sid_append_rid(sid, rid);
123 }
124
125 /* turn a sAMAccountType into a SID_NAME_USE */
126 static enum SID_NAME_USE ads_atype_map(uint32 atype)
127 {
128         switch (atype & 0xF0000000) {
129         case ATYPE_GROUP:
130                 return SID_NAME_DOM_GRP;
131         case ATYPE_USER:
132                 return SID_NAME_USER;
133         default:
134                 DEBUG(1,("hmm, need to map account type 0x%x\n", atype));
135         }
136         return SID_NAME_UNKNOWN;
137 }
138
139 /* Query display info for a realm. This is the basic user list fn */
140 static NTSTATUS query_user_list(struct winbindd_domain *domain,
141                                TALLOC_CTX *mem_ctx,
142                                uint32 *start_ndx, uint32 *num_entries, 
143                                WINBIND_USERINFO **info)
144 {
145         ADS_STRUCT *ads = NULL;
146         const char *attrs[] = {"sAMAccountName", "name", "objectSid", "primaryGroupID", 
147                                "userAccountControl", NULL};
148         int rc, i, count;
149         void *res = NULL;
150         void *msg = NULL;
151         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
152
153         DEBUG(3,("ads: query_user_list\n"));
154
155         if ((*start_ndx) != 0) {
156                 DEBUG(1,("ads backend start_ndx not implemented!\n"));
157                 status = NT_STATUS_NOT_IMPLEMENTED;
158                 goto done;
159         }
160
161         ads = ads_cached_connection(domain);
162         if (!ads) goto done;
163
164         rc = ads_search_retry(ads, &res, "(objectclass=user)", attrs);
165         if (rc) {
166                 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
167                 goto done;
168         }
169
170         count = ads_count_replies(ads, res);
171         if (count == 0) {
172                 DEBUG(1,("query_user_list: No users found\n"));
173                 goto done;
174         }
175
176         (*info) = talloc(mem_ctx, count * sizeof(**info));
177         if (!*info) {
178                 status = NT_STATUS_NO_MEMORY;
179                 goto done;
180         }
181
182         i = 0;
183
184         for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
185                 char *name, *gecos;
186                 DOM_SID sid;
187                 uint32 rid, group;
188                 uint32 account_control;
189
190                 if (!ads_pull_uint32(ads, msg, "userAccountControl", 
191                                      &account_control) ||
192                     !(account_control & UF_NORMAL_ACCOUNT)) continue;
193
194                 name = ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
195                 gecos = ads_pull_string(ads, mem_ctx, msg, "name");
196                 if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
197                         DEBUG(1,("No sid for %s !?\n", name));
198                         continue;
199                 }
200                 if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group)) {
201                         DEBUG(1,("No primary group for %s !?\n", name));
202                         continue;
203                 }
204
205                 if (!sid_peek_rid(&sid, &rid)) {
206                         DEBUG(1,("No rid for %s !?\n", name));
207                         continue;
208                 }
209
210                 (*info)[i].acct_name = name;
211                 (*info)[i].full_name = gecos;
212                 (*info)[i].user_rid = rid;
213                 (*info)[i].group_rid = group;
214                 i++;
215         }
216
217         (*num_entries) = i;
218         status = NT_STATUS_OK;
219
220 done:
221         if (res) ads_msgfree(ads, res);
222
223         return status;
224 }
225
226 /* list all domain groups */
227 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
228                                 TALLOC_CTX *mem_ctx,
229                                 uint32 *start_ndx, uint32 *num_entries, 
230                                 struct acct_info **info)
231 {
232         ADS_STRUCT *ads = NULL;
233         const char *attrs[] = {"sAMAccountName", "name", "objectSid", 
234                                "sAMAccountType", NULL};
235         int rc, i, count;
236         void *res = NULL;
237         void *msg = NULL;
238         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
239
240         DEBUG(3,("ads: enum_dom_groups\n"));
241
242         if ((*start_ndx) != 0) {
243                 DEBUG(1,("ads backend start_ndx not implemented\n"));
244                 status = NT_STATUS_NOT_IMPLEMENTED;
245                 goto done;
246         }
247
248         ads = ads_cached_connection(domain);
249         if (!ads) goto done;
250
251         rc = ads_search_retry(ads, &res, "(objectclass=group)", attrs);
252         if (rc) {
253                 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
254                 goto done;
255         }
256
257         count = ads_count_replies(ads, res);
258         if (count == 0) {
259                 DEBUG(1,("query_user_list: No users found\n"));
260                 goto done;
261         }
262
263         (*info) = talloc(mem_ctx, count * sizeof(**info));
264         if (!*info) {
265                 status = NT_STATUS_NO_MEMORY;
266                 goto done;
267         }
268
269         i = 0;
270
271         for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
272                 char *name, *gecos;
273                 DOM_SID sid;
274                 uint32 rid;
275                 uint32 account_type;
276
277                 if (!ads_pull_uint32(ads, msg, "sAMAccountType", 
278                                      &account_type) ||
279                     !(account_type & ATYPE_GROUP)) continue;
280
281                 name = ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
282                 gecos = ads_pull_string(ads, mem_ctx, msg, "name");
283                 if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
284                         DEBUG(1,("No sid for %s !?\n", name));
285                         continue;
286                 }
287
288                 if (!sid_peek_rid(&sid, &rid)) {
289                         DEBUG(1,("No rid for %s !?\n", name));
290                         continue;
291                 }
292
293                 fstrcpy((*info)[i].acct_name, name);
294                 fstrcpy((*info)[i].acct_desc, gecos);
295                 (*info)[i].rid = rid;
296                 i++;
297         }
298
299         (*num_entries) = i;
300
301         status = NT_STATUS_OK;
302
303 done:
304         if (res) ads_msgfree(ads, res);
305
306         return status;
307 }
308
309
310 /* convert a single name to a sid in a domain */
311 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
312                             const char *name,
313                             DOM_SID *sid,
314                             enum SID_NAME_USE *type)
315 {
316         ADS_STRUCT *ads = NULL;
317         const char *attrs[] = {"objectSid", "sAMAccountType", NULL};
318         int rc, count;
319         void *res = NULL;
320         char *exp;
321         uint32 t;
322         fstring name2, dom2;
323         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
324
325         /* sigh. Need to fix interface to give us a raw name */
326         if (!parse_domain_user(name, dom2, name2)) {
327                 goto done;
328         }
329
330         DEBUG(3,("ads: name_to_sid\n"));
331
332         ads = ads_cached_connection(domain);
333         if (!ads) goto done;
334
335         asprintf(&exp, "(sAMAccountName=%s)", name2);
336         rc = ads_search_retry(ads, &res, exp, attrs);
337         free(exp);
338         if (rc) {
339                 DEBUG(1,("name_to_sid ads_search: %s\n", ads_errstr(rc)));
340                 goto done;
341         }
342
343         count = ads_count_replies(ads, res);
344         if (count != 1) {
345                 DEBUG(1,("name_to_sid: %s not found\n", name));
346                 goto done;
347         }
348
349         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
350                 DEBUG(1,("No sid for %s !?\n", name));
351                 goto done;
352         }
353
354         if (!ads_pull_uint32(ads, res, "sAMAccountType", &t)) {
355                 DEBUG(1,("No sAMAccountType for %s !?\n", name));
356                 goto done;
357         }
358
359         *type = ads_atype_map(t);
360
361         status = NT_STATUS_OK;
362
363 done:
364         if (res) ads_msgfree(ads, res);
365
366         return status;
367 }
368
369 /* convert a sid to a user or group name */
370 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
371                             TALLOC_CTX *mem_ctx,
372                             DOM_SID *sid,
373                             char **name,
374                             enum SID_NAME_USE *type)
375 {
376         ADS_STRUCT *ads = NULL;
377         const char *attrs[] = {"sAMAccountName", "sAMAccountType", NULL};
378         int rc;
379         void *msg = NULL;
380         char *exp;
381         char *sidstr;
382         uint32 atype;
383         char *s;
384         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
385
386         DEBUG(3,("ads: sid_to_name\n"));
387
388         ads = ads_cached_connection(domain);
389         if (!ads) goto done;
390
391         sidstr = ads_sid_binstring(sid);
392         asprintf(&exp, "(objectSid=%s)", sidstr);
393         rc = ads_search_retry(ads, &msg, exp, attrs);
394         free(exp);
395         free(sidstr);
396         if (rc) {
397                 DEBUG(1,("sid_to_name ads_search: %s\n", ads_errstr(rc)));
398                 goto done;
399         }
400
401         if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype)) {
402                 goto done;
403         }
404
405         s = ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
406         *name = talloc_asprintf(mem_ctx, "%s%s%s", domain->name, lp_winbind_separator(), s);
407         *type = ads_atype_map(atype);
408
409         status = NT_STATUS_OK;
410 done:
411         if (msg) ads_msgfree(ads, msg);
412
413         return status;
414 }
415
416
417 /* Lookup user information from a rid */
418 static NTSTATUS query_user(struct winbindd_domain *domain, 
419                            TALLOC_CTX *mem_ctx, 
420                            uint32 user_rid, 
421                            WINBIND_USERINFO *info)
422 {
423         ADS_STRUCT *ads = NULL;
424         const char *attrs[] = {"sAMAccountName", "name", "objectSid", "primaryGroupID", 
425                                "userAccountControl", NULL};
426         int rc, count;
427         void *msg = NULL;
428         char *exp;
429         DOM_SID sid;
430         char *sidstr;
431         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
432
433         DEBUG(3,("ads: query_user\n"));
434
435         sid_from_rid(domain, user_rid, &sid);
436
437         ads = ads_cached_connection(domain);
438         if (!ads) goto done;
439
440         sidstr = ads_sid_binstring(&sid);
441         asprintf(&exp, "(objectSid=%s)", sidstr);
442         rc = ads_search_retry(ads, &msg, exp, attrs);
443         free(exp);
444         free(sidstr);
445         if (rc) {
446                 DEBUG(1,("query_user(rid=%d) ads_search: %s\n", user_rid, ads_errstr(rc)));
447                 goto done;
448         }
449
450         count = ads_count_replies(ads, msg);
451         if (count != 1) {
452                 DEBUG(1,("query_user(rid=%d): Not found\n", user_rid));
453                 goto done;
454         }
455
456         info->acct_name = ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
457         info->full_name = ads_pull_string(ads, mem_ctx, msg, "name");
458         if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
459                 DEBUG(1,("No sid for %d !?\n", user_rid));
460                 goto done;
461         }
462         if (!ads_pull_uint32(ads, msg, "primaryGroupID", &info->group_rid)) {
463                 DEBUG(1,("No primary group for %d !?\n", user_rid));
464                 goto done;
465         }
466         
467         if (!sid_peek_rid(&sid, &info->user_rid)) {
468                 DEBUG(1,("No rid for %d !?\n", user_rid));
469                 goto done;
470         }
471
472         status = NT_STATUS_OK;
473
474 done:
475         if (msg) ads_msgfree(ads, msg);
476
477         return status;
478 }
479
480
481 /* Lookup groups a user is a member of. */
482 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
483                                   TALLOC_CTX *mem_ctx,
484                                   uint32 user_rid, 
485                                   uint32 *num_groups, uint32 **user_gids)
486 {
487         ADS_STRUCT *ads = NULL;
488         const char *attrs[] = {"distinguishedName", NULL};
489         const char *attrs2[] = {"tokenGroups", "primaryGroupID", NULL};
490         int rc, count;
491         void *msg = NULL;
492         char *exp;
493         char *user_dn;
494         DOM_SID *sids;
495         int i;
496         uint32 primary_group;
497         DOM_SID sid;
498         char *sidstr;
499         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
500
501         DEBUG(3,("ads: lookup_usergroups\n"));
502
503         (*num_groups) = 0;
504
505         sid_from_rid(domain, user_rid, &sid);
506
507         ads = ads_cached_connection(domain);
508         if (!ads) goto done;
509
510         sidstr = ads_sid_binstring(&sid);
511         asprintf(&exp, "(objectSid=%s)", sidstr);
512         rc = ads_search_retry(ads, &msg, exp, attrs);
513         free(exp);
514         free(sidstr);
515         if (rc) {
516                 DEBUG(1,("lookup_usergroups(rid=%d) ads_search: %s\n", user_rid, ads_errstr(rc)));
517                 goto done;
518         }
519
520         user_dn = ads_pull_string(ads, mem_ctx, msg, "distinguishedName");
521
522         if (msg) ads_msgfree(ads, msg);
523
524         rc = ads_search_retry_dn(ads, &msg, user_dn, attrs2);
525         if (rc) {
526                 DEBUG(1,("lookup_usergroups(rid=%d) ads_search tokenGroups: %s\n", user_rid, ads_errstr(rc)));
527                 goto done;
528         }
529
530         if (!ads_pull_uint32(ads, msg, "primaryGroupID", &primary_group)) {
531                 DEBUG(1,("No primary group for rid=%d !?\n", user_rid));
532                 goto done;
533         }
534
535         count = ads_pull_sids(ads, mem_ctx, msg, "tokenGroups", &sids) + 1;
536         (*user_gids) = (uint32 *)talloc(mem_ctx, sizeof(uint32) * count);
537         (*user_gids)[(*num_groups)++] = primary_group;
538
539         for (i=1;i<count;i++) {
540                 uint32 rid;
541                 if (!sid_peek_rid(&sids[i-1], &rid)) continue;
542                 (*user_gids)[*num_groups] = rid;
543                 (*num_groups)++;
544         }
545
546         status = NT_STATUS_OK;
547 done:
548         if (msg) ads_msgfree(ads, msg);
549
550         return status;
551 }
552
553
554 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
555                                 TALLOC_CTX *mem_ctx,
556                                 uint32 group_rid, uint32 *num_names, 
557                                 uint32 **rid_mem, char ***names, 
558                                 uint32 **name_types)
559 {
560         DOM_SID group_sid;
561         char *sidstr;
562         const char *attrs[] = {"sAMAccountName", "objectSid", "sAMAccountType", NULL};
563         int rc, count;
564         void *res=NULL, *msg=NULL;
565         ADS_STRUCT *ads = NULL;
566         char *exp;
567         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
568
569         *num_names = 0;
570
571         ads = ads_cached_connection(domain);
572         if (!ads) goto done;
573
574         sid_from_rid(domain, group_rid, &group_sid);
575         sidstr = ads_sid_binstring(&group_sid);
576         /* search for all users who have that group sid as primary group or as member */
577         asprintf(&exp, "(&(objectclass=user)(|(primaryGroupID=%d)(memberOf=%s)))",
578                  group_rid, sidstr);
579         rc = ads_search_retry(ads, &res, exp, attrs);
580         free(exp);
581         free(sidstr);
582         if (rc) {
583                 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
584                 goto done;
585         }
586
587         count = ads_count_replies(ads, res);
588         if (count == 0) {
589                 status = NT_STATUS_OK;
590                 goto done;
591         }
592
593         (*rid_mem) = talloc(mem_ctx, sizeof(uint32) * count);
594         (*name_types) = talloc(mem_ctx, sizeof(uint32) * count);
595         (*names) = talloc(mem_ctx, sizeof(char *) * count);
596
597         for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
598                 uint32 atype, rid;
599                 DOM_SID sid;
600
601                 (*names)[*num_names] = ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
602                 if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype)) {
603                         continue;
604                 }
605                 (*name_types)[*num_names] = ads_atype_map(atype);
606                 if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
607                         DEBUG(1,("No sid for %s !?\n", (*names)[*num_names]));
608                         continue;
609                 }
610                 if (!sid_peek_rid(&sid, &rid)) {
611                         DEBUG(1,("No rid for %s !?\n", (*names)[*num_names]));
612                         continue;
613                 }
614                 (*rid_mem)[*num_names] = rid;
615                 (*num_names)++;
616         }       
617
618         status = NT_STATUS_OK;
619 done:
620         if (res) ads_msgfree(ads, res);
621
622         return status;
623 }
624
625 /* find the sequence number for a domain */
626 static uint32 sequence_number(struct winbindd_domain *domain)
627 {
628         uint32 usn;
629         ADS_STRUCT *ads = NULL;
630
631         ads = ads_cached_connection(domain);
632         if (!ads) return DOM_SEQUENCE_NONE;
633
634         if (!ads_USN(ads, &usn)) return DOM_SEQUENCE_NONE;
635
636         return usn;
637 }
638
639 /* the ADS backend methods are exposed via this structure */
640 struct winbindd_methods ads_methods = {
641         query_user_list,
642         enum_dom_groups,
643         name_to_sid,
644         sid_to_name,
645         query_user,
646         lookup_usergroups,
647         lookup_groupmem,
648         sequence_number
649 };
650
651 #endif