/***********************************************************************
 * 
 *  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"

//===[ Manifest constants ]================================================

#define  APPLICATION_NOT_MULTI_THREADED   "CASA_APPLICATION_NOT_MULTI_THREADED"

#define DOMAIN_SOCKET_FILE_NAME "/var/lib/CASA/authtoken/validate/socket"

//===[ Type definitions ]==================================================

//===[ Function prototypes ]===============================================

//===[ Global variables ]==================================================

// Debug Level
int   DebugLevel = 0;

//
// Initialization variables
//
static
bool  g_moduleInitialized = false;

//
// Configuration variables
//
bool  g_multiThreadedApplication = true;

//
// IPC Client Sub-system variables
//
PFN_IpcClientInit                    g_ipcInitPtr = NULL;
PFN_IpcClientShutdown                g_ipcShutdownPtr = NULL;
//PFN_IpcClientOpenInetRemoteEndPoint  g_ipcOpenEndPointPtr = NULL;
PFN_IpcClientOpenUnixRemoteEndPoint  g_ipcOpenEndPointPtr = NULL;
PFN_IpcClientCloseRemoteEndPoint     g_ipcCloseEndPointPtr = NULL;
PFN_IpcClientSubmitReq               g_ipcSubmitReq = NULL;

uint32_t g_atvsEndPointHandle;   // Authentication Token Validation Service endpoint handle


//++=======================================================================
CasaStatus SSCS_CALL
ValidateAuthToken(
   IN       const char  *pServiceName,
   IN       const char  *pTokenBuf,
   IN       const int   tokenBufLen,
   INOUT    PrincipalIf **ppPrincipalIf)
//
// Arguments:  
//    pServiceName -
//       Pointer to NULL terminated string that contains the
//       name of the service targeted by the token.
//               
//    pTokenBuf -
//       Pointer to buffer that will receive the authentication
//       token. The length of this buffer is specified by the
//       pTokenBufLen parameter. Note that the the authentication
//       token will be in the form of a NULL terminated string.
//
//    tokenBufLen -
//       Length of the data contained within the buffer pointed
//       at by pTokenBuf.
//   
//    ppPrincipalIf -
//       Pointer to variable that will receive a pointer to a principal
//       interface with information about the authenticated entity.
//       IMPORTANT NOTE: The caller is responsible for releasing the
//       interface after it is done with it to avoid a resource leak.
//   
// Returns:
//    Casa status.
//                           
// Description:
//    Validates authentication token.
//
// L2
//=======================================================================--
{
   CasaStatus        retStatus;
   char              *pDecodedTokenBuf;
   int               decodedTokenBufLen;
   PrincipalIf       *pPrincipalIf;

   DbgTrace(1, "-ValidateAuthToken- Start\n", 0);

   // Validate input parameters
   if (pServiceName == NULL
       || pTokenBuf == NULL
       || tokenBufLen == 0
       || ppPrincipalIf == NULL)
   {
      DbgTrace(0, "-ValidateAuthToken- Invalid input parameter\n", 0);

      retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                  CASA_FACILITY_AUTHTOKEN,
                                  CASA_STATUS_INVALID_PARAMETER);
      goto exit;
   }

   // Make sure that the module has been initialized
   if (g_moduleInitialized == false)
   {
      // The module has not been initialized, synchronize access thought this section
      // to avoid having two threads performing initialization.
      AcquireModuleMutex;

      // Assume success
      retStatus = CASA_STATUS_SUCCESS;

      // Check again in case another thread pre-empted us.
      if (g_moduleInitialized == false)
      {
         // Initialize the ConfigIf complex
         retStatus = ConfigIfInit();
         if (CASA_SUCCESS(retStatus))
         {
            // Initialize the PrincipalIf complex
            retStatus = PrincipalIfInit();
            if (CASA_SUCCESS(retStatus))
            {
               // Initialize the IdenToken complex
               retStatus = IdenTokenInit();
               if (CASA_SUCCESS(retStatus))
               {
                  // Initialize the Client Ipc Subsystem
                  //
                  // First load the library. We load it itself to keep the system
                  // from unloading it in-case that the application unloads us. Some
                  // applications such as PAM application will repeateadly load and
                  // unload us.
                  void* libHandle = OpenLibrary(IpcClientLibraryPath);
                  if (libHandle)
                  {
                     // The Ipc library has been loaded, now get the symbols that we need.
                     g_ipcInitPtr = GetFunctionPtr(libHandle, "IpcClientInit");
                     g_ipcShutdownPtr = GetFunctionPtr(libHandle, "IpcClientShutdown");
                     //g_ipcOpenEndPointPtr = GetFunctionPtr(libHandle, "IpcClientOpenInetRemoteEndPoint");
                     g_ipcOpenEndPointPtr = GetFunctionPtr(libHandle, "IpcClientOpenUnixRemoteEndPoint");
                     g_ipcCloseEndPointPtr = GetFunctionPtr(libHandle, "IpcClientCloseRemoteEndPoint");
                     g_ipcSubmitReq = GetFunctionPtr(libHandle, "IpcClientSubmitReq");
                     if (g_ipcInitPtr == NULL
                         || g_ipcShutdownPtr == NULL
                         || g_ipcOpenEndPointPtr == NULL
                         || g_ipcCloseEndPointPtr == NULL
                         || g_ipcSubmitReq == NULL)
                     {
                        DbgTrace(0, "-ValidateAuthToken- Failed to get needed Ipc library function pointer\n", 0);
                        IdenTokenUninit();
                        PrincipalIfUninit();
                        ConfigIfUninit();
                     }
                     else
                     {
                        if ((g_ipcInitPtr)("CASA_AuthTokenValidate",
                                           g_multiThreadedApplication,
                                           DebugLevel,
                                           false) == 0)
                        {
                           // Open endpoint for the Authentication Token Validation Service
                           //if ((g_ipcOpenEndPointPtr)(5000,
                           //                           0x7F000001,
                           //                           0,
                           //                           &g_atvsEndPointHandle) == 0)
                           if ((g_ipcOpenEndPointPtr)(DOMAIN_SOCKET_FILE_NAME,
                                                      0,
                                                      &g_atvsEndPointHandle) == 0)
                           {
                              // Success
                              g_moduleInitialized = true;
                           }
                           else
                           {
                              DbgTrace(0, "-ValidateAuthToken- Failed to open remote endpoint\n", 0);
                              (g_ipcShutdownPtr)();
                              IdenTokenUninit();
                              PrincipalIfUninit();
                              ConfigIfUninit();
                           }
                        }
                        else
                        {
                           DbgTrace(0, "-ValidateAuthToken- Ipc subsystem initialization failed\n", 0);
                           (g_ipcShutdownPtr)();
                           IdenTokenUninit();
                           PrincipalIfUninit();
                           ConfigIfUninit();
                        }
                     }
                  }
                  else
                  {
                     DbgTrace(0, "-ValidateAuthToken- Failed to load Ipc library, error = %d\n", dlerror());
                  }
               }
               else
               {
                  PrincipalIfUninit();
                  ConfigIfUninit();
               }
            }
            else
            {
               ConfigIfUninit();
            }
         }
      }

      // Stop synchronization
      ReleaseModuleMutex;

      // Exit if we failed
      if (g_moduleInitialized == false)
         goto exit;
   }

   // First decode the token string
   retStatus = DecodeData(pTokenBuf,
                          tokenBufLen,
                          (void**) &pDecodedTokenBuf,
                          &decodedTokenBufLen);
   if (CASA_SUCCESS(retStatus))
   {
      char *pIdenTokenData;
      int idenTokenDataLen;

      // Assume failure
      retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                  CASA_FACILITY_AUTHTOKEN,
                                  CASA_STATUS_AUTHENTICATION_FAILURE);

      // Token was decoded successfully, now submit the authentication token to the
      // authentication token validation service.
      if ((g_ipcSubmitReq)(g_atvsEndPointHandle,
                           pDecodedTokenBuf,
                           decodedTokenBufLen,
                           &pIdenTokenData,
                           &idenTokenDataLen) == 0)
      {
         // The submit succeeded, make sure that we got some identity data back.
         if (pIdenTokenData)
         {
            if (idenTokenDataLen != 0)
            {
               IdenTokenProviderIf  *pIdenTokenProviderIf;

               // The authentication token was validated, now obtain
               // Identity Token Provider interface.
               retStatus = GetIdenTokenProviderInterface("CasaIdentityToken",    // tbd - Hard code until we enhance the protocol with the atvs to also return this information.
                                                         &pIdenTokenProviderIf);
               if (CASA_SUCCESS(retStatus))
               {
                  IdenTokenIf *pIdenTokenIf;

                  // Use the Identity Token Provider to get an Identity Token Interface instance
                  retStatus = pIdenTokenProviderIf->getIdentityTokenIf(pIdenTokenProviderIf,
                                                                       pIdenTokenData,
                                                                       idenTokenDataLen,
                                                                       &pIdenTokenIf);
                  if (CASA_SUCCESS(retStatus))
                  {
                     // Now create a principal interface instance with the identity information present in
                     // the identity token.
                     retStatus = GetPrincipalInterface(pIdenTokenIf, &pPrincipalIf);
                     if (CASA_SUCCESS(retStatus))
                     {
                        // Success, return the principal interface to the caller.
                        *ppPrincipalIf = pPrincipalIf;
                     }
                     else
                     {
                        DbgTrace(0, "-ValidateAuthToken- Failed to instantiate principal interface\n", 0);
                     }

                     // Release identity token interface
                     pIdenTokenIf->releaseReference(pIdenTokenIf);
                  }
                  else
                  {
                     DbgTrace(0, "-ValidateAuthToken- Failed to instantiate identity token\n", 0);
                  }

                  // Release identity token provider interface
                  pIdenTokenProviderIf->releaseReference(pIdenTokenProviderIf);
               }
               else
               {
                  DbgTrace(0, "-ValidateAuthToken- Failed to obtain identity token provider interface\n", 0);
               }
            }
            else
            {
               DbgTrace(0, "-ValidateAuthToken- ValidateAuthToken submit did not return identity token data\n", 0);
            }

            // Free the buffer containing the identity token data
            free(pIdenTokenData);
         }
         else
         {
            DbgTrace(0, "-ValidateAuthToken- ValidateAuthToken submit did not return identity token data buffer\n", 0);
         }
      }
      else
      {
         DbgTrace(1, "-ValidateAuthToken- ValidateAuthToken submit failed\n", 0);
      }


      // Free the decoded token buffer
      free(pDecodedTokenBuf);
   }
   else
   {
      DbgTrace(0, "-ValidateAuthToken- Token decode failure\n", 0);
   }

exit:

   DbgTrace(1, "-ValidateAuthToken- End, retStatus = %08X\n", retStatus);

   return retStatus;
}


//++=======================================================================
static void __attribute__((constructor))
so_init()
//
// Arguments In:  None.
//
// Arguments Out: None.
//
// Returns:       Nothing.
//
// Abstract:      Library initialization routine.
//
// L2
//=======================================================================--
{
   // Check for environment variable specifying that the application is
   // multi-threaded.
   if (getenv(APPLICATION_NOT_MULTI_THREADED) != NULL)
   {
      // The parameter has been configured, remember it.
      g_multiThreadedApplication = false;
   }
}


//++=======================================================================
static void __attribute__((destructor))
so_fini()
//
// Arguments In:  None.
//
// Arguments Out: None.
//
// Returns:       Nothing.
//
// Abstract:      Library un-initialization routine.
//
// L2
//=======================================================================--
{
   if (g_ipcShutdownPtr)
      (g_ipcShutdownPtr)();
}


//++=======================================================================
//++=======================================================================
//++=======================================================================