• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* UNIX group file access module */
2 
3 // Argument Clinic uses the internal C API
4 #ifndef Py_BUILD_CORE_BUILTIN
5 #  define Py_BUILD_CORE_MODULE 1
6 #endif
7 
8 #include "Python.h"
9 #include "posixmodule.h"
10 
11 #include <errno.h>                // ERANGE
12 #include <grp.h>                  // getgrgid_r()
13 #include <string.h>               // memcpy()
14 #include <unistd.h>               // sysconf()
15 
16 #include "clinic/grpmodule.c.h"
17 /*[clinic input]
18 module grp
19 [clinic start generated code]*/
20 /*[clinic end generated code: output=da39a3ee5e6b4b0d input=cade63f2ed1bd9f8]*/
21 
22 static PyStructSequence_Field struct_group_type_fields[] = {
23    {"gr_name", "group name"},
24    {"gr_passwd", "password"},
25    {"gr_gid", "group id"},
26    {"gr_mem", "group members"},
27    {0}
28 };
29 
30 PyDoc_STRVAR(struct_group__doc__,
31 "grp.struct_group: Results from getgr*() routines.\n\n\
32 This object may be accessed either as a tuple of\n\
33   (gr_name,gr_passwd,gr_gid,gr_mem)\n\
34 or via the object attributes as named in the above tuple.\n");
35 
36 static PyStructSequence_Desc struct_group_type_desc = {
37    "grp.struct_group",
38    struct_group__doc__,
39    struct_group_type_fields,
40    4,
41 };
42 
43 
44 typedef struct {
45   PyTypeObject *StructGrpType;
46 } grpmodulestate;
47 
48 static inline grpmodulestate*
get_grp_state(PyObject * module)49 get_grp_state(PyObject *module)
50 {
51     void *state = PyModule_GetState(module);
52     assert(state != NULL);
53     return (grpmodulestate *)state;
54 }
55 
56 static struct PyModuleDef grpmodule;
57 
58 #define DEFAULT_BUFFER_SIZE 1024
59 
60 static PyObject *
mkgrent(PyObject * module,struct group * p)61 mkgrent(PyObject *module, struct group *p)
62 {
63     int setIndex = 0;
64     PyObject *v, *w;
65     char **member;
66 
67     v = PyStructSequence_New(get_grp_state(module)->StructGrpType);
68     if (v == NULL)
69         return NULL;
70 
71     if ((w = PyList_New(0)) == NULL) {
72         Py_DECREF(v);
73         return NULL;
74     }
75     for (member = p->gr_mem; ; member++) {
76         char *group_member;
77         // member can be misaligned
78         memcpy(&group_member, member, sizeof(group_member));
79         if (group_member == NULL) {
80             break;
81         }
82         PyObject *x = PyUnicode_DecodeFSDefault(group_member);
83         if (x == NULL || PyList_Append(w, x) != 0) {
84             Py_XDECREF(x);
85             Py_DECREF(w);
86             Py_DECREF(v);
87             return NULL;
88         }
89         Py_DECREF(x);
90     }
91 
92 #define SET(i,val) PyStructSequence_SetItem(v, i, val)
93     SET(setIndex++, PyUnicode_DecodeFSDefault(p->gr_name));
94     if (p->gr_passwd)
95             SET(setIndex++, PyUnicode_DecodeFSDefault(p->gr_passwd));
96     else {
97             SET(setIndex++, Py_None);
98             Py_INCREF(Py_None);
99     }
100     SET(setIndex++, _PyLong_FromGid(p->gr_gid));
101     SET(setIndex++, w);
102 #undef SET
103 
104     if (PyErr_Occurred()) {
105         Py_DECREF(v);
106         return NULL;
107     }
108 
109     return v;
110 }
111 
112 static PyObject *
grp_getgrgid(PyObject * module,PyObject * args,PyObject * kwargs)113 grp_getgrgid(PyObject *module, PyObject *args, PyObject *kwargs)
114 {
115     static char *kwlist[] = {"id", NULL};
116     PyObject *id;
117     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwlist, &id)) {
118         return NULL;
119     }
120 
121     PyObject *retval = NULL;
122     int nomem = 0;
123     char *buf = NULL, *buf2 = NULL;
124     gid_t gid;
125     struct group *p;
126 
127     if (!_Py_Gid_Converter(id, &gid)) {
128         return NULL;
129     }
130 #ifdef HAVE_GETGRGID_R
131     int status;
132     Py_ssize_t bufsize;
133     /* Note: 'grp' will be used via pointer 'p' on getgrgid_r success. */
134     struct group grp;
135 
136     Py_BEGIN_ALLOW_THREADS
137     bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
138     if (bufsize == -1) {
139         bufsize = DEFAULT_BUFFER_SIZE;
140     }
141 
142     while (1) {
143         buf2 = PyMem_RawRealloc(buf, bufsize);
144         if (buf2 == NULL) {
145             p = NULL;
146             nomem = 1;
147             break;
148         }
149         buf = buf2;
150         status = getgrgid_r(gid, &grp, buf, bufsize, &p);
151         if (status != 0) {
152             p = NULL;
153         }
154         if (p != NULL || status != ERANGE) {
155             break;
156         }
157         if (bufsize > (PY_SSIZE_T_MAX >> 1)) {
158             nomem = 1;
159             break;
160         }
161         bufsize <<= 1;
162     }
163 
164     Py_END_ALLOW_THREADS
165 #else
166     p = getgrgid(gid);
167 #endif
168     if (p == NULL) {
169         PyMem_RawFree(buf);
170         if (nomem == 1) {
171             return PyErr_NoMemory();
172         }
173         PyObject *gid_obj = _PyLong_FromGid(gid);
174         if (gid_obj == NULL)
175             return NULL;
176         PyErr_Format(PyExc_KeyError, "getgrgid(): gid not found: %S", gid_obj);
177         Py_DECREF(gid_obj);
178         return NULL;
179     }
180     retval = mkgrent(module, p);
181 #ifdef HAVE_GETGRGID_R
182     PyMem_RawFree(buf);
183 #endif
184     return retval;
185 }
186 
187 PyDoc_STRVAR(grp_getgrgid__doc__,
188 "getgrgid($module, /, id)\n"
189 "--\n"
190 "\n"
191 "Return the group database entry for the given numeric group ID.\n"
192 "\n"
193 "If id is not valid, raise KeyError.");
194 
195 
196 /*[clinic input]
197 grp.getgrnam
198 
199     name: unicode
200 
201 Return the group database entry for the given group name.
202 
203 If name is not valid, raise KeyError.
204 [clinic start generated code]*/
205 
206 static PyObject *
grp_getgrnam_impl(PyObject * module,PyObject * name)207 grp_getgrnam_impl(PyObject *module, PyObject *name)
208 /*[clinic end generated code: output=67905086f403c21c input=08ded29affa3c863]*/
209 {
210     char *buf = NULL, *buf2 = NULL, *name_chars;
211     int nomem = 0;
212     struct group *p;
213     PyObject *bytes, *retval = NULL;
214 
215     if ((bytes = PyUnicode_EncodeFSDefault(name)) == NULL)
216         return NULL;
217     /* check for embedded null bytes */
218     if (PyBytes_AsStringAndSize(bytes, &name_chars, NULL) == -1)
219         goto out;
220 #ifdef HAVE_GETGRNAM_R
221     int status;
222     Py_ssize_t bufsize;
223     /* Note: 'grp' will be used via pointer 'p' on getgrnam_r success. */
224     struct group grp;
225 
226     Py_BEGIN_ALLOW_THREADS
227     bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
228     if (bufsize == -1) {
229         bufsize = DEFAULT_BUFFER_SIZE;
230     }
231 
232     while(1) {
233         buf2 = PyMem_RawRealloc(buf, bufsize);
234         if (buf2 == NULL) {
235             p = NULL;
236             nomem = 1;
237             break;
238         }
239         buf = buf2;
240         status = getgrnam_r(name_chars, &grp, buf, bufsize, &p);
241         if (status != 0) {
242             p = NULL;
243         }
244         if (p != NULL || status != ERANGE) {
245             break;
246         }
247         if (bufsize > (PY_SSIZE_T_MAX >> 1)) {
248             nomem = 1;
249             break;
250         }
251         bufsize <<= 1;
252     }
253 
254     Py_END_ALLOW_THREADS
255 #else
256     p = getgrnam(name_chars);
257 #endif
258     if (p == NULL) {
259         if (nomem == 1) {
260             PyErr_NoMemory();
261         }
262         else {
263             PyErr_Format(PyExc_KeyError, "getgrnam(): name not found: %R", name);
264         }
265         goto out;
266     }
267     retval = mkgrent(module, p);
268 out:
269     PyMem_RawFree(buf);
270     Py_DECREF(bytes);
271     return retval;
272 }
273 
274 /*[clinic input]
275 grp.getgrall
276 
277 Return a list of all available group entries, in arbitrary order.
278 
279 An entry whose name starts with '+' or '-' represents an instruction
280 to use YP/NIS and may not be accessible via getgrnam or getgrgid.
281 [clinic start generated code]*/
282 
283 static PyObject *
grp_getgrall_impl(PyObject * module)284 grp_getgrall_impl(PyObject *module)
285 /*[clinic end generated code: output=585dad35e2e763d7 input=d7df76c825c367df]*/
286 {
287     PyObject *d = PyList_New(0);
288     if (d == NULL) {
289         return NULL;
290     }
291 
292     static PyMutex getgrall_mutex = {0};
293     PyMutex_Lock(&getgrall_mutex);
294     setgrent();
295 
296     struct group *p;
297     while ((p = getgrent()) != NULL) {
298         // gh-126316: Don't release the mutex around mkgrent() since
299         // setgrent()/endgrent() are not reentrant / thread-safe. A deadlock
300         // is unlikely since mkgrent() should not be able to call arbitrary
301         // Python code.
302         PyObject *v = mkgrent(module, p);
303         if (v == NULL || PyList_Append(d, v) != 0) {
304             Py_XDECREF(v);
305             Py_CLEAR(d);
306             goto done;
307         }
308         Py_DECREF(v);
309     }
310 
311 done:
312     endgrent();
313     PyMutex_Unlock(&getgrall_mutex);
314     return d;
315 }
316 
317 static PyMethodDef grp_methods[] = {
318     {"getgrgid", _PyCFunction_CAST(grp_getgrgid), METH_VARARGS|METH_KEYWORDS, grp_getgrgid__doc__},
319     GRP_GETGRNAM_METHODDEF
320     GRP_GETGRALL_METHODDEF
321     {NULL, NULL}
322 };
323 
324 PyDoc_STRVAR(grp__doc__,
325 "Access to the Unix group database.\n\
326 \n\
327 Group entries are reported as 4-tuples containing the following fields\n\
328 from the group database, in order:\n\
329 \n\
330   gr_name   - name of the group\n\
331   gr_passwd - group password (encrypted); often empty\n\
332   gr_gid    - numeric ID of the group\n\
333   gr_mem    - list of members\n\
334 \n\
335 The gid is an integer, name and password are strings.  (Note that most\n\
336 users are not explicitly listed as members of the groups they are in\n\
337 according to the password database.  Check both databases to get\n\
338 complete membership information.)");
339 
340 static int
grpmodule_exec(PyObject * module)341 grpmodule_exec(PyObject *module)
342 {
343     grpmodulestate *state = get_grp_state(module);
344 
345     state->StructGrpType = PyStructSequence_NewType(&struct_group_type_desc);
346     if (state->StructGrpType == NULL) {
347         return -1;
348     }
349     if (PyModule_AddType(module, state->StructGrpType) < 0) {
350         return -1;
351     }
352     return 0;
353 }
354 
355 static PyModuleDef_Slot grpmodule_slots[] = {
356     {Py_mod_exec, grpmodule_exec},
357     {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
358     {Py_mod_gil, Py_MOD_GIL_NOT_USED},
359     {0, NULL}
360 };
361 
grpmodule_traverse(PyObject * m,visitproc visit,void * arg)362 static int grpmodule_traverse(PyObject *m, visitproc visit, void *arg) {
363     Py_VISIT(get_grp_state(m)->StructGrpType);
364     return 0;
365 }
366 
grpmodule_clear(PyObject * m)367 static int grpmodule_clear(PyObject *m) {
368     Py_CLEAR(get_grp_state(m)->StructGrpType);
369     return 0;
370 }
371 
grpmodule_free(void * m)372 static void grpmodule_free(void *m) {
373     grpmodule_clear((PyObject *)m);
374 }
375 
376 static struct PyModuleDef grpmodule = {
377     PyModuleDef_HEAD_INIT,
378     .m_name = "grp",
379     .m_doc = grp__doc__,
380     .m_size = sizeof(grpmodulestate),
381     .m_methods = grp_methods,
382     .m_slots = grpmodule_slots,
383     .m_traverse = grpmodule_traverse,
384     .m_clear = grpmodule_clear,
385     .m_free = grpmodule_free,
386 };
387 
388 PyMODINIT_FUNC
PyInit_grp(void)389 PyInit_grp(void)
390 {
391    return PyModuleDef_Init(&grpmodule);
392 }
393