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_KRB4
= 1, AUTH_KRB5
= 2, AUTH_OTP
= 3 } auth
;
139 static krb5_boolean get_v4_tgt
= FALSE
;
143 static OtpContext otp_ctx
;
146 otp_verify(struct passwd
*pwd
, const char *password
)
148 return (otp_verify_user (&otp_ctx
, password
));
153 static int pag_set
= 0;
156 static krb5_context context
;
157 static krb5_ccache id
, id2
;
160 krb5_verify(struct passwd
*pwd
, const char *password
)
163 krb5_principal princ
;
165 ret
= krb5_parse_name(context
, pwd
->pw_name
, &princ
);
168 ret
= krb5_cc_gen_new(context
, &krb5_mcc_ops
, &id
);
170 krb5_free_principal(context
, princ
);
173 ret
= krb5_verify_user_lrealm(context
,
179 krb5_free_principal(context
, princ
);
184 static krb5_error_code
185 krb5_to4 (krb5_ccache id
)
188 krb5_principal princ
;
190 ret
= krb5_cc_get_principal(context
, id
, &princ
);
192 krb5_appdefault_boolean(context
, "login",
193 krb5_principal_get_realm(context
, princ
),
194 "krb4_get_tickets", FALSE
, &get_v4_tgt
);
195 krb5_free_principal(context
, princ
);
197 krb5_realm realm
= NULL
;
198 krb5_get_default_realm(context
, &realm
);
199 krb5_appdefault_boolean(context
, "login",
201 "krb4_get_tickets", FALSE
, &get_v4_tgt
);
207 krb5_creds mcred
, cred
;
208 char krb4tkfile
[MAXPATHLEN
];
210 krb5_principal princ
;
212 krb5_cc_clear_mcred(&mcred
);
214 ret
= krb5_cc_get_principal (context
, id
, &princ
);
218 ret
= krb5_make_principal(context
, &mcred
.server
,
224 krb5_free_principal(context
, princ
);
227 mcred
.client
= princ
;
229 ret
= krb5_cc_retrieve_cred(context
, id
, 0, &mcred
, &cred
);
231 ret
= krb524_convert_creds_kdc_ccache(context
, id
, &cred
, &c
);
233 snprintf(krb4tkfile
,sizeof(krb4tkfile
),"%s%d",TKT_ROOT
,
235 krb_set_tkt_string(krb4tkfile
);
236 tf_setup(&c
, c
.pname
, c
.pinst
);
238 memset(&c
, 0, sizeof(c
));
239 krb5_free_cred_contents(context
, &cred
);
243 krb5_free_principal(context
, mcred
.server
);
244 krb5_free_principal(context
, mcred
.client
);
251 krb5_start_session (const struct passwd
*pwd
)
256 /* copy credentials to file cache */
257 snprintf(residual
, sizeof(residual
), "FILE:/tmp/krb5cc_%u",
258 (unsigned)pwd
->pw_uid
);
259 krb5_cc_resolve(context
, residual
, &id2
);
260 ret
= krb5_cc_copy_cache(context
, id
, id2
);
262 add_env("KRB5CCNAME", residual
);
264 krb5_cc_destroy (context
, id2
);
270 krb5_cc_close(context
, id2
);
271 krb5_cc_destroy(context
, id
);
278 krb5_free_context(context
);
282 krb5_get_afs_tokens (const struct passwd
*pwd
)
291 ret
= krb5_cc_default(context
, &id2
);
294 pw_dir
= pwd
->pw_dir
;
301 if(k_afs_cell_of_file(pw_dir
, cell
, sizeof(cell
)) == 0)
302 krb5_afslog_uid_home (context
, id2
,
303 cell
, NULL
, pwd
->pw_uid
, pwd
->pw_dir
);
304 krb5_afslog_uid_home (context
, id2
, NULL
, NULL
,
305 pwd
->pw_uid
, pwd
->pw_dir
);
306 krb5_cc_close (context
, id2
);
315 krb4_verify(struct passwd
*pwd
, const char *password
)
317 char lrealm
[REALM_SZ
];
319 char ticket_file
[MaxPathLen
];
321 ret
= krb_get_lrealm (lrealm
, 1);
325 snprintf (ticket_file
, sizeof(ticket_file
),
327 TKT_ROOT
, (unsigned)pwd
->pw_uid
, (unsigned)getpid());
329 krb_set_tkt_string (ticket_file
);
331 ret
= krb_verify_user (pwd
->pw_name
, "", lrealm
, (char *)password
,
332 KRB_VERIFY_SECURE_FAIL
, NULL
);
336 if (chown (ticket_file
, pwd
->pw_uid
, pwd
->pw_gid
) < 0) {
341 add_env ("KRBTKFILE", ticket_file
);
346 krb4_get_afs_tokens (const struct passwd
*pwd
)
354 pw_dir
= pwd
->pw_dir
;
361 if(k_afs_cell_of_file(pw_dir
, cell
, sizeof(cell
)) == 0)
362 krb_afslog_uid_home (cell
, NULL
, pwd
->pw_uid
, pwd
->pw_dir
);
364 krb_afslog_uid_home (NULL
, NULL
, pwd
->pw_uid
, pwd
->pw_dir
);
374 static int version_flag
;
375 static int help_flag
;
376 static char *remote_host
;
377 static char *auth_level
= NULL
;
379 struct getargs args
[] = {
380 { NULL
, 'a', arg_string
, &auth_level
, "authentication mode" },
384 { NULL
, 'f', arg_flag
, &f_flag
, "pre-authenticated" },
385 { NULL
, 'h', arg_string
, &remote_host
, "remote host", "hostname" },
386 { NULL
, 'p', arg_flag
, &p_flag
, "don't purge environment" },
388 { NULL
, 'r', arg_flag
, &r_flag
, "rlogin protocol" },
390 { "version", 0, arg_flag
, &version_flag
},
391 { "help", 0, arg_flag
,&help_flag
, }
394 int nargs
= sizeof(args
) / sizeof(args
[0]);
397 update_utmp(const char *username
, const char *hostname
,
398 char *tty
, char *ttyn
)
401 * Update the utmp files, both BSD and SYSV style.
403 if (utmpx_login(tty
, username
, hostname
) != 0 && !f_flag
) {
404 printf("No utmpx entry. You must exec \"login\" from the "
405 "lowest level shell.\n");
408 utmp_login(ttyn
, username
, hostname
);
417 f
= fopen(_PATH_NOLOGIN
, "r");
420 while(fgets(buf
, sizeof(buf
), f
))
426 /* print contents of a file */
428 show_file(const char *file
)
432 if((f
= fopen(file
, "r")) == NULL
)
434 while (fgets(buf
, sizeof(buf
), f
))
440 * Actually log in the user. `pwd' contains all the relevant
441 * information about the user. `ttyn' is the complete name of the tty
442 * and `tty' the short name.
446 do_login(const struct passwd
*pwd
, char *tty
, char *ttyn
)
451 int rootlogin
= (pwd
->pw_uid
== 0);
454 const char *home_dir
;
461 sp
= getspnam(pwd
->pw_name
);
464 update_utmp(pwd
->pw_name
, remote_host
? remote_host
: "",
467 gr
= getgrnam ("tty");
469 tty_gid
= gr
->gr_gid
;
471 tty_gid
= pwd
->pw_gid
;
473 if (chown (ttyn
, pwd
->pw_uid
, tty_gid
) < 0) {
474 warn("chown %s", ttyn
);
479 if (chmod (ttyn
, S_IRUSR
| S_IWUSR
| S_IWGRP
) < 0) {
480 warn("chmod %s", ttyn
);
486 if(setlogin(pwd
->pw_name
)){
487 warn("setlogin(%s)", pwd
->pw_name
);
493 const char *file
= login_conf_get_string("limits");
495 file
= _PATH_LIMITS_CONF
;
497 read_limits_conf(file
, pwd
);
501 if (setpcred (pwd
->pw_name
, NULL
) == -1)
502 warn("setpcred(%s)", pwd
->pw_name
);
503 #endif /* HAVE_SETPCRED */
504 #ifdef HAVE_INITGROUPS
505 if(initgroups(pwd
->pw_name
, pwd
->pw_gid
)){
506 warn("initgroups(%s, %u)", pwd
->pw_name
, (unsigned)pwd
->pw_gid
);
511 if(do_osfc2_magic(pwd
->pw_uid
))
513 if(setgid(pwd
->pw_gid
)){
514 warn("setgid(%u)", (unsigned)pwd
->pw_gid
);
518 if(setuid(pwd
->pw_uid
) || (pwd
->pw_uid
!= 0 && setuid(0) == 0)) {
519 warn("setuid(%u)", (unsigned)pwd
->pw_uid
);
524 /* make sure signals are set to default actions, apparently some
525 OS:es like to ignore SIGINT, which is not very convenient */
527 for (i
= 1; i
< NSIG
; ++i
)
530 /* all kinds of different magic */
533 check_shadow(pwd
, sp
);
536 #if defined(HAVE_GETUDBNAM) && defined(HAVE_SETLIM)
540 const long maxcpu
= 46116860184; /* some random constant */
541 udb
= getudbnam(pwd
->pw_name
);
543 errx(1, "Failed to get UDB entry.");
544 t
= udb
->ue_pcpulim
[UDBRC_INTER
];
545 if(t
== 0 || t
> maxcpu
)
548 t
*= 100 * CLOCKS_PER_SEC
;
550 if(limit(C_PROC
, 0, L_CPU
, t
) < 0)
551 warn("limit C_PROC");
553 t
= udb
->ue_jcpulim
[UDBRC_INTER
];
554 if(t
== 0 || t
> maxcpu
)
557 t
*= 100 * CLOCKS_PER_SEC
;
559 if(limit(C_JOBPROCS
, 0, L_CPU
, t
) < 0)
560 warn("limit C_JOBPROCS");
562 nice(udb
->ue_nice
[UDBRC_INTER
]);
565 #if defined(HAVE_SGI_GETCAPABILITYBYNAME) && defined(HAVE_CAP_SET_PROC)
566 /* XXX SGI capability hack IRIX 6.x (x >= 0?) has something
567 called capabilities, that allow you to give away
568 permissions (such as chown) to specific processes. From 6.5
569 this is default on, and the default capability set seems to
570 not always be the empty set. The problem is that the
571 runtime linker refuses to do just about anything if the
572 process has *any* capabilities set, so we have to remove
573 them here (unless otherwise instructed by /etc/capability).
574 In IRIX < 6.5, these functions was called sgi_cap_setproc,
575 etc, but we ignore this fact (it works anyway). */
577 struct user_cap
*ucap
= sgi_getcapabilitybyname(pwd
->pw_name
);
580 cap
= cap_from_text("all=");
582 cap
= cap_from_text(ucap
->ca_default
);
584 err(1, "cap_from_text");
585 if(cap_set_proc(cap
) < 0)
586 err(1, "cap_set_proc");
591 home_dir
= pwd
->pw_dir
;
592 if (chdir(home_dir
) < 0) {
593 fprintf(stderr
, "No home directory \"%s\"!\n", pwd
->pw_dir
);
597 fprintf(stderr
, "Logging in with home = \"/\".\n");
600 if (auth
== AUTH_KRB5
) {
601 krb5_start_session (pwd
);
604 else if (auth
== 0) {
608 ret
= krb5_cc_default (context
, &id
);
611 krb5_cc_close (context
, id
);
616 krb5_get_afs_tokens (pwd
);
622 if (auth
== AUTH_KRB4
|| get_v4_tgt
)
623 krb4_get_afs_tokens (pwd
);
626 add_env("PATH", _PATH_DEFPATH
);
629 const char *str
= login_conf_get_string("environment");
630 char buf
[MAXPATHLEN
];
633 login_read_env(_PATH_ETC_ENVIRONMENT
);
635 while(strsep_copy(&str
, ",", buf
, sizeof(buf
)) != -1) {
643 const char *str
= login_conf_get_string("motd");
644 char buf
[MAXPATHLEN
];
647 while(strsep_copy(&str
, ",", buf
, sizeof(buf
)) != -1) {
653 str
= login_conf_get_string("welcome");
658 add_env("HOME", home_dir
);
659 add_env("USER", pwd
->pw_name
);
660 add_env("LOGNAME", pwd
->pw_name
);
661 add_env("SHELL", pwd
->pw_shell
);
662 exec_shell(pwd
->pw_shell
, rootlogin
);
666 check_password(struct passwd
*pwd
, const char *password
)
668 if(pwd
->pw_passwd
== NULL
)
670 if(pwd
->pw_passwd
[0] == '\0'){
671 #ifdef ALLOW_NULL_PASSWORD
672 return password
[0] != '\0';
677 if(strcmp(pwd
->pw_passwd
, crypt(password
, pwd
->pw_passwd
)) == 0)
680 if(krb5_verify(pwd
, password
) == 0) {
686 if (krb4_verify (pwd
, password
) == 0) {
692 if (otp_verify (pwd
, password
) == 0) {
703 arg_printusage(args
, nargs
, NULL
, "[username]");
711 fprintf(stderr
, "Login timed out after %d seconds\n",
714 fprintf(stderr
, "Login received signal, exiting\n");
719 main(int argc
, char **argv
)
730 setprogname(argv
[0]);
736 ret
= krb5_init_context(&context
);
738 errx (1, "krb5_init_context failed: %d", ret
);
742 openlog("login", LOG_ODELAY
| LOG_PID
, LOG_AUTH
);
744 if (getarg (args
, sizeof(args
) / sizeof(args
[0]), argc
, argv
,
753 print_version (NULL
);
758 errx(1, "only root may use login, use su");
760 /* Default tty settings. */
766 /* this set of variables is always preserved by BSD login */
768 add_env("TERM", getenv("TERM"));
770 add_env("TZ", getenv("TZ"));
774 if(strchr(*argv
, '=') == NULL
&& strcmp(*argv
, "-") != 0){
775 strlcpy (username
, *argv
, sizeof(username
));
780 #if defined(DCE) && defined(AIX)
781 esetenv("AUTHSTATE", "DCE", 1);
784 /* XXX should we care about environment on the command line? */
786 memset(&sa
, 0, sizeof(sa
));
787 sa
.sa_handler
= sig_handler
;
788 sigemptyset(&sa
.sa_mask
);
790 sigaction(SIGALRM
, &sa
, NULL
);
791 alarm(login_timeout
);
793 for(try = 0; try < max_tries
; try++){
809 ret
= read_string("login: ", username
, sizeof(username
), 1);
813 sig_handler(0); /* exit */
815 pwd
= k_getpwnam(username
);
816 #ifdef ALLOW_NULL_PASSWORD
817 if (pwd
!= NULL
&& (pwd
->pw_passwd
[0] == '\0')) {
825 if(auth_level
&& strcmp(auth_level
, "otp") == 0 &&
826 otp_challenge(&otp_ctx
, username
,
827 otp_str
, sizeof(otp_str
)) == 0)
828 snprintf (prompt
, sizeof(prompt
), "%s's %s Password: ",
832 strncpy(prompt
, "Password: ", sizeof(prompt
));
835 ret
= read_string(prompt
, password
, sizeof(password
), 0);
846 fprintf(stderr
, "Login incorrect.\n");
851 if(f_flag
== 0 && check_password(pwd
, password
)){
852 fprintf(stderr
, "Login incorrect.\n");
856 ttyn
= ttyname(STDIN_FILENO
);
858 snprintf(ttname
, sizeof(ttname
), "%s??", _PATH_TTY
);
861 if (strncmp (ttyn
, _PATH_DEV
, strlen(_PATH_DEV
)) == 0)
862 tty
= ttyn
+ strlen(_PATH_DEV
);
866 if (login_access (pwd
, remote_host
? remote_host
: tty
) == 0) {
867 fprintf(stderr
, "Permission denied\n");
869 syslog(LOG_NOTICE
, "%s LOGIN REFUSED FROM %s",
870 pwd
->pw_name
, remote_host
);
872 syslog(LOG_NOTICE
, "%s LOGIN REFUSED ON %s",
877 syslog(LOG_NOTICE
, "%s LOGIN ACCEPTED FROM %s ppid=%d",
878 pwd
->pw_name
, remote_host
, (int) getppid());
880 syslog(LOG_NOTICE
, "%s LOGIN ACCEPTED ON %s ppid=%d",
881 pwd
->pw_name
, tty
, (int) getppid());
884 do_login(pwd
, tty
, ttyn
);