/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

  • Committer: Teddy Hogeborn
  • Date: 2012-07-23 07:01:16 UTC
  • mto: (237.7.272 trunk)
  • mto: This revision was merged to the branch mainline in revision 303.
  • Revision ID: teddy@recompile.se-20120723070116-m53ztbi619gj5txj
* plugins.d/mandos-client.xml: Fix minor grammar.

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
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
 
234
234
               Used to optionally bind to the specified interface.
235
235
    name: string; Example: 'Mandos'
236
236
    type: string; Example: '_mandos._tcp'.
237
 
     See <https://www.iana.org/assignments/service-names-port-numbers>
 
237
                  See <http://www.dns-sd.org/ServiceTypes.html>
238
238
    port: integer; what port to announce
239
239
    TXT: list of strings; TXT record for the service
240
240
    domain: string; Domain to publish on, default to .local if empty.
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)
1091
1078
                interface_names.add(alt_interface)
1092
1079
                # Is this a D-Bus signal?
1093
1080
                if getattr(attribute, "_dbus_is_signal", False):
1094
 
                    # Extract the original non-method undecorated
1095
 
                    # function by black magic
 
1081
                    # Extract the original non-method function by
 
1082
                    # black magic
1096
1083
                    nonmethod_func = (dict(
1097
1084
                            zip(attribute.func_code.co_freevars,
1098
1085
                                attribute.__closure__))["func"]
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
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)