Example #1
0
void
setclasscpumask(login_cap_t *lc)
{
	const char *maskstr;
	cpuset_t maskset;
	cpusetid_t setid;

	maskstr = login_getcapstr(lc, "cpumask", NULL, NULL);
	CPU_ZERO(&maskset);
	if (maskstr == NULL)
		return;
	if (strcasecmp("default", maskstr) == 0)
		return;
	if (!list2cpuset(maskstr, &maskset)) {
		syslog(LOG_WARNING,
		    "list2cpuset(%s) invalid mask specification", maskstr);
		return;
	}

	if (cpuset(&setid) != 0) {
		syslog(LOG_ERR, "cpuset(): %s", strerror(errno));
		return;
	}

	if (cpuset_setaffinity(CPU_LEVEL_CPUSET, CPU_WHICH_PID, -1,
	    sizeof(maskset), &maskset) != 0)
		syslog(LOG_ERR, "cpuset_setaffinity(%s): %s", maskstr,
		    strerror(errno));
}
Example #2
0
/*
 * Select the system's default password hashing scheme and generate
 * a stable fake salt under it for use by a non-existent account.
 * Prevents jpake method being used to infer the validity of accounts.
 */
static void
fake_salt_and_scheme(Authctxt *authctxt, char **salt, char **scheme)
{
	char *rounds_s, *style;
	long long rounds;
	login_cap_t *lc;


	if ((lc = login_getclass(authctxt->pw->pw_class)) == NULL &&
	    (lc = login_getclass(NULL)) == NULL)
		fatal("%s: login_getclass failed", __func__);
	style = login_getcapstr(lc, "localcipher", NULL, NULL);
	if (style == NULL)
		style = xstrdup("blowfish,6");
	login_close(lc);
	
	if ((rounds_s = strchr(style, ',')) != NULL)
		*rounds_s++ = '\0';
	rounds = strtonum(rounds_s, 1, 1<<31, NULL);
	
	if (strcmp(style, "md5") == 0) {
		xasprintf(salt, "$1$%s$", makesalt(8, authctxt->user));
		*scheme = xstrdup("md5");
	} else if (strcmp(style, "old") == 0) {
		*salt = xstrdup(makesalt(2, authctxt->user));
		*scheme = xstrdup("crypt");
	} else if (strcmp(style, "newsalt") == 0) {
		rounds = MAX(rounds, 7250);
		rounds = MIN(rounds, (1<<24) - 1);
		xasprintf(salt, "_%c%c%c%c%s",
		    pw_encode64(rounds), pw_encode64(rounds >> 6),
		    pw_encode64(rounds >> 12), pw_encode64(rounds >> 18),
		    makesalt(4, authctxt->user));
		*scheme = xstrdup("crypt-extended");
	} else {
Example #3
0
void
motd(void)
{
	char tbuf[8192], *motd;
	int fd, nchars;
	struct sigaction sa, osa;

	motd = login_getcapstr(lc, "welcome", _PATH_MOTDFILE, _PATH_MOTDFILE);

	if ((fd = open(motd, O_RDONLY, 0)) < 0)
		return;

	memset(&sa, 0, sizeof(sa));
	sa.sa_handler = sigint;
	sigemptyset(&sa.sa_mask);
	sa.sa_flags = 0;		/* don't set SA_RESTART */
	(void)sigaction(SIGINT, &sa, &osa);

	/* read and spew motd until EOF, error, or SIGINT */
	while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0 &&
	    write(STDOUT_FILENO, tbuf, nchars) == nchars)
		;

	(void)sigaction(SIGINT, &osa, NULL);
	(void)close(fd);
}
Example #4
0
char *
stypeof(char *ttyid)
{
	struct ttyent *t;

	return (ttyid && (t = getttynam(ttyid)) ? t->ty_type :
	    login_getcapstr(lc, "term", UNKNOWN, UNKNOWN));
}
Example #5
0
void
setclassenvironment(login_cap_t *lc, const struct passwd * pwd, int paths)
{
    struct login_vars	*vars = paths ? pathvars : envars;
    int			hlen = pwd ? strlen(pwd->pw_dir) : 0;
    int			nlen = pwd ? strlen(pwd->pw_name) : 0;
    char pch = 0;

    if (hlen && pwd->pw_dir[hlen-1] != '/')
	++pch;

    while (vars->tag != NULL) {
	const char * var = paths ? login_getpath(lc, vars->tag, NULL)
				 : login_getcapstr(lc, vars->tag, NULL, NULL);

	char * np  = substvar(var, pwd, hlen, pch, nlen);

	if (np != NULL) {
	    setenv(vars->var, np, vars->overwrite);
	    free(np);
	} else if (vars->def != NULL) {
	    setenv(vars->var, vars->def, 0);
	}
	++vars;
    }

    /*
     * If we're not processing paths, then see if there is a setenv list by
     * which the admin and/or user may set an arbitrary set of env vars.
     */
    if (!paths) {
	const char	**set_env = login_getcaplist(lc, "setenv", ",");

	if (set_env != NULL) {
	    while (*set_env != NULL) {
		char	*p = strchr(*set_env, '=');

		if (p != NULL) {  /* Discard invalid entries */
		    char	*np;

		    *p++ = '\0';
		    if ((np = substvar(p, pwd, hlen, pch, nlen)) != NULL) {
			setenv(*set_env, np, 1);
			free(np);
		    }
		}
		++set_env;
	    }
	}
    }
}
Example #6
0
const char *
login_setcryptfmt(login_cap_t *lc, const char *def, const char *error) {
	const char *cipher;

	cipher = login_getcapstr(lc, "passwd_format", def, NULL);
	if (getenv("CRYPT_DEBUG") != NULL)
		fprintf(stderr, "login_setcryptfmt: "
		    "passwd_format = %s\n", cipher);
	if (cipher == NULL)
		return (error);
	if (!crypt_set_format(cipher))
		return (error);
	return (cipher);
}
Example #7
0
void
auth_checknologin(login_cap_t *lc)
{
  const char *file;

  /* Do we ignore a nologin file? */
  if (login_getcapbool(lc, "ignorenologin", 0))
    return;

  /* Note that <file> will be "" if there is no nologin capability */
  if ((file = login_getcapstr(lc, "nologin", "", NULL)) == NULL)
    exit(1);

  /*
   * *file is true IFF there was a "nologin" capability
   * Note that auth_cat() returns 1 only if the specified
   * file exists, and is readable.  E.g., /.nologin exists.
   */
  if ((*file && auth_cat(file)) || auth_cat(_PATH_NOLOGIN))
    exit(1);
}
Example #8
0
/*
 * Check if the user is allowed to log in via ssh. If user is listed
 * in DenyUsers or one of user's groups is listed in DenyGroups, false
 * will be returned. If AllowUsers isn't empty and user isn't listed
 * there, or if AllowGroups isn't empty and one of user's groups isn't
 * listed there, false will be returned.
 * If the user's shell is not executable, false will be returned.
 * Otherwise true is returned.
 */
int
allowed_user(struct passwd * pw)
{
#ifdef HAVE_LOGIN_CAP
	extern login_cap_t *lc;
	int match_name, match_ip;
	char *cap_hlist, *hp;
#endif
	struct ssh *ssh = active_state; /* XXX */
	struct stat st;
	const char *hostname = NULL, *ipaddr = NULL;
	int r;
	u_int i;

	/* Shouldn't be called if pw is NULL, but better safe than sorry... */
	if (!pw || !pw->pw_name)
		return 0;

#ifdef HAVE_LOGIN_CAP
	hostname = auth_get_canonical_hostname(ssh, options.use_dns);
	ipaddr = ssh_remote_ipaddr(ssh);

	lc = login_getclass(pw->pw_class);

	/*
	 * Check the deny list.
	 */
	cap_hlist = login_getcapstr(lc, "host.deny", NULL, NULL);
	if (cap_hlist != NULL) {
		hp = strtok(cap_hlist, ",");
		while (hp != NULL) {
			match_name = match_hostname(hostname, hp);
			match_ip = match_hostname(ipaddr, hp);
			/*
			 * Only a positive match here causes a "deny".
			 */
			if (match_name > 0 || match_ip > 0) {
				free(cap_hlist);
				login_close(lc);
				return 0;
			}
			hp = strtok(NULL, ",");
		}
		free(cap_hlist);
	}

	/*
	 * Check the allow list.  If the allow list exists, and the
	 * remote host is not in it, the user is implicitly denied.
	 */
	cap_hlist = login_getcapstr(lc, "host.allow", NULL, NULL);
	if (cap_hlist != NULL) {
		hp = strtok(cap_hlist, ",");
		if (hp == NULL) {
			/* Just in case there's an empty string... */
			free(cap_hlist);
			login_close(lc);
			return 0;
		}
		while (hp != NULL) {
			match_name = match_hostname(hostname, hp);
			match_ip = match_hostname(ipaddr, hp);
			/*
			 * Negative match causes an immediate "deny".
			 * Positive match causes us to break out
			 * of the loop (allowing a fallthrough).
			 */
			if (match_name < 0 || match_ip < 0) {
				free(cap_hlist);
				login_close(lc);
				return 0;
			}
			if (match_name > 0 || match_ip > 0)
				break;
			hp = strtok(NULL, ",");
		}
		free(cap_hlist);
		if (hp == NULL) {
			login_close(lc);
			return 0;
		}
	}

	login_close(lc);
#endif

#ifdef USE_PAM
	if (!options.use_pam) {
#endif
	/*
	 * password/account expiration.
	 */
	if (pw->pw_change || pw->pw_expire) {
		struct timeval tv;

		(void)gettimeofday(&tv, (struct timezone *)NULL);
		if (pw->pw_expire) {
			if (tv.tv_sec >= pw->pw_expire) {
				logit("User %.100s not allowed because account has expired",
				    pw->pw_name);
				return 0;	/* expired */
			}
		}
#ifdef _PASSWORD_CHGNOW
		if (pw->pw_change == _PASSWORD_CHGNOW) {
			logit("User %.100s not allowed because password needs to be changed",
			    pw->pw_name);

			return 0;	/* can't force password change (yet) */
		}
#endif
		if (pw->pw_change) {
			if (tv.tv_sec >= pw->pw_change) {
				logit("User %.100s not allowed because password has expired",
				    pw->pw_name);
				return 0;	/* expired */
			}
		}
	}
#ifdef USE_PAM
	}
#endif

	/*
	 * Deny if shell does not exist or is not executable unless we
	 * are chrooting.
	 */
	/*
	 * XXX Should check to see if it is executable by the
	 * XXX requesting user.  --thorpej
	 */
	if (options.chroot_directory == NULL ||
	    strcasecmp(options.chroot_directory, "none") == 0) {
		char *shell = xstrdup((pw->pw_shell[0] == '\0') ?
		    _PATH_BSHELL : pw->pw_shell); /* empty = /bin/sh */

		if (stat(shell, &st) != 0) {
			logit("User %.100s not allowed because shell %.100s "
			    "does not exist", pw->pw_name, shell);
			free(shell);
			return 0;
		}
		if (S_ISREG(st.st_mode) == 0 ||
		    (st.st_mode & (S_IXOTH|S_IXUSR|S_IXGRP)) == 0) {
			logit("User %.100s not allowed because shell %.100s "
			    "is not executable", pw->pw_name, shell);
			free(shell);
			return 0;
		}
		free(shell);
	}
	/*
	 * XXX Consider nuking {Allow,Deny}{Users,Groups}.  We have the
	 * XXX login_cap(3) mechanism which covers all other types of
	 * XXX logins, too.
	 */

	if (options.num_deny_users > 0 || options.num_allow_users > 0 ||
	    options.num_deny_groups > 0 || options.num_allow_groups > 0) {
		hostname = auth_get_canonical_hostname(ssh, options.use_dns);
		ipaddr = ssh_remote_ipaddr(ssh);
	}

	/* Return false if user is listed in DenyUsers */
	if (options.num_deny_users > 0) {
		for (i = 0; i < options.num_deny_users; i++) {
			r = match_user(pw->pw_name, hostname, ipaddr,
			    options.deny_users[i]);
			if (r < 0) {
				fatal("Invalid DenyUsers pattern \"%.100s\"",
				    options.deny_users[i]);
			} else if (r != 0) {
				logit("User %.100s from %.100s not allowed "
				    "because listed in DenyUsers",
				    pw->pw_name, hostname);
				return 0;
			}
		}
	}
	/* Return false if AllowUsers isn't empty and user isn't listed there */
	if (options.num_allow_users > 0) {
		for (i = 0; i < options.num_allow_users; i++) {
			r = match_user(pw->pw_name, hostname, ipaddr,
			    options.allow_users[i]);
			if (r < 0) {
				fatal("Invalid AllowUsers pattern \"%.100s\"",
				    options.allow_users[i]);
			} else if (r == 1)
				break;
		}
		/* i < options.num_allow_users iff we break for loop */
		if (i >= options.num_allow_users) {
			logit("User %.100s from %.100s not allowed because "
			    "not listed in AllowUsers", pw->pw_name, hostname);
			return 0;
		}
	}
	if (options.num_deny_groups > 0 || options.num_allow_groups > 0) {
		/* Get the user's group access list (primary and supplementary) */
		if (ga_init(pw->pw_name, pw->pw_gid) == 0) {
			logit("User %.100s from %.100s not allowed because "
			    "not in any group", pw->pw_name, hostname);
			return 0;
		}

		/* Return false if one of user's groups is listed in DenyGroups */
		if (options.num_deny_groups > 0)
			if (ga_match(options.deny_groups,
			    options.num_deny_groups)) {
				ga_free();
				logit("User %.100s from %.100s not allowed "
				    "because a group is listed in DenyGroups",
				    pw->pw_name, hostname);
				return 0;
			}
		/*
		 * Return false if AllowGroups isn't empty and one of user's groups
		 * isn't listed there
		 */
		if (options.num_allow_groups > 0)
			if (!ga_match(options.allow_groups,
			    options.num_allow_groups)) {
				ga_free();
				logit("User %.100s from %.100s not allowed "
				    "because none of user's groups are listed "
				    "in AllowGroups", pw->pw_name, hostname);
				return 0;
			}
		ga_free();
	}
	/* We found no reason not to let this user try to log on... */
	return 1;
}
Example #9
0
int
setusercontext(login_cap_t *lc, const struct passwd *pwd, uid_t uid, unsigned int flags)
{
    quad_t	p;
    mode_t	mymask;
    login_cap_t *llc = NULL;
    struct sigaction sa, prevsa;
    struct rtprio rtp;
    int error;

    if (lc == NULL) {
	if (pwd != NULL && (lc = login_getpwclass(pwd)) != NULL)
	    llc = lc; /* free this when we're done */
    }

    if (flags & LOGIN_SETPATH)
	pathvars[0].def = uid ? _PATH_DEFPATH : _PATH_STDPATH;

    /* we need a passwd entry to set these */
    if (pwd == NULL)
	flags &= ~(LOGIN_SETGROUP | LOGIN_SETLOGIN | LOGIN_SETMAC);

    /* Set the process priority */
    if (flags & LOGIN_SETPRIORITY) {
	p = login_getcapnum(lc, "priority", LOGIN_DEFPRI, LOGIN_DEFPRI);

	if (p > PRIO_MAX) {
	    rtp.type = RTP_PRIO_IDLE;
	    rtp.prio = p - PRIO_MAX - 1;
	    p = (rtp.prio > RTP_PRIO_MAX) ? 31 : p;
	    if (rtprio(RTP_SET, 0, &rtp))
		syslog(LOG_WARNING, "rtprio '%s' (%s): %m",
		    pwd ? pwd->pw_name : "-",
		    lc ? lc->lc_class : LOGIN_DEFCLASS);
	} else if (p < PRIO_MIN) {
	    rtp.type = RTP_PRIO_REALTIME;
	    rtp.prio = abs(p - PRIO_MIN + RTP_PRIO_MAX);
	    p = (rtp.prio > RTP_PRIO_MAX) ? 1 : p;
	    if (rtprio(RTP_SET, 0, &rtp))
		syslog(LOG_WARNING, "rtprio '%s' (%s): %m",
		    pwd ? pwd->pw_name : "-",
		    lc ? lc->lc_class : LOGIN_DEFCLASS);
	} else {
	    if (setpriority(PRIO_PROCESS, 0, (int)p) != 0)
		syslog(LOG_WARNING, "setpriority '%s' (%s): %m",
		    pwd ? pwd->pw_name : "-",
		    lc ? lc->lc_class : LOGIN_DEFCLASS);
	}
    }

    /* Setup the user's group permissions */
    if (flags & LOGIN_SETGROUP) {
	if (setgid(pwd->pw_gid) != 0) {
	    syslog(LOG_ERR, "setgid(%lu): %m", (u_long)pwd->pw_gid);
	    login_close(llc);
	    return (-1);
	}
	if (initgroups(pwd->pw_name, pwd->pw_gid) == -1) {
	    syslog(LOG_ERR, "initgroups(%s,%lu): %m", pwd->pw_name,
		   (u_long)pwd->pw_gid);
	    login_close(llc);
	    return (-1);
	}
    }

    /* Set up the user's MAC label. */
    if ((flags & LOGIN_SETMAC) && mac_is_present(NULL) == 1) {
	const char *label_string;
	mac_t label;

	label_string = login_getcapstr(lc, "label", NULL, NULL);
	if (label_string != NULL) {
	    if (mac_from_text(&label, label_string) == -1) {
		syslog(LOG_ERR, "mac_from_text('%s') for %s: %m",
		    pwd->pw_name, label_string);
		return (-1);
	    }
	    if (mac_set_proc(label) == -1)
		error = errno;
	    else
		error = 0;
	    mac_free(label);
	    if (error != 0) {
		syslog(LOG_ERR, "mac_set_proc('%s') for %s: %s",
		    label_string, pwd->pw_name, strerror(error));
		return (-1);
	    }
	}
    }

    /* Set the sessions login */
    if ((flags & LOGIN_SETLOGIN) && setlogin(pwd->pw_name) != 0) {
	syslog(LOG_ERR, "setlogin(%s): %m", pwd->pw_name);
	login_close(llc);
	return (-1);
    }

    /* Inform the kernel about current login class */
    if (lc != NULL && lc->lc_class != NULL && (flags & LOGIN_SETLOGINCLASS)) {
	/*
	 * XXX: This is a workaround to fail gracefully in case the kernel
	 *      does not support setloginclass(2).
	 */
	bzero(&sa, sizeof(sa));
	sa.sa_handler = SIG_IGN;
	sigfillset(&sa.sa_mask);
	sigaction(SIGSYS, &sa, &prevsa);
	error = setloginclass(lc->lc_class);
	sigaction(SIGSYS, &prevsa, NULL);
	if (error != 0) {
	    syslog(LOG_ERR, "setloginclass(%s): %m", lc->lc_class);
#ifdef notyet
	    login_close(llc);
	    return (-1);
#endif
	}
    }

    mymask = (flags & LOGIN_SETUMASK) ? umask(LOGIN_DEFUMASK) : 0;
    mymask = setlogincontext(lc, pwd, mymask, flags);
    login_close(llc);

    /* This needs to be done after anything that needs root privs */
    if ((flags & LOGIN_SETUSER) && setuid(uid) != 0) {
	syslog(LOG_ERR, "setuid(%lu): %m", (u_long)uid);
	return (-1);	/* Paranoia again */
    }

    /*
     * Now, we repeat some of the above for the user's private entries
     */
    if (getuid() == uid && (lc = login_getuserclass(pwd)) != NULL) {
	mymask = setlogincontext(lc, pwd, mymask, flags);
	login_close(lc);
    }

    /* Finally, set any umask we've found */
    if (flags & LOGIN_SETUMASK)
	umask(mymask);

    return (0);
}
Example #10
0
int
main(int argc, char *argv[])
{
	struct group *gr;
	struct stat st;
	int ask, ch, cnt, fflag, hflag, pflag, sflag, quietlog, rootlogin, rval;
	uid_t uid, saved_uid;
	gid_t saved_gid, saved_gids[NGROUPS_MAX];
	int nsaved_gids;
#ifdef notdef
	char *domain;
#endif
	char *p, *ttyn;
	const char *pwprompt;
	char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_TTY) + 10];
	char localhost[MAXHOSTNAMELEN + 1];
	int need_chpass, require_chpass;
	int login_retries = DEFAULT_RETRIES, 
	    login_backoff = DEFAULT_BACKOFF;
	time_t pw_warntime = _PASSWORD_WARNDAYS * SECSPERDAY;
	char *loginname = NULL;
#ifdef KERBEROS5
	int Fflag;
	krb5_error_code kerror;
#endif
#if defined(KERBEROS5)
	int got_tickets = 0;
#endif
#ifdef LOGIN_CAP
	char *shell = NULL;
	login_cap_t *lc = NULL;
#endif

	tbuf[0] = '\0';
	rval = 0;
	pwprompt = NULL;
	nested = NULL;
	need_chpass = require_chpass = 0;

	(void)signal(SIGALRM, timedout);
	(void)alarm(timeout);
	(void)signal(SIGQUIT, SIG_IGN);
	(void)signal(SIGINT, SIG_IGN);
	(void)setpriority(PRIO_PROCESS, 0, 0);

	openlog("login", 0, LOG_AUTH);

	/*
	 * -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/utmpx and wtmp/wtmpx
	 * -a in addition to -h, a server may supply -a to pass the actual
	 *    server address.
	 * -s is used to force use of S/Key or equivalent.
	 */
	if (gethostname(localhost, sizeof(localhost)) < 0) {
		syslog(LOG_ERR, "couldn't get local hostname: %m");
		strcpy(hostname, "amnesiac");
	}
#ifdef notdef
	domain = strchr(localhost, '.');
#endif
	localhost[sizeof(localhost) - 1] = '\0';

	fflag = hflag = pflag = sflag = 0;
	have_ss = 0;
#ifdef KERBEROS5
	Fflag = 0;
	have_forward = 0;
#endif
	uid = getuid();
	while ((ch = getopt(argc, argv, "a:Ffh:ps")) != -1)
		switch (ch) {
		case 'a':
			if (uid)
				errx(EXIT_FAILURE, "-a option: %s", strerror(EPERM));
			decode_ss(optarg);
#ifdef notdef
			(void)sockaddr_snprintf(optarg,
			    sizeof(struct sockaddr_storage), "%a", (void *)&ss);
#endif
			break;
		case 'F':
#ifdef KERBEROS5
			Fflag = 1;
#endif
			/* FALLTHROUGH */
		case 'f':
			fflag = 1;
			break;
		case 'h':
			if (uid)
				errx(EXIT_FAILURE, "-h option: %s", strerror(EPERM));
			hflag = 1;
#ifdef notdef
			if (domain && (p = strchr(optarg, '.')) != NULL &&
			    strcasecmp(p, domain) == 0)
				*p = '\0';
#endif
			hostname = optarg;
			break;
		case 'p':
			pflag = 1;
			break;
		case 's':
			sflag = 1;
			break;
		default:
		case '?':
			usage();
			break;
		}

	setproctitle(NULL);
	argc -= optind;
	argv += optind;

	if (*argv) {
		username = loginname = *argv;
		ask = 0;
	} else
		ask = 1;

#ifdef F_CLOSEM
	(void)fcntl(3, F_CLOSEM, 0);
#else
	for (cnt = getdtablesize(); cnt > 2; cnt--)
		(void)close(cnt);
#endif

	ttyn = ttyname(STDIN_FILENO);
	if (ttyn == NULL || *ttyn == '\0') {
		(void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY);
		ttyn = tname;
	}
	if ((tty = strstr(ttyn, "/pts/")) != NULL)
		++tty;
	else if ((tty = strrchr(ttyn, '/')) != NULL)
		++tty;
	else
		tty = ttyn;

	if (issetugid()) {
		nested = strdup(user_from_uid(getuid(), 0));
		if (nested == NULL) {
			syslog(LOG_ERR, "strdup: %m");
			sleepexit(EXIT_FAILURE);
		}
	}

#ifdef LOGIN_CAP
	/* Get "login-retries" and "login-backoff" from default class */
	if ((lc = login_getclass(NULL)) != NULL) {
		login_retries = (int)login_getcapnum(lc, "login-retries",
		    DEFAULT_RETRIES, DEFAULT_RETRIES);
		login_backoff = (int)login_getcapnum(lc, "login-backoff", 
		    DEFAULT_BACKOFF, DEFAULT_BACKOFF);
		login_close(lc);
		lc = NULL;
	}
#endif

#ifdef KERBEROS5
	kerror = krb5_init_context(&kcontext);
	if (kerror) {
		/*
		 * If Kerberos is not configured, that is, we are
		 * not using Kerberos, do not log the error message.
		 * However, if Kerberos is configured,  and the
		 * context init fails for some other reason, we need
		 * to issue a no tickets warning to the user when the
		 * login succeeds.
		 */
		if (kerror != ENXIO) {	/* XXX NetBSD-local Heimdal hack */
			syslog(LOG_NOTICE,
			    "%s when initializing Kerberos context",
			    error_message(kerror));
			krb5_configured = 1;
		}
		login_krb5_get_tickets = 0;
	}
#endif /* KERBEROS5 */

	for (cnt = 0;; ask = 1) {
#if defined(KERBEROS5)
		if (login_krb5_get_tickets)
			k5destroy();
#endif
		if (ask) {
			fflag = 0;
			loginname = getloginname();
		}
		rootlogin = 0;
#ifdef KERBEROS5
		if ((instance = strchr(loginname, '/')) != NULL)
			*instance++ = '\0';
		else
			instance = __UNCONST("");
#endif
		username = trimloginname(loginname);
		/*
		 * Note if trying multiple user names; log failures for
		 * previous user name, but don't bother logging one failure
		 * for nonexistent name (mistyped username).
		 */
		if (failures && strcmp(tbuf, username)) {
			if (failures > (pwd ? 0 : 1))
				badlogin(tbuf);
			failures = 0;
		}
		(void)strlcpy(tbuf, username, sizeof(tbuf));

		pwd = getpwnam(username);

#ifdef LOGIN_CAP
		/*
		 * Establish the class now, before we might goto
		 * within the next block. pwd can be NULL since it
		 * falls back to the "default" class if it is.
		 */
		lc = login_getclass(pwd ? pwd->pw_class : NULL);
#endif
		/*
		 * if we have a valid account name, and it doesn't have a
		 * password, or the -f option was specified and the caller
		 * is root or the caller isn't changing their uid, don't
		 * authenticate.
		 */
		if (pwd) {
			if (pwd->pw_uid == 0)
				rootlogin = 1;

			if (fflag && (uid == 0 || uid == pwd->pw_uid)) {
				/* already authenticated */
#ifdef KERBEROS5
				if (login_krb5_get_tickets && Fflag)
					k5_read_creds(username);
#endif
				break;
			} else if (pwd->pw_passwd[0] == '\0') {
				/* pretend password okay */
				rval = 0;
				goto ttycheck;
			}
		}

		fflag = 0;

		(void)setpriority(PRIO_PROCESS, 0, -4);

#ifdef SKEY
		if (skey_haskey(username) == 0) {
			static char skprompt[80];
			const char *skinfo = skey_keyinfo(username);
				
			(void)snprintf(skprompt, sizeof(skprompt),
			    "Password [ %s ]:",
			    skinfo ? skinfo : "error getting challenge");
			pwprompt = skprompt;
		} else
#endif
			pwprompt = "Password:"******"Login incorrect or refused on this "
			    "terminal.\n");
			if (hostname)
				syslog(LOG_NOTICE,
				    "LOGIN %s REFUSED FROM %s ON TTY %s",
				    pwd->pw_name, hostname, tty);
			else
				syslog(LOG_NOTICE,
				    "LOGIN %s REFUSED ON TTY %s",
				     pwd->pw_name, tty);
			continue;
		}

		if (pwd && !rval)
			break;

		(void)printf("Login incorrect or refused on this "
		    "terminal.\n");
		failures++;
		cnt++;
		/*
		 * We allow login_retries tries, but after login_backoff
		 * we start backing off.  These default to 10 and 3
		 * respectively.
		 */
		if (cnt > login_backoff) {
			if (cnt >= login_retries) {
				badlogin(username);
				sleepexit(EXIT_FAILURE);
			}
			sleep((u_int)((cnt - login_backoff) * 5));
		}
	}

	/* committed to login -- turn off timeout */
	(void)alarm((u_int)0);

	endpwent();

	/* if user not super-user, check for disabled logins */
#ifdef LOGIN_CAP
	if (!login_getcapbool(lc, "ignorenologin", rootlogin))
		checknologin(login_getcapstr(lc, "nologin", NULL, NULL));
#else
	if (!rootlogin)
		checknologin(NULL);
#endif

#ifdef LOGIN_CAP
	quietlog = login_getcapbool(lc, "hushlogin", 0);
#else
	quietlog = 0;
#endif
	/* Temporarily give up special privileges so we can change */
	/* into NFS-mounted homes that are exported for non-root */
	/* access and have mode 7x0 */
	saved_uid = geteuid();
	saved_gid = getegid();
	nsaved_gids = getgroups(NGROUPS_MAX, saved_gids);
	
	(void)setegid(pwd->pw_gid);
	initgroups(username, pwd->pw_gid);
	(void)seteuid(pwd->pw_uid);
	
	if (chdir(pwd->pw_dir) < 0) {
#ifdef LOGIN_CAP
		if (login_getcapbool(lc, "requirehome", 0)) {
			(void)printf("Home directory %s required\n",
			    pwd->pw_dir);
			sleepexit(EXIT_FAILURE);
		}
#endif	
		(void)printf("No home directory %s!\n", pwd->pw_dir);
		if (chdir("/") == -1)
			exit(EXIT_FAILURE);
		pwd->pw_dir = __UNCONST("/");
		(void)printf("Logging in with home = \"/\".\n");
	}

	if (!quietlog)
		quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0;

	/* regain special privileges */
	(void)seteuid(saved_uid);
	setgroups(nsaved_gids, saved_gids);
	(void)setegid(saved_gid);

#ifdef LOGIN_CAP
	pw_warntime = login_getcaptime(lc, "password-warn",
		_PASSWORD_WARNDAYS * SECSPERDAY,
		_PASSWORD_WARNDAYS * SECSPERDAY);
#endif

	(void)gettimeofday(&now, NULL);
	if (pwd->pw_expire) {
		if (now.tv_sec >= pwd->pw_expire) {
			(void)printf("Sorry -- your account has expired.\n");
			sleepexit(EXIT_FAILURE);
		} else if (pwd->pw_expire - now.tv_sec < pw_warntime && 
		    !quietlog)
			(void)printf("Warning: your account expires on %s",
			    ctime(&pwd->pw_expire));
	}
	if (pwd->pw_change) {
		if (pwd->pw_change == _PASSWORD_CHGNOW)
			need_chpass = 1;
		else if (now.tv_sec >= pwd->pw_change) {
			(void)printf("Sorry -- your password has expired.\n");
			sleepexit(EXIT_FAILURE);
		} else if (pwd->pw_change - now.tv_sec < pw_warntime && 
		    !quietlog)
			(void)printf("Warning: your password expires on %s",
			    ctime(&pwd->pw_change));

	}
	/* Nothing else left to fail -- really log in. */
	update_db(quietlog, rootlogin, fflag);

	(void)chown(ttyn, pwd->pw_uid,
	    (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid);

	if (ttyaction(ttyn, "login", pwd->pw_name))
		(void)printf("Warning: ttyaction failed.\n");

#if defined(KERBEROS5)
	/* Fork so that we can call kdestroy */
	if (! login_krb5_retain_ccache && has_ccache)
		dofork();
#endif

	/* Destroy environment unless user has requested its preservation. */
	if (!pflag)
		environ = envinit;

#ifdef LOGIN_CAP
	if (nested == NULL && setusercontext(lc, pwd, pwd->pw_uid,
	    LOGIN_SETLOGIN) != 0) {
		syslog(LOG_ERR, "setusercontext failed");
		exit(EXIT_FAILURE);
	}
	if (setusercontext(lc, pwd, pwd->pw_uid,
	    (LOGIN_SETALL & ~(LOGIN_SETPATH|LOGIN_SETLOGIN))) != 0) {
		syslog(LOG_ERR, "setusercontext failed");
		exit(EXIT_FAILURE);
	}
#else
	(void)setgid(pwd->pw_gid);

	initgroups(username, pwd->pw_gid);
	
	if (nested == NULL && setlogin(pwd->pw_name) < 0)
		syslog(LOG_ERR, "setlogin() failure: %m");

	/* Discard permissions last so can't get killed and drop core. */
	if (rootlogin)
		(void)setuid(0);
	else
		(void)setuid(pwd->pw_uid);
#endif

	if (*pwd->pw_shell == '\0')
		pwd->pw_shell = __UNCONST(_PATH_BSHELL);
#ifdef LOGIN_CAP
	if ((shell = login_getcapstr(lc, "shell", NULL, NULL)) != NULL) {
		if ((shell = strdup(shell)) == NULL) {
			syslog(LOG_ERR, "Cannot alloc mem");
			sleepexit(EXIT_FAILURE);
		}
		pwd->pw_shell = shell;
	}
#endif
	
	(void)setenv("HOME", pwd->pw_dir, 1);
	(void)setenv("SHELL", pwd->pw_shell, 1);
	if (term[0] == '\0') {
		const char *tt = stypeof(tty);
#ifdef LOGIN_CAP
		if (tt == NULL)
			tt = login_getcapstr(lc, "term", NULL, NULL);
#endif
		/* unknown term -> "su" */
		(void)strlcpy(term, tt != NULL ? tt : "su", sizeof(term));
	}
	(void)setenv("TERM", term, 0);
	(void)setenv("LOGNAME", pwd->pw_name, 1);
	(void)setenv("USER", pwd->pw_name, 1);

#ifdef LOGIN_CAP
	setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH);
#else
	(void)setenv("PATH", _PATH_DEFPATH, 0);
#endif

#ifdef KERBEROS5
	if (krb5tkfile_env)
		(void)setenv("KRB5CCNAME", krb5tkfile_env, 1);
#endif

	/* If fflag is on, assume caller/authenticator has logged root login. */
	if (rootlogin && fflag == 0) {
		if (hostname)
			syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s",
			    username, tty, hostname);
		else
			syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s",
			    username, tty);
	}

#if defined(KERBEROS5)
	if (KERBEROS_CONFIGURED && !quietlog && notickets == 1)
		(void)printf("Warning: no Kerberos tickets issued.\n");
#endif

	if (!quietlog) {
		const char *fname;
#ifdef LOGIN_CAP
		fname = login_getcapstr(lc, "copyright", NULL, NULL);
		if (fname != NULL && access(fname, F_OK) == 0)
			motd(fname);
		else
#endif
			(void)printf("%s", copyrightstr);

#ifdef LOGIN_CAP
		fname = login_getcapstr(lc, "welcome", NULL, NULL);
		if (fname == NULL || access(fname, F_OK) != 0)
#endif
			fname = _PATH_MOTDFILE;
		motd(fname);

		(void)snprintf(tbuf,
		    sizeof(tbuf), "%s/%s", _PATH_MAILDIR, pwd->pw_name);
		if (stat(tbuf, &st) == 0 && st.st_size != 0)
			(void)printf("You have %smail.\n",
			    (st.st_mtime > st.st_atime) ? "new " : "");
	}

#ifdef LOGIN_CAP
	login_close(lc);
#endif

	(void)signal(SIGALRM, SIG_DFL);
	(void)signal(SIGQUIT, SIG_DFL);
	(void)signal(SIGINT, SIG_DFL);
	(void)signal(SIGTSTP, SIG_IGN);

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

	/* Wait to change password until we're unprivileged */
	if (need_chpass) {
		if (!require_chpass)
			(void)printf(
"Warning: your password has expired. Please change it as soon as possible.\n");
		else {
			int	status;

			(void)printf(
		    "Your password has expired. Please choose a new one.\n");
			switch (fork()) {
			case -1:
				warn("fork");
				sleepexit(EXIT_FAILURE);
			case 0:
				execl(_PATH_BINPASSWD, "passwd", NULL);
				_exit(EXIT_FAILURE);
			default:
				if (wait(&status) == -1 ||
				    WEXITSTATUS(status))
					sleepexit(EXIT_FAILURE);
			}
		}
	}

#ifdef KERBEROS5
	if (login_krb5_get_tickets)
		k5_write_creds();
#endif
	execlp(pwd->pw_shell, tbuf, NULL);
	err(EXIT_FAILURE, "%s", pwd->pw_shell);
}
Example #11
0
/*
 * User shell for authenticating gateways. Sole purpose is to allow
 * a user to ssh to a gateway, and have the gateway modify packet
 * filters to allow access, then remove access when the user finishes
 * up. Meant to be used only from ssh(1) connections.
 */
int
main(void)
{
	int		 lockcnt = 0, n;
	FILE		*config;
	struct in6_addr	 ina;
	struct passwd	*pw;
	char		*cp;
	gid_t		 gid;
	uid_t		 uid;
	const char	*shell;
	login_cap_t	*lc;

	if (strcmp(__progname, "-authpf-noip") == 0)
                user_ip = 0;

	config = fopen(PATH_CONFFILE, "r");
	if (config == NULL) {
		syslog(LOG_ERR, "cannot open %s (%m)", PATH_CONFFILE);
		exit(1);
	}

	if ((cp = getenv("SSH_TTY")) == NULL) {
		syslog(LOG_ERR, "non-interactive session connection for authpf");
		exit(1);
	}

	if ((cp = getenv("SSH_CLIENT")) == NULL) {
		syslog(LOG_ERR, "cannot determine connection source");
		exit(1);
	}

	if (strlcpy(ipsrc, cp, sizeof(ipsrc)) >= sizeof(ipsrc)) {
		syslog(LOG_ERR, "SSH_CLIENT variable too long");
		exit(1);
	}
	cp = strchr(ipsrc, ' ');
	if (!cp) {
		syslog(LOG_ERR, "corrupt SSH_CLIENT variable %s", ipsrc);
		exit(1);
	}
	*cp = '\0';
	if (inet_pton(AF_INET, ipsrc, &ina) != 1 &&
	    inet_pton(AF_INET6, ipsrc, &ina) != 1) {
		syslog(LOG_ERR,
		    "cannot determine IP from SSH_CLIENT %s", ipsrc);
		exit(1);
	}
	/* open the pf device */
	dev = open(PATH_DEVFILE, O_RDWR);
	if (dev == -1) {
		syslog(LOG_ERR, "cannot open packet filter device (%m)");
		goto die;
	}

	uid = getuid();
	pw = getpwuid(uid);
	if (pw == NULL) {
		syslog(LOG_ERR, "cannot find user for uid %u", uid);
		goto die;
	}

	if ((lc = login_getclass(pw->pw_class)) != NULL)
		shell = login_getcapstr(lc, "shell", pw->pw_shell,
		    pw->pw_shell);
	else
		shell = pw->pw_shell;

#ifndef __FreeBSD__
	login_close(lc);
#endif

	if (strcmp(shell, PATH_AUTHPF_SHELL) && 
	    strcmp(shell, PATH_AUTHPF_SHELL_NOIP)) {
		syslog(LOG_ERR, "wrong shell for user %s, uid %u",
		    pw->pw_name, pw->pw_uid);
#ifdef __FreeBSD__
	login_close(lc);
#else
		if (shell != pw->pw_shell)
			free(shell);
#endif
		goto die;
	}

#ifdef __FreeBSD__
	login_close(lc);
#else
	if (shell != pw->pw_shell)
		free(shell);
#endif

	/*
	 * Paranoia, but this data _does_ come from outside authpf, and
	 * truncation would be bad.
	 */
	if (strlcpy(luser, pw->pw_name, sizeof(luser)) >= sizeof(luser)) {
		syslog(LOG_ERR, "username too long: %s", pw->pw_name);
		goto die;
	}

	if ((n = snprintf(rulesetname, sizeof(rulesetname), "%s(%ld)",
	    luser, (long)getpid())) < 0 || (u_int)n >= sizeof(rulesetname)) {
		syslog(LOG_INFO, "%s(%ld) too large, ruleset name will be %ld",
		    luser, (long)getpid(), (long)getpid());
		if ((n = snprintf(rulesetname, sizeof(rulesetname), "%ld",
		    (long)getpid())) < 0 || (u_int)n >= sizeof(rulesetname)) {
			syslog(LOG_ERR, "pid too large for ruleset name");
			goto die;
		}
	}


	/* Make our entry in /var/authpf as ipaddr or username */
	n = snprintf(pidfile, sizeof(pidfile), "%s/%s",
	    PATH_PIDFILE, user_ip ? ipsrc : luser);
	if (n < 0 || (u_int)n >= sizeof(pidfile)) {
		syslog(LOG_ERR, "path to pidfile too long");
		goto die;
	}

	signal(SIGTERM, need_death);
	signal(SIGINT, need_death);
	signal(SIGALRM, need_death);
	signal(SIGPIPE, need_death);
	signal(SIGHUP, need_death);
	signal(SIGQUIT, need_death);
	signal(SIGTSTP, need_death);

	/*
	 * If someone else is already using this ip, then this person
	 * wants to switch users - so kill the old process and exit
	 * as well.
	 *
	 * Note, we could print a message and tell them to log out, but the
	 * usual case of this is that someone has left themselves logged in,
	 * with the authenticated connection iconized and someone else walks
	 * up to use and automatically logs in before using. If this just
	 * gets rid of the old one silently, the new user never knows they
	 * could have used someone else's old authentication. If we
	 * tell them to log out before switching users it is an invitation
	 * for abuse.
	 */

	do {
		int	save_errno, otherpid = -1;
		char	otherluser[MAXLOGNAME];

		if ((pidfd = open(pidfile, O_RDWR|O_CREAT, 0644)) == -1 ||
		    (pidfp = fdopen(pidfd, "r+")) == NULL) {
			if (pidfd != -1)
				close(pidfd);
			syslog(LOG_ERR, "cannot open or create %s: %s", pidfile,
			    strerror(errno));
			goto die;
		}

		if (flock(fileno(pidfp), LOCK_EX|LOCK_NB) == 0)
			break;
		save_errno = errno;

		/* Mark our pid, and username to our file. */

		rewind(pidfp);
		/* 31 == MAXLOGNAME - 1 */
		if (fscanf(pidfp, "%d\n%31s\n", &otherpid, otherluser) != 2)
			otherpid = -1;
		syslog(LOG_DEBUG, "tried to lock %s, in use by pid %d: %s",
		    pidfile, otherpid, strerror(save_errno));

		if (otherpid > 0) {
			syslog(LOG_INFO,
			    "killing prior auth (pid %d) of %s by user %s",
			    otherpid, ipsrc, otherluser);
			if (kill((pid_t) otherpid, SIGTERM) == -1) {
				syslog(LOG_INFO,
				    "could not kill process %d: (%m)",
				    otherpid);
			}
		}

		/*
		 * We try to kill the previous process and acquire the lock
		 * for 10 seconds, trying once a second. if we can't after
		 * 10 attempts we log an error and give up.
		 */
		if (want_death || ++lockcnt > 10) {
			if (!want_death)
				syslog(LOG_ERR, "cannot kill previous authpf (pid %d)",
				    otherpid);
			fclose(pidfp);
			pidfp = NULL;
			pidfd = -1;
			goto dogdeath;
		}
		sleep(1);

		/* re-open, and try again. The previous authpf process
		 * we killed above should unlink the file and release
		 * it's lock, giving us a chance to get it now
		 */
		fclose(pidfp);
		pidfp = NULL;
		pidfd = -1;
	} while (1);
	
	/* whack the group list */
	gid = getegid();
	if (setgroups(1, &gid) == -1) {
		syslog(LOG_INFO, "setgroups: %s", strerror(errno));
		do_death(0);
	}

	/* revoke privs */
	uid = getuid();
	if (setresuid(uid, uid, uid) == -1) {
		syslog(LOG_INFO, "setresuid: %s", strerror(errno));
		do_death(0);
	}
	openlog("authpf", LOG_PID | LOG_NDELAY, LOG_DAEMON);

	if (!check_luser(PATH_BAN_DIR, luser) || !allowed_luser(pw)) {
		syslog(LOG_INFO, "user %s prohibited", luser);
		do_death(0);
	}

	if (read_config(config)) {
		syslog(LOG_ERR, "invalid config file %s", PATH_CONFFILE);
		do_death(0);
	}

	if (remove_stale_rulesets()) {
		syslog(LOG_INFO, "error removing stale rulesets");
		do_death(0);
	}

	/* We appear to be making headway, so actually mark our pid */
	rewind(pidfp);
	fprintf(pidfp, "%ld\n%s\n", (long)getpid(), luser);
	fflush(pidfp);
	(void) ftruncate(fileno(pidfp), ftello(pidfp));

	if (change_filter(1, luser, ipsrc) == -1) {
		printf("Unable to modify filters\r\n");
		do_death(0);
	}
	if (user_ip && change_table(1, ipsrc) == -1) {
		printf("Unable to modify table\r\n");
		change_filter(0, luser, ipsrc);
		do_death(0);
	}

	while (1) {
		printf("\r\nHello %s. ", luser);
		printf("You are authenticated from host \"%s\"\r\n", ipsrc);
		setproctitle("%s@%s", luser, ipsrc);
		print_message(PATH_MESSAGE);
		while (1) {
			sleep(10);
			if (want_death)
				do_death(1);
		}
	}

	/* NOTREACHED */
dogdeath:
	printf("\r\n\r\nSorry, this service is currently unavailable due to ");
	printf("technical difficulties\r\n\r\n");
	print_message(PATH_PROBLEM);
	printf("\r\nYour authentication process (pid %ld) was unable to run\n",
	    (long)getpid());
	sleep(180); /* them lusers read reaaaaal slow */
die:
	do_death(0);
}
Example #12
0
int
main(int argc, char **argv)
{
	struct group *gr;
	struct stat st;
	int retries, backoff;
	int ask, ch, cnt, quietlog, rootlogin, rval;
	uid_t uid, euid;
	gid_t egid;
	char *term;
	char *p, *ttyn;
	char tname[sizeof(_PATH_TTY) + 10];
	char *arg0;
	const char *tp;
	const char *shell = NULL;
	login_cap_t *lc = NULL;
	login_cap_t *lc_user = NULL;
	pid_t pid;
#ifdef USE_BSM_AUDIT
	char auditsuccess = 1;
#endif

	signal(SIGQUIT, SIG_IGN);
	signal(SIGINT, SIG_IGN);
	signal(SIGHUP, SIG_IGN);
	if (setjmp(timeout_buf)) {
		if (failures)
			badlogin(username);
		fprintf(stderr, "Login timed out after %d seconds\n",
		    timeout);
		bail(NO_SLEEP_EXIT, 0);
	}
	signal(SIGALRM, timedout);
	alarm(timeout);
	setpriority(PRIO_PROCESS, 0, 0);

	openlog("login", LOG_ODELAY, LOG_AUTH);

	uid = getuid();
	euid = geteuid();
	egid = getegid();

	while ((ch = getopt(argc, argv, "fh:p")) != -1)
		switch (ch) {
		case 'f':
			fflag = 1;
			break;
		case 'h':
			if (uid != 0)
				errx(1, "-h option: %s", strerror(EPERM));
			if (strlen(optarg) >= MAXHOSTNAMELEN)
				errx(1, "-h option: %s: exceeds maximum "
				    "hostname size", optarg);
			hflag = 1;
			hostname = optarg;
			break;
		case 'p':
			pflag = 1;
			break;
		case '?':
		default:
			if (uid == 0)
				syslog(LOG_ERR, "invalid flag %c", ch);
			usage();
		}
	argc -= optind;
	argv += optind;

	if (argc > 0) {
		username = strdup(*argv);
		if (username == NULL)
			err(1, "strdup()");
		ask = 0;
	} else {
		ask = 1;
	}

	setproctitle("-%s", getprogname());

	for (cnt = getdtablesize(); cnt > 2; cnt--)
		close(cnt);

	/*
	 * Get current TTY
	 */
	ttyn = ttyname(STDIN_FILENO);
	if (ttyn == NULL || *ttyn == '\0') {
		snprintf(tname, sizeof(tname), "%s??", _PATH_TTY);
		ttyn = tname;
	}
	if (strncmp(ttyn, _PATH_DEV, sizeof(_PATH_DEV) -1) == 0)
		tty = ttyn + sizeof(_PATH_DEV) -1;
	else
		tty = ttyn;

	/*
	 * Get "login-retries" & "login-backoff" from default class
	 */
	lc = login_getclass(NULL);
	prompt = login_getcapstr(lc, "login_prompt",
	    default_prompt, default_prompt);
	passwd_prompt = login_getcapstr(lc, "passwd_prompt",
	    default_passwd_prompt, default_passwd_prompt);
	retries = login_getcapnum(lc, "login-retries",
	    DEFAULT_RETRIES, DEFAULT_RETRIES);
	backoff = login_getcapnum(lc, "login-backoff",
	    DEFAULT_BACKOFF, DEFAULT_BACKOFF);
	login_close(lc);
	lc = NULL;

	/*
	 * Try to authenticate the user until we succeed or time out.
	 */
	for (cnt = 0;; ask = 1) {
		if (ask) {
			fflag = 0;
			if (olduser != NULL)
				free(olduser);
			olduser = username;
			username = getloginname();
		}
		rootlogin = 0;

		/*
		 * Note if trying multiple user names; log failures for
		 * previous user name, but don't bother logging one failure
		 * for nonexistent name (mistyped username).
		 */
		if (failures && strcmp(olduser, username) != 0) {
			if (failures > (pwd ? 0 : 1))
				badlogin(olduser);
		}

		/*
		 * Load the PAM policy and set some variables
		 */
		pam_err = pam_start("login", username, &pamc, &pamh);
		if (pam_err != PAM_SUCCESS) {
			pam_syslog("pam_start()");
#ifdef USE_BSM_AUDIT
			au_login_fail("PAM Error", 1);
#endif
			bail(NO_SLEEP_EXIT, 1);
		}
		pam_err = pam_set_item(pamh, PAM_TTY, tty);
		if (pam_err != PAM_SUCCESS) {
			pam_syslog("pam_set_item(PAM_TTY)");
#ifdef USE_BSM_AUDIT
			au_login_fail("PAM Error", 1);
#endif
			bail(NO_SLEEP_EXIT, 1);
		}
		pam_err = pam_set_item(pamh, PAM_RHOST, hostname);
		if (pam_err != PAM_SUCCESS) {
			pam_syslog("pam_set_item(PAM_RHOST)");
#ifdef USE_BSM_AUDIT
			au_login_fail("PAM Error", 1);
#endif
			bail(NO_SLEEP_EXIT, 1);
		}

		pwd = getpwnam(username);
		if (pwd != NULL && pwd->pw_uid == 0)
			rootlogin = 1;

		/*
		 * If the -f option was specified and the caller is
		 * root or the caller isn't changing their uid, don't
		 * authenticate.
		 */
		if (pwd != NULL && fflag &&
		    (uid == (uid_t)0 || uid == (uid_t)pwd->pw_uid)) {
			/* already authenticated */
			rval = 0;
#ifdef USE_BSM_AUDIT
			auditsuccess = 0; /* opened a terminal window only */
#endif
		} else {
			fflag = 0;
			setpriority(PRIO_PROCESS, 0, -4);
			rval = auth_pam();
			setpriority(PRIO_PROCESS, 0, 0);
		}

		if (pwd && rval == 0)
			break;

		pam_cleanup();

		/*
		 * We are not exiting here, but this corresponds to a failed
		 * login event, so set exitstatus to 1.
		 */
#ifdef USE_BSM_AUDIT
		au_login_fail("Login incorrect", 1);
#endif

		printf("Login incorrect\n");
		failures++;

		pwd = NULL;

		/*
		 * Allow up to 'retry' (10) attempts, but start
		 * backing off after 'backoff' (3) attempts.
		 */
		if (++cnt > backoff) {
			if (cnt >= retries) {
				badlogin(username);
				bail(SLEEP_EXIT, 1);
			}
			sleep((u_int)((cnt - backoff) * 5));
		}
	}

	/* committed to login -- turn off timeout */
	alarm((u_int)0);
	signal(SIGHUP, SIG_DFL);

	endpwent();

#ifdef USE_BSM_AUDIT
	/* Audit successful login. */
	if (auditsuccess)
		au_login_success();
#endif

	/*
	 * Establish the login class.
	 */
	lc = login_getpwclass(pwd);
	lc_user = login_getuserclass(pwd);

	if (!(quietlog = login_getcapbool(lc_user, "hushlogin", 0)))
		quietlog = login_getcapbool(lc, "hushlogin", 0);

	/*
	 * Switching needed for NFS with root access disabled.
	 *
	 * XXX: This change fails to modify the additional groups for the
	 * process, and as such, may restrict rights normally granted
	 * through those groups.
	 */
	setegid(pwd->pw_gid);
	seteuid(rootlogin ? 0 : pwd->pw_uid);
	if (!*pwd->pw_dir || chdir(pwd->pw_dir) < 0) {
		if (login_getcapbool(lc, "requirehome", 0))
			refused("Home directory not available", "HOMEDIR", 1);
		if (chdir("/") < 0)
			refused("Cannot find root directory", "ROOTDIR", 1);
		if (!quietlog || *pwd->pw_dir)
			printf("No home directory.\nLogging in with home = \"/\".\n");
		pwd->pw_dir = strdup("/");
		if (pwd->pw_dir == NULL) {
			syslog(LOG_NOTICE, "strdup(): %m");
			bail(SLEEP_EXIT, 1);
		}
	}
	seteuid(euid);
	setegid(egid);
	if (!quietlog) {
		quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0;
		if (!quietlog)
			pam_silent = 0;
	}

	shell = login_getcapstr(lc, "shell", pwd->pw_shell, pwd->pw_shell);
	if (*pwd->pw_shell == '\0')
		pwd->pw_shell = strdup(_PATH_BSHELL);
	if (pwd->pw_shell == NULL) {
		syslog(LOG_NOTICE, "strdup(): %m");
		bail(SLEEP_EXIT, 1);
	}
	if (*shell == '\0')   /* Not overridden */
		shell = pwd->pw_shell;
	if ((shell = strdup(shell)) == NULL) {
		syslog(LOG_NOTICE, "strdup(): %m");
		bail(SLEEP_EXIT, 1);
	}

	/*
	 * Set device protections, depending on what terminal the
	 * user is logged in. This feature is used on Suns to give
	 * console users better privacy.
	 */
	login_fbtab(tty, pwd->pw_uid, pwd->pw_gid);

	/*
	 * Clear flags of the tty.  None should be set, and when the
	 * user sets them otherwise, this can cause the chown to fail.
	 * Since it isn't clear that flags are useful on character
	 * devices, we just clear them.
	 *
	 * We don't log in the case of EOPNOTSUPP because dev might be
	 * on NFS, which doesn't support chflags.
	 *
	 * We don't log in the EROFS because that means that /dev is on
	 * a read only file system and we assume that the permissions there
	 * are sane.
	 */
	if (ttyn != tname && chflags(ttyn, 0))
		if (errno != EOPNOTSUPP && errno != EROFS)
			syslog(LOG_ERR, "chflags(%s): %m", ttyn);
	if (ttyn != tname && chown(ttyn, pwd->pw_uid,
	    (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid))
		if (errno != EROFS)
			syslog(LOG_ERR, "chown(%s): %m", ttyn);

	/*
	 * Exclude cons/vt/ptys only, assume dialup otherwise
	 * TODO: Make dialup tty determination a library call
	 * for consistency (finger etc.)
	 */
	if (hflag && isdialuptty(tty))
		syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name);

#ifdef LOGALL
	/*
	 * Syslog each successful login, so we don't have to watch
	 * hundreds of wtmp or lastlogin files.
	 */
	if (hflag)
		syslog(LOG_INFO, "login from %s on %s as %s",
		       hostname, tty, pwd->pw_name);
	else
		syslog(LOG_INFO, "login on %s as %s",
		       tty, pwd->pw_name);
#endif

	/*
	 * If fflag is on, assume caller/authenticator has logged root
	 * login.
	 */
	if (rootlogin && fflag == 0) {
		if (hflag)
			syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s",
			    username, tty, hostname);
		else
			syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s",
			    username, tty);
	}

	/*
	 * Destroy environment unless user has requested its
	 * preservation - but preserve TERM in all cases
	 */
	term = getenv("TERM");
	if (!pflag)
		environ = envinit;
	if (term != NULL) {
		if (setenv("TERM", term, 0) == -1)
			err(1, "setenv: cannot set TERM=%s", term);
	}

	/*
	 * PAM modules might add supplementary groups during pam_setcred().
	 */
	if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) != 0) {
		syslog(LOG_ERR, "setusercontext() failed - exiting");
		bail(NO_SLEEP_EXIT, 1);
	}

	pam_err = pam_setcred(pamh, pam_silent|PAM_ESTABLISH_CRED);
	if (pam_err != PAM_SUCCESS) {
		pam_syslog("pam_setcred()");
		bail(NO_SLEEP_EXIT, 1);
	}
	pam_cred_established = 1;

	pam_err = pam_open_session(pamh, pam_silent);
	if (pam_err != PAM_SUCCESS) {
		pam_syslog("pam_open_session()");
		bail(NO_SLEEP_EXIT, 1);
	}
	pam_session_established = 1;

	/*
	 * We must fork() before setuid() because we need to call
	 * pam_close_session() as root.
	 */
	pid = fork();
	if (pid < 0) {
		err(1, "fork");
	} else if (pid != 0) {
		/*
		 * Parent: wait for child to finish, then clean up
		 * session.
		 */
		int status;
		setproctitle("-%s [pam]", getprogname());
		waitpid(pid, &status, 0);
		bail(NO_SLEEP_EXIT, 0);
	}

	/*
	 * NOTICE: We are now in the child process!
	 */

	/*
	 * Add any environment variables the PAM modules may have set.
	 */
	export_pam_environment();

	/*
	 * We're done with PAM now; our parent will deal with the rest.
	 */
	pam_end(pamh, 0);
	pamh = NULL;

	/*
	 * We don't need to be root anymore, so set the login name and
	 * the UID.
	 */
	if (setlogin(username) != 0) {
		syslog(LOG_ERR, "setlogin(%s): %m - exiting", username);
		bail(NO_SLEEP_EXIT, 1);
	}
	if (setusercontext(lc, pwd, pwd->pw_uid,
	    LOGIN_SETALL & ~(LOGIN_SETLOGIN|LOGIN_SETGROUP)) != 0) {
		syslog(LOG_ERR, "setusercontext() failed - exiting");
		exit(1);
	}

	if (setenv("SHELL", pwd->pw_shell, 1) == -1)
		err(1, "setenv: cannot set SHELL=%s", pwd->pw_shell);
	if (setenv("HOME", pwd->pw_dir, 1) == -1)
		err(1, "setenv: cannot set HOME=%s", pwd->pw_dir);
	/* Overwrite "term" from login.conf(5) for any known TERM */
	if (term == NULL && (tp = stypeof(tty)) != NULL) {
		if (setenv("TERM", tp, 1) == -1)
			err(1, "setenv: cannot set TERM=%s", tp);
	} else {
		if (setenv("TERM", TERM_UNKNOWN, 0) == -1)
			err(1, "setenv: cannot set TERM=%s", TERM_UNKNOWN);
	}

	if (setenv("LOGNAME", username, 1) == -1)
		err(1, "setenv: cannot set LOGNAME=%s", username);
	if (setenv("USER", username, 1) == -1)
		err(1, "setenv: cannot set USER=%s", username);
	if (setenv("PATH",
	    rootlogin ? _PATH_STDPATH : _PATH_DEFPATH, 0) == -1) {
		err(1, "setenv: cannot set PATH=%s",
		    rootlogin ? _PATH_STDPATH : _PATH_DEFPATH);
	}

	if (!quietlog) {
		const char *cw;

		cw = login_getcapstr(lc, "copyright", NULL, NULL);
		if (cw == NULL || motd(cw) == -1)
			printf("%s", copyright);

		printf("\n");

		cw = login_getcapstr(lc, "welcome", NULL, NULL);
		if (cw != NULL && access(cw, F_OK) == 0)
			motd(cw);
		else
			motd(_PATH_MOTDFILE);

		if (login_getcapbool(lc_user, "nocheckmail", 0) == 0 &&
		    login_getcapbool(lc, "nocheckmail", 0) == 0) {
			char *cx;

			/* $MAIL may have been set by class. */
			cx = getenv("MAIL");
			if (cx == NULL) {
				asprintf(&cx, "%s/%s",
				    _PATH_MAILDIR, pwd->pw_name);
			}
			if (cx && stat(cx, &st) == 0 && st.st_size != 0)
				printf("You have %smail.\n",
				    (st.st_mtime > st.st_atime) ? "new " : "");
			if (getenv("MAIL") == NULL)
				free(cx);
		}
	}

	login_close(lc_user);
	login_close(lc);

	signal(SIGALRM, SIG_DFL);
	signal(SIGQUIT, SIG_DFL);
	signal(SIGINT, SIG_DFL);
	signal(SIGTSTP, SIG_IGN);

	/*
	 * Login shells have a leading '-' in front of argv[0]
	 */
	p = strrchr(pwd->pw_shell, '/');
	if (asprintf(&arg0, "-%s", p ? p + 1 : pwd->pw_shell) >= MAXPATHLEN) {
		syslog(LOG_ERR, "user: %s: shell exceeds maximum pathname size",
		    username);
		errx(1, "shell exceeds maximum pathname size");
	} else if (arg0 == NULL) {
		err(1, "asprintf()");
	}

	execlp(shell, arg0, NULL);
	err(1, "%s", shell);

	/*
	 * That's it, folks!
	 */
}
Example #13
0
static int
#if defined(USE_PAM) || defined(_AIX)
isNoPassAllowed( const char *un )
{
	struct passwd *pw = 0;
# ifdef HAVE_GETSPNAM /* (sic!) - not USESHADOW */
	struct spwd *spw;
# endif
#else
isNoPassAllowed( const char *un, struct passwd *pw )
{
#endif
	struct group *gr;
	char **fp;
	int hg;

	if (!*un)
		return 0;

	if (cursource != PWSRC_MANUAL)
		return 1;

	for (hg = 0, fp = td->noPassUsers; *fp; fp++)
		if (**fp == '@')
			hg = 1;
		else if (!strcmp( un, *fp ))
			return 1;
		else if (!strcmp( "*", *fp )) {
#if defined(USE_PAM) || defined(_AIX)
			if (!(pw = getpwnam( un )))
				return 0;
			if (pw->pw_passwd[0] == '!' || pw->pw_passwd[0] == '*')
				continue;
# ifdef HAVE_GETSPNAM /* (sic!) - not USESHADOW */
			if ((spw = getspnam( un )) &&
			    (spw->sp_pwdp[0] == '!' || spw->sp_pwdp[0] == '*'))
					continue;
# endif
#endif
			if (pw->pw_uid)
				return 1;
		}

#if defined(USE_PAM) || defined(_AIX)
	if (hg && (pw || (pw = getpwnam( un )))) {
#else
	if (hg) {
#endif
		for (setgrent(); (gr = getgrent()); )
			for (fp = td->noPassUsers; *fp; fp++)
				if (**fp == '@' && !strcmp( gr->gr_name, *fp + 1 )) {
					if (pw->pw_gid == gr->gr_gid) {
						endgrent();
						return 1;
					}
					for (; *gr->gr_mem; gr->gr_mem++)
						if (!strcmp( un, *gr->gr_mem )) {
							endgrent();
							return 1;
						}
				}
		endgrent();
	}

	return 0;
}

#if !defined(USE_PAM) && !defined(_AIX) && defined(HAVE_SETUSERCONTEXT)
# define LC_RET0 do { login_close(lc); return 0; } while(0)
#else
# define LC_RET0 return 0
#endif

int
verify( GConvFunc gconv, int rootok )
{
#ifdef USE_PAM
	const char *psrv;
	struct pam_data pdata;
	int pretc, pnopass;
	char psrvb[64];
#elif defined(_AIX)
	char *msg, *curret;
	int i, reenter;
#else
	struct stat st;
	const char *nolg;
	char *buf;
	int fd;
# ifdef HAVE_GETUSERSHELL
	char *s;
# endif
# if defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || defined(USESHADOW)
	int tim, expir, warntime, quietlog;
# endif
#endif

	debug( "verify ...\n" );

#ifdef USE_PAM

	pnopass = FALSE;
	if (!strcmp( curtype, "classic" )) {
		if (!gconv( GCONV_USER, 0 ))
			return 0;
		if (isNoPassAllowed( curuser )) {
			gconv( GCONV_PASS_ND, 0 );
			if (!*curpass) {
				pnopass = TRUE;
				sprintf( psrvb, "%.31s-np", PAMService );
				psrv = psrvb;
			} else
				psrv = PAMService;
		} else
			psrv = PAMService;
		pdata.usecur = TRUE;
	} else {
		sprintf( psrvb, "%.31s-%.31s", PAMService, curtype );
		psrv = psrvb;
		pdata.usecur = FALSE;
	}
	pdata.gconv = gconv;
	if (!doPAMAuth( psrv, &pdata ))
		return 0;

#elif defined(_AIX)

	if ((td->displayType & d_location) == dForeign) {
		char *tmpch;
		strncpy( hostname, td->name, sizeof(hostname) - 1 );
		hostname[sizeof(hostname)-1] = '\0';
		if ((tmpch = strchr( hostname, ':' )))
			*tmpch = '\0';
	} else
		hostname[0] = '\0';

	/* tty names should only be 15 characters long */
# if 0
	for (i = 0; i < 15 && td->name[i]; i++) {
		if (td->name[i] == ':' || td->name[i] == '.')
			tty[i] = '_';
		else
			tty[i] = td->name[i];
	}
	tty[i] = '\0';
# else
	memcpy( tty, "/dev/xdm/", 9 );
	for (i = 0; i < 6 && td->name[i]; i++) {
		if (td->name[i] == ':' || td->name[i] == '.')
			tty[9 + i] = '_';
		else
			tty[9 + i] = td->name[i];
	}
	tty[9 + i] = '\0';
# endif

	if (!strcmp( curtype, "classic" )) {
		if (!gconv( GCONV_USER, 0 ))
			return 0;
		if (isNoPassAllowed( curuser )) {
			gconv( GCONV_PASS_ND, 0 );
			if (!*curpass) {
				debug( "accepting despite empty password\n" );
				goto done;
			}
		} else
			if (!gconv( GCONV_PASS, 0 ))
				return 0;
		enduserdb();
		msg = NULL;
		if ((i = authenticate( curuser, curpass, &reenter, &msg ))) {
			debug( "authenticate() failed: %s\n", msg );
			if (msg)
				free( msg );
			loginfailed( curuser, hostname, tty );
			if (i == ENOENT || i == ESAD)
				V_RET_AUTH;
			else
				V_RET_FAIL( 0 );
		}
		if (reenter) {
			logError( "authenticate() requests more data: %s\n", msg );
			free( msg );
			V_RET_FAIL( 0 );
		}
	} else if (!strcmp( curtype, "generic" )) {
		if (!gconv( GCONV_USER, 0 ))
			return 0;
		for (curret = 0;;) {
			msg = NULL;
			if ((i = authenticate( curuser, curret, &reenter, &msg ))) {
				debug( "authenticate() failed: %s\n", msg );
				if (msg)
					free( msg );
				loginfailed( curuser, hostname, tty );
				if (i == ENOENT || i == ESAD)
					V_RET_AUTH;
				else
					V_RET_FAIL( 0 );
			}
			if (curret)
				free( curret );
			if (!reenter)
				break;
			if (!(curret = gconv( GCONV_HIDDEN, msg )))
				return 0;
			free( msg );
		}
	} else {
		logError( "Unsupported authentication type %\"s requested\n", curtype );
		V_RET_FAIL( 0 );
	}
	if (msg) {
		displayStr( V_MSG_INFO, msg );
		free( msg );
	}

  done:

#else

	if (strcmp( curtype, "classic" )) {
		logError( "Unsupported authentication type %\"s requested\n", curtype );
		V_RET_FAIL( 0 );
	}

	if (!gconv( GCONV_USER, 0 ))
		return 0;

	if (!(p = getpwnam( curuser ))) {
		debug( "getpwnam() failed.\n" );
		gconv( GCONV_PASS, 0 );
		V_RET_AUTH;
	}
	if (p->pw_passwd[0] == '!' || p->pw_passwd[0] == '*') {
		debug( "account is locked\n" );
		gconv( GCONV_PASS, 0 );
		V_RET_AUTH;
	}

# ifdef USESHADOW
	if ((sp = getspnam( curuser ))) {
		p->pw_passwd = sp->sp_pwdp;
		if (p->pw_passwd[0] == '!' || p->pw_passwd[0] == '*') {
			debug( "account is locked\n" );
			gconv( GCONV_PASS, 0 );
			V_RET_AUTH;
		}
	} else
		debug( "getspnam() failed: %m. Are you root?\n" );
# endif

	if (!*p->pw_passwd) {
		if (!td->allowNullPasswd) {
			debug( "denying user with empty password\n" );
			gconv( GCONV_PASS, 0 );
			V_RET_AUTH;
		}
		goto nplogin;
	}

	if (isNoPassAllowed( curuser, p )) {
	  nplogin:
		gconv( GCONV_PASS_ND, 0 );
		if (!*curpass) {
			debug( "accepting password-less login\n" );
			goto done;
		}
	} else
		if (!gconv( GCONV_PASS, 0 ))
			return 0;

# ifdef KERBEROS
	if (p->pw_uid) {
		int ret;
		char realm[REALM_SZ];

		if (krb_get_lrealm( realm, 1 )) {
			logError( "Cannot get KerberosIV realm.\n" );
			V_RET_FAIL( 0 );
		}

		sprintf( krbtkfile, "%s.%.*s", TKT_ROOT, MAXPATHLEN - strlen( TKT_ROOT ) - 2, td->name );
		krb_set_tkt_string( krbtkfile );
		unlink( krbtkfile );

		ret = krb_verify_user( curuser, "", realm, curpass, 1, "rcmd" );
		if (ret == KSUCCESS) {
			chown( krbtkfile, p->pw_uid, p->pw_gid );
			debug( "KerberosIV verify succeeded\n" );
			goto done;
		} else if (ret != KDC_PR_UNKNOWN && ret != SKDC_CANT) {
			logError( "KerberosIV verification failure %\"s for %s\n",
			          krb_get_err_text( ret ), curuser );
			krbtkfile[0] = '\0';
			V_RET_FAIL( 0 );
		}
		debug( "KerberosIV verify failed: %s\n", krb_get_err_text( ret ) );
	}
	krbtkfile[0] = '\0';
# endif	 /* KERBEROS */

# if defined(ultrix) || defined(__ultrix__)
	if (authenticate_user( p, curpass, NULL ) < 0)
# elif defined(HAVE_PW_ENCRYPT)
	if (strcmp( pw_encrypt( curpass, p->pw_passwd ), p->pw_passwd ))
# elif defined(HAVE_CRYPT)
	if (strcmp( crypt( curpass, p->pw_passwd ), p->pw_passwd ))
# else
	if (strcmp( curpass, p->pw_passwd ))
# endif
	{
		debug( "password verify failed\n" );
		V_RET_AUTH;
	}

  done:

#endif /* !defined(USE_PAM) && !defined(_AIX) */

	debug( "restrict %s ...\n", curuser );

#if defined(USE_PAM) || defined(_AIX)
	if (!(p = getpwnam( curuser ))) {
		logError( "getpwnam(%s) failed.\n", curuser );
		V_RET_FAIL( 0 );
	}
#endif
	if (!p->pw_uid) {
		if (!rootok && !td->allowRootLogin)
			V_RET_FAIL( "Root logins are not allowed" );
		return 1; /* don't deny root to log in */
	}

#ifdef USE_PAM

	debug( " pam_acct_mgmt() ...\n" );
	pretc = pam_acct_mgmt( pamh, 0 );
	reInitErrorLog();
	debug( " pam_acct_mgmt() returned: %s\n", pam_strerror( pamh, pretc ) );
	if (pretc == PAM_NEW_AUTHTOK_REQD) {
		pdata.usecur = FALSE;
		pdata.gconv = conv_interact;
		/* pam will have output a message already, so no prepareErrorGreet() */
		if (gconv != conv_interact || pnopass) {
			pam_end( pamh, PAM_SUCCESS );
			pamh = 0;
			gSendInt( V_CHTOK_AUTH );
			/* this cannot auth the wrong user, as only classic auths get here */
			while (!doPAMAuth( PAMService, &pdata ))
				if (pdata.abort)
					return 0;
			gSendInt( V_PRE_OK );
		} else
			gSendInt( V_CHTOK );
		for (;;) {
			debug( " pam_chauthtok() ...\n" );
			pretc = pam_chauthtok( pamh, PAM_CHANGE_EXPIRED_AUTHTOK );
			reInitErrorLog();
			debug( " pam_chauthtok() returned: %s\n", pam_strerror( pamh, pretc ) );
			if (pdata.abort) {
				pam_end( pamh, PAM_SUCCESS );
				pamh = 0;
				return 0;
			}
			if (pretc == PAM_SUCCESS)
				break;
			/* effectively there is only PAM_AUTHTOK_ERR */
			gSendInt( V_FAIL );
		}
		if (curpass)
			free( curpass );
		curpass = newpass;
		newpass = 0;
	} else if (pretc != PAM_SUCCESS) {
		pam_end( pamh, pretc );
		pamh = 0;
		V_RET_AUTH;
	}

#elif defined(_AIX) /* USE_PAM */

	msg = NULL;
	if (loginrestrictions( curuser,
	                       ((td->displayType & d_location) == dForeign) ? S_RLOGIN : S_LOGIN,
	                       tty, &msg ) == -1)
	{
		debug( "loginrestrictions() - %s\n", msg ? msg : "error" );
		loginfailed( curuser, hostname, tty );
		prepareErrorGreet();
		if (msg) {
			displayStr( V_MSG_ERR, msg );
			free( msg );
		}
		gSendInt( V_AUTH );
		return 0;
	}
	if (msg)
		free( (void *)msg );

#endif /* USE_PAM || _AIX */

#ifndef _AIX

# ifdef HAVE_SETUSERCONTEXT
#  ifdef HAVE_LOGIN_GETCLASS
	lc = login_getclass( p->pw_class );
#  else
	lc = login_getpwclass( p );
#  endif
	if (!lc)
		V_RET_FAIL( 0 );

	p->pw_shell = login_getcapstr( lc, "shell", p->pw_shell, p->pw_shell );
# endif

# ifndef USE_PAM

/* restrict_expired */
#  if defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || defined(USESHADOW)

#   if !defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || (!defined(HAVE_SETUSERCONTEXT) && defined(USESHADOW))
	if (sp)
#   endif
	{

#   define DEFAULT_WARN	(2L * 7L)  /* Two weeks */

		tim = time( NULL ) / 86400L;

#   ifdef HAVE_SETUSERCONTEXT
		quietlog = login_getcapbool( lc, "hushlogin", 0 );
		warntime = login_getcaptime( lc, "warnexpire",
		                             DEFAULT_WARN * 86400L,
		                             DEFAULT_WARN * 86400L ) / 86400L;
#   else
		quietlog = 0;
#    ifdef USESHADOW
		warntime = sp->sp_warn != -1 ? sp->sp_warn : DEFAULT_WARN;
#    else
		warntime = DEFAULT_WARN;
#    endif
#   endif

#   ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE
		if (p->pw_expire) {
			expir = p->pw_expire / 86400L;
#   else
		if (sp->sp_expire != -1) {
			expir = sp->sp_expire;
#   endif
			if (tim > expir) {
				displayStr( V_MSG_ERR,
				            "Your account has expired;"
				            " please contact your system administrator" );
				gSendInt( V_FAIL );
				LC_RET0;
			} else if (tim > (expir - warntime) && !quietlog) {
				displayMsg( V_MSG_INFO,
				            "Warning: your account will expire in %d day(s)",
				            expir - tim );
			}
		}

#   ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE
		if (p->pw_change) {
			expir = p->pw_change / 86400L;
#   else
		if (!sp->sp_lstchg) {
			displayStr( V_MSG_ERR,
			            "You are required to change your password immediately"
			            " (root enforced)" );
			/* XXX todo password change */
			gSendInt( V_FAIL );
			LC_RET0;
		} else if (sp->sp_max != -1) {
			expir = sp->sp_lstchg + sp->sp_max;
			if (sp->sp_inact != -1 && tim > expir + sp->sp_inact) {
				displayStr( V_MSG_ERR,
				            "Your account has expired;"
				            " please contact your system administrator" );
				gSendInt( V_FAIL );
				LC_RET0;
			}
#   endif
			if (tim > expir) {
				displayStr( V_MSG_ERR,
				            "You are required to change your password immediately"
				            " (password aged)" );
				/* XXX todo password change */
				gSendInt( V_FAIL );
				LC_RET0;
			} else if (tim > (expir - warntime) && !quietlog) {
				displayMsg( V_MSG_INFO,
				            "Warning: your password will expire in %d day(s)",
				            expir - tim );
			}
		}

	}

#  endif /* HAVE_STRUCT_PASSWD_PW_EXPIRE || USESHADOW */

/* restrict_nologin */
#  ifndef _PATH_NOLOGIN
#   define _PATH_NOLOGIN "/etc/nologin"
#  endif

	if ((
#  ifdef HAVE_SETUSERCONTEXT
	     /* Do we ignore a nologin file? */
	     !login_getcapbool( lc, "ignorenologin", 0 )) &&
	    (!stat( (nolg = login_getcapstr( lc, "nologin", "", NULL )), &st ) ||
#  endif
		 !stat( (nolg = _PATH_NOLOGIN), &st )))
	{
		if (st.st_size && (fd = open( nolg, O_RDONLY )) >= 0) {
			if ((buf = Malloc( st.st_size + 1 ))) {
				if (read( fd, buf, st.st_size ) == st.st_size) {
					close( fd );
					buf[st.st_size] = 0;
					displayStr( V_MSG_ERR, buf );
					free( buf );
					gSendInt( V_FAIL );
					LC_RET0;
				}
				free( buf );
			}
			close( fd );
		}
		displayStr( V_MSG_ERR,
		            "Logins are not allowed at the moment.\nTry again later" );
		gSendInt( V_FAIL );
		LC_RET0;
	}

/* restrict_time */
#  if defined(HAVE_SETUSERCONTEXT) && defined(HAVE_AUTH_TIMEOK)
	if (!auth_timeok( lc, time( NULL ) )) {
		displayStr( V_MSG_ERR,
		            "You are not allowed to login at the moment" );
		gSendInt( V_FAIL );
		LC_RET0;
	}
#  endif

#  ifdef HAVE_GETUSERSHELL
	for (;;) {
		if (!(s = getusershell())) {
			debug( "shell not in /etc/shells\n" );
			endusershell();
			V_RET_FAIL( "Your login shell is not listed in /etc/shells" );
		}
		if (!strcmp( s, p->pw_shell )) {
			endusershell();
			break;
		}
	}
#  endif

# endif /* !USE_PAM */

/* restrict_nohome */
# ifdef HAVE_SETUSERCONTEXT
	if (login_getcapbool( lc, "requirehome", 0 )) {
		struct stat st;
		if (!*p->pw_dir || stat( p->pw_dir, &st ) || st.st_uid != p->pw_uid) {
			displayStr( V_MSG_ERR, "Home folder not available" );
			gSendInt( V_FAIL );
			LC_RET0;
		}
	}
# endif

#endif /* !_AIX */

	return 1;

}


static const char *envvars[] = {
	"TZ", /* SYSV and SVR4, but never hurts */
#ifdef _AIX
	"AUTHSTATE", /* for kerberos */
#endif
	NULL
};


#if defined(USE_PAM) && defined(HAVE_INITGROUPS)
static int num_saved_gids;
static gid_t *saved_gids;

static int
saveGids( void )
{
	num_saved_gids = getgroups( 0, 0 );
	if (!(saved_gids = Malloc( sizeof(gid_t) * num_saved_gids )))
		return 0;
	if (getgroups( num_saved_gids, saved_gids ) < 0) {
		logError( "saving groups failed: %m\n" );
		return 0;
	}
	return 1;
}

static int
restoreGids( void )
{
	if (setgroups( num_saved_gids, saved_gids ) < 0) {
		logError( "restoring groups failed: %m\n" );
		return 0;
	}
	if (setgid( p->pw_gid ) < 0) {
		logError( "restoring gid failed: %m\n" );
		return 0;
	}
	return 1;
}
#endif /* USE_PAM && HAVE_INITGROUPS */

static int
resetGids( void )
{
#ifdef HAVE_INITGROUPS
	if (setgroups( 0, &p->pw_gid /* anything */ ) < 0) {
		logError( "restoring groups failed: %m\n" );
		return 0;
	}
#endif
	if (setgid( 0 ) < 0) {
		logError( "restoring gid failed: %m\n" );
		return 0;
	}
	return 1;
}

static int
setGid( const char *name, int gid )
{
	if (setgid( gid ) < 0) {
		logError( "setgid(%d) (user %s) failed: %m\n", gid, name );
		return 0;
	}
#ifdef HAVE_INITGROUPS
	if (initgroups( name, gid ) < 0) {
		logError( "initgroups for %s failed: %m\n", name );
		setgid( 0 );
		return 0;
	}
#endif	 /* QNX4 doesn't support multi-groups, no initgroups() */
	return 1;
}

static int
setUid( const char *name, int uid )
{
	if (setuid( uid ) < 0) {
		logError( "setuid(%d) (user %s) failed: %m\n", uid, name );
		return 0;
	}
	return 1;
}

static int
setUser( const char *name, int uid, int gid )
{
	if (setGid( name, gid )) {
		if (setUid( name, uid ))
			return 1;
		resetGids();
	}
	return 0;
}

#if defined(SECURE_RPC) || defined(K5AUTH)
static void
nukeAuth( int len, const char *name )
{
	int i;

	for (i = 0; i < td->authNum; i++)
		if (td->authorizations[i]->name_length == len &&
		    !memcmp( td->authorizations[i]->name, name, len ))
		{
			memcpy( &td->authorizations[i], &td->authorizations[i+1],
			        sizeof(td->authorizations[i]) * (--td->authNum - i) );
			break;
		}
}
#endif

static void
mergeSessionArgs( int cansave )
{
	char *mfname;
	const char *fname;
	int i, needsave;

	mfname = 0;
	fname = ".dmrc";
	if ((!curdmrc || newdmrc) && *dmrcDir)
		if (strApp( &mfname, dmrcDir, "/", curuser, fname, (char *)0 ))
			fname = mfname;
	needsave = 0;
	if (!curdmrc) {
		curdmrc = iniLoad( fname );
		if (!curdmrc) {
			strDup( &curdmrc, "[Desktop]\nSession=default\n" );
			needsave = 1;
		}
	}
	if (newdmrc) {
		curdmrc = iniMerge( curdmrc, newdmrc );
		needsave = 1;
	}
	if (needsave && cansave)
		if (!iniSave( curdmrc, fname ) && errno == ENOENT && mfname) {
			for (i = 0; mfname[i]; i++)
				if (mfname[i] == '/') {
					mfname[i] = 0;
					mkdir( mfname, 0755 );
					mfname[i] = '/';
				}
			iniSave( curdmrc, mfname );
		}
	if (mfname)
		free( mfname );
}

static int
createClientLog( const char *log )
{
	char randstr[32], *randstrp = 0, *lname;
	int lfd;

	for (;;) {
		struct expando macros[] = {
			{ 'd', 0, td->name },
			{ 'u', 0, curuser },
			{ 'r', 0, randstrp },
			{ 0, 0, 0 }
		};
		if (!(lname = expandMacros( log, macros )))
			exit( 1 );
		unlink( lname );
		if ((lfd = open( lname, O_WRONLY|O_CREAT|O_EXCL, 0600 )) >= 0) {
			dup2( lfd, 1 );
			dup2( lfd, 2 );
			close( lfd );
			free( lname );
			return TRUE;
		}
		if (errno != EEXIST || !macros[2].uses) {
			free( lname );
			return FALSE;
		}
		logInfo( "Session log file %s not usable, trying another one.\n",
		         lname );
		free( lname );
		sprintf( randstr, "%d", secureRandom() );
		randstrp = randstr;
	}
}
Example #14
0
int
main(int argc, char *argv[])
{
	char *domain, *p, *ttyn, *shell, *fullname, *instance;
	char *lipaddr, *script, *ripaddr, *style, *type, *fqdn;
	char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_TTY) + 10];
	char localhost[MAXHOSTNAMELEN], *copyright;
	char mail[sizeof(_PATH_MAILDIR) + 1 + NAME_MAX];
	int ask, ch, cnt, fflag, pflag, quietlog, rootlogin, lastchance;
	int error, homeless, needto, authok, tries, backoff;
	struct addrinfo *ai, hints;
	struct rlimit cds, scds;
	quad_t expire, warning;
	struct utmp utmp;
	struct group *gr;
	struct stat st;
	uid_t uid;

	openlog("login", LOG_ODELAY, LOG_AUTH);

	fqdn = lipaddr = ripaddr = fullname = type = NULL;
	authok = 0;
	tries = 10;
	backoff = 3;

	domain = NULL;
	if (gethostname(localhost, sizeof(localhost)) < 0) {
		syslog(LOG_ERR, "couldn't get local hostname: %m");
		strlcpy(localhost, "localhost", sizeof(localhost));
	} else if ((domain = strchr(localhost, '.'))) {
		domain++;
		if (*domain && strchr(domain, '.') == NULL)
			domain = localhost;
	}

	if ((as = auth_open()) == NULL) {
		syslog(LOG_ERR, "auth_open: %m");
		err(1, "unable to initialize BSD authentication");
	}
	auth_setoption(as, "login", "yes");

	/*
	 * -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
	 */
	fflag = pflag = 0;
	uid = getuid();
	while ((ch = getopt(argc, argv, "fh:pu:L:R:")) != -1)
		switch (ch) {
		case 'f':
			fflag = 1;
			break;
		case 'h':
			if (uid) {
				warnc(EPERM, "-h option");
				quickexit(1);
			}
			free(fqdn);
			if ((fqdn = strdup(optarg)) == NULL) {
				warn(NULL);
				quickexit(1);
			}
			auth_setoption(as, "fqdn", fqdn);
			if (domain && (p = strchr(optarg, '.')) &&
			    strcasecmp(p+1, domain) == 0)
				*p = 0;
			hostname = optarg;
			auth_setoption(as, "hostname", hostname);
			break;
		case 'L':
			if (uid) {
				warnc(EPERM, "-L option");
				quickexit(1);
			}
			if (lipaddr) {
				warnx("duplicate -L option");
				quickexit(1);
			}
			lipaddr = optarg;
			memset(&hints, 0, sizeof(hints));
			hints.ai_family = PF_UNSPEC;
			hints.ai_flags = AI_CANONNAME;
			error = getaddrinfo(lipaddr, NULL, &hints, &ai);
			if (!error) {
				strlcpy(localhost, ai->ai_canonname,
				    sizeof(localhost));
				freeaddrinfo(ai);
			} else
				strlcpy(localhost, lipaddr, sizeof(localhost));
			auth_setoption(as, "local_addr", lipaddr);
			break;
		case 'p':
			pflag = 1;
			break;
		case 'R':
			if (uid) {
				warnc(EPERM, "-R option");
				quickexit(1);
			}
			if (ripaddr) {
				warnx("duplicate -R option");
				quickexit(1);
			}
			ripaddr = optarg;
			auth_setoption(as, "remote_addr", ripaddr);
			break;
		case 'u':
			if (uid) {
				warnc(EPERM, "-u option");
				quickexit(1);
			}
			rusername = optarg;
			break;
		default:
			if (!uid)
				syslog(LOG_ERR, "invalid flag %c", ch);
			(void)fprintf(stderr,
			    "usage: login [-fp] [-h hostname] [-L local-addr] "
			    "[-R remote-addr] [-u username]\n\t[user]\n");
			quickexit(1);
		}
	argc -= optind;
	argv += optind;

	if (*argv) {
		username = *argv;
		ask = 0;
	} else
		ask = 1;

	/*
	 * If effective user is not root, just run su(1) to emulate login(1).
	 */
	if (geteuid() != 0) {
		char *av[5], **ap;

		auth_close(as);
		closelog();
		closefrom(STDERR_FILENO + 1);

		ap = av;
		*ap++ = _PATH_SU;
		*ap++ = "-L";
		if (!pflag)
			*ap++ = "-l";
		if (!ask)
			*ap++ = username;
		*ap = NULL;
		execv(_PATH_SU, av);
		warn("unable to exec %s", _PATH_SU);
		_exit(1);
	}

	ttyn = ttyname(STDIN_FILENO);
	if (ttyn == NULL || *ttyn == '\0') {
		(void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY);
		ttyn = tname;
	}
	if ((tty = strrchr(ttyn, '/')))
		++tty;
	else
		tty = ttyn;

	/*
	 * Since login deals with sensitive information, turn off coredumps.
	 */
	if (getrlimit(RLIMIT_CORE, &scds) < 0) {
		syslog(LOG_ERR, "couldn't get core dump size: %m");
		scds.rlim_cur = scds.rlim_max = QUAD_MIN;
	}
	cds.rlim_cur = cds.rlim_max = 0;
	if (setrlimit(RLIMIT_CORE, &cds) < 0) {
		syslog(LOG_ERR, "couldn't set core dump size to 0: %m");
		scds.rlim_cur = scds.rlim_max = QUAD_MIN;
	}

	(void)signal(SIGALRM, timedout);
	if (argc > 1) {
		needto = 0;
		(void)alarm(timeout);
	} else
		needto = 1;
	(void)signal(SIGQUIT, SIG_IGN);
	(void)signal(SIGINT, SIG_IGN);
	(void)signal(SIGHUP, SIG_IGN);
	(void)setpriority(PRIO_PROCESS, 0, 0);

#ifdef notyet
	/* XXX - we don't (yet) support per-tty auth stuff */
	/* BSDi uses a ttys.conf file but we could just overload /etc/ttys */
	/*
	 * Classify the attempt.
	 * By default we use the value in the ttys file.
	 * If there is a classify script we run that as
	 *
	 *	classify [-f] [username]
	 */
	if (type = getttyauth(tty))
		auth_setoption(as, "auth_type", type);
#endif

	/* get the default login class */
	if ((lc = login_getclass(0)) == NULL) { /* get the default class */
		warnx("Failure to retrieve default class");
		quickexit(1);
	}
	timeout = (u_int)login_getcapnum(lc, "login-timeout", 300, 300);
	if ((script = login_getcapstr(lc, "classify", NULL, NULL)) != NULL) {
		unsetenv("AUTH_TYPE");
		unsetenv("REMOTE_NAME");
		if (script[0] != '/') {
			syslog(LOG_ERR, "Invalid classify script: %s", script);
			warnx("Classification failure");
			quickexit(1);
		}
		shell = strrchr(script, '/') + 1;
		auth_setstate(as, AUTH_OKAY);
		auth_call(as, script, shell,
		    fflag ? "-f" : username, fflag ? username : 0, (char *)0);
		if (!(auth_getstate(as) & AUTH_ALLOW))
			quickexit(1);
		auth_setenv(as);
		if ((p = getenv("AUTH_TYPE")) != NULL &&
		    strncmp(p, "auth-", 5) == 0)
			type = p;
		if ((p = getenv("REMOTE_NAME")) != NULL)
			hostname = p;
		/*
		 * we may have changed some values, reset them
		 */
		auth_clroptions(as);
		if (type)
			auth_setoption(as, "auth_type", type);
		if (fqdn)
			auth_setoption(as, "fqdn", fqdn);
		if (hostname)
			auth_setoption(as, "hostname", hostname);
		if (lipaddr)
			auth_setoption(as, "local_addr", lipaddr);
		if (ripaddr)
			auth_setoption(as, "remote_addr", ripaddr);
	}

	/*
	 * Request the things like the approval script print things
	 * to stdout (in particular, the nologins files)
	 */
	auth_setitem(as, AUTHV_INTERACTIVE, "True");

	for (cnt = 0;; ask = 1) {
		/*
		 * Clean up our current authentication session.
		 * Options are not cleared so we need to clear any
		 * we might set below.
		 */
		auth_clean(as);
		auth_clroption(as, "style");
		auth_clroption(as, "lastchance");

		lastchance = 0;

		if (ask) {
			fflag = 0;
			getloginname();
		}
		if (needto) {
			needto = 0;
			alarm(timeout);
		}
		if ((style = strchr(username, ':')) != NULL)
			*style++ = '\0';
		if (fullname)
			free(fullname);
		if (auth_setitem(as, AUTHV_NAME, username) < 0 ||
		    (fullname = strdup(username)) == NULL) {
			syslog(LOG_ERR, "%m");
			warn(NULL);
			quickexit(1);
		}
		rootlogin = 0;
		if ((instance = strchr(username, '/')) != NULL) {
			if (strncmp(instance + 1, "root", 4) == 0)
				rootlogin = 1;
			*instance++ = '\0';
		} else
			instance = "";

		if (strlen(username) > UT_NAMESIZE)
			username[UT_NAMESIZE] = '\0';

		/*
		 * Note if trying multiple user names; log failures for
		 * previous user name, but don't bother logging one failure
		 * for nonexistent name (mistyped username).
		 */
		if (failures && strcmp(tbuf, username)) {
			if (failures > (pwd ? 0 : 1))
				badlogin(tbuf);
			failures = 0;
		}
		(void)strlcpy(tbuf, username, sizeof(tbuf));

		if ((pwd = getpwnam(username)) != NULL &&
		    auth_setpwd(as, pwd) < 0) {
			syslog(LOG_ERR, "%m");
			warn(NULL);
			quickexit(1);
		}

		lc = login_getclass(pwd ? pwd->pw_class : NULL);
		if (!lc)
			goto failed;

		style = login_getstyle(lc, style, type);
		if (!style)
			goto failed;

		/*
		 * We allow "login-tries" attempts to login but start
		 * slowing down after "login-backoff" attempts.
		 */
		tries = (int)login_getcapnum(lc, "login-tries", 10, 10);
		backoff = (int)login_getcapnum(lc, "login-backoff", 3, 3);

		/*
		 * Turn off the fflag if we have an invalid user
		 * or we are not root and we are trying to change uids.
		 */
		if (!pwd || (uid && uid != pwd->pw_uid))
			fflag = 0;

		if (pwd && pwd->pw_uid == 0)
			rootlogin = 1;

		/*
		 * If we do not have the force flag authenticate the user
		 */
		if (!fflag) {
			lastchance =
			    login_getcaptime(lc, "password-dead", 0, 0) != 0;
			if (lastchance)
				auth_setoption(as, "lastchance", "yes");
			/*
			 * Once we start asking for a password
			 *  we want to log a failure on a hup.
			 */
			signal(SIGHUP, sighup);
			auth_verify(as, style, NULL, lc->lc_class, NULL);
			authok = auth_getstate(as);
			/*
			 * If their password expired and it has not been
			 * too long since then, give the user one last
			 * chance to change their password
			 */
			if ((authok & AUTH_PWEXPIRED) && lastchance) {
				authok = AUTH_OKAY;
			} else
				lastchance = 0;
			if ((authok & AUTH_ALLOW) == 0)
				goto failed;
			if (auth_setoption(as, "style", style) < 0) {
				syslog(LOG_ERR, "%m");
				warn(NULL);
				quickexit(1);
			}
		}
		/*
		 * explicitly reject users without password file entries
		 */
		if (pwd == NULL)
			goto failed;

		/*
		 * If trying to log in as root on an insecure terminal,
		 * refuse the login attempt unless the authentication
		 * style explicitly says a root login is okay.
		 */
		if (pwd && rootlogin && !rootterm(tty))
			goto failed;

		if (fflag) {
			type = 0;
			style = "forced";
		}
		break;

failed:
		if (authok & AUTH_SILENT)
			quickexit(0);
		if (rootlogin && !rootterm(tty)) {
			warnx("%s login refused on this terminal.",
			    fullname);
			if (hostname)
				syslog(LOG_NOTICE,
				    "LOGIN %s REFUSED FROM %s%s%s ON TTY %s",
				    fullname, rusername ? rusername : "",
				    rusername ? "@" : "", hostname, tty);
			else
				syslog(LOG_NOTICE,
				    "LOGIN %s REFUSED ON TTY %s",
				    fullname, tty);
		} else {
			if (!as || (p = auth_getvalue(as, "errormsg")) == NULL)
				p = "Login incorrect";
			(void)printf("%s\n", p);
		}
		failures++;
		if (pwd)
			log_failedlogin(pwd->pw_uid, hostname, rusername, tty);
		/*
		 * By default, we allow 10 tries, but after 3 we start
		 * backing off to slow down password guessers.
		 */
		if (++cnt > backoff) {
			if (cnt >= tries) {
				badlogin(username);
				sleepexit(1);
			}
			sleep((u_int)((cnt - backoff) * tries / 2));
		}
	}

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

	endpwent();

	shell = login_getcapstr(lc, "shell", pwd->pw_shell, pwd->pw_shell);
	if (*shell == '\0')
		shell = _PATH_BSHELL;
	else if (strlen(shell) >= MAXPATHLEN) {
		syslog(LOG_ERR, "shell path too long: %s", shell);
		warnx("invalid shell");
		quickexit(1);
	}

	/* Destroy environment unless user has requested its preservation. */
	if (!pflag) {
		if ((environ = calloc(1, sizeof (char *))) == NULL)
			err(1, "calloc");
	} else {
		char **cpp, **cpp2;

		for (cpp2 = cpp = environ; *cpp; cpp++) {
			if (strncmp(*cpp, "LD_", 3) &&
			    strncmp(*cpp, "ENV=", 4) &&
			    strncmp(*cpp, "BASH_ENV=", 9) &&
			    strncmp(*cpp, "IFS=", 4))
				*cpp2++ = *cpp;
		}
		*cpp2 = 0;
	}
	/* Note: setusercontext(3) will set PATH */
	if (setenv("HOME", pwd->pw_dir, 1) == -1 ||
	    setenv("SHELL", pwd->pw_shell, 1) == -1) {
		warn("unable to setenv()");
		quickexit(1);
	}
	if (term[0] == '\0')
		(void)strlcpy(term, stypeof(tty), sizeof(term));
	(void)snprintf(mail, sizeof(mail), "%s/%s", _PATH_MAILDIR,
		pwd->pw_name);
	if (setenv("TERM", term, 0) == -1 ||
	    setenv("LOGNAME", pwd->pw_name, 1) == -1 ||
	    setenv("USER", pwd->pw_name, 1) == -1 ||
	    setenv("MAIL", mail, 1) == -1) {
		warn("unable to setenv()");
		quickexit(1);
	}
	if (hostname) {
		if (setenv("REMOTEHOST", hostname, 1) == -1) {
			warn("unable to setenv()");
			quickexit(1);
		}
	}
	if (rusername) {
		if (setenv("REMOTEUSER", rusername, 1) == -1) {
			warn("unable to setenv()");
			quickexit(1);
		}
	}

	if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH)) {
		warn("unable to set user context");
		quickexit(1);
	}
	auth_setenv(as);

	/* if user not super-user, check for disabled logins */
	if (!rootlogin)
		auth_checknologin(lc);

	setegid(pwd->pw_gid);
	seteuid(pwd->pw_uid);

	homeless = chdir(pwd->pw_dir);
	if (homeless) {
		if (login_getcapbool(lc, "requirehome", 0)) {
			(void)printf("No home directory %s!\n", pwd->pw_dir);
			quickexit(1);
		}
		if (chdir("/"))
			quickexit(0);
	}

	quietlog = ((strcmp(pwd->pw_shell, "/sbin/nologin") == 0) ||
	    login_getcapbool(lc, "hushlogin", 0) ||
	    (access(_PATH_HUSHLOGIN, F_OK) == 0));

	seteuid(0);
	setegid(0);	/* XXX use a saved gid instead? */

	if ((p = auth_getvalue(as, "warnmsg")) != NULL)
		(void)printf("WARNING: %s\n\n", p);

	expire = auth_check_expire(as);
	if (expire < 0) {
		(void)printf("Sorry -- your account has expired.\n");
		quickexit(1);
	} else if (expire > 0 && !quietlog) {
		warning = login_getcaptime(lc, "expire-warn",
		    2 * DAYSPERWEEK * SECSPERDAY, 2 * DAYSPERWEEK * SECSPERDAY);
		if (expire < warning)
			(void)printf("Warning: your account expires on %s",
			    ctime(&pwd->pw_expire));
	}

	/* Nothing else left to fail -- really log in. */
	(void)signal(SIGHUP, SIG_DFL);
	memset(&utmp, 0, sizeof(utmp));
	(void)time(&utmp.ut_time);
	(void)strncpy(utmp.ut_name, username, sizeof(utmp.ut_name));
	if (hostname)
		(void)strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host));
	(void)strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line));
	login(&utmp);

	if (!quietlog)
		(void)check_failedlogin(pwd->pw_uid);
	dolastlog(quietlog);

	login_fbtab(tty, pwd->pw_uid, pwd->pw_gid);

	(void)chown(ttyn, pwd->pw_uid,
	    (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid);

	/* If fflag is on, assume caller/authenticator has logged root login. */
	if (rootlogin && fflag == 0) {
		if (hostname)
			syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s%s%s",
			    username, tty, rusername ? rusername : "",
			    rusername ? "@" : "", hostname);
		else
			syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s", username, tty);
	}

	if (!quietlog) {
		if ((copyright =
		    login_getcapstr(lc, "copyright", NULL, NULL)) != NULL)
			auth_cat(copyright);
		motd();
		if (stat(mail, &st) == 0 && st.st_size != 0)
			(void)printf("You have %smail.\n",
			    (st.st_mtime > st.st_atime) ? "new " : "");
	}

	(void)signal(SIGALRM, SIG_DFL);
	(void)signal(SIGQUIT, SIG_DFL);
	(void)signal(SIGHUP, SIG_DFL);
	(void)signal(SIGINT, SIG_DFL);
	(void)signal(SIGTSTP, SIG_IGN);

	tbuf[0] = '-';
	(void)strlcpy(tbuf + 1, (p = strrchr(shell, '/')) ?
	    p + 1 : shell, sizeof(tbuf) - 1);

	if ((scds.rlim_cur != QUAD_MIN || scds.rlim_max != QUAD_MIN) &&
	    setrlimit(RLIMIT_CORE, &scds) < 0)
		syslog(LOG_ERR, "couldn't reset core dump size: %m");

	if (lastchance)
		(void)printf("WARNING: Your password has expired."
		    "  You must change your password, now!\n");

	if (setusercontext(lc, pwd, rootlogin ? 0 : pwd->pw_uid,
	    LOGIN_SETALL & ~LOGIN_SETPATH) < 0) {
		warn("unable to set user context");
		quickexit(1);
	}

	if (homeless) {
		(void)printf("No home directory %s!\n", pwd->pw_dir);
		(void)printf("Logging in with home = \"/\".\n");
		(void)setenv("HOME", "/", 1);
	}

	if (auth_approval(as, lc, NULL, "login") == 0) {
		if (auth_getstate(as) & AUTH_EXPIRED)
			(void)printf("Sorry -- your account has expired.\n");
		else
			(void)printf("approval failure\n");
		quickexit(1);
	}

	/*
	 * The last thing we do is discard all of the open file descriptors.
	 * Last because the C library may have some open.
	 */
	closefrom(STDERR_FILENO + 1);

	/*
	 * Close the authentication session, make sure it is marked
	 * as okay so no files are removed.
	 */
	auth_setstate(as, AUTH_OKAY);
	auth_close(as);

	execlp(shell, tbuf, (char *)NULL);
	err(1, "%s", shell);
}
Example #15
0
int
pwd_gensalt(char *salt, int saltlen, login_cap_t *lc, char type)
{
	char *next, *now, *oldnext;

	*salt = '\0';

	switch (type) {
	case 'y':
		next = login_getcapstr(lc, "ypcipher", NULL, NULL);
		if (next == NULL && (next = strdup(YPCIPHER_DEF)) == NULL) {
			warn(NULL);
			return 0;
		}
		break;
	case 'l':
	default:
		next = login_getcapstr(lc, "localcipher", NULL, NULL);
		if (next == NULL && (next = strdup(LOCALCIPHER_DEF)) == NULL) {
			warn(NULL);
			return 0;
		}
		break;
	}

	oldnext = next;
	now = strsep(&next, ",");
	if (!strcmp(now, "old")) {
		if (saltlen < 3) {
			free(oldnext);
			return 0;
		}
		to64(&salt[0], arc4random(), 2);
		salt[2] = '\0';
	} else if (!strcmp(now, "newsalt")) {
		u_int32_t rounds = 7250;

		if (next)
			rounds = atol(next);
		if (saltlen < 10) {
			free(oldnext);
			return 0;
		}
		/* Check rounds, 24 bit is max */
		if (rounds < 7250)
			rounds = 7250;
		else if (rounds > 0xffffff)
			rounds = 0xffffff;
		salt[0] = _PASSWORD_EFMT1;
		to64(&salt[1], (u_int32_t)rounds, 4);
		to64(&salt[5], arc4random(), 4);
		salt[9] = '\0';
	} else if (!strcmp(now, "md5")) {
		if (saltlen < 13) {	/* $1$8salt$\0 */
			free(oldnext);
			return 0;
		}

		strlcpy(salt, "$1$", saltlen);
		to64(&salt[3], arc4random(), 4);
		to64(&salt[7], arc4random(), 4);
		strlcpy(&salt[11], "$", saltlen - 11);
	} else if (!strcmp(now, "blowfish")) {
		int rounds = 6;

		if (next)
			rounds = atoi(next);
		if (rounds < 4)
			rounds = 4;
		if (rounds > 31)
			rounds = 31;
		strlcpy(salt, bcrypt_gensalt(rounds), saltlen);
	} else {
		warnx("Unknown option %s.", now);
		free(oldnext);
		return 0;
	}
	free(oldnext);
	return 1;
}