xref: /third_party/python/Modules/pwdmodule.c (revision 7db96d56)
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]
11module pwd
12[clinic start generated code]*/
13/*[clinic end generated code: output=da39a3ee5e6b4b0d input=60f628ef356b97b6]*/
14
15static 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
26PyDoc_STRVAR(struct_passwd__doc__,
27"pwd.struct_passwd: Results from getpw*() routines.\n\n\
28This 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\
30or via the object attributes as named in the above tuple.");
31
32static PyStructSequence_Desc struct_pwd_type_desc = {
33    "pwd.struct_passwd",
34    struct_passwd__doc__,
35    struct_pwd_type_fields,
36    7,
37};
38
39PyDoc_STRVAR(pwd__doc__,
40"This module provides access to the Unix password database.\n\
41It is available on all Unix versions.\n\
42\n\
43Password database entries are reported as 7-tuples containing the following\n\
44items from the password database (see `<pwd.h>'), in order:\n\
45pw_name, pw_passwd, pw_uid, pw_gid, pw_gecos, pw_dir, pw_shell.\n\
46The uid and gid items are integers, all others are strings. An\n\
47exception is raised if the entry asked for cannot be found.");
48
49
50typedef struct {
51    PyTypeObject *StructPwdType;
52} pwdmodulestate;
53
54static inline pwdmodulestate*
55get_pwd_state(PyObject *module)
56{
57    void *state = PyModule_GetState(module);
58    assert(state != NULL);
59    return (pwdmodulestate *)state;
60}
61
62static struct PyModuleDef pwdmodule;
63
64#define DEFAULT_BUFFER_SIZE 1024
65
66static void
67sets(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
79static PyObject *
80mkpwent(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]
116pwd.getpwuid
117
118    uidobj: object
119    /
120
121Return the password database entry for the given numeric user ID.
122
123See `help(pwd)` for more on password database entries.
124[clinic start generated code]*/
125
126static PyObject *
127pwd_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]
201pwd.getpwnam
202
203    name: unicode
204    /
205
206Return the password database entry for the given user name.
207
208See `help(pwd)` for more on password database entries.
209[clinic start generated code]*/
210
211static PyObject *
212pwd_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);
274out:
275    PyMem_RawFree(buf);
276    Py_DECREF(bytes);
277    return retval;
278}
279
280#ifdef HAVE_GETPWENT
281/*[clinic input]
282pwd.getpwall
283
284Return a list of all available password database entries, in arbitrary order.
285
286See help(pwd) for more on password database entries.
287[clinic start generated code]*/
288
289static PyObject *
290pwd_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
313static 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
322static int
323pwdmodule_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
337static PyModuleDef_Slot pwdmodule_slots[] = {
338    {Py_mod_exec, pwdmodule_exec},
339    {0, NULL}
340};
341
342static int pwdmodule_traverse(PyObject *m, visitproc visit, void *arg) {
343    Py_VISIT(get_pwd_state(m)->StructPwdType);
344    return 0;
345}
346static int pwdmodule_clear(PyObject *m) {
347    Py_CLEAR(get_pwd_state(m)->StructPwdType);
348    return 0;
349}
350static void pwdmodule_free(void *m) {
351    pwdmodule_clear((PyObject *)m);
352}
353
354static 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
367PyMODINIT_FUNC
368PyInit_pwd(void)
369{
370    return PyModuleDef_Init(&pwdmodule);
371}
372