/*
 * Copyright 1989 - 1994, Julianne Frances Haugh
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of Julianne F. Haugh nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <config.h>

#include "rcsid.h"
RCSID("$Id: sulogin.c,v 1.7 1998/04/16 19:57:50 marekm Exp $")

#include "prototypes.h"
#include "defines.h"
#include "getdef.h"

#if HAVE_UTMPX_H
#include <utmpx.h>
#else
#include <utmp.h>
#endif

#include <signal.h>
#include <stdio.h>
#include <pwd.h>
#include <fcntl.h>
#include "pwauth.h"

static char name[BUFSIZ];
static char pass[BUFSIZ];

static struct passwd pwent;
#if 0
#if HAVE_UTMPX_H
static struct utmpx utent;
#else
static struct utmp utent;
#endif
#endif


extern char **newenvp;
extern size_t newenvc;

extern	char	**environ;
extern char *tz P_((const char *));

#ifndef	ALARM
#define	ALARM	60
#endif

/* local function prototypes */
static RETSIGTYPE catch P_((int));
int main P_((int, char **));

static RETSIGTYPE
catch(int sig)
{
	exit(1);
}

/* syslogd is usually not running at the time when sulogin is typically
   called, cluttering the screen with unnecessary messages.  Suggested
   by Ivan Nejgebauer <ian@unsux.ns.ac.yu>.  --marekm */
#undef USE_SYSLOG

#define SULOGIN_PROMPT "\nType control-d to proceed with normal startup,\n\
(or give root password for system maintenance):"

/*ARGSUSED*/
int
main(int argc, char **argv)
{
	char	*cp;
	char	**envp = environ;
	TERMIO	termio;

#ifdef	USE_SGTTY
	ioctl (0, TIOCGETP, &termio);
	termio.sg_flags |= (ECHO|CRMOD);
	termio.sg_flags &= ~(RAW|CBREAK);
	ioctl (0, TIOCSETN, &termio);
#endif
#ifdef	USE_TERMIO
	ioctl (0, TCGETA, &termio);
	termio.c_iflag |= (ICRNL|IXON);
	termio.c_oflag |= (OPOST|ONLCR);
	termio.c_cflag |= (CREAD);
	termio.c_lflag |= (ISIG|ICANON|ECHO|ECHOE|ECHOK);
	ioctl (0, TCSETAF, &termio);
#endif
#ifdef	USE_TERMIOS
	tcgetattr (0, &termio);
	termio.c_iflag |= (ICRNL|IXON);
	termio.c_oflag |= (CREAD);
	termio.c_lflag |= (ECHO|ECHOE|ECHOK|ICANON|ISIG);
	tcsetattr (0, TCSANOW, &termio);
#endif
#ifdef	USE_SYSLOG
	openlog ("sulogin", LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH);
#endif
	initenv();
	if (argc > 1) {
		close (0);
		close (1);
		close (2);

		if (open (argv[1], O_RDWR) >= 0) {
			dup (0);
			dup (0);
		} else {
#ifdef	USE_SYSLOG
			syslog (LOG_WARN, "cannot open %s\n", argv[1]);
			closelog ();
#endif
			exit (1);
		}
	}
	if (access(PASSWD_FILE, F_OK) == -1) { /* must be a password file! */
		printf ("No password file\n");
#ifdef	USE_SYSLOG
		syslog (LOG_WARN, "No password file\n");
		closelog ();
#endif
		exit (1);
	}
#if !defined(DEBUG) && defined(SULOGIN_ONLY_INIT)
	if (getppid () != 1) {		/* parent must be INIT */
#ifdef	USE_SYSLOG
		syslog (LOG_WARN, "Pid == %d, not 1\n", getppid ());
		closelog ();
#endif
		exit (1);
	}
#endif
	if (! isatty (0) || ! isatty (1) || ! isatty (2)) {
#ifdef	USE_SYSLOG
		closelog ();
#endif
		exit (1);		/* must be a terminal */
	}
	while (*envp)			/* add inherited environment, */
		addenv(*envp++, NULL);	/* some variables change later */

	if ((cp = getdef_str("ENV_TZ")))
		addenv(*cp == '/' ? tz(cp) : cp, NULL);
	if ((cp = getdef_str("ENV_HZ")))
		addenv(cp, NULL);	/* set the default $HZ, if one */
	(void) strcpy (name, "root");	/* KLUDGE!!! */

	signal (SIGALRM, catch);	/* exit if the timer expires */
	alarm (ALARM);			/* only wait so long ... */

	while (1) {		/* repeatedly get login/password pairs */
		entry (name, &pwent);	/* get entry from password file */
		if (pwent.pw_name == (char *) 0) {

			/*
			 * Fail secure
			 */

			printf ("No password entry for 'root'\n");
#ifdef	USE_SYSLOG
			syslog (LOG_WARN, "No password entry for 'root'\n");
			closelog ();
#endif
			exit (1);
		}

	/*
	 * Here we prompt for the root password, or if no password is
	 * given we just exit.
	 */

					/* get a password for root */
		cp = getpass(SULOGIN_PROMPT);
	/*
	 * XXX - can't enter single user mode if root password is empty.
	 * I think this doesn't happen very often :-).  But it will work
	 * with standard getpass() (no NULL on EOF).  --marekm
	 */
		if (!cp || !*cp) {
#ifdef	USE_SYSLOG
			syslog (LOG_INFO, "Normal startup\n");
			closelog ();
#endif
			puts("\n");
#ifdef	TELINIT
			execl (PATH_TELINIT, "telinit", RUNLEVEL, (char *) 0);
#endif
			exit (0);
		} else {
			STRFCPY(pass, cp);
			strzero(cp);
		}
#ifdef AUTH_METHODS
		if (pwent.pw_name && pwent.pw_passwd[0] == '@') {
			if (pw_auth (pwent.pw_passwd + 1, name, PW_LOGIN, (char *) 0)) {
#ifdef	USE_SYSLOG
				syslog (LOG_WARN,
					"Incorrect root authentication");
#endif
				continue;
			}
			goto auth_done;
		}
#endif
		if (valid (pass, &pwent)) /* check encrypted passwords ... */
			break;		/* ... encrypted passwords matched */

#ifdef	USE_SYSLOG
		syslog (LOG_WARN, "Incorrect root password\n");
#endif
		sleep(2);
		puts ("Login incorrect");
	}
#ifdef AUTH_METHODS
auth_done:
#endif
	strzero(pass);
	alarm (0);
	signal (SIGALRM, SIG_DFL);
	environ = newenvp;		/* make new environment active */

	puts("Entering System Maintenance Mode\n");
#ifdef	USE_SYSLOG
	syslog (LOG_INFO, "System Maintenance Mode\n");
#endif

#if 0 /* do we need all this? we are logging in as root anyway...  --marekm */
	/*
	 * Normally there would be a utmp entry for login to mung on
	 * to get the tty name, date, etc. from.  We don't need all that
	 * stuff because we won't update the utmp or wtmp files.  BUT!,
	 * we do need the tty name so we can set the permissions and
	 * ownership.
	 */

	if ((cp = ttyname (0))) {		/* found entry in /dev/ */
		if (strncmp(cp, "/dev/", 5) == 0)
			cp += 5;

		strncpy(utent.ut_line, cp, sizeof utent.ut_line);
	}
	if (getenv ("IFS"))		/* don't export user IFS ... */
		addenv("IFS= \t\n", NULL);	/* ... instead, set a safe IFS */

	setup (&pwent, 0);		/* set UID, GID, HOME, etc ... */
#endif

#ifdef	USE_SYSLOG
	closelog ();
#endif
	shell (pwent.pw_shell, (char *) 0); /* exec the shell finally. */
	/*NOTREACHED*/
	return (0);
}
