xref: /openbsd-src/usr.sbin/ospfd/auth.c (revision 4cf3f6fda4106306baf1f5c503e8869d28187047)
1*4cf3f6fdSclaudio /*	$OpenBSD: auth.c,v 1.23 2023/11/07 11:29:05 claudio Exp $ */
2204df0f8Sclaudio 
3204df0f8Sclaudio /*
4367f601bSnorby  * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
5204df0f8Sclaudio  *
6204df0f8Sclaudio  * Permission to use, copy, modify, and distribute this software for any
7204df0f8Sclaudio  * purpose with or without fee is hereby granted, provided that the above
8204df0f8Sclaudio  * copyright notice and this permission notice appear in all copies.
9204df0f8Sclaudio  *
10204df0f8Sclaudio  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11204df0f8Sclaudio  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12204df0f8Sclaudio  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13204df0f8Sclaudio  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14204df0f8Sclaudio  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15204df0f8Sclaudio  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16204df0f8Sclaudio  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17204df0f8Sclaudio  */
18204df0f8Sclaudio 
19204df0f8Sclaudio #include <sys/types.h>
20204df0f8Sclaudio #include <sys/socket.h>
210491ce75Sclaudio #include <limits.h>
2203431b74Snorby #include <md5.h>
2303431b74Snorby #include <stdlib.h>
24204df0f8Sclaudio #include <string.h>
25204df0f8Sclaudio 
26204df0f8Sclaudio #include "ospfd.h"
27204df0f8Sclaudio #include "ospf.h"
28204df0f8Sclaudio #include "log.h"
29204df0f8Sclaudio #include "ospfe.h"
30204df0f8Sclaudio 
3176b51f83Sclaudio struct auth_md *md_list_find(struct auth_md_head *, u_int8_t);
3276b51f83Sclaudio 
33204df0f8Sclaudio int
auth_validate(void * buf,u_int16_t len,struct iface * iface,struct nbr * nbr)3403431b74Snorby auth_validate(void *buf, u_int16_t len, struct iface *iface, struct nbr *nbr)
35204df0f8Sclaudio {
3603431b74Snorby 	MD5_CTX		 hash;
3703431b74Snorby 	u_int8_t	 digest[MD5_DIGEST_LENGTH];
3803431b74Snorby 	u_int8_t	 recv_digest[MD5_DIGEST_LENGTH];
3903431b74Snorby 	struct ospf_hdr	*ospf_hdr = buf;
4003431b74Snorby 	struct auth_md	*md;
4103431b74Snorby 	char		*auth_data;
4203431b74Snorby 
4303431b74Snorby 	if (ntohs(ospf_hdr->auth_type) != (u_int16_t)iface->auth_type) {
44204df0f8Sclaudio 		log_debug("auth_validate: wrong auth type, interface %s",
45204df0f8Sclaudio 		    iface->name);
46204df0f8Sclaudio 		return (-1);
47204df0f8Sclaudio 	}
48204df0f8Sclaudio 
49204df0f8Sclaudio 	switch (iface->auth_type) {
50204df0f8Sclaudio 	case AUTH_SIMPLE:
51171085cbSclaudio 		if (memcmp(ospf_hdr->auth_key.simple, iface->auth_key,
5203431b74Snorby 		    sizeof(ospf_hdr->auth_key.simple))) {
53204df0f8Sclaudio 			log_debug("auth_validate: wrong password, interface %s",
54204df0f8Sclaudio 			    iface->name);
55204df0f8Sclaudio 			return (-1);
56204df0f8Sclaudio 		}
5703431b74Snorby 		/* FALLTHROUGH */
5803431b74Snorby 	case AUTH_NONE:
59204df0f8Sclaudio 		/* clear the key before chksum */
6003431b74Snorby 		bzero(ospf_hdr->auth_key.simple,
6103431b74Snorby 		     sizeof(ospf_hdr->auth_key.simple));
6203431b74Snorby 
6303431b74Snorby 		if (in_cksum(ospf_hdr, ntohs(ospf_hdr->len))) {
64aeb9d7c6Sclaudio 			log_debug("auth_validate: invalid checksum, "
65aeb9d7c6Sclaudio 			    "interface %s", iface->name);
66204df0f8Sclaudio 			return (-1);
6703431b74Snorby 		}
6803431b74Snorby 		break;
6903431b74Snorby 	case AUTH_CRYPT:
702ac453d1Sclaudio 		/*
712ac453d1Sclaudio 		 * We must allow keys that are configured on the interface
722ac453d1Sclaudio 		 * but not necessarily set as the transmit key
732ac453d1Sclaudio 		 * (iface->auth_keyid). This allows for key rotation to new
742ac453d1Sclaudio 		 * keys without taking down the network.
752ac453d1Sclaudio 		 */
7676b51f83Sclaudio 		if ((md = md_list_find(&iface->auth_md_list,
7776b51f83Sclaudio 		    ospf_hdr->auth_key.crypt.keyid)) == NULL) {
782ac453d1Sclaudio 			log_debug("auth_validate: keyid %d not configured, "
792ac453d1Sclaudio 			    "interface %s", ospf_hdr->auth_key.crypt.keyid,
802ac453d1Sclaudio 			    iface->name);
8103431b74Snorby 			return (-1);
8203431b74Snorby 		}
8303431b74Snorby 
8403431b74Snorby 		if (nbr != NULL && ntohl(ospf_hdr->auth_key.crypt.seq_num) <
8503431b74Snorby 		    nbr->crypt_seq_num) {
8603431b74Snorby 			log_debug("auth_validate: decreasing seq num, "
8703431b74Snorby 			    "interface %s", iface->name);
8803431b74Snorby 			return (-1);
8903431b74Snorby 		}
9003431b74Snorby 
9103431b74Snorby 		if (ospf_hdr->auth_key.crypt.len != MD5_DIGEST_LENGTH) {
9203431b74Snorby 			log_debug("auth_validate: invalid key length, "
9303431b74Snorby 			    "interface %s", iface->name);
9403431b74Snorby 			return (-1);
9503431b74Snorby 		}
9603431b74Snorby 
9703431b74Snorby 		if (len - ntohs(ospf_hdr->len) < MD5_DIGEST_LENGTH) {
9803431b74Snorby 			log_debug("auth_validate: invalid key length, "
9903431b74Snorby 			    "interface %s", iface->name);
10003431b74Snorby 			return (-1);
10103431b74Snorby 		}
10203431b74Snorby 
10303431b74Snorby 		auth_data = buf;
10403431b74Snorby 		auth_data += ntohs(ospf_hdr->len);
10503431b74Snorby 
10603431b74Snorby 		/* save the received digest and clear it in the packet */
107171085cbSclaudio 		memcpy(recv_digest, auth_data, sizeof(recv_digest));
10803431b74Snorby 		bzero(auth_data, MD5_DIGEST_LENGTH);
10903431b74Snorby 
11003431b74Snorby 		/* insert plaintext key */
11103431b74Snorby 		bzero(digest, MD5_DIGEST_LENGTH);
11203431b74Snorby 		strncpy(digest, md->key, MD5_DIGEST_LENGTH);
11303431b74Snorby 
11403431b74Snorby 		/* calculate MD5 digest */
11503431b74Snorby 		MD5Init(&hash);
11603431b74Snorby 		MD5Update(&hash, buf, ntohs(ospf_hdr->len));
11703431b74Snorby 		MD5Update(&hash, digest, MD5_DIGEST_LENGTH);
11803431b74Snorby 		MD5Final(digest, &hash);
11903431b74Snorby 
120171085cbSclaudio 		if (memcmp(recv_digest, digest, sizeof(digest))) {
12103431b74Snorby 			log_debug("auth_validate: invalid MD5 digest, "
12203431b74Snorby 			    "interface %s", iface->name);
12303431b74Snorby 			return (-1);
12403431b74Snorby 		}
12503431b74Snorby 
12603431b74Snorby 		if (nbr != NULL)
12703431b74Snorby 			nbr->crypt_seq_num =
12803431b74Snorby 			    ntohl(ospf_hdr->auth_key.crypt.seq_num);
12903431b74Snorby 		break;
13003431b74Snorby 	default:
13103431b74Snorby 		log_debug("auth_validate: unknown auth type, interface %s",
132204df0f8Sclaudio 		    iface->name);
133204df0f8Sclaudio 		return (-1);
134204df0f8Sclaudio 	}
135204df0f8Sclaudio 
136204df0f8Sclaudio 	return (0);
137204df0f8Sclaudio }
138204df0f8Sclaudio 
139204df0f8Sclaudio int
auth_gen(struct ibuf * buf,struct iface * iface)140e39620e5Snicm auth_gen(struct ibuf *buf, struct iface *iface)
141204df0f8Sclaudio {
14203431b74Snorby 	MD5_CTX		 hash;
14303431b74Snorby 	u_int8_t	 digest[MD5_DIGEST_LENGTH];
14443e70bb4Sclaudio 	struct crypt	 crypt;
14503431b74Snorby 	struct auth_md	*md;
14643e70bb4Sclaudio 	u_int16_t	 chksum;
147204df0f8Sclaudio 
14803431b74Snorby 	/* update length */
149e39620e5Snicm 	if (ibuf_size(buf) > USHRT_MAX)
150ae61edb2Sstevesk 		fatalx("auth_gen: resulting ospf packet too big");
15143e70bb4Sclaudio 	if (ibuf_set_n16(buf, offsetof(struct ospf_hdr, len),
15243e70bb4Sclaudio 	    ibuf_size(buf)) == -1)
15343e70bb4Sclaudio 		fatalx("auth_gen: ibuf_set_n16 failed");
154204df0f8Sclaudio 
155204df0f8Sclaudio 	switch (iface->auth_type) {
156204df0f8Sclaudio 	case AUTH_NONE:
157a50d52cbSclaudio 		chksum = in_cksum(ibuf_data(buf), ibuf_size(buf));
15843e70bb4Sclaudio 		if (ibuf_set(buf, offsetof(struct ospf_hdr, chksum),
15943e70bb4Sclaudio 		    &chksum, sizeof(chksum)) == -1)
16043e70bb4Sclaudio 			fatalx("auth_gen: ibuf_set failed");
161204df0f8Sclaudio 		break;
162204df0f8Sclaudio 	case AUTH_SIMPLE:
163a50d52cbSclaudio 		chksum = in_cksum(ibuf_data(buf), ibuf_size(buf));
16443e70bb4Sclaudio 		if (ibuf_set(buf, offsetof(struct ospf_hdr, chksum),
16543e70bb4Sclaudio 		    &chksum, sizeof(chksum)) == -1)
16643e70bb4Sclaudio 			fatalx("auth_gen: ibuf_set failed");
16703431b74Snorby 
16843e70bb4Sclaudio 		if (ibuf_set(buf, offsetof(struct ospf_hdr, auth_key),
169*4cf3f6fdSclaudio 		    iface->auth_key, strnlen(iface->auth_key,
170*4cf3f6fdSclaudio 		    sizeof(iface->auth_key))) == -1)
17143e70bb4Sclaudio 			fatalx("auth_gen: ibuf_set failed");
172204df0f8Sclaudio 		break;
173204df0f8Sclaudio 	case AUTH_CRYPT:
17443e70bb4Sclaudio 		bzero(&crypt, sizeof(crypt));
17543e70bb4Sclaudio 		crypt.keyid = iface->auth_keyid;
17643e70bb4Sclaudio 		crypt.seq_num = htonl(iface->crypt_seq_num);
17743e70bb4Sclaudio 		crypt.len = MD5_DIGEST_LENGTH;
17803431b74Snorby 		iface->crypt_seq_num++;
17903431b74Snorby 
18043e70bb4Sclaudio 		if (ibuf_set(buf, offsetof(struct ospf_hdr, auth_key),
18143e70bb4Sclaudio 		    &crypt, sizeof(crypt)) == -1)
18243e70bb4Sclaudio 			fatalx("auth_gen: ibuf_set failed");
18343e70bb4Sclaudio 
18403431b74Snorby 		/* insert plaintext key */
18576b51f83Sclaudio 		if ((md = md_list_find(&iface->auth_md_list,
18676b51f83Sclaudio 		    iface->auth_keyid)) == NULL) {
187e6a2be05Snorby 			log_debug("auth_gen: keyid %d not configured, "
18803431b74Snorby 			    "interface %s", iface->auth_keyid, iface->name);
189204df0f8Sclaudio 			return (-1);
19003431b74Snorby 		}
19103431b74Snorby 
19203431b74Snorby 		bzero(digest, MD5_DIGEST_LENGTH);
19303431b74Snorby 		strncpy(digest, md->key, MD5_DIGEST_LENGTH);
19403431b74Snorby 
19503431b74Snorby 		/* calculate MD5 digest */
19603431b74Snorby 		MD5Init(&hash);
197a50d52cbSclaudio 		MD5Update(&hash, ibuf_data(buf), ibuf_size(buf));
19803431b74Snorby 		MD5Update(&hash, digest, MD5_DIGEST_LENGTH);
19903431b74Snorby 		MD5Final(digest, &hash);
20003431b74Snorby 
201e39620e5Snicm 		return (ibuf_add(buf, digest, MD5_DIGEST_LENGTH));
202204df0f8Sclaudio 	default:
203204df0f8Sclaudio 		log_debug("auth_gen: unknown auth type, interface %s",
204204df0f8Sclaudio 		    iface->name);
205204df0f8Sclaudio 		return (-1);
206204df0f8Sclaudio 	}
207204df0f8Sclaudio 
208204df0f8Sclaudio 	return (0);
209204df0f8Sclaudio }
21003431b74Snorby 
21103431b74Snorby /* md list */
21203431b74Snorby void
md_list_add(struct auth_md_head * head,u_int8_t keyid,char * key)21376b51f83Sclaudio md_list_add(struct auth_md_head *head, u_int8_t keyid, char *key)
21403431b74Snorby {
21576b51f83Sclaudio 	struct auth_md	*md;
21603431b74Snorby 
21776b51f83Sclaudio 	if ((md = md_list_find(head, keyid)) != NULL) {
21803431b74Snorby 		/* update key */
21903431b74Snorby 		strncpy(md->key, key, sizeof(md->key));
22003431b74Snorby 		return;
22103431b74Snorby 	}
22203431b74Snorby 
22303431b74Snorby 	if ((md = calloc(1, sizeof(struct auth_md))) == NULL)
22403431b74Snorby 		fatalx("md_list_add");
22503431b74Snorby 
22603431b74Snorby 	md->keyid = keyid;
22703431b74Snorby 	strncpy(md->key, key, sizeof(md->key));
22876b51f83Sclaudio 	TAILQ_INSERT_TAIL(head, md, entry);
22903431b74Snorby }
23003431b74Snorby 
23103431b74Snorby void
md_list_copy(struct auth_md_head * to,struct auth_md_head * from)23276b51f83Sclaudio md_list_copy(struct auth_md_head *to, struct auth_md_head *from)
23376b51f83Sclaudio {
23476b51f83Sclaudio 	struct auth_md	*m, *md;
23576b51f83Sclaudio 
23676b51f83Sclaudio 	TAILQ_INIT(to);
23776b51f83Sclaudio 
23876b51f83Sclaudio 	TAILQ_FOREACH(m, from, entry) {
23976b51f83Sclaudio 		if ((md = calloc(1, sizeof(struct auth_md))) == NULL)
240e6a2be05Snorby 			fatalx("md_list_copy");
24176b51f83Sclaudio 
24276b51f83Sclaudio 		md->keyid = m->keyid;
24376b51f83Sclaudio 		strncpy(md->key, m->key, sizeof(md->key));
24476b51f83Sclaudio 		TAILQ_INSERT_TAIL(to, md, entry);
24576b51f83Sclaudio 	}
24676b51f83Sclaudio }
24776b51f83Sclaudio 
24876b51f83Sclaudio void
md_list_clr(struct auth_md_head * head)24976b51f83Sclaudio md_list_clr(struct auth_md_head *head)
25003431b74Snorby {
25103431b74Snorby 	struct auth_md	*m;
25203431b74Snorby 
25376b51f83Sclaudio 	while ((m = TAILQ_FIRST(head)) != NULL) {
25476b51f83Sclaudio 		TAILQ_REMOVE(head, m, entry);
25503431b74Snorby 		free(m);
25603431b74Snorby 	}
25703431b74Snorby }
25803431b74Snorby 
25903431b74Snorby struct auth_md *
md_list_find(struct auth_md_head * head,u_int8_t keyid)26076b51f83Sclaudio md_list_find(struct auth_md_head *head, u_int8_t keyid)
26103431b74Snorby {
26203431b74Snorby 	struct auth_md	*m;
26303431b74Snorby 
26476b51f83Sclaudio 	TAILQ_FOREACH(m, head, entry)
26503431b74Snorby 		if (m->keyid == keyid)
26603431b74Snorby 			return (m);
26703431b74Snorby 
26803431b74Snorby 	return (NULL);
26903431b74Snorby }
27096c7fd34Sclaudio 
27196c7fd34Sclaudio int
md_list_send(struct auth_md_head * head,struct imsgev * to)2727b4c0c10Seric md_list_send(struct auth_md_head *head, struct imsgev *to)
27396c7fd34Sclaudio {
27496c7fd34Sclaudio 	struct auth_md	*m;
27596c7fd34Sclaudio 
27696c7fd34Sclaudio 	TAILQ_FOREACH(m, head, entry)
277d13e54e1Spyr 		if (imsg_compose_event(to, IMSG_RECONF_AUTHMD, 0, 0, -1, m,
27896c7fd34Sclaudio 		    sizeof(*m)) == -1)
27996c7fd34Sclaudio 			return (-1);
28096c7fd34Sclaudio 
28196c7fd34Sclaudio 	return (0);
28296c7fd34Sclaudio }
283