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 2006 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #pragma ident   "%Z%%M% %I%     %E% SMI"
  27 
  28 #include <sys/proc.h>
  29 #include <sys/systm.h>
  30 #include <sys/param.h>
  31 #include <sys/atomic.h>
  32 #include <sys/kmem.h>
  33 #include <sys/sysmacros.h>
  34 #include <sys/procset.h>
  35 #include <sys/corectl.h>
  36 #include <sys/zone.h>
  37 #include <sys/cmn_err.h>
  38 #include <sys/policy.h>
  39 
  40 /*
  41  * Core File Settings
  42  * ------------------
  43  *
  44  * A process's core file path and content live in separate reference-counted
  45  * structures. The corectl_content_t structure is fairly straightforward --
  46  * the only subtlety is that we only really _need_ the mutex on architectures
  47  * on which 64-bit memory operations are not atomic. The corectl_path_t
  48  * structure is slightly trickier in that it contains a refstr_t rather than
  49  * just a char * string. This is to allow consumers of the data in that
  50  * structure (the core dumping sub-system for example) to safely use the
  51  * string without holding any locks on it in light of updates.
  52  *
  53  * At system and zone boot, init_core() sets init(1M)'s core file path and
  54  * content to the same value as the fields core_default_path and
  55  * core_default_content respectively (for the global zone). All subsequent
  56  * children of init(1M) reference those same settings. During boot coreadm(1M)
  57  * is invoked with the -u option to update the system settings from
  58  * /etc/coreadm.conf. This has the effect of also changing the values in
  59  * core_default_path and core_default_content which updates the core file
  60  * settings for all processes in the zone.  Each zone has different default
  61  * settings; when processes enter a non-global zone, their core file path and
  62  * content are set to the zone's default path and content.
  63  *
  64  * Processes that have their core file settings explicitly overridden using
  65  * coreadm(1M) no longer reference core_default_path or core_default_content
  66  * so subsequent changes to the default will not affect them.
  67  */
  68 
  69 zone_key_t      core_zone_key;
  70 
  71 static int set_proc_info(pid_t pid, const char *path, core_content_t content);
  72 
  73 static corectl_content_t *
  74 corectl_content_alloc(core_content_t cc)
  75 {
  76         corectl_content_t *ccp;
  77 
  78         ccp = kmem_zalloc(sizeof (corectl_content_t), KM_SLEEP);
  79         ccp->ccc_content = cc;
  80         ccp->ccc_refcnt = 1;
  81 
  82         return (ccp);
  83 }
  84 
  85 core_content_t
  86 corectl_content_value(corectl_content_t *ccp)
  87 {
  88         core_content_t content;
  89 
  90         mutex_enter(&ccp->ccc_mtx);
  91         content = ccp->ccc_content;
  92         mutex_exit(&ccp->ccc_mtx);
  93 
  94         return (content);
  95 }
  96 
  97 static void
  98 corectl_content_set(corectl_content_t *ccp, core_content_t content)
  99 {
 100         mutex_enter(&ccp->ccc_mtx);
 101         ccp->ccc_content = content;
 102         mutex_exit(&ccp->ccc_mtx);
 103 }
 104 
 105 void
 106 corectl_content_hold(corectl_content_t *ccp)
 107 {
 108         atomic_add_32(&ccp->ccc_refcnt, 1);
 109 }
 110 
 111 void
 112 corectl_content_rele(corectl_content_t *ccp)
 113 {
 114         if (atomic_add_32_nv(&ccp->ccc_refcnt, -1) == 0)
 115                 kmem_free(ccp, sizeof (corectl_content_t));
 116 }
 117 
 118 
 119 static corectl_path_t *
 120 corectl_path_alloc(const char *path)
 121 {
 122         corectl_path_t *ccp;
 123 
 124         ccp = kmem_zalloc(sizeof (corectl_path_t), KM_SLEEP);
 125         ccp->ccp_path = refstr_alloc(path);
 126         ccp->ccp_refcnt = 1;
 127 
 128         return (ccp);
 129 }
 130 
 131 refstr_t *
 132 corectl_path_value(corectl_path_t *ccp)
 133 {
 134         refstr_t *path;
 135 
 136         mutex_enter(&ccp->ccp_mtx);
 137         refstr_hold(path = ccp->ccp_path);
 138         mutex_exit(&ccp->ccp_mtx);
 139 
 140         return (path);
 141 }
 142 
 143 static void
 144 corectl_path_set(corectl_path_t *ccp, const char *path)
 145 {
 146         refstr_t *npath = refstr_alloc(path);
 147 
 148         mutex_enter(&ccp->ccp_mtx);
 149         refstr_rele(ccp->ccp_path);
 150         ccp->ccp_path = npath;
 151         mutex_exit(&ccp->ccp_mtx);
 152 }
 153 
 154 void
 155 corectl_path_hold(corectl_path_t *ccp)
 156 {
 157         atomic_add_32(&ccp->ccp_refcnt, 1);
 158 }
 159 
 160 void
 161 corectl_path_rele(corectl_path_t *ccp)
 162 {
 163         if (atomic_add_32_nv(&ccp->ccp_refcnt, -1) == 0) {
 164                 refstr_rele(ccp->ccp_path);
 165                 kmem_free(ccp, sizeof (corectl_path_t));
 166         }
 167 }
 168 
 169 /*
 170  * Constructor routine to be called when a zone is created.
 171  */
 172 /*ARGSUSED*/
 173 static void *
 174 core_init_zone(zoneid_t zoneid)
 175 {
 176         struct core_globals *cg;
 177 
 178         cg = kmem_alloc(sizeof (*cg), KM_SLEEP);
 179         mutex_init(&cg->core_lock, NULL, MUTEX_DEFAULT, NULL);
 180         cg->core_file = NULL;
 181         cg->core_options = CC_PROCESS_PATH;
 182         cg->core_content = CC_CONTENT_DEFAULT;
 183         cg->core_rlimit = RLIM64_INFINITY;
 184         cg->core_default_path = corectl_path_alloc("core");
 185         cg->core_default_content = corectl_content_alloc(CC_CONTENT_DEFAULT);
 186 
 187         return (cg);
 188 }
 189 
 190 /*
 191  * Destructor routine to be called when a zone is destroyed.
 192  */
 193 /*ARGSUSED*/
 194 static void
 195 core_free_zone(zoneid_t zoneid, void *arg)
 196 {
 197         struct core_globals *cg = arg;
 198 
 199         if (cg == NULL)
 200                 return;
 201         if (cg->core_file != NULL)
 202                 refstr_rele(cg->core_file);
 203         corectl_path_rele(cg->core_default_path);
 204         corectl_content_rele(cg->core_default_content);
 205         kmem_free(cg, sizeof (*cg));
 206 }
 207 
 208 /*
 209  * Called from start_init_common(), to set init's core file path and content.
 210  */
 211 void
 212 init_core(void)
 213 {
 214         struct core_globals *cg;
 215 
 216         /*
 217          * The first time we hit this, in the global zone, we have to
 218          * initialize the zsd key.
 219          */
 220         if (INGLOBALZONE(curproc)) {
 221                 zone_key_create(&core_zone_key, core_init_zone, NULL,
 222                     core_free_zone);
 223         }
 224 
 225         /*
 226          * zone_key_create will have called core_init_zone for the
 227          * global zone, which sets up the default path and content
 228          * variables.
 229          */
 230         VERIFY((cg = zone_getspecific(core_zone_key, curproc->p_zone)) != NULL);
 231 
 232         corectl_path_hold(cg->core_default_path);
 233         corectl_content_hold(cg->core_default_content);
 234 
 235         curproc->p_corefile = cg->core_default_path;
 236         curproc->p_content = cg->core_default_content;
 237 }
 238 
 239 int
 240 corectl(int subcode, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3)
 241 {
 242         int error = 0;
 243         proc_t *p;
 244         refstr_t *rp;
 245         size_t size;
 246         char *path;
 247         core_content_t content = CC_CONTENT_INVALID;
 248         struct core_globals *cg;
 249         zone_t *zone = curproc->p_zone;
 250 
 251         cg = zone_getspecific(core_zone_key, zone);
 252         ASSERT(cg != NULL);
 253 
 254         switch (subcode) {
 255         case CC_SET_OPTIONS:
 256                 if ((error = secpolicy_coreadm(CRED())) == 0) {
 257                         if (arg1 & ~CC_OPTIONS)
 258                                 error = EINVAL;
 259                         else
 260                                 cg->core_options = (uint32_t)arg1;
 261                 }
 262                 break;
 263 
 264         case CC_GET_OPTIONS:
 265                 return (cg->core_options);
 266 
 267         case CC_GET_GLOBAL_PATH:
 268         case CC_GET_DEFAULT_PATH:
 269         case CC_GET_PROCESS_PATH:
 270                 if (subcode == CC_GET_GLOBAL_PATH) {
 271                         mutex_enter(&cg->core_lock);
 272                         if ((rp = cg->core_file) != NULL)
 273                                 refstr_hold(rp);
 274                         mutex_exit(&cg->core_lock);
 275                 } else if (subcode == CC_GET_DEFAULT_PATH) {
 276                         rp = corectl_path_value(cg->core_default_path);
 277                 } else {
 278                         rp = NULL;
 279                         mutex_enter(&pidlock);
 280                         if ((p = prfind((pid_t)arg3)) == NULL ||
 281                             p->p_stat == SIDL) {
 282                                 mutex_exit(&pidlock);
 283                                 error = ESRCH;
 284                         } else {
 285                                 mutex_enter(&p->p_lock);
 286                                 mutex_exit(&pidlock);
 287                                 mutex_enter(&p->p_crlock);
 288                                 if (!hasprocperm(p->p_cred, CRED()))
 289                                         error = EPERM;
 290                                 else if (p->p_corefile != NULL)
 291                                         rp = corectl_path_value(p->p_corefile);
 292                                 mutex_exit(&p->p_crlock);
 293                                 mutex_exit(&p->p_lock);
 294                         }
 295                 }
 296                 if (rp == NULL) {
 297                         if (error == 0 && suword8((void *)arg1, 0))
 298                                 error = EFAULT;
 299                 } else {
 300                         error = copyoutstr(refstr_value(rp), (char *)arg1,
 301                             (size_t)arg2, NULL);
 302                         refstr_rele(rp);
 303                 }
 304                 break;
 305 
 306         case CC_SET_GLOBAL_PATH:
 307         case CC_SET_DEFAULT_PATH:
 308                 if ((error = secpolicy_coreadm(CRED())) != 0)
 309                         break;
 310 
 311                 /* FALLTHROUGH */
 312         case CC_SET_PROCESS_PATH:
 313                 if ((size = MIN((size_t)arg2, MAXPATHLEN)) == 0) {
 314                         error = EINVAL;
 315                         break;
 316                 }
 317                 path = kmem_alloc(size, KM_SLEEP);
 318                 error = copyinstr((char *)arg1, path, size, NULL);
 319                 if (error == 0) {
 320                         if (subcode == CC_SET_PROCESS_PATH) {
 321                                 error = set_proc_info((pid_t)arg3, path, 0);
 322                         } else if (subcode == CC_SET_DEFAULT_PATH) {
 323                                 corectl_path_set(cg->core_default_path, path);
 324                         } else if (*path != '\0' && *path != '/') {
 325                                 error = EINVAL;
 326                         } else {
 327                                 refstr_t *nrp = refstr_alloc(path);
 328 
 329                                 mutex_enter(&cg->core_lock);
 330                                 rp = cg->core_file;
 331                                 if (*path == '\0')
 332                                         cg->core_file = NULL;
 333                                 else
 334                                         refstr_hold(cg->core_file = nrp);
 335                                 mutex_exit(&cg->core_lock);
 336 
 337                                 if (rp != NULL)
 338                                         refstr_rele(rp);
 339 
 340                                 refstr_rele(nrp);
 341                         }
 342                 }
 343                 kmem_free(path, size);
 344                 break;
 345 
 346         case CC_SET_GLOBAL_CONTENT:
 347         case CC_SET_DEFAULT_CONTENT:
 348                 if ((error = secpolicy_coreadm(CRED())) != 0)
 349                         break;
 350 
 351                 /* FALLTHROUGH */
 352         case CC_SET_PROCESS_CONTENT:
 353                 error = copyin((void *)arg1, &content, sizeof (content));
 354                 if (error != 0)
 355                         break;
 356 
 357                 /*
 358                  * If any unknown bits are set, don't let this charade
 359                  * continue.
 360                  */
 361                 if (content & ~CC_CONTENT_ALL) {
 362                         error = EINVAL;
 363                         break;
 364                 }
 365 
 366                 if (subcode == CC_SET_PROCESS_CONTENT) {
 367                         error = set_proc_info((pid_t)arg2, NULL, content);
 368                 } else if (subcode == CC_SET_DEFAULT_CONTENT) {
 369                         corectl_content_set(cg->core_default_content, content);
 370                 } else {
 371                         mutex_enter(&cg->core_lock);
 372                         cg->core_content = content;
 373                         mutex_exit(&cg->core_lock);
 374                 }
 375 
 376                 break;
 377 
 378         case CC_GET_GLOBAL_CONTENT:
 379                 content = cg->core_content;
 380                 error = copyout(&content, (void *)arg1, sizeof (content));
 381                 break;
 382 
 383         case CC_GET_DEFAULT_CONTENT:
 384                 content = corectl_content_value(cg->core_default_content);
 385                 error = copyout(&content, (void *)arg1, sizeof (content));
 386                 break;
 387 
 388         case CC_GET_PROCESS_CONTENT:
 389                 mutex_enter(&pidlock);
 390                 if ((p = prfind((pid_t)arg2)) == NULL || p->p_stat == SIDL) {
 391                         mutex_exit(&pidlock);
 392                         error = ESRCH;
 393                         break;
 394                 }
 395 
 396                 mutex_enter(&p->p_lock);
 397                 mutex_exit(&pidlock);
 398                 mutex_enter(&p->p_crlock);
 399                 if (!hasprocperm(p->p_cred, CRED()))
 400                         error = EPERM;
 401                 else if (p->p_content == NULL)
 402                         content = CC_CONTENT_NONE;
 403                 else
 404                         content = corectl_content_value(p->p_content);
 405                 mutex_exit(&p->p_crlock);
 406                 mutex_exit(&p->p_lock);
 407 
 408                 if (error == 0)
 409                         error = copyout(&content, (void *)arg1,
 410                             sizeof (content));
 411                 break;
 412 
 413         default:
 414                 error = EINVAL;
 415                 break;
 416         }
 417 
 418         if (error)
 419                 return (set_errno(error));
 420         return (0);
 421 }
 422 
 423 typedef struct {
 424         int                     cc_count;
 425         corectl_path_t          *cc_path;
 426         corectl_content_t       *cc_content;
 427 } counter_t;
 428 
 429 static int
 430 set_one_proc_info(proc_t *p, counter_t *counterp)
 431 {
 432         corectl_path_t *corefile;
 433         corectl_content_t *content;
 434 
 435         mutex_enter(&p->p_crlock);
 436 
 437         if (!(p->p_flag & SSYS) && hasprocperm(p->p_cred, CRED())) {
 438                 mutex_exit(&p->p_crlock);
 439                 counterp->cc_count++;
 440                 if (counterp->cc_path != NULL) {
 441                         corectl_path_hold(counterp->cc_path);
 442                         mutex_enter(&p->p_lock);
 443                         corefile = p->p_corefile;
 444                         p->p_corefile = counterp->cc_path;
 445                         mutex_exit(&p->p_lock);
 446                         if (corefile != NULL)
 447                                 corectl_path_rele(corefile);
 448                 } else {
 449                         corectl_content_hold(counterp->cc_content);
 450                         mutex_enter(&p->p_lock);
 451                         content = p->p_content;
 452                         p->p_content = counterp->cc_content;
 453                         mutex_exit(&p->p_lock);
 454                         if (content != NULL)
 455                                 corectl_content_rele(content);
 456                 }
 457         } else {
 458                 mutex_exit(&p->p_crlock);
 459         }
 460 
 461         return (0);
 462 }
 463 
 464 static int
 465 set_proc_info(pid_t pid, const char *path, core_content_t content)
 466 {
 467         proc_t *p;
 468         counter_t counter;
 469         int error = 0;
 470 
 471         counter.cc_count = 0;
 472         /*
 473          * Only one of the core file path or content can be set at a time.
 474          */
 475         if (path != NULL) {
 476                 counter.cc_path = corectl_path_alloc(path);
 477                 counter.cc_content = NULL;
 478         } else {
 479                 counter.cc_path = NULL;
 480                 counter.cc_content = corectl_content_alloc(content);
 481         }
 482 
 483         if (pid == -1) {
 484                 procset_t set;
 485 
 486                 setprocset(&set, POP_AND, P_ALL, P_MYID, P_ALL, P_MYID);
 487                 error = dotoprocs(&set, set_one_proc_info, (char *)&counter);
 488                 if (error == 0 && counter.cc_count == 0)
 489                         error = EPERM;
 490         } else if (pid > 0) {
 491                 mutex_enter(&pidlock);
 492                 if ((p = prfind(pid)) == NULL || p->p_stat == SIDL) {
 493                         error = ESRCH;
 494                 } else {
 495                         (void) set_one_proc_info(p, &counter);
 496                         if (counter.cc_count == 0)
 497                                 error = EPERM;
 498                 }
 499                 mutex_exit(&pidlock);
 500         } else {
 501                 int nfound = 0;
 502                 pid_t pgid;
 503 
 504                 if (pid == 0)
 505                         pgid = curproc->p_pgrp;
 506                 else
 507                         pgid = -pid;
 508 
 509                 mutex_enter(&pidlock);
 510                 for (p = pgfind(pgid); p != NULL; p = p->p_pglink) {
 511                         if (p->p_stat != SIDL) {
 512                                 nfound++;
 513                                 (void) set_one_proc_info(p, &counter);
 514                         }
 515                 }
 516                 mutex_exit(&pidlock);
 517                 if (nfound == 0)
 518                         error = ESRCH;
 519                 else if (counter.cc_count == 0)
 520                         error = EPERM;
 521         }
 522 
 523         if (path != NULL)
 524                 corectl_path_rele(counter.cc_path);
 525         else
 526                 corectl_content_rele(counter.cc_content);
 527 
 528         if (error)
 529                 return (set_errno(error));
 530         return (0);
 531 }
 532 
 533 /*
 534  * Give current process the default core settings for its current zone;
 535  * used for processes entering a zone via zone_enter.
 536  */
 537 void
 538 set_core_defaults(void)
 539 {
 540         proc_t *p = curproc;
 541         struct core_globals *cg;
 542         corectl_path_t *oldpath, *newpath;
 543         corectl_content_t *oldcontent, *newcontent;
 544 
 545         cg = zone_getspecific(core_zone_key, p->p_zone);
 546 
 547         /* make local copies of default values to protect against change */
 548         newpath = cg->core_default_path;
 549         newcontent = cg->core_default_content;
 550 
 551         corectl_path_hold(newpath);
 552         corectl_content_hold(newcontent);
 553         mutex_enter(&p->p_lock);
 554         oldpath = p->p_corefile;
 555         p->p_corefile = newpath;
 556         oldcontent = p->p_content;
 557         p->p_content = newcontent;
 558         mutex_exit(&p->p_lock);
 559         if (oldpath != NULL)
 560                 corectl_path_rele(oldpath);
 561         if (oldcontent != NULL)
 562                 corectl_content_rele(oldcontent);
 563 }