/* miCASAd PAM module
 * 
 * This is a PAM module which is used to capture the workstation
 * user/password and store the same in miCASAd .
 * This would be placed in login/xdm/gdm/kdm/sshd PAM configuration files.
 * 
 * This module needs to be present before any other PAM module which
 * requires the services of miCASAd. It needs to be present in both
 * the auth and session stacks of the PAM configuration files.
 * 
 * In the auth stack, the functionality of the  module is to store
 * the workstation user/password in micasad.
 * In the session stack, the functionality of the module is to do
 * a Close of the user's SESSION Keychain.
 * 
 */


#include "pam_sscs.h"

#define PAM_SM_AUTH
#define PAM_SM_ACCOUNT
#define PAM_SM_SESSION

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

PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh,int flags,int argc,
			const char **argv)
{
    int retVal = 0, rc = 0;

    const char *user = NULL;
    const char *wkstnPasswd = NULL;

    uid_t saved_uid = geteuid();
    struct passwd *passwdEntry = NULL;

    struct pam_message msg[1], *pmsg[1];
    struct pam_response *resp;
    struct pam_conv *conv;

    /*
     * Get the username first.
     */
    retVal = pam_get_user(pamh, &user, NULL);

    if ( PAM_SUCCESS != retVal )
    {
        pam_sscs_log( LOG_ERR, "pam_get_user returned error: %d - %s\n",retVal,pam_strerror(pamh,retVal));
        return PAM_SUCCESS;
    }

    pam_get_item(pamh,PAM_AUTHTOK,(const void**)&wkstnPasswd);
    passwdEntry = getpwnam(user);
   
    /* SSCS determines the client uid using the SO_PEERCRED socket option.
     * Hence the euid is temporarily modified to that of the user logging in.
     */


    seteuid( passwdEntry->pw_uid );

    do
    {
        char *error = NULL;
        void *ssContext         = NULL;
        SSCS_SECRETSTORE_T ssId = {0};

        SSCS_SECRET_ID_T secretID = {0};
        SSCS_SECRET_ID_T sharedSecretID = {0};

        SSCS_BASIC_CREDENTIAL basicCredential;
        int credType;

        void *nsscsIdkHandle = dlopen(NSSCSIDK_LIB,RTLD_NOW);
        if( NULL == nsscsIdkHandle )
        {
            pam_sscs_log(LOG_ERR,"Unable to open %s\n",NSSCSIDK_LIB);
            break;
        }

        pNSSCSSetCredential = dlsym( nsscsIdkHandle,
                                     "miCASASetCredential");
        if( (error = dlerror()) != NULL )
        {
            pam_sscs_log(LOG_ERR,"Unable to find miCASASetCredential symbol.- %s\n",error);
            break;
        }
      
        secretID.len = strlen(WORKSTATION_SECRET_ID) + 1;
        strcpy(secretID.id,WORKSTATION_SECRET_ID);
        
        sharedSecretID.len = strlen(WORKSTATION_SHARED_SECRET_ID) + 1;
        strcpy(sharedSecretID.id,WORKSTATION_SHARED_SECRET_ID);
        
        memset(&basicCredential,0,sizeof(basicCredential));

		if (user && wkstnPasswd)
		{
			basicCredential.unFlags = 0;
			strcpy(basicCredential.username,user);
			basicCredential.unLen = strlen(user) + 1;
			strcpy(basicCredential.password,wkstnPasswd);
			basicCredential.pwordLen = strlen(wkstnPasswd) + 1;

			retVal = (*pNSSCSSetCredential) (0,&secretID,NULL,
											SSCS_CRED_TYPE_BASIC_F,
											&basicCredential,NULL);
			if( retVal != 0)
			{ 
				pam_sscs_log( LOG_ERR,"Setting the default credential failed.Errcode = %d\n",retVal);
				break;
			}
		}
    }while(0);        

    seteuid(saved_uid);
    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)
{
     PRINT_FN_NAME
     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)
{
    const char *user = NULL;
    int retVal = 0;
    uid_t saved_uid = geteuid();
    struct passwd *passwdEntry = NULL;

    PRINT_FN_NAME

    retVal = pam_get_user(pamh, &user, NULL);
    if ( PAM_SUCCESS != retVal )
    {
        pam_sscs_log( LOG_ERR, "pam_get_user returned error: %d - %s\n",retVal,pam_strerror(pamh,retVal));
        return PAM_SUCCESS;
    }

    passwdEntry = getpwnam(user);

    seteuid( passwdEntry->pw_uid );

    do
    {
        char *error = NULL;
        void *ssContext = NULL;
        SSCS_SECRETSTORE_T ssId = {0};

        void *nsscsIdkHandle = dlopen(NSSCSIDK_LIB,RTLD_NOW);
        if( NULL == nsscsIdkHandle )
        {
            pam_sscs_log(LOG_ERR,"Unable to open %s\n",NSSCSIDK_LIB);
            break;
        }

         pNSSCSOpenSecretStoreCache = dlsym(nsscsIdkHandle,
                                        "miCASAOpenSecretStoreCache");
        if( (error = dlerror()) != NULL )
        {
            pam_sscs_log(LOG_ERR,"Unable to find miCASAOpenSecretStoreCache symbol. - %s\n",error);
            break;
        }

        pNSSCSCloseSecretStoreCache = dlsym(nsscsIdkHandle,
                                        "miCASACloseSecretStoreCache");
        if( (error = dlerror()) != NULL )
        {
            pam_sscs_log(LOG_ERR,"Unable to find miCASACloseSecretStoreCache symbol. - %s\n",error);
            break;
        }
        strcpy(ssId.ssName, passwdEntry->pw_name);
        ssId.version = NSSCS_VERSION_NUMBER;

        ssContext = (*pNSSCSOpenSecretStoreCache)(&ssId,0,NULL);
        if( NULL == ssContext )
        {
            pam_sscs_log( LOG_ERR,"Opening SecretStore for the user %s failed.\n",passwdEntry->pw_name);
            break;
        }

        retVal = (*pNSSCSCloseSecretStoreCache) (ssContext,1,NULL);    
        if( retVal != 0 )
        {
            pam_sscs_log(LOG_ERR,"Closing SecretStore for the user %s failed.\n",passwdEntry->pw_name);
            break;
        }
      
    }while(0);

    seteuid(saved_uid);
    return PAM_SUCCESS;
}

/* end of module definition */

#ifdef PAM_STATIC

/* static module data */

struct pam_module _pam_passphrase_modstruct = {
    "pam_sscs",
    pam_sm_authenticate,
    pam_sm_setcred,
    NULL,
    NULL,
    NULL
    pam_sm_acct_mgmt,
    pam_sm_open_session,
    pam_sm_close_session,
#if 0
    pam_sm_chauthtok
#endif
};

#endif