Add group{add,del,mod}

This commit is contained in:
Ingo Weinhold 2013-09-18 16:28:58 +02:00
parent dc3be29614
commit 519bb60aef
7 changed files with 599 additions and 26 deletions

View File

@ -14,7 +14,7 @@ SYSTEM_BIN = [ FFilterByBuildFeatures
echo eject env error expand expr
factor false fdinfo ffm filepanel find finddir FirstBootPrompt fmt fold
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
id ident ifconfig <bin>install installsound iroster isvolume
ideinfo@ide idestatus@ide

View File

@ -14,7 +14,7 @@ SYSTEM_BIN = [ FFilterByBuildFeatures
echo eject env error expand expr
factor false fdinfo ffm filepanel find finddir fmt fold
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
id ident ifconfig <bin>install isvolume
ideinfo@ide idestatus@ide

View File

@ -17,5 +17,11 @@ BinCommand useradd : useradd.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
MODE on passwd = 04755 ;

View File

@ -0,0 +1,109 @@
/*
* Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
* 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;
}

View File

@ -0,0 +1,98 @@
/*
* Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
* 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;
}

View File

@ -0,0 +1,159 @@
/*
* Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
* 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;
}

View File

@ -12,9 +12,11 @@
#include <map>
#include <new>
#include <set>
#include <string>
#include <DataIO.h>
#include <StringList.h>
#include <AutoDeleter.h>
#include <RegistrarDefs.h>
@ -30,6 +32,9 @@ using std::string;
using namespace BPrivate;
typedef std::set<std::string> StringSet;
class AuthenticationManager::FlatStore {
public:
FlatStore()
@ -337,22 +342,29 @@ private:
class AuthenticationManager::Group {
public:
Group()
:
fGID(0),
fName(),
fPassword(),
fMembers()
{
}
Group(const char* name, const char* password, gid_t gid,
const char* const* members, int memberCount)
:
fGID(gid),
fName(name),
fPassword(password),
fMembers(new string[memberCount]),
fMemberCount(memberCount)
fMembers()
{
for (int i = 0; i < memberCount; i++)
fMembers[i] = members[i];
fMembers.insert(members[i]);
}
~Group()
{
delete[] fMembers;
}
const string& Name() const { return fName; }
@ -360,12 +372,40 @@ public:
bool HasMember(const char* name)
{
for (int i = 0; i < fMemberCount; i++) {
if (fMembers[i] == name)
return true;
try {
return fMembers.find(name) != fMembers.end();
} 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
@ -373,15 +413,18 @@ public:
struct group group;
char* members[MAX_GROUP_MEMBER_COUNT + 1];
for (int i = 0; i < fMemberCount; i++)
members[i] = store.AppendString(fMembers[i].c_str());
members[fMemberCount] = (char*)-1;
int32 count = 0;
for (StringSet::const_iterator it = fMembers.begin();
it != fMembers.end(); ++it) {
members[count++] = store.AppendString(it->c_str());
}
members[count] = (char*)-1;
group.gr_gid = fGID;
group.gr_name = store.AppendString(fName);
group.gr_passwd = store.AppendString(fPassword);
group.gr_mem = (char**)store.AppendData(members,
sizeof(char*) * (fMemberCount + 1), true);
sizeof(char*) * (count + 1), true);
return store.AppendData(group);
}
@ -396,22 +439,34 @@ public:
return error;
}
for (int i = 0; i < fMemberCount; i++) {
if ((error = message.AddString("members", fMembers[i].c_str()))
!= B_OK) {
for (StringSet::const_iterator it = fMembers.begin();
it != fMembers.end(); ++it) {
if ((error = message.AddString("members", it->c_str())) != B_OK)
return error;
}
}
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:
gid_t fGID;
string fName;
string fPassword;
string* fMembers;
int fMemberCount;
gid_t fGID;
string fName;
string fPassword;
StringSet fMembers;
};
@ -555,6 +610,23 @@ public:
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
{
map<gid_t, Group*>::const_iterator it = fGroupsByID.find(gid);
@ -605,6 +677,31 @@ public:
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:
map<uid_t, Group*> fGroupsByID;
map<string, Group*> fGroupsByName;
@ -1002,10 +1099,17 @@ AuthenticationManager::_RequestThread()
// apply the change
if (error == B_OK) {
std::string userName = user->Name();
fUserDB->RemoveUser(user);
fUserDB->WriteToDisk();
_InvalidatePasswdDBReply();
_InvalidateShadowPwdDBReply();
if (fGroupDB->UserRemoved(userName)) {
fGroupDB->WriteToDisk();
_InvalidateGroupDBReply();
}
}
// send reply
@ -1017,13 +1121,110 @@ AuthenticationManager::_RequestThread()
}
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;
}
case B_REG_DELETE_GROUP:
{
debug_printf(
"B_REG_DELETE_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;
}
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;
}