/* - bundle - bundle e and n into an RFC2537-format lump * Note, calls hexOut. * * NOTE: returns a pointer into a STATIC buffer */ static const unsigned char *bundle(int e, SECItem *n, size_t *sizep) { const char *hexp = hexOut(n); static unsigned char bundbuf[2 + BYTES_FOR_BITS(MAXBITS)]; const char *er; size_t size; assert(e <= 255); bundbuf[0] = 1; bundbuf[1] = e; er = ttodata(hexp, 0, 0, (char *)bundbuf + 2, sizeof(bundbuf) - 2, &size); if (er != NULL) { fprintf(stderr, "%s: can't-happen bundle convert error `%s'\n", me, er); exit(1); } if (size > sizeof(bundbuf) - 2) { fprintf(stderr, "%s: can't-happen bundle overflow (need %d)\n", me, (int) size); exit(1); } if (sizep != NULL) *sizep = size + 2; return bundbuf; }
/* Convert textual form of id into a (temporary) struct id. * Note that if the id is to be kept, unshare_id_content will be necessary. */ err_t atoid(char *src, struct id *id, bool myid_ok) { err_t ugh = NULL; *id = empty_id; if (myid_ok && streq("%myid", src)) { id->kind = ID_MYID; } else if (streq("%fromcert", src)) { id->kind = ID_FROMCERT; } else if (streq("%none", src)) { id->kind = ID_NONE; } else if (strchr(src, '=') != NULL) { /* we interpret this as an ASCII X.501 ID_DER_ASN1_DN */ id->kind = ID_DER_ASN1_DN; id->name.ptr = temporary_cyclic_buffer(); /* assign temporary buffer */ id->name.len = 0; /* convert from LDAP style or openssl x509 -subject style to ASN.1 DN * discard optional @ character in front of DN */ ugh = atodn((*src == '@')?src+1:src, &id->name); } else if (strchr(src, '@') == NULL) { if (streq(src, "%any") || streq(src, "0.0.0.0")) { /* any ID will be accepted */ id->kind = ID_NONE; } else { /* !!! this test is not sufficient for distinguishing address families. * We need a notation to specify that a FQDN is to be resolved to IPv6. */ const struct af_info *afi = strchr(src, ':') == NULL ? &af_inet4_info: &af_inet6_info; id->kind = afi->id_addr; ugh = ttoaddr(src, 0, afi->af, &id->ip_addr); } } else { if (*src == '@') { if (*(src+1) == '#') { /* if there is a second specifier (#) on the line * we interprete this as ID_KEY_ID */ id->kind = ID_KEY_ID; id->name.ptr = (unsigned char *)src; /* discard @~, convert from hex to bin */ ugh = ttodata(src+2, 0, 16, (char *)id->name.ptr , strlen(src), &id->name.len); } else if (*(src+1) == '~') { /* if there is a second specifier (~) on the line * we interprete this as a binary ID_DER_ASN1_DN */ id->kind = ID_DER_ASN1_DN; id->name.ptr = (unsigned char *)src; /* discard @~, convert from hex to bin */ ugh = ttodata(src+2, 0, 16, (char *)id->name.ptr , strlen(src), &id->name.len); } else if (*(src+1) == '[') { /* if there is a second specifier ([) on the line * we interprete this as a text ID_KEY_ID, and we remove * a trailing ", if there is one. */ int len = strlen(src+2); id->kind = ID_KEY_ID; id->name.ptr = (unsigned char *)src+2; if(src[len+2]==']') { src[len+2-1]='\0'; len--; } id->name.len = len; } else { id->kind = ID_FQDN; id->name.ptr = (unsigned char *)src+1; /* discard @ */ id->name.len = strlen(src)-1; } } else { /* We leave in @, as per DOI 4.6.2.4 * (but DNS wants . instead). */ id->kind = ID_USER_FQDN; id->name.ptr = (unsigned char *)src; id->name.len = strlen(src); } } return ugh; }
/* Converts a PEM encoded file into its binary form * * RFC 1421 Privacy Enhancement for Electronic Mail, February 1993 * RFC 934 Message Encapsulation, January 1985 */ err_t pem_to_bin(chunk_t *blob, chunk_t *passphrase, bool *pgp) { typedef enum { PEM_PRE = 0, PEM_MSG = 1, PEM_HEADER = 2, PEM_BODY = 3, PEM_POST = 4, PEM_ABORT = 5 } state_t; encryption_algorithm_t alg = ENCR_UNDEFINED; size_t key_size = 0; bool encrypted = FALSE; state_t state = PEM_PRE; chunk_t src = *blob; chunk_t dst = *blob; chunk_t line = CHUNK_INITIALIZER; chunk_t iv = CHUNK_INITIALIZER; u_char iv_buf[16]; /* MD5 digest size */ /* zero size of converted blob */ dst.len = 0; /* zero size of IV */ iv.ptr = iv_buf; iv.len = 0; pem_init_logger(); while (fetchline(&src, &line)) { if (state == PEM_PRE) { if (find_boundary("BEGIN", &line)) { state = PEM_MSG; } continue; } else { if (find_boundary("END", &line)) { state = PEM_POST; break; } if (state == PEM_MSG) { state = (memchr(line.ptr, ':', line.len) == NULL) ? PEM_BODY : PEM_HEADER; } if (state == PEM_HEADER) { err_t ugh = NULL; chunk_t name = CHUNK_INITIALIZER; chunk_t value = CHUNK_INITIALIZER; /* an empty line separates HEADER and BODY */ if (line.len == 0) { state = PEM_BODY; continue; } /* we are looking for a parameter: value pair */ logger->log(logger, CONTROL|LEVEL2, " %.*s", (int)line.len, line.ptr); ugh = extract_parameter_value(&name, &value, &line); if (ugh != NULL) continue; if (match("Proc-Type", &name) && *value.ptr == '4') encrypted = TRUE; else if (match("DEK-Info", &name)) { size_t len = 0; chunk_t dek; if (!extract_token(&dek, ',', &value)) dek = value; /* we support DES-EDE3-CBC encrypted files, only */ if (match("DES-EDE3-CBC", &dek)) { alg = ENCR_3DES; key_size = 24; } else if (match("AES-128-CBC", &dek)) { alg = ENCR_AES_CBC; key_size = 16; } else if (match("AES-192-CBC", &dek)) { alg = ENCR_AES_CBC; key_size = 24; } else if (match("AES-256-CBC", &dek)) { alg = ENCR_AES_CBC; key_size = 32; } else { return "encryption algorithm not supported"; } eat_whitespace(&value); ugh = ttodata(value.ptr, value.len, 16, iv.ptr, 16, &len); if (ugh) return "error in IV"; iv.len = len; } } else /* state is PEM_BODY */ { const char *ugh = NULL; size_t len = 0; chunk_t data; /* remove any trailing whitespace */ if (!extract_token(&data ,' ', &line)) { data = line; } /* check for PGP armor checksum */ if (*data.ptr == '=') { *pgp = TRUE; data.ptr++; data.len--; logger->log(logger, CONTROL|LEVEL2, " Armor checksum: %.*s", (int)data.len, data.ptr); continue; } ugh = ttodata(data.ptr, data.len, 64, dst.ptr, blob->len - dst.len, &len); if (ugh) { state = PEM_ABORT; break; } else { dst.ptr += len; dst.len += len; } } } } /* set length to size of binary blob */ blob->len = dst.len; if (state != PEM_POST) return "file coded in unknown format, discarded"; return (encrypted)? pem_decrypt(blob, alg, key_size, &iv, passphrase) : NULL; }
/* * Converts a PEM encoded file into its binary form * * RFC 1421 Privacy Enhancement for Electronic Mail, February 1993 * RFC 934 Message Encapsulation, January 1985 * * We no longer support decrypting PEM files - those can only come in via NSS */ err_t pemtobin(chunk_t *blob) { typedef enum { PEM_PRE = 0, PEM_MSG = 1, PEM_HEADER = 2, PEM_BODY = 3, PEM_POST = 4, PEM_ABORT = 5 } state_t; state_t state = PEM_PRE; chunk_t src = *blob; chunk_t dst = *blob; chunk_t line = empty_chunk; /* zero size of converted blob */ dst.len = 0; while (fetchline(&src, &line)) { if (state == PEM_PRE) { if (find_boundary("BEGIN", &line)) { state = PEM_MSG; } continue; } else { if (find_boundary("END", &line)) { state = PEM_POST; break; } if (state == PEM_MSG) { state = (memchr(line.ptr, ':', line.len) == NULL) ? PEM_BODY : PEM_HEADER; } if (state == PEM_HEADER) { chunk_t name = empty_chunk; chunk_t value = empty_chunk; /* an empty line separates HEADER and BODY */ if (line.len == 0) { state = PEM_BODY; continue; } /* we are looking for a name: value pair */ if (!extract_parameter(&name, &value, &line)) continue; if (match("Proc-Type", &name) && *value.ptr == '4') return "Proc-Type: encrypted files no longer supported outside of the NSS database, please import these into NSS"; else if (match("DEK-Info", &name)) return "DEK-Info: encrypted files no longer supported outside of the NSS database, please import these into NSS"; } else { /* state is PEM_BODY */ const char *ugh = NULL; size_t len = 0; chunk_t data; /* remove any trailing whitespace */ if (!extract_token(&data, ' ', &line)) data = line; ugh = ttodata((char *)data.ptr, data.len, 64, (char *)dst.ptr, blob->len - dst.len, &len); if (ugh) { DBG(DBG_PARSING, DBG_log(" %s", ugh)); state = PEM_ABORT; break; } else { dst.ptr += len; dst.len += len; } } } } /* set length to size of binary blob */ blob->len = dst.len; if (state != PEM_POST) return "file coded in unknown format, discarded"; return NULL; }
static bool validate_end( #ifdef DNSSEC struct ub_ctx *dnsctx, #endif struct starter_conn *conn_st, struct starter_end *end, const char *leftright, bool resolvip UNUSED, err_t *perr) { err_t er = NULL; char *err_str = NULL; int family = conn_st->options[KBF_CONNADDRFAMILY]; bool err = FALSE; # define ERR_FOUND(...) { error_append(&err_str, __VA_ARGS__); err = TRUE; } if (!end->options_set[KNCF_IP]) conn_st->state = STATE_INCOMPLETE; end->addrtype = end->options[KNCF_IP]; end->addr_family = family; /* validate the KSCF_IP/KNCF_IP */ switch (end->addrtype) { case KH_ANY: anyaddr(family, &end->addr); break; case KH_IFACE: /* generally, this doesn't show up at this stage */ starter_log(LOG_LEVEL_DEBUG, "starter: %s is KH_IFACE", leftright); break; case KH_IPADDR: assert(end->strings[KSCF_IP] != NULL); if (end->strings[KSCF_IP][0] == '%') { pfree(end->iface); end->iface = clone_str(end->strings[KSCF_IP] + 1, "KH_IPADDR end->iface"); if (!starter_iface_find(end->iface, family, &end->addr, &end->nexthop)) conn_st->state = STATE_INVALID; /* not numeric, so set the type to the iface type */ end->addrtype = KH_IFACE; break; } er = ttoaddr_num(end->strings[KNCF_IP], 0, family, &end->addr); if (er != NULL) { /* not numeric, so set the type to the string type */ end->addrtype = KH_IPHOSTNAME; } if (end->id == NULL) { ipstr_buf b; end->id = clone_str(ipstr(&end->addr, &b), "end if"); } break; case KH_OPPO: conn_st->policy |= POLICY_OPPORTUNISTIC; break; case KH_OPPOGROUP: conn_st->policy |= POLICY_OPPORTUNISTIC | POLICY_GROUP; break; case KH_GROUP: conn_st->policy |= POLICY_GROUP; break; case KH_IPHOSTNAME: /* generally, this doesn't show up at this stage */ starter_log(LOG_LEVEL_DEBUG, "starter: %s is KH_IPHOSTNAME", leftright); break; case KH_DEFAULTROUTE: starter_log(LOG_LEVEL_DEBUG, "starter: %s is KH_DEFAULTROUTE", leftright); break; case KH_NOTSET: starter_log(LOG_LEVEL_DEBUG, "starter: %s is KH_NOTSET", leftright); break; } if (end->strings_set[KSCF_VTI_IP]) { char *value = end->strings[KSCF_VTI_IP]; if (strchr(value, '/') == NULL) { ERR_FOUND("%svti= needs address/mask", leftright); } else { /* * ttosubnet() helpfully sets the IP address to the lowest IP * in the subnet. Which is great for subnets but we want to * retain the specific IP in this case. * So we subsequently overwrite the IP address of the subnet. */ er = ttosubnet(value, 0, AF_UNSPEC, &end->vti_ip); if (er != NULL) { ERR_FOUND("bad addr %svti=%s [%s]", leftright, value, er); } else { er = tnatoaddr(value, strchr(value, '/') - value, AF_UNSPEC, &end->vti_ip.addr); if (er != NULL) { ERR_FOUND("bad addr in subnet for %svti=%s [%s]", leftright, value, er); } } } } /* validate the KSCF_SUBNET */ if (end->strings_set[KSCF_SUBNET]) { char *value = end->strings[KSCF_SUBNET]; if (end->strings_set[KSCF_ADDRESSPOOL]) { ERR_FOUND("cannot specify both %ssubnet= and %saddresspool=", leftright, leftright); } if (startswith(value, "vhost:") || startswith(value, "vnet:")) { er = NULL; end->virt = clone_str(value, "validate_end item"); } else { end->has_client = TRUE; er = ttosubnet(value, 0, AF_UNSPEC, &end->subnet); } if (er != NULL) ERR_FOUND("bad subnet %ssubnet=%s [%s]", leftright, value, er); } /* set nexthop address to something consistent, by default */ anyaddr(family, &end->nexthop); anyaddr(addrtypeof(&end->addr), &end->nexthop); /* validate the KSCF_NEXTHOP */ if (end->strings_set[KSCF_NEXTHOP]) { char *value = end->strings[KSCF_NEXTHOP]; if (strcaseeq(value, "%defaultroute")) { end->nexttype = KH_DEFAULTROUTE; } else { if (tnatoaddr(value, strlen(value), AF_UNSPEC, &end->nexthop) != NULL) { #ifdef DNSSEC starter_log(LOG_LEVEL_DEBUG, "Calling unbound_resolve() for %snexthop value", leftright); if (!unbound_resolve(dnsctx, value, strlen(value), AF_INET, &end->nexthop) && !unbound_resolve(dnsctx, value, strlen(value), AF_INET6, &end->nexthop)) ERR_FOUND("bad value for %snexthop=%s\n", leftright, value); #else er = ttoaddr(value, 0, AF_UNSPEC, &end->nexthop); if (er != NULL) ERR_FOUND("bad value for %snexthop=%s [%s]", leftright, value, er); #endif } end->nexttype = KH_IPADDR; } } else { #if 0 if (conn_st->policy & POLICY_OPPORTUNISTIC) end->nexttype = KH_DEFAULTROUTE; #endif anyaddr(family, &end->nexthop); if (end->addrtype == KH_DEFAULTROUTE) { end->nexttype = KH_DEFAULTROUTE; } } /* validate the KSCF_ID */ if (end->strings_set[KSCF_ID]) { char *value = end->strings[KSCF_ID]; pfreeany(end->id); end->id = clone_str(value, "end->id"); } if (end->options_set[KSCF_RSAKEY1]) { end->rsakey1_type = end->options[KSCF_RSAKEY1]; end->rsakey2_type = end->options[KSCF_RSAKEY2]; switch (end->options[KSCF_RSAKEY1]) { case PUBKEY_DNSONDEMAND: end->key_from_DNS_on_demand = TRUE; break; default: end->key_from_DNS_on_demand = FALSE; /* validate the KSCF_RSAKEY1/RSAKEY2 */ if (end->strings[KSCF_RSAKEY1] != NULL) { char *value = end->strings[KSCF_RSAKEY1]; pfreeany(end->rsakey1); end->rsakey1 = clone_str(value,"end->rsakey1"); } if (end->strings[KSCF_RSAKEY2] != NULL) { char *value = end->strings[KSCF_RSAKEY2]; pfreeany(end->rsakey2); end->rsakey2 = clone_str(value,"end->rsakey2"); } } } /* validate the KSCF_SOURCEIP, if any, and if set, * set the subnet to same value, if not set. */ if (end->strings_set[KSCF_SOURCEIP]) { char *value = end->strings[KSCF_SOURCEIP]; if (tnatoaddr(value, strlen(value), AF_UNSPEC, &end->sourceip) != NULL) { #ifdef DNSSEC starter_log(LOG_LEVEL_DEBUG, "Calling unbound_resolve() for %ssourceip value", leftright); if (!unbound_resolve(dnsctx, value, strlen(value), AF_INET, &end->sourceip) && !unbound_resolve(dnsctx, value, strlen(value), AF_INET6, &end->sourceip)) ERR_FOUND("bad value for %ssourceip=%s\n", leftright, value); #else er = ttoaddr(value, 0, AF_UNSPEC, &end->sourceip); if (er != NULL) ERR_FOUND("bad addr %ssourceip=%s [%s]", leftright, value, er); #endif } else { er = tnatoaddr(value, 0, AF_UNSPEC, &end->sourceip); if (er != NULL) ERR_FOUND("bad numerical addr %ssourceip=%s [%s]", leftright, value, er); } if (!end->has_client) { starter_log(LOG_LEVEL_INFO, "%ssourceip= used but not %ssubnet= defined, defaulting %ssubnet to %s", leftright, leftright, leftright, value); er = addrtosubnet(&end->sourceip, &end->subnet); if (er != NULL) { ERR_FOUND("attempt to default %ssubnet from %s failed: %s", leftright, value, er); } end->has_client = TRUE; end->has_client_wildcard = FALSE; } } /* copy certificate path name */ if (end->strings_set[KSCF_CERT] && end->strings_set[KSCF_CKAID]) { ERR_FOUND("only one of %scert and %sckaid can be specified", leftright, leftright); } if (end->strings_set[KSCF_CERT]) { end->cert = clone_str(end->strings[KSCF_CERT], "KSCF_CERT"); } if ( end->strings_set[KSCF_CKAID]) { const char *ckaid = end->strings[KSCF_CKAID]; /* try parsing it */ const char *ugh = ttodata(ckaid, 0, 16, NULL, 0, NULL); if (ugh != NULL) { ERR_FOUND("invalid %sckaid: %s", leftright, ugh); } end->ckaid = clone_str(ckaid, "KSCF_CKAID"); } if (end->strings_set[KSCF_CA]) end->ca = clone_str(end->strings[KSCF_CA], "KSCF_CA"); if (end->strings_set[KSCF_UPDOWN]) end->updown = clone_str(end->strings[KSCF_UPDOWN], "KSCF_UPDOWN"); if (end->strings_set[KSCF_PROTOPORT]) { err_t ugh; char *value = end->strings[KSCF_PROTOPORT]; ugh = ttoprotoport(value, 0, &end->protocol, &end->port, &end->has_port_wildcard); if (ugh != NULL) ERR_FOUND("bad %sprotoport=%s [%s]", leftright, value, ugh); } if (end->strings_set[KSCF_ADDRESSPOOL]) { char *addresspool = end->strings[KSCF_ADDRESSPOOL]; if (end->strings_set[KSCF_SUBNET]) ERR_FOUND("cannot specify both %ssubnet= and %saddresspool=", leftright, leftright); starter_log(LOG_LEVEL_DEBUG, "connection's %saddresspool set to: %s", leftright, end->strings[KSCF_ADDRESSPOOL] ); er = ttorange(addresspool, 0, AF_INET, &end->pool_range, TRUE); if (er != NULL) ERR_FOUND("bad %saddresspool=%s [%s]", leftright, addresspool, er); } if (end->options_set[KNCF_XAUTHSERVER] || end->options_set[KNCF_XAUTHCLIENT]) conn_st->policy |= POLICY_XAUTH; /* * KSCF_SOURCEIP = 16, */ if (err) *perr = err_str; return err; # undef ERR_FOUND }