📄 dnskey.c
字号:
/* Find public key in DNS * Copyright (C) 2000-2002 D. Hugh Redelmeier. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * RCSID $Id: dnskey.c,v 1.84 2004/11/30 16:34:08 mcr Exp $ */#include <stdlib.h>#include <stddef.h>#include <string.h>#include <errno.h>#include <unistd.h>#include <fcntl.h>#include <sys/types.h>#include <sys/wait.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/nameser.h>#include <resolv.h>#include <netdb.h> /* ??? for h_errno */#include <sys/queue.h>#include <openswan.h>#include <openswan/ipsec_policy.h>#include "constants.h"#include "adns.h" /* needs <resolv.h> */#include "defs.h"#include "id.h"#include "log.h"#include "x509.h"#include "pgp.h"#include "certs.h"#include "smartcard.h"#ifdef XAUTH_USEPAM#include <security/pam_appl.h>#endif#include "connections.h" /* needs id.h */#include "keys.h" /* needs connections.h */#include "dnskey.h"#include "packet.h"#include "timer.h"#include "server.h"/* somebody has to decide */#define MAX_TXT_RDATA ((MAX_KEY_BYTES * 8 / 6) + 40) /* somewhat arbitrary overkill *//* ADNS stuff */int adns_qfd = NULL_FD, /* file descriptor for sending queries to adns (O_NONBLOCK) */ adns_afd = NULL_FD; /* file descriptor for receiving answers from adns */static pid_t adns_pid = 0;const char *pluto_adns_option = NULL; /* path from --pluto_adns */static int adns_in_flight = 0; /* queries outstanding */int adns_restart_count;#define ADNS_RESTART_MAX 20static void release_all_continuations(void);bool adns_reapchild(pid_t pid, int status UNUSED){ if(pid == adns_pid) { close_any(adns_qfd); adns_qfd = NULL_FD; close_any(adns_afd); adns_afd = NULL_FD; adns_pid = 0; if(adns_in_flight > 0) { release_all_continuations(); } passert(adns_in_flight == 0); return TRUE; } return FALSE;}voidinit_adns(void){ const char *adns_path = pluto_adns_option; const char *helper_bin_dir = getenv("IPSEC_EXECDIR");#ifndef USE_LWRES static const char adns_name[] = "_pluto_adns";#else /* USE_LWRES */ static const char adns_name[] = "lwdnsq";#endif /* USE_LWRES */ char adns_path_space[4096]; /* plenty long? */ int qfds[2]; int afds[2]; /* find a pathname to the ADNS program */ if (adns_path == NULL) { /* pathname was not specified as an option: build it. * First, figure out the directory to be used. */ ssize_t n; if (helper_bin_dir != NULL) { n = strlen(helper_bin_dir); if ((size_t)n <= sizeof(adns_path_space) - sizeof(adns_name)) { strcpy(adns_path_space, helper_bin_dir); if (n > 0 && adns_path_space[n -1] != '/') adns_path_space[n++] = '/'; } } else { /* The program will be in the same directory as Pluto, * so we use the sympolic link /proc/self/exe to * tell us of the path prefix. */ n = readlink("/proc/self/exe", adns_path_space, sizeof(adns_path_space)); if (n < 0) exit_log_errno((e , "readlink(\"/proc/self/exe\") failed in init_adns()")); } if ((size_t)n > sizeof(adns_path_space) - sizeof(adns_name)) exit_log("path to %s is too long", adns_name); while (n > 0 && adns_path_space[n - 1] != '/') n--; strcpy(adns_path_space + n, adns_name); adns_path = adns_path_space; } if (access(adns_path, X_OK) < 0) exit_log_errno((e, "%s missing or not executable", adns_path)); if (pipe(qfds) != 0 || pipe(afds) != 0) exit_log_errno((e, "pipe(2) failed in init_adns()")); adns_pid = fork(); switch (adns_pid) { case -1: exit_log_errno((e, "fork() failed in init_adns()")); case 0: /* child */ { /* Make stdin and stdout our pipes. * Take care to handle case where pipes already use these fds. */ if (afds[1] == 0) afds[1] = dup(afds[1]); /* avoid being overwritten */ if (qfds[0] != 0) { dup2(qfds[0], 0); close(qfds[0]); } if (afds[1] != 1) { dup2(afds[1], 1); close(qfds[1]); } if (afds[0] > 1) close(afds[0]); if (afds[1] > 1) close(afds[1]); DBG(DBG_DNS, execlp(adns_path, adns_name, "-d", NULL)); execlp(adns_path, adns_name, NULL); exit_log_errno((e, "execlp of %s failed", adns_path)); } default: /* parent */ close(qfds[0]); adns_qfd = qfds[1]; adns_afd = afds[0]; close(afds[1]); fcntl(adns_qfd, F_SETFL, O_NONBLOCK); break; }}voidstop_adns(void){ close_any(adns_qfd); adns_qfd = NULL_FD; close_any(adns_afd); adns_afd = NULL_FD; if (adns_pid != 0) { int status; pid_t p = waitpid(adns_pid, &status, 0); if (p == -1) { log_errno((e, "waitpid for ADNS process failed")); } else if (WIFEXITED(status)) { if (WEXITSTATUS(status) != 0) openswan_log("ADNS process exited with status %d" , (int) WEXITSTATUS(status)); adns_pid = 0; } else if (WIFSIGNALED(status)) { openswan_log("ADNS process terminated by signal %d", (int)WTERMSIG(status)); adns_pid = 0; } else { openswan_log("wait for end of ADNS process returned odd status 0x%x\n" , status); adns_pid = 0; } }}/* tricky macro to pass any hot potato */#define TRY(x) { err_t ugh = x; if (ugh != NULL) return ugh; }/* Process TXT X-IPsec-Server record, accumulating relevant ones * in cr->gateways_from_dns, a list sorted by "preference". * * Format of TXT record body: X-IPsec-Server ( nnn ) = iii kkk * nnn is a 16-bit unsigned integer preference * iii is @FQDN or dotted-decimal IPv4 address or colon-hex IPv6 address * kkk is an optional RSA public signing key in base 64. * * NOTE: we've got to be very wary of anything we find -- bad guys * might have prepared it. */#define our_TXT_attr_string "X-IPsec-Server"static const char our_TXT_attr[] = our_TXT_attr_string;static err_tdecode_iii(u_char **pp, struct id *gw_id){ u_char *p = *pp + strspn(*pp, " \t"); u_char *e = p + strcspn(p, " \t"); u_char under = *e; if (p == e) return "TXT " our_TXT_attr_string " badly formed (no gateway specified)"; *e = '\0'; if (*p == '@') { /* gateway specification in this record is @FQDN */ err_t ugh = atoid(p, gw_id, FALSE); if (ugh != NULL) return builddiag("malformed FQDN in TXT " our_TXT_attr_string ": %s" , ugh); } else { /* gateway specification is numeric */ ip_address ip; err_t ugh = tnatoaddr(p, e-p , strchr(p, ':') == NULL? AF_INET : AF_INET6 , &ip); if (ugh != NULL) return builddiag("malformed IP address in TXT " our_TXT_attr_string ": %s" , ugh); if (isanyaddr(&ip)) return "gateway address must not be 0.0.0.0 or 0::0"; iptoid(&ip, gw_id); } *e = under; *pp = e + strspn(e, " \t"); return NULL;}static err_tprocess_txt_rr_body(u_char *str, bool doit /* should we capture information? */, enum dns_auth_level dns_auth_level, struct adns_continuation *const cr){ const struct id *client_id = &cr->id; /* subject of query */ u_char *p = str; unsigned long pref = 0; struct gw_info gi; p += strspn(p, " \t"); /* ignore leading whitespace */ /* is this for us? */ if (strncasecmp(p, our_TXT_attr, sizeof(our_TXT_attr)-1) != 0) return NULL; /* neither interesting nor bad */ p += sizeof(our_TXT_attr) - 1; /* ignore our attribute name */ p += strspn(p, " \t"); /* ignore leading whitespace */ /* decode '(' nnn ')' */ if (*p != '(') return "X-IPsec-Server missing '('"; { char *e; p++; pref = strtoul(p, &e, 0); if ((u_char *)e == p) return "malformed X-IPsec-Server priority"; p = e + strspn(e, " \t"); if (*p != ')') return "X-IPsec-Server priority missing ')'"; p++; p += strspn(p, " \t"); if (pref > 0xFFFF) return "X-IPsec-Server priority larger than 0xFFFF"; } /* time for '=' */ if (*p != '=') return "X-IPsec-Server priority missing '='"; p++; p += strspn(p, " \t"); /* Decode iii (Security Gateway ID). */ zero(&gi); /* before first use */ TRY(decode_iii(&p, &gi.gw_id)); /* will need to unshare_id_content */ if (!cr->sgw_specified) { /* we don't know the peer's ID (because we are initiating * and we don't know who to initiate with. * So we're looking for gateway specs with an IP address */ if (!id_is_ipaddr(&gi.gw_id)) { DBG(DBG_DNS, { char cidb[IDTOA_BUF]; char gwidb[IDTOA_BUF]; idtoa(client_id, cidb, sizeof(cidb)); idtoa(&gi.gw_id, gwidb, sizeof(gwidb)); DBG_log("TXT %s record for %s: security gateway %s;" " ignored because gateway's IP is unspecified" , our_TXT_attr, cidb, gwidb); }); return NULL; /* we cannot use this record, but it isn't wrong */ } } else { /* We do know the peer's ID (because we are responding) * So we're looking for gateway specs specifying this known ID. */ const struct id *peer_id = &cr->sgw_id; if (!same_id(peer_id, &gi.gw_id)) { DBG(DBG_DNS, { char cidb[IDTOA_BUF]; char gwidb[IDTOA_BUF]; char pidb[IDTOA_BUF]; idtoa(client_id, cidb, sizeof(cidb)); idtoa(&gi.gw_id, gwidb, sizeof(gwidb)); idtoa(peer_id, pidb, sizeof(pidb)); DBG_log("TXT %s record for %s: security gateway %s;" " ignored -- looking to confirm %s as gateway" , our_TXT_attr, cidb, gwidb, pidb); }); return NULL; /* we cannot use this record, but it isn't wrong */ } } if (doit) { /* really accept gateway */ struct gw_info **gwip; /* gateway insertion point */ gi.client_id = *client_id; /* will need to unshare_id_content */ /* decode optional kkk: base 64 encoding of key */ gi.gw_key_present = *p != '\0'; if (gi.gw_key_present) { /* Decode base 64 encoding of key. * Similar code is in process_lwdnsq_key. */ u_char kb[RSA_MAX_ENCODING_BYTES]; /* plenty of space for binary form of public key */ chunk_t kbc; struct RSA_public_key r; err_t ugh = ttodatav(p, 0, 64, kb, sizeof(kb), &kbc.len , diag_space, sizeof(diag_space), TTODATAV_SPACECOUNTS); if (ugh != NULL) return builddiag("malformed key data: %s", ugh); if (kbc.len > sizeof(kb)) return builddiag("key data larger than %lu bytes" , (unsigned long) sizeof(kb)); kbc.ptr = kb; ugh = unpack_RSA_public_key(&r, &kbc); if (ugh != NULL) return builddiag("invalid key data: %s", ugh); /* now find a key entry to put it in */ gi.key = public_key_from_rsa(&r); free_RSA_public_content(&r); unreference_key(&cr->last_info); cr->last_info = reference_key(gi.key); } /* we're home free! Allocate everything and add to gateways list. */ gi.refcnt = 1; gi.pref = pref; gi.key->dns_auth_level = dns_auth_level; gi.key->last_tried_time = gi.key->last_worked_time = NO_TIME; /* find insertion point */ for (gwip = &cr->gateways_from_dns; *gwip != NULL && (*gwip)->pref < pref; gwip = &(*gwip)->next) ; DBG(DBG_DNS, { char cidb[IDTOA_BUF]; char gwidb[IDTOA_BUF]; idtoa(client_id, cidb, sizeof(cidb)); idtoa(&gi.gw_id, gwidb, sizeof(gwidb)); if (gi.gw_key_present) { DBG_log("gateway for %s is %s with key %s" , cidb, gwidb, gi.key->u.rsa.keyid); } else { DBG_log("gateway for %s is %s; no key specified" , cidb, gwidb); } }); gi.next = *gwip; *gwip = clone_thing(gi, "gateway info"); unshare_id_content(&(*gwip)->gw_id); unshare_id_content(&(*gwip)->client_id); } return NULL;}static const char *rr_typename(int type){ switch (type) { case T_TXT: return "TXT"; case T_KEY: return "KEY"; default: return "???"; }}#ifdef USE_LWRES# ifdef USE_KEYRRstatic err_t
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -