Caching user, group and domain sam handles was a stupid idea.
[ira/wip.git] / source3 / nsswitch / wb_client.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 2.0
4
5    winbind client code
6
7    Copyright (C) Tim Potter 2000
8    Copyright (C) Andrew Tridgell 2000
9    
10    This library is free software; you can redistribute it and/or
11    modify it under the terms of the GNU Library General Public
12    License as published by the Free Software Foundation; either
13    version 2 of the License, or (at your option) any later version.
14    
15    This library is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18    Library General Public License for more details.
19    
20    You should have received a copy of the GNU Library General Public
21    License along with this library; if not, write to the
22    Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23    Boston, MA  02111-1307, USA.   
24 */
25
26 #include "includes.h"
27
28 NSS_STATUS winbindd_request(int req_type,
29                                  struct winbindd_request *request,
30                                  struct winbindd_response *response);
31
32 /* Copy of parse_domain_user from winbindd_util.c.  Parse a string of the
33    form DOMAIN/user into a domain and a user */
34
35 static void parse_domain_user(char *domuser, fstring domain, fstring user)
36 {
37         char *p = strchr(domuser,*lp_winbind_separator());
38
39         if (!p) {
40                 fstrcpy(domain,"");
41                 fstrcpy(user, domuser);
42                 return;
43         }
44         
45         fstrcpy(user, p+1);
46         fstrcpy(domain, domuser);
47         domain[PTR_DIFF(p, domuser)] = 0;
48         strupper(domain);
49 }
50
51 /* Call winbindd to convert a name to a sid */
52
53 BOOL winbind_lookup_name(const char *name, DOM_SID *sid, 
54                          enum SID_NAME_USE *name_type)
55 {
56         struct winbindd_request request;
57         struct winbindd_response response;
58         NSS_STATUS result;
59         
60         if (!sid || !name_type)
61                 return False;
62
63         /*
64          * Don't do the lookup if the name has no separator.
65          */
66
67         if (!strchr(name, *lp_winbind_separator()))
68                 return False;
69
70         /* Send off request */
71
72         ZERO_STRUCT(request);
73         ZERO_STRUCT(response);
74
75         fstrcpy(request.data.name, name);
76
77         if ((result = winbindd_request(WINBINDD_LOOKUPNAME, &request, 
78                                        &response)) == NSS_STATUS_SUCCESS) {
79                 string_to_sid(sid, response.data.sid.sid);
80                 *name_type = (enum SID_NAME_USE)response.data.sid.type;
81         }
82
83         return result == NSS_STATUS_SUCCESS;
84 }
85
86 /* Call winbindd to convert sid to name */
87
88 BOOL winbind_lookup_sid(DOM_SID *sid, fstring dom_name, fstring name, 
89                         enum SID_NAME_USE *name_type)
90 {
91         struct winbindd_request request;
92         struct winbindd_response response;
93         NSS_STATUS result;
94         fstring sid_str;
95         
96         /* Initialise request */
97
98         ZERO_STRUCT(request);
99         ZERO_STRUCT(response);
100
101         sid_to_string(sid_str, sid);
102         fstrcpy(request.data.sid, sid_str);
103         
104         /* Make request */
105
106         result = winbindd_request(WINBINDD_LOOKUPSID, &request, &response);
107
108         /* Copy out result */
109
110         if (result == NSS_STATUS_SUCCESS) {
111                 parse_domain_user(response.data.name.name, dom_name, name);
112                 *name_type = (enum SID_NAME_USE)response.data.name.type;
113
114                 DEBUG(10, ("winbind_lookup_sid: SUCCESS: SID %s -> %s %s\n", 
115                            sid_str, dom_name, name));
116         }
117
118         return (result == NSS_STATUS_SUCCESS);
119 }
120
121 /* Call winbindd to convert SID to uid */
122
123 BOOL winbind_sid_to_uid(uid_t *puid, DOM_SID *sid)
124 {
125         struct winbindd_request request;
126         struct winbindd_response response;
127         int result;
128         fstring sid_str;
129
130         if (!puid)
131                 return False;
132
133         /* Initialise request */
134
135         ZERO_STRUCT(request);
136         ZERO_STRUCT(response);
137
138         sid_to_string(sid_str, sid);
139         fstrcpy(request.data.sid, sid_str);
140         
141         /* Make request */
142
143         result = winbindd_request(WINBINDD_SID_TO_UID, &request, &response);
144
145         /* Copy out result */
146
147         if (result == NSS_STATUS_SUCCESS) {
148                 *puid = response.data.uid;
149         }
150
151         return (result == NSS_STATUS_SUCCESS);
152 }
153
154 /* Call winbindd to convert uid to sid */
155
156 BOOL winbind_uid_to_sid(DOM_SID *sid, uid_t uid)
157 {
158         struct winbindd_request request;
159         struct winbindd_response response;
160         int result;
161
162         if (!sid)
163                 return False;
164
165         /* Initialise request */
166
167         ZERO_STRUCT(request);
168         ZERO_STRUCT(response);
169
170         request.data.uid = uid;
171
172         /* Make request */
173
174         result = winbindd_request(WINBINDD_UID_TO_SID, &request, &response);
175
176         /* Copy out result */
177
178         if (result == NSS_STATUS_SUCCESS) {
179                 string_to_sid(sid, response.data.sid.sid);
180         } else {
181                 sid_copy(sid, &global_sid_NULL);
182         }
183
184         return (result == NSS_STATUS_SUCCESS);
185 }
186
187 /* Call winbindd to convert SID to gid */
188
189 BOOL winbind_sid_to_gid(gid_t *pgid, DOM_SID *sid)
190 {
191         struct winbindd_request request;
192         struct winbindd_response response;
193         int result;
194         fstring sid_str;
195
196         if (!pgid)
197                 return False;
198
199         /* Initialise request */
200
201         ZERO_STRUCT(request);
202         ZERO_STRUCT(response);
203
204         sid_to_string(sid_str, sid);
205         fstrcpy(request.data.sid, sid_str);
206         
207         /* Make request */
208
209         result = winbindd_request(WINBINDD_SID_TO_GID, &request, &response);
210
211         /* Copy out result */
212
213         if (result == NSS_STATUS_SUCCESS) {
214                 *pgid = response.data.gid;
215         }
216
217         return (result == NSS_STATUS_SUCCESS);
218 }
219
220 /* Call winbindd to convert gid to sid */
221
222 BOOL winbind_gid_to_sid(DOM_SID *sid, gid_t gid)
223 {
224         struct winbindd_request request;
225         struct winbindd_response response;
226         int result;
227
228         if (!sid)
229                 return False;
230
231         /* Initialise request */
232
233         ZERO_STRUCT(request);
234         ZERO_STRUCT(response);
235
236         request.data.gid = gid;
237
238         /* Make request */
239
240         result = winbindd_request(WINBINDD_GID_TO_SID, &request, &response);
241
242         /* Copy out result */
243
244         if (result == NSS_STATUS_SUCCESS) {
245                 string_to_sid(sid, response.data.sid.sid);
246         } else {
247                 sid_copy(sid, &global_sid_NULL);
248         }
249
250         return (result == NSS_STATUS_SUCCESS);
251 }
252
253 /* Fetch the list of groups a user is a member of from winbindd.  This is
254    used by winbind_initgroups and winbind_getgroups. */
255
256 static int wb_getgroups(const char *user, gid_t **groups)
257 {
258         struct winbindd_request request;
259         struct winbindd_response response;
260         int result;
261
262         /* Call winbindd */
263
264         fstrcpy(request.data.username, user);
265
266         ZERO_STRUCT(response);
267
268         result = winbindd_request(WINBINDD_GETGROUPS, &request, &response);
269
270         if (result == NSS_STATUS_SUCCESS) {
271                 
272                 /* Return group list.  Don't forget to free the group list
273                    when finished. */
274
275                 *groups = (gid_t *)response.extra_data;
276                 return response.data.num_entries;
277         }
278
279         return -1;
280 }
281
282 /* Call winbindd to initialise group membership.  This is necessary for
283    some systems (i.e RH5.2) that do not have an initgroups function as part
284    of the nss extension.  In RH5.2 this is implemented using getgrent()
285    which can be amazingly inefficient as well as having problems with
286    username case. */
287
288 int winbind_initgroups(char *user, gid_t gid)
289 {
290         gid_t *tgr, *groups = NULL;
291         int result;
292
293         /* Call normal initgroups if we are a local user */
294
295         if (!strchr(user, *lp_winbind_separator())) {
296                 return initgroups(user, gid);
297         }
298
299         result = wb_getgroups(user, &groups);
300
301         DEBUG(10,("winbind_getgroups: %s: result = %s\n", user, 
302                   result == -1 ? "FAIL" : "SUCCESS"));
303
304         if (result != -1) {
305                 int ngroups = result, i;
306                 BOOL is_member = False;
307
308                 /* Check to see if the passed gid is already in the list */
309
310                 for (i = 0; i < ngroups; i++) {
311                         if (groups[i] == gid) {
312                                 is_member = True;
313                         }
314                 }
315
316                 /* Add group to list if necessary */
317
318                 if (!is_member) {
319                         tgr = (gid_t *)Realloc(groups, sizeof(gid_t) * ngroups + 1);
320                         
321                         if (!tgr) {
322                                 errno = ENOMEM;
323                                 result = -1;
324                                 goto done;
325                         }
326                         else groups = tgr;
327
328                         groups[ngroups] = gid;
329                         ngroups++;
330                 }
331
332                 /* Set the groups */
333
334                 if (sys_setgroups(ngroups, groups) == -1) {
335                         errno = EPERM;
336                         result = -1;
337                         goto done;
338                 }
339
340         } else {
341                 
342                 /* The call failed.  Set errno to something so we don't get
343                    a bogus value from the last failed system call. */
344
345                 errno = EIO;
346         }
347
348         /* Free response data if necessary */
349
350  done:
351         SAFE_FREE(groups);
352
353         return result;
354 }
355
356 /* Return a list of groups the user is a member of.  This function is
357    useful for large systems where inverting the group database would be too
358    time consuming.  If size is zero, list is not modified and the total
359    number of groups for the user is returned. */
360
361 int winbind_getgroups(const char *user, int size, gid_t *list)
362 {
363         gid_t *groups = NULL;
364         int result, i;
365
366         /*
367          * Don't do the lookup if the name has no separator.
368          */
369
370         if (!strchr(user, *lp_winbind_separator()))
371                 return -1;
372
373         /* Fetch list of groups */
374
375         result = wb_getgroups(user, &groups);
376
377         if (size == 0)
378                 goto done;
379
380         if (result > size) {
381                 result = -1;
382                 errno = EINVAL; /* This is what getgroups() does */
383                 goto done;
384         }
385
386         /* Copy list of groups across */
387
388         for (i = 0; i < result; i++) {
389                 list[i] = groups[i];
390         }
391
392  done:
393         SAFE_FREE(groups);
394         return result;
395 }
396
397 /* Utility function. Convert a uid_t to a name if possible. */
398
399 BOOL winbind_uidtoname(fstring name, uid_t uid)
400 {
401         DOM_SID sid;
402         fstring dom_name;
403         fstring user_name;
404         enum SID_NAME_USE name_type;
405
406         if (!winbind_uid_to_sid(&sid, uid))
407                 return False;
408         if (!winbind_lookup_sid(&sid, dom_name, user_name, &name_type))
409                 return False;
410
411         if (name_type != SID_NAME_USER)
412                 return False;
413
414         slprintf(name, sizeof(fstring)-1, "%s%s%s", dom_name, 
415                  lp_winbind_separator(), user_name);
416
417         return True;
418 }
419
420 /* Utility function. Convert a gid_t to a name if possible. */
421
422 BOOL winbind_gidtoname(fstring name, gid_t gid)
423 {
424         DOM_SID sid;
425         fstring dom_name;
426         fstring group_name;
427         enum SID_NAME_USE name_type;
428
429         if (!winbind_gid_to_sid(&sid, gid))
430                 return False;
431         if (!winbind_lookup_sid(&sid, dom_name, group_name, &name_type))
432                 return False;
433
434         if (name_type != SID_NAME_DOM_GRP)
435                 return False;
436
437         slprintf(name, sizeof(fstring)-1, "%s%s%s", dom_name, 
438                  lp_winbind_separator(), group_name);
439
440         return True;
441 }
442
443 /* Utility function. Convert a name to a uid_t if possible. */
444
445 BOOL winbind_nametouid(uid_t *puid, const char *name)
446 {
447         DOM_SID sid;
448         enum SID_NAME_USE name_type;
449
450         if (!winbind_lookup_name(name, &sid, &name_type))
451                 return False;
452
453         if (name_type != SID_NAME_USER)
454                 return False;
455
456         return winbind_sid_to_uid(puid, &sid);
457 }
458
459 /* Utility function. Convert a name to a gid_t if possible. */
460
461 BOOL winbind_nametogid(gid_t *pgid, const char *gname)
462 {
463         DOM_SID g_sid;
464         enum SID_NAME_USE name_type;
465
466         if (!winbind_lookup_name(gname, &g_sid, &name_type))
467                 return False;
468
469         if (name_type != SID_NAME_DOM_GRP)
470                 return False;
471
472         return winbind_sid_to_gid(pgid, &g_sid);
473 }