aboutsummaryrefslogtreecommitdiffstats
path: root/net/sctp/socket.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/sctp/socket.c')
-rw-r--r--net/sctp/socket.c113
1 files changed, 97 insertions, 16 deletions
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 7d3476a4860d..7a10ae3c3d82 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -156,6 +156,9 @@ static inline void sctp_set_owner_w(struct sctp_chunk *chunk)
/* The sndbuf space is tracked per association. */
sctp_association_hold(asoc);
+ if (chunk->shkey)
+ sctp_auth_shkey_hold(chunk->shkey);
+
skb_set_owner_w(chunk->skb, sk);
chunk->skb->destructor = sctp_wfree;
@@ -1677,7 +1680,7 @@ static int sctp_sendmsg_new_asoc(struct sock *sk, __u16 sflags,
struct sctp_association *asoc;
enum sctp_scope scope;
struct cmsghdr *cmsg;
- int err = -EINVAL;
+ int err;
*tp = NULL;
@@ -1761,16 +1764,20 @@ static int sctp_sendmsg_new_asoc(struct sock *sk, __u16 sflags,
memset(daddr, 0, sizeof(*daddr));
dlen = cmsg->cmsg_len - sizeof(struct cmsghdr);
if (cmsg->cmsg_type == SCTP_DSTADDRV4) {
- if (dlen < sizeof(struct in_addr))
+ if (dlen < sizeof(struct in_addr)) {
+ err = -EINVAL;
goto free;
+ }
dlen = sizeof(struct in_addr);
daddr->v4.sin_family = AF_INET;
daddr->v4.sin_port = htons(asoc->peer.port);
memcpy(&daddr->v4.sin_addr, CMSG_DATA(cmsg), dlen);
} else {
- if (dlen < sizeof(struct in6_addr))
+ if (dlen < sizeof(struct in6_addr)) {
+ err = -EINVAL;
goto free;
+ }
dlen = sizeof(struct in6_addr);
daddr->v6.sin6_family = AF_INET6;
@@ -1876,6 +1883,19 @@ static int sctp_sendmsg_to_asoc(struct sctp_association *asoc,
goto err;
}
+ if (asoc->pmtu_pending)
+ sctp_assoc_pending_pmtu(asoc);
+
+ if (sctp_wspace(asoc) < msg_len)
+ sctp_prsctp_prune(asoc, sinfo, msg_len - sctp_wspace(asoc));
+
+ if (!sctp_wspace(asoc)) {
+ timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);
+ err = sctp_wait_for_sndbuf(asoc, &timeo, msg_len);
+ if (err)
+ goto err;
+ }
+
if (sctp_state(asoc, CLOSED)) {
err = sctp_primitive_ASSOCIATE(net, asoc, NULL);
if (err)
@@ -1893,19 +1913,6 @@ static int sctp_sendmsg_to_asoc(struct sctp_association *asoc,
pr_debug("%s: we associated primitively\n", __func__);
}
- if (asoc->pmtu_pending)
- sctp_assoc_pending_pmtu(asoc);
-
- if (sctp_wspace(asoc) < msg_len)
- sctp_prsctp_prune(asoc, sinfo, msg_len - sctp_wspace(asoc));
-
- if (!sctp_wspace(asoc)) {
- timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);
- err = sctp_wait_for_sndbuf(asoc, &timeo, msg_len);
- if (err)
- goto err;
- }
-
datamsg = sctp_datamsg_from_user(asoc, sinfo, &msg->msg_iter);
if (IS_ERR(datamsg)) {
err = PTR_ERR(datamsg);
@@ -1980,6 +1987,14 @@ static void sctp_sendmsg_update_sinfo(struct sctp_association *asoc,
if (!cmsgs->srinfo && !cmsgs->prinfo)
sinfo->sinfo_timetolive = asoc->default_timetolive;
+
+ if (cmsgs->authinfo) {
+ /* Reuse sinfo_tsn to indicate that authinfo was set and
+ * sinfo_ssn to save the keyid on tx path.
+ */
+ sinfo->sinfo_tsn = 1;
+ sinfo->sinfo_ssn = cmsgs->authinfo->auth_keynumber;
+ }
}
static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len)
@@ -3632,6 +3647,33 @@ static int sctp_setsockopt_del_key(struct sock *sk,
}
/*
+ * 8.3.4 Deactivate a Shared Key (SCTP_AUTH_DEACTIVATE_KEY)
+ *
+ * This set option will deactivate a shared secret key.
+ */
+static int sctp_setsockopt_deactivate_key(struct sock *sk, char __user *optval,
+ unsigned int optlen)
+{
+ struct sctp_endpoint *ep = sctp_sk(sk)->ep;
+ struct sctp_authkeyid val;
+ struct sctp_association *asoc;
+
+ if (!ep->auth_enable)
+ return -EACCES;
+
+ if (optlen != sizeof(struct sctp_authkeyid))
+ return -EINVAL;
+ if (copy_from_user(&val, optval, optlen))
+ return -EFAULT;
+
+ asoc = sctp_id2assoc(sk, val.scact_assoc_id);
+ if (!asoc && val.scact_assoc_id && sctp_style(sk, UDP))
+ return -EINVAL;
+
+ return sctp_auth_deact_key_id(ep, asoc, val.scact_keynumber);
+}
+
+/*
* 8.1.23 SCTP_AUTO_ASCONF
*
* This option will enable or disable the use of the automatic generation of
@@ -4223,6 +4265,9 @@ static int sctp_setsockopt(struct sock *sk, int level, int optname,
case SCTP_AUTH_DELETE_KEY:
retval = sctp_setsockopt_del_key(sk, optval, optlen);
break;
+ case SCTP_AUTH_DEACTIVATE_KEY:
+ retval = sctp_setsockopt_deactivate_key(sk, optval, optlen);
+ break;
case SCTP_AUTO_ASCONF:
retval = sctp_setsockopt_auto_asconf(sk, optval, optlen);
break;
@@ -7197,6 +7242,7 @@ static int sctp_getsockopt(struct sock *sk, int level, int optname,
case SCTP_AUTH_KEY:
case SCTP_AUTH_CHUNK:
case SCTP_AUTH_DELETE_KEY:
+ case SCTP_AUTH_DEACTIVATE_KEY:
retval = -EOPNOTSUPP;
break;
case SCTP_HMAC_IDENT:
@@ -7867,6 +7913,21 @@ static int sctp_msghdr_parse(const struct msghdr *msg, struct sctp_cmsgs *cmsgs)
if (cmsgs->prinfo->pr_policy == SCTP_PR_SCTP_NONE)
cmsgs->prinfo->pr_value = 0;
break;
+ case SCTP_AUTHINFO:
+ /* SCTP Socket API Extension
+ * 5.3.8 SCTP AUTH Information Structure (SCTP_AUTHINFO)
+ *
+ * This cmsghdr structure specifies SCTP options for sendmsg().
+ *
+ * cmsg_level cmsg_type cmsg_data[]
+ * ------------ ------------ ---------------------
+ * IPPROTO_SCTP SCTP_AUTHINFO struct sctp_authinfo
+ */
+ if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct sctp_authinfo)))
+ return -EINVAL;
+
+ cmsgs->authinfo = CMSG_DATA(cmsg);
+ break;
case SCTP_DSTADDRV4:
case SCTP_DSTADDRV6:
/* SCTP Socket API Extension
@@ -8105,6 +8166,26 @@ static void sctp_wfree(struct sk_buff *skb)
sk->sk_wmem_queued -= skb->truesize;
sk_mem_uncharge(sk, skb->truesize);
+ if (chunk->shkey) {
+ struct sctp_shared_key *shkey = chunk->shkey;
+
+ /* refcnt == 2 and !list_empty mean after this release, it's
+ * not being used anywhere, and it's time to notify userland
+ * that this shkey can be freed if it's been deactivated.
+ */
+ if (shkey->deactivated && !list_empty(&shkey->key_list) &&
+ refcount_read(&shkey->refcnt) == 2) {
+ struct sctp_ulpevent *ev;
+
+ ev = sctp_ulpevent_make_authkey(asoc, shkey->key_id,
+ SCTP_AUTH_FREE_KEY,
+ GFP_KERNEL);
+ if (ev)
+ asoc->stream.si->enqueue_event(&asoc->ulpq, ev);
+ }
+ sctp_auth_shkey_release(chunk->shkey);
+ }
+
sock_wfree(skb);
sctp_wake_up_waiters(sk, asoc);