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