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