155682Smarkm/*
2178825Sdfr * Copyright (c) 1997 - 2006 Kungliga Tekniska H�gskolan
355682Smarkm * (Royal Institute of Technology, Stockholm, Sweden).
455682Smarkm * All rights reserved.
555682Smarkm *
655682Smarkm * Redistribution and use in source and binary forms, with or without
755682Smarkm * modification, are permitted provided that the following conditions
855682Smarkm * are met:
955682Smarkm *
1055682Smarkm * 1. Redistributions of source code must retain the above copyright
1155682Smarkm *    notice, this list of conditions and the following disclaimer.
1255682Smarkm *
1355682Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1455682Smarkm *    notice, this list of conditions and the following disclaimer in the
1555682Smarkm *    documentation and/or other materials provided with the distribution.
1655682Smarkm *
1755682Smarkm * 3. Neither the name of the Institute nor the names of its contributors
1855682Smarkm *    may be used to endorse or promote products derived from this software
1955682Smarkm *    without specific prior written permission.
2055682Smarkm *
2155682Smarkm * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
2255682Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2355682Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2455682Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
2555682Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2655682Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2755682Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2855682Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2955682Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3055682Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3155682Smarkm * 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
45178825SdfrRCSID("$Id: login.c 16498 2006-01-09 16:26:25Z joda $");
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	    }
10355682Smarkm	} 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;
11355682Smarkm
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){
12855682Smarkm	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
13672445Sassarstatic enum { NONE = 0, AUTH_KRB4 = 1, AUTH_KRB5 = 2, AUTH_OTP = 3 } auth;
13755682Smarkm
138178825Sdfr#ifdef KRB4
139178825Sdfrstatic krb5_boolean get_v4_tgt = FALSE;
140178825Sdfr#endif
141178825Sdfr
14272445Sassar#ifdef OTP
14372445Sassarstatic OtpContext otp_ctx;
14472445Sassar
14572445Sassarstatic int
14672445Sassarotp_verify(struct passwd *pwd, const char *password)
14772445Sassar{
14872445Sassar   return (otp_verify_user (&otp_ctx, password));
14972445Sassar}
15072445Sassar#endif /* OTP */
15172445Sassar
15272445Sassar
153102644Snectarstatic int pag_set = 0;
154102644Snectar
15555682Smarkm#ifdef KRB5
15655682Smarkmstatic krb5_context context;
15755682Smarkmstatic krb5_ccache  id, id2;
15855682Smarkm
15955682Smarkmstatic int
16055682Smarkmkrb5_verify(struct passwd *pwd, const char *password)
16155682Smarkm{
16255682Smarkm    krb5_error_code ret;
16355682Smarkm    krb5_principal princ;
16455682Smarkm
16572445Sassar    ret = krb5_parse_name(context, pwd->pw_name, &princ);
16655682Smarkm    if(ret)
16755682Smarkm	return 1;
16855682Smarkm    ret = krb5_cc_gen_new(context, &krb5_mcc_ops, &id);
16972445Sassar    if(ret) {
17055682Smarkm	krb5_free_principal(context, princ);
17155682Smarkm	return 1;
17255682Smarkm    }
17355682Smarkm    ret = krb5_verify_user_lrealm(context,
17455682Smarkm				  princ,
17555682Smarkm				  id,
17655682Smarkm				  password,
17755682Smarkm				  1,
17855682Smarkm				  NULL);
17955682Smarkm    krb5_free_principal(context, princ);
18055682Smarkm    return ret;
18155682Smarkm}
18255682Smarkm
18372445Sassar#ifdef KRB4
18472445Sassarstatic krb5_error_code
18572445Sassarkrb5_to4 (krb5_ccache id)
18655682Smarkm{
18790926Snectar    krb5_error_code ret;
18890926Snectar    krb5_principal princ;
18990926Snectar
19090926Snectar    ret = krb5_cc_get_principal(context, id, &princ);
191142403Snectar    if(ret == 0) {
192142403Snectar	krb5_appdefault_boolean(context, "login",
193142403Snectar				krb5_principal_get_realm(context, princ),
194142403Snectar				"krb4_get_tickets", FALSE, &get_v4_tgt);
19590926Snectar	krb5_free_principal(context, princ);
196142403Snectar    } else {
197142403Snectar	krb5_realm realm = NULL;
198142403Snectar	krb5_get_default_realm(context, &realm);
199142403Snectar	krb5_appdefault_boolean(context, "login",
200142403Snectar				realm,
201142403Snectar				"krb4_get_tickets", FALSE, &get_v4_tgt);
202142403Snectar	free(realm);
20390926Snectar    }
20490926Snectar
20590926Snectar    if (get_v4_tgt) {
20655682Smarkm        CREDENTIALS c;
20755682Smarkm        krb5_creds mcred, cred;
20855682Smarkm        char krb4tkfile[MAXPATHLEN];
20972445Sassar	krb5_error_code ret;
21072445Sassar	krb5_principal princ;
21155682Smarkm
212178825Sdfr	krb5_cc_clear_mcred(&mcred);
213178825Sdfr
21472445Sassar	ret = krb5_cc_get_principal (context, id, &princ);
21572445Sassar	if (ret)
21672445Sassar	    return ret;
21772445Sassar
21872445Sassar	ret = krb5_make_principal(context, &mcred.server,
21972445Sassar				  princ->realm,
22072445Sassar				  "krbtgt",
22172445Sassar				  princ->realm,
22272445Sassar				  NULL);
223178825Sdfr	if (ret) {
224178825Sdfr	    krb5_free_principal(context, princ);
22572445Sassar	    return ret;
226178825Sdfr	}
227178825Sdfr	mcred.client = princ;
22872445Sassar
22972445Sassar	ret = krb5_cc_retrieve_cred(context, id, 0, &mcred, &cred);
23055682Smarkm	if(ret == 0) {
23190926Snectar	    ret = krb524_convert_creds_kdc_ccache(context, id, &cred, &c);
23255682Smarkm	    if(ret == 0) {
23355682Smarkm		snprintf(krb4tkfile,sizeof(krb4tkfile),"%s%d",TKT_ROOT,
23455682Smarkm			 getuid());
23555682Smarkm		krb_set_tkt_string(krb4tkfile);
23655682Smarkm		tf_setup(&c, c.pname, c.pinst);
23755682Smarkm	    }
23855682Smarkm	    memset(&c, 0, sizeof(c));
239178825Sdfr	    krb5_free_cred_contents(context, &cred);
24055682Smarkm	}
241178825Sdfr	if (ret != 0)
242178825Sdfr	    get_v4_tgt = FALSE;
24355682Smarkm	krb5_free_principal(context, mcred.server);
244178825Sdfr	krb5_free_principal(context, mcred.client);
24555682Smarkm    }
24672445Sassar    return 0;
24772445Sassar}
24872445Sassar#endif /* KRB4 */
24972445Sassar
25072445Sassarstatic int
25172445Sassarkrb5_start_session (const struct passwd *pwd)
25272445Sassar{
25372445Sassar    krb5_error_code ret;
25472445Sassar    char residual[64];
25572445Sassar
25672445Sassar    /* copy credentials to file cache */
25772445Sassar    snprintf(residual, sizeof(residual), "FILE:/tmp/krb5cc_%u",
25872445Sassar	     (unsigned)pwd->pw_uid);
25972445Sassar    krb5_cc_resolve(context, residual, &id2);
26072445Sassar    ret = krb5_cc_copy_cache(context, id, id2);
26172445Sassar    if (ret == 0)
26272445Sassar	add_env("KRB5CCNAME", residual);
26372445Sassar    else {
26472445Sassar	krb5_cc_destroy (context, id2);
26572445Sassar	return ret;
26672445Sassar    }
26772445Sassar#ifdef KRB4
26872445Sassar    krb5_to4 (id2);
26955682Smarkm#endif
27055682Smarkm    krb5_cc_close(context, id2);
27155682Smarkm    krb5_cc_destroy(context, id);
27255682Smarkm    return 0;
27355682Smarkm}
27455682Smarkm
27555682Smarkmstatic void
27655682Smarkmkrb5_finish (void)
27755682Smarkm{
27855682Smarkm    krb5_free_context(context);
27955682Smarkm}
28055682Smarkm
28155682Smarkmstatic void
28255682Smarkmkrb5_get_afs_tokens (const struct passwd *pwd)
28355682Smarkm{
28455682Smarkm    char cell[64];
28555682Smarkm    char *pw_dir;
28655682Smarkm    krb5_error_code ret;
28755682Smarkm
28855682Smarkm    if (!k_hasafs ())
28955682Smarkm	return;
29055682Smarkm
29155682Smarkm    ret = krb5_cc_default(context, &id2);
29255682Smarkm
29355682Smarkm    if (ret == 0) {
29455682Smarkm	pw_dir = pwd->pw_dir;
29555682Smarkm
29655682Smarkm	if (!pag_set) {
29755682Smarkm	    k_setpag();
29855682Smarkm	    pag_set = 1;
29955682Smarkm	}
30055682Smarkm
30155682Smarkm	if(k_afs_cell_of_file(pw_dir, cell, sizeof(cell)) == 0)
30255682Smarkm	    krb5_afslog_uid_home (context, id2,
30355682Smarkm				  cell, NULL, pwd->pw_uid, pwd->pw_dir);
30455682Smarkm	krb5_afslog_uid_home (context, id2, NULL, NULL,
30555682Smarkm			      pwd->pw_uid, pwd->pw_dir);
30655682Smarkm	krb5_cc_close (context, id2);
30755682Smarkm    }
30855682Smarkm}
30955682Smarkm
31055682Smarkm#endif /* KRB5 */
31155682Smarkm
31255682Smarkm#ifdef KRB4
31355682Smarkm
31455682Smarkmstatic int
31555682Smarkmkrb4_verify(struct passwd *pwd, const char *password)
31655682Smarkm{
31755682Smarkm    char lrealm[REALM_SZ];
31855682Smarkm    int ret;
31955682Smarkm    char ticket_file[MaxPathLen];
32055682Smarkm
32155682Smarkm    ret = krb_get_lrealm (lrealm, 1);
32255682Smarkm    if (ret)
32355682Smarkm	return 1;
32455682Smarkm
32555682Smarkm    snprintf (ticket_file, sizeof(ticket_file),
32655682Smarkm	      "%s%u_%u",
32755682Smarkm	      TKT_ROOT, (unsigned)pwd->pw_uid, (unsigned)getpid());
32855682Smarkm
32955682Smarkm    krb_set_tkt_string (ticket_file);
33055682Smarkm
33155682Smarkm    ret = krb_verify_user (pwd->pw_name, "", lrealm, (char *)password,
33255682Smarkm			   KRB_VERIFY_SECURE_FAIL, NULL);
33355682Smarkm    if (ret)
33455682Smarkm	return 1;
33555682Smarkm
33655682Smarkm    if (chown (ticket_file, pwd->pw_uid, pwd->pw_gid) < 0) {
33755682Smarkm	dest_tkt();
33855682Smarkm	return 1;
33955682Smarkm    }
34055682Smarkm
34155682Smarkm    add_env ("KRBTKFILE", ticket_file);
34255682Smarkm    return 0;
34355682Smarkm}
34455682Smarkm
34555682Smarkmstatic void
34655682Smarkmkrb4_get_afs_tokens (const struct passwd *pwd)
34755682Smarkm{
34855682Smarkm    char cell[64];
34955682Smarkm    char *pw_dir;
35055682Smarkm
35155682Smarkm    if (!k_hasafs ())
35255682Smarkm	return;
35355682Smarkm
35455682Smarkm    pw_dir = pwd->pw_dir;
35555682Smarkm
35655682Smarkm    if (!pag_set) {
35755682Smarkm	k_setpag();
35855682Smarkm	pag_set = 1;
35955682Smarkm    }
36055682Smarkm
36155682Smarkm    if(k_afs_cell_of_file(pw_dir, cell, sizeof(cell)) == 0)
36255682Smarkm	krb_afslog_uid_home (cell, NULL, pwd->pw_uid, pwd->pw_dir);
36355682Smarkm
36455682Smarkm    krb_afslog_uid_home (NULL, NULL, pwd->pw_uid, pwd->pw_dir);
36555682Smarkm}
36655682Smarkm
36755682Smarkm#endif /* KRB4 */
36855682Smarkm
36955682Smarkmstatic int f_flag;
37055682Smarkmstatic int p_flag;
37172445Sassar#if 0
37255682Smarkmstatic int r_flag;
37372445Sassar#endif
37455682Smarkmstatic int version_flag;
37555682Smarkmstatic int help_flag;
37655682Smarkmstatic char *remote_host;
37772445Sassarstatic char *auth_level = NULL;
37855682Smarkm
37955682Smarkmstruct getargs args[] = {
38072445Sassar    { NULL, 'a', arg_string,    &auth_level,    "authentication mode" },
38155682Smarkm#if 0
38255682Smarkm    { NULL, 'd' },
38355682Smarkm#endif
38455682Smarkm    { NULL, 'f', arg_flag,	&f_flag,	"pre-authenticated" },
38555682Smarkm    { NULL, 'h', arg_string,	&remote_host,	"remote host", "hostname" },
38655682Smarkm    { NULL, 'p', arg_flag,	&p_flag,	"don't purge environment" },
38755682Smarkm#if 0
38855682Smarkm    { NULL, 'r', arg_flag,	&r_flag,	"rlogin protocol" },
38955682Smarkm#endif
39055682Smarkm    { "version", 0,  arg_flag,	&version_flag },
39155682Smarkm    { "help",	 0,  arg_flag,&help_flag, }
39255682Smarkm};
39355682Smarkm
39455682Smarkmint nargs = sizeof(args) / sizeof(args[0]);
39555682Smarkm
39655682Smarkmstatic void
39755682Smarkmupdate_utmp(const char *username, const char *hostname,
39855682Smarkm	    char *tty, char *ttyn)
39955682Smarkm{
40055682Smarkm    /*
40155682Smarkm     * Update the utmp files, both BSD and SYSV style.
40255682Smarkm     */
40355682Smarkm    if (utmpx_login(tty, username, hostname) != 0 && !f_flag) {
40455682Smarkm	printf("No utmpx entry.  You must exec \"login\" from the "
40555682Smarkm	       "lowest level shell.\n");
40655682Smarkm	exit(1);
40755682Smarkm    }
40855682Smarkm    utmp_login(ttyn, username, hostname);
40955682Smarkm}
41055682Smarkm
41155682Smarkmstatic void
41255682Smarkmchecknologin(void)
41355682Smarkm{
41455682Smarkm    FILE *f;
41555682Smarkm    char buf[1024];
41655682Smarkm
41755682Smarkm    f = fopen(_PATH_NOLOGIN, "r");
41855682Smarkm    if(f == NULL)
41955682Smarkm	return;
42055682Smarkm    while(fgets(buf, sizeof(buf), f))
42155682Smarkm	fputs(buf, stdout);
42255682Smarkm    fclose(f);
42355682Smarkm    exit(0);
42455682Smarkm}
42555682Smarkm
426102644Snectar/* print contents of a file */
427102644Snectarstatic void
428102644Snectarshow_file(const char *file)
429102644Snectar{
430102644Snectar    FILE *f;
431102644Snectar    char buf[BUFSIZ];
432102644Snectar    if((f = fopen(file, "r")) == NULL)
433102644Snectar	return;
434102644Snectar    while (fgets(buf, sizeof(buf), f))
435102644Snectar	fputs(buf, stdout);
436102644Snectar    fclose(f);
437102644Snectar}
438102644Snectar
43955682Smarkm/*
44055682Smarkm * Actually log in the user.  `pwd' contains all the relevant
44155682Smarkm * information about the user.  `ttyn' is the complete name of the tty
44255682Smarkm * and `tty' the short name.
44355682Smarkm */
44455682Smarkm
44555682Smarkmstatic void
44655682Smarkmdo_login(const struct passwd *pwd, char *tty, char *ttyn)
44755682Smarkm{
44855682Smarkm#ifdef HAVE_GETSPNAM
44955682Smarkm    struct spwd *sp;
45055682Smarkm#endif
45155682Smarkm    int rootlogin = (pwd->pw_uid == 0);
45255682Smarkm    gid_t tty_gid;
45355682Smarkm    struct group *gr;
45455682Smarkm    const char *home_dir;
455102644Snectar    int i;
45655682Smarkm
45755682Smarkm    if(!rootlogin)
45855682Smarkm	checknologin();
45955682Smarkm
46055682Smarkm#ifdef HAVE_GETSPNAM
46155682Smarkm    sp = getspnam(pwd->pw_name);
46255682Smarkm#endif
46355682Smarkm
46455682Smarkm    update_utmp(pwd->pw_name, remote_host ? remote_host : "",
46555682Smarkm		tty, ttyn);
46655682Smarkm
46755682Smarkm    gr = getgrnam ("tty");
46855682Smarkm    if (gr != NULL)
46955682Smarkm	tty_gid = gr->gr_gid;
47055682Smarkm    else
47155682Smarkm	tty_gid = pwd->pw_gid;
47255682Smarkm
47372445Sassar    if (chown (ttyn, pwd->pw_uid, tty_gid) < 0) {
47455682Smarkm	warn("chown %s", ttyn);
47555682Smarkm	if (rootlogin == 0)
47655682Smarkm	    exit (1);
47755682Smarkm    }
47855682Smarkm
47955682Smarkm    if (chmod (ttyn, S_IRUSR | S_IWUSR | S_IWGRP) < 0) {
48055682Smarkm	warn("chmod %s", ttyn);
48155682Smarkm	if (rootlogin == 0)
48255682Smarkm	    exit (1);
48355682Smarkm    }
48455682Smarkm
48555682Smarkm#ifdef HAVE_SETLOGIN
48655682Smarkm    if(setlogin(pwd->pw_name)){
48755682Smarkm	warn("setlogin(%s)", pwd->pw_name);
48855682Smarkm	if(rootlogin == 0)
48955682Smarkm	    exit(1);
49055682Smarkm    }
49155682Smarkm#endif
492178825Sdfr    if(rootlogin == 0) {
493178825Sdfr	const char *file = login_conf_get_string("limits");
494178825Sdfr	if(file == NULL)
495178825Sdfr	    file = _PATH_LIMITS_CONF;
496178825Sdfr
497178825Sdfr	read_limits_conf(file, pwd);
498178825Sdfr    }
499178825Sdfr
50090926Snectar#ifdef HAVE_SETPCRED
50190926Snectar    if (setpcred (pwd->pw_name, NULL) == -1)
50290926Snectar	warn("setpcred(%s)", pwd->pw_name);
50390926Snectar#endif /* HAVE_SETPCRED */
50455682Smarkm#ifdef HAVE_INITGROUPS
50555682Smarkm    if(initgroups(pwd->pw_name, pwd->pw_gid)){
50655682Smarkm	warn("initgroups(%s, %u)", pwd->pw_name, (unsigned)pwd->pw_gid);
50755682Smarkm	if(rootlogin == 0)
50855682Smarkm	    exit(1);
50955682Smarkm    }
51055682Smarkm#endif
51190926Snectar    if(do_osfc2_magic(pwd->pw_uid))
51290926Snectar	exit(1);
51355682Smarkm    if(setgid(pwd->pw_gid)){
51455682Smarkm	warn("setgid(%u)", (unsigned)pwd->pw_gid);
51555682Smarkm	if(rootlogin == 0)
51655682Smarkm	    exit(1);
51755682Smarkm    }
51872445Sassar    if(setuid(pwd->pw_uid) || (pwd->pw_uid != 0 && setuid(0) == 0)) {
51955682Smarkm	warn("setuid(%u)", (unsigned)pwd->pw_uid);
52055682Smarkm	if(rootlogin == 0)
52155682Smarkm	    exit(1);
52255682Smarkm    }
523102644Snectar
524102644Snectar    /* make sure signals are set to default actions, apparently some
525102644Snectar       OS:es like to ignore SIGINT, which is not very convenient */
526102644Snectar
527102644Snectar    for (i = 1; i < NSIG; ++i)
528102644Snectar	signal(i, SIG_DFL);
529102644Snectar
53055682Smarkm    /* all kinds of different magic */
53155682Smarkm
53255682Smarkm#ifdef HAVE_GETSPNAM
53355682Smarkm    check_shadow(pwd, sp);
53455682Smarkm#endif
53555682Smarkm
53655682Smarkm#if defined(HAVE_GETUDBNAM) && defined(HAVE_SETLIM)
53755682Smarkm    {
53855682Smarkm	struct udb *udb;
53955682Smarkm	long t;
54055682Smarkm	const long maxcpu = 46116860184; /* some random constant */
54155682Smarkm	udb = getudbnam(pwd->pw_name);
54255682Smarkm	if(udb == UDB_NULL)
54355682Smarkm	    errx(1, "Failed to get UDB entry.");
54455682Smarkm	t = udb->ue_pcpulim[UDBRC_INTER];
54555682Smarkm	if(t == 0 || t > maxcpu)
54655682Smarkm	    t = CPUUNLIM;
54755682Smarkm	else
54855682Smarkm	    t *= 100 * CLOCKS_PER_SEC;
54955682Smarkm
55055682Smarkm	if(limit(C_PROC, 0, L_CPU, t) < 0)
55155682Smarkm	    warn("limit C_PROC");
55255682Smarkm
55355682Smarkm	t = udb->ue_jcpulim[UDBRC_INTER];
55455682Smarkm	if(t == 0 || t > maxcpu)
55555682Smarkm	    t = CPUUNLIM;
55655682Smarkm	else
55755682Smarkm	    t *= 100 * CLOCKS_PER_SEC;
55855682Smarkm
55955682Smarkm	if(limit(C_JOBPROCS, 0, L_CPU, t) < 0)
56055682Smarkm	    warn("limit C_JOBPROCS");
56155682Smarkm
56255682Smarkm	nice(udb->ue_nice[UDBRC_INTER]);
56355682Smarkm    }
56455682Smarkm#endif
56555682Smarkm#if defined(HAVE_SGI_GETCAPABILITYBYNAME) && defined(HAVE_CAP_SET_PROC)
56655682Smarkm	/* XXX SGI capability hack IRIX 6.x (x >= 0?) has something
56755682Smarkm	   called capabilities, that allow you to give away
56855682Smarkm	   permissions (such as chown) to specific processes. From 6.5
56955682Smarkm	   this is default on, and the default capability set seems to
57055682Smarkm	   not always be the empty set. The problem is that the
57155682Smarkm	   runtime linker refuses to do just about anything if the
57255682Smarkm	   process has *any* capabilities set, so we have to remove
57355682Smarkm	   them here (unless otherwise instructed by /etc/capability).
57455682Smarkm	   In IRIX < 6.5, these functions was called sgi_cap_setproc,
57555682Smarkm	   etc, but we ignore this fact (it works anyway). */
57655682Smarkm	{
57755682Smarkm	    struct user_cap *ucap = sgi_getcapabilitybyname(pwd->pw_name);
57855682Smarkm	    cap_t cap;
57955682Smarkm	    if(ucap == NULL)
58055682Smarkm		cap = cap_from_text("all=");
58155682Smarkm	    else
58255682Smarkm		cap = cap_from_text(ucap->ca_default);
58355682Smarkm	    if(cap == NULL)
58455682Smarkm		err(1, "cap_from_text");
58555682Smarkm	    if(cap_set_proc(cap) < 0)
58655682Smarkm		err(1, "cap_set_proc");
58755682Smarkm	    cap_free(cap);
58855682Smarkm	    free(ucap);
58955682Smarkm	}
59055682Smarkm#endif
59155682Smarkm    home_dir = pwd->pw_dir;
59255682Smarkm    if (chdir(home_dir) < 0) {
59355682Smarkm	fprintf(stderr, "No home directory \"%s\"!\n", pwd->pw_dir);
59455682Smarkm	if (chdir("/"))
59555682Smarkm	    exit(0);
59655682Smarkm	home_dir = "/";
59755682Smarkm	fprintf(stderr, "Logging in with home = \"/\".\n");
59855682Smarkm    }
59955682Smarkm#ifdef KRB5
60055682Smarkm    if (auth == AUTH_KRB5) {
60155682Smarkm	krb5_start_session (pwd);
60255682Smarkm    }
60355682Smarkm#ifdef KRB4
60472445Sassar    else if (auth == 0) {
60572445Sassar	krb5_error_code ret;
60672445Sassar	krb5_ccache id;
60772445Sassar
60872445Sassar	ret = krb5_cc_default (context, &id);
60972445Sassar	if (ret == 0) {
61072445Sassar	    krb5_to4 (id);
61172445Sassar	    krb5_cc_close (context, id);
61272445Sassar	}
61372445Sassar    }
614120945Snectar#endif /* KRB4 */
61572445Sassar
61655682Smarkm    krb5_get_afs_tokens (pwd);
617120945Snectar
61872445Sassar    krb5_finish ();
61955682Smarkm#endif /* KRB5 */
62055682Smarkm
62155682Smarkm#ifdef KRB4
622178825Sdfr    if (auth == AUTH_KRB4 || get_v4_tgt)
623178825Sdfr	krb4_get_afs_tokens (pwd);
62455682Smarkm#endif /* KRB4 */
62555682Smarkm
62672445Sassar    add_env("PATH", _PATH_DEFPATH);
62772445Sassar
62872445Sassar    {
62972445Sassar	const char *str = login_conf_get_string("environment");
63072445Sassar	char buf[MAXPATHLEN];
63172445Sassar
63272445Sassar	if(str == NULL) {
63372445Sassar	    login_read_env(_PATH_ETC_ENVIRONMENT);
63472445Sassar	} else {
63572445Sassar	    while(strsep_copy(&str, ",", buf, sizeof(buf)) != -1) {
63672445Sassar		if(buf[0] == '\0')
63772445Sassar		    continue;
63872445Sassar		login_read_env(buf);
63972445Sassar	    }
64072445Sassar	}
64172445Sassar    }
642102644Snectar    {
643102644Snectar	const char *str = login_conf_get_string("motd");
644102644Snectar	char buf[MAXPATHLEN];
645102644Snectar
646102644Snectar	if(str != NULL) {
647102644Snectar	    while(strsep_copy(&str, ",", buf, sizeof(buf)) != -1) {
648102644Snectar		if(buf[0] == '\0')
649102644Snectar		    continue;
650102644Snectar		show_file(buf);
651102644Snectar	    }
652120945Snectar	} else {
653120945Snectar	    str = login_conf_get_string("welcome");
654120945Snectar	    if(str != NULL)
655120945Snectar		show_file(str);
656102644Snectar	}
657102644Snectar    }
65855682Smarkm    add_env("HOME", home_dir);
65955682Smarkm    add_env("USER", pwd->pw_name);
66055682Smarkm    add_env("LOGNAME", pwd->pw_name);
66155682Smarkm    add_env("SHELL", pwd->pw_shell);
66255682Smarkm    exec_shell(pwd->pw_shell, rootlogin);
66355682Smarkm}
66455682Smarkm
66555682Smarkmstatic int
66655682Smarkmcheck_password(struct passwd *pwd, const char *password)
66755682Smarkm{
66855682Smarkm    if(pwd->pw_passwd == NULL)
66955682Smarkm	return 1;
67055682Smarkm    if(pwd->pw_passwd[0] == '\0'){
67155682Smarkm#ifdef ALLOW_NULL_PASSWORD
67255682Smarkm	return password[0] != '\0';
67355682Smarkm#else
67455682Smarkm	return 1;
67555682Smarkm#endif
67655682Smarkm    }
67755682Smarkm    if(strcmp(pwd->pw_passwd, crypt(password, pwd->pw_passwd)) == 0)
67855682Smarkm	return 0;
67955682Smarkm#ifdef KRB5
68055682Smarkm    if(krb5_verify(pwd, password) == 0) {
68155682Smarkm	auth = AUTH_KRB5;
68255682Smarkm	return 0;
68355682Smarkm    }
68455682Smarkm#endif
68555682Smarkm#ifdef KRB4
68655682Smarkm    if (krb4_verify (pwd, password) == 0) {
68755682Smarkm	auth = AUTH_KRB4;
68855682Smarkm	return 0;
68955682Smarkm    }
69055682Smarkm#endif
69172445Sassar#ifdef OTP
69272445Sassar    if (otp_verify (pwd, password) == 0) {
69372445Sassar       auth = AUTH_OTP;
69472445Sassar       return 0;
69572445Sassar    }
69672445Sassar#endif
69755682Smarkm    return 1;
69855682Smarkm}
69955682Smarkm
70055682Smarkmstatic void
70155682Smarkmusage(int status)
70255682Smarkm{
70355682Smarkm    arg_printusage(args, nargs, NULL, "[username]");
70455682Smarkm    exit(status);
70555682Smarkm}
70655682Smarkm
70772445Sassarstatic RETSIGTYPE
70872445Sassarsig_handler(int sig)
70972445Sassar{
71072445Sassar    if (sig == SIGALRM)
71172445Sassar         fprintf(stderr, "Login timed out after %d seconds\n",
71272445Sassar                login_timeout);
71372445Sassar      else
71472445Sassar         fprintf(stderr, "Login received signal, exiting\n");
71572445Sassar    exit(0);
71672445Sassar}
71772445Sassar
71855682Smarkmint
71955682Smarkmmain(int argc, char **argv)
72055682Smarkm{
72155682Smarkm    int max_tries = 5;
72255682Smarkm    int try;
72355682Smarkm
72455682Smarkm    char username[32];
725178825Sdfr    int optidx = 0;
72655682Smarkm
72755682Smarkm    int ask = 1;
72872445Sassar    struct sigaction sa;
72955682Smarkm
73078527Sassar    setprogname(argv[0]);
73155682Smarkm
73272445Sassar#ifdef KRB5
73372445Sassar    {
73472445Sassar	krb5_error_code ret;
73572445Sassar
73672445Sassar	ret = krb5_init_context(&context);
73772445Sassar	if (ret)
73872445Sassar	    errx (1, "krb5_init_context failed: %d", ret);
73972445Sassar    }
74072445Sassar#endif
74172445Sassar
742178825Sdfr    openlog("login", LOG_ODELAY | LOG_PID, LOG_AUTH);
74355682Smarkm
74455682Smarkm    if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv,
745178825Sdfr		&optidx))
74655682Smarkm	usage (1);
747178825Sdfr    argc -= optidx;
748178825Sdfr    argv += optidx;
74955682Smarkm
75055682Smarkm    if(help_flag)
75155682Smarkm	usage(0);
75255682Smarkm    if (version_flag) {
75355682Smarkm	print_version (NULL);
75455682Smarkm	return 0;
75555682Smarkm    }
75655682Smarkm
75755682Smarkm    if (geteuid() != 0)
75855682Smarkm	errx(1, "only root may use login, use su");
75955682Smarkm
76055682Smarkm    /* Default tty settings. */
76155682Smarkm    stty_default();
76255682Smarkm
76355682Smarkm    if(p_flag)
76455682Smarkm	copy_env();
76555682Smarkm    else {
76655682Smarkm	/* this set of variables is always preserved by BSD login */
76755682Smarkm	if(getenv("TERM"))
76855682Smarkm	    add_env("TERM", getenv("TERM"));
76955682Smarkm	if(getenv("TZ"))
77055682Smarkm	    add_env("TZ", getenv("TZ"));
77155682Smarkm    }
77255682Smarkm
77355682Smarkm    if(*argv){
77455682Smarkm	if(strchr(*argv, '=') == NULL && strcmp(*argv, "-") != 0){
77555682Smarkm	    strlcpy (username, *argv, sizeof(username));
77655682Smarkm	    ask = 0;
77755682Smarkm	}
77855682Smarkm    }
77972445Sassar
78072445Sassar#if defined(DCE) && defined(AIX)
78172445Sassar    esetenv("AUTHSTATE", "DCE", 1);
78272445Sassar#endif
78372445Sassar
78455682Smarkm    /* XXX should we care about environment on the command line? */
78572445Sassar
78672445Sassar    memset(&sa, 0, sizeof(sa));
78772445Sassar    sa.sa_handler = sig_handler;
78872445Sassar    sigemptyset(&sa.sa_mask);
78972445Sassar    sa.sa_flags = 0;
79072445Sassar    sigaction(SIGALRM, &sa, NULL);
79172445Sassar    alarm(login_timeout);
79272445Sassar
79355682Smarkm    for(try = 0; try < max_tries; try++){
79455682Smarkm	struct passwd *pwd;
79555682Smarkm	char password[128];
79655682Smarkm	int ret;
79755682Smarkm	char ttname[32];
79855682Smarkm	char *tty, *ttyn;
79972445Sassar        char prompt[128];
80072445Sassar#ifdef OTP
80172445Sassar        char otp_str[256];
80272445Sassar#endif
80355682Smarkm
80455682Smarkm	if(ask){
80572445Sassar	    f_flag = 0;
80672445Sassar#if 0
80772445Sassar	    r_flag = 0;
80872445Sassar#endif
80955682Smarkm	    ret = read_string("login: ", username, sizeof(username), 1);
81055682Smarkm	    if(ret == -3)
81155682Smarkm		exit(0);
81255682Smarkm	    if(ret == -2)
81372445Sassar		sig_handler(0); /* exit */
81455682Smarkm	}
81555682Smarkm        pwd = k_getpwnam(username);
81655682Smarkm#ifdef ALLOW_NULL_PASSWORD
81755682Smarkm        if (pwd != NULL && (pwd->pw_passwd[0] == '\0')) {
81855682Smarkm            strcpy(password,"");
81955682Smarkm        }
82055682Smarkm        else
82155682Smarkm#endif
82272445Sassar
82372445Sassar        {
82472445Sassar#ifdef OTP
82572445Sassar           if(auth_level && strcmp(auth_level, "otp") == 0 &&
82672445Sassar                 otp_challenge(&otp_ctx, username,
82772445Sassar                            otp_str, sizeof(otp_str)) == 0)
82872445Sassar                 snprintf (prompt, sizeof(prompt), "%s's %s Password: ",
82972445Sassar                            username, otp_str);
83072445Sassar            else
83172445Sassar#endif
83272445Sassar                 strncpy(prompt, "Password: ", sizeof(prompt));
83372445Sassar
83472445Sassar	    if (f_flag == 0) {
83572445Sassar	       ret = read_string(prompt, password, sizeof(password), 0);
83672445Sassar               if (ret == -3) {
83772445Sassar                  ask = 1;
83872445Sassar                  continue;
83972445Sassar               }
84072445Sassar               if (ret == -2)
84172445Sassar                  sig_handler(0);
84272445Sassar            }
84372445Sassar         }
84455682Smarkm
84555682Smarkm	if(pwd == NULL){
84655682Smarkm	    fprintf(stderr, "Login incorrect.\n");
84755682Smarkm	    ask = 1;
84855682Smarkm	    continue;
84955682Smarkm	}
85055682Smarkm
85155682Smarkm	if(f_flag == 0 && check_password(pwd, password)){
85255682Smarkm	    fprintf(stderr, "Login incorrect.\n");
85355682Smarkm            ask = 1;
85455682Smarkm	    continue;
85555682Smarkm	}
85655682Smarkm	ttyn = ttyname(STDIN_FILENO);
85755682Smarkm	if(ttyn == NULL){
85855682Smarkm	    snprintf(ttname, sizeof(ttname), "%s??", _PATH_TTY);
85955682Smarkm	    ttyn = ttname;
86055682Smarkm	}
86155682Smarkm	if (strncmp (ttyn, _PATH_DEV, strlen(_PATH_DEV)) == 0)
86255682Smarkm	    tty = ttyn + strlen(_PATH_DEV);
86355682Smarkm	else
86455682Smarkm	    tty = ttyn;
86555682Smarkm
86655682Smarkm	if (login_access (pwd, remote_host ? remote_host : tty) == 0) {
86755682Smarkm	    fprintf(stderr, "Permission denied\n");
86855682Smarkm	    if (remote_host)
86955682Smarkm		syslog(LOG_NOTICE, "%s LOGIN REFUSED FROM %s",
87055682Smarkm		       pwd->pw_name, remote_host);
87155682Smarkm	    else
87255682Smarkm		syslog(LOG_NOTICE, "%s LOGIN REFUSED ON %s",
87355682Smarkm		       pwd->pw_name, tty);
87455682Smarkm	    exit (1);
875178825Sdfr	} else {
876178825Sdfr	    if (remote_host)
877178825Sdfr		syslog(LOG_NOTICE, "%s LOGIN ACCEPTED FROM %s ppid=%d",
878178825Sdfr		       pwd->pw_name, remote_host, (int) getppid());
879178825Sdfr	    else
880178825Sdfr		syslog(LOG_NOTICE, "%s LOGIN ACCEPTED ON %s ppid=%d",
881178825Sdfr		       pwd->pw_name, tty, (int) getppid());
88255682Smarkm	}
88372445Sassar        alarm(0);
88455682Smarkm	do_login(pwd, tty, ttyn);
88555682Smarkm    }
88655682Smarkm    exit(1);
88755682Smarkm}
888