155682Smarkm/*
2233294Sstas * Copyright (c) 1997 - 2006 Kungliga Tekniska H��gskolan
3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden).
4233294Sstas * All rights reserved.
555682Smarkm *
6233294Sstas * Redistribution and use in source and binary forms, with or without
7233294Sstas * modification, are permitted provided that the following conditions
8233294Sstas * are met:
955682Smarkm *
10233294Sstas * 1. Redistributions of source code must retain the above copyright
11233294Sstas *    notice, this list of conditions and the following disclaimer.
1255682Smarkm *
13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright
14233294Sstas *    notice, this list of conditions and the following disclaimer in the
15233294Sstas *    documentation and/or other materials provided with the distribution.
1655682Smarkm *
17233294Sstas * 3. Neither the name of the Institute nor the names of its contributors
18233294Sstas *    may be used to endorse or promote products derived from this software
19233294Sstas *    without specific prior written permission.
2055682Smarkm *
21233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24233294Sstas * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31233294Sstas * SUCH DAMAGE.
3255682Smarkm */
3355682Smarkm
3455682Smarkm#include "login_locl.h"
3555682Smarkm#ifdef HAVE_CAPABILITY_H
3655682Smarkm#include <capability.h>
3755682Smarkm#endif
3855682Smarkm#ifdef HAVE_SYS_CAPABILITY_H
3955682Smarkm#include <sys/capability.h>
4055682Smarkm#endif
41178825Sdfr#ifdef HAVE_CRYPT_H
42178825Sdfr#include <crypt.h>
43178825Sdfr#endif
4455682Smarkm
45233294SstasRCSID("$Id$");
4655682Smarkm
4772445Sassarstatic int login_timeout = 60;
4855682Smarkm
4955682Smarkmstatic int
5055682Smarkmstart_login_process(void)
5155682Smarkm{
5255682Smarkm    char *prog, *argv0;
5355682Smarkm    prog = login_conf_get_string("login_program");
5455682Smarkm    if(prog == NULL)
5555682Smarkm	return 0;
5655682Smarkm    argv0 = strrchr(prog, '/');
5755682Smarkm
5855682Smarkm    if(argv0)
5955682Smarkm	argv0++;
6055682Smarkm    else
6155682Smarkm	argv0 = prog;
6255682Smarkm
6355682Smarkm    return simple_execle(prog, argv0, NULL, env);
6455682Smarkm}
6555682Smarkm
6655682Smarkmstatic int
6755682Smarkmstart_logout_process(void)
6855682Smarkm{
6955682Smarkm    char *prog, *argv0;
7055682Smarkm    pid_t pid;
7155682Smarkm
7255682Smarkm    prog = login_conf_get_string("logout_program");
7355682Smarkm    if(prog == NULL)
7455682Smarkm	return 0;
7555682Smarkm    argv0 = strrchr(prog, '/');
7655682Smarkm
7755682Smarkm    if(argv0)
7855682Smarkm	argv0++;
7955682Smarkm    else
8055682Smarkm	argv0 = prog;
8155682Smarkm
8255682Smarkm    pid = fork();
8372445Sassar    if(pid == 0) {
8472445Sassar	/* avoid getting signals sent to the shell */
8572445Sassar	setpgid(0, getpid());
8655682Smarkm	return 0;
8772445Sassar    }
8855682Smarkm    if(pid == -1)
8955682Smarkm	err(1, "fork");
9055682Smarkm    /* wait for the real login process to exit */
9155682Smarkm#ifdef HAVE_SETPROCTITLE
9255682Smarkm    setproctitle("waitpid %d", pid);
9355682Smarkm#endif
9455682Smarkm    while(1) {
9555682Smarkm	int status;
9655682Smarkm	int ret;
9755682Smarkm	ret = waitpid(pid, &status, 0);
9855682Smarkm	if(ret > 0) {
9955682Smarkm	    if(WIFEXITED(status) || WIFSIGNALED(status)) {
10055682Smarkm		execle(prog, argv0, NULL, env);
10155682Smarkm		err(1, "exec %s", prog);
10255682Smarkm	    }
103233294Sstas	} else if(ret < 0)
10455682Smarkm	    err(1, "waitpid");
10555682Smarkm    }
10655682Smarkm}
10755682Smarkm
10855682Smarkmstatic void
10955682Smarkmexec_shell(const char *shell, int fallback)
11055682Smarkm{
11155682Smarkm    char *sh;
11255682Smarkm    const char *p;
113233294Sstas
11455682Smarkm    extend_env(NULL);
11555682Smarkm    if(start_login_process() < 0)
11655682Smarkm	warn("login process");
11755682Smarkm    start_logout_process();
11855682Smarkm
11955682Smarkm    p = strrchr(shell, '/');
12055682Smarkm    if(p)
12155682Smarkm	p++;
12255682Smarkm    else
12355682Smarkm	p = shell;
124178825Sdfr    if (asprintf(&sh, "-%s", p) == -1)
125178825Sdfr	errx(1, "Out of memory");
12655682Smarkm    execle(shell, sh, NULL, env);
12755682Smarkm    if(fallback){
128233294Sstas	warnx("Can't exec %s, trying %s",
12955682Smarkm	      shell, _PATH_BSHELL);
13055682Smarkm	execle(_PATH_BSHELL, "-sh", NULL, env);
13155682Smarkm	err(1, "%s", _PATH_BSHELL);
13255682Smarkm    }
13355682Smarkm    err(1, "%s", shell);
13455682Smarkm}
13555682Smarkm
136233294Sstasstatic enum { NONE = 0, AUTH_KRB5 = 2, AUTH_OTP = 3 } auth;
13755682Smarkm
13872445Sassar#ifdef OTP
13972445Sassarstatic OtpContext otp_ctx;
14072445Sassar
14172445Sassarstatic int
14272445Sassarotp_verify(struct passwd *pwd, const char *password)
14372445Sassar{
14472445Sassar   return (otp_verify_user (&otp_ctx, password));
14572445Sassar}
14672445Sassar#endif /* OTP */
14772445Sassar
14872445Sassar
149102644Snectarstatic int pag_set = 0;
150102644Snectar
15155682Smarkm#ifdef KRB5
15255682Smarkmstatic krb5_context context;
15355682Smarkmstatic krb5_ccache  id, id2;
15455682Smarkm
15555682Smarkmstatic int
15655682Smarkmkrb5_verify(struct passwd *pwd, const char *password)
15755682Smarkm{
15855682Smarkm    krb5_error_code ret;
15955682Smarkm    krb5_principal princ;
16055682Smarkm
16172445Sassar    ret = krb5_parse_name(context, pwd->pw_name, &princ);
16255682Smarkm    if(ret)
16355682Smarkm	return 1;
164233294Sstas    ret = krb5_cc_new_unique(context, krb5_cc_type_memory, NULL, &id);
16572445Sassar    if(ret) {
16655682Smarkm	krb5_free_principal(context, princ);
16755682Smarkm	return 1;
16855682Smarkm    }
16955682Smarkm    ret = krb5_verify_user_lrealm(context,
170233294Sstas				  princ,
17155682Smarkm				  id,
172233294Sstas				  password,
17355682Smarkm				  1,
17455682Smarkm				  NULL);
17555682Smarkm    krb5_free_principal(context, princ);
17655682Smarkm    return ret;
17755682Smarkm}
17855682Smarkm
17972445Sassarstatic int
18072445Sassarkrb5_start_session (const struct passwd *pwd)
18172445Sassar{
18272445Sassar    krb5_error_code ret;
18372445Sassar    char residual[64];
18472445Sassar
18572445Sassar    /* copy credentials to file cache */
186233294Sstas    snprintf(residual, sizeof(residual), "FILE:/tmp/krb5cc_%u",
18772445Sassar	     (unsigned)pwd->pw_uid);
18872445Sassar    krb5_cc_resolve(context, residual, &id2);
18972445Sassar    ret = krb5_cc_copy_cache(context, id, id2);
19072445Sassar    if (ret == 0)
19172445Sassar	add_env("KRB5CCNAME", residual);
19272445Sassar    else {
19372445Sassar	krb5_cc_destroy (context, id2);
19472445Sassar	return ret;
19572445Sassar    }
19655682Smarkm    krb5_cc_close(context, id2);
19755682Smarkm    krb5_cc_destroy(context, id);
19855682Smarkm    return 0;
19955682Smarkm}
20055682Smarkm
20155682Smarkmstatic void
20255682Smarkmkrb5_finish (void)
20355682Smarkm{
20455682Smarkm    krb5_free_context(context);
20555682Smarkm}
20655682Smarkm
20755682Smarkmstatic void
20855682Smarkmkrb5_get_afs_tokens (const struct passwd *pwd)
20955682Smarkm{
21055682Smarkm    char cell[64];
21155682Smarkm    char *pw_dir;
21255682Smarkm    krb5_error_code ret;
21355682Smarkm
21455682Smarkm    if (!k_hasafs ())
21555682Smarkm	return;
21655682Smarkm
21755682Smarkm    ret = krb5_cc_default(context, &id2);
218233294Sstas
21955682Smarkm    if (ret == 0) {
22055682Smarkm	pw_dir = pwd->pw_dir;
22155682Smarkm
22255682Smarkm	if (!pag_set) {
22355682Smarkm	    k_setpag();
22455682Smarkm	    pag_set = 1;
22555682Smarkm	}
22655682Smarkm
22755682Smarkm	if(k_afs_cell_of_file(pw_dir, cell, sizeof(cell)) == 0)
22855682Smarkm	    krb5_afslog_uid_home (context, id2,
22955682Smarkm				  cell, NULL, pwd->pw_uid, pwd->pw_dir);
23055682Smarkm	krb5_afslog_uid_home (context, id2, NULL, NULL,
23155682Smarkm			      pwd->pw_uid, pwd->pw_dir);
23255682Smarkm	krb5_cc_close (context, id2);
23355682Smarkm    }
23455682Smarkm}
23555682Smarkm
23655682Smarkm#endif /* KRB5 */
23755682Smarkm
23855682Smarkmstatic int f_flag;
23955682Smarkmstatic int p_flag;
24072445Sassar#if 0
24155682Smarkmstatic int r_flag;
24272445Sassar#endif
24355682Smarkmstatic int version_flag;
24455682Smarkmstatic int help_flag;
24555682Smarkmstatic char *remote_host;
24672445Sassarstatic char *auth_level = NULL;
24755682Smarkm
24855682Smarkmstruct getargs args[] = {
24972445Sassar    { NULL, 'a', arg_string,    &auth_level,    "authentication mode" },
25055682Smarkm#if 0
25155682Smarkm    { NULL, 'd' },
25255682Smarkm#endif
25355682Smarkm    { NULL, 'f', arg_flag,	&f_flag,	"pre-authenticated" },
25455682Smarkm    { NULL, 'h', arg_string,	&remote_host,	"remote host", "hostname" },
25555682Smarkm    { NULL, 'p', arg_flag,	&p_flag,	"don't purge environment" },
25655682Smarkm#if 0
25755682Smarkm    { NULL, 'r', arg_flag,	&r_flag,	"rlogin protocol" },
25855682Smarkm#endif
25955682Smarkm    { "version", 0,  arg_flag,	&version_flag },
26055682Smarkm    { "help",	 0,  arg_flag,&help_flag, }
26155682Smarkm};
26255682Smarkm
26355682Smarkmint nargs = sizeof(args) / sizeof(args[0]);
26455682Smarkm
26555682Smarkmstatic void
26655682Smarkmupdate_utmp(const char *username, const char *hostname,
26755682Smarkm	    char *tty, char *ttyn)
26855682Smarkm{
26955682Smarkm    /*
27055682Smarkm     * Update the utmp files, both BSD and SYSV style.
27155682Smarkm     */
27255682Smarkm    if (utmpx_login(tty, username, hostname) != 0 && !f_flag) {
27355682Smarkm	printf("No utmpx entry.  You must exec \"login\" from the "
27455682Smarkm	       "lowest level shell.\n");
27555682Smarkm	exit(1);
27655682Smarkm    }
27755682Smarkm    utmp_login(ttyn, username, hostname);
27855682Smarkm}
27955682Smarkm
28055682Smarkmstatic void
28155682Smarkmchecknologin(void)
28255682Smarkm{
28355682Smarkm    FILE *f;
28455682Smarkm    char buf[1024];
28555682Smarkm
28655682Smarkm    f = fopen(_PATH_NOLOGIN, "r");
28755682Smarkm    if(f == NULL)
28855682Smarkm	return;
28955682Smarkm    while(fgets(buf, sizeof(buf), f))
29055682Smarkm	fputs(buf, stdout);
29155682Smarkm    fclose(f);
29255682Smarkm    exit(0);
29355682Smarkm}
29455682Smarkm
295102644Snectar/* print contents of a file */
296102644Snectarstatic void
297102644Snectarshow_file(const char *file)
298102644Snectar{
299102644Snectar    FILE *f;
300102644Snectar    char buf[BUFSIZ];
301102644Snectar    if((f = fopen(file, "r")) == NULL)
302102644Snectar	return;
303102644Snectar    while (fgets(buf, sizeof(buf), f))
304102644Snectar	fputs(buf, stdout);
305102644Snectar    fclose(f);
306102644Snectar}
307102644Snectar
308233294Sstas/*
30955682Smarkm * Actually log in the user.  `pwd' contains all the relevant
31055682Smarkm * information about the user.  `ttyn' is the complete name of the tty
31155682Smarkm * and `tty' the short name.
31255682Smarkm */
31355682Smarkm
31455682Smarkmstatic void
31555682Smarkmdo_login(const struct passwd *pwd, char *tty, char *ttyn)
31655682Smarkm{
31755682Smarkm#ifdef HAVE_GETSPNAM
31855682Smarkm    struct spwd *sp;
31955682Smarkm#endif
32055682Smarkm    int rootlogin = (pwd->pw_uid == 0);
32155682Smarkm    gid_t tty_gid;
32255682Smarkm    struct group *gr;
32355682Smarkm    const char *home_dir;
324102644Snectar    int i;
32555682Smarkm
32655682Smarkm    if(!rootlogin)
32755682Smarkm	checknologin();
328233294Sstas
32955682Smarkm#ifdef HAVE_GETSPNAM
33055682Smarkm    sp = getspnam(pwd->pw_name);
33155682Smarkm#endif
33255682Smarkm
33355682Smarkm    update_utmp(pwd->pw_name, remote_host ? remote_host : "",
33455682Smarkm		tty, ttyn);
33555682Smarkm
33655682Smarkm    gr = getgrnam ("tty");
33755682Smarkm    if (gr != NULL)
33855682Smarkm	tty_gid = gr->gr_gid;
33955682Smarkm    else
34055682Smarkm	tty_gid = pwd->pw_gid;
34155682Smarkm
34272445Sassar    if (chown (ttyn, pwd->pw_uid, tty_gid) < 0) {
34355682Smarkm	warn("chown %s", ttyn);
34455682Smarkm	if (rootlogin == 0)
34555682Smarkm	    exit (1);
34655682Smarkm    }
34755682Smarkm
34855682Smarkm    if (chmod (ttyn, S_IRUSR | S_IWUSR | S_IWGRP) < 0) {
34955682Smarkm	warn("chmod %s", ttyn);
35055682Smarkm	if (rootlogin == 0)
35155682Smarkm	    exit (1);
35255682Smarkm    }
35355682Smarkm
35455682Smarkm#ifdef HAVE_SETLOGIN
35555682Smarkm    if(setlogin(pwd->pw_name)){
35655682Smarkm	warn("setlogin(%s)", pwd->pw_name);
35755682Smarkm	if(rootlogin == 0)
35855682Smarkm	    exit(1);
35955682Smarkm    }
36055682Smarkm#endif
361178825Sdfr    if(rootlogin == 0) {
362178825Sdfr	const char *file = login_conf_get_string("limits");
363178825Sdfr	if(file == NULL)
364178825Sdfr	    file = _PATH_LIMITS_CONF;
365178825Sdfr
366178825Sdfr	read_limits_conf(file, pwd);
367178825Sdfr    }
368233294Sstas
36990926Snectar#ifdef HAVE_SETPCRED
37090926Snectar    if (setpcred (pwd->pw_name, NULL) == -1)
37190926Snectar	warn("setpcred(%s)", pwd->pw_name);
37290926Snectar#endif /* HAVE_SETPCRED */
37355682Smarkm#ifdef HAVE_INITGROUPS
37455682Smarkm    if(initgroups(pwd->pw_name, pwd->pw_gid)){
37555682Smarkm	warn("initgroups(%s, %u)", pwd->pw_name, (unsigned)pwd->pw_gid);
37655682Smarkm	if(rootlogin == 0)
37755682Smarkm	    exit(1);
37855682Smarkm    }
37955682Smarkm#endif
38090926Snectar    if(do_osfc2_magic(pwd->pw_uid))
38190926Snectar	exit(1);
38255682Smarkm    if(setgid(pwd->pw_gid)){
38355682Smarkm	warn("setgid(%u)", (unsigned)pwd->pw_gid);
38455682Smarkm	if(rootlogin == 0)
38555682Smarkm	    exit(1);
38655682Smarkm    }
38772445Sassar    if(setuid(pwd->pw_uid) || (pwd->pw_uid != 0 && setuid(0) == 0)) {
38855682Smarkm	warn("setuid(%u)", (unsigned)pwd->pw_uid);
38955682Smarkm	if(rootlogin == 0)
39055682Smarkm	    exit(1);
39155682Smarkm    }
392102644Snectar
393102644Snectar    /* make sure signals are set to default actions, apparently some
394102644Snectar       OS:es like to ignore SIGINT, which is not very convenient */
395233294Sstas
396102644Snectar    for (i = 1; i < NSIG; ++i)
397102644Snectar	signal(i, SIG_DFL);
398102644Snectar
39955682Smarkm    /* all kinds of different magic */
40055682Smarkm
40155682Smarkm#ifdef HAVE_GETSPNAM
40255682Smarkm    check_shadow(pwd, sp);
40355682Smarkm#endif
40455682Smarkm
40555682Smarkm#if defined(HAVE_GETUDBNAM) && defined(HAVE_SETLIM)
40655682Smarkm    {
40755682Smarkm	struct udb *udb;
40855682Smarkm	long t;
40955682Smarkm	const long maxcpu = 46116860184; /* some random constant */
41055682Smarkm	udb = getudbnam(pwd->pw_name);
41155682Smarkm	if(udb == UDB_NULL)
41255682Smarkm	    errx(1, "Failed to get UDB entry.");
41355682Smarkm	t = udb->ue_pcpulim[UDBRC_INTER];
41455682Smarkm	if(t == 0 || t > maxcpu)
41555682Smarkm	    t = CPUUNLIM;
41655682Smarkm	else
41755682Smarkm	    t *= 100 * CLOCKS_PER_SEC;
41855682Smarkm
41955682Smarkm	if(limit(C_PROC, 0, L_CPU, t) < 0)
42055682Smarkm	    warn("limit C_PROC");
42155682Smarkm
42255682Smarkm	t = udb->ue_jcpulim[UDBRC_INTER];
42355682Smarkm	if(t == 0 || t > maxcpu)
42455682Smarkm	    t = CPUUNLIM;
42555682Smarkm	else
42655682Smarkm	    t *= 100 * CLOCKS_PER_SEC;
42755682Smarkm
42855682Smarkm	if(limit(C_JOBPROCS, 0, L_CPU, t) < 0)
42955682Smarkm	    warn("limit C_JOBPROCS");
43055682Smarkm
43155682Smarkm	nice(udb->ue_nice[UDBRC_INTER]);
43255682Smarkm    }
43355682Smarkm#endif
43455682Smarkm#if defined(HAVE_SGI_GETCAPABILITYBYNAME) && defined(HAVE_CAP_SET_PROC)
43555682Smarkm	/* XXX SGI capability hack IRIX 6.x (x >= 0?) has something
43655682Smarkm	   called capabilities, that allow you to give away
43755682Smarkm	   permissions (such as chown) to specific processes. From 6.5
43855682Smarkm	   this is default on, and the default capability set seems to
43955682Smarkm	   not always be the empty set. The problem is that the
44055682Smarkm	   runtime linker refuses to do just about anything if the
44155682Smarkm	   process has *any* capabilities set, so we have to remove
44255682Smarkm	   them here (unless otherwise instructed by /etc/capability).
44355682Smarkm	   In IRIX < 6.5, these functions was called sgi_cap_setproc,
44455682Smarkm	   etc, but we ignore this fact (it works anyway). */
44555682Smarkm	{
44655682Smarkm	    struct user_cap *ucap = sgi_getcapabilitybyname(pwd->pw_name);
44755682Smarkm	    cap_t cap;
44855682Smarkm	    if(ucap == NULL)
44955682Smarkm		cap = cap_from_text("all=");
45055682Smarkm	    else
45155682Smarkm		cap = cap_from_text(ucap->ca_default);
45255682Smarkm	    if(cap == NULL)
45355682Smarkm		err(1, "cap_from_text");
45455682Smarkm	    if(cap_set_proc(cap) < 0)
45555682Smarkm		err(1, "cap_set_proc");
45655682Smarkm	    cap_free(cap);
45755682Smarkm	    free(ucap);
45855682Smarkm	}
45955682Smarkm#endif
46055682Smarkm    home_dir = pwd->pw_dir;
46155682Smarkm    if (chdir(home_dir) < 0) {
46255682Smarkm	fprintf(stderr, "No home directory \"%s\"!\n", pwd->pw_dir);
46355682Smarkm	if (chdir("/"))
46455682Smarkm	    exit(0);
46555682Smarkm	home_dir = "/";
46655682Smarkm	fprintf(stderr, "Logging in with home = \"/\".\n");
46755682Smarkm    }
46855682Smarkm#ifdef KRB5
46955682Smarkm    if (auth == AUTH_KRB5) {
47055682Smarkm	krb5_start_session (pwd);
47155682Smarkm    }
47272445Sassar
47355682Smarkm    krb5_get_afs_tokens (pwd);
474120945Snectar
47572445Sassar    krb5_finish ();
47655682Smarkm#endif /* KRB5 */
47755682Smarkm
47872445Sassar    add_env("PATH", _PATH_DEFPATH);
47972445Sassar
48072445Sassar    {
48172445Sassar	const char *str = login_conf_get_string("environment");
48272445Sassar	char buf[MAXPATHLEN];
48372445Sassar
48472445Sassar	if(str == NULL) {
48572445Sassar	    login_read_env(_PATH_ETC_ENVIRONMENT);
48672445Sassar	} else {
48772445Sassar	    while(strsep_copy(&str, ",", buf, sizeof(buf)) != -1) {
48872445Sassar		if(buf[0] == '\0')
48972445Sassar		    continue;
49072445Sassar		login_read_env(buf);
49172445Sassar	    }
49272445Sassar	}
49372445Sassar    }
494102644Snectar    {
495102644Snectar	const char *str = login_conf_get_string("motd");
496102644Snectar	char buf[MAXPATHLEN];
497102644Snectar
498102644Snectar	if(str != NULL) {
499102644Snectar	    while(strsep_copy(&str, ",", buf, sizeof(buf)) != -1) {
500102644Snectar		if(buf[0] == '\0')
501102644Snectar		    continue;
502102644Snectar		show_file(buf);
503102644Snectar	    }
504120945Snectar	} else {
505120945Snectar	    str = login_conf_get_string("welcome");
506120945Snectar	    if(str != NULL)
507120945Snectar		show_file(str);
508102644Snectar	}
509102644Snectar    }
51055682Smarkm    add_env("HOME", home_dir);
51155682Smarkm    add_env("USER", pwd->pw_name);
51255682Smarkm    add_env("LOGNAME", pwd->pw_name);
51355682Smarkm    add_env("SHELL", pwd->pw_shell);
51455682Smarkm    exec_shell(pwd->pw_shell, rootlogin);
51555682Smarkm}
51655682Smarkm
51755682Smarkmstatic int
51855682Smarkmcheck_password(struct passwd *pwd, const char *password)
51955682Smarkm{
52055682Smarkm    if(pwd->pw_passwd == NULL)
52155682Smarkm	return 1;
52255682Smarkm    if(pwd->pw_passwd[0] == '\0'){
52355682Smarkm#ifdef ALLOW_NULL_PASSWORD
52455682Smarkm	return password[0] != '\0';
52555682Smarkm#else
52655682Smarkm	return 1;
52755682Smarkm#endif
52855682Smarkm    }
52955682Smarkm    if(strcmp(pwd->pw_passwd, crypt(password, pwd->pw_passwd)) == 0)
53055682Smarkm	return 0;
53155682Smarkm#ifdef KRB5
53255682Smarkm    if(krb5_verify(pwd, password) == 0) {
53355682Smarkm	auth = AUTH_KRB5;
53455682Smarkm	return 0;
53555682Smarkm    }
53655682Smarkm#endif
53772445Sassar#ifdef OTP
53872445Sassar    if (otp_verify (pwd, password) == 0) {
53972445Sassar       auth = AUTH_OTP;
54072445Sassar       return 0;
54172445Sassar    }
54272445Sassar#endif
54355682Smarkm    return 1;
54455682Smarkm}
54555682Smarkm
54655682Smarkmstatic void
54755682Smarkmusage(int status)
54855682Smarkm{
54955682Smarkm    arg_printusage(args, nargs, NULL, "[username]");
55055682Smarkm    exit(status);
55155682Smarkm}
55255682Smarkm
55372445Sassarstatic RETSIGTYPE
55472445Sassarsig_handler(int sig)
55572445Sassar{
55672445Sassar    if (sig == SIGALRM)
55772445Sassar         fprintf(stderr, "Login timed out after %d seconds\n",
55872445Sassar                login_timeout);
55972445Sassar      else
56072445Sassar         fprintf(stderr, "Login received signal, exiting\n");
56172445Sassar    exit(0);
56272445Sassar}
56372445Sassar
56455682Smarkmint
56555682Smarkmmain(int argc, char **argv)
56655682Smarkm{
56755682Smarkm    int max_tries = 5;
56855682Smarkm    int try;
56955682Smarkm
57055682Smarkm    char username[32];
571178825Sdfr    int optidx = 0;
57255682Smarkm
57355682Smarkm    int ask = 1;
57472445Sassar    struct sigaction sa;
575233294Sstas
57678527Sassar    setprogname(argv[0]);
57755682Smarkm
57872445Sassar#ifdef KRB5
57972445Sassar    {
58072445Sassar	krb5_error_code ret;
58172445Sassar
58272445Sassar	ret = krb5_init_context(&context);
58372445Sassar	if (ret)
58472445Sassar	    errx (1, "krb5_init_context failed: %d", ret);
58572445Sassar    }
58672445Sassar#endif
58772445Sassar
588178825Sdfr    openlog("login", LOG_ODELAY | LOG_PID, LOG_AUTH);
58955682Smarkm
59055682Smarkm    if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv,
591178825Sdfr		&optidx))
59255682Smarkm	usage (1);
593178825Sdfr    argc -= optidx;
594178825Sdfr    argv += optidx;
59555682Smarkm
59655682Smarkm    if(help_flag)
59755682Smarkm	usage(0);
59855682Smarkm    if (version_flag) {
59955682Smarkm	print_version (NULL);
60055682Smarkm	return 0;
60155682Smarkm    }
602233294Sstas
60355682Smarkm    if (geteuid() != 0)
60455682Smarkm	errx(1, "only root may use login, use su");
60555682Smarkm
60655682Smarkm    /* Default tty settings. */
60755682Smarkm    stty_default();
60855682Smarkm
60955682Smarkm    if(p_flag)
61055682Smarkm	copy_env();
61155682Smarkm    else {
61255682Smarkm	/* this set of variables is always preserved by BSD login */
61355682Smarkm	if(getenv("TERM"))
61455682Smarkm	    add_env("TERM", getenv("TERM"));
61555682Smarkm	if(getenv("TZ"))
61655682Smarkm	    add_env("TZ", getenv("TZ"));
61755682Smarkm    }
61855682Smarkm
61955682Smarkm    if(*argv){
62055682Smarkm	if(strchr(*argv, '=') == NULL && strcmp(*argv, "-") != 0){
62155682Smarkm	    strlcpy (username, *argv, sizeof(username));
62255682Smarkm	    ask = 0;
62355682Smarkm	}
62455682Smarkm    }
62572445Sassar
62672445Sassar#if defined(DCE) && defined(AIX)
62772445Sassar    esetenv("AUTHSTATE", "DCE", 1);
62872445Sassar#endif
62972445Sassar
63055682Smarkm    /* XXX should we care about environment on the command line? */
63172445Sassar
63272445Sassar    memset(&sa, 0, sizeof(sa));
63372445Sassar    sa.sa_handler = sig_handler;
63472445Sassar    sigemptyset(&sa.sa_mask);
63572445Sassar    sa.sa_flags = 0;
63672445Sassar    sigaction(SIGALRM, &sa, NULL);
63772445Sassar    alarm(login_timeout);
63872445Sassar
63955682Smarkm    for(try = 0; try < max_tries; try++){
64055682Smarkm	struct passwd *pwd;
64155682Smarkm	char password[128];
64255682Smarkm	int ret;
64355682Smarkm	char ttname[32];
64455682Smarkm	char *tty, *ttyn;
64572445Sassar        char prompt[128];
64672445Sassar#ifdef OTP
64772445Sassar        char otp_str[256];
64872445Sassar#endif
64955682Smarkm
65055682Smarkm	if(ask){
65172445Sassar	    f_flag = 0;
65272445Sassar#if 0
65372445Sassar	    r_flag = 0;
65472445Sassar#endif
65555682Smarkm	    ret = read_string("login: ", username, sizeof(username), 1);
65655682Smarkm	    if(ret == -3)
65755682Smarkm		exit(0);
65855682Smarkm	    if(ret == -2)
65972445Sassar		sig_handler(0); /* exit */
66055682Smarkm	}
66155682Smarkm        pwd = k_getpwnam(username);
66255682Smarkm#ifdef ALLOW_NULL_PASSWORD
66355682Smarkm        if (pwd != NULL && (pwd->pw_passwd[0] == '\0')) {
66455682Smarkm            strcpy(password,"");
66555682Smarkm        }
66655682Smarkm        else
66755682Smarkm#endif
66872445Sassar
66972445Sassar        {
67072445Sassar#ifdef OTP
67172445Sassar           if(auth_level && strcmp(auth_level, "otp") == 0 &&
67272445Sassar                 otp_challenge(&otp_ctx, username,
67372445Sassar                            otp_str, sizeof(otp_str)) == 0)
67472445Sassar                 snprintf (prompt, sizeof(prompt), "%s's %s Password: ",
67572445Sassar                            username, otp_str);
67672445Sassar            else
67772445Sassar#endif
67872445Sassar                 strncpy(prompt, "Password: ", sizeof(prompt));
67972445Sassar
68072445Sassar	    if (f_flag == 0) {
68172445Sassar	       ret = read_string(prompt, password, sizeof(password), 0);
68272445Sassar               if (ret == -3) {
68372445Sassar                  ask = 1;
68472445Sassar                  continue;
68572445Sassar               }
68672445Sassar               if (ret == -2)
68772445Sassar                  sig_handler(0);
68872445Sassar            }
68972445Sassar         }
690233294Sstas
69155682Smarkm	if(pwd == NULL){
69255682Smarkm	    fprintf(stderr, "Login incorrect.\n");
69355682Smarkm	    ask = 1;
69455682Smarkm	    continue;
69555682Smarkm	}
69655682Smarkm
69755682Smarkm	if(f_flag == 0 && check_password(pwd, password)){
69855682Smarkm	    fprintf(stderr, "Login incorrect.\n");
69955682Smarkm            ask = 1;
70055682Smarkm	    continue;
70155682Smarkm	}
70255682Smarkm	ttyn = ttyname(STDIN_FILENO);
70355682Smarkm	if(ttyn == NULL){
70455682Smarkm	    snprintf(ttname, sizeof(ttname), "%s??", _PATH_TTY);
70555682Smarkm	    ttyn = ttname;
70655682Smarkm	}
70755682Smarkm	if (strncmp (ttyn, _PATH_DEV, strlen(_PATH_DEV)) == 0)
70855682Smarkm	    tty = ttyn + strlen(_PATH_DEV);
70955682Smarkm	else
71055682Smarkm	    tty = ttyn;
711233294Sstas
71255682Smarkm	if (login_access (pwd, remote_host ? remote_host : tty) == 0) {
71355682Smarkm	    fprintf(stderr, "Permission denied\n");
71455682Smarkm	    if (remote_host)
71555682Smarkm		syslog(LOG_NOTICE, "%s LOGIN REFUSED FROM %s",
71655682Smarkm		       pwd->pw_name, remote_host);
71755682Smarkm	    else
71855682Smarkm		syslog(LOG_NOTICE, "%s LOGIN REFUSED ON %s",
71955682Smarkm		       pwd->pw_name, tty);
72055682Smarkm	    exit (1);
721178825Sdfr	} else {
722178825Sdfr	    if (remote_host)
723178825Sdfr		syslog(LOG_NOTICE, "%s LOGIN ACCEPTED FROM %s ppid=%d",
724178825Sdfr		       pwd->pw_name, remote_host, (int) getppid());
725178825Sdfr	    else
726178825Sdfr		syslog(LOG_NOTICE, "%s LOGIN ACCEPTED ON %s ppid=%d",
727178825Sdfr		       pwd->pw_name, tty, (int) getppid());
72855682Smarkm	}
72972445Sassar        alarm(0);
73055682Smarkm	do_login(pwd, tty, ttyn);
73155682Smarkm    }
73255682Smarkm    exit(1);
73355682Smarkm}
734