/*
 * pam_sshauth: PAM module for authentication via a remote ssh server.
 * Copyright (C) 2010-2013 Scott Balneaves <sbalneav@ltsp.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

/*
 * I met a traveller from an antique land
 * Who said: `Two vast and trunkless legs of stone
 * Stand in the desert. Near them, on the sand,
 * Half sunk, a shattered visage lies, whose frown,
 * And wrinkled lip, and sneer of cold command,
 * Tell that its sculptor well those passions read
 * Which yet survive, stamped on these lifeless things,
 * The hand that mocked them and the heart that fed.
 * And on the pedestal these words appear --
 * "My name is Ozymandias, king of kings:
 * Look on my works, ye Mighty, and despair!"
 * Nothing beside remains. Round the decay
 * Of that colossal wreck, boundless and bare
 * The lone and level sands stretch far away.'
 *
 * --Percy Bysshe Shelley
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <syslog.h>
#include <config.h>
#include <pwd.h>

/*
 * PAM_SM_* define.
 */

#define PAM_SM_AUTH             /* supports Authentication */
#define PAM_SM_SESSION          /* supports Session management */

#include <security/pam_modules.h>
#include <security/pam_ext.h>

#include "pam_sshauth.h"

/*
 * Globals.
 */

int psadebug;                   /* Debug flag */
int nostrict;                   /* nostrict flag */
int authtries = AUTHTRIES;      /* Number of times we'll try to authenticate */
int try_first_pass;             /* Try to obtain auth token from pam stack */
int askpass;                    /* Support shm_askpass */

/*
 * Authentication function
 */

PAM_EXTERN int
pam_sm_authenticate (pam_handle_t * pamh, int flags, int argc,
                     const char **argv)
{
  const char *username;
  int pam_result;
  char *host = getenv (HOST);
  char *port = getenv (PORT);
  char *display = getenv ("DISPLAY");
  struct passwd *pwent;

  /*
   * Valid pam handle?
   */

  if (pamh == NULL)
    {
      return PAM_SYSTEM_ERR;
    }

  /*
   * Get the username.
   */

  pam_result = pam_get_user (pamh, &username, NULL);
  if (pam_result != PAM_SUCCESS)
    {
      pam_syslog (pamh, LOG_ERR, "Couldn't determine username.");
      return pam_result;
    }

  /*
   * Is it a system user?  Succeed.
   */

  pam_debug (pamh, "username %s", username);

  if ((pwent = getpwnam (username)) != NULL)
    {
      if (pwent->pw_uid < UID_MIN)
        {
          return PAM_SUCCESS;
        }
    }

  pam_process_args (pamh, argc, argv, &host, &port);
  pam_debug (pamh, "Authentication begins.");

  /*
   * Process the hostname
   */

  if ((pam_result = sshauth_pam_env (pamh, HOST, host, NOCLEANUP)) != PAM_SUCCESS)
    {
      return pam_result;
    }

  /*
   * Process the port
   */

  if ((pam_result = sshauth_pam_env (pamh, PORT, port, NOCLEANUP)) != PAM_SUCCESS)
    {
      return pam_result;
    }

  /*
   * Process the DISPLAY.  Set PAM_XDISPLAY if applicable.
   */

  if ((pam_result = sshauth_pam_env (pamh, PAMXDISPLAY, display, NOCLEANUP)) != PAM_SUCCESS)
    {
      return pam_result;
    }

  /*
   * Create our temp dir.
   */

  pam_result = create_sshauthdir (pamh, username);
  if (pam_result != PAM_SUCCESS)
    {
      pam_syslog (pamh, LOG_ERR, "Couldn't create sshauthdir.");
      return pam_result;
    }

  /*
   * Perform the authentication.
   */

  pam_result = do_sshauth (pamh, username);

  pam_debug (pamh, "Authentication finished.");
  if (pam_result == PAM_SUCCESS && askpass > 0)
    {
      pam_debug (pamh, "Handling shm_askpass support.");
      pam_result = askpass_create (pamh);
    }

  return pam_result;
}

PAM_EXTERN int
pam_sm_setcred (pam_handle_t * pamh, int flags, int argc, const char **argv)
{
  return PAM_SUCCESS;
}

PAM_EXTERN int
pam_sm_open_session (pam_handle_t * pamh, int flags, int argc, const char **argv)
{
  return PAM_SUCCESS;
}

PAM_EXTERN int
pam_sm_close_session (pam_handle_t * pamh, int flags, int argc, const char **argv)
{
  int pam_result;
  const char *username;
  struct passwd *pwent;
  char *host, *port;

  /*
   * Valid pam handle?
   */

  if (pamh == NULL)
    {
      return PAM_SYSTEM_ERR;
    }

  /*
   * Get the username.
   */

  pam_result = pam_get_user (pamh, &username, NULL);
  if (pam_result != PAM_SUCCESS)
    {
      pam_syslog (pamh, LOG_ERR, "Couldn't determine username.");
      return pam_result;
    }

  /*
   * Is it a system user?  Succeed.
   */


  if ((pwent = getpwnam (username)) != NULL)
    {
      if (pwent->pw_uid < UID_MIN)
        {
          return PAM_SUCCESS;
        }
    }

  pam_process_args (pamh, argc, argv, &host, &port);

  if (askpass)
    {
      return askpass_remove (pamh);
    }

  return PAM_SUCCESS;
}
