1# -*- coding: utf-8 -*-
2#
3# (c) Copyright 2003-2015 HP Development Company, L.P.
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
18#
19# Author: Stan Dolson , Goutam Kodu
20#
21
22# Std Lib
23import os
24import os.path
25import sys
26
27# Local
28from .g import *
29from .codes import *
30from . import utils, password
31from installer import pluginhandler
32
33# DBus
34import dbus
35import dbus.service
36
37if PY3:
38    from gi import _gobject as gobject
39else:
40    import gobject
41
42import warnings
43# Ignore: .../dbus/connection.py:242: DeprecationWarning: object.__init__() takes no parameters
44# (occurring on Python 2.6/dBus 0.83/Ubuntu 9.04)
45warnings.simplefilter("ignore", DeprecationWarning)
46
47
48class AccessDeniedException(dbus.DBusException):
49    _dbus_error_name = 'com.hp.hplip.AccessDeniedException'
50
51class UnsupportedException(dbus.DBusException):
52    _dbus_error_name = 'com.hp.hplip.UnsupportedException'
53
54class UsageError(dbus.DBusException):
55    _dbus_error_name = 'com.hp.hplip.UsageError'
56
57
58POLICY_KIT_ACTION = "com.hp.hplip"
59INSTALL_PLUGIN_ACTION = "com.hp.hplip.installplugin"
60
61
62def get_service_bus():
63    return dbus.SystemBus()
64
65
66def get_service(bus=None):
67    if not bus:
68        bus = get_service_bus()
69
70    service = bus.get_object(BackendService.SERVICE_NAME, '/')
71    service = dbus.Interface(service, BackendService.INTERFACE_NAME)
72    return service
73
74
75class PolicyKitAuthentication(object):
76    def __init__(self):
77        super(PolicyKitAuthentication, self).__init__()
78        self.pkit = None
79        self.auth = None
80
81
82    def is_authorized(self, action_id, pid=None):
83        if pid == None:
84            pid = os.getpid()
85
86        pid = dbus.UInt32(pid)
87
88        authorized = self.policy_kit.IsProcessAuthorized(action_id, pid, False)
89        log.debug("is_authorized(%s) = %r" % (action_id, authorized))
90
91        return (authorized == 'yes')
92
93
94    def obtain_authorization(self, action_id, widget=None):
95        if self.is_authorized(action_id):
96            return True
97
98        xid = (widget and widget.get_toplevel().window.xid or 0)
99        xid, pid = dbus.UInt32(xid), dbus.UInt32(os.getpid())
100
101        granted = self.auth_agent.ObtainAuthorization(action_id, xid, pid)
102        log.debug("obtain_authorization(%s) = %r" % (action_id, granted))
103
104        return bool(granted)
105
106
107    def get_policy_kit(self):
108        if self.pkit:
109            return self.pkit
110
111        service = dbus.SystemBus().get_object('org.freedesktop.PolicyKit', '/')
112        self.pkit = dbus.Interface(service, 'org.freedesktop.PolicyKit')
113        return self.pkit
114
115    policy_kit = property(get_policy_kit)
116
117
118    def get_auth_agent(self):
119        if self.auth:
120            return self.auth
121
122        self.auth = dbus.SessionBus().get_object(
123            'org.freedesktop.PolicyKit.AuthenticationAgent', '/')
124        return self.auth
125
126    auth_agent = property(get_auth_agent)
127
128
129
130class PolicyKitService(dbus.service.Object):
131    def check_permission_v0(self, sender, action=POLICY_KIT_ACTION):
132        if not sender:
133            log.error("Session not authorized by PolicyKit")
134            raise AccessDeniedException('Session not authorized by PolicyKit')
135
136        try:
137            policy_auth = PolicyKitAuthentication()
138            bus = dbus.SystemBus()
139
140            dbus_object = bus.get_object('org.freedesktop.DBus', '/')
141            dbus_object = dbus.Interface(dbus_object, 'org.freedesktop.DBus')
142
143            pid = dbus.UInt32(dbus_object.GetConnectionUnixProcessID(sender))
144
145            granted = policy_auth.is_authorized(action, pid)
146            if not granted:
147                log.error("Process not authorized by PolicyKit")
148                raise AccessDeniedException('Process not authorized by PolicyKit')
149
150            granted = policy_auth.policy_kit.IsSystemBusNameAuthorized(action,
151                                                                       sender,
152                                                                       False)
153            if granted != 'yes':
154                log.error("Session not authorized by PolicyKit version 0")
155                raise AccessDeniedException('Session not authorized by PolicyKit')
156
157        except AccessDeniedException:
158            log.warning("AccessDeniedException")
159            raise
160
161        except dbus.DBusException as ex:
162            log.warning("AccessDeniedException %r", ex)
163            raise AccessDeniedException(ex.message)
164
165
166    def check_permission_v1(self, sender, connection, action=POLICY_KIT_ACTION):
167        if not sender or not connection:
168            log.error("Session not authorized by PolicyKit")
169            raise AccessDeniedException('Session not authorized by PolicyKit')
170
171        system_bus = dbus.SystemBus()
172        obj = system_bus.get_object("org.freedesktop.PolicyKit1",
173                                    "/org/freedesktop/PolicyKit1/Authority",
174                                    "org.freedesktop.PolicyKit1.Authority")
175        policy_kit = dbus.Interface(obj, "org.freedesktop.PolicyKit1.Authority")
176
177        subject = (
178           'system-bus-name',
179            { 'name' : dbus.String(sender, variant_level = 1) }
180        )
181        details = { '' : '' }
182        flags = dbus.UInt32(1)         # AllowUserInteraction = 0x00000001
183        cancel_id = ''
184
185        (ok, notused, details) = \
186            policy_kit.CheckAuthorization(subject,
187                                          action,
188                                          details,
189                                          flags,
190                                          cancel_id)
191        if not ok:
192            log.error("Session not authorized by PolicyKit version 1")
193            raise AccessDeniedException("Session not authorized by PolicyKit")
194
195        return ok
196
197
198if utils.to_bool(sys_conf.get('configure', 'policy-kit')):
199    class BackendService(PolicyKitService):
200        INTERFACE_NAME = 'com.hp.hplip'
201        SERVICE_NAME   = 'com.hp.hplip'
202
203        def __init__(self, connection=None, path='/'):
204            if connection is None:
205                connection = get_service_bus()
206
207            super(BackendService, self).__init__(connection, path)
208
209            self.name = dbus.service.BusName(self.SERVICE_NAME, connection)
210            self.loop = gobject.MainLoop()
211            self.version = 0
212            log.set_level("debug")
213
214        def run(self, version=None):
215            if version is None:
216                version = policykit_version()
217                if version is None:
218                    log.error("Unable to determine installed PolicyKit version")
219                    return
220
221            self.version = version
222            log.debug("Starting back-end service loop (version %d)" % version)
223
224            self.loop.run()
225
226
227        @dbus.service.method(dbus_interface=INTERFACE_NAME,
228                                in_signature='s', out_signature='b',
229                                sender_keyword='sender',
230                                connection_keyword='connection')
231        def installPlugin(self, src_dir, sender=None, connection=None):
232            if self.version == 0:
233                try:
234                    self.check_permission_v0(sender, INSTALL_PLUGIN_ACTION)
235                except AccessDeniedException as e:
236                    log.error("installPlugin:  Failed due to permission error [%s]" %e)
237                    return False
238
239            elif self.version == 1:
240                if not self.check_permission_v1(sender,
241                                                connection,
242                                                INSTALL_PLUGIN_ACTION):
243                    return False
244
245            else:
246                log.error("installPlugin: invalid PolicyKit version %d" % self.version)
247                return False
248
249            log.debug("installPlugin: installing from '%s'" % src_dir)
250            try:
251                from installer import pluginhandler
252            except ImportError as e:
253                log.error("Failed to Import pluginhandler")
254                return False
255
256            pluginObj = pluginhandler.PluginHandle()
257            if not pluginObj.copyFiles(src_dir):
258                log.error("Plugin installation failed")
259                return False
260
261            return True
262
263
264        @dbus.service.method(dbus_interface=INTERFACE_NAME,
265                                in_signature='s', out_signature='b',
266                                sender_keyword='sender',
267                                connection_keyword='connection')
268        def shutdown(self, arg, sender=None, connection=None):
269            log.debug("Stopping backend service")
270            self.loop.quit()
271
272            return True
273
274
275
276class PolicyKit(object):
277    def __init__(self, version=None):
278        if version is None:
279            version = policykit_version()
280            if version is None:
281                log.debug("Unable to determine installed PolicyKit version")
282                return
283
284        self.bus = dbus.SystemBus()
285        self.obj = self.bus.get_object(POLICY_KIT_ACTION, "/")
286        self.iface = dbus.Interface(self.obj, dbus_interface=POLICY_KIT_ACTION)
287        self.version = version
288
289    def installPlugin(self, src_dir):
290        if self.version == 0:
291            auth = PolicyKitAuthentication()
292            if not auth.is_authorized(INSTALL_PLUGIN_ACTION):
293                if not auth.obtain_authorization(INSTALL_PLUGIN_ACTION):
294                    return None
295
296        try:
297            ok = self.iface.installPlugin(src_dir)
298            return ok
299        except dbus.DBusException as e:
300            log.debug("installPlugin: %s" % str(e))
301            return False
302
303
304    def shutdown(self):
305        if self.version == 0:
306            auth = PolicyKitAuthentication()
307            if not auth.is_authorized(INSTALL_PLUGIN_ACTION):
308                if not auth.obtain_authorization(INSTALL_PLUGIN_ACTION):
309                    return None
310
311        try:
312            ok = self.iface.shutdown("")
313            return ok
314        except dbus.DBusException as e:
315            log.debug("shutdown: %s" % str(e))
316            return False
317
318
319
320
321
322def run_plugin_command(required=True, plugin_reason=PLUGIN_REASON_NONE, Mode = GUI_MODE):
323
324    if utils.to_bool(sys_conf.get('configure', 'policy-kit')):
325        try:
326            obj = PolicyKit()
327            su_sudo = "%s"
328            need_sudo = False
329            log.debug("Using PolicyKit for authentication")
330        except dbus.DBusException as ex:
331            log.error("PolicyKit NOT installed when configured for use. [%s]"%ex)
332
333    req = '--required'
334    if not required:
335        req = '--optional'
336
337    if utils.which("hp-plugin"):
338        p_path="hp-plugin"
339    else:
340        p_path="python ./plugin.py"
341
342    cmd = "%s -u %s --reason %s" %(p_path, req, plugin_reason)
343    log.debug("%s" % cmd)
344    status = os_utils.execute(cmd)
345
346    return (status == 0, True)
347
348
349def policykit_version():
350    if os.path.isdir("/usr/local/share/polkit-1"):
351        return 1
352    elif os.path.isdir("/usr/local/share/PolicyKit"):
353        return 0
354    else:
355        return None
356