Beispiel #1
0
/* Returns 0 if the user is successfully authenticated, and sets the appropriate group name.
 */
static int plain_auth_pass(void *ctx, const char *pass, unsigned pass_len)
{
    struct plain_ctx_st *pctx = ctx;

    if (pctx->failed || (pctx->cpass[0] != 0
                         && strcmp(crypt(pass, pctx->cpass), pctx->cpass) != 0)) {

        if (pctx->retries++ < MAX_PASSWORD_TRIES-1) {
            pctx->pass_msg = pass_msg_failed;
            return ERR_AUTH_CONTINUE;
        } else {
            syslog(LOG_AUTH,
                   "plain-auth: error authenticating user '%s'",
                   pctx->username);
            return ERR_AUTH_FAIL;
        }
    }

    if (pctx->cpass[0] == 0 && otp_file == NULL) {
        syslog(LOG_AUTH,
               "plain-auth: user '%s' has empty password and no OTP file configured",
               pctx->username);
        return ERR_AUTH_FAIL;
    }

#ifdef HAVE_LIBOATH
    if (otp_file != NULL) {
        int ret;
        time_t last;

        if (pctx->cpass[0] != 0) { /* we just checked the password */
            pctx->cpass[0] = 0;
            pctx->pass_msg = pass_msg_otp;
            return ERR_AUTH_CONTINUE;
        }

        /* no primary password -> check OTP */
        ret = oath_authenticate_usersfile(otp_file, pctx->username,
                                          pass, HOTP_WINDOW, NULL, &last);
        if (ret != OATH_OK) {
            syslog(LOG_AUTH,
                   "plain-auth: OTP auth failed for '%s': %s",
                   pctx->username, oath_strerror(ret));
            return ERR_AUTH_FAIL;
        }
    }
#endif

    if (pctx->failed)
        return ERR_AUTH_FAIL;

    return 0;
}
int
main (void)
{
  oath_rc rc;
  time_t last_otp;
  struct stat ufstat1;
  struct stat ufstat2;

  if (!oath_check_version (OATH_VERSION))
    {
      printf ("oath_check_version (%s) failed [%s]\n", OATH_VERSION,
	      oath_check_version (NULL));
      return 1;
    }

  rc = oath_init ();
  if (rc != OATH_OK)
    {
      printf ("oath_init: %s (%d)\n", oath_strerror_name (rc), rc);
      return 1;
    }

  rc = oath_authenticate_usersfile ("no-such-file", "joe", "755224",
				    0, "1234", &last_otp);
  if (rc != OATH_NO_SUCH_FILE)
    {
      printf ("oath_authenticate_usersfile[1]: %s (%d)\n",
	      oath_strerror_name (rc), rc);
      return 1;
    }

  /* Record the current usersfile inode */
  stat (CREDS, &ufstat1);

  rc = oath_authenticate_usersfile (CREDS, "joe", "755224",
				    0, "1234", &last_otp);
  if (rc != OATH_BAD_PASSWORD)
    {
      printf ("oath_authenticate_usersfile[2]: %s (%d)\n",
	      oath_strerror_name (rc), rc);
      return 1;
    }

  /* Check that we do not update usersfile on not OATH_OK */
  stat (CREDS, &ufstat2);
  if (ufstat1.st_ino != ufstat2.st_ino)
    {
      printf ("oath_authenticate_usersfile[26]: usersfile %s changed "
	      "on OATH_BAD_PASSWORD\n", CREDS);
      return 1;
    }

  rc = oath_authenticate_usersfile (CREDS, "bob", "755224",
				    0, "1234", &last_otp);
  if (rc != OATH_BAD_PASSWORD)
    {
      printf ("oath_authenticate_usersfile[3]: %s (%d)\n",
	      oath_strerror_name (rc), rc);
      return 1;
    }

  rc = oath_authenticate_usersfile (CREDS, "silver", "670691",
				    0, "4711", &last_otp);
  if (rc != OATH_OK)
    {
      printf ("oath_authenticate_usersfile[4]: %s (%d)\n",
	      oath_strerror_name (rc), rc);
      return 1;
    }

  stat (CREDS, &ufstat2);
  if (ufstat1.st_ino == ufstat2.st_ino)
    {
      printf ("oath_authenticate_usersfile[27]: usersfile %s did not "
	      "change on OATH_OK\n", CREDS);
      return 1;
    }

  rc = oath_authenticate_usersfile (CREDS, "silver", "599872",
				    1, "4711", &last_otp);
  if (rc != OATH_OK)
    {
      printf ("oath_authenticate_usersfile[5]: %s (%d)\n",
	      oath_strerror_name (rc), rc);
      return 1;
    }

  rc = oath_authenticate_usersfile (CREDS, "silver", "072768",
				    1, "4711", &last_otp);
  if (rc != OATH_OK)
    {
      printf ("oath_authenticate_usersfile[6]: %s (%d)\n",
	      oath_strerror_name (rc), rc);
      return 1;
    }

  stat (CREDS, &ufstat1);
  rc = oath_authenticate_usersfile (CREDS,
				    "foo", "755224", 0, "8989", &last_otp);
  if (rc != OATH_REPLAYED_OTP)
    {
      printf ("oath_authenticate_usersfile[7]: %s (%d)\n",
	      oath_strerror_name (rc), rc);
      return 1;
    }
  if (last_otp != 1260206742)
    {
      printf ("oath_authenticate_usersfile timestamp %ld != 1260203142\n",
	      last_otp);
      return 1;
    }

  stat (CREDS, &ufstat2);
  if (ufstat1.st_ino != ufstat2.st_ino)
    {
      printf ("oath_authenticate_usersfile[28]: usersfile %s changed "
	      "on OATH_REPLAYED_OTP\n", CREDS);
      return 1;
    }

  rc = oath_authenticate_usersfile (CREDS,
				    "rms", "755224", 0, "4321", &last_otp);
  if (rc != OATH_BAD_PASSWORD)
    {
      printf ("oath_authenticate_usersfile[8]: %s (%d)\n",
	      oath_strerror_name (rc), rc);
      return 1;
    }

  rc = oath_authenticate_usersfile (CREDS,
				    "rms", "436521", 10, "6767", &last_otp);
  if (rc != OATH_OK)
    {
      printf ("oath_authenticate_usersfile[9]: %s (%d)\n",
	      oath_strerror_name (rc), rc);
      return 1;
    }

  /*
     Run 'TZ=UTC oathtool --totp --now=2006-12-07 00 -w10' to generate:

     963013
     068866
     734019
     038980
     630208
     533058
     042289
     046988
     047407
     892423
     619507
   */

  /* Test completely invalid OTP */
  rc = oath_authenticate_usersfile (CREDS,
				    "eve", "386397", 0, "4711", &last_otp);
  if (rc != OATH_BAD_PASSWORD)
    {
      printf ("oath_authenticate_usersfile[10]: %s (%d)\n",
	      oath_strerror_name (rc), rc);
      return 1;
    }

  /* Test the next OTP but search window = 0. */
  rc = oath_authenticate_usersfile (CREDS,
				    "eve", "068866", 0, NULL, &last_otp);
  if (rc != OATH_INVALID_OTP)
    {
      printf ("oath_authenticate_usersfile[11]: %s (%d)\n",
	      oath_strerror_name (rc), rc);
      return 1;
    }

  /* Test the next OTP with search window = 1. */
  rc = oath_authenticate_usersfile (CREDS,
				    "eve", "068866", 1, NULL, &last_otp);
  if (rc != OATH_OK)
    {
      printf ("oath_authenticate_usersfile[12]: %s (%d)\n",
	      oath_strerror_name (rc), rc);
      return 1;
    }

  /* Test to replay last OTP. */
  rc = oath_authenticate_usersfile (CREDS,
				    "eve", "068866", 1, NULL, &last_otp);
  if (rc != OATH_REPLAYED_OTP)
    {
      printf ("oath_authenticate_usersfile[13]: %s (%d)\n",
	      oath_strerror_name (rc), rc);
      return 1;
    }

  /* Test to replay previous OTP. */
  rc = oath_authenticate_usersfile (CREDS,
				    "eve", "963013", 1, NULL, &last_otp);
  if (rc != OATH_REPLAYED_OTP)
    {
      printf ("oath_authenticate_usersfile[14]: %s (%d)\n",
	      oath_strerror_name (rc), rc);
      return 1;
    }

  /* Try an OTP in the future but outside search window. */
  rc = oath_authenticate_usersfile (CREDS,
				    "eve", "892423", 1, NULL, &last_otp);
  if (rc != OATH_INVALID_OTP)
    {
      printf ("oath_authenticate_usersfile[15]: %s (%d)\n",
	      oath_strerror_name (rc), rc);
      return 1;
    }

  /* Try OTP in the future with good search window. */
  rc = oath_authenticate_usersfile (CREDS,
				    "eve", "892423", 10, NULL, &last_otp);
  if (rc != OATH_OK)
    {
      printf ("oath_authenticate_usersfile[16]: %s (%d)\n",
	      oath_strerror_name (rc), rc);
      return 1;
    }

  /* Now try a rather old OTP within search window. */
  rc = oath_authenticate_usersfile (CREDS,
				    "eve", "630208", 10, NULL, &last_otp);
  if (rc != OATH_REPLAYED_OTP)
    {
      printf ("oath_authenticate_usersfile[17]: %s (%d)\n",
	      oath_strerror_name (rc), rc);
      return 1;
    }

  /* Try OTP that matches user's second line. */
  rc = oath_authenticate_usersfile (CREDS, "twouser",
				    "874680", 10, NULL, &last_otp);
  if (rc != OATH_OK)
    {
      printf ("oath_authenticate_usersfile[18]: %s (%d)\n",
	      oath_strerror_name (rc), rc);
      return 1;
    }

  /* Try OTP that matches user's third and final line. */
  rc = oath_authenticate_usersfile (CREDS, "threeuser",
				    "255509", 10, NULL, &last_otp);
  if (rc != OATH_OK)
    {
      printf ("oath_authenticate_usersfile[19]: %s (%d)\n",
	      oath_strerror_name (rc), rc);
      return 1;
    }

  /* Try OTP that matches user's third and next-to-last line. */
  rc = oath_authenticate_usersfile (CREDS, "fouruser",
				    "663447", 10, NULL, &last_otp);
  if (rc != OATH_OK)
    {
      printf ("oath_authenticate_usersfile[19]: %s (%d)\n",
	      oath_strerror_name (rc), rc);
      return 1;
    }

  /* Try incorrect OTP for user with five lines. */
  rc = oath_authenticate_usersfile (CREDS, "fiveuser",
				    "812658", 10, NULL, &last_otp);
  if (rc != OATH_INVALID_OTP)
    {
      printf ("oath_authenticate_usersfile[20]: %s (%d)\n",
	      oath_strerror_name (rc), rc);
      return 1;
    }

  /* Try OTP that matches user's second line. */
  rc = oath_authenticate_usersfile (CREDS, "fiveuser",
				    "123001", 10, NULL, &last_otp);
  if (rc != OATH_OK)
    {
      printf ("oath_authenticate_usersfile[21]: %s (%d)\n",
	      oath_strerror_name (rc), rc);
      return 1;
    }

  /* Try OTP that matches user's fourth line. */
  rc = oath_authenticate_usersfile (CREDS, "fiveuser",
				    "893841", 10, NULL, &last_otp);
  if (rc != OATH_OK)
    {
      printf ("oath_authenticate_usersfile[22]: %s (%d)\n",
	      oath_strerror_name (rc), rc);
      return 1;
    }

  /* Try another OTP that matches user's second line. */
  rc = oath_authenticate_usersfile (CREDS, "fiveuser",
				    "746888", 10, NULL, &last_otp);
  if (rc != OATH_OK)
    {
      printf ("oath_authenticate_usersfile[23]: %s (%d)\n",
	      oath_strerror_name (rc), rc);
      return 1;
    }

  /* Try another OTP that matches user's fifth line. */
  rc = oath_authenticate_usersfile (CREDS, "fiveuser",
				    "730790", 10, NULL, &last_otp);
  if (rc != OATH_OK)
    {
      printf ("oath_authenticate_usersfile[24]: %s (%d)\n",
	      oath_strerror_name (rc), rc);
      return 1;
    }

  /* Try too old OTP for user's second line. */
  rc = oath_authenticate_usersfile (CREDS, "fiveuser",
				    "692901", 10, NULL, &last_otp);
  if (rc != OATH_INVALID_OTP)
    {
      printf ("oath_authenticate_usersfile[25]: %s (%d)\n",
	      oath_strerror_name (rc), rc);
      return 1;
    }

  /* Test password field of + */
  rc = oath_authenticate_usersfile (CREDS,
				    "plus", "328482", 1, "4711", &last_otp);
  if (rc != OATH_OK)
    {
      printf ("oath_authenticate_usersfile[26]: %s (%d)\n",
	      oath_strerror_name (rc), rc);
      return 1;
    }

  rc = oath_authenticate_usersfile (CREDS,
				    "plus", "812658", 1, "4712", &last_otp);
  if (rc != OATH_OK)
    {
      printf ("oath_authenticate_usersfile[27]: %s (%d)\n",
	      oath_strerror_name (rc), rc);
      return 1;
    }

  /* Test different tokens with different passwords for one user */
  rc = oath_authenticate_usersfile (CREDS,
				    "password", "898463", 5, NULL, &last_otp);
  if (rc != OATH_OK)
    {
      printf ("oath_authenticate_usersfile[28]: %s (%d)\n",
	      oath_strerror_name (rc), rc);
      return 1;
    }

  rc = oath_authenticate_usersfile (CREDS,
				    "password", "989803", 5, "test", &last_otp);
  if (rc != OATH_OK)
    {
      printf ("oath_authenticate_usersfile[29]: %s (%d)\n",
	      oath_strerror_name (rc), rc);
      return 1;
    }

  rc = oath_authenticate_usersfile (CREDS,
				    "password", "427517", 5, "darn", &last_otp);
  if (rc != OATH_OK)
    {
      printf ("oath_authenticate_usersfile[30]: %s (%d)\n",
	      oath_strerror_name (rc), rc);
      return 1;
    }

  /* Valid OTP for first token but incorrect password. */
  rc = oath_authenticate_usersfile (CREDS,
				    "password", "917625", 5, "nope", &last_otp);
  if (rc != OATH_BAD_PASSWORD)
    {
      printf ("oath_authenticate_usersfile[31]: %s (%d)\n",
	      oath_strerror_name (rc), rc);
      return 1;
    }

  /* Valid OTP for second token but incorrect password. */
  rc = oath_authenticate_usersfile (CREDS,
				    "password", "459145", 5, "nope", &last_otp);
  if (rc != OATH_BAD_PASSWORD)
    {
      printf ("oath_authenticate_usersfile[32]: %s (%d)\n",
	      oath_strerror_name (rc), rc);
      return 1;
    }

  /* Valid OTP for first token but with password for second user. */
  rc = oath_authenticate_usersfile (CREDS,
				    "password", "917625", 5, "test", &last_otp);
  if (rc != OATH_BAD_PASSWORD)
    {
      printf ("oath_authenticate_usersfile[33]: %s (%d)\n",
	      oath_strerror_name (rc), rc);
      return 1;
    }

  /* Valid OTP for second token but with password for first user. */
  rc = oath_authenticate_usersfile (CREDS,
				    "password", "459145", 5, "", &last_otp);
  if (rc != OATH_BAD_PASSWORD)
    {
      printf ("oath_authenticate_usersfile[34]: %s (%d)\n",
	      oath_strerror_name (rc), rc);
      return 1;
    }

  /* Valid OTP for third token but with password for second user. */
  rc = oath_authenticate_usersfile (CREDS,
				    "password", "633070", 9, "test", &last_otp);
  if (rc != OATH_BAD_PASSWORD)
    {
      printf ("oath_authenticate_usersfile[35]: %s (%d)\n",
	      oath_strerror_name (rc), rc);
      return 1;
    }

  rc = oath_done ();
  if (rc != OATH_OK)
    {
      printf ("oath_done: %s (%d)\n", oath_strerror_name (rc), rc);
      return 1;
    }

  return 0;
}
PAM_EXTERN int
pam_sm_authenticate (pam_handle_t * pamh,
		     int flags, int argc, const char **argv)
{
  int retval, rc;
  const char *user = NULL;
  const char *password = NULL;
  char otp[MAX_OTP_LEN + 1];
  int password_len = 0;
  struct pam_conv *conv;
  struct pam_message *pmsg[1], msg[1];
  struct pam_response *resp;
  int nargs = 1;
  struct cfg cfg;
  char *query_prompt = NULL;
  char *onlypasswd = strdup ("");	/* empty passwords never match */

  parse_cfg (flags, argc, argv, &cfg);

  retval = pam_get_user (pamh, &user, NULL);
  if (retval != PAM_SUCCESS)
    {
      DBG (("get user returned error: %s", pam_strerror (pamh, retval)));
      goto done;
    }
  DBG (("get user returned: %s", user));

  if (cfg.try_first_pass || cfg.use_first_pass)
    {
      retval = pam_get_item (pamh, PAM_AUTHTOK, (const void **) &password);
      if (retval != PAM_SUCCESS)
	{
	  DBG (("get password returned error: %s",
		pam_strerror (pamh, retval)));
	  goto done;
	}
      DBG (("get password returned: %s", password));
    }

  if (cfg.use_first_pass && password == NULL)
    {
      DBG (("use_first_pass set and no password, giving up"));
      retval = PAM_AUTH_ERR;
      goto done;
    }

  rc = oath_init ();
  if (rc != OATH_OK)
    {
      DBG (("oath_init() failed (%d)", rc));
      retval = PAM_AUTHINFO_UNAVAIL;
      goto done;
    }

  if (password == NULL)
    {
      retval = pam_get_item (pamh, PAM_CONV, (const void **) &conv);
      if (retval != PAM_SUCCESS)
	{
	  DBG (("get conv returned error: %s", pam_strerror (pamh, retval)));
	  goto done;
	}

      pmsg[0] = &msg[0];
      {
	const char *query_template = "One-time password (OATH) for `%s': ";
	size_t len = strlen (query_template) + strlen (user);
	size_t wrote;

	query_prompt = malloc (len);
	if (!query_prompt)
	  {
	    retval = PAM_BUF_ERR;
	    goto done;
	  }

	wrote = snprintf (query_prompt, len, query_template, user);
	if (wrote < 0 || wrote >= len)
	  {
	    retval = PAM_BUF_ERR;
	    goto done;
	  }

	msg[0].msg = query_prompt;
      }
      msg[0].msg_style = PAM_PROMPT_ECHO_OFF;
      resp = NULL;

      retval = conv->conv (nargs, (const struct pam_message **) pmsg,
			   &resp, conv->appdata_ptr);

      free (query_prompt);
      query_prompt = NULL;

      if (retval != PAM_SUCCESS)
	{
	  DBG (("conv returned error: %s", pam_strerror (pamh, retval)));
	  goto done;
	}

      DBG (("conv returned: %s", resp->resp));

      password = resp->resp;
    }

  if (password)
    password_len = strlen (password);
  else
    {
      DBG (("Could not read password"));
      retval = PAM_AUTH_ERR;
      goto done;
    }

  if (password_len < MIN_OTP_LEN)
    {
      DBG (("OTP too short: %s", password));
      retval = PAM_AUTH_ERR;
      goto done;
    }
  else if (cfg.digits != 0 && password_len < cfg.digits)
    {
      DBG (("OTP shorter than digits=%d: %s", cfg.digits, password));
      retval = PAM_AUTH_ERR;
      goto done;
    }
  else if (cfg.digits == 0 && password_len > MAX_OTP_LEN)
    {
      DBG (("OTP too long (and no digits=): %s", password));
      retval = PAM_AUTH_ERR;
      goto done;
    }
  else if (cfg.digits != 0 && password_len > cfg.digits)
    {
      free (onlypasswd);
      onlypasswd = strdup (password);

      /* user entered their system password followed by generated OTP? */

      onlypasswd[password_len - cfg.digits] = '\0';

      DBG (("Password: %s ", onlypasswd));

      memcpy (otp, password + password_len - cfg.digits, cfg.digits);
      otp[cfg.digits] = '\0';

      retval = pam_set_item (pamh, PAM_AUTHTOK, onlypasswd);
      if (retval != PAM_SUCCESS)
	{
	  DBG (("set_item returned error: %s", pam_strerror (pamh, retval)));
	  goto done;
	}
    }
  else
    {
      strcpy (otp, password);
      password = NULL;
    }

  DBG (("OTP: %s", otp ? otp : "(null)"));

  {
    time_t last_otp;

    rc = oath_authenticate_usersfile (cfg.usersfile,
				      user,
				      otp, cfg.window, onlypasswd, &last_otp);
    DBG (("authenticate rc %d (%s: %s) last otp %s", rc,
	  oath_strerror_name (rc) ? oath_strerror_name (rc) : "UNKNOWN",
	  oath_strerror (rc), ctime (&last_otp)));
  }

  if (rc != OATH_OK)
    {
      DBG (("One-time password not authorized to login as user '%s'", user));
      retval = PAM_AUTH_ERR;
      goto done;
    }

  retval = PAM_SUCCESS;

done:
  oath_done ();
  free (query_prompt);
  free (onlypasswd);
  if (cfg.alwaysok && retval != PAM_SUCCESS)
    {
      DBG (("alwaysok needed (otherwise return with %d)", retval));
      retval = PAM_SUCCESS;
    }
  DBG (("done. [%s]", pam_strerror (pamh, retval)));

  return retval;
}