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 (c) 1990-2001 by Sun Microsystems, Inc. 24 * All rights reserved. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * Ported from : 31 * "@(#)nis_ns_proc.c 1.15 91/03/01 Copyr 1990 Sun Micro"; 32 * 33 * nis_ns_proc.c 34 * 35 * This module contains the actual implementation of NIS version 3. 36 * NB : It provides the routines that the dispatch function in nis_svc.c 37 * call. That file, nis_svc.c, is automatically generated and reflects the 38 * interface definition that is described in the nis.x file. When the 39 * nis.x file changes, you must make sure that any parameters that change 40 * get reflected in these routines. 41 * 42 * This module contains the Namespace manipulation procedures. 43 */ 44 45 #include <stdio.h> 46 #include <syslog.h> 47 #include <stdlib.h> 48 #include <rpc/rpc.h> 49 #include <rpc/clnt.h> 50 #include <rpc/svc.h> 51 #include <rpc/auth.h> 52 #include <rpc/auth_des.h> 53 #include <rpcsvc/nis.h> 54 #include "nis_proc.h" 55 56 extern bool_t xdr_nis_result(); 57 58 extern nis_object* get_root_object(); 59 extern int update_root_object(nis_name, nis_object *); 60 extern void add_pingitem_with_name(char *buf, nis_object *dir, 61 u_long ptime, NIS_HASH_TABLE *tbl); 62 extern nis_name __nis_local_root(); 63 extern char *relative_name(); 64 65 /* 66 * Handle the case in which we have the directory object but not its table. 67 * It is called when we try to lookup an item and get back a NIS_NOSUCHTABLE. 68 * On master, table should always exist; missing due to a mkdir failure 69 * master: create table & return (item) not found 70 * master & readonly: return system error 71 * On replica: indicates we have not seen directory's update yet. 72 * replica: add to pinglist and return NIS_NOT_ME 73 * replica & readonly: return NIS_NOT_ME 74 */ 75 nis_error 76 recover_from_no_table(nis_object* d_obj, nis_name dirname) 77 { 78 if (we_serve(&(d_obj->DI_data), MASTER_ONLY)) { 79 if (readonly) 80 return (NIS_TRYAGAIN); 81 else { 82 /* create table */ 83 nis_error status; 84 85 if ((status = db_create(dirname, &tbl_prototype)) != 86 NIS_SUCCESS) { 87 syslog(LOG_ERR, 88 "Unable to create table %s: %s.", 89 dirname, nis_sperrno(status)); 90 return (status); 91 } else 92 return (NIS_SUCCESS); 93 } 94 } else { 95 /* replica */ 96 if (!readonly) /* force update */ 97 add_pingitem(d_obj, time(0), &upd_list); 98 return (NIS_NOT_ME); 99 } 100 } 101 102 /* 103 * nis_lookup is the basic function used by clients of the name service. 104 * This function translates the request into an access of the local 105 * database. 106 */ 107 nis_result * 108 nis_lookup_svc(argp, reqstp) 109 ns_request *argp; 110 struct svc_req *reqstp; 111 { 112 nis_result *res; 113 nis_db_result *dbres; 114 nis_object *d_obj = 0; 115 char pname[1024]; 116 struct ticks t; 117 int read_dir, /* read access on directory */ 118 read_obj; /* read access on an object */ 119 nis_error xx; 120 char *p; 121 122 if (verbose) 123 syslog(LOG_INFO, "LOOKUP_SVC : '%s'", argp->ns_name); 124 __start_clock(0); 125 res = (nis_result *)XCALLOC(1, sizeof (nis_result)); 126 add_cleanup((void (*)()) XFREE, (char *)(res), "ns_lookup result"); 127 128 if (reqstp) 129 nis_getprincipal(pname, reqstp); 130 else 131 pname[0] = '\0'; 132 133 if ((p = relative_name(argp->ns_name)) == NULL) { 134 int want_root; 135 nis_name root_dir = __nis_local_root(); 136 137 want_root = root_dir && 138 (nis_dir_cmp(argp->ns_name, root_dir) == SAME_NAME); 139 if (!want_root) 140 res->status = NIS_NOT_ME; 141 else { 142 if (! root_server) { 143 /* maybe we have been added */ 144 xx = __directory_object(argp->ns_name, 145 &t, 0, &d_obj); 146 res->aticks = t.aticks; 147 res->dticks = t.dticks; 148 res->zticks = t.zticks; 149 res->cticks = t.cticks; 150 if (d_obj == NULL) 151 res->status = NIS_NOT_ME; 152 else { 153 /* make copy to be returned */ 154 nis_object* clone; 155 156 clone = nis_clone_object(d_obj, 0); 157 d_obj = clone; 158 } 159 } else { 160 d_obj = get_root_object(); 161 if (d_obj == NULL) 162 res->status = NIS_NAMEUNREACHABLE; 163 } 164 165 if (d_obj) { 166 read_dir = __can_do(NIS_READ_ACC, 167 d_obj->zo_access, 168 d_obj, pname); 169 if (! read_dir) { 170 res->status = NIS_PERMISSION; 171 nis_destroy_object(d_obj); 172 } else { 173 NIS_RES_OBJECT(res) = d_obj; 174 NIS_RES_NUMOBJ(res) = 1; 175 res->status = NIS_SUCCESS; 176 add_cleanup(nis_destroy_object, 177 (char *)(d_obj), 178 "ns_lookup objects"); 179 } 180 } 181 } 182 res->zticks = __stop_clock(0); 183 return (res); 184 } 185 free((void *)p); 186 187 /* 188 * Make sure we are allowed to READ this directory. 189 * 190 * POLICY: servers _must_ be able to read their own directory 191 * objects. 192 */ 193 xx = __directory_object(nis_domain_of(argp->ns_name), &t, 0, &d_obj); 194 res->aticks = t.aticks; 195 res->dticks = t.dticks; 196 res->zticks = t.zticks; 197 res->cticks = t.cticks; 198 if (d_obj == NULL) { 199 res->status = xx; /* status from __directory_object call */ 200 res->zticks = __stop_clock(0); 201 return (res); 202 } 203 204 read_dir = __can_do(NIS_READ_ACC, d_obj->zo_access, d_obj, pname); 205 206 /* Look it up in the data base */ 207 dbres = db_lookup(argp->ns_name); 208 if (dbres->status != NIS_SUCCESS) { 209 if (dbres->status == NIS_NOSUCHTABLE) { 210 /* This means table of domain does not exist */ 211 dbres->status = 212 recover_from_no_table(d_obj, 213 nis_domain_of(argp->ns_name)); 214 /* for sure, argp->ns_name will not be there */ 215 if (dbres->status == NIS_SUCCESS) 216 dbres->status = NIS_NOTFOUND; 217 } 218 if (read_dir) { 219 res->status = dbres->status; 220 res->dticks = dbres->ticks; 221 } else { 222 res->status = NIS_PERMISSION; 223 res->dticks = 0; 224 } 225 res->zticks = __stop_clock(0); 226 if (verbose) 227 syslog(LOG_INFO, "nis_lookup : exit status %s", 228 nis_sperrno(res->status)); 229 return (res); 230 } 231 232 read_obj = __can_do(NIS_READ_ACC, dbres->obj->zo_access, 233 dbres->obj, pname); 234 235 if (! read_dir && ! read_obj) { 236 res->status = NIS_PERMISSION; 237 res->zticks = __stop_clock(0); 238 if (verbose) 239 syslog(LOG_INFO, 240 "nis_lookup : exit status PERMISSION DENIED"); 241 return (res); 242 } 243 244 res->status = NIS_SUCCESS; 245 res->objects.objects_len = 1; 246 res->objects.objects_val = dbres->obj; 247 res->dticks = dbres->ticks; 248 res->zticks = __stop_clock(0); 249 250 if (verbose) 251 syslog(LOG_INFO, "nis_lookup : exit status %s", 252 nis_sperrno(res->status)); 253 254 return (res); 255 } 256 257 /* 258 * Internal function that adds an object into the namespace. 259 */ 260 static void 261 add_object(name, obj, dobj, princp, res) 262 nis_name name; 263 nis_object *obj, 264 *dobj; 265 nis_name princp; 266 nis_result *res; 267 { 268 directory_obj *d; 269 nis_db_result *dbres; 270 u_long xid, ttime; 271 int i; 272 int add_ok = 0; 273 274 /* 275 * d points at the directory specific information 276 */ 277 d = &(dobj->DI_data); 278 279 dbres = db_lookup(name); 280 res->dticks += dbres->ticks; 281 if (dbres->status != NIS_NOTFOUND) { 282 if (dbres->status == NIS_SUCCESS) 283 res->status = NIS_NAMEEXISTS; 284 else 285 res->status = dbres->status; 286 return; 287 } 288 289 /* 290 * If we are adding a directory, make sure that it is a legitimate 291 * name, i.e. the new directory must be a descendant of the domain 292 * whose server is making this request. If we are in the root domain, 293 * then we can add a new directory object in the same domain. 294 */ 295 if (__type_of(obj) == NIS_DIRECTORY_OBJ) { 296 switch (nis_dir_cmp(obj->DI_data.do_name, dobj->DI_data.do_name)) { 297 case LOWER_NAME: 298 break; 299 case SAME_NAME: 300 if (root_server) break; 301 default: 302 res->status = NIS_BADNAME; 303 return; 304 } 305 } 306 307 /* 308 * Verify access rights. Check first to see if the directory object 309 * grants the right. If not, then check to see if the object access 310 * rights for that type of object grant the right. And if that 311 * doesn't then abort with PERMISSION denied. 312 */ 313 add_ok = __can_do(NIS_CREATE_ACC, dobj->zo_access, dobj, princp); 314 if (! add_ok) { 315 if (auth_verbose) { 316 syslog(LOG_INFO, 317 "add_object : create DENIED by directory %s.%s", 318 dobj->zo_name, dobj->zo_domain); 319 } 320 for (i = 0; i < d->do_armask.do_armask_len; i++) { 321 if (obj->zo_data.zo_type == OATYPE(d, i)) { 322 add_ok = __can_do(NIS_CREATE_ACC, 323 OARIGHTS(d, i), dobj, princp); 324 if (auth_verbose) { 325 syslog(LOG_INFO, 326 "add_object : create %s by object type.", 327 (add_ok) ? "ALLOWED" : "DENIED"); 328 } 329 break; 330 } 331 } 332 } else if (auth_verbose) { 333 syslog(LOG_INFO, 334 "add_object : create is ALLOWED by the directory."); 335 } 336 337 if (! add_ok) { 338 res->status = NIS_PERMISSION; 339 return; 340 } 341 342 /* 343 * Start the transaction, if we can't get an XID abort 344 */ 345 xid = begin_transaction(princp); 346 if (! xid) { 347 res->status = NIS_TRYAGAIN; 348 return; 349 } 350 351 /* Do the add operation */ 352 obj->zo_oid.ctime = (unsigned long)time(0); 353 obj->zo_oid.mtime = obj->zo_oid.ctime; 354 dbres = db_add(name, obj, 0); 355 res->dticks += dbres->ticks; 356 res->status = dbres->status; 357 if (res->status != NIS_SUCCESS) 358 abort_transaction(xid); 359 else { 360 ttime = (u_long)(time(0)); 361 end_transaction(xid); /* COMMIT */ 362 /* Notify replicates of the change. */ 363 if (dobj->DI_data.do_servers.do_servers_len > 1) 364 add_pingitem(dobj, ttime, &ping_list); 365 } 366 return; 367 368 } 369 370 /* 371 * Internal function that removes an object from the namespace. 372 */ 373 static void 374 remove_object(name, obj, dobj, princp, res) 375 nis_name name; 376 nis_object *obj, 377 *dobj; 378 nis_name princp; 379 nis_result *res; 380 { 381 directory_obj *d; 382 nis_object *old; 383 nis_db_result *dbres; 384 u_long xid, ttime; 385 int i, rem_ok; 386 387 /* 388 * d points at the directory specific information 389 */ 390 d = &(dobj->DI_data); 391 392 dbres = db_lookup(name); 393 res->dticks += dbres->ticks; 394 if (dbres->status != NIS_SUCCESS) { 395 res->status = dbres->status; 396 return; 397 } 398 399 /* This is the existing object in the database */ 400 old = dbres->obj; 401 402 if (obj && ! same_oid(obj, old)) { 403 res->status = NIS_NOTSAMEOBJ; 404 return; 405 } 406 407 /* 408 * Verify delete access rights. Check first to see if the directory 409 * object grants the right. If not, then check to see if the object 410 * itself grants the right, then finally check to see of we have 411 * this access right for this type of object. 412 */ 413 rem_ok = __can_do(NIS_DESTROY_ACC, dobj->zo_access, dobj, princp); 414 if (auth_verbose) { 415 syslog(LOG_INFO, 416 "remove_object : destroy %s by directory %s.%s", 417 (rem_ok) ? "ALLOWED" : "DENIED", 418 dobj->zo_name, dobj->zo_domain); 419 } 420 if (! rem_ok) { 421 rem_ok = __can_do(NIS_DESTROY_ACC, old->zo_access, old, princp); 422 if (auth_verbose) { 423 syslog(LOG_INFO, 424 "remove_object : destroy %s by object %s.%s", 425 (rem_ok) ? "ALLOWED" : "DENIED", 426 old->zo_name, old->zo_domain); 427 } 428 } 429 430 if (! rem_ok) { 431 for (i = 0; i < d->do_armask.do_armask_len; i++) { 432 if (old->zo_data.zo_type == OATYPE(d, i)) { 433 rem_ok = __can_do(NIS_DESTROY_ACC, 434 OARIGHTS(d, i), dobj, princp); 435 break; 436 } 437 } 438 } 439 if (! rem_ok) { 440 res->status = NIS_PERMISSION; 441 if (auth_verbose) { 442 syslog(LOG_INFO, 443 "remove_object : destroy DENIED by object type."); 444 } 445 return; 446 } else if (auth_verbose) { 447 syslog(LOG_INFO, 448 "remove_object : destroy ALLOWED by object type."); 449 } 450 451 /* 452 * Start the transaction, if we can't get an XID abort 453 */ 454 xid = begin_transaction(princp); 455 if (! xid) { 456 res->status = NIS_TRYAGAIN; 457 return; 458 } 459 460 /* Do the remove operation */ 461 dbres = db_remove(name, old, time(0)); 462 res->dticks += dbres->ticks; 463 res->status = dbres->status; 464 if (res->status != NIS_SUCCESS) 465 abort_transaction(xid); 466 else { 467 ttime = (u_long)(time(0)); 468 end_transaction(xid); /* COMMIT */ 469 /* Notify replicates of the change. */ 470 if (dobj->DI_data.do_servers.do_servers_len > 1) 471 add_pingitem(dobj, ttime, &ping_list); 472 /* 473 * No need to flush the caches - they have been flushed 474 * inside db_remove 475 */ 476 } 477 } 478 479 /* macro to get to the table column structures */ 480 #define TABLE_COL(o, n) o->TA_data.ta_cols.ta_cols_val[n] 481 482 /* 483 * Internal function that modifies an object in the namespace. 484 */ 485 static void 486 modify_object(name, obj, dobj, princp, res) 487 nis_name name; 488 nis_object *obj, 489 *dobj; 490 nis_name princp; 491 nis_result *res; 492 { 493 directory_obj *d; 494 nis_object *old; 495 nis_db_result *dbres; 496 u_long xid, ttime; 497 int i, mod_ok; 498 log_entry le; 499 nis_object mod_obj; 500 501 /* 502 * d points at the directory specific information 503 */ 504 d = &(dobj->DI_data); 505 506 dbres = db_lookup(name); 507 res->dticks += dbres->ticks; 508 if (dbres->status != NIS_SUCCESS) { 509 res->status = dbres->status; 510 return; 511 } 512 513 /* This is the existing object in the database */ 514 old = dbres->obj; 515 516 /* 517 * Verify modify access rights. Check first to see if the directory 518 * object grants the right. If not, then check to see if the object 519 * itself grants the right, then finally check to see if we have 520 * this access right for this type of object. 521 */ 522 mod_ok = __can_do(NIS_MODIFY_ACC, dobj->zo_access, dobj, princp); 523 if (auth_verbose) { 524 syslog(LOG_INFO, 525 "modify_object : modify %s by directory %s.%s", 526 (mod_ok) ? "ALLOWED" : "DENIED", 527 dobj->zo_name, dobj->zo_domain); 528 } 529 if (! mod_ok) { 530 mod_ok = __can_do(NIS_MODIFY_ACC, old->zo_access, old, princp); 531 if (auth_verbose) { 532 syslog(LOG_INFO, 533 "remove_object : modify %s by object %s.%s", 534 (mod_ok) ? "ALLOWED" : "DENIED", 535 old->zo_name, old->zo_domain); 536 } 537 } 538 539 if (! mod_ok) { 540 for (i = 0; i < d->do_armask.do_armask_len; i++) { 541 if (old->zo_data.zo_type == OATYPE(d, i)) { 542 if (! __can_do(NIS_MODIFY_ACC, OARIGHTS(d, i), 543 dobj, princp)) { 544 res->status = NIS_PERMISSION; 545 if (auth_verbose) { 546 syslog(LOG_INFO, 547 "modify_object : modify DENIED by object type."); 548 } 549 return; 550 } else { 551 if (auth_verbose) { 552 syslog(LOG_INFO, 553 "modify_object : modify ALLOWED by object type."); 554 } 555 mod_ok = 1; 556 break; 557 } 558 } 559 } 560 } 561 /* 562 * if no one allows modify then fail. 563 */ 564 if (! mod_ok) { 565 res->status = NIS_PERMISSION; 566 return; 567 } 568 569 /* 570 * POLICY : Allow changing type ? 571 * 572 * ANSWER : No, only changing the meta data 573 * such as access etc is allowed. 574 */ 575 if (__type_of(old) != __type_of(obj)) { 576 res->status = NIS_BADOBJECT; 577 return; 578 } 579 580 if (nis_dir_cmp(old->zo_domain, obj->zo_domain) != SAME_NAME) { 581 res->status = NIS_BADNAME; 582 return; 583 } 584 585 /* 586 * Start the transaction, if we can't get an XID abort 587 */ 588 xid = begin_transaction(princp); 589 if (! xid) { 590 res->status = NIS_TRYAGAIN; 591 return; 592 } 593 594 /* 595 * Now we generate a log entry with the old value so that if 596 * we crash, the transaction system can restore the object 597 * to its pre-modified state. 598 */ 599 memset((char *)&le, 0, sizeof (le)); 600 le.le_type = MOD_NAME_OLD; 601 le.le_time = (u_long) (time(0)); 602 le.le_name = name; 603 le.le_attrs.le_attrs_len = 0; 604 le.le_object = *old; 605 add_update(&le); 606 607 mod_obj = *old; 608 if (same_oid(old, obj)) { 609 mod_obj.zo_owner = obj->zo_owner; 610 mod_obj.zo_group = obj->zo_group; 611 mod_obj.zo_access = obj->zo_access; 612 mod_obj.zo_ttl = obj->zo_ttl; 613 } else if (obj->zo_oid.mtime || obj->zo_oid.ctime) { 614 res->status = NIS_NOTSAMEOBJ; 615 abort_transaction(xid); 616 return; 617 } 618 619 /* 620 * Data replacement of the variant part. 621 * NOTE: We play games with TABLE objects because we can't 622 * allow their schema to change on the fly. But there are 623 * fields in the table data that _can_ change such as the 624 * path of the table, separator character, and access rights. 625 * 626 * As in the case of ENTRY objects, we let the 'same oid' test be 627 * the signal that it is ok to overwrite these variables. This 628 * is overloading the OID value but I can't think of any other 629 * way to signal this operation without changing the protocol at 630 * this point. 631 */ 632 if (__type_of(old) != NIS_TABLE_OBJ) 633 mod_obj.zo_data = obj->zo_data; 634 else if (same_oid(old, obj)) { 635 mod_obj.TA_data.ta_path = obj->TA_data.ta_path; 636 mod_obj.TA_data.ta_sep = obj->TA_data.ta_sep; 637 mod_obj.TA_data.ta_type = obj->TA_data.ta_type; 638 for (i = 0; i < mod_obj.TA_data.ta_maxcol; i++) { 639 if (TABLE_COL(obj, i).tc_flags & TA_MODIFIED) 640 TABLE_COL((&mod_obj), i).tc_rights = 641 TABLE_COL(obj, i).tc_rights; 642 } 643 } 644 645 mod_obj.zo_oid.mtime = (unsigned long)time(0); 646 /* 647 * Now we add the object over the previous 648 * one. This instructs the database to 649 * discard the current one and replace it 650 * with our modified one. (And it's atomic) 651 */ 652 dbres = db_add(name, &mod_obj, 1); 653 res->dticks += dbres->ticks; 654 res->status = dbres->status; 655 if (res->status != NIS_SUCCESS) 656 abort_transaction(xid); 657 else { 658 nis_taglist taglist; 659 nis_tag tags; 660 661 ttime = (u_long)(time(0)); 662 end_transaction(xid); /* COMMIT */ 663 664 /* Notify replicates of the change. */ 665 if (dobj->DI_data.do_servers.do_servers_len > 1) 666 add_pingitem(dobj, ttime, &ping_list); 667 668 /* 669 * No need to flush the caches - they have been flushed 670 * inside db_remove 671 */ 672 if (__type_of(old) == NIS_DIRECTORY_OBJ) { 673 /* 674 * Send all the servers that serve this particular 675 * directory object a flush_cache message. 676 * We dont do this for group and table caches because 677 * they are served by those servers; and we can 678 * just hope that they get the updates real fast. 679 * For Directory objects, since we dont want the servers 680 * to throw out the cached directory objects and then go 681 * and refresh it with the stale copies, we will use 682 * TAG_DCACHE_ONE_REFRESH which will 683 * get a fresh copy of the object from the master. 684 * 685 * XXX: Since we are sending this message to all 686 * servers (including MASTER), this will lead to 687 * multiple calls to the master. One could have 688 * perhaps passed a modified directory object 689 * without the master. 690 */ 691 tags.tag_type = TAG_DCACHE_ONE_REFRESH; 692 tags.tag_val = old->DI_data.do_name; 693 taglist.tags.tags_len = 1; 694 taglist.tags.tags_val = &tags; 695 (void) nis_mcast_tags(&old->DI_data, &taglist); 696 } 697 } 698 } 699 700 /* 701 * If prim and sec contain the same servers, return 0. 702 * If sec contains different servers than prim, result is set to: 703 * <all prim servers> <sec servers that are not in prim's list> 704 */ 705 static int 706 merge_srv_list(directory_obj* prim, directory_obj* sec, nis_server**result) 707 { 708 nis_server* answer = 0; 709 int psize = prim->do_servers.do_servers_len; 710 int ssize = sec->do_servers.do_servers_len; 711 nis_server* prim_server = prim->do_servers.do_servers_val; 712 nis_server* sec_server = sec->do_servers.do_servers_val; 713 int i, j, diff = 0, newsize = 0, newadd; 714 715 /* First count how many names in sec are different */ 716 for (i = 0; i < ssize; i++) { 717 for (j = 0; j < psize; j++) { 718 if (nis_dir_cmp(prim_server[j].name, 719 sec_server[i].name) == SAME_NAME) 720 break; 721 } 722 if (j == psize) 723 ++diff; 724 } 725 726 if (diff == 0) 727 return (0); 728 729 /* different names were found on sec_server list */ 730 newsize = psize+diff; 731 answer = (nis_server*)malloc(newsize*sizeof (nis_server)); 732 733 if (answer == 0) 734 return (0); 735 736 /* Copy prim first */ 737 for (i = 0; i < psize; i++) 738 answer[i] = prim_server[i]; 739 740 newadd = i; 741 for (i = 0; i < ssize; i++) { 742 for (j = 0; j < psize; j++) { 743 if (nis_dir_cmp(prim_server[j].name, 744 sec_server[i].name) == SAME_NAME) 745 break; 746 } 747 if (j == psize) 748 answer[newadd++] = sec_server[i]; 749 } 750 751 *result = answer; 752 return (newsize); 753 } 754 755 static void 756 modify_root_object(nis_name name, 757 nis_object *obj, 758 nis_name princp, 759 nis_result *res) 760 { 761 nis_object* oldroot = get_root_object(); 762 nis_object newroot; 763 u_long ttime; 764 765 if (! oldroot) { 766 syslog(LOG_ERR, "Cannot read %s!", ROOT_OBJ); 767 res->status = NIS_SYSTEMERROR; 768 return; 769 } 770 771 if (__can_do(NIS_MODIFY_ACC, oldroot->zo_access, oldroot, princp)) { 772 newroot = *oldroot; /* copy all fields from old object */ 773 774 if (same_oid(oldroot, obj)) { 775 /* updating non-data portion as well. */ 776 newroot.zo_owner = obj->zo_owner; 777 newroot.zo_group = obj->zo_group; 778 newroot.zo_access = obj->zo_access; 779 newroot.zo_ttl = obj->zo_ttl; 780 } else if (obj->zo_oid.mtime || obj->zo_oid.ctime) { 781 /* somehow got an outdated copy of object. */ 782 res->status = NIS_NOTSAMEOBJ; 783 nis_destroy_object(oldroot); 784 return; 785 } /* else, only interested in changing data portion. */ 786 787 newroot.DI_data = obj->DI_data; /* always update data */ 788 ttime = (u_long)(time(0)); 789 newroot.zo_oid.mtime = ttime; 790 if (update_root_object(name, &newroot)) { 791 nis_server* merged_srvs = 0; 792 int howmany; 793 794 howmany = merge_srv_list(&(newroot.DI_data), 795 &(oldroot->DI_data), 796 &merged_srvs); 797 /* howmany is 0 if new and old list are the same */ 798 if (howmany > 0) { 799 directory_obj *dobj = &(newroot.DI_data); 800 dobj->do_servers.do_servers_val = merged_srvs; 801 dobj->do_servers.do_servers_len = howmany; 802 } 803 if (newroot.DI_data.do_servers.do_servers_len > 1) 804 add_pingitem_with_name(ROOT_OBJ, 805 &newroot, 806 ttime, 807 &ping_list); 808 if (merged_srvs) 809 free(merged_srvs); 810 res->status = NIS_SUCCESS; 811 } else 812 res->status = NIS_MODFAIL; 813 } else 814 res->status = NIS_PERMISSION; 815 816 /* newroot just copies contents of other objects; need not be freed */ 817 nis_destroy_object(oldroot); 818 } 819 820 /* 821 * __nis_nameops() 822 * 823 * This code actually implements the operation that is requested. 824 * It is collected here in one place. 825 */ 826 static nis_result * 827 nis_nameops(op, name, obj, princp) 828 int op; /* operation to perform */ 829 nis_name name; /* Object's name */ 830 nis_object *obj; /* Object to use (NULL for rem) */ 831 nis_name princp; /* Principal making the request */ 832 { 833 nis_result *res; 834 nis_object *d_obj; 835 struct ticks t; 836 nis_error xx; 837 char optxt[32]; 838 839 res = (nis_result *)XCALLOC(1, sizeof (nis_result)); 840 add_cleanup((void (*)())XFREE, (char *)res, "nameops result"); 841 if (readonly) { 842 res->status = NIS_TRYAGAIN; 843 return (res); 844 } 845 846 if (auth_verbose) { 847 switch (op) { 848 case ADD_OP : 849 strcpy(optxt, "ADD"); 850 break; 851 case REM_OP : 852 strcpy(optxt, "REMOVE"); 853 break; 854 case MOD_OP : 855 strcpy(optxt, "MODIFY"); 856 break; 857 } 858 syslog(LOG_INFO, "Object operation '%s' for principal %s", 859 optxt, princp); 860 } 861 862 if (((op == ADD_OP) || (op == MOD_OP)) && (obj == NULL)) { 863 res->status = NIS_BADOBJECT; 864 return (res); 865 } 866 867 /* 868 * don't allow an object to be added without an owner. 869 */ 870 if ((op == ADD_OP) && (strlen(obj->zo_owner) < 2)) { 871 res->status = NIS_BADOBJECT; 872 return (res); 873 } 874 875 /* 876 * We have to handle the root object specially if we want to 877 * manipulate it using the NIS code. 878 */ 879 if (root_server && 880 (nis_dir_cmp(name, __nis_rpc_domain()) == SAME_NAME)) { 881 882 /* All we allow is a modify */ 883 if (op != MOD_OP) { 884 res->status = NIS_BADREQUEST; 885 return (res); 886 } 887 modify_root_object(name, obj, princp, res); 888 return (res); 889 } 890 891 /* 892 * Fetch the directory object for this domain. 893 */ 894 xx = __directory_object(nis_domain_of(name), &t, TRUE, &d_obj); 895 res->aticks = t.aticks; 896 res->dticks = t.dticks; 897 res->zticks = t.zticks; 898 res->cticks = t.cticks; 899 if ((xx != NIS_SUCCESS) && (xx != NIS_S_SUCCESS)) { 900 res->status = xx; 901 return (res); 902 } 903 904 /* 905 * Get the last update time. If the current time is earlier 906 * someone has set the clock back which is very bad as far 907 * as NIS+ is concerned. This will catch almost all possible 908 * situations except the single second when time has finally 909 * caught up with the last entry in the log. Fixing this 910 * would require more radical changes to the whole update 911 * mechanism which is potentially risky and may even require 912 * protocol changes. 913 */ 914 if (last_update(d_obj->DI_data.do_name) > time(0)) { 915 syslog(LOG_ERR, "Update rejected because system time is " 916 "earlier than most recent log entry"); 917 res->status = NIS_SYSTEMERROR; 918 return (res); 919 } 920 921 switch (op) { 922 case ADD_OP : 923 add_object(name, obj, d_obj, princp, res); 924 break; 925 case REM_OP : 926 remove_object(name, obj, d_obj, princp, res); 927 break; 928 case MOD_OP : 929 modify_object(name, obj, d_obj, princp, res); 930 break; 931 } 932 933 return (res); 934 } 935 936 /* 937 * nis_add, this function will add a name to the name space if the 938 * permissions are allowed. The object that is about to be added to 939 * must allow "CREATE" access to the principal making the request. 940 */ 941 nis_result * 942 nis_add_svc(argp, reqstp) 943 ns_request *argp; 944 struct svc_req *reqstp; 945 { 946 nis_result *res; 947 char pname[1024]; 948 char *p; 949 950 __start_clock(0); /* Timing information */ 951 if (verbose) 952 syslog(LOG_INFO, "ADD_SVC : '%s'", argp->ns_name); 953 if ((p = relative_name(argp->ns_name)) == NULL) 954 return (nis_make_error(NIS_BADNAME, 0, 0, 0, __stop_clock(0))); 955 free((void *)p); 956 957 nis_getprincipal(pname, reqstp); 958 res = nis_nameops(ADD_OP, argp->ns_name, 959 argp->ns_object.ns_object_val, pname); 960 if (verbose) 961 syslog(LOG_INFO, "nis_add : exit status %s", 962 nis_sperrno(res->status)); 963 964 res->zticks += __stop_clock(0); 965 return (res); 966 } 967 968 /* 969 * nis_modify, this function modifies the contents of the object that 970 * are named. It checks the MODIFY access right in the existing object 971 * and then does the operation. 972 */ 973 nis_result * 974 nis_modify_svc(argp, reqstp) 975 ns_request *argp; 976 struct svc_req *reqstp; 977 { 978 nis_result *res; 979 char pname[1024]; 980 char *p = NULL; 981 982 __start_clock(0); 983 if (verbose) 984 syslog(LOG_INFO, "MODIFY_SVC : '%s'", argp->ns_name); 985 if (! root_server && ((p = relative_name(argp->ns_name)) == NULL)) 986 return (nis_make_error(NIS_BADNAME, 0, 0, 0, __stop_clock(0))); 987 if (p) 988 free((void *)p); 989 990 nis_getprincipal(pname, reqstp); 991 res = nis_nameops(MOD_OP, argp->ns_name, 992 argp->ns_object.ns_object_val, pname); 993 if (verbose) 994 syslog(LOG_INFO, "nis_modify : exit status %s", 995 nis_sperrno(res->status)); 996 res->zticks += __stop_clock(0); 997 return (res); 998 } 999 1000 /* 1001 * nis_remove removes and object from the name space. You will notice it 1002 * shares a lot of code with the Add and remove functions. 1003 */ 1004 nis_result * 1005 nis_remove_svc(argp, reqstp) 1006 ns_request *argp; 1007 struct svc_req *reqstp; 1008 { 1009 nis_result *res; 1010 char *principal, pname[1024]; 1011 char *p; 1012 1013 __start_clock(0); 1014 if (verbose) 1015 syslog(LOG_INFO, "REMOVE_SVC : '%s'", argp->ns_name); 1016 if ((p = relative_name(argp->ns_name)) == NULL) 1017 return (nis_make_error(NIS_BADNAME, 0, 0, 0, __stop_clock(0))); 1018 free((void *)p); 1019 1020 /* 1021 * If 'reqstp' is NULL, we're being called internally, and supply 1022 * the local principal. 1023 */ 1024 if (reqstp != 0) { 1025 nis_getprincipal(pname, reqstp); 1026 principal = pname; 1027 } else { 1028 principal = nis_local_principal(); 1029 } 1030 res = nis_nameops(REM_OP, argp->ns_name, 1031 argp->ns_object.ns_object_val, 1032 principal); 1033 if (verbose) 1034 syslog(LOG_INFO, "nis_remove : exit status %s", 1035 nis_sperrno(res->status)); 1036 res->zticks += __stop_clock(0); 1037 return (res); 1038 } 1039