/* pam_tmpdir module, based on pam_env which in turn was based on
   pam_mail */

/*
 * $Id: pam_env.c,v 1.3 1999/11/08 05:46:53 morgan Exp $
 * 
 * Written by Tollef Fog Heen <tollef@add.no> 2001-02-03
 *
 * Based on pam_env
 * Written by Dave Kinchlea <kinch@kinch.ark.com> 1997/01/31
 * Inspired by Andrew Morgan <morgan@parc.power.net, who also supplied the 
 * template for this file (via pam_mail)
 */

#define _GNU_SOURCE

#include <ctype.h>
#include <errno.h>
#include <pwd.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "util.h"

/*
 * here, we make a definition for the externally accessible function
 * in this file (this definition is required for static a module
 * but strongly encouraged generally) it is used to instruct the
 * modules include file to define the function prototypes.
 */

#define PAM_SM_SESSION      /* This is primarily a AUTH_SESSION module */
#define PAM_SM_AUTH         /* But I like to be friendly */
#define PAM_SM_PASSWORD     /*        ""                 */
#define PAM_SM_ACCOUNT      /*        ""                 */

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

#define PAM_TMPDIR_HELPER "/sbin/pam-tmpdir-helper"

static int set_environment(pam_handle_t *pamh);
static int make_tmp_directory(pam_handle_t *pamh);

uid_t get_user_id(pam_handle_t *pamh) {
  char *username = malloc(25);
  struct passwd* user_entry;
  
  pam_get_item(pamh,PAM_USER,(void*)&username);
  user_entry = getpwnam(username);
  return user_entry->pw_uid;
}

gid_t get_group_id(pam_handle_t *pamh) {
  char *username = malloc(25);
  struct passwd* user_entry;
  
  pam_get_item(pamh,PAM_USER,(void*)&username);
  user_entry = getpwnam(username);
  return user_entry->pw_gid;
}

/* argument parsing */

#define PAM_DEBUG_ARG       0x01
#define PAM_NEW_CONF_FILE   0x02
#define PAM_ENV_SILENT      0x04
#define PAM_NEW_ENV_FILE    0x10

static int set_environment(pam_handle_t *pamh) {
  char *buf;
  char *tmpdir = get_tmp_dir();
  char *envs[] = { "TMP", "TMPDIR", "TEMP", "TEMPDIR", NULL };
  for (int i = 0; envs[i]; i++) {
    asprintf(&buf, "%s=%s/%d", envs[i], tmpdir, get_user_id(pamh));
    pam_putenv(pamh, buf);
    free(buf);
  }

  return 0;
}

/* --- authentication management functions (only) --- */

static int make_tmp_directory(pam_handle_t *pamh) {
  int ret = 0;
  int status;
  pid_t pid;
  struct sigaction old, new;

  new.sa_handler = SIG_DFL;
  sigemptyset(&new.sa_mask);
  new.sa_flags = 0;

  /* save old signal handler */
  ret = sigaction(SIGCHLD, &new, &old);
  if (ret == -1) {
    _log_err(LOG_WARNING, "could not save SIGCHLD handler");
    return 1;
  }

  /* Do fork and exec by hand.. */
  pid = fork();
  if (!pid) {
    /* child */
    if (geteuid() == 0)
      setuid(get_user_id(pamh));
    execl(PAM_TMPDIR_HELPER,PAM_TMPDIR_HELPER,NULL);
  }

  ret = waitpid(pid, &status, 0);
  if (ret == -1) {
    _log_err(LOG_ERR, "error waiting for helper binary (%d)\n", errno);
    return 1;
  }

  /* restore old signal handler */
  ret = sigaction(SIGCHLD, &old, NULL);
  if (ret == -1) {
    _log_err(LOG_WARNING, "could not restore SIGCHLD handler");
    return 1;
  }

  return WEXITSTATUS(status);
}


PAM_EXTERN
int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc,
			const char **argv)
{ 
  (void)pamh;
  (void)flags;
  (void)argc;
  (void)argv;
  return PAM_IGNORE;
}

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

  if (make_tmp_directory(pamh) == 0) {
    set_environment(pamh);
    return PAM_SUCCESS;
  } else {
    return PAM_ABORT;
  }
}

PAM_EXTERN 
int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, 
		     const char **argv)
{
  (void)pamh;
  (void)flags;
  (void)argc;
  (void)argv;

  _log_err(LOG_NOTICE, "pam_sm_acct_mgmt called inappropriatly");
  return PAM_SERVICE_ERR;
}
 
PAM_EXTERN
int pam_sm_open_session(pam_handle_t *pamh,int flags,int argc, const char **argv)
{
  (void)pamh;
  (void)flags;
  (void)argc;
  (void)argv;

  if (make_tmp_directory(pamh) == 0) {
    set_environment(pamh);
    return PAM_SUCCESS;
  } else {
    return PAM_ABORT;
  }
  
}

PAM_EXTERN
int pam_sm_close_session(pam_handle_t *pamh,int flags,int argc,
			 const char **argv)
{
  (void)pamh;
  (void)flags;
  (void)argc;
  (void)argv;

  D(("Called and Exit"));
  return PAM_SUCCESS;
}

PAM_EXTERN 
int pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, 
		     const char **argv)
{
  (void)pamh;
  (void)flags;
  (void)argc;
  (void)argv;

  _log_err(LOG_NOTICE, "pam_sm_chauthtok called inappropriatly");
  return PAM_SERVICE_ERR;
}

#ifdef PAM_STATIC

/* static module data */

struct pam_module _pam_env_modstruct = {
     "pam_tmpdir",
     pam_sm_authenticate,
     pam_sm_setcred,
     pam_sm_acct_mgmt,
     pam_sm_open_session,
     pam_sm_close_session,
     pam_sm_chauthtok,
};

#endif

/* end of module definition */
