diff --git a/sys/kern/kern_prot.c b/sys/kern/kern_prot.c --- a/sys/kern/kern_prot.c +++ b/sys/kern/kern_prot.c @@ -2150,6 +2150,44 @@ CTLFLAG_MPSAFE, 0, 0, sysctl_unprivileged_proc_debug, "I", "Unprivileged processes may use process debugging facilities"); +/* + * Return true if the object owner/group ids are subset of the active + * credentials. + */ +bool +cr_xids_subset(struct ucred *active_cred, struct ucred *obj_cred) +{ + int i; + bool grpsubset, uidsubset; + + /* + * Is p's group set a subset of td's effective group set? This + * includes p's egid, group access list, rgid, and svgid. + */ + grpsubset = true; + for (i = 0; i < obj_cred->cr_ngroups; i++) { + if (!groupmember(obj_cred->cr_groups[i], active_cred)) { + grpsubset = false; + break; + } + } + grpsubset = grpsubset && + groupmember(obj_cred->cr_rgid, active_cred) && + groupmember(obj_cred->cr_svgid, active_cred); + + /* + * Are the uids present in obj_cred's credential equal to + * active_cred's effective uid? This includes obj_cred's + * euid, svuid, and ruid. + */ + uidsubset = (active_cred->cr_uid == obj_cred->cr_uid && + active_cred->cr_uid == obj_cred->cr_svuid && + active_cred->cr_uid == obj_cred->cr_ruid); + + return (uidsubset && grpsubset); + +} + /*- * Determine whether td may debug p. * Returns: 0 for permitted, an errno value otherwise @@ -2161,7 +2199,7 @@ int p_candebug(struct thread *td, struct proc *p) { - int error, grpsubset, i, uidsubset; + int error; KASSERT(td == curthread, ("%s: td not curthread", __func__)); PROC_LOCK_ASSERT(p, MA_OWNED); @@ -2178,35 +2216,12 @@ if ((error = cr_bsd_visible(td->td_ucred, p->p_ucred))) return (error); - /* - * Is p's group set a subset of td's effective group set? This - * includes p's egid, group access list, rgid, and svgid. - */ - grpsubset = 1; - for (i = 0; i < p->p_ucred->cr_ngroups; i++) { - if (!groupmember(p->p_ucred->cr_groups[i], td->td_ucred)) { - grpsubset = 0; - break; - } - } - grpsubset = grpsubset && - groupmember(p->p_ucred->cr_rgid, td->td_ucred) && - groupmember(p->p_ucred->cr_svgid, td->td_ucred); - - /* - * Are the uids present in p's credential equal to td's - * effective uid? This includes p's euid, svuid, and ruid. - */ - uidsubset = (td->td_ucred->cr_uid == p->p_ucred->cr_uid && - td->td_ucred->cr_uid == p->p_ucred->cr_svuid && - td->td_ucred->cr_uid == p->p_ucred->cr_ruid); - /* * If p's gids aren't a subset, or the uids aren't a subset, * or the credential has changed, require appropriate privilege * for td to debug p. */ - if (!grpsubset || !uidsubset) { + if (!cr_xids_subset(td->td_ucred, p->p_ucred)) { error = priv_check(td, PRIV_DEBUG_DIFFCRED); if (error) return (error); diff --git a/sys/kern/uipc_ktls.c b/sys/kern/uipc_ktls.c --- a/sys/kern/uipc_ktls.c +++ b/sys/kern/uipc_ktls.c @@ -595,6 +595,8 @@ return (error); } +uint64_t ktls_glob_gen = 1; + static int ktls_create_session(struct socket *so, struct tls_enable *en, struct ktls_session **tlsp, int direction) @@ -819,6 +821,8 @@ arc4rand(tls->params.iv + 8, sizeof(uint64_t), 0); } + atomic_thread_fence_rel(); + tls->gen = atomic_fetchadd_64(&ktls_glob_gen, 1); *tlsp = tls; return (0); } @@ -861,6 +865,8 @@ memcpy(tls_new->params.cipher_key, tls->params.cipher_key, tls->params.cipher_key_len); + atomic_thread_fence_rel(); + tls_new->gen = atomic_fetchadd_64(&ktls_glob_gen, 1); return (tls_new); } @@ -1940,6 +1946,8 @@ MPASS(tls->refcount == 0); + atomic_add_acq_64(&ktls_glob_gen, 1); + inp = tls->inp; if (tls->tx) { wlocked = INP_WLOCKED(inp); @@ -3439,3 +3447,51 @@ TASK_INIT(&tls->disable_ifnet_task, 0, ktls_disable_ifnet_help, tls); (void)taskqueue_enqueue(taskqueue_thread, &tls->disable_ifnet_task); } + +void +ktls_session_to_xktls_onedir(const struct ktls_session *ktls, + struct xktls_session_onedir *xk) +{ + if_t ifp; + struct m_snd_tag *st; + + memcpy(&xk->iv, &ktls->params.iv, TLS_CBC_IMPLICIT_IV_LEN); + xk->gen = ktls->gen; +#define A(m) xk->m = ktls->params.m + A(cipher_algorithm); + A(auth_algorithm); + A(cipher_key_len); + A(iv_len); + A(auth_key_len); + A(max_frame_len); + A(tls_vmajor); + A(tls_vminor); + A(tls_hlen); + A(tls_tlen); + A(tls_bs); + A(flags); +#undef A + if ((st = ktls->snd_tag) != NULL && + (ifp = ktls->snd_tag->ifp) != NULL) + strncpy(xk->ifnet, if_name(ifp), sizeof(xk->ifnet)); +} + +void +ktls_session_copy_keys(const struct ktls_session *ktls, + uint8_t *data, size_t *sz) +{ + size_t t, ta, tc; + + if (ktls == NULL) { + *sz = 0; + return; + } + t = *sz; + tc = MIN(t, ktls->params.cipher_key_len); + if (data != NULL) + memcpy(data, ktls->params.cipher_key, tc); + ta = MIN(t - tc, ktls->params.auth_key_len); + if (data != NULL) + memcpy(data + tc, ktls->params.auth_key, ta); + *sz = ta + tc; +} diff --git a/sys/net/if_var.h b/sys/net/if_var.h --- a/sys/net/if_var.h +++ b/sys/net/if_var.h @@ -297,12 +297,14 @@ typedef int (if_snd_tag_query_t)(struct m_snd_tag *, union if_snd_tag_query_params *); typedef void (if_snd_tag_free_t)(struct m_snd_tag *); typedef struct m_snd_tag *(if_next_send_tag_t)(struct m_snd_tag *); +typedef int (if_snd_tag_status_str_t)(struct m_snd_tag *, char *buf, size_t *sz); struct if_snd_tag_sw { if_snd_tag_modify_t *snd_tag_modify; if_snd_tag_query_t *snd_tag_query; if_snd_tag_free_t *snd_tag_free; if_next_send_tag_t *next_snd_tag; + if_snd_tag_status_str_t *snd_tag_status_str; u_int type; /* One of IF_SND_TAG_TYPE_*. */ }; diff --git a/sys/netinet/in_pcb.h b/sys/netinet/in_pcb.h --- a/sys/netinet/in_pcb.h +++ b/sys/netinet/in_pcb.h @@ -303,6 +303,30 @@ char sop_optval[]; }; +#ifdef _SYS_KTLS_H_ +struct xktls_session { + uint32_t tsz; /* total sz of elm, next elm is at this+tsz */ + uint32_t fsz; /* size of the struct up to keys */ + uint64_t inp_gencnt; + kvaddr_t so_pcb; + struct in_conninfo coninf; + u_short rx_vlan_id; + struct xktls_session_onedir rcv; + struct xktls_session_onedir snd; +/* + * Next are + * - keydata for rcv, first cipher of length rcv.cipher_key_len, then + * authentication of length rcv.auth_key_len; + * - driver data (string) of length rcv.drv_st_len, if the rcv session is + * offloaded to ifnet rcv.ifnet; + * - keydata for snd, first cipher of length snd.cipher_key_len, then + * authentication of length snd.auth_key_len; + * - driver data (string) of length snd.drv_st_len, if the snd session is + * offloaded to ifnet snd.ifnet; + */ +}; +#endif /* _SYS_KTLS_H_ */ + #ifdef _KERNEL int sysctl_setsockopt(SYSCTL_HANDLER_ARGS, struct inpcbinfo *pcbinfo, int (*ctloutput_set)(struct inpcb *, struct sockopt *)); diff --git a/sys/netinet/in_prot.c b/sys/netinet/in_prot.c --- a/sys/netinet/in_prot.c +++ b/sys/netinet/in_prot.c @@ -26,21 +26,17 @@ */ /* - * System calls related to processes and protection + * Helpers related to visibility and protection of sockets and inpcb. */ -#include -#include "opt_inet.h" -#include "opt_inet6.h" - -#include #include +#include #include #include #include +#include #include #include -#include #include #include @@ -72,3 +68,16 @@ return (0); } + +bool +cr_canexport_ktlskeys(struct thread *td, struct inpcb *inp) +{ + int error; + + if (cr_canseeinpcb(td->td_ucred, inp) == 0 && + cr_xids_subset(td->td_ucred, inp->inp_cred)) + return (true); + error = priv_check(td, PRIV_NETINET_KTLSKEYS); + return (error == 0); + +} diff --git a/sys/netinet/in_systm.h b/sys/netinet/in_systm.h --- a/sys/netinet/in_systm.h +++ b/sys/netinet/in_systm.h @@ -58,8 +58,10 @@ #ifdef _KERNEL struct inpcb; struct ucred; +struct thread; int cr_canseeinpcb(struct ucred *cred, struct inpcb *inp); +bool cr_canexport_ktlskeys(struct thread *td, struct inpcb *inp); uint32_t iptime(void); #endif diff --git a/sys/netinet/tcp_subr.c b/sys/netinet/tcp_subr.c --- a/sys/netinet/tcp_subr.c +++ b/sys/netinet/tcp_subr.c @@ -2664,6 +2664,219 @@ NULL, 0, tcp_pcblist, "S,xtcpcb", "List of active TCP connections"); +#define SND_TAG_STATUS_MAXLEN 128 + +#ifdef KERN_TLS +static int +tcp_ktlslist(SYSCTL_HANDLER_ARGS, bool export_keys) +{ + struct xinpgen xig; + struct inpcb *inp; + struct socket *so; + struct ktls_session *ksr, *kss; + char *buf; + struct xktls_session *xktls; + uint64_t ipi_gencnt; + size_t buflen, len, sz; + u_int cnt; + int error; + bool p; + + if (req->newptr != NULL) + return (EPERM); + + len = 0; + cnt = 0; + ipi_gencnt = V_tcbinfo.ipi_gencnt; + bzero(&xig, sizeof(xig)); + xig.xig_len = sizeof(xig); + xig.xig_gen = atomic_load_acq_64(&ktls_glob_gen); + xig.xig_sogen = so_gencnt; + + struct inpcb_iterator inpi = INP_ALL_ITERATOR(&V_tcbinfo, + INPLOOKUP_RLOCKPCB); + while ((inp = inp_next(&inpi)) != NULL) { + if (inp->inp_gencnt > ipi_gencnt || + cr_canseeinpcb(req->td->td_ucred, inp) != 0) + continue; + + so = inp->inp_socket; + if (so != NULL && so->so_gencnt <= xig.xig_sogen) { + p = false; + ksr = so->so_rcv.sb_tls_info; + if (ktls_session_genvis(ksr, xig.xig_gen)) { + p = true; + if (export_keys && cr_canexport_ktlskeys( + req->td, inp)) { + sz = SIZE_T_MAX; + ktls_session_copy_keys(ksr, + NULL, &sz); + len += sz; + } + if (ksr->snd_tag != NULL && + ksr->snd_tag->sw->snd_tag_status_str != + NULL) { + sz = SND_TAG_STATUS_MAXLEN; + ksr->snd_tag->sw->snd_tag_status_str( + ksr->snd_tag, NULL, &sz); + len += sz; + } + } + kss = so->so_snd.sb_tls_info; + if (ktls_session_genvis(kss, xig.xig_gen)) { + p = true; + if (export_keys && cr_canexport_ktlskeys( + req->td, inp)) { + sz = SIZE_T_MAX; + ktls_session_copy_keys(kss, + NULL, &sz); + len += sz; + } + if (kss->snd_tag != NULL && + kss->snd_tag->sw->snd_tag_status_str != + NULL) { + sz = SND_TAG_STATUS_MAXLEN; + kss->snd_tag->sw->snd_tag_status_str( + kss->snd_tag, NULL, &sz); + len += sz; + } + } + if (p) { + len += sizeof(*xktls); + len = roundup2(len, __alignof(struct + xktls_session)); + } + } + } + if (req->oldptr == NULL) { + len += 2 * sizeof(xig); + len += 3 * len / 4; + req->oldidx = len; + return (0); + } + + if ((error = sysctl_wire_old_buffer(req, 0)) != 0) + return (error); + + error = SYSCTL_OUT(req, &xig, sizeof xig); + if (error != 0) + return (error); + + buflen = roundup2(sizeof(*xktls) + 2 * TLS_MAX_PARAM_SIZE + + 2 * SND_TAG_STATUS_MAXLEN, __alignof(struct xktls_session)); + buf = malloc(buflen, M_TEMP, M_WAITOK | M_ZERO); + struct inpcb_iterator inpi1 = INP_ALL_ITERATOR(&V_tcbinfo, + INPLOOKUP_RLOCKPCB); + while ((inp = inp_next(&inpi1)) != NULL) { + if (inp->inp_gencnt > ipi_gencnt || + cr_canseeinpcb(req->td->td_ucred, inp) != 0) + continue; + + so = inp->inp_socket; + if (so == NULL) + continue; + + p = false; + ksr = so->so_rcv.sb_tls_info; + kss = so->so_snd.sb_tls_info; + xktls = (struct xktls_session *)buf; + if (ktls_session_genvis(ksr, xig.xig_gen)) { + p = true; + ktls_session_to_xktls_onedir(ksr, &xktls->rcv); + } + if (ktls_session_genvis(kss, xig.xig_gen)) { + p = true; + ktls_session_to_xktls_onedir(kss, &xktls->snd); + } + if (!p) + continue; + + xktls->inp_gencnt = inp->inp_gencnt; + xktls->so_pcb = (kvaddr_t)inp; + memcpy(&xktls->coninf, &inp->inp_inc, sizeof(xktls->coninf)); + len = sizeof(*xktls); + if (ktls_session_genvis(ksr, xig.xig_gen)) { + if (export_keys && cr_canexport_ktlskeys(req->td, + inp)) { + sz = buflen - len; + ktls_session_copy_keys(ksr, buf + len, &sz); + len += sz; + } else { + xktls->rcv.cipher_key_len = 0; + xktls->rcv.auth_key_len = 0; + } + if (ksr->snd_tag != NULL && + ksr->snd_tag->sw->snd_tag_status_str != NULL) { + sz = SND_TAG_STATUS_MAXLEN; + ksr->snd_tag->sw->snd_tag_status_str( + ksr->snd_tag, buf + len, &sz); + len += sz; + } + } + if (ktls_session_genvis(kss, xig.xig_gen)) { + if (export_keys && cr_canexport_ktlskeys(req->td, + inp)) { + sz = buflen - len; + ktls_session_copy_keys(kss, buf + len, &sz); + len += sz; + } else { + xktls->snd.cipher_key_len = 0; + xktls->snd.auth_key_len = 0; + } + if (kss->snd_tag != NULL && + kss->snd_tag->sw->snd_tag_status_str != NULL) { + sz = SND_TAG_STATUS_MAXLEN; + kss->snd_tag->sw->snd_tag_status_str( + kss->snd_tag, buf + len, &sz); + len += sz; + } + } + len = roundup2(len, __alignof(*xktls)); + xktls->tsz = len; + xktls->fsz = sizeof(*xktls); + + error = SYSCTL_OUT(req, xktls, len); + if (error != 0) { + INP_RUNLOCK(inp); + break; + } + cnt++; + } + + if (error == 0) { + atomic_thread_fence_rel(); + xig.xig_gen = atomic_load_64(&ktls_glob_gen); + xig.xig_sogen = so_gencnt; + xig.xig_count = cnt; + error = SYSCTL_OUT(req, &xig, sizeof(xig)); + } + + free(buf, M_TEMP); + return (error); +} + +static int +tcp_ktlslist_nokeys(SYSCTL_HANDLER_ARGS) +{ + return (tcp_ktlslist(oidp, arg1, arg2, req, false)); +} + +static int +tcp_ktlslist_wkeys(SYSCTL_HANDLER_ARGS) +{ + return (tcp_ktlslist(oidp, arg1, arg2, req, true)); +} + +SYSCTL_PROC(_net_inet_tcp, TCPCTL_KTLSLIST, ktlslist, + CTLTYPE_OPAQUE | CTLFLAG_RD | CTLFLAG_MPSAFE, + NULL, 0, tcp_ktlslist_nokeys, "S,xktls_session", + "List of active kTLS sessions for TCP connections"); +SYSCTL_PROC(_net_inet_tcp, TCPCTL_KTLSLIST_WKEYS, ktlslist_wkeys, + CTLTYPE_OPAQUE | CTLFLAG_RD | CTLFLAG_MPSAFE, + NULL, 0, tcp_ktlslist_wkeys, "S,xktls_session", + "List of active kTLS sessions for TCP connections with keys"); +#endif /* KERN_TLS */ + #ifdef INET static int tcp_getcred(SYSCTL_HANDLER_ARGS) diff --git a/sys/netinet/tcp_var.h b/sys/netinet/tcp_var.h --- a/sys/netinet/tcp_var.h +++ b/sys/netinet/tcp_var.h @@ -1234,6 +1234,8 @@ #define TCPCTL_SACK 14 /* Selective Acknowledgement,rfc 2018 */ #define TCPCTL_DROP 15 /* drop tcp connection */ #define TCPCTL_STATES 16 /* connection counts by TCP state */ +#define TCPCTL_KTLSLIST 17 +#define TCPCTL_KTLSLIST_WKEYS 18 #ifdef _KERNEL #ifdef SYSCTL_DECL diff --git a/sys/sys/ktls.h b/sys/sys/ktls.h --- a/sys/sys/ktls.h +++ b/sys/sys/ktls.h @@ -145,6 +145,27 @@ uint16_t tls_length; }; +struct xktls_session_onedir { + uint64_t gen; + uint64_t rsrv1[8]; + uint32_t rsrv2[8]; + uint8_t iv[TLS_CBC_IMPLICIT_IV_LEN]; + int cipher_algorithm; + int auth_algorithm; + uint16_t cipher_key_len; + uint16_t iv_len; + uint16_t auth_key_len; + uint16_t max_frame_len; + uint8_t tls_vmajor; + uint8_t tls_vminor; + uint8_t tls_hlen; + uint8_t tls_tlen; + uint8_t tls_bs; + uint8_t flags; + uint16_t drv_st_len; + char ifnet[16]; /* IFNAMSIZ */ +}; + #ifdef _KERNEL struct tls_session_params { @@ -206,9 +227,12 @@ /* Used to destroy any kTLS session */ struct task destroy_task; + + uint64_t gen; } __aligned(CACHE_LINE_SIZE); extern unsigned int ktls_ifnet_max_rexmit_pct; +extern uint64_t ktls_glob_gen; typedef enum { KTLS_MBUF_CRYPTO_ST_MIXED = 0, @@ -258,5 +282,16 @@ ktls_destroy(tls); } +static inline bool +ktls_session_genvis(const struct ktls_session *ks, uint64_t gen) +{ + return (ks != NULL && ks->gen <= gen); +} + +void ktls_session_to_xktls_onedir(const struct ktls_session *ks, + struct xktls_session_onedir *xktls_od); +void ktls_session_copy_keys(const struct ktls_session *ktls, + uint8_t *data, size_t *sz); + #endif /* !_KERNEL */ #endif /* !_SYS_KTLS_H_ */ diff --git a/sys/sys/priv.h b/sys/sys/priv.h --- a/sys/sys/priv.h +++ b/sys/sys/priv.h @@ -406,6 +406,7 @@ #define PRIV_NETINET_SETHDROPTS 505 /* Set certain IPv4/6 header options. */ #define PRIV_NETINET_BINDANY 506 /* Allow bind to any address. */ #define PRIV_NETINET_HASHKEY 507 /* Get and set hash keys for IPv4/6. */ +#define PRIV_NETINET_KTLSKEYS 508 /* Read ktls session keys. */ /* * Placeholders for IPX/SPX privileges, not supported any more. diff --git a/sys/sys/ucred.h b/sys/sys/ucred.h --- a/sys/sys/ucred.h +++ b/sys/sys/ucred.h @@ -237,6 +237,7 @@ void crsetgroups(struct ucred *cr, int ngrp, const gid_t *groups); void crsetgroups_fallback(struct ucred *cr, int ngrp, const gid_t *groups, const gid_t fallback); +bool cr_xids_subset(struct ucred *active_cred, struct ucred *obj_cred); /* * Returns whether gid designates a primary group in cred.