]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
libceph: add authorizer challenge
authorIlya Dryomov <idryomov@gmail.com>
Fri, 27 Jul 2018 17:18:34 +0000 (19:18 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 8 Dec 2018 12:05:10 +0000 (13:05 +0100)
commit 6daca13d2e72bedaaacfc08f873114c9307d5aea upstream.

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>
Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/ceph/mds_client.c
include/linux/ceph/auth.h
include/linux/ceph/messenger.h
include/linux/ceph/msgr.h
net/ceph/auth.c
net/ceph/auth_x.c
net/ceph/auth_x_protocol.h
net/ceph/messenger.c
net/ceph/osd_client.c

index 7e1891e4917ada563f95b59dd051c62a800ca5b5..6cbd0d805c9d62ca2da17a4892ebcb808e6fed74 100644 (file)
@@ -3983,6 +3983,16 @@ static struct ceph_auth_handshake *get_authorizer(struct ceph_connection *con,
        return auth;
 }
 
+static int add_authorizer_challenge(struct ceph_connection *con,
+                                   void *challenge_buf, int challenge_buf_len)
+{
+       struct ceph_mds_session *s = con->private;
+       struct ceph_mds_client *mdsc = s->s_mdsc;
+       struct ceph_auth_client *ac = mdsc->fsc->client->monc.auth;
+
+       return ceph_auth_add_authorizer_challenge(ac, s->s_auth.authorizer,
+                                           challenge_buf, challenge_buf_len);
+}
 
 static int verify_authorizer_reply(struct ceph_connection *con)
 {
@@ -4046,6 +4056,7 @@ static const struct ceph_connection_operations mds_con_ops = {
        .put = con_put,
        .dispatch = dispatch,
        .get_authorizer = get_authorizer,
+       .add_authorizer_challenge = add_authorizer_challenge,
        .verify_authorizer_reply = verify_authorizer_reply,
        .invalidate_authorizer = invalidate_authorizer,
        .peer_reset = peer_reset,
index a6747789fe5c25679d99fcc42b74042ea29251a3..035f26a04364c8fcdfb2964d61ec8a114eb3680c 100644 (file)
@@ -63,6 +63,10 @@ struct ceph_auth_client_ops {
        /* ensure that an existing authorizer is up to date */
        int (*update_authorizer)(struct ceph_auth_client *ac, int peer_type,
                                 struct ceph_auth_handshake *auth);
+       int (*add_authorizer_challenge)(struct ceph_auth_client *ac,
+                                       struct ceph_authorizer *a,
+                                       void *challenge_buf,
+                                       int challenge_buf_len);
        int (*verify_authorizer_reply)(struct ceph_auth_client *ac,
                                       struct ceph_authorizer *a);
        void (*invalidate_authorizer)(struct ceph_auth_client *ac,
@@ -117,6 +121,10 @@ void ceph_auth_destroy_authorizer(struct ceph_authorizer *a);
 extern int ceph_auth_update_authorizer(struct ceph_auth_client *ac,
                                       int peer_type,
                                       struct ceph_auth_handshake *a);
+int ceph_auth_add_authorizer_challenge(struct ceph_auth_client *ac,
+                                      struct ceph_authorizer *a,
+                                      void *challenge_buf,
+                                      int challenge_buf_len);
 extern int ceph_auth_verify_authorizer_reply(struct ceph_auth_client *ac,
                                             struct ceph_authorizer *a);
 extern void ceph_auth_invalidate_authorizer(struct ceph_auth_client *ac,
index 08b748797f10751a2707c2060648be95e62bb03e..5e1c9c80d5367c84577ea31c4fb3f491b88c2fb1 100644 (file)
@@ -30,6 +30,9 @@ struct ceph_connection_operations {
        struct ceph_auth_handshake *(*get_authorizer) (
                                struct ceph_connection *con,
                               int *proto, int force_new);
+       int (*add_authorizer_challenge)(struct ceph_connection *con,
+                                       void *challenge_buf,
+                                       int challenge_buf_len);
        int (*verify_authorizer_reply) (struct ceph_connection *con);
        int (*invalidate_authorizer)(struct ceph_connection *con);
 
index 0fe2656ac415711cce5bfc53f3c9ed753bf3ed26..063f9d7f1b7466317eadd164cb651868572f76df 100644 (file)
@@ -90,7 +90,7 @@ struct ceph_entity_inst {
 #define CEPH_MSGR_TAG_SEQ           13 /* 64-bit int follows with seen seq number */
 #define CEPH_MSGR_TAG_KEEPALIVE2    14 /* keepalive2 byte + ceph_timespec */
 #define CEPH_MSGR_TAG_KEEPALIVE2_ACK 15 /* keepalive2 reply */
-
+#define CEPH_MSGR_TAG_CHALLENGE_AUTHORIZER 16  /* cephx v2 doing server challenge */
 
 /*
  * connection negotiation
index 48bb8d95195b1df91d3f34d20d815d7021e10693..8e79dca817485c58bde60ae11d4e77cbd321fde9 100644 (file)
@@ -314,6 +314,22 @@ int ceph_auth_update_authorizer(struct ceph_auth_client *ac,
 }
 EXPORT_SYMBOL(ceph_auth_update_authorizer);
 
+int ceph_auth_add_authorizer_challenge(struct ceph_auth_client *ac,
+                                      struct ceph_authorizer *a,
+                                      void *challenge_buf,
+                                      int challenge_buf_len)
+{
+       int ret = 0;
+
+       mutex_lock(&ac->mutex);
+       if (ac->ops && ac->ops->add_authorizer_challenge)
+               ret = ac->ops->add_authorizer_challenge(ac, a, challenge_buf,
+                                                       challenge_buf_len);
+       mutex_unlock(&ac->mutex);
+       return ret;
+}
+EXPORT_SYMBOL(ceph_auth_add_authorizer_challenge);
+
 int ceph_auth_verify_authorizer_reply(struct ceph_auth_client *ac,
                                      struct ceph_authorizer *a)
 {
index aeb47b867ccfe133b82f4b390b900e4aa297698b..35a5694cefb1cf3e55eabc89c7d51328e2d75ab6 100644 (file)
@@ -291,7 +291,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;
@@ -304,16 +305,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;
 }
@@ -378,7 +391,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;
@@ -660,6 +673,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)
 {
@@ -812,6 +873,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,
index 671d30576c4f5359649b1322033f53cdbc89c9c0..a7cd203aacc2569a31c3a780410c27ce4894c4b0 100644 (file)
@@ -69,6 +69,13 @@ struct ceph_x_authorize_a {
 struct ceph_x_authorize_b {
        __u8 struct_v;
        __le64 nonce;
+       __u8 have_challenge;
+       __le64 server_challenge_plus_one;
+} __attribute__ ((packed));
+
+struct ceph_x_authorize_challenge {
+       __u8 struct_v;
+       __le64 server_challenge;
 } __attribute__ ((packed));
 
 struct ceph_x_authorize_reply {
index 1b97103125b8f96511e7aa7e55b1eb1b302a8e8c..d3ceb13ec3d993f1339db1bc11b05b57ed4ea5c8 100644 (file)
@@ -2037,9 +2037,24 @@ static int process_connect(struct ceph_connection *con)
        if (con->auth) {
                /*
                 * Any connection that defines ->get_authorizer()
-                * should also define ->verify_authorizer_reply().
+                * should also define ->add_authorizer_challenge() and
+                * ->verify_authorizer_reply().
+                *
                 * See get_connect_authorizer().
                 */
+               if (con->in_reply.tag == CEPH_MSGR_TAG_CHALLENGE_AUTHORIZER) {
+                       ret = con->ops->add_authorizer_challenge(
+                                   con, con->auth->authorizer_reply_buf,
+                                   le32_to_cpu(con->in_reply.authorizer_len));
+                       if (ret < 0)
+                               return ret;
+
+                       con_out_kvec_reset(con);
+                       __prepare_write_connect(con);
+                       prepare_read_connect(con);
+                       return 0;
+               }
+
                ret = con->ops->verify_authorizer_reply(con);
                if (ret < 0) {
                        con->error_msg = "bad authorize reply";
index 13110fac56f553bf6440f40bfe45ab6f8dd040ea..70ccb0716fc5250754a9030a904abc6e17def812 100644 (file)
@@ -4478,6 +4478,16 @@ static struct ceph_auth_handshake *get_authorizer(struct ceph_connection *con,
        return auth;
 }
 
+static int add_authorizer_challenge(struct ceph_connection *con,
+                                   void *challenge_buf, int challenge_buf_len)
+{
+       struct ceph_osd *o = con->private;
+       struct ceph_osd_client *osdc = o->o_osdc;
+       struct ceph_auth_client *ac = osdc->client->monc.auth;
+
+       return ceph_auth_add_authorizer_challenge(ac, o->o_auth.authorizer,
+                                           challenge_buf, challenge_buf_len);
+}
 
 static int verify_authorizer_reply(struct ceph_connection *con)
 {
@@ -4519,6 +4529,7 @@ static const struct ceph_connection_operations osd_con_ops = {
        .put = put_osd_con,
        .dispatch = dispatch,
        .get_authorizer = get_authorizer,
+       .add_authorizer_challenge = add_authorizer_challenge,
        .verify_authorizer_reply = verify_authorizer_reply,
        .invalidate_authorizer = invalidate_authorizer,
        .alloc_msg = alloc_msg,