1 /* $NetBSD: kern_prot.c,v 1.89 2006/05/14 21:15:11 elad Exp $ */ 2 3 /* 4 * Copyright (c) 1982, 1986, 1989, 1990, 1991, 1993 5 * The Regents of the University of California. All rights reserved. 6 * (c) UNIX System Laboratories, Inc. 7 * All or some portions of this file are derived from material licensed 8 * to the University of California by American Telephone and Telegraph 9 * Co. or Unix System Laboratories, Inc. and are reproduced herein with 10 * the permission of UNIX System Laboratories, Inc. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * 36 * @(#)kern_prot.c 8.9 (Berkeley) 2/14/95 37 */ 38 39 /* 40 * System calls related to processes and protection 41 */ 42 43 #include <sys/cdefs.h> 44 __KERNEL_RCSID(0, "$NetBSD: kern_prot.c,v 1.89 2006/05/14 21:15:11 elad Exp $"); 45 46 #include "opt_compat_43.h" 47 48 #include <sys/param.h> 49 #include <sys/acct.h> 50 #include <sys/systm.h> 51 #include <sys/ucred.h> 52 #include <sys/proc.h> 53 #include <sys/timeb.h> 54 #include <sys/times.h> 55 #include <sys/pool.h> 56 #include <sys/syslog.h> 57 #include <sys/resourcevar.h> 58 #include <sys/kauth.h> 59 60 #include <sys/mount.h> 61 #include <sys/sa.h> 62 #include <sys/syscallargs.h> 63 64 #include <sys/malloc.h> 65 66 int sys_getpid(struct lwp *, void *, register_t *); 67 int sys_getpid_with_ppid(struct lwp *, void *, register_t *); 68 int sys_getuid(struct lwp *, void *, register_t *); 69 int sys_getuid_with_euid(struct lwp *, void *, register_t *); 70 int sys_getgid(struct lwp *, void *, register_t *); 71 int sys_getgid_with_egid(struct lwp *, void *, register_t *); 72 73 static int grsortu(gid_t *, int); 74 75 /* ARGSUSED */ 76 int 77 sys_getpid(struct lwp *l, void *v, register_t *retval) 78 { 79 struct proc *p = l->l_proc; 80 81 *retval = p->p_pid; 82 return (0); 83 } 84 85 /* ARGSUSED */ 86 int 87 sys_getpid_with_ppid(struct lwp *l, void *v, register_t *retval) 88 { 89 struct proc *p = l->l_proc; 90 91 retval[0] = p->p_pid; 92 retval[1] = p->p_pptr->p_pid; 93 return (0); 94 } 95 96 /* ARGSUSED */ 97 int 98 sys_getppid(struct lwp *l, void *v, register_t *retval) 99 { 100 struct proc *p = l->l_proc; 101 102 *retval = p->p_pptr->p_pid; 103 return (0); 104 } 105 106 /* Get process group ID; note that POSIX getpgrp takes no parameter */ 107 int 108 sys_getpgrp(struct lwp *l, void *v, register_t *retval) 109 { 110 struct proc *p = l->l_proc; 111 112 *retval = p->p_pgrp->pg_id; 113 return (0); 114 } 115 116 /* 117 * Return the process group ID of the session leader (session ID) 118 * for the specified process. 119 */ 120 int 121 sys_getsid(struct lwp *l, void *v, register_t *retval) 122 { 123 struct sys_getsid_args /* { 124 syscalldarg(pid_t) pid; 125 } */ *uap = v; 126 struct proc *p = l->l_proc; 127 128 if (SCARG(uap, pid) == 0) 129 goto found; 130 if ((p = pfind(SCARG(uap, pid))) == 0) 131 return (ESRCH); 132 found: 133 *retval = p->p_session->s_sid; 134 return (0); 135 } 136 137 int 138 sys_getpgid(struct lwp *l, void *v, register_t *retval) 139 { 140 struct sys_getpgid_args /* { 141 syscallarg(pid_t) pid; 142 } */ *uap = v; 143 struct proc *p = l->l_proc; 144 145 if (SCARG(uap, pid) == 0) 146 goto found; 147 if ((p = pfind(SCARG(uap, pid))) == 0) 148 return (ESRCH); 149 found: 150 *retval = p->p_pgid; 151 return (0); 152 } 153 154 /* ARGSUSED */ 155 int 156 sys_getuid(struct lwp *l, void *v, register_t *retval) 157 { 158 struct proc *p = l->l_proc; 159 160 *retval = kauth_cred_getuid(p->p_cred); 161 return (0); 162 } 163 164 /* ARGSUSED */ 165 int 166 sys_getuid_with_euid(struct lwp *l, void *v, register_t *retval) 167 { 168 struct proc *p = l->l_proc; 169 170 retval[0] = kauth_cred_getuid(p->p_cred); 171 retval[1] = kauth_cred_geteuid(p->p_cred); 172 return (0); 173 } 174 175 /* ARGSUSED */ 176 int 177 sys_geteuid(struct lwp *l, void *v, register_t *retval) 178 { 179 struct proc *p = l->l_proc; 180 181 *retval = kauth_cred_geteuid(p->p_cred); 182 return (0); 183 } 184 185 /* ARGSUSED */ 186 int 187 sys_getgid(struct lwp *l, void *v, register_t *retval) 188 { 189 struct proc *p = l->l_proc; 190 191 *retval = kauth_cred_getgid(p->p_cred); 192 return (0); 193 } 194 195 /* ARGSUSED */ 196 int 197 sys_getgid_with_egid(struct lwp *l, void *v, register_t *retval) 198 { 199 struct proc *p = l->l_proc; 200 201 retval[0] = kauth_cred_getgid(p->p_cred); 202 retval[1] = kauth_cred_getegid(p->p_cred); 203 return (0); 204 } 205 206 /* 207 * Get effective group ID. The "egid" is groups[0], and could be obtained 208 * via getgroups. This syscall exists because it is somewhat painful to do 209 * correctly in a library function. 210 */ 211 /* ARGSUSED */ 212 int 213 sys_getegid(struct lwp *l, void *v, register_t *retval) 214 { 215 struct proc *p = l->l_proc; 216 217 *retval = kauth_cred_getegid(p->p_cred); 218 return (0); 219 } 220 221 int 222 sys_getgroups(struct lwp *l, void *v, register_t *retval) 223 { 224 struct sys_getgroups_args /* { 225 syscallarg(int) gidsetsize; 226 syscallarg(gid_t *) gidset; 227 } */ *uap = v; 228 struct proc *p = l->l_proc; 229 kauth_cred_t pc = p->p_cred; 230 u_int ngrp; 231 int error; 232 gid_t *grbuf; 233 234 if (SCARG(uap, gidsetsize) == 0) { 235 *retval = kauth_cred_ngroups(pc); 236 return (0); 237 } else if (SCARG(uap, gidsetsize) < 0) 238 return (EINVAL); 239 ngrp = SCARG(uap, gidsetsize); 240 if (ngrp < kauth_cred_ngroups(pc)) 241 return (EINVAL); 242 ngrp = kauth_cred_ngroups(pc); 243 244 grbuf = malloc(ngrp * sizeof(*grbuf), M_TEMP, M_WAITOK); 245 kauth_cred_getgroups(pc, grbuf, ngrp); 246 error = copyout(grbuf, (caddr_t)SCARG(uap, gidset), 247 ngrp * sizeof(gid_t)); 248 free(grbuf, M_TEMP); 249 if (error) 250 return (error); 251 *retval = ngrp; 252 return (0); 253 } 254 255 /* ARGSUSED */ 256 int 257 sys_setsid(struct lwp *l, void *v, register_t *retval) 258 { 259 struct proc *p = l->l_proc; 260 261 if (p->p_pgid == p->p_pid || pgfind(p->p_pid)) { 262 return (EPERM); 263 } else { 264 (void)enterpgrp(p, p->p_pid, 1); 265 *retval = p->p_pid; 266 return (0); 267 } 268 } 269 270 /* 271 * set process group (setpgid/old setpgrp) 272 * 273 * caller does setpgid(targpid, targpgid) 274 * 275 * pgid must be in valid range (EINVAL) 276 * pid must be caller or child of caller (ESRCH) 277 * if a child 278 * pid must be in same session (EPERM) 279 * pid can't have done an exec (EACCES) 280 * if pgid != pid 281 * there must exist some pid in same session having pgid (EPERM) 282 * pid must not be session leader (EPERM) 283 * 284 * Permission checks now in enterpgrp() 285 */ 286 /* ARGSUSED */ 287 int 288 sys_setpgid(struct lwp *l, void *v, register_t *retval) 289 { 290 struct sys_setpgid_args /* { 291 syscallarg(int) pid; 292 syscallarg(int) pgid; 293 } */ *uap = v; 294 struct proc *curp = l->l_proc; 295 struct proc *targp; /* target process */ 296 297 if (SCARG(uap, pgid) < 0) 298 return EINVAL; 299 300 /* XXX MP - there is a horrid race here with targp exiting! */ 301 if (SCARG(uap, pid) != 0 && SCARG(uap, pid) != curp->p_pid) { 302 targp = pfind(SCARG(uap, pid)); 303 if (targp == NULL) 304 return ESRCH; 305 } else 306 targp = curp; 307 308 if (SCARG(uap, pgid) == 0) 309 SCARG(uap, pgid) = targp->p_pid; 310 return enterpgrp(targp, SCARG(uap, pgid), 0); 311 } 312 313 /* 314 * Set real, effective and saved uids to the requested values. 315 * non-root callers can only ever change uids to values that match 316 * one of the processes current uid values. 317 * This is further restricted by the flags argument. 318 */ 319 320 int 321 do_setresuid(struct lwp *l, uid_t r, uid_t e, uid_t sv, u_int flags) 322 { 323 int error; 324 struct proc *p = l->l_proc; 325 kauth_cred_t cred = p->p_cred; 326 327 /* Superuser can do anything it wants to.... */ 328 error = kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, &p->p_acflag); 329 if (error) { 330 /* Otherwise check new value is one of the allowed 331 existing values. */ 332 if (r != -1 && !((flags & ID_R_EQ_R) && r == kauth_cred_getuid(cred)) 333 && !((flags & ID_R_EQ_E) && r == kauth_cred_geteuid(cred)) 334 && !((flags & ID_R_EQ_S) && r == kauth_cred_getsvuid(cred))) 335 return error; 336 if (e != -1 && !((flags & ID_E_EQ_R) && e == kauth_cred_getuid(cred)) 337 && !((flags & ID_E_EQ_E) && e == kauth_cred_geteuid(cred)) 338 && !((flags & ID_E_EQ_S) && e == kauth_cred_getsvuid(cred))) 339 return error; 340 if (sv != -1 && !((flags & ID_S_EQ_R) && sv == kauth_cred_getuid(cred)) 341 && !((flags & ID_S_EQ_E) && sv == kauth_cred_geteuid(cred)) 342 && !((flags & ID_S_EQ_S) && sv == kauth_cred_getsvuid(cred))) 343 return error; 344 } 345 346 /* If nothing has changed, short circuit the request */ 347 if ((r == -1 || r == kauth_cred_getuid(cred)) 348 && (e == -1 || e == kauth_cred_geteuid(cred)) 349 && (sv == -1 || sv == kauth_cred_getsvuid(cred))) 350 /* nothing to do */ 351 return 0; 352 353 /* The pcred structure is not actually shared... */ 354 if (r != -1 && r != kauth_cred_getuid(cred)) { 355 /* Update count of processes for this user */ 356 (void)chgproccnt(kauth_cred_getuid(cred), -1); 357 (void)chgproccnt(r, 1); 358 kauth_cred_setuid(cred, r); 359 } 360 if (sv != -1) 361 kauth_cred_setsvuid(cred, sv); 362 if (e != -1 && e != kauth_cred_geteuid(cred)) { 363 /* Update a clone of the current credentials */ 364 cred = kauth_cred_copy(cred); 365 kauth_cred_seteuid(cred, e); 366 p->p_cred = cred; 367 } 368 369 /* Mark process as having changed credentials, stops tracing etc */ 370 p_sugid(p); 371 return 0; 372 } 373 374 /* 375 * Set real, effective and saved gids to the requested values. 376 * non-root callers can only ever change gids to values that match 377 * one of the processes current gid values. 378 * This is further restricted by the flags argument. 379 */ 380 381 int 382 do_setresgid(struct lwp *l, gid_t r, gid_t e, gid_t sv, u_int flags) 383 { 384 int error; 385 struct proc *p = l->l_proc; 386 kauth_cred_t cred = p->p_cred; 387 388 /* Superuser can do anything it wants to.... */ 389 error = kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, &p->p_acflag); 390 if (error) { 391 /* Otherwise check new value is one of the allowed 392 existing values. */ 393 if (r != -1 && !((flags & ID_R_EQ_R) && r == kauth_cred_getgid(cred)) 394 && !((flags & ID_R_EQ_E) && r == kauth_cred_getegid(cred)) 395 && !((flags & ID_R_EQ_S) && r == kauth_cred_getsvgid(cred))) 396 return error; 397 if (e != -1 && !((flags & ID_E_EQ_R) && e == kauth_cred_getgid(cred)) 398 && !((flags & ID_E_EQ_E) && e == kauth_cred_getegid(cred)) 399 && !((flags & ID_E_EQ_S) && e == kauth_cred_getsvgid(cred))) 400 return error; 401 if (sv != -1 && !((flags & ID_S_EQ_R) && sv == kauth_cred_getgid(cred)) 402 && !((flags & ID_S_EQ_E) && sv == kauth_cred_getegid(cred)) 403 && !((flags & ID_S_EQ_S) && sv == kauth_cred_getsvgid(cred))) 404 return error; 405 } 406 407 /* If nothing has changed, short circuit the request */ 408 if ((r == -1 || r == kauth_cred_getgid(cred)) 409 && (e == -1 || e == kauth_cred_getegid(cred)) 410 && (sv == -1 || sv == kauth_cred_getsvgid(cred))) 411 /* nothing to do */ 412 return 0; 413 414 /* The pcred structure is not actually shared... */ 415 if (r != -1) 416 kauth_cred_setgid(cred, r); 417 if (sv != -1) 418 kauth_cred_setsvgid(cred, sv); 419 if (e != -1 && e != kauth_cred_getegid(cred)) { 420 /* Update a clone of the current credentials */ 421 cred = kauth_cred_copy(cred); 422 kauth_cred_setegid(cred, e); 423 p->p_cred = cred; 424 } 425 426 /* Mark process as having changed credentials, stops tracing etc */ 427 p_sugid(p); 428 return 0; 429 } 430 431 /* ARGSUSED */ 432 int 433 sys_setuid(struct lwp *l, void *v, register_t *retval) 434 { 435 struct sys_setuid_args /* { 436 syscallarg(uid_t) uid; 437 } */ *uap = v; 438 uid_t uid = SCARG(uap, uid); 439 440 return do_setresuid(l, uid, uid, uid, 441 ID_R_EQ_R | ID_E_EQ_R | ID_S_EQ_R); 442 } 443 444 /* ARGSUSED */ 445 int 446 sys_seteuid(struct lwp *l, void *v, register_t *retval) 447 { 448 struct sys_seteuid_args /* { 449 syscallarg(uid_t) euid; 450 } */ *uap = v; 451 452 return do_setresuid(l, -1, SCARG(uap, euid), -1, ID_E_EQ_R | ID_E_EQ_S); 453 } 454 455 int 456 sys_setreuid(struct lwp *l, void *v, register_t *retval) 457 { 458 struct sys_setreuid_args /* { 459 syscallarg(uid_t) ruid; 460 syscallarg(uid_t) euid; 461 } */ *uap = v; 462 struct proc *p = l->l_proc; 463 uid_t ruid, euid, svuid; 464 465 ruid = SCARG(uap, ruid); 466 euid = SCARG(uap, euid); 467 if (ruid == -1) 468 ruid = kauth_cred_getuid(p->p_cred); 469 if (euid == -1) 470 euid = kauth_cred_geteuid(p->p_cred); 471 /* Saved uid is set to the new euid if the ruid changed */ 472 svuid = (ruid == kauth_cred_getuid(p->p_cred)) ? -1 : euid; 473 474 return do_setresuid(l, ruid, euid, svuid, 475 ID_R_EQ_R | ID_R_EQ_E | 476 ID_E_EQ_R | ID_E_EQ_E | ID_E_EQ_S | 477 ID_S_EQ_R | ID_S_EQ_E | ID_S_EQ_S); 478 } 479 480 /* ARGSUSED */ 481 int 482 sys_setgid(struct lwp *l, void *v, register_t *retval) 483 { 484 struct sys_setgid_args /* { 485 syscallarg(gid_t) gid; 486 } */ *uap = v; 487 gid_t gid = SCARG(uap, gid); 488 489 return do_setresgid(l, gid, gid, gid, 490 ID_R_EQ_R | ID_E_EQ_R | ID_S_EQ_R); 491 } 492 493 /* ARGSUSED */ 494 int 495 sys_setegid(struct lwp *l, void *v, register_t *retval) 496 { 497 struct sys_setegid_args /* { 498 syscallarg(gid_t) egid; 499 } */ *uap = v; 500 501 return do_setresgid(l, -1, SCARG(uap, egid), -1, ID_E_EQ_R | ID_E_EQ_S); 502 } 503 504 int 505 sys_setregid(struct lwp *l, void *v, register_t *retval) 506 { 507 struct sys_setregid_args /* { 508 syscallarg(gid_t) rgid; 509 syscallarg(gid_t) egid; 510 } */ *uap = v; 511 struct proc *p = l->l_proc; 512 gid_t rgid, egid, svgid; 513 514 rgid = SCARG(uap, rgid); 515 egid = SCARG(uap, egid); 516 if (rgid == -1) 517 rgid = kauth_cred_getgid(p->p_cred); 518 if (egid == -1) 519 egid = kauth_cred_getegid(p->p_cred); 520 /* Saved gid is set to the new egid if the rgid changed */ 521 svgid = rgid == kauth_cred_getgid(p->p_cred) ? -1 : egid; 522 523 return do_setresgid(l, rgid, egid, svgid, 524 ID_R_EQ_R | ID_R_EQ_E | 525 ID_E_EQ_R | ID_E_EQ_E | ID_E_EQ_S | 526 ID_S_EQ_R | ID_S_EQ_E | ID_S_EQ_S); 527 } 528 529 int 530 sys_issetugid(struct lwp *l, void *v, register_t *retval) 531 { 532 struct proc *p = l->l_proc; 533 534 /* 535 * Note: OpenBSD sets a P_SUGIDEXEC flag set at execve() time, 536 * we use P_SUGID because we consider changing the owners as 537 * "tainting" as well. 538 * This is significant for procs that start as root and "become" 539 * a user without an exec - programs cannot know *everything* 540 * that libc *might* have put in their data segment. 541 */ 542 *retval = (p->p_flag & P_SUGID) != 0; 543 return (0); 544 } 545 546 /* 547 * sort -u for groups. 548 */ 549 static int 550 grsortu(gid_t *grp, int ngrp) 551 { 552 const gid_t *src, *end; 553 gid_t *dst; 554 gid_t group; 555 int i, j; 556 557 /* bubble sort */ 558 for (i = 0; i < ngrp; i++) 559 for (j = i + 1; j < ngrp; j++) 560 if (grp[i] > grp[j]) { 561 gid_t tmp = grp[i]; 562 grp[i] = grp[j]; 563 grp[j] = tmp; 564 } 565 566 /* uniq */ 567 end = grp + ngrp; 568 src = grp; 569 dst = grp; 570 while (src < end) { 571 group = *src++; 572 while (src < end && *src == group) 573 src++; 574 *dst++ = group; 575 } 576 577 #ifdef DIAGNOSTIC 578 /* zero out the rest of the array */ 579 (void)memset(dst, 0, sizeof(*grp) * (end - dst)); 580 #endif 581 582 return dst - grp; 583 } 584 585 /* ARGSUSED */ 586 int 587 sys_setgroups(struct lwp *l, void *v, register_t *retval) 588 { 589 struct sys_setgroups_args /* { 590 syscallarg(int) gidsetsize; 591 syscallarg(const gid_t *) gidset; 592 } */ *uap = v; 593 struct proc *p = l->l_proc; 594 kauth_cred_t pc = p->p_cred; 595 int ngrp; 596 int error; 597 gid_t grp[NGROUPS]; 598 size_t grsize; 599 600 if ((error = kauth_authorize_generic(pc, KAUTH_GENERIC_ISSUSER, 601 &p->p_acflag)) != 0) 602 return (error); 603 604 ngrp = SCARG(uap, gidsetsize); 605 if ((u_int)ngrp > NGROUPS) 606 return (EINVAL); 607 608 grsize = ngrp * sizeof(gid_t); 609 error = copyin(SCARG(uap, gidset), grp, grsize); 610 if (error) 611 return (error); 612 613 ngrp = grsortu(grp, ngrp); 614 615 pc = kauth_cred_copy(pc); 616 p->p_cred = pc; 617 618 kauth_cred_setgroups(p->p_cred, grp, ngrp, -1); 619 620 p_sugid(p); 621 return (0); 622 } 623 624 /* 625 * Get login name, if available. 626 */ 627 /* ARGSUSED */ 628 int 629 sys___getlogin(struct lwp *l, void *v, register_t *retval) 630 { 631 struct sys___getlogin_args /* { 632 syscallarg(char *) namebuf; 633 syscallarg(size_t) namelen; 634 } */ *uap = v; 635 struct proc *p = l->l_proc; 636 637 if (SCARG(uap, namelen) > sizeof(p->p_pgrp->pg_session->s_login)) 638 SCARG(uap, namelen) = sizeof(p->p_pgrp->pg_session->s_login); 639 return (copyout((caddr_t) p->p_pgrp->pg_session->s_login, 640 (caddr_t) SCARG(uap, namebuf), SCARG(uap, namelen))); 641 } 642 643 /* 644 * Set login name. 645 */ 646 /* ARGSUSED */ 647 int 648 sys___setlogin(struct lwp *l, void *v, register_t *retval) 649 { 650 struct sys___setlogin_args /* { 651 syscallarg(const char *) namebuf; 652 } */ *uap = v; 653 struct proc *p = l->l_proc; 654 struct session *s = p->p_pgrp->pg_session; 655 char newname[sizeof s->s_login + 1]; 656 int error; 657 658 if ((error = kauth_authorize_generic(p->p_cred, KAUTH_GENERIC_ISSUSER, 659 &p->p_acflag)) != 0) 660 return (error); 661 error = copyinstr(SCARG(uap, namebuf), &newname, sizeof newname, NULL); 662 if (error != 0) 663 return (error == ENAMETOOLONG ? EINVAL : error); 664 665 if (s->s_flags & S_LOGIN_SET && p->p_pid != s->s_sid && 666 strncmp(newname, s->s_login, sizeof s->s_login) != 0) 667 log(LOG_WARNING, "%s (pid %d) changing logname from " 668 "%.*s to %s\n", p->p_comm, p->p_pid, 669 (int)sizeof s->s_login, s->s_login, newname); 670 s->s_flags |= S_LOGIN_SET; 671 strncpy(s->s_login, newname, sizeof s->s_login); 672 return (0); 673 } 674