/*********************************************************************** * * Copyright (C) 2006 Novell, Inc. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; version 2.1 * of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, Novell, Inc. * * To contact Novell about this file by physical or electronic mail, * you may find current contact information at www.novell.com. * * Author: Juan Carlos Luciani * ***********************************************************************/ #define _GNU_SOURCE #include #include #include #ifndef LINUX #include #endif #define PAM_SM_AUTH #define PAM_SM_ACCOUNT #define PAM_SM_PASSWORD #define PAM_SM_SESSION #include #include #include // // Environment variables set by module // static char CasaIdentityIdEnvVar[] = "IdentityId= "; static char CasaIdentitySourceNameEnvVar[] = "IdentityDataSourceName= "; static char CasaIdentitySourceUrlEnvVar[] = "IdentityDataSourceUrl= "; /* ************************************************************************ * LogError() * * Logs error to syslog. * * L2 * ************************************************************************/ static void LogError(char *pFormatStr, ... ) { va_list args; openlog("pam_casaauthtok", LOG_CONS | LOG_NOWAIT | LOG_ODELAY, LOG_USER); va_start(args, pFormatStr); vsyslog(LOG_USER | LOG_INFO, pFormatStr, args); va_end(args); closelog(); } /* ************************************************************************ * pam_sm_authenticate() * * Service provider implementation for pam_authenticate(). * * This is a PAM authentication management function. * * We are going to validate the credentials using the CASA Authentication * Token Credential APIs. * * L2 * ************************************************************************/ PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) { int retStatus = PAM_SUCCESS; bool performUsernameCheck = false; int i; char *pServicename = NULL; char *pAuthToken = NULL; // Verify input parameters if (pamh == NULL || (argc > 0 && argv == NULL)) { LogError("Invalid parameter detected"); return PAM_SYSTEM_ERR; } // Determine if we are supposed to perform the username check // based on the arguments specified. for (i = 0; i < argc; i++) { // Do safety check if (argv[i] == NULL) { LogError("Invalid parameter detected"); return PAM_SYSTEM_ERR; } if (*(argv[i]) == 'U') { // The arguments indicate that we should check the username performUsernameCheck = true; // No need to keep going through the arguments break; } } // Get the servicename. if (pam_get_item(pamh, PAM_SERVICE, (void*) &pServicename) == PAM_SUCCESS && pServicename != NULL) { // We got the service name, now check if it is necessary to perform // the username check. if (performUsernameCheck) { char *pUsername = NULL; struct pam_response *responses = NULL; // Obtain the username so that it can be checked. // . // Note that we are not calling pam_get_user() because we // assume that the service has set it before calling PAM_Authenticate. if (pam_get_item(pamh, PAM_USER, (void*) &pUsername) == PAM_SUCCESS && pUsername != NULL) { // Check if the username matches the name that we are expecting if (strcmp(pUsername, "CasaPrincipal") != 0) { LogError("Un-expected username, %s", pUsername); retStatus = PAM_USER_UNKNOWN; } } else { struct pam_conv *pConv = NULL; // The username has not been set, try to obtain it from the // application through the use of the conversation function. if (pam_get_item(pamh, PAM_CONV, (void*) &pConv) == PAM_SUCCESS && pConv != NULL) { struct pam_message msg; struct pam_message *messages = &msg; // Obtained the conversation structure, now query the conversation // function for the username. msg.msg_style = PAM_PROMPT_ECHO_ON; if (pConv->conv(1, (const struct pam_message **) &messages, &responses, pConv->appdata_ptr) == PAM_SUCCESS && responses != NULL) { // Check if we have a successful response if (responses[0].resp_retcode == PAM_SUCCESS && responses[0].resp) { // Check if the username matches the name that we are expecting if (strcmp(responses[0].resp, "CasaPrincipal") != 0) { LogError("Un-expected username, %s", responses[0].resp); retStatus = PAM_USER_UNKNOWN; } } else { LogError("Username not returned"); retStatus = PAM_CRED_INSUFFICIENT; } } else { LogError("Conversation function error"); retStatus = PAM_AUTH_ERR; } } else { LogError("Unable to obtain conversation structure"); retStatus = PAM_AUTH_ERR; } } // Free conversation function response buffers if necessary if (responses) { if (responses[0].resp) free(responses[0].resp); free(responses); } } // Proceed with the authentication token check if we have not encountered any // problems. if (retStatus == PAM_SUCCESS) { struct pam_response *responses = NULL; // Now obtain the authentication token. if (pam_get_item(pamh, PAM_AUTHTOK, (void*) &pAuthToken) != PAM_SUCCESS || pAuthToken == NULL) { struct pam_conv *pConv; // The authentication token has not been set, try to obtain it from the // application through the use of the conversation function. if (pam_get_item(pamh, PAM_CONV, (void*) &pConv) == PAM_SUCCESS) { struct pam_message msg; struct pam_message *messages = &msg; // Obtained the conversation structure, now query the conversation // function for the authentication token. msg.msg_style = PAM_PROMPT_ECHO_OFF; if (pConv->conv(1, (const struct pam_message **) &messages, &responses, pConv->appdata_ptr) == PAM_SUCCESS && responses != NULL) { // Check if we have a successful response if (responses[0].resp_retcode == PAM_SUCCESS && responses[0].resp) { // Set the authentication token with PAM if (pam_set_item(pamh, PAM_AUTHTOK, responses[0].resp) == PAM_SUCCESS) { // Use the buffer returned by the caller as the authentication token pAuthToken = responses[0].resp; } else { LogError("Unable to set the authentication token"); } } else { LogError("Token not returned"); } } else { LogError("Conversation function error"); } } else { LogError("Unable to obtain conversation structure"); } } // Check if we succeeded at obtaining the authentication token if (pAuthToken) { CasaStatus casaStatus; PrincipalIf *pPrincipalIf; // Validate the token casaStatus = ValidateAuthToken(pServicename, pAuthToken, strlen(pAuthToken), &pPrincipalIf); if (CASA_SUCCESS(casaStatus)) { size_t buffLen; // Assume success retStatus = PAM_SUCCESS; // Associate necessary environment variables with the PAM Handle buffLen = 0; casaStatus = pPrincipalIf->getIdentityId(pPrincipalIf, NULL, &buffLen); if (CasaStatusCode(casaStatus) == CASA_STATUS_BUFFER_OVERFLOW) { char *pBuff; // Allocate buffer to contain the Identity Id Environment Variable pBuff = malloc(sizeof(CasaIdentityIdEnvVar) + buffLen); if (pBuff) { // Start constructing the environment variable memcpy(pBuff, CasaIdentityIdEnvVar, sizeof(CasaIdentityIdEnvVar) - 1); // Read the value into our buffer if (CASA_SUCCESS(pPrincipalIf->getIdentityId(pPrincipalIf, pBuff + sizeof(CasaIdentityIdEnvVar) - 1, &buffLen))) { // Now set the environment variable if (pam_putenv(pamh, pBuff) != PAM_SUCCESS) { LogError("Unable to set identity id environment variable"); retStatus = PAM_SYSTEM_ERR; } // Also set the identity id as the username if (pam_set_item(pamh, PAM_USER, pBuff + sizeof(CasaIdentityIdEnvVar) - 1) != PAM_SUCCESS) { LogError("Error setting the username"); } } else { LogError("Unable to obtain identity id"); retStatus = PAM_SYSTEM_ERR; } // Free allocated buffer free(pBuff); } else { LogError("Buffer allocation failure"); retStatus = PAM_BUF_ERR; } } else { LogError("Un-expected error obtaining identity id, %08X", casaStatus); retStatus = PAM_SYSTEM_ERR; } if (retStatus == PAM_SUCCESS) { buffLen = 0; casaStatus = pPrincipalIf->getSourceName(pPrincipalIf, NULL, &buffLen); if (CasaStatusCode(casaStatus) == CASA_STATUS_BUFFER_OVERFLOW) { char *pBuff; // Allocate buffer to contain the Identity Source Name Environment Variable pBuff = malloc(sizeof(CasaIdentitySourceNameEnvVar) + buffLen); if (pBuff) { // Start constructing the environment variable memcpy(pBuff, CasaIdentitySourceNameEnvVar, sizeof(CasaIdentitySourceNameEnvVar) - 1); // Read the value into our buffer if (CASA_SUCCESS(pPrincipalIf->getSourceName(pPrincipalIf, pBuff + sizeof(CasaIdentitySourceNameEnvVar) - 1, &buffLen))) { // Now set the environment variable if (pam_putenv(pamh, pBuff) != PAM_SUCCESS) { LogError("Unable to set identity source name environment variable"); retStatus = PAM_SYSTEM_ERR; } } else { LogError("Unable to obtain identity source name"); retStatus = PAM_SYSTEM_ERR; } // Free allocated buffer free(pBuff); } else { LogError("Buffer allocation failure"); retStatus = PAM_BUF_ERR; } } else { LogError("Un-expected error obtaining identity source name, %08X", casaStatus); retStatus = PAM_SYSTEM_ERR; } } if (retStatus == PAM_SUCCESS) { buffLen = 0; casaStatus = pPrincipalIf->getSourceUrl(pPrincipalIf, NULL, &buffLen); if (CasaStatusCode(casaStatus) == CASA_STATUS_BUFFER_OVERFLOW) { char *pBuff; // Allocate buffer to contain the Identity Source Url Environment Variable pBuff = malloc(sizeof(CasaIdentitySourceUrlEnvVar) + buffLen); if (pBuff) { // Start constructing the environment variable memcpy(pBuff, CasaIdentitySourceUrlEnvVar, sizeof(CasaIdentitySourceUrlEnvVar) - 1); // Read the value into our buffer if (CASA_SUCCESS(pPrincipalIf->getSourceUrl(pPrincipalIf, pBuff + sizeof(CasaIdentitySourceUrlEnvVar) - 1, &buffLen))) { // Now set the environment variable if (pam_putenv(pamh, pBuff) != PAM_SUCCESS) { LogError("Unable to set identity source url environment variable"); retStatus = PAM_SYSTEM_ERR; } } else { LogError("Unable to obtain identity source url"); retStatus = PAM_SYSTEM_ERR; } // Free allocated buffer free(pBuff); } else { LogError("Buffer allocation failure"); retStatus = PAM_BUF_ERR; } } else { LogError("Un-expected error obtaining identity source url, %08X", casaStatus); retStatus = PAM_SYSTEM_ERR; } } if (retStatus == PAM_SUCCESS) { char *pBuff; unsigned int enumHandle = 0; size_t buff2Len; while (retStatus == PAM_SUCCESS) { // Get attribute lengths buffLen = buff2Len = 0; casaStatus = pPrincipalIf->attributeEnumerate(pPrincipalIf, &enumHandle, NULL, &buffLen, NULL, &buff2Len); if (CasaStatusCode(casaStatus) == CASA_STATUS_BUFFER_OVERFLOW) { // Allocate buffer to contain the Identity attribute Environment Variable pBuff = malloc(buffLen + 2 + buff2Len); if (pBuff) { // Read the attribute into our buffer if (CASA_SUCCESS(pPrincipalIf->attributeEnumerate(pPrincipalIf, &enumHandle, pBuff, &buffLen, pBuff + buffLen + 1, // This includes the NULL terminator &buff2Len))) { // Finish constructing the environment variable string *(pBuff + buffLen - 1) = '='; *(pBuff + buffLen) = ' '; // Now set the environment variable if (pam_putenv(pamh, pBuff) != PAM_SUCCESS) { LogError("Unable to set identity attribute environment variable"); retStatus = PAM_SYSTEM_ERR; } } else { LogError("Unable to obtain identity attribute"); retStatus = PAM_SYSTEM_ERR; } // Free allocated buffer free(pBuff); } else { LogError("Buffer allocation failure"); retStatus = PAM_BUF_ERR; } } else { // Check if we are done going through the attributes if (CasaStatusCode(casaStatus) == CASA_STATUS_NO_MORE_ENTRIES) { // Done break; } else { LogError("Un-expected error during attribute enumeration, %08X", casaStatus); retStatus = PAM_SYSTEM_ERR; } } } } // Release the principal interface instance pPrincipalIf->releaseReference(pPrincipalIf); } else { LogError("Service %s failed to authenticate with status = %08X", pServicename, casaStatus); retStatus = PAM_AUTH_ERR; } } else { LogError("Unable to obtain authentication token"); retStatus = PAM_CRED_INSUFFICIENT; } // Free conversation function response buffers if necessary if (responses) { if (responses[0].resp) free(responses[0].resp); free(responses); } } } else { LogError("Unable to obtain servicename"); retStatus = PAM_SYSTEM_ERR; } return retStatus; } /* ************************************************************************ * pam_sm_setcred() * * Service provider implementation for pam_setcred(). * * This is a PAM authentication management function. * * This function is here just for completedness and to protect against * PAM misconfiguration. * * ************************************************************************/ PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) { return PAM_SUCCESS; } /* ************************************************************************ * pam_sm_acct_mgmt() * * Service provider implementation for pam_acct_mgmt(). * * This is a PAM account management function. * * This function is here just for completedness and to protect against * PAM misconfiguration. * * ************************************************************************/ PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv) { return PAM_SUCCESS; } /* ************************************************************************ * pam_sm_chauthtok() * * Service provider implementation for pam_chauthtok(). * * This is a PAM password management function. * * This function is here just for completedness and to protect against * PAM misconfiguration. * * ************************************************************************/ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) { return PAM_SUCCESS; } /* ************************************************************************ * pam_sm_open_session() * * Service provider implementation for pam_open_session(). * * This is a PAM session management function. * * This function is here just for completedness and to protect against * PAM misconfiguration. * * ************************************************************************/ PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv) { return PAM_SUCCESS; } /* ************************************************************************ * pam_sm_close_session() * * Service provider implementation for pam_close_session(). * * This is a PAM session management function. * * This function is here just for completedness and to protect against * PAM misconfiguration. * * ************************************************************************/ PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv) { return PAM_SUCCESS; } /* static module data */ #ifdef PAM_STATIC struct pam_module _pam_casa_authtoken_modstruct = { "pam_casa_authtoken", pam_sm_authenticate, pam_sm_setcred, pam_sm_acct_mgmt, pam_sm_chauthtok, pam_sm_open_session, pam_sm_close_session }; #endif