xref: /openbsd/usr.sbin/ospfd/auth.c (revision 96c7fd34)
1 /*	$OpenBSD: auth.c,v 1.11 2007/02/01 12:41:03 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
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 (bcmp(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 		bcopy(auth_data, recv_digest, 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 (bcmp(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
140 auth_gen(struct buf *buf, struct iface *iface)
141 {
142 	MD5_CTX		 hash;
143 	u_int8_t	 digest[MD5_DIGEST_LENGTH];
144 	struct ospf_hdr	*ospf_hdr;
145 	struct auth_md	*md;
146 
147 	if ((ospf_hdr = buf_seek(buf, 0, sizeof(ospf_hdr))) == NULL)
148 		fatalx("auth_gen: buf_seek failed");
149 
150 	/* update length */
151 	if (buf->wpos > USHRT_MAX)
152 		fatalx("auth_gen: resulting ospf packet too big");
153 	ospf_hdr->len = htons((u_int16_t)buf->wpos);
154 	/* clear auth_key field */
155 	bzero(ospf_hdr->auth_key.simple, sizeof(ospf_hdr->auth_key.simple));
156 
157 	switch (iface->auth_type) {
158 	case AUTH_NONE:
159 		ospf_hdr->chksum = in_cksum(buf->buf, buf->wpos);
160 		break;
161 	case AUTH_SIMPLE:
162 		ospf_hdr->chksum = in_cksum(buf->buf, buf->wpos);
163 
164 		strncpy(ospf_hdr->auth_key.simple, iface->auth_key,
165 		    sizeof(ospf_hdr->auth_key.simple));
166 		break;
167 	case AUTH_CRYPT:
168 		ospf_hdr->chksum = 0;
169 		ospf_hdr->auth_key.crypt.keyid = iface->auth_keyid;
170 		ospf_hdr->auth_key.crypt.seq_num = htonl(iface->crypt_seq_num);
171 		ospf_hdr->auth_key.crypt.len = MD5_DIGEST_LENGTH;
172 		iface->crypt_seq_num++;
173 
174 		/* insert plaintext key */
175 		if ((md = md_list_find(&iface->auth_md_list,
176 		    iface->auth_keyid)) == NULL) {
177 			log_debug("auth_validate: keyid %d not configured, "
178 			    "interface %s", iface->auth_keyid, iface->name);
179 			return (-1);
180 		}
181 
182 		bzero(digest, MD5_DIGEST_LENGTH);
183 		strncpy(digest, md->key, MD5_DIGEST_LENGTH);
184 
185 		/* calculate MD5 digest */
186 		MD5Init(&hash);
187 		MD5Update(&hash, buf->buf, buf->wpos);
188 		MD5Update(&hash, digest, MD5_DIGEST_LENGTH);
189 		MD5Final(digest, &hash);
190 
191 		return (buf_add(buf, digest, MD5_DIGEST_LENGTH));
192 	default:
193 		log_debug("auth_gen: unknown auth type, interface %s",
194 		    iface->name);
195 		return (-1);
196 	}
197 
198 	return (0);
199 }
200 
201 /* md list */
202 void
203 md_list_add(struct auth_md_head *head, u_int8_t keyid, char *key)
204 {
205 	struct auth_md	*md;
206 
207 	if ((md = md_list_find(head, keyid)) != NULL) {
208 		/* update key */
209 		strncpy(md->key, key, sizeof(md->key));
210 		return;
211 	}
212 
213 	if ((md = calloc(1, sizeof(struct auth_md))) == NULL)
214 		fatalx("md_list_add");
215 
216 	md->keyid = keyid;
217 	strncpy(md->key, key, sizeof(md->key));
218 	TAILQ_INSERT_TAIL(head, md, entry);
219 }
220 
221 void
222 md_list_copy(struct auth_md_head *to, struct auth_md_head *from)
223 {
224 	struct auth_md	*m, *md;
225 
226 	TAILQ_INIT(to);
227 
228 	TAILQ_FOREACH(m, from, entry) {
229 		if ((md = calloc(1, sizeof(struct auth_md))) == NULL)
230 			fatalx("md_list_add");
231 
232 		md->keyid = m->keyid;
233 		strncpy(md->key, m->key, sizeof(md->key));
234 		TAILQ_INSERT_TAIL(to, md, entry);
235 	}
236 }
237 
238 void
239 md_list_clr(struct auth_md_head *head)
240 {
241 	struct auth_md	*m;
242 
243 	while ((m = TAILQ_FIRST(head)) != NULL) {
244 		TAILQ_REMOVE(head, m, entry);
245 		free(m);
246 	}
247 }
248 
249 struct auth_md *
250 md_list_find(struct auth_md_head *head, u_int8_t keyid)
251 {
252 	struct auth_md	*m;
253 
254 	TAILQ_FOREACH(m, head, entry)
255 		if (m->keyid == keyid)
256 			return (m);
257 
258 	return (NULL);
259 }
260 
261 int
262 md_list_send(struct auth_md_head *head, struct imsgbuf *to)
263 {
264 	struct auth_md	*m;
265 
266 	TAILQ_FOREACH(m, head, entry)
267 		if (imsg_compose(to, IMSG_RECONF_AUTHMD, 0, 0, m,
268 		    sizeof(*m)) == -1)
269 			return (-1);
270 
271 	return (0);
272 }
273