#include <sys/types.h>
-#include <grp.h>
-#include <pwd.h>
#include <unistd.h>
+#include <cstdio>
+#include <fstream>
#include <dmlite/cpp/dmlite.h>
#include "Vfs.h"
#include "VfsAuthn.h"
+#define VFS_USERS ".#vfs.users"
+#define VFS_GROUPS ".#vfs.groups"
+#define VFS_UID_START 500
+#define VFS_GID_START 500
+
using namespace dmlite;
-VfsAuthn::VfsAuthn() throw (DmException)
+VfsAuthn::VfsAuthn(const std::string& prefix) throw (DmException)
{
- char buffer[1024];
- struct passwd pwd;
- struct passwd *result;
- uid_t uid;
+ if (*prefix.rbegin() == '/') this->prefix_ = prefix;
+ else this->prefix_ = prefix + "/";
- uid = geteuid();
+ this->nextUid_ = VFS_UID_START;
+ this->nextGid_ = VFS_GID_START;
- if (getpwuid_r(uid, &pwd, buffer, sizeof(buffer), &result) != 0 || result == NULL)
- vfsThrowErrno("current user not found (uid %d)", uid);
+ this->noSync_ = false;
- this->currentUser = std::string(result->pw_name);
- this->currentUid = uid;
+ vfsReload();
}
{
UserInfo user;
std::vector<GroupInfo> groups;
- std::string userNameEmpty;
- std::vector<std::string> groupNamesEmpty;
- this->getIdMap(userNameEmpty, groupNamesEmpty, &user, &groups);
+ debug("user '%s', groups %zu", cred.clientName.c_str(), cred.fqans.size());
+ this->getIdMap(cred.clientName, cred.fqans, &user, &groups);
return new SecurityContext(cred, user, groups);
}
void VfsAuthn::getIdMap(const std::string &userName, const std::vector<std::string> &groupNames, UserInfo *user, std::vector<GroupInfo> *groups) throw (DmException)
{
- gid_t gid;
- gid_t gids[20];
- int ngids, i;
UserInfo ui;
- bool egid = false;
GroupInfo gi;
+ size_t ngroups;
- ui["banned"] = 0;
- ui["uid"] = this->currentUid;
- ui.name = this->currentUser;
- *user = ui;
+ ui = this->newUser(userName);
+ if (user) *user = ui;
- gid = getegid();
- ngids = getgroups(sizeof(gids)/sizeof(gid_t), gids);
- wrapCall(ngids, "error getting current groups");
+ // disable sync
+ this->noSync_ = true;
+ ngroups = this->groups_.size();
- for (i = 0; i < ngids; i++) {
- if (gids[i] == gid) egid = true;
- gi = this->getGroup(gids[i]);
- groups->push_back(gi);
- }
- if (!egid) {
- gi = this->getGroup(gid);
- groups->push_back(gi);
+ // find and create if needed
+ if (groups) groups->clear();
+ for (size_t i = 0; i < groupNames.size(); i++) {
+ gi = this->newGroup(groupNames[i]);
+ if (groups) groups->push_back(gi);
}
+
+ // bulk update
+ this->noSync_ = false;
+ if (ngroups != this->groups_.size())
+ vfsSaveGroups();
+}
+
+
+
+void VfsAuthn::vfsNewGroup(const std::string& groupName) throw (DmException) {
+ unsigned int gid;
+ GroupInfo gi;
+
+ // new gid
+ gid = this->nextGid_;
+ while (this->gids_.find(gid) != this->gids_.end())
+ gid++;
+
+ // insert
+ gi.name = groupName;
+ gi["gid"] = gid;
+ this->groups_.push_back(gi);
+ this->gids_.insert(gid);
+ this->nextGid_ = gid + 1;
+
+ debug("new group '%s', gid %d", groupName.c_str(), gid);
}
-GroupInfo VfsAuthn::newGroup(const std::string&) throw (DmException)
+void VfsAuthn::vfsNewUser(const std::string& userName) throw (DmException) {
+ unsigned int uid;
+ UserInfo ui;
+
+ // new uid
+ uid = this->nextUid_;
+ while (this->uids_.find(uid) != this->uids_.end())
+ uid++;
+
+ // insert
+ ui.name = userName;
+ ui["uid"] = uid;
+ this->users_.push_back(ui);
+ this->uids_.insert(uid);
+ this->nextUid_ = uid + 1;
+
+ debug("new user '%s', uid %d", userName.c_str(), uid);
+}
+
+
+
+GroupInfo VfsAuthn::newGroup(const std::string& groupName) throw (DmException)
{
- vfsThrow(DMLITE_SYSERR(ENOSYS), "not supported in VfsAuthn");
+ size_t i;
+
+ for (i = 0; i < this->groups_.size(); i++)
+ if (this->groups_[i].name == groupName) break;
+
+ if (i >= this->groups_.size()) {
+ vfsNewGroup(groupName);
+ vfsSaveGroups();
+ }
+
+ return this->groups_[i];
}
GroupInfo VfsAuthn::getGroup(gid_t gid) throw (DmException)
{
- struct group grp;
- struct group *result;
- char buffer[1024];
- int res;
- GroupInfo gi;
-
- res = getgrgid_r(gid, &grp, buffer, sizeof(buffer), &result);
+ size_t i;
- if (res != 0 || result == NULL)
- vfsThrow(DMLITE_NO_SUCH_GROUP, "Group with id %d not found", gid);
+ for (i = 0; i < this->groups_.size(); i++)
+ if (this->groups_[i].getUnsigned("gid", 0) == gid)
+ break;
- gi["banned"] = 0;
- gi["gid"] = result->gr_gid;
- gi.name = result->gr_name;
+ if (i >= this->groups_.size())
+ vfsThrow(DMLITE_SYSERR(DMLITE_NO_SUCH_GROUP), "group id %u not found", gid);
- return gi;
+ return this->groups_[i];
}
GroupInfo VfsAuthn::getGroup(const std::string &groupName) throw (DmException)
{
- struct group grp;
- struct group *result;
- char buffer[1024];
- int res;
- GroupInfo gi;
-
- res = getgrnam_r(groupName.c_str(), &grp, buffer, sizeof(buffer), &result);
+ size_t i;
- if (res != 0 || result == NULL)
- vfsThrow(DMLITE_NO_SUCH_GROUP, "Group '%s' not found", groupName.c_str());
+ for (i = 0; i < this->groups_.size(); i++)
+ if (this->groups_[i].name == groupName)
+ break;
- gi["banned"] = 0;
- gi["gid"] = result->gr_gid;
- gi.name = result->gr_name;
+ if (i >= this->groups_.size())
+ vfsThrow(DMLITE_SYSERR(DMLITE_NO_SUCH_GROUP), "group name '%s' not found", groupName.c_str());
- return gi;
+ return this->groups_[i];
}
std::vector<GroupInfo> VfsAuthn::getGroups(void) throw (DmException)
{
- std::vector<GroupInfo> groups;
- GroupInfo group;
- struct group* ent;
-
- setgrent();
- while ((ent = getgrent()) != NULL) {
- group.clear();
- group.name = ent->gr_name;
- group["gid"] = ent->gr_gid;
- groups.push_back(group);
- }
- endgrent();
-
- return groups;
+ return this->groups_;
}
-void VfsAuthn::deleteGroup(const std::string&) throw (DmException)
+void VfsAuthn::deleteGroup(const std::string& groupName) throw (DmException)
{
- vfsThrow(DMLITE_SYSERR(ENOSYS), "not supported in VfsAuthn");
-}
+ size_t i;
+ gid_t gid;
+ for (i = 0; i < this->groups_.size(); i++)
+ if (this->groups_[i].name == groupName) break;
+ if (i >= this->groups_.size())
+ return;
-UserInfo VfsAuthn::newUser(const std::string&) throw (DmException)
-{
- vfsThrow(DMLITE_SYSERR(ENOSYS), "not supported in VfsAuthn");
+ gid = this->groups_[i].getUnsigned("gid");
+ debug("delete group '%s', gid %u", groupName.c_str(), gid);
+ this->groups_.erase(this->groups_.begin() + i);
+ this->gids_.erase(gid);
+ //not needed immediately: if (this->nextGid_ > gid) this->nextGid_ = gid;
+ vfsSaveGroups();
}
-UserInfo VfsAuthn::getUser(const std::string& userName, gid_t* group) throw (DmException)
+UserInfo VfsAuthn::newUser(const std::string& userName) throw (DmException)
{
- struct passwd pwd;
- struct passwd *result;
- char buffer[1024];
- int res;
- UserInfo ui;
+ size_t i;
+
+ for (i = 0; i < this->users_.size(); i++)
+ if (this->users_[i].name == userName) break;
+
+ if (i >= this->users_.size()) {
+ vfsNewUser(userName);
+ vfsSaveUsers();
+ }
+
+ return this->users_[i];
+}
- if (userName != this->currentUser)
- vfsThrow(DMLITE_NO_SUCH_USER, "Only current effective local user is supported (%s)", this->currentUser.c_str());
- res = getpwnam_r(userName.c_str(), &pwd, buffer, sizeof(buffer), &result);
- if (res != 0 || result == NULL)
- vfsThrow(DMLITE_NO_SUCH_USER, "User '%s' not found", userName.c_str());
+UserInfo VfsAuthn::getUser(uid_t uid) throw (DmException)
+{
+ size_t i;
- ui["banned"] = 0;
- ui["uid"] = result->pw_uid;
- ui.name = result->pw_name;
+ for (i = 0; i < this->users_.size(); i++)
+ if (this->users_[i].getUnsigned("uid", 0) == uid)
+ break;
- *group = result->pw_gid;
+ if (i >= this->users_.size())
+ vfsThrow(DMLITE_SYSERR(DMLITE_NO_SUCH_USER), "user id %u not found", uid);
- return ui;
+ return this->users_[i];
}
UserInfo VfsAuthn::getUser(const std::string& userName) throw (DmException)
{
- gid_t ignore;
- return this->getUser(userName, &ignore);
+ size_t i;
+
+ for (i = 0; i < this->users_.size(); i++)
+ if (this->users_[i].name == userName)
+ break;
+
+ if (i >= this->users_.size())
+ vfsThrow(DMLITE_SYSERR(DMLITE_NO_SUCH_USER), "user name '%s' not found", userName.c_str());
+
+ return this->users_[i];
}
UserInfo VfsAuthn::getUser(const std::string& key,
const boost::any& value) throw (DmException)
{
- uid_t uid;
- struct passwd pwdbuf, *upwd;
- char buffer[512];
- int res;
- UserInfo u;
-
if (key != "uid")
vfsThrow(DMLITE_SYSERR(DMLITE_UNKNOWN_KEY),
"VfsAuthn does not support querying by " + key);
- uid = Extensible::anyToUnsigned(value);
- if (uid != this->currentUid)
- vfsThrow(DMLITE_NO_SUCH_USER, "Only current effective local user is supported (%s)", this->currentUser.c_str());
-
- res = getpwuid_r(uid, &pwdbuf, buffer, sizeof(buffer), &upwd);
- if (res != 0 || upwd == NULL)
- vfsThrow(DMLITE_NO_SUCH_USER, "User with id %d not found", uid);
-
- u.name = upwd->pw_name;
- u["uid"] = upwd->pw_uid;
- return u;
+ uid_t uid = Extensible::anyToUnsigned(value);
+ return this->getUser(uid);
}
std::vector<UserInfo> VfsAuthn::getUsers(void) throw (DmException)
{
- std::vector<UserInfo> users;
- UserInfo user;
-
- user.name = this->currentUser;
- user["uid"] = this->currentUid;
- users.push_back(user);
-
- return users;
+ return this->users_;
}
-void VfsAuthn::deleteUser(const std::string&) throw (DmException)
+void VfsAuthn::deleteUser(const std::string& userName) throw (DmException)
{
- vfsThrow(DMLITE_SYSERR(ENOSYS), "not supported in VfsAuthn");
+ size_t i;
+ uid_t uid;
+
+ for (i = 0; i < this->users_.size(); i++)
+ if (this->users_[i].name == userName) break;
+
+ if (i >= this->users_.size())
+ return;
+
+ uid = this->users_[i].getUnsigned("uid");
+ debug("delete user '%s', uid %u", userName.c_str(), uid);
+ this->users_.erase(this->users_.begin() + i);
+ this->uids_.erase(uid);
+ //not needed immediately: if (this->nextUid_ > uid) this->nextUid_ = uid;
+ vfsSaveUsers();
+}
+
+
+
+void VfsAuthn::vfsReload() throw (DmException) {
+ std::string entrypp;
+ std::ifstream *f;
+ const char *entry;
+ unsigned int uid, gid, banned;
+ int pos, ret, n;
+ UserInfo ui;
+ GroupInfo gi;
+
+ this->users_.clear();
+ this->uids_.clear();
+ f = new std::ifstream((this->prefix_ + VFS_USERS).c_str());
+ if (f->is_open()) {
+ n = 0;
+ while (std::getline(*f, entrypp)) {
+ n++;
+ entry = entrypp.c_str();
+ ret = sscanf(entry, "%u%*[ \t]%u%*[ \t]%n", &uid, &banned, &pos);
+ if (ret != 2) {
+ f->close();
+ delete f;
+ vfsThrow(ret == EOF ? DMLITE_SYSERR(EINVAL) : errno, "parsing users file failed at line %d (%s)", n, entry);
+ }
+ //printf("entry: %u %u '%s'\n", uid, banned, entry + pos);
+
+ ui.clear();
+ ui.name = entry + pos;
+ ui["uid"] = uid;
+ ui["banned"] = banned ? 1 : 0;
+ this->users_.push_back(ui);
+ // no check - we're OK with multiple users on the same uid
+ this->uids_.insert(uid);
+ }
+ f->close();
+ }
+ delete f;
+
+ this->groups_.clear();
+ this->gids_.clear();
+ f = new std::ifstream((this->prefix_ + VFS_GROUPS).c_str());
+ if (f->is_open()) {
+ n = 0;
+ while (std::getline(*f, entrypp)) {
+ n++;
+ entry = entrypp.c_str();
+ ret = sscanf(entry, "%u%*[ \t]%u%*[ \t]%n", &gid, &banned, &pos);
+ if (ret != 2) {
+ f->close();
+ delete f;
+ vfsThrow(ret == EOF ? DMLITE_SYSERR(EINVAL) : errno, "parsing groups file failed at line %d (%s)", n, entry);
+ }
+ //printf("entry: %u %u '%s'\n", gid, banned, entry + pos);
+
+ gi.clear();
+ gi.name = entry + pos;
+ gi["gid"] = gid;
+ gi["banned"] = banned ? 1 : 0;
+ this->groups_.push_back(gi);
+ // no check - we're OK with multiple groups on the same gid
+ this->gids_.insert(gid);
+ }
+ f->close();
+ }
+ delete f;
+}
+
+
+
+void VfsAuthn::vfsSaveUsers() throw (DmException) {
+ std::ofstream f;
+ std::string newName, name;
+
+ if (this->noSync_) return;
+
+ name = this->prefix_ + VFS_USERS;
+ newName = this->prefix_ + ".new" + VFS_USERS;
+
+ f.open(newName.c_str());
+ if (!f)
+ vfsThrow(EIO, "could not create new version of users file");
+
+ for (size_t i = 0; i < this->users_.size(); i++) {
+ f << this->users_[i].getUnsigned("uid") << "\t";
+ f << this->users_[i].getUnsigned("banned") << "\t";
+ f << this->users_[i].name << "\n";
+ }
+
+ f.close();
+ wrapCall(rename(newName.c_str(), name.c_str()), "could not save users file");
+}
+
+
+
+void VfsAuthn::vfsSaveGroups() throw (DmException) {
+ std::ofstream f;
+ std::string newName, name;
+
+ if (this->noSync_) return;
+
+ name = this->prefix_ + VFS_GROUPS;
+ newName = this->prefix_ + ".new" + VFS_GROUPS;
+
+ f.open(newName.c_str());
+ if (!f)
+ vfsThrow(EIO, "could not create new version of groups file");
+
+ for (size_t i = 0; i < this->groups_.size(); i++) {
+ f << this->groups_[i].getUnsigned("gid") << "\t";
+ f << this->groups_[i].getUnsigned("banned") << "\t";
+ f << this->groups_[i].name << "\n";
+ }
+
+ f.close();
+ wrapCall(rename(newName.c_str(), name.c_str()), "could not save groups file");
}