/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk

« back to all changes in this revision

Viewing changes to initramfs-tools-hook

  • Committer: teddy at recompile
  • Date: 2020-04-05 21:30:59 UTC
  • Revision ID: teddy@recompile.se-20200405213059-fb2a61ckqynrmatk
Fix file descriptor leak in mandos-client

When the local network has Mandos servers announcing themselves using
real, globally reachable, IPv6 addresses (i.e. not link-local
addresses), but there is no router on the local network providing IPv6
RA (Router Advertisement) packets, the client cannot reach the server
by normal means, since the client only has a link-local IPv6 address,
and has no usable route to reach the server's global IPv6 address.
(This is not a common situation, and usually only happens when the
router itself reboots and runs a Mandos client, since it cannot then
give RA packets to itself.)  The client code has a solution for
this, which consists of adding a temporary local route to reach the
address of the server during communication, and removing this
temporary route afterwards.

This solution with a temporary route works, but has a file descriptor
leak; it leaks one file descriptor for each addition and for each
removal of a route.  If one server requiring an added route is present
on the network, but no servers gives a password, making the client
retry after the default ten seconds, and we furthermore assume a
default 1024 open files limit, the client runs out of file descriptors
after about 90 minutes, after which time the client process will be
useless and fail to retrieve any passwords, necessitating manual
password entry via the keyboard.

Fix this by eliminating the file descriptor leak in the client.

* plugins.d/mandos-client.c (add_delete_local_route): Do
  close(devnull) also in parent process, also if fork() fails, and on
  any failure in child process.

Show diffs side-by-side

added added

removed removed

Lines of Context:
3
3
# This script will be run by 'mkinitramfs' when it creates the image.
4
4
# Its job is to decide which files to install, then install them into
5
5
# the staging area, where the initramfs is being created.  This
6
 
# happens when a new 'linux-image' package is installed, or when the
 
6
# happens when a new 'linux-image' package is installed, or when an
7
7
# administrator runs 'update-initramfs' by hand to update an initramfs
8
8
# image.
9
9
 
29
29
 
30
30
. /usr/share/initramfs-tools/hook-functions
31
31
 
32
 
if [ -d /usr/lib/mandos ]; then
33
 
    prefix=/usr
34
 
elif [ -d /usr/local/lib/mandos ]; then
35
 
    prefix=/usr/local
36
 
else
 
32
for d in /usr/lib \
 
33
    "/usr/lib/`dpkg-architecture -qDEB_HOST_MULTIARCH 2>/dev/null`" \
 
34
    "`rpm --eval='%{_libdir}' 2>/dev/null`" /usr/local/lib; do
 
35
    if [ -d "$d"/mandos ]; then
 
36
        libdir="$d"
 
37
        break
 
38
    fi
 
39
done
 
40
if [ -z "$libdir" ]; then
37
41
    # Mandos not found
38
42
    exit 1
39
43
fi
40
44
 
 
45
for d in /etc/keys/mandos /etc/mandos/keys; do
 
46
    if [ -d "$d" ]; then
 
47
        keydir="$d"
 
48
        break
 
49
    fi
 
50
done
 
51
if [ -z "$keydir" ]; then
 
52
    # Mandos key directory not found
 
53
    exit 1
 
54
fi
 
55
 
 
56
set `{ getent passwd _mandos \
 
57
    || getent passwd nobody \
 
58
    || echo ::65534:65534:::; } \
 
59
    | cut --delimiter=: --fields=3,4 --only-delimited \
 
60
    --output-delimiter=" "`
 
61
mandos_user="$1"
 
62
mandos_group="$2"
 
63
 
41
64
# The Mandos network client uses the network
42
65
auto_add_modules net
43
66
# The Mandos network client uses IPv6
47
70
CONFDIR="/conf/conf.d/mandos"
48
71
MANDOSDIR="/lib/mandos"
49
72
PLUGINDIR="${MANDOSDIR}/plugins.d"
 
73
PLUGINHELPERDIR="${MANDOSDIR}/plugin-helpers"
 
74
HOOKDIR="${MANDOSDIR}/network-hooks.d"
50
75
 
51
76
# Make directories
52
 
mkdir --parents "${DESTDIR}${CONFDIR}"
53
 
mkdir --parents "${DESTDIR}${PLUGINDIR}"
 
77
install --directory --mode=u=rwx,go=rx "${DESTDIR}${CONFDIR}" \
 
78
        "${DESTDIR}${MANDOSDIR}" "${DESTDIR}${HOOKDIR}"
 
79
install --owner=${mandos_user} --group=${mandos_group} --directory \
 
80
        --mode=u=rwx "${DESTDIR}${PLUGINDIR}" \
 
81
        "${DESTDIR}${PLUGINHELPERDIR}"
 
82
 
 
83
copy_exec "$libdir"/mandos/mandos-to-cryptroot-unlock "${MANDOSDIR}"
54
84
 
55
85
# Copy the Mandos plugin runner
56
 
copy_exec "$prefix"/lib/mandos/plugin-runner "${DESTDIR}${MANDOSDIR}"
 
86
copy_exec "$libdir"/mandos/plugin-runner "${MANDOSDIR}"
57
87
 
58
88
# Copy the plugins
59
89
 
60
90
# Copy the packaged plugins
61
 
for file in "$prefix"/lib/mandos/plugins.d/*; do
 
91
for file in "$libdir"/mandos/plugins.d/*; do
62
92
    base="`basename \"$file\"`"
63
93
    # Is this plugin overridden?
64
94
    if [ -e "/etc/mandos/plugins.d/$base" ]; then
65
95
        continue
66
96
    fi
67
97
    case "$base" in
68
 
        *~|.*|\#*\#|*.dpkg-old|*.dpkg-new|*.dpkg-divert) : ;;
69
 
        *) copy_exec "$file" "${PLUGINDIR}";;
 
98
        *~|.*|\#*\#|*.dpkg-old|*.dpkg-bak|*.dpkg-new|*.dpkg-divert)
 
99
            : ;;
 
100
        "*") echo "W: Mandos client plugin directory is empty." >&2 ;;
 
101
        *) copy_exec "$file" "${PLUGINDIR}" ;;
 
102
    esac
 
103
done
 
104
 
 
105
# Copy the packaged plugin helpers
 
106
for file in "$libdir"/mandos/plugin-helpers/*; do
 
107
    base="`basename \"$file\"`"
 
108
    # Is this plugin overridden?
 
109
    if [ -e "/etc/mandos/plugin-helpers/$base" ]; then
 
110
        continue
 
111
    fi
 
112
    case "$base" in
 
113
        *~|.*|\#*\#|*.dpkg-old|*.dpkg-bak|*.dpkg-new|*.dpkg-divert)
 
114
            : ;;
 
115
        "*") : ;;
 
116
        *) copy_exec "$file" "${PLUGINHELPERDIR}" ;;
70
117
    esac
71
118
done
72
119
 
74
121
for file in /etc/mandos/plugins.d/*; do
75
122
    base="`basename \"$file\"`"
76
123
    case "$base" in
77
 
        *~|.*|*.dpkg-old|*.dpkg-new|*.dpkg-divert) : ;;
78
 
        *) copy_exec "$file" "${PLUGINDIR}";;
79
 
    esac
80
 
done
81
 
 
82
 
# GPGME needs /usr/bin/gpg
83
 
if ! [ -e "${DESTDIR}/usr/bin/gpg" ] \
84
 
    && [ -n "`ls \"${DESTDIR}\"/usr/lib/libgpgme.so* 2>/dev/null`" ]; then
85
 
    copy_exec /usr/bin/gpg
 
124
        *~|.*|\#*\#|*.dpkg-old|*.dpkg-bak|*.dpkg-new|*.dpkg-divert)
 
125
            : ;;
 
126
        "*") : ;;
 
127
        *) copy_exec "$file" "${PLUGINDIR}" ;;
 
128
    esac
 
129
done
 
130
 
 
131
# Copy any user-supplied plugin helpers
 
132
for file in /etc/mandos/plugin-helpers/*; do
 
133
    base="`basename \"$file\"`"
 
134
    case "$base" in
 
135
        *~|.*|\#*\#|*.dpkg-old|*.dpkg-bak|*.dpkg-new|*.dpkg-divert)
 
136
            : ;;
 
137
        "*") : ;;
 
138
        *) copy_exec "$file" "${PLUGINHELPERDIR}" ;;
 
139
    esac
 
140
done
 
141
 
 
142
# Get DEVICE from initramfs.conf and other files
 
143
. /etc/initramfs-tools/initramfs.conf
 
144
for conf in /etc/initramfs-tools/conf.d/*; do
 
145
    if [ -n "`basename \"$conf\" \
 
146
        | grep '^[[:alnum:]][[:alnum:]\._-]*$' \
 
147
        | grep -v '\.dpkg-.*$'`" ]; then
 
148
        [ -f "${conf}" ] && . "${conf}"
 
149
    fi
 
150
done
 
151
export DEVICE
 
152
 
 
153
# Copy network hooks
 
154
for hook in /etc/mandos/network-hooks.d/*; do
 
155
    case "`basename \"$hook\"`" in
 
156
        "*") continue ;;
 
157
        *[!A-Za-z0-9_.-]*) continue ;;
 
158
        *) test -d "$hook" || copy_exec "$hook" "${HOOKDIR}" ;;
 
159
    esac
 
160
    if [ -x "$hook" ]; then
 
161
        # Copy any files needed by the network hook
 
162
        MANDOSNETHOOKDIR=/etc/mandos/network-hooks.d MODE=files \
 
163
            VERBOSITY=0 "$hook" files | while read -r file target; do
 
164
            if [ ! -e "${file}" ]; then
 
165
                echo "WARNING: file ${file} not found, requested by Mandos network hook '${hook##*/}'" >&2
 
166
            fi
 
167
            if [ -z "${target}" ]; then
 
168
                copy_exec "$file"
 
169
            else
 
170
                copy_exec "$file" "$target"
 
171
            fi
 
172
        done
 
173
        # Copy and load any modules needed by the network hook
 
174
        MANDOSNETHOOKDIR=/etc/mandos/network-hooks.d MODE=modules \
 
175
            VERBOSITY=0 "$hook" modules | while read -r module; do
 
176
            force_load "$module"
 
177
        done
 
178
    fi
 
179
done
 
180
 
 
181
# GPGME needs GnuPG
 
182
gpg=/usr/bin/gpg
 
183
libgpgme11_version="`dpkg-query --showformat='${Version}' --show libgpgme11`"
 
184
if dpkg --compare-versions "$libgpgme11_version" ge 1.5.0-0.1; then
 
185
    if [ -e /usr/bin/gpgconf ]; then
 
186
        if [ ! -e "${DESTDIR}/usr/bin/gpgconf" ]; then
 
187
            copy_exec /usr/bin/gpgconf
 
188
        fi
 
189
        gpg="`/usr/bin/gpgconf|sed --quiet --expression='s/^gpg:[^:]*://p'`"
 
190
        gpgagent="`/usr/bin/gpgconf|sed --quiet --expression='s/^gpg-agent:[^:]*://p'`"
 
191
        # Newer versions of GnuPG 2 requires the gpg-agent binary
 
192
        if [ -e "$gpgagent" ] && [ ! -e "${DESTDIR}$gpgagent" ]; then
 
193
            copy_exec "$gpgagent"
 
194
        fi
 
195
    fi
 
196
elif dpkg --compare-versions "$libgpgme11_version" ge 1.4.1-0.1; then
 
197
    gpg=/usr/bin/gpg2
 
198
fi
 
199
if [ ! -e "${DESTDIR}$gpg" ]; then
 
200
    copy_exec "$gpg"
 
201
fi
 
202
unset gpg
 
203
unset libgpgme11_version
 
204
 
 
205
# Config files
 
206
for file in /etc/mandos/plugin-runner.conf; do
 
207
    if [ -d "$file" ]; then
 
208
        continue
 
209
    fi
 
210
    cp --archive --sparse=always "$file" "${DESTDIR}${CONFDIR}"
 
211
done
 
212
 
 
213
if [ ${mandos_user} != 65534 ]; then
 
214
    sed --in-place --expression="1i--userid=${mandos_user}" \
 
215
        "${DESTDIR}${CONFDIR}/plugin-runner.conf"
 
216
fi
 
217
 
 
218
if [ ${mandos_group} != 65534 ]; then
 
219
    sed --in-place --expression="1i--groupid=${mandos_group}" \
 
220
        "${DESTDIR}${CONFDIR}/plugin-runner.conf"
86
221
fi
87
222
 
88
223
# Key files
89
 
for file in /etc/mandos/*; do
 
224
for file in "$keydir"/*; do
90
225
    if [ -d "$file" ]; then
91
226
        continue
92
227
    fi
93
 
    cp --archive --sparse=always "$file" "${DESTDIR}${CONFDIR}"
 
228
    case "$file" in
 
229
        *~|.*|\#*\#|*.dpkg-old|*.dpkg-bak|*.dpkg-new|*.dpkg-divert)
 
230
            : ;;
 
231
        "*") : ;;
 
232
        *)
 
233
            cp --archive --sparse=always "$file" \
 
234
               "${DESTDIR}${CONFDIR}"
 
235
            chown ${mandos_user}:${mandos_group} \
 
236
                  "${DESTDIR}${CONFDIR}/`basename \"$file\"`"
 
237
            ;;
 
238
    esac
94
239
done
95
 
# Create key ring files
96
 
gpg --no-random-seed-file --quiet --batch --no-tty --armor \
97
 
    --no-default-keyring --no-options --enable-dsa2 \
98
 
    --homedir "${DESTDIR}${CONFDIR}" --no-permission-warning \
99
 
    --trust-model always --import-options import-minimal \
100
 
    --import "${DESTDIR}${CONFDIR}/seckey.txt"
101
 
chown nobody "${DESTDIR}${CONFDIR}/secring.gpg"
 
240
# Use Diffie-Hellman parameters file if available
 
241
if [ -e "${DESTDIR}${CONFDIR}"/dhparams.pem ]; then
 
242
    sed --in-place \
 
243
        --expression="1i--options-for=mandos-client:--dh-params=${CONFDIR}/dhparams.pem" \
 
244
        "${DESTDIR}/${CONFDIR}/plugin-runner.conf"
 
245
fi
102
246
 
103
247
# /lib/mandos/plugin-runner will drop priviliges, but needs access to
104
248
# its plugin directory and its config file.  However, since almost all
109
253
# initrd; it is intended to affect the initrd.img file itself, since
110
254
# it now contains secret key files.  There is, however, no other way
111
255
# to set the permission of the initrd.img file without a race
112
 
# condition.  This umask is set by "initramfs-tools-hook-conf",
113
 
# installed as "/usr/share/initramfs-tools/conf-hooks.d/mandos".)
 
256
# condition.  This umask is set by "initramfs-tools-conf", installed
 
257
# as "/usr/share/initramfs-tools/conf.d/mandos-conf".)
114
258
115
 
for full in "${PLUGINDIR}" "${CONFDIR}"; do
 
259
for full in "${MANDOSDIR}" "${CONFDIR}"; do
116
260
    while [ "$full" != "/" ]; do
117
261
        chmod a+rX "${DESTDIR}$full"
118
262
        full="`dirname \"$full\"`"
122
266
# Reset some other things to sane permissions which we have
123
267
# inadvertently affected with our umask setting.
124
268
for dir in / /bin /etc /keyscripts /sbin /scripts /usr /usr/bin; do
125
 
    chmod a+rX "${DESTDIR}$dir"
 
269
    if [ -d "${DESTDIR}$dir" ]; then
 
270
        chmod a+rX "${DESTDIR}$dir"
 
271
    fi
126
272
done
127
 
for dir in /lib /usr/lib; do
128
 
    chmod --recursive a+rX "${DESTDIR}$dir"
 
273
for dir in "${DESTDIR}"/lib* "${DESTDIR}"/usr/lib*; do
 
274
    if [ -d "$dir" ]; then
 
275
        find "$dir" \! -perm -u+rw,g+r -prune -or \! -type l -print0 \
 
276
            | xargs --null --no-run-if-empty chmod a+rX --
 
277
    fi
129
278
done