Bug 1535109

Summary: gpg-agent does not work when user is confined (user_u or staff_u)
Product: Red Hat Enterprise Linux 7 Reporter: Jonathan Billings <jsbillin>
Component: selinux-policyAssignee: Lukas Vrabec <lvrabec>
Status: CLOSED ERRATA QA Contact: Milos Malik <mmalik>
Severity: medium Docs Contact:
Priority: medium    
Version: 7.4CC: lvrabec, mmalik, plautrba, rdulhani, rmetrich, ssekidde
Target Milestone: rc   
Target Release: ---   
Hardware: x86_64   
OS: Linux   
Whiteboard:
Fixed In Version: selinux-policy-3.13.1-203.el7 Doc Type: If docs needed, set a value
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2019-08-06 12:51:45 UTC Type: Bug
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:
Bug Depends On:    
Bug Blocks: 1653106    

Description Jonathan Billings 2018-01-16 16:08:06 UTC
Description of problem:
There is a problem with the default SELinux policy for GPG when run as a Confined user.  It prevents the pinentry from collecting a password.  user_u and staff_u are restricted from getattr() on many files in /etc, which I'm ignoring because it doesn't seem to affect the ability of gpg to run, but there are several paths that seem to be blocked by selinux that prevents gpg-agent from operating.

Version-Release number of selected component (if applicable):
selinux-policy-targeted-3.13.1-166.el7_4.7.noarch
gnupg2-2.0.22-4.el7.x86_64

How reproducible:
Always

Steps to Reproduce:
1. Make sure your user is confined (id -Z should not be unconfined_u)
2. eval $(gpg-agent --daemon)
3. gpg --gen-key

Actual results:

-bash-4.2$ id -Z
user_u:user_r:user_t:s0
-bash-4.2$ eval $(gpg-agent --daemon)
-bash-4.2$ gpg --gen-key
gpg (GnuPG) 2.0.22; Copyright (C) 2013 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Please select what kind of key you want:
   (1) RSA and RSA (default)
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
Your selection? 1
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048) 
Requested keysize is 2048 bits
Please specify how long the key should be valid.
         0 = key does not expire
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0) 1y
Key expires at Wed 16 Jan 2019 10:53:06 AM EST
Is this correct? (y/N) y

GnuPG needs to construct a user ID to identify your key.

Real name: test user
Email address: test
Comment: 
You selected this USER-ID:
    "test user <test>"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O
You need a Passphrase to protect your secret key.

gpg: cancelled by user
gpg: Key generation canceled.
-bash-4.2$

Expected results:
-bash-4.2$ id -Z
user_u:user_r:user_t:s0
-bash-4.2$ eval $(gpg-agent --daemon)
-bash-4.2$ gpg --gen-key
gpg (GnuPG) 2.0.22; Copyright (C) 2013 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Please select what kind of key you want:
   (1) RSA and RSA (default)
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
Your selection? 1
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048) 
Requested keysize is 2048 bits
Please specify how long the key should be valid.
         0 = key does not expire
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0) 1y
Key expires at Wed 16 Jan 2019 11:00:31 AM EST
Is this correct? (y/N) y

GnuPG needs to construct a user ID to identify your key.

Real name: test user
Email address: test
Comment: 
You selected this USER-ID:
    "test user <test>"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O
You need a Passphrase to protect your secret key.

[prompting for password from pinentry-curses]

We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
gpg: key B1887542 marked as ultimately trusted
public and secret key created and signed.

gpg: checking the trustdb
gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model
gpg: depth: 0  valid:   2  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 2u
gpg: next trustdb check due at 2019-01-16
pub   2048R/B1887542 2018-01-16 [expires: 2019-01-16]
      Key fingerprint = AE37 D8B6 0C17 4CBE 483A  DAB1 5C4B D5ED B188 7542
uid                  test user <test>
sub   2048R/D4CAEB32 2018-01-16 [expires: 2019-01-16]



Additional info:

I've created a custom policy module that fixes this for our users:

# cat local_gpg.te 

module local_gpg 1.0;

require {
	type staff_t;
	type user_t;
	type gpg_t;
	type gpg_agent_tmp_t;
	type gpg_agent_t;
	type gpg_pinentry_t;
	class process { noatsecure rlimitinh siginh };
	class netlink_audit_socket { create nlmsg_relay write };
	class sock_file { getattr write };
	class dir { getattr search };
}

#============= gpg_agent_t ==============
allow gpg_agent_t gpg_pinentry_t:process { noatsecure rlimitinh siginh };

#============= gpg_pinentry_t ==============
allow gpg_pinentry_t gpg_agent_tmp_t:dir search;
allow gpg_pinentry_t gpg_agent_tmp_t:sock_file { getattr write };

#============= gpg_t ==============
allow gpg_t self:netlink_audit_socket { create nlmsg_relay write };

#============= staff_t ==============
allow staff_t gpg_t:process { noatsecure rlimitinh siginh };
allow user_t gpg_t:process { noatsecure rlimitinh siginh };

Comment 2 Milos Malik 2018-01-17 06:44:42 UTC
The reproducer for staff_u user triggers many SELinux denials in enforcing mode. Here is the audit2allow summary:

#============= gpg_agent_t ==============
allow gpg_agent_t apm_bios_t:chr_file getattr;
allow gpg_agent_t autofs_device_t:chr_file getattr;
allow gpg_agent_t clock_device_t:chr_file getattr;
allow gpg_agent_t console_device_t:chr_file getattr;
allow gpg_agent_t devlog_t:sock_file getattr;
allow gpg_agent_t event_device_t:chr_file getattr;
allow gpg_agent_t fixed_disk_device_t:blk_file getattr;
allow gpg_agent_t framebuf_device_t:chr_file getattr;
allow gpg_agent_t fuse_device_t:chr_file getattr;

#!!!! This avc can be allowed using the boolean 'domain_can_write_kmsg'
allow gpg_agent_t kmsg_device_t:chr_file getattr;
allow gpg_agent_t kvm_device_t:chr_file getattr;
allow gpg_agent_t loop_control_device_t:chr_file getattr;
allow gpg_agent_t memory_device_t:chr_file getattr;
allow gpg_agent_t netcontrol_device_t:chr_file getattr;
allow gpg_agent_t nvram_device_t:chr_file getattr;
allow gpg_agent_t ppp_device_t:chr_file getattr;
allow gpg_agent_t ptmx_t:chr_file getattr;
allow gpg_agent_t tty_device_t:chr_file getattr;
allow gpg_agent_t uhid_device_t:chr_file getattr;
allow gpg_agent_t usb_device_t:chr_file getattr;
allow gpg_agent_t usbmon_device_t:chr_file getattr;
allow gpg_agent_t vhost_device_t:chr_file getattr;
allow gpg_agent_t xserver_misc_device_t:chr_file getattr;

#============= gpg_pinentry_t ==============
allow gpg_pinentry_t gpg_secret_t:sock_file getattr;

# rpm -qa selinux\* gnupg\* | sort
gnupg2-2.0.22-4.el7.x86_64
selinux-policy-3.13.1-183.el7.noarch
selinux-policy-targeted-3.13.1-183.el7.noarch
#

Comment 3 Milos Malik 2018-01-17 06:54:06 UTC
The reproducer for staff_u user triggers additional SELinux denial in permissive mode:
----
type=PROCTITLE msg=audit(01/17/2018 01:45:58.043:555) : proctitle=/usr/bin/pinentry-curses 
type=SYSCALL msg=audit(01/17/2018 01:45:58.043:555) : arch=x86_64 syscall=utime success=yes exit=0 a0=0x1c850f0 a1=0x0 a2=0x7ffdedb6d6c0 a3=0x7ffdedb6d060 items=0 ppid=8135 pid=8176 auid=staff-user uid=staff-user gid=staff-user euid=staff-user suid=staff-user fsuid=staff-user egid=staff-user sgid=staff-user fsgid=staff-user tty=(none) ses=4 comm=pinentry-curses exe=/usr/bin/pinentry-curses subj=staff_u:staff_r:gpg_pinentry_t:s0-s0:c0.c1023 key=(null) 
type=AVC msg=audit(01/17/2018 01:45:58.043:555) : avc:  denied  { write } for  pid=8176 comm=pinentry-curses name=S.gpg-agent dev="vda1" ino=12583996 scontext=staff_u:staff_r:gpg_pinentry_t:s0-s0:c0.c1023 tcontext=staff_u:object_r:gpg_secret_t:s0 tclass=sock_file permissive=1 
----

and produces following files:

$ ls -Z /home/staff-user/.gnupg/
drwx------. staff-user staff-user staff_u:object_r:gpg_secret_t:s0 private-keys-v1.d
-rw-------. staff-user staff-user staff_u:object_r:gpg_secret_t:s0 pubring.gpg
-rw-------. staff-user staff-user staff_u:object_r:gpg_secret_t:s0 pubring.gpg~
-rw-------. staff-user staff-user staff_u:object_r:gpg_secret_t:s0 random_seed
-rw-------. staff-user staff-user staff_u:object_r:gpg_secret_t:s0 secring.gpg
srwxrwxr-x. staff-user staff-user staff_u:object_r:gpg_secret_t:s0 S.gpg-agent
-rw-------. staff-user staff-user staff_u:object_r:gpg_secret_t:s0 trustdb.gpg
$

Comment 4 Milos Malik 2018-01-17 07:01:27 UTC
The reproducer for user_u user triggers the same SELinux denials but they contain "user_u:user:" instead of "staff_u:staff_r:" strings. For example:

----
type=PROCTITLE msg=audit(01/17/2018 02:00:16.333:825) : proctitle=/usr/bin/pinentry-curses 
type=SYSCALL msg=audit(01/17/2018 02:00:16.333:825) : arch=x86_64 syscall=utime success=yes exit=0 a0=0x121d0f0 a1=0x0 a2=0x7ffd1f147640 a3=0x7ffd1f146fe0 items=0 ppid=8333 pid=8348 auid=user-user uid=user-user gid=user-user euid=user-user suid=user-user fsuid=user-user egid=user-user sgid=user-user fsgid=user-user tty=(none) ses=5 comm=pinentry-curses exe=/usr/bin/pinentry-curses subj=user_u:user_r:gpg_pinentry_t:s0 key=(null) 
type=AVC msg=audit(01/17/2018 02:00:16.333:825) : avc:  denied  { write } for  pid=8348 comm=pinentry-curses name=S.gpg-agent dev="vda1" ino=23069025 scontext=user_u:user_r:gpg_pinentry_t:s0 tcontext=user_u:object_r:gpg_secret_t:s0 tclass=sock_file permissive=1 
----
type=PROCTITLE msg=audit(01/17/2018 02:00:16.333:824) : proctitle=/usr/bin/pinentry-curses 
type=SYSCALL msg=audit(01/17/2018 02:00:16.333:824) : arch=x86_64 syscall=stat success=yes exit=0 a0=0x121d0f0 a1=0x7ffd1f147640 a2=0x7ffd1f147640 a3=0x7ffd1f146fe0 items=0 ppid=8333 pid=8348 auid=user-user uid=user-user gid=user-user euid=user-user suid=user-user fsuid=user-user egid=user-user sgid=user-user fsgid=user-user tty=(none) ses=5 comm=pinentry-curses exe=/usr/bin/pinentry-curses subj=user_u:user_r:gpg_pinentry_t:s0 key=(null) 
type=AVC msg=audit(01/17/2018 02:00:16.333:824) : avc:  denied  { getattr } for  pid=8348 comm=pinentry-curses path=/home/user-user/.gnupg/S.gpg-agent dev="vda1" ino=23069025 scontext=user_u:user_r:gpg_pinentry_t:s0 tcontext=user_u:object_r:gpg_secret_t:s0 tclass=sock_file permissive=1 
----

Comment 15 Milos Malik 2019-04-29 11:57:22 UTC
----
type=PROCTITLE msg=audit(04/29/2019 07:53:38.013:1408) : proctitle=gpg --gen-key 
type=SYSCALL msg=audit(04/29/2019 07:53:38.013:1408) : arch=x86_64 syscall=socket success=yes exit=4 a0=netlink a1=SOCK_RAW a2=igp a3=0x7ffd71627220 items=0 ppid=1353 pid=1395 auid=user21061 uid=user21061 gid=staff-user euid=user21061 suid=user21061 fsuid=user21061 egid=staff-user sgid=staff-user fsgid=staff-user tty=pts1 ses=21 comm=gpg exe=/usr/bin/gpg2 subj=staff_u:staff_r:gpg_t:s0-s0:c0.c1023 key=(null) 
type=AVC msg=audit(04/29/2019 07:53:38.013:1408) : avc:  denied  { create } for  pid=1395 comm=gpg scontext=staff_u:staff_r:gpg_t:s0-s0:c0.c1023 tcontext=staff_u:staff_r:gpg_t:s0-s0:c0.c1023 tclass=netlink_audit_socket permissive=1 
----
type=PROCTITLE msg=audit(04/29/2019 07:53:38.013:1409) : proctitle=gpg --gen-key 
type=SOCKADDR msg=audit(04/29/2019 07:53:38.013:1409) : saddr={ fam=netlink nlnk-fam=16 nlnk-pid=0 } 
type=SYSCALL msg=audit(04/29/2019 07:53:38.013:1409) : arch=x86_64 syscall=sendmsg success=yes exit=18 a0=0x4 a1=0x7ffd71627e60 a2=0x0 a3=0x7ffd71627220 items=0 ppid=1353 pid=1395 auid=user21061 uid=user21061 gid=staff-user euid=user21061 suid=user21061 fsuid=user21061 egid=staff-user sgid=staff-user fsgid=staff-user tty=pts1 ses=21 comm=gpg exe=/usr/bin/gpg2 subj=staff_u:staff_r:gpg_t:s0-s0:c0.c1023 key=(null) 
type=AVC msg=audit(04/29/2019 07:53:38.013:1409) : avc:  denied  { nlmsg_relay } for  pid=1395 comm=gpg scontext=staff_u:staff_r:gpg_t:s0-s0:c0.c1023 tcontext=staff_u:staff_r:gpg_t:s0-s0:c0.c1023 tclass=netlink_audit_socket permissive=1 
type=AVC msg=audit(04/29/2019 07:53:38.013:1409) : avc:  denied  { write } for  pid=1395 comm=gpg scontext=staff_u:staff_r:gpg_t:s0-s0:c0.c1023 tcontext=staff_u:staff_r:gpg_t:s0-s0:c0.c1023 tclass=netlink_audit_socket permissive=1 
----

Comment 16 Milos Malik 2019-04-29 12:05:16 UTC
----
type=PROCTITLE msg=audit(04/29/2019 07:53:38.013:1408) : proctitle=gpg --gen-key 
type=SYSCALL msg=audit(04/29/2019 07:53:38.013:1408) : arch=x86_64 syscall=socket success=yes exit=4 a0=netlink a1=SOCK_RAW a2=igp a3=0x7ffd71627220 items=0 ppid=1353 pid=1395 auid=user21061 uid=user21061 gid=staff-user euid=user21061 suid=user21061 fsuid=user21061 egid=staff-user sgid=staff-user fsgid=staff-user tty=pts1 ses=21 comm=gpg exe=/usr/bin/gpg2 subj=staff_u:staff_r:gpg_t:s0-s0:c0.c1023 key=(null) 
type=AVC msg=audit(04/29/2019 07:53:38.013:1408) : avc:  denied  { create } for  pid=1395 comm=gpg scontext=staff_u:staff_r:gpg_t:s0-s0:c0.c1023 tcontext=staff_u:staff_r:gpg_t:s0-s0:c0.c1023 tclass=netlink_audit_socket permissive=1 
----
type=PROCTITLE msg=audit(04/29/2019 07:53:38.013:1409) : proctitle=gpg --gen-key 
type=SOCKADDR msg=audit(04/29/2019 07:53:38.013:1409) : saddr={ fam=netlink nlnk-fam=16 nlnk-pid=0 } 
type=SYSCALL msg=audit(04/29/2019 07:53:38.013:1409) : arch=x86_64 syscall=sendmsg success=yes exit=18 a0=0x4 a1=0x7ffd71627e60 a2=0x0 a3=0x7ffd71627220 items=0 ppid=1353 pid=1395 auid=user21061 uid=user21061 gid=staff-user euid=user21061 suid=user21061 fsuid=user21061 egid=staff-user sgid=staff-user fsgid=staff-user tty=pts1 ses=21 comm=gpg exe=/usr/bin/gpg2 subj=staff_u:staff_r:gpg_t:s0-s0:c0.c1023 key=(null) 
type=AVC msg=audit(04/29/2019 07:53:38.013:1409) : avc:  denied  { nlmsg_relay } for  pid=1395 comm=gpg scontext=staff_u:staff_r:gpg_t:s0-s0:c0.c1023 tcontext=staff_u:staff_r:gpg_t:s0-s0:c0.c1023 tclass=netlink_audit_socket permissive=1 
type=AVC msg=audit(04/29/2019 07:53:38.013:1409) : avc:  denied  { write } for  pid=1395 comm=gpg scontext=staff_u:staff_r:gpg_t:s0-s0:c0.c1023 tcontext=staff_u:staff_r:gpg_t:s0-s0:c0.c1023 tclass=netlink_audit_socket permissive=1 
----

# rpm -qa pgp\* gpg\* selinux\* | sort
gpgme-1.3.2-5.el7.x86_64
gpg-pubkey-352c64e5-52ae6884
selinux-policy-3.13.1-244.el7.noarch
selinux-policy-devel-3.13.1-244.el7.noarch
selinux-policy-targeted-3.13.1-244.el7.noarch
#

Comment 20 Lukas Vrabec 2019-05-17 22:56:44 UTC
commit 56a226ff0fa985b3635f8157d4f014f53a973d8b (HEAD -> rhel7.7-base, origin/rhel7.7-base)
Author: Lukas Vrabec <lvrabec>
Date:   Sat May 18 00:55:20 2019 +0200

    Update userdomains to allow confined users to create gpg keys
    Resolves: rhbz#1535109


commit f9f55b936e1ba08fe1483f8ea333289dc533095c (HEAD -> rhel7.7-contrib, origin/rhel7.7-contrib)
Author: Lukas Vrabec <lvrabec>
Date:   Sat May 18 00:49:45 2019 +0200

    Update gpg policy to make ti working with confined users
    Resolves: rhbz#1535109

Comment 24 errata-xmlrpc 2019-08-06 12:51:45 UTC
Since the problem described in this bug report should be
resolved in a recent advisory, it has been closed with a
resolution of ERRATA.

For information on the advisory, and where to find the updated
files, follow the link below.

If the solution does not work for you, open a new bug report.

https://access.redhat.com/errata/RHBA-2019:2127