/*
 * Password / Login checker. Developed on Linux, might work elsewhere.
 * Supports /etc/passwd and /etc/shadow.
 *
 * Reads login:password (in plain text) from standard input, and prints
 * "true" on standard input if authentication is successful, "false" or
 * nothing otherwise.
 *
 * Should be pretty secure.
 *
 * (c) Frank Pilhofer 1999, fp@fpx.de.
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <shadow.h>
#include <signal.h>
#include <string.h>
#include <sys/stat.h>

char login[64], password[64];

void
sighdlr (int sig)
{
  memset (login, 0, 64);
  memset (password, 0, 64);
  printf ("false\n");
  exit (1);
}

int
main (int argc, char *argv[])
{
  struct passwd * pwdata;
  struct spwd * spwdata;
  char *pwd, *ptr;
  int i, c;

  umask  (0777);
  chdir  ("/");
  signal (SIGINT,  sighdlr);
  signal (SIGQUIT, sighdlr);
  signal (SIGALRM, sighdlr);
  signal (SIGPIPE, sighdlr);

  for (i=0; i<argc; i++) {
    if (argv[i]) {
      memset (argv[i], 0, strlen (argv[i]));
    }
  }

  for (i=0, ptr=login; i<63 && !feof (stdin); i++, ptr++) {
    if ((c = fgetc (stdin)) == EOF)
      break;
    if (c == ':')
      break;
    *ptr = c;
  }
  *ptr = '\0';
  
  for (i=0, ptr=password; i<63 && !feof (stdin); i++, ptr++) {
    if ((c = fgetc (stdin)) == EOF)
      break;
    if (c == '\n' || c == '\r')
      break;
    *ptr = c;
  }
  *ptr = '\0';

  if (!login[0]) {
    goto false;
  }

  if ((pwdata = getpwnam (login)) == NULL) {
    goto false;
  }

  /*
   * Empty Password
   */

  if (strlen (pwdata->pw_passwd) == 0 && !password[0]) {
    goto true;
  }

  /*
   * Shadow Password ?
   */

  if (strlen (pwdata->pw_passwd) == 13) {
    pwd = pwdata->pw_passwd;
  }
  else {
    if ((spwdata = getspnam (login)) == NULL) {
      goto false;
    }
    /*
     * Empty password
     */
    if (strlen (spwdata->sp_pwdp) == 0 && !password[0]) {
      goto true;
    }
    if (strlen (spwdata->sp_pwdp) != 13) {
      goto false;
    }
    pwd = spwdata->sp_pwdp;
  }

  /*
   * Encrypt user's password and compare
   */

  if ((ptr = crypt (password, pwd)) == NULL) {
    goto false;
  }

  if (strncmp (ptr, pwd, 13) == 0) {
    goto true;
  }

false:
  memset (login, 0, 64);
  memset (password, 0, 64);
  printf ("false\n");
  exit (1);

true:
  memset (login, 0, 64);
  memset (password, 0, 64);
  printf ("true\n");
  exit (0);
}
