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