2 * Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
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.
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.
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
34 #include "login_locl.h"
35 #ifdef HAVE_CAPABILITY_H
36 #include <capability.h>
38 #ifdef HAVE_SYS_CAPABILITY_H
39 #include <sys/capability.h>
47 static int login_timeout
= 60;
50 start_login_process(void)
53 prog
= login_conf_get_string("login_program");
56 argv0
= strrchr(prog
, '/');
63 return simple_execle(prog
, argv0
, NULL
, env
);
67 start_logout_process(void)
72 prog
= login_conf_get_string("logout_program");
75 argv0
= strrchr(prog
, '/');
84 /* avoid getting signals sent to the shell */
90 /* wait for the real login process to exit */
91 #ifdef HAVE_SETPROCTITLE
92 setproctitle("waitpid %d", pid
);
97 ret
= waitpid(pid
, &status
, 0);
99 if(WIFEXITED(status
) || WIFSIGNALED(status
)) {
100 execle(prog
, argv0
, NULL
, env
);
101 err(1, "exec %s", prog
);
109 exec_shell(const char *shell
, int fallback
)
115 if(start_login_process() < 0)
116 warn("login process");
117 start_logout_process();
119 p
= strrchr(shell
, '/');
124 if (asprintf(&sh
, "-%s", p
) == -1)
125 errx(1, "Out of memory");
126 execle(shell
, sh
, NULL
, env
);
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
);
136 static enum { NONE
= 0, AUTH_KRB5
= 2, AUTH_OTP
= 3 } auth
;
139 static OtpContext otp_ctx
;
142 otp_verify(struct passwd
*pwd
, const char *password
)
144 return (otp_verify_user (&otp_ctx
, password
));
149 static int pag_set
= 0;
152 static krb5_context context
;
153 static krb5_ccache id
, id2
;
156 krb5_verify(struct passwd
*pwd
, const char *password
)
159 krb5_principal princ
;
161 ret
= krb5_parse_name(context
, pwd
->pw_name
, &princ
);
164 ret
= krb5_cc_new_unique(context
, krb5_cc_type_memory
, NULL
, &id
);
166 krb5_free_principal(context
, princ
);
169 ret
= krb5_verify_user_lrealm(context
,
175 krb5_free_principal(context
, princ
);
180 krb5_start_session (const struct passwd
*pwd
)
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
);
191 add_env("KRB5CCNAME", residual
);
193 krb5_cc_destroy (context
, id2
);
196 krb5_cc_close(context
, id2
);
197 krb5_cc_destroy(context
, id
);
204 krb5_free_context(context
);
208 krb5_get_afs_tokens (const struct passwd
*pwd
)
217 ret
= krb5_cc_default(context
, &id2
);
220 pw_dir
= pwd
->pw_dir
;
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
);
243 static int version_flag
;
244 static int help_flag
;
245 static char *remote_host
;
246 static char *auth_level
= NULL
;
248 struct getargs args
[] = {
249 { NULL
, 'a', arg_string
, &auth_level
, "authentication mode" },
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" },
257 { NULL
, 'r', arg_flag
, &r_flag
, "rlogin protocol" },
259 { "version", 0, arg_flag
, &version_flag
},
260 { "help", 0, arg_flag
,&help_flag
, }
263 int nargs
= sizeof(args
) / sizeof(args
[0]);
266 update_utmp(const char *username
, const char *hostname
,
267 char *tty
, char *ttyn
)
270 * Update the utmp files, both BSD and SYSV style.
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");
277 utmp_login(ttyn
, username
, hostname
);
286 f
= fopen(_PATH_NOLOGIN
, "r");
289 while(fgets(buf
, sizeof(buf
), f
))
295 /* print contents of a file */
297 show_file(const char *file
)
301 if((f
= fopen(file
, "r")) == NULL
)
303 while (fgets(buf
, sizeof(buf
), f
))
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.
315 do_login(const struct passwd
*pwd
, char *tty
, char *ttyn
)
320 int rootlogin
= (pwd
->pw_uid
== 0);
323 const char *home_dir
;
330 sp
= getspnam(pwd
->pw_name
);
333 update_utmp(pwd
->pw_name
, remote_host
? remote_host
: "",
336 gr
= getgrnam ("tty");
338 tty_gid
= gr
->gr_gid
;
340 tty_gid
= pwd
->pw_gid
;
342 if (chown (ttyn
, pwd
->pw_uid
, tty_gid
) < 0) {
343 warn("chown %s", ttyn
);
348 if (chmod (ttyn
, S_IRUSR
| S_IWUSR
| S_IWGRP
) < 0) {
349 warn("chmod %s", ttyn
);
355 if(setlogin(pwd
->pw_name
)){
356 warn("setlogin(%s)", pwd
->pw_name
);
362 const char *file
= login_conf_get_string("limits");
364 file
= _PATH_LIMITS_CONF
;
366 read_limits_conf(file
, pwd
);
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
);
380 if(do_osfc2_magic(pwd
->pw_uid
))
382 if(setgid(pwd
->pw_gid
)){
383 warn("setgid(%u)", (unsigned)pwd
->pw_gid
);
387 if(setuid(pwd
->pw_uid
) || (pwd
->pw_uid
!= 0 && setuid(0) == 0)) {
388 warn("setuid(%u)", (unsigned)pwd
->pw_uid
);
393 /* make sure signals are set to default actions, apparently some
394 OS:es like to ignore SIGINT, which is not very convenient */
396 for (i
= 1; i
< NSIG
; ++i
)
399 /* all kinds of different magic */
402 check_shadow(pwd
, sp
);
405 #if defined(HAVE_GETUDBNAM) && defined(HAVE_SETLIM)
409 const long maxcpu
= 46116860184; /* some random constant */
410 udb
= getudbnam(pwd
->pw_name
);
412 errx(1, "Failed to get UDB entry.");
413 t
= udb
->ue_pcpulim
[UDBRC_INTER
];
414 if(t
== 0 || t
> maxcpu
)
417 t
*= 100 * CLOCKS_PER_SEC
;
419 if(limit(C_PROC
, 0, L_CPU
, t
) < 0)
420 warn("limit C_PROC");
422 t
= udb
->ue_jcpulim
[UDBRC_INTER
];
423 if(t
== 0 || t
> maxcpu
)
426 t
*= 100 * CLOCKS_PER_SEC
;
428 if(limit(C_JOBPROCS
, 0, L_CPU
, t
) < 0)
429 warn("limit C_JOBPROCS");
431 nice(udb
->ue_nice
[UDBRC_INTER
]);
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). */
446 struct user_cap
*ucap
= sgi_getcapabilitybyname(pwd
->pw_name
);
449 cap
= cap_from_text("all=");
451 cap
= cap_from_text(ucap
->ca_default
);
453 err(1, "cap_from_text");
454 if(cap_set_proc(cap
) < 0)
455 err(1, "cap_set_proc");
460 home_dir
= pwd
->pw_dir
;
461 if (chdir(home_dir
) < 0) {
462 fprintf(stderr
, "No home directory \"%s\"!\n", pwd
->pw_dir
);
466 fprintf(stderr
, "Logging in with home = \"/\".\n");
469 if (auth
== AUTH_KRB5
) {
470 krb5_start_session (pwd
);
473 krb5_get_afs_tokens (pwd
);
478 add_env("PATH", _PATH_DEFPATH
);
481 const char *str
= login_conf_get_string("environment");
482 char buf
[MAXPATHLEN
];
485 login_read_env(_PATH_ETC_ENVIRONMENT
);
487 while(strsep_copy(&str
, ",", buf
, sizeof(buf
)) != -1) {
495 const char *str
= login_conf_get_string("motd");
496 char buf
[MAXPATHLEN
];
499 while(strsep_copy(&str
, ",", buf
, sizeof(buf
)) != -1) {
505 str
= login_conf_get_string("welcome");
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
);
518 check_password(struct passwd
*pwd
, const char *password
)
520 if(pwd
->pw_passwd
== NULL
)
522 if(pwd
->pw_passwd
[0] == '\0'){
523 #ifdef ALLOW_NULL_PASSWORD
524 return password
[0] != '\0';
529 if(strcmp(pwd
->pw_passwd
, crypt(password
, pwd
->pw_passwd
)) == 0)
532 if(krb5_verify(pwd
, password
) == 0) {
538 if (otp_verify (pwd
, password
) == 0) {
549 arg_printusage(args
, nargs
, NULL
, "[username]");
557 fprintf(stderr
, "Login timed out after %d seconds\n",
560 fprintf(stderr
, "Login received signal, exiting\n");
565 main(int argc
, char **argv
)
576 setprogname(argv
[0]);
582 ret
= krb5_init_context(&context
);
584 errx (1, "krb5_init_context failed: %d", ret
);
588 openlog("login", LOG_ODELAY
| LOG_PID
, LOG_AUTH
);
590 if (getarg (args
, sizeof(args
) / sizeof(args
[0]), argc
, argv
,
599 print_version (NULL
);
604 errx(1, "only root may use login, use su");
606 /* Default tty settings. */
612 /* this set of variables is always preserved by BSD login */
614 add_env("TERM", getenv("TERM"));
616 add_env("TZ", getenv("TZ"));
620 if(strchr(*argv
, '=') == NULL
&& strcmp(*argv
, "-") != 0){
621 strlcpy (username
, *argv
, sizeof(username
));
626 #if defined(DCE) && defined(AIX)
627 esetenv("AUTHSTATE", "DCE", 1);
630 /* XXX should we care about environment on the command line? */
632 memset(&sa
, 0, sizeof(sa
));
633 sa
.sa_handler
= sig_handler
;
634 sigemptyset(&sa
.sa_mask
);
636 sigaction(SIGALRM
, &sa
, NULL
);
637 alarm(login_timeout
);
639 for(try = 0; try < max_tries
; try++){
655 ret
= read_string("login: ", username
, sizeof(username
), 1);
659 sig_handler(0); /* exit */
661 pwd
= k_getpwnam(username
);
662 #ifdef ALLOW_NULL_PASSWORD
663 if (pwd
!= NULL
&& (pwd
->pw_passwd
[0] == '\0')) {
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: ",
678 strncpy(prompt
, "Password: ", sizeof(prompt
));
681 ret
= read_string(prompt
, password
, sizeof(password
), 0);
692 fprintf(stderr
, "Login incorrect.\n");
697 if(f_flag
== 0 && check_password(pwd
, password
)){
698 fprintf(stderr
, "Login incorrect.\n");
702 ttyn
= ttyname(STDIN_FILENO
);
704 snprintf(ttname
, sizeof(ttname
), "%s??", _PATH_TTY
);
707 if (strncmp (ttyn
, _PATH_DEV
, strlen(_PATH_DEV
)) == 0)
708 tty
= ttyn
+ strlen(_PATH_DEV
);
712 if (login_access (pwd
, remote_host
? remote_host
: tty
) == 0) {
713 fprintf(stderr
, "Permission denied\n");
715 syslog(LOG_NOTICE
, "%s LOGIN REFUSED FROM %s",
716 pwd
->pw_name
, remote_host
);
718 syslog(LOG_NOTICE
, "%s LOGIN REFUSED ON %s",
723 syslog(LOG_NOTICE
, "%s LOGIN ACCEPTED FROM %s ppid=%d",
724 pwd
->pw_name
, remote_host
, (int) getppid());
726 syslog(LOG_NOTICE
, "%s LOGIN ACCEPTED ON %s ppid=%d",
727 pwd
->pw_name
, tty
, (int) getppid());
730 do_login(pwd
, tty
, ttyn
);