Files
MentOS/libc/src/pwd.c
T
2023-08-24 12:17:16 -04:00

220 lines
5.8 KiB
C

/// @file pwd.c
/// @brief
/// @copyright (c) 2014-2023 This file is distributed under the MIT License.
/// See LICENSE.md for details.
#include "pwd.h"
#include "assert.h"
#include "fcntl.h"
#include "io/debug.h"
#include "stdio.h"
#include "string.h"
#include "sys/errno.h"
#include "sys/unistd.h"
/// @brief Parses the input buffer and fills pwd with its details.
/// @param pwd the structure we need to fill.
/// @param buf the buffer from which we extract the information.
static inline void __parse_line(passwd_t *pwd, char *buf)
{
assert(pwd && "Received null pwd!");
char *token, *ch;
// Parse the username.
if ((token = strtok(buf, ":")) != NULL) {
pwd->pw_name = token;
}
// Parse the password.
if ((token = strtok(NULL, ":")) != NULL) {
pwd->pw_passwd = token;
}
// Parse the user ID.
if ((token = strtok(NULL, ":")) != NULL) {
pwd->pw_uid = atoi(token);
}
// Parse the group ID.
if ((token = strtok(NULL, ":")) != NULL) {
pwd->pw_gid = atoi(token);
}
// Parse the user information.
if ((token = strtok(NULL, ":")) != NULL) {
pwd->pw_gecos = token;
}
// Parse the dir.
if ((token = strtok(NULL, ":")) != NULL) {
pwd->pw_dir = token;
}
// Parse the shell.
if ((token = strtok(NULL, ":")) != NULL) {
pwd->pw_shell = token;
// Find carriege return.
if ((ch = strchr(pwd->pw_shell, '\r'))) {
*ch = 0;
}
// Find newline.
if ((ch = strchr(pwd->pw_shell, '\n'))) {
*ch = 0;
}
}
}
/// @brief Reads a line from the file.
/// @param fd the file descriptor.
/// @param buffer the buffer where we place the line.
/// @param buflen the length of the buffer.
/// @return the amount we read.
ssize_t __readline(int fd, char *buffer, size_t buflen)
{
memset(buffer, 0, buflen);
long num_read = read(fd, buffer, buflen);
if (num_read == 0) {
return 0;
}
char *newline = strchr(buffer, '\n');
if (newline == NULL) {
newline = strchr(buffer, EOF);
if (newline == NULL) {
newline = strchr(buffer, 0);
if (newline == NULL) {
return 0;
}
}
}
long newline_len = (newline - buffer);
if (newline_len <= 0) {
return 0;
}
buffer[newline_len] = 0;
long rollback = newline_len - num_read + 1;
if (rollback > 1) {
return 0;
}
lseek(fd, rollback, SEEK_CUR);
return newline_len;
}
/// @brief Searches for the given entry inside the buffer.
/// @param fd the file descriptor of the file.
/// @param buffer the support buffer we use to read the file.
/// @param buflen the length of the support buffer.
/// @param name the username we are looking for.
/// @param uid the user-id of the user we are looking for.
/// @return the buffer itself if we have found the entry, NULL otherwise.
static inline char *__search_entry(int fd, char *buffer, int buflen, const char *name, uid_t uid)
{
while (__readline(fd, buffer, buflen)) {
if (name != NULL) {
char *name_end = strchr(buffer, ':');
if (name_end) {
*name_end = '\0';
if (strncmp(buffer, name, strlen(name)) == 0) {
*name_end = ':';
return buffer;
}
}
} else {
// Name
char *ptr = strchr(buffer, ':');
if (ptr == NULL) {
continue;
}
// Password
++ptr;
char *uid_start = strchr(ptr, ':');
if (uid_start == NULL) {
continue;
}
++uid_start;
ptr = strchr(uid_start, ':');
if (ptr == NULL) {
continue;
}
*ptr = '\0';
// Parse the uid.
int found_uid = atoi(uid_start);
// Check the uid.
if (found_uid == uid) {
*ptr = ':';
return buffer;
}
}
}
return NULL;
}
passwd_t *getpwnam(const char *name)
{
if (name == NULL) {
return NULL;
}
static passwd_t pwd;
static char buffer[BUFSIZ];
passwd_t *result;
if (!getpwnam_r(name, &pwd, buffer, BUFSIZ, &result)) {
return NULL;
}
return &pwd;
}
passwd_t *getpwuid(uid_t uid)
{
static passwd_t pwd;
static char buffer[BUFSIZ];
passwd_t *result;
if (!getpwuid_r(uid, &pwd, buffer, BUFSIZ, &result)) {
return NULL;
}
return &pwd;
}
int getpwnam_r(const char *name, passwd_t *pwd, char *buf, size_t buflen, passwd_t **result)
{
if (name == NULL) {
return 0;
}
int fd = open("/etc/passwd", O_RDONLY, 0);
if (fd == -1) {
pr_debug("Cannot open `/etc/passwd`\n");
errno = ENOENT;
*result = NULL;
return 0;
}
char *entry = __search_entry(fd, buf, buflen, name, 0);
if (entry != NULL) {
// Close the file.
close(fd);
// Parse the line.
__parse_line(pwd, entry);
// Return success.
return 1;
}
errno = ENOENT;
// Close the file.
close(fd);
// Return fail.
return 0;
}
int getpwuid_r(uid_t uid, passwd_t *pwd, char *buf, size_t buflen, passwd_t **result)
{
int fd = open("/etc/passwd", O_RDONLY, 0);
if (fd == -1) {
pr_debug("Cannot open `/etc/passwd`\n");
errno = ENOENT;
*result = NULL;
return 0;
}
char *entry = __search_entry(fd, buf, buflen, NULL, uid);
if (entry != NULL) {
// Close the file.
close(fd);
// Parse the line.
__parse_line(pwd, entry);
// Return success.
return 1;
}
// Close the file.
close(fd);
// Return fail.
return 0;
}