使用policykit 的程序一般都有一个dbus daemon程序来完成相关操作,这个dbus daemon 会在系统注册一个system bus 服务名,用于响应要求root privileged的操作,当dbus请求到达时会先验证请求程序是否有相应的权限来调用这个操作(方法),而这是在.conf文件中定义的(后面说明)。

首先定义个System Dbus daemon,写一个.service文件来启动我们的daemon

org.example.foo.service

文件放置目录:/usr/share/dbus-1/system-services

1 [D-BUS Service]
2 Name=org.example.foo
3 Exec=/usr/local/libexec/policykit_dbus_foo_daemon.py
4 User=root

其中Name是注册的SystemBus 服务名

Exec 是daemon 程序所在路径

我们以root权限启动

当有程序请求org.example.foo服务时,系统会自动以root启动我们的daemon。

相关信息看这里D-Bus system bus activation

注: SessionBus 的 'org.freedesktop.PolicyKit.AuthenticationAgent' 的服务,只有在请求认证的时候才自动启动,打开过一段时间会自动关闭。

再看我们的daemon程序

policykit_dbus_foo_daemon.py

文件放置目录:/usr/local/libexec


 1 #!/usr/bin/python
2 # -*- coding: UTF-8 -*-
3 """
4 Author: joe.zhou
5 """
6 import os
7 import sys
8 import gobject
9 import dbus
10 import dbus.service
11 import dbus.mainloop.glib
12
13 class NotPrivilegedException (dbus.DBusException):
14 _dbus_error_name = "org.example.foo.dbus.service.PolKit.NotPrivilegedException"
15 def __init__ (self, action_id, *p, **k):
16 self._dbus_error_name = self.__class__._dbus_error_name + "." + action_id
17 super (NotPrivilegedException, self).__init__ (*p, **k)
18
19 def require_auth (action_id):
20 def require_auth_decorator(func):
21 def _func(*args,**kwds):
22 revoke_if_one_shot = True
23 system_bus = dbus.SystemBus()
24 auth_obj = system_bus.get_object('org.freedesktop.PolicyKit','/')
25 auth_interface = dbus.Interface(auth_obj,'org.freedesktop.PolicyKit')
26 try:
27 dbus_name = kwds['sender_keyword']
28 except:
29 raise NotPrivilegedException (action_id)
30 granted = auth_interface.IsSystemBusNameAuthorized(action_id,dbus_name,revoke_if_one_shot)
31 if granted != 'yes':
32 raise NotPrivilegedException (action_id)
33
34 return func(*args,**kwds)
35
36 _func.func_name = func.func_name
37 _func.__name__ = func.__name__
38 _func.__doc__ = func.__doc__
39 return _func
40 return require_auth_decorator
41
42 '''
43 A D-Bus service that PolicyKit controls access to.
44 '''
45 class PolicyKitFooMechanism(dbus.service.Object):
46 SERVICE_NAME = 'org.example.foo'
47 SERVICE_PATH = '/org/example/foo'
48 INTERFACE_NAME = 'org.example.foo'
49
50 def __init__(self, conn, object_path=SERVICE_PATH):
51 dbus.service.Object.__init__(self, conn, object_path)
52
53 @dbus.service.method(dbus_interface=INTERFACE_NAME, in_signature='ss',out_signature='s',sender_keyword='sender')
54 def WriteFile(self, filepath, contents,sender=None):
55 '''
56 Write the contents to a file that requires sudo/root access to do so.
57 PolicyKit will not allow this function to be called without sudo/root
58 access, and will ask the user to authenticate if necessary, when
59 the application calls PolicyKit's ObtainAuthentication().
60 '''
61 @require_auth('org.example.foo.modify')
62 def _write_file(filepath,contents,sender_keyword = None):
63 f = open(filepath, 'w')
64 f.write(contents)
65 f.close()
66 return 'done'
67 return _write_file(filepath,contents,sender_keyword = sender)
68
69 @dbus.service.method(dbus_interface=INTERFACE_NAME, in_signature='s',out_signature='as',sender_keyword='sender')
70 def RunCmd(self, cmdStr, sender=None):
71 @require_auth('org.example.foo.sys')
72 def _run_cmd(cmdStr,sender_keyword = None):
73 f = os.popen(cmdStr)
74 output = f.readlines()
75 f.close()
76 return output
77 return _run_cmd(cmdStr,sender_keyword = sender)
78
79 @dbus.service.method(dbus_interface=INTERFACE_NAME,in_signature='', out_signature='',sender_keyword='sender')
80 def Exit(self, sender=None):
81 @require_auth('org.example.foo.sys')
82 def _exit(sender_keyword = None):
83 loop.quit()
84 return _exit(sender_keyword = sender)
85
86 @dbus.service.method(dbus_interface=INTERFACE_NAME,in_signature='', out_signature='s')
87 def hello(self):
88 return 'hello'
89
90 if __name__ == '__main__':
91 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
92 bus = dbus.SystemBus()
93 name = dbus.service.BusName(PolicyKitFooMechanism.SERVICE_NAME, bus)
94 the_object = PolicyKitFooMechanism(bus)
95 loop = gobject.MainLoop()
96 loop.run()
97
98

在daemon程序中定义了三个需要权限的操作,一个不需要权限的操作,也定义了操作(方法)要求验证的action id,程序請求写文件操作时需先向org.freedesktop.PolicyKit.AuthenticationAgent 请求对应的 action id权限才能再进行调用,

否则会提示没有权限,hello操作不需要操作权限,两者区别在于来请求时是否先向 org.freedesktop.Policykit 检测这个 dbus 請求是否有 previleged。

具体是调用 IsSystemBusNameAuthorized 方法来验证,通过则返回'yes',否则 返回其它字符串

使用命令以下命令查看方法的 IsSystemBusNameAuthorized 详细 introspec

dbus-send --system --print-reply --dest=org.freedesktop.PolicyKit / org.freedesktop.DBus.Introspectable.Introspect

在这里值得注意的是如果你定义了一系列的系统级调用操作(以root方式启动前面的程序,但去除了前面的@require_auth 部分),你必须保证每个操作要进行权限验证,即加上这个东西@require_auth('org.example.foo.sys')

如果你定义了写文件的dbus操作,但是没有进行权限验证的话,一个普通用户的dbus 调用請求也会调用通过,即普通用户可以随意改写任何文件,这是很危险的

你也可以尝试把前面的@require_auth部分去掉,再启动服务,用d-feet 就可以调用WriteFile方法随意地在根目录上写入文件

--题外话——

本想将程序写成这种形式的

1 @require_auth('org.example.foo.sys')
2 @dbus.service.method(dbus_interface=INTERFACE_NAME,in_signature='', out_signature='',sender_keyword='sender')
3 def Exit(self, sender=None):
4 loop.quit()

这样写,用d-feet 看了下,服务起不来,调了很久也找不出原因,无奈写成这种冗余的方式 --!,看能否有高手指点下,不尽感激!!

接着定义谁可以调用这些操作(方法),在.conf 文件定义

org.example.foo.conf

文件放置目录:/etc/dbus-1/system.d


 1 <?xml version="1.0" encoding="UTF-8"?> <!-- -*- XML -*- -->
2
3 <!DOCTYPE busconfig PUBLIC
4 "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
5 "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
6 <busconfig>
7
8 <!-- Only root can own the service -->
9 <policy user="root">
10 <allow own="org.example.foo"/>
11 <allow send_interface="org.example.foo"/>
12 </policy>
13
14 <!-- allow Introspectable --><!-- 任何人都可以调用,在后面使用.policy进行約束-->
15 <policy context="default">
16 <allow send_interface="org.example.foo"/>
17 <allow send_interface="org.freedesktop.DBus.Introspectable"/>
18 </policy>
19
20 </busconfig>
21

再跟着是定义相关的 action id了,在.policy 文件定义

其中定义授权认证的方式和时效可以有以下几种


no
auth_self$$
auth_admin$$
yes 其中加$$的可以附加后缀 _one_shot,_keep_session,_keep_always
其意义字面已经很清楚了

另外也可以看看 man policykit.conf

不会写?参照/usr/share/PolicyKit/policy 目录下一堆 .policy文件总会了吧

写好后可以用工具 polkit-policy-file-validate 验证下是否有效

org.example.foo.policy

文件放置目录:/usr/share/PolicyKit/policy


 1 <?xml version="1.0" encoding="UTF-8"?>
2 <!DOCTYPE policyconfig PUBLIC
3 "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
4 "http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">
5 <policyconfig>
6
7 <vendor>Example Application</vendor>
8 <vendor_url>http://fedoraproject.org/example</vendor_url>
9
10 <action id="org.example.foo.modify">
11 <description>Example Write Access</description>
12 <message>System policy prevents write access to the Example service</message>
13 <defaults>
14 <allow_inactive>no</allow_inactive>
15 <allow_active>auth_admin</allow_active>
16 </defaults>
17 </action>
18
19 <action id="org.example.foo.sys">
20 <description>Example system action</description>
21 <message>System policy prevents do system action to the Example service</message>
22 <defaults>
23 <allow_inactive>no</allow_inactive>
24 <allow_active>auth_admin</allow_active>
25 </defaults>
26 </action>
27
28
29 </policyconfig>

做好以上工作,我们可以写我们的调用端程序了


 1 #!/usr/bin/python
2 # -*- coding: UTF-8 -*-
3 import os
4 import sys
5 import gobject
6 import dbus
7 import dbus.service
8 import dbus.mainloop.glib
9 import traceback
10
11 def auth_proxy (func):
12 DBUSNAME = 'org.freedesktop.PolicyKit.AuthenticationAgent'
13 DBUSPATH = '/'
14 DBUSINTERFACE = 'org.freedesktop.PolicyKit.AuthenticationAgent'
15 EXC_NAME = "org.example.foo.dbus.service.PolKit.NotPrivilegedException"
16 def auth_proxy_wrapper (*args, **kwds):
17 try:
18 return func (*args, **kwds)
19 except dbus.DBusException, e:
20 exc_name = e.get_dbus_name ()
21 if exc_name.startswith (EXC_NAME + "."):
22 session_bus = dbus.SessionBus ()
23 auth_obj = session_bus.get_object (DBUSNAME, DBUSPATH)
24 auth_interface = dbus.Interface(auth_obj,DBUSINTERFACE)
25 action_id = exc_name[len (EXC_NAME)+1:]
26 granted = auth_interface.ObtainAuthorization (action_id, dbus.UInt32 (0),dbus.UInt32 (os.getpid ()))
27 if not granted:
28 raise
29 else:
30 raise
31
32 return func(*args, **kwds)
33 return auth_proxy_wrapper
34
35 class DbusTestProxy:
36 SERVICE_NAME = 'org.example.foo'
37 SERVICE_PATH = '/org/example/foo'
38 INTERFACE_NAME = 'org.example.foo'
39 def __init__(self):
40 self.bus = dbus.SystemBus()
41 self.o = self.bus.get_object(self.SERVICE_NAME,self.SERVICE_PATH)
42 self.i = dbus.Interface(self.o,self.INTERFACE_NAME)
43
44 @auth_proxy
45 def WriteFileWithAuth(self,filePath,contents):
46 return self.i.WriteFile(filePath,contents)
47
48 def WriteFileWithoutAuth(self,filePath,contents):
49 return self.i.WriteFile(filePath,contents)
50
51 @auth_proxy
52 def RunCmd(self,cmdStr):
53 return self.i.RunCmd(cmdStr)
54
55 @auth_proxy
56 def Exit(self):
57 return self.i.Exit()
58
59 #do not need to auth
60 def hello(self):
61 return self.i.hello()
62
63
64 if __name__ == "__main__":
65 p = DbusTestProxy()
66 #print p.RunCmd('ls -al')
67 print p.WriteFileWithAuth('/text','test\n')
68 #print p.WriteFileWithoutAuth('/text','test\n')
69 #p.Exit()
70 print p.hello()

运行上面的程序尝试WriteFileWithAuth 方法会弹出验证的对话框,口令正确的话会在根目录写入文件,调用WriteFileWithoutAuth会因为没有调用权限验证

而返回没有privileged的 异常,因为WriteFile操作是需要权限的。

以上程序相当的简单,因为我也是python新手,相信你也看得明白。

最后打个包,点击下载 policykit_dbus_foo.7z


#install
sudo ./install.sh install #remove
sudo ./install.sh uninstall #test
./policykit_dbus_foo_client.py

以上系统环境为ubuntu 8.04

Reference:

http://hal.freedesktop.org/docs/PolicyKit

Policy, Mechanism and Time zones

http://dbus.freedesktop.org/doc/dbus-specification.html

http://dbus.freedesktop.org/doc/dbus-python/doc/tutorial.html

python Decorator for functions and methods

最后发现了个开源项目 python-slip

其中 slip.dbus.polkit 部分,封装了policykit, 能使你在项目中使用policykit 更容易些.(我觉得python已经够简单的了Orz)

注:转载注明出处

http://www.cnblogs.com/joe2k8/archive/2009/05/24/1488074.html

dbus 和 policykit 实例篇(python) ()转的更多相关文章

  1. 实例解析Python设计模式编程之桥接模式的运用

    实例解析Python设计模式编程之桥接模式的运用 这篇文章主要介绍了Python设计模式编程之桥接模式的运用,桥接模式主张把抽象部分与它的实现部分分离,需要的朋友可以参考下 我们先来看一个例子: #e ...

  2. ASP.NET MVC 4 插件化架构简单实现-实例篇

    先回顾一下上篇决定的做法: 1.定义程序集搜索目录(临时目录). 2.将要使用的各种程序集(插件)复制到该目录. 3.加载临时目录中的程序集. 4.定义模板引擎的搜索路径. 5.在模板引擎的查找页面方 ...

  3. MVC 4 插件化架构简单实现实例篇

    ASP.NET MVC 4 插件化架构简单实现-实例篇   先回顾一下上篇决定的做法: 1.定义程序集搜索目录(临时目录). 2.将要使用的各种程序集(插件)复制到该目录. 3.加载临时目录中的程序集 ...

  4. Asp.Net MVC2.0 Url 路由入门---实例篇

    本篇主要讲述Routing组件的作用,以及举几个实例来学习Asp.Net MVC2.0 Url路由技术. 接着上一篇开始讲,我们在Global.asax中注册一条路由后,我们的请求是怎么转到相应的Vi ...

  5. Flex 布局教程:实例篇【转】

    Flex 布局教程:实例篇   作者: 阮一峰 日期: 2015年7月14日 原文:http://www.ruanyifeng.com/blog/2015/07/flex-examples.html ...

  6. 【0728 | 预习】第三篇 Python基础

    第三篇 Python基础预习 Part 1 变量 一.什么是变量? 二.为什么要有变量? 三.定义变量 四.变量的组成 五.变量名的命名规范 六.变量名的两种风格 Part 2 常量 Part 3 P ...

  7. 第二篇 python进阶

    目录 第二篇 python进阶 一 数字类型内置方法 二 字符串类型内置方法 三 列表类型内置方法(list) 四 元组类型内置方法(tuple) 五 字典内置方法 六 集合类型内置方法(self) ...

  8. 看完100篇Python技术精华文章,平均涨薪30%!

    一个以技术为立身根基的教育机构做出来的微信号,干货程度会有多高? 马哥Linux运维公众号运营五年,从一开始的定位就是给技术人分享加薪干货的地方.这五年里,公众号运营最重的任务就是做内容.内容并不好做 ...

  9. 第五篇python进阶之深浅拷贝

    目录 第五篇python进阶之深浅拷贝 一.引言 1.1可变 和不可变 二.拷贝(只针对可变数据类型) 三.浅拷贝 四.深拷贝 第五篇python进阶之深浅拷贝 一.引言 1.1可变 和不可变 id不 ...

随机推荐

  1. moodle中文API之表单API

    Form API 表单API 文件夹 1.概述 2.亮点 3.使用方法 4.表单元素 4.1 基本表单元素 4.2 定制表单元素 5.经常使用函数 5.1  add_action_buttons($c ...

  2. 有时候碰到String赋值就出错,原因有三

    1. String所在的对象,根本就不存在(不是String不存在,而是它所属的类对象不存在) 2. 增加一个String作为类元素后,运行直接就崩溃.一次又一次找原因,结果发现,只需要完全重编译就行 ...

  3. [IDEs]Eclipse设置花括号样式

    用惯Vistual Studio,在使用Eclipse时发现有很多东西还是挺不习惯,第一个就要解决花括号的样式 步骤: 1.Windows->Preferences->Java->C ...

  4. 破解phpjm.net加密,解密程序,全部公布

    原文:破解phpjm.net加密,解密程序,全部公布 2014-05-23更新: 很久没人找我解密了,看来这加密已过时,现公布我这边最新的解密工具. 若有解不出的可联系qq: 267014855 (不 ...

  5. OCP读书笔记(1) - Oracle核心概念和工具

    ohasdoracle high available service daemon OEMweb -- Database Control资料库 -- sysman Starting Oracle Re ...

  6. JSP网站开发基础总结《九》(转)

    本篇属于附加篇,在之前的总结中给大家提到过一个关于登录状态验证的效果,当时是通过Session对象完成的,今天我查了一下,JSP为我们封装了一个用于过滤用的过滤器类Filter,通过它我们就可以非常轻 ...

  7. MooTools,jQuery库的一些比对

    jQuery与MooTools库的一些比对   今天就我自己的一些认识比对下这两个JS框架,更多的是希望大家能够对MooTools这个JS框架有更多的认识.毕竟,大多数从事web前端的人对上手容易的j ...

  8. linux--文件夹下批量改动IP

    sed -i 's/10.11/10.22/g' `grep ir 10.11 *| grep -E'.xml:|.cfg:|.ini:|.wsdl|.properties:' |awk -F:'{p ...

  9. 淘宝分布式 key/value 存储引擎Tair安装部署过程及Javaclient測试一例

    文件夹 1. 简单介绍 2. 安装步骤及问题小记 3. 部署配置 4. Javaclient測试 5. 參考资料 声明 1. 以下的安装部署基于Linux系统环境:centos 6(64位),其他Li ...

  10. 安卓SDK更新host文件地址

    之前在安装jdk时,安装进度一直很缓慢,在更新这个(两个任选,都有效)host文件地址后,瞬间就可以了. 203.208.46.146 dl.google.com 203.208.46.146 dl- ...