[PATCH xdm] Allow users to specify environment variables to be passed to PAM by greeter

William Price bill at cotse.net
Tue Jul 19 19:25:42 UTC 2016


There are times when it is useful to pass environment variables to PAM on
a per display basis.  Since Xresource files in xdm can vary by display, a
resource string in one of these files is ideal.  Normally, pam.conf files
configure items on a per service basis, but a single XDM instance can
manage many local (or remote) displays.  One example of where this feature
was useful to the author was in getting ConsoleKit on FreeBSD to provide
active local sessions by setting CKCON_X11_DISPLAY_DEVICE=<tty>.  This
worked without ANY ConsoleKit related changes to XDM itself.  However,
it is likely that other PAM modules can also be configured in this manner,
making this patch generally useful.

Signed-off-by: William Price <bill at cotse.net>
---
 greeter/Login.c  |   6 +-
 greeter/Login.h  |   5 ++
 greeter/LoginP.h |   3 +
 greeter/greet.c  | 193 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 man/xdm.man      |  17 +++++
 5 files changed, 223 insertions(+), 1 deletion(-)

diff --git a/greeter/Login.c b/greeter/Login.c
index 6a99050..693f719 100644
--- a/greeter/Login.c
+++ b/greeter/Login.c
@@ -225,7 +225,11 @@ static XtResource resources[] = {
     {XtNechoPasswd, XtCEchoPasswd, XtRBoolean, sizeof(Boolean),
 	offset(echo_passwd), XtRImmediate, (XtPointer) False},
     {XtNechoPasswdChar, XtCEchoPasswdChar, XtRString,	sizeof (char *),
-	offset(echo_passwd_char), XtRString, (XtPointer) "*" }
+	offset(echo_passwd_char), XtRString, (XtPointer) "*" },
+# ifdef USE_PAM
+    {XtNpamAddEnviron, XtCPamAddEnviron, XtRString, sizeof (char *),
+        offset(pamAddEnviron), XtRString, ""}
+# endif
 };
 
 #undef offset
diff --git a/greeter/Login.h b/greeter/Login.h
index 6af3369..13b8d14 100644
--- a/greeter/Login.h
+++ b/greeter/Login.h
@@ -149,6 +149,11 @@ from The Open Group.
 # define XtNchangePasswdMessage	"changePasswdMessage"
 # define XtCChangePasswdMessage	"ChangePasswdMessage"
 
+# ifdef USE_PAM
+# define XtNpamAddEnviron	"pamAddEnviron"
+# define XtCPamAddEnviron	"PamAddEnviron"
+# endif
+
 /* notifyDone interface definition */
 
 # ifdef __OpenBSD__
diff --git a/greeter/LoginP.h b/greeter/LoginP.h
index 57ed182..05bc7f6 100644
--- a/greeter/LoginP.h
+++ b/greeter/LoginP.h
@@ -169,6 +169,9 @@ typedef struct {
 	XftColor	greetcolor;	/* greeting color */
 	XftColor	failcolor;	/* failure color */
 # endif
+# ifdef USE_PAM
+	char           *pamAddEnviron;	/* VAR="VAL" strings exported to PAM */
+# endif /* PAM */
    } LoginPart;
 
 /* Full instance record declaration */
diff --git a/greeter/greet.c b/greeter/greet.c
index 9b5cef4..da46391 100644
--- a/greeter/greet.c
+++ b/greeter/greet.c
@@ -87,6 +87,7 @@ from The Open Group.
 #endif
 
 #include <string.h>
+#include <ctype.h>
 
 #if defined(SECURE_RPC) && defined(sun)
 /* Go figure, there's no getdomainname() prototype available */
@@ -415,6 +416,189 @@ FailedLogin (struct display *d, const char *username)
     DrawFail (login);
 }
 
+#ifdef USE_PAM
+
+static char pamvar[4096];
+
+/* PAM_INT_TO_CHAR assumes caller checked that int is [0..UCHAR_MAX] */
+# if CHAR_MIN == 0
+#  define PAM_INT_TO_CHAR(i) ((char)(i))
+# elif CHAR_MIN == -128 && CHAR_MAX == 127
+#  define PAM_INT_TO_CHAR(i) (((i) >CHAR_MAX) ? (char)((i) - 256) : (char)(i))
+# else
+#  error Unable to handle range/precision of this platforms _char_ type
+# endif
+
+# define IS_PAM_START(c) \
+    (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z') || (c) == '_')
+# define IS_PAM_CHAR(c) (IS_PAM_START((c)) || ((c) >= '0' && (c) <= '9'))
+# define IS_PAM_WS(c) ((c) == ' ' || (c) == '\t')
+
+/* Returns false (0) on error */
+static int
+processEscapes(char *result, char **ptr)
+{
+    char *p = *ptr + 1;
+    int retval = 0;
+
+    *result = '\0';
+
+    if (*p == '\0')
+	goto out;
+
+    if (*p >= '0' && *p <= '7') {
+	char obuf[4];
+	size_t i = 0;
+	unsigned long uval;
+
+	/* no more than 3 octal digits read */
+	while (*p >= '0' && *p <= '7' && i < (sizeof(obuf) - 1))
+	    obuf[i++] = *p++;
+	obuf[i] = '\0';
+
+	uval = strtoul(obuf, NULL, 8);
+	if (uval > UCHAR_MAX)
+	    goto out;
+	*result = PAM_INT_TO_CHAR(uval);
+	retval = 1;
+	goto out;
+    }
+
+    if (*p == 'x') {
+	char obuf[3];
+	size_t i = 0;
+	unsigned long uval;
+
+	if (!isxdigit((unsigned char)*++p))
+	    goto out;
+
+	/* no more than 2 hex digits read */
+	while (isxdigit((unsigned char)*p) && i < (sizeof(obuf) - 1))
+	    obuf[i++] = *p++;
+	obuf[i] = '\0';
+
+	uval = strtoul(obuf, NULL, 16);
+	*result = PAM_INT_TO_CHAR(uval);
+	retval = 1;
+	goto out;
+    }
+
+    switch(*p++) {
+    case '\'':
+	*result = '\'';
+	break;
+    case '"':
+	*result = '"';
+	break;
+    case '?':
+	*result = '?';
+	break;
+    case '\\':
+	*result = '\\';
+	break;
+    case 'a':
+	*result = '\a';
+	break;
+    case 'b':
+	*result = '\b';
+	break;
+    case 'f':
+	*result = '\f';
+	break;
+    case 'n':
+	*result = '\n';
+	break;
+    case 'r':
+	*result = '\r';
+	break;
+    case 't':
+	*result = '\t';
+	break;
+    case 'v':
+	*result = '\v';
+	break;
+    default:
+	goto out;
+    }
+
+    retval = 1;
+out:
+    *ptr = p;
+    return retval;
+}
+
+static char *
+nextPamEnviron(char *p)
+{
+    char *const start = p;
+    size_t i;
+
+    if (!p)
+	return NULL;
+
+    while (IS_PAM_WS(*p)) ++p;
+    if (*p == '\0')
+	return NULL;
+
+    if (!IS_PAM_START(*p)) {
+	LogError("PAM environ: illegal name: %s\n", start);
+	return NULL;
+    }
+
+    i = 0;
+    while (IS_PAM_CHAR(*p)) {
+	if (i < (sizeof(pamvar) - 2))
+	    pamvar[i++] = *p;
+	else {
+	    LogError("PAM environ: name too long: %s\n", start);
+	    return NULL;
+	}
+	++p;
+    }
+    pamvar[i++] = '=';
+
+    if (*p != '=' || *(p+1) != '"') {
+	LogError("PAM environ: missing opening quote: %s\n", start);
+	return NULL;
+    }
+
+    p += 2;
+
+    while (*p != '"') {
+	if (*p == '\0') {
+	    LogError("PAM environ: missing closing quote: %s\n", start);
+	    return NULL;
+	}
+
+	if (i >= (sizeof(pamvar) - 1)) {
+	    LogError("PAM environ: value too long: %s\n", start);
+	    return NULL;
+	}
+
+	if (*p == '\\') {
+	    char c;
+	    if (!processEscapes(&c, &p)) {
+		LogError("PAM environ: invalid escape sequence: %s\n", start);
+		return NULL;
+	    }
+	    pamvar[i++] = c;
+	    continue;
+	}
+
+	pamvar[i++] = *p++;
+    }
+    pamvar[i] = '\0';
+
+    return p + 1;
+}
+
+#undef IS_PAM_START
+#undef IS_PAM_CHAR
+#undef IS_PAM_WS
+#undef PAM_INT_TO_CHAR
+
+#endif /* PAM */
+
 _X_EXPORT
 greet_user_rtn GreetUser(
     struct display          *d,
@@ -555,6 +739,15 @@ greet_user_rtn GreetUser(
 	    RUN_AND_CHECK_PAM_ERROR(pam_set_item, (*pamhp, PAM_TTY, ttyname));
 	}
 
+	char *envp = NULL;
+	XtVaGetValues (login, XtNpamAddEnviron, &envp, NULL);
+	while (envp)
+	{
+	    envp = nextPamEnviron(envp);
+	    if (envp)
+		RUN_AND_CHECK_PAM_ERROR(pam_putenv, (*pamhp, pamvar));
+	}
+
 	if (!greet->allow_null_passwd) {
 	    pam_flags |= PAM_DISALLOW_NULL_AUTHTOK;
 	}
diff --git a/man/xdm.man b/man/xdm.man
index 0648d71..0a26aa0 100644
--- a/man/xdm.man
+++ b/man/xdm.man
@@ -1068,6 +1068,23 @@ The default is ``false''.
 Character to display if echoPasswd is true.  The default is ``*''.
 If set to an empty value, the cursor will advance for each character input,
 but no text will be drawn.
+.IP "\fBxlogin.Login.pamAddEnviron\fP"
+In PAM enabled builds of XDM, specifies a set of environment variables
+to pass to PAM before the authentication sequence begins.
+This is useful to configure certain PAM modules on a per display basis.
+This resource must be set to a string of one or more
+``VARNAME="VALUE"'' pairs, with the pairs separated by spaces or tabs.
+``VARNAME'' must be named using only the characters ``A-Z'', ``a-z'', ``0-9''
+and ``_'' (first character of name may not be a digit).
+The character sequence ``="'' must \fIimmediately\fP follow ``VARNAME''
+with no intervening spaces.
+``VALUE'' must be encoded inside the quotes.
+All characters inside the quotes are part of ``VALUE'',
+including leading/trailing whitespace.
+``VALUE'' strings may contain standard C99 style escape sequences,
+except for ``universal'' characters (i.e. escapes starting ``\\u'' or ``\\U'').
+For example, ``X="a\\x21b\\nc\\\\" T=""'' sets X to ``a!b<newline>c\\''
+and T to the empty string.
 .IP "\fBxlogin.Login.translations\fP"
 This specifies the translations used for the login widget.  Refer to the X
 Toolkit documentation for a complete discussion on translations.  The default
-- 
2.9.2



More information about the xorg-devel mailing list