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