summaryrefslogtreecommitdiffstats
path: root/net/ceph/auth_x.c
diff options
context:
space:
mode:
authorIlya Dryomov <idryomov@gmail.com>2018-07-27 19:18:34 +0200
committerIlya Dryomov <idryomov@gmail.com>2018-08-02 21:33:24 +0200
commit6daca13d2e72bedaaacfc08f873114c9307d5aea (patch)
tree97278ec273414431dd2fab13ccbc3a5d98140cec /net/ceph/auth_x.c
parentlibceph: factor out encrypt_authorizer() (diff)
downloadlinux-6daca13d2e72bedaaacfc08f873114c9307d5aea.tar.xz
linux-6daca13d2e72bedaaacfc08f873114c9307d5aea.zip
libceph: add authorizer challenge
When a client authenticates with a service, an authorizer is sent with a nonce to the service (ceph_x_authorize_[ab]) and the service responds with a mutation of that nonce (ceph_x_authorize_reply). This lets the client verify the service is who it says it is but it doesn't protect against a replay: someone can trivially capture the exchange and reuse the same authorizer to authenticate themselves. Allow the service to reject an initial authorizer with a random challenge (ceph_x_authorize_challenge). The client then has to respond with an updated authorizer proving they are able to decrypt the service's challenge and that the new authorizer was produced for this specific connection instance. The accepting side requires this challenge and response unconditionally if the client side advertises they have CEPHX_V2 feature bit. This addresses CVE-2018-1128. Link: http://tracker.ceph.com/issues/24836 Signed-off-by: Ilya Dryomov <idryomov@gmail.com> Reviewed-by: Sage Weil <sage@redhat.com>
Diffstat (limited to 'net/ceph/auth_x.c')
-rw-r--r--net/ceph/auth_x.c72
1 files changed, 67 insertions, 5 deletions
diff --git a/net/ceph/auth_x.c b/net/ceph/auth_x.c
index 61cccb93f653..512eed4291fe 100644
--- a/net/ceph/auth_x.c
+++ b/net/ceph/auth_x.c
@@ -295,7 +295,8 @@ bad:
* authorizer. The first part (ceph_x_authorize_a) should already be
* encoded.
*/
-static int encrypt_authorizer(struct ceph_x_authorizer *au)
+static int encrypt_authorizer(struct ceph_x_authorizer *au,
+ u64 *server_challenge)
{
struct ceph_x_authorize_a *msg_a;
struct ceph_x_authorize_b *msg_b;
@@ -308,16 +309,28 @@ static int encrypt_authorizer(struct ceph_x_authorizer *au)
end = au->buf->vec.iov_base + au->buf->vec.iov_len;
msg_b = p + ceph_x_encrypt_offset();
- msg_b->struct_v = 1;
+ msg_b->struct_v = 2;
msg_b->nonce = cpu_to_le64(au->nonce);
+ if (server_challenge) {
+ msg_b->have_challenge = 1;
+ msg_b->server_challenge_plus_one =
+ cpu_to_le64(*server_challenge + 1);
+ } else {
+ msg_b->have_challenge = 0;
+ msg_b->server_challenge_plus_one = 0;
+ }
ret = ceph_x_encrypt(&au->session_key, p, end - p, sizeof(*msg_b));
if (ret < 0)
return ret;
p += ret;
- WARN_ON(p > end);
- au->buf->vec.iov_len = p - au->buf->vec.iov_base;
+ if (server_challenge) {
+ WARN_ON(p != end);
+ } else {
+ WARN_ON(p > end);
+ au->buf->vec.iov_len = p - au->buf->vec.iov_base;
+ }
return 0;
}
@@ -382,7 +395,7 @@ static int ceph_x_build_authorizer(struct ceph_auth_client *ac,
le64_to_cpu(msg_a->ticket_blob.secret_id));
get_random_bytes(&au->nonce, sizeof(au->nonce));
- ret = encrypt_authorizer(au);
+ ret = encrypt_authorizer(au, NULL);
if (ret) {
pr_err("failed to encrypt authorizer: %d", ret);
goto out_au;
@@ -664,6 +677,54 @@ static int ceph_x_update_authorizer(
return 0;
}
+static int decrypt_authorize_challenge(struct ceph_x_authorizer *au,
+ void *challenge_buf,
+ int challenge_buf_len,
+ u64 *server_challenge)
+{
+ struct ceph_x_authorize_challenge *ch =
+ challenge_buf + sizeof(struct ceph_x_encrypt_header);
+ int ret;
+
+ /* no leading len */
+ ret = __ceph_x_decrypt(&au->session_key, challenge_buf,
+ challenge_buf_len);
+ if (ret < 0)
+ return ret;
+ if (ret < sizeof(*ch)) {
+ pr_err("bad size %d for ceph_x_authorize_challenge\n", ret);
+ return -EINVAL;
+ }
+
+ *server_challenge = le64_to_cpu(ch->server_challenge);
+ return 0;
+}
+
+static int ceph_x_add_authorizer_challenge(struct ceph_auth_client *ac,
+ struct ceph_authorizer *a,
+ void *challenge_buf,
+ int challenge_buf_len)
+{
+ struct ceph_x_authorizer *au = (void *)a;
+ u64 server_challenge;
+ int ret;
+
+ ret = decrypt_authorize_challenge(au, challenge_buf, challenge_buf_len,
+ &server_challenge);
+ if (ret) {
+ pr_err("failed to decrypt authorize challenge: %d", ret);
+ return ret;
+ }
+
+ ret = encrypt_authorizer(au, &server_challenge);
+ if (ret) {
+ pr_err("failed to encrypt authorizer w/ challenge: %d", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
static int ceph_x_verify_authorizer_reply(struct ceph_auth_client *ac,
struct ceph_authorizer *a)
{
@@ -816,6 +877,7 @@ static const struct ceph_auth_client_ops ceph_x_ops = {
.handle_reply = ceph_x_handle_reply,
.create_authorizer = ceph_x_create_authorizer,
.update_authorizer = ceph_x_update_authorizer,
+ .add_authorizer_challenge = ceph_x_add_authorizer_challenge,
.verify_authorizer_reply = ceph_x_verify_authorizer_reply,
.invalidate_authorizer = ceph_x_invalidate_authorizer,
.reset = ceph_x_reset,