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