/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 mandos-keygen

  • Committer: Teddy Hogeborn
  • Date: 2015-05-23 20:18:34 UTC
  • mto: This revision was merged to the branch mainline in revision 756.
  • Revision ID: teddy@recompile.se-20150523201834-e89ex4ito93yni8x
mandos: Use multiprocessing module to run checkers.

For a long time, the Mandos server has occasionally logged the message
"ERROR: Child process vanished".  This was never a fatal error, but it
has been annoying and slightly worrying, since a definite cause was
not found.  One potential cause could be the "multiprocessing" and
"subprocess" modules conflicting w.r.t. SIGCHLD.  To avoid this,
change the running of checkers from using subprocess.Popen
asynchronously to instead first create a multiprocessing.Process()
(which is asynchronous) calling a function, and have that function
then call subprocess.call() (which is synchronous).  In this way, the
only thing using any asynchronous subprocesses is the multiprocessing
module.

This makes it necessary to change one small thing in the D-Bus API,
since the subprocesses.call() function does not expose the raw wait(2)
status value.

DBUS-API (CheckerCompleted): Change the second value provided by this
                             D-Bus signal from the raw wait(2) status
                             to the actual terminating signal number.
mandos (subprocess_call_pipe): New function to be called by
                               multiprocessing.Process (starting a
                               separate process).
(Client.last_checker signal): New attribute for signal which
                              terminated last checker.  Like
                              last_checker_status, only not accessible
                              via D-Bus.
(Client.checker_callback): Take new "connection" argument and use it
                           to get returncode; set last_checker_signal.
                           Return False so gobject does not call this
                           callback again.
(Client.start_checker): Start checker using a multiprocessing.Process
                        instead of a subprocess.Popen.
(ClientDBus.checker_callback): Take new "connection" argument.        Call
                               Client.checker_callback early to have
                               it set last_checker_status and
                               last_checker_signal; use those.  Change
                               second value provided to D-Bus signal
                               CheckerCompleted to use
                               last_checker_signal if checker was
                               terminated by signal.
mandos-monitor: Update to reflect DBus API change.
(MandosClientWidget.checker_completed): Take "signal" instead of
                                        "condition" argument.  Use it
                                        accordingly.  Remove dead code
                                        (os.WCOREDUMP case).

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
#!/bin/sh -e
2
2
3
 
# Mandos key generator - create new keys for a Mandos client
4
 
5
 
# Copyright © 2008-2019 Teddy Hogeborn
6
 
# Copyright © 2008-2019 Björn Påhlsson
7
 
8
 
# This file is part of Mandos.
9
 
#
10
 
# Mandos is free software: you can redistribute it and/or modify it
11
 
# under the terms of the GNU General Public License as published by
 
3
# Mandos key generator - create a new OpenPGP key for a Mandos client
 
4
 
5
# Copyright © 2008-2015 Teddy Hogeborn
 
6
# Copyright © 2008-2015 Björn Påhlsson
 
7
 
8
# This program is free software: you can redistribute it and/or modify
 
9
# it under the terms of the GNU General Public License as published by
12
10
# the Free Software Foundation, either version 3 of the License, or
13
11
# (at your option) any later version.
14
12
#
15
 
#     Mandos is distributed in the hope that it will be useful, but
16
 
#     WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
#     This program is distributed in the hope that it will be useful,
 
14
#     but WITHOUT ANY WARRANTY; without even the implied warranty of
17
15
#     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
16
#     GNU General Public License for more details.
19
17
20
18
# You should have received a copy of the GNU General Public License
21
 
# along with Mandos.  If not, see <http://www.gnu.org/licenses/>.
 
19
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
22
20
23
21
# Contact the authors at <mandos@recompile.se>.
24
22
25
23
 
26
 
VERSION="1.8.17"
 
24
VERSION="1.6.9"
27
25
 
28
26
KEYDIR="/etc/keys/mandos"
29
27
KEYTYPE=RSA
34
32
KEYEMAIL=""
35
33
KEYCOMMENT=""
36
34
KEYEXPIRE=0
37
 
TLS_KEYTYPE=ed25519
38
35
FORCE=no
39
36
SSH=yes
40
37
KEYCOMMENT_ORIG="$KEYCOMMENT"
45
42
fi
46
43
 
47
44
# Parse options
48
 
TEMP=`getopt --options vhpF:d:t:l:s:L:n:e:c:x:T:fS \
49
 
    --longoptions version,help,password,passfile:,dir:,type:,length:,subtype:,sublength:,name:,email:,comment:,expire:,tls-keytype:,force,no-ssh \
 
45
TEMP=`getopt --options vhpF:d:t:l:s:L:n:e:c:x:fS \
 
46
    --longoptions version,help,password,passfile:,dir:,type:,length:,subtype:,sublength:,name:,email:,comment:,expire:,force,no-ssh \
50
47
    --name "$0" -- "$@"`
51
48
 
52
49
help(){
64
61
  -v, --version         Show program's version number and exit
65
62
  -h, --help            Show this help message and exit
66
63
  -d DIR, --dir DIR     Target directory for key files
67
 
  -t TYPE, --type TYPE  OpenPGP key type.  Default is RSA.
 
64
  -t TYPE, --type TYPE  Key type.  Default is RSA.
68
65
  -l BITS, --length BITS
69
 
                        OpenPGP key length in bits.  Default is 4096.
 
66
                        Key length in bits.  Default is 4096.
70
67
  -s TYPE, --subtype TYPE
71
 
                        OpenPGP subkey type.  Default is RSA.
 
68
                        Subkey type.  Default is RSA.
72
69
  -L BITS, --sublength BITS
73
 
                        OpenPGP subkey length in bits.  Default 4096.
 
70
                        Subkey length in bits.  Default is 4096.
74
71
  -n NAME, --name NAME  Name of key.  Default is the FQDN.
75
72
  -e ADDRESS, --email ADDRESS
76
 
                        Email address of OpenPGP key.  Default empty.
 
73
                        Email address of key.  Default is empty.
77
74
  -c TEXT, --comment TEXT
78
 
                        Comment field for OpenPGP key.  Default empty.
 
75
                        Comment field for key.  The default is empty.
79
76
  -x TIME, --expire TIME
80
 
                        OpenPGP key expire time.  Default is none.
 
77
                        Key expire time.  Default is no expiration.
81
78
                        See gpg(1) for syntax.
82
 
  -T TYPE, --tls-keytype TYPE
83
 
                        TLS key type.  Default is ed25519.
84
79
  -f, --force           Force overwriting old key files.
85
80
 
86
81
Password creation options:
109
104
        -e|--email) KEYEMAIL="$2"; shift 2;;
110
105
        -c|--comment) KEYCOMMENT="$2"; shift 2;;
111
106
        -x|--expire) KEYEXPIRE="$2"; shift 2;;
112
 
        -T|--tls-keytype) TLS_KEYTYPE="$2"; shift 2;;
113
107
        -f|--force) FORCE=yes; shift;;
114
108
        -S|--no-ssh) SSH=no; shift;;
115
109
        -v|--version) echo "$0 $VERSION"; exit;;
125
119
 
126
120
SECKEYFILE="$KEYDIR/seckey.txt"
127
121
PUBKEYFILE="$KEYDIR/pubkey.txt"
128
 
TLS_PRIVKEYFILE="$KEYDIR/tls-privkey.pem"
129
 
TLS_PUBKEYFILE="$KEYDIR/tls-pubkey.pem"
130
122
 
131
123
# Check for some invalid values
132
124
if [ ! -d "$KEYDIR" ]; then
147
139
        echo "Empty key type" >&2
148
140
        exit 1
149
141
    fi
150
 
 
 
142
    
151
143
    if [ -z "$KEYNAME" ]; then
152
144
        echo "Empty key name" >&2
153
145
        exit 1
154
146
    fi
155
 
 
 
147
    
156
148
    if [ -z "$KEYLENGTH" ] || [ "$KEYLENGTH" -lt 512 ]; then
157
149
        echo "Invalid key length" >&2
158
150
        exit 1
159
151
    fi
160
 
 
 
152
    
161
153
    if [ -z "$KEYEXPIRE" ]; then
162
154
        echo "Empty key expiration" >&2
163
155
        exit 1
164
156
    fi
165
 
 
 
157
    
166
158
    # Make FORCE be 0 or 1
167
159
    case "$FORCE" in
168
160
        [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]) FORCE=1;;
169
161
        [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|*) FORCE=0;;
170
162
    esac
171
 
 
172
 
    if { [ -e "$SECKEYFILE" ] || [ -e "$PUBKEYFILE" ] \
173
 
             || [ -e "$TLS_PRIVKEYFILE" ] \
174
 
             || [ -e "$TLS_PUBKEYFILE" ]; } \
175
 
        && [ "$FORCE" -eq 0 ]; then
 
163
    
 
164
    if [ \( -e "$SECKEYFILE" -o -e "$PUBKEYFILE" \) \
 
165
        -a "$FORCE" -eq 0 ]; then
176
166
        echo "Refusing to overwrite old key files; use --force" >&2
177
167
        exit 1
178
168
    fi
179
 
 
 
169
    
180
170
    # Set lines for GnuPG batch file
181
171
    if [ -n "$KEYCOMMENT" ]; then
182
172
        KEYCOMMENTLINE="Name-Comment: $KEYCOMMENT"
184
174
    if [ -n "$KEYEMAIL" ]; then
185
175
        KEYEMAILLINE="Name-Email: $KEYEMAIL"
186
176
    fi
187
 
 
 
177
    
188
178
    # Create temporary gpg batch file
189
179
    BATCHFILE="`mktemp -t mandos-keygen-batch.XXXXXXXXXX`"
190
 
    TLS_PRIVKEYTMP="`mktemp -t mandos-keygen-privkey.XXXXXXXXXX`"
191
180
fi
192
181
 
193
182
if [ "$mode" = password ]; then
202
191
trap "
203
192
set +e; \
204
193
test -n \"$SECFILE\" && shred --remove \"$SECFILE\"; \
205
 
test -n \"$TLS_PRIVKEYTMP\" && shred --remove \"$TLS_PRIVKEYTMP\"; \
206
194
shred --remove \"$RINGDIR\"/sec* 2>/dev/null;
207
195
test -n \"$BATCHFILE\" && rm --force \"$BATCHFILE\"; \
208
196
rm --recursive --force \"$RINGDIR\";
230
218
        #Handle: <no-spaces>
231
219
        #%pubring pubring.gpg
232
220
        #%secring secring.gpg
233
 
        %no-protection
234
221
        %commit
235
222
        EOF
236
 
 
 
223
    
237
224
    if tty --quiet; then
238
225
        cat <<-EOF
239
226
        Note: Due to entropy requirements, key generation could take
243
230
        echo -n "Started: "
244
231
        date
245
232
    fi
246
 
 
247
 
    # Generate TLS private key
248
 
    if certtool --generate-privkey --password='' \
249
 
                --outfile "$TLS_PRIVKEYTMP" --sec-param ultra \
250
 
                --key-type="$TLS_KEYTYPE" --pkcs8 --no-text 2>/dev/null; then
251
 
        
252
 
        # Backup any old key files
253
 
        if cp --backup=numbered --force "$TLS_PRIVKEYFILE" "$TLS_PRIVKEYFILE" \
254
 
              2>/dev/null; then
255
 
            shred --remove "$TLS_PRIVKEYFILE" 2>/dev/null || :
256
 
        fi
257
 
        if cp --backup=numbered --force "$TLS_PUBKEYFILE" "$TLS_PUBKEYFILE" \
258
 
              2>/dev/null; then
259
 
            rm --force "$TLS_PUBKEYFILE"
260
 
        fi
261
 
        cp --archive "$TLS_PRIVKEYTMP" "$TLS_PRIVKEYFILE"
262
 
        shred --remove "$TLS_PRIVKEYTMP" 2>/dev/null || :
263
 
 
264
 
        ## TLS public key
265
 
 
266
 
        # First try certtool from GnuTLS
267
 
        if ! certtool --password='' --load-privkey="$TLS_PRIVKEYFILE" \
268
 
             --outfile="$TLS_PUBKEYFILE" --pubkey-info --no-text \
269
 
             2>/dev/null; then
270
 
            # Otherwise try OpenSSL
271
 
            if ! openssl pkey -in "$TLS_PRIVKEYFILE" \
272
 
                 -out "$TLS_PUBKEYFILE" -pubout; then
273
 
                rm --force "$TLS_PUBKEYFILE"
274
 
                # None of the commands succeded; give up
275
 
                return 1
276
 
            fi
277
 
        fi
278
 
    fi
279
 
 
 
233
    
280
234
    # Make sure trustdb.gpg exists;
281
235
    # this is a workaround for Debian bug #737128
282
236
    gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
287
241
        --homedir "$RINGDIR" --trust-model always \
288
242
        --gen-key "$BATCHFILE"
289
243
    rm --force "$BATCHFILE"
290
 
 
 
244
    
291
245
    if tty --quiet; then
292
246
        echo -n "Finished: "
293
247
        date
294
248
    fi
295
 
 
 
249
    
296
250
    # Backup any old key files
297
251
    if cp --backup=numbered --force "$SECKEYFILE" "$SECKEYFILE" \
298
252
        2>/dev/null; then
299
 
        shred --remove "$SECKEYFILE" 2>/dev/null || :
 
253
        shred --remove "$SECKEYFILE"
300
254
    fi
301
255
    if cp --backup=numbered --force "$PUBKEYFILE" "$PUBKEYFILE" \
302
256
        2>/dev/null; then
303
257
        rm --force "$PUBKEYFILE"
304
258
    fi
305
 
 
 
259
    
306
260
    FILECOMMENT="Mandos client key for $KEYNAME"
307
261
    if [ "$KEYCOMMENT" != "$KEYCOMMENT_ORIG" ]; then
308
262
        FILECOMMENT="$FILECOMMENT ($KEYCOMMENT)"
309
263
    fi
310
 
 
 
264
    
311
265
    if [ -n "$KEYEMAIL" ]; then
312
266
        FILECOMMENT="$FILECOMMENT <$KEYEMAIL>"
313
267
    fi
314
 
 
 
268
    
315
269
    # Export key from key rings to key files
316
270
    gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
317
271
        --homedir "$RINGDIR" --armor --export-options export-minimal \
323
277
fi
324
278
 
325
279
if [ "$mode" = password ]; then
326
 
 
 
280
    
327
281
    # Make SSH be 0 or 1
328
282
    case "$SSH" in
329
283
        [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]) SSH=1;;
330
284
        [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|*) SSH=0;;
331
285
    esac
332
 
 
 
286
    
333
287
    if [ $SSH -eq 1 ]; then
334
 
        # The -q option is new in OpenSSH 9.8
335
 
        for ssh_keyscan_quiet in "-q " ""; do
336
 
            for ssh_keytype in ecdsa-sha2-nistp256 ed25519 rsa; do
337
 
                set +e
338
 
                ssh_fingerprint="`ssh-keyscan ${ssh_keyscan_quiet}-t $ssh_keytype localhost 2>/dev/null`"
339
 
                err=$?
340
 
                set -e
341
 
                if [ $err -ne 0 ]; then
342
 
                    ssh_fingerprint=""
343
 
                    continue
344
 
                fi
345
 
                if [ -n "$ssh_fingerprint" ]; then
346
 
                    ssh_fingerprint="${ssh_fingerprint#localhost }"
347
 
                    break 2
348
 
                fi
349
 
            done
 
288
        for ssh_keytype in ed25519 rsa; do
 
289
            set +e
 
290
            ssh_fingerprint="`ssh-keyscan -t $ssh_keytype localhost 2>/dev/null`"
 
291
            set -e
 
292
            if [ $? -ne 0 ]; then
 
293
                ssh_fingerprint=""
 
294
                continue
 
295
            fi
 
296
            if [ -n "$ssh_fingerprint" ]; then
 
297
                ssh_fingerprint="${ssh_fingerprint#localhost }"
 
298
                break
 
299
            fi
350
300
        done
351
301
    fi
352
 
 
 
302
    
353
303
    # Import key into temporary key rings
354
304
    gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
355
305
        --homedir "$RINGDIR" --trust-model always --armor \
357
307
    gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
358
308
        --homedir "$RINGDIR" --trust-model always --armor \
359
309
        --import "$PUBKEYFILE"
360
 
 
 
310
    
361
311
    # Get fingerprint of key
362
312
    FINGERPRINT="`gpg --quiet --batch --no-tty --no-options \
363
313
        --enable-dsa2 --homedir "$RINGDIR" --trust-model always \
364
314
        --fingerprint --with-colons \
365
315
        | sed --quiet \
366
316
        --expression='/^fpr:/{s/^fpr:.*:\\([0-9A-Z]*\\):\$/\\1/p;q}'`"
367
 
 
 
317
    
368
318
    test -n "$FINGERPRINT"
369
 
 
370
 
    if [ -r "$TLS_PUBKEYFILE" ]; then
371
 
       KEY_ID="$(certtool --key-id --hash=sha256 \
372
 
                       --infile="$TLS_PUBKEYFILE" 2>/dev/null || :)"
373
 
 
374
 
       if [ -z "$KEY_ID" ]; then
375
 
           KEY_ID=$(openssl pkey -pubin -in "$TLS_PUBKEYFILE" \
376
 
                            -outform der \
377
 
                        | openssl sha256 \
378
 
                        | sed --expression='s/^.*[^[:xdigit:]]//')
379
 
       fi
380
 
       test -n "$KEY_ID"
381
 
    fi
382
 
 
 
319
    
383
320
    FILECOMMENT="Encrypted password for a Mandos client"
384
 
 
 
321
    
385
322
    while [ ! -s "$SECFILE" ]; do
386
323
        if [ -n "$PASSFILE" ]; then
387
 
            cat -- "$PASSFILE"
 
324
            cat "$PASSFILE"
388
325
        else
389
326
            tty --quiet && stty -echo
390
 
            echo -n "Enter passphrase: " >/dev/tty
391
 
            read -r first
 
327
            echo -n "Enter passphrase: " >&2
 
328
            read first
392
329
            tty --quiet && echo >&2
393
 
            echo -n "Repeat passphrase: " >/dev/tty
394
 
            read -r second
 
330
            echo -n "Repeat passphrase: " >&2
 
331
            read second
395
332
            if tty --quiet; then
396
333
                echo >&2
397
334
                stty echo
400
337
                echo "Passphrase mismatch" >&2
401
338
                touch "$RINGDIR"/mismatch
402
339
            else
403
 
                printf "%s" "$first"
 
340
                echo -n "$first"
404
341
            fi
405
342
        fi | gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
406
343
            --homedir "$RINGDIR" --trust-model always --armor \
415
352
            fi
416
353
        fi
417
354
    done
418
 
 
 
355
    
419
356
    cat <<-EOF
420
357
        [$KEYNAME]
421
358
        host = $KEYNAME
422
 
        EOF
423
 
    if [ -n "$KEY_ID" ]; then
424
 
        echo "key_id = $KEY_ID"
425
 
    fi
426
 
    cat <<-EOF
427
359
        fingerprint = $FINGERPRINT
428
360
        secret =
429
361
        EOF
437
369
            }
438
370
        }' < "$SECFILE"
439
371
    if [ -n "$ssh_fingerprint" ]; then
440
 
        if [ -n "$ssh_keyscan_quiet" ]; then
441
 
            echo "# Note: if the Mandos server has OpenSSH older than 9.8, the ${ssh_keyscan_quiet}"
442
 
            echo "# option *must* be removed from the 'checker' setting below"
443
 
        fi
444
 
        echo 'checker = ssh-keyscan '"$ssh_keyscan_quiet"'-t '"$ssh_keytype"' %%(host)s 2>/dev/null | grep --fixed-strings --line-regexp --quiet --regexp=%%(host)s" %(ssh_fingerprint)s"'
 
372
        echo 'checker = ssh-keyscan -t '"$ssh_keytype"' %%(host)s 2>/dev/null | grep --fixed-strings --line-regexp --quiet --regexp=%%(host)s" %(ssh_fingerprint)s"'
445
373
        echo "ssh_fingerprint = ${ssh_fingerprint}"
446
374
    fi
447
375
fi
451
379
set +e
452
380
# Remove the password file, if any
453
381
if [ -n "$SECFILE" ]; then
454
 
    shred --remove "$SECFILE" 2>/dev/null
 
382
    shred --remove "$SECFILE"
455
383
fi
456
384
# Remove the key rings
457
385
shred --remove "$RINGDIR"/sec* 2>/dev/null