205 lines
4.3 KiB
C
205 lines
4.3 KiB
C
/**
|
|
* @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 <stdio.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <time.h>
|
|
#include <termios.h>
|
|
#include <errno.h>
|
|
#include <pwd.h>
|
|
#include <dirent.h>
|
|
#include <sys/utsname.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/time.h>
|
|
#include <toaru/auth.h>
|
|
|
|
#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);
|
|
}
|
|
|