1 /* $OpenBSD: auth.c,v 1.19 2010/09/02 14:03:21 sobrado 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 (memcmp(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 memcpy(recv_digest, auth_data, 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 (memcmp(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 ibuf *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 = ibuf_seek(buf, 0, sizeof(ospf_hdr))) == NULL) 148 fatalx("auth_gen: buf_seek failed"); 149 150 /* update length */ 151 if (ibuf_size(buf) > USHRT_MAX) 152 fatalx("auth_gen: resulting ospf packet too big"); 153 ospf_hdr->len = htons(ibuf_size(buf)); 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, ibuf_size(buf)); 160 break; 161 case AUTH_SIMPLE: 162 ospf_hdr->chksum = in_cksum(buf->buf, ibuf_size(buf)); 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_gen: 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, ibuf_size(buf)); 188 MD5Update(&hash, digest, MD5_DIGEST_LENGTH); 189 MD5Final(digest, &hash); 190 191 return (ibuf_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_copy"); 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 imsgev *to) 263 { 264 struct auth_md *m; 265 266 TAILQ_FOREACH(m, head, entry) 267 if (imsg_compose_event(to, IMSG_RECONF_AUTHMD, 0, 0, -1, m, 268 sizeof(*m)) == -1) 269 return (-1); 270 271 return (0); 272 } 273