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 2009 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #include <sys/types.h>
  27 
  28 #include <sys/cmn_err.h>
  29 #include <sys/cred.h>
  30 #include <sys/errno.h>
  31 #include <sys/rctl.h>
  32 #include <sys/rctl_impl.h>
  33 #include <sys/strlog.h>
  34 #include <sys/syslog.h>
  35 #include <sys/sysmacros.h>
  36 #include <sys/systm.h>
  37 #include <sys/policy.h>
  38 #include <sys/proc.h>
  39 #include <sys/task.h>
  40 
  41 /*
  42  * setrctl(2), getrctl(2), and private rctlsys(2*) system calls
  43  *
  44  * Resource control block (rctlblk_ptr_t, rctl_opaque_t)
  45  *   The resource control system call interfaces present the resource control
  46  *   values and flags via the resource control block abstraction, made manifest
  47  *   via an opaque data type with strict type definitions.  Keeping the formal
  48  *   definitions in the rcontrol block allows us to be clever in the kernel,
  49  *   combining attributes where appropriate in the current implementation while
  50  *   preserving binary compatibility in the face of implementation changes.
  51  */
  52 
  53 #define RBX_TO_BLK      0x1
  54 #define RBX_FROM_BLK    0x2
  55 #define RBX_VAL         0x4
  56 #define RBX_CTL         0x8
  57 
  58 static void
  59 rctlsys_rblk_xfrm(rctl_opaque_t *blk, rctl_dict_entry_t *rde,
  60     rctl_val_t *val, int flags)
  61 {
  62         if (flags & RBX_FROM_BLK) {
  63                 if (flags & RBX_VAL) {
  64                         /*
  65                          * Firing time cannot be set.
  66                          */
  67                         val->rcv_privilege = blk->rcq_privilege;
  68                         val->rcv_value = blk->rcq_value;
  69                         val->rcv_flagaction = blk->rcq_local_flagaction;
  70                         val->rcv_action_signal = blk->rcq_local_signal;
  71                         val->rcv_action_recip_pid =
  72                             blk->rcq_local_recipient_pid;
  73                 }
  74                 if (flags & RBX_CTL) {
  75                         rde->rcd_flagaction = blk->rcq_global_flagaction;
  76                         rde->rcd_syslog_level = blk->rcq_global_syslog_level;
  77 
  78                         /*
  79                          * Because the strlog() interface supports fewer options
  80                          * than are made available via the syslog() interface to
  81                          * userland, we map the syslog level down to a smaller
  82                          * set of distinct logging behaviours.
  83                          */
  84                         rde->rcd_strlog_flags = 0;
  85                         switch (blk->rcq_global_syslog_level) {
  86                                 case LOG_EMERG:
  87                                 case LOG_ALERT:
  88                                 case LOG_CRIT:
  89                                         rde->rcd_strlog_flags |= SL_CONSOLE;
  90                                         /*FALLTHROUGH*/
  91                                 case LOG_ERR:
  92                                         rde->rcd_strlog_flags |= SL_ERROR;
  93                                         /*FALLTHROUGH*/
  94                                 case LOG_WARNING:
  95                                         rde->rcd_strlog_flags |= SL_WARN;
  96                                         break;
  97                                 case LOG_NOTICE:
  98                                         rde->rcd_strlog_flags |= SL_CONSOLE;
  99                                         /*FALLTHROUGH*/
 100                                 case LOG_INFO:  /* informational */
 101                                 case LOG_DEBUG: /* debug-level messages */
 102                                 default:
 103                                         rde->rcd_strlog_flags |= SL_NOTE;
 104                                         break;
 105                         }
 106                 }
 107         } else {
 108                 bzero(blk,  sizeof (rctl_opaque_t));
 109                 if (flags & RBX_VAL) {
 110                         blk->rcq_privilege = val->rcv_privilege;
 111                         blk->rcq_value = val->rcv_value;
 112                         blk->rcq_enforced_value = rctl_model_value(rde,
 113                             curproc, val->rcv_value);
 114                         blk->rcq_local_flagaction = val->rcv_flagaction;
 115                         blk->rcq_local_signal = val->rcv_action_signal;
 116                         blk->rcq_firing_time = val->rcv_firing_time;
 117                         blk->rcq_local_recipient_pid =
 118                             val->rcv_action_recip_pid;
 119                 }
 120                 if (flags & RBX_CTL) {
 121                         blk->rcq_global_flagaction = rde->rcd_flagaction;
 122                         blk->rcq_global_syslog_level = rde->rcd_syslog_level;
 123                 }
 124         }
 125 }
 126 
 127 /*
 128  * int rctl_invalid_value(rctl_dict_entry_t *, rctl_val_t *)
 129  *
 130  * Overview
 131  *   Perform basic validation of proposed new resource control value against the
 132  *   global properties set on the control.  Any system call operation presented
 133  *   with an invalid resource control value should return -1 and set errno to
 134  *   EINVAL.
 135  *
 136  * Return values
 137  *   0 if valid, 1 if invalid.
 138  *
 139  * Caller's context
 140  *   No restriction on context.
 141  */
 142 int
 143 rctl_invalid_value(rctl_dict_entry_t *rde, rctl_val_t *rval)
 144 {
 145         rctl_val_t *sys_rval;
 146 
 147         if (rval->rcv_privilege != RCPRIV_BASIC &&
 148             rval->rcv_privilege != RCPRIV_PRIVILEGED &&
 149             rval->rcv_privilege != RCPRIV_SYSTEM)
 150                 return (1);
 151 
 152         if (rval->rcv_flagaction & ~RCTL_LOCAL_MASK)
 153                 return (1);
 154 
 155         if (rval->rcv_privilege == RCPRIV_BASIC &&
 156             (rde->rcd_flagaction & RCTL_GLOBAL_NOBASIC) != 0)
 157                 return (1);
 158 
 159         if ((rval->rcv_flagaction & RCTL_LOCAL_DENY) == 0 &&
 160             (rde->rcd_flagaction & RCTL_GLOBAL_DENY_ALWAYS) != 0)
 161                 return (1);
 162 
 163         if ((rval->rcv_flagaction & RCTL_LOCAL_DENY) &&
 164             (rde->rcd_flagaction & RCTL_GLOBAL_DENY_NEVER))
 165                 return (1);
 166 
 167         if ((rval->rcv_flagaction & RCTL_LOCAL_SIGNAL) &&
 168             (rde->rcd_flagaction & RCTL_GLOBAL_SIGNAL_NEVER))
 169                 return (1);
 170 
 171         if ((rval->rcv_flagaction & RCTL_LOCAL_SIGNAL) &&
 172             rval->rcv_action_signal == 0)
 173                 return (1);
 174 
 175         if (rval->rcv_action_signal == SIGXCPU &&
 176             (rde->rcd_flagaction & RCTL_GLOBAL_CPU_TIME) == 0)
 177                 return (1);
 178         else if (rval->rcv_action_signal == SIGXFSZ &&
 179             (rde->rcd_flagaction & RCTL_GLOBAL_FILE_SIZE) == 0)
 180                 return (1);
 181         else if (rval->rcv_action_signal != SIGHUP &&
 182             rval->rcv_action_signal != SIGABRT &&
 183             rval->rcv_action_signal != SIGKILL &&
 184             rval->rcv_action_signal != SIGTERM &&
 185             rval->rcv_action_signal != SIGSTOP &&
 186             rval->rcv_action_signal != SIGXCPU &&
 187             rval->rcv_action_signal != SIGXFSZ &&
 188             rval->rcv_action_signal != SIGXRES &&
 189             rval->rcv_action_signal != 0)    /* That is, no signal is ok. */
 190                 return (1);
 191 
 192         sys_rval = rde->rcd_default_value;
 193         while (sys_rval->rcv_privilege != RCPRIV_SYSTEM)
 194                 sys_rval = sys_rval->rcv_next;
 195 
 196         if (rval->rcv_value > sys_rval->rcv_value)
 197                 return (1);
 198 
 199         return (0);
 200 }
 201 
 202 /*
 203  * static long rctlsys_get(char *name, rctl_opaque_t *old_rblk,
 204  *   rctl_opaque_t *new_rblk, int flags)
 205  *
 206  * Overview
 207  *   rctlsys_get() is the implementation of the core logic of getrctl(2), the
 208  *   public system call for fetching resource control values.  Three mutually
 209  *   exclusive flag values are supported: RCTL_USAGE, RCTL_FIRST and RCTL_NEXT.
 210  *   When RCTL_USAGE is presented, the current usage for the resource control
 211  *   is returned in new_blk if the resource control provides an implementation
 212  *   of the usage operation.  When RCTL_FIRST is presented, the value of
 213  *   old_rblk is ignored, and the first value in the resource control value
 214  *   sequence for the named control is transformed and placed in the user
 215  *   memory location at new_rblk.  In the RCTL_NEXT case, the value of old_rblk
 216  *   is examined, and the next value in the sequence is transformed and placed
 217  *   at new_rblk.
 218  */
 219 static long
 220 rctlsys_get(char *name, rctl_opaque_t *old_rblk, rctl_opaque_t *new_rblk,
 221     int flags)
 222 {
 223         rctl_val_t *nval;
 224         rctl_opaque_t *nblk;
 225         rctl_hndl_t hndl;
 226         char *kname;
 227         size_t klen;
 228         rctl_dict_entry_t *krde;
 229         int ret;
 230         int action = flags & (~RCTLSYS_ACTION_MASK);
 231 
 232         if (flags & (~RCTLSYS_MASK))
 233                 return (set_errno(EINVAL));
 234 
 235         if (action != RCTL_FIRST && action != RCTL_NEXT &&
 236             action != RCTL_USAGE)
 237                 return (set_errno(EINVAL));
 238 
 239         if (new_rblk == NULL || name == NULL)
 240                 return (set_errno(EFAULT));
 241 
 242         kname = kmem_alloc(MAXPATHLEN, KM_SLEEP);
 243         krde = kmem_alloc(sizeof (rctl_dict_entry_t), KM_SLEEP);
 244 
 245         if (copyinstr(name, kname, MAXPATHLEN, &klen) != 0) {
 246                 kmem_free(kname, MAXPATHLEN);
 247                 kmem_free(krde, sizeof (rctl_dict_entry_t));
 248                 return (set_errno(EFAULT));
 249         }
 250 
 251         if ((hndl = rctl_hndl_lookup(kname)) == -1) {
 252                 kmem_free(kname, MAXPATHLEN);
 253                 kmem_free(krde, sizeof (rctl_dict_entry_t));
 254                 return (set_errno(EINVAL));
 255         }
 256 
 257         if (rctl_global_get(kname, krde) == -1) {
 258                 kmem_free(kname, MAXPATHLEN);
 259                 kmem_free(krde, sizeof (rctl_dict_entry_t));
 260                 return (set_errno(ESRCH));
 261         }
 262 
 263         kmem_free(kname, MAXPATHLEN);
 264 
 265         if (action != RCTL_USAGE)
 266                 nval = kmem_cache_alloc(rctl_val_cache, KM_SLEEP);
 267 
 268         if (action == RCTL_USAGE) {
 269                 rctl_set_t *rset;
 270                 rctl_t *rctl;
 271                 rctl_qty_t usage;
 272 
 273                 mutex_enter(&curproc->p_lock);
 274                 if ((rset = rctl_entity_obtain_rset(krde, curproc)) == NULL) {
 275                         mutex_exit(&curproc->p_lock);
 276                         kmem_free(krde, sizeof (rctl_dict_entry_t));
 277                         return (set_errno(ESRCH));
 278                 }
 279                 mutex_enter(&rset->rcs_lock);
 280                 if (rctl_set_find(rset, hndl, &rctl) == -1) {
 281                         mutex_exit(&rset->rcs_lock);
 282                         mutex_exit(&curproc->p_lock);
 283                         kmem_free(krde, sizeof (rctl_dict_entry_t));
 284                         return (set_errno(ESRCH));
 285                 }
 286                 if (RCTLOP_NO_USAGE(rctl)) {
 287                         mutex_exit(&rset->rcs_lock);
 288                         mutex_exit(&curproc->p_lock);
 289                         kmem_free(krde, sizeof (rctl_dict_entry_t));
 290                         return (set_errno(ENOTSUP));
 291                 }
 292                 usage = RCTLOP_GET_USAGE(rctl, curproc);
 293                 mutex_exit(&rset->rcs_lock);
 294                 mutex_exit(&curproc->p_lock);
 295 
 296                 nblk = kmem_alloc(sizeof (rctl_opaque_t), KM_SLEEP);
 297                 bzero(nblk, sizeof (rctl_opaque_t));
 298                 nblk->rcq_value = usage;
 299 
 300                 ret = copyout(nblk, new_rblk, sizeof (rctl_opaque_t));
 301                 kmem_free(nblk, sizeof (rctl_opaque_t));
 302                 kmem_free(krde, sizeof (rctl_dict_entry_t));
 303                 return (ret == 0 ? 0 : set_errno(EFAULT));
 304         } else if (action == RCTL_FIRST) {
 305 
 306                 mutex_enter(&curproc->p_lock);
 307                 if (ret = rctl_local_get(hndl, NULL, nval, curproc)) {
 308                         mutex_exit(&curproc->p_lock);
 309                         kmem_cache_free(rctl_val_cache, nval);
 310                         kmem_free(krde, sizeof (rctl_dict_entry_t));
 311                         return (set_errno(ret));
 312                 }
 313                 mutex_exit(&curproc->p_lock);
 314         } else {
 315                 /*
 316                  * RCTL_NEXT
 317                  */
 318                 rctl_val_t *oval;
 319                 rctl_opaque_t *oblk;
 320 
 321                 oblk = kmem_alloc(sizeof (rctl_opaque_t), KM_SLEEP);
 322 
 323                 if (copyin(old_rblk, oblk, sizeof (rctl_opaque_t)) == -1) {
 324                         kmem_cache_free(rctl_val_cache, nval);
 325                         kmem_free(oblk, sizeof (rctl_opaque_t));
 326                         kmem_free(krde, sizeof (rctl_dict_entry_t));
 327                         return (set_errno(EFAULT));
 328                 }
 329 
 330                 oval = kmem_cache_alloc(rctl_val_cache, KM_SLEEP);
 331 
 332                 rctlsys_rblk_xfrm(oblk, NULL, oval, RBX_FROM_BLK | RBX_VAL);
 333                 mutex_enter(&curproc->p_lock);
 334                 ret = rctl_local_get(hndl, oval, nval, curproc);
 335                 mutex_exit(&curproc->p_lock);
 336 
 337                 kmem_cache_free(rctl_val_cache, oval);
 338                 kmem_free(oblk, sizeof (rctl_opaque_t));
 339 
 340                 if (ret != 0) {
 341                         kmem_cache_free(rctl_val_cache, nval);
 342                         kmem_free(krde, sizeof (rctl_dict_entry_t));
 343                         return (set_errno(ret));
 344                 }
 345         }
 346 
 347         nblk = kmem_alloc(sizeof (rctl_opaque_t), KM_SLEEP);
 348 
 349         rctlsys_rblk_xfrm(nblk, krde, nval, RBX_TO_BLK | RBX_VAL | RBX_CTL);
 350 
 351         kmem_free(krde, sizeof (rctl_dict_entry_t));
 352         kmem_cache_free(rctl_val_cache, nval);
 353 
 354         if (copyout(nblk, new_rblk, sizeof (rctl_opaque_t)) == -1) {
 355                 kmem_free(nblk, sizeof (rctl_opaque_t));
 356                 return (set_errno(EFAULT));
 357         }
 358 
 359         kmem_free(nblk, sizeof (rctl_opaque_t));
 360 
 361         return (0);
 362 }
 363 
 364 /*
 365  * static long rctlsys_set(char *name, rctl_opaque_t *old_rblk,
 366  *   rctl_opaque_t *new_rblk, int flags)
 367  *
 368  * Overview
 369  *   rctlsys_set() is the implementation of the core login of setrctl(2), which
 370  *   allows the establishment of resource control values.  Flags may take on any
 371  *   of three exclusive values:  RCTL_INSERT, RCTL_DELETE, and RCTL_REPLACE.
 372  *   RCTL_INSERT ignores old_rblk and inserts the value in the appropriate
 373  *   position in the ordered sequence of resource control values.  RCTL_DELETE
 374  *   ignores old_rblk and deletes the first resource control value matching
 375  *   (value, priority) in the given resource block.  If no matching value is
 376  *   found, -1 is returned and errno is set to ENOENT.  Finally, in the case of
 377  *   RCTL_REPLACE, old_rblk is used to match (value, priority); the matching
 378  *   resource control value in the sequence is replaced with the contents of
 379  *   new_rblk.  Again, if no match is found, -1 is returned and errno is set to
 380  *   ENOENT.
 381  *
 382  *   rctlsys_set() causes a cursor test, which can reactivate resource controls
 383  *   that have previously fired.
 384  */
 385 static long
 386 rctlsys_set(char *name, rctl_opaque_t *old_rblk, rctl_opaque_t *new_rblk,
 387     int flags)
 388 {
 389         rctl_val_t *nval;
 390         rctl_dict_entry_t *rde;
 391         rctl_opaque_t *nblk;
 392         rctl_hndl_t hndl;
 393         char *kname;
 394         size_t klen;
 395         long ret = 0;
 396         proc_t *pp = NULL;
 397         pid_t pid;
 398         int action = flags & (~RCTLSYS_ACTION_MASK);
 399         rctl_val_t *oval;
 400         rctl_val_t *rval1;
 401         rctl_val_t *rval2;
 402         rctl_val_t *tval;
 403         rctl_opaque_t *oblk;
 404 
 405         if (flags & (~RCTLSYS_MASK))
 406                 return (set_errno(EINVAL));
 407 
 408         if (action != RCTL_INSERT &&
 409             action != RCTL_DELETE &&
 410             action != RCTL_REPLACE)
 411                 return (set_errno(EINVAL));
 412 
 413         if (new_rblk == NULL || name == NULL)
 414                 return (set_errno(EFAULT));
 415 
 416         kname = kmem_alloc(MAXPATHLEN, KM_SLEEP);
 417         if (copyinstr(name, kname, MAXPATHLEN, &klen) != 0) {
 418                 kmem_free(kname, MAXPATHLEN);
 419                 return (set_errno(EFAULT));
 420         }
 421 
 422         if ((hndl = rctl_hndl_lookup(kname)) == -1) {
 423                 kmem_free(kname, MAXPATHLEN);
 424                 return (set_errno(EINVAL));
 425         }
 426 
 427         kmem_free(kname, MAXPATHLEN);
 428 
 429         rde = rctl_dict_lookup_hndl(hndl);
 430 
 431         nblk = kmem_alloc(sizeof (rctl_opaque_t), KM_SLEEP);
 432 
 433         if (copyin(new_rblk, nblk, sizeof (rctl_opaque_t)) == -1) {
 434                 kmem_free(nblk, sizeof (rctl_opaque_t));
 435                 return (set_errno(EFAULT));
 436         }
 437 
 438         nval = kmem_cache_alloc(rctl_val_cache, KM_SLEEP);
 439 
 440         rctlsys_rblk_xfrm(nblk, NULL, nval, RBX_FROM_BLK | RBX_VAL);
 441 
 442         if (rctl_invalid_value(rde, nval)) {
 443                 kmem_free(nblk, sizeof (rctl_opaque_t));
 444                 kmem_cache_free(rctl_val_cache, nval);
 445                 return (set_errno(EINVAL));
 446         }
 447 
 448         /* allocate what we might need before potentially grabbing p_lock */
 449         oblk = kmem_alloc(sizeof (rctl_opaque_t), KM_SLEEP);
 450         oval = kmem_cache_alloc(rctl_val_cache, KM_SLEEP);
 451         rval1 = kmem_cache_alloc(rctl_val_cache, KM_SLEEP);
 452         rval2 = kmem_cache_alloc(rctl_val_cache, KM_SLEEP);
 453 
 454         if (nval->rcv_privilege == RCPRIV_BASIC) {
 455                 if (flags & RCTL_USE_RECIPIENT_PID) {
 456                         pid = nval->rcv_action_recip_pid;
 457 
 458                         /* case for manipulating rctl values on other procs */
 459                         if (pid != curproc->p_pid) {
 460                                 /* cannot be other pid on process rctls */
 461                                 if (rde->rcd_entity == RCENTITY_PROCESS) {
 462                                         ret = set_errno(EINVAL);
 463                                         goto rctlsys_out;
 464                                 }
 465                                 /*
 466                                  * must have privilege to manipulate controls
 467                                  * on other processes
 468                                  */
 469                                 if (secpolicy_rctlsys(CRED(), B_FALSE) != 0) {
 470                                         ret = set_errno(EACCES);
 471                                         goto rctlsys_out;
 472                                 }
 473 
 474                                 pid = nval->rcv_action_recip_pid;
 475                                 mutex_enter(&pidlock);
 476                                 pp = prfind(pid);
 477                                 if (!pp) {
 478                                         mutex_exit(&pidlock);
 479                                         ret = set_errno(ESRCH);
 480                                         goto rctlsys_out;
 481                                 }
 482 
 483                                 /*
 484                                  * idle or zombie procs have either not yet
 485                                  * set up their rctls or have already done
 486                                  * their rctl_set_tearoff's.
 487                                  */
 488                                 if (pp->p_stat == SZOMB ||
 489                                     pp->p_stat == SIDL) {
 490                                         mutex_exit(&pidlock);
 491                                         ret = set_errno(ESRCH);
 492                                         goto rctlsys_out;
 493                                 }
 494 
 495                                 /*
 496                                  * hold this pp's p_lock to ensure that
 497                                  * it does not do it's rctl_set_tearoff
 498                                  * If we did not do this, we could
 499                                  * potentially add rctls to the entity
 500                                  * with a recipient that is a process
 501                                  * that has exited.
 502                                  */
 503                                 mutex_enter(&pp->p_lock);
 504                                 mutex_exit(&pidlock);
 505 
 506                                 /*
 507                                  * We know that curproc's task, project,
 508                                  * and zone pointers will not change
 509                                  * because functions that change them
 510                                  * call holdlwps(SHOLDFORK1) first.
 511                                  */
 512 
 513                                 /*
 514                                  * verify that the found pp is in the
 515                                  * current task.  If it is, then it
 516                                  * is also within the current project
 517                                  * and zone.
 518                                  */
 519                                 if (rde->rcd_entity == RCENTITY_TASK &&
 520                                     pp->p_task != curproc->p_task) {
 521                                         ret = set_errno(ESRCH);
 522                                         goto rctlsys_out;
 523                                 }
 524 
 525                                 ASSERT(pp->p_task->tk_proj ==
 526                                     curproc->p_task->tk_proj);
 527                                 ASSERT(pp->p_zone == curproc->p_zone);
 528 
 529 
 530                                 nval->rcv_action_recipient = pp;
 531                                 nval->rcv_action_recip_pid = pid;
 532 
 533                         } else {
 534                                 /* for manipulating rctl values on this proc */
 535                                 mutex_enter(&curproc->p_lock);
 536                                 pp = curproc;
 537                                 nval->rcv_action_recipient = curproc;
 538                                 nval->rcv_action_recip_pid = curproc->p_pid;
 539                         }
 540 
 541                 } else {
 542                         /* RCTL_USE_RECIPIENT_PID not set, use this proc */
 543                         mutex_enter(&curproc->p_lock);
 544                         pp = curproc;
 545                         nval->rcv_action_recipient = curproc;
 546                         nval->rcv_action_recip_pid = curproc->p_pid;
 547                 }
 548 
 549         } else {
 550                 /* privileged controls have no recipient pid */
 551                 mutex_enter(&curproc->p_lock);
 552                 pp = curproc;
 553                 nval->rcv_action_recipient = NULL;
 554                 nval->rcv_action_recip_pid = -1;
 555         }
 556 
 557         nval->rcv_firing_time = 0;
 558 
 559         if (action == RCTL_REPLACE) {
 560 
 561                 if (copyin(old_rblk, oblk, sizeof (rctl_opaque_t)) == -1) {
 562                         ret = set_errno(EFAULT);
 563                         goto rctlsys_out;
 564                 }
 565 
 566                 rctlsys_rblk_xfrm(oblk, NULL, oval, RBX_FROM_BLK | RBX_VAL);
 567 
 568                 if (rctl_invalid_value(rde, oval)) {
 569                         ret = set_errno(EINVAL);
 570                         goto rctlsys_out;
 571                 }
 572 
 573                 if (oval->rcv_privilege == RCPRIV_BASIC) {
 574                         if (!(flags & RCTL_USE_RECIPIENT_PID)) {
 575                                 oval->rcv_action_recipient = curproc;
 576                                 oval->rcv_action_recip_pid = curproc->p_pid;
 577                         }
 578                 } else {
 579                         oval->rcv_action_recipient = NULL;
 580                         oval->rcv_action_recip_pid = -1;
 581                 }
 582 
 583                 /*
 584                  * Find the real value we're attempting to replace on the
 585                  * sequence, rather than trusting the one delivered from
 586                  * userland.
 587                  */
 588                 if (ret = rctl_local_get(hndl, NULL, rval1, pp)) {
 589                         (void) set_errno(ret);
 590                         goto rctlsys_out;
 591                 }
 592 
 593                 do {
 594                         if (rval1->rcv_privilege == RCPRIV_SYSTEM ||
 595                             rctl_val_cmp(oval, rval1, 0) == 0)
 596                                 break;
 597 
 598                         tval = rval1;
 599                         rval1 = rval2;
 600                         rval2 = tval;
 601                 } while (rctl_local_get(hndl, rval2, rval1, pp) == 0);
 602 
 603                 if (rval1->rcv_privilege == RCPRIV_SYSTEM) {
 604                         if (rctl_val_cmp(oval, rval1, 1) == 0)
 605                                 ret = set_errno(EPERM);
 606                         else
 607                                 ret = set_errno(ESRCH);
 608 
 609                         goto rctlsys_out;
 610                 }
 611 
 612                 bcopy(rval1, oval, sizeof (rctl_val_t));
 613 
 614                 /*
 615                  * System controls are immutable.
 616                  */
 617                 if (nval->rcv_privilege == RCPRIV_SYSTEM) {
 618                         ret = set_errno(EPERM);
 619                         goto rctlsys_out;
 620                 }
 621 
 622                 /*
 623                  * Only privileged processes in the global zone can modify
 624                  * privileged rctls of type RCENTITY_ZONE; replacing privileged
 625                  * controls with basic ones are not allowed either.  Lowering a
 626                  * lowerable one might be OK for privileged processes in a
 627                  * non-global zone, but lowerable rctls probably don't make
 628                  * sense for zones (hence, not modifiable from within a zone).
 629                  */
 630                 if (rde->rcd_entity == RCENTITY_ZONE &&
 631                     (nval->rcv_privilege == RCPRIV_PRIVILEGED ||
 632                     oval->rcv_privilege == RCPRIV_PRIVILEGED) &&
 633                     secpolicy_rctlsys(CRED(), B_TRUE) != 0) {
 634                         ret = set_errno(EACCES);
 635                         goto rctlsys_out;
 636                 }
 637 
 638                 /*
 639                  * Must be privileged to replace a privileged control with
 640                  * a basic one.
 641                  */
 642                 if (oval->rcv_privilege == RCPRIV_PRIVILEGED &&
 643                     nval->rcv_privilege != RCPRIV_PRIVILEGED &&
 644                     secpolicy_rctlsys(CRED(), B_FALSE) != 0) {
 645                         ret = set_errno(EACCES);
 646                         goto rctlsys_out;
 647                 }
 648 
 649                 /*
 650                  * Must have lowerable global property for non-privileged
 651                  * to lower the value of a privileged control; otherwise must
 652                  * have sufficient privileges to modify privileged controls
 653                  * at all.
 654                  */
 655                 if (oval->rcv_privilege == RCPRIV_PRIVILEGED &&
 656                     nval->rcv_privilege == RCPRIV_PRIVILEGED &&
 657                     ((((rde->rcd_flagaction & RCTL_GLOBAL_LOWERABLE) == 0) ||
 658                     oval->rcv_flagaction != nval->rcv_flagaction ||
 659                     oval->rcv_action_signal != nval->rcv_action_signal ||
 660                     oval->rcv_value < nval->rcv_value)) &&
 661                     secpolicy_rctlsys(CRED(), B_FALSE) != 0) {
 662                         ret = set_errno(EACCES);
 663                         goto rctlsys_out;
 664                 }
 665 
 666                 if (ret = rctl_local_replace(hndl, oval, nval, pp)) {
 667                         (void) set_errno(ret);
 668                         goto rctlsys_out;
 669                 }
 670 
 671                 /* ensure that nval is not freed */
 672                 nval = NULL;
 673 
 674         } else if (action == RCTL_INSERT) {
 675                 /*
 676                  * System controls are immutable.
 677                  */
 678                 if (nval->rcv_privilege == RCPRIV_SYSTEM) {
 679                         ret = set_errno(EPERM);
 680                         goto rctlsys_out;
 681                 }
 682 
 683                 /*
 684                  * Only privileged processes in the global zone may add
 685                  * privileged zone.* rctls.  Only privileged processes
 686                  * may add other privileged rctls.
 687                  */
 688                 if (nval->rcv_privilege == RCPRIV_PRIVILEGED) {
 689                         if ((rde->rcd_entity == RCENTITY_ZONE &&
 690                             secpolicy_rctlsys(CRED(), B_TRUE) != 0) ||
 691                             (rde->rcd_entity != RCENTITY_ZONE &&
 692                             secpolicy_rctlsys(CRED(), B_FALSE) != 0)) {
 693                                 ret = set_errno(EACCES);
 694                                 goto rctlsys_out;
 695                         }
 696                 }
 697 
 698                 /*
 699                  * Only one basic control is allowed per rctl.
 700                  * If a basic control is being inserted, delete
 701                  * any other basic control.
 702                  */
 703                 if ((nval->rcv_privilege == RCPRIV_BASIC) &&
 704                     (rctl_local_get(hndl, NULL, rval1, pp) == 0)) {
 705                         do {
 706                                 if (rval1->rcv_privilege == RCPRIV_BASIC &&
 707                                     rval1->rcv_action_recipient == curproc) {
 708                                         (void) rctl_local_delete(hndl, rval1,
 709                                             pp);
 710                                         if (rctl_local_get(hndl, NULL, rval1,
 711                                             pp) != 0)
 712                                                 break;
 713                                 }
 714 
 715                                 tval = rval1;
 716                                 rval1 = rval2;
 717                                 rval2 = tval;
 718                         } while (rctl_local_get(hndl, rval2, rval1, pp)
 719                             == 0);
 720                 }
 721 
 722 
 723                 if (ret = rctl_local_insert(hndl, nval, pp)) {
 724                         (void) set_errno(ret);
 725                         goto rctlsys_out;
 726                 }
 727 
 728                 /* ensure that nval is not freed */
 729                 nval = NULL;
 730 
 731         } else {
 732                 /*
 733                  * RCTL_DELETE
 734                  */
 735                 if (nval->rcv_privilege == RCPRIV_SYSTEM) {
 736                         ret = set_errno(EPERM);
 737                         goto rctlsys_out;
 738                 }
 739 
 740                 if (nval->rcv_privilege == RCPRIV_PRIVILEGED) {
 741                         if ((rde->rcd_entity == RCENTITY_ZONE &&
 742                             secpolicy_rctlsys(CRED(), B_TRUE) != 0) ||
 743                             (rde->rcd_entity != RCENTITY_ZONE &&
 744                             secpolicy_rctlsys(CRED(), B_FALSE) != 0)) {
 745                                 ret = set_errno(EACCES);
 746                                 goto rctlsys_out;
 747                         }
 748                 }
 749 
 750                 if (ret = rctl_local_delete(hndl, nval, pp)) {
 751                         (void) set_errno(ret);
 752                         goto rctlsys_out;
 753                 }
 754         }
 755 
 756 rctlsys_out:
 757 
 758         if (pp)
 759                 mutex_exit(&pp->p_lock);
 760 
 761         kmem_free(nblk, sizeof (rctl_opaque_t));
 762         kmem_free(oblk, sizeof (rctl_opaque_t));
 763 
 764         /* only free nval if we did not rctl_local_insert it */
 765         if (nval)
 766                 kmem_cache_free(rctl_val_cache, nval);
 767 
 768         kmem_cache_free(rctl_val_cache, oval);
 769         kmem_cache_free(rctl_val_cache, rval1);
 770         kmem_cache_free(rctl_val_cache, rval2);
 771 
 772         return (ret);
 773 }
 774 
 775 static long
 776 rctlsys_lst(char *ubuf, size_t ubufsz)
 777 {
 778         char *kbuf;
 779         size_t kbufsz;
 780 
 781         kbufsz = rctl_build_name_buf(&kbuf);
 782 
 783         if (kbufsz <= ubufsz &&
 784             copyout(kbuf, ubuf, kbufsz) != 0) {
 785                 kmem_free(kbuf, kbufsz);
 786                 return (set_errno(EFAULT));
 787         }
 788 
 789         kmem_free(kbuf, kbufsz);
 790 
 791         return (kbufsz);
 792 }
 793 
 794 static long
 795 rctlsys_ctl(char *name, rctl_opaque_t *rblk, int flags)
 796 {
 797         rctl_dict_entry_t *krde;
 798         rctl_opaque_t *krblk;
 799         char *kname;
 800         size_t klen;
 801 
 802         kname = kmem_alloc(MAXPATHLEN, KM_SLEEP);
 803 
 804         if (name == NULL || copyinstr(name, kname, MAXPATHLEN, &klen) != 0) {
 805                 kmem_free(kname, MAXPATHLEN);
 806                 return (set_errno(EFAULT));
 807         }
 808 
 809         switch (flags) {
 810         case RCTLCTL_GET:
 811                 krde = kmem_alloc(sizeof (rctl_dict_entry_t), KM_SLEEP);
 812                 krblk = kmem_zalloc(sizeof (rctl_opaque_t), KM_SLEEP);
 813 
 814                 if (rctl_global_get(kname, krde) == -1) {
 815                         kmem_free(krde, sizeof (rctl_dict_entry_t));
 816                         kmem_free(krblk, sizeof (rctl_opaque_t));
 817                         kmem_free(kname, MAXPATHLEN);
 818                         return (set_errno(ESRCH));
 819                 }
 820 
 821                 rctlsys_rblk_xfrm(krblk, krde, NULL, RBX_TO_BLK | RBX_CTL);
 822 
 823                 if (copyout(krblk, rblk, sizeof (rctl_opaque_t)) != 0) {
 824                         kmem_free(krde, sizeof (rctl_dict_entry_t));
 825                         kmem_free(krblk, sizeof (rctl_opaque_t));
 826                         kmem_free(kname, MAXPATHLEN);
 827                         return (set_errno(EFAULT));
 828                 }
 829 
 830                 kmem_free(krde, sizeof (rctl_dict_entry_t));
 831                 kmem_free(krblk, sizeof (rctl_opaque_t));
 832                 kmem_free(kname, MAXPATHLEN);
 833                 break;
 834         case RCTLCTL_SET:
 835                 if (secpolicy_rctlsys(CRED(), B_TRUE) != 0) {
 836                         kmem_free(kname, MAXPATHLEN);
 837                         return (set_errno(EPERM));
 838                 }
 839 
 840                 krde = kmem_alloc(sizeof (rctl_dict_entry_t), KM_SLEEP);
 841                 krblk = kmem_zalloc(sizeof (rctl_opaque_t), KM_SLEEP);
 842 
 843                 if (rctl_global_get(kname, krde) == -1) {
 844                         kmem_free(krde, sizeof (rctl_dict_entry_t));
 845                         kmem_free(krblk, sizeof (rctl_opaque_t));
 846                         kmem_free(kname, MAXPATHLEN);
 847                         return (set_errno(ESRCH));
 848                 }
 849 
 850                 if (copyin(rblk, krblk, sizeof (rctl_opaque_t)) != 0) {
 851                         kmem_free(krde, sizeof (rctl_dict_entry_t));
 852                         kmem_free(krblk, sizeof (rctl_opaque_t));
 853                         kmem_free(kname, MAXPATHLEN);
 854                         return (set_errno(EFAULT));
 855                 }
 856 
 857                 rctlsys_rblk_xfrm(krblk, krde, NULL, RBX_FROM_BLK | RBX_CTL);
 858 
 859                 if (rctl_global_set(kname, krde) == -1) {
 860                         kmem_free(krde, sizeof (rctl_dict_entry_t));
 861                         kmem_free(krblk, sizeof (rctl_opaque_t));
 862                         kmem_free(kname, MAXPATHLEN);
 863                         return (set_errno(ESRCH));
 864                 }
 865 
 866                 kmem_free(krde, sizeof (rctl_dict_entry_t));
 867                 kmem_free(krblk, sizeof (rctl_opaque_t));
 868                 kmem_free(kname, MAXPATHLEN);
 869 
 870                 break;
 871         default:
 872                 kmem_free(kname, MAXPATHLEN);
 873                 return (set_errno(EINVAL));
 874         }
 875 
 876         return (0);
 877 }
 878 
 879 /*
 880  * The arbitrary maximum number of rctl_opaque_t that we can pass to
 881  * rctl_projset().
 882  */
 883 #define RCTL_PROJSET_MAXSIZE    1024
 884 
 885 static long
 886 rctlsys_projset(char *name, rctl_opaque_t *rblk, size_t size, int flags)
 887 {
 888         rctl_dict_entry_t *krde;
 889         rctl_opaque_t *krblk;
 890         char *kname;
 891         size_t klen;
 892         rctl_hndl_t hndl;
 893         rctl_val_t *new_values = NULL;
 894         rctl_val_t *alloc_values = NULL;
 895         rctl_val_t *new_val;
 896         rctl_val_t *alloc_val;
 897         int error = 0;
 898         int count;
 899 
 900         kname = kmem_alloc(MAXPATHLEN, KM_SLEEP);
 901 
 902         if (name == NULL || copyinstr(name, kname, MAXPATHLEN, &klen) != 0) {
 903                 kmem_free(kname, MAXPATHLEN);
 904                 return (set_errno(EFAULT));
 905         }
 906 
 907         if (size > RCTL_PROJSET_MAXSIZE) {
 908                 kmem_free(kname, MAXPATHLEN);
 909                 return (set_errno(EINVAL));
 910         }
 911 
 912         if ((hndl = rctl_hndl_lookup(kname)) == -1) {
 913                 kmem_free(kname, MAXPATHLEN);
 914                 return (set_errno(EINVAL));
 915         }
 916 
 917         krde = rctl_dict_lookup_hndl(hndl);
 918 
 919         /* If not a project entity then exit */
 920         if ((krde->rcd_entity != RCENTITY_PROJECT) || (size <= 0)) {
 921                 kmem_free(kname, MAXPATHLEN);
 922                 return (set_errno(EINVAL));
 923         }
 924 
 925         if (secpolicy_rctlsys(CRED(), B_FALSE) != 0) {
 926                 kmem_free(kname, MAXPATHLEN);
 927                 return (set_errno(EPERM));
 928         }
 929 
 930         /* Allocate an array large enough for all resource control blocks */
 931         krblk = kmem_zalloc(sizeof (rctl_opaque_t) * size, KM_SLEEP);
 932 
 933         if (copyin(rblk, krblk, sizeof (rctl_opaque_t) * size) == 0) {
 934 
 935                 for (count = 0; (count < size) && (error == 0); count++) {
 936                         new_val = kmem_cache_alloc(rctl_val_cache, KM_SLEEP);
 937                         alloc_val = kmem_cache_alloc(rctl_val_cache, KM_SLEEP);
 938 
 939                         rctlsys_rblk_xfrm(&krblk[count], NULL, new_val,
 940                             RBX_FROM_BLK | RBX_VAL);
 941 
 942                         /*
 943                          * Project entity resource control values should always
 944                          * be privileged
 945                          */
 946                         if (new_val->rcv_privilege != RCPRIV_PRIVILEGED) {
 947                                 kmem_cache_free(rctl_val_cache, new_val);
 948                                 kmem_cache_free(rctl_val_cache, alloc_val);
 949 
 950                                 error = EPERM;
 951                         } else if (rctl_invalid_value(krde, new_val) == 0) {
 952 
 953                                 /*
 954                                  * This is a project entity; we do not set
 955                                  * rcv_action_recipient or rcv_action_recip_pid
 956                                  */
 957                                 new_val->rcv_action_recipient = NULL;
 958                                 new_val->rcv_action_recip_pid = -1;
 959                                 new_val->rcv_flagaction |= RCTL_LOCAL_PROJDB;
 960                                 new_val->rcv_firing_time = 0;
 961 
 962                                 new_val->rcv_prev = NULL;
 963                                 new_val->rcv_next = new_values;
 964                                 new_values = new_val;
 965 
 966                                 /*
 967                                  * alloc_val is left largely uninitialized, it
 968                                  * is a pre-allocated rctl_val_t which is used
 969                                  * later in rctl_local_replace_all() /
 970                                  * rctl_local_insert_all().
 971                                  */
 972                                 alloc_val->rcv_prev = NULL;
 973                                 alloc_val->rcv_next = alloc_values;
 974                                 alloc_values = alloc_val;
 975                         } else {
 976                                 kmem_cache_free(rctl_val_cache, new_val);
 977                                 kmem_cache_free(rctl_val_cache, alloc_val);
 978 
 979                                 error = EINVAL;
 980                         }
 981                 }
 982 
 983         } else {
 984                 error = EFAULT;
 985         }
 986 
 987         kmem_free(krblk, sizeof (rctl_opaque_t) * size);
 988         kmem_free(kname, MAXPATHLEN);
 989 
 990         if (error) {
 991                 /*
 992                  * We will have the same number of items in the alloc_values
 993                  * linked list, as we have in new_values.  However, we remain
 994                  * cautious, and teardown the linked lists individually.
 995                  */
 996                 while (new_values != NULL) {
 997                         new_val = new_values;
 998                         new_values = new_values->rcv_next;
 999                         kmem_cache_free(rctl_val_cache, new_val);
1000                 }
1001 
1002                 while (alloc_values != NULL) {
1003                         alloc_val = alloc_values;
1004                         alloc_values = alloc_values->rcv_next;
1005                         kmem_cache_free(rctl_val_cache, alloc_val);
1006                 }
1007 
1008                 return (set_errno(error));
1009         }
1010 
1011         /*
1012          * We take the p_lock here to maintain consistency with other functions
1013          * - rctlsys_get() and rctlsys_set()
1014          */
1015         mutex_enter(&curproc->p_lock);
1016         if (flags & TASK_PROJ_PURGE)  {
1017                 (void) rctl_local_replace_all(hndl, new_values, alloc_values,
1018                     curproc);
1019         } else {
1020                 (void) rctl_local_insert_all(hndl, new_values, alloc_values,
1021                     curproc);
1022         }
1023         mutex_exit(&curproc->p_lock);
1024 
1025         return (0);
1026 }
1027 
1028 long
1029 rctlsys(int code, char *name, void *obuf, void *nbuf, size_t obufsz, int flags)
1030 {
1031         switch (code) {
1032         case 0:
1033                 return (rctlsys_get(name, obuf, nbuf, flags));
1034 
1035         case 1:
1036                 return (rctlsys_set(name, obuf, nbuf, flags));
1037 
1038         case 2:
1039                 /*
1040                  * Private call for rctl_walk(3C).
1041                  */
1042                 return (rctlsys_lst(obuf, obufsz));
1043 
1044         case 3:
1045                 /*
1046                  * Private code for rctladm(1M):  "rctlctl".
1047                  */
1048                 return (rctlsys_ctl(name, obuf, flags));
1049         case 4:
1050                 /*
1051                  * Private code for setproject(3PROJECT).
1052                  */
1053                 return (rctlsys_projset(name, nbuf, obufsz, flags));
1054 
1055         default:
1056                 return (set_errno(EINVAL));
1057         }
1058 }