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 /* 23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 /* 27 * util.c contains a set of miscellaneous utility functions which, 28 * among other things: 29 * - start a child process 30 * - look up the zone name 31 * - look up/set SMF properties 32 * - drop/escalate privs 33 */ 34 35 #include <assert.h> 36 #include <errno.h> 37 #include <libdllink.h> 38 #include <limits.h> 39 #include <libscf.h> 40 #include <net/if.h> 41 #include <pthread.h> 42 #include <pwd.h> 43 #include <spawn.h> 44 #include <stdarg.h> 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <string.h> 48 #include <strings.h> 49 #include <stropts.h> 50 #include <sys/socket.h> 51 #include <sys/sockio.h> 52 #include <sys/types.h> 53 #include <unistd.h> 54 #include <wait.h> 55 #include <zone.h> 56 57 #include "util.h" 58 #include "llp.h" 59 60 extern char **environ; 61 extern sigset_t original_sigmask; 62 63 /* 64 * A holder for all the resources needed to get a property value 65 * using libscf. 66 */ 67 typedef struct scf_resources { 68 scf_handle_t *sr_handle; 69 scf_instance_t *sr_inst; 70 scf_snapshot_t *sr_snap; 71 scf_propertygroup_t *sr_pg; 72 scf_property_t *sr_prop; 73 scf_value_t *sr_val; 74 scf_transaction_t *sr_tx; 75 scf_transaction_entry_t *sr_ent; 76 } scf_resources_t; 77 78 static pthread_mutex_t uid_mutex = PTHREAD_MUTEX_INITIALIZER; 79 static uid_t uid; 80 static int uid_cnt; 81 82 void 83 nwamd_escalate(void) { 84 priv_set_t *priv_set; 85 priv_set = priv_str_to_set("zone", ",", NULL); 86 87 if (priv_set == NULL) 88 pfail("creating privilege set: %s", strerror(errno)); 89 90 (void) pthread_mutex_lock(&uid_mutex); 91 if (uid == 0) 92 uid = getuid(); 93 if (uid_cnt++ == 0) { 94 if (setppriv(PRIV_SET, PRIV_EFFECTIVE, priv_set) == -1) { 95 priv_freeset(priv_set); 96 pfail("setppriv effective: %s", strerror(errno)); 97 } 98 } 99 (void) pthread_mutex_unlock(&uid_mutex); 100 101 priv_freeset(priv_set); 102 } 103 104 void 105 nwamd_deescalate(void) { 106 (void) pthread_mutex_lock(&uid_mutex); 107 108 assert(uid_cnt > 0); 109 if (--uid_cnt == 0) { 110 priv_set_t *priv_set, *allpriv_set; 111 112 /* build up our minimal set of privs. */ 113 priv_set = priv_str_to_set("basic", ",", NULL); 114 allpriv_set = priv_str_to_set("zone", ",", NULL); 115 if (priv_set == NULL || allpriv_set == NULL) 116 pfail("converting privilege sets: %s", strerror(errno)); 117 118 (void) priv_addset(priv_set, PRIV_FILE_CHOWN_SELF); 119 (void) priv_addset(priv_set, PRIV_FILE_DAC_READ); 120 (void) priv_addset(priv_set, PRIV_FILE_DAC_WRITE); 121 (void) priv_addset(priv_set, PRIV_NET_RAWACCESS); 122 (void) priv_addset(priv_set, PRIV_NET_PRIVADDR); 123 (void) priv_addset(priv_set, PRIV_PROC_AUDIT); 124 (void) priv_addset(priv_set, PRIV_PROC_OWNER); 125 (void) priv_addset(priv_set, PRIV_PROC_SETID); 126 (void) priv_addset(priv_set, PRIV_SYS_CONFIG); 127 (void) priv_addset(priv_set, PRIV_SYS_IP_CONFIG); 128 (void) priv_addset(priv_set, PRIV_SYS_IPC_CONFIG); 129 (void) priv_addset(priv_set, PRIV_SYS_MOUNT); 130 (void) priv_addset(priv_set, PRIV_SYS_NET_CONFIG); 131 (void) priv_addset(priv_set, PRIV_SYS_RES_CONFIG); 132 (void) priv_addset(priv_set, PRIV_SYS_RESOURCE); 133 134 /* 135 * Since our zone might not have all these privs, 136 * just ask for those that are available. 137 */ 138 priv_intersect(allpriv_set, priv_set); 139 140 if (setppriv(PRIV_SET, PRIV_INHERITABLE, priv_set) == -1) { 141 priv_freeset(allpriv_set); 142 priv_freeset(priv_set); 143 pfail("setppriv inheritable: %s", strerror(errno)); 144 } 145 /* 146 * Need to ensure permitted set contains all privs so we can 147 * escalate later. 148 */ 149 if (setppriv(PRIV_SET, PRIV_PERMITTED, allpriv_set) == -1) { 150 priv_freeset(allpriv_set); 151 priv_freeset(priv_set); 152 pfail("setppriv permitted: %s", strerror(errno)); 153 } 154 /* 155 * We need to find a smaller set of privs that are important to 156 * us. Otherwise we really are not gaining much by doing this. 157 */ 158 if (setppriv(PRIV_SET, PRIV_EFFECTIVE, priv_set) == -1) { 159 priv_freeset(allpriv_set); 160 priv_freeset(priv_set); 161 pfail("setppriv effective: %s", strerror(errno)); 162 } 163 164 priv_freeset(priv_set); 165 priv_freeset(allpriv_set); 166 } 167 (void) pthread_mutex_unlock(&uid_mutex); 168 } 169 170 /* 171 * 172 * This starts a child process determined by command. If command contains a 173 * slash then it is assumed to be a full path; otherwise the path is searched 174 * for an executable file with the name command. Command is also used as 175 * argv[0] of the new process. The rest of the arguments of the function 176 * up to the first NULL make up pointers to arguments of the new process. 177 * 178 * This function returns child exit status on success and -1 on failure. 179 * 180 * NOTE: original_sigmask must be set before this function is called. 181 */ 182 int 183 nwamd_start_childv(const char *command, char const * const *argv) 184 { 185 posix_spawnattr_t attr; 186 sigset_t fullset; 187 int i, rc, status, n; 188 pid_t pid; 189 char vbuf[1024]; 190 191 vbuf[0] = 0; 192 n = sizeof (vbuf); 193 for (i = 1; argv[i] != NULL && n > 2; i++) { 194 n -= strlcat(vbuf, " ", n); 195 n -= strlcat(vbuf, argv[i], n); 196 } 197 if (argv[i] != NULL || n < 0) 198 nlog(LOG_ERR, "nwamd_start_childv can't log full arg vector"); 199 200 if ((rc = posix_spawnattr_init(&attr)) != 0) { 201 nlog(LOG_DEBUG, "posix_spawnattr_init %d %s\n", 202 rc, strerror(rc)); 203 return (-1); 204 } 205 (void) sigfillset(&fullset); 206 if ((rc = posix_spawnattr_setsigdefault(&attr, &fullset)) != 0) { 207 nlog(LOG_DEBUG, "setsigdefault %d %s\n", rc, strerror(rc)); 208 return (-1); 209 } 210 if ((rc = posix_spawnattr_setsigmask(&attr, &original_sigmask)) != 0) { 211 nlog(LOG_DEBUG, "setsigmask %d %s\n", rc, strerror(rc)); 212 return (-1); 213 } 214 if ((rc = posix_spawnattr_setflags(&attr, 215 POSIX_SPAWN_SETSIGDEF|POSIX_SPAWN_SETSIGMASK)) != 0) { 216 nlog(LOG_DEBUG, "setflags %d %s\n", rc, strerror(rc)); 217 return (-1); 218 } 219 220 if ((rc = posix_spawnp(&pid, command, NULL, &attr, (char * const *)argv, 221 environ)) > 0) { 222 nlog(LOG_DEBUG, "posix_spawnp failed errno %d", rc); 223 return (-1); 224 } 225 226 if ((rc = posix_spawnattr_destroy(&attr)) != 0) { 227 nlog(LOG_DEBUG, "posix_spawn_attr_destroy %d %s\n", 228 rc, strerror(rc)); 229 return (-1); 230 } 231 232 (void) waitpid(pid, &status, 0); 233 if (WIFSIGNALED(status) || WIFSTOPPED(status)) { 234 i = WIFSIGNALED(status) ? WTERMSIG(status) : WSTOPSIG(status); 235 nlog(LOG_ERR, "'%s%s' %s with signal %d (%s)", command, vbuf, 236 (WIFSIGNALED(status) ? "terminated" : "stopped"), i, 237 strsignal(i)); 238 return (-2); 239 } else { 240 nlog(LOG_INFO, "'%s%s' completed normally: %d", command, vbuf, 241 WEXITSTATUS(status)); 242 return (WEXITSTATUS(status)); 243 } 244 } 245 246 /* 247 * For global zone, check if the link is used by a non-global 248 * zone, note that the non-global zones doesn't need this check, 249 * because zoneadm has taken care of this when the zone boots. 250 * In the global zone, we ignore events for local-zone-owned links 251 * since these are taken care of by the local zone's network 252 * configuration services. 253 */ 254 boolean_t 255 nwamd_link_belongs_to_this_zone(const char *linkname) 256 { 257 zoneid_t zoneid; 258 char zonename[ZONENAME_MAX]; 259 int ret; 260 261 zoneid = getzoneid(); 262 if (zoneid == GLOBAL_ZONEID) { 263 datalink_id_t linkid; 264 dladm_status_t status; 265 char errstr[DLADM_STRSIZE]; 266 267 if ((status = dladm_name2info(dld_handle, linkname, &linkid, 268 NULL, NULL, NULL)) != DLADM_STATUS_OK) { 269 nlog(LOG_DEBUG, "nwamd_link_belongs_to_this_zone: " 270 "could not get linkid for %s: %s", 271 linkname, dladm_status2str(status, errstr)); 272 return (B_FALSE); 273 } 274 zoneid = ALL_ZONES; 275 ret = zone_check_datalink(&zoneid, linkid); 276 if (ret == 0) { 277 (void) getzonenamebyid(zoneid, zonename, ZONENAME_MAX); 278 nlog(LOG_DEBUG, "nwamd_link_belongs_to_this_zone: " 279 "%s is used by non-global zone: %s", 280 linkname, zonename); 281 return (B_FALSE); 282 } 283 } 284 return (B_TRUE); 285 } 286 287 /* 288 * Inputs: 289 * res is a pointer to the scf_resources_t to be released. 290 */ 291 static void 292 release_scf_resources(scf_resources_t *res) 293 { 294 scf_entry_destroy(res->sr_ent); 295 scf_transaction_destroy(res->sr_tx); 296 scf_value_destroy(res->sr_val); 297 scf_property_destroy(res->sr_prop); 298 scf_pg_destroy(res->sr_pg); 299 scf_snapshot_destroy(res->sr_snap); 300 scf_instance_destroy(res->sr_inst); 301 (void) scf_handle_unbind(res->sr_handle); 302 scf_handle_destroy(res->sr_handle); 303 } 304 305 /* 306 * Inputs: 307 * fmri is the instance to look up 308 * Outputs: 309 * res is a pointer to an scf_resources_t. This is an internal 310 * structure that holds all the handles needed to get a specific 311 * property from the running snapshot; on a successful return it 312 * contains the scf_value_t that should be passed to the desired 313 * scf_value_get_foo() function, and must be freed after use by 314 * calling release_scf_resources(). On a failure return, any 315 * resources that may have been assigned to res are released, so 316 * the caller does not need to do any cleanup in the failure case. 317 * Returns: 318 * 0 on success 319 * -1 on failure 320 */ 321 322 static int 323 create_scf_resources(const char *fmri, scf_resources_t *res) 324 { 325 res->sr_tx = NULL; 326 res->sr_ent = NULL; 327 res->sr_inst = NULL; 328 res->sr_snap = NULL; 329 res->sr_pg = NULL; 330 res->sr_prop = NULL; 331 res->sr_val = NULL; 332 333 if ((res->sr_handle = scf_handle_create(SCF_VERSION)) == NULL) { 334 return (-1); 335 } 336 337 if (scf_handle_bind(res->sr_handle) != 0) { 338 scf_handle_destroy(res->sr_handle); 339 return (-1); 340 } 341 if ((res->sr_inst = scf_instance_create(res->sr_handle)) == NULL) { 342 goto failure; 343 } 344 if (scf_handle_decode_fmri(res->sr_handle, fmri, NULL, NULL, 345 res->sr_inst, NULL, NULL, SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0) { 346 goto failure; 347 } 348 if ((res->sr_snap = scf_snapshot_create(res->sr_handle)) == NULL) { 349 goto failure; 350 } 351 if (scf_instance_get_snapshot(res->sr_inst, "running", 352 res->sr_snap) != 0) { 353 goto failure; 354 } 355 if ((res->sr_pg = scf_pg_create(res->sr_handle)) == NULL) { 356 goto failure; 357 } 358 if ((res->sr_prop = scf_property_create(res->sr_handle)) == NULL) { 359 goto failure; 360 } 361 if ((res->sr_val = scf_value_create(res->sr_handle)) == NULL) { 362 goto failure; 363 } 364 if ((res->sr_tx = scf_transaction_create(res->sr_handle)) == NULL) { 365 goto failure; 366 } 367 if ((res->sr_ent = scf_entry_create(res->sr_handle)) == NULL) { 368 goto failure; 369 } 370 return (0); 371 372 failure: 373 nlog(LOG_ERR, "create_scf_resources failed: %s", 374 scf_strerror(scf_error())); 375 release_scf_resources(res); 376 return (-1); 377 } 378 379 /* 380 * Inputs: 381 * fmri is the instance to look up 382 * pg is the property group to look up 383 * prop is the property within that group to look up 384 * running specifies if running snapshot is to be used 385 * Outputs: 386 * res is a pointer to an scf_resources_t. This is an internal 387 * structure that holds all the handles needed to get a specific 388 * property from the running snapshot; on a successful return it 389 * contains the scf_value_t that should be passed to the desired 390 * scf_value_get_foo() function, and must be freed after use by 391 * calling release_scf_resources(). On a failure return, any 392 * resources that may have been assigned to res are released, so 393 * the caller does not need to do any cleanup in the failure case. 394 * Returns: 395 * 0 on success 396 * -1 on failure 397 */ 398 static int 399 get_property_value(const char *fmri, const char *pg, const char *prop, 400 boolean_t running, scf_resources_t *res) 401 { 402 if (create_scf_resources(fmri, res) != 0) 403 return (-1); 404 405 if (scf_instance_get_pg_composed(res->sr_inst, 406 running ? res->sr_snap : NULL, pg, res->sr_pg) != 0) { 407 goto failure; 408 } 409 if (scf_pg_get_property(res->sr_pg, prop, res->sr_prop) != 0) { 410 goto failure; 411 } 412 if (scf_property_get_value(res->sr_prop, res->sr_val) != 0) { 413 goto failure; 414 } 415 return (0); 416 417 failure: 418 release_scf_resources(res); 419 return (-1); 420 } 421 422 /* 423 * Inputs: 424 * lfmri is the instance fmri to look up 425 * lpg is the property group to look up 426 * lprop is the property within that group to look up 427 * Outputs: 428 * answer is a pointer to the property value 429 * Returns: 430 * 0 on success 431 * -1 on failure 432 * If successful, the property value is retured in *answer. 433 * Otherwise, *answer is undefined, and it is up to the caller to decide 434 * how to handle that case. 435 */ 436 int 437 nwamd_lookup_boolean_property(const char *lfmri, const char *lpg, 438 const char *lprop, boolean_t *answer) 439 { 440 int result = -1; 441 scf_resources_t res; 442 uint8_t prop_val; 443 444 if (get_property_value(lfmri, lpg, lprop, B_TRUE, &res) != 0) { 445 446 /* 447 * an error was already logged by get_property_value, 448 * and it released any resources assigned to res before 449 * returning. 450 */ 451 return (result); 452 } 453 if (scf_value_get_boolean(res.sr_val, &prop_val) != 0) { 454 goto cleanup; 455 } 456 *answer = (boolean_t)prop_val; 457 result = 0; 458 cleanup: 459 release_scf_resources(&res); 460 return (result); 461 } 462 463 /* 464 * Inputs: 465 * lfmri is the instance fmri to look up 466 * lpg is the property group to look up 467 * lprop is the property within that group to look up 468 * buf is the place to put the answer 469 * bufsz is the size of buf 470 * Outputs: 471 * 472 * Returns: 473 * 0 on success 474 * -1 on failure 475 * If successful, the property value is retured in buf. 476 * Otherwise, buf is undefined, and it is up to the caller to decide 477 * how to handle that case. 478 */ 479 int 480 nwamd_lookup_string_property(const char *lfmri, const char *lpg, 481 const char *lprop, char *buf, size_t bufsz) 482 { 483 int result = -1; 484 scf_resources_t res; 485 486 if (get_property_value(lfmri, lpg, lprop, B_TRUE, &res) != 0) { 487 /* 488 * The above function fails when trying to get a 489 * non-persistent property group from the running snapshot. 490 * Try going for the non-running snapshot. 491 */ 492 if (get_property_value(lfmri, lpg, lprop, B_FALSE, &res) != 0) { 493 /* 494 * an error was already logged by get_property_value, 495 * and it released any resources assigned to res before 496 * returning. 497 */ 498 return (result); 499 } 500 } 501 if (scf_value_get_astring(res.sr_val, buf, bufsz) == 0) 502 goto cleanup; 503 504 result = 0; 505 cleanup: 506 release_scf_resources(&res); 507 return (result); 508 } 509 510 /* 511 * Inputs: 512 * lfmri is the instance fmri to look up 513 * lpg is the property group to look up 514 * lprop is the property within that group to look up 515 * Outputs: 516 * answer is a pointer to the property value 517 * Returns: 518 * 0 on success 519 * -1 on failure 520 * If successful, the property value is retured in *answer. 521 * Otherwise, *answer is undefined, and it is up to the caller to decide 522 * how to handle that case. 523 */ 524 int 525 nwamd_lookup_count_property(const char *lfmri, const char *lpg, 526 const char *lprop, uint64_t *answer) 527 { 528 int result = -1; 529 scf_resources_t res; 530 531 if (get_property_value(lfmri, lpg, lprop, B_TRUE, &res) != 0) { 532 533 /* 534 * an error was already logged by get_property_value, 535 * and it released any resources assigned to res before 536 * returning. 537 */ 538 return (result); 539 } 540 if (scf_value_get_count(res.sr_val, answer) != 0) { 541 goto cleanup; 542 } 543 result = 0; 544 cleanup: 545 release_scf_resources(&res); 546 return (result); 547 } 548 549 static int 550 set_property_value(scf_resources_t *res, const char *propname, 551 scf_type_t proptype) 552 { 553 int result = -1; 554 boolean_t new; 555 556 retry: 557 new = (scf_pg_get_property(res->sr_pg, propname, res->sr_prop) != 0); 558 559 if (scf_transaction_start(res->sr_tx, res->sr_pg) == -1) { 560 goto failure; 561 } 562 if (new) { 563 if (scf_transaction_property_new(res->sr_tx, res->sr_ent, 564 propname, proptype) == -1) { 565 goto failure; 566 } 567 } else { 568 if (scf_transaction_property_change(res->sr_tx, res->sr_ent, 569 propname, proptype) == -1) { 570 goto failure; 571 } 572 } 573 574 if (scf_entry_add_value(res->sr_ent, res->sr_val) != 0) { 575 goto failure; 576 } 577 578 result = scf_transaction_commit(res->sr_tx); 579 if (result == 0) { 580 scf_transaction_reset(res->sr_tx); 581 if (scf_pg_update(res->sr_pg) == -1) { 582 goto failure; 583 } 584 nlog(LOG_INFO, "set_property_value: transaction commit failed " 585 "for %s; retrying", propname); 586 goto retry; 587 } 588 if (result == -1) 589 goto failure; 590 return (0); 591 592 failure: 593 return (-1); 594 } 595 596 int 597 nwamd_set_count_property(const char *fmri, const char *pg, const char *prop, 598 uint64_t count) 599 { 600 scf_resources_t res; 601 602 if (create_scf_resources(fmri, &res) != 0) 603 return (-1); 604 605 if (scf_instance_add_pg(res.sr_inst, pg, SCF_GROUP_APPLICATION, 606 SCF_PG_FLAG_NONPERSISTENT, res.sr_pg) != 0) { 607 if (scf_error() != SCF_ERROR_EXISTS) 608 goto failure; 609 if (scf_instance_get_pg_composed(res.sr_inst, NULL, pg, 610 res.sr_pg) != 0) 611 goto failure; 612 } 613 614 scf_value_set_count(res.sr_val, (uint64_t)count); 615 616 if (set_property_value(&res, prop, SCF_TYPE_COUNT) != 0) 617 goto failure; 618 619 release_scf_resources(&res); 620 return (0); 621 622 failure: 623 nlog(LOG_INFO, "nwamd_set_count_property: scf failure %s while " 624 "setting %s", scf_strerror(scf_error()), prop); 625 release_scf_resources(&res); 626 return (-1); 627 } 628 629 int 630 nwamd_set_string_property(const char *fmri, const char *pg, const char *prop, 631 const char *str) 632 { 633 scf_resources_t res; 634 635 if (create_scf_resources(fmri, &res) != 0) 636 return (-1); 637 638 if (scf_instance_add_pg(res.sr_inst, pg, SCF_GROUP_APPLICATION, 639 SCF_PG_FLAG_NONPERSISTENT, res.sr_pg) != 0) { 640 if (scf_error() != SCF_ERROR_EXISTS) 641 goto failure; 642 if (scf_instance_get_pg_composed(res.sr_inst, NULL, pg, 643 res.sr_pg) != 0) 644 goto failure; 645 } 646 647 if (scf_value_set_astring(res.sr_val, str) != 0) 648 goto failure; 649 650 if (set_property_value(&res, prop, SCF_TYPE_ASTRING) != 0) 651 goto failure; 652 653 release_scf_resources(&res); 654 return (0); 655 656 failure: 657 nlog(LOG_INFO, "nwamd_set_string_property: scf failure %s while " 658 "setting %s", scf_strerror(scf_error()), prop); 659 release_scf_resources(&res); 660 return (-1); 661 } 662 663 /* 664 * Deletes property prop from property group pg in SMF instance fmri. 665 * Returns 0 on success, -1 on failure. 666 */ 667 int 668 nwamd_delete_scf_property(const char *fmri, const char *pg, const char *prop) 669 { 670 scf_resources_t res; 671 int result = -1; 672 673 if (create_scf_resources(fmri, &res) != 0) 674 return (-1); 675 676 if (scf_instance_add_pg(res.sr_inst, pg, SCF_GROUP_APPLICATION, 677 SCF_PG_FLAG_NONPERSISTENT, res.sr_pg) != 0) { 678 if (scf_error() != SCF_ERROR_EXISTS) 679 goto failure; 680 if (scf_instance_get_pg_composed(res.sr_inst, NULL, pg, 681 res.sr_pg) != 0) 682 goto failure; 683 } 684 685 if (scf_pg_get_property(res.sr_pg, prop, res.sr_prop) != 0) 686 goto failure; 687 retry: 688 if (scf_transaction_start(res.sr_tx, res.sr_pg) == -1) 689 goto failure; 690 691 if (scf_transaction_property_delete(res.sr_tx, res.sr_ent, prop) == -1) 692 goto failure; 693 694 result = scf_transaction_commit(res.sr_tx); 695 if (result == 0) { 696 scf_transaction_reset(res.sr_tx); 697 if (scf_pg_update(res.sr_pg) == -1) 698 goto failure; 699 goto retry; 700 } 701 if (result == -1) 702 goto failure; 703 704 release_scf_resources(&res); 705 return (0); 706 failure: 707 release_scf_resources(&res); 708 return (-1); 709 } 710