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