/* Unix SMB/CIFS implementation. Winbind client API - SSSD version Copyright (C) Sumit Bose 2014 This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ /* Required Headers */ #include "replace.h" #include "libwbclient.h" #include "../winbind_client.h" #include "wbc_sssd_internal.h" #define DEFAULT_BUFSIZE 4096 #define MAX_BUFSIZE (1024*1204) struct nss_ops_ctx { void *dl_handle; enum nss_status (*getpwnam_r)(const char *name, struct passwd *result, char *buffer, size_t buflen, int *errnop); enum nss_status (*getpwuid_r)(uid_t uid, struct passwd *result, char *buffer, size_t buflen, int *errnop); enum nss_status (*setpwent)(void); enum nss_status (*getpwent_r)(struct passwd *result, char *buffer, size_t buflen, int *errnop); enum nss_status (*endpwent)(void); enum nss_status (*getgrnam_r)(const char *name, struct group *result, char *buffer, size_t buflen, int *errnop); enum nss_status (*getgrgid_r)(gid_t gid, struct group *result, char *buffer, size_t buflen, int *errnop); enum nss_status (*setgrent)(void); enum nss_status (*getgrent_r)(struct group *result, char *buffer, size_t buflen, int *errnop); enum nss_status (*endgrent)(void); enum nss_status (*initgroups_dyn)(const char *user, gid_t group, long int *start, long int *size, gid_t **groups, long int limit, int *errnop); }; struct nss_ops_ctx *ctx = NULL; static bool open_libnss_sss(void) { ctx = calloc(1, sizeof(struct nss_ops_ctx)); if (ctx == NULL) { return false; } ctx->dl_handle = dlopen("libnss_sss.so.2", RTLD_NOW); if (ctx->dl_handle == NULL) { goto fail; } ctx->getpwnam_r = dlsym(ctx->dl_handle, "_nss_sss_getpwnam_r"); if (ctx->getpwnam_r == NULL) { goto fail; } ctx->getpwuid_r = dlsym(ctx->dl_handle, "_nss_sss_getpwuid_r"); if (ctx->getpwuid_r == NULL) { goto fail; } ctx->setpwent = dlsym(ctx->dl_handle, "_nss_sss_setpwent"); if (ctx->setpwent == NULL) { goto fail; } ctx->getpwent_r = dlsym(ctx->dl_handle, "_nss_sss_getpwent_r"); if (ctx->getpwent_r == NULL) { goto fail; } ctx->endpwent = dlsym(ctx->dl_handle, "_nss_sss_endpwent"); if (ctx->endpwent == NULL) { goto fail; } ctx->getgrnam_r = dlsym(ctx->dl_handle, "_nss_sss_getgrnam_r"); if (ctx->getgrnam_r == NULL) { goto fail; } ctx->getgrgid_r = dlsym(ctx->dl_handle, "_nss_sss_getgrgid_r"); if (ctx->getgrgid_r == NULL) { goto fail; } ctx->setgrent = dlsym(ctx->dl_handle, "_nss_sss_setgrent"); if (ctx->setgrent == NULL) { goto fail; } ctx->getgrent_r = dlsym(ctx->dl_handle, "_nss_sss_getgrent_r"); if (ctx->getgrent_r == NULL) { goto fail; } ctx->endgrent = dlsym(ctx->dl_handle, "_nss_sss_endgrent"); if (ctx->endgrent == NULL) { goto fail; } ctx->initgroups_dyn = dlsym(ctx->dl_handle, "_nss_sss_initgroups_dyn"); if (ctx->initgroups_dyn == NULL) { goto fail; } return true; fail: if (ctx->dl_handle != NULL) { dlclose(ctx->dl_handle); } free(ctx); ctx = NULL; return false; } static void wbcPasswdDestructor(void *ptr) { struct passwd *pw = (struct passwd *)ptr; free(pw->pw_name); free(pw->pw_passwd); free(pw->pw_gecos); free(pw->pw_shell); free(pw->pw_dir); } static wbcErr copy_pwd(struct passwd *in, struct passwd **out) { struct passwd *pw; pw = (struct passwd *)wbcAllocateMemory(1, sizeof(struct passwd), wbcPasswdDestructor); if (pw == NULL) { return WBC_ERR_NO_MEMORY; } pw->pw_name = strdup(in->pw_name); if (pw->pw_name == NULL) { goto fail; } pw->pw_passwd = strdup(in->pw_passwd); if (pw->pw_passwd == NULL) { goto fail; } pw->pw_uid = in->pw_uid; pw->pw_gid = in->pw_gid; pw->pw_gecos = strdup(in->pw_gecos); if (pw->pw_gecos == NULL) { goto fail; } pw->pw_shell = strdup(in->pw_shell); if (pw->pw_shell == NULL) { goto fail; } pw->pw_dir = strdup(in->pw_dir); if (pw->pw_dir == NULL) { goto fail; } *out = pw; return WBC_ERR_SUCCESS; fail: wbcFreeMemory(pw); return WBC_ERR_NO_MEMORY; } static wbcErr nss_to_wbc(enum nss_status status) { wbcErr wbc_status; switch (status) { case NSS_STATUS_SUCCESS: wbc_status = WBC_ERR_SUCCESS; break; case NSS_STATUS_NOTFOUND: wbc_status = WBC_ERR_UNKNOWN_USER; break; default: wbc_status = WBC_ERR_UNKNOWN_FAILURE; } return wbc_status; } /* Fill in a struct passwd* for a domain user based on username */ wbcErr wbcGetpwnam(const char *name, struct passwd **pwd) { struct passwd lpwd = {0}; enum nss_status status; char *buffer = NULL; size_t buflen; wbcErr wbc_status; int nss_errno; if (ctx == NULL && !open_libnss_sss()) { return WBC_ERR_NSS_ERROR; } if (name == NULL || pwd == NULL) { return WBC_ERR_INVALID_PARAM; } buflen = DEFAULT_BUFSIZE; buffer = malloc(buflen); if (buffer == NULL) { return WBC_ERR_NO_MEMORY; } status = ctx->getpwnam_r(name, &lpwd, buffer, buflen, &nss_errno); wbc_status = nss_to_wbc(status); if (WBC_ERROR_IS_OK(wbc_status)) { wbc_status = copy_pwd(&lpwd, pwd); } free(buffer); return wbc_status; } /* Fill in a struct passwd* for a domain user based on uid */ wbcErr wbcGetpwuid(uid_t uid, struct passwd **pwd) { struct passwd lpwd = {0}; enum nss_status status; char *buffer = NULL; size_t buflen; wbcErr wbc_status; int nss_errno; if (ctx == NULL && !open_libnss_sss()) { return WBC_ERR_NSS_ERROR; } if (pwd == NULL) { return WBC_ERR_INVALID_PARAM; } buflen = DEFAULT_BUFSIZE; buffer = malloc(buflen); if (buffer == NULL) { return WBC_ERR_NO_MEMORY; } status = ctx->getpwuid_r(uid, &lpwd, buffer, buflen, &nss_errno); wbc_status = nss_to_wbc(status); if (WBC_ERROR_IS_OK(wbc_status)) { wbc_status = copy_pwd(&lpwd, pwd); } free(buffer); return wbc_status; } /* Fill in a struct passwd* for a domain user based on sid */ wbcErr wbcGetpwsid(struct wbcDomainSid *sid, struct passwd **pwd) { wbcErr wbc_status; uid_t uid; wbc_status = wbcSidToUid(sid, &uid); if (!WBC_ERROR_IS_OK(wbc_status)) { return wbc_status; } wbc_status = wbcGetpwuid(uid, pwd); return wbc_status; } static void wbcGroupDestructor(void *ptr) { struct group *gr = (struct group *)ptr; size_t c; free(gr->gr_name); free(gr->gr_passwd); /* if the array was partly created this can be NULL */ if (gr->gr_mem == NULL) { return; } for (c=0; gr->gr_mem[c] != NULL; c++) { free(gr->gr_mem[c]); } free(gr->gr_mem); } static wbcErr copy_grp(struct group *in, struct group **out) { struct group *gr; size_t members; size_t c; gr = (struct group *)wbcAllocateMemory(1, sizeof(struct group), wbcGroupDestructor); if (gr == NULL) { return WBC_ERR_NO_MEMORY; } gr->gr_name = strdup(in->gr_name); if (gr->gr_name == NULL) { goto fail; } gr->gr_passwd = strdup(in->gr_passwd); if (gr->gr_passwd == NULL) { goto fail; } gr->gr_gid = in->gr_gid; for (members = 0; in->gr_mem[members] != NULL; members++); if (members > 0) { gr->gr_mem = (char **)calloc(members+1, sizeof(char *)); if (gr->gr_mem == NULL) { goto fail; } for (c = 0; c < members; c++) { gr->gr_mem[c] = strdup(in->gr_mem[c]); if (gr->gr_mem[c] == NULL) { goto fail; } } } *out = gr; return WBC_ERR_SUCCESS; fail: wbcFreeMemory(gr); return WBC_ERR_NO_MEMORY; } /* Fill in a struct passwd* for a domain user based on username */ wbcErr wbcGetgrnam(const char *name, struct group **grp) { struct group lgrp; enum nss_status status; char *newbuffer = NULL; char *buffer = NULL; size_t buflen = 0; wbcErr wbc_status; int nss_errno; if (ctx == NULL && !open_libnss_sss()) { return WBC_ERR_NSS_ERROR; } if (name == NULL || grp == NULL) { return WBC_ERR_INVALID_PARAM; } do { if (buflen == 0) { buflen = DEFAULT_BUFSIZE; } else { buflen *= 2; } newbuffer = realloc(buffer, buflen); if (newbuffer == NULL) { free(buffer); return WBC_ERR_NO_MEMORY; } buffer = newbuffer; memset(grp, 0, sizeof(struct group)); status = ctx->getgrnam_r(name, &lgrp, buffer, buflen, &nss_errno); wbc_status = nss_to_wbc(status); if (WBC_ERROR_IS_OK(wbc_status)) { wbc_status = copy_grp(&lgrp, grp); } } while (status == NSS_STATUS_TRYAGAIN && nss_errno == ERANGE \ && buflen < MAX_BUFSIZE); free(buffer); return wbc_status; } /* Fill in a struct passwd* for a domain user based on uid */ wbcErr wbcGetgrgid(gid_t gid, struct group **grp) { struct group lgrp; enum nss_status status; char *newbuffer = NULL; char *buffer = NULL; size_t buflen = 0; wbcErr wbc_status; int nss_errno; if (ctx == NULL && !open_libnss_sss()) { return WBC_ERR_NSS_ERROR; } if (grp == NULL) { return WBC_ERR_INVALID_PARAM; } do { if (buflen == 0) { buflen = DEFAULT_BUFSIZE; } else { buflen *= 2; } newbuffer = realloc(buffer, buflen); if (newbuffer == NULL) { free(buffer); return WBC_ERR_NO_MEMORY; } buffer = newbuffer; memset(grp, 0, sizeof(struct group)); status = ctx->getgrgid_r(gid, &lgrp, buffer, buflen, &nss_errno); wbc_status = nss_to_wbc(status); if (WBC_ERROR_IS_OK(wbc_status)) { wbc_status = copy_grp(&lgrp, grp); } } while (status == NSS_STATUS_TRYAGAIN && nss_errno == ERANGE \ && buflen < MAX_BUFSIZE); free(buffer); return wbc_status; } /* Reset the passwd iterator */ wbcErr wbcSetpwent(void) { enum nss_status status; wbcErr wbc_status; if (ctx == NULL && !open_libnss_sss()) { return WBC_ERR_NSS_ERROR; } status = ctx->setpwent(); wbc_status = nss_to_wbc(status); return wbc_status; } /* Close the passwd iterator */ wbcErr wbcEndpwent(void) { enum nss_status status; wbcErr wbc_status; if (ctx == NULL && !open_libnss_sss()) { return WBC_ERR_NSS_ERROR; } status = ctx->endpwent(); wbc_status = nss_to_wbc(status); return wbc_status; } /* Return the next struct passwd* entry from the pwent iterator */ wbcErr wbcGetpwent(struct passwd **pwd) { struct passwd lpwd = {0}; enum nss_status status; char *buffer = NULL; size_t buflen; wbcErr wbc_status; int nss_errno; if (ctx == NULL && !open_libnss_sss()) { return WBC_ERR_NSS_ERROR; } if (pwd == NULL) { return WBC_ERR_INVALID_PARAM; } buflen = DEFAULT_BUFSIZE; buffer = malloc(buflen); if (buffer == NULL) { return WBC_ERR_NO_MEMORY; } status = ctx->getpwent_r(&lpwd, buffer, buflen, &nss_errno); wbc_status = nss_to_wbc(status); if (WBC_ERROR_IS_OK(wbc_status)) { wbc_status = copy_pwd(&lpwd, pwd); } free(buffer); return wbc_status; } /* Reset the group iterator */ wbcErr wbcSetgrent(void) { enum nss_status status; wbcErr wbc_status; if (ctx == NULL && !open_libnss_sss()) { return WBC_ERR_NSS_ERROR; } status = ctx->setgrent(); wbc_status = nss_to_wbc(status); return wbc_status; } /* Close the group iterator */ wbcErr wbcEndgrent(void) { enum nss_status status; wbcErr wbc_status; if (ctx == NULL && !open_libnss_sss()) { return WBC_ERR_NSS_ERROR; } status = ctx->endgrent(); wbc_status = nss_to_wbc(status); return wbc_status; } /* Return the next struct group* entry from the pwent iterator */ wbcErr wbcGetgrent(struct group **grp) { struct group lgrp; enum nss_status status; char *newbuffer = NULL; char *buffer = NULL; size_t buflen = 0; wbcErr wbc_status; int nss_errno; if (ctx == NULL && !open_libnss_sss()) { return WBC_ERR_NSS_ERROR; } if (grp == NULL) { return WBC_ERR_INVALID_PARAM; } do { if (buflen == 0) { buflen = DEFAULT_BUFSIZE; } else { buflen *= 2; } newbuffer = realloc(buffer, buflen); if (newbuffer == NULL) { free(buffer); return WBC_ERR_NO_MEMORY; } buffer = newbuffer; memset(grp, 0, sizeof(struct group)); status = ctx->getgrent_r(&lgrp, buffer, buflen, &nss_errno); wbc_status = nss_to_wbc(status); if (WBC_ERROR_IS_OK(wbc_status)) { wbc_status = copy_grp(&lgrp, grp); } } while (status == NSS_STATUS_TRYAGAIN && nss_errno == ERANGE \ && buflen < MAX_BUFSIZE); free(buffer); return wbc_status; } /* Return the next struct group* entry from the pwent iterator */ wbcErr wbcGetgrlist(struct group **grp) { /* Not used anywhere */ WBC_SSSD_NOT_IMPLEMENTED; } /* Return the unix group array belonging to the given user */ wbcErr wbcGetGroups(const char *account, uint32_t *num_groups, gid_t **_groups) { wbcErr wbc_status; enum nss_status status; struct passwd *pwd; long int gr_size = 0; long int start = 0; gid_t *gids = NULL; int nss_errno; wbc_status = wbcGetpwnam(account, &pwd); if (!WBC_ERROR_IS_OK(wbc_status)) { return wbc_status; } gr_size = DEFAULT_BUFSIZE; gids = calloc(gr_size, sizeof(gid_t)); if (gids == NULL) { wbc_status = WBC_ERR_NO_MEMORY; goto done; } /* nss modules may skip the primary group when we pass it in so always * add it in advance */ gids[0] = pwd->pw_gid; start++; status = ctx->initgroups_dyn(pwd->pw_name, pwd->pw_gid, &start, &gr_size, &gids, -1, &nss_errno); wbc_status = nss_to_wbc(status); if (!WBC_ERROR_IS_OK(wbc_status)) { goto done; } *_groups = gids; *num_groups = start; wbc_status = WBC_ERR_SUCCESS; done: wbcFreeMemory(pwd); return wbc_status; }