Type Confusion via Stale Function Pointers in ceph_auth_none_create_authorizer

Summary

net/ceph/auth_none.cceph_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

  1. Initial connection: CephX auth is used. ceph_x_create_authorizer() sets:

    • auth->authorizerstruct ceph_x_authorizer *
    • auth->sign_messageceph_x_sign_message
    • auth->check_message_signatureceph_x_check_message_signature
  2. Reconnection with force_new=true and auth downgrade to auth_none (e.g., server no longer requires CephX):

    • ceph_auth_destroy_authorizer() frees the old ceph_x_authorizer
    • ceph_auth_none_create_authorizer() creates a new ceph_none_authorizer and sets auth->authorizer to it
    • Stale pointers remain: auth->sign_message still points to ceph_x_sign_message
  3. Message send: ceph_auth_sign_message() checks auth->sign_message != NULL and calls it:

    // net/ceph/auth_x.c:1038
    ret = calc_signature((struct ceph_x_authorizer *)auth->authorizer, msg, &sig);
    

    auth->authorizer is now a ceph_none_authorizer, but is cast to ceph_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;