moved lookup_usergroups() into the backend structure
[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 /* Query display info for a realm. This is the basic user list fn */
28 static NTSTATUS query_user_list(struct winbindd_domain *domain,
29                                TALLOC_CTX *mem_ctx,
30                                uint32 *start_ndx, uint32 *num_entries, 
31                                WINBIND_USERINFO **info)
32 {
33         ADS_STRUCT *ads;
34         const char *attrs[] = {"sAMAccountName", "name", "objectSid", "primaryGroupID", 
35                                "userAccountControl", NULL};
36         int rc, i, count;
37         void *res;
38         void *msg;
39
40         DEBUG(3,("ads: query_user_list\n"));
41
42         if ((*start_ndx) != 0) {
43                 DEBUG(1,("ads backend start_ndx not implemented\n"));
44                 return NT_STATUS_NOT_IMPLEMENTED;
45         }
46
47         ads = ads_init(NULL, NULL, NULL);
48         if (!ads) {
49                 DEBUG(1,("ads_init failed\n"));
50                 return NT_STATUS_UNSUCCESSFUL;
51         }
52
53         rc = ads_connect(ads);
54         if (rc) {
55                 DEBUG(1,("query_user_list ads_connect: %s\n", ads_errstr(rc)));
56                 return NT_STATUS_UNSUCCESSFUL;
57         }
58
59         rc = ads_search(ads, &res, "(objectclass=user)", attrs);
60         if (rc) {
61                 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
62                 return NT_STATUS_UNSUCCESSFUL;
63         }
64
65         count = ads_count_replies(ads, res);
66         if (count == 0) {
67                 DEBUG(1,("query_user_list: No users found\n"));
68                 return NT_STATUS_UNSUCCESSFUL;
69         }
70
71         (*info) = talloc(mem_ctx, count * sizeof(**info));
72         if (!*info) return NT_STATUS_NO_MEMORY;
73
74         i = 0;
75
76         for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
77                 char *name, *gecos;
78                 DOM_SID sid;
79                 uint32 rid, group;
80                 uint32 account_control;
81
82                 if (!ads_pull_uint32(ads, msg, "userAccountControl", 
83                                      &account_control) ||
84                     !(account_control & UF_NORMAL_ACCOUNT)) continue;
85
86                 name = ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
87                 gecos = ads_pull_string(ads, mem_ctx, msg, "name");
88                 if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
89                         DEBUG(1,("No sid for %s !?\n", name));
90                         continue;
91                 }
92                 if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group)) {
93                         DEBUG(1,("No primary group for %s !?\n", name));
94                         continue;
95                 }
96
97                 if (!sid_peek_rid(&sid, &rid)) {
98                         DEBUG(1,("No rid for %s !?\n", name));
99                         continue;
100                 }
101
102                 (*info)[i].acct_name = name;
103                 (*info)[i].full_name = gecos;
104                 (*info)[i].user_rid = rid;
105                 (*info)[i].group_rid = group;
106                 i++;
107         }
108
109         (*num_entries) = i;
110
111         ads_destroy(&ads);
112
113         return NT_STATUS_OK;
114 }
115
116 /* list all domain groups */
117 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
118                                 TALLOC_CTX *mem_ctx,
119                                 uint32 *start_ndx, uint32 *num_entries, 
120                                 struct acct_info **info)
121 {
122         ADS_STRUCT *ads;
123         const char *attrs[] = {"sAMAccountName", "name", "objectSid", 
124                                "sAMAccountType", NULL};
125         int rc, i, count;
126         void *res;
127         void *msg;
128
129         DEBUG(3,("ads: enum_dom_groups\n"));
130
131         if ((*start_ndx) != 0) {
132                 DEBUG(1,("ads backend start_ndx not implemented\n"));
133                 return NT_STATUS_NOT_IMPLEMENTED;
134         }
135
136         ads = ads_init(NULL, NULL, NULL);
137         if (!ads) {
138                 DEBUG(1,("ads_init failed\n"));
139                 return NT_STATUS_UNSUCCESSFUL;
140         }
141
142         rc = ads_connect(ads);
143         if (rc) {
144                 DEBUG(1,("query_user_list ads_connect: %s\n", ads_errstr(rc)));
145                 return NT_STATUS_UNSUCCESSFUL;
146         }
147
148         rc = ads_search(ads, &res, "(objectclass=group)", attrs);
149         if (rc) {
150                 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
151                 return NT_STATUS_UNSUCCESSFUL;
152         }
153
154         count = ads_count_replies(ads, res);
155         if (count == 0) {
156                 DEBUG(1,("query_user_list: No users found\n"));
157                 return NT_STATUS_UNSUCCESSFUL;
158         }
159
160         (*info) = talloc(mem_ctx, count * sizeof(**info));
161         if (!*info) return NT_STATUS_NO_MEMORY;
162
163         i = 0;
164
165         for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
166                 char *name, *gecos;
167                 DOM_SID sid;
168                 uint32 rid;
169                 uint32 account_type;
170
171                 if (!ads_pull_uint32(ads, msg, "sAMAccountType", 
172                                      &account_type) ||
173                     !(account_type & ATYPE_GROUP)) continue;
174
175                 name = ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
176                 gecos = ads_pull_string(ads, mem_ctx, msg, "name");
177                 if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
178                         DEBUG(1,("No sid for %s !?\n", name));
179                         continue;
180                 }
181
182                 if (!sid_peek_rid(&sid, &rid)) {
183                         DEBUG(1,("No rid for %s !?\n", name));
184                         continue;
185                 }
186
187                 fstrcpy((*info)[i].acct_name, name);
188                 fstrcpy((*info)[i].acct_desc, gecos);
189                 (*info)[i].rid = rid;
190                 i++;
191         }
192
193         (*num_entries) = i;
194
195         ads_destroy(&ads);
196
197         return NT_STATUS_OK;
198 }
199
200
201 /* convert a single name to a sid in a domain */
202 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
203                             const char *name,
204                             DOM_SID *sid,
205                             enum SID_NAME_USE *type)
206 {
207         ADS_STRUCT *ads;
208         const char *attrs[] = {"objectSid", "sAMAccountType", NULL};
209         int rc, count;
210         void *res;
211         char *exp;
212         uint32 t;
213         fstring name2, dom2;
214         
215         /* sigh. Need to fix interface to give us a raw name */
216         parse_domain_user(name, dom2, name2);
217
218         DEBUG(3,("ads: name_to_sid\n"));
219
220         ads = ads_init(NULL, NULL, NULL);
221         if (!ads) {
222                 DEBUG(1,("ads_init failed\n"));
223                 return NT_STATUS_UNSUCCESSFUL;
224         }
225
226         rc = ads_connect(ads);
227         if (rc) {
228                 DEBUG(1,("name_to_sid ads_connect: %s\n", ads_errstr(rc)));
229                 return NT_STATUS_UNSUCCESSFUL;
230         }
231
232         asprintf(&exp, "(sAMAccountName=%s)", name2);
233         rc = ads_search(ads, &res, exp, attrs);
234         free(exp);
235         if (rc) {
236                 DEBUG(1,("name_to_sid ads_search: %s\n", ads_errstr(rc)));
237                 return NT_STATUS_UNSUCCESSFUL;
238         }
239
240         count = ads_count_replies(ads, res);
241         if (count != 1) {
242                 DEBUG(1,("name_to_sid: %s not found\n", name));
243                 return NT_STATUS_UNSUCCESSFUL;
244         }
245
246         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
247                 DEBUG(1,("No sid for %s !?\n", name));
248                 return NT_STATUS_UNSUCCESSFUL;
249         }
250
251         if (!ads_pull_uint32(ads, res, "sAMAccountType", &t)) {
252                 DEBUG(1,("No sAMAccountType for %s !?\n", name));
253                 return NT_STATUS_UNSUCCESSFUL;
254         }
255
256         switch (t & 0xF0000000) {
257         case ATYPE_GROUP:
258                 *type = SID_NAME_DOM_GRP;
259                 break;
260         case ATYPE_USER:
261                 *type = SID_NAME_USER;
262                 break;
263         default:
264                 DEBUG(1,("hmm, need to map account type 0x%x\n", t));
265                 *type = SID_NAME_UNKNOWN;
266                 break;
267         }
268
269         ads_destroy(&ads);
270
271         return NT_STATUS_OK;
272 }
273
274 /* Lookup user information from a rid or username. */
275 static NTSTATUS query_user(struct winbindd_domain *domain, 
276                            TALLOC_CTX *mem_ctx, 
277                            const char *user_name, uint32 user_rid, 
278                            WINBIND_USERINFO *info)
279 {
280         ADS_STRUCT *ads;
281         const char *attrs[] = {"sAMAccountName", "name", "objectSid", "primaryGroupID", 
282                                "userAccountControl", NULL};
283         int rc, count;
284         void *msg;
285         char *exp;
286         DOM_SID sid;
287         fstring dom2, name2;
288
289         /* sigh. Need to fix interface to give us a raw name */
290         parse_domain_user(user_name, dom2, name2);
291         
292         DEBUG(3,("ads: query_user\n"));
293
294         ads = ads_init(NULL, NULL, NULL);
295         if (!ads) {
296                 DEBUG(1,("ads_init failed\n"));
297                 return NT_STATUS_UNSUCCESSFUL;
298         }
299
300         rc = ads_connect(ads);
301         if (rc) {
302                 DEBUG(1,("query_user ads_connect: %s\n", ads_errstr(rc)));
303                 return NT_STATUS_UNSUCCESSFUL;
304         }
305
306         asprintf(&exp, "(sAMAccountName=%s)", name2);
307         rc = ads_search(ads, &msg, exp, attrs);
308         free(exp);
309         if (rc) {
310                 DEBUG(1,("query_user(%s) ads_search: %s\n", user_name, ads_errstr(rc)));
311                 return NT_STATUS_UNSUCCESSFUL;
312         }
313
314         count = ads_count_replies(ads, msg);
315         if (count != 1) {
316                 DEBUG(1,("query_user(%s): Not found\n", user_name));
317                 return NT_STATUS_UNSUCCESSFUL;
318         }
319
320         info->acct_name = ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
321         info->full_name = ads_pull_string(ads, mem_ctx, msg, "name");
322         if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
323                 DEBUG(1,("No sid for %s !?\n", user_name));
324                 goto error;
325         }
326         if (!ads_pull_uint32(ads, msg, "primaryGroupID", &info->group_rid)) {
327                 DEBUG(1,("No primary group for %s !?\n", user_name));
328                 goto error;
329         }
330         
331         if (!sid_peek_rid(&sid, &info->user_rid)) {
332                 DEBUG(1,("No rid for %s !?\n", user_name));
333                 goto error;
334         }
335
336         ads_destroy(&ads);
337
338         return NT_STATUS_OK;
339 error:
340         ads_destroy(&ads);
341         return NT_STATUS_UNSUCCESSFUL;
342 }
343
344 /* Lookup groups a user is a member of. */
345 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
346                                   TALLOC_CTX *mem_ctx,
347                                   uint32 user_rid, uint32 *num_groups,
348                                   uint32 **user_gids)
349 {
350         return NT_STATUS_NOT_IMPLEMENTED;
351 }
352
353 /* the ADS backend methods are exposed via this structure */
354 struct winbindd_methods ads_methods = {
355         query_user_list,
356         enum_dom_groups,
357         name_to_sid,
358         /* I can't see a good way to do a sid to name mapping with ldap,
359            and MS servers always allow RPC for this (even in native mode) so
360            just use RPC for sid_to_name. Maybe that's why they allow it? */
361         winbindd_rpc_sid_to_name,
362         query_user,
363         lookup_usergroups
364 };
365
366 #endif