2 * Copyright (c) 1997 - 2003 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>
42 RCSID("$Id: login.c,v 1.59.2.1 2004/09/08 09:15:39 joda Exp $");
44 static int login_timeout = 60;
47 start_login_process(void)
50 prog = login_conf_get_string("login_program");
53 argv0 = strrchr(prog, '/');
60 return simple_execle(prog, argv0, NULL, env);
64 start_logout_process(void)
69 prog = login_conf_get_string("logout_program");
72 argv0 = strrchr(prog, '/');
81 /* avoid getting signals sent to the shell */
87 /* wait for the real login process to exit */
88 #ifdef HAVE_SETPROCTITLE
89 setproctitle("waitpid %d", pid);
94 ret = waitpid(pid, &status, 0);
96 if(WIFEXITED(status) || WIFSIGNALED(status)) {
97 execle(prog, argv0, NULL, env);
98 err(1, "exec %s", prog);
106 exec_shell(const char *shell, int fallback)
112 if(start_login_process() < 0)
113 warn("login process");
114 start_logout_process();
116 p = strrchr(shell, '/');
121 asprintf(&sh, "-%s", p);
122 execle(shell, sh, NULL, env);
124 warnx("Can't exec %s, trying %s",
125 shell, _PATH_BSHELL);
126 execle(_PATH_BSHELL, "-sh", NULL, env);
127 err(1, "%s", _PATH_BSHELL);
132 static enum { NONE = 0, AUTH_KRB4 = 1, AUTH_KRB5 = 2, AUTH_OTP = 3 } auth;
135 static OtpContext otp_ctx;
138 otp_verify(struct passwd *pwd, const char *password)
140 return (otp_verify_user (&otp_ctx, password));
145 static int pag_set = 0;
148 static krb5_context context;
149 static krb5_ccache id, id2;
152 krb5_verify(struct passwd *pwd, const char *password)
155 krb5_principal princ;
157 ret = krb5_parse_name(context, pwd->pw_name, &princ);
160 ret = krb5_cc_gen_new(context, &krb5_mcc_ops, &id);
162 krb5_free_principal(context, princ);
165 ret = krb5_verify_user_lrealm(context,
171 krb5_free_principal(context, princ);
176 static krb5_error_code
177 krb5_to4 (krb5_ccache id)
180 krb5_principal princ;
184 ret = krb5_cc_get_principal(context, id, &princ);
186 krb5_appdefault_boolean(context, "login",
187 krb5_principal_get_realm(context, princ),
188 "krb4_get_tickets", FALSE, &get_v4_tgt);
189 krb5_free_principal(context, princ);
191 krb5_realm realm = NULL;
192 krb5_get_default_realm(context, &realm);
193 krb5_appdefault_boolean(context, "login",
195 "krb4_get_tickets", FALSE, &get_v4_tgt);
201 krb5_creds mcred, cred;
202 char krb4tkfile[MAXPATHLEN];
204 krb5_principal princ;
206 ret = krb5_cc_get_principal (context, id, &princ);
210 ret = krb5_make_principal(context, &mcred.server,
215 krb5_free_principal (context, princ);
219 ret = krb5_cc_retrieve_cred(context, id, 0, &mcred, &cred);
221 ret = krb524_convert_creds_kdc_ccache(context, id, &cred, &c);
223 snprintf(krb4tkfile,sizeof(krb4tkfile),"%s%d",TKT_ROOT,
225 krb_set_tkt_string(krb4tkfile);
226 tf_setup(&c, c.pname, c.pinst);
228 memset(&c, 0, sizeof(c));
229 krb5_free_creds_contents(context, &cred);
231 krb5_free_principal(context, mcred.server);
238 krb5_start_session (const struct passwd *pwd)
243 /* copy credentials to file cache */
244 snprintf(residual, sizeof(residual), "FILE:/tmp/krb5cc_%u",
245 (unsigned)pwd->pw_uid);
246 krb5_cc_resolve(context, residual, &id2);
247 ret = krb5_cc_copy_cache(context, id, id2);
249 add_env("KRB5CCNAME", residual);
251 krb5_cc_destroy (context, id2);
257 krb5_cc_close(context, id2);
258 krb5_cc_destroy(context, id);
265 krb5_free_context(context);
269 krb5_get_afs_tokens (const struct passwd *pwd)
278 ret = krb5_cc_default(context, &id2);
281 pw_dir = pwd->pw_dir;
288 if(k_afs_cell_of_file(pw_dir, cell, sizeof(cell)) == 0)
289 krb5_afslog_uid_home (context, id2,
290 cell, NULL, pwd->pw_uid, pwd->pw_dir);
291 krb5_afslog_uid_home (context, id2, NULL, NULL,
292 pwd->pw_uid, pwd->pw_dir);
293 krb5_cc_close (context, id2);
302 krb4_verify(struct passwd *pwd, const char *password)
304 char lrealm[REALM_SZ];
306 char ticket_file[MaxPathLen];
308 ret = krb_get_lrealm (lrealm, 1);
312 snprintf (ticket_file, sizeof(ticket_file),
314 TKT_ROOT, (unsigned)pwd->pw_uid, (unsigned)getpid());
316 krb_set_tkt_string (ticket_file);
318 ret = krb_verify_user (pwd->pw_name, "", lrealm, (char *)password,
319 KRB_VERIFY_SECURE_FAIL, NULL);
323 if (chown (ticket_file, pwd->pw_uid, pwd->pw_gid) < 0) {
328 add_env ("KRBTKFILE", ticket_file);
333 krb4_get_afs_tokens (const struct passwd *pwd)
341 pw_dir = pwd->pw_dir;
348 if(k_afs_cell_of_file(pw_dir, cell, sizeof(cell)) == 0)
349 krb_afslog_uid_home (cell, NULL, pwd->pw_uid, pwd->pw_dir);
351 krb_afslog_uid_home (NULL, NULL, pwd->pw_uid, pwd->pw_dir);
361 static int version_flag;
362 static int help_flag;
363 static char *remote_host;
364 static char *auth_level = NULL;
366 struct getargs args[] = {
367 { NULL, 'a', arg_string, &auth_level, "authentication mode" },
371 { NULL, 'f', arg_flag, &f_flag, "pre-authenticated" },
372 { NULL, 'h', arg_string, &remote_host, "remote host", "hostname" },
373 { NULL, 'p', arg_flag, &p_flag, "don't purge environment" },
375 { NULL, 'r', arg_flag, &r_flag, "rlogin protocol" },
377 { "version", 0, arg_flag, &version_flag },
378 { "help", 0, arg_flag,&help_flag, }
381 int nargs = sizeof(args) / sizeof(args[0]);
384 update_utmp(const char *username, const char *hostname,
385 char *tty, char *ttyn)
388 * Update the utmp files, both BSD and SYSV style.
390 if (utmpx_login(tty, username, hostname) != 0 && !f_flag) {
391 printf("No utmpx entry. You must exec \"login\" from the "
392 "lowest level shell.\n");
395 utmp_login(ttyn, username, hostname);
404 f = fopen(_PATH_NOLOGIN, "r");
407 while(fgets(buf, sizeof(buf), f))
413 /* print contents of a file */
415 show_file(const char *file)
419 if((f = fopen(file, "r")) == NULL)
421 while (fgets(buf, sizeof(buf), f))
427 * Actually log in the user. `pwd' contains all the relevant
428 * information about the user. `ttyn' is the complete name of the tty
429 * and `tty' the short name.
433 do_login(const struct passwd *pwd, char *tty, char *ttyn)
438 int rootlogin = (pwd->pw_uid == 0);
441 const char *home_dir;
448 sp = getspnam(pwd->pw_name);
451 update_utmp(pwd->pw_name, remote_host ? remote_host : "",
454 gr = getgrnam ("tty");
456 tty_gid = gr->gr_gid;
458 tty_gid = pwd->pw_gid;
460 if (chown (ttyn, pwd->pw_uid, tty_gid) < 0) {
461 warn("chown %s", ttyn);
466 if (chmod (ttyn, S_IRUSR | S_IWUSR | S_IWGRP) < 0) {
467 warn("chmod %s", ttyn);
473 if(setlogin(pwd->pw_name)){
474 warn("setlogin(%s)", pwd->pw_name);
480 if (setpcred (pwd->pw_name, NULL) == -1)
481 warn("setpcred(%s)", pwd->pw_name);
482 #endif /* HAVE_SETPCRED */
483 #ifdef HAVE_INITGROUPS
484 if(initgroups(pwd->pw_name, pwd->pw_gid)){
485 warn("initgroups(%s, %u)", pwd->pw_name, (unsigned)pwd->pw_gid);
490 if(do_osfc2_magic(pwd->pw_uid))
492 if(setgid(pwd->pw_gid)){
493 warn("setgid(%u)", (unsigned)pwd->pw_gid);
497 if(setuid(pwd->pw_uid) || (pwd->pw_uid != 0 && setuid(0) == 0)) {
498 warn("setuid(%u)", (unsigned)pwd->pw_uid);
503 /* make sure signals are set to default actions, apparently some
504 OS:es like to ignore SIGINT, which is not very convenient */
506 for (i = 1; i < NSIG; ++i)
509 /* all kinds of different magic */
512 check_shadow(pwd, sp);
515 #if defined(HAVE_GETUDBNAM) && defined(HAVE_SETLIM)
519 const long maxcpu = 46116860184; /* some random constant */
520 udb = getudbnam(pwd->pw_name);
522 errx(1, "Failed to get UDB entry.");
523 t = udb->ue_pcpulim[UDBRC_INTER];
524 if(t == 0 || t > maxcpu)
527 t *= 100 * CLOCKS_PER_SEC;
529 if(limit(C_PROC, 0, L_CPU, t) < 0)
530 warn("limit C_PROC");
532 t = udb->ue_jcpulim[UDBRC_INTER];
533 if(t == 0 || t > maxcpu)
536 t *= 100 * CLOCKS_PER_SEC;
538 if(limit(C_JOBPROCS, 0, L_CPU, t) < 0)
539 warn("limit C_JOBPROCS");
541 nice(udb->ue_nice[UDBRC_INTER]);
544 #if defined(HAVE_SGI_GETCAPABILITYBYNAME) && defined(HAVE_CAP_SET_PROC)
545 /* XXX SGI capability hack IRIX 6.x (x >= 0?) has something
546 called capabilities, that allow you to give away
547 permissions (such as chown) to specific processes. From 6.5
548 this is default on, and the default capability set seems to
549 not always be the empty set. The problem is that the
550 runtime linker refuses to do just about anything if the
551 process has *any* capabilities set, so we have to remove
552 them here (unless otherwise instructed by /etc/capability).
553 In IRIX < 6.5, these functions was called sgi_cap_setproc,
554 etc, but we ignore this fact (it works anyway). */
556 struct user_cap *ucap = sgi_getcapabilitybyname(pwd->pw_name);
559 cap = cap_from_text("all=");
561 cap = cap_from_text(ucap->ca_default);
563 err(1, "cap_from_text");
564 if(cap_set_proc(cap) < 0)
565 err(1, "cap_set_proc");
570 home_dir = pwd->pw_dir;
571 if (chdir(home_dir) < 0) {
572 fprintf(stderr, "No home directory \"%s\"!\n", pwd->pw_dir);
576 fprintf(stderr, "Logging in with home = \"/\".\n");
579 if (auth == AUTH_KRB5) {
580 krb5_start_session (pwd);
583 else if (auth == 0) {
587 ret = krb5_cc_default (context, &id);
590 krb5_cc_close (context, id);
595 krb5_get_afs_tokens (pwd);
601 krb4_get_afs_tokens (pwd);
604 add_env("PATH", _PATH_DEFPATH);
607 const char *str = login_conf_get_string("environment");
608 char buf[MAXPATHLEN];
611 login_read_env(_PATH_ETC_ENVIRONMENT);
613 while(strsep_copy(&str, ",", buf, sizeof(buf)) != -1) {
621 const char *str = login_conf_get_string("motd");
622 char buf[MAXPATHLEN];
625 while(strsep_copy(&str, ",", buf, sizeof(buf)) != -1) {
631 str = login_conf_get_string("welcome");
636 add_env("HOME", home_dir);
637 add_env("USER", pwd->pw_name);
638 add_env("LOGNAME", pwd->pw_name);
639 add_env("SHELL", pwd->pw_shell);
640 exec_shell(pwd->pw_shell, rootlogin);
644 check_password(struct passwd *pwd, const char *password)
646 if(pwd->pw_passwd == NULL)
648 if(pwd->pw_passwd[0] == '\0'){
649 #ifdef ALLOW_NULL_PASSWORD
650 return password[0] != '\0';
655 if(strcmp(pwd->pw_passwd, crypt(password, pwd->pw_passwd)) == 0)
658 if(krb5_verify(pwd, password) == 0) {
664 if (krb4_verify (pwd, password) == 0) {
670 if (otp_verify (pwd, password) == 0) {
681 arg_printusage(args, nargs, NULL, "[username]");
689 fprintf(stderr, "Login timed out after %d seconds\n",
692 fprintf(stderr, "Login received signal, exiting\n");
697 main(int argc, char **argv)
708 setprogname(argv[0]);
714 ret = krb5_init_context(&context);
716 errx (1, "krb5_init_context failed: %d", ret);
720 openlog("login", LOG_ODELAY, LOG_AUTH);
722 if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv,
731 print_version (NULL);
736 errx(1, "only root may use login, use su");
738 /* Default tty settings. */
744 /* this set of variables is always preserved by BSD login */
746 add_env("TERM", getenv("TERM"));
748 add_env("TZ", getenv("TZ"));
752 if(strchr(*argv, '=') == NULL && strcmp(*argv, "-") != 0){
753 strlcpy (username, *argv, sizeof(username));
758 #if defined(DCE) && defined(AIX)
759 esetenv("AUTHSTATE", "DCE", 1);
762 /* XXX should we care about environment on the command line? */
764 memset(&sa, 0, sizeof(sa));
765 sa.sa_handler = sig_handler;
766 sigemptyset(&sa.sa_mask);
768 sigaction(SIGALRM, &sa, NULL);
769 alarm(login_timeout);
771 for(try = 0; try < max_tries; try++){
787 ret = read_string("login: ", username, sizeof(username), 1);
791 sig_handler(0); /* exit */
793 pwd = k_getpwnam(username);
794 #ifdef ALLOW_NULL_PASSWORD
795 if (pwd != NULL && (pwd->pw_passwd[0] == '\0')) {
803 if(auth_level && strcmp(auth_level, "otp") == 0 &&
804 otp_challenge(&otp_ctx, username,
805 otp_str, sizeof(otp_str)) == 0)
806 snprintf (prompt, sizeof(prompt), "%s's %s Password: ",
810 strncpy(prompt, "Password: ", sizeof(prompt));
813 ret = read_string(prompt, password, sizeof(password), 0);
824 fprintf(stderr, "Login incorrect.\n");
829 if(f_flag == 0 && check_password(pwd, password)){
830 fprintf(stderr, "Login incorrect.\n");
834 ttyn = ttyname(STDIN_FILENO);
836 snprintf(ttname, sizeof(ttname), "%s??", _PATH_TTY);
839 if (strncmp (ttyn, _PATH_DEV, strlen(_PATH_DEV)) == 0)
840 tty = ttyn + strlen(_PATH_DEV);
844 if (login_access (pwd, remote_host ? remote_host : tty) == 0) {
845 fprintf(stderr, "Permission denied\n");
847 syslog(LOG_NOTICE, "%s LOGIN REFUSED FROM %s",
848 pwd->pw_name, remote_host);
850 syslog(LOG_NOTICE, "%s LOGIN REFUSED ON %s",
855 do_login(pwd, tty, ttyn);