xref: /openbsd/usr.sbin/ripd/auth.c (revision 905646f0)
1 /*	$OpenBSD: auth.c,v 1.12 2019/12/19 16:47:14 remi 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 <stdlib.h>
25 #include <string.h>
26 
27 #include "ripd.h"
28 #include "rip.h"
29 #include "log.h"
30 #include "ripe.h"
31 
32 u_int32_t	 auth_calc_modulator(struct auth_md *md);
33 struct auth_md	*md_list_find(struct auth_md_head *, u_int8_t);
34 void		 auth_trailer_header_gen(struct ibuf *);
35 u_int32_t	 auth_get_seq_num(struct auth_md*);
36 
37 u_int32_t
38 auth_calc_modulator(struct auth_md *md)
39 {
40 	u_int32_t		r;
41 	MD5_CTX			md5ctx;
42 	u_int8_t		digest[MD5_DIGEST_LENGTH];
43 
44 	MD5Init(&md5ctx);
45 	MD5Update(&md5ctx, (void *)&md->keyid, sizeof(md->keyid));
46 	MD5Update(&md5ctx, (void *)&md->key, MD5_DIGEST_LENGTH);
47 	MD5Final(digest, &md5ctx);
48 
49 	bcopy(&digest, &r, sizeof(r));
50 
51 	return ((r >> 1) - time(NULL));
52 }
53 
54 u_int32_t
55 auth_get_seq_num(struct auth_md *md)
56 {
57 	return (time(NULL) + md->seq_modulator);
58 }
59 
60 void
61 auth_trailer_header_gen(struct ibuf *buf)
62 {
63 	u_int16_t	 field1 = 0xFFFF;
64 	u_int16_t	 field2 = htons(0x01);
65 
66 	ibuf_add(buf, &field1, sizeof(field1));
67 	ibuf_add(buf, &field2, sizeof(field2));
68 }
69 
70 /* XXX add the support for key lifetime and rollover */
71 int
72 auth_validate(u_int8_t **buf, u_int16_t *len, struct iface *iface,
73     struct nbr *nbr, struct nbr_failed *nbr_failed, u_int32_t *crypt_seq_num)
74 {
75 	MD5_CTX			 hash;
76 	u_int8_t		 digest[MD5_DIGEST_LENGTH];
77 	u_int8_t		 recv_digest[MD5_DIGEST_LENGTH];
78 	char			 pwd[MAX_SIMPLE_AUTH_LEN];
79 	struct rip_auth		*auth_head;
80 	struct md5_auth		*a;
81 	struct auth_md		*md;
82 	u_int8_t		*auth_data;
83 	u_int8_t		*b = *buf;
84 
85 	*buf += RIP_HDR_LEN;
86 	*len -= RIP_HDR_LEN;
87 
88 	auth_head = (struct rip_auth *)(*buf);
89 
90 	if (auth_head->auth_fixed != AUTH) {
91 		if (iface->auth_type != AUTH_NONE) {
92 			log_debug("auth_validate: packet carrying no"
93 			    " authentication");
94 			return (-1);
95 		}
96 		return (0);
97 	} else {
98 		if (ntohs(auth_head->auth_type) !=
99 		    (u_int16_t)iface->auth_type) {
100 			log_debug("auth_validate: wrong auth type");
101 			return (-1);
102 		}
103 	}
104 
105 	switch (iface->auth_type) {
106 	case AUTH_SIMPLE:
107 		bcopy(*buf+sizeof(*auth_head), pwd, MAX_SIMPLE_AUTH_LEN);
108 		if (bcmp(pwd, iface->auth_key, MAX_SIMPLE_AUTH_LEN)) {
109 			log_debug("auth_validate: wrong password, "
110 			    "interface: %s", iface->name);
111 			return (-1);
112 		}
113 		break;
114 	case AUTH_CRYPT:
115 		a = (struct md5_auth *)(*buf + sizeof(*auth_head));
116 
117 		if ((md = md_list_find(&iface->auth_md_list,
118 		    a->auth_keyid)) == NULL) {
119 			log_debug("auth_validate: keyid %d not configured, "
120 			    "interface %s", a->auth_keyid,
121 			    iface->name);
122 			return (-1);
123 		}
124 
125 		if (nbr != NULL) {
126 			if (ntohl(a->auth_seq) < nbr->auth_seq_num) {
127 				log_debug("auth_validate: decreasing seq num, "
128 				    "interface %s", iface->name);
129 				return (-1);
130 			}
131 		} else if (nbr_failed != NULL) {
132 			if (ntohl(a->auth_seq) < nbr_failed->auth_seq_num &&
133 			    ntohl(a->auth_seq)) {
134 				log_debug("auth_validate: decreasing seq num, "
135 				    "interface %s", iface->name);
136 				return (-1);
137 			}
138 		}
139 
140 		/* XXX: maybe validate also the trailer header */
141 		if (a->auth_length != MD5_DIGEST_LENGTH + AUTH_TRLR_HDR_LEN) {
142 			log_debug("auth_validate: invalid key length, "
143 			    "interface %s", iface->name);
144 			return (-1);
145 		}
146 
147 		if (ntohs(a->auth_offset) != *len + RIP_HDR_LEN -
148 		    AUTH_TRLR_HDR_LEN - MD5_DIGEST_LENGTH) {
149 			log_debug("auth_validate: invalid authentication data "
150 			    "offset %hu, interface %s", ntohs(a->auth_offset),
151 			    iface->name);
152 			return (-1);
153 		}
154 
155 		auth_data = *buf;
156 		auth_data += ntohs(a->auth_offset);
157 
158 		/* save the received digest and clear it in the packet */
159 		bcopy(auth_data, recv_digest, sizeof(recv_digest));
160 		bzero(auth_data, MD5_DIGEST_LENGTH);
161 
162 		/* insert plaintext key */
163 		memcpy(digest, md->key, MD5_DIGEST_LENGTH);
164 
165 		/* calculate MD5 digest */
166 		MD5Init(&hash);
167 		MD5Update(&hash, b, ntohs(a->auth_offset) + RIP_HDR_LEN);
168 		MD5Update(&hash, digest, MD5_DIGEST_LENGTH);
169 		MD5Final(digest, &hash);
170 
171 		if (bcmp(recv_digest, digest, sizeof(digest))) {
172 			log_debug("auth_validate: invalid MD5 digest, "
173 			    "interface %s", iface->name);
174 			return (-1);
175 		}
176 
177 		*crypt_seq_num = ntohl(a->auth_seq);
178 
179 		*len -= AUTH_TRLR_HDR_LEN + MD5_DIGEST_LENGTH;
180 
181 		break;
182 	default:
183 		log_debug("auth_validate: unknown auth type, interface %s",
184 		    iface->name);
185 		return (-1);
186 	}
187 
188 	*buf += RIP_ENTRY_LEN;
189 	*len -= RIP_ENTRY_LEN;
190 
191 	return (0);
192 }
193 
194 int
195 auth_gen(struct ibuf *buf, struct iface *iface)
196 {
197 	struct rip_auth		 auth_head;
198 	struct md5_auth		 a;
199 	struct auth_md		 *md;
200 
201 	auth_head.auth_fixed = AUTH;
202 	auth_head.auth_type = htons(iface->auth_type);
203 
204 	ibuf_add(buf, &auth_head, sizeof(auth_head));
205 
206 	switch (iface->auth_type) {
207 	case AUTH_SIMPLE:
208 		ibuf_add(buf, &iface->auth_key, MAX_SIMPLE_AUTH_LEN);
209 		break;
210 	case AUTH_CRYPT:
211 		if ((md = md_list_find(&iface->auth_md_list,
212 		    iface->auth_keyid)) == NULL) {
213 			log_debug("auth_gen: keyid %d not configured, "
214 			    "interface %s", iface->auth_keyid, iface->name);
215 			return (-1);
216 		}
217 		bzero(&a, sizeof(a));
218 		a.auth_keyid = iface->auth_keyid;
219 		a.auth_seq = htonl(auth_get_seq_num(md));
220 		a.auth_length = MD5_DIGEST_LENGTH + AUTH_TRLR_HDR_LEN;
221 
222 		ibuf_add(buf, &a, sizeof(a));
223 		break;
224 	default:
225 		log_debug("auth_gen: unknown auth type, interface %s",
226 		    iface->name);
227 		return (-1);
228 	}
229 
230 	return (0);
231 }
232 
233 int
234 auth_add_trailer(struct ibuf *buf, struct iface *iface)
235 {
236 	MD5_CTX			 hash;
237 	u_int8_t		 digest[MD5_DIGEST_LENGTH];
238 	struct auth_md		*md;
239 	struct md5_auth		*a;
240 	int			 pos;
241 
242 	pos = sizeof(struct rip_hdr) + sizeof(struct rip_auth);
243 
244 	/* add offset to header */
245 	a = ibuf_seek(buf, pos, sizeof(*a));
246 	a->auth_offset = htons(buf->wpos);
247 
248 	/* insert plaintext key */
249 	if ((md = md_list_find(&iface->auth_md_list,
250 	    iface->auth_keyid)) == NULL) {
251 		log_debug("auth_add_trailer: keyid %d not configured, "
252 		    "interface %s", iface->auth_keyid, iface->name);
253 			return (-1);
254 	}
255 
256 	memcpy(digest, md->key, MD5_DIGEST_LENGTH);
257 
258 	auth_trailer_header_gen(buf);
259 
260 	/* calculate MD5 digest */
261 	MD5Init(&hash);
262 	MD5Update(&hash, buf->buf, buf->wpos);
263 	MD5Update(&hash, digest, MD5_DIGEST_LENGTH);
264 	MD5Final(digest, &hash);
265 
266 	return (ibuf_add(buf, digest, MD5_DIGEST_LENGTH));
267 }
268 
269 /* md list */
270 int
271 md_list_add(struct auth_md_head *head, u_int8_t keyid, char *key)
272 {
273 	struct auth_md	*md;
274 
275 	if (strlen(key) > MD5_DIGEST_LENGTH)
276 		return (-1);
277 
278 	if ((md = md_list_find(head, keyid)) != NULL) {
279 		/* update key */
280 		bzero(md->key, sizeof(md->key));
281 		memcpy(md->key, key, strlen(key));
282 		return (0);
283 	}
284 
285 	if ((md = calloc(1, sizeof(struct auth_md))) == NULL)
286 		fatalx("md_list_add");
287 
288 	md->keyid = keyid;
289 	memcpy(md->key, key, strlen(key));
290 	md->seq_modulator = auth_calc_modulator(md);
291 	TAILQ_INSERT_TAIL(head, md, entry);
292 
293 	return (0);
294 }
295 
296 void
297 md_list_copy(struct auth_md_head *to, struct auth_md_head *from)
298 {
299 	struct auth_md	*m, *md;
300 
301 	TAILQ_INIT(to);
302 
303 	TAILQ_FOREACH(m, from, entry) {
304 		if ((md = calloc(1, sizeof(struct auth_md))) == NULL)
305 			fatalx("md_list_copy");
306 
307 		md->keyid = m->keyid;
308 		memcpy(md->key, m->key, sizeof(md->key));
309 		md->seq_modulator = m->seq_modulator;
310 		TAILQ_INSERT_TAIL(to, md, entry);
311 	}
312 }
313 
314 void
315 md_list_clr(struct auth_md_head *head)
316 {
317 	struct auth_md	*m;
318 
319 	while ((m = TAILQ_FIRST(head)) != NULL) {
320 		TAILQ_REMOVE(head, m, entry);
321 		free(m);
322 	}
323 }
324 
325 struct auth_md *
326 md_list_find(struct auth_md_head *head, u_int8_t keyid)
327 {
328 	struct auth_md	*m;
329 
330 	TAILQ_FOREACH(m, head, entry)
331 		if (m->keyid == keyid)
332 			return (m);
333 
334 	return (NULL);
335 }
336