/*ARGSUSED*/ static int nca_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp) { /* Reopen supported */ if (q->q_ptr != NULL) return (0); /* * NCA is not supported in non-global zones; we enforce this restriction * here. */ if (credp != NULL && crgetzoneid(credp) != GLOBAL_ZONEID) { return (ENOTSUP); } if (! (sflag & MODOPEN)) { /* Device instance */ RD(q)->q_ptr = (void *)B_TRUE; WR(q)->q_ptr = (void *)B_TRUE; } else { /* Modopen just pass through */ RD(q)->q_ptr = (void *)B_FALSE; WR(q)->q_ptr = (void *)B_FALSE; } qprocson(q); return (0); }
/* * Implements flow walk ioctl. * Retrieves a specific flow or a list of flows from the specified link. * ENOSPC is returned a bigger buffer is needed. */ int dld_walk_flow(dld_ioc_walkflow_t *wf, intptr_t uaddr, cred_t *credp) { flowinfo_state_t state; mac_flowinfo_t finfo; int err = 0; /* For now, one can only view flows from the global zone. */ if (crgetzoneid(credp) != GLOBAL_ZONEID) return (EPERM); state.fi_bufsize = wf->wf_len; state.fi_fl = (uchar_t *)uaddr + sizeof (*wf); state.fi_nflows = 0; if (wf->wf_name[0] == '\0') { err = mac_link_flow_walk(wf->wf_linkid, dld_walk_flow_cb, &state); } else { err = mac_link_flow_info(wf->wf_name, &finfo); if (err != 0) return (err); err = dld_walk_flow_cb(&finfo, &state); } wf->wf_nflows = state.fi_nflows; return (err); }
/* * Find a stack instance given the cred. * This is used by the modules to potentially allow for a future when * something other than the zoneid is used to determine the stack. */ netstack_t * netstack_find_by_cred(const cred_t *cr) { zoneid_t zoneid = crgetzoneid(cr); /* Handle the case when cr_zone is NULL */ if (zoneid == (zoneid_t)-1) zoneid = GLOBAL_ZONEID; /* For performance ... */ if (curproc->p_zone->zone_id == zoneid) return (netstack_get_current()); else return (netstack_find_by_zoneid(zoneid)); }
/* * pfp_open_index is an internal function used to open a MAC device by * its index. Both a mac_handle_t and mac_client_handle_t are acquired * because some of the interfaces provided by the mac layer require either * only the mac_handle_t or both it and mac_handle_t. * * Whilst inside the kernel we can access data structures supporting any * zone, access to interfaces from non-global zones is restricted to those * interfaces (if any) that are exclusively assigned to a zone. */ static int pfp_open_index(int index, mac_handle_t *mhp, mac_client_handle_t *mcip, cred_t *cred) { mac_client_handle_t mch; zoneid_t ifzoneid; mac_handle_t mh; zoneid_t zoneid; int error; mh = 0; mch = 0; error = mac_open_by_linkid(index, &mh); if (error != 0) goto bad_open; error = mac_client_open(mh, &mch, NULL, MAC_OPEN_FLAGS_USE_DATALINK_NAME); if (error != 0) goto bad_open; zoneid = crgetzoneid(cred); if (zoneid != GLOBAL_ZONEID) { mac_perim_handle_t perim; mac_perim_enter_by_mh(mh, &perim); error = dls_link_getzid(mac_client_name(mch), &ifzoneid); mac_perim_exit(perim); if (error != 0) goto bad_open; if (ifzoneid != zoneid) { error = EACCES; goto bad_open; } } *mcip = mch; *mhp = mh; return (0); bad_open: if (mch != 0) mac_client_close(mch, 0); if (mh != 0) mac_close(mh); return (error); }
/* * Convert a credential into a "ucred". Allow the caller to specify * and aligned buffer, e.g., in an mblk, so we don't have to allocate * memory and copy it twice. * * This function may call cred2ucaud(), which calls CRED(). Since this * can be called from an interrupt thread, receiver's cred (rcr) is needed * to determine whether audit info should be included. */ struct ucred_s * cred2ucred(const cred_t *cr, pid_t pid, void *buf, const cred_t *rcr) { struct ucred_s *uc; uint32_t realsz = ucredminsize(cr); ts_label_t *tslp = is_system_labeled() ? crgetlabel(cr) : NULL; /* The structure isn't always completely filled in, so zero it */ if (buf == NULL) { uc = kmem_zalloc(realsz, KM_SLEEP); } else { bzero(buf, realsz); uc = buf; } uc->uc_size = realsz; uc->uc_pid = pid; uc->uc_projid = cr->cr_projid; uc->uc_zoneid = crgetzoneid(cr); if (REMOTE_PEER_CRED(cr)) { /* * Other than label, the rest of cred info about a * remote peer isn't available. Copy the label directly * after the header where we generally copy the prcred. * That's why we use sizeof (struct ucred_s). The other * offset fields are initialized to 0. */ uc->uc_labeloff = tslp == NULL ? 0 : sizeof (struct ucred_s); } else { uc->uc_credoff = UCRED_CRED_OFF; uc->uc_privoff = UCRED_PRIV_OFF; uc->uc_audoff = UCRED_AUD_OFF; uc->uc_labeloff = tslp == NULL ? 0 : UCRED_LABEL_OFF; cred2prcred(cr, UCCRED(uc)); cred2prpriv(cr, UCPRIV(uc)); if (audoff == 0 || cred2ucaud(cr, UCAUD(uc), rcr) != 0) uc->uc_audoff = 0; } if (tslp != NULL) bcopy(&tslp->tsl_label, UCLABEL(uc), sizeof (bslabel_t)); return (uc); }
/*ARGSUSED*/ static int nsmb_open(dev_t *dev, int flags, int otyp, cred_t *cr) { smb_dev_t *sdp; minor_t m; mutex_enter(&dev_lck); for (m = last_minor + 1; m != last_minor; m++) { if (m > NSMB_MAX_MINOR) m = NSMB_MIN_MINOR; if (ddi_get_soft_state(statep, m) == NULL) { last_minor = m; goto found; } } /* No available minor units. */ mutex_exit(&dev_lck); return (ENXIO); found: /* NB: dev_lck still held */ if (ddi_soft_state_zalloc(statep, m) == DDI_FAILURE) { mutex_exit(&dev_lck); return (ENXIO); } if ((sdp = ddi_get_soft_state(statep, m)) == NULL) { mutex_exit(&dev_lck); return (ENXIO); } *dev = makedevice(nsmb_major, m); mutex_exit(&dev_lck); sdp->sd_cred = cr; sdp->sd_smbfid = -1; sdp->sd_flags |= NSMBFL_OPEN; sdp->zoneid = crgetzoneid(cr); return (0); }
int vnic_info(vnic_info_t *info, cred_t *credp) { vnic_t *vnic; int err; /* Make sure that the VNIC link is visible from the caller's zone. */ if (!dls_devnet_islinkvisible(info->vn_vnic_id, crgetzoneid(credp))) return (ENOENT); rw_enter(&vnic_lock, RW_WRITER); err = mod_hash_find(vnic_hash, VNIC_HASH_KEY(info->vn_vnic_id), (mod_hash_val_t *)&vnic); if (err != 0) { rw_exit(&vnic_lock); return (ENOENT); } info->vn_link_id = vnic->vn_link_id; info->vn_mac_addr_type = vnic->vn_addr_type; info->vn_mac_len = vnic->vn_addr_len; bcopy(vnic->vn_addr, info->vn_mac_addr, MAXMACADDRLEN); info->vn_mac_slot = vnic->vn_slot_id; info->vn_mac_prefix_len = 0; info->vn_vid = vnic->vn_vid; info->vn_force = vnic->vn_force; info->vn_vrid = vnic->vn_vrid; info->vn_af = vnic->vn_af; bzero(&info->vn_resource_props, sizeof (mac_resource_props_t)); if (vnic->vn_mch != NULL) mac_client_get_resources(vnic->vn_mch, &info->vn_resource_props); rw_exit(&vnic_lock); return (0); }
/*ARGSUSED*/ static int lo_mount(struct vfs *vfsp, struct vnode *vp, struct mounta *uap, struct cred *cr) { int error; struct vnode *srootvp = NULL; /* the server's root */ struct vnode *realrootvp; struct loinfo *li; int nodev; nodev = vfs_optionisset(vfsp, MNTOPT_NODEVICES, NULL); if ((error = secpolicy_fs_mount(cr, vp, vfsp)) != 0) return (EPERM); /* * Loopback devices which get "nodevices" added can be done without * "nodevices" set because we cannot import devices into a zone * with loopback. Note that we have all zone privileges when * this happens; if not, we'd have gotten "nosuid". */ if (!nodev && vfs_optionisset(vfsp, MNTOPT_NODEVICES, NULL)) vfs_setmntopt(vfsp, MNTOPT_DEVICES, NULL, VFS_NODISPLAY); mutex_enter(&vp->v_lock); if (!(uap->flags & MS_OVERLAY) && (vp->v_count != 1 || (vp->v_flag & VROOT))) { mutex_exit(&vp->v_lock); return (EBUSY); } mutex_exit(&vp->v_lock); /* * Find real root, and make vfs point to real vfs */ if (error = lookupname(uap->spec, (uap->flags & MS_SYSSPACE) ? UIO_SYSSPACE : UIO_USERSPACE, FOLLOW, NULLVPP, &realrootvp)) return (error); /* * Enforce MAC policy if needed. * * Loopback mounts must not allow writing up. The dominance test * is intended to prevent a global zone caller from accidentally * creating write-up conditions between two labeled zones. * Local zones can't violate MAC on their own without help from * the global zone because they can't name a pathname that * they don't already have. * * The special case check for the NET_MAC_AWARE process flag is * to support the case of the automounter in the global zone. We * permit automounting of local zone directories such as home * directories, into the global zone as required by setlabel, * zonecopy, and saving of desktop sessions. Such mounts are * trusted not to expose the contents of one zone's directories * to another by leaking them through the global zone. */ if (is_system_labeled() && crgetzoneid(cr) == GLOBAL_ZONEID) { char specname[MAXPATHLEN]; zone_t *from_zptr; zone_t *to_zptr; if (vnodetopath(NULL, realrootvp, specname, sizeof (specname), CRED()) != 0) { VN_RELE(realrootvp); return (EACCES); } from_zptr = zone_find_by_path(specname); to_zptr = zone_find_by_path(refstr_value(vfsp->vfs_mntpt)); /* * Special case for zone devfs: the zone for /dev will * incorrectly appear as the global zone since it's not * under the zone rootpath. So for zone devfs check allow * read-write mounts. * * Second special case for scratch zones used for Live Upgrade: * this is used to mount the zone's root from /root to /a in * the scratch zone. As with the other special case, this * appears to be outside of the zone because it's not under * the zone rootpath, which is $ZONEPATH/lu in the scratch * zone case. */ if (from_zptr != to_zptr && !(to_zptr->zone_flags & ZF_IS_SCRATCH)) { /* * We know at this point that the labels aren't equal * because the zone pointers aren't equal, and zones * can't share a label. * * If the source is the global zone then making * it available to a local zone must be done in * read-only mode as the label will become admin_low. * * If it is a mount between local zones then if * the current process is in the global zone and has * the NET_MAC_AWARE flag, then regular read-write * access is allowed. If it's in some other zone, but * the label on the mount point dominates the original * source, then allow the mount as read-only * ("read-down"). */ if (from_zptr->zone_id == GLOBAL_ZONEID) { /* make the mount read-only */ vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 0); } else { /* cross-zone mount */ if (to_zptr->zone_id == GLOBAL_ZONEID && /* LINTED: no consequent */ getpflags(NET_MAC_AWARE, cr) != 0) { /* Allow the mount as read-write */ } else if (bldominates( label2bslabel(to_zptr->zone_slabel), label2bslabel(from_zptr->zone_slabel))) { /* make the mount read-only */ vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 0); } else { VN_RELE(realrootvp); zone_rele(to_zptr); zone_rele(from_zptr); return (EACCES); } } } zone_rele(to_zptr); zone_rele(from_zptr); } /* * realrootvp may be an AUTOFS node, in which case we * perform a VOP_ACCESS() to trigger the mount of the * intended filesystem, so we loopback mount the intended * filesystem instead of the AUTOFS filesystem. */ (void) VOP_ACCESS(realrootvp, 0, 0, cr, NULL); /* * We're interested in the top most filesystem. * This is specially important when uap->spec is a trigger * AUTOFS node, since we're really interested in mounting the * filesystem AUTOFS mounted as result of the VOP_ACCESS() * call not the AUTOFS node itself. */ if (vn_mountedvfs(realrootvp) != NULL) { if (error = traverse(&realrootvp)) { VN_RELE(realrootvp); return (error); } } /* * Allocate a vfs info struct and attach it */ li = kmem_zalloc(sizeof (struct loinfo), KM_SLEEP); li->li_realvfs = realrootvp->v_vfsp; li->li_mountvfs = vfsp; /* * Set mount flags to be inherited by loopback vfs's */ if (vfs_optionisset(vfsp, MNTOPT_RO, NULL)) { li->li_mflag |= VFS_RDONLY; } if (vfs_optionisset(vfsp, MNTOPT_NOSUID, NULL)) { li->li_mflag |= (VFS_NOSETUID|VFS_NODEVICES); } if (vfs_optionisset(vfsp, MNTOPT_NODEVICES, NULL)) { li->li_mflag |= VFS_NODEVICES; } if (vfs_optionisset(vfsp, MNTOPT_NOSETUID, NULL)) { li->li_mflag |= VFS_NOSETUID; } /* * Permissive flags are added to the "deny" bitmap. */ if (vfs_optionisset(vfsp, MNTOPT_NOXATTR, NULL)) { li->li_dflag |= VFS_XATTR; } if (vfs_optionisset(vfsp, MNTOPT_NONBMAND, NULL)) { li->li_dflag |= VFS_NBMAND; } /* * Propagate inheritable mount flags from the real vfs. */ if ((li->li_realvfs->vfs_flag & VFS_RDONLY) && !vfs_optionisset(vfsp, MNTOPT_RO, NULL)) vfs_setmntopt(vfsp, MNTOPT_RO, NULL, VFS_NODISPLAY); if ((li->li_realvfs->vfs_flag & VFS_NOSETUID) && !vfs_optionisset(vfsp, MNTOPT_NOSETUID, NULL)) vfs_setmntopt(vfsp, MNTOPT_NOSETUID, NULL, VFS_NODISPLAY); if ((li->li_realvfs->vfs_flag & VFS_NODEVICES) && !vfs_optionisset(vfsp, MNTOPT_NODEVICES, NULL)) vfs_setmntopt(vfsp, MNTOPT_NODEVICES, NULL, VFS_NODISPLAY); /* * Permissive flags such as VFS_XATTR, as opposed to restrictive flags * such as VFS_RDONLY, are handled differently. An explicit * MNTOPT_NOXATTR should override the underlying filesystem's VFS_XATTR. */ if ((li->li_realvfs->vfs_flag & VFS_XATTR) && !vfs_optionisset(vfsp, MNTOPT_NOXATTR, NULL) && !vfs_optionisset(vfsp, MNTOPT_XATTR, NULL)) vfs_setmntopt(vfsp, MNTOPT_XATTR, NULL, VFS_NODISPLAY); if ((li->li_realvfs->vfs_flag & VFS_NBMAND) && !vfs_optionisset(vfsp, MNTOPT_NBMAND, NULL) && !vfs_optionisset(vfsp, MNTOPT_NONBMAND, NULL)) vfs_setmntopt(vfsp, MNTOPT_NBMAND, NULL, VFS_NODISPLAY); li->li_refct = 0; vfsp->vfs_data = (caddr_t)li; vfsp->vfs_bcount = 0; vfsp->vfs_fstype = lofsfstype; vfsp->vfs_bsize = li->li_realvfs->vfs_bsize; vfsp->vfs_dev = li->li_realvfs->vfs_dev; vfsp->vfs_fsid.val[0] = li->li_realvfs->vfs_fsid.val[0]; vfsp->vfs_fsid.val[1] = li->li_realvfs->vfs_fsid.val[1]; if (vfs_optionisset(vfsp, MNTOPT_LOFS_NOSUB, NULL)) { li->li_flag |= LO_NOSUB; } /* * Propagate any VFS features */ vfs_propagate_features(li->li_realvfs, vfsp); /* * Setup the hashtable. If the root of this mount isn't a directory, * there's no point in allocating a large hashtable. A table with one * bucket is sufficient. */ if (realrootvp->v_type != VDIR) lsetup(li, 1); else lsetup(li, 0); /* * Make the root vnode */ srootvp = makelonode(realrootvp, li, 0); srootvp->v_flag |= VROOT; li->li_rootvp = srootvp; #ifdef LODEBUG lo_dprint(4, "lo_mount: vfs %p realvfs %p root %p realroot %p li %p\n", vfsp, li->li_realvfs, srootvp, realrootvp, li); #endif return (0); }
/* ARGSUSED */ int vnic_dev_delete(datalink_id_t vnic_id, uint32_t flags, cred_t *credp) { vnic_t *vnic = NULL; mod_hash_val_t val; datalink_id_t tmpid; int rc; rw_enter(&vnic_lock, RW_WRITER); if (mod_hash_find(vnic_hash, VNIC_HASH_KEY(vnic_id), (mod_hash_val_t *)&vnic) != 0) { rw_exit(&vnic_lock); return (ENOENT); } if ((rc = dls_devnet_destroy(vnic->vn_mh, &tmpid, B_TRUE)) != 0) { rw_exit(&vnic_lock); return (rc); } ASSERT(vnic_id == tmpid); /* * We cannot unregister the MAC yet. Unregistering would * free up mac_impl_t which should not happen at this time. * So disable mac_impl_t by calling mac_disable(). This will prevent * any new claims on mac_impl_t. */ if ((rc = mac_disable(vnic->vn_mh)) != 0) { (void) dls_devnet_create(vnic->vn_mh, vnic_id, crgetzoneid(credp)); rw_exit(&vnic_lock); return (rc); } vnic_cleanup_secondary_macs(vnic, vnic->vn_nhandles); vnic->vn_enabled = B_FALSE; (void) mod_hash_remove(vnic_hash, VNIC_HASH_KEY(vnic_id), &val); ASSERT(vnic == (vnic_t *)val); vnic_count--; rw_exit(&vnic_lock); /* * XXX-nicolas shouldn't have a void cast here, if it's * expected that the function will never fail, then we should * have an ASSERT(). */ (void) mac_unregister(vnic->vn_mh); if (vnic->vn_lower_mh != NULL) { /* * Check if MAC address for the vnic was obtained from the * factory MAC addresses. If yes, release it. */ if (vnic->vn_addr_type == VNIC_MAC_ADDR_TYPE_FACTORY) { (void) mac_addr_factory_release(vnic->vn_mch, vnic->vn_slot_id); } (void) mac_margin_remove(vnic->vn_lower_mh, vnic->vn_margin); (void) mac_mtu_remove(vnic->vn_lower_mh, vnic->vn_mtu); (void) mac_notify_remove(vnic->vn_mnh, B_TRUE); (void) mac_unicast_remove(vnic->vn_mch, vnic->vn_muh); mac_client_close(vnic->vn_mch, MAC_CLOSE_FLAGS_IS_VNIC); mac_close(vnic->vn_lower_mh); } kmem_cache_free(vnic_cache, vnic); return (0); }
/* ARGSUSED */ int vnic_dev_create(datalink_id_t vnic_id, datalink_id_t linkid, vnic_mac_addr_type_t *vnic_addr_type, int *mac_len, uchar_t *mac_addr, int *mac_slot, uint_t mac_prefix_len, uint16_t vid, vrid_t vrid, int af, mac_resource_props_t *mrp, uint32_t flags, vnic_ioc_diag_t *diag, cred_t *credp) { vnic_t *vnic; mac_register_t *mac; int err; boolean_t is_anchor = ((flags & VNIC_IOC_CREATE_ANCHOR) != 0); char vnic_name[MAXNAMELEN]; const mac_info_t *minfop; uint32_t req_hwgrp_flag = B_FALSE; *diag = VNIC_IOC_DIAG_NONE; rw_enter(&vnic_lock, RW_WRITER); /* does a VNIC with the same id already exist? */ err = mod_hash_find(vnic_hash, VNIC_HASH_KEY(vnic_id), (mod_hash_val_t *)&vnic); if (err == 0) { rw_exit(&vnic_lock); return (EEXIST); } vnic = kmem_cache_alloc(vnic_cache, KM_NOSLEEP); if (vnic == NULL) { rw_exit(&vnic_lock); return (ENOMEM); } bzero(vnic, sizeof (*vnic)); vnic->vn_id = vnic_id; vnic->vn_link_id = linkid; vnic->vn_vrid = vrid; vnic->vn_af = af; if (!is_anchor) { if (linkid == DATALINK_INVALID_LINKID) { err = EINVAL; goto bail; } /* * Open the lower MAC and assign its initial bandwidth and * MAC address. We do this here during VNIC creation and * do not wait until the upper MAC client open so that we * can validate the VNIC creation parameters (bandwidth, * MAC address, etc) and reserve a factory MAC address if * one was requested. */ err = mac_open_by_linkid(linkid, &vnic->vn_lower_mh); if (err != 0) goto bail; /* * VNIC(vlan) over VNICs(vlans) is not supported. */ if (mac_is_vnic(vnic->vn_lower_mh)) { err = EINVAL; goto bail; } /* only ethernet support for now */ minfop = mac_info(vnic->vn_lower_mh); if (minfop->mi_nativemedia != DL_ETHER) { err = ENOTSUP; goto bail; } (void) dls_mgmt_get_linkinfo(vnic_id, vnic_name, NULL, NULL, NULL); err = mac_client_open(vnic->vn_lower_mh, &vnic->vn_mch, vnic_name, MAC_OPEN_FLAGS_IS_VNIC); if (err != 0) goto bail; /* assign a MAC address to the VNIC */ err = vnic_unicast_add(vnic, *vnic_addr_type, mac_slot, mac_prefix_len, mac_len, mac_addr, flags, diag, vid, req_hwgrp_flag); if (err != 0) { vnic->vn_muh = NULL; if (diag != NULL && req_hwgrp_flag) *diag = VNIC_IOC_DIAG_NO_HWRINGS; goto bail; } /* register to receive notification from underlying MAC */ vnic->vn_mnh = mac_notify_add(vnic->vn_lower_mh, vnic_notify_cb, vnic); *vnic_addr_type = vnic->vn_addr_type; vnic->vn_addr_len = *mac_len; vnic->vn_vid = vid; bcopy(mac_addr, vnic->vn_addr, vnic->vn_addr_len); if (vnic->vn_addr_type == VNIC_MAC_ADDR_TYPE_FACTORY) vnic->vn_slot_id = *mac_slot; /* * Set the initial VNIC capabilities. If the VNIC is created * over MACs which does not support nactive vlan, disable * VNIC's hardware checksum capability if its VID is not 0, * since the underlying MAC would get the hardware checksum * offset wrong in case of VLAN packets. */ if (vid == 0 || !mac_capab_get(vnic->vn_lower_mh, MAC_CAPAB_NO_NATIVEVLAN, NULL)) { if (!mac_capab_get(vnic->vn_lower_mh, MAC_CAPAB_HCKSUM, &vnic->vn_hcksum_txflags)) vnic->vn_hcksum_txflags = 0; } else { vnic->vn_hcksum_txflags = 0; } } /* register with the MAC module */ if ((mac = mac_alloc(MAC_VERSION)) == NULL) goto bail; mac->m_type_ident = MAC_PLUGIN_IDENT_ETHER; mac->m_driver = vnic; mac->m_dip = vnic_get_dip(); mac->m_instance = (uint_t)-1; mac->m_src_addr = vnic->vn_addr; mac->m_callbacks = &vnic_m_callbacks; if (!is_anchor) { /* * If this is a VNIC based VLAN, then we check for the * margin unless it has been created with the force * flag. If we are configuring a VLAN over an etherstub, * we don't check the margin even if force is not set. */ if (vid == 0 || (flags & VNIC_IOC_CREATE_FORCE) != 0) { if (vid != VLAN_ID_NONE) vnic->vn_force = B_TRUE; /* * As the current margin size of the underlying mac is * used to determine the margin size of the VNIC * itself, request the underlying mac not to change * to a smaller margin size. */ err = mac_margin_add(vnic->vn_lower_mh, &vnic->vn_margin, B_TRUE); ASSERT(err == 0); } else { vnic->vn_margin = VLAN_TAGSZ; err = mac_margin_add(vnic->vn_lower_mh, &vnic->vn_margin, B_FALSE); if (err != 0) { mac_free(mac); if (diag != NULL) *diag = VNIC_IOC_DIAG_MACMARGIN_INVALID; goto bail; } } mac_sdu_get(vnic->vn_lower_mh, &mac->m_min_sdu, &mac->m_max_sdu); err = mac_mtu_add(vnic->vn_lower_mh, &mac->m_max_sdu, B_FALSE); if (err != 0) { VERIFY(mac_margin_remove(vnic->vn_lower_mh, vnic->vn_margin) == 0); mac_free(mac); if (diag != NULL) *diag = VNIC_IOC_DIAG_MACMTU_INVALID; goto bail; } vnic->vn_mtu = mac->m_max_sdu; } else { vnic->vn_margin = VLAN_TAGSZ; mac->m_min_sdu = 1; mac->m_max_sdu = ANCHOR_VNIC_MAX_MTU; vnic->vn_mtu = ANCHOR_VNIC_MAX_MTU; } mac->m_margin = vnic->vn_margin; err = mac_register(mac, &vnic->vn_mh); mac_free(mac); if (err != 0) { if (!is_anchor) { VERIFY(mac_mtu_remove(vnic->vn_lower_mh, vnic->vn_mtu) == 0); VERIFY(mac_margin_remove(vnic->vn_lower_mh, vnic->vn_margin) == 0); } goto bail; } /* Set the VNIC's MAC in the client */ if (!is_anchor) { mac_set_upper_mac(vnic->vn_mch, vnic->vn_mh, mrp); if (mrp != NULL) { if ((mrp->mrp_mask & MRP_RX_RINGS) != 0 || (mrp->mrp_mask & MRP_TX_RINGS) != 0) { req_hwgrp_flag = B_TRUE; } err = mac_client_set_resources(vnic->vn_mch, mrp); if (err != 0) { VERIFY(mac_mtu_remove(vnic->vn_lower_mh, vnic->vn_mtu) == 0); VERIFY(mac_margin_remove(vnic->vn_lower_mh, vnic->vn_margin) == 0); (void) mac_unregister(vnic->vn_mh); goto bail; } } } err = dls_devnet_create(vnic->vn_mh, vnic->vn_id, crgetzoneid(credp)); if (err != 0) { VERIFY(is_anchor || mac_margin_remove(vnic->vn_lower_mh, vnic->vn_margin) == 0); if (!is_anchor) { VERIFY(mac_mtu_remove(vnic->vn_lower_mh, vnic->vn_mtu) == 0); VERIFY(mac_margin_remove(vnic->vn_lower_mh, vnic->vn_margin) == 0); } (void) mac_unregister(vnic->vn_mh); goto bail; } /* add new VNIC to hash table */ err = mod_hash_insert(vnic_hash, VNIC_HASH_KEY(vnic_id), (mod_hash_val_t)vnic); ASSERT(err == 0); vnic_count++; /* * Now that we've enabled this VNIC, we should go through and update the * link state by setting it to our parents. */ vnic->vn_enabled = B_TRUE; if (is_anchor) { mac_link_update(vnic->vn_mh, LINK_STATE_UP); } else { mac_link_update(vnic->vn_mh, mac_client_stat_get(vnic->vn_mch, MAC_STAT_LINK_STATE)); } rw_exit(&vnic_lock); return (0); bail: rw_exit(&vnic_lock); if (!is_anchor) { if (vnic->vn_mnh != NULL) (void) mac_notify_remove(vnic->vn_mnh, B_TRUE); if (vnic->vn_muh != NULL) (void) mac_unicast_remove(vnic->vn_mch, vnic->vn_muh); if (vnic->vn_mch != NULL) mac_client_close(vnic->vn_mch, MAC_CLOSE_FLAGS_IS_VNIC); if (vnic->vn_lower_mh != NULL) mac_close(vnic->vn_lower_mh); } kmem_cache_free(vnic_cache, vnic); return (err); }
static int log_wput(queue_t *q, mblk_t *mp) { log_t *lp = (log_t *)q->q_ptr; struct iocblk *iocp; mblk_t *mp2; cred_t *cr = DB_CRED(mp); zoneid_t zoneid; /* * Default to global zone if dblk doesn't have a valid cred. * Calls to syslog() go through putmsg(), which does set up * the cred. */ zoneid = (cr != NULL) ? crgetzoneid(cr) : GLOBAL_ZONEID; switch (DB_TYPE(mp)) { case M_FLUSH: if (*mp->b_rptr & FLUSHW) { flushq(q, FLUSHALL); *mp->b_rptr &= ~FLUSHW; } if (*mp->b_rptr & FLUSHR) { flushq(RD(q), FLUSHALL); qreply(q, mp); return (0); } break; case M_IOCTL: iocp = (struct iocblk *)mp->b_rptr; if (lp->log_major != LOG_LOGMIN) { /* write-only device */ miocnak(q, mp, 0, EINVAL); return (0); } if (iocp->ioc_count == TRANSPARENT) { miocnak(q, mp, 0, EINVAL); return (0); } if (lp->log_flags) { miocnak(q, mp, 0, EBUSY); return (0); } freemsg(lp->log_data); lp->log_data = mp->b_cont; mp->b_cont = NULL; switch (iocp->ioc_cmd) { case I_CONSLOG: log_update(lp, RD(q), SL_CONSOLE, log_console); break; case I_TRCLOG: if (lp->log_data == NULL) { miocnak(q, mp, 0, EINVAL); return (0); } log_update(lp, RD(q), SL_TRACE, log_trace); break; case I_ERRLOG: log_update(lp, RD(q), SL_ERROR, log_error); break; default: miocnak(q, mp, 0, EINVAL); return (0); } miocack(q, mp, 0, 0); return (0); case M_PROTO: if (MBLKL(mp) == sizeof (log_ctl_t) && mp->b_cont != NULL) { log_ctl_t *lc = (log_ctl_t *)mp->b_rptr; /* This code is used by savecore to log dump msgs */ if (mp->b_band != 0 && secpolicy_sys_config(CRED(), B_FALSE) == 0) { (void) putq(log_consq, mp); return (0); } if ((lc->pri & LOG_FACMASK) == LOG_KERN) lc->pri |= LOG_USER; mp2 = log_makemsg(LOG_MID, LOG_CONSMIN, lc->level, lc->flags, lc->pri, mp->b_cont->b_rptr, MBLKL(mp->b_cont) + 1, 0); if (mp2 != NULL) log_sendmsg(mp2, zoneid); } break; case M_DATA: mp2 = log_makemsg(LOG_MID, LOG_CONSMIN, 0, SL_CONSOLE, LOG_USER | LOG_INFO, mp->b_rptr, MBLKL(mp) + 1, 0); if (mp2 != NULL) log_sendmsg(mp2, zoneid); break; } freemsg(mp); return (0); }
/*ARGSUSED*/ static int nsmb_open(dev_t *dev, int flags, int otyp, cred_t *cr) { major_t new_major; smb_dev_t *sdp, *sdv; mutex_enter(&dev_lck); for (; ; ) { minor_t start = nsmb_minor; do { if (nsmb_minor >= MAXMIN32) { if (nsmb_major == getmajor(*dev)) nsmb_minor = NSMB_MIN_MINOR; else nsmb_minor = 0; } else { nsmb_minor++; } sdv = ddi_get_soft_state(statep, nsmb_minor); } while ((sdv != NULL) && (nsmb_minor != start)); if (nsmb_minor == start) { /* * The condition we need to solve here is all the * MAXMIN32(~262000) minors numbers are reached. We * need to create a new major number. * zfs uses getudev() to create a new major number. */ if ((new_major = getudev()) == (major_t)-1) { cmn_err(CE_WARN, "nsmb: Can't get unique major " "device number."); mutex_exit(&dev_lck); return (-1); } nsmb_major = new_major; nsmb_minor = 0; } else { break; } } /* * This is called by mount or open call. * The open() routine is passed a pointer to a device number so * that the driver can change the minor number. This allows * drivers to dynamically create minor instances of the dev- * ice. An example of this might be a pseudo-terminal driver * that creates a new pseudo-terminal whenever it is opened. * A driver that chooses the minor number dynamically, normally * creates only one minor device node in attach(9E) with * ddi_create_minor_node(9F) then changes the minor number com- * ponent of *devp using makedevice(9F) and getmajor(9F) The * driver needs to keep track of available minor numbers inter- * nally. * Stuff the structure smb_dev. * return. */ if (ddi_soft_state_zalloc(statep, nsmb_minor) == DDI_FAILURE) { mutex_exit(&dev_lck); return (ENXIO); } if ((sdp = ddi_get_soft_state(statep, nsmb_minor)) == NULL) { mutex_exit(&dev_lck); return (ENXIO); } sdp->sd_seq = nsmb_minor; sdp->sd_cred = cr; sdp->sd_smbfid = -1; sdp->sd_flags |= NSMBFL_OPEN; sdp->zoneid = crgetzoneid(cr); mutex_exit(&dev_lck); *dev = makedevice(nsmb_major, nsmb_minor); return (0); }
/* * Remove zombie children from the process table. */ void freeproc(proc_t *p) { proc_t *q; task_t *tk; ASSERT(p->p_stat == SZOMB); ASSERT(p->p_tlist == NULL); ASSERT(MUTEX_HELD(&pidlock)); sigdelq(p, NULL, 0); if (p->p_killsqp) { siginfofree(p->p_killsqp); p->p_killsqp = NULL; } prfree(p); /* inform /proc */ /* * Don't free the init processes. * Other dying processes will access it. */ if (p == proc_init) return; /* * We wait until now to free the cred structure because a * zombie process's credentials may be examined by /proc. * No cred locking needed because there are no threads at this point. */ upcount_dec(crgetruid(p->p_cred), crgetzoneid(p->p_cred)); crfree(p->p_cred); if (p->p_corefile != NULL) { corectl_path_rele(p->p_corefile); p->p_corefile = NULL; } if (p->p_content != NULL) { corectl_content_rele(p->p_content); p->p_content = NULL; } if (p->p_nextofkin && !((p->p_nextofkin->p_flag & SNOWAIT) || (PTOU(p->p_nextofkin)->u_signal[SIGCLD - 1] == SIG_IGN))) { /* * This should still do the right thing since p_utime/stime * get set to the correct value on process exit, so it * should get properly updated */ p->p_nextofkin->p_cutime += p->p_utime; p->p_nextofkin->p_cstime += p->p_stime; p->p_nextofkin->p_cacct[LMS_USER] += p->p_acct[LMS_USER]; p->p_nextofkin->p_cacct[LMS_SYSTEM] += p->p_acct[LMS_SYSTEM]; p->p_nextofkin->p_cacct[LMS_TRAP] += p->p_acct[LMS_TRAP]; p->p_nextofkin->p_cacct[LMS_TFAULT] += p->p_acct[LMS_TFAULT]; p->p_nextofkin->p_cacct[LMS_DFAULT] += p->p_acct[LMS_DFAULT]; p->p_nextofkin->p_cacct[LMS_KFAULT] += p->p_acct[LMS_KFAULT]; p->p_nextofkin->p_cacct[LMS_USER_LOCK] += p->p_acct[LMS_USER_LOCK]; p->p_nextofkin->p_cacct[LMS_SLEEP] += p->p_acct[LMS_SLEEP]; p->p_nextofkin->p_cacct[LMS_WAIT_CPU] += p->p_acct[LMS_WAIT_CPU]; p->p_nextofkin->p_cacct[LMS_STOPPED] += p->p_acct[LMS_STOPPED]; p->p_nextofkin->p_cru.minflt += p->p_ru.minflt; p->p_nextofkin->p_cru.majflt += p->p_ru.majflt; p->p_nextofkin->p_cru.nswap += p->p_ru.nswap; p->p_nextofkin->p_cru.inblock += p->p_ru.inblock; p->p_nextofkin->p_cru.oublock += p->p_ru.oublock; p->p_nextofkin->p_cru.msgsnd += p->p_ru.msgsnd; p->p_nextofkin->p_cru.msgrcv += p->p_ru.msgrcv; p->p_nextofkin->p_cru.nsignals += p->p_ru.nsignals; p->p_nextofkin->p_cru.nvcsw += p->p_ru.nvcsw; p->p_nextofkin->p_cru.nivcsw += p->p_ru.nivcsw; p->p_nextofkin->p_cru.sysc += p->p_ru.sysc; p->p_nextofkin->p_cru.ioch += p->p_ru.ioch; } q = p->p_nextofkin; if (q && q->p_orphan == p) q->p_orphan = p->p_nextorph; else if (q) { for (q = q->p_orphan; q; q = q->p_nextorph) if (q->p_nextorph == p) break; ASSERT(q && q->p_nextorph == p); q->p_nextorph = p->p_nextorph; } /* * The process table slot is being freed, so it is now safe to give up * task and project membership. */ mutex_enter(&p->p_lock); tk = p->p_task; task_detach(p); mutex_exit(&p->p_lock); proc_detach(p); pid_exit(p, tk); /* frees pid and proc structure */ task_rele(tk); }
/* * sppp_dl_attach_upper() * * MT-Perimeters: * exclusive inner, exclusive outer. * * Description: * Called by qwriter (INNER) from sppp_dlattachreq as the result of * receiving a DL_ATTACH_REQ message. */ static void sppp_dl_attach_upper(queue_t *q, mblk_t *mp) { sppa_t *ppa; spppstr_t *sps = q->q_ptr; union DL_primitives *dlp; int err = ENOMEM; cred_t *cr; zoneid_t zoneid; ASSERT(!IS_SPS_PIOATTACH(sps)); dlp = (union DL_primitives *)mp->b_rptr; /* If there's something here, it's detached. */ if (sps->sps_ppa != NULL) { sppp_remove_ppa(sps); } if ((cr = msg_getcred(mp, NULL)) == NULL) zoneid = sps->sps_zoneid; else zoneid = crgetzoneid(cr); ppa = sppp_find_ppa(dlp->attach_req.dl_ppa); if (ppa == NULL) { ppa = sppp_create_ppa(dlp->attach_req.dl_ppa, zoneid); } else if (ppa->ppa_zoneid != zoneid) { ppa = NULL; err = EPERM; } /* * If we can't find or create it, then it's either because we're out of * memory or because the requested PPA is owned by a different zone. */ if (ppa == NULL) { DBGERROR((CE_CONT, "DLPI attach: cannot create ppa %u\n", dlp->attach_req.dl_ppa)); dlerrorack(q, mp, dlp->dl_primitive, DL_SYSERR, err); return; } /* * Preallocate the hangup message so that we're always able to * send this upstream in the event of a catastrophic failure. */ if ((sps->sps_hangup = allocb(1, BPRI_MED)) == NULL) { DBGERROR((CE_CONT, "DLPI attach: cannot allocate hangup\n")); dlerrorack(q, mp, dlp->dl_primitive, DL_SYSERR, ENOSR); return; } sps->sps_dlstate = DL_UNBOUND; sps->sps_ppa = ppa; /* * Add this stream to the head of the list of sibling streams * which belong to the specified ppa. */ rw_enter(&ppa->ppa_sib_lock, RW_WRITER); ppa->ppa_refcnt++; sps->sps_nextsib = ppa->ppa_streams; ppa->ppa_streams = sps; /* * And if this stream was marked as promiscuous (SPS_PROMISC), then we * need to update the promiscuous streams count. This should only * happen when DL_PROMISCON_REQ was issued prior to attachment. */ if (IS_SPS_PROMISC(sps)) { ppa->ppa_promicnt++; } rw_exit(&ppa->ppa_sib_lock); DBGDLPI((CE_CONT, "/%d: attached to ppa %d\n", sps->sps_mn_id, ppa->ppa_ppa_id)); dlokack(q, mp, DL_ATTACH_REQ); }
/* * smbfs_mount_label_policy: * Determine whether the mount is allowed according to MAC check, * by comparing (where appropriate) label of the remote server * against the label of the zone being mounted into. * * Returns: * 0 : access allowed * -1 : read-only access allowed (i.e., read-down) * >0 : error code, such as EACCES * * NB: * NFS supports Cipso labels by parsing the vfs_resource * to see what the Solaris server global zone has shared. * We can't support that for CIFS since resource names * contain share names, not paths. */ static int smbfs_mount_label_policy(vfs_t *vfsp, void *ipaddr, int addr_type, cred_t *cr) { bslabel_t *server_sl, *mntlabel; zone_t *mntzone = NULL; ts_label_t *zlabel; tsol_tpc_t *tp; ts_label_t *tsl = NULL; int retv; /* * Get the zone's label. Each zone on a labeled system has a label. */ mntzone = zone_find_by_any_path(refstr_value(vfsp->vfs_mntpt), B_FALSE); zlabel = mntzone->zone_slabel; ASSERT(zlabel != NULL); label_hold(zlabel); retv = EACCES; /* assume the worst */ /* * Next, get the assigned label of the remote server. */ tp = find_tpc(ipaddr, addr_type, B_FALSE); if (tp == NULL) goto out; /* error getting host entry */ if (tp->tpc_tp.tp_doi != zlabel->tsl_doi) goto rel_tpc; /* invalid domain */ if ((tp->tpc_tp.host_type != UNLABELED)) goto rel_tpc; /* invalid hosttype */ server_sl = &tp->tpc_tp.tp_def_label; mntlabel = label2bslabel(zlabel); /* * Now compare labels to complete the MAC check. If the labels * are equal or if the requestor is in the global zone and has * NET_MAC_AWARE, then allow read-write access. (Except for * mounts into the global zone itself; restrict these to * read-only.) * * If the requestor is in some other zone, but his label * dominates the server, then allow read-down. * * Otherwise, access is denied. */ if (blequal(mntlabel, server_sl) || (crgetzoneid(cr) == GLOBAL_ZONEID && getpflags(NET_MAC_AWARE, cr) != 0)) { if ((mntzone == global_zone) || !blequal(mntlabel, server_sl)) retv = -1; /* read-only */ else retv = 0; /* access OK */ } else if (bldominates(mntlabel, server_sl)) { retv = -1; /* read-only */ } else { retv = EACCES; } if (tsl != NULL) label_rele(tsl); rel_tpc: /*LINTED*/ TPC_RELE(tp); out: if (mntzone) zone_rele(mntzone); label_rele(zlabel); return (retv); }