diff options
Diffstat (limited to 'src/appl/bsd/login.c')
-rw-r--r-- | src/appl/bsd/login.c | 1831 |
1 files changed, 1318 insertions, 513 deletions
diff --git a/src/appl/bsd/login.c b/src/appl/bsd/login.c index d1e98e23a..778d73d4e 100644 --- a/src/appl/bsd/login.c +++ b/src/appl/bsd/login.c @@ -27,19 +27,51 @@ char copyright[] = /* based on @(#)login.c 5.25 (Berkeley) 1/6/89 */ +/* While the code may be compiled with some of these options turned off, + the default will be to turn them *all* on if v4 compatibility is + available, and allow them to be configured via krb5.conf. */ +/* The configuration is of the form + [login] + # login stanza + krb5_get_tickets = 1 + # use password to get v5 tickets + krb4_get_tickets = 1 + # use password to get v4 tickets + krb4_convert = 1 + # use kerberos conversion daemon to get v4 tickets + krb_run_aklog = 1 + # attempt to run aklog + aklog_path = $(prefix)/bin/aklog + # where to find it [not yet implemented] + accept_passwd = 0 + # don't accept plaintext passwords [not yet implemented] +*/ +#define KRB5_GET_TICKETS +int login_krb5_get_tickets = 1; +#ifdef KRB5_KRB4_COMPAT +#define KRB4_GET_TICKETS +int login_krb4_get_tickets = 1; +#define KRB4_CONVERT +int login_krb4_convert = 1; +#define KRB_RUN_AKLOG +int login_krb_run_aklog = 1; +#endif /* KRB5_KRB4_COMPAT */ +int login_accept_passwd = 0; + /* * login [ name ] * login -r hostname (for rlogind) * login -h hostname (for telnetd, etc.) - * login -f name (for pre-authenticated login: datakit, xterm, etc.) + * login -f name (for pre-authenticated login: datakit, xterm, etc., + * does not allow preauthenticated login as root) * login -F name (for pre-authenticated login: datakit, xterm, etc., * allows preauthenticated login as root) * login -e name (for pre-authenticated encrypted, must do term * negotiation) - * ifdef KRB4 + * ifdef KRB4_KLOGIN * login -k hostname (for Kerberos V4 rlogind with password access) * login -K hostname (for Kerberos V4 rlogind with restricted access) - * endif KRB4 + * endif KRB4_KLOGIN * * only one of: -r -f -e -k -K -F * only one of: -r -h -k -K @@ -68,6 +100,8 @@ char copyright[] = #include <utmp.h> #include <signal.h> +#include <assert.h> + #ifdef HAVE_LASTLOG_H #include <lastlog.h> #endif @@ -87,13 +121,54 @@ char copyright[] = #include <stdio.h> #include <grp.h> #include <pwd.h> -#include <setjmp.h> #include <string.h> +#include <setjmp.h> +#ifndef POSIX_SETJMP +#undef sigjmp_buf +#undef sigsetjmp +#undef siglongjmp +#define sigjmp_buf jmp_buf +#define sigsetjmp(j,s) setjmp(j) +#define siglongjmp longjmp +#endif + +#ifdef POSIX_SIGNALS +typedef struct sigaction handler; +#define handler_init(H,F) (sigemptyset(&(H).sa_mask), \ + (H).sa_flags=0, \ + (H).sa_handler=(F)) +#define handler_swap(S,NEW,OLD) sigaction(S, &NEW, &OLD) +#define handler_set(S,OLD) sigaction(S, &OLD, NULL) +#else +typedef sigtype (*handler)(); +#define handler_init(H,F) ((H) = (F)) +#define handler_swap(S,NEW,OLD) ((OLD) = signal ((S), (NEW))) +#define handler_set(S,OLD) (signal ((S), (OLD))) +#endif + + #ifdef HAVE_SHADOW #include <shadow.h> #endif +#ifdef KRB5_GET_TICKETS +/* #include "krb5.h" */ +/* need k5-int.h to get ->profile from krb5_context */ +#include "k5-int.h" +#include "osconf.h" +#endif /* KRB5_GET_TICKETS */ + +#ifdef KRB4_KLOGIN +/* support for running under v4 klogind, -k -K flags */ +#define KRB4 +#endif + +#ifdef KRB4_GET_TICKETS +/* support for prompting for v4 initial tickets */ +#define KRB4 +#endif + #ifdef KRB4 #include <krb.h> #include <netdb.h> @@ -106,6 +181,12 @@ char copyright[] = #endif /* BIND_HACK */ #endif /* KRB4 */ +#ifndef __STDC__ +#ifndef volatile +#define volatile +#endif +#endif + #include "loginpaths.h" #ifdef POSIX_TERMIOS @@ -144,15 +225,6 @@ char copyright[] = #define QUOTAWARN "/usr/ucb/quota" /* warn user about quotas */ #endif -#define PROTOTYPE_DIR "/usr/athena/lib/prototype_tmpuser" -#define TEMP_DIR_PERM 0711 - -#define NOATTACH "/etc/noattach" -#define NOCREATE "/etc/nocreate" -#define NOREMOTE "/etc/noremote" -#define REGISTER "/usr/etc/go_register" -#define GET_MOTD "/bin/athena/get_message" - #ifndef NO_UT_HOST #ifndef UT_HOSTSIZE /* linux defines it directly in <utmp.h> */ @@ -164,6 +236,11 @@ char copyright[] = #define UT_NAMESIZE sizeof(((struct utmp *)0)->ut_name) #endif +#ifndef HAVE_SETPRIORITY +/* if we don't have it, punt it cleanly */ +#define setpriority(which,who,prio) +#endif /* HAVE_SETPRIORITY */ + #define MAXENVIRON 32 /* @@ -172,44 +249,23 @@ char copyright[] = */ int timeout = 300; -struct passwd *pwd; -#ifdef HAVE_SHADOW -struct spwd *spwd; -#endif - char term[64], *hostname, *username; -#ifndef POSIX_TERMIOS -struct sgttyb sgttyb; -struct tchars tc = { - CINTR, CQUIT, CSTART, CSTOP, CEOT, CBRK -}; -struct ltchars ltc = { - CSUSP, CDSUSP, CRPRNT, CFLUSH, CWERASE, CLNEXT -}; -#endif +extern int errno; -#ifdef KRB4 +#ifdef KRB4_GET_TICKETS #define KRB_ENVIRON "KRBTKFILE" /* Ticket file environment variable */ #define KRB_TK_DIR "/tmp/tkt_" /* Where to put the ticket */ #define MAXPWSIZE 128 /* Biggest string accepted for KRB4 passsword */ - -AUTH_DAT *kdata = (AUTH_DAT *) NULL; -KTEXT ticket = (KTEXT) NULL; -char tkfile[MAXPATHLEN]; -int krbflag = 0; /* set if tickets have been obtained */ -#ifdef SETPAG -int pagflag = 0; /* true if setpag() has been called */ -#endif /* SETPAG */ -#endif /* KRB4 */ +#endif /* KRB4_GET_TICKETS */ char *getenv(); void dofork(); int doremotelogin(), do_krb_login(), rootterm(); -void lgetstr(), doremoteterm(), getloginname(), checknologin(), sleepexit(); -void dolastlog(), motd(); +void lgetstr(), getloginname(), checknologin(), sleepexit(); +void dolastlog(), motd(), check_mail(); #ifndef HAVE_STRSAVE char * strsave(); @@ -218,6 +274,783 @@ char * strsave(); typedef krb5_sigtype sigtype; +#ifndef HAVE_INITGROUPS +static int initgroups(char* name, gid_t basegid) { + gid_t others[NGROUPS_MAX+1]; + int ngrps; + + others[0] = basegid; + ngrps = getgroups(NGROUPS_MAX, others+1); + return setgroups(ngrps+1, others); +} +#endif + +#ifdef KRB5_GET_TICKETS +static struct login_confs { + char *flagname; + int *flag; +} login_conf_set[] = { + "krb5_get_tickets", &login_krb5_get_tickets, +#ifdef KRB5_KRB4_COMPAT + "krb4_get_tickets", &login_krb4_get_tickets, + "krb4_convert", &login_krb4_convert, + "krb4_run_aklog", &login_krb_run_aklog, +#endif /* KRB5_KRB4_COMPAT */ +}; +static char *conf_yes[] = { + "y", "yes", "true", "t", "1", "on", + 0 +}; +static char *conf_no[] = { + "n", "no", "false", "nil", "0", "off", + 0 +}; +/* 1 = true, 0 = false, -1 = ambiguous */ +static int conf_affirmative(s) + char *s; +{ + char **p; + for(p=conf_yes; *p; p++) { + if (!strcasecmp(*p,s)) + return 1; + } + for(p=conf_no; *p; p++) { + if (!strcasecmp(*p,s)) + return 0; + } + /* ambiguous */ + return -1; +} +#endif /* KRB5_GET_TICKETS */ + +#ifdef KRB5_GET_TICKETS +krb5_data tgtname = { + 0, + KRB5_TGS_NAME_SIZE, + KRB5_TGS_NAME +}; +#endif + +#ifdef KRB5_GET_TICKETS +/* get flags (listed above) from the profile */ +void login_get_kconf(k) + krb5_context k; +{ + int i, max_i; + const char* kconf_names[3]; + char **kconf_val; + int retval; + + max_i = sizeof(login_conf_set)/sizeof(struct login_confs); + for (i = 0; i<max_i; i++) { + kconf_names[0] = "login"; + kconf_names[1] = login_conf_set[i].flagname; + kconf_names[2] = 0; + retval = profile_get_values(k->profile, + kconf_names, &kconf_val); + if (retval) { + /* ignore most (all?) errors */ + } else if (kconf_val) { + switch(conf_affirmative(*kconf_val)) { + case 1: + *login_conf_set[i].flag = 1; + break; + case 0: + *login_conf_set[i].flag = 0; + break; + default: + case -1: + com_err("login/kconf", 0, + "invalid flag value %s for flag %s", + *kconf_val, kconf_names[1]); + break; + } + } + } +} +#endif /* KRB5_GET_TICKETS */ + + +/* UNIX password support */ + +struct passwd *pwd; +static char *salt; + +#ifdef HAVE_SHADOW +struct spwd *spwd; +#endif + +void lookup_user (name) + char *name; +{ + pwd = getpwnam (name); + salt = pwd ? pwd->pw_passwd : "xx"; +#ifdef HAVE_SHADOW + spwd = getspnam (name); + if (spwd) + salt = spwd->sp_pwdp; +#endif +} + +int unix_needs_passwd () +{ +#ifdef HAVE_SHADOW + if (spwd) + return spwd->sp_pwdp[0] != 0; +#endif + if (pwd) + return pwd->pw_passwd[0] != 0; + return 1; +} + +int unix_passwd_okay (pass) + char *pass; +{ + char user_pwcopy[9], *namep; + char *crypt (); + + assert (pwd != 0); + + /* copy the first 8 chars of the password for unix crypt */ + strncpy(user_pwcopy, pass, sizeof(user_pwcopy)); + user_pwcopy[8]='\0'; + namep = crypt(user_pwcopy, salt); + memset (user_pwcopy, 0, sizeof(user_pwcopy)); + /* ... and wipe the copy now that we have the string */ + + /* verify the local password string */ +#ifdef HAVE_SHADOW + if (spwd) + return !strcmp(namep, spwd->sp_pwdp); +#endif + return !strcmp (namep, pwd->pw_passwd); +} + +/* Kerberos support */ +#ifdef KRB5_GET_TICKETS +krb5_context kcontext; +krb5_ccache ccache; +static int got_v5_tickets, got_v4_tickets; +char ccfile[MAXPATHLEN+6]; /* FILE:path+\0 */ +int krbflag; /* set if tickets have been obtained */ + +#ifdef KRB4_GET_TICKETS +AUTH_DAT *kdata = (AUTH_DAT *) NULL; +KTEXT ticket = (KTEXT) NULL; +char tkfile[MAXPATHLEN]; +char realm[REALM_SZ]; +#endif + +void k_init (ttyn) + char *ttyn; +{ + krb5_init_context(&kcontext); + krb5_init_ets(kcontext); + login_get_kconf(kcontext); + + /* Set up the credential cache environment variable */ + if (!getenv(KRB5_ENV_CCNAME)) { + sprintf(ccfile, "FILE:/tmp/krb5cc_%s", strrchr(ttyn, '/')+1); + setenv(KRB5_ENV_CCNAME, ccfile, 1); + unlink(ccfile+strlen("FILE:")); + } else { + /* note it correctly */ + strcpy(ccfile, getenv(KRB5_ENV_CCNAME)); + } + +#ifdef KRB4_GET_TICKETS + if (krb_get_lrealm(realm, 1) != KSUCCESS) { + strncpy(realm, KRB_REALM, sizeof(realm)); + } + if (login_krb4_get_tickets) { + /* Set up the ticket file environment variable */ + strncpy(tkfile, KRB_TK_DIR, sizeof(tkfile)); + strncat(tkfile, strrchr(ttyn, '/')+1, + sizeof(tkfile) - strlen(tkfile)); + (void) unlink (tkfile); + setenv(KRB_ENVIRON, tkfile, 1); + } +#endif + +#ifdef BIND_HACK + /* Set name server timeout to be reasonable, + so that people don't take 5 minutes to + log in. Can you say abstraction violation? */ + _res.retrans = 1; +#endif /* BIND_HACK */ +} + +int k5_get_password (user_pwstring, pwsize) + char *user_pwstring; +{ + krb5_error_code code; + char prompt[255]; + sprintf(prompt,"Password for %s: ", username); + + /* reduce opportunities to be swapped out */ + code = krb5_read_password(kcontext, prompt, 0, user_pwstring, &pwsize); + if (code || pwsize == 0) { + fprintf(stderr, "Error while reading password for '%s'\n", username); + /* reading password failed... */ + return 0; + } + if (pwsize == 0) { + fprintf(stderr, "No password read\n"); + /* reading password failed... */ + return 0; + } + return 1; +} + +#define KRB5_DEFAULT_LIFE 60*60*10 /* 10 hours */ +int krb5_options = 0; +krb5_deltat krb5_ticket_lifetime = KRB5_DEFAULT_LIFE; + +int try_krb5 (me_p, pass) + krb5_principal *me_p; + char *pass; +{ + krb5_error_code code; + krb5_principal server, me; + krb5_creds my_creds; + krb5_timestamp now; + krb5_deltat lifetime = krb5_ticket_lifetime; + + /* set up credential cache -- obeying KRB5_ENV_CCNAME + set earlier */ + /* (KRB5_ENV_CCNAME == "KRB5CCNAME" via osconf.h) */ + if (code = krb5_cc_default(kcontext, &ccache)) { + com_err("login", code, "while getting default ccache"); + return 0; + } + /* setup code from v5 kinit */ + memset((char *)&my_creds, 0, sizeof(my_creds)); + + code = krb5_parse_name (kcontext, username, &me); + if (code) { + com_err ("login", code, "when parsing name %s",username); + return 0; + } + *me_p = my_creds.client = me; + + code = krb5_cc_initialize (kcontext, ccache, me); + if (code) { + com_err ("login", code, + "when initializing cache"); + return 0; + } + + code = krb5_build_principal_ext(kcontext, &server, + krb5_princ_realm(kcontext, me)->length, + krb5_princ_realm(kcontext, me)->data, + tgtname.length, tgtname.data, + krb5_princ_realm(kcontext, me)->length, + krb5_princ_realm(kcontext, me)->data, + 0); + if (code) { + com_err("login", code, + "while building server name"); + goto nuke_ccache; + } + + my_creds.server = server; + code = krb5_timeofday(kcontext, &now); + if (code) { + com_err("login", code, + "while getting time of day"); + goto nuke_ccache; + } + my_creds.times.starttime = 0; /* start timer when + request gets to KDC */ + my_creds.times.endtime = now + lifetime; + my_creds.times.renew_till = 0; + + code = krb5_get_in_tkt_with_password(kcontext, krb5_options, + 0, NULL, 0 /*preauth*/, + pass, + ccache, + &my_creds, 0); + + if (code) { + if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY) + fprintf (stderr, + "%s: Kerberos password incorrect\n", + username); + else + com_err ("login", code, + "while getting initial credentials"); + nuke_ccache: + krb5_cc_destroy (kcontext, ccache); + return 0; + } else { + /* get_name pulls out just the name not the + type */ + strcpy(ccfile, krb5_cc_get_name(kcontext, ccache)); + (void) chown(ccfile, pwd->pw_uid, pwd->pw_gid); + krbflag = got_v5_tickets = 1; + return 1; + } +} + +int have_v5_tickets (me) + krb5_principal *me; +{ + if (krb5_cc_default (kcontext, &ccache)) + return 0; + if (krb5_cc_get_principal (kcontext, ccache, me)) { + krb5_cc_close (kcontext, ccache); + return 0; + } + krbflag = 1; + return 1; +} + +#ifdef KRB4_CONVERT +try_convert524 (kcontext, me) + krb5_context kcontext; + krb5_principal me; +{ + krb5_principal kpcserver; + krb5_error_code kpccode; + int kpcval; + krb5_creds increds, *v5creds; + CREDENTIALS v4creds; + + if (!got_v5_tickets) + return 0; + + /* or do this directly with krb524_convert_creds_kdc */ + krb524_init_ets(kcontext); + /* cc->ccache, already set up */ + /* client->me, already set up */ + if ((kpccode = krb5_build_principal(kcontext, + &kpcserver, + krb5_princ_realm(kcontext, me)->length, + krb5_princ_realm(kcontext, me)->data, + "krbtgt", + krb5_princ_realm(kcontext, me)->data, + NULL))) { + com_err("login/v4", kpccode, + "while creating service principal name"); + return 0; + } + + memset((char *) &increds, 0, sizeof(increds)); + increds.client = me; + increds.server = kpcserver; + increds.times.endtime = 0; + increds.keyblock.enctype = ENCTYPE_DES_CBC_CRC; + if ((kpccode = krb5_get_credentials(kcontext, 0, + ccache, + &increds, + &v5creds))) { + com_err("login/v4", kpccode, + "getting V5 credentials"); + return 0; + } + if ((kpccode = krb524_convert_creds_kdc(kcontext, + v5creds, + &v4creds))) { + com_err("login/v4", kpccode, + "converting to V4 credentials"); + return 0; + } + /* this is stolen from the v4 kinit */ + /* initialize ticket cache */ + if ((kpcval = in_tkt(v4creds.pname,v4creds.pinst) + != KSUCCESS)) { + com_err("login/v4", kpcval, + "trying to create the V4 ticket file"); + return 0; + } + /* stash ticket, session key, etc. for future use */ + if ((kpcval = krb_save_credentials(v4creds.service, + v4creds.instance, + v4creds.realm, + v4creds.session, + v4creds.lifetime, + v4creds.kvno, + &(v4creds.ticket_st), + v4creds.issue_date))) { + com_err("login/v4", kpcval, + "trying to save the V4 ticket"); + return 0; + } + got_v4_tickets = 1; + strcpy(tkfile, tkt_string()); + (void) chown(tkfile, pwd->pw_uid, pwd->pw_gid); + return 1; +} +#endif + +#ifdef KRB4_GET_TICKETS +try_krb4 (me, user_pwstring) + krb5_principal me; + char *user_pwstring; +{ + int krbval, kpass_ok = 0; + + krbval = krb_get_pw_in_tkt(username, "", realm, + "krbtgt", realm, + DEFAULT_TKT_LIFE, + user_pwstring); + + switch (krbval) { + case INTK_OK: + kpass_ok = 1; + krbflag = 1; + strcpy(tkfile, tkt_string()); + (void) chown(tkfile, pwd->pw_uid, pwd->pw_gid); + break; + /* These errors should be silent */ + /* So the Kerberos database can't be probed */ + case KDC_NULL_KEY: + case KDC_PR_UNKNOWN: + case INTK_BADPW: + case KDC_PR_N_UNIQUE: + case -1: + break; +#if 0 /* I want to see where INTK_W_NOTALL comes from before letting + kpass_ok be set in that case. KR */ + /* These should be printed but are not fatal */ + case INTK_W_NOTALL: + krbflag = 1; + kpass_ok = 1; + fprintf(stderr, "Kerberos error: %s\n", + krb_get_err_text(krbval)); + break; +#endif + default: + fprintf(stderr, "Kerberos error: %s\n", + krb_get_err_text(krbval)); + break; + } + got_v4_tickets = kpass_ok; + return kpass_ok; +} +#endif /* KRB4_GET_TICKETS */ + +/* Kerberos ticket-handling routines */ + +#ifdef KRB4_GET_TICKETS +/* call already conditionalized on login_krb4_get_tickets */ +/* + * Verify the Kerberos ticket-granting ticket just retrieved for the + * user. If the Kerberos server doesn't respond, assume the user is + * trying to fake us out (since we DID just get a TGT from what is + * supposedly our KDC). If the rcmd.<host> service is unknown (i.e., + * the local srvtab doesn't have it), let her in. + * + * Returns 1 for confirmation, -1 for failure, 0 for uncertainty. + */ +int verify_krb_v4_tgt (realm) + char *realm; +{ + char hostname[MAXHOSTNAMELEN], phost[BUFSIZ]; + struct hostent *hp; + KTEXT_ST ticket; + AUTH_DAT authdata; + unsigned long addr; + static /*const*/ char rcmd[] = "rcmd"; + char key[8]; + int krbval, retval, have_keys; + + if (gethostname(hostname, sizeof(hostname)) == -1) { + perror ("cannot retrieve local hostname"); + return -1; + } + strncpy (phost, krb_get_phost (hostname), sizeof (phost)); + phost[sizeof(phost)-1] = 0; + hp = gethostbyname (hostname); + if (!hp) { + perror ("cannot retrieve local host address"); + return -1; + } + memcpy ((char *) &addr, (char *)hp->h_addr, sizeof (addr)); + /* Do we have rcmd.<host> keys? */ +#if 0 /* Be paranoid. If srvtab exists, assume it must contain the + right key. */ + have_keys = read_service_key (rcmd, phost, realm, 0, KEYFILE, key) + ? 0 : 1; + memset (key, 0, sizeof (key)); +#else + have_keys = 0 == access (KEYFILE, F_OK); +#endif + krbval = krb_mk_req (&ticket, rcmd, phost, realm, 0); + if (krbval == KDC_PR_UNKNOWN) { + /* + * Our rcmd.<host> principal isn't known -- just assume valid + * for now? This is one case that the user _could_ fake out. + */ + if (have_keys) + return -1; + else + return 0; + } + else if (krbval != KSUCCESS) { + printf ("Unable to verify Kerberos TGT: %s\n", + krb_get_err_text(krbval)); +#ifndef SYSLOG42 + syslog (LOG_NOTICE|LOG_AUTH, "Kerberos TGT bad: %s", + krb_get_err_text(krbval)); +#endif + return -1; + } + /* got ticket, try to use it */ + krbval = krb_rd_req (&ticket, rcmd, phost, addr, &authdata, ""); + if (krbval != KSUCCESS) { + if (krbval == RD_AP_UNDEC && !have_keys) + retval = 0; + else { + retval = -1; + printf ("Unable to verify `rcmd' ticket: %s\n", + krb_get_err_text(krbval)); + } +#ifndef SYSLOG42 + syslog (LOG_NOTICE|LOG_AUTH, "can't verify rcmd ticket: %s;%s\n", + krb_get_err_text(krbval), + retval + ? "srvtab found, assuming failure" + : "no srvtab found, assuming success"); +#endif + goto EGRESS; + } + /* + * The rcmd.<host> ticket has been received _and_ verified. + */ + retval = 1; + /* do cleanup and return */ +EGRESS: + memset (&ticket, 0, sizeof (ticket)); + memset (&authdata, 0, sizeof (authdata)); + return retval; +} +#endif /* KRB4_GET_TICKETS */ + +/* call already conditionalized on login_krb5_get_tickets */ +/* + * Verify the Kerberos ticket-granting ticket just retrieved for the + * user. If the Kerberos server doesn't respond, assume the user is + * trying to fake us out (since we DID just get a TGT from what is + * supposedly our KDC). If the host/<host> service is unknown (i.e., + * the local keytab doesn't have it), let her in. + * + * Returns 1 for confirmation, -1 for failure, 0 for uncertainty. + */ +int verify_krb_v5_tgt (c, realm) + krb5_context c; + char *realm; +{ + char phost[BUFSIZ]; + krb5_ccache ccdef; + int retval, have_keys; + krb5_principal princ; + krb5_keyblock *kb = 0; + krb5_error_code krbval; + krb5_data packet; + krb5_auth_context auth_context = NULL; + krb5_ticket *ticket = NULL; + + /* XXX This is to work around a library bug. I'm not sure if it's + been fixed for beta-6, so leave this in for now. Remove it (and + fix the bug if necessary) after beta-6 ships. */ + sleep(2); + + /* get the server principal for the local host */ + /* (use defaults of "host" and canonicalized local name) */ + krbval = krb5_sname_to_principal(c, 0, 0, KRB5_NT_SRV_HST, &princ); + if (krbval) { + com_err ("login", krbval, "constructing local service name"); + return -1; + } + + /* since krb5_sname_to_principal has done the work for us, just + extract the name directly */ + strncpy(phost, krb5_princ_component(c, princ, 1)->data, BUFSIZ); + + /* Do we have host/<host> keys? */ + /* (use default keytab, kvno IGNORE_VNO to get the first match, + and enctype is currently ignored anyhow.) */ + krbval = krb5_kt_read_service_key (c, NULL, princ, 0, ENCTYPE_DES_CBC_CRC, &kb); + if (kb) + krb5_free_keyblock (c, kb); + /* any failure means we don't have keys at all. */ + have_keys = krbval ? 0 : 1; + + /* set up credential cache -- obeying KRB5_ENV_CCNAME set earlier */ + /* (KRB5_ENV_CCNAME == "KRB5CCNAME" via osconf.h) */ + if (krbval = krb5_cc_default(c, &ccdef)) { + com_err("login", krbval, "while getting default ccache"); + return -1; + } + /* talk to the kdc and construct the ticket */ + krbval = krb5_mk_req(c, &auth_context, 0, "host", phost, + 0, ccdef, &packet); + /* wipe the auth context for mk_req */ + if (auth_context) { + krb5_auth_con_free(c, auth_context); + auth_context = NULL; + } + if (krbval == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN) { + /* we have a service key, so something should be + in the database, therefore this error packet could + have come from an attacker. */ + if (have_keys) { retval = -1; goto EGRESS; } + /* but if it is unknown and we've got no key, we don't + have any security anyhow, so it is ok. */ + else { retval = 0; goto EGRESS; } + } + else if (krbval) { + com_err("login", krbval, + "Unable to verify Kerberos V5 TGT: %s", phost); +#ifndef SYSLOG42 + syslog (LOG_NOTICE|LOG_AUTH, "Kerberos V5 TGT bad: %s", + error_message(krbval)); +#endif + retval = -1; + goto EGRESS; + } + /* got ticket, try to use it */ + krbval = krb5_rd_req(c, &auth_context, &packet, + princ, NULL, NULL, &ticket); + if (krbval) { + if (!have_keys) + /* The krb5 errors aren't specified well, but I think + these values cover the cases we expect. */ + switch (krbval) { + /* no keytab */ + case ENOENT: + /* keytab found, missing entry */ +#if 0 /* Don't depend on the nameserver for security. Assume that if + we have a keytab, it must contain the right host/FQDN key. */ + case KRB5_KT_NOTFOUND: +#endif + retval = 0; + break; + default: + /* unexpected error: fail */ + retval = -1; + break; + } + else + /* Any error here is bad. */ + retval = -1; + com_err("login", krbval, "Unable to verify host ticket"); +#ifndef SYSLOG42 + syslog (LOG_NOTICE|LOG_AUTH, "can't verify v5 ticket: %s; %s\n", + error_message(krbval), + retval + ? "keytab found, assuming failure" + : "no keytab found, assuming success"); +#endif + goto EGRESS; + } + /* + * The host/<host> ticket has been received _and_ verified. + */ + retval = 1; + /* do cleanup and return */ +EGRESS: + if (auth_context) krb5_auth_con_free(c, auth_context); + krb5_free_principal(c, princ); + /* possibly ticket and packet need freeing here as well */ + /* memset (&ticket, 0, sizeof (ticket)); */ + return retval; +} + +destroy_tickets() +{ + krb5_context c; + krb5_ccache cache; + + if (login_krb5_get_tickets) { + krb5_init_context(&c); + + if(!krb5_cc_default(c, &cache)) + krb5_cc_destroy (c, cache); + } +#ifdef KRB4_GET_TICKETS + if (login_krb4_get_tickets) + dest_tkt(); +#endif /* KRB4_GET_TICKETS */ +} + +#endif /* KRB5_GET_TICKETS */ + +/* AFS support routines */ +#ifdef SETPAG + +int pagflag = 0; /* true if setpag() has been called */ + +static sigjmp_buf setpag_buf; + +static sigtype sigsys () +{ + siglongjmp(setpag_buf, 1); +} + +static int try_afscall () +{ + handler sa, osa; + volatile int retval = 0; + + (void) &retval; + handler_init (sa, sigsys); + handler_swap (SIGSYS, sa, osa); + if (sigsetjmp(setpag_buf, 1) == 0) { + setpag (); + retval = 1; + } + handler_set (SIGSYS, osa); + return retval; +} + +/* This doesn't seem to be declared in the AFS header files. */ +extern ktc_ForgetAllTokens (), setpag (); + +#define try_setpag() try_afscall(setpag) +#define try_unlog() try_afscall(ktc_ForgetAllTokens) +#endif /* SETPAG */ + +void +afs_login () +{ +#ifdef KRB4_GET_TICKETS +#ifdef SETPAG + if (login_krb4_get_tickets && pwd->pw_uid) { + /* Only reset the pag for non-root users. */ + /* This allows root to become anything. */ + pagflag = try_setpag (); + } +#endif +#endif /* KRB4_GET_TICKETS */ +#ifdef KRB_RUN_AKLOG + if (got_v4_tickets && login_krb_run_aklog) { + /* KPROGDIR is $(prefix)/bin */ + char aklog_path[MAXPATHLEN]; + struct stat st; + /* construct the name */ + /* get this from profile later */ + strcpy (aklog_path, KPROGDIR); + strcat (aklog_path, "/aklog"); + /* only run it if we can find it */ + if (stat (aklog_path, &st) == 0) { + system(aklog_path); + } + } +#endif /* KRB_RUN_AKLOG */ +} + +void +afs_cleanup () +{ +#ifdef SETPAG + if (pagflag) + try_unlog (); +#endif +} + +/* Main routines */ #define EXCL_AUTH_TEST if (rflag || kflag || Kflag || eflag || fflag || Fflag ) { \ fprintf(stderr, \ "login: only one of -r, -k, -K, -e, -F, and -f allowed.\n"); \ @@ -230,16 +1063,54 @@ typedef krb5_sigtype sigtype; exit(1);\ } -#ifndef HAVE_INITGROUPS -int initgroups(char* name, gid_t basegid) { - gid_t others[NGROUPS_MAX+1]; - int ngrps; - - others[0] = basegid; - ngrps = getgroups(NGROUPS_MAX, others+1); - return setgroups(ngrps+1, others); +static void +read_env_vars_from_file (filename) + char *filename; +{ + FILE *fp; + char *p, *eq; + char tbuf[MAXPATHLEN+2]; + + if ((fp = fopen("/etc/environment", "r")) != NULL) { + while (fgets(tbuf, sizeof(tbuf), fp)) { + if (tbuf[0] == '#') + continue; + eq = strchr(tbuf, '='); + if (eq == 0) + continue; + p = strchr (tbuf, '\n'); + if (p) + *p = 0; + *eq++ = 0; + /* Don't override, in case -p was used. */ + setenv (tbuf, eq, 0); + } + fclose(fp); + } } + +static void +log_repeated_failures (tty, hostname) + char *tty, *hostname; +{ + if (hostname) { +#ifdef UT_HOSTSIZE + syslog(LOG_ERR, + "REPEATED LOGIN FAILURES ON %s FROM %.*s, %.*s", + tty, UT_HOSTSIZE, hostname, UT_NAMESIZE, + username); +#else + syslog(LOG_ERR, + "REPEATED LOGIN FAILURES ON %s FROM %s, %.*s", + tty, hostname, UT_NAMESIZE, + username); #endif + } + else + syslog(LOG_ERR, + "REPEATED LOGIN FAILURES ON %s, %.*s", + tty, UT_NAMESIZE, username); +} int main(argc, argv) int argc; @@ -248,46 +1119,34 @@ int main(argc, argv) extern int optind; extern char *optarg, **environ; struct group *gr; - register int ch, i; - register char *p; + int ch; + char *p; int fflag, hflag, pflag, rflag, Fflag, cnt; int kflag, Kflag, eflag; int quietlog, passwd_req, ioctlval; sigtype timedout(); - char *domain, *salt, **envinit, *ttyn, *tty; + char *domain, **envinit, *ttyn, *tty; char tbuf[MAXPATHLEN + 2]; char *ttyname(), *stypeof(), *crypt(), *getpass(); time_t login_time; + int retval; +#ifdef KRB5_GET_TICKETS + krb5_principal me; +#endif /* KRB5_GET_TICKETS */ char *ccname = 0; /* name of forwarded cache */ -int retval; - + char *tz = 0; + off_t lseek(); -#ifdef POSIX_TERMIOS - struct termios tc; -#endif -#ifdef POSIX_SIGNALS - struct sigaction sa; -#endif + handler sa; -#ifdef POSIX_SIGNALS - (void)sigemptyset(&sa.sa_mask); - sa.sa_flags = 0; - sa.sa_handler = timedout; - (void)sigaction(SIGALRM, &sa, (struct sigaction *)0); -#else - (void)signal(SIGALRM, timedout); -#endif + handler_init (sa, timedout); + handler_set (SIGALRM, sa); (void)alarm((u_int)timeout); -#ifdef POSIX_SIGNALS - sa.sa_handler = SIG_IGN; - (void)sigaction(SIGALRM, &sa, (struct sigaction *)0); -#else - (void)signal(SIGQUIT, SIG_IGN); - (void)signal(SIGINT, SIG_IGN); -#endif -#ifdef HAVE_SETPRIORITY - (void)setpriority(PRIO_PROCESS, 0, 0 + PRIO_OFFSET); -#endif + + handler_init (sa, SIG_IGN); + handler_set (SIGQUIT, sa); + handler_set (SIGINT, sa); + setpriority(PRIO_PROCESS, 0, 0 + PRIO_OFFSET); #ifdef OQUOTA (void)quota(Q_SETUID, 0, 0, 0); #endif @@ -356,7 +1215,7 @@ int retval; *p = '\0'; hostname = optarg; break; -#ifdef KRB4 +#ifdef KRB4_KLOGIN case 'k': case 'K': EXCL_AUTH_TEST; @@ -382,7 +1241,7 @@ int retval; *p = '\0'; hostname = optarg; break; -#endif /* KRB4 */ +#endif /* KRB4_KLOGIN */ case 'e': EXCL_AUTH_TEST; if (getuid()) { @@ -422,85 +1281,14 @@ int retval; #endif (void)fcntl(0, F_SETFL, ioctlval); -#ifdef POSIX_TERMIOS - (void)tcgetattr(0, &tc); -#else - (void)ioctl(0, TIOCGETP, (char *)&sgttyb); -#endif - /* * If talking to an rlogin process, propagate the terminal type and * baud rate across the network. */ if (eflag) lgetstr(term, sizeof(term), "Terminal type"); -#ifdef POSIX_TERMIOS - if (rflag || kflag || Kflag || eflag) - doremoteterm(&tc); - tc.c_cc[VMIN] = 1; - tc.c_cc[VTIME] = 0; -#ifndef NO_INIT_CC - tc.c_cc[VERASE] = CERASE; - tc.c_cc[VKILL] = CKILL; - tc.c_cc[VEOF] = CEOF; - tc.c_cc[VINTR] = CINTR; - tc.c_cc[VQUIT] = CQUIT; - tc.c_cc[VSTART] = CSTART; - tc.c_cc[VSTOP] = CSTOP; -#ifndef CNUL -#define CNUL CEOL -#endif - tc.c_cc[VEOL] = CNUL; - /* The following are common extensions to POSIX */ -#ifdef VEOL2 - tc.c_cc[VEOL2] = CNUL; -#endif -#ifdef VSUSP -#if !defined(CSUSP) && defined(CSWTCH) -#define CSUSP CSWTCH -#endif - tc.c_cc[VSUSP] = CSUSP; -#endif -#ifdef VDSUSP - tc.c_cc[VDSUSP] = CDSUSP; -#endif -#ifdef VLNEXT - tc.c_cc[VLNEXT] = CLNEXT; -#endif -#ifdef VREPRINT - tc.c_cc[VREPRINT] = CRPRNT; -#endif -#ifdef VDISCRD - tc.c_cc[VDISCRD] = CFLUSH; -#endif -#ifdef VDISCARD -#ifndef CDISCARD -#define CDISCARD CFLUSH -#endif - tc.c_cc[VDISCARD] = CDISCARD; -#endif -#ifdef VWERSE - tc.c_cc[VWERSE] = CWERASE; -#endif -#ifdef VWERASE - tc.c_cc[VWERASE] = CWERASE; -#endif -#ifdef VSTATUS -#ifdef CSTATUS - tc.c_cc[VSTATUS] = CSTATUS; -#endif /* CSTATUS */ -#endif /* VSTATUS */ -#endif /* NO_INIT_CC */ - tcsetattr(0, TCSANOW, &tc); -#else - if (rflag || kflag || Kflag || eflag) - doremoteterm(&sgttyb); - sgttyb.sg_erase = CERASE; - sgttyb.sg_kill = CKILL; - (void)ioctl(0, TIOCSLTC, (char *)<c); - (void)ioctl(0, TIOCSETC, (char *)&tc); - (void)ioctl(0, TIOCSETP, (char *)&sgttyb); -#endif + term_init (rflag || kflag || Kflag || eflag); + for (cnt = getdtablesize(); cnt > 2; cnt--) (void) close(cnt); @@ -520,36 +1308,35 @@ int retval; openlog("login", LOG_ODELAY, LOG_AUTH); #endif /* 4.2 syslog */ +/******* begin askpw *******/ + /* overall: + ask for username if we don't have it already + look it up in local pw or shadow file (to get crypt string) + ask for password + try and get v4, v5 tickets with it + try and use the tickets against the local srvtab + if the password matches, always let them in + if the ticket decrypts, let them in. + v5 needs to work, does v4? + */ + +#ifdef KRB5_GET_TICKETS + k_init (ttyn); +#endif + for (cnt = 0;; username = NULL) { -#ifdef KRB4 - char pp[9], pp2[MAXPWSIZE], *namep; - int krbval; - char realm[REALM_SZ]; +#ifdef KRB5_GET_TICKETS int kpass_ok,lpass_ok; -#ifdef NOENCRYPTION -#define read_long_pw_string placebo_read_pw_string -#else -#define read_long_pw_string des_read_pw_string -#endif - int read_long_pw_string(); -#endif /* KRB4 */ -#if defined(TIOCSETD)&&(!defined(POSIX_TERMIOS)) - ioctlval = 0; - (void)ioctl(0, TIOCSETD, (char *)&ioctlval); -#endif + char user_pwstring[MAXPWSIZE]; + /* variables from v5 kinit */ +#endif /* KRB5_GET_TICKETS */ + if (username == NULL) { fflag = Fflag = 0; getloginname(); } - if ((pwd = getpwnam(username))) - salt = pwd->pw_passwd; - else - salt = "xx"; -#ifdef HAVE_SHADOW - if (spwd = getspnam(username)) - salt = spwd->sp_pwdp; -#endif + lookup_user (username); /* sets pwd */ /* if user not super-user, check for disabled logins */ if (pwd == NULL || pwd->pw_uid) @@ -580,96 +1367,56 @@ int retval; * If no remote login authentication and a password exists * for this user, prompt for one and verify it. */ - if (!passwd_req) break; -#ifdef HAVE_SHADOW - if (spwd) { - if (!*(spwd->sp_pwdp)) break; - } else -#endif - if (pwd && !*(pwd->pw_passwd)) - break; + if (!passwd_req) + break; -#ifdef KRB4 + if (! unix_needs_passwd ()) + break; + + /* we have several sets of code: + 1) get v5 tickets alone -DKRB5_GET_TICKETS + 2) get v4 tickets alone [** don't! only get them *with* v5 **] + 3) get both tickets -DKRB5_GET_TICKETS -DKRB4_GET_TICKETS + 3a) use krb524 calls to get the v4 tickets -DKRB4_CONVERT plus (3). + 4) get no tickets and use the password file (none of thes defined.) + + Likewise we need to (optionally?) test these tickets against local srvtabs. + */ +#ifdef KRB5_GET_TICKETS + if (login_krb5_get_tickets) { + /* rename these to something more verbose */ kpass_ok = 0; lpass_ok = 0; -#ifdef HAVE_SETPRIORITY - (void) setpriority(PRIO_PROCESS, 0, -4 + PRIO_OFFSET); -#endif - if (read_long_pw_string(pp2, sizeof(pp2)-1, "Password: ", 0)) { - /* reading password failed... */ -#ifdef HAVE_SETPRIORITY - (void) setpriority(PRIO_PROCESS, 0, 0 + PRIO_OFFSET); -#endif + setpriority(PRIO_PROCESS, 0, -4 + PRIO_OFFSET); + if (! k5_get_password (user_pwstring, sizeof (user_pwstring))) { goto bad_login; } - if (!pwd) /* avoid doing useless work */ - goto bad_login; - /* Modifications for Kerberos authentication -- asp */ - (void) strncpy(pp, pp2, sizeof(pp)); - pp[8]='\0'; - namep = crypt(pp, salt); - memset (pp, 0, sizeof(pp)); /* To the best of my recollection, Senator... */ + /* now that we have the password, we've obscured things + sufficiently, and can avoid trying tickets */ + if (!pwd) + goto bad_login; + + lpass_ok = unix_passwd_okay (user_pwstring); -#ifdef HAVE_SHADOW - if (spwd) - lpass_ok = !strcmp(namep, spwd->sp_pwdp); - else -#endif - lpass_ok = !strcmp (namep, pwd->pw_passwd); - if (pwd->pw_uid != 0) { /* Don't get tickets for root */ + try_krb5 (&me, user_pwstring); - if (krb_get_lrealm(realm, 1) != KSUCCESS) { - (void) strncpy(realm, KRB_REALM, sizeof(realm)); - } -#ifdef BIND_HACK - /* Set name server timeout to be reasonable, - so that people don't take 5 minutes to - log in. Can you say abstraction violation? */ - _res.retrans = 1; -#endif /* BIND_HACK */ +#ifdef KRB4_GET_TICKETS + if (login_krb4_get_tickets + && (!got_v5_tickets + || !login_krb4_convert)) + try_krb4 (me, user_pwstring); +#endif + krbflag = got_v5_tickets || got_v4_tickets; + memset (user_pwstring, 0, sizeof(user_pwstring)); + /* password wiped, so we can relax */ + setpriority(PRIO_PROCESS, 0, 0 + PRIO_OFFSET); - krbval = krb_get_pw_in_tkt(username, "", realm, "krbtgt", - realm, DEFAULT_TKT_LIFE, pp2); - memset (pp2, 0, sizeof(pp2)); -#ifdef HAVE_SETPRIORITY - (void) setpriority(PRIO_PROCESS, 0, 0 + PRIO_OFFSET); -#endif - switch (krbval) { - case INTK_OK: - kpass_ok = 1; - krbflag = 1; - strcpy(tkfile, tkt_string()); - (void) chown(tkfile, pwd->pw_uid, pwd->pw_gid); - break; - - /* These errors should be silent */ - /* So the Kerberos database can't be probed */ - case KDC_NULL_KEY: - case KDC_PR_UNKNOWN: - case INTK_BADPW: - case KDC_PR_N_UNIQUE: - case -1: - break; - /* These should be printed but are not fatal */ - case INTK_W_NOTALL: - krbflag = 1; - kpass_ok = 1; - fprintf(stderr, "Kerberos error: %s\n", - krb_err_txt[krbval]); - break; - default: - fprintf(stderr, "Kerberos error: %s\n", - krb_err_txt[krbval]); - break; - } } else { - (void) memset (pp2, 0, sizeof(pp2)); -#ifdef HAVE_SETPRIORITY - (void) setpriority(PRIO_PROCESS, 0, 0 + PRIO_OFFSET); -#endif + memset (user_pwstring, 0, sizeof(user_pwstring)); + setpriority(PRIO_PROCESS, 0, 0 + PRIO_OFFSET); } /* Policy: If local password is good, user is good. @@ -682,52 +1429,41 @@ int retval; if (kpass_ok) */ if (lpass_ok) - break; -bad_login: - if (krbflag) - dest_tkt(); /* clean up tickets if login fails */ -#else /* !KRB4 */ -#ifdef HAVE_SETPRIORITY - (void) setpriority(PRIO_PROCESS, 0, -4 + PRIO_OFFSET); -#endif - p = crypt(getpass("Password:"), salt); -#ifdef HAVE_SETPRIORITY - (void) setpriority(PRIO_PROCESS, 0, 0 + PRIO_OFFSET); -#endif -#ifdef HAVE_SHADOW - if (spwd && !strcmp(p, spwd->sp_pwdp)) - break; - else -#endif - if (pwd && !strcmp(p, pwd->pw_passwd)) break; -#endif /* KRB4 */ + if (got_v5_tickets + && verify_krb_v5_tgt(kcontext, realm) != -1) + break; /* we're ok */ +#ifdef KRB4_GET_TICKETS + if (login_krb4_get_tickets) { + if (got_v4_tickets + && ! got_v5_tickets + && verify_krb_v4_tgt(realm) != -1) + break; /* we're ok */ + } +#endif /* KRB4_GET_TICKETS */ + bad_login: + setpriority(PRIO_PROCESS, 0, 0 + PRIO_OFFSET); + if (krbflag) + destroy_tickets(); /* clean up tickets if login fails */ + } +#endif /* KRB5_GET_TICKETS */ +#ifdef OLD_PASSWD + p = getpass ("Password:"); + /* conventional password only */ + if (unix_passwd_okay (p)) + break; +#endif /* OLD_PASSWD */ printf("Login incorrect\n"); if (++cnt >= 5) { - if (hostname) -#ifdef UT_HOSTSIZE - syslog(LOG_ERR, - "REPEATED LOGIN FAILURES ON %s FROM %.*s, %.*s", - tty, UT_HOSTSIZE, hostname, UT_NAMESIZE, - username); -#else - syslog(LOG_ERR, - "REPEATED LOGIN FAILURES ON %s FROM %s, %.*s", - tty, hostname, UT_NAMESIZE, - username); -#endif - else - syslog(LOG_ERR, - "REPEATED LOGIN FAILURES ON %s, %.*s", - tty, UT_NAMESIZE, username); + log_repeated_failures (tty, hostname); /* irix has no tichpcl */ #ifdef TIOCHPCL (void)ioctl(0, TIOCHPCL, (char *)0); #endif sleepexit(1); } - } + } /* end of password retry loop */ /* committed to login -- turn off timeout */ (void)alarm((u_int)0); @@ -770,7 +1506,6 @@ bad_login: sleepexit(0); } #endif - if (chdir(pwd->pw_dir) < 0) { printf("No directory %s!\n", pwd->pw_dir); if (chdir("/")) @@ -801,17 +1536,26 @@ bad_login: (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid); (void)chmod(ttyn, 0620); -#ifdef KRB4 -#ifdef SETPAG - if (pwd->pw_uid) { - /* Only reset the pag for non-root users. */ - /* This allows root to become anything. */ - pagflag = 1; - setpag(); + +#ifdef KRB4_GET_TICKETS + if (login_krb4_get_tickets && login_krb4_convert && !got_v4_tickets) { + + /* Maybe telnetd got tickets for us? */ + if (!got_v5_tickets && have_v5_tickets (&me)) + got_v5_tickets = 1; + + if (got_v5_tickets) + try_convert524 (kcontext, me); + } #endif - /* Fork so that we can call kdestroy */ - dofork(); + +#ifdef KRB5_GET_TICKETS + if (login_krb4_get_tickets || login_krb5_get_tickets) { + /* Fork so that we can call kdestroy */ + dofork(); + } +#endif /* KRB4_GET_TICKETS */ /* If the user's shell does not do job control we should put it in a different process group than than us, and set the tty process group @@ -819,25 +1563,48 @@ bad_login: telnetd or rlogind if they don't properly detach from their controlling tty, which is the case (under SunOS at least.) */ - { int p = getpid(); - if (setpgrp(p,p) < 0) perror("login.krb5: setpgrp"); - if (ioctl(0, TIOCSPGRP, &p) < 0) perror("login.krb5: tiocspgrp"); - } - -/* SunOS has an ioctl which can set the controlling tty and make sure - that no other process also has it. That's exactly what we want to - do for the shell we are about to exec, since it is the documented - way to avoid the problem noted just above. */ + { + int p = getpid(); + struct sigaction sa, osa; -#ifdef sun -#ifdef TIOCSCTTY - { if (setsid() < 0) perror("login.krb5: setsid"); - if (ioctl(0, TIOCSCTTY, 1) < 0) perror("login.krb5: tiocsctty"); - } + /* this will set the PGID to the PID. */ +#ifdef HAVE_SETPGID + if (setpgid(p,p) < 0) perror("login.krb5: setpgid"); +#else +#ifdef SETPGRP_TWOARG + if (setpgrp(p,p) < 0) perror("login.krb5: setpgrp"); +#else + if (setpgrp() < 0) perror("login.krb5: setpgrp"); #endif #endif -#endif /* KRB4 */ + /* This will cause SIGTTOU to be ignored for the duration + of the TIOCSPGRP. If this is not done, and the parent's + process group is the foreground pgrp of the tty, then + this will suspend the child, which is bad. */ + + sa.sa_flags = 0; + sa.sa_handler = SIG_IGN; + sigemptyset(&(sa.sa_mask)); + + if (sigaction(SIGTTOU, &sa, &osa)) + perror("login.krb5: sigaction(SIGTTOU, SIG_IGN)"); + + /* This will set the foreground process group of the + controlling terminal to this process group (containing + only this process). */ +#ifdef HAVE_TCSETPGRP + if (tcsetpgrp(0, p) < 0) perror("login.krb5: tcsetpgrp"); +#else + if (ioctl(0, TIOCSPGRP, &p) < 0) perror("login.krb5: tiocspgrp"); +#endif + + /* This will reset the SIGTTOU handler */ + + if (sigaction(SIGTTOU, &osa, NULL)) + perror("login.krb5: sigaction(SIGTTOU, [old handler])"); + } + (void)setgid((gid_t) pwd->pw_gid); (void) initgroups(username, pwd->pw_gid); @@ -872,69 +1639,41 @@ bad_login: pwd->pw_shell = BSHELL; #if defined(NTTYDISC) && defined(TIOCSETD) /* turn on new line discipline for the csh */ - else if (!strcmp(pwd->pw_shell, "/bin/csh")) { + if (!strcmp(pwd->pw_shell, "/bin/csh")) { ioctlval = NTTYDISC; (void)ioctl(0, TIOCSETD, (char *)&ioctlval); } #endif ccname = getenv("KRB5CCNAME"); /* save cache */ + tz = getenv("TZ"); /* and time zone */ /* destroy environment unless user has requested preservation */ - envinit = (char **)malloc(MAXENVIRON * sizeof(char *)); - if (envinit == 0) { + if (!pflag) { + envinit = (char **)malloc(MAXENVIRON * sizeof(char *)); + if (envinit == 0) { fprintf(stderr, "Can't malloc empty environment.\n"); sleepexit(1); + } + envinit[0] = NULL; + environ = envinit; } - if (!pflag) - environ = envinit; - i = 0; + setenv ("LOGNAME", pwd->pw_name, 1); + setenv ("LOGIN", pwd->pw_name, 1); -#ifdef _IBMR2 - { - FILE *fp; - if ((fp = fopen("/etc/environment", "r")) != NULL) { - while(fgets(tbuf, sizeof(tbuf), fp)) { - if ((tbuf[0] == '#') || (strchr(tbuf, '=') == 0)) - continue; - for (p = tbuf; *p; p++) - if (*p == '\n') { - *p = '\0'; - break; - } - envinit[i++] = strsave(tbuf); - } - fclose(fp); - } - } -#endif -/* Set login timezone for date information (PDG) */ -#ifdef __sgi__ - { - FILE *fp; - if ((fp = fopen("/etc/TIMEZONE", "r")) != NULL) { - while(fgets(tbuf, sizeof(tbuf), fp)) { - if ((tbuf[0] == '#') || (strchr(tbuf, '=') == 0)) - continue; - for (p = tbuf; *p; p++) - if (*p == '\n') { - *p = '\0'; - break; - } - envinit[i++] = strsave(tbuf); - } - fclose(fp); - } - } + /* read the /etc/environment file on AIX */ +#ifdef HAVE_ETC_ENVIRONMENT + read_env_vars_from_file ("/etc/environment"); #endif - sprintf(tbuf,"LOGNAME=%s",pwd->pw_name); - envinit[i++] = strsave(tbuf); - sprintf(tbuf,"LOGIN=%s",pwd->pw_name); - envinit[i++] = strsave(tbuf); - - envinit[i++] = NULL; + /* Set login timezone for date information (sgi PDG) */ +#ifdef HAVE_ETC_TIMEZONE + read_env_vars_from_file ("/etc/TIMEZONE"); +#else + if (tz) + setenv ("TZ", tz, 0); +#endif if (ccname) setenv("KRB5CCNAME", ccname, 0); @@ -946,24 +1685,24 @@ bad_login: if (term[0] == '\0') (void) strncpy(term, stypeof(tty), sizeof(term)); - (void)setenv("TERM", term, 0); -#ifdef KRB4 + if (term[0]) + (void)setenv("TERM", term, 0); +#ifdef KRB4_GET_TICKETS /* tkfile[0] is only set if we got tickets above */ - if (tkfile[0]) + if (login_krb4_get_tickets && tkfile[0]) (void) setenv(KRB_ENVIRON, tkfile, 1); -#endif /* KRB4 */ - -#if 0 - strcpy(wgfile, "/tmp/wg.XXXXXX"); - mktemp(wgfile); - setenv("WGFILE", wgfile, 0); -#endif +#endif /* KRB4_GET_TICKETS */ +#ifdef KRB5_GET_TICKETS + /* ccfile[0] is only set if we got tickets above */ + if (login_krb5_get_tickets && ccfile[0]) + (void) setenv(KRB5_ENV_CCNAME, ccfile, 1); +#endif /* KRB5_GET_TICKETS */ if (tty[sizeof("tty")-1] == 'd') syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name); if (pwd->pw_uid == 0) if (hostname) -#ifdef KRB4 +#ifdef KRB4_KLOGIN if (kdata) { /* @*$&@#*($)#@$ syslog doesn't handle very many arguments */ @@ -983,7 +1722,7 @@ bad_login: #endif syslog(LOG_NOTICE, "%s", buf); } else { -#endif /* KRB4 */ +#endif /* KRB4_KLOGIN */ #ifdef UT_HOSTSIZE syslog(LOG_NOTICE, "ROOT LOGIN %s FROM %.*s", tty, UT_HOSTSIZE, hostname); @@ -991,7 +1730,7 @@ bad_login: syslog(LOG_NOTICE, "ROOT LOGIN %s FROM %s", tty, hostname); #endif -#ifdef KRB4 +#ifdef KRB4_KLOGIN } else if (kdata) { @@ -1001,43 +1740,30 @@ bad_login: kdata->pname, kdata->pinst, kdata->prealm); } -#endif /* KRB4 */ +#endif /* KRB4_KLOGIN */ else syslog(LOG_NOTICE, "ROOT LOGIN %s", tty); - if (!quietlog) { - struct stat st; + afs_login (); -#ifdef KRB4 + if (!quietlog) { +#ifdef KRB4_KLOGIN if (!krbflag && !fflag && !Fflag && !eflag ) printf("\nWarning: No Kerberos tickets obtained.\n\n"); -#endif /* KRB4 */ -#ifndef NO_MOTD - motd(); -#endif - (void)sprintf(tbuf, "%s/%s", MAILDIR, pwd->pw_name); - if (stat(tbuf, &st) == 0 && st.st_size != 0) - printf("You have %smail.\n", - (st.st_mtime > st.st_atime) ? "new " : ""); +#endif /* KRB4_KLOGIN */ + motd (); + check_mail (); } #ifndef OQUOTA if (! access( QUOTAWARN, X_OK)) (void) system(QUOTAWARN); #endif -#ifdef POSIX_SIGNALS - sa.sa_handler = SIG_DFL; - (void)sigaction(SIGALRM, &sa, (struct sigaction *)0); - (void)sigaction(SIGQUIT, &sa, (struct sigaction *)0); - (void)sigaction(SIGINT, &sa, (struct sigaction *)0); - - sa.sa_handler = SIG_IGN; - (void)sigaction(SIGTSTP, &sa, (struct sigaction *)0); -#else - (void)signal(SIGALRM, SIG_DFL); - (void)signal(SIGQUIT, SIG_DFL); - (void)signal(SIGINT, SIG_DFL); - (void)signal(SIGTSTP, SIG_IGN); -#endif + handler_init (sa, SIG_DFL); + handler_set (SIGALRM, sa); + handler_set (SIGQUIT, sa); + handler_set (SIGINT, sa); + handler_init (sa, SIG_IGN); + handler_set (SIGTSTP, sa); tbuf[0] = '-'; (void) strcpy(tbuf + 1, (p = strrchr(pwd->pw_shell, '/')) ? @@ -1048,6 +1774,150 @@ bad_login: exit(0); } +char *speeds[] = { + "0", "50", "75", "110", "134", "150", "200", "300", "600", + "1200", "1800", "2400", "4800", "9600", "19200", "38400", +}; +#define NSPEEDS (sizeof(speeds) / sizeof(speeds[0])) + +#ifdef POSIX_TERMIOS +/* this must be in sync with the list above */ +speed_t b_speeds[] = { + B0, B50, B75, B110, B134, B150, B200, B300, B600, + B1200, B1800, B2400, B4800, B9600, B19200, B38400, +}; +#endif + +term_init (do_rlogin) +{ + int line_speed = -1; + + if (do_rlogin) { + register char *cp = strchr(term, '/'), **cpp; + char *speed; + + if (cp) { + *cp++ = '\0'; + speed = cp; + cp = strchr(speed, '/'); + if (cp) + *cp++ = '\0'; + for (cpp = speeds; cpp < &speeds[NSPEEDS]; cpp++) + if (strcmp(*cpp, speed) == 0) { + line_speed = cpp-speeds; + break; + } + } + } +#ifdef POSIX_TERMIOS + { + struct termios tc; + + (void)tcgetattr(0, &tc); + if (line_speed != -1) { + cfsetispeed(&tc, b_speeds[line_speed]); + cfsetospeed(&tc, b_speeds[line_speed]); + } + tc.c_cc[VMIN] = 1; + tc.c_cc[VTIME] = 0; +#ifndef NO_INIT_CC + tc.c_cc[VERASE] = CERASE; + tc.c_cc[VKILL] = CKILL; + tc.c_cc[VEOF] = CEOF; + tc.c_cc[VINTR] = CINTR; + tc.c_cc[VQUIT] = CQUIT; + tc.c_cc[VSTART] = CSTART; + tc.c_cc[VSTOP] = CSTOP; +#ifndef CNUL +#define CNUL CEOL +#endif + tc.c_cc[VEOL] = CNUL; + /* The following are common extensions to POSIX */ +#ifdef VEOL2 + tc.c_cc[VEOL2] = CNUL; +#endif +#ifdef VSUSP +#if !defined(CSUSP) && defined(CSWTCH) +#define CSUSP CSWTCH +#endif + tc.c_cc[VSUSP] = CSUSP; +#endif +#ifdef VDSUSP + tc.c_cc[VDSUSP] = CDSUSP; +#endif +#ifdef VLNEXT + tc.c_cc[VLNEXT] = CLNEXT; +#endif +#ifdef VREPRINT + tc.c_cc[VREPRINT] = CRPRNT; +#endif +#ifdef VDISCRD + tc.c_cc[VDISCRD] = CFLUSH; +#endif +#ifdef VDISCARD +#ifndef CDISCARD +#define CDISCARD CFLUSH +#endif + tc.c_cc[VDISCARD] = CDISCARD; +#endif +#ifdef VWERSE + tc.c_cc[VWERSE] = CWERASE; +#endif +#ifdef VWERASE + tc.c_cc[VWERASE] = CWERASE; +#endif +#if defined (VSTATUS) && defined (CSTATUS) + tc.c_cc[VSTATUS] = CSTATUS; +#endif /* VSTATUS && CSTATUS */ +#endif /* NO_INIT_CC */ + /* set all standard echo, edit, and job control options */ + /* but leave any extensions */ + tc.c_lflag |= ECHO|ECHOE|ECHOK|ICANON|ISIG|IEXTEN; + tc.c_lflag &= ~(NOFLSH|TOSTOP); +#ifdef ECHOCTL + /* Not POSIX, but if we have it, we probably want it */ + tc.c_lflag |= ECHOCTL; +#endif +#ifdef ECHOKE + /* Not POSIX, but if we have it, we probably want it */ + tc.c_lflag |= ECHOKE; +#endif + tc.c_iflag |= ICRNL|BRKINT; + tc.c_oflag |= ONLCR|OPOST|TAB3; + tcsetattr(0, TCSANOW, &tc); + } + +#else /* not POSIX_TERMIOS */ + + { + struct sgttyb sgttyb; + static struct tchars tc = { + CINTR, CQUIT, CSTART, CSTOP, CEOT, CBRK + }; + static struct ltchars ltc = { + CSUSP, CDSUSP, CRPRNT, CFLUSH, CWERASE, CLNEXT + }; + + (void) ioctl(0, TIOCGETP, (char *)&sgttyb); + if (line_speed != -1) + sgttyb.sg_ispeed = sgttyb.sg_ospeed = line_speed; + sgttyb.sg_flags = ECHO|CRMOD|ANYP|XTABS; + sgttyb.sg_erase = CERASE; + sgttyb.sg_kill = CKILL; + (void)ioctl(0, TIOCSLTC, (char *)<c); + (void)ioctl(0, TIOCSETC, (char *)&tc); + (void)ioctl(0, TIOCSETP, (char *)&sgttyb); +#if defined(TIOCSETD) + { + int ioctlval; + ioctlval = 0; + (void)ioctl(0, TIOCSETD, (char *)&ioctlval); + } +#endif + } +#endif +} + void getloginname() { register int ch; @@ -1096,58 +1966,42 @@ int rootterm(tty) #endif /* HAVE_TTYENT_H */ } -#ifdef POSIX_SETJMP +#ifndef NO_MOTD sigjmp_buf motdinterrupt; -#else -jmp_buf motdinterrupt; -#endif +sigtype +sigint() +{ + siglongjmp(motdinterrupt, 1); +} void motd() { register int fd, nchars; char tbuf[8192]; - sigtype sigint(); -#ifdef POSIX_SIGNALS - struct sigaction sa, osa; -#else - sigtype (*oldint)(); -#endif + handler sa, osa; if ((fd = open(MOTDFILE, O_RDONLY, 0)) < 0) return; -#ifdef POSIX_SIGNALS - (void)sigemptyset(&sa.sa_mask); - sa.sa_flags = 0; - sa.sa_handler = sigint; - (void)sigaction(SIGINT, &sa, &osa); -#else - oldint = signal(SIGINT, sigint); -#endif -#ifdef POSIX_SETJMP + handler_init (sa, sigint); + handler_swap (SIGINT, sa, osa); if (sigsetjmp(motdinterrupt, 1) == 0) while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0) (void)write(fileno(stdout), tbuf, nchars); -#else - if (setjmp(motdinterrupt) == 0) - while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0) - (void)write(fileno(stdout), tbuf, nchars); -#endif -#ifdef POSIX_SIGNALS - (void)sigaction(SIGINT, &osa, (struct sigaction *)0); -#else - (void)signal(SIGINT, oldint); -#endif + handler_set (SIGINT, osa); (void)close(fd); } - -sigtype -sigint() -{ -#ifdef POSIX_SETJMP - siglongjmp(motdinterrupt, 1); #else - longjmp(motdinterrupt, 1); +void motd () { } #endif + +void check_mail () +{ + char tbuf[MAXPATHLEN+2]; + struct stat st; + (void)sprintf(tbuf, "%s/%s", MAILDIR, pwd->pw_name); + if (stat(tbuf, &st) == 0 && st.st_size != 0) + printf("You have %smail.\n", + (st.st_mtime > st.st_atime) ? "new " : ""); } void checknologin() @@ -1199,7 +2053,11 @@ void dolastlog(quiet, tty) } #undef UNKNOWN +#ifdef __hpux +#define UNKNOWN 0 +#else #define UNKNOWN "su" +#endif char * stypeof(ttyid) @@ -1230,7 +2088,7 @@ int doremotelogin(host) return(ruserok(host, (pwd->pw_uid == 0), rusername, username)); } -#ifdef KRB4 +#ifdef KRB4_KLOGIN int do_krb_login(host, strict) char *host; int strict; @@ -1276,7 +2134,7 @@ int do_krb_login(host, strict) instance, &sin, (struct sockaddr_in *)0, kdata, "", (bit_64 *) 0, version))) { - printf("Kerberos rlogin failed: %s\r\n",krb_err_txt[rc]); + printf("Kerberos rlogin failed: %s\r\n",krb_get_err_text(rc)); if (strict) { paranoid: /* @@ -1322,7 +2180,7 @@ paranoid: } return(0); } -#endif /* KRB4 */ +#endif /* KRB4_KLOGIN */ void lgetstr(buf, cnt, err) char *buf, *err; @@ -1344,79 +2202,19 @@ void lgetstr(buf, cnt, err) } while (ch); } -char *speeds[] = { - "0", "50", "75", "110", "134", "150", "200", "300", "600", - "1200", "1800", "2400", "4800", "9600", "19200", "38400", -}; -#define NSPEEDS (sizeof(speeds) / sizeof(speeds[0])) - -#ifdef POSIX_TERMIOS -/* this must be in sync with the list above */ -speed_t b_speeds[] = { - B0, B50, B75, B110, B134, B150, B200, B300, B600, - B1200, B1800, B2400, B4800, B9600, B19200, B38400, -}; -#endif - -void doremoteterm(tp) -#ifdef POSIX_TERMIOS - struct termios *tp; -#else - struct sgttyb *tp; -#endif -{ - register char *cp = strchr(term, '/'), **cpp; - char *speed; - - if (cp) { - *cp++ = '\0'; - speed = cp; - cp = strchr(speed, '/'); - if (cp) - *cp++ = '\0'; - for (cpp = speeds; cpp < &speeds[NSPEEDS]; cpp++) - if (strcmp(*cpp, speed) == 0) { -#ifdef POSIX_TERMIOS - cfsetispeed(tp, b_speeds[cpp-speeds]); - cfsetospeed(tp, b_speeds[cpp-speeds]); -#else - tp->sg_ispeed = tp->sg_ospeed = cpp-speeds; -#endif - break; - } - } -#ifdef POSIX_TERMIOS - /* set all standard echo, edit, and job control options */ - /* but leave any extensions */ - tp->c_lflag |= ECHO|ECHOE|ECHOK|ICANON|ISIG|IEXTEN; - tp->c_lflag &= ~(NOFLSH|TOSTOP); -#ifdef ECHOCTL - /* Not POSIX, but if we have it, we probably want it */ - tp->c_lflag |= ECHOCTL; -#endif -#ifdef ECHOKE - /* Not POSIX, but if we have it, we probably want it */ - tp->c_lflag |= ECHOKE; -#endif - tp->c_iflag |= ICRNL|BRKINT; - tp->c_oflag |= ONLCR|OPOST|TAB3; -#else /* !POSIX_TERMIOS */ - tp->sg_flags = ECHO|CRMOD|ANYP|XTABS; -#endif -} - void sleepexit(eval) int eval; { -#ifdef KRB4 - if (krbflag) - (void) dest_tkt(); -#endif /* KRB4 */ +#ifdef KRB4_GET_TICKETS + if (login_krb4_get_tickets && krbflag) + (void) destroy_tickets(); +#endif /* KRB4_GET_TICKETS */ sleep((u_int)5); exit(eval); } -#ifdef KRB4 +#ifdef KRB4_GET_TICKETS +/* call already conditionalized on login_krb4_get_tickets */ /* * This routine handles cleanup stuff, and the like. * It exits only in the child process. @@ -1431,10 +2229,15 @@ dofork() update_ref_count(1); #endif if(!(child=fork())) - return; /* Child process */ + return; /* Child process returns */ + + /* The parent continues here */ { /* Try and get rid of our controlling tty. On SunOS, this may or may not work depending on if our parent did a setsid before exec-ing us. */ +#ifndef __linux__ + /* On linux, TIOCNOTTY causes us to die on a + SIGHUP, so don't even try it. */ #ifdef TIOCNOTTY { int fd; if ((fd = open("/dev/tty", O_RDWR)) >= 0) { @@ -1443,10 +2246,15 @@ dofork() } } #endif +#endif /* __linux__ */ #ifdef HAVE_SETSID (void)setsid(); #endif +#ifdef SETPGRP_TWOARG (void)setpgrp(0, 0); +#else + (void)setpgrp(); +#endif } /* Setup stuff? This would be things we could do in parallel with login */ @@ -1464,12 +2272,9 @@ dofork() #endif /* Cleanup stuff */ - /* Run dest_tkt to destroy tickets */ - (void) dest_tkt(); /* If this fails, we lose quietly */ -#ifdef SETPAG - if (pagflag) - ktc_ForgetAllTokens(); -#endif + /* Run destroy_tickets to destroy tickets */ + (void) destroy_tickets(); /* If this fails, we lose quietly */ + afs_cleanup (); #ifdef _IBMR2 update_ref_count(-1); #endif @@ -1477,7 +2282,7 @@ dofork() /* Leave */ exit(0); } -#endif /* KRB4 */ +#endif /* KRB4_GET_TICKETS */ #ifndef HAVE_STRSAVE |