/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: 2011-11-26 19:08:37 UTC
  • mto: (518.2.5 persistent-state-gpgme)
  • mto: This revision was merged to the branch mainline in revision 524.
  • Revision ID: teddy@recompile.se-20111126190837-wyn24lekil5q6jiq
* mandos: Fix whitespace.

Show diffs side-by-side

added added

removed removed

Lines of Context:
421
421
                                        .Lock()))
422
422
        self.client_structure = [attr for attr in self.__dict__.iterkeys() if not attr.startswith("_")]
423
423
        self.client_structure.append("client_structure")
424
 
 
425
 
 
 
424
        
426
425
        for name, t in inspect.getmembers(type(self),
427
426
                                          lambda obj: isinstance(obj, property)):
428
427
            if not name.startswith("_"):
466
465
    
467
466
    def __del__(self):
468
467
        self.disable()
469
 
 
 
468
    
470
469
    def init_checker(self):
471
470
        # Schedule a new checker to be started an 'interval' from now,
472
471
        # and every interval from then on.
479
478
                                    self.disable))
480
479
        # Also start a new checker *right now*.
481
480
        self.start_checker()
482
 
 
483
 
        
 
481
    
484
482
    def checker_callback(self, pid, condition, command):
485
483
        """The checker has completed, so take appropriate actions."""
486
484
        self.checker_callback_tag = None
611
609
            if error.errno != errno.ESRCH: # No such process
612
610
                raise
613
611
        self.checker = None
614
 
 
 
612
    
615
613
    # Encrypts a client secret and stores it in a varible encrypted_secret
616
614
    def encrypt_secret(self, key):
617
615
        # Encryption-key need to be of a specific size, so we hash inputed key
618
616
        hasheng = hashlib.sha256()
619
617
        hasheng.update(key)
620
618
        encryptionkey = hasheng.digest()
621
 
 
 
619
        
622
620
        # Create validation hash so we know at decryption if it was sucessful
623
621
        hasheng = hashlib.sha256()
624
622
        hasheng.update(self.secret)
625
623
        validationhash = hasheng.digest()
626
 
 
 
624
        
627
625
        # Encrypt secret
628
626
        iv = os.urandom(Crypto.Cipher.AES.block_size)
629
627
        ciphereng = Crypto.Cipher.AES.new(encryptionkey,
630
628
                                        Crypto.Cipher.AES.MODE_CFB, iv)
631
629
        ciphertext = ciphereng.encrypt(validationhash+self.secret)
632
630
        self.encrypted_secret = (ciphertext, iv)
633
 
 
 
631
    
634
632
    # Decrypt a encrypted client secret
635
633
    def decrypt_secret(self, key):
636
634
        # Decryption-key need to be of a specific size, so we hash inputed key
637
635
        hasheng = hashlib.sha256()
638
636
        hasheng.update(key)
639
637
        encryptionkey = hasheng.digest()
640
 
 
 
638
        
641
639
        # Decrypt encrypted secret
642
640
        ciphertext, iv = self.encrypted_secret
643
641
        ciphereng = Crypto.Cipher.AES.new(encryptionkey,
644
642
                                        Crypto.Cipher.AES.MODE_CFB, iv)
645
643
        plain = ciphereng.decrypt(ciphertext)
646
 
 
 
644
        
647
645
        # Validate decrypted secret to know if it was succesful
648
646
        hasheng = hashlib.sha256()
649
647
        validationhash = plain[:hasheng.digest_size]
650
648
        secret = plain[hasheng.digest_size:]
651
649
        hasheng.update(secret)
652
 
 
 
650
        
653
651
        # if validation fails, we use key as new secret. Otherwhise, we use
654
652
        # the decrypted secret
655
653
        if hasheng.digest() == validationhash:
853
851
    return dbus.String(dt.isoformat(),
854
852
                       variant_level=variant_level)
855
853
 
 
854
 
856
855
class AlternateDBusNamesMetaclass(DBusObjectWithProperties
857
856
                                  .__metaclass__):
858
857
    """Applied to an empty subclass of a D-Bus object, this metaclass
950
949
                                        attribute.func_closure)))
951
950
        return type.__new__(mcs, name, bases, attr)
952
951
 
 
952
 
953
953
class ClientDBus(Client, DBusObjectWithProperties):
954
954
    """A Client class using D-Bus
955
955
    
966
966
    def __init__(self, bus = None, *args, **kwargs):
967
967
        self.bus = bus
968
968
        Client.__init__(self, *args, **kwargs)
969
 
 
 
969
        
970
970
        self._approvals_pending = 0
971
971
        # Only now, when this client is initialized, can it show up on
972
972
        # the D-Bus
983
983
                             variant_level=1):
984
984
        """ Modify a variable so that it's a property which announces
985
985
        its changes to DBus.
986
 
 
 
986
        
987
987
        transform_fun: Function that takes a value and a variant_level
988
988
                       and transforms it to a D-Bus type.
989
989
        dbus_name: D-Bus name of the variable
1150
1150
        Is sent after a client request a password.
1151
1151
        """
1152
1152
        pass
1153
 
 
 
1153
    
1154
1154
    ## Methods
1155
1155
    
1156
1156
    # Approve - method
1385
1385
            return super(ProxyClient, self).__setattr__(name, value)
1386
1386
        self._pipe.send(('setattr', name, value))
1387
1387
 
 
1388
 
1388
1389
class ClientDBusTransitional(ClientDBus):
1389
1390
    __metaclass__ = AlternateDBusNamesMetaclass
1390
1391
 
 
1392
 
1391
1393
class ClientHandler(socketserver.BaseRequestHandler, object):
1392
1394
    """A class to handle client connections.
1393
1395
    
1932
1934
    parser.add_argument("--no-restore", action="store_false",
1933
1935
                        dest="restore", help="Do not restore stored state",
1934
1936
                        default=True)
1935
 
 
 
1937
    
1936
1938
    options = parser.parse_args()
1937
1939
    
1938
1940
    if options.check:
2142
2144
    
2143
2145
    old_client_settings = {}
2144
2146
    clients_data = []
2145
 
 
 
2147
    
2146
2148
    # Get client data and settings from last running state. 
2147
2149
    if server_settings["restore"]:
2148
2150
        try:
2153
2155
            logger.warning("Could not load persistant state: {0}".format(e))
2154
2156
            if e.errno != errno.ENOENT:
2155
2157
                raise
2156
 
 
 
2158
    
2157
2159
    for client in clients_data:
2158
2160
        client_name = client["name"]
2159
2161
        
2171
2173
                    setattr(client, name, value)
2172
2174
            except KeyError:
2173
2175
                pass
2174
 
 
 
2176
        
2175
2177
        # Clients who has passed its expire date, can still be enabled if its
2176
2178
        # last checker was sucessful. Clients who checkers failed before we
2177
2179
        # stored it state is asumed to had failed checker during downtime.
2182
2184
                    client["enabled"] = False
2183
2185
                else:
2184
2186
                    client["expires"] = datetime.datetime.utcnow() + client["timeout"]
2185
 
 
 
2187
        
2186
2188
        client["changedstate"] = (multiprocessing_manager
2187
2189
                                  .Condition(multiprocessing_manager
2188
2190
                                             .Lock()))
2217
2219
                                                       client_settings
2218
2220
                                                       [clientname]))
2219
2221
    
2220
 
 
 
2222
    
2221
2223
    if not tcp_server.clients:
2222
2224
        logger.warning("No clients defined")
2223
2225
        
2305
2307
        multiprocessing.active_children()
2306
2308
        if not (tcp_server.clients or client_settings):
2307
2309
            return
2308
 
 
 
2310
        
2309
2311
        # Store client before exiting. Secrets are encrypted with key based
2310
2312
        # on what config file has. If config file is removed/edited, old
2311
2313
        # secret will thus be unrecovable.
2312
2314
        clients = []
2313
2315
        for client in tcp_server.clients.itervalues():
2314
2316
            client.encrypt_secret(client_settings[client.name]["secret"])
2315
 
 
 
2317
            
2316
2318
            client_dict = {}
2317
 
 
 
2319
            
2318
2320
            # A list of attributes that will not be stored when shuting down.
2319
2321
            exclude = set(("bus", "changedstate", "secret"))            
2320
2322
            for name, typ in inspect.getmembers(dbus.service.Object):
2324
2326
            for attr in client.client_structure:
2325
2327
                if attr not in exclude:
2326
2328
                    client_dict[attr] = getattr(client, attr)
2327
 
 
 
2329
            
2328
2330
            clients.append(client_dict) 
2329
2331
            del client_settings[client.name]["secret"]
2330
2332
            
2335
2337
            logger.warning("Could not save persistant state: {0}".format(e))
2336
2338
            if e.errno != errno.ENOENT:
2337
2339
                raise
2338
 
 
 
2340
        
2339
2341
        # Delete all clients, and settings from config
2340
2342
        while tcp_server.clients:
2341
2343
            name, client = tcp_server.clients.popitem()