/***********************************************************************
 * 
 *  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 ]==================================================

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

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


//++=======================================================================
CasaStatus SSCS_CALL
AuthTokenIf_GetAuthToken(
   IN       const void  *pIfInstance,
   IN       const char  *pContext,
   IN       const char  *pMechInfo,
   INOUT    char        *pTokenBuf,
   INOUT    int         *pTokenBufLen)
//
// Arguments:  
//    pIfInstance -
//       Pointer to interface object.
//   
//    pServiceConfigIf -
//       Pointer to service config object to which the client is trying to
//       authenticate.
//               
//    pContext -
//       Pointer to null terminated string containing mechanism specific
//       context information. Another name for context is Authentication
//       Realm.
//
//    pMechInfo -
//       Pointer to null terminated string containing mechanism specific
//       information. This is information is provided by the server to
//       aid the mechanism to generate an authentication token. For
//       example, the mechanism information for a Kerberos mechanism
//       may be the service principal name to which the user will be
//       authenticating.
//               
//    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.
//
//    pTokenBufLen -
//       Pointer to integer that contains the length of the
//       buffer pointed at by pTokenBuf. Upon return of the
//       function, the integer will contain the actual length
//       of the authentication token if the function successfully
//       completes or the buffer length required if the function
//       fails because the buffer pointed at by pUserNameBuf is
//       not large enough.
//   
// Returns:
//    Casa Status
//                           
// Description:
//    Get authentication token to authenticate user to specified service.
//
// L2
//=======================================================================--
{
   CasaStatus        retStatus;
   char              *pKrbServiceName = pMechInfo;
   SECURITY_STATUS   secStatus;
   TimeStamp         expiry;
   CredHandle        hCredentials = {0};


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

   // Validate input parameters
   if (pIfInstance == NULL
       || pContext == NULL
       || pMechInfo == NULL
       || pTokenBufLen == NULL
       || (pTokenBuf == NULL && *pTokenBufLen != 0))
   {
      DbgTrace(0, "-AuthTokenIf_GetAuthToken- Invalid input parameter\n", 0);

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

   // Acquire a credential handle for the current user
   secStatus = AcquireCredentialsHandle(NULL,                  // no principal name
                                        "Kerberos",            // package name
                                        SECPKG_CRED_OUTBOUND,
                                        NULL,                  // no logon id
                                        NULL,                  // no auth data
                                        NULL,                  // no get key fn
                                        NULL,                  // noget key arg
                                        &hCredentials,
                                        &expiry);
   if (secStatus == SEC_E_OK)
   {
      CtxtHandle        hContext = {0};
      SecBuffer         sendTok;
      SecBufferDesc     outputDesc;
      ULONG             retFlags;

      // We acquired the credential, now initialize a security context
      // so that we can authenticate the user to the specified service.
      //
      // First ready an output descriptor so that we can receive the
      // token buffer.
      outputDesc.cBuffers = 1;
      outputDesc.pBuffers = &sendTok;
      outputDesc.ulVersion = SECBUFFER_VERSION;

      sendTok.BufferType = SECBUFFER_TOKEN;
      sendTok.cbBuffer = 0;
      sendTok.pvBuffer = NULL;

      // Initialize the security context for the specified service
      secStatus = InitializeSecurityContext(&hCredentials,
                                            NULL,
                                            pKrbServiceName,
                                            ISC_REQ_ALLOCATE_MEMORY,
                                            0,          // reserved
                                            SECURITY_NATIVE_DREP,
                                            NULL,
                                            0,          // reserved
                                            &hContext,
                                            &outputDesc,
                                            &retFlags,
                                            &expiry);
      if (secStatus == SEC_E_OK)
      {
         char  *pEncodedToken;
         int   encodedTokenLen;

         // The security context was initialized, now return it to the caller after base64 encoding it.
         retStatus = EncodeData(sendTok.pvBuffer,
                                (const int) sendTok.cbBuffer,
                                &pEncodedToken,
                                &encodedTokenLen);
         if (CASA_SUCCESS(retStatus))
         {
            // Verify that the caller provided a buffer that is big enough
            if (encodedTokenLen > *pTokenBufLen)
            {
               // The buffer is not big enough
               retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                           CASA_FACILITY_KRB5TOKEN,
                                           CASA_STATUS_BUFFER_OVERFLOW);
            }
            else
            {
               // The buffer provided is large enough, copy the data.
               memcpy((void*) pTokenBuf, pEncodedToken, encodedTokenLen);

               // Success
               retStatus = CASA_STATUS_SUCCESS;
            }

            // Return the actual size or the size required
            *pTokenBufLen = encodedTokenLen;

            // Free the buffer containing the encoded token
            free(pEncodedToken);
         }

         // Delete the security context
         DeleteSecurityContext(&hContext);
      }
      else
      {
         DbgTrace(0, "-AuthTokenIf_GetAuthToken- Failed to initialize the security context, error = %08X\n", secStatus);

         retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                     CASA_FACILITY_KRB5TOKEN,
                                     CASA_STATUS_UNSUCCESSFUL);
      }

      // Free any buffer associated with the sendToken
      if (sendTok.pvBuffer)
         FreeContextBuffer(sendTok.pvBuffer);

      // Free the credential handle obtained
      FreeCredentialsHandle(&hCredentials);

   }
   else
   {
      DbgTrace(1, "-AuthTokenIf_GetAuthToken- Failed to obtain the credentials handle, error = %08X\n", secStatus);

      // Set retStatus based on secStatus
      if (secStatus == SEC_E_NOT_OWNER
          || secStatus == SEC_E_NO_CREDENTIALS)
      {
         retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                     CASA_FACILITY_KRB5TOKEN,
                                     CASA_STATUS_NO_CREDENTIALS);
      }
      else
      {
         retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                     CASA_FACILITY_KRB5TOKEN,
                                     CASA_STATUS_UNSUCCESSFUL);
      }
   }
         
exit:

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

   return retStatus;
}


//++=======================================================================
int
InitializeLibrary(void)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   int   retStatus = 0;

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

   // Nothing to do at this time.

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

   return retStatus;
}


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