/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

  • Committer: Teddy Hogeborn
  • Date: 2012-10-24 19:34:13 UTC
  • Revision ID: teddy@recompile.se-20121024193413-yiorm0x5lj48fu7i
* mandos: Comment changes.
* mandos-keygen (--password): Fix bashism.  Thanks to Raphael Geissert
                              <atomo64@gmail.com> for the bug report.

Show diffs side-by-side

added added

removed removed

Lines of Context:
11
11
# "AvahiService" class, and some lines in "main".
12
12
13
13
# Everything else is
14
 
# Copyright © 2008-2013 Teddy Hogeborn
15
 
# Copyright © 2008-2013 Björn Påhlsson
 
14
# Copyright © 2008-2012 Teddy Hogeborn
 
15
# Copyright © 2008-2012 Björn Påhlsson
16
16
17
17
# This program is free software: you can redistribute it and/or modify
18
18
# it under the terms of the GNU General Public License as published by
79
79
import ctypes.util
80
80
import xml.dom.minidom
81
81
import inspect
 
82
import GnuPGInterface
82
83
 
83
84
try:
84
85
    SO_BINDTODEVICE = socket.SO_BINDTODEVICE
88
89
    except ImportError:
89
90
        SO_BINDTODEVICE = None
90
91
 
91
 
version = "1.6.1"
 
92
version = "1.6.0"
92
93
stored_state_file = "clients.pickle"
93
94
 
94
95
logger = logging.getLogger()
139
140
class PGPEngine(object):
140
141
    """A simple class for OpenPGP symmetric encryption & decryption"""
141
142
    def __init__(self):
 
143
        self.gnupg = GnuPGInterface.GnuPG()
142
144
        self.tempdir = tempfile.mkdtemp(prefix="mandos-")
143
 
        self.gnupgargs = ['--batch',
144
 
                          '--home', self.tempdir,
145
 
                          '--force-mdc',
146
 
                          '--quiet',
147
 
                          '--no-use-agent']
 
145
        self.gnupg = GnuPGInterface.GnuPG()
 
146
        self.gnupg.options.meta_interactive = False
 
147
        self.gnupg.options.homedir = self.tempdir
 
148
        self.gnupg.options.extra_args.extend(['--force-mdc',
 
149
                                              '--quiet',
 
150
                                              '--no-use-agent'])
148
151
    
149
152
    def __enter__(self):
150
153
        return self
175
178
        return b"mandos" + binascii.hexlify(password)
176
179
    
177
180
    def encrypt(self, data, password):
178
 
        passphrase = self.password_encode(password)
179
 
        with tempfile.NamedTemporaryFile(dir=self.tempdir
180
 
                                         ) as passfile:
181
 
            passfile.write(passphrase)
182
 
            passfile.flush()
183
 
            proc = subprocess.Popen(['gpg', '--symmetric',
184
 
                                     '--passphrase-file',
185
 
                                     passfile.name]
186
 
                                    + self.gnupgargs,
187
 
                                    stdin = subprocess.PIPE,
188
 
                                    stdout = subprocess.PIPE,
189
 
                                    stderr = subprocess.PIPE)
190
 
            ciphertext, err = proc.communicate(input = data)
191
 
        if proc.returncode != 0:
192
 
            raise PGPError(err)
 
181
        self.gnupg.passphrase = self.password_encode(password)
 
182
        with open(os.devnull, "w") as devnull:
 
183
            try:
 
184
                proc = self.gnupg.run(['--symmetric'],
 
185
                                      create_fhs=['stdin', 'stdout'],
 
186
                                      attach_fhs={'stderr': devnull})
 
187
                with contextlib.closing(proc.handles['stdin']) as f:
 
188
                    f.write(data)
 
189
                with contextlib.closing(proc.handles['stdout']) as f:
 
190
                    ciphertext = f.read()
 
191
                proc.wait()
 
192
            except IOError as e:
 
193
                raise PGPError(e)
 
194
        self.gnupg.passphrase = None
193
195
        return ciphertext
194
196
    
195
197
    def decrypt(self, data, password):
196
 
        passphrase = self.password_encode(password)
197
 
        with tempfile.NamedTemporaryFile(dir = self.tempdir
198
 
                                         ) as passfile:
199
 
            passfile.write(passphrase)
200
 
            passfile.flush()
201
 
            proc = subprocess.Popen(['gpg', '--decrypt',
202
 
                                     '--passphrase-file',
203
 
                                     passfile.name]
204
 
                                    + self.gnupgargs,
205
 
                                    stdin = subprocess.PIPE,
206
 
                                    stdout = subprocess.PIPE,
207
 
                                    stderr = subprocess.PIPE)
208
 
            decrypted_plaintext, err = proc.communicate(input
209
 
                                                        = data)
210
 
        if proc.returncode != 0:
211
 
            raise PGPError(err)
 
198
        self.gnupg.passphrase = self.password_encode(password)
 
199
        with open(os.devnull, "w") as devnull:
 
200
            try:
 
201
                proc = self.gnupg.run(['--decrypt'],
 
202
                                      create_fhs=['stdin', 'stdout'],
 
203
                                      attach_fhs={'stderr': devnull})
 
204
                with contextlib.closing(proc.handles['stdin']) as f:
 
205
                    f.write(data)
 
206
                with contextlib.closing(proc.handles['stdout']) as f:
 
207
                    decrypted_plaintext = f.read()
 
208
                proc.wait()
 
209
            except IOError as e:
 
210
                raise PGPError(e)
 
211
        self.gnupg.passphrase = None
212
212
        return decrypted_plaintext
213
213
 
214
214
 
440
440
    runtime_expansions: Allowed attributes for runtime expansion.
441
441
    expires:    datetime.datetime(); time (UTC) when a client will be
442
442
                disabled, or None
443
 
    server_settings: The server_settings dict from main()
444
443
    """
445
444
    
446
445
    runtime_expansions = ("approval_delay", "approval_duration",
521
520
        
522
521
        return settings
523
522
    
524
 
    def __init__(self, settings, name = None, server_settings=None):
 
523
    def __init__(self, settings, name = None):
525
524
        self.name = name
526
 
        if server_settings is None:
527
 
            server_settings = {}
528
 
        self.server_settings = server_settings
529
525
        # adding all client settings
530
526
        for setting, value in settings.iteritems():
531
527
            setattr(self, setting, value)
715
711
                # in normal mode, that is already done by daemon(),
716
712
                # and in debug mode we don't want to.  (Stdin is
717
713
                # always replaced by /dev/null.)
718
 
                # The exception is when not debugging but nevertheless
719
 
                # running in the foreground; use the previously
720
 
                # created wnull.
721
 
                popen_args = {}
722
 
                if (not self.server_settings["debug"]
723
 
                    and self.server_settings["foreground"]):
724
 
                    popen_args.update({"stdout": wnull,
725
 
                                       "stderr": wnull })
726
714
                self.checker = subprocess.Popen(command,
727
715
                                                close_fds=True,
728
 
                                                shell=True, cwd="/",
729
 
                                                **popen_args)
 
716
                                                shell=True, cwd="/")
730
717
            except OSError as error:
731
718
                logger.error("Failed to start subprocess",
732
719
                             exc_info=error)
1991
1978
                if self.address_family == socket.AF_INET6:
1992
1979
                    any_address = "::" # in6addr_any
1993
1980
                else:
1994
 
                    any_address = "0.0.0.0" # INADDR_ANY
 
1981
                    any_address = socket.INADDR_ANY
1995
1982
                self.server_address = (any_address,
1996
1983
                                       self.server_address[1])
1997
1984
            elif not self.server_address[1]:
2302
2289
                        help="Run self-test")
2303
2290
    parser.add_argument("--debug", action="store_true",
2304
2291
                        help="Debug mode; run in foreground and log"
2305
 
                        " to terminal", default=None)
 
2292
                        " to terminal")
2306
2293
    parser.add_argument("--debuglevel", metavar="LEVEL",
2307
2294
                        help="Debug level for stdout output")
2308
2295
    parser.add_argument("--priority", help="GnuTLS"
2315
2302
                        " files")
2316
2303
    parser.add_argument("--no-dbus", action="store_false",
2317
2304
                        dest="use_dbus", help="Do not provide D-Bus"
2318
 
                        " system bus interface", default=None)
 
2305
                        " system bus interface")
2319
2306
    parser.add_argument("--no-ipv6", action="store_false",
2320
 
                        dest="use_ipv6", help="Do not use IPv6",
2321
 
                        default=None)
 
2307
                        dest="use_ipv6", help="Do not use IPv6")
2322
2308
    parser.add_argument("--no-restore", action="store_false",
2323
2309
                        dest="restore", help="Do not restore stored"
2324
 
                        " state", default=None)
 
2310
                        " state")
2325
2311
    parser.add_argument("--socket", type=int,
2326
2312
                        help="Specify a file descriptor to a network"
2327
2313
                        " socket to use instead of creating one")
2328
2314
    parser.add_argument("--statedir", metavar="DIR",
2329
2315
                        help="Directory to save/restore state in")
2330
2316
    parser.add_argument("--foreground", action="store_true",
2331
 
                        help="Run in foreground", default=None)
 
2317
                        help="Run in foreground")
2332
2318
    
2333
2319
    options = parser.parse_args()
2334
2320
    
2343
2329
                        "port": "",
2344
2330
                        "debug": "False",
2345
2331
                        "priority":
2346
 
                        "SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP:+SIGN-RSA-SHA224",
 
2332
                        "SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
2347
2333
                        "servicename": "Mandos",
2348
2334
                        "use_dbus": "True",
2349
2335
                        "use_ipv6": "True",
2393
2379
    for option in server_settings.keys():
2394
2380
        if type(server_settings[option]) is str:
2395
2381
            server_settings[option] = unicode(server_settings[option])
2396
 
    # Force all boolean options to be boolean
2397
 
    for option in ("debug", "use_dbus", "use_ipv6", "restore",
2398
 
                   "foreground"):
2399
 
        server_settings[option] = bool(server_settings[option])
2400
2382
    # Debug implies foreground
2401
2383
    if server_settings["debug"]:
2402
2384
        server_settings["foreground"] = True
2450
2432
                              socketfd=(server_settings["socket"]
2451
2433
                                        or None))
2452
2434
    if not foreground:
2453
 
        pidfilename = "/run/mandos.pid"
 
2435
        pidfilename = "/var/run/mandos.pid"
2454
2436
        pidfile = None
2455
2437
        try:
2456
2438
            pidfile = open(pidfilename, "w")
2542
2524
    old_client_settings = {}
2543
2525
    clients_data = {}
2544
2526
    
2545
 
    # This is used to redirect stdout and stderr for checker processes
2546
 
    global wnull
2547
 
    wnull = open(os.devnull, "w") # A writable /dev/null
2548
 
    # Only used if server is running in foreground but not in debug
2549
 
    # mode
2550
 
    if debug or not foreground:
2551
 
        wnull.close()
2552
 
    
2553
2527
    # Get client data and settings from last running state.
2554
2528
    if server_settings["restore"]:
2555
2529
        try:
2571
2545
    
2572
2546
    with PGPEngine() as pgp:
2573
2547
        for client_name, client in clients_data.iteritems():
2574
 
            # Skip removed clients
2575
 
            if client_name not in client_settings:
2576
 
                continue
2577
 
            
2578
2548
            # Decide which value to use after restoring saved state.
2579
2549
            # We have three different values: Old config file,
2580
2550
            # new config file, and saved state.
2642
2612
    # Create all client objects
2643
2613
    for client_name, client in clients_data.iteritems():
2644
2614
        tcp_server.clients[client_name] = client_class(
2645
 
            name = client_name, settings = client,
2646
 
            server_settings = server_settings)
 
2615
            name = client_name, settings = client)
2647
2616
    
2648
2617
    if not tcp_server.clients:
2649
2618
        logger.warning("No clients defined")
2732
2701
        service.cleanup()
2733
2702
        
2734
2703
        multiprocessing.active_children()
2735
 
        wnull.close()
2736
2704
        if not (tcp_server.clients or client_settings):
2737
2705
            return
2738
2706
        
2750
2718
                # A list of attributes that can not be pickled
2751
2719
                # + secret.
2752
2720
                exclude = set(("bus", "changedstate", "secret",
2753
 
                               "checker", "server_settings"))
 
2721
                               "checker"))
2754
2722
                for name, typ in (inspect.getmembers
2755
2723
                                  (dbus.service.Object)):
2756
2724
                    exclude.add(name)