File Coverage

blib/lib/Protocol/DBus/Authn/Mechanism/EXTERNAL.pm
Criterion Covered Total %
statement 22 36 61.1
branch 2 6 33.3
condition 2 9 22.2
subroutine 6 7 85.7
pod 0 3 0.0
total 32 61 52.4


line stmt bran cond sub pod time code
1             package Protocol::DBus::Authn::Mechanism::EXTERNAL;
2              
3 1     1   2193 use strict;
  1         10  
  1         70  
4 1     1   8 use warnings;
  1         3  
  1         125  
5              
6 1     1   7 use parent 'Protocol::DBus::Authn::Mechanism';
  1         10  
  1         42  
7              
8 1     1   83 use Protocol::DBus::Socket ();
  1         4  
  1         515  
9              
10 1     1 0 50 sub INITIAL_RESPONSE { unpack 'H*', $> }
11              
12             # The reference server implementation does a number of things to try to
13             # fetch the peer credentials. See dbus/dbus-sysdeps-unix.c.
14             sub must_send_initial {
15 2     2 0 6 my ($self) = @_;
16              
17 2 100       9 if (!defined $self->{'_must_send_initial'}) {
18              
19             # On Linux this module doesn’t need to make any special
20             # effort to send credentials because the server will request them on
21             # its own, and SO_PASSCRED works independently of the client anyway.
22             # (Although Linux only sends the real credentials, we’ll send the
23             # EUID in the AUTH line.)
24             #
25             # As it happens, though, the reference implementation uses
26             # SO_PEERCRED on Linux anyway, as well as OpenBSD. It also
27             # tries LOCAL_PEEREID for NetBSD, getpeerucred() for Solaris,
28             # and getpeereid() as a fallback. Only FreeBSD & DragonflyBSD
29             # appear to be expected to send SCM_CREDS directly, even though
30             # those OSes do have LOCAL_PEERCRED which should work.
31             #
32 1         2 my $can_skip_msghdr = eval { my $v = Socket::SO_PEERCRED(); 1 };
  1         2  
  1         2  
33 1   33     5 $can_skip_msghdr ||= eval { my $v = Socket::LOCAL_PEEREID(); 1 };
  0         0  
  0         0  
34              
35             # macOS can’t send SCM_CREDS, despite that the constants
36             # are defined.
37 1   33     3 $can_skip_msghdr ||= ($^O eq 'darwin');
38              
39 1         3 $self->{'_must_send_initial'} = !$can_skip_msghdr;
40             }
41              
42 2         13 return $self->{'_must_send_initial'};
43             }
44              
45             sub send_initial {
46 0     0 0   my ($self, $s) = @_;
47              
48 0 0         eval { require Socket::MsgHdr; 1 } or do {
  0            
  0            
49 0           die "Socket::MsgHdr appears to be needed for EXTERNAL authn but failed to load: $@";
50             };
51              
52 0           my $msg = Socket::MsgHdr->new( buf => "\0" );
53              
54             # The kernel should fill in the payload.
55 0           $msg->cmsghdr( Socket::SOL_SOCKET(), Socket::SCM_CREDS(), "\0" x 64 );
56              
57 0           local $!;
58 0           my $ok = Protocol::DBus::Socket::sendmsg_nosignal($s, $msg, 0);
59              
60 0 0 0       if (!$ok && !$!{'EAGAIN'}) {
61 0           die "sendmsg($s): $!";
62             }
63              
64 0           return $ok;
65             }
66              
67             1;