wdoekes / asterisk-chan-dongle

chan_dongle channel driver for Huawei UMTS cards, works with Asterisk 14+
Other
299 stars 104 forks source link

chan_dongle cannot lock devices #63

Open frispete opened 5 years ago

frispete commented 5 years ago

Hi,

being a happy chan_dongle camper since a few weeks, I'm facing a strange issue with locking. Here's a typical chan_dongle setup log from asterisk:

[2018-12-11 18:40:11] VERBOSE[2713] chan_dongle.c: [dongle0] Trying to connect on /dev/ttyUSB2...
[2018-12-11 18:40:11] ERROR[2713] chan_dongle.c: open('/var/lock/LCK..ttyUSB2') failed: Permission denied
[2018-12-11 18:40:11] ERROR[2713] chan_dongle.c: open('/var/lock/LCK..ttyUSB1') failed: Permission denied
[2018-12-11 18:40:11] VERBOSE[2713] chan_dongle.c: [dongle0] Dongle has connected, initializing...
[2018-12-11 18:40:11] VERBOSE[24623] at_response.c: [dongle0] Dongle initialized and ready

In current linux distributions (using openSUSE 15.0 here), accessing /var/lock requires to be a member of the lock group:

$ id asterisk
uid=489(asterisk) gid=487(asterisk) groups=54(lock),487(asterisk)

and /var/lock looks sane as well:

$ ls -la /var/lock/
total 0
drwxrwxr-x  3 root lock  60 Dec 11 22:45 .
drwxr-xr-x 24 root root 720 Dec  9 14:45 ..
drwxr-xr-x  2 root root  40 Dec  7 17:36 subsys

When chan_dongle runs this code, strace reveals, what happens:

openat(AT_FDCWD, "/var/lock/LCK..ttyUSB1", O_WRONLY|O_CREAT|O_TRUNC, 0444) = 
-1 EACCES (Permission denied)

Now the fun thing, simulating this call with sudo and a python script succeeds nevertheless:

$ sudo -u asterisk python3 /tmp/lckopen.py
$ ls -la /var/lock/
total 0
drwxrwxr-x  3 root     lock      80 Dec 12 12:50 .
drwxr-xr-x 24 root     root     720 Dec  9 14:45 ..
-r--r--r--  1 asterisk asterisk   0 Dec 12 12:50 LCK..ttyUSB1
drwxr-xr-x  2 root     root      40 Dec  7 17:36 subsys

$ cat /tmp/lckopen.py

import os
try:
    os.open('/var/lock/LCK..ttyUSB1', os.O_WRONLY|os.O_CREAT|os.O_TRUNC, 0o444)
except IOError as e:
    print('failed: %s' % e)

strace excerpt:

openat(AT_FDCWD, "/var/lock/LCK..ttyUSB1", O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 
0444) = 3

I patched chan_dongle to add the O_CLOEXEC flag, which python3 seems to add behind the scenes, but no bonus. Added code to check for uid and euid on both parties reveals the expected results: the systems asterisk uid is effectively in use, so there's no reason to fail.

I've checked any policy related system tools, but they seem innocent as well. My system uses apparmor, I've checked that throughout, but it doesn't know anything about asterisk, hence it will not enforce any kind of special protection. SELinux isn't in use.

I've raised this issue already on asterisk-dev, but Joshua, a core developer attested, that Asterisk doesn't do anything special, that could result in this. Does chan_dongle something special permission-wise, that I may have missed?

All my Asterisk related package builds are available in public here.

In short, I'm using 16.0.1 still, and current chan_dongle from this project.

Obviously, this is system related, it would have raised more attention here.

Anyway, any insights are much appreciated.

wdoekes commented 5 years ago

Does chan_dongle something special permission-wise, that I may have missed?

I don't think so. (A quick grep for uid/perms-functions shows nothing, and I didn't expect any either.)

I even think you listed all the usual suspects.

Isn't the problem that openat() is called twice?

walter@walter-desktop:0:~/Junk$ python3 lckopen.py 
walter@walter-desktop:0:~/Junk$ python3 lckopen.py 
failed: [Errno 13] Permission denied: '/var/lock/LCK..ttyUSB1'

Please double check the strace?

frispete commented 5 years ago

Thanks, @wdoekes for your quick response.

Isn't the problem that openat() is called twice?

No, here's the filtered strace:

12903 14:32:19.779226 unlink("/var/lock/LCK..ttyUSB1") = -1 ENOENT (No such file or directory)
12903 14:32:19.787335 unlink("/var/lock/LCK..ttyUSB2") = -1 ENOENT (No such file or directory)
12875 14:32:19.789603 openat(AT_FDCWD, "/var/lock/LCK..ttyUSB2", O_RDONLY <unfinished ...>
12875 14:32:19.789685 <... openat resumed> ) = -1 ENOENT (No such file or directory)
12875 14:32:19.789773 unlink("/var/lock/LCK..ttyUSB2" <unfinished ...>
12875 14:32:19.789850 <... unlink resumed> ) = -1 ENOENT (No such file or directory)
12875 14:32:19.790321 openat(AT_FDCWD, "/var/lock/LCK..ttyUSB2", O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0444 <unfinished ...>
12875 14:32:19.790429 <... openat resumed> ) = -1 EACCES (Permission denied)
12875 14:32:19.790699 openat(AT_FDCWD, "/dev/ttyUSB2", O_RDWR|O_NOCTTY <unfinished ...>
12875 14:32:19.791132 <... openat resumed> ) = 19
12875 14:32:19.792464 openat(AT_FDCWD, "/var/lock/LCK..ttyUSB1", O_RDONLY <unfinished ...>
12875 14:32:19.792553 <... openat resumed> ) = -1 ENOENT (No such file or directory)
12875 14:32:19.792631 unlink("/var/lock/LCK..ttyUSB1" <unfinished ...>
12875 14:32:19.792774 <... unlink resumed> ) = -1 ENOENT (No such file or directory)
12875 14:32:19.793262 openat(AT_FDCWD, "/var/lock/LCK..ttyUSB1", O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0444 <unfinished ...>
12875 14:32:19.793342 <... openat resumed> ) = -1 EACCES (Permission denied)
12875 14:32:19.793606 openat(AT_FDCWD, "/dev/ttyUSB1", O_RDWR|O_NOCTTY <unfinished ...>
12875 14:32:19.793964 <... openat resumed> ) = 20

chan_dongle always tries to unlink the lock file twice before attempting to create the lock, and that call fails. Interestingly, the devices can be accessed properly:

$ ls -la /dev/ttyUSB*
crw-rw---- 1 root asterisk 188, 0 Dec 11 18:38 /dev/ttyUSB0
crw-rw---- 1 root asterisk 188, 1 Dec 12 11:57 /dev/ttyUSB1
crw-rw---- 1 root asterisk 188, 2 Dec 12 14:37 /dev/ttyUSB2

It's really puzzling...

wdoekes commented 5 years ago

Did you strace with -f? I don't see any pids/tids.

frispete commented 5 years ago

They're in the first column.

Walter, I got an advise from openSUSEs security officer to use the lockdev library instead. What do you think about this? I haven't looked into the details, yet. (Anything auto* scares me away usually...)

Anyway, would you consider such a PR?

wdoekes commented 5 years ago

They're in the first column.

Sorry, I, missed those. Your strace has different output than mine.

I cannot immediately correlate those unlinks+openats to the calling code, but then again, I didn't look too close.

Have you tried calling lckopen.py from a dialplan System()/SHELL() call?

Anyway, would you consider such a PR?

There's even a ticket for that! https://github.com/wdoekes/asterisk-chan-dongle/issues/6

As long as things still compile without liblockdev, I'll gladly merge.

wdoekes commented 5 years ago

Have you tried calling lckopen.py from a dialplan System()/SHELL() call?

And tried starting asterisk from a shell instead of from systemd (assuming that's what you're using on opensuse)?

h31p commented 5 years ago

btw, uucp lock directory is slightly different on FreeBSD, so it will be good (and enough):

--- a/chan_dongle.c
+++ b/chan_dongle.c
@@ -116,7 +116,11 @@ static int lock_build(const char * devname, char * buf, unsigned length)
                basename = devname;

        /* NOTE: use system system wide lock directory */
+       #if defined(__FreeBSD__)
+       return snprintf(buf, length, "/var/spool/lock/LCK..%s", basename);
+       #else
        return snprintf(buf, length, "/var/lock/LCK..%s", basename);
+       #endif
 }