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