/** * 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