/** * @brief sudo - Run processes as the root user, after authenticating. * * Our sudo supports cached authentication, so you don't need to keep * entering your password. * * Probably terribly insecure, but our main password auth function is * a plain text comparison, so *shrug*. * * @copyright * This file is part of ToaruOS and is released under the terms * of the NCSA / University of Illinois License - see LICENSE.md * Copyright (C) 2014 K. Lange */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MINUTES * 60 #define SUDO_TIME 5 MINUTES extern int setgroups(int size, const gid_t list[]); static int sudo_loop(int (*prompt_callback)(char * username, char * password, int failures, char * argv[]), char * argv[]) { int fails = 0; if (geteuid() != 0) { fprintf(stderr, "%s: effective uid is not 0\n", argv[0]); return 1; } struct stat buf; if (stat("/var/sudoers", &buf)) { mkdir("/var/sudoers", 0700); } while (1) { int need_password = 1; int need_sudoers = 1; uid_t me = getuid(); if (me == 0) { need_password = 0; need_sudoers = 0; } struct passwd * p = getpwuid(me); if (!p) { fprintf(stderr, "%s: unable to obtain username for real uid=%d\n", argv[0], getuid()); return 1; } char * username = strdup(p->pw_name); char token_file[64]; sprintf(token_file, "/var/sudoers/%d", me); /* TODO: Restrict to this session? */ if (need_password) { struct stat buf; if (!stat(token_file, &buf)) { /* check the time */ if (buf.st_mtime > (SUDO_TIME) && time(NULL) - buf.st_mtime < (SUDO_TIME)) { need_password = 0; } } } if (need_password) { char * password = calloc(sizeof(char) * 1024, 1); if (prompt_callback(username, password, fails, argv)) { free(username); free(password); return 1; } int uid = toaru_auth_check_pass(username, password); free(password); if (uid < 0) { free(username); fails++; if (fails == 3) { fprintf(stderr, "%s: %d incorrect password attempts\n", argv[0], fails); return 1; } fprintf(stderr, "Sorry, try again.\n"); continue; } } /* Determine if this user is in the sudoers file */ if (need_sudoers) { FILE * sudoers = fopen("/etc/sudoers","r"); if (!sudoers) { free(username); fprintf(stderr, "%s: /etc/sudoers is not available\n", argv[0]); return 1; } /* Read each line */ int in_sudoers = 0; while (!feof(sudoers)) { char line[1024]; fgets(line, 1024, sudoers); char * nl = strchr(line, '\n'); if (nl) { *nl = '\0'; } if (!strncmp(line,username,1024)) { in_sudoers = 1; break; } } fclose(sudoers); if (!in_sudoers) { fprintf(stderr, "%s is not in sudoers file.\n", username); free(username); return 1; } } free(username); /* Write a timestamp file */ FILE * f = fopen(token_file, "w"); if (!f) { fprintf(stderr, "%s: (warning) failed to create token file\n", argv[0]); } fclose(f); /* Set username to root */ putenv("USER=root"); /* Actually become root, so real user id = 0 */ setgid(0); setuid(0); setgroups(0,NULL); if (!strcmp(argv[1], "-s")) { argv[1] = getenv("SHELL"); } char ** args = &argv[1]; execvp(args[0], args); /* XXX: There are other things that can cause an exec to fail. */ fprintf(stderr, "%s: %s: command not found\n", argv[0], args[0]); return 1; } return 0; } static int basic_callback(char * username, char * password, int fails, char * argv[]) { fprintf(stderr, "[%s] password for %s: ", argv[0], username); fflush(stderr); /* Disable echo */ struct termios old, new; tcgetattr(fileno(stdin), &old); new = old; new.c_lflag &= (~ECHO); tcsetattr(fileno(stdin), TCSAFLUSH, &new); fgets(password, 1024, stdin); if (feof(stdin)) return 1; password[strlen(password)-1] = '\0'; tcsetattr(fileno(stdin), TCSAFLUSH, &old); fprintf(stderr, "\n"); return 0; } void usage(int argc, char * argv[]) { fprintf(stderr, "usage: %s [command]\n", argv[0]); } int main(int argc, char ** argv) { if (argc < 2) { usage(argc, argv); return 1; } return sudo_loop(basic_callback, argv); }