first cut at authentication helper library
authorDavid Zeuthen <davidz@redhat.com>
Tue, 20 Jan 2009 05:36:25 +0000 (00:36 -0500)
committerDavid Zeuthen <davidz@redhat.com>
Tue, 20 Jan 2009 05:36:25 +0000 (00:36 -0500)
Not finished yet...

src/Makefile.am
src/polkitagent/Makefile.am [new file with mode: 0644]
src/polkitagent/polkitagent.h [new file with mode: 0644]
src/polkitagent/polkitagenthelper.c [new file with mode: 0644]
src/polkitagent/polkitagenttypes.h [new file with mode: 0644]
src/polkitagent/polkitauthenticationsession.c [new file with mode: 0644]
src/polkitagent/polkitauthenticationsession.h [new file with mode: 0644]

index 7538232..33d3284 100644 (file)
@@ -1,5 +1,5 @@
 
-SUBDIRS = polkit polkitbackend polkitd programs
+SUBDIRS = polkit polkitbackend polkitagent polkitd programs
 
 clean-local :
        rm -f *~
diff --git a/src/polkitagent/Makefile.am b/src/polkitagent/Makefile.am
new file mode 100644 (file)
index 0000000..ac74bd5
--- /dev/null
@@ -0,0 +1,63 @@
+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 *~
diff --git a/src/polkitagent/polkitagent.h b/src/polkitagent/polkitagent.h
new file mode 100644 (file)
index 0000000..b6c2ffb
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * 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 */
diff --git a/src/polkitagent/polkitagenthelper.c b/src/polkitagent/polkitagenthelper.c
new file mode 100644 (file)
index 0000000..df59f18
--- /dev/null
@@ -0,0 +1,255 @@
+/*
+ * 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;
+}
diff --git a/src/polkitagent/polkitagenttypes.h b/src/polkitagent/polkitagenttypes.h
new file mode 100644 (file)
index 0000000..de62caa
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * 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 */
diff --git a/src/polkitagent/polkitauthenticationsession.c b/src/polkitagent/polkitauthenticationsession.c
new file mode 100644 (file)
index 0000000..62dcd7a
--- /dev/null
@@ -0,0 +1,354 @@
+/*
+ * 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,
+                                 add_newline ? '\n' : '\0');
+          write (session->child_stdin, buf, strlen (buf));
+          g_free (buf);
+          g_free (response);
+        }
+    }
+
+  if (line != NULL)
+    free (line);
+
+  return FALSE;
+}
+
+gboolean
+polkit_authentication_session_initiate_auth (PolkitAuthenticationSession *session)
+{
+  uid_t uid;
+  GError *error;
+  gchar *helper_argv[4];
+  GIOChannel *channel;
+  gboolean ret;
+  struct passwd *passwd;
+
+  ret = FALSE;
+
+  /* TODO: also support authorization for other kinds of identities */
+  if (!POLKIT_IS_UNIX_USER (session->identity))
+    {
+      g_warning ("Unsupported identity type");
+      goto error;
+    }
+
+  uid = polkit_unix_user_get_uid (POLKIT_UNIX_USER (session->identity));
+
+  passwd = getpwuid (uid);
+  if (passwd == NULL)
+    {
+      g_warning ("No user with uid %d", uid);
+      goto error;
+    }
+
+  helper_argv[0] = PACKAGE_LIBEXEC_DIR "/polkit-session-helper-1";
+  helper_argv[1] = passwd->pw_name;
+  helper_argv[2] = session->cookie;
+  helper_argv[3] = NULL;
+
+  session->child_stdin = -1;
+  session->child_stdout = -1;
+
+  error = NULL;
+  if (!g_spawn_async_with_pipes (NULL,
+                                 (char **) helper_argv,
+                                 NULL,
+                                 G_SPAWN_DO_NOT_REAP_CHILD |
+                                 0,//G_SPAWN_STDERR_TO_DEV_NULL,
+                                 NULL,
+                                 NULL,
+                                 &session->child_pid,
+                                 &session->child_stdin,
+                                 &session->child_stdout,
+                                 NULL,
+                                 &error))
+    {
+      g_warning ("Cannot spawn helper: %s\n", error->message);
+      g_error_free (error);
+      goto error;
+    }
+
+  session->child_watch_id = g_child_watch_add (session->child_pid, child_watch_func, session);
+
+  channel = g_io_channel_unix_new (session->child_stdout);
+  session->io_watch_id = g_io_add_watch (channel, G_IO_IN, io_watch_have_data, session);
+  g_io_channel_unref (channel);
+
+  /* so we can use getline... */
+  session->child_stdout_f = fdopen (session->child_stdout, "r");
+
+  session->success = FALSE;
+
+  session->helper_is_running = TRUE;
+
+  ret = TRUE;
+
+error:
+
+  return ret;
+}
+
+
+void
+polkit_authentication_session_cancel (PolkitAuthenticationSession *session)
+{
+  GPid pid;
+
+  g_return_if_fail (session->helper_is_running);
+
+  pid = session->child_pid;
+  session->child_pid = 0;
+  if (pid > 0)
+    {
+      int status;
+      kill (pid, SIGTERM);
+      waitpid (pid, &status, 0);
+      session->helper_is_running = FALSE;
+    }
+  session->func_done (session, FALSE, FALSE, session->user_data);
+}
diff --git a/src/polkitagent/polkitauthenticationsession.h b/src/polkitagent/polkitauthenticationsession.h
new file mode 100644 (file)
index 0000000..1ccc4ca
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * 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_AUTHENTICATION_SESSION_H
+#define __POLKIT_AUTHENTICATION_SESSION_H
+
+#include <polkit/polkit.h>
+#include <polkitagent/polkitagenttypes.h>
+
+G_BEGIN_DECLS
+
+#define POLKIT_TYPE_AUTHENTICATION_SESSION          (polkit_authentication_session_get_type())
+#define POLKIT_AUTHENTICATION_SESSION(o)            (G_TYPE_CHECK_INSTANCE_CAST ((o), POLKIT_TYPE_AUTHENTICATION_SESSION, PolkitAuthenticationSession))
+#define POLKIT_AUTHENTICATION_SESSION_CLASS(k)      (G_TYPE_CHECK_CLASS_CAST((k), POLKIT_TYPE_AUTHENTICATION_SESSION, PolkitAuthenticationSessionClass))
+#define POLKIT_AUTHENTICATION_SESSION_GET_CLASS(o)  (G_TYPE_INSTANCE_GET_CLASS ((o), POLKIT_TYPE_AUTHENTICATION_SESSION, PolkitAuthenticationSessionClass))
+#define POLKIT_IS_AUTHENTICATION_SESSION(o)         (G_TYPE_CHECK_INSTANCE_TYPE ((o), POLKIT_TYPE_AUTHENTICATION_SESSION))
+#define POLKIT_IS_AUTHENTICATION_SESSION_CLASS(k)   (G_TYPE_CHECK_CLASS_TYPE ((k), POLKIT_TYPE_AUTHENTICATION_SESSION))
+
+/**
+ * PolkitAuthenticationSessionConversationPromptEchoOff:
+ * @session: A #PolkitAuthenticationSession.
+ * @prompt: prompt passed by the authentication layer; do not free this string
+ * @user_data: user data pointer as passed into polkit_authorization_session_set_functions()
+ *
+ * Type for callback function that is invoked when the authentication
+ * layer needs to ask the user a secret and the UI should NOT echo what
+ * the user types on the screen.
+ *
+ * Returns: the answer obtained from the user; must be allocated with
+ * malloc(3) and will be freed by the #PolkitAuthenticationSession class.
+ **/
+typedef char* (*PolkitAuthenticationSessionConversationPromptEchoOff) (PolkitAuthenticationSession *session,
+                                                                       const gchar               *prompt,
+                                                                       gpointer                   user_data);
+
+/**
+ * PolkitAuthenticationSessionConversationPromptEchoOn:
+ * @session: A #PolkitAuthenticationSession.
+ * @prompt: prompt passed by the authentication layer; do not free this string
+ * @user_data: user data pointer as passed into polkit_authorization_session_set_functions()
+ *
+ * Type for callback function that is invoked when the authentication
+ * layer needs to ask the user a secret and the UI should echo what
+ * the user types on the screen.
+ *
+ * Returns: the answer obtained from the user; must be allocated with
+ * malloc(3) and will be freed by the #PolkitAuthenticationSession class.
+ **/
+typedef char* (*PolkitAuthenticationSessionConversationPromptEchoOn) (PolkitAuthenticationSession *session,
+                                                                      const gchar               *prompt,
+                                                                      gpointer                   user_data);
+
+/**
+ * PolkitAuthenticationSessionConversationErrorMessage:
+ * @session: A #PolkitAuthenticationSession.
+ * @error_message: error message passed by the authentication layer; do not free this string
+ * @user_data: user data pointer as passed into polkit_authorization_session_set_functions()
+ *
+ * Type for callback function that is invoked when the authentication
+ * layer produces an error message that should be displayed in the UI.
+ **/
+typedef void (*PolkitAuthenticationSessionConversationErrorMessage) (PolkitAuthenticationSession *session,
+                                                                     const gchar               *error_message,
+                                                                     gpointer                   user_data);
+
+/**
+ * PolkitAuthenticationSessionConversationTextInfo:
+ * @session: A #PolkitAuthenticationSession.
+ * @text_info: information passed by the authentication layer; do not free this string
+ * @user_data: user data pointer as passed into polkit_authorization_session_set_functions()
+ *
+ * Type for callback function that is invoked when the authentication
+ * layer produces an informational message that should be displayed in
+ * the UI.
+ **/
+typedef void (*PolkitAuthenticationSessionConversationTextInfo) (PolkitAuthenticationSession *session,
+                                                                 const gchar               *text_info,
+                                                                 gpointer                   user_data);
+
+/**
+ * PolkitAuthenticationSessionDone:
+ * @session: A #PolkitAuthenticationSession.
+ * @gained_authorization: whether the authorization was obtained
+ * @invalid_data: whether the input data was bogus (not including bad passwords)
+ * @user_data: user data pointer as passed into polkit_authorization_session_set_functions()
+ *
+ * This function is called when the granting process ends; either if
+ * successful or if it was canceled using e.g. polkit_authorization_session_cancel_auth().
+ **/
+typedef void (*PolkitAuthenticationSessionDone) (PolkitAuthenticationSession *session,
+                                                 gboolean                   gained_authorization,
+                                                 gboolean                   invalid_data,
+                                                 gpointer                   user_data);
+
+
+#if 0
+typedef struct _PolkitAuthenticationSession PolkitAuthenticationSession;
+#endif
+typedef struct _PolkitAuthenticationSessionClass PolkitAuthenticationSessionClass;
+
+GType                      polkit_authentication_session_get_type         (void) G_GNUC_CONST;
+PolkitAuthenticationSession *polkit_authentication_session_new              (PolkitIdentity            *identity,
+                                                                         const gchar               *cookie);
+
+/* TODO: would be much nicer to use signals here */
+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);
+
+gboolean                   polkit_authentication_session_initiate_auth    (PolkitAuthenticationSession  *session);
+
+void                       polkit_authentication_session_cancel           (PolkitAuthenticationSession  *session);
+
+G_END_DECLS
+
+#endif /* __POLKIT_AUTHENTICATION_SESSION_H */