1/*	$OpenBSD: auth.c,v 1.23 2023/11/07 11:29:05 claudio Exp $ */
2
3/*
4 * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/types.h>
20#include <sys/socket.h>
21#include <limits.h>
22#include <md5.h>
23#include <stdlib.h>
24#include <string.h>
25
26#include "ospfd.h"
27#include "ospf.h"
28#include "log.h"
29#include "ospfe.h"
30
31struct auth_md *md_list_find(struct auth_md_head *, u_int8_t);
32
33int
34auth_validate(void *buf, u_int16_t len, struct iface *iface, struct nbr *nbr)
35{
36	MD5_CTX		 hash;
37	u_int8_t	 digest[MD5_DIGEST_LENGTH];
38	u_int8_t	 recv_digest[MD5_DIGEST_LENGTH];
39	struct ospf_hdr	*ospf_hdr = buf;
40	struct auth_md	*md;
41	char		*auth_data;
42
43	if (ntohs(ospf_hdr->auth_type) != (u_int16_t)iface->auth_type) {
44		log_debug("auth_validate: wrong auth type, interface %s",
45		    iface->name);
46		return (-1);
47	}
48
49	switch (iface->auth_type) {
50	case AUTH_SIMPLE:
51		if (memcmp(ospf_hdr->auth_key.simple, iface->auth_key,
52		    sizeof(ospf_hdr->auth_key.simple))) {
53			log_debug("auth_validate: wrong password, interface %s",
54			    iface->name);
55			return (-1);
56		}
57		/* FALLTHROUGH */
58	case AUTH_NONE:
59		/* clear the key before chksum */
60		bzero(ospf_hdr->auth_key.simple,
61		     sizeof(ospf_hdr->auth_key.simple));
62
63		if (in_cksum(ospf_hdr, ntohs(ospf_hdr->len))) {
64			log_debug("auth_validate: invalid checksum, "
65			    "interface %s", iface->name);
66			return (-1);
67		}
68		break;
69	case AUTH_CRYPT:
70		/*
71		 * We must allow keys that are configured on the interface
72		 * but not necessarily set as the transmit key
73		 * (iface->auth_keyid). This allows for key rotation to new
74		 * keys without taking down the network.
75		 */
76		if ((md = md_list_find(&iface->auth_md_list,
77		    ospf_hdr->auth_key.crypt.keyid)) == NULL) {
78			log_debug("auth_validate: keyid %d not configured, "
79			    "interface %s", ospf_hdr->auth_key.crypt.keyid,
80			    iface->name);
81			return (-1);
82		}
83
84		if (nbr != NULL && ntohl(ospf_hdr->auth_key.crypt.seq_num) <
85		    nbr->crypt_seq_num) {
86			log_debug("auth_validate: decreasing seq num, "
87			    "interface %s", iface->name);
88			return (-1);
89		}
90
91		if (ospf_hdr->auth_key.crypt.len != MD5_DIGEST_LENGTH) {
92			log_debug("auth_validate: invalid key length, "
93			    "interface %s", iface->name);
94			return (-1);
95		}
96
97		if (len - ntohs(ospf_hdr->len) < MD5_DIGEST_LENGTH) {
98			log_debug("auth_validate: invalid key length, "
99			    "interface %s", iface->name);
100			return (-1);
101		}
102
103		auth_data = buf;
104		auth_data += ntohs(ospf_hdr->len);
105
106		/* save the received digest and clear it in the packet */
107		memcpy(recv_digest, auth_data, sizeof(recv_digest));
108		bzero(auth_data, MD5_DIGEST_LENGTH);
109
110		/* insert plaintext key */
111		bzero(digest, MD5_DIGEST_LENGTH);
112		strncpy(digest, md->key, MD5_DIGEST_LENGTH);
113
114		/* calculate MD5 digest */
115		MD5Init(&hash);
116		MD5Update(&hash, buf, ntohs(ospf_hdr->len));
117		MD5Update(&hash, digest, MD5_DIGEST_LENGTH);
118		MD5Final(digest, &hash);
119
120		if (memcmp(recv_digest, digest, sizeof(digest))) {
121			log_debug("auth_validate: invalid MD5 digest, "
122			    "interface %s", iface->name);
123			return (-1);
124		}
125
126		if (nbr != NULL)
127			nbr->crypt_seq_num =
128			    ntohl(ospf_hdr->auth_key.crypt.seq_num);
129		break;
130	default:
131		log_debug("auth_validate: unknown auth type, interface %s",
132		    iface->name);
133		return (-1);
134	}
135
136	return (0);
137}
138
139int
140auth_gen(struct ibuf *buf, struct iface *iface)
141{
142	MD5_CTX		 hash;
143	u_int8_t	 digest[MD5_DIGEST_LENGTH];
144	struct crypt	 crypt;
145	struct auth_md	*md;
146	u_int16_t	 chksum;
147
148	/* update length */
149	if (ibuf_size(buf) > USHRT_MAX)
150		fatalx("auth_gen: resulting ospf packet too big");
151	if (ibuf_set_n16(buf, offsetof(struct ospf_hdr, len),
152	    ibuf_size(buf)) == -1)
153		fatalx("auth_gen: ibuf_set_n16 failed");
154
155	switch (iface->auth_type) {
156	case AUTH_NONE:
157		chksum = in_cksum(ibuf_data(buf), ibuf_size(buf));
158		if (ibuf_set(buf, offsetof(struct ospf_hdr, chksum),
159		    &chksum, sizeof(chksum)) == -1)
160			fatalx("auth_gen: ibuf_set failed");
161		break;
162	case AUTH_SIMPLE:
163		chksum = in_cksum(ibuf_data(buf), ibuf_size(buf));
164		if (ibuf_set(buf, offsetof(struct ospf_hdr, chksum),
165		    &chksum, sizeof(chksum)) == -1)
166			fatalx("auth_gen: ibuf_set failed");
167
168		if (ibuf_set(buf, offsetof(struct ospf_hdr, auth_key),
169		    iface->auth_key, strnlen(iface->auth_key,
170		    sizeof(iface->auth_key))) == -1)
171			fatalx("auth_gen: ibuf_set failed");
172		break;
173	case AUTH_CRYPT:
174		bzero(&crypt, sizeof(crypt));
175		crypt.keyid = iface->auth_keyid;
176		crypt.seq_num = htonl(iface->crypt_seq_num);
177		crypt.len = MD5_DIGEST_LENGTH;
178		iface->crypt_seq_num++;
179
180		if (ibuf_set(buf, offsetof(struct ospf_hdr, auth_key),
181		    &crypt, sizeof(crypt)) == -1)
182			fatalx("auth_gen: ibuf_set failed");
183
184		/* insert plaintext key */
185		if ((md = md_list_find(&iface->auth_md_list,
186		    iface->auth_keyid)) == NULL) {
187			log_debug("auth_gen: keyid %d not configured, "
188			    "interface %s", iface->auth_keyid, iface->name);
189			return (-1);
190		}
191
192		bzero(digest, MD5_DIGEST_LENGTH);
193		strncpy(digest, md->key, MD5_DIGEST_LENGTH);
194
195		/* calculate MD5 digest */
196		MD5Init(&hash);
197		MD5Update(&hash, ibuf_data(buf), ibuf_size(buf));
198		MD5Update(&hash, digest, MD5_DIGEST_LENGTH);
199		MD5Final(digest, &hash);
200
201		return (ibuf_add(buf, digest, MD5_DIGEST_LENGTH));
202	default:
203		log_debug("auth_gen: unknown auth type, interface %s",
204		    iface->name);
205		return (-1);
206	}
207
208	return (0);
209}
210
211/* md list */
212void
213md_list_add(struct auth_md_head *head, u_int8_t keyid, char *key)
214{
215	struct auth_md	*md;
216
217	if ((md = md_list_find(head, keyid)) != NULL) {
218		/* update key */
219		strncpy(md->key, key, sizeof(md->key));
220		return;
221	}
222
223	if ((md = calloc(1, sizeof(struct auth_md))) == NULL)
224		fatalx("md_list_add");
225
226	md->keyid = keyid;
227	strncpy(md->key, key, sizeof(md->key));
228	TAILQ_INSERT_TAIL(head, md, entry);
229}
230
231void
232md_list_copy(struct auth_md_head *to, struct auth_md_head *from)
233{
234	struct auth_md	*m, *md;
235
236	TAILQ_INIT(to);
237
238	TAILQ_FOREACH(m, from, entry) {
239		if ((md = calloc(1, sizeof(struct auth_md))) == NULL)
240			fatalx("md_list_copy");
241
242		md->keyid = m->keyid;
243		strncpy(md->key, m->key, sizeof(md->key));
244		TAILQ_INSERT_TAIL(to, md, entry);
245	}
246}
247
248void
249md_list_clr(struct auth_md_head *head)
250{
251	struct auth_md	*m;
252
253	while ((m = TAILQ_FIRST(head)) != NULL) {
254		TAILQ_REMOVE(head, m, entry);
255		free(m);
256	}
257}
258
259struct auth_md *
260md_list_find(struct auth_md_head *head, u_int8_t keyid)
261{
262	struct auth_md	*m;
263
264	TAILQ_FOREACH(m, head, entry)
265		if (m->keyid == keyid)
266			return (m);
267
268	return (NULL);
269}
270
271int
272md_list_send(struct auth_md_head *head, struct imsgev *to)
273{
274	struct auth_md	*m;
275
276	TAILQ_FOREACH(m, head, entry)
277		if (imsg_compose_event(to, IMSG_RECONF_AUTHMD, 0, 0, -1, m,
278		    sizeof(*m)) == -1)
279			return (-1);
280
281	return (0);
282}
283