diff options
Diffstat (limited to 'programs/_confread/_confread.in')
-rwxr-xr-x | programs/_confread/_confread.in | 520 |
1 files changed, 520 insertions, 0 deletions
diff --git a/programs/_confread/_confread.in b/programs/_confread/_confread.in new file mode 100755 index 000000000..4561af9fe --- /dev/null +++ b/programs/_confread/_confread.in @@ -0,0 +1,520 @@ +#!/bin/sh +# configuration-file reader utility +# Copyright (C) 1999-2002 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 <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: _confread.in,v 1.15 2006/04/20 04:42:12 as Exp $ +# +# Extract configuration info from /etc/ipsec.conf, repackage as assignments +# to shell variables or tab-delimited fields. Success or failure is reported +# inline, as extra data, due to the vagaries of shell backquote handling. +# In the absence of --varprefix, output is tab-separated fields, like: +# = sectionname +# : parameter value +# ! status (empty for success, else complaint) +# In the presence of (say) "--varprefix IPSEC", output is like: +# IPSEC_confreadsection="sectionname" +# IPSECparameter="value" +# IPSEC_confreadstatus="status" (same empty/complaint convention) +# +# The "--search parametername" option inverts the search: instead of +# yielding the parameters of the specified name(s), it yields the names +# of sections with parameter <parametername> having (one of) the +# specified value(s). In this case, --varprefix output is a list of +# names in the <prefix>_confreadnames variable. Search values with +# white space in them are currently not handled properly. +# +# Typical usage: +# eval `ipsec _confread --varprefix IPSEC --type config setup` +# if test " $IPSEC_confreadstatus" != " " +# then +# echo "$0: $IPSEC_confreadstatus -- aborting" 2>&1 +# exit 1 +# fi + +# absent default config file treated as empty +config=${IPSEC_CONFS-@FINALCONFDIR@}/ipsec.conf +if test ! -f "$config" ; then config=/dev/null ; fi + +include=yes +type=conn +fieldfmt=yes +prefix= +search= +export=0 +version= +optional=0 +me="ipsec _confread" + +for dummy +do + case "$1" in + --config) config="$2" ; shift ;; + --noinclude) include= ;; + --type) type="$2" ; shift ;; + --varprefix) fieldfmt= + prefix="$2" + shift ;; + --export) export=1 ;; + --search) search="$2" ; shift ;; + --version) echo "$me $IPSEC_VERSION" ; exit 0 ;; + --optional) optional=1 ;; + --) shift ; break ;; + -*) echo "$0: unknown option \`$1'" >&2 ; exit 2 ;; + *) break ;; + esac + shift +done + +if test "$include" +then + ipsec _include --inband $config +else + cat $config +fi | +awk 'BEGIN { + type = "'"$type"'" + names = "'"$*"'" + prefix = "'"$prefix"'" + export = "'"$export"'" + optional = 0 + '"$optional"' + myid = "'"$IPSECmyid"'" + search = "'"$search"'" + searching = 0 + if (search != "") { + searching = 1 + searchpat = search "[ \t]*=[ \t]*" + } + fieldfmt = 0 + if ("'"$fieldfmt"'" == "yes") + fieldfmt = 1 + including = 0 + if ("'"$include"'" == "yes") + including = 1 + filename = "'"$config"'" + lineno = 0 + originalfilename = filename + if (fieldfmt) + bq = eq = "\"" + else + bq = eq = "\\\"" + failed = 0 + insection = 0 + wrongtype = 0 + indefault = 0 + outputting = 0 + sawnondefault = 0 + OFS = "\t" + o_status = "!" + o_parm = ":" + o_section = "=" + o_names = "%" + o_end = "." + n = split(names, na, " ") + if (n == 0) + fail("no section names supplied") + for (i = 1; i <= n; i++) { + if (na[i] in wanted) + fail("section " bq na[i] eq " requested more than once") + wanted[na[i]] = 1 + pending[na[i]] = 1 + if (!searching && na[i] !~ /^[a-zA-Z][a-zA-Z0-9._-]*$/) + fail("invalid section name " bq na[i] eq) + } + + good = "also alsoflip type auto authby _plutodevel" + left = " left leftsubnet leftnexthop leftfirewall lefthostaccess leftupdown" + akey = " keyexchange auth pfs keylife rekey rekeymargin rekeyfuzz" + akey = akey " dpdaction dpddelay dpdtimeout" + akey = akey " pfsgroup compress" + akey = akey " keyingtries ikelifetime disablearrivalcheck failureshunt ike" + mkey = " spibase spi esp espenckey espauthkey espreplay_window" + left = left " leftespenckey leftespauthkey leftahkey" + left = left " leftespspi leftahspi leftid leftrsasigkey leftrsasigkey2" + left = left " leftsendcert leftcert leftca leftsubnetwithin leftprotoport" + left = left " leftgroups leftsourceip" + mkey = mkey " ah ahkey ahreplay_window" + right = left + gsub(/left/, "right", right) + n = split(good left right akey mkey, g) + for (i = 1; i <= n; i++) + goodnames["conn:" g[i]] = 1 + + good = "also interfaces forwardcontrol myid" + good = good " syslog klipsdebug plutodebug plutoopts plutostderrlog" + good = good " plutorestartoncrash" + good = good " dumpdir manualstart pluto" + good = good " plutowait prepluto postpluto" + good = good " fragicmp hidetos rp_filter uniqueids" + good = good " overridemtu pkcs11module pkcs11keepstate pkcs11proxy" + good = good " nocrsend strictcrlpolicy crlcheckinterval cachecrls" + good = good " nat_traversal keep_alive force_keepalive" + good = good " disable_port_floating virtual_private" + + n = split(good, g) + for (i = 1; i <= n; i++) + goodnames["config:" g[i]] = 1 + + good = "auto cacert ldaphost ldapbase crluri crluri2 ocspuri" + good = good " strictcrlpolicy" + + n = split(good, g) + for (i = 1; i <= n; i++) + goodnames["ca:" g[i]] = 1 + + goodtypes["conn"] = 1 + goodtypes["config"] = 1 + goodtypes["ca"] = 1 + + badchars = "" + for (i = 1; i < 32; i++) + badchars = badchars sprintf("%c", i) + for (i = 127; i < 128+32; i++) + badchars = badchars sprintf("%c", i) + badchar = "[" badchars "]" + + # if searching, seen is set of sectionnames which match + # if not searching, seen is set of parameter names found + seen[""] = "" + defaults[""] = "" + usesdefault[""] = "" + orientation = 1 +} + + + +function output(code, v1, v2) { + if (code == o_parm) { + if (v2 == "") # suppress empty parameters + return + if (privatename(v1)) # and private ones + return + if (v2 ~ badchar) + fail("parameter value " bq v2 eq " contains unprintable character") + } + + if (fieldfmt) { + print code, v1, v2 + return + } + + if (code == o_status) { + v2 = v1 + v1 = "_confreadstatus" + } else if (code == o_section) { + v2 = v1 + v1 = "_confreadsection" + } else if (code == o_names) { + v2 = v1 + v1 = "_confreadnames" + } else if (code != o_parm) + return # currently no variable version of o_end + + print prefix v1 "=\"" v2 "\"" + if (export) + print "export " prefix v1 +} +function searchfound(sectionname, n, i, reflist) { + # a hit in x is a hit in everybody who refers to x too + n = split(refsto[sectionname], reflist, ";") + for (i = 1; i <= n; i++) + if (reflist[i] in seen) + fail("duplicated parameter " bq search eq) + else + seen[reflist[i]] = 1 + seen[sectionname] = 1 +} +function fail(msg) { + output(o_status, ("(" filename ", line " lineno ") " msg)) + failed = 1 + while ((getline junk) > 0) + continue + exit +} +function badname(n) { + if ((type ":" n) in goodnames) + return 0 + if (privatename(n)) + return 0 + return 1 +} +function privatename(n) { + if (n ~ /^[xX][-_]/) + return 1 + return 0 +} +function orient(n) { + if (orientation == -1) { + if (n ~ /left/) + gsub(/left/, "right", n) + else if (n ~ /right/) + gsub(/right/, "left", n) + } + return n +} +# in searching, referencing is transitive: xyz->from->to +function chainref(from, to, i, reflist, listnum) { + if (from in refsto) { + listnum = split(refsto[from], reflist, ";") + for (i = 1; i <= listnum; i++) + chainref(reflist[i], to) + } + if (to in refsto) + refsto[to] = refsto[to] ";" from + else + refsto[to] = from +} + +# start of rules + +{ + lineno++ + # lineno is now the number of this line + + # we must remember indentation because comment stripping loses it + exdented = $0 !~ /^[ \t]/ + sub(/^[ \t]+/, "") # get rid of leading white space + sub(/[ \t]+$/, "") # get rid of trailing white space +} +including && $0 ~ /^#[<>:]/ { + # _include control line + if ($1 ~ /^#[<>]$/) { + filename = $2 + lineno = $3 - 1 + } else if ($0 ~ /^#:/) { + msg = substr($0, 3) + gsub(/"/, "\\\"", msg) + fail(msg) + } + next +} +exdented { + # any non-leading-white-space line is a section end + ### but not the end of relevant stuff, might be also= sections later + ###if (insection && !indefault && !searching && outputting) + ### output(o_end) + insection = 0 + wrongtype = 0 + indefault = 0 + outputting = 0 +} +/[ \t]#/ { + # strip trailing comments including the leading whitespace + # tricky because we must respect quotes + q = 0 + for (i = 1; i <= NF; i++) { + if ($i ~ /^#/ && q % 2 == 0) { + NF = i - 1; + break + } + # using $i in gsub loses whitespace?!? + junk = $i + q += gsub(/"/, "&", junk) + } +} +$0 == "" || $0 ~ /^#/ { + # empty lines and comments are ignored + next +} +exdented && NF != 2 { + # bad section header + fail("section header " bq $0 eq " has wrong number of fields (" NF ")") +} +exdented && $1 == "version" { + version = $2 + 0 + if (version < 2.0 || 2.0 < version) + fail("we only support version 2.0 ipsec.conf files, not " bq version eq) + next +} +version == "" { + fail("we only support version 2 ipsec.conf files") +} +exdented && !($1 in goodtypes) { + # unknown section type + fail("section type " bq $1 eq " not recognized") +} +exdented && $1 != type { + # section header, but not of the type we want + insection = 1 + wrongtype = 1 + next +} +extented { + # type fits + wrongtype = 0 +} +exdented && $1 == "config" && $2 != "setup" { + fail("unknown config section " bq $2 eq) +} +exdented && $2 != "%default" { + # non-default section header of our type + sawnondefault = 1 +} +exdented && searching && $2 != "%default" { + # section header, during search + insection = 1 + sectionname = $2 + usesdefault[sectionname] = 1 # tentatively + next +} +exdented && !searching && $2 in wanted { + # one of our wanted section headers + if (!($2 in pending)) + fail("duplicate " type " section " bq $2 eq) + delete pending[$2] + tag = bq type " " $2 eq + outputting = 1 + insection = 1 + orientation = wanted[$2] + output(o_section, $2) + next +} +exdented && $2 == "%default" { + # relevant default section header + if (sawnondefault) + fail(bq $1 " %default" eq " sections must precede non-default ones") + tag = bq type " " $2 eq + indefault = 1 + next +} +exdented { + # section header, but not one we want + insection = 1 + next +} +!insection && !indefault { + # starts with white space but not in a section... oops + fail("parameter is not within a section") +} +!wrongtype && searching && $0 ~ searchpat { + # search found the right parameter name + match($0, searchpat) + rest = substr($0, RLENGTH+1) + if (rest ~ /^".*"$/) + rest = substr(rest, 2, length(rest)-2) + if (!indefault) { + if (!usesdefault[sectionname]) + fail("duplicated parameter " bq search eq) + usesdefault[sectionname] = 0 + } else if (search in defaults) + fail("duplicated parameter " bq search eq) + if (rest in wanted) { # a hit + if (indefault) + defaults[search] = rest + else + searchfound(sectionname) + } else { + # rather a kludge, but must check this somewhere + if (search == "auto" && rest !~ /^(add|route|start|ignore|manual)$/) + fail("illegal auto value " bq rest eq) + } + next +} +!searching && !outputting && !indefault { + # uninteresting line + next +} +$0 ~ /"/ && $0 !~ /^[^=]+=[ \t]*"[^"]*"$/ { + if (!searching) + fail("mismatched quotes in parameter value") + else + gsub(/"/, "", $0) +} +$0 !~ /^[a-zA-Z_][a-zA-Z0-9_-]*[ \t]*=/ { + if (searching) + next # just ignore it + fail("syntax error or illegal parameter name") +} +{ + sub(/[ \t]*=[ \t]*/, "=") # get rid of white space around = +} +$0 ~ /^(also|alsoflip)=/ { + v = orientation + if ($0 ~ /^alsoflip/) + v = -v; + if (indefault) + fail("%default section may not contain " bq "also" eq " or " bq "alsoflip" eq " parameter") + sub(/^(also|alsoflip)=/, "") + if ($0 !~ /^[a-zA-Z][a-zA-Z0-9._-]*$/) + fail("invalid section name " bq $0 eq) + if (!searching) { + if ($0 in wanted) + fail("section " bq $0 eq " requested more than once") + wanted[$0] = v + pending[$0] = 1 + } else + chainref(sectionname, $0) + next +} +!outputting && !indefault { + # uninteresting line even for a search + next +} +{ + equal = match($0, /[=]/) + name = substr($0, 1, equal-1) + if (badname(name)) + fail("unknown parameter name " bq name eq) + value = substr($0, equal+1) + if (value ~ /^"/) + value = substr(value, 2, length(value)-2) + else if (value ~ /[ \t]/) + fail("white space within non-quoted parameter " bq name eq) +} +indefault { + if (name in defaults) + fail("duplicated default parameter " bq name eq) + defaults[name] = value + next +} +{ + name = orient(name) + if (name in seen) + fail("duplicated parameter " bq name eq) + seen[name] = 1 + output(o_parm, name, value) +} +END { + if (failed) + exit 1 + + filename = originalfilename + unseen = "" + for (i in pending) + unseen = unseen " " i + if (!optional && !searching && unseen != "") + fail("did not find " type " section(s) " bq substr(unseen, 2) eq) + if (!searching) { + for (name in defaults) + if (!(name in seen)) + output(o_parm, name, defaults[name]) + } else { + if (defaults[search] in wanted) + for (name in usesdefault) + if (usesdefault[name]) + seen[name] = 1 + delete seen[""] + if (fieldfmt) + for (name in seen) + output(o_section, name) + else { + outlist = "" + for (name in seen) + if (outlist == "") + outlist = name + else + outlist = outlist " " name + output(o_names, outlist) + } + } + output(o_status, "") +}' |