Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <AK/String.h>
28#include <AK/Vector.h>
29#include <pwd.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33#include <sys/mman.h>
34#include <unistd.h>
35
36extern "C" {
37
38#define PWDB_STR_MAX_LEN 256
39
40struct passwd_with_strings : public passwd {
41 char name_buffer[PWDB_STR_MAX_LEN];
42 char passwd_buffer[PWDB_STR_MAX_LEN];
43 char gecos_buffer[PWDB_STR_MAX_LEN];
44 char dir_buffer[PWDB_STR_MAX_LEN];
45 char shell_buffer[PWDB_STR_MAX_LEN];
46};
47
48static FILE* __pwdb_stream = nullptr;
49static unsigned __pwdb_line_number = 0;
50static struct passwd_with_strings* __pwdb_entry = nullptr;
51
52void setpwent()
53{
54 __pwdb_line_number = 0;
55 if (__pwdb_stream) {
56 rewind(__pwdb_stream);
57 } else {
58 __pwdb_stream = fopen("/etc/passwd", "r");
59 if (!__pwdb_stream) {
60 perror("open /etc/passwd");
61 }
62 assert(__pwdb_stream);
63 __pwdb_entry = (struct passwd_with_strings*)mmap_with_name(nullptr, getpagesize(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0, "setpwent");
64 }
65}
66
67void endpwent()
68{
69 __pwdb_line_number = 0;
70 if (__pwdb_stream) {
71 fclose(__pwdb_stream);
72 __pwdb_stream = nullptr;
73 }
74 if (__pwdb_entry) {
75 munmap(__pwdb_entry, getpagesize());
76 __pwdb_entry = nullptr;
77 }
78}
79
80struct passwd* getpwuid(uid_t uid)
81{
82 setpwent();
83 while (auto* pw = getpwent()) {
84 if (pw->pw_uid == uid)
85 return pw;
86 }
87 return nullptr;
88}
89
90struct passwd* getpwnam(const char* name)
91{
92 setpwent();
93 while (auto* pw = getpwent()) {
94 if (!strcmp(pw->pw_name, name))
95 return pw;
96 }
97 return nullptr;
98}
99
100struct passwd* getpwent()
101{
102 if (!__pwdb_stream)
103 setpwent();
104
105 assert(__pwdb_stream);
106 if (feof(__pwdb_stream))
107 return nullptr;
108
109next_entry:
110 char buffer[1024];
111 ++__pwdb_line_number;
112 char* s = fgets(buffer, sizeof(buffer), __pwdb_stream);
113 if (!s)
114 return nullptr;
115 assert(__pwdb_stream);
116 if (feof(__pwdb_stream))
117 return nullptr;
118 String line(s, Chomp);
119 auto parts = line.split(':', true);
120 if (parts.size() != 7) {
121 fprintf(stderr, "getpwent(): Malformed entry on line %u\n", __pwdb_line_number);
122 goto next_entry;
123 }
124 auto& e_name = parts[0];
125 auto& e_passwd = parts[1];
126 auto& e_uid_string = parts[2];
127 auto& e_gid_string = parts[3];
128 auto& e_gecos = parts[4];
129 auto& e_dir = parts[5];
130 auto& e_shell = parts[6];
131 bool ok;
132 uid_t e_uid = e_uid_string.to_uint(ok);
133 if (!ok) {
134 fprintf(stderr, "getpwent(): Malformed UID on line %u\n", __pwdb_line_number);
135 goto next_entry;
136 }
137 gid_t e_gid = e_gid_string.to_uint(ok);
138 if (!ok) {
139 fprintf(stderr, "getpwent(): Malformed GID on line %u\n", __pwdb_line_number);
140 goto next_entry;
141 }
142 __pwdb_entry->pw_uid = e_uid;
143 __pwdb_entry->pw_gid = e_gid;
144 __pwdb_entry->pw_name = __pwdb_entry->name_buffer;
145 __pwdb_entry->pw_passwd = __pwdb_entry->passwd_buffer;
146 __pwdb_entry->pw_gecos = __pwdb_entry->gecos_buffer;
147 __pwdb_entry->pw_dir = __pwdb_entry->dir_buffer;
148 __pwdb_entry->pw_shell = __pwdb_entry->shell_buffer;
149 strncpy(__pwdb_entry->name_buffer, e_name.characters(), PWDB_STR_MAX_LEN);
150 strncpy(__pwdb_entry->passwd_buffer, e_passwd.characters(), PWDB_STR_MAX_LEN);
151 strncpy(__pwdb_entry->gecos_buffer, e_gecos.characters(), PWDB_STR_MAX_LEN);
152 strncpy(__pwdb_entry->dir_buffer, e_dir.characters(), PWDB_STR_MAX_LEN);
153 strncpy(__pwdb_entry->shell_buffer, e_shell.characters(), PWDB_STR_MAX_LEN);
154 return __pwdb_entry;
155}
156
157int putpwent(const struct passwd* p, FILE* stream)
158{
159 if (!p || !stream || !p->pw_name || !p->pw_dir || !p->pw_gecos || !p->pw_shell) {
160 errno = EINVAL;
161 return -1;
162 }
163
164 auto is_valid_field = [](const char* str) {
165 return str && !strpbrk(str, ":\n");
166 };
167
168 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)) {
169 errno = EINVAL;
170 return -1;
171 }
172
173 int nwritten = fprintf(stream, "%s:x:%u:%u:%s,,,:%s:%s\n", p->pw_name, p->pw_uid, p->pw_gid, p->pw_gecos, p->pw_dir, p->pw_shell);
174 if (!nwritten || nwritten < 0) {
175 errno = ferror(stream);
176 return -1;
177 }
178
179 return 0;
180}
181}