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