/* opieauth.c: Sitecheck program for doing opie authentication. Usage: keyauth username [ remote_host [ remote_username ] ] Prompt for an s/key password and authenticate it. Return status: 0: Success; user was authenticated, log in. 1: Failure; exit login. 2: Failure; try again (don't exit login). other: Use normal UNIX authentication. History: Created by drk. A stripped down version of opielogin.c, suitable for use as a login SITECHECK program on Silicon Graphics machines running IRIX. A SITECHECK program must be executable, owned by root, and not writable by anyone else. */ #define SITE_OK 0 #define SITE_FAIL 1 #define SITE_AGAIN 2 #define SITE_CONTINUE 3 #include "opie_cfg.h" /* OPIE: defines symbols for filenames & pathnames */ #if HAVE_SYS_PARAM_H #include #endif /* HAVE_SYS_PARAM_H */ #include #include #if HAVE_SYS_FILE_H #include #endif /* HAVE_SYS_FILE_H */ #include #if HAVE_PWD_H #include /* POSIX Password routines */ #endif /* HAVE_PWD_H */ #include #include #if HAVE_UNISTD_H #include /* Basic POSIX macros and functions */ #endif /* HAVE_UNISTD_H */ #if HAVE_STRING_H #include /* ANSI C string functions */ #endif /* HAVE_STRING_H */ #include /* File I/O functions */ #include #if HAVE_STDLIB_H #include #endif /* HAVE_STDLIB_H */ #include "opie.h" #define NMAX 32 #define HMAX 256 static char rusername[NMAX + 1]; static char name[NMAX + 1]; static char host[HMAX + 1]; static struct passwd nouser; static struct passwd thisuser; static int need_opieverify = 0; static struct opie opie; #if HAVE_SHADOW_H #include #endif /* HAVE_SHADOW_H */ extern int errno; /* * The "timeout" variable bounds the time given to login. * We initialize it here for safety and so that it can be * patched on machines where the default value is not appropriate. */ static int timeout = 120; #if HAVE_CRYPT_H #include #endif /* HAVE_CRYPT_H */ #undef TRUE #define TRUE -1 /*------------------ BEGIN REAL CODE --------------------------------*/ /* 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_NOARGS { 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(); return ((thisuser.pw_passwd[0] == '*') || (thisuser.pw_passwd[0] == '#')); lookupuserbad: memcpy(&thisuser, &nouser, sizeof(thisuser)); return -1; } static VOIDRET timedout FUNCTION((i), int i) { /* input variable declared just to keep the compiler quiet */ printf("Login timed out after %d seconds\n", timeout); syslog(LOG_CRIT, "Login timed out after %d seconds!", timeout); if (need_opieverify) { char buf[256] = ""; (void) opieverify(&opie, buf); } exit(SITE_FAIL); } int main FUNCTION((argc, argv), int argc AND char *argv[]) { int invalid; char *tty; int i; char opieprompt[OPIE_CHALLENGE_MAX + 1]; int af_pwok; int authsok = 0; char *pp; char buf[256]; int opiepassed; #ifndef DEBUG if (geteuid()) { fprintf(stderr, "This program requires super-user privileges.\n"); exit(SITE_FAIL); } #endif /* DEBUG */ for (i = sysconf(_SC_OPEN_MAX); i > 2; i--) close(i); openlog("opieauth", LOG_ODELAY, LOG_AUTH); /* initialisation */ host[0] = '\0'; rusername[0] = '\0'; opieprompt[0] = '\0'; memset(&nouser, 0, sizeof(nouser)); nouser.pw_uid = -1; nouser.pw_gid = -1; nouser.pw_passwd = "#nope"; nouser.pw_name = nouser.pw_gecos = nouser.pw_dir = nouser.pw_shell = ""; signal(SIGALRM, timedout); signal(SIGQUIT, SIG_IGN); signal(SIGINT, SIG_IGN); #ifdef DEBUG syslog(LOG_DEBUG, "my args are: (argc=%d)", i = argc); while (--i) syslog(LOG_DEBUG, "%d: %s", i, argv[i]); #endif /* DEBUG */ /* Save the username */ if (argc > 1) { strncpy(name, argv[1], sizeof(name)); name[sizeof(name) - 1] = '\0'; if (name[0] == '-') { fprintf(stderr, "User names can't start with '-'.\n"); syslog(LOG_AUTH, "Attempt to use invalid username: %s.", name); exit(SITE_AGAIN); } argc--; argv++; } else { fprintf(stderr, "Missing username argument.\n"); syslog(LOG_AUTH, "Missing username."); exit(SITE_FAIL); } /* Save the remotehost */ if (argc > 1) { strncpy(host, argv[1], sizeof(host)); host[sizeof(host) - 1] = '\0'; if (host[0] == '-') { fprintf(stderr, "Host names can't start with '-'.\n"); syslog(LOG_AUTH, "Attempt to use invalid host name: %s.", host); exit(SITE_FAIL); } argc--; argv++; } /* Save the remote username */ if (argc > 1) { strncpy(rusername, argv[1], sizeof(rusername)); rusername[sizeof(rusername) - 1] = '\0'; if (rusername[0] == '-') { fprintf(stderr, "Remote user names can't start with '-'.\n"); syslog(LOG_AUTH, "Attempt to use invalid remote username: %s.", rusername); exit(SITE_AGAIN); } argc--; argv++; } /* Check for extra args. */ if (argc > 1) { fprintf(stderr, "Too many arguments.\n"); syslog(LOG_AUTH, "Too many arguments."); exit(SITE_FAIL); } #ifdef TIOCNXCL /* BSDism: not sure how to rewrite for POSIX. rja */ ioctl(0, TIOCNXCL, 0); /* set non-exclusive use of tty */ #endif tty = ttyname(0); if (tty == (char *) 0 || *tty == '\0') tty = "UNKNOWN"; /* was: "/dev/tty??" */ #if HAVE_SETVBUF && defined(_IONBF) #if SETVBUF_REVERSED setvbuf(stdout, _IONBF, NULL, 0); setvbuf(stderr, _IONBF, NULL, 0); #else /* SETVBUF_REVERSED */ setvbuf(stdout, NULL, _IONBF, 0); setvbuf(stderr, NULL, _IONBF, 0); #endif /* SETVBUF_REVERSED */ #endif /* HAVE_SETVBUF && defined(_IONBF) */ #ifdef DEBUG syslog(LOG_DEBUG, "tty = %s", tty); #endif /* DEBUG */ invalid = TRUE; af_pwok = opieaccessfile(host); invalid = lookupuser(); #ifdef DEBUG syslog(LOG_DEBUG, "login name is +%s+, of length %d, [0] = %d", name, strlen(name), name[0]); #endif /* DEBUG */ if (feof(stdin)) exit(SITE_FAIL); /* If this user does not have a password punt back to login. */ if (! *thisuser.pw_passwd) exit(SITE_CONTINUE); #ifdef DEBUG syslog(LOG_DEBUG, "login name is +%s+, of length %d, [0] = %d\n", name, strlen(name), name[0]); #endif /* DEBUG */ /* Attempt a one-time password challenge */ i = opiechallenge(&opie, name, opieprompt); need_opieverify = TRUE; if ((i < 0) || (i > 1)) { syslog(LOG_ERR, "error: opiechallenge() returned %d, errno=%d!\n", i, errno); exit(SITE_FAIL); } printf("%s\n", opieprompt); authsok |= 1; if (!memcmp(&thisuser, &nouser, sizeof(thisuser))) if (host[0] && rusername[0]) syslog(LOG_WARNING, "Invalid login attempt for %s on %s from %s@%s.", name, tty, rusername, host); else if (host[0]) syslog(LOG_WARNING, "Invalid login attempt for %s on %s from %s.", name, tty, host); else syslog(LOG_WARNING, "Invalid login attempt for %s on %s.", name, tty); if (af_pwok && opiealways(thisuser.pw_dir)) authsok |= 2; #if DEBUG syslog(LOG_DEBUG, "af_pwok = %d, authsok = %d", af_pwok, authsok); #endif /* DEBUG */ if (!authsok) syslog(LOG_ERR, "no authentication methods are available for %s!", name); /* Only timeout based on user input delay, not NIS holdups. */ alarm(timeout); #if NEW_PROMPTS if ((authsok & 1) || !authsok) printf("Response"); if (((authsok & 3) == 3) || !authsok) printf(" or "); if ((authsok & 2) || !authsok) printf("Password"); printf(": "); fflush(stdout); if (!opiereadpass(buf, sizeof(buf), !(authsok & 2))) invalid = TRUE; #else /* NEW_PROMPTS */ if (!(authsok & 2) && authsok) printf("(OTP response required)\n"); printf("Password:"); fflush(stdout); if (!opiereadpass(buf, sizeof(buf), 0)) invalid = TRUE; #endif /* NEW_PROMPTS */ if (!buf[0] && (authsok & 1)) { authsok &= ~2; /* Null line entered, so display appropriate prompt & flush current data. */ #if NEW_PROMPTS printf("Response: "); #else /* NEW_PROMPTS */ printf(" (echo on)\nPassword:"); #endif /* NEW_PROMPTS */ if (!opiereadpass(buf, sizeof(buf), 1)) invalid = TRUE; } alarm(0); if (authsok & 1) { i = opiegetsequence(&opie); opiepassed = !opieverify(&opie, buf); need_opieverify = 0; #ifdef DEBUG syslog(LOG_DEBUG, "opiepassed = %d", opiepassed); #endif /* DEBUG */ } if (!invalid) { if ((authsok & 1) && opiepassed) { if (i < 10) { printf("Warning: Re-initialize your OTP information"); if (i < 5) printf(" NOW!"); printf("\n"); } } else if (authsok & 2) { pp = crypt(buf, thisuser.pw_passwd); invalid = strcmp(pp, thisuser.pw_passwd); } else { invalid = TRUE; } } /* If invalid, then log failure attempt data to appropriate system logfiles and close the connection. */ if (invalid) { printf("Login incorrect\n"); if (rusername[0] && host[0]) syslog(LOG_ERR, "LOGIN FAILURE ON %s FROM %.*s@%.*s, %.*s", tty, HMAX, host, sizeof(rusername), rusername, sizeof(name), name); else if (host[0]) syslog(LOG_ERR, "LOGIN FAILURE ON %s FROM %.*s, %.*s", tty, HMAX, host, sizeof(name), name); else syslog(LOG_ERR, "LOGIN FAILURE ON %s, %.*s", tty, sizeof(name), name); exit(SITE_AGAIN); } signal(SIGALRM, SIG_DFL); signal(SIGQUIT, SIG_DFL); signal(SIGINT, SIG_DFL); signal(SIGTSTP, SIG_IGN); exit(SITE_OK); }