xref: /openbsd/usr.sbin/ospfd/auth.c (revision e712b025)
1 /*	$OpenBSD: auth.c,v 1.8 2006/02/02 15:11:54 norby 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 int
32 auth_validate(void *buf, u_int16_t len, struct iface *iface, struct nbr *nbr)
33 {
34 	MD5_CTX		 hash;
35 	u_int8_t	 digest[MD5_DIGEST_LENGTH];
36 	u_int8_t	 recv_digest[MD5_DIGEST_LENGTH];
37 	struct ospf_hdr	*ospf_hdr = buf;
38 	struct auth_md	*md;
39 	char		*auth_data;
40 
41 	if (ntohs(ospf_hdr->auth_type) != (u_int16_t)iface->auth_type) {
42 		log_debug("auth_validate: wrong auth type, interface %s",
43 		    iface->name);
44 		return (-1);
45 	}
46 
47 	switch (iface->auth_type) {
48 	case AUTH_SIMPLE:
49 		if (bcmp(ospf_hdr->auth_key.simple, iface->auth_key,
50 		    sizeof(ospf_hdr->auth_key.simple))) {
51 			log_debug("auth_validate: wrong password, interface %s",
52 			    iface->name);
53 			return (-1);
54 		}
55 		/* FALLTHROUGH */
56 	case AUTH_NONE:
57 		/* clear the key before chksum */
58 		bzero(ospf_hdr->auth_key.simple,
59 		     sizeof(ospf_hdr->auth_key.simple));
60 
61 		if (in_cksum(ospf_hdr, ntohs(ospf_hdr->len))) {
62 			log_debug("auth_validate: invalid checksum, interface %s",
63 			    iface->name);
64 			return (-1);
65 		}
66 		break;
67 	case AUTH_CRYPT:
68 		/*
69 		 * We must allow keys that are configured on the interface
70 		 * but not necessarily set as the transmit key
71 		 * (iface->auth_keyid). This allows for key rotation to new
72 		 * keys without taking down the network.
73 		 */
74 		if ((md = md_list_find(iface, ospf_hdr->auth_key.crypt.keyid))
75 		    == NULL) {
76 			log_debug("auth_validate: keyid %d not configured, "
77 			    "interface %s", ospf_hdr->auth_key.crypt.keyid,
78 			    iface->name);
79 			return (-1);
80 		}
81 
82 		if (nbr != NULL && ntohl(ospf_hdr->auth_key.crypt.seq_num) <
83 		    nbr->crypt_seq_num) {
84 			log_debug("auth_validate: decreasing seq num, "
85 			    "interface %s", iface->name);
86 			return (-1);
87 		}
88 
89 		if (ospf_hdr->auth_key.crypt.len != MD5_DIGEST_LENGTH) {
90 			log_debug("auth_validate: invalid key length, "
91 			    "interface %s", iface->name);
92 			return (-1);
93 		}
94 
95 		if (len - ntohs(ospf_hdr->len) < MD5_DIGEST_LENGTH) {
96 			log_debug("auth_validate: invalid key length, "
97 			    "interface %s", iface->name);
98 			return (-1);
99 		}
100 
101 		auth_data = buf;
102 		auth_data += ntohs(ospf_hdr->len);
103 
104 		/* save the received digest and clear it in the packet */
105 		bcopy(auth_data, recv_digest, sizeof(recv_digest));
106 		bzero(auth_data, MD5_DIGEST_LENGTH);
107 
108 		/* insert plaintext key */
109 		bzero(digest, MD5_DIGEST_LENGTH);
110 		strncpy(digest, md->key, MD5_DIGEST_LENGTH);
111 
112 		/* calculate MD5 digest */
113 		MD5Init(&hash);
114 		MD5Update(&hash, buf, ntohs(ospf_hdr->len));
115 		MD5Update(&hash, digest, MD5_DIGEST_LENGTH);
116 		MD5Final(digest, &hash);
117 
118 		if (bcmp(recv_digest, digest, sizeof(digest))) {
119 			log_debug("auth_validate: invalid MD5 digest, "
120 			    "interface %s", iface->name);
121 			return (-1);
122 		}
123 
124 		if (nbr != NULL)
125 			nbr->crypt_seq_num =
126 			    ntohl(ospf_hdr->auth_key.crypt.seq_num);
127 		break;
128 	default:
129 		log_debug("auth_validate: unknown auth type, interface %s",
130 		    iface->name);
131 		return (-1);
132 	}
133 
134 	return (0);
135 }
136 
137 int
138 auth_gen(struct buf *buf, struct iface *iface)
139 {
140 	MD5_CTX		 hash;
141 	u_int8_t	 digest[MD5_DIGEST_LENGTH];
142 	struct ospf_hdr	*ospf_hdr;
143 	struct auth_md	*md;
144 
145 	if ((ospf_hdr = buf_seek(buf, 0, sizeof(ospf_hdr))) == NULL)
146 		fatalx("auth_gen: buf_seek failed");
147 
148 	/* update length */
149 	if (buf->wpos > USHRT_MAX)
150 		fatalx("auth_gen: resulting ospf packet too big");
151 	ospf_hdr->len = htons((u_int16_t)buf->wpos);
152 	/* clear auth_key field */
153 	bzero(ospf_hdr->auth_key.simple,
154 	    sizeof(ospf_hdr->auth_key.simple));
155 
156 	switch (iface->auth_type) {
157 	case AUTH_NONE:
158 		ospf_hdr->chksum = in_cksum(buf->buf, buf->wpos);
159 		break;
160 	case AUTH_SIMPLE:
161 		ospf_hdr->chksum = in_cksum(buf->buf, buf->wpos);
162 
163 		strncpy(ospf_hdr->auth_key.simple, iface->auth_key,
164 		    sizeof(ospf_hdr->auth_key.simple));
165 		break;
166 	case AUTH_CRYPT:
167 		ospf_hdr->chksum = 0;
168 		ospf_hdr->auth_key.crypt.keyid = iface->auth_keyid;
169 		ospf_hdr->auth_key.crypt.seq_num = htonl(iface->crypt_seq_num);
170 		ospf_hdr->auth_key.crypt.len = MD5_DIGEST_LENGTH;
171 		iface->crypt_seq_num++;
172 
173 		/* insert plaintext key */
174 		if ((md = md_list_find(iface, iface->auth_keyid))
175 		    == NULL) {
176 			log_debug("auth_validate: keyid %d not configured, "
177 			    "interface %s", iface->auth_keyid, iface->name);
178 			return (-1);
179 		}
180 
181 		bzero(digest, MD5_DIGEST_LENGTH);
182 		strncpy(digest, md->key, MD5_DIGEST_LENGTH);
183 
184 		/* calculate MD5 digest */
185 		MD5Init(&hash);
186 		MD5Update(&hash, buf->buf, buf->wpos);
187 		MD5Update(&hash, digest, MD5_DIGEST_LENGTH);
188 		MD5Final(digest, &hash);
189 
190 		return (buf_add(buf, digest, MD5_DIGEST_LENGTH));
191 	default:
192 		log_debug("auth_gen: unknown auth type, interface %s",
193 		    iface->name);
194 		return (-1);
195 	}
196 
197 	return (0);
198 }
199 
200 /* md list */
201 void
202 md_list_init(struct iface *iface)
203 {
204 	TAILQ_INIT(&iface->auth_md_list);
205 }
206 
207 void
208 md_list_add(struct iface *iface, u_int8_t keyid, char *key)
209 {
210 	struct auth_md	*m, *md;
211 
212 	if ((md = md_list_find(iface, keyid)) != NULL) {
213 		/* update key */
214 		strncpy(md->key, key, sizeof(md->key));
215 		return;
216 	}
217 
218 	if ((md = calloc(1, sizeof(struct auth_md))) == NULL)
219 		fatalx("md_list_add");
220 
221 	md->keyid = keyid;
222 	strncpy(md->key, key, sizeof(md->key));
223 
224 	TAILQ_FOREACH(m, &iface->auth_md_list, entry) {
225 		if (m->keyid > keyid) {
226 			TAILQ_INSERT_BEFORE(m, md, entry);
227 			return;
228 		}
229 	}
230 	TAILQ_INSERT_TAIL(&iface->auth_md_list, md, entry);
231 }
232 
233 void
234 md_list_clr(struct iface *iface)
235 {
236 	struct auth_md	*m;
237 
238 	while ((m = TAILQ_FIRST(&iface->auth_md_list)) != NULL) {
239 		TAILQ_REMOVE(&iface->auth_md_list, m, entry);
240 		free(m);
241 	}
242 }
243 
244 struct auth_md *
245 md_list_find(struct iface *iface, u_int8_t keyid)
246 {
247 	struct auth_md	*m;
248 
249 	TAILQ_FOREACH(m, &iface->auth_md_list, entry)
250 		if (m->keyid == keyid)
251 			return (m);
252 
253 	return (NULL);
254 }
255