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 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * vnode ops for the /dev/pts directory 30 * The lookup is based on the internal pty table. We also 31 * override readdir in order to delete pts nodes no longer 32 * in use. 33 */ 34 35 #include <sys/types.h> 36 #include <sys/param.h> 37 #include <sys/sysmacros.h> 38 #include <sys/sunndi.h> 39 #include <fs/fs_subr.h> 40 #include <sys/fs/dv_node.h> 41 #include <sys/fs/sdev_impl.h> 42 #include <sys/policy.h> 43 #include <sys/ptms.h> 44 #include <sys/stat.h> 45 #include <sys/vfs_opreg.h> 46 47 #define DEVPTS_UID_DEFAULT 0 48 #define DEVPTS_GID_DEFAULT 3 49 #define DEVPTS_DEVMODE_DEFAULT (0620) 50 51 #define isdigit(ch) ((ch) >= '0' && (ch) <= '9') 52 53 static vattr_t devpts_vattr = { 54 AT_TYPE|AT_MODE|AT_UID|AT_GID, /* va_mask */ 55 VCHR, /* va_type */ 56 S_IFCHR | DEVPTS_DEVMODE_DEFAULT, /* va_mode */ 57 DEVPTS_UID_DEFAULT, /* va_uid */ 58 DEVPTS_GID_DEFAULT, /* va_gid */ 59 0 /* 0 hereafter */ 60 }; 61 62 struct vnodeops *devpts_vnodeops; 63 64 struct vnodeops * 65 devpts_getvnodeops(void) 66 { 67 return (devpts_vnodeops); 68 } 69 70 /* 71 * Convert string to minor number. Some care must be taken 72 * as we are processing user input. Catch cases like 73 * /dev/pts/4foo and /dev/pts/-1 74 */ 75 static int 76 devpts_strtol(const char *nm, minor_t *mp) 77 { 78 long uminor = 0; 79 char *endptr = NULL; 80 81 if (nm == NULL || !isdigit(*nm)) 82 return (EINVAL); 83 84 *mp = 0; 85 if (ddi_strtol(nm, &endptr, 10, &uminor) != 0 || 86 *endptr != '\0' || uminor < 0) { 87 return (EINVAL); 88 } 89 90 *mp = (minor_t)uminor; 91 return (0); 92 } 93 94 /* 95 * Check if a pts sdev_node is still valid - i.e. it represents a current pty. 96 * This serves two purposes 97 * - only valid pts nodes are returned during lookup() and readdir(). 98 * - since pts sdev_nodes are not actively destroyed when a pty goes 99 * away, we use the validator to do deferred cleanup i.e. when such 100 * nodes are encountered during subsequent lookup() and readdir(). 101 */ 102 /*ARGSUSED*/ 103 int 104 devpts_validate(struct sdev_node *dv) 105 { 106 minor_t min; 107 uid_t uid; 108 gid_t gid; 109 timestruc_t now; 110 char *nm = dv->sdev_name; 111 112 ASSERT(!(dv->sdev_flags & SDEV_STALE)); 113 ASSERT(dv->sdev_state == SDEV_READY); 114 115 /* validate only READY nodes */ 116 if (dv->sdev_state != SDEV_READY) { 117 sdcmn_err(("dev fs: skipping: node not ready %s(%p)", 118 nm, (void *)dv)); 119 return (SDEV_VTOR_SKIP); 120 } 121 122 if (devpts_strtol(nm, &min) != 0) { 123 sdcmn_err7(("devpts_validate: not a valid minor: %s\n", nm)); 124 return (SDEV_VTOR_INVALID); 125 } 126 127 /* 128 * Check if pts driver is attached 129 */ 130 if (ptms_slave_attached() == (major_t)-1) { 131 sdcmn_err7(("devpts_validate: slave not attached\n")); 132 return (SDEV_VTOR_INVALID); 133 } 134 135 if (ptms_minor_valid(min, &uid, &gid) == 0) { 136 if (ptms_minor_exists(min)) { 137 sdcmn_err7(("devpts_validate: valid in different zone " 138 "%s\n", nm)); 139 return (SDEV_VTOR_SKIP); 140 } else { 141 sdcmn_err7(("devpts_validate: %s not valid pty\n", 142 nm)); 143 return (SDEV_VTOR_INVALID); 144 } 145 } 146 147 ASSERT(dv->sdev_attr); 148 if (dv->sdev_attr->va_uid != uid || dv->sdev_attr->va_gid != gid) { 149 ASSERT(uid >= 0); 150 ASSERT(gid >= 0); 151 dv->sdev_attr->va_uid = uid; 152 dv->sdev_attr->va_gid = gid; 153 gethrestime(&now); 154 dv->sdev_attr->va_atime = now; 155 dv->sdev_attr->va_mtime = now; 156 dv->sdev_attr->va_ctime = now; 157 sdcmn_err7(("devpts_validate: update uid/gid/times%s\n", nm)); 158 } 159 160 return (SDEV_VTOR_VALID); 161 } 162 163 /* 164 * This callback is invoked from devname_lookup_func() to create 165 * a pts entry when the node is not found in the cache. 166 */ 167 /*ARGSUSED*/ 168 static int 169 devpts_create_rvp(struct sdev_node *ddv, char *nm, 170 void **arg, cred_t *cred, void *whatever, char *whichever) 171 { 172 minor_t min; 173 major_t maj; 174 uid_t uid; 175 gid_t gid; 176 timestruc_t now; 177 struct vattr *vap = (struct vattr *)arg; 178 179 if (devpts_strtol(nm, &min) != 0) { 180 sdcmn_err7(("devpts_create_rvp: not a valid minor: %s\n", nm)); 181 return (-1); 182 } 183 184 /* 185 * Check if pts driver is attached and if it is 186 * get the major number. 187 */ 188 maj = ptms_slave_attached(); 189 if (maj == (major_t)-1) { 190 sdcmn_err7(("devpts_create_rvp: slave not attached\n")); 191 return (-1); 192 } 193 194 /* 195 * Only allow creation of ptys allocated to our zone 196 */ 197 if (!ptms_minor_valid(min, &uid, &gid)) { 198 sdcmn_err7(("devpts_create_rvp: %s not valid pty" 199 "or not valid in this zone\n", nm)); 200 return (-1); 201 } 202 203 204 /* 205 * This is a valid pty (at least at this point in time). 206 * Create the node by setting the attribute. The rest 207 * is taken care of by devname_lookup_func(). 208 */ 209 *vap = devpts_vattr; 210 vap->va_rdev = makedevice(maj, min); 211 ASSERT(uid >= 0); 212 ASSERT(gid >= 0); 213 vap->va_uid = uid; 214 vap->va_gid = gid; 215 gethrestime(&now); 216 vap->va_atime = now; 217 vap->va_mtime = now; 218 vap->va_ctime = now; 219 220 return (0); 221 } 222 223 /* 224 * Clean pts sdev_nodes that are no longer valid. 225 */ 226 static void 227 devpts_prunedir(struct sdev_node *ddv) 228 { 229 struct vnode *vp; 230 struct sdev_node *dv, *next = NULL; 231 int (*vtor)(struct sdev_node *) = NULL; 232 233 ASSERT(ddv->sdev_flags & SDEV_VTOR); 234 235 vtor = (int (*)(struct sdev_node *))sdev_get_vtor(ddv); 236 ASSERT(vtor); 237 238 if (rw_tryupgrade(&ddv->sdev_contents) == NULL) { 239 rw_exit(&ddv->sdev_contents); 240 rw_enter(&ddv->sdev_contents, RW_WRITER); 241 } 242 243 for (dv = ddv->sdev_dot; dv; dv = next) { 244 next = dv->sdev_next; 245 246 /* skip stale nodes */ 247 if (dv->sdev_flags & SDEV_STALE) 248 continue; 249 250 /* validate and prune only ready nodes */ 251 if (dv->sdev_state != SDEV_READY) 252 continue; 253 254 switch (vtor(dv)) { 255 case SDEV_VTOR_VALID: 256 case SDEV_VTOR_SKIP: 257 continue; 258 case SDEV_VTOR_INVALID: 259 sdcmn_err7(("prunedir: destroy invalid " 260 "node: %s(%p)\n", dv->sdev_name, (void *)dv)); 261 break; 262 } 263 vp = SDEVTOV(dv); 264 if (vp->v_count > 0) 265 continue; 266 SDEV_HOLD(dv); 267 /* remove the cache node */ 268 (void) sdev_cache_update(ddv, &dv, dv->sdev_name, 269 SDEV_CACHE_DELETE); 270 } 271 rw_downgrade(&ddv->sdev_contents); 272 } 273 274 /* 275 * Lookup for /dev/pts directory 276 * If the entry does not exist, the devpts_create_rvp() callback 277 * is invoked to create it. Nodes do not persist across reboot. 278 * 279 * There is a potential denial of service here via 280 * fattach on top of a /dev/pts node - any permission changes 281 * applied to the node, apply to the fattached file and not 282 * to the underlying pts node. As a result when the previous 283 * user fdetaches, the pts node is still owned by the previous 284 * owner. To prevent this we don't allow fattach() on top of a pts 285 * node. This is done by a modification in the namefs filesystem 286 * where we check if the underlying node has the /dev/pts vnodeops. 287 * We do this via VOP_REALVP() on the underlying specfs node. 288 * sdev_nodes currently don't have a realvp. If a realvp is ever 289 * created for sdev_nodes, then VOP_REALVP() will return the 290 * actual realvp (possibly a ufs vnode). This will defeat the check 291 * in namefs code which checks if VOP_REALVP() returns a devpts 292 * node. We add an ASSERT here in /dev/pts lookup() to check for 293 * this condition. If sdev_nodes ever get a VOP_REALVP() entry point, 294 * change the code in the namefs filesystem code (in nm_mount()) to 295 * access the realvp of the specfs node directly instead of using 296 * VOP_REALVP(). 297 */ 298 /*ARGSUSED3*/ 299 static int 300 devpts_lookup(struct vnode *dvp, char *nm, struct vnode **vpp, 301 struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred) 302 { 303 struct sdev_node *sdvp = VTOSDEV(dvp); 304 struct sdev_node *dv; 305 struct vnode *rvp = NULL; 306 int error; 307 308 error = devname_lookup_func(sdvp, nm, vpp, cred, devpts_create_rvp, 309 SDEV_VATTR); 310 311 if (error == 0) { 312 switch ((*vpp)->v_type) { 313 case VCHR: 314 dv = VTOSDEV(VTOS(*vpp)->s_realvp); 315 ASSERT(VOP_REALVP(SDEVTOV(dv), &rvp) == ENOSYS); 316 break; 317 case VDIR: 318 dv = VTOSDEV(*vpp); 319 break; 320 default: 321 cmn_err(CE_PANIC, "devpts_lookup: Unsupported node " 322 "type: %p: %d", (void *)(*vpp), (*vpp)->v_type); 323 break; 324 } 325 ASSERT(SDEV_HELD(dv)); 326 } 327 328 return (error); 329 } 330 331 /* 332 * We allow create to find existing nodes 333 * - if the node doesn't exist - EROFS 334 * - creating an existing dir read-only succeeds, otherwise EISDIR 335 * - exclusive creates fail - EEXIST 336 */ 337 /*ARGSUSED2*/ 338 static int 339 devpts_create(struct vnode *dvp, char *nm, struct vattr *vap, vcexcl_t excl, 340 int mode, struct vnode **vpp, struct cred *cred, int flag) 341 { 342 int error; 343 struct vnode *vp; 344 345 *vpp = NULL; 346 347 error = devpts_lookup(dvp, nm, &vp, NULL, 0, NULL, cred); 348 if (error == 0) { 349 if (excl == EXCL) 350 error = EEXIST; 351 else if (vp->v_type == VDIR && (mode & VWRITE)) 352 error = EISDIR; 353 else 354 error = VOP_ACCESS(vp, mode, 0, cred); 355 356 if (error) { 357 VN_RELE(vp); 358 } else 359 *vpp = vp; 360 } else if (error == ENOENT) { 361 error = EROFS; 362 } 363 364 return (error); 365 } 366 367 /* 368 * Display all instantiated pts (slave) device nodes. 369 * A /dev/pts entry will be created only after the first lookup of the slave 370 * device succeeds. 371 */ 372 static int 373 devpts_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred, 374 int *eofp) 375 { 376 struct sdev_node *sdvp = VTOSDEV(dvp); 377 if (uiop->uio_offset == 0) { 378 devpts_prunedir(sdvp); 379 } 380 381 return (devname_readdir_func(dvp, uiop, cred, eofp, 0)); 382 } 383 384 385 static int 386 devpts_set_id(struct sdev_node *dv, struct vattr *vap, int protocol) 387 { 388 ASSERT((protocol & AT_UID) || (protocol & AT_GID)); 389 ptms_set_owner(getminor(SDEVTOV(dv)->v_rdev), 390 vap->va_uid, vap->va_gid); 391 return (0); 392 393 } 394 395 /*ARGSUSED4*/ 396 static int 397 devpts_setattr(struct vnode *vp, struct vattr *vap, int flags, 398 struct cred *cred, caller_context_t *ctp) 399 { 400 ASSERT((vp->v_type == VCHR) || (vp->v_type == VDIR)); 401 return (devname_setattr_func(vp, vap, flags, cred, 402 devpts_set_id, AT_UID|AT_GID)); 403 } 404 405 406 /* 407 * We override lookup and readdir to build entries based on the 408 * in kernel pty table. Also override setattr/setsecattr to 409 * avoid persisting permissions. 410 */ 411 const fs_operation_def_t devpts_vnodeops_tbl[] = { 412 VOPNAME_READDIR, { .vop_readdir = devpts_readdir }, 413 VOPNAME_LOOKUP, { .vop_lookup = devpts_lookup }, 414 VOPNAME_CREATE, { .vop_create = devpts_create }, 415 VOPNAME_SETATTR, { .vop_setattr = devpts_setattr }, 416 VOPNAME_REMOVE, { .error = fs_nosys }, 417 VOPNAME_MKDIR, { .error = fs_nosys }, 418 VOPNAME_RMDIR, { .error = fs_nosys }, 419 VOPNAME_SYMLINK, { .error = fs_nosys }, 420 VOPNAME_SETSECATTR, { .error = fs_nosys }, 421 NULL, NULL 422 }; 423