1
2 /* UNIX shadow password file access module */
3 /* A lot of code has been taken from pwdmodule.c */
4 /* For info also see http://www.unixpapa.com/incnote/passwd.html */
5
6 #include "Python.h"
7
8 #include <sys/types.h>
9 #ifdef HAVE_SHADOW_H
10 #include <shadow.h>
11 #endif
12
13 #include "clinic/spwdmodule.c.h"
14
15 /*[clinic input]
16 module spwd
17 [clinic start generated code]*/
18 /*[clinic end generated code: output=da39a3ee5e6b4b0d input=c0b841b90a6a07ce]*/
19
20 PyDoc_STRVAR(spwd__doc__,
21 "This module provides access to the Unix shadow password database.\n\
22 It is available on various Unix versions.\n\
23 \n\
24 Shadow password database entries are reported as 9-tuples of type struct_spwd,\n\
25 containing the following items from the password database (see `<shadow.h>'):\n\
26 sp_namp, sp_pwdp, sp_lstchg, sp_min, sp_max, sp_warn, sp_inact, sp_expire, sp_flag.\n\
27 The sp_namp and sp_pwdp are strings, the rest are integers.\n\
28 An exception is raised if the entry asked for cannot be found.\n\
29 You have to be root to be able to use this module.");
30
31
32 #if defined(HAVE_GETSPNAM) || defined(HAVE_GETSPENT)
33
34 static PyStructSequence_Field struct_spwd_type_fields[] = {
35 {"sp_namp", "login name"},
36 {"sp_pwdp", "encrypted password"},
37 {"sp_lstchg", "date of last change"},
38 {"sp_min", "min #days between changes"},
39 {"sp_max", "max #days between changes"},
40 {"sp_warn", "#days before pw expires to warn user about it"},
41 {"sp_inact", "#days after pw expires until account is disabled"},
42 {"sp_expire", "#days since 1970-01-01 when account expires"},
43 {"sp_flag", "reserved"},
44 {"sp_nam", "login name; deprecated"}, /* Backward compatibility */
45 {"sp_pwd", "encrypted password; deprecated"}, /* Backward compatibility */
46 {0}
47 };
48
49 PyDoc_STRVAR(struct_spwd__doc__,
50 "spwd.struct_spwd: Results from getsp*() routines.\n\n\
51 This object may be accessed either as a 9-tuple of\n\
52 (sp_namp,sp_pwdp,sp_lstchg,sp_min,sp_max,sp_warn,sp_inact,sp_expire,sp_flag)\n\
53 or via the object attributes as named in the above tuple.");
54
55 static PyStructSequence_Desc struct_spwd_type_desc = {
56 "spwd.struct_spwd",
57 struct_spwd__doc__,
58 struct_spwd_type_fields,
59 9,
60 };
61
62 typedef struct {
63 PyTypeObject *StructSpwdType;
64 } spwdmodulestate;
65
66 static inline spwdmodulestate*
get_spwd_state(PyObject * module)67 get_spwd_state(PyObject *module)
68 {
69 void *state = PyModule_GetState(module);
70 assert(state != NULL);
71 return (spwdmodulestate *)state;
72 }
73
74 static struct PyModuleDef spwdmodule;
75
76 static void
sets(PyObject * v,int i,const char * val)77 sets(PyObject *v, int i, const char* val)
78 {
79 if (val) {
80 PyObject *o = PyUnicode_DecodeFSDefault(val);
81 PyStructSequence_SET_ITEM(v, i, o);
82 } else {
83 PyStructSequence_SET_ITEM(v, i, Py_None);
84 Py_INCREF(Py_None);
85 }
86 }
87
mkspent(PyObject * module,struct spwd * p)88 static PyObject *mkspent(PyObject *module, struct spwd *p)
89 {
90 int setIndex = 0;
91 PyObject *v = PyStructSequence_New(get_spwd_state(module)->StructSpwdType);
92 if (v == NULL)
93 return NULL;
94
95 #define SETI(i,val) PyStructSequence_SET_ITEM(v, i, PyLong_FromLong((long) val))
96 #define SETS(i,val) sets(v, i, val)
97
98 SETS(setIndex++, p->sp_namp);
99 SETS(setIndex++, p->sp_pwdp);
100 SETI(setIndex++, p->sp_lstchg);
101 SETI(setIndex++, p->sp_min);
102 SETI(setIndex++, p->sp_max);
103 SETI(setIndex++, p->sp_warn);
104 SETI(setIndex++, p->sp_inact);
105 SETI(setIndex++, p->sp_expire);
106 SETI(setIndex++, p->sp_flag);
107 SETS(setIndex++, p->sp_namp); /* Backward compatibility for sp_nam */
108 SETS(setIndex++, p->sp_pwdp); /* Backward compatibility for sp_pwd */
109
110 #undef SETS
111 #undef SETI
112
113 if (PyErr_Occurred()) {
114 Py_DECREF(v);
115 return NULL;
116 }
117
118 return v;
119 }
120
121 #endif /* HAVE_GETSPNAM || HAVE_GETSPENT */
122
123
124 #ifdef HAVE_GETSPNAM
125
126 /*[clinic input]
127 spwd.getspnam
128
129 arg: unicode
130 /
131
132 Return the shadow password database entry for the given user name.
133
134 See `help(spwd)` for more on shadow password database entries.
135 [clinic start generated code]*/
136
137 static PyObject *
spwd_getspnam_impl(PyObject * module,PyObject * arg)138 spwd_getspnam_impl(PyObject *module, PyObject *arg)
139 /*[clinic end generated code: output=701250cf57dc6ebe input=dd89429e6167a00f]*/
140 {
141 char *name;
142 struct spwd *p;
143 PyObject *bytes, *retval = NULL;
144
145 if ((bytes = PyUnicode_EncodeFSDefault(arg)) == NULL)
146 return NULL;
147 /* check for embedded null bytes */
148 if (PyBytes_AsStringAndSize(bytes, &name, NULL) == -1)
149 goto out;
150 if ((p = getspnam(name)) == NULL) {
151 if (errno != 0)
152 PyErr_SetFromErrno(PyExc_OSError);
153 else
154 PyErr_SetString(PyExc_KeyError, "getspnam(): name not found");
155 goto out;
156 }
157 retval = mkspent(module, p);
158 out:
159 Py_DECREF(bytes);
160 return retval;
161 }
162
163 #endif /* HAVE_GETSPNAM */
164
165 #ifdef HAVE_GETSPENT
166
167 /*[clinic input]
168 spwd.getspall
169
170 Return a list of all available shadow password database entries, in arbitrary order.
171
172 See `help(spwd)` for more on shadow password database entries.
173 [clinic start generated code]*/
174
175 static PyObject *
spwd_getspall_impl(PyObject * module)176 spwd_getspall_impl(PyObject *module)
177 /*[clinic end generated code: output=4fda298d6bf6d057 input=b2c84b7857d622bd]*/
178 {
179 PyObject *d;
180 struct spwd *p;
181 if ((d = PyList_New(0)) == NULL)
182 return NULL;
183 setspent();
184 while ((p = getspent()) != NULL) {
185 PyObject *v = mkspent(module, p);
186 if (v == NULL || PyList_Append(d, v) != 0) {
187 Py_XDECREF(v);
188 Py_DECREF(d);
189 endspent();
190 return NULL;
191 }
192 Py_DECREF(v);
193 }
194 endspent();
195 return d;
196 }
197
198 #endif /* HAVE_GETSPENT */
199
200 static PyMethodDef spwd_methods[] = {
201 #ifdef HAVE_GETSPNAM
202 SPWD_GETSPNAM_METHODDEF
203 #endif
204 #ifdef HAVE_GETSPENT
205 SPWD_GETSPALL_METHODDEF
206 #endif
207 {NULL, NULL} /* sentinel */
208 };
209
210 static int
spwdmodule_exec(PyObject * module)211 spwdmodule_exec(PyObject *module)
212 {
213 spwdmodulestate *state = get_spwd_state(module);
214
215 state->StructSpwdType = PyStructSequence_NewType(&struct_spwd_type_desc);
216 if (state->StructSpwdType == NULL) {
217 return -1;
218 }
219 if (PyModule_AddType(module, state->StructSpwdType) < 0) {
220 return -1;
221 }
222 return 0;
223 }
224
225 static PyModuleDef_Slot spwdmodule_slots[] = {
226 {Py_mod_exec, spwdmodule_exec},
227 {0, NULL}
228 };
229
spwdmodule_traverse(PyObject * m,visitproc visit,void * arg)230 static int spwdmodule_traverse(PyObject *m, visitproc visit, void *arg) {
231 Py_VISIT(get_spwd_state(m)->StructSpwdType);
232 return 0;
233 }
234
spwdmodule_clear(PyObject * m)235 static int spwdmodule_clear(PyObject *m) {
236 Py_CLEAR(get_spwd_state(m)->StructSpwdType);
237 return 0;
238 }
239
spwdmodule_free(void * m)240 static void spwdmodule_free(void *m) {
241 spwdmodule_clear((PyObject *)m);
242 }
243
244 static struct PyModuleDef spwdmodule = {
245 PyModuleDef_HEAD_INIT,
246 .m_name = "spwd",
247 .m_doc = spwd__doc__,
248 .m_size = sizeof(spwdmodulestate),
249 .m_methods = spwd_methods,
250 .m_slots = spwdmodule_slots,
251 .m_traverse = spwdmodule_traverse,
252 .m_clear = spwdmodule_clear,
253 .m_free = spwdmodule_free,
254 };
255
256 PyMODINIT_FUNC
PyInit_spwd(void)257 PyInit_spwd(void)
258 {
259 return PyModuleDef_Init(&spwdmodule);
260 }
261