r16945: Sync trunk -> 3.0 for 3.0.24 code. Still need
[vlendec/samba-autobuild/.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 BOOL winbind_lookup_rids(TALLOC_CTX *mem_ctx,
117                          const DOM_SID *domain_sid,
118                          int num_rids, uint32 *rids,
119                          const char **domain_name,
120                          const char ***names, enum SID_NAME_USE **types)
121 {
122         size_t i, buflen;
123         ssize_t len;
124         char *ridlist;
125         char *p;
126         struct winbindd_request request;
127         struct winbindd_response response;
128         NSS_STATUS result;
129
130         if (num_rids == 0) {
131                 return False;
132         }
133
134         /* Initialise request */
135
136         ZERO_STRUCT(request);
137         ZERO_STRUCT(response);
138
139         fstrcpy(request.data.sid, sid_string_static(domain_sid));
140         
141         len = 0;
142         buflen = 0;
143         ridlist = NULL;
144
145         for (i=0; i<num_rids; i++) {
146                 sprintf_append(mem_ctx, &ridlist, &len, &buflen,
147                                "%ld\n", rids[i]);
148         }
149
150         if ((num_rids != 0) && (ridlist == NULL)) {
151                 return False;
152         }
153
154         request.extra_data.data = ridlist;
155         request.extra_len = strlen(ridlist)+1;
156
157         result = winbindd_request_response(WINBINDD_LOOKUPRIDS,
158                                            &request, &response);
159
160         TALLOC_FREE(ridlist);
161
162         if (result != NSS_STATUS_SUCCESS) {
163                 return False;
164         }
165
166         *domain_name = talloc_strdup(mem_ctx, response.data.domain_name);
167
168         *names = TALLOC_ARRAY(mem_ctx, const char *, num_rids);
169         *types = TALLOC_ARRAY(mem_ctx, enum SID_NAME_USE, num_rids);
170
171         if ((*names == NULL) || (*types == NULL)) {
172                 goto fail;
173         }
174
175         p = response.extra_data.data;
176
177         for (i=0; i<num_rids; i++) {
178                 char *q;
179
180                 if (*p == '\0') {
181                         DEBUG(10, ("Got invalid reply: %s\n",
182                                    (char *)response.extra_data.data));
183                         goto fail;
184                 }
185                         
186                 (*types)[i] = (enum SID_NAME_USE)strtoul(p, &q, 10);
187
188                 if (*q != ' ') {
189                         DEBUG(10, ("Got invalid reply: %s\n",
190                                    (char *)response.extra_data.data));
191                         goto fail;
192                 }
193
194                 p = q+1;
195
196                 q = strchr(p, '\n');
197                 if (q == NULL) {
198                         DEBUG(10, ("Got invalid reply: %s\n",
199                                    (char *)response.extra_data.data));
200                         goto fail;
201                 }
202
203                 *q = '\0';
204
205                 (*names)[i] = talloc_strdup(*names, p);
206
207                 p = q+1;
208         }
209
210         if (*p != '\0') {
211                 DEBUG(10, ("Got invalid reply: %s\n",
212                            (char *)response.extra_data.data));
213                 goto fail;
214         }
215
216         SAFE_FREE(response.extra_data.data);
217
218         return True;
219
220  fail:
221         TALLOC_FREE(*names);
222         TALLOC_FREE(*types);
223         return False;
224 }
225
226 /* Call winbindd to convert SID to uid */
227
228 BOOL winbind_sid_to_uid(uid_t *puid, const DOM_SID *sid)
229 {
230         struct winbindd_request request;
231         struct winbindd_response response;
232         int result;
233         fstring sid_str;
234
235         if (!puid)
236                 return False;
237
238         /* Initialise request */
239
240         ZERO_STRUCT(request);
241         ZERO_STRUCT(response);
242
243         sid_to_string(sid_str, sid);
244         fstrcpy(request.data.sid, sid_str);
245         
246         /* Make request */
247
248         result = winbindd_request_response(WINBINDD_SID_TO_UID, &request, &response);
249
250         /* Copy out result */
251
252         if (result == NSS_STATUS_SUCCESS) {
253                 *puid = response.data.uid;
254         }
255
256         return (result == NSS_STATUS_SUCCESS);
257 }
258
259 /* Call winbindd to convert uid to sid */
260
261 BOOL winbind_uid_to_sid(DOM_SID *sid, uid_t uid)
262 {
263         struct winbindd_request request;
264         struct winbindd_response response;
265         int result;
266
267         if (!sid)
268                 return False;
269
270         /* Initialise request */
271
272         ZERO_STRUCT(request);
273         ZERO_STRUCT(response);
274
275         request.data.uid = uid;
276
277         /* Make request */
278
279         result = winbindd_request_response(WINBINDD_UID_TO_SID, &request, &response);
280
281         /* Copy out result */
282
283         if (result == NSS_STATUS_SUCCESS) {
284                 if (!string_to_sid(sid, response.data.sid.sid))
285                         return False;
286         } else {
287                 sid_copy(sid, &global_sid_NULL);
288         }
289
290         return (result == NSS_STATUS_SUCCESS);
291 }
292
293 /* Call winbindd to convert SID to gid */
294
295 BOOL winbind_sid_to_gid(gid_t *pgid, const DOM_SID *sid)
296 {
297         struct winbindd_request request;
298         struct winbindd_response response;
299         int result;
300         fstring sid_str;
301
302         if (!pgid)
303                 return False;
304
305         /* Initialise request */
306
307         ZERO_STRUCT(request);
308         ZERO_STRUCT(response);
309
310         sid_to_string(sid_str, sid);
311         fstrcpy(request.data.sid, sid_str);
312         
313         /* Make request */
314
315         result = winbindd_request_response(WINBINDD_SID_TO_GID, &request, &response);
316
317         /* Copy out result */
318
319         if (result == NSS_STATUS_SUCCESS) {
320                 *pgid = response.data.gid;
321         }
322
323         return (result == NSS_STATUS_SUCCESS);
324 }
325
326 /* Call winbindd to convert gid to sid */
327
328 BOOL winbind_gid_to_sid(DOM_SID *sid, gid_t gid)
329 {
330         struct winbindd_request request;
331         struct winbindd_response response;
332         int result;
333
334         if (!sid)
335                 return False;
336
337         /* Initialise request */
338
339         ZERO_STRUCT(request);
340         ZERO_STRUCT(response);
341
342         request.data.gid = gid;
343
344         /* Make request */
345
346         result = winbindd_request_response(WINBINDD_GID_TO_SID, &request, &response);
347
348         /* Copy out result */
349
350         if (result == NSS_STATUS_SUCCESS) {
351                 if (!string_to_sid(sid, response.data.sid.sid))
352                         return False;
353         } else {
354                 sid_copy(sid, &global_sid_NULL);
355         }
356
357         return (result == NSS_STATUS_SUCCESS);
358 }
359
360 BOOL winbind_allocate_uid(uid_t *uid)
361 {
362         struct winbindd_request request;
363         struct winbindd_response response;
364         int result;
365
366         /* Initialise request */
367
368         ZERO_STRUCT(request);
369         ZERO_STRUCT(response);
370
371         /* Make request */
372
373         result = winbindd_request_response(WINBINDD_ALLOCATE_UID,
374                                            &request, &response);
375
376         if (result != NSS_STATUS_SUCCESS)
377                 return False;
378
379         /* Copy out result */
380         *uid = response.data.uid;
381
382         return True;
383 }
384
385 BOOL winbind_allocate_gid(gid_t *gid)
386 {
387         struct winbindd_request request;
388         struct winbindd_response response;
389         int result;
390
391         /* Initialise request */
392
393         ZERO_STRUCT(request);
394         ZERO_STRUCT(response);
395
396         /* Make request */
397
398         result = winbindd_request_response(WINBINDD_ALLOCATE_GID,
399                                            &request, &response);
400
401         if (result != NSS_STATUS_SUCCESS)
402                 return False;
403
404         /* Copy out result */
405         *gid = response.data.gid;
406
407         return True;
408 }
409
410 /* Fetch the list of groups a user is a member of from winbindd.  This is
411    used by winbind_getgroups. */
412
413 static int wb_getgroups(const char *user, gid_t **groups)
414 {
415         struct winbindd_request request;
416         struct winbindd_response response;
417         int result;
418
419         /* Call winbindd */
420
421         ZERO_STRUCT(request);
422         fstrcpy(request.data.username, user);
423
424         ZERO_STRUCT(response);
425
426         result = winbindd_request_response(WINBINDD_GETGROUPS, &request, &response);
427
428         if (result == NSS_STATUS_SUCCESS) {
429                 
430                 /* Return group list.  Don't forget to free the group list
431                    when finished. */
432
433                 *groups = (gid_t *)response.extra_data.data;
434                 return response.data.num_entries;
435         }
436
437         return -1;
438 }
439
440 /* Call winbindd to initialise group membership.  This is necessary for
441    some systems (i.e RH5.2) that do not have an initgroups function as part
442    of the nss extension.  In RH5.2 this is implemented using getgrent()
443    which can be amazingly inefficient as well as having problems with
444    username case. */
445
446 int winbind_initgroups(char *user, gid_t gid)
447 {
448         gid_t *groups = NULL;
449         int result;
450
451         /* Call normal initgroups if we are a local user */
452
453         if (!strchr(user, *lp_winbind_separator())) {
454                 return initgroups(user, gid);
455         }
456
457         result = wb_getgroups(user, &groups);
458
459         DEBUG(10,("winbind_getgroups: %s: result = %s\n", user, 
460                   result == -1 ? "FAIL" : "SUCCESS"));
461
462         if (result != -1) {
463                 int ngroups = result, i;
464                 BOOL is_member = False;
465
466                 /* Check to see if the passed gid is already in the list */
467
468                 for (i = 0; i < ngroups; i++) {
469                         if (groups[i] == gid) {
470                                 is_member = True;
471                         }
472                 }
473
474                 /* Add group to list if necessary */
475
476                 if (!is_member) {
477                         groups = SMB_REALLOC_ARRAY(groups, gid_t, ngroups + 1);
478                         if (!groups) {
479                                 errno = ENOMEM;
480                                 result = -1;
481                                 goto done;
482                         }
483
484                         groups[ngroups] = gid;
485                         ngroups++;
486                 }
487
488                 /* Set the groups */
489
490                 if (sys_setgroups(ngroups, groups) == -1) {
491                         errno = EPERM;
492                         result = -1;
493                         goto done;
494                 }
495
496         } else {
497                 
498                 /* The call failed.  Set errno to something so we don't get
499                    a bogus value from the last failed system call. */
500
501                 errno = EIO;
502         }
503
504         /* Free response data if necessary */
505
506  done:
507         SAFE_FREE(groups);
508
509         return result;
510 }
511
512 /* Return a list of groups the user is a member of.  This function is
513    useful for large systems where inverting the group database would be too
514    time consuming.  If size is zero, list is not modified and the total
515    number of groups for the user is returned. */
516
517 int winbind_getgroups(const char *user, gid_t **list)
518 {
519         /*
520          * Don't do the lookup if the name has no separator _and_ we are not in
521          * 'winbind use default domain' mode.
522          */
523
524         if (!(strchr(user, *lp_winbind_separator()) || lp_winbind_use_default_domain()))
525                 return -1;
526
527         /* Fetch list of groups */
528
529         return wb_getgroups(user, list);
530 }
531
532 /**********************************************************************
533  simple wrapper function to see if winbindd is alive
534 **********************************************************************/
535
536 BOOL winbind_ping( void )
537 {
538         NSS_STATUS result;
539
540         result = winbindd_request_response(WINBINDD_PING, NULL, NULL);
541
542         return result == NSS_STATUS_SUCCESS;
543 }
544
545 /**********************************************************************
546  Is a domain trusted?
547
548  result == NSS_STATUS_UNAVAIL: winbind not around
549  result == NSS_STATUS_NOTFOUND: winbind around, but domain missing
550
551  Due to a bad API NSS_STATUS_NOTFOUND is returned both when winbind_off and
552  when winbind return WINBINDD_ERROR. So the semantics of this routine depends
553  on winbind_on. Grepping for winbind_off I just found 3 places where winbind
554  is turned off, and this does not conflict (as far as I have seen) with the
555  callers of is_trusted_domains.
556
557  I *hate* global variables....
558
559  Volker
560
561 **********************************************************************/
562
563 NSS_STATUS wb_is_trusted_domain(const char *domain)
564 {
565         struct winbindd_request request;
566         struct winbindd_response response;
567
568         /* Call winbindd */
569
570         ZERO_STRUCT(request);
571         ZERO_STRUCT(response);
572
573         fstrcpy(request.domain_name, domain);
574
575         return winbindd_request_response(WINBINDD_DOMAIN_INFO, &request, &response);
576 }