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 (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 27 28 /* 29 * Ported from 30 * "@(#)nis_ib_proc.c 1.37 91/03/21 Copyr 1990 Sun Micro"; 31 * 32 * nis_ib_proc.c 33 * 34 * This module contains information base function of NIS Version 3 35 * NB : It provides the routines that the dispatch function in nis_svc.c 36 * call. That file, nis_svc.c, is automatically generated and reflects the 37 * interface definition that is described in the nis.x file. When the 38 * nis.x file changes, you must make sure that any parameters that change 39 * get reflected in these routines. 40 * 41 */ 42 43 #include <stdio.h> 44 #include <syslog.h> 45 #include <stdlib.h> 46 #include <rpc/rpc.h> 47 #include <rpc/clnt.h> 48 #include <rpc/svc.h> 49 #include <rpc/auth.h> 50 #include <rpc/auth_des.h> 51 #include <rpcsvc/nis.h> 52 #include <rpcsvc/nis_callback.h> 53 #include "nis_proc.h" 54 #include "nisdb_mt.h" 55 56 #include <string.h> 57 #include <netdir.h> 58 #include <netconfig.h> 59 #include <shadow.h> 60 61 extern bool_t xdr_nis_result(); 62 extern bool_t xdr_nis_error(); 63 extern bool_t xdr_nis_object(); 64 extern void (*_svc_getreqset_proc)(); 65 extern nis_server *__nis_server_dup(nis_server *, nis_server *); 66 67 #define NO_ENTRY_OBJS 96 68 69 /* 70 * nis_censor_object[_attr] 71 * 72 * This function enforces the access rights policies for objects. 73 * It is called with a candidate ENTRY object and a principal name. 74 * The result is either NULL (object not readable) or a pointer to 75 * an object that has been "censored" by this code to remove sensitive 76 * columns or encrypt encrypted columns. 77 * nis_censor_object_attr adds a fix which ensures that 78 * users cannot infer information from the database by using special 79 * search criteria. 80 */ 81 nis_object * 82 nis_censor_object_attr(o, tc, p, zn, za) 83 nis_object *o; /* The object to censor */ 84 table_col *tc; /* Table column descriptor */ 85 nis_name p; /* The principal in question */ 86 uint_t zn; /* Number of attributes */ 87 nis_attr *za; /* The search attributes */ 88 { 89 nis_object zo; /* our censored object */ 90 int mc; /* Number of columns */ 91 int i, j, 92 somedata; 93 #define buf __nis_get_tsd()->censor_object_buf 94 entry_col *oec, /* The old columns pointer */ 95 *ec; /* Some temporary buffer */ 96 97 98 /* copy the object */ 99 zo = *o; 100 101 mc = o->EN_data.en_cols.en_cols_len; 102 /* get some temporay space. */ 103 ec = (entry_col *)nis_get_static_storage(&buf, sizeof (entry_col), mc); 104 if (! ec) 105 return (NULL); 106 107 /* Use our static version of the columns */ 108 zo.EN_data.en_cols.en_cols_val = ec; 109 110 /* Get a pointer to the original columns */ 111 oec = o->EN_data.en_cols.en_cols_val; 112 somedata = 0; 113 for (j = 0; j < mc; j++) { 114 ec[j] = oec[j]; /* copy the pointers */ 115 ec[j].ec_flags = 0; 116 /* Transfer the flags values */ 117 if (tc[j].tc_flags & TA_BINARY) 118 ec[j].ec_flags |= EN_BINARY; 119 if (tc[j].tc_flags & TA_XDR) 120 ec[j].ec_flags |= EN_XDR; 121 #ifdef NIS_CRYPT_SUPPORT 122 if (tc[j].tc_flags & TA_CRYPT) 123 ec[j].ec_flags |= EN_CRYPT; 124 #endif 125 126 /* Check to see if we can read this column */ 127 if (! __can_do(NIS_READ_ACC, tc[j].tc_rights, o, p)) { 128 /* close inference holes */ 129 for (i = 0; i < zn; i++) 130 if (za[i].zattr_ndx && 131 (strcasecmp(tc[j].tc_name, za[i].zattr_ndx) == 0)) 132 return (NULL); 133 /* Replace with No Permission value */ 134 ec[j].ENLEN = 5; 135 ec[j].ENVAL = NOPWDRTR; 136 } else { 137 somedata++; 138 #ifdef NIS_CRYPT_SUPPORT 139 if (ec[j].ec_flags & EN_CRYPT) { 140 /* Encrypt the data using the session key */ 141 /* XXX */ 142 } 143 #endif 144 } 145 } 146 if (somedata == NULL) 147 return (NULL); 148 149 return (nis_clone_object(&zo, 0)); 150 } 151 152 #undef buf 153 154 /* 155 * The original function (nis_censor_object) takes 156 * the old arguments, and passes them on with new placeholder arguments to 157 * the new function (nis_censor_object_attr). Only YP compat functions 158 * still call nis_censor_object. 159 */ 160 nis_object 161 *nis_censor_object(o, tc, p) 162 nis_object *o; /* The object to censor */ 163 table_col *tc; /* Table column descriptor */ 164 nis_name p; /* The principal in question */ 165 { 166 return (nis_censor_object_attr(o, tc, p, 0, NULL)); 167 } 168 169 /* 170 * This is the free function for the list results that are returned "enmasse" 171 * without a callback. 172 */ 173 static void 174 free_rlist(entries) 175 nis_object *entries; 176 { 177 XFREE(entries); 178 } 179 180 /* 181 * nis_return_list() 182 * 183 * This function converts a full list of entries returned by the database 184 * into a list that can be returned to the user. It has two main functions, 185 * #1) Remove those entries from the list which the client does not 186 * have read access too. 187 * #2) If the client has read access to the entry, censor those 188 * columns which the client does not have read access to. 189 */ 190 191 nis_object * 192 nis_return_list(obj, list, num, p, got, ar, zn, za) 193 nis_object *obj; /* Information base object */ 194 obj_list *list; /* Array of objects */ 195 int num; /* Size of the array */ 196 nis_name p; /* principal making reqst */ 197 int *got; /* Number actually returned */ 198 int ar; /* All readable flag */ 199 uint_t zn; /* All readable flag */ 200 nis_attr *za; /* The search attributes */ 201 { 202 register table_col *tc; /* Table column pointer */ 203 nis_object *eo; /* Temporary object */ 204 nis_object *entries; /* our result pointer */ 205 int i, /* Temporary counters */ 206 cur_entry; /* The current entry */ 207 208 *got = 0; 209 /* Get some parameters from the table object */ 210 if (__type_of(obj) == NIS_TABLE_OBJ) 211 tc = obj->TA_data.ta_cols.ta_cols_val; 212 else 213 tc = tbl_prototype.ta_cols.ta_cols_val; 214 215 /* This may allocate more than we need but it is faster this way */ 216 entries = (nis_object *)XCALLOC(num+1, sizeof (nis_object)); 217 if (! entries) 218 return (NULL); 219 220 add_cleanup(free_rlist, (char *)entries, "nis_return_list array."); 221 222 if (entries == NULL) 223 return (NULL); 224 225 for (i = 0, cur_entry = 0; i < num; i++) { 226 if (! list[i].o) 227 continue; 228 if (ar || 229 __can_do(NIS_READ_ACC, list[i].o->zo_access, list[i].o, p)) { 230 entries[cur_entry] = *list[i].o; 231 cur_entry++; 232 } else { 233 eo = nis_censor_object_attr(list[i].o, tc, p, zn, za); 234 if (eo) { 235 *(entries+cur_entry) = *eo; 236 add_cleanup(nis_destroy_object, eo, 237 "nis_censor_object_attr result"); 238 cur_entry++; 239 } 240 } 241 } 242 243 *got = cur_entry; 244 return (entries); 245 } 246 247 /* 248 * __search_ok() 249 * 250 * This function does a sanity check on the request (which has attribute 251 * value pairs) and the table which defines the names of searchable columns. 252 * if an attribute is passed that doesn't match up to a column the request 253 * is rejected. 254 */ 255 static int 256 __search_ok(zn, za, tobj) 257 uint_t zn; /* Number of attributes */ 258 nis_attr *za; /* The search attributes */ 259 nis_object *tobj; /* A Table object */ 260 { 261 register table_col *tc; 262 short acm[256]; /* XXX HARD LIMIT on columns XXX */ 263 int i, j, mc; 264 265 if (__type_of(tobj) == NIS_TABLE_OBJ) { 266 mc = tobj->TA_data.ta_maxcol; 267 tc = tobj->TA_data.ta_cols.ta_cols_val; 268 } else { 269 /* Can't specify anything for directories */ 270 return (zn == 0); 271 } 272 for (i = 0; i < zn; i++) { 273 acm[i] = -1; /* Entry column map */ 274 for (j = 0; j < mc; j++) { 275 if ((tc[j].tc_flags & TA_SEARCHABLE) && 276 (strcasecmp(tc[j].tc_name, za[i].zattr_ndx) 277 == 0)) { 278 if (acm[i] == -1) 279 acm[i] = j; 280 else 281 acm[i] = -2; 282 break; 283 } 284 } 285 286 /* 287 * If we didn't find a searchable column with the name, 288 * check to see if it is a multival column. If it is, 289 * then continue on to the other attributes. We don't 290 * update acm because multival columns can be specified 291 * multiple times. 292 */ 293 if (acm[i] == -1) { 294 if (multival_check(tobj, &za[i], tc, mc)) 295 continue; 296 } 297 298 /* Didn't match or matched twice */ 299 if (acm[i] < 0) { 300 return (0); 301 } 302 303 } 304 return (1); 305 } 306 307 /* 308 * __nis_listinit() 309 * 310 * This function set's up either a list or first/next operation. 311 * If it is successful, it returns a pointer to the object to be 312 * listed (either a table or directory object), if it is unsuccessful 313 * it returns NULL and fills in the result structure appropriately. 314 */ 315 static nis_object * 316 __nis_listinit(res, argp, princp) 317 nis_result *res; 318 ib_request *argp; 319 nis_name princp; 320 { 321 nis_object *d_obj, *ib_obj = NULL; 322 struct ticks t; 323 nis_db_result *dbres; 324 nis_error xx; 325 enum name_pos p; 326 327 NIS_RES_NUMOBJ(res) = 0; 328 p = nis_dir_cmp(argp->ibr_name, __nis_rpc_domain()); 329 if ((p != SAME_NAME) && (p != LOWER_NAME)) { 330 res->status = NIS_BADNAME; 331 return (NULL); 332 } 333 334 /* Make sure we serve this directory. */ 335 xx = __directory_object(nis_domain_of(argp->ibr_name), &t, 0, &d_obj); 336 res->aticks = t.aticks; 337 res->dticks = t.dticks; 338 res->cticks = t.cticks; 339 res->zticks = t.zticks; 340 341 if ((xx != NIS_SUCCESS) && (xx != NIS_S_SUCCESS)) { 342 /* 343 * Hmm, we didn't find it. Maybe it _is_ our directory 344 * object. 345 */ 346 xx = __directory_object(argp->ibr_name, &t, 0, &d_obj); 347 res->aticks += t.aticks; 348 res->dticks += t.dticks; 349 res->cticks += t.cticks; 350 res->zticks += t.zticks; 351 if ((xx != NIS_SUCCESS) && (xx != NIS_S_SUCCESS)) { 352 /* They really blew it */ 353 syslog(LOG_INFO, "Can't find directory for %s", 354 argp->ibr_name); 355 res->status = xx; 356 return (NULL); 357 } else { 358 ib_obj = d_obj; 359 } 360 } 361 362 /* Now get the information base object from the database */ 363 if (! ib_obj) { 364 dbres = db_lookup(argp->ibr_name); 365 res->dticks += dbres->ticks; 366 if (dbres->status == NIS_NOSUCHTABLE) { 367 /* This means table of directory does not exist. */ 368 dbres->status = recover_from_no_table(d_obj, 369 nis_domain_of(argp->ibr_name)); 370 /* for sure, argp->ibr_name will not be there */ 371 if (dbres->status == NIS_SUCCESS) 372 dbres->status = NIS_NOTFOUND; 373 } 374 if (dbres->status != NIS_SUCCESS) { 375 if (dbres->status == NIS_NOTFOUND) 376 res->status = NIS_NOSUCHTABLE; 377 else 378 res->status = dbres->status; 379 res->zticks += __stop_clock(0); 380 return (NULL); 381 } 382 ib_obj = dbres->obj; 383 } else { 384 /* 385 * If its a directory, make sure we've got a database 386 * for it. (if not create one and synchronize) 387 */ 388 nis_error status = db_find_table(argp->ibr_name); 389 switch (status) { 390 case NIS_SUCCESS: 391 break; 392 case NIS_NOSUCHTABLE: 393 res->status = recover_from_no_table(d_obj, 394 argp->ibr_name); 395 if (res->status != NIS_SUCCESS) { 396 return (NULL); 397 } 398 break; 399 default: 400 res->status = status; 401 return (NULL); 402 } 403 } 404 405 switch (__type_of(ib_obj)) { 406 case NIS_TABLE_OBJ: 407 case NIS_DIRECTORY_OBJ: 408 break; 409 410 case NIS_LINK_OBJ: 411 if (verbose) 412 syslog(LOG_INFO, "__nis_listinit: Object is a link."); 413 /* 414 * we return a partial result indicating that the LINK was 415 * found and the client can follow it if they choose to. 416 */ 417 res->status = NIS_PARTIAL; 418 NIS_RES_NUMOBJ(res) = 1; 419 NIS_RES_OBJECT(res) = ib_obj; 420 return (NULL); 421 422 default: 423 /* 424 * XXX this hard codes the "kinds" of objects that can be 425 * information bases. This is not nice if we want to support 426 * user designed objects. 427 */ 428 res->status = NIS_INVALIDOBJ; 429 if (verbose) 430 syslog(LOG_INFO, 431 "__nis_listinit: Nonsearchable object (type = %d).", 432 __type_of(ib_obj)); 433 return (NULL); 434 } 435 436 /* Check to see is we have a valid search criteria */ 437 if (!__search_ok(argp->ibr_srch.ibr_srch_len, 438 argp->ibr_srch.ibr_srch_val, ib_obj)) { 439 res->status = NIS_BADATTRIBUTE; 440 if (verbose) 441 syslog(LOG_INFO, 442 "__nis_listinit: Unparsable attribute failure."); 443 return (NULL); 444 } 445 446 res->status = NIS_SUCCESS; 447 return (ib_obj); 448 } 449 450 /* 451 * Does the callback part of iblist 452 */ 453 static nis_result * 454 nis_iblist_callback(argp, pname, ib_obj, all_read, res) 455 ib_request *argp; 456 char *pname; /* caller's name */ 457 nis_object *ib_obj; 458 int all_read; /* all readable flag */ 459 nis_result *res; 460 { 461 nis_error err; 462 nis_object *d_obj; 463 int pid = -1; /* Process ID of child making call back */ 464 int i, queued; 465 cback_data cbarg; /* Callback arguments */ 466 /* An array of object pointers */ 467 struct timeval tv; /* Timeout for callback */ 468 nis_fn_result *fnr; 469 netobj cookie; /* Cookie storage for first/nxt */ 470 enum clnt_stat status = RPC_SUCCESS; 471 nis_error result; 472 table_col *tc; 473 char tblbuf[NIS_MAXNAMELEN * 2]; 474 char *table; 475 nis_attr *a; 476 int na; /* number of regular attributes */ 477 int nm; /* number of multival attributes */ 478 479 480 /* 481 * Used to create the callback handle here. We now delay 482 * until we know that we actually intend to perform the callback. 483 */ 484 485 table = internal_table_name(argp->ibr_name, tblbuf); 486 if (! table) { 487 res->status = NIS_BADNAME; 488 return (res); 489 } 490 491 na = argp->ibr_srch.ibr_srch_len; 492 a = argp->ibr_srch.ibr_srch_val; 493 err = multival_attr(ib_obj, a, &na, &nm); 494 if (err != NIS_SUCCESS) { 495 res->status = err; 496 return (res); 497 } 498 499 /* 500 * This will force the database to be loaded. 501 */ 502 fnr = db_firstib(argp->ibr_name, na, a, FN_MANGLE+FN_NORAGS, table); 503 504 if (fnr->status != NIS_SUCCESS) { 505 if (fnr->status == NIS_NOTFOUND) { 506 res->status = NIS_PARTIAL; 507 res->objects.objects_val = ib_obj; 508 res->objects.objects_len = 1; 509 } else { 510 res->status = fnr->status; 511 res->objects.objects_val = NULL; 512 res->objects.objects_len = 0; 513 } 514 res->dticks += fnr->ticks; 515 res->zticks += __stop_clock(0); 516 XFREE(fnr); 517 fnr = NULL; 518 return (res); 519 } 520 521 /* Create a new thread to perform the callback */ 522 { 523 pthread_t tid; 524 pthread_attr_t attr; 525 int stat; 526 callback_thread_arg_t *cbtarg; 527 int attrsize = na * sizeof (cbtarg->a[0]); 528 nis_server *srvl; 529 530 if ((cbtarg = calloc(1, sizeof (*cbtarg))) == 0) { 531 syslog(LOG_WARNING, 532 "nis_iblist_callback: memory allocation failed for %d bytes", 533 sizeof (*cbtarg)); 534 res->status = NIS_NOMEMORY; 535 res->zticks = __stop_clock(0); 536 XFREE(fnr); 537 return (res); 538 } 539 cbtarg->fnr = fnr; 540 cbtarg->ib_obj = nis_clone_object(ib_obj, 0); 541 if (na <= (sizeof (cbtarg->a)/sizeof (cbtarg->a[0]))) { 542 cbtarg->na = na; 543 } else { 544 syslog(LOG_WARNING, 545 "nis_iblist_callback: too many attributes; max %d expected, %d found", 546 (sizeof (cbtarg->a)/sizeof (cbtarg->a[0])), na); 547 cbtarg->na = sizeof (cbtarg->a)/sizeof (cbtarg->a[0]); 548 #ifdef NIS_MT_DEBUG 549 abort(); 550 #endif /* NIS_MT_DEBUG */ 551 } 552 memcpy(cbtarg->a, a, cbtarg->na * sizeof (cbtarg->a[0])); 553 cbtarg->nm = nm; 554 cbtarg->all_read = all_read; 555 strncpy(cbtarg->pname, pname, sizeof (cbtarg->pname)); 556 cbtarg->pname[sizeof (cbtarg->pname) - 1] = '\0'; 557 cbtarg->cbarg = cbarg; 558 srvl = (nis_server *) __nis_server_dup( 559 (nis_server *)argp->ibr_cbhost.ibr_cbhost_val, NULL); 560 if (srvl == NULL) { 561 syslog(LOG_WARNING, 562 "nis_iblist_callback: out of memory"); 563 res->status = NIS_NOMEMORY; 564 res->zticks = __stop_clock(0); 565 XFREE(fnr); 566 free(cbtarg); 567 return (res); 568 } 569 cbtarg->nserver = srvl; 570 strncpy(cbtarg->cbhostname, 571 argp->ibr_cbhost.ibr_cbhost_val[0].name, 572 sizeof (cbtarg->cbhostname)); 573 cbtarg->cbhostname[sizeof (cbtarg->cbhostname) - 1] = '\0'; 574 strncpy(cbtarg->ibr_name, 575 argp->ibr_name, sizeof (cbtarg->ibr_name)); 576 cbtarg->ibr_name[sizeof (cbtarg->ibr_name) - 1] = '\0'; 577 578 (void) pthread_attr_init(&attr); 579 (void) pthread_attr_setdetachstate(&attr, 580 PTHREAD_CREATE_DETACHED); 581 if ((stat = pthread_create(&tid, &attr, callback_thread, 582 cbtarg)) == 0) { 583 if (verbose) 584 syslog(LOG_INFO, 585 "nis_iblist_callback: created callback thread %d", tid); 586 res->status = NIS_CBRESULTS; 587 } else { 588 if (verbose) 589 syslog(LOG_WARNING, 590 "nis_iblist_callback: callback thread create failed: %d", 591 stat); 592 res->status = NIS_TRYAGAIN; 593 } 594 res->zticks += __stop_clock(0); 595 596 (void) pthread_attr_destroy(&attr); 597 598 res->cookie.n_bytes = (char *)malloc(sizeof (anonid_t)); 599 if (res->cookie.n_bytes == NULL) { 600 syslog(LOG_WARNING, 601 "Couldn't allocate memory for callback id"); 602 /* what error to return in case of no memory */ 603 res->status = NIS_NOMEMORY; 604 } else { 605 anonid_t anonid = tid; 606 add_cleanup((void (*)())XFREE, 607 (char *)res->cookie.n_bytes, "callback id"); 608 res->cookie.n_len = sizeof (anonid); 609 memcpy(res->cookie.n_bytes, &anonid, 610 sizeof (anonid)); 611 if (verbose) 612 syslog(LOG_INFO, 613 "returning, callback id = %d", 614 *((anonid_t *)res->cookie.n_bytes)); 615 } 616 return (res); 617 } 618 } 619 620 621 /* 622 * __nis_alt_callback_server() 623 * 624 * Return a pointer to a new chunk of memory containing a copy of the 625 * org_endpoint argument, with the rpc_origin address substituted (merged, 626 * since the port number is retained) for the first endpoint whose family 627 * matches an NC_TPI_COTS netconfig entry. 628 * 629 * If (*uaddr != 0), it is assumed to point to a piece of memory of length 630 * uaddrlen, where the caller wants a copy of the uaddr. 631 * 632 * It is the responsibility of the caller to free() the returned endpoint 633 * structure, as well as the uaddr if (*uaddr == 0). 634 */ 635 endpoint * 636 __nis_alt_callback_server(endpoint *org_endpoint, 637 uint_t count, 638 struct netbuf *rpc_origin, 639 char **uaddr, 640 int uaddrlen) 641 { 642 endpoint *alt_endpoint; 643 uint_t size, i, j, ei; 644 struct netconfig *nc, *match = 0; 645 struct netbuf *taddr; 646 void *newaddr; 647 void *rpcaddr; 648 int addrlen; 649 sa_family_t af, rpcaf; 650 char *tmpuaddr; 651 652 /* Sanity check arguments */ 653 if (org_endpoint == 0 || count == 0 || rpc_origin == 0) { 654 return (0); 655 } 656 657 /* We only know how to deal with IP */ 658 rpcaf = ((struct sockaddr *)rpc_origin->buf)->sa_family; 659 if (rpcaf != AF_INET && rpcaf != AF_INET6) { 660 return (0); 661 } 662 663 /* Retrieve a netconfig entry for "inet" that matches an endpoint */ 664 for (i = 0; i < count; i++) { 665 if ((nc = __nis_get_netconfig(&org_endpoint[i])) != 0 && 666 nc->nc_semantics == NC_TPI_COTS_ORD) { 667 if (strcasecmp(nc->nc_protofmly, NC_INET) == 0 && 668 rpcaf == AF_INET) { 669 af = AF_INET; 670 } else if (strcasecmp( 671 nc->nc_protofmly, NC_INET6) == 0 && 672 rpcaf == AF_INET6) { 673 af = AF_INET6; 674 } else { 675 continue; 676 } 677 match = nc; 678 ei = i; 679 break; 680 } 681 } 682 683 /* Did we find one ? */ 684 if (match == 0) { 685 return (0); 686 } 687 688 /* 689 * Allocate memory for the alternate endpoint array. 690 * Must be freed by caller, if we return successfully. 691 */ 692 size = count * sizeof (endpoint); 693 if ((alt_endpoint = malloc(size)) == 0) { 694 return (0); 695 } 696 697 for (j = 0; j < count; j++) { 698 alt_endpoint[j] = org_endpoint[j]; 699 } 700 701 /* Get the address suggested by the remote caller */ 702 if ((taddr = uaddr2taddr(match, alt_endpoint[ei].uaddr)) == 0) { 703 free(alt_endpoint); 704 return (0); 705 } 706 707 /* 708 * The ugly part: merge port number and RPC source address. 709 * This requires knowledge about the internals of a netbuf. 710 */ 711 712 if (af == AF_INET) { 713 struct in_addr dummy; 714 newaddr = 715 &((struct sockaddr_in *)taddr->buf)->sin_addr.s_addr; 716 rpcaddr = 717 &((struct sockaddr_in *)rpc_origin->buf)->sin_addr.s_addr; 718 addrlen = sizeof (dummy.s_addr); 719 } else { 720 struct in6_addr dummy; 721 newaddr = 722 &((struct sockaddr_in6 *)taddr->buf)->sin6_addr.s6_addr; 723 rpcaddr = &((struct sockaddr_in6 *) 724 rpc_origin->buf)->sin6_addr.s6_addr; 725 addrlen = sizeof (dummy.s6_addr); 726 } 727 728 /* 729 * If addresses are the same, there is no need to try an alternate 730 * address, so just pretend that we failed. 731 */ 732 if (!memcmp(newaddr, rpcaddr, addrlen)) { 733 free(alt_endpoint); 734 netdir_free(taddr, ND_ADDR); 735 return (0); 736 } 737 738 /* Alternate address differs; worth trying */ 739 memcpy(newaddr, rpcaddr, addrlen); 740 if ((tmpuaddr = taddr2uaddr(match, taddr)) == 0) { 741 free(alt_endpoint); 742 netdir_free(taddr, ND_ADDR); 743 return (0); 744 } 745 746 if (*uaddr == 0) { 747 *uaddr = tmpuaddr; 748 } else if (strlen(tmpuaddr) < uaddrlen) { 749 (void) strcpy(*uaddr, tmpuaddr); 750 free(tmpuaddr); 751 } else { 752 free(alt_endpoint); 753 netdir_free(taddr, ND_ADDR); 754 free(tmpuaddr); 755 return (0); 756 } 757 758 /* Clean up */ 759 netdir_free(taddr, ND_ADDR); 760 761 alt_endpoint[ei].uaddr = *uaddr; 762 763 return (alt_endpoint); 764 } 765 766 /* 767 * nis_iblist, this function will take the search criteria passed, 768 * and pass it on to the database. If the callback structure is 769 * null then the results are simply returned, otherwise the server 770 * forks and and begins returning results one by one. In a multithreaded 771 * environment this is handled by a thread. The library will check 772 * a resource limit variable before forking. 773 */ 774 nis_result * 775 nis_iblist_svc(argp, reqstp) 776 ib_request *argp; 777 struct svc_req *reqstp; 778 { 779 nis_result *res = NULL; 780 nis_object *ib_obj, 781 *list; 782 int got; 783 char *s; 784 int all_read; /* all readable flag */ 785 char pname[1024]; 786 nis_db_list_result *ib_list; 787 int zn; 788 nis_attr *za; 789 790 __start_clock(0); 791 if (verbose) 792 syslog(LOG_INFO, "LIST_SVC: Listing table %s", 793 argp->ibr_name); 794 795 res = (nis_result *)XCALLOC(1, sizeof (nis_result)); 796 add_cleanup((void (*)())XFREE, (char *)res, "iblist result"); 797 798 /* If reqstp == NULL then we're recursing */ 799 if (reqstp) 800 nis_getprincipal(pname, reqstp); 801 else 802 pname[0] = '\0'; 803 804 ib_obj = __nis_listinit(res, argp, pname); 805 806 if (res->status != NIS_SUCCESS) { 807 res->zticks = __stop_clock(0); 808 return (res); 809 } 810 /* If we have read access on the table, we can read all of it */ 811 all_read = __can_do(NIS_READ_ACC, ib_obj->zo_access, ib_obj, pname); 812 813 /* 814 * Now we actually do the list operation. If there is a callback, 815 * then we use the first/next operations to send the objects back 816 * one at a time. 817 */ 818 if (argp->ibr_cbhost.ibr_cbhost_len) { 819 /* Process the callback request. */ 820 /* 821 * The client supplied an address for the callback service. 822 * However, we may not be able to reach that address, so 823 * try the source address of the RPC request first. 824 * 825 * Note: The alt_endpoint array (which usually contains just 826 * a single element) is a copy of of org_endpoint, including 827 * pointers, except for the uaddr of one element. This uaddr 828 * contains a merged version of the RPC source address and the 829 * port number specified by the client in the callback data. 830 */ 831 { 832 struct netbuf *rpc_origin; 833 endpoint *org_endpoint, *alt_endpoint; 834 char *uaddr = 0; 835 836 org_endpoint = 837 argp->ibr_cbhost.ibr_cbhost_val->ep.ep_val; 838 rpc_origin = svc_getrpccaller(reqstp->rq_xprt); 839 if ((alt_endpoint = __nis_alt_callback_server( 840 org_endpoint, 841 argp->ibr_cbhost.ibr_cbhost_val->ep.ep_len, 842 rpc_origin, 843 &uaddr, 0)) != 0) { 844 argp->ibr_cbhost.ibr_cbhost_val->ep.ep_val = 845 alt_endpoint; 846 res = nis_iblist_callback(argp, pname, ib_obj, 847 all_read, res); 848 argp->ibr_cbhost.ibr_cbhost_val->ep.ep_val = 849 org_endpoint; 850 free(alt_endpoint); 851 free(uaddr); 852 if (res->status != NIS_NOCALLBACK) { 853 return (res); 854 } 855 } 856 } 857 return (nis_iblist_callback(argp, pname, ib_obj, 858 all_read, res)); 859 } 860 /* 861 * If the client didn't ask for a callback, we process the 862 * entire list at once and return it. (implicit else clause) 863 */ 864 ib_list = db_list(argp->ibr_name, argp->ibr_srch.ibr_srch_len, 865 argp->ibr_srch.ibr_srch_val); 866 867 res->dticks += ib_list->ticks; 868 if (ib_list->status != NIS_SUCCESS) { 869 if (ib_list->status == NIS_NOTFOUND) { 870 res->status = NIS_PARTIAL; 871 NIS_RES_OBJECT(res) = ib_obj; 872 NIS_RES_NUMOBJ(res) = 1; 873 } else { 874 if (__type_of(ib_obj) == NIS_TABLE_OBJ) { 875 s = "table"; 876 } else 877 s = "directory"; 878 syslog(LOG_ERR, "Unable to list %s '%s' err=%d", 879 s, argp->ibr_name, ib_list->status); 880 res->status = ib_list->status; 881 NIS_RES_OBJECT(res) = NULL; 882 NIS_RES_NUMOBJ(res) = 0; 883 } 884 res->zticks = __stop_clock(0); 885 if (verbose) 886 syslog(LOG_INFO, "nis_iblist_svc: return status %s", 887 nis_sperrno(res->status)); 888 return (res); 889 } 890 /* Now process the list of objects we've got to return */ 891 zn = argp->ibr_srch.ibr_srch_len; 892 za = argp->ibr_srch.ibr_srch_val; 893 list = nis_return_list(ib_obj, ib_list->objs, ib_list->numo, pname, 894 &got, all_read, 895 zn, za); 896 897 /* Construct the result */ 898 if (got) { 899 res->status = NIS_SUCCESS; 900 NIS_RES_NUMOBJ(res) = got; 901 NIS_RES_OBJECT(res) = list; 902 } else { 903 res->status = NIS_NOTFOUND; 904 NIS_RES_NUMOBJ(res) = 0; 905 NIS_RES_OBJECT(res) = NULL; 906 } 907 res->zticks = __stop_clock(0); 908 return (res); 909 } 910 911 /* 912 * This is an internal Information Base operations function. It is collected 913 * here for clarity. It makes the actual database calls to manipulate the 914 * namespace. 915 */ 916 static void 917 add_entry(name, zn, za, obj, tbl, flags, princp, xid, res) 918 nis_name name; /* Table name */ 919 int zn; /* Number of attributes */ 920 nis_attr *za; /* NIS+ attribute list. */ 921 nis_object *obj, /* The entry we're adding */ 922 *tbl; /* Table we're adding to */ 923 ulong_t flags; /* Semantic flags */ 924 nis_name princp; /* Principal making request */ 925 int *xid; /* XID for transaction */ 926 nis_result *res; /* Place for results. */ 927 { 928 nis_db_list_result *db; 929 nis_db_result *ars; 930 int add_ok, mod_ok; 931 time_t currtime; 932 933 /* 934 * Check to see if we can actually create an entry in this 935 * table. 936 */ 937 add_ok = __can_do(NIS_CREATE_ACC, tbl->zo_access, tbl, princp); 938 mod_ok = __can_do(NIS_MODIFY_ACC, tbl->zo_access, tbl, princp); 939 if (auth_verbose) { 940 syslog(LOG_INFO, 941 "add_entry: creation is %s by the table.", 942 (add_ok) ? "ALLOWED" : "DISALLOWED"); 943 944 } 945 946 db = db_list(name, zn, za); 947 948 *xid = begin_transaction(princp); 949 if (*xid == 0) { 950 res->status = NIS_TRYAGAIN; 951 return; 952 } 953 954 currtime = time(0); /* Make sure REMOVE/ADD has same time */ 955 obj->zo_oid.ctime = (ulong_t)currtime; 956 obj->zo_oid.mtime = obj->zo_oid.ctime; 957 if (db->status == NIS_SUCCESS) { 958 if (db->numo != 1) { 959 res->status = NIS_NOTUNIQUE; 960 } else if ((flags & ADD_OVERWRITE) == 0) { 961 res->status = NIS_NAMEEXISTS; 962 } else if (! mod_ok && 963 ! __can_do(NIS_MODIFY_ACC, db->objs[0].o->zo_access, 964 db->objs[0].o, princp)) { 965 res->status = NIS_PERMISSION; 966 } else { 967 nisdb_tsd_t *tsd; 968 969 /* Act like a modify on the OID */ 970 obj->zo_oid.ctime = db->objs->o->zo_oid.ctime; 971 972 /* 973 * Tell the DB that we're doing a modify, so that 974 * it can merge the remove/add to a single LDAP 975 * update. 976 */ 977 tsd = __nisdb_get_tsd(); 978 tsd->doingModify = 1; 979 980 /* This updates the log */ 981 db_remib(name, zn, za, db->objs, db->numo, tbl, 982 (ulong_t)currtime); 983 /* As does this, a virtual modify operation */ 984 ars = db_addib(name, zn, za, obj, tbl); 985 986 tsd->doingModify = 0; 987 988 res->dticks += ars->ticks; 989 res->status = ars->status; 990 } 991 } else if (db->status == NIS_NOTFOUND) { 992 /* Ok, it doesn't exist so lets add it to the table */ 993 if (! add_ok) { 994 res->status = NIS_PERMISSION; 995 return; 996 } 997 998 ars = db_addib(name, zn, za, obj, tbl); 999 if ((ars->status == NIS_SUCCESS) && (flags & RETURN_RESULT)) { 1000 res->objects.objects_val = nis_clone_object(obj, NULL); 1001 res->objects.objects_len = 1; 1002 } 1003 res->dticks += ars->ticks; 1004 res->status = ars->status; 1005 } else 1006 res->status = db->status; 1007 } 1008 1009 /* 1010 * This is an internal Information Base operations function that implements 1011 * the remove operation. It is collected here for clarity. 1012 */ 1013 static void 1014 remove_entry(name, zn, za, obj, tbl, flags, princp, xid, res) 1015 nis_name name; /* Table name */ 1016 int zn; /* Number of attributes */ 1017 nis_attr *za; /* NIS attribute list. */ 1018 nis_object *obj, /* The entry we're removing */ 1019 *tbl; /* Table we're adding to */ 1020 ulong_t flags; /* Semantic flags */ 1021 nis_name princp; /* Principal making request */ 1022 int *xid; /* XID for transaction */ 1023 nis_result *res; /* Place for results. */ 1024 { 1025 nis_db_list_result *db; 1026 nis_db_result *rrs; 1027 int i, rem_ok; 1028 1029 /* 1030 * Access control, if we have remove access granted at the 1031 * table level then we can remove all entries. 1032 */ 1033 rem_ok = __can_do(NIS_DESTROY_ACC, tbl->zo_access, tbl, princp); 1034 if (auth_verbose) { 1035 syslog(LOG_INFO, 1036 "remove_entry: removal is %s by the table.", 1037 (rem_ok) ? "ALLOWED" : "DISALLOWED"); 1038 1039 } 1040 1041 1042 db = db_list(name, zn, za); 1043 res->dticks += db->ticks; 1044 1045 /* 1046 * Check for errors, first to see if we actually found an 1047 * object to remove. 1048 */ 1049 if (db->status != NIS_SUCCESS) { 1050 res->status = db->status; 1051 return; 1052 } 1053 1054 /* 1055 * next to see if more than one object may be removed. 1056 */ 1057 if ((db->numo > 1) && ((flags & REM_MULTIPLE) == 0)) { 1058 res->status = NIS_NOTUNIQUE; 1059 return; 1060 } 1061 1062 /* 1063 * Third to see if we need the same object to remove 1064 */ 1065 if (obj && (! same_oid(obj, db->objs->o))) { 1066 res->status = NIS_NOTSAMEOBJ; 1067 return; 1068 } 1069 1070 /* 1071 * Fourth check to see if we have the right to remove 1072 * these entrie(s) 1073 */ 1074 if (! rem_ok) { 1075 for (i = 0; i < db->numo; i++) 1076 if (! __can_do(NIS_DESTROY_ACC, 1077 db->objs[i].o->zo_access, 1078 db->objs[i].o, princp)) 1079 break; 1080 if (i < db->numo) { 1081 res->status = NIS_PERMISSION; 1082 return; 1083 } 1084 } 1085 1086 *xid = begin_transaction(princp); 1087 if (*xid == 0) { 1088 res->status = NIS_TRYAGAIN; 1089 return; 1090 } 1091 1092 /* 1093 * Finally, remove the object in question. 1094 */ 1095 rrs = db_remib(name, zn, za, db->objs, db->numo, tbl, 1096 (ulong_t)(time(0))); 1097 res->dticks += rrs->ticks; 1098 res->status = rrs->status; 1099 } 1100 1101 /* 1102 * This is the internal Information base function that implements the modify 1103 * operation. It is put here for clarity. 1104 */ 1105 static void 1106 modify_entry(name, zn, za, obj, tbl, flags, princp, xid, res) 1107 nis_name name; /* Table name */ 1108 int zn; /* Number of attributes */ 1109 nis_attr *za; /* NIS attribute list. */ 1110 nis_object *obj, /* The entry we're removing */ 1111 *tbl; /* Table we're adding to */ 1112 ulong_t flags; /* Semantic flags */ 1113 nis_name princp; /* Principal making request */ 1114 int *xid; /* XID for transaction */ 1115 nis_result *res; /* Place for results. */ 1116 { 1117 #define buf __nis_get_tsd()->modify_entry_buf 1118 nis_db_list_result *db; 1119 nis_db_result *rrs; 1120 int i, mc; 1121 entry_col *o_ec, /* Old entry columns */ 1122 *n_ec; /* New entry columns */ 1123 nis_object mobj; /* Modified object ... */ 1124 int clone; /* True if new object is a clone */ 1125 entry_col *mec; /* ... and it's columns */ 1126 int mod_ok; 1127 table_col *tcol; 1128 nis_db_list_result *o_db; 1129 nis_attr *test_attr; 1130 int na, mod_attrs = 0; 1131 time_t currtime; 1132 nisdb_tsd_t *tsd; 1133 1134 /* 1135 * Set the global modify access bit for the objects by checking 1136 * the table's access rights. 1137 */ 1138 mod_ok = __can_do(NIS_MODIFY_ACC, tbl->zo_access, tbl, princp); 1139 1140 if (auth_verbose) { 1141 syslog(LOG_INFO, 1142 "modify_entry: modification is %s by the table.", 1143 (mod_ok) ? "ALLOWED" : "DISALLOWED"); 1144 1145 } 1146 1147 mc = tbl->TA_data.ta_maxcol; 1148 tcol = tbl->TA_data.ta_cols.ta_cols_val; 1149 mec = (entry_col *)nis_get_static_storage(&buf, sizeof (entry_col), mc); 1150 if (! mec) { 1151 res->status = NIS_NOMEMORY; 1152 return; 1153 } 1154 1155 db = db_list(name, zn, za); 1156 1157 /* If we didn't find the original just return the error */ 1158 if (db->status != NIS_SUCCESS) { 1159 res->status = db->status; 1160 return; 1161 } 1162 1163 /* If the object isn't unique we can't modify it. */ 1164 if (db->numo > 1) { 1165 res->status = NIS_NOTUNIQUE; 1166 return; 1167 } 1168 1169 clone = same_oid(obj, db->objs->o); 1170 1171 if ((flags & MOD_SAMEOBJ) && (! clone)) { 1172 res->status = NIS_NOTSAMEOBJ; 1173 return; 1174 } 1175 1176 /* 1177 * Now check the permissions on the object itself. 1178 * If we don't have modify access to the table we check 1179 * to see if we have modify access to the object itself, 1180 * if that fails we check to see if we have modify access 1181 * to the column we are modifying. If that fails we return 1182 * a permission error. 1183 */ 1184 o_ec = db->objs->o->EN_data.en_cols.en_cols_val; 1185 n_ec = obj->EN_data.en_cols.en_cols_val; 1186 if (! mod_ok) 1187 mod_ok = __can_do(NIS_MODIFY_ACC, db->objs[0].o->zo_access, 1188 db->objs[0].o, princp); 1189 if (! mod_ok) { 1190 for (i = 0; i < mc; i++) 1191 if (((n_ec[i].ec_flags & EN_MODIFIED) != 0) && 1192 ! __can_do(NIS_MODIFY_ACC, 1193 tcol[i].tc_rights, 1194 db->objs[0].o, princp)) { 1195 res->status = NIS_PERMISSION; 1196 return; 1197 } 1198 } 1199 1200 /* 1201 * Check the MOD_EXCLUSIVE flag. If a searchable column has been 1202 * modified, the key index will be changed, which may conflict with 1203 * an existing entry. 1204 */ 1205 1206 if (flags & MOD_EXCLUSIVE) { 1207 /* Check for a searchable column that has been modified */ 1208 for (i = 0; i < mc; i++) { 1209 if (mod_attrs = ((tcol[i].tc_flags & TA_SEARCHABLE) && 1210 (n_ec[i].ec_flags & EN_MODIFIED))) { 1211 break; 1212 } 1213 } 1214 /* 1215 * If key modified, then create new attr list and see if 1216 * it matches an existing entry 1217 */ 1218 if (mod_attrs) { 1219 test_attr = __get_attrs(mc); 1220 if (test_attr == NULL) { 1221 res->status = NIS_NOMEMORY; 1222 return; 1223 } 1224 for (i = 0, na = 0; i < mc; i++) { 1225 if (tcol[i].tc_flags & TA_SEARCHABLE) { 1226 test_attr[na].zattr_ndx = 1227 tcol[i].tc_name; 1228 if ((n_ec[i].ec_flags & EN_MODIFIED) == 1229 0) { 1230 test_attr[na].ZAVAL = o_ec[i].ENVAL; 1231 test_attr[na].ZALEN = o_ec[i].ENLEN; 1232 } else { 1233 test_attr[na].ZAVAL = n_ec[i].ENVAL; 1234 test_attr[na].ZALEN = n_ec[i].ENLEN; 1235 } 1236 na++; 1237 } 1238 } 1239 o_db = db_list(name, na, test_attr); 1240 if (o_db->status == NIS_SUCCESS) { 1241 res->status = NIS_NAMEEXISTS; 1242 return; 1243 } 1244 } 1245 } 1246 1247 mobj = *(db->objs->o); 1248 currtime = time(0); /* Make sure REMOVE/ADD has same time */ 1249 mobj.zo_oid.mtime = (ulong_t)currtime; 1250 1251 /* Now check to see if the attributes need to change */ 1252 /* 1253 * XXX this can fail silently when attributes change 1254 * but mod_ok isn't set, we should figure out how to 1255 * return a permission error. XXX 1256 */ 1257 if (clone && mod_ok) { 1258 mobj.zo_owner = obj->zo_owner; 1259 mobj.zo_group = obj->zo_group; 1260 mobj.zo_access = obj->zo_access; 1261 mobj.zo_ttl = obj->zo_ttl; 1262 } 1263 mobj.EN_data.en_cols.en_cols_val = mec; 1264 1265 for (i = 0; i < mc; i++) { 1266 if ((n_ec[i].ec_flags & EN_MODIFIED) == 0) 1267 mec[i] = o_ec[i]; 1268 else 1269 mec[i] = n_ec[i]; 1270 } 1271 1272 *xid = begin_transaction(princp); 1273 if (*xid == 0) { 1274 res->status = NIS_TRYAGAIN; 1275 return; 1276 } 1277 1278 /* 1279 * If changes are written through to LDAP, we want to make 1280 * just one LDAP update, not one remove and one add, which 1281 * causes problems when more than one NIS+ table maps to 1282 * one and the same LDAP container. Hence, inform the DB 1283 * that this removal is part of a modify operation. 1284 */ 1285 tsd = __nisdb_get_tsd(); /* Never returns NULL */ 1286 tsd->doingModify = 1; 1287 1288 /* Remove the old version of the entry */ 1289 rrs = db_remib(name, zn, za, db->objs, db->numo, tbl, 1290 (ulong_t)currtime); 1291 res->dticks += rrs->ticks; 1292 1293 if (res->status != NIS_SUCCESS) 1294 syslog(LOG_ERR, "modify_entry: Unable to remove object %s", 1295 name); 1296 /* 1297 * Now do the modify by overwriting the existing 1298 * object. 1299 */ 1300 rrs = db_addib(name, zn, za, &mobj, tbl); 1301 if ((rrs->status == NIS_SUCCESS) && (flags & RETURN_RESULT)) { 1302 res->objects.objects_val = nis_clone_object(&mobj, NULL); 1303 res->objects.objects_len = 1; 1304 } 1305 1306 /* Reset modification indication to the DB */ 1307 tsd->doingModify = 0; 1308 1309 res->dticks += rrs->ticks; 1310 res->status = rrs->status; 1311 } 1312 1313 #undef buf 1314 1315 /* 1316 * __nis_ibops() 1317 * This is the common information base ops routine. It implements the 1318 * various operations that can be done on an information base. It is 1319 * analagous to the nameops() routine above. 1320 */ 1321 static nis_result * 1322 __nis_ibops(op, argp, princp) 1323 int op; 1324 ib_request *argp; 1325 nis_name princp; 1326 { 1327 nis_result *res; 1328 #define buf __nis_get_tsd()->__ibops_buf 1329 nis_object *t_obj, *n_obj, *d_obj; 1330 nis_attr *za; 1331 int zn; 1332 table_col *tc; 1333 ulong_t ttime; 1334 int i, mc, xid; 1335 entry_col *ec; 1336 nis_db_result *table; 1337 struct ticks t; 1338 char optxt[32]; 1339 nis_error xx; 1340 1341 res = (nis_result *)XCALLOC(1, sizeof (nis_result)); 1342 add_xdr_cleanup(xdr_nis_result, (char *)res, "ibops result"); 1343 1344 if (readonly) { 1345 res->status = NIS_TRYAGAIN; 1346 return (res); 1347 } 1348 1349 if (auth_verbose) { 1350 switch (op) { 1351 case ADD_OP : 1352 strcpy(optxt, "ADD"); 1353 break; 1354 case REM_OP : 1355 strcpy(optxt, "REMOVE"); 1356 break; 1357 case MOD_OP : 1358 strcpy(optxt, "MODIFY"); 1359 break; 1360 } 1361 syslog(LOG_INFO, "Entry operation '%s' for principal %s", 1362 optxt, princp); 1363 } 1364 1365 /* 1366 * Check to see if we serve the directory this table is in. 1367 * NOTE: this could do a NIS+ lookup to the parent. 1368 */ 1369 xx = __directory_object(nis_domain_of(argp->ibr_name), &t, TRUE, 1370 &d_obj); 1371 res->aticks = t.aticks; 1372 res->zticks = t.zticks; 1373 res->cticks = t.cticks; 1374 res->dticks = t.dticks; 1375 if (d_obj == NULL) { 1376 syslog(LOG_ERR, "Couldn't locate directory object for %s", 1377 argp->ibr_name); 1378 res->status = NIS_NOT_ME; /* XXX should we use 'xx'? */ 1379 return (res); 1380 } 1381 1382 /* 1383 * Get the last update time. If the current time is earlier 1384 * someone has set the clock back which is very bad as far 1385 * as NIS+ is concerned. This will catch almost all possible 1386 * situations except the single second when time has finally 1387 * caught up with the last entry in the log. Fixing this 1388 * would require more radical changes to the whole update 1389 * mechanism which is potentially risky and may even require 1390 * protocol changes. 1391 */ 1392 if (last_update(d_obj->DI_data.do_name) > time(0)) { 1393 syslog(LOG_ERR, "Update rejected because system time is " 1394 "earlier than most recent log entry"); 1395 res->status = NIS_SYSTEMERROR; 1396 return (res); 1397 } 1398 1399 /* 1400 * POLICY : Should we have to read the directory to 1401 * read the table ? 1402 * ANSWER : No, if we _know_ the name of the table we 1403 * should be able to access it. 1404 */ 1405 1406 /* quick pointer to the "new" object */ 1407 if (argp->ibr_obj.ibr_obj_len) 1408 n_obj = argp->ibr_obj.ibr_obj_val; 1409 else 1410 n_obj = NULL; 1411 1412 /* Get the table that we will be manipulating. */ 1413 table = db_lookup(argp->ibr_name); 1414 res->dticks += table->ticks; 1415 if (table->status != NIS_SUCCESS) { 1416 res->status = table->status; 1417 return (res); 1418 } 1419 1420 t_obj = table->obj; 1421 if (__type_of(t_obj) != NIS_TABLE_OBJ) { 1422 res->status = NIS_INVALIDOBJ; 1423 return (res); 1424 } 1425 1426 if (! __search_ok(argp->ibr_srch.ibr_srch_len, 1427 argp->ibr_srch.ibr_srch_val, t_obj)) { 1428 res->status = NIS_BADATTRIBUTE; 1429 return (res); 1430 } 1431 1432 /* For ADD/MODify Make sure the types are the same */ 1433 if ((op == MOD_OP) || (op == ADD_OP)) { 1434 if (! n_obj) { 1435 res->status = NIS_BADOBJECT; 1436 return (res); 1437 } 1438 } 1439 1440 /* 1441 * For all operations, if there is an object, check that 1442 * it matches the table. 1443 */ 1444 if (n_obj) { 1445 if (strcasecmp(n_obj->EN_data.en_type, 1446 t_obj->TA_data.ta_type) != 0) { 1447 res->status = NIS_TYPEMISMATCH; 1448 return (res); 1449 } 1450 1451 /* Make sure the number of columns are the same */ 1452 if (n_obj->EN_data.en_cols.en_cols_len != 1453 t_obj->TA_data.ta_maxcol) { 1454 res->status = NIS_TYPEMISMATCH; 1455 return (res); 1456 } 1457 } 1458 1459 /* 1460 * Now we check the attribute list, doing some sanity checking 1461 * and then go to the database. 1462 */ 1463 1464 tc = t_obj->TA_data.ta_cols.ta_cols_val; /* The Table columns */ 1465 mc = t_obj->TA_data.ta_maxcol; /* The total columns */ 1466 if (n_obj) /* The Entry columns */ 1467 ec = n_obj->EN_data.en_cols.en_cols_val; 1468 else 1469 ec = NULL; 1470 1471 if (argp->ibr_srch.ibr_srch_len) { 1472 za = argp->ibr_srch.ibr_srch_val; /* The AVA list */ 1473 zn = argp->ibr_srch.ibr_srch_len; /* The current AVA */ 1474 } else if (ec) { 1475 /* build a search criteria from the passed entry */ 1476 za = (nis_attr *)nis_get_static_storage(&buf, sizeof (nis_attr), 1477 mc); 1478 for (i = 0, zn = 0; i < mc; i++) { 1479 if ((tc[i].tc_flags & TA_SEARCHABLE) && 1480 (ec[i].ec_value.ec_value_val)) { 1481 za[zn].zattr_ndx = tc[i].tc_name; 1482 za[zn].ZAVAL = ec[i].ENVAL; 1483 za[zn].ZALEN = ec[i].ENLEN; 1484 zn++; 1485 } 1486 } 1487 } else { 1488 za = NULL; 1489 zn = 0; 1490 } 1491 1492 xid = 0; 1493 1494 switch (op) { 1495 1496 case ADD_OP : 1497 add_entry(argp->ibr_name, zn, za, n_obj, t_obj, 1498 argp->ibr_flags, princp, &xid, 1499 res); 1500 break; 1501 case REM_OP : 1502 remove_entry(argp->ibr_name, zn, za, n_obj, t_obj, 1503 argp->ibr_flags, princp, &xid, 1504 res); 1505 break; 1506 case MOD_OP : 1507 modify_entry(argp->ibr_name, zn, za, n_obj, t_obj, 1508 argp->ibr_flags, princp, &xid, 1509 res); 1510 break; 1511 } 1512 1513 if (verbose) 1514 syslog(LOG_INFO, "ibops: operation completed."); 1515 if (res->status == NIS_SUCCESS) { 1516 end_transaction(xid); 1517 ttime = last_update(d_obj->DI_data.do_name); 1518 if (d_obj->DI_data.do_servers.do_servers_len > 1) { 1519 RLOCK(ping_list); 1520 add_pingitem(d_obj, ttime, &ping_list); 1521 RULOCK(ping_list); 1522 } 1523 } else if (xid != 0) 1524 abort_transaction(xid); 1525 1526 if (verbose) 1527 syslog(LOG_INFO, "ibops: returning..."); 1528 return (res); 1529 } 1530 1531 #undef buf 1532 1533 /* 1534 * nis_fnops() 1535 * This is routine is common to the first and next functions below. Quite 1536 * a bit of code is shared so this cuts down on the chance for errors. 1537 * The first part is identical to the ibops function above but this 1538 * routine needs to return different data so I didn't want to overload 1539 * the return value of ibobs() above. 1540 */ 1541 static nis_result * 1542 __nis_fnops(op, argp, princp) 1543 int op; 1544 ib_request *argp; 1545 nis_name princp; 1546 { 1547 nis_object *r_obj, *t_obj, *ib_obj = NULL; 1548 nis_fn_result *fnr; 1549 table_col *tc; 1550 int all_read = 0; 1551 nis_result *res; 1552 int zn; 1553 nis_attr *za; 1554 1555 res = (nis_result *)XCALLOC(1, sizeof (nis_result)); 1556 if (! res) { 1557 syslog(LOG_ERR, "rpc.nisd: Out of memory"); 1558 return (NULL); 1559 } 1560 add_cleanup((void (*)())XFREE, (char *)res, "fnops result"); 1561 1562 ib_obj = __nis_listinit(res, argp, princp); 1563 1564 if (! ib_obj) { 1565 res->zticks = __stop_clock(0); 1566 return (res); 1567 } 1568 1569 all_read = __can_do(NIS_READ_ACC, ib_obj->zo_access, ib_obj, princp); 1570 if (op == FIRST_OP) 1571 fnr = db_firstib(argp->ibr_name, argp->ibr_srch.ibr_srch_len, 1572 argp->ibr_srch.ibr_srch_val, FN_MANGLE+FN_NORAGS, NULL); 1573 else if (op == NEXT_OP) { 1574 res->cookie.n_len = argp->ibr_cookie.n_len; 1575 res->cookie.n_bytes = (char *)malloc(res->cookie.n_len); 1576 memcpy(res->cookie.n_bytes, 1577 argp->ibr_cookie.n_bytes, res->cookie.n_len); 1578 fnr = db_nextib(argp->ibr_name, &(res->cookie), 1579 FN_MANGLE+FN_NORAGS, NULL); 1580 } 1581 1582 res->dticks += fnr->ticks; 1583 if (fnr->status != NIS_SUCCESS) { 1584 res->status = fnr->status; 1585 XFREE(fnr); 1586 return (res); 1587 } 1588 1589 res->cookie = fnr->cookie; 1590 res->status = fnr->status; 1591 /* 1592 * If we have read access to everything or the object then we're done. 1593 */ 1594 if (all_read || 1595 __can_do(NIS_READ_ACC, fnr->obj->zo_access, fnr->obj, princp)) { 1596 res->objects.objects_val = fnr->obj; 1597 res->objects.objects_len = 1; 1598 add_cleanup(nis_destroy_object, (char *)fnr->obj, "fnops obj"); 1599 add_cleanup((void (*)())XFREE, (char *)fnr->cookie.n_bytes, 1600 "fnops descript"); 1601 XFREE(fnr); 1602 return (res); 1603 } 1604 1605 r_obj = fnr->obj; 1606 XFREE(fnr); /* free the result structure */ 1607 if (__type_of(ib_obj) == NIS_TABLE_OBJ) 1608 tc = ib_obj->TA_data.ta_cols.ta_cols_val; 1609 else 1610 tc = tbl_prototype.ta_cols.ta_cols_val; 1611 do { 1612 if (__can_do(NIS_READ_ACC, r_obj->zo_access, r_obj, princp)) 1613 t_obj = r_obj; 1614 else { 1615 zn = argp->ibr_srch.ibr_srch_len; 1616 za = argp->ibr_srch.ibr_srch_val; 1617 t_obj = nis_censor_object_attr(r_obj, tc, princp, 1618 zn, za); 1619 nis_destroy_object(r_obj); 1620 } 1621 if (t_obj) { 1622 res->objects.objects_val = t_obj; 1623 res->objects.objects_len = 1; 1624 add_cleanup(nis_destroy_object, (char *)t_obj, 1625 "fnops obj"); 1626 add_cleanup((void (*)())XFREE, 1627 (char *)res->cookie.n_bytes, 1628 "fnops descript"); 1629 return (res); 1630 } 1631 1632 fnr = db_nextib(argp->ibr_name, &(res->cookie), 1633 FN_MANGLE+FN_NORAGS, NULL); 1634 res->dticks += fnr->ticks; 1635 res->status = fnr->status; 1636 if (res->status == NIS_SUCCESS) 1637 res->cookie = fnr->cookie; 1638 r_obj = fnr->obj; 1639 XFREE(fnr); 1640 } while (res->status == NIS_SUCCESS); 1641 1642 return (res); 1643 } 1644 1645 /* 1646 * nis_addib, this function adds an entry into an Information Base. 1647 * the entry is not "visible" in the namespace, only as a component 1648 * of the information base that contains it. 1649 */ 1650 nis_result * 1651 nis_ibadd_svc(argp, reqstp) 1652 ib_request *argp; 1653 struct svc_req *reqstp; 1654 { 1655 nis_result *res; 1656 char pname[1024]; 1657 1658 __start_clock(0); 1659 if (verbose) 1660 syslog(LOG_INFO, "Entry ADD_SVC: to table %s", argp->ibr_name); 1661 /* A quick and easy check... */ 1662 if ((argp->ibr_obj.ibr_obj_len == 1) && 1663 (__type_of(argp->ibr_obj.ibr_obj_val) != NIS_ENTRY_OBJ)) { 1664 return (nis_make_error(NIS_INVALIDOBJ, 0, 0, 0, 1665 __stop_clock(0))); 1666 } 1667 nis_getprincipal(pname, reqstp); 1668 res = __nis_ibops(ADD_OP, argp, pname); 1669 if (verbose) 1670 syslog(LOG_INFO, "Done, exit status %s", 1671 nis_sperrno(res->status)); 1672 res->zticks += __stop_clock(0); 1673 return (res); 1674 } 1675 1676 /* 1677 * nis_ibmodify, this function modifys an entry in the information 1678 * base by changing the columns that are marked as modified in the 1679 * passed entry. Permission checking is done here as well. 1680 */ 1681 nis_result * 1682 nis_ibmodify_svc(argp, reqstp) 1683 ib_request *argp; 1684 struct svc_req *reqstp; 1685 { 1686 char pname[1024]; 1687 nis_result *res; 1688 1689 __start_clock(0); 1690 if (verbose) 1691 syslog(LOG_INFO, "Entry MODIFY_SVC: to table %s", 1692 argp->ibr_name); 1693 /* A quick and easy check... */ 1694 if ((argp->ibr_obj.ibr_obj_len == 1) && 1695 (__type_of(argp->ibr_obj.ibr_obj_val) != NIS_ENTRY_OBJ)) { 1696 return (nis_make_error(NIS_INVALIDOBJ, 0, 0, 0, 1697 __stop_clock(0))); 1698 } 1699 nis_getprincipal(pname, reqstp); 1700 res = __nis_ibops(MOD_OP, argp, pname); 1701 if (verbose) 1702 syslog(LOG_INFO, "Done, exit status %s", 1703 nis_sperrno(res->status)); 1704 res->zticks += __stop_clock(0); 1705 return (res); 1706 } 1707 1708 /* 1709 * nis_remove, delete an entry from the indicated information base. 1710 */ 1711 nis_result * 1712 nis_ibremove_svc(argp, reqstp) 1713 ib_request *argp; 1714 struct svc_req *reqstp; 1715 { 1716 char pname[1024]; 1717 nis_result *res; 1718 1719 __start_clock(0); 1720 if (verbose) 1721 syslog(LOG_INFO, "Entry REMOVE_SVC: to table %s", 1722 argp->ibr_name); 1723 /* A quick and easy check... */ 1724 if ((argp->ibr_obj.ibr_obj_len == 1) && 1725 (__type_of(argp->ibr_obj.ibr_obj_val) != NIS_ENTRY_OBJ)) { 1726 return (nis_make_error(NIS_INVALIDOBJ, 0, 0, 0, 1727 __stop_clock(0))); 1728 } 1729 nis_getprincipal(pname, reqstp); 1730 res = __nis_ibops(REM_OP, argp, pname); 1731 if (verbose) 1732 syslog(LOG_INFO, "Done, exit status %s", 1733 nis_sperrno(res->status)); 1734 res->zticks += __stop_clock(0); 1735 return (res); 1736 } 1737 1738 /* 1739 * nis_ibfirst() 1740 * This function will return the first entry in an information base. 1741 * It is provided primarily for backward compatibility with YP and its 1742 * use is not encouraged. 1743 */ 1744 nis_result * 1745 nis_ibfirst_svc(argp, reqstp) 1746 ib_request *argp; 1747 struct svc_req *reqstp; 1748 { 1749 char pname[1024]; 1750 nis_result *res; 1751 1752 1753 __start_clock(0); 1754 if (verbose) 1755 syslog(LOG_INFO, "Entry FIRST_SVC : Fetch from table %s", 1756 argp->ibr_name); 1757 nis_getprincipal(pname, reqstp); 1758 res = __nis_fnops(FIRST_OP, argp, pname); 1759 if (verbose) 1760 syslog(LOG_INFO, "Done, exit status %s", 1761 nis_sperrno(res->status)); 1762 res->zticks += __stop_clock(0); 1763 return (res); 1764 } 1765 1766 /* 1767 * nis_ibnext, return a subsequent entry in the information base. 1768 */ 1769 nis_result * 1770 nis_ibnext_svc(argp, reqstp) 1771 ib_request *argp; 1772 struct svc_req *reqstp; 1773 { 1774 char pname[1024]; 1775 nis_result *res; 1776 1777 __start_clock(0); 1778 if (verbose) 1779 syslog(LOG_INFO, "Entry NEXT_SVC : Fetch from table %s", 1780 argp->ibr_name); 1781 nis_getprincipal(pname, reqstp); 1782 res = __nis_fnops(NEXT_OP, argp, pname); 1783 if (verbose) 1784 syslog(LOG_INFO, "Done, exit status %s", 1785 nis_sperrno(res->status)); 1786 res->zticks += __stop_clock(0); 1787 return (res); 1788 } 1789