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