From 7d0e442cdee78bdc566597a456f5059784572cd6 Mon Sep 17 00:00:00 2001 From: apio Date: Fri, 28 Oct 2022 20:55:00 +0200 Subject: [PATCH] libc: Add /etc/passwd and the pwd.h API getpwent, getpwnam, getpwuid... they may have been a pain to implement but once they work they're awesome :) Right now passwords are stored in plaintext in the world-readable passwd file, which is not good. But I don't have any sort of hashing implemented so it'll stay that way for now. --- initrd/etc/passwd | 2 + libs/libc/include/pwd.h | 42 +++++++++ libs/libc/src/pwd.cpp | 186 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 230 insertions(+) create mode 100644 initrd/etc/passwd create mode 100644 libs/libc/include/pwd.h create mode 100644 libs/libc/src/pwd.cpp diff --git a/initrd/etc/passwd b/initrd/etc/passwd new file mode 100644 index 00000000..3a1d10e2 --- /dev/null +++ b/initrd/etc/passwd @@ -0,0 +1,2 @@ +root:secure:0:0:Administrator:/:/bin/sh +selene:moon:1:1:Default User:/:/bin/sh \ No newline at end of file diff --git a/libs/libc/include/pwd.h b/libs/libc/include/pwd.h new file mode 100644 index 00000000..517a5f38 --- /dev/null +++ b/libs/libc/include/pwd.h @@ -0,0 +1,42 @@ +#ifndef _PWD_H +#define _PWD_H + +#include + +/* Structure representing a password file entry. */ +struct passwd +{ + char* pw_name; + char* pw_passwd; + uid_t pw_uid; + gid_t pw_gid; + char* pw_gecos; + char* pw_dir; + char* pw_shell; +}; + +#ifdef __cplusplus +extern "C" +{ +#endif + + /* Returns the next password file entry. */ + struct passwd* getpwent(void); + + /* Returns the first password file entry with a login name matching name, or NULL if there are none. */ + struct passwd* getpwnam(const char* name); + + /* Returns the first password file entry with a user ID matching uid, or NULL if there are none. */ + struct passwd* getpwuid(uid_t uid); + + /* Rewinds to the first password file entry. */ + void setpwent(void); + + /* Ends password file processing. */ + void endpwent(void); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/libs/libc/src/pwd.cpp b/libs/libc/src/pwd.cpp new file mode 100644 index 00000000..b7c7c16a --- /dev/null +++ b/libs/libc/src/pwd.cpp @@ -0,0 +1,186 @@ +#include +#include +#include +#include +#include +#include +#include + +static FILE* pwd_file = nullptr; + +static int initialize_pwd(FILE** stream) +{ + int pwfd = open("/etc/passwd", O_RDONLY | O_CLOEXEC); + if (pwfd < 0) return 0; + FILE* file = fdopen(pwfd, "r"); + if (!file) return 0; + *stream = file; + return 1; +} + +static int pwd_unbuffered_fgetc(FILE* stream) +{ + char c; + fread(&c, 1, 1, stream); + if (ferror(stream) || feof(stream)) return EOF; + return (int)c; +} + +static char* pwd_unbuffered_fgets(char* buf, size_t size, FILE* stream) +{ + char* s = buf; + memset(buf, 0, size); + while (size) + { + int c = pwd_unbuffered_fgetc(stream); + if (c == EOF) + { + if (ferror(stream)) return NULL; + if (feof(stream)) + { + if (s != buf) return s; + else + return NULL; + }; + } + size--; + *buf = (char)c; + buf++; + *buf = 0; + if ((char)c == '\n') return s; + } + return s; +} + +static char* pwd_strchrtok(char* s, char delim) +{ + static char* saved = nullptr; + if (s) saved = s; + else + s = saved; + if (!saved) return nullptr; + char* loc = strchr(saved, delim); + if (loc) + { + saved = loc + 1; + if (*saved == 0) saved = nullptr; + *loc = 0; + } + else { saved = nullptr; } + return s; +} + +static void pwd_strip_newline(char* buf) +{ + size_t len = strlen(buf); + if (buf[len - 1] == '\n') buf[len - 1] = 0; +} + +static struct passwd* internal_getpwent(FILE* stream) +{ +#define PWENT_INVALID \ + do { \ + errno = EINVAL; \ + return NULL; \ + } while (0) + + static struct passwd result; + static char buf[BUFSIZ]; + + if (!pwd_unbuffered_fgets(buf, BUFSIZ, stream)) return NULL; + pwd_strip_newline(buf); + result.pw_name = pwd_strchrtok(buf, ':'); + if (!result.pw_name) PWENT_INVALID; + result.pw_passwd = pwd_strchrtok(NULL, ':'); + if (!result.pw_passwd) PWENT_INVALID; + char* uid_string = pwd_strchrtok(NULL, ':'); + if (!uid_string) PWENT_INVALID; + result.pw_uid = atoi(uid_string); + char* gid_string = pwd_strchrtok(NULL, ':'); + if (!gid_string) PWENT_INVALID; + result.pw_gid = atoi(gid_string); + result.pw_gecos = pwd_strchrtok(NULL, ':'); + if (!result.pw_gecos) PWENT_INVALID; + result.pw_dir = pwd_strchrtok(NULL, ':'); + if (!result.pw_dir) PWENT_INVALID; + result.pw_shell = pwd_strchrtok(NULL, ':'); + if (!result.pw_shell) PWENT_INVALID; + return &result; +} + +extern "C" +{ + struct passwd* getpwent() + { + if (!pwd_file) + { + if (!initialize_pwd(&pwd_file)) return NULL; + } + if (feof(pwd_file)) + { + endpwent(); + return NULL; + } + return internal_getpwent(pwd_file); + } + + struct passwd* getpwnam(const char* name) + { + static FILE* pwnam_file = nullptr; + if (!pwnam_file) + { + if (!initialize_pwd(&pwnam_file)) return NULL; + } + else + rewind(pwnam_file); + struct passwd* pwd = internal_getpwent(pwnam_file); + while (pwd) + { + if (strcmp(pwd->pw_name, name) == 0) break; + if (feof(pwnam_file)) + { + pwd = nullptr; + break; + } + pwd = internal_getpwent(pwnam_file); + } + return pwd; // if we matched one, pwd contains a pointer to it. else it is NULL + } + + struct passwd* getpwuid(uid_t uid) + { + static FILE* pwuid_file = nullptr; + if (!pwuid_file) + { + if (!initialize_pwd(&pwuid_file)) return NULL; + } + else + rewind(pwuid_file); + struct passwd* pwd = internal_getpwent(pwuid_file); + while (pwd) + { + if (pwd->pw_uid == uid) break; + if (feof(pwuid_file)) + { + pwd = nullptr; + break; + } + pwd = internal_getpwent(pwuid_file); + } + return pwd; // if we matched one, pwd contains a pointer to it. else it is NULL + } + + void setpwent() + { + if (pwd_file) rewind(pwd_file); + } + + void endpwent() + { + if (pwd_file) + { + fclose(pwd_file); + pwd_file = nullptr; + } + } +} \ No newline at end of file