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