xref: /openbsd-src/usr.sbin/ripd/auth.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: auth.c,v 1.9 2007/10/24 20:52:50 claudio 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 	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 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(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 		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 		memcpy(digest, md->key, MD5_DIGEST_LENGTH);
156 
157 		/* calculate MD5 digest */
158 		MD5Init(&hash);
159 		MD5Update(&hash, b, ntohs(a->auth_offset) + RIP_HDR_LEN);
160 		MD5Update(&hash, digest, MD5_DIGEST_LENGTH);
161 		MD5Final(digest, &hash);
162 
163 		if (bcmp(recv_digest, digest, sizeof(digest))) {
164 			log_debug("auth_validate: invalid MD5 digest, "
165 			    "interface %s", iface->name);
166 			return (-1);
167 		}
168 
169 		*crypt_seq_num = ntohl(a->auth_seq);
170 
171 		*len -= AUTH_TRLR_HDR_LEN + MD5_DIGEST_LENGTH;
172 
173 		break;
174 	default:
175 		log_debug("auth_validate: unknown auth type, interface %s",
176 		    iface->name);
177 		return (-1);
178 	}
179 
180 	*buf += RIP_ENTRY_LEN;
181 	*len -= RIP_ENTRY_LEN;
182 
183 	return (0);
184 }
185 
186 int
187 auth_gen(struct buf *buf, struct iface *iface)
188 {
189 	struct rip_auth		 auth_head;
190 	struct md5_auth		 a;
191 	struct auth_md		 *md;
192 
193 	auth_head.auth_fixed = AUTH;
194 	auth_head.auth_type = htons(iface->auth_type);
195 
196 	buf_add(buf, &auth_head, sizeof(auth_head));
197 
198 	switch (iface->auth_type) {
199 	case AUTH_SIMPLE:
200 		buf_add(buf, &iface->auth_key, MAX_SIMPLE_AUTH_LEN);
201 		break;
202 	case AUTH_CRYPT:
203 		if ((md = md_list_find(&iface->auth_md_list,
204 		    iface->auth_keyid)) == NULL) {
205 			log_debug("auth_gen: keyid %d not configured, "
206 			    "interface %s", iface->auth_keyid, iface->name);
207 			return (-1);
208 		}
209 		bzero(&a, sizeof(a));
210 		a.auth_keyid = iface->auth_keyid;
211 		a.auth_seq = htonl(auth_get_seq_num(md));
212 		a.auth_length = MD5_DIGEST_LENGTH + AUTH_TRLR_HDR_LEN;
213 
214 		buf_add(buf, &a, sizeof(a));
215 		break;
216 	default:
217 		log_debug("auth_gen: unknown auth type, interface %s",
218 		    iface->name);
219 		return (-1);
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 	memcpy(digest, md->key, MD5_DIGEST_LENGTH);
249 
250 	auth_trailer_header_gen(buf);
251 
252 	/* calculate MD5 digest */
253 	MD5Init(&hash);
254 	MD5Update(&hash, buf->buf, buf->wpos);
255 	MD5Update(&hash, digest, MD5_DIGEST_LENGTH);
256 	MD5Final(digest, &hash);
257 
258 	return (buf_add(buf, digest, MD5_DIGEST_LENGTH));
259 }
260 
261 /* md list */
262 int
263 md_list_add(struct auth_md_head *head, u_int8_t keyid, char *key)
264 {
265 	struct auth_md	*md;
266 
267 	if (strlen(key) > MD5_DIGEST_LENGTH)
268 		return (-1);
269 
270 	if ((md = md_list_find(head, keyid)) != NULL) {
271 		/* update key */
272 		bzero(md->key, sizeof(md->key));
273 		memcpy(md->key, key, strlen(key));
274 		return (0);
275 	}
276 
277 	if ((md = calloc(1, sizeof(struct auth_md))) == NULL)
278 		fatalx("md_list_add");
279 
280 	md->keyid = keyid;
281 	memcpy(md->key, key, strlen(key));
282 	md->seq_modulator = auth_calc_modulator(md);
283 	TAILQ_INSERT_TAIL(head, md, entry);
284 
285 	return (0);
286 }
287 
288 void
289 md_list_copy(struct auth_md_head *to, struct auth_md_head *from)
290 {
291 	struct auth_md	*m, *md;
292 
293 	TAILQ_INIT(to);
294 
295 	TAILQ_FOREACH(m, from, entry) {
296 		if ((md = calloc(1, sizeof(struct auth_md))) == NULL)
297 			fatalx("md_list_copy");
298 
299 		md->keyid = m->keyid;
300 		memcpy(md->key, m->key, sizeof(md->key));
301 		md->seq_modulator = m->seq_modulator;
302 		TAILQ_INSERT_TAIL(to, md, entry);
303 	}
304 }
305 
306 void
307 md_list_clr(struct auth_md_head *head)
308 {
309 	struct auth_md	*m;
310 
311 	while ((m = TAILQ_FIRST(head)) != NULL) {
312 		TAILQ_REMOVE(head, m, entry);
313 		free(m);
314 	}
315 }
316 
317 struct auth_md *
318 md_list_find(struct auth_md_head *head, u_int8_t keyid)
319 {
320 	struct auth_md	*m;
321 
322 	TAILQ_FOREACH(m, head, entry)
323 		if (m->keyid == keyid)
324 			return (m);
325 
326 	return (NULL);
327 }
328