File Coverage

blib/lib/Protocol/DBus/Authn/Mechanism/EXTERNAL.pm
Criterion Covered Total %
statement 21 37 56.7
branch 2 6 33.3
condition 2 9 22.2
subroutine 6 7 85.7
pod 0 3 0.0
total 31 62 50.0


line stmt bran cond sub pod time code
1             package Protocol::DBus::Authn::Mechanism::EXTERNAL;
2              
3 4     4   2088 use strict;
  4         12  
  4         112  
4 4     4   20 use warnings;
  4         8  
  4         108  
5              
6 4     4   16 use parent 'Protocol::DBus::Authn::Mechanism';
  4         8  
  4         20  
7              
8 4     4   208 use Protocol::DBus::Socket ();
  4         8  
  4         1356  
9              
10             # The methods of user credential retrieval that the reference D-Bus
11             # server relies on prefer “out-of-band” methods like SO_PEERCRED
12             # on Linux rather than SCM_CREDS. (See See dbus/dbus-sysdeps-unix.c.)
13             # So for some OSes it’s just not necessary to do anything special to
14             # send credentials.
15             #
16             # On other OSes it doesn’t seem possible to send credentials via a
17             # UNIX socket in the first place. But let’s still try.
18             #
19             our @OS_NO_MSGHDR_LIST = (
20              
21             # Reference server doesn’t need our help:
22             'linux',
23             'netbsd', # via LOCAL_PEEREID, which dbus calls
24              
25             # LOCAL_PEERCRED exists and works, but the reference server
26             # doesn’t appear to use it. Nonetheless, EXTERNAL authn works
27             # on these OSes. Maybe getpeereid() calls LOCAL_PEERCRED?
28             'freebsd', # NB: doesn’t work w/ socketpair()
29             'gnukfreebsd', # ditto, probably
30             'darwin',
31              
32             # 'openbsd', ??? Still trying to test.
33              
34             # No way to pass credentials via UNIX socket,
35             # so let’s just send EXTERNAL and see what happens.
36             # It’ll likely just fail over to DBUS_COOKIE_SHA1.
37             'cygwin',
38             'mswin32',
39             );
40              
41 1     1 0 78 sub INITIAL_RESPONSE { unpack 'H*', $> }
42              
43             # The reference server implementation does a number of things to try to
44             # fetch the peer credentials. .
45             sub must_send_initial {
46 6     6 0 26 my ($self) = @_;
47              
48 6 100       39 if (!defined $self->{'_must_send_initial'}) {
49              
50 5         16 my $can_skip_msghdr = grep { $_ eq $^O } @OS_NO_MSGHDR_LIST;
  35         89  
51              
52 5   33     18 $can_skip_msghdr ||= eval { my $v = Socket::SO_PEERCRED(); 1 };
  0         0  
  0         0  
53 5   33     19 $can_skip_msghdr ||= eval { my $v = Socket::LOCAL_PEEREID(); 1 };
  0         0  
  0         0  
54              
55             # As of this writing it seems FreeBSD and DragonflyBSD do require
56             # Socket::MsgHdr, even though they both have LOCAL_PEERCRED which
57             # should take care of that.
58 5         22 $self->{'_must_send_initial'} = !$can_skip_msghdr;
59             }
60              
61 6         66 return $self->{'_must_send_initial'};
62             }
63              
64             sub send_initial {
65 0     0 0   my ($self, $s) = @_;
66              
67 0 0         eval { require Socket::MsgHdr; 1 } or do {
  0            
  0            
68 0           die "Socket::MsgHdr appears to be needed for EXTERNAL authn (OS=$^O) but failed to load: $@";
69             };
70              
71 0           my $msg = Socket::MsgHdr->new( buf => "\0" );
72              
73             # The kernel should fill in the payload.
74 0           $msg->cmsghdr( Socket::SOL_SOCKET(), Socket::SCM_CREDS(), "\0" x 64 );
75              
76 0           local $!;
77 0           my $ok = Protocol::DBus::Socket::sendmsg_nosignal($s, $msg, 0);
78              
79 0 0 0       if (!$ok && !$!{'EAGAIN'}) {
80 0           die "sendmsg($s): $!";
81             }
82              
83 0           return $ok;
84             }
85              
86             1;