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 <user_group.h> 7 8 #include <ctype.h> 9 #include <errno.h> 10 #include <limits.h> 11 #include <stdio.h> 12 #include <stdlib.h> 13 #include <string.h> 14 15 #include <new> 16 17 #include <libroot_lock.h> 18 #include <libroot_private.h> 19 #include <RegistrarDefs.h> 20 21 #include <util/KMessage.h> 22 23 24 using BPrivate::Tokenizer; 25 26 27 const char* BPrivate::kPasswdFile = "/etc/passwd"; 28 const char* BPrivate::kGroupFile = "/etc/group"; 29 const char* BPrivate::kShadowPwdFile = "/etc/shadow"; 30 31 static benaphore sUserGroupLock; 32 static port_id sRegistrarPort = -1; 33 34 35 status_t 36 BPrivate::user_group_lock() 37 { 38 return benaphore_lock(&sUserGroupLock); 39 } 40 41 42 status_t 43 BPrivate::user_group_unlock() 44 { 45 return benaphore_unlock(&sUserGroupLock); 46 } 47 48 49 port_id 50 BPrivate::get_registrar_authentication_port() 51 { 52 if (sRegistrarPort < 0) 53 sRegistrarPort = find_port(REGISTRAR_AUTHENTICATION_PORT_NAME); 54 55 return sRegistrarPort; 56 } 57 58 59 status_t 60 BPrivate::send_authentication_request_to_registrar(KMessage& request, 61 KMessage& reply) 62 { 63 status_t error = request.SendTo(get_registrar_authentication_port(), 0, 64 &reply); 65 if (error != B_OK) 66 return error; 67 68 return (status_t)reply.What(); 69 } 70 71 72 class Tokenizer { 73 public: 74 Tokenizer(char* string) 75 : fString(string) 76 { 77 } 78 79 char* NextToken(char separator) 80 { 81 if (fString == NULL) 82 return NULL; 83 84 char* token = fString; 85 fString = strchr(fString, separator); 86 if (fString != NULL) { 87 *fString = '\0'; 88 fString++; 89 } 90 91 return token; 92 } 93 94 char* NextTrimmedToken(char separator) 95 { 96 char* token = NextToken(separator); 97 if (token == NULL) 98 return NULL; 99 100 // skip spaces at the beginning 101 while (*token != '\0' && isspace(*token)) 102 token++; 103 104 // cut off spaces at the end 105 char* end = token + strlen(token); 106 while (end != token && isspace(end[-1])) 107 end--; 108 *end = '\0'; 109 110 return token; 111 } 112 113 private: 114 char* fString; 115 }; 116 117 118 static char* 119 buffer_dup_string(const char* string, char*& buffer, size_t& bufferLen) 120 { 121 if (string == NULL) 122 return NULL; 123 124 size_t size = strlen(string) + 1; 125 if (size > bufferLen) 126 return NULL; 127 128 strcpy(buffer, string); 129 char* result = buffer; 130 buffer += size; 131 bufferLen -= size; 132 133 return result; 134 } 135 136 137 static void* 138 buffer_allocate(size_t size, size_t align, char*& buffer, size_t& bufferSize) 139 { 140 // align padding 141 addr_t pad = align - (((addr_t)buffer - 1) & (align - 1)) - 1; 142 if (pad + size > bufferSize) 143 return NULL; 144 145 char* result = buffer + pad; 146 buffer = result + size; 147 bufferSize -= pad + size; 148 149 return result; 150 } 151 152 153 // #pragma mark - passwd support 154 155 156 status_t 157 BPrivate::copy_passwd_to_buffer(const char* name, const char* password, 158 uid_t uid, gid_t gid, const char* home, const char* shell, 159 const char* realName, passwd* entry, char* buffer, size_t bufferSize) 160 { 161 entry->pw_uid = uid; 162 entry->pw_gid = gid; 163 164 entry->pw_name = buffer_dup_string(name, buffer, bufferSize); 165 entry->pw_passwd = buffer_dup_string(password, buffer, bufferSize); 166 entry->pw_dir = buffer_dup_string(home, buffer, bufferSize); 167 entry->pw_shell = buffer_dup_string(shell, buffer, bufferSize); 168 entry->pw_gecos = buffer_dup_string(realName, buffer, bufferSize); 169 170 if (entry->pw_name && entry->pw_passwd && entry->pw_dir 171 && entry->pw_shell && entry->pw_gecos) { 172 return 0; 173 } 174 175 return ERANGE; 176 } 177 178 179 status_t 180 BPrivate::copy_passwd_to_buffer(const passwd* from, passwd* entry, char* buffer, 181 size_t bufferSize) 182 { 183 return copy_passwd_to_buffer(from->pw_name, from->pw_passwd, from->pw_uid, 184 from->pw_gid, from->pw_dir, from->pw_shell, from->pw_gecos, entry, 185 buffer, bufferSize); 186 } 187 188 189 status_t 190 BPrivate::parse_passwd_line(char* line, char*& name, char*& password, 191 uid_t& uid, gid_t& gid, char*& home, char*& shell, char*& realName) 192 { 193 Tokenizer tokenizer(line); 194 195 name = tokenizer.NextTrimmedToken(':'); 196 password = tokenizer.NextTrimmedToken(':'); 197 char* userID = tokenizer.NextTrimmedToken(':'); 198 char* groupID = tokenizer.NextTrimmedToken(':'); 199 realName = tokenizer.NextTrimmedToken(':'); 200 home = tokenizer.NextTrimmedToken(':'); 201 shell = tokenizer.NextTrimmedToken(':'); 202 203 // skip if invalid 204 size_t nameLen; 205 if (shell == NULL || (nameLen = strlen(name)) == 0 206 || !isdigit(*userID) || !isdigit(*groupID) 207 || nameLen >= MAX_PASSWD_NAME_LEN 208 || strlen(password) >= MAX_PASSWD_PASSWORD_LEN 209 || strlen(realName) >= MAX_PASSWD_REAL_NAME_LEN 210 || strlen(home) >= MAX_PASSWD_HOME_DIR_LEN 211 || strlen(shell) >= MAX_PASSWD_SHELL_LEN) { 212 return B_BAD_VALUE; 213 } 214 215 uid = atoi(userID); 216 gid = atoi(groupID); 217 218 return B_OK; 219 } 220 221 222 // #pragma mark - group support 223 224 225 status_t 226 BPrivate::copy_group_to_buffer(const char* name, const char* password, 227 gid_t gid, const char* const* members, int memberCount, group* entry, 228 char* buffer, size_t bufferSize) 229 { 230 entry->gr_gid = gid; 231 232 // allocate member array (do that first for alignment reasons) 233 entry->gr_mem = (char**)buffer_allocate(sizeof(char*) * (memberCount + 1), 234 sizeof(char*), buffer, bufferSize); 235 if (entry->gr_mem == NULL) 236 return ERANGE; 237 238 // copy name and password 239 entry->gr_name = buffer_dup_string(name, buffer, bufferSize); 240 entry->gr_passwd = buffer_dup_string(password, buffer, bufferSize); 241 if (entry->gr_name == NULL || entry->gr_passwd == NULL) 242 return ERANGE; 243 244 // copy member array 245 for (int i = 0; i < memberCount; i++) { 246 entry->gr_mem[i] = buffer_dup_string(members[i], buffer, bufferSize); 247 if (entry->gr_mem[i] == NULL) 248 return ERANGE; 249 } 250 entry->gr_mem[memberCount] = NULL; 251 252 return 0; 253 } 254 255 256 status_t 257 BPrivate::copy_group_to_buffer(const group* from, group* entry, char* buffer, 258 size_t bufferSize) 259 { 260 int memberCount = 0; 261 while (from->gr_mem[memberCount] != NULL) 262 memberCount++; 263 264 return copy_group_to_buffer(from->gr_name, from->gr_passwd, 265 from->gr_gid, from->gr_mem, memberCount, entry, buffer, bufferSize); 266 } 267 268 269 status_t 270 BPrivate::parse_group_line(char* line, char*& name, char*& password, gid_t& gid, 271 char** members, int& memberCount) 272 { 273 Tokenizer tokenizer(line); 274 275 name = tokenizer.NextTrimmedToken(':'); 276 password = tokenizer.NextTrimmedToken(':'); 277 char* groupID = tokenizer.NextTrimmedToken(':'); 278 279 // skip if invalid 280 size_t nameLen; 281 if (groupID == NULL || (nameLen = strlen(name)) == 0 || !isdigit(*groupID) 282 || nameLen >= MAX_GROUP_NAME_LEN 283 || strlen(password) >= MAX_GROUP_PASSWORD_LEN) { 284 return B_BAD_VALUE; 285 } 286 287 gid = atol(groupID); 288 289 memberCount = 0; 290 291 while (char* groupUser = tokenizer.NextTrimmedToken(',')) { 292 // ignore invalid members 293 if (*groupUser == '\0' || strlen(groupUser) >= MAX_PASSWD_NAME_LEN) 294 continue; 295 296 members[memberCount++] = groupUser; 297 298 // ignore excess members 299 if (memberCount == MAX_GROUP_MEMBER_COUNT) 300 break; 301 } 302 303 return B_OK; 304 } 305 306 307 // #pragma mark - shadow password support 308 309 310 status_t 311 BPrivate::copy_shadow_pwd_to_buffer(const char* name, const char* password, 312 int lastChanged, int min, int max, int warn, int inactive, int expiration, 313 int flags, spwd* entry, char* buffer, size_t bufferSize) 314 { 315 entry->sp_lstchg = lastChanged; 316 entry->sp_min = min; 317 entry->sp_max = max; 318 entry->sp_warn = warn; 319 entry->sp_inact = inactive; 320 entry->sp_expire = expiration; 321 entry->sp_flag = flags; 322 323 entry->sp_namp = buffer_dup_string(name, buffer, bufferSize); 324 entry->sp_pwdp = buffer_dup_string(password, buffer, bufferSize); 325 326 if (entry->sp_namp && entry->sp_pwdp) 327 return 0; 328 329 return ERANGE; 330 } 331 332 333 status_t 334 BPrivate::copy_shadow_pwd_to_buffer(const spwd* from, spwd* entry, 335 char* buffer, size_t bufferSize) 336 { 337 return copy_shadow_pwd_to_buffer(from->sp_namp, from->sp_pwdp, 338 from->sp_lstchg, from->sp_min, from->sp_max, from->sp_warn, 339 from->sp_inact, from->sp_expire, from->sp_flag, entry, buffer, 340 bufferSize); 341 } 342 343 344 status_t 345 BPrivate::parse_shadow_pwd_line(char* line, char*& name, char*& password, 346 int& lastChanged, int& min, int& max, int& warn, int& inactive, 347 int& expiration, int& flags) 348 { 349 Tokenizer tokenizer(line); 350 351 name = tokenizer.NextTrimmedToken(':'); 352 password = tokenizer.NextTrimmedToken(':'); 353 char* lastChangedString = tokenizer.NextTrimmedToken(':'); 354 char* minString = tokenizer.NextTrimmedToken(':'); 355 char* maxString = tokenizer.NextTrimmedToken(':'); 356 char* warnString = tokenizer.NextTrimmedToken(':'); 357 char* inactiveString = tokenizer.NextTrimmedToken(':'); 358 char* expirationString = tokenizer.NextTrimmedToken(':'); 359 char* flagsString = tokenizer.NextTrimmedToken(':'); 360 361 // skip if invalid 362 size_t nameLen; 363 if (flagsString == NULL || (nameLen = strlen(name)) == 0 364 || nameLen >= MAX_SHADOW_PWD_NAME_LEN 365 || strlen(password) >= MAX_SHADOW_PWD_PASSWORD_LEN) { 366 return B_BAD_VALUE; 367 } 368 369 lastChanged = atoi(lastChangedString); 370 min = minString[0] != '\0' ? atoi(minString) : -1; 371 max = maxString[0] != '\0' ? atoi(maxString) : -1; 372 warn = warnString[0] != '\0' ? atoi(warnString) : -1; 373 inactive = inactiveString[0] != '\0' ? atoi(inactiveString) : -1; 374 expiration = expirationString[0] != '\0' ? atoi(expirationString) : -1; 375 flags = atoi(flagsString); 376 377 return B_OK; 378 } 379 380 381 // #pragma mark - 382 383 384 void 385 __init_pwd_backend(void) 386 { 387 benaphore_init(&sUserGroupLock, "user group"); 388 } 389 390 391 void 392 __reinit_pwd_backend_after_fork(void) 393 { 394 benaphore_init(&sUserGroupLock, "user group"); 395 } 396