drm/linux: Restore wait_event*() functionality
[dragonfly.git] / contrib / opie / opiesu.c
1 /* opiesu.c: main body of code for the su(1m) program
2
3 %%% portions-copyright-cmetz-96
4 Portions of this software are Copyright 1996-1999 by Craig Metz, All Rights
5 Reserved. The Inner Net License Version 2 applies to these portions of
6 the software.
7 You should have received a copy of the license with this software. If
8 you didn't get a copy, you may request one from <license@inner.net>.
9
10 Portions of this software are Copyright 1995 by Randall Atkinson and Dan
11 McDonald, All Rights Reserved. All Rights under this copyright are assigned
12 to the U.S. Naval Research Laboratory (NRL). The NRL Copyright Notice and
13 License Agreement applies to this software.
14
15         History:
16
17         Modified by cmetz for OPIE 2.4. Check euid on startup. Use
18                 opiestrncpy().
19         Modified by cmetz for OPIE 2.32. Set up TERM and PATH correctly.
20         Modified by cmetz for OPIE 2.31. Fix sulog(). Replaced Getlogin() with
21                 currentuser. Fixed fencepost error in month printed by sulog().
22         Modified by cmetz for OPIE 2.3. Limit the length of TERM on full login.
23                 Use HAVE_SULOG instead of DOSULOG.
24         Modified by cmetz for OPIE 2.2. Don't try to clear non-blocking I/O.
25                 Use opiereadpass(). Minor speedup. Removed termios manipulation
26                 -- that's opiereadpass()'s job. Change opiereadpass() calls
27                 to add echo arg. Removed useless strings (I don't think that
28                 removing the ucb copyright one is a problem -- please let me
29                 know if I'm wrong). Use FUNCTION declaration et al. Ifdef
30                 around some headers. Make everything static. Removed
31                 closelog() prototype. Use the same catchexit() trickery as
32                 opielogin.
33         Modified at NRL for OPIE 2.2. Changed opiestrip_crlf to
34                 opiestripcrlf.
35         Modified at NRL for OPIE 2.1. Added struct group declaration.
36                 Added Solaris(+others?) sulog capability. Symbol changes
37                 for autoconf. Removed des_crypt.h. File renamed to
38                 opiesu.c. Symbol+misc changes for autoconf. Added bletch
39                 for setpriority.
40         Modified at NRL for OPIE 2.02. Added SU_STAR_CHECK (turning a bug
41                 into a feature ;). Fixed Solaris shadow password problem
42                 introduced in OPIE 2.01 (the shadow password structure is
43                 spwd, not spasswd).
44         Modified at NRL for OPIE 2.01. Changed password lookup handling
45                 to use a static structure to avoid problems with drain-
46                 bamaged shadow password packages. Always log failures.
47                 Make sure to close syslog by function to avoid problems 
48                 with drain bamaged syslog implementations. Log a few 
49                 interesting errors.
50         Modified at NRL for OPIE 2.0.
51         Modified at Bellcore for the S/Key Version 1 software distribution.
52         Originally from BSD.
53 */
54
55 /*
56  * Copyright (c) 1980 Regents of the University of California.
57  * All rights reserved.  The Berkeley software License Agreement
58  * specifies the terms and conditions for redistribution.
59  */
60
61 #include "opie_cfg.h"
62
63 #include <stdio.h>
64 #if HAVE_PWD_H
65 #include <pwd.h>
66 #endif /* HAVE_PWD_H */
67 #include <grp.h>
68 #include <syslog.h>
69 #include <sys/types.h>
70 #if HAVE_SETPRIORITY && HAVE_SYS_RESOURCE_H
71 #if TIME_WITH_SYS_TIME
72 # include <sys/time.h>
73 # include <time.h>
74 #else /* TIME_WITH_SYS_TIME */
75 #if HAVE_SYS_TIME_H
76 #include <sys/time.h>
77 #else /* HAVE_SYS_TIME_H */
78 #include <time.h>
79 #endif /* HAVE_SYS_TIME_H */
80 #endif /* TIME_WITH_SYS_TIME */
81 #include <sys/resource.h>
82 #else /* HAVE_SETPRIORITY && HAVE_SYS_RESOURCE_H */
83 #if TM_IN_SYS_TIME
84 #include <sys/time.h>
85 #else /* TM_IN_SYS_TIME */
86 #include <time.h>
87 #endif /* TM_IN_SYS_TIME */
88 #endif /* HAVE_SETPRIORITY && HAVE_SYS_RESOURCE_H */
89 #if HAVE_STDLIB_H
90 #include <stdlib.h>
91 #endif /* HAVE_STDLIB_H */
92 #if HAVE_UNISTD_H
93 #include <unistd.h>
94 #endif /* HAVE_UNISTD_H */
95 #if HAVE_STRING_H
96 #include <string.h>
97 #endif /* HAVE_STRING_H */
98 #include <errno.h>
99
100 #include "opie.h"
101
102 static char userbuf[16] = "USER=";
103 static char homebuf[128] = "HOME=";
104 static char shellbuf[128] = "SHELL=";
105 static char pathbuf[sizeof("PATH") + sizeof(DEFAULT_PATH) - 1] = "PATH=";
106 static char termbuf[32] = "TERM=";
107 static char *cleanenv[] = {userbuf, homebuf, shellbuf, pathbuf, 0, 0};
108 static char *user = "root";
109 static char *shell = "/bin/sh";
110 static int fulllogin;
111 #if 0
112 static int fastlogin;
113 #else /* 0 */
114 static int force = 0;
115 #endif /* 0 */
116
117 static char currentuser[65];
118
119 extern char **environ;
120 static struct passwd thisuser, nouser;
121
122 #if HAVE_SHADOW_H
123 #include <shadow.h>
124 #endif /* HAVE_SHADOW_H */
125
126 #if HAVE_CRYPT_H
127 #include <crypt.h>
128 #endif /* HAVE_CRYPT_H */
129
130 static VOIDRET catchexit FUNCTION_NOARGS
131 {
132   int i;
133   closelog();
134   for (i = sysconf(_SC_OPEN_MAX); i > 2; i--)
135     close(i);
136 }
137
138 /* We allow the malloc()s to potentially leak data out because we can
139 only call this routine about four times in the lifetime of this process
140 and the kernel will free all heap memory when we exit or exec. */
141 static int lookupuser FUNCTION((name), char *name)
142 {
143   struct passwd *pwd;
144 #if HAVE_SHADOW
145   struct spwd *spwd;
146 #endif /* HAVE_SHADOW */
147
148   memcpy(&thisuser, &nouser, sizeof(thisuser));
149
150   if (!(pwd = getpwnam(name)))
151     return -1;
152
153   thisuser.pw_uid = pwd->pw_uid;
154   thisuser.pw_gid = pwd->pw_gid;
155
156   if (!(thisuser.pw_name = malloc(strlen(pwd->pw_name) + 1)))
157     goto lookupuserbad;
158   strcpy(thisuser.pw_name, pwd->pw_name);
159
160   if (!(thisuser.pw_dir = malloc(strlen(pwd->pw_dir) + 1)))
161     goto lookupuserbad;
162   strcpy(thisuser.pw_dir, pwd->pw_dir);
163
164   if (!(thisuser.pw_shell = malloc(strlen(pwd->pw_shell) + 1)))
165     goto lookupuserbad;
166   strcpy(thisuser.pw_shell, pwd->pw_shell);
167
168 #if HAVE_SHADOW
169   if (!(spwd = getspnam(name)))
170         goto lookupuserbad;
171
172   pwd->pw_passwd = spwd->sp_pwdp;
173
174   endspent();
175 #endif /* HAVE_SHADOW */
176
177   if (!(thisuser.pw_passwd = malloc(strlen(pwd->pw_passwd) + 1)))
178     goto lookupuserbad;
179   strcpy(thisuser.pw_passwd, pwd->pw_passwd);
180
181   endpwent();
182
183 #if SU_STAR_CHECK
184   return ((thisuser.pw_passwd[0] == '*') || (thisuser.pw_passwd[0] == '#'));
185 #else /* SU_STAR_CHECK */
186   return 0;
187 #endif /* SU_STAR_CHECK */
188
189 lookupuserbad:
190   memcpy(&thisuser, &nouser, sizeof(thisuser));
191   return -1;
192 }
193
194 static VOIDRET lsetenv FUNCTION((ename, eval, buf), char *ename AND char *eval AND char *buf)
195 {
196   register char *cp, *dp;
197   register char **ep = environ;
198
199   /* this assumes an environment variable "ename" already exists */
200   while (dp = *ep++) {
201     for (cp = ename; *cp == *dp && *cp; cp++, dp++)
202       continue;
203     if (*cp == 0 && (*dp == '=' || *dp == 0)) {
204       strcat(buf, eval);
205       *--ep = buf;
206       return;
207     }
208   }
209 }
210
211 #if HAVE_SULOG
212 static int sulog FUNCTION((status, who), int status AND char *who)
213 {
214   char *from;
215   char *ttynam;
216   struct tm *tm;
217   FILE *f;
218   time_t now;
219
220   if (who)
221     from = who;
222   else
223     from = currentuser;
224
225   if (!strncmp(ttynam = ttyname(2), "/dev/", 5))
226     ttynam += 5;
227
228   now = time(NULL);
229   tm = localtime(&now);
230   
231   if (!(f = fopen("/var/adm/sulog", "a"))) {
232     fprintf(stderr, "Can't update su log!\n");
233     exit(1);
234   }
235
236   fprintf(f, "SU %02d/%02d %02d:%02d %c %s %s-%s\n",
237           tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min,
238           status ? '+' : '-', ttynam, from, user);
239   fclose(f);
240 }
241 #endif /* HAVE_SULOG */
242
243 int main FUNCTION((argc, argv), int argc AND char *argv[])
244 {
245   char *p;
246   struct opie opie;
247   int i;
248   char pbuf[256];
249   char opieprompt[80];
250   int console = 0;
251   char *argvbuf;
252
253   for (i = sysconf(_SC_OPEN_MAX); i > 2; i--)
254     close(i);
255
256   openlog("su", 0, LOG_AUTH);
257   atexit(catchexit);
258
259   {
260   int argvsize = 0;
261   for (i = 0; i < argc; argvsize += strlen(argv[i++]));
262   argvsize += argc;
263   if (!(argvbuf = malloc(argvsize))) {
264     syslog(LOG_ERR, "can't allocate memory to store command line");
265     exit(1);
266   };
267   for (i = 0, *argvbuf = 0; i < argc;) {
268     strcat(argvbuf, argv[i]);
269     if (++i < argc)
270       strcat(argvbuf, " ");
271   };
272   };
273
274   strcat(pathbuf, DEFAULT_PATH);
275
276 again:
277   if (argc > 1 && strcmp(argv[1], "-f") == 0) {
278 #if 0
279     fastlogin++;
280 #else /* 0 */
281 #if INSECURE_OVERRIDE
282     force = 1;
283 #else /* INSECURE_OVERRIDE */
284     fprintf(stderr, "Sorry, but the -f option is not supported by this build of OPIE.\n");
285 #endif /* INSECURE_OVERRIDE */
286 #endif /* 0 */
287     argc--, argv++;
288     goto again;
289   }
290   if (argc > 1 && strcmp(argv[1], "-c") == 0) {
291     console++;
292     argc--, argv++;
293     goto again;
294   }
295   if (argc > 1 && strcmp(argv[1], "-") == 0) {
296     fulllogin++;
297     argc--;
298     argv++;
299     goto again;
300   }
301   if (argc > 1 && argv[1][0] != '-') {
302     user = argv[1];
303     argc--;
304     argv++;
305   }
306
307
308   {
309   struct passwd *pwd;
310   char *p = getlogin();
311   char buf[32];
312   char *cryptpw;
313
314   if ((pwd = getpwuid(getuid())) == NULL) {
315     syslog(LOG_CRIT, "'%s' failed for unknown uid %d on %s", argvbuf, getuid(), ttyname(2));
316 #if HAVE_SULOG
317     sulog(0, "unknown");
318 #endif /* HAVE_SULOG */
319     exit(1);
320   }
321   opiestrncpy(buf, pwd->pw_name, sizeof(buf));
322
323   if (!p)
324     p = "unknown";
325
326   opiestrncpy(currentuser, p, 31);
327
328   if (p && *p && strcmp(currentuser, buf)) {
329     strcat(currentuser, "(");
330     strcat(currentuser, buf);
331     strcat(currentuser, ")");
332   };
333
334   if (lookupuser(user)) {
335     syslog(LOG_CRIT, "'%s' failed for %s on %s", argvbuf, currentuser, ttyname(2));
336 #if HAVE_SULOG
337     sulog(0, NULL);
338 #endif /* HAVE_SULOG */
339     fprintf(stderr, "Unknown user: %s\n", user);
340     exit(1);
341   }
342
343   if (geteuid()) {
344     syslog(LOG_CRIT, "'%s' failed for %s on %s: not running with superuser priveleges", argvbuf, currentuser, ttyname(2));
345 #if HAVE_SULOG
346     sulog(0, NULL);
347 #endif /* HAVE_SULOG */
348     fprintf(stderr, "You do not have permission to su %s\n", user);
349     exit(1);
350   };
351
352 /* Implement the BSD "wheel group" su restriction. */
353 #if DOWHEEL
354   /* Only allow those in group zero to su to root? */
355   if (thisuser.pw_uid == 0) {
356     struct group *gr;
357     if ((gr = getgrgid(0)) != NULL) {
358       for (i = 0; gr->gr_mem[i] != NULL; i++)
359         if (strcmp(buf, gr->gr_mem[i]) == 0)
360           goto userok;
361       fprintf(stderr, "You do not have permission to su %s\n", user);
362       exit(1);
363     }
364 userok:
365     ;
366 #if HAVE_SETPRIORITY && HAVE_SYS_RESOURCE_H
367     setpriority(PRIO_PROCESS, 0, -2);
368 #endif /* HAVE_SETPRIORITY && HAVE_SYS_RESOURCE_H */
369   }
370 #endif  /* DOWHEEL */
371   };
372
373   if (!thisuser.pw_passwd[0] || getuid() == 0)
374     goto ok;
375
376   if (console) {
377     if (!opiealways(thisuser.pw_dir)) {
378       fprintf(stderr, "That account requires OTP responses.\n");
379       exit(1);
380     };
381     /* Get user's secret password */
382     fprintf(stderr, "Reminder - Only use this method from the console; NEVER from remote. If you\n");
383     fprintf(stderr, "are using telnet, xterm, or a dial-in, type ^C now or exit with no password.\n");
384     fprintf(stderr, "Then run su without the -c parameter.\n");
385     if (opieinsecure()) {
386       fprintf(stderr, "Sorry, but you don't seem to be on the console or a secure terminal.\n");
387 #if INSECURE_OVERRIDE
388     if (force)
389       fprintf(stderr, "Warning: Continuing could disclose your secret pass phrase to an attacker!\n");
390     else
391 #endif /* INSECURE_OVERRIDE */
392       exit(1);
393     };
394 #if NEW_PROMPTS
395     printf("%s's system password: ", thisuser.pw_name);
396     if (!opiereadpass(pbuf, sizeof(pbuf), 0))
397       goto error;
398 #endif /* NEW_PROMPTS */
399   } else {
400     /* Attempt an OTP challenge */
401     i = opiechallenge(&opie, user, opieprompt);
402     printf("%s\n", opieprompt);
403 #if NEW_PROMPTS
404     printf("%s's response: ", thisuser.pw_name);
405     if (!opiereadpass(pbuf, sizeof(pbuf), 1))
406       goto error;
407 #else /* NEW_PROMPTS */
408     printf("(OTP response required)\n");
409 #endif /* NEW_PROMPTS */
410     fflush(stdout);
411   };
412 #if !NEW_PROMPTS
413   printf("%s's password: ", thisuser.pw_name);
414   if (!opiereadpass(pbuf, sizeof(pbuf), 0))
415     goto error;
416 #endif /* !NEW_PROMPTS */
417
418 #if !NEW_PROMPTS
419   if (!pbuf[0] && !console) {
420     /* Null line entered; turn echoing back on and read again */
421     printf(" (echo on)\n%s's password: ", thisuser.pw_name);
422     if (!opiereadpass(pbuf, sizeof(pbuf), 1))
423       goto error;
424   }
425 #endif /* !NEW_PROMPTS */
426
427   if (console) {
428     /* Try regular password check, if allowed */
429     cryptpw = crypt(pbuf, thisuser.pw_passwd);
430     if (cryptpw != NULL && !strcmp(cryptpw, thisuser.pw_passwd))
431       goto ok;
432   } else {
433     int i = opiegetsequence(&opie);
434     if (!opieverify(&opie, pbuf)) {
435       /* OPIE authentication succeeded */
436       if (i < 5)
437         fprintf(stderr, "Warning: Change %s's OTP secret pass phrase NOW!\n", user);
438       else
439         if (i < 10)
440           fprintf(stderr, "Warning: Change %s's OTP secret pass phrase soon.\n", user);
441       goto ok;
442     };
443   };
444 error:
445   if (!console)
446     opieverify(&opie, "");
447   fprintf(stderr, "Sorry\n");
448   syslog(LOG_CRIT, "'%s' failed for %s on %s", argvbuf, currentuser, ttyname(2));
449 #if HAVE_SULOG
450   sulog(0, NULL);
451 #endif /* HAVE_SULOG */
452   exit(2);
453
454 ok:
455   syslog(LOG_NOTICE, "'%s' by %s on %s", argvbuf, currentuser, ttyname(2));
456 #if HAVE_SULOG
457   sulog(1, NULL);
458 #endif /* HAVE_SULOG */
459   
460   if (setgid(thisuser.pw_gid) < 0) {
461     perror("su: setgid");
462     exit(3);
463   }
464   if (initgroups(user, thisuser.pw_gid)) {
465     fprintf(stderr, "su: initgroups failed (errno=%d)\n", errno);
466     exit(4);
467   }
468   if (setuid(thisuser.pw_uid) < 0) {
469     perror("su: setuid");
470     exit(5);
471   }
472   if (thisuser.pw_shell && *thisuser.pw_shell)
473     shell = thisuser.pw_shell;
474   if (fulllogin) {
475     if ((p = getenv("TERM")) && (strlen(termbuf) + strlen(p) - 1 < sizeof(termbuf))) {
476       strcat(termbuf, p);
477       cleanenv[4] = termbuf;
478     }
479     environ = cleanenv;
480   }
481   if (fulllogin || strcmp(user, "root") != 0)
482     lsetenv("USER", thisuser.pw_name, userbuf);
483   lsetenv("SHELL", shell, shellbuf);
484   lsetenv("HOME", thisuser.pw_dir, homebuf);
485
486 #if HAVE_SETPRIORITY && HAVE_SYS_RESOURCE_H
487   setpriority(PRIO_PROCESS, 0, 0);
488 #endif /* HAVE_SETPRIORITY && HAVE_SYS_RESOURCE_H */
489
490 #if 0
491   if (fastlogin) {
492     *argv-- = "-f";
493     *argv = "su";
494   } else
495 #endif /* 0 */
496     if (fulllogin) {
497       if (chdir(thisuser.pw_dir) < 0) {
498         fprintf(stderr, "No directory\n");
499         exit(6);
500       }
501       *argv = "-su";
502     } else {
503       *argv = "su";
504     }
505
506   catchexit();
507
508 #if DEBUG
509   syslog(LOG_DEBUG, "execing %s", shell);
510 #endif /* DEBUG */
511   execv(shell, argv);
512   fprintf(stderr, "No shell\n");
513   exit(7);
514 }