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