2 * Copyright (c) 1997 - 2002 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.56 2002/08/23 12:11:09 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));
146 static int pag_set = 0;
150 static krb5_context context;
151 static krb5_ccache id, id2;
154 krb5_verify(struct passwd *pwd, const char *password)
157 krb5_principal princ;
159 ret = krb5_parse_name(context, pwd->pw_name, &princ);
162 ret = krb5_cc_gen_new(context, &krb5_mcc_ops, &id);
164 krb5_free_principal(context, princ);
167 ret = krb5_verify_user_lrealm(context,
173 krb5_free_principal(context, princ);
178 static krb5_error_code
179 krb5_to4 (krb5_ccache id)
182 krb5_principal princ;
186 get_v4_tgt = krb5_config_get_bool(context, NULL,
191 ret = krb5_cc_get_principal(context, id, &princ);
193 get_v4_tgt = krb5_config_get_bool_default(context, NULL,
196 *krb5_princ_realm(context,
200 krb5_free_principal(context, princ);
205 krb5_creds mcred, cred;
206 char krb4tkfile[MAXPATHLEN];
208 krb5_principal princ;
210 ret = krb5_cc_get_principal (context, id, &princ);
214 ret = krb5_make_principal(context, &mcred.server,
219 krb5_free_principal (context, princ);
223 ret = krb5_cc_retrieve_cred(context, id, 0, &mcred, &cred);
225 ret = krb524_convert_creds_kdc_ccache(context, id, &cred, &c);
227 snprintf(krb4tkfile,sizeof(krb4tkfile),"%s%d",TKT_ROOT,
229 krb_set_tkt_string(krb4tkfile);
230 tf_setup(&c, c.pname, c.pinst);
232 memset(&c, 0, sizeof(c));
233 krb5_free_creds_contents(context, &cred);
235 krb5_free_principal(context, mcred.server);
242 krb5_start_session (const struct passwd *pwd)
247 /* copy credentials to file cache */
248 snprintf(residual, sizeof(residual), "FILE:/tmp/krb5cc_%u",
249 (unsigned)pwd->pw_uid);
250 krb5_cc_resolve(context, residual, &id2);
251 ret = krb5_cc_copy_cache(context, id, id2);
253 add_env("KRB5CCNAME", residual);
255 krb5_cc_destroy (context, id2);
261 krb5_cc_close(context, id2);
262 krb5_cc_destroy(context, id);
269 krb5_free_context(context);
275 krb5_get_afs_tokens (const struct passwd *pwd)
284 ret = krb5_cc_default(context, &id2);
287 pw_dir = pwd->pw_dir;
294 if(k_afs_cell_of_file(pw_dir, cell, sizeof(cell)) == 0)
295 krb5_afslog_uid_home (context, id2,
296 cell, NULL, pwd->pw_uid, pwd->pw_dir);
297 krb5_afslog_uid_home (context, id2, NULL, NULL,
298 pwd->pw_uid, pwd->pw_dir);
299 krb5_cc_close (context, id2);
310 krb4_verify(struct passwd *pwd, const char *password)
312 char lrealm[REALM_SZ];
314 char ticket_file[MaxPathLen];
316 ret = krb_get_lrealm (lrealm, 1);
320 snprintf (ticket_file, sizeof(ticket_file),
322 TKT_ROOT, (unsigned)pwd->pw_uid, (unsigned)getpid());
324 krb_set_tkt_string (ticket_file);
326 ret = krb_verify_user (pwd->pw_name, "", lrealm, (char *)password,
327 KRB_VERIFY_SECURE_FAIL, NULL);
331 if (chown (ticket_file, pwd->pw_uid, pwd->pw_gid) < 0) {
336 add_env ("KRBTKFILE", ticket_file);
341 krb4_get_afs_tokens (const struct passwd *pwd)
349 pw_dir = pwd->pw_dir;
356 if(k_afs_cell_of_file(pw_dir, cell, sizeof(cell)) == 0)
357 krb_afslog_uid_home (cell, NULL, pwd->pw_uid, pwd->pw_dir);
359 krb_afslog_uid_home (NULL, NULL, pwd->pw_uid, pwd->pw_dir);
369 static int version_flag;
370 static int help_flag;
371 static char *remote_host;
372 static char *auth_level = NULL;
374 struct getargs args[] = {
375 { NULL, 'a', arg_string, &auth_level, "authentication mode" },
379 { NULL, 'f', arg_flag, &f_flag, "pre-authenticated" },
380 { NULL, 'h', arg_string, &remote_host, "remote host", "hostname" },
381 { NULL, 'p', arg_flag, &p_flag, "don't purge environment" },
383 { NULL, 'r', arg_flag, &r_flag, "rlogin protocol" },
385 { "version", 0, arg_flag, &version_flag },
386 { "help", 0, arg_flag,&help_flag, }
389 int nargs = sizeof(args) / sizeof(args[0]);
392 update_utmp(const char *username, const char *hostname,
393 char *tty, char *ttyn)
396 * Update the utmp files, both BSD and SYSV style.
398 if (utmpx_login(tty, username, hostname) != 0 && !f_flag) {
399 printf("No utmpx entry. You must exec \"login\" from the "
400 "lowest level shell.\n");
403 utmp_login(ttyn, username, hostname);
412 f = fopen(_PATH_NOLOGIN, "r");
415 while(fgets(buf, sizeof(buf), f))
421 /* print contents of a file */
423 show_file(const char *file)
427 if((f = fopen(file, "r")) == NULL)
429 while (fgets(buf, sizeof(buf), f))
435 * Actually log in the user. `pwd' contains all the relevant
436 * information about the user. `ttyn' is the complete name of the tty
437 * and `tty' the short name.
441 do_login(const struct passwd *pwd, char *tty, char *ttyn)
446 int rootlogin = (pwd->pw_uid == 0);
449 const char *home_dir;
456 sp = getspnam(pwd->pw_name);
459 update_utmp(pwd->pw_name, remote_host ? remote_host : "",
462 gr = getgrnam ("tty");
464 tty_gid = gr->gr_gid;
466 tty_gid = pwd->pw_gid;
468 if (chown (ttyn, pwd->pw_uid, tty_gid) < 0) {
469 warn("chown %s", ttyn);
474 if (chmod (ttyn, S_IRUSR | S_IWUSR | S_IWGRP) < 0) {
475 warn("chmod %s", ttyn);
481 if(setlogin(pwd->pw_name)){
482 warn("setlogin(%s)", pwd->pw_name);
488 if (setpcred (pwd->pw_name, NULL) == -1)
489 warn("setpcred(%s)", pwd->pw_name);
490 #endif /* HAVE_SETPCRED */
491 #ifdef HAVE_INITGROUPS
492 if(initgroups(pwd->pw_name, pwd->pw_gid)){
493 warn("initgroups(%s, %u)", pwd->pw_name, (unsigned)pwd->pw_gid);
498 if(do_osfc2_magic(pwd->pw_uid))
500 if(setgid(pwd->pw_gid)){
501 warn("setgid(%u)", (unsigned)pwd->pw_gid);
505 if(setuid(pwd->pw_uid) || (pwd->pw_uid != 0 && setuid(0) == 0)) {
506 warn("setuid(%u)", (unsigned)pwd->pw_uid);
511 /* make sure signals are set to default actions, apparently some
512 OS:es like to ignore SIGINT, which is not very convenient */
514 for (i = 1; i < NSIG; ++i)
517 /* all kinds of different magic */
520 check_shadow(pwd, sp);
523 #if defined(HAVE_GETUDBNAM) && defined(HAVE_SETLIM)
527 const long maxcpu = 46116860184; /* some random constant */
528 udb = getudbnam(pwd->pw_name);
530 errx(1, "Failed to get UDB entry.");
531 t = udb->ue_pcpulim[UDBRC_INTER];
532 if(t == 0 || t > maxcpu)
535 t *= 100 * CLOCKS_PER_SEC;
537 if(limit(C_PROC, 0, L_CPU, t) < 0)
538 warn("limit C_PROC");
540 t = udb->ue_jcpulim[UDBRC_INTER];
541 if(t == 0 || t > maxcpu)
544 t *= 100 * CLOCKS_PER_SEC;
546 if(limit(C_JOBPROCS, 0, L_CPU, t) < 0)
547 warn("limit C_JOBPROCS");
549 nice(udb->ue_nice[UDBRC_INTER]);
552 #if defined(HAVE_SGI_GETCAPABILITYBYNAME) && defined(HAVE_CAP_SET_PROC)
553 /* XXX SGI capability hack IRIX 6.x (x >= 0?) has something
554 called capabilities, that allow you to give away
555 permissions (such as chown) to specific processes. From 6.5
556 this is default on, and the default capability set seems to
557 not always be the empty set. The problem is that the
558 runtime linker refuses to do just about anything if the
559 process has *any* capabilities set, so we have to remove
560 them here (unless otherwise instructed by /etc/capability).
561 In IRIX < 6.5, these functions was called sgi_cap_setproc,
562 etc, but we ignore this fact (it works anyway). */
564 struct user_cap *ucap = sgi_getcapabilitybyname(pwd->pw_name);
567 cap = cap_from_text("all=");
569 cap = cap_from_text(ucap->ca_default);
571 err(1, "cap_from_text");
572 if(cap_set_proc(cap) < 0)
573 err(1, "cap_set_proc");
578 home_dir = pwd->pw_dir;
579 if (chdir(home_dir) < 0) {
580 fprintf(stderr, "No home directory \"%s\"!\n", pwd->pw_dir);
584 fprintf(stderr, "Logging in with home = \"/\".\n");
587 if (auth == AUTH_KRB5) {
588 krb5_start_session (pwd);
591 else if (auth == 0) {
595 ret = krb5_cc_default (context, &id);
598 krb5_cc_close (context, id);
602 krb5_get_afs_tokens (pwd);
608 krb4_get_afs_tokens (pwd);
611 add_env("PATH", _PATH_DEFPATH);
614 const char *str = login_conf_get_string("environment");
615 char buf[MAXPATHLEN];
618 login_read_env(_PATH_ETC_ENVIRONMENT);
620 while(strsep_copy(&str, ",", buf, sizeof(buf)) != -1) {
628 const char *str = login_conf_get_string("motd");
629 char buf[MAXPATHLEN];
632 while(strsep_copy(&str, ",", buf, sizeof(buf)) != -1) {
639 add_env("HOME", home_dir);
640 add_env("USER", pwd->pw_name);
641 add_env("LOGNAME", pwd->pw_name);
642 add_env("SHELL", pwd->pw_shell);
643 exec_shell(pwd->pw_shell, rootlogin);
647 check_password(struct passwd *pwd, const char *password)
649 if(pwd->pw_passwd == NULL)
651 if(pwd->pw_passwd[0] == '\0'){
652 #ifdef ALLOW_NULL_PASSWORD
653 return password[0] != '\0';
658 if(strcmp(pwd->pw_passwd, crypt(password, pwd->pw_passwd)) == 0)
661 if(krb5_verify(pwd, password) == 0) {
667 if (krb4_verify (pwd, password) == 0) {
673 if (otp_verify (pwd, password) == 0) {
684 arg_printusage(args, nargs, NULL, "[username]");
692 fprintf(stderr, "Login timed out after %d seconds\n",
695 fprintf(stderr, "Login received signal, exiting\n");
700 main(int argc, char **argv)
711 setprogname(argv[0]);
717 ret = krb5_init_context(&context);
719 errx (1, "krb5_init_context failed: %d", ret);
723 openlog("login", LOG_ODELAY, LOG_AUTH);
725 if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv,
734 print_version (NULL);
739 errx(1, "only root may use login, use su");
741 /* Default tty settings. */
747 /* this set of variables is always preserved by BSD login */
749 add_env("TERM", getenv("TERM"));
751 add_env("TZ", getenv("TZ"));
755 if(strchr(*argv, '=') == NULL && strcmp(*argv, "-") != 0){
756 strlcpy (username, *argv, sizeof(username));
761 #if defined(DCE) && defined(AIX)
762 esetenv("AUTHSTATE", "DCE", 1);
765 /* XXX should we care about environment on the command line? */
767 memset(&sa, 0, sizeof(sa));
768 sa.sa_handler = sig_handler;
769 sigemptyset(&sa.sa_mask);
771 sigaction(SIGALRM, &sa, NULL);
772 alarm(login_timeout);
774 for(try = 0; try < max_tries; try++){
790 ret = read_string("login: ", username, sizeof(username), 1);
794 sig_handler(0); /* exit */
796 pwd = k_getpwnam(username);
797 #ifdef ALLOW_NULL_PASSWORD
798 if (pwd != NULL && (pwd->pw_passwd[0] == '\0')) {
806 if(auth_level && strcmp(auth_level, "otp") == 0 &&
807 otp_challenge(&otp_ctx, username,
808 otp_str, sizeof(otp_str)) == 0)
809 snprintf (prompt, sizeof(prompt), "%s's %s Password: ",
813 strncpy(prompt, "Password: ", sizeof(prompt));
816 ret = read_string(prompt, password, sizeof(password), 0);
827 fprintf(stderr, "Login incorrect.\n");
832 if(f_flag == 0 && check_password(pwd, password)){
833 fprintf(stderr, "Login incorrect.\n");
837 ttyn = ttyname(STDIN_FILENO);
839 snprintf(ttname, sizeof(ttname), "%s??", _PATH_TTY);
842 if (strncmp (ttyn, _PATH_DEV, strlen(_PATH_DEV)) == 0)
843 tty = ttyn + strlen(_PATH_DEV);
847 if (login_access (pwd, remote_host ? remote_host : tty) == 0) {
848 fprintf(stderr, "Permission denied\n");
850 syslog(LOG_NOTICE, "%s LOGIN REFUSED FROM %s",
851 pwd->pw_name, remote_host);
853 syslog(LOG_NOTICE, "%s LOGIN REFUSED ON %s",
858 do_login(pwd, tty, ttyn);