/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-11-17 18:43:11 UTC
  • Revision ID: teddy@recompile.se-20241117184311-ox25kvngy62h209g
Debian package: Avoid suggesting a C compiler unnecessarily

The list of suggested packages, meant to enable the "mandos" program
to find the correct value of SO_BINDTODEVICE by using a C compiler,
are not necessary when Python 3.3 or later is used, since it has the
SO_BINDTODEVICE constant defined in the "socket" module.  Also, Python
2.6 or older has the same constant in the old "IN" module.  Therefore,
we should suggest these Python versions as alternatives to a C
compiler, so that a C compiler is not installed unnecessarily.

debian/control (Package: mandos/Suggests): Add "python3 (>= 3.3)" and
"python (<= 2.6)" as alternatives to "libc6-dev | libc-dev" and
"c-compiler".

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-2011 Teddy Hogeborn
6
 
# Copyright © 2008-2011 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.4.1"
 
26
VERSION="1.8.17"
25
27
 
26
28
KEYDIR="/etc/keys/mandos"
27
 
KEYTYPE=DSA
28
 
KEYLENGTH=2048
29
 
SUBKEYTYPE=ELG-E
30
 
SUBKEYLENGTH=2048
 
29
KEYTYPE=RSA
 
30
KEYLENGTH=4096
 
31
SUBKEYTYPE=RSA
 
32
SUBKEYLENGTH=4096
31
33
KEYNAME="`hostname --fqdn 2>/dev/null || hostname`"
32
34
KEYEMAIL=""
33
 
KEYCOMMENT="Mandos client key"
 
35
KEYCOMMENT=""
34
36
KEYEXPIRE=0
 
37
TLS_KEYTYPE=ed25519
35
38
FORCE=no
 
39
SSH=yes
36
40
KEYCOMMENT_ORIG="$KEYCOMMENT"
37
41
mode=keygen
38
42
 
41
45
fi
42
46
 
43
47
# Parse options
44
 
TEMP=`getopt --options vhpF:d:t:l:s:L:n:e:c:x:f \
45
 
    --longoptions version,help,password,passfile:,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 \
46
50
    --name "$0" -- "$@"`
47
51
 
48
52
help(){
49
 
basename="`basename $0`"
 
53
basename="`basename "$0"`"
50
54
cat <<EOF
51
55
Usage: $basename [ -v | --version ]
52
56
       $basename [ -h | --help ]
60
64
  -v, --version         Show program's version number and exit
61
65
  -h, --help            Show this help message and exit
62
66
  -d DIR, --dir DIR     Target directory for key files
63
 
  -t TYPE, --type TYPE  Key type.  Default is DSA.
 
67
  -t TYPE, --type TYPE  OpenPGP key type.  Default is RSA.
64
68
  -l BITS, --length BITS
65
 
                        Key length in bits.  Default is 2048.
 
69
                        OpenPGP key length in bits.  Default is 4096.
66
70
  -s TYPE, --subtype TYPE
67
 
                        Subkey type.  Default is ELG-E.
 
71
                        OpenPGP subkey type.  Default is RSA.
68
72
  -L BITS, --sublength BITS
69
 
                        Subkey length in bits.  Default is 2048.
 
73
                        OpenPGP subkey length in bits.  Default 4096.
70
74
  -n NAME, --name NAME  Name of key.  Default is the FQDN.
71
75
  -e ADDRESS, --email ADDRESS
72
 
                        Email address of key.  Default is empty.
 
76
                        Email address of OpenPGP key.  Default empty.
73
77
  -c TEXT, --comment TEXT
74
 
                        Comment field for key.  The default value is
75
 
                        "Mandos client key".
 
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:
86
91
                        Encrypt a password from FILE using the key in
87
92
                        the key directory.  All options other than
88
93
                        --dir and --name are ignored.
 
94
  -S, --no-ssh          Don't get SSH key or set "checker" option.
89
95
EOF
90
96
}
91
97
 
103
109
        -e|--email) KEYEMAIL="$2"; shift 2;;
104
110
        -c|--comment) KEYCOMMENT="$2"; shift 2;;
105
111
        -x|--expire) KEYEXPIRE="$2"; shift 2;;
 
112
        -T|--tls-keytype) TLS_KEYTYPE="$2"; shift 2;;
106
113
        -f|--force) FORCE=yes; shift;;
 
114
        -S|--no-ssh) SSH=no; shift;;
107
115
        -v|--version) echo "$0 $VERSION"; exit;;
108
116
        -h|--help) help; exit;;
109
117
        --) shift; break;;
111
119
    esac
112
120
done
113
121
if [ "$#" -gt 0 ]; then
114
 
    echo "Unknown arguments: '$@'" >&2
 
122
    echo "Unknown arguments: '$*'" >&2
115
123
    exit 1
116
124
fi
117
125
 
118
126
SECKEYFILE="$KEYDIR/seckey.txt"
119
127
PUBKEYFILE="$KEYDIR/pubkey.txt"
 
128
TLS_PRIVKEYFILE="$KEYDIR/tls-privkey.pem"
 
129
TLS_PUBKEYFILE="$KEYDIR/tls-pubkey.pem"
120
130
 
121
131
# Check for some invalid values
122
132
if [ ! -d "$KEYDIR" ]; then
137
147
        echo "Empty key type" >&2
138
148
        exit 1
139
149
    fi
140
 
    
 
150
 
141
151
    if [ -z "$KEYNAME" ]; then
142
152
        echo "Empty key name" >&2
143
153
        exit 1
144
154
    fi
145
 
    
 
155
 
146
156
    if [ -z "$KEYLENGTH" ] || [ "$KEYLENGTH" -lt 512 ]; then
147
157
        echo "Invalid key length" >&2
148
158
        exit 1
149
159
    fi
150
 
    
 
160
 
151
161
    if [ -z "$KEYEXPIRE" ]; then
152
162
        echo "Empty key expiration" >&2
153
163
        exit 1
154
164
    fi
155
 
    
 
165
 
156
166
    # Make FORCE be 0 or 1
157
167
    case "$FORCE" in
158
168
        [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]) FORCE=1;;
159
169
        [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|*) FORCE=0;;
160
170
    esac
161
 
    
162
 
    if [ \( -e "$SECKEYFILE" -o -e "$PUBKEYFILE" \) \
163
 
        -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
164
176
        echo "Refusing to overwrite old key files; use --force" >&2
165
177
        exit 1
166
178
    fi
167
 
    
 
179
 
168
180
    # Set lines for GnuPG batch file
169
181
    if [ -n "$KEYCOMMENT" ]; then
170
182
        KEYCOMMENTLINE="Name-Comment: $KEYCOMMENT"
172
184
    if [ -n "$KEYEMAIL" ]; then
173
185
        KEYEMAILLINE="Name-Email: $KEYEMAIL"
174
186
    fi
175
 
    
 
187
 
176
188
    # Create temporary gpg batch file
177
189
    BATCHFILE="`mktemp -t mandos-keygen-batch.XXXXXXXXXX`"
 
190
    TLS_PRIVKEYTMP="`mktemp -t mandos-keygen-privkey.XXXXXXXXXX`"
178
191
fi
179
192
 
180
193
if [ "$mode" = password ]; then
189
202
trap "
190
203
set +e; \
191
204
test -n \"$SECFILE\" && shred --remove \"$SECFILE\"; \
192
 
shred --remove \"$RINGDIR\"/sec*;
 
205
test -n \"$TLS_PRIVKEYTMP\" && shred --remove \"$TLS_PRIVKEYTMP\"; \
 
206
shred --remove \"$RINGDIR\"/sec* 2>/dev/null;
193
207
test -n \"$BATCHFILE\" && rm --force \"$BATCHFILE\"; \
194
208
rm --recursive --force \"$RINGDIR\";
195
209
tty --quiet && stty echo; \
204
218
    cat >"$BATCHFILE" <<-EOF
205
219
        Key-Type: $KEYTYPE
206
220
        Key-Length: $KEYLENGTH
207
 
        #Key-Usage: encrypt,sign,auth
 
221
        Key-Usage: sign,auth
208
222
        Subkey-Type: $SUBKEYTYPE
209
223
        Subkey-Length: $SUBKEYLENGTH
210
 
        #Subkey-Usage: encrypt,sign,auth
 
224
        Subkey-Usage: encrypt
211
225
        Name-Real: $KEYNAME
212
226
        $KEYCOMMENTLINE
213
227
        $KEYEMAILLINE
216
230
        #Handle: <no-spaces>
217
231
        #%pubring pubring.gpg
218
232
        #%secring secring.gpg
 
233
        %no-protection
219
234
        %commit
220
235
        EOF
221
 
    
 
236
 
222
237
    if tty --quiet; then
223
238
        cat <<-EOF
224
239
        Note: Due to entropy requirements, key generation could take
228
243
        echo -n "Started: "
229
244
        date
230
245
    fi
231
 
    
 
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
232
285
    # Generate a new key in the key rings
233
286
    gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
234
287
        --homedir "$RINGDIR" --trust-model always \
235
288
        --gen-key "$BATCHFILE"
236
289
    rm --force "$BATCHFILE"
237
 
    
 
290
 
238
291
    if tty --quiet; then
239
292
        echo -n "Finished: "
240
293
        date
241
294
    fi
242
 
    
 
295
 
243
296
    # Backup any old key files
244
297
    if cp --backup=numbered --force "$SECKEYFILE" "$SECKEYFILE" \
245
298
        2>/dev/null; then
246
 
        shred --remove "$SECKEYFILE"
 
299
        shred --remove "$SECKEYFILE" 2>/dev/null || :
247
300
    fi
248
301
    if cp --backup=numbered --force "$PUBKEYFILE" "$PUBKEYFILE" \
249
302
        2>/dev/null; then
250
303
        rm --force "$PUBKEYFILE"
251
304
    fi
252
 
    
 
305
 
253
306
    FILECOMMENT="Mandos client key for $KEYNAME"
254
307
    if [ "$KEYCOMMENT" != "$KEYCOMMENT_ORIG" ]; then
255
308
        FILECOMMENT="$FILECOMMENT ($KEYCOMMENT)"
256
309
    fi
257
 
    
 
310
 
258
311
    if [ -n "$KEYEMAIL" ]; then
259
312
        FILECOMMENT="$FILECOMMENT <$KEYEMAIL>"
260
313
    fi
261
 
    
 
314
 
262
315
    # Export key from key rings to key files
263
316
    gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
264
317
        --homedir "$RINGDIR" --armor --export-options export-minimal \
270
323
fi
271
324
 
272
325
if [ "$mode" = password ]; then
 
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
        # 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
 
350
        done
 
351
    fi
 
352
 
273
353
    # Import key into temporary key rings
274
354
    gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
275
355
        --homedir "$RINGDIR" --trust-model always --armor \
277
357
    gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
278
358
        --homedir "$RINGDIR" --trust-model always --armor \
279
359
        --import "$PUBKEYFILE"
280
 
    
 
360
 
281
361
    # Get fingerprint of key
282
362
    FINGERPRINT="`gpg --quiet --batch --no-tty --no-options \
283
 
        --enable-dsa2 --homedir \"$RINGDIR\" --trust-model always \
 
363
        --enable-dsa2 --homedir "$RINGDIR" --trust-model always \
284
364
        --fingerprint --with-colons \
285
365
        | sed --quiet \
286
366
        --expression='/^fpr:/{s/^fpr:.*:\\([0-9A-Z]*\\):\$/\\1/p;q}'`"
287
 
    
 
367
 
288
368
    test -n "$FINGERPRINT"
289
 
    
 
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
 
290
383
    FILECOMMENT="Encrypted password for a Mandos client"
291
 
    
 
384
 
292
385
    while [ ! -s "$SECFILE" ]; do
293
386
        if [ -n "$PASSFILE" ]; then
294
 
            cat "$PASSFILE"
 
387
            cat -- "$PASSFILE"
295
388
        else
296
389
            tty --quiet && stty -echo
297
 
            read -p "Enter passphrase: " first
 
390
            echo -n "Enter passphrase: " >/dev/tty
 
391
            read -r first
298
392
            tty --quiet && echo >&2
299
 
            read -p "Repeat passphrase: " second
 
393
            echo -n "Repeat passphrase: " >/dev/tty
 
394
            read -r second
300
395
            if tty --quiet; then
301
396
                echo >&2
302
397
                stty echo
305
400
                echo "Passphrase mismatch" >&2
306
401
                touch "$RINGDIR"/mismatch
307
402
            else
308
 
                echo -n "$first"
 
403
                printf "%s" "$first"
309
404
            fi
310
405
        fi | gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
311
406
            --homedir "$RINGDIR" --trust-model always --armor \
320
415
            fi
321
416
        fi
322
417
    done
323
 
    
 
418
 
324
419
    cat <<-EOF
325
420
        [$KEYNAME]
326
421
        host = $KEYNAME
 
422
        EOF
 
423
    if [ -n "$KEY_ID" ]; then
 
424
        echo "key_id = $KEY_ID"
 
425
    fi
 
426
    cat <<-EOF
327
427
        fingerprint = $FINGERPRINT
328
428
        secret =
329
429
        EOF
336
436
                /^[^-]/s/^/    /p
337
437
            }
338
438
        }' < "$SECFILE"
 
439
    if [ -n "$ssh_fingerprint" ]; then
 
440
        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"'
 
441
        echo "ssh_fingerprint = ${ssh_fingerprint}"
 
442
    fi
339
443
fi
340
444
 
341
445
trap - EXIT
343
447
set +e
344
448
# Remove the password file, if any
345
449
if [ -n "$SECFILE" ]; then
346
 
    shred --remove "$SECFILE"
 
450
    shred --remove "$SECFILE" 2>/dev/null
347
451
fi
348
452
# Remove the key rings
349
 
shred --remove "$RINGDIR"/sec*
 
453
shred --remove "$RINGDIR"/sec* 2>/dev/null
350
454
rm --recursive --force "$RINGDIR"