#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include <linux/vt.h>
#include <errno.h>
#include <fcntl.h>

#define PAM_SM_AUTH
#define PAM_SM_ACCOUNT
#define PAM_SM_SESSION
#define PAM_SM_PASSWORD

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

#define AUTH_DIR "/var/run/console"
#define DATANAME "pam_foreground_console_file"

static void
cleanup(pam_handle_t *pamh, void *filename, int err)
{
	unlink((char *)filename);
        free(filename);
	filename = NULL;
}


/* --- authentication management functions --- */

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

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

/* --- account management functions --- */

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

/* --- password management --- */

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

/* --- session management --- */

PAM_EXTERN int
pam_sm_open_session(pam_handle_t *pamh , int flags ,
		    int argc , const char **argv )
{
	const char *user=NULL;
	struct stat finfo;
	char *filename;
	unsigned short console;
	int fd;
	struct vt_stat vtstat;
	size_t len;
	int retval;
	void *rhost=NULL;

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

	retval =  pam_get_item(pamh, PAM_RHOST, (const void **)&rhost);
	if (retval != PAM_SUCCESS) {
		D(("get remote host returned error: %s", 
		   pam_strerror(pamh,retval)));
		return retval;
	}
	
	/* Skip lockfile creation when non-local */
	if (rhost != NULL && *(char *)rhost != '\0') {
		return PAM_SUCCESS;
	}
	if (stat (AUTH_DIR, &finfo)) {
		if (errno != ENOENT) {
			D(("Can't access %s - %s\n", AUTH_DIR, 
			   strerror(errno)));
			goto fail;
		} else {
			if (mkdir(AUTH_DIR, 0755)) {
				D(("Can't mkdir %s - %s\n", AUTH_DIR, 
				   strerror(errno)));
				goto fail;
			}
			stat (AUTH_DIR, &finfo);
		}
	}

	/* Check that it's a directory */

	if (!S_ISDIR(finfo.st_mode)) {
		D(("%s isn't a directory\n",AUTH_DIR));
		goto fail;
	}
		
	fd = open("/dev/console", O_RDWR);
	if (fd < 1) {
		D(("Can't open console - %s\n",strerror(errno)));
		goto fail;
	}

	if (ioctl(fd, VT_GETSTATE, &vtstat)) {
		D(("ioctl failed - %s\n",strerror(errno)));
		goto fail;
	}

	close (fd);

	console = vtstat.v_active;

	if (console > 99) {
		D(("Console number too large\n"));
		goto fail;
	}

	len = strlen(user);
	len += strlen(AUTH_DIR);

	len += 5;

	filename = malloc(sizeof(char) * len);

	if (filename == NULL) {
		D(("Malloc failed\n"));
		goto fail;
	}

	sprintf(filename,"%s/%s:%d",AUTH_DIR,user,console);

	fd = open(filename,O_CREAT|O_WRONLY|O_EXCL,0644);
	
	if (fd < 1) {
		D(("Can't open %s - %s\n",filename,strerr(errno)));
		goto free;
	}

	/* HURRAH WE GET THERE IN THE END */

	close(fd);

	/* Store the damn thing */

	if (pam_set_data(pamh, DATANAME, (void *) filename, cleanup) != PAM_SUCCESS) {
		D(("Can't store data\n"));
		goto remove;
	}

	user = NULL;
	return PAM_SUCCESS;
 remove:
	unlink(filename);
 free:
	free(filename);
	close(fd);
 fail:
	user = NULL;
	return PAM_SESSION_ERR;

}

PAM_EXTERN int
pam_sm_close_session(pam_handle_t *pamh, int flags,
		     int argc, const char **argv)
{
	/* If errors happen here, we're screwed. So. */
	void *filename;
	if (pam_get_data(pamh, DATANAME, (const void **) &filename) == PAM_SUCCESS) {
		unlink((char *)filename);
	}

	return PAM_SUCCESS;
}
