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>
45 __RCSID("$Heimdal: login.c 16498 2006-01-09 16:26:25Z joda $"
48 static int login_timeout
= 60;
51 start_login_process(void)
54 prog
= login_conf_get_string("login_program");
57 argv0
= strrchr(prog
, '/');
64 return simple_execle(prog
, argv0
, NULL
, env
);
68 start_logout_process(void)
73 prog
= login_conf_get_string("logout_program");
76 argv0
= strrchr(prog
, '/');
85 /* avoid getting signals sent to the shell */
91 /* wait for the real login process to exit */
92 #ifdef HAVE_SETPROCTITLE
93 setproctitle("waitpid %d", pid
);
98 ret
= waitpid(pid
, &status
, 0);
100 if(WIFEXITED(status
) || WIFSIGNALED(status
)) {
101 execle(prog
, argv0
, NULL
, env
);
102 err(1, "exec %s", prog
);
110 exec_shell(const char *shell
, int fallback
)
116 if(start_login_process() < 0)
117 warn("login process");
118 start_logout_process();
120 p
= strrchr(shell
, '/');
125 if (asprintf(&sh
, "-%s", p
) == -1)
126 errx(1, "Out of memory");
127 execle(shell
, sh
, NULL
, env
);
129 warnx("Can't exec %s, trying %s",
130 shell
, _PATH_BSHELL
);
131 execle(_PATH_BSHELL
, "-sh", NULL
, env
);
132 err(1, "%s", _PATH_BSHELL
);
137 static enum { NONE
= 0, AUTH_KRB4
= 1, AUTH_KRB5
= 2, AUTH_OTP
= 3 } auth
;
140 static krb5_boolean get_v4_tgt
= FALSE
;
144 static OtpContext otp_ctx
;
147 otp_verify(struct passwd
*pwd
, const char *password
)
149 return (otp_verify_user (&otp_ctx
, password
));
154 static int pag_set
= 0;
157 static krb5_context context
;
158 static krb5_ccache id
, id2
;
161 krb5_verify(struct passwd
*pwd
, const char *password
)
164 krb5_principal princ
;
166 ret
= krb5_parse_name(context
, pwd
->pw_name
, &princ
);
169 ret
= krb5_cc_gen_new(context
, &krb5_mcc_ops
, &id
);
171 krb5_free_principal(context
, princ
);
174 ret
= krb5_verify_user_lrealm(context
,
180 krb5_free_principal(context
, princ
);
185 static krb5_error_code
186 krb5_to4 (krb5_ccache id
)
189 krb5_principal princ
;
191 ret
= krb5_cc_get_principal(context
, id
, &princ
);
193 krb5_appdefault_boolean(context
, "login",
194 krb5_principal_get_realm(context
, princ
),
195 "krb4_get_tickets", FALSE
, &get_v4_tgt
);
196 krb5_free_principal(context
, princ
);
198 krb5_realm realm
= NULL
;
199 krb5_get_default_realm(context
, &realm
);
200 krb5_appdefault_boolean(context
, "login",
202 "krb4_get_tickets", FALSE
, &get_v4_tgt
);
208 krb5_creds mcred
, cred
;
209 char krb4tkfile
[MAXPATHLEN
];
211 krb5_principal princ
;
213 krb5_cc_clear_mcred(&mcred
);
215 ret
= krb5_cc_get_principal (context
, id
, &princ
);
219 ret
= krb5_make_principal(context
, &mcred
.server
,
225 krb5_free_principal(context
, princ
);
228 mcred
.client
= princ
;
230 ret
= krb5_cc_retrieve_cred(context
, id
, 0, &mcred
, &cred
);
232 ret
= krb524_convert_creds_kdc_ccache(context
, id
, &cred
, &c
);
234 snprintf(krb4tkfile
,sizeof(krb4tkfile
),"%s%d",TKT_ROOT
,
236 krb_set_tkt_string(krb4tkfile
);
237 tf_setup(&c
, c
.pname
, c
.pinst
);
239 memset(&c
, 0, sizeof(c
));
240 krb5_free_cred_contents(context
, &cred
);
244 krb5_free_principal(context
, mcred
.server
);
245 krb5_free_principal(context
, mcred
.client
);
252 krb5_start_session (const struct passwd
*pwd
)
257 /* copy credentials to file cache */
258 snprintf(residual
, sizeof(residual
), "FILE:/tmp/krb5cc_%u",
259 (unsigned)pwd
->pw_uid
);
260 krb5_cc_resolve(context
, residual
, &id2
);
261 ret
= krb5_cc_copy_cache(context
, id
, id2
);
263 add_env("KRB5CCNAME", residual
);
265 krb5_cc_destroy (context
, id2
);
271 krb5_cc_close(context
, id2
);
272 krb5_cc_destroy(context
, id
);
279 krb5_free_context(context
);
283 krb5_get_afs_tokens (const struct passwd
*pwd
)
292 ret
= krb5_cc_default(context
, &id2
);
295 pw_dir
= pwd
->pw_dir
;
302 if(k_afs_cell_of_file(pw_dir
, cell
, sizeof(cell
)) == 0)
303 krb5_afslog_uid_home (context
, id2
,
304 cell
, NULL
, pwd
->pw_uid
, pwd
->pw_dir
);
305 krb5_afslog_uid_home (context
, id2
, NULL
, NULL
,
306 pwd
->pw_uid
, pwd
->pw_dir
);
307 krb5_cc_close (context
, id2
);
316 krb4_verify(struct passwd
*pwd
, const char *password
)
318 char lrealm
[REALM_SZ
];
320 char ticket_file
[MaxPathLen
];
322 ret
= krb_get_lrealm (lrealm
, 1);
326 snprintf (ticket_file
, sizeof(ticket_file
),
328 TKT_ROOT
, (unsigned)pwd
->pw_uid
, (unsigned)getpid());
330 krb_set_tkt_string (ticket_file
);
332 ret
= krb_verify_user (pwd
->pw_name
, "", lrealm
, (char *)password
,
333 KRB_VERIFY_SECURE_FAIL
, NULL
);
337 if (chown (ticket_file
, pwd
->pw_uid
, pwd
->pw_gid
) < 0) {
342 add_env ("KRBTKFILE", ticket_file
);
347 krb4_get_afs_tokens (const struct passwd
*pwd
)
355 pw_dir
= pwd
->pw_dir
;
362 if(k_afs_cell_of_file(pw_dir
, cell
, sizeof(cell
)) == 0)
363 krb_afslog_uid_home (cell
, NULL
, pwd
->pw_uid
, pwd
->pw_dir
);
365 krb_afslog_uid_home (NULL
, NULL
, pwd
->pw_uid
, pwd
->pw_dir
);
375 static int version_flag
;
376 static int help_flag
;
377 static char *remote_host
;
378 static char *auth_level
= NULL
;
380 struct getargs args
[] = {
381 { NULL
, 'a', arg_string
, &auth_level
, "authentication mode" },
385 { NULL
, 'f', arg_flag
, &f_flag
, "pre-authenticated" },
386 { NULL
, 'h', arg_string
, &remote_host
, "remote host", "hostname" },
387 { NULL
, 'p', arg_flag
, &p_flag
, "don't purge environment" },
389 { NULL
, 'r', arg_flag
, &r_flag
, "rlogin protocol" },
391 { "version", 0, arg_flag
, &version_flag
},
392 { "help", 0, arg_flag
,&help_flag
, }
395 int nargs
= sizeof(args
) / sizeof(args
[0]);
398 update_utmp(const char *username
, const char *hostname
,
399 char *tty
, char *ttyn
)
402 * Update the utmp files, both BSD and SYSV style.
404 if (utmpx_login(tty
, username
, hostname
) != 0 && !f_flag
) {
405 printf("No utmpx entry. You must exec \"login\" from the "
406 "lowest level shell.\n");
409 utmp_login(ttyn
, username
, hostname
);
418 f
= fopen(_PATH_NOLOGIN
, "r");
421 while(fgets(buf
, sizeof(buf
), f
))
427 /* print contents of a file */
429 show_file(const char *file
)
433 if((f
= fopen(file
, "r")) == NULL
)
435 while (fgets(buf
, sizeof(buf
), f
))
441 * Actually log in the user. `pwd' contains all the relevant
442 * information about the user. `ttyn' is the complete name of the tty
443 * and `tty' the short name.
447 do_login(const struct passwd
*pwd
, char *tty
, char *ttyn
)
452 int rootlogin
= (pwd
->pw_uid
== 0);
455 const char *home_dir
;
462 sp
= getspnam(pwd
->pw_name
);
465 update_utmp(pwd
->pw_name
, remote_host
? remote_host
: "",
468 gr
= getgrnam ("tty");
470 tty_gid
= gr
->gr_gid
;
472 tty_gid
= pwd
->pw_gid
;
474 if (chown (ttyn
, pwd
->pw_uid
, tty_gid
) < 0) {
475 warn("chown %s", ttyn
);
480 if (chmod (ttyn
, S_IRUSR
| S_IWUSR
| S_IWGRP
) < 0) {
481 warn("chmod %s", ttyn
);
487 if(setlogin(pwd
->pw_name
)){
488 warn("setlogin(%s)", pwd
->pw_name
);
494 const char *file
= login_conf_get_string("limits");
496 file
= _PATH_LIMITS_CONF
;
498 read_limits_conf(file
, pwd
);
502 if (setpcred (pwd
->pw_name
, NULL
) == -1)
503 warn("setpcred(%s)", pwd
->pw_name
);
504 #endif /* HAVE_SETPCRED */
505 #ifdef HAVE_INITGROUPS
506 if(initgroups(pwd
->pw_name
, pwd
->pw_gid
)){
507 warn("initgroups(%s, %u)", pwd
->pw_name
, (unsigned)pwd
->pw_gid
);
512 if(do_osfc2_magic(pwd
->pw_uid
))
514 if(setgid(pwd
->pw_gid
)){
515 warn("setgid(%u)", (unsigned)pwd
->pw_gid
);
519 if(setuid(pwd
->pw_uid
) || (pwd
->pw_uid
!= 0 && setuid(0) == 0)) {
520 warn("setuid(%u)", (unsigned)pwd
->pw_uid
);
525 /* make sure signals are set to default actions, apparently some
526 OS:es like to ignore SIGINT, which is not very convenient */
528 for (i
= 1; i
< NSIG
; ++i
)
531 /* all kinds of different magic */
534 check_shadow(pwd
, sp
);
537 #if defined(HAVE_GETUDBNAM) && defined(HAVE_SETLIM)
541 const long maxcpu
= 46116860184; /* some random constant */
542 udb
= getudbnam(pwd
->pw_name
);
544 errx(1, "Failed to get UDB entry.");
545 t
= udb
->ue_pcpulim
[UDBRC_INTER
];
546 if(t
== 0 || t
> maxcpu
)
549 t
*= 100 * CLOCKS_PER_SEC
;
551 if(limit(C_PROC
, 0, L_CPU
, t
) < 0)
552 warn("limit C_PROC");
554 t
= udb
->ue_jcpulim
[UDBRC_INTER
];
555 if(t
== 0 || t
> maxcpu
)
558 t
*= 100 * CLOCKS_PER_SEC
;
560 if(limit(C_JOBPROCS
, 0, L_CPU
, t
) < 0)
561 warn("limit C_JOBPROCS");
563 nice(udb
->ue_nice
[UDBRC_INTER
]);
566 #if defined(HAVE_SGI_GETCAPABILITYBYNAME) && defined(HAVE_CAP_SET_PROC)
567 /* XXX SGI capability hack IRIX 6.x (x >= 0?) has something
568 called capabilities, that allow you to give away
569 permissions (such as chown) to specific processes. From 6.5
570 this is default on, and the default capability set seems to
571 not always be the empty set. The problem is that the
572 runtime linker refuses to do just about anything if the
573 process has *any* capabilities set, so we have to remove
574 them here (unless otherwise instructed by /etc/capability).
575 In IRIX < 6.5, these functions was called sgi_cap_setproc,
576 etc, but we ignore this fact (it works anyway). */
578 struct user_cap
*ucap
= sgi_getcapabilitybyname(pwd
->pw_name
);
581 cap
= cap_from_text("all=");
583 cap
= cap_from_text(ucap
->ca_default
);
585 err(1, "cap_from_text");
586 if(cap_set_proc(cap
) < 0)
587 err(1, "cap_set_proc");
592 home_dir
= pwd
->pw_dir
;
593 if (chdir(home_dir
) < 0) {
594 fprintf(stderr
, "No home directory \"%s\"!\n", pwd
->pw_dir
);
598 fprintf(stderr
, "Logging in with home = \"/\".\n");
601 if (auth
== AUTH_KRB5
) {
602 krb5_start_session (pwd
);
605 else if (auth
== 0) {
609 ret
= krb5_cc_default (context
, &id
);
612 krb5_cc_close (context
, id
);
617 krb5_get_afs_tokens (pwd
);
623 if (auth
== AUTH_KRB4
|| get_v4_tgt
)
624 krb4_get_afs_tokens (pwd
);
627 add_env("PATH", _PATH_DEFPATH
);
630 const char *str
= login_conf_get_string("environment");
631 char buf
[MAXPATHLEN
];
634 login_read_env(_PATH_ETC_ENVIRONMENT
);
636 while(strsep_copy(&str
, ",", buf
, sizeof(buf
)) != -1) {
644 const char *str
= login_conf_get_string("motd");
645 char buf
[MAXPATHLEN
];
648 while(strsep_copy(&str
, ",", buf
, sizeof(buf
)) != -1) {
654 str
= login_conf_get_string("welcome");
659 add_env("HOME", home_dir
);
660 add_env("USER", pwd
->pw_name
);
661 add_env("LOGNAME", pwd
->pw_name
);
662 add_env("SHELL", pwd
->pw_shell
);
663 exec_shell(pwd
->pw_shell
, rootlogin
);
667 check_password(struct passwd
*pwd
, const char *password
)
669 if(pwd
->pw_passwd
== NULL
)
671 if(pwd
->pw_passwd
[0] == '\0'){
672 #ifdef ALLOW_NULL_PASSWORD
673 return password
[0] != '\0';
678 if(strcmp(pwd
->pw_passwd
, crypt(password
, pwd
->pw_passwd
)) == 0)
681 if(krb5_verify(pwd
, password
) == 0) {
687 if (krb4_verify (pwd
, password
) == 0) {
693 if (otp_verify (pwd
, password
) == 0) {
704 arg_printusage(args
, nargs
, NULL
, "[username]");
712 fprintf(stderr
, "Login timed out after %d seconds\n",
715 fprintf(stderr
, "Login received signal, exiting\n");
720 main(int argc
, char **argv
)
731 setprogname(argv
[0]);
737 ret
= krb5_init_context(&context
);
739 errx (1, "krb5_init_context failed: %d", ret
);
743 openlog("login", LOG_ODELAY
| LOG_PID
, LOG_AUTH
);
745 if (getarg (args
, sizeof(args
) / sizeof(args
[0]), argc
, argv
,
754 print_version (NULL
);
759 errx(1, "only root may use login, use su");
761 /* Default tty settings. */
767 /* this set of variables is always preserved by BSD login */
769 add_env("TERM", getenv("TERM"));
771 add_env("TZ", getenv("TZ"));
775 if(strchr(*argv
, '=') == NULL
&& strcmp(*argv
, "-") != 0){
776 strlcpy (username
, *argv
, sizeof(username
));
781 #if defined(DCE) && defined(AIX)
782 esetenv("AUTHSTATE", "DCE", 1);
785 /* XXX should we care about environment on the command line? */
787 memset(&sa
, 0, sizeof(sa
));
788 sa
.sa_handler
= sig_handler
;
789 sigemptyset(&sa
.sa_mask
);
791 sigaction(SIGALRM
, &sa
, NULL
);
792 alarm(login_timeout
);
794 for(try = 0; try < max_tries
; try++){
810 ret
= read_string("login: ", username
, sizeof(username
), 1);
814 sig_handler(0); /* exit */
816 pwd
= k_getpwnam(username
);
817 #ifdef ALLOW_NULL_PASSWORD
818 if (pwd
!= NULL
&& (pwd
->pw_passwd
[0] == '\0')) {
826 if(auth_level
&& strcmp(auth_level
, "otp") == 0 &&
827 otp_challenge(&otp_ctx
, username
,
828 otp_str
, sizeof(otp_str
)) == 0)
829 snprintf (prompt
, sizeof(prompt
), "%s's %s Password: ",
833 strncpy(prompt
, "Password: ", sizeof(prompt
));
836 ret
= read_string(prompt
, password
, sizeof(password
), 0);
847 fprintf(stderr
, "Login incorrect.\n");
852 if(f_flag
== 0 && check_password(pwd
, password
)){
853 fprintf(stderr
, "Login incorrect.\n");
857 ttyn
= ttyname(STDIN_FILENO
);
859 snprintf(ttname
, sizeof(ttname
), "%s??", _PATH_TTY
);
862 if (strncmp (ttyn
, _PATH_DEV
, strlen(_PATH_DEV
)) == 0)
863 tty
= ttyn
+ strlen(_PATH_DEV
);
867 if (login_access (pwd
, remote_host
? remote_host
: tty
) == 0) {
868 fprintf(stderr
, "Permission denied\n");
870 syslog(LOG_NOTICE
, "%s LOGIN REFUSED FROM %s",
871 pwd
->pw_name
, remote_host
);
873 syslog(LOG_NOTICE
, "%s LOGIN REFUSED ON %s",
878 syslog(LOG_NOTICE
, "%s LOGIN ACCEPTED FROM %s ppid=%d",
879 pwd
->pw_name
, remote_host
, (int) getppid());
881 syslog(LOG_NOTICE
, "%s LOGIN ACCEPTED ON %s ppid=%d",
882 pwd
->pw_name
, tty
, (int) getppid());
885 do_login(pwd
, tty
, ttyn
);