Serenity Operating System
at master 207 lines 5.5 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <AK/DeprecatedString.h> 8#include <AK/ScopeGuard.h> 9#include <AK/TemporaryChange.h> 10#include <AK/Vector.h> 11#include <errno.h> 12#include <pwd.h> 13#include <stdio.h> 14#include <string.h> 15#include <unistd.h> 16 17extern "C" { 18 19static FILE* s_stream = nullptr; 20static unsigned s_line_number = 0; 21 22void setpwent() 23{ 24 s_line_number = 0; 25 if (s_stream) { 26 rewind(s_stream); 27 } else { 28 s_stream = fopen("/etc/passwd", "r"); 29 if (!s_stream) { 30 perror("open /etc/passwd"); 31 } 32 } 33} 34 35void endpwent() 36{ 37 s_line_number = 0; 38 if (s_stream) { 39 fclose(s_stream); 40 s_stream = nullptr; 41 } 42} 43 44struct passwd* getpwuid(uid_t uid) 45{ 46 setpwent(); 47 ScopeGuard guard = [] { endpwent(); }; 48 while (auto* pw = getpwent()) { 49 if (pw->pw_uid == uid) 50 return pw; 51 } 52 return nullptr; 53} 54 55struct passwd* getpwnam(char const* name) 56{ 57 setpwent(); 58 ScopeGuard guard = [] { endpwent(); }; 59 while (auto* pw = getpwent()) { 60 if (!strcmp(pw->pw_name, name)) 61 return pw; 62 } 63 return nullptr; 64} 65 66static bool parse_pwddb_entry(char* raw_line, struct passwd& passwd_entry) 67{ 68 size_t line_length = strlen(raw_line); 69 for (size_t i = 0; i < line_length; ++i) { 70 auto& ch = raw_line[i]; 71 if (ch == '\r' || ch == '\n') 72 line_length = i; 73 if (ch == ':' || ch == '\r' || ch == '\n') 74 ch = '\0'; 75 } 76 auto line = StringView { raw_line, line_length }; 77 auto parts = line.split_view('\0', SplitBehavior::KeepEmpty); 78 if (parts.size() != 7) { 79 dbgln("getpwent(): Malformed entry on line {}", s_line_number); 80 return false; 81 } 82 83 auto& name = parts[0]; 84 auto& passwd = parts[1]; 85 auto& uid_string = parts[2]; 86 auto& gid_string = parts[3]; 87 auto& gecos = parts[4]; 88 auto& dir = parts[5]; 89 auto& shell = parts[6]; 90 91 auto uid = uid_string.to_uint(); 92 if (!uid.has_value()) { 93 dbgln("getpwent(): Malformed UID on line {}", s_line_number); 94 return false; 95 } 96 auto gid = gid_string.to_uint(); 97 if (!gid.has_value()) { 98 dbgln("getpwent(): Malformed GID on line {}", s_line_number); 99 return false; 100 } 101 102 passwd_entry.pw_name = const_cast<char*>(name.characters_without_null_termination()); 103 passwd_entry.pw_passwd = const_cast<char*>(passwd.characters_without_null_termination()); 104 passwd_entry.pw_uid = uid.value(); 105 passwd_entry.pw_gid = gid.value(); 106 passwd_entry.pw_gecos = const_cast<char*>(gecos.characters_without_null_termination()); 107 passwd_entry.pw_dir = const_cast<char*>(dir.characters_without_null_termination()); 108 passwd_entry.pw_shell = const_cast<char*>(shell.characters_without_null_termination()); 109 110 return true; 111} 112 113struct passwd* getpwent() 114{ 115 static struct passwd passwd_entry; 116 static char buffer[1024]; 117 struct passwd* result; 118 if (getpwent_r(&passwd_entry, buffer, sizeof(buffer), &result) < 0) 119 return nullptr; 120 return result; 121} 122 123int getpwent_r(struct passwd* passwd_buf, char* buffer, size_t buffer_size, struct passwd** passwd_entry_ptr) 124{ 125 if (!s_stream) 126 setpwent(); 127 128 while (true) { 129 if (!s_stream || feof(s_stream)) { 130 *passwd_entry_ptr = nullptr; 131 return ENOENT; 132 } 133 134 if (ferror(s_stream)) { 135 *passwd_entry_ptr = nullptr; 136 return ferror(s_stream); 137 } 138 139 ++s_line_number; 140 char* s = fgets(buffer, buffer_size, s_stream); 141 142 if ((!s || !s[0]) && feof(s_stream)) { 143 *passwd_entry_ptr = nullptr; 144 return ENOENT; 145 } 146 147 if (strlen(s) == buffer_size - 1) { 148 *passwd_entry_ptr = nullptr; 149 return ERANGE; 150 } 151 152 if (parse_pwddb_entry(buffer, *passwd_buf)) { 153 *passwd_entry_ptr = passwd_buf; 154 return 0; 155 } 156 } 157} 158 159// https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpwnam.html 160int getpwnam_r(char const* name, struct passwd* pwd, char* buffer, size_t bufsize, struct passwd** result) 161{ 162 setpwent(); 163 for (;;) { 164 if (auto rc = getpwent_r(pwd, buffer, bufsize, result); rc != 0) 165 return rc; 166 if (strcmp(pwd->pw_name, name) == 0) 167 return 0; 168 } 169} 170 171// https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpwuid.html 172int getpwuid_r(uid_t uid, struct passwd* pwd, char* buffer, size_t bufsize, struct passwd** result) 173{ 174 setpwent(); 175 for (;;) { 176 if (auto rc = getpwent_r(pwd, buffer, bufsize, result); rc != 0) 177 return rc; 178 if (pwd->pw_uid == uid) 179 return 0; 180 } 181} 182 183int putpwent(const struct passwd* p, FILE* stream) 184{ 185 if (!p || !stream || !p->pw_passwd || !p->pw_name || !p->pw_dir || !p->pw_gecos || !p->pw_shell) { 186 errno = EINVAL; 187 return -1; 188 } 189 190 auto is_valid_field = [](char const* str) { 191 return str && !strpbrk(str, ":\n"); 192 }; 193 194 if (!is_valid_field(p->pw_name) || !is_valid_field(p->pw_dir) || !is_valid_field(p->pw_gecos) || !is_valid_field(p->pw_shell)) { 195 errno = EINVAL; 196 return -1; 197 } 198 199 int nwritten = fprintf(stream, "%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); 200 if (!nwritten || nwritten < 0) { 201 errno = ferror(stream); 202 return -1; 203 } 204 205 return 0; 206} 207}