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