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 <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* |
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 |
60 | nis_clear(PyObject *m) |
61 | { |
62 | Py_CLEAR(get_nis_state(m)->nis_error); |
63 | return 0; |
64 | } |
65 | |
66 | static int |
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 |
74 | nis_free(void *m) |
75 | { |
76 | nis_clear((PyObject *) m); |
77 | } |
78 | |
79 | static PyObject * |
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 * |
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 |
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 * |
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 * |
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 * |
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 |
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 |
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 |
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 |
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 |
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 * |
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 * |
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 * |
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 |
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 |
524 | PyInit_nis(void) |
525 | { |
526 | return PyModuleDef_Init(&nismodule); |
527 | } |
528 | |