dnssec.c revision 1.10
1/* $OpenBSD: dnssec.c,v 1.10 2001/08/22 13:30:33 ho Exp $ */ 2 3/* 4 * Copyright (c) 2001 H�kan Olsson. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. The name of the author may not be used to endorse or promote products 15 * derived from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#include <sys/types.h> 30#include <netinet/in.h> 31#include <arpa/nameser.h> 32#include <arpa/inet.h> 33#include <stdlib.h> 34 35#include <openssl/rsa.h> 36 37#ifdef LWRES 38#include <lwres/netdb.h> 39#include <dns/keyvalues.h> 40#else 41#include <netdb.h> 42#endif 43 44#include "sysdep.h" 45 46#include "dnssec.h" 47#include "exchange.h" 48#include "ipsec_num.h" 49#include "libcrypto.h" 50#include "log.h" 51#include "message.h" 52#include "transport.h" 53#include "util.h" 54 55#ifndef DNS_UFQDN_SEPARATOR 56#define DNS_UFQDN_SEPARATOR "._ipsec." 57#endif 58 59/* adapted from <dns/rdatastruct.h> / RFC 2535 */ 60struct dns_rdata_key { 61 u_int16_t flags; 62 u_int8_t protocol; 63 u_int8_t algorithm; 64 u_int16_t datalen; 65 unsigned char *data; 66}; 67 68void * 69dns_get_key (int type, struct message *msg, int *keylen) 70{ 71 struct exchange *exchange = msg->exchange; 72 struct rrsetinfo *rr; 73 struct dns_rdata_key key_rr; 74 char name[MAXHOSTNAMELEN]; 75 in_addr_t ip4; 76 u_int8_t algorithm; 77 u_int8_t *id, *umark; 78 size_t id_len; 79 int ret, i; 80 81 switch (type) 82 { 83 case IKE_AUTH_RSA_SIG: 84 algorithm = DNS_KEYALG_RSA; 85 break; 86 87 case IKE_AUTH_RSA_ENC: 88 case IKE_AUTH_RSA_ENC_REV: 89 /* XXX Not yet. */ 90 /* algorithm = DNS_KEYALG_RSA; */ 91 return 0; 92 93 case IKE_AUTH_DSS: 94 /* XXX Not yet. */ 95 /* algorithm = DNS_KEYALG_DSS; */ 96 return 0; 97 98 case IKE_AUTH_PRE_SHARED: 99 default: 100 return 0; 101 } 102 103 id = exchange->initiator ? exchange->id_r : exchange->id_i; 104 id_len = exchange->initiator ? exchange->id_r_len : exchange->id_i_len; 105 memset (name, 0, MAXHOSTNAMELEN); 106 107 if (!id || id_len == 0) 108 { 109 log_print ("dns_get_key: ID is missing"); 110 return 0; 111 } 112 113 /* Exchanges (and SAs) don't carry the ID in ISAKMP form */ 114 id -= ISAKMP_GEN_SZ; 115 id_len += ISAKMP_GEN_SZ - ISAKMP_ID_DATA_OFF; 116 117 switch (GET_ISAKMP_ID_TYPE (id)) 118 { 119 case IPSEC_ID_IPV4_ADDR: 120 /* We want to lookup a KEY RR in the reverse zone. */ 121 if (id_len < sizeof ip4) 122 return 0; 123 memcpy (&ip4, id + ISAKMP_ID_DATA_OFF, sizeof ip4); 124 sprintf (name, "%d.%d.%d.%d.in-addr.arpa.", ip4 >> 24, 125 (ip4 >> 16) & 0xFF, (ip4 >> 8) & 0xFF, ip4 & 0xFF); 126 break; 127 128 case IPSEC_ID_IPV6_ADDR: 129 /* XXX Not yet. */ 130 return 0; 131 break; 132 133 case IPSEC_ID_FQDN: 134 if ((id_len + 1) >= MAXHOSTNAMELEN) 135 return 0; 136 /* ID is not NULL-terminated. Add trailing dot and terminate. */ 137 memcpy (name, id + ISAKMP_ID_DATA_OFF, id_len); 138 *(name + id_len) = '.'; 139 *(name + id_len + 1) = '\0'; 140 break; 141 142 case IPSEC_ID_USER_FQDN: 143 /* 144 * Some special handling here. We want to convert the ID 145 * 'user@host.domain' string into 'user._ipsec.host.domain.'. 146 */ 147 if ((id_len + sizeof (DNS_UFQDN_SEPARATOR)) >= MAXHOSTNAMELEN) 148 return 0; 149 /* Look for the '@' separator. */ 150 for (umark = id + ISAKMP_ID_DATA_OFF; (umark - id) < id_len; umark++) 151 if (*umark == '@') 152 break; 153 if (*umark != '@') 154 { 155 LOG_DBG((LOG_MISC, 50, "dns_get_key: bad UFQDN ID")); 156 return 0; 157 } 158 *umark++ = '\0'; 159 /* id is now terminated. 'umark', however, is not. */ 160 sprintf (name, "%s%s", id + ISAKMP_ID_DATA_OFF, DNS_UFQDN_SEPARATOR); 161 memcpy (name + strlen (name), umark, id_len - strlen (id) - 1); 162 *(name + id_len + sizeof (DNS_UFQDN_SEPARATOR) - 2) = '.'; 163 *(name + id_len + sizeof (DNS_UFQDN_SEPARATOR) - 1) = '\0'; 164 break; 165 166 default: 167 return 0; 168 } 169 170 LOG_DBG ((LOG_MISC, 50, "dns_get_key: trying KEY RR for %s", name)); 171 ret = getrrsetbyname (name, C_IN, T_KEY, 0, &rr); 172 173 if (ret) 174 { 175 LOG_DBG ((LOG_MISC, 30, "dns_get_key: no DNS responses (error %d)", 176 ret)); 177 return 0; 178 } 179 180 LOG_DBG ((LOG_MISC, 80, 181 "dns_get_key: rrset class %d type %d ttl %d nrdatas %d nrsigs %d", 182 rr->rri_rdclass, rr->rri_rdtype, rr->rri_ttl, rr->rri_nrdatas, 183 rr->rri_nsigs)); 184 185 /* We don't accept unvalidated data. */ 186 if (!(rr->rri_flags & RRSET_VALIDATED)) 187 { 188 LOG_DBG ((LOG_MISC, 10, "dns_get_key: got unvalidated response")); 189 freerrset (rr); 190 return 0; 191 } 192 193 /* Sanity. */ 194 if (rr->rri_nrdatas == 0 || rr->rri_rdtype != T_KEY) 195 { 196 LOG_DBG ((LOG_MISC, 30, "dns_get_key: no KEY RRs recieved")); 197 freerrset (rr); 198 return 0; 199 } 200 201 memset (&key_rr, 0, sizeof key_rr); 202 203 /* 204 * Find a key with the wanted algorithm, if any. 205 * XXX If there are several keys present, we currently only find the first. 206 */ 207 for (i = 0; i < rr->rri_nrdatas && key_rr.datalen == 0; i++) 208 { 209 key_rr.flags = ntohs ((u_int16_t) *rr->rri_rdatas[i].rdi_data); 210 key_rr.protocol = *(rr->rri_rdatas[i].rdi_data + 2); 211 key_rr.algorithm = *(rr->rri_rdatas[i].rdi_data + 3); 212 213 if (key_rr.protocol != DNS_KEYPROTO_IPSEC) 214 { 215 LOG_DBG ((LOG_MISC, 50, "dns_get_key: ignored non-IPsec key")); 216 continue; 217 } 218 219 if (key_rr.algorithm != algorithm) 220 { 221 LOG_DBG ((LOG_MISC, 50, "dns_get_key: ignored key with other alg")); 222 continue; 223 } 224 225 key_rr.datalen = rr->rri_rdatas[i].rdi_length - 4; 226 if (key_rr.datalen <= 0) 227 { 228 LOG_DBG ((LOG_MISC, 50, "dns_get_key: ignored bad key")); 229 key_rr.datalen = 0; 230 continue; 231 } 232 233 /* This key seems to fit our requirements... */ 234 key_rr.data = (char *)malloc (key_rr.datalen); 235 if (!key_rr.data) 236 { 237 log_error ("dns_get_key: malloc (%d) failed", key_rr.datalen); 238 freerrset (rr); 239 return 0; 240 } 241 memcpy (key_rr.data, rr->rri_rdatas[i].rdi_data + 4, key_rr.datalen); 242 *keylen = key_rr.datalen; 243 } 244 245 freerrset (rr); 246 247 if (key_rr.datalen) 248 return key_rr.data; 249 else 250 return 0; 251} 252 253int 254dns_RSA_dns_to_x509 (u_int8_t *key, int keylen, RSA **rsa_key) 255{ 256 RSA *rsa; 257 int key_offset; 258 u_int8_t e_len; 259 260 if (!key || keylen <= 0) 261 { 262 log_print ("dns_RSA_dns_to_x509: invalid public key"); 263 return -1; 264 } 265 266 rsa = LC (RSA_new, ()); 267 if (rsa == NULL) 268 { 269 log_error ("dns_RSA_dns_to_x509: failed to allocate new RSA struct"); 270 return -1; 271 } 272 273 e_len = *key; 274 key_offset = 1; 275 276 if (e_len == 0) 277 { 278 if (keylen < 3) 279 { 280 log_print ("dns_RSA_dns_to_x509: invalid public key"); 281 LC (RSA_free, (rsa)); 282 return -1; 283 } 284 e_len = *(key + key_offset++) << 8; 285 e_len += *(key + key_offset++); 286 } 287 288 if (e_len > (keylen - key_offset)) 289 { 290 log_print ("dns_RSA_dns_to_x509: invalid public key"); 291 LC (RSA_free, (rsa)); 292 return -1; 293 } 294 295 rsa->e = LC (BN_bin2bn, (key + key_offset, e_len, NULL)); 296 key_offset += e_len; 297 298 /* XXX if (keylen <= key_offset) -> "invalid public key" ? */ 299 300 rsa->n = LC (BN_bin2bn, (key + key_offset, keylen - key_offset, NULL)); 301 302 *rsa_key = rsa; 303 304 LOG_DBG ((LOG_MISC, 30, "dns_RSA_dns_to_x509: got %d bits RSA key", 305 LC (BN_num_bits, (rsa->n)))); 306 307 return 0; 308} 309 310#if notyet 311int 312dns_RSA_x509_to_dns (RSA *rsa_key, u_int8_t *key, int *keylen) 313{ 314 return 0; 315} 316#endif 317