Add group{add,del,mod}

This commit is contained in:
Ingo Weinhold
2013-09-18 16:33:19 +02:00
parent dc3be29614
commit 519bb60aef
7 changed files with 599 additions and 26 deletions
+1 -1
View File
@@ -14,7 +14,7 @@ SYSTEM_BIN = [ FFilterByBuildFeatures
echo eject env error expand expr echo eject env error expand expr
factor false fdinfo ffm filepanel find finddir FirstBootPrompt fmt fold factor false fdinfo ffm filepanel find finddir FirstBootPrompt fmt fold
fortune frcode ftp ftpd funzip fwcontrol fortune frcode ftp ftpd funzip fwcontrol
gawk gdb@x86 getlimits groups gzip gzexe gawk gdb@x86 getlimits groupadd groupdel groupmod groups gzip gzexe
hd head hey hostname hd head hey hostname
id ident ifconfig <bin>install installsound iroster isvolume id ident ifconfig <bin>install installsound iroster isvolume
ideinfo@ide idestatus@ide ideinfo@ide idestatus@ide
+1 -1
View File
@@ -14,7 +14,7 @@ SYSTEM_BIN = [ FFilterByBuildFeatures
echo eject env error expand expr echo eject env error expand expr
factor false fdinfo ffm filepanel find finddir fmt fold factor false fdinfo ffm filepanel find finddir fmt fold
fortune frcode ftp ftpd funzip fortune frcode ftp ftpd funzip
gawk gdb@x86 getlimits groups gzip gzexe gawk gdb@x86 getlimits groupadd groupdel groupmod groups gzip gzexe
hd head hey hostname hd head hey hostname
id ident ifconfig <bin>install isvolume id ident ifconfig <bin>install isvolume
ideinfo@ide idestatus@ide ideinfo@ide idestatus@ide
+6
View File
@@ -17,5 +17,11 @@ BinCommand useradd : useradd.cpp ;
BinCommand userdel : userdel.cpp ; BinCommand userdel : userdel.cpp ;
BinCommand groupadd : groupadd.cpp ;
BinCommand groupdel : groupdel.cpp ;
BinCommand groupmod : groupmod.cpp : $(TARGET_LIBSTDC++) ;
# set set-uid bit on passwd # set set-uid bit on passwd
MODE on passwd = 04755 ; MODE on passwd = 04755 ;
+109
View File
@@ -0,0 +1,109 @@
/*
* Copyright 2013, Ingo Weinhold, [email protected].
* Distributed under the terms of the MIT License.
*/
#include <errno.h>
#include <getopt.h>
#include <grp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <OS.h>
#include <RegistrarDefs.h>
#include <user_group.h>
#include <util/KMessage.h>
#include "multiuser_utils.h"
extern const char *__progname;
static const char* kUsage =
"Usage: %s [ <options> ] <group name>\n"
"Creates a new group <group name>.\n"
"\n"
"Options:\n"
" -h, --help\n"
" Print usage info.\n"
;
static void
print_usage_and_exit(bool error)
{
fprintf(error ? stderr : stdout, kUsage, __progname);
exit(error ? 1 : 0);
}
int
main(int argc, const char* const* argv)
{
while (true) {
static struct option sLongOptions[] = {
{ "help", no_argument, 0, 'h' },
{ 0, 0, 0, 0 }
};
opterr = 0; // don't print errors
int c = getopt_long(argc, (char**)argv, "h", sLongOptions, NULL);
if (c == -1)
break;
switch (c) {
case 'h':
print_usage_and_exit(false);
break;
default:
print_usage_and_exit(true);
break;
}
}
if (optind != argc - 1)
print_usage_and_exit(true);
const char* group = argv[optind];
if (geteuid() != 0) {
fprintf(stderr, "Error: Only root may add groups.\n");
exit(1);
}
// check, if group already exists
if (getgrnam(group) != NULL) {
fprintf(stderr, "Error: Group \"%s\" already exists.\n", group);
exit(1);
}
// find an unused GID
gid_t gid = 100;
while (getgrgid(gid) != NULL)
gid++;
// prepare request for the registrar
KMessage message(BPrivate::B_REG_UPDATE_GROUP);
if (message.AddInt32("gid", gid) != B_OK
|| message.AddString("name", group) != B_OK
|| message.AddString("password", "x") != B_OK
|| message.AddBool("add group", true) != B_OK) {
fprintf(stderr, "Error: Out of memory!\n");
exit(1);
}
// send the request
KMessage reply;
status_t error = send_authentication_request_to_registrar(message, reply);
if (error != B_OK) {
fprintf(stderr, "Error: Failed to create group: %s\n", strerror(error));
exit(1);
}
return 0;
}
+98
View File
@@ -0,0 +1,98 @@
/*
* Copyright 2013, Ingo Weinhold, [email protected].
* Distributed under the terms of the MIT License.
*/
#include <getopt.h>
#include <grp.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <OS.h>
#include <RegistrarDefs.h>
#include <user_group.h>
#include <util/KMessage.h>
#include "multiuser_utils.h"
extern const char *__progname;
static const char* kUsage =
"Usage: %s [ <options> ] <group name>\n"
"Deletes the specified group.\n"
"\n"
"Options:\n"
" -h, --help\n"
" Print usage info.\n"
;
static void
print_usage_and_exit(bool error)
{
fprintf(error ? stderr : stdout, kUsage, __progname);
exit(error ? 1 : 0);
}
int
main(int argc, const char* const* argv)
{
while (true) {
static struct option sLongOptions[] = {
{ "help", no_argument, 0, 'h' },
{ 0, 0, 0, 0 }
};
opterr = 0; // don't print errors
int c = getopt_long(argc, (char**)argv, "h", sLongOptions, NULL);
if (c == -1)
break;
switch (c) {
case 'h':
print_usage_and_exit(false);
break;
default:
print_usage_and_exit(true);
break;
}
}
if (optind != argc - 1)
print_usage_and_exit(true);
const char* group = argv[optind];
if (geteuid() != 0) {
fprintf(stderr, "Error: Only root may delete groups.\n");
exit(1);
}
if (getgrnam(group) == NULL) {
fprintf(stderr, "Error: Group \"%s\" doesn't exists.\n", group);
exit(1);
}
// prepare request for the registrar
KMessage message(BPrivate::B_REG_DELETE_GROUP);
if (message.AddString("name", group) != B_OK) {
fprintf(stderr, "Error: Out of memory!\n");
exit(1);
}
// send the request
KMessage reply;
status_t error = send_authentication_request_to_registrar(message, reply);
if (error != B_OK) {
fprintf(stderr, "Error: Failed to delete group: %s\n", strerror(error));
exit(1);
}
return 0;
}
+159
View File
@@ -0,0 +1,159 @@
/*
* Copyright 2013, Ingo Weinhold, [email protected].
* Distributed under the terms of the MIT License.
*/
#include <errno.h>
#include <getopt.h>
#include <grp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <set>
#include <string>
#include <OS.h>
#include <RegistrarDefs.h>
#include <user_group.h>
#include <util/KMessage.h>
#include "multiuser_utils.h"
extern const char *__progname;
static const char* kUsage =
"Usage: %s [ <options> ] <group name>\n"
"Creates a new group <group name>.\n"
"\n"
"Options:\n"
" -A, --add-user <user>\n"
" Add the user <user> to the group.\n"
" -h, --help\n"
" Print usage info.\n"
" -R, --remove-user <user>\n"
" Remove the user <user> from the group.\n"
;
static void
print_usage_and_exit(bool error)
{
fprintf(error ? stderr : stdout, kUsage, __progname);
exit(error ? 1 : 0);
}
int
main(int argc, const char* const* argv)
{
typedef std::set<std::string> StringSet;
StringSet usersToAdd;
StringSet usersToRemove;
while (true) {
static struct option sLongOptions[] = {
{ "add-user", required_argument, 0, 'A' },
{ "help", no_argument, 0, 'h' },
{ "remove-user", required_argument, 0, 'A' },
{ 0, 0, 0, 0 }
};
opterr = 0; // don't print errors
int c = getopt_long(argc, (char**)argv, "A:hR:", sLongOptions, NULL);
if (c == -1)
break;
switch (c) {
case 'A':
usersToAdd.insert(optarg);
break;
case 'h':
print_usage_and_exit(false);
break;
case 'R':
usersToRemove.insert(optarg);
break;
default:
print_usage_and_exit(true);
break;
}
}
if (optind != argc - 1)
print_usage_and_exit(true);
const char* group = argv[optind];
if (geteuid() != 0) {
fprintf(stderr, "Error: Only root may modify groups.\n");
exit(1);
}
// get the group
struct group* groupInfo = getgrnam(group);
if (groupInfo == NULL) {
fprintf(stderr, "Error: Group \"%s\" doesn't exist.\n", group);
exit(1);
}
// check, if anything needs to be done
if (usersToAdd.empty() && usersToRemove.empty()) {
fprintf(stderr, "Error: No modification specified.\n");
exit(1);
}
// prepare request for the registrar
KMessage message(BPrivate::B_REG_UPDATE_GROUP);
if (message.AddInt32("gid", groupInfo->gr_gid) != B_OK
|| message.AddString("name", group) != B_OK
|| message.AddString("password", groupInfo->gr_passwd) != B_OK
|| message.AddBool("add group", false) != B_OK) {
fprintf(stderr, "Error: Out of memory!\n");
exit(1);
}
for (int32 i = 0; const char* user = groupInfo->gr_mem[i]; i++) {
if (usersToRemove.erase(user) > 0)
continue;
usersToAdd.insert(user);
}
if (!usersToRemove.empty()) {
fprintf(stderr, "Error: \"%s\" is not a member of group \"%s\"\n",
usersToRemove.begin()->c_str(), group);
exit(1);
}
// If the group doesn't have any more members, insert an empty string as an
// indicator for the registrar to remove all members.
if (usersToAdd.empty())
usersToAdd.insert("");
for (StringSet::const_iterator it = usersToAdd.begin();
it != usersToAdd.end(); ++it) {
if (message.AddString("members", it->c_str()) != B_OK) {
fprintf(stderr, "Error: Out of memory!\n");
exit(1);
}
}
// send the request
KMessage reply;
status_t error = send_authentication_request_to_registrar(message, reply);
if (error != B_OK) {
fprintf(stderr, "Error: Failed to create group: %s\n", strerror(error));
exit(1);
}
return 0;
}
+225 -24
View File
@@ -12,9 +12,11 @@
#include <map> #include <map>
#include <new> #include <new>
#include <set>
#include <string> #include <string>
#include <DataIO.h> #include <DataIO.h>
#include <StringList.h>
#include <AutoDeleter.h> #include <AutoDeleter.h>
#include <RegistrarDefs.h> #include <RegistrarDefs.h>
@@ -30,6 +32,9 @@ using std::string;
using namespace BPrivate; using namespace BPrivate;
typedef std::set<std::string> StringSet;
class AuthenticationManager::FlatStore { class AuthenticationManager::FlatStore {
public: public:
FlatStore() FlatStore()
@@ -337,22 +342,29 @@ private:
class AuthenticationManager::Group { class AuthenticationManager::Group {
public: public:
Group()
:
fGID(0),
fName(),
fPassword(),
fMembers()
{
}
Group(const char* name, const char* password, gid_t gid, Group(const char* name, const char* password, gid_t gid,
const char* const* members, int memberCount) const char* const* members, int memberCount)
: :
fGID(gid), fGID(gid),
fName(name), fName(name),
fPassword(password), fPassword(password),
fMembers(new string[memberCount]), fMembers()
fMemberCount(memberCount)
{ {
for (int i = 0; i < memberCount; i++) for (int i = 0; i < memberCount; i++)
fMembers[i] = members[i]; fMembers.insert(members[i]);
} }
~Group() ~Group()
{ {
delete[] fMembers;
} }
const string& Name() const { return fName; } const string& Name() const { return fName; }
@@ -360,12 +372,40 @@ public:
bool HasMember(const char* name) bool HasMember(const char* name)
{ {
for (int i = 0; i < fMemberCount; i++) { try {
if (fMembers[i] == name) return fMembers.find(name) != fMembers.end();
return true; } catch (...) {
return false;
} }
}
return false; bool MemberRemoved(const std::string& name)
{
return fMembers.erase(name) > 0;
}
void UpdateFromMessage(const KMessage& message)
{
int32 intValue;
if (message.FindInt32("gid", &intValue) == B_OK)
fGID = intValue;
const char* stringValue;
if (message.FindString("name", &stringValue) == B_OK)
fName = stringValue;
if (message.FindString("password", &stringValue) == B_OK)
fPassword = stringValue;
if (message.FindString("members", &stringValue) == B_OK) {
fMembers.clear();
for (int32 i = 0;
(stringValue = message.GetString("members", i, NULL)) != NULL;
i++) {
if (stringValue != NULL && *stringValue != '\0')
fMembers.insert(stringValue);
}
}
} }
group* WriteFlatGroup(FlatStore& store) const group* WriteFlatGroup(FlatStore& store) const
@@ -373,15 +413,18 @@ public:
struct group group; struct group group;
char* members[MAX_GROUP_MEMBER_COUNT + 1]; char* members[MAX_GROUP_MEMBER_COUNT + 1];
for (int i = 0; i < fMemberCount; i++) int32 count = 0;
members[i] = store.AppendString(fMembers[i].c_str()); for (StringSet::const_iterator it = fMembers.begin();
members[fMemberCount] = (char*)-1; it != fMembers.end(); ++it) {
members[count++] = store.AppendString(it->c_str());
}
members[count] = (char*)-1;
group.gr_gid = fGID; group.gr_gid = fGID;
group.gr_name = store.AppendString(fName); group.gr_name = store.AppendString(fName);
group.gr_passwd = store.AppendString(fPassword); group.gr_passwd = store.AppendString(fPassword);
group.gr_mem = (char**)store.AppendData(members, group.gr_mem = (char**)store.AppendData(members,
sizeof(char*) * (fMemberCount + 1), true); sizeof(char*) * (count + 1), true);
return store.AppendData(group); return store.AppendData(group);
} }
@@ -396,22 +439,34 @@ public:
return error; return error;
} }
for (int i = 0; i < fMemberCount; i++) { for (StringSet::const_iterator it = fMembers.begin();
if ((error = message.AddString("members", fMembers[i].c_str())) it != fMembers.end(); ++it) {
!= B_OK) { if ((error = message.AddString("members", it->c_str())) != B_OK)
return error; return error;
}
} }
return B_OK; return B_OK;
} }
void WriteGroupLine(FILE* file)
{
fprintf(file, "%s:%s:%d:",
fName.c_str(), fPassword.c_str(), (int)fGID);
for (StringSet::const_iterator it = fMembers.begin();
it != fMembers.end(); ++it) {
if (it == fMembers.begin())
fprintf(file, "%s", it->c_str());
else
fprintf(file, ",%s", it->c_str());
}
fputs("\n", file);
}
private: private:
gid_t fGID; gid_t fGID;
string fName; string fName;
string fPassword; string fPassword;
string* fMembers; StringSet fMembers;
int fMemberCount;
}; };
@@ -555,6 +610,23 @@ public:
return B_OK; return B_OK;
} }
void RemoveGroup(Group* group)
{
fGroupsByID.erase(fGroupsByID.find(group->GID()));
fGroupsByName.erase(fGroupsByName.find(group->Name()));
}
bool UserRemoved(const std::string& user)
{
bool changed = false;
for (map<gid_t, Group*>::const_iterator it = fGroupsByID.begin();
it != fGroupsByID.end(); ++it) {
Group* group = it->second;
changed |= group->MemberRemoved(user);
}
return changed;
}
Group* GroupByID(gid_t gid) const Group* GroupByID(gid_t gid) const
{ {
map<gid_t, Group*>::const_iterator it = fGroupsByID.find(gid); map<gid_t, Group*>::const_iterator it = fGroupsByID.find(gid);
@@ -605,6 +677,31 @@ public:
return count; return count;
} }
void WriteToDisk()
{
// rename the old files
string groupBackup(kGroupFile);
groupBackup += ".old";
rename(kGroupFile, groupBackup.c_str());
// Don't check errors. We can't do anything anyway.
// open file
FILE* groupFile = fopen(kGroupFile, "w");
if (groupFile == NULL) {
debug_printf("REG: Failed to open group file \"%s\" for "
"writing: %s\n", kGroupFile, strerror(errno));
}
CObjectDeleter<FILE, int> _1(groupFile, fclose);
// write groups
for (map<gid_t, Group*>::const_iterator it = fGroupsByID.begin();
it != fGroupsByID.end(); ++it) {
Group* group = it->second;
group->WriteGroupLine(groupFile);
}
}
private: private:
map<uid_t, Group*> fGroupsByID; map<uid_t, Group*> fGroupsByID;
map<string, Group*> fGroupsByName; map<string, Group*> fGroupsByName;
@@ -1002,10 +1099,17 @@ AuthenticationManager::_RequestThread()
// apply the change // apply the change
if (error == B_OK) { if (error == B_OK) {
std::string userName = user->Name();
fUserDB->RemoveUser(user); fUserDB->RemoveUser(user);
fUserDB->WriteToDisk(); fUserDB->WriteToDisk();
_InvalidatePasswdDBReply(); _InvalidatePasswdDBReply();
_InvalidateShadowPwdDBReply(); _InvalidateShadowPwdDBReply();
if (fGroupDB->UserRemoved(userName)) {
fGroupDB->WriteToDisk();
_InvalidateGroupDBReply();
}
} }
// send reply // send reply
@@ -1017,13 +1121,110 @@ AuthenticationManager::_RequestThread()
} }
case B_REG_UPDATE_GROUP: case B_REG_UPDATE_GROUP:
debug_printf("B_REG_UPDATE_GROUP done: currently unsupported!\n"); {
// find group
Group* group = NULL;
int32 gid;
const char* name;
if (message.FindInt32("gid", &gid) == B_OK) {
group = fGroupDB->GroupByID(gid);
} else if (message.FindString("name", &name) == B_OK) {
group = fGroupDB->GroupByName(name);
} else {
error = B_BAD_VALUE;
}
// only root can change anything
if (error == B_OK && !isRoot)
error = EPERM;
// check addGroup vs. existing group
bool addGroup = message.GetBool("add group", false);
if (error == B_OK) {
if (addGroup) {
if (group != NULL)
error = EEXIST;
} else if (group == NULL)
error = ENOENT;
}
// apply all changes
if (error == B_OK) {
// clone the group object and update it from the message
Group* oldGroup = group;
group = NULL;
try {
group = (oldGroup != NULL ? new Group(*oldGroup)
: new Group);
group->UpdateFromMessage(message);
// gid and name should remain the same
if (oldGroup != NULL) {
if (oldGroup->GID() != group->GID()
|| oldGroup->Name() != group->Name()) {
error = B_BAD_VALUE;
}
}
// replace the old group and write DBs to disk
if (error == B_OK) {
fGroupDB->AddGroup(group);
fGroupDB->WriteToDisk();
_InvalidateGroupDBReply();
}
} catch (...) {
error = B_NO_MEMORY;
}
if (error == B_OK)
delete oldGroup;
else
delete group;
}
// send reply
KMessage reply;
reply.SetWhat(error);
message.SendReply(&reply, -1, -1, 0, registrarTeam);
break; break;
}
case B_REG_DELETE_GROUP: case B_REG_DELETE_GROUP:
{ {
debug_printf( // find group
"B_REG_DELETE_GROUP done: currently unsupported!\n"); Group* group = NULL;
int32 gid;
const char* name;
if (message.FindInt32("gid", &gid) == B_OK) {
group = fGroupDB->GroupByID(gid);
} else if (message.FindString("name", &name) == B_OK) {
group = fGroupDB->GroupByName(name);
} else {
error = B_BAD_VALUE;
}
if (error == B_OK && group == NULL)
error = ENOENT;
// only root can change anything
if (error == B_OK && !isRoot)
error = EPERM;
// apply the change
if (error == B_OK) {
fGroupDB->RemoveGroup(group);
fGroupDB->WriteToDisk();
_InvalidateGroupDBReply();
}
// send reply
KMessage reply;
reply.SetWhat(error);
message.SendReply(&reply, -1, -1, 0, registrarTeam);
break; break;
} }