This is Unofficial EPICS BASE Doxygen Site
getgroups.cpp
Go to the documentation of this file.
1 
7 #include <set>
8 
9 #if defined(_WIN32)
10 # define USE_LANMAN
11 #elif !defined(__rtems__) && !defined(vxWorks)
12 # define USE_UNIX_GROUPS
13 #endif
14 
15 /* conditionally include any system headers */
16 #if defined(USE_UNIX_GROUPS)
17 
18 #include <sys/types.h>
19 #include <unistd.h>
20 #include <pwd.h>
21 #include <grp.h>
22 #include <errno.h>
23 
24 // getgrouplist() has a slightly different prototype on OSX.
25 # ifdef __APPLE__
26 // OSX has gid_t, which isn't "int", but doesn't use it here.
27 // int getgrouplist(const char *name, int basegid, int *groups, int *ngroups);
28 typedef int osi_gid_t;
29 # else
30 // int getgrouplist(const char *user, gid_t group, gid_t *groups, int *ngroups);
31 typedef gid_t osi_gid_t;
32 # endif
33 
34 #elif defined(USE_LANMAN)
35 
36 #include <stdlib.h>
37 #include <winsock2.h>
38 #include <windows.h>
39 #include <lm.h>
40 
41 #endif
42 
43 #define epicsExportSharedSymbols
44 #include <pv/security.h>
45 
46 namespace epics {
47 namespace pvAccess {
48 
49 #if defined(USE_UNIX_GROUPS)
50 
51 void osdGetRoles(const std::string& account, PeerInfo::roles_t& roles)
52 {
53  passwd *user = getpwnam(account.c_str());
54  if(!user)
55  return; // don't know who this is
56 
57  typedef std::set<gid_t> gids_t;
58  gids_t gids;
59 
60  gids.insert(user->pw_gid); // include primary group
61 
62  /* List supplementary groups.
63  *
64  * Rant...
65  * getgrouplist() differs subtly when the *count is too short.
66  * Some libc (Mac) don't update the count
67  * Some libc (glibc) don't write a truncated list.
68  *
69  * We might also use getgrent(), but this isn't reentrant, and
70  * would anyway require visiting all groups.
71  * The GNU alternative getgrent_r() would require us to allocate
72  * enough space to hold the list of all members of the largest
73  * group. This may be hundreds.
74  *
75  * So we iterate with getgrouplist() as the lesser evil...
76  */
77  {
78  // start with a guess
79  std::vector<osi_gid_t> gtemp(16, (osi_gid_t)-1);
80 
81  while(true) {
82  int gcount = int(gtemp.size());
83  int ret = getgrouplist(user->pw_name, user->pw_gid, &gtemp[0], &gcount);
84 
85  if(ret>=0 && gcount>=0 && gcount <= int(gtemp.size())) {
86  // success
87  gtemp.resize(gcount);
88  break;
89 
90  } else if(ret>=0) {
91  // success, but invalid count? give up
92  gtemp.clear();
93  break;
94 
95  } else if(gcount == int(gtemp.size())) {
96  // too small, but gcount not updated. (Mac)
97  // arbitrary increase to size and retry
98  gtemp.resize(gtemp.size()*2u, (osi_gid_t)-1);
99 
100  } else if(gcount > int(gtemp.size())) {
101  // too small, gcount holds actual size. retry
102  gtemp.resize(gcount, (osi_gid_t)-1);
103 
104  } else {
105  // too small, but gcount got smaller? give up
106  gtemp.clear();
107  break;
108  }
109  }
110 
111  for(size_t i=0, N=gtemp.size(); i<N; i++)
112  gids.insert(gtemp[i]);
113  }
114 
115  // map GIDs to names
116  for(gids_t::iterator it(gids.begin()), end(gids.end()); it!=end; it++) {
117  group* gr = getgrgid(*it);
118  if(!gr)
119  continue;
120  roles.insert(gr->gr_name);
121  }
122 }
123 
124 #elif defined(USE_LANMAN)
125 
126 void osdGetRoles(const std::string& account, PeerInfo::roles_t& roles)
127 {
128  NET_API_STATUS sts;
129  LPLOCALGROUP_USERS_INFO_0 pinfo = NULL;
130  DWORD ninfo = 0, nmaxinfo = 0;
131  std::vector<wchar_t> wbuf;
132 
133  {
134  size_t N = mbstowcs(NULL, account.c_str(), 0);
135  if(N==size_t(-1))
136  return; // username has invalid MB char
137  wbuf.resize(N+1);
138  N = mbstowcs(&wbuf[0], account.c_str(), account.size());
139  assert(N+1==wbuf.size());
140  wbuf[N] = 0; // paranoia
141  }
142 
143  // this call may involve network I/O
144  sts = NetUserGetLocalGroups(NULL, &wbuf[0], 0,
145  LG_INCLUDE_INDIRECT,
146  (LPBYTE*)&pinfo,
147  MAX_PREFERRED_LENGTH,
148  &ninfo, &nmaxinfo);
149 
150  if(sts!=NERR_Success)
151  return; // silently do nothing.
152 
153  try {
154  std::vector<char> buf;
155 
156  for(DWORD i=0; i<ninfo; i++) {
157  size_t N = wcstombs(NULL, pinfo[i].lgrui0_name, 0);
158  if(N==size_t(-1))
159  continue; // has invalid MB char
160 
161  buf.resize(N+1);
162  N = wcstombs(&buf[0], pinfo[i].lgrui0_name, buf.size());
163  buf[N] = 0; // paranoia
164 
165  roles.insert(&buf[0]);
166  }
167 
168  NetApiBufferFree(pinfo);
169  }catch(...){
170  NetApiBufferFree(pinfo);
171  throw;
172  }
173 }
174 
175 #else
176 
177 void osdGetRoles(const std::string& account, PeerInfo::roles_t& roles)
178 {}
179 #endif
180 
181 }} // namespace epics::pvAccess
#define assert(exp)
Declare that a condition should be true.
Definition: epicsAssert.h:70
int i
Definition: scan.c:967
TODO only here because of the Lockable.
Definition: ntaggregate.cpp:16
epicsShareFunc void osdGetRoles(const std::string &account, PeerInfo::roles_t &roles)
Query OS specific DB for role/group names assocated with a user account.
Definition: getgroups.cpp:51
#define NULL
Definition: catime.c:38
gid_t osi_gid_t
Definition: getgroups.cpp:31
std::set< std::string > roles_t
Definition: security.h:133