/* 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 <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <libgen.h>
#include "util.h"

static int make_tmp_directory() {
  int ret;

  char *user_tmpdir;
  struct stat statbuf;
  mode_t old_umask;
  char *tmpdir = get_tmp_dir();

  if (tmpdir == NULL) {
    return 1;
  }
  
  /* We want 100% control of permissions */
  old_umask = umask(0000);

  /* Create $tmpdir */
  ret = mkdir(tmpdir,0711);

  /* Check whether the mkdir succeeded */
  if (ret == -1 && errno != EEXIST) {
    _log_err(LOG_NOTICE, "mkdir %s failed: %s\n", tmpdir, strerror(errno));
    return 1;
  } else if (ret != -1) {
    if (chown(tmpdir, 0, 0) == -1) {
      _log_err(LOG_NOTICE, "chown 0:0 %s failed: %s\n", tmpdir, strerror(errno));
      return 1;
    }
  }

  umask(old_umask);

  /* $tmpdir should now exist.  Make sure it is
     Check that $tmpdir is acceptable */

  ret = lstat(tmpdir,&statbuf);
  if (ret == -1) {
    _log_err(LOG_NOTICE, "%s", "lstat %s failed: %s\n", tmpdir, strerror(errno));
    return 1;
  } else if (statbuf.st_uid != 0) {
    /* Somebody else than root has grabbed /tmp/user.  Bad, bad, bad. */
    _log_err(LOG_ERR, "%s is owned by uid %d instead of root " 
	    "(uid 0). Failed to create safe $TMPDIR\n", tmpdir, 
             statbuf.st_uid);
    return 1;
  } else if (!S_ISDIR(statbuf.st_mode)) {
    _log_err(LOG_NOTICE, "%s is not a directory. Failed to create safe "
             "$TMPDIR\n", tmpdir);
    return 1;
  } else if ((statbuf.st_mode & S_IWGRP) || (statbuf.st_mode & S_IWOTH)) {
    _log_err(LOG_NOTICE, "%s is group or world writable. "
	     "Failed to create safe $TMPDIR\n", tmpdir);
    return 1;
  } else if (!(statbuf.st_mode & S_IXOTH)) {
    _log_err(LOG_NOTICE,"%s is not world searchable. "
	     "Failed to create safe $TMPDIR\n", tmpdir); 
    return 1;
  }

  if (asprintf(&user_tmpdir, "%s/%d",tmpdir,getuid()) == -1) {
    return 1;
  }

  /* Create user tmp dir */
  ret = mkdir(user_tmpdir,0700);

  if (ret == -1 && errno != EEXIST) {
    _log_err(LOG_NOTICE, "mkdir %s failed: %s\n", user_tmpdir, strerror(errno));
    return 1;
  } else if (ret != -1) {
    if (chown(user_tmpdir, getuid(), getgid()) == -1) {
      _log_err(LOG_NOTICE, "chown %d:%d %s failed: %s\n", getuid(), getgid(), 
               user_tmpdir, strerror(errno));
      return 1;
    }
  }

  ret = lstat(user_tmpdir,&statbuf);
  if (ret == -1) {
    _log_err(LOG_NOTICE, "lstat %s failed: %s\n", user_tmpdir, strerror(errno));
    return 1;
  } else if (statbuf.st_uid != getuid()) {
    _log_err(LOG_NOTICE, "%s owned by uid %d instead of uid %d. "
	     "Failed to create safe $TMPDIR\n", user_tmpdir, statbuf.st_uid, 
             getuid());
    return 1;
  } else if (!S_ISDIR(statbuf.st_mode)) {
    _log_err(LOG_NOTICE, "%s is not a directory. Failed to create safe $TMPDIR\n",
             user_tmpdir);
    return 1;
  }
  if ((statbuf.st_mode & S_IWGRP) || (statbuf.st_mode & S_IWOTH)) {
    _log_err(LOG_NOTICE, "Warning: %s is group or world writable. "
	     "Changing permissions to 0700\n", user_tmpdir);
    if (chmod(user_tmpdir, 0700) == -1) {
      _log_err(LOG_NOTICE, "chmod 0700 %s failed: %s\n", user_tmpdir, 
               strerror(errno));
      return 1;
    }
  }
  return 0;
}

int main(int argc, char **argv) {
  (void)argc;
  (void)argv;

  return make_tmp_directory();
}
