src/usr.sbin/ospfd/auth.c

273 lines
6.9 KiB
C

/* $OpenBSD: auth.c,v 1.20 2015/05/05 01:26:37 jsg Exp $ */
/*
* Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <limits.h>
#include <md5.h>
#include <stdlib.h>
#include <string.h>
#include "ospfd.h"
#include "ospf.h"
#include "log.h"
#include "ospfe.h"
struct auth_md *md_list_find(struct auth_md_head *, u_int8_t);
int
auth_validate(void *buf, u_int16_t len, struct iface *iface, struct nbr *nbr)
{
MD5_CTX hash;
u_int8_t digest[MD5_DIGEST_LENGTH];
u_int8_t recv_digest[MD5_DIGEST_LENGTH];
struct ospf_hdr *ospf_hdr = buf;
struct auth_md *md;
char *auth_data;
if (ntohs(ospf_hdr->auth_type) != (u_int16_t)iface->auth_type) {
log_debug("auth_validate: wrong auth type, interface %s",
iface->name);
return (-1);
}
switch (iface->auth_type) {
case AUTH_SIMPLE:
if (memcmp(ospf_hdr->auth_key.simple, iface->auth_key,
sizeof(ospf_hdr->auth_key.simple))) {
log_debug("auth_validate: wrong password, interface %s",
iface->name);
return (-1);
}
/* FALLTHROUGH */
case AUTH_NONE:
/* clear the key before chksum */
bzero(ospf_hdr->auth_key.simple,
sizeof(ospf_hdr->auth_key.simple));
if (in_cksum(ospf_hdr, ntohs(ospf_hdr->len))) {
log_debug("auth_validate: invalid checksum, "
"interface %s", iface->name);
return (-1);
}
break;
case AUTH_CRYPT:
/*
* We must allow keys that are configured on the interface
* but not necessarily set as the transmit key
* (iface->auth_keyid). This allows for key rotation to new
* keys without taking down the network.
*/
if ((md = md_list_find(&iface->auth_md_list,
ospf_hdr->auth_key.crypt.keyid)) == NULL) {
log_debug("auth_validate: keyid %d not configured, "
"interface %s", ospf_hdr->auth_key.crypt.keyid,
iface->name);
return (-1);
}
if (nbr != NULL && ntohl(ospf_hdr->auth_key.crypt.seq_num) <
nbr->crypt_seq_num) {
log_debug("auth_validate: decreasing seq num, "
"interface %s", iface->name);
return (-1);
}
if (ospf_hdr->auth_key.crypt.len != MD5_DIGEST_LENGTH) {
log_debug("auth_validate: invalid key length, "
"interface %s", iface->name);
return (-1);
}
if (len - ntohs(ospf_hdr->len) < MD5_DIGEST_LENGTH) {
log_debug("auth_validate: invalid key length, "
"interface %s", iface->name);
return (-1);
}
auth_data = buf;
auth_data += ntohs(ospf_hdr->len);
/* save the received digest and clear it in the packet */
memcpy(recv_digest, auth_data, sizeof(recv_digest));
bzero(auth_data, MD5_DIGEST_LENGTH);
/* insert plaintext key */
bzero(digest, MD5_DIGEST_LENGTH);
strncpy(digest, md->key, MD5_DIGEST_LENGTH);
/* calculate MD5 digest */
MD5Init(&hash);
MD5Update(&hash, buf, ntohs(ospf_hdr->len));
MD5Update(&hash, digest, MD5_DIGEST_LENGTH);
MD5Final(digest, &hash);
if (memcmp(recv_digest, digest, sizeof(digest))) {
log_debug("auth_validate: invalid MD5 digest, "
"interface %s", iface->name);
return (-1);
}
if (nbr != NULL)
nbr->crypt_seq_num =
ntohl(ospf_hdr->auth_key.crypt.seq_num);
break;
default:
log_debug("auth_validate: unknown auth type, interface %s",
iface->name);
return (-1);
}
return (0);
}
int
auth_gen(struct ibuf *buf, struct iface *iface)
{
MD5_CTX hash;
u_int8_t digest[MD5_DIGEST_LENGTH];
struct ospf_hdr *ospf_hdr;
struct auth_md *md;
if ((ospf_hdr = ibuf_seek(buf, 0, sizeof(*ospf_hdr))) == NULL)
fatalx("auth_gen: buf_seek failed");
/* update length */
if (ibuf_size(buf) > USHRT_MAX)
fatalx("auth_gen: resulting ospf packet too big");
ospf_hdr->len = htons(ibuf_size(buf));
/* clear auth_key field */
bzero(ospf_hdr->auth_key.simple, sizeof(ospf_hdr->auth_key.simple));
switch (iface->auth_type) {
case AUTH_NONE:
ospf_hdr->chksum = in_cksum(buf->buf, ibuf_size(buf));
break;
case AUTH_SIMPLE:
ospf_hdr->chksum = in_cksum(buf->buf, ibuf_size(buf));
strncpy(ospf_hdr->auth_key.simple, iface->auth_key,
sizeof(ospf_hdr->auth_key.simple));
break;
case AUTH_CRYPT:
ospf_hdr->chksum = 0;
ospf_hdr->auth_key.crypt.keyid = iface->auth_keyid;
ospf_hdr->auth_key.crypt.seq_num = htonl(iface->crypt_seq_num);
ospf_hdr->auth_key.crypt.len = MD5_DIGEST_LENGTH;
iface->crypt_seq_num++;
/* insert plaintext key */
if ((md = md_list_find(&iface->auth_md_list,
iface->auth_keyid)) == NULL) {
log_debug("auth_gen: keyid %d not configured, "
"interface %s", iface->auth_keyid, iface->name);
return (-1);
}
bzero(digest, MD5_DIGEST_LENGTH);
strncpy(digest, md->key, MD5_DIGEST_LENGTH);
/* calculate MD5 digest */
MD5Init(&hash);
MD5Update(&hash, buf->buf, ibuf_size(buf));
MD5Update(&hash, digest, MD5_DIGEST_LENGTH);
MD5Final(digest, &hash);
return (ibuf_add(buf, digest, MD5_DIGEST_LENGTH));
default:
log_debug("auth_gen: unknown auth type, interface %s",
iface->name);
return (-1);
}
return (0);
}
/* md list */
void
md_list_add(struct auth_md_head *head, u_int8_t keyid, char *key)
{
struct auth_md *md;
if ((md = md_list_find(head, keyid)) != NULL) {
/* update key */
strncpy(md->key, key, sizeof(md->key));
return;
}
if ((md = calloc(1, sizeof(struct auth_md))) == NULL)
fatalx("md_list_add");
md->keyid = keyid;
strncpy(md->key, key, sizeof(md->key));
TAILQ_INSERT_TAIL(head, md, entry);
}
void
md_list_copy(struct auth_md_head *to, struct auth_md_head *from)
{
struct auth_md *m, *md;
TAILQ_INIT(to);
TAILQ_FOREACH(m, from, entry) {
if ((md = calloc(1, sizeof(struct auth_md))) == NULL)
fatalx("md_list_copy");
md->keyid = m->keyid;
strncpy(md->key, m->key, sizeof(md->key));
TAILQ_INSERT_TAIL(to, md, entry);
}
}
void
md_list_clr(struct auth_md_head *head)
{
struct auth_md *m;
while ((m = TAILQ_FIRST(head)) != NULL) {
TAILQ_REMOVE(head, m, entry);
free(m);
}
}
struct auth_md *
md_list_find(struct auth_md_head *head, u_int8_t keyid)
{
struct auth_md *m;
TAILQ_FOREACH(m, head, entry)
if (m->keyid == keyid)
return (m);
return (NULL);
}
int
md_list_send(struct auth_md_head *head, struct imsgev *to)
{
struct auth_md *m;
TAILQ_FOREACH(m, head, entry)
if (imsg_compose_event(to, IMSG_RECONF_AUTHMD, 0, 0, -1, m,
sizeof(*m)) == -1)
return (-1);
return (0);
}