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 FILE* passwdFile = fopen(kPasswdFile, "w"); 568 if (passwdFile == NULL) { 569 debug_printf("REG: Failed to open passwd file \"%s\" for " 570 "writing: %s\n", kPasswdFile, strerror(errno)); 571 } 572 FileCloser _1(passwdFile); 573 574 FILE* shadowFile = fopen(kShadowPwdFile, "w"); 575 if (shadowFile == NULL) { 576 debug_printf("REG: Failed to open shadow passwd file \"%s\" for " 577 "writing: %s\n", kShadowPwdFile, strerror(errno)); 578 } 579 FileCloser _2(shadowFile); 580 581 // write users 582 for (map<uid_t, User*>::const_iterator it = fUsersByID.begin(); 583 it != fUsersByID.end(); ++it) { 584 User* user = it->second; 585 user->WritePasswdLine(passwdFile); 586 user->WriteShadowPwdLine(shadowFile); 587 } 588 } 589 590 private: 591 map<uid_t, User*> fUsersByID; 592 map<string, User*> fUsersByName; 593 }; 594 595 596 class AuthenticationManager::GroupDB { 597 public: 598 status_t AddGroup(Group* group) 599 { 600 try { 601 fGroupsByID[group->GID()] = group; 602 } catch (...) { 603 return B_NO_MEMORY; 604 } 605 606 try { 607 fGroupsByName[group->Name()] = group; 608 } catch (...) { 609 fGroupsByID.erase(fGroupsByID.find(group->GID())); 610 return B_NO_MEMORY; 611 } 612 613 return B_OK; 614 } 615 616 void RemoveGroup(Group* group) 617 { 618 fGroupsByID.erase(fGroupsByID.find(group->GID())); 619 fGroupsByName.erase(fGroupsByName.find(group->Name())); 620 } 621 622 bool UserRemoved(const std::string& user) 623 { 624 bool changed = false; 625 for (map<gid_t, Group*>::const_iterator it = fGroupsByID.begin(); 626 it != fGroupsByID.end(); ++it) { 627 Group* group = it->second; 628 changed |= group->MemberRemoved(user); 629 } 630 return changed; 631 } 632 633 Group* GroupByID(gid_t gid) const 634 { 635 map<gid_t, Group*>::const_iterator it = fGroupsByID.find(gid); 636 return (it == fGroupsByID.end() ? NULL : it->second); 637 } 638 639 Group* GroupByName(const char* name) const 640 { 641 map<string, Group*>::const_iterator it = fGroupsByName.find(name); 642 return (it == fGroupsByName.end() ? NULL : it->second); 643 } 644 645 int32 GetUserGroups(const char* name, gid_t* groups, int maxCount) 646 { 647 int count = 0; 648 649 for (map<gid_t, Group*>::const_iterator it = fGroupsByID.begin(); 650 it != fGroupsByID.end(); ++it) { 651 Group* group = it->second; 652 if (group->HasMember(name)) { 653 if (count < maxCount) 654 groups[count] = group->GID(); 655 count++; 656 } 657 } 658 659 return count; 660 } 661 662 663 int32 WriteFlatGroupDB(FlatStore& store) const 664 { 665 int32 count = fGroupsByID.size(); 666 667 size_t entriesSpace = sizeof(group*) * count; 668 size_t offset = store.ReserveSpace(entriesSpace, true); 669 group** entries = new group*[count]; 670 ArrayDeleter<group*> _(entries); 671 672 int32 index = 0; 673 for (map<gid_t, Group*>::const_iterator it = fGroupsByID.begin(); 674 it != fGroupsByID.end(); ++it) { 675 entries[index++] = it->second->WriteFlatGroup(store); 676 } 677 678 store.WriteData(offset, entries, entriesSpace); 679 680 return count; 681 } 682 683 void WriteToDisk() 684 { 685 // rename the old files 686 string groupBackup(kGroupFile); 687 groupBackup += ".old"; 688 689 rename(kGroupFile, groupBackup.c_str()); 690 // Don't check errors. We can't do anything anyway. 691 692 // open file 693 FILE* groupFile = fopen(kGroupFile, "w"); 694 if (groupFile == NULL) { 695 debug_printf("REG: Failed to open group file \"%s\" for " 696 "writing: %s\n", kGroupFile, strerror(errno)); 697 } 698 FileCloser _1(groupFile); 699 700 // write groups 701 for (map<gid_t, Group*>::const_iterator it = fGroupsByID.begin(); 702 it != fGroupsByID.end(); ++it) { 703 Group* group = it->second; 704 group->WriteGroupLine(groupFile); 705 } 706 } 707 708 private: 709 map<uid_t, Group*> fGroupsByID; 710 map<string, Group*> fGroupsByName; 711 }; 712 713 714 AuthenticationManager::AuthenticationManager() 715 : 716 fRequestPort(-1), 717 fRequestThread(-1), 718 fUserDB(NULL), 719 fGroupDB(NULL), 720 fPasswdDBReply(NULL), 721 fGroupDBReply(NULL), 722 fShadowPwdDBReply(NULL) 723 { 724 } 725 726 727 AuthenticationManager::~AuthenticationManager() 728 { 729 // Quit the request thread and wait for it to finish 730 write_port(fRequestPort, 'quit', NULL, 0); 731 wait_for_thread(fRequestThread, NULL); 732 733 delete fUserDB; 734 delete fGroupDB; 735 delete fPasswdDBReply; 736 delete fGroupDBReply; 737 delete fShadowPwdDBReply; 738 } 739 740 741 status_t 742 AuthenticationManager::Init() 743 { 744 fUserDB = new(std::nothrow) UserDB; 745 fGroupDB = new(std::nothrow) GroupDB; 746 fPasswdDBReply = new(std::nothrow) KMessage(1); 747 fGroupDBReply = new(std::nothrow) KMessage(1); 748 fShadowPwdDBReply = new(std::nothrow) KMessage(1); 749 750 if (fUserDB == NULL || fGroupDB == NULL || fPasswdDBReply == NULL 751 || fGroupDBReply == NULL || fShadowPwdDBReply == NULL) { 752 return B_NO_MEMORY; 753 } 754 755 fRequestPort = BLaunchRoster().GetPort( 756 B_REGISTRAR_AUTHENTICATION_PORT_NAME); 757 if (fRequestPort < 0) 758 return fRequestPort; 759 760 fRequestThread = spawn_thread(&_RequestThreadEntry, 761 "authentication manager", B_NORMAL_PRIORITY + 1, this); 762 if (fRequestThread < 0) 763 return fRequestThread; 764 765 resume_thread(fRequestThread); 766 767 return B_OK; 768 } 769 770 771 status_t 772 AuthenticationManager::_RequestThreadEntry(void* data) 773 { 774 return ((AuthenticationManager*)data)->_RequestThread(); 775 } 776 777 778 status_t 779 AuthenticationManager::_RequestThread() 780 { 781 // read the DB files 782 _InitPasswdDB(); 783 _InitGroupDB(); 784 _InitShadowPwdDB(); 785 786 // get our team ID 787 team_id registrarTeam = -1; 788 { 789 thread_info info; 790 if (get_thread_info(find_thread(NULL), &info) == B_OK) 791 registrarTeam = info.team; 792 } 793 794 // request loop 795 while (true) { 796 KMessage message; 797 port_message_info messageInfo; 798 status_t error = message.ReceiveFrom(fRequestPort, -1, &messageInfo); 799 if (error != B_OK) 800 return B_OK; 801 802 bool isRoot = (messageInfo.sender == 0); 803 804 switch (message.What()) { 805 case B_REG_GET_PASSWD_DB: 806 { 807 // lazily build the reply 808 try { 809 if (fPasswdDBReply->What() == 1) { 810 FlatStore store; 811 int32 count = fUserDB->WriteFlatPasswdDB(store); 812 if (fPasswdDBReply->AddInt32("count", count) != B_OK 813 || fPasswdDBReply->AddData("entries", B_RAW_TYPE, 814 store.Buffer(), store.BufferLength(), 815 false) != B_OK) { 816 error = B_NO_MEMORY; 817 } 818 819 fPasswdDBReply->SetWhat(0); 820 } 821 } catch (...) { 822 error = B_NO_MEMORY; 823 } 824 825 if (error == B_OK) { 826 message.SendReply(fPasswdDBReply, -1, -1, 0, registrarTeam); 827 } else { 828 _InvalidatePasswdDBReply(); 829 KMessage reply(error); 830 message.SendReply(&reply, -1, -1, 0, registrarTeam); 831 } 832 833 break; 834 } 835 836 case B_REG_GET_GROUP_DB: 837 { 838 // lazily build the reply 839 try { 840 if (fGroupDBReply->What() == 1) { 841 FlatStore store; 842 int32 count = fGroupDB->WriteFlatGroupDB(store); 843 if (fGroupDBReply->AddInt32("count", count) != B_OK 844 || fGroupDBReply->AddData("entries", B_RAW_TYPE, 845 store.Buffer(), store.BufferLength(), 846 false) != B_OK) { 847 error = B_NO_MEMORY; 848 } 849 850 fGroupDBReply->SetWhat(0); 851 } 852 } catch (...) { 853 error = B_NO_MEMORY; 854 } 855 856 if (error == B_OK) { 857 message.SendReply(fGroupDBReply, -1, -1, 0, registrarTeam); 858 } else { 859 _InvalidateGroupDBReply(); 860 KMessage reply(error); 861 message.SendReply(&reply, -1, -1, 0, registrarTeam); 862 } 863 864 break; 865 } 866 867 868 case B_REG_GET_SHADOW_PASSWD_DB: 869 { 870 // only root may see the shadow passwd 871 if (!isRoot) 872 error = EPERM; 873 874 // lazily build the reply 875 try { 876 if (error == B_OK && fShadowPwdDBReply->What() == 1) { 877 FlatStore store; 878 int32 count = fUserDB->WriteFlatShadowDB(store); 879 if (fShadowPwdDBReply->AddInt32("count", count) != B_OK 880 || fShadowPwdDBReply->AddData("entries", B_RAW_TYPE, 881 store.Buffer(), store.BufferLength(), 882 false) != B_OK) { 883 error = B_NO_MEMORY; 884 } 885 886 fShadowPwdDBReply->SetWhat(0); 887 } 888 } catch (...) { 889 error = B_NO_MEMORY; 890 } 891 892 if (error == B_OK) { 893 message.SendReply(fShadowPwdDBReply, -1, -1, 0, 894 registrarTeam); 895 } else { 896 _InvalidateShadowPwdDBReply(); 897 KMessage reply(error); 898 message.SendReply(&reply, -1, -1, 0, registrarTeam); 899 } 900 901 break; 902 } 903 904 case B_REG_GET_USER: 905 { 906 User* user = NULL; 907 int32 uid; 908 const char* name; 909 910 // find user 911 if (message.FindInt32("uid", &uid) == B_OK) { 912 user = fUserDB->UserByID(uid); 913 } else if (message.FindString("name", &name) == B_OK) { 914 user = fUserDB->UserByName(name); 915 } else { 916 error = B_BAD_VALUE; 917 } 918 919 if (error == B_OK && user == NULL) 920 error = ENOENT; 921 922 bool getShadowPwd = message.GetBool("shadow", false); 923 924 // only root may see the shadow passwd 925 if (error == B_OK && getShadowPwd && !isRoot) 926 error = EPERM; 927 928 // add user to message 929 KMessage reply; 930 if (error == B_OK) 931 error = user->WriteToMessage(reply, getShadowPwd); 932 933 // send reply 934 reply.SetWhat(error); 935 message.SendReply(&reply, -1, -1, 0, registrarTeam); 936 937 break; 938 } 939 940 case B_REG_GET_GROUP: 941 { 942 Group* group = NULL; 943 int32 gid; 944 const char* name; 945 946 // find group 947 if (message.FindInt32("gid", &gid) == B_OK) { 948 group = fGroupDB->GroupByID(gid); 949 } else if (message.FindString("name", &name) == B_OK) { 950 group = fGroupDB->GroupByName(name); 951 } else { 952 error = B_BAD_VALUE; 953 } 954 955 if (error == B_OK && group == NULL) 956 error = ENOENT; 957 958 // add group to message 959 KMessage reply; 960 if (error == B_OK) 961 error = group->WriteToMessage(reply); 962 963 // send reply 964 reply.SetWhat(error); 965 message.SendReply(&reply, -1, -1, 0, registrarTeam); 966 967 break; 968 } 969 970 case B_REG_GET_USER_GROUPS: 971 { 972 // get user name 973 const char* name; 974 int32 maxCount; 975 if (message.FindString("name", &name) != B_OK 976 || message.FindInt32("max count", &maxCount) != B_OK 977 || maxCount <= 0) { 978 error = B_BAD_VALUE; 979 } 980 981 // get groups 982 gid_t groups[NGROUPS_MAX + 1]; 983 int32 count = 0; 984 if (error == B_OK) { 985 maxCount = min_c(maxCount, NGROUPS_MAX + 1); 986 count = fGroupDB->GetUserGroups(name, groups, maxCount); 987 } 988 989 // add groups to message 990 KMessage reply; 991 if (error == B_OK) { 992 if (reply.AddInt32("count", count) != B_OK 993 || reply.AddData("groups", B_INT32_TYPE, 994 groups, min_c(maxCount, count) * sizeof(gid_t), 995 false) != B_OK) { 996 error = B_NO_MEMORY; 997 } 998 } 999 1000 // send reply 1001 reply.SetWhat(error); 1002 message.SendReply(&reply, -1, -1, 0, registrarTeam); 1003 1004 break; 1005 } 1006 1007 case B_REG_UPDATE_USER: 1008 { 1009 // find user 1010 User* user = NULL; 1011 int32 uid; 1012 const char* name; 1013 1014 if (message.FindInt32("uid", &uid) == B_OK) { 1015 user = fUserDB->UserByID(uid); 1016 } else if (message.FindString("name", &name) == B_OK) { 1017 user = fUserDB->UserByName(name); 1018 } else { 1019 error = B_BAD_VALUE; 1020 } 1021 1022 // only root can change anything 1023 if (error == B_OK && !isRoot) 1024 error = EPERM; 1025 1026 // check addUser vs. existing user 1027 bool addUser = message.GetBool("add user", false); 1028 if (error == B_OK) { 1029 if (addUser) { 1030 if (user != NULL) 1031 error = EEXIST; 1032 } else if (user == NULL) 1033 error = ENOENT; 1034 } 1035 1036 // apply all changes 1037 if (error == B_OK) { 1038 // clone the user object and update it from the message 1039 User* oldUser = user; 1040 user = NULL; 1041 try { 1042 user = (oldUser != NULL ? new User(*oldUser) 1043 : new User); 1044 user->UpdateFromMessage(message); 1045 1046 // uid and name should remain the same 1047 if (oldUser != NULL) { 1048 if (oldUser->UID() != user->UID() 1049 || oldUser->Name() != user->Name()) { 1050 error = B_BAD_VALUE; 1051 } 1052 } 1053 1054 // replace the old user and write DBs to disk 1055 if (error == B_OK) { 1056 fUserDB->AddUser(user); 1057 fUserDB->WriteToDisk(); 1058 _InvalidatePasswdDBReply(); 1059 _InvalidateShadowPwdDBReply(); 1060 } 1061 } catch (...) { 1062 error = B_NO_MEMORY; 1063 } 1064 1065 if (error == B_OK) 1066 delete oldUser; 1067 else 1068 delete user; 1069 } 1070 1071 // send reply 1072 KMessage reply; 1073 reply.SetWhat(error); 1074 message.SendReply(&reply, -1, -1, 0, registrarTeam); 1075 1076 break; 1077 } 1078 1079 case B_REG_DELETE_USER: 1080 { 1081 // find user 1082 User* user = NULL; 1083 int32 uid; 1084 const char* name; 1085 1086 if (message.FindInt32("uid", &uid) == B_OK) { 1087 user = fUserDB->UserByID(uid); 1088 } else if (message.FindString("name", &name) == B_OK) { 1089 user = fUserDB->UserByName(name); 1090 } else { 1091 error = B_BAD_VALUE; 1092 } 1093 1094 if (error == B_OK && user == NULL) 1095 error = ENOENT; 1096 1097 // only root can change anything 1098 if (error == B_OK && !isRoot) 1099 error = EPERM; 1100 1101 // apply the change 1102 if (error == B_OK) { 1103 std::string userName = user->Name(); 1104 1105 fUserDB->RemoveUser(user); 1106 fUserDB->WriteToDisk(); 1107 _InvalidatePasswdDBReply(); 1108 _InvalidateShadowPwdDBReply(); 1109 1110 if (fGroupDB->UserRemoved(userName)) { 1111 fGroupDB->WriteToDisk(); 1112 _InvalidateGroupDBReply(); 1113 } 1114 } 1115 1116 // send reply 1117 KMessage reply; 1118 reply.SetWhat(error); 1119 message.SendReply(&reply, -1, -1, 0, registrarTeam); 1120 1121 break; 1122 } 1123 1124 case B_REG_UPDATE_GROUP: 1125 { 1126 // find group 1127 Group* group = NULL; 1128 int32 gid; 1129 const char* name; 1130 1131 if (message.FindInt32("gid", &gid) == B_OK) { 1132 group = fGroupDB->GroupByID(gid); 1133 } else if (message.FindString("name", &name) == B_OK) { 1134 group = fGroupDB->GroupByName(name); 1135 } else { 1136 error = B_BAD_VALUE; 1137 } 1138 1139 // only root can change anything 1140 if (error == B_OK && !isRoot) 1141 error = EPERM; 1142 1143 // check addGroup vs. existing group 1144 bool addGroup = message.GetBool("add group", false); 1145 if (error == B_OK) { 1146 if (addGroup) { 1147 if (group != NULL) 1148 error = EEXIST; 1149 } else if (group == NULL) 1150 error = ENOENT; 1151 } 1152 1153 // apply all changes 1154 if (error == B_OK) { 1155 // clone the group object and update it from the message 1156 Group* oldGroup = group; 1157 group = NULL; 1158 try { 1159 group = (oldGroup != NULL ? new Group(*oldGroup) 1160 : new Group); 1161 group->UpdateFromMessage(message); 1162 1163 // gid and name should remain the same 1164 if (oldGroup != NULL) { 1165 if (oldGroup->GID() != group->GID() 1166 || oldGroup->Name() != group->Name()) { 1167 error = B_BAD_VALUE; 1168 } 1169 } 1170 1171 // replace the old group and write DBs to disk 1172 if (error == B_OK) { 1173 fGroupDB->AddGroup(group); 1174 fGroupDB->WriteToDisk(); 1175 _InvalidateGroupDBReply(); 1176 } 1177 } catch (...) { 1178 error = B_NO_MEMORY; 1179 } 1180 1181 if (error == B_OK) 1182 delete oldGroup; 1183 else 1184 delete group; 1185 } 1186 1187 // send reply 1188 KMessage reply; 1189 reply.SetWhat(error); 1190 message.SendReply(&reply, -1, -1, 0, registrarTeam); 1191 1192 break; 1193 } 1194 1195 case B_REG_DELETE_GROUP: 1196 { 1197 // find group 1198 Group* group = NULL; 1199 int32 gid; 1200 const char* name; 1201 1202 if (message.FindInt32("gid", &gid) == B_OK) { 1203 group = fGroupDB->GroupByID(gid); 1204 } else if (message.FindString("name", &name) == B_OK) { 1205 group = fGroupDB->GroupByName(name); 1206 } else { 1207 error = B_BAD_VALUE; 1208 } 1209 1210 if (error == B_OK && group == NULL) 1211 error = ENOENT; 1212 1213 // only root can change anything 1214 if (error == B_OK && !isRoot) 1215 error = EPERM; 1216 1217 // apply the change 1218 if (error == B_OK) { 1219 fGroupDB->RemoveGroup(group); 1220 fGroupDB->WriteToDisk(); 1221 _InvalidateGroupDBReply(); 1222 } 1223 1224 // send reply 1225 KMessage reply; 1226 reply.SetWhat(error); 1227 message.SendReply(&reply, -1, -1, 0, registrarTeam); 1228 1229 break; 1230 } 1231 1232 default: 1233 debug_printf("REG: invalid message: %" B_PRIu32 "\n", 1234 message.What()); 1235 } 1236 } 1237 } 1238 1239 1240 status_t 1241 AuthenticationManager::_InitPasswdDB() 1242 { 1243 FILE* file = fopen(kPasswdFile, "r"); 1244 if (file == NULL) { 1245 debug_printf("REG: Failed to open passwd DB file \"%s\": %s\n", 1246 kPasswdFile, strerror(errno)); 1247 return errno; 1248 } 1249 FileCloser _(file); 1250 1251 char lineBuffer[LINE_MAX]; 1252 while (char* line = fgets(lineBuffer, sizeof(lineBuffer), file)) { 1253 if (strlen(line) == 0) 1254 continue; 1255 1256 char* name; 1257 char* password; 1258 uid_t uid; 1259 gid_t gid; 1260 char* home; 1261 char* shell; 1262 char* realName; 1263 1264 status_t error = parse_passwd_line(line, name, password, uid, gid, 1265 home, shell, realName); 1266 if (error != B_OK) { 1267 debug_printf("REG: Unparsable line in passwd DB file: \"%s\"\n", 1268 strerror(errno)); 1269 continue; 1270 } 1271 1272 User* user = NULL; 1273 try { 1274 user = new User(name, password, uid, gid, home, shell, realName); 1275 } catch (...) { 1276 } 1277 1278 if (user == NULL || fUserDB->AddUser(user) != B_OK) { 1279 delete user; 1280 debug_printf("REG: Out of memory\n"); 1281 return B_NO_MEMORY; 1282 } 1283 } 1284 1285 return B_OK; 1286 } 1287 1288 1289 status_t 1290 AuthenticationManager::_InitGroupDB() 1291 { 1292 FILE* file = fopen(kGroupFile, "r"); 1293 if (file == NULL) { 1294 debug_printf("REG: Failed to open group DB file \"%s\": %s\n", 1295 kGroupFile, strerror(errno)); 1296 return errno; 1297 } 1298 FileCloser _(file); 1299 1300 char lineBuffer[LINE_MAX]; 1301 while (char* line = fgets(lineBuffer, sizeof(lineBuffer), file)) { 1302 if (strlen(line) == 0) 1303 continue; 1304 1305 char* name; 1306 char* password; 1307 gid_t gid; 1308 char* members[MAX_GROUP_MEMBER_COUNT]; 1309 int memberCount; 1310 1311 1312 status_t error = parse_group_line(line, name, password, gid, members, 1313 memberCount); 1314 if (error != B_OK) { 1315 debug_printf("REG: Unparsable line in group DB file: \"%s\"\n", 1316 strerror(errno)); 1317 continue; 1318 } 1319 1320 Group* group = NULL; 1321 try { 1322 group = new Group(name, password, gid, members, memberCount); 1323 } catch (...) { 1324 } 1325 1326 if (group == NULL || fGroupDB->AddGroup(group) != B_OK) { 1327 delete group; 1328 debug_printf("REG: Out of memory\n"); 1329 return B_NO_MEMORY; 1330 } 1331 } 1332 1333 return B_OK; 1334 } 1335 1336 1337 status_t 1338 AuthenticationManager::_InitShadowPwdDB() 1339 { 1340 FILE* file = fopen(kShadowPwdFile, "r"); 1341 if (file == NULL) { 1342 debug_printf("REG: Failed to open shadow passwd DB file \"%s\": %s\n", 1343 kShadowPwdFile, strerror(errno)); 1344 return errno; 1345 } 1346 FileCloser _(file); 1347 1348 char lineBuffer[LINE_MAX]; 1349 while (char* line = fgets(lineBuffer, sizeof(lineBuffer), file)) { 1350 if (strlen(line) == 0) 1351 continue; 1352 1353 char* name; 1354 char* password; 1355 int lastChanged; 1356 int min; 1357 int max; 1358 int warn; 1359 int inactive; 1360 int expiration; 1361 int flags; 1362 1363 status_t error = parse_shadow_pwd_line(line, name, password, 1364 lastChanged, min, max, warn, inactive, expiration, flags); 1365 if (error != B_OK) { 1366 debug_printf("REG: Unparsable line in shadow passwd DB file: " 1367 "\"%s\"\n", strerror(errno)); 1368 continue; 1369 } 1370 1371 User* user = fUserDB->UserByName(name); 1372 if (user == NULL) { 1373 debug_printf("REG: shadow pwd entry for unknown user \"%s\"\n", 1374 name); 1375 continue; 1376 } 1377 1378 try { 1379 user->SetShadowInfo(password, lastChanged, min, max, warn, inactive, 1380 expiration, flags); 1381 } catch (...) { 1382 debug_printf("REG: Out of memory\n"); 1383 return B_NO_MEMORY; 1384 } 1385 } 1386 1387 return B_OK; 1388 } 1389 1390 1391 void 1392 AuthenticationManager::_InvalidatePasswdDBReply() 1393 { 1394 fPasswdDBReply->SetTo(1); 1395 } 1396 1397 1398 void 1399 AuthenticationManager::_InvalidateGroupDBReply() 1400 { 1401 fGroupDBReply->SetTo(1); 1402 } 1403 1404 1405 void 1406 AuthenticationManager::_InvalidateShadowPwdDBReply() 1407 { 1408 fShadowPwdDBReply->SetTo(1); 1409 } 1410