xref: /openbsd/usr.sbin/ripd/auth.c (revision e6c7c102)
1 /*	$OpenBSD: auth.c,v 1.14 2024/04/23 13:34:51 jsg Exp $ */
2 
3 /*
4  * Copyright (c) 2006 Michele Marchetto <mydecay@openbeer.it>
5  * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/types.h>
21 #include <sys/socket.h>
22 #include <limits.h>
23 #include <md5.h>
24 #include <stddef.h>
25 #include <stdlib.h>
26 #include <string.h>
27 
28 #include "ripd.h"
29 #include "rip.h"
30 #include "log.h"
31 #include "ripe.h"
32 
33 u_int32_t	 auth_calc_modulator(struct auth_md *md);
34 struct auth_md	*md_list_find(struct auth_md_head *, u_int8_t);
35 void		 auth_trailer_header_gen(struct ibuf *);
36 u_int32_t	 auth_get_seq_num(struct auth_md*);
37 
38 u_int32_t
auth_calc_modulator(struct auth_md * md)39 auth_calc_modulator(struct auth_md *md)
40 {
41 	u_int32_t		r;
42 	MD5_CTX			md5ctx;
43 	u_int8_t		digest[MD5_DIGEST_LENGTH];
44 
45 	MD5Init(&md5ctx);
46 	MD5Update(&md5ctx, (void *)&md->keyid, sizeof(md->keyid));
47 	MD5Update(&md5ctx, (void *)&md->key, MD5_DIGEST_LENGTH);
48 	MD5Final(digest, &md5ctx);
49 
50 	bcopy(&digest, &r, sizeof(r));
51 
52 	return ((r >> 1) - time(NULL));
53 }
54 
55 u_int32_t
auth_get_seq_num(struct auth_md * md)56 auth_get_seq_num(struct auth_md *md)
57 {
58 	return (time(NULL) + md->seq_modulator);
59 }
60 
61 void
auth_trailer_header_gen(struct ibuf * buf)62 auth_trailer_header_gen(struct ibuf *buf)
63 {
64 	u_int16_t	 field1 = 0xFFFF;
65 	u_int16_t	 field2 = htons(0x01);
66 
67 	ibuf_add(buf, &field1, sizeof(field1));
68 	ibuf_add(buf, &field2, sizeof(field2));
69 }
70 
71 /* XXX add the support for key lifetime and rollover */
72 int
auth_validate(u_int8_t ** buf,u_int16_t * len,struct iface * iface,struct nbr * nbr,struct nbr_failed * nbr_failed,u_int32_t * crypt_seq_num)73 auth_validate(u_int8_t **buf, u_int16_t *len, struct iface *iface,
74     struct nbr *nbr, struct nbr_failed *nbr_failed, u_int32_t *crypt_seq_num)
75 {
76 	MD5_CTX			 hash;
77 	u_int8_t		 digest[MD5_DIGEST_LENGTH];
78 	u_int8_t		 recv_digest[MD5_DIGEST_LENGTH];
79 	char			 pwd[MAX_SIMPLE_AUTH_LEN];
80 	struct rip_auth		*auth_head;
81 	struct md5_auth		*a;
82 	struct auth_md		*md;
83 	u_int8_t		*auth_data;
84 	u_int8_t		*b = *buf;
85 
86 	*buf += RIP_HDR_LEN;
87 	*len -= RIP_HDR_LEN;
88 
89 	auth_head = (struct rip_auth *)(*buf);
90 
91 	if (auth_head->auth_fixed != AUTH) {
92 		if (iface->auth_type != AUTH_NONE) {
93 			log_debug("auth_validate: packet carrying no"
94 			    " authentication");
95 			return (-1);
96 		}
97 		return (0);
98 	} else {
99 		if (ntohs(auth_head->auth_type) !=
100 		    (u_int16_t)iface->auth_type) {
101 			log_debug("auth_validate: wrong auth type");
102 			return (-1);
103 		}
104 	}
105 
106 	switch (iface->auth_type) {
107 	case AUTH_SIMPLE:
108 		bcopy(*buf+sizeof(*auth_head), pwd, MAX_SIMPLE_AUTH_LEN);
109 		if (bcmp(pwd, iface->auth_key, MAX_SIMPLE_AUTH_LEN)) {
110 			log_debug("auth_validate: wrong password, "
111 			    "interface: %s", iface->name);
112 			return (-1);
113 		}
114 		break;
115 	case AUTH_CRYPT:
116 		a = (struct md5_auth *)(*buf + sizeof(*auth_head));
117 
118 		if ((md = md_list_find(&iface->auth_md_list,
119 		    a->auth_keyid)) == NULL) {
120 			log_debug("auth_validate: keyid %d not configured, "
121 			    "interface %s", a->auth_keyid,
122 			    iface->name);
123 			return (-1);
124 		}
125 
126 		if (nbr != NULL) {
127 			if (ntohl(a->auth_seq) < nbr->auth_seq_num) {
128 				log_debug("auth_validate: decreasing seq num, "
129 				    "interface %s", iface->name);
130 				return (-1);
131 			}
132 		} else if (nbr_failed != NULL) {
133 			if (ntohl(a->auth_seq) < nbr_failed->auth_seq_num &&
134 			    ntohl(a->auth_seq)) {
135 				log_debug("auth_validate: decreasing seq num, "
136 				    "interface %s", iface->name);
137 				return (-1);
138 			}
139 		}
140 
141 		/* XXX: maybe validate also the trailer header */
142 		if (a->auth_length != MD5_DIGEST_LENGTH + AUTH_TRLR_HDR_LEN) {
143 			log_debug("auth_validate: invalid key length, "
144 			    "interface %s", iface->name);
145 			return (-1);
146 		}
147 
148 		if (ntohs(a->auth_offset) != *len + RIP_HDR_LEN -
149 		    AUTH_TRLR_HDR_LEN - MD5_DIGEST_LENGTH) {
150 			log_debug("auth_validate: invalid authentication data "
151 			    "offset %hu, interface %s", ntohs(a->auth_offset),
152 			    iface->name);
153 			return (-1);
154 		}
155 
156 		auth_data = *buf;
157 		auth_data += ntohs(a->auth_offset);
158 
159 		/* save the received digest and clear it in the packet */
160 		bcopy(auth_data, recv_digest, sizeof(recv_digest));
161 		bzero(auth_data, MD5_DIGEST_LENGTH);
162 
163 		/* insert plaintext key */
164 		memcpy(digest, md->key, MD5_DIGEST_LENGTH);
165 
166 		/* calculate MD5 digest */
167 		MD5Init(&hash);
168 		MD5Update(&hash, b, ntohs(a->auth_offset) + RIP_HDR_LEN);
169 		MD5Update(&hash, digest, MD5_DIGEST_LENGTH);
170 		MD5Final(digest, &hash);
171 
172 		if (bcmp(recv_digest, digest, sizeof(digest))) {
173 			log_debug("auth_validate: invalid MD5 digest, "
174 			    "interface %s", iface->name);
175 			return (-1);
176 		}
177 
178 		*crypt_seq_num = ntohl(a->auth_seq);
179 
180 		*len -= AUTH_TRLR_HDR_LEN + MD5_DIGEST_LENGTH;
181 
182 		break;
183 	default:
184 		log_debug("auth_validate: unknown auth type, interface %s",
185 		    iface->name);
186 		return (-1);
187 	}
188 
189 	*buf += RIP_ENTRY_LEN;
190 	*len -= RIP_ENTRY_LEN;
191 
192 	return (0);
193 }
194 
195 int
auth_gen(struct ibuf * buf,struct iface * iface)196 auth_gen(struct ibuf *buf, struct iface *iface)
197 {
198 	struct rip_auth		 auth_head;
199 	struct md5_auth		 a;
200 	struct auth_md		 *md;
201 
202 	auth_head.auth_fixed = AUTH;
203 	auth_head.auth_type = htons(iface->auth_type);
204 
205 	ibuf_add(buf, &auth_head, sizeof(auth_head));
206 
207 	switch (iface->auth_type) {
208 	case AUTH_SIMPLE:
209 		ibuf_add(buf, &iface->auth_key, MAX_SIMPLE_AUTH_LEN);
210 		break;
211 	case AUTH_CRYPT:
212 		if ((md = md_list_find(&iface->auth_md_list,
213 		    iface->auth_keyid)) == NULL) {
214 			log_debug("auth_gen: keyid %d not configured, "
215 			    "interface %s", iface->auth_keyid, iface->name);
216 			return (-1);
217 		}
218 		bzero(&a, sizeof(a));
219 		a.auth_keyid = iface->auth_keyid;
220 		a.auth_seq = htonl(auth_get_seq_num(md));
221 		a.auth_length = MD5_DIGEST_LENGTH + AUTH_TRLR_HDR_LEN;
222 
223 		ibuf_add(buf, &a, sizeof(a));
224 		break;
225 	default:
226 		log_debug("auth_gen: unknown auth type, interface %s",
227 		    iface->name);
228 		return (-1);
229 	}
230 
231 	return (0);
232 }
233 
234 int
auth_add_trailer(struct ibuf * buf,struct iface * iface)235 auth_add_trailer(struct ibuf *buf, struct iface *iface)
236 {
237 	MD5_CTX			 hash;
238 	u_int8_t		 digest[MD5_DIGEST_LENGTH];
239 	struct auth_md		*md;
240 	size_t			 pos;
241 
242 	pos = sizeof(struct rip_hdr) + sizeof(struct rip_auth) +
243 	    offsetof(struct md5_auth, auth_offset);
244 
245 	/* add offset to header */
246 	if (ibuf_set_n16(buf, pos, ibuf_size(buf)) == -1)
247 		return (-1);
248 
249 	/* insert plaintext key */
250 	if ((md = md_list_find(&iface->auth_md_list,
251 	    iface->auth_keyid)) == NULL) {
252 		log_debug("auth_add_trailer: keyid %d not configured, "
253 		    "interface %s", iface->auth_keyid, iface->name);
254 		return (-1);
255 	}
256 
257 	memcpy(digest, md->key, MD5_DIGEST_LENGTH);
258 
259 	auth_trailer_header_gen(buf);
260 
261 	/* calculate MD5 digest */
262 	MD5Init(&hash);
263 	MD5Update(&hash, ibuf_data(buf), ibuf_size(buf));
264 	MD5Update(&hash, digest, MD5_DIGEST_LENGTH);
265 	MD5Final(digest, &hash);
266 
267 	return (ibuf_add(buf, digest, MD5_DIGEST_LENGTH));
268 }
269 
270 /* md list */
271 int
md_list_add(struct auth_md_head * head,u_int8_t keyid,char * key)272 md_list_add(struct auth_md_head *head, u_int8_t keyid, char *key)
273 {
274 	struct auth_md	*md;
275 
276 	if (strlen(key) > MD5_DIGEST_LENGTH)
277 		return (-1);
278 
279 	if ((md = md_list_find(head, keyid)) != NULL) {
280 		/* update key */
281 		bzero(md->key, sizeof(md->key));
282 		memcpy(md->key, key, strlen(key));
283 		return (0);
284 	}
285 
286 	if ((md = calloc(1, sizeof(struct auth_md))) == NULL)
287 		fatalx("md_list_add");
288 
289 	md->keyid = keyid;
290 	memcpy(md->key, key, strlen(key));
291 	md->seq_modulator = auth_calc_modulator(md);
292 	TAILQ_INSERT_TAIL(head, md, entry);
293 
294 	return (0);
295 }
296 
297 void
md_list_copy(struct auth_md_head * to,struct auth_md_head * from)298 md_list_copy(struct auth_md_head *to, struct auth_md_head *from)
299 {
300 	struct auth_md	*m, *md;
301 
302 	TAILQ_INIT(to);
303 
304 	TAILQ_FOREACH(m, from, entry) {
305 		if ((md = calloc(1, sizeof(struct auth_md))) == NULL)
306 			fatalx("md_list_copy");
307 
308 		md->keyid = m->keyid;
309 		memcpy(md->key, m->key, sizeof(md->key));
310 		md->seq_modulator = m->seq_modulator;
311 		TAILQ_INSERT_TAIL(to, md, entry);
312 	}
313 }
314 
315 void
md_list_clr(struct auth_md_head * head)316 md_list_clr(struct auth_md_head *head)
317 {
318 	struct auth_md	*m;
319 
320 	while ((m = TAILQ_FIRST(head)) != NULL) {
321 		TAILQ_REMOVE(head, m, entry);
322 		free(m);
323 	}
324 }
325 
326 struct auth_md *
md_list_find(struct auth_md_head * head,u_int8_t keyid)327 md_list_find(struct auth_md_head *head, u_int8_t keyid)
328 {
329 	struct auth_md	*m;
330 
331 	TAILQ_FOREACH(m, head, entry)
332 		if (m->keyid == keyid)
333 			return (m);
334 
335 	return (NULL);
336 }
337