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: %lu\n", message.What()); 979 980 } 981 } 982 } 983 984 985 status_t 986 AuthenticationManager::_InitPasswdDB() 987 { 988 FILE* file = fopen(kPasswdFile, "r"); 989 if (file == NULL) { 990 debug_printf("REG: Failed to open passwd DB file \"%s\": %s\n", 991 kPasswdFile, strerror(errno)); 992 return errno; 993 } 994 CObjectDeleter<FILE, int> _(file, fclose); 995 996 char lineBuffer[LINE_MAX]; 997 while (char* line = fgets(lineBuffer, sizeof(lineBuffer), file)) { 998 if (strlen(line) == 0) 999 continue; 1000 1001 char* name; 1002 char* password; 1003 uid_t uid; 1004 gid_t gid; 1005 char* home; 1006 char* shell; 1007 char* realName; 1008 1009 status_t error = parse_passwd_line(line, name, password, uid, gid, 1010 home, shell, realName); 1011 if (error != B_OK) { 1012 debug_printf("REG: Unparsable line in passwd DB file: \"%s\"\n", 1013 strerror(errno)); 1014 continue; 1015 } 1016 1017 User* user = NULL; 1018 try { 1019 user = new User(name, password, uid, gid, home, shell, realName); 1020 } catch (...) { 1021 } 1022 1023 if (user == NULL || fUserDB->AddUser(user) != B_OK) { 1024 delete user; 1025 debug_printf("REG: Out of memory\n"); 1026 return B_NO_MEMORY; 1027 } 1028 } 1029 1030 return B_OK; 1031 } 1032 1033 1034 status_t 1035 AuthenticationManager::_InitGroupDB() 1036 { 1037 FILE* file = fopen(kGroupFile, "r"); 1038 if (file == NULL) { 1039 debug_printf("REG: Failed to open group DB file \"%s\": %s\n", 1040 kGroupFile, strerror(errno)); 1041 return errno; 1042 } 1043 CObjectDeleter<FILE, int> _(file, fclose); 1044 1045 char lineBuffer[LINE_MAX]; 1046 while (char* line = fgets(lineBuffer, sizeof(lineBuffer), file)) { 1047 if (strlen(line) == 0) 1048 continue; 1049 1050 char* name; 1051 char* password; 1052 gid_t gid; 1053 char* members[MAX_GROUP_MEMBER_COUNT]; 1054 int memberCount; 1055 1056 1057 status_t error = parse_group_line(line, name, password, gid, members, 1058 memberCount); 1059 if (error != B_OK) { 1060 debug_printf("REG: Unparsable line in group DB file: \"%s\"\n", 1061 strerror(errno)); 1062 continue; 1063 } 1064 1065 Group* group = NULL; 1066 try { 1067 group = new Group(name, password, gid, members, memberCount); 1068 } catch (...) { 1069 } 1070 1071 if (group == NULL || fGroupDB->AddGroup(group) != B_OK) { 1072 delete group; 1073 debug_printf("REG: Out of memory\n"); 1074 return B_NO_MEMORY; 1075 } 1076 } 1077 1078 return B_OK; 1079 } 1080 1081 1082 status_t 1083 AuthenticationManager::_InitShadowPwdDB() 1084 { 1085 FILE* file = fopen(kShadowPwdFile, "r"); 1086 if (file == NULL) { 1087 debug_printf("REG: Failed to open shadow passwd DB file \"%s\": %s\n", 1088 kShadowPwdFile, strerror(errno)); 1089 return errno; 1090 } 1091 CObjectDeleter<FILE, int> _(file, fclose); 1092 1093 char lineBuffer[LINE_MAX]; 1094 while (char* line = fgets(lineBuffer, sizeof(lineBuffer), file)) { 1095 if (strlen(line) == 0) 1096 continue; 1097 1098 char* name; 1099 char* password; 1100 int lastChanged; 1101 int min; 1102 int max; 1103 int warn; 1104 int inactive; 1105 int expiration; 1106 int flags; 1107 1108 status_t error = parse_shadow_pwd_line(line, name, password, 1109 lastChanged, min, max, warn, inactive, expiration, flags); 1110 if (error != B_OK) { 1111 debug_printf("REG: Unparsable line in shadow passwd DB file: " 1112 "\"%s\"\n", strerror(errno)); 1113 continue; 1114 } 1115 1116 User* user = fUserDB->UserByName(name); 1117 if (user == NULL) { 1118 debug_printf("REG: shadow pwd entry for unknown user \"%s\"\n", 1119 name); 1120 continue; 1121 } 1122 1123 try { 1124 user->SetShadowInfo(password, lastChanged, min, max, warn, inactive, 1125 expiration, flags); 1126 } catch (...) { 1127 debug_printf("REG: Out of memory\n"); 1128 return B_NO_MEMORY; 1129 } 1130 } 1131 1132 return B_OK; 1133 } 1134