xref: /linux/net/rxrpc/security.c (revision b6ebbac51bedf9e98e837688bc838f400196da5e)
1 /* RxRPC security handling
2  *
3  * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
4  * Written by David Howells (dhowells@redhat.com)
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version
9  * 2 of the License, or (at your option) any later version.
10  */
11 
12 #include <linux/module.h>
13 #include <linux/net.h>
14 #include <linux/skbuff.h>
15 #include <linux/udp.h>
16 #include <linux/crypto.h>
17 #include <net/sock.h>
18 #include <net/af_rxrpc.h>
19 #include <keys/rxrpc-type.h>
20 #include "ar-internal.h"
21 
22 static LIST_HEAD(rxrpc_security_methods);
23 static DECLARE_RWSEM(rxrpc_security_sem);
24 
25 static const struct rxrpc_security *rxrpc_security_types[] = {
26 	[RXRPC_SECURITY_NONE]	= &rxrpc_no_security,
27 #ifdef CONFIG_RXKAD
28 	[RXRPC_SECURITY_RXKAD]	= &rxkad,
29 #endif
30 };
31 
32 int __init rxrpc_init_security(void)
33 {
34 	int i, ret;
35 
36 	for (i = 0; i < ARRAY_SIZE(rxrpc_security_types); i++) {
37 		if (rxrpc_security_types[i]) {
38 			ret = rxrpc_security_types[i]->init();
39 			if (ret < 0)
40 				goto failed;
41 		}
42 	}
43 
44 	return 0;
45 
46 failed:
47 	for (i--; i >= 0; i--)
48 		if (rxrpc_security_types[i])
49 			rxrpc_security_types[i]->exit();
50 	return ret;
51 }
52 
53 void rxrpc_exit_security(void)
54 {
55 	int i;
56 
57 	for (i = 0; i < ARRAY_SIZE(rxrpc_security_types); i++)
58 		if (rxrpc_security_types[i])
59 			rxrpc_security_types[i]->exit();
60 }
61 
62 /*
63  * look up an rxrpc security module
64  */
65 static const struct rxrpc_security *rxrpc_security_lookup(u8 security_index)
66 {
67 	if (security_index >= ARRAY_SIZE(rxrpc_security_types))
68 		return NULL;
69 	return rxrpc_security_types[security_index];
70 }
71 
72 /*
73  * initialise the security on a client connection
74  */
75 int rxrpc_init_client_conn_security(struct rxrpc_connection *conn)
76 {
77 	const struct rxrpc_security *sec;
78 	struct rxrpc_key_token *token;
79 	struct key *key = conn->params.key;
80 	int ret;
81 
82 	_enter("{%d},{%x}", conn->debug_id, key_serial(key));
83 
84 	if (!key)
85 		return 0;
86 
87 	ret = key_validate(key);
88 	if (ret < 0)
89 		return ret;
90 
91 	token = key->payload.data[0];
92 	if (!token)
93 		return -EKEYREJECTED;
94 
95 	sec = rxrpc_security_lookup(token->security_index);
96 	if (!sec)
97 		return -EKEYREJECTED;
98 	conn->security = sec;
99 
100 	ret = conn->security->init_connection_security(conn);
101 	if (ret < 0) {
102 		conn->security = &rxrpc_no_security;
103 		return ret;
104 	}
105 
106 	_leave(" = 0");
107 	return 0;
108 }
109 
110 /*
111  * initialise the security on a server connection
112  */
113 int rxrpc_init_server_conn_security(struct rxrpc_connection *conn)
114 {
115 	const struct rxrpc_security *sec;
116 	struct rxrpc_local *local = conn->params.local;
117 	struct rxrpc_sock *rx;
118 	struct key *key;
119 	key_ref_t kref;
120 	char kdesc[5 + 1 + 3 + 1];
121 
122 	_enter("");
123 
124 	sprintf(kdesc, "%u:%u", conn->params.service_id, conn->security_ix);
125 
126 	sec = rxrpc_security_lookup(conn->security_ix);
127 	if (!sec) {
128 		_leave(" = -ENOKEY [lookup]");
129 		return -ENOKEY;
130 	}
131 
132 	/* find the service */
133 	read_lock_bh(&local->services_lock);
134 	list_for_each_entry(rx, &local->services, listen_link) {
135 		if (rx->srx.srx_service == conn->params.service_id)
136 			goto found_service;
137 	}
138 
139 	/* the service appears to have died */
140 	read_unlock_bh(&local->services_lock);
141 	_leave(" = -ENOENT");
142 	return -ENOENT;
143 
144 found_service:
145 	if (!rx->securities) {
146 		read_unlock_bh(&local->services_lock);
147 		_leave(" = -ENOKEY");
148 		return -ENOKEY;
149 	}
150 
151 	/* look through the service's keyring */
152 	kref = keyring_search(make_key_ref(rx->securities, 1UL),
153 			      &key_type_rxrpc_s, kdesc);
154 	if (IS_ERR(kref)) {
155 		read_unlock_bh(&local->services_lock);
156 		_leave(" = %ld [search]", PTR_ERR(kref));
157 		return PTR_ERR(kref);
158 	}
159 
160 	key = key_ref_to_ptr(kref);
161 	read_unlock_bh(&local->services_lock);
162 
163 	conn->server_key = key;
164 	conn->security = sec;
165 
166 	_leave(" = 0");
167 	return 0;
168 }
169