Merge lp:~zhangew401/usensord/fix-lp-1433590 into lp:usensord

Proposed by Zhang Enwei
Status: Merged
Approved by: Bill Filler
Approved revision: 35
Merged at revision: 26
Proposed branch: lp:~zhangew401/usensord/fix-lp-1433590
Merge into: lp:usensord
Diff against target: 260 lines (+172/-2)
3 files modified
debian/changelog (+12/-0)
debian/control (+2/-0)
haptic/haptic.go (+158/-2)
To merge this branch: bzr merge lp:~zhangew401/usensord/fix-lp-1433590
Reviewer Review Type Date Requested Status
Pat McGowan (community) Approve
Tyler Hicks Approve
Seth Arnold (community) Approve
Y.C Cheng Pending
John McAleely Pending
PS Jenkins bot continuous-integration Pending
Penk Chen Pending
Michael Sheldon Pending
James Henstridge Pending
Zsombor Egri Pending
Michael Frey Pending
Review via email: mp+299959@code.launchpad.net

Description of the change

Fix lp:1433590
1. Export OtherVibrate property from com.canonical.usensord.haptic so that system settings can enable/disable it.
2. The property is saved in file, $HOME/.config/usensord/prop.json so that it will be kept after reboot and restored after factory reset.
3. Identify OSK by its apparmor and /proc/ID/exe. if it is OSK, do vibration always. If not, check the OtherVibrate property, if it is 1, then do vibration. If it is 0, don't do vibration.
4. libapparmor is linked to call aa_is_enabled().

To post a comment you must log in.
Revision history for this message
Tyler Hicks (tyhicks) wrote :

/proc/PID/comm cannot be trusted for security related decisions. See inline comment.

review: Disapprove
Revision history for this message
Zhang Enwei (zhangew401) wrote :

Thanks Tyler.
can /proc/PID/cmdline be trusted? or do you have any other suggestion to identify the process name?

Revision history for this message
Tyler Hicks (tyhicks) wrote :

No, you can't trust /proc/PID/cmdline, either.

You can ask dbus for the AppArmor profile that is confining the connecting process. All click apps are confined by AppArmor and, therefore, cannot change to other profiles or escape confinement. All processes that are unconfined can be treated as "trusted" processes since they're services that are shipped as part of the image we produce.

To ask dbus for a peer's AppArmor confinement context, call GetConnectionCredentials and then use the LinuxSecurityLabel in the returned dict. See this for more info:

  https://dbus.freedesktop.org/doc/dbus-specification.html#bus-messages-get-connection-credentials

Then you'll need to use libapparmor's aa_splitcon() function to break the confinement context into a label and a mode. See the aa_splitcon(3) man page for details.

You can use the label to identify the dbus peer. It will be "unconfined" if it is a trusted process. I believe that will be the case for maliit-server. You can verify by running `ps auxZ | grep maliit-server` and looking at the first column to see its AppArmor confinement context.

Revision history for this message
Zhang Enwei (zhangew401) wrote :

Thanks a lot for your detailed explanation, Tyler
Please let me explain our requirement in the bug.
1. If the caller is OSK, we will do vibration always.
2. If the caller is app, including click package and others like system-settings who have used Toolkit in their implementation, we will do vibration based the value of exposed property OtherVibrate.

I don't know if system settings have unconfined profile. But from the info I got from Zosmbi, the user of Toolkit includes unconfined apps.

Thanks again.

Revision history for this message
Zhang Enwei (zhangew401) wrote :

Hi again Tyler,
For the bug, since OSK has unconfined AppArmor, so we can trusted its comm, is this correct?
If so, the logic is if it is unconfined, and then if its comm is maliit-server, then we are sure it is OSK. right?
Thanks a lot.

Revision history for this message
James Henstridge (jamesh) wrote :

If you want to identify the executable, calling os.Readlink() on /proc/$PID/exe would be more appropriate:

    $ ps x | grep maliit
     5823 ? Ssl 2:38 maliit-server
    25788 pts/16 S+ 0:00 grep --color=auto maliit
    $ ls -l /proc/5823/exe
    lrwxrwxrwx 1 phablet phablet 0 Jul 7 11:47 /proc/5823/exe -> /usr/bin/maliit-server

I'd combine that with the a check that the security label is "unconfined" as Tyler suggested (which you can do using the code fragment I gave via mail). That should be enough to ensure you aren't being faked out by an untrusted application, and are talking to the expected system service.

As for other comments on the branch:

Hard coding /home/phablet looks wrong. If you want to determine the user's home directory, the os/user package should help. Something like:

    u, err := user.Current()
    configFile := path.Join(u.HomeDir, ".config", "usensord", "whatever")

Also, storing a plain integer in the file could limit what you can do going forward if you need to add more configuration settings. Perhaps consider using something a bit more structured that will let you add more settings later without breaking forward or backward compatibility. Go's json package might be a good fit here.

Revision history for this message
Zhang Enwei (zhangew401) wrote :

Thanks so much James.
@Tyler, if you also agree with the proposition by James, I will begin to do it.
Thanks.

28. By Zhang Enwei

Change according to James's comments

29. By Zhang Enwei

finetune the logic

Revision history for this message
Tyler Hicks (tyhicks) wrote :

I don't love the approach of reading /proc/PID/exe and I have some real blockers that I mention inline below.

Since your entire goal is to identify maliit-server, I'd much prefer that you confine maliit-server with an AppArmor profile (it could even be loosely confined at first). You'd then call GetConnectionCredentials() and use the LinuxSecurityLabel to identify maliit-server by its AppArmor profile name. This approach has the benefit that we confine another process on the system (more hardening) and don't have to rely on oddities in the behavior of /proc/PID/exe (see proc(5) man page for an example seen in child threads).

Confining maliit-server could be easy or it could become more difficult if it requires changes to other AppArmor profiles. I can't really say at this point but I'd like for this approach to at least be considered. If it is too complex then you should at least address the issues I mention inline below.

Thanks for working through this merge feedback! :)

review: Needs Fixing
Revision history for this message
Zhang Enwei (zhangew401) wrote :

Thank you so much for suggestions, Tyler.
Sorry I don't quite understand "I'd much prefer that you confine maliit-server with an AppArmor profile", do you mean we need to change the AppArmor profile of maliit-server?

I will address your mentioned issues at least at the time.
Thank you.

Revision history for this message
Tyler Hicks (tyhicks) wrote :

Is maliit-server confined by an AppArmor profile on your device? It isn't on my emulator instance:

root@ubuntu-phablet:~# system-image-cli -i
current build number: 27
device name: generic_x86
channel: ubuntu-touch/rc/ubuntu
last update: 2016-07-08 05:47:59
version version: 27
version ubuntu: 20160708
version device: 20160401.1
version custom: 20160708
root@ubuntu-phablet:~# ps auxZ | grep maliit
unconfined phablet 2120 0.2 8.3 278072 42392 ? Ssl 19:47 0:00 maliit-server

(the first column is the AppArmor confinement context)

What I'm suggesting is that you confine maliit-server with an AppArmor profile and then you can securely identify it.

Revision history for this message
Zhang Enwei (zhangew401) wrote :

It is unconfined on the device.
unconfined phablet 3067 8.5 4.1 281312 40820 ? Ssl 14:18 0:01 maliit-server
Let me ask OSK team if they can confine maliit-server with and AppArmor profile.

30. By Zhang Enwei

Add aa_is_enabled judgement, finetune some logic

Revision history for this message
Seth Arnold (seth-arnold) wrote :

Thanks Zhang for working with us on this; this looks good, with a few small points:

"unkown", "faild"

Is this line correct?
if _, err := os.Lstat(file); os.IsNotExist(err) {

There are many possible reasons why the os.Lstat() might fail, it would probably be better to just log the actual error.

The error information from erreadexe isn't printed.

os.MkdirAll(configPath, 0776)

This mode looks funny; why not 0700 or 0750 or 0755? What are the goals?

Thanks

Revision history for this message
Zhang Enwei (zhangew401) wrote :

Thanks Seth.
0776 is really my careless fault. I will update accordingly soon.

31. By Zhang Enwei

fix according to Seth's comments

32. By Zhang Enwei

add dependencies and update changelog

Revision history for this message
Michael Sheldon (michael-sheldon) wrote :

Hi Zhang, Tyler

 When you say that you'd like the maliit-server confined with an apparmor profile, do you purely need this for identification at the moment? i.e. could we just add a profile that uses the unconfined template? Or do you actually need more strict confinement of maliit-server?

Revision history for this message
Zhang Enwei (zhangew401) wrote :

Hi Mike,
IMHO, we only need this for identification. i.e. to use the unconfined template with a name, then I can use that name for identification in usensord.
@Seth, could you please help check if my understanding is correct? Thanks a lot.

Revision history for this message
Seth Arnold (seth-arnold) wrote :

Correct, using an AppArmor profile for identification and not confinement is fine at this point. (Though I think meaningful improvements of security via a better-than-unconfined profile can still be made with just half an hour or so of profiling work.)

Thanks

Revision history for this message
Zhang Enwei (zhangew401) wrote :

Thanks a lot Seth.
@Mike, I think you need to decide if we only use one named unconfined profile or a better-than-unconfined profile.
Anyway I believe the name must be unique. Thanks so much. And also please send me the binary and tell me the name then I could test usensord. Thank you.

Revision history for this message
Seth Arnold (seth-arnold) wrote :

The changes look good to me, thanks!

review: Approve
Revision history for this message
Zhang Enwei (zhangew401) wrote :

Thanks Seth.
I will update usensord if Mike can do the better solution from OSK.

Revision history for this message
Zhang Enwei (zhangew401) wrote :

Hi John,
Could you please help review this patch to fix lp:1433590?
I need your top approval on it.
Thanks.

Revision history for this message
John McAleely (john.mcaleely) wrote :

I'm not happy to approve it until tyhicks is +1

Revision history for this message
Zhang Enwei (zhangew401) wrote :

Hi Tyler,
Could you please help review again? Thanks a lot.

Revision history for this message
Tyler Hicks (tyhicks) wrote :

Looks good. Thanks for iterating on this!

review: Approve
33. By Zhang Enwei

Support architecture amd64 and arm64

Revision history for this message
Zhang Enwei (zhangew401) wrote :

Thanks Tyler.
@John, could you please help review?
Thanks.

34. By Zhang Enwei

support more arch for ci-train build

Revision history for this message
Pat McGowan (pat-mcgowan) wrote :

I proposed https://code.launchpad.net/~pat-mcgowan/ubuntu-system-settings/other-vibrations/+merge/303566 with the system settings support.

The default should be to have this enabled since it is on for everyone now, can you make that change?

Revision history for this message
Pat McGowan (pat-mcgowan) wrote :

The default should be to have this enabled since it is on for everyone now

review: Needs Fixing
35. By Zhang Enwei

Change default value to be 1

Revision history for this message
Zhang Enwei (zhangew401) wrote :

Hi Pat,
Done and silo17 verified.

Revision history for this message
Pat McGowan (pat-mcgowan) :
review: Approve
Revision history for this message
Zhang Enwei (zhangew401) wrote :

Thanks Bill for top approval.
@Bill and Pat, sorry I am not familiar with the process.
After this branch is approved, do I need to manually merge it back to lp:usensord?
Thanks a lot.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/changelog'
2--- debian/changelog 2015-04-22 23:37:15 +0000
3+++ debian/changelog 2016-08-23 01:30:37 +0000
4@@ -1,3 +1,15 @@
5+usensord (1.1+15.04.20160720-0ubuntu2) vivid; urgency=medium
6+
7+ * Fix lp:1433590.
8+ Expose OtherVibrate property for system settings to change(Enable/Disable).
9+ The value of the property is saved in file(.config/usensord/prop.json) in
10+ order to be kept after reboot and restored after factory reset.
11+ Identify if the peer is OSK and vibrate for it always. If not, check
12+ the OtherVibrate property, if it is 1, then do vibration. If it is 0,
13+ don't do vibration.
14+
15+ -- enwei <enwei.zhang@canonical.com> Wed, 20 Jul 2016 12:20:59 +0800
16+
17 usensord (1.1+15.04.20150422.1-0ubuntu1) vivid; urgency=medium
18
19 [ CI Train Bot ]
20
21=== modified file 'debian/control'
22--- debian/control 2014-05-28 08:27:55 +0000
23+++ debian/control 2016-08-23 01:30:37 +0000
24@@ -6,6 +6,7 @@
25 dh-golang,
26 golang-go,
27 golang-go-dbus-dev,
28+ libapparmor-dev,
29 Standards-Version: 3.9.5
30 Homepage: http://launchpad.net/usensord
31 Vcs-Bzr: lp:usensord
32@@ -15,6 +16,7 @@
33 Architecture: any
34 Depends: ${misc:Depends},
35 ${shlibs:Depends},
36+ libapparmor1,
37 Built-Using: ${misc:Built-Using}
38 Description: Ubuntu Touch Sensor Service
39 Daemon that provides standard access to sensors for Ubuntu Touch
40
41=== modified file 'haptic/haptic.go'
42--- haptic/haptic.go 2015-04-22 23:06:23 +0000
43+++ haptic/haptic.go 2016-08-23 01:30:37 +0000
44@@ -22,14 +22,35 @@
45 package haptic
46
47 import (
48+ "encoding/json"
49 "fmt"
50 "log"
51+ "io/ioutil"
52 "os"
53+ "os/user"
54+ "path"
55+ "strconv"
56+ "strings"
57 "sync"
58 "time"
59
60 "launchpad.net/go-dbus/v1"
61 )
62+// #cgo CFLAGS: -I/usr/include
63+// #cgo linux,ppc LDFLAGS: -L/usr/lib/powerpc-linux-gnu -lapparmor
64+// #cgo linux,ppc64le LDFLAGS: -L/usr/lib/powerpc64le-linux-gnu -lapparmor
65+// #cgo linux,s390x LDFLAGS: -L/usr/lib/s390x-linux-gnu -lapparmor
66+// #cgo linux,386 LDFLAGS: -L/usr/lib/i386-linux-gnu -lapparmor
67+// #cgo linux,amd64 LDFLAGS: -L/usr/lib/x86_64-linux-gnu -lapparmor
68+// #cgo linux,arm LDFLAGS: -L/usr/lib/arm-linux-gnueabihf -lapparmor
69+// #cgo linux,arm64 LDFLAGS: -L/usr/lib/aarch64-linux-gnu -lapparmor
70+//#include <sys/apparmor.h>
71+//#include <errno.h>
72+import "C"
73+
74+type Prop struct {
75+ OtherVibrate uint32
76+}
77
78 var (
79 conn *dbus.Connection
80@@ -40,20 +61,28 @@
81 mutex *sync.Mutex
82 cookie string
83 timer *time.Timer
84+ pvalue uint32
85+ configFile string
86 )
87
88 const (
89 HAPTIC_DBUS_IFACE = "com.canonical.usensord.haptic"
90 HAPTIC_DEVICE = "/sys/class/timed_output/vibrator/enable"
91+ PROP_DBUS_IFACE = "org.freedesktop.DBus.Properties"
92+ OSK_PROCESS_NAME = "/usr/bin/maliit-server"
93+ UNCONFINED_PROFILE = "unconfined"
94 )
95
96 func watchDBusMethodCalls(msgChan <-chan *dbus.Message) {
97 for msg := range msgChan {
98+ logger.Println("msg sender", msg.Sender)
99 var reply *dbus.Message
100
101 if msg.Interface == HAPTIC_DBUS_IFACE {
102 reply = handleHapticInterface(msg)
103- } else {
104+ } else if msg.Interface == PROP_DBUS_IFACE {
105+ reply = handlePropInterface(msg)
106+ } else {
107 reply = dbus.NewErrorMessage(
108 msg,
109 "org.freedesktop.DBus.Error.UnknownInterface",
110@@ -66,7 +95,107 @@
111 }
112 }
113
114+func handlePropInterface(msg *dbus.Message) (reply *dbus.Message) {
115+ switch msg.Member {
116+ case "Get":
117+ var iname, pname string
118+ msg.Args(&iname, &pname)
119+ if iname == HAPTIC_DBUS_IFACE && pname == "OtherVibrate" {
120+ reply = dbus.NewMethodReturnMessage(msg)
121+ reply.AppendArgs(dbus.Variant{uint32(pvalue)})
122+ } else {
123+ reply = dbus.NewErrorMessage(msg, "com.canonical.usensord.Error", "interface or property not correct")
124+ }
125+ case "GetAll":
126+ var iname string
127+ msg.Args(&iname)
128+ if iname == HAPTIC_DBUS_IFACE {
129+ reply = dbus.NewMethodReturnMessage(msg)
130+ reply.AppendArgs(dbus.Variant{uint32(pvalue)})
131+ } else {
132+ reply = dbus.NewErrorMessage(msg, "com.canonical.usensord.Error", "interface or property not correct")
133+ }
134+ case "Set":
135+ var iname, pname string
136+ msg.Args(&iname, &pname, &pvalue)
137+ if iname == HAPTIC_DBUS_IFACE && pname == "OtherVibrate" && (pvalue == 1 || pvalue == 0) {
138+ //save the property value
139+ prop := Prop{OtherVibrate: pvalue,}
140+ propJson, _ := json.Marshal(prop)
141+ errwrite := ioutil.WriteFile(configFile, propJson, 0644)
142+ if errwrite != nil {
143+ logger.Println("WriteFile error:", errwrite)
144+ }
145+
146+ //reply = dbus.NewSignalMessage("/com/canonical/usensord/haptic", HAPTIC_DBUS_IFACE, "Set")
147+ reply = dbus.NewMethodReturnMessage(msg)
148+ reply.AppendArgs(dbus.Variant{uint32(pvalue)})
149+ logger.Println("Set property to be ", pvalue)
150+ } else {
151+ reply = dbus.NewErrorMessage(msg, "com.canonical.usensord.Error", "interface or property not correct")
152+ }
153+ default:
154+ logger.Println("Received unknown method call on", msg.Interface, msg.Member)
155+ reply = dbus.NewErrorMessage(msg, "org.freedesktop.DBus.Error.UnknownMethod", "Unknown method")
156+ }
157+ return reply
158+}
159+
160 func handleHapticInterface(msg *dbus.Message) (reply *dbus.Message) {
161+ messageBus := conn.Object("org.freedesktop.DBus", "/org/freedesktop/DBus")
162+ processreply, err := messageBus.Call("org.freedesktop.DBus", "GetConnectionCredentials", msg.Sender)
163+ if err != nil {
164+ reply = dbus.NewErrorMessage(msg, "com.canonical.usensord.Error", err.Error())
165+ return reply
166+ }
167+ var credentials map[string]dbus.Variant
168+ if err := processreply.Args(&credentials); err != nil {
169+ reply = dbus.NewErrorMessage(msg, "com.canonical.usensord.Error", err.Error())
170+ return reply
171+ }
172+ pid := credentials["ProcessID"].Value.(uint32)
173+ logger.Printf("caller process id: %d", pid)
174+ var profile string
175+ ret, error := C.aa_is_enabled()
176+ if ret == 1 {
177+ logger.Println("aa_is_enabled")
178+ label := credentials["LinuxSecurityLabel"].Value.([]interface{})
179+ var bb []uint8
180+ for _, f := range label {
181+ bb = append(bb, f.(uint8))
182+ }
183+ profile = strings.TrimSpace(string(bb))
184+ //LinuxSecurityLabel ends with null
185+ profile = profile[:len(profile)-1]
186+ logger.Println("caller process label:", profile)
187+ } else {
188+ logger.Println("aa_is_enabled failed:", error)
189+ profile = UNCONFINED_PROFILE
190+ }
191+ isOSK := false
192+ if profile == UNCONFINED_PROFILE {
193+ file := "/proc/" + strconv.FormatUint(uint64(pid), 10) + "/exe"
194+ _, err := os.Lstat(file)
195+ if err != nil {
196+ logger.Println("error while calling os.Lstat", err)
197+ }
198+ exe, erreadexe := os.Readlink(file)
199+ if erreadexe != nil {
200+ logger.Printf("fail to read %s with error:", file, erreadexe.Error())
201+ } else {
202+ pname := strings.TrimSpace(string(exe))
203+ logger.Println("process name:", pname)
204+ if pname == OSK_PROCESS_NAME {
205+ isOSK = true
206+ logger.Println("OSK calling")
207+ }
208+ }
209+ }
210+ if !isOSK && pvalue == 0 {
211+ logger.Println("not vibrate since not osk and pvalue is 0")
212+ reply = dbus.NewMethodReturnMessage(msg)
213+ return reply
214+ }
215 switch msg.Member {
216 case "Vibrate":
217 var duration uint32
218@@ -88,7 +217,7 @@
219 reply = dbus.NewMethodReturnMessage(msg)
220 }
221 default:
222- logger.Println("Received unkown method call on", msg.Interface, msg.Member)
223+ logger.Println("Received unknown method call on", msg.Interface, msg.Member)
224 reply = dbus.NewErrorMessage(msg, "org.freedesktop.DBus.Error.UnknownMethod", "Unknown method")
225 }
226 return reply
227@@ -180,6 +309,33 @@
228
229 powerd = sysbus.Object("com.canonical.powerd", "/com/canonical/powerd")
230 mutex = &sync.Mutex{}
231+ //save and load the property value
232+ u, err := user.Current()
233+ configPath := path.Join(u.HomeDir, ".config", "usensord")
234+ configFile = path.Join(u.HomeDir, ".config", "usensord", "prop.json")
235+ log.Println("configFile:", configFile)
236+ os.MkdirAll(configPath, 0755)
237+ b, errread := ioutil.ReadFile(configFile)
238+ if errread != nil {
239+ pvalue = 1
240+ prop := Prop{OtherVibrate: pvalue,}
241+ propJson, _ := json.Marshal(prop)
242+ errwrite := ioutil.WriteFile(configFile, propJson, 0644)
243+ if errwrite != nil {
244+ logger.Fatal("WriteFile error:", errwrite)
245+ return errwrite
246+ }
247+ } else {
248+ var prop Prop
249+ err := json.Unmarshal(b, &prop)
250+ if err == nil {
251+ pvalue = prop.OtherVibrate
252+ log.Println("pvalueb is", pvalue)
253+ } else {
254+ log.Println("err is", err)
255+ pvalue = 0
256+ }
257+ }
258
259 ch := make(chan *dbus.Message)
260 go watchDBusMethodCalls(ch)

Subscribers

People subscribed via source and target branches