1 /*
2 * Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 *
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.
16 *
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.
20 *
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
31 * SUCH DAMAGE.
32 */
33
34 #include "login_locl.h"
35 #ifdef HAVE_CAPABILITY_H
36 #include <capability.h>
37 #endif
38 #ifdef HAVE_SYS_CAPABILITY_H
39 #include <sys/capability.h>
40 #endif
41 #ifdef HAVE_CRYPT_H
42 #include <crypt.h>
43 #endif
44
45 RCSID("$Id$");
46
47 static int login_timeout = 60;
48
49 static int
start_login_process(void)50 start_login_process(void)
51 {
52 char *prog, *argv0;
53 prog = login_conf_get_string("login_program");
54 if(prog == NULL)
55 return 0;
56 argv0 = strrchr(prog, '/');
57
58 if(argv0)
59 argv0++;
60 else
61 argv0 = prog;
62
63 return simple_execle(prog, argv0, NULL, env);
64 }
65
66 static int
start_logout_process(void)67 start_logout_process(void)
68 {
69 char *prog, *argv0;
70 pid_t pid;
71
72 prog = login_conf_get_string("logout_program");
73 if(prog == NULL)
74 return 0;
75 argv0 = strrchr(prog, '/');
76
77 if(argv0)
78 argv0++;
79 else
80 argv0 = prog;
81
82 pid = fork();
83 if(pid == 0) {
84 /* avoid getting signals sent to the shell */
85 setpgid(0, getpid());
86 return 0;
87 }
88 if(pid == -1)
89 err(1, "fork");
90 /* wait for the real login process to exit */
91 #ifdef HAVE_SETPROCTITLE
92 setproctitle("waitpid %d", pid);
93 #endif
94 while(1) {
95 int status;
96 int ret;
97 ret = waitpid(pid, &status, 0);
98 if(ret > 0) {
99 if(WIFEXITED(status) || WIFSIGNALED(status)) {
100 execle(prog, argv0, NULL, env);
101 err(1, "exec %s", prog);
102 }
103 } else if(ret < 0)
104 err(1, "waitpid");
105 }
106 }
107
108 static void
exec_shell(const char * shell,int fallback)109 exec_shell(const char *shell, int fallback)
110 {
111 char *sh;
112 const char *p;
113
114 extend_env(NULL);
115 if(start_login_process() < 0)
116 warn("login process");
117 start_logout_process();
118
119 p = strrchr(shell, '/');
120 if(p)
121 p++;
122 else
123 p = shell;
124 if (asprintf(&sh, "-%s", p) == -1)
125 errx(1, "Out of memory");
126 execle(shell, sh, NULL, env);
127 if(fallback){
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);
132 }
133 err(1, "%s", shell);
134 }
135
136 static enum { NONE = 0, AUTH_KRB5 = 2, AUTH_OTP = 3 } auth;
137
138 #ifdef OTP
139 static OtpContext otp_ctx;
140
141 static int
otp_verify(struct passwd * pwd,const char * password)142 otp_verify(struct passwd *pwd, const char *password)
143 {
144 return (otp_verify_user (&otp_ctx, password));
145 }
146 #endif /* OTP */
147
148
149 static int pag_set = 0;
150
151 #ifdef KRB5
152 static krb5_context context;
153 static krb5_ccache id, id2;
154
155 static int
krb5_verify(struct passwd * pwd,const char * password)156 krb5_verify(struct passwd *pwd, const char *password)
157 {
158 krb5_error_code ret;
159 krb5_principal princ;
160
161 ret = krb5_parse_name(context, pwd->pw_name, &princ);
162 if(ret)
163 return 1;
164 ret = krb5_cc_new_unique(context, krb5_cc_type_memory, NULL, &id);
165 if(ret) {
166 krb5_free_principal(context, princ);
167 return 1;
168 }
169 ret = krb5_verify_user_lrealm(context,
170 princ,
171 id,
172 password,
173 1,
174 NULL);
175 krb5_free_principal(context, princ);
176 return ret;
177 }
178
179 static int
krb5_start_session(const struct passwd * pwd)180 krb5_start_session (const struct passwd *pwd)
181 {
182 krb5_error_code ret;
183 char residual[64];
184
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);
190 if (ret == 0)
191 add_env("KRB5CCNAME", residual);
192 else {
193 krb5_cc_destroy (context, id2);
194 return ret;
195 }
196 krb5_cc_close(context, id2);
197 krb5_cc_destroy(context, id);
198 return 0;
199 }
200
201 static void
krb5_finish(void)202 krb5_finish (void)
203 {
204 krb5_free_context(context);
205 }
206
207 static void
krb5_get_afs_tokens(const struct passwd * pwd)208 krb5_get_afs_tokens (const struct passwd *pwd)
209 {
210 char cell[64];
211 char *pw_dir;
212 krb5_error_code ret;
213
214 if (!k_hasafs ())
215 return;
216
217 ret = krb5_cc_default(context, &id2);
218
219 if (ret == 0) {
220 pw_dir = pwd->pw_dir;
221
222 if (!pag_set) {
223 k_setpag();
224 pag_set = 1;
225 }
226
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);
233 }
234 }
235
236 #endif /* KRB5 */
237
238 static int f_flag;
239 static int p_flag;
240 #if 0
241 static int r_flag;
242 #endif
243 static int version_flag;
244 static int help_flag;
245 static char *remote_host;
246 static char *auth_level = NULL;
247
248 struct getargs args[] = {
249 { NULL, 'a', arg_string, &auth_level, "authentication mode" },
250 #if 0
251 { NULL, 'd' },
252 #endif
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" },
256 #if 0
257 { NULL, 'r', arg_flag, &r_flag, "rlogin protocol" },
258 #endif
259 { "version", 0, arg_flag, &version_flag },
260 { "help", 0, arg_flag,&help_flag, }
261 };
262
263 int nargs = sizeof(args) / sizeof(args[0]);
264
265 static void
update_utmp(const char * username,const char * hostname,char * tty,char * ttyn)266 update_utmp(const char *username, const char *hostname,
267 char *tty, char *ttyn)
268 {
269 /*
270 * Update the utmp files, both BSD and SYSV style.
271 */
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");
275 exit(1);
276 }
277 utmp_login(ttyn, username, hostname);
278 }
279
280 static void
checknologin(void)281 checknologin(void)
282 {
283 FILE *f;
284 char buf[1024];
285
286 f = fopen(_PATH_NOLOGIN, "r");
287 if(f == NULL)
288 return;
289 while(fgets(buf, sizeof(buf), f))
290 fputs(buf, stdout);
291 fclose(f);
292 exit(0);
293 }
294
295 /* print contents of a file */
296 static void
show_file(const char * file)297 show_file(const char *file)
298 {
299 FILE *f;
300 char buf[BUFSIZ];
301 if((f = fopen(file, "r")) == NULL)
302 return;
303 while (fgets(buf, sizeof(buf), f))
304 fputs(buf, stdout);
305 fclose(f);
306 }
307
308 /*
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.
312 */
313
314 static void
do_login(const struct passwd * pwd,char * tty,char * ttyn)315 do_login(const struct passwd *pwd, char *tty, char *ttyn)
316 {
317 #ifdef HAVE_GETSPNAM
318 struct spwd *sp;
319 #endif
320 int rootlogin = (pwd->pw_uid == 0);
321 gid_t tty_gid;
322 struct group *gr;
323 const char *home_dir;
324 int i;
325
326 if(!rootlogin)
327 checknologin();
328
329 #ifdef HAVE_GETSPNAM
330 sp = getspnam(pwd->pw_name);
331 #endif
332
333 update_utmp(pwd->pw_name, remote_host ? remote_host : "",
334 tty, ttyn);
335
336 gr = getgrnam ("tty");
337 if (gr != NULL)
338 tty_gid = gr->gr_gid;
339 else
340 tty_gid = pwd->pw_gid;
341
342 if (chown (ttyn, pwd->pw_uid, tty_gid) < 0) {
343 warn("chown %s", ttyn);
344 if (rootlogin == 0)
345 exit (1);
346 }
347
348 if (chmod (ttyn, S_IRUSR | S_IWUSR | S_IWGRP) < 0) {
349 warn("chmod %s", ttyn);
350 if (rootlogin == 0)
351 exit (1);
352 }
353
354 #ifdef HAVE_SETLOGIN
355 if(setlogin(pwd->pw_name)){
356 warn("setlogin(%s)", pwd->pw_name);
357 if(rootlogin == 0)
358 exit(1);
359 }
360 #endif
361 if(rootlogin == 0) {
362 const char *file = login_conf_get_string("limits");
363 if(file == NULL)
364 file = _PATH_LIMITS_CONF;
365
366 read_limits_conf(file, pwd);
367 }
368
369 #ifdef HAVE_SETPCRED
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);
376 if(rootlogin == 0)
377 exit(1);
378 }
379 #endif
380 if(do_osfc2_magic(pwd->pw_uid))
381 exit(1);
382 if(setgid(pwd->pw_gid)){
383 warn("setgid(%u)", (unsigned)pwd->pw_gid);
384 if(rootlogin == 0)
385 exit(1);
386 }
387 if(setuid(pwd->pw_uid) || (pwd->pw_uid != 0 && setuid(0) == 0)) {
388 warn("setuid(%u)", (unsigned)pwd->pw_uid);
389 if(rootlogin == 0)
390 exit(1);
391 }
392
393 /* make sure signals are set to default actions, apparently some
394 OS:es like to ignore SIGINT, which is not very convenient */
395
396 for (i = 1; i < NSIG; ++i)
397 signal(i, SIG_DFL);
398
399 /* all kinds of different magic */
400
401 #ifdef HAVE_GETSPNAM
402 check_shadow(pwd, sp);
403 #endif
404
405 #if defined(HAVE_GETUDBNAM) && defined(HAVE_SETLIM)
406 {
407 struct udb *udb;
408 long t;
409 const long maxcpu = 46116860184; /* some random constant */
410 udb = getudbnam(pwd->pw_name);
411 if(udb == UDB_NULL)
412 errx(1, "Failed to get UDB entry.");
413 t = udb->ue_pcpulim[UDBRC_INTER];
414 if(t == 0 || t > maxcpu)
415 t = CPUUNLIM;
416 else
417 t *= 100 * CLOCKS_PER_SEC;
418
419 if(limit(C_PROC, 0, L_CPU, t) < 0)
420 warn("limit C_PROC");
421
422 t = udb->ue_jcpulim[UDBRC_INTER];
423 if(t == 0 || t > maxcpu)
424 t = CPUUNLIM;
425 else
426 t *= 100 * CLOCKS_PER_SEC;
427
428 if(limit(C_JOBPROCS, 0, L_CPU, t) < 0)
429 warn("limit C_JOBPROCS");
430
431 nice(udb->ue_nice[UDBRC_INTER]);
432 }
433 #endif
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). */
445 {
446 struct user_cap *ucap = sgi_getcapabilitybyname(pwd->pw_name);
447 cap_t cap;
448 if(ucap == NULL)
449 cap = cap_from_text("all=");
450 else
451 cap = cap_from_text(ucap->ca_default);
452 if(cap == NULL)
453 err(1, "cap_from_text");
454 if(cap_set_proc(cap) < 0)
455 err(1, "cap_set_proc");
456 cap_free(cap);
457 free(ucap);
458 }
459 #endif
460 home_dir = pwd->pw_dir;
461 if (chdir(home_dir) < 0) {
462 fprintf(stderr, "No home directory \"%s\"!\n", pwd->pw_dir);
463 if (chdir("/"))
464 exit(0);
465 home_dir = "/";
466 fprintf(stderr, "Logging in with home = \"/\".\n");
467 }
468 #ifdef KRB5
469 if (auth == AUTH_KRB5) {
470 krb5_start_session (pwd);
471 }
472
473 krb5_get_afs_tokens (pwd);
474
475 krb5_finish ();
476 #endif /* KRB5 */
477
478 add_env("PATH", _PATH_DEFPATH);
479
480 {
481 const char *str = login_conf_get_string("environment");
482 char buf[MAXPATHLEN];
483
484 if(str == NULL) {
485 login_read_env(_PATH_ETC_ENVIRONMENT);
486 } else {
487 while(strsep_copy(&str, ",", buf, sizeof(buf)) != -1) {
488 if(buf[0] == '\0')
489 continue;
490 login_read_env(buf);
491 }
492 }
493 }
494 {
495 const char *str = login_conf_get_string("motd");
496 char buf[MAXPATHLEN];
497
498 if(str != NULL) {
499 while(strsep_copy(&str, ",", buf, sizeof(buf)) != -1) {
500 if(buf[0] == '\0')
501 continue;
502 show_file(buf);
503 }
504 } else {
505 str = login_conf_get_string("welcome");
506 if(str != NULL)
507 show_file(str);
508 }
509 }
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);
515 }
516
517 static int
check_password(struct passwd * pwd,const char * password)518 check_password(struct passwd *pwd, const char *password)
519 {
520 if(pwd->pw_passwd == NULL)
521 return 1;
522 if(pwd->pw_passwd[0] == '\0'){
523 #ifdef ALLOW_NULL_PASSWORD
524 return password[0] != '\0';
525 #else
526 return 1;
527 #endif
528 }
529 if(strcmp(pwd->pw_passwd, crypt(password, pwd->pw_passwd)) == 0)
530 return 0;
531 #ifdef KRB5
532 if(krb5_verify(pwd, password) == 0) {
533 auth = AUTH_KRB5;
534 return 0;
535 }
536 #endif
537 #ifdef OTP
538 if (otp_verify (pwd, password) == 0) {
539 auth = AUTH_OTP;
540 return 0;
541 }
542 #endif
543 return 1;
544 }
545
546 static void
usage(int status)547 usage(int status)
548 {
549 arg_printusage(args, nargs, NULL, "[username]");
550 exit(status);
551 }
552
553 static RETSIGTYPE
sig_handler(int sig)554 sig_handler(int sig)
555 {
556 if (sig == SIGALRM)
557 fprintf(stderr, "Login timed out after %d seconds\n",
558 login_timeout);
559 else
560 fprintf(stderr, "Login received signal, exiting\n");
561 exit(0);
562 }
563
564 int
main(int argc,char ** argv)565 main(int argc, char **argv)
566 {
567 int max_tries = 5;
568 int try;
569
570 char username[32];
571 int optidx = 0;
572
573 int ask = 1;
574 struct sigaction sa;
575
576 setprogname(argv[0]);
577
578 #ifdef KRB5
579 {
580 krb5_error_code ret;
581
582 ret = krb5_init_context(&context);
583 if (ret)
584 errx (1, "krb5_init_context failed: %d", ret);
585 }
586 #endif
587
588 openlog("login", LOG_ODELAY | LOG_PID, LOG_AUTH);
589
590 if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv,
591 &optidx))
592 usage (1);
593 argc -= optidx;
594 argv += optidx;
595
596 if(help_flag)
597 usage(0);
598 if (version_flag) {
599 print_version (NULL);
600 return 0;
601 }
602
603 if (geteuid() != 0)
604 errx(1, "only root may use login, use su");
605
606 /* Default tty settings. */
607 stty_default();
608
609 if(p_flag)
610 copy_env();
611 else {
612 /* this set of variables is always preserved by BSD login */
613 if(getenv("TERM"))
614 add_env("TERM", getenv("TERM"));
615 if(getenv("TZ"))
616 add_env("TZ", getenv("TZ"));
617 }
618
619 if(*argv){
620 if(strchr(*argv, '=') == NULL && strcmp(*argv, "-") != 0){
621 strlcpy (username, *argv, sizeof(username));
622 ask = 0;
623 }
624 }
625
626 #if defined(DCE) && defined(AIX)
627 esetenv("AUTHSTATE", "DCE", 1);
628 #endif
629
630 /* XXX should we care about environment on the command line? */
631
632 memset(&sa, 0, sizeof(sa));
633 sa.sa_handler = sig_handler;
634 sigemptyset(&sa.sa_mask);
635 sa.sa_flags = 0;
636 sigaction(SIGALRM, &sa, NULL);
637 alarm(login_timeout);
638
639 for(try = 0; try < max_tries; try++){
640 struct passwd *pwd;
641 char password[128];
642 int ret;
643 char ttname[32];
644 char *tty, *ttyn;
645 char prompt[128];
646 #ifdef OTP
647 char otp_str[256];
648 #endif
649
650 if(ask){
651 f_flag = 0;
652 #if 0
653 r_flag = 0;
654 #endif
655 ret = read_string("login: ", username, sizeof(username), 1);
656 if(ret == -3)
657 exit(0);
658 if(ret == -2)
659 sig_handler(0); /* exit */
660 }
661 pwd = k_getpwnam(username);
662 #ifdef ALLOW_NULL_PASSWORD
663 if (pwd != NULL && (pwd->pw_passwd[0] == '\0')) {
664 strcpy(password,"");
665 }
666 else
667 #endif
668
669 {
670 #ifdef OTP
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: ",
675 username, otp_str);
676 else
677 #endif
678 strncpy(prompt, "Password: ", sizeof(prompt));
679
680 if (f_flag == 0) {
681 ret = read_string(prompt, password, sizeof(password), 0);
682 if (ret == -3) {
683 ask = 1;
684 continue;
685 }
686 if (ret == -2)
687 sig_handler(0);
688 }
689 }
690
691 if(pwd == NULL){
692 fprintf(stderr, "Login incorrect.\n");
693 ask = 1;
694 continue;
695 }
696
697 if(f_flag == 0 && check_password(pwd, password)){
698 fprintf(stderr, "Login incorrect.\n");
699 ask = 1;
700 continue;
701 }
702 ttyn = ttyname(STDIN_FILENO);
703 if(ttyn == NULL){
704 snprintf(ttname, sizeof(ttname), "%s??", _PATH_TTY);
705 ttyn = ttname;
706 }
707 if (strncmp (ttyn, _PATH_DEV, strlen(_PATH_DEV)) == 0)
708 tty = ttyn + strlen(_PATH_DEV);
709 else
710 tty = ttyn;
711
712 if (login_access (pwd, remote_host ? remote_host : tty) == 0) {
713 fprintf(stderr, "Permission denied\n");
714 if (remote_host)
715 syslog(LOG_NOTICE, "%s LOGIN REFUSED FROM %s",
716 pwd->pw_name, remote_host);
717 else
718 syslog(LOG_NOTICE, "%s LOGIN REFUSED ON %s",
719 pwd->pw_name, tty);
720 exit (1);
721 } else {
722 if (remote_host)
723 syslog(LOG_NOTICE, "%s LOGIN ACCEPTED FROM %s ppid=%d",
724 pwd->pw_name, remote_host, (int) getppid());
725 else
726 syslog(LOG_NOTICE, "%s LOGIN ACCEPTED ON %s ppid=%d",
727 pwd->pw_name, tty, (int) getppid());
728 }
729 alarm(0);
730 do_login(pwd, tty, ttyn);
731 }
732 exit(1);
733 }
734