/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2013 Nexenta Systems, Inc. All rights reserved.
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T
* All rights reserved.
*
* Copyright (c) 1987, 1988 Microsoft Corporation.
* All rights reserved.
*/
/*
* sulogin - special login program exec'd from init to let user
* come up single user, or go to default init state straight away.
*
* Explain the scoop to the user, prompt for an authorized user
* name or ^D and then prompt for password or ^D. If the password
* is correct, check if the user is authorized, if so enter
* single user. ^D exits sulogin, and init will go to default init state.
*
* go single user, no questions asked.
*/
#include <sys/sysmsg_impl.h>
#include <sys/resource.h>
#include <fcntl.h>
#include <termio.h>
#include <pwd.h>
#include <shadow.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <siginfo.h>
#include <utmpx.h>
#include <unistd.h>
#include <ucontext.h>
#include <string.h>
#include <strings.h>
#include <deflt.h>
#include <limits.h>
#include <errno.h>
#include <crypt.h>
#include <auth_attr.h>
#include <auth_list.h>
#include <nss_dbdefs.h>
#include <user_attr.h>
/*
* Intervals to sleep after failed login
*/
#ifndef SLEEPTIME
#endif
/*
* the name of the file containing the login defaults we deliberately
* use the same file as login(1)
*/
static int nchild = 0;
static char *findttyname(int fd);
static char *stripttyname(char *);
static char *sulogin_getinput(char *, int);
static void noop(int);
static void single(const char *, char *);
static void parenthandler();
static void termhandler(int);
static void setupsigs(void);
static int pathcmp(char *, char *);
static void doit(char *, char *);
static void childcleanup(int);
#define ECHOON 0
/* ARGSUSED */
int
{
int flags;
int fd;
int bufsize;
if (geteuid() != 0) {
return (EXIT_FAILURE);
}
/* Do the magic to determine the children */
return (EXIT_FAILURE);
/*
* If the console supports the CIOCTTYCONSOLE ioctl, then fetch
* its console device list. If not, then we use the default
* console name.
*/
return (EXIT_FAILURE);
if (bufsize > 0) {
return (EXIT_FAILURE);
return (EXIT_FAILURE);
} else
} else {
}
/*
* The attempt to turn the controlling terminals dev_t into a string
* may not be successful, thus leaving the variable cttyname as a
* NULL. This occurs if during boot we find
* the root partition (or some other partition)
* requires manual fsck, thus resulting in sulogin
* getting invoked. The ioctl for CIOCTTYCONSOLE
* called above returned NODEV for cttyd
* in these cases. NODEV gets returned when the vnode pointer
* in our session structure is NULL. In these cases it
* must be assumed that the default console is used.
*
*/
ptr = scratchlist;
if (p == NULL) {
return (EXIT_FAILURE);
break;
}
*p++ = '\0';
return (EXIT_FAILURE);
break;
}
ptr = p;
}
/*
* Use the same value of SLEEPTIME that login(1) uses. This
* the def*() functions.
*/
if (defopen(DEFAULT_LOGIN) == 0) {
/* ignore case */
}
/*
* Use our own value of PASSREQ, separate from the one login(1) uses.
* the def*() functions.
*/
if (defopen(DEFAULT_SULOGIN) == 0) {
}
/*
* user shell prompt
*/
setspent();
"in shadow password file ***\n\n");
}
endspent();
/*
* user shell prompt
*/
setpwent();
"in password file ***\n\n");
}
endpwent();
/* process with controlling tty treated special */
if (pid == -1)
return (EXIT_FAILURE);
else {
setupsigs();
originalpid = getpid();
/*
* init() was invoked from a console that was not
* the default console, nor was it an auxiliary.
*/
termhandler(0);
/* Never returns */
/* Never returns */
}
}
originalpid = getppid();
/*
* If there isn't a password on root, then don't permit
* the fanout capability of sulogin.
*/
if (p == NULL) {
break;
}
*p++ = '\0';
ptr = p;
}
}
setupsigs();
} else if (pid == -1)
return (EXIT_FAILURE);
}
/*
* When parent is all done, it pauses until one of its children
* signals that its time to kill the underpriviledged.
*/
return (0);
}
/*
* These flags are taken from stty's "sane" table entries in
*/
/*
* Do the equivalent of 'stty sane' on the terminal since we don't know
* what state it was in on startup.
*/
static void
{
}
/*
* Fork a child of sulogin for each of the auxiliary consoles.
*/
static void
{
setupsigs();
} else if (pid == -1)
}
}
static int
{
return (1);
return (0);
return (1);
return (1);
return (0);
return (1);
}
/* Handlers for the children at initialization */
static void
{
}
static void
{
for (i = 0; i < 3; i++)
(void) close(i);
if (setsid() == -1)
}
/*
* In system maintenance mode, all virtual console instances
* of the svc:/system/console-login service are not available
* any more, and only the system console is available. So here
* we always switch to the system console in case at the moment
* the active console isn't it.
*/
if (fd != 0)
if (fd != 1)
if (fd != 2)
if (fd > 2)
/* Stop progress bar and reset console mode to text */
}
for (;;) {
do {
(void) printf("\nEnter user name for system "
"maintenance (control-d to bypass): ");
/* signal other children to exit */
/* ^D, so straight to default init state */
}
} while (user[0] == '\0');
(void) printf("Enter %s password (control-d to bypass): ",
user);
/* signal other children to exit */
/* ^D, so straight to default init state */
}
/*
* the user entered doesn't exist, too bad.
*/
goto sorry;
}
/*
* There is a special case error to catch here:
* If the password is hashed with an algorithm
* other than the old unix crypt the call to crypt(3c)
* could fail if /usr is corrupt or not available
* since by default /etc/security/crypt.conf will
* Or it could happen if /etc/security/crypt.conf
* is corrupted.
*
* If this happens crypt(3c) will return NULL and
* set errno to ELIBACC for the former condition or
* EINVAL for the latter, in this case we bypass
* authentication and just verify that the user is
* authorized.
*/
errno = 0;
goto checkauth;
goto sorry;
}
/*
* There is a special case error here as well.
* returns NULL.
* In this case, we just give access because this is similar
*/
goto sorry;
}
/* single never returns */
(void) printf("\nLogin incorrect or user %s not authorized\n",
user);
}
}
/*
* single() - exec shell for single user mode
*/
static void
{
struct utmpx *u;
/*
* utmpx records on the console device are expected to be "console"
* by other processes, such as dtlogin.
*/
/* update the utmpx file. */
u->ut_type = USER_PROCESS;
(void) pututxline(u);
break;
}
}
if (!found) {
entryx.ut_session = 0;
(void) pututxline(&entryx);
}
endutxent();
(void) printf("Entering System Maintenance Mode\n\n");
}
/*
* sulogin_getinput() - hacked from the standard PAM tty conversation
* function getpassphrase() library version
* so we can distinguish newline and EOF.
* also don't need this routine to give a prompt.
*
* returns the password string, or NULL if the used typed EOF.
*/
static char *
{
int c;
void (*saved_handler)();
int i = 0;
}
if (echooff) {
}
/* get characters up to PASS_MAX, but don't overflow */
if (c == EOF && i == 0) { /* ^D, no input */
break;
}
if (i < PASS_MAX) {
input[i++] = (char)c;
}
}
input[i] = '\0';
if (echooff) {
}
if (saved_handler != SIG_ERR)
}
static char *
{
ttyn = "/dev/???";
else {
/*
*/
}
return (ttyn);
}
static char *
{
/* saw off the /dev/ */
else
return (ttyn);
}
/* ARGSUSED */
static void
{
/*
* This signal handler does nothing except return. We use it
* as the signal disposition in this program instead of
* SIG_IGN so that we do not have to restore the disposition
* back to SIG_DFL. Instead we allow exec(2) to set the
* dispostion to SIG_DFL to avoid a race condition.
*/
}
/* ARGSUSED */
static void
{
int i;
/*
* We get here if someone has successfully entered a password
* from the auxiliary console and is getting the single-user shell.
* When this happens, the parent needs to kill the children
* that didn't get the shell.
*
*/
for (i = 0; i < nchild; i++) {
}
}
/*
* The master pid will get SIGTERM or SIGHUP from init, and then
* has to make sure the shell isn't still running.
*/
/* ARGSUSED */
static void
{
int i;
/* Only need to kill the child that became the shell. */
for (i = 0; i < nchild; i++) {
/* Don't kill gramps before his time */
}
}
/* ARGSUSED */
static void
{
/* Processes come here when they fail to receive the password. */
else
/* If you're the controlling tty, then just wait */
}
exit(0);
}