aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIlya Dryomov <idryomov@gmail.com>2018-07-27 13:18:34 -0400
committerIlya Dryomov <idryomov@gmail.com>2018-08-02 15:33:24 -0400
commit6daca13d2e72bedaaacfc08f873114c9307d5aea (patch)
tree97278ec273414431dd2fab13ccbc3a5d98140cec
parent149cac4a50b0b4081b38b2f38de6ef71c27eaa85 (diff)
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>
-rw-r--r--fs/ceph/mds_client.c11
-rw-r--r--include/linux/ceph/auth.h8
-rw-r--r--include/linux/ceph/messenger.h3
-rw-r--r--include/linux/ceph/msgr.h2
-rw-r--r--net/ceph/auth.c16
-rw-r--r--net/ceph/auth_x.c72
-rw-r--r--net/ceph/auth_x_protocol.h7
-rw-r--r--net/ceph/messenger.c17
-rw-r--r--net/ceph/osd_client.c11
9 files changed, 140 insertions, 7 deletions
diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c
index f5a299daae95..4fc7582233db 100644
--- a/fs/ceph/mds_client.c
+++ b/fs/ceph/mds_client.c
@@ -4154,6 +4154,16 @@ static struct ceph_auth_handshake *get_authorizer(struct ceph_connection *con,
4154 return auth; 4154 return auth;
4155} 4155}
4156 4156
4157static int add_authorizer_challenge(struct ceph_connection *con,
4158 void *challenge_buf, int challenge_buf_len)
4159{
4160 struct ceph_mds_session *s = con->private;
4161 struct ceph_mds_client *mdsc = s->s_mdsc;
4162 struct ceph_auth_client *ac = mdsc->fsc->client->monc.auth;
4163
4164 return ceph_auth_add_authorizer_challenge(ac, s->s_auth.authorizer,
4165 challenge_buf, challenge_buf_len);
4166}
4157 4167
4158static int verify_authorizer_reply(struct ceph_connection *con) 4168static int verify_authorizer_reply(struct ceph_connection *con)
4159{ 4169{
@@ -4217,6 +4227,7 @@ static const struct ceph_connection_operations mds_con_ops = {
4217 .put = con_put, 4227 .put = con_put,
4218 .dispatch = dispatch, 4228 .dispatch = dispatch,
4219 .get_authorizer = get_authorizer, 4229 .get_authorizer = get_authorizer,
4230 .add_authorizer_challenge = add_authorizer_challenge,
4220 .verify_authorizer_reply = verify_authorizer_reply, 4231 .verify_authorizer_reply = verify_authorizer_reply,
4221 .invalidate_authorizer = invalidate_authorizer, 4232 .invalidate_authorizer = invalidate_authorizer,
4222 .peer_reset = peer_reset, 4233 .peer_reset = peer_reset,
diff --git a/include/linux/ceph/auth.h b/include/linux/ceph/auth.h
index e931da8424a4..6728c2ee0205 100644
--- a/include/linux/ceph/auth.h
+++ b/include/linux/ceph/auth.h
@@ -64,6 +64,10 @@ struct ceph_auth_client_ops {
64 /* ensure that an existing authorizer is up to date */ 64 /* ensure that an existing authorizer is up to date */
65 int (*update_authorizer)(struct ceph_auth_client *ac, int peer_type, 65 int (*update_authorizer)(struct ceph_auth_client *ac, int peer_type,
66 struct ceph_auth_handshake *auth); 66 struct ceph_auth_handshake *auth);
67 int (*add_authorizer_challenge)(struct ceph_auth_client *ac,
68 struct ceph_authorizer *a,
69 void *challenge_buf,
70 int challenge_buf_len);
67 int (*verify_authorizer_reply)(struct ceph_auth_client *ac, 71 int (*verify_authorizer_reply)(struct ceph_auth_client *ac,
68 struct ceph_authorizer *a); 72 struct ceph_authorizer *a);
69 void (*invalidate_authorizer)(struct ceph_auth_client *ac, 73 void (*invalidate_authorizer)(struct ceph_auth_client *ac,
@@ -118,6 +122,10 @@ void ceph_auth_destroy_authorizer(struct ceph_authorizer *a);
118extern int ceph_auth_update_authorizer(struct ceph_auth_client *ac, 122extern int ceph_auth_update_authorizer(struct ceph_auth_client *ac,
119 int peer_type, 123 int peer_type,
120 struct ceph_auth_handshake *a); 124 struct ceph_auth_handshake *a);
125int ceph_auth_add_authorizer_challenge(struct ceph_auth_client *ac,
126 struct ceph_authorizer *a,
127 void *challenge_buf,
128 int challenge_buf_len);
121extern int ceph_auth_verify_authorizer_reply(struct ceph_auth_client *ac, 129extern int ceph_auth_verify_authorizer_reply(struct ceph_auth_client *ac,
122 struct ceph_authorizer *a); 130 struct ceph_authorizer *a);
123extern void ceph_auth_invalidate_authorizer(struct ceph_auth_client *ac, 131extern void ceph_auth_invalidate_authorizer(struct ceph_auth_client *ac,
diff --git a/include/linux/ceph/messenger.h b/include/linux/ceph/messenger.h
index 021718570b50..fc2b4491ee0a 100644
--- a/include/linux/ceph/messenger.h
+++ b/include/linux/ceph/messenger.h
@@ -31,6 +31,9 @@ struct ceph_connection_operations {
31 struct ceph_auth_handshake *(*get_authorizer) ( 31 struct ceph_auth_handshake *(*get_authorizer) (
32 struct ceph_connection *con, 32 struct ceph_connection *con,
33 int *proto, int force_new); 33 int *proto, int force_new);
34 int (*add_authorizer_challenge)(struct ceph_connection *con,
35 void *challenge_buf,
36 int challenge_buf_len);
34 int (*verify_authorizer_reply) (struct ceph_connection *con); 37 int (*verify_authorizer_reply) (struct ceph_connection *con);
35 int (*invalidate_authorizer)(struct ceph_connection *con); 38 int (*invalidate_authorizer)(struct ceph_connection *con);
36 39
diff --git a/include/linux/ceph/msgr.h b/include/linux/ceph/msgr.h
index 73ae2a926548..9e50aede46c8 100644
--- a/include/linux/ceph/msgr.h
+++ b/include/linux/ceph/msgr.h
@@ -91,7 +91,7 @@ struct ceph_entity_inst {
91#define CEPH_MSGR_TAG_SEQ 13 /* 64-bit int follows with seen seq number */ 91#define CEPH_MSGR_TAG_SEQ 13 /* 64-bit int follows with seen seq number */
92#define CEPH_MSGR_TAG_KEEPALIVE2 14 /* keepalive2 byte + ceph_timespec */ 92#define CEPH_MSGR_TAG_KEEPALIVE2 14 /* keepalive2 byte + ceph_timespec */
93#define CEPH_MSGR_TAG_KEEPALIVE2_ACK 15 /* keepalive2 reply */ 93#define CEPH_MSGR_TAG_KEEPALIVE2_ACK 15 /* keepalive2 reply */
94 94#define CEPH_MSGR_TAG_CHALLENGE_AUTHORIZER 16 /* cephx v2 doing server challenge */
95 95
96/* 96/*
97 * connection negotiation 97 * connection negotiation
diff --git a/net/ceph/auth.c b/net/ceph/auth.c
index dbde2b3c3c15..fbeee068ea14 100644
--- a/net/ceph/auth.c
+++ b/net/ceph/auth.c
@@ -315,6 +315,22 @@ int ceph_auth_update_authorizer(struct ceph_auth_client *ac,
315} 315}
316EXPORT_SYMBOL(ceph_auth_update_authorizer); 316EXPORT_SYMBOL(ceph_auth_update_authorizer);
317 317
318int ceph_auth_add_authorizer_challenge(struct ceph_auth_client *ac,
319 struct ceph_authorizer *a,
320 void *challenge_buf,
321 int challenge_buf_len)
322{
323 int ret = 0;
324
325 mutex_lock(&ac->mutex);
326 if (ac->ops && ac->ops->add_authorizer_challenge)
327 ret = ac->ops->add_authorizer_challenge(ac, a, challenge_buf,
328 challenge_buf_len);
329 mutex_unlock(&ac->mutex);
330 return ret;
331}
332EXPORT_SYMBOL(ceph_auth_add_authorizer_challenge);
333
318int ceph_auth_verify_authorizer_reply(struct ceph_auth_client *ac, 334int ceph_auth_verify_authorizer_reply(struct ceph_auth_client *ac,
319 struct ceph_authorizer *a) 335 struct ceph_authorizer *a)
320{ 336{
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:
295 * authorizer. The first part (ceph_x_authorize_a) should already be 295 * authorizer. The first part (ceph_x_authorize_a) should already be
296 * encoded. 296 * encoded.
297 */ 297 */
298static int encrypt_authorizer(struct ceph_x_authorizer *au) 298static int encrypt_authorizer(struct ceph_x_authorizer *au,
299 u64 *server_challenge)
299{ 300{
300 struct ceph_x_authorize_a *msg_a; 301 struct ceph_x_authorize_a *msg_a;
301 struct ceph_x_authorize_b *msg_b; 302 struct ceph_x_authorize_b *msg_b;
@@ -308,16 +309,28 @@ static int encrypt_authorizer(struct ceph_x_authorizer *au)
308 end = au->buf->vec.iov_base + au->buf->vec.iov_len; 309 end = au->buf->vec.iov_base + au->buf->vec.iov_len;
309 310
310 msg_b = p + ceph_x_encrypt_offset(); 311 msg_b = p + ceph_x_encrypt_offset();
311 msg_b->struct_v = 1; 312 msg_b->struct_v = 2;
312 msg_b->nonce = cpu_to_le64(au->nonce); 313 msg_b->nonce = cpu_to_le64(au->nonce);
314 if (server_challenge) {
315 msg_b->have_challenge = 1;
316 msg_b->server_challenge_plus_one =
317 cpu_to_le64(*server_challenge + 1);
318 } else {
319 msg_b->have_challenge = 0;
320 msg_b->server_challenge_plus_one = 0;
321 }
313 322
314 ret = ceph_x_encrypt(&au->session_key, p, end - p, sizeof(*msg_b)); 323 ret = ceph_x_encrypt(&au->session_key, p, end - p, sizeof(*msg_b));
315 if (ret < 0) 324 if (ret < 0)
316 return ret; 325 return ret;
317 326
318 p += ret; 327 p += ret;
319 WARN_ON(p > end); 328 if (server_challenge) {
320 au->buf->vec.iov_len = p - au->buf->vec.iov_base; 329 WARN_ON(p != end);
330 } else {
331 WARN_ON(p > end);
332 au->buf->vec.iov_len = p - au->buf->vec.iov_base;
333 }
321 334
322 return 0; 335 return 0;
323} 336}
@@ -382,7 +395,7 @@ static int ceph_x_build_authorizer(struct ceph_auth_client *ac,
382 le64_to_cpu(msg_a->ticket_blob.secret_id)); 395 le64_to_cpu(msg_a->ticket_blob.secret_id));
383 396
384 get_random_bytes(&au->nonce, sizeof(au->nonce)); 397 get_random_bytes(&au->nonce, sizeof(au->nonce));
385 ret = encrypt_authorizer(au); 398 ret = encrypt_authorizer(au, NULL);
386 if (ret) { 399 if (ret) {
387 pr_err("failed to encrypt authorizer: %d", ret); 400 pr_err("failed to encrypt authorizer: %d", ret);
388 goto out_au; 401 goto out_au;
@@ -664,6 +677,54 @@ static int ceph_x_update_authorizer(
664 return 0; 677 return 0;
665} 678}
666 679
680static int decrypt_authorize_challenge(struct ceph_x_authorizer *au,
681 void *challenge_buf,
682 int challenge_buf_len,
683 u64 *server_challenge)
684{
685 struct ceph_x_authorize_challenge *ch =
686 challenge_buf + sizeof(struct ceph_x_encrypt_header);
687 int ret;
688
689 /* no leading len */
690 ret = __ceph_x_decrypt(&au->session_key, challenge_buf,
691 challenge_buf_len);
692 if (ret < 0)
693 return ret;
694 if (ret < sizeof(*ch)) {
695 pr_err("bad size %d for ceph_x_authorize_challenge\n", ret);
696 return -EINVAL;
697 }
698
699 *server_challenge = le64_to_cpu(ch->server_challenge);
700 return 0;
701}
702
703static int ceph_x_add_authorizer_challenge(struct ceph_auth_client *ac,
704 struct ceph_authorizer *a,
705 void *challenge_buf,
706 int challenge_buf_len)
707{
708 struct ceph_x_authorizer *au = (void *)a;
709 u64 server_challenge;
710 int ret;
711
712 ret = decrypt_authorize_challenge(au, challenge_buf, challenge_buf_len,
713 &server_challenge);
714 if (ret) {
715 pr_err("failed to decrypt authorize challenge: %d", ret);
716 return ret;
717 }
718
719 ret = encrypt_authorizer(au, &server_challenge);
720 if (ret) {
721 pr_err("failed to encrypt authorizer w/ challenge: %d", ret);
722 return ret;
723 }
724
725 return 0;
726}
727
667static int ceph_x_verify_authorizer_reply(struct ceph_auth_client *ac, 728static int ceph_x_verify_authorizer_reply(struct ceph_auth_client *ac,
668 struct ceph_authorizer *a) 729 struct ceph_authorizer *a)
669{ 730{
@@ -816,6 +877,7 @@ static const struct ceph_auth_client_ops ceph_x_ops = {
816 .handle_reply = ceph_x_handle_reply, 877 .handle_reply = ceph_x_handle_reply,
817 .create_authorizer = ceph_x_create_authorizer, 878 .create_authorizer = ceph_x_create_authorizer,
818 .update_authorizer = ceph_x_update_authorizer, 879 .update_authorizer = ceph_x_update_authorizer,
880 .add_authorizer_challenge = ceph_x_add_authorizer_challenge,
819 .verify_authorizer_reply = ceph_x_verify_authorizer_reply, 881 .verify_authorizer_reply = ceph_x_verify_authorizer_reply,
820 .invalidate_authorizer = ceph_x_invalidate_authorizer, 882 .invalidate_authorizer = ceph_x_invalidate_authorizer,
821 .reset = ceph_x_reset, 883 .reset = ceph_x_reset,
diff --git a/net/ceph/auth_x_protocol.h b/net/ceph/auth_x_protocol.h
index 32c13d763b9a..24b0b74564d0 100644
--- a/net/ceph/auth_x_protocol.h
+++ b/net/ceph/auth_x_protocol.h
@@ -70,6 +70,13 @@ struct ceph_x_authorize_a {
70struct ceph_x_authorize_b { 70struct ceph_x_authorize_b {
71 __u8 struct_v; 71 __u8 struct_v;
72 __le64 nonce; 72 __le64 nonce;
73 __u8 have_challenge;
74 __le64 server_challenge_plus_one;
75} __attribute__ ((packed));
76
77struct ceph_x_authorize_challenge {
78 __u8 struct_v;
79 __le64 server_challenge;
73} __attribute__ ((packed)); 80} __attribute__ ((packed));
74 81
75struct ceph_x_authorize_reply { 82struct ceph_x_authorize_reply {
diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c
index 500cc3da586f..e915c8bce117 100644
--- a/net/ceph/messenger.c
+++ b/net/ceph/messenger.c
@@ -2080,9 +2080,24 @@ static int process_connect(struct ceph_connection *con)
2080 if (con->auth) { 2080 if (con->auth) {
2081 /* 2081 /*
2082 * Any connection that defines ->get_authorizer() 2082 * Any connection that defines ->get_authorizer()
2083 * should also define ->verify_authorizer_reply(). 2083 * should also define ->add_authorizer_challenge() and
2084 * ->verify_authorizer_reply().
2085 *
2084 * See get_connect_authorizer(). 2086 * See get_connect_authorizer().
2085 */ 2087 */
2088 if (con->in_reply.tag == CEPH_MSGR_TAG_CHALLENGE_AUTHORIZER) {
2089 ret = con->ops->add_authorizer_challenge(
2090 con, con->auth->authorizer_reply_buf,
2091 le32_to_cpu(con->in_reply.authorizer_len));
2092 if (ret < 0)
2093 return ret;
2094
2095 con_out_kvec_reset(con);
2096 __prepare_write_connect(con);
2097 prepare_read_connect(con);
2098 return 0;
2099 }
2100
2086 ret = con->ops->verify_authorizer_reply(con); 2101 ret = con->ops->verify_authorizer_reply(con);
2087 if (ret < 0) { 2102 if (ret < 0) {
2088 con->error_msg = "bad authorize reply"; 2103 con->error_msg = "bad authorize reply";
diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c
index 8002b8e9ce24..60934bd8796c 100644
--- a/net/ceph/osd_client.c
+++ b/net/ceph/osd_client.c
@@ -5393,6 +5393,16 @@ static struct ceph_auth_handshake *get_authorizer(struct ceph_connection *con,
5393 return auth; 5393 return auth;
5394} 5394}
5395 5395
5396static int add_authorizer_challenge(struct ceph_connection *con,
5397 void *challenge_buf, int challenge_buf_len)
5398{
5399 struct ceph_osd *o = con->private;
5400 struct ceph_osd_client *osdc = o->o_osdc;
5401 struct ceph_auth_client *ac = osdc->client->monc.auth;
5402
5403 return ceph_auth_add_authorizer_challenge(ac, o->o_auth.authorizer,
5404 challenge_buf, challenge_buf_len);
5405}
5396 5406
5397static int verify_authorizer_reply(struct ceph_connection *con) 5407static int verify_authorizer_reply(struct ceph_connection *con)
5398{ 5408{
@@ -5442,6 +5452,7 @@ static const struct ceph_connection_operations osd_con_ops = {
5442 .put = put_osd_con, 5452 .put = put_osd_con,
5443 .dispatch = dispatch, 5453 .dispatch = dispatch,
5444 .get_authorizer = get_authorizer, 5454 .get_authorizer = get_authorizer,
5455 .add_authorizer_challenge = add_authorizer_challenge,
5445 .verify_authorizer_reply = verify_authorizer_reply, 5456 .verify_authorizer_reply = verify_authorizer_reply,
5446 .invalidate_authorizer = invalidate_authorizer, 5457 .invalidate_authorizer = invalidate_authorizer,
5447 .alloc_msg = alloc_msg, 5458 .alloc_msg = alloc_msg,