changeset 4344:4cd49af6f951

6558487 zlogin should not call getpwnam() after zone_enter() during non-interactive zlogin -l 6561300 zlogin error messages are not locale specific for non-native zones
author sl108498
date Tue, 29 May 2007 15:28:30 -0700
parents d010db0a849a
children 20eb5d8abe27
files usr/src/cmd/zlogin/Makefile usr/src/cmd/zlogin/zlogin.c usr/src/lib/brand/lx/zone/config.xml usr/src/lib/brand/native/zone/config.xml usr/src/lib/brand/sn1/zone/config.xml usr/src/lib/libbrand/common/libbrand.c usr/src/lib/libbrand/common/libbrand.h usr/src/lib/libbrand/common/mapfile-vers usr/src/lib/libbrand/dtd/brand.dtd.1
diffstat 9 files changed, 145 insertions(+), 17 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/cmd/zlogin/Makefile	Tue May 29 15:13:25 2007 -0700
+++ b/usr/src/cmd/zlogin/Makefile	Tue May 29 15:28:30 2007 -0700
@@ -19,7 +19,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 # ident	"%Z%%M%	%I%	%E% SMI"
@@ -28,6 +28,7 @@
 
 include ../Makefile.cmd
 
+LINTFLAGS += -u
 LDLIBS += -lsocket -lzonecfg -lcontract -lbrand
 CFLAGS += $(CCVERBOSE)
 FILEMODE = 0555
--- a/usr/src/cmd/zlogin/zlogin.c	Tue May 29 15:13:25 2007 -0700
+++ b/usr/src/cmd/zlogin/zlogin.c	Tue May 29 15:28:30 2007 -0700
@@ -58,12 +58,13 @@
 #include <sys/contract/process.h>
 #include <sys/ctfs.h>
 #include <sys/brand.h>
-
+#include <sys/wait.h>
 #include <alloca.h>
 #include <assert.h>
 #include <ctype.h>
 #include <door.h>
 #include <errno.h>
+#include <nss_dbdefs.h>
 #include <poll.h>
 #include <priv.h>
 #include <pwd.h>
@@ -745,20 +746,20 @@
  * This is the main I/O loop, and is shared across all zlogin modes.
  * Parameters:
  * 	stdin_fd:  The fd representing 'stdin' for the slave side; input to
- *	           the zone will be written here.
+ *		   the zone will be written here.
  *
  * 	appin_fd:  The fd representing the other end of the 'stdin' pipe (when
  *		   we're running non-interactive); used in process_raw_input
  *		   to ensure we don't fill up the application's stdin pipe.
  *
  *	stdout_fd: The fd representing 'stdout' for the slave side; output
- *	           from the zone will arrive here.
+ *		   from the zone will arrive here.
  *
  *	stderr_fd: The fd representing 'stderr' for the slave side; output
- *	           from the zone will arrive here.
+ *		   from the zone will arrive here.
  *
  *	raw_mode:  If TRUE, then no processing (for example, for '~.') will
- *	           be performed on the input coming from STDIN.
+ *		   be performed on the input coming from STDIN.
  *
  * stderr_fd may be specified as -1 if there is no stderr (only non-interactive
  * mode supplies a stderr).
@@ -901,6 +902,62 @@
 	}
 }
 
+/*
+ * Fetch the user_cmd brand hook for getting a user's passwd(4) entry.
+ */
+static const char *
+zone_get_user_cmd(brand_handle_t bh, const char *login, char *user_cmd,
+    size_t len)
+{
+	bzero(user_cmd, sizeof (user_cmd));
+	if (brand_get_user_cmd(bh, login, user_cmd, len) != 0)
+		return (NULL);
+
+	return (user_cmd);
+}
+
+/* From libc */
+extern int str2passwd(const char *, int, void *, char *, int);
+
+/*
+ * exec() the user_cmd brand hook, and convert the output string to a
+ * struct passwd.  This is to be called after zone_enter().
+ *
+ */
+static struct passwd *
+zone_get_user_pw(const char *user_cmd, struct passwd *pwent, char *pwbuf,
+    int pwbuflen)
+{
+	char pwline[NSS_BUFLEN_PASSWD];
+	char *cin = NULL;
+	FILE *fin;
+	int status;
+
+	assert(getzoneid() != GLOBAL_ZONEID);
+
+	if ((fin = popen(user_cmd, "r")) == NULL)
+		return (NULL);
+
+	while (cin == NULL && !feof(fin))
+		cin = fgets(pwline, sizeof (pwline), fin);
+
+	if (cin == NULL) {
+		(void) pclose(fin);
+		return (NULL);
+	}
+
+	status = pclose(fin);
+	if (!WIFEXITED(status))
+		return (NULL);
+	if (WEXITSTATUS(status) != 0)
+		return (NULL);
+
+	if (str2passwd(pwline, sizeof (pwline), pwent, pwbuf, pwbuflen) == 0)
+		return (pwent);
+	else
+		return (NULL);
+}
+
 static char **
 zone_login_cmd(brand_handle_t bh, const char *login)
 {
@@ -1102,32 +1159,45 @@
  * that.
  */
 static char **
-prep_env_noninteractive(char *login, char **env)
+prep_env_noninteractive(const char *user_cmd, char **env)
 {
 	size_t size;
-	struct passwd *pw;
 	char **new_env;
 	int e, i;
 	char *estr;
 	char varmail[LOGNAME_MAX + 11]; /* strlen(/var/mail/) = 10, NUL */
+	char pwbuf[NSS_BUFLEN_PASSWD + 1];
+	struct passwd pwent;
+	struct passwd *pw = NULL;
 
 	assert(env != NULL);
 	assert(failsafe == 0);
 
 	/*
+	 * Exec the "user_cmd" brand hook to get a pwent for the
+	 * login user.  If this fails, HOME will be set to "/", SHELL
+	 * will be set to $DEFAULTSHELL, and we will continue to exec
+	 * SUPATH <login> -c <cmd>.
+	 */
+	pw = zone_get_user_pw(user_cmd, &pwent, pwbuf, sizeof (pwbuf));
+
+	/*
 	 * Get existing envp size.
 	 */
 	for (size = 0; env[size] != NULL; size++)
 		;
+
 	e = size;
 
 	/*
 	 * Finish filling out the environment; we duplicate the environment
 	 * setup described in login(1), for lack of a better precedent.
 	 */
-	if ((pw = getpwnam(login)) != NULL) {
+	if (pw != NULL)
 		size += 3;	/* LOGNAME, HOME, MAIL */
-	}
+	else
+		size += 1;	/* HOME */
+
 	size++;	/* always fill in SHELL */
 	size++; /* terminating NULL */
 
@@ -1161,6 +1231,10 @@
 		if ((estr = add_env("MAIL", varmail)) == NULL)
 			goto malloc_fail;
 		new_env[e++] = estr;
+	} else {
+		if ((estr = add_env("HOME", "/")) == NULL)
+			goto malloc_fail;
+		new_env[e++] = estr;
 	}
 
 	if (pw != NULL && strlen(pw->pw_shell) > 0) {
@@ -1376,7 +1450,7 @@
 }
 
 static int
-noninteractive_login(char *zonename, zoneid_t zoneid, char *login,
+noninteractive_login(char *zonename, const char *user_cmd, zoneid_t zoneid,
     char **new_args, char **new_env)
 {
 	pid_t retval;
@@ -1475,8 +1549,16 @@
 			_exit(1);
 		}
 
+		/*
+		 * For non-native zones, tell libc where it can find locale
+		 * specific getttext() messages.
+		 */
+		if (access("/native/usr/lib/locale", R_OK) == 0)
+			(void) bindtextdomain(TEXT_DOMAIN,
+			    "/native/usr/lib/locale");
+
 		if (!failsafe)
-			new_env = prep_env_noninteractive(login, new_env);
+			new_env = prep_env_noninteractive(user_cmd, new_env);
 
 		if (new_env == NULL) {
 			_exit(1);
@@ -1534,6 +1616,7 @@
 	struct stat sb;
 	char kernzone[ZONENAME_MAX];
 	brand_handle_t bh;
+	char user_cmd[MAXPATHLEN];
 
 	(void) setlocale(LC_ALL, "");
 	(void) textdomain(TEXT_DOMAIN);
@@ -1759,6 +1842,19 @@
 		brand_close(bh);
 		return (1);
 	}
+	/*
+	 * Get the brand specific user_cmd.  This command is used to get
+	 * a passwd(4) entry for login.
+	 */
+	if (!interactive && !failsafe) {
+		if (zone_get_user_cmd(bh, login, user_cmd,
+		    sizeof (user_cmd)) == NULL) {
+			zerror(gettext("could not get user_cmd for zone %s"),
+			    zonename);
+			brand_close(bh);
+			return (1);
+		}
+	}
 	brand_close(bh);
 
 	if ((new_env = prep_env()) == NULL) {
@@ -1767,8 +1863,8 @@
 	}
 
 	if (!interactive)
-		return (noninteractive_login(zonename, zoneid, login, new_args,
-		    new_env));
+		return (noninteractive_login(zonename, user_cmd, zoneid,
+		    new_args, new_env));
 
 	if (zonecfg_in_alt_root()) {
 		zerror(gettext("cannot use interactive login with scratch "
--- a/usr/src/lib/brand/lx/zone/config.xml	Tue May 29 15:13:25 2007 -0700
+++ b/usr/src/lib/brand/lx/zone/config.xml	Tue May 29 15:28:30 2007 -0700
@@ -36,6 +36,7 @@
 
 	<initname>/sbin/init</initname>
 	<login_cmd>/bin/login -h zone:%Z -f %u</login_cmd>
+	<user_cmd>/usr/bin/getent passwd %u</user_cmd>
 
 	<install>/usr/lib/brand/lx/lx_install %z %R %*</install>
 	<installopts>d:hsvX</installopts>
--- a/usr/src/lib/brand/native/zone/config.xml	Tue May 29 15:13:25 2007 -0700
+++ b/usr/src/lib/brand/native/zone/config.xml	Tue May 29 15:28:30 2007 -0700
@@ -36,6 +36,7 @@
 
 	<initname>/sbin/init</initname>
 	<login_cmd>/usr/bin/login -z %Z -f %u</login_cmd>
+	<user_cmd>/usr/bin/getent passwd %u</user_cmd>
 
 	<install>/usr/lib/lu/lucreatezone -z %z</install>
 	<installopts></installopts>
--- a/usr/src/lib/brand/sn1/zone/config.xml	Tue May 29 15:13:25 2007 -0700
+++ b/usr/src/lib/brand/sn1/zone/config.xml	Tue May 29 15:28:30 2007 -0700
@@ -36,6 +36,7 @@
 
 	<initname>/sbin/init</initname>
 	<login_cmd>/usr/bin/login -z %Z -f %u</login_cmd>
+	<user_cmd>/usr/bin/getent passwd %u</user_cmd>
 
 	<install>/usr/lib/lu/lucreatezone -z %z</install>
 	<installopts></installopts>
--- a/usr/src/lib/libbrand/common/libbrand.c	Tue May 29 15:13:25 2007 -0700
+++ b/usr/src/lib/libbrand/common/libbrand.c	Tue May 29 15:28:30 2007 -0700
@@ -62,6 +62,7 @@
 #define	DTD_ELEM_POSTCLONE	((const xmlChar *) "postclone")
 #define	DTD_ELEM_PRIVILEGE	((const xmlChar *) "privilege")
 #define	DTD_ELEM_SYMLINK	((const xmlChar *) "symlink")
+#define	DTD_ELEM_USER_CMD	((const xmlChar *) "user_cmd")
 #define	DTD_ELEM_VERIFY_CFG	((const xmlChar *) "verify_cfg")
 #define	DTD_ELEM_VERIFY_ADM	((const xmlChar *) "verify_adm")
 
@@ -485,6 +486,16 @@
 }
 
 int
+brand_get_user_cmd(brand_handle_t bh, const char *username,
+    char *buf, size_t len)
+{
+	struct brand_handle *bhp = (struct brand_handle *)bh;
+
+	return (brand_get_value(bhp, NULL, NULL, username, NULL,
+	    buf, len, 0, NULL, DTD_ELEM_USER_CMD, B_TRUE, B_FALSE));
+}
+
+int
 brand_get_install(brand_handle_t bh, const char *zonename,
     const char *zoneroot, char *buf, size_t len, int argc, char **argv)
 {
--- a/usr/src/lib/libbrand/common/libbrand.h	Tue May 29 15:13:25 2007 -0700
+++ b/usr/src/lib/libbrand/common/libbrand.h	Tue May 29 15:28:30 2007 -0700
@@ -63,6 +63,7 @@
 extern int brand_get_modname(brand_handle_t, char *, size_t);
 extern int brand_get_postclone(brand_handle_t, const char *, const char *,
     char *, size_t, int, char **);
+extern int brand_get_user_cmd(brand_handle_t, const char *, char *, size_t);
 extern int brand_get_verify_cfg(brand_handle_t, char *, size_t);
 extern int brand_get_verify_adm(brand_handle_t, const char *, const char *,
     char *, size_t, int, char **);
--- a/usr/src/lib/libbrand/common/mapfile-vers	Tue May 29 15:13:25 2007 -0700
+++ b/usr/src/lib/libbrand/common/mapfile-vers	Tue May 29 15:28:30 2007 -0700
@@ -39,6 +39,7 @@
 	brand_get_login_cmd;
 	brand_get_modname;
 	brand_get_postclone;
+	brand_get_user_cmd;
 	brand_get_verify_adm;
 	brand_get_verify_cfg;
 	brand_is_native;
--- a/usr/src/lib/libbrand/dtd/brand.dtd.1	Tue May 29 15:13:25 2007 -0700
+++ b/usr/src/lib/libbrand/dtd/brand.dtd.1	Tue May 29 15:28:30 2007 -0700
@@ -186,6 +186,21 @@
 <!ATTLIST login_cmd>
 
 <!--
+  user_cmd
+
+    Path to the binary that will translate a user name to a passwd(4) entry.
+
+    The following replacements are performed:
+
+      %u        User login name
+
+    It has no attributes.  The passwd(4) entry is used to determine $LOGNAME,
+    $HOME, and $SHELL for non-interactive "zlogin -l <user> <cmd>".
+-->
+<!ELEMENT user_cmd   (#PCDATA) >
+<!ATTLIST user_cmd>
+
+<!--
   postclone
 
     Path to a script that will perform any necessary post-processing on
@@ -242,8 +257,8 @@
 		directory in which the configuration file is stored.
 -->
 
-<!ELEMENT brand		(modname, initname, login_cmd, install, installopts,
-			boot, halt, verify_cfg, verify_adm, postclone,
-			privilege+)>
+<!ELEMENT brand		(modname, initname, login_cmd, user_cmd, install,
+			installopts, boot, halt, verify_cfg, verify_adm,
+			postclone, privilege+)>
 
 <!ATTLIST brand		name		CDATA #REQUIRED>