mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 02:52:43 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			312 lines
		
	
	
	
		
			7.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			312 lines
		
	
	
	
		
			7.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
 | |
|  * Copyright (c) 2021, Gunnar Beutner <gbeutner@serenityos.org>
 | |
|  *
 | |
|  * SPDX-License-Identifier: BSD-2-Clause
 | |
|  */
 | |
| 
 | |
| #include <AK/String.h>
 | |
| #include <AK/TemporaryChange.h>
 | |
| #include <AK/Vector.h>
 | |
| #include <errno.h>
 | |
| #include <shadow.h>
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <unistd.h>
 | |
| 
 | |
| extern "C" {
 | |
| 
 | |
| static FILE* s_stream = nullptr;
 | |
| static unsigned s_line_number = 0;
 | |
| static struct spwd s_shadow_entry;
 | |
| 
 | |
| static String s_name;
 | |
| static String s_pwdp;
 | |
| 
 | |
| void setspent()
 | |
| {
 | |
|     s_line_number = 0;
 | |
|     if (s_stream) {
 | |
|         rewind(s_stream);
 | |
|     } else {
 | |
|         s_stream = fopen("/etc/shadow", "r");
 | |
|         if (!s_stream) {
 | |
|             dbgln("open /etc/shadow failed: {}", strerror(errno));
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| void endspent()
 | |
| {
 | |
|     s_line_number = 0;
 | |
|     if (s_stream) {
 | |
|         fclose(s_stream);
 | |
|         s_stream = nullptr;
 | |
|     }
 | |
| 
 | |
|     memset(&s_shadow_entry, 0, sizeof(s_shadow_entry));
 | |
| 
 | |
|     s_name = {};
 | |
|     s_pwdp = {};
 | |
| }
 | |
| 
 | |
| struct spwd* getspnam(char const* name)
 | |
| {
 | |
|     setspent();
 | |
|     while (auto* sp = getspent()) {
 | |
|         if (!strcmp(sp->sp_namp, name)) {
 | |
|             return sp;
 | |
|         }
 | |
|     }
 | |
|     return nullptr;
 | |
| }
 | |
| 
 | |
| static bool parse_shadow_entry(String const& line)
 | |
| {
 | |
|     auto parts = line.split_view(':', true);
 | |
|     if (parts.size() != 9) {
 | |
|         dbgln("getspent(): Malformed entry on line {}", s_line_number);
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     s_name = parts[0];
 | |
|     s_pwdp = parts[1];
 | |
|     auto& lstchg_string = parts[2];
 | |
|     auto& min_string = parts[3];
 | |
|     auto& max_string = parts[4];
 | |
|     auto& warn_string = parts[5];
 | |
|     auto& inact_string = parts[6];
 | |
|     auto& expire_string = parts[7];
 | |
|     auto& flag_string = parts[8];
 | |
| 
 | |
|     auto lstchg = lstchg_string.to_int();
 | |
|     if (!lstchg.has_value()) {
 | |
|         dbgln("getspent(): Malformed lstchg on line {}", s_line_number);
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     if (min_string.is_empty())
 | |
|         min_string = "-1";
 | |
|     auto min_value = min_string.to_int();
 | |
|     if (!min_value.has_value()) {
 | |
|         dbgln("getspent(): Malformed min value on line {}", s_line_number);
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     if (max_string.is_empty())
 | |
|         max_string = "-1";
 | |
|     auto max_value = max_string.to_int();
 | |
|     if (!max_value.has_value()) {
 | |
|         dbgln("getspent(): Malformed max value on line {}", s_line_number);
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     if (warn_string.is_empty())
 | |
|         warn_string = "-1";
 | |
|     auto warn = warn_string.to_int();
 | |
|     if (!warn.has_value()) {
 | |
|         dbgln("getspent(): Malformed warn on line {}", s_line_number);
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     if (inact_string.is_empty())
 | |
|         inact_string = "-1";
 | |
|     auto inact = inact_string.to_int();
 | |
|     if (!inact.has_value()) {
 | |
|         dbgln("getspent(): Malformed inact on line {}", s_line_number);
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     if (expire_string.is_empty())
 | |
|         expire_string = "-1";
 | |
|     auto expire = expire_string.to_int();
 | |
|     if (!expire.has_value()) {
 | |
|         dbgln("getspent(): Malformed expire on line {}", s_line_number);
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     if (flag_string.is_empty())
 | |
|         flag_string = "0";
 | |
|     auto flag = flag_string.to_int();
 | |
|     if (!flag.has_value()) {
 | |
|         dbgln("getspent(): Malformed flag on line {}", s_line_number);
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     s_shadow_entry.sp_namp = const_cast<char*>(s_name.characters());
 | |
|     s_shadow_entry.sp_pwdp = const_cast<char*>(s_pwdp.characters());
 | |
|     s_shadow_entry.sp_lstchg = lstchg.value();
 | |
|     s_shadow_entry.sp_min = min_value.value();
 | |
|     s_shadow_entry.sp_max = max_value.value();
 | |
|     s_shadow_entry.sp_warn = warn.value();
 | |
|     s_shadow_entry.sp_inact = inact.value();
 | |
|     s_shadow_entry.sp_expire = expire.value();
 | |
|     s_shadow_entry.sp_flag = flag.value();
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| struct spwd* getspent()
 | |
| {
 | |
|     if (!s_stream)
 | |
|         setspent();
 | |
| 
 | |
|     while (true) {
 | |
|         if (!s_stream || feof(s_stream))
 | |
|             return nullptr;
 | |
| 
 | |
|         if (ferror(s_stream)) {
 | |
|             dbgln("getspent(): Read error: {}", strerror(ferror(s_stream)));
 | |
|             return nullptr;
 | |
|         }
 | |
| 
 | |
|         char buffer[1024];
 | |
|         ++s_line_number;
 | |
|         char* s = fgets(buffer, sizeof(buffer), s_stream);
 | |
| 
 | |
|         // Silently tolerate an empty line at the end.
 | |
|         if ((!s || !s[0]) && feof(s_stream))
 | |
|             return nullptr;
 | |
| 
 | |
|         String line(s, Chomp);
 | |
|         if (parse_shadow_entry(line))
 | |
|             return &s_shadow_entry;
 | |
|         // Otherwise, proceed to the next line.
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void construct_spwd(struct spwd* sp, char* buf, struct spwd** result)
 | |
| {
 | |
|     auto* buf_name = &buf[0];
 | |
|     auto* buf_pwdp = &buf[s_name.length() + 1];
 | |
| 
 | |
|     bool ok = true;
 | |
|     ok = ok && s_name.copy_characters_to_buffer(buf_name, s_name.length() + 1);
 | |
|     ok = ok && s_pwdp.copy_characters_to_buffer(buf_pwdp, s_pwdp.length() + 1);
 | |
| 
 | |
|     VERIFY(ok);
 | |
| 
 | |
|     *result = sp;
 | |
|     sp->sp_namp = buf_name;
 | |
|     sp->sp_pwdp = buf_pwdp;
 | |
| }
 | |
| 
 | |
| int getspnam_r(char const* name, struct spwd* sp, char* buf, size_t buflen, struct spwd** result)
 | |
| {
 | |
|     // FIXME: This is a HACK!
 | |
|     TemporaryChange name_change { s_name, {} };
 | |
|     TemporaryChange pwdp_change { s_pwdp, {} };
 | |
| 
 | |
|     setspent();
 | |
|     bool found = false;
 | |
|     while (auto* sp = getspent()) {
 | |
|         if (!strcmp(sp->sp_namp, name)) {
 | |
|             found = true;
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (!found) {
 | |
|         *result = nullptr;
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
|     auto const total_buffer_length = s_name.length() + s_pwdp.length() + 8;
 | |
|     if (buflen < total_buffer_length)
 | |
|         return ERANGE;
 | |
| 
 | |
|     construct_spwd(sp, buf, result);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| int putspent(struct spwd* p, FILE* stream)
 | |
| {
 | |
|     if (!p || !stream || !p->sp_namp || !p->sp_pwdp) {
 | |
|         errno = EINVAL;
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     auto is_valid_field = [](char const* str) {
 | |
|         return str && !strpbrk(str, ":\n");
 | |
|     };
 | |
| 
 | |
|     if (!is_valid_field(p->sp_namp) || !is_valid_field(p->sp_pwdp)) {
 | |
|         errno = EINVAL;
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     int nwritten;
 | |
| 
 | |
|     nwritten = fprintf(stream, "%s:%s:", p->sp_namp, p->sp_pwdp);
 | |
|     if (!nwritten || nwritten < 0) {
 | |
|         errno = ferror(stream);
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     if (p->sp_lstchg != (long int)-1)
 | |
|         nwritten = fprintf(stream, "%ld:", p->sp_lstchg);
 | |
|     else
 | |
|         nwritten = fprintf(stream, "%c", ':');
 | |
|     if (!nwritten || nwritten < 0) {
 | |
|         errno = ferror(stream);
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     if (p->sp_min != (long int)-1)
 | |
|         nwritten = fprintf(stream, "%ld:", p->sp_min);
 | |
|     else
 | |
|         nwritten = fprintf(stream, "%c", ':');
 | |
|     if (!nwritten || nwritten < 0) {
 | |
|         errno = ferror(stream);
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     if (p->sp_max != (long int)-1)
 | |
|         nwritten = fprintf(stream, "%ld:", p->sp_max);
 | |
|     else
 | |
|         nwritten = fprintf(stream, "%c", ':');
 | |
|     if (!nwritten || nwritten < 0) {
 | |
|         errno = ferror(stream);
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     if (p->sp_warn != (long int)-1)
 | |
|         nwritten = fprintf(stream, "%ld:", p->sp_warn);
 | |
|     else
 | |
|         nwritten = fprintf(stream, "%c", ':');
 | |
|     if (!nwritten || nwritten < 0) {
 | |
|         errno = ferror(stream);
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     if (p->sp_inact != (long int)-1)
 | |
|         nwritten = fprintf(stream, "%ld:", p->sp_inact);
 | |
|     else
 | |
|         nwritten = fprintf(stream, "%c", ':');
 | |
|     if (!nwritten || nwritten < 0) {
 | |
|         errno = ferror(stream);
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     if (p->sp_expire != (long int)-1)
 | |
|         nwritten = fprintf(stream, "%ld:", p->sp_expire);
 | |
|     else
 | |
|         nwritten = fprintf(stream, "%c", ':');
 | |
|     if (!nwritten || nwritten < 0) {
 | |
|         errno = ferror(stream);
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     if (p->sp_flag != (unsigned long int)-1)
 | |
|         nwritten = fprintf(stream, "%ld\n", p->sp_flag);
 | |
|     else
 | |
|         nwritten = fprintf(stream, "%c", '\n');
 | |
|     if (!nwritten || nwritten < 0) {
 | |
|         errno = ferror(stream);
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| }
 | 
