Commit 10cdbe0e authored by David Zeuthen's avatar David Zeuthen

first cut at authentication helper library

Not finished yet...
parent e800e80f
SUBDIRS = polkit polkitbackend polkitd programs
SUBDIRS = polkit polkitbackend polkitagent polkitd programs
clean-local :
rm -f *~
NULL =
INCLUDES = \
-I$(top_builddir)/src \
-I$(top_srcdir)/src \
-DPACKAGE_LIBEXEC_DIR=\""$(libexecdir)"\" \
-DPACKAGE_SYSCONF_DIR=\""$(sysconfdir)"\" \
-DPACKAGE_DATA_DIR=\""$(datadir)"\" \
-DPACKAGE_BIN_DIR=\""$(bindir)"\" \
-DPACKAGE_LOCALSTATE_DIR=\""$(localstatedir)"\" \
-DPACKAGE_LOCALE_DIR=\""$(localedir)"\" \
-DPACKAGE_LIB_DIR=\""$(libdir)"\" \
-D_POSIX_PTHREAD_SEMANTICS \
-D_REENTRANT \
$(NULL)
lib_LTLIBRARIES=libpolkit-agent-1.la
libpolkit_agent_1includedir=$(includedir)/polkit-1/polkitagent
libpolkit_agent_1include_HEADERS = \
polkitagent.h \
polkitagenttypes.h \
polkitauthenticationsession.h \
$(NULL)
libpolkit_agent_1_la_SOURCES = \
polkitagent.h \
polkitagenttypes.h \
polkitauthenticationsession.h polkitauthenticationsession.c \
$(NULL)
libpolkit_agent_1_la_CFLAGS = \
-D_POLKIT_AGENT_COMPILATION \
$(GLIB_CFLAGS) \
$(EGG_DBUS_CFLAGS) \
$(NULL)
libpolkit_agent_1_la_LIBADD = \
$(GLIB_LIBS) \
$(EGG_DBUS_LIBS) \
$(top_builddir)/src/polkit/libpolkit-gobject-1.la \
$(EXPAT_LIBS) \
$(NULL)
libexec_PROGRAMS = polkit-agent-helper-1
polkit_agent_helper_1_SOURCES = polkitagenthelper.c
polkit_agent_helper_1_LDADD = $(AUTH_LIBS)
# polkit-agent-helper-1 need to be setuid root because it's used to
# authenticate not only the invoking user, but possibly also root
# and/or other users.
#
install-exec-hook:
-chown root $(DESTDIR)$(libexecdir)/polkit-agent-helper-1
-chmod 4755 $(DESTDIR)$(libexecdir)/polkit-agent-helper-1
CLEANFILES = $(BUILT_SOURCES)
clean-local :
rm -f *~
/*
* Copyright (C) 2008 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#ifndef __POLKIT_AGENT_H
#define __POLKIT_AGENT_H
#define _POLKIT_AGENT_INSIDE_POLKIT_AGENT_H 1
#include <polkitagent/polkitauthenticationsession.h>
#undef _POLKIT_AGENT_INSIDE_POLKIT_AGENT_H
#endif /* __POLKIT_AGENT_H */
/*
* Copyright (C) 2008 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <syslog.h>
#include <security/pam_appl.h>
#ifdef HAVE_SOLARIS
# define LOG_AUTHPRIV (10<<3)
#endif
/* Development aid: define PAH_DEBUG to get debugging output. Do _NOT_
* enable this in production builds; it may leak passwords and other
* sensitive information.
*/
#undef PAH_DEBUG
#define PAH_DEBUG
static int conversation_function (int n, const struct pam_message **msg, struct pam_response **resp, void *data);
int
main (int argc, char *argv[])
{
int rc;
const char *user_to_auth;
const char *cookie;
struct pam_conv pam_conversation;
pam_handle_t *pam_h;
const void *authed_user;
rc = 0;
pam_h = NULL;
/* clear the entire environment to avoid attacks using with libraries honoring environment variables */
if (clearenv () != 0)
goto error;
/* set a minimal environment */
setenv ("PATH", "/usr/sbin:/usr/bin:/sbin:/bin", 1);
/* check that we are setuid root */
if (geteuid () != 0)
{
fprintf (stderr, "polkit-grant-helper-pam: needs to be setuid root\n");
goto error;
}
openlog ("polkit-agent-helper-1", LOG_CONS | LOG_PID, LOG_AUTHPRIV);
/* check for correct invocation */
if (argc != 3)
{
syslog (LOG_NOTICE, "inappropriate use of helper, wrong number of arguments [uid=%d]", getuid ());
fprintf (stderr, "polkit-agent-helper-1: wrong number of arguments. This incident has been logged.\n");
goto error;
}
user_to_auth = argv[1];
cookie = argv[2];
if (getuid () != 0)
{
/* check we're running with a non-tty stdin */
if (isatty (STDIN_FILENO) != 0)
{
syslog (LOG_NOTICE, "inappropriate use of helper, stdin is a tty [uid=%d]", getuid ());
fprintf (stderr, "polkit-agent-helper-1: inappropriate use of helper, stdin is a tty. This incident has been logged.\n");
goto error;
}
}
#ifdef PAH_DEBUG
fprintf (stderr, "polkit-agent-helper-1: user to auth is '%s'.\n", user_to_auth);
#endif /* PAH_DEBUG */
pam_conversation.conv = conversation_function;
pam_conversation.appdata_ptr = NULL;
/* start the pam stack */
rc = pam_start ("polkit-1",
user_to_auth,
&pam_conversation,
&pam_h);
if (rc != PAM_SUCCESS)
{
fprintf (stderr, "polkit-agent-helper-1: pam_start failed: %s\n", pam_strerror (pam_h, rc));
goto error;
}
/* set the requesting user */
rc = pam_set_item (pam_h, PAM_RUSER, user_to_auth);
if (rc != PAM_SUCCESS)
{
fprintf (stderr, "polkit-agent-helper-1: pam_set_item failed: %s\n", pam_strerror (pam_h, rc));
goto error;
}
/* is user really user? */
rc = pam_authenticate (pam_h, 0);
if (rc != PAM_SUCCESS)
{
fprintf (stderr, "polkit-agent-helper-1: pam_authenticated failed: %s\n", pam_strerror (pam_h, rc));
goto error;
}
/* permitted access? */
rc = pam_acct_mgmt (pam_h, 0);
if (rc != PAM_SUCCESS)
{
fprintf (stderr, "polkit-agent-helper-1: pam_acct_mgmt failed: %s\n", pam_strerror (pam_h, rc));
goto error;
}
/* did we auth the right user? */
rc = pam_get_item (pam_h, PAM_USER, &authed_user);
if (rc != PAM_SUCCESS)
{
fprintf (stderr, "polkit-agent-helper-1: pam_get_item failed: %s\n", pam_strerror (pam_h, rc));
goto error;
}
if (strcmp (authed_user, user_to_auth) != 0)
{
fprintf (stderr, "polkit-agent-helper-1: Tried to auth user '%s' but we got auth for user '%s' instead",
user_to_auth, (const char *) authed_user);
goto error;
}
#ifdef PAH_DEBUG
fprintf (stderr, "polkit-agent-helper-1: successfully authenticated user '%s'.\n", user_to_auth);
#endif /* PAH_DEBUG */
/* TODO: now send a D-Bus message to the PolicyKit daemon that
* includes a) the cookie; and b) the user we authenticated
*/
fprintf (stdout, "SUCCESS\n");
fflush (stdout);
pam_end (pam_h, rc);
return 0;
error:
if (pam_h != NULL)
pam_end (pam_h, rc);
fprintf (stdout, "FAILURE\n");
fflush (stdout);
return 1;
}
static int
conversation_function (int n, const struct pam_message **msg, struct pam_response **resp, void *data)
{
struct pam_response *aresp;
char buf[PAM_MAX_RESP_SIZE];
int i;
data = data;
if (n <= 0 || n > PAM_MAX_NUM_MSG)
return PAM_CONV_ERR;
if ((aresp = calloc(n, sizeof *aresp)) == NULL)
return PAM_BUF_ERR;
for (i = 0; i < n; ++i)
{
aresp[i].resp_retcode = 0;
aresp[i].resp = NULL;
switch (msg[i]->msg_style)
{
case PAM_PROMPT_ECHO_OFF:
fprintf (stdout, "PAM_PROMPT_ECHO_OFF ");
goto conv1;
case PAM_PROMPT_ECHO_ON:
fprintf (stdout, "PAM_PROMPT_ECHO_ON ");
conv1:
fputs (msg[i]->msg, stdout);
if (strlen (msg[i]->msg) > 0 && msg[i]->msg[strlen (msg[i]->msg) - 1] != '\n')
fputc ('\n', stdout);
fflush (stdout);
if (fgets (buf, sizeof buf, stdin) == NULL)
goto error;
if (strlen (buf) > 0 &&
buf[strlen (buf) - 1] == '\n')
buf[strlen (buf) - 1] = '\0';
aresp[i].resp = strdup (buf);
if (aresp[i].resp == NULL)
goto error;
break;
case PAM_ERROR_MSG:
fprintf (stdout, "PAM_ERROR_MSG ");
goto conv2;
case PAM_TEXT_INFO:
fprintf (stdout, "PAM_TEXT_INFO ");
conv2:
fputs (msg[i]->msg, stdout);
if (strlen (msg[i]->msg) > 0 &&
msg[i]->msg[strlen (msg[i]->msg) - 1] != '\n')
fputc ('\n', stdout);
fflush (stdout);
break;
default:
goto error;
}
}
*resp = aresp;
return PAM_SUCCESS;
error:
for (i = 0; i < n; ++i)
{
if (aresp[i].resp != NULL) {
memset (aresp[i].resp, 0, strlen(aresp[i].resp));
free (aresp[i].resp);
}
}
memset (aresp, 0, n * sizeof *aresp);
*resp = NULL;
return PAM_CONV_ERR;
}
/*
* Copyright (C) 2008 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#ifndef __POLKIT_AGENT_TYPES_H
#define __POLKIT_AGENT_TYPES_H
#include <glib-object.h>
G_BEGIN_DECLS
struct _PolkitAuthenticationSession;
typedef struct _PolkitAuthenticationSession PolkitAuthenticationSession;
G_END_DECLS
#endif /* __POLKIT_AGENT_TYPES_H */
/*
* Copyright (C) 2008 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
/* TODO: This whole class needs to be rewritten so it uses the main loop etc. etc.
*
* And we REALLY REALLY really really should use signals instead of callbacks...
*/
/* for getline(), see below */
#define _GNU_SOURCE
#include "config.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <pwd.h>
#include "polkitauthenticationsession.h"
struct _PolkitAuthenticationSession
{
GObject parent_instance;
gchar *cookie;
PolkitIdentity *identity;
int child_stdin;
int child_stdout;
GPid child_pid;
FILE *child_stdout_f;
int child_watch_id;
int io_watch_id;
gboolean success;
gboolean helper_is_running;
PolkitAuthenticationSessionConversationPromptEchoOff func_prompt_echo_off;
PolkitAuthenticationSessionConversationPromptEchoOn func_prompt_echo_on;
PolkitAuthenticationSessionConversationErrorMessage func_error_message;
PolkitAuthenticationSessionConversationTextInfo func_text_info;
PolkitAuthenticationSessionDone func_done;
void *user_data;
};
struct _PolkitAuthenticationSessionClass
{
GObjectClass parent_class;
};
G_DEFINE_TYPE (PolkitAuthenticationSession, polkit_authentication_session, G_TYPE_OBJECT);
static void
polkit_authentication_session_init (PolkitAuthenticationSession *session)
{
}
static void
polkit_authentication_session_finalize (GObject *object)
{
PolkitAuthenticationSession *session;
session = POLKIT_AUTHENTICATION_SESSION (object);
g_free (session->cookie);
if (session->identity != NULL)
g_object_unref (session->identity);
if (G_OBJECT_CLASS (polkit_authentication_session_parent_class)->finalize != NULL)
G_OBJECT_CLASS (polkit_authentication_session_parent_class)->finalize (object);
}
static void
polkit_authentication_session_class_init (PolkitAuthenticationSessionClass *klass)
{
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = polkit_authentication_session_finalize;
}
PolkitAuthenticationSession *
polkit_authentication_session_new (PolkitIdentity *identity,
const gchar *cookie)
{
PolkitAuthenticationSession *session;
session = POLKIT_AUTHENTICATION_SESSION (g_object_new (POLKIT_TYPE_AUTHENTICATION_SESSION, NULL));
session->identity = g_object_ref (identity);
session->cookie = g_strdup (cookie);
return session;
}
void
polkit_authentication_session_set_functions (PolkitAuthenticationSession *session,
PolkitAuthenticationSessionConversationPromptEchoOff func_prompt_echo_off,
PolkitAuthenticationSessionConversationPromptEchoOn func_prompt_echo_on,
PolkitAuthenticationSessionConversationErrorMessage func_error_message,
PolkitAuthenticationSessionConversationTextInfo func_text_info,
PolkitAuthenticationSessionDone func_done,
void *user_data)
{
session->func_prompt_echo_off = func_prompt_echo_off;
session->func_prompt_echo_on = func_prompt_echo_on;
session->func_error_message = func_error_message;
session->func_text_info = func_text_info;
session->func_done = func_done;
session->user_data = user_data;
}
static void
child_watch_func (GPid pid, gint status, gpointer user_data)
{
PolkitAuthenticationSession *session = POLKIT_AUTHENTICATION_SESSION (user_data);
gint exit_code;
gboolean input_was_bogus;
g_return_if_fail (session->helper_is_running);
exit_code = WEXITSTATUS (status);
g_debug ("pid %d terminated", pid);
waitpid (pid, &status, 0);
if (exit_code >= 2)
input_was_bogus = TRUE;
else
input_was_bogus = FALSE;
session->success = (exit_code == 0);
session->helper_is_running = FALSE;
session->func_done (session, session->success, input_was_bogus, session->user_data);
}
static gboolean
io_watch_have_data (GIOChannel *channel, GIOCondition condition, gpointer user_data)
{
PolkitAuthenticationSession *session = POLKIT_AUTHENTICATION_SESSION (user_data);
char *line;
size_t line_len;
gchar *id;
size_t id_len;
gchar *response;
gchar *response_prefix;
int fd;
g_return_val_if_fail (session->helper_is_running, FALSE);
fd = g_io_channel_unix_get_fd (channel);
line = NULL;
line_len = 0;
/* TODO: getline is GNU only, see kit_getline() in old polkit */
while (getline (&line, &line_len, session->child_stdout_f) != -1)
{
if (strlen (line) > 0 && line[strlen (line) - 1] == '\n')
line[strlen (line) - 1] = '\0';
response = NULL;
response_prefix = NULL;
id = "PAM_PROMPT_ECHO_OFF ";
if (g_str_has_prefix (line, id))
{
id_len = strlen (id);
response_prefix = "";
response = session->func_prompt_echo_off (session,
line + id_len,
session->user_data);
goto processed;
}
id = "PAM_PROMPT_ECHO_ON ";
if (g_str_has_prefix (line, id))
{
id_len = strlen (id);
response_prefix = "";
response = session->func_prompt_echo_on (session,
line + id_len,
session->user_data);
goto processed;
}
id = "PAM_ERROR_MSG ";
if (g_str_has_prefix (line, id))
{
id_len = strlen (id);
session->func_error_message (session,
line + id_len,
session->user_data);
goto processed;
}
id = "PAM_TEXT_INFO ";
if (g_str_has_prefix (line, id))
{
id_len = strlen (id);
session->func_text_info (session,
line + id_len,
session->user_data);
goto processed;
}
processed:
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')
{
add_newline = TRUE;
}
buf = g_strdup_printf ("%s%s%c",
response_prefix,
response,