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


//++=======================================================================
static
CasaStatus
GetUserCredentials(
   IN       const char *pRealm,
   INOUT    char **ppUsername,
   INOUT    char **ppPassword)
//
// Arguments:  
//    pRealm -
//       The realm to which the credentials apply.
//   
//    ppUsername -
//       Pointer to variable that will receive buffer with the username.
//               
//    ppPassword -
//       Pointer to variable that will receive buffer with the password.
//   
// Returns:
//    Casa Status
//                           
// Description:
//    Get authentication credentials for the specified realm.
//
// L2
//=======================================================================--
{
   CasaStatus              retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                                       CASA_FACILITY_PWTOKEN,
                                                       CASA_STATUS_UNSUCCESSFUL);
   char                    *pUsername;
   char                    *pPassword;
   int                     rcode = NSSCS_E_OBJECT_NOT_FOUND;
   int32_t                 credtype = SSCS_CRED_TYPE_BASIC_F;
   SSCS_BASIC_CREDENTIAL   credential = {0};
   SSCS_SECRET_ID_T        secretId = {0};
   SSCS_SECRET_ID_T        sharedSecretId = {0};
   
   
   DbgTrace(1, "-GetUserCredentials- Start\n", 0);

   // Initialize output parameters
   *ppUsername = NULL;
   *ppPassword = NULL;

   // Get the length of the realm string into the secret id structure
   // and verify thatr it is not too long.
   secretId.len = sscs_Utf8Strlen(pRealm) + 1;
   if (secretId.len <= NSSCS_MAX_SECRET_ID_LEN)
   {
      // Set the secret id in the structure
      sscs_Utf8Strcpy(secretId.id, pRealm);

      // Specify that we want the common name
      credential.unFlags = USERNAME_TYPE_CN_F;

      // Now try to get the credentials
      rcode = miCASAGetCredential(0,
                                  &secretId,
                                  NULL,
                                  &credtype,
                                  &credential,
                                  NULL);
      if (rcode != NSSCS_SUCCESS)
      {
         // There were no credentials for the realm, now try to obtain the
         // desktop credentials.
         secretId.len = sscs_Utf8Strlen("Desktop") + 1;
         sscs_Utf8Strcpy(secretId.id, "Desktop");
         rcode = miCASAGetCredential(0,
                                     &secretId,
                                     NULL,
                                     &credtype,
                                     &credential,
                                     NULL);
      }
   }
   else
   {
      DbgTrace(0, "-GetUserCredentials- Realm name too long\n", 0);
      retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                  CASA_FACILITY_PWTOKEN,
                                  CASA_STATUS_UNSUCCESSFUL);
   }

   // Proceed based on the result of the operatiosn above
   if (rcode == NSSCS_SUCCESS
       && credential.username != NULL
       && credential.password != NULL)
   {
      // Allocate a buffer to return the username
      pUsername = (char*) malloc(strlen(credential.username) + 1);
      if (pUsername)
      {
         // Copy the username into the buffer that we will be returning
         strcpy(pUsername, credential.username);

         // Allocate a buffer to return the password
         pPassword = (char*) malloc(strlen(credential.password) + 1);
         if (pPassword)
         {
            // Copy the password into the buffer that we will be returning
            strcpy(pPassword, credential.password);

            // Success
            retStatus = CASA_STATUS_SUCCESS;
         }
         else
         {
            DbgTrace(0, "-GetUserCredentials- Buffer allocation error\n", 0);
            retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                        CASA_FACILITY_PWTOKEN,
                                        CASA_STATUS_INSUFFICIENT_RESOURCES);

            // Free the buffer allocated for the username
            free(pUsername);
         }
      }
      else
      {
         DbgTrace(0, "-GetUserCredentials- Buffer allocation error\n", 0);
         retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                     CASA_FACILITY_PWTOKEN,
                                     CASA_STATUS_INSUFFICIENT_RESOURCES);
      }
   }

   // Return the buffers to the caller if successful
   if (CASA_SUCCESS(retStatus))
   {
      *ppUsername = pUsername;
      *ppPassword = pPassword;
   }

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

   return retStatus;
}


//++=======================================================================
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        *pUsername = NULL;
   char        *pPassword = NULL;
   char        *pToken;

   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_PWTOKEN,
                                  CASA_STATUS_INVALID_PARAMETER);
      goto exit;
   }

   // Get the user credentials
   retStatus = GetUserCredentials(pContext, &pUsername, &pPassword);
   if (CASA_SUCCESS(retStatus))
   {
      // Now construct the PW token with the following format:
      // "username\r\n" + "password\r\n"
      //
      // First allocate a buffer large enough to hold the token
      pToken = (char*) malloc(strlen(pUsername) + 2 + strlen(pPassword) + 2 + 1);
      if (pToken)
      {
         char  *pEncodedToken;
         int   encodedTokenLen;

         // Now assemble the token
         sprintf(pToken, "%s\r\n%s\r\n", pUsername, pPassword);

         // The token has been assembled, now encode it.
         retStatus = EncodeData(pToken,
                                (const int) strlen(pToken),
                                &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_PWTOKEN,
                                           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);
         }

         // Free the buffer allocated for the token
         free(pToken);
      }
      else
      {
         DbgTrace(0, "-AuthTokenIf_GetAuthToken- Buffer allocation error\n", 0);
         retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                     CASA_FACILITY_PWTOKEN,
                                     CASA_STATUS_INSUFFICIENT_RESOURCES);
      }

      // Free allocated buffers
      free(pUsername);
      free(pPassword);
   }
   else
   {
      DbgTrace(1, "-AuthTokenIf_GetAuthToken- Failed to obtain the user credentials\n", 0);
   }

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;
}


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