summaryrefslogtreecommitdiff
authorNikki VonHollen <vonhollen@google.com>2011-12-20 23:11:23 (GMT)
committer David Zeuthen <davidz@redhat.com>2011-12-22 22:59:33 (GMT)
commit674357c20c1b6cb421fea6eb6924b274ec477c0e (patch) (side-by-side diff)
treef434c0d7f0e0d5d932ce6e8b27fad31005b92d5f
parent15d2e90a54009efb31300d8b59292d71ce98a5b2 (diff)
downloadpolkit-674357c20c1b6cb421fea6eb6924b274ec477c0e.zip
polkit-674357c20c1b6cb421fea6eb6924b274ec477c0e.tar.gz
Bug 43610 - Add netgroup support
https://bugs.freedesktop.org/show_bug.cgi?id=43610 Added netgroup support and additional unit tests with MockLibc support. Signed-off-by: David Zeuthen <davidz@redhat.com>
Diffstat (more/less context) (ignore whitespace changes)
-rwxr-xr-xautogen.sh13
-rw-r--r--configure.ac3
-rw-r--r--docs/man/pklocalauthority.xml9
-rw-r--r--docs/polkit/polkit-1-docs.xml1
-rw-r--r--docs/polkit/polkit-1-sections.txt18
-rw-r--r--docs/polkit/polkit-1.types1
-rw-r--r--src/polkit/Makefile.am2
-rw-r--r--src/polkit/polkit.h1
-rw-r--r--src/polkit/polkitidentity.c26
-rw-r--r--src/polkit/polkittypes.h3
-rw-r--r--src/polkit/polkitunixnetgroup.c242
-rw-r--r--src/polkit/polkitunixnetgroup.h58
-rw-r--r--src/polkit/polkitunixuser.c46
-rw-r--r--src/polkit/polkitunixuser.h1
-rw-r--r--src/polkitbackend/polkitbackendlocalauthority.c63
-rw-r--r--src/polkitbackend/polkitbackendlocalauthorizationstore.c34
-rw-r--r--test/Makefile.am21
-rw-r--r--test/data/etc/group7
-rw-r--r--test/data/etc/netgroup5
-rw-r--r--test/data/etc/passwd5
-rw-r--r--test/data/etc/polkit-1/localauthority.conf.d/10-test.conf2
-rw-r--r--test/data/etc/polkit-1/localauthority/10-test/com.example.pkla14
-rw-r--r--test/data/var/lib/polkit-1/localauthority/10-test/com.example.pkla (renamed from test/polkitbackend/data/authstore2/10-test/com.example.pkla)0
-rw-r--r--test/mocklibc/AUTHORS1
-rw-r--r--test/mocklibc/COPYING202
-rw-r--r--test/mocklibc/ChangeLog10
-rw-r--r--test/mocklibc/INSTALL365
-rw-r--r--test/mocklibc/Makefile.am3
-rw-r--r--test/mocklibc/NEWS0
-rw-r--r--test/mocklibc/README121
-rw-r--r--test/mocklibc/bin/Makefile.am25
-rw-r--r--test/mocklibc/bin/mocklibc-test.in136
-rw-r--r--test/mocklibc/bin/mocklibc.in34
-rw-r--r--test/mocklibc/configure.ac38
-rw-r--r--test/mocklibc/example/group4
-rw-r--r--test/mocklibc/example/netgroup5
-rw-r--r--test/mocklibc/example/passwd3
-rw-r--r--test/mocklibc/src/Makefile.am8
-rw-r--r--test/mocklibc/src/grp.c156
-rw-r--r--test/mocklibc/src/netdb.c100
-rw-r--r--test/mocklibc/src/netgroup-debug.c84
-rw-r--r--test/mocklibc/src/netgroup-debug.h58
-rw-r--r--test/mocklibc/src/netgroup.c342
-rw-r--r--test/mocklibc/src/netgroup.h144
-rw-r--r--test/mocklibc/src/pwd.c99
-rw-r--r--test/polkit/Makefile.am6
-rw-r--r--test/polkit/polkitidentitytest.c184
-rw-r--r--test/polkit/polkitunixnetgrouptest.c76
-rw-r--r--test/polkit/polkitunixusertest.c50
-rw-r--r--test/polkitbackend/Makefile.am2
-rw-r--r--test/polkitbackend/data/authstore1/10-test/com.example.pkla6
-rw-r--r--test/polkitbackend/polkitbackendlocalauthoritytest.c120
-rw-r--r--test/polkitbackend/polkitbackendlocalauthorizationstoretest.c28
-rw-r--r--test/polkittesthelper.c20
-rw-r--r--test/polkittesthelper.h2
55 files changed, 2889 insertions, 118 deletions
diff --git a/autogen.sh b/autogen.sh
index 4d25597..462fc35 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -57,6 +57,16 @@ test -n "$NO_AUTOMAKE" || (aclocal --version) < /dev/null > /dev/null 2>&1 || {
DIE=1
}
+
+# if no automake, don't bother testing for autoreconf
+test -n "$NO_AUTOMAKE" || (autoreconf --version) < /dev/null > /dev/null 2>&1 || {
+ echo
+ echo "**Error**: You must have autoreconf installed."
+ echo "You can get autoreconf from ..."
+ DIE=1
+}
+
+
if test "$DIE" -eq 1; then
exit 1
fi
@@ -75,6 +85,9 @@ esac
aclocalinclude="$ACLOCAL_FLAGS"
+ echo "Running autoreconf on test/mocklibc ..."
+ (cd "test/mocklibc"; autoreconf --install)
+
if grep "^AM_PROG_LIBTOOL" configure.ac >/dev/null; then
if test -z "$NO_LIBTOOLIZE" ; then
echo "Running libtoolize..."
diff --git a/configure.ac b/configure.ac
index 89f48ca..2ed8401 100644
--- a/configure.ac
+++ b/configure.ac
@@ -8,6 +8,9 @@ AM_MAINTAINER_MODE
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+# Include external mocklibc tool for unit testing
+AC_CONFIG_SUBDIRS([test/mocklibc])
+
# libtool versioning - this applies to all libraries in this package
#
# See http://sources.redhat.com/autobook/autobook/autobook_91.html#SEC91 for details
diff --git a/docs/man/pklocalauthority.xml b/docs/man/pklocalauthority.xml
index 02e3f4e..a03a434 100644
--- a/docs/man/pklocalauthority.xml
+++ b/docs/man/pklocalauthority.xml
@@ -80,8 +80,9 @@
this key is a semi-colon separated list of identities that can
be used when administrator authentication is required. Users are
specified by prefixing the user name with
- <literal>unix-user:</literal> and groups of users are specified
- by prefixing with <literal>unix-group:</literal>. See
+ <literal>unix-user:</literal>, groups of users are specified by
+ prefixing with <literal>unix-group:</literal>, and netgroups of
+ users are specified with <literal>unix-netgroup:</literal>. See
<xref linkend="pklocalauthority-examples"/> for an example of a
configuration file.
</para>
@@ -208,7 +209,9 @@
A semi-colon separated list of globs to match identities. Each glob
should start with <literal>unix-user:</literal> or
<literal>unix-group:</literal> to specify whether to match on a
- UNIX user name or a UNIX group name.
+ UNIX user name or a UNIX group name. Netgroups are supported with
+ the <literal>unix-netgroup:</literal> prefix, but cannot support
+ glob syntax.
</para>
</listitem>
</varlistentry>
diff --git a/docs/polkit/polkit-1-docs.xml b/docs/polkit/polkit-1-docs.xml
index 22092d9..38a69a4 100644
--- a/docs/polkit/polkit-1-docs.xml
+++ b/docs/polkit/polkit-1-docs.xml
@@ -86,6 +86,7 @@
<xi:include href="xml/polkitidentity.xml"/>
<xi:include href="xml/polkitunixuser.xml"/>
<xi:include href="xml/polkitunixgroup.xml"/>
+ <xi:include href="xml/polkitunixnetgroup.xml"/>
</chapter>
</part>
diff --git a/docs/polkit/polkit-1-sections.txt b/docs/polkit/polkit-1-sections.txt
index 9f4fcf8..ff6a301 100644
--- a/docs/polkit/polkit-1-sections.txt
+++ b/docs/polkit/polkit-1-sections.txt
@@ -5,6 +5,7 @@ polkit_unix_user_new
polkit_unix_user_new_for_name
polkit_unix_user_get_uid
polkit_unix_user_set_uid
+polkit_unix_user_get_name
<SUBSECTION Standard>
PolkitUnixUserClass
POLKIT_UNIX_USER
@@ -121,6 +122,23 @@ POLKIT_UNIX_GROUP_GET_CLASS
</SECTION>
<SECTION>
+<FILE>polkitunixnetgroup</FILE>
+PolkitUnixNetgroup
+polkit_unix_netgroup_new
+polkit_unix_netgroup_get_name
+polkit_unix_netgroup_set_name
+<SUBSECTION Standard>
+PolkitUnixNetgroupClass
+POLKIT_UNIX_NETGROUP
+POLKIT_IS_UNIX_NETGROUP
+POLKIT_TYPE_UNIX_NETGROUP
+polkit_unix_netgroup_get_type
+POLKIT_UNIX_NETGROUP_CLASS
+POLKIT_IS_UNIX_NETGROUP_CLASS
+POLKIT_UNIX_NETGROUP_GET_CLASS
+</SECTION>
+
+<SECTION>
<FILE>polkitunixsession</FILE>
PolkitUnixSession
polkit_unix_session_new
diff --git a/docs/polkit/polkit-1.types b/docs/polkit/polkit-1.types
index e50812b..b1e13cc 100644
--- a/docs/polkit/polkit-1.types
+++ b/docs/polkit/polkit-1.types
@@ -6,6 +6,7 @@ polkit_implicit_authorization_get_type
polkit_identity_get_type
polkit_unix_user_get_type
polkit_unix_group_get_type
+polkit_unix_netgroup_get_type
polkit_subject_get_type
polkit_unix_process_get_type
polkit_unix_session_get_type
diff --git a/src/polkit/Makefile.am b/src/polkit/Makefile.am
index 9d7c4ce..6c5a586 100644
--- a/src/polkit/Makefile.am
+++ b/src/polkit/Makefile.am
@@ -51,6 +51,7 @@ libpolkit_gobject_1include_HEADERS = \
polkitidentity.h \
polkitunixuser.h \
polkitunixgroup.h \
+ polkitunixnetgroup.h \
polkitauthorizationresult.h \
polkitcheckauthorizationflags.h \
polkitimplicitauthorization.h \
@@ -73,6 +74,7 @@ libpolkit_gobject_1_la_SOURCES = \
polkitidentity.c polkitidentity.h \
polkitunixuser.c polkitunixuser.h \
polkitunixgroup.c polkitunixgroup.h \
+ polkitunixnetgroup.c polkitunixnetgroup.h \
polkitauthorizationresult.c polkitauthorizationresult.h \
polkitcheckauthorizationflags.c polkitcheckauthorizationflags.h \
polkitimplicitauthorization.c polkitimplicitauthorization.h \
diff --git a/src/polkit/polkit.h b/src/polkit/polkit.h
index f677ca1..bfe4c7d 100644
--- a/src/polkit/polkit.h
+++ b/src/polkit/polkit.h
@@ -33,6 +33,7 @@
#include <polkit/polkitidentity.h>
#include <polkit/polkitunixuser.h>
#include <polkit/polkitunixgroup.h>
+#include <polkit/polkitunixnetgroup.h>
#include <polkit/polkitsubject.h>
#include <polkit/polkitunixprocess.h>
#include <polkit/polkitunixsession.h>
diff --git a/src/polkit/polkitidentity.c b/src/polkit/polkitidentity.c
index 21c326b..dd15b2f 100644
--- a/src/polkit/polkitidentity.c
+++ b/src/polkit/polkitidentity.c
@@ -28,6 +28,7 @@
#include "polkitidentity.h"
#include "polkitunixuser.h"
#include "polkitunixgroup.h"
+#include "polkitunixnetgroup.h"
#include "polkiterror.h"
#include "polkitprivate.h"
@@ -177,6 +178,10 @@ polkit_identity_from_string (const gchar *str,
identity = polkit_unix_group_new_for_name (str + sizeof "unix-group:" - 1,
error);
}
+ else if (g_str_has_prefix (str, "unix-netgroup:"))
+ {
+ identity = polkit_unix_netgroup_new (str + sizeof "unix-netgroup:" - 1);
+ }
if (identity == NULL && (error != NULL && *error == NULL))
{
@@ -214,6 +219,12 @@ polkit_identity_to_gvariant (PolkitIdentity *identity)
g_variant_builder_add (&builder, "{sv}", "gid",
g_variant_new_uint32 (polkit_unix_group_get_gid (POLKIT_UNIX_GROUP (identity))));
}
+ else if (POLKIT_IS_UNIX_NETGROUP (identity))
+ {
+ kind = "unix-netgroup";
+ g_variant_builder_add (&builder, "{sv}", "name",
+ g_variant_new_string (polkit_unix_netgroup_get_name (POLKIT_UNIX_NETGROUP (identity))));
+ }
else
{
g_warning ("Unknown class %s implementing PolkitIdentity", g_type_name (G_TYPE_FROM_INSTANCE (identity)));
@@ -326,6 +337,21 @@ polkit_identity_new_for_gvariant (GVariant *variant,
ret = polkit_unix_group_new (gid);
}
+ else if (g_strcmp0 (kind, "unix-netgroup") == 0)
+ {
+ GVariant *v;
+ const char *name;
+
+ v = lookup_asv (details_gvariant, "name", G_VARIANT_TYPE_STRING, error);
+ if (v == NULL)
+ {
+ g_prefix_error (error, "Error parsing net identity: ");
+ goto out;
+ }
+ name = g_variant_get_string (v, NULL);
+ ret = polkit_unix_netgroup_new (name);
+ g_variant_unref (v);
+ }
else
{
g_set_error (error,
diff --git a/src/polkit/polkittypes.h b/src/polkit/polkittypes.h
index 636b418..3de1778 100644
--- a/src/polkit/polkittypes.h
+++ b/src/polkit/polkittypes.h
@@ -49,6 +49,9 @@ typedef struct _PolkitUnixUser PolkitUnixUser;
struct _PolkitUnixGroup;
typedef struct _PolkitUnixGroup PolkitUnixGroup;
+struct _PolkitUnixNetgroup;
+typedef struct _PolkitUnixNetgroup PolkitUnixNetgroup;
+
struct _PolkitAuthorizationResult;
typedef struct _PolkitAuthorizationResult PolkitAuthorizationResult;
diff --git a/src/polkit/polkitunixnetgroup.c b/src/polkit/polkitunixnetgroup.c
new file mode 100644
index 0000000..e4438eb
--- a/dev/null
+++ b/src/polkit/polkitunixnetgroup.c
@@ -0,0 +1,242 @@
+/*
+ * 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>
+ * Author: Nikki VonHollen <vonhollen@google.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <string.h>
+#include <errno.h>
+#include "polkitunixnetgroup.h"
+#include "polkitidentity.h"
+#include "polkiterror.h"
+#include "polkitprivate.h"
+
+/**
+ * SECTION:polkitunixnetgroup
+ * @title: PolkitUnixNetgroup
+ * @short_description: Unix netgroups
+ *
+ * An object representing a netgroup identity on a UNIX system.
+ */
+
+/**
+ * PolkitUnixNetgroup:
+ *
+ * The #PolkitUnixNetgroup struct should not be accessed directly.
+ */
+struct _PolkitUnixNetgroup
+{
+ GObject parent_instance;
+
+ gchar *name;
+};
+
+struct _PolkitUnixNetgroupClass
+{
+ GObjectClass parent_class;
+};
+
+enum
+{
+ PROP_0,
+ PROP_NAME,
+};
+
+static void identity_iface_init (PolkitIdentityIface *identity_iface);
+
+G_DEFINE_TYPE_WITH_CODE (PolkitUnixNetgroup, polkit_unix_netgroup, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (POLKIT_TYPE_IDENTITY, identity_iface_init)
+ );
+
+static void
+polkit_unix_netgroup_init (PolkitUnixNetgroup *net_group)
+{
+ net_group->name = NULL;
+}
+
+static void
+polkit_unix_netgroup_finalize (GObject *object)
+{
+ PolkitUnixNetgroup *net_group = POLKIT_UNIX_NETGROUP (object);
+
+ g_free(net_group->name);
+
+ G_OBJECT_CLASS (polkit_unix_netgroup_parent_class)->finalize (object);
+}
+
+static void
+polkit_unix_netgroup_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ PolkitUnixNetgroup *net_group = POLKIT_UNIX_NETGROUP (object);
+
+ switch (prop_id)
+ {
+ case PROP_NAME:
+ g_value_set_string (value, polkit_unix_netgroup_get_name (net_group));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+polkit_unix_netgroup_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ PolkitUnixNetgroup *net_group = POLKIT_UNIX_NETGROUP (object);
+
+ switch (prop_id)
+ {
+ case PROP_NAME:
+ polkit_unix_netgroup_set_name (net_group, g_value_get_string (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+polkit_unix_netgroup_class_init (PolkitUnixNetgroupClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->finalize = polkit_unix_netgroup_finalize;
+ gobject_class->get_property = polkit_unix_netgroup_get_property;
+ gobject_class->set_property = polkit_unix_netgroup_set_property;
+
+ /**
+ * PolkitUnixNetgroup:name:
+ *
+ * The NIS netgroup name.
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_NAME,
+ g_param_spec_string ("name",
+ "Group Name",
+ "The NIS netgroup name",
+ NULL,
+ G_PARAM_CONSTRUCT |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB |
+ G_PARAM_STATIC_NICK));
+
+}
+
+/**
+ * polkit_unix_netgroup_get_name:
+ * @group: A #PolkitUnixNetgroup.
+ *
+ * Gets the netgroup name for @group.
+ *
+ * Returns: A netgroup name string.
+ */
+const gchar *
+polkit_unix_netgroup_get_name (PolkitUnixNetgroup *group)
+{
+ g_return_val_if_fail (POLKIT_IS_UNIX_NETGROUP (group), NULL);
+ return group->name;
+}
+
+/**
+ * polkit_unix_netgroup_set_gid:
+ * @group: A #PolkitUnixNetgroup.
+ * @name: A netgroup name.
+ *
+ * Sets @name for @group.
+ */
+void
+polkit_unix_netgroup_set_name (PolkitUnixNetgroup *group,
+ const gchar * name)
+{
+ g_return_if_fail (POLKIT_IS_UNIX_NETGROUP (group));
+ g_free(group->name);
+ group->name = g_strdup(name);
+}
+
+/**
+ * polkit_unix_netgroup_new:
+ * @name: A netgroup name.
+ *
+ * Creates a new #PolkitUnixNetgroup object for @name.
+ *
+ * Returns: (transfer full): A #PolkitUnixNetgroup object. Free with g_object_unref().
+ */
+PolkitIdentity *
+polkit_unix_netgroup_new (const gchar *name)
+{
+ g_return_val_if_fail (name != NULL, NULL);
+ return POLKIT_IDENTITY (g_object_new (POLKIT_TYPE_UNIX_NETGROUP,
+ "name", name,
+ NULL));
+}
+
+static guint
+polkit_unix_netgroup_hash (PolkitIdentity *identity)
+{
+ PolkitUnixNetgroup *group;
+
+ group = POLKIT_UNIX_NETGROUP (identity);
+
+ return g_str_hash(group->name);
+}
+
+static gboolean
+polkit_unix_netgroup_equal (PolkitIdentity *a,
+ PolkitIdentity *b)
+{
+ PolkitUnixNetgroup *group_a;
+ PolkitUnixNetgroup *group_b;
+
+ group_a = POLKIT_UNIX_NETGROUP (a);
+ group_b = POLKIT_UNIX_NETGROUP (b);
+
+ if (g_strcmp0(group_a->name, group_b->name) == 0)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+static gchar *
+polkit_unix_netgroup_to_string (PolkitIdentity *identity)
+{
+ PolkitUnixNetgroup *group = POLKIT_UNIX_NETGROUP (identity);
+ return g_strconcat("unix-netgroup:", group->name, NULL);
+}
+
+static void
+identity_iface_init (PolkitIdentityIface *identity_iface)
+{
+ identity_iface->hash = polkit_unix_netgroup_hash;
+ identity_iface->equal = polkit_unix_netgroup_equal;
+ identity_iface->to_string = polkit_unix_netgroup_to_string;
+}
diff --git a/src/polkit/polkitunixnetgroup.h b/src/polkit/polkitunixnetgroup.h
new file mode 100644
index 0000000..873d428
--- a/dev/null
+++ b/src/polkit/polkitunixnetgroup.h
@@ -0,0 +1,58 @@
+/*
+ * 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>
+ * Author: Nikki VonHollen <vonhollen@google.com>
+ */
+
+#if !defined (_POLKIT_COMPILATION) && !defined(_POLKIT_INSIDE_POLKIT_H)
+#error "Only <polkit/polkit.h> can be included directly, this file may disappear or change contents."
+#endif
+
+#ifndef __POLKIT_UNIX_NETGROUP_H
+#define __POLKIT_UNIX_NETGROUP_H
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <glib-object.h>
+#include <gio/gio.h>
+#include <polkit/polkittypes.h>
+
+G_BEGIN_DECLS
+
+#define POLKIT_TYPE_UNIX_NETGROUP (polkit_unix_netgroup_get_type())
+#define POLKIT_UNIX_NETGROUP(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), POLKIT_TYPE_UNIX_NETGROUP, PolkitUnixNetgroup))
+#define POLKIT_UNIX_NETGROUP_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), POLKIT_TYPE_UNIX_NETGROUP, PolkitUnixNetgroupClass))
+#define POLKIT_UNIX_NETGROUP_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), POLKIT_TYPE_UNIX_NETGROUP, PolkitUnixNetgroupClass))
+#define POLKIT_IS_UNIX_NETGROUP(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), POLKIT_TYPE_UNIX_NETGROUP))
+#define POLKIT_IS_UNIX_NETGROUP_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), POLKIT_TYPE_UNIX_NETGROUP))
+
+#if 0
+typedef struct _PolkitUnixNetgroup PolkitUnixNetgroup;
+#endif
+typedef struct _PolkitUnixNetgroupClass PolkitUnixNetgroupClass;
+
+GType polkit_unix_netgroup_get_type (void) G_GNUC_CONST;
+PolkitIdentity *polkit_unix_netgroup_new (const gchar *name);
+const gchar *polkit_unix_netgroup_get_name (PolkitUnixNetgroup *group);
+void polkit_unix_netgroup_set_name (PolkitUnixNetgroup *group,
+ const gchar *name);
+
+G_END_DECLS
+
+#endif /* __POLKIT_UNIX_NETGROUP_H */
diff --git a/src/polkit/polkitunixuser.c b/src/polkit/polkitunixuser.c
index 1c9cf49..8bfd3a1 100644
--- a/src/polkit/polkitunixuser.c
+++ b/src/polkit/polkitunixuser.c
@@ -49,6 +49,7 @@ struct _PolkitUnixUser
GObject parent_instance;
gint uid;
+ gchar *name;
};
struct _PolkitUnixUserClass
@@ -71,6 +72,17 @@ G_DEFINE_TYPE_WITH_CODE (PolkitUnixUser, polkit_unix_user, G_TYPE_OBJECT,
static void
polkit_unix_user_init (PolkitUnixUser *unix_user)
{
+ unix_user->name = NULL;
+}
+
+static void
+polkit_unix_user_finalize (GObject *object)
+{
+ PolkitUnixUser *unix_user = POLKIT_UNIX_USER (object);
+
+ g_free(unix_user->name);
+
+ G_OBJECT_CLASS (polkit_unix_user_parent_class)->finalize (object);
}
static void
@@ -118,6 +130,7 @@ polkit_unix_user_class_init (PolkitUnixUserClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ gobject_class->finalize = polkit_unix_user_finalize;
gobject_class->get_property = polkit_unix_user_get_property;
gobject_class->set_property = polkit_unix_user_set_property;
@@ -228,6 +241,29 @@ polkit_unix_user_new_for_name (const gchar *name,
return identity;
}
+/**
+ * polkit_unix_user_get_name:
+ * @user: A #PolkitUnixUser.
+ *
+ * Get the user's name.
+ *
+ * Returns: (allow-none) (transfer none): User name string or %NULL if user uid not found.
+ */
+const gchar *
+polkit_unix_user_get_name (PolkitUnixUser *user)
+{
+ if (user->name == NULL)
+ {
+ struct passwd *passwd;
+ passwd = getpwuid (user->uid);
+
+ if (passwd != NULL)
+ user->name = g_strdup(passwd->pw_name);
+ }
+
+ return user->name;
+}
+
static gboolean
polkit_unix_user_equal (PolkitIdentity *a,
PolkitIdentity *b)
@@ -255,14 +291,12 @@ static gchar *
polkit_unix_user_to_string (PolkitIdentity *identity)
{
PolkitUnixUser *user = POLKIT_UNIX_USER (identity);
- struct passwd *passwd;
-
- passwd = getpwuid (user->uid);
+ const gchar *user_name = polkit_unix_user_get_name(user);
- if (passwd == NULL)
- return g_strdup_printf ("unix-user:%d", user->uid);
+ if (user_name != NULL)
+ return g_strdup_printf ("unix-user:%s", user_name);
else
- return g_strdup_printf ("unix-user:%s", passwd->pw_name);
+ return g_strdup_printf ("unix-user:%d", user->uid);
}
static void
diff --git a/src/polkit/polkitunixuser.h b/src/polkit/polkitunixuser.h
index 8d4a9e1..2f227d4 100644
--- a/src/polkit/polkitunixuser.h
+++ b/src/polkit/polkitunixuser.h
@@ -53,6 +53,7 @@ PolkitIdentity *polkit_unix_user_new_for_name (const gchar *name,
gint polkit_unix_user_get_uid (PolkitUnixUser *user);
void polkit_unix_user_set_uid (PolkitUnixUser *user,
gint uid);
+const gchar *polkit_unix_user_get_name (PolkitUnixUser *user);
G_END_DECLS
diff --git a/src/polkitbackend/polkitbackendlocalauthority.c b/src/polkitbackend/polkitbackendlocalauthority.c
index 0f3cd65..b53eda3 100644
--- a/src/polkitbackend/polkitbackendlocalauthority.c
+++ b/src/polkitbackend/polkitbackendlocalauthority.c
@@ -23,6 +23,7 @@
#include <errno.h>
#include <pwd.h>
#include <grp.h>
+#include <netdb.h>
#include <string.h>
#include <glib/gstdio.h>
#include <locale.h>
@@ -52,6 +53,9 @@
static GList *get_users_in_group (PolkitIdentity *group,
gboolean include_root);
+static GList *get_users_in_net_group (PolkitIdentity *group,
+ gboolean include_root);
+
static GList *get_groups_for_user (PolkitIdentity *user);
/* ---------------------------------------------------------------------------------------------------- */
@@ -507,6 +511,10 @@ polkit_backend_local_authority_get_admin_auth_identities (PolkitBackendInteracti
{
ret = g_list_concat (ret, get_users_in_group (identity, FALSE));
}
+ else if (POLKIT_IS_UNIX_NETGROUP (identity))
+ {
+ ret = g_list_concat (ret, get_users_in_net_group (identity, FALSE));
+ }
else
{
g_warning ("Unsupported identity %s", admin_identities[n]);
@@ -660,7 +668,7 @@ get_users_in_group (PolkitIdentity *group,
PolkitIdentity *user;
GError *error;
- if (!include_root && strcmp (grp->gr_mem[n], "root") == 0)
+ if (!include_root && g_strcmp0 (grp->gr_mem[n], "root") == 0)
continue;
error = NULL;
@@ -683,6 +691,59 @@ get_users_in_group (PolkitIdentity *group,
}
static GList *
+get_users_in_net_group (PolkitIdentity *group,
+ gboolean include_root)
+{
+ const gchar *name;
+ GList *ret;
+
+ ret = NULL;
+ name = polkit_unix_netgroup_get_name (POLKIT_UNIX_NETGROUP (group));
+
+ if (setnetgrent (name) == 0)
+ {
+ g_warning ("Error looking up net group with name %s: %s", name, g_strerror (errno));
+ goto out;
+ }
+
+ for (;;)
+ {
+ char *hostname, *username, *domainname;
+ PolkitIdentity *user;
+ GError *error = NULL;
+
+ if (getnetgrent (&hostname, &username, &domainname) == 0)
+ break;
+
+ /* Skip NULL entries since we never want to make everyone an admin
+ * Skip "-" entries which mean "no match ever" in netgroup land */
+ if (username == NULL || g_strcmp0 (username, "-") == 0)
+ continue;
+
+ /* TODO: Should we match on hostname? Maybe only allow "-" as a hostname
+ * for safety. */
+
+ user = polkit_unix_user_new_for_name (username, &error);
+ if (user == NULL)
+ {
+ g_warning ("Unknown username '%s' in unix-netgroup: %s", username, error->message);
+ g_error_free (error);
+ }
+ else
+ {
+ ret = g_list_prepend (ret, user);
+ }
+ }
+
+ ret = g_list_reverse (ret);
+
+ out:
+ endnetgrent ();
+ return ret;
+}
+
+
+static GList *
get_groups_for_user (PolkitIdentity *user)
{
uid_t uid;
diff --git a/src/polkitbackend/polkitbackendlocalauthorizationstore.c b/src/polkitbackend/polkitbackendlocalauthorizationstore.c
index d10121f..2ddfe75 100644
--- a/src/polkitbackend/polkitbackendlocalauthorizationstore.c
+++ b/src/polkitbackend/polkitbackendlocalauthorizationstore.c
@@ -21,6 +21,7 @@
#include "config.h"
+#include <netdb.h>
#include <string.h>
#include <polkit/polkit.h>
#include "polkitbackendlocalauthorizationstore.h"
@@ -74,7 +75,12 @@ typedef struct
{
gchar *id;
+ /* Identities with glob support */
GList *identity_specs;
+
+ /* Netgroup identity strings, which can not support glob syntax */
+ GList *netgroup_identities;
+
GList *action_specs;
PolkitImplicitAuthorization result_any;
@@ -90,6 +96,7 @@ local_authorization_free (LocalAuthorization *authorization)
g_free (authorization->id);
g_list_foreach (authorization->identity_specs, (GFunc) g_pattern_spec_free, NULL);
g_list_free (authorization->identity_specs);
+ g_list_free_full (authorization->netgroup_identities, g_free);
g_list_foreach (authorization->action_specs, (GFunc) g_pattern_spec_free, NULL);
g_list_free (authorization->action_specs);
if (authorization->return_value != NULL)
@@ -135,8 +142,13 @@ local_authorization_new (GKeyFile *key_file,
}
for (n = 0; identity_strings[n] != NULL; n++)
{
- authorization->identity_specs = g_list_prepend (authorization->identity_specs,
- g_pattern_spec_new (identity_strings[n]));
+ /* Put netgroup entries in a seperate list from other identities who support glob syntax */
+ if (g_str_has_prefix (identity_strings[n], "unix-netgroup:"))
+ authorization->netgroup_identities = g_list_prepend (authorization->netgroup_identities,
+ g_strdup (identity_strings[n] + sizeof "unix-netgroup:" - 1));
+ else
+ authorization->identity_specs = g_list_prepend (authorization->identity_specs,
+ g_pattern_spec_new (identity_strings[n]));
}
action_strings = g_key_file_get_string_list (key_file,
@@ -704,7 +716,7 @@ polkit_backend_local_authorization_store_lookup (PolkitBackendLocalAuthorization
if (ll == NULL)
continue;
- /* then match the identity */
+ /* then match the identity against identity specs */
if (identity_string == NULL)
identity_string = polkit_identity_to_string (identity);
for (ll = authorization->identity_specs; ll != NULL; ll = ll->next)
@@ -712,6 +724,22 @@ polkit_backend_local_authorization_store_lookup (PolkitBackendLocalAuthorization
if (g_pattern_match_string ((GPatternSpec *) ll->data, identity_string))
break;
}
+
+ /* if no identity specs matched and identity is a user, match against netgroups */
+ if (ll == NULL && POLKIT_IS_UNIX_USER (identity))
+ {
+ PolkitUnixUser *user_identity = POLKIT_UNIX_USER (identity);
+ const gchar *user_name = polkit_unix_user_get_name (user_identity);
+ if (!user_name)
+ continue;
+
+ for (ll = authorization->netgroup_identities; ll != NULL; ll = ll->next)
+ {
+ if (innetgr ((const gchar *) ll->data, NULL, user_name, NULL))
+ break;
+ }
+ }
+
if (ll == NULL)
continue;
diff --git a/test/Makefile.am b/test/Makefile.am
index 9927eab..8426977 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -1,11 +1,30 @@
-SUBDIRS = . polkit polkitbackend
+SUBDIRS = mocklibc . polkit polkitbackend
AM_CFLAGS = $(GLIB_CFLAGS)
check_LTLIBRARIES = libpolkit-test-helper.la
libpolkit_test_helper_la_SOURCES = polkittesthelper.c polkittesthelper.h
libpolkit_test_helper_la_LIBADD = $(GLIB_LIBS)
+EXTRA_DIST = data
+
+# Use mocklibc to override NSS services for tests
+export MOCK_PASSWD := $(abs_top_srcdir)/test/data/etc/passwd
+export MOCK_GROUP := $(abs_top_srcdir)/test/data/etc/group
+export MOCK_NETGROUP := $(abs_top_srcdir)/test/data/etc/netgroup
+export TESTS_ENVIRONMENT := $(abs_top_builddir)/test/mocklibc/bin/mocklibc
+
+# Include path to mock config files
+export POLKIT_TEST_DATA := $(abs_top_srcdir)/test/data
+
clean-local :
rm -f *~
+
+
+# Never install anything in this dir (specifically MockLibc)
+install:; @:
+install-exec:; @:
+install-data:; @:
+uninstall:; @:
+
diff --git a/test/data/etc/group b/test/data/etc/group
new file mode 100644
index 0000000..12ef328
--- a/dev/null
+++ b/test/data/etc/group
@@ -0,0 +1,7 @@
+root:x:0:
+users:x:100:john,jane
+admin:x:101:sally,henry
+john:x:500:
+jane:x:501:
+sally:x:502:
+henry:x:503:
diff --git a/test/data/etc/netgroup b/test/data/etc/netgroup
new file mode 100644
index 0000000..21a27f9
--- a/dev/null
+++ b/test/data/etc/netgroup
@@ -0,0 +1,5 @@
+foo (-,john,)
+bar (-,jane,)
+baz foo bar
+all (,,)
+none
diff --git a/test/data/etc/passwd b/test/data/etc/passwd
new file mode 100644
index 0000000..8544feb
--- a/dev/null
+++ b/test/data/etc/passwd
@@ -0,0 +1,5 @@
+root:x:0:0:root:/root:/bin/bash
+john:x:500:500:John Done:/home/john:/bin/bash
+jane:x:501:501:Jane Smith:/home/jane:/bin/bash
+sally:x:502:502:Sally Derp:/home/sally:/bin/bash
+henry:x:503:503:Henry Herp:/home/henry:/bin/bash
diff --git a/test/data/etc/polkit-1/localauthority.conf.d/10-test.conf b/test/data/etc/polkit-1/localauthority.conf.d/10-test.conf
new file mode 100644
index 0000000..d7a9824
--- a/dev/null
+++ b/test/data/etc/polkit-1/localauthority.conf.d/10-test.conf
@@ -0,0 +1,2 @@
+[Configuration]
+AdminIdentities=unix-user:root;unix-netgroup:bar;unix-group:admin
diff --git a/test/data/etc/polkit-1/localauthority/10-test/com.example.pkla b/test/data/etc/polkit-1/localauthority/10-test/com.example.pkla
new file mode 100644
index 0000000..bc64c5e
--- a/dev/null
+++ b/test/data/etc/polkit-1/localauthority/10-test/com.example.pkla
@@ -0,0 +1,14 @@
+[Users and Root can do Foo]
+Identity=unix-group:users;unix-user:root
+Action=com.example.awesomeproduct.foo
+ResultAny=no
+ResultInactive=auth_self
+ResultActive=yes
+
+[Users in netgroup baz can do Bar]
+Identity=unix-netgroup:baz
+Action=com.example.awesomeproduct.bar
+ResultAny=no
+ResultInactive=auth_self
+ResultActive=yes
+
diff --git a/test/polkitbackend/data/authstore2/10-test/com.example.pkla b/test/data/var/lib/polkit-1/localauthority/10-test/com.example.pkla
index f013c5b..f013c5b 100644
--- a/test/polkitbackend/data/authstore2/10-test/com.example.pkla
+++ b/test/data/var/lib/polkit-1/localauthority/10-test/com.example.pkla
diff --git a/test/mocklibc/AUTHORS b/test/mocklibc/AUTHORS
new file mode 100644
index 0000000..c2347f6
--- a/dev/null
+++ b/test/mocklibc/AUTHORS
@@ -0,0 +1 @@
+Nikki VonHollen <vonhollen@google.com>
diff --git a/test/mocklibc/COPYING b/test/mocklibc/COPYING
new file mode 100644
index 0000000..d645695
--- a/dev/null
+++ b/test/mocklibc/COPYING
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/test/mocklibc/ChangeLog b/test/mocklibc/ChangeLog
new file mode 100644
index 0000000..00dd245
--- a/dev/null
+++ b/test/mocklibc/ChangeLog
@@ -0,0 +1,10 @@
+2011-12-19 Nikki VonHollen <vonhollen@google.com>
+
+* Added check for 'id' and 'innetgr' commands before running tests that depend
+ on them. 'make check' now passes without them, without running tests.
+
+
+2011-12-14 Nikki VonHollen <vonhollen@google.com>
+
+* Released version 1.0 with basic NSS passwd, group, and netgroup mocks.
+
diff --git a/test/mocklibc/INSTALL b/test/mocklibc/INSTALL
new file mode 100644
index 0000000..7d1c323
--- a/dev/null
+++ b/test/mocklibc/INSTALL
@@ -0,0 +1,365 @@
+Installation Instructions
+*************************
+
+Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
+2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+are permitted in any medium without royalty provided the copyright
+notice and this notice are preserved. This file is offered as-is,
+without warranty of any kind.
+
+Basic Installation
+==================
+
+ Briefly, the shell commands `./configure; make; make install' should
+configure, build, and install this package. The following
+more-detailed instructions are generic; see the `README' file for
+instructions specific to this package. Some packages provide this
+`INSTALL' file but do not implement all of the features documented
+below. The lack of an optional feature in a given package is not
+necessarily a bug. More recommendations for GNU packages can be found
+in *note Makefile Conventions: (standards)Makefile Conventions.
+
+ The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation. It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions. Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
+
+ It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring. Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.
+
+ If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release. If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+ The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'. You need `configure.ac' if
+you want to change it or regenerate `configure' using a newer version
+of `autoconf'.
+
+ The simplest way to compile this package is:
+
+ 1. `cd' to the directory containing the package's source code and type
+ `./configure' to configure the package for your system.
+
+ Running `configure' might take a while. While running, it prints
+ some messages telling which features it is checking for.
+
+ 2. Type `make' to compile the package.
+
+ 3. Optionally, type `make check' to run any self-tests that come with
+ the package, generally using the just-built uninstalled binaries.
+
+ 4. Type `make install' to install the programs and any data files and
+ documentation. When installing into a prefix owned by root, it is
+ recommended that the package be configured and built as a regular
+ user, and only the `make install' phase executed with root
+ privileges.
+
+ 5. Optionally, type `make installcheck' to repeat any self-tests, but
+ this time using the binaries in their final installed location.
+ This target does not install anything. Running this target as a
+ regular user, particularly if the prior `make install' required
+ root privileges, verifies that the installation completed
+ correctly.
+
+ 6. You can remove the program binaries and object files from the
+ source code directory by typing `make clean'. To also remove the
+ files that `configure' created (so you can compile the package for
+ a different kind of computer), type `make distclean'. There is
+ also a `make maintainer-clean' target, but that is intended mainly
+ for the package's developers. If you use it, you may have to get
+ all sorts of other programs in order to regenerate files that came
+ with the distribution.
+
+ 7. Often, you can also type `make uninstall' to remove the installed
+ files again. In practice, not all packages have tested that
+ uninstallation works correctly, even though it is required by the
+ GNU Coding Standards.
+
+ 8. Some packages, particularly those that use Automake, provide `make
+ distcheck', which can by used by developers to test that all other
+ targets like `make install' and `make uninstall' work correctly.
+ This target is generally not run by end users.
+
+Compilers and Options
+=====================
+
+ Some systems require unusual options for compilation or linking that
+the `configure' script does not know about. Run `./configure --help'
+for details on some of the pertinent environment variables.
+
+ You can give `configure' initial values for configuration parameters
+by setting variables in the command line or in the environment. Here
+is an example:
+
+ ./configure CC=c99 CFLAGS=-g LIBS=-lposix
+
+ *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+ You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory. To do this, you can use GNU `make'. `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script. `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'. This
+is known as a "VPATH" build.
+
+ With a non-GNU `make', it is safer to compile the package for one
+architecture at a time in the source code directory. After you have
+installed the package for one architecture, use `make distclean' before
+reconfiguring for another architecture.
+
+ On MacOS X 10.5 and later systems, you can create libraries and
+executables that work on multiple system types--known as "fat" or
+"universal" binaries--by specifying multiple `-arch' options to the
+compiler but only a single `-arch' option to the preprocessor. Like
+this:
+
+ ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
+ CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
+ CPP="gcc -E" CXXCPP="g++ -E"
+
+ This is not guaranteed to produce working output in all cases, you
+may have to build one architecture at a time and combine the results
+using the `lipo' tool if you have problems.
+
+Installation Names
+==================
+
+ By default, `make install' installs the package's commands under
+`/usr/local/bin', include files under `/usr/local/include', etc. You
+can specify an installation prefix other than `/usr/local' by giving
+`configure' the option `--prefix=PREFIX', where PREFIX must be an
+absolute file name.
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+pass the option `--exec-prefix=PREFIX' to `configure', the package uses
+PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files still use the regular prefix.
+
+ In addition, if you use an unusual directory layout you can give
+options like `--bindir=DIR' to specify different values for particular
+kinds of files. Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them. In general, the
+default for these options is expressed in terms of `${prefix}', so that
+specifying just `--prefix' will affect all of the other directory
+specifications that were not explicitly provided.
+
+ The most portable way to affect installation locations is to pass the
+correct locations to `configure'; however, many packages provide one or
+both of the following shortcuts of passing variable assignments to the
+`make install' command line to change installation locations without
+having to reconfigure or recompile.
+
+ The first method involves providing an override variable for each
+affected directory. For example, `make install
+prefix=/alternate/directory' will choose an alternate location for all
+directory configuration variables that were expressed in terms of
+`${prefix}'. Any directories that were specified during `configure',
+but not in terms of `${prefix}', must each be overridden at install
+time for the entire installation to be relocated. The approach of
+makefile variable overrides for each directory variable is required by
+the GNU Coding Standards, and ideally causes no recompilation.
+However, some platforms have known limitations with the semantics of
+shared libraries that end up requiring recompilation when using this
+method, particularly noticeable in packages that use GNU Libtool.
+
+ The second method involves providing the `DESTDIR' variable. For
+example, `make install DESTDIR=/alternate/directory' will prepend
+`/alternate/directory' before all installation names. The approach of
+`DESTDIR' overrides is not required by the GNU Coding Standards, and
+does not work on platforms that have drive letters. On the other hand,
+it does better at avoiding recompilation issues, and works well even
+when some directory options were not specified in terms of `${prefix}'
+at `configure' time.
+
+Optional Features
+=================
+
+ If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+ Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System). The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+ For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+ Some packages offer the ability to configure how verbose the
+execution of `make' will be. For these packages, running `./configure
+--enable-silent-rules' sets the default to minimal output, which can be
+overridden with `make V=1'; while running `./configure
+--disable-silent-rules' sets the default to verbose, which can be
+overridden with `make V=0'.
+
+Particular systems
+==================
+
+ On HP-UX, the default C compiler is not ANSI C compatible. If GNU
+CC is not installed, it is recommended to use the following options in
+order to use an ANSI C compiler:
+
+ ./configure CC="cc -Ae -D_XOPEN_SOURCE=500"
+
+and if that doesn't work, install pre-built binaries of GCC for HP-UX.
+
+ On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
+parse its `<wchar.h>' header file. The option `-nodtk' can be used as
+a workaround. If GNU CC is not installed, it is therefore recommended
+to try
+
+ ./configure CC="cc"
+
+and if that doesn't work, try
+
+ ./configure CC="cc -nodtk"
+
+ On Solaris, don't put `/usr/ucb' early in your `PATH'. This
+directory contains several dysfunctional programs; working variants of
+these programs are available in `/usr/bin'. So, if you need `/usr/ucb'
+in your `PATH', put it _after_ `/usr/bin'.
+
+ On Haiku, software installed for all users goes in `/boot/common',
+not `/usr/local'. It is recommended to use the following options:
+
+ ./configure --prefix=/boot/common
+
+Specifying the System Type
+==========================
+
+ There may be some features `configure' cannot figure out
+automatically, but needs to determine by the type of machine the package
+will run on. Usually, assuming the package is built to be run on the
+_same_ architectures, `configure' can figure that out, but if it prints
+a message saying it cannot guess the machine type, give it the
+`--build=TYPE' option. TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
+ CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+ OS
+ KERNEL-OS
+
+ See the file `config.sub' for the possible values of each field. If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+ If you are _building_ compiler tools for cross-compiling, you should
+use the option `--target=TYPE' to select the type of system they will
+produce code for.
+
+ If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with `--host=TYPE'.
+
+Sharing Defaults
+================
+
+ If you want to set default values for `configure' scripts to share,
+you can create a site shell script called `config.site' that gives
+default values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists. Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+ Variables not defined in a site shell script can be set in the
+environment passed to `configure'. However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost. In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'. For example:
+
+ ./configure CC=/usr/local2/bin/gcc
+
+causes the specified `gcc' to be used as the C compiler (unless it is
+overridden in the site shell script).
+
+Unfortunately, this technique does not work for `CONFIG_SHELL' due to
+an Autoconf bug. Until the bug is fixed you can use this workaround:
+
+ CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
+
+`configure' Invocation
+======================
+
+ `configure' recognizes the following options to control how it
+operates.
+
+`--help'
+`-h'
+ Print a summary of all of the options to `configure', and exit.
+
+`--help=short'
+`--help=recursive'
+ Print a summary of the options unique to this package's
+ `configure', and exit. The `short' variant lists options used
+ only in the top level, while the `recursive' variant lists options
+ also present in any nested packages.
+
+`--version'
+`-V'
+ Print the version of Autoconf used to generate the `configure'
+ script, and exit.
+
+`--cache-file=FILE'
+ Enable the cache: use and save the results of the tests in FILE,
+ traditionally `config.cache'. FILE defaults to `/dev/null' to
+ disable caching.
+
+`--config-cache'
+`-C'
+ Alias for `--cache-file=config.cache'.
+
+`--quiet'
+`--silent'
+`-q'
+ Do not print messages saying which checks are being made. To
+ suppress all normal output, redirect it to `/dev/null' (any error
+ messages will still be shown).
+
+`--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+ `configure' can determine that directory automatically.
+
+`--prefix=DIR'
+ Use DIR as the installation prefix. *note Installation Names::
+ for more details, including other options available for fine-tuning
+ the installation locations.
+
+`--no-create'
+`-n'
+ Run the configure checks, but stop before creating any output
+ files.
+
+`configure' also accepts some other, not widely useful, options. Run
+`configure --help' for more details.
+
diff --git a/test/mocklibc/Makefile.am b/test/mocklibc/Makefile.am
new file mode 100644
index 0000000..3508ecd
--- a/dev/null
+++ b/test/mocklibc/Makefile.am
@@ -0,0 +1,3 @@
+
+SUBDIRS = src bin
+EXTRA_DIST = example
diff --git a/test/mocklibc/NEWS b/test/mocklibc/NEWS
new file mode 100644
index 0000000..e69de29
--- a/dev/null
+++ b/test/mocklibc/NEWS
diff --git a/test/mocklibc/README b/test/mocklibc/README
new file mode 100644
index 0000000..2bd44ef
--- a/dev/null
+++ b/test/mocklibc/README
@@ -0,0 +1,121 @@
+= MockLibc 1.1 =
+
+Mocks of common libc functions who have global state. Version 1.1 focuses on
+NSS related methods (user, group, and netgroup queries).
+
+This library is a re-implementation of specific libc methods, not a tool for
+creating mock functions. Use MockLibc to create a consistent environment for
+your unit tests, when they need to query system information.
+
+
+== Requirements ==
+
+* Tests require the 'id' and 'innetgr' commands in the PATH
+
+
+== Build ==
+
+$ cd mocklibc-1.1
+$ ./configure
+$ make
+$ make check
+
+
+== Install ==
+
+$ make install
+
+
+== Example Usage ==
+
+$ id foo
+id: foo: No such user
+$ export MOCK_PASSWD=./testdata/passwd
+$ export MOCK_GROUP=./testdata/group
+$ mkdir ./testdata
+$ echo “foo:x:9000:9000::/home/foo:/bin/bash” > “$MOCK_PASSWD”
+$ echo “mockusers:x:9001:foo” > “$MOCK_GROUP”
+$ mocklibc id foo
+uid=9000(foo) gid=9000(foo) groups=9000(foo),9001(mockusers)
+
+
+== Use without install ==
+
+mocklibc can be used directly from the bin directory, without being installed:
+$ cd mocklibc-1.1
+$ ./configure
+$ make
+$ bin/mocklibc id foo
+
+
+== Hacking ==
+
+If using a git checkout instead of a source tarball, always run
+'autogen --install' before './configure'. Whenever a Makefile.am or
+configure.ac is modified, run 'autogen' again without --install.
+
+
+== Mocked Functions ==
+
+NSS Methods completely disregard /etc/nsswitch.conf, similar to using just
+"files", but with modified paths. DNS is not modified and no *_r methods will
+be implemented in this version.
+
+* pwd.h (NSS users, configured with MOCK_PASSWD)
+ * setpwent
+ * getpwent
+ * endpwent
+ * getpwnam
+ * getpwuid
+* grp.h (NSS groups, configured with MOCK_GROUP)
+ * setpwent
+ * getpwent
+ * endpwent
+ * getpwnam
+ * getpwuid
+* netdb.h (NSS netgroups, no DNS, configured with MOCK_NETGROUP)
+ * setnetgrent
+ * getnetgrent
+ * endnetgrent
+ * innetgr
+
+
+== Configuration ==
+
+All configuration is handled through environment variables, though specific
+mocklibc_* methods may be added in the future for things like time and random
+number generation.
+
+Environment Variables:
+* MOCK_PASSWD - Path to /etc/passwd replacement
+* MOCK_GROUP - Path to /etc/group replacement
+* MOCK_NETGROUP - Path to /etc/netgroup replacement
+
+
+== F.A.Q. ==
+
+* Why not use a chroot? Chroot requires root, and forcing unit tests to run as
+ root is not desirable.
+* Is there something that already does this? There are mock frameworks for C,
+ but this library is an implementation of specific common mocks C developers
+ need. A mock of set/get/endgrent still requires some basic code for iterating
+ group objects. This library provides that.
+
+
+== TODO ==
+
+* Add functions to free unused memory in 'netdb_netgroup.c'. It leaks a ton of
+ memory every call. See TODO comments in code.
+
+
+== Future ==
+
+The following may be supported in the future, and I'm taking requests for other
+functionality at 'vonhollen@gmail.com'.
+
+Features:
+* Redirect syslog messages to file at $MOCK_SYSLOG
+* '*_r' methods in pwd.h, grp.h, and netdb.h
+* netdb.h: gethostbyname, gethostbyaddr, getaddrinfo, get/freeaddrinfo
+* Whitelist apps with $MOCK_ONLY (includes list of argv[0] names)
+
diff --git a/test/mocklibc/bin/Makefile.am b/test/mocklibc/bin/Makefile.am
new file mode 100644
index 0000000..a2e65e0
--- a/dev/null
+++ b/test/mocklibc/bin/Makefile.am
@@ -0,0 +1,25 @@
+
+bin_SCRIPTS = mocklibc
+
+check_SCRIPTS = mocklibc-test
+TESTS = mocklibc-test
+
+EXTRA_DIST = mocklibc.in mocklibc-test.in
+CLEANFILES = mocklibc mocklibc-test
+
+
+# Substitute build variables in shell scripts
+# See section "4.8.2 Installation Directory Variables" in autoconf manual
+
+edit = sed \
+ -e 's|@libdir[@]|$(libdir)|g' \
+ -e 's|@libname[@]|$(libname)|g' \
+ -e 's|@top_srcdir[@]|$(top_srcdir)|g' \
+ -e 's|@top_builddir[@]|$(top_builddir)|g'
+
+mocklibc mocklibc-test: Makefile
+ $(edit) $(srcdir)/$@.in > $@
+ chmod a+x $@
+
+mocklibc: $(srcdir)/mocklibc.in
+mocklibc-test: $(srcdir)/mocklibc-test.in
diff --git a/test/mocklibc/bin/mocklibc-test.in b/test/mocklibc/bin/mocklibc-test.in
new file mode 100644
index 0000000..9f00a77
--- a/dev/null
+++ b/test/mocklibc/bin/mocklibc-test.in
@@ -0,0 +1,136 @@
+#!/bin/bash
+
+# Copyright 2011 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Author: Nikki VonHollen <vonhollen@gmail.com>
+
+
+# Figure out where everything is
+
+MOCKLIBC="@top_builddir@/bin/mocklibc"
+ETCDIR="@top_srcdir@/example"
+
+
+# Setup the mock environment
+
+export MOCK_PASSWD="${ETCDIR}/passwd"
+export MOCK_GROUP="${ETCDIR}/group"
+export MOCK_NETGROUP="${ETCDIR}/netgroup"
+
+
+# Test helper definitions
+
+TESTCOUNT=0
+FAILCOUNT=0
+
+fail () {
+ echo "Test Failed:"
+ echo $@ >&2
+ echo
+ FAILCOUNT=$((FAILCOUNT+1))
+}
+
+finish () {
+ if [[ $FAILCOUNT -gt 0 ]]
+ then
+ echo "Failed $FAILCOUNT of $TESTCOUNT tests."
+ exit 1
+ else
+ echo "Passed $TESTCOUNT tests."
+ exit 0
+ fi
+}
+
+assert_true () {
+ $MOCKLIBC $@ || fail "assert true: $@"
+ TESTCOUNT=$((TESTCOUNT+1))
+}
+
+assert_false () {
+ $MOCKLIBC $@ && fail "assert false: $@"
+ TESTCOUNT=$((TESTCOUNT+1))
+}
+
+assert_grep () {
+ $MOCKLIBC ${@:2} | grep -q "^${1}\$" || fail "'$1' doesn't match output of: ${@:2}"
+ TESTCOUNT=$((TESTCOUNT+1))
+}
+
+
+# Test implementations
+
+test_passwd () {
+ # Test user ids
+ assert_grep "0" id -u root
+ assert_grep "500" id -u john
+ assert_grep "501" id -u jane
+
+ # Test primary groups
+ assert_grep "root" id -gn root
+ assert_grep "john" id -gn john
+ assert_grep "jane" id -gn jane
+}
+
+test_group () {
+ # Test group lists for users
+ assert_grep "root" id -Gn root
+ assert_grep "john users" id -Gn john
+ assert_grep "jane users" id -Gn jane
+}
+
+test_netgroup () {
+ # Test whether each user is each netgroup
+ assert_true innetgr foo -u john
+ assert_false innetgr foo -u jane
+
+ assert_true innetgr bar -u jane
+ assert_false innetgr bar -u john
+
+ assert_true innetgr baz -u john
+ assert_true innetgr baz -u jane
+ assert_false innetgr baz -u henry
+
+ assert_true innetgr all -u john
+ assert_true innetgr all -u jane
+ assert_true innetgr all -u henry
+
+ assert_false innetgr none -u john
+ assert_false innetgr none -u jane
+ assert_false innetgr none -u henry
+
+ assert_false innetgr fake -u john
+}
+
+
+# Run the tests and print a report
+
+if (which id >/dev/null 2>&1)
+then
+ test_passwd
+ test_group
+else
+ echo "No 'id' command found, skipping passwd and group tests." >&2
+fi
+
+if (which innetgr >/dev/null 2>&1)
+then
+ test_netgroup
+else
+ echo "No 'innetgr' command found, skipping netgroup tests." >&2
+fi
+
+
+finish
+
diff --git a/test/mocklibc/bin/mocklibc.in b/test/mocklibc/bin/mocklibc.in
new file mode 100644
index 0000000..dc18855
--- a/dev/null
+++ b/test/mocklibc/bin/mocklibc.in
@@ -0,0 +1,34 @@
+#!/bin/bash
+
+# Copyright 2011 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Author: Nikki VonHollen <vonhollen@gmail.com>
+
+
+BASEDIR=`dirname $0`
+LIBDIR="${BASEDIR}/../src/.libs"
+LIBPATH="${LIBDIR}/@libname@"
+
+if [[ -f "$LIBPATH" ]]
+then
+ # Include Mocklibc's project build dir if we can find it
+ export LD_LIBRARY_PATH="${LIBDIR}:${LD_LIBRARY_PATH}"
+else
+ # Use the system version instead, w/o requiring ldconfig
+ export LD_LIBRARY_PATH="@libdir@:${LD_LIBRARY_PATH}"
+fi
+
+# Exec the requested app, replacing this one
+LD_PRELOAD="@libname@" exec $@
diff --git a/test/mocklibc/configure.ac b/test/mocklibc/configure.ac
new file mode 100644
index 0000000..70798ee
--- a/dev/null
+++ b/test/mocklibc/configure.ac
@@ -0,0 +1,38 @@
+# -*- Autoconf -*-
+# Process this file with autoconf to produce a configure script.
+
+AC_PREREQ([2.65])
+AC_INIT([MockLibc], [1.1], [vonhollen@google.com])
+AC_CONFIG_SRCDIR([src])
+AC_CONFIG_HEADERS([config.h])
+AM_INIT_AUTOMAKE
+
+# Checks for programs.
+AC_PROG_CC
+
+# Checks for libraries.
+AC_PROG_LIBTOOL
+
+# Checks for header files.
+AC_CHECK_HEADERS([netdb.h stdlib.h string.h])
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_TYPE_UID_T
+AC_TYPE_SIZE_T
+AC_TYPE_SSIZE_T
+
+# Checks for library functions.
+AC_FUNC_MALLOC
+AC_CHECK_FUNCS([endgrent endpwent memset regcomp strdup])
+
+# Build wrapper scripts from templates
+AC_SUBST([libname], [libmocklibc.so])
+#AC_CONFIG_FILES([bin/mocklibc], [chmod +x bin/mocklibc], [libname=${libname}])
+#AC_CONFIG_FILES([bin/mocklibc-test], [chmod +x bin/mocklibc-test],
+# [libname=${libname}]))
+
+AC_OUTPUT([
+Makefile
+src/Makefile
+bin/Makefile
+])
diff --git a/test/mocklibc/example/group b/test/mocklibc/example/group
new file mode 100644
index 0000000..e2253f0
--- a/dev/null
+++ b/test/mocklibc/example/group
@@ -0,0 +1,4 @@
+root:x:0:
+users:x:100:john,jane
+john:x:500:
+jane:x:501:
diff --git a/test/mocklibc/example/netgroup b/test/mocklibc/example/netgroup
new file mode 100644
index 0000000..21a27f9
--- a/dev/null
+++ b/test/mocklibc/example/netgroup
@@ -0,0 +1,5 @@
+foo (-,john,)
+bar (-,jane,)
+baz foo bar
+all (,,)
+none
diff --git a/test/mocklibc/example/passwd b/test/mocklibc/example/passwd
new file mode 100644
index 0000000..62aa6f5
--- a/dev/null
+++ b/test/mocklibc/example/passwd
@@ -0,0 +1,3 @@
+root:x:0:0:root:/root:/bin/bash
+john:x:500:500:John Smith:/home/john:/bin/bash
+jane:x:501:501:Jane Doe:/home/jane:/bin/bash
diff --git a/test/mocklibc/src/Makefile.am b/test/mocklibc/src/Makefile.am
new file mode 100644
index 0000000..7cb934e
--- a/dev/null
+++ b/test/mocklibc/src/Makefile.am
@@ -0,0 +1,8 @@
+
+lib_LTLIBRARIES = libmocklibc.la
+libmocklibc_la_SOURCES = pwd.c grp.c netdb.c netgroup.c netgroup.h
+
+bin_PROGRAMS = mocklibc-debug-netgroup
+mocklibc_debug_netgroup_SOURCES = netgroup-debug.c netgroup-debug.h
+mocklibc_debug_netgroup_LDADD = libmocklibc.la
+
diff --git a/test/mocklibc/src/grp.c b/test/mocklibc/src/grp.c
new file mode 100644
index 0000000..c671e1f
--- a/dev/null
+++ b/test/mocklibc/src/grp.c
@@ -0,0 +1,156 @@
+/**
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Nikki VonHollen <vonhollen@gmail.com>
+ */
+
+#include <grp.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#define GROUP_CONFIG_KEY "MOCK_GROUP"
+
+static FILE *global_stream = NULL;
+
+void setgrent(void) {
+ if (global_stream)
+ endgrent();
+
+ const char *path = getenv(GROUP_CONFIG_KEY);
+ if (!path)
+ return;
+
+ global_stream = fopen(path, "r");
+}
+
+struct group *getgrent(void) {
+ if (!global_stream)
+ setgrent();
+
+ if (!global_stream)
+ return NULL;
+
+ return fgetgrent(global_stream);
+}
+
+void endgrent(void) {
+ if (!global_stream)
+ return;
+
+ fclose(global_stream);
+ global_stream = NULL;
+}
+
+struct group *getgrnam(const char *name) {
+ const char *path = getenv(GROUP_CONFIG_KEY);
+ if (!path)
+ return NULL;
+
+ FILE *stream = fopen(path, "r");
+ if (!stream)
+ return NULL;
+
+ struct group *entry;
+ while ((entry = fgetgrent(stream))) {
+ if (strcmp(entry->gr_name, name) == 0) {
+ fclose(stream);
+ return entry;
+ }
+ }
+
+ fclose(stream);
+ return NULL;
+}
+
+struct group *getgrgid(gid_t gid) {
+ const char *path = getenv(GROUP_CONFIG_KEY);
+ if (!path)
+ return NULL;
+
+ FILE *stream = fopen(path, "r");
+ if (!stream)
+ return NULL;
+
+ struct group *entry;
+ while ((entry = fgetgrent(stream))) {
+ if (entry->gr_gid == gid) {
+ fclose(stream);
+ return entry;
+ }
+ }
+
+ fclose(stream);
+ return NULL;
+}
+
+int getgrouplist(const char *user, gid_t group, gid_t *groups, int *ngroups) {
+ const char *path = getenv(GROUP_CONFIG_KEY);
+ if (!path) {
+ *ngroups = 0;
+ return -1;
+ }
+
+ FILE *stream = fopen(path, "r");
+ if (!stream) {
+ *ngroups = 0;
+ return -1;
+ }
+
+ int default_group_found = 0;
+ int groups_found = 0;
+
+ // Loop through all groups
+ struct group *entry;
+ while ((entry = fgetgrent(stream))) {
+ // Loop through all users in group
+ char **cur_user;
+ for (cur_user = entry->gr_mem; *cur_user; cur_user++) {
+ // Skip users who don't match arg 'user'
+ if (strcmp(*cur_user, user))
+ continue;
+
+ // Is this the default group? if so, flag it
+ if (entry->gr_gid == group)
+ default_group_found = 1;
+
+ // Only insert new entries if we have room
+ if (groups_found < *ngroups) {
+ groups[groups_found] = entry->gr_gid;
+ }
+
+ groups_found++;
+ }
+ }
+
+ // Include the default group if it wasn't found
+ if (!default_group_found) {
+ if (groups_found < *ngroups) {
+ groups[groups_found] = group;
+ }
+ groups_found++;
+ }
+
+ // Did we have to leave out some groups? If not, tell how many we found.
+ int retval = (groups_found > *ngroups) ? -1 : groups_found;
+
+ // Always tell the user how many groups we found via *ngroups
+ *ngroups = groups_found;
+
+ fclose(stream);
+ return retval;
+}
diff --git a/test/mocklibc/src/netdb.c b/test/mocklibc/src/netdb.c
new file mode 100644
index 0000000..9442403
--- a/dev/null
+++ b/test/mocklibc/src/netdb.c
@@ -0,0 +1,100 @@
+/**
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Nikki VonHollen <vonhollen@gmail.com>
+ */
+
+#include "netgroup.h"
+
+#include <netdb.h>
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#define INNETGR_CHECK(match, value) if (match && value && strcmp(match, value)) continue;
+
+/** Private static data. */
+
+static struct netgroup *global_netgroup_head = NULL;
+static struct netgroup_iter global_iter;
+
+/** Public methods */
+
+// REMEMBER: 1 means success, 0 means failure for netgroup methods
+
+int setnetgrent(const char *netgroup) {
+ if (!global_netgroup_head)
+ global_netgroup_head = netgroup_parse_all();
+
+ struct netgroup *group = netgroup_find(global_netgroup_head, netgroup);
+ if (!group) {
+ netgroup_free_all(global_netgroup_head);
+ global_netgroup_head = NULL;
+ return 0;
+ }
+
+ netgroup_iter_init(&global_iter, group);
+ return 1;
+}
+
+void endnetgrent(void) {
+ netgroup_free_all(global_netgroup_head);
+ global_netgroup_head = NULL;
+}
+
+int getnetgrent(char **host, char **user, char **domain) {
+ if (!global_netgroup_head)
+ return 0;
+
+ struct entry *result = netgroup_iter_next(&global_iter);
+ if (!result)
+ return 0;
+
+ *host = result->data.triple.hostname;
+ *user = result->data.triple.username;
+ *domain = result->data.triple.domainname;
+ return 1;
+}
+
+int innetgr(const char *netgroup, const char *host, const char *user,
+ const char *domain) {
+ int retval = 0;
+ struct netgroup *head = netgroup_parse_all();
+ struct netgroup *group = netgroup_find(head, netgroup);
+ if (!group) {
+ // Can't find group
+ netgroup_free_all(head);
+ return 0;
+ }
+
+ struct netgroup_iter iter;
+ netgroup_iter_init(&iter, group);
+
+ struct entry *cur;
+ while ((cur = netgroup_iter_next(&iter))) {
+ INNETGR_CHECK(host, cur->data.triple.hostname);
+ INNETGR_CHECK(user, cur->data.triple.username);
+ INNETGR_CHECK(domain, cur->data.triple.domainname);
+
+ // No INNETGR_CHECK failed, so we matched!
+ retval = 1;
+ break;
+ }
+
+ netgroup_free_all(head);
+ return retval;
+}
diff --git a/test/mocklibc/src/netgroup-debug.c b/test/mocklibc/src/netgroup-debug.c
new file mode 100644
index 0000000..81d6e72
--- a/dev/null
+++ b/test/mocklibc/src/netgroup-debug.c
@@ -0,0 +1,84 @@
+/**
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Nikki VonHollen <vonhollen@gmail.com>
+ */
+
+#include "netgroup-debug.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+void netgroup_debug_print_entry(struct entry *entry, FILE *stream, unsigned int indent) {
+ print_indent(stream, indent);
+
+ if (entry->type == TRIPLE_ENTRY) {
+ fprintf(stream, "triple (%s,%s,%s)\n",
+ entry->data.triple.hostname,
+ entry->data.triple.username,
+ entry->data.triple.domainname);
+ } else if (entry->type == CHILD_ENTRY) {
+ fprintf(stream, "child '%s'\n", entry->data.child.name);
+ struct entry *child;
+ for (child = entry->data.child.head; child; child = child->next) {
+ netgroup_debug_print_entry(child, stream, indent + 1);
+ }
+ } else {
+ fprintf(stream, "UNKNOWN_TYPE");
+ }
+}
+
+void netgroup_debug_print_group(struct netgroup *group, FILE *stream, unsigned int indent) {
+ print_indent(stream, indent);
+ fprintf(stream, "%s\n", group->name);
+ struct entry *entry;
+ for (entry = group->head; entry; entry = entry->next) {
+ netgroup_debug_print_entry(entry, stream, indent + 1);
+ }
+}
+
+void netgroup_debug_print_group_unrolled(struct netgroup *group, FILE *stream, unsigned int indent) {
+ print_indent(stream, indent);
+ fprintf(stream, "%s\n", group->name);
+
+ struct netgroup_iter iter;
+ netgroup_iter_init(&iter, group);
+
+ struct entry *entry;
+ while ((entry = netgroup_iter_next(&iter))) {
+ netgroup_debug_print_entry(entry, stream, indent + 1);
+ }
+}
+
+void netgroup_debug_print_all(struct netgroup *head, FILE *stream, unsigned int indent) {
+ struct netgroup *group;
+ for (group = head; group; group = group->next) {
+ netgroup_debug_print_group(group, stream, indent);
+ }
+}
+
+int main(int argc, char **argv) {
+ struct netgroup *groups = netgroup_parse_all();
+ if (argc == 1)
+ netgroup_debug_print_all(groups, stdout, 0);
+ else if (argc == 2) {
+ struct netgroup *group = netgroup_find(groups, argv[1]);
+ if (!group)
+ return 1;
+ netgroup_debug_print_group_unrolled(group, stdout, 0);
+ }
+
+ return 0;
+}
diff --git a/test/mocklibc/src/netgroup-debug.h b/test/mocklibc/src/netgroup-debug.h
new file mode 100644
index 0000000..d733c95
--- a/dev/null
+++ b/test/mocklibc/src/netgroup-debug.h
@@ -0,0 +1,58 @@
+/**
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Nikki VonHollen <vonhollen@gmail.com>
+ */
+
+#ifndef NETGROUP_DEBUG_H_
+#define NETGROUP_DEBUG_H_
+
+#include "netgroup.h"
+
+#include <stdio.h>
+
+/**
+ * Print entry and it's children to the given stream.
+ * @param entry Netgroup entry to print
+ * @param stream Stream to print to
+ * @param indent Number of indents to use
+ */
+void netgroup_debug_print_entry(struct entry *entry, FILE *stream, unsigned int indent);
+
+/**
+ * Print a single netgroup to the given stream.
+ * @param group Netgroup to print
+ * @param stream Stream to print to
+ * @param indent Number of indents to use
+ */
+void netgroup_debug_print_group(struct netgroup *group, FILE *stream, unsigned int indent);
+
+/**
+ * Print a single netgroup with all triples included recursively.
+ * @param group Netgroup to print
+ * @param stream Stream to print to
+ * @param indent Number of indents to use
+ */
+void netgroup_debug_print_group_unrolled(struct netgroup *group, FILE *stream, unsigned int indent);
+
+/**
+ * Print all netgroups to the given stream.
+ * @param head Head of list of netgroups
+ * @param stream Stream to print to
+ * @param indent Number of indents to use
+ */
+void netgroup_debug_print_all(struct netgroup *head, FILE *stream, unsigned int indent);
+
+#endif
diff --git a/test/mocklibc/src/netgroup.c b/test/mocklibc/src/netgroup.c
new file mode 100644
index 0000000..f2ee857
--- a/dev/null
+++ b/test/mocklibc/src/netgroup.c
@@ -0,0 +1,342 @@
+/**
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Nikki VonHollen <vonhollen@gmail.com>
+ */
+
+#include "netgroup.h"
+
+#include <ctype.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#define NETGROUP_CONFIG_KEY "MOCK_NETGROUP"
+#define NETGROUP_TRIPLE_REGEX "\\(([^,]*),([^,]*),([^\\)]*)\\)"
+#define FREE_IF_NOT_NULL(ptr) if (ptr) free(ptr)
+
+/** Private methods. */
+
+/**
+ * Move the given pointer past any whitespace.
+ * @param cur Pointer to string (char *) to advance
+ */
+static void parser_skip_whitespace(char **cur) {
+ for (; isspace(**cur); (*cur)++) {}
+}
+
+/**
+ * Copy the next group of non-space characters and move the pointer past
+ * consumed characters.
+ * @param cur Pointer to string (char *) to search/advance
+ * @return Copy of chars consumed. Must be free'd by user.
+ */
+static char *parser_copy_word(char **cur) {
+ char *value = *cur;
+ size_t i;
+
+ // Find the next non-null non-space character
+ for (i = 0; !isspace(value[i]) && value[i] != '\0'; i++) {}
+
+ // Don't allocate zero-length strings, just die
+ if (i == 0) {
+ return NULL;
+ }
+
+ // Allocate the new string, with room for a null terminator
+ char *result = malloc(i + 1);
+ if (!result) {
+ return NULL;
+ }
+
+ // Set the current pointer past the parsed region
+ *cur += i;
+
+ memcpy(result, value, i);
+ result[i] = '\0';
+ return result;
+}
+
+/**
+ * Print a varaible indentation to the stream.
+ * @param stream Stream to print to
+ * @param indent Number of indents to use
+ */
+void print_indent(FILE *stream, unsigned int indent) {
+ int i;
+ for (i = 0; i < indent; i++)
+ fprintf(stream, " ");
+}
+
+/**
+ * Connect entries with 'child' type to their child entries.
+ * @param headentry Head of list of entries that need to be connected
+ * @param headgroup Head of list of netgroups to connect child entries to
+ */
+static void netgroup_connect_children(struct entry *headentry, struct netgroup *headgroup) {
+ struct entry *curentry;
+ for (curentry = headentry; curentry; curentry = curentry->next) {
+ // Skip entries that don't have children
+ if (curentry->type != CHILD_ENTRY)
+ continue;
+
+ // Set the entry's children to the head of the netgroup with the same name
+ struct netgroup *group = netgroup_find(headgroup, curentry->data.child.name);
+ if (group)
+ curentry->data.child.head = group->head;
+ }
+}
+
+
+/* Public methods. */
+
+struct netgroup *netgroup_parse_all() {
+ const char *path = getenv(NETGROUP_CONFIG_KEY);
+ if (!path)
+ return NULL;
+
+ FILE *stream = fopen(path, "r");
+ if (!stream)
+ return NULL;
+
+ struct netgroup *headgroup = NULL;
+ struct netgroup *lastgroup = NULL;
+
+ // Parse netgroups but don't fill in child entry pointers
+ for (;;) {
+ size_t line_alloc = 0;
+ char * line = NULL;
+ ssize_t line_size = getline(&line, &line_alloc, stream);
+ if (line_size == -1)
+ break;
+
+ struct netgroup *nextgroup = netgroup_parse_line(line);
+ free(line);
+ if (!nextgroup)
+ continue;
+
+ if (!headgroup) {
+ headgroup = nextgroup;
+ lastgroup = nextgroup;
+ } else {
+ lastgroup->next = nextgroup;
+ lastgroup = nextgroup;
+ }
+ }
+
+ fclose(stream);
+
+ // Fill in child entry pointers
+ struct netgroup *curgroup;
+ for (curgroup = headgroup; curgroup; curgroup = curgroup->next) {
+ netgroup_connect_children(curgroup->head, headgroup);
+ }
+
+ return headgroup;
+}
+
+void netgroup_free_all(struct netgroup *head) {
+ struct netgroup *group = head;
+ struct netgroup *nextgroup;
+ while (group) {
+ nextgroup = group->next;
+ netgroup_free(group);
+ group = nextgroup;
+ }
+}
+
+struct netgroup *netgroup_parse_line(char *line) {
+ char *cur = line;
+
+ // Get the netgroup's name
+ parser_skip_whitespace(&cur);
+ char *group_name = parser_copy_word(&cur);
+ if (!group_name)
+ return NULL;
+
+ // Create new netgroup object
+ struct netgroup *result = malloc(sizeof(struct netgroup));
+ if (!result)
+ return NULL;
+ result->next = NULL;
+ result->name = group_name;
+ result->head = NULL;
+
+ // Fill in netgroup entries
+ struct entry* lastentry = NULL;
+ for (;;) {
+ // Get the next word (anything non-space and non-null)
+ parser_skip_whitespace(&cur);
+ char *word = parser_copy_word(&cur);
+ if (!word)
+ break;
+
+ // Parse the entry
+ struct entry *entry = netgroup_parse_entry(word);
+ free(word);
+ if (!entry)
+ continue;
+
+ // Connect the entries together in a singly-linked list
+ if (lastentry) {
+ lastentry->next = entry;
+ } else {
+ result->head = entry;
+ }
+
+ lastentry = entry;
+ }
+
+ return result;
+}
+
+void netgroup_free(struct netgroup *group) {
+ if (!group)
+ return;
+
+ free(group->name);
+ netgroup_entry_free_all(group->head);
+ free(group);
+}
+
+struct entry *netgroup_parse_entry(const char *value) {
+ // Initialize the regex to match triples only on first call
+ static int regex_needs_init = 1;
+ static regex_t regex_triple;
+ if (regex_needs_init) {
+ if (regcomp(&regex_triple, NETGROUP_TRIPLE_REGEX, REG_EXTENDED))
+ return NULL;
+ regex_needs_init = 0;
+ }
+
+ struct entry *result = malloc(sizeof(struct entry));
+ if (!result)
+ return NULL;
+
+ memset(result, 0, sizeof(struct entry));
+
+ regmatch_t regex_triple_match [4];
+ if (regexec(&regex_triple, value, 4, regex_triple_match, 0) == REG_NOMATCH) {
+ // Match failed, assume entry is a netgroup name
+ result->type = CHILD_ENTRY;
+ result->data.child.name = strdup(value);
+ if (!result->data.child.name) {
+ netgroup_entry_free(result);
+ return NULL;
+ }
+ } else {
+ // Match success, entry is a triple
+ result->type = TRIPLE_ENTRY;
+
+ // Array of pointers to fields to set in triple
+ char ** triple [3] = {
+ &result->data.triple.hostname,
+ &result->data.triple.username,
+ &result->data.triple.domainname };
+ int i;
+
+ // Loop through each potential field in triple
+ for (i = 0; i < 3; i++) {
+ regoff_t start = regex_triple_match[i + 1].rm_so;
+ regoff_t end = regex_triple_match[i + 1].rm_eo;
+ regoff_t len = end - start;
+
+ if (start == -1 || len == 0) {
+ // This field is empty, so it matches anything
+ *triple[i] = NULL;
+ } else {
+ // Allocate and copy new field for triple
+ char *field = malloc(len + 1);
+ if (!field) {
+ netgroup_entry_free(result);
+ return NULL;
+ }
+ memcpy(field, &value[start], len);
+ field[len] = '\0';
+ *triple[i] = field;
+ }
+ }
+ }
+ return result;
+}
+
+void netgroup_entry_free_all(struct entry *head) {
+ struct entry *entry = head;
+ struct entry *nextentry;
+ while (entry) {
+ nextentry = entry->next;
+ netgroup_entry_free(entry);
+ entry = nextentry;
+ }
+}
+
+void netgroup_entry_free(struct entry *entry) {
+ if (!entry)
+ return;
+
+ if (entry->type == TRIPLE_ENTRY) {
+ FREE_IF_NOT_NULL(entry->data.triple.hostname);
+ FREE_IF_NOT_NULL(entry->data.triple.username);
+ FREE_IF_NOT_NULL(entry->data.triple.domainname);
+ } else {
+ FREE_IF_NOT_NULL(entry->data.child.name);
+ }
+
+ free(entry);
+}
+
+struct netgroup *netgroup_find(struct netgroup *head, const char *name) {
+ struct netgroup *group;
+ for (group = head; group && strcmp(group->name, name); group = group->next) {}
+ return group;
+}
+
+void netgroup_iter_init(struct netgroup_iter *iter, struct netgroup *group) {
+ iter->stack[0] = group->head;
+ iter->depth = 0;
+}
+
+struct entry *netgroup_iter_next(struct netgroup_iter *iter) {
+ while (iter->depth >= 0) {
+ struct entry *cur = iter->stack[iter->depth];
+
+ if (!cur) {
+ // Pop current finished entry off stack
+ iter->depth--;
+ } else if (cur->type == CHILD_ENTRY) {
+ // Replace the current location on the stack with the next sibling
+ iter->stack[iter->depth] = cur->next;
+
+ // Grow the stack
+ iter->depth++;
+ if (iter->depth > NETGROUP_MAX_DEPTH) {
+ iter->depth = -1;
+ return NULL; // Too much recursion
+ }
+
+ // Put this entry's children on top of the stack
+ struct entry *child = cur->data.child.head;
+ iter->stack[iter->depth] = child;
+ } else {
+ // Replace the current location on the stack with the next sibling
+ iter->stack[iter->depth] = cur->next;
+ return cur;
+ }
+ }
+
+ return NULL;
+}
diff --git a/test/mocklibc/src/netgroup.h b/test/mocklibc/src/netgroup.h
new file mode 100644
index 0000000..11cf7eb
--- a/dev/null
+++ b/test/mocklibc/src/netgroup.h
@@ -0,0 +1,144 @@
+/**
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Nikki VonHollen <vonhollen@gmail.com>
+ */
+
+#ifndef NETGROUP_H_
+#define NETGROUP_H_
+
+#define NETGROUP_MAX_DEPTH 32
+
+/**
+ * Netgroup with a name and list of entries.
+ */
+struct netgroup;
+
+/**
+ * Entry in a netgroup, either a triple or sub-group (child).
+ */
+struct entry;
+
+struct netgroup {
+ /* Next netgroup in list. */
+ struct netgroup *next; // Next netgroup in list
+
+ /* Netgroup name. */
+ char *name;
+
+ /* First entry in list of entries. */
+ struct entry *head;
+};
+
+struct entry {
+ /* Next entry in list of entries for the parent netgroup. */
+ struct entry *next;
+
+ /* Entry type is triple (host,user,domain) or child (netgroup name). */
+ enum {CHILD_ENTRY, TRIPLE_ENTRY} type;
+
+ union {
+ /* Child data if entry is a netgroup name. */
+ struct {
+ /* Child netgroup name. */
+ char *name;
+
+ /* Pointer to first entry in child netgroup. */
+ struct entry *head;
+ } child;
+
+ /* Triple data if entry type is triple. */
+ struct {
+ char *hostname;
+ char *username;
+ char *domainname;
+ } triple;
+ } data;
+};
+
+/* Recursive netgroup entry iterator. */
+struct netgroup_iter {
+ struct entry *stack [NETGROUP_MAX_DEPTH];
+ int depth;
+};
+
+
+/**
+ * Load full netgroup database into memory.
+ * @return Head netgroup
+ */
+struct netgroup *netgroup_parse_all();
+
+/**
+ * Free a list of netgroups.
+ * @param head Head of list of netgroups
+ */
+void netgroup_free_all(struct netgroup *head);
+
+/**
+ * Parse a single netgroup.
+ * @param line Line for netgroup definition
+ * @return Single netgroup with list of netgroup entries
+ */
+struct netgroup *netgroup_parse_line(char *line);
+
+/**
+ * Free single netgroup.
+ * @param group Netgroup to free
+ */
+void netgroup_free(struct netgroup *group);
+
+/**
+ * Parse a single netgroup entry.
+ * @param value Entry triple or name as string
+ * @return Single netgroup entry
+ */
+struct entry *netgroup_parse_entry(const char *value);
+
+/**
+ * Free a list of netgroup entries.
+ * @param head Head of list of entries
+ */
+void netgroup_entry_free_all(struct entry *head);
+
+/**
+ * Free a single netgroup entry.
+ * @param entry Netgroup entry to free
+ */
+void netgroup_entry_free(struct entry *entry);
+
+/**
+ * Find netgroup with given name.
+ * @param head Head of list of netgroups
+ * @param name Name to find
+ * @return Netgroup with name or NULL if not found
+ */
+struct netgroup *netgroup_find(struct netgroup *head, const char *name);
+
+/**
+ * Create recursive iterator over all entries in a netgroup.
+ * @param iter Pointer to iterator struct
+ * @param group Group to iterate over
+ */
+void netgroup_iter_init(struct netgroup_iter *iter, struct netgroup *group);
+
+/**
+ * Get the next entry in the netgroup iterator.
+ * @param iter Pointer to iterator struct
+ * @return Netgroup entry of type triple, or NULL if done iterating
+ */
+struct entry *netgroup_iter_next(struct netgroup_iter *iter);
+
+#endif
diff --git a/test/mocklibc/src/pwd.c b/test/mocklibc/src/pwd.c
new file mode 100644
index 0000000..6005a1d
--- a/dev/null
+++ b/test/mocklibc/src/pwd.c
@@ -0,0 +1,99 @@
+/**
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Nikki VonHollen <vonhollen@gmail.com>
+ */
+
+#include <pwd.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#define PASSWD_CONFIG_KEY "MOCK_PASSWD"
+
+static FILE *global_stream = NULL;
+
+void setpwent(void) {
+ if (global_stream)
+ endpwent();
+
+ const char *path = getenv(PASSWD_CONFIG_KEY);
+ if (!path)
+ return;
+
+ global_stream = fopen(path, "r");
+}
+
+struct passwd *getpwent(void) {
+ if (!global_stream)
+ setpwent();
+
+ if (!global_stream)
+ return NULL;
+
+ return fgetpwent(global_stream);
+}
+
+void endpwent(void) {
+ if (!global_stream)
+ return;
+
+ fclose(global_stream);
+ global_stream = NULL;
+}
+
+struct passwd *getpwnam(const char *name) {
+ const char *path = getenv(PASSWD_CONFIG_KEY);
+ if (!path)
+ return NULL;
+
+ FILE *stream = fopen(path, "r");
+ if (!stream)
+ return NULL;
+
+ struct passwd *entry;
+ while ((entry = fgetpwent(stream))) {
+ if (strcmp(entry->pw_name, name) == 0) {
+ fclose(stream);
+ return entry;
+ }
+ }
+
+ fclose(stream);
+ return NULL;
+}
+
+struct passwd *getpwuid(uid_t uid) {
+ const char *path = getenv(PASSWD_CONFIG_KEY);
+ if (!path)
+ return NULL;
+
+ FILE *stream = fopen(path, "r");
+ if (!stream)
+ return NULL;
+
+ struct passwd *entry;
+ while ((entry = fgetpwent(stream))) {
+ if (entry->pw_uid == uid) {
+ fclose(stream);
+ return entry;
+ }
+ }
+
+ fclose(stream);
+ return NULL;
+}
diff --git a/test/polkit/Makefile.am b/test/polkit/Makefile.am
index 70fbf67..27bcb95 100644
--- a/test/polkit/Makefile.am
+++ b/test/polkit/Makefile.am
@@ -4,6 +4,7 @@ NULL =
INCLUDES = \
-I$(top_builddir)/src \
-I$(top_srcdir)/src \
+ -I$(top_srcdir)/test \
-DPACKAGE_LIBEXEC_DIR=\""$(libexecdir)"\" \
-DPACKAGE_SYSCONF_DIR=\""$(sysconfdir)"\" \
-DPACKAGE_DATA_DIR=\""$(datadir)"\" \
@@ -16,6 +17,8 @@ INCLUDES = \
$(NULL)
AM_CFLAGS = \
+ -D_POLKIT_COMPILATION \
+ -D_POLKIT_BACKEND_COMPILATION \
$(GLIB_CFLAGS) \
$(NULL)
@@ -34,6 +37,9 @@ polkitunixusertest_SOURCES = polkitunixusertest.c
TEST_PROGS += polkitunixgrouptest
polkitunixgrouptest_SOURCES = polkitunixgrouptest.c
+TEST_PROGS += polkitunixnetgrouptest
+polkitunixnetgrouptest_SOURCES = polkitunixnetgrouptest.c
+
TEST_PROGS += polkitidentitytest
polkitidentitytest_SOURCES = polkitidentitytest.c
diff --git a/test/polkit/polkitidentitytest.c b/test/polkit/polkitidentitytest.c
index edbc765..3b8dd5e 100644
--- a/test/polkit/polkitidentitytest.c
+++ b/test/polkit/polkitidentitytest.c
@@ -21,115 +21,149 @@
#include "glib.h"
#include <polkit/polkit.h>
+#include <polkit/polkitprivate.h>
+/* Test helper types */
-static void
-test_user_from_string (void)
-{
- PolkitIdentity *identity;
- PolkitUnixUser *user;
- GError *error = NULL;
-
- identity = polkit_identity_from_string ("unix-user:root", &error);
- g_assert (identity);
- g_assert_no_error (error);
- g_assert (POLKIT_IS_UNIX_USER (identity));
+struct ComparisonTestData {
+ const gchar *subject_a;
+ const gchar *subject_b;
+ gboolean equal;
+};
- user = POLKIT_UNIX_USER (identity);
- g_assert (user);
-
- g_object_unref (user);
-}
+/* Test definitions */
static void
-test_group_from_string (void)
+test_string (const void *_subject)
{
+ const gchar *subject = (const gchar *) _subject;
+
PolkitIdentity *identity;
- PolkitUnixGroup *group;
GError *error = NULL;
+ gchar *subject_new;
- identity = polkit_identity_from_string ("unix-group:root", &error);
+ /* Create the subject from a string */
+ identity = polkit_identity_from_string (subject, &error);
g_assert (identity);
g_assert_no_error (error);
- g_assert (POLKIT_IS_UNIX_GROUP (identity));
- group = POLKIT_UNIX_GROUP (identity);
- g_assert (group);
+ /* Create new string for identity */
+ subject_new = polkit_identity_to_string (identity);
+
+ /* Make sure they match */
+ g_assert_cmpstr (subject_new, ==, subject);
- g_object_unref (group);
+ g_free (subject_new);
+ g_object_unref (identity);
}
static void
-test_user_to_string (void)
+test_gvariant (const void *_subject)
{
- PolkitIdentity *identity;
+ const gchar *subject = (const gchar *) _subject;
+
+ PolkitIdentity *identity, *new_identity;
GError *error = NULL;
- gchar *value;
+ GVariant *value;
- identity = polkit_identity_from_string ("unix-user:root", &error);
+ /* Create the subject from a string */
+ identity = polkit_identity_from_string (subject, &error);
+ g_assert_no_error (error);
g_assert (identity);
+
+ /* Create a GVariant for the subject */
+ value = polkit_identity_to_gvariant (identity);
+ g_assert (value);
+
+ /* Unserialize the subject */
+ new_identity = polkit_identity_new_for_gvariant (value, &error);
g_assert_no_error (error);
+ g_assert (new_identity);
+ g_variant_unref (value);
- value = polkit_identity_to_string (identity);
- g_assert_cmpstr (value, ==, "unix-user:root");
+ /* Make sure the two identities are equal */
+ g_assert (new_identity);
+ g_assert (polkit_identity_equal (identity, new_identity));
- g_free (value);
g_object_unref (identity);
+ g_object_unref (new_identity);
}
static void
-test_group_to_string (void)
+test_comparison (const void *_data)
{
- PolkitIdentity *identity;
+ struct ComparisonTestData *data = (struct ComparisonTestData *) _data;
+
+ PolkitIdentity *identity_a, *identity_b;
GError *error = NULL;
- gchar *value;
+ guint hash_a, hash_b;
- identity = polkit_identity_from_string ("unix-group:root", &error);
- g_assert (identity);
+ /* Create identities A and B */
+ identity_a = polkit_identity_from_string (data->subject_a, &error);
g_assert_no_error (error);
+ g_assert (identity_a);
- value = polkit_identity_to_string (identity);
- g_assert_cmpstr (value, ==, "unix-group:root");
-
- g_free (value);
- g_object_unref (identity);
-}
-
+ identity_b = polkit_identity_from_string (data->subject_b, &error);
+ g_assert_no_error (error);
+ g_assert (identity_b);
-static void
-test_equal (void)
-{
- PolkitIdentity *identity_a, *identity_b;
- GError *error = NULL;
+ /* Compute their hashes */
+ hash_a = polkit_identity_hash (identity_a);
+ hash_b = polkit_identity_hash (identity_b);
- identity_a = polkit_identity_from_string ("unix-group:root", &error);
- identity_b = polkit_identity_from_string ("unix-group:root", &error);
- g_assert (polkit_identity_equal (identity_a, identity_b));
+ /* Comparison to self should always work */
+ g_assert (polkit_identity_equal (identity_a, identity_a));
+
+ /* Are A and B supposed to match? Test hash and comparators */
+ if (data->equal)
+ {
+ g_assert_cmpint (hash_a, ==, hash_b);
+ g_assert (polkit_identity_equal (identity_a, identity_b));
+ }
+ else
+ {
+ g_assert_cmpint (hash_a, !=, hash_b);
+ g_assert (!polkit_identity_equal (identity_a, identity_b));
+ }
g_object_unref (identity_a);
g_object_unref (identity_b);
}
-static void
-test_hash (void)
-{
- PolkitIdentity *identity_a, *identity_b;
- guint hash_a, hash_b;
- GError *error = NULL;
+/* Test helpers */
- identity_a = polkit_identity_from_string ("unix-group:root", &error);
- identity_b = polkit_identity_from_string ("unix-group:root", &error);
+struct ComparisonTestData comparison_test_data [] = {
+ {"unix-user:root", "unix-user:root", TRUE},
+ {"unix-user:root", "unix-user:john", FALSE},
+ {"unix-user:john", "unix-user:john", TRUE},
- hash_a = polkit_identity_hash (identity_a);
- hash_b = polkit_identity_hash (identity_b);
- g_assert_cmpint (hash_a, ==, hash_b);
+ {"unix-group:root", "unix-group:root", TRUE},
+ {"unix-group:root", "unix-group:jane", FALSE},
+ {"unix-group:jane", "unix-group:jane", TRUE},
- g_object_unref (identity_a);
- g_object_unref (identity_b);
+ {"unix-netgroup:foo", "unix-netgroup:foo", TRUE},
+ {"unix-netgroup:foo", "unix-netgroup:bar", FALSE},
+
+ {"unix-user:root", "unix-group:root", FALSE},
+ {"unix-user:jane", "unix-netgroup:foo", FALSE},
+
+ {NULL},
+};
+
+static void
+add_comparison_tests (void)
+{
+ unsigned int i;
+ for (i = 0; comparison_test_data[i].subject_a != NULL; i++)
+ {
+ struct ComparisonTestData *test_data = &comparison_test_data[i];
+ gchar *test_name = g_strdup_printf ("/PolkitIdentity/comparison_%d", i);
+ g_test_add_data_func (test_name, test_data, test_comparison);
+ }
}
@@ -138,11 +172,23 @@ main (int argc, char *argv[])
{
g_type_init ();
g_test_init (&argc, &argv, NULL);
- g_test_add_func ("/PolkitIdentity/user_from_string", test_user_from_string);
- g_test_add_func ("/PolkitIdentity/user_to_string", test_user_to_string);
- g_test_add_func ("/PolkitIdentity/group_from_string", test_group_from_string);
- g_test_add_func ("/PolkitIdentity/group_to_string", test_group_to_string);
- g_test_add_func ("/PolkitIdentity/equal", test_equal);
- g_test_add_func ("/PolkitIdentity/hash", test_hash);
+
+ g_test_add_data_func ("/PolkitIdentity/user_string_0", "unix-user:root", test_string);
+ g_test_add_data_func ("/PolkitIdentity/user_string_1", "unix-user:john", test_string);
+ g_test_add_data_func ("/PolkitIdentity/user_string_2", "unix-user:jane", test_string);
+
+ g_test_add_data_func ("/PolkitIdentity/group_string_0", "unix-group:root", test_string);
+ g_test_add_data_func ("/PolkitIdentity/group_string_1", "unix-group:john", test_string);
+ g_test_add_data_func ("/PolkitIdentity/group_string_2", "unix-group:jane", test_string);
+ g_test_add_data_func ("/PolkitIdentity/group_string_3", "unix-group:users", test_string);
+
+ g_test_add_data_func ("/PolkitIdentity/netgroup_string", "unix-netgroup:foo", test_string);
+
+ g_test_add_data_func ("/PolkitIdentity/user_gvariant", "unix-user:root", test_gvariant);
+ g_test_add_data_func ("/PolkitIdentity/group_gvariant", "unix-group:root", test_gvariant);
+ g_test_add_data_func ("/PolkitIdentity/netgroup_gvariant", "unix-netgroup:foo", test_gvariant);
+
+ add_comparison_tests ();
+
return g_test_run ();
}
diff --git a/test/polkit/polkitunixnetgrouptest.c b/test/polkit/polkitunixnetgrouptest.c
new file mode 100644
index 0000000..c67822e
--- a/dev/null
+++ b/test/polkit/polkitunixnetgrouptest.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2011 Google 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: Nikki VonHollen <vonhollen@google.com>
+ */
+
+#include "glib.h"
+#include <polkit/polkit.h>
+#include <string.h>
+
+
+static void
+test_new (void)
+{
+ PolkitUnixNetgroup *netgroup;
+ const char *netgroup_name;
+
+ netgroup = POLKIT_UNIX_NETGROUP (polkit_unix_netgroup_new ("testgroup"));
+ g_assert (netgroup);
+
+ netgroup_name = polkit_unix_netgroup_get_name (netgroup);
+ g_assert_cmpstr (netgroup_name, ==, "testgroup");
+
+ g_object_unref (netgroup);
+}
+
+
+static void
+test_set_name (void)
+{
+ PolkitUnixNetgroup *netgroup;
+ const char *netgroup_name;
+ char new_name_buf [] = "foo";
+
+ netgroup = POLKIT_UNIX_NETGROUP (polkit_unix_netgroup_new ("testgroup"));
+
+ polkit_unix_netgroup_set_name (netgroup, new_name_buf);
+ netgroup_name = polkit_unix_netgroup_get_name (netgroup);
+ g_assert_cmpstr (netgroup_name, ==, "foo");
+
+ memcpy(new_name_buf, "bar", 3);
+ netgroup_name = polkit_unix_netgroup_get_name (netgroup);
+ g_assert_cmpstr (netgroup_name, ==, "foo");
+
+ polkit_unix_netgroup_set_name (netgroup, new_name_buf);
+ netgroup_name = polkit_unix_netgroup_get_name (netgroup);
+ g_assert_cmpstr (netgroup_name, ==, "bar");
+
+ g_object_unref (netgroup);
+}
+
+
+int
+main (int argc, char *argv[])
+{
+ g_type_init ();
+ g_test_init (&argc, &argv, NULL);
+ g_test_add_func ("/PolkitUnixNetgroup/new", test_new);
+ g_test_add_func ("/PolkitUnixNetgroup/set_name", test_set_name);
+ return g_test_run ();
+}
diff --git a/test/polkit/polkitunixusertest.c b/test/polkit/polkitunixusertest.c
index 1ad0a65..ce35088 100644
--- a/test/polkit/polkitunixusertest.c
+++ b/test/polkit/polkitunixusertest.c
@@ -22,36 +22,58 @@
#include "glib.h"
#include <polkit/polkit.h>
+struct user_entry {
+ const gchar *name;
+ gint uid;
+};
+
+static struct user_entry user_entries [] = {
+ {"root", 0},
+ {"john", 500},
+ {"jane", 501},
+ {NULL},
+};
static void
test_new (void)
{
- PolkitUnixUser *user;
+ unsigned int i;
+ for (i = 0; user_entries[i].name != NULL; i++) {
+ gint uid = user_entries[i].uid;
- user = POLKIT_UNIX_USER (polkit_unix_user_new (0));
- g_assert (user);
+ PolkitUnixUser *user;
- gint user_uid = polkit_unix_user_get_uid (user);
- g_assert_cmpint (user_uid, ==, 0);
+ user = POLKIT_UNIX_USER (polkit_unix_user_new (uid));
+ g_assert (user);
- g_object_unref (user);
+ gint user_uid = polkit_unix_user_get_uid (user);
+ g_assert_cmpint (user_uid, ==, uid);
+
+ g_object_unref (user);
+ }
}
static void
test_new_for_name (void)
{
- GError *error = NULL;
- PolkitUnixUser *user;
+ unsigned int i;
+ for (i = 0; user_entries[i].name != NULL; i++) {
+ const gchar *name = user_entries[i].name;
+ gint expect_uid = user_entries[i].uid;
- user = POLKIT_UNIX_USER (polkit_unix_user_new_for_name ("root", &error));
- g_assert (user);
- g_assert_no_error (error);
+ GError *error = NULL;
+ PolkitUnixUser *user;
- gint user_uid = polkit_unix_user_get_uid (user);
- g_assert_cmpint (user_uid, ==, 0);
+ user = POLKIT_UNIX_USER (polkit_unix_user_new_for_name (name, &error));
+ g_assert (user);
+ g_assert_no_error (error);
- g_object_unref (user);
+ gint user_uid = polkit_unix_user_get_uid (user);
+ g_assert_cmpint (user_uid, ==, expect_uid);
+
+ g_object_unref (user);
+ }
}
diff --git a/test/polkitbackend/Makefile.am b/test/polkitbackend/Makefile.am
index 8067232..c611b5b 100644
--- a/test/polkitbackend/Makefile.am
+++ b/test/polkitbackend/Makefile.am
@@ -3,8 +3,8 @@ NULL =
INCLUDES = \
-I$(top_builddir)/src \
- -I$(top_builddir)/test \
-I$(top_srcdir)/src \
+ -I$(top_srcdir)/test \
-DPACKAGE_LIBEXEC_DIR=\""$(libexecdir)"\" \
-DPACKAGE_SYSCONF_DIR=\""$(sysconfdir)"\" \
-DPACKAGE_DATA_DIR=\""$(datadir)"\" \
diff --git a/test/polkitbackend/data/authstore1/10-test/com.example.pkla b/test/polkitbackend/data/authstore1/10-test/com.example.pkla
deleted file mode 100644
index e716465..0000000
--- a/test/polkitbackend/data/authstore1/10-test/com.example.pkla
+++ b/dev/null
@@ -1,6 +0,0 @@
-[Normal Staff Permissions]
-Identity=unix-group:users;unix-user:root
-Action=com.example.awesomeproduct.*
-ResultAny=no
-ResultInactive=auth_self
-ResultActive=yes
diff --git a/test/polkitbackend/polkitbackendlocalauthoritytest.c b/test/polkitbackend/polkitbackendlocalauthoritytest.c
index f76ea41..617c254 100644
--- a/test/polkitbackend/polkitbackendlocalauthoritytest.c
+++ b/test/polkitbackend/polkitbackendlocalauthoritytest.c
@@ -25,9 +25,9 @@
#include <polkit/polkit.h>
#include <polkitbackend/polkitbackendlocalauthority.h>
-#define TEST_CONFIG_PATH "./data/config"
-#define TEST_AUTH_PATH1 "./data/authstore1"
-#define TEST_AUTH_PATH2 "./data/authstore2"
+#define TEST_CONFIG_PATH "etc/polkit-1/localauthority.conf.d"
+#define TEST_AUTH_PATH1 "etc/polkit-1/localauthority"
+#define TEST_AUTH_PATH2 "var/lib/polkit-1/localauthority"
/* Test helper types */
@@ -93,21 +93,96 @@ test_check_authorization_sync (const void *_ctx)
g_object_unref (out_details);
}
+static void
+test_get_admin_identities (void)
+{
+ /* Note: The implementation for get_admin_identities is called
+ * get_admin_auth_identities in PolkitBackendLocalAuthority */
+
+ PolkitBackendLocalAuthority *authority = create_authority ();
+
+ /* Setup required arguments, but none of their values matter */
+ PolkitSubject *caller = polkit_unix_session_new ("caller-session");
+ g_assert (caller);
+
+ PolkitSubject *subject = polkit_unix_session_new ("subject-session");;
+ g_assert (subject);
+
+ GError *error = NULL;
+ PolkitIdentity *user_for_subject = polkit_identity_from_string ("unix-user:root", &error);
+ g_assert_no_error (error);
+ g_assert (user_for_subject);
+
+ PolkitDetails *details = polkit_details_new ();
+ g_assert (details);
+
+ /* Get the list of PolkitUnixUser objects who are admins */
+ GList *result;
+ result = polkit_backend_interactive_authority_get_admin_identities (
+ POLKIT_BACKEND_INTERACTIVE_AUTHORITY (authority),
+ caller,
+ subject,
+ user_for_subject,
+ "com.example.doesntmatter",
+ details);
+
+ guint result_len = g_list_length (result);
+ g_assert_cmpint (result_len, >, 0);
+
+ /* Test against each of the admins in the following list */
+ const gchar *expect_admins [] = {
+ "unix-user:root",
+ "unix-user:jane",
+ "unix-user:sally",
+ "unix-user:henry",
+ NULL,
+ };
+
+ unsigned int i;
+ for (i = 0; expect_admins[i] != NULL; i++)
+ {
+ g_assert_cmpint (i, <, result_len);
+
+ PolkitIdentity *test_identity = POLKIT_IDENTITY (g_list_nth_data (result, i));
+ g_assert (test_identity);
+
+ gchar *test_identity_str = polkit_identity_to_string (test_identity);
+ g_assert_cmpstr (expect_admins[i], ==, test_identity_str);
+ }
+}
+
/* Factory for mock local authority. */
static PolkitBackendLocalAuthority *
create_authority (void)
{
- return g_object_new (
+ gchar *config_path = polkit_test_get_data_path (TEST_CONFIG_PATH);
+ gchar *auth_path1 = polkit_test_get_data_path (TEST_AUTH_PATH1);
+ gchar *auth_path2 = polkit_test_get_data_path (TEST_AUTH_PATH2);
+ gchar *auth_paths = g_strconcat (auth_path1, ";", auth_path2, NULL);
+
+ g_assert (config_path);
+ g_assert (auth_path1);
+ g_assert (auth_path2);
+ g_assert (auth_paths);
+
+ PolkitBackendLocalAuthority *authority = g_object_new (
POLKIT_BACKEND_TYPE_LOCAL_AUTHORITY,
- "config-path", TEST_CONFIG_PATH,
- "auth-store-paths", TEST_AUTH_PATH1 ";" TEST_AUTH_PATH2,
+ "config-path", config_path,
+ "auth-store-paths", auth_paths,
NULL);
+
+ g_free (config_path);
+ g_free (auth_path1);
+ g_free (auth_path2);
+ g_free (auth_paths);
+ return authority;
}
/* Variations of the check_authorization_sync */
struct auth_context check_authorization_test_data [] = {
+ /* Test root, john, and jane on action awesomeproduct.foo (all users are ok) */
{"unix-user:root", TRUE, TRUE, "com.example.awesomeproduct.foo",
POLKIT_IMPLICIT_AUTHORIZATION_UNKNOWN,
POLKIT_IMPLICIT_AUTHORIZATION_AUTHORIZED},
@@ -117,12 +192,41 @@ struct auth_context check_authorization_test_data [] = {
{"unix-user:root", FALSE, FALSE, "com.example.awesomeproduct.foo",
POLKIT_IMPLICIT_AUTHORIZATION_UNKNOWN,
POLKIT_IMPLICIT_AUTHORIZATION_NOT_AUTHORIZED},
+ {"unix-user:john", TRUE, TRUE, "com.example.awesomeproduct.foo",
+ POLKIT_IMPLICIT_AUTHORIZATION_UNKNOWN,
+ POLKIT_IMPLICIT_AUTHORIZATION_AUTHORIZED},
+ {"unix-user:jane", TRUE, TRUE, "com.example.awesomeproduct.foo",
+ POLKIT_IMPLICIT_AUTHORIZATION_UNKNOWN,
+ POLKIT_IMPLICIT_AUTHORIZATION_AUTHORIZED},
+
+ /* Test root, john, and jane on action restrictedproduct.foo (only root is ok) */
{"unix-user:root", TRUE, TRUE, "com.example.restrictedproduct.foo",
POLKIT_IMPLICIT_AUTHORIZATION_UNKNOWN,
POLKIT_IMPLICIT_AUTHORIZATION_AUTHENTICATION_REQUIRED},
+ {"unix-user:john", TRUE, TRUE, "com.example.restrictedproduct.foo",
+ POLKIT_IMPLICIT_AUTHORIZATION_UNKNOWN,
+ POLKIT_IMPLICIT_AUTHORIZATION_UNKNOWN},
+ {"unix-user:jane", TRUE, TRUE, "com.example.restrictedproduct.foo",
+ POLKIT_IMPLICIT_AUTHORIZATION_UNKNOWN,
+ POLKIT_IMPLICIT_AUTHORIZATION_UNKNOWN},
+
+ /* Test root against some missing actions */
{"unix-user:root", TRUE, TRUE, "com.example.missingproduct.foo",
POLKIT_IMPLICIT_AUTHORIZATION_UNKNOWN,
POLKIT_IMPLICIT_AUTHORIZATION_UNKNOWN},
+
+ /* Test root, john, and jane against action awesomeproduct.bar
+ * which uses "unix-netgroup:baz" for auth (john and jane are OK, root is not) */
+ {"unix-user:root", TRUE, TRUE, "com.example.awesomeproduct.bar",
+ POLKIT_IMPLICIT_AUTHORIZATION_UNKNOWN,
+ POLKIT_IMPLICIT_AUTHORIZATION_UNKNOWN},
+ {"unix-user:john", TRUE, TRUE, "com.example.awesomeproduct.bar",
+ POLKIT_IMPLICIT_AUTHORIZATION_UNKNOWN,
+ POLKIT_IMPLICIT_AUTHORIZATION_AUTHORIZED},
+ {"unix-user:jane", TRUE, TRUE, "com.example.awesomeproduct.bar",
+ POLKIT_IMPLICIT_AUTHORIZATION_UNKNOWN,
+ POLKIT_IMPLICIT_AUTHORIZATION_AUTHORIZED},
+
{NULL},
};
@@ -135,7 +239,7 @@ add_check_authorization_tests (void) {
struct auth_context *ctx = &check_authorization_test_data[i];
gchar *test_name = g_strdup_printf (
"/PolkitBackendLocalAuthority/check_authorization_sync_%d", i);
- g_test_add_data_func(test_name, ctx, test_check_authorization_sync);
+ g_test_add_data_func (test_name, ctx, test_check_authorization_sync);
}
};
@@ -154,5 +258,7 @@ main (int argc, char *argv[])
POLKIT_BACKEND_TYPE_AUTHORITY);
add_check_authorization_tests ();
+ g_test_add_func ("/PolkitBackendLocalAuthority/get_admin_identities", test_get_admin_identities);
+
return g_test_run ();
};
diff --git a/test/polkitbackend/polkitbackendlocalauthorizationstoretest.c b/test/polkitbackend/polkitbackendlocalauthorizationstoretest.c
index 617acf9..945e163 100644
--- a/test/polkitbackend/polkitbackendlocalauthorizationstoretest.c
+++ b/test/polkitbackend/polkitbackendlocalauthorizationstoretest.c
@@ -25,16 +25,23 @@
#include <polkit/polkit.h>
#include <polkitbackend/polkitbackendlocalauthorizationstore.h>
-#define DATA_DIR "./data/authstore1/10-test"
+#define DATA_DIR "etc/polkit-1/localauthority/10-test"
#define DATA_EXT ".pkla"
static void
test_new (void)
{
PolkitBackendLocalAuthorizationStore *store;
+ gchar *data_dir_path;
GFile *data_dir;
- data_dir = g_file_new_for_path (DATA_DIR);
+ data_dir_path = polkit_test_get_data_path (DATA_DIR);
+ g_assert (data_dir_path);
+
+ data_dir = g_file_new_for_path (data_dir_path);
+ g_assert (data_dir);
+
+ g_free (data_dir_path);
store = polkit_backend_local_authorization_store_new (data_dir, DATA_EXT);
g_assert (store);
@@ -44,6 +51,7 @@ test_new (void)
static void
test_lookup (void)
{
+ gchar *data_dir_path;
GFile *data_dir;
PolkitBackendLocalAuthorizationStore *store;
GError *error = NULL;
@@ -54,8 +62,14 @@ test_lookup (void)
PolkitImplicitAuthorization ret_active;
PolkitDetails *details;
+ // Get auth store path
+ data_dir_path = polkit_test_get_data_path (DATA_DIR);
+ g_assert (data_dir_path);
+
+ data_dir = g_file_new_for_path (data_dir_path);
+ g_assert (data_dir);
+
// Create the auth store
- data_dir = g_file_new_for_path (DATA_DIR);
store = polkit_backend_local_authorization_store_new (data_dir, DATA_EXT);
g_assert (store);
@@ -63,7 +77,7 @@ test_lookup (void)
details = polkit_details_new ();
// Create an identity to query with
- identity = polkit_identity_from_string("unix-group:users", &error);
+ identity = polkit_identity_from_string ("unix-group:users", &error);
g_assert (identity);
g_assert_no_error (error);
@@ -71,7 +85,7 @@ test_lookup (void)
ok = polkit_backend_local_authorization_store_lookup (
store,
identity,
- "com.example.awesomeproduct.dofoo",
+ "com.example.awesomeproduct.foo",
details,
&ret_any,
&ret_inactive,
@@ -83,7 +97,7 @@ test_lookup (void)
g_assert_cmpstr ("yes", ==, polkit_implicit_authorization_to_string (ret_active));
// Create another identity to query with
- identity = polkit_identity_from_string("unix-user:root", &error);
+ identity = polkit_identity_from_string ("unix-user:root", &error);
g_assert (identity);
g_assert_no_error (error);
@@ -91,7 +105,7 @@ test_lookup (void)
ok = polkit_backend_local_authorization_store_lookup (
store,
identity,
- "com.example.awesomeproduct.dofoo",
+ "com.example.awesomeproduct.foo",
details,
&ret_any,
&ret_inactive,
diff --git a/test/polkittesthelper.c b/test/polkittesthelper.c
index 5373bdc..41c4ce5 100644
--- a/test/polkittesthelper.c
+++ b/test/polkittesthelper.c
@@ -20,6 +20,8 @@
*/
#include "polkittesthelper.h"
+#include <stdlib.h>
+
/* TODO: Log handling with unit tests is horrible. Figure out a way to always
* show logs, without munging up test output. For now, we hide them
@@ -46,3 +48,21 @@ polkit_test_redirect_logs (void)
g_log_set_default_handler (polkit_test_log_handler, NULL);
}
+/**
+ * Get absolute path to test data.
+ *
+ * Requires POLKIT_TEST_DATA environment variable to point to root data dir.
+ *
+ * @param relpath Relative path to test data
+ * @return Full path to data as string. Free with g_free().
+ */
+gchar *
+polkit_test_get_data_path (const gchar *relpath)
+{
+ const gchar *root = getenv ("POLKIT_TEST_DATA");
+ if (root == NULL)
+ return NULL;
+
+ return g_strconcat(root, "/", relpath, NULL);
+}
+
diff --git a/test/polkittesthelper.h b/test/polkittesthelper.h
index c8ce161..da49b06 100644
--- a/test/polkittesthelper.h
+++ b/test/polkittesthelper.h
@@ -31,4 +31,6 @@ void polkit_test_log_handler (const gchar *log_domain,
void polkit_test_redirect_logs (void);
+gchar *polkit_test_get_data_path (const gchar *relpath);
+
#endif