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 #include "AuthenticationManager.h" 7 8 #include <errno.h> 9 #include <stdlib.h> 10 #include <stdio.h> 11 #include <sys/param.h> 12 13 #include <map> 14 #include <new> 15 #include <set> 16 #include <string> 17 18 #include <DataIO.h> 19 #include <StringList.h> 20 21 #include <AutoDeleter.h> 22 #include <RegistrarDefs.h> 23 24 #include <libroot_private.h> 25 #include <user_group.h> 26 #include <util/KMessage.h> 27 28 29 using std::map; 30 using std::string; 31 32 using namespace BPrivate; 33 34 35 typedef std::set<std::string> StringSet; 36 37 38 class AuthenticationManager::FlatStore { 39 public: 40 FlatStore() 41 : fSize(0) 42 { 43 fBuffer.SetBlockSize(1024); 44 } 45 46 void WriteData(size_t offset, const void* data, size_t length) 47 { 48 ssize_t result = fBuffer.WriteAt(offset, data, length); 49 if (result < 0) 50 throw status_t(result); 51 } 52 53 template<typename Type> 54 void WriteData(size_t offset, const Type& data) 55 { 56 WriteData(&data, sizeof(Type)); 57 } 58 59 size_t ReserveSpace(size_t length, bool align) 60 { 61 if (align) 62 fSize = _ALIGN(fSize); 63 64 size_t pos = fSize; 65 fSize += length; 66 67 return pos; 68 } 69 70 void* AppendData(const void* data, size_t length, bool align) 71 { 72 size_t pos = ReserveSpace(length, align); 73 WriteData(pos, data, length); 74 return (void*)(addr_t)pos; 75 } 76 77 template<typename Type> 78 Type* AppendData(const Type& data) 79 { 80 return (Type*)AppendData(&data, sizeof(Type), true); 81 } 82 83 char* AppendString(const char* string) 84 { 85 return (char*)AppendData(string, strlen(string) + 1, false); 86 } 87 88 char* AppendString(const string& str) 89 { 90 return (char*)AppendData(str.c_str(), str.length() + 1, false); 91 } 92 93 const void* Buffer() const 94 { 95 return fBuffer.Buffer(); 96 } 97 98 size_t BufferLength() const 99 { 100 return fSize; 101 } 102 103 private: 104 BMallocIO fBuffer; 105 size_t fSize; 106 }; 107 108 109 class AuthenticationManager::User { 110 public: 111 User() 112 : 113 fUID(0), 114 fGID(0), 115 fLastChanged(0), 116 fMin(-1), 117 fMax(-1), 118 fWarn(-1), 119 fInactive(-1), 120 fExpiration(-1), 121 fFlags(0) 122 { 123 } 124 125 User(const char* name, const char* password, uid_t uid, gid_t gid, 126 const char* home, const char* shell, const char* realName) 127 : 128 fUID(uid), 129 fGID(gid), 130 fName(name), 131 fPassword(password), 132 fHome(home), 133 fShell(shell), 134 fRealName(realName), 135 fLastChanged(0), 136 fMin(-1), 137 fMax(-1), 138 fWarn(-1), 139 fInactive(-1), 140 fExpiration(-1), 141 fFlags(0) 142 { 143 } 144 145 User(const User& other) 146 : 147 fUID(other.fUID), 148 fGID(other.fGID), 149 fName(other.fName), 150 fPassword(other.fPassword), 151 fHome(other.fHome), 152 fShell(other.fShell), 153 fRealName(other.fRealName), 154 fShadowPassword(other.fShadowPassword), 155 fLastChanged(other.fLastChanged), 156 fMin(other.fMin), 157 fMax(other.fMax), 158 fWarn(other.fWarn), 159 fInactive(other.fInactive), 160 fExpiration(other.fExpiration), 161 fFlags(other.fFlags) 162 { 163 } 164 165 const string& Name() const { return fName; } 166 const uid_t UID() const { return fUID; } 167 168 void SetShadowInfo(const char* password, int lastChanged, int min, int max, 169 int warn, int inactive, int expiration, int flags) 170 { 171 fShadowPassword = password; 172 fLastChanged = lastChanged; 173 fMin = min; 174 fMax = max; 175 fWarn = warn; 176 fInactive = inactive; 177 fExpiration = expiration; 178 fFlags = flags; 179 } 180 181 void UpdateFromMessage(const KMessage& message) 182 { 183 int32 intValue; 184 const char* stringValue; 185 186 if (message.FindInt32("uid", &intValue) == B_OK) 187 fUID = intValue; 188 189 if (message.FindInt32("gid", &intValue) == B_OK) 190 fGID = intValue; 191 192 if (message.FindString("name", &stringValue) == B_OK) 193 fName = stringValue; 194 195 if (message.FindString("password", &stringValue) == B_OK) 196 fPassword = stringValue; 197 198 if (message.FindString("home", &stringValue) == B_OK) 199 fHome = stringValue; 200 201 if (message.FindString("shell", &stringValue) == B_OK) 202 fShell = stringValue; 203 204 if (message.FindString("real name", &stringValue) == B_OK) 205 fRealName = stringValue; 206 207 if (message.FindString("shadow password", &stringValue) == B_OK) { 208 fShadowPassword = stringValue; 209 // TODO: 210 // fLastChanged = now; 211 } 212 213 if (message.FindInt32("last changed", &intValue) == B_OK) 214 fLastChanged = intValue; 215 216 if (message.FindInt32("min", &intValue) == B_OK) 217 fMin = intValue; 218 219 if (message.FindInt32("max", &intValue) == B_OK) 220 fMax = intValue; 221 222 if (message.FindInt32("warn", &intValue) == B_OK) 223 fWarn = intValue; 224 225 if (message.FindInt32("inactive", &intValue) == B_OK) 226 fInactive = intValue; 227 228 if (message.FindInt32("expiration", &intValue) == B_OK) 229 fExpiration = intValue; 230 231 if (message.FindInt32("flags", &intValue) == B_OK) 232 fFlags = intValue; 233 } 234 235 passwd* WriteFlatPasswd(FlatStore& store) const 236 { 237 struct passwd passwd; 238 239 passwd.pw_uid = fUID; 240 passwd.pw_gid = fGID; 241 passwd.pw_name = store.AppendString(fName); 242 passwd.pw_passwd = store.AppendString(fPassword); 243 passwd.pw_dir = store.AppendString(fHome); 244 passwd.pw_shell = store.AppendString(fShell); 245 passwd.pw_gecos = store.AppendString(fRealName); 246 247 return store.AppendData(passwd); 248 } 249 250 spwd* WriteFlatShadowPwd(FlatStore& store) const 251 { 252 struct spwd spwd; 253 254 spwd.sp_namp = store.AppendString(fName); 255 spwd.sp_pwdp = store.AppendString(fShadowPassword); 256 spwd.sp_lstchg = fLastChanged; 257 spwd.sp_min = fMin; 258 spwd.sp_max = fMax; 259 spwd.sp_warn = fWarn; 260 spwd.sp_inact = fInactive; 261 spwd.sp_expire = fExpiration; 262 spwd.sp_flag = fFlags; 263 264 return store.AppendData(spwd); 265 } 266 267 status_t WriteToMessage(KMessage& message, bool addShadowPwd) 268 { 269 status_t error; 270 if ((error = message.AddInt32("uid", fUID)) != B_OK 271 || (error = message.AddInt32("gid", fGID)) != B_OK 272 || (error = message.AddString("name", fName.c_str())) != B_OK 273 || (error = message.AddString("password", fPassword.c_str())) 274 != B_OK 275 || (error = message.AddString("home", fHome.c_str())) != B_OK 276 || (error = message.AddString("shell", fShell.c_str())) != B_OK 277 || (error = message.AddString("real name", fRealName.c_str())) 278 != B_OK) { 279 return error; 280 } 281 282 if (!addShadowPwd) 283 return B_OK; 284 285 if ((error = message.AddString("shadow password", 286 fShadowPassword.c_str())) != B_OK 287 || (error = message.AddInt32("last changed", fLastChanged)) != B_OK 288 || (error = message.AddInt32("min", fMin)) != B_OK 289 || (error = message.AddInt32("max", fMax)) != B_OK 290 || (error = message.AddInt32("warn", fWarn)) != B_OK 291 || (error = message.AddInt32("inactive", fInactive)) != B_OK 292 || (error = message.AddInt32("expiration", fExpiration)) != B_OK 293 || (error = message.AddInt32("flags", fFlags)) != B_OK) { 294 return error; 295 } 296 297 return B_OK; 298 } 299 300 void WritePasswdLine(FILE* file) 301 { 302 fprintf(file, "%s:%s:%d:%d:%s:%s:%s\n", 303 fName.c_str(), fPassword.c_str(), (int)fUID, (int)fGID, 304 fRealName.c_str(), fHome.c_str(), fShell.c_str()); 305 } 306 307 void WriteShadowPwdLine(FILE* file) 308 { 309 fprintf(file, "%s:%s:%d:", fName.c_str(), fShadowPassword.c_str(), 310 fLastChanged); 311 312 // The following values are supposed to be printed as empty strings, 313 // if negative. 314 int values[5] = { fMin, fMax, fWarn, fInactive, fExpiration }; 315 for (int i = 0; i < 5; i++) { 316 if (values[i] >= 0) 317 fprintf(file, "%d", values[i]); 318 fprintf(file, ":"); 319 } 320 321 fprintf(file, "%d\n", fFlags); 322 } 323 324 private: 325 uid_t fUID; 326 gid_t fGID; 327 string fName; 328 string fPassword; 329 string fHome; 330 string fShell; 331 string fRealName; 332 string fShadowPassword; 333 int fLastChanged; 334 int fMin; 335 int fMax; 336 int fWarn; 337 int fInactive; 338 int fExpiration; 339 int fFlags; 340 }; 341 342 343 class AuthenticationManager::Group { 344 public: 345 Group() 346 : 347 fGID(0), 348 fName(), 349 fPassword(), 350 fMembers() 351 { 352 } 353 354 Group(const char* name, const char* password, gid_t gid, 355 const char* const* members, int memberCount) 356 : 357 fGID(gid), 358 fName(name), 359 fPassword(password), 360 fMembers() 361 { 362 for (int i = 0; i < memberCount; i++) 363 fMembers.insert(members[i]); 364 } 365 366 ~Group() 367 { 368 } 369 370 const string& Name() const { return fName; } 371 const gid_t GID() const { return fGID; } 372 373 bool HasMember(const char* name) 374 { 375 try { 376 return fMembers.find(name) != fMembers.end(); 377 } catch (...) { 378 return false; 379 } 380 } 381 382 bool MemberRemoved(const std::string& name) 383 { 384 return fMembers.erase(name) > 0; 385 } 386 387 void UpdateFromMessage(const KMessage& message) 388 { 389 int32 intValue; 390 if (message.FindInt32("gid", &intValue) == B_OK) 391 fGID = intValue; 392 393 const char* stringValue; 394 if (message.FindString("name", &stringValue) == B_OK) 395 fName = stringValue; 396 397 if (message.FindString("password", &stringValue) == B_OK) 398 fPassword = stringValue; 399 400 if (message.FindString("members", &stringValue) == B_OK) { 401 fMembers.clear(); 402 for (int32 i = 0; 403 (stringValue = message.GetString("members", i, NULL)) != NULL; 404 i++) { 405 if (stringValue != NULL && *stringValue != '\0') 406 fMembers.insert(stringValue); 407 } 408 } 409 } 410 411 group* WriteFlatGroup(FlatStore& store) const 412 { 413 struct group group; 414 415 char* members[MAX_GROUP_MEMBER_COUNT + 1]; 416 int32 count = 0; 417 for (StringSet::const_iterator it = fMembers.begin(); 418 it != fMembers.end(); ++it) { 419 members[count++] = store.AppendString(it->c_str()); 420 } 421 members[count] = (char*)-1; 422 423 group.gr_gid = fGID; 424 group.gr_name = store.AppendString(fName); 425 group.gr_passwd = store.AppendString(fPassword); 426 group.gr_mem = (char**)store.AppendData(members, 427 sizeof(char*) * (count + 1), true); 428 429 return store.AppendData(group); 430 } 431 432 status_t WriteToMessage(KMessage& message) 433 { 434 status_t error; 435 if ((error = message.AddInt32("gid", fGID)) != B_OK 436 || (error = message.AddString("name", fName.c_str())) != B_OK 437 || (error = message.AddString("password", fPassword.c_str())) 438 != B_OK) { 439 return error; 440 } 441 442 for (StringSet::const_iterator it = fMembers.begin(); 443 it != fMembers.end(); ++it) { 444 if ((error = message.AddString("members", it->c_str())) != B_OK) 445 return error; 446 } 447 448 return B_OK; 449 } 450 451 void WriteGroupLine(FILE* file) 452 { 453 fprintf(file, "%s:%s:%d:", 454 fName.c_str(), fPassword.c_str(), (int)fGID); 455 for (StringSet::const_iterator it = fMembers.begin(); 456 it != fMembers.end(); ++it) { 457 if (it == fMembers.begin()) 458 fprintf(file, "%s", it->c_str()); 459 else 460 fprintf(file, ",%s", it->c_str()); 461 } 462 fputs("\n", file); 463 } 464 465 private: 466 gid_t fGID; 467 string fName; 468 string fPassword; 469 StringSet fMembers; 470 }; 471 472 473 class AuthenticationManager::UserDB { 474 public: 475 status_t AddUser(User* user) 476 { 477 try { 478 fUsersByID[user->UID()] = user; 479 } catch (...) { 480 return B_NO_MEMORY; 481 } 482 483 try { 484 fUsersByName[user->Name()] = user; 485 } catch (...) { 486 fUsersByID.erase(fUsersByID.find(user->UID())); 487 return B_NO_MEMORY; 488 } 489 490 return B_OK; 491 } 492 493 void RemoveUser(User* user) 494 { 495 fUsersByID.erase(fUsersByID.find(user->UID())); 496 fUsersByName.erase(fUsersByName.find(user->Name())); 497 } 498 499 User* UserByID(uid_t uid) const 500 { 501 map<uid_t, User*>::const_iterator it = fUsersByID.find(uid); 502 return (it == fUsersByID.end() ? NULL : it->second); 503 } 504 505 User* UserByName(const char* name) const 506 { 507 map<string, User*>::const_iterator it = fUsersByName.find(name); 508 return (it == fUsersByName.end() ? NULL : it->second); 509 } 510 511 int32 WriteFlatPasswdDB(FlatStore& store) const 512 { 513 int32 count = fUsersByID.size(); 514 515 size_t entriesSpace = sizeof(passwd*) * count; 516 size_t offset = store.ReserveSpace(entriesSpace, true); 517 passwd** entries = new passwd*[count]; 518 ArrayDeleter<passwd*> _(entries); 519 520 int32 index = 0; 521 for (map<uid_t, User*>::const_iterator it = fUsersByID.begin(); 522 it != fUsersByID.end(); ++it) { 523 entries[index++] = it->second->WriteFlatPasswd(store); 524 } 525 526 store.WriteData(offset, entries, entriesSpace); 527 528 return count; 529 } 530 531 int32 WriteFlatShadowDB(FlatStore& store) const 532 { 533 int32 count = fUsersByID.size(); 534 535 size_t entriesSpace = sizeof(spwd*) * count; 536 size_t offset = store.ReserveSpace(entriesSpace, true); 537 spwd** entries = new spwd*[count]; 538 ArrayDeleter<spwd*> _(entries); 539 540 int32 index = 0; 541 for (map<uid_t, User*>::const_iterator it = fUsersByID.begin(); 542 it != fUsersByID.end(); ++it) { 543 entries[index++] = it->second->WriteFlatShadowPwd(store); 544 } 545 546 store.WriteData(offset, entries, entriesSpace); 547 548 return count; 549 } 550 551 void WriteToDisk() 552 { 553 // rename the old files 554 string passwdBackup(kPasswdFile); 555 string shadowBackup(kShadowPwdFile); 556 passwdBackup += ".old"; 557 shadowBackup += ".old"; 558 559 rename(kPasswdFile, passwdBackup.c_str()); 560 rename(kShadowPwdFile, shadowBackup.c_str()); 561 // Don't check errors. We can't do anything anyway. 562 563 // open files 564 FILE* passwdFile = fopen(kPasswdFile, "w"); 565 if (passwdFile == NULL) { 566 debug_printf("REG: Failed to open passwd file \"%s\" for " 567 "writing: %s\n", kPasswdFile, strerror(errno)); 568 } 569 CObjectDeleter<FILE, int> _1(passwdFile, fclose); 570 571 FILE* shadowFile = fopen(kShadowPwdFile, "w"); 572 if (shadowFile == NULL) { 573 debug_printf("REG: Failed to open shadow passwd file \"%s\" for " 574 "writing: %s\n", kShadowPwdFile, strerror(errno)); 575 } 576 CObjectDeleter<FILE, int> _2(shadowFile, fclose); 577 578 // write users 579 for (map<uid_t, User*>::const_iterator it = fUsersByID.begin(); 580 it != fUsersByID.end(); ++it) { 581 User* user = it->second; 582 user->WritePasswdLine(passwdFile); 583 user->WriteShadowPwdLine(shadowFile); 584 } 585 } 586 587 private: 588 map<uid_t, User*> fUsersByID; 589 map<string, User*> fUsersByName; 590 }; 591 592 593 class AuthenticationManager::GroupDB { 594 public: 595 status_t AddGroup(Group* group) 596 { 597 try { 598 fGroupsByID[group->GID()] = group; 599 } catch (...) { 600 return B_NO_MEMORY; 601 } 602 603 try { 604 fGroupsByName[group->Name()] = group; 605 } catch (...) { 606 fGroupsByID.erase(fGroupsByID.find(group->GID())); 607 return B_NO_MEMORY; 608 } 609 610 return B_OK; 611 } 612 613 void RemoveGroup(Group* group) 614 { 615 fGroupsByID.erase(fGroupsByID.find(group->GID())); 616 fGroupsByName.erase(fGroupsByName.find(group->Name())); 617 } 618 619 bool UserRemoved(const std::string& user) 620 { 621 bool changed = false; 622 for (map<gid_t, Group*>::const_iterator it = fGroupsByID.begin(); 623 it != fGroupsByID.end(); ++it) { 624 Group* group = it->second; 625 changed |= group->MemberRemoved(user); 626 } 627 return changed; 628 } 629 630 Group* GroupByID(gid_t gid) const 631 { 632 map<gid_t, Group*>::const_iterator it = fGroupsByID.find(gid); 633 return (it == fGroupsByID.end() ? NULL : it->second); 634 } 635 636 Group* GroupByName(const char* name) const 637 { 638 map<string, Group*>::const_iterator it = fGroupsByName.find(name); 639 return (it == fGroupsByName.end() ? NULL : it->second); 640 } 641 642 int32 GetUserGroups(const char* name, gid_t* groups, int maxCount) 643 { 644 int count = 0; 645 646 for (map<gid_t, Group*>::const_iterator it = fGroupsByID.begin(); 647 it != fGroupsByID.end(); ++it) { 648 Group* group = it->second; 649 if (group->HasMember(name)) { 650 if (count < maxCount) 651 groups[count] = group->GID(); 652 count++; 653 } 654 } 655 656 return count; 657 } 658 659 660 int32 WriteFlatGroupDB(FlatStore& store) const 661 { 662 int32 count = fGroupsByID.size(); 663 664 size_t entriesSpace = sizeof(group*) * count; 665 size_t offset = store.ReserveSpace(entriesSpace, true); 666 group** entries = new group*[count]; 667 ArrayDeleter<group*> _(entries); 668 669 int32 index = 0; 670 for (map<gid_t, Group*>::const_iterator it = fGroupsByID.begin(); 671 it != fGroupsByID.end(); ++it) { 672 entries[index++] = it->second->WriteFlatGroup(store); 673 } 674 675 store.WriteData(offset, entries, entriesSpace); 676 677 return count; 678 } 679 680 void WriteToDisk() 681 { 682 // rename the old files 683 string groupBackup(kGroupFile); 684 groupBackup += ".old"; 685 686 rename(kGroupFile, groupBackup.c_str()); 687 // Don't check errors. We can't do anything anyway. 688 689 // open file 690 FILE* groupFile = fopen(kGroupFile, "w"); 691 if (groupFile == NULL) { 692 debug_printf("REG: Failed to open group file \"%s\" for " 693 "writing: %s\n", kGroupFile, strerror(errno)); 694 } 695 CObjectDeleter<FILE, int> _1(groupFile, fclose); 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); 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 // delete port and wait for the request thread to finish 727 if (fRequestPort >= 0) 728 delete_port(fRequestPort); 729 730 status_t dummy; 731 wait_for_thread(fRequestThread, &dummy); 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 = create_port(100, 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