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