zlogin.c (634e26ec75c89095090605284938356a3145f2b8) zlogin.c (cb8a054b1ab30d5caa746e6c44f29d4c9d3071c1)
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
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

--- 5 unchanged lines hidden (view full) ---

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/*
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
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

--- 5 unchanged lines hidden (view full) ---

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 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
22 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26/*
27 * zlogin provides three types of login which allow users in the global
28 * zone to access non-global zones.
29 *
30 * - "interactive login" is similar to rlogin(1); for example, the user could
31 * issue 'zlogin my-zone' or 'zlogin -e ^ -l me my-zone'. The user is

--- 48 unchanged lines hidden (view full) ---

80#include <zone.h>
81#include <fcntl.h>
82#include <libdevinfo.h>
83#include <libintl.h>
84#include <locale.h>
85#include <libzonecfg.h>
86#include <libcontract.h>
87#include <libbrand.h>
23 */
24
25/*
26 * zlogin provides three types of login which allow users in the global
27 * zone to access non-global zones.
28 *
29 * - "interactive login" is similar to rlogin(1); for example, the user could
30 * issue 'zlogin my-zone' or 'zlogin -e ^ -l me my-zone'. The user is

--- 48 unchanged lines hidden (view full) ---

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

--- 902 unchanged lines hidden (view full) ---

1017zone_login_cmd(brand_handle_t bh, const char *login)
1018{
1019 static char result_buf[ARG_MAX];
1020 char **new_argv, *ptr, *lasts;
1021 int n, a;
1022
1023 /* Get the login command for the target zone. */
1024 bzero(result_buf, sizeof (result_buf));
116#if !defined(TEXT_DOMAIN) /* should be defined by cc -D */
117#define TEXT_DOMAIN "SYS_TEST" /* Use this only if it wasn't */
118#endif
119
120#define SUPATH "/usr/bin/su"
121#define FAILSAFESHELL "/sbin/sh"
122#define DEFAULTSHELL "/sbin/sh"
123#define DEF_PATH "/usr/sbin:/usr/bin"

--- 902 unchanged lines hidden (view full) ---

1026zone_login_cmd(brand_handle_t bh, const char *login)
1027{
1028 static char result_buf[ARG_MAX];
1029 char **new_argv, *ptr, *lasts;
1030 int n, a;
1031
1032 /* Get the login command for the target zone. */
1033 bzero(result_buf, sizeof (result_buf));
1025 if (brand_get_login_cmd(bh, login,
1026 result_buf, sizeof (result_buf)) != 0)
1027 return (NULL);
1028
1034
1035 if (forced_login) {
1036 if (brand_get_forcedlogin_cmd(bh, login,
1037 result_buf, sizeof (result_buf)) != 0)
1038 return (NULL);
1039 } else {
1040 if (brand_get_login_cmd(bh, login,
1041 result_buf, sizeof (result_buf)) != 0)
1042 return (NULL);
1043 }
1044
1029 /*
1030 * We got back a string that we'd like to execute. But since
1031 * we're not doing the execution via a shell we'll need to convert
1032 * the exec string to an array of strings. We'll do that here
1033 * but we're going to be very simplistic about it and break stuff
1034 * up based on spaces. We're not even going to support any kind
1035 * of quoting or escape characters. It's truly amazing that
1036 * there is no library function in OpenSolaris to do this for us.

--- 72 unchanged lines hidden (view full) ---

1109
1110 new_argv[a++] = FAILSAFESHELL;
1111 } else {
1112 n = 5;
1113 if ((new_argv = malloc(sizeof (char *) * n)) == NULL)
1114 return (NULL);
1115
1116 new_argv[a++] = SUPATH;
1045 /*
1046 * We got back a string that we'd like to execute. But since
1047 * we're not doing the execution via a shell we'll need to convert
1048 * the exec string to an array of strings. We'll do that here
1049 * but we're going to be very simplistic about it and break stuff
1050 * up based on spaces. We're not even going to support any kind
1051 * of quoting or escape characters. It's truly amazing that
1052 * there is no library function in OpenSolaris to do this for us.

--- 72 unchanged lines hidden (view full) ---

1125
1126 new_argv[a++] = FAILSAFESHELL;
1127 } else {
1128 n = 5;
1129 if ((new_argv = malloc(sizeof (char *) * n)) == NULL)
1130 return (NULL);
1131
1132 new_argv[a++] = SUPATH;
1133 if (strcmp(login, "root") != 0) {
1134 new_argv[a++] = "-";
1135 n++;
1136 }
1117 new_argv[a++] = (char *)login;
1118 }
1119 new_argv[a++] = "-c";
1120 new_argv[a++] = subshell;
1121 new_argv[a++] = NULL;
1122 assert(a == n);
1123 } else {
1124 if (failsafe) {

--- 507 unchanged lines hidden (view full) ---

1632
1633 /*
1634 * Move into a new process group; the zone_enter will have
1635 * placed us into zsched's session, and we want to be in
1636 * a unique process group.
1637 */
1638 (void) setpgid(getpid(), getpid());
1639
1137 new_argv[a++] = (char *)login;
1138 }
1139 new_argv[a++] = "-c";
1140 new_argv[a++] = subshell;
1141 new_argv[a++] = NULL;
1142 assert(a == n);
1143 } else {
1144 if (failsafe) {

--- 507 unchanged lines hidden (view full) ---

1652
1653 /*
1654 * Move into a new process group; the zone_enter will have
1655 * placed us into zsched's session, and we want to be in
1656 * a unique process group.
1657 */
1658 (void) setpgid(getpid(), getpid());
1659
1660 /*
1661 * The child needs to run as root to
1662 * execute the su program.
1663 */
1664 if (setuid(0) == -1) {
1665 zperror(gettext("insufficient privilege"));
1666 return (1);
1667 }
1668
1640 (void) execve(new_args[0], new_args, new_env);
1641 zperror(gettext("exec failure"));
1642 _exit(1);
1643 }
1644 /* parent */
1645
1646 /* close pipe sides written by child */
1647 (void) close(stdout_pipe[1]);

--- 14 unchanged lines hidden (view full) ---

1662 if (retval == -1) {
1663 child_status = 0;
1664 }
1665 } while (retval != child_pid && errno != ECHILD);
1666
1667 return (WEXITSTATUS(child_status));
1668}
1669
1669 (void) execve(new_args[0], new_args, new_env);
1670 zperror(gettext("exec failure"));
1671 _exit(1);
1672 }
1673 /* parent */
1674
1675 /* close pipe sides written by child */
1676 (void) close(stdout_pipe[1]);

--- 14 unchanged lines hidden (view full) ---

1691 if (retval == -1) {
1692 child_status = 0;
1693 }
1694 } while (retval != child_pid && errno != ECHILD);
1695
1696 return (WEXITSTATUS(child_status));
1697}
1698
1699static char *
1700get_username()
1701{
1702 uid_t uid;
1703 struct passwd *nptr;
1704
1705 /*
1706 * Authorizations are checked to restrict access based on the
1707 * requested operation and zone name, It is assumed that the
1708 * program is running with all privileges, but that the real
1709 * user ID is that of the user or role on whose behalf we are
1710 * operating. So we start by getting the username that will be
1711 * used for subsequent authorization checks.
1712 */
1713
1714 uid = getuid();
1715 if ((nptr = getpwuid(uid)) == NULL) {
1716 zerror(gettext("could not get user name."));
1717 _exit(1);
1718 }
1719 return (nptr->pw_name);
1720}
1721
1670int
1671main(int argc, char **argv)
1672{
1673 int arg, console = 0;
1674 zoneid_t zoneid;
1675 zone_state_t st;
1676 char *login = "root";
1677 int lflag = 0;

--- 6 unchanged lines hidden (view full) ---

1684 priv_set_t *privset;
1685 int tmpl_fd;
1686 char zonebrand[MAXNAMELEN];
1687 char default_brand[MAXNAMELEN];
1688 struct stat sb;
1689 char kernzone[ZONENAME_MAX];
1690 brand_handle_t bh;
1691 char user_cmd[MAXPATHLEN];
1722int
1723main(int argc, char **argv)
1724{
1725 int arg, console = 0;
1726 zoneid_t zoneid;
1727 zone_state_t st;
1728 char *login = "root";
1729 int lflag = 0;

--- 6 unchanged lines hidden (view full) ---

1736 priv_set_t *privset;
1737 int tmpl_fd;
1738 char zonebrand[MAXNAMELEN];
1739 char default_brand[MAXNAMELEN];
1740 struct stat sb;
1741 char kernzone[ZONENAME_MAX];
1742 brand_handle_t bh;
1743 char user_cmd[MAXPATHLEN];
1744 char authname[MAXAUTHS];
1692
1693 (void) setlocale(LC_ALL, "");
1694 (void) textdomain(TEXT_DOMAIN);
1695
1696 (void) getpname(argv[0]);
1745
1746 (void) setlocale(LC_ALL, "");
1747 (void) textdomain(TEXT_DOMAIN);
1748
1749 (void) getpname(argv[0]);
1750 username = get_username();
1697
1698 while ((arg = getopt(argc, argv, "ECR:Se:l:")) != EOF) {
1699 switch (arg) {
1700 case 'C':
1701 console = 1;
1702 break;
1703 case 'E':
1704 nocmdchar = 1;

--- 90 unchanged lines hidden (view full) ---

1795 return (1);
1796 }
1797
1798 /*
1799 * In both console and non-console cases, we require all privs.
1800 * In the console case, because we may need to startup zoneadmd.
1801 * In the non-console case in order to do zone_enter(2), zonept()
1802 * and other tasks.
1751
1752 while ((arg = getopt(argc, argv, "ECR:Se:l:")) != EOF) {
1753 switch (arg) {
1754 case 'C':
1755 console = 1;
1756 break;
1757 case 'E':
1758 nocmdchar = 1;

--- 90 unchanged lines hidden (view full) ---

1849 return (1);
1850 }
1851
1852 /*
1853 * In both console and non-console cases, we require all privs.
1854 * In the console case, because we may need to startup zoneadmd.
1855 * In the non-console case in order to do zone_enter(2), zonept()
1856 * and other tasks.
1803 *
1804 * Future work: this solution is temporary. Ultimately, we need to
1805 * move to a flexible system which allows the global admin to
1806 * designate that a particular user can zlogin (and probably zlogin
1807 * -C) to a particular zone. This all-root business we have now is
1808 * quite sketchy.
1809 */
1857 */
1858
1810 if ((privset = priv_allocset()) == NULL) {
1811 zperror(gettext("priv_allocset failed"));
1812 return (1);
1813 }
1814
1815 if (getppriv(PRIV_EFFECTIVE, privset) != 0) {
1816 zperror(gettext("getppriv failed"));
1817 priv_freeset(privset);

--- 4 unchanged lines hidden (view full) ---

1822 zerror(gettext("You lack sufficient privilege to run "
1823 "this command (all privs required)"));
1824 priv_freeset(privset);
1825 return (1);
1826 }
1827 priv_freeset(privset);
1828
1829 /*
1859 if ((privset = priv_allocset()) == NULL) {
1860 zperror(gettext("priv_allocset failed"));
1861 return (1);
1862 }
1863
1864 if (getppriv(PRIV_EFFECTIVE, privset) != 0) {
1865 zperror(gettext("getppriv failed"));
1866 priv_freeset(privset);

--- 4 unchanged lines hidden (view full) ---

1871 zerror(gettext("You lack sufficient privilege to run "
1872 "this command (all privs required)"));
1873 priv_freeset(privset);
1874 return (1);
1875 }
1876 priv_freeset(privset);
1877
1878 /*
1879 * Check if user is authorized for requested usage of the zone
1880 */
1881
1882 (void) snprintf(authname, MAXAUTHS, "%s%s%s",
1883 ZONE_MANAGE_AUTH, KV_OBJECT, zonename);
1884 if (chkauthattr(authname, username) == 0) {
1885 if (console) {
1886 zerror(gettext("%s is not authorized for console "
1887 "access to %s zone."),
1888 username, zonename);
1889 return (1);
1890 } else {
1891 (void) snprintf(authname, MAXAUTHS, "%s%s%s",
1892 ZONE_LOGIN_AUTH, KV_OBJECT, zonename);
1893 if (failsafe || !interactive) {
1894 zerror(gettext("%s is not authorized for "
1895 "failsafe or non-interactive login "
1896 "to %s zone."), username, zonename);
1897 return (1);
1898 } else if (chkauthattr(authname, username) == 0) {
1899 zerror(gettext("%s is not authorized "
1900 " to login to %s zone."),
1901 username, zonename);
1902 return (1);
1903 }
1904 }
1905 } else {
1906 forced_login = B_TRUE;
1907 }
1908
1909 /*
1830 * The console is a separate case from the rest of the code; handle
1831 * it first.
1832 */
1833 if (console) {
1834 /*
1835 * Ensure that zoneadmd for this zone is running.
1836 */
1837 if (start_zoneadmd(zonename) == -1)

--- 273 unchanged lines hidden (view full) ---

2111 * we can simply skip it. If future brands don't fall into
2112 * either category, we'll have to add a per-brand utmpx
2113 * setup hook.
2114 */
2115 if (!failsafe && (strcmp(zonebrand, "lx") != 0))
2116 if (setup_utmpx(slaveshortname) == -1)
2117 return (1);
2118
1910 * The console is a separate case from the rest of the code; handle
1911 * it first.
1912 */
1913 if (console) {
1914 /*
1915 * Ensure that zoneadmd for this zone is running.
1916 */
1917 if (start_zoneadmd(zonename) == -1)

--- 273 unchanged lines hidden (view full) ---

2191 * we can simply skip it. If future brands don't fall into
2192 * either category, we'll have to add a per-brand utmpx
2193 * setup hook.
2194 */
2195 if (!failsafe && (strcmp(zonebrand, "lx") != 0))
2196 if (setup_utmpx(slaveshortname) == -1)
2197 return (1);
2198
2199 /*
2200 * The child needs to run as root to
2201 * execute the brand's login program.
2202 */
2203 if (setuid(0) == -1) {
2204 zperror(gettext("insufficient privilege"));
2205 return (1);
2206 }
2207
2119 (void) execve(new_args[0], new_args, new_env);
2120 zperror(gettext("exec failure"));
2121 return (1);
2122 }
2208 (void) execve(new_args[0], new_args, new_env);
2209 zperror(gettext("exec failure"));
2210 return (1);
2211 }
2212
2123 (void) ct_tmpl_clear(tmpl_fd);
2124 (void) close(tmpl_fd);
2125
2126 /*
2127 * The rest is only for the parent process.
2128 */
2129 (void) sigset(SIGWINCH, sigwinch);
2130

--- 18 unchanged lines hidden ---
2213 (void) ct_tmpl_clear(tmpl_fd);
2214 (void) close(tmpl_fd);
2215
2216 /*
2217 * The rest is only for the parent process.
2218 */
2219 (void) sigset(SIGWINCH, sigwinch);
2220

--- 18 unchanged lines hidden ---