/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: 2016-03-07 23:39:36 UTC
  • Revision ID: teddy@recompile.se-20160307233936-mhgpxhggamde443n
Server bug fix: Include CAP_SETGID so it does not run as root

* debian/mandos.postinst (configure): If old version was 1.7.4-1 or
  1.7.4-1~bpo8+1, fix situation where clients.pickle file is owned by
  root.
* mandos (main): Print debug info about setuid() and setgid()
* mandos.service ([Service]/CapabilityBoundingSet): Add "CAP_KILL
  CAP_SETGID"; the latter is needed for setgid() to be allowed.

Show diffs side-by-side

added added

removed removed

Lines of Context:
34
34
from __future__ import (division, absolute_import, print_function,
35
35
                        unicode_literals)
36
36
 
37
 
try:
38
 
    from future_builtins import *
39
 
except ImportError:
40
 
    pass
 
37
from future_builtins import *
41
38
 
42
39
try:
43
40
    import SocketServer as socketserver
83
80
    from gi.repository import GObject
84
81
except ImportError:
85
82
    import gobject as GObject
 
83
import avahi
86
84
from dbus.mainloop.glib import DBusGMainLoop
87
85
import ctypes
88
86
import ctypes.util
100
98
if sys.version_info.major == 2:
101
99
    str = unicode
102
100
 
103
 
version = "1.7.5"
 
101
version = "1.7.4"
104
102
stored_state_file = "clients.pickle"
105
103
 
106
104
logger = logging.getLogger()
121
119
        return interface_index
122
120
 
123
121
 
124
 
def copy_function(func):
125
 
    """Make a copy of a function"""
126
 
    if sys.version_info.major == 2:
127
 
        return types.FunctionType(func.func_code,
128
 
                                  func.func_globals,
129
 
                                  func.func_name,
130
 
                                  func.func_defaults,
131
 
                                  func.func_closure)
132
 
    else:
133
 
        return types.FunctionType(func.__code__,
134
 
                                  func.__globals__,
135
 
                                  func.__name__,
136
 
                                  func.__defaults__,
137
 
                                  func.__closure__)
138
 
 
139
 
 
140
122
def initlogger(debug, level=logging.WARNING):
141
123
    """init logger and add loglevel"""
142
124
    
173
155
        try:
174
156
            output = subprocess.check_output(["gpgconf"])
175
157
            for line in output.splitlines():
176
 
                name, text, path = line.split(b":")
 
158
                name, text, path = line.split(":")
177
159
                if name == "gpg":
178
160
                    self.gpg = path
179
161
                    break
256
238
            raise PGPError(err)
257
239
        return decrypted_plaintext
258
240
 
259
 
# Pretend that we have an Avahi module
260
 
class Avahi(object):
261
 
    """This isn't so much a class as it is a module-like namespace.
262
 
    It is instantiated once, and simulates having an Avahi module."""
263
 
    IF_UNSPEC = -1              # avahi-common/address.h
264
 
    PROTO_UNSPEC = -1           # avahi-common/address.h
265
 
    PROTO_INET = 0              # avahi-common/address.h
266
 
    PROTO_INET6 = 1             # avahi-common/address.h
267
 
    DBUS_NAME = "org.freedesktop.Avahi"
268
 
    DBUS_INTERFACE_ENTRY_GROUP = DBUS_NAME + ".EntryGroup"
269
 
    DBUS_INTERFACE_SERVER = DBUS_NAME + ".Server"
270
 
    DBUS_PATH_SERVER = "/"
271
 
    def string_array_to_txt_array(self, t):
272
 
        return dbus.Array((dbus.ByteArray(s.encode("utf-8"))
273
 
                           for s in t), signature="ay")
274
 
    ENTRY_GROUP_ESTABLISHED = 2 # avahi-common/defs.h
275
 
    ENTRY_GROUP_COLLISION = 3   # avahi-common/defs.h
276
 
    ENTRY_GROUP_FAILURE = 4     # avahi-common/defs.h
277
 
    SERVER_INVALID = 0          # avahi-common/defs.h
278
 
    SERVER_REGISTERING = 1      # avahi-common/defs.h
279
 
    SERVER_RUNNING = 2          # avahi-common/defs.h
280
 
    SERVER_COLLISION = 3        # avahi-common/defs.h
281
 
    SERVER_FAILURE = 4          # avahi-common/defs.h
282
 
avahi = Avahi()
283
241
 
284
242
class AvahiError(Exception):
285
243
    def __init__(self, value, *args, **kwargs):
489
447
    
490
448
    _library = ctypes.cdll.LoadLibrary(
491
449
        ctypes.util.find_library("gnutls"))
492
 
    _need_version = b"3.3.0"
 
450
    _need_version = "3.3.0"
493
451
    def __init__(self):
494
452
        # Need to use class name "GnuTLS" here, since this method is
495
453
        # called before the assignment to the "gnutls" global variable
836
794
            client["fingerprint"] = (section["fingerprint"].upper()
837
795
                                     .replace(" ", ""))
838
796
            if "secret" in section:
839
 
                client["secret"] = codecs.decode(section["secret"]
840
 
                                                 .encode("utf-8"),
841
 
                                                 "base64")
 
797
                client["secret"] = section["secret"].decode("base64")
842
798
            elif "secfile" in section:
843
799
                with open(os.path.expanduser(os.path.expandvars
844
800
                                             (section["secfile"])),
897
853
        self.changedstate = multiprocessing_manager.Condition(
898
854
            multiprocessing_manager.Lock())
899
855
        self.client_structure = [attr
900
 
                                 for attr in self.__dict__.keys()
 
856
                                 for attr in self.__dict__.iterkeys()
901
857
                                 if not attr.startswith("_")]
902
858
        self.client_structure.append("client_structure")
903
859
        
1551
1507
                interface_names.add(alt_interface)
1552
1508
                # Is this a D-Bus signal?
1553
1509
                if getattr(attribute, "_dbus_is_signal", False):
1554
 
                    # Extract the original non-method undecorated
1555
 
                    # function by black magic
1556
1510
                    if sys.version_info.major == 2:
 
1511
                        # Extract the original non-method undecorated
 
1512
                        # function by black magic
1557
1513
                        nonmethod_func = (dict(
1558
1514
                            zip(attribute.func_code.co_freevars,
1559
1515
                                attribute.__closure__))
1560
1516
                                          ["func"].cell_contents)
1561
1517
                    else:
1562
 
                        nonmethod_func = (dict(
1563
 
                            zip(attribute.__code__.co_freevars,
1564
 
                                attribute.__closure__))
1565
 
                                          ["func"].cell_contents)
 
1518
                        nonmethod_func = attribute
1566
1519
                    # Create a new, but exactly alike, function
1567
1520
                    # object, and decorate it to be a new D-Bus signal
1568
1521
                    # with the alternate D-Bus interface name
1569
 
                    new_function = copy_function(nonmethod_func)
 
1522
                    if sys.version_info.major == 2:
 
1523
                        new_function = types.FunctionType(
 
1524
                            nonmethod_func.func_code,
 
1525
                            nonmethod_func.func_globals,
 
1526
                            nonmethod_func.func_name,
 
1527
                            nonmethod_func.func_defaults,
 
1528
                            nonmethod_func.func_closure)
 
1529
                    else:
 
1530
                        new_function = types.FunctionType(
 
1531
                            nonmethod_func.__code__,
 
1532
                            nonmethod_func.__globals__,
 
1533
                            nonmethod_func.__name__,
 
1534
                            nonmethod_func.__defaults__,
 
1535
                            nonmethod_func.__closure__)
1570
1536
                    new_function = (dbus.service.signal(
1571
1537
                        alt_interface,
1572
1538
                        attribute._dbus_signature)(new_function))
1611
1577
                            alt_interface,
1612
1578
                            attribute._dbus_in_signature,
1613
1579
                            attribute._dbus_out_signature)
1614
 
                        (copy_function(attribute)))
 
1580
                        (types.FunctionType(attribute.func_code,
 
1581
                                            attribute.func_globals,
 
1582
                                            attribute.func_name,
 
1583
                                            attribute.func_defaults,
 
1584
                                            attribute.func_closure)))
1615
1585
                    # Copy annotations, if any
1616
1586
                    try:
1617
1587
                        attr[attrname]._dbus_annotations = dict(
1629
1599
                        attribute._dbus_access,
1630
1600
                        attribute._dbus_get_args_options
1631
1601
                        ["byte_arrays"])
1632
 
                                      (copy_function(attribute)))
 
1602
                                      (types.FunctionType(
 
1603
                                          attribute.func_code,
 
1604
                                          attribute.func_globals,
 
1605
                                          attribute.func_name,
 
1606
                                          attribute.func_defaults,
 
1607
                                          attribute.func_closure)))
1633
1608
                    # Copy annotations, if any
1634
1609
                    try:
1635
1610
                        attr[attrname]._dbus_annotations = dict(
1644
1619
                    # to the class.
1645
1620
                    attr[attrname] = (
1646
1621
                        dbus_interface_annotations(alt_interface)
1647
 
                        (copy_function(attribute)))
 
1622
                        (types.FunctionType(attribute.func_code,
 
1623
                                            attribute.func_globals,
 
1624
                                            attribute.func_name,
 
1625
                                            attribute.func_defaults,
 
1626
                                            attribute.func_closure)))
1648
1627
            if deprecate:
1649
1628
                # Deprecate all alternate interfaces
1650
1629
                iname="_AlternateDBusNames_interface_annotation{}"
1663
1642
            if interface_names:
1664
1643
                # Replace the class with a new subclass of it with
1665
1644
                # methods, signals, etc. as created above.
1666
 
                if sys.version_info.major == 2:
1667
 
                    cls = type(b"{}Alternate".format(cls.__name__),
1668
 
                               (cls, ), attr)
1669
 
                else:
1670
 
                    cls = type("{}Alternate".format(cls.__name__),
1671
 
                               (cls, ), attr)
 
1645
                cls = type(b"{}Alternate".format(cls.__name__),
 
1646
                           (cls, ), attr)
1672
1647
        return cls
1673
1648
    
1674
1649
    return wrapper
2543
2518
            fpr = request[1]
2544
2519
            address = request[2]
2545
2520
            
2546
 
            for c in self.clients.values():
 
2521
            for c in self.clients.itervalues():
2547
2522
                if c.fingerprint == fpr:
2548
2523
                    client = c
2549
2524
                    break
3052
3027
    if server_settings["restore"]:
3053
3028
        try:
3054
3029
            with open(stored_state_path, "rb") as stored_state:
3055
 
                if sys.version_info.major == 2:                
3056
 
                    clients_data, old_client_settings = pickle.load(
3057
 
                        stored_state)
3058
 
                else:
3059
 
                    bytes_clients_data, bytes_old_client_settings = (
3060
 
                        pickle.load(stored_state, encoding = "bytes"))
3061
 
                    ### Fix bytes to strings
3062
 
                    ## clients_data
3063
 
                    # .keys()
3064
 
                    clients_data = { (key.decode("utf-8")
3065
 
                                      if isinstance(key, bytes)
3066
 
                                      else key): value
3067
 
                                     for key, value in
3068
 
                                     bytes_clients_data.items() }
3069
 
                    for key in clients_data:
3070
 
                        value = { (k.decode("utf-8")
3071
 
                                   if isinstance(k, bytes) else k): v
3072
 
                                  for k, v in
3073
 
                                  clients_data[key].items() }
3074
 
                        clients_data[key] = value
3075
 
                        # .client_structure
3076
 
                        value["client_structure"] = [
3077
 
                            (s.decode("utf-8")
3078
 
                             if isinstance(s, bytes)
3079
 
                             else s) for s in
3080
 
                            value["client_structure"] ]
3081
 
                        # .name & .host
3082
 
                        for k in ("name", "host"):
3083
 
                            if isinstance(value[k], bytes):
3084
 
                                value[k] = value[k].decode("utf-8")
3085
 
                    ## old_client_settings
3086
 
                    # .keys
3087
 
                    old_client_settings = {
3088
 
                        (key.decode("utf-8")
3089
 
                         if isinstance(key, bytes)
3090
 
                         else key): value
3091
 
                        for key, value in
3092
 
                        bytes_old_client_settings.items() }
3093
 
                    # .host
3094
 
                    for value in old_client_settings.values():
3095
 
                        value["host"] = value["host"].decode("utf-8")
 
3030
                clients_data, old_client_settings = pickle.load(
 
3031
                    stored_state)
3096
3032
            os.remove(stored_state_path)
3097
3033
        except IOError as e:
3098
3034
            if e.errno == errno.ENOENT:
3237
3173
            def GetAllClients(self):
3238
3174
                "D-Bus method"
3239
3175
                return dbus.Array(c.dbus_object_path for c in
3240
 
                                  tcp_server.clients.values())
 
3176
                                  tcp_server.clients.itervalues())
3241
3177
            
3242
3178
            @dbus_annotations({"org.freedesktop.DBus.Deprecated":
3243
3179
                               "true"})
3248
3184
                return dbus.Dictionary(
3249
3185
                    { c.dbus_object_path: c.GetAll(
3250
3186
                        "se.recompile.Mandos.Client")
3251
 
                      for c in tcp_server.clients.values() },
 
3187
                      for c in tcp_server.clients.itervalues() },
3252
3188
                    signature="oa{sv}")
3253
3189
            
3254
3190
            @dbus.service.method(_interface, in_signature="o")
3255
3191
            def RemoveClient(self, object_path):
3256
3192
                "D-Bus method"
3257
 
                for c in tcp_server.clients.values():
 
3193
                for c in tcp_server.clients.itervalues():
3258
3194
                    if c.dbus_object_path == object_path:
3259
3195
                        del tcp_server.clients[c.name]
3260
3196
                        c.remove_from_connection()
3320
3256
        # removed/edited, old secret will thus be unrecovable.
3321
3257
        clients = {}
3322
3258
        with PGPEngine() as pgp:
3323
 
            for client in tcp_server.clients.values():
 
3259
            for client in tcp_server.clients.itervalues():
3324
3260
                key = client_settings[client.name]["secret"]
3325
3261
                client.encrypted_secret = pgp.encrypt(client.secret,
3326
3262
                                                      key)
3350
3286
                    prefix='clients-',
3351
3287
                    dir=os.path.dirname(stored_state_path),
3352
3288
                    delete=False) as stored_state:
3353
 
                pickle.dump((clients, client_settings), stored_state,
3354
 
                            protocol = 2)
 
3289
                pickle.dump((clients, client_settings), stored_state)
3355
3290
                tempname = stored_state.name
3356
3291
            os.rename(tempname, stored_state_path)
3357
3292
        except (IOError, OSError) as e:
3382
3317
    
3383
3318
    atexit.register(cleanup)
3384
3319
    
3385
 
    for client in tcp_server.clients.values():
 
3320
    for client in tcp_server.clients.itervalues():
3386
3321
        if use_dbus:
3387
3322
            # Emit D-Bus signal for adding
3388
3323
            mandos_dbus_service.client_added_signal(client)