xref: /openbsd-src/usr.sbin/ripd/auth.c (revision daf88648c0e349d5c02e1504293082072c981640)
1 /*	$OpenBSD: auth.c,v 1.4 2006/11/27 15:02:34 stevesk 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 buf *);
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 	char			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 buf *buf)
62 {
63 	u_int16_t	 field1 = 0xFFFF;
64 	u_int16_t	 field2 = htons(0x01);
65 
66 	buf_add(buf, &field1, sizeof(field1));
67 	buf_add(buf, &field2, sizeof(field2));
68 }
69 
70 /* XXX add the support for key lifetime and rollover */
71 int
72 auth_validate(char **buf, u_int16_t *len, struct iface *iface, struct nbr *nbr,
73     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 	char			*auth_data;
83 	char			*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 		auth_data = *buf;
148 		auth_data += ntohs(a->auth_offset);
149 
150 		/* save the received digest and clear it in the packet */
151 		bcopy(auth_data, recv_digest, sizeof(recv_digest));
152 		bzero(auth_data, MD5_DIGEST_LENGTH);
153 
154 		/* insert plaintext key */
155 		bzero(digest, MD5_DIGEST_LENGTH);
156 		strncpy(digest, md->key, MD5_DIGEST_LENGTH);
157 
158 		/* calculate MD5 digest */
159 		MD5Init(&hash);
160 		MD5Update(&hash, b, ntohs(a->auth_offset) + RIP_HDR_LEN);
161 		MD5Update(&hash, digest, MD5_DIGEST_LENGTH);
162 		MD5Final(digest, &hash);
163 
164 		if (bcmp(recv_digest, digest, sizeof(digest))) {
165 			log_debug("auth_validate: invalid MD5 digest, "
166 			    "interface %s", iface->name);
167 			return (-1);
168 		}
169 
170 		*crypt_seq_num = ntohl(a->auth_seq);
171 
172 		*len -= AUTH_TRLR_HDR_LEN + MD5_DIGEST_LENGTH;
173 
174 		break;
175 	default:
176 		log_debug("auth_validate: unknown auth type, interface %s",
177 		    iface->name);
178 		return (-1);
179 	}
180 
181 	*buf += RIP_ENTRY_LEN;
182 	*len -= RIP_ENTRY_LEN;
183 
184 	return (0);
185 }
186 
187 int
188 auth_gen(struct buf *buf, struct iface *iface)
189 {
190 	struct rip_auth		 auth_head;
191 	struct md5_auth		 a;
192 	struct auth_md		 *md;
193 
194 	auth_head.auth_fixed = AUTH;
195 	auth_head.auth_type = htons(iface->auth_type);
196 
197 	buf_add(buf, &auth_head, sizeof(auth_head));
198 
199 	switch (iface->auth_type) {
200 	case AUTH_SIMPLE:
201 		return (buf_add(buf, &iface->auth_key, MAX_SIMPLE_AUTH_LEN));
202 		break;
203 	case AUTH_CRYPT:
204 		if ((md = md_list_find(&iface->auth_md_list,
205 		    iface->auth_keyid)) == NULL) {
206 			log_debug("auth_gen: keyid %d not configured, "
207 			    "interface %s", iface->auth_keyid, iface->name);
208 			return (-1);
209 		}
210 		bzero(&a, sizeof(a));
211 		a.auth_keyid = iface->auth_keyid;
212 		a.auth_seq = htonl(auth_get_seq_num(md));
213 		a.auth_length = MD5_DIGEST_LENGTH + AUTH_TRLR_HDR_LEN;
214 
215 		return (buf_add(buf, &a, sizeof(a)));
216 		break;
217 	default:
218 		/* NOTREACHED */
219 		break;
220 	}
221 
222 	return (0);
223 }
224 
225 int
226 auth_add_trailer(struct buf *buf, struct iface *iface)
227 {
228 	MD5_CTX			 hash;
229 	u_int8_t		 digest[MD5_DIGEST_LENGTH];
230 	struct auth_md		*md;
231 	struct md5_auth		*a;
232 	int			 pos;
233 
234 	pos = sizeof(struct rip_hdr) + sizeof(struct rip_auth);
235 
236 	/* add offset to header */
237 	a = buf_seek(buf, pos, sizeof(*a));
238 	a->auth_offset = htons(buf->wpos);
239 
240 	/* insert plaintext key */
241 	if ((md = md_list_find(&iface->auth_md_list,
242 	    iface->auth_keyid)) == NULL) {
243 		log_debug("auth_add_trailer: keyid %d not configured, "
244 		    "interface %s", iface->auth_keyid, iface->name);
245 			return (-1);
246 	}
247 
248 	bzero(digest, MD5_DIGEST_LENGTH);
249 	strncpy(digest, md->key, MD5_DIGEST_LENGTH);
250 
251 	auth_trailer_header_gen(buf);
252 
253 	/* calculate MD5 digest */
254 	MD5Init(&hash);
255 	MD5Update(&hash, buf->buf, buf->wpos);
256 	MD5Update(&hash, digest, MD5_DIGEST_LENGTH);
257 	MD5Final(digest, &hash);
258 
259 	return (buf_add(buf, digest, MD5_DIGEST_LENGTH));
260 }
261 
262 /* md list */
263 void
264 md_list_add(struct auth_md_head *head, u_int8_t keyid, char *key)
265 {
266 	struct auth_md	*md;
267 
268 	if ((md = md_list_find(head, keyid)) != NULL) {
269 		/* update key */
270 		strncpy(md->key, key, sizeof(md->key));
271 		return;
272 	}
273 
274 	if ((md = calloc(1, sizeof(struct auth_md))) == NULL)
275 		fatalx("md_list_add");
276 
277 	md->keyid = keyid;
278 	strncpy(md->key, key, sizeof(md->key));
279 	md->seq_modulator = auth_calc_modulator(md);
280 	TAILQ_INSERT_TAIL(head, md, entry);
281 }
282 
283 void
284 md_list_copy(struct auth_md_head *to, struct auth_md_head *from)
285 {
286 	struct auth_md	*m, *md;
287 
288 	TAILQ_INIT(to);
289 
290 	TAILQ_FOREACH(m, from, entry) {
291 		if ((md = calloc(1, sizeof(struct auth_md))) == NULL)
292 			fatalx("md_list_copy");
293 
294 		md->keyid = m->keyid;
295 		strncpy(md->key, m->key, sizeof(md->key));
296 		md->seq_modulator = m->seq_modulator;
297 		TAILQ_INSERT_TAIL(to, md, entry);
298 	}
299 }
300 
301 void
302 md_list_clr(struct auth_md_head *head)
303 {
304 	struct auth_md	*m;
305 
306 	while ((m = TAILQ_FIRST(head)) != NULL) {
307 		TAILQ_REMOVE(head, m, entry);
308 		free(m);
309 	}
310 }
311 
312 struct auth_md *
313 md_list_find(struct auth_md_head *head, u_int8_t keyid)
314 {
315 	struct auth_md	*m;
316 
317 	TAILQ_FOREACH(m, head, entry)
318 		if (m->keyid == keyid)
319 			return (m);
320 
321 	return (NULL);
322 }
323