使用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. 基于Linux根据僵尸网络病毒平台《比尔盖茨》

    感觉分析的非常好,所以决定翻译出来,希望和大家多多交流O(∩_∩)O~ 转载请注明出处:http://blog.csdn.net/u010484477     O(∩_∩)O谢谢 keyword:病毒 ...

  2. oracle 在操作blob该字段是否会产生很多redo

    操作blob该字段是否会产生很多redo,答案是否定的.以下来做一个实验,測试数据库版本号是11.2.0.1.0: --创建一张表做測试之用 create table test_blob (   id ...

  3. Kendo UI开发教程(14): Kendo MVVM 数据绑定(三) Click

    Click绑定可以把由ViewModel定义的方法不绑定到目标DOM的click事件.当点击目标DOM元素时触发ViewModel的对应方法.例如: 使用Click绑定 1 <div id=&q ...

  4. LVS的调度算法分析

    LVS调度算法 一.静态调度算法 1.  rr(round robin)轮询调度,即调度器将客户端的请求依次的传递给内部的服务器,从1到N,算法简洁,无须记录状态,但是不考虑每台服务器的性能. 配置如 ...

  5. C++ Primer中文版(第5版)

    <C++ Primer中文版(第5版)> 基本信息 作者: (美)Stanley B. Lippman(斯坦利 李普曼)    Josee Lajoie(约瑟 拉乔伊)    Barbar ...

  6. atitit.团队建设--要不要招技术储备人才的问题

    atitit.团队建设--要不要招技术储备人才的问题 1.      人才的储备和招聘. 1 1.1.   模式1.     养兵千日,用兵一时 1 1.2. 模式2,暂时抱佛脚,也不多招一个人 1 ...

  7. hdu 4529 Double Dealing (置换群)

    # include <stdio.h> # include <algorithm> # include <string.h> using namespace std ...

  8. 腾讯视频QLV格式转换mp4的方法

    腾讯视频QLV格式转换mp4的方法不知道大家知不知道用?喜欢用腾讯视频的朋友应该都知道腾讯视频单独搞出了个QLV格式文件,只能用腾讯独有的腾讯视频软件才能播放,就算用格式工厂转换也不行,那么腾讯视频的 ...

  9. cocos2dx手写js绑定C++

    这两天连续查阅了js绑定c++的非常多文章  , 有手动与自己主动两种方式 . 本来想用自己主动绑定的 , 可是NDK一直下载不下来.....就给算了 . 以下总结一下手动绑定的实现过程 : 一共三步 ...

  10. 编程算法 - 字典分词 代码(C)

    字典分词 代码(C) 本文地址: http://blog.csdn.net/caroline_wendy 给定字典, 给定一句话, 进行分词. 使用深度遍历(DFS)的方法. 使用一个參数string ...