1/*
2 * Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the Institute nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#include "login_locl.h"
35#ifdef HAVE_CAPABILITY_H
36#include <capability.h>
37#endif
38#ifdef HAVE_SYS_CAPABILITY_H
39#include <sys/capability.h>
40#endif
41#ifdef HAVE_CRYPT_H
42#include <crypt.h>
43#endif
44
45RCSID("$Id$");
46
47static int login_timeout = 60;
48
49static int
50start_login_process(void)
51{
52    char *prog, *argv0;
53    prog = login_conf_get_string("login_program");
54    if(prog == NULL)
55	return 0;
56    argv0 = strrchr(prog, '/');
57
58    if(argv0)
59	argv0++;
60    else
61	argv0 = prog;
62
63    return simple_execle(prog, argv0, NULL, env);
64}
65
66static int
67start_logout_process(void)
68{
69    char *prog, *argv0;
70    pid_t pid;
71
72    prog = login_conf_get_string("logout_program");
73    if(prog == NULL)
74	return 0;
75    argv0 = strrchr(prog, '/');
76
77    if(argv0)
78	argv0++;
79    else
80	argv0 = prog;
81
82    pid = fork();
83    if(pid == 0) {
84	/* avoid getting signals sent to the shell */
85	setpgid(0, getpid());
86	return 0;
87    }
88    if(pid == -1)
89	err(1, "fork");
90    /* wait for the real login process to exit */
91#ifdef HAVE_SETPROCTITLE
92    setproctitle("waitpid %d", pid);
93#endif
94    while(1) {
95	int status;
96	int ret;
97	ret = waitpid(pid, &status, 0);
98	if(ret > 0) {
99	    if(WIFEXITED(status) || WIFSIGNALED(status)) {
100		execle(prog, argv0, NULL, env);
101		err(1, "exec %s", prog);
102	    }
103	} else if(ret < 0)
104	    err(1, "waitpid");
105    }
106}
107
108static void
109exec_shell(const char *shell, int fallback)
110{
111    char *sh;
112    const char *p;
113
114    extend_env(NULL);
115    if(start_login_process() < 0)
116	warn("login process");
117    start_logout_process();
118
119    p = strrchr(shell, '/');
120    if(p)
121	p++;
122    else
123	p = shell;
124    if (asprintf(&sh, "-%s", p) == -1)
125	errx(1, "Out of memory");
126    execle(shell, sh, NULL, env);
127    if(fallback){
128	warnx("Can't exec %s, trying %s",
129	      shell, _PATH_BSHELL);
130	execle(_PATH_BSHELL, "-sh", NULL, env);
131	err(1, "%s", _PATH_BSHELL);
132    }
133    err(1, "%s", shell);
134}
135
136static enum { NONE = 0, AUTH_KRB5 = 2, AUTH_OTP = 3 } auth;
137
138#ifdef OTP
139static OtpContext otp_ctx;
140
141static int
142otp_verify(struct passwd *pwd, const char *password)
143{
144   return (otp_verify_user (&otp_ctx, password));
145}
146#endif /* OTP */
147
148
149static int pag_set = 0;
150
151#ifdef KRB5
152static krb5_context context;
153static krb5_ccache  id, id2;
154
155static int
156krb5_verify(struct passwd *pwd, const char *password)
157{
158    krb5_error_code ret;
159    krb5_principal princ;
160
161    ret = krb5_parse_name(context, pwd->pw_name, &princ);
162    if(ret)
163	return 1;
164    ret = krb5_cc_new_unique(context, krb5_cc_type_memory, NULL, &id);
165    if(ret) {
166	krb5_free_principal(context, princ);
167	return 1;
168    }
169    ret = krb5_verify_user_lrealm(context,
170				  princ,
171				  id,
172				  password,
173				  1,
174				  NULL);
175    krb5_free_principal(context, princ);
176    return ret;
177}
178
179static int
180krb5_start_session (const struct passwd *pwd)
181{
182    krb5_error_code ret;
183    char residual[64];
184
185    /* copy credentials to file cache */
186    snprintf(residual, sizeof(residual), "FILE:/tmp/krb5cc_%u",
187	     (unsigned)pwd->pw_uid);
188    krb5_cc_resolve(context, residual, &id2);
189    ret = krb5_cc_copy_cache(context, id, id2);
190    if (ret == 0)
191	add_env("KRB5CCNAME", residual);
192    else {
193	krb5_cc_destroy (context, id2);
194	return ret;
195    }
196    krb5_cc_close(context, id2);
197    krb5_cc_destroy(context, id);
198    return 0;
199}
200
201static void
202krb5_finish (void)
203{
204    krb5_free_context(context);
205}
206
207static void
208krb5_get_afs_tokens (const struct passwd *pwd)
209{
210    char cell[64];
211    char *pw_dir;
212    krb5_error_code ret;
213
214    if (!k_hasafs ())
215	return;
216
217    ret = krb5_cc_default(context, &id2);
218
219    if (ret == 0) {
220	pw_dir = pwd->pw_dir;
221
222	if (!pag_set) {
223	    k_setpag();
224	    pag_set = 1;
225	}
226
227	if(k_afs_cell_of_file(pw_dir, cell, sizeof(cell)) == 0)
228	    krb5_afslog_uid_home (context, id2,
229				  cell, NULL, pwd->pw_uid, pwd->pw_dir);
230	krb5_afslog_uid_home (context, id2, NULL, NULL,
231			      pwd->pw_uid, pwd->pw_dir);
232	krb5_cc_close (context, id2);
233    }
234}
235
236#endif /* KRB5 */
237
238static int f_flag;
239static int p_flag;
240#if 0
241static int r_flag;
242#endif
243static int version_flag;
244static int help_flag;
245static char *remote_host;
246static char *auth_level = NULL;
247
248struct getargs args[] = {
249    { NULL, 'a', arg_string,    &auth_level,    "authentication mode" },
250#if 0
251    { NULL, 'd' },
252#endif
253    { NULL, 'f', arg_flag,	&f_flag,	"pre-authenticated" },
254    { NULL, 'h', arg_string,	&remote_host,	"remote host", "hostname" },
255    { NULL, 'p', arg_flag,	&p_flag,	"don't purge environment" },
256#if 0
257    { NULL, 'r', arg_flag,	&r_flag,	"rlogin protocol" },
258#endif
259    { "version", 0,  arg_flag,	&version_flag },
260    { "help",	 0,  arg_flag,&help_flag, }
261};
262
263int nargs = sizeof(args) / sizeof(args[0]);
264
265static void
266update_utmp(const char *username, const char *hostname,
267	    char *tty, char *ttyn)
268{
269    /*
270     * Update the utmp files, both BSD and SYSV style.
271     */
272    if (utmpx_login(tty, username, hostname) != 0 && !f_flag) {
273	printf("No utmpx entry.  You must exec \"login\" from the "
274	       "lowest level shell.\n");
275	exit(1);
276    }
277    utmp_login(ttyn, username, hostname);
278}
279
280static void
281checknologin(void)
282{
283    FILE *f;
284    char buf[1024];
285
286    f = fopen(_PATH_NOLOGIN, "r");
287    if(f == NULL)
288	return;
289    while(fgets(buf, sizeof(buf), f))
290	fputs(buf, stdout);
291    fclose(f);
292    exit(0);
293}
294
295/* print contents of a file */
296static void
297show_file(const char *file)
298{
299    FILE *f;
300    char buf[BUFSIZ];
301    if((f = fopen(file, "r")) == NULL)
302	return;
303    while (fgets(buf, sizeof(buf), f))
304	fputs(buf, stdout);
305    fclose(f);
306}
307
308/*
309 * Actually log in the user.  `pwd' contains all the relevant
310 * information about the user.  `ttyn' is the complete name of the tty
311 * and `tty' the short name.
312 */
313
314static void
315do_login(const struct passwd *pwd, char *tty, char *ttyn)
316{
317#ifdef HAVE_GETSPNAM
318    struct spwd *sp;
319#endif
320    int rootlogin = (pwd->pw_uid == 0);
321    gid_t tty_gid;
322    struct group *gr;
323    const char *home_dir;
324    int i;
325
326    if(!rootlogin)
327	checknologin();
328
329#ifdef HAVE_GETSPNAM
330    sp = getspnam(pwd->pw_name);
331#endif
332
333    update_utmp(pwd->pw_name, remote_host ? remote_host : "",
334		tty, ttyn);
335
336    gr = getgrnam ("tty");
337    if (gr != NULL)
338	tty_gid = gr->gr_gid;
339    else
340	tty_gid = pwd->pw_gid;
341
342    if (chown (ttyn, pwd->pw_uid, tty_gid) < 0) {
343	warn("chown %s", ttyn);
344	if (rootlogin == 0)
345	    exit (1);
346    }
347
348    if (chmod (ttyn, S_IRUSR | S_IWUSR | S_IWGRP) < 0) {
349	warn("chmod %s", ttyn);
350	if (rootlogin == 0)
351	    exit (1);
352    }
353
354#ifdef HAVE_SETLOGIN
355    if(setlogin(pwd->pw_name)){
356	warn("setlogin(%s)", pwd->pw_name);
357	if(rootlogin == 0)
358	    exit(1);
359    }
360#endif
361    if(rootlogin == 0) {
362	const char *file = login_conf_get_string("limits");
363	if(file == NULL)
364	    file = _PATH_LIMITS_CONF;
365
366	read_limits_conf(file, pwd);
367    }
368
369#ifdef HAVE_SETPCRED
370    if (setpcred (pwd->pw_name, NULL) == -1)
371	warn("setpcred(%s)", pwd->pw_name);
372#endif /* HAVE_SETPCRED */
373#ifdef HAVE_INITGROUPS
374    if(initgroups(pwd->pw_name, pwd->pw_gid)){
375	warn("initgroups(%s, %u)", pwd->pw_name, (unsigned)pwd->pw_gid);
376	if(rootlogin == 0)
377	    exit(1);
378    }
379#endif
380    if(do_osfc2_magic(pwd->pw_uid))
381	exit(1);
382    if(setgid(pwd->pw_gid)){
383	warn("setgid(%u)", (unsigned)pwd->pw_gid);
384	if(rootlogin == 0)
385	    exit(1);
386    }
387    if(setuid(pwd->pw_uid) || (pwd->pw_uid != 0 && setuid(0) == 0)) {
388	warn("setuid(%u)", (unsigned)pwd->pw_uid);
389	if(rootlogin == 0)
390	    exit(1);
391    }
392
393    /* make sure signals are set to default actions, apparently some
394       OS:es like to ignore SIGINT, which is not very convenient */
395
396    for (i = 1; i < NSIG; ++i)
397	signal(i, SIG_DFL);
398
399    /* all kinds of different magic */
400
401#ifdef HAVE_GETSPNAM
402    check_shadow(pwd, sp);
403#endif
404
405#if defined(HAVE_GETUDBNAM) && defined(HAVE_SETLIM)
406    {
407	struct udb *udb;
408	long t;
409	const long maxcpu = 46116860184; /* some random constant */
410	udb = getudbnam(pwd->pw_name);
411	if(udb == UDB_NULL)
412	    errx(1, "Failed to get UDB entry.");
413	t = udb->ue_pcpulim[UDBRC_INTER];
414	if(t == 0 || t > maxcpu)
415	    t = CPUUNLIM;
416	else
417	    t *= 100 * CLOCKS_PER_SEC;
418
419	if(limit(C_PROC, 0, L_CPU, t) < 0)
420	    warn("limit C_PROC");
421
422	t = udb->ue_jcpulim[UDBRC_INTER];
423	if(t == 0 || t > maxcpu)
424	    t = CPUUNLIM;
425	else
426	    t *= 100 * CLOCKS_PER_SEC;
427
428	if(limit(C_JOBPROCS, 0, L_CPU, t) < 0)
429	    warn("limit C_JOBPROCS");
430
431	nice(udb->ue_nice[UDBRC_INTER]);
432    }
433#endif
434#if defined(HAVE_SGI_GETCAPABILITYBYNAME) && defined(HAVE_CAP_SET_PROC)
435	/* XXX SGI capability hack IRIX 6.x (x >= 0?) has something
436	   called capabilities, that allow you to give away
437	   permissions (such as chown) to specific processes. From 6.5
438	   this is default on, and the default capability set seems to
439	   not always be the empty set. The problem is that the
440	   runtime linker refuses to do just about anything if the
441	   process has *any* capabilities set, so we have to remove
442	   them here (unless otherwise instructed by /etc/capability).
443	   In IRIX < 6.5, these functions was called sgi_cap_setproc,
444	   etc, but we ignore this fact (it works anyway). */
445	{
446	    struct user_cap *ucap = sgi_getcapabilitybyname(pwd->pw_name);
447	    cap_t cap;
448	    if(ucap == NULL)
449		cap = cap_from_text("all=");
450	    else
451		cap = cap_from_text(ucap->ca_default);
452	    if(cap == NULL)
453		err(1, "cap_from_text");
454	    if(cap_set_proc(cap) < 0)
455		err(1, "cap_set_proc");
456	    cap_free(cap);
457	    free(ucap);
458	}
459#endif
460    home_dir = pwd->pw_dir;
461    if (chdir(home_dir) < 0) {
462	fprintf(stderr, "No home directory \"%s\"!\n", pwd->pw_dir);
463	if (chdir("/"))
464	    exit(0);
465	home_dir = "/";
466	fprintf(stderr, "Logging in with home = \"/\".\n");
467    }
468#ifdef KRB5
469    if (auth == AUTH_KRB5) {
470	krb5_start_session (pwd);
471    }
472
473    krb5_get_afs_tokens (pwd);
474
475    krb5_finish ();
476#endif /* KRB5 */
477
478    add_env("PATH", _PATH_DEFPATH);
479
480    {
481	const char *str = login_conf_get_string("environment");
482	char buf[MAXPATHLEN];
483
484	if(str == NULL) {
485	    login_read_env(_PATH_ETC_ENVIRONMENT);
486	} else {
487	    while(strsep_copy(&str, ",", buf, sizeof(buf)) != -1) {
488		if(buf[0] == '\0')
489		    continue;
490		login_read_env(buf);
491	    }
492	}
493    }
494    {
495	const char *str = login_conf_get_string("motd");
496	char buf[MAXPATHLEN];
497
498	if(str != NULL) {
499	    while(strsep_copy(&str, ",", buf, sizeof(buf)) != -1) {
500		if(buf[0] == '\0')
501		    continue;
502		show_file(buf);
503	    }
504	} else {
505	    str = login_conf_get_string("welcome");
506	    if(str != NULL)
507		show_file(str);
508	}
509    }
510    add_env("HOME", home_dir);
511    add_env("USER", pwd->pw_name);
512    add_env("LOGNAME", pwd->pw_name);
513    add_env("SHELL", pwd->pw_shell);
514    exec_shell(pwd->pw_shell, rootlogin);
515}
516
517static int
518check_password(struct passwd *pwd, const char *password)
519{
520    if(pwd->pw_passwd == NULL)
521	return 1;
522    if(pwd->pw_passwd[0] == '\0'){
523#ifdef ALLOW_NULL_PASSWORD
524	return password[0] != '\0';
525#else
526	return 1;
527#endif
528    }
529    if(strcmp(pwd->pw_passwd, crypt(password, pwd->pw_passwd)) == 0)
530	return 0;
531#ifdef KRB5
532    if(krb5_verify(pwd, password) == 0) {
533	auth = AUTH_KRB5;
534	return 0;
535    }
536#endif
537#ifdef OTP
538    if (otp_verify (pwd, password) == 0) {
539       auth = AUTH_OTP;
540       return 0;
541    }
542#endif
543    return 1;
544}
545
546static void
547usage(int status)
548{
549    arg_printusage(args, nargs, NULL, "[username]");
550    exit(status);
551}
552
553static RETSIGTYPE
554sig_handler(int sig)
555{
556    if (sig == SIGALRM)
557         fprintf(stderr, "Login timed out after %d seconds\n",
558                login_timeout);
559      else
560         fprintf(stderr, "Login received signal, exiting\n");
561    exit(0);
562}
563
564int
565main(int argc, char **argv)
566{
567    int max_tries = 5;
568    int try;
569
570    char username[32];
571    int optidx = 0;
572
573    int ask = 1;
574    struct sigaction sa;
575
576    setprogname(argv[0]);
577
578#ifdef KRB5
579    {
580	krb5_error_code ret;
581
582	ret = krb5_init_context(&context);
583	if (ret)
584	    errx (1, "krb5_init_context failed: %d", ret);
585    }
586#endif
587
588    openlog("login", LOG_ODELAY | LOG_PID, LOG_AUTH);
589
590    if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv,
591		&optidx))
592	usage (1);
593    argc -= optidx;
594    argv += optidx;
595
596    if(help_flag)
597	usage(0);
598    if (version_flag) {
599	print_version (NULL);
600	return 0;
601    }
602
603    if (geteuid() != 0)
604	errx(1, "only root may use login, use su");
605
606    /* Default tty settings. */
607    stty_default();
608
609    if(p_flag)
610	copy_env();
611    else {
612	/* this set of variables is always preserved by BSD login */
613	if(getenv("TERM"))
614	    add_env("TERM", getenv("TERM"));
615	if(getenv("TZ"))
616	    add_env("TZ", getenv("TZ"));
617    }
618
619    if(*argv){
620	if(strchr(*argv, '=') == NULL && strcmp(*argv, "-") != 0){
621	    strlcpy (username, *argv, sizeof(username));
622	    ask = 0;
623	}
624    }
625
626#if defined(DCE) && defined(AIX)
627    esetenv("AUTHSTATE", "DCE", 1);
628#endif
629
630    /* XXX should we care about environment on the command line? */
631
632    memset(&sa, 0, sizeof(sa));
633    sa.sa_handler = sig_handler;
634    sigemptyset(&sa.sa_mask);
635    sa.sa_flags = 0;
636    sigaction(SIGALRM, &sa, NULL);
637    alarm(login_timeout);
638
639    for(try = 0; try < max_tries; try++){
640	struct passwd *pwd;
641	char password[128];
642	int ret;
643	char ttname[32];
644	char *tty, *ttyn;
645        char prompt[128];
646#ifdef OTP
647        char otp_str[256];
648#endif
649
650	if(ask){
651	    f_flag = 0;
652#if 0
653	    r_flag = 0;
654#endif
655	    ret = read_string("login: ", username, sizeof(username), 1);
656	    if(ret == -3)
657		exit(0);
658	    if(ret == -2)
659		sig_handler(0); /* exit */
660	}
661        pwd = k_getpwnam(username);
662#ifdef ALLOW_NULL_PASSWORD
663        if (pwd != NULL && (pwd->pw_passwd[0] == '\0')) {
664            strcpy(password,"");
665        }
666        else
667#endif
668
669        {
670#ifdef OTP
671           if(auth_level && strcmp(auth_level, "otp") == 0 &&
672                 otp_challenge(&otp_ctx, username,
673                            otp_str, sizeof(otp_str)) == 0)
674                 snprintf (prompt, sizeof(prompt), "%s's %s Password: ",
675                            username, otp_str);
676            else
677#endif
678                 strncpy(prompt, "Password: ", sizeof(prompt));
679
680	    if (f_flag == 0) {
681	       ret = read_string(prompt, password, sizeof(password), 0);
682               if (ret == -3) {
683                  ask = 1;
684                  continue;
685               }
686               if (ret == -2)
687                  sig_handler(0);
688            }
689         }
690
691	if(pwd == NULL){
692	    fprintf(stderr, "Login incorrect.\n");
693	    ask = 1;
694	    continue;
695	}
696
697	if(f_flag == 0 && check_password(pwd, password)){
698	    fprintf(stderr, "Login incorrect.\n");
699            ask = 1;
700	    continue;
701	}
702	ttyn = ttyname(STDIN_FILENO);
703	if(ttyn == NULL){
704	    snprintf(ttname, sizeof(ttname), "%s??", _PATH_TTY);
705	    ttyn = ttname;
706	}
707	if (strncmp (ttyn, _PATH_DEV, strlen(_PATH_DEV)) == 0)
708	    tty = ttyn + strlen(_PATH_DEV);
709	else
710	    tty = ttyn;
711
712	if (login_access (pwd, remote_host ? remote_host : tty) == 0) {
713	    fprintf(stderr, "Permission denied\n");
714	    if (remote_host)
715		syslog(LOG_NOTICE, "%s LOGIN REFUSED FROM %s",
716		       pwd->pw_name, remote_host);
717	    else
718		syslog(LOG_NOTICE, "%s LOGIN REFUSED ON %s",
719		       pwd->pw_name, tty);
720	    exit (1);
721	} else {
722	    if (remote_host)
723		syslog(LOG_NOTICE, "%s LOGIN ACCEPTED FROM %s ppid=%d",
724		       pwd->pw_name, remote_host, (int) getppid());
725	    else
726		syslog(LOG_NOTICE, "%s LOGIN ACCEPTED ON %s ppid=%d",
727		       pwd->pw_name, tty, (int) getppid());
728	}
729        alarm(0);
730	do_login(pwd, tty, ttyn);
731    }
732    exit(1);
733}
734