/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: 2024-09-09 01:36:41 UTC
  • Revision ID: teddy@recompile.se-20240909013641-6zu6kx2f7meu134k
Make all required directories when installing

When installing into a normal system, one can assume that target
directories, such as /usr/bin, already exists.  But when installing
into a subdirectory for the purpose of creating a package, one cannot
assume that all directories already exist.  Therefore, when
installing, we must not check if any directories exist, and must
instead always create any directories we want to install into.

* Makefile (confdir/mandos.conf, confdir/clients.conf, install-html):
  Use the "-D" option to "install" instead of creating the directory
  separately.
  (install-server): Move creation of $(CONFDIR) down to before it is
  needed.  Don't check if the $(TMPFILES) or $(SYSUSERS) directories
  exist; instead create them by using the "-D" option to "install".
  Create the $(PREFIX)/sbin directory.  Always use
  "--target-directory" if possible; i.e. if the file name is the same.
  Create the $(DBUSPOLICYDIR) and $(DESTDIR)/etc/init.d directories by
  using the "-D" option to "install".  Don't check if the $(SYSTEMD)
  directory exists; instead create it by using the "-D" option to
  "install".  Create the $(DESTDIR)/etc/default and $(MANDIR)/man8
  directories by using the "-D" option to "install".  Create the
  $(MANDIR)/man5 directories explicitly.
  (install-client-nokey): Remove unnecessary creation of the
  $(CONFDIR) directory.  Don't check if the $(SYSUSERS) directory
  exists; instead create it by using the "-D" option to "install".
  Move the "--directory" argument to be the first argument, for
  clarity.  Create the $(PREFIX)/sbin directory.  Use the "-D"
  argument to "install" when installing
  $(INITRAMFSTOOLS)/hooks/mandos,
  $(INITRAMFSTOOLS)/conf.d/mandos-conf,
  $(INITRAMFSTOOLS)/conf-hooks.d/zz-mandos,
  $(INITRAMFSTOOLS)/scripts/init-premount/mandos,
  $(INITRAMFSTOOLS)/scripts/local-premount/mandos,
  $(DRACUTMODULE)/ask-password-mandos.path, and
  $(DRACUTMODULE)/dracut-module/ask-password-mandos.service.  Create
  the $(MANDIR)/man8 directory.

Reported-By: Erich Eckner <erich@eckner.net>
Thanks: Erich Eckner <erich@eckner.net> for analysis

Show diffs side-by-side

added added

removed removed

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