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