git.cappig.dev / apheleiaOS.git master

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
}