1 /* 2 * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 #include <grp.h> 7 8 #include <errno.h> 9 #include <string.h> 10 #include <unistd.h> 11 12 #include <new> 13 14 #include <OS.h> 15 16 #include <libroot_private.h> 17 18 #include "user_group_common.h" 19 20 21 using BPrivate::GroupDB; 22 using BPrivate::GroupDBReader; 23 using BPrivate::GroupEntryHandler; 24 using BPrivate::UserGroupLocker; 25 26 static GroupDB* sGroupDB = NULL; 27 28 static struct group sGroupBuffer; 29 static char sGroupStringBuffer[MAX_GROUP_BUFFER_SIZE]; 30 31 32 namespace { 33 34 class GroupEntryFindHandler : public GroupEntryHandler { 35 public: 36 GroupEntryFindHandler(const char* name, uid_t gid, 37 group* entry, char* buffer, size_t bufferSize) 38 : 39 fName(name), 40 fGID(gid), 41 fEntry(entry), 42 fBuffer(buffer), 43 fBufferSize(bufferSize) 44 { 45 } 46 47 virtual status_t HandleEntry(const char* name, const char* password, 48 gid_t gid, const char* const* members, int memberCount) 49 { 50 if (fName != NULL ? strcmp(fName, name) != 0 : fGID != gid) 51 return 0; 52 53 // found 54 status_t error = BPrivate::copy_group_to_buffer(name, password, gid, 55 members, memberCount, fEntry, fBuffer, fBufferSize); 56 57 return error == B_OK ? 1 : error; 58 } 59 60 private: 61 const char* fName; 62 gid_t fGID; 63 group* fEntry; 64 char* fBuffer; 65 size_t fBufferSize; 66 }; 67 68 69 class UserGroupEntryHandler : public BPrivate::GroupEntryHandler { 70 public: 71 UserGroupEntryHandler(const char* user, gid_t* groupList, int maxGroupCount, 72 int* groupCount) 73 : 74 fUser(user), 75 fGroupList(groupList), 76 fMaxGroupCount(maxGroupCount), 77 fGroupCount(groupCount) 78 { 79 } 80 81 virtual status_t HandleEntry(const char* name, const char* password, 82 gid_t gid, const char* const* members, int memberCount) 83 { 84 for (int i = 0; i < memberCount; i++) { 85 const char* member = members[i]; 86 if (*member != '\0' && strcmp(member, fUser) == 0) { 87 if (*fGroupCount < fMaxGroupCount) 88 fGroupList[*fGroupCount] = gid; 89 ++*fGroupCount; 90 } 91 } 92 93 return 0; 94 } 95 96 private: 97 const char* fUser; 98 gid_t* fGroupList; 99 int fMaxGroupCount; 100 int* fGroupCount; 101 }; 102 103 } // empty namespace 104 105 106 static GroupDB* 107 init_group_db() 108 { 109 if (sGroupDB != NULL) 110 return sGroupDB; 111 112 sGroupDB = new(std::nothrow) GroupDB; 113 if (sGroupDB == NULL) 114 return NULL; 115 116 if (sGroupDB->Init() != B_OK) { 117 delete sGroupDB; 118 sGroupDB = NULL; 119 } 120 121 return sGroupDB; 122 } 123 124 125 // #pragma mark - 126 127 128 struct group* 129 getgrent(void) 130 { 131 struct group* result = NULL; 132 int status = getgrent_r(&sGroupBuffer, sGroupStringBuffer, 133 sizeof(sGroupStringBuffer), &result); 134 if (status != 0) 135 errno = status; 136 return result; 137 } 138 139 140 int 141 getgrent_r(struct group* group, char* buffer, size_t bufferSize, 142 struct group** _result) 143 { 144 UserGroupLocker _; 145 146 int status = B_NO_MEMORY; 147 148 *_result = NULL; 149 150 if (GroupDB* db = init_group_db()) { 151 status = db->GetNextEntry(group, buffer, bufferSize); 152 if (status == 0) 153 *_result = group; 154 155 } 156 157 return status; 158 } 159 160 161 void 162 setgrent(void) 163 { 164 UserGroupLocker _; 165 166 if (GroupDB* db = init_group_db()) 167 db->RewindEntries(); 168 } 169 170 171 void 172 endgrent(void) 173 { 174 UserGroupLocker locker; 175 176 GroupDB* db = sGroupDB; 177 sGroupDB = NULL; 178 179 locker.Unlock(); 180 181 delete db; 182 } 183 184 185 struct group * 186 getgrnam(const char *name) 187 { 188 struct group* result = NULL; 189 int status = getgrnam_r(name, &sGroupBuffer, sGroupStringBuffer, 190 sizeof(sGroupStringBuffer), &result); 191 if (status != 0) 192 errno = status; 193 return result; 194 } 195 196 197 int 198 getgrnam_r(const char *name, struct group *group, char *buffer, 199 size_t bufferSize, struct group **_result) 200 { 201 GroupEntryFindHandler handler(name, 0, group, buffer, bufferSize); 202 status_t status = GroupDBReader(&handler).Read(BPrivate::kGroupFile); 203 204 *_result = (status == 1 ? group : NULL); 205 return (status == 1 ? 0 : (status == 0 ? ENOENT : status)); 206 } 207 208 209 struct group * 210 getgrgid(gid_t gid) 211 { 212 struct group* result = NULL; 213 int status = getgrgid_r(gid, &sGroupBuffer, sGroupStringBuffer, 214 sizeof(sGroupStringBuffer), &result); 215 if (status != 0) 216 errno = status; 217 return result; 218 } 219 220 221 int 222 getgrgid_r(gid_t gid, struct group *group, char *buffer, 223 size_t bufferSize, struct group **_result) 224 { 225 GroupEntryFindHandler handler(NULL, gid, group, buffer, bufferSize); 226 status_t status = GroupDBReader(&handler).Read(BPrivate::kGroupFile); 227 228 *_result = (status == 1 ? group : NULL); 229 return (status == 1 ? 0 : (status == 0 ? ENOENT : status)); 230 } 231 232 233 int 234 getgrouplist(const char* user, gid_t baseGroup, gid_t* groupList, 235 int* groupCount) 236 { 237 int maxGroupCount = *groupCount; 238 *groupCount = 0; 239 240 UserGroupEntryHandler handler(user, groupList, maxGroupCount, groupCount); 241 BPrivate::GroupDBReader(&handler).Read(BPrivate::kGroupFile); 242 243 // put in the base group 244 if (*groupCount < maxGroupCount) 245 groupList[*groupCount] = baseGroup; 246 ++*groupCount; 247 248 return *groupCount <= maxGroupCount ? *groupCount : -1; 249 } 250 251 252 int 253 initgroups(const char* user, gid_t baseGroup) 254 { 255 gid_t groups[NGROUPS_MAX + 1]; 256 int groupCount = NGROUPS_MAX + 1; 257 if (getgrouplist(user, baseGroup, groups, &groupCount) < 0) 258 return -1; 259 260 return setgroups(groupCount, groups); 261 } 262