Initial import
[samba] / source / 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(const DOM_SID *sid, 
68                         fstring dom_name, fstring name, 
69                         enum SID_NAME_USE *name_type)
70 {
71         struct winbindd_request request;
72         struct winbindd_response response;
73         NSS_STATUS result;
74         fstring sid_str;
75         
76         /* Initialise request */
77
78         ZERO_STRUCT(request);
79         ZERO_STRUCT(response);
80
81         sid_to_string(sid_str, sid);
82         fstrcpy(request.data.sid, sid_str);
83         
84         /* Make request */
85
86         result = winbindd_request_response(WINBINDD_LOOKUPSID, &request, &response);
87
88         /* Copy out result */
89
90         if (result == NSS_STATUS_SUCCESS) {
91                 fstrcpy(dom_name, response.data.name.dom_name);
92                 fstrcpy(name, response.data.name.name);
93                 *name_type = (enum SID_NAME_USE)response.data.name.type;
94
95                 DEBUG(10, ("winbind_lookup_sid: SUCCESS: SID %s -> %s %s\n", 
96                            sid_str, dom_name, name));
97         }
98
99         return (result == NSS_STATUS_SUCCESS);
100 }
101
102 /* Call winbindd to convert SID to uid */
103
104 BOOL winbind_sid_to_uid(uid_t *puid, const DOM_SID *sid)
105 {
106         struct winbindd_request request;
107         struct winbindd_response response;
108         int result;
109         fstring sid_str;
110
111         if (!puid)
112                 return False;
113
114         /* Initialise request */
115
116         ZERO_STRUCT(request);
117         ZERO_STRUCT(response);
118
119         sid_to_string(sid_str, sid);
120         fstrcpy(request.data.sid, sid_str);
121         
122         /* Make request */
123
124         result = winbindd_request_response(WINBINDD_SID_TO_UID, &request, &response);
125
126         /* Copy out result */
127
128         if (result == NSS_STATUS_SUCCESS) {
129                 *puid = response.data.uid;
130         }
131
132         return (result == NSS_STATUS_SUCCESS);
133 }
134
135 /* Call winbindd to convert uid to sid */
136
137 BOOL winbind_uid_to_sid(DOM_SID *sid, uid_t uid)
138 {
139         struct winbindd_request request;
140         struct winbindd_response response;
141         int result;
142
143         if (!sid)
144                 return False;
145
146         /* Initialise request */
147
148         ZERO_STRUCT(request);
149         ZERO_STRUCT(response);
150
151         request.data.uid = uid;
152
153         /* Make request */
154
155         result = winbindd_request_response(WINBINDD_UID_TO_SID, &request, &response);
156
157         /* Copy out result */
158
159         if (result == NSS_STATUS_SUCCESS) {
160                 if (!string_to_sid(sid, response.data.sid.sid))
161                         return False;
162         } else {
163                 sid_copy(sid, &global_sid_NULL);
164         }
165
166         return (result == NSS_STATUS_SUCCESS);
167 }
168
169 /* Call winbindd to convert SID to gid */
170
171 BOOL winbind_sid_to_gid(gid_t *pgid, const DOM_SID *sid)
172 {
173         struct winbindd_request request;
174         struct winbindd_response response;
175         int result;
176         fstring sid_str;
177
178         if (!pgid)
179                 return False;
180
181         /* Initialise request */
182
183         ZERO_STRUCT(request);
184         ZERO_STRUCT(response);
185
186         sid_to_string(sid_str, sid);
187         fstrcpy(request.data.sid, sid_str);
188         
189         /* Make request */
190
191         result = winbindd_request_response(WINBINDD_SID_TO_GID, &request, &response);
192
193         /* Copy out result */
194
195         if (result == NSS_STATUS_SUCCESS) {
196                 *pgid = response.data.gid;
197         }
198
199         return (result == NSS_STATUS_SUCCESS);
200 }
201
202 /* Call winbindd to convert gid to sid */
203
204 BOOL winbind_gid_to_sid(DOM_SID *sid, gid_t gid)
205 {
206         struct winbindd_request request;
207         struct winbindd_response response;
208         int result;
209
210         if (!sid)
211                 return False;
212
213         /* Initialise request */
214
215         ZERO_STRUCT(request);
216         ZERO_STRUCT(response);
217
218         request.data.gid = gid;
219
220         /* Make request */
221
222         result = winbindd_request_response(WINBINDD_GID_TO_SID, &request, &response);
223
224         /* Copy out result */
225
226         if (result == NSS_STATUS_SUCCESS) {
227                 if (!string_to_sid(sid, response.data.sid.sid))
228                         return False;
229         } else {
230                 sid_copy(sid, &global_sid_NULL);
231         }
232
233         return (result == NSS_STATUS_SUCCESS);
234 }
235
236 BOOL winbind_allocate_rid(uint32 *rid)
237 {
238         struct winbindd_request request;
239         struct winbindd_response response;
240         int result;
241
242         /* Initialise request */
243
244         ZERO_STRUCT(request);
245         ZERO_STRUCT(response);
246
247         /* Make request */
248
249         result = winbindd_request_response(WINBINDD_ALLOCATE_RID, &request, &response);
250
251         if (result != NSS_STATUS_SUCCESS)
252                 return False;
253
254         /* Copy out result */
255         *rid = response.data.rid;
256
257         return True;
258 }
259
260 BOOL winbind_allocate_rid_and_gid(uint32 *rid, gid_t *gid)
261 {
262         struct winbindd_request request;
263         struct winbindd_response response;
264         int result;
265
266         /* Initialise request */
267
268         ZERO_STRUCT(request);
269         ZERO_STRUCT(response);
270
271         /* Make request */
272
273         result = winbindd_request_response(WINBINDD_ALLOCATE_RID_AND_GID, &request,
274                                   &response);
275
276         if (result != NSS_STATUS_SUCCESS)
277                 return False;
278
279         /* Copy out result */
280         *rid = response.data.rid_and_gid.rid;
281         *gid = response.data.rid_and_gid.gid;
282
283         return True;
284 }
285
286 /* Fetch the list of groups a user is a member of from winbindd.  This is
287    used by winbind_getgroups. */
288
289 static int wb_getgroups(const char *user, gid_t **groups)
290 {
291         struct winbindd_request request;
292         struct winbindd_response response;
293         int result;
294
295         /* Call winbindd */
296
297         ZERO_STRUCT(request);
298         fstrcpy(request.data.username, user);
299
300         ZERO_STRUCT(response);
301
302         result = winbindd_request_response(WINBINDD_GETGROUPS, &request, &response);
303
304         if (result == NSS_STATUS_SUCCESS) {
305                 
306                 /* Return group list.  Don't forget to free the group list
307                    when finished. */
308
309                 *groups = (gid_t *)response.extra_data;
310                 return response.data.num_entries;
311         }
312
313         return -1;
314 }
315
316 /* Call winbindd to initialise group membership.  This is necessary for
317    some systems (i.e RH5.2) that do not have an initgroups function as part
318    of the nss extension.  In RH5.2 this is implemented using getgrent()
319    which can be amazingly inefficient as well as having problems with
320    username case. */
321
322 int winbind_initgroups(char *user, gid_t gid)
323 {
324         gid_t *tgr, *groups = NULL;
325         int result;
326
327         /* Call normal initgroups if we are a local user */
328
329         if (!strchr(user, *lp_winbind_separator())) {
330                 return initgroups(user, gid);
331         }
332
333         result = wb_getgroups(user, &groups);
334
335         DEBUG(10,("winbind_getgroups: %s: result = %s\n", user, 
336                   result == -1 ? "FAIL" : "SUCCESS"));
337
338         if (result != -1) {
339                 int ngroups = result, i;
340                 BOOL is_member = False;
341
342                 /* Check to see if the passed gid is already in the list */
343
344                 for (i = 0; i < ngroups; i++) {
345                         if (groups[i] == gid) {
346                                 is_member = True;
347                         }
348                 }
349
350                 /* Add group to list if necessary */
351
352                 if (!is_member) {
353                         tgr = SMB_REALLOC_ARRAY(groups, gid_t, ngroups + 1);
354                         
355                         if (!tgr) {
356                                 errno = ENOMEM;
357                                 result = -1;
358                                 goto done;
359                         }
360                         else groups = tgr;
361
362                         groups[ngroups] = gid;
363                         ngroups++;
364                 }
365
366                 /* Set the groups */
367
368                 if (sys_setgroups(ngroups, groups) == -1) {
369                         errno = EPERM;
370                         result = -1;
371                         goto done;
372                 }
373
374         } else {
375                 
376                 /* The call failed.  Set errno to something so we don't get
377                    a bogus value from the last failed system call. */
378
379                 errno = EIO;
380         }
381
382         /* Free response data if necessary */
383
384  done:
385         SAFE_FREE(groups);
386
387         return result;
388 }
389
390 /* Return a list of groups the user is a member of.  This function is
391    useful for large systems where inverting the group database would be too
392    time consuming.  If size is zero, list is not modified and the total
393    number of groups for the user is returned. */
394
395 int winbind_getgroups(const char *user, gid_t **list)
396 {
397         /*
398          * Don't do the lookup if the name has no separator _and_ we are not in
399          * 'winbind use default domain' mode.
400          */
401
402         if (!(strchr(user, *lp_winbind_separator()) || lp_winbind_use_default_domain()))
403                 return -1;
404
405         /* Fetch list of groups */
406
407         return wb_getgroups(user, list);
408 }
409
410 /**********************************************************************
411  simple wrapper function to see if winbindd is alive
412 **********************************************************************/
413
414 BOOL winbind_ping( void )
415 {
416         NSS_STATUS result;
417
418         result = winbindd_request_response(WINBINDD_PING, NULL, NULL);
419
420         return result == NSS_STATUS_SUCCESS;
421 }
422
423 /**********************************************************************
424  Is a domain trusted?
425
426  result == NSS_STATUS_UNAVAIL: winbind not around
427  result == NSS_STATUS_NOTFOUND: winbind around, but domain missing
428
429  Due to a bad API NSS_STATUS_NOTFOUND is returned both when winbind_off and
430  when winbind return WINBINDD_ERROR. So the semantics of this routine depends
431  on winbind_on. Grepping for winbind_off I just found 3 places where winbind
432  is turned off, and this does not conflict (as far as I have seen) with the
433  callers of is_trusted_domains.
434
435  I *hate* global variables....
436
437  Volker
438
439 **********************************************************************/
440
441 NSS_STATUS wb_is_trusted_domain(const char *domain)
442 {
443         struct winbindd_request request;
444         struct winbindd_response response;
445
446         /* Call winbindd */
447
448         ZERO_STRUCT(request);
449         ZERO_STRUCT(response);
450
451         fstrcpy(request.domain_name, domain);
452
453         return winbindd_request_response(WINBINDD_DOMAIN_INFO, &request, &response);
454 }