|
@@ -2946,6 +2946,164 @@ static int sctp_setsockopt_maxburst(struct sock *sk,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * 7.1.18. Add a chunk that must be authenticated (SCTP_AUTH_CHUNK)
|
|
|
+ *
|
|
|
+ * This set option adds a chunk type that the user is requesting to be
|
|
|
+ * received only in an authenticated way. Changes to the list of chunks
|
|
|
+ * will only effect future associations on the socket.
|
|
|
+ */
|
|
|
+static int sctp_setsockopt_auth_chunk(struct sock *sk,
|
|
|
+ char __user *optval,
|
|
|
+ int optlen)
|
|
|
+{
|
|
|
+ struct sctp_authchunk val;
|
|
|
+
|
|
|
+ if (optlen != sizeof(struct sctp_authchunk))
|
|
|
+ return -EINVAL;
|
|
|
+ if (copy_from_user(&val, optval, optlen))
|
|
|
+ return -EFAULT;
|
|
|
+
|
|
|
+ switch (val.sauth_chunk) {
|
|
|
+ case SCTP_CID_INIT:
|
|
|
+ case SCTP_CID_INIT_ACK:
|
|
|
+ case SCTP_CID_SHUTDOWN_COMPLETE:
|
|
|
+ case SCTP_CID_AUTH:
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* add this chunk id to the endpoint */
|
|
|
+ return sctp_auth_ep_add_chunkid(sctp_sk(sk)->ep, val.sauth_chunk);
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * 7.1.19. Get or set the list of supported HMAC Identifiers (SCTP_HMAC_IDENT)
|
|
|
+ *
|
|
|
+ * This option gets or sets the list of HMAC algorithms that the local
|
|
|
+ * endpoint requires the peer to use.
|
|
|
+ */
|
|
|
+static int sctp_setsockopt_hmac_ident(struct sock *sk,
|
|
|
+ char __user *optval,
|
|
|
+ int optlen)
|
|
|
+{
|
|
|
+ struct sctp_hmacalgo *hmacs;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ if (optlen < sizeof(struct sctp_hmacalgo))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ hmacs = kmalloc(optlen, GFP_KERNEL);
|
|
|
+ if (!hmacs)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ if (copy_from_user(hmacs, optval, optlen)) {
|
|
|
+ err = -EFAULT;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (hmacs->shmac_num_idents == 0 ||
|
|
|
+ hmacs->shmac_num_idents > SCTP_AUTH_NUM_HMACS) {
|
|
|
+ err = -EINVAL;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ err = sctp_auth_ep_set_hmacs(sctp_sk(sk)->ep, hmacs);
|
|
|
+out:
|
|
|
+ kfree(hmacs);
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * 7.1.20. Set a shared key (SCTP_AUTH_KEY)
|
|
|
+ *
|
|
|
+ * This option will set a shared secret key which is used to build an
|
|
|
+ * association shared key.
|
|
|
+ */
|
|
|
+static int sctp_setsockopt_auth_key(struct sock *sk,
|
|
|
+ char __user *optval,
|
|
|
+ int optlen)
|
|
|
+{
|
|
|
+ struct sctp_authkey *authkey;
|
|
|
+ struct sctp_association *asoc;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ if (optlen <= sizeof(struct sctp_authkey))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ authkey = kmalloc(optlen, GFP_KERNEL);
|
|
|
+ if (!authkey)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ if (copy_from_user(authkey, optval, optlen)) {
|
|
|
+ ret = -EFAULT;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ asoc = sctp_id2assoc(sk, authkey->sca_assoc_id);
|
|
|
+ if (!asoc && authkey->sca_assoc_id && sctp_style(sk, UDP)) {
|
|
|
+ ret = -EINVAL;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = sctp_auth_set_key(sctp_sk(sk)->ep, asoc, authkey);
|
|
|
+out:
|
|
|
+ kfree(authkey);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * 7.1.21. Get or set the active shared key (SCTP_AUTH_ACTIVE_KEY)
|
|
|
+ *
|
|
|
+ * This option will get or set the active shared key to be used to build
|
|
|
+ * the association shared key.
|
|
|
+ */
|
|
|
+static int sctp_setsockopt_active_key(struct sock *sk,
|
|
|
+ char __user *optval,
|
|
|
+ int optlen)
|
|
|
+{
|
|
|
+ struct sctp_authkeyid val;
|
|
|
+ struct sctp_association *asoc;
|
|
|
+
|
|
|
+ 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_set_active_key(sctp_sk(sk)->ep, asoc,
|
|
|
+ val.scact_keynumber);
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * 7.1.22. Delete a shared key (SCTP_AUTH_DELETE_KEY)
|
|
|
+ *
|
|
|
+ * This set option will delete a shared secret key from use.
|
|
|
+ */
|
|
|
+static int sctp_setsockopt_del_key(struct sock *sk,
|
|
|
+ char __user *optval,
|
|
|
+ int optlen)
|
|
|
+{
|
|
|
+ struct sctp_authkeyid val;
|
|
|
+ struct sctp_association *asoc;
|
|
|
+
|
|
|
+ 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_del_key_id(sctp_sk(sk)->ep, asoc,
|
|
|
+ val.scact_keynumber);
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
/* API 6.2 setsockopt(), getsockopt()
|
|
|
*
|
|
|
* Applications use setsockopt() and getsockopt() to set or retrieve
|
|
@@ -3069,6 +3227,21 @@ SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname,
|
|
|
case SCTP_MAX_BURST:
|
|
|
retval = sctp_setsockopt_maxburst(sk, optval, optlen);
|
|
|
break;
|
|
|
+ case SCTP_AUTH_CHUNK:
|
|
|
+ retval = sctp_setsockopt_auth_chunk(sk, optval, optlen);
|
|
|
+ break;
|
|
|
+ case SCTP_HMAC_IDENT:
|
|
|
+ retval = sctp_setsockopt_hmac_ident(sk, optval, optlen);
|
|
|
+ break;
|
|
|
+ case SCTP_AUTH_KEY:
|
|
|
+ retval = sctp_setsockopt_auth_key(sk, optval, optlen);
|
|
|
+ break;
|
|
|
+ case SCTP_AUTH_ACTIVE_KEY:
|
|
|
+ retval = sctp_setsockopt_active_key(sk, optval, optlen);
|
|
|
+ break;
|
|
|
+ case SCTP_AUTH_DELETE_KEY:
|
|
|
+ retval = sctp_setsockopt_del_key(sk, optval, optlen);
|
|
|
+ break;
|
|
|
default:
|
|
|
retval = -ENOPROTOOPT;
|
|
|
break;
|
|
@@ -4840,6 +5013,118 @@ static int sctp_getsockopt_maxburst(struct sock *sk, int len,
|
|
|
return -ENOTSUPP;
|
|
|
}
|
|
|
|
|
|
+static int sctp_getsockopt_hmac_ident(struct sock *sk, int len,
|
|
|
+ char __user *optval, int __user *optlen)
|
|
|
+{
|
|
|
+ struct sctp_hmac_algo_param *hmacs;
|
|
|
+ __u16 param_len;
|
|
|
+
|
|
|
+ hmacs = sctp_sk(sk)->ep->auth_hmacs_list;
|
|
|
+ param_len = ntohs(hmacs->param_hdr.length);
|
|
|
+
|
|
|
+ if (len < param_len)
|
|
|
+ return -EINVAL;
|
|
|
+ if (put_user(len, optlen))
|
|
|
+ return -EFAULT;
|
|
|
+ if (copy_to_user(optval, hmacs->hmac_ids, len))
|
|
|
+ return -EFAULT;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int sctp_getsockopt_active_key(struct sock *sk, int len,
|
|
|
+ char __user *optval, int __user *optlen)
|
|
|
+{
|
|
|
+ struct sctp_authkeyid val;
|
|
|
+ struct sctp_association *asoc;
|
|
|
+
|
|
|
+ if (len < sizeof(struct sctp_authkeyid))
|
|
|
+ return -EINVAL;
|
|
|
+ if (copy_from_user(&val, optval, sizeof(struct sctp_authkeyid)))
|
|
|
+ return -EFAULT;
|
|
|
+
|
|
|
+ asoc = sctp_id2assoc(sk, val.scact_assoc_id);
|
|
|
+ if (!asoc && val.scact_assoc_id && sctp_style(sk, UDP))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (asoc)
|
|
|
+ val.scact_keynumber = asoc->active_key_id;
|
|
|
+ else
|
|
|
+ val.scact_keynumber = sctp_sk(sk)->ep->active_key_id;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int sctp_getsockopt_peer_auth_chunks(struct sock *sk, int len,
|
|
|
+ char __user *optval, int __user *optlen)
|
|
|
+{
|
|
|
+ struct sctp_authchunks val;
|
|
|
+ struct sctp_association *asoc;
|
|
|
+ struct sctp_chunks_param *ch;
|
|
|
+ char __user *to;
|
|
|
+
|
|
|
+ if (len <= sizeof(struct sctp_authchunks))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (copy_from_user(&val, optval, sizeof(struct sctp_authchunks)))
|
|
|
+ return -EFAULT;
|
|
|
+
|
|
|
+ to = val.gauth_chunks;
|
|
|
+ asoc = sctp_id2assoc(sk, val.gauth_assoc_id);
|
|
|
+ if (!asoc)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ ch = asoc->peer.peer_chunks;
|
|
|
+
|
|
|
+ /* See if the user provided enough room for all the data */
|
|
|
+ if (len < ntohs(ch->param_hdr.length))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ len = ntohs(ch->param_hdr.length);
|
|
|
+ if (put_user(len, optlen))
|
|
|
+ return -EFAULT;
|
|
|
+ if (copy_to_user(to, ch->chunks, len))
|
|
|
+ return -EFAULT;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int sctp_getsockopt_local_auth_chunks(struct sock *sk, int len,
|
|
|
+ char __user *optval, int __user *optlen)
|
|
|
+{
|
|
|
+ struct sctp_authchunks val;
|
|
|
+ struct sctp_association *asoc;
|
|
|
+ struct sctp_chunks_param *ch;
|
|
|
+ char __user *to;
|
|
|
+
|
|
|
+ if (len <= sizeof(struct sctp_authchunks))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (copy_from_user(&val, optval, sizeof(struct sctp_authchunks)))
|
|
|
+ return -EFAULT;
|
|
|
+
|
|
|
+ to = val.gauth_chunks;
|
|
|
+ asoc = sctp_id2assoc(sk, val.gauth_assoc_id);
|
|
|
+ if (!asoc && val.gauth_assoc_id && sctp_style(sk, UDP))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (asoc)
|
|
|
+ ch = (struct sctp_chunks_param*)asoc->c.auth_chunks;
|
|
|
+ else
|
|
|
+ ch = sctp_sk(sk)->ep->auth_chunk_list;
|
|
|
+
|
|
|
+ if (len < ntohs(ch->param_hdr.length))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ len = ntohs(ch->param_hdr.length);
|
|
|
+ if (put_user(len, optlen))
|
|
|
+ return -EFAULT;
|
|
|
+ if (copy_to_user(to, ch->chunks, len))
|
|
|
+ return -EFAULT;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
|
|
|
char __user *optval, int __user *optlen)
|
|
|
{
|
|
@@ -4963,6 +5248,25 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
|
|
|
case SCTP_MAX_BURST:
|
|
|
retval = sctp_getsockopt_maxburst(sk, len, optval, optlen);
|
|
|
break;
|
|
|
+ case SCTP_AUTH_KEY:
|
|
|
+ case SCTP_AUTH_CHUNK:
|
|
|
+ case SCTP_AUTH_DELETE_KEY:
|
|
|
+ retval = -EOPNOTSUPP;
|
|
|
+ break;
|
|
|
+ case SCTP_HMAC_IDENT:
|
|
|
+ retval = sctp_getsockopt_hmac_ident(sk, len, optval, optlen);
|
|
|
+ break;
|
|
|
+ case SCTP_AUTH_ACTIVE_KEY:
|
|
|
+ retval = sctp_getsockopt_active_key(sk, len, optval, optlen);
|
|
|
+ break;
|
|
|
+ case SCTP_PEER_AUTH_CHUNKS:
|
|
|
+ retval = sctp_getsockopt_peer_auth_chunks(sk, len, optval,
|
|
|
+ optlen);
|
|
|
+ break;
|
|
|
+ case SCTP_LOCAL_AUTH_CHUNKS:
|
|
|
+ retval = sctp_getsockopt_local_auth_chunks(sk, len, optval,
|
|
|
+ optlen);
|
|
|
+ break;
|
|
|
default:
|
|
|
retval = -ENOPROTOOPT;
|
|
|
break;
|