/* * Derive a new cred from the existing cred, but with a different label. */ cred_t * copycred_from_bslabel(const cred_t *cr, bslabel_t *blabel, uint32_t doi, int flags) { ts_label_t *lbl = labelalloc(blabel, doi, flags); cred_t *newcr = NULL; if (lbl != NULL) { newcr = copycred_from_tslabel(cr, lbl, flags); label_rele(lbl); } return (newcr); }
/* * 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); }
static int cgetlabel(bslabel_t *label_p, vnode_t *vp) { ts_label_t *tsl; int error = 0; if ((tsl = getflabel(vp)) == NULL) return (EIO); if (copyout((caddr_t)label2bslabel(tsl), (caddr_t)label_p, sizeof (*(label_p))) != 0) error = EFAULT; label_rele(tsl); return (error); }
/* * Create a new cred based on the supplied label */ cred_t * newcred_from_bslabel(bslabel_t *blabel, uint32_t doi, int flags) { ts_label_t *lbl = labelalloc(blabel, doi, flags); cred_t *cr = NULL; if (lbl != NULL) { if ((cr = crdup_flags(dummycr, flags)) != NULL) { cr->cr_label = lbl; } else { label_rele(lbl); } } return (cr); }
/* * Release previous hold on a cred structure. Free it if refcnt == 0. * If cred uses label different from zone label, free it. */ void crfree(cred_t *cr) { if (atomic_add_32_nv(&cr->cr_ref, -1) == 0) { ASSERT(cr != kcred); if (cr->cr_label) label_rele(cr->cr_label); if (cr->cr_klpd) crklpd_rele(cr->cr_klpd); if (cr->cr_zone) zone_cred_rele(cr->cr_zone); if (cr->cr_ksid) kcrsid_rele(cr->cr_ksid); if (cr->cr_grps) crgrprele(cr->cr_grps); kmem_cache_free(cred_cache, cr); } }
/* * Returns 0 on success, ENOMEM on memory allocation failure, EHOSTUNREACH * if the connection credentials fail remote host accreditation or * if the new destination does not support the previously established * connection security label. If sleep is true, this function should * never fail for a memory allocation failure. The boolean parameter * "first" decides whether the newly created faddr structure should be * added at the beginning of the list or at the end. * * Note: caller must hold conn fanout lock. */ int sctp_add_faddr(sctp_t *sctp, in6_addr_t *addr, int sleep, boolean_t first) { sctp_faddr_t *faddr; mblk_t *timer_mp; int err; conn_t *connp = sctp->sctp_connp; if (is_system_labeled()) { ip_xmit_attr_t *ixa = connp->conn_ixa; ts_label_t *effective_tsl = NULL; ASSERT(ixa->ixa_tsl != NULL); /* * Verify the destination is allowed to receive packets * at the security label of the connection we are initiating. * * tsol_check_dest() will create a new effective label for * this connection with a modified label or label flags only * if there are changes from the original label. * * Accept whatever label we get if this is the first * destination address for this connection. The security * label and label flags must match any previuous settings * for all subsequent destination addresses. */ if (IN6_IS_ADDR_V4MAPPED(addr)) { uint32_t dst; IN6_V4MAPPED_TO_IPADDR(addr, dst); err = tsol_check_dest(ixa->ixa_tsl, &dst, IPV4_VERSION, connp->conn_mac_mode, connp->conn_zone_is_global, &effective_tsl); } else { err = tsol_check_dest(ixa->ixa_tsl, addr, IPV6_VERSION, connp->conn_mac_mode, connp->conn_zone_is_global, &effective_tsl); } if (err != 0) return (err); if (sctp->sctp_faddrs == NULL && effective_tsl != NULL) { ip_xmit_attr_replace_tsl(ixa, effective_tsl); } else if (effective_tsl != NULL) { label_rele(effective_tsl); return (EHOSTUNREACH); } } if ((faddr = kmem_cache_alloc(sctp_kmem_faddr_cache, sleep)) == NULL) return (ENOMEM); bzero(faddr, sizeof (*faddr)); timer_mp = sctp_timer_alloc((sctp), sctp_rexmit_timer, sleep); if (timer_mp == NULL) { kmem_cache_free(sctp_kmem_faddr_cache, faddr); return (ENOMEM); } ((sctpt_t *)(timer_mp->b_rptr))->sctpt_faddr = faddr; /* Start with any options set on the conn */ faddr->ixa = conn_get_ixa_exclusive(connp); if (faddr->ixa == NULL) { freemsg(timer_mp); kmem_cache_free(sctp_kmem_faddr_cache, faddr); return (ENOMEM); } faddr->ixa->ixa_notify_cookie = connp->conn_sctp; sctp_init_faddr(sctp, faddr, addr, timer_mp); ASSERT(faddr->ixa->ixa_cred != NULL); /* ip_attr_connect didn't allow broadcats/multicast dest */ ASSERT(faddr->next == NULL); if (sctp->sctp_faddrs == NULL) { ASSERT(sctp->sctp_lastfaddr == NULL); /* only element on list; first and last are same */ sctp->sctp_faddrs = sctp->sctp_lastfaddr = faddr; } else if (first) { ASSERT(sctp->sctp_lastfaddr != NULL); faddr->next = sctp->sctp_faddrs; sctp->sctp_faddrs = faddr; } else { sctp->sctp_lastfaddr->next = faddr; sctp->sctp_lastfaddr = faddr; } sctp->sctp_nfaddrs++; return (0); }
/* 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); }
/* * 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); }