Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : Winbind client API - SSSD version
5 :
6 : Copyright (C) Sumit Bose <sbose@redhat.com> 2014
7 :
8 : This library is free software; you can redistribute it and/or
9 : modify it under the terms of the GNU Lesser General Public
10 : License as published by the Free Software Foundation; either
11 : version 3 of the License, or (at your option) any later version.
12 :
13 : This library is distributed in the hope that it will be useful,
14 : but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 : Library General Public License for more details.
17 :
18 : You should have received a copy of the GNU Lesser General Public License
19 : along with this program. If not, see <http://www.gnu.org/licenses/>.
20 : */
21 : /* Required Headers */
22 :
23 : #include <nss.h>
24 : #include <dlfcn.h>
25 : #include <errno.h>
26 :
27 : #include "libwbclient.h"
28 : #include "wbc_sssd_internal.h"
29 :
30 : #define DEFAULT_BUFSIZE_HALF 2048
31 : #define DEFAULT_BUFSIZE (2 * DEFAULT_BUFSIZE_HALF)
32 : #define MAX_BUFSIZE (1024*1204)
33 :
34 : struct nss_ops_ctx {
35 : void *dl_handle;
36 :
37 : enum nss_status (*getpwnam_r)(const char *name, struct passwd *result,
38 : char *buffer, size_t buflen, int *errnop);
39 : enum nss_status (*getpwuid_r)(uid_t uid, struct passwd *result,
40 : char *buffer, size_t buflen, int *errnop);
41 : enum nss_status (*setpwent)(void);
42 : enum nss_status (*getpwent_r)(struct passwd *result,
43 : char *buffer, size_t buflen, int *errnop);
44 : enum nss_status (*endpwent)(void);
45 :
46 : enum nss_status (*getgrnam_r)(const char *name, struct group *result,
47 : char *buffer, size_t buflen, int *errnop);
48 : enum nss_status (*getgrgid_r)(gid_t gid, struct group *result,
49 : char *buffer, size_t buflen, int *errnop);
50 : enum nss_status (*setgrent)(void);
51 : enum nss_status (*getgrent_r)(struct group *result,
52 : char *buffer, size_t buflen, int *errnop);
53 : enum nss_status (*endgrent)(void);
54 :
55 : enum nss_status (*initgroups_dyn)(const char *user, gid_t group,
56 : long int *start, long int *size,
57 : gid_t **groups, long int limit,
58 : int *errnop);
59 : };
60 :
61 : struct nss_ops_ctx *ctx = NULL;
62 :
63 0 : static bool open_libnss_sss(void)
64 : {
65 0 : ctx = calloc(1, sizeof(struct nss_ops_ctx));
66 0 : if (ctx == NULL) {
67 0 : return false;
68 : }
69 :
70 0 : ctx->dl_handle = dlopen("libnss_sss.so.2", RTLD_NOW);
71 0 : if (ctx->dl_handle == NULL) {
72 0 : goto fail;
73 : }
74 :
75 0 : ctx->getpwnam_r = dlsym(ctx->dl_handle, "_nss_sss_getpwnam_r");
76 0 : if (ctx->getpwnam_r == NULL) {
77 0 : goto fail;
78 : }
79 :
80 0 : ctx->getpwuid_r = dlsym(ctx->dl_handle, "_nss_sss_getpwuid_r");
81 0 : if (ctx->getpwuid_r == NULL) {
82 0 : goto fail;
83 : }
84 :
85 0 : ctx->setpwent = dlsym(ctx->dl_handle, "_nss_sss_setpwent");
86 0 : if (ctx->setpwent == NULL) {
87 0 : goto fail;
88 : }
89 :
90 0 : ctx->getpwent_r = dlsym(ctx->dl_handle, "_nss_sss_getpwent_r");
91 0 : if (ctx->getpwent_r == NULL) {
92 0 : goto fail;
93 : }
94 :
95 0 : ctx->endpwent = dlsym(ctx->dl_handle, "_nss_sss_endpwent");
96 0 : if (ctx->endpwent == NULL) {
97 0 : goto fail;
98 : }
99 :
100 0 : ctx->getgrnam_r = dlsym(ctx->dl_handle, "_nss_sss_getgrnam_r");
101 0 : if (ctx->getgrnam_r == NULL) {
102 0 : goto fail;
103 : }
104 :
105 0 : ctx->getgrgid_r = dlsym(ctx->dl_handle, "_nss_sss_getgrgid_r");
106 0 : if (ctx->getgrgid_r == NULL) {
107 0 : goto fail;
108 : }
109 :
110 0 : ctx->setgrent = dlsym(ctx->dl_handle, "_nss_sss_setgrent");
111 0 : if (ctx->setgrent == NULL) {
112 0 : goto fail;
113 : }
114 :
115 0 : ctx->getgrent_r = dlsym(ctx->dl_handle, "_nss_sss_getgrent_r");
116 0 : if (ctx->getgrent_r == NULL) {
117 0 : goto fail;
118 : }
119 :
120 0 : ctx->endgrent = dlsym(ctx->dl_handle, "_nss_sss_endgrent");
121 0 : if (ctx->endgrent == NULL) {
122 0 : goto fail;
123 : }
124 :
125 0 : ctx->initgroups_dyn = dlsym(ctx->dl_handle, "_nss_sss_initgroups_dyn");
126 0 : if (ctx->initgroups_dyn == NULL) {
127 0 : goto fail;
128 : }
129 :
130 0 : return true;
131 :
132 : fail:
133 0 : if (ctx->dl_handle != NULL) {
134 0 : dlclose(ctx->dl_handle);
135 : }
136 :
137 0 : free(ctx);
138 0 : ctx = NULL;
139 :
140 0 : return false;
141 : }
142 :
143 0 : static void wbcPasswdDestructor(void *ptr)
144 : {
145 0 : struct passwd *pw = (struct passwd *)ptr;
146 0 : free(pw->pw_name);
147 0 : free(pw->pw_passwd);
148 0 : free(pw->pw_gecos);
149 0 : free(pw->pw_shell);
150 0 : free(pw->pw_dir);
151 0 : }
152 :
153 0 : static wbcErr copy_pwd(struct passwd *in, struct passwd **out)
154 : {
155 : struct passwd *pw;
156 :
157 0 : pw = (struct passwd *)wbcAllocateMemory(1, sizeof(struct passwd),
158 : wbcPasswdDestructor);
159 0 : if (pw == NULL) {
160 0 : return WBC_ERR_NO_MEMORY;
161 : }
162 :
163 0 : pw->pw_name = strdup(in->pw_name);
164 0 : if (pw->pw_name == NULL) {
165 0 : goto fail;
166 : }
167 :
168 0 : pw->pw_passwd = strdup(in->pw_passwd);
169 0 : if (pw->pw_passwd == NULL) {
170 0 : goto fail;
171 : }
172 :
173 0 : pw->pw_uid = in->pw_uid;
174 0 : pw->pw_gid = in->pw_gid;
175 :
176 0 : pw->pw_gecos = strdup(in->pw_gecos);
177 0 : if (pw->pw_gecos == NULL) {
178 0 : goto fail;
179 : }
180 :
181 0 : pw->pw_shell = strdup(in->pw_shell);
182 0 : if (pw->pw_shell == NULL) {
183 0 : goto fail;
184 : }
185 :
186 0 : pw->pw_dir = strdup(in->pw_dir);
187 0 : if (pw->pw_dir == NULL) {
188 0 : goto fail;
189 : }
190 :
191 0 : *out = pw;
192 0 : return WBC_ERR_SUCCESS;
193 : fail:
194 0 : wbcFreeMemory(pw);
195 :
196 0 : return WBC_ERR_NO_MEMORY;
197 : }
198 :
199 0 : static wbcErr nss_to_wbc(enum nss_status status)
200 : {
201 : wbcErr wbc_status;
202 :
203 0 : switch (status) {
204 : case NSS_STATUS_SUCCESS:
205 0 : wbc_status = WBC_ERR_SUCCESS;
206 0 : break;
207 : case NSS_STATUS_NOTFOUND:
208 0 : wbc_status = WBC_ERR_UNKNOWN_USER;
209 0 : break;
210 : case NSS_STATUS_UNAVAIL:
211 0 : wbc_status = WBC_ERR_WINBIND_NOT_AVAILABLE;
212 0 : break;
213 : default:
214 0 : wbc_status = WBC_ERR_UNKNOWN_FAILURE;
215 : }
216 :
217 0 : return wbc_status;
218 : }
219 :
220 : /* Fill in a struct passwd* for a domain user based on username */
221 0 : wbcErr wbcGetpwnam(const char *name, struct passwd **pwd)
222 : {
223 0 : struct passwd lpwd = {0};
224 : enum nss_status status;
225 0 : char *buffer = NULL;
226 : size_t buflen;
227 : wbcErr wbc_status;
228 : int nss_errno;
229 :
230 0 : if (ctx == NULL && !open_libnss_sss()) {
231 0 : return WBC_ERR_NSS_ERROR;
232 : }
233 :
234 0 : if (name == NULL || pwd == NULL) {
235 0 : return WBC_ERR_INVALID_PARAM;
236 : }
237 :
238 0 : buflen = DEFAULT_BUFSIZE;
239 0 : buffer = malloc(buflen);
240 0 : if (buffer == NULL) {
241 0 : return WBC_ERR_NO_MEMORY;
242 : }
243 :
244 0 : status = ctx->getpwnam_r(name, &lpwd, buffer, buflen, &nss_errno);
245 0 : wbc_status = nss_to_wbc(status);
246 0 : if (WBC_ERROR_IS_OK(wbc_status) == true) {
247 0 : wbc_status = copy_pwd(&lpwd, pwd);
248 : }
249 :
250 0 : free(buffer);
251 :
252 0 : return wbc_status;
253 : }
254 :
255 : /* Fill in a struct passwd* for a domain user based on uid */
256 0 : wbcErr wbcGetpwuid(uid_t uid, struct passwd **pwd)
257 : {
258 0 : struct passwd lpwd = {0};
259 : enum nss_status status;
260 0 : char *buffer = NULL;
261 : size_t buflen;
262 : wbcErr wbc_status;
263 : int nss_errno;
264 :
265 0 : if (ctx == NULL && !open_libnss_sss()) {
266 0 : return WBC_ERR_NSS_ERROR;
267 : }
268 :
269 0 : if (pwd == NULL) {
270 0 : return WBC_ERR_INVALID_PARAM;
271 : }
272 :
273 0 : buflen = DEFAULT_BUFSIZE;
274 0 : buffer = malloc(buflen);
275 0 : if (buffer == NULL) {
276 0 : return WBC_ERR_NO_MEMORY;
277 : }
278 :
279 0 : status = ctx->getpwuid_r(uid, &lpwd, buffer, buflen, &nss_errno);
280 0 : wbc_status = nss_to_wbc(status);
281 0 : if (WBC_ERROR_IS_OK(wbc_status) == true) {
282 0 : wbc_status = copy_pwd(&lpwd, pwd);
283 : }
284 :
285 0 : free(buffer);
286 :
287 0 : return wbc_status;
288 : }
289 :
290 : /* Fill in a struct passwd* for a domain user based on sid */
291 0 : wbcErr wbcGetpwsid(struct wbcDomainSid *sid, struct passwd **pwd)
292 : {
293 : wbcErr wbc_status;
294 : uid_t uid;
295 :
296 0 : wbc_status = wbcSidToUid(sid, &uid);
297 0 : if (!WBC_ERROR_IS_OK(wbc_status)) {
298 0 : return wbc_status;
299 : }
300 :
301 0 : wbc_status = wbcGetpwuid(uid, pwd);
302 :
303 0 : return wbc_status;
304 :
305 : }
306 :
307 0 : static void wbcGroupDestructor(void *ptr)
308 : {
309 0 : struct group *gr = (struct group *)ptr;
310 : size_t c;
311 :
312 0 : free(gr->gr_name);
313 0 : free(gr->gr_passwd);
314 :
315 : /* if the array was partly created this can be NULL */
316 0 : if (gr->gr_mem == NULL) {
317 0 : return;
318 : }
319 :
320 0 : for (c=0; gr->gr_mem[c] != NULL; c++) {
321 0 : free(gr->gr_mem[c]);
322 : }
323 0 : free(gr->gr_mem);
324 : }
325 :
326 0 : static wbcErr copy_grp(struct group *in, struct group **out)
327 : {
328 : struct group *gr;
329 : size_t members;
330 : size_t c;
331 :
332 0 : gr = (struct group *)wbcAllocateMemory(1, sizeof(struct group),
333 : wbcGroupDestructor);
334 0 : if (gr == NULL) {
335 0 : return WBC_ERR_NO_MEMORY;
336 : }
337 :
338 0 : gr->gr_name = strdup(in->gr_name);
339 0 : if (gr->gr_name == NULL) {
340 0 : goto fail;
341 : }
342 :
343 0 : gr->gr_passwd = strdup(in->gr_passwd);
344 0 : if (gr->gr_passwd == NULL) {
345 0 : goto fail;
346 : }
347 :
348 0 : gr->gr_gid = in->gr_gid;
349 :
350 0 : for (members = 0; in->gr_mem[members] != NULL; members++);
351 :
352 0 : gr->gr_mem = (char **)calloc(members+1, sizeof(char *));
353 0 : if (gr->gr_mem == NULL) {
354 0 : goto fail;
355 : }
356 :
357 0 : for (c = 0; c < members; c++) {
358 0 : gr->gr_mem[c] = strdup(in->gr_mem[c]);
359 0 : if (gr->gr_mem[c] == NULL) {
360 0 : goto fail;
361 : }
362 : }
363 :
364 0 : *out = gr;
365 0 : return WBC_ERR_SUCCESS;
366 : fail:
367 0 : wbcFreeMemory(gr);
368 :
369 0 : return WBC_ERR_NO_MEMORY;
370 : }
371 : /* Fill in a struct passwd* for a domain user based on username */
372 0 : wbcErr wbcGetgrnam(const char *name, struct group **grp)
373 : {
374 : struct group lgrp;
375 : enum nss_status status;
376 0 : char *newbuffer = NULL;
377 0 : char *buffer = NULL;
378 0 : size_t buflen = 0;
379 : wbcErr wbc_status;
380 : int nss_errno;
381 :
382 0 : if (ctx == NULL && !open_libnss_sss()) {
383 0 : return WBC_ERR_NSS_ERROR;
384 : }
385 :
386 0 : if (name == NULL || grp == NULL) {
387 0 : return WBC_ERR_INVALID_PARAM;
388 : }
389 :
390 0 : buflen = DEFAULT_BUFSIZE_HALF;
391 : do {
392 0 : buflen *= 2;
393 :
394 0 : newbuffer = realloc(buffer, buflen);
395 0 : if (newbuffer == NULL) {
396 0 : free(buffer);
397 0 : return WBC_ERR_NO_MEMORY;
398 : }
399 0 : buffer = newbuffer;
400 :
401 0 : memset(grp, 0, sizeof(struct group));
402 0 : status = ctx->getgrnam_r(name, &lgrp, buffer, buflen, &nss_errno);
403 0 : wbc_status = nss_to_wbc(status);
404 0 : if (WBC_ERROR_IS_OK(wbc_status) == true) {
405 0 : wbc_status = copy_grp(&lgrp, grp);
406 : }
407 0 : } while (status == NSS_STATUS_TRYAGAIN && nss_errno == ERANGE \
408 0 : && buflen < MAX_BUFSIZE);
409 :
410 0 : free(buffer);
411 :
412 0 : return wbc_status;
413 : }
414 :
415 : /* Fill in a struct passwd* for a domain user based on uid */
416 0 : wbcErr wbcGetgrgid(gid_t gid, struct group **grp)
417 : {
418 : struct group lgrp;
419 : enum nss_status status;
420 0 : char *newbuffer = NULL;
421 0 : char *buffer = NULL;
422 0 : size_t buflen = 0;
423 : wbcErr wbc_status;
424 : int nss_errno;
425 :
426 0 : if (ctx == NULL && !open_libnss_sss()) {
427 0 : return WBC_ERR_NSS_ERROR;
428 : }
429 :
430 0 : if (grp == NULL) {
431 0 : return WBC_ERR_INVALID_PARAM;
432 : }
433 :
434 0 : buflen = DEFAULT_BUFSIZE_HALF;
435 : do {
436 0 : buflen *= 2;
437 :
438 0 : newbuffer = realloc(buffer, buflen);
439 0 : if (newbuffer == NULL) {
440 0 : free(buffer);
441 0 : return WBC_ERR_NO_MEMORY;
442 : }
443 0 : buffer = newbuffer;
444 :
445 0 : memset(grp, 0, sizeof(struct group));
446 0 : status = ctx->getgrgid_r(gid, &lgrp, buffer, buflen, &nss_errno);
447 0 : wbc_status = nss_to_wbc(status);
448 0 : if (WBC_ERROR_IS_OK(wbc_status) == true) {
449 0 : wbc_status = copy_grp(&lgrp, grp);
450 : }
451 0 : } while (status == NSS_STATUS_TRYAGAIN && nss_errno == ERANGE \
452 0 : && buflen < MAX_BUFSIZE);
453 :
454 0 : free(buffer);
455 :
456 0 : return wbc_status;
457 : }
458 :
459 : /* Reset the passwd iterator */
460 0 : wbcErr wbcSetpwent(void)
461 : {
462 : enum nss_status status;
463 : wbcErr wbc_status;
464 :
465 0 : if (ctx == NULL && !open_libnss_sss()) {
466 0 : return WBC_ERR_NSS_ERROR;
467 : }
468 :
469 0 : status = ctx->setpwent();
470 0 : wbc_status = nss_to_wbc(status);
471 :
472 0 : return wbc_status;
473 : }
474 :
475 : /* Close the passwd iterator */
476 0 : wbcErr wbcEndpwent(void)
477 : {
478 : enum nss_status status;
479 : wbcErr wbc_status;
480 :
481 0 : if (ctx == NULL && !open_libnss_sss()) {
482 0 : return WBC_ERR_NSS_ERROR;
483 : }
484 :
485 0 : status = ctx->endpwent();
486 0 : wbc_status = nss_to_wbc(status);
487 :
488 0 : return wbc_status;
489 : }
490 :
491 : /* Return the next struct passwd* entry from the pwent iterator */
492 0 : wbcErr wbcGetpwent(struct passwd **pwd)
493 : {
494 0 : struct passwd lpwd = {0};
495 : enum nss_status status;
496 0 : char *buffer = NULL;
497 : size_t buflen;
498 : wbcErr wbc_status;
499 : int nss_errno;
500 :
501 0 : if (ctx == NULL && !open_libnss_sss()) {
502 0 : return WBC_ERR_NSS_ERROR;
503 : }
504 :
505 0 : if (pwd == NULL) {
506 0 : return WBC_ERR_INVALID_PARAM;
507 : }
508 :
509 0 : buflen = DEFAULT_BUFSIZE;
510 0 : buffer = malloc(buflen);
511 0 : if (buffer == NULL) {
512 0 : return WBC_ERR_NO_MEMORY;
513 : }
514 :
515 0 : status = ctx->getpwent_r(&lpwd, buffer, buflen, &nss_errno);
516 0 : wbc_status = nss_to_wbc(status);
517 0 : if (WBC_ERROR_IS_OK(wbc_status) == true) {
518 0 : wbc_status = copy_pwd(&lpwd, pwd);
519 : }
520 :
521 0 : free(buffer);
522 :
523 0 : return wbc_status;
524 : }
525 :
526 : /* Reset the group iterator */
527 0 : wbcErr wbcSetgrent(void)
528 : {
529 : enum nss_status status;
530 : wbcErr wbc_status;
531 :
532 0 : if (ctx == NULL && !open_libnss_sss()) {
533 0 : return WBC_ERR_NSS_ERROR;
534 : }
535 :
536 0 : status = ctx->setgrent();
537 0 : wbc_status = nss_to_wbc(status);
538 :
539 0 : return wbc_status;
540 : }
541 :
542 : /* Close the group iterator */
543 0 : wbcErr wbcEndgrent(void)
544 : {
545 : enum nss_status status;
546 : wbcErr wbc_status;
547 :
548 0 : if (ctx == NULL && !open_libnss_sss()) {
549 0 : return WBC_ERR_NSS_ERROR;
550 : }
551 :
552 0 : status = ctx->endgrent();
553 0 : wbc_status = nss_to_wbc(status);
554 :
555 0 : return wbc_status;
556 : }
557 :
558 : /* Return the next struct group* entry from the pwent iterator */
559 0 : wbcErr wbcGetgrent(struct group **grp)
560 : {
561 : struct group lgrp;
562 : enum nss_status status;
563 0 : char *newbuffer = NULL;
564 0 : char *buffer = NULL;
565 0 : size_t buflen = 0;
566 : wbcErr wbc_status;
567 : int nss_errno;
568 :
569 0 : if (ctx == NULL && !open_libnss_sss()) {
570 0 : return WBC_ERR_NSS_ERROR;
571 : }
572 :
573 0 : if (grp == NULL) {
574 0 : return WBC_ERR_INVALID_PARAM;
575 : }
576 :
577 0 : buflen = DEFAULT_BUFSIZE_HALF;
578 : do {
579 0 : buflen *= 2;
580 :
581 0 : newbuffer = realloc(buffer, buflen);
582 0 : if (newbuffer == NULL) {
583 0 : free(buffer);
584 0 : return WBC_ERR_NO_MEMORY;
585 : }
586 0 : buffer = newbuffer;
587 :
588 0 : memset(grp, 0, sizeof(struct group));
589 0 : status = ctx->getgrent_r(&lgrp, buffer, buflen, &nss_errno);
590 0 : wbc_status = nss_to_wbc(status);
591 0 : if (WBC_ERROR_IS_OK(wbc_status) == true) {
592 0 : wbc_status = copy_grp(&lgrp, grp);
593 : }
594 0 : } while (status == NSS_STATUS_TRYAGAIN && nss_errno == ERANGE \
595 0 : && buflen < MAX_BUFSIZE);
596 :
597 0 : free(buffer);
598 :
599 0 : return wbc_status;
600 : }
601 :
602 : /* Return the next struct group* entry from the pwent iterator */
603 0 : wbcErr wbcGetgrlist(struct group **grp)
604 : {
605 : /* Not used anywhere */
606 0 : WBC_SSSD_NOT_IMPLEMENTED;
607 : }
608 :
609 : /* Return the unix group array belonging to the given user */
610 0 : wbcErr wbcGetGroups(const char *account,
611 : uint32_t *num_groups,
612 : gid_t **_groups)
613 : {
614 : wbcErr wbc_status;
615 : enum nss_status status;
616 : struct passwd *pwd;
617 0 : long int gr_size = 0;
618 0 : long int start = 0;
619 0 : gid_t *gids = NULL;
620 : int nss_errno;
621 :
622 0 : wbc_status = wbcGetpwnam(account, &pwd);
623 0 : if (!WBC_ERROR_IS_OK(wbc_status)) {
624 0 : return wbc_status;
625 : }
626 :
627 0 : gr_size = DEFAULT_BUFSIZE;
628 0 : gids = calloc(gr_size, sizeof(gid_t));
629 0 : if (gids == NULL) {
630 0 : wbc_status = WBC_ERR_NO_MEMORY;
631 0 : goto done;
632 : }
633 :
634 : /* nss modules may skip the primary group when we pass it in so always
635 : * add it in advance */
636 0 : gids[0] = pwd->pw_gid;
637 0 : start++;
638 :
639 0 : status = ctx->initgroups_dyn(pwd->pw_name, pwd->pw_gid, &start,
640 : &gr_size, &gids, -1, &nss_errno);
641 0 : wbc_status = nss_to_wbc(status);
642 0 : if (!WBC_ERROR_IS_OK(wbc_status)) {
643 0 : goto done;
644 : }
645 :
646 0 : *_groups = gids;
647 0 : *num_groups = start;
648 :
649 0 : wbc_status = WBC_ERR_SUCCESS;
650 :
651 : done:
652 0 : wbcFreeMemory(pwd);
653 :
654 0 : if (!WBC_ERROR_IS_OK(wbc_status)) {
655 0 : free(gids);
656 : }
657 :
658 0 : return wbc_status;
659 : }
|