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 7 #include "AuthenticationManager.h" 8 9 #include <errno.h> 10 #include <stdlib.h> 11 #include <stdio.h> 12 #include <sys/param.h> 13 14 #include <map> 15 #include <new> 16 #include <set> 17 #include <string> 18 19 #include <DataIO.h> 20 #include <StringList.h> 21 22 #include <AutoDeleter.h> 23 #include <LaunchRoster.h> 24 #include <RegistrarDefs.h> 25 26 #include <libroot_private.h> 27 #include <user_group.h> 28 #include <util/KMessage.h> 29 30 31 using std::map; 32 using std::string; 33 34 using namespace BPrivate; 35 36 37 typedef std::set<std::string> StringSet; 38 39 40 class AuthenticationManager::FlatStore { 41 public: 42 FlatStore() 43 : fSize(0) 44 { 45 fBuffer.SetBlockSize(1024); 46 } 47 48 void WriteData(size_t offset, const void* data, size_t length) 49 { 50 ssize_t result = fBuffer.WriteAt(offset, data, length); 51 if (result < 0) 52 throw status_t(result); 53 } 54 55 template<typename Type> 56 void WriteData(size_t offset, const Type& data) 57 { 58 WriteData(&data, sizeof(Type)); 59 } 60 61 size_t ReserveSpace(size_t length, bool align) 62 { 63 if (align) 64 fSize = _ALIGN(fSize); 65 66 size_t pos = fSize; 67 fSize += length; 68 69 return pos; 70 } 71 72 void* AppendData(const void* data, size_t length, bool align) 73 { 74 size_t pos = ReserveSpace(length, align); 75 WriteData(pos, data, length); 76 return (void*)(addr_t)pos; 77 } 78 79 template<typename Type> 80 Type* AppendData(const Type& data) 81 { 82 return (Type*)AppendData(&data, sizeof(Type), true); 83 } 84 85 char* AppendString(const char* string) 86 { 87 return (char*)AppendData(string, strlen(string) + 1, false); 88 } 89 90 char* AppendString(const string& str) 91 { 92 return (char*)AppendData(str.c_str(), str.length() + 1, false); 93 } 94 95 const void* Buffer() const 96 { 97 return fBuffer.Buffer(); 98 } 99 100 size_t BufferLength() const 101 { 102 return fSize; 103 } 104 105 private: 106 BMallocIO fBuffer; 107 size_t fSize; 108 }; 109 110 111 class AuthenticationManager::User { 112 public: 113 User() 114 : 115 fUID(0), 116 fGID(0), 117 fLastChanged(0), 118 fMin(-1), 119 fMax(-1), 120 fWarn(-1), 121 fInactive(-1), 122 fExpiration(-1), 123 fFlags(0) 124 { 125 } 126 127 User(const char* name, const char* password, uid_t uid, gid_t gid, 128 const char* home, const char* shell, const char* realName) 129 : 130 fUID(uid), 131 fGID(gid), 132 fName(name), 133 fPassword(password), 134 fHome(home), 135 fShell(shell), 136 fRealName(realName), 137 fLastChanged(0), 138 fMin(-1), 139 fMax(-1), 140 fWarn(-1), 141 fInactive(-1), 142 fExpiration(-1), 143 fFlags(0) 144 { 145 } 146 147 User(const User& other) 148 : 149 fUID(other.fUID), 150 fGID(other.fGID), 151 fName(other.fName), 152 fPassword(other.fPassword), 153 fHome(other.fHome), 154 fShell(other.fShell), 155 fRealName(other.fRealName), 156 fShadowPassword(other.fShadowPassword), 157 fLastChanged(other.fLastChanged), 158 fMin(other.fMin), 159 fMax(other.fMax), 160 fWarn(other.fWarn), 161 fInactive(other.fInactive), 162 fExpiration(other.fExpiration), 163 fFlags(other.fFlags) 164 { 165 } 166 167 const string& Name() const { return fName; } 168 const uid_t UID() const { return fUID; } 169 170 void SetShadowInfo(const char* password, int lastChanged, int min, int max, 171 int warn, int inactive, int expiration, int flags) 172 { 173 fShadowPassword = password; 174 fLastChanged = lastChanged; 175 fMin = min; 176 fMax = max; 177 fWarn = warn; 178 fInactive = inactive; 179 fExpiration = expiration; 180 fFlags = flags; 181 } 182 183 void UpdateFromMessage(const KMessage& message) 184 { 185 int32 intValue; 186 const char* stringValue; 187 188 if (message.FindInt32("uid", &intValue) == B_OK) 189 fUID = intValue; 190 191 if (message.FindInt32("gid", &intValue) == B_OK) 192 fGID = intValue; 193 194 if (message.FindString("name", &stringValue) == B_OK) 195 fName = stringValue; 196 197 if (message.FindString("password", &stringValue) == B_OK) 198 fPassword = stringValue; 199 200 if (message.FindString("home", &stringValue) == B_OK) 201 fHome = stringValue; 202 203 if (message.FindString("shell", &stringValue) == B_OK) 204 fShell = stringValue; 205 206 if (message.FindString("real name", &stringValue) == B_OK) 207 fRealName = stringValue; 208 209 if (message.FindString("shadow password", &stringValue) == B_OK) { 210 fShadowPassword = stringValue; 211 // TODO: 212 // fLastChanged = now; 213 } 214 215 if (message.FindInt32("last changed", &intValue) == B_OK) 216 fLastChanged = intValue; 217 218 if (message.FindInt32("min", &intValue) == B_OK) 219 fMin = intValue; 220 221 if (message.FindInt32("max", &intValue) == B_OK) 222 fMax = intValue; 223 224 if (message.FindInt32("warn", &intValue) == B_OK) 225 fWarn = intValue; 226 227 if (message.FindInt32("inactive", &intValue) == B_OK) 228 fInactive = intValue; 229 230 if (message.FindInt32("expiration", &intValue) == B_OK) 231 fExpiration = intValue; 232 233 if (message.FindInt32("flags", &intValue) == B_OK) 234 fFlags = intValue; 235 } 236 237 passwd* WriteFlatPasswd(FlatStore& store) const 238 { 239 struct passwd passwd; 240 241 passwd.pw_uid = fUID; 242 passwd.pw_gid = fGID; 243 passwd.pw_name = store.AppendString(fName); 244 passwd.pw_passwd = store.AppendString(fPassword); 245 passwd.pw_dir = store.AppendString(fHome); 246 passwd.pw_shell = store.AppendString(fShell); 247 passwd.pw_gecos = store.AppendString(fRealName); 248 249 return store.AppendData(passwd); 250 } 251 252 spwd* WriteFlatShadowPwd(FlatStore& store) const 253 { 254 struct spwd spwd; 255 256 spwd.sp_namp = store.AppendString(fName); 257 spwd.sp_pwdp = store.AppendString(fShadowPassword); 258 spwd.sp_lstchg = fLastChanged; 259 spwd.sp_min = fMin; 260 spwd.sp_max = fMax; 261 spwd.sp_warn = fWarn; 262 spwd.sp_inact = fInactive; 263 spwd.sp_expire = fExpiration; 264 spwd.sp_flag = fFlags; 265 266 return store.AppendData(spwd); 267 } 268 269 status_t WriteToMessage(KMessage& message, bool addShadowPwd) 270 { 271 status_t error; 272 if ((error = message.AddInt32("uid", fUID)) != B_OK 273 || (error = message.AddInt32("gid", fGID)) != B_OK 274 || (error = message.AddString("name", fName.c_str())) != B_OK 275 || (error = message.AddString("password", fPassword.c_str())) 276 != B_OK 277 || (error = message.AddString("home", fHome.c_str())) != B_OK 278 || (error = message.AddString("shell", fShell.c_str())) != B_OK 279 || (error = message.AddString("real name", fRealName.c_str())) 280 != B_OK) { 281 return error; 282 } 283 284 if (!addShadowPwd) 285 return B_OK; 286 287 if ((error = message.AddString("shadow password", 288 fShadowPassword.c_str())) != B_OK 289 || (error = message.AddInt32("last changed", fLastChanged)) != B_OK 290 || (error = message.AddInt32("min", fMin)) != B_OK 291 || (error = message.AddInt32("max", fMax)) != B_OK 292 || (error = message.AddInt32("warn", fWarn)) != B_OK 293 || (error = message.AddInt32("inactive", fInactive)) != B_OK 294 || (error = message.AddInt32("expiration", fExpiration)) != B_OK 295 || (error = message.AddInt32("flags", fFlags)) != B_OK) { 296 return error; 297 } 298 299 return B_OK; 300 } 301 302 void WritePasswdLine(FILE* file) 303 { 304 fprintf(file, "%s:%s:%d:%d:%s:%s:%s\n", 305 fName.c_str(), fPassword.c_str(), (int)fUID, (int)fGID, 306 fRealName.c_str(), fHome.c_str(), fShell.c_str()); 307 } 308 309 void WriteShadowPwdLine(FILE* file) 310 { 311 fprintf(file, "%s:%s:%d:", fName.c_str(), fShadowPassword.c_str(), 312 fLastChanged); 313 314 // The following values are supposed to be printed as empty strings, 315 // if negative. 316 int values[5] = { fMin, fMax, fWarn, fInactive, fExpiration }; 317 for (int i = 0; i < 5; i++) { 318 if (values[i] >= 0) 319 fprintf(file, "%d", values[i]); 320 fprintf(file, ":"); 321 } 322 323 fprintf(file, "%d\n", fFlags); 324 } 325 326 private: 327 uid_t fUID; 328 gid_t fGID; 329 string fName; 330 string fPassword; 331 string fHome; 332 string fShell; 333 string fRealName; 334 string fShadowPassword; 335 int fLastChanged; 336 int fMin; 337 int fMax; 338 int fWarn; 339 int fInactive; 340 int fExpiration; 341 int fFlags; 342 }; 343 344 345 class AuthenticationManager::Group { 346 public: 347 Group() 348 : 349 fGID(0), 350 fName(), 351 fPassword(), 352 fMembers() 353 { 354 } 355 356 Group(const char* name, const char* password, gid_t gid, 357 const char* const* members, int memberCount) 358 : 359 fGID(gid), 360 fName(name), 361 fPassword(password), 362 fMembers() 363 { 364 for (int i = 0; i < memberCount; i++) 365 fMembers.insert(members[i]); 366 } 367 368 ~Group() 369 { 370 } 371 372 const string& Name() const { return fName; } 373 const gid_t GID() const { return fGID; } 374 375 bool HasMember(const char* name) 376 { 377 try { 378 return fMembers.find(name) != fMembers.end(); 379 } catch (...) { 380 return false; 381 } 382 } 383 384 bool MemberRemoved(const std::string& name) 385 { 386 return fMembers.erase(name) > 0; 387 } 388 389 void UpdateFromMessage(const KMessage& message) 390 { 391 int32 intValue; 392 if (message.FindInt32("gid", &intValue) == B_OK) 393 fGID = intValue; 394 395 const char* stringValue; 396 if (message.FindString("name", &stringValue) == B_OK) 397 fName = stringValue; 398 399 if (message.FindString("password", &stringValue) == B_OK) 400 fPassword = stringValue; 401 402 if (message.FindString("members", &stringValue) == B_OK) { 403 fMembers.clear(); 404 for (int32 i = 0; 405 (stringValue = message.GetString("members", i, NULL)) != NULL; 406 i++) { 407 if (stringValue != NULL && *stringValue != '\0') 408 fMembers.insert(stringValue); 409 } 410 } 411 } 412 413 group* WriteFlatGroup(FlatStore& store) const 414 { 415 struct group group; 416 417 char* members[MAX_GROUP_MEMBER_COUNT + 1]; 418 int32 count = 0; 419 for (StringSet::const_iterator it = fMembers.begin(); 420 it != fMembers.end(); ++it) { 421 members[count++] = store.AppendString(it->c_str()); 422 } 423 members[count] = (char*)-1; 424 425 group.gr_gid = fGID; 426 group.gr_name = store.AppendString(fName); 427 group.gr_passwd = store.AppendString(fPassword); 428 group.gr_mem = (char**)store.AppendData(members, 429 sizeof(char*) * (count + 1), true); 430 431 return store.AppendData(group); 432 } 433 434 status_t WriteToMessage(KMessage& message) 435 { 436 status_t error; 437 if ((error = message.AddInt32("gid", fGID)) != B_OK 438 || (error = message.AddString("name", fName.c_str())) != B_OK 439 || (error = message.AddString("password", fPassword.c_str())) 440 != B_OK) { 441 return error; 442 } 443 444 for (StringSet::const_iterator it = fMembers.begin(); 445 it != fMembers.end(); ++it) { 446 if ((error = message.AddString("members", it->c_str())) != B_OK) 447 return error; 448 } 449 450 return B_OK; 451 } 452 453 void WriteGroupLine(FILE* file) 454 { 455 fprintf(file, "%s:%s:%d:", 456 fName.c_str(), fPassword.c_str(), (int)fGID); 457 for (StringSet::const_iterator it = fMembers.begin(); 458 it != fMembers.end(); ++it) { 459 if (it == fMembers.begin()) 460 fprintf(file, "%s", it->c_str()); 461 else 462 fprintf(file, ",%s", it->c_str()); 463 } 464 fputs("\n", file); 465 } 466 467 private: 468 gid_t fGID; 469 string fName; 470 string fPassword; 471 StringSet fMembers; 472 }; 473 474 475 class AuthenticationManager::UserDB { 476 public: 477 status_t AddUser(User* user) 478 { 479 try { 480 fUsersByID[user->UID()] = user; 481 } catch (...) { 482 return B_NO_MEMORY; 483 } 484 485 try { 486 fUsersByName[user->Name()] = user; 487 } catch (...) { 488 fUsersByID.erase(fUsersByID.find(user->UID())); 489 return B_NO_MEMORY; 490 } 491 492 return B_OK; 493 } 494 495 void RemoveUser(User* user) 496 { 497 fUsersByID.erase(fUsersByID.find(user->UID())); 498 fUsersByName.erase(fUsersByName.find(user->Name())); 499 } 500 501 User* UserByID(uid_t uid) const 502 { 503 map<uid_t, User*>::const_iterator it = fUsersByID.find(uid); 504 return (it == fUsersByID.end() ? NULL : it->second); 505 } 506 507 User* UserByName(const char* name) const 508 { 509 map<string, User*>::const_iterator it = fUsersByName.find(name); 510 return (it == fUsersByName.end() ? NULL : it->second); 511 } 512 513 int32 WriteFlatPasswdDB(FlatStore& store) const 514 { 515 int32 count = fUsersByID.size(); 516 517 size_t entriesSpace = sizeof(passwd*) * count; 518 size_t offset = store.ReserveSpace(entriesSpace, true); 519 passwd** entries = new passwd*[count]; 520 ArrayDeleter<passwd*> _(entries); 521 522 int32 index = 0; 523 for (map<uid_t, User*>::const_iterator it = fUsersByID.begin(); 524 it != fUsersByID.end(); ++it) { 525 entries[index++] = it->second->WriteFlatPasswd(store); 526 } 527 528 store.WriteData(offset, entries, entriesSpace); 529 530 return count; 531 } 532 533 int32 WriteFlatShadowDB(FlatStore& store) const 534 { 535 int32 count = fUsersByID.size(); 536 537 size_t entriesSpace = sizeof(spwd*) * count; 538 size_t offset = store.ReserveSpace(entriesSpace, true); 539 spwd** entries = new spwd*[count]; 540 ArrayDeleter<spwd*> _(entries); 541 542 int32 index = 0; 543 for (map<uid_t, User*>::const_iterator it = fUsersByID.begin(); 544 it != fUsersByID.end(); ++it) { 545 entries[index++] = it->second->WriteFlatShadowPwd(store); 546 } 547 548 store.WriteData(offset, entries, entriesSpace); 549 550 return count; 551 } 552 553 void WriteToDisk() 554 { 555 // rename the old files 556 string passwdBackup(kPasswdFile); 557 string shadowBackup(kShadowPwdFile); 558 passwdBackup += ".old"; 559 shadowBackup += ".old"; 560 561 rename(kPasswdFile, passwdBackup.c_str()); 562 rename(kShadowPwdFile, shadowBackup.c_str()); 563 // Don't check errors. We can't do anything anyway. 564 565 // open files 566 FILE* passwdFile = fopen(kPasswdFile, "w"); 567 if (passwdFile == NULL) { 568 debug_printf("REG: Failed to open passwd file \"%s\" for " 569 "writing: %s\n", kPasswdFile, strerror(errno)); 570 } 571 CObjectDeleter<FILE, int> _1(passwdFile, fclose); 572 573 FILE* shadowFile = fopen(kShadowPwdFile, "w"); 574 if (shadowFile == NULL) { 575 debug_printf("REG: Failed to open shadow passwd file \"%s\" for " 576 "writing: %s\n", kShadowPwdFile, strerror(errno)); 577 } 578 CObjectDeleter<FILE, int> _2(shadowFile, fclose); 579 580 // write users 581 for (map<uid_t, User*>::const_iterator it = fUsersByID.begin(); 582 it != fUsersByID.end(); ++it) { 583 User* user = it->second; 584 user->WritePasswdLine(passwdFile); 585 user->WriteShadowPwdLine(shadowFile); 586 } 587 } 588 589 private: 590 map<uid_t, User*> fUsersByID; 591 map<string, User*> fUsersByName; 592 }; 593 594 595 class AuthenticationManager::GroupDB { 596 public: 597 status_t AddGroup(Group* group) 598 { 599 try { 600 fGroupsByID[group->GID()] = group; 601 } catch (...) { 602 return B_NO_MEMORY; 603 } 604 605 try { 606 fGroupsByName[group->Name()] = group; 607 } catch (...) { 608 fGroupsByID.erase(fGroupsByID.find(group->GID())); 609 return B_NO_MEMORY; 610 } 611 612 return B_OK; 613 } 614 615 void RemoveGroup(Group* group) 616 { 617 fGroupsByID.erase(fGroupsByID.find(group->GID())); 618 fGroupsByName.erase(fGroupsByName.find(group->Name())); 619 } 620 621 bool UserRemoved(const std::string& user) 622 { 623 bool changed = false; 624 for (map<gid_t, Group*>::const_iterator it = fGroupsByID.begin(); 625 it != fGroupsByID.end(); ++it) { 626 Group* group = it->second; 627 changed |= group->MemberRemoved(user); 628 } 629 return changed; 630 } 631 632 Group* GroupByID(gid_t gid) const 633 { 634 map<gid_t, Group*>::const_iterator it = fGroupsByID.find(gid); 635 return (it == fGroupsByID.end() ? NULL : it->second); 636 } 637 638 Group* GroupByName(const char* name) const 639 { 640 map<string, Group*>::const_iterator it = fGroupsByName.find(name); 641 return (it == fGroupsByName.end() ? NULL : it->second); 642 } 643 644 int32 GetUserGroups(const char* name, gid_t* groups, int maxCount) 645 { 646 int count = 0; 647 648 for (map<gid_t, Group*>::const_iterator it = fGroupsByID.begin(); 649 it != fGroupsByID.end(); ++it) { 650 Group* group = it->second; 651 if (group->HasMember(name)) { 652 if (count < maxCount) 653 groups[count] = group->GID(); 654 count++; 655 } 656 } 657 658 return count; 659 } 660 661 662 int32 WriteFlatGroupDB(FlatStore& store) const 663 { 664 int32 count = fGroupsByID.size(); 665 666 size_t entriesSpace = sizeof(group*) * count; 667 size_t offset = store.ReserveSpace(entriesSpace, true); 668 group** entries = new group*[count]; 669 ArrayDeleter<group*> _(entries); 670 671 int32 index = 0; 672 for (map<gid_t, Group*>::const_iterator it = fGroupsByID.begin(); 673 it != fGroupsByID.end(); ++it) { 674 entries[index++] = it->second->WriteFlatGroup(store); 675 } 676 677 store.WriteData(offset, entries, entriesSpace); 678 679 return count; 680 } 681 682 void WriteToDisk() 683 { 684 // rename the old files 685 string groupBackup(kGroupFile); 686 groupBackup += ".old"; 687 688 rename(kGroupFile, groupBackup.c_str()); 689 // Don't check errors. We can't do anything anyway. 690 691 // open file 692 FILE* groupFile = fopen(kGroupFile, "w"); 693 if (groupFile == NULL) { 694 debug_printf("REG: Failed to open group file \"%s\" for " 695 "writing: %s\n", kGroupFile, strerror(errno)); 696 } 697 CObjectDeleter<FILE, int> _1(groupFile, fclose); 698 699 // write groups 700 for (map<gid_t, Group*>::const_iterator it = fGroupsByID.begin(); 701 it != fGroupsByID.end(); ++it) { 702 Group* group = it->second; 703 group->WriteGroupLine(groupFile); 704 } 705 } 706 707 private: 708 map<uid_t, Group*> fGroupsByID; 709 map<string, Group*> fGroupsByName; 710 }; 711 712 713 AuthenticationManager::AuthenticationManager() 714 : 715 fRequestPort(-1), 716 fRequestThread(-1), 717 fUserDB(NULL), 718 fGroupDB(NULL), 719 fPasswdDBReply(NULL), 720 fGroupDBReply(NULL), 721 fShadowPwdDBReply(NULL) 722 { 723 } 724 725 726 AuthenticationManager::~AuthenticationManager() 727 { 728 // Quit the request thread and wait for it to finish 729 write_port(fRequestPort, 'quit', NULL, 0); 730 wait_for_thread(fRequestThread, NULL); 731 732 delete fUserDB; 733 delete fGroupDB; 734 delete fPasswdDBReply; 735 delete fGroupDBReply; 736 delete fShadowPwdDBReply; 737 } 738 739 740 status_t 741 AuthenticationManager::Init() 742 { 743 fUserDB = new(std::nothrow) UserDB; 744 fGroupDB = new(std::nothrow) GroupDB; 745 fPasswdDBReply = new(std::nothrow) KMessage(1); 746 fGroupDBReply = new(std::nothrow) KMessage(1); 747 fShadowPwdDBReply = new(std::nothrow) KMessage(1); 748 749 if (fUserDB == NULL || fGroupDB == NULL || fPasswdDBReply == NULL 750 || fGroupDBReply == NULL || fShadowPwdDBReply == NULL) { 751 return B_NO_MEMORY; 752 } 753 754 fRequestPort = BLaunchRoster().GetPort( 755 B_REGISTRAR_AUTHENTICATION_PORT_NAME); 756 if (fRequestPort < 0) 757 return fRequestPort; 758 759 fRequestThread = spawn_thread(&_RequestThreadEntry, 760 "authentication manager", B_NORMAL_PRIORITY + 1, this); 761 if (fRequestThread < 0) 762 return fRequestThread; 763 764 resume_thread(fRequestThread); 765 766 return B_OK; 767 } 768 769 770 status_t 771 AuthenticationManager::_RequestThreadEntry(void* data) 772 { 773 return ((AuthenticationManager*)data)->_RequestThread(); 774 } 775 776 777 status_t 778 AuthenticationManager::_RequestThread() 779 { 780 // read the DB files 781 _InitPasswdDB(); 782 _InitGroupDB(); 783 _InitShadowPwdDB(); 784 785 // get our team ID 786 team_id registrarTeam = -1; 787 { 788 thread_info info; 789 if (get_thread_info(find_thread(NULL), &info) == B_OK) 790 registrarTeam = info.team; 791 } 792 793 // request loop 794 while (true) { 795 KMessage message; 796 port_message_info messageInfo; 797 status_t error = message.ReceiveFrom(fRequestPort, -1, &messageInfo); 798 if (error != B_OK) 799 return B_OK; 800 801 bool isRoot = (messageInfo.sender == 0); 802 803 switch (message.What()) { 804 case B_REG_GET_PASSWD_DB: 805 { 806 // lazily build the reply 807 try { 808 if (fPasswdDBReply->What() == 1) { 809 FlatStore store; 810 int32 count = fUserDB->WriteFlatPasswdDB(store); 811 if (fPasswdDBReply->AddInt32("count", count) != B_OK 812 || fPasswdDBReply->AddData("entries", B_RAW_TYPE, 813 store.Buffer(), store.BufferLength(), 814 false) != B_OK) { 815 error = B_NO_MEMORY; 816 } 817 818 fPasswdDBReply->SetWhat(0); 819 } 820 } catch (...) { 821 error = B_NO_MEMORY; 822 } 823 824 if (error == B_OK) { 825 message.SendReply(fPasswdDBReply, -1, -1, 0, registrarTeam); 826 } else { 827 _InvalidatePasswdDBReply(); 828 KMessage reply(error); 829 message.SendReply(&reply, -1, -1, 0, registrarTeam); 830 } 831 832 break; 833 } 834 835 case B_REG_GET_GROUP_DB: 836 { 837 // lazily build the reply 838 try { 839 if (fGroupDBReply->What() == 1) { 840 FlatStore store; 841 int32 count = fGroupDB->WriteFlatGroupDB(store); 842 if (fGroupDBReply->AddInt32("count", count) != B_OK 843 || fGroupDBReply->AddData("entries", B_RAW_TYPE, 844 store.Buffer(), store.BufferLength(), 845 false) != B_OK) { 846 error = B_NO_MEMORY; 847 } 848 849 fGroupDBReply->SetWhat(0); 850 } 851 } catch (...) { 852 error = B_NO_MEMORY; 853 } 854 855 if (error == B_OK) { 856 message.SendReply(fGroupDBReply, -1, -1, 0, registrarTeam); 857 } else { 858 _InvalidateGroupDBReply(); 859 KMessage reply(error); 860 message.SendReply(&reply, -1, -1, 0, registrarTeam); 861 } 862 863 break; 864 } 865 866 867 case B_REG_GET_SHADOW_PASSWD_DB: 868 { 869 // only root may see the shadow passwd 870 if (!isRoot) 871 error = EPERM; 872 873 // lazily build the reply 874 try { 875 if (error == B_OK && fShadowPwdDBReply->What() == 1) { 876 FlatStore store; 877 int32 count = fUserDB->WriteFlatShadowDB(store); 878 if (fShadowPwdDBReply->AddInt32("count", count) != B_OK 879 || fShadowPwdDBReply->AddData("entries", B_RAW_TYPE, 880 store.Buffer(), store.BufferLength(), 881 false) != B_OK) { 882 error = B_NO_MEMORY; 883 } 884 885 fShadowPwdDBReply->SetWhat(0); 886 } 887 } catch (...) { 888 error = B_NO_MEMORY; 889 } 890 891 if (error == B_OK) { 892 message.SendReply(fShadowPwdDBReply, -1, -1, 0, 893 registrarTeam); 894 } else { 895 _InvalidateShadowPwdDBReply(); 896 KMessage reply(error); 897 message.SendReply(&reply, -1, -1, 0, registrarTeam); 898 } 899 900 break; 901 } 902 903 case B_REG_GET_USER: 904 { 905 User* user = NULL; 906 int32 uid; 907 const char* name; 908 909 // find user 910 if (message.FindInt32("uid", &uid) == B_OK) { 911 user = fUserDB->UserByID(uid); 912 } else if (message.FindString("name", &name) == B_OK) { 913 user = fUserDB->UserByName(name); 914 } else { 915 error = B_BAD_VALUE; 916 } 917 918 if (error == B_OK && user == NULL) 919 error = ENOENT; 920 921 bool getShadowPwd = message.GetBool("shadow", false); 922 923 // only root may see the shadow passwd 924 if (error == B_OK && getShadowPwd && !isRoot) 925 error = EPERM; 926 927 // add user to message 928 KMessage reply; 929 if (error == B_OK) 930 error = user->WriteToMessage(reply, getShadowPwd); 931 932 // send reply 933 reply.SetWhat(error); 934 message.SendReply(&reply, -1, -1, 0, registrarTeam); 935 936 break; 937 } 938 939 case B_REG_GET_GROUP: 940 { 941 Group* group = NULL; 942 int32 gid; 943 const char* name; 944 945 // find group 946 if (message.FindInt32("gid", &gid) == B_OK) { 947 group = fGroupDB->GroupByID(gid); 948 } else if (message.FindString("name", &name) == B_OK) { 949 group = fGroupDB->GroupByName(name); 950 } else { 951 error = B_BAD_VALUE; 952 } 953 954 if (error == B_OK && group == NULL) 955 error = ENOENT; 956 957 // add group to message 958 KMessage reply; 959 if (error == B_OK) 960 error = group->WriteToMessage(reply); 961 962 // send reply 963 reply.SetWhat(error); 964 message.SendReply(&reply, -1, -1, 0, registrarTeam); 965 966 break; 967 } 968 969 case B_REG_GET_USER_GROUPS: 970 { 971 // get user name 972 const char* name; 973 int32 maxCount; 974 if (message.FindString("name", &name) != B_OK 975 || message.FindInt32("max count", &maxCount) != B_OK 976 || maxCount <= 0) { 977 error = B_BAD_VALUE; 978 } 979 980 // get groups 981 gid_t groups[NGROUPS_MAX + 1]; 982 int32 count = 0; 983 if (error == B_OK) { 984 maxCount = min_c(maxCount, NGROUPS_MAX + 1); 985 count = fGroupDB->GetUserGroups(name, groups, maxCount); 986 } 987 988 // add groups to message 989 KMessage reply; 990 if (error == B_OK) { 991 if (reply.AddInt32("count", count) != B_OK 992 || reply.AddData("groups", B_INT32_TYPE, 993 groups, min_c(maxCount, count) * sizeof(gid_t), 994 false) != B_OK) { 995 error = B_NO_MEMORY; 996 } 997 } 998 999 // send reply 1000 reply.SetWhat(error); 1001 message.SendReply(&reply, -1, -1, 0, registrarTeam); 1002 1003 break; 1004 } 1005 1006 case B_REG_UPDATE_USER: 1007 { 1008 // find user 1009 User* user = NULL; 1010 int32 uid; 1011 const char* name; 1012 1013 if (message.FindInt32("uid", &uid) == B_OK) { 1014 user = fUserDB->UserByID(uid); 1015 } else if (message.FindString("name", &name) == B_OK) { 1016 user = fUserDB->UserByName(name); 1017 } else { 1018 error = B_BAD_VALUE; 1019 } 1020 1021 // only root can change anything 1022 if (error == B_OK && !isRoot) 1023 error = EPERM; 1024 1025 // check addUser vs. existing user 1026 bool addUser = message.GetBool("add user", false); 1027 if (error == B_OK) { 1028 if (addUser) { 1029 if (user != NULL) 1030 error = EEXIST; 1031 } else if (user == NULL) 1032 error = ENOENT; 1033 } 1034 1035 // apply all changes 1036 if (error == B_OK) { 1037 // clone the user object and update it from the message 1038 User* oldUser = user; 1039 user = NULL; 1040 try { 1041 user = (oldUser != NULL ? new User(*oldUser) 1042 : new User); 1043 user->UpdateFromMessage(message); 1044 1045 // uid and name should remain the same 1046 if (oldUser != NULL) { 1047 if (oldUser->UID() != user->UID() 1048 || oldUser->Name() != user->Name()) { 1049 error = B_BAD_VALUE; 1050 } 1051 } 1052 1053 // replace the old user and write DBs to disk 1054 if (error == B_OK) { 1055 fUserDB->AddUser(user); 1056 fUserDB->WriteToDisk(); 1057 _InvalidatePasswdDBReply(); 1058 _InvalidateShadowPwdDBReply(); 1059 } 1060 } catch (...) { 1061 error = B_NO_MEMORY; 1062 } 1063 1064 if (error == B_OK) 1065 delete oldUser; 1066 else 1067 delete user; 1068 } 1069 1070 // send reply 1071 KMessage reply; 1072 reply.SetWhat(error); 1073 message.SendReply(&reply, -1, -1, 0, registrarTeam); 1074 1075 break; 1076 } 1077 1078 case B_REG_DELETE_USER: 1079 { 1080 // find user 1081 User* user = NULL; 1082 int32 uid; 1083 const char* name; 1084 1085 if (message.FindInt32("uid", &uid) == B_OK) { 1086 user = fUserDB->UserByID(uid); 1087 } else if (message.FindString("name", &name) == B_OK) { 1088 user = fUserDB->UserByName(name); 1089 } else { 1090 error = B_BAD_VALUE; 1091 } 1092 1093 if (error == B_OK && user == NULL) 1094 error = ENOENT; 1095 1096 // only root can change anything 1097 if (error == B_OK && !isRoot) 1098 error = EPERM; 1099 1100 // apply the change 1101 if (error == B_OK) { 1102 std::string userName = user->Name(); 1103 1104 fUserDB->RemoveUser(user); 1105 fUserDB->WriteToDisk(); 1106 _InvalidatePasswdDBReply(); 1107 _InvalidateShadowPwdDBReply(); 1108 1109 if (fGroupDB->UserRemoved(userName)) { 1110 fGroupDB->WriteToDisk(); 1111 _InvalidateGroupDBReply(); 1112 } 1113 } 1114 1115 // send reply 1116 KMessage reply; 1117 reply.SetWhat(error); 1118 message.SendReply(&reply, -1, -1, 0, registrarTeam); 1119 1120 break; 1121 } 1122 1123 case B_REG_UPDATE_GROUP: 1124 { 1125 // find group 1126 Group* group = NULL; 1127 int32 gid; 1128 const char* name; 1129 1130 if (message.FindInt32("gid", &gid) == B_OK) { 1131 group = fGroupDB->GroupByID(gid); 1132 } else if (message.FindString("name", &name) == B_OK) { 1133 group = fGroupDB->GroupByName(name); 1134 } else { 1135 error = B_BAD_VALUE; 1136 } 1137 1138 // only root can change anything 1139 if (error == B_OK && !isRoot) 1140 error = EPERM; 1141 1142 // check addGroup vs. existing group 1143 bool addGroup = message.GetBool("add group", false); 1144 if (error == B_OK) { 1145 if (addGroup) { 1146 if (group != NULL) 1147 error = EEXIST; 1148 } else if (group == NULL) 1149 error = ENOENT; 1150 } 1151 1152 // apply all changes 1153 if (error == B_OK) { 1154 // clone the group object and update it from the message 1155 Group* oldGroup = group; 1156 group = NULL; 1157 try { 1158 group = (oldGroup != NULL ? new Group(*oldGroup) 1159 : new Group); 1160 group->UpdateFromMessage(message); 1161 1162 // gid and name should remain the same 1163 if (oldGroup != NULL) { 1164 if (oldGroup->GID() != group->GID() 1165 || oldGroup->Name() != group->Name()) { 1166 error = B_BAD_VALUE; 1167 } 1168 } 1169 1170 // replace the old group and write DBs to disk 1171 if (error == B_OK) { 1172 fGroupDB->AddGroup(group); 1173 fGroupDB->WriteToDisk(); 1174 _InvalidateGroupDBReply(); 1175 } 1176 } catch (...) { 1177 error = B_NO_MEMORY; 1178 } 1179 1180 if (error == B_OK) 1181 delete oldGroup; 1182 else 1183 delete group; 1184 } 1185 1186 // send reply 1187 KMessage reply; 1188 reply.SetWhat(error); 1189 message.SendReply(&reply, -1, -1, 0, registrarTeam); 1190 1191 break; 1192 } 1193 1194 case B_REG_DELETE_GROUP: 1195 { 1196 // find group 1197 Group* group = NULL; 1198 int32 gid; 1199 const char* name; 1200 1201 if (message.FindInt32("gid", &gid) == B_OK) { 1202 group = fGroupDB->GroupByID(gid); 1203 } else if (message.FindString("name", &name) == B_OK) { 1204 group = fGroupDB->GroupByName(name); 1205 } else { 1206 error = B_BAD_VALUE; 1207 } 1208 1209 if (error == B_OK && group == NULL) 1210 error = ENOENT; 1211 1212 // only root can change anything 1213 if (error == B_OK && !isRoot) 1214 error = EPERM; 1215 1216 // apply the change 1217 if (error == B_OK) { 1218 fGroupDB->RemoveGroup(group); 1219 fGroupDB->WriteToDisk(); 1220 _InvalidateGroupDBReply(); 1221 } 1222 1223 // send reply 1224 KMessage reply; 1225 reply.SetWhat(error); 1226 message.SendReply(&reply, -1, -1, 0, registrarTeam); 1227 1228 break; 1229 } 1230 1231 default: 1232 debug_printf("REG: invalid message: %" B_PRIu32 "\n", 1233 message.What()); 1234 } 1235 } 1236 } 1237 1238 1239 status_t 1240 AuthenticationManager::_InitPasswdDB() 1241 { 1242 FILE* file = fopen(kPasswdFile, "r"); 1243 if (file == NULL) { 1244 debug_printf("REG: Failed to open passwd DB file \"%s\": %s\n", 1245 kPasswdFile, strerror(errno)); 1246 return errno; 1247 } 1248 CObjectDeleter<FILE, int> _(file, fclose); 1249 1250 char lineBuffer[LINE_MAX]; 1251 while (char* line = fgets(lineBuffer, sizeof(lineBuffer), file)) { 1252 if (strlen(line) == 0) 1253 continue; 1254 1255 char* name; 1256 char* password; 1257 uid_t uid; 1258 gid_t gid; 1259 char* home; 1260 char* shell; 1261 char* realName; 1262 1263 status_t error = parse_passwd_line(line, name, password, uid, gid, 1264 home, shell, realName); 1265 if (error != B_OK) { 1266 debug_printf("REG: Unparsable line in passwd DB file: \"%s\"\n", 1267 strerror(errno)); 1268 continue; 1269 } 1270 1271 User* user = NULL; 1272 try { 1273 user = new User(name, password, uid, gid, home, shell, realName); 1274 } catch (...) { 1275 } 1276 1277 if (user == NULL || fUserDB->AddUser(user) != B_OK) { 1278 delete user; 1279 debug_printf("REG: Out of memory\n"); 1280 return B_NO_MEMORY; 1281 } 1282 } 1283 1284 return B_OK; 1285 } 1286 1287 1288 status_t 1289 AuthenticationManager::_InitGroupDB() 1290 { 1291 FILE* file = fopen(kGroupFile, "r"); 1292 if (file == NULL) { 1293 debug_printf("REG: Failed to open group DB file \"%s\": %s\n", 1294 kGroupFile, strerror(errno)); 1295 return errno; 1296 } 1297 CObjectDeleter<FILE, int> _(file, fclose); 1298 1299 char lineBuffer[LINE_MAX]; 1300 while (char* line = fgets(lineBuffer, sizeof(lineBuffer), file)) { 1301 if (strlen(line) == 0) 1302 continue; 1303 1304 char* name; 1305 char* password; 1306 gid_t gid; 1307 char* members[MAX_GROUP_MEMBER_COUNT]; 1308 int memberCount; 1309 1310 1311 status_t error = parse_group_line(line, name, password, gid, members, 1312 memberCount); 1313 if (error != B_OK) { 1314 debug_printf("REG: Unparsable line in group DB file: \"%s\"\n", 1315 strerror(errno)); 1316 continue; 1317 } 1318 1319 Group* group = NULL; 1320 try { 1321 group = new Group(name, password, gid, members, memberCount); 1322 } catch (...) { 1323 } 1324 1325 if (group == NULL || fGroupDB->AddGroup(group) != B_OK) { 1326 delete group; 1327 debug_printf("REG: Out of memory\n"); 1328 return B_NO_MEMORY; 1329 } 1330 } 1331 1332 return B_OK; 1333 } 1334 1335 1336 status_t 1337 AuthenticationManager::_InitShadowPwdDB() 1338 { 1339 FILE* file = fopen(kShadowPwdFile, "r"); 1340 if (file == NULL) { 1341 debug_printf("REG: Failed to open shadow passwd DB file \"%s\": %s\n", 1342 kShadowPwdFile, strerror(errno)); 1343 return errno; 1344 } 1345 CObjectDeleter<FILE, int> _(file, fclose); 1346 1347 char lineBuffer[LINE_MAX]; 1348 while (char* line = fgets(lineBuffer, sizeof(lineBuffer), file)) { 1349 if (strlen(line) == 0) 1350 continue; 1351 1352 char* name; 1353 char* password; 1354 int lastChanged; 1355 int min; 1356 int max; 1357 int warn; 1358 int inactive; 1359 int expiration; 1360 int flags; 1361 1362 status_t error = parse_shadow_pwd_line(line, name, password, 1363 lastChanged, min, max, warn, inactive, expiration, flags); 1364 if (error != B_OK) { 1365 debug_printf("REG: Unparsable line in shadow passwd DB file: " 1366 "\"%s\"\n", strerror(errno)); 1367 continue; 1368 } 1369 1370 User* user = fUserDB->UserByName(name); 1371 if (user == NULL) { 1372 debug_printf("REG: shadow pwd entry for unknown user \"%s\"\n", 1373 name); 1374 continue; 1375 } 1376 1377 try { 1378 user->SetShadowInfo(password, lastChanged, min, max, warn, inactive, 1379 expiration, flags); 1380 } catch (...) { 1381 debug_printf("REG: Out of memory\n"); 1382 return B_NO_MEMORY; 1383 } 1384 } 1385 1386 return B_OK; 1387 } 1388 1389 1390 void 1391 AuthenticationManager::_InvalidatePasswdDBReply() 1392 { 1393 fPasswdDBReply->SetTo(1); 1394 } 1395 1396 1397 void 1398 AuthenticationManager::_InvalidateGroupDBReply() 1399 { 1400 fGroupDBReply->SetTo(1); 1401 } 1402 1403 1404 void 1405 AuthenticationManager::_InvalidateShadowPwdDBReply() 1406 { 1407 fShadowPwdDBReply->SetTo(1); 1408 } 1409