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