/mandos/release

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/release

« back to all changes in this revision

Viewing changes to mandos-keygen

First version of a somewhat complete D-Bus server interface.  Also
change user/group name to "_mandos".

* debian/mandos.postinst: Rename old "mandos" user and group to
                          "_mandos"; create "_mandos" user and group
                          if none exist.
* debian/mandos-client.postinst: - '' -

* initramfs-tools-hook: Try "_mandos" before "mandos" as user and
                        group name.

* mandos (_datetime_to_dbus_struct): New; was previously local.
  (Client.started): Renamed to "last_started".  All users changed.
  (Client.started): New; boolean.
  (Client.dbus_object_path): New.
  (Client.check_command): Renamed to "checker_command".  All users
                          changed.
  (Client.__init__): Set and use "self.dbus_object_path".  Set
                     "self.started".
  (Client.start): Update "self.started".  Emit "self.PropertyChanged"
                  signals for both "started" and "last_started".
  (Client.stop): Update "self.started".  Emit "self.PropertyChanged"
                 signal for "started".
  (Client.checker_callback): Take additional "command" argument.  All
                             callers changed. Emit
                             "self.PropertyChanged" signal.
  (Client.bump_timeout): Emit "self.PropertyChanged" signal for
                         "last_checked_ok".
  (Client.start_checker): Emit "self.PropertyChanged" signal for
                          "checker_running".
  (Client.stop_checker): Emit "self.PropertyChanged" signal for
                         "checker_running".
  (Client.still_valid): Bug fix: use "getattr(self, started, False)"
                        instead of "self.started" in case this client
                        object is so new that the "started" attribute
                        has not been created yet.
  (Client.IntervalChanged, Client.CheckerIsRunning, Client.GetChecker,
  Client.GetCreated, Client.GetFingerprint, Client.GetHost,
  Client.GetInterval, Client.GetName, Client.GetStarted,
  Client.GetTimeout, Client.StateChanged, Client.TimeoutChanged):
  Removed; all callers changed.
  (Client.CheckerCompleted): Add "condition" and "command" arguments.
                             All callers changed.
  (Client.GetAllProperties, Client.PropertyChanged): New.
  (Client.StillValid): Renamed to "IsStillValid".
  (Client.StartChecker): Changed to its own function to avoid the
                         return value from "Client.start_checker()".
  (Client.Stop): Changed to its own function to avoid the return value
                 from "Client.stop()".
  (main): Try "_mandos" before "mandos" as user and group name.
          Removed inner function "remove_from_clients".  New inner
          class "MandosServer".

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 Teddy Hogeborn
 
6
# Copyright © 2008 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@fukt.bsnet.se>.
 
22
 
23
 
 
24
VERSION="1.0.2"
 
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
stty echo; \
 
196
" EXIT
 
197
 
 
198
umask 077
 
199
 
 
200
if [ "$mode" = keygen ]; then
 
201
    # Create batch file for GnuPG
 
202
    cat >"$BATCHFILE" <<-EOF
 
203
        Key-Type: $KEYTYPE
 
204
        Key-Length: $KEYLENGTH
 
205
        #Key-Usage: encrypt,sign,auth
 
206
        Subkey-Type: $SUBKEYTYPE
 
207
        Subkey-Length: $SUBKEYLENGTH
 
208
        #Subkey-Usage: encrypt,sign,auth
 
209
        Name-Real: $KEYNAME
 
210
        $KEYCOMMENTLINE
 
211
        $KEYEMAILLINE
 
212
        Expire-Date: $KEYEXPIRE
 
213
        #Preferences: <string>
 
214
        #Handle: <no-spaces>
 
215
        #%pubring pubring.gpg
 
216
        #%secring secring.gpg
 
217
        %commit
 
218
        EOF
 
219
    
 
220
    # Generate a new key in the key rings
 
221
    gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
 
222
        --homedir "$RINGDIR" --trust-model always \
 
223
        --gen-key "$BATCHFILE"
 
224
    rm --force "$BATCHFILE"
 
225
    
 
226
    # Backup any old key files
 
227
    if cp --backup=numbered --force "$SECKEYFILE" "$SECKEYFILE" \
 
228
        2>/dev/null; then
 
229
        shred --remove "$SECKEYFILE"
 
230
    fi
 
231
    if cp --backup=numbered --force "$PUBKEYFILE" "$PUBKEYFILE" \
 
232
        2>/dev/null; then
 
233
        rm --force "$PUBKEYFILE"
 
234
    fi
 
235
    
 
236
    FILECOMMENT="Mandos client key for $KEYNAME"
 
237
    if [ "$KEYCOMMENT" != "$KEYCOMMENT_ORIG" ]; then
 
238
        FILECOMMENT="$FILECOMMENT ($KEYCOMMENT)"
 
239
    fi
 
240
    
 
241
    if [ -n "$KEYEMAIL" ]; then
 
242
        FILECOMMENT="$FILECOMMENT <$KEYEMAIL>"
 
243
    fi
 
244
    
 
245
    # Export key from key rings to key files
 
246
    gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
 
247
        --homedir "$RINGDIR" --armor --export-options export-minimal \
 
248
        --comment "$FILECOMMENT" --output "$SECKEYFILE" \
 
249
        --export-secret-keys
 
250
    gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
 
251
        --homedir "$RINGDIR" --armor --export-options export-minimal \
 
252
        --comment "$FILECOMMENT" --output "$PUBKEYFILE" --export
 
253
fi
 
254
 
 
255
if [ "$mode" = password ]; then
 
256
    # Import key into temporary key rings
 
257
    gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
 
258
        --homedir "$RINGDIR" --trust-model always --armor \
 
259
        --import "$SECKEYFILE"
 
260
    gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
 
261
        --homedir "$RINGDIR" --trust-model always --armor \
 
262
        --import "$PUBKEYFILE"
 
263
    
 
264
    # Get fingerprint of key
 
265
    FINGERPRINT="`gpg --quiet --batch --no-tty --no-options \
 
266
        --enable-dsa2 --homedir \"$RINGDIR\" --trust-model always \
 
267
        --fingerprint --with-colons \
 
268
        | sed --quiet \
 
269
        --expression='/^fpr:/{s/^fpr:.*:\\([0-9A-Z]*\\):\$/\\1/p;q}'`"
 
270
    
 
271
    test -n "$FINGERPRINT"
 
272
    
 
273
    FILECOMMENT="Encrypted password for a Mandos client"
 
274
    
 
275
    if [ -n "$PASSFILE" ]; then
 
276
        cat "$PASSFILE"
 
277
    else
 
278
        stty -echo
 
279
        echo -n "Enter passphrase: " >&2
 
280
        first="$(head --lines=1 | tr --delete '\n')"
 
281
        echo -n -e "\nRepeat passphrase: " >&2
 
282
        second="$(head --lines=1 | tr --delete '\n')"
 
283
        echo >&2
 
284
        stty echo
 
285
        if [ "$first" != "$second" ]; then
 
286
            echo -e "Passphrase mismatch" >&2
 
287
            false
 
288
        else
 
289
            echo -n "$first"
 
290
        fi
 
291
    fi | gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
 
292
        --homedir "$RINGDIR" --trust-model always --armor --encrypt \
 
293
        --sign --recipient "$FINGERPRINT" --comment "$FILECOMMENT" \
 
294
        > "$SECFILE"
 
295
    status="${PIPESTATUS[0]}"
 
296
    if [ "$status" -ne 0 ]; then
 
297
        exit "$status"
 
298
    fi
 
299
    
 
300
    cat <<-EOF
 
301
        [$KEYNAME]
 
302
        host = $KEYNAME
 
303
        fingerprint = $FINGERPRINT
 
304
        secret =
 
305
        EOF
 
306
    sed --quiet --expression='
 
307
        /^-----BEGIN PGP MESSAGE-----$/,/^-----END PGP MESSAGE-----$/{
 
308
            /^$/,${
 
309
                # Remove 24-bit Radix-64 checksum
 
310
                s/=....$//
 
311
                # Indent four spaces
 
312
                /^[^-]/s/^/    /p
 
313
            }
 
314
        }' < "$SECFILE"
 
315
fi
 
316
 
 
317
trap - EXIT
 
318
 
 
319
set +e
 
320
# Remove the password file, if any
 
321
if [ -n "$SECFILE" ]; then
 
322
    shred --remove "$SECFILE"
 
323
fi
 
324
# Remove the key rings
 
325
shred --remove "$RINGDIR"/sec*
 
326
rm --recursive --force "$RINGDIR"