/* PAM to automate Uni-Klu login tasks */

/* 
 * Albrecht Gebhardt <agebhard@uni-klu.ac.at>
 * Copyright: GPL, See COPYING.LIB-2.0
 *
 * Created Sun Jun 16 21:12:20 CEST 2002
 * 
 * Version:
 * $Id: pam_linux_uniklu_sess.c,v 1.11 2009-01-11 15:38:07 agebhard Exp $
 * 
 * Last Revised on
 * $Date: 2009-01-11 15:38:07 $
 * 
 * Changelog:
 *
 * $Log: pam_linux_uniklu_sess.c,v $
 * Revision 1.11  2009-01-11 15:38:07  agebhard
 * reduce compiler warnings
 *
 * Revision 1.10  2009-01-09 10:17:09  agebhard
 * ...
 *
 * Revision 1.9  2004/10/30 18:07:25  agebhard
 * docu reorganized
 * ignore_root works now
 *
 * Revision 1.8  2004/10/30 16:32:50  agebhard
 * fixed kdm crash in close_session
 * fixed wrong password length in kdm session
 * added more expect scripts, untested
 *
 * Revision 1.7  2004/10/29 22:49:59  agebhard
 * sort of works now also for kdm
 * short passwords are read back with extra characters, leads to wrong degarble
 *
 * Revision 1.6  2004/10/28 21:40:47  agebhard
 * working again (debian sarge)
 *
 * Revision 1.5  2004/10/28 18:31:43  agebhard
 * rename mount-home program to uniklu_helper
 *
 * Revision 1.4  2002/06/18 13:25:09  agebhard
 * new debugging. still not working on madrake.
 * pam_sm_authenticate fails because it is called before entereing a password!
 *
 * Revision 1.3  2002/06/17 09:57:28  agebhard
 * more cvs keywords ...
 *
 * 
 */

#define PAM_SM_SESSION

#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <syslog.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <security/pam_modules.h>
#include "pam_linux_uniklu.h"

PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags,
				   int argc, const char **argv)
{
    const char *procname="pam_sm_open_session";
    char *username=NULL, *password=NULL;
    const char *uniklu_helper_program = UNIKLU_HELPER_PROGRAM;
    char *buf;
    int	retval, i, authenticate=0, ignore_root=0, is_root=0;

#   ifdef DEBUG   
    syslog(LOG_DEBUG, "%s",procname);
#   endif
    /* get values from environment */
    //    (const char *) mount_home_program = 
    //                   pam_getenv(pamh, "UNIKLU_HELPER_PROGRAM");

    /* handle arguments */
    for (i=0; i<argc; i++)
	{
#           ifdef DEBUG   
	    syslog(LOG_DEBUG, "%s: Argument #%d/%d: '%s'", procname, i, argc,
		   argv[i]); 
#           endif
	    /* get uniklu_helper program name (if applicable) */
	    if (strcmp(argv[i], "uniklu_helper_program") == 0)
		{
		    /* next argument is uniklu_helper program name */
		    i++;
                    if((buf=(char*) malloc(sizeof(char)*(strlen(argv[i])+1))) ==NULL){
                        syslog(LOG_CRIT, "%s: malloc for uniklu_helper_program parameter failed!",procname);
                        return(PAM_SESSION_ERR);
                    }

		    strncpy(buf, argv[i], strlen(argv[i]));
                    buf[strlen(argv[i])]='\0';
                    uniklu_helper_program=buf;
#                   ifdef DEBUG   
		    syslog(LOG_DEBUG, "%s: uniklu_helper_program: '%s'", procname,
			   argv[i]);
#                   endif
		}
	    else if(strcmp(argv[i], "authenticate") == 0){
		authenticate=1;
	    }
	    else if (strcmp(argv[i], "ignore_root") == 0)
		{
		    /* skip root logins */
                    ignore_root=1;
		}
	    else
		{
		    /* unknown option! write to syslog! */
		    syslog(LOG_ERR, "%s: Unknown option '%s'", procname,
			   argv[i]);
		}
	    
	}
#   ifdef DEBUG   
    syslog(LOG_DEBUG, "%s: all options read", procname);
#   endif

    if(authenticate){
	/* get user name */
	retval = pam_get_user(pamh, (void *) &username, NULL);
	if (retval != PAM_SUCCESS){
	    syslog(LOG_CRIT, "%s: pam_get_user failed: %s\n",procname, 
		   pam_strerror(pamh,retval));
	    return(PAM_SESSION_ERR);
	}
#       ifdef DEBUG   
	syslog(LOG_DEBUG, "%s: username '%s'", procname,username);
#       endif
        
        /* is root logging in? */
        if (strcmp(username, "root") == 0) is_root=1;

        if (is_root && ignore_root)
            {
#               ifdef DEBUG
                syslog(LOG_DEBUG, "%s: ignore root login",procname);
#               endif    
                return(PAM_SUCCESS);
            }
	
	/* retrieve protected password 
	 * method 1: from PAM data using pam_get_data
	 */
	retval = pam_get_data(pamh, UNIQUE_DATA_NAME, (void *) &password); 
	if (retval != PAM_SUCCESS) {
	    syslog(LOG_WARNING, "%s: pam_get_data failed: %s\n",procname, 
		   pam_strerror(pamh,retval));
	    /* try method 2: from PAM environment using pam_getenv
	     * this is neccessary for kdm which doesn't call pam_sm_setcred 
	     * before pam_sm_open_session
	     */

            password=(char *)pam_getenv(pamh, "GARBLED_PASSWORD");
            if(password==NULL) {
	      syslog(LOG_CRIT, "%s: pam_getenv for GARBLED_PASSWORD failed.\n",
	             procname);
              return(PAM_SESSION_ERR);
            };

	}
#       ifdef DEBUG   
	syslog(LOG_DEBUG, "%s: User '%s' garbled Password '%s'", 
	       procname, username, PASSWD); 
#       endif
	retval = pam_linux_uniklu_garble_string(pamh, password, 
						strlen(password)); 
	if (retval != PAM_SUCCESS) {
	    syslog(LOG_CRIT, "%s: pam_linux_uniklu_garble_string failed: %s\n",
		   procname, 
		   pam_strerror(pamh,retval));
	    return(PAM_SESSION_ERR);
	}
#       ifdef DEBUG   
	syslog(LOG_DEBUG, "%s: User '%s' degarbled Password '%s'", 
	       procname, username, PASSWD); 
#       endif
	
 
	/* fork uniklu_helper */
	retval = pam_linux_uniklu_helper(username, password,
					 uniklu_helper_program);
    
	if (retval != PAM_SUCCESS) {
	    syslog(LOG_CRIT, "%s: pam_linux_uniklu_helper failed: %s\n",procname, 
		   pam_strerror(pamh,retval));
	    return(PAM_SESSION_ERR);
	}
	
	retval = pam_linux_uniklu_garble_string(pamh, password, strlen(password));
    }
    if (retval == PAM_SUCCESS) return(retval);
    return(PAM_SESSION_ERR);
}

/* needs CLOSE_SESSION yes in /etc/login.defs !! */

PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags,
				    int argc, const char **argv)
{
    const char *procname="pam_sm_close_session";
    const char *uniklu_helper_program = UNIKLU_HELPER_PROGRAM;
    char *username=NULL, *buf;
    int	retval, i, authenticate=0, ignore_root=0, is_root=0;

#   ifdef DEBUG   
    syslog(LOG_DEBUG, "%s",procname);
#   endif

    /* get values from environment */
    uniklu_helper_program = pam_getenv(pamh, "UNIKLU_HELPER_PROGRAM");

    /* get user name */
    retval = pam_get_user(pamh, (void *) &username, NULL);
    if (retval != PAM_SUCCESS) return(retval);

    /* is root logging in? */
    if (strcmp(username, "root") == 0) is_root=1;

 
    /* handle arguments */
    for (i=0; i<argc; i++)
	{
#           ifdef DEBUG   
	    syslog(LOG_DEBUG, "%s: Argument #%d: '%s'", procname, i, argv[i]);
#           endif
	    /* get uniklu_helper program name (if applicable) */
	    if (strcmp(argv[i], "uniklu_helper_program") == 0)
		{
		    /* next argument is uniklu_helper program name */
		    i++;
		    if(argv[i]){
                    if((buf=(char*) malloc(sizeof(char)*(strlen(argv[i])+1))) ==NULL){
                        syslog(LOG_CRIT, "%s: malloc for uniklu_helper_program parameter failed!",procname);
                        return(PAM_SESSION_ERR);
                    }

		    strncpy(buf, argv[i], strlen(argv[i]));
                    buf[strlen(argv[i])]='\0';
                    uniklu_helper_program=buf;
#                   ifdef DEBUG   
		    syslog(LOG_DEBUG, "%s: uniklu_helper_program: '%s'", procname,
			   argv[i]);
#                   endif
		    } else {
                        syslog(LOG_CRIT, "%s: uniklu_helper_program parameter incomplete!",procname);
                        return(PAM_SESSION_ERR);
		    }
            }
	    else if(strcmp(argv[i], "authenticate") == 0){
		authenticate=1;
	    }
	    else if (strcmp(argv[i], "ignore_root") == 0)
		{
		    /* skip root logins */
                    ignore_root=1;
		}
	    else
		{
		    /* unknown option! write to syslog! */
		    syslog(LOG_ERR, "%s: Unknown option '%s'", procname,
			   argv[i]);
		}
	}
    
    /* nothing to do, may be umount-home later? */
    return(PAM_SUCCESS);
 
}
