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 "ar-internal.h" 20 21static LIST_HEAD(rxrpc_security_methods); 22static DECLARE_RWSEM(rxrpc_security_sem); 23 24/* 25 * get an RxRPC security module 26 */ 27static struct rxrpc_security *rxrpc_security_get(struct rxrpc_security *sec) 28{ 29 return try_module_get(sec->owner) ? sec : NULL; 30} 31 32/* 33 * release an RxRPC security module 34 */ 35static void rxrpc_security_put(struct rxrpc_security *sec) 36{ 37 module_put(sec->owner); 38} 39 40/* 41 * look up an rxrpc security module 42 */ 43struct rxrpc_security *rxrpc_security_lookup(u8 security_index) 44{ 45 struct rxrpc_security *sec = NULL; 46 47 _enter(""); 48 49 down_read(&rxrpc_security_sem); 50 51 list_for_each_entry(sec, &rxrpc_security_methods, link) { 52 if (sec->security_index == security_index) { 53 if (unlikely(!rxrpc_security_get(sec))) 54 break; 55 goto out; 56 } 57 } 58 59 sec = NULL; 60out: 61 up_read(&rxrpc_security_sem); 62 _leave(" = %p [%s]", sec, sec ? sec->name : ""); 63 return sec; 64} 65 66/** 67 * rxrpc_register_security - register an RxRPC security handler 68 * @sec: security module 69 * 70 * register an RxRPC security handler for use by RxRPC 71 */ 72int rxrpc_register_security(struct rxrpc_security *sec) 73{ 74 struct rxrpc_security *psec; 75 int ret; 76 77 _enter(""); 78 down_write(&rxrpc_security_sem); 79 80 ret = -EEXIST; 81 list_for_each_entry(psec, &rxrpc_security_methods, link) { 82 if (psec->security_index == sec->security_index) 83 goto out; 84 } 85 86 list_add(&sec->link, &rxrpc_security_methods); 87 88 printk(KERN_NOTICE "RxRPC: Registered security type %d '%s'\n", 89 sec->security_index, sec->name); 90 ret = 0; 91 92out: 93 up_write(&rxrpc_security_sem); 94 _leave(" = %d", ret); 95 return ret; 96} 97 98EXPORT_SYMBOL_GPL(rxrpc_register_security); 99 100/** 101 * rxrpc_unregister_security - unregister an RxRPC security handler 102 * @sec: security module 103 * 104 * unregister an RxRPC security handler 105 */ 106void rxrpc_unregister_security(struct rxrpc_security *sec) 107{ 108 109 _enter(""); 110 down_write(&rxrpc_security_sem); 111 list_del_init(&sec->link); 112 up_write(&rxrpc_security_sem); 113 114 printk(KERN_NOTICE "RxRPC: Unregistered security type %d '%s'\n", 115 sec->security_index, sec->name); 116} 117 118EXPORT_SYMBOL_GPL(rxrpc_unregister_security); 119 120/* 121 * initialise the security on a client connection 122 */ 123int rxrpc_init_client_conn_security(struct rxrpc_connection *conn) 124{ 125 struct rxrpc_security *sec; 126 struct key *key = conn->key; 127 int ret; 128 129 _enter("{%d},{%x}", conn->debug_id, key_serial(key)); 130 131 if (!key) 132 return 0; 133 134 ret = key_validate(key); 135 if (ret < 0) 136 return ret; 137 138 sec = rxrpc_security_lookup(key->type_data.x[0]); 139 if (!sec) 140 return -EKEYREJECTED; 141 conn->security = sec; 142 143 ret = conn->security->init_connection_security(conn); 144 if (ret < 0) { 145 rxrpc_security_put(conn->security); 146 conn->security = NULL; 147 return ret; 148 } 149 150 _leave(" = 0"); 151 return 0; 152} 153 154/* 155 * initialise the security on a server connection 156 */ 157int rxrpc_init_server_conn_security(struct rxrpc_connection *conn) 158{ 159 struct rxrpc_security *sec; 160 struct rxrpc_local *local = conn->trans->local; 161 struct rxrpc_sock *rx; 162 struct key *key; 163 key_ref_t kref; 164 char kdesc[5+1+3+1]; 165 166 _enter(""); 167 168 sprintf(kdesc, "%u:%u", ntohs(conn->service_id), conn->security_ix); 169 170 sec = rxrpc_security_lookup(conn->security_ix); 171 if (!sec) { 172 _leave(" = -ENOKEY [lookup]"); 173 return -ENOKEY; 174 } 175 176 /* find the service */ 177 read_lock_bh(&local->services_lock); 178 list_for_each_entry(rx, &local->services, listen_link) { 179 if (rx->service_id == conn->service_id) 180 goto found_service; 181 } 182 183 /* the service appears to have died */ 184 read_unlock_bh(&local->services_lock); 185 rxrpc_security_put(sec); 186 _leave(" = -ENOENT"); 187 return -ENOENT; 188 189found_service: 190 if (!rx->securities) { 191 read_unlock_bh(&local->services_lock); 192 rxrpc_security_put(sec); 193 _leave(" = -ENOKEY"); 194 return -ENOKEY; 195 } 196 197 /* look through the service's keyring */ 198 kref = keyring_search(make_key_ref(rx->securities, 1UL), 199 &key_type_rxrpc_s, kdesc); 200 if (IS_ERR(kref)) { 201 read_unlock_bh(&local->services_lock); 202 rxrpc_security_put(sec); 203 _leave(" = %ld [search]", PTR_ERR(kref)); 204 return PTR_ERR(kref); 205 } 206 207 key = key_ref_to_ptr(kref); 208 read_unlock_bh(&local->services_lock); 209 210 conn->server_key = key; 211 conn->security = sec; 212 213 _leave(" = 0"); 214 return 0; 215} 216 217/* 218 * secure a packet prior to transmission 219 */ 220int rxrpc_secure_packet(const struct rxrpc_call *call, 221 struct sk_buff *skb, 222 size_t data_size, 223 void *sechdr) 224{ 225 if (call->conn->security) 226 return call->conn->security->secure_packet( 227 call, skb, data_size, sechdr); 228 return 0; 229} 230 231/* 232 * secure a packet prior to transmission 233 */ 234int rxrpc_verify_packet(const struct rxrpc_call *call, struct sk_buff *skb, 235 u32 *_abort_code) 236{ 237 if (call->conn->security) 238 return call->conn->security->verify_packet( 239 call, skb, _abort_code); 240 return 0; 241} 242 243/* 244 * clear connection security 245 */ 246void rxrpc_clear_conn_security(struct rxrpc_connection *conn) 247{ 248 _enter("{%d}", conn->debug_id); 249 250 if (conn->security) { 251 conn->security->clear(conn); 252 rxrpc_security_put(conn->security); 253 conn->security = NULL; 254 } 255 256 key_put(conn->key); 257 key_put(conn->server_key); 258} 259