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   2116 use strict;
  4         8  
  4         112  
4 4     4   20 use warnings;
  4         16  
  4         96  
5              
6 4     4   20 use parent 'Protocol::DBus::Authn::Mechanism';
  4         8  
  4         16  
7              
8 4     4   172 use Protocol::DBus::Socket ();
  4         8  
  4         1308  
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’s just not possible to send credentials via a
17             # UNIX socket in the first place.
18             #
19             our @OS_NO_MSGHDR_LIST = (
20              
21             # Reference server doesn’t need our help:
22             'linux',
23             'netbsd',
24              
25             # 'openbsd', ??? Still trying to test.
26              
27             # No way to pass credentials via UNIX socket anyway:
28             'cygwin',
29             'darwin',
30             );
31              
32 1     1 0 74 sub INITIAL_RESPONSE { unpack 'H*', $> }
33              
34             # The reference server implementation does a number of things to try to
35             # fetch the peer credentials. .
36             sub must_send_initial {
37 6     6 0 35 my ($self) = @_;
38              
39 6 100       49 if (!defined $self->{'_must_send_initial'}) {
40              
41 5         24 my $can_skip_msghdr = grep { $_ eq $^O } @OS_NO_MSGHDR_LIST;
  20         61  
42              
43 5   33     24 $can_skip_msghdr ||= eval { my $v = Socket::SO_PEERCRED(); 1 };
  0         0  
  0         0  
44 5   33     19 $can_skip_msghdr ||= eval { my $v = Socket::LOCAL_PEEREID(); 1 };
  0         0  
  0         0  
45              
46 5         26 $self->{'_must_send_initial'} = !$can_skip_msghdr;
47             }
48              
49 6         33 return $self->{'_must_send_initial'};
50             }
51              
52             sub send_initial {
53 0     0 0   my ($self, $s) = @_;
54              
55 0 0         eval { require Socket::MsgHdr; 1 } or do {
  0            
  0            
56 0           die "Socket::MsgHdr appears to be needed for EXTERNAL authn (OS=$^O) but failed to load: $@";
57             };
58              
59 0           my $msg = Socket::MsgHdr->new( buf => "\0" );
60              
61             # The kernel should fill in the payload.
62 0           $msg->cmsghdr( Socket::SOL_SOCKET(), Socket::SCM_CREDS(), "\0" x 64 );
63              
64 0           local $!;
65 0           my $ok = Protocol::DBus::Socket::sendmsg_nosignal($s, $msg, 0);
66              
67 0 0 0       if (!$ok && !$!{'EAGAIN'}) {
68 0           die "sendmsg($s): $!";
69             }
70              
71 0           return $ok;
72             }
73              
74             1;