Commit af39b832 authored by David Zeuthen's avatar David Zeuthen
Browse files

export PolKitConfig and provide a <define_admin_auth/> config file directive

Also change the libpolkit-grant API a bit to work with these changes.
parent 7034e0c2
......@@ -3,7 +3,8 @@
- Verify the security model
- Audit all code; especially the setgid helper
- Audit all code; especially the setgid polkit_user helper and setuid
root pam specific helper
- Granted privileges are currently world-visible; see
https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=244941
......@@ -24,12 +25,6 @@
- Make sure API coverage is 100%
- Have support for systems that don't use the root account; e.g.
instead of authenticating as root, authenticate a user in
e.g. the 'wheel' group. Probably means we need a combobox in the UI
bits (e.g. PolicyKit-gnome + friends) for selecting the user to
auth as.
- Potentially drop the glib dependency (it's not visible in the
external API). This is mainly to be able to handle OOM for
mechanisms that will need this (such as dbus-daemon)
......
......@@ -76,7 +76,7 @@
<xi:include href="xml/polkit-seat.xml"/>
<xi:include href="xml/polkit-session.xml"/>
<xi:include href="xml/polkit-caller.xml"/>
<xi:include href="xml/polkit-module.xml"/>
<xi:include href="xml/polkit-config.xml"/>
</reference>
<index>
......
......@@ -100,15 +100,15 @@ is supported and it can assume the following values
.B no
Access denied.
.TP
.B auth_root
.B auth_admin
Access denied, but authentication of the caller as root will grant
access to only that caller.
.TP
.B auth_root_keep_session
.B auth_admin_keep_session
Access denied, but authentication of the caller as root will grant
access for the remainder of the session the caller stems from.
.TP
.B auth_root_keep_always
.B auth_admin_keep_always
Access denied, but authentication of the caller as root will grant
access to the user of the caller in the future.
.TP
......@@ -127,6 +127,44 @@ access to the user of the caller in the future.
.B yes
Access granted.
.PP
.I define_admin_auth
This element is used to specify the meaning of "authenticate as
administrator". It is normally used at the top-level but can also be
used deep inside a number of
.I match
elements for conditional behavior.
There can only be a single attribute in each
.I define_admin_auth
element. POSIX Extended Regular Expression syntax are
.B not
supported in the value part, however multiple values to match on can
be separated with the bar (|) character. The following attributes
are supported:
.TP
.B user
Administrator authentication means authenticate as the given user.
If no
.I define_admin_auth
element is given, the default is to use
.B user="root"
e.g. administrator authentication mean authenticate as the super user.
.TP
.B group
Administrator authentication means that any user in the groups matching
the given value can be used to authenticate. Typically, on a system
with the root account disabled one wants to use something like
.B group="wheel"
to e.g. enable all UNIX users in the UNIX group
.B wheel
to be able to authentication whenever administrator authentication
is required.
.SH EXAMPLES
For brevity the standard XML and doctype headers are omitted in the
......
......@@ -78,13 +78,24 @@
* the right to do the Action.
*
* <-- Tell libpolkit-grant about grant details, e.g.
* {self,admin}_{,keep_session,keep_always}
* using stdout
* {self,admin}_{,keep_session,keep_always} +
* what users can authenticate using stdout
*
* Receive grant details on stdin.
* Caller prepares UI dialog depending
* on grant details.
*
* if admin_users is not empty, wait for
* user name of admin user to auth on stdin
*
* if admin_users is not empty, write
* user name of admin user to auth on stdout -->
*
*
* verify that given username is
* in admin_users
*
*
* Spawn polkit-grant-helper-pam
* with no args -->
*
......@@ -132,8 +143,13 @@
*/
/* the authentication itself is done via a setuid root helper; this is
* to make the code running as uid 0 easier to audit. */
/**
* do_auth:
*
* the authentication itself is done via a setuid root helper; this is
* to make the code running as uid 0 easier to audit.
*
*/
static polkit_bool_t
do_auth (const char *user_to_auth)
{
......@@ -208,6 +224,7 @@ do_auth (const char *user_to_auth)
/* read from parent */
if (fgets (buf, sizeof buf, stdin) == NULL)
goto out;
#ifdef PGH_DEBUG
fprintf (stderr, "received: '%s' from parent; sending to child\n", buf);
#endif /* PGH_DEBUG */
......@@ -224,11 +241,32 @@ out:
return ret;
}
/**
* verify_with_polkit:
* @caller_pid: the process id of the caller
* @action_name: name of the action
* @result: return location for result AKA how the user can auth
* @out_session_objpath: return location for ConsoleKit session identifier
* @out_admin_users: return location for a NULL-terminated array of
* strings that can be user to auth as admin. Is set to NULL if the
* super user (e.g. uid 0) should be user to auth as admin.
*
* Verify that the given caller can authenticate to gain a privilege
* to do the given action. If the authentication requires
* administrator privileges, also return a list of users that can be
* used to do this cf. the <define_admin_auth/> element in the
* configuration file; see the PolicyKit.conf(5) manual page for
* details.
*
* Returns: TRUE if, and only if, the given caller can authenticate to
* gain a privilege to do the given action.
*/
static polkit_bool_t
verify_with_polkit (pid_t caller_pid,
const char *action_name,
PolKitResult *result,
char **out_session_objpath)
char **out_session_objpath,
char ***out_admin_users)
{
PolKitCaller *caller;
PolKitSession *session;
......@@ -287,6 +325,103 @@ verify_with_polkit (pid_t caller_pid,
goto error;
}
*out_admin_users = NULL;
/* for admin auth, get a list of users that can be used - this is basically evaluating the
* <define_admin_auth/> directives in the config file...
*/
if (*result == POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH ||
*result == POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH_KEEP_SESSION ||
*result == POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH_KEEP_ALWAYS) {
PolKitConfig *pk_config;
PolKitConfigAdminAuthType admin_auth_type;
const char *admin_auth_data;
pk_config = polkit_context_get_config (pol_ctx);
if (polkit_config_determine_admin_auth_type (pk_config,
action,
caller,
&admin_auth_type,
&admin_auth_data)) {
#ifdef PGH_DEBUG
fprintf (stderr, "polkit-grant-helper: admin_auth_type=%d data='%s'\n", admin_auth_type, admin_auth_data);
#endif /* PGH_DEBUG */
switch (admin_auth_type) {
case POLKIT_CONFIG_ADMIN_AUTH_TYPE_USER:
if (admin_auth_data != NULL)
*out_admin_users = g_strsplit (admin_auth_data, "|", 0);
break;
case POLKIT_CONFIG_ADMIN_AUTH_TYPE_GROUP:
if (admin_auth_data != NULL) {
int n;
char **groups;
GSList *i;
GSList *users;
users = NULL;
groups = g_strsplit (admin_auth_data, "|", 0);
for (n = 0; groups[n] != NULL; n++) {
int m;
struct group *group;
/* This is fine; we're a single-threaded app */
if ((group = getgrnam (groups[n])) == NULL)
continue;
for (m = 0; group->gr_mem[m] != NULL; m++) {
const char *user;
gboolean found;
user = group->gr_mem[m];
found = FALSE;
#ifdef PGH_DEBUG
fprintf (stderr, "polkit-grant-helper: examining member '%s' of group '%s'\n", user, groups[n]);
#endif /* PGH_DEBUG */
/* skip user 'root' since he is often member of 'wheel' etc. */
if (strcmp (user, "root") == 0)
continue;
/* TODO: we should probably only consider users with an uid
* in a given "safe" range, e.g. between 500 and 32000 or
* something like that...
*/
for (i = users; i != NULL; i = g_slist_next (i)) {
if (strcmp (user, (const char *) i->data) == 0) {
found = TRUE;
break;
}
}
if (found)
continue;
#ifdef PGH_DEBUG
fprintf (stderr, "polkit-grant-helper: added user '%s'\n", user);
#endif /* PGH_DEBUG */
users = g_slist_prepend (users, g_strdup (user));
}
}
g_strfreev (groups);
users = g_slist_sort (users, (GCompareFunc) strcmp);
*out_admin_users = g_new0 (char *, g_slist_length (users) + 1);
for (i = users, n = 0; i != NULL; i = g_slist_next (i)) {
(*out_admin_users)[n++] = i->data;
}
g_slist_free (users);
}
break;
}
}
}
/* TODO: we should probably clean up */
return TRUE;
......@@ -298,6 +433,7 @@ static polkit_bool_t
get_and_validate_override_details (PolKitResult *result)
{
char buf[256];
char *textual_result;
PolKitResult desired_result;
if (fgets (buf, sizeof buf, stdin) == NULL)
......@@ -305,10 +441,17 @@ get_and_validate_override_details (PolKitResult *result)
if (strlen (buf) > 0 &&
buf[strlen (buf) - 1] == '\n')
buf[strlen (buf) - 1] = '\0';
fprintf (stderr, "polkit-grant-helper: caller said '%s'\n", buf);
if (!polkit_result_from_string_representation (buf, &desired_result))
if (strncmp (buf,
"POLKIT_GRANT_CALLER_PASS_OVERRIDE_GRANT_TYPE ",
sizeof "POLKIT_GRANT_CALLER_PASS_OVERRIDE_GRANT_TYPE " - 1) != 0) {
goto error;
}
textual_result = buf + sizeof "POLKIT_GRANT_CALLER_PASS_OVERRIDE_GRANT_TYPE " - 1;
fprintf (stderr, "polkit-grant-helper: caller said '%s'\n", textual_result);
if (!polkit_result_from_string_representation (textual_result, &desired_result))
goto error;
fprintf (stderr, "polkit-grant-helper: testing for voluntarily downgrade from '%s' to '%s'\n",
......@@ -386,6 +529,7 @@ main (int argc, char *argv[])
char *session_objpath;
struct passwd *pw;
polkit_bool_t dbres;
char **admin_users;
ret = 3;
......@@ -461,9 +605,19 @@ main (int argc, char *argv[])
* - figure out if the caller can really auth to do the action
* - learn what ConsoleKit session the caller belongs to
*/
if (!verify_with_polkit (caller_pid, action_name, &result, &session_objpath))
if (!verify_with_polkit (caller_pid, action_name, &result, &session_objpath, &admin_users))
goto out;
#ifdef PGH_DEBUG
if (admin_users != NULL) {
int n;
fprintf (stderr, "polkit-grant-helper: admin_users: ");
for (n = 0; admin_users[n] != NULL; n++)
fprintf (stderr, "'%s' ", admin_users[n]);
fprintf (stderr, "\n");
}
#endif /* PGH_DEBUG */
#ifdef PGH_DEBUG
fprintf (stderr, "polkit-grant-helper: polkit result = '%s'\n",
polkit_result_to_string_representation (result));
......@@ -477,21 +631,70 @@ main (int argc, char *argv[])
polkit_result_to_string_representation (result));
fflush (stdout);
/* figure out what user to auth */
if (result == POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH ||
result == POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH_KEEP_SESSION ||
result == POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH_KEEP_ALWAYS) {
/* TODO: with wheel support, figure out what user to auth */
user_to_auth = "root";
/* if admin auth is required, tell caller about possible users */
if (admin_users != NULL) {
int n;
fprintf (stdout, "POLKIT_GRANT_HELPER_TELL_ADMIN_USERS");
for (n = 0; admin_users[n] != NULL; n++)
fprintf (stdout, " %s", admin_users[n]);
fprintf (stdout, "\n");
fflush (stdout);
}
/* wait for libpolkit-grant to tell us what user to use */
if (admin_users != NULL) {
int n;
char buf[256];
#ifdef PGH_DEBUG
fprintf (stderr, "waiting for admin user name...\n");
#endif /* PGH_DEBUG */
/* read from parent */
if (fgets (buf, sizeof buf, stdin) == NULL)
goto out;
if (strlen (buf) > 0 && buf[strlen (buf) - 1] == '\n')
buf[strlen (buf) - 1] = '\0';
if (strncmp (buf,
"POLKIT_GRANT_CALLER_SELECT_ADMIN_USER ",
sizeof "POLKIT_GRANT_CALLER_SELECT_ADMIN_USER " - 1) != 0) {
goto out;
}
user_to_auth = strdup (buf) + sizeof "POLKIT_GRANT_CALLER_SELECT_ADMIN_USER " - 1;
#ifdef PGH_DEBUG
fprintf (stderr, "libpolkit-grant wants to auth as '%s'\n", user_to_auth);
#endif /* PGH_DEBUG */
/* now sanity check that returned user is actually in admin_users */
for (n = 0; admin_users[n] != NULL; n++) {
if (strcmp (admin_users[n], user_to_auth) == 0)
break;
}
if (admin_users[n] == NULL) {
ret = 2;
goto out;
}
} else {
user_to_auth = invoking_user_name;
/* figure out what user to auth */
if (result == POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH ||
result == POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH_KEEP_SESSION ||
result == POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH_KEEP_ALWAYS) {
user_to_auth = "root";
} else {
user_to_auth = invoking_user_name;
}
}
ret = 1;
/* Start authentication */
if (!do_auth (user_to_auth))
if (!do_auth (user_to_auth)) {
goto out;
}
/* Ask caller if he want to slim down grant type... e.g. he
* might want to go from auth_self_keep_always to
......
......@@ -60,6 +60,7 @@ struct PolKitGrant
PolKitGrantAddChildWatch func_add_child_watch;
PolKitGrantRemoveWatch func_remove_watch;
PolKitGrantType func_type;
PolKitGrantSelectAdminUser func_select_admin_user;
PolKitGrantConversationPromptEchoOff func_prompt_echo_off;
PolKitGrantConversationPromptEchoOn func_prompt_echo_on;
PolKitGrantConversationErrorMessage func_error_message;
......@@ -77,7 +78,7 @@ struct PolKitGrant
int io_watch_id;
gboolean success;
gboolean auth_in_progress;
gboolean helper_is_running;
};
/**
......@@ -162,6 +163,7 @@ polkit_grant_unref (PolKitGrant *polkit_grant)
* @func_add_child_watch: Callback function
* @func_remove_watch: Callback function
* @func_type: Callback function
* @func_select_admin_user: Callback function
* @func_prompt_echo_off: Callback function
* @func_prompt_echo_on: Callback function
* @func_error_message: Callback function
......@@ -174,23 +176,25 @@ polkit_grant_unref (PolKitGrant *polkit_grant)
**/
void
polkit_grant_set_functions (PolKitGrant *polkit_grant,
PolKitGrantAddIOWatch func_add_io_watch,
PolKitGrantAddChildWatch func_add_child_watch,
PolKitGrantRemoveWatch func_remove_watch,
PolKitGrantType func_type,
PolKitGrantConversationPromptEchoOff func_prompt_echo_off,
PolKitGrantConversationPromptEchoOn func_prompt_echo_on,
PolKitGrantConversationErrorMessage func_error_message,
PolKitGrantConversationTextInfo func_text_info,
PolKitGrantOverrideGrantType func_override_grant_type,
PolKitGrantDone func_done,
void *user_data)
PolKitGrantAddIOWatch func_add_io_watch,
PolKitGrantAddChildWatch func_add_child_watch,
PolKitGrantRemoveWatch func_remove_watch,
PolKitGrantType func_type,
PolKitGrantSelectAdminUser func_select_admin_user,
PolKitGrantConversationPromptEchoOff func_prompt_echo_off,
PolKitGrantConversationPromptEchoOn func_prompt_echo_on,
PolKitGrantConversationErrorMessage func_error_message,
PolKitGrantConversationTextInfo func_text_info,
PolKitGrantOverrideGrantType func_override_grant_type,
PolKitGrantDone func_done,
void *user_data)
{
g_return_if_fail (polkit_grant != NULL);
g_return_if_fail (func_add_io_watch != NULL);
g_return_if_fail (func_add_child_watch != NULL);
g_return_if_fail (func_remove_watch != NULL);
g_return_if_fail (func_type != NULL);
g_return_if_fail (func_select_admin_user != NULL);
g_return_if_fail (func_prompt_echo_off != NULL);
g_return_if_fail (func_prompt_echo_on != NULL);
g_return_if_fail (func_error_message != NULL);
......@@ -200,6 +204,7 @@ polkit_grant_set_functions (PolKitGrant *polkit_grant,
polkit_grant->func_add_child_watch = func_add_child_watch;
polkit_grant->func_remove_watch = func_remove_watch;
polkit_grant->func_type = func_type;
polkit_grant->func_select_admin_user = func_select_admin_user;
polkit_grant->func_prompt_echo_off = func_prompt_echo_off;
polkit_grant->func_prompt_echo_on = func_prompt_echo_on;
polkit_grant->func_error_message = func_error_message;
......@@ -227,7 +232,7 @@ polkit_grant_child_func (PolKitGrant *polkit_grant, pid_t pid, int exit_code)
polkit_bool_t input_was_bogus;
g_return_if_fail (polkit_grant != NULL);
g_return_if_fail (polkit_grant->auth_in_progress);
g_return_if_fail (polkit_grant->helper_is_running);
g_debug ("pid %d terminated", pid);
waitpid (pid, &status, 0);
......@@ -238,6 +243,7 @@ polkit_grant_child_func (PolKitGrant *polkit_grant, pid_t pid, int exit_code)
input_was_bogus = FALSE;
polkit_grant->success = (exit_code == 0);
polkit_grant->helper_is_running = FALSE;
polkit_grant->func_done (polkit_grant, polkit_grant->success, input_was_bogus, polkit_grant->user_data);
}
......@@ -259,22 +265,23 @@ polkit_grant_io_func (PolKitGrant *polkit_grant, int fd)
char *id;
size_t id_len;
char *response;
char *response_prefix;
g_return_if_fail (polkit_grant != NULL);
g_return_if_fail (polkit_grant->auth_in_progress);
g_return_if_fail (polkit_grant->helper_is_running);
while (getline (&line, &line_len, polkit_grant->child_stdout_f) != -1) {
if (strlen (line) > 0 &&
line[strlen (line) - 1] == '\n')
line[strlen (line) - 1] = '\0';
//printf ("from child '%s'\n", line);
response = NULL;
response_prefix = NULL;
id = "PAM_PROMPT_ECHO_OFF ";
if (g_str_has_prefix (line, id)) {
id_len = strlen (id);
response_prefix = "";
response = polkit_grant->func_prompt_echo_off (polkit_grant,
line + id_len,
polkit_grant->user_data);
......@@ -284,6 +291,7 @@ polkit_grant_io_func (PolKitGrant *polkit_grant, int fd)
id = "PAM_PROMPT_ECHO_ON ";
if (g_str_has_prefix (line, id)) {
id_len = strlen (id);
response_prefix = "";
response = polkit_grant->func_prompt_echo_on (polkit_grant,
line + id_len,
polkit_grant->user_data);
......@@ -311,16 +319,36 @@ polkit_grant_io_func (PolKitGrant *polkit_grant, int fd)
id = "POLKIT_GRANT_HELPER_TELL_TYPE ";
if (g_str_has_prefix (line, id)) {
PolKitResult result;
char *result_textual;
id_len = strlen (id);
if (!polkit_result_from_string_representation (line + id_len, &result)) {
result_textual = line + id_len;
if (!polkit_result_from_string_representation (result_textual, &result)) {
/* TODO: danger will robinson */
}
polkit_grant->func_type (polkit_grant,
result,
polkit_grant->user_data);
goto processed;
}
id = "POLKIT_GRANT_HELPER_TELL_ADMIN_USERS ";
if (g_str_has_prefix (line, id)) {
char **admin_users;
id_len = strlen (id);
admin_users = g_strsplit (line + id_len, " ", 0);
response_prefix = "POLKIT_GRANT_CALLER_SELECT_ADMIN_USER ";
response = polkit_grant->func_select_admin_user (polkit_grant,
admin_users,
polkit_grant->user_data);
g_strfreev (admin_users);
goto processed;
}
id = "POLKIT_GRANT_HELPER_ASK_OVERRIDE_GRANT_TYPE ";
if (g_str_has_prefix (line, id)) {
PolKitResult override;
......@@ -332,19 +360,27 @@ polkit_grant_io_func (PolKitGrant *polkit_grant, int fd)
override = polkit_grant->func_override_grant_type (polkit_grant,
result,
polkit_grant->user_data);
response_prefix = "POLKIT_GRANT_CALLER_PASS_OVERRIDE_GRANT_TYPE ";
response = g_strdup (polkit_result_to_string_representation (override));
goto processed;
}
processed:
if (response != NULL) {
if (response != NULL && response_prefix != NULL) {
char *buf;
gboolean add_newline;
/* add a newline if there isn't one already... */
add_newline = FALSE;
if (response[strlen (response) - 1] != '\n') {
char *old = response;
response = g_strdup_printf ("%s\n", response);
g_free (old);
add_newline = TRUE;
}
write (polkit_grant->child_stdin, response, strlen (response));
buf = g_strdup_printf ("%s%s%c",
response_prefix,
response,
add_newline ? '\n' : '\0');
write (polkit_grant->child_stdin, buf, strlen (buf));
g_free (buf);
free (response);
}
}
......@@ -364,7 +400,7 @@ polkit_grant_cancel_auth (PolKitGrant *polkit_grant)
{
GPid pid;
g_return_if_fail (polkit_grant != NULL);
g_return_if_fail (polkit_grant->auth_in_progress);
g_return_if_fail (polkit_grant->helper_is_running);
pid = polkit_grant->child_pid;
polkit_grant->child_pid = 0;
......@@ -372,6 +408,7 @@ polkit_grant_cancel_auth (PolKitGrant *polkit_grant)
int status;
kill (pid, SIGTERM);
waitpid (pid, &status, 0);
polkit_grant->helper_is_running = FALSE;
}
polkit_grant->func_done (polkit_grant, FALSE, FALSE, polkit_grant->user_data);
}
......@@ -463,7 +500,7 @@ polkit_grant_initiate_auth (PolKitGrant *polkit_grant,
polkit_grant->success = FALSE;