/* $OpenBSD: auth.c,v 1.11 2010/05/26 13:56:08 nicm Exp $ */ /* * Copyright (c) 2006 Michele Marchetto * Copyright (c) 2004, 2005 Esben Norby * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include "ripd.h" #include "rip.h" #include "log.h" #include "ripe.h" u_int32_t auth_calc_modulator(struct auth_md *md); struct auth_md *md_list_find(struct auth_md_head *, u_int8_t); void auth_trailer_header_gen(struct ibuf *); u_int32_t auth_get_seq_num(struct auth_md*); u_int32_t auth_calc_modulator(struct auth_md *md) { u_int32_t r; MD5_CTX md5ctx; u_int8_t digest[MD5_DIGEST_LENGTH]; MD5Init(&md5ctx); MD5Update(&md5ctx, (void *)&md->keyid, sizeof(md->keyid)); MD5Update(&md5ctx, (void *)&md->key, MD5_DIGEST_LENGTH); MD5Final(digest, &md5ctx); bcopy(&digest, &r, sizeof(r)); return ((r >> 1) - time(NULL)); } u_int32_t auth_get_seq_num(struct auth_md *md) { return (time(NULL) + md->seq_modulator); } void auth_trailer_header_gen(struct ibuf *buf) { u_int16_t field1 = 0xFFFF; u_int16_t field2 = htons(0x01); ibuf_add(buf, &field1, sizeof(field1)); ibuf_add(buf, &field2, sizeof(field2)); } /* XXX add the support for key lifetime and rollover */ int auth_validate(u_int8_t **buf, u_int16_t *len, struct iface *iface, struct nbr *nbr, struct nbr_failed *nbr_failed, u_int32_t *crypt_seq_num) { MD5_CTX hash; u_int8_t digest[MD5_DIGEST_LENGTH]; u_int8_t recv_digest[MD5_DIGEST_LENGTH]; char pwd[MAX_SIMPLE_AUTH_LEN]; struct rip_auth *auth_head; struct md5_auth *a; struct auth_md *md; u_int8_t *auth_data; u_int8_t *b = *buf; *buf += RIP_HDR_LEN; *len -= RIP_HDR_LEN; auth_head = (struct rip_auth *)(*buf); if (auth_head->auth_fixed != AUTH) { if (iface->auth_type != AUTH_NONE) { log_debug("auth_validate: packet carrying no" " authentication"); return (-1); } return (0); } else { if (ntohs(auth_head->auth_type) != (u_int16_t)iface->auth_type) { log_debug("auth_validate: wrong auth type"); return (-1); } } switch (iface->auth_type) { case AUTH_SIMPLE: bcopy(*buf+sizeof(*auth_head), pwd, MAX_SIMPLE_AUTH_LEN); if (bcmp(pwd, iface->auth_key, MAX_SIMPLE_AUTH_LEN)) { log_debug("auth_validate: wrong password, " "interface: %s", iface->name); return (-1); } break; case AUTH_CRYPT: a = (struct md5_auth *)(*buf + sizeof(*auth_head)); if ((md = md_list_find(&iface->auth_md_list, a->auth_keyid)) == NULL) { log_debug("auth_validate: keyid %d not configured, " "interface %s", a->auth_keyid, iface->name); return (-1); } if (nbr != NULL) { if (ntohl(a->auth_seq) < nbr->auth_seq_num) { log_debug("auth_validate: decreasing seq num, " "interface %s", iface->name); return (-1); } } else if (nbr_failed != NULL) { if (ntohl(a->auth_seq) < nbr_failed->auth_seq_num && ntohl(a->auth_seq)) { log_debug("auth_validate: decreasing seq num, " "interface %s", iface->name); return (-1); } } /* XXX: maybe validate also the trailer header */ if (a->auth_length != MD5_DIGEST_LENGTH + AUTH_TRLR_HDR_LEN) { log_debug("auth_validate: invalid key length, " "interface %s", iface->name); return (-1); } auth_data = *buf; auth_data += ntohs(a->auth_offset); /* save the received digest and clear it in the packet */ bcopy(auth_data, recv_digest, sizeof(recv_digest)); bzero(auth_data, MD5_DIGEST_LENGTH); /* insert plaintext key */ memcpy(digest, md->key, MD5_DIGEST_LENGTH); /* calculate MD5 digest */ MD5Init(&hash); MD5Update(&hash, b, ntohs(a->auth_offset) + RIP_HDR_LEN); MD5Update(&hash, digest, MD5_DIGEST_LENGTH); MD5Final(digest, &hash); if (bcmp(recv_digest, digest, sizeof(digest))) { log_debug("auth_validate: invalid MD5 digest, " "interface %s", iface->name); return (-1); } *crypt_seq_num = ntohl(a->auth_seq); *len -= AUTH_TRLR_HDR_LEN + MD5_DIGEST_LENGTH; break; default: log_debug("auth_validate: unknown auth type, interface %s", iface->name); return (-1); } *buf += RIP_ENTRY_LEN; *len -= RIP_ENTRY_LEN; return (0); } int auth_gen(struct ibuf *buf, struct iface *iface) { struct rip_auth auth_head; struct md5_auth a; struct auth_md *md; auth_head.auth_fixed = AUTH; auth_head.auth_type = htons(iface->auth_type); ibuf_add(buf, &auth_head, sizeof(auth_head)); switch (iface->auth_type) { case AUTH_SIMPLE: ibuf_add(buf, &iface->auth_key, MAX_SIMPLE_AUTH_LEN); break; case AUTH_CRYPT: if ((md = md_list_find(&iface->auth_md_list, iface->auth_keyid)) == NULL) { log_debug("auth_gen: keyid %d not configured, " "interface %s", iface->auth_keyid, iface->name); return (-1); } bzero(&a, sizeof(a)); a.auth_keyid = iface->auth_keyid; a.auth_seq = htonl(auth_get_seq_num(md)); a.auth_length = MD5_DIGEST_LENGTH + AUTH_TRLR_HDR_LEN; ibuf_add(buf, &a, sizeof(a)); break; default: log_debug("auth_gen: unknown auth type, interface %s", iface->name); return (-1); } return (0); } int auth_add_trailer(struct ibuf *buf, struct iface *iface) { MD5_CTX hash; u_int8_t digest[MD5_DIGEST_LENGTH]; struct auth_md *md; struct md5_auth *a; int pos; pos = sizeof(struct rip_hdr) + sizeof(struct rip_auth); /* add offset to header */ a = ibuf_seek(buf, pos, sizeof(*a)); a->auth_offset = htons(buf->wpos); /* insert plaintext key */ if ((md = md_list_find(&iface->auth_md_list, iface->auth_keyid)) == NULL) { log_debug("auth_add_trailer: keyid %d not configured, " "interface %s", iface->auth_keyid, iface->name); return (-1); } memcpy(digest, md->key, MD5_DIGEST_LENGTH); auth_trailer_header_gen(buf); /* calculate MD5 digest */ MD5Init(&hash); MD5Update(&hash, buf->buf, buf->wpos); MD5Update(&hash, digest, MD5_DIGEST_LENGTH); MD5Final(digest, &hash); return (ibuf_add(buf, digest, MD5_DIGEST_LENGTH)); } /* md list */ int md_list_add(struct auth_md_head *head, u_int8_t keyid, char *key) { struct auth_md *md; if (strlen(key) > MD5_DIGEST_LENGTH) return (-1); if ((md = md_list_find(head, keyid)) != NULL) { /* update key */ bzero(md->key, sizeof(md->key)); memcpy(md->key, key, strlen(key)); return (0); } if ((md = calloc(1, sizeof(struct auth_md))) == NULL) fatalx("md_list_add"); md->keyid = keyid; memcpy(md->key, key, strlen(key)); md->seq_modulator = auth_calc_modulator(md); TAILQ_INSERT_TAIL(head, md, entry); return (0); } void md_list_copy(struct auth_md_head *to, struct auth_md_head *from) { struct auth_md *m, *md; TAILQ_INIT(to); TAILQ_FOREACH(m, from, entry) { if ((md = calloc(1, sizeof(struct auth_md))) == NULL) fatalx("md_list_copy"); md->keyid = m->keyid; memcpy(md->key, m->key, sizeof(md->key)); md->seq_modulator = m->seq_modulator; TAILQ_INSERT_TAIL(to, md, entry); } } void md_list_clr(struct auth_md_head *head) { struct auth_md *m; while ((m = TAILQ_FIRST(head)) != NULL) { TAILQ_REMOVE(head, m, entry); free(m); } } struct auth_md * md_list_find(struct auth_md_head *head, u_int8_t keyid) { struct auth_md *m; TAILQ_FOREACH(m, head, entry) if (m->keyid == keyid) return (m); return (NULL); }