(root)/
Python-3.12.0/
Modules/
nismodule.c
       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 <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*
      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
      61  nis_clear(PyObject *m)
      62  {
      63      Py_CLEAR(get_nis_state(m)->nis_error);
      64      return 0;
      65  }
      66  
      67  static int
      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
      75  nis_free(void *m)
      76  {
      77      nis_clear((PyObject *) m);
      78  }
      79  
      80  static PyObject *
      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 *
     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
     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 *
     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 *
     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 *
     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
     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
     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
     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
     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
     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 *
     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 *
     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 *
     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_SETREF(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_CAST(nis_match),
     472                                      METH_VARARGS | METH_KEYWORDS,
     473                                      match__doc__},
     474      {"cat",                     _PyCFunction_CAST(nis_cat),
     475                                      METH_VARARGS | METH_KEYWORDS,
     476                                      cat__doc__},
     477      {"maps",                    _PyCFunction_CAST(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      // XXX gh-103092: fix isolation.
     506      {Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED},
     507      //{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
     508      {0, NULL}
     509  };
     510  
     511  PyDoc_STRVAR(nis__doc__,
     512  "This module contains functions for accessing NIS maps.\n");
     513  
     514  static struct PyModuleDef nismodule = {
     515      PyModuleDef_HEAD_INIT,
     516      .m_name = "nis",
     517      .m_doc = nis__doc__,
     518      .m_size = sizeof(nis_state),
     519      .m_methods = nis_methods,
     520      .m_traverse = nis_traverse,
     521      .m_clear = nis_clear,
     522      .m_free = nis_free,
     523      .m_slots = nis_slots,
     524  };
     525  
     526  PyMODINIT_FUNC
     527  PyInit_nis(void)
     528  {
     529      if (PyErr_WarnEx(PyExc_DeprecationWarning,
     530                       "'nis' is deprecated and slated for removal in "
     531                       "Python 3.13",
     532                       7)) {
     533          return NULL;
     534      }
     535      return PyModuleDef_Init(&nismodule);
     536  }