diff --git a/us.c b/us.c index ec5e5f0..a34bab3 100644 --- a/us.c +++ b/us.c @@ -1,4 +1,5 @@ #define _POSIX_C_SOURCE 200809L +#define _DEFAULT_SOURCE #include #include @@ -7,11 +8,13 @@ #include #include #include +#include +#include #include #include static void usage (void); -static int perm_set (uid_t, gid_t); +static int perm_set (const char *, const char *); // FIXME: misc_conv is a separate library, should stick to plain PAM static struct pam_conv conv = {misc_conv, NULL}; @@ -25,7 +28,7 @@ int main (int argc, char *argv[]) } uid_t ruid = getuid(); - gid_t rgid = getgid(); +// gid_t rgid = getgid(); struct passwd *pw = getpwuid(ruid); if (!pw) { fprintf(stderr, "getpwid: %s\n", strerror(errno)); @@ -77,7 +80,7 @@ int main (int argc, char *argv[]) errno = 0; /* Set permissions */ - if (perm_set(0, 0) == -1) { // 0 = root + if (perm_set(NULL, NULL) == -1) { // 0 = root fprintf(stderr, "perm_set: %s\n", strerror(errno)); goto fail_end; } @@ -86,11 +89,13 @@ int main (int argc, char *argv[]) if (execvp(*c_argv, c_argv) == -1) // execvp searches in path fprintf(stderr, "execv: %s\n", strerror(errno)); - /* If exec fails reset the permissions */ - if (perm_set(ruid, rgid) == -1) { // 0 = root + + /* We may no longer have permission to reset the permissions + // If exec fails reset the permissions + if (perm_set(pw->pw_name, NULL) == -1) { // 0 = root fprintf(stderr, "perm_set: %s\n", strerror(errno)); goto fail_end; - } + } */ /* Cleanup and return */ fail_end: @@ -115,23 +120,79 @@ static inline void usage (void) printf("usage: us [command]\n"); } -static int perm_set (uid_t uid, gid_t gid) +static int perm_set (const char *user, const char *group) { - // FIXME: This doesn't change the uid and gid, it only changes the - // e(ffective)uid and egid keeping the original gid, uid and groups list - // to make it work like sudo, the uid gid and groups list must be all - // set to 0 (or the corresponding value for other users). - // An option could be to add an argument to keep the groups list, - // another to change only the euid/egid. - // For more information run: - // $ sudo id - // and check the output - int err = 0; - // FIXME: using setresuid() and setresgid() is preferred - if ((err = seteuid(uid)) == -1) - return err; - else if ((err = setegid(gid)) == -1) - return err; - // FIXME: to set the group list use initgroups() - return err; + if (!user) + user = "root"; + + struct passwd *pw; + struct group *gr; + uid_t uid; + gid_t gid; + long uid_l, gid_l; + + errno = 0; + if (user[0] != '#') { + pw = getpwnam(user); + } else { + uid_l = strtol(&user[1], NULL, 10); + if (uid_l < 0 || errno) { + errno = errno ? errno : EINVAL; + return -1; + } + pw = getpwuid((uid_t)uid_l); + } + + if (!pw) { + if (!errno) + errno = EINVAL; + return -1; + } + + uid = pw->pw_uid; + gid = pw->pw_gid; + + if (group) { + if (group[0] != '#') { + gr = getgrnam(group); + } else { + gid_l = strtol(&group[1], NULL, 10); + if (gid_l < 0 || errno) { + errno = errno ? errno : EINVAL; + return -1; + } + gr = getgrgid((gid_t)gid_l); + } + + if (!gr) { + if (!errno) + errno = EINVAL; + return -1; + } + gid = gr->gr_gid; + } + + /* Set permissions, setting group perms first because in the case of + * dropping from higher permissions setting the uid first results in + * an error */ + int err; + /* Non POSIX but implemented in most systems anyways */ + err = initgroups(pw->pw_name, pw->pw_gid); + if (err == -1) { + printf("initgroups failed\n"); + return -1; + } + + // FIXME: ideally when failing reset the permissions + if (setregid(gid, gid) == -1) { + printf("setregid failed\n"); + return -1; + } + + if (setreuid(uid, uid) == -1) { + printf("setreuid failed\n"); + return -1; + } + + return 0; }