1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2003 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * Routines to handle getuser* calls in nscd 31 */ 32 33 #include <assert.h> 34 #include <errno.h> 35 #include <memory.h> 36 #include <signal.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <string.h> 40 #include <sys/door.h> 41 #include <sys/stat.h> 42 #include <sys/time.h> 43 #include <sys/types.h> 44 #include <sys/wait.h> 45 #include <thread.h> 46 #include <unistd.h> 47 #include <ucred.h> 48 #include <nss_common.h> 49 50 #include <user_attr.h> 51 52 #include <getxby_door.h> 53 #include "server_door.h" 54 #include "nscd.h" 55 56 extern userstr_t *_getusernam(const char *, userstr_t *, char *, int, int *); 57 58 static hash_t *nam_hash; 59 static mutex_t db_lock = DEFAULTMUTEX; 60 static waiter_t db_wait; 61 62 static void getuser_namekeepalive(int keep, int interval); 63 static void update_user_bucket(nsc_bucket_t **old, nsc_bucket_t *new, 64 int callnumber); 65 static nsc_bucket_t *fixbuffer(nsc_return_t *in, int maxlen); 66 static void do_findgnams(nsc_bucket_t *ptr, int *table, char *gnam); 67 static void do_invalidate(nsc_bucket_t **ptr, int callnumber); 68 static void getuser_invalidate_unlocked(void); 69 70 void 71 getuser_init(void) 72 { 73 nam_hash = make_hash(current_admin.user.nsc_suggestedsize); 74 } 75 76 static void 77 do_invalidate(nsc_bucket_t ** ptr, int callnumber) 78 { 79 if (*ptr != NULL && *ptr != (nsc_bucket_t *)-1) { 80 /* leave pending calls alone */ 81 update_user_bucket(ptr, NULL, callnumber); 82 } 83 } 84 85 static void 86 do_findgnams(nsc_bucket_t *ptr, int *table, char *gnam) 87 { 88 89 /* 90 * be careful with ptr - it may be -1 or NULL. 91 */ 92 93 if (ptr != NULL && ptr != (nsc_bucket_t *)-1) { 94 char *tmp = (char *)insertn(table, ptr->nsc_hits, 95 (int)strdup(gnam)); 96 if (tmp != (char *)-1) 97 free(tmp); 98 } 99 } 100 101 void 102 getuser_revalidate(void) 103 { 104 for (;;) { 105 int slp; 106 int interval; 107 int count; 108 109 slp = current_admin.user.nsc_pos_ttl; 110 111 if (slp < 60) { 112 slp = 60; 113 } 114 115 if ((count = current_admin.user.nsc_keephot) != 0) { 116 interval = (slp / 2)/count; 117 if (interval == 0) interval = 1; 118 sleep(slp * 2 / 3); 119 getuser_namekeepalive(count, interval); 120 } else { 121 sleep(slp); 122 } 123 } 124 } 125 126 static void 127 getuser_namekeepalive(int keep, int interval) 128 { 129 int *table; 130 union { 131 nsc_data_t ping; 132 char space[sizeof (nsc_data_t) + NSCDMAXNAMELEN]; 133 } u; 134 135 int i; 136 137 if (!keep) 138 return; 139 140 table = maken(keep); 141 mutex_lock(&db_lock); 142 operate_hash(nam_hash, do_findgnams, (char *)table); 143 mutex_unlock(&db_lock); 144 145 for (i = 1; i <= keep; i++) { 146 char *tmp; 147 u.ping.nsc_call.nsc_callnumber = GETUSERNAM; 148 149 if ((tmp = (char *)table[keep + 1 + i]) == (char *)-1) 150 continue; /* unused slot in table */ 151 152 strcpy(u.ping.nsc_call.nsc_u.name, tmp); 153 154 launch_update(&u.ping.nsc_call); 155 sleep(interval); 156 } 157 158 for (i = 1; i <= keep; i++) { 159 char *tmp; 160 if ((tmp = (char *)table[keep + 1 + i]) != (char *)-1) 161 free(tmp); 162 } 163 164 free(table); 165 } 166 167 168 /* 169 * This routine marks all entries as invalid 170 * 171 */ 172 173 void 174 getuser_invalidate() 175 { 176 mutex_lock(&db_lock); 177 getuser_invalidate_unlocked(); 178 mutex_unlock(&db_lock); 179 } 180 181 static void 182 getuser_invalidate_unlocked() 183 { 184 operate_hash_addr(nam_hash, do_invalidate, (char *)GETUSERNAM); 185 current_admin.user.nsc_invalidate_count++; 186 } 187 188 void 189 getuser_lookup(nsc_return_t *out, int maxsize, nsc_call_t *in, time_t now) 190 { 191 int out_of_date; 192 nsc_bucket_t *retb; 193 char **bucket; 194 195 static time_t lastmod; 196 197 int bufferspace = maxsize - sizeof (nsc_return_t); 198 199 if (current_admin.user.nsc_enabled == 0) { 200 out->nsc_return_code = NOSERVER; 201 out->nsc_bufferbytesused = sizeof (*out); 202 return; 203 } 204 205 mutex_lock(&db_lock); 206 207 if (current_admin.user.nsc_check_files) { 208 struct stat buf; 209 210 if (stat(USERATTR_FILENAME, &buf) < 0) { 211 /*EMPTY*/; 212 } else if (lastmod == 0) { 213 lastmod = buf.st_mtime; 214 } else if (lastmod < buf.st_mtime) { 215 getuser_invalidate_unlocked(); 216 lastmod = buf.st_mtime; 217 } 218 } 219 220 if (current_admin.debug_level >= DBG_ALL) { 221 logit("getuser_lookup: looking for name %s\n", 222 in->nsc_u.name); 223 } 224 225 for (;;) { 226 if (attr_strlen(in->nsc_u.name) > NSCDMAXNAMELEN) { 227 ucred_t *uc = NULL; 228 229 if (door_ucred(&uc) != 0) { 230 logit("getuser_lookup: Name too long, " 231 "but no user credential: %s\n", 232 strerror(errno)); 233 } else { 234 logit("getuser_lookup: Name too long " 235 "from pid %d uid %d\n", 236 ucred_getpid(uc), 237 ucred_getruid(uc)); 238 ucred_free(uc); 239 } 240 241 out->nsc_errno = NSS_NOTFOUND; 242 out->nsc_return_code = NOTFOUND; 243 out->nsc_bufferbytesused = sizeof (*out); 244 goto getout; 245 } 246 bucket = get_hash(nam_hash, in->nsc_u.name); 247 248 if (*bucket == (char *)-1) { /* pending lookup */ 249 if (get_clearance(in->nsc_callnumber) != 0) { 250 /* no threads available */ 251 out->nsc_return_code = NOSERVER; 252 /* cannot process now */ 253 out->nsc_bufferbytesused = 254 sizeof (*out); 255 current_admin.user.nsc_throttle_count++; 256 goto getout; 257 } 258 nscd_wait(&db_wait, &db_lock, bucket); 259 release_clearance(in->nsc_callnumber); 260 continue; /* go back and relookup hash bucket */ 261 } 262 break; 263 } 264 265 /* 266 * check for no name_service mode 267 */ 268 269 if (*bucket == NULL && current_admin.avoid_nameservice) { 270 out->nsc_return_code = NOTFOUND; 271 out->nsc_bufferbytesused = sizeof (*out); 272 } else if ((*bucket == NULL) || /* New entry in name service */ 273 (in->nsc_callnumber & UPDATEBIT) || /* needs updating */ 274 (out_of_date = (!current_admin.avoid_nameservice && 275 (current_admin.user.nsc_old_data_ok == 0) && 276 (((nsc_bucket_t *)*bucket)->nsc_timestamp < now)))) { 277 /* time has expired */ 278 int saved_errno; 279 int saved_hits = 0; 280 userstr_t *p; 281 282 if (get_clearance(in->nsc_callnumber) != 0) { 283 /* no threads available */ 284 out->nsc_return_code = NOSERVER; 285 /* cannot process now */ 286 out->nsc_bufferbytesused = sizeof (*out); 287 current_admin.user.nsc_throttle_count++; 288 goto getout; 289 } 290 291 if (*bucket != NULL) { 292 saved_hits = 293 ((nsc_bucket_t *)*bucket)->nsc_hits; 294 } 295 296 /* 297 * block any threads accessing this bucket if data is 298 * non-existent out of date 299 */ 300 301 if (*bucket == NULL || out_of_date) { 302 update_user_bucket((nsc_bucket_t **)bucket, 303 (nsc_bucket_t *)-1, in->nsc_callnumber); 304 } else { 305 /* 306 * if still not -1 bucket we are doing update... 307 * mark to prevent 308 * pileups of threads if the name service is hanging.... 309 */ 310 ((nsc_bucket_t *)(*bucket))->nsc_status |= 311 ST_UPDATE_PENDING; 312 /* cleared by deletion of old data */ 313 } 314 mutex_unlock(&db_lock); 315 316 /* 317 * Call non-caching version in libnsl. 318 */ 319 p = _getusernam(in->nsc_u.name, &out->nsc_u.user, 320 out->nsc_u.buff + sizeof (userstr_t), 321 bufferspace, &errno); 322 saved_errno = errno; 323 324 mutex_lock(&db_lock); 325 326 release_clearance(in->nsc_callnumber); 327 328 if (p == NULL) { /* data not found */ 329 330 if (current_admin.debug_level >= DBG_CANT_FIND) { 331 logit("getuser_lookup: nscd COULDN'T FIND user_attr name %s\n", 332 in->nsc_u.name); 333 } 334 335 336 if (!(UPDATEBIT & in->nsc_callnumber)) 337 current_admin.user.nsc_neg_cache_misses++; 338 339 retb = (nsc_bucket_t *)malloc(sizeof (nsc_bucket_t)); 340 341 retb->nsc_refcount = 1; 342 retb->nsc_data.nsc_bufferbytesused = 343 sizeof (nsc_return_t); 344 retb->nsc_data.nsc_return_code = NOTFOUND; 345 retb->nsc_data.nsc_errno = saved_errno; 346 memcpy(out, &retb->nsc_data, 347 retb->nsc_data.nsc_bufferbytesused); 348 update_user_bucket((nsc_bucket_t **)bucket, 349 retb, in->nsc_callnumber); 350 goto getout; 351 } else { 352 if (current_admin.debug_level >= DBG_ALL) { 353 logit("getuser_lookup: nscd FOUND user_attr name %s\n", 354 in->nsc_u.name); 355 } 356 if (!(UPDATEBIT & in->nsc_callnumber)) 357 current_admin.user.nsc_pos_cache_misses++; 358 359 retb = fixbuffer(out, bufferspace); 360 update_user_bucket((nsc_bucket_t **)bucket, 361 retb, in->nsc_callnumber); 362 if (saved_hits) 363 retb->nsc_hits = saved_hits; 364 } 365 } else { /* found entry in cache */ 366 retb = (nsc_bucket_t *)*bucket; 367 368 retb->nsc_hits++; 369 370 memcpy(out, &(retb->nsc_data), 371 retb->nsc_data.nsc_bufferbytesused); 372 373 if (out->nsc_return_code == SUCCESS) { 374 if (!(UPDATEBIT & in->nsc_callnumber)) 375 current_admin.user.nsc_pos_cache_hits++; 376 if (current_admin.debug_level >= DBG_ALL) { 377 logit("getuser_lookup: found name %s in cache\n", 378 in->nsc_u.name); 379 } 380 } else { 381 if (!(UPDATEBIT & in->nsc_callnumber)) 382 current_admin.user.nsc_neg_cache_hits++; 383 if (current_admin.debug_level >= DBG_ALL) { 384 logit("getuser_lookup: %s marked as NOT FOUND in cache.\n", 385 in->nsc_u.name); 386 } 387 } 388 389 if ((retb->nsc_timestamp < now) && 390 !(in->nsc_callnumber & UPDATEBIT) && 391 !(retb->nsc_status & ST_UPDATE_PENDING)) { 392 logit("launch update since time = %d\n", retb->nsc_timestamp); 393 retb->nsc_status |= ST_UPDATE_PENDING; 394 /* cleared by deletion of old data */ 395 launch_update(in); 396 } 397 } 398 399 getout: 400 mutex_unlock(&db_lock); 401 } 402 403 /*ARGSUSED*/ 404 static void 405 update_user_bucket(nsc_bucket_t **old, nsc_bucket_t *new, int callnumber) 406 { 407 if (*old != NULL && *old != (nsc_bucket_t *)-1) { /* old data exists */ 408 free(*old); 409 current_admin.user.nsc_entries--; 410 } 411 412 /* 413 * we can do this before reseting *old since we're holding the lock 414 */ 415 416 else if (*old == (nsc_bucket_t *)-1) { 417 nscd_signal(&db_wait, (char **)old); 418 } 419 420 *old = new; 421 422 if ((new != NULL) && 423 (new != (nsc_bucket_t *)-1)) { 424 /* real data, not just update pending or invalidate */ 425 new->nsc_hits = 1; 426 new->nsc_status = 0; 427 new->nsc_refcount = 1; 428 current_admin.user.nsc_entries++; 429 430 if (new->nsc_data.nsc_return_code == SUCCESS) { 431 new->nsc_timestamp = time(NULL) + 432 current_admin.user.nsc_pos_ttl; 433 } else { 434 new->nsc_timestamp = time(NULL) + 435 current_admin.user.nsc_neg_ttl; 436 } 437 } 438 } 439 440 /*ARGSUSED*/ 441 static nsc_bucket_t * 442 fixbuffer(nsc_return_t *in, int maxlen) 443 { 444 nsc_bucket_t *retb; 445 nsc_return_t *out; 446 char *dest; 447 int offset; 448 int strs; 449 450 /* 451 * find out the size of the data block we're going to need 452 */ 453 454 strs = attr_strlen(in->nsc_u.user.name) + 455 attr_strlen(in->nsc_u.user.qualifier) + 456 attr_strlen(in->nsc_u.user.res1) + 457 attr_strlen(in->nsc_u.user.res2) + 458 attr_strlen(in->nsc_u.user.attr) + USERATTR_DB_NCOL; 459 460 /* 461 * allocate it and copy it in 462 * code doesn't assume packing order in original buffer 463 */ 464 465 if ((retb = (nsc_bucket_t *)malloc(sizeof (*retb) + strs)) == NULL) { 466 return (NULL); 467 } 468 469 out = &(retb->nsc_data); 470 out->nsc_bufferbytesused = strs + ((int)&out->nsc_u.user - (int)out) + 471 sizeof (userstr_t); 472 out->nsc_return_code = SUCCESS; 473 out->nsc_errno = 0; 474 475 dest = retb->nsc_data.nsc_u.buff + sizeof (userstr_t); 476 offset = (int)dest; 477 478 attr_strcpy(dest, in->nsc_u.user.name); 479 strs = 1 + attr_strlen(in->nsc_u.user.name); 480 out->nsc_u.user.name = dest - offset; 481 dest += strs; 482 483 attr_strcpy(dest, in->nsc_u.user.qualifier); 484 strs = 1 + attr_strlen(in->nsc_u.user.qualifier); 485 out->nsc_u.user.qualifier = dest - offset; 486 dest += strs; 487 488 attr_strcpy(dest, in->nsc_u.user.res1); 489 strs = 1 + attr_strlen(in->nsc_u.user.res1); 490 out->nsc_u.user.res1 = dest - offset; 491 dest += strs; 492 493 attr_strcpy(dest, in->nsc_u.user.res2); 494 strs = 1 + attr_strlen(in->nsc_u.user.res2); 495 out->nsc_u.user.res2 = dest - offset; 496 dest += strs; 497 498 attr_strcpy(dest, in->nsc_u.user.attr); 499 out->nsc_u.user.attr = dest - offset; 500 501 memcpy(in, out, out->nsc_bufferbytesused); 502 503 return (retb); 504 } 505 506 void 507 getuser_reaper(void) 508 { 509 nsc_reaper("getuser", nam_hash, ¤t_admin.user, &db_lock); 510 } 511