Luna/apps/src/session.c
2022-10-30 10:08:52 +01:00

145 lines
2.9 KiB
C

#include <errno.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
static char* echoing_fgets(char* buf, size_t size, FILE* stream)
{
char* s = buf;
memset(buf, 0, size);
size_t oldsize = size;
while (size)
{
int c = fgetc(stream);
if (c == EOF)
{
if (ferror(stream)) return NULL;
if (feof(stream))
{
if (s != buf) return s;
else
return NULL;
};
}
if ((char)c == '\b')
{
if (size != oldsize)
{
buf--;
size++;
putchar('\b');
}
}
else
{
size--;
*buf = (char)c;
buf++;
putchar((char)c);
if ((char)c == '\n') return s;
}
*buf = 0;
}
return s;
}
static void strip_newline(char* str)
{
size_t len = strlen(str);
if (str[len - 1] == '\n') str[len - 1] = 0;
}
static char* collect_password()
{
static char buf[BUFSIZ];
printf("Password: ");
fgets(buf, BUFSIZ, stdin);
strip_newline(buf);
putchar('\n');
char* copy = strdup(
buf); // The password only stays in a caller-controlled heap-allocated buffer, where it can be freed at will.
memset(buf, 0, BUFSIZ);
return copy;
}
static void login_as(struct passwd* user)
{
pid_t child = fork();
if (child < 0)
{
perror("fork");
return;
}
if (child == 0)
{
setuid(user->pw_uid);
setgid(user->pw_gid);
char* argv[] = {user->pw_shell, NULL};
execv(argv[0], argv);
perror("execv");
exit(EXIT_FAILURE);
}
wait(NULL);
}
static int login()
{
printf("Username: ");
char username[BUFSIZ];
echoing_fgets(username, BUFSIZ, stdin);
strip_newline(username);
if (strcmp("exit", username) == 0) return 1;
struct passwd* user = getpwnam(username);
if (!user)
{
if (errno) perror("getpwnam");
else
printf("Unknown user %s\n", username);
return 0;
}
char* password = collect_password();
putchar('\n');
if (strcmp(user->pw_passwd, password) == 0)
{
free(password);
login_as(user);
puts("logout\n");
}
else
{
free(password);
puts("Invalid password.\n");
}
return 0;
}
int main(int argc, char** argv)
{
(void)argc;
if (getuid() != 0)
{
fprintf(stderr,
"%s must be run as root.\nYou are probably looking for the 'su' command, which lets you switch users "
"once logged in.\n",
argv[0]);
return EXIT_FAILURE;
}
for (;;)
{
if (login()) break;
}
endpwent();
return EXIT_SUCCESS;
}