1/*	$NetBSD$	*/
2
3/*
4 * Copyright (c) 1997-2014 Erez Zadok
5 * Copyright (c) 1989 Jan-Simon Pendry
6 * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
7 * Copyright (c) 1989 The Regents of the University of California.
8 * All rights reserved.
9 *
10 * This code is derived from software contributed to Berkeley by
11 * Jan-Simon Pendry at Imperial College, London.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 *    notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 *    notice, this list of conditions and the following disclaimer in the
20 *    documentation and/or other materials provided with the distribution.
21 * 3. Neither the name of the University nor the names of its contributors
22 *    may be used to endorse or promote products derived from this software
23 *    without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
36 *
37 *
38 * File: am-utils/amd/info_nis.c
39 *
40 */
41
42/*
43 * Get info from NIS map
44 */
45
46#ifdef HAVE_CONFIG_H
47# include <config.h>
48#endif /* HAVE_CONFIG_H */
49#include <am_defs.h>
50#include <amd.h>
51#include <sun_map.h>
52
53
54/*
55 * NIS+ servers in NIS compat mode don't have yp_order()
56 *
57 *	has_yp_order = 1	NIS server
58 *		     = 0	NIS+ server
59 *		     = -1	server is down
60 */
61static int has_yp_order = -1;
62
63/* forward declarations */
64int nis_reload(mnt_map *m, char *map, void (*fn) (mnt_map *, char *, char *));
65int nis_search(mnt_map *m, char *map, char *key, char **val, time_t *tp);
66int nis_init(mnt_map *m, char *map, time_t *tp);
67int nis_isup(mnt_map *m, char *map);
68int nis_mtime(mnt_map *m, char *map, time_t *tp);
69
70/* typedefs */
71typedef void (*nis_callback_fxn_t)(mnt_map *, char *, char *);
72#ifndef DEFINED_YPALL_CALLBACK_FXN_T
73typedef int (*ypall_callback_fxn_t)();
74#endif /* DEFINED_YPALL_CALLBACK_FXN_T */
75
76struct nis_callback_data {
77  mnt_map *ncd_m;
78  char *ncd_map;
79  nis_callback_fxn_t ncd_fn;
80};
81
82/* Map to the right version of yp_all */
83#ifdef HAVE_BAD_YP_ALL
84# define yp_all am_yp_all
85static int am_yp_all(char *indomain, char *inmap, struct ypall_callback *incallback);
86#endif /* HAVE_BAD_YP_ALL */
87
88
89/*
90 * Figure out the nis domain name
91 */
92static int
93determine_nis_domain(void)
94{
95  static int nis_not_running = 0;
96  char default_domain[YPMAXDOMAIN];
97
98  if (nis_not_running)
99    return ENOENT;
100
101  if (getdomainname(default_domain, sizeof(default_domain)) < 0) {
102    nis_not_running = 1;
103    plog(XLOG_ERROR, "getdomainname: %m");
104    return EIO;
105  }
106  if (!*default_domain) {
107    nis_not_running = 1;
108    plog(XLOG_WARNING, "NIS domain name is not set.  NIS ignored.");
109    return ENOENT;
110  }
111  gopt.nis_domain = xstrdup(default_domain);
112
113  return 0;
114}
115
116
117/*
118 * Callback from yp_all
119 */
120static int
121callback(int status, char *key, int kl, char *val, int vl, char *data)
122{
123  struct nis_callback_data *ncdp = (struct nis_callback_data *) data;
124
125  if (status == YP_TRUE) {
126
127    /* add to list of maps */
128    char *kp = strnsave(key, kl);
129    char *vp = strnsave(val, vl);
130
131    (*ncdp->ncd_fn) (ncdp->ncd_m, kp, vp);
132
133    /* we want more ... */
134    return FALSE;
135
136  } else {
137
138    /* NOMORE means end of map - otherwise log error */
139    if (status != YP_NOMORE) {
140      /* check what went wrong */
141      int e = ypprot_err(status);
142
143      plog(XLOG_ERROR, "yp enumeration of %s: %s, status=%d, e=%d",
144	   ncdp->ncd_map, yperr_string(e), status, e);
145    }
146    return TRUE;
147  }
148}
149
150
151int
152nis_reload(mnt_map *m, char *map, void (*fn) (mnt_map *, char *, char *))
153{
154  int error;
155  struct nis_callback_data data;
156  struct ypall_callback cbinfo;
157
158  if (!gopt.nis_domain) {
159    error = determine_nis_domain();
160    if (error)
161      return error;
162  }
163  data.ncd_m = m;
164  data.ncd_map = map;
165  data.ncd_fn = fn;
166  cbinfo.data = (voidp) &data;
167  cbinfo.foreach = (ypall_callback_fxn_t) callback;
168
169  plog(XLOG_INFO, "NIS map %s reloading using yp_all", map);
170  /*
171   * If you are using NIS and your yp_all function is "broken", you have to
172   * get it fixed.  The bug in yp_all() is that it does not close a TCP
173   * connection to ypserv, and this ypserv runs out of open file descriptors,
174   * getting into an infinite loop, thus all YP clients eventually unbind
175   * and hang too.
176   */
177  error = yp_all(gopt.nis_domain, map, &cbinfo);
178
179  if (error)
180    plog(XLOG_ERROR, "error grabbing nis map of %s: %s", map, yperr_string(ypprot_err(error)));
181  return error;
182}
183
184
185/*
186 * Check if NIS is up, so we can determine if to clear the map or not.
187 * Test it by checking the yp order.
188 * Returns: 0 if NIS is down, 1 if it is up.
189 */
190int
191nis_isup(mnt_map *m, char *map)
192{
193  YP_ORDER_OUTORDER_TYPE order;
194  int error;
195  char *master;
196  static int last_status = 1;	/* assume up by default */
197
198  switch (has_yp_order) {
199  case 1:
200    /*
201     * NIS server with yp_order
202     */
203    error = yp_order(gopt.nis_domain, map, &order);
204    if (error != 0) {
205      plog(XLOG_ERROR,
206	   "nis_isup: error getting the order of map %s: %s",
207	   map, yperr_string(ypprot_err(error)));
208      last_status = 0;
209      return 0;			/* NIS is down */
210    }
211    break;
212
213  case 0:
214    /*
215     * NIS+ server without yp_order
216     */
217    error = yp_master(gopt.nis_domain, map, &master);
218    if (error != 0) {
219      plog(XLOG_ERROR,
220	   "nis_isup: error getting the master of map %s: %s",
221	   map, yperr_string(ypprot_err(error)));
222      last_status = 0;
223      return 0;			/* NIS+ is down */
224    }
225    break;
226
227  default:
228    /*
229     * server was down
230     */
231    last_status = 0;
232  }
233
234  if (last_status == 0) {	/* reinitialize if was down before */
235    time_t dummy;
236    error = nis_init(m, map, &dummy);
237    if (error)
238      return 0;			/* still down */
239    plog(XLOG_INFO, "nis_isup: NIS came back up for map %s", map);
240    last_status = 1;
241  }
242  return 1;			/* NIS is up */
243}
244
245
246/*
247 * Try to locate a key using NIS.
248 */
249int
250nis_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp)
251{
252  int outlen;
253  int res;
254  YP_ORDER_OUTORDER_TYPE order;
255
256  /*
257   * Make sure domain initialized
258   */
259  if (!gopt.nis_domain) {
260    int error = determine_nis_domain();
261    if (error)
262      return error;
263  }
264
265
266  switch (has_yp_order) {
267  case 1:
268    /*
269     * NIS server with yp_order
270     * Check if map has changed
271     */
272    if (yp_order(gopt.nis_domain, map, &order))
273      return EIO;
274    if ((time_t) order > *tp) {
275      *tp = (time_t) order;
276      return -1;
277    }
278    break;
279
280  case 0:
281    /*
282     * NIS+ server without yp_order
283     * Check if timeout has expired to invalidate the cache
284     */
285    order = time(NULL);
286    if ((time_t)order - *tp > gopt.am_timeo) {
287      *tp = (time_t)order;
288      return(-1);
289    }
290    break;
291
292  default:
293    /*
294     * server was down
295     */
296     if (nis_isup(m, map))
297       return -1;
298     return EIO;
299  }
300
301  /*
302   * Lookup key
303   */
304  res = yp_match(gopt.nis_domain, map, key, strlen(key), pval, &outlen);
305  if (m->cfm && (m->cfm->cfm_flags & CFM_SUN_MAP_SYNTAX) && res == 0) {
306    char *oldval = *pval;
307    *pval = sun_entry2amd(key, oldval);
308    /* We always need to free the output of the yp_match call. */
309    XFREE(oldval);
310    if (*pval == NULL)
311      return -1;		/* sun2amd parser error */
312  }
313
314  /*
315   * Do something interesting with the return code
316   */
317  switch (res) {
318  case 0:
319    return 0;
320
321  case YPERR_KEY:
322    return ENOENT;
323
324  default:
325    plog(XLOG_ERROR, "nis_search: %s: %s", map, yperr_string(res));
326    return EIO;
327  }
328}
329
330
331int
332nis_init(mnt_map *m, char *map, time_t *tp)
333{
334  YP_ORDER_OUTORDER_TYPE order;
335  int yp_order_result;
336  char *master;
337
338  if (!gopt.nis_domain) {
339    int error = determine_nis_domain();
340    if (error)
341      return error;
342  }
343
344  /*
345   * To see if the map exists, try to find
346   * a master for it.
347   */
348  yp_order_result = yp_order(gopt.nis_domain, map, &order);
349  switch (yp_order_result) {
350  case 0:
351    /* NIS server found */
352    has_yp_order = 1;
353    *tp = (time_t) order;
354    dlog("NIS master for %s@%s has order %lu", map, gopt.nis_domain, (unsigned long) order);
355    break;
356  case YPERR_YPERR:
357    /* NIS+ server found ! */
358    has_yp_order = 0;
359    /* try yp_master() instead */
360    if (yp_master(gopt.nis_domain, map, &master)) {
361      return ENOENT;
362    } else {
363      dlog("NIS master for %s@%s is a NIS+ server", map, gopt.nis_domain);
364      /* Use fake timestamps */
365      *tp = time(NULL);
366    }
367    break;
368  default:
369    /* server is down */
370    has_yp_order = -1;
371    return ENOENT;
372  }
373  return 0;
374}
375
376
377int
378nis_mtime(mnt_map *m, char *map, time_t *tp)
379{
380  return nis_init(m, map, tp);
381}
382
383
384#ifdef HAVE_BAD_YP_ALL
385/*
386 * If you are using NIS and your yp_all function is "broken", use an
387 * alternate code which avoids a bug in yp_all().  The bug in yp_all() is
388 * that it does not close a TCP connection to ypserv, and this ypserv runs
389 * out of open filedescriptors, getting into an infinite loop, thus all YP
390 * clients eventually unbind and hang too.
391 *
392 * Systems known to be plagued with this bug:
393 *	earlier SunOS 4.x
394 *	all irix systems (at this time, up to 6.4 was checked)
395 *
396 * -Erez Zadok <ezk@cs.columbia.edu>
397 * -James Tanis <jtt@cs.columbia.edu> */
398static int
399am_yp_all(char *indomain, char *inmap, struct ypall_callback *incallback)
400{
401  int i, j;
402  char *outkey, *outval;
403  int outkeylen, outvallen;
404  char *outkey_old;
405  int outkeylen_old;
406
407  plog(XLOG_INFO, "NIS map %s reloading using am_yp_all", inmap);
408
409  i = yp_first(indomain, inmap, &outkey, &outkeylen, &outval, &outvallen);
410  if (i) {
411    plog(XLOG_ERROR, "yp_first() returned error: %s\n", yperr_string(i));
412  }
413  do {
414    j = (incallback->foreach)(YP_TRUE,
415			      outkey,
416			      outkeylen,
417			      outval,
418			      outvallen,
419			      incallback->data);
420    if (j != FALSE)		/* terminate loop */
421      break;
422
423    /*
424     * We have to manually free all char ** arguments to yp_first/yp_next
425     * outval must be freed *before* calling yp_next again, outkey can be
426     * freed as outkey_old *after* the call (this saves one call to
427     * strnsave).
428     */
429    XFREE(outval);
430    outkey_old = outkey;
431    outkeylen_old = outkeylen;
432    i = yp_next(indomain,
433		inmap,
434		outkey_old,
435		outkeylen_old,
436		&outkey,
437		&outkeylen,
438		&outval,
439		&outvallen);
440    XFREE(outkey_old);
441  } while (!i);
442  if (i) {
443    dlog("yp_next() returned error: %s\n", yperr_string(i));
444  }
445  if (i == YPERR_NOMORE)
446    return 0;
447  return i;
448}
449#endif /* HAVE_BAD_YP_ALL */
450