xref: /netbsd-src/external/bsd/am-utils/dist/amd/info_nis.c (revision 8bae5d409deb915cf7c8f0539fae22ff2cb8a313)
1 /*	$NetBSD: info_nis.c,v 1.1.1.3 2015/01/17 16:34:15 christos Exp $	*/
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  */
61 static int has_yp_order = -1;
62 
63 /* forward declarations */
64 int nis_reload(mnt_map *m, char *map, void (*fn) (mnt_map *, char *, char *));
65 int nis_search(mnt_map *m, char *map, char *key, char **val, time_t *tp);
66 int nis_init(mnt_map *m, char *map, time_t *tp);
67 int nis_isup(mnt_map *m, char *map);
68 int nis_mtime(mnt_map *m, char *map, time_t *tp);
69 
70 /* typedefs */
71 typedef void (*nis_callback_fxn_t)(mnt_map *, char *, char *);
72 #ifndef DEFINED_YPALL_CALLBACK_FXN_T
73 typedef int (*ypall_callback_fxn_t)();
74 #endif /* DEFINED_YPALL_CALLBACK_FXN_T */
75 
76 struct 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
85 static 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  */
92 static int
determine_nis_domain(void)93 determine_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  */
120 static int
callback(int status,char * key,int kl,char * val,int vl,char * data)121 callback(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 
151 int
nis_reload(mnt_map * m,char * map,void (* fn)(mnt_map *,char *,char *))152 nis_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  */
190 int
nis_isup(mnt_map * m,char * map)191 nis_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  */
249 int
nis_search(mnt_map * m,char * map,char * key,char ** pval,time_t * tp)250 nis_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 
331 int
nis_init(mnt_map * m,char * map,time_t * tp)332 nis_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 
377 int
nis_mtime(mnt_map * m,char * map,time_t * tp)378 nis_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> */
398 static int
am_yp_all(char * indomain,char * inmap,struct ypall_callback * incallback)399 am_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