From: František Dvořák Date: Wed, 16 Oct 2013 21:11:42 +0000 (+0200) Subject: Initial version of identity management. X-Git-Url: http://scientific.zcu.cz/git/?a=commitdiff_plain;h=aa6ce62d859476e2190d06a21299dcf05a649a19;p=dmlite-plugins-vfs-old.git Initial version of identity management. --- diff --git a/src/Vfs.cpp b/src/Vfs.cpp index 65f8ea9..10493f8 100644 --- a/src/Vfs.cpp +++ b/src/Vfs.cpp @@ -134,7 +134,7 @@ IODriver* VfsFactory::createIODriver(PluginManager*) throw (DmException) Authn* VfsFactory::createAuthn(PluginManager*) throw (DmException) { debug(""); - return new VfsAuthn(); + return new VfsAuthn(this->nsPrefix_); } diff --git a/src/VfsAuthn.cpp b/src/VfsAuthn.cpp index 725eda7..cf2823b 100644 --- a/src/VfsAuthn.cpp +++ b/src/VfsAuthn.cpp @@ -1,29 +1,31 @@ #include -#include -#include #include +#include +#include #include #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(); } @@ -45,10 +47,9 @@ SecurityContext *VfsAuthn::createSecurityContext(const SecurityCredentials &cred { UserInfo user; std::vector groups; - std::string userNameEmpty; - std::vector 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); } @@ -56,82 +57,119 @@ SecurityContext *VfsAuthn::createSecurityContext(const SecurityCredentials &cred void VfsAuthn::getIdMap(const std::string &userName, const std::vector &groupNames, UserInfo *user, std::vector *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]; } @@ -151,20 +189,7 @@ GroupInfo VfsAuthn::getGroup(const std::string& key, std::vector VfsAuthn::getGroups(void) throw (DmException) { - std::vector 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_; } @@ -176,51 +201,72 @@ void VfsAuthn::updateGroup(const GroupInfo&) throw (DmException) -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]; } @@ -228,41 +274,19 @@ UserInfo VfsAuthn::getUser(const std::string& userName) throw (DmException) 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 VfsAuthn::getUsers(void) throw (DmException) { - std::vector users; - UserInfo user; - - user.name = this->currentUser; - user["uid"] = this->currentUid; - users.push_back(user); - - return users; + return this->users_; } @@ -274,7 +298,139 @@ void VfsAuthn::updateUser(const UserInfo&) throw (DmException) -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"); } diff --git a/src/VfsAuthn.h b/src/VfsAuthn.h index 6effb5d..396b519 100644 --- a/src/VfsAuthn.h +++ b/src/VfsAuthn.h @@ -2,6 +2,7 @@ #define VFSAUTHN_H #include +#include #include @@ -10,7 +11,7 @@ namespace dmlite { class VfsAuthn: public Authn { public: - VfsAuthn() throw (DmException); + VfsAuthn(const std::string& prefix) throw (DmException); ~VfsAuthn(); std::string getImplId(void) const throw (); @@ -26,7 +27,7 @@ namespace dmlite { void deleteGroup(const std::string&) throw (DmException); UserInfo newUser(const std::string&) throw (DmException); - UserInfo getUser(const std::string& userName, gid_t* group) throw (DmException); + UserInfo getUser(uid_t uid) throw (DmException); UserInfo getUser(const std::string& userName) throw (DmException); UserInfo getUser(const std::string& key, const boost::any& value) throw (DmException); std::vector getUsers(void) throw (DmException); @@ -34,8 +35,18 @@ namespace dmlite { void deleteUser(const std::string&) throw (DmException); protected: - uid_t currentUid; - std::string currentUser; + void vfsNewGroup(const std::string& groupName) throw (DmException); + void vfsNewUser(const std::string& userName) throw (DmException); + void vfsReload() throw (DmException); + void vfsSaveGroups() throw (DmException); + void vfsSaveUsers() throw (DmException); + + std::string prefix_; + std::vector users_; + std::vector groups_; + std::set uids_, gids_; /// helper sets with UID/GID + unsigned int nextUid_, nextGid_; /// next UID/GID after the one last inserted + bool noSync_; /// saving users/groups disabled }; };