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 static int initialized;
51 static PyTypeObject StructPwdType;
52 
53 #define DEFAULT_BUFFER_SIZE 1024
54 
55 static void
sets(PyObject * v,int i,const char * val)56 sets(PyObject *v, int i, const char* val)
57 {
58   if (val) {
59       PyObject *o = PyUnicode_DecodeFSDefault(val);
60       PyStructSequence_SET_ITEM(v, i, o);
61   }
62   else {
63       PyStructSequence_SET_ITEM(v, i, Py_None);
64       Py_INCREF(Py_None);
65   }
66 }
67 
68 static PyObject *
mkpwent(struct passwd * p)69 mkpwent(struct passwd *p)
70 {
71     int setIndex = 0;
72     PyObject *v = PyStructSequence_New(&StructPwdType);
73     if (v == NULL)
74         return NULL;
75 
76 #define SETI(i,val) PyStructSequence_SET_ITEM(v, i, PyLong_FromLong((long) val))
77 #define SETS(i,val) sets(v, i, val)
78 
79     SETS(setIndex++, p->pw_name);
80 #if defined(HAVE_STRUCT_PASSWD_PW_PASSWD) && !defined(__ANDROID__)
81     SETS(setIndex++, p->pw_passwd);
82 #else
83     SETS(setIndex++, "");
84 #endif
85     PyStructSequence_SET_ITEM(v, setIndex++, _PyLong_FromUid(p->pw_uid));
86     PyStructSequence_SET_ITEM(v, setIndex++, _PyLong_FromGid(p->pw_gid));
87 #if defined(HAVE_STRUCT_PASSWD_PW_GECOS)
88     SETS(setIndex++, p->pw_gecos);
89 #else
90     SETS(setIndex++, "");
91 #endif
92     SETS(setIndex++, p->pw_dir);
93     SETS(setIndex++, p->pw_shell);
94 
95 #undef SETS
96 #undef SETI
97 
98     if (PyErr_Occurred()) {
99         Py_XDECREF(v);
100         return NULL;
101     }
102 
103     return v;
104 }
105 
106 /*[clinic input]
107 pwd.getpwuid
108 
109     uidobj: object
110     /
111 
112 Return the password database entry for the given numeric user ID.
113 
114 See `help(pwd)` for more on password database entries.
115 [clinic start generated code]*/
116 
117 static PyObject *
pwd_getpwuid(PyObject * module,PyObject * uidobj)118 pwd_getpwuid(PyObject *module, PyObject *uidobj)
119 /*[clinic end generated code: output=c4ee1d4d429b86c4 input=ae64d507a1c6d3e8]*/
120 {
121     PyObject *retval = NULL;
122     uid_t uid;
123     int nomem = 0;
124     struct passwd *p;
125     char *buf = NULL, *buf2 = NULL;
126 
127     if (!_Py_Uid_Converter(uidobj, &uid)) {
128         if (PyErr_ExceptionMatches(PyExc_OverflowError))
129             PyErr_Format(PyExc_KeyError,
130                          "getpwuid(): uid not found");
131         return NULL;
132     }
133 #ifdef HAVE_GETPWUID_R
134     int status;
135     Py_ssize_t bufsize;
136     /* Note: 'pwd' will be used via pointer 'p' on getpwuid_r success. */
137     struct passwd pwd;
138 
139     Py_BEGIN_ALLOW_THREADS
140     bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
141     if (bufsize == -1) {
142         bufsize = DEFAULT_BUFFER_SIZE;
143     }
144 
145     while(1) {
146         buf2 = PyMem_RawRealloc(buf, bufsize);
147         if (buf2 == NULL) {
148             p = NULL;
149             nomem = 1;
150             break;
151         }
152         buf = buf2;
153         status = getpwuid_r(uid, &pwd, buf, bufsize, &p);
154         if (status != 0) {
155             p = NULL;
156         }
157         if (p != NULL || status != ERANGE) {
158             break;
159         }
160         if (bufsize > (PY_SSIZE_T_MAX >> 1)) {
161             nomem = 1;
162             break;
163         }
164         bufsize <<= 1;
165     }
166 
167     Py_END_ALLOW_THREADS
168 #else
169     p = getpwuid(uid);
170 #endif
171     if (p == NULL) {
172         PyMem_RawFree(buf);
173         if (nomem == 1) {
174             return PyErr_NoMemory();
175         }
176         PyObject *uid_obj = _PyLong_FromUid(uid);
177         if (uid_obj == NULL)
178             return NULL;
179         PyErr_Format(PyExc_KeyError,
180                      "getpwuid(): uid not found: %S", uid_obj);
181         Py_DECREF(uid_obj);
182         return NULL;
183     }
184     retval = mkpwent(p);
185 #ifdef HAVE_GETPWUID_R
186     PyMem_RawFree(buf);
187 #endif
188     return retval;
189 }
190 
191 /*[clinic input]
192 pwd.getpwnam
193 
194     name: unicode
195     /
196 
197 Return the password database entry for the given user name.
198 
199 See `help(pwd)` for more on password database entries.
200 [clinic start generated code]*/
201 
202 static PyObject *
pwd_getpwnam_impl(PyObject * module,PyObject * name)203 pwd_getpwnam_impl(PyObject *module, PyObject *name)
204 /*[clinic end generated code: output=359ce1ddeb7a824f input=a6aeb5e3447fb9e0]*/
205 {
206     char *buf = NULL, *buf2 = NULL, *name_chars;
207     int nomem = 0;
208     struct passwd *p;
209     PyObject *bytes, *retval = NULL;
210 
211     if ((bytes = PyUnicode_EncodeFSDefault(name)) == NULL)
212         return NULL;
213     /* check for embedded null bytes */
214     if (PyBytes_AsStringAndSize(bytes, &name_chars, NULL) == -1)
215         goto out;
216 #ifdef HAVE_GETPWNAM_R
217     int status;
218     Py_ssize_t bufsize;
219     /* Note: 'pwd' will be used via pointer 'p' on getpwnam_r success. */
220     struct passwd pwd;
221 
222     Py_BEGIN_ALLOW_THREADS
223     bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
224     if (bufsize == -1) {
225         bufsize = DEFAULT_BUFFER_SIZE;
226     }
227 
228     while(1) {
229         buf2 = PyMem_RawRealloc(buf, bufsize);
230         if (buf2 == NULL) {
231             p = NULL;
232             nomem = 1;
233             break;
234         }
235         buf = buf2;
236         status = getpwnam_r(name_chars, &pwd, buf, bufsize, &p);
237         if (status != 0) {
238             p = NULL;
239         }
240         if (p != NULL || status != ERANGE) {
241             break;
242         }
243         if (bufsize > (PY_SSIZE_T_MAX >> 1)) {
244             nomem = 1;
245             break;
246         }
247         bufsize <<= 1;
248     }
249 
250     Py_END_ALLOW_THREADS
251 #else
252     p = getpwnam(name_chars);
253 #endif
254     if (p == NULL) {
255         if (nomem == 1) {
256             PyErr_NoMemory();
257         }
258         else {
259             PyErr_Format(PyExc_KeyError,
260                          "getpwnam(): name not found: %R", name);
261         }
262         goto out;
263     }
264     retval = mkpwent(p);
265 out:
266     PyMem_RawFree(buf);
267     Py_DECREF(bytes);
268     return retval;
269 }
270 
271 #ifdef HAVE_GETPWENT
272 /*[clinic input]
273 pwd.getpwall
274 
275 Return a list of all available password database entries, in arbitrary order.
276 
277 See help(pwd) for more on password database entries.
278 [clinic start generated code]*/
279 
280 static PyObject *
pwd_getpwall_impl(PyObject * module)281 pwd_getpwall_impl(PyObject *module)
282 /*[clinic end generated code: output=4853d2f5a0afac8a input=d7ecebfd90219b85]*/
283 {
284     PyObject *d;
285     struct passwd *p;
286     if ((d = PyList_New(0)) == NULL)
287         return NULL;
288     setpwent();
289     while ((p = getpwent()) != NULL) {
290         PyObject *v = mkpwent(p);
291         if (v == NULL || PyList_Append(d, v) != 0) {
292             Py_XDECREF(v);
293             Py_DECREF(d);
294             endpwent();
295             return NULL;
296         }
297         Py_DECREF(v);
298     }
299     endpwent();
300     return d;
301 }
302 #endif
303 
304 static PyMethodDef pwd_methods[] = {
305     PWD_GETPWUID_METHODDEF
306     PWD_GETPWNAM_METHODDEF
307 #ifdef HAVE_GETPWENT
308     PWD_GETPWALL_METHODDEF
309 #endif
310     {NULL,              NULL}           /* sentinel */
311 };
312 
313 static struct PyModuleDef pwdmodule = {
314     PyModuleDef_HEAD_INIT,
315     "pwd",
316     pwd__doc__,
317     -1,
318     pwd_methods,
319     NULL,
320     NULL,
321     NULL,
322     NULL
323 };
324 
325 
326 PyMODINIT_FUNC
PyInit_pwd(void)327 PyInit_pwd(void)
328 {
329     PyObject *m;
330     m = PyModule_Create(&pwdmodule);
331     if (m == NULL)
332         return NULL;
333 
334     if (!initialized) {
335         if (PyStructSequence_InitType2(&StructPwdType,
336                                        &struct_pwd_type_desc) < 0)
337             return NULL;
338         initialized = 1;
339     }
340     Py_INCREF((PyObject *) &StructPwdType);
341     PyModule_AddObject(m, "struct_passwd", (PyObject *) &StructPwdType);
342     return m;
343 }
344