Print this page
5700 add zlogin -d option to allow graceful disconnect when zone is halted
Reviewed by: Andrew Gabriel <illumos@cucumber.demon.co.uk>
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>


   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
  23  * Copyright 2013 DEY Storage Systems, Inc.
  24  * Copyright (c) 2014 Gary Mills
  25  * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
  26  */
  27 
  28 /*
  29  * zlogin provides three types of login which allow users in the global
  30  * zone to access non-global zones.
  31  *
  32  * - "interactive login" is similar to rlogin(1); for example, the user could
  33  *   issue 'zlogin my-zone' or 'zlogin -e ^ -l me my-zone'.   The user is
  34  *   granted a new pty (which is then shoved into the zone), and an I/O
  35  *   loop between parent and child processes takes care of the interactive
  36  *   session.  In this mode, login(1) (and its -c option, which means
  37  *   "already authenticated") is employed to take care of the initialization
  38  *   of the user's session.
  39  *
  40  * - "non-interactive login" is similar to su(1M); the user could issue
  41  *   'zlogin my-zone ls -l' and the command would be run as specified.
  42  *   In this mode, zlogin sets up pipes as the communication channel, and
  43  *   'su' is used to do the login setup work.
  44  *
  45  * - "console login" is the equivalent to accessing the tip line for a


  87 #include <locale.h>
  88 #include <libzonecfg.h>
  89 #include <libcontract.h>
  90 #include <libbrand.h>
  91 #include <auth_list.h>
  92 #include <auth_attr.h>
  93 #include <secdb.h>
  94 
  95 static int masterfd;
  96 static struct termios save_termios;
  97 static struct termios effective_termios;
  98 static int save_fd;
  99 static struct winsize winsize;
 100 static volatile int dead;
 101 static volatile pid_t child_pid = -1;
 102 static int interactive = 0;
 103 static priv_set_t *dropprivs;
 104 
 105 static int nocmdchar = 0;
 106 static int failsafe = 0;

 107 static char cmdchar = '~';
 108 static int quiet = 0;
 109 
 110 static int pollerr = 0;
 111 
 112 static const char *pname;
 113 static char *username;
 114 
 115 /*
 116  * When forced_login is true, the user is not prompted
 117  * for an authentication password in the target zone.
 118  */
 119 static boolean_t forced_login = B_FALSE;
 120 
 121 #if !defined(TEXT_DOMAIN)               /* should be defined by cc -D */
 122 #define TEXT_DOMAIN     "SYS_TEST"      /* Use this only if it wasn't */
 123 #endif
 124 
 125 #define SUPATH  "/usr/bin/su"
 126 #define FAILSAFESHELL   "/sbin/sh"


 135  * than ZLOGIN_BUFSIZ (because we share the buffer in doio).  This value is
 136  * also chosen in conjunction with the HI_WATER setting to make sure we
 137  * don't fill up the pipe.  We can write FIFOHIWAT (16k) into the pipe before
 138  * blocking.  By having ZLOGIN_RDBUFSIZ set to 1k and HI_WATER set to 8k, we
 139  * know we can always write a ZLOGIN_RDBUFSIZ chunk into the pipe when there
 140  * is less than HI_WATER data already in the pipe.
 141  */
 142 #define ZLOGIN_BUFSIZ   8192
 143 #define ZLOGIN_RDBUFSIZ 1024
 144 #define HI_WATER        8192
 145 
 146 /*
 147  * See canonify() below.  CANONIFY_LEN is the maximum length that a
 148  * "canonical" sequence will expand to (backslash, three octal digits, NUL).
 149  */
 150 #define CANONIFY_LEN 5
 151 
 152 static void
 153 usage(void)
 154 {
 155         (void) fprintf(stderr, gettext("usage: %s [ -nQCES ] [ -e cmdchar ] "
 156             "[-l user] zonename [command [args ...] ]\n"), pname);
 157         exit(2);
 158 }
 159 
 160 static const char *
 161 getpname(const char *arg0)
 162 {
 163         const char *p = strrchr(arg0, '/');
 164 
 165         if (p == NULL)
 166                 p = arg0;
 167         else
 168                 p++;
 169 
 170         pname = p;
 171         return (p);
 172 }
 173 
 174 static void
 175 zerror(const char *fmt, ...)


 261         int msglen;
 262         int i = 0, err = 0;
 263 
 264         if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
 265                 zperror(gettext("could not create socket"));
 266                 return (-1);
 267         }
 268 
 269         bzero(&servaddr, sizeof (servaddr));
 270         servaddr.sun_family = AF_UNIX;
 271         (void) snprintf(servaddr.sun_path, sizeof (servaddr.sun_path),
 272             "%s/%s.console_sock", ZONES_TMPDIR, zname);
 273 
 274         if (connect(sockfd, (struct sockaddr *)&servaddr,
 275             sizeof (servaddr)) == -1) {
 276                 zperror(gettext("Could not connect to zone console"));
 277                 goto bad;
 278         }
 279         masterfd = sockfd;
 280 
 281         msglen = snprintf(clientid, sizeof (clientid), "IDENT %lu %s\n",
 282             getpid(), setlocale(LC_MESSAGES, NULL));
 283 
 284         if (msglen >= sizeof (clientid) || msglen < 0) {
 285                 zerror("protocol error");
 286                 goto bad;
 287         }
 288 
 289         if (write(masterfd, clientid, msglen) != msglen) {
 290                 zerror("protocol error");
 291                 goto bad;
 292         }
 293 
 294         bzero(handshake, sizeof (handshake));
 295 
 296         /*
 297          * Take care not to accumulate more than our fill, and leave room for
 298          * the NUL at the end.
 299          */
 300         while ((err = read(masterfd, &c, 1)) == 1) {
 301                 if (i >= (sizeof (handshake) - 1))
 302                         break;


1736         char **new_args, **new_env;
1737         sigset_t block_cld;
1738         char devroot[MAXPATHLEN];
1739         char *slavename, slaveshortname[MAXPATHLEN];
1740         priv_set_t *privset;
1741         int tmpl_fd;
1742         char zonebrand[MAXNAMELEN];
1743         char default_brand[MAXNAMELEN];
1744         struct stat sb;
1745         char kernzone[ZONENAME_MAX];
1746         brand_handle_t bh;
1747         char user_cmd[MAXPATHLEN];
1748         char authname[MAXAUTHS];
1749 
1750         (void) setlocale(LC_ALL, "");
1751         (void) textdomain(TEXT_DOMAIN);
1752 
1753         (void) getpname(argv[0]);
1754         username = get_username();
1755 
1756         while ((arg = getopt(argc, argv, "nECR:Se:l:Q")) != EOF) {
1757                 switch (arg) {
1758                 case 'C':
1759                         console = 1;
1760                         break;
1761                 case 'E':
1762                         nocmdchar = 1;
1763                         break;
1764                 case 'R':       /* undocumented */
1765                         if (*optarg != '/') {
1766                                 zerror(gettext("root path must be absolute."));
1767                                 exit(2);
1768                         }
1769                         if (stat(optarg, &sb) == -1 || !S_ISDIR(sb.st_mode)) {
1770                                 zerror(
1771                                     gettext("root path must be a directory."));
1772                                 exit(2);
1773                         }
1774                         zonecfg_set_root(optarg);
1775                         break;
1776                 case 'Q':
1777                         quiet = 1;
1778                         break;
1779                 case 'S':
1780                         failsafe = 1;
1781                         break;



1782                 case 'e':
1783                         set_cmdchar(optarg);
1784                         break;
1785                 case 'l':
1786                         login = optarg;
1787                         lflag = 1;
1788                         break;
1789                 case 'n':
1790                         nflag = 1;
1791                         break;
1792                 default:
1793                         usage();
1794                 }
1795         }
1796 
1797         if (console != 0) {
1798 
1799                 if (lflag != 0) {
1800                         zerror(gettext(
1801                             "-l may not be specified for console login"));


1809                 }
1810 
1811                 if (failsafe != 0) {
1812                         zerror(gettext(
1813                             "-S may not be specified for console login"));
1814                         usage();
1815                 }
1816 
1817                 if (zonecfg_in_alt_root()) {
1818                         zerror(gettext(
1819                             "-R may not be specified for console login"));
1820                         exit(2);
1821                 }
1822 
1823         }
1824 
1825         if (failsafe != 0 && lflag != 0) {
1826                 zerror(gettext("-l may not be specified for failsafe login"));
1827                 usage();
1828         }






1829 
1830         if (optind == (argc - 1)) {
1831                 /*
1832                  * zone name, no process name; this should be an interactive
1833                  * as long as STDIN is really a tty.
1834                  */
1835                 if (nflag != 0) {
1836                         zerror(gettext(
1837                             "-n may not be specified for interactive login"));
1838                         usage();
1839                 }
1840                 if (isatty(STDIN_FILENO))
1841                         interactive = 1;
1842                 zonename = argv[optind];
1843         } else if (optind < (argc - 1)) {
1844                 if (console) {
1845                         zerror(gettext("Commands may not be specified for "
1846                             "console login."));
1847                         usage();
1848                 }




   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
  23  * Copyright 2013 DEY Storage Systems, Inc.
  24  * Copyright (c) 2014 Gary Mills
  25  * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
  26  */
  27 
  28 /*
  29  * zlogin provides three types of login which allow users in the global
  30  * zone to access non-global zones.
  31  *
  32  * - "interactive login" is similar to rlogin(1); for example, the user could
  33  *   issue 'zlogin my-zone' or 'zlogin -e ^ -l me my-zone'.   The user is
  34  *   granted a new pty (which is then shoved into the zone), and an I/O
  35  *   loop between parent and child processes takes care of the interactive
  36  *   session.  In this mode, login(1) (and its -c option, which means
  37  *   "already authenticated") is employed to take care of the initialization
  38  *   of the user's session.
  39  *
  40  * - "non-interactive login" is similar to su(1M); the user could issue
  41  *   'zlogin my-zone ls -l' and the command would be run as specified.
  42  *   In this mode, zlogin sets up pipes as the communication channel, and
  43  *   'su' is used to do the login setup work.
  44  *
  45  * - "console login" is the equivalent to accessing the tip line for a


  87 #include <locale.h>
  88 #include <libzonecfg.h>
  89 #include <libcontract.h>
  90 #include <libbrand.h>
  91 #include <auth_list.h>
  92 #include <auth_attr.h>
  93 #include <secdb.h>
  94 
  95 static int masterfd;
  96 static struct termios save_termios;
  97 static struct termios effective_termios;
  98 static int save_fd;
  99 static struct winsize winsize;
 100 static volatile int dead;
 101 static volatile pid_t child_pid = -1;
 102 static int interactive = 0;
 103 static priv_set_t *dropprivs;
 104 
 105 static int nocmdchar = 0;
 106 static int failsafe = 0;
 107 static int disconnect = 0;
 108 static char cmdchar = '~';
 109 static int quiet = 0;
 110 
 111 static int pollerr = 0;
 112 
 113 static const char *pname;
 114 static char *username;
 115 
 116 /*
 117  * When forced_login is true, the user is not prompted
 118  * for an authentication password in the target zone.
 119  */
 120 static boolean_t forced_login = B_FALSE;
 121 
 122 #if !defined(TEXT_DOMAIN)               /* should be defined by cc -D */
 123 #define TEXT_DOMAIN     "SYS_TEST"      /* Use this only if it wasn't */
 124 #endif
 125 
 126 #define SUPATH  "/usr/bin/su"
 127 #define FAILSAFESHELL   "/sbin/sh"


 136  * than ZLOGIN_BUFSIZ (because we share the buffer in doio).  This value is
 137  * also chosen in conjunction with the HI_WATER setting to make sure we
 138  * don't fill up the pipe.  We can write FIFOHIWAT (16k) into the pipe before
 139  * blocking.  By having ZLOGIN_RDBUFSIZ set to 1k and HI_WATER set to 8k, we
 140  * know we can always write a ZLOGIN_RDBUFSIZ chunk into the pipe when there
 141  * is less than HI_WATER data already in the pipe.
 142  */
 143 #define ZLOGIN_BUFSIZ   8192
 144 #define ZLOGIN_RDBUFSIZ 1024
 145 #define HI_WATER        8192
 146 
 147 /*
 148  * See canonify() below.  CANONIFY_LEN is the maximum length that a
 149  * "canonical" sequence will expand to (backslash, three octal digits, NUL).
 150  */
 151 #define CANONIFY_LEN 5
 152 
 153 static void
 154 usage(void)
 155 {
 156         (void) fprintf(stderr, gettext("usage: %s [ -dnQCES ] [ -e cmdchar ] "
 157             "[-l user] zonename [command [args ...] ]\n"), pname);
 158         exit(2);
 159 }
 160 
 161 static const char *
 162 getpname(const char *arg0)
 163 {
 164         const char *p = strrchr(arg0, '/');
 165 
 166         if (p == NULL)
 167                 p = arg0;
 168         else
 169                 p++;
 170 
 171         pname = p;
 172         return (p);
 173 }
 174 
 175 static void
 176 zerror(const char *fmt, ...)


 262         int msglen;
 263         int i = 0, err = 0;
 264 
 265         if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
 266                 zperror(gettext("could not create socket"));
 267                 return (-1);
 268         }
 269 
 270         bzero(&servaddr, sizeof (servaddr));
 271         servaddr.sun_family = AF_UNIX;
 272         (void) snprintf(servaddr.sun_path, sizeof (servaddr.sun_path),
 273             "%s/%s.console_sock", ZONES_TMPDIR, zname);
 274 
 275         if (connect(sockfd, (struct sockaddr *)&servaddr,
 276             sizeof (servaddr)) == -1) {
 277                 zperror(gettext("Could not connect to zone console"));
 278                 goto bad;
 279         }
 280         masterfd = sockfd;
 281 
 282         msglen = snprintf(clientid, sizeof (clientid), "IDENT %lu %s %d\n",
 283             getpid(), setlocale(LC_MESSAGES, NULL), disconnect);
 284 
 285         if (msglen >= sizeof (clientid) || msglen < 0) {
 286                 zerror("protocol error");
 287                 goto bad;
 288         }
 289 
 290         if (write(masterfd, clientid, msglen) != msglen) {
 291                 zerror("protocol error");
 292                 goto bad;
 293         }
 294 
 295         bzero(handshake, sizeof (handshake));
 296 
 297         /*
 298          * Take care not to accumulate more than our fill, and leave room for
 299          * the NUL at the end.
 300          */
 301         while ((err = read(masterfd, &c, 1)) == 1) {
 302                 if (i >= (sizeof (handshake) - 1))
 303                         break;


1737         char **new_args, **new_env;
1738         sigset_t block_cld;
1739         char devroot[MAXPATHLEN];
1740         char *slavename, slaveshortname[MAXPATHLEN];
1741         priv_set_t *privset;
1742         int tmpl_fd;
1743         char zonebrand[MAXNAMELEN];
1744         char default_brand[MAXNAMELEN];
1745         struct stat sb;
1746         char kernzone[ZONENAME_MAX];
1747         brand_handle_t bh;
1748         char user_cmd[MAXPATHLEN];
1749         char authname[MAXAUTHS];
1750 
1751         (void) setlocale(LC_ALL, "");
1752         (void) textdomain(TEXT_DOMAIN);
1753 
1754         (void) getpname(argv[0]);
1755         username = get_username();
1756 
1757         while ((arg = getopt(argc, argv, "dnECR:Se:l:Q")) != EOF) {
1758                 switch (arg) {
1759                 case 'C':
1760                         console = 1;
1761                         break;
1762                 case 'E':
1763                         nocmdchar = 1;
1764                         break;
1765                 case 'R':       /* undocumented */
1766                         if (*optarg != '/') {
1767                                 zerror(gettext("root path must be absolute."));
1768                                 exit(2);
1769                         }
1770                         if (stat(optarg, &sb) == -1 || !S_ISDIR(sb.st_mode)) {
1771                                 zerror(
1772                                     gettext("root path must be a directory."));
1773                                 exit(2);
1774                         }
1775                         zonecfg_set_root(optarg);
1776                         break;
1777                 case 'Q':
1778                         quiet = 1;
1779                         break;
1780                 case 'S':
1781                         failsafe = 1;
1782                         break;
1783                 case 'd':
1784                         disconnect = 1;
1785                         break;
1786                 case 'e':
1787                         set_cmdchar(optarg);
1788                         break;
1789                 case 'l':
1790                         login = optarg;
1791                         lflag = 1;
1792                         break;
1793                 case 'n':
1794                         nflag = 1;
1795                         break;
1796                 default:
1797                         usage();
1798                 }
1799         }
1800 
1801         if (console != 0) {
1802 
1803                 if (lflag != 0) {
1804                         zerror(gettext(
1805                             "-l may not be specified for console login"));


1813                 }
1814 
1815                 if (failsafe != 0) {
1816                         zerror(gettext(
1817                             "-S may not be specified for console login"));
1818                         usage();
1819                 }
1820 
1821                 if (zonecfg_in_alt_root()) {
1822                         zerror(gettext(
1823                             "-R may not be specified for console login"));
1824                         exit(2);
1825                 }
1826 
1827         }
1828 
1829         if (failsafe != 0 && lflag != 0) {
1830                 zerror(gettext("-l may not be specified for failsafe login"));
1831                 usage();
1832         }
1833 
1834         if (!console && disconnect != 0) {
1835                 zerror(gettext(
1836                     "-d may only be specified with console login"));
1837                 usage();
1838         }
1839 
1840         if (optind == (argc - 1)) {
1841                 /*
1842                  * zone name, no process name; this should be an interactive
1843                  * as long as STDIN is really a tty.
1844                  */
1845                 if (nflag != 0) {
1846                         zerror(gettext(
1847                             "-n may not be specified for interactive login"));
1848                         usage();
1849                 }
1850                 if (isatty(STDIN_FILENO))
1851                         interactive = 1;
1852                 zonename = argv[optind];
1853         } else if (optind < (argc - 1)) {
1854                 if (console) {
1855                         zerror(gettext("Commands may not be specified for "
1856                             "console login."));
1857                         usage();
1858                 }