]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - crypto/heimdal/appl/login/login.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / crypto / heimdal / appl / login / login.c
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
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
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
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
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
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
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
202 krb5_finish (void)
203 {
204     krb5_free_context(context);
205 }
206
207 static void
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
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
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
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
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
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
547 usage(int status)
548 {
549     arg_printusage(args, nargs, NULL, "[username]");
550     exit(status);
551 }
552
553 static RETSIGTYPE
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
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 }