rxrpc: Don't retain the server key in the connection
Don't retain a pointer to the server key in the connection, but rather get it on demand when the server has to deal with a response packet. This is necessary to implement RxGK (GSSAPI-mediated transport class), where we can't know which key we'll need until we've challenged the client and got back the response. This also means that we don't need to do a key search in the accept path in softirq mode. Also, whilst we're at it, allow the security class to ask for a kvno and encoding-type variant of a server key as RxGK needs different keys for different encoding types. Keys of this type have an extra bit in the description: "<service-id>:<security-index>:<kvno>:<enctype>" Signed-off-by: David Howells <dhowells@redhat.com>
This commit is contained in:
Родитель
41057ebde0
Коммит
ec832bd06d
|
@ -441,7 +441,6 @@ struct rxrpc_connection {
|
||||||
struct list_head link; /* link in master connection list */
|
struct list_head link; /* link in master connection list */
|
||||||
struct sk_buff_head rx_queue; /* received conn-level packets */
|
struct sk_buff_head rx_queue; /* received conn-level packets */
|
||||||
const struct rxrpc_security *security; /* applied security module */
|
const struct rxrpc_security *security; /* applied security module */
|
||||||
struct key *server_key; /* security for this service */
|
|
||||||
struct crypto_sync_skcipher *cipher; /* encryption handle */
|
struct crypto_sync_skcipher *cipher; /* encryption handle */
|
||||||
struct rxrpc_crypt csum_iv; /* packet checksum base */
|
struct rxrpc_crypt csum_iv; /* packet checksum base */
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
@ -890,8 +889,7 @@ struct rxrpc_connection *rxrpc_find_service_conn_rcu(struct rxrpc_peer *,
|
||||||
struct sk_buff *);
|
struct sk_buff *);
|
||||||
struct rxrpc_connection *rxrpc_prealloc_service_connection(struct rxrpc_net *, gfp_t);
|
struct rxrpc_connection *rxrpc_prealloc_service_connection(struct rxrpc_net *, gfp_t);
|
||||||
void rxrpc_new_incoming_connection(struct rxrpc_sock *, struct rxrpc_connection *,
|
void rxrpc_new_incoming_connection(struct rxrpc_sock *, struct rxrpc_connection *,
|
||||||
const struct rxrpc_security *, struct key *,
|
const struct rxrpc_security *, struct sk_buff *);
|
||||||
struct sk_buff *);
|
|
||||||
void rxrpc_unpublish_service_conn(struct rxrpc_connection *);
|
void rxrpc_unpublish_service_conn(struct rxrpc_connection *);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1056,9 +1054,10 @@ extern const struct rxrpc_security rxkad;
|
||||||
int __init rxrpc_init_security(void);
|
int __init rxrpc_init_security(void);
|
||||||
void rxrpc_exit_security(void);
|
void rxrpc_exit_security(void);
|
||||||
int rxrpc_init_client_conn_security(struct rxrpc_connection *);
|
int rxrpc_init_client_conn_security(struct rxrpc_connection *);
|
||||||
bool rxrpc_look_up_server_security(struct rxrpc_local *, struct rxrpc_sock *,
|
const struct rxrpc_security *rxrpc_get_incoming_security(struct rxrpc_sock *,
|
||||||
const struct rxrpc_security **, struct key **,
|
struct sk_buff *);
|
||||||
struct sk_buff *);
|
struct key *rxrpc_look_up_server_security(struct rxrpc_connection *,
|
||||||
|
struct sk_buff *, u32, u32);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* sendmsg.c
|
* sendmsg.c
|
||||||
|
|
|
@ -261,7 +261,6 @@ static struct rxrpc_call *rxrpc_alloc_incoming_call(struct rxrpc_sock *rx,
|
||||||
struct rxrpc_peer *peer,
|
struct rxrpc_peer *peer,
|
||||||
struct rxrpc_connection *conn,
|
struct rxrpc_connection *conn,
|
||||||
const struct rxrpc_security *sec,
|
const struct rxrpc_security *sec,
|
||||||
struct key *key,
|
|
||||||
struct sk_buff *skb)
|
struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
struct rxrpc_backlog *b = rx->backlog;
|
struct rxrpc_backlog *b = rx->backlog;
|
||||||
|
@ -309,7 +308,7 @@ static struct rxrpc_call *rxrpc_alloc_incoming_call(struct rxrpc_sock *rx,
|
||||||
conn->params.local = rxrpc_get_local(local);
|
conn->params.local = rxrpc_get_local(local);
|
||||||
conn->params.peer = peer;
|
conn->params.peer = peer;
|
||||||
rxrpc_see_connection(conn);
|
rxrpc_see_connection(conn);
|
||||||
rxrpc_new_incoming_connection(rx, conn, sec, key, skb);
|
rxrpc_new_incoming_connection(rx, conn, sec, skb);
|
||||||
} else {
|
} else {
|
||||||
rxrpc_get_connection(conn);
|
rxrpc_get_connection(conn);
|
||||||
}
|
}
|
||||||
|
@ -353,7 +352,6 @@ struct rxrpc_call *rxrpc_new_incoming_call(struct rxrpc_local *local,
|
||||||
struct rxrpc_connection *conn;
|
struct rxrpc_connection *conn;
|
||||||
struct rxrpc_peer *peer = NULL;
|
struct rxrpc_peer *peer = NULL;
|
||||||
struct rxrpc_call *call = NULL;
|
struct rxrpc_call *call = NULL;
|
||||||
struct key *key = NULL;
|
|
||||||
|
|
||||||
_enter("");
|
_enter("");
|
||||||
|
|
||||||
|
@ -374,11 +372,13 @@ struct rxrpc_call *rxrpc_new_incoming_call(struct rxrpc_local *local,
|
||||||
*/
|
*/
|
||||||
conn = rxrpc_find_connection_rcu(local, skb, &peer);
|
conn = rxrpc_find_connection_rcu(local, skb, &peer);
|
||||||
|
|
||||||
if (!conn && !rxrpc_look_up_server_security(local, rx, &sec, &key, skb))
|
if (!conn) {
|
||||||
goto no_call;
|
sec = rxrpc_get_incoming_security(rx, skb);
|
||||||
|
if (!sec)
|
||||||
|
goto no_call;
|
||||||
|
}
|
||||||
|
|
||||||
call = rxrpc_alloc_incoming_call(rx, local, peer, conn, sec, key, skb);
|
call = rxrpc_alloc_incoming_call(rx, local, peer, conn, sec, skb);
|
||||||
key_put(key);
|
|
||||||
if (!call) {
|
if (!call) {
|
||||||
skb->mark = RXRPC_SKB_MARK_REJECT_BUSY;
|
skb->mark = RXRPC_SKB_MARK_REJECT_BUSY;
|
||||||
goto no_call;
|
goto no_call;
|
||||||
|
|
|
@ -378,7 +378,6 @@ static void rxrpc_secure_connection(struct rxrpc_connection *conn)
|
||||||
_enter("{%d}", conn->debug_id);
|
_enter("{%d}", conn->debug_id);
|
||||||
|
|
||||||
ASSERT(conn->security_ix != 0);
|
ASSERT(conn->security_ix != 0);
|
||||||
ASSERT(conn->server_key);
|
|
||||||
|
|
||||||
if (conn->security->issue_challenge(conn) < 0) {
|
if (conn->security->issue_challenge(conn) < 0) {
|
||||||
abort_code = RX_CALL_DEAD;
|
abort_code = RX_CALL_DEAD;
|
||||||
|
|
|
@ -363,7 +363,6 @@ static void rxrpc_destroy_connection(struct rcu_head *rcu)
|
||||||
|
|
||||||
conn->security->clear(conn);
|
conn->security->clear(conn);
|
||||||
key_put(conn->params.key);
|
key_put(conn->params.key);
|
||||||
key_put(conn->server_key);
|
|
||||||
rxrpc_put_bundle(conn->bundle);
|
rxrpc_put_bundle(conn->bundle);
|
||||||
rxrpc_put_peer(conn->params.peer);
|
rxrpc_put_peer(conn->params.peer);
|
||||||
|
|
||||||
|
|
|
@ -156,7 +156,6 @@ struct rxrpc_connection *rxrpc_prealloc_service_connection(struct rxrpc_net *rxn
|
||||||
void rxrpc_new_incoming_connection(struct rxrpc_sock *rx,
|
void rxrpc_new_incoming_connection(struct rxrpc_sock *rx,
|
||||||
struct rxrpc_connection *conn,
|
struct rxrpc_connection *conn,
|
||||||
const struct rxrpc_security *sec,
|
const struct rxrpc_security *sec,
|
||||||
struct key *key,
|
|
||||||
struct sk_buff *skb)
|
struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
|
struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
|
||||||
|
@ -170,7 +169,6 @@ void rxrpc_new_incoming_connection(struct rxrpc_sock *rx,
|
||||||
conn->security_ix = sp->hdr.securityIndex;
|
conn->security_ix = sp->hdr.securityIndex;
|
||||||
conn->out_clientflag = 0;
|
conn->out_clientflag = 0;
|
||||||
conn->security = sec;
|
conn->security = sec;
|
||||||
conn->server_key = key_get(key);
|
|
||||||
if (conn->security_ix)
|
if (conn->security_ix)
|
||||||
conn->state = RXRPC_CONN_SERVICE_UNSECURED;
|
conn->state = RXRPC_CONN_SERVICE_UNSECURED;
|
||||||
else
|
else
|
||||||
|
|
|
@ -647,11 +647,7 @@ static int rxkad_issue_challenge(struct rxrpc_connection *conn)
|
||||||
u32 serial;
|
u32 serial;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
_enter("{%d,%x}", conn->debug_id, key_serial(conn->server_key));
|
_enter("{%d}", conn->debug_id);
|
||||||
|
|
||||||
ret = key_validate(conn->server_key);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
get_random_bytes(&conn->security_nonce, sizeof(conn->security_nonce));
|
get_random_bytes(&conn->security_nonce, sizeof(conn->security_nonce));
|
||||||
|
|
||||||
|
@ -891,6 +887,7 @@ other_error:
|
||||||
* decrypt the kerberos IV ticket in the response
|
* decrypt the kerberos IV ticket in the response
|
||||||
*/
|
*/
|
||||||
static int rxkad_decrypt_ticket(struct rxrpc_connection *conn,
|
static int rxkad_decrypt_ticket(struct rxrpc_connection *conn,
|
||||||
|
struct key *server_key,
|
||||||
struct sk_buff *skb,
|
struct sk_buff *skb,
|
||||||
void *ticket, size_t ticket_len,
|
void *ticket, size_t ticket_len,
|
||||||
struct rxrpc_crypt *_session_key,
|
struct rxrpc_crypt *_session_key,
|
||||||
|
@ -910,30 +907,17 @@ static int rxkad_decrypt_ticket(struct rxrpc_connection *conn,
|
||||||
u32 abort_code;
|
u32 abort_code;
|
||||||
u8 *p, *q, *name, *end;
|
u8 *p, *q, *name, *end;
|
||||||
|
|
||||||
_enter("{%d},{%x}", conn->debug_id, key_serial(conn->server_key));
|
_enter("{%d},{%x}", conn->debug_id, key_serial(server_key));
|
||||||
|
|
||||||
*_expiry = 0;
|
*_expiry = 0;
|
||||||
|
|
||||||
ret = key_validate(conn->server_key);
|
ASSERT(server_key->payload.data[0] != NULL);
|
||||||
if (ret < 0) {
|
|
||||||
switch (ret) {
|
|
||||||
case -EKEYEXPIRED:
|
|
||||||
abort_code = RXKADEXPIRED;
|
|
||||||
goto other_error;
|
|
||||||
default:
|
|
||||||
abort_code = RXKADNOAUTH;
|
|
||||||
goto other_error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ASSERT(conn->server_key->payload.data[0] != NULL);
|
|
||||||
ASSERTCMP((unsigned long) ticket & 7UL, ==, 0);
|
ASSERTCMP((unsigned long) ticket & 7UL, ==, 0);
|
||||||
|
|
||||||
memcpy(&iv, &conn->server_key->payload.data[2], sizeof(iv));
|
memcpy(&iv, &server_key->payload.data[2], sizeof(iv));
|
||||||
|
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
req = skcipher_request_alloc(conn->server_key->payload.data[0],
|
req = skcipher_request_alloc(server_key->payload.data[0], GFP_NOFS);
|
||||||
GFP_NOFS);
|
|
||||||
if (!req)
|
if (!req)
|
||||||
goto temporary_error;
|
goto temporary_error;
|
||||||
|
|
||||||
|
@ -1089,6 +1073,7 @@ static int rxkad_verify_response(struct rxrpc_connection *conn,
|
||||||
struct rxkad_response *response;
|
struct rxkad_response *response;
|
||||||
struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
|
struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
|
||||||
struct rxrpc_crypt session_key;
|
struct rxrpc_crypt session_key;
|
||||||
|
struct key *server_key;
|
||||||
const char *eproto;
|
const char *eproto;
|
||||||
time64_t expiry;
|
time64_t expiry;
|
||||||
void *ticket;
|
void *ticket;
|
||||||
|
@ -1096,7 +1081,27 @@ static int rxkad_verify_response(struct rxrpc_connection *conn,
|
||||||
__be32 csum;
|
__be32 csum;
|
||||||
int ret, i;
|
int ret, i;
|
||||||
|
|
||||||
_enter("{%d,%x}", conn->debug_id, key_serial(conn->server_key));
|
_enter("{%d}", conn->debug_id);
|
||||||
|
|
||||||
|
server_key = rxrpc_look_up_server_security(conn, skb, 0, 0);
|
||||||
|
if (IS_ERR(server_key)) {
|
||||||
|
switch (PTR_ERR(server_key)) {
|
||||||
|
case -ENOKEY:
|
||||||
|
abort_code = RXKADUNKNOWNKEY;
|
||||||
|
break;
|
||||||
|
case -EKEYEXPIRED:
|
||||||
|
abort_code = RXKADEXPIRED;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
abort_code = RXKADNOAUTH;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
trace_rxrpc_abort(0, "SVK",
|
||||||
|
sp->hdr.cid, sp->hdr.callNumber, sp->hdr.seq,
|
||||||
|
abort_code, PTR_ERR(server_key));
|
||||||
|
*_abort_code = abort_code;
|
||||||
|
return -EPROTO;
|
||||||
|
}
|
||||||
|
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
response = kzalloc(sizeof(struct rxkad_response), GFP_NOFS);
|
response = kzalloc(sizeof(struct rxkad_response), GFP_NOFS);
|
||||||
|
@ -1144,8 +1149,8 @@ static int rxkad_verify_response(struct rxrpc_connection *conn,
|
||||||
ticket, ticket_len) < 0)
|
ticket, ticket_len) < 0)
|
||||||
goto protocol_error_free;
|
goto protocol_error_free;
|
||||||
|
|
||||||
ret = rxkad_decrypt_ticket(conn, skb, ticket, ticket_len, &session_key,
|
ret = rxkad_decrypt_ticket(conn, server_key, skb, ticket, ticket_len,
|
||||||
&expiry, _abort_code);
|
&session_key, &expiry, _abort_code);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto temporary_error_free_ticket;
|
goto temporary_error_free_ticket;
|
||||||
|
|
||||||
|
@ -1224,6 +1229,7 @@ protocol_error_free:
|
||||||
protocol_error:
|
protocol_error:
|
||||||
kfree(response);
|
kfree(response);
|
||||||
trace_rxrpc_rx_eproto(NULL, sp->hdr.serial, eproto);
|
trace_rxrpc_rx_eproto(NULL, sp->hdr.serial, eproto);
|
||||||
|
key_put(server_key);
|
||||||
*_abort_code = abort_code;
|
*_abort_code = abort_code;
|
||||||
return -EPROTO;
|
return -EPROTO;
|
||||||
|
|
||||||
|
@ -1236,6 +1242,7 @@ temporary_error:
|
||||||
* ENOMEM. We just want to send the challenge again. Note that we
|
* ENOMEM. We just want to send the challenge again. Note that we
|
||||||
* also come out this way if the ticket decryption fails.
|
* also come out this way if the ticket decryption fails.
|
||||||
*/
|
*/
|
||||||
|
key_put(server_key);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -102,22 +102,16 @@ found:
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Find the security key for a server connection.
|
* Set the ops a server connection.
|
||||||
*/
|
*/
|
||||||
bool rxrpc_look_up_server_security(struct rxrpc_local *local, struct rxrpc_sock *rx,
|
const struct rxrpc_security *rxrpc_get_incoming_security(struct rxrpc_sock *rx,
|
||||||
const struct rxrpc_security **_sec,
|
struct sk_buff *skb)
|
||||||
struct key **_key,
|
|
||||||
struct sk_buff *skb)
|
|
||||||
{
|
{
|
||||||
const struct rxrpc_security *sec;
|
const struct rxrpc_security *sec;
|
||||||
struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
|
struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
|
||||||
key_ref_t kref = NULL;
|
|
||||||
char kdesc[5 + 1 + 3 + 1];
|
|
||||||
|
|
||||||
_enter("");
|
_enter("");
|
||||||
|
|
||||||
sprintf(kdesc, "%u:%u", sp->hdr.serviceId, sp->hdr.securityIndex);
|
|
||||||
|
|
||||||
sec = rxrpc_security_lookup(sp->hdr.securityIndex);
|
sec = rxrpc_security_lookup(sp->hdr.securityIndex);
|
||||||
if (!sec) {
|
if (!sec) {
|
||||||
trace_rxrpc_abort(0, "SVS",
|
trace_rxrpc_abort(0, "SVS",
|
||||||
|
@ -125,35 +119,72 @@ bool rxrpc_look_up_server_security(struct rxrpc_local *local, struct rxrpc_sock
|
||||||
RX_INVALID_OPERATION, EKEYREJECTED);
|
RX_INVALID_OPERATION, EKEYREJECTED);
|
||||||
skb->mark = RXRPC_SKB_MARK_REJECT_ABORT;
|
skb->mark = RXRPC_SKB_MARK_REJECT_ABORT;
|
||||||
skb->priority = RX_INVALID_OPERATION;
|
skb->priority = RX_INVALID_OPERATION;
|
||||||
return false;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sp->hdr.securityIndex == RXRPC_SECURITY_NONE)
|
if (sp->hdr.securityIndex != RXRPC_SECURITY_NONE &&
|
||||||
goto out;
|
!rx->securities) {
|
||||||
|
|
||||||
if (!rx->securities) {
|
|
||||||
trace_rxrpc_abort(0, "SVR",
|
trace_rxrpc_abort(0, "SVR",
|
||||||
sp->hdr.cid, sp->hdr.callNumber, sp->hdr.seq,
|
sp->hdr.cid, sp->hdr.callNumber, sp->hdr.seq,
|
||||||
RX_INVALID_OPERATION, EKEYREJECTED);
|
RX_INVALID_OPERATION, EKEYREJECTED);
|
||||||
skb->mark = RXRPC_SKB_MARK_REJECT_ABORT;
|
skb->mark = RXRPC_SKB_MARK_REJECT_ABORT;
|
||||||
skb->priority = RX_INVALID_OPERATION;
|
skb->priority = sec->no_key_abort;
|
||||||
return false;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return sec;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Find the security key for a server connection.
|
||||||
|
*/
|
||||||
|
struct key *rxrpc_look_up_server_security(struct rxrpc_connection *conn,
|
||||||
|
struct sk_buff *skb,
|
||||||
|
u32 kvno, u32 enctype)
|
||||||
|
{
|
||||||
|
struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
|
||||||
|
struct rxrpc_sock *rx;
|
||||||
|
struct key *key = ERR_PTR(-EKEYREJECTED);
|
||||||
|
key_ref_t kref = NULL;
|
||||||
|
char kdesc[5 + 1 + 3 + 1 + 12 + 1 + 12 + 1];
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
_enter("");
|
||||||
|
|
||||||
|
if (enctype)
|
||||||
|
sprintf(kdesc, "%u:%u:%u:%u",
|
||||||
|
sp->hdr.serviceId, sp->hdr.securityIndex, kvno, enctype);
|
||||||
|
else if (kvno)
|
||||||
|
sprintf(kdesc, "%u:%u:%u",
|
||||||
|
sp->hdr.serviceId, sp->hdr.securityIndex, kvno);
|
||||||
|
else
|
||||||
|
sprintf(kdesc, "%u:%u",
|
||||||
|
sp->hdr.serviceId, sp->hdr.securityIndex);
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
|
||||||
|
rx = rcu_dereference(conn->params.local->service);
|
||||||
|
if (!rx)
|
||||||
|
goto out;
|
||||||
|
|
||||||
/* look through the service's keyring */
|
/* look through the service's keyring */
|
||||||
kref = keyring_search(make_key_ref(rx->securities, 1UL),
|
kref = keyring_search(make_key_ref(rx->securities, 1UL),
|
||||||
&key_type_rxrpc_s, kdesc, true);
|
&key_type_rxrpc_s, kdesc, true);
|
||||||
if (IS_ERR(kref)) {
|
if (IS_ERR(kref)) {
|
||||||
trace_rxrpc_abort(0, "SVK",
|
key = ERR_CAST(kref);
|
||||||
sp->hdr.cid, sp->hdr.callNumber, sp->hdr.seq,
|
goto out;
|
||||||
sec->no_key_abort, EKEYREJECTED);
|
}
|
||||||
skb->mark = RXRPC_SKB_MARK_REJECT_ABORT;
|
|
||||||
skb->priority = sec->no_key_abort;
|
key = key_ref_to_ptr(kref);
|
||||||
return false;
|
|
||||||
|
ret = key_validate(key);
|
||||||
|
if (ret < 0) {
|
||||||
|
key_put(key);
|
||||||
|
key = ERR_PTR(ret);
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
*_sec = sec;
|
rcu_read_unlock();
|
||||||
*_key = key_ref_to_ptr(kref);
|
return key;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче