9a9b91c940
It's finally done.. Signed-off-by: kaguya <vpshinomiya@protonmail.com>
310 lines
6.2 KiB
C++
310 lines
6.2 KiB
C++
|
|
#include <errno.h>
|
|
#include <pwd.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <bits/ensure.h>
|
|
|
|
#include <mlibc/debug.hpp>
|
|
|
|
namespace {
|
|
FILE *global_file; // Used by setpwent/getpwent/endpwent.
|
|
|
|
bool open_global_file() {
|
|
if(!global_file) {
|
|
global_file = fopen("/etc/passwd", "r");
|
|
if(!global_file) {
|
|
errno = EIO;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void close_global_file() {
|
|
if(global_file) {
|
|
fclose(global_file);
|
|
global_file = nullptr;
|
|
}
|
|
}
|
|
|
|
bool extract_entry(frg::string_view line, passwd *entry) {
|
|
frg::string_view segments[8];
|
|
|
|
// Parse the line into 7 or 8 segments.
|
|
size_t s = 0;
|
|
int n;
|
|
for(n = 0; n < 7; n++) {
|
|
size_t d = line.find_first(':', s);
|
|
if(d == size_t(-1))
|
|
break;
|
|
segments[n] = line.sub_string(s, d - s);
|
|
s = d + 1;
|
|
}
|
|
if(line.find_first(':', s) != size_t(-1))
|
|
return false;
|
|
segments[n] = line.sub_string(s, line.size() - s);
|
|
n++;
|
|
|
|
if(n < 7)
|
|
return false;
|
|
|
|
// TODO: Handle strndup() failure.
|
|
auto name = strndup(segments[0].data(), segments[0].size());
|
|
__ensure(name);
|
|
|
|
auto passwd = strndup(segments[1].data(), segments[1].size());
|
|
__ensure(passwd);
|
|
|
|
auto uid = segments[2].to_number<int>();
|
|
if(!uid)
|
|
return false;
|
|
auto gid = segments[3].to_number<int>();
|
|
if(!gid)
|
|
return false;
|
|
|
|
auto real_name = strndup(segments[4].data(), segments[4].size());
|
|
__ensure(real_name);
|
|
auto dir = strndup(segments[5].data(), segments[5].size());
|
|
__ensure(dir);
|
|
auto shell = strndup(segments[6].data(), segments[6].size());
|
|
__ensure(shell);
|
|
|
|
// Chop the newline off the end of shell
|
|
__ensure(strlen(shell) > 0);
|
|
shell[strlen(shell) - 1] = '\0';
|
|
|
|
entry->pw_name = name;
|
|
entry->pw_passwd = passwd;
|
|
entry->pw_uid = *uid;
|
|
entry->pw_gid = *gid;
|
|
entry->pw_dir = dir;
|
|
entry->pw_shell = shell;
|
|
entry->pw_gecos = real_name;
|
|
return true;
|
|
}
|
|
|
|
void copy_to_buffer(passwd *pwd, char *buffer, size_t size) {
|
|
char *pw_dir = stpcpy(buffer, pwd->pw_name) + 1;
|
|
free(pwd->pw_name);
|
|
pwd->pw_name = buffer;
|
|
|
|
char *pw_shell = stpcpy(pw_dir, pwd->pw_dir) + 1;
|
|
free(pwd->pw_dir);
|
|
pwd->pw_dir = pw_dir;
|
|
|
|
char *pw_passwd = stpcpy(pw_shell, pwd->pw_shell) + 1;
|
|
free(pwd->pw_shell);
|
|
pwd->pw_shell = pw_shell;
|
|
|
|
char *end = stpcpy(pw_passwd, pwd->pw_passwd);
|
|
__ensure(end <= buffer + size);
|
|
free(pwd->pw_passwd);
|
|
pwd->pw_passwd = pw_passwd;
|
|
}
|
|
|
|
void clear_entry(passwd *entry) {
|
|
free(entry->pw_name);
|
|
free(entry->pw_dir);
|
|
free(entry->pw_passwd);
|
|
free(entry->pw_shell);
|
|
entry->pw_name = nullptr;
|
|
entry->pw_dir = nullptr;
|
|
entry->pw_passwd = nullptr;
|
|
entry->pw_shell = nullptr;
|
|
}
|
|
} // namespace
|
|
|
|
struct passwd *getpwent(void) {
|
|
static passwd entry;
|
|
char line[NSS_BUFLEN_PASSWD];
|
|
|
|
if(!open_global_file()) {
|
|
return nullptr;
|
|
}
|
|
|
|
if (fgets(line, NSS_BUFLEN_PASSWD, global_file)) {
|
|
clear_entry(&entry);
|
|
if(!extract_entry(line, &entry)) {
|
|
errno = EINVAL; // I suppose this can be a valid errno?
|
|
return nullptr;
|
|
}
|
|
return &entry;
|
|
}
|
|
|
|
if(ferror(global_file)) {
|
|
errno = EIO;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
struct passwd *getpwnam(const char *name) {
|
|
static passwd entry;
|
|
auto file = fopen("/etc/passwd", "r");
|
|
if(!file)
|
|
return nullptr;
|
|
|
|
char line[NSS_BUFLEN_PASSWD];
|
|
while(fgets(line, NSS_BUFLEN_PASSWD, file)) {
|
|
clear_entry(&entry);
|
|
if(!extract_entry(line, &entry))
|
|
continue;
|
|
if(!strcmp(entry.pw_name, name)) {
|
|
fclose(file);
|
|
return &entry;
|
|
}
|
|
}
|
|
|
|
int err = errno;
|
|
if(ferror(file)) {
|
|
err = EIO;
|
|
}
|
|
|
|
fclose(file);
|
|
errno = err;
|
|
return nullptr;
|
|
}
|
|
|
|
int getpwnam_r(const char *name, struct passwd *pwd, char *buffer, size_t size, struct passwd **result) {
|
|
*result = nullptr;
|
|
auto file = fopen("/etc/passwd", "r");
|
|
if(!file) {
|
|
return EIO;
|
|
}
|
|
|
|
char line[NSS_BUFLEN_PASSWD];
|
|
while(fgets(line, NSS_BUFLEN_PASSWD, file)) {
|
|
if(!extract_entry(line, pwd))
|
|
continue;
|
|
if(!strcmp(pwd->pw_name, name)) {
|
|
fclose(file);
|
|
|
|
size_t required_size = strlen(pwd->pw_name) + strlen(pwd->pw_dir)
|
|
+ strlen(pwd->pw_shell) + strlen(pwd->pw_passwd) + 4;
|
|
if (size < required_size)
|
|
return ERANGE;
|
|
|
|
copy_to_buffer(pwd, buffer, size);
|
|
*result = pwd;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int ret = 0;
|
|
if(ferror(file)) {
|
|
ret = EIO;
|
|
}
|
|
|
|
fclose(file);
|
|
return ret;
|
|
}
|
|
|
|
struct passwd *getpwuid(uid_t uid) {
|
|
static passwd entry;
|
|
auto file = fopen("/etc/passwd", "r");
|
|
if(!file)
|
|
return nullptr;
|
|
|
|
char line[NSS_BUFLEN_PASSWD];
|
|
while(fgets(line, NSS_BUFLEN_PASSWD, file)) {
|
|
clear_entry(&entry);
|
|
if(!extract_entry(line, &entry))
|
|
continue;
|
|
if(entry.pw_uid == uid) {
|
|
fclose(file);
|
|
return &entry;
|
|
}
|
|
}
|
|
|
|
int err = ESRCH;
|
|
if(ferror(file)) {
|
|
err = EIO;
|
|
}
|
|
|
|
fclose(file);
|
|
errno = err;
|
|
return nullptr;
|
|
}
|
|
|
|
int getpwuid_r(uid_t uid, struct passwd *pwd, char *buffer, size_t size, struct passwd **result) {
|
|
*result = nullptr;
|
|
auto file = fopen("/etc/passwd", "r");
|
|
if(!file) {
|
|
return EIO;
|
|
}
|
|
|
|
char line[NSS_BUFLEN_PASSWD];
|
|
while(fgets(line, NSS_BUFLEN_PASSWD, file)) {
|
|
if(!extract_entry(line, pwd))
|
|
continue;
|
|
if(pwd->pw_uid == uid) {
|
|
fclose(file);
|
|
|
|
size_t required_size = strlen(pwd->pw_name) + strlen(pwd->pw_dir)
|
|
+ strlen(pwd->pw_shell) + + strlen(pwd->pw_passwd) + 4;
|
|
if (size < required_size)
|
|
return ERANGE;
|
|
|
|
copy_to_buffer(pwd, buffer, size);
|
|
*result = pwd;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int ret = 0;
|
|
if(ferror(file)) {
|
|
ret = EIO;
|
|
}
|
|
|
|
fclose(file);
|
|
return ret;
|
|
}
|
|
|
|
void setpwent(void) {
|
|
if(!open_global_file()) {
|
|
return;
|
|
}
|
|
rewind(global_file);
|
|
}
|
|
|
|
void endpwent(void) {
|
|
close_global_file();
|
|
}
|
|
|
|
int putpwent(const struct passwd *p, FILE *f) {
|
|
auto invalid = [](const char *s) {
|
|
return s == nullptr || strchr(s, '\n') || strchr(s, ':');
|
|
};
|
|
|
|
if (p == nullptr || invalid(p->pw_name) || invalid(p->pw_passwd) || invalid(p->pw_gecos) || invalid(p->pw_dir) || invalid(p->pw_shell)) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
// Taken from musl.
|
|
return fprintf(f, "%s:%s:%u:%u:%s:%s:%s\n", p->pw_name, p->pw_passwd, p->pw_uid, p->pw_gid, p->pw_gecos, p->pw_dir, p->pw_shell) < 0 ? -1 : 0;
|
|
}
|
|
|
|
struct passwd *fgetpwent(FILE *file) {
|
|
static passwd entry;
|
|
char line[NSS_BUFLEN_PASSWD];
|
|
|
|
|
|
if (fgets(line, NSS_BUFLEN_PASSWD, file)) {
|
|
clear_entry(&entry);
|
|
if(!extract_entry(line, &entry)) {
|
|
errno = EINVAL; // I suppose this can be a valid errno?
|
|
return nullptr;
|
|
}
|
|
return &entry;
|
|
}
|
|
|
|
if(ferror(file)) {
|
|
errno = EIO;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|