xref: /openbsd-src/usr.sbin/ospfd/auth.c (revision 4cf3f6fda4106306baf1f5c503e8869d28187047)
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 
31 struct auth_md *md_list_find(struct auth_md_head *, u_int8_t);
32 
33 int
auth_validate(void * buf,u_int16_t len,struct iface * iface,struct nbr * nbr)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
auth_gen(struct ibuf * buf,struct iface * iface)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, 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 */
212 void
md_list_add(struct auth_md_head * head,u_int8_t keyid,char * key)213 md_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 
231 void
md_list_copy(struct auth_md_head * to,struct auth_md_head * from)232 md_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 
248 void
md_list_clr(struct auth_md_head * head)249 md_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 
259 struct auth_md *
md_list_find(struct auth_md_head * head,u_int8_t keyid)260 md_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 
271 int
md_list_send(struct auth_md_head * head,struct imsgev * to)272 md_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