Serenity Operating System
at master 311 lines 7.9 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * Copyright (c) 2021, Gunnar Beutner <gbeutner@serenityos.org> 4 * 5 * SPDX-License-Identifier: BSD-2-Clause 6 */ 7 8#include <AK/DeprecatedString.h> 9#include <AK/TemporaryChange.h> 10#include <AK/Vector.h> 11#include <errno.h> 12#include <shadow.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; 21static struct spwd s_shadow_entry; 22 23static DeprecatedString s_name; 24static DeprecatedString s_pwdp; 25 26void setspent() 27{ 28 s_line_number = 0; 29 if (s_stream) { 30 rewind(s_stream); 31 } else { 32 s_stream = fopen("/etc/shadow", "r"); 33 if (!s_stream) { 34 dbgln("open /etc/shadow failed: {}", strerror(errno)); 35 } 36 } 37} 38 39void endspent() 40{ 41 s_line_number = 0; 42 if (s_stream) { 43 fclose(s_stream); 44 s_stream = nullptr; 45 } 46 47 memset(&s_shadow_entry, 0, sizeof(s_shadow_entry)); 48 49 s_name = {}; 50 s_pwdp = {}; 51} 52 53struct spwd* getspnam(char const* name) 54{ 55 setspent(); 56 while (auto* sp = getspent()) { 57 if (!strcmp(sp->sp_namp, name)) { 58 return sp; 59 } 60 } 61 return nullptr; 62} 63 64static bool parse_shadow_entry(DeprecatedString const& line) 65{ 66 auto parts = line.split_view(':', SplitBehavior::KeepEmpty); 67 if (parts.size() != 9) { 68 dbgln("getspent(): Malformed entry on line {}", s_line_number); 69 return false; 70 } 71 72 s_name = parts[0]; 73 s_pwdp = parts[1]; 74 auto& lstchg_string = parts[2]; 75 auto& min_string = parts[3]; 76 auto& max_string = parts[4]; 77 auto& warn_string = parts[5]; 78 auto& inact_string = parts[6]; 79 auto& expire_string = parts[7]; 80 auto& flag_string = parts[8]; 81 82 auto lstchg = lstchg_string.to_int(); 83 if (!lstchg.has_value()) { 84 dbgln("getspent(): Malformed lstchg on line {}", s_line_number); 85 return false; 86 } 87 88 if (min_string.is_empty()) 89 min_string = "-1"sv; 90 auto min_value = min_string.to_int(); 91 if (!min_value.has_value()) { 92 dbgln("getspent(): Malformed min value on line {}", s_line_number); 93 return false; 94 } 95 96 if (max_string.is_empty()) 97 max_string = "-1"sv; 98 auto max_value = max_string.to_int(); 99 if (!max_value.has_value()) { 100 dbgln("getspent(): Malformed max value on line {}", s_line_number); 101 return false; 102 } 103 104 if (warn_string.is_empty()) 105 warn_string = "-1"sv; 106 auto warn = warn_string.to_int(); 107 if (!warn.has_value()) { 108 dbgln("getspent(): Malformed warn on line {}", s_line_number); 109 return false; 110 } 111 112 if (inact_string.is_empty()) 113 inact_string = "-1"sv; 114 auto inact = inact_string.to_int(); 115 if (!inact.has_value()) { 116 dbgln("getspent(): Malformed inact on line {}", s_line_number); 117 return false; 118 } 119 120 if (expire_string.is_empty()) 121 expire_string = "-1"sv; 122 auto expire = expire_string.to_int(); 123 if (!expire.has_value()) { 124 dbgln("getspent(): Malformed expire on line {}", s_line_number); 125 return false; 126 } 127 128 if (flag_string.is_empty()) 129 flag_string = "0"sv; 130 auto flag = flag_string.to_int(); 131 if (!flag.has_value()) { 132 dbgln("getspent(): Malformed flag on line {}", s_line_number); 133 return false; 134 } 135 136 s_shadow_entry.sp_namp = const_cast<char*>(s_name.characters()); 137 s_shadow_entry.sp_pwdp = const_cast<char*>(s_pwdp.characters()); 138 s_shadow_entry.sp_lstchg = lstchg.value(); 139 s_shadow_entry.sp_min = min_value.value(); 140 s_shadow_entry.sp_max = max_value.value(); 141 s_shadow_entry.sp_warn = warn.value(); 142 s_shadow_entry.sp_inact = inact.value(); 143 s_shadow_entry.sp_expire = expire.value(); 144 s_shadow_entry.sp_flag = flag.value(); 145 146 return true; 147} 148 149struct spwd* getspent() 150{ 151 if (!s_stream) 152 setspent(); 153 154 while (true) { 155 if (!s_stream || feof(s_stream)) 156 return nullptr; 157 158 if (ferror(s_stream)) { 159 dbgln("getspent(): Read error: {}", strerror(ferror(s_stream))); 160 return nullptr; 161 } 162 163 char buffer[1024]; 164 ++s_line_number; 165 char* s = fgets(buffer, sizeof(buffer), s_stream); 166 167 // Silently tolerate an empty line at the end. 168 if ((!s || !s[0]) && feof(s_stream)) 169 return nullptr; 170 171 DeprecatedString line(s, Chomp); 172 if (parse_shadow_entry(line)) 173 return &s_shadow_entry; 174 // Otherwise, proceed to the next line. 175 } 176} 177 178static void construct_spwd(struct spwd* sp, char* buf, struct spwd** result) 179{ 180 auto* buf_name = &buf[0]; 181 auto* buf_pwdp = &buf[s_name.length() + 1]; 182 183 bool ok = true; 184 ok = ok && s_name.copy_characters_to_buffer(buf_name, s_name.length() + 1); 185 ok = ok && s_pwdp.copy_characters_to_buffer(buf_pwdp, s_pwdp.length() + 1); 186 187 VERIFY(ok); 188 189 *result = sp; 190 sp->sp_namp = buf_name; 191 sp->sp_pwdp = buf_pwdp; 192} 193 194int getspnam_r(char const* name, struct spwd* sp, char* buf, size_t buflen, struct spwd** result) 195{ 196 // FIXME: This is a HACK! 197 TemporaryChange name_change { s_name, {} }; 198 TemporaryChange pwdp_change { s_pwdp, {} }; 199 200 setspent(); 201 bool found = false; 202 while (auto* sp = getspent()) { 203 if (!strcmp(sp->sp_namp, name)) { 204 found = true; 205 break; 206 } 207 } 208 209 if (!found) { 210 *result = nullptr; 211 return 0; 212 } 213 214 auto const total_buffer_length = s_name.length() + s_pwdp.length() + 8; 215 if (buflen < total_buffer_length) 216 return ERANGE; 217 218 construct_spwd(sp, buf, result); 219 return 0; 220} 221 222int putspent(struct spwd* p, FILE* stream) 223{ 224 if (!p || !stream || !p->sp_namp || !p->sp_pwdp) { 225 errno = EINVAL; 226 return -1; 227 } 228 229 auto is_valid_field = [](char const* str) { 230 return str && !strpbrk(str, ":\n"); 231 }; 232 233 if (!is_valid_field(p->sp_namp) || !is_valid_field(p->sp_pwdp)) { 234 errno = EINVAL; 235 return -1; 236 } 237 238 int nwritten; 239 240 nwritten = fprintf(stream, "%s:%s:", p->sp_namp, p->sp_pwdp); 241 if (!nwritten || nwritten < 0) { 242 errno = ferror(stream); 243 return -1; 244 } 245 246 if (p->sp_lstchg != (long int)-1) 247 nwritten = fprintf(stream, "%ld:", p->sp_lstchg); 248 else 249 nwritten = fprintf(stream, "%c", ':'); 250 if (!nwritten || nwritten < 0) { 251 errno = ferror(stream); 252 return -1; 253 } 254 255 if (p->sp_min != (long int)-1) 256 nwritten = fprintf(stream, "%ld:", p->sp_min); 257 else 258 nwritten = fprintf(stream, "%c", ':'); 259 if (!nwritten || nwritten < 0) { 260 errno = ferror(stream); 261 return -1; 262 } 263 264 if (p->sp_max != (long int)-1) 265 nwritten = fprintf(stream, "%ld:", p->sp_max); 266 else 267 nwritten = fprintf(stream, "%c", ':'); 268 if (!nwritten || nwritten < 0) { 269 errno = ferror(stream); 270 return -1; 271 } 272 273 if (p->sp_warn != (long int)-1) 274 nwritten = fprintf(stream, "%ld:", p->sp_warn); 275 else 276 nwritten = fprintf(stream, "%c", ':'); 277 if (!nwritten || nwritten < 0) { 278 errno = ferror(stream); 279 return -1; 280 } 281 282 if (p->sp_inact != (long int)-1) 283 nwritten = fprintf(stream, "%ld:", p->sp_inact); 284 else 285 nwritten = fprintf(stream, "%c", ':'); 286 if (!nwritten || nwritten < 0) { 287 errno = ferror(stream); 288 return -1; 289 } 290 291 if (p->sp_expire != (long int)-1) 292 nwritten = fprintf(stream, "%ld:", p->sp_expire); 293 else 294 nwritten = fprintf(stream, "%c", ':'); 295 if (!nwritten || nwritten < 0) { 296 errno = ferror(stream); 297 return -1; 298 } 299 300 if (p->sp_flag != (unsigned long int)-1) 301 nwritten = fprintf(stream, "%ld\n", p->sp_flag); 302 else 303 nwritten = fprintf(stream, "%c", '\n'); 304 if (!nwritten || nwritten < 0) { 305 errno = ferror(stream); 306 return -1; 307 } 308 309 return 0; 310} 311}