/src/cpython/Modules/pwdmodule.c
Line | Count | Source (jump to first uncovered line) |
1 | | |
2 | | /* UNIX password file access module */ |
3 | | |
4 | | #include "Python.h" |
5 | | #include "posixmodule.h" |
6 | | |
7 | | #include <errno.h> // ERANGE |
8 | | #include <pwd.h> // getpwuid() |
9 | | #include <unistd.h> // sysconf() |
10 | | |
11 | | #include "clinic/pwdmodule.c.h" |
12 | | /*[clinic input] |
13 | | module pwd |
14 | | [clinic start generated code]*/ |
15 | | /*[clinic end generated code: output=da39a3ee5e6b4b0d input=60f628ef356b97b6]*/ |
16 | | |
17 | | static PyStructSequence_Field struct_pwd_type_fields[] = { |
18 | | {"pw_name", "user name"}, |
19 | | {"pw_passwd", "password"}, |
20 | | {"pw_uid", "user id"}, |
21 | | {"pw_gid", "group id"}, |
22 | | {"pw_gecos", "real name"}, |
23 | | {"pw_dir", "home directory"}, |
24 | | {"pw_shell", "shell program"}, |
25 | | {0} |
26 | | }; |
27 | | |
28 | | PyDoc_STRVAR(struct_passwd__doc__, |
29 | | "pwd.struct_passwd: Results from getpw*() routines.\n\n\ |
30 | | This object may be accessed either as a tuple of\n\ |
31 | | (pw_name,pw_passwd,pw_uid,pw_gid,pw_gecos,pw_dir,pw_shell)\n\ |
32 | | or via the object attributes as named in the above tuple."); |
33 | | |
34 | | static PyStructSequence_Desc struct_pwd_type_desc = { |
35 | | "pwd.struct_passwd", |
36 | | struct_passwd__doc__, |
37 | | struct_pwd_type_fields, |
38 | | 7, |
39 | | }; |
40 | | |
41 | | PyDoc_STRVAR(pwd__doc__, |
42 | | "This module provides access to the Unix password database.\n\ |
43 | | It is available on all Unix versions.\n\ |
44 | | \n\ |
45 | | Password database entries are reported as 7-tuples containing the following\n\ |
46 | | items from the password database (see `<pwd.h>'), in order:\n\ |
47 | | pw_name, pw_passwd, pw_uid, pw_gid, pw_gecos, pw_dir, pw_shell.\n\ |
48 | | The uid and gid items are integers, all others are strings. An\n\ |
49 | | exception is raised if the entry asked for cannot be found."); |
50 | | |
51 | | |
52 | | typedef struct { |
53 | | PyTypeObject *StructPwdType; |
54 | | } pwdmodulestate; |
55 | | |
56 | | static inline pwdmodulestate* |
57 | | get_pwd_state(PyObject *module) |
58 | 0 | { |
59 | 0 | void *state = PyModule_GetState(module); |
60 | 0 | assert(state != NULL); |
61 | 0 | return (pwdmodulestate *)state; |
62 | 0 | } |
63 | | |
64 | | static struct PyModuleDef pwdmodule; |
65 | | |
66 | | /* Mutex to protect calls to getpwuid(), getpwnam(), and getpwent(). |
67 | | * These functions return pointer to static data structure, which |
68 | | * may be overwritten by any subsequent calls. */ |
69 | | static PyMutex pwd_db_mutex = {0}; |
70 | | |
71 | 0 | #define DEFAULT_BUFFER_SIZE 1024 |
72 | | |
73 | | static PyObject * |
74 | | mkpwent(PyObject *module, struct passwd *p) |
75 | 0 | { |
76 | 0 | PyObject *v = PyStructSequence_New(get_pwd_state(module)->StructPwdType); |
77 | 0 | if (v == NULL) { |
78 | 0 | return NULL; |
79 | 0 | } |
80 | | |
81 | 0 | int setIndex = 0; |
82 | |
|
83 | 0 | #define SET_STRING(VAL) \ |
84 | 0 | SET_RESULT((VAL) ? PyUnicode_DecodeFSDefault((VAL)) : Py_NewRef(Py_None)) |
85 | |
|
86 | 0 | #define SET_RESULT(CALL) \ |
87 | 0 | do { \ |
88 | 0 | PyObject *item = (CALL); \ |
89 | 0 | if (item == NULL) { \ |
90 | 0 | goto error; \ |
91 | 0 | } \ |
92 | 0 | PyStructSequence_SetItem(v, setIndex++, item); \ |
93 | 0 | } while(0) |
94 | |
|
95 | 0 | SET_STRING(p->pw_name); |
96 | 0 | #if defined(HAVE_STRUCT_PASSWD_PW_PASSWD) && !defined(__ANDROID__) |
97 | 0 | SET_STRING(p->pw_passwd); |
98 | | #else |
99 | | SET_STRING(""); |
100 | | #endif |
101 | 0 | SET_RESULT(_PyLong_FromUid(p->pw_uid)); |
102 | 0 | SET_RESULT(_PyLong_FromGid(p->pw_gid)); |
103 | 0 | #if defined(HAVE_STRUCT_PASSWD_PW_GECOS) |
104 | 0 | SET_STRING(p->pw_gecos); |
105 | | #else |
106 | | SET_STRING(""); |
107 | | #endif |
108 | 0 | SET_STRING(p->pw_dir); |
109 | 0 | SET_STRING(p->pw_shell); |
110 | | |
111 | 0 | #undef SET_STRING |
112 | 0 | #undef SET_RESULT |
113 | | |
114 | 0 | return v; |
115 | | |
116 | 0 | error: |
117 | 0 | Py_DECREF(v); |
118 | 0 | return NULL; |
119 | 0 | } |
120 | | |
121 | | /*[clinic input] |
122 | | pwd.getpwuid |
123 | | |
124 | | uidobj: object |
125 | | / |
126 | | |
127 | | Return the password database entry for the given numeric user ID. |
128 | | |
129 | | See `help(pwd)` for more on password database entries. |
130 | | [clinic start generated code]*/ |
131 | | |
132 | | static PyObject * |
133 | | pwd_getpwuid(PyObject *module, PyObject *uidobj) |
134 | | /*[clinic end generated code: output=c4ee1d4d429b86c4 input=ae64d507a1c6d3e8]*/ |
135 | 0 | { |
136 | 0 | PyObject *retval = NULL; |
137 | 0 | uid_t uid; |
138 | 0 | int nomem = 0; |
139 | 0 | struct passwd *p; |
140 | 0 | char *buf = NULL, *buf2 = NULL; |
141 | |
|
142 | 0 | if (!_Py_Uid_Converter(uidobj, &uid)) { |
143 | 0 | if (PyErr_ExceptionMatches(PyExc_OverflowError)) |
144 | 0 | PyErr_Format(PyExc_KeyError, |
145 | 0 | "getpwuid(): uid not found"); |
146 | 0 | return NULL; |
147 | 0 | } |
148 | 0 | #ifdef HAVE_GETPWUID_R |
149 | 0 | int status; |
150 | 0 | Py_ssize_t bufsize; |
151 | | /* Note: 'pwd' will be used via pointer 'p' on getpwuid_r success. */ |
152 | 0 | struct passwd pwd; |
153 | |
|
154 | 0 | Py_BEGIN_ALLOW_THREADS |
155 | 0 | bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); |
156 | 0 | if (bufsize == -1) { |
157 | 0 | bufsize = DEFAULT_BUFFER_SIZE; |
158 | 0 | } |
159 | |
|
160 | 0 | while(1) { |
161 | 0 | buf2 = PyMem_RawRealloc(buf, bufsize); |
162 | 0 | if (buf2 == NULL) { |
163 | 0 | p = NULL; |
164 | 0 | nomem = 1; |
165 | 0 | break; |
166 | 0 | } |
167 | 0 | buf = buf2; |
168 | 0 | status = getpwuid_r(uid, &pwd, buf, bufsize, &p); |
169 | 0 | if (status != 0) { |
170 | 0 | p = NULL; |
171 | 0 | } |
172 | 0 | if (p != NULL || status != ERANGE) { |
173 | 0 | break; |
174 | 0 | } |
175 | 0 | if (bufsize > (PY_SSIZE_T_MAX >> 1)) { |
176 | 0 | nomem = 1; |
177 | 0 | break; |
178 | 0 | } |
179 | 0 | bufsize <<= 1; |
180 | 0 | } |
181 | |
|
182 | 0 | Py_END_ALLOW_THREADS |
183 | | #else |
184 | | PyMutex_Lock(&pwd_db_mutex); |
185 | | // The getpwuid() function is not required to be thread-safe. |
186 | | // https://pubs.opengroup.org/onlinepubs/009604499/functions/getpwuid.html |
187 | | p = getpwuid(uid); |
188 | | #endif |
189 | 0 | if (p == NULL) { |
190 | | #ifndef HAVE_GETPWUID_R |
191 | | PyMutex_Unlock(&pwd_db_mutex); |
192 | | #endif |
193 | 0 | PyMem_RawFree(buf); |
194 | 0 | if (nomem == 1) { |
195 | 0 | return PyErr_NoMemory(); |
196 | 0 | } |
197 | 0 | PyObject *uid_obj = _PyLong_FromUid(uid); |
198 | 0 | if (uid_obj == NULL) |
199 | 0 | return NULL; |
200 | 0 | PyErr_Format(PyExc_KeyError, |
201 | 0 | "getpwuid(): uid not found: %S", uid_obj); |
202 | 0 | Py_DECREF(uid_obj); |
203 | 0 | return NULL; |
204 | 0 | } |
205 | 0 | retval = mkpwent(module, p); |
206 | 0 | #ifdef HAVE_GETPWUID_R |
207 | 0 | PyMem_RawFree(buf); |
208 | | #else |
209 | | PyMutex_Unlock(&pwd_db_mutex); |
210 | | #endif |
211 | 0 | return retval; |
212 | 0 | } |
213 | | |
214 | | /*[clinic input] |
215 | | pwd.getpwnam |
216 | | |
217 | | name: unicode |
218 | | / |
219 | | |
220 | | Return the password database entry for the given user name. |
221 | | |
222 | | See `help(pwd)` for more on password database entries. |
223 | | [clinic start generated code]*/ |
224 | | |
225 | | static PyObject * |
226 | | pwd_getpwnam_impl(PyObject *module, PyObject *name) |
227 | | /*[clinic end generated code: output=359ce1ddeb7a824f input=a6aeb5e3447fb9e0]*/ |
228 | 0 | { |
229 | 0 | char *buf = NULL, *buf2 = NULL, *name_chars; |
230 | 0 | int nomem = 0; |
231 | 0 | struct passwd *p; |
232 | 0 | PyObject *bytes, *retval = NULL; |
233 | |
|
234 | 0 | if ((bytes = PyUnicode_EncodeFSDefault(name)) == NULL) |
235 | 0 | return NULL; |
236 | | /* check for embedded null bytes */ |
237 | 0 | if (PyBytes_AsStringAndSize(bytes, &name_chars, NULL) == -1) |
238 | 0 | goto out; |
239 | 0 | #ifdef HAVE_GETPWNAM_R |
240 | 0 | int status; |
241 | 0 | Py_ssize_t bufsize; |
242 | | /* Note: 'pwd' will be used via pointer 'p' on getpwnam_r success. */ |
243 | 0 | struct passwd pwd; |
244 | |
|
245 | 0 | Py_BEGIN_ALLOW_THREADS |
246 | 0 | bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); |
247 | 0 | if (bufsize == -1) { |
248 | 0 | bufsize = DEFAULT_BUFFER_SIZE; |
249 | 0 | } |
250 | |
|
251 | 0 | while(1) { |
252 | 0 | buf2 = PyMem_RawRealloc(buf, bufsize); |
253 | 0 | if (buf2 == NULL) { |
254 | 0 | p = NULL; |
255 | 0 | nomem = 1; |
256 | 0 | break; |
257 | 0 | } |
258 | 0 | buf = buf2; |
259 | 0 | status = getpwnam_r(name_chars, &pwd, buf, bufsize, &p); |
260 | 0 | if (status != 0) { |
261 | 0 | p = NULL; |
262 | 0 | } |
263 | 0 | if (p != NULL || status != ERANGE) { |
264 | 0 | break; |
265 | 0 | } |
266 | 0 | if (bufsize > (PY_SSIZE_T_MAX >> 1)) { |
267 | 0 | nomem = 1; |
268 | 0 | break; |
269 | 0 | } |
270 | 0 | bufsize <<= 1; |
271 | 0 | } |
272 | |
|
273 | 0 | Py_END_ALLOW_THREADS |
274 | | #else |
275 | | PyMutex_Lock(&pwd_db_mutex); |
276 | | // The getpwnam() function is not required to be thread-safe. |
277 | | // https://pubs.opengroup.org/onlinepubs/009604599/functions/getpwnam.html |
278 | | p = getpwnam(name_chars); |
279 | | #endif |
280 | 0 | if (p == NULL) { |
281 | | #ifndef HAVE_GETPWNAM_R |
282 | | PyMutex_Unlock(&pwd_db_mutex); |
283 | | #endif |
284 | 0 | if (nomem == 1) { |
285 | 0 | PyErr_NoMemory(); |
286 | 0 | } |
287 | 0 | else { |
288 | 0 | PyErr_Format(PyExc_KeyError, |
289 | 0 | "getpwnam(): name not found: %R", name); |
290 | 0 | } |
291 | 0 | goto out; |
292 | 0 | } |
293 | 0 | retval = mkpwent(module, p); |
294 | | #ifndef HAVE_GETPWNAM_R |
295 | | PyMutex_Unlock(&pwd_db_mutex); |
296 | | #endif |
297 | 0 | out: |
298 | 0 | PyMem_RawFree(buf); |
299 | 0 | Py_DECREF(bytes); |
300 | 0 | return retval; |
301 | 0 | } |
302 | | |
303 | | #ifdef HAVE_GETPWENT |
304 | | /*[clinic input] |
305 | | pwd.getpwall |
306 | | |
307 | | Return a list of all available password database entries, in arbitrary order. |
308 | | |
309 | | See help(pwd) for more on password database entries. |
310 | | [clinic start generated code]*/ |
311 | | |
312 | | static PyObject * |
313 | | pwd_getpwall_impl(PyObject *module) |
314 | | /*[clinic end generated code: output=4853d2f5a0afac8a input=d7ecebfd90219b85]*/ |
315 | 0 | { |
316 | 0 | PyObject *d; |
317 | 0 | struct passwd *p; |
318 | 0 | if ((d = PyList_New(0)) == NULL) |
319 | 0 | return NULL; |
320 | | |
321 | 0 | PyMutex_Lock(&pwd_db_mutex); |
322 | 0 | int failure = 0; |
323 | 0 | PyObject *v = NULL; |
324 | | // The setpwent(), getpwent() and endpwent() functions are not required to |
325 | | // be thread-safe. |
326 | | // https://pubs.opengroup.org/onlinepubs/009696799/functions/setpwent.html |
327 | 0 | setpwent(); |
328 | 0 | while ((p = getpwent()) != NULL) { |
329 | 0 | v = mkpwent(module, p); |
330 | 0 | if (v == NULL || PyList_Append(d, v) != 0) { |
331 | | /* NOTE: cannot dec-ref here, while holding the mutex. */ |
332 | 0 | failure = 1; |
333 | 0 | goto done; |
334 | 0 | } |
335 | 0 | Py_DECREF(v); |
336 | 0 | } |
337 | | |
338 | 0 | done: |
339 | 0 | endpwent(); |
340 | 0 | PyMutex_Unlock(&pwd_db_mutex); |
341 | 0 | if (failure) { |
342 | 0 | Py_XDECREF(v); |
343 | 0 | Py_CLEAR(d); |
344 | 0 | } |
345 | 0 | return d; |
346 | 0 | } |
347 | | #endif |
348 | | |
349 | | static PyMethodDef pwd_methods[] = { |
350 | | PWD_GETPWUID_METHODDEF |
351 | | PWD_GETPWNAM_METHODDEF |
352 | | #ifdef HAVE_GETPWENT |
353 | | PWD_GETPWALL_METHODDEF |
354 | | #endif |
355 | | {NULL, NULL} /* sentinel */ |
356 | | }; |
357 | | |
358 | | static int |
359 | | pwdmodule_exec(PyObject *module) |
360 | 0 | { |
361 | 0 | pwdmodulestate *state = get_pwd_state(module); |
362 | |
|
363 | 0 | state->StructPwdType = PyStructSequence_NewType(&struct_pwd_type_desc); |
364 | 0 | if (state->StructPwdType == NULL) { |
365 | 0 | return -1; |
366 | 0 | } |
367 | 0 | if (PyModule_AddType(module, state->StructPwdType) < 0) { |
368 | 0 | return -1; |
369 | 0 | } |
370 | 0 | return 0; |
371 | 0 | } |
372 | | |
373 | | static PyModuleDef_Slot pwdmodule_slots[] = { |
374 | | {Py_mod_exec, pwdmodule_exec}, |
375 | | {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, |
376 | | {Py_mod_gil, Py_MOD_GIL_NOT_USED}, |
377 | | {0, NULL} |
378 | | }; |
379 | | |
380 | 0 | static int pwdmodule_traverse(PyObject *m, visitproc visit, void *arg) { |
381 | 0 | Py_VISIT(get_pwd_state(m)->StructPwdType); |
382 | 0 | return 0; |
383 | 0 | } |
384 | 0 | static int pwdmodule_clear(PyObject *m) { |
385 | 0 | Py_CLEAR(get_pwd_state(m)->StructPwdType); |
386 | 0 | return 0; |
387 | 0 | } |
388 | 0 | static void pwdmodule_free(void *m) { |
389 | 0 | pwdmodule_clear((PyObject *)m); |
390 | 0 | } |
391 | | |
392 | | static struct PyModuleDef pwdmodule = { |
393 | | PyModuleDef_HEAD_INIT, |
394 | | .m_name = "pwd", |
395 | | .m_doc = pwd__doc__, |
396 | | .m_size = sizeof(pwdmodulestate), |
397 | | .m_methods = pwd_methods, |
398 | | .m_slots = pwdmodule_slots, |
399 | | .m_traverse = pwdmodule_traverse, |
400 | | .m_clear = pwdmodule_clear, |
401 | | .m_free = pwdmodule_free, |
402 | | }; |
403 | | |
404 | | |
405 | | PyMODINIT_FUNC |
406 | | PyInit_pwd(void) |
407 | 0 | { |
408 | 0 | return PyModuleDef_Init(&pwdmodule); |
409 | 0 | } |