Skip to content

Commit f2206c2

Browse files
committed
agent helper: support separate socket-activated service to run without SETUID
SETUID binaries are considered harmful, as te execution context is under the control of unprivileged attackers. Enhance the polkit pam agent helper with a new mode: when running under systemd, add a socket-activated service that the helper will run under, as root. The agent talks to this service via AF_UNIX instead of spawning it, and STDIN/STDOUT are connected as before. The helper can make use of PID FDs and SO_PEERCRED to reliably identify the caller. In order to do this, a third version of the auth D-Bus method is added, that also takes a subject, built using the PID FD. If the AF_UNIX socket is not present, the agent will fork the helper as before, with no changes. Fixes polkit-org#169
1 parent d68a3b4 commit f2206c2

15 files changed

+474
-36
lines changed

.packit/polkit.spec

+2
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,8 @@ rm -f $RPM_BUILD_ROOT%{_libdir}/*.la
125125
%{_datadir}/dbus-1/system.d/org.freedesktop.PolicyKit1.conf
126126
%{_datadir}/dbus-1/system-services/*
127127
%{_unitdir}/polkit.service
128+
%{_unitdir}/polkit-agent-helper.socket
129+
%{_unitdir}/[email protected]
128130
%dir %{_datadir}/polkit-1/
129131
%dir %{_datadir}/polkit-1/actions
130132
%attr(0750,root,polkitd) %dir %{_datadir}/polkit-1/rules.d

data/meson.build

+13
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,19 @@ configure_file(
3838
install_dir: systemdsystemunitdir,
3939
)
4040

41+
configure_file(
42+
43+
output: '@BASENAME@',
44+
configuration: service_conf,
45+
install: true,
46+
install_dir: systemdsystemunitdir,
47+
)
48+
49+
install_data(
50+
'polkit-agent-helper.socket',
51+
install_dir: systemdsystemunitdir,
52+
)
53+
4154
configure_file(
4255
input: 'polkit.conf.in',
4356
output: '@BASENAME@',

data/polkit-agent-helper.socket

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[Unit]
2+
Description=Authorization Manager Agent Helper
3+
Documentation=man:polkit(8)
4+
5+
[Socket]
6+
Accept=yes
7+
RemoveOnStop=yes
8+
ListenStream=/run/polkit/agent-helper.socket
9+
10+
[Install]
11+
WantedBy=sockets.target

data/[email protected]

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
[Unit]
2+
Description=Authorization Manager Agent Helper
3+
Documentation=man:polkit(8)
4+
5+
[Service]
6+
Type=oneshot
7+
DeviceAllow=/dev/null rw
8+
DevicePolicy=strict
9+
ExecStart=@libprivdir@/polkit-agent-helper-1 --socket-activated
10+
StandardInput=socket
11+
StandardOutput=socket
12+
LimitMEMLOCK=0
13+
LockPersonality=yes
14+
MemoryDenyWriteExecute=yes
15+
NoNewPrivileges=yes
16+
PrivateDevices=yes
17+
PrivateNetwork=yes
18+
PrivateTmp=yes
19+
ProtectControlGroups=yes
20+
ProtectHome=yes
21+
ProtectKernelModules=yes
22+
ProtectKernelLogs=yes
23+
ProtectKernelTunables=yes
24+
ProtectSystem=strict
25+
ProtectClock=yes
26+
ProtectHostname=yes
27+
RemoveIPC=yes
28+
RestrictAddressFamilies=AF_UNIX
29+
RestrictNamespaces=yes
30+
RestrictRealtime=yes
31+
RestrictSUIDSGID=yes
32+
SystemCallArchitectures=native
33+
SystemCallFilter=@system-service
34+
UMask=0077

docs/polkit/docbook-interface-org.freedesktop.PolicyKit1.Authority.xml

+45-1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ Structure <link linkend="eggdbus-struct-TemporaryAuthorization">TemporaryAuth
4444
IN <link linkend="eggdbus-struct-Identity">Identity</link> identity)
4545
<link linkend="eggdbus-method-org.freedesktop.PolicyKit1.Authority.AuthenticationAgentResponse2">AuthenticationAgentResponse2</link> (IN uint32 uid, IN String cookie,
4646
IN <link linkend="eggdbus-struct-Identity">Identity</link> identity)
47+
<link linkend="eggdbus-method-org.freedesktop.PolicyKit1.Authority.AuthenticationAgentResponse3">AuthenticationAgentResponse3</link> (IN String cookie,
48+
IN <link linkend="eggdbus-struct-Identity">Identity</link> identity,
49+
IN <link linkend="eggdbus-struct-Subject">Subject</link> subject)
4750
<link linkend="eggdbus-method-org.freedesktop.PolicyKit1.Authority.EnumerateTemporaryAuthorizations">EnumerateTemporaryAuthorizations</link> (IN <link linkend="eggdbus-struct-Subject">Subject</link> subject,
4851
OUT Array&lt;<link linkend="eggdbus-struct-TemporaryAuthorization">TemporaryAuthorization</link>&gt; temporary_authorizations)
4952
<link linkend="eggdbus-method-org.freedesktop.PolicyKit1.Authority.RevokeTemporaryAuthorizations">RevokeTemporaryAuthorizations</link> (IN <link linkend="eggdbus-struct-Subject">Subject</link> subject)
@@ -316,7 +319,7 @@ Details about the subject. Depending of the value of <parameter>subject_kind</pa
316319
}
317320
</programlisting>
318321
<para>
319-
<para>This struct describes identities such as UNIX users and UNIX groups. It is typically used to check if a given process is authorized for an action.</para><para>The following kinds of identities are known:</para> <formalpara><title>Unix User</title><para><literal>identity_kind</literal> should be set to <literal>unix-user</literal> with key <literal>uid</literal> (of type <literal>uint32</literal>).</para></formalpara> <formalpara><title>Unix Group</title><para><literal>identity_kind</literal> should be set to <literal>unix-group</literal> with key <literal>gid</literal> (of type <literal>uint32</literal>).</para></formalpara>
322+
<para>This struct describes identities such as UNIX users and UNIX groups. It is typically used to check if a given process is authorized for an action.</para><para>The following kinds of identities are known:</para> <formalpara><title>Unix User</title><para><literal>identity_kind</literal> should be set to <literal>unix-user</literal> with key <literal>uid</literal> (of type <literal>uint32</literal>).</para></formalpara> <formalpara><title>Unix Group</title><para><literal>identity_kind</literal> should be set to <literal>unix-group</literal> with key <literal>gid</literal> (of type <literal>uint32</literal>).</para></formalpara>
320323
</para>
321324
<variablelist role="struct">
322325
<varlistentry>
@@ -853,6 +856,47 @@ A <link linkend="eggdbus-struct-Identity">Identity</link> struct describing what
853856
</para>
854857
</listitem>
855858
</varlistentry>
859+
</variablelist>
860+
</refsect2>
861+
<refsect2 role="function" id="eggdbus-method-org.freedesktop.PolicyKit1.Authority.AuthenticationAgentResponse3">
862+
<title>AuthenticationAgentResponse3 ()</title>
863+
<programlisting>
864+
AuthenticationAgentResponse3 (IN String cookie,
865+
IN <link linkend="eggdbus-struct-Identity">Identity</link> identity,
866+
IN <link linkend="eggdbus-struct-Subject">Subject</link> subject)
867+
</programlisting>
868+
<para>
869+
Method for authentication agents to invoke on successful
870+
authentication, intended only for use by a privileged helper process
871+
running via socket activation. This method will fail unless a sufficiently privileged
872+
caller invokes it. In contract to other methods this takes a subject as input, which
873+
allows reliably tracking the requester via PID FD.
874+
</para>
875+
<variablelist role="params">
876+
<varlistentry>
877+
<term><literal>IN String <parameter>cookie</parameter></literal>:</term>
878+
<listitem>
879+
<para>
880+
The cookie identifying the authentication request that was passed to the authentication agent.
881+
</para>
882+
</listitem>
883+
</varlistentry>
884+
<varlistentry>
885+
<term><literal>IN <link linkend="eggdbus-struct-Identity">Identity</link> <parameter>identity</parameter></literal>:</term>
886+
<listitem>
887+
<para>
888+
A <link linkend="eggdbus-struct-Identity">Identity</link> struct describing what identity was authenticated.
889+
</para>
890+
</listitem>
891+
</varlistentry>
892+
<varlistentry>
893+
<term><literal>IN <link linkend="eggdbus-struct-Subject">Subject</link> <parameter>subject</parameter></literal>:</term>
894+
<listitem>
895+
<para>
896+
A <link linkend="eggdbus-struct-Subject">Subject</link> struct describing what entity requested the authentication.
897+
</para>
898+
</listitem>
899+
</varlistentry>
856900
</variablelist>
857901
</refsect2>
858902
<refsect2 role="function" id="eggdbus-method-org.freedesktop.PolicyKit1.Authority.EnumerateTemporaryAuthorizations">

docs/polkit/polkit-1-sections.txt

+2
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ polkit_authority_unregister_authentication_agent_sync
4848
polkit_authority_authentication_agent_response
4949
polkit_authority_authentication_agent_response_finish
5050
polkit_authority_authentication_agent_response_sync
51+
polkit_authority_authentication_agent_response_with_subject
52+
polkit_authority_authentication_agent_response_with_subject_sync
5153
polkit_authority_enumerate_temporary_authorizations
5254
polkit_authority_enumerate_temporary_authorizations_finish
5355
polkit_authority_enumerate_temporary_authorizations_sync

src/polkit/polkitauthority.c

+108
Original file line numberDiff line numberDiff line change
@@ -1488,6 +1488,67 @@ polkit_authority_unregister_authentication_agent_sync (PolkitAuthority *auth
14881488

14891489
/* ---------------------------------------------------------------------------------------------------- */
14901490

1491+
/**
1492+
* polkit_authority_authentication_agent_response:
1493+
* @authority: A #PolkitAuthority.
1494+
* @cookie: The cookie passed to the authentication agent from the authority.
1495+
* @identity: The identity that was authenticated.
1496+
* @subject: The subject that requested the authentication.
1497+
* @cancellable: (allow-none): A #GCancellable or %NULL.
1498+
* @callback: A #GAsyncReadyCallback to call when the request is satisfied.
1499+
* @user_data: The data to pass to @callback.
1500+
*
1501+
* Asynchronously provide response that @identity successfully authenticated
1502+
* for the authentication request identified by @cookie as requested by @subject.
1503+
*
1504+
* This function is only used by the socket-activated agent helper, running as uiid
1505+
* 0, and will fail otherwise. The requesting process is identified via @subject
1506+
* which will contain a PID FD identifying the process.
1507+
*
1508+
* When the operation is finished, @callback will be invoked in the
1509+
* <link linkend="g-main-context-push-thread-default">thread-default
1510+
* main loop</link> of the thread you are calling this method
1511+
* from. You can then call
1512+
* polkit_authority_authentication_agent_response_finish() to get the
1513+
* result of the operation.
1514+
**/
1515+
void
1516+
polkit_authority_authentication_agent_response_with_subject (PolkitAuthority *authority,
1517+
const gchar *cookie,
1518+
PolkitIdentity *identity,
1519+
PolkitSubject *subject,
1520+
GCancellable *cancellable,
1521+
GAsyncReadyCallback callback,
1522+
gpointer user_data)
1523+
{
1524+
/* Unlike the polkit_authority_authentication_agent_response variant,
1525+
* this one is called from a socket-activated service, rather than a
1526+
* setuid helper invoked directly by the authenticating process.
1527+
*/
1528+
g_return_if_fail (POLKIT_IS_AUTHORITY (authority));
1529+
g_return_if_fail (cookie != NULL);
1530+
g_return_if_fail (POLKIT_IS_IDENTITY (identity));
1531+
g_return_if_fail (POLKIT_IS_SUBJECT (subject));
1532+
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
1533+
1534+
g_dbus_proxy_call (authority->proxy,
1535+
"AuthenticationAgentResponse3",
1536+
g_variant_new ("(s@(sa{sv})@(sa{sv}))",
1537+
cookie,
1538+
polkit_identity_to_gvariant (identity), /* Floating value */
1539+
polkit_subject_to_gvariant (subject)), /* Floating value */
1540+
G_DBUS_CALL_FLAGS_NONE,
1541+
-1,
1542+
cancellable,
1543+
generic_async_cb,
1544+
g_simple_async_result_new (G_OBJECT (authority),
1545+
callback,
1546+
user_data,
1547+
polkit_authority_authentication_agent_response));
1548+
}
1549+
1550+
/* ---------------------------------------------------------------------------------------------------- */
1551+
14911552
/**
14921553
* polkit_authority_authentication_agent_response:
14931554
* @authority: A #PolkitAuthority.
@@ -1630,6 +1691,53 @@ polkit_authority_authentication_agent_response_sync (PolkitAuthority *author
16301691
return ret;
16311692
}
16321693

1694+
1695+
/**
1696+
* polkit_authority_authentication_agent_response_with_subject_sync:
1697+
* @authority: A #PolkitAuthority.
1698+
* @cookie: The cookie passed to the authentication agent from the authority.
1699+
* @identity: The identity that was authenticated.
1700+
* @subject: The subject that requested the authentication.
1701+
* @cancellable: (allow-none): A #GCancellable or %NULL.
1702+
* @error: (allow-none): Return location for error or %NULL.
1703+
*
1704+
* Provide response that @identity successfully authenticated for the
1705+
* authentication request identified by @cookie. See polkit_authority_authentication_agent_response_with_subject()
1706+
* for limitations on who is allowed is to call this method.
1707+
*
1708+
* The calling thread is blocked until a reply is received. See
1709+
* polkit_authority_authentication_agent_response_with_subject() for the
1710+
* asynchronous version.
1711+
*
1712+
* Returns: %TRUE if @authority acknowledged the call, %FALSE if @error is set.
1713+
**/
1714+
gboolean
1715+
polkit_authority_authentication_agent_response_with_subject_sync (PolkitAuthority *authority,
1716+
const gchar *cookie,
1717+
PolkitIdentity *identity,
1718+
PolkitSubject *subject,
1719+
GCancellable *cancellable,
1720+
GError **error)
1721+
{
1722+
gboolean ret;
1723+
CallSyncData *data;
1724+
1725+
g_return_val_if_fail (POLKIT_IS_AUTHORITY (authority), FALSE);
1726+
g_return_val_if_fail (cookie != NULL, FALSE);
1727+
g_return_val_if_fail (POLKIT_IS_IDENTITY (identity), FALSE);
1728+
g_return_val_if_fail (POLKIT_IS_SUBJECT (subject), FALSE);
1729+
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
1730+
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1731+
1732+
data = call_sync_new ();
1733+
polkit_authority_authentication_agent_response_with_subject (authority, cookie, identity, subject, cancellable, call_sync_cb, data);
1734+
call_sync_block (data);
1735+
ret = polkit_authority_authentication_agent_response_finish (authority, data->res, error);
1736+
call_sync_free (data);
1737+
1738+
return ret;
1739+
}
1740+
16331741
/* ---------------------------------------------------------------------------------------------------- */
16341742

16351743
/**

src/polkit/polkitauthority.h

+15
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,13 @@ gboolean polkit_authority_authentication_agent_response_sync (
103103
GCancellable *cancellable,
104104
GError **error);
105105

106+
gboolean polkit_authority_authentication_agent_response_with_subject_sync (PolkitAuthority *authority,
107+
const gchar *cookie,
108+
PolkitIdentity *identity,
109+
PolkitSubject *subject,
110+
GCancellable *cancellable,
111+
GError **error);
112+
106113
GList *polkit_authority_enumerate_temporary_authorizations_sync (PolkitAuthority *authority,
107114
PolkitSubject *subject,
108115
GCancellable *cancellable,
@@ -186,6 +193,14 @@ void polkit_authority_authentication_agent_response (Polki
186193
GAsyncReadyCallback callback,
187194
gpointer user_data);
188195

196+
void polkit_authority_authentication_agent_response_with_subject (PolkitAuthority *authority,
197+
const gchar *cookie,
198+
PolkitIdentity *identity,
199+
PolkitSubject *subject,
200+
GCancellable *cancellable,
201+
GAsyncReadyCallback callback,
202+
gpointer user_data);
203+
189204
gboolean polkit_authority_authentication_agent_response_finish (PolkitAuthority *authority,
190205
GAsyncResult *res,
191206
GError **error);

src/polkitagent/polkitagenthelper-bsdauth.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ main (int argc, char *argv[])
114114
/* now send a D-Bus message to the polkit daemon that
115115
* includes a) the cookie; and b) the user we authenticated
116116
*/
117-
if (!send_dbus_message (cookie, user_to_auth))
117+
if (!send_dbus_message (cookie, user_to_auth, -1, -1))
118118
{
119119
#ifdef PAH_DEBUG
120120
fprintf (stderr, "polkit-agent-helper-1: error sending D-Bus message to polkit daemon\n");

0 commit comments

Comments
 (0)