17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5facf4a8dSllai1 * Common Development and Distribution License (the "License"). 6facf4a8dSllai1 * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 22a20ee416SGlenn Faden * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. 23c2589d13SGarrett D'Amore * Copyright 2013 DEY Storage Systems, Inc. 24279721bfSGary Mills * Copyright (c) 2014 Gary Mills 25*741343adSAlexander Eremin * Copyright 2015 Nexenta Systems, Inc. All rights reserved. 267c478bd9Sstevel@tonic-gate */ 277c478bd9Sstevel@tonic-gate 287c478bd9Sstevel@tonic-gate /* 297c478bd9Sstevel@tonic-gate * zlogin provides three types of login which allow users in the global 307c478bd9Sstevel@tonic-gate * zone to access non-global zones. 317c478bd9Sstevel@tonic-gate * 327c478bd9Sstevel@tonic-gate * - "interactive login" is similar to rlogin(1); for example, the user could 337c478bd9Sstevel@tonic-gate * issue 'zlogin my-zone' or 'zlogin -e ^ -l me my-zone'. The user is 347c478bd9Sstevel@tonic-gate * granted a new pty (which is then shoved into the zone), and an I/O 357c478bd9Sstevel@tonic-gate * loop between parent and child processes takes care of the interactive 367c478bd9Sstevel@tonic-gate * session. In this mode, login(1) (and its -c option, which means 377c478bd9Sstevel@tonic-gate * "already authenticated") is employed to take care of the initialization 387c478bd9Sstevel@tonic-gate * of the user's session. 397c478bd9Sstevel@tonic-gate * 407c478bd9Sstevel@tonic-gate * - "non-interactive login" is similar to su(1M); the user could issue 417c478bd9Sstevel@tonic-gate * 'zlogin my-zone ls -l' and the command would be run as specified. 427c478bd9Sstevel@tonic-gate * In this mode, zlogin sets up pipes as the communication channel, and 437c478bd9Sstevel@tonic-gate * 'su' is used to do the login setup work. 447c478bd9Sstevel@tonic-gate * 457c478bd9Sstevel@tonic-gate * - "console login" is the equivalent to accessing the tip line for a 467c478bd9Sstevel@tonic-gate * zone. For example, the user can issue 'zlogin -C my-zone'. 477c478bd9Sstevel@tonic-gate * In this mode, zlogin contacts the zoneadmd process via unix domain 487c478bd9Sstevel@tonic-gate * socket. If zoneadmd is not running, it starts it. This allows the 497c478bd9Sstevel@tonic-gate * console to be available anytime the zone is installed, regardless of 507c478bd9Sstevel@tonic-gate * whether it is running. 517c478bd9Sstevel@tonic-gate */ 527c478bd9Sstevel@tonic-gate 537c478bd9Sstevel@tonic-gate #include <sys/socket.h> 547c478bd9Sstevel@tonic-gate #include <sys/termios.h> 557c478bd9Sstevel@tonic-gate #include <sys/utsname.h> 567c478bd9Sstevel@tonic-gate #include <sys/stat.h> 577c478bd9Sstevel@tonic-gate #include <sys/types.h> 587c478bd9Sstevel@tonic-gate #include <sys/contract/process.h> 597c478bd9Sstevel@tonic-gate #include <sys/ctfs.h> 609acbbeafSnn35248 #include <sys/brand.h> 61858a4b99Ssl108498 #include <sys/wait.h> 627c478bd9Sstevel@tonic-gate #include <alloca.h> 637c478bd9Sstevel@tonic-gate #include <assert.h> 647c478bd9Sstevel@tonic-gate #include <ctype.h> 65279721bfSGary Mills #include <paths.h> 667c478bd9Sstevel@tonic-gate #include <door.h> 677c478bd9Sstevel@tonic-gate #include <errno.h> 68858a4b99Ssl108498 #include <nss_dbdefs.h> 697c478bd9Sstevel@tonic-gate #include <poll.h> 707c478bd9Sstevel@tonic-gate #include <priv.h> 717c478bd9Sstevel@tonic-gate #include <pwd.h> 727c478bd9Sstevel@tonic-gate #include <unistd.h> 737c478bd9Sstevel@tonic-gate #include <utmpx.h> 747c478bd9Sstevel@tonic-gate #include <sac.h> 757c478bd9Sstevel@tonic-gate #include <signal.h> 767c478bd9Sstevel@tonic-gate #include <stdarg.h> 777c478bd9Sstevel@tonic-gate #include <stdio.h> 787c478bd9Sstevel@tonic-gate #include <stdlib.h> 797c478bd9Sstevel@tonic-gate #include <string.h> 807c478bd9Sstevel@tonic-gate #include <strings.h> 817c478bd9Sstevel@tonic-gate #include <stropts.h> 827c478bd9Sstevel@tonic-gate #include <wait.h> 837c478bd9Sstevel@tonic-gate #include <zone.h> 847c478bd9Sstevel@tonic-gate #include <fcntl.h> 857c478bd9Sstevel@tonic-gate #include <libdevinfo.h> 867c478bd9Sstevel@tonic-gate #include <libintl.h> 877c478bd9Sstevel@tonic-gate #include <locale.h> 887c478bd9Sstevel@tonic-gate #include <libzonecfg.h> 897c478bd9Sstevel@tonic-gate #include <libcontract.h> 909acbbeafSnn35248 #include <libbrand.h> 91cb8a054bSGlenn Faden #include <auth_list.h> 92cb8a054bSGlenn Faden #include <auth_attr.h> 93cb8a054bSGlenn Faden #include <secdb.h> 947c478bd9Sstevel@tonic-gate 957c478bd9Sstevel@tonic-gate static int masterfd; 967c478bd9Sstevel@tonic-gate static struct termios save_termios; 977c478bd9Sstevel@tonic-gate static struct termios effective_termios; 987c478bd9Sstevel@tonic-gate static int save_fd; 997c478bd9Sstevel@tonic-gate static struct winsize winsize; 1007c478bd9Sstevel@tonic-gate static volatile int dead; 1017c478bd9Sstevel@tonic-gate static volatile pid_t child_pid = -1; 1027c478bd9Sstevel@tonic-gate static int interactive = 0; 1037c478bd9Sstevel@tonic-gate static priv_set_t *dropprivs; 1047c478bd9Sstevel@tonic-gate 1057c478bd9Sstevel@tonic-gate static int nocmdchar = 0; 1067c478bd9Sstevel@tonic-gate static int failsafe = 0; 107*741343adSAlexander Eremin static int disconnect = 0; 1087c478bd9Sstevel@tonic-gate static char cmdchar = '~'; 109c2589d13SGarrett D'Amore static int quiet = 0; 1107c478bd9Sstevel@tonic-gate 1117c478bd9Sstevel@tonic-gate static int pollerr = 0; 1127c478bd9Sstevel@tonic-gate 1137c478bd9Sstevel@tonic-gate static const char *pname; 114cb8a054bSGlenn Faden static char *username; 115cb8a054bSGlenn Faden 116cb8a054bSGlenn Faden /* 117cb8a054bSGlenn Faden * When forced_login is true, the user is not prompted 118cb8a054bSGlenn Faden * for an authentication password in the target zone. 119cb8a054bSGlenn Faden */ 120cb8a054bSGlenn Faden static boolean_t forced_login = B_FALSE; 1217c478bd9Sstevel@tonic-gate 1227c478bd9Sstevel@tonic-gate #if !defined(TEXT_DOMAIN) /* should be defined by cc -D */ 1237c478bd9Sstevel@tonic-gate #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it wasn't */ 1247c478bd9Sstevel@tonic-gate #endif 1257c478bd9Sstevel@tonic-gate 1267c478bd9Sstevel@tonic-gate #define SUPATH "/usr/bin/su" 1277c478bd9Sstevel@tonic-gate #define FAILSAFESHELL "/sbin/sh" 1287c478bd9Sstevel@tonic-gate #define DEFAULTSHELL "/sbin/sh" 1297c478bd9Sstevel@tonic-gate #define DEF_PATH "/usr/sbin:/usr/bin" 1307c478bd9Sstevel@tonic-gate 13162868012SSteve Lawrence #define CLUSTER_BRAND_NAME "cluster" 13262868012SSteve Lawrence 133b52a7fd7Sgjelinek /* 134b52a7fd7Sgjelinek * The ZLOGIN_BUFSIZ is larger than PIPE_BUF so we can be sure we're clearing 135b52a7fd7Sgjelinek * out the pipe when the child is exiting. The ZLOGIN_RDBUFSIZ must be less 136b52a7fd7Sgjelinek * than ZLOGIN_BUFSIZ (because we share the buffer in doio). This value is 137b52a7fd7Sgjelinek * also chosen in conjunction with the HI_WATER setting to make sure we 138b52a7fd7Sgjelinek * don't fill up the pipe. We can write FIFOHIWAT (16k) into the pipe before 139b52a7fd7Sgjelinek * blocking. By having ZLOGIN_RDBUFSIZ set to 1k and HI_WATER set to 8k, we 140b52a7fd7Sgjelinek * know we can always write a ZLOGIN_RDBUFSIZ chunk into the pipe when there 141b52a7fd7Sgjelinek * is less than HI_WATER data already in the pipe. 142b52a7fd7Sgjelinek */ 143e6e67599Sdp #define ZLOGIN_BUFSIZ 8192 144b52a7fd7Sgjelinek #define ZLOGIN_RDBUFSIZ 1024 145b52a7fd7Sgjelinek #define HI_WATER 8192 146e6e67599Sdp 1477c478bd9Sstevel@tonic-gate /* 1487c478bd9Sstevel@tonic-gate * See canonify() below. CANONIFY_LEN is the maximum length that a 1497c478bd9Sstevel@tonic-gate * "canonical" sequence will expand to (backslash, three octal digits, NUL). 1507c478bd9Sstevel@tonic-gate */ 1517c478bd9Sstevel@tonic-gate #define CANONIFY_LEN 5 1527c478bd9Sstevel@tonic-gate 1537c478bd9Sstevel@tonic-gate static void 1547c478bd9Sstevel@tonic-gate usage(void) 1557c478bd9Sstevel@tonic-gate { 156*741343adSAlexander Eremin (void) fprintf(stderr, gettext("usage: %s [ -dnQCES ] [ -e cmdchar ] " 1577c478bd9Sstevel@tonic-gate "[-l user] zonename [command [args ...] ]\n"), pname); 1587c478bd9Sstevel@tonic-gate exit(2); 1597c478bd9Sstevel@tonic-gate } 1607c478bd9Sstevel@tonic-gate 1617c478bd9Sstevel@tonic-gate static const char * 1627c478bd9Sstevel@tonic-gate getpname(const char *arg0) 1637c478bd9Sstevel@tonic-gate { 1647c478bd9Sstevel@tonic-gate const char *p = strrchr(arg0, '/'); 1657c478bd9Sstevel@tonic-gate 1667c478bd9Sstevel@tonic-gate if (p == NULL) 1677c478bd9Sstevel@tonic-gate p = arg0; 1687c478bd9Sstevel@tonic-gate else 1697c478bd9Sstevel@tonic-gate p++; 1707c478bd9Sstevel@tonic-gate 1717c478bd9Sstevel@tonic-gate pname = p; 1727c478bd9Sstevel@tonic-gate return (p); 1737c478bd9Sstevel@tonic-gate } 1747c478bd9Sstevel@tonic-gate 1757c478bd9Sstevel@tonic-gate static void 1767c478bd9Sstevel@tonic-gate zerror(const char *fmt, ...) 1777c478bd9Sstevel@tonic-gate { 1787c478bd9Sstevel@tonic-gate va_list alist; 1797c478bd9Sstevel@tonic-gate 1807c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "%s: ", pname); 1817c478bd9Sstevel@tonic-gate va_start(alist, fmt); 1827c478bd9Sstevel@tonic-gate (void) vfprintf(stderr, fmt, alist); 1837c478bd9Sstevel@tonic-gate va_end(alist); 1847c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "\n"); 1857c478bd9Sstevel@tonic-gate } 1867c478bd9Sstevel@tonic-gate 1877c478bd9Sstevel@tonic-gate static void 1887c478bd9Sstevel@tonic-gate zperror(const char *str) 1897c478bd9Sstevel@tonic-gate { 1907c478bd9Sstevel@tonic-gate const char *estr; 1917c478bd9Sstevel@tonic-gate 1927c478bd9Sstevel@tonic-gate if ((estr = strerror(errno)) != NULL) 1937c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "%s: %s: %s\n", pname, str, estr); 1947c478bd9Sstevel@tonic-gate else 1957c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "%s: %s: errno %d\n", pname, str, errno); 1967c478bd9Sstevel@tonic-gate } 1977c478bd9Sstevel@tonic-gate 1987c478bd9Sstevel@tonic-gate /* 1997c478bd9Sstevel@tonic-gate * The first part of our privilege dropping scheme needs to be called before 2007c478bd9Sstevel@tonic-gate * fork(), since we must have it for security; we don't want to be surprised 2017c478bd9Sstevel@tonic-gate * later that we couldn't allocate the privset. 2027c478bd9Sstevel@tonic-gate */ 2037c478bd9Sstevel@tonic-gate static int 2047c478bd9Sstevel@tonic-gate prefork_dropprivs() 2057c478bd9Sstevel@tonic-gate { 2067c478bd9Sstevel@tonic-gate if ((dropprivs = priv_allocset()) == NULL) 2077c478bd9Sstevel@tonic-gate return (1); 208634e26ecSCasper H.S. Dik 209634e26ecSCasper H.S. Dik priv_basicset(dropprivs); 210634e26ecSCasper H.S. Dik (void) priv_delset(dropprivs, PRIV_PROC_INFO); 211634e26ecSCasper H.S. Dik (void) priv_delset(dropprivs, PRIV_PROC_FORK); 212634e26ecSCasper H.S. Dik (void) priv_delset(dropprivs, PRIV_PROC_EXEC); 213634e26ecSCasper H.S. Dik (void) priv_delset(dropprivs, PRIV_FILE_LINK_ANY); 2147c478bd9Sstevel@tonic-gate 2157c478bd9Sstevel@tonic-gate /* 216634e26ecSCasper H.S. Dik * We need to keep the basic privilege PROC_SESSION and all unknown 217634e26ecSCasper H.S. Dik * basic privileges as well as the privileges PROC_ZONE and 218634e26ecSCasper H.S. Dik * PROC_OWNER in order to query session information and 2197c478bd9Sstevel@tonic-gate * send signals. 2207c478bd9Sstevel@tonic-gate */ 2217c478bd9Sstevel@tonic-gate if (interactive == 0) { 222634e26ecSCasper H.S. Dik (void) priv_addset(dropprivs, PRIV_PROC_ZONE); 223634e26ecSCasper H.S. Dik (void) priv_addset(dropprivs, PRIV_PROC_OWNER); 224634e26ecSCasper H.S. Dik } else { 225634e26ecSCasper H.S. Dik (void) priv_delset(dropprivs, PRIV_PROC_SESSION); 2267c478bd9Sstevel@tonic-gate } 2277c478bd9Sstevel@tonic-gate 2287c478bd9Sstevel@tonic-gate return (0); 2297c478bd9Sstevel@tonic-gate } 2307c478bd9Sstevel@tonic-gate 2317c478bd9Sstevel@tonic-gate /* 2327c478bd9Sstevel@tonic-gate * The second part of the privilege drop. We are paranoid about being attacked 2337c478bd9Sstevel@tonic-gate * by the zone, so we drop all privileges. This should prevent a compromise 2347c478bd9Sstevel@tonic-gate * which gets us to fork(), exec(), symlink(), etc. 2357c478bd9Sstevel@tonic-gate */ 2367c478bd9Sstevel@tonic-gate static void 2377c478bd9Sstevel@tonic-gate postfork_dropprivs() 2387c478bd9Sstevel@tonic-gate { 2397c478bd9Sstevel@tonic-gate if ((setppriv(PRIV_SET, PRIV_PERMITTED, dropprivs)) == -1) { 2407c478bd9Sstevel@tonic-gate zperror(gettext("Warning: could not set permitted privileges")); 2417c478bd9Sstevel@tonic-gate } 2427c478bd9Sstevel@tonic-gate if ((setppriv(PRIV_SET, PRIV_LIMIT, dropprivs)) == -1) { 2437c478bd9Sstevel@tonic-gate zperror(gettext("Warning: could not set limit privileges")); 2447c478bd9Sstevel@tonic-gate } 2457c478bd9Sstevel@tonic-gate if ((setppriv(PRIV_SET, PRIV_INHERITABLE, dropprivs)) == -1) { 2467c478bd9Sstevel@tonic-gate zperror(gettext("Warning: could not set inheritable " 2477c478bd9Sstevel@tonic-gate "privileges")); 2487c478bd9Sstevel@tonic-gate } 2497c478bd9Sstevel@tonic-gate } 2507c478bd9Sstevel@tonic-gate 2517c478bd9Sstevel@tonic-gate /* 2527c478bd9Sstevel@tonic-gate * Create the unix domain socket and call the zoneadmd server; handshake 2537c478bd9Sstevel@tonic-gate * with it to determine whether it will allow us to connect. 2547c478bd9Sstevel@tonic-gate */ 2557c478bd9Sstevel@tonic-gate static int 2567c478bd9Sstevel@tonic-gate get_console_master(const char *zname) 2577c478bd9Sstevel@tonic-gate { 2587c478bd9Sstevel@tonic-gate int sockfd = -1; 2597c478bd9Sstevel@tonic-gate struct sockaddr_un servaddr; 2607c478bd9Sstevel@tonic-gate char clientid[MAXPATHLEN]; 2617c478bd9Sstevel@tonic-gate char handshake[MAXPATHLEN], c; 2627c478bd9Sstevel@tonic-gate int msglen; 2637c478bd9Sstevel@tonic-gate int i = 0, err = 0; 2647c478bd9Sstevel@tonic-gate 2657c478bd9Sstevel@tonic-gate if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { 2667c478bd9Sstevel@tonic-gate zperror(gettext("could not create socket")); 2677c478bd9Sstevel@tonic-gate return (-1); 2687c478bd9Sstevel@tonic-gate } 2697c478bd9Sstevel@tonic-gate 2707c478bd9Sstevel@tonic-gate bzero(&servaddr, sizeof (servaddr)); 2717c478bd9Sstevel@tonic-gate servaddr.sun_family = AF_UNIX; 2727c478bd9Sstevel@tonic-gate (void) snprintf(servaddr.sun_path, sizeof (servaddr.sun_path), 2737c478bd9Sstevel@tonic-gate "%s/%s.console_sock", ZONES_TMPDIR, zname); 2747c478bd9Sstevel@tonic-gate 2757c478bd9Sstevel@tonic-gate if (connect(sockfd, (struct sockaddr *)&servaddr, 2767c478bd9Sstevel@tonic-gate sizeof (servaddr)) == -1) { 2777c478bd9Sstevel@tonic-gate zperror(gettext("Could not connect to zone console")); 2787c478bd9Sstevel@tonic-gate goto bad; 2797c478bd9Sstevel@tonic-gate } 2807c478bd9Sstevel@tonic-gate masterfd = sockfd; 2817c478bd9Sstevel@tonic-gate 282*741343adSAlexander Eremin msglen = snprintf(clientid, sizeof (clientid), "IDENT %lu %s %d\n", 283*741343adSAlexander Eremin getpid(), setlocale(LC_MESSAGES, NULL), disconnect); 2847c478bd9Sstevel@tonic-gate 2857c478bd9Sstevel@tonic-gate if (msglen >= sizeof (clientid) || msglen < 0) { 2867c478bd9Sstevel@tonic-gate zerror("protocol error"); 2877c478bd9Sstevel@tonic-gate goto bad; 2887c478bd9Sstevel@tonic-gate } 2897c478bd9Sstevel@tonic-gate 2907c478bd9Sstevel@tonic-gate if (write(masterfd, clientid, msglen) != msglen) { 2917c478bd9Sstevel@tonic-gate zerror("protocol error"); 2927c478bd9Sstevel@tonic-gate goto bad; 2937c478bd9Sstevel@tonic-gate } 2947c478bd9Sstevel@tonic-gate 2957c478bd9Sstevel@tonic-gate bzero(handshake, sizeof (handshake)); 2967c478bd9Sstevel@tonic-gate 2977c478bd9Sstevel@tonic-gate /* 2987c478bd9Sstevel@tonic-gate * Take care not to accumulate more than our fill, and leave room for 2997c478bd9Sstevel@tonic-gate * the NUL at the end. 3007c478bd9Sstevel@tonic-gate */ 3017c478bd9Sstevel@tonic-gate while ((err = read(masterfd, &c, 1)) == 1) { 3027c478bd9Sstevel@tonic-gate if (i >= (sizeof (handshake) - 1)) 3037c478bd9Sstevel@tonic-gate break; 3047c478bd9Sstevel@tonic-gate if (c == '\n') 3057c478bd9Sstevel@tonic-gate break; 3067c478bd9Sstevel@tonic-gate handshake[i] = c; 3077c478bd9Sstevel@tonic-gate i++; 3087c478bd9Sstevel@tonic-gate } 3097c478bd9Sstevel@tonic-gate 3107c478bd9Sstevel@tonic-gate /* 3117c478bd9Sstevel@tonic-gate * If something went wrong during the handshake we bail; perhaps 3127c478bd9Sstevel@tonic-gate * the server died off. 3137c478bd9Sstevel@tonic-gate */ 3147c478bd9Sstevel@tonic-gate if (err == -1) { 3157c478bd9Sstevel@tonic-gate zperror(gettext("Could not connect to zone console")); 3167c478bd9Sstevel@tonic-gate goto bad; 3177c478bd9Sstevel@tonic-gate } 3187c478bd9Sstevel@tonic-gate 3197c478bd9Sstevel@tonic-gate if (strncmp(handshake, "OK", sizeof (handshake)) == 0) 3207c478bd9Sstevel@tonic-gate return (0); 3217c478bd9Sstevel@tonic-gate 3227c478bd9Sstevel@tonic-gate zerror(gettext("Console is already in use by process ID %s."), 3237c478bd9Sstevel@tonic-gate handshake); 3247c478bd9Sstevel@tonic-gate bad: 3257c478bd9Sstevel@tonic-gate (void) close(sockfd); 3267c478bd9Sstevel@tonic-gate masterfd = -1; 3277c478bd9Sstevel@tonic-gate return (-1); 3287c478bd9Sstevel@tonic-gate } 3297c478bd9Sstevel@tonic-gate 3307c478bd9Sstevel@tonic-gate 3317c478bd9Sstevel@tonic-gate /* 3327c478bd9Sstevel@tonic-gate * Routines to handle pty creation upon zone entry and to shuttle I/O back 3337c478bd9Sstevel@tonic-gate * and forth between the two terminals. We also compute and store the 3347c478bd9Sstevel@tonic-gate * name of the slave terminal associated with the master side. 3357c478bd9Sstevel@tonic-gate */ 3367c478bd9Sstevel@tonic-gate static int 3377c478bd9Sstevel@tonic-gate get_master_pty() 3387c478bd9Sstevel@tonic-gate { 3397c478bd9Sstevel@tonic-gate if ((masterfd = open("/dev/ptmx", O_RDWR|O_NONBLOCK)) < 0) { 3407c478bd9Sstevel@tonic-gate zperror(gettext("failed to obtain a pseudo-tty")); 3417c478bd9Sstevel@tonic-gate return (-1); 3427c478bd9Sstevel@tonic-gate } 3437c478bd9Sstevel@tonic-gate if (tcgetattr(STDIN_FILENO, &save_termios) == -1) { 3447c478bd9Sstevel@tonic-gate zperror(gettext("failed to get terminal settings from stdin")); 3457c478bd9Sstevel@tonic-gate return (-1); 3467c478bd9Sstevel@tonic-gate } 3477c478bd9Sstevel@tonic-gate (void) ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&winsize); 3487c478bd9Sstevel@tonic-gate 3497c478bd9Sstevel@tonic-gate return (0); 3507c478bd9Sstevel@tonic-gate } 3517c478bd9Sstevel@tonic-gate 3527c478bd9Sstevel@tonic-gate /* 3537c478bd9Sstevel@tonic-gate * This is a bit tricky; normally a pts device will belong to the zone it 3547c478bd9Sstevel@tonic-gate * is granted to. But in the case of "entering" a zone, we need to establish 3557c478bd9Sstevel@tonic-gate * the pty before entering the zone so that we can vector I/O to and from it 3567c478bd9Sstevel@tonic-gate * from the global zone. 3577c478bd9Sstevel@tonic-gate * 3587c478bd9Sstevel@tonic-gate * We use the zonept() call to let the ptm driver know what we are up to; 3597c478bd9Sstevel@tonic-gate * the only other hairy bit is the setting of zoneslavename (which happens 3607c478bd9Sstevel@tonic-gate * above, in get_master_pty()). 3617c478bd9Sstevel@tonic-gate */ 3627c478bd9Sstevel@tonic-gate static int 363facf4a8dSllai1 init_slave_pty(zoneid_t zoneid, char *devroot) 3647c478bd9Sstevel@tonic-gate { 3657c478bd9Sstevel@tonic-gate int slavefd = -1; 3667c478bd9Sstevel@tonic-gate char *slavename, zoneslavename[MAXPATHLEN]; 3677c478bd9Sstevel@tonic-gate 3687c478bd9Sstevel@tonic-gate /* 3697c478bd9Sstevel@tonic-gate * Set slave permissions, zone the pts, then unlock it. 3707c478bd9Sstevel@tonic-gate */ 3717c478bd9Sstevel@tonic-gate if (grantpt(masterfd) != 0) { 3727c478bd9Sstevel@tonic-gate zperror(gettext("grantpt failed")); 3737c478bd9Sstevel@tonic-gate return (-1); 3747c478bd9Sstevel@tonic-gate } 3757c478bd9Sstevel@tonic-gate 3767c478bd9Sstevel@tonic-gate if (unlockpt(masterfd) != 0) { 3777c478bd9Sstevel@tonic-gate zperror(gettext("unlockpt failed")); 3787c478bd9Sstevel@tonic-gate return (-1); 3797c478bd9Sstevel@tonic-gate } 3807c478bd9Sstevel@tonic-gate 3817c478bd9Sstevel@tonic-gate /* 3827c478bd9Sstevel@tonic-gate * We must open the slave side before zoning this pty; otherwise 3837c478bd9Sstevel@tonic-gate * the kernel would refuse us the open-- zoning a pty makes it 384facf4a8dSllai1 * inaccessible to the global zone. Note we are trying to open 385facf4a8dSllai1 * the device node via the $ZONEROOT/dev path for this pty. 3867c478bd9Sstevel@tonic-gate * 3877c478bd9Sstevel@tonic-gate * Later we'll close the slave out when once we've opened it again 3887c478bd9Sstevel@tonic-gate * from within the target zone. Blarg. 3897c478bd9Sstevel@tonic-gate */ 3907c478bd9Sstevel@tonic-gate if ((slavename = ptsname(masterfd)) == NULL) { 3917c478bd9Sstevel@tonic-gate zperror(gettext("failed to get name for pseudo-tty")); 3927c478bd9Sstevel@tonic-gate return (-1); 3937c478bd9Sstevel@tonic-gate } 3947c478bd9Sstevel@tonic-gate 3957c478bd9Sstevel@tonic-gate (void) snprintf(zoneslavename, sizeof (zoneslavename), "%s%s", 396facf4a8dSllai1 devroot, slavename); 3977c478bd9Sstevel@tonic-gate 3987c478bd9Sstevel@tonic-gate if ((slavefd = open(zoneslavename, O_RDWR)) < 0) { 3997c478bd9Sstevel@tonic-gate zerror(gettext("failed to open %s: %s"), zoneslavename, 4007c478bd9Sstevel@tonic-gate strerror(errno)); 4017c478bd9Sstevel@tonic-gate return (-1); 4027c478bd9Sstevel@tonic-gate } 4037c478bd9Sstevel@tonic-gate 4047c478bd9Sstevel@tonic-gate /* 4057c478bd9Sstevel@tonic-gate * Push hardware emulation (ptem), line discipline (ldterm), 4067c478bd9Sstevel@tonic-gate * and V7/4BSD/Xenix compatibility (ttcompat) modules. 4077c478bd9Sstevel@tonic-gate */ 4087c478bd9Sstevel@tonic-gate if (ioctl(slavefd, I_PUSH, "ptem") == -1) { 4097c478bd9Sstevel@tonic-gate zperror(gettext("failed to push ptem module")); 4107c478bd9Sstevel@tonic-gate if (!failsafe) 4117c478bd9Sstevel@tonic-gate goto bad; 4127c478bd9Sstevel@tonic-gate } 4137c478bd9Sstevel@tonic-gate 4147c478bd9Sstevel@tonic-gate /* 4157c478bd9Sstevel@tonic-gate * Anchor the stream to prevent malicious I_POPs; we prefer to do 4167c478bd9Sstevel@tonic-gate * this prior to entering the zone so that we can detect any errors 4177c478bd9Sstevel@tonic-gate * early, and so that we can set the anchor from the global zone. 4187c478bd9Sstevel@tonic-gate */ 4197c478bd9Sstevel@tonic-gate if (ioctl(slavefd, I_ANCHOR) == -1) { 4207c478bd9Sstevel@tonic-gate zperror(gettext("failed to set stream anchor")); 4217c478bd9Sstevel@tonic-gate if (!failsafe) 4227c478bd9Sstevel@tonic-gate goto bad; 4237c478bd9Sstevel@tonic-gate } 4247c478bd9Sstevel@tonic-gate 4257c478bd9Sstevel@tonic-gate if (ioctl(slavefd, I_PUSH, "ldterm") == -1) { 4267c478bd9Sstevel@tonic-gate zperror(gettext("failed to push ldterm module")); 4277c478bd9Sstevel@tonic-gate if (!failsafe) 4287c478bd9Sstevel@tonic-gate goto bad; 4297c478bd9Sstevel@tonic-gate } 4307c478bd9Sstevel@tonic-gate if (ioctl(slavefd, I_PUSH, "ttcompat") == -1) { 4317c478bd9Sstevel@tonic-gate zperror(gettext("failed to push ttcompat module")); 4327c478bd9Sstevel@tonic-gate if (!failsafe) 4337c478bd9Sstevel@tonic-gate goto bad; 4347c478bd9Sstevel@tonic-gate } 4357c478bd9Sstevel@tonic-gate 4367c478bd9Sstevel@tonic-gate /* 4377c478bd9Sstevel@tonic-gate * Propagate terminal settings from the external term to the new one. 4387c478bd9Sstevel@tonic-gate */ 4397c478bd9Sstevel@tonic-gate if (tcsetattr(slavefd, TCSAFLUSH, &save_termios) == -1) { 4407c478bd9Sstevel@tonic-gate zperror(gettext("failed to set terminal settings")); 4417c478bd9Sstevel@tonic-gate if (!failsafe) 4427c478bd9Sstevel@tonic-gate goto bad; 4437c478bd9Sstevel@tonic-gate } 4447c478bd9Sstevel@tonic-gate (void) ioctl(slavefd, TIOCSWINSZ, (char *)&winsize); 4457c478bd9Sstevel@tonic-gate 4467c478bd9Sstevel@tonic-gate if (zonept(masterfd, zoneid) != 0) { 4477c478bd9Sstevel@tonic-gate zperror(gettext("could not set zoneid of pty")); 4487c478bd9Sstevel@tonic-gate goto bad; 4497c478bd9Sstevel@tonic-gate } 4507c478bd9Sstevel@tonic-gate 4517c478bd9Sstevel@tonic-gate return (slavefd); 4527c478bd9Sstevel@tonic-gate 4537c478bd9Sstevel@tonic-gate bad: 4547c478bd9Sstevel@tonic-gate (void) close(slavefd); 4557c478bd9Sstevel@tonic-gate return (-1); 4567c478bd9Sstevel@tonic-gate } 4577c478bd9Sstevel@tonic-gate 4587c478bd9Sstevel@tonic-gate /* 4597c478bd9Sstevel@tonic-gate * Place terminal into raw mode. 4607c478bd9Sstevel@tonic-gate */ 4617c478bd9Sstevel@tonic-gate static int 4627c478bd9Sstevel@tonic-gate set_tty_rawmode(int fd) 4637c478bd9Sstevel@tonic-gate { 4647c478bd9Sstevel@tonic-gate struct termios term; 4657c478bd9Sstevel@tonic-gate if (tcgetattr(fd, &term) < 0) { 4667c478bd9Sstevel@tonic-gate zperror(gettext("failed to get user terminal settings")); 4677c478bd9Sstevel@tonic-gate return (-1); 4687c478bd9Sstevel@tonic-gate } 4697c478bd9Sstevel@tonic-gate 4707c478bd9Sstevel@tonic-gate /* Stash for later, so we can revert back to previous mode */ 4717c478bd9Sstevel@tonic-gate save_termios = term; 4727c478bd9Sstevel@tonic-gate save_fd = fd; 4737c478bd9Sstevel@tonic-gate 4747c478bd9Sstevel@tonic-gate /* disable 8->7 bit strip, start/stop, enable any char to restart */ 4757c478bd9Sstevel@tonic-gate term.c_iflag &= ~(ISTRIP|IXON|IXANY); 4767c478bd9Sstevel@tonic-gate /* disable NL->CR, CR->NL, ignore CR, UPPER->lower */ 4777c478bd9Sstevel@tonic-gate term.c_iflag &= ~(INLCR|ICRNL|IGNCR|IUCLC); 4787c478bd9Sstevel@tonic-gate /* disable output post-processing */ 4797c478bd9Sstevel@tonic-gate term.c_oflag &= ~OPOST; 4807c478bd9Sstevel@tonic-gate /* disable canonical mode, signal chars, echo & extended functions */ 4817c478bd9Sstevel@tonic-gate term.c_lflag &= ~(ICANON|ISIG|ECHO|IEXTEN); 4827c478bd9Sstevel@tonic-gate 4837c478bd9Sstevel@tonic-gate term.c_cc[VMIN] = 1; /* byte-at-a-time */ 4847c478bd9Sstevel@tonic-gate term.c_cc[VTIME] = 0; 4857c478bd9Sstevel@tonic-gate 4867c478bd9Sstevel@tonic-gate if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &term)) { 4877c478bd9Sstevel@tonic-gate zperror(gettext("failed to set user terminal to raw mode")); 4887c478bd9Sstevel@tonic-gate return (-1); 4897c478bd9Sstevel@tonic-gate } 4907c478bd9Sstevel@tonic-gate 4917c478bd9Sstevel@tonic-gate /* 4927c478bd9Sstevel@tonic-gate * We need to know the value of VEOF so that we can properly process for 4937c478bd9Sstevel@tonic-gate * client-side ~<EOF>. But we have obliterated VEOF in term, 4947c478bd9Sstevel@tonic-gate * because VMIN overloads the same array slot in non-canonical mode. 4957c478bd9Sstevel@tonic-gate * Stupid @&^%! 4967c478bd9Sstevel@tonic-gate * 4977c478bd9Sstevel@tonic-gate * So here we construct the "effective" termios from the current 4987c478bd9Sstevel@tonic-gate * terminal settings, and the corrected VEOF and VEOL settings. 4997c478bd9Sstevel@tonic-gate */ 5007c478bd9Sstevel@tonic-gate if (tcgetattr(STDIN_FILENO, &effective_termios) < 0) { 5017c478bd9Sstevel@tonic-gate zperror(gettext("failed to get user terminal settings")); 5027c478bd9Sstevel@tonic-gate return (-1); 5037c478bd9Sstevel@tonic-gate } 5047c478bd9Sstevel@tonic-gate effective_termios.c_cc[VEOF] = save_termios.c_cc[VEOF]; 5057c478bd9Sstevel@tonic-gate effective_termios.c_cc[VEOL] = save_termios.c_cc[VEOL]; 5067c478bd9Sstevel@tonic-gate 5077c478bd9Sstevel@tonic-gate return (0); 5087c478bd9Sstevel@tonic-gate } 5097c478bd9Sstevel@tonic-gate 5107c478bd9Sstevel@tonic-gate /* 5117c478bd9Sstevel@tonic-gate * Copy terminal window size from our terminal to the pts. 5127c478bd9Sstevel@tonic-gate */ 5137c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 5147c478bd9Sstevel@tonic-gate static void 5157c478bd9Sstevel@tonic-gate sigwinch(int s) 5167c478bd9Sstevel@tonic-gate { 5177c478bd9Sstevel@tonic-gate struct winsize ws; 5187c478bd9Sstevel@tonic-gate 5197c478bd9Sstevel@tonic-gate if (ioctl(0, TIOCGWINSZ, &ws) == 0) 5207c478bd9Sstevel@tonic-gate (void) ioctl(masterfd, TIOCSWINSZ, &ws); 5217c478bd9Sstevel@tonic-gate } 5227c478bd9Sstevel@tonic-gate 523ff17c8bfSgjelinek static volatile int close_on_sig = -1; 524ff17c8bfSgjelinek 5257c478bd9Sstevel@tonic-gate static void 5267c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 5277c478bd9Sstevel@tonic-gate sigcld(int s) 5287c478bd9Sstevel@tonic-gate { 5297c478bd9Sstevel@tonic-gate int status; 5307c478bd9Sstevel@tonic-gate pid_t pid; 5317c478bd9Sstevel@tonic-gate 5327c478bd9Sstevel@tonic-gate /* 5337c478bd9Sstevel@tonic-gate * Peek at the exit status. If this isn't the process we cared 5347c478bd9Sstevel@tonic-gate * about, then just reap it. 5357c478bd9Sstevel@tonic-gate */ 5367c478bd9Sstevel@tonic-gate if ((pid = waitpid(child_pid, &status, WNOHANG|WNOWAIT)) != -1) { 5377c478bd9Sstevel@tonic-gate if (pid == child_pid && 538ff17c8bfSgjelinek (WIFEXITED(status) || WIFSIGNALED(status))) { 5397c478bd9Sstevel@tonic-gate dead = 1; 540ff17c8bfSgjelinek if (close_on_sig != -1) { 541ff17c8bfSgjelinek (void) write(close_on_sig, "a", 1); 542ff17c8bfSgjelinek (void) close(close_on_sig); 543ff17c8bfSgjelinek close_on_sig = -1; 544ff17c8bfSgjelinek } 545ff17c8bfSgjelinek } else { 5467c478bd9Sstevel@tonic-gate (void) waitpid(pid, &status, WNOHANG); 5477c478bd9Sstevel@tonic-gate } 5487c478bd9Sstevel@tonic-gate } 549ff17c8bfSgjelinek } 5507c478bd9Sstevel@tonic-gate 5517c478bd9Sstevel@tonic-gate /* 5527c478bd9Sstevel@tonic-gate * Some signals (currently, SIGINT) must be forwarded on to the process 5537c478bd9Sstevel@tonic-gate * group of the child process. 5547c478bd9Sstevel@tonic-gate */ 5557c478bd9Sstevel@tonic-gate static void 5567c478bd9Sstevel@tonic-gate sig_forward(int s) 5577c478bd9Sstevel@tonic-gate { 5587c478bd9Sstevel@tonic-gate if (child_pid != -1) { 559136dc9cbSAlexander Eremin (void) sigsend(P_PGID, child_pid, s); 5607c478bd9Sstevel@tonic-gate } 5617c478bd9Sstevel@tonic-gate } 5627c478bd9Sstevel@tonic-gate 5637c478bd9Sstevel@tonic-gate /* 5647c478bd9Sstevel@tonic-gate * reset terminal settings for global environment 5657c478bd9Sstevel@tonic-gate */ 5667c478bd9Sstevel@tonic-gate static void 5677c478bd9Sstevel@tonic-gate reset_tty() 5687c478bd9Sstevel@tonic-gate { 5697c478bd9Sstevel@tonic-gate (void) tcsetattr(save_fd, TCSADRAIN, &save_termios); 5707c478bd9Sstevel@tonic-gate } 5717c478bd9Sstevel@tonic-gate 5727c478bd9Sstevel@tonic-gate /* 5737c478bd9Sstevel@tonic-gate * Convert character to printable representation, for display with locally 5747c478bd9Sstevel@tonic-gate * echoed command characters (like when we need to display ~^D) 5757c478bd9Sstevel@tonic-gate */ 5767c478bd9Sstevel@tonic-gate static void 5777c478bd9Sstevel@tonic-gate canonify(char c, char *cc) 5787c478bd9Sstevel@tonic-gate { 5797c478bd9Sstevel@tonic-gate if (isprint(c)) { 5807c478bd9Sstevel@tonic-gate cc[0] = c; 5817c478bd9Sstevel@tonic-gate cc[1] = '\0'; 5827c478bd9Sstevel@tonic-gate } else if (c >= 0 && c <= 31) { /* ^@ through ^_ */ 5837c478bd9Sstevel@tonic-gate cc[0] = '^'; 5847c478bd9Sstevel@tonic-gate cc[1] = c + '@'; 5857c478bd9Sstevel@tonic-gate cc[2] = '\0'; 5867c478bd9Sstevel@tonic-gate } else { 5877c478bd9Sstevel@tonic-gate cc[0] = '\\'; 5887c478bd9Sstevel@tonic-gate cc[1] = ((c >> 6) & 7) + '0'; 5897c478bd9Sstevel@tonic-gate cc[2] = ((c >> 3) & 7) + '0'; 5907c478bd9Sstevel@tonic-gate cc[3] = (c & 7) + '0'; 5917c478bd9Sstevel@tonic-gate cc[4] = '\0'; 5927c478bd9Sstevel@tonic-gate } 5937c478bd9Sstevel@tonic-gate } 5947c478bd9Sstevel@tonic-gate 5957c478bd9Sstevel@tonic-gate /* 5967c478bd9Sstevel@tonic-gate * process_user_input watches the input stream for the escape sequence for 5977c478bd9Sstevel@tonic-gate * 'quit' (by default, tilde-period). Because we might be fed just one 5987c478bd9Sstevel@tonic-gate * keystroke at a time, state associated with the user input (are we at the 5997c478bd9Sstevel@tonic-gate * beginning of the line? are we locally echoing the next character?) is 6007c478bd9Sstevel@tonic-gate * maintained by beginning_of_line and local_echo across calls to the routine. 601d9e728a2Sgjelinek * If the write to outfd fails, we'll try to read from infd in an attempt 602d9e728a2Sgjelinek * to prevent deadlock between the two processes. 6037c478bd9Sstevel@tonic-gate * 6047c478bd9Sstevel@tonic-gate * This routine returns -1 when the 'quit' escape sequence has been issued, 605ff17c8bfSgjelinek * or an error is encountered, 1 if stdin is EOF, and 0 otherwise. 6067c478bd9Sstevel@tonic-gate */ 6077c478bd9Sstevel@tonic-gate static int 608ff17c8bfSgjelinek process_user_input(int outfd, int infd) 6097c478bd9Sstevel@tonic-gate { 6107c478bd9Sstevel@tonic-gate static boolean_t beginning_of_line = B_TRUE; 6117c478bd9Sstevel@tonic-gate static boolean_t local_echo = B_FALSE; 612ff17c8bfSgjelinek char ibuf[ZLOGIN_BUFSIZ]; 613ff17c8bfSgjelinek int nbytes; 614ff17c8bfSgjelinek char *buf = ibuf; 6157c478bd9Sstevel@tonic-gate char c = *buf; 616ff17c8bfSgjelinek 617ff17c8bfSgjelinek nbytes = read(STDIN_FILENO, ibuf, ZLOGIN_RDBUFSIZ); 618ff17c8bfSgjelinek if (nbytes == -1 && (errno != EINTR || dead)) 619ff17c8bfSgjelinek return (-1); 620ff17c8bfSgjelinek 621ff17c8bfSgjelinek if (nbytes == -1) /* The read was interrupted. */ 622ff17c8bfSgjelinek return (0); 623ff17c8bfSgjelinek 624ff17c8bfSgjelinek /* 0 read means EOF, close the pipe to the child */ 625ff17c8bfSgjelinek if (nbytes == 0) 626ff17c8bfSgjelinek return (1); 627ff17c8bfSgjelinek 6287c478bd9Sstevel@tonic-gate for (c = *buf; nbytes > 0; c = *buf, --nbytes) { 6297c478bd9Sstevel@tonic-gate buf++; 6307c478bd9Sstevel@tonic-gate if (beginning_of_line && !nocmdchar) { 6317c478bd9Sstevel@tonic-gate beginning_of_line = B_FALSE; 6327c478bd9Sstevel@tonic-gate if (c == cmdchar) { 6337c478bd9Sstevel@tonic-gate local_echo = B_TRUE; 6347c478bd9Sstevel@tonic-gate continue; 6357c478bd9Sstevel@tonic-gate } 6367c478bd9Sstevel@tonic-gate } else if (local_echo) { 6377c478bd9Sstevel@tonic-gate local_echo = B_FALSE; 6387c478bd9Sstevel@tonic-gate if (c == '.' || c == effective_termios.c_cc[VEOF]) { 6397c478bd9Sstevel@tonic-gate char cc[CANONIFY_LEN]; 640d9e728a2Sgjelinek 6417c478bd9Sstevel@tonic-gate canonify(c, cc); 6427c478bd9Sstevel@tonic-gate (void) write(STDOUT_FILENO, &cmdchar, 1); 6437c478bd9Sstevel@tonic-gate (void) write(STDOUT_FILENO, cc, strlen(cc)); 6447c478bd9Sstevel@tonic-gate return (-1); 6457c478bd9Sstevel@tonic-gate } 6467c478bd9Sstevel@tonic-gate } 647d9e728a2Sgjelinek retry: 648d9e728a2Sgjelinek if (write(outfd, &c, 1) <= 0) { 649d9e728a2Sgjelinek /* 650d9e728a2Sgjelinek * Since the fd we are writing to is opened with 651d9e728a2Sgjelinek * O_NONBLOCK it is possible to get EAGAIN if the 652d9e728a2Sgjelinek * pipe is full. One way this could happen is if we 653d9e728a2Sgjelinek * are writing a lot of data into the pipe in this loop 654d9e728a2Sgjelinek * and the application on the other end is echoing that 655d9e728a2Sgjelinek * data back out to its stdout. The output pipe can 656d9e728a2Sgjelinek * fill up since we are stuck here in this loop and not 657d9e728a2Sgjelinek * draining the other pipe. We can try to read some of 658d9e728a2Sgjelinek * the data to see if we can drain the pipe so that the 659d9e728a2Sgjelinek * application can continue to make progress. The read 660d9e728a2Sgjelinek * is non-blocking so we won't hang here. We also wait 661d9e728a2Sgjelinek * a bit before retrying since there could be other 662d9e728a2Sgjelinek * reasons why the pipe is full and we don't want to 663d9e728a2Sgjelinek * continuously retry. 664d9e728a2Sgjelinek */ 665d9e728a2Sgjelinek if (errno == EAGAIN) { 666d9e728a2Sgjelinek struct timespec rqtp; 667d9e728a2Sgjelinek int ln; 668ff17c8bfSgjelinek char obuf[ZLOGIN_BUFSIZ]; 669d9e728a2Sgjelinek 670ff17c8bfSgjelinek if ((ln = read(infd, obuf, ZLOGIN_BUFSIZ)) > 0) 671ff17c8bfSgjelinek (void) write(STDOUT_FILENO, obuf, ln); 672d9e728a2Sgjelinek 673d9e728a2Sgjelinek /* sleep for 10 milliseconds */ 674d9e728a2Sgjelinek rqtp.tv_sec = 0; 67519449258SJosef 'Jeff' Sipek rqtp.tv_nsec = MSEC2NSEC(10); 676d9e728a2Sgjelinek (void) nanosleep(&rqtp, NULL); 677d9e728a2Sgjelinek if (!dead) 678d9e728a2Sgjelinek goto retry; 679d9e728a2Sgjelinek } 680d9e728a2Sgjelinek 6817c478bd9Sstevel@tonic-gate return (-1); 682d9e728a2Sgjelinek } 6837c478bd9Sstevel@tonic-gate beginning_of_line = (c == '\r' || c == '\n' || 6847c478bd9Sstevel@tonic-gate c == effective_termios.c_cc[VKILL] || 6857c478bd9Sstevel@tonic-gate c == effective_termios.c_cc[VEOL] || 6867c478bd9Sstevel@tonic-gate c == effective_termios.c_cc[VSUSP] || 6877c478bd9Sstevel@tonic-gate c == effective_termios.c_cc[VINTR]); 6887c478bd9Sstevel@tonic-gate } 6897c478bd9Sstevel@tonic-gate return (0); 6907c478bd9Sstevel@tonic-gate } 6917c478bd9Sstevel@tonic-gate 6927c478bd9Sstevel@tonic-gate /* 693b52a7fd7Sgjelinek * This function prevents deadlock between zlogin and the application in the 694b52a7fd7Sgjelinek * zone that it is talking to. This can happen when we read from zlogin's 695b52a7fd7Sgjelinek * stdin and write the data down the pipe to the application. If the pipe 696b52a7fd7Sgjelinek * is full, we'll block in the write. Because zlogin could be blocked in 697b52a7fd7Sgjelinek * the write, it would never read the application's stdout/stderr so the 698b52a7fd7Sgjelinek * application can then block on those writes (when the pipe fills up). If the 699b52a7fd7Sgjelinek * the application gets blocked this way, it can never get around to reading 700b52a7fd7Sgjelinek * its stdin so that zlogin can unblock from its write. Once in this state, 701b52a7fd7Sgjelinek * the two processes are deadlocked. 702b52a7fd7Sgjelinek * 703b52a7fd7Sgjelinek * To prevent this, we want to verify that we can write into the pipe before we 704b52a7fd7Sgjelinek * read from our stdin. If the pipe already is pretty full, we bypass the read 705b52a7fd7Sgjelinek * for now. We'll circle back here again after the poll() so that we can 706b52a7fd7Sgjelinek * try again. When this function is called, we already know there is data 707ff17c8bfSgjelinek * ready to read on STDIN_FILENO. We return -1 if there is a problem, 1 if 708ff17c8bfSgjelinek * stdin is EOF, and 0 if everything is ok (even though we might not have 709ff17c8bfSgjelinek * read/written any data into the pipe on this iteration). 710b52a7fd7Sgjelinek */ 711b52a7fd7Sgjelinek static int 712b52a7fd7Sgjelinek process_raw_input(int stdin_fd, int appin_fd) 713b52a7fd7Sgjelinek { 714b52a7fd7Sgjelinek int cc; 715ad593b7fSArindam Sarkar struct stat64 sb; 716b52a7fd7Sgjelinek char ibuf[ZLOGIN_RDBUFSIZ]; 717b52a7fd7Sgjelinek 718b52a7fd7Sgjelinek /* Check how much data is already in the pipe */ 719ad593b7fSArindam Sarkar if (fstat64(appin_fd, &sb) == -1) { 720b52a7fd7Sgjelinek perror("stat failed"); 721b52a7fd7Sgjelinek return (-1); 722b52a7fd7Sgjelinek } 723b52a7fd7Sgjelinek 724b52a7fd7Sgjelinek if (dead) 725b52a7fd7Sgjelinek return (-1); 726b52a7fd7Sgjelinek 727b52a7fd7Sgjelinek /* 728b52a7fd7Sgjelinek * The pipe already has a lot of data in it, don't write any more 729b52a7fd7Sgjelinek * right now. 730b52a7fd7Sgjelinek */ 731b52a7fd7Sgjelinek if (sb.st_size >= HI_WATER) 732b52a7fd7Sgjelinek return (0); 733b52a7fd7Sgjelinek 734b52a7fd7Sgjelinek cc = read(STDIN_FILENO, ibuf, ZLOGIN_RDBUFSIZ); 735b52a7fd7Sgjelinek if (cc == -1 && (errno != EINTR || dead)) 736b52a7fd7Sgjelinek return (-1); 737b52a7fd7Sgjelinek 738b52a7fd7Sgjelinek if (cc == -1) /* The read was interrupted. */ 739b52a7fd7Sgjelinek return (0); 740b52a7fd7Sgjelinek 741ff17c8bfSgjelinek /* 0 read means EOF, close the pipe to the child */ 742ff17c8bfSgjelinek if (cc == 0) 743ff17c8bfSgjelinek return (1); 744ff17c8bfSgjelinek 745b52a7fd7Sgjelinek /* 746b52a7fd7Sgjelinek * stdin_fd is stdin of the target; so, the thing we'll write the user 747ff17c8bfSgjelinek * data *to*. 748b52a7fd7Sgjelinek */ 749b52a7fd7Sgjelinek if (write(stdin_fd, ibuf, cc) == -1) 750b52a7fd7Sgjelinek return (-1); 751b52a7fd7Sgjelinek 752b52a7fd7Sgjelinek return (0); 753b52a7fd7Sgjelinek } 754b52a7fd7Sgjelinek 755b52a7fd7Sgjelinek /* 756b52a7fd7Sgjelinek * Write the output from the application running in the zone. We can get 757b52a7fd7Sgjelinek * a signal during the write (usually it would be SIGCHLD when the application 758b52a7fd7Sgjelinek * has exited) so we loop to make sure we have written all of the data we read. 759b52a7fd7Sgjelinek */ 760b52a7fd7Sgjelinek static int 761b52a7fd7Sgjelinek process_output(int in_fd, int out_fd) 762b52a7fd7Sgjelinek { 763b52a7fd7Sgjelinek int wrote = 0; 764b52a7fd7Sgjelinek int cc; 765b52a7fd7Sgjelinek char ibuf[ZLOGIN_BUFSIZ]; 766b52a7fd7Sgjelinek 767b52a7fd7Sgjelinek cc = read(in_fd, ibuf, ZLOGIN_BUFSIZ); 768b52a7fd7Sgjelinek if (cc == -1 && (errno != EINTR || dead)) 769b52a7fd7Sgjelinek return (-1); 770b52a7fd7Sgjelinek if (cc == 0) /* EOF */ 771b52a7fd7Sgjelinek return (-1); 772b52a7fd7Sgjelinek if (cc == -1) /* The read was interrupted. */ 773b52a7fd7Sgjelinek return (0); 774b52a7fd7Sgjelinek 775b52a7fd7Sgjelinek do { 776b52a7fd7Sgjelinek int len; 777b52a7fd7Sgjelinek 778b52a7fd7Sgjelinek len = write(out_fd, ibuf + wrote, cc - wrote); 779b52a7fd7Sgjelinek if (len == -1 && errno != EINTR) 780b52a7fd7Sgjelinek return (-1); 781b52a7fd7Sgjelinek if (len != -1) 782b52a7fd7Sgjelinek wrote += len; 783b52a7fd7Sgjelinek } while (wrote < cc); 784b52a7fd7Sgjelinek 785b52a7fd7Sgjelinek return (0); 786b52a7fd7Sgjelinek } 787b52a7fd7Sgjelinek 788b52a7fd7Sgjelinek /* 7897c478bd9Sstevel@tonic-gate * This is the main I/O loop, and is shared across all zlogin modes. 7907c478bd9Sstevel@tonic-gate * Parameters: 7917c478bd9Sstevel@tonic-gate * stdin_fd: The fd representing 'stdin' for the slave side; input to 7927c478bd9Sstevel@tonic-gate * the zone will be written here. 7937c478bd9Sstevel@tonic-gate * 794b52a7fd7Sgjelinek * appin_fd: The fd representing the other end of the 'stdin' pipe (when 795b52a7fd7Sgjelinek * we're running non-interactive); used in process_raw_input 796b52a7fd7Sgjelinek * to ensure we don't fill up the application's stdin pipe. 797b52a7fd7Sgjelinek * 7987c478bd9Sstevel@tonic-gate * stdout_fd: The fd representing 'stdout' for the slave side; output 7997c478bd9Sstevel@tonic-gate * from the zone will arrive here. 8007c478bd9Sstevel@tonic-gate * 8017c478bd9Sstevel@tonic-gate * stderr_fd: The fd representing 'stderr' for the slave side; output 8027c478bd9Sstevel@tonic-gate * from the zone will arrive here. 8037c478bd9Sstevel@tonic-gate * 8047c478bd9Sstevel@tonic-gate * raw_mode: If TRUE, then no processing (for example, for '~.') will 8057c478bd9Sstevel@tonic-gate * be performed on the input coming from STDIN. 8067c478bd9Sstevel@tonic-gate * 8077c478bd9Sstevel@tonic-gate * stderr_fd may be specified as -1 if there is no stderr (only non-interactive 8087c478bd9Sstevel@tonic-gate * mode supplies a stderr). 8097c478bd9Sstevel@tonic-gate * 8107c478bd9Sstevel@tonic-gate */ 8117c478bd9Sstevel@tonic-gate static void 812ff17c8bfSgjelinek doio(int stdin_fd, int appin_fd, int stdout_fd, int stderr_fd, int sig_fd, 813b52a7fd7Sgjelinek boolean_t raw_mode) 8147c478bd9Sstevel@tonic-gate { 815ff17c8bfSgjelinek struct pollfd pollfds[4]; 816e6e67599Sdp char ibuf[ZLOGIN_BUFSIZ]; 8177c478bd9Sstevel@tonic-gate int cc, ret; 8187c478bd9Sstevel@tonic-gate 8197c478bd9Sstevel@tonic-gate /* read from stdout of zone and write to stdout of global zone */ 8207c478bd9Sstevel@tonic-gate pollfds[0].fd = stdout_fd; 8217c478bd9Sstevel@tonic-gate pollfds[0].events = POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI; 8227c478bd9Sstevel@tonic-gate 8237c478bd9Sstevel@tonic-gate /* read from stderr of zone and write to stderr of global zone */ 8247c478bd9Sstevel@tonic-gate pollfds[1].fd = stderr_fd; 8257c478bd9Sstevel@tonic-gate pollfds[1].events = pollfds[0].events; 8267c478bd9Sstevel@tonic-gate 8277c478bd9Sstevel@tonic-gate /* read from stdin of global zone and write to stdin of zone */ 8287c478bd9Sstevel@tonic-gate pollfds[2].fd = STDIN_FILENO; 8297c478bd9Sstevel@tonic-gate pollfds[2].events = pollfds[0].events; 8307c478bd9Sstevel@tonic-gate 831ff17c8bfSgjelinek /* read from signalling pipe so we know when child dies */ 832ff17c8bfSgjelinek pollfds[3].fd = sig_fd; 833ff17c8bfSgjelinek pollfds[3].events = pollfds[0].events; 834ff17c8bfSgjelinek 8357c478bd9Sstevel@tonic-gate for (;;) { 8367c478bd9Sstevel@tonic-gate pollfds[0].revents = pollfds[1].revents = 837ff17c8bfSgjelinek pollfds[2].revents = pollfds[3].revents = 0; 8387c478bd9Sstevel@tonic-gate 8397c478bd9Sstevel@tonic-gate if (dead) 8407c478bd9Sstevel@tonic-gate break; 8417c478bd9Sstevel@tonic-gate 842ff17c8bfSgjelinek /* 843ff17c8bfSgjelinek * There is a race condition here where we can receive the 844ff17c8bfSgjelinek * child death signal, set the dead flag, but since we have 845ff17c8bfSgjelinek * passed the test above, we would go into poll and hang. 846ff17c8bfSgjelinek * To avoid this we use the sig_fd as an additional poll fd. 847ff17c8bfSgjelinek * The signal handler writes into the other end of this pipe 848ff17c8bfSgjelinek * when the child dies so that the poll will always see that 849ff17c8bfSgjelinek * input and proceed. We just loop around at that point and 850ff17c8bfSgjelinek * then notice the dead flag. 851ff17c8bfSgjelinek */ 852ff17c8bfSgjelinek 8537c478bd9Sstevel@tonic-gate ret = poll(pollfds, 8547c478bd9Sstevel@tonic-gate sizeof (pollfds) / sizeof (struct pollfd), -1); 855ff17c8bfSgjelinek 8567c478bd9Sstevel@tonic-gate if (ret == -1 && errno != EINTR) { 8577c478bd9Sstevel@tonic-gate perror("poll failed"); 8587c478bd9Sstevel@tonic-gate break; 8597c478bd9Sstevel@tonic-gate } 8607c478bd9Sstevel@tonic-gate 8617c478bd9Sstevel@tonic-gate if (errno == EINTR && dead) { 8627c478bd9Sstevel@tonic-gate break; 8637c478bd9Sstevel@tonic-gate } 8647c478bd9Sstevel@tonic-gate 8657c478bd9Sstevel@tonic-gate /* event from master side stdout */ 8667c478bd9Sstevel@tonic-gate if (pollfds[0].revents) { 8677c478bd9Sstevel@tonic-gate if (pollfds[0].revents & 8687c478bd9Sstevel@tonic-gate (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) { 869b52a7fd7Sgjelinek if (process_output(stdout_fd, STDOUT_FILENO) 870b52a7fd7Sgjelinek != 0) 8717c478bd9Sstevel@tonic-gate break; 8727c478bd9Sstevel@tonic-gate } else { 8737c478bd9Sstevel@tonic-gate pollerr = pollfds[0].revents; 8747c478bd9Sstevel@tonic-gate break; 8757c478bd9Sstevel@tonic-gate } 8767c478bd9Sstevel@tonic-gate } 8777c478bd9Sstevel@tonic-gate 8787c478bd9Sstevel@tonic-gate /* event from master side stderr */ 8797c478bd9Sstevel@tonic-gate if (pollfds[1].revents) { 8807c478bd9Sstevel@tonic-gate if (pollfds[1].revents & 8817c478bd9Sstevel@tonic-gate (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) { 882b52a7fd7Sgjelinek if (process_output(stderr_fd, STDERR_FILENO) 883b52a7fd7Sgjelinek != 0) 8847c478bd9Sstevel@tonic-gate break; 8857c478bd9Sstevel@tonic-gate } else { 8867c478bd9Sstevel@tonic-gate pollerr = pollfds[1].revents; 8877c478bd9Sstevel@tonic-gate break; 8887c478bd9Sstevel@tonic-gate } 8897c478bd9Sstevel@tonic-gate } 8907c478bd9Sstevel@tonic-gate 8917c478bd9Sstevel@tonic-gate /* event from user STDIN side */ 8927c478bd9Sstevel@tonic-gate if (pollfds[2].revents) { 8937c478bd9Sstevel@tonic-gate if (pollfds[2].revents & 8947c478bd9Sstevel@tonic-gate (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) { 8957c478bd9Sstevel@tonic-gate /* 8967c478bd9Sstevel@tonic-gate * stdin fd is stdin of the target; so, 8977c478bd9Sstevel@tonic-gate * the thing we'll write the user data *to*. 8987c478bd9Sstevel@tonic-gate * 8997c478bd9Sstevel@tonic-gate * Also, unlike on the output side, we 900ff17c8bfSgjelinek * close the pipe on a zero-length message. 9017c478bd9Sstevel@tonic-gate */ 902ff17c8bfSgjelinek int res; 903b52a7fd7Sgjelinek 904ff17c8bfSgjelinek if (raw_mode) 905ff17c8bfSgjelinek res = process_raw_input(stdin_fd, 906ff17c8bfSgjelinek appin_fd); 907ff17c8bfSgjelinek else 908ff17c8bfSgjelinek res = process_user_input(stdin_fd, 909ff17c8bfSgjelinek stdout_fd); 910ff17c8bfSgjelinek 911ff17c8bfSgjelinek if (res < 0) 912ff17c8bfSgjelinek break; 913ff17c8bfSgjelinek if (res > 0) { 914ff17c8bfSgjelinek /* EOF (close) child's stdin_fd */ 915ff17c8bfSgjelinek pollfds[2].fd = -1; 916ff17c8bfSgjelinek while ((res = close(stdin_fd)) != 0 && 917ff17c8bfSgjelinek errno == EINTR) 918ff17c8bfSgjelinek ; 919ff17c8bfSgjelinek if (res != 0) 9207c478bd9Sstevel@tonic-gate break; 9217c478bd9Sstevel@tonic-gate } 922ff17c8bfSgjelinek 923ff17c8bfSgjelinek } else if (raw_mode && pollfds[2].revents & POLLHUP) { 9247c478bd9Sstevel@tonic-gate /* 9257c478bd9Sstevel@tonic-gate * It's OK to get a POLLHUP on STDIN-- it 9267c478bd9Sstevel@tonic-gate * always happens if you do: 9277c478bd9Sstevel@tonic-gate * 9287c478bd9Sstevel@tonic-gate * echo foo | zlogin <zone> <command> 9297c478bd9Sstevel@tonic-gate * 9307c478bd9Sstevel@tonic-gate * We reset fd to -1 in this case to clear 931ff17c8bfSgjelinek * the condition and close the pipe (EOF) to 932ff17c8bfSgjelinek * the other side in order to wrap things up. 9337c478bd9Sstevel@tonic-gate */ 934ff17c8bfSgjelinek int res; 935ff17c8bfSgjelinek 9367c478bd9Sstevel@tonic-gate pollfds[2].fd = -1; 937ff17c8bfSgjelinek while ((res = close(stdin_fd)) != 0 && 938ff17c8bfSgjelinek errno == EINTR) 939ff17c8bfSgjelinek ; 940ff17c8bfSgjelinek if (res != 0) 941ff17c8bfSgjelinek break; 9427c478bd9Sstevel@tonic-gate } else { 9437c478bd9Sstevel@tonic-gate pollerr = pollfds[2].revents; 9447c478bd9Sstevel@tonic-gate break; 9457c478bd9Sstevel@tonic-gate } 9467c478bd9Sstevel@tonic-gate } 9477c478bd9Sstevel@tonic-gate } 9487c478bd9Sstevel@tonic-gate 9497c478bd9Sstevel@tonic-gate /* 9507c478bd9Sstevel@tonic-gate * We are in the midst of dying, but try to poll with a short 9517c478bd9Sstevel@tonic-gate * timeout to see if we can catch the last bit of I/O from the 9527c478bd9Sstevel@tonic-gate * children. 9537c478bd9Sstevel@tonic-gate */ 954b52a7fd7Sgjelinek retry: 955b52a7fd7Sgjelinek pollfds[0].revents = pollfds[1].revents = 0; 956b52a7fd7Sgjelinek (void) poll(pollfds, 2, 100); 9577c478bd9Sstevel@tonic-gate if (pollfds[0].revents & 9587c478bd9Sstevel@tonic-gate (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) { 959b52a7fd7Sgjelinek if ((cc = read(stdout_fd, ibuf, ZLOGIN_BUFSIZ)) > 0) { 9607c478bd9Sstevel@tonic-gate (void) write(STDOUT_FILENO, ibuf, cc); 961b52a7fd7Sgjelinek goto retry; 962b52a7fd7Sgjelinek } 9637c478bd9Sstevel@tonic-gate } 9647c478bd9Sstevel@tonic-gate if (pollfds[1].revents & 9657c478bd9Sstevel@tonic-gate (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) { 966b52a7fd7Sgjelinek if ((cc = read(stderr_fd, ibuf, ZLOGIN_BUFSIZ)) > 0) { 9677c478bd9Sstevel@tonic-gate (void) write(STDERR_FILENO, ibuf, cc); 968b52a7fd7Sgjelinek goto retry; 969b52a7fd7Sgjelinek } 9707c478bd9Sstevel@tonic-gate } 9717c478bd9Sstevel@tonic-gate } 9727c478bd9Sstevel@tonic-gate 973858a4b99Ssl108498 /* 974858a4b99Ssl108498 * Fetch the user_cmd brand hook for getting a user's passwd(4) entry. 975858a4b99Ssl108498 */ 976858a4b99Ssl108498 static const char * 977858a4b99Ssl108498 zone_get_user_cmd(brand_handle_t bh, const char *login, char *user_cmd, 978858a4b99Ssl108498 size_t len) 979858a4b99Ssl108498 { 980858a4b99Ssl108498 bzero(user_cmd, sizeof (user_cmd)); 981858a4b99Ssl108498 if (brand_get_user_cmd(bh, login, user_cmd, len) != 0) 982858a4b99Ssl108498 return (NULL); 983858a4b99Ssl108498 984858a4b99Ssl108498 return (user_cmd); 985858a4b99Ssl108498 } 986858a4b99Ssl108498 987858a4b99Ssl108498 /* From libc */ 988858a4b99Ssl108498 extern int str2passwd(const char *, int, void *, char *, int); 989858a4b99Ssl108498 990858a4b99Ssl108498 /* 991858a4b99Ssl108498 * exec() the user_cmd brand hook, and convert the output string to a 992858a4b99Ssl108498 * struct passwd. This is to be called after zone_enter(). 993858a4b99Ssl108498 * 994858a4b99Ssl108498 */ 995858a4b99Ssl108498 static struct passwd * 996858a4b99Ssl108498 zone_get_user_pw(const char *user_cmd, struct passwd *pwent, char *pwbuf, 997858a4b99Ssl108498 int pwbuflen) 998858a4b99Ssl108498 { 999858a4b99Ssl108498 char pwline[NSS_BUFLEN_PASSWD]; 1000858a4b99Ssl108498 char *cin = NULL; 1001858a4b99Ssl108498 FILE *fin; 1002858a4b99Ssl108498 int status; 1003858a4b99Ssl108498 1004858a4b99Ssl108498 assert(getzoneid() != GLOBAL_ZONEID); 1005858a4b99Ssl108498 1006858a4b99Ssl108498 if ((fin = popen(user_cmd, "r")) == NULL) 1007858a4b99Ssl108498 return (NULL); 1008858a4b99Ssl108498 1009858a4b99Ssl108498 while (cin == NULL && !feof(fin)) 1010858a4b99Ssl108498 cin = fgets(pwline, sizeof (pwline), fin); 1011858a4b99Ssl108498 1012858a4b99Ssl108498 if (cin == NULL) { 1013858a4b99Ssl108498 (void) pclose(fin); 1014858a4b99Ssl108498 return (NULL); 1015858a4b99Ssl108498 } 1016858a4b99Ssl108498 1017858a4b99Ssl108498 status = pclose(fin); 1018858a4b99Ssl108498 if (!WIFEXITED(status)) 1019858a4b99Ssl108498 return (NULL); 1020858a4b99Ssl108498 if (WEXITSTATUS(status) != 0) 1021858a4b99Ssl108498 return (NULL); 1022858a4b99Ssl108498 1023858a4b99Ssl108498 if (str2passwd(pwline, sizeof (pwline), pwent, pwbuf, pwbuflen) == 0) 1024858a4b99Ssl108498 return (pwent); 1025858a4b99Ssl108498 else 1026858a4b99Ssl108498 return (NULL); 1027858a4b99Ssl108498 } 1028858a4b99Ssl108498 10299acbbeafSnn35248 static char ** 1030123807fbSedp zone_login_cmd(brand_handle_t bh, const char *login) 10319acbbeafSnn35248 { 10329acbbeafSnn35248 static char result_buf[ARG_MAX]; 10339acbbeafSnn35248 char **new_argv, *ptr, *lasts; 10349acbbeafSnn35248 int n, a; 10359acbbeafSnn35248 10369acbbeafSnn35248 /* Get the login command for the target zone. */ 10379acbbeafSnn35248 bzero(result_buf, sizeof (result_buf)); 1038cb8a054bSGlenn Faden 1039cb8a054bSGlenn Faden if (forced_login) { 1040cb8a054bSGlenn Faden if (brand_get_forcedlogin_cmd(bh, login, 1041cb8a054bSGlenn Faden result_buf, sizeof (result_buf)) != 0) 1042cb8a054bSGlenn Faden return (NULL); 1043cb8a054bSGlenn Faden } else { 1044123807fbSedp if (brand_get_login_cmd(bh, login, 10459acbbeafSnn35248 result_buf, sizeof (result_buf)) != 0) 10469acbbeafSnn35248 return (NULL); 1047cb8a054bSGlenn Faden } 10489acbbeafSnn35248 10499acbbeafSnn35248 /* 10509acbbeafSnn35248 * We got back a string that we'd like to execute. But since 10519acbbeafSnn35248 * we're not doing the execution via a shell we'll need to convert 10529acbbeafSnn35248 * the exec string to an array of strings. We'll do that here 10539acbbeafSnn35248 * but we're going to be very simplistic about it and break stuff 10549acbbeafSnn35248 * up based on spaces. We're not even going to support any kind 10559acbbeafSnn35248 * of quoting or escape characters. It's truly amazing that 10569acbbeafSnn35248 * there is no library function in OpenSolaris to do this for us. 10579acbbeafSnn35248 */ 10589acbbeafSnn35248 10599acbbeafSnn35248 /* 10609acbbeafSnn35248 * Be paranoid. Since we're deliniating based on spaces make 10619acbbeafSnn35248 * sure there are no adjacent spaces. 10629acbbeafSnn35248 */ 10639acbbeafSnn35248 if (strstr(result_buf, " ") != NULL) 10649acbbeafSnn35248 return (NULL); 10659acbbeafSnn35248 10669acbbeafSnn35248 /* Remove any trailing whitespace. */ 10679acbbeafSnn35248 n = strlen(result_buf); 10689acbbeafSnn35248 if (result_buf[n - 1] == ' ') 10699acbbeafSnn35248 result_buf[n - 1] = '\0'; 10709acbbeafSnn35248 10719acbbeafSnn35248 /* Count how many elements there are in the exec string. */ 10729acbbeafSnn35248 ptr = result_buf; 10739acbbeafSnn35248 for (n = 2; ((ptr = strchr(ptr + 1, (int)' ')) != NULL); n++) 10749acbbeafSnn35248 ; 10759acbbeafSnn35248 10769acbbeafSnn35248 /* Allocate the argv array that we're going to return. */ 10779acbbeafSnn35248 if ((new_argv = malloc(sizeof (char *) * n)) == NULL) 10789acbbeafSnn35248 return (NULL); 10799acbbeafSnn35248 10809acbbeafSnn35248 /* Tokenize the exec string and return. */ 10819acbbeafSnn35248 a = 0; 10829acbbeafSnn35248 new_argv[a++] = result_buf; 10839acbbeafSnn35248 if (n > 2) { 10849acbbeafSnn35248 (void) strtok_r(result_buf, " ", &lasts); 10859acbbeafSnn35248 while ((new_argv[a++] = strtok_r(NULL, " ", &lasts)) != NULL) 10869acbbeafSnn35248 ; 10879acbbeafSnn35248 } else { 10889acbbeafSnn35248 new_argv[a++] = NULL; 10899acbbeafSnn35248 } 10909acbbeafSnn35248 assert(n == a); 10919acbbeafSnn35248 return (new_argv); 10929acbbeafSnn35248 } 10939acbbeafSnn35248 10947c478bd9Sstevel@tonic-gate /* 10957c478bd9Sstevel@tonic-gate * Prepare argv array for exec'd process; if we're passing commands to the 10967c478bd9Sstevel@tonic-gate * new process, then use su(1M) to do the invocation. Otherwise, use 10977c478bd9Sstevel@tonic-gate * 'login -z <from_zonename> -f' (-z is an undocumented option which tells 10987c478bd9Sstevel@tonic-gate * login that we're coming from another zone, and to disregard its CONSOLE 10997c478bd9Sstevel@tonic-gate * checks). 11007c478bd9Sstevel@tonic-gate */ 11017c478bd9Sstevel@tonic-gate static char ** 1102123807fbSedp prep_args(brand_handle_t bh, const char *login, char **argv) 11037c478bd9Sstevel@tonic-gate { 11047c478bd9Sstevel@tonic-gate int argc = 0, a = 0, i, n = -1; 11057c478bd9Sstevel@tonic-gate char **new_argv; 11067c478bd9Sstevel@tonic-gate 11077c478bd9Sstevel@tonic-gate if (argv != NULL) { 11087c478bd9Sstevel@tonic-gate size_t subshell_len = 1; 11097c478bd9Sstevel@tonic-gate char *subshell; 11107c478bd9Sstevel@tonic-gate 11117c478bd9Sstevel@tonic-gate while (argv[argc] != NULL) 11127c478bd9Sstevel@tonic-gate argc++; 11137c478bd9Sstevel@tonic-gate 11147c478bd9Sstevel@tonic-gate for (i = 0; i < argc; i++) { 11157c478bd9Sstevel@tonic-gate subshell_len += strlen(argv[i]) + 1; 11167c478bd9Sstevel@tonic-gate } 11177c478bd9Sstevel@tonic-gate if ((subshell = calloc(1, subshell_len)) == NULL) 11187c478bd9Sstevel@tonic-gate return (NULL); 11197c478bd9Sstevel@tonic-gate 11207c478bd9Sstevel@tonic-gate for (i = 0; i < argc; i++) { 11217c478bd9Sstevel@tonic-gate (void) strcat(subshell, argv[i]); 11227c478bd9Sstevel@tonic-gate (void) strcat(subshell, " "); 11237c478bd9Sstevel@tonic-gate } 11247c478bd9Sstevel@tonic-gate 11257c478bd9Sstevel@tonic-gate if (failsafe) { 11267c478bd9Sstevel@tonic-gate n = 4; 11277c478bd9Sstevel@tonic-gate if ((new_argv = malloc(sizeof (char *) * n)) == NULL) 11287c478bd9Sstevel@tonic-gate return (NULL); 11297c478bd9Sstevel@tonic-gate 11307c478bd9Sstevel@tonic-gate new_argv[a++] = FAILSAFESHELL; 11317c478bd9Sstevel@tonic-gate } else { 11327c478bd9Sstevel@tonic-gate n = 5; 11337c478bd9Sstevel@tonic-gate if ((new_argv = malloc(sizeof (char *) * n)) == NULL) 11347c478bd9Sstevel@tonic-gate return (NULL); 11357c478bd9Sstevel@tonic-gate 11367c478bd9Sstevel@tonic-gate new_argv[a++] = SUPATH; 1137cb8a054bSGlenn Faden if (strcmp(login, "root") != 0) { 1138cb8a054bSGlenn Faden new_argv[a++] = "-"; 1139cb8a054bSGlenn Faden n++; 1140cb8a054bSGlenn Faden } 11419acbbeafSnn35248 new_argv[a++] = (char *)login; 11427c478bd9Sstevel@tonic-gate } 11437c478bd9Sstevel@tonic-gate new_argv[a++] = "-c"; 11447c478bd9Sstevel@tonic-gate new_argv[a++] = subshell; 11457c478bd9Sstevel@tonic-gate new_argv[a++] = NULL; 11467c478bd9Sstevel@tonic-gate assert(a == n); 11477c478bd9Sstevel@tonic-gate } else { 11487c478bd9Sstevel@tonic-gate if (failsafe) { 11497c478bd9Sstevel@tonic-gate n = 2; 11507c478bd9Sstevel@tonic-gate if ((new_argv = malloc(sizeof (char *) * n)) == NULL) 11517c478bd9Sstevel@tonic-gate return (NULL); 11527c478bd9Sstevel@tonic-gate new_argv[a++] = FAILSAFESHELL; 11537c478bd9Sstevel@tonic-gate new_argv[a++] = NULL; 11547c478bd9Sstevel@tonic-gate assert(n == a); 11559acbbeafSnn35248 } else { 1156123807fbSedp new_argv = zone_login_cmd(bh, login); 11579acbbeafSnn35248 } 11589acbbeafSnn35248 } 11599acbbeafSnn35248 11607c478bd9Sstevel@tonic-gate return (new_argv); 11617c478bd9Sstevel@tonic-gate } 11627c478bd9Sstevel@tonic-gate 11637c478bd9Sstevel@tonic-gate /* 11647c478bd9Sstevel@tonic-gate * Helper routine for prep_env below. 11657c478bd9Sstevel@tonic-gate */ 11667c478bd9Sstevel@tonic-gate static char * 11677c478bd9Sstevel@tonic-gate add_env(char *name, char *value) 11687c478bd9Sstevel@tonic-gate { 11697c478bd9Sstevel@tonic-gate size_t sz = strlen(name) + strlen(value) + 2; /* name, =, value, NUL */ 11707c478bd9Sstevel@tonic-gate char *str; 11717c478bd9Sstevel@tonic-gate 11727c478bd9Sstevel@tonic-gate if ((str = malloc(sz)) == NULL) 11737c478bd9Sstevel@tonic-gate return (NULL); 11747c478bd9Sstevel@tonic-gate 11757c478bd9Sstevel@tonic-gate (void) snprintf(str, sz, "%s=%s", name, value); 11767c478bd9Sstevel@tonic-gate return (str); 11777c478bd9Sstevel@tonic-gate } 11787c478bd9Sstevel@tonic-gate 11797c478bd9Sstevel@tonic-gate /* 11807c478bd9Sstevel@tonic-gate * Prepare envp array for exec'd process. 11817c478bd9Sstevel@tonic-gate */ 11827c478bd9Sstevel@tonic-gate static char ** 11837c478bd9Sstevel@tonic-gate prep_env() 11847c478bd9Sstevel@tonic-gate { 11857c478bd9Sstevel@tonic-gate int e = 0, size = 1; 11867c478bd9Sstevel@tonic-gate char **new_env, *estr; 11877c478bd9Sstevel@tonic-gate char *term = getenv("TERM"); 11887c478bd9Sstevel@tonic-gate 11897c478bd9Sstevel@tonic-gate size++; /* for $PATH */ 11907c478bd9Sstevel@tonic-gate if (term != NULL) 11917c478bd9Sstevel@tonic-gate size++; 11927c478bd9Sstevel@tonic-gate 11937c478bd9Sstevel@tonic-gate /* 11947c478bd9Sstevel@tonic-gate * In failsafe mode we set $HOME, since '-l' isn't valid in this mode. 11957c478bd9Sstevel@tonic-gate * We also set $SHELL, since neither login nor su will be around to do 11967c478bd9Sstevel@tonic-gate * it. 11977c478bd9Sstevel@tonic-gate */ 11987c478bd9Sstevel@tonic-gate if (failsafe) 11997c478bd9Sstevel@tonic-gate size += 2; 12007c478bd9Sstevel@tonic-gate 12017c478bd9Sstevel@tonic-gate if ((new_env = malloc(sizeof (char *) * size)) == NULL) 12027c478bd9Sstevel@tonic-gate return (NULL); 12037c478bd9Sstevel@tonic-gate 12047c478bd9Sstevel@tonic-gate if ((estr = add_env("PATH", DEF_PATH)) == NULL) 12057c478bd9Sstevel@tonic-gate return (NULL); 12067c478bd9Sstevel@tonic-gate new_env[e++] = estr; 12077c478bd9Sstevel@tonic-gate 12087c478bd9Sstevel@tonic-gate if (term != NULL) { 12097c478bd9Sstevel@tonic-gate if ((estr = add_env("TERM", term)) == NULL) 12107c478bd9Sstevel@tonic-gate return (NULL); 12117c478bd9Sstevel@tonic-gate new_env[e++] = estr; 12127c478bd9Sstevel@tonic-gate } 12137c478bd9Sstevel@tonic-gate 12147c478bd9Sstevel@tonic-gate if (failsafe) { 12157c478bd9Sstevel@tonic-gate if ((estr = add_env("HOME", "/")) == NULL) 12167c478bd9Sstevel@tonic-gate return (NULL); 12177c478bd9Sstevel@tonic-gate new_env[e++] = estr; 12187c478bd9Sstevel@tonic-gate 12197c478bd9Sstevel@tonic-gate if ((estr = add_env("SHELL", FAILSAFESHELL)) == NULL) 12207c478bd9Sstevel@tonic-gate return (NULL); 12217c478bd9Sstevel@tonic-gate new_env[e++] = estr; 12227c478bd9Sstevel@tonic-gate } 12237c478bd9Sstevel@tonic-gate 12247c478bd9Sstevel@tonic-gate new_env[e++] = NULL; 12257c478bd9Sstevel@tonic-gate 12267c478bd9Sstevel@tonic-gate assert(e == size); 12277c478bd9Sstevel@tonic-gate 12287c478bd9Sstevel@tonic-gate return (new_env); 12297c478bd9Sstevel@tonic-gate } 12307c478bd9Sstevel@tonic-gate 12317c478bd9Sstevel@tonic-gate /* 12327c478bd9Sstevel@tonic-gate * Finish the preparation of the envp array for exec'd non-interactive 12337c478bd9Sstevel@tonic-gate * zlogins. This is called in the child process *after* we zone_enter(), since 12347c478bd9Sstevel@tonic-gate * it derives things we can only know within the zone, such as $HOME, $SHELL, 12357c478bd9Sstevel@tonic-gate * etc. We need only do this in the non-interactive, mode, since otherwise 12367c478bd9Sstevel@tonic-gate * login(1) will do it. We don't do this in failsafe mode, since it presents 12377c478bd9Sstevel@tonic-gate * additional ways in which the command could fail, and we'd prefer to avoid 12387c478bd9Sstevel@tonic-gate * that. 12397c478bd9Sstevel@tonic-gate */ 12407c478bd9Sstevel@tonic-gate static char ** 1241858a4b99Ssl108498 prep_env_noninteractive(const char *user_cmd, char **env) 12427c478bd9Sstevel@tonic-gate { 12437c478bd9Sstevel@tonic-gate size_t size; 12447c478bd9Sstevel@tonic-gate char **new_env; 12457c478bd9Sstevel@tonic-gate int e, i; 12467c478bd9Sstevel@tonic-gate char *estr; 12477c478bd9Sstevel@tonic-gate char varmail[LOGNAME_MAX + 11]; /* strlen(/var/mail/) = 10, NUL */ 1248858a4b99Ssl108498 char pwbuf[NSS_BUFLEN_PASSWD + 1]; 1249858a4b99Ssl108498 struct passwd pwent; 1250858a4b99Ssl108498 struct passwd *pw = NULL; 12517c478bd9Sstevel@tonic-gate 12527c478bd9Sstevel@tonic-gate assert(env != NULL); 12537c478bd9Sstevel@tonic-gate assert(failsafe == 0); 12547c478bd9Sstevel@tonic-gate 12557c478bd9Sstevel@tonic-gate /* 1256858a4b99Ssl108498 * Exec the "user_cmd" brand hook to get a pwent for the 1257858a4b99Ssl108498 * login user. If this fails, HOME will be set to "/", SHELL 1258858a4b99Ssl108498 * will be set to $DEFAULTSHELL, and we will continue to exec 1259858a4b99Ssl108498 * SUPATH <login> -c <cmd>. 1260858a4b99Ssl108498 */ 1261858a4b99Ssl108498 pw = zone_get_user_pw(user_cmd, &pwent, pwbuf, sizeof (pwbuf)); 1262858a4b99Ssl108498 1263858a4b99Ssl108498 /* 12647c478bd9Sstevel@tonic-gate * Get existing envp size. 12657c478bd9Sstevel@tonic-gate */ 12667c478bd9Sstevel@tonic-gate for (size = 0; env[size] != NULL; size++) 12677c478bd9Sstevel@tonic-gate ; 1268858a4b99Ssl108498 12697c478bd9Sstevel@tonic-gate e = size; 12707c478bd9Sstevel@tonic-gate 12717c478bd9Sstevel@tonic-gate /* 12727c478bd9Sstevel@tonic-gate * Finish filling out the environment; we duplicate the environment 12737c478bd9Sstevel@tonic-gate * setup described in login(1), for lack of a better precedent. 12747c478bd9Sstevel@tonic-gate */ 1275858a4b99Ssl108498 if (pw != NULL) 12767c478bd9Sstevel@tonic-gate size += 3; /* LOGNAME, HOME, MAIL */ 1277858a4b99Ssl108498 else 1278858a4b99Ssl108498 size += 1; /* HOME */ 1279858a4b99Ssl108498 12807c478bd9Sstevel@tonic-gate size++; /* always fill in SHELL */ 12817c478bd9Sstevel@tonic-gate size++; /* terminating NULL */ 12827c478bd9Sstevel@tonic-gate 12837c478bd9Sstevel@tonic-gate if ((new_env = malloc(sizeof (char *) * size)) == NULL) 12847c478bd9Sstevel@tonic-gate goto malloc_fail; 12857c478bd9Sstevel@tonic-gate 12867c478bd9Sstevel@tonic-gate /* 12877c478bd9Sstevel@tonic-gate * Copy existing elements of env into new_env. 12887c478bd9Sstevel@tonic-gate */ 12897c478bd9Sstevel@tonic-gate for (i = 0; env[i] != NULL; i++) { 12907c478bd9Sstevel@tonic-gate if ((new_env[i] = strdup(env[i])) == NULL) 12917c478bd9Sstevel@tonic-gate goto malloc_fail; 12927c478bd9Sstevel@tonic-gate } 12937c478bd9Sstevel@tonic-gate assert(e == i); 12947c478bd9Sstevel@tonic-gate 12957c478bd9Sstevel@tonic-gate if (pw != NULL) { 12967c478bd9Sstevel@tonic-gate if ((estr = add_env("LOGNAME", pw->pw_name)) == NULL) 12977c478bd9Sstevel@tonic-gate goto malloc_fail; 12987c478bd9Sstevel@tonic-gate new_env[e++] = estr; 12997c478bd9Sstevel@tonic-gate 13007c478bd9Sstevel@tonic-gate if ((estr = add_env("HOME", pw->pw_dir)) == NULL) 13017c478bd9Sstevel@tonic-gate goto malloc_fail; 13027c478bd9Sstevel@tonic-gate new_env[e++] = estr; 13037c478bd9Sstevel@tonic-gate 13047c478bd9Sstevel@tonic-gate if (chdir(pw->pw_dir) != 0) 13057c478bd9Sstevel@tonic-gate zerror(gettext("Could not chdir to home directory " 13067c478bd9Sstevel@tonic-gate "%s: %s"), pw->pw_dir, strerror(errno)); 13077c478bd9Sstevel@tonic-gate 13087c478bd9Sstevel@tonic-gate (void) snprintf(varmail, sizeof (varmail), "/var/mail/%s", 13097c478bd9Sstevel@tonic-gate pw->pw_name); 13107c478bd9Sstevel@tonic-gate if ((estr = add_env("MAIL", varmail)) == NULL) 13117c478bd9Sstevel@tonic-gate goto malloc_fail; 13127c478bd9Sstevel@tonic-gate new_env[e++] = estr; 1313858a4b99Ssl108498 } else { 1314858a4b99Ssl108498 if ((estr = add_env("HOME", "/")) == NULL) 1315858a4b99Ssl108498 goto malloc_fail; 1316858a4b99Ssl108498 new_env[e++] = estr; 13177c478bd9Sstevel@tonic-gate } 13187c478bd9Sstevel@tonic-gate 13197c478bd9Sstevel@tonic-gate if (pw != NULL && strlen(pw->pw_shell) > 0) { 13207c478bd9Sstevel@tonic-gate if ((estr = add_env("SHELL", pw->pw_shell)) == NULL) 13217c478bd9Sstevel@tonic-gate goto malloc_fail; 13227c478bd9Sstevel@tonic-gate new_env[e++] = estr; 13237c478bd9Sstevel@tonic-gate } else { 13247c478bd9Sstevel@tonic-gate if ((estr = add_env("SHELL", DEFAULTSHELL)) == NULL) 13257c478bd9Sstevel@tonic-gate goto malloc_fail; 13267c478bd9Sstevel@tonic-gate new_env[e++] = estr; 13277c478bd9Sstevel@tonic-gate } 13287c478bd9Sstevel@tonic-gate 13297c478bd9Sstevel@tonic-gate new_env[e++] = NULL; /* add terminating NULL */ 13307c478bd9Sstevel@tonic-gate 13317c478bd9Sstevel@tonic-gate assert(e == size); 13327c478bd9Sstevel@tonic-gate return (new_env); 13337c478bd9Sstevel@tonic-gate 13347c478bd9Sstevel@tonic-gate malloc_fail: 13357c478bd9Sstevel@tonic-gate zperror(gettext("failed to allocate memory for process environment")); 13367c478bd9Sstevel@tonic-gate return (NULL); 13377c478bd9Sstevel@tonic-gate } 13387c478bd9Sstevel@tonic-gate 13397c478bd9Sstevel@tonic-gate static int 13407c478bd9Sstevel@tonic-gate close_func(void *slavefd, int fd) 13417c478bd9Sstevel@tonic-gate { 13427c478bd9Sstevel@tonic-gate if (fd != *(int *)slavefd) 13437c478bd9Sstevel@tonic-gate (void) close(fd); 13447c478bd9Sstevel@tonic-gate return (0); 13457c478bd9Sstevel@tonic-gate } 13467c478bd9Sstevel@tonic-gate 13477c478bd9Sstevel@tonic-gate static void 13487c478bd9Sstevel@tonic-gate set_cmdchar(char *cmdcharstr) 13497c478bd9Sstevel@tonic-gate { 13507c478bd9Sstevel@tonic-gate char c; 13517c478bd9Sstevel@tonic-gate long lc; 13527c478bd9Sstevel@tonic-gate 13537c478bd9Sstevel@tonic-gate if ((c = *cmdcharstr) != '\\') { 13547c478bd9Sstevel@tonic-gate cmdchar = c; 13557c478bd9Sstevel@tonic-gate return; 13567c478bd9Sstevel@tonic-gate } 13577c478bd9Sstevel@tonic-gate 13587c478bd9Sstevel@tonic-gate c = cmdcharstr[1]; 13597c478bd9Sstevel@tonic-gate if (c == '\0' || c == '\\') { 13607c478bd9Sstevel@tonic-gate cmdchar = '\\'; 13617c478bd9Sstevel@tonic-gate return; 13627c478bd9Sstevel@tonic-gate } 13637c478bd9Sstevel@tonic-gate 13647c478bd9Sstevel@tonic-gate if (c < '0' || c > '7') { 13657c478bd9Sstevel@tonic-gate zerror(gettext("Unrecognized escape character option %s"), 13667c478bd9Sstevel@tonic-gate cmdcharstr); 13677c478bd9Sstevel@tonic-gate usage(); 13687c478bd9Sstevel@tonic-gate } 13697c478bd9Sstevel@tonic-gate 13707c478bd9Sstevel@tonic-gate lc = strtol(cmdcharstr + 1, NULL, 8); 13717c478bd9Sstevel@tonic-gate if (lc < 0 || lc > 255) { 13727c478bd9Sstevel@tonic-gate zerror(gettext("Octal escape character '%s' too large"), 13737c478bd9Sstevel@tonic-gate cmdcharstr); 13747c478bd9Sstevel@tonic-gate usage(); 13757c478bd9Sstevel@tonic-gate } 13767c478bd9Sstevel@tonic-gate cmdchar = (char)lc; 13777c478bd9Sstevel@tonic-gate } 13787c478bd9Sstevel@tonic-gate 13797c478bd9Sstevel@tonic-gate static int 13807c478bd9Sstevel@tonic-gate setup_utmpx(char *slavename) 13817c478bd9Sstevel@tonic-gate { 13827c478bd9Sstevel@tonic-gate struct utmpx ut; 13837c478bd9Sstevel@tonic-gate 13847c478bd9Sstevel@tonic-gate bzero(&ut, sizeof (ut)); 13857c478bd9Sstevel@tonic-gate (void) strncpy(ut.ut_user, ".zlogin", sizeof (ut.ut_user)); 13867c478bd9Sstevel@tonic-gate (void) strncpy(ut.ut_line, slavename, sizeof (ut.ut_line)); 13877c478bd9Sstevel@tonic-gate ut.ut_pid = getpid(); 13887c478bd9Sstevel@tonic-gate ut.ut_id[0] = 'z'; 13897c478bd9Sstevel@tonic-gate ut.ut_id[1] = ut.ut_id[2] = ut.ut_id[3] = (char)SC_WILDC; 13907c478bd9Sstevel@tonic-gate ut.ut_type = LOGIN_PROCESS; 13917c478bd9Sstevel@tonic-gate (void) time(&ut.ut_tv.tv_sec); 13927c478bd9Sstevel@tonic-gate 13937c478bd9Sstevel@tonic-gate if (makeutx(&ut) == NULL) { 13947c478bd9Sstevel@tonic-gate zerror(gettext("makeutx failed")); 13957c478bd9Sstevel@tonic-gate return (-1); 13967c478bd9Sstevel@tonic-gate } 13977c478bd9Sstevel@tonic-gate return (0); 13987c478bd9Sstevel@tonic-gate } 13997c478bd9Sstevel@tonic-gate 14007c478bd9Sstevel@tonic-gate static void 14017c478bd9Sstevel@tonic-gate release_lock_file(int lockfd) 14027c478bd9Sstevel@tonic-gate { 14037c478bd9Sstevel@tonic-gate (void) close(lockfd); 14047c478bd9Sstevel@tonic-gate } 14057c478bd9Sstevel@tonic-gate 14067c478bd9Sstevel@tonic-gate static int 14077c478bd9Sstevel@tonic-gate grab_lock_file(const char *zone_name, int *lockfd) 14087c478bd9Sstevel@tonic-gate { 14097c478bd9Sstevel@tonic-gate char pathbuf[PATH_MAX]; 14107c478bd9Sstevel@tonic-gate struct flock flock; 14117c478bd9Sstevel@tonic-gate 14127c478bd9Sstevel@tonic-gate if (mkdir(ZONES_TMPDIR, S_IRWXU) < 0 && errno != EEXIST) { 14137c478bd9Sstevel@tonic-gate zerror(gettext("could not mkdir %s: %s"), ZONES_TMPDIR, 14147c478bd9Sstevel@tonic-gate strerror(errno)); 14157c478bd9Sstevel@tonic-gate return (-1); 14167c478bd9Sstevel@tonic-gate } 14177c478bd9Sstevel@tonic-gate (void) chmod(ZONES_TMPDIR, S_IRWXU); 14187c478bd9Sstevel@tonic-gate (void) snprintf(pathbuf, sizeof (pathbuf), "%s/%s.zoneadm.lock", 14197c478bd9Sstevel@tonic-gate ZONES_TMPDIR, zone_name); 14207c478bd9Sstevel@tonic-gate 14217c478bd9Sstevel@tonic-gate if ((*lockfd = open(pathbuf, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR)) < 0) { 14227c478bd9Sstevel@tonic-gate zerror(gettext("could not open %s: %s"), pathbuf, 14237c478bd9Sstevel@tonic-gate strerror(errno)); 14247c478bd9Sstevel@tonic-gate return (-1); 14257c478bd9Sstevel@tonic-gate } 14267c478bd9Sstevel@tonic-gate /* 14277c478bd9Sstevel@tonic-gate * Lock the file to synchronize with other zoneadmds 14287c478bd9Sstevel@tonic-gate */ 14297c478bd9Sstevel@tonic-gate flock.l_type = F_WRLCK; 14307c478bd9Sstevel@tonic-gate flock.l_whence = SEEK_SET; 14317c478bd9Sstevel@tonic-gate flock.l_start = (off_t)0; 14327c478bd9Sstevel@tonic-gate flock.l_len = (off_t)0; 14337c478bd9Sstevel@tonic-gate if (fcntl(*lockfd, F_SETLKW, &flock) < 0) { 14347c478bd9Sstevel@tonic-gate zerror(gettext("unable to lock %s: %s"), pathbuf, 14357c478bd9Sstevel@tonic-gate strerror(errno)); 14367c478bd9Sstevel@tonic-gate release_lock_file(*lockfd); 14377c478bd9Sstevel@tonic-gate return (-1); 14387c478bd9Sstevel@tonic-gate } 14397c478bd9Sstevel@tonic-gate return (Z_OK); 14407c478bd9Sstevel@tonic-gate } 14417c478bd9Sstevel@tonic-gate 14427c478bd9Sstevel@tonic-gate static int 14437c478bd9Sstevel@tonic-gate start_zoneadmd(const char *zone_name) 14447c478bd9Sstevel@tonic-gate { 14457c478bd9Sstevel@tonic-gate pid_t retval; 14467c478bd9Sstevel@tonic-gate int pstatus = 0, error = -1, lockfd, doorfd; 14477c478bd9Sstevel@tonic-gate struct door_info info; 14487c478bd9Sstevel@tonic-gate char doorpath[MAXPATHLEN]; 14497c478bd9Sstevel@tonic-gate 14507c478bd9Sstevel@tonic-gate (void) snprintf(doorpath, sizeof (doorpath), ZONE_DOOR_PATH, zone_name); 14517c478bd9Sstevel@tonic-gate 14527c478bd9Sstevel@tonic-gate if (grab_lock_file(zone_name, &lockfd) != Z_OK) 14537c478bd9Sstevel@tonic-gate return (-1); 14547c478bd9Sstevel@tonic-gate /* 14557c478bd9Sstevel@tonic-gate * We must do the door check with the lock held. Otherwise, we 14567c478bd9Sstevel@tonic-gate * might race against another zoneadm/zlogin process and wind 14577c478bd9Sstevel@tonic-gate * up with two processes trying to start zoneadmd at the same 14587c478bd9Sstevel@tonic-gate * time. zoneadmd will detect this, and fail, but we prefer this 14597c478bd9Sstevel@tonic-gate * to be as seamless as is practical, from a user perspective. 14607c478bd9Sstevel@tonic-gate */ 14617c478bd9Sstevel@tonic-gate if ((doorfd = open(doorpath, O_RDONLY)) < 0) { 14627c478bd9Sstevel@tonic-gate if (errno != ENOENT) { 14637c478bd9Sstevel@tonic-gate zerror("failed to open %s: %s", doorpath, 14647c478bd9Sstevel@tonic-gate strerror(errno)); 14657c478bd9Sstevel@tonic-gate goto out; 14667c478bd9Sstevel@tonic-gate } 14677c478bd9Sstevel@tonic-gate } else { 14687c478bd9Sstevel@tonic-gate /* 14697c478bd9Sstevel@tonic-gate * Seems to be working ok. 14707c478bd9Sstevel@tonic-gate */ 14717c478bd9Sstevel@tonic-gate if (door_info(doorfd, &info) == 0 && 14727c478bd9Sstevel@tonic-gate ((info.di_attributes & DOOR_REVOKED) == 0)) { 14737c478bd9Sstevel@tonic-gate error = 0; 14747c478bd9Sstevel@tonic-gate goto out; 14757c478bd9Sstevel@tonic-gate } 14767c478bd9Sstevel@tonic-gate } 14777c478bd9Sstevel@tonic-gate 14787c478bd9Sstevel@tonic-gate if ((child_pid = fork()) == -1) { 14797c478bd9Sstevel@tonic-gate zperror(gettext("could not fork")); 14807c478bd9Sstevel@tonic-gate goto out; 14817c478bd9Sstevel@tonic-gate } else if (child_pid == 0) { 14827c478bd9Sstevel@tonic-gate /* child process */ 14837c478bd9Sstevel@tonic-gate (void) execl("/usr/lib/zones/zoneadmd", "zoneadmd", "-z", 14847c478bd9Sstevel@tonic-gate zone_name, NULL); 14857c478bd9Sstevel@tonic-gate zperror(gettext("could not exec zoneadmd")); 14867c478bd9Sstevel@tonic-gate _exit(1); 14877c478bd9Sstevel@tonic-gate } 14887c478bd9Sstevel@tonic-gate 14897c478bd9Sstevel@tonic-gate /* parent process */ 14907c478bd9Sstevel@tonic-gate do { 14917c478bd9Sstevel@tonic-gate retval = waitpid(child_pid, &pstatus, 0); 14927c478bd9Sstevel@tonic-gate } while (retval != child_pid); 14937c478bd9Sstevel@tonic-gate if (WIFSIGNALED(pstatus) || 14947c478bd9Sstevel@tonic-gate (WIFEXITED(pstatus) && WEXITSTATUS(pstatus) != 0)) { 14957c478bd9Sstevel@tonic-gate zerror(gettext("could not start %s"), "zoneadmd"); 14967c478bd9Sstevel@tonic-gate goto out; 14977c478bd9Sstevel@tonic-gate } 14987c478bd9Sstevel@tonic-gate error = 0; 14997c478bd9Sstevel@tonic-gate out: 15007c478bd9Sstevel@tonic-gate release_lock_file(lockfd); 15017c478bd9Sstevel@tonic-gate (void) close(doorfd); 15027c478bd9Sstevel@tonic-gate return (error); 15037c478bd9Sstevel@tonic-gate } 15047c478bd9Sstevel@tonic-gate 15057c478bd9Sstevel@tonic-gate static int 15067c478bd9Sstevel@tonic-gate init_template(void) 15077c478bd9Sstevel@tonic-gate { 15087c478bd9Sstevel@tonic-gate int fd; 15097c478bd9Sstevel@tonic-gate int err = 0; 15107c478bd9Sstevel@tonic-gate 15117c478bd9Sstevel@tonic-gate fd = open64(CTFS_ROOT "/process/template", O_RDWR); 15127c478bd9Sstevel@tonic-gate if (fd == -1) 15137c478bd9Sstevel@tonic-gate return (-1); 15147c478bd9Sstevel@tonic-gate 15157c478bd9Sstevel@tonic-gate /* 15167c478bd9Sstevel@tonic-gate * zlogin doesn't do anything with the contract. 15177c478bd9Sstevel@tonic-gate * Deliver no events, don't inherit, and allow it to be orphaned. 15187c478bd9Sstevel@tonic-gate */ 15197c478bd9Sstevel@tonic-gate err |= ct_tmpl_set_critical(fd, 0); 15207c478bd9Sstevel@tonic-gate err |= ct_tmpl_set_informative(fd, 0); 15217c478bd9Sstevel@tonic-gate err |= ct_pr_tmpl_set_fatal(fd, CT_PR_EV_HWERR); 15227c478bd9Sstevel@tonic-gate err |= ct_pr_tmpl_set_param(fd, CT_PR_PGRPONLY | CT_PR_REGENT); 15237c478bd9Sstevel@tonic-gate if (err || ct_tmpl_activate(fd)) { 15247c478bd9Sstevel@tonic-gate (void) close(fd); 15257c478bd9Sstevel@tonic-gate return (-1); 15267c478bd9Sstevel@tonic-gate } 15277c478bd9Sstevel@tonic-gate 15287c478bd9Sstevel@tonic-gate return (fd); 15297c478bd9Sstevel@tonic-gate } 15307c478bd9Sstevel@tonic-gate 15317c478bd9Sstevel@tonic-gate static int 1532858a4b99Ssl108498 noninteractive_login(char *zonename, const char *user_cmd, zoneid_t zoneid, 15337c478bd9Sstevel@tonic-gate char **new_args, char **new_env) 15347c478bd9Sstevel@tonic-gate { 15357c478bd9Sstevel@tonic-gate pid_t retval; 1536ff17c8bfSgjelinek int stdin_pipe[2], stdout_pipe[2], stderr_pipe[2], dead_child_pipe[2]; 15377c478bd9Sstevel@tonic-gate int child_status; 15387c478bd9Sstevel@tonic-gate int tmpl_fd; 15397c478bd9Sstevel@tonic-gate sigset_t block_cld; 15407c478bd9Sstevel@tonic-gate 15417c478bd9Sstevel@tonic-gate if ((tmpl_fd = init_template()) == -1) { 15427c478bd9Sstevel@tonic-gate reset_tty(); 15437c478bd9Sstevel@tonic-gate zperror(gettext("could not create contract")); 15447c478bd9Sstevel@tonic-gate return (1); 15457c478bd9Sstevel@tonic-gate } 15467c478bd9Sstevel@tonic-gate 15477c478bd9Sstevel@tonic-gate if (pipe(stdin_pipe) != 0) { 15487c478bd9Sstevel@tonic-gate zperror(gettext("could not create STDIN pipe")); 15497c478bd9Sstevel@tonic-gate return (1); 15507c478bd9Sstevel@tonic-gate } 15517c478bd9Sstevel@tonic-gate /* 15527c478bd9Sstevel@tonic-gate * When the user types ^D, we get a zero length message on STDIN. 15537c478bd9Sstevel@tonic-gate * We need to echo that down the pipe to send it to the other side; 15547c478bd9Sstevel@tonic-gate * but by default, pipes don't propagate zero-length messages. We 15557c478bd9Sstevel@tonic-gate * toggle that behavior off using I_SWROPT. See streamio(7i). 15567c478bd9Sstevel@tonic-gate */ 15577c478bd9Sstevel@tonic-gate if (ioctl(stdin_pipe[0], I_SWROPT, SNDZERO) != 0) { 15587c478bd9Sstevel@tonic-gate zperror(gettext("could not configure STDIN pipe")); 15597c478bd9Sstevel@tonic-gate return (1); 15607c478bd9Sstevel@tonic-gate 15617c478bd9Sstevel@tonic-gate } 15627c478bd9Sstevel@tonic-gate if (pipe(stdout_pipe) != 0) { 15637c478bd9Sstevel@tonic-gate zperror(gettext("could not create STDOUT pipe")); 15647c478bd9Sstevel@tonic-gate return (1); 15657c478bd9Sstevel@tonic-gate } 15667c478bd9Sstevel@tonic-gate if (pipe(stderr_pipe) != 0) { 15677c478bd9Sstevel@tonic-gate zperror(gettext("could not create STDERR pipe")); 15687c478bd9Sstevel@tonic-gate return (1); 15697c478bd9Sstevel@tonic-gate } 15707c478bd9Sstevel@tonic-gate 1571ff17c8bfSgjelinek if (pipe(dead_child_pipe) != 0) { 1572ff17c8bfSgjelinek zperror(gettext("could not create signalling pipe")); 1573ff17c8bfSgjelinek return (1); 1574ff17c8bfSgjelinek } 1575ff17c8bfSgjelinek close_on_sig = dead_child_pipe[0]; 1576ff17c8bfSgjelinek 15777c478bd9Sstevel@tonic-gate /* 15787c478bd9Sstevel@tonic-gate * If any of the pipe FD's winds up being less than STDERR, then we 15797c478bd9Sstevel@tonic-gate * have a mess on our hands-- and we are lacking some of the I/O 15807c478bd9Sstevel@tonic-gate * streams we would expect anyway. So we bail. 15817c478bd9Sstevel@tonic-gate */ 15827c478bd9Sstevel@tonic-gate if (stdin_pipe[0] <= STDERR_FILENO || 15837c478bd9Sstevel@tonic-gate stdin_pipe[1] <= STDERR_FILENO || 15847c478bd9Sstevel@tonic-gate stdout_pipe[0] <= STDERR_FILENO || 15857c478bd9Sstevel@tonic-gate stdout_pipe[1] <= STDERR_FILENO || 15867c478bd9Sstevel@tonic-gate stderr_pipe[0] <= STDERR_FILENO || 1587ff17c8bfSgjelinek stderr_pipe[1] <= STDERR_FILENO || 1588ff17c8bfSgjelinek dead_child_pipe[0] <= STDERR_FILENO || 1589ff17c8bfSgjelinek dead_child_pipe[1] <= STDERR_FILENO) { 15907c478bd9Sstevel@tonic-gate zperror(gettext("process lacks valid STDIN, STDOUT, STDERR")); 15917c478bd9Sstevel@tonic-gate return (1); 15927c478bd9Sstevel@tonic-gate } 15937c478bd9Sstevel@tonic-gate 15947c478bd9Sstevel@tonic-gate if (prefork_dropprivs() != 0) { 15957c478bd9Sstevel@tonic-gate zperror(gettext("could not allocate privilege set")); 15967c478bd9Sstevel@tonic-gate return (1); 15977c478bd9Sstevel@tonic-gate } 15987c478bd9Sstevel@tonic-gate 15997c478bd9Sstevel@tonic-gate (void) sigset(SIGCLD, sigcld); 16007c478bd9Sstevel@tonic-gate (void) sigemptyset(&block_cld); 16017c478bd9Sstevel@tonic-gate (void) sigaddset(&block_cld, SIGCLD); 16027c478bd9Sstevel@tonic-gate (void) sigprocmask(SIG_BLOCK, &block_cld, NULL); 16037c478bd9Sstevel@tonic-gate 16047c478bd9Sstevel@tonic-gate if ((child_pid = fork()) == -1) { 16057c478bd9Sstevel@tonic-gate (void) ct_tmpl_clear(tmpl_fd); 16067c478bd9Sstevel@tonic-gate (void) close(tmpl_fd); 16077c478bd9Sstevel@tonic-gate zperror(gettext("could not fork")); 16087c478bd9Sstevel@tonic-gate return (1); 16097c478bd9Sstevel@tonic-gate } else if (child_pid == 0) { /* child process */ 16107c478bd9Sstevel@tonic-gate (void) ct_tmpl_clear(tmpl_fd); 16117c478bd9Sstevel@tonic-gate 16127c478bd9Sstevel@tonic-gate /* 16137c478bd9Sstevel@tonic-gate * Do a dance to get the pipes hooked up as FD's 0, 1 and 2. 16147c478bd9Sstevel@tonic-gate */ 16157c478bd9Sstevel@tonic-gate (void) close(STDIN_FILENO); 16167c478bd9Sstevel@tonic-gate (void) close(STDOUT_FILENO); 16177c478bd9Sstevel@tonic-gate (void) close(STDERR_FILENO); 16187c478bd9Sstevel@tonic-gate (void) dup2(stdin_pipe[1], STDIN_FILENO); 16197c478bd9Sstevel@tonic-gate (void) dup2(stdout_pipe[1], STDOUT_FILENO); 16207c478bd9Sstevel@tonic-gate (void) dup2(stderr_pipe[1], STDERR_FILENO); 16217c478bd9Sstevel@tonic-gate (void) closefrom(STDERR_FILENO + 1); 16227c478bd9Sstevel@tonic-gate 16237c478bd9Sstevel@tonic-gate (void) sigset(SIGCLD, SIG_DFL); 16247c478bd9Sstevel@tonic-gate (void) sigprocmask(SIG_UNBLOCK, &block_cld, NULL); 16257c478bd9Sstevel@tonic-gate /* 16267c478bd9Sstevel@tonic-gate * In case any of stdin, stdout or stderr are streams, 16277c478bd9Sstevel@tonic-gate * anchor them to prevent malicious I_POPs. 16287c478bd9Sstevel@tonic-gate */ 16297c478bd9Sstevel@tonic-gate (void) ioctl(STDIN_FILENO, I_ANCHOR); 16307c478bd9Sstevel@tonic-gate (void) ioctl(STDOUT_FILENO, I_ANCHOR); 16317c478bd9Sstevel@tonic-gate (void) ioctl(STDERR_FILENO, I_ANCHOR); 16327c478bd9Sstevel@tonic-gate 16337c478bd9Sstevel@tonic-gate if (zone_enter(zoneid) == -1) { 16347c478bd9Sstevel@tonic-gate zerror(gettext("could not enter zone %s: %s"), 16357c478bd9Sstevel@tonic-gate zonename, strerror(errno)); 16367c478bd9Sstevel@tonic-gate _exit(1); 16377c478bd9Sstevel@tonic-gate } 16387c478bd9Sstevel@tonic-gate 1639858a4b99Ssl108498 /* 1640858a4b99Ssl108498 * For non-native zones, tell libc where it can find locale 1641858a4b99Ssl108498 * specific getttext() messages. 1642858a4b99Ssl108498 */ 16431100f00dSgjelinek if (access("/.SUNWnative/usr/lib/locale", R_OK) == 0) 16441100f00dSgjelinek (void) bindtextdomain(TEXT_DOMAIN, 16451100f00dSgjelinek "/.SUNWnative/usr/lib/locale"); 16461100f00dSgjelinek else if (access("/native/usr/lib/locale", R_OK) == 0) 1647858a4b99Ssl108498 (void) bindtextdomain(TEXT_DOMAIN, 1648858a4b99Ssl108498 "/native/usr/lib/locale"); 1649858a4b99Ssl108498 16507c478bd9Sstevel@tonic-gate if (!failsafe) 1651858a4b99Ssl108498 new_env = prep_env_noninteractive(user_cmd, new_env); 16527c478bd9Sstevel@tonic-gate 16537c478bd9Sstevel@tonic-gate if (new_env == NULL) { 16547c478bd9Sstevel@tonic-gate _exit(1); 16557c478bd9Sstevel@tonic-gate } 16567c478bd9Sstevel@tonic-gate 16577c478bd9Sstevel@tonic-gate /* 16587c478bd9Sstevel@tonic-gate * Move into a new process group; the zone_enter will have 16597c478bd9Sstevel@tonic-gate * placed us into zsched's session, and we want to be in 16607c478bd9Sstevel@tonic-gate * a unique process group. 16617c478bd9Sstevel@tonic-gate */ 16627c478bd9Sstevel@tonic-gate (void) setpgid(getpid(), getpid()); 16637c478bd9Sstevel@tonic-gate 1664cb8a054bSGlenn Faden /* 1665cb8a054bSGlenn Faden * The child needs to run as root to 1666cb8a054bSGlenn Faden * execute the su program. 1667cb8a054bSGlenn Faden */ 1668cb8a054bSGlenn Faden if (setuid(0) == -1) { 1669cb8a054bSGlenn Faden zperror(gettext("insufficient privilege")); 1670cb8a054bSGlenn Faden return (1); 1671cb8a054bSGlenn Faden } 1672cb8a054bSGlenn Faden 16737c478bd9Sstevel@tonic-gate (void) execve(new_args[0], new_args, new_env); 16747c478bd9Sstevel@tonic-gate zperror(gettext("exec failure")); 16757c478bd9Sstevel@tonic-gate _exit(1); 16767c478bd9Sstevel@tonic-gate } 16777c478bd9Sstevel@tonic-gate /* parent */ 1678ff17c8bfSgjelinek 1679ff17c8bfSgjelinek /* close pipe sides written by child */ 1680ff17c8bfSgjelinek (void) close(stdout_pipe[1]); 1681ff17c8bfSgjelinek (void) close(stderr_pipe[1]); 1682ff17c8bfSgjelinek 16837c478bd9Sstevel@tonic-gate (void) sigset(SIGINT, sig_forward); 16847c478bd9Sstevel@tonic-gate 16857c478bd9Sstevel@tonic-gate postfork_dropprivs(); 16867c478bd9Sstevel@tonic-gate 16877c478bd9Sstevel@tonic-gate (void) ct_tmpl_clear(tmpl_fd); 16887c478bd9Sstevel@tonic-gate (void) close(tmpl_fd); 16897c478bd9Sstevel@tonic-gate 16907c478bd9Sstevel@tonic-gate (void) sigprocmask(SIG_UNBLOCK, &block_cld, NULL); 1691b52a7fd7Sgjelinek doio(stdin_pipe[0], stdin_pipe[1], stdout_pipe[0], stderr_pipe[0], 1692ff17c8bfSgjelinek dead_child_pipe[1], B_TRUE); 16937c478bd9Sstevel@tonic-gate do { 16947c478bd9Sstevel@tonic-gate retval = waitpid(child_pid, &child_status, 0); 16957c478bd9Sstevel@tonic-gate if (retval == -1) { 16967c478bd9Sstevel@tonic-gate child_status = 0; 16977c478bd9Sstevel@tonic-gate } 16987c478bd9Sstevel@tonic-gate } while (retval != child_pid && errno != ECHILD); 16997c478bd9Sstevel@tonic-gate 17007c478bd9Sstevel@tonic-gate return (WEXITSTATUS(child_status)); 17017c478bd9Sstevel@tonic-gate } 17027c478bd9Sstevel@tonic-gate 1703cb8a054bSGlenn Faden static char * 1704cb8a054bSGlenn Faden get_username() 1705cb8a054bSGlenn Faden { 1706cb8a054bSGlenn Faden uid_t uid; 1707cb8a054bSGlenn Faden struct passwd *nptr; 1708cb8a054bSGlenn Faden 1709cb8a054bSGlenn Faden /* 1710cb8a054bSGlenn Faden * Authorizations are checked to restrict access based on the 1711cb8a054bSGlenn Faden * requested operation and zone name, It is assumed that the 1712cb8a054bSGlenn Faden * program is running with all privileges, but that the real 1713cb8a054bSGlenn Faden * user ID is that of the user or role on whose behalf we are 1714cb8a054bSGlenn Faden * operating. So we start by getting the username that will be 1715cb8a054bSGlenn Faden * used for subsequent authorization checks. 1716cb8a054bSGlenn Faden */ 1717cb8a054bSGlenn Faden 1718cb8a054bSGlenn Faden uid = getuid(); 1719cb8a054bSGlenn Faden if ((nptr = getpwuid(uid)) == NULL) { 1720cb8a054bSGlenn Faden zerror(gettext("could not get user name.")); 1721cb8a054bSGlenn Faden _exit(1); 1722cb8a054bSGlenn Faden } 1723cb8a054bSGlenn Faden return (nptr->pw_name); 1724cb8a054bSGlenn Faden } 1725cb8a054bSGlenn Faden 17267c478bd9Sstevel@tonic-gate int 17277c478bd9Sstevel@tonic-gate main(int argc, char **argv) 17287c478bd9Sstevel@tonic-gate { 17297c478bd9Sstevel@tonic-gate int arg, console = 0; 17307c478bd9Sstevel@tonic-gate zoneid_t zoneid; 17317c478bd9Sstevel@tonic-gate zone_state_t st; 17327c478bd9Sstevel@tonic-gate char *login = "root"; 17337c478bd9Sstevel@tonic-gate int lflag = 0; 1734279721bfSGary Mills int nflag = 0; 17357c478bd9Sstevel@tonic-gate char *zonename = NULL; 17367c478bd9Sstevel@tonic-gate char **proc_args = NULL; 17377c478bd9Sstevel@tonic-gate char **new_args, **new_env; 17387c478bd9Sstevel@tonic-gate sigset_t block_cld; 1739facf4a8dSllai1 char devroot[MAXPATHLEN]; 17407c478bd9Sstevel@tonic-gate char *slavename, slaveshortname[MAXPATHLEN]; 17417c478bd9Sstevel@tonic-gate priv_set_t *privset; 17427c478bd9Sstevel@tonic-gate int tmpl_fd; 17439acbbeafSnn35248 char zonebrand[MAXNAMELEN]; 1744e5816e35SEdward Pilatowicz char default_brand[MAXNAMELEN]; 1745108322fbScarlsonj struct stat sb; 1746108322fbScarlsonj char kernzone[ZONENAME_MAX]; 1747123807fbSedp brand_handle_t bh; 1748858a4b99Ssl108498 char user_cmd[MAXPATHLEN]; 1749cb8a054bSGlenn Faden char authname[MAXAUTHS]; 17507c478bd9Sstevel@tonic-gate 17517c478bd9Sstevel@tonic-gate (void) setlocale(LC_ALL, ""); 17527c478bd9Sstevel@tonic-gate (void) textdomain(TEXT_DOMAIN); 17537c478bd9Sstevel@tonic-gate 17547c478bd9Sstevel@tonic-gate (void) getpname(argv[0]); 1755cb8a054bSGlenn Faden username = get_username(); 17567c478bd9Sstevel@tonic-gate 1757*741343adSAlexander Eremin while ((arg = getopt(argc, argv, "dnECR:Se:l:Q")) != EOF) { 17587c478bd9Sstevel@tonic-gate switch (arg) { 17597c478bd9Sstevel@tonic-gate case 'C': 17607c478bd9Sstevel@tonic-gate console = 1; 17617c478bd9Sstevel@tonic-gate break; 17627c478bd9Sstevel@tonic-gate case 'E': 17637c478bd9Sstevel@tonic-gate nocmdchar = 1; 17647c478bd9Sstevel@tonic-gate break; 1765108322fbScarlsonj case 'R': /* undocumented */ 1766108322fbScarlsonj if (*optarg != '/') { 1767108322fbScarlsonj zerror(gettext("root path must be absolute.")); 1768108322fbScarlsonj exit(2); 1769108322fbScarlsonj } 1770108322fbScarlsonj if (stat(optarg, &sb) == -1 || !S_ISDIR(sb.st_mode)) { 1771108322fbScarlsonj zerror( 1772108322fbScarlsonj gettext("root path must be a directory.")); 1773108322fbScarlsonj exit(2); 1774108322fbScarlsonj } 1775108322fbScarlsonj zonecfg_set_root(optarg); 1776108322fbScarlsonj break; 1777c2589d13SGarrett D'Amore case 'Q': 1778c2589d13SGarrett D'Amore quiet = 1; 1779c2589d13SGarrett D'Amore break; 17807c478bd9Sstevel@tonic-gate case 'S': 17817c478bd9Sstevel@tonic-gate failsafe = 1; 17827c478bd9Sstevel@tonic-gate break; 1783*741343adSAlexander Eremin case 'd': 1784*741343adSAlexander Eremin disconnect = 1; 1785*741343adSAlexander Eremin break; 17867c478bd9Sstevel@tonic-gate case 'e': 17877c478bd9Sstevel@tonic-gate set_cmdchar(optarg); 17887c478bd9Sstevel@tonic-gate break; 17897c478bd9Sstevel@tonic-gate case 'l': 17907c478bd9Sstevel@tonic-gate login = optarg; 17917c478bd9Sstevel@tonic-gate lflag = 1; 17927c478bd9Sstevel@tonic-gate break; 1793279721bfSGary Mills case 'n': 1794279721bfSGary Mills nflag = 1; 1795279721bfSGary Mills break; 17967c478bd9Sstevel@tonic-gate default: 17977c478bd9Sstevel@tonic-gate usage(); 17987c478bd9Sstevel@tonic-gate } 17997c478bd9Sstevel@tonic-gate } 18007c478bd9Sstevel@tonic-gate 1801279721bfSGary Mills if (console != 0) { 1802279721bfSGary Mills 1803279721bfSGary Mills if (lflag != 0) { 1804279721bfSGary Mills zerror(gettext( 1805279721bfSGary Mills "-l may not be specified for console login")); 18067c478bd9Sstevel@tonic-gate usage(); 18077c478bd9Sstevel@tonic-gate } 18087c478bd9Sstevel@tonic-gate 1809279721bfSGary Mills if (nflag != 0) { 1810279721bfSGary Mills zerror(gettext( 1811279721bfSGary Mills "-n may not be specified for console login")); 18127c478bd9Sstevel@tonic-gate usage(); 18137c478bd9Sstevel@tonic-gate } 18147c478bd9Sstevel@tonic-gate 1815279721bfSGary Mills if (failsafe != 0) { 1816279721bfSGary Mills zerror(gettext( 1817279721bfSGary Mills "-S may not be specified for console login")); 1818279721bfSGary Mills usage(); 1819279721bfSGary Mills } 1820279721bfSGary Mills 1821279721bfSGary Mills if (zonecfg_in_alt_root()) { 1822279721bfSGary Mills zerror(gettext( 1823279721bfSGary Mills "-R may not be specified for console login")); 1824108322fbScarlsonj exit(2); 1825108322fbScarlsonj } 1826108322fbScarlsonj 1827279721bfSGary Mills } 1828279721bfSGary Mills 18297c478bd9Sstevel@tonic-gate if (failsafe != 0 && lflag != 0) { 18307c478bd9Sstevel@tonic-gate zerror(gettext("-l may not be specified for failsafe login")); 18317c478bd9Sstevel@tonic-gate usage(); 18327c478bd9Sstevel@tonic-gate } 18337c478bd9Sstevel@tonic-gate 1834*741343adSAlexander Eremin if (!console && disconnect != 0) { 1835*741343adSAlexander Eremin zerror(gettext( 1836*741343adSAlexander Eremin "-d may only be specified with console login")); 1837*741343adSAlexander Eremin usage(); 1838*741343adSAlexander Eremin } 1839*741343adSAlexander Eremin 18407c478bd9Sstevel@tonic-gate if (optind == (argc - 1)) { 18417c478bd9Sstevel@tonic-gate /* 18427c478bd9Sstevel@tonic-gate * zone name, no process name; this should be an interactive 18437c478bd9Sstevel@tonic-gate * as long as STDIN is really a tty. 18447c478bd9Sstevel@tonic-gate */ 1845279721bfSGary Mills if (nflag != 0) { 1846279721bfSGary Mills zerror(gettext( 1847279721bfSGary Mills "-n may not be specified for interactive login")); 1848279721bfSGary Mills usage(); 1849279721bfSGary Mills } 18507c478bd9Sstevel@tonic-gate if (isatty(STDIN_FILENO)) 18517c478bd9Sstevel@tonic-gate interactive = 1; 18527c478bd9Sstevel@tonic-gate zonename = argv[optind]; 18537c478bd9Sstevel@tonic-gate } else if (optind < (argc - 1)) { 18547c478bd9Sstevel@tonic-gate if (console) { 18557c478bd9Sstevel@tonic-gate zerror(gettext("Commands may not be specified for " 18567c478bd9Sstevel@tonic-gate "console login.")); 18577c478bd9Sstevel@tonic-gate usage(); 18587c478bd9Sstevel@tonic-gate } 18597c478bd9Sstevel@tonic-gate /* zone name and process name, and possibly some args */ 18607c478bd9Sstevel@tonic-gate zonename = argv[optind]; 18617c478bd9Sstevel@tonic-gate proc_args = &argv[optind + 1]; 18627c478bd9Sstevel@tonic-gate interactive = 0; 18637c478bd9Sstevel@tonic-gate } else { 18647c478bd9Sstevel@tonic-gate usage(); 18657c478bd9Sstevel@tonic-gate } 18667c478bd9Sstevel@tonic-gate 18677c478bd9Sstevel@tonic-gate if (getzoneid() != GLOBAL_ZONEID) { 18687c478bd9Sstevel@tonic-gate zerror(gettext("'%s' may only be used from the global zone"), 18697c478bd9Sstevel@tonic-gate pname); 18707c478bd9Sstevel@tonic-gate return (1); 18717c478bd9Sstevel@tonic-gate } 18727c478bd9Sstevel@tonic-gate 18737c478bd9Sstevel@tonic-gate if (strcmp(zonename, GLOBAL_ZONENAME) == 0) { 18747c478bd9Sstevel@tonic-gate zerror(gettext("'%s' not applicable to the global zone"), 18757c478bd9Sstevel@tonic-gate pname); 18767c478bd9Sstevel@tonic-gate return (1); 18777c478bd9Sstevel@tonic-gate } 18787c478bd9Sstevel@tonic-gate 18797c478bd9Sstevel@tonic-gate if (zone_get_state(zonename, &st) != Z_OK) { 18807c478bd9Sstevel@tonic-gate zerror(gettext("zone '%s' unknown"), zonename); 18817c478bd9Sstevel@tonic-gate return (1); 18827c478bd9Sstevel@tonic-gate } 18837c478bd9Sstevel@tonic-gate 18847c478bd9Sstevel@tonic-gate if (st < ZONE_STATE_INSTALLED) { 18857c478bd9Sstevel@tonic-gate zerror(gettext("cannot login to a zone which is '%s'"), 18867c478bd9Sstevel@tonic-gate zone_state_str(st)); 18877c478bd9Sstevel@tonic-gate return (1); 18887c478bd9Sstevel@tonic-gate } 18897c478bd9Sstevel@tonic-gate 18907c478bd9Sstevel@tonic-gate /* 18917c478bd9Sstevel@tonic-gate * In both console and non-console cases, we require all privs. 18927c478bd9Sstevel@tonic-gate * In the console case, because we may need to startup zoneadmd. 18937c478bd9Sstevel@tonic-gate * In the non-console case in order to do zone_enter(2), zonept() 18947c478bd9Sstevel@tonic-gate * and other tasks. 18957c478bd9Sstevel@tonic-gate */ 1896cb8a054bSGlenn Faden 18977c478bd9Sstevel@tonic-gate if ((privset = priv_allocset()) == NULL) { 18987c478bd9Sstevel@tonic-gate zperror(gettext("priv_allocset failed")); 18997c478bd9Sstevel@tonic-gate return (1); 19007c478bd9Sstevel@tonic-gate } 19017c478bd9Sstevel@tonic-gate 19027c478bd9Sstevel@tonic-gate if (getppriv(PRIV_EFFECTIVE, privset) != 0) { 19037c478bd9Sstevel@tonic-gate zperror(gettext("getppriv failed")); 19047c478bd9Sstevel@tonic-gate priv_freeset(privset); 19057c478bd9Sstevel@tonic-gate return (1); 19067c478bd9Sstevel@tonic-gate } 19077c478bd9Sstevel@tonic-gate 19087c478bd9Sstevel@tonic-gate if (priv_isfullset(privset) == B_FALSE) { 19097c478bd9Sstevel@tonic-gate zerror(gettext("You lack sufficient privilege to run " 19107c478bd9Sstevel@tonic-gate "this command (all privs required)")); 19117c478bd9Sstevel@tonic-gate priv_freeset(privset); 19127c478bd9Sstevel@tonic-gate return (1); 19137c478bd9Sstevel@tonic-gate } 19147c478bd9Sstevel@tonic-gate priv_freeset(privset); 19157c478bd9Sstevel@tonic-gate 19167c478bd9Sstevel@tonic-gate /* 1917cb8a054bSGlenn Faden * Check if user is authorized for requested usage of the zone 1918cb8a054bSGlenn Faden */ 1919cb8a054bSGlenn Faden 1920cb8a054bSGlenn Faden (void) snprintf(authname, MAXAUTHS, "%s%s%s", 1921cb8a054bSGlenn Faden ZONE_MANAGE_AUTH, KV_OBJECT, zonename); 1922cb8a054bSGlenn Faden if (chkauthattr(authname, username) == 0) { 1923cb8a054bSGlenn Faden if (console) { 1924cb8a054bSGlenn Faden zerror(gettext("%s is not authorized for console " 1925cb8a054bSGlenn Faden "access to %s zone."), 1926cb8a054bSGlenn Faden username, zonename); 1927cb8a054bSGlenn Faden return (1); 1928cb8a054bSGlenn Faden } else { 1929cb8a054bSGlenn Faden (void) snprintf(authname, MAXAUTHS, "%s%s%s", 1930cb8a054bSGlenn Faden ZONE_LOGIN_AUTH, KV_OBJECT, zonename); 1931cb8a054bSGlenn Faden if (failsafe || !interactive) { 1932cb8a054bSGlenn Faden zerror(gettext("%s is not authorized for " 1933cb8a054bSGlenn Faden "failsafe or non-interactive login " 1934cb8a054bSGlenn Faden "to %s zone."), username, zonename); 1935cb8a054bSGlenn Faden return (1); 1936cb8a054bSGlenn Faden } else if (chkauthattr(authname, username) == 0) { 1937cb8a054bSGlenn Faden zerror(gettext("%s is not authorized " 1938cb8a054bSGlenn Faden " to login to %s zone."), 1939cb8a054bSGlenn Faden username, zonename); 1940cb8a054bSGlenn Faden return (1); 1941cb8a054bSGlenn Faden } 1942cb8a054bSGlenn Faden } 1943cb8a054bSGlenn Faden } else { 1944cb8a054bSGlenn Faden forced_login = B_TRUE; 1945cb8a054bSGlenn Faden } 1946cb8a054bSGlenn Faden 1947cb8a054bSGlenn Faden /* 19487c478bd9Sstevel@tonic-gate * The console is a separate case from the rest of the code; handle 19497c478bd9Sstevel@tonic-gate * it first. 19507c478bd9Sstevel@tonic-gate */ 19517c478bd9Sstevel@tonic-gate if (console) { 19527c478bd9Sstevel@tonic-gate /* 19537c478bd9Sstevel@tonic-gate * Ensure that zoneadmd for this zone is running. 19547c478bd9Sstevel@tonic-gate */ 19557c478bd9Sstevel@tonic-gate if (start_zoneadmd(zonename) == -1) 19567c478bd9Sstevel@tonic-gate return (1); 19577c478bd9Sstevel@tonic-gate 19587c478bd9Sstevel@tonic-gate /* 19597c478bd9Sstevel@tonic-gate * Make contact with zoneadmd. 19607c478bd9Sstevel@tonic-gate */ 19617c478bd9Sstevel@tonic-gate if (get_console_master(zonename) == -1) 19627c478bd9Sstevel@tonic-gate return (1); 19637c478bd9Sstevel@tonic-gate 1964c2589d13SGarrett D'Amore if (!quiet) 1965c2589d13SGarrett D'Amore (void) printf( 1966c2589d13SGarrett D'Amore gettext("[Connected to zone '%s' console]\n"), 19677c478bd9Sstevel@tonic-gate zonename); 19687c478bd9Sstevel@tonic-gate 19697c478bd9Sstevel@tonic-gate if (set_tty_rawmode(STDIN_FILENO) == -1) { 19707c478bd9Sstevel@tonic-gate reset_tty(); 19717c478bd9Sstevel@tonic-gate zperror(gettext("failed to set stdin pty to raw mode")); 19727c478bd9Sstevel@tonic-gate return (1); 19737c478bd9Sstevel@tonic-gate } 19747c478bd9Sstevel@tonic-gate 19757c478bd9Sstevel@tonic-gate (void) sigset(SIGWINCH, sigwinch); 19767c478bd9Sstevel@tonic-gate (void) sigwinch(0); 19777c478bd9Sstevel@tonic-gate 19787c478bd9Sstevel@tonic-gate /* 19797c478bd9Sstevel@tonic-gate * Run the I/O loop until we get disconnected. 19807c478bd9Sstevel@tonic-gate */ 1981ff17c8bfSgjelinek doio(masterfd, -1, masterfd, -1, -1, B_FALSE); 19827c478bd9Sstevel@tonic-gate reset_tty(); 1983c2589d13SGarrett D'Amore if (!quiet) 1984c2589d13SGarrett D'Amore (void) printf( 1985c2589d13SGarrett D'Amore gettext("\n[Connection to zone '%s' console " 19867c478bd9Sstevel@tonic-gate "closed]\n"), zonename); 19877c478bd9Sstevel@tonic-gate 19887c478bd9Sstevel@tonic-gate return (0); 19897c478bd9Sstevel@tonic-gate } 19907c478bd9Sstevel@tonic-gate 1991108322fbScarlsonj if (st != ZONE_STATE_RUNNING && st != ZONE_STATE_MOUNTED) { 19927c478bd9Sstevel@tonic-gate zerror(gettext("login allowed only to running zones " 19937c478bd9Sstevel@tonic-gate "(%s is '%s')."), zonename, zone_state_str(st)); 19947c478bd9Sstevel@tonic-gate return (1); 19957c478bd9Sstevel@tonic-gate } 19967c478bd9Sstevel@tonic-gate 1997108322fbScarlsonj (void) strlcpy(kernzone, zonename, sizeof (kernzone)); 1998108322fbScarlsonj if (zonecfg_in_alt_root()) { 1999108322fbScarlsonj FILE *fp = zonecfg_open_scratch("", B_FALSE); 2000108322fbScarlsonj 2001108322fbScarlsonj if (fp == NULL || zonecfg_find_scratch(fp, zonename, 2002108322fbScarlsonj zonecfg_get_root(), kernzone, sizeof (kernzone)) == -1) { 2003108322fbScarlsonj zerror(gettext("cannot find scratch zone %s"), 2004108322fbScarlsonj zonename); 2005108322fbScarlsonj if (fp != NULL) 2006108322fbScarlsonj zonecfg_close_scratch(fp); 2007108322fbScarlsonj return (1); 2008108322fbScarlsonj } 2009108322fbScarlsonj zonecfg_close_scratch(fp); 2010108322fbScarlsonj } 2011108322fbScarlsonj 2012108322fbScarlsonj if ((zoneid = getzoneidbyname(kernzone)) == -1) { 20137c478bd9Sstevel@tonic-gate zerror(gettext("failed to get zoneid for zone '%s'"), 20147c478bd9Sstevel@tonic-gate zonename); 20157c478bd9Sstevel@tonic-gate return (1); 20167c478bd9Sstevel@tonic-gate } 20177c478bd9Sstevel@tonic-gate 2018108322fbScarlsonj /* 2019facf4a8dSllai1 * We need the zone root path only if we are setting up a pty. 2020108322fbScarlsonj */ 2021facf4a8dSllai1 if (zone_get_devroot(zonename, devroot, sizeof (devroot)) == -1) { 2022facf4a8dSllai1 zerror(gettext("could not get dev path for zone %s"), 20237c478bd9Sstevel@tonic-gate zonename); 20247c478bd9Sstevel@tonic-gate return (1); 20257c478bd9Sstevel@tonic-gate } 20267c478bd9Sstevel@tonic-gate 202762868012SSteve Lawrence if (zone_get_brand(zonename, zonebrand, sizeof (zonebrand)) != Z_OK) { 20289acbbeafSnn35248 zerror(gettext("could not get brand for zone %s"), zonename); 20297c478bd9Sstevel@tonic-gate return (1); 20307c478bd9Sstevel@tonic-gate } 203162868012SSteve Lawrence /* 203262868012SSteve Lawrence * In the alternate root environment, the only supported 203362868012SSteve Lawrence * operations are mount and unmount. In this case, just treat 203462868012SSteve Lawrence * the zone as native if it is cluster. Cluster zones can be 203562868012SSteve Lawrence * native for the purpose of LU or upgrade, and the cluster 203662868012SSteve Lawrence * brand may not exist in the miniroot (such as in net install 203762868012SSteve Lawrence * upgrade). 203862868012SSteve Lawrence */ 2039e5816e35SEdward Pilatowicz if (zonecfg_default_brand(default_brand, 2040e5816e35SEdward Pilatowicz sizeof (default_brand)) != Z_OK) { 2041e5816e35SEdward Pilatowicz zerror(gettext("unable to determine default brand")); 2042e5816e35SEdward Pilatowicz return (1); 2043e5816e35SEdward Pilatowicz } 204462868012SSteve Lawrence if (zonecfg_in_alt_root() && 204562868012SSteve Lawrence strcmp(zonebrand, CLUSTER_BRAND_NAME) == 0) { 2046e5816e35SEdward Pilatowicz (void) strlcpy(zonebrand, default_brand, sizeof (zonebrand)); 204762868012SSteve Lawrence } 2048e5816e35SEdward Pilatowicz 204962868012SSteve Lawrence if ((bh = brand_open(zonebrand)) == NULL) { 205062868012SSteve Lawrence zerror(gettext("could not open brand for zone %s"), zonename); 205162868012SSteve Lawrence return (1); 205262868012SSteve Lawrence } 205362868012SSteve Lawrence 2054123807fbSedp if ((new_args = prep_args(bh, login, proc_args)) == NULL) { 20559acbbeafSnn35248 zperror(gettext("could not assemble new arguments")); 2056123807fbSedp brand_close(bh); 20579acbbeafSnn35248 return (1); 20589acbbeafSnn35248 } 2059858a4b99Ssl108498 /* 2060858a4b99Ssl108498 * Get the brand specific user_cmd. This command is used to get 2061858a4b99Ssl108498 * a passwd(4) entry for login. 2062858a4b99Ssl108498 */ 2063858a4b99Ssl108498 if (!interactive && !failsafe) { 2064858a4b99Ssl108498 if (zone_get_user_cmd(bh, login, user_cmd, 2065858a4b99Ssl108498 sizeof (user_cmd)) == NULL) { 2066858a4b99Ssl108498 zerror(gettext("could not get user_cmd for zone %s"), 2067858a4b99Ssl108498 zonename); 2068858a4b99Ssl108498 brand_close(bh); 2069858a4b99Ssl108498 return (1); 2070858a4b99Ssl108498 } 2071858a4b99Ssl108498 } 2072123807fbSedp brand_close(bh); 20737c478bd9Sstevel@tonic-gate 20747c478bd9Sstevel@tonic-gate if ((new_env = prep_env()) == NULL) { 20757c478bd9Sstevel@tonic-gate zperror(gettext("could not assemble new environment")); 20767c478bd9Sstevel@tonic-gate return (1); 20777c478bd9Sstevel@tonic-gate } 20787c478bd9Sstevel@tonic-gate 2079279721bfSGary Mills if (!interactive) { 2080279721bfSGary Mills if (nflag) { 2081279721bfSGary Mills int nfd; 2082279721bfSGary Mills 2083279721bfSGary Mills if ((nfd = open(_PATH_DEVNULL, O_RDONLY)) < 0) { 2084279721bfSGary Mills zperror(gettext("failed to open null device")); 2085279721bfSGary Mills return (1); 2086279721bfSGary Mills } 2087279721bfSGary Mills if (nfd != STDIN_FILENO) { 2088279721bfSGary Mills if (dup2(nfd, STDIN_FILENO) < 0) { 2089279721bfSGary Mills zperror(gettext( 2090279721bfSGary Mills "failed to dup2 null device")); 2091279721bfSGary Mills return (1); 2092279721bfSGary Mills } 2093279721bfSGary Mills (void) close(nfd); 2094279721bfSGary Mills } 2095279721bfSGary Mills /* /dev/null is now standard input */ 2096279721bfSGary Mills } 2097858a4b99Ssl108498 return (noninteractive_login(zonename, user_cmd, zoneid, 2098858a4b99Ssl108498 new_args, new_env)); 2099279721bfSGary Mills } 21007c478bd9Sstevel@tonic-gate 2101108322fbScarlsonj if (zonecfg_in_alt_root()) { 2102108322fbScarlsonj zerror(gettext("cannot use interactive login with scratch " 2103108322fbScarlsonj "zone")); 2104108322fbScarlsonj return (1); 2105108322fbScarlsonj } 2106108322fbScarlsonj 21077c478bd9Sstevel@tonic-gate /* 21087c478bd9Sstevel@tonic-gate * Things are more complex in interactive mode; we get the 21097c478bd9Sstevel@tonic-gate * master side of the pty, then place the user's terminal into 21107c478bd9Sstevel@tonic-gate * raw mode. 21117c478bd9Sstevel@tonic-gate */ 21127c478bd9Sstevel@tonic-gate if (get_master_pty() == -1) { 21137c478bd9Sstevel@tonic-gate zerror(gettext("could not setup master pty device")); 21147c478bd9Sstevel@tonic-gate return (1); 21157c478bd9Sstevel@tonic-gate } 21167c478bd9Sstevel@tonic-gate 21177c478bd9Sstevel@tonic-gate /* 21187c478bd9Sstevel@tonic-gate * Compute the "short name" of the pts. /dev/pts/2 --> pts/2 21197c478bd9Sstevel@tonic-gate */ 21207c478bd9Sstevel@tonic-gate if ((slavename = ptsname(masterfd)) == NULL) { 21217c478bd9Sstevel@tonic-gate zperror(gettext("failed to get name for pseudo-tty")); 21227c478bd9Sstevel@tonic-gate return (1); 21237c478bd9Sstevel@tonic-gate } 21247c478bd9Sstevel@tonic-gate if (strncmp(slavename, "/dev/", strlen("/dev/")) == 0) 21257c478bd9Sstevel@tonic-gate (void) strlcpy(slaveshortname, slavename + strlen("/dev/"), 21267c478bd9Sstevel@tonic-gate sizeof (slaveshortname)); 21277c478bd9Sstevel@tonic-gate else 21287c478bd9Sstevel@tonic-gate (void) strlcpy(slaveshortname, slavename, 21297c478bd9Sstevel@tonic-gate sizeof (slaveshortname)); 21307c478bd9Sstevel@tonic-gate 2131c2589d13SGarrett D'Amore if (!quiet) 2132c2589d13SGarrett D'Amore (void) printf(gettext("[Connected to zone '%s' %s]\n"), 2133c2589d13SGarrett D'Amore zonename, slaveshortname); 21347c478bd9Sstevel@tonic-gate 21357c478bd9Sstevel@tonic-gate if (set_tty_rawmode(STDIN_FILENO) == -1) { 21367c478bd9Sstevel@tonic-gate reset_tty(); 21377c478bd9Sstevel@tonic-gate zperror(gettext("failed to set stdin pty to raw mode")); 21387c478bd9Sstevel@tonic-gate return (1); 21397c478bd9Sstevel@tonic-gate } 21407c478bd9Sstevel@tonic-gate 21417c478bd9Sstevel@tonic-gate if (prefork_dropprivs() != 0) { 21427c478bd9Sstevel@tonic-gate reset_tty(); 21437c478bd9Sstevel@tonic-gate zperror(gettext("could not allocate privilege set")); 21447c478bd9Sstevel@tonic-gate return (1); 21457c478bd9Sstevel@tonic-gate } 21467c478bd9Sstevel@tonic-gate 21477c478bd9Sstevel@tonic-gate /* 21487c478bd9Sstevel@tonic-gate * We must mask SIGCLD until after we have coped with the fork 21497c478bd9Sstevel@tonic-gate * sufficiently to deal with it; otherwise we can race and receive the 21507c478bd9Sstevel@tonic-gate * signal before child_pid has been initialized (yes, this really 21517c478bd9Sstevel@tonic-gate * happens). 21527c478bd9Sstevel@tonic-gate */ 21537c478bd9Sstevel@tonic-gate (void) sigset(SIGCLD, sigcld); 21547c478bd9Sstevel@tonic-gate (void) sigemptyset(&block_cld); 21557c478bd9Sstevel@tonic-gate (void) sigaddset(&block_cld, SIGCLD); 21567c478bd9Sstevel@tonic-gate (void) sigprocmask(SIG_BLOCK, &block_cld, NULL); 21577c478bd9Sstevel@tonic-gate 21587c478bd9Sstevel@tonic-gate /* 21597c478bd9Sstevel@tonic-gate * We activate the contract template at the last minute to 21607c478bd9Sstevel@tonic-gate * avoid intermediate functions that could be using fork(2) 21617c478bd9Sstevel@tonic-gate * internally. 21627c478bd9Sstevel@tonic-gate */ 21637c478bd9Sstevel@tonic-gate if ((tmpl_fd = init_template()) == -1) { 21647c478bd9Sstevel@tonic-gate reset_tty(); 21657c478bd9Sstevel@tonic-gate zperror(gettext("could not create contract")); 21667c478bd9Sstevel@tonic-gate return (1); 21677c478bd9Sstevel@tonic-gate } 21687c478bd9Sstevel@tonic-gate 21697c478bd9Sstevel@tonic-gate if ((child_pid = fork()) == -1) { 21707c478bd9Sstevel@tonic-gate (void) ct_tmpl_clear(tmpl_fd); 21717c478bd9Sstevel@tonic-gate reset_tty(); 21727c478bd9Sstevel@tonic-gate zperror(gettext("could not fork")); 21737c478bd9Sstevel@tonic-gate return (1); 21747c478bd9Sstevel@tonic-gate } else if (child_pid == 0) { /* child process */ 21757c478bd9Sstevel@tonic-gate int slavefd, newslave; 21767c478bd9Sstevel@tonic-gate 21777c478bd9Sstevel@tonic-gate (void) ct_tmpl_clear(tmpl_fd); 21787c478bd9Sstevel@tonic-gate (void) close(tmpl_fd); 21797c478bd9Sstevel@tonic-gate 21807c478bd9Sstevel@tonic-gate (void) sigprocmask(SIG_UNBLOCK, &block_cld, NULL); 21817c478bd9Sstevel@tonic-gate 2182facf4a8dSllai1 if ((slavefd = init_slave_pty(zoneid, devroot)) == -1) 21837c478bd9Sstevel@tonic-gate return (1); 21847c478bd9Sstevel@tonic-gate 21857c478bd9Sstevel@tonic-gate /* 21867c478bd9Sstevel@tonic-gate * Close all fds except for the slave pty. 21877c478bd9Sstevel@tonic-gate */ 21887c478bd9Sstevel@tonic-gate (void) fdwalk(close_func, &slavefd); 21897c478bd9Sstevel@tonic-gate 21907c478bd9Sstevel@tonic-gate /* 21917c478bd9Sstevel@tonic-gate * Temporarily dup slavefd to stderr; that way if we have 21927c478bd9Sstevel@tonic-gate * to print out that zone_enter failed, the output will 21937c478bd9Sstevel@tonic-gate * have somewhere to go. 21947c478bd9Sstevel@tonic-gate */ 21957c478bd9Sstevel@tonic-gate if (slavefd != STDERR_FILENO) 21967c478bd9Sstevel@tonic-gate (void) dup2(slavefd, STDERR_FILENO); 21977c478bd9Sstevel@tonic-gate 21987c478bd9Sstevel@tonic-gate if (zone_enter(zoneid) == -1) { 21997c478bd9Sstevel@tonic-gate zerror(gettext("could not enter zone %s: %s"), 22007c478bd9Sstevel@tonic-gate zonename, strerror(errno)); 22017c478bd9Sstevel@tonic-gate return (1); 22027c478bd9Sstevel@tonic-gate } 22037c478bd9Sstevel@tonic-gate 22047c478bd9Sstevel@tonic-gate if (slavefd != STDERR_FILENO) 22057c478bd9Sstevel@tonic-gate (void) close(STDERR_FILENO); 22067c478bd9Sstevel@tonic-gate 22077c478bd9Sstevel@tonic-gate /* 22087c478bd9Sstevel@tonic-gate * We take pains to get this process into a new process 22097c478bd9Sstevel@tonic-gate * group, and subsequently a new session. In this way, 22107c478bd9Sstevel@tonic-gate * we'll have a session which doesn't yet have a controlling 22117c478bd9Sstevel@tonic-gate * terminal. When we open the slave, it will become the 22127c478bd9Sstevel@tonic-gate * controlling terminal; no PIDs concerning pgrps or sids 22137c478bd9Sstevel@tonic-gate * will leak inappropriately into the zone. 22147c478bd9Sstevel@tonic-gate */ 22157c478bd9Sstevel@tonic-gate (void) setpgrp(); 22167c478bd9Sstevel@tonic-gate 22177c478bd9Sstevel@tonic-gate /* 22187c478bd9Sstevel@tonic-gate * We need the slave pty to be referenced from the zone's 22197c478bd9Sstevel@tonic-gate * /dev in order to ensure that the devt's, etc are all 22207c478bd9Sstevel@tonic-gate * correct. Otherwise we break ttyname and the like. 22217c478bd9Sstevel@tonic-gate */ 22227c478bd9Sstevel@tonic-gate if ((newslave = open(slavename, O_RDWR)) == -1) { 22237c478bd9Sstevel@tonic-gate (void) close(slavefd); 22247c478bd9Sstevel@tonic-gate return (1); 22257c478bd9Sstevel@tonic-gate } 22267c478bd9Sstevel@tonic-gate (void) close(slavefd); 22277c478bd9Sstevel@tonic-gate slavefd = newslave; 22287c478bd9Sstevel@tonic-gate 22297c478bd9Sstevel@tonic-gate /* 22307c478bd9Sstevel@tonic-gate * dup the slave to the various FDs, so that when the 22317c478bd9Sstevel@tonic-gate * spawned process does a write/read it maps to the slave 22327c478bd9Sstevel@tonic-gate * pty. 22337c478bd9Sstevel@tonic-gate */ 22347c478bd9Sstevel@tonic-gate (void) dup2(slavefd, STDIN_FILENO); 22357c478bd9Sstevel@tonic-gate (void) dup2(slavefd, STDOUT_FILENO); 22367c478bd9Sstevel@tonic-gate (void) dup2(slavefd, STDERR_FILENO); 22377c478bd9Sstevel@tonic-gate if (slavefd != STDIN_FILENO && slavefd != STDOUT_FILENO && 22387c478bd9Sstevel@tonic-gate slavefd != STDERR_FILENO) { 22397c478bd9Sstevel@tonic-gate (void) close(slavefd); 22407c478bd9Sstevel@tonic-gate } 22417c478bd9Sstevel@tonic-gate 22427c478bd9Sstevel@tonic-gate /* 22437c478bd9Sstevel@tonic-gate * In failsafe mode, we don't use login(1), so don't try 22447c478bd9Sstevel@tonic-gate * setting up a utmpx entry. 22457c478bd9Sstevel@tonic-gate */ 2246eb5a5c78SSurya Prakki if (!failsafe) 22477c478bd9Sstevel@tonic-gate if (setup_utmpx(slaveshortname) == -1) 22487c478bd9Sstevel@tonic-gate return (1); 22497c478bd9Sstevel@tonic-gate 2250cb8a054bSGlenn Faden /* 2251cb8a054bSGlenn Faden * The child needs to run as root to 2252cb8a054bSGlenn Faden * execute the brand's login program. 2253cb8a054bSGlenn Faden */ 2254cb8a054bSGlenn Faden if (setuid(0) == -1) { 2255cb8a054bSGlenn Faden zperror(gettext("insufficient privilege")); 2256cb8a054bSGlenn Faden return (1); 2257cb8a054bSGlenn Faden } 2258cb8a054bSGlenn Faden 22597c478bd9Sstevel@tonic-gate (void) execve(new_args[0], new_args, new_env); 22607c478bd9Sstevel@tonic-gate zperror(gettext("exec failure")); 22617c478bd9Sstevel@tonic-gate return (1); 22627c478bd9Sstevel@tonic-gate } 2263cb8a054bSGlenn Faden 22647c478bd9Sstevel@tonic-gate (void) ct_tmpl_clear(tmpl_fd); 22657c478bd9Sstevel@tonic-gate (void) close(tmpl_fd); 22667c478bd9Sstevel@tonic-gate 22677c478bd9Sstevel@tonic-gate /* 22687c478bd9Sstevel@tonic-gate * The rest is only for the parent process. 22697c478bd9Sstevel@tonic-gate */ 22707c478bd9Sstevel@tonic-gate (void) sigset(SIGWINCH, sigwinch); 22717c478bd9Sstevel@tonic-gate 22727c478bd9Sstevel@tonic-gate postfork_dropprivs(); 22737c478bd9Sstevel@tonic-gate 22747c478bd9Sstevel@tonic-gate (void) sigprocmask(SIG_UNBLOCK, &block_cld, NULL); 2275ff17c8bfSgjelinek doio(masterfd, -1, masterfd, -1, -1, B_FALSE); 22767c478bd9Sstevel@tonic-gate 22777c478bd9Sstevel@tonic-gate reset_tty(); 2278c2589d13SGarrett D'Amore if (!quiet) 22797c478bd9Sstevel@tonic-gate (void) fprintf(stderr, 2280c2589d13SGarrett D'Amore gettext("\n[Connection to zone '%s' %s closed]\n"), 2281c2589d13SGarrett D'Amore zonename, slaveshortname); 22827c478bd9Sstevel@tonic-gate 22837c478bd9Sstevel@tonic-gate if (pollerr != 0) { 22847c478bd9Sstevel@tonic-gate (void) fprintf(stderr, gettext("Error: connection closed due " 22857c478bd9Sstevel@tonic-gate "to unexpected pollevents=0x%x.\n"), pollerr); 22867c478bd9Sstevel@tonic-gate return (1); 22877c478bd9Sstevel@tonic-gate } 22887c478bd9Sstevel@tonic-gate 22897c478bd9Sstevel@tonic-gate return (0); 22907c478bd9Sstevel@tonic-gate } 2291