diff options
author | pst <pst@FreeBSD.org> | 1997-02-06 17:52:29 +0000 |
---|---|---|
committer | pst <pst@FreeBSD.org> | 1997-02-06 17:52:29 +0000 |
commit | 2dfcbf193123fd16b26454eeffa4bbd014e52c53 (patch) | |
tree | ec9d150c9da4390c2d223a04ac002523cbfd7f36 /contrib/opie/opiesu.c | |
download | FreeBSD-src-2dfcbf193123fd16b26454eeffa4bbd014e52c53.zip FreeBSD-src-2dfcbf193123fd16b26454eeffa4bbd014e52c53.tar.gz |
Initial import of OPIE v2.3 from
ftp://ftp.nrl.navy.mil/pub/security/opie/
Diffstat (limited to 'contrib/opie/opiesu.c')
-rw-r--r-- | contrib/opie/opiesu.c | 448 |
1 files changed, 448 insertions, 0 deletions
diff --git a/contrib/opie/opiesu.c b/contrib/opie/opiesu.c new file mode 100644 index 0000000..403305b --- /dev/null +++ b/contrib/opie/opiesu.c @@ -0,0 +1,448 @@ +/* opiesu.c: main body of code for the su(1m) program + +%%% portions-copyright-cmetz +Portions of this software are Copyright 1996 by Craig Metz, All Rights +Reserved. The Inner Net License Version 2 applies to these portions of +the software. +You should have received a copy of the license with this software. If +you didn't get a copy, you may request one from <license@inner.net>. + +Portions of this software are Copyright 1995 by Randall Atkinson and Dan +McDonald, All Rights Reserved. All Rights under this copyright are assigned +to the U.S. Naval Research Laboratory (NRL). The NRL Copyright Notice and +License Agreement applies to this software. + + History: + + Modified by cmetz for OPIE 2.3. Limit the length of TERM on full login. + Use HAVE_SULOG instead of DOSULOG. + Modified by cmetz for OPIE 2.2. Don't try to clear non-blocking I/O. + Use opiereadpass(). Minor speedup. Removed termios manipulation + -- that's opiereadpass()'s job. Change opiereadpass() calls + to add echo arg. Removed useless strings (I don't think that + removing the ucb copyright one is a problem -- please let me + know if I'm wrong). Use FUNCTION declaration et al. Ifdef + around some headers. Make everything static. Removed + closelog() prototype. Use the same catchexit() trickery as + opielogin. + Modified at NRL for OPIE 2.2. Changed opiestrip_crlf to + opiestripcrlf. + Modified at NRL for OPIE 2.1. Added struct group declaration. + Added Solaris(+others?) sulog capability. Symbol changes + for autoconf. Removed des_crypt.h. File renamed to + opiesu.c. Symbol+misc changes for autoconf. Added bletch + for setpriority. + Modified at NRL for OPIE 2.02. Added SU_STAR_CHECK (turning a bug + into a feature ;). Fixed Solaris shadow password problem + introduced in OPIE 2.01 (the shadow password structure is + spwd, not spasswd). + Modified at NRL for OPIE 2.01. Changed password lookup handling + to use a static structure to avoid problems with drain- + bamaged shadow password packages. Always log failures. + Make sure to close syslog by function to avoid problems + with drain bamaged syslog implementations. Log a few + interesting errors. + Modified at NRL for OPIE 2.0. + Modified at Bellcore for the S/Key Version 1 software distribution. + Originally from BSD. +*/ + +/* + * Copyright (c) 1980 Regents of the University of California. + * All rights reserved. The Berkeley software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#include "opie_cfg.h" + +#include <stdio.h> +#if HAVE_PWD_H +#include <pwd.h> +#endif /* HAVE_PWD_H */ +#include <grp.h> +#include <syslog.h> +#include <sys/types.h> +#if HAVE_SETPRIORITY && HAVE_SYS_RESOURCE_H +#if TIME_WITH_SYS_TIME +# include <sys/time.h> +# include <time.h> +#else /* TIME_WITH_SYS_TIME */ +#if HAVE_SYS_TIME_H +#include <sys/time.h> +#else /* HAVE_SYS_TIME_H */ +#include <time.h> +#endif /* HAVE_SYS_TIME_H */ +#endif /* TIME_WITH_SYS_TIME */ +#include <sys/resource.h> +#else /* HAVE_SETPRIORITY && HAVE_SYS_RESOURCE_H */ +#if TM_IN_SYS_TIME +#include <sys/time.h> +#else /* TM_IN_SYS_TIME */ +#include <time.h> +#endif /* TM_IN_SYS_TIME */ +#endif /* HAVE_SETPRIORITY && HAVE_SYS_RESOURCE_H */ +#if HAVE_STDLIB_H +#include <stdlib.h> +#endif /* HAVE_STDLIB_H */ +#if HAVE_UNISTD_H +#include <unistd.h> +#endif /* HAVE_UNISTD_H */ +#if HAVE_STRING_H +#include <string.h> +#endif /* HAVE_STRING_H */ +#include <errno.h> + +#include "opie.h" + +static char userbuf[16] = "USER="; +static char homebuf[128] = "HOME="; +static char shellbuf[128] = "SHELL="; +static char pathbuf[128] = "PATH="; +static char termbuf[32] = "TERM="; +static char *cleanenv[] = {userbuf, homebuf, shellbuf, pathbuf, 0, 0}; +static char *user = "root"; +static char *shell = "/bin/sh"; +static int fulllogin; +static int fastlogin; + +extern char **environ; +static struct passwd thisuser, nouser; + +#if HAVE_SHADOW_H +#include <shadow.h> +#endif /* HAVE_SHADOW_H */ + +#if HAVE_CRYPT_H +#include <crypt.h> +#endif /* HAVE_CRYPT_H */ + +static VOIDRET catchexit FUNCTION_NOARGS +{ + int i; + closelog(); + for (i = sysconf(_SC_OPEN_MAX); i > 2; i--) + close(i); +} + +/* We allow the malloc()s to potentially leak data out because we can +only call this routine about four times in the lifetime of this process +and the kernel will free all heap memory when we exit or exec. */ +static int lookupuser FUNCTION((name), char *name) +{ + struct passwd *pwd; +#if HAVE_SHADOW + struct spwd *spwd; +#endif /* HAVE_SHADOW */ + + memcpy(&thisuser, &nouser, sizeof(thisuser)); + + if (!(pwd = getpwnam(name))) + return -1; + + thisuser.pw_uid = pwd->pw_uid; + thisuser.pw_gid = pwd->pw_gid; + + if (!(thisuser.pw_name = malloc(strlen(pwd->pw_name) + 1))) + goto lookupuserbad; + strcpy(thisuser.pw_name, pwd->pw_name); + + if (!(thisuser.pw_dir = malloc(strlen(pwd->pw_dir) + 1))) + goto lookupuserbad; + strcpy(thisuser.pw_dir, pwd->pw_dir); + + if (!(thisuser.pw_shell = malloc(strlen(pwd->pw_shell) + 1))) + goto lookupuserbad; + strcpy(thisuser.pw_shell, pwd->pw_shell); + +#if HAVE_SHADOW + if (!(spwd = getspnam(name))) + goto lookupuserbad; + + pwd->pw_passwd = spwd->sp_pwdp; + + endspent(); +#endif /* HAVE_SHADOW */ + + if (!(thisuser.pw_passwd = malloc(strlen(pwd->pw_passwd) + 1))) + goto lookupuserbad; + strcpy(thisuser.pw_passwd, pwd->pw_passwd); + + endpwent(); + +#if SU_STAR_CHECK + return ((thisuser.pw_passwd[0] == '*') || (thisuser.pw_passwd[0] == '#')); +#else /* SU_STAR_CHECK */ + return 0; +#endif /* SU_STAR_CHECK */ + +lookupuserbad: + memcpy(&thisuser, &nouser, sizeof(thisuser)); + return -1; +} + +static VOIDRET lsetenv FUNCTION((ename, eval, buf), char *ename AND char *eval AND char *buf) +{ + register char *cp, *dp; + register char **ep = environ; + + /* this assumes an environment variable "ename" already exists */ + while (dp = *ep++) { + for (cp = ename; *cp == *dp && *cp; cp++, dp++) + continue; + if (*cp == 0 && (*dp == '=' || *dp == 0)) { + strcat(buf, eval); + *--ep = buf; + return; + } + } +} + +#if HAVE_SULOG +static int sulog FUNCTION((status, who), int status AND char *who) +{ + char *from; + char *ttynam; + struct tm *tm; + FILE *f; + time_t now; + + if (who) + from = who; + else + from = Getlogin(); + + if (!strncmp(ttynam = ttyname(2), "/dev/", 5)) + ttynam += 5; + + now = time(NULL); + tm = localtime(&now); + + if (!(f = fopen("/var/adm/sulog", "a"))) { + fprintf(stderr, "Can't update su log!\n"); + exit(1); + } + + fprintf(f, "SU %02d/%02d %02d:%02d %c %s %s-%s\n", + tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, + result ? '+' : '-', ttynam, from, user); + fclose(f); +} +#endif /* HAVE_SULOG */ + +int main FUNCTION((argc, argv), int argc AND char *argv[]) +{ + char buf[1000], *p; + struct opie opie; + int i; + char pbuf[256]; + char opieprompt[80]; + int console = 0; + +#define Getlogin() (((p = getlogin()) && *p) ? p : buf) + + for (i = sysconf(_SC_OPEN_MAX); i > 2; i--) + close(i); + + strcat(pathbuf, DEFAULT_PATH); + +again: + if (argc > 1 && strcmp(argv[1], "-f") == 0) { + fastlogin++; + argc--, argv++; + goto again; + } + if (argc > 1 && strcmp(argv[1], "-c") == 0) { + console++; + argc--, argv++; + goto again; + } + if (argc > 1 && strcmp(argv[1], "-") == 0) { + fulllogin++; + argc--; + argv++; + goto again; + } + if (argc > 1 && argv[1][0] != '-') { + user = argv[1]; + argc--; + argv++; + } + + openlog("su", LOG_ODELAY, LOG_AUTH); + atexit(catchexit); + + { + struct passwd *pwd; + + if ((pwd = getpwuid(getuid())) == NULL) { + syslog(LOG_CRIT, "'%s' failed for unknown uid %d on %s", argv[0], getuid(), ttyname(2)); +#if HAVE_SULOG + sulog(0, "unknown"); +#endif /* HAVE_SULOG */ + exit(1); + } + strcpy(buf, pwd->pw_name); + } + + if (lookupuser(user)) { + syslog(LOG_CRIT, "'%s' failed for %s on %s", argv[0], Getlogin(), ttyname(2)); +#if HAVE_SULOG + sulog(0, NULL); +#endif /* HAVE_SULOG */ + fprintf(stderr, "Unknown user: %s\n", user); + exit(1); + } + +/* Implement the BSD "wheel group" su restriction. */ +#if DOWHEEL + /* Only allow those in group zero to su to root? */ + if (thisuser.pw_uid == 0) { + struct group *gr; + if ((gr = getgrgid(0)) != NULL) { + for (i = 0; gr->gr_mem[i] != NULL; i++) + if (strcmp(buf, gr->gr_mem[i]) == 0) + goto userok; + fprintf(stderr, "You do not have permission to su %s\n", user); + exit(1); + } +userok: + ; +#if HAVE_SETPRIORITY && HAVE_SYS_RESOURCE_H + setpriority(PRIO_PROCESS, 0, -2); +#endif /* HAVE_SETPRIORITY && HAVE_SYS_RESOURCE_H */ + } +#endif /* DOWHEEL */ + + if (!thisuser.pw_passwd[0] || getuid() == 0) + goto ok; + + if (console) { + if (!opiealways(thisuser.pw_dir)) { + fprintf(stderr, "That account requires OTP responses.\n"); + exit(1); + }; + /* Get user's secret password */ + fprintf(stderr, "Reminder - Only use this method from the console; NEVER from remote. If you\n"); + fprintf(stderr, "are using telnet, xterm, or a dial-in, type ^C now or exit with no password.\n"); + fprintf(stderr, "Then run su without the -c parameter.\n"); + if (opieinsecure()) { + fprintf(stderr, "Sorry, but you don't seem to be on the console or a secure terminal.\n"); + exit(1); + }; +#if NEW_PROMPTS + printf("%s's system password: ", thisuser.pw_name); + if (!opiereadpass(pbuf, sizeof(pbuf), 0)) + goto error; +#endif /* NEW_PROMPTS */ + } else { + /* Attempt an OTP challenge */ + i = opiechallenge(&opie, user, opieprompt); + printf("%s\n", opieprompt); +#if NEW_PROMPTS + printf("%s's response: ", thisuser.pw_name); + if (!opiereadpass(pbuf, sizeof(pbuf), 1)) + goto error; +#else /* NEW_PROMPTS */ + printf("(OTP response required)\n"); +#endif /* NEW_PROMPTS */ + fflush(stdout); + }; +#if !NEW_PROMPTS + printf("%s's password: ", thisuser.pw_name); + if (!opiereadpass(pbuf, sizeof(pbuf), 0)) + goto error; +#endif /* !NEW_PROMPTS */ + +#if !NEW_PROMPTS + if (!pbuf[0] && !console) { + /* Null line entered; turn echoing back on and read again */ + printf(" (echo on)\n%s's password: ", thisuser.pw_name); + if (!opiereadpass(pbuf, sizeof(pbuf), 1)) + goto error; + } +#endif /* !NEW_PROMPTS */ + + if (console) { + /* Try regular password check, if allowed */ + if (!strcmp(crypt(pbuf, thisuser.pw_passwd), thisuser.pw_passwd)) + goto ok; + } else { + int i = opiegetsequence(&opie); + if (!opieverify(&opie, pbuf)) { + /* OPIE authentication succeeded */ + if (i < 5) + fprintf(stderr, "Warning: Change %s's OTP secret pass phrase NOW!\n", user); + else + if (i < 10) + fprintf(stderr, "Warning: Change %s's OTP secret pass phrase soon.\n", user); + goto ok; + }; + }; +error: + opieverify(&opie, ""); + fprintf(stderr, "Sorry\n"); + syslog(LOG_CRIT, "'%s' failed for %s on %s", argv[0], Getlogin(), ttyname(2)); +#if HAVE_SULOG + sulog(0, NULL); +#endif /* HAVE_SULOG */ + exit(2); + +ok: + syslog(LOG_NOTICE, "'%s' by %s on %s", argv[0], Getlogin(), ttyname(2)); +#if HAVE_SULOG + sulog(1, NULL); +#endif /* HAVE_SULOG */ + + if (setgid(thisuser.pw_gid) < 0) { + perror("su: setgid"); + exit(3); + } + if (initgroups(user, thisuser.pw_gid)) { + fprintf(stderr, "su: initgroups failed (errno=%d)\n", errno); + exit(4); + } + if (setuid(thisuser.pw_uid) < 0) { + perror("su: setuid"); + exit(5); + } + if (thisuser.pw_shell && *thisuser.pw_shell) + shell = thisuser.pw_shell; + if (fulllogin) { + if (p = getenv("TERM")) { + strncpy(termbuf, p, sizeof(termbuf)); + cleanenv[4] = termbuf; + } + environ = cleanenv; + } + if (fulllogin || strcmp(user, "root") != 0) + lsetenv("USER", thisuser.pw_name, userbuf); + lsetenv("SHELL", shell, shellbuf); + lsetenv("HOME", thisuser.pw_dir, homebuf); + +#if HAVE_SETPRIORITY && HAVE_SYS_RESOURCE_H + setpriority(PRIO_PROCESS, 0, 0); +#endif /* HAVE_SETPRIORITY && HAVE_SYS_RESOURCE_H */ + + if (fastlogin) { + *argv-- = "-f"; + *argv = "su"; + } else + if (fulllogin) { + if (chdir(thisuser.pw_dir) < 0) { + fprintf(stderr, "No directory\n"); + exit(6); + } + *argv = "-su"; + } else { + *argv = "su"; + } + + catchexit(); + +#if DEBUG + syslog(LOG_DEBUG, "execing %s", shell); +#endif /* DEBUG */ + execv(shell, argv); + fprintf(stderr, "No shell\n"); + exit(7); +} |