Path: blob/main/crypto/heimdal/appl/login/login.c
107833 views
/*1* Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan2* (Royal Institute of Technology, Stockholm, Sweden).3* All rights reserved.4*5* Redistribution and use in source and binary forms, with or without6* modification, are permitted provided that the following conditions7* are met:8*9* 1. Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11*12* 2. Redistributions in binary form must reproduce the above copyright13* notice, this list of conditions and the following disclaimer in the14* documentation and/or other materials provided with the distribution.15*16* 3. Neither the name of the Institute nor the names of its contributors17* may be used to endorse or promote products derived from this software18* without specific prior written permission.19*20* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND21* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE22* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE23* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE24* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL25* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS26* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)27* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT28* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY29* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF30* SUCH DAMAGE.31*/3233#include "login_locl.h"34#ifdef HAVE_CAPABILITY_H35#include <capability.h>36#endif37#ifdef HAVE_SYS_CAPABILITY_H38#include <sys/capability.h>39#endif40#ifdef HAVE_CRYPT_H41#include <crypt.h>42#endif4344RCSID("$Id$");4546static int login_timeout = 60;4748static int49start_login_process(void)50{51char *prog, *argv0;52prog = login_conf_get_string("login_program");53if(prog == NULL)54return 0;55argv0 = strrchr(prog, '/');5657if(argv0)58argv0++;59else60argv0 = prog;6162return simple_execle(prog, argv0, NULL, env);63}6465static int66start_logout_process(void)67{68char *prog, *argv0;69pid_t pid;7071prog = login_conf_get_string("logout_program");72if(prog == NULL)73return 0;74argv0 = strrchr(prog, '/');7576if(argv0)77argv0++;78else79argv0 = prog;8081pid = fork();82if(pid == 0) {83/* avoid getting signals sent to the shell */84setpgid(0, getpid());85return 0;86}87if(pid == -1)88err(1, "fork");89/* wait for the real login process to exit */90#ifdef HAVE_SETPROCTITLE91setproctitle("waitpid %d", pid);92#endif93while(1) {94int status;95int ret;96ret = waitpid(pid, &status, 0);97if(ret > 0) {98if(WIFEXITED(status) || WIFSIGNALED(status)) {99execle(prog, argv0, NULL, env);100err(1, "exec %s", prog);101}102} else if(ret < 0)103err(1, "waitpid");104}105}106107static void108exec_shell(const char *shell, int fallback)109{110char *sh;111const char *p;112113extend_env(NULL);114if(start_login_process() < 0)115warn("login process");116start_logout_process();117118p = strrchr(shell, '/');119if(p)120p++;121else122p = shell;123if (asprintf(&sh, "-%s", p) == -1)124errx(1, "Out of memory");125execle(shell, sh, NULL, env);126if(fallback){127warnx("Can't exec %s, trying %s",128shell, _PATH_BSHELL);129execle(_PATH_BSHELL, "-sh", NULL, env);130err(1, "%s", _PATH_BSHELL);131}132err(1, "%s", shell);133}134135static enum { NONE = 0, AUTH_KRB5 = 2, AUTH_OTP = 3 } auth;136137#ifdef OTP138static OtpContext otp_ctx;139140static int141otp_verify(struct passwd *pwd, const char *password)142{143return (otp_verify_user (&otp_ctx, password));144}145#endif /* OTP */146147148static int pag_set = 0;149150#ifdef KRB5151static krb5_context context;152static krb5_ccache id, id2;153154static int155krb5_verify(struct passwd *pwd, const char *password)156{157krb5_error_code ret;158krb5_principal princ;159160ret = krb5_parse_name(context, pwd->pw_name, &princ);161if(ret)162return 1;163ret = krb5_cc_new_unique(context, krb5_cc_type_memory, NULL, &id);164if(ret) {165krb5_free_principal(context, princ);166return 1;167}168ret = krb5_verify_user_lrealm(context,169princ,170id,171password,1721,173NULL);174krb5_free_principal(context, princ);175return ret;176}177178static int179krb5_start_session (const struct passwd *pwd)180{181krb5_error_code ret;182char residual[64];183184/* copy credentials to file cache */185snprintf(residual, sizeof(residual), "FILE:/tmp/krb5cc_%u",186(unsigned)pwd->pw_uid);187krb5_cc_resolve(context, residual, &id2);188ret = krb5_cc_copy_cache(context, id, id2);189if (ret == 0)190add_env("KRB5CCNAME", residual);191else {192krb5_cc_destroy (context, id2);193return ret;194}195krb5_cc_close(context, id2);196krb5_cc_destroy(context, id);197return 0;198}199200static void201krb5_finish (void)202{203krb5_free_context(context);204}205206static void207krb5_get_afs_tokens (const struct passwd *pwd)208{209char cell[64];210char *pw_dir;211krb5_error_code ret;212213if (!k_hasafs ())214return;215216ret = krb5_cc_default(context, &id2);217218if (ret == 0) {219pw_dir = pwd->pw_dir;220221if (!pag_set) {222k_setpag();223pag_set = 1;224}225226if(k_afs_cell_of_file(pw_dir, cell, sizeof(cell)) == 0)227krb5_afslog_uid_home (context, id2,228cell, NULL, pwd->pw_uid, pwd->pw_dir);229krb5_afslog_uid_home (context, id2, NULL, NULL,230pwd->pw_uid, pwd->pw_dir);231krb5_cc_close (context, id2);232}233}234235#endif /* KRB5 */236237static int f_flag;238static int p_flag;239#if 0240static int r_flag;241#endif242static int version_flag;243static int help_flag;244static char *remote_host;245static char *auth_level = NULL;246247struct getargs args[] = {248{ NULL, 'a', arg_string, &auth_level, "authentication mode" },249#if 0250{ NULL, 'd' },251#endif252{ NULL, 'f', arg_flag, &f_flag, "pre-authenticated" },253{ NULL, 'h', arg_string, &remote_host, "remote host", "hostname" },254{ NULL, 'p', arg_flag, &p_flag, "don't purge environment" },255#if 0256{ NULL, 'r', arg_flag, &r_flag, "rlogin protocol" },257#endif258{ "version", 0, arg_flag, &version_flag },259{ "help", 0, arg_flag,&help_flag, }260};261262int nargs = sizeof(args) / sizeof(args[0]);263264static void265update_utmp(const char *username, const char *hostname,266char *tty, char *ttyn)267{268/*269* Update the utmp files, both BSD and SYSV style.270*/271if (utmpx_login(tty, username, hostname) != 0 && !f_flag) {272printf("No utmpx entry. You must exec \"login\" from the "273"lowest level shell.\n");274exit(1);275}276utmp_login(ttyn, username, hostname);277}278279static void280checknologin(void)281{282FILE *f;283char buf[1024];284285f = fopen(_PATH_NOLOGIN, "r");286if(f == NULL)287return;288while(fgets(buf, sizeof(buf), f))289fputs(buf, stdout);290fclose(f);291exit(0);292}293294/* print contents of a file */295static void296show_file(const char *file)297{298FILE *f;299char buf[BUFSIZ];300if((f = fopen(file, "r")) == NULL)301return;302while (fgets(buf, sizeof(buf), f))303fputs(buf, stdout);304fclose(f);305}306307/*308* Actually log in the user. `pwd' contains all the relevant309* information about the user. `ttyn' is the complete name of the tty310* and `tty' the short name.311*/312313static void314do_login(const struct passwd *pwd, char *tty, char *ttyn)315{316#ifdef HAVE_GETSPNAM317struct spwd *sp;318#endif319int rootlogin = (pwd->pw_uid == 0);320gid_t tty_gid;321struct group *gr;322const char *home_dir;323int i;324325if(!rootlogin)326checknologin();327328#ifdef HAVE_GETSPNAM329sp = getspnam(pwd->pw_name);330#endif331332update_utmp(pwd->pw_name, remote_host ? remote_host : "",333tty, ttyn);334335gr = getgrnam ("tty");336if (gr != NULL)337tty_gid = gr->gr_gid;338else339tty_gid = pwd->pw_gid;340341if (chown (ttyn, pwd->pw_uid, tty_gid) < 0) {342warn("chown %s", ttyn);343if (rootlogin == 0)344exit (1);345}346347if (chmod (ttyn, S_IRUSR | S_IWUSR | S_IWGRP) < 0) {348warn("chmod %s", ttyn);349if (rootlogin == 0)350exit (1);351}352353#ifdef HAVE_SETLOGIN354if(setlogin(pwd->pw_name)){355warn("setlogin(%s)", pwd->pw_name);356if(rootlogin == 0)357exit(1);358}359#endif360if(rootlogin == 0) {361const char *file = login_conf_get_string("limits");362if(file == NULL)363file = _PATH_LIMITS_CONF;364365read_limits_conf(file, pwd);366}367368#ifdef HAVE_SETPCRED369if (setpcred (pwd->pw_name, NULL) == -1)370warn("setpcred(%s)", pwd->pw_name);371#endif /* HAVE_SETPCRED */372#ifdef HAVE_INITGROUPS373if(initgroups(pwd->pw_name, pwd->pw_gid)){374warn("initgroups(%s, %u)", pwd->pw_name, (unsigned)pwd->pw_gid);375if(rootlogin == 0)376exit(1);377}378#endif379if(do_osfc2_magic(pwd->pw_uid))380exit(1);381if(setgid(pwd->pw_gid)){382warn("setgid(%u)", (unsigned)pwd->pw_gid);383if(rootlogin == 0)384exit(1);385}386if(setuid(pwd->pw_uid) || (pwd->pw_uid != 0 && setuid(0) == 0)) {387warn("setuid(%u)", (unsigned)pwd->pw_uid);388if(rootlogin == 0)389exit(1);390}391392/* make sure signals are set to default actions, apparently some393OS:es like to ignore SIGINT, which is not very convenient */394395for (i = 1; i < NSIG; ++i)396signal(i, SIG_DFL);397398/* all kinds of different magic */399400#ifdef HAVE_GETSPNAM401check_shadow(pwd, sp);402#endif403404#if defined(HAVE_GETUDBNAM) && defined(HAVE_SETLIM)405{406struct udb *udb;407long t;408const long maxcpu = 46116860184; /* some random constant */409udb = getudbnam(pwd->pw_name);410if(udb == UDB_NULL)411errx(1, "Failed to get UDB entry.");412t = udb->ue_pcpulim[UDBRC_INTER];413if(t == 0 || t > maxcpu)414t = CPUUNLIM;415else416t *= 100 * CLOCKS_PER_SEC;417418if(limit(C_PROC, 0, L_CPU, t) < 0)419warn("limit C_PROC");420421t = udb->ue_jcpulim[UDBRC_INTER];422if(t == 0 || t > maxcpu)423t = CPUUNLIM;424else425t *= 100 * CLOCKS_PER_SEC;426427if(limit(C_JOBPROCS, 0, L_CPU, t) < 0)428warn("limit C_JOBPROCS");429430nice(udb->ue_nice[UDBRC_INTER]);431}432#endif433#if defined(HAVE_SGI_GETCAPABILITYBYNAME) && defined(HAVE_CAP_SET_PROC)434/* XXX SGI capability hack IRIX 6.x (x >= 0?) has something435called capabilities, that allow you to give away436permissions (such as chown) to specific processes. From 6.5437this is default on, and the default capability set seems to438not always be the empty set. The problem is that the439runtime linker refuses to do just about anything if the440process has *any* capabilities set, so we have to remove441them here (unless otherwise instructed by /etc/capability).442In IRIX < 6.5, these functions was called sgi_cap_setproc,443etc, but we ignore this fact (it works anyway). */444{445struct user_cap *ucap = sgi_getcapabilitybyname(pwd->pw_name);446cap_t cap;447if(ucap == NULL)448cap = cap_from_text("all=");449else450cap = cap_from_text(ucap->ca_default);451if(cap == NULL)452err(1, "cap_from_text");453if(cap_set_proc(cap) < 0)454err(1, "cap_set_proc");455cap_free(cap);456free(ucap);457}458#endif459home_dir = pwd->pw_dir;460if (chdir(home_dir) < 0) {461fprintf(stderr, "No home directory \"%s\"!\n", pwd->pw_dir);462if (chdir("/"))463exit(0);464home_dir = "/";465fprintf(stderr, "Logging in with home = \"/\".\n");466}467#ifdef KRB5468if (auth == AUTH_KRB5) {469krb5_start_session (pwd);470}471472krb5_get_afs_tokens (pwd);473474krb5_finish ();475#endif /* KRB5 */476477add_env("PATH", _PATH_DEFPATH);478479{480const char *str = login_conf_get_string("environment");481char buf[MAXPATHLEN];482483if(str == NULL) {484login_read_env(_PATH_ETC_ENVIRONMENT);485} else {486while(strsep_copy(&str, ",", buf, sizeof(buf)) != -1) {487if(buf[0] == '\0')488continue;489login_read_env(buf);490}491}492}493{494const char *str = login_conf_get_string("motd");495char buf[MAXPATHLEN];496497if(str != NULL) {498while(strsep_copy(&str, ",", buf, sizeof(buf)) != -1) {499if(buf[0] == '\0')500continue;501show_file(buf);502}503} else {504str = login_conf_get_string("welcome");505if(str != NULL)506show_file(str);507}508}509add_env("HOME", home_dir);510add_env("USER", pwd->pw_name);511add_env("LOGNAME", pwd->pw_name);512add_env("SHELL", pwd->pw_shell);513exec_shell(pwd->pw_shell, rootlogin);514}515516static int517check_password(struct passwd *pwd, const char *password)518{519if(pwd->pw_passwd == NULL)520return 1;521if(pwd->pw_passwd[0] == '\0'){522#ifdef ALLOW_NULL_PASSWORD523return password[0] != '\0';524#else525return 1;526#endif527}528if(strcmp(pwd->pw_passwd, crypt(password, pwd->pw_passwd)) == 0)529return 0;530#ifdef KRB5531if(krb5_verify(pwd, password) == 0) {532auth = AUTH_KRB5;533return 0;534}535#endif536#ifdef OTP537if (otp_verify (pwd, password) == 0) {538auth = AUTH_OTP;539return 0;540}541#endif542return 1;543}544545static void546usage(int status)547{548arg_printusage(args, nargs, NULL, "[username]");549exit(status);550}551552static RETSIGTYPE553sig_handler(int sig)554{555if (sig == SIGALRM)556fprintf(stderr, "Login timed out after %d seconds\n",557login_timeout);558else559fprintf(stderr, "Login received signal, exiting\n");560exit(0);561}562563int564main(int argc, char **argv)565{566int max_tries = 5;567int try;568569char username[32];570int optidx = 0;571572int ask = 1;573struct sigaction sa;574575setprogname(argv[0]);576577#ifdef KRB5578{579krb5_error_code ret;580581ret = krb5_init_context(&context);582if (ret)583errx (1, "krb5_init_context failed: %d", ret);584}585#endif586587openlog("login", LOG_ODELAY | LOG_PID, LOG_AUTH);588589if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv,590&optidx))591usage (1);592argc -= optidx;593argv += optidx;594595if(help_flag)596usage(0);597if (version_flag) {598print_version (NULL);599return 0;600}601602if (geteuid() != 0)603errx(1, "only root may use login, use su");604605/* Default tty settings. */606stty_default();607608if(p_flag)609copy_env();610else {611/* this set of variables is always preserved by BSD login */612if(getenv("TERM"))613add_env("TERM", getenv("TERM"));614if(getenv("TZ"))615add_env("TZ", getenv("TZ"));616}617618if(*argv){619if(strchr(*argv, '=') == NULL && strcmp(*argv, "-") != 0){620strlcpy (username, *argv, sizeof(username));621ask = 0;622}623}624625#if defined(DCE) && defined(AIX)626esetenv("AUTHSTATE", "DCE", 1);627#endif628629/* XXX should we care about environment on the command line? */630631memset(&sa, 0, sizeof(sa));632sa.sa_handler = sig_handler;633sigemptyset(&sa.sa_mask);634sa.sa_flags = 0;635sigaction(SIGALRM, &sa, NULL);636alarm(login_timeout);637638for(try = 0; try < max_tries; try++){639struct passwd *pwd;640char password[128];641int ret;642char ttname[32];643char *tty, *ttyn;644char prompt[128];645#ifdef OTP646char otp_str[256];647#endif648649if(ask){650f_flag = 0;651#if 0652r_flag = 0;653#endif654ret = read_string("login: ", username, sizeof(username), 1);655if(ret == -3)656exit(0);657if(ret == -2)658sig_handler(0); /* exit */659}660pwd = k_getpwnam(username);661#ifdef ALLOW_NULL_PASSWORD662if (pwd != NULL && (pwd->pw_passwd[0] == '\0')) {663strcpy(password,"");664}665else666#endif667668{669#ifdef OTP670if(auth_level && strcmp(auth_level, "otp") == 0 &&671otp_challenge(&otp_ctx, username,672otp_str, sizeof(otp_str)) == 0)673snprintf (prompt, sizeof(prompt), "%s's %s Password: ",674username, otp_str);675else676#endif677strncpy(prompt, "Password: ", sizeof(prompt));678679if (f_flag == 0) {680ret = read_string(prompt, password, sizeof(password), 0);681if (ret == -3) {682ask = 1;683continue;684}685if (ret == -2)686sig_handler(0);687}688}689690if(pwd == NULL){691fprintf(stderr, "Login incorrect.\n");692ask = 1;693continue;694}695696if(f_flag == 0 && check_password(pwd, password)){697fprintf(stderr, "Login incorrect.\n");698ask = 1;699continue;700}701ttyn = ttyname(STDIN_FILENO);702if(ttyn == NULL){703snprintf(ttname, sizeof(ttname), "%s??", _PATH_TTY);704ttyn = ttname;705}706if (strncmp (ttyn, _PATH_DEV, strlen(_PATH_DEV)) == 0)707tty = ttyn + strlen(_PATH_DEV);708else709tty = ttyn;710711if (login_access (pwd, remote_host ? remote_host : tty) == 0) {712fprintf(stderr, "Permission denied\n");713if (remote_host)714syslog(LOG_NOTICE, "%s LOGIN REFUSED FROM %s",715pwd->pw_name, remote_host);716else717syslog(LOG_NOTICE, "%s LOGIN REFUSED ON %s",718pwd->pw_name, tty);719exit (1);720} else {721if (remote_host)722syslog(LOG_NOTICE, "%s LOGIN ACCEPTED FROM %s ppid=%d",723pwd->pw_name, remote_host, (int) getppid());724else725syslog(LOG_NOTICE, "%s LOGIN ACCEPTED ON %s ppid=%d",726pwd->pw_name, tty, (int) getppid());727}728alarm(0);729do_login(pwd, tty, ttyn);730}731exit(1);732}733734735