security.c 3.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/* RxRPC security handling
 *
 * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
 * Written by David Howells (dhowells@redhat.com)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version
 * 2 of the License, or (at your option) any later version.
 */

#include <linux/module.h>
#include <linux/net.h>
#include <linux/skbuff.h>
#include <linux/udp.h>
#include <linux/crypto.h>
#include <net/sock.h>
#include <net/af_rxrpc.h>
19
#include <keys/rxrpc-type.h>
20 21 22 23 24
#include "ar-internal.h"

static LIST_HEAD(rxrpc_security_methods);
static DECLARE_RWSEM(rxrpc_security_sem);

25
static const struct rxrpc_security *rxrpc_security_types[] = {
26
	[RXRPC_SECURITY_NONE]	= &rxrpc_no_security,
27 28 29 30
#ifdef CONFIG_RXKAD
	[RXRPC_SECURITY_RXKAD]	= &rxkad,
#endif
};
31

32
int __init rxrpc_init_security(void)
33
{
34
	int i, ret;
35

36 37 38 39 40
	for (i = 0; i < ARRAY_SIZE(rxrpc_security_types); i++) {
		if (rxrpc_security_types[i]) {
			ret = rxrpc_security_types[i]->init();
			if (ret < 0)
				goto failed;
41 42 43
		}
	}

44 45 46 47 48 49 50
	return 0;

failed:
	for (i--; i >= 0; i--)
		if (rxrpc_security_types[i])
			rxrpc_security_types[i]->exit();
	return ret;
51 52
}

53
void rxrpc_exit_security(void)
54
{
55
	int i;
56

57 58 59
	for (i = 0; i < ARRAY_SIZE(rxrpc_security_types); i++)
		if (rxrpc_security_types[i])
			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 68 69
	if (security_index >= ARRAY_SIZE(rxrpc_security_types))
		return NULL;
	return rxrpc_security_types[security_index];
70 71 72 73 74 75 76
}

/*
 * initialise the security on a client connection
 */
int rxrpc_init_client_conn_security(struct rxrpc_connection *conn)
{
77
	const struct rxrpc_security *sec;
78
	struct rxrpc_key_token *token;
79
	struct key *key = conn->params.key;
80 81 82 83 84 85 86 87 88 89 90
	int ret;

	_enter("{%d},{%x}", conn->debug_id, key_serial(key));

	if (!key)
		return 0;

	ret = key_validate(key);
	if (ret < 0)
		return ret;

91 92
	token = key->payload.data[0];
	if (!token)
93 94 95
		return -EKEYREJECTED;

	sec = rxrpc_security_lookup(token->security_index);
96 97 98 99 100 101
	if (!sec)
		return -EKEYREJECTED;
	conn->security = sec;

	ret = conn->security->init_connection_security(conn);
	if (ret < 0) {
102
		conn->security = &rxrpc_no_security;
103 104 105 106 107 108 109 110 111 112 113 114
		return ret;
	}

	_leave(" = 0");
	return 0;
}

/*
 * initialise the security on a server connection
 */
int rxrpc_init_server_conn_security(struct rxrpc_connection *conn)
{
115
	const struct rxrpc_security *sec;
116
	struct rxrpc_local *local = conn->params.local;
117 118 119
	struct rxrpc_sock *rx;
	struct key *key;
	key_ref_t kref;
120
	char kdesc[5 + 1 + 3 + 1];
121 122 123

	_enter("");

124
	sprintf(kdesc, "%u:%u", conn->params.service_id, conn->security_ix);
125 126 127 128 129 130 131 132 133 134

	sec = rxrpc_security_lookup(conn->security_ix);
	if (!sec) {
		_leave(" = -ENOKEY [lookup]");
		return -ENOKEY;
	}

	/* find the service */
	read_lock_bh(&local->services_lock);
	list_for_each_entry(rx, &local->services, listen_link) {
135
		if (rx->srx.srx_service == conn->params.service_id)
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
			goto found_service;
	}

	/* the service appears to have died */
	read_unlock_bh(&local->services_lock);
	_leave(" = -ENOENT");
	return -ENOENT;

found_service:
	if (!rx->securities) {
		read_unlock_bh(&local->services_lock);
		_leave(" = -ENOKEY");
		return -ENOKEY;
	}

	/* look through the service's keyring */
	kref = keyring_search(make_key_ref(rx->securities, 1UL),
			      &key_type_rxrpc_s, kdesc);
	if (IS_ERR(kref)) {
		read_unlock_bh(&local->services_lock);
		_leave(" = %ld [search]", PTR_ERR(kref));
		return PTR_ERR(kref);
	}

	key = key_ref_to_ptr(kref);
	read_unlock_bh(&local->services_lock);

	conn->server_key = key;
	conn->security = sec;

	_leave(" = 0");
	return 0;
}