]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/unbound/local-setup/local-unbound-setup.sh
Fix IPv6 socket option race condition and use after free.
[FreeBSD/FreeBSD.git] / usr.sbin / unbound / local-setup / local-unbound-setup.sh
1 #!/bin/sh
2 #-
3 # SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 #
5 # Copyright (c) 2013 Dag-Erling Smørgrav
6 # All rights reserved.
7 #
8 # Redistribution and use in source and binary forms, with or without
9 # modification, are permitted provided that the following conditions
10 # are met:
11 # 1. Redistributions of source code must retain the above copyright
12 #    notice, this list of conditions and the following disclaimer.
13 # 2. Redistributions in binary form must reproduce the above copyright
14 #    notice, this list of conditions and the following disclaimer in the
15 #    documentation and/or other materials provided with the distribution.
16 #
17 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 # ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 # SUCH DAMAGE.
28 #
29 # $FreeBSD$
30 #
31
32 #
33 # Configuration variables
34 #
35 user=""
36 unbound_conf=""
37 forward_conf=""
38 lanzones_conf=""
39 control_conf=""
40 control_socket=""
41 workdir=""
42 confdir=""
43 chrootdir=""
44 anchor=""
45 pidfile=""
46 resolv_conf=""
47 resolvconf_conf=""
48 service=""
49 start_unbound=""
50 forwarders=""
51
52 #
53 # Global variables
54 #
55 self=$(basename $(realpath "$0"))
56 bkext=$(date "+%Y%m%d.%H%M%S")
57
58 #
59 # Set default values for unset configuration variables.
60 #
61 set_defaults() {
62         : ${user:=unbound}
63         : ${workdir:=/var/unbound}
64         : ${confdir:=${workdir}/conf.d}
65         : ${unbound_conf:=${workdir}/unbound.conf}
66         : ${forward_conf:=${workdir}/forward.conf}
67         : ${lanzones_conf:=${workdir}/lan-zones.conf}
68         : ${control_conf:=${workdir}/control.conf}
69         : ${control_socket:=/var/run/local_unbound.ctl}
70         : ${anchor:=${workdir}/root.key}
71         : ${pidfile:=/var/run/local_unbound.pid}
72         : ${resolv_conf:=/etc/resolv.conf}
73         : ${resolvconf_conf:=/etc/resolvconf.conf}
74         : ${service:=local_unbound}
75         : ${start_unbound:=yes}
76 }
77
78 #
79 # Verify that the configuration files are inside the working
80 # directory, and if so, set the chroot directory accordingly.
81 #
82 set_chrootdir() {
83         chrootdir="${workdir}"
84         for file in "${unbound_conf}" "${forward_conf}" \
85             "${lanzones_conf}" "${control_conf}" "${anchor}" ; do
86                 if [ "${file#${workdir%/}/}" = "${file}" ] ; then
87                         echo "warning: ${file} is outside ${workdir}" >&2
88                         chrootdir=""
89                 fi
90         done
91         if [ -z "${chrootdir}" ] ; then
92                 echo "warning: disabling chroot" >&2
93         fi
94 }
95
96 #
97 # Scan through /etc/resolv.conf looking for uncommented nameserver
98 # lines that don't point to localhost and return their values.
99 #
100 get_nameservers() {
101         while read line ; do
102                 local bareline=${line%%\#*}
103                 local key=${bareline%% *}
104                 local value=${bareline#* }
105                 case ${key} in
106                 nameserver)
107                         case ${value} in
108                         127.0.0.1|::1|localhost|localhost.*)
109                                 ;;
110                         *)
111                                 echo "${value}"
112                                 ;;
113                         esac
114                         ;;
115                 esac
116         done
117 }
118
119 #
120 # Scan through /etc/resolv.conf looking for uncommented nameserver
121 # lines.  Comment out any that don't point to localhost.  Finally,
122 # append a nameserver line that points to localhost, if there wasn't
123 # one already, and enable the edns0 option.
124 #
125 gen_resolv_conf() {
126         local localhost=no
127         local edns0=no
128         while read line ; do
129                 local bareline=${line%%\#*}
130                 local key=${bareline%% *}
131                 local value=${bareline#* }
132                 case ${key} in
133                 nameserver)
134                         case ${value} in
135                         127.0.0.1|::1|localhost|localhost.*)
136                                 localhost=yes
137                                 ;;
138                         *)
139                                 echo -n "# "
140                                 ;;
141                         esac
142                         ;;
143                 options)
144                         case ${value} in
145                         *edns0*)
146                                 edns0=yes
147                                 ;;
148                         esac
149                         ;;
150                 esac
151                 echo "${line}"
152         done
153         if [ "${localhost}" = "no" ] ; then
154                 echo "nameserver 127.0.0.1"
155         fi
156         if [ "${edns0}" = "no" ] ; then
157                 echo "options edns0"
158         fi
159 }
160
161 #
162 # Boilerplate
163 #
164 do_not_edit() {
165         echo "# This file was generated by $self."
166         echo "# Modifications will be overwritten."
167 }
168
169 #
170 # Generate resolvconf.conf so it updates forward.conf in addition to
171 # resolv.conf.  Note "in addition to" rather than "instead of",
172 # because we still want it to update the domain name and search path
173 # if they change.  Setting name_servers to "127.0.0.1" ensures that
174 # the libc resolver will try unbound first.
175 #
176 gen_resolvconf_conf() {
177         local style="$1"
178         do_not_edit
179         echo "resolv_conf=\"/dev/null\" # prevent updating ${resolv_conf}"
180         if [ "${style}" = "dynamic" ] ; then
181                 echo "unbound_conf=\"${forward_conf}\""
182                 echo "unbound_pid=\"${pidfile}\""
183                 echo "unbound_service=\"${service}\""
184                 # resolvconf(8) likes to restart rather than reload
185                 echo "unbound_restart=\"service ${service} reload\""
186         else
187                 echo "# Static DNS configuration"
188         fi
189 }
190
191 #
192 # Generate forward.conf
193 #
194 gen_forward_conf() {
195         do_not_edit
196         echo "forward-zone:"
197         echo "        name: ."
198         for forwarder ; do
199                 if expr "${forwarder}" : "^[0-9A-Fa-f:.]\{1,\}$" >/dev/null ; then
200                         echo "        forward-addr: ${forwarder}"
201                 else
202                         echo "        forward-host: ${forwarder}"
203                 fi
204         done
205 }
206
207 #
208 # Generate lan-zones.conf
209 #
210 gen_lanzones_conf() {
211         do_not_edit
212         echo "server:"
213         echo "        # Unblock reverse lookups for LAN addresses"
214         echo "        unblock-lan-zones: yes"
215         echo "        insecure-lan-zones: yes"
216 }
217
218 #
219 # Generate control.conf
220 #
221 gen_control_conf() {
222         do_not_edit
223         echo "remote-control:"
224         echo "        control-enable: yes"
225         echo "        control-interface: ${control_socket}"
226         echo "        control-use-cert: no"
227 }
228
229 #
230 # Generate unbound.conf
231 #
232 gen_unbound_conf() {
233         do_not_edit
234         echo "server:"
235         echo "        username: ${user}"
236         echo "        directory: ${workdir}"
237         echo "        chroot: ${chrootdir}"
238         echo "        pidfile: ${pidfile}"
239         echo "        auto-trust-anchor-file: ${anchor}"
240         echo ""
241         if [ -f "${forward_conf}" ] ; then
242                 echo "include: ${forward_conf}"
243         fi
244         if [ -f "${lanzones_conf}" ] ; then
245                 echo "include: ${lanzones_conf}"
246         fi
247         if [ -f "${control_conf}" ] ; then
248                 echo "include: ${control_conf}"
249         fi
250         if [ -d "${confdir}" ] ; then
251                 echo "include: ${confdir}/*.conf"
252         fi
253 }
254
255 #
256 # Rename a file we are about to replace.
257 #
258 backup() {
259         local file="$1"
260         if [ -f "${file}" ] ; then
261                 local bkfile="${file}.${bkext}"
262                 echo "Original ${file} saved as ${bkfile}"
263                 mv "${file}" "${bkfile}"
264         fi
265 }
266
267 #
268 # Replace one file with another, making a backup copy of the first,
269 # but only if the new file is different from the old.
270 #
271 replace() {
272         local file="$1"
273         local newfile="$2"
274         if [ ! -f "${file}" ] ; then
275                 echo "${file} created"
276                 mv "${newfile}" "${file}"
277         elif ! cmp -s "${file}" "${newfile}" ; then
278                 backup "${file}"
279                 mv "${newfile}" "${file}"
280         else
281                 echo "${file} not modified"
282                 rm "${newfile}"
283         fi
284 }
285
286 #
287 # Print usage message and exit
288 #
289 usage() {
290         exec >&2
291         echo "usage: $self [options] [forwarder ...]"
292         echo "options:"
293         echo "    -n          do not start unbound"
294         echo "    -a path     full path to trust anchor file"
295         echo "    -C path     full path to additional configuration directory"
296         echo "    -c path     full path to unbound configuration file"
297         echo "    -f path     full path to forwarding configuration"
298         echo "    -O path     full path to remote control socket"
299         echo "    -o path     full path to remote control configuration"
300         echo "    -p path     full path to pid file"
301         echo "    -R path     full path to resolvconf.conf"
302         echo "    -r path     full path to resolv.conf"
303         echo "    -s service  name of unbound service"
304         echo "    -u user     user to run unbound as"
305         echo "    -w path     full path to working directory"
306         exit 1
307 }
308
309 #
310 # Main
311 #
312 main() {
313         umask 022
314
315         #
316         # Parse and validate command-line options
317         #
318         while getopts "a:C:c:f:no:p:R:r:s:u:w:" option ; do
319                 case $option in
320                 a)
321                         anchor="$OPTARG"
322                         ;;
323                 C)
324                         confdir="$OPTARG"
325                         ;;
326                 c)
327                         unbound_conf="$OPTARG"
328                         ;;
329                 f)
330                         forward_conf="$OPTARG"
331                         ;;
332                 n)
333                         start_unbound="no"
334                         ;;
335                 O)
336                         control_socket="$OPTARG"
337                         ;;
338                 o)
339                         control_conf="$OPTARG"
340                         ;;      
341                 p)
342                         pidfile="$OPTARG"
343                         ;;
344                 R)
345                         resolvconf_conf="$OPTARG"
346                         ;;
347                 r)
348                         resolv_conf="$OPTARG"
349                         ;;
350                 s)
351                         service="$OPTARG"
352                         ;;
353                 u)
354                         user="$OPTARG"
355                         ;;
356                 w)
357                         workdir="$OPTARG"
358                         ;;
359                 *)
360                         usage
361                         ;;
362                 esac
363         done
364         shift $((OPTIND-1))
365         set_defaults
366
367         #
368         # Get the list of forwarders, either from the command line or
369         # from resolv.conf.
370         #
371         forwarders="$@"
372         case "${forwarders}" in
373         [Nn][Oo][Nn][Ee])
374                 forwarders="none"
375                 style=recursing
376                 ;;
377         "")
378                 echo "Extracting forwarders from ${resolv_conf}."
379                 forwarders=$(get_nameservers <"${resolv_conf}")
380                 style=dynamic
381                 ;;
382         *)
383                 style=static
384                 ;;
385         esac
386
387         #
388         # Generate forward.conf.
389         #
390         if [ -z "${forwarders}" ] ; then
391                 echo -n "No forwarders found in ${resolv_conf##*/}, "
392                 if [ -f "${forward_conf}" ] ; then
393                         echo "using existing ${forward_conf##*/}."
394                 else
395                         echo "unbound will recurse."
396                 fi
397         elif [ "${forwarders}" = "none" ] ; then
398                 echo "Forwarding disabled, unbound will recurse."
399                 backup "${forward_conf}"
400         else
401                 local tmp_forward_conf=$(mktemp -u "${forward_conf}.XXXXX")
402                 gen_forward_conf ${forwarders} | unexpand >"${tmp_forward_conf}"
403                 replace "${forward_conf}" "${tmp_forward_conf}"
404         fi
405
406         #
407         # Generate lan-zones.conf.
408         #
409         local tmp_lanzones_conf=$(mktemp -u "${lanzones_conf}.XXXXX")
410         gen_lanzones_conf | unexpand >"${tmp_lanzones_conf}"
411         replace "${lanzones_conf}" "${tmp_lanzones_conf}"
412
413         #
414         # Generate control.conf.
415         #
416         local tmp_control_conf=$(mktemp -u "${control_conf}.XXXXX")
417         gen_control_conf | unexpand >"${tmp_control_conf}"
418         replace "${control_conf}" "${tmp_control_conf}"
419
420         #
421         # Generate unbound.conf.
422         #
423         local tmp_unbound_conf=$(mktemp -u "${unbound_conf}.XXXXX")
424         set_chrootdir
425         gen_unbound_conf | unexpand >"${tmp_unbound_conf}"
426         replace "${unbound_conf}" "${tmp_unbound_conf}"
427
428         #
429         # Start unbound, unless requested not to.  Stop immediately if
430         # it is not enabled so we don't end up with a resolv.conf that
431         # points into nothingness.  We could "onestart" it, but it
432         # wouldn't stick.
433         #
434         if [ "${start_unbound}" = "no" ] ; then
435                 # skip
436         elif ! service "${service}" enabled ; then
437                 echo "Please enable $service in rc.conf(5) and try again."
438                 return 1
439         elif ! service "${service}" restart ; then
440                 echo "Failed to start $service."
441                 return 1
442         fi
443
444         #
445         # Rewrite resolvconf.conf so resolvconf updates forward.conf
446         # instead of resolv.conf.
447         #
448         local tmp_resolvconf_conf=$(mktemp -u "${resolvconf_conf}.XXXXX")
449         gen_resolvconf_conf "${style}" | unexpand >"${tmp_resolvconf_conf}"
450         replace "${resolvconf_conf}" "${tmp_resolvconf_conf}"
451
452         #
453         # Finally, rewrite resolv.conf.
454         #
455         local tmp_resolv_conf=$(mktemp -u "${resolv_conf}.XXXXX")
456         gen_resolv_conf <"${resolv_conf}" | unexpand >"${tmp_resolv_conf}"
457         replace "${resolv_conf}" "${tmp_resolv_conf}"
458 }
459
460 main "$@"