/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-ctl

  • Committer: Teddy Hogeborn
  • Date: 2009-02-09 02:01:13 UTC
  • Revision ID: teddy@fukt.bsnet.se-20090209020113-726hq380zvp8zt97
Four new interrelated features:

1. Support using a different network interface via both initramfs.conf
   (the DEVICE setting) and the kernel command line (sixth field of
   the "ip=" option as in Linux' Documentation/nfsroot.txt).

2. Support connecting to a specified Mandos server directly using a
   kernel command line option ("mandos=connect:<ADDRESS>:<PORT>").

3. Support connecting directly to an IPv4 address (and port) using the
   "--connect" option of mandos-client.

4. Support an empty string to the --interface option to mandos-client.

* Makefile (WARN): Increase strictness by changing to
                   "-Wstrict-aliasing=1".

* debian/mandos-client.README.Debian (Use the Correct Network
  Interface): Changed to refer to initramfs.conf and nfsroot.txt.
  (Test the Server): Improve wording.
  (Non-local Connection): New section.
* initramfs-tools-script: Obey DEVICE environment variable and setting
                          from "/conf/initramfs.conf".  Also let any
                          "ip=" kernel command line option override
                          it.  Support new "mandos=connect" option.
                          Call "configure_networking" to set up IP
                          address on interface if necessary.
* plugin-runner.conf: Change example.
* plugins.d/mandos-client.c: Some whitespace and comment changes.
  (start_mandos_communication): Take an additional argument for
                                address family, all callers changed.
                                Connect to an IPv4 address if address
                                family is AF_INET.  Only set IPv6
                                scope_id for link-local addresses.
  (main): Accept empty interface name; this will not bring up any
         interface and leave the interface as unspecified.  Also do
         not restore kernel log level if lowering it failed.
* plugins.d/mandos-client.xml (OPTIONS): Document that the
                                         "--interface" option accepts
                                         an empty string.
  (EXAMPLE): Change example IPv6 address to a link-local address.

Show diffs side-by-side

added added

removed removed

Lines of Context:
8
8
import locale
9
9
import datetime
10
10
import re
11
 
import os
12
11
 
13
12
locale.setlocale(locale.LC_ALL, u'')
14
13
 
15
14
tablewords = {
16
 
    'Name': u'Name',
17
 
    'Enabled': u'Enabled',
18
 
    'Timeout': u'Timeout',
19
 
    'LastCheckedOK': u'Last Successful Check',
20
 
    'Created': u'Created',
21
 
    'Interval': u'Interval',
22
 
    'Host': u'Host',
23
 
    'Fingerprint': u'Fingerprint',
24
 
    'CheckerRunning': u'Check Is Running',
25
 
    'LastEnabled': u'Last Enabled',
26
 
    'Checker': u'Checker',
 
15
    'name': u'Name',
 
16
    'enabled': u'Enabled',
 
17
    'timeout': u'Timeout',
 
18
    'last_checked_ok': u'Last Successful Check',
 
19
    'created': u'Created',
 
20
    'interval': u'Interval',
 
21
    'host': u'Host',
 
22
    'fingerprint': u'Fingerprint',
 
23
    'checker_running': u'Check Is Running',
 
24
    'last_enabled': u'Last Enabled',
 
25
    'checker': u'Checker',
27
26
    }
28
 
defaultkeywords = ('Name', 'Enabled', 'Timeout', 'LastCheckedOK')
29
 
domain = 'se.bsnet.fukt'
30
 
busname = domain + '.Mandos'
31
 
server_path = '/'
32
 
server_interface = domain + '.Mandos'
33
 
client_interface = domain + '.Mandos.Client'
34
 
version = "1.0.14"
35
 
 
36
 
def timedelta_to_milliseconds(td):
37
 
    "Convert a datetime.timedelta object to milliseconds"
38
 
    return ((td.days * 24 * 60 * 60 * 1000)
39
 
            + (td.seconds * 1000)
40
 
            + (td.microseconds // 1000))
 
27
defaultkeywords = ('name', 'enabled', 'timeout', 'last_checked_ok',
 
28
                   'checker')
 
29
busname = 'org.mandos-system.Mandos'
 
30
server_path = '/Mandos'
 
31
server_interface = 'org.mandos_system.Mandos'
 
32
client_interface = 'org.mandos_system.Mandos.Client'
 
33
version = "1.0.5"
 
34
 
 
35
bus = dbus.SystemBus()
 
36
mandos_dbus_objc = bus.get_object(busname, server_path)
 
37
mandos_serv = dbus.Interface(mandos_dbus_objc,
 
38
                             dbus_interface = server_interface)
 
39
mandos_clients = mandos_serv.GetAllClientsWithProperties()
 
40
 
 
41
def datetime_to_milliseconds(dt):
 
42
    "Return the 'timeout' attribute in milliseconds"
 
43
    return ((dt.days * 24 * 60 * 60 * 1000)
 
44
            + (dt.seconds * 1000)
 
45
            + (dt.microseconds // 1000))
41
46
 
42
47
def milliseconds_to_string(ms):
43
48
    td = datetime.timedelta(0, 0, 0, ms)
44
 
    return (u"%(days)s%(hours)02d:%(minutes)02d:%(seconds)02d"
45
 
            % { "days": "%dT" % td.days if td.days else "",
46
 
                "hours": td.seconds // 3600,
47
 
                "minutes": (td.seconds % 3600) // 60,
48
 
                "seconds": td.seconds % 60,
49
 
                })
 
49
    return "%s%02d:%02d:%02d" % (("%dT" % td.days) if td.days else "", # days
 
50
                           td.seconds // 3600,        # hours
 
51
                           (td.seconds % 3600) // 60, # minutes
 
52
                           (td.seconds % 60))         # seconds
50
53
 
51
54
 
52
55
def string_to_delta(interval):
89
92
        timevalue += delta
90
93
    return timevalue
91
94
 
92
 
def print_clients(clients, keywords):
 
95
def print_clients(clients):
93
96
    def valuetostring(value, keyword):
94
97
        if type(value) is dbus.Boolean:
95
98
            return u"Yes" if value else u"No"
96
 
        if keyword in (u"timeout", u"interval"):
 
99
        if keyword in ("timeout", "interval"):
97
100
            return milliseconds_to_string(value)
98
101
        return unicode(value)
99
102
    
100
 
    # Create format string to print table rows
101
103
    format_string = u' '.join(u'%%-%ds' %
102
104
                              max(len(tablewords[key]),
103
 
                                  max(len(valuetostring(client[key],
104
 
                                                        key))
 
105
                                  max(len(valuetostring(client[key], key))
105
106
                                      for client in
106
107
                                      clients))
107
108
                              for key in keywords)
108
 
    # Print header line
109
 
    print format_string % tuple(tablewords[key] for key in keywords)
 
109
    print format_string % tuple(tablewords[key] for key in keywords) 
110
110
    for client in clients:
111
111
        print format_string % tuple(valuetostring(client[key], key)
112
112
                                    for key in keywords)
113
 
def has_actions(options):
114
 
    return any((options.enable,
115
 
                options.disable,
116
 
                options.bump_timeout,
117
 
                options.start_checker,
118
 
                options.stop_checker,
119
 
                options.is_enabled,
120
 
                options.remove,
121
 
                options.checker is not None,
122
 
                options.timeout is not None,
123
 
                options.interval is not None,
124
 
                options.host is not None,
125
 
                options.secret is not None,
126
 
                options.approve,
127
 
                options.deny))
128
 
        
129
 
def main():
130
 
        parser = OptionParser(version = "%%prog %s" % version)
131
 
        parser.add_option("-a", "--all", action="store_true",
132
 
                          help="Select all clients")
133
 
        parser.add_option("-v", "--verbose", action="store_true",
134
 
                          help="Print all fields")
135
 
        parser.add_option("-e", "--enable", action="store_true",
136
 
                          help="Enable client")
137
 
        parser.add_option("-d", "--disable", action="store_true",
138
 
                          help="disable client")
139
 
        parser.add_option("-b", "--bump-timeout", action="store_true",
140
 
                          help="Bump timeout for client")
141
 
        parser.add_option("--start-checker", action="store_true",
142
 
                          help="Start checker for client")
143
 
        parser.add_option("--stop-checker", action="store_true",
144
 
                          help="Stop checker for client")
145
 
        parser.add_option("-V", "--is-enabled", action="store_true",
146
 
                          help="Check if client is enabled")
147
 
        parser.add_option("-r", "--remove", action="store_true",
148
 
                          help="Remove client")
149
 
        parser.add_option("-c", "--checker", type="string",
150
 
                          help="Set checker command for client")
151
 
        parser.add_option("-t", "--timeout", type="string",
152
 
                          help="Set timeout for client")
153
 
        parser.add_option("-i", "--interval", type="string",
154
 
                          help="Set checker interval for client")
155
 
        parser.add_option("-H", "--host", type="string",
156
 
                          help="Set host for client")
157
 
        parser.add_option("-s", "--secret", type="string",
158
 
                          help="Set password blob (file) for client")
159
 
        parser.add_option("-A", "--approve", action="store_true",
160
 
                          help="Approve any current client request")
161
 
        parser.add_option("-D", "--deny", action="store_true",
162
 
                          help="Deny any current client request")
163
 
        options, client_names = parser.parse_args()
164
 
 
165
 
        if has_actions(options) and not client_names and not options.all:
166
 
            parser.error('Options requires clients names or --all.')
167
 
        if options.verbose and has_actions(options):
168
 
            parser.error('Verbose option can only be used alone or with --all.')
169
 
        if options.all and not has_actions(options):
170
 
            parser.error('--all requires an action')
171
 
            
172
 
        try:
173
 
            bus = dbus.SystemBus()
174
 
            mandos_dbus_objc = bus.get_object(busname, server_path)
175
 
        except dbus.exceptions.DBusException:
176
 
            print >> sys.stderr, "Could not connect to Mandos server"
177
 
            sys.exit(1)
 
113
 
 
114
parser = OptionParser(version = "%%prog %s" % version)
 
115
parser.add_option("-a", "--all", action="store_true",
 
116
                  help="Print all fields")
 
117
parser.add_option("-e", "--enable", action="store_true",
 
118
                  help="Enable client")
 
119
parser.add_option("-d", "--disable", action="store_true",
 
120
                  help="disable client")
 
121
parser.add_option("-b", "--bump-timeout", action="store_true",
 
122
                  help="Bump timeout for client")
 
123
parser.add_option("--start-checker", action="store_true",
 
124
                  help="Start checker for client")
 
125
parser.add_option("--stop-checker", action="store_true",
 
126
                  help="Stop checker for client")
 
127
parser.add_option("-V", "--is-valid", action="store_true",
 
128
                  help="Check if client is still valid")
 
129
parser.add_option("-r", "--remove", action="store_true",
 
130
                  help="Remove client")
 
131
parser.add_option("-c", "--checker", type="string",
 
132
                  help="Set checker command for client")
 
133
parser.add_option("-t", "--timeout", type="string",
 
134
                  help="Set timeout for client")
 
135
parser.add_option("-i", "--interval", type="string",
 
136
                  help="Set checker interval for client")
 
137
parser.add_option("-H", "--host", type="string",
 
138
                  help="Set host for client")
 
139
parser.add_option("-s", "--secret", type="string",
 
140
                  help="Set password blob (file) for client")
 
141
options, client_names = parser.parse_args()
 
142
 
 
143
# Compile list of clients to process
 
144
clients=[]
 
145
for name in client_names:
 
146
    for path, client in mandos_clients.iteritems():
 
147
        if client['name'] == name:
 
148
            client_objc = bus.get_object(busname, path)
 
149
            clients.append(dbus.Interface(client_objc,
 
150
                                          dbus_interface
 
151
                                          = client_interface))
 
152
            break
 
153
    else:
 
154
        print >> sys.stderr, "Client not found on server: %r" % name
 
155
        sys.exit(1)
 
156
 
 
157
if not clients and mandos_clients.values():
 
158
    keywords = defaultkeywords
 
159
    if options.all:
 
160
        keywords = ('name', 'enabled', 'timeout', 'last_checked_ok',
 
161
                    'created', 'interval', 'host', 'fingerprint',
 
162
                    'checker_running', 'last_enabled', 'checker')
 
163
    print_clients(mandos_clients.values())
 
164
 
 
165
# Process each client in the list by all selected options
 
166
for client in clients:
 
167
    if options.remove:
 
168
        mandos_serv.RemoveClient(client.__dbus_object_path__)
 
169
    if options.enable:
 
170
        client.Enable()
 
171
    if options.disable:
 
172
        client.Disable()
 
173
    if options.bump_timeout:
 
174
        client.BumpTimeout()
 
175
    if options.start_checker:
 
176
        client.StartChecker()
 
177
    if options.stop_checker:
 
178
        client.StopChecker()
 
179
    if options.is_valid:
 
180
        sys.exit(0 if client.IsStillValid() else 1)
 
181
    if options.checker:
 
182
        client.SetChecker(options.checker)
 
183
    if options.host:
 
184
        client.SetHost(options.host)
 
185
    if options.interval:
 
186
        client.SetInterval(datetime_to_milliseconds
 
187
                           (string_to_delta(options.interval)))
 
188
    if options.timeout:
 
189
        client.SetTimeout(datetime_to_milliseconds
 
190
                          (string_to_delta(options.timeout)))
 
191
    if options.secret:
 
192
        client.SetSecret(dbus.ByteArray(open(options.secret, 'rb').read()))
178
193
    
179
 
        mandos_serv = dbus.Interface(mandos_dbus_objc,
180
 
                                     dbus_interface = server_interface)
181
 
 
182
 
        #block stderr since dbus library prints to stderr
183
 
        null = os.open(os.path.devnull, os.O_RDWR)
184
 
        stderrcopy = os.dup(sys.stderr.fileno())
185
 
        os.dup2(null, sys.stderr.fileno())
186
 
        os.close(null)
187
 
        try:
188
 
            try:
189
 
                mandos_clients = mandos_serv.GetAllClientsWithProperties()
190
 
            finally:
191
 
                #restore stderr
192
 
                os.dup2(stderrcopy, sys.stderr.fileno())
193
 
                os.close(stderrcopy)
194
 
        except dbus.exceptions.DBusException, e:
195
 
            print >> sys.stderr, "Access denied: Accessing mandos server through dbus."
196
 
            sys.exit(1)
197
 
            
198
 
        # Compile list of clients to process
199
 
        clients=[]
200
 
 
201
 
        if options.all or not client_names:
202
 
            clients = (bus.get_object(busname, path) for path in mandos_clients.iterkeys())
203
 
        else:
204
 
            for name in client_names:
205
 
                for path, client in mandos_clients.iteritems():
206
 
                    if client['Name'] == name:
207
 
                        client_objc = bus.get_object(busname, path)
208
 
                        clients.append(client_objc)
209
 
                        break
210
 
                else:
211
 
                    print >> sys.stderr, "Client not found on server: %r" % name
212
 
                    sys.exit(1)
213
 
            
214
 
        if not has_actions(options) and clients:
215
 
            if options.verbose:
216
 
                keywords = ('Name', 'Enabled', 'Timeout', 'LastCheckedOK',
217
 
                            'Created', 'Interval', 'Host', 'Fingerprint',
218
 
                            'CheckerRunning', 'LastEnabled', 'Checker')
219
 
            else:
220
 
                keywords = defaultkeywords
221
 
                
222
 
            print_clients(mandos_clients.values(), keywords)
223
 
        else:
224
 
            # Process each client in the list by all selected options
225
 
            for client in clients:
226
 
                if options.remove:
227
 
                    mandos_serv.RemoveClient(client.__dbus_object_path__)
228
 
                if options.enable:
229
 
                    client.Enable(dbus_interface=client_interface)
230
 
                if options.disable:
231
 
                    client.Disable(dbus_interface=client_interface)
232
 
                if options.bump_timeout:
233
 
                    client.CheckedOK(dbus_interface=client_interface)
234
 
                if options.start_checker:
235
 
                    client.StartChecker(dbus_interface=client_interface)
236
 
                if options.stop_checker:
237
 
                    client.StopChecker(dbus_interface=client_interface)
238
 
                if options.is_enabled:
239
 
                    sys.exit(0 if client.Get(client_interface,
240
 
                                             u"Enabled",
241
 
                                             dbus_interface=dbus.PROPERTIES_IFACE)
242
 
                             else 1)
243
 
                if options.checker:
244
 
                    client.Set(client_interface, u"Checker", options.checker,
245
 
                               dbus_interface=dbus.PROPERTIES_IFACE)
246
 
                if options.host:
247
 
                    client.Set(client_interface, u"Host", options.host,
248
 
                               dbus_interface=dbus.PROPERTIES_IFACE)
249
 
                if options.interval:
250
 
                    client.Set(client_interface, u"Interval",
251
 
                               timedelta_to_milliseconds
252
 
                               (string_to_delta(options.interval)),
253
 
                               dbus_interface=dbus.PROPERTIES_IFACE)
254
 
                if options.timeout:
255
 
                    client.Set(client_interface, u"Timeout",
256
 
                               timedelta_to_milliseconds(string_to_delta
257
 
                                                         (options.timeout)),
258
 
                               dbus_interface=dbus.PROPERTIES_IFACE)
259
 
                if options.secret:
260
 
                    client.Set(client_interface, u"Secret",
261
 
                               dbus.ByteArray(open(options.secret, u'rb').read()),
262
 
                               dbus_interface=dbus.PROPERTIES_IFACE)
263
 
                if options.approve:
264
 
                    client.Approve(dbus.Boolean(True), dbus_interface=client_interface)
265
 
                if options.deny:
266
 
                    client.Approve(dbus.Boolean(False), dbus_interface=client_interface)
267
 
 
268
 
if __name__ == '__main__':
269
 
    main()