summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am1
-rw-r--r--configure.ac11
-rw-r--r--daemon/Makefile.am5
-rw-r--r--daemon/gdm-session-worker.c106
-rw-r--r--daemon/gdm-session-worker.h2
-rw-r--r--daemon/gdm-session-worker.xml2
-rw-r--r--daemon/gdm-session.c59
-rw-r--r--daemon/gdm-session.xml3
-rw-r--r--pam-extensions/Makefile.am23
-rw-r--r--pam-extensions/gdm-pam-extensions.h133
-rw-r--r--pam-extensions/gdm-pam-extensions.pc.in9
11 files changed, 342 insertions, 12 deletions
diff --git a/Makefile.am b/Makefile.am
index a6409e1b..25d679f6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -3,6 +3,7 @@ NULL =
SUBDIRS = \
data \
common \
+ pam-extensions \
daemon \
libgdm \
utils \
diff --git a/configure.ac b/configure.ac
index 20d47306..2bf8736e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -539,6 +539,15 @@ if test "x$have_pam" = "xyes"; then
)],
[AC_MSG_RESULT(["not found - assume const, Linux-type PAM"])]
)
+ AC_CHECK_DECL(PAM_BINARY_PROMPT,
+ [supports_pam_extensions=yes],
+ [supports_pam_extensions=no],
+ #include <security/pam_appl.h>
+ )
+fi
+if test "x$supports_pam_extensions" = "xyes" ; then
+ AM_CONDITIONAL(SUPPORTS_PAM_EXTENSIONS, true)
+ AC_DEFINE(SUPPORTS_PAM_EXTENSIONS, 1, [Define if PAM supports GDMs custom extensions])
fi
AC_CHECK_LIB(keyutils, keyctl_read, [
@@ -1548,6 +1557,8 @@ AC_DEFINE_UNQUOTED(X_XNEST_UNSCALED_FONTPATH,"$X_XNEST_UNSCALED_FONTPATH",[])
AC_CONFIG_FILES([
Makefile
+pam-extensions/Makefile
+pam-extensions/gdm-pam-extensions.pc
daemon/Makefile
docs/Makefile
chooser/Makefile
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index ab5dda06..8b82af9c 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -4,6 +4,7 @@ AM_CPPFLAGS = \
-I. \
-I.. \
-I$(top_srcdir)/common \
+ -I$(top_srcdir)/pam-extensions \
-I$(top_builddir)/common \
-DBINDIR=\"$(bindir)\" \
-DDATADIR=\"$(datadir)\" \
@@ -133,6 +134,10 @@ gdm_session_worker_SOURCES = \
gdm-dbus-util.h \
$(NULL)
+if SUPPORTS_PAM_EXTENSIONS
+gdm_session_worker_SOURCES += $(top_srcdir)/pam-extensions/gdm-pam-extensions.h
+endif
+
nodist_gdm_session_worker_SOURCES = \
gdm-session-glue.h \
gdm-session-glue.c \
diff --git a/daemon/gdm-session-worker.c b/daemon/gdm-session-worker.c
index 9342352d..02827470 100644
--- a/daemon/gdm-session-worker.c
+++ b/daemon/gdm-session-worker.c
@@ -61,6 +61,11 @@
#include "gdm-common.h"
#include "gdm-log.h"
+
+#ifdef SUPPORTS_PAM_EXTENSIONS
+#include "gdm-pam-extensions.h"
+#endif
+
#include "gdm-session-worker.h"
#include "gdm-session-glue.h"
#include "gdm-session.h"
@@ -149,6 +154,7 @@ struct GdmSessionWorkerPrivate
uid_t uid;
gid_t gid;
gboolean password_is_required;
+ char **extensions;
int cred_flags;
int login_vt;
@@ -177,6 +183,15 @@ struct GdmSessionWorkerPrivate
GDBusMethodInvocation *pending_invocation;
};
+#ifdef SUPPORTS_PAM_EXTENSIONS
+static char gdm_pam_extension_environment_block[_POSIX_ARG_MAX];
+
+static const char * const
+gdm_supported_pam_extensions[] = {
+ NULL
+};
+#endif
+
enum {
PROP_0,
PROP_SERVER_ADDRESS,
@@ -519,6 +534,32 @@ gdm_session_worker_report_problem (GdmSessionWorker *worker,
NULL);
}
+#ifdef SUPPORTS_PAM_EXTENSIONS
+static gboolean
+gdm_session_worker_process_extended_pam_message (GdmSessionWorker *worker,
+ const struct pam_message *query,
+ char **response)
+{
+ GdmPamExtensionMessage *extended_message;
+ gboolean res;
+
+ extended_message = GDM_PAM_EXTENSION_MESSAGE_FROM_PAM_MESSAGE (query);
+
+ if (GDM_PAM_EXTENSION_MESSAGE_TRUNCATED (extended_message)) {
+ g_warning ("PAM service requested binary response for truncated query");
+ return FALSE;
+ }
+
+ if (GDM_PAM_EXTENSION_MESSAGE_INVALID_TYPE (extended_message)) {
+ g_warning ("PAM service requested binary response for unadvertised query type");
+ return FALSE;
+ }
+
+ g_debug ("GdmSessionWorker: received extended pam message of unknown type %u", (unsigned int) extended_message->type);
+ return FALSE;
+}
+#endif
+
static char *
convert_to_utf8 (const char *str)
{
@@ -563,6 +604,11 @@ gdm_session_worker_process_pam_message (GdmSessionWorker *worker,
gdm_session_worker_update_username (worker);
+#ifdef SUPPORTS_PAM_EXTENSIONS
+ if (query->msg_style == PAM_BINARY_PROMPT)
+ return gdm_session_worker_process_extended_pam_message (worker, query, response);
+#endif
+
g_debug ("GdmSessionWorker: received pam message of type %u with payload '%s'",
query->msg_style, query->msg);
@@ -989,16 +1035,17 @@ out:
}
static gboolean
-gdm_session_worker_initialize_pam (GdmSessionWorker *worker,
- const char *service,
- const char *username,
- const char *hostname,
- gboolean display_is_local,
- const char *x11_display_name,
- const char *x11_authority_file,
- const char *display_device,
- const char *seat_id,
- GError **error)
+gdm_session_worker_initialize_pam (GdmSessionWorker *worker,
+ const char *service,
+ const char * const *extensions,
+ const char *username,
+ const char *hostname,
+ gboolean display_is_local,
+ const char *x11_display_name,
+ const char *x11_authority_file,
+ const char *display_device,
+ const char *seat_id,
+ GError **error)
{
struct pam_conv pam_conversation;
int error_code;
@@ -1011,6 +1058,12 @@ gdm_session_worker_initialize_pam (GdmSessionWorker *worker,
username ? username : "(null)",
seat_id ? seat_id : "(null)");
+#ifdef SUPPORTS_PAM_EXTENSIONS
+ if (extensions != NULL) {
+ GDM_PAM_EXTENSION_ADVERTISE_SUPPORTED_EXTENSIONS (gdm_pam_extension_environment_block, extensions);
+ }
+#endif
+
pam_conversation.conv = (GdmSessionWorkerPamNewMessagesFunc) gdm_session_worker_pam_new_messages_handler;
pam_conversation.appdata_ptr = worker;
@@ -2447,6 +2500,7 @@ do_setup (GdmSessionWorker *worker)
error = NULL;
res = gdm_session_worker_initialize_pam (worker,
worker->priv->service,
+ (const char **) worker->priv->extensions,
worker->priv->username,
worker->priv->hostname,
worker->priv->display_is_local,
@@ -2813,10 +2867,38 @@ gdm_session_worker_handle_open (GdmDBusWorker *object,
return TRUE;
}
+static char **
+filter_extensions (const char * const *extensions)
+{
+ size_t i, j;
+ GPtrArray *array = NULL;
+ char **filtered_extensions = NULL;
+
+ array = g_ptr_array_new ();
+
+ for (i = 0; extensions[i] != NULL; i++) {
+ for (j = 0; gdm_supported_pam_extensions[j] != NULL; j++) {
+ if (g_strcmp0 (extensions[i], gdm_supported_pam_extensions[j]) == 0) {
+ g_ptr_array_add (array, g_strdup (gdm_supported_pam_extensions[j]));
+ break;
+ }
+ }
+ }
+ g_ptr_array_add (array, NULL);
+
+ if (array->len > 0)
+ filtered_extensions = g_strdupv ((char **) array->pdata);
+
+ g_ptr_array_free (array, TRUE);
+
+ return filtered_extensions;
+}
+
static gboolean
gdm_session_worker_handle_setup (GdmDBusWorker *object,
GDBusMethodInvocation *invocation,
const char *service,
+ const char * const *extensions,
const char *x11_display_name,
const char *x11_authority_file,
const char *console,
@@ -2829,6 +2911,7 @@ gdm_session_worker_handle_setup (GdmDBusWorker *object,
validate_and_queue_state_change (worker, invocation, GDM_SESSION_WORKER_STATE_SETUP_COMPLETE);
worker->priv->service = g_strdup (service);
+ worker->priv->extensions = filter_extensions (extensions);
worker->priv->x11_display_name = g_strdup (x11_display_name);
worker->priv->x11_authority_file = g_strdup (x11_authority_file);
worker->priv->display_device = g_strdup (console);
@@ -2854,6 +2937,7 @@ static gboolean
gdm_session_worker_handle_setup_for_user (GdmDBusWorker *object,
GDBusMethodInvocation *invocation,
const char *service,
+ const char * const *extensions,
const char *username,
const char *x11_display_name,
const char *x11_authority_file,
@@ -2869,6 +2953,7 @@ gdm_session_worker_handle_setup_for_user (GdmDBusWorker *object,
return TRUE;
worker->priv->service = g_strdup (service);
+ worker->priv->extensions = filter_extensions (extensions);
worker->priv->x11_display_name = g_strdup (x11_display_name);
worker->priv->x11_authority_file = g_strdup (x11_authority_file);
worker->priv->display_device = g_strdup (console);
@@ -3308,6 +3393,7 @@ gdm_session_worker_finalize (GObject *object)
g_free (worker->priv->username);
g_free (worker->priv->server_address);
g_strfreev (worker->priv->arguments);
+ g_strfreev (worker->priv->extensions);
g_hash_table_unref (worker->priv->reauthentication_requests);
diff --git a/daemon/gdm-session-worker.h b/daemon/gdm-session-worker.h
index cba3a4ec..5603e80e 100644
--- a/daemon/gdm-session-worker.h
+++ b/daemon/gdm-session-worker.h
@@ -52,7 +52,5 @@ GType gdm_session_worker_get_type (void);
GdmSessionWorker * gdm_session_worker_new (const char *server_address,
gboolean is_for_reauth) G_GNUC_MALLOC;
-
G_END_DECLS
-
#endif /* GDM_SESSION_WORKER_H */
diff --git a/daemon/gdm-session-worker.xml b/daemon/gdm-session-worker.xml
index 9f6d8b35..8773cce6 100644
--- a/daemon/gdm-session-worker.xml
+++ b/daemon/gdm-session-worker.xml
@@ -26,6 +26,7 @@
</method>
<method name="Setup">
<arg name="service_name" direction="in" type="s"/>
+ <arg name="extensions" direction="in" type="as"/>
<arg name="x11_display_name" direction="in" type="s"/>
<arg name="x11_authority_file" direction="in" type="s"/>
<arg name="display_device" direction="in" type="s"/>
@@ -36,6 +37,7 @@
</method>
<method name="SetupForUser">
<arg name="service_name" direction="in" type="s"/>
+ <arg name="extensions" direction="in" type="as"/>
<arg name="user_name" direction="in" type="s"/>
<arg name="x11_display_name" direction="in" type="s"/>
<arg name="x11_authority_file" direction="in" type="s"/>
diff --git a/daemon/gdm-session.c b/daemon/gdm-session.c
index 0b832231..f7d85da7 100644
--- a/daemon/gdm-session.c
+++ b/daemon/gdm-session.c
@@ -101,6 +101,7 @@ struct _GdmSessionPrivate
char **conversation_environment;
GdmDBusUserVerifier *user_verifier_interface;
+ GHashTable *user_verifier_extensions;
GdmDBusGreeter *greeter_interface;
GdmDBusRemoteGreeter *remote_greeter_interface;
GdmDBusChooser *chooser_interface;
@@ -1226,6 +1227,37 @@ begin_verification_conversation (GdmSession *self,
return conversation;
}
+static void
+unexport_and_free_user_verifier_extension (GDBusInterfaceSkeleton *interface)
+{
+ g_dbus_interface_skeleton_unexport (interface);
+
+ g_object_run_dispose (G_OBJECT (interface));
+ g_object_unref (interface);
+}
+
+static gboolean
+gdm_session_handle_client_enable_extensions (GdmDBusUserVerifier *user_verifier_interface,
+ GDBusMethodInvocation *invocation,
+ const char * const * extensions,
+ GDBusConnection *connection)
+{
+ GdmSession *self = g_object_get_data (G_OBJECT (connection), "gdm-session");
+ if (self->priv->user_verifier_extensions == NULL) {
+ self->priv->user_verifier_extensions = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ NULL,
+ (GDestroyNotify)
+ unexport_and_free_user_verifier_extension);
+ } else {
+ g_hash_table_remove_all (self->priv->user_verifier_extensions);
+ }
+
+
+ gdm_dbus_user_verifier_complete_enable_extensions (user_verifier_interface, invocation);
+
+ return TRUE;
+}
static gboolean
gdm_session_handle_client_begin_verification (GdmDBusUserVerifier *user_verifier_interface,
GDBusMethodInvocation *invocation,
@@ -1380,6 +1412,13 @@ export_user_verifier_interface (GdmSession *self,
{
GdmDBusUserVerifier *user_verifier_interface;
user_verifier_interface = GDM_DBUS_USER_VERIFIER (gdm_dbus_user_verifier_skeleton_new ());
+
+ g_object_set_data (G_OBJECT (connection), "gdm-session", self);
+
+ g_signal_connect (user_verifier_interface,
+ "handle-enable-extensions",
+ G_CALLBACK (gdm_session_handle_client_enable_extensions),
+ connection);
g_signal_connect (user_verifier_interface,
"handle-begin-verification",
G_CALLBACK (gdm_session_handle_client_begin_verification),
@@ -2110,10 +2149,17 @@ send_setup (GdmSession *self,
const char *display_seat_id;
const char *display_hostname;
const char *display_x11_authority_file;
+ const char **extensions;
GdmSessionConversation *conversation;
g_assert (service_name != NULL);
+ if (self->priv->user_verifier_extensions != NULL) {
+ extensions = (const char **) g_hash_table_get_keys_as_array (self->priv->user_verifier_extensions, NULL);
+ } else {
+ extensions = NULL;
+ }
+
if (self->priv->display_name != NULL) {
display_name = self->priv->display_name;
} else {
@@ -2146,6 +2192,7 @@ send_setup (GdmSession *self,
if (conversation != NULL) {
gdm_dbus_worker_call_setup (conversation->worker_proxy,
service_name,
+ extensions,
display_name,
display_x11_authority_file,
display_device,
@@ -2157,6 +2204,8 @@ send_setup (GdmSession *self,
(GAsyncReadyCallback) on_setup_complete_cb,
conversation);
}
+
+ g_free (extensions);
}
static void
@@ -2169,12 +2218,19 @@ send_setup_for_user (GdmSession *self,
const char *display_hostname;
const char *display_x11_authority_file;
const char *selected_user;
+ const char **extensions;
GdmSessionConversation *conversation;
g_assert (service_name != NULL);
conversation = find_conversation_by_name (self, service_name);
+ if (self->priv->user_verifier_extensions != NULL) {
+ extensions = (const char **) g_hash_table_get_keys_as_array (self->priv->user_verifier_extensions, NULL);
+ } else {
+ extensions = NULL;
+ }
+
if (self->priv->display_name != NULL) {
display_name = self->priv->display_name;
} else {
@@ -2211,6 +2267,7 @@ send_setup_for_user (GdmSession *self,
if (conversation != NULL) {
gdm_dbus_worker_call_setup_for_user (conversation->worker_proxy,
service_name,
+ extensions,
selected_user,
display_name,
display_x11_authority_file,
@@ -3424,6 +3481,8 @@ gdm_session_dispose (GObject *object)
g_hash_table_unref);
g_clear_object (&self->priv->user_verifier_interface);
+ g_clear_pointer (&self->priv->user_verifier_extensions,
+ g_hash_table_unref);
g_clear_object (&self->priv->greeter_interface);
g_clear_object (&self->priv->chooser_interface);
diff --git a/daemon/gdm-session.xml b/daemon/gdm-session.xml
index 9d44005b..af2976a4 100644
--- a/daemon/gdm-session.xml
+++ b/daemon/gdm-session.xml
@@ -24,6 +24,9 @@
</method>
</interface>
<interface name="org.gnome.DisplayManager.UserVerifier">
+ <method name="EnableExtensions">
+ <arg name="extensions" direction="in" type="as"/>
+ </method>
<method name="BeginVerification">
<arg name="service_name" direction="in" type="s"/>
</method>
diff --git a/pam-extensions/Makefile.am b/pam-extensions/Makefile.am
new file mode 100644
index 00000000..572494ac
--- /dev/null
+++ b/pam-extensions/Makefile.am
@@ -0,0 +1,23 @@
+NULL =
+
+AM_CPPFLAGS = \
+ -I$(srcdir) \
+ -I$(builddir) \
+ -I$(top_srcdir) \
+ -I$(top_builddir) \
+ -DG_LOG_DOMAIN=\"Gdm\" \
+ -DDMCONFDIR=\""$(dmconfdir)"\" \
+ -DDATADIR=\""$(datadir)"\" \
+ $(NULL)
+
+if SUPPORTS_PAM_EXTENSIONS
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = gdm-pam-extensions.pc
+
+pam_extensions_includedir = $(includedir)/gdm
+pam_extensions_include_HEADERS = gdm-pam-extensions.h
+endif
+
+EXTRA_DIST = \
+ gdm-pam-extensions.pc \
+ $(NULL)
diff --git a/pam-extensions/gdm-pam-extensions.h b/pam-extensions/gdm-pam-extensions.h
new file mode 100644
index 00000000..d3bec81f
--- /dev/null
+++ b/pam-extensions/gdm-pam-extensions.h
@@ -0,0 +1,133 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2017 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef GDM_PAM_EXTENSIONS_H
+#define GDM_PAM_EXTENSIONS_H
+
+#include <alloca.h>
+#include <endian.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <limits.h>
+
+#include <security/pam_appl.h>
+
+typedef struct {
+ uint32_t length;
+
+ unsigned char type;
+ unsigned char data[];
+} GdmPamExtensionMessage;
+
+#define GDM_PAM_EXTENSION_MESSAGE_FROM_PAM_MESSAGE(query) (GdmPamExtensionMessage *) (void *) query->msg
+#define GDM_PAM_EXTENSION_MESSAGE_TO_PAM_REPLY(msg) (char *) (void *) msg
+#define GDM_PAM_EXTENSION_MESSAGE_TO_BINARY_PROMPT_MESSAGE(extended_message, binary_message) \
+{ \
+ (binary_message)->msg_style = PAM_BINARY_PROMPT; \
+ (binary_message)->msg = (void *) extended_message; \
+}
+#define GDM_PAM_EXTENSION_MESSAGE_TRUNCATED(msg) be32toh(msg->length) < sizeof (GdmPamExtensionMessage)
+#define GDM_PAM_EXTENSION_MESSAGE_INVALID_TYPE(msg) \
+({ \
+ bool _invalid = true; \
+ int _n = -1; \
+ const char *_supported_extensions; \
+ _supported_extensions = getenv ("GDM_SUPPORTED_PAM_EXTENSIONS"); \
+ if (_supported_extensions != NULL) { \
+ const char *_p = _supported_extensions; \
+ while (*_p != '\0' && _n < UCHAR_MAX) { \
+ size_t _length; \
+ _length = strcspn (_p, " "); \
+ if (_length > 0) \
+ _n++; \
+ _p += _length; \
+ _length = strspn (_p, " "); \
+ _p += _length; \
+ } \
+ if (_n >= msg->type) \
+ _invalid = false; \
+ } \
+ _invalid; \
+})
+#define GDM_PAM_EXTENSION_MESSAGE_MATCH(msg, supported_extensions, name) (strcmp (supported_extensions[msg->type], name) == 0)
+
+/* environment block should be a statically allocated chunk of memory. This is important because
+ * putenv() will leak otherwise (and setenv isn't thread safe)
+ */
+#define GDM_PAM_EXTENSION_ADVERTISE_SUPPORTED_EXTENSIONS(environment_block, supported_extensions) \
+{ \
+ size_t _size = 0; \
+ unsigned char _t, _num_chunks; \
+ char *_p; \
+ _p = environment_block; \
+ _p = stpncpy (_p, "GDM_SUPPORTED_PAM_EXTENSIONS", sizeof(environment_block)); \
+ *_p = '\0'; \
+ _size += strlen (_p); \
+ for (_t = 0; supported_extensions[_t] != NULL && _t <= UCHAR_MAX; _t++) {\
+ size_t _next_chunk = strlen (supported_extensions[_t]) + strlen (" "); \
+ if (_size + _next_chunk >= sizeof (environment_block)) \
+ break; \
+ _size += _next_chunk; \
+ }\
+ _num_chunks = _t; \
+ if (_t != 0) { \
+ _p = stpcpy (_p, "="); \
+ for (_t = 0; _t < _num_chunks; _t++) { \
+ if (_t != 0) \
+ _p = stpcpy (_p, " "); \
+ _p = stpcpy (_p, supported_extensions[_t]); \
+ } \
+ *_p = '\0'; \
+ putenv (environment_block); \
+ } \
+}
+
+#define GDM_PAM_EXTENSION_LOOK_UP_TYPE(name, extension_type) \
+({ \
+ bool _supported = false; \
+ unsigned char _t = 0; \
+ const char *_supported_extensions; \
+ _supported_extensions = getenv ("GDM_SUPPORTED_PAM_EXTENSIONS"); \
+ if (_supported_extensions != NULL) { \
+ const char *_p = _supported_extensions; \
+ while (*_p != '\0') { \
+ size_t _length; \
+ _length = strcspn (_p, " "); \
+ if (strncmp (_p, name, _length) == 0) { \
+ _supported = true; \
+ break; \
+ } \
+ _p += _length; \
+ _length = strspn (_p, " "); \
+ _p += _length; \
+ if (_t >= UCHAR_MAX) { \
+ break; \
+ } \
+ _t++; \
+ } \
+ if (_supported && extension_type != NULL) \
+ *extension_type = _t; \
+ } \
+ _supported; \
+})
+
+#define GDM_PAM_EXTENSION_SUPPORTED(name) GDM_PAM_EXTENSION_LOOK_UP_TYPE(name, (unsigned char *) NULL)
+
+#endif
diff --git a/pam-extensions/gdm-pam-extensions.pc.in b/pam-extensions/gdm-pam-extensions.pc.in
new file mode 100644
index 00000000..5fc64b01
--- /dev/null
+++ b/pam-extensions/gdm-pam-extensions.pc.in
@@ -0,0 +1,9 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: GDM PAM Extensions
+Description: Macros for custom protocols over PAM
+Version: @VERSION@
+Cflags: -I${includedir}/gdm