xref: /freebsd/crypto/heimdal/appl/login/login.c (revision c19800e8)
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 
45 RCSID("$Id$");
46 
47 static int login_timeout = 60;
48 
49 static int
start_login_process(void)50 start_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 
66 static int
start_logout_process(void)67 start_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 
108 static void
exec_shell(const char * shell,int fallback)109 exec_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 
136 static enum { NONE = 0, AUTH_KRB5 = 2, AUTH_OTP = 3 } auth;
137 
138 #ifdef OTP
139 static OtpContext otp_ctx;
140 
141 static int
otp_verify(struct passwd * pwd,const char * password)142 otp_verify(struct passwd *pwd, const char *password)
143 {
144    return (otp_verify_user (&otp_ctx, password));
145 }
146 #endif /* OTP */
147 
148 
149 static int pag_set = 0;
150 
151 #ifdef KRB5
152 static krb5_context context;
153 static krb5_ccache  id, id2;
154 
155 static int
krb5_verify(struct passwd * pwd,const char * password)156 krb5_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 
179 static int
krb5_start_session(const struct passwd * pwd)180 krb5_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 
201 static void
krb5_finish(void)202 krb5_finish (void)
203 {
204     krb5_free_context(context);
205 }
206 
207 static void
krb5_get_afs_tokens(const struct passwd * pwd)208 krb5_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 
238 static int f_flag;
239 static int p_flag;
240 #if 0
241 static int r_flag;
242 #endif
243 static int version_flag;
244 static int help_flag;
245 static char *remote_host;
246 static char *auth_level = NULL;
247 
248 struct 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 
263 int nargs = sizeof(args) / sizeof(args[0]);
264 
265 static void
update_utmp(const char * username,const char * hostname,char * tty,char * ttyn)266 update_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 
280 static void
checknologin(void)281 checknologin(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 */
296 static void
show_file(const char * file)297 show_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 
314 static void
do_login(const struct passwd * pwd,char * tty,char * ttyn)315 do_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 
517 static int
check_password(struct passwd * pwd,const char * password)518 check_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 
546 static void
usage(int status)547 usage(int status)
548 {
549     arg_printusage(args, nargs, NULL, "[username]");
550     exit(status);
551 }
552 
553 static RETSIGTYPE
sig_handler(int sig)554 sig_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 
564 int
main(int argc,char ** argv)565 main(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