login.c revision 1.51 1 1.51 mjl /* $NetBSD: login.c,v 1.51 2000/01/13 12:43:20 mjl Exp $ */
2 1.12 jtc
3 1.1 cgd /*-
4 1.12 jtc * Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994
5 1.10 brezak * The Regents of the University of California. All rights reserved.
6 1.1 cgd *
7 1.1 cgd * Redistribution and use in source and binary forms, with or without
8 1.1 cgd * modification, are permitted provided that the following conditions
9 1.1 cgd * are met:
10 1.1 cgd * 1. Redistributions of source code must retain the above copyright
11 1.1 cgd * notice, this list of conditions and the following disclaimer.
12 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright
13 1.1 cgd * notice, this list of conditions and the following disclaimer in the
14 1.1 cgd * documentation and/or other materials provided with the distribution.
15 1.1 cgd * 3. All advertising materials mentioning features or use of this software
16 1.1 cgd * must display the following acknowledgement:
17 1.1 cgd * This product includes software developed by the University of
18 1.1 cgd * California, Berkeley and its contributors.
19 1.1 cgd * 4. Neither the name of the University nor the names of its contributors
20 1.1 cgd * may be used to endorse or promote products derived from this software
21 1.1 cgd * without specific prior written permission.
22 1.1 cgd *
23 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 1.1 cgd * SUCH DAMAGE.
34 1.1 cgd */
35 1.1 cgd
36 1.24 lukem #include <sys/cdefs.h>
37 1.1 cgd #ifndef lint
38 1.24 lukem __COPYRIGHT(
39 1.12 jtc "@(#) Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994\n\
40 1.24 lukem The Regents of the University of California. All rights reserved.\n");
41 1.1 cgd #endif /* not lint */
42 1.1 cgd
43 1.1 cgd #ifndef lint
44 1.12 jtc #if 0
45 1.12 jtc static char sccsid[] = "@(#)login.c 8.4 (Berkeley) 4/2/94";
46 1.12 jtc #endif
47 1.51 mjl __RCSID("$NetBSD: login.c,v 1.51 2000/01/13 12:43:20 mjl Exp $");
48 1.1 cgd #endif /* not lint */
49 1.1 cgd
50 1.1 cgd /*
51 1.1 cgd * login [ name ]
52 1.1 cgd * login -h hostname (for telnetd, etc.)
53 1.1 cgd * login -f name (for pre-authenticated login: datakit, xterm, etc.)
54 1.1 cgd */
55 1.1 cgd
56 1.1 cgd #include <sys/param.h>
57 1.1 cgd #include <sys/stat.h>
58 1.1 cgd #include <sys/time.h>
59 1.1 cgd #include <sys/resource.h>
60 1.1 cgd #include <sys/file.h>
61 1.38 mrg #include <sys/wait.h>
62 1.1 cgd
63 1.12 jtc #include <err.h>
64 1.5 cgd #include <errno.h>
65 1.1 cgd #include <grp.h>
66 1.1 cgd #include <pwd.h>
67 1.12 jtc #include <setjmp.h>
68 1.12 jtc #include <signal.h>
69 1.19 veego #include <stdio.h>
70 1.5 cgd #include <stdlib.h>
71 1.1 cgd #include <string.h>
72 1.12 jtc #include <syslog.h>
73 1.36 kleink #include <time.h>
74 1.12 jtc #include <ttyent.h>
75 1.12 jtc #include <tzfile.h>
76 1.12 jtc #include <unistd.h>
77 1.12 jtc #include <utmp.h>
78 1.13 jtc #include <util.h>
79 1.29 mycroft #ifdef SKEY
80 1.29 mycroft #include <skey.h>
81 1.29 mycroft #endif
82 1.25 mycroft #ifdef KERBEROS5
83 1.45 christos #include <krb5/krb5.h>
84 1.45 christos #include <kerberosIV/com_err.h>
85 1.25 mycroft #endif
86 1.48 mjl #ifdef LOGIN_CAP
87 1.48 mjl #include <login_cap.h>
88 1.48 mjl #endif
89 1.25 mycroft
90 1.1 cgd #include "pathnames.h"
91 1.1 cgd
92 1.5 cgd void badlogin __P((char *));
93 1.48 mjl void checknologin __P((char *));
94 1.5 cgd void dolastlog __P((int));
95 1.5 cgd void getloginname __P((void));
96 1.24 lukem int main __P((int, char *[]));
97 1.48 mjl void motd __P((char *));
98 1.5 cgd int rootterm __P((char *));
99 1.5 cgd void sigint __P((int));
100 1.5 cgd void sleepexit __P((int));
101 1.39 mycroft const char *stypeof __P((const char *));
102 1.5 cgd void timedout __P((int));
103 1.10 brezak #if defined(KERBEROS) || defined(KERBEROS5)
104 1.5 cgd int klogin __P((struct passwd *, char *, char *, char *));
105 1.12 jtc void kdestroy __P((void));
106 1.12 jtc void dofork __P((void));
107 1.5 cgd #endif
108 1.44 aidan #ifdef KERBEROS5
109 1.44 aidan int k5_read_creds __P((char*));
110 1.44 aidan int k5_write_creds __P((void));
111 1.44 aidan #endif
112 1.12 jtc
113 1.1 cgd #define TTYGRPNAME "tty" /* name of group to own ttys */
114 1.1 cgd
115 1.50 mjl #define DEFAULT_BACKOFF 3
116 1.50 mjl #define DEFAULT_RETRIES 10
117 1.48 mjl
118 1.1 cgd /*
119 1.1 cgd * This bounds the time given to login. Not a define so it can
120 1.1 cgd * be patched on machines where it's too small.
121 1.1 cgd */
122 1.12 jtc u_int timeout = 300;
123 1.5 cgd
124 1.10 brezak #if defined(KERBEROS) || defined(KERBEROS5)
125 1.1 cgd int notickets = 1;
126 1.1 cgd char *instance;
127 1.12 jtc char *krbtkfile_env;
128 1.1 cgd #endif
129 1.25 mycroft #ifdef KERBEROS5
130 1.25 mycroft extern krb5_context kcontext;
131 1.44 aidan extern int have_forward;
132 1.46 aidan extern int use_krb5;
133 1.25 mycroft #endif
134 1.1 cgd
135 1.1 cgd struct passwd *pwd;
136 1.1 cgd int failures;
137 1.1 cgd char term[64], *envinit[1], *hostname, *username, *tty;
138 1.1 cgd
139 1.34 thorpej static const char copyrightstr[] = "\
140 1.47 billc Copyright (c) 1996, 1997, 1998, 1999, 2000
141 1.34 thorpej \tThe NetBSD Foundation, Inc. All rights reserved.
142 1.34 thorpej Copyright (c) 1980, 1983, 1986, 1988, 1990, 1991, 1993, 1994
143 1.34 thorpej \tThe Regents of the University of California. All rights reserved.\n\n";
144 1.34 thorpej
145 1.5 cgd int
146 1.1 cgd main(argc, argv)
147 1.1 cgd int argc;
148 1.5 cgd char *argv[];
149 1.1 cgd {
150 1.5 cgd extern char **environ;
151 1.5 cgd struct group *gr;
152 1.5 cgd struct stat st;
153 1.5 cgd struct timeval tp;
154 1.5 cgd struct utmp utmp;
155 1.29 mycroft int ask, ch, cnt, fflag, hflag, pflag, sflag, quietlog, rootlogin, rval;
156 1.44 aidan int Fflag;
157 1.33 hubertf uid_t uid, saved_uid;
158 1.35 hubertf gid_t saved_gid, saved_gids[NGROUPS_MAX];
159 1.35 hubertf int nsaved_gids;
160 1.39 mycroft char *domain, *p, *ttyn, *pwprompt;
161 1.39 mycroft const char *salt;
162 1.1 cgd char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_TTY) + 10];
163 1.37 mrg char localhost[MAXHOSTNAMELEN + 1];
164 1.29 mycroft int need_chpass, require_chpass;
165 1.48 mjl int login_retries = DEFAULT_RETRIES,
166 1.48 mjl login_backoff = DEFAULT_BACKOFF;
167 1.48 mjl time_t pw_warntime = _PASSWORD_WARNDAYS * SECSPERDAY;
168 1.25 mycroft #ifdef KERBEROS5
169 1.25 mycroft krb5_error_code kerror;
170 1.25 mycroft #endif
171 1.48 mjl #ifdef LOGIN_CAP
172 1.49 mjl char *shell = NULL;
173 1.48 mjl login_cap_t *lc = NULL;
174 1.48 mjl #endif
175 1.1 cgd
176 1.20 lukem tbuf[0] = '\0';
177 1.20 lukem rval = 0;
178 1.20 lukem pwprompt = NULL;
179 1.29 mycroft need_chpass = require_chpass = 0;
180 1.20 lukem
181 1.1 cgd (void)signal(SIGALRM, timedout);
182 1.12 jtc (void)alarm(timeout);
183 1.1 cgd (void)signal(SIGQUIT, SIG_IGN);
184 1.1 cgd (void)signal(SIGINT, SIG_IGN);
185 1.1 cgd (void)setpriority(PRIO_PROCESS, 0, 0);
186 1.1 cgd
187 1.1 cgd openlog("login", LOG_ODELAY, LOG_AUTH);
188 1.1 cgd
189 1.1 cgd /*
190 1.1 cgd * -p is used by getty to tell login not to destroy the environment
191 1.5 cgd * -f is used to skip a second login authentication
192 1.1 cgd * -h is used by other servers to pass the name of the remote
193 1.1 cgd * host to login so that it may be placed in utmp and wtmp
194 1.20 lukem * -s is used to force use of S/Key or equivalent.
195 1.1 cgd */
196 1.1 cgd domain = NULL;
197 1.1 cgd if (gethostname(localhost, sizeof(localhost)) < 0)
198 1.1 cgd syslog(LOG_ERR, "couldn't get local hostname: %m");
199 1.1 cgd else
200 1.12 jtc domain = strchr(localhost, '.');
201 1.37 mrg localhost[sizeof(localhost) - 1] = '\0';
202 1.1 cgd
203 1.44 aidan Fflag = fflag = hflag = pflag = sflag = 0;
204 1.44 aidan #ifdef KERBEROS5
205 1.44 aidan have_forward = 0;
206 1.46 aidan use_krb5 = 1;
207 1.44 aidan #endif
208 1.1 cgd uid = getuid();
209 1.44 aidan while ((ch = getopt(argc, argv, "Ffh:ps")) != -1)
210 1.1 cgd switch (ch) {
211 1.44 aidan case 'F':
212 1.44 aidan Fflag = 1;
213 1.44 aidan /* FALLTHROUGH */
214 1.1 cgd case 'f':
215 1.1 cgd fflag = 1;
216 1.1 cgd break;
217 1.1 cgd case 'h':
218 1.12 jtc if (uid)
219 1.12 jtc errx(1, "-h option: %s", strerror(EPERM));
220 1.1 cgd hflag = 1;
221 1.23 mikel if (domain && (p = strchr(optarg, '.')) != NULL &&
222 1.1 cgd strcasecmp(p, domain) == 0)
223 1.1 cgd *p = 0;
224 1.1 cgd hostname = optarg;
225 1.1 cgd break;
226 1.1 cgd case 'p':
227 1.1 cgd pflag = 1;
228 1.1 cgd break;
229 1.20 lukem case 's':
230 1.29 mycroft sflag = 1;
231 1.20 lukem break;
232 1.29 mycroft default:
233 1.1 cgd case '?':
234 1.1 cgd (void)fprintf(stderr,
235 1.29 mycroft "usage: login [-fps] [-h hostname] [username]\n");
236 1.1 cgd exit(1);
237 1.1 cgd }
238 1.1 cgd argc -= optind;
239 1.1 cgd argv += optind;
240 1.5 cgd
241 1.1 cgd if (*argv) {
242 1.1 cgd username = *argv;
243 1.1 cgd ask = 0;
244 1.1 cgd } else
245 1.1 cgd ask = 1;
246 1.1 cgd
247 1.1 cgd for (cnt = getdtablesize(); cnt > 2; cnt--)
248 1.5 cgd (void)close(cnt);
249 1.1 cgd
250 1.5 cgd ttyn = ttyname(STDIN_FILENO);
251 1.1 cgd if (ttyn == NULL || *ttyn == '\0') {
252 1.5 cgd (void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY);
253 1.1 cgd ttyn = tname;
254 1.1 cgd }
255 1.23 mikel if ((tty = strrchr(ttyn, '/')) != NULL)
256 1.1 cgd ++tty;
257 1.1 cgd else
258 1.1 cgd tty = ttyn;
259 1.25 mycroft
260 1.48 mjl #ifdef LOGIN_CAP
261 1.48 mjl /* Get "login-retries" and "login-backoff" from default class */
262 1.48 mjl if((lc = login_getclass(NULL)))
263 1.48 mjl {
264 1.48 mjl login_retries = (int) login_getcapnum(lc, "login-retries",
265 1.48 mjl DEFAULT_RETRIES, DEFAULT_RETRIES);
266 1.48 mjl login_backoff = (int) login_getcapnum(lc, "login-backoff",
267 1.48 mjl DEFAULT_BACKOFF, DEFAULT_BACKOFF);
268 1.48 mjl login_close(lc);
269 1.48 mjl lc = NULL;
270 1.48 mjl }
271 1.48 mjl #endif
272 1.48 mjl
273 1.25 mycroft #ifdef KERBEROS5
274 1.25 mycroft kerror = krb5_init_context(&kcontext);
275 1.25 mycroft if (kerror) {
276 1.25 mycroft syslog(LOG_NOTICE, "%s when initializing Kerberos context",
277 1.25 mycroft error_message(kerror));
278 1.46 aidan use_krb5 = 0;
279 1.25 mycroft }
280 1.25 mycroft #endif KERBEROS5
281 1.1 cgd
282 1.1 cgd for (cnt = 0;; ask = 1) {
283 1.10 brezak #if defined(KERBEROS) || defined(KERBEROS5)
284 1.10 brezak kdestroy();
285 1.10 brezak #endif
286 1.1 cgd if (ask) {
287 1.1 cgd fflag = 0;
288 1.1 cgd getloginname();
289 1.1 cgd }
290 1.5 cgd rootlogin = 0;
291 1.29 mycroft #ifdef KERBEROS
292 1.29 mycroft if ((instance = strchr(username, '.')) != NULL)
293 1.1 cgd *instance++ = '\0';
294 1.29 mycroft else
295 1.1 cgd instance = "";
296 1.1 cgd #endif
297 1.10 brezak #ifdef KERBEROS5
298 1.29 mycroft if ((instance = strchr(username, '/')) != NULL)
299 1.10 brezak *instance++ = '\0';
300 1.29 mycroft else
301 1.10 brezak instance = "";
302 1.10 brezak #endif
303 1.16 sommerfe if (strlen(username) > MAXLOGNAME)
304 1.16 sommerfe username[MAXLOGNAME] = '\0';
305 1.1 cgd
306 1.1 cgd /*
307 1.1 cgd * Note if trying multiple user names; log failures for
308 1.1 cgd * previous user name, but don't bother logging one failure
309 1.1 cgd * for nonexistent name (mistyped username).
310 1.1 cgd */
311 1.1 cgd if (failures && strcmp(tbuf, username)) {
312 1.1 cgd if (failures > (pwd ? 0 : 1))
313 1.1 cgd badlogin(tbuf);
314 1.1 cgd failures = 0;
315 1.1 cgd }
316 1.17 mrg (void)strncpy(tbuf, username, sizeof(tbuf) - 1);
317 1.38 mrg tbuf[sizeof(tbuf) - 1] = '\0';
318 1.1 cgd
319 1.23 mikel if ((pwd = getpwnam(username)) != NULL)
320 1.1 cgd salt = pwd->pw_passwd;
321 1.1 cgd else
322 1.1 cgd salt = "xx";
323 1.1 cgd
324 1.48 mjl #ifdef LOGIN_CAP
325 1.48 mjl /*
326 1.48 mjl * Establish the class now, before we might goto
327 1.48 mjl * within the next block. pwd can be NULL since it
328 1.48 mjl * falls back to the "default" class if it is.
329 1.48 mjl */
330 1.48 mjl if (pwd != NULL)
331 1.48 mjl lc = login_getclass(pwd->pw_class);
332 1.48 mjl #endif
333 1.1 cgd /*
334 1.1 cgd * if we have a valid account name, and it doesn't have a
335 1.1 cgd * password, or the -f option was specified and the caller
336 1.1 cgd * is root or the caller isn't changing their uid, don't
337 1.1 cgd * authenticate.
338 1.1 cgd */
339 1.7 mycroft if (pwd) {
340 1.7 mycroft if (pwd->pw_uid == 0)
341 1.7 mycroft rootlogin = 1;
342 1.7 mycroft
343 1.8 mycroft if (fflag && (uid == 0 || uid == pwd->pw_uid)) {
344 1.7 mycroft /* already authenticated */
345 1.44 aidan #ifdef KERBEROS5
346 1.44 aidan if (Fflag)
347 1.44 aidan k5_read_creds(username);
348 1.44 aidan #endif
349 1.7 mycroft break;
350 1.7 mycroft } else if (pwd->pw_passwd[0] == '\0') {
351 1.7 mycroft /* pretend password okay */
352 1.7 mycroft rval = 0;
353 1.7 mycroft goto ttycheck;
354 1.7 mycroft }
355 1.7 mycroft }
356 1.7 mycroft
357 1.1 cgd fflag = 0;
358 1.1 cgd
359 1.1 cgd (void)setpriority(PRIO_PROCESS, 0, -4);
360 1.1 cgd
361 1.27 mycroft #ifdef SKEY
362 1.29 mycroft if (skey_haskey(username) == 0) {
363 1.29 mycroft static char skprompt[80];
364 1.29 mycroft char *skinfo = skey_keyinfo(username);
365 1.20 lukem
366 1.29 mycroft (void)snprintf(skprompt, sizeof(skprompt)-1,
367 1.29 mycroft "Password [%s]:",
368 1.29 mycroft skinfo ? skinfo : "error getting challenge");
369 1.29 mycroft pwprompt = skprompt;
370 1.29 mycroft } else
371 1.27 mycroft #endif
372 1.29 mycroft pwprompt = "Password:";
373 1.27 mycroft
374 1.29 mycroft p = getpass(pwprompt);
375 1.1 cgd
376 1.29 mycroft if (pwd == NULL) {
377 1.29 mycroft rval = 1;
378 1.29 mycroft goto skip;
379 1.29 mycroft }
380 1.29 mycroft #ifdef KERBEROS
381 1.29 mycroft if (klogin(pwd, instance, localhost, p) == 0) {
382 1.29 mycroft rval = 0;
383 1.29 mycroft goto skip;
384 1.29 mycroft }
385 1.1 cgd #endif
386 1.29 mycroft #ifdef KERBEROS5
387 1.29 mycroft if (klogin(pwd, instance, localhost, p) == 0) {
388 1.29 mycroft rval = 0;
389 1.29 mycroft goto skip;
390 1.1 cgd }
391 1.29 mycroft #endif
392 1.20 lukem #ifdef SKEY
393 1.29 mycroft if (skey_haskey(username) == 0 &&
394 1.29 mycroft skey_passcheck(username, p) != -1) {
395 1.29 mycroft rval = 0;
396 1.29 mycroft goto skip;
397 1.29 mycroft }
398 1.20 lukem #endif
399 1.29 mycroft if (!sflag && *pwd->pw_passwd != '\0' &&
400 1.29 mycroft !strcmp(crypt(p, pwd->pw_passwd), pwd->pw_passwd)) {
401 1.29 mycroft rval = 0;
402 1.29 mycroft require_chpass = 1;
403 1.29 mycroft goto skip;
404 1.29 mycroft }
405 1.29 mycroft rval = 1;
406 1.29 mycroft
407 1.29 mycroft skip:
408 1.29 mycroft memset(p, 0, strlen(p));
409 1.1 cgd
410 1.1 cgd (void)setpriority(PRIO_PROCESS, 0, 0);
411 1.1 cgd
412 1.7 mycroft ttycheck:
413 1.1 cgd /*
414 1.1 cgd * If trying to log in as root without Kerberos,
415 1.1 cgd * but with insecure terminal, refuse the login attempt.
416 1.1 cgd */
417 1.7 mycroft if (pwd && !rval && rootlogin && !rootterm(tty)) {
418 1.1 cgd (void)fprintf(stderr,
419 1.1 cgd "%s login refused on this terminal.\n",
420 1.1 cgd pwd->pw_name);
421 1.1 cgd if (hostname)
422 1.1 cgd syslog(LOG_NOTICE,
423 1.1 cgd "LOGIN %s REFUSED FROM %s ON TTY %s",
424 1.1 cgd pwd->pw_name, hostname, tty);
425 1.1 cgd else
426 1.1 cgd syslog(LOG_NOTICE,
427 1.1 cgd "LOGIN %s REFUSED ON TTY %s",
428 1.1 cgd pwd->pw_name, tty);
429 1.1 cgd continue;
430 1.1 cgd }
431 1.1 cgd
432 1.1 cgd if (pwd && !rval)
433 1.1 cgd break;
434 1.1 cgd
435 1.1 cgd (void)printf("Login incorrect\n");
436 1.1 cgd failures++;
437 1.48 mjl cnt++;
438 1.1 cgd /* we allow 10 tries, but after 3 we start backing off */
439 1.50 mjl if (cnt > login_backoff) {
440 1.48 mjl if (cnt >= login_retries) {
441 1.1 cgd badlogin(username);
442 1.1 cgd sleepexit(1);
443 1.1 cgd }
444 1.1 cgd sleep((u_int)((cnt - 3) * 5));
445 1.1 cgd }
446 1.1 cgd }
447 1.1 cgd
448 1.1 cgd /* committed to login -- turn off timeout */
449 1.1 cgd (void)alarm((u_int)0);
450 1.1 cgd
451 1.1 cgd endpwent();
452 1.1 cgd
453 1.1 cgd /* if user not super-user, check for disabled logins */
454 1.48 mjl #ifdef LOGIN_CAP
455 1.48 mjl if (!rootlogin || login_getcapbool(lc, "ignorenologin", 0)) {
456 1.48 mjl char *fname;
457 1.48 mjl
458 1.48 mjl fname = login_getcapstr(lc, "nologin", NULL, NULL);
459 1.48 mjl checknologin(fname);
460 1.48 mjl }
461 1.48 mjl #else
462 1.48 mjl if (!rootlogin)
463 1.48 mjl checknologin(NULL);
464 1.48 mjl #endif
465 1.1 cgd
466 1.48 mjl #ifdef LOGIN_CAP
467 1.48 mjl quietlog = login_getcapbool(lc, "hushlogin", 0);
468 1.48 mjl #else
469 1.48 mjl quietlog = 0;
470 1.48 mjl #endif
471 1.33 hubertf /* Temporarily give up special privileges so we can change */
472 1.33 hubertf /* into NFS-mounted homes that are exported for non-root */
473 1.33 hubertf /* access and have mode 7x0 */
474 1.33 hubertf saved_uid = geteuid();
475 1.35 hubertf saved_gid = getegid();
476 1.35 hubertf nsaved_gids = getgroups(NGROUPS_MAX, saved_gids);
477 1.35 hubertf
478 1.35 hubertf (void)setegid(pwd->pw_gid);
479 1.35 hubertf initgroups(username, pwd->pw_gid);
480 1.35 hubertf (void)seteuid(pwd->pw_uid);
481 1.33 hubertf
482 1.1 cgd if (chdir(pwd->pw_dir) < 0) {
483 1.48 mjl #ifdef LOGIN_CAP
484 1.48 mjl if (login_getcapbool(lc, "requirehome", 0)) {
485 1.48 mjl (void) printf("Home directory %s required\n", pwd->pw_dir);
486 1.48 mjl sleepexit(1);
487 1.48 mjl }
488 1.48 mjl #endif
489 1.1 cgd (void)printf("No home directory %s!\n", pwd->pw_dir);
490 1.1 cgd if (chdir("/"))
491 1.1 cgd exit(0);
492 1.1 cgd pwd->pw_dir = "/";
493 1.1 cgd (void)printf("Logging in with home = \"/\".\n");
494 1.1 cgd }
495 1.1 cgd
496 1.48 mjl if(!quietlog)
497 1.48 mjl quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0;
498 1.33 hubertf
499 1.33 hubertf /* regain special privileges */
500 1.33 hubertf (void)seteuid(saved_uid);
501 1.35 hubertf setgroups(nsaved_gids, saved_gids);
502 1.35 hubertf (void)setegid(saved_gid);
503 1.1 cgd
504 1.48 mjl #ifdef LOGIN_CAP
505 1.48 mjl pw_warntime = login_getcaptime(lc, "password-warn",
506 1.48 mjl _PASSWORD_WARNDAYS * SECSPERDAY,
507 1.48 mjl _PASSWORD_WARNDAYS * SECSPERDAY);
508 1.48 mjl #endif
509 1.48 mjl
510 1.1 cgd if (pwd->pw_change || pwd->pw_expire)
511 1.1 cgd (void)gettimeofday(&tp, (struct timezone *)NULL);
512 1.40 ross if (pwd->pw_expire) {
513 1.1 cgd if (tp.tv_sec >= pwd->pw_expire) {
514 1.1 cgd (void)printf("Sorry -- your account has expired.\n");
515 1.1 cgd sleepexit(1);
516 1.48 mjl } else if (pwd->pw_expire - tp.tv_sec < pw_warntime &&
517 1.48 mjl !quietlog)
518 1.1 cgd (void)printf("Warning: your account expires on %s",
519 1.1 cgd ctime(&pwd->pw_expire));
520 1.40 ross }
521 1.40 ross if (pwd->pw_change) {
522 1.30 mycroft if (pwd->pw_change == _PASSWORD_CHGNOW)
523 1.30 mycroft need_chpass = 1;
524 1.31 mycroft else if (tp.tv_sec >= pwd->pw_change) {
525 1.31 mycroft (void)printf("Sorry -- your password has expired.\n");
526 1.31 mycroft sleepexit(1);
527 1.48 mjl } else if (pwd->pw_change - tp.tv_sec < pw_warntime &&
528 1.48 mjl !quietlog)
529 1.30 mycroft (void)printf("Warning: your password expires on %s",
530 1.30 mycroft ctime(&pwd->pw_change));
531 1.1 cgd
532 1.40 ross }
533 1.5 cgd /* Nothing else left to fail -- really log in. */
534 1.12 jtc memset((void *)&utmp, 0, sizeof(utmp));
535 1.5 cgd (void)time(&utmp.ut_time);
536 1.5 cgd (void)strncpy(utmp.ut_name, username, sizeof(utmp.ut_name));
537 1.5 cgd if (hostname)
538 1.5 cgd (void)strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host));
539 1.5 cgd (void)strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line));
540 1.5 cgd login(&utmp);
541 1.1 cgd
542 1.1 cgd dolastlog(quietlog);
543 1.1 cgd
544 1.1 cgd (void)chown(ttyn, pwd->pw_uid,
545 1.1 cgd (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid);
546 1.15 gwr
547 1.15 gwr if (ttyaction(ttyn, "login", pwd->pw_name))
548 1.15 gwr (void)printf("Warning: ttyaction failed.\n");
549 1.15 gwr
550 1.10 brezak #if defined(KERBEROS) || defined(KERBEROS5)
551 1.10 brezak /* Fork so that we can call kdestroy */
552 1.10 brezak if (krbtkfile_env)
553 1.29 mycroft dofork();
554 1.10 brezak #endif
555 1.48 mjl #ifdef LOGIN_CAP
556 1.50 mjl if(lc) {
557 1.50 mjl if (setusercontext(lc, pwd, pwd->pw_uid,
558 1.50 mjl LOGIN_SETALL & ~LOGIN_SETPATH) != 0) {
559 1.50 mjl syslog(LOG_ERR, "setusercontext failed");
560 1.50 mjl exit(1);
561 1.50 mjl }
562 1.48 mjl }
563 1.50 mjl else
564 1.50 mjl #endif
565 1.50 mjl {
566 1.1 cgd (void)setgid(pwd->pw_gid);
567 1.1 cgd
568 1.1 cgd initgroups(username, pwd->pw_gid);
569 1.48 mjl
570 1.48 mjl if (setlogin(pwd->pw_name) < 0)
571 1.48 mjl syslog(LOG_ERR, "setlogin() failure: %m");
572 1.48 mjl
573 1.48 mjl /* Discard permissions last so can't get killed and drop core. */
574 1.48 mjl if (rootlogin)
575 1.48 mjl (void)setuid(0);
576 1.48 mjl else
577 1.48 mjl (void)setuid(pwd->pw_uid);
578 1.50 mjl }
579 1.48 mjl
580 1.1 cgd
581 1.1 cgd if (*pwd->pw_shell == '\0')
582 1.1 cgd pwd->pw_shell = _PATH_BSHELL;
583 1.48 mjl #ifdef LOGIN_CAP
584 1.48 mjl if((shell = login_getcapstr(lc, "shell", NULL, NULL))) {
585 1.48 mjl if(!(shell = strdup(shell))) {
586 1.48 mjl syslog(LOG_NOTICE, "Cannot alloc mem");
587 1.48 mjl sleepexit(1);
588 1.48 mjl }
589 1.48 mjl pwd->pw_shell = shell;
590 1.48 mjl }
591 1.48 mjl #endif
592 1.48 mjl
593 1.5 cgd /* Destroy environment unless user has requested its preservation. */
594 1.1 cgd if (!pflag)
595 1.1 cgd environ = envinit;
596 1.1 cgd (void)setenv("HOME", pwd->pw_dir, 1);
597 1.1 cgd (void)setenv("SHELL", pwd->pw_shell, 1);
598 1.48 mjl if (term[0] == '\0') {
599 1.48 mjl char *tt = (char *) stypeof(tty);
600 1.48 mjl #ifdef LOGIN_CAP
601 1.48 mjl if(!tt)
602 1.48 mjl tt = login_getcapstr(lc, "term", NULL, NULL);
603 1.48 mjl #endif
604 1.48 mjl /* unknown term -> "su" */
605 1.48 mjl (void)strncpy(term, tt ? tt : "su", sizeof(term));
606 1.48 mjl }
607 1.1 cgd (void)setenv("TERM", term, 0);
608 1.1 cgd (void)setenv("LOGNAME", pwd->pw_name, 1);
609 1.1 cgd (void)setenv("USER", pwd->pw_name, 1);
610 1.51 mjl
611 1.48 mjl #ifdef LOGIN_CAP
612 1.51 mjl if(lc)
613 1.51 mjl setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH);
614 1.51 mjl else
615 1.51 mjl (void)setenv("PATH", _PATH_DEFPATH, 0);
616 1.48 mjl #else
617 1.1 cgd (void)setenv("PATH", _PATH_DEFPATH, 0);
618 1.48 mjl #endif
619 1.51 mjl
620 1.1 cgd #ifdef KERBEROS
621 1.1 cgd if (krbtkfile_env)
622 1.1 cgd (void)setenv("KRBTKFILE", krbtkfile_env, 1);
623 1.1 cgd #endif
624 1.10 brezak #ifdef KERBEROS5
625 1.10 brezak if (krbtkfile_env)
626 1.10 brezak (void)setenv("KRB5CCNAME", krbtkfile_env, 1);
627 1.10 brezak #endif
628 1.1 cgd
629 1.12 jtc if (tty[sizeof("tty")-1] == 'd')
630 1.12 jtc syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name);
631 1.12 jtc
632 1.5 cgd /* If fflag is on, assume caller/authenticator has logged root login. */
633 1.40 ross if (rootlogin && fflag == 0) {
634 1.1 cgd if (hostname)
635 1.1 cgd syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s",
636 1.1 cgd username, tty, hostname);
637 1.1 cgd else
638 1.40 ross syslog(LOG_NOTICE,
639 1.40 ross "ROOT LOGIN (%s) ON %s", username, tty);
640 1.40 ross }
641 1.1 cgd
642 1.10 brezak #if defined(KERBEROS) || defined(KERBEROS5)
643 1.1 cgd if (!quietlog && notickets == 1)
644 1.1 cgd (void)printf("Warning: no Kerberos tickets issued.\n");
645 1.1 cgd #endif
646 1.1 cgd
647 1.1 cgd if (!quietlog) {
648 1.51 mjl char *fname;
649 1.48 mjl #ifdef LOGIN_CAP
650 1.48 mjl fname = login_getcapstr(lc, "copyright", NULL, NULL);
651 1.48 mjl if (fname && access(fname, F_OK) == 0)
652 1.48 mjl motd(fname);
653 1.48 mjl else
654 1.51 mjl (void)printf(copyrightstr);
655 1.51 mjl #else
656 1.51 mjl (void)printf(copyrightstr);
657 1.48 mjl #endif
658 1.48 mjl
659 1.48 mjl #ifdef LOGIN_CAP
660 1.48 mjl fname = login_getcapstr(lc, "welcome", NULL, NULL);
661 1.48 mjl if (fname == NULL || access(fname, F_OK) != 0)
662 1.48 mjl #endif
663 1.48 mjl fname = _PATH_MOTDFILE;
664 1.48 mjl motd(fname);
665 1.48 mjl
666 1.5 cgd (void)snprintf(tbuf,
667 1.5 cgd sizeof(tbuf), "%s/%s", _PATH_MAILDIR, pwd->pw_name);
668 1.1 cgd if (stat(tbuf, &st) == 0 && st.st_size != 0)
669 1.1 cgd (void)printf("You have %smail.\n",
670 1.1 cgd (st.st_mtime > st.st_atime) ? "new " : "");
671 1.1 cgd }
672 1.1 cgd
673 1.48 mjl #ifdef LOGIN_CAP
674 1.48 mjl login_close(lc);
675 1.48 mjl #endif
676 1.48 mjl
677 1.1 cgd (void)signal(SIGALRM, SIG_DFL);
678 1.1 cgd (void)signal(SIGQUIT, SIG_DFL);
679 1.1 cgd (void)signal(SIGINT, SIG_DFL);
680 1.1 cgd (void)signal(SIGTSTP, SIG_IGN);
681 1.1 cgd
682 1.1 cgd tbuf[0] = '-';
683 1.17 mrg (void)strncpy(tbuf + 1, (p = strrchr(pwd->pw_shell, '/')) ?
684 1.17 mrg p + 1 : pwd->pw_shell, sizeof(tbuf) - 2);
685 1.1 cgd
686 1.24 lukem /* Wait to change password until we're unprivileged */
687 1.24 lukem if (need_chpass) {
688 1.29 mycroft if (!require_chpass)
689 1.24 lukem (void)printf(
690 1.24 lukem "Warning: your password has expired. Please change it as soon as possible.\n");
691 1.29 mycroft else {
692 1.38 mrg int status;
693 1.38 mrg
694 1.29 mycroft (void)printf(
695 1.24 lukem "Your password has expired. Please choose a new one.\n");
696 1.38 mrg switch (fork()) {
697 1.38 mrg case -1:
698 1.38 mrg warn("fork");
699 1.29 mycroft sleepexit(1);
700 1.38 mrg case 0:
701 1.38 mrg execl(_PATH_BINPASSWD, "passwd", 0);
702 1.38 mrg _exit(1);
703 1.38 mrg default:
704 1.38 mrg if (wait(&status) == -1 ||
705 1.38 mrg WEXITSTATUS(status))
706 1.38 mrg sleepexit(1);
707 1.38 mrg }
708 1.29 mycroft }
709 1.24 lukem }
710 1.1 cgd
711 1.44 aidan #ifdef KERBEROS5
712 1.44 aidan k5_write_creds();
713 1.44 aidan #endif
714 1.1 cgd execlp(pwd->pw_shell, tbuf, 0);
715 1.12 jtc err(1, "%s", pwd->pw_shell);
716 1.1 cgd }
717 1.1 cgd
718 1.10 brezak #if defined(KERBEROS) || defined(KERBEROS5)
719 1.16 sommerfe #define NBUFSIZ (MAXLOGNAME + 1 + 5) /* .root suffix */
720 1.1 cgd #else
721 1.16 sommerfe #define NBUFSIZ (MAXLOGNAME + 1)
722 1.10 brezak #endif
723 1.10 brezak
724 1.10 brezak #if defined(KERBEROS) || defined(KERBEROS5)
725 1.10 brezak /*
726 1.10 brezak * This routine handles cleanup stuff, and the like.
727 1.10 brezak * It exists only in the child process.
728 1.10 brezak */
729 1.10 brezak #include <sys/wait.h>
730 1.10 brezak void
731 1.10 brezak dofork()
732 1.10 brezak {
733 1.38 mrg int child;
734 1.38 mrg
735 1.38 mrg if (!(child = fork()))
736 1.38 mrg return; /* Child process */
737 1.38 mrg
738 1.38 mrg /* Setup stuff? This would be things we could do in parallel with login */
739 1.38 mrg (void) chdir("/"); /* Let's not keep the fs busy... */
740 1.10 brezak
741 1.38 mrg /* If we're the parent, watch the child until it dies */
742 1.38 mrg while (wait(0) != child)
743 1.38 mrg ;
744 1.10 brezak
745 1.38 mrg /* Cleanup stuff */
746 1.38 mrg /* Run kdestroy to destroy tickets */
747 1.38 mrg kdestroy();
748 1.10 brezak
749 1.38 mrg /* Leave */
750 1.38 mrg exit(0);
751 1.10 brezak }
752 1.1 cgd #endif
753 1.1 cgd
754 1.5 cgd void
755 1.1 cgd getloginname()
756 1.1 cgd {
757 1.12 jtc int ch;
758 1.12 jtc char *p;
759 1.1 cgd static char nbuf[NBUFSIZ];
760 1.1 cgd
761 1.1 cgd for (;;) {
762 1.1 cgd (void)printf("login: ");
763 1.1 cgd for (p = nbuf; (ch = getchar()) != '\n'; ) {
764 1.1 cgd if (ch == EOF) {
765 1.1 cgd badlogin(username);
766 1.1 cgd exit(0);
767 1.1 cgd }
768 1.1 cgd if (p < nbuf + (NBUFSIZ - 1))
769 1.1 cgd *p++ = ch;
770 1.1 cgd }
771 1.40 ross if (p > nbuf) {
772 1.1 cgd if (nbuf[0] == '-')
773 1.1 cgd (void)fprintf(stderr,
774 1.1 cgd "login names may not start with '-'.\n");
775 1.1 cgd else {
776 1.1 cgd *p = '\0';
777 1.1 cgd username = nbuf;
778 1.1 cgd break;
779 1.1 cgd }
780 1.40 ross }
781 1.1 cgd }
782 1.1 cgd }
783 1.1 cgd
784 1.5 cgd int
785 1.1 cgd rootterm(ttyn)
786 1.1 cgd char *ttyn;
787 1.1 cgd {
788 1.1 cgd struct ttyent *t;
789 1.1 cgd
790 1.12 jtc return ((t = getttynam(ttyn)) && t->ty_status & TTY_SECURE);
791 1.1 cgd }
792 1.1 cgd
793 1.1 cgd jmp_buf motdinterrupt;
794 1.1 cgd
795 1.5 cgd void
796 1.48 mjl motd(fname)
797 1.48 mjl char *fname;
798 1.1 cgd {
799 1.12 jtc int fd, nchars;
800 1.1 cgd sig_t oldint;
801 1.1 cgd char tbuf[8192];
802 1.1 cgd
803 1.48 mjl if ((fd = open(fname ? fname : _PATH_MOTDFILE, O_RDONLY, 0)) < 0)
804 1.1 cgd return;
805 1.1 cgd oldint = signal(SIGINT, sigint);
806 1.1 cgd if (setjmp(motdinterrupt) == 0)
807 1.1 cgd while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
808 1.1 cgd (void)write(fileno(stdout), tbuf, nchars);
809 1.1 cgd (void)signal(SIGINT, oldint);
810 1.1 cgd (void)close(fd);
811 1.1 cgd }
812 1.1 cgd
813 1.5 cgd /* ARGSUSED */
814 1.1 cgd void
815 1.5 cgd sigint(signo)
816 1.5 cgd int signo;
817 1.1 cgd {
818 1.38 mrg
819 1.1 cgd longjmp(motdinterrupt, 1);
820 1.1 cgd }
821 1.1 cgd
822 1.5 cgd /* ARGSUSED */
823 1.5 cgd void
824 1.5 cgd timedout(signo)
825 1.5 cgd int signo;
826 1.5 cgd {
827 1.38 mrg
828 1.5 cgd (void)fprintf(stderr, "Login timed out after %d seconds\n", timeout);
829 1.5 cgd exit(0);
830 1.5 cgd }
831 1.5 cgd
832 1.5 cgd void
833 1.48 mjl checknologin(fname)
834 1.48 mjl char *fname;
835 1.1 cgd {
836 1.12 jtc int fd, nchars;
837 1.1 cgd char tbuf[8192];
838 1.1 cgd
839 1.48 mjl if ((fd = open(fname ? fname : _PATH_NOLOGIN, O_RDONLY, 0)) >= 0) {
840 1.1 cgd while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
841 1.1 cgd (void)write(fileno(stdout), tbuf, nchars);
842 1.1 cgd sleepexit(0);
843 1.1 cgd }
844 1.1 cgd }
845 1.1 cgd
846 1.5 cgd void
847 1.1 cgd dolastlog(quiet)
848 1.1 cgd int quiet;
849 1.1 cgd {
850 1.1 cgd struct lastlog ll;
851 1.1 cgd int fd;
852 1.1 cgd
853 1.1 cgd if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) {
854 1.26 kleink (void)lseek(fd, (off_t)(pwd->pw_uid * sizeof(ll)), SEEK_SET);
855 1.1 cgd if (!quiet) {
856 1.1 cgd if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) &&
857 1.1 cgd ll.ll_time != 0) {
858 1.1 cgd (void)printf("Last login: %.*s ",
859 1.42 kim 24, (char *)ctime(&ll.ll_time));
860 1.1 cgd if (*ll.ll_host != '\0')
861 1.1 cgd (void)printf("from %.*s\n",
862 1.5 cgd (int)sizeof(ll.ll_host),
863 1.5 cgd ll.ll_host);
864 1.1 cgd else
865 1.1 cgd (void)printf("on %.*s\n",
866 1.5 cgd (int)sizeof(ll.ll_line),
867 1.5 cgd ll.ll_line);
868 1.1 cgd }
869 1.26 kleink (void)lseek(fd, (off_t)(pwd->pw_uid * sizeof(ll)),
870 1.26 kleink SEEK_SET);
871 1.1 cgd }
872 1.12 jtc memset((void *)&ll, 0, sizeof(ll));
873 1.1 cgd (void)time(&ll.ll_time);
874 1.5 cgd (void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
875 1.1 cgd if (hostname)
876 1.5 cgd (void)strncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
877 1.1 cgd (void)write(fd, (char *)&ll, sizeof(ll));
878 1.1 cgd (void)close(fd);
879 1.1 cgd }
880 1.1 cgd }
881 1.1 cgd
882 1.5 cgd void
883 1.1 cgd badlogin(name)
884 1.1 cgd char *name;
885 1.1 cgd {
886 1.1 cgd if (failures == 0)
887 1.1 cgd return;
888 1.1 cgd if (hostname) {
889 1.1 cgd syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s",
890 1.1 cgd failures, failures > 1 ? "S" : "", hostname);
891 1.1 cgd syslog(LOG_AUTHPRIV|LOG_NOTICE,
892 1.1 cgd "%d LOGIN FAILURE%s FROM %s, %s",
893 1.1 cgd failures, failures > 1 ? "S" : "", hostname, name);
894 1.1 cgd } else {
895 1.1 cgd syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s",
896 1.1 cgd failures, failures > 1 ? "S" : "", tty);
897 1.1 cgd syslog(LOG_AUTHPRIV|LOG_NOTICE,
898 1.1 cgd "%d LOGIN FAILURE%s ON %s, %s",
899 1.1 cgd failures, failures > 1 ? "S" : "", tty, name);
900 1.1 cgd }
901 1.1 cgd }
902 1.1 cgd
903 1.39 mycroft const char *
904 1.1 cgd stypeof(ttyid)
905 1.39 mycroft const char *ttyid;
906 1.1 cgd {
907 1.1 cgd struct ttyent *t;
908 1.1 cgd
909 1.48 mjl return (ttyid && (t = getttynam(ttyid)) ? t->ty_type : NULL);
910 1.1 cgd }
911 1.1 cgd
912 1.5 cgd void
913 1.1 cgd sleepexit(eval)
914 1.1 cgd int eval;
915 1.1 cgd {
916 1.38 mrg
917 1.12 jtc (void)sleep(5);
918 1.1 cgd exit(eval);
919 1.1 cgd }
920