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