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