CASA/CASA-auth-token/server/PamSupport/pam_authtoken.c

668 lines
24 KiB
C
Raw Normal View History

/***********************************************************************
*
* 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 <jluciani@novell.com>
*
***********************************************************************/
#define _GNU_SOURCE
#include <stdarg.h>
#include <syslog.h>
#include <stdbool.h>
#ifndef LINUX
#include <security/pam_appl.h>
#endif
#define PAM_SM_AUTH
#define PAM_SM_ACCOUNT
#define PAM_SM_PASSWORD
#define PAM_SM_SESSION
#include <security/pam_modules.h>
#include <security/_pam_macros.h>
#include <casa_s_authtoken.h>
//
// 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