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