Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/heimdal/appl/login/login.c
96309 views
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
}
734
735