File: [local] / src / sbin / photurisd / Attic / spi.c (download)
Revision 1.8, Sun Jan 28 22:45:17 2001 UTC (23 years, 4 months ago) by niklas
Branch: MAIN
CVS Tags: OPENBSD_3_1_BASE, OPENBSD_3_1, OPENBSD_3_0_BASE, OPENBSD_3_0, OPENBSD_2_9_BASE, OPENBSD_2_9 Changes since 1.7: +3 -1 lines
$OpenBSD$
|
/* $OpenBSD: spi.c,v 1.8 2001/01/28 22:45:17 niklas Exp $ */
/*
* Copyright 1997-2000 Niels Provos <provos@citi.umich.edu>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Niels Provos.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* spi.c:
* SPI handling functions
*/
#ifndef lint
static char rcsid[] = "$OpenBSD: spi.c,v 1.8 2001/01/28 22:45:17 niklas Exp $";
#endif
#define _SPI_C_
#include <sys/types.h>
#include <sys/queue.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "config.h"
#include "photuris.h"
#include "state.h"
#include "attributes.h"
#include "buffer.h"
#include "spi.h"
#include "secrets.h"
#include "schedule.h"
#include "log.h"
#ifdef IPSEC
#include "kernel.h"
#endif
TAILQ_HEAD(spilist, spiob) spihead;
void
spi_init(void)
{
TAILQ_INIT(&spihead);
}
time_t
getspilifetime(struct stateob *st)
{
/* XXX - destination depend lifetimes */
return (st->spi_lifetime);
}
int
make_spi(struct stateob *st, char *local_address,
u_int8_t *SPI, time_t *lifetime,
u_int8_t **attributes, u_int16_t *attribsize)
{
u_int32_t tmp = 0;
int i, flags = 0;
if(*attributes == NULL) { /* We are in need of attributes */
if (select_attrib(st, attributes, attribsize) == -1) {
log_print("select_attrib() in make_spi()");
return (-1);
}
}
#ifdef IPSEC
/* Let the kernel reserve a SPI for us */
for (i=0; i<*attribsize; i += (*attributes)[i+1]+2)
if ((*attributes)[i] == AT_ESP_ATTRIB)
flags |= IPSEC_OPT_ENC;
else if ((*attributes)[i] == AT_AH_ATTRIB)
flags |= IPSEC_OPT_AUTH;
tmp = kernel_reserve_spi(local_address, st->address, flags);
#else
/* Just grab a random number, this should be uniq */
tmp = arc4random();
#endif
for (i = SPI_SIZE - 1; i >= 0; i--) {
SPI[i] = tmp & 0xFF;
tmp = tmp >> 8;
}
*lifetime = getspilifetime(st) + (arc4random() & 0x1F);
return (0);
}
int
spi_insert(struct spiob *ob)
{
TAILQ_INSERT_TAIL(&spihead, ob, next);
return (1);
}
int
spi_unlink(struct spiob *ob)
{
LOG_DBG((LOG_SPI, 45, __FUNCTION__": unlinking %s spi %x",
ob->flags & SPI_OWNER ? "Owner" : "User",
ntohl(*(u_int32_t *)ob->SPI)));
TAILQ_REMOVE(&spihead, ob, next);
free(ob);
return (1);
}
struct spiob *
spi_new(char *address, u_int8_t *spi)
{
struct spiob *p;
if (spi_find(address, spi) != NULL)
return (NULL);
if ((p = calloc(1, sizeof(struct spiob))) == NULL)
return (NULL);
if ((p->address = strdup(address)) == NULL) {
free(p);
return (NULL);
}
bcopy(spi, p->SPI, SPI_SIZE);
return (p);
}
int
spi_value_reset(struct spiob *ob)
{
if (ob->address != NULL) {
free(ob->address);
ob->address = NULL;
}
if (ob->local_address != NULL) {
free(ob->local_address);
ob->local_address = NULL;
}
if (ob->attributes != NULL) {
free(ob->attributes);
ob->attributes = NULL;
}
if (ob->sessionkey != NULL) {
memset(ob->sessionkey, 0, ob->sessionkeysize);
free(ob->sessionkey);
ob->sessionkey = NULL;
}
return (1);
}
struct spiob *
spi_find_attrib(char *address, u_int8_t *attrib, u_int16_t attribsize)
{
struct spiob *tmp;
u_int16_t i;
for (tmp = TAILQ_FIRST(&spihead); tmp; tmp = TAILQ_NEXT(tmp, next)) {
if (!strcmp(address, tmp->address)) {
for (i = 0; i < attribsize; i += attrib[i + 1] + 2) {
if (attrib[i] == AT_AH_ATTRIB ||
attrib[i] == AT_ESP_ATTRIB)
continue;
if (!isinattrib(tmp->attributes, tmp->attribsize, attrib[i]))
break;
}
if (i == attribsize)
return (tmp);
}
}
return (NULL);
}
/*
* find the spi ob with matching address
* Alas this is tweaked, for SPI_OWNER compare with local_address
* and for user compare with address.
*/
struct spiob *
spi_find(char *address, u_int8_t *spi)
{
struct spiob *tmp;
for (tmp = TAILQ_FIRST(&spihead); tmp; tmp = TAILQ_NEXT(tmp, next)) {
if (bcmp(spi, tmp->SPI, SPI_SIZE))
continue;
if (address == NULL)
break;
if (tmp->flags & SPI_OWNER ?
!strcmp(address, tmp->local_address) :
!strcmp(address, tmp->address))
break;
}
return (tmp);
}
void
spi_expire(void)
{
struct spiob *tmp, *next;
time_t tm;
tm = time(NULL);
for (tmp = TAILQ_FIRST(&spihead); tmp; tmp = next) {
next = TAILQ_NEXT(tmp, next);
if (tmp->lifetime == -1 || tmp->lifetime > tm)
continue;
LOG_DBG((LOG_SPI, 30, __FUNCTION__
": expiring %s spi %x to %s",
tmp->flags & SPI_OWNER ? "Owner" : "User",
ntohl(*(u_int32_t *)tmp->SPI), tmp->address));
#ifdef IPSEC
kernel_unlink_spi(tmp);
#endif
spi_value_reset(tmp);
spi_unlink(tmp);
}
}
void
spi_update_insert(struct spiob *spi)
{
time_t tm = time(NULL);
int seconds;
seconds = spi->lifetime - tm;
if (seconds < 0)
seconds = 0;
seconds = seconds * 9 / 10;
schedule_insert(UPDATE, seconds, spi->SPI, SPI_SIZE);
}
void
spi_update(int sock, u_int8_t *spinr)
{
struct stateob *st;
struct spiob *spi, *nspi;
struct sockaddr_in sin;
/* We are to create a new SPI */
if ((spi = spi_find(NULL, spinr)) == NULL) {
log_print("spi_find() in schedule_process()");
return;
}
if (!(spi->flags & SPI_OWNER))
return;
if (spi->flags & SPI_UPDATED) {
LOG_DBG((LOG_SPI, 55, __FUNCTION__": SPI %x already updated",
ntohl(*(u_int32_t *)spinr)));
return;
}
LOG_DBG((LOG_SPI, 45, __FUNCTION__": updating SPI %x",
ntohl(*(u_int32_t *)spinr)));
if ((st = state_find_cookies(spi->address, spi->icookie, NULL)) == NULL) {
/*
* This happens always when an exchange expires but
* updates are still scheduled for it.
*/
LOG_DBG((LOG_SPI, 65, __FUNCTION__": state_find_cookies()"));
return;
}
if (st->oSPIattrib != NULL)
free(st->oSPIattrib);
if ((st->oSPIattrib = calloc(spi->attribsize, sizeof(u_int8_t))) == NULL) {
log_error("calloc() in schedule_process()");
return;
}
st->oSPIattribsize = spi->attribsize;
bcopy(spi->attributes, st->oSPIattrib, st->oSPIattribsize);
/* We can keep our old attributes, this is only an update */
if (make_spi(st, spi->local_address, st->oSPI, &(st->olifetime),
&(st->oSPIattrib), &(st->oSPIattribsize)) == -1) {
log_print(__FUNCTION__": make_spi()");
return;
}
packet_size = PACKET_BUFFER_SIZE;
if (photuris_spi_update(st, packet_buffer, &packet_size) == -1) {
log_print(__FUNCTION__": photuris_spi_update()");
return;
}
/* Send the packet */
sin.sin_port = htons(st->port);
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = inet_addr(st->address);
if (sendto(sock, packet_buffer, packet_size, 0,
(struct sockaddr *) &sin, sizeof(sin)) != packet_size) {
log_error("sendto() in schedule_process()");
return;
}
#ifdef DEBUG
printf("Sending SPI UPDATE to %s.\n", st->address);
#endif
/* Insert Owner SPI */
if ((nspi = spi_new(st->address, st->oSPI)) == NULL) {
log_error("spi_new() in handle_spi_needed()");
return;
}
if ((nspi->local_address = strdup(spi->local_address)) == NULL) {
log_error("strdup() in handle_spi_needed()");
spi_value_reset(nspi);
return;
}
bcopy(st->icookie, nspi->icookie, COOKIE_SIZE);
nspi->flags |= SPI_OWNER;
nspi->attribsize = st->oSPIattribsize;
nspi->attributes = calloc(nspi->attribsize, sizeof(u_int8_t));
if (nspi->attributes == NULL) {
log_error("calloc() in handle_spi_needed()");
spi_value_reset(nspi);
return;
}
bcopy(st->oSPIattrib, nspi->attributes, nspi->attribsize);
nspi->lifetime = time(NULL) + st->olifetime;
make_session_keys(st, nspi);
spi_insert(nspi);
spi_update_insert(nspi);
#ifdef IPSEC
kernel_insert_spi(st, nspi);
#endif
/* Our old SPI has been updated, dont update it again */
spi->flags |= SPI_UPDATED;
}