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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 /* 31 * Portions of this source code were derived from Berkeley 4.3 BSD 32 * under license from the Regents of the University of California. 33 */ 34 35 #pragma ident "%Z%%M% %I% %E% SMI" 36 37 #include <sys/param.h> 38 #include <sys/isa_defs.h> 39 #include <sys/types.h> 40 #include <sys/sysmacros.h> 41 #include <sys/cred.h> 42 #include <sys/user.h> 43 #include <sys/systm.h> 44 #include <sys/errno.h> 45 #include <sys/fcntl.h> 46 #include <sys/pathname.h> 47 #include <sys/var.h> 48 #include <sys/vfs.h> 49 #include <sys/vnode.h> 50 #include <sys/file.h> 51 #include <sys/mode.h> 52 #include <sys/proc.h> 53 #include <sys/uio.h> 54 #include <sys/ioreq.h> 55 #include <sys/poll.h> 56 #include <sys/kmem.h> 57 #include <sys/filio.h> 58 #include <sys/cmn_err.h> 59 #include <sys/policy.h> 60 #include <sys/zone.h> 61 62 #include <sys/debug.h> 63 #include <c2/audit.h> 64 65 /* 66 * Change current working directory ("."). 67 */ 68 static int chdirec(vnode_t *, int ischroot, int do_traverse); 69 70 int 71 chdir(char *fname) 72 { 73 vnode_t *vp; 74 int error; 75 76 lookup: 77 if (error = lookupname(fname, UIO_USERSPACE, FOLLOW, NULLVPP, &vp)) { 78 if (error == ESTALE) 79 goto lookup; 80 return (set_errno(error)); 81 } 82 83 error = chdirec(vp, 0, 1); 84 if (error) { 85 if (error == ESTALE) 86 goto lookup; 87 return (set_errno(error)); 88 } 89 return (0); 90 } 91 92 /* 93 * File-descriptor based version of 'chdir'. 94 */ 95 int 96 fchdir(int fd) 97 { 98 vnode_t *vp; 99 file_t *fp; 100 int error; 101 102 if ((fp = getf(fd)) == NULL) 103 return (set_errno(EBADF)); 104 vp = fp->f_vnode; 105 VN_HOLD(vp); 106 releasef(fd); 107 error = chdirec(vp, 0, 0); 108 if (error) 109 return (set_errno(error)); 110 return (0); 111 } 112 113 /* 114 * Change notion of root ("/") directory. 115 */ 116 int 117 chroot(char *fname) 118 { 119 vnode_t *vp; 120 int error; 121 122 lookup: 123 if (error = lookupname(fname, UIO_USERSPACE, FOLLOW, NULLVPP, &vp)) { 124 if (error == ESTALE) 125 goto lookup; 126 return (set_errno(error)); 127 } 128 129 error = chdirec(vp, 1, 1); 130 if (error) { 131 if (error == ESTALE) 132 goto lookup; 133 return (set_errno(error)); 134 } 135 return (0); 136 } 137 138 /* 139 * ++++++++++++++++++++++++ 140 * ++ SunOS4.1 Buyback ++ 141 * ++++++++++++++++++++++++ 142 * Change root directory with a user given fd 143 */ 144 int 145 fchroot(int fd) 146 { 147 vnode_t *vp; 148 file_t *fp; 149 int error; 150 151 if ((fp = getf(fd)) == NULL) 152 return (set_errno(EBADF)); 153 vp = fp->f_vnode; 154 VN_HOLD(vp); 155 releasef(fd); 156 error = chdirec(vp, 1, 0); 157 if (error) 158 return (set_errno(error)); 159 return (0); 160 } 161 162 static int 163 chdirec(vnode_t *vp, int ischroot, int do_traverse) 164 { 165 int error; 166 vnode_t *oldvp; 167 proc_t *pp = curproc; 168 vnode_t **vpp; 169 refstr_t *cwd; 170 int newcwd = 1; 171 172 if (vp->v_type != VDIR) { 173 error = ENOTDIR; 174 goto bad; 175 } 176 if (error = VOP_ACCESS(vp, VEXEC, 0, CRED())) 177 goto bad; 178 179 /* 180 * The VOP_ACCESS() may have covered 'vp' with a new filesystem, 181 * if 'vp' is an autoFS vnode. Traverse the mountpoint so 182 * that we don't end up with a covered current directory. 183 */ 184 if (vn_mountedvfs(vp) != NULL && do_traverse) { 185 if (error = traverse(&vp)) 186 goto bad; 187 } 188 189 /* 190 * Special chroot semantics: chroot is allowed if privileged 191 * or if the target is really a loopback mount of the root (or 192 * root of the zone) as determined by comparing dev and inode 193 * numbers 194 */ 195 if (ischroot) { 196 struct vattr tattr; 197 struct vattr rattr; 198 vnode_t *zonevp = curproc->p_zone->zone_rootvp; 199 200 tattr.va_mask = AT_FSID|AT_NODEID; 201 if (error = VOP_GETATTR(vp, &tattr, 0, CRED())) 202 goto bad; 203 204 rattr.va_mask = AT_FSID|AT_NODEID; 205 if (error = VOP_GETATTR(zonevp, &rattr, 0, CRED())) 206 goto bad; 207 208 if ((tattr.va_fsid != rattr.va_fsid || 209 tattr.va_nodeid != rattr.va_nodeid) && 210 (error = secpolicy_chroot(CRED())) != 0) 211 goto bad; 212 213 vpp = &PTOU(pp)->u_rdir; 214 } else { 215 vpp = &PTOU(pp)->u_cdir; 216 } 217 218 #ifdef C2_AUDIT 219 if (audit_active) /* update abs cwd/root path see c2audit.c */ 220 audit_chdirec(vp, vpp); 221 #endif 222 223 mutex_enter(&pp->p_lock); 224 /* 225 * This bit of logic prevents us from overwriting u_cwd if we are 226 * changing to the same directory. We set the cwd to NULL so that we 227 * don't try to do the lookup on the next call to getcwd(). 228 */ 229 if (!ischroot && *vpp != NULL && vp != NULL && VN_CMP(*vpp, vp)) 230 newcwd = 0; 231 232 oldvp = *vpp; 233 *vpp = vp; 234 if ((cwd = PTOU(pp)->u_cwd) != NULL && newcwd) 235 PTOU(pp)->u_cwd = NULL; 236 mutex_exit(&pp->p_lock); 237 238 if (cwd && newcwd) 239 refstr_rele(cwd); 240 if (oldvp) 241 VN_RELE(oldvp); 242 return (0); 243 244 bad: 245 VN_RELE(vp); 246 return (error); 247 } 248