625 lines
21 KiB
C
625 lines
21 KiB
C
/***********************************************************************
|
|
*
|
|
* 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>
|
|
*
|
|
***********************************************************************/
|
|
|
|
|
|
//===[ Include files ]=====================================================
|
|
|
|
#include "internal.h"
|
|
|
|
//===[ Type definitions ]==================================================
|
|
|
|
#define DEFAULT_RETRY_LIFETIME 5 // seconds
|
|
|
|
//===[ Function prototypes ]===============================================
|
|
|
|
//===[ Global variables ]==================================================
|
|
|
|
//
|
|
// Debug tracing level
|
|
//
|
|
int DebugLevel = 0;
|
|
|
|
//
|
|
// Operating parameter
|
|
//
|
|
bool secureRpcSetting = false;
|
|
int retryLifetime = DEFAULT_RETRY_LIFETIME;
|
|
|
|
|
|
//++=======================================================================
|
|
static
|
|
CasaStatus
|
|
ObtainSessionToken(
|
|
IN RpcSession *pRpcSession,
|
|
IN char *pHostName,
|
|
IN AuthPolicy *pAuthPolicy,
|
|
INOUT char **ppSessionToken)
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Abstract:
|
|
//
|
|
// Notes:
|
|
//
|
|
// L0
|
|
//=======================================================================--
|
|
{
|
|
CasaStatus retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
|
|
CASA_FACILITY_AUTHTOKEN,
|
|
CASA_STATUS_UNSUCCESSFUL);
|
|
LIST_ENTRY *pListEntry;
|
|
AuthCacheEntry *pCacheEntry = NULL;
|
|
|
|
DbgTrace(1, "-ObtainSessionToken- Start\n", 0);
|
|
|
|
// Initialize output parameter
|
|
*ppSessionToken = NULL;
|
|
|
|
// Look in our cache for an entry that matches one of the auth
|
|
// contexts specified in the AuthPolicy object.
|
|
pListEntry = pAuthPolicy->authContextListHead.Flink;
|
|
while (pListEntry != &pAuthPolicy->authContextListHead)
|
|
{
|
|
AuthContext *pAuthContext;
|
|
|
|
// Get pointer to AuthContext structure
|
|
pAuthContext = CONTAINING_RECORD(pListEntry, AuthContext, listEntry);
|
|
|
|
// Try to find a cache entry for the auth context
|
|
pCacheEntry = FindEntryInAuthCache(pAuthContext->pContext, pHostName);
|
|
if (pCacheEntry != NULL)
|
|
{
|
|
// Cache entry found, update the return status with the information
|
|
// saved in it and stop looking.
|
|
retStatus = pCacheEntry->status;
|
|
break;
|
|
}
|
|
|
|
// Advance to the next entry
|
|
pListEntry = pListEntry->Flink;
|
|
}
|
|
|
|
// If we did not find a cache entry that we can use, then Try to create one.
|
|
pListEntry = pAuthPolicy->authContextListHead.Flink;
|
|
while (!CASA_SUCCESS(retStatus)
|
|
&& pListEntry != &pAuthPolicy->authContextListHead)
|
|
{
|
|
AuthContext *pAuthContext;
|
|
char *pAuthMechToken;
|
|
|
|
// Get pointer to AuthContext structure
|
|
pAuthContext = CONTAINING_RECORD(pListEntry, AuthContext, listEntry);
|
|
|
|
// Get authentication mechanism token
|
|
retStatus = GetAuthMechToken(pAuthContext, &pAuthMechToken);
|
|
if (!CASA_SUCCESS(retStatus))
|
|
{
|
|
// We were not able to obtain an authentication mechanism token
|
|
// for the context.
|
|
//
|
|
// Advance to the next entry
|
|
pListEntry = pListEntry->Flink;
|
|
continue;
|
|
}
|
|
|
|
// Create a cache entry for the auth context
|
|
pCacheEntry = CreateAuthCacheEntry(pAuthContext->pContext, pHostName);
|
|
if (pCacheEntry)
|
|
{
|
|
char *pReqMsg = NULL;
|
|
char *pRespMsg = NULL;
|
|
int respLen;
|
|
int cacheEntryLifetime = retryLifetime; // Initialize to retry in case of failure
|
|
|
|
// Request auth token for the service
|
|
pReqMsg = BuildAuthenticateMsg(pAuthContext, pAuthMechToken);
|
|
if (pReqMsg)
|
|
{
|
|
// Issue rpc
|
|
retStatus = Rpc(pRpcSession,
|
|
pAuthContext->pMechanism,
|
|
secureRpcSetting,
|
|
pReqMsg,
|
|
&pRespMsg,
|
|
&respLen);
|
|
if (CASA_SUCCESS(retStatus))
|
|
{
|
|
AuthenticateResp *pAuthenticateResp;
|
|
|
|
// Create Authenticate response object
|
|
retStatus = CreateAuthenticateResp(pRespMsg, respLen, &pAuthenticateResp);
|
|
if (CASA_SUCCESS(retStatus))
|
|
{
|
|
// Return the auth token to the caller
|
|
pCacheEntry->pToken = pAuthenticateResp->pToken;
|
|
pAuthenticateResp->pToken = NULL; // To keep us from freeing the buffer
|
|
cacheEntryLifetime = pAuthenticateResp->tokenLifetime;
|
|
|
|
// Free the Authenticate response object
|
|
RelAuthenticateResp(pAuthenticateResp);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DbgTrace(0, "-ObtainSessionToken- Authenticate Rpc failure, error = %08X\n", retStatus);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DbgTrace(0, "-ObtainSessionToken- Error building Authenticate msg\n", 0);
|
|
retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
|
|
CASA_FACILITY_AUTHTOKEN,
|
|
CASA_STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
// Add the entry to the cache if successful or if the reason that we failed
|
|
// was because the server was unavailable.
|
|
if (CASA_SUCCESS(retStatus)
|
|
|| CasaStatusCode(retStatus) == CASA_STATUS_AUTH_SERVER_UNAVAILABLE)
|
|
{
|
|
pCacheEntry->status = retStatus;
|
|
AddEntryToAuthCache(pCacheEntry, cacheEntryLifetime);
|
|
}
|
|
else
|
|
{
|
|
// Free the entry
|
|
FreeAuthCacheEntry(pCacheEntry);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DbgTrace(0, "-ObtainSessionToken- Cache entry creation failure\n", 0);
|
|
retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
|
|
CASA_FACILITY_AUTHTOKEN,
|
|
CASA_STATUS_INSUFFICIENT_RESOURCES);
|
|
|
|
// Stop trying after freeing up the buffer associated with
|
|
// the authentication mechanism token.
|
|
free(pAuthMechToken);
|
|
break;
|
|
}
|
|
|
|
// Free up the buffer associated with the authentication mechanism token
|
|
free(pAuthMechToken);
|
|
|
|
// Advance to the next entry
|
|
pListEntry = pListEntry->Flink;
|
|
}
|
|
|
|
// Return session token if successful
|
|
if (CASA_SUCCESS(retStatus))
|
|
{
|
|
// Allocate a buffer for the return token
|
|
*ppSessionToken = (char*) malloc(strlen(pCacheEntry->pToken) + 1);
|
|
if (*ppSessionToken)
|
|
{
|
|
// Copy the token onto the allocated buffer
|
|
strcpy(*ppSessionToken, pCacheEntry->pToken);
|
|
}
|
|
else
|
|
{
|
|
DbgTrace(0, "-ObtainSessionToken- Buffer allocation failure\n", 0);
|
|
retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
|
|
CASA_FACILITY_AUTHTOKEN,
|
|
CASA_STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
}
|
|
|
|
DbgTrace(1, "-ObtainSessionToken- End, retStatus = %08X\n", retStatus);
|
|
|
|
return retStatus;
|
|
}
|
|
|
|
|
|
//++=======================================================================
|
|
static
|
|
CasaStatus
|
|
ObtainAuthTokenFromServer(
|
|
IN char *pServiceName,
|
|
IN char *pHostName,
|
|
INOUT char **ppAuthToken,
|
|
INOUT int *pTokenLifetime)
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Abstract:
|
|
//
|
|
// Notes:
|
|
//
|
|
// L0
|
|
//=======================================================================--
|
|
{
|
|
CasaStatus retStatus = CASA_STATUS_SUCCESS;
|
|
RpcSession *pRpcSession;
|
|
|
|
DbgTrace(1, "-ObtainAuthTokenFromServer- Start\n", 0);
|
|
|
|
// Initialize output parameter
|
|
*ppAuthToken = NULL;
|
|
|
|
// Open Rpc Session to the auth service at the specified host
|
|
pRpcSession = OpenRpcSession(pHostName);
|
|
if (pRpcSession)
|
|
{
|
|
char *pReqMsg = NULL;
|
|
char *pRespMsg = NULL;
|
|
int respLen;
|
|
AuthPolicy *pAuthPolicy = NULL;
|
|
GetAuthPolicyResp *pGetAuthPolicyResp = NULL;
|
|
GetAuthTokenResp *pGetAuthTokenResp = NULL;
|
|
char *pSessionToken = NULL;
|
|
|
|
// Request the auth parameters associated with this service
|
|
pReqMsg = BuildGetAuthPolicyMsg(pServiceName, pHostName);
|
|
if (pReqMsg)
|
|
{
|
|
// Issue rpc
|
|
retStatus = Rpc(pRpcSession,
|
|
"GetAuthPolicy",
|
|
secureRpcSetting,
|
|
pReqMsg,
|
|
&pRespMsg,
|
|
&respLen);
|
|
if (CASA_SUCCESS(retStatus))
|
|
{
|
|
// Create GetAuthPolicy response object
|
|
retStatus = CreateGetAuthPolicyResp(pRespMsg, respLen, &pGetAuthPolicyResp);
|
|
if (CASA_SUCCESS(retStatus))
|
|
{
|
|
// Create the AuthPolicy object
|
|
retStatus = CreateAuthPolicy(pGetAuthPolicyResp->pPolicy,
|
|
pGetAuthPolicyResp->policyLen,
|
|
&pAuthPolicy);
|
|
if (CASA_SUCCESS(retStatus))
|
|
{
|
|
// Now try to obtain a session token
|
|
retStatus = ObtainSessionToken(pRpcSession, pHostName, pAuthPolicy, &pSessionToken);
|
|
if (CASA_SUCCESS(retStatus))
|
|
{
|
|
// Request auth token for the service
|
|
free(pReqMsg);
|
|
pReqMsg = BuildGetAuthTokenMsg(pServiceName, pHostName, pSessionToken);
|
|
if (pReqMsg)
|
|
{
|
|
// Free the previous response msg buffer
|
|
free(pRespMsg);
|
|
pRespMsg = NULL;
|
|
|
|
// Issue rpc
|
|
retStatus = Rpc(pRpcSession,
|
|
"GetAuthToken",
|
|
secureRpcSetting,
|
|
pReqMsg,
|
|
&pRespMsg,
|
|
&respLen);
|
|
if (CASA_SUCCESS(retStatus))
|
|
{
|
|
// Create GetAuthPolicy response object
|
|
retStatus = CreateGetAuthTokenResp(pRespMsg, respLen, &pGetAuthTokenResp);
|
|
if (CASA_SUCCESS(retStatus))
|
|
{
|
|
// Return the auth token to the caller
|
|
*ppAuthToken = pGetAuthTokenResp->pToken;
|
|
pGetAuthTokenResp->pToken = NULL; // To keep us from freeing the buffer
|
|
*pTokenLifetime = pGetAuthTokenResp->tokenLifetime;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DbgTrace(0, "-ObtainAuthTokenFromServer- GetAuthToken Rpc failure, error = %08X\n", retStatus);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DbgTrace(0, "-ObtainAuthTokenFromServer- Error building GetAuthToken msg\n", 0);
|
|
retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
|
|
CASA_FACILITY_AUTHTOKEN,
|
|
CASA_STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DbgTrace(1, "-ObtainAuthTokenFromServer- Failed to obtain session token, error = %08X\n", retStatus);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DbgTrace(0, "-ObtainAuthTokenFromServer- GetAuthPolicy Rpc failure, error = %08X\n", retStatus);
|
|
}
|
|
|
|
// Free resources that may be hanging around
|
|
if (pReqMsg)
|
|
free(pReqMsg);
|
|
|
|
if (pRespMsg)
|
|
free(pRespMsg);
|
|
|
|
if (pSessionToken)
|
|
free(pSessionToken);
|
|
|
|
if (pGetAuthTokenResp)
|
|
RelGetAuthTokenResp(pGetAuthTokenResp);
|
|
|
|
if (pGetAuthPolicyResp)
|
|
RelGetAuthPolicyResp(pGetAuthPolicyResp);
|
|
|
|
if (pAuthPolicy)
|
|
RelAuthPolicy(pAuthPolicy);
|
|
}
|
|
else
|
|
{
|
|
DbgTrace(0, "-ObtainAuthTokenFromServer- Error building GetAuthPolicy msg\n", 0);
|
|
retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
|
|
CASA_FACILITY_AUTHTOKEN,
|
|
CASA_STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
// Close the Rpc Session
|
|
CloseRpcSession(pRpcSession);
|
|
}
|
|
else
|
|
{
|
|
DbgTrace(0, "-ObtainAuthTokenFromServer- Error opening Rpc session\n", 0);
|
|
retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
|
|
CASA_FACILITY_AUTHTOKEN,
|
|
CASA_STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
DbgTrace(1, "-ObtainAuthTokenFromServer- End, retStatus = %08X\n", retStatus);
|
|
|
|
return retStatus;
|
|
}
|
|
|
|
|
|
//++=======================================================================
|
|
CasaStatus SSCS_CALL
|
|
ObtainAuthToken(
|
|
IN const char *pServiceAtHostName,
|
|
INOUT char *pAuthTokenBuf,
|
|
INOUT int *pAuthTokenBufLen)
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Abstract:
|
|
//
|
|
// Notes:
|
|
//
|
|
// L0
|
|
//=======================================================================--
|
|
{
|
|
CasaStatus retStatus = CASA_STATUS_SUCCESS;
|
|
char *pParseString;
|
|
|
|
DbgTrace(1, "-ObtainAuthToken- Start\n", 0);
|
|
|
|
// Verify the input parameters
|
|
if (pServiceAtHostName == NULL
|
|
|| pAuthTokenBufLen == NULL
|
|
|| (*pAuthTokenBufLen != 0 && pAuthTokenBuf == NULL))
|
|
{
|
|
DbgTrace(0, "-ObtainAuthToken- Invalid parameter\n", 0);
|
|
retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
|
|
CASA_FACILITY_AUTHTOKEN,
|
|
CASA_STATUS_INVALID_PARAMETER);
|
|
goto exit;
|
|
}
|
|
|
|
// Allocate space to copy the service name string
|
|
pParseString = (char*) malloc(strlen(pServiceAtHostName) + 1);
|
|
if (pParseString)
|
|
{
|
|
char *pServiceName, *pHostName;
|
|
|
|
// Space allocated, now copy the string onto it
|
|
// and parse it into its components.
|
|
strcpy(pParseString, pServiceAtHostName);
|
|
pServiceName = strtok(pParseString, "@");
|
|
pHostName = strtok(NULL, "@");
|
|
if (pHostName == NULL)
|
|
{
|
|
DbgTrace(0, "-ObtainAuthToken- Missing host name\n", 0);
|
|
retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
|
|
CASA_FACILITY_AUTHTOKEN,
|
|
CASA_STATUS_INVALID_PARAMETER);
|
|
}
|
|
else
|
|
{
|
|
AuthCacheEntry *pCacheEntry;
|
|
char *pNormalizedHostName;
|
|
|
|
// Normalize the host name
|
|
pNormalizedHostName = NormalizeHostName(pHostName);
|
|
if (pNormalizedHostName)
|
|
{
|
|
// Start user process synchronization
|
|
LockUserMutex();
|
|
|
|
// Try to find a cache entry for the service
|
|
pCacheEntry = FindEntryInAuthCache(pServiceName, pNormalizedHostName);
|
|
if (pCacheEntry == NULL)
|
|
{
|
|
// No entry found in the cache, create one.
|
|
pCacheEntry = CreateAuthCacheEntry(pServiceName, pNormalizedHostName);
|
|
if (pCacheEntry)
|
|
{
|
|
int cacheEntryLifetime = retryLifetime; // Initialize to retry in case of failure
|
|
|
|
// Cache entry created, now try to obtain auth token from the CASA Server
|
|
retStatus = ObtainAuthTokenFromServer(pServiceName,
|
|
pNormalizedHostName,
|
|
&pCacheEntry->pToken,
|
|
&cacheEntryLifetime);
|
|
|
|
// Add the entry to the cache if successful or if the reason that we failed
|
|
// was because the server was un-available.
|
|
if (CASA_SUCCESS(retStatus)
|
|
|| CasaStatusCode(retStatus) == CASA_STATUS_AUTH_SERVER_UNAVAILABLE)
|
|
{
|
|
pCacheEntry->status = retStatus;
|
|
AddEntryToAuthCache(pCacheEntry, cacheEntryLifetime);
|
|
}
|
|
else
|
|
{
|
|
// Free the entry
|
|
FreeAuthCacheEntry(pCacheEntry);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DbgTrace(0, "-ObtainAuthToken- Cache entry creation failure\n", 0);
|
|
retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
|
|
CASA_FACILITY_AUTHTOKEN,
|
|
CASA_STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Cache entry found, update the return status with the information saved in it.
|
|
retStatus = pCacheEntry->status;
|
|
}
|
|
|
|
// Try to return auth token if we have one to return
|
|
if (CASA_SUCCESS(retStatus))
|
|
{
|
|
int tokenLen = (int) strlen(pCacheEntry->pToken) + 1;
|
|
|
|
// We have an authentication token, try to return it to the caller.
|
|
if (pAuthTokenBuf)
|
|
{
|
|
// Verify that the supplied buffer is big enough
|
|
if (*pAuthTokenBufLen >= tokenLen)
|
|
{
|
|
// Return the auth token to the caller
|
|
strcpy(pAuthTokenBuf, pCacheEntry->pToken);
|
|
}
|
|
else
|
|
{
|
|
DbgTrace(0, "-ObtainAuthToken- The supplied buffer is not large enough", 0);
|
|
retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
|
|
CASA_FACILITY_AUTHTOKEN,
|
|
CASA_STATUS_BUFFER_OVERFLOW);
|
|
}
|
|
|
|
// Notify the caller about the token length
|
|
*pAuthTokenBufLen = tokenLen;
|
|
}
|
|
else
|
|
{
|
|
// The caller just wants the length of buffer that is required to
|
|
// obtain the token.
|
|
*pAuthTokenBufLen = tokenLen;
|
|
}
|
|
}
|
|
|
|
// Stop user process synchronization
|
|
FreeUserMutex();
|
|
|
|
// Free the space allocated for the normalized host name
|
|
free(pNormalizedHostName);
|
|
}
|
|
else
|
|
{
|
|
DbgTrace(0, "-ObtainAuthToken- Host name normalization failed\n", 0);
|
|
retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
|
|
CASA_FACILITY_AUTHTOKEN,
|
|
CASA_STATUS_UNSUCCESSFUL);
|
|
}
|
|
}
|
|
|
|
// Free allocated space
|
|
free(pParseString);
|
|
}
|
|
else
|
|
{
|
|
DbgTrace(0, "-ObtainAuthToken- Buffer allocation error\n", 0);
|
|
}
|
|
|
|
exit:
|
|
|
|
DbgTrace(1, "-ObtainAuthToken- End, retStatus = %08X\n", retStatus);
|
|
|
|
return retStatus;
|
|
}
|
|
|
|
|
|
//++=======================================================================
|
|
int
|
|
InitializeLibrary(void)
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Abstract:
|
|
//
|
|
// Notes:
|
|
//
|
|
// L0
|
|
//=======================================================================--
|
|
{
|
|
int retStatus = -1;
|
|
|
|
DbgTrace(1, "-InitializeLibrary- Start\n", 0);
|
|
|
|
// Create user synchronization mutex
|
|
if (CreateUserMutex() == 0)
|
|
{
|
|
// Initialize the auth cache
|
|
if (CASA_SUCCESS(InitializeAuthCache()))
|
|
{
|
|
// Initialize the host name normalization
|
|
if (CASA_SUCCESS(InitializeHostNameNormalization()))
|
|
{
|
|
// Success
|
|
retStatus = 0;
|
|
}
|
|
else
|
|
{
|
|
DbgTrace(0, "-InitializeLibrary- Error initializing host name normalization\n", 0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DbgTrace(0, "-InitializeLibrary- Error initializing the auth cache\n", 0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DbgTrace(0, "-InitializeLibrary- Error creating mutex for the user\n", 0);
|
|
}
|
|
|
|
DbgTrace(1, "-InitializeLibrary- End, retStatus = %08X\n", retStatus);
|
|
|
|
return retStatus;
|
|
}
|
|
|