/* $NetBSD: parse.y,v 1.14 2010/12/03 14:32:52 tteras Exp $ */ /* $KAME: parse.y,v 1.81 2003/07/01 04:01:48 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ %{ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include PATH_IPSEC_H #include #include #include #include #include #include #include #include #include "libpfkey.h" #include "vchar.h" #include "extern.h" #define DEFAULT_NATT_PORT 4500 #ifndef UDP_ENCAP_ESPINUDP #define UDP_ENCAP_ESPINUDP 2 #endif #define ATOX(c) \ (isdigit((int)c) ? (c - '0') : \ (isupper((int)c) ? (c - 'A' + 10) : (c - 'a' + 10))) #ifndef __rtems__ u_int32_t p_spi; u_int p_ext, p_alg_enc, p_alg_auth, p_replay, p_mode; u_int32_t p_reqid; u_int p_key_enc_len, p_key_auth_len; const char *p_key_enc; const char *p_key_auth; time_t p_lt_hard, p_lt_soft; size_t p_lb_hard, p_lb_soft; #else /* __rtems__ */ static u_int32_t p_spi; static u_int p_ext, p_alg_enc, p_alg_auth, p_replay, p_mode; static u_int32_t p_reqid; static u_int p_key_enc_len, p_key_auth_len; static const char *p_key_enc; static const char *p_key_auth; static time_t p_lt_hard, p_lt_soft; static size_t p_lb_hard, p_lb_soft; #endif /* __rtems__ */ struct security_ctx { u_int8_t doi; u_int8_t alg; u_int16_t len; char *buf; }; static struct security_ctx sec_ctx; static u_int p_natt_type; static struct addrinfo * p_natt_oa = NULL; static int p_aiflags = 0, p_aifamily = PF_UNSPEC; static struct addrinfo *parse_addr __P((char *, char *)); static int fix_portstr __P((int, vchar_t *, vchar_t *, vchar_t *)); static int setvarbuf __P((char *, int *, struct sadb_ext *, int, const void *, int)); void parse_init __P((void)); void free_buffer __P((void)); int setkeymsg0 __P((struct sadb_msg *, unsigned int, unsigned int, size_t)); static int setkeymsg_spdaddr __P((unsigned int, unsigned int, vchar_t *, struct addrinfo *, int, struct addrinfo *, int)); static int setkeymsg_spdaddr_tag __P((unsigned int, char *, vchar_t *)); static int setkeymsg_addr __P((unsigned int, unsigned int, struct addrinfo *, struct addrinfo *, int)); static int setkeymsg_add __P((unsigned int, unsigned int, struct addrinfo *, struct addrinfo *)); %} %union { int num; unsigned long ulnum; vchar_t val; struct addrinfo *res; } %token EOT SLASH BLCL ELCL %token ADD GET DELETE DELETEALL FLUSH DUMP EXIT %token PR_ESP PR_AH PR_IPCOMP PR_ESPUDP PR_TCP %token F_PROTOCOL F_AUTH F_ENC F_REPLAY F_COMP F_RAWCPI %token F_MODE MODE F_REQID %token F_EXT EXTENSION NOCYCLICSEQ %token ALG_AUTH ALG_AUTH_NOKEY %token ALG_ENC ALG_ENC_NOKEY ALG_ENC_DESDERIV ALG_ENC_DES32IV ALG_ENC_OLD %token ALG_COMP %token F_LIFETIME_HARD F_LIFETIME_SOFT %token F_LIFEBYTE_HARD F_LIFEBYTE_SOFT %token DECSTRING QUOTEDSTRING HEXSTRING STRING ANY /* SPD management */ %token SPDADD SPDUPDATE SPDDELETE SPDDUMP SPDFLUSH %token F_POLICY PL_REQUESTS %token F_AIFLAGS %token TAGGED %token SECURITY_CTX %type prefix protocol_spec upper_spec %type ALG_ENC ALG_ENC_DESDERIV ALG_ENC_DES32IV ALG_ENC_OLD ALG_ENC_NOKEY %type ALG_AUTH ALG_AUTH_NOKEY %type ALG_COMP %type PR_ESP PR_AH PR_IPCOMP PR_ESPUDP PR_TCP %type EXTENSION MODE %type DECSTRING %type PL_REQUESTS portstr key_string %type policy_requests %type QUOTEDSTRING HEXSTRING STRING %type F_AIFLAGS %type upper_misc_spec policy_spec %type ipaddr ipandport %% commands : /*NOTHING*/ | commands command { free_buffer(); parse_init(); } ; command : add_command | get_command | delete_command | deleteall_command | flush_command | dump_command | exit_command | spdadd_command | spdupdate_command | spddelete_command | spddump_command | spdflush_command ; /* commands concerned with management, there is in tail of this file. */ /* add command */ add_command : ADD ipaddropts ipandport ipandport protocol_spec spi extension_spec algorithm_spec EOT { int status; status = setkeymsg_add(SADB_ADD, $5, $3, $4); if (status < 0) return -1; } ; /* delete */ delete_command : DELETE ipaddropts ipandport ipandport protocol_spec spi extension_spec EOT { int status; if ($3->ai_next || $4->ai_next) { yyerror("multiple address specified"); return -1; } if (p_mode != IPSEC_MODE_ANY) yyerror("WARNING: mode is obsolete"); status = setkeymsg_addr(SADB_DELETE, $5, $3, $4, 0); if (status < 0) return -1; } ; /* deleteall command */ deleteall_command : DELETEALL ipaddropts ipaddr ipaddr protocol_spec EOT { #ifndef __linux__ if (setkeymsg_addr(SADB_DELETE, $5, $3, $4, 1) < 0) return -1; #else /* __linux__ */ /* linux strictly adheres to RFC2367, and returns * an error if we send an SADB_DELETE request without * an SPI. Therefore, we must first retrieve a list * of SPIs for all matching SADB entries, and then * delete each one separately. */ u_int32_t *spi; int i, n; spi = sendkeymsg_spigrep($5, $3, $4, &n); for (i = 0; i < n; i++) { p_spi = spi[i]; if (setkeymsg_addr(SADB_DELETE, $5, $3, $4, 0) < 0) return -1; } free(spi); #endif /* __linux__ */ } ; /* get command */ get_command : GET ipaddropts ipandport ipandport protocol_spec spi extension_spec EOT { int status; if (p_mode != IPSEC_MODE_ANY) yyerror("WARNING: mode is obsolete"); status = setkeymsg_addr(SADB_GET, $5, $3, $4, 0); if (status < 0) return -1; } ; /* flush */ flush_command : FLUSH protocol_spec EOT { struct sadb_msg msg; setkeymsg0(&msg, SADB_FLUSH, $2, sizeof(msg)); sendkeymsg((char *)&msg, sizeof(msg)); } ; /* dump */ dump_command : DUMP protocol_spec EOT { struct sadb_msg msg; setkeymsg0(&msg, SADB_DUMP, $2, sizeof(msg)); sendkeymsg((char *)&msg, sizeof(msg)); } ; protocol_spec : /*NOTHING*/ { $$ = SADB_SATYPE_UNSPEC; } | PR_ESP { $$ = SADB_SATYPE_ESP; if ($1 == 1) p_ext |= SADB_X_EXT_OLD; else p_ext &= ~SADB_X_EXT_OLD; } | PR_AH { $$ = SADB_SATYPE_AH; if ($1 == 1) p_ext |= SADB_X_EXT_OLD; else p_ext &= ~SADB_X_EXT_OLD; } | PR_IPCOMP { $$ = SADB_X_SATYPE_IPCOMP; } | PR_ESPUDP { $$ = SADB_SATYPE_ESP; p_ext &= ~SADB_X_EXT_OLD; p_natt_oa = 0; p_natt_type = UDP_ENCAP_ESPINUDP; } | PR_ESPUDP ipaddr { $$ = SADB_SATYPE_ESP; p_ext &= ~SADB_X_EXT_OLD; p_natt_oa = $2; p_natt_type = UDP_ENCAP_ESPINUDP; } | PR_TCP { #ifdef SADB_X_SATYPE_TCPSIGNATURE $$ = SADB_X_SATYPE_TCPSIGNATURE; #endif } ; spi : DECSTRING { p_spi = $1; } | HEXSTRING { char *ep; unsigned long v; ep = NULL; v = strtoul($1.buf, &ep, 16); if (!ep || *ep) { yyerror("invalid SPI"); return -1; } if (v & ~0xffffffff) { yyerror("SPI too big."); return -1; } p_spi = v; } ; algorithm_spec : esp_spec | ah_spec | ipcomp_spec ; esp_spec : F_ENC enc_alg F_AUTH auth_alg | F_ENC enc_alg ; ah_spec : F_AUTH auth_alg ; ipcomp_spec : F_COMP ALG_COMP { if ($2 < 0) { yyerror("unsupported algorithm"); return -1; } p_alg_enc = $2; } | F_COMP ALG_COMP F_RAWCPI { if ($2 < 0) { yyerror("unsupported algorithm"); return -1; } p_alg_enc = $2; p_ext |= SADB_X_EXT_RAWCPI; } ; enc_alg : ALG_ENC_NOKEY { if ($1 < 0) { yyerror("unsupported algorithm"); return -1; } p_alg_enc = $1; p_key_enc_len = 0; p_key_enc = ""; if (ipsec_check_keylen(SADB_EXT_SUPPORTED_ENCRYPT, p_alg_enc, PFKEY_UNUNIT64(p_key_enc_len)) < 0) { yyerror(ipsec_strerror()); return -1; } } | ALG_ENC key_string { if ($1 < 0) { yyerror("unsupported algorithm"); return -1; } p_alg_enc = $1; p_key_enc_len = $2.len; p_key_enc = $2.buf; if (ipsec_check_keylen(SADB_EXT_SUPPORTED_ENCRYPT, p_alg_enc, PFKEY_UNUNIT64(p_key_enc_len)) < 0) { yyerror(ipsec_strerror()); return -1; } } | ALG_ENC_OLD { if ($1 < 0) { yyerror("unsupported algorithm"); return -1; } yyerror("WARNING: obsolete algorithm"); p_alg_enc = $1; p_key_enc_len = 0; p_key_enc = ""; if (ipsec_check_keylen(SADB_EXT_SUPPORTED_ENCRYPT, p_alg_enc, PFKEY_UNUNIT64(p_key_enc_len)) < 0) { yyerror(ipsec_strerror()); return -1; } } | ALG_ENC_DESDERIV key_string { if ($1 < 0) { yyerror("unsupported algorithm"); return -1; } p_alg_enc = $1; if (p_ext & SADB_X_EXT_OLD) { yyerror("algorithm mismatched"); return -1; } p_ext |= SADB_X_EXT_DERIV; p_key_enc_len = $2.len; p_key_enc = $2.buf; if (ipsec_check_keylen(SADB_EXT_SUPPORTED_ENCRYPT, p_alg_enc, PFKEY_UNUNIT64(p_key_enc_len)) < 0) { yyerror(ipsec_strerror()); return -1; } } | ALG_ENC_DES32IV key_string { if ($1 < 0) { yyerror("unsupported algorithm"); return -1; } p_alg_enc = $1; if (!(p_ext & SADB_X_EXT_OLD)) { yyerror("algorithm mismatched"); return -1; } p_ext |= SADB_X_EXT_IV4B; p_key_enc_len = $2.len; p_key_enc = $2.buf; if (ipsec_check_keylen(SADB_EXT_SUPPORTED_ENCRYPT, p_alg_enc, PFKEY_UNUNIT64(p_key_enc_len)) < 0) { yyerror(ipsec_strerror()); return -1; } } ; auth_alg : ALG_AUTH key_string { if ($1 < 0) { yyerror("unsupported algorithm"); return -1; } p_alg_auth = $1; p_key_auth_len = $2.len; p_key_auth = $2.buf; #ifdef SADB_X_AALG_TCP_MD5 if (p_alg_auth == SADB_X_AALG_TCP_MD5) { if ((p_key_auth_len < 1) || (p_key_auth_len > 80)) return -1; } else #endif { if (ipsec_check_keylen(SADB_EXT_SUPPORTED_AUTH, p_alg_auth, PFKEY_UNUNIT64(p_key_auth_len)) < 0) { yyerror(ipsec_strerror()); return -1; } } } | ALG_AUTH_NOKEY { if ($1 < 0) { yyerror("unsupported algorithm"); return -1; } p_alg_auth = $1; p_key_auth_len = 0; p_key_auth = NULL; } ; key_string : QUOTEDSTRING { $$ = $1; } | HEXSTRING { caddr_t pp_key; caddr_t bp; caddr_t yp = $1.buf; int l; l = strlen(yp) % 2 + strlen(yp) / 2; if ((pp_key = malloc(l)) == 0) { yyerror("not enough core"); return -1; } memset(pp_key, 0, l); bp = pp_key; if (strlen(yp) % 2) { *bp = ATOX(yp[0]); yp++, bp++; } while (*yp) { *bp = (ATOX(yp[0]) << 4) | ATOX(yp[1]); yp += 2, bp++; } $$.len = l; $$.buf = pp_key; } ; extension_spec : /*NOTHING*/ | extension_spec extension ; extension : F_EXT EXTENSION { p_ext |= $2; } | F_EXT NOCYCLICSEQ { p_ext &= ~SADB_X_EXT_CYCSEQ; } | F_MODE MODE { p_mode = $2; } | F_MODE ANY { p_mode = IPSEC_MODE_ANY; } | F_REQID DECSTRING { p_reqid = $2; } | F_REPLAY DECSTRING { if ((p_ext & SADB_X_EXT_OLD) != 0) { yyerror("replay prevention cannot be used with " "ah/esp-old"); return -1; } p_replay = $2; } | F_LIFETIME_HARD DECSTRING { p_lt_hard = $2; } | F_LIFETIME_SOFT DECSTRING { p_lt_soft = $2; } | F_LIFEBYTE_HARD DECSTRING { p_lb_hard = $2; } | F_LIFEBYTE_SOFT DECSTRING { p_lb_soft = $2; } | SECURITY_CTX DECSTRING DECSTRING QUOTEDSTRING { sec_ctx.doi = $2; sec_ctx.alg = $3; sec_ctx.len = $4.len+1; sec_ctx.buf = $4.buf; } ; /* definition about command for SPD management */ /* spdadd */ spdadd_command /* XXX merge with spdupdate ??? */ : SPDADD ipaddropts STRING prefix portstr STRING prefix portstr upper_spec upper_misc_spec context_spec policy_spec EOT { int status; struct addrinfo *src, *dst; #ifdef HAVE_PFKEY_POLICY_PRIORITY last_msg_type = SADB_X_SPDADD; #endif /* fixed port fields if ulp is icmp */ if (fix_portstr($9, &$10, &$5, &$8)) return -1; src = parse_addr($3.buf, $5.buf); dst = parse_addr($6.buf, $8.buf); if (!src || !dst) { /* yyerror is already called */ return -1; } if (src->ai_next || dst->ai_next) { yyerror("multiple address specified"); freeaddrinfo(src); freeaddrinfo(dst); return -1; } status = setkeymsg_spdaddr(SADB_X_SPDADD, $9, &$12, src, $4, dst, $7); freeaddrinfo(src); freeaddrinfo(dst); if (status < 0) return -1; } | SPDADD TAGGED QUOTEDSTRING policy_spec EOT { int status; status = setkeymsg_spdaddr_tag(SADB_X_SPDADD, $3.buf, &$4); if (status < 0) return -1; } ; spdupdate_command /* XXX merge with spdadd ??? */ : SPDUPDATE ipaddropts STRING prefix portstr STRING prefix portstr upper_spec upper_misc_spec context_spec policy_spec EOT { int status; struct addrinfo *src, *dst; #ifdef HAVE_PFKEY_POLICY_PRIORITY last_msg_type = SADB_X_SPDUPDATE; #endif /* fixed port fields if ulp is icmp */ if (fix_portstr($9, &$10, &$5, &$8)) return -1; src = parse_addr($3.buf, $5.buf); dst = parse_addr($6.buf, $8.buf); if (!src || !dst) { /* yyerror is already called */ return -1; } if (src->ai_next || dst->ai_next) { yyerror("multiple address specified"); freeaddrinfo(src); freeaddrinfo(dst); return -1; } status = setkeymsg_spdaddr(SADB_X_SPDUPDATE, $9, &$12, src, $4, dst, $7); freeaddrinfo(src); freeaddrinfo(dst); if (status < 0) return -1; } | SPDUPDATE TAGGED QUOTEDSTRING policy_spec EOT { int status; status = setkeymsg_spdaddr_tag(SADB_X_SPDUPDATE, $3.buf, &$4); if (status < 0) return -1; } ; spddelete_command : SPDDELETE ipaddropts STRING prefix portstr STRING prefix portstr upper_spec upper_misc_spec context_spec policy_spec EOT { int status; struct addrinfo *src, *dst; /* fixed port fields if ulp is icmp */ if (fix_portstr($9, &$10, &$5, &$8)) return -1; src = parse_addr($3.buf, $5.buf); dst = parse_addr($6.buf, $8.buf); if (!src || !dst) { /* yyerror is already called */ return -1; } if (src->ai_next || dst->ai_next) { yyerror("multiple address specified"); freeaddrinfo(src); freeaddrinfo(dst); return -1; } status = setkeymsg_spdaddr(SADB_X_SPDDELETE, $9, &$12, src, $4, dst, $7); freeaddrinfo(src); freeaddrinfo(dst); if (status < 0) return -1; } ; spddump_command: SPDDUMP EOT { struct sadb_msg msg; setkeymsg0(&msg, SADB_X_SPDDUMP, SADB_SATYPE_UNSPEC, sizeof(msg)); sendkeymsg((char *)&msg, sizeof(msg)); } ; spdflush_command : SPDFLUSH EOT { struct sadb_msg msg; setkeymsg0(&msg, SADB_X_SPDFLUSH, SADB_SATYPE_UNSPEC, sizeof(msg)); sendkeymsg((char *)&msg, sizeof(msg)); } ; ipaddropts : /* nothing */ | ipaddropts ipaddropt ; ipaddropt : F_AIFLAGS { char *p; for (p = $1.buf + 1; *p; p++) switch (*p) { case '4': p_aifamily = AF_INET; break; #ifdef INET6 case '6': p_aifamily = AF_INET6; break; #endif case 'n': p_aiflags = AI_NUMERICHOST; break; default: yyerror("invalid flag"); return -1; } } ; ipaddr : STRING { $$ = parse_addr($1.buf, NULL); if ($$ == NULL) { /* yyerror already called by parse_addr */ return -1; } } ; ipandport : STRING { $$ = parse_addr($1.buf, NULL); if ($$ == NULL) { /* yyerror already called by parse_addr */ return -1; } } | STRING portstr { $$ = parse_addr($1.buf, $2.buf); if ($$ == NULL) { /* yyerror already called by parse_addr */ return -1; } } ; prefix : /*NOTHING*/ { $$ = -1; } | SLASH DECSTRING { $$ = $2; } ; portstr : /*NOTHING*/ { $$.buf = strdup("0"); if (!$$.buf) { yyerror("insufficient memory"); return -1; } $$.len = strlen($$.buf); } | BLCL ANY ELCL { $$.buf = strdup("0"); if (!$$.buf) { yyerror("insufficient memory"); return -1; } $$.len = strlen($$.buf); } | BLCL DECSTRING ELCL { char buf[20]; snprintf(buf, sizeof(buf), "%lu", $2); $$.buf = strdup(buf); if (!$$.buf) { yyerror("insufficient memory"); return -1; } $$.len = strlen($$.buf); } | BLCL STRING ELCL { $$ = $2; } ; upper_spec : DECSTRING { $$ = $1; } | ANY { $$ = IPSEC_ULPROTO_ANY; } | PR_TCP { $$ = IPPROTO_TCP; } | STRING { struct protoent *ent; ent = getprotobyname($1.buf); if (ent) $$ = ent->p_proto; else { if (strcmp("icmp6", $1.buf) == 0) { $$ = IPPROTO_ICMPV6; } else if(strcmp("ip4", $1.buf) == 0) { $$ = IPPROTO_IPV4; } else { yyerror("invalid upper layer protocol"); return -1; } } endprotoent(); } ; upper_misc_spec : /*NOTHING*/ { $$.buf = NULL; $$.len = 0; } | STRING { $$.buf = strdup($1.buf); if (!$$.buf) { yyerror("insufficient memory"); return -1; } $$.len = strlen($$.buf); } ; context_spec : /* NOTHING */ | SECURITY_CTX DECSTRING DECSTRING QUOTEDSTRING { sec_ctx.doi = $2; sec_ctx.alg = $3; sec_ctx.len = $4.len+1; sec_ctx.buf = $4.buf; } ; policy_spec : F_POLICY policy_requests { char *policy; #ifdef HAVE_PFKEY_POLICY_PRIORITY struct sadb_x_policy *xpl; #endif policy = ipsec_set_policy($2.buf, $2.len); if (policy == NULL) { yyerror(ipsec_strerror()); return -1; } $$.buf = policy; $$.len = ipsec_get_policylen(policy); #ifdef HAVE_PFKEY_POLICY_PRIORITY xpl = (struct sadb_x_policy *) $$.buf; last_priority = xpl->sadb_x_policy_priority; #endif } ; policy_requests : PL_REQUESTS { $$ = $1; } ; /* exit */ exit_command : EXIT EOT { exit_now = 1; YYACCEPT; } ; %% int setkeymsg0(msg, type, satype, l) struct sadb_msg *msg; unsigned int type; unsigned int satype; size_t l; { msg->sadb_msg_version = PF_KEY_V2; msg->sadb_msg_type = type; msg->sadb_msg_errno = 0; msg->sadb_msg_satype = satype; msg->sadb_msg_reserved = 0; msg->sadb_msg_seq = 0; msg->sadb_msg_pid = getpid(); msg->sadb_msg_len = PFKEY_UNIT64(l); return 0; } /* XXX NO BUFFER OVERRUN CHECK! BAD BAD! */ static int setkeymsg_spdaddr(type, upper, policy, srcs, splen, dsts, dplen) unsigned int type; unsigned int upper; vchar_t *policy; struct addrinfo *srcs; int splen; struct addrinfo *dsts; int dplen; { struct sadb_msg *msg; char buf[BUFSIZ]; int l, l0; struct sadb_address m_addr; struct addrinfo *s, *d; int n; int plen; struct sockaddr *sa; int salen; struct sadb_x_policy *sp; #ifdef HAVE_POLICY_FWD struct sadb_x_ipsecrequest *ps = NULL; int saved_level, saved_id = 0; #endif msg = (struct sadb_msg *)buf; if (!srcs || !dsts) return -1; /* fix up length afterwards */ setkeymsg0(msg, type, SADB_SATYPE_UNSPEC, 0); l = sizeof(struct sadb_msg); sp = (struct sadb_x_policy*) (buf + l); memcpy(buf + l, policy->buf, policy->len); l += policy->len; l0 = l; n = 0; /* do it for all src/dst pairs */ for (s = srcs; s; s = s->ai_next) { for (d = dsts; d; d = d->ai_next) { /* rewind pointer */ l = l0; if (s->ai_addr->sa_family != d->ai_addr->sa_family) continue; switch (s->ai_addr->sa_family) { case AF_INET: plen = sizeof(struct in_addr) << 3; break; #ifdef INET6 case AF_INET6: plen = sizeof(struct in6_addr) << 3; break; #endif default: continue; } /* set src */ sa = s->ai_addr; salen = sysdep_sa_len(s->ai_addr); m_addr.sadb_address_len = PFKEY_UNIT64(sizeof(m_addr) + PFKEY_ALIGN8(salen)); m_addr.sadb_address_exttype = SADB_EXT_ADDRESS_SRC; m_addr.sadb_address_proto = upper; m_addr.sadb_address_prefixlen = (splen >= 0 ? splen : plen); m_addr.sadb_address_reserved = 0; setvarbuf(buf, &l, (struct sadb_ext *)&m_addr, sizeof(m_addr), (caddr_t)sa, salen); /* set dst */ sa = d->ai_addr; salen = sysdep_sa_len(d->ai_addr); m_addr.sadb_address_len = PFKEY_UNIT64(sizeof(m_addr) + PFKEY_ALIGN8(salen)); m_addr.sadb_address_exttype = SADB_EXT_ADDRESS_DST; m_addr.sadb_address_proto = upper; m_addr.sadb_address_prefixlen = (dplen >= 0 ? dplen : plen); m_addr.sadb_address_reserved = 0; setvarbuf(buf, &l, (struct sadb_ext *)&m_addr, sizeof(m_addr), sa, salen); #ifdef SADB_X_EXT_SEC_CTX /* Add security context label */ if (sec_ctx.doi) { struct sadb_x_sec_ctx m_sec_ctx; u_int slen = sizeof(struct sadb_x_sec_ctx); memset(&m_sec_ctx, 0, slen); m_sec_ctx.sadb_x_sec_len = PFKEY_UNIT64(slen + PFKEY_ALIGN8(sec_ctx.len)); m_sec_ctx.sadb_x_sec_exttype = SADB_X_EXT_SEC_CTX; m_sec_ctx.sadb_x_ctx_len = sec_ctx.len;/*bytes*/ m_sec_ctx.sadb_x_ctx_doi = sec_ctx.doi; m_sec_ctx.sadb_x_ctx_alg = sec_ctx.alg; setvarbuf(buf, &l, (struct sadb_ext *)&m_sec_ctx, slen, (caddr_t)sec_ctx.buf, sec_ctx.len); } #endif msg->sadb_msg_len = PFKEY_UNIT64(l); sendkeymsg(buf, l); #ifdef HAVE_POLICY_FWD /* create extra call for FWD policy */ if (f_rfcmode && sp->sadb_x_policy_dir == IPSEC_DIR_INBOUND) { sp->sadb_x_policy_dir = IPSEC_DIR_FWD; ps = (struct sadb_x_ipsecrequest*) (sp+1); /* if request level is unique, change it to * require for fwd policy */ /* XXX: currently, only first policy is updated * only. Update following too... */ saved_level = ps->sadb_x_ipsecrequest_level; if (saved_level == IPSEC_LEVEL_UNIQUE) { saved_id = ps->sadb_x_ipsecrequest_reqid; ps->sadb_x_ipsecrequest_reqid=0; ps->sadb_x_ipsecrequest_level=IPSEC_LEVEL_REQUIRE; } sendkeymsg(buf, l); /* restoring for next message */ sp->sadb_x_policy_dir = IPSEC_DIR_INBOUND; if (saved_level == IPSEC_LEVEL_UNIQUE) { ps->sadb_x_ipsecrequest_reqid = saved_id; ps->sadb_x_ipsecrequest_level = saved_level; } } #endif n++; } } if (n == 0) return -1; else return 0; } static int setkeymsg_spdaddr_tag(type, tag, policy) unsigned int type; char *tag; vchar_t *policy; { struct sadb_msg *msg; char buf[BUFSIZ]; int l, l0; #ifdef SADB_X_EXT_TAG struct sadb_x_tag m_tag; #endif int n; msg = (struct sadb_msg *)buf; /* fix up length afterwards */ setkeymsg0(msg, type, SADB_SATYPE_UNSPEC, 0); l = sizeof(struct sadb_msg); memcpy(buf + l, policy->buf, policy->len); l += policy->len; l0 = l; n = 0; #ifdef SADB_X_EXT_TAG memset(&m_tag, 0, sizeof(m_tag)); m_tag.sadb_x_tag_len = PFKEY_UNIT64(sizeof(m_tag)); m_tag.sadb_x_tag_exttype = SADB_X_EXT_TAG; if (strlcpy(m_tag.sadb_x_tag_name, tag, sizeof(m_tag.sadb_x_tag_name)) >= sizeof(m_tag.sadb_x_tag_name)) return -1; memcpy(buf + l, &m_tag, sizeof(m_tag)); l += sizeof(m_tag); #endif msg->sadb_msg_len = PFKEY_UNIT64(l); sendkeymsg(buf, l); return 0; } /* XXX NO BUFFER OVERRUN CHECK! BAD BAD! */ static int setkeymsg_addr(type, satype, srcs, dsts, no_spi) unsigned int type; unsigned int satype; struct addrinfo *srcs; struct addrinfo *dsts; int no_spi; { struct sadb_msg *msg; char buf[BUFSIZ]; int l, l0, len; struct sadb_sa m_sa; struct sadb_x_sa2 m_sa2; struct sadb_address m_addr; struct addrinfo *s, *d; int n; int plen; struct sockaddr *sa; int salen; msg = (struct sadb_msg *)buf; if (!srcs || !dsts) return -1; /* fix up length afterwards */ setkeymsg0(msg, type, satype, 0); l = sizeof(struct sadb_msg); if (!no_spi) { len = sizeof(struct sadb_sa); m_sa.sadb_sa_len = PFKEY_UNIT64(len); m_sa.sadb_sa_exttype = SADB_EXT_SA; m_sa.sadb_sa_spi = htonl(p_spi); m_sa.sadb_sa_replay = p_replay; m_sa.sadb_sa_state = 0; m_sa.sadb_sa_auth = p_alg_auth; m_sa.sadb_sa_encrypt = p_alg_enc; m_sa.sadb_sa_flags = p_ext; memcpy(buf + l, &m_sa, len); l += len; len = sizeof(struct sadb_x_sa2); m_sa2.sadb_x_sa2_len = PFKEY_UNIT64(len); m_sa2.sadb_x_sa2_exttype = SADB_X_EXT_SA2; m_sa2.sadb_x_sa2_mode = p_mode; m_sa2.sadb_x_sa2_reqid = p_reqid; memcpy(buf + l, &m_sa2, len); l += len; } l0 = l; n = 0; /* do it for all src/dst pairs */ for (s = srcs; s; s = s->ai_next) { for (d = dsts; d; d = d->ai_next) { /* rewind pointer */ l = l0; if (s->ai_addr->sa_family != d->ai_addr->sa_family) continue; switch (s->ai_addr->sa_family) { case AF_INET: plen = sizeof(struct in_addr) << 3; break; #ifdef INET6 case AF_INET6: plen = sizeof(struct in6_addr) << 3; break; #endif default: continue; } /* set src */ sa = s->ai_addr; salen = sysdep_sa_len(s->ai_addr); m_addr.sadb_address_len = PFKEY_UNIT64(sizeof(m_addr) + PFKEY_ALIGN8(salen)); m_addr.sadb_address_exttype = SADB_EXT_ADDRESS_SRC; m_addr.sadb_address_proto = IPSEC_ULPROTO_ANY; m_addr.sadb_address_prefixlen = plen; m_addr.sadb_address_reserved = 0; setvarbuf(buf, &l, (struct sadb_ext *)&m_addr, sizeof(m_addr), sa, salen); /* set dst */ sa = d->ai_addr; salen = sysdep_sa_len(d->ai_addr); m_addr.sadb_address_len = PFKEY_UNIT64(sizeof(m_addr) + PFKEY_ALIGN8(salen)); m_addr.sadb_address_exttype = SADB_EXT_ADDRESS_DST; m_addr.sadb_address_proto = IPSEC_ULPROTO_ANY; m_addr.sadb_address_prefixlen = plen; m_addr.sadb_address_reserved = 0; setvarbuf(buf, &l, (struct sadb_ext *)&m_addr, sizeof(m_addr), sa, salen); msg->sadb_msg_len = PFKEY_UNIT64(l); sendkeymsg(buf, l); n++; } } if (n == 0) return -1; else return 0; } #ifdef SADB_X_EXT_NAT_T_TYPE static u_int16_t get_port (struct addrinfo *addr) { struct sockaddr *s = addr->ai_addr; u_int16_t port = 0; switch (s->sa_family) { case AF_INET: { struct sockaddr_in *sin4 = (struct sockaddr_in *)s; port = ntohs(sin4->sin_port); break; } case AF_INET6: { struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)s; port = ntohs(sin6->sin6_port); break; } } if (port == 0) port = DEFAULT_NATT_PORT; return port; } #endif /* XXX NO BUFFER OVERRUN CHECK! BAD BAD! */ static int setkeymsg_add(type, satype, srcs, dsts) unsigned int type; unsigned int satype; struct addrinfo *srcs; struct addrinfo *dsts; { struct sadb_msg *msg; char buf[BUFSIZ]; int l, l0, len; struct sadb_sa m_sa; struct sadb_x_sa2 m_sa2; struct sadb_address m_addr; struct addrinfo *s, *d; int n; int plen; struct sockaddr *sa; int salen; msg = (struct sadb_msg *)buf; if (!srcs || !dsts) return -1; /* fix up length afterwards */ setkeymsg0(msg, type, satype, 0); l = sizeof(struct sadb_msg); /* set encryption algorithm, if present. */ if (satype != SADB_X_SATYPE_IPCOMP && p_key_enc) { union { struct sadb_key key; struct sadb_ext ext; } m; m.key.sadb_key_len = PFKEY_UNIT64(sizeof(m.key) + PFKEY_ALIGN8(p_key_enc_len)); m.key.sadb_key_exttype = SADB_EXT_KEY_ENCRYPT; m.key.sadb_key_bits = p_key_enc_len * 8; m.key.sadb_key_reserved = 0; setvarbuf(buf, &l, &m.ext, sizeof(m.key), p_key_enc, p_key_enc_len); } /* set authentication algorithm, if present. */ if (p_key_auth) { union { struct sadb_key key; struct sadb_ext ext; } m; m.key.sadb_key_len = PFKEY_UNIT64(sizeof(m.key) + PFKEY_ALIGN8(p_key_auth_len)); m.key.sadb_key_exttype = SADB_EXT_KEY_AUTH; m.key.sadb_key_bits = p_key_auth_len * 8; m.key.sadb_key_reserved = 0; setvarbuf(buf, &l, &m.ext, sizeof(m.key), p_key_auth, p_key_auth_len); } /* set lifetime for HARD */ if (p_lt_hard != 0 || p_lb_hard != 0) { struct sadb_lifetime m_lt; u_int slen = sizeof(struct sadb_lifetime); m_lt.sadb_lifetime_len = PFKEY_UNIT64(slen); m_lt.sadb_lifetime_exttype = SADB_EXT_LIFETIME_HARD; m_lt.sadb_lifetime_allocations = 0; m_lt.sadb_lifetime_bytes = p_lb_hard; m_lt.sadb_lifetime_addtime = p_lt_hard; m_lt.sadb_lifetime_usetime = 0; memcpy(buf + l, &m_lt, slen); l += slen; } /* set lifetime for SOFT */ if (p_lt_soft != 0 || p_lb_soft != 0) { struct sadb_lifetime m_lt; u_int slen = sizeof(struct sadb_lifetime); m_lt.sadb_lifetime_len = PFKEY_UNIT64(slen); m_lt.sadb_lifetime_exttype = SADB_EXT_LIFETIME_SOFT; m_lt.sadb_lifetime_allocations = 0; m_lt.sadb_lifetime_bytes = p_lb_soft; m_lt.sadb_lifetime_addtime = p_lt_soft; m_lt.sadb_lifetime_usetime = 0; memcpy(buf + l, &m_lt, slen); l += slen; } #ifdef SADB_X_EXT_SEC_CTX /* Add security context label */ if (sec_ctx.doi) { struct sadb_x_sec_ctx m_sec_ctx; u_int slen = sizeof(struct sadb_x_sec_ctx); memset(&m_sec_ctx, 0, slen); m_sec_ctx.sadb_x_sec_len = PFKEY_UNIT64(slen + PFKEY_ALIGN8(sec_ctx.len)); m_sec_ctx.sadb_x_sec_exttype = SADB_X_EXT_SEC_CTX; m_sec_ctx.sadb_x_ctx_len = sec_ctx.len; /* bytes */ m_sec_ctx.sadb_x_ctx_doi = sec_ctx.doi; m_sec_ctx.sadb_x_ctx_alg = sec_ctx.alg; setvarbuf(buf, &l, (struct sadb_ext *)&m_sec_ctx, slen, (caddr_t)sec_ctx.buf, sec_ctx.len); } #endif len = sizeof(struct sadb_sa); m_sa.sadb_sa_len = PFKEY_UNIT64(len); m_sa.sadb_sa_exttype = SADB_EXT_SA; m_sa.sadb_sa_spi = htonl(p_spi); m_sa.sadb_sa_replay = p_replay; m_sa.sadb_sa_state = 0; m_sa.sadb_sa_auth = p_alg_auth; m_sa.sadb_sa_encrypt = p_alg_enc; m_sa.sadb_sa_flags = p_ext; memcpy(buf + l, &m_sa, len); l += len; len = sizeof(struct sadb_x_sa2); m_sa2.sadb_x_sa2_len = PFKEY_UNIT64(len); m_sa2.sadb_x_sa2_exttype = SADB_X_EXT_SA2; m_sa2.sadb_x_sa2_mode = p_mode; m_sa2.sadb_x_sa2_reqid = p_reqid; memcpy(buf + l, &m_sa2, len); l += len; #ifdef SADB_X_EXT_NAT_T_TYPE if (p_natt_type) { struct sadb_x_nat_t_type natt_type; len = sizeof(struct sadb_x_nat_t_type); memset(&natt_type, 0, len); natt_type.sadb_x_nat_t_type_len = PFKEY_UNIT64(len); natt_type.sadb_x_nat_t_type_exttype = SADB_X_EXT_NAT_T_TYPE; natt_type.sadb_x_nat_t_type_type = p_natt_type; memcpy(buf + l, &natt_type, len); l += len; if (p_natt_oa) { sa = p_natt_oa->ai_addr; switch (sa->sa_family) { case AF_INET: plen = sizeof(struct in_addr) << 3; break; #ifdef INET6 case AF_INET6: plen = sizeof(struct in6_addr) << 3; break; #endif default: return -1; } salen = sysdep_sa_len(sa); m_addr.sadb_address_len = PFKEY_UNIT64(sizeof(m_addr) + PFKEY_ALIGN8(salen)); m_addr.sadb_address_exttype = SADB_X_EXT_NAT_T_OA; m_addr.sadb_address_proto = IPSEC_ULPROTO_ANY; m_addr.sadb_address_prefixlen = plen; m_addr.sadb_address_reserved = 0; setvarbuf(buf, &l, (struct sadb_ext *)&m_addr, sizeof(m_addr), sa, salen); } } #endif l0 = l; n = 0; /* do it for all src/dst pairs */ for (s = srcs; s; s = s->ai_next) { for (d = dsts; d; d = d->ai_next) { /* rewind pointer */ l = l0; if (s->ai_addr->sa_family != d->ai_addr->sa_family) continue; switch (s->ai_addr->sa_family) { case AF_INET: plen = sizeof(struct in_addr) << 3; break; #ifdef INET6 case AF_INET6: plen = sizeof(struct in6_addr) << 3; break; #endif default: continue; } /* set src */ sa = s->ai_addr; salen = sysdep_sa_len(s->ai_addr); m_addr.sadb_address_len = PFKEY_UNIT64(sizeof(m_addr) + PFKEY_ALIGN8(salen)); m_addr.sadb_address_exttype = SADB_EXT_ADDRESS_SRC; m_addr.sadb_address_proto = IPSEC_ULPROTO_ANY; m_addr.sadb_address_prefixlen = plen; m_addr.sadb_address_reserved = 0; setvarbuf(buf, &l, (struct sadb_ext *)&m_addr, sizeof(m_addr), sa, salen); /* set dst */ sa = d->ai_addr; salen = sysdep_sa_len(d->ai_addr); m_addr.sadb_address_len = PFKEY_UNIT64(sizeof(m_addr) + PFKEY_ALIGN8(salen)); m_addr.sadb_address_exttype = SADB_EXT_ADDRESS_DST; m_addr.sadb_address_proto = IPSEC_ULPROTO_ANY; m_addr.sadb_address_prefixlen = plen; m_addr.sadb_address_reserved = 0; setvarbuf(buf, &l, (struct sadb_ext *)&m_addr, sizeof(m_addr), sa, salen); #ifdef SADB_X_EXT_NAT_T_TYPE if (p_natt_type) { struct sadb_x_nat_t_port natt_port; /* NATT_SPORT */ len = sizeof(struct sadb_x_nat_t_port); memset(&natt_port, 0, len); natt_port.sadb_x_nat_t_port_len = PFKEY_UNIT64(len); natt_port.sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_SPORT; natt_port.sadb_x_nat_t_port_port = htons(get_port(s)); memcpy(buf + l, &natt_port, len); l += len; /* NATT_DPORT */ natt_port.sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_DPORT; natt_port.sadb_x_nat_t_port_port = htons(get_port(d)); memcpy(buf + l, &natt_port, len); l += len; } #endif msg->sadb_msg_len = PFKEY_UNIT64(l); sendkeymsg(buf, l); n++; } } if (n == 0) return -1; else return 0; } static struct addrinfo * parse_addr(host, port) char *host; char *port; { struct addrinfo hints, *res = NULL; int error; memset(&hints, 0, sizeof(hints)); hints.ai_family = p_aifamily; hints.ai_socktype = SOCK_DGRAM; /*dummy*/ hints.ai_protocol = IPPROTO_UDP; /*dummy*/ hints.ai_flags = p_aiflags; error = getaddrinfo(host, port, &hints, &res); if (error != 0) { yyerror(gai_strerror(error)); return NULL; } return res; } static int fix_portstr(ulproto, spec, sport, dport) int ulproto; vchar_t *spec, *sport, *dport; { char sp[16], dp[16]; int a, b, c, d; unsigned long u; if (spec->buf == NULL) return 0; switch (ulproto) { case IPPROTO_ICMP: case IPPROTO_ICMPV6: case IPPROTO_MH: if (sscanf(spec->buf, "%d,%d", &a, &b) == 2) { sprintf(sp, "%d", a); sprintf(dp, "%d", b); } else if (sscanf(spec->buf, "%d", &a) == 1) { sprintf(sp, "%d", a); } else { yyerror("invalid an upper layer protocol spec"); return -1; } break; case IPPROTO_GRE: if (sscanf(spec->buf, "%d.%d.%d.%d", &a, &b, &c, &d) == 4) { sprintf(sp, "%d", (a << 8) + b); sprintf(dp, "%d", (c << 8) + d); } else if (sscanf(spec->buf, "%lu", &u) == 1) { sprintf(sp, "%d", (int) (u >> 16)); sprintf(dp, "%d", (int) (u & 0xffff)); } else { yyerror("invalid an upper layer protocol spec"); return -1; } break; } free(sport->buf); sport->buf = strdup(sp); if (!sport->buf) { yyerror("insufficient memory"); return -1; } sport->len = strlen(sport->buf); free(dport->buf); dport->buf = strdup(dp); if (!dport->buf) { yyerror("insufficient memory"); return -1; } dport->len = strlen(dport->buf); return 0; } static int setvarbuf(buf, off, ebuf, elen, vbuf, vlen) char *buf; int *off; struct sadb_ext *ebuf; int elen; const void *vbuf; int vlen; { memset(buf + *off, 0, PFKEY_UNUNIT64(ebuf->sadb_ext_len)); memcpy(buf + *off, (caddr_t)ebuf, elen); memcpy(buf + *off + elen, vbuf, vlen); (*off) += PFKEY_ALIGN8(elen + vlen); return 0; } void parse_init() { p_spi = 0; p_ext = SADB_X_EXT_CYCSEQ; p_alg_enc = SADB_EALG_NONE; p_alg_auth = SADB_AALG_NONE; p_mode = IPSEC_MODE_ANY; p_reqid = 0; p_replay = 0; p_key_enc_len = p_key_auth_len = 0; p_key_enc = p_key_auth = 0; p_lt_hard = p_lt_soft = 0; p_lb_hard = p_lb_soft = 0; memset(&sec_ctx, 0, sizeof(struct security_ctx)); p_aiflags = 0; p_aifamily = PF_UNSPEC; /* Clear out any natt OA information */ if (p_natt_oa) freeaddrinfo (p_natt_oa); p_natt_oa = NULL; p_natt_type = 0; return; } void free_buffer() { /* we got tons of memory leaks in the parser anyways, leave them */ return; }