From dbed32309eeaa1a9bd8240e8be538730599f7596 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Sat, 26 Dec 2020 13:14:07 -0800 Subject: [PATCH] add osdGetRoles() --- src/Makefile | 1 + src/osgroups.cpp | 194 ++++++++++++++++++++++++++++++++++++++++++++++ src/utilpvt.h | 4 + test/testutil.cpp | 27 ++++++- 4 files changed, 225 insertions(+), 1 deletion(-) create mode 100644 src/osgroups.cpp diff --git a/src/Makefile b/src/Makefile index 71929ee..0831591 100644 --- a/src/Makefile +++ b/src/Makefile @@ -72,6 +72,7 @@ LIB_SRCS += describe.cpp LIB_SRCS += log.cpp LIB_SRCS += unittest.cpp LIB_SRCS += util.cpp +LIB_SRCS += osgroups.cpp LIB_SRCS += sharedarray.cpp LIB_SRCS += bitmask.cpp LIB_SRCS += type.cpp diff --git a/src/osgroups.cpp b/src/osgroups.cpp new file mode 100644 index 0000000..59b58cc --- /dev/null +++ b/src/osgroups.cpp @@ -0,0 +1,194 @@ +/** + * Copyright - See the COPYRIGHT that is included with this distribution. + * pvxs is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + */ +/* originally taken from getgroups.cpp in pvAccessCPP + */ + +#include + +#if defined(_WIN32) +# define USE_LANMAN +#elif !defined(__rtems__) && !defined(vxWorks) +# define USE_UNIX_GROUPS +#endif + +/* conditionally include any system headers */ +#if defined(USE_UNIX_GROUPS) + +#include +#include +#include +#include +#include + +// getgrouplist() has a slightly different prototype on OSX. +# ifdef __APPLE__ +// OSX has gid_t, which isn't "int", but doesn't use it here. +// int getgrouplist(const char *name, int basegid, int *groups, int *ngroups); +typedef int osi_gid_t; +# else +// int getgrouplist(const char *user, gid_t group, gid_t *groups, int *ngroups); +typedef gid_t osi_gid_t; +# endif + +#elif defined(USE_LANMAN) + +#include +#include +#include +#include + +#endif + +#include + +#include "utilpvt.h" + +namespace pvxs { +namespace impl { + +#if defined(USE_UNIX_GROUPS) + +void osdGetRoles(const std::string& account, std::set& roles) +{ + passwd *user = getpwnam(account.c_str()); + if(!user) { + roles.insert(account); + return; // don't know who this is + } + + typedef std::set gids_t; + gids_t gids; + + gids.insert(user->pw_gid); // include primary group + + /* List supplementary groups. + * + * Rant... + * getgrouplist() differs subtly when the *count is too short. + * Some libc (Mac) don't update the count + * Some libc (glibc) don't write a truncated list. + * + * We might also use getgrent(), but this isn't reentrant, and + * would anyway require visiting all groups. + * The GNU alternative getgrent_r() would require us to allocate + * enough space to hold the list of all members of the largest + * group. This may be hundreds. + * + * So we iterate with getgrouplist() as the lesser evil... + */ + { + // start with a guess + std::vector gtemp(16, (osi_gid_t)-1); + + while(true) { + int gcount = int(gtemp.size()); + int ret = getgrouplist(user->pw_name, user->pw_gid, >emp[0], &gcount); + + if(ret>=0 && gcount>=0 && gcount <= int(gtemp.size())) { + // success + gtemp.resize(gcount); + break; + + } else if(ret>=0) { + // success, but invalid count? give up + gtemp.clear(); + break; + + } else if(gcount == int(gtemp.size())) { + // too small, but gcount not updated. (Mac) + // arbitrary increase to size and retry + gtemp.resize(gtemp.size()*2u, (osi_gid_t)-1); + + } else if(gcount > int(gtemp.size())) { + // too small, gcount holds actual size. retry + gtemp.resize(gcount, (osi_gid_t)-1); + + } else { + // too small, but gcount got smaller? give up + gtemp.clear(); + break; + } + } + + for(size_t i=0, N=gtemp.size(); igr_name); + } +} + +#elif defined(USE_LANMAN) + +void osdGetRoles(const std::string& account, std::set& roles) +{ + NET_API_STATUS sts; + LPLOCALGROUP_USERS_INFO_0 pinfo = NULL; + DWORD ninfo = 0, nmaxinfo = 0; + std::vector wbuf; + + { + size_t N = mbstowcs(NULL, account.c_str(), 0); + if(N==size_t(-1)) + return; // username has invalid MB char + wbuf.resize(N+1); + N = mbstowcs(&wbuf[0], account.c_str(), account.size()); + assert(N+1==wbuf.size()); + wbuf[N] = 0; // paranoia + } + + // this call may involve network I/O + sts = NetUserGetLocalGroups(NULL, &wbuf[0], 0, + LG_INCLUDE_INDIRECT, + (LPBYTE*)&pinfo, + MAX_PREFERRED_LENGTH, + &ninfo, &nmaxinfo); + + if(sts!=NERR_Success) { + ninfo = 0; + } + + try { + std::vector buf; + + for(DWORD i=0; i& roles) +{ + /* Group list not available (RTEMS, vxWorks) + * Report the remote account as the only role. + */ + roles.insert(account); +} +#endif + +}} // namespace pvxs::impl diff --git a/src/utilpvt.h b/src/utilpvt.h index 0ff6692..cb85efd 100644 --- a/src/utilpvt.h +++ b/src/utilpvt.h @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -152,6 +153,9 @@ public: #undef RWLOCK_RLOCK #undef RWLOCK_RUNLOCK +PVXS_API +void osdGetRoles(const std::string& account, std::set& roles); + void logger_shutdown(); // std::max() isn't constexpr until c++14 :( diff --git a/test/testutil.cpp b/test/testutil.cpp index 2dbed3a..3c91383 100644 --- a/test/testutil.cpp +++ b/test/testutil.cpp @@ -8,20 +8,45 @@ #include #include +#include #include #include #include +#include namespace { using namespace pvxs; +void testAccount() +{ + testShow()<<__func__; + + std::string account; + { + std::vector buf(128); + testOk(osiGetUserName(buf.data(), buf.size()-1u)==osiGetUserNameSuccess, "osiGetUserName()"); + buf.back() = '\0'; + account = buf.data(); + } + testOk(!account.empty(), "User: '%s'", account.c_str()); + + std::set roles; + osdGetRoles(account, roles); + + testNotEq(roles.size(), 0u); + for(auto& role : roles) { + testDiag(" %s", role.c_str()); + } +} + } // namespace MAIN(testutil) { - testPlan(0); + testPlan(3); + testAccount(); return testDone(); }