6b53bd96a6a0d85db468b24fecc5093062c404ec
[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 /* useful utility */
28 static void sid_from_rid(struct winbindd_domain *domain, uint32 rid, DOM_SID *sid)
29 {
30         sid_copy(sid, &domain->sid);
31         sid_append_rid(sid, rid);
32 }
33
34 /* turn a sAMAccountType into a SID_NAME_USE */
35 static enum SID_NAME_USE ads_atype_map(uint32 atype)
36 {
37         switch (atype & 0xF0000000) {
38         case ATYPE_GROUP:
39                 return SID_NAME_DOM_GRP;
40         case ATYPE_USER:
41                 return SID_NAME_USER;
42         default:
43                 DEBUG(1,("hmm, need to map account type 0x%x\n", atype));
44         }
45         return SID_NAME_UNKNOWN;
46 }
47
48 /* Query display info for a realm. This is the basic user list fn */
49 static NTSTATUS query_user_list(struct winbindd_domain *domain,
50                                TALLOC_CTX *mem_ctx,
51                                uint32 *start_ndx, uint32 *num_entries, 
52                                WINBIND_USERINFO **info)
53 {
54         ADS_STRUCT *ads;
55         const char *attrs[] = {"sAMAccountName", "name", "objectSid", "primaryGroupID", 
56                                "userAccountControl", NULL};
57         int rc, i, count;
58         void *res;
59         void *msg;
60
61         DEBUG(3,("ads: query_user_list\n"));
62
63         if ((*start_ndx) != 0) {
64                 DEBUG(1,("ads backend start_ndx not implemented\n"));
65                 return NT_STATUS_NOT_IMPLEMENTED;
66         }
67
68         ads = ads_init(NULL, NULL, NULL);
69         if (!ads) {
70                 DEBUG(1,("ads_init failed\n"));
71                 return NT_STATUS_UNSUCCESSFUL;
72         }
73
74         rc = ads_connect(ads);
75         if (rc) {
76                 DEBUG(1,("query_user_list ads_connect: %s\n", ads_errstr(rc)));
77                 return NT_STATUS_UNSUCCESSFUL;
78         }
79
80         rc = ads_search(ads, &res, "(objectclass=user)", attrs);
81         if (rc) {
82                 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
83                 return NT_STATUS_UNSUCCESSFUL;
84         }
85
86         count = ads_count_replies(ads, res);
87         if (count == 0) {
88                 DEBUG(1,("query_user_list: No users found\n"));
89                 return NT_STATUS_UNSUCCESSFUL;
90         }
91
92         (*info) = talloc(mem_ctx, count * sizeof(**info));
93         if (!*info) return NT_STATUS_NO_MEMORY;
94
95         i = 0;
96
97         for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
98                 char *name, *gecos;
99                 DOM_SID sid;
100                 uint32 rid, group;
101                 uint32 account_control;
102
103                 if (!ads_pull_uint32(ads, msg, "userAccountControl", 
104                                      &account_control) ||
105                     !(account_control & UF_NORMAL_ACCOUNT)) continue;
106
107                 name = ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
108                 gecos = ads_pull_string(ads, mem_ctx, msg, "name");
109                 if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
110                         DEBUG(1,("No sid for %s !?\n", name));
111                         continue;
112                 }
113                 if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group)) {
114                         DEBUG(1,("No primary group for %s !?\n", name));
115                         continue;
116                 }
117
118                 if (!sid_peek_rid(&sid, &rid)) {
119                         DEBUG(1,("No rid for %s !?\n", name));
120                         continue;
121                 }
122
123                 (*info)[i].acct_name = name;
124                 (*info)[i].full_name = gecos;
125                 (*info)[i].user_rid = rid;
126                 (*info)[i].group_rid = group;
127                 i++;
128         }
129
130         (*num_entries) = i;
131
132         ads_destroy(&ads);
133
134         return NT_STATUS_OK;
135 }
136
137 /* list all domain groups */
138 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
139                                 TALLOC_CTX *mem_ctx,
140                                 uint32 *start_ndx, uint32 *num_entries, 
141                                 struct acct_info **info)
142 {
143         ADS_STRUCT *ads;
144         const char *attrs[] = {"sAMAccountName", "name", "objectSid", 
145                                "sAMAccountType", NULL};
146         int rc, i, count;
147         void *res;
148         void *msg;
149
150         DEBUG(3,("ads: enum_dom_groups\n"));
151
152         if ((*start_ndx) != 0) {
153                 DEBUG(1,("ads backend start_ndx not implemented\n"));
154                 return NT_STATUS_NOT_IMPLEMENTED;
155         }
156
157         ads = ads_init(NULL, NULL, NULL);
158         if (!ads) {
159                 DEBUG(1,("ads_init failed\n"));
160                 return NT_STATUS_UNSUCCESSFUL;
161         }
162
163         rc = ads_connect(ads);
164         if (rc) {
165                 DEBUG(1,("query_user_list ads_connect: %s\n", ads_errstr(rc)));
166                 return NT_STATUS_UNSUCCESSFUL;
167         }
168
169         rc = ads_search(ads, &res, "(objectclass=group)", attrs);
170         if (rc) {
171                 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
172                 return NT_STATUS_UNSUCCESSFUL;
173         }
174
175         count = ads_count_replies(ads, res);
176         if (count == 0) {
177                 DEBUG(1,("query_user_list: No users found\n"));
178                 return NT_STATUS_UNSUCCESSFUL;
179         }
180
181         (*info) = talloc(mem_ctx, count * sizeof(**info));
182         if (!*info) return NT_STATUS_NO_MEMORY;
183
184         i = 0;
185
186         for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
187                 char *name, *gecos;
188                 DOM_SID sid;
189                 uint32 rid;
190                 uint32 account_type;
191
192                 if (!ads_pull_uint32(ads, msg, "sAMAccountType", 
193                                      &account_type) ||
194                     !(account_type & ATYPE_GROUP)) continue;
195
196                 name = ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
197                 gecos = ads_pull_string(ads, mem_ctx, msg, "name");
198                 if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
199                         DEBUG(1,("No sid for %s !?\n", name));
200                         continue;
201                 }
202
203                 if (!sid_peek_rid(&sid, &rid)) {
204                         DEBUG(1,("No rid for %s !?\n", name));
205                         continue;
206                 }
207
208                 fstrcpy((*info)[i].acct_name, name);
209                 fstrcpy((*info)[i].acct_desc, gecos);
210                 (*info)[i].rid = rid;
211                 i++;
212         }
213
214         (*num_entries) = i;
215
216         ads_destroy(&ads);
217
218         return NT_STATUS_OK;
219 }
220
221
222 /* convert a single name to a sid in a domain */
223 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
224                             const char *name,
225                             DOM_SID *sid,
226                             enum SID_NAME_USE *type)
227 {
228         ADS_STRUCT *ads;
229         const char *attrs[] = {"objectSid", "sAMAccountType", NULL};
230         int rc, count;
231         void *res;
232         char *exp;
233         uint32 t;
234         fstring name2, dom2;
235         
236         /* sigh. Need to fix interface to give us a raw name */
237         if (!parse_domain_user(name, dom2, name2))
238                 return NT_STATUS_UNSUCCESSFUL;
239
240         DEBUG(3,("ads: name_to_sid\n"));
241
242         ads = ads_init(NULL, NULL, NULL);
243         if (!ads) {
244                 DEBUG(1,("ads_init failed\n"));
245                 return NT_STATUS_UNSUCCESSFUL;
246         }
247
248         rc = ads_connect(ads);
249         if (rc) {
250                 DEBUG(1,("name_to_sid ads_connect: %s\n", ads_errstr(rc)));
251                 return NT_STATUS_UNSUCCESSFUL;
252         }
253
254         asprintf(&exp, "(sAMAccountName=%s)", name2);
255         rc = ads_search(ads, &res, exp, attrs);
256         free(exp);
257         if (rc) {
258                 DEBUG(1,("name_to_sid ads_search: %s\n", ads_errstr(rc)));
259                 return NT_STATUS_UNSUCCESSFUL;
260         }
261
262         count = ads_count_replies(ads, res);
263         if (count != 1) {
264                 DEBUG(1,("name_to_sid: %s not found\n", name));
265                 return NT_STATUS_UNSUCCESSFUL;
266         }
267
268         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
269                 DEBUG(1,("No sid for %s !?\n", name));
270                 return NT_STATUS_UNSUCCESSFUL;
271         }
272
273         if (!ads_pull_uint32(ads, res, "sAMAccountType", &t)) {
274                 DEBUG(1,("No sAMAccountType for %s !?\n", name));
275                 return NT_STATUS_UNSUCCESSFUL;
276         }
277
278         *type = ads_atype_map(t);
279
280         ads_destroy(&ads);
281
282
283         return NT_STATUS_OK;
284 }
285
286 /* convert a sid to a user or group name */
287 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
288                             TALLOC_CTX *mem_ctx,
289                             DOM_SID *sid,
290                             char **name,
291                             enum SID_NAME_USE *type)
292 {
293         ADS_STRUCT *ads;
294         const char *attrs[] = {"sAMAccountName", "sAMAccountType", NULL};
295         int rc;
296         void *msg;
297         char *exp;
298         char *sidstr;
299         uint32 atype;
300         char *s;
301
302         DEBUG(3,("ads: sid_to_name\n"));
303
304         ads = ads_init(NULL, NULL, NULL);
305         if (!ads) {
306                 DEBUG(1,("ads_init failed\n"));
307                 return NT_STATUS_UNSUCCESSFUL;
308         }
309
310         rc = ads_connect(ads);
311         if (rc) {
312                 DEBUG(1,("sid_to_name ads_connect: %s\n", ads_errstr(rc)));
313                 return NT_STATUS_UNSUCCESSFUL;
314         }
315
316         sidstr = ads_sid_binstring(sid);
317         asprintf(&exp, "(objectSid=%s)", sidstr);
318         rc = ads_search(ads, &msg, exp, attrs);
319         free(exp);
320         free(sidstr);
321         if (rc) {
322                 DEBUG(1,("sid_to_name ads_search: %s\n", ads_errstr(rc)));
323                 return NT_STATUS_UNSUCCESSFUL;
324         }
325
326         if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype)) {
327                 return NT_STATUS_UNSUCCESSFUL;
328         }
329
330         s = ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
331         *name = talloc_asprintf(mem_ctx, "%s%s%s", domain->name, lp_winbind_separator(), s);
332         *type = ads_atype_map(atype);
333
334         return NT_STATUS_OK;
335 }
336
337
338 /* Lookup user information from a rid */
339 static NTSTATUS query_user(struct winbindd_domain *domain, 
340                            TALLOC_CTX *mem_ctx, 
341                            uint32 user_rid, 
342                            WINBIND_USERINFO *info)
343 {
344         ADS_STRUCT *ads;
345         const char *attrs[] = {"sAMAccountName", "name", "objectSid", "primaryGroupID", 
346                                "userAccountControl", NULL};
347         int rc, count;
348         void *msg;
349         char *exp;
350         DOM_SID sid;
351         char *sidstr;
352
353         DEBUG(3,("ads: query_user\n"));
354
355         sid_from_rid(domain, user_rid, &sid);
356
357         ads = ads_init(NULL, NULL, NULL);
358         if (!ads) {
359                 DEBUG(1,("ads_init failed\n"));
360                 return NT_STATUS_UNSUCCESSFUL;
361         }
362
363         rc = ads_connect(ads);
364         if (rc) {
365                 DEBUG(1,("query_user ads_connect: %s\n", ads_errstr(rc)));
366                 return NT_STATUS_UNSUCCESSFUL;
367         }
368
369         sidstr = ads_sid_binstring(&sid);
370         asprintf(&exp, "(objectSid=%s)", sidstr);
371         rc = ads_search(ads, &msg, exp, attrs);
372         free(exp);
373         free(sidstr);
374         if (rc) {
375                 DEBUG(1,("query_user(rid=%d) ads_search: %s\n", user_rid, ads_errstr(rc)));
376                 return NT_STATUS_UNSUCCESSFUL;
377         }
378
379         count = ads_count_replies(ads, msg);
380         if (count != 1) {
381                 DEBUG(1,("query_user(rid=%d): Not found\n", user_rid));
382                 return NT_STATUS_UNSUCCESSFUL;
383         }
384
385         info->acct_name = ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
386         info->full_name = ads_pull_string(ads, mem_ctx, msg, "name");
387         if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
388                 DEBUG(1,("No sid for %d !?\n", user_rid));
389                 goto error;
390         }
391         if (!ads_pull_uint32(ads, msg, "primaryGroupID", &info->group_rid)) {
392                 DEBUG(1,("No primary group for %d !?\n", user_rid));
393                 goto error;
394         }
395         
396         if (!sid_peek_rid(&sid, &info->user_rid)) {
397                 DEBUG(1,("No rid for %d !?\n", user_rid));
398                 goto error;
399         }
400
401         ads_destroy(&ads);
402
403         return NT_STATUS_OK;
404 error:
405         ads_destroy(&ads);
406         return NT_STATUS_UNSUCCESSFUL;
407 }
408
409 /* Lookup groups a user is a member of. */
410 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
411                                   TALLOC_CTX *mem_ctx,
412                                   uint32 user_rid, 
413                                   uint32 *num_groups, uint32 **user_gids)
414 {
415         ADS_STRUCT *ads;
416         const char *attrs[] = {"distinguishedName", NULL};
417         const char *attrs2[] = {"tokenGroups", "primaryGroupID", NULL};
418         int rc, count;
419         void *msg;
420         char *exp;
421         char *user_dn;
422         DOM_SID *sids;
423         int i;
424         uint32 primary_group;
425         DOM_SID sid;
426         char *sidstr;
427
428         DEBUG(3,("ads: lookup_usergroups\n"));
429
430         (*num_groups) = 0;
431
432         sid_from_rid(domain, user_rid, &sid);
433
434         ads = ads_init(NULL, NULL, NULL);
435         if (!ads) {
436                 DEBUG(1,("ads_init failed\n"));
437                 return NT_STATUS_UNSUCCESSFUL;
438         }
439
440         rc = ads_connect(ads);
441         if (rc) {
442                 DEBUG(1,("lookup_usergroups ads_connect: %s\n", ads_errstr(rc)));
443                 return NT_STATUS_UNSUCCESSFUL;
444         }
445
446         sidstr = ads_sid_binstring(&sid);
447         asprintf(&exp, "(objectSid=%s)", sidstr);
448         rc = ads_search(ads, &msg, exp, attrs);
449         free(exp);
450         free(sidstr);
451         if (rc) {
452                 DEBUG(1,("lookup_usergroups(rid=%d) ads_search: %s\n", user_rid, ads_errstr(rc)));
453                 return NT_STATUS_UNSUCCESSFUL;
454         }
455
456         user_dn = ads_pull_string(ads, mem_ctx, msg, "distinguishedName");
457
458         rc = ads_search_dn(ads, &msg, user_dn, attrs2);
459         if (rc) {
460                 DEBUG(1,("lookup_usergroups(rid=%d) ads_search tokenGroups: %s\n", user_rid, ads_errstr(rc)));
461                 return NT_STATUS_UNSUCCESSFUL;
462         }
463
464         if (!ads_pull_uint32(ads, msg, "primaryGroupID", &primary_group)) {
465                 DEBUG(1,("No primary group for rid=%d !?\n", user_rid));
466                 return NT_STATUS_UNSUCCESSFUL;
467         }
468
469         count = ads_pull_sids(ads, mem_ctx, msg, "tokenGroups", &sids) + 1;
470         (*user_gids) = (uint32 *)talloc(mem_ctx, sizeof(uint32) * count);
471         (*user_gids)[(*num_groups)++] = primary_group;
472
473         for (i=1;i<count;i++) {
474                 uint32 rid;
475                 if (!sid_peek_rid(&sids[i-1], &rid)) continue;
476                 (*user_gids)[*num_groups] = rid;
477                 (*num_groups)++;
478         }
479
480         ads_destroy(&ads);
481         return NT_STATUS_OK;
482 }
483
484
485 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
486                                 TALLOC_CTX *mem_ctx,
487                                 uint32 group_rid, uint32 *num_names, 
488                                 uint32 **rid_mem, char ***names, 
489                                 uint32 **name_types)
490 {
491         DOM_SID group_sid;
492         char *sidstr;
493         const char *attrs[] = {"sAMAccountName", "objectSid", "sAMAccountType", NULL};
494         int rc, count;
495         void *res, *msg;
496         ADS_STRUCT *ads;
497         char *exp;
498
499         *num_names = 0;
500
501         ads = ads_init(NULL, NULL, NULL);
502         if (!ads) {
503                 DEBUG(1,("ads_init failed\n"));
504                 return NT_STATUS_UNSUCCESSFUL;
505         }
506
507         rc = ads_connect(ads);
508         if (rc) {
509                 DEBUG(1,("query_user_list ads_connect: %s\n", ads_errstr(rc)));
510                 return NT_STATUS_UNSUCCESSFUL;
511         }
512
513         sid_from_rid(domain, group_rid, &group_sid);
514         sidstr = ads_sid_binstring(&group_sid);
515         /* search for all users who have that group sid as primary group or as member */
516         asprintf(&exp, "(&(objectclass=user)(|(primaryGroupID=%d)(memberOf=%s)))",
517                  group_rid, sidstr);
518         rc = ads_search(ads, &res, exp, attrs);
519         if (rc) {
520                 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
521                 return NT_STATUS_UNSUCCESSFUL;
522         }
523
524         count = ads_count_replies(ads, res);
525         if (count == 0) {
526                 return NT_STATUS_OK;
527         }
528
529         (*rid_mem) = talloc(mem_ctx, sizeof(uint32) * count);
530         (*name_types) = talloc(mem_ctx, sizeof(uint32) * count);
531         (*names) = talloc(mem_ctx, sizeof(char *) * count);
532
533         for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
534                 uint32 atype, rid;
535                 DOM_SID sid;
536
537                 (*names)[*num_names] = ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
538                 if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype)) {
539                         continue;
540                 }
541                 (*name_types)[*num_names] = ads_atype_map(atype);
542                 if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
543                         DEBUG(1,("No sid for %s !?\n", (*names)[*num_names]));
544                         continue;
545                 }
546                 if (!sid_peek_rid(&sid, &rid)) {
547                         DEBUG(1,("No rid for %s !?\n", (*names)[*num_names]));
548                         continue;
549                 }
550                 (*rid_mem)[*num_names] = rid;
551                 (*num_names)++;
552         }       
553
554         ads_destroy(&ads);
555
556         return NT_STATUS_OK;
557 }
558
559 /* the ADS backend methods are exposed via this structure */
560 struct winbindd_methods ads_methods = {
561         query_user_list,
562         enum_dom_groups,
563         name_to_sid,
564         sid_to_name,
565         query_user,
566         lookup_usergroups,
567         lookup_groupmem
568 };
569
570 #endif