Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+140 -7
+11
fs/ceph/mds_client.c
··· 4154 4154 return auth; 4155 4155 } 4156 4156 4157 + static 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 4158 4168 static int verify_authorizer_reply(struct ceph_connection *con) 4159 4169 { ··· 4227 4217 .put = con_put, 4228 4218 .dispatch = dispatch, 4229 4219 .get_authorizer = get_authorizer, 4220 + .add_authorizer_challenge = add_authorizer_challenge, 4230 4221 .verify_authorizer_reply = verify_authorizer_reply, 4231 4222 .invalidate_authorizer = invalidate_authorizer, 4232 4223 .peer_reset = peer_reset,
+8
include/linux/ceph/auth.h
··· 64 64 /* ensure that an existing authorizer is up to date */ 65 65 int (*update_authorizer)(struct ceph_auth_client *ac, int peer_type, 66 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 71 int (*verify_authorizer_reply)(struct ceph_auth_client *ac, 68 72 struct ceph_authorizer *a); 69 73 void (*invalidate_authorizer)(struct ceph_auth_client *ac, ··· 122 118 extern int ceph_auth_update_authorizer(struct ceph_auth_client *ac, 123 119 int peer_type, 124 120 struct ceph_auth_handshake *a); 121 + int ceph_auth_add_authorizer_challenge(struct ceph_auth_client *ac, 122 + struct ceph_authorizer *a, 123 + void *challenge_buf, 124 + int challenge_buf_len); 125 125 extern int ceph_auth_verify_authorizer_reply(struct ceph_auth_client *ac, 126 126 struct ceph_authorizer *a); 127 127 extern void ceph_auth_invalidate_authorizer(struct ceph_auth_client *ac,
+3
include/linux/ceph/messenger.h
··· 31 31 struct ceph_auth_handshake *(*get_authorizer) ( 32 32 struct ceph_connection *con, 33 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 37 int (*verify_authorizer_reply) (struct ceph_connection *con); 35 38 int (*invalidate_authorizer)(struct ceph_connection *con); 36 39
+1 -1
include/linux/ceph/msgr.h
··· 91 91 #define CEPH_MSGR_TAG_SEQ 13 /* 64-bit int follows with seen seq number */ 92 92 #define CEPH_MSGR_TAG_KEEPALIVE2 14 /* keepalive2 byte + ceph_timespec */ 93 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 97 * connection negotiation
+16
net/ceph/auth.c
··· 315 315 } 316 316 EXPORT_SYMBOL(ceph_auth_update_authorizer); 317 317 318 + int 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 + } 332 + EXPORT_SYMBOL(ceph_auth_add_authorizer_challenge); 333 + 318 334 int ceph_auth_verify_authorizer_reply(struct ceph_auth_client *ac, 319 335 struct ceph_authorizer *a) 320 336 {
+67 -5
net/ceph/auth_x.c
··· 295 295 * authorizer. The first part (ceph_x_authorize_a) should already be 296 296 * encoded. 297 297 */ 298 - static int encrypt_authorizer(struct ceph_x_authorizer *au) 298 + static int encrypt_authorizer(struct ceph_x_authorizer *au, 299 + u64 *server_challenge) 299 300 { 300 301 struct ceph_x_authorize_a *msg_a; 301 302 struct ceph_x_authorize_b *msg_b; ··· 309 308 end = au->buf->vec.iov_base + au->buf->vec.iov_len; 310 309 311 310 msg_b = p + ceph_x_encrypt_offset(); 312 - msg_b->struct_v = 1; 311 + msg_b->struct_v = 2; 313 312 msg_b->nonce = cpu_to_le64(au->nonce); 313 + if (server_challenge) { 314 + msg_b->have_challenge = 1; 315 + msg_b->server_challenge_plus_one = 316 + cpu_to_le64(*server_challenge + 1); 317 + } else { 318 + msg_b->have_challenge = 0; 319 + msg_b->server_challenge_plus_one = 0; 320 + } 314 321 315 322 ret = ceph_x_encrypt(&au->session_key, p, end - p, sizeof(*msg_b)); 316 323 if (ret < 0) 317 324 return ret; 318 325 319 326 p += ret; 320 - WARN_ON(p > end); 321 - au->buf->vec.iov_len = p - au->buf->vec.iov_base; 327 + if (server_challenge) { 328 + WARN_ON(p != end); 329 + } else { 330 + WARN_ON(p > end); 331 + au->buf->vec.iov_len = p - au->buf->vec.iov_base; 332 + } 322 333 323 334 return 0; 324 335 } ··· 395 382 le64_to_cpu(msg_a->ticket_blob.secret_id)); 396 383 397 384 get_random_bytes(&au->nonce, sizeof(au->nonce)); 398 - ret = encrypt_authorizer(au); 385 + ret = encrypt_authorizer(au, NULL); 399 386 if (ret) { 400 387 pr_err("failed to encrypt authorizer: %d", ret); 401 388 goto out_au; ··· 677 664 return 0; 678 665 } 679 666 667 + static int decrypt_authorize_challenge(struct ceph_x_authorizer *au, 668 + void *challenge_buf, 669 + int challenge_buf_len, 670 + u64 *server_challenge) 671 + { 672 + struct ceph_x_authorize_challenge *ch = 673 + challenge_buf + sizeof(struct ceph_x_encrypt_header); 674 + int ret; 675 + 676 + /* no leading len */ 677 + ret = __ceph_x_decrypt(&au->session_key, challenge_buf, 678 + challenge_buf_len); 679 + if (ret < 0) 680 + return ret; 681 + if (ret < sizeof(*ch)) { 682 + pr_err("bad size %d for ceph_x_authorize_challenge\n", ret); 683 + return -EINVAL; 684 + } 685 + 686 + *server_challenge = le64_to_cpu(ch->server_challenge); 687 + return 0; 688 + } 689 + 690 + static int ceph_x_add_authorizer_challenge(struct ceph_auth_client *ac, 691 + struct ceph_authorizer *a, 692 + void *challenge_buf, 693 + int challenge_buf_len) 694 + { 695 + struct ceph_x_authorizer *au = (void *)a; 696 + u64 server_challenge; 697 + int ret; 698 + 699 + ret = decrypt_authorize_challenge(au, challenge_buf, challenge_buf_len, 700 + &server_challenge); 701 + if (ret) { 702 + pr_err("failed to decrypt authorize challenge: %d", ret); 703 + return ret; 704 + } 705 + 706 + ret = encrypt_authorizer(au, &server_challenge); 707 + if (ret) { 708 + pr_err("failed to encrypt authorizer w/ challenge: %d", ret); 709 + return ret; 710 + } 711 + 712 + return 0; 713 + } 714 + 680 715 static int ceph_x_verify_authorizer_reply(struct ceph_auth_client *ac, 681 716 struct ceph_authorizer *a) 682 717 { ··· 877 816 .handle_reply = ceph_x_handle_reply, 878 817 .create_authorizer = ceph_x_create_authorizer, 879 818 .update_authorizer = ceph_x_update_authorizer, 819 + .add_authorizer_challenge = ceph_x_add_authorizer_challenge, 880 820 .verify_authorizer_reply = ceph_x_verify_authorizer_reply, 881 821 .invalidate_authorizer = ceph_x_invalidate_authorizer, 882 822 .reset = ceph_x_reset,
+7
net/ceph/auth_x_protocol.h
··· 70 70 struct ceph_x_authorize_b { 71 71 __u8 struct_v; 72 72 __le64 nonce; 73 + __u8 have_challenge; 74 + __le64 server_challenge_plus_one; 75 + } __attribute__ ((packed)); 76 + 77 + struct ceph_x_authorize_challenge { 78 + __u8 struct_v; 79 + __le64 server_challenge; 73 80 } __attribute__ ((packed)); 74 81 75 82 struct ceph_x_authorize_reply {
+16 -1
net/ceph/messenger.c
··· 2080 2080 if (con->auth) { 2081 2081 /* 2082 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 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 2101 ret = con->ops->verify_authorizer_reply(con); 2087 2102 if (ret < 0) { 2088 2103 con->error_msg = "bad authorize reply";
+11
net/ceph/osd_client.c
··· 5393 5393 return auth; 5394 5394 } 5395 5395 5396 + static 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 5397 5407 static int verify_authorizer_reply(struct ceph_connection *con) 5398 5408 { ··· 5452 5442 .put = put_osd_con, 5453 5443 .dispatch = dispatch, 5454 5444 .get_authorizer = get_authorizer, 5445 + .add_authorizer_challenge = add_authorizer_challenge, 5455 5446 .verify_authorizer_reply = verify_authorizer_reply, 5456 5447 .invalidate_authorizer = invalidate_authorizer, 5457 5448 .alloc_msg = alloc_msg,