[pkg-fso-commits] [SCM] Automatic Display Manager branch, master, updated. debian/0.1-52-g76526e2

Enrico Zini enrico at enricozini.org
Mon Feb 23 18:02:44 UTC 2009


The following commit has been merged in the master branch:
commit 38d64463174c8d01a666714a7915474768dc7ef2
Author: Enrico Zini <enrico at enricozini.org>
Date:   Mon Feb 2 12:13:24 2009 +0000

    Started pam-helper as a fork of su

diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..1a9e13a
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,2 @@
+pam-helper: pam-helper.c
+	gcc -o $@ $< -lpam -lpam_misc -Wall
diff --git a/pam-helper.c b/pam-helper.c
new file mode 100644
index 0000000..e45afa6
--- /dev/null
+++ b/pam-helper.c
@@ -0,0 +1,563 @@
+/*
+ * Copyright 1989 - 1994, Julianne Frances Haugh
+ * Copyright 2008, Enrico Zini
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Julianne F. Haugh nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/* Some parts substantially derived from an ancestor of: */
+/* su for GNU.  Run a shell with substitute user and group IDs.
+   Copyright (C) 1992-2003 Free Software Foundation, 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, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+
+#define PACKAGE "nodm"
+
+#include <getopt.h>
+#include <grp.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <syslog.h>
+
+#include <security/pam_appl.h>
+#include <security/pam_misc.h>
+
+static struct pam_conv conv = {
+	misc_conv,
+	NULL
+};
+
+/* compatibility with different versions of Linux-PAM */
+#ifndef PAM_ESTABLISH_CRED
+#define PAM_ESTABLISH_CRED PAM_CRED_ESTABLISH
+#endif
+#ifndef PAM_DELETE_CRED
+#define PAM_DELETE_CRED PAM_CRED_DELETE
+#endif
+#ifndef PAM_NEW_AUTHTOK_REQD
+#define PAM_NEW_AUTHTOK_REQD PAM_AUTHTOKEN_REQD
+#endif
+#ifndef PAM_DATA_SILENT
+#define PAM_DATA_SILENT 0
+#endif
+
+#define LOG_WARN LOG_WARNING
+#define SYSLOG(x) syslog x
+#define SYSLOG_OPTIONS (LOG_PID)
+#define SYSLOG_FACILITY LOG_AUTHPRIV
+#define OPENLOG(progname) openlog(progname, SYSLOG_OPTIONS, SYSLOG_FACILITY)
+#define _(...) (__VA_ARGS__)
+/* Copy string pointed by B to array A with size checking.  It was originally
+   in lmain.c but is _very_ useful elsewhere.  Some setuid root programs with
+   very sloppy coding used to assume that BUFSIZ will always be enough...  */
+                                        /* danger - side effects */
+#define STRFCPY(A,B) \
+        (strncpy((A), (B), sizeof(A) - 1), (A)[sizeof(A) - 1] = '\0')
+
+/*
+ * Exit codes used by shadow programs
+ */
+#define E_SUCCESS               0       /* success */
+#define E_NOPERM                1       /* permission denied */
+#define E_USAGE                 2       /* invalid command syntax */
+#define E_BAD_ARG               3       /* invalid argument to option */
+#define E_PASSWD_NOTFOUND       14      /* not found password file */
+#define E_SHADOW_NOTFOUND       15      /* not found shadow password file */
+#define E_GROUP_NOTFOUND        16      /* not found group file */
+#define E_GSHADOW_NOTFOUND      17      /* not found shadow group file */
+#define E_CMD_NOEXEC            126     /* can't run command/shell */
+#define E_CMD_NOTFOUND          127     /* can't find command/shell to run */
+
+#define RETSIGTYPE void
+
+/*
+ * Assorted #defines to control su's behavior
+ */
+/*
+ * Global variables
+ */
+/* not needed by sulog.c anymore */
+static char name[BUFSIZ];
+static char oldname[BUFSIZ];
+
+static pam_handle_t *pamh = NULL;
+static int caught = 0;
+
+static char *Prog;
+struct passwd pwent;
+
+/*
+ * External identifiers
+ */
+
+extern char **newenvp;
+extern char **environ;
+extern size_t newenvc;
+
+/* local function prototypes */
+
+static char *Basename (char *str)
+{
+	char *cp = strrchr (str, '/');
+
+	return cp ? cp + 1 : str;
+}
+
+/*
+ * setup_uid_gid() split in two functions for PAM support -
+ * pam_setcred() needs to be called after initgroups(), but
+ * before setuid().
+ */
+static int setup_groups (const struct passwd *info)
+{
+	/*
+	 * Set the real group ID to the primary group ID in the password
+	 * file.
+	 */
+	if (setgid (info->pw_gid) == -1) {
+		perror ("setgid");
+		SYSLOG ((LOG_ERR, "bad group ID `%d' for user `%s': %m\n",
+			 info->pw_gid, info->pw_name));
+		closelog ();
+		return -1;
+	}
+#ifdef HAVE_INITGROUPS
+	/*
+	 * For systems which support multiple concurrent groups, go get
+	 * the group set from the /etc/group file.
+	 */
+	if (initgroups (info->pw_name, info->pw_gid) == -1) {
+		perror ("initgroups");
+		SYSLOG ((LOG_ERR, "initgroups failed for user `%s': %m\n",
+			 info->pw_name));
+		closelog ();
+		return -1;
+	}
+#endif
+	return 0;
+}
+
+int change_uid (const struct passwd *info)
+{
+	/*
+	 * Set the real UID to the UID value in the password file.
+	 */
+	if (setuid (info->pw_uid)) {
+		perror ("setuid");
+		SYSLOG ((LOG_ERR, "bad user ID `%d' for user `%s': %m\n",
+			 (int) info->pw_uid, info->pw_name));
+		closelog ();
+		return -1;
+	}
+	return 0;
+}
+
+/*
+ * sulog - log a SU command execution result
+ */
+static void sulog (const char *tty, int success, const char *oldname, const char *name)
+{
+        if (success) {
+                SYSLOG ((LOG_INFO,
+                        "Successful su for %s by %s",name,oldname));
+        } else {
+                SYSLOG ((LOG_NOTICE,
+                        "FAILED su for %s by %s",name,oldname));
+        }
+}
+
+static void su_failure (const char *tty)
+{
+	sulog (tty, 0, oldname, name);	/* log failed attempt */
+#ifdef USE_SYSLOG
+	if (getdef_bool ("SYSLOG_SU_ENAB"))
+		SYSLOG ((pwent.pw_uid ? LOG_INFO : LOG_NOTICE,
+			 "- %s %s:%s", tty,
+			 oldname[0] ? oldname : "???", name[0] ? name : "???"));
+	closelog ();
+#endif
+	exit (1);
+}
+
+
+/* Signal handler for parent process later */
+static void catch_signals (int sig)
+{
+	++caught;
+}
+
+/* This I ripped out of su.c from sh-utils after the Mandrake pam patch
+ * have been applied.  Some work was needed to get it integrated into
+ * su.c from shadow.
+ */
+static void run_shell ()
+{
+	int child;
+	sigset_t ourset;
+	int status;
+	int ret;
+	char* argv0 = getenv("NODM_XINIT");
+	if (argv0 == NULL)
+		argv0 = "/usr/bin/xinit";
+
+	child = fork ();
+	if (child == 0) {	/* child shell */
+		/*
+		 * PAM_DATA_SILENT is not supported by some modules, and
+		 * there is no strong need to clean up the process space's
+		 * memory since we will either call exec or exit.
+		pam_end (pamh, PAM_SUCCESS | PAM_DATA_SILENT);
+		 */
+		char* args[5];
+		args[0] = argv0;
+		args[1] = getenv("NODM_XSESSION");
+		if (args[1] == NULL) args[1] = "/etc/X11/Xsession";
+		args[2] = "--";
+		args[3] = getenv("NODM_X_OPTIONS");
+		args[4] = NULL;
+		(void) execv (args[0], (char **) args);
+		exit (errno == ENOENT ? E_CMD_NOTFOUND : E_CMD_NOEXEC);
+	} else if (child == -1) {
+		(void) fprintf (stderr, "%s: Cannot fork user shell\n", Prog);
+		SYSLOG ((LOG_WARN, "Cannot execute %s", argv0));
+		closelog ();
+		exit (1);
+	}
+	/* parent only */
+	sigfillset (&ourset);
+	if (sigprocmask (SIG_BLOCK, &ourset, NULL)) {
+		(void) fprintf (stderr, "%s: signal malfunction\n", Prog);
+		caught = 1;
+	}
+	if (!caught) {
+		struct sigaction action;
+
+		action.sa_handler = catch_signals;
+		sigemptyset (&action.sa_mask);
+		action.sa_flags = 0;
+		sigemptyset (&ourset);
+
+		if (sigaddset (&ourset, SIGTERM)
+		    || sigaddset (&ourset, SIGALRM)
+		    || sigaction (SIGTERM, &action, NULL)
+		    || sigprocmask (SIG_UNBLOCK, &ourset, NULL)
+		    ) {
+			fprintf (stderr,
+				 "%s: signal masking malfunction\n", Prog);
+			caught = 1;
+		}
+	}
+
+	if (!caught) {
+		do {
+			int pid;
+
+			pid = waitpid (-1, &status, WUNTRACED);
+
+			if (WIFSTOPPED (status)) {
+				kill (getpid (), SIGSTOP);
+				/* once we get here, we must have resumed */
+				kill (pid, SIGCONT);
+			}
+		} while (WIFSTOPPED (status));
+	}
+
+	if (caught) {
+		fprintf (stderr, "\nSession terminated, killing shell...");
+		kill (child, SIGTERM);
+	}
+
+	ret = pam_close_session (pamh, 0);
+	if (ret != PAM_SUCCESS) {
+		SYSLOG ((LOG_ERR, "pam_close_session: %s",
+			 pam_strerror (pamh, ret)));
+		fprintf (stderr, _("%s: %s\n"), Prog, pam_strerror (pamh, ret));
+		pam_end (pamh, ret);
+		exit (1);
+	}
+
+	ret = pam_end (pamh, PAM_SUCCESS);
+
+	if (caught) {
+		sleep (2);
+		kill (child, SIGKILL);
+		fprintf (stderr, " ...killed.\n");
+		exit (-1);
+	}
+
+	exit (WIFEXITED (status)
+	      ? WEXITSTATUS (status)
+	      : WTERMSIG (status) + 128);
+}
+
+static struct passwd *get_my_pwent (void)
+{
+	struct passwd *pw;
+	const char *cp = getlogin ();
+	uid_t ruid = getuid ();
+
+	/*
+	 * Try getlogin() first - if it fails or returns a non-existent
+	 * username, or a username which doesn't match the real UID, fall
+	 * back to getpwuid(getuid()).  This should work reasonably with
+	 * usernames longer than the utmp limit (8 characters), as well as
+	 * shared UIDs - but not both at the same time...
+	 *
+	 * XXX - when running from su, will return the current user (not
+	 * the original user, like getlogin() does).  Does this matter?
+	 */
+	if (cp && *cp && (pw = getpwnam (cp)) && pw->pw_uid == ruid)
+		return pw;
+
+	return getpwuid (ruid);
+}
+
+/*
+ * su - switch user id
+ *
+ *	su changes the user's ids to the values for the specified user.  if
+ *	no new user name is specified, "root" is used by default.
+ *
+ *	Any additional arguments are passed to the user's shell. In
+ *	particular, the argument "-c" will cause the next argument to be
+ *	interpreted as a command by the common shell programs.
+ */
+int main (int argc, char **argv)
+{
+	char *cp;
+	const char *tty = 0;	/* Name of tty SU is run from        */
+	int amroot = 0;
+	uid_t my_uid;
+	struct passwd *pw = 0;
+	char **envcp;
+	int ret;
+
+	/*
+	 * Get the program name. The program name is used as a prefix to
+	 * most error messages.
+	 */
+	Prog = Basename (argv[0]);
+
+	OPENLOG ("su");
+
+	/*
+	 * Process the command line arguments. 
+	 */
+
+	my_uid = getuid ();
+	amroot = (my_uid == 0);
+
+	/*
+	 * Get the tty name. Entries will be logged indicating that the user
+	 * tried to change to the named new user from the current terminal.
+	 */
+	if (isatty (0) && (cp = ttyname (0))) {
+		if (strncmp (cp, "/dev/", 5) == 0)
+			tty = cp + 5;
+		else
+			tty = cp;
+	} else {
+		/*
+		 * Be more paranoid, like su from SimplePAMApps.  --marekm
+		 */
+		if (!amroot) {
+			fprintf (stderr,
+				 _("%s: must be run from a terminal\n"), Prog);
+			exit (1);
+		}
+		tty = "???";
+	}
+
+	if (getenv("NODM_USER") == NULL)
+		strcpy(name, "root");
+	else
+		strncpy(name, getenv("NODM_USER"), BUFSIZ);
+
+	/*
+	 * Get the user's real name. The current UID is used to determine
+	 * who has executed su. That user ID must exist.
+	 */
+	pw = get_my_pwent ();
+	if (!pw) {
+		SYSLOG ((LOG_CRIT, "Unknown UID: %u", my_uid));
+		su_failure (tty);
+	}
+	STRFCPY (oldname, pw->pw_name);
+
+	ret = pam_start ("su", name, &conv, &pamh);
+	if (ret != PAM_SUCCESS) {
+		SYSLOG ((LOG_ERR, "pam_start: error %d", ret);
+			fprintf (stderr, _("%s: pam_start: error %d\n"),
+				 Prog, ret));
+		exit (1);
+	}
+
+	ret = pam_set_item (pamh, PAM_TTY, (const void *) tty);
+	if (ret == PAM_SUCCESS)
+		ret = pam_set_item (pamh, PAM_RUSER, (const void *) oldname);
+	if (ret != PAM_SUCCESS) {
+		SYSLOG ((LOG_ERR, "pam_set_item: %s",
+			 pam_strerror (pamh, ret)));
+		fprintf (stderr, _("%s: %s\n"), Prog, pam_strerror (pamh, ret));
+		pam_end (pamh, ret);
+		exit (1);
+	}
+
+	/*
+	 * This is the common point for validating a user whose name is
+	 * known. It will be reached either by normal processing, or if the
+	 * user is to be logged into a subsystem root.
+	 *
+	 * The password file entries for the user is gotten and the account
+	 * validated.
+	 */
+	if (!(pw = getpwnam (name))) {
+		(void) fprintf (stderr, _("Unknown id: %s\n"), name);
+		closelog ();
+		exit (1);
+	}
+	pwent = *pw;
+
+	signal (SIGINT, SIG_IGN);
+	signal (SIGQUIT, SIG_IGN);
+
+	ret = pam_acct_mgmt (pamh, 0);
+	if (ret != PAM_SUCCESS) {
+		if (amroot) {
+			fprintf (stderr, _("%s: %s\n(Ignored)\n"), Prog,
+				 pam_strerror (pamh, ret));
+		} else if (ret == PAM_NEW_AUTHTOK_REQD) {
+			ret = pam_chauthtok (pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
+			if (ret != PAM_SUCCESS) {
+				SYSLOG ((LOG_ERR, "pam_chauthtok: %s",
+					 pam_strerror (pamh, ret)));
+				fprintf (stderr, _("%s: %s\n"), Prog,
+					 pam_strerror (pamh, ret));
+				pam_end (pamh, ret);
+				su_failure (tty);
+			}
+		} else {
+			SYSLOG ((LOG_ERR, "pam_acct_mgmt: %s",
+				 pam_strerror (pamh, ret)));
+			fprintf (stderr, _("%s: %s\n"), Prog,
+				 pam_strerror (pamh, ret));
+			pam_end (pamh, ret);
+			su_failure (tty);
+		}
+	}
+
+	signal (SIGINT, SIG_DFL);
+	signal (SIGQUIT, SIG_DFL);
+
+	setenv ("PATH", "/bin:/usr/bin", 1);
+
+	sulog (tty, 1, oldname, name);	/* save SU information */
+
+#ifdef USE_SYSLOG
+	SYSLOG ((LOG_INFO, "+ %s %s:%s", tty,
+		 oldname[0] ? oldname : "???", name[0] ? name : "???"));
+#endif
+
+	/* set primary group id and supplementary groups */
+	if (setup_groups (&pwent)) {
+		pam_end (pamh, PAM_ABORT);
+		exit (1);
+	}
+
+	/*
+	 * pam_setcred() may do things like resource limits, console groups,
+	 * and much more, depending on the configured modules
+	 */
+	ret = pam_setcred (pamh, PAM_ESTABLISH_CRED);
+	if (ret != PAM_SUCCESS) {
+		SYSLOG ((LOG_ERR, "pam_setcred: %s", pam_strerror (pamh, ret)));
+		fprintf (stderr, _("%s: %s\n"), Prog, pam_strerror (pamh, ret));
+		pam_end (pamh, ret);
+		exit (1);
+	}
+
+	ret = pam_open_session (pamh, 0);
+	if (ret != PAM_SUCCESS) {
+		SYSLOG ((LOG_ERR, "pam_open_session: %s",
+			 pam_strerror (pamh, ret)));
+		fprintf (stderr, _("%s: %s\n"), Prog, pam_strerror (pamh, ret));
+		pam_setcred (pamh, PAM_DELETE_CRED);
+		pam_end (pamh, ret);
+		exit (1);
+	}
+
+	/* update environment with all pam set variables */
+	envcp = pam_getenvlist (pamh);
+	if (envcp) {
+		while (*envcp) {
+			putenv(*envcp);
+			envcp++;
+		}
+	}
+
+	/* become the new user */
+	if (change_uid (&pwent)) {
+		pam_close_session (pamh, 0);
+		pam_setcred (pamh, PAM_DELETE_CRED);
+		pam_end (pamh, PAM_ABORT);
+		exit (1);
+	}
+
+	setenv ("HOME", pwent.pw_dir, 1);
+	setenv ("USER", pwent.pw_name, 1);
+	setenv ("LOGNAME", pwent.pw_name, 1);
+
+	/*
+	 * This is a workaround for Linux libc bug/feature (?) - the
+	 * /dev/log file descriptor is open without the close-on-exec flag
+	 * and used to be passed to the new shell. There is "fcntl(LogFile,
+	 * F_SETFD, 1)" in libc/misc/syslog.c, but it is commented out (at
+	 * least in 5.4.33). Why?  --marekm
+	 */
+	closelog ();
+
+	run_shell();
+
+	/* NOT REACHED */
+	exit (1);
+}

-- 
Automatic Display Manager



More information about the pkg-fso-commits mailing list