Line data Source code
1 : /**
2 : * @file
3 : *
4 : * @brief Source for passwd plugin
5 : *
6 : * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
7 : *
8 : */
9 :
10 : #include "passwd.h"
11 :
12 : #include <kdberrors.h>
13 : #include <kdbhelper.h>
14 :
15 : #include <pwd.h>
16 : #include <stdio.h>
17 : #include <stdlib.h>
18 : #include <string.h>
19 : #include <sys/stat.h>
20 : #include <sys/types.h>
21 :
22 : #define ID_MAX_CHARACTERS 11
23 :
24 : typedef enum
25 : {
26 : NAME,
27 : UID,
28 : } SortBy;
29 :
30 : // simple validation of passwd entries
31 12 : static int validatepwent (struct passwd * pwd)
32 : {
33 12 : if (!pwd->pw_name) return -1;
34 12 : if (strlen (pwd->pw_name) == 0) return -1;
35 12 : if (pwd->pw_name[0] == '-') // POSIX.1-2008 3.431 User Name
36 : return -1;
37 : const char * invalidCharacters =
38 : "/:;<=>?@[\\]^`"; // POSIX.1-2008 3.278 Portable File Character Set - invalid characters > 45 && <= 122
39 48 : for (char * ptr = pwd->pw_name; *ptr != '\0'; ++ptr)
40 : {
41 48 : if ((*ptr < '-') || (*ptr > 'z') || (strchr (invalidCharacters, *ptr) != NULL)) return -1;
42 : }
43 12 : if (!pwd->pw_passwd) return -1;
44 12 : if (pwd->pw_uid == (uid_t) -1) return -1;
45 12 : if (pwd->pw_gid == (gid_t) -1) return -1;
46 12 : if (!pwd->pw_gecos) return -1;
47 12 : if (!pwd->pw_dir) return -1;
48 12 : if (!pwd->pw_shell) return -1;
49 12 : return 0;
50 : }
51 :
52 : #if defined(USE_FGETPWENT_LOCAL)
53 : // clang-format off
54 : /* Taken from musl libc
55 : *
56 : * https://github.com/ifduyue/musl/blob/b4b1e10364c8737a632be61582e05a8d3acf5690/src/passwd/getpwent_a.c
57 : * https://github.com/ifduyue/musl/blob/b4b1e10364c8737a632be61582e05a8d3acf5690/src/passwd/fgetpwent.c
58 : */
59 :
60 : static unsigned atou(char **s)
61 : {
62 : unsigned x;
63 : for (x=0; (unsigned char)(**s-'0')<10U; ++*s) x=10*x+((unsigned char)**s-'0');
64 : return x;
65 : }
66 :
67 : int __getpwent_a(FILE *f, struct passwd *pw, char **line, size_t *size, struct passwd **res)
68 : {
69 : ssize_t l;
70 : char *s;
71 : int rv = 0;
72 : for (;;) {
73 : if ((l=getline(line, size, f)) < 0) {
74 : rv = ferror(f) ? errno : 0;
75 : free(*line);
76 : *line = 0;
77 : pw = 0;
78 : break;
79 : }
80 : line[0][l-1] = 0;
81 :
82 : s = line[0];
83 : pw->pw_name = s++;
84 : if (!(s = strchr(s, ':'))) continue;
85 :
86 : *s++ = 0; pw->pw_passwd = s;
87 : if (!(s = strchr(s, ':'))) continue;
88 :
89 : *s++ = 0; pw->pw_uid = atou(&s);
90 : if (*s != ':') continue;
91 :
92 : *s++ = 0; pw->pw_gid = atou(&s);
93 : if (*s != ':') continue;
94 :
95 : *s++ = 0; pw->pw_gecos = s;
96 : if (!(s = strchr(s, ':'))) continue;
97 :
98 : *s++ = 0; pw->pw_dir = s;
99 : if (!(s = strchr(s, ':'))) continue;
100 :
101 : *s++ = 0; pw->pw_shell = s;
102 : break;
103 : }
104 : *res = pw;
105 : if (rv) errno = rv;
106 : return rv;
107 : }
108 :
109 : struct passwd *fgetpwent_l(FILE *f)
110 : {
111 : static char *line;
112 : static struct passwd pw;
113 : size_t size=0;
114 : struct passwd *res;
115 : __getpwent_a(f, &pw, &line, &size, &res);
116 : return res;
117 : }
118 : // clang-format on
119 : #endif
120 :
121 20 : static KeySet * pwentToKS (struct passwd * pwd, Key * parentKey, SortBy index)
122 : {
123 20 : KeySet * ks = ksNew (0, KS_END);
124 20 : Key * append = keyNew (keyName (parentKey), KEY_END);
125 : char id[ID_MAX_CHARACTERS];
126 20 : if (index == UID)
127 : {
128 0 : snprintf (id, sizeof (id), "%u", pwd->pw_uid);
129 0 : keyAddBaseName (append, id);
130 0 : keySetBinary (append, 0, 0);
131 0 : ksAppendKey (ks, keyDup (append));
132 0 : keyAddBaseName (append, "name");
133 0 : keySetString (append, pwd->pw_name);
134 : }
135 : else
136 : {
137 20 : keyAddBaseName (append, pwd->pw_name);
138 20 : keySetBinary (append, 0, 0);
139 20 : ksAppendKey (ks, keyDup (append));
140 20 : snprintf (id, sizeof (id), "%u", pwd->pw_uid);
141 20 : keyAddBaseName (append, "uid");
142 20 : keySetString (append, id);
143 : }
144 20 : ksAppendKey (ks, keyDup (append));
145 20 : keySetString (append, 0);
146 20 : keySetBaseName (append, "shell");
147 20 : keySetString (append, pwd->pw_shell);
148 20 : ksAppendKey (ks, keyDup (append));
149 20 : keySetString (append, 0);
150 20 : keySetBaseName (append, "home");
151 20 : keySetString (append, pwd->pw_dir);
152 20 : ksAppendKey (ks, keyDup (append));
153 20 : keySetString (append, 0);
154 20 : keySetBaseName (append, "gid");
155 20 : snprintf (id, sizeof (id), "%u", pwd->pw_gid);
156 20 : keySetString (append, id);
157 20 : ksAppendKey (ks, keyDup (append));
158 20 : keySetString (append, 0);
159 20 : keySetBaseName (append, "passwd");
160 20 : keySetString (append, pwd->pw_passwd);
161 20 : ksAppendKey (ks, keyDup (append));
162 20 : keySetString (append, 0);
163 20 : keySetBaseName (append, "gecos");
164 20 : keySetString (append, pwd->pw_gecos);
165 20 : ksAppendKey (ks, keyDup (append));
166 20 : keyDel (append);
167 20 : return ks;
168 : }
169 :
170 24 : int elektraPasswdGet (Plugin * handle ELEKTRA_UNUSED, KeySet * returned ELEKTRA_UNUSED, Key * parentKey ELEKTRA_UNUSED)
171 : {
172 24 : if (!elektraStrCmp (keyName (parentKey), "system/elektra/modules/passwd"))
173 : {
174 20 : KeySet * contract =
175 20 : ksNew (30, keyNew ("system/elektra/modules/passwd", KEY_VALUE, "passwd plugin waits for your orders", KEY_END),
176 : keyNew ("system/elektra/modules/passwd/exports", KEY_END),
177 : keyNew ("system/elektra/modules/passwd/exports/get", KEY_FUNC, elektraPasswdGet, KEY_END),
178 : keyNew ("system/elektra/modules/passwd/exports/set", KEY_FUNC, elektraPasswdSet, KEY_END),
179 : #include ELEKTRA_README
180 : keyNew ("system/elektra/modules/passwd/infos/version", KEY_VALUE, PLUGINVERSION, KEY_END), KS_END);
181 20 : ksAppend (returned, contract);
182 20 : ksDel (contract);
183 :
184 20 : return 1; // success
185 : }
186 : // get all keys
187 : SortBy index;
188 4 : KeySet * config = elektraPluginGetConfig (handle);
189 4 : Key * sortByKey = ksLookupByName (config, "/index", 0);
190 4 : if (sortByKey)
191 : {
192 4 : if (!strcmp (keyString (sortByKey), "uid"))
193 : index = UID;
194 4 : else if (!strcmp (keyString (sortByKey), "name"))
195 : index = NAME;
196 : else
197 0 : index = UID;
198 : }
199 : else
200 : index = UID;
201 : struct passwd * pwd;
202 4 : FILE * pwfile = fopen (keyString (parentKey), "r");
203 4 : if (!pwfile)
204 : {
205 0 : ELEKTRA_SET_RESOURCE_ERRORF (parentKey, "Failed to open configuration file %s. Reason: %s\n", keyString (parentKey),
206 : strerror (errno));
207 0 : return -1;
208 : }
209 : #if defined(USE_FGETPWENT)
210 24 : while ((pwd = fgetpwent (pwfile)) != NULL)
211 : #elif defined(USE_FGETPWENT_LOCAL)
212 : while ((pwd = fgetpwent_l (pwfile)) != NULL)
213 : #else
214 : #error Configuration error in CMakeLists.txt. Neither fgetpwent nor getline were provided. Please open an issue at https://issue.libelektra.org.
215 : #endif
216 : {
217 20 : KeySet * ks = pwentToKS (pwd, parentKey, index);
218 20 : ksAppend (returned, ks);
219 20 : ksDel (ks);
220 : }
221 4 : fclose (pwfile);
222 4 : return 1; // success
223 : }
224 :
225 12 : static struct passwd * KStoPasswd (KeySet * ks, SortBy index)
226 : {
227 12 : struct passwd * pwd = elektraMalloc (sizeof (struct passwd));
228 12 : ksRewind (ks);
229 12 : Key * parent = ksNext (ks);
230 12 : Key * lookup = keyDup (parent);
231 12 : Key * found = NULL;
232 12 : if (index == UID)
233 : {
234 0 : found = ksLookup (ks, parent, 0);
235 0 : if (!found)
236 0 : pwd->pw_uid = (uid_t) -1;
237 : else
238 0 : pwd->pw_uid = atoi (keyBaseName (found));
239 0 : keyAddBaseName (lookup, "name");
240 0 : found = ksLookup (ks, lookup, 0);
241 0 : if (!found)
242 0 : pwd->pw_name = NULL;
243 : else
244 0 : pwd->pw_name = (char *) keyString (found);
245 : }
246 : else
247 : {
248 12 : found = ksLookup (ks, parent, 0);
249 12 : if (!found)
250 0 : pwd->pw_name = NULL;
251 : else
252 12 : pwd->pw_name = (char *) keyBaseName (found);
253 12 : keyAddBaseName (lookup, "uid");
254 12 : found = ksLookup (ks, lookup, 0);
255 12 : if (!found)
256 0 : pwd->pw_uid = (uid_t) -1;
257 : else
258 24 : pwd->pw_uid = atoi (keyString (found));
259 : }
260 12 : keySetBaseName (lookup, "shell");
261 12 : found = ksLookup (ks, lookup, 0);
262 12 : if (!found)
263 0 : pwd->pw_shell = NULL;
264 : else
265 12 : pwd->pw_shell = (char *) keyString (found);
266 12 : keySetBaseName (lookup, "gid");
267 12 : found = ksLookup (ks, lookup, 0);
268 12 : if (!found)
269 0 : pwd->pw_gid = (gid_t) -1;
270 : else
271 24 : pwd->pw_gid = atoi (keyString (found));
272 12 : keySetBaseName (lookup, "home");
273 12 : found = ksLookup (ks, lookup, 0);
274 12 : if (!found)
275 0 : pwd->pw_dir = NULL;
276 : else
277 12 : pwd->pw_dir = (char *) keyString (found);
278 12 : keySetBaseName (lookup, "gecos");
279 12 : found = ksLookup (ks, lookup, 0);
280 12 : if (!found)
281 0 : pwd->pw_gecos = "";
282 : else
283 12 : pwd->pw_gecos = (char *) keyString (found);
284 12 : keySetBaseName (lookup, "passwd");
285 12 : found = ksLookup (ks, lookup, 0);
286 12 : if (!found)
287 0 : pwd->pw_passwd = "";
288 : else
289 12 : pwd->pw_passwd = (char *) keyString (found);
290 12 : keyDel (parent);
291 12 : keyDel (lookup);
292 12 : return pwd;
293 : }
294 :
295 4 : static int writeKS (KeySet * returned, Key * parentKey, SortBy index)
296 : {
297 4 : FILE * pwfile = fopen (keyString (parentKey), "w");
298 4 : if (!pwfile)
299 : {
300 0 : ELEKTRA_SET_RESOURCE_ERRORF (parentKey, "Failed to open %s for writing\n. Reason: %s", keyString (parentKey),
301 : strerror (errno));
302 0 : return -1;
303 : }
304 : Key * cur;
305 4 : ksRewind (returned);
306 20 : while ((cur = ksNext (returned)) != NULL)
307 : {
308 12 : if (!keyIsDirectBelow (parentKey, cur)) continue;
309 12 : KeySet * cutKS = ksCut (returned, cur);
310 12 : struct passwd * pwd = KStoPasswd (cutKS, index);
311 12 : if (validatepwent (pwd) == -1)
312 : {
313 0 : ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (parentKey, "Invalid passwd entry %s:%s:%u:%u:%s:%s:%s\n", pwd->pw_name,
314 : pwd->pw_passwd, pwd->pw_uid, pwd->pw_gid, pwd->pw_gecos, pwd->pw_dir,
315 : pwd->pw_shell);
316 : }
317 : else
318 : {
319 : #if defined(USE_PUTPWENT)
320 12 : putpwent (pwd, pwfile);
321 : #else
322 : fprintf (pwfile, "%s:%s:%u:%u:%s:%s:%s\n", pwd->pw_name, pwd->pw_passwd, pwd->pw_uid, pwd->pw_gid, pwd->pw_gecos,
323 : pwd->pw_dir, pwd->pw_shell);
324 : #endif
325 : }
326 12 : elektraFree (pwd);
327 12 : ksAppend (returned, cutKS);
328 12 : ksDel (cutKS);
329 : }
330 4 : fclose (pwfile);
331 4 : return 1;
332 : }
333 :
334 4 : int elektraPasswdSet (Plugin * handle ELEKTRA_UNUSED, KeySet * returned ELEKTRA_UNUSED, Key * parentKey ELEKTRA_UNUSED)
335 : {
336 : // get all keys
337 : // this function is optional
338 : SortBy index;
339 4 : KeySet * config = elektraPluginGetConfig (handle);
340 4 : Key * sortByKey = ksLookupByName (config, "/index", 0);
341 4 : if (sortByKey)
342 : {
343 4 : if (!strcmp (keyString (sortByKey), "uid"))
344 : index = UID;
345 4 : else if (!strcmp (keyString (sortByKey), "name"))
346 : index = NAME;
347 : else
348 0 : index = UID;
349 : }
350 : else
351 : index = UID;
352 :
353 4 : int rc = writeKS (returned, parentKey, index);
354 :
355 4 : return rc; // success
356 : }
357 :
358 26 : Plugin * ELEKTRA_PLUGIN_EXPORT
359 : {
360 : // clang-format off
361 26 : return elektraPluginExport ("passwd",
362 : ELEKTRA_PLUGIN_GET, &elektraPasswdGet,
363 : ELEKTRA_PLUGIN_SET, &elektraPasswdSet,
364 : ELEKTRA_PLUGIN_END);
365 : }
366 :
|