sus/sus.c
Slendi d8d42221f1
Re-write program and add support for /etc/sus.
Signed-off-by: Slendi <slendi@socopon.com>
2023-05-10 16:35:26 +03:00

203 lines
3.9 KiB
C

#define _GNU_SOURCE
#include <crypt.h>
#include <fcntl.h>
#include <grp.h>
#include <pwd.h>
#include <shadow.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <termios.h>
#include <unistd.h>
#define MAX_IDS 100
long long ids[MAX_IDS] = { 0 };
size_t ids_len = 0;
read_ids()
{
FILE *f = fopen("/etc/sus", "r");
if (!f) return 0;
while (!feof(f))
fscanf(f, "%lld", &ids[ids_len++]);
if (ids_len == 0) return 0;
return 1;
}
check_password(pass_buf)
char *pass_buf;
{
struct spwd* shadow_entry;
struct passwd* pa;
char *p, *correct, *supplied, *salt;
pa = getpwuid(getuid());
shadow_entry = getspnam(pa->pw_name);
if (shadow_entry == NULL)
return 2;
supplied = crypt(pass_buf, shadow_entry->sp_pwdp);
if (supplied == NULL)
return 6;
return !!strcmp(supplied, shadow_entry->sp_pwdp);
}
int getting_pass = 0;
ask_password()
{
getting_pass = 1;
int i = 3;
while (i--) {
char *pass_buf = getpass("Password: ");
if (!pass_buf || *pass_buf == '\0') {
i++;
continue;
}
int res = check_password(pass_buf);
memset(pass_buf, 0, strlen(pass_buf)-1);
if (res == 0) {
getting_pass = 0;
return 1;
} else
fputs("Wrong password!\n", stderr);
sleep(1);
}
getting_pass = 0;
return 0;
}
int_handler()
{
if (getting_pass)
return;
fputs("Cancelled.\n", stderr);
exit(0);
}
is_valid()
{
int i, j, groups = getgroups(0, NULL);
gid_t list[groups];
int ngroups = getgroups(groups, list);
if (ngroups < 0)
return -1;
for (i = 0; i < groups; i++) {
struct group* grp;
grp = getgrgid(list[i]);
if (grp == NULL)
return 0;
for (j = 0; j < ids_len; j++)
if (grp->gr_gid == ids[j])
return 1;
}
return 0;
}
main(argc, argv, envp, cstart, uid)
char **argv; char **envp;
uid_t uid;
{
cstart = 1;
uid = 0;
struct sigaction savealrm, saveint, savehup, savequit, saveterm;
struct sigaction savetstp, savettin, savettou, savepipe;
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sa.sa_handler = int_handler;
sigaction(SIGALRM, &sa, &savealrm);
sigaction(SIGHUP, &sa, &savehup);
sigaction(SIGINT, &sa, &saveint);
sigaction(SIGPIPE, &sa, &savepipe);
sigaction(SIGQUIT, &sa, &savequit);
sigaction(SIGTERM, &sa, &saveterm);
sigaction(SIGTSTP, &sa, &savetstp);
sigaction(SIGTTIN, &sa, &savettin);
sigaction(SIGTTOU, &sa, &savettou);
if (argc < 2) {
fprintf(stderr, "Usage: %s [- user] -- command\n", argv[0]);
return 0;
}
if (strcmp(*(argv + cstart), "-") == 0) {
if (argc < 4) {
fprintf(stderr, "No command or user specified.\n");
return -1;
}
struct passwd *p = getpwnam(argv[2]);
if (!p) {
fprintf(stderr, "Invalid user specified.\n");
return -1;
}
uid = p->pw_uid;
char **envp2 = envp;
while (*envp2 != NULL) {
if (strncmp(*envp2, "USER=", 5) == 0) {
char *new_user_string = malloc(strlen(p->pw_name) + 6);
sprintf(new_user_string, "USER=%s", p->pw_name);
strcpy(*envp2, new_user_string);
free(new_user_string);
break;
}
envp2++;
}
cstart += 2;
}
if (strcmp(*(argv + cstart), "--") == 0) {
struct passwd *p = getpwuid(uid);
if (!p) {
fprintf(stderr, "UID not found, this should not happen.\n");
return -1;
}
chdir(p->pw_dir);
cstart++;
if (argc - cstart < 1) {
fprintf(stderr, "Command not found.\n");
return -1;
}
}
if (!read_ids()) {
fputs("Failed to read ids file /etc/sus.\n", stderr);
return -1;
}
if (!is_valid()) {
fputs("Not in the susers file, this incident won't be reported.\n", stderr);
return -1;
}
if (ask_password() != 1) {
fputs("Too many attempts.\n", stderr);
return -1;
}
sigaction(SIGALRM, &sa, NULL);
sigaction(SIGHUP, &sa, NULL);
sigaction(SIGINT, &sa, NULL);
sigaction(SIGPIPE, &sa, NULL);
sigaction(SIGQUIT, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
sigaction(SIGTSTP, &sa, NULL);
sigaction(SIGTTIN, &sa, NULL);
sigaction(SIGTTOU, &sa, NULL);
argv += cstart;
setuid(uid);
execvpe(argv[0], argv, envp);
return 0;
}