1
#include <errno.h>
2
#include <fcntl.h>
3
#include <libc_usr/pwd.h>
4
#include <parse/textdb.h>
5
#include <stdbool.h>
6
#include <stdlib.h>
7
#include <string.h>
8
#include <unistd.h>
9
#include <user/kv.h>
10
11
#define PASSWD_PATH "/etc/passwd"
12
13
static int copy_field(char **cursor, size_t *left, char **out, const char *src) {
14
if (!cursor || !left || !out || !src) {
15
return ERANGE;
16
}17
18
size_t n = strlen(src) + 1;
19
if (n > *left) {
20
return ERANGE;
21
}22
23
memcpy(*cursor, src, n);
24
*out = *cursor;
25
*cursor += n;
26
*left -= n;
27
return 0;
28
}29
30
static int parse_passwd_line(const char *line, struct passwd *pwd, char *buf, size_t buflen) {
31
if (!line || !pwd || !buf || !buflen) {
32
return EINVAL;
33
}34
35
char pw_name[64] = { 0 };
36
char pw_passwd[128] = { 0 };
37
char uid_buf[32] = { 0 };
38
char gid_buf[32] = { 0 };
39
char pw_gecos[128] = { 0 };
40
char pw_dir[128] = { 0 };
41
char pw_shell[128] = { 0 };
42
43
const char *cursor = line;
44
45
cursor = textdb_next_field(cursor, pw_name, sizeof(pw_name));
46
cursor = textdb_next_field(cursor, pw_passwd, sizeof(pw_passwd));
47
cursor = textdb_next_field(cursor, uid_buf, sizeof(uid_buf));
48
cursor = textdb_next_field(cursor, gid_buf, sizeof(gid_buf));
49
cursor = textdb_next_field(cursor, pw_gecos, sizeof(pw_gecos));
50
cursor = textdb_next_field(cursor, pw_dir, sizeof(pw_dir));
51
cursor = textdb_next_field(cursor, pw_shell, sizeof(pw_shell));
52
53
(void)cursor;
54
55
char *end = NULL;
56
long uid = strtol(uid_buf, &end, 10);
57
58
if (end == uid_buf || *end != '\0' || uid < 0) {
59
return EINVAL;
60
}61
62
end = NULL;
63
long gid = strtol(gid_buf, &end, 10);
64
65
if (end == gid_buf || *end != '\0' || gid < 0) {
66
return EINVAL;
67
}68
69
memset(pwd, 0, sizeof(*pwd));
70
pwd->pw_uid = (uid_t)uid;
71
pwd->pw_gid = (gid_t)gid;
72
73
char *dst = buf;
74
size_t left = buflen;
75
76
int rc = copy_field(&dst, &left, &pwd->pw_name, pw_name);
77
78
if (!rc) {
79
rc = copy_field(&dst, &left, &pwd->pw_passwd, pw_passwd);
80
}81
if (!rc) {
82
rc = copy_field(&dst, &left, &pwd->pw_gecos, pw_gecos);
83
}84
if (!rc) {
85
rc = copy_field(&dst, &left, &pwd->pw_dir, pw_dir);
86
}87
if (!rc) {
88
rc = copy_field(&dst, &left, &pwd->pw_shell, pw_shell);
89
}90
91
return rc;
92
}93
94
static int find_passwd(
95
const char *match_name,
96
uid_t match_uid,
97
bool by_name,
98
struct passwd *pwd,
99
char *buf,
100
size_t buflen,
101
struct passwd **result
102
) {
103
if (!pwd || !buf || !buflen || !result) {
104
return EINVAL;
105
}106
107
char file_buf[4096];
108
int fd = open(PASSWD_PATH, O_RDONLY, 0);
109
if (fd < 0) {
110
return ENOENT;
111
}112
113
ssize_t len = kv_read_fd(fd, file_buf, sizeof(file_buf));
114
close(fd);
115
if (len <= 0) {
116
return ENOENT;
117
}118
119
const char *line = file_buf;
120
while (*line) {
121
const char *next = strchr(line, '\n');
122
size_t line_len = next ? (size_t)(next - line) : strlen(line);
123
124
if (line_len) {
125
char tmp[256];
126
if (line_len >= sizeof(tmp)) {
127
return ERANGE;
128
}129
130
memcpy(tmp, line, line_len);
131
tmp[line_len] = '\0';
132
133
struct passwd parsed = { 0 };
134
int rc = parse_passwd_line(tmp, &parsed, buf, buflen);
135
if (rc && rc != EINVAL) {
136
return rc;
137
}138
139
if (!rc) {
140
if (by_name) {
141
if (match_name && !strcmp(parsed.pw_name, match_name)) {
142
*pwd = parsed;
143
*result = pwd;
144
return 0;
145
}146
} else if (parsed.pw_uid == match_uid) {
147
*pwd = parsed;
148
*result = pwd;
149
return 0;
150
}151
}152
}153
154
if (!next) {
155
break;
156
}157
line = next + 1;
158
}159
160
*result = NULL;
161
return 0;
162
}163
164
int getpwnam_r(const char *name, struct passwd *pwd, char *buf, size_t buflen, struct passwd **result) {
165
if (!name || !pwd || !buf || !buflen || !result) {
166
return EINVAL;
167
}168
169
return find_passwd(name, 0, true, pwd, buf, buflen, result);
170
}171
172
int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf, size_t buflen, struct passwd **result) {
173
if (!pwd || !buf || !buflen || !result) {
174
return EINVAL;
175
}176
177
return find_passwd(NULL, uid, false, pwd, buf, buflen, result);
178
}179
180
struct passwd *getpwnam(const char *name) {
181
static struct passwd pwd;
182
static char buf[512];
183
184
struct passwd *result = NULL;
185
186
int rc = getpwnam_r(name, &pwd, buf, sizeof(buf), &result);
187
188
if (rc || !result) {
189
errno = rc ? rc : ENOENT;
190
return NULL;
191
}192
193
return result;
194
}195
196
struct passwd *getpwuid(uid_t uid) {
197
static struct passwd pwd;
198
static char buf[512];
199
200
struct passwd *result = NULL;
201
202
int rc = getpwuid_r(uid, &pwd, buf, sizeof(buf), &result);
203
204
if (rc || !result) {
205
errno = rc ? rc : ENOENT;
206
return NULL;
207
}208
209
return result;
210
}