Merge lp:~zhangew401/usensord/fix-lp-1433590 into lp:usensord
- fix-lp-1433590
- Merge into trunk
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 |
Related bugs: |
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 |
Commit message
Description of the change
Fix lp:1433590
1. Export OtherVibrate property from com.canonical.
2. The property is saved in file, $HOME/.
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().
Zhang Enwei (zhangew401) wrote : | # |
Thanks Tyler.
can /proc/PID/cmdline be trusted? or do you have any other suggestion to identify the process name?
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 GetConnectionCr
https:/
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.
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.
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.
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/
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(
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.
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
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 GetConnectionCr
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! :)
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.
Tyler Hicks (tyhicks) wrote : | # |
Is maliit-server confined by an AppArmor profile on your device? It isn't on my emulator instance:
root@ubuntu-
current build number: 27
device name: generic_x86
channel: ubuntu-
last update: 2016-07-08 05:47:59
version version: 27
version ubuntu: 20160708
version device: 20160401.1
version custom: 20160708
root@ubuntu-
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.
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
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(
This mode looks funny; why not 0700 or 0750 or 0755? What are the goals?
Thanks
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
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?
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.
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-
Thanks
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-
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.
Seth Arnold (seth-arnold) wrote : | # |
The changes look good to me, thanks!
Zhang Enwei (zhangew401) wrote : | # |
Thanks Seth.
I will update usensord if Mike can do the better solution from OSK.
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.
John McAleely (john.mcaleely) wrote : | # |
I'm not happy to approve it until tyhicks is +1
Zhang Enwei (zhangew401) wrote : | # |
Hi Tyler,
Could you please help review again? Thanks a lot.
Tyler Hicks (tyhicks) wrote : | # |
Looks good. Thanks for iterating on this!
- 33. By Zhang Enwei
-
Support architecture amd64 and arm64
Zhang Enwei (zhangew401) wrote : | # |
Thanks Tyler.
@John, could you please help review?
Thanks.
- 34. By Zhang Enwei
-
support more arch for ci-train build
Pat McGowan (pat-mcgowan) wrote : | # |
I proposed https:/
The default should be to have this enabled since it is on for everyone now, can you make that change?
Pat McGowan (pat-mcgowan) wrote : | # |
The default should be to have this enabled since it is on for everyone now
- 35. By Zhang Enwei
-
Change default value to be 1
Zhang Enwei (zhangew401) wrote : | # |
Hi Pat,
Done and silo17 verified.
Pat McGowan (pat-mcgowan) : | # |
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
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) |
/proc/PID/comm cannot be trusted for security related decisions. See inline comment.