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