OpenSolaris_b135/cmd/rexd/unix_login.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (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
 * or http://www.opensolaris.org/os/licensing.
 * 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 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#define	BSD_COMP
#include <errno.h>
#include <fcntl.h>
#include <pwd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <sac.h>		/* for SC_WILDC */
#include <utmpx.h>

#include <rpc/rpc.h>
#include <sys/file.h>
#include <sys/filio.h>
#include <sys/ioctl.h>
#include <sys/signal.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>

/*
 * # include <sys/label.h>
 * # include <sys/audit.h>
 *
 *
 *
 * # include <pwdadj.h>
 */

#include <sys/ttold.h>
#include <stropts.h>
#include <sys/stream.h>



#include "rex.h"

#include <security/pam_appl.h>
pam_handle_t *pamh;

#define	NTTYDISC	2	/* New ttydiscipline: stolen from ttold.h */

/*
 * unix_login - hairy junk to simulate logins for Unix
 */

int	Master,	Slave;			/* sides of the pty */
int	Slave_is_closed_on_master_side;

static char	*slavename;
extern char *ptsname();


int	InputSocket,			/* Network sockets */
	OutputSocket;
int	Helper1,			/* pids of the helpers */
	Helper2;
char	UserName[256];			/* saves the user name for loging */
char	HostName[256];			/* saves the host name for loging */

static	int	TtySlot;		/* slot number in Utmpx */

/*
 * pseudo-xprts used to add pty fds to svc_pollfd[]. This allows the
 * polling for all i/o in one poll().
 */
SVCXPRT uxprt[2];

#define	INPUTSOCKET	0		/* InputSocket xprt */
#define	MASTER		1		/* Master xprt */


extern	int child;		/* pid of the executed process */
extern	int ChildDied;		/* flag */
extern	int HasHelper;		/* flag */

extern	void setproctitle(char *user, char *host);
extern int Debug;

extern void audit_rexd_fail(char *, char *, char *, uid_t, gid_t,
				char *, char **);

#define	bzero(s, n)	memset((s), 0, (n))
#define	bcopy(a, b, c)	memcpy((b), (a), (c))

static void LogoutUser(void);

/*
 * Check for user being able to run on this machine.
 * returns 0 if OK, TRUE if problem, error message in "error"
 * copies name of shell and home directory if user is valid.
 */
int
ValidUser(host, uid, gid, error, shell, dir, rst)
	char *host;		/* passed in */
	uid_t uid;
	gid_t gid;
	char *error;		/* filled in on return */
	char *shell;		/* filled in on return */
	char *dir;		/* filled in on return */
	struct rex_start *rst;	/* passed in */
{
	struct passwd *pw, *getpwuid();
	int v;

	pw = getpwuid(uid);
	if (pw == NULL || pw->pw_name == NULL)
	{
		errprintf(error, "rexd: User id %d not valid\n", uid);
		audit_rexd_fail("user id is not valid",
				host,
				NULL,
				uid,
				gid,
				NULL,
				rst->rst_cmd);	    /* BSM */
		return (1);
	}
	strncpy(UserName, pw->pw_name, sizeof (UserName) - 1);
	strncpy(HostName, host, sizeof (HostName) - 1);
	strcpy(shell, pw->pw_shell);
	strcpy(dir, pw->pw_dir);
	setproctitle(pw->pw_name, host);

	if (pam_start("rexd", pw->pw_name, NULL, &pamh) != PAM_SUCCESS ||
	    pam_set_item(pamh, PAM_RHOST, host) != PAM_SUCCESS) {
		audit_rexd_fail("user id is not valid",
				host,
				pw->pw_name,
				uid,
				gid,
				shell,
				rst->rst_cmd);	    /* BSM */
		errprintf(error, "rexd: User id %d not valid\n", uid);
		if (pamh) {
			pam_end(pamh, PAM_ABORT);
			pamh = NULL;
		}
		return (1);
	}

	if ((v = pam_acct_mgmt(pamh, 0)) != PAM_SUCCESS) {
		switch (v) {
		case PAM_NEW_AUTHTOK_REQD:
			errprintf(error,
				"rexd: User id %d Password Expired\n", uid);
			break;
		case PAM_PERM_DENIED:
			errprintf(error,
				"rexd: User id %d Account Expired\n", uid);
			break;
		case PAM_AUTHTOK_EXPIRED:
			errprintf(error,
				"rexd: User id %d Password Expired\n", uid);
			break;
		default:
			errprintf(error,
				"rexd: User id %d not valid\n", uid);
			break;
		}
		pam_end(pamh, PAM_ABORT);
		pamh = NULL;

		audit_rexd_fail("user account expired",
				host,
				pw->pw_name,
				uid,
				gid,
				shell,
				rst->rst_cmd);	    /* BSM */
		return (1);
	}

	return (0);
}

/*
 * Add an audit record with argv that was pre-set, plus the given string
 */

/*
 * Allocate a pseudo-terminal
 * sets the global variables Master and Slave.
 * returns 1 on error, 0 if OK
 */
int
AllocatePty(socket0, socket1)
	int socket0, socket1;
{

	int on = 1;

	sigset(SIGHUP, SIG_IGN);
	sigset(SIGTTOU, SIG_IGN);
	sigset(SIGTTIN, SIG_IGN);

	if ((Master = open("/dev/ptmx", O_RDWR)) == -1) {
	    if (Debug)
		    printf("open-ptmx-failure\n");
	    perror("AloocatePtyMaster fails");
	    return (1);		/* error could not open /dev/ptmx */
	}
	if (Debug)
	    printf("open-ptmx success Master =%d\n", Master);
	if (Debug)
	    printf("Before grantpt...Master=%d\n", Master);

	if (grantpt(Master) == -1) {
	    perror("could not grant slave pty");
	    exit(1);
	}
	if (unlockpt(Master) == -1) {
	    perror("could not unlock slave pty");
	    exit(1);
	}
	if ((slavename = ptsname(Master)) == NULL) {
	    perror("could not enable slave pty");
	    exit(1);
	}
	if ((Slave = open(slavename, O_RDWR)) == -1) {
	    perror("could not open slave pty");
	    exit(1);
	}
	if (ioctl(Slave, I_PUSH, "ptem") == -1) {
	    perror("ioctl I_PUSH ptem");
	    exit(1);
	}
	if (ioctl(Slave, I_PUSH, "ldterm") == -1) {
	    perror("ioctl I_PUSH ldterm");
	    exit(1);
	}
	if (ioctl(Slave, I_PUSH, "ttcompat") == -1) {
	    perror("ioctl I_PUSH ttcompat");
	    exit(1);
	}

	Slave_is_closed_on_master_side = FALSE;
	setsid(); /* get rid of controlling terminal */
	/* LoginUser(); */

	InputSocket = socket0;
	OutputSocket = socket1;
	ioctl(Master, FIONBIO, &on);
	uxprt[INPUTSOCKET].xp_fd = InputSocket;
	xprt_register(&uxprt[INPUTSOCKET]);
	uxprt[MASTER].xp_fd = Master;
	xprt_register(&uxprt[MASTER]);
	return (0);

}

void
OpenPtySlave()
{
	close(Slave);
	Slave = open(slavename, O_RDWR);
	if (Slave < 0) {
		perror(slavename);
		exit(1);
	}
}



	/*
	 * Special processing for interactive operation.
	 * Given pointers to three standard file descriptors,
	 * which get set to point to the pty.
	 */
void
DoHelper(pfd0, pfd1, pfd2)
	int *pfd0, *pfd1, *pfd2;
{
	int pgrp;


	sigset(SIGINT, SIG_IGN);
	close(Master);
	close(InputSocket);
	close(OutputSocket);

	*pfd0 = Slave;
	*pfd1 = Slave;
	*pfd2 = Slave;
}


/*
 * destroy the helpers when the executing process dies
 */
void
KillHelper(int grp)
{
	if (Debug)
		printf("Enter KillHelper\n");
	close(Master);
	xprt_unregister(&uxprt[MASTER]);
	close(InputSocket);
	xprt_unregister(&uxprt[INPUTSOCKET]);
	close(OutputSocket);
	LogoutUser();

	if (grp)
	    kill((-grp), SIGKILL);
}


/*
 * edit the Unix traditional data files that tell who is logged
 * into "the system"
 */
unsigned char	utid[] = {'o', 'n', SC_WILDC, SC_WILDC};

void
LoginUser(void)
{

	char *user;
	char *rhost;
	/* the next 4 variables are needed for utmpx mgmt */
	int		tmplen;
	struct utmpx	*u = NULL;
	struct utmpx	set_utmp;
	char		*ttyntail;

	/* We're pretty drastic here, exiting if an error is detected */
	if (pam_set_item(pamh, PAM_TTY, slavename)	!= PAM_SUCCESS ||
	    pam_get_item(pamh, PAM_USER, (void **) &user) != PAM_SUCCESS ||
	    pam_get_item(pamh, PAM_RHOST, (void **) &rhost) != PAM_SUCCESS ||
	    pam_open_session(pamh, 0)			!= PAM_SUCCESS) {
		/*
		 * XXX should print something but for now we exit
		 */
		exit(1);
	}

	(void) memset((void *)&set_utmp, 0, sizeof (set_utmp));
	(void) time(&set_utmp.ut_tv.tv_sec);
	set_utmp.ut_pid = getpid();
	if (rhost != NULL && rhost[0] != '\0') {
		(void) strcpy(set_utmp.ut_host, rhost);
		tmplen = strlen(rhost) + 1;
		if (tmplen < sizeof (set_utmp.ut_host))
			set_utmp.ut_syslen = tmplen;
		else
			set_utmp.ut_syslen = sizeof (set_utmp.ut_host);
	} else {
		(void) memset(set_utmp.ut_host, 0, sizeof (set_utmp.ut_host));
		set_utmp.ut_syslen = 0;
	}
	(void) strcpy(set_utmp.ut_user, user);

	/*
	 * Copy in the name of the tty minus the "/dev/" if a /dev/ is
	 * in the path name.
	 */
	ttyntail = slavename;
	if (strstr(ttyntail, "/dev/") != 0)
		ttyntail = ttyntail + strlen("/dev/");
	(void) strcpy(set_utmp.ut_line, ttyntail);

	set_utmp.ut_type = USER_PROCESS;
	if (utid != NULL)
		(void) memcpy(set_utmp.ut_id, utid, sizeof (set_utmp.ut_id));
	/*
	 * Go through each entry one by one, looking only at INIT,
	 * LOGIN or USER Processes.  Use the entry found if flags == 0
	 * and the line name matches, or if the process ID matches if
	 * the UPDATE_ENTRY flag is set.  The UPDATE_ENTRY flag is mainly
	 * for login which normally only wants to update an entry if
	 * the pid fields matches.
	 */

	if (u == (struct utmpx *)NULL) {
		(void) makeutx(&set_utmp);
	} else
		updwtmpx(WTMPX_FILE, &set_utmp);

}

/*
 * edit the Unix traditional data files that tell who is logged
 * into "the system".
 */
static void
LogoutUser(void)
{
	struct utmpx *up;
	struct utmpx ut;
	int pid;
	char user[sizeof (ut.ut_user) + 1];
	char ttyn[sizeof (ut.ut_line) + 1];
	char rhost[sizeof (ut.ut_host) + 1];

	sighold(SIGCHLD);		/* no disruption during cleanup */

	if (pamh) {
		pam_end(pamh, PAM_SUCCESS);
		pamh = NULL;
	}

	/* BEGIN RESET UTMP */
	pid = child;
	setutxent();
	while (up = getutxent()) {
		if (up->ut_pid == pid) {
			if (up->ut_type == DEAD_PROCESS) {
				/*
				 * Cleaned up elsewhere.
				 */
				break;
			}

			strncpy(user, up->ut_user, sizeof (up->ut_user));
			user[sizeof (up->ut_user)] = '\0';
			strncpy(ttyn, up->ut_line, sizeof (up->ut_line));
			ttyn[sizeof (up->ut_line)] = '\0';
			strncpy(rhost, up->ut_host, sizeof (up->ut_host));
			rhost[sizeof (up->ut_host)] = '\0';

			if ((pam_start("rexd", user, NULL, &pamh))
							== PAM_SUCCESS) {
				(void) pam_set_item(pamh, PAM_TTY, ttyn);
				(void) pam_set_item(pamh, PAM_RHOST, rhost);
				(void) pam_close_session(pamh, 0);
				(void) pam_end(pamh, PAM_SUCCESS);
				pamh = NULL;
			}

			up->ut_type = DEAD_PROCESS;
			up->ut_exit.e_termination = WTERMSIG(0);
			up->ut_exit.e_exit = WEXITSTATUS(0);
			(void) time(&up->ut_tv.tv_sec);
			if (modutx(up) == NULL) {
				/*
				 * Since modutx failed we'll
				 * write out the new entry
				 * ourselves.
				 */
				(void) pututxline(up);
				updwtmpx("wtmpx", up);
			}
			break;
		}
	}
	endutxent();
	/* END RESET UTMP */
	sigrelse(SIGCHLD);
}

/*
 * set the pty modes to the given values
 */
void
SetPtyMode(mode)
	struct rex_ttymode *mode;
{
	struct sgttyb svr4_sgttyb_var;
	int ldisc = NTTYDISC;

	if (Debug)
		printf("Enter SetPtyMode\n");
	if (Debug)
		printf("SetPtyMode:opened slave\n");
	ioctl(Slave, TIOCSETD, &ldisc);
	if (Debug)
		printf("SetPtyMode:Slave TIOCSETD done\n");

	/*
	 * Copy from over-the-net(bsd) to SVR4 format
	 */
	svr4_sgttyb_var.sg_ispeed = mode->basic.sg_ispeed;
	svr4_sgttyb_var.sg_ospeed = mode->basic.sg_ospeed;
	svr4_sgttyb_var.sg_erase  = mode->basic.sg_erase;
	svr4_sgttyb_var.sg_kill = mode->basic.sg_kill;
	svr4_sgttyb_var.sg_flags = (int)mode->basic.sg_flags;
	/*
	 * Clear any possible sign extension caused by (int)
	 * typecast
	 */
	svr4_sgttyb_var.sg_flags &= 0xFFFF;

	ioctl(Slave, TIOCSETN, &svr4_sgttyb_var);
	if (Debug)
		printf("SetPtyMode:Slave TIOCSETN done\n");
	ioctl(Slave, TIOCSETC, &mode->more);
	if (Debug)
		printf("SetPtyMode:Slave TIOCSETC done\n");
	ioctl(Slave, TIOCSLTC, &mode->yetmore);
	if (Debug)
		printf("SetPtyMode:Slave TIOCSLTC done\n");
	ioctl(Slave, TIOCLSET, &mode->andmore);
	if (Debug)
		printf("SetPtyMode:Slave TIOCSET done\n");

	/* Opened in AllocPty for parent, still open in child */
	if (Slave_is_closed_on_master_side == FALSE) {
		close(Slave);
		Slave_is_closed_on_master_side = TRUE;
	}
}

/*
 * set the pty window size to the given value
 */
void
SetPtySize(struct rex_ttysize *sizep)
{
	struct winsize newsize;

	/* if size has changed, this ioctl changes it */
	/* *and* sends SIGWINCH to process group */

	newsize.ws_row = (unsigned short) sizep->ts_lines;
	newsize.ws_col = (unsigned short) sizep->ts_cols;

	(void) ioctl(Master, TIOCSWINSZ, &newsize);
	if (Slave_is_closed_on_master_side == FALSE) {
		close(Slave);
		Slave_is_closed_on_master_side = TRUE;
	}
}


/*
 * send the given signal to the group controlling the terminal
 */
void
SendSignal(int sig)
{
	pid_t pgrp;

	pgrp = getpgid(child);
	if (pgrp != (pid_t)-1)
		(void) kill((-pgrp), sig);
}

/*
 * called when the main select loop detects that we might want to
 * read something.
 */
void
HelperRead(pollfd_t *fdp, int nfds, int *pollretval)
{
	char buf[128];
	int retval;
	extern int errno;
	int mask;
	int master = -1;
	int inputsocket = -1;

	/*
	 * fdp pollset may be compressed. Search for Master and
	 * InputSocket fds.
	 */
	int i;
	for (i = 0; i < nfds; i++) {
		if (fdp[i].fd == Master && fdp[i].revents != 0)
			master = i;
		if (fdp[i].fd == InputSocket && fdp[i].revents != 0)
			inputsocket = i;
	}

/*	mask = sigsetmask (sigmask (SIGCHLD));	*/
	mask = sighold(SIGCHLD);
	retval = 0;
	if (master != -1) {
		if (!(fdp[master].revents & (POLLERR | POLLHUP | POLLNVAL))) {
			retval = read(Master, buf, sizeof (buf));
			if (retval > 0) {
				(void) write(OutputSocket, buf, retval);
			} else {
				if (errno != EINTR && errno != EIO &&
				    errno != EWOULDBLOCK)
					perror("pty read");
				/* 1 => further sends disallowed */
				shutdown(OutputSocket, 1);
				xprt_unregister(&uxprt[MASTER]);
			}
		}

		/* clear this event for svc_getreq_poll */
		fdp[master].revents = 0;
		*pollretval = *pollretval - 1;

		if (retval <= 0 && ChildDied) {
			KillHelper(child);
			HasHelper = 0;
			if (inputsocket != -1) {
				fdp[inputsocket].revents = 0;
				*pollretval = *pollretval - 1;
			}
			goto done;
		}
	}

	if (inputsocket != -1) {
		if (!(fdp[inputsocket].revents & (POLLERR | POLLHUP |
							    POLLNVAL))) {
			retval = read(InputSocket, buf, sizeof (buf));
			if (retval > 0) {
				(void) write(Master, buf, retval);
			} else {
				if (errno != EINTR && errno != EWOULDBLOCK)
					perror("socket read");
				xprt_unregister(&uxprt[INPUTSOCKET]);
			}
		}

		/* clear this event for svc_getreq_poll */
		fdp[inputsocket].revents = 0;
		*pollretval = *pollretval - 1;
	}

	done:
/*	sigsetmask (mask);	*/
	sigrelse(SIGCHLD);
}