Bug 420921 - execute_kwallet() accidentally closes syslog socket, which results in failure to listen() on its own socket, breaking kwalletd
Summary: execute_kwallet() accidentally closes syslog socket, which results in failure...
Status: REPORTED
Alias: None
Product: kwallet-pam
Classification: Frameworks and Libraries
Component: general (show other bugs)
Version: unspecified
Platform: Debian unstable Linux
: NOR major
Target Milestone: ---
Assignee: Plasma Bugs List
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2020-05-02 20:34 UTC by András Korn
Modified: 2020-05-02 20:34 UTC (History)
0 users

See Also:
Latest Commit:
Version Fixed In:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description András Korn 2020-05-02 20:34:50 UTC
SUMMARY

execute_kwallet() closes many of its inherited file descriptors, which includes the one used for syslog() previously (I assume by pam_syslog()). However, somewhere there is still global state that remembers that FD number.

The function goes on to create envSocket of type SOCK_STREAM, then calls syslog() to log a debug message, and eventually tries to listen on envSocket.

The problem is that envSocket ends up having the same FD as the one saved for syslog in global state; so in the call to syslog(), libc(?) closes the FD and reopens it for logging as SOCK_DGRAM. listen() won't work on a datagram socket, so that the listen() call fails and kwalletd does not run.

Severity set to major because this makes pam_kwallet unusable. There are a number of reports scattered in various forums about this, but I didn't find an actual bug report.

strace:

socket(AF_UNIX, SOCK_STREAM, 0)         = 3 // this is the 'envSocket = socket(AF_UNIX, SOCK_STREAM, 0)' call
unlink("/run/user/xxxx/kwallet5.socket") = -1 ENOENT (No such file or directory)
getpid()                                = xxxx
sendto(3, "<87>[some date] lightdm[xxxx]: pam_kwallet5: final socket path: /run/user/xxxx/kwallet5.socket", 98, MSG_NOSIGNAL, NULL, 0) = -1 ENOTCONN (Transport endpoint is not connected) // this is the syslog() call
close(3)                 = 0 // this is syslog() being smart about apparently having lost its connection to /dev/log
socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 3
connect(3, {sa_family=AF_UNIX, sun_path="/dev/log"}, 110) = 0
sendto(3, "<87>[some date] lightdm[xxxx]: pam_kwallet5: final socket path: /run/user/1011/kwallet5.socket", 98, MSG_NOSIGNAL, NULL, 0) = 98        // this is where syslog() actually logs the message, but FD3 is already SOCK_DGRAM
bind(3, {sa_family=AF_UNIX, sun_path="/run/user/xxxx/kwallet5.socket"}, 32) = 0    // we're back from syslog() and in execute_kwallet() but at this point, FD3 is already a SOCK_DGRAM
listen(3, 5)      = -1 EOPNOTSUPP (Operation not supported)                        // so listen() fails

STEPS TO REPRODUCE

Try to start kwalletd from pam.

OBSERVED RESULT

kwalletd doesn't start and instead logs "pam_kwallet5-kwalletd: Couldn't listen in socket"

EXPECTED RESULT

kwalletd starts and listens on its Unix domain socket.

SOFTWARE/OS VERSIONS
Windows: 
macOS: 
Linux/KDE Plasma: all
(available in About System)
KDE Plasma Version: reproduced with 5.17.5, but the code on github seems to be the same
KDE Frameworks Version: 
Qt Version: irrelevant

ADDITIONAL INFORMATION

Calling closelog() explicitly might be a fix.

Also, how do you know there are no inherited FDs above 63 you need to close? Maybe you know, but in that case a comment that points this out would be helpful. Currently the loop boundary at the beginning of the function looks like either magic or a guess.