static struct keychain *keychain_get(const char *name) { struct keychain *keychain; keychain = keychain_lookup(name); if (keychain) return keychain; keychain = keychain_new(); keychain->name = XSTRDUP(MTYPE_KEYCHAIN, name); keychain->key = list_new(); keychain->key->cmp = (int (*)(void *, void *))key_cmp_func; keychain->key->del = (void (*)(void *))key_delete_func; listnode_add(keychain_list, keychain); return keychain; }
/* RIP-2 simple password authentication. */ static int rip_auth_check_password (struct rip_interface *ri, struct rip_auth_rte *auth) { if (ri->auth_str) { u_int8_t padded_simple_password[RIP_AUTH_SIMPLE_SIZE] = { 0 }; memcpy (padded_simple_password, ri->auth_str, MIN (RIP_AUTH_SIMPLE_SIZE, strlen (ri->auth_str))); if (! memcmp (auth->u.password, padded_simple_password, RIP_AUTH_SIMPLE_SIZE)) { if (IS_RIP_DEBUG_AUTH) zlog_debug ("interface authentication string is configured and matches"); return 1; } if (IS_RIP_DEBUG_AUTH) zlog_debug ("interface authentication string is configured, but does not match"); } if (ri->key_chain) { struct keychain *keychain; struct key *key; keychain = keychain_lookup (ri->key_chain); if (keychain == NULL) { if (IS_RIP_DEBUG_AUTH) zlog_debug ("key chain '%s' is configured, but does not exist", ri->key_chain); return 0; } if (IS_RIP_DEBUG_AUTH) zlog_debug ("trying configured key chain '%s'", ri->key_chain); key = key_match_for_accept (keychain, auth->u.password); if (key) { if (IS_RIP_DEBUG_AUTH) zlog_debug ("key %u matched the packet", key->index); return 1; } if (IS_RIP_DEBUG_AUTH) zlog_debug ("no key matched the packet"); } return 0; }
/* Check hash authentication. Assume the packet has passed rip_packet_examin() and rip_auth_check_packet(), which implies: 1. Authentication header is the first RTE and filled in correctly. 2. Authentication trailer is present and within the buffer. */ static int rip_auth_check_hash (struct rip_interface *ri, struct in_addr *from, struct rip_packet *packet) { struct rip_auth_rte *hi, *hd; struct keychain *keychain; struct key *key; u_char received_digest[RIP_AUTH_MAX_SIZE], local_digest[RIP_AUTH_MAX_SIZE]; u_int16_t packet_len; char *auth_str = NULL; u_int8_t local_dlen, remote_dlen; unsigned hash_error; u_int32_t peer_prev_seqno, peer_cur_seqno; /* setup header and trailer */ hi = (struct rip_auth_rte *) &packet->rte; packet_len = ntohs (hi->u.hash_info.packet_len); hd = (struct rip_auth_rte *) (((caddr_t) packet) + packet_len); if (IS_RIP_DEBUG_AUTH) zlog_debug ("declared main body length is %u", packet_len); /* check authentication data size */ if (IS_RIP_DEBUG_AUTH) zlog_debug ("interface configured for %s", LOOKUP (hash_algo_str, ri->hash_algo)); remote_dlen = hi->u.hash_info.auth_len; local_dlen = hash_digest_length[ri->hash_algo]; if ( local_dlen != remote_dlen && /* basic RFC constraint */ /* non-RFC MD5 special exception */ (ri->hash_algo != HASH_KEYED_MD5 || remote_dlen != RIP_AUTH_MD5_COMPAT_SIZE) ) { if (IS_RIP_DEBUG_AUTH) zlog_debug ("authentication data length mismatch: local %u, remote %u", local_dlen, remote_dlen); return 0; } /* check sequence number */ peer_prev_seqno = rip_peer_getseqno (from); peer_cur_seqno = ntohl (hi->u.hash_info.sequence); if (IS_RIP_DEBUG_AUTH) zlog_debug ("crypto sequence number for %s was %u, now %u", inet_ntoa (*from), peer_prev_seqno, peer_cur_seqno); if (peer_cur_seqno < peer_prev_seqno) { if (IS_RIP_DEBUG_AUTH) zlog_debug ("crypto sequence number check failed"); return 0; } /* pick local key */ if (ri->key_chain) { if ((keychain = keychain_lookup (ri->key_chain)) == NULL) { if (IS_RIP_DEBUG_AUTH) zlog_debug ("key chain '%s' is configured, but does not exist", ri->key_chain); return 0; } if (IS_RIP_DEBUG_AUTH) zlog_debug ("trying configured key chain '%s'", ri->key_chain); if ((key = key_lookup_for_accept (keychain, hi->u.hash_info.key_id)) == NULL) { if (IS_RIP_DEBUG_AUTH) zlog_debug ("key %u lookup failed", hi->u.hash_info.key_id); return 0; } if (IS_RIP_DEBUG_AUTH) zlog_debug ("using keychain '%s', key %u for receiving", ri->key_chain, key->index); auth_str = key->string; } else if (ri->auth_str) { if (IS_RIP_DEBUG_AUTH) zlog_debug ("using interface authentication string"); auth_str = ri->auth_str; } if (auth_str == NULL) { if (IS_RIP_DEBUG_AUTH) zlog_debug ("authentication string lookup failed"); return 0; } if (IS_RIP_DEBUG_AUTH) zlog_debug ("key is ready, calculating hash digest value %uB long", local_dlen); /* set aside, calculate and compare */ memcpy (received_digest, hd->u.hash_digest, local_dlen); switch (ri->hash_algo) { case HASH_KEYED_MD5: if (IS_RIP_DEBUG_AUTH) zlog_debug ("%s: %uB of input buffer, %zuB of key", __func__, packet_len + RIP_HEADER_SIZE, strlen (auth_str)); hash_error = hash_make_keyed_md5 ((caddr_t) packet, packet_len + RIP_HEADER_SIZE, auth_str, strlen (auth_str), local_digest); break; #ifdef HAVE_LIBGCRYPT case HASH_HMAC_SHA1: case HASH_HMAC_SHA256: case HASH_HMAC_SHA384: case HASH_HMAC_SHA512: { u_int8_t compr_auth_str[RIP_AUTH_MAX_SIZE]; size_t compr_authlen; hash_key_compress_rfc4822 (ri->hash_algo, auth_str, strlen (auth_str), compr_auth_str, &compr_authlen); /* RFC4822 2.5: Fill Apad, process whole packet with HMAC rounds. */ memcpy (hd->u.hash_digest, hash_apad_sha512, local_dlen); if (IS_RIP_DEBUG_AUTH) zlog_debug ("%s: %uB of input buffer, %zuB of key (%zuB compressed)", __func__, packet_len + 4 + local_dlen, strlen (auth_str), compr_authlen); hash_error = hash_make_hmac (ri->hash_algo, packet, packet_len + 4 + local_dlen, compr_auth_str, compr_authlen, local_digest); memcpy (hd->u.hash_digest, received_digest, local_dlen); break; } #endif /* HAVE_LIBGCRYPT */ default: assert (0); } if (hash_error) { if (IS_RIP_DEBUG_AUTH) zlog_debug ("hash function returned error %u", hash_error); return 0; } if (memcmp (local_digest, received_digest, local_dlen)) return 0; if (peer_cur_seqno > peer_prev_seqno) { if (IS_RIP_DEBUG_AUTH) zlog_debug ("updating crypto sequence number for %s from %u to %u", inet_ntoa (*from), peer_prev_seqno, peer_cur_seqno); rip_peer_setseqno (from, peer_cur_seqno); } return packet_len; }
/* Take a sequence of payload (routing) RTE structures, decide on particular * authentication required for the given interface and build a complete RIP * packet in a stream structure. The packet will consist of header, optional * heading RTE, the payload RTEs and optional trailing data. Return the stream. */ int rip_auth_make_packet ( struct rip_interface * ri, struct stream * packet, struct stream * rtes, const u_int8_t version, const u_int8_t command ) { struct key *key = NULL; char *auth_str = NULL; if (IS_RIP_DEBUG_AUTH) zlog_debug ("interface auth type is '%s', inet RTEs payload size is %zuB", LOOKUP (rip_ffff_type_str, ri->auth_type), stream_get_endp (rtes)); /* packet header, unconditional */ stream_reset (packet); stream_putc (packet, command); stream_putc (packet, version); stream_putw (packet, 0); /* authentication leading RTE, conditional */ if (version == RIPv2 && ri->auth_type != RIP_NO_AUTH) { if (ri->key_chain) { struct keychain *keychain; keychain = keychain_lookup (ri->key_chain); if (keychain) { if (IS_RIP_DEBUG_AUTH) zlog_debug ("trying configured key chain '%s'", ri->key_chain); key = key_lookup_for_send (keychain); } else { if (IS_RIP_DEBUG_AUTH) zlog_debug ("key chain '%s' is configured, but does not exist", ri->key_chain); } } /* Pick correct auth string for sends, prepare auth_str buffer for use. * (left justified and padded). * * presumes one of ri or key is valid, and that the auth strings they point * to are nul terminated. If neither are present, auth_str will be fully * zero padded. * */ if (key && key->string) { if (IS_RIP_DEBUG_AUTH) zlog_debug ("using keychain '%s', key %u for sending", ri->key_chain, key->index); auth_str = key->string; } else if (ri->auth_str) { if (IS_RIP_DEBUG_AUTH) zlog_debug ("using interface authentication string"); auth_str = ri->auth_str; } if (auth_str == NULL) { if (IS_RIP_DEBUG_AUTH) zlog_debug ("authentication string lookup failed"); return -1; } rip_auth_write_leading_rte (packet, ri, key ? key->index % 256 : 1, auth_str, RIP_HEADER_SIZE + RIP_RTE_SIZE + stream_get_endp (rtes)); } /* RTEs payload, unconditional */ if (stream_get_endp (rtes) % RIP_RTE_SIZE) { zlog_err ("%s: malformed input RTE buffer", __func__); return -1; } stream_write (packet, STREAM_DATA (rtes), stream_get_endp (rtes)); stream_reset (rtes); /* authentication trailing data, even more conditional */ if (version == RIPv2 && ri->auth_type == RIP_AUTH_HASH) rip_auth_write_trailer (packet, ri, auth_str); return 0; }