Пример #1
0
static void chown_tty(struct login_context *cxt)
{
	const char *grname;
	uid_t uid = cxt->pwd->pw_uid;
	gid_t gid = cxt->pwd->pw_gid;

	grname = getlogindefs_str("TTYGROUP", TTYGRPNAME);
	if (grname && *grname) {
		struct group *gr = getgrnam(grname);
		if (gr)	/* group by name */
			gid = gr->gr_gid;
		else	/* group by ID */
			gid = (gid_t) getlogindefs_num("TTYGROUP", gid);
	}
	if (fchown(0, uid, gid))				/* tty */
		chown_err(cxt->tty_name, uid, gid);
	if (fchmod(0, cxt->tty_mode))
		chmod_err(cxt->tty_name, cxt->tty_mode);

#ifdef LOGIN_CHOWN_VCS
	if (is_consoletty(0)) {
		if (chown(cxt->vcsn, uid, gid))			/* vcs */
			chown_err(cxt->vcsn, uid, gid);
		if (chmod(cxt->vcsn, cxt->tty_mode))
			chmod_err(cxt->vcsn, cxt->tty_mode);

		if (chown(cxt->vcsan, uid, gid))		/* vcsa */
			chown_err(cxt->vcsan, uid, gid);
		if (chmod(cxt->vcsan, cxt->tty_mode))
			chmod_err(cxt->vcsan, cxt->tty_mode);
	}
#endif
}
static void
authenticate (const struct passwd* pw) {
    const struct passwd* lpw = NULL;
    const char* cp, *srvname = NULL;
    int retval;

    switch (su_mode) {
    case SU_MODE:
        srvname = simulate_login ? PAM_SRVNAME_SU_L : PAM_SRVNAME_SU;
        break;
    case RUNUSER_MODE:
        srvname = simulate_login ? PAM_SRVNAME_RUNUSER_L : PAM_SRVNAME_RUNUSER;
        break;
    default:
        abort();
        break;
    }

    retval = pam_start (srvname, pw->pw_name, &conv, &pamh);
    if (is_pam_failure(retval)) {
        goto done;
    }

    if (isatty (0) && (cp = ttyname (0)) != NULL) {
        const char* tty;

        if (strncmp (cp, "/dev/", 5) == 0) {
            tty = cp + 5;
        } else {
            tty = cp;
        }
        retval = pam_set_item (pamh, PAM_TTY, tty);
        if (is_pam_failure(retval)) {
            goto done;
        }
    }

    lpw = current_getpwuid ();
    if (lpw && lpw->pw_name) {
        retval = pam_set_item (pamh, PAM_RUSER, (const void*) lpw->pw_name);
        if (is_pam_failure(retval)) {
            goto done;
        }
    }

    if (su_mode == RUNUSER_MODE) {
        /*
         * This is the only difference between runuser(1) and su(1). The command
         * runuser(1) does not required authentication, because user is root.
         */
        if (restricted) {
            errx(EXIT_FAILURE, _("may not be used by non-root users"));
        }
        return;
    }

    retval = pam_authenticate (pamh, 0);
    if (is_pam_failure(retval)) {
        goto done;
    }

    retval = pam_acct_mgmt (pamh, 0);
    if (retval == PAM_NEW_AUTHTOK_REQD) {
        /* Password has expired.  Offer option to change it.  */
        retval = pam_chauthtok (pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
    }

done:

    log_syslog(pw, !is_pam_failure(retval));

    if (is_pam_failure(retval)) {
        const char* msg;

        log_btmp(pw);

        msg  = pam_strerror(pamh, retval);
        pam_end(pamh, retval);
        sleep (getlogindefs_num ("FAIL_DELAY", 1));
        errx (EXIT_FAILURE, "%s", msg ? msg : _("incorrect password"));
    }
}
Пример #3
0
static void loginpam_auth(struct login_context *cxt)
{
	int rc, show_unknown;
	unsigned int retries, failcount = 0;
	const char *hostname = cxt->hostname ? cxt->hostname :
			       cxt->tty_name ? cxt->tty_name : "<unknown>";
	pam_handle_t *pamh = cxt->pamh;

	/* if we didn't get a user on the command line, set it to NULL */
	loginpam_get_username(pamh, &cxt->username);

	show_unknown = getlogindefs_bool("LOG_UNKFAIL_ENAB", 0);
	retries = getlogindefs_num("LOGIN_RETRIES", LOGIN_MAX_TRIES);

	/*
	 * There may be better ways to deal with some of these conditions, but
	 * at least this way I don't think we'll be giving away information...
	 *
	 * Perhaps someday we can trust that all PAM modules will pay attention
	 * to failure count and get rid of LOGIN_MAX_TRIES?
	 */
	rc = pam_authenticate(pamh, 0);

	while ((++failcount < retries) &&
	       ((rc == PAM_AUTH_ERR) ||
		(rc == PAM_USER_UNKNOWN) ||
		(rc == PAM_CRED_INSUFFICIENT) ||
		(rc == PAM_AUTHINFO_UNAVAIL))) {

		if (rc == PAM_USER_UNKNOWN && !show_unknown)
			/*
			 * Logging unknown usernames may be a security issue if
			 * a user enters her password instead of her login name.
			 */
			cxt->username = NULL;
		else
			loginpam_get_username(pamh, &cxt->username);

		syslog(LOG_NOTICE,
		       _("FAILED LOGIN %u FROM %s FOR %s, %s"),
		       failcount, hostname,
		       cxt->username ? cxt->username : "******",
		       pam_strerror(pamh, rc));

		log_btmp(cxt);
		log_audit(cxt, 0);

		fprintf(stderr, _("Login incorrect\n\n"));

		pam_set_item(pamh, PAM_USER, NULL);
		rc = pam_authenticate(pamh, 0);
	}

	if (is_pam_failure(rc)) {

		if (rc == PAM_USER_UNKNOWN && !show_unknown)
			cxt->username = NULL;
		else
			loginpam_get_username(pamh, &cxt->username);

		if (rc == PAM_MAXTRIES)
			syslog(LOG_NOTICE,
			       _("TOO MANY LOGIN TRIES (%u) FROM %s FOR %s, %s"),
			       failcount, hostname,
			       cxt->username ? cxt->username : "******",
			       pam_strerror(pamh, rc));
		else
			syslog(LOG_NOTICE,
			       _("FAILED LOGIN SESSION FROM %s FOR %s, %s"),
			       hostname,
			       cxt->username ? cxt->username : "******",
			       pam_strerror(pamh, rc));

		log_btmp(cxt);
		log_audit(cxt, 0);

		fprintf(stderr, _("\nLogin incorrect\n"));
		pam_end(pamh, rc);
		sleepexit(EXIT_SUCCESS);
	}
}
Пример #4
0
/*
 * Reads the currect terminal path and initializes cxt->tty_* variables.
 */
static void init_tty(struct login_context *cxt)
{
	struct stat st;
	struct termios tt, ttt;

	cxt->tty_mode = (mode_t) getlogindefs_num("TTYPERM", TTY_MODE);

	get_terminal_name(0, &cxt->tty_path, &cxt->tty_name, &cxt->tty_number);

	/*
	 * In case login is suid it was possible to use a hardlink as stdin
	 * and exploit races for a local root exploit. (Wojciech Purczynski).
	 *
	 * More precisely, the problem is  ttyn := ttyname(0); ...; chown(ttyn);
	 * here ttyname() might return "/tmp/x", a hardlink to a pseudotty.
	 * All of this is a problem only when login is suid, which it isn't.
	 */
	if (!cxt->tty_path || !*cxt->tty_path ||
	    lstat(cxt->tty_path, &st) != 0 || !S_ISCHR(st.st_mode) ||
	    (st.st_nlink > 1 && strncmp(cxt->tty_path, "/dev/", 5)) ||
	    access(cxt->tty_path, R_OK | W_OK) != 0) {

		syslog(LOG_ERR, _("FATAL: bad tty"));
		sleepexit(EXIT_FAILURE);
	}

#ifdef LOGIN_CHOWN_VCS
	if (cxt->tty_number) {
		/* find names of Virtual Console devices, for later mode change */
		snprintf(cxt->vcsn, sizeof(cxt->vcsn), "/dev/vcs%s", cxt->tty_number);
		snprintf(cxt->vcsan, sizeof(cxt->vcsan), "/dev/vcsa%s", cxt->tty_number);
	}
#endif

	tcgetattr(0, &tt);
	ttt = tt;
	ttt.c_cflag &= ~HUPCL;

	if ((fchown(0, 0, 0) || fchmod(0, cxt->tty_mode)) && errno != EROFS) {

		syslog(LOG_ERR, _("FATAL: %s: change permissions failed: %m"),
				cxt->tty_path);
		sleepexit(EXIT_FAILURE);
	}

	/* Kill processes left on this tty */
	tcsetattr(0, TCSANOW, &ttt);

	/*
	 * Let's close file decriptors before vhangup
	 * https://lkml.org/lkml/2012/6/5/145
	 */
	close(STDIN_FILENO);
	close(STDOUT_FILENO);
	close(STDERR_FILENO);

	signal(SIGHUP, SIG_IGN);	/* so vhangup() wont kill us */
	vhangup();
	signal(SIGHUP, SIG_DFL);

	/* open stdin,stdout,stderr to the tty */
	open_tty(cxt->tty_path);

	/* restore tty modes */
	tcsetattr(0, TCSAFLUSH, &tt);
}
Пример #5
0
/*
 * Let us delay all exit() calls when the user is not authenticated
 * or the session not fully initialized (loginpam_session()).
 */
static void __attribute__ ((__noreturn__)) sleepexit(int eval)
{
	sleep((unsigned int)getlogindefs_num("FAIL_DELAY", LOGIN_EXIT_TIMEOUT));
	exit(eval);
}
Пример #6
0
	char *pwdbuf = NULL;
	struct passwd *pwd = NULL, _pwd;

	struct login_context cxt = {
		.tty_mode = TTY_MODE,		  /* tty chmod() */
		.pid = getpid(),		  /* PID */
#ifdef HAVE_SECURITY_PAM_MISC_H
		.conv = { misc_conv, NULL }	  /* Linux-PAM conversation function */
#elif defined(HAVE_SECURITY_OPENPAM_H)
		.conv = { openpam_ttyconv, NULL } /* OpenPAM conversation function */
#endif

	};

	timeout = (unsigned int)getlogindefs_num("LOGIN_TIMEOUT", LOGIN_TIMEOUT);

	signal(SIGALRM, timedout);
	(void) sigaction(SIGALRM, NULL, &act);
	act.sa_flags &= ~SA_RESTART;
	sigaction(SIGALRM, &act, NULL);
	alarm(timeout);
	signal(SIGQUIT, SIG_IGN);
	signal(SIGINT, SIG_IGN);

	setlocale(LC_ALL, "");
	bindtextdomain(PACKAGE, LOCALEDIR);
	textdomain(PACKAGE);

	setpriority(PRIO_PROCESS, 0, 0);
	initproctitle(argc, argv);
Пример #7
0
int main(int argc, char **argv)
{
	int c;
	int cnt;
	char *childArgv[10];
	char *buff;
	int childArgc = 0;
	int retcode;

	char *pwdbuf = NULL;
	struct passwd *pwd = NULL, _pwd;

	struct login_context cxt = {
		.tty_mode = TTY_MODE,		/* tty chmod() */
		.pid = getpid(),		/* PID */
		.conv = { misc_conv, NULL }	/* PAM conversation function */
	};

	timeout = getlogindefs_num("LOGIN_TIMEOUT", LOGIN_TIMEOUT);

	signal(SIGALRM, timedout);
	siginterrupt(SIGALRM, 1);	/* we have to interrupt syscalls like ioclt() */
	alarm((unsigned int)timeout);
	signal(SIGQUIT, SIG_IGN);
	signal(SIGINT, SIG_IGN);

	setlocale(LC_ALL, "");
	bindtextdomain(PACKAGE, LOCALEDIR);
	textdomain(PACKAGE);

	setpriority(PRIO_PROCESS, 0, 0);
	initproctitle(argc, argv);

	/*
	 * -p is used by getty to tell login not to destroy the environment
	 * -f is used to skip a second login authentication
	 * -h is used by other servers to pass the name of the remote
	 *    host to login so that it may be placed in utmp and wtmp
	 */
	while ((c = getopt(argc, argv, "fHh:pV")) != -1)
		switch (c) {
		case 'f':
			cxt.noauth = 1;
			break;

		case 'H':
			cxt.nohost = 1;
			break;

		case 'h':
			if (getuid()) {
				fprintf(stderr,
					_("login: -h for super-user only.\n"));
				exit(EXIT_FAILURE);
			}
			init_remote_info(&cxt, optarg);
			break;

		case 'p':
			cxt.keep_env = 1;
			break;

		case 'V':
			printf(UTIL_LINUX_VERSION);
			return EXIT_SUCCESS;
		case '?':
		default:
			fprintf(stderr, _("usage: login [ -p ] [ -h host ] [ -H ] [ -f username | username ]\n"));
			exit(EXIT_FAILURE);
		}
	argc -= optind;
	argv += optind;

	if (*argv) {
		char *p = *argv;
		cxt.username = xstrdup(p);

		/* wipe name - some people mistype their password here */
		/* (of course we are too late, but perhaps this helps a little ..) */
		while (*p)
			*p++ = ' ';
	}

	for (cnt = get_fd_tabsize() - 1; cnt > 2; cnt--)
		close(cnt);

	setpgrp();	 /* set pgid to pid this means that setsid() will fail */

	openlog("login", LOG_ODELAY, LOG_AUTHPRIV);

	init_tty(&cxt);
	init_loginpam(&cxt);

	/* login -f, then the user has already been authenticated */
	cxt.noauth = cxt.noauth && getuid() == 0 ? 1 : 0;

	if (!cxt.noauth)
		loginpam_auth(&cxt);

	/*
	 * Authentication may be skipped (for example, during krlogin, rlogin,
	 * etc...), but it doesn't mean that we can skip other account checks.
	 * The account could be disabled or password expired (althought
	 * kerberos ticket is valid).         -- [email protected] (22-Feb-2006)
	 */
	loginpam_acct(&cxt);

	if (!(cxt.pwd = get_passwd_entry(cxt.username, &pwdbuf, &_pwd))) {
		warnx(_("\nSession setup problem, abort."));
		syslog(LOG_ERR, _("Invalid user name \"%s\" in %s:%d. Abort."),
		       cxt.username, __FUNCTION__, __LINE__);
		pam_end(cxt.pamh, PAM_SYSTEM_ERR);
		sleepexit(EXIT_FAILURE);
	}

	pwd = cxt.pwd;
	cxt.username = pwd->pw_name;

	/*
	 * Initialize the supplementary group list. This should be done before
	 * pam_setcred because the PAM modules might add groups during
	 * pam_setcred.
	 *
         * For root we don't call initgroups, instead we call setgroups with
	 * group 0. This avoids the need to step through the whole group file,
	 * which can cause problems if NIS, NIS+, LDAP or something similar
	 * is used and the machine has network problems.
	 */
	retcode = pwd->pw_uid ? initgroups(cxt.username, pwd->pw_gid) :	/* user */
			        setgroups(0, NULL);			/* root */
	if (retcode < 0) {
		syslog(LOG_ERR, _("groups initialization failed: %m"));
		warnx(_("\nSession setup problem, abort."));
		pam_end(cxt.pamh, PAM_SYSTEM_ERR);
		sleepexit(EXIT_FAILURE);
	}

	/*
	 * Open PAM session (after successful authentication and account check)
	 */
	loginpam_session(&cxt);

	/* committed to login -- turn off timeout */
	alarm((unsigned int)0);

	endpwent();

	cxt.quiet = get_hushlogin_status(pwd);

	log_utmp(&cxt);
	log_audit(&cxt, 1);
	log_lastlog(&cxt);

	chown_tty(&cxt);

	if (setgid(pwd->pw_gid) < 0 && pwd->pw_gid) {
		syslog(LOG_ALERT, _("setgid() failed"));
		exit(EXIT_FAILURE);
	}

	if (pwd->pw_shell == NULL || *pwd->pw_shell == '\0')
		pwd->pw_shell = _PATH_BSHELL;

	init_environ(&cxt);		/* init $HOME, $TERM ... */

	setproctitle("login", cxt.username);

	log_syslog(&cxt);

	if (!cxt.quiet) {
		motd();

#ifdef LOGIN_STAT_MAIL
		/*
		 * This turns out to be a bad idea: when the mail spool
		 * is NFS mounted, and the NFS connection hangs, the
		 * login hangs, even root cannot login.
		 * Checking for mail should be done from the shell.
		 */
		{
			struct stat st;
			char *mail;

			mail = getenv("MAIL");
			if (mail && stat(mail, &st) == 0 && st.st_size != 0) {
				if (st.st_mtime > st.st_atime)
					printf(_("You have new mail.\n"));
				else
					printf(_("You have mail.\n"));
			}
		}
#endif
	}

	/*
	 * Detach the controlling terminal, fork() and create, new session
	 * and reinilizalize syslog stuff.
	 */
	fork_session(&cxt);

	/* discard permissions last so can't get killed and drop core */
	if (setuid(pwd->pw_uid) < 0 && pwd->pw_uid) {
		syslog(LOG_ALERT, _("setuid() failed"));
		exit(EXIT_FAILURE);
	}

	/* wait until here to change directory! */
	if (chdir(pwd->pw_dir) < 0) {
		warn(_("%s: change directory failed"), pwd->pw_dir);

		if (!getlogindefs_bool("DEFAULT_HOME", 1))
			exit(0);
		if (chdir("/"))
			exit(EXIT_FAILURE);
		pwd->pw_dir = "/";
		printf(_("Logging in with home = \"/\".\n"));
	}

	/* if the shell field has a space: treat it like a shell script */
	if (strchr(pwd->pw_shell, ' ')) {
		buff = xmalloc(strlen(pwd->pw_shell) + 6);

		strcpy(buff, "exec ");
		strcat(buff, pwd->pw_shell);
		childArgv[childArgc++] = "/bin/sh";
		childArgv[childArgc++] = "-sh";
		childArgv[childArgc++] = "-c";
		childArgv[childArgc++] = buff;
	} else {
		char tbuf[PATH_MAX + 2], *p;

		tbuf[0] = '-';
		xstrncpy(tbuf + 1, ((p = strrchr(pwd->pw_shell, '/')) ?
				    p + 1 : pwd->pw_shell), sizeof(tbuf) - 1);

		childArgv[childArgc++] = pwd->pw_shell;
		childArgv[childArgc++] = xstrdup(tbuf);
	}

	childArgv[childArgc++] = NULL;

	execvp(childArgv[0], childArgv + 1);

	if (!strcmp(childArgv[0], "/bin/sh"))
		warn(_("couldn't exec shell script"));
	else
		warn(_("no shell"));

	exit(EXIT_SUCCESS);
}
Пример #8
0
static void log_lastlog(struct login_context *cxt)
{
	struct sigaction sa, oldsa_xfsz;
	struct lastlog ll;
	time_t t;
	int fd;

	if (!cxt->pwd)
		return;

	if (cxt->pwd->pw_uid > (uid_t) getlogindefs_num("LASTLOG_UID_MAX", ULONG_MAX))
		return;

	/* lastlog is huge on systems with large UIDs, ignore SIGXFSZ */
	memset(&sa, 0, sizeof(sa));
	sa.sa_handler = SIG_IGN;
	sigaction(SIGXFSZ, &sa, &oldsa_xfsz);

	fd = open(_PATH_LASTLOG, O_RDWR, 0);
	if (fd < 0)
		goto done;

	if (lseek(fd, (off_t) cxt->pwd->pw_uid * sizeof(ll), SEEK_SET) == -1)
		goto done;

	/*
	 * Print last log message.
	 */
	if (!cxt->quiet) {
		if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) &&
							ll.ll_time != 0) {
			time_t ll_time = (time_t) ll.ll_time;

			printf(_("Last login: %.*s "), 24 - 5, ctime(&ll_time));
			if (*ll.ll_host != '\0')
				printf(_("from %.*s\n"),
				       (int)sizeof(ll.ll_host), ll.ll_host);
			else
				printf(_("on %.*s\n"),
				       (int)sizeof(ll.ll_line), ll.ll_line);
		}
		if (lseek(fd, (off_t) cxt->pwd->pw_uid * sizeof(ll), SEEK_SET) == -1)
			goto done;
	}

	memset((char *)&ll, 0, sizeof(ll));

	time(&t);
	ll.ll_time = t;		/* ll_time is always 32bit */

	if (cxt->tty_name)
		str2memcpy(ll.ll_line, cxt->tty_name, sizeof(ll.ll_line));
	if (cxt->hostname)
		str2memcpy(ll.ll_host, cxt->hostname, sizeof(ll.ll_host));

	if (write_all(fd, (char *)&ll, sizeof(ll)))
		warn(_("write lastlog failed"));
done:
	if (fd >= 0)
		close(fd);

	sigaction(SIGXFSZ, &oldsa_xfsz, NULL);		/* restore original setting */
}