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