wbclient: gr_mem can be NULL
[gd/samba-autobuild/.git] / nsswitch / libwbclient / wbc_pwd.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    Winbind client API
5
6    Copyright (C) Gerald (Jerry) Carter 2007
7
8
9    This library is free software; you can redistribute it and/or
10    modify it under the terms of the GNU Lesser General Public
11    License as published by the Free Software Foundation; either
12    version 3 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 Lesser General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 /* Required Headers */
24
25 #include "replace.h"
26 #include "libwbclient.h"
27 #include "../winbind_client.h"
28
29 /** @brief The maximum number of pwent structs to get from winbindd
30  *
31  */
32 #define MAX_GETPWENT_USERS 500
33
34 /** @brief The maximum number of grent structs to get from winbindd
35  *
36  */
37 #define MAX_GETGRENT_GROUPS 500
38
39 /**
40  *
41  **/
42
43 static void wbcPasswdDestructor(void *ptr)
44 {
45         struct passwd *pw = (struct passwd *)ptr;
46         free(pw->pw_name);
47         free(pw->pw_passwd);
48         free(pw->pw_gecos);
49         free(pw->pw_shell);
50         free(pw->pw_dir);
51 }
52
53 static struct passwd *copy_passwd_entry(struct winbindd_pw *p)
54 {
55         struct passwd *pw = NULL;
56
57         pw = (struct passwd *)wbcAllocateMemory(1, sizeof(struct passwd),
58                                                 wbcPasswdDestructor);
59         if (pw == NULL) {
60                 return NULL;
61         }
62         pw->pw_name = strdup(p->pw_name);
63         if (pw->pw_name == NULL) {
64                 goto fail;
65         }
66         pw->pw_passwd = strdup(p->pw_passwd);
67         if (pw->pw_passwd == NULL) {
68                 goto fail;
69         }
70         pw->pw_gecos = strdup(p->pw_gecos);
71         if (pw->pw_gecos == NULL) {
72                 goto fail;
73         }
74         pw->pw_shell = strdup(p->pw_shell);
75         if (pw->pw_shell == NULL) {
76                 goto fail;
77         }
78         pw->pw_dir = strdup(p->pw_dir);
79         if (pw->pw_dir == NULL) {
80                 goto fail;
81         }
82         pw->pw_uid = p->pw_uid;
83         pw->pw_gid = p->pw_gid;
84         return pw;
85
86 fail:
87         wbcFreeMemory(pw);
88         return NULL;
89 }
90
91 /**
92  *
93  **/
94
95 static void wbcGroupDestructor(void *ptr)
96 {
97         struct group *gr = (struct group *)ptr;
98         int i;
99
100         free(gr->gr_name);
101         free(gr->gr_passwd);
102
103         /* if the array was partly created this can be NULL */
104         if (gr->gr_mem == NULL) {
105                 return;
106         }
107
108         for (i=0; gr->gr_mem[i] != NULL; i++) {
109                 free(gr->gr_mem[i]);
110         }
111         free(gr->gr_mem);
112 }
113
114 static struct group *copy_group_entry(struct winbindd_gr *g,
115                                       char *mem_buf)
116 {
117         struct group *gr = NULL;
118         int i;
119         char *mem_p, *mem_q;
120
121         gr = (struct group *)wbcAllocateMemory(
122                 1, sizeof(struct group), wbcGroupDestructor);
123         if (gr == NULL) {
124                 return NULL;
125         }
126
127         gr->gr_name = strdup(g->gr_name);
128         if (gr->gr_name == NULL) {
129                 goto fail;
130         }
131         gr->gr_passwd = strdup(g->gr_passwd);
132         if (gr->gr_passwd == NULL) {
133                 goto fail;
134         }
135         gr->gr_gid = g->gr_gid;
136
137         gr->gr_mem = (char **)calloc(g->num_gr_mem+1, sizeof(char *));
138         if (gr->gr_mem == NULL) {
139                 goto fail;
140         }
141
142         mem_p = mem_q = mem_buf;
143         for (i=0; i<g->num_gr_mem && mem_p; i++) {
144                 mem_q = strchr(mem_p, ',');
145                 if (mem_q != NULL) {
146                         *mem_q = '\0';
147                 }
148
149                 gr->gr_mem[i] = strdup(mem_p);
150                 if (gr->gr_mem[i] == NULL) {
151                         goto fail;
152                 }
153
154                 if (mem_q == NULL) {
155                         i += 1;
156                         break;
157                 }
158                 mem_p = mem_q + 1;
159         }
160         gr->gr_mem[i] = NULL;
161
162         return gr;
163
164 fail:
165         wbcFreeMemory(gr);
166         return NULL;
167 }
168
169 /* Fill in a struct passwd* for a domain user based on username */
170 wbcErr wbcGetpwnam(const char *name, struct passwd **pwd)
171 {
172         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
173         struct winbindd_request request;
174         struct winbindd_response response;
175
176         if (!name || !pwd) {
177                 wbc_status = WBC_ERR_INVALID_PARAM;
178                 BAIL_ON_WBC_ERROR(wbc_status);
179         }
180
181         /* Initialize request */
182
183         ZERO_STRUCT(request);
184         ZERO_STRUCT(response);
185
186         /* dst is already null terminated from the memset above */
187
188         strncpy(request.data.username, name, sizeof(request.data.username)-1);
189
190         wbc_status = wbcRequestResponse(WINBINDD_GETPWNAM,
191                                         &request,
192                                         &response);
193         BAIL_ON_WBC_ERROR(wbc_status);
194
195         *pwd = copy_passwd_entry(&response.data.pw);
196         BAIL_ON_PTR_ERROR(*pwd, wbc_status);
197
198  done:
199         return wbc_status;
200 }
201
202 /* Fill in a struct passwd* for a domain user based on uid */
203 wbcErr wbcGetpwuid(uid_t uid, struct passwd **pwd)
204 {
205         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
206         struct winbindd_request request;
207         struct winbindd_response response;
208
209         if (!pwd) {
210                 wbc_status = WBC_ERR_INVALID_PARAM;
211                 BAIL_ON_WBC_ERROR(wbc_status);
212         }
213
214         /* Initialize request */
215
216         ZERO_STRUCT(request);
217         ZERO_STRUCT(response);
218
219         request.data.uid = uid;
220
221         wbc_status = wbcRequestResponse(WINBINDD_GETPWUID,
222                                         &request,
223                                         &response);
224         BAIL_ON_WBC_ERROR(wbc_status);
225
226         *pwd = copy_passwd_entry(&response.data.pw);
227         BAIL_ON_PTR_ERROR(*pwd, wbc_status);
228
229  done:
230         return wbc_status;
231 }
232
233 /* Fill in a struct passwd* for a domain user based on sid */
234 wbcErr wbcGetpwsid(struct wbcDomainSid *sid, struct passwd **pwd)
235 {
236         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
237         struct winbindd_request request;
238         struct winbindd_response response;
239         char * sid_string = NULL;
240
241         if (!pwd) {
242                 wbc_status = WBC_ERR_INVALID_PARAM;
243                 BAIL_ON_WBC_ERROR(wbc_status);
244         }
245
246         wbc_status = wbcSidToString(sid, &sid_string);
247         BAIL_ON_WBC_ERROR(wbc_status);
248
249         /* Initialize request */
250
251         ZERO_STRUCT(request);
252         ZERO_STRUCT(response);
253
254         strncpy(request.data.sid, sid_string, sizeof(request.data.sid));
255
256         wbc_status = wbcRequestResponse(WINBINDD_GETPWSID,
257                                         &request,
258                                         &response);
259         BAIL_ON_WBC_ERROR(wbc_status);
260
261         *pwd = copy_passwd_entry(&response.data.pw);
262         BAIL_ON_PTR_ERROR(*pwd, wbc_status);
263
264  done:
265         wbcFreeMemory(sid_string);
266         return wbc_status;
267 }
268
269 /* Fill in a struct passwd* for a domain user based on username */
270 wbcErr wbcGetgrnam(const char *name, struct group **grp)
271 {
272         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
273         struct winbindd_request request;
274         struct winbindd_response response;
275
276         /* Initialize request */
277
278         ZERO_STRUCT(request);
279         ZERO_STRUCT(response);
280
281         if (!name || !grp) {
282                 wbc_status = WBC_ERR_INVALID_PARAM;
283                 BAIL_ON_WBC_ERROR(wbc_status);
284         }
285
286         /* dst is already null terminated from the memset above */
287
288         strncpy(request.data.groupname, name, sizeof(request.data.groupname)-1);
289
290         wbc_status = wbcRequestResponse(WINBINDD_GETGRNAM,
291                                         &request,
292                                         &response);
293         BAIL_ON_WBC_ERROR(wbc_status);
294
295         *grp = copy_group_entry(&response.data.gr,
296                                 (char*)response.extra_data.data);
297         BAIL_ON_PTR_ERROR(*grp, wbc_status);
298
299  done:
300         winbindd_free_response(&response);
301
302         return wbc_status;
303 }
304
305 /* Fill in a struct passwd* for a domain user based on uid */
306 wbcErr wbcGetgrgid(gid_t gid, struct group **grp)
307 {
308         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
309         struct winbindd_request request;
310         struct winbindd_response response;
311
312         /* Initialize request */
313
314         ZERO_STRUCT(request);
315         ZERO_STRUCT(response);
316
317         if (!grp) {
318                 wbc_status = WBC_ERR_INVALID_PARAM;
319                 BAIL_ON_WBC_ERROR(wbc_status);
320         }
321
322         request.data.gid = gid;
323
324         wbc_status = wbcRequestResponse(WINBINDD_GETGRGID,
325                                         &request,
326                                         &response);
327         BAIL_ON_WBC_ERROR(wbc_status);
328
329         *grp = copy_group_entry(&response.data.gr,
330                                 (char*)response.extra_data.data);
331         BAIL_ON_PTR_ERROR(*grp, wbc_status);
332
333  done:
334         winbindd_free_response(&response);
335
336         return wbc_status;
337 }
338
339 /** @brief Number of cached passwd structs
340  *
341  */
342 static uint32_t pw_cache_size;
343
344 /** @brief Position of the pwent context
345  *
346  */
347 static uint32_t pw_cache_idx;
348
349 /** @brief Winbindd response containing the passwd structs
350  *
351  */
352 static struct winbindd_response pw_response;
353
354 /* Reset the passwd iterator */
355 wbcErr wbcSetpwent(void)
356 {
357         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
358
359         if (pw_cache_size > 0) {
360                 pw_cache_idx = pw_cache_size = 0;
361                 winbindd_free_response(&pw_response);
362         }
363
364         ZERO_STRUCT(pw_response);
365
366         wbc_status = wbcRequestResponse(WINBINDD_SETPWENT,
367                                         NULL, NULL);
368         BAIL_ON_WBC_ERROR(wbc_status);
369
370  done:
371         return wbc_status;
372 }
373
374 /* Close the passwd iterator */
375 wbcErr wbcEndpwent(void)
376 {
377         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
378
379         if (pw_cache_size > 0) {
380                 pw_cache_idx = pw_cache_size = 0;
381                 winbindd_free_response(&pw_response);
382         }
383
384         wbc_status = wbcRequestResponse(WINBINDD_ENDPWENT,
385                                         NULL, NULL);
386         BAIL_ON_WBC_ERROR(wbc_status);
387
388  done:
389         return wbc_status;
390 }
391
392 /* Return the next struct passwd* entry from the pwent iterator */
393 wbcErr wbcGetpwent(struct passwd **pwd)
394 {
395         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
396         struct winbindd_request request;
397         struct winbindd_pw *wb_pw;
398
399         /* If there's a cached result, return that. */
400         if (pw_cache_idx < pw_cache_size) {
401                 goto return_result;
402         }
403
404         /* Otherwise, query winbindd for some entries. */
405
406         pw_cache_idx = 0;
407
408         winbindd_free_response(&pw_response);
409
410         ZERO_STRUCT(request);
411         request.data.num_entries = MAX_GETPWENT_USERS;
412
413         wbc_status = wbcRequestResponse(WINBINDD_GETPWENT, &request,
414                                         &pw_response);
415
416         BAIL_ON_WBC_ERROR(wbc_status);
417
418         pw_cache_size = pw_response.data.num_entries;
419
420 return_result:
421
422         wb_pw = (struct winbindd_pw *) pw_response.extra_data.data;
423
424         *pwd = copy_passwd_entry(&wb_pw[pw_cache_idx]);
425
426         BAIL_ON_PTR_ERROR(*pwd, wbc_status);
427
428         pw_cache_idx++;
429
430 done:
431         return wbc_status;
432 }
433
434 /** @brief Number of cached group structs
435  *
436  */
437 static uint32_t gr_cache_size;
438
439 /** @brief Position of the grent context
440  *
441  */
442 static uint32_t gr_cache_idx;
443
444 /** @brief Winbindd response containing the group structs
445  *
446  */
447 static struct winbindd_response gr_response;
448
449 /* Reset the group iterator */
450 wbcErr wbcSetgrent(void)
451 {
452         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
453
454         if (gr_cache_size > 0) {
455                 gr_cache_idx = gr_cache_size = 0;
456                 winbindd_free_response(&gr_response);
457         }
458
459         ZERO_STRUCT(gr_response);
460
461         wbc_status = wbcRequestResponse(WINBINDD_SETGRENT,
462                                         NULL, NULL);
463         BAIL_ON_WBC_ERROR(wbc_status);
464
465  done:
466         return wbc_status;
467 }
468
469 /* Close the group iterator */
470 wbcErr wbcEndgrent(void)
471 {
472         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
473
474         if (gr_cache_size > 0) {
475                 gr_cache_idx = gr_cache_size = 0;
476                 winbindd_free_response(&gr_response);
477         }
478
479         wbc_status = wbcRequestResponse(WINBINDD_ENDGRENT,
480                                         NULL, NULL);
481         BAIL_ON_WBC_ERROR(wbc_status);
482
483  done:
484         return wbc_status;
485 }
486
487 /* Return the next struct group* entry from the pwent iterator */
488 wbcErr wbcGetgrent(struct group **grp)
489 {
490         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
491         struct winbindd_request request;
492         struct winbindd_gr *wb_gr;
493         uint32_t mem_ofs;
494
495         /* If there's a cached result, return that. */
496         if (gr_cache_idx < gr_cache_size) {
497                 goto return_result;
498         }
499
500         /* Otherwise, query winbindd for some entries. */
501
502         gr_cache_idx = 0;
503
504         winbindd_free_response(&gr_response);
505
506         ZERO_STRUCT(request);
507         request.data.num_entries = MAX_GETGRENT_GROUPS;
508
509         wbc_status = wbcRequestResponse(WINBINDD_GETGRENT, &request,
510                                         &gr_response);
511
512         BAIL_ON_WBC_ERROR(wbc_status);
513
514         gr_cache_size = gr_response.data.num_entries;
515
516 return_result:
517
518         wb_gr = (struct winbindd_gr *) gr_response.extra_data.data;
519
520         mem_ofs = wb_gr[gr_cache_idx].gr_mem_ofs +
521                   gr_cache_size * sizeof(struct winbindd_gr);
522
523         *grp = copy_group_entry(&wb_gr[gr_cache_idx],
524                                 ((char *)gr_response.extra_data.data)+mem_ofs);
525
526         BAIL_ON_PTR_ERROR(*grp, wbc_status);
527
528         gr_cache_idx++;
529
530 done:
531         return wbc_status;
532 }
533
534 /* Return the next struct group* entry from the pwent iterator */
535 wbcErr wbcGetgrlist(struct group **grp)
536 {
537         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
538         struct winbindd_request request;
539         struct winbindd_gr *wb_gr;
540
541         /* If there's a cached result, return that. */
542         if (gr_cache_idx < gr_cache_size) {
543                 goto return_result;
544         }
545
546         /* Otherwise, query winbindd for some entries. */
547
548         gr_cache_idx = 0;
549
550         winbindd_free_response(&gr_response);
551         ZERO_STRUCT(gr_response);
552
553         ZERO_STRUCT(request);
554         request.data.num_entries = MAX_GETGRENT_GROUPS;
555
556         wbc_status = wbcRequestResponse(WINBINDD_GETGRLST, &request,
557                                         &gr_response);
558
559         BAIL_ON_WBC_ERROR(wbc_status);
560
561         gr_cache_size = gr_response.data.num_entries;
562
563 return_result:
564
565         wb_gr = (struct winbindd_gr *) gr_response.extra_data.data;
566
567         *grp = copy_group_entry(&wb_gr[gr_cache_idx], NULL);
568
569         BAIL_ON_PTR_ERROR(*grp, wbc_status);
570
571         gr_cache_idx++;
572
573 done:
574         return wbc_status;
575 }
576
577 /* Return the unix group array belonging to the given user */
578 wbcErr wbcGetGroups(const char *account,
579                     uint32_t *num_groups,
580                     gid_t **_groups)
581 {
582         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
583         struct winbindd_request request;
584         struct winbindd_response response;
585         uint32_t i;
586         gid_t *groups = NULL;
587
588         /* Initialize request */
589
590         ZERO_STRUCT(request);
591         ZERO_STRUCT(response);
592
593         if (!account) {
594                 wbc_status = WBC_ERR_INVALID_PARAM;
595                 BAIL_ON_WBC_ERROR(wbc_status);
596         }
597
598         /* Send request */
599
600         strncpy(request.data.username, account, sizeof(request.data.username)-1);
601
602         wbc_status = wbcRequestResponse(WINBINDD_GETGROUPS,
603                                         &request,
604                                         &response);
605         BAIL_ON_WBC_ERROR(wbc_status);
606
607         groups = (gid_t *)wbcAllocateMemory(
608                 sizeof(gid_t), response.data.num_entries, NULL);
609         BAIL_ON_PTR_ERROR(groups, wbc_status);
610
611         for (i = 0; i < response.data.num_entries; i++) {
612                 groups[i] = ((gid_t *)response.extra_data.data)[i];
613         }
614
615         *num_groups = response.data.num_entries;
616         *_groups = groups;
617         groups = NULL;
618
619         wbc_status = WBC_ERR_SUCCESS;
620
621  done:
622         winbindd_free_response(&response);
623         wbcFreeMemory(groups);
624         return wbc_status;
625 }