forget difference between big and small commands - obsolete with vm.
[minix.git] / commands / simple / login.c
blobb720faa492ba9edce87c67c93ac200f8df7a19e6
1 /* login - log into the system Author: Patrick van Kleef */
3 /* Original version by Patrick van Kleef. History of modifications:
5 * Peter S. Housel Jan. 1988
6 * - Set up $USER, $HOME and $TERM.
7 * - Set signals to SIG_DFL.
9 * Terrence W. Holm June 1988
10 * - Allow a username as an optional argument.
11 * - Time out if a password is not typed within 60 seconds.
12 * - Perform a dummy delay after a bad username is entered.
13 * - Don't allow a login if "/etc/nologin" exists.
14 * - Cause a failure on bad "pw_shell" fields.
15 * - Record the login in "/usr/adm/wtmp".
17 * Peter S. Housel Dec. 1988
18 * - Record the login in "/etc/utmp" also.
20 * F. van Kempen June 1989
21 * - various patches for Minix V1.4a.
23 * F. van Kempen September 1989
24 * - added login-failure administration (new utmp.h needed!).
25 * - support arguments in pw_shell field
26 * - adapted source text to MINIX Style Sheet
28 * F. van Kempen October 1989
29 * - adapted to new utmp database.
30 * F. van Kempen, December 1989
31 * - fixed 'slot' assumption in wtmp()
32 * - fixed all MSS-stuff
33 * - adapted to POSIX (MINIX 1.5)
34 * F. van Kempen, January 1990
35 * - made all 'bad login accounting' optional by "#ifdef BADLOG".
36 * F. van Kempen, Februari 1990
37 * - fixed 'first argument' bug and added some casts.
39 * Andy Tanenbaum April 1990
40 * - if /bin/sh cannot be located, try /usr/bin/sh
42 * Michael A. Temari October 1990
43 * - handle more than single digit tty devices
45 * Philip Homburg - Feb 28 1992
46 * - use ttyname to get the name of a tty.
48 * Kees J. Bot - Feb 13 1993
49 * - putting out garbage.
50 * - added lastlog.
52 * Kees J. Bot - Feb 13 1993
53 * - supplementary groups.
55 * Kees J. Bot - Jan 3 1996
56 * - ported back to standard Minix.
59 #define _MINIX_SOURCE
60 #define _POSIX_C_SOURCE 2
62 #include <sys/types.h>
63 #include <ttyent.h>
64 #include <sys/stat.h>
65 #include <errno.h>
66 #include <fcntl.h>
67 #include <pwd.h>
68 #include <grp.h>
69 #include <signal.h>
70 #include <stdio.h>
71 #include <stdlib.h>
72 #include <string.h>
73 #include <unistd.h>
74 #include <utmp.h>
75 #include <time.h>
76 #include <sys/utsname.h>
77 #include <minix/minlib.h>
78 #include <minix/paths.h>
80 char PATH_UTMP[] = _PATH_UTMP; /* current logins */
81 char PATH_WTMP[] = _PATH_WTMP; /* login/logout history */
82 char PATH_LASTLOG[] = _PATH_LASTLOG; /* last login history */
83 char PATH_MOTD[] = _PATH_MOTD; /* message of the day */
85 #define TTY_GID 4 /* group ID of ttys */
87 #define EXTRA_ENV 6
89 /* Crude indication of a tty being physically secure: */
90 #define securetty(dev) ((unsigned) ((dev) - 0x0400) < (unsigned) 8)
92 int time_out;
93 char *hostname;
94 char user[32];
95 char logname[35];
96 char home[128];
97 char shell[128];
98 char term[128];
99 char **env;
100 extern char **environ;
102 _PROTOTYPE(int main, (int argc, char **argv));
103 _PROTOTYPE(void wtmp, (char *user, int uid));
104 _PROTOTYPE(void show_file, (char *nam));
105 _PROTOTYPE(void Time_out, (int dummy));
106 _PROTOTYPE(void usage, (void));
107 _PROTOTYPE(void add2env, (char **env, char *entry, int replace));
109 void wtmp(user, uid)
110 char *user; /* user name */
111 int uid; /* user id */
113 /* Make entries in /usr/adm/wtmp and /etc/utmp. */
114 struct utmp entry;
115 register int fd= -1;
116 int lineno;
117 int err = 0;
118 char *what;
120 /* First, read the current UTMP entry. we need some of its
121 * parameters! (like PID, ID etc...).
123 what= "ttyslot()";
124 lineno= ttyslot();
125 if (lineno == 0) err= errno; /* ttyslot failed */
127 if (err == 0 && (fd = open(what = PATH_UTMP, O_RDONLY)) < 0) {
128 if (errno == ENOENT) return;
129 err= errno;
131 if (err == 0 && lseek(fd, (off_t) lineno * sizeof(entry), SEEK_SET) < 0)
132 err= errno;
133 if (err == 0 && read(fd, (char *) &entry, sizeof(entry)) != sizeof(entry))
134 err= errno;
135 if (fd >= 0) close(fd);
137 /* Enter new fields. */
138 strncpy(entry.ut_user, user, sizeof(entry.ut_user));
139 if (hostname) strncpy(entry.ut_host, hostname, sizeof(entry.ut_host));
141 if (entry.ut_pid == 0) entry.ut_pid = getpid();
143 entry.ut_type = USER_PROCESS; /* we are past login... */
144 time(&entry.ut_time);
146 /* Write a WTMP record. */
147 if (err == 0) {
148 if ((fd = open(what = PATH_WTMP, O_WRONLY|O_APPEND)) < 0) {
149 if (errno != ENOENT) err= errno;
150 } else {
151 if (write(fd, (char *) &entry, sizeof(entry)) < 0) err= errno;
152 close(fd);
156 /* Rewrite the UTMP entry. */
157 if (err == 0 && (fd = open(what = PATH_UTMP, O_WRONLY)) < 0)
158 err= errno;
159 if (err == 0 && lseek(fd, (off_t) lineno * sizeof(entry), SEEK_SET) < 0)
160 err= errno;
161 if (err == 0 && write(fd, (char *) &entry, sizeof(entry)) < 0)
162 err= errno;
163 if (fd >= 0) close(fd);
165 /* Write the LASTLOG entry. */
166 if (err == 0 && (fd = open(what = PATH_LASTLOG, O_WRONLY)) < 0) {
167 if (errno == ENOENT) return;
168 err= errno;
170 if (err == 0 && lseek(fd, (off_t) uid * sizeof(entry), SEEK_SET) < 0)
171 err= errno;
172 if (err == 0 && write(fd, (char *) &entry, sizeof(entry)) < 0)
173 err= errno;
174 if (fd >= 0) close(fd);
176 if (err != 0) {
177 fprintf(stderr, "login: %s: %s\n", what, strerror(err));
178 return;
183 void show_file(nam)
184 char *nam;
186 /* Read a textfile and show it on the desired terminal. */
187 register int fd, len;
188 char buf[80];
190 if ((fd = open(nam, O_RDONLY)) > 0) {
191 len = 1;
192 while (len > 0) {
193 len = read(fd, buf, 80);
194 write(1, buf, len);
196 close(fd);
201 int main(argc, argv)
202 int argc;
203 char *argv[];
205 char name[30];
206 char *password, *cryptedpwd;
207 char *tty_name, *p;
208 int n, ap, check_pw, bad, secure, i, envsiz, do_banner;
209 struct passwd *pwd;
210 char *bp, *argx[8], **ep; /* pw_shell arguments */
211 char argx0[64]; /* argv[0] of the shell */
212 char *sh = "/bin/sh"; /* sh/pw_shell field value */
213 char *initialname;
214 int c, b_flag, f_flag, p_flag;
215 char *h_arg;
216 int authorized, preserv_env;
217 struct ttyent *ttyp;
218 struct stat ttystat;
219 struct sigaction sa;
220 struct utsname uts;
222 /* Don't let QUIT dump core. */
223 sigemptyset(&sa.sa_mask);
224 sa.sa_flags = 0;
225 sa.sa_handler = exit;
226 sigaction(SIGQUIT, &sa, NULL);
228 /* Parse options. */
229 b_flag= 0;
230 f_flag= 0;
231 p_flag= 0;
232 h_arg= NULL;
233 while ((c= getopt(argc, argv, "?bfh:p")) != -1)
235 switch(c)
237 case 'b': b_flag= 1; break;
238 case 'f': f_flag= 1; break;
239 case 'h':
240 if (h_arg)
241 usage();
242 if (getuid() == 0)
243 h_arg= optarg;
244 break;
245 case 'p': p_flag= 1; break;
246 case '?':
247 usage();
248 default:
249 fprintf(stderr, "login: getopt failed: '%c'\n", c);
250 exit(1);
253 if (optind < argc)
254 initialname= argv[optind++];
255 else
256 initialname= NULL;
257 if (optind != argc)
258 usage();
260 authorized= f_flag;
261 hostname= h_arg;
262 preserv_env= p_flag;
263 do_banner= b_flag;
265 /* Look up /dev/tty number. */
266 tty_name= ttyname(0);
267 if (tty_name == NULL)
269 write(1, "Unable to lookup tty name\n", 26);
270 exit(1);
273 if (do_banner)
275 uname(&uts);
276 write(1, "\n", 1);
277 write(1, uts.sysname, strlen(uts.sysname));
278 write(1, "/", 1);
279 write(1, uts.machine, strlen(uts.machine));
280 write(1, " Release ", 9);
281 write(1, uts.release, strlen(uts.release));
282 write(1, " Version ", 9);
283 write(1, uts.version, strlen(uts.version));
284 write(1, " (", 2);
285 p= strrchr(tty_name, '/');
286 if (!p)
287 p= tty_name;
288 else
289 p++;
290 write(1, p, strlen(p));
291 write(1, ")\n\n", 3);
292 write(1, uts.nodename, strlen(uts.nodename));
293 write(1, " ", 1);
296 /* Get login name and passwd. */
297 for (;;initialname= NULL) {
298 if (initialname)
299 strcpy(name, initialname);
300 else {
301 do {
302 write(1, "login: ", 7);
303 n = read(0, name, 30);
304 if (n == 0) exit(1);
305 if (n < 0)
307 if (errno != EINTR)
308 fprintf(stderr,
309 "login: read failed: %s\n",
310 strerror(errno));
311 exit(1);
313 } while (n < 2);
314 name[n - 1] = 0;
317 /* Start timer running. */
318 time_out = 0;
319 sa.sa_handler = Time_out;
320 sigaction(SIGALRM, &sa, NULL);
321 alarm(60);
324 /* Look up login/passwd. */
325 pwd = getpwnam(name);
327 check_pw = 1; /* default is check password. */
329 /* For now, only console is secure. */
330 secure = fstat(0, &ttystat) == 0 && securetty(ttystat.st_rdev);
332 if (pwd && authorized && initialname
333 && (pwd->pw_uid == getuid() || getuid() == 0)) {
334 check_pw= 0; /* Don't ask a password for
335 * pre-authorized users.
337 } else
338 if (pwd && secure && strcmp(crypt("", pwd->pw_passwd),
339 pwd->pw_passwd) == 0) {
340 check_pw= 0; /* empty password */
343 if (check_pw) {
344 password = getpass("Password:");
346 if (time_out) exit(1);
348 bad = 0;
349 if (!pwd) bad = 1;
350 if (!password) { password = ""; bad = 1; }
351 if (!secure && pwd && strcmp(crypt("", pwd->pw_passwd),
352 pwd->pw_passwd) == 0) bad = 1;
354 cryptedpwd = bad ? "*" : pwd->pw_passwd;
356 if (strcmp(crypt(password, cryptedpwd), cryptedpwd) != 0) {
357 write(1, "Login incorrect\n", 16);
358 continue;
361 /* Check if the system is going down */
362 if (access("/etc/nologin", 0) == 0 && strcmp(name, "root") != 0) {
363 write(1, "System going down\n\n", 19);
364 continue;
367 /* Stop timer. */
368 alarm(0);
370 /* Write login record to /usr/adm/wtmp and /etc/utmp */
371 wtmp(name, pwd->pw_uid);
373 /* Create the argv[] array from the pw_shell field. */
374 ap = 0;
375 argx[ap++] = argx0; /* "-sh" most likely */
376 if (pwd->pw_shell[0]) {
377 sh = pwd->pw_shell;
378 bp = sh;
379 while (*bp) {
380 while (*bp && *bp != ' ' && *bp != '\t') bp++;
381 if (*bp == ' ' || *bp == '\t') {
382 *bp++ = '\0'; /* mark end of string */
383 argx[ap++] = bp;
386 } else
387 argx[ap] = NULL;
388 strcpy(argx0, "-"); /* most shells need it for their .profile */
389 if ((bp= strrchr(sh, '/')) == NULL) bp = sh; else bp++;
390 strncat(argx0, bp, sizeof(argx0) - 2);
392 /* Set the environment */
393 if (p_flag)
395 for (ep= environ; *ep; ep++)
398 else
399 ep= environ;
401 envsiz= ep-environ;
402 env= calloc(envsiz + EXTRA_ENV, sizeof(*env));
403 if (env == NULL)
405 fprintf(stderr, "login: out of memory\n");
406 exit(1);
408 for (i= 0; i<envsiz; i++)
409 env[i]= environ[i];
411 strcpy(user, "USER=");
412 strcat(user, name);
413 add2env(env, user, 1);
414 strcpy(logname, "LOGNAME=");
415 strcat(logname, name);
416 add2env(env, logname, 1);
417 strcpy(home, "HOME=");
418 strcat(home, pwd->pw_dir);
419 add2env(env, home, 1);
420 strcpy(shell, "SHELL=");
421 strcat(shell, sh);
422 add2env(env, shell, 1);
423 if ((ttyp = getttynam(tty_name + 5)) != NULL) {
424 strcpy(term, "TERM=");
425 strcat(term, ttyp->ty_type);
426 add2env(env, term, 0);
429 /* Show the message-of-the-day. */
430 show_file(PATH_MOTD);
432 /* Assign the terminal to this user. */
433 chown(tty_name, pwd->pw_uid, TTY_GID);
434 chmod(tty_name, 0620);
436 /* Change id. */
437 initgroups(pwd->pw_name, pwd->pw_gid);
438 setgid(pwd->pw_gid);
439 setuid(pwd->pw_uid);
441 /* cd $HOME */
442 chdir(pwd->pw_dir);
444 /* Reset signals to default values. */
445 sa.sa_handler = SIG_DFL;
446 for (n = 1; n < _NSIG; ++n) sigaction(n, &sa, NULL);
448 /* Execute the user's shell. */
449 execve(sh, argx, env);
451 if (pwd->pw_gid == 0) {
452 /* Privileged user gets /bin/sh in times of crisis. */
453 sh= "/bin/sh";
454 argx[0]= "-sh";
455 strcpy(shell, "SHELL=");
456 strcat(shell, sh);
457 execve(sh, argx, env);
459 fprintf(stderr, "login: can't execute %s: %s\n", sh, strerror(errno));
460 exit(1);
462 return(0);
466 void Time_out(dummy)
467 int dummy; /* to keep the compiler happy */
469 write(2, "\r\nLogin timed out after 60 seconds\r\n", 36);
470 time_out = 1;
473 void usage()
475 fprintf(stderr,
476 "Usage: login [-h hostname] [-b] [-f] [-p] [username]\n");
477 exit(1);
480 void add2env(env, entry, replace)
481 char **env;
482 char *entry;
483 int replace;
485 /* Replace an environment variable with entry or add entry if the environment
486 * variable doesn't exit yet.
488 char *cp;
489 int keylen;
491 cp= strchr(entry, '=');
492 keylen= cp-entry+1;
494 for(; *env; env++)
496 if (strncmp(*env, entry, keylen) == 0) {
497 if (!replace) return; /* Don't replace */
498 break;
501 *env= entry;
505 * $PchId: login.c,v 1.6 2001/07/31 14:23:28 philip Exp $