/* * There's no protocol today to obtain the label from the server. * So we rely on conventions: zones, zone names, and zone paths * must match across TX servers and their TX clients. Now use * the exported name to find the equivalent local zone and its * label. Caller is responsible for doing a label_rele of the * returned ts_label. */ ts_label_t * getflabel_cipso(vfs_t *vfsp) { zone_t *reszone; zone_t *new_reszone; char *nfspath, *respath; refstr_t *resource_ref; boolean_t treat_abs = B_FALSE; if (vfsp->vfs_resource == NULL) return (NULL); /* error */ resource_ref = vfs_getresource(vfsp); nfspath = (char *)refstr_value(resource_ref); respath = strchr(nfspath, ':'); /* skip server name */ if (respath) respath++; /* skip over ":" */ if (*respath != '/') { /* treat path as absolute but it doesn't have leading '/' */ treat_abs = B_TRUE; } reszone = zone_find_by_any_path(respath, treat_abs); if (reszone == global_zone) { refstr_rele(resource_ref); label_hold(l_admin_low); zone_rele(reszone); return (l_admin_low); } /* * Skip over zonepath (not including "root"), e.g. /zone/internal */ respath += reszone->zone_rootpathlen - 7; if (treat_abs) respath--; /* no leading '/' to skip */ if (strncmp(respath, "/root/", 6) == 0) { /* Check if we now have something like "/zone/public/" */ respath += 5; /* skip "/root" first */ new_reszone = zone_find_by_any_path(respath, B_FALSE); if (new_reszone != global_zone) { zone_rele(reszone); reszone = new_reszone; } else { zone_rele(new_reszone); } } refstr_rele(resource_ref); label_hold(reszone->zone_slabel); zone_rele(reszone); return (reszone->zone_slabel); }
/* * Allocate a initialized cred structure and crhold() it. * Initialized means: all ids 0, group count 0, L=Full, E=P=I=I0 */ cred_t * crget(void) { cred_t *cr = kmem_cache_alloc(cred_cache, KM_SLEEP); bcopy(kcred, cr, crsize); cr->cr_ref = 1; zone_cred_hold(cr->cr_zone); if (cr->cr_label) label_hold(cr->cr_label); ASSERT(cr->cr_klpd == NULL); ASSERT(cr->cr_grps == NULL); return (cr); }
/* * Derive a new cred from the existing cred, but with a different label. * To be used when a cred is being shared, but the label needs to be changed * by a caller without affecting other users */ cred_t * copycred_from_tslabel(const cred_t *cr, ts_label_t *label, int flags) { cred_t *newcr = NULL; if ((newcr = crdup_flags(cr, flags)) != NULL) { if (newcr->cr_label != NULL) label_rele(newcr->cr_label); label_hold(label); newcr->cr_label = label; } return (newcr); }
/* * Dup a cred struct to a new held one. * The old cred is not freed. * This variation on crdup uses a pre-allocated structure for the * "new" cred. */ void crdup_to(cred_t *oldcr, cred_t *newcr) { credsid_t *nkcr = newcr->cr_ksid; bcopy(oldcr, newcr, crsize); if (newcr->cr_zone) zone_cred_hold(newcr->cr_zone); if (newcr->cr_label) label_hold(newcr->cr_label); if (newcr->cr_klpd) crklpd_hold(newcr->cr_klpd); if (newcr->cr_grps) crgrphold(newcr->cr_grps); if (nkcr) { newcr->cr_ksid = nkcr; kcrsidcopy_to(oldcr->cr_ksid, newcr->cr_ksid); } else if (newcr->cr_ksid) kcrsid_hold(newcr->cr_ksid); newcr->cr_ref = 1; }
/* * Copy a cred structure to a new one and free the old one. * The new cred will have two references. One for the calling process, * and one for the thread. */ cred_t * crcopy(cred_t *cr) { cred_t *newcr; newcr = cralloc(); bcopy(cr, newcr, crsize); if (newcr->cr_zone) zone_cred_hold(newcr->cr_zone); if (newcr->cr_label) label_hold(newcr->cr_label); if (newcr->cr_ksid) kcrsid_hold(newcr->cr_ksid); if (newcr->cr_klpd) crklpd_hold(newcr->cr_klpd); if (newcr->cr_grps) crgrphold(newcr->cr_grps); crfree(cr); newcr->cr_ref = 2; /* caller gets two references */ return (newcr); }
/* * Copy a cred structure to a new one and free the old one. * The new cred will have two references. One for the calling process, * and one for the thread. * This variation on crcopy uses a pre-allocated structure for the * "new" cred. */ void crcopy_to(cred_t *oldcr, cred_t *newcr) { credsid_t *nkcr = newcr->cr_ksid; bcopy(oldcr, newcr, crsize); if (newcr->cr_zone) zone_cred_hold(newcr->cr_zone); if (newcr->cr_label) label_hold(newcr->cr_label); if (newcr->cr_klpd) crklpd_hold(newcr->cr_klpd); if (newcr->cr_grps) crgrphold(newcr->cr_grps); if (nkcr) { newcr->cr_ksid = nkcr; kcrsidcopy_to(oldcr->cr_ksid, newcr->cr_ksid); } else if (newcr->cr_ksid) kcrsid_hold(newcr->cr_ksid); crfree(oldcr); newcr->cr_ref = 2; /* caller gets two references */ }
/* * Dup a cred struct to a new held one. * The old cred is not freed. */ static cred_t * crdup_flags(const cred_t *cr, int flgs) { cred_t *newcr; newcr = cralloc_flags(flgs); if (newcr == NULL) return (NULL); bcopy(cr, newcr, crsize); if (newcr->cr_zone) zone_cred_hold(newcr->cr_zone); if (newcr->cr_label) label_hold(newcr->cr_label); if (newcr->cr_klpd) crklpd_hold(newcr->cr_klpd); if (newcr->cr_ksid) kcrsid_hold(newcr->cr_ksid); if (newcr->cr_grps) crgrphold(newcr->cr_grps); newcr->cr_ref = 1; return (newcr); }
/* Process the COOKIE packet, mp, directed at the listener 'sctp' */ sctp_t * sctp_conn_request(sctp_t *sctp, mblk_t *mp, uint_t ifindex, uint_t ip_hdr_len, sctp_init_chunk_t *iack, ip_recv_attr_t *ira) { sctp_t *eager; ip6_t *ip6h; int err; conn_t *connp, *econnp; sctp_stack_t *sctps; struct sock_proto_props sopp; cred_t *cr; pid_t cpid; in6_addr_t faddr, laddr; ip_xmit_attr_t *ixa; /* * No need to check for duplicate as this is the listener * and we are holding the lock. This means that no new * connection can be created out of it. And since the * fanout already done cannot find a match, it means that * there is no duplicate. */ ASSERT(OK_32PTR(mp->b_rptr)); if ((eager = sctp_create_eager(sctp)) == NULL) { return (NULL); } connp = sctp->sctp_connp; sctps = sctp->sctp_sctps; econnp = eager->sctp_connp; if (connp->conn_policy != NULL) { /* Inherit the policy from the listener; use actions from ira */ if (!ip_ipsec_policy_inherit(econnp, connp, ira)) { sctp_close_eager(eager); BUMP_MIB(&sctps->sctps_mib, sctpListenDrop); return (NULL); } } ip6h = (ip6_t *)mp->b_rptr; if (ira->ira_flags & IXAF_IS_IPV4) { ipha_t *ipha; ipha = (ipha_t *)ip6h; IN6_IPADDR_TO_V4MAPPED(ipha->ipha_dst, &laddr); IN6_IPADDR_TO_V4MAPPED(ipha->ipha_src, &faddr); } else { laddr = ip6h->ip6_dst; faddr = ip6h->ip6_src; } if (ira->ira_flags & IRAF_IPSEC_SECURE) { /* * XXX need to fix the cached policy issue here. * We temporarily set the conn_laddr/conn_faddr here so * that IPsec can use it for the latched policy * selector. This is obvioursly wrong as SCTP can * use different addresses... */ econnp->conn_laddr_v6 = laddr; econnp->conn_faddr_v6 = faddr; econnp->conn_saddr_v6 = laddr; } if (ipsec_conn_cache_policy(econnp, (ira->ira_flags & IRAF_IS_IPV4) != 0) != 0) { sctp_close_eager(eager); BUMP_MIB(&sctps->sctps_mib, sctpListenDrop); return (NULL); } /* Save for getpeerucred */ cr = ira->ira_cred; cpid = ira->ira_cpid; if (is_system_labeled()) { ip_xmit_attr_t *ixa = econnp->conn_ixa; ASSERT(ira->ira_tsl != NULL); /* Discard any old label */ if (ixa->ixa_free_flags & IXA_FREE_TSL) { ASSERT(ixa->ixa_tsl != NULL); label_rele(ixa->ixa_tsl); ixa->ixa_free_flags &= ~IXA_FREE_TSL; ixa->ixa_tsl = NULL; } if ((connp->conn_mlp_type != mlptSingle || connp->conn_mac_mode != CONN_MAC_DEFAULT) && ira->ira_tsl != NULL) { /* * If this is an MLP connection or a MAC-Exempt * connection with an unlabeled node, packets are to be * exchanged using the security label of the received * Cookie packet instead of the server application's * label. * tsol_check_dest called from ip_set_destination * might later update TSF_UNLABELED by replacing * ixa_tsl with a new label. */ label_hold(ira->ira_tsl); ip_xmit_attr_replace_tsl(ixa, ira->ira_tsl); } else { ixa->ixa_tsl = crgetlabel(econnp->conn_cred); } } err = sctp_accept_comm(sctp, eager, mp, ip_hdr_len, iack); if (err != 0) { sctp_close_eager(eager); BUMP_MIB(&sctps->sctps_mib, sctpListenDrop); return (NULL); } ASSERT(eager->sctp_current->ixa != NULL); ixa = eager->sctp_current->ixa; if (!(ira->ira_flags & IXAF_IS_IPV4)) { ASSERT(!(ixa->ixa_flags & IXAF_IS_IPV4)); if (IN6_IS_ADDR_LINKLOCAL(&ip6h->ip6_src) || IN6_IS_ADDR_LINKLOCAL(&ip6h->ip6_dst)) { eager->sctp_linklocal = 1; ixa->ixa_flags |= IXAF_SCOPEID_SET; ixa->ixa_scopeid = ifindex; econnp->conn_incoming_ifindex = ifindex; } } /* * On a clustered note send this notification to the clustering * subsystem. */ if (cl_sctp_connect != NULL) { uchar_t *slist; uchar_t *flist; size_t fsize; size_t ssize; fsize = sizeof (in6_addr_t) * eager->sctp_nfaddrs; ssize = sizeof (in6_addr_t) * eager->sctp_nsaddrs; slist = kmem_alloc(ssize, KM_NOSLEEP); flist = kmem_alloc(fsize, KM_NOSLEEP); if (slist == NULL || flist == NULL) { if (slist != NULL) kmem_free(slist, ssize); if (flist != NULL) kmem_free(flist, fsize); sctp_close_eager(eager); BUMP_MIB(&sctps->sctps_mib, sctpListenDrop); SCTP_KSTAT(sctps, sctp_cl_connect); return (NULL); } /* The clustering module frees these list */ sctp_get_saddr_list(eager, slist, ssize); sctp_get_faddr_list(eager, flist, fsize); (*cl_sctp_connect)(econnp->conn_family, slist, eager->sctp_nsaddrs, econnp->conn_lport, flist, eager->sctp_nfaddrs, econnp->conn_fport, B_FALSE, (cl_sctp_handle_t)eager); } /* Connection established, so send up the conn_ind */ if ((eager->sctp_ulpd = sctp->sctp_ulp_newconn(sctp->sctp_ulpd, (sock_lower_handle_t)eager, NULL, cr, cpid, &eager->sctp_upcalls)) == NULL) { sctp_close_eager(eager); BUMP_MIB(&sctps->sctps_mib, sctpListenDrop); return (NULL); } ASSERT(SCTP_IS_DETACHED(eager)); eager->sctp_detached = B_FALSE; bzero(&sopp, sizeof (sopp)); sopp.sopp_flags = SOCKOPT_MAXBLK|SOCKOPT_WROFF; sopp.sopp_maxblk = strmsgsz; if (econnp->conn_family == AF_INET) { sopp.sopp_wroff = sctps->sctps_wroff_xtra + sizeof (sctp_data_hdr_t) + sctp->sctp_hdr_len; } else { sopp.sopp_wroff = sctps->sctps_wroff_xtra + sizeof (sctp_data_hdr_t) + sctp->sctp_hdr6_len; } eager->sctp_ulp_prop(eager->sctp_ulpd, &sopp); return (eager); }
/* * getflabel - * * Return pointer to the ts_label associated with the specified file, * or returns NULL if error occurs. Caller is responsible for doing * a label_rele of the ts_label. */ ts_label_t * getflabel(vnode_t *vp) { vfs_t *vfsp, *rvfsp; vnode_t *rvp, *rvp2; zone_t *zone; ts_label_t *zl; int err; boolean_t vfs_is_held = B_FALSE; char vpath[MAXPATHLEN]; ASSERT(vp); vfsp = vp->v_vfsp; if (vfsp == NULL) return (NULL); rvp = vp; /* * Traverse lofs mounts and fattach'es to get the real vnode */ if (VOP_REALVP(rvp, &rvp2, NULL) == 0) rvp = rvp2; rvfsp = rvp->v_vfsp; /* rvp/rvfsp now represent the real vnode/vfs we will be using */ /* Go elsewhere to handle all nfs files. */ if (strncmp(vfssw[rvfsp->vfs_fstype].vsw_name, "nfs", 3) == 0) return (getflabel_nfs(rvfsp)); /* * Fast path, for objects in a labeled zone: everything except * for lofs/nfs will be just the label of that zone. */ if ((rvfsp->vfs_zone != NULL) && (rvfsp->vfs_zone != global_zone)) { if ((strcmp(vfssw[rvfsp->vfs_fstype].vsw_name, "lofs") != 0)) { zone = rvfsp->vfs_zone; zone_hold(zone); goto zone_out; /* return this label */ } } /* * Get the vnode path -- it may be missing or weird for some * cases, like devices. In those cases use the label of the * current zone. */ err = vnodetopath(rootdir, rvp, vpath, sizeof (vpath), kcred); if ((err != 0) || (*vpath != '/')) { zone = curproc->p_zone; zone_hold(zone); goto zone_out; } /* * For zfs filesystem, return the explicit label property if a * meaningful one exists. */ if (strncmp(vfssw[rvfsp->vfs_fstype].vsw_name, "zfs", 3) == 0) { ts_label_t *tsl; tsl = getflabel_zfs(rvfsp); /* if label found, return it, otherwise continue... */ if (tsl != NULL) return (tsl); } /* * If a mountpoint exists, hold the vfs while we reference it. * Otherwise if mountpoint is NULL it should not be held (e.g., * a hold/release on spec_vfs would result in an attempted free * and panic.) */ if (vfsp->vfs_mntpt != NULL) { VFS_HOLD(vfsp); vfs_is_held = B_TRUE; } zone = zone_find_by_any_path(vpath, B_FALSE); /* * If the vnode source zone is properly set to a non-global zone, or * any zone if the mount is R/W, then use the label of that zone. */ if ((zone != global_zone) || ((vfsp->vfs_flag & VFS_RDONLY) != 0)) goto zone_out; /* return this label */ /* * Otherwise, if we're not in the global zone, use the label of * our zone. */ if ((zone = curproc->p_zone) != global_zone) { zone_hold(zone); goto zone_out; /* return this label */ } /* * We're in the global zone and the mount is R/W ... so the file * may actually be in the global zone -- or in the root of any zone. * Always build our own path for the file, to be sure it's simplified * (i.e., no ".", "..", "//", and so on). */ zone_rele(zone); zone = zone_find_by_any_path(vpath, B_FALSE); zone_out: if ((curproc->p_zone == global_zone) && (zone == global_zone)) { vfs_t *nvfs; boolean_t exported = B_FALSE; refstr_t *mntpt_ref; char *mntpt; /* * File is in the global zone - check whether it's admin_high. * If it's in a filesys that was exported from the global zone, * it's admin_low by definition. Otherwise, if it's in a * filesys that's NOT exported to any zone, it's admin_high. * * And for these files if there wasn't a valid mount resource, * the file must be admin_high (not exported, probably a global * zone device). */ if (!vfs_is_held) goto out_high; mntpt_ref = vfs_getmntpoint(vfsp); mntpt = (char *)refstr_value(mntpt_ref); if ((mntpt != NULL) && (*mntpt == '/')) { zone_t *to_zone; to_zone = zone_find_by_any_path(mntpt, B_FALSE); zone_rele(to_zone); if (to_zone != global_zone) { /* force admin_low */ exported = B_TRUE; } } if (mntpt_ref) refstr_rele(mntpt_ref); if (!exported) { size_t plen = strlen(vpath); vfs_list_read_lock(); nvfs = vfsp->vfs_next; while (nvfs != vfsp) { const char *rstr; size_t rlen = 0; /* * Skip checking this vfs if it's not lofs * (the only way to export from the global * zone to a zone). */ if (strncmp(vfssw[nvfs->vfs_fstype].vsw_name, "lofs", 4) != 0) { nvfs = nvfs->vfs_next; continue; } rstr = refstr_value(nvfs->vfs_resource); if (rstr != NULL) rlen = strlen(rstr); /* * Check for a match: does this vfs correspond * to our global zone file path? I.e., check * if the resource string of this vfs is a * prefix of our path. */ if ((rlen > 0) && (rlen <= plen) && (strncmp(rstr, vpath, rlen) == 0) && (vpath[rlen] == '/' || vpath[rlen] == '\0')) { /* force admin_low */ exported = B_TRUE; break; } nvfs = nvfs->vfs_next; } vfs_list_unlock(); } if (!exported) goto out_high; } if (vfs_is_held) VFS_RELE(vfsp); /* * Now that we have the "home" zone for the file, return the slabel * of that zone. */ zl = zone->zone_slabel; label_hold(zl); zone_rele(zone); return (zl); out_high: if (vfs_is_held) VFS_RELE(vfsp); label_hold(l_admin_high); zone_rele(zone); return (l_admin_high); }
/* * 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); }