/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 04:24:39 UTC
  • Revision ID: teddy@recompile.se-20240909042439-j85mr20uli2hnyis
Eliminate compiler warnings

Many programs use nested functions, which now result in a linker
warning about executable stack.  Hide this warning.  Also, rewrite a
loop in the plymouth plugin to avoid warning about signed overflow.
This change also makes the plugin pick the alphabetically first
process entry instead of the last, in case many plymouth processes are
found (which should be unlikely).

* Makefile (plugin-runner, dracut-module/password-agent,
  plugins.d/password-prompt, plugins.d/mandos-client,
  plugins.d/plymouth): New target; set LDFLAGS to add "-Xlinker
  --no-warn-execstack".
* plugins.d/plymouth.c (get_pid): When no pid files are found, and we
  are looking through the process list, go though it from the start
  instead of from the end, i.e. in normal alphabetical order and not
  in reverse order.

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 © 2007-2008 Teddy Hogeborn & Björn Påhlsson
6
 
7
 
# This program is free software: you can redistribute it and/or modify
8
 
# 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
9
12
# the Free Software Foundation, either version 3 of the License, or
10
13
# (at your option) any later version.
11
14
#
12
 
#     This program is distributed in the hope that it will be useful,
13
 
#     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
14
17
#     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
18
#     GNU General Public License for more details.
16
19
17
20
# You should have received a copy of the GNU General Public License
18
 
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
21
# along with Mandos.  If not, see <http://www.gnu.org/licenses/>.
19
22
20
 
# Contact the authors at <mandos@fukt.bsnet.se>.
 
23
# Contact the authors at <mandos@recompile.se>.
21
24
22
25
 
23
 
VERSION="1.0"
 
26
VERSION="1.8.16"
24
27
 
25
28
KEYDIR="/etc/keys/mandos"
26
 
KEYTYPE=DSA
27
 
KEYLENGTH=2048
28
 
SUBKEYTYPE=ELG-E
29
 
SUBKEYLENGTH=2048
30
 
KEYNAME="`hostname --fqdn`"
 
29
KEYTYPE=RSA
 
30
KEYLENGTH=4096
 
31
SUBKEYTYPE=RSA
 
32
SUBKEYLENGTH=4096
 
33
KEYNAME="`hostname --fqdn 2>/dev/null || hostname`"
31
34
KEYEMAIL=""
32
 
KEYCOMMENT="Mandos client key"
 
35
KEYCOMMENT=""
33
36
KEYEXPIRE=0
 
37
TLS_KEYTYPE=ed25519
34
38
FORCE=no
 
39
SSH=yes
35
40
KEYCOMMENT_ORIG="$KEYCOMMENT"
36
41
mode=keygen
37
42
 
 
43
if [ ! -d "$KEYDIR" ]; then
 
44
    KEYDIR="/etc/mandos/keys"
 
45
fi
 
46
 
38
47
# Parse options
39
 
TEMP=`getopt --options vhd:t:l:n:e:c:x:f \
40
 
    --longoptions version,help,password,dir:,type:,length:,subtype:,sublength:,name:,email:,comment:,expire:,force \
 
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 \
41
50
    --name "$0" -- "$@"`
42
51
 
43
52
help(){
44
 
basename="`basename $0`"
 
53
basename="`basename "$0"`"
45
54
cat <<EOF
46
55
Usage: $basename [ -v | --version ]
47
56
       $basename [ -h | --help ]
49
58
       $basename [ OPTIONS ]
50
59
   Encrypted password creation:
51
60
       $basename { -p | --password } [ --name NAME ] [ --dir DIR]
 
61
       $basename { -F | --passfile } FILE [ --name NAME ] [ --dir DIR]
52
62
 
53
63
Key creation options:
54
64
  -v, --version         Show program's version number and exit
55
65
  -h, --help            Show this help message and exit
56
66
  -d DIR, --dir DIR     Target directory for key files
57
 
  -t TYPE, --type TYPE  Key type.  Default is DSA.
 
67
  -t TYPE, --type TYPE  OpenPGP key type.  Default is RSA.
58
68
  -l BITS, --length BITS
59
 
                        Key length in bits.  Default is 2048.
 
69
                        OpenPGP key length in bits.  Default is 4096.
60
70
  -s TYPE, --subtype TYPE
61
 
                        Subkey type.  Default is ELG-E.
 
71
                        OpenPGP subkey type.  Default is RSA.
62
72
  -L BITS, --sublength BITS
63
 
                        Subkey length in bits.  Default is 2048.
 
73
                        OpenPGP subkey length in bits.  Default 4096.
64
74
  -n NAME, --name NAME  Name of key.  Default is the FQDN.
65
75
  -e ADDRESS, --email ADDRESS
66
 
                        Email address of key.  Default is empty.
 
76
                        Email address of OpenPGP key.  Default empty.
67
77
  -c TEXT, --comment TEXT
68
 
                        Comment field for key.  The default value is
69
 
                        "Mandos client key".
 
78
                        Comment field for OpenPGP key.  Default empty.
70
79
  -x TIME, --expire TIME
71
 
                        Key expire time.  Default is no expiration.
 
80
                        OpenPGP key expire time.  Default is none.
72
81
                        See gpg(1) for syntax.
73
 
  -f, --force           Force overwriting old keys.
 
82
  -T TYPE, --tls-keytype TYPE
 
83
                        TLS key type.  Default is ed25519.
 
84
  -f, --force           Force overwriting old key files.
74
85
 
75
86
Password creation options:
76
 
  -p, --password        Create an encrypted password using the keys in
77
 
                        the key directory.  All options other than
78
 
                        --dir and --name are ignored.
 
87
  -p, --password        Create an encrypted password using the key in
 
88
                        the key directory.  All options other than
 
89
                        --dir and --name are ignored.
 
90
  -F FILE, --passfile FILE
 
91
                        Encrypt a password from FILE using the key in
 
92
                        the key directory.  All options other than
 
93
                        --dir and --name are ignored.
 
94
  -S, --no-ssh          Don't get SSH key or set "checker" option.
79
95
EOF
80
96
}
81
97
 
83
99
while :; do
84
100
    case "$1" in
85
101
        -p|--password) mode=password; shift;;
 
102
        -F|--passfile) mode=password; PASSFILE="$2"; shift 2;;
86
103
        -d|--dir) KEYDIR="$2"; shift 2;;
87
104
        -t|--type) KEYTYPE="$2"; shift 2;;
88
105
        -s|--subtype) SUBKEYTYPE="$2"; shift 2;;
92
109
        -e|--email) KEYEMAIL="$2"; shift 2;;
93
110
        -c|--comment) KEYCOMMENT="$2"; shift 2;;
94
111
        -x|--expire) KEYEXPIRE="$2"; shift 2;;
 
112
        -T|--tls-keytype) TLS_KEYTYPE="$2"; shift 2;;
95
113
        -f|--force) FORCE=yes; shift;;
 
114
        -S|--no-ssh) SSH=no; shift;;
96
115
        -v|--version) echo "$0 $VERSION"; exit;;
97
116
        -h|--help) help; exit;;
98
117
        --) shift; break;;
100
119
    esac
101
120
done
102
121
if [ "$#" -gt 0 ]; then
103
 
    echo "Unknown arguments: '$@'" >&2
 
122
    echo "Unknown arguments: '$*'" >&2
104
123
    exit 1
105
124
fi
106
125
 
107
126
SECKEYFILE="$KEYDIR/seckey.txt"
108
127
PUBKEYFILE="$KEYDIR/pubkey.txt"
 
128
TLS_PRIVKEYFILE="$KEYDIR/tls-privkey.pem"
 
129
TLS_PUBKEYFILE="$KEYDIR/tls-pubkey.pem"
109
130
 
110
131
# Check for some invalid values
111
132
if [ ! -d "$KEYDIR" ]; then
126
147
        echo "Empty key type" >&2
127
148
        exit 1
128
149
    fi
129
 
    
 
150
 
130
151
    if [ -z "$KEYNAME" ]; then
131
152
        echo "Empty key name" >&2
132
153
        exit 1
133
154
    fi
134
 
    
 
155
 
135
156
    if [ -z "$KEYLENGTH" ] || [ "$KEYLENGTH" -lt 512 ]; then
136
157
        echo "Invalid key length" >&2
137
158
        exit 1
141
162
        echo "Empty key expiration" >&2
142
163
        exit 1
143
164
    fi
144
 
    
 
165
 
145
166
    # Make FORCE be 0 or 1
146
167
    case "$FORCE" in
147
168
        [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]) FORCE=1;;
148
169
        [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|*) FORCE=0;;
149
170
    esac
150
 
    
151
 
    if { [ -e "$SECKEYFILE" ] || [ -e "$PUBKEYFILE" ]; } \
 
171
 
 
172
    if { [ -e "$SECKEYFILE" ] || [ -e "$PUBKEYFILE" ] \
 
173
             || [ -e "$TLS_PRIVKEYFILE" ] \
 
174
             || [ -e "$TLS_PUBKEYFILE" ]; } \
152
175
        && [ "$FORCE" -eq 0 ]; then
153
176
        echo "Refusing to overwrite old key files; use --force" >&2
154
177
        exit 1
155
178
    fi
156
 
    
 
179
 
157
180
    # Set lines for GnuPG batch file
158
181
    if [ -n "$KEYCOMMENT" ]; then
159
182
        KEYCOMMENTLINE="Name-Comment: $KEYCOMMENT"
164
187
 
165
188
    # Create temporary gpg batch file
166
189
    BATCHFILE="`mktemp -t mandos-keygen-batch.XXXXXXXXXX`"
 
190
    TLS_PRIVKEYTMP="`mktemp -t mandos-keygen-privkey.XXXXXXXXXX`"
167
191
fi
168
192
 
169
193
if [ "$mode" = password ]; then
178
202
trap "
179
203
set +e; \
180
204
test -n \"$SECFILE\" && shred --remove \"$SECFILE\"; \
181
 
shred --remove \"$RINGDIR\"/sec*;
 
205
test -n \"$TLS_PRIVKEYTMP\" && shred --remove \"$TLS_PRIVKEYTMP\"; \
 
206
shred --remove \"$RINGDIR\"/sec* 2>/dev/null;
182
207
test -n \"$BATCHFILE\" && rm --force \"$BATCHFILE\"; \
183
208
rm --recursive --force \"$RINGDIR\";
184
 
stty echo; \
 
209
tty --quiet && stty echo; \
185
210
" EXIT
186
211
 
187
 
umask 027
 
212
set -e
 
213
 
 
214
umask 077
188
215
 
189
216
if [ "$mode" = keygen ]; then
190
217
    # Create batch file for GnuPG
191
218
    cat >"$BATCHFILE" <<-EOF
192
219
        Key-Type: $KEYTYPE
193
220
        Key-Length: $KEYLENGTH
194
 
        #Key-Usage: encrypt,sign,auth
 
221
        Key-Usage: sign,auth
195
222
        Subkey-Type: $SUBKEYTYPE
196
223
        Subkey-Length: $SUBKEYLENGTH
197
 
        #Subkey-Usage: encrypt,sign,auth
 
224
        Subkey-Usage: encrypt
198
225
        Name-Real: $KEYNAME
199
226
        $KEYCOMMENTLINE
200
227
        $KEYEMAILLINE
203
230
        #Handle: <no-spaces>
204
231
        #%pubring pubring.gpg
205
232
        #%secring secring.gpg
 
233
        %no-protection
206
234
        %commit
207
235
        EOF
208
 
    
 
236
 
 
237
    if tty --quiet; then
 
238
        cat <<-EOF
 
239
        Note: Due to entropy requirements, key generation could take
 
240
        anything from a few minutes to SEVERAL HOURS.  Please be
 
241
        patient and/or supply the system with more entropy if needed.
 
242
        EOF
 
243
        echo -n "Started: "
 
244
        date
 
245
    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
 
 
280
    # Make sure trustdb.gpg exists;
 
281
    # this is a workaround for Debian bug #737128
 
282
    gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
 
283
        --homedir "$RINGDIR" \
 
284
        --import-ownertrust < /dev/null
209
285
    # Generate a new key in the key rings
210
286
    gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
211
287
        --homedir "$RINGDIR" --trust-model always \
212
288
        --gen-key "$BATCHFILE"
213
289
    rm --force "$BATCHFILE"
214
 
    
 
290
 
 
291
    if tty --quiet; then
 
292
        echo -n "Finished: "
 
293
        date
 
294
    fi
 
295
 
215
296
    # Backup any old key files
216
297
    if cp --backup=numbered --force "$SECKEYFILE" "$SECKEYFILE" \
217
298
        2>/dev/null; then
218
 
        shred --remove "$SECKEYFILE"
 
299
        shred --remove "$SECKEYFILE" 2>/dev/null || :
219
300
    fi
220
301
    if cp --backup=numbered --force "$PUBKEYFILE" "$PUBKEYFILE" \
221
302
        2>/dev/null; then
222
303
        rm --force "$PUBKEYFILE"
223
304
    fi
224
 
    
 
305
 
225
306
    FILECOMMENT="Mandos client key for $KEYNAME"
226
307
    if [ "$KEYCOMMENT" != "$KEYCOMMENT_ORIG" ]; then
227
308
        FILECOMMENT="$FILECOMMENT ($KEYCOMMENT)"
228
309
    fi
229
 
    
 
310
 
230
311
    if [ -n "$KEYEMAIL" ]; then
231
312
        FILECOMMENT="$FILECOMMENT <$KEYEMAIL>"
232
313
    fi
233
 
    
234
 
    # Export keys from key rings to key files
 
314
 
 
315
    # Export key from key rings to key files
235
316
    gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
236
317
        --homedir "$RINGDIR" --armor --export-options export-minimal \
237
318
        --comment "$FILECOMMENT" --output "$SECKEYFILE" \
242
323
fi
243
324
 
244
325
if [ "$mode" = password ]; then
245
 
    # Import keys into temporary key rings
 
326
 
 
327
    # Make SSH be 0 or 1
 
328
    case "$SSH" in
 
329
        [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]) SSH=1;;
 
330
        [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|*) SSH=0;;
 
331
    esac
 
332
 
 
333
    if [ $SSH -eq 1 ]; then
 
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
 
348
    fi
 
349
 
 
350
    # Import key into temporary key rings
246
351
    gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
247
352
        --homedir "$RINGDIR" --trust-model always --armor \
248
353
        --import "$SECKEYFILE"
249
354
    gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
250
355
        --homedir "$RINGDIR" --trust-model always --armor \
251
356
        --import "$PUBKEYFILE"
252
 
    
 
357
 
253
358
    # Get fingerprint of key
254
359
    FINGERPRINT="`gpg --quiet --batch --no-tty --no-options \
255
 
        --enable-dsa2 --homedir \"$RINGDIR\" --trust-model always \
 
360
        --enable-dsa2 --homedir "$RINGDIR" --trust-model always \
256
361
        --fingerprint --with-colons \
257
 
        | sed -n -e '/^fpr:/{s/^fpr:.*:\\([0-9A-Z]*\\):\$/\\1/p;q}'`"
258
 
    
 
362
        | sed --quiet \
 
363
        --expression='/^fpr:/{s/^fpr:.*:\\([0-9A-Z]*\\):\$/\\1/p;q}'`"
 
364
 
259
365
    test -n "$FINGERPRINT"
260
 
    
 
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
 
261
380
    FILECOMMENT="Encrypted password for a Mandos client"
262
 
    
263
 
    stty -echo
264
 
    echo -n "Enter passphrase: " >&2
265
 
    sed -e '1q' \
266
 
        | gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
267
 
        --homedir "$RINGDIR" --trust-model always --armor --encrypt \
268
 
        --recipient "$FINGERPRINT" --comment "$FILECOMMENT" \
269
 
        > "$SECFILE"
270
 
    echo >&2
271
 
    stty echo
272
 
    
 
381
 
 
382
    while [ ! -s "$SECFILE" ]; do
 
383
        if [ -n "$PASSFILE" ]; then
 
384
            cat -- "$PASSFILE"
 
385
        else
 
386
            tty --quiet && stty -echo
 
387
            echo -n "Enter passphrase: " >/dev/tty
 
388
            read -r first
 
389
            tty --quiet && echo >&2
 
390
            echo -n "Repeat passphrase: " >/dev/tty
 
391
            read -r second
 
392
            if tty --quiet; then
 
393
                echo >&2
 
394
                stty echo
 
395
            fi
 
396
            if [ "$first" != "$second" ]; then
 
397
                echo "Passphrase mismatch" >&2
 
398
                touch "$RINGDIR"/mismatch
 
399
            else
 
400
                printf "%s" "$first"
 
401
            fi
 
402
        fi | gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
 
403
            --homedir "$RINGDIR" --trust-model always --armor \
 
404
            --encrypt --sign --recipient "$FINGERPRINT" --comment \
 
405
            "$FILECOMMENT" > "$SECFILE"
 
406
        if [ -e "$RINGDIR"/mismatch ]; then
 
407
            rm --force "$RINGDIR"/mismatch
 
408
            if tty --quiet; then
 
409
                > "$SECFILE"
 
410
            else
 
411
                exit 1
 
412
            fi
 
413
        fi
 
414
    done
 
415
 
273
416
    cat <<-EOF
274
417
        [$KEYNAME]
275
418
        host = $KEYNAME
 
419
        EOF
 
420
    if [ -n "$KEY_ID" ]; then
 
421
        echo "key_id = $KEY_ID"
 
422
    fi
 
423
    cat <<-EOF
276
424
        fingerprint = $FINGERPRINT
277
425
        secret =
278
 
EOF
279
 
    sed -n -e '
 
426
        EOF
 
427
    sed --quiet --expression='
280
428
        /^-----BEGIN PGP MESSAGE-----$/,/^-----END PGP MESSAGE-----$/{
281
429
            /^$/,${
282
430
                # Remove 24-bit Radix-64 checksum
285
433
                /^[^-]/s/^/    /p
286
434
            }
287
435
        }' < "$SECFILE"
 
436
    if [ -n "$ssh_fingerprint" ]; then
 
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"'
 
438
        echo "ssh_fingerprint = ${ssh_fingerprint}"
 
439
    fi
288
440
fi
289
441
 
290
442
trap - EXIT
292
444
set +e
293
445
# Remove the password file, if any
294
446
if [ -n "$SECFILE" ]; then
295
 
    shred --remove "$SECFILE"
 
447
    shred --remove "$SECFILE" 2>/dev/null
296
448
fi
297
449
# Remove the key rings
298
 
shred --remove "$RINGDIR"/sec*
 
450
shred --remove "$RINGDIR"/sec* 2>/dev/null
299
451
rm --recursive --force "$RINGDIR"