Re-write program and add support for /etc/sus.

Signed-off-by: Slendi <slendi@socopon.com>
This commit is contained in:
Slendi 2023-05-10 16:34:28 +03:00
parent 3d3d237c27
commit d8d42221f1
No known key found for this signature in database
GPG Key ID: F8405BA38DE1E10D
2 changed files with 174 additions and 258 deletions

View File

@ -1,5 +1,5 @@
build: build:
cc -Wall -lcrypt -I. sus.c -o sus tcc -lcrypt sus.c -o sus
all: build install all: build install

430
sus.c
View File

@ -1,286 +1,202 @@
#define _GNU_SOURCE #define _GNU_SOURCE
#include <stdio.h> #include <crypt.h>
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>
#include <signal.h>
#include <string.h>
#include <fcntl.h> #include <fcntl.h>
#include <grp.h> #include <grp.h>
#include <pwd.h> #include <pwd.h>
#include <shadow.h> #include <shadow.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h> #include <sys/types.h>
#include <termios.h>
#include <unistd.h>
#include <errno.h> #define MAX_IDS 100
long long ids[MAX_IDS] = { 0 };
size_t ids_len = 0;
#define MAX_PASSWORD 3 read_ids()
void get_password(char* password);
void cats(char** str, const char* str2);
int CheckIfInGroup(const char* string);
int GetUID(const char* username, uid_t* uid);
void PrintHelp();
void CreatePasswordMessage();
int CheckPassword(char* password);
int AskPassword();
uid_t uid = 0;
int command_start = 1;
char* command_name;
struct termios old_terminal;
void getln(int, char*, size_t);
struct passwd* pw;
void intHandler(int dummy)
{ {
tcsetattr(STDIN_FILENO, TCSANOW, &old_terminal); FILE *f = fopen("/etc/sus", "r");
puts("Cancelled."); if (!f) return 0;
exit(0); while (!feof(f))
fscanf(f, "%lld", &ids[ids_len++]);
if (ids_len == 0) return 0;
return 1;
} }
int main(int argc, char** argv, char** envp) check_password(pass_buf)
char *pass_buf;
{ {
uid = getuid(); struct spwd* shadow_entry;
pw = getpwuid(uid); struct passwd* pa;
char *p, *correct, *supplied, *salt;
if (CheckIfInGroup("wheel") != 1) { pa = getpwuid(getuid());
puts("ERROR: Not in the wheel group.");
return 0; shadow_entry = getspnam(pa->pw_name);
} if (shadow_entry == NULL)
return 2;
struct sigaction savealrm, saveint, savehup, savequit, saveterm;
struct sigaction savetstp, savettin, savettou, savepipe; supplied = crypt(pass_buf, shadow_entry->sp_pwdp);
if (supplied == NULL)
struct sigaction sa; return 6;
sigemptyset(&sa.sa_mask); return !!strcmp(supplied, shadow_entry->sp_pwdp);
sa.sa_flags = 0;
sa.sa_handler = intHandler;
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);
command_name = malloc((1 + strlen(argv[0])) * sizeof(char));
strcpy(command_name, argv[0]);
if (argc < 2) {
PrintHelp();
return 0;
}
if (AskPassword() != 1) {
puts("ERROR: Too many attempts.");
return 1;
}
char username[32];
username[0] = '\0';
if (argv[1][0] == '-') {
// - specified. Check if user is in next argument or
// in the same argument as the switch.
if (strlen(argv[1]) > 1) {
if (strlen(argv[1]) > 34) {
puts("ERROR: Username limit reached!");
return 1;
}
strcpy(username, argv[1]);
command_start = 2;
} else {
if (strlen(argv[2]) > 33) {
puts("ERROR: Username limit reached!");
return 1;
}
strcpy(username, argv[2]);
command_start = 3;
}
if (username[0] == '-')
memmove(username, username + 1, strlen(username));
}
if (username[0] == '\0')
uid = 0;
else if (GetUID(username, &uid) == -1) {
printf("ERROR: Could not find username: %s", username);
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 += command_start;
setuid(uid);
execvpe(argv[0], argv, envp);
return 0;
} }
int GetUID(const char* username, uid_t* uid) int getting_pass = 0;
ask_password()
{ {
pw = getpwnam(username); getting_pass = 1;
if (pw != NULL) { int i = 3;
*uid = pw->pw_uid; while (i--) {
return 1; char *pass_buf = getpass("Password: ");
} if (!pass_buf || *pass_buf == '\0') {
i++;
return -1; 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 CheckIfInGroup(const char* string) int_handler()
{ {
int groups = getgroups(0, NULL); if (getting_pass)
return;
gid_t list[groups]; fputs("Cancelled.\n", stderr);
exit(0);
int ngroups = getgroups(groups, list);
if (ngroups < 0)
return -1;
for (int i = 0; i < groups; i++) {
struct group* grp;
grp = getgrgid(list[i]);
if (grp == NULL)
return -1;
if (strcmp(grp->gr_name, "wheel") == 0)
return 1;
}
return -1;
} }
void PrintHelp() { printf("Usage: %s [- user] command\n", command_name); } is_valid()
int AskPassword()
{ {
for (int i = 0; i < MAX_PASSWORD; i++) { int i, j, groups = getgroups(0, NULL);
char password[BUFSIZ]; gid_t list[groups];
get_password(password); int ngroups = getgroups(groups, list);
int res = CheckPassword(password); if (ngroups < 0)
if (res == 0) return -1;
return 1;
else
CreatePasswordMessage();
sleep(1);
}
return 0; 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;
} }
int CheckPassword(char* password) main(argc, argv, envp, cstart, uid)
char **argv; char **envp;
uid_t uid;
{ {
struct spwd* shadow_entry; cstart = 1;
struct passwd* pa = getpwuid(getuid()); uid = 0;
char *p, *correct, *supplied, *salt; struct sigaction savealrm, saveint, savehup, savequit, saveterm;
shadow_entry = getspnam(pa->pw_name); struct sigaction savetstp, savettin, savettou, savepipe;
if (shadow_entry == NULL)
return 2; struct sigaction sa;
correct = shadow_entry->sp_pwdp; sigemptyset(&sa.sa_mask);
salt = strdup(correct); sa.sa_flags = 0;
if (salt == NULL) sa.sa_handler = int_handler;
return 3;
p = strchr(salt + 1, '$'); sigaction(SIGALRM, &sa, &savealrm);
if (p == NULL) sigaction(SIGHUP, &sa, &savehup);
return 4; sigaction(SIGINT, &sa, &saveint);
p = strchr(p + 1, '$'); sigaction(SIGPIPE, &sa, &savepipe);
if (p == NULL) sigaction(SIGQUIT, &sa, &savequit);
return 5; sigaction(SIGTERM, &sa, &saveterm);
p[1] = 0; sigaction(SIGTSTP, &sa, &savetstp);
sigaction(SIGTTIN, &sa, &savettin);
sigaction(SIGTTOU, &sa, &savettou);
supplied = crypt(password, salt); if (argc < 2) {
if (supplied == NULL) fprintf(stderr, "Usage: %s [- user] -- command\n", argv[0]);
return 6; return 0;
return !!strcmp(supplied, correct); }
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;
} }
void CreatePasswordMessage() { puts("Incorrect password! Try again."); }
void cats(char** str, const char* str2)
{
char* tmp = NULL;
// Reset *str
if (*str != NULL && str2 == NULL) {
free(*str);
*str = NULL;
return;
}
// Initial copy
if (*str == NULL) {
*str = calloc(strlen(str2) + 1, sizeof(char));
memcpy(*str, str2, strlen(str2));
} else { // Append
tmp = calloc(strlen(*str) + 1, sizeof(char));
memcpy(tmp, *str, strlen(*str));
*str = calloc(strlen(*str) + strlen(str2) + 1, sizeof(char));
memcpy(*str, tmp, strlen(tmp));
memcpy(*str + strlen(*str), str2, strlen(str2));
free(tmp);
}
}
void get_password(char* password)
{
static struct termios new_terminal;
int ttyfd = open("/dev/tty", O_RDWR);
tcgetattr(ttyfd, &old_terminal);
new_terminal = old_terminal;
new_terminal.c_lflag &= ~(ECHO);
tcsetattr(ttyfd, TCSAFLUSH, &new_terminal);
write(ttyfd, "Password: ", 11);
getln(ttyfd, password, sizeof(password));
tcsetattr(ttyfd, TCSAFLUSH, &old_terminal);
close(ttyfd);
putchar('\n');
}
void getln(int fd, char* buf, size_t bufsiz)
{
ssize_t nr = -1;
char ch;
while ((nr = read(fd, &ch, 1)) == 1 && ch != '\n' && ch != '\r') {
if (buf < buf + bufsiz - 1) {
*buf++ = ch;
}
}
*buf = '\0';
}