#! /bin/sh # user interface to manual keying # Copyright (C) 1998, 1999 Henry Spencer. # # 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 . # # 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: manual,v 1.79 2002/03/11 20:42:27 henry Exp $ me='ipsec manual' usage="Usage: $me [--showonly] --{up|down|route|unroute} name $me [--showonly] --{up|down|route|unroute} --union partname ... other options: [--config ipsecconfigfile] [--other] [--show] [--iam ipaddress@interface]" # make sure outputs of (e.g.) ifconfig are in English unset LANG LANGUAGE LC_ALL LC_MESSAGES showonly= config= info=/var/run/ipsec.info shopts= other=0 union=0 noinclude= interfs= op= for dummy do case "$1" in --help) echo "$usage" ; exit 0 ;; --version) echo "$me $IPSEC_VERSION" ; exit 0 ;; --show) shopts=-x ;; --showonly) showonly=yes ;; --other) other=1 ;; --union) union=1 ;; --config) config="--config $2" ; shift ;; --noinclude) noinclude=--noinclude ;; --iam) interfs="$2" ; shift ;; --up|--down|--route|--unroute) if test " $op" != " " then echo "$usage" >&2 exit 2 fi op="$1" ;; --) shift ; break ;; -*) echo "$me: unknown option \`$1'" >&2 ; exit 2 ;; *) break ;; esac shift done case "$op$#:$union" in [01]:*) echo "$usage" >&2 ; exit 2 ;; 2:0) echo "$me: warning: obsolete command syntax used" >&2 op="--$2" names="$1" ;; [0-9]*:1) ;; --*) if test $# -eq 0 then echo "$usage" >&2 exit 2 fi names="$*" ;; *) echo "$usage" >&2 ; exit 2 ;; esac if test " $op" = " " then # --union obsolete-syntax case, op is last argument echo "$me: warning: obsolete command syntax used" >&2 names= prev= for arg do names="$names $prev" prev="$arg" done op="--$prev" fi case "$op" in --up|--down|--route|--unroute) ;; *) echo "$usage" >&2 ; exit 2 ;; esac case "$interfs" in '') interfs="`ifconfig | awk ' /^ipsec/ { interf = $1 ; next } /^[^ \t]/ { interf = "" ; next } /^[ \t]*inet addr/ { sub(/:/, " ", $0) if (interf != "") print $3 "@" interf }' | tr '\n' ' '`" ;; esac if test -s $info then . $info fi ipsec _confread $config $noinclude $names | awk ' BEGIN { FS = "\t" myname = "'"$me"'" err = "cat >&2" op = "'"$op"'" other = '"$other"' names = "'"$names"'" interfs = "'"$interfs"'" ni = split(interfs, terfs, " ") if (ni == 0) fail("no IPsec-enabled interfaces found") for (i = 1; i <= ni; i++) { nc = split(terfs[i], cpts, "@") if (nc != 2) fail("internal error on " terfs[i]) interface[cpts[1]] = cpts[2] } draddr = "'"$defaultrouteaddr"'" drnexthop = "'"$defaultroutenexthop"'" s[""] = "" nlspi = 0 nrspi = 0 failed = 0 maskbits[0] = "0.0.0.0" maskbits[1] = "128.0.0.0" maskbits[2] = "192.0.0.0" maskbits[3] = "224.0.0.0" maskbits[4] = "240.0.0.0" maskbits[5] = "248.0.0.0" maskbits[6] = "252.0.0.0" maskbits[7] = "254.0.0.0" maskbits[8] = "255.0.0.0" maskbits[9] = "255.128.0.0" maskbits[10] = "255.192.0.0" maskbits[11] = "255.224.0.0" maskbits[12] = "255.240.0.0" maskbits[13] = "255.248.0.0" maskbits[14] = "255.252.0.0" maskbits[15] = "255.254.0.0" maskbits[16] = "255.255.0.0" maskbits[17] = "255.255.128.0" maskbits[18] = "255.255.192.0" maskbits[19] = "255.255.224.0" maskbits[20] = "255.255.240.0" maskbits[21] = "255.255.248.0" maskbits[22] = "255.255.252.0" maskbits[23] = "255.255.254.0" maskbits[24] = "255.255.255.0" maskbits[25] = "255.255.255.128" maskbits[26] = "255.255.255.192" maskbits[27] = "255.255.255.224" maskbits[28] = "255.255.255.240" maskbits[29] = "255.255.255.248" maskbits[30] = "255.255.255.252" maskbits[31] = "255.255.255.254" maskbits[32] = "255.255.255.255" } $1 == "=" { next } $1 == "!" { if ($2 != "") fail($2) next } $1 != ":" { fail("internal error, unknown type code \"" $1 "\"") } { s[$2] = $3 } function q(s) { return "\"" s "\"" } function fail(m) { print myname ": fatal error in " q(names) ": " m |err failed = 1 exit } function swap(k, t, l, r) { l = "left" k r = "right" k if ((l in s) && (r in s)) { t = s[l] s[l] = s[r] s[r] = t } else if (l in s) { # but not r s[r] = s[l] delete s[l] } else if (r in s) { # but not l s[l] = s[r] delete s[r] } } function yesno(k) { if ((k in s) && s[k] != "yes" && s[k] != "no") fail("parameter \"" k "\" must be \"yes\" or \"no\"") } function default(k, v) { if (!(k in s)) s[k] = v } function need(k) { if (!(k in s)) fail("connection has no \"" k "\" parameter specified") if (s[k] == "") fail("parameter \"" k "\" value must be non-empty") } function integer(k) { if (!(k in s)) return if (s[k] !~ /^[0-9]+$/) fail("parameter \"" k "\" value must be integer") } function nexthopset(dir, val, k) { k = dir "nexthop" if (k in s) fail("non-default value of " k " is being overridden") if (val != "") s[k] = val else if (k in s) delete s[k] } function leftward( t) { nlspi++ if ("spi" in s) return s["spi"] t = spibase spil spil += 2 return t } function rightward( t) { nrspi++ if ("spi" in s) return s["spi"] t = spibase spir spir += 2 return t } function netfix(dir, n, t) { n = s[dir "subnet"] if (n == "%default") n = "0.0.0.0/0" if (n !~ /\//) fail(dir "subnet=" n " has no mask specified") t = split(n, netfixarray, "/") if (t != 2) fail("bad syntax in " dir "subnet=" n) s[dir "net"] = netfixarray[1] s[dir "mask"] = mask(netfixarray[2]) } function mask(m) { if (m ~ /\./) return m if (!(m in maskbits)) fail("unknown mask syntax \"" m "\"") return maskbits[m] } function bidir(name, l, r) { l = "left" name r = "right" name if (!(l in s) && (name in s)) s[l] = s[name] if (!(r in s) && (name in s)) s[r] = s[name] if ((l in s) != (r in s)) fail("must give both or neither \"" l "\" and \"" \ r "\"") } function espspi(src, dest, spi, dir) { if (!("esp" in s)) return dir = (dest == me) ? "left" : "right" print "ipsec spi --label", q(names), "--af inet", "--said", ("esp" spi "@" dest), "\\" print "\t--esp", s["esp"], "--src", src, "\\" if ((dir "espauthkey") in s) print "\t--authkey", s[dir "espauthkey"], "\\" if ("espreplay_window" in s) print "\t--replay_window", s["espreplay_window"], "\\" if ((dir "espenckey") in s) print "\t--enckey", s[dir "espenckey"], "&&" else print "\t&&" } function ahspi(src, dest, spi, dir) { if (!("ah" in s)) return dir = (dest == me) ? "left" : "right" if (!((dir "ahkey") in s)) fail("AH specified but no ahkey= given") print "ipsec spi --label", q(names), "--af inet", "--said", ("ah" spi "@" dest), "\\" print "\t--ah", s["ah"], "--src", src, "\\" if ("ahreplay_window" in s) print "\t--replay_window", s["ahreplay_window"], "\\" print "\t--authkey", s[dir "ahkey"], "&&" } # issue a suitable invocation of updown command function updown(verb, suffix, cmd) { if ("leftupdown" in s) { cmd = s["leftupdown"] if (s["leftfirewall"] == "yes") fail("cannot specify both updown and firewall") } else { cmd = "ipsec _updown" if (s["leftfirewall"] == "yes") cmd = cmd " ipfwadm" } print "PLUTO_VERB=" verb verbsuf " " cmd " " suffix } END { ######### if (failed) exit 1 default("type", "tunnel") if (s["type"] == "transport") { if ("leftsubnet" in s) fail("type=transport incompatible with leftsubnet") if ("rightsubnet" in s) fail("type=transport incompatible with rightsubnet") } else if (s["type"] != "tunnel" && s["type"] != "passthrough") fail("only know how to do types tunnel/transport/passthrough") pass = 0 if (s["type"] == "passthrough") { if (("ah" in s) || ("esp" in s)) fail("passthrough connection may not specify AH or ESP") pass = 1 } else { if (!("ah" in s) && !("esp" in s)) fail("neither AH nor ESP specified for connection") } need("left") need("right") if (s["left"] == "%defaultroute") { if (s["right"] == "%defaultroute") fail("left and right cannot both be %defaultroute") if (draddr == "") fail("%defaultroute requested but not known") s["left"] = draddr nexthopset("left", drnexthop) } else if (s["right"] == "%defaultroute") { if (draddr == "") fail("%defaultroute requested but not known") s["right"] = draddr nexthopset("right", drnexthop) } leftsub = ("leftsubnet" in s) ? 1 : 0 default("leftsubnet", s["left"] "/32") rightsub = ("rightsubnet" in s) ? 1 : 0 default("rightsubnet", s["right"] "/32") default("leftfirewall", "no") default("rightfirewall", "no") yesno("leftfirewall") yesno("rightfirewall") integer("espreplay_window") if (("espreplay_window" in s) && s["espreplay_window"] == 0) delete s["espreplay_window"] integer("ahreplay_window") if (("ahreplay_window" in s) && s["ahreplay_window"] == 0) delete s["ahreplay_window"] netfix("left") netfix("right") default("leftnexthop", s["right"]) default("rightnexthop", s["left"]) if (s["leftnexthop"] == s["left"]) fail("left and leftnexthop must not be the same") if (s["rightnexthop"] == s["right"]) fail("right and rightnexthop must not be the same") bidir("espenckey") bidir("espauthkey") bidir("ahkey") if ("spi" in s && "spibase" in s) fail("cannot specify both spi and spibase") if (!pass) { if ("spibase" in s) { b = s["spibase"] if (b !~ /^0x[0-9a-fA-F]+0$/) fail("bad syntax in spibase -- must be 0x...0") spibase = substr(b, 1, length(b)-1) } else { need("spi") if (s["spi"] !~ /^0x[0-9a-fA-F]+$/) fail("bad syntax in spi -- must be 0x...") } } spir = 0 spil = 1 # who am I? me = "" for (addr in interface) { if (addr == s["left"] || addr == s["right"]) { if (me != "") fail("ambiguous: could be on \"" iface \ "\" or \"" interface[addr] "\"") me = addr iface = interface[addr] } } if (me == "") fail("cannot find interface for " s["left"] " or " s["right"]) if (other) { if (s["left"] == me) me = s["right"] else if (s["right"] == me) me = s["left"] } havesubnet = leftsubnet if (s["right"] == me) { swap("") # swaps "left" and "right" swap("subnet") swap("nexthop") swap("net") swap("mask") swap("firewall") swap("espspi") swap("ahspi") swap("espenckey") swap("espauthkey") swap("ahkey") swap("updown") t = spil spil = spir spir = t havesubnet = rightsubnet } him = s["right"] if (s["leftnexthop"] == "%defaultroute") { if (drnexthop == "") fail("%defaultroute requested but not known") s["leftnexthop"] = drnexthop } tspi = rightward() if (s["type"] == "tunnel") { espi = rightward() intspi = leftward() } else espi = tspi if (s["rightespspi"] != "") espi = s["rightespspi"] respi = leftward() if (s["leftespspi"] != "") respi = s["leftespspi"] if ("ah" in s) { if ("esp" in s) { aspi = rightward() raspi = leftward() } else { aspi = espi raspi = respi } if (s["rightahspi"] != "") aspi = s["rightahspi"] if (s["leftahspi"] != "") raspi = s["leftahspi"] } routeid = "-net " s["rightnet"] " netmask " s["rightmask"] if (s["rightmask"] == "255.255.255.255") routeid = "-host " s["rightnet"] print "PATH=\"'"$PATH"'\"" print "export PATH" print "PLUTO_VERSION=1.1" verbsuf = (havesubnet) ? "-client" : "-host" print "PLUTO_CONNECTION=" q(names) print "PLUTO_NEXT_HOP=" s["leftnexthop"] print "PLUTO_INTERFACE=" iface print "PLUTO_ME=" me print "PLUTO_MY_CLIENT=" s["leftsubnet"] print "PLUTO_MY_CLIENT_NET=" s["leftnet"] print "PLUTO_MY_CLIENT_MASK=" s["leftmask"] print "PLUTO_PEER=" him print "PLUTO_PEER_CLIENT=" s["rightsubnet"] print "PLUTO_PEER_CLIENT_NET=" s["rightnet"] print "PLUTO_PEER_CLIENT_MASK=" s["rightmask"] print "export PLUTO_VERSION PLUTO_CONNECTION PLUTO_NEXT_HOP" print "export PLUTO_INTERFACE PLUTO_ME PLUTO_MY_CLIENT" print "export PLUTO_MY_CLIENT_NET PLUTO_MY_CLIENT_MASK PLUTO_PEER" print "export PLUTO_PEER_CLIENT PLUTO_PEER_CLIENT_NET" print "export PLUTO_PEER_CLIENT_MASK" if (op == "--up") { print "{" # first, the outbound SAs if (s["type"] == "tunnel") { print "ipsec spi --label", q(names), "--af inet", "--said", ("tun" tspi "@" him), "\\" print "\t--ip4", "--src", me, "--dst", him, "&&" } espspi(me, him, espi) ahspi(me, him, aspi) if (nrspi > 1) { # group them printf "ipsec spigrp --label %s --said ", q(names) if (s["type"] == "tunnel") printf "tun%s@%s ", tspi, him if (("esp" in s)) printf "esp%s@%s ", espi, him if ("ah" in s) printf "ah%s@%s ", aspi, him printf " &&\n" } # inbound SAs if (s["type"] == "tunnel") { print "ipsec spi --label", q(names), "--af inet", "--said", ("tun" intspi "@" me), "\\" print "\t--ip4", "--src", him, "--dst", me, "&&" } espspi(him, me, respi) ahspi(him, me, raspi) if (nlspi > 1) { # group them printf "ipsec spigrp --label %s --said ", q(names) if (s["type"] == "tunnel") printf "tun%s@%s ", intspi, me if (("esp" in s)) printf "esp%s@%s ", respi, me if ("ah" in s) printf "ah%s@%s ", raspi, me printf " &&\n" } # with the SAs in place, eroute to them print "ipsec eroute --label", q(names), "--eraf inet --replace", "\\" if (pass) p = "%pass" else { if (s["type"] == "tunnel") p = "tun" else if (("esp" in s)) p = "esp" else p = "ah" p = p tspi "@" him } print "\t--src", s["leftsubnet"], "--dst", s["rightsubnet"], "--said", p, "&&" # with the eroute in place, NOW we can route to it #print "{ route del", routeid, "2>/dev/null ; true ; } &&" updown("prepare", "&&") #print "route add", routeid, "dev", iface, "gw", # s["leftnexthop"], "&&" updown("route", "&&") # and with all processing in place, we can penetrate firewall #if (s["leftfirewall"] == "yes") { # print "ipfwadm -F -i accept -b -S", s["leftsubnet"], # "-D", s["rightsubnet"], "&&" #} updown("up", "&&") print "true" print "} || {" } else if (op == "--route") { #print "{ route del", routeid, "2>/dev/null ; true ; } &&" updown("prepare", "&&") #print "route add", routeid, "dev", iface, "gw", # s["leftnexthop"] updown("route") exit 0 } else if (op == "--unroute") { #print "route del", routeid, "dev", iface, "gw", # s["leftnexthop"] updown("unroute") exit 0 } else # down print "{" # now do "down", unconditionally, since the desired output for "up" # is { up && up && up && true } || { down ; down ; down } # tear things down in fairly strict reverse order #if (s["leftfirewall"] == "yes") # print "ipfwadm -F -d accept -b -S", s["leftsubnet"], # "-D", s["rightsubnet"] updown("down") #print "route del", routeid, "dev", iface, "gw", s["leftnexthop"] print "# do not delete route" print "ipsec eroute --label", q(names), "--eraf inet --del", "\\" print "\t--src", s["leftsubnet"], "--dst", s["rightsubnet"] #if ("ah" in s) { # print "ipsec spi --label", q(names), "--af inet", "--del", # "--said", ("ah" raspi "@" me) #} #if ("esp" in s) { # print "ipsec spi --label", q(names), "--af inet", "--del", # "--said", ("esp" respi "@" me) #} if (!pass) { if (s["type"] == "tunnel") p = "tun" else if (("esp" in s)) p = "esp" else p = "ah" print "ipsec spi --label", q(names), "--af inet", "--del", "--said", (p tspi "@" him), " # outbound" print "ipsec spi --label", q(names), "--af inet", "--del", "--said", (p intspi "@" me), " # inbound" } if (op == "--up") print "} 2>/dev/null" else print "}" ######### }' | if test $showonly then cat else sh $shopts fi