diff options
Diffstat (limited to 'nsswitch/libwbclient/wbc_pwd_sssd.c')
-rw-r--r-- | nsswitch/libwbclient/wbc_pwd_sssd.c | 575 |
1 files changed, 563 insertions, 12 deletions
diff --git a/nsswitch/libwbclient/wbc_pwd_sssd.c b/nsswitch/libwbclient/wbc_pwd_sssd.c index 164166090e1..b44c8c3eb95 100644 --- a/nsswitch/libwbclient/wbc_pwd_sssd.c +++ b/nsswitch/libwbclient/wbc_pwd_sssd.c @@ -25,75 +25,586 @@ #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) { - WBC_SSSD_NOT_IMPLEMENTED; + 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) { - WBC_SSSD_NOT_IMPLEMENTED; + 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) { - WBC_SSSD_NOT_IMPLEMENTED; + 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) { - WBC_SSSD_NOT_IMPLEMENTED; + 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) { - WBC_SSSD_NOT_IMPLEMENTED; + 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) { - WBC_SSSD_NOT_IMPLEMENTED; + 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) { - WBC_SSSD_NOT_IMPLEMENTED; + 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) { - WBC_SSSD_NOT_IMPLEMENTED; + 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) { - WBC_SSSD_NOT_IMPLEMENTED; + 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) { - WBC_SSSD_NOT_IMPLEMENTED; + 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) { - WBC_SSSD_NOT_IMPLEMENTED; + 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; } @@ -102,5 +613,45 @@ wbcErr wbcGetGroups(const char *account, uint32_t *num_groups, gid_t **_groups) { - WBC_SSSD_NOT_IMPLEMENTED; + 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; } |