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