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