Type Confusion via Stale Function Pointers in ceph_auth_none_create_authorizer
Summary
net/ceph/auth_none.c — ceph_auth_none_create_authorizer() — fails to clear the sign_message and check_message_signature function pointers in the ceph_auth_handshake struct when creating an auth_none authorizer. If a Ceph connection previously used CephX authentication and then downgrades to auth_none (e.g., on reconnection with force_new=true), stale CephX function pointers persist in the handshake. When any subsequent message is signed, the kernel calls ceph_x_sign_message() while auth->authorizer actually points to a ceph_none_authorizer — causing type confusion, out-of-bounds memory access, and potential kernel memory corruption or privilege escalation.
Vulnerability Details
Root Cause
In ceph_auth_none_create_authorizer() (net/ceph/auth_none.c:93):
auth->authorizer = (struct ceph_authorizer *) au;
auth->authorizer_buf = au->buf;
auth->authorizer_buf_len = au->buf_len;
auth->authorizer_reply_buf = NULL;
auth->authorizer_reply_buf_len = 0;
// BUG: auth->sign_message and auth->check_message_signature are NOT cleared
Compare to ceph_x_create_authorizer() (net/ceph/auth_x.c:765), which explicitly sets them:
auth->sign_message = ac->ops->sign_message;
auth->check_message_signature = ac->ops->check_message_signature;
Exploitation Path
-
Initial connection: CephX auth is used.
ceph_x_create_authorizer()sets:auth->authorizer→struct ceph_x_authorizer *auth->sign_message→ceph_x_sign_messageauth->check_message_signature→ceph_x_check_message_signature
-
Reconnection with
force_new=trueand auth downgrade to auth_none (e.g., server no longer requires CephX):ceph_auth_destroy_authorizer()frees the oldceph_x_authorizerceph_auth_none_create_authorizer()creates a newceph_none_authorizerand setsauth->authorizerto it- Stale pointers remain:
auth->sign_messagestill points toceph_x_sign_message
-
Message send:
ceph_auth_sign_message()checksauth->sign_message != NULLand calls it:// net/ceph/auth_x.c:1038 ret = calc_signature((struct ceph_x_authorizer *)auth->authorizer, msg, &sig);auth->authorizeris now aceph_none_authorizer, but is cast toceph_x_authorizer.
Type Confusion in calc_signature
calc_signature() (net/ceph/auth_x.c:964) accesses fields by offset assuming ceph_x_authorizer layout:
struct ceph_x_authorizer layout (64-bit):
offset 0: base (destroy fn ptr, 8 bytes)
offset 8: session_key (struct ceph_crypto_key, ~32 bytes)
- type (int, 4B)
- created (ceph_timespec, 8B)
- len (int, 4B)
- key (void*, 8B) ← kernel pointer read from auth_none buf
- tfm (skcipher*, 8B)
offset ~40: buf (pointer, 8B)
offset ~48: service (u32, 4B)
offset ~52: nonce (u64, 8B)
offset ~60: secret_id (u64, 8B)
offset ~72: enc_buf[128] ← written to by calc_signature
struct ceph_none_authorizer layout (64-bit):
offset 0: base (destroy fn ptr, 8 bytes)
offset 8: buf[128] ← read as session_key (crypto key!)
offset 136: buf_len (int)
Total: ~140 bytes
When calc_signature reads au->session_key (offset 8), it reads 32 bytes of ceph_none_authorizer::buf — attacker-influenced authorizer data — and treats it as a ceph_crypto_key. The key field within (a void pointer at offset 8+16=24, so buf[24..31]) is read as a kernel pointer and passed to ceph_x_encrypt().
When calc_signature writes to au->enc_buf (offset ~72), it writes encrypted data into the middle of ceph_none_authorizer::buf[64..191] — overflowing the 128-byte buf array by roughly 64 bytes, corrupting adjacent heap memory.
Impact
| Effect | Severity |
|---|---|
Kernel heap buffer overflow (~64B past end of buf[128]) |
Critical |
Arbitrary kernel pointer dereference via attacker-influenced session_key.key |
Critical |
| Kernel crash (null deref / invalid pointer from buf contents) | High |
| Kernel memory disclosure via crafted HMAC signature leaking heap data | High |
An attacker who controls the contents of the auth_none authorizer buffer (by controlling the entity name or global_id encoded into it) can place a crafted pointer at buf[24..31], which is then dereferenced as a ceph_crypto_key::key pointer inside ceph_x_encrypt(). Combined with the heap overflow from the enc_buf write, this is a strong primitive for local privilege escalation from a malicious Ceph client context.
Affected Code
| File | Line | Issue |
|---|---|---|
net/ceph/auth_none.c |
93–119 | create_authorizer does not clear sign_message/check_message_signature |
net/ceph/auth_x.c |
1029–1045 | ceph_x_sign_message unsafely casts auth->authorizer to ceph_x_authorizer |
net/ceph/auth_x.c |
964–1028 | calc_signature writes to enc_buf at a ceph_x offset, overflowing auth_none’s buf[128] |
Fix
In ceph_auth_none_create_authorizer(), explicitly clear the stale CephX function pointers:
auth->sign_message = NULL;
auth->check_message_signature = NULL;