/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: 2011-12-31 20:07:11 UTC
  • mfrom: (535.1.9 wireless-network-hook)
  • Revision ID: teddy@recompile.se-20111231200711-6dli3r8drftem57r
Merge new wireless network hook.  Fix bridge network hook to use
hardware addresses instead of interface names.  Implement and document
new "CONNECT" environment variable for network hooks.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/bin/sh -e
 
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
 
10
# the Free Software Foundation, either version 3 of the License, or
 
11
# (at your option) any later version.
 
12
#
 
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
#     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
16
#     GNU General Public License for more details.
 
17
 
18
# 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/>.
 
20
 
21
# Contact the authors at <mandos@recompile.se>.
 
22
 
23
 
 
24
VERSION="1.4.1"
 
25
 
 
26
KEYDIR="/etc/keys/mandos"
 
27
KEYTYPE=DSA
 
28
KEYLENGTH=2048
 
29
SUBKEYTYPE=ELG-E
 
30
SUBKEYLENGTH=2048
 
31
KEYNAME="`hostname --fqdn 2>/dev/null || hostname`"
 
32
KEYEMAIL=""
 
33
KEYCOMMENT="Mandos client key"
 
34
KEYEXPIRE=0
 
35
FORCE=no
 
36
KEYCOMMENT_ORIG="$KEYCOMMENT"
 
37
mode=keygen
 
38
 
 
39
if [ ! -d "$KEYDIR" ]; then
 
40
    KEYDIR="/etc/mandos/keys"
 
41
fi
 
42
 
 
43
# 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 \
 
46
    --name "$0" -- "$@"`
 
47
 
 
48
help(){
 
49
basename="`basename $0`"
 
50
cat <<EOF
 
51
Usage: $basename [ -v | --version ]
 
52
       $basename [ -h | --help ]
 
53
   Key creation:
 
54
       $basename [ OPTIONS ]
 
55
   Encrypted password creation:
 
56
       $basename { -p | --password } [ --name NAME ] [ --dir DIR]
 
57
       $basename { -F | --passfile } FILE [ --name NAME ] [ --dir DIR]
 
58
 
 
59
Key creation options:
 
60
  -v, --version         Show program's version number and exit
 
61
  -h, --help            Show this help message and exit
 
62
  -d DIR, --dir DIR     Target directory for key files
 
63
  -t TYPE, --type TYPE  Key type.  Default is DSA.
 
64
  -l BITS, --length BITS
 
65
                        Key length in bits.  Default is 2048.
 
66
  -s TYPE, --subtype TYPE
 
67
                        Subkey type.  Default is ELG-E.
 
68
  -L BITS, --sublength BITS
 
69
                        Subkey length in bits.  Default is 2048.
 
70
  -n NAME, --name NAME  Name of key.  Default is the FQDN.
 
71
  -e ADDRESS, --email ADDRESS
 
72
                        Email address of key.  Default is empty.
 
73
  -c TEXT, --comment TEXT
 
74
                        Comment field for key.  The default value is
 
75
                        "Mandos client key".
 
76
  -x TIME, --expire TIME
 
77
                        Key expire time.  Default is no expiration.
 
78
                        See gpg(1) for syntax.
 
79
  -f, --force           Force overwriting old key files.
 
80
 
 
81
Password creation options:
 
82
  -p, --password        Create an encrypted password using the key in
 
83
                        the key directory.  All options other than
 
84
                        --dir and --name are ignored.
 
85
  -F FILE, --passfile FILE
 
86
                        Encrypt a password from FILE using the key in
 
87
                        the key directory.  All options other than
 
88
                        --dir and --name are ignored.
 
89
EOF
 
90
}
 
91
 
 
92
eval set -- "$TEMP"
 
93
while :; do
 
94
    case "$1" in
 
95
        -p|--password) mode=password; shift;;
 
96
        -F|--passfile) mode=password; PASSFILE="$2"; shift 2;;
 
97
        -d|--dir) KEYDIR="$2"; shift 2;;
 
98
        -t|--type) KEYTYPE="$2"; shift 2;;
 
99
        -s|--subtype) SUBKEYTYPE="$2"; shift 2;;
 
100
        -l|--length) KEYLENGTH="$2"; shift 2;;
 
101
        -L|--sublength) SUBKEYLENGTH="$2"; shift 2;;
 
102
        -n|--name) KEYNAME="$2"; shift 2;;
 
103
        -e|--email) KEYEMAIL="$2"; shift 2;;
 
104
        -c|--comment) KEYCOMMENT="$2"; shift 2;;
 
105
        -x|--expire) KEYEXPIRE="$2"; shift 2;;
 
106
        -f|--force) FORCE=yes; shift;;
 
107
        -v|--version) echo "$0 $VERSION"; exit;;
 
108
        -h|--help) help; exit;;
 
109
        --) shift; break;;
 
110
        *) echo "Internal error" >&2; exit 1;;
 
111
    esac
 
112
done
 
113
if [ "$#" -gt 0 ]; then
 
114
    echo "Unknown arguments: '$@'" >&2
 
115
    exit 1
 
116
fi
 
117
 
 
118
SECKEYFILE="$KEYDIR/seckey.txt"
 
119
PUBKEYFILE="$KEYDIR/pubkey.txt"
 
120
 
 
121
# Check for some invalid values
 
122
if [ ! -d "$KEYDIR" ]; then
 
123
    echo "$KEYDIR not a directory" >&2
 
124
    exit 1
 
125
fi
 
126
if [ ! -r "$KEYDIR" ]; then
 
127
    echo "Directory $KEYDIR not readable" >&2
 
128
    exit 1
 
129
fi
 
130
 
 
131
if [ "$mode" = keygen ]; then
 
132
    if [ ! -w "$KEYDIR" ]; then
 
133
        echo "Directory $KEYDIR not writeable" >&2
 
134
        exit 1
 
135
    fi
 
136
    if [ -z "$KEYTYPE" ]; then
 
137
        echo "Empty key type" >&2
 
138
        exit 1
 
139
    fi
 
140
    
 
141
    if [ -z "$KEYNAME" ]; then
 
142
        echo "Empty key name" >&2
 
143
        exit 1
 
144
    fi
 
145
    
 
146
    if [ -z "$KEYLENGTH" ] || [ "$KEYLENGTH" -lt 512 ]; then
 
147
        echo "Invalid key length" >&2
 
148
        exit 1
 
149
    fi
 
150
    
 
151
    if [ -z "$KEYEXPIRE" ]; then
 
152
        echo "Empty key expiration" >&2
 
153
        exit 1
 
154
    fi
 
155
    
 
156
    # Make FORCE be 0 or 1
 
157
    case "$FORCE" in
 
158
        [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]) FORCE=1;;
 
159
        [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|*) FORCE=0;;
 
160
    esac
 
161
    
 
162
    if [ \( -e "$SECKEYFILE" -o -e "$PUBKEYFILE" \) \
 
163
        -a "$FORCE" -eq 0 ]; then
 
164
        echo "Refusing to overwrite old key files; use --force" >&2
 
165
        exit 1
 
166
    fi
 
167
    
 
168
    # Set lines for GnuPG batch file
 
169
    if [ -n "$KEYCOMMENT" ]; then
 
170
        KEYCOMMENTLINE="Name-Comment: $KEYCOMMENT"
 
171
    fi
 
172
    if [ -n "$KEYEMAIL" ]; then
 
173
        KEYEMAILLINE="Name-Email: $KEYEMAIL"
 
174
    fi
 
175
    
 
176
    # Create temporary gpg batch file
 
177
    BATCHFILE="`mktemp -t mandos-keygen-batch.XXXXXXXXXX`"
 
178
fi
 
179
 
 
180
if [ "$mode" = password ]; then
 
181
    # Create temporary encrypted password file
 
182
    SECFILE="`mktemp -t mandos-keygen-secfile.XXXXXXXXXX`"
 
183
fi
 
184
 
 
185
# Create temporary key ring directory
 
186
RINGDIR="`mktemp -d -t mandos-keygen-keyrings.XXXXXXXXXX`"
 
187
 
 
188
# Remove temporary files on exit
 
189
trap "
 
190
set +e; \
 
191
test -n \"$SECFILE\" && shred --remove \"$SECFILE\"; \
 
192
shred --remove \"$RINGDIR\"/sec*;
 
193
test -n \"$BATCHFILE\" && rm --force \"$BATCHFILE\"; \
 
194
rm --recursive --force \"$RINGDIR\";
 
195
tty --quiet && stty echo; \
 
196
" EXIT
 
197
 
 
198
set -e
 
199
 
 
200
umask 077
 
201
 
 
202
if [ "$mode" = keygen ]; then
 
203
    # Create batch file for GnuPG
 
204
    cat >"$BATCHFILE" <<-EOF
 
205
        Key-Type: $KEYTYPE
 
206
        Key-Length: $KEYLENGTH
 
207
        #Key-Usage: encrypt,sign,auth
 
208
        Subkey-Type: $SUBKEYTYPE
 
209
        Subkey-Length: $SUBKEYLENGTH
 
210
        #Subkey-Usage: encrypt,sign,auth
 
211
        Name-Real: $KEYNAME
 
212
        $KEYCOMMENTLINE
 
213
        $KEYEMAILLINE
 
214
        Expire-Date: $KEYEXPIRE
 
215
        #Preferences: <string>
 
216
        #Handle: <no-spaces>
 
217
        #%pubring pubring.gpg
 
218
        #%secring secring.gpg
 
219
        %commit
 
220
        EOF
 
221
    
 
222
    if tty --quiet; then
 
223
        cat <<-EOF
 
224
        Note: Due to entropy requirements, key generation could take
 
225
        anything from a few minutes to SEVERAL HOURS.  Please be
 
226
        patient and/or supply the system with more entropy if needed.
 
227
        EOF
 
228
        echo -n "Started: "
 
229
        date
 
230
    fi
 
231
    
 
232
    # Generate a new key in the key rings
 
233
    gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
 
234
        --homedir "$RINGDIR" --trust-model always \
 
235
        --gen-key "$BATCHFILE"
 
236
    rm --force "$BATCHFILE"
 
237
    
 
238
    if tty --quiet; then
 
239
        echo -n "Finished: "
 
240
        date
 
241
    fi
 
242
    
 
243
    # Backup any old key files
 
244
    if cp --backup=numbered --force "$SECKEYFILE" "$SECKEYFILE" \
 
245
        2>/dev/null; then
 
246
        shred --remove "$SECKEYFILE"
 
247
    fi
 
248
    if cp --backup=numbered --force "$PUBKEYFILE" "$PUBKEYFILE" \
 
249
        2>/dev/null; then
 
250
        rm --force "$PUBKEYFILE"
 
251
    fi
 
252
    
 
253
    FILECOMMENT="Mandos client key for $KEYNAME"
 
254
    if [ "$KEYCOMMENT" != "$KEYCOMMENT_ORIG" ]; then
 
255
        FILECOMMENT="$FILECOMMENT ($KEYCOMMENT)"
 
256
    fi
 
257
    
 
258
    if [ -n "$KEYEMAIL" ]; then
 
259
        FILECOMMENT="$FILECOMMENT <$KEYEMAIL>"
 
260
    fi
 
261
    
 
262
    # Export key from key rings to key files
 
263
    gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
 
264
        --homedir "$RINGDIR" --armor --export-options export-minimal \
 
265
        --comment "$FILECOMMENT" --output "$SECKEYFILE" \
 
266
        --export-secret-keys
 
267
    gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
 
268
        --homedir "$RINGDIR" --armor --export-options export-minimal \
 
269
        --comment "$FILECOMMENT" --output "$PUBKEYFILE" --export
 
270
fi
 
271
 
 
272
if [ "$mode" = password ]; then
 
273
    # Import key into temporary key rings
 
274
    gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
 
275
        --homedir "$RINGDIR" --trust-model always --armor \
 
276
        --import "$SECKEYFILE"
 
277
    gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
 
278
        --homedir "$RINGDIR" --trust-model always --armor \
 
279
        --import "$PUBKEYFILE"
 
280
    
 
281
    # Get fingerprint of key
 
282
    FINGERPRINT="`gpg --quiet --batch --no-tty --no-options \
 
283
        --enable-dsa2 --homedir \"$RINGDIR\" --trust-model always \
 
284
        --fingerprint --with-colons \
 
285
        | sed --quiet \
 
286
        --expression='/^fpr:/{s/^fpr:.*:\\([0-9A-Z]*\\):\$/\\1/p;q}'`"
 
287
    
 
288
    test -n "$FINGERPRINT"
 
289
    
 
290
    FILECOMMENT="Encrypted password for a Mandos client"
 
291
    
 
292
    while [ ! -s "$SECFILE" ]; do
 
293
        if [ -n "$PASSFILE" ]; then
 
294
            cat "$PASSFILE"
 
295
        else
 
296
            tty --quiet && stty -echo
 
297
            read -p "Enter passphrase: " first
 
298
            tty --quiet && echo >&2
 
299
            read -p "Repeat passphrase: " second
 
300
            if tty --quiet; then
 
301
                echo >&2
 
302
                stty echo
 
303
            fi
 
304
            if [ "$first" != "$second" ]; then
 
305
                echo "Passphrase mismatch" >&2
 
306
                touch "$RINGDIR"/mismatch
 
307
            else
 
308
                echo -n "$first"
 
309
            fi
 
310
        fi | gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
 
311
            --homedir "$RINGDIR" --trust-model always --armor \
 
312
            --encrypt --sign --recipient "$FINGERPRINT" --comment \
 
313
            "$FILECOMMENT" > "$SECFILE"
 
314
        if [ -e "$RINGDIR"/mismatch ]; then
 
315
            rm --force "$RINGDIR"/mismatch
 
316
            if tty --quiet; then
 
317
                > "$SECFILE"
 
318
            else
 
319
                exit 1
 
320
            fi
 
321
        fi
 
322
    done
 
323
    
 
324
    cat <<-EOF
 
325
        [$KEYNAME]
 
326
        host = $KEYNAME
 
327
        fingerprint = $FINGERPRINT
 
328
        secret =
 
329
        EOF
 
330
    sed --quiet --expression='
 
331
        /^-----BEGIN PGP MESSAGE-----$/,/^-----END PGP MESSAGE-----$/{
 
332
            /^$/,${
 
333
                # Remove 24-bit Radix-64 checksum
 
334
                s/=....$//
 
335
                # Indent four spaces
 
336
                /^[^-]/s/^/    /p
 
337
            }
 
338
        }' < "$SECFILE"
 
339
fi
 
340
 
 
341
trap - EXIT
 
342
 
 
343
set +e
 
344
# Remove the password file, if any
 
345
if [ -n "$SECFILE" ]; then
 
346
    shred --remove "$SECFILE"
 
347
fi
 
348
# Remove the key rings
 
349
shred --remove "$RINGDIR"/sec*
 
350
rm --recursive --force "$RINGDIR"