#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include static void usage (void); static int perm_set (uid_t, gid_t); // FIXME: misc_conv is a separate library, should stick to plain PAM static struct pam_conv conv = {misc_conv, NULL}; int main (int argc, char *argv[]) { if (argc < 2) { usage(); exit(1); } uid_t ruid = getuid(); gid_t rgid = getgid(); struct passwd *pw = getpwuid(ruid); if (!pw) { fprintf(stderr, "getpwid: %s\n", strerror(errno)); exit(errno); } const char *uname = pw->pw_name; pam_handle_t *pamh; int pam_err, count = 0; // TODO: Add arguments // TODO: add PAM authentication pam_err = pam_start("User Switcher", uname, &conv, &pamh); if (pam_err != PAM_SUCCESS) { fprintf(stderr, "pam_start: %s\n", pam_strerror(pamh, pam_err)); exit(pam_err); } do { pam_err = pam_authenticate(pamh, 0); if (pam_err != PAM_SUCCESS) printf("Auth failed: %s\n", pam_strerror(pamh, pam_err)); count++; } while (pam_err != PAM_SUCCESS && count < 4); if (pam_err != PAM_SUCCESS) { fprintf(stderr, "better luck next time\n"); pam_end(pamh, pam_err); exit(pam_err); } // FIXME: check again for the validity of the login for more security // as in: https://docs.oracle.com/cd/E19120-01/open.solaris/819-2145/pam-20/index.html pam_end(pamh, pam_err); // TODO: clean up env // Copy argv and argc int c_argc = argc - 1; char **c_argv = malloc((c_argc + 1) * sizeof(char *)); if (!c_argv) { fprintf(stderr, "malloc: %s\n", strerror(errno)); goto fail_end; } for (int i = 0; i < c_argc; i++) c_argv[i] = strdup(argv[i+1]); c_argc[c_argv] = NULL; errno = 0; /* Set permissions */ if (perm_set(0, 0) == -1) { // 0 = root fprintf(stderr, "perm_set: %s\n", strerror(errno)); goto fail_end; } /* Execute the command */ 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 fprintf(stderr, "perm_set: %s\n", strerror(errno)); goto fail_end; } /* Cleanup and return */ fail_end: /* Free up the copied argv */ for (int i = 0; i < c_argc; i++) free(c_argv[i]); free(c_argv); return errno; } static inline void usage (void) { printf("usage: us [command]\n"); } static int perm_set (uid_t uid, gid_t gid) { // 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; if ((err = seteuid(uid)) == -1) return err; else if ((err = setegid(gid)) == -1) return err; return err; }