xref: /openbsd/usr.sbin/ospfd/auth.c (revision a50d52cb)
1 /*	$OpenBSD: auth.c,v 1.22 2023/07/03 09:40:47 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 
31 struct auth_md *md_list_find(struct auth_md_head *, u_int8_t);
32 
33 int
34 auth_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 
139 int
140 auth_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, strlen(iface->auth_key)) == -1)
170 			fatalx("auth_gen: ibuf_set failed");
171 		break;
172 	case AUTH_CRYPT:
173 		bzero(&crypt, sizeof(crypt));
174 		crypt.keyid = iface->auth_keyid;
175 		crypt.seq_num = htonl(iface->crypt_seq_num);
176 		crypt.len = MD5_DIGEST_LENGTH;
177 		iface->crypt_seq_num++;
178 
179 		if (ibuf_set(buf, offsetof(struct ospf_hdr, auth_key),
180 		    &crypt, sizeof(crypt)) == -1)
181 			fatalx("auth_gen: ibuf_set failed");
182 
183 		/* insert plaintext key */
184 		if ((md = md_list_find(&iface->auth_md_list,
185 		    iface->auth_keyid)) == NULL) {
186 			log_debug("auth_gen: keyid %d not configured, "
187 			    "interface %s", iface->auth_keyid, iface->name);
188 			return (-1);
189 		}
190 
191 		bzero(digest, MD5_DIGEST_LENGTH);
192 		strncpy(digest, md->key, MD5_DIGEST_LENGTH);
193 
194 		/* calculate MD5 digest */
195 		MD5Init(&hash);
196 		MD5Update(&hash, ibuf_data(buf), ibuf_size(buf));
197 		MD5Update(&hash, digest, MD5_DIGEST_LENGTH);
198 		MD5Final(digest, &hash);
199 
200 		return (ibuf_add(buf, digest, MD5_DIGEST_LENGTH));
201 	default:
202 		log_debug("auth_gen: unknown auth type, interface %s",
203 		    iface->name);
204 		return (-1);
205 	}
206 
207 	return (0);
208 }
209 
210 /* md list */
211 void
212 md_list_add(struct auth_md_head *head, u_int8_t keyid, char *key)
213 {
214 	struct auth_md	*md;
215 
216 	if ((md = md_list_find(head, keyid)) != NULL) {
217 		/* update key */
218 		strncpy(md->key, key, sizeof(md->key));
219 		return;
220 	}
221 
222 	if ((md = calloc(1, sizeof(struct auth_md))) == NULL)
223 		fatalx("md_list_add");
224 
225 	md->keyid = keyid;
226 	strncpy(md->key, key, sizeof(md->key));
227 	TAILQ_INSERT_TAIL(head, md, entry);
228 }
229 
230 void
231 md_list_copy(struct auth_md_head *to, struct auth_md_head *from)
232 {
233 	struct auth_md	*m, *md;
234 
235 	TAILQ_INIT(to);
236 
237 	TAILQ_FOREACH(m, from, entry) {
238 		if ((md = calloc(1, sizeof(struct auth_md))) == NULL)
239 			fatalx("md_list_copy");
240 
241 		md->keyid = m->keyid;
242 		strncpy(md->key, m->key, sizeof(md->key));
243 		TAILQ_INSERT_TAIL(to, md, entry);
244 	}
245 }
246 
247 void
248 md_list_clr(struct auth_md_head *head)
249 {
250 	struct auth_md	*m;
251 
252 	while ((m = TAILQ_FIRST(head)) != NULL) {
253 		TAILQ_REMOVE(head, m, entry);
254 		free(m);
255 	}
256 }
257 
258 struct auth_md *
259 md_list_find(struct auth_md_head *head, u_int8_t keyid)
260 {
261 	struct auth_md	*m;
262 
263 	TAILQ_FOREACH(m, head, entry)
264 		if (m->keyid == keyid)
265 			return (m);
266 
267 	return (NULL);
268 }
269 
270 int
271 md_list_send(struct auth_md_head *head, struct imsgev *to)
272 {
273 	struct auth_md	*m;
274 
275 	TAILQ_FOREACH(m, head, entry)
276 		if (imsg_compose_event(to, IMSG_RECONF_AUTHMD, 0, 0, -1, m,
277 		    sizeof(*m)) == -1)
278 			return (-1);
279 
280 	return (0);
281 }
282