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