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)140 nis_get_default_domain (PyObject *self)
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)nis_match,
428 METH_VARARGS | METH_KEYWORDS,
429 match__doc__},
430 {"cat", (PyCFunction)nis_cat,
431 METH_VARARGS | METH_KEYWORDS,
432 cat__doc__},
433 {"maps", (PyCFunction)nis_maps,
434 METH_VARARGS | METH_KEYWORDS,
435 maps__doc__},
436 {"get_default_domain", (PyCFunction)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