1 /***********************************************************
2 Written by:
3 Fred Gansevles <[email protected]>
4 B&O group,
5 Faculteit der Informatica,
6 Universiteit Twente,
7 Enschede,
8 the Netherlands.
9 ******************************************************************/
10
11 /* NIS module implementation */
12
13 #include "Python.h"
14
15 #include <stdlib.h> // free()
16 #include <sys/time.h>
17 #include <sys/types.h>
18 #include <rpc/rpc.h>
19 #include <rpcsvc/yp_prot.h>
20 #include <rpcsvc/ypclnt.h>
21
22 #ifdef __sgi
23 /* This is missing from rpcsvc/ypclnt.h */
24 extern int yp_get_default_domain(char **);
25 #endif
26
27 PyDoc_STRVAR(get_default_domain__doc__,
28 "get_default_domain() -> str\n\
29 Corresponds to the C library yp_get_default_domain() call, returning\n\
30 the default NIS domain.\n");
31
32 PyDoc_STRVAR(match__doc__,
33 "match(key, map, domain = defaultdomain)\n\
34 Corresponds to the C library yp_match() call, returning the value of\n\
35 key in the given map. Optionally domain can be specified but it\n\
36 defaults to the system default domain.\n");
37
38 PyDoc_STRVAR(cat__doc__,
39 "cat(map, domain = defaultdomain)\n\
40 Returns the entire map as a dictionary. Optionally domain can be\n\
41 specified but it defaults to the system default domain.\n");
42
43 PyDoc_STRVAR(maps__doc__,
44 "maps(domain = defaultdomain)\n\
45 Returns an array of all available NIS maps within a domain. If domain\n\
46 is not specified it defaults to the system default domain.\n");
47
48 typedef struct {
49 PyObject *nis_error;
50 } nis_state;
51
52 static inline nis_state*
get_nis_state(PyObject * module)53 get_nis_state(PyObject *module)
54 {
55 void *state = PyModule_GetState(module);
56 assert(state != NULL);
57 return (nis_state *)state;
58 }
59
60 static int
nis_clear(PyObject * m)61 nis_clear(PyObject *m)
62 {
63 Py_CLEAR(get_nis_state(m)->nis_error);
64 return 0;
65 }
66
67 static int
nis_traverse(PyObject * m,visitproc visit,void * arg)68 nis_traverse(PyObject *m, visitproc visit, void *arg)
69 {
70 Py_VISIT(get_nis_state(m)->nis_error);
71 return 0;
72 }
73
74 static void
nis_free(void * m)75 nis_free(void *m)
76 {
77 nis_clear((PyObject *) m);
78 }
79
80 static PyObject *
nis_error(nis_state * state,int err)81 nis_error(nis_state *state, int err)
82 {
83 PyErr_SetString(state->nis_error, yperr_string(err));
84 return NULL;
85 }
86
87 static struct nis_map {
88 char *alias;
89 char *map;
90 int fix;
91 } aliases [] = {
92 {"passwd", "passwd.byname", 0},
93 {"group", "group.byname", 0},
94 {"networks", "networks.byaddr", 0},
95 {"hosts", "hosts.byname", 0},
96 {"protocols", "protocols.bynumber", 0},
97 {"services", "services.byname", 0},
98 {"aliases", "mail.aliases", 1}, /* created with 'makedbm -a' */
99 {"ethers", "ethers.byname", 0},
100 {0L, 0L, 0}
101 };
102
103 static char *
nis_mapname(char * map,int * pfix)104 nis_mapname(char *map, int *pfix)
105 {
106 int i;
107
108 *pfix = 0;
109 for (i=0; aliases[i].alias != 0L; i++) {
110 if (!strcmp (aliases[i].alias, map) || !strcmp (aliases[i].map, map)) {
111 *pfix = aliases[i].fix;
112 return aliases[i].map;
113 }
114 }
115
116 return map;
117 }
118
119 #if defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__)
120 typedef int (*foreachfunc)(unsigned long, char *, int, char *, int, void *);
121 #else
122 typedef int (*foreachfunc)(int, char *, int, char *, int, char *);
123 #endif
124
125 struct ypcallback_data {
126 PyObject *dict;
127 int fix;
128 PyThreadState *state;
129 };
130
131 static int
nis_foreach(int instatus,char * inkey,int inkeylen,char * inval,int invallen,struct ypcallback_data * indata)132 nis_foreach(int instatus, char *inkey, int inkeylen, char *inval,
133 int invallen, struct ypcallback_data *indata)
134 {
135 if (instatus == YP_TRUE) {
136 PyObject *key;
137 PyObject *val;
138 int err;
139
140 PyEval_RestoreThread(indata->state);
141 if (indata->fix) {
142 if (inkeylen > 0 && inkey[inkeylen-1] == '\0')
143 inkeylen--;
144 if (invallen > 0 && inval[invallen-1] == '\0')
145 invallen--;
146 }
147 key = PyUnicode_DecodeFSDefaultAndSize(inkey, inkeylen);
148 val = PyUnicode_DecodeFSDefaultAndSize(inval, invallen);
149 if (key == NULL || val == NULL) {
150 /* XXX error -- don't know how to handle */
151 PyErr_Clear();
152 Py_XDECREF(key);
153 Py_XDECREF(val);
154 indata->state = PyEval_SaveThread();
155 return 1;
156 }
157 err = PyDict_SetItem(indata->dict, key, val);
158 Py_DECREF(key);
159 Py_DECREF(val);
160 if (err != 0)
161 PyErr_Clear();
162 indata->state = PyEval_SaveThread();
163 if (err != 0)
164 return 1;
165 return 0;
166 }
167 return 1;
168 }
169
170 static PyObject *
nis_get_default_domain(PyObject * module,PyObject * Py_UNUSED (ignored))171 nis_get_default_domain(PyObject *module, PyObject *Py_UNUSED(ignored))
172 {
173 char *domain;
174 int err;
175 PyObject *res;
176 nis_state *state = get_nis_state(module);
177 if ((err = yp_get_default_domain(&domain)) != 0) {
178 return nis_error(state, err);
179 }
180
181 res = PyUnicode_FromStringAndSize (domain, strlen(domain));
182 return res;
183 }
184
185 static PyObject *
nis_match(PyObject * module,PyObject * args,PyObject * kwdict)186 nis_match(PyObject *module, PyObject *args, PyObject *kwdict)
187 {
188 char *match;
189 char *domain = NULL;
190 Py_ssize_t keylen;
191 int len;
192 char *key, *map;
193 int err;
194 PyObject *ukey, *bkey, *res;
195 int fix;
196 static char *kwlist[] = {"key", "map", "domain", NULL};
197
198 if (!PyArg_ParseTupleAndKeywords(args, kwdict,
199 "Us|s:match", kwlist,
200 &ukey, &map, &domain)) {
201 return NULL;
202 }
203 if ((bkey = PyUnicode_EncodeFSDefault(ukey)) == NULL) {
204 return NULL;
205 }
206 /* check for embedded null bytes */
207 if (PyBytes_AsStringAndSize(bkey, &key, &keylen) == -1) {
208 Py_DECREF(bkey);
209 return NULL;
210 }
211
212 nis_state *state = get_nis_state(module);
213 if (!domain && ((err = yp_get_default_domain(&domain)) != 0)) {
214 Py_DECREF(bkey);
215 return nis_error(state, err);
216 }
217 map = nis_mapname (map, &fix);
218 if (fix)
219 keylen++;
220 Py_BEGIN_ALLOW_THREADS
221 err = yp_match (domain, map, key, keylen, &match, &len);
222 Py_END_ALLOW_THREADS
223 Py_DECREF(bkey);
224 if (fix)
225 len--;
226 if (err != 0) {
227 return nis_error(state, err);
228 }
229 res = PyUnicode_DecodeFSDefaultAndSize(match, len);
230 free (match);
231 return res;
232 }
233
234 static PyObject *
nis_cat(PyObject * module,PyObject * args,PyObject * kwdict)235 nis_cat(PyObject *module, PyObject *args, PyObject *kwdict)
236 {
237 char *domain = NULL;
238 char *map;
239 struct ypall_callback cb;
240 struct ypcallback_data data;
241 PyObject *dict;
242 int err;
243 static char *kwlist[] = {"map", "domain", NULL};
244
245 if (!PyArg_ParseTupleAndKeywords(args, kwdict, "s|s:cat",
246 kwlist, &map, &domain)) {
247 return NULL;
248 }
249 nis_state *state = get_nis_state(module);
250 if (!domain && ((err = yp_get_default_domain(&domain)) != 0)) {
251 return nis_error(state, err);
252 }
253 dict = PyDict_New ();
254 if (dict == NULL)
255 return NULL;
256 cb.foreach = (foreachfunc)nis_foreach;
257 data.dict = dict;
258 map = nis_mapname (map, &data.fix);
259 cb.data = (char *)&data;
260 data.state = PyEval_SaveThread();
261 err = yp_all (domain, map, &cb);
262 PyEval_RestoreThread(data.state);
263 if (err != 0) {
264 Py_DECREF(dict);
265 return nis_error(state, err);
266 }
267 return dict;
268 }
269
270 /* These should be u_long on Sun h/w but not on 64-bit h/w.
271 This is not portable to machines with 16-bit ints and no prototypes */
272 #ifndef YPPROC_MAPLIST
273 #define YPPROC_MAPLIST 11
274 #endif
275 #ifndef YPPROG
276 #define YPPROG 100004
277 #endif
278 #ifndef YPVERS
279 #define YPVERS 2
280 #endif
281
282 typedef char *domainname;
283 typedef char *mapname;
284
285 enum nisstat {
286 NIS_TRUE = 1,
287 NIS_NOMORE = 2,
288 NIS_FALSE = 0,
289 NIS_NOMAP = -1,
290 NIS_NODOM = -2,
291 NIS_NOKEY = -3,
292 NIS_BADOP = -4,
293 NIS_BADDB = -5,
294 NIS_YPERR = -6,
295 NIS_BADARGS = -7,
296 NIS_VERS = -8
297 };
298 typedef enum nisstat nisstat;
299
300 struct nismaplist {
301 mapname map;
302 struct nismaplist *next;
303 };
304 typedef struct nismaplist nismaplist;
305
306 struct nisresp_maplist {
307 nisstat stat;
308 nismaplist *maps;
309 };
310 typedef struct nisresp_maplist nisresp_maplist;
311
312 static struct timeval TIMEOUT = { 25, 0 };
313
314 static
315 bool_t
nis_xdr_domainname(XDR * xdrs,domainname * objp)316 nis_xdr_domainname(XDR *xdrs, domainname *objp)
317 {
318 if (!xdr_string(xdrs, objp, YPMAXDOMAIN)) {
319 return (FALSE);
320 }
321 return (TRUE);
322 }
323
324 static
325 bool_t
nis_xdr_mapname(XDR * xdrs,mapname * objp)326 nis_xdr_mapname(XDR *xdrs, mapname *objp)
327 {
328 if (!xdr_string(xdrs, objp, YPMAXMAP)) {
329 return (FALSE);
330 }
331 return (TRUE);
332 }
333
334 static
335 bool_t
nis_xdr_ypmaplist(XDR * xdrs,nismaplist * objp)336 nis_xdr_ypmaplist(XDR *xdrs, nismaplist *objp)
337 {
338 if (!nis_xdr_mapname(xdrs, &objp->map)) {
339 return (FALSE);
340 }
341 if (!xdr_pointer(xdrs, (char **)&objp->next,
342 sizeof(nismaplist), (xdrproc_t)nis_xdr_ypmaplist))
343 {
344 return (FALSE);
345 }
346 return (TRUE);
347 }
348
349 static
350 bool_t
nis_xdr_ypstat(XDR * xdrs,nisstat * objp)351 nis_xdr_ypstat(XDR *xdrs, nisstat *objp)
352 {
353 if (!xdr_enum(xdrs, (enum_t *)objp)) {
354 return (FALSE);
355 }
356 return (TRUE);
357 }
358
359
360 static
361 bool_t
nis_xdr_ypresp_maplist(XDR * xdrs,nisresp_maplist * objp)362 nis_xdr_ypresp_maplist(XDR *xdrs, nisresp_maplist *objp)
363 {
364 if (!nis_xdr_ypstat(xdrs, &objp->stat)) {
365 return (FALSE);
366 }
367 if (!xdr_pointer(xdrs, (char **)&objp->maps,
368 sizeof(nismaplist), (xdrproc_t)nis_xdr_ypmaplist))
369 {
370 return (FALSE);
371 }
372 return (TRUE);
373 }
374
375
376 static
377 nisresp_maplist *
nisproc_maplist_2(domainname * argp,CLIENT * clnt)378 nisproc_maplist_2(domainname *argp, CLIENT *clnt)
379 {
380 static nisresp_maplist res;
381
382 memset(&res, 0, sizeof(res));
383 if (clnt_call(clnt, YPPROC_MAPLIST,
384 (xdrproc_t)nis_xdr_domainname, (caddr_t)argp,
385 (xdrproc_t)nis_xdr_ypresp_maplist, (caddr_t)&res,
386 TIMEOUT) != RPC_SUCCESS)
387 {
388 return (NULL);
389 }
390 return (&res);
391 }
392
393 static
394 nismaplist *
nis_maplist(nis_state * state,char * dom)395 nis_maplist(nis_state *state, char *dom)
396 {
397 nisresp_maplist *list;
398 CLIENT *cl;
399 char *server = NULL;
400 int mapi = 0;
401
402 while (!server && aliases[mapi].map != 0L) {
403 yp_master (dom, aliases[mapi].map, &server);
404 mapi++;
405 }
406 if (!server) {
407 PyErr_SetString(state->nis_error, "No NIS master found for any map");
408 return NULL;
409 }
410 cl = clnt_create(server, YPPROG, YPVERS, "tcp");
411 if (cl == NULL) {
412 PyErr_SetString(state->nis_error, clnt_spcreateerror(server));
413 goto finally;
414 }
415 list = nisproc_maplist_2 (&dom, cl);
416 clnt_destroy(cl);
417 if (list == NULL)
418 goto finally;
419 if (list->stat != NIS_TRUE)
420 goto finally;
421
422 free(server);
423 return list->maps;
424
425 finally:
426 free(server);
427 return NULL;
428 }
429
430 static PyObject *
nis_maps(PyObject * module,PyObject * args,PyObject * kwdict)431 nis_maps (PyObject *module, PyObject *args, PyObject *kwdict)
432 {
433 char *domain = NULL;
434 nismaplist *maps;
435 PyObject *list;
436 int err;
437 static char *kwlist[] = {"domain", NULL};
438
439 if (!PyArg_ParseTupleAndKeywords(args, kwdict,
440 "|s:maps", kwlist, &domain)) {
441 return NULL;
442 }
443
444 nis_state *state = get_nis_state(module);
445 if (!domain && ((err = yp_get_default_domain (&domain)) != 0)) {
446 nis_error(state, err);
447 return NULL;
448 }
449
450 if ((maps = nis_maplist(state, domain)) == NULL) {
451 return NULL;
452 }
453 if ((list = PyList_New(0)) == NULL) {
454 return NULL;
455 }
456 for (; maps; maps = maps->next) {
457 PyObject *str = PyUnicode_FromString(maps->map);
458 if (!str || PyList_Append(list, str) < 0)
459 {
460 Py_XDECREF(str);
461 Py_DECREF(list);
462 list = NULL;
463 break;
464 }
465 Py_DECREF(str);
466 }
467 /* XXX Shouldn't we free the list of maps now? */
468 return list;
469 }
470
471 static PyMethodDef nis_methods[] = {
472 {"match", _PyCFunction_CAST(nis_match),
473 METH_VARARGS | METH_KEYWORDS,
474 match__doc__},
475 {"cat", _PyCFunction_CAST(nis_cat),
476 METH_VARARGS | METH_KEYWORDS,
477 cat__doc__},
478 {"maps", _PyCFunction_CAST(nis_maps),
479 METH_VARARGS | METH_KEYWORDS,
480 maps__doc__},
481 {"get_default_domain", nis_get_default_domain,
482 METH_NOARGS,
483 get_default_domain__doc__},
484 {NULL, NULL} /* Sentinel */
485 };
486
487 static int
nis_exec(PyObject * module)488 nis_exec(PyObject *module)
489 {
490 nis_state* state = get_nis_state(module);
491 state->nis_error = PyErr_NewException("nis.error", NULL, NULL);
492 if (state->nis_error == NULL) {
493 return -1;
494 }
495
496 Py_INCREF(state->nis_error);
497 if (PyModule_AddObject(module, "error", state->nis_error) < 0) {
498 Py_DECREF(state->nis_error);
499 return -1;
500 }
501 return 0;
502 }
503
504 static PyModuleDef_Slot nis_slots[] = {
505 {Py_mod_exec, nis_exec},
506 {0, NULL}
507 };
508
509 PyDoc_STRVAR(nis__doc__,
510 "This module contains functions for accessing NIS maps.\n");
511
512 static struct PyModuleDef nismodule = {
513 PyModuleDef_HEAD_INIT,
514 .m_name = "nis",
515 .m_doc = nis__doc__,
516 .m_size = sizeof(nis_state),
517 .m_methods = nis_methods,
518 .m_traverse = nis_traverse,
519 .m_clear = nis_clear,
520 .m_free = nis_free,
521 .m_slots = nis_slots,
522 };
523
524 PyMODINIT_FUNC
PyInit_nis(void)525 PyInit_nis(void)
526 {
527 if (PyErr_WarnEx(PyExc_DeprecationWarning,
528 "'nis' is deprecated and slated for removal in "
529 "Python 3.13",
530 7)) {
531 return NULL;
532 }
533 return PyModuleDef_Init(&nismodule);
534 }
535