/*#----------------------------------------------------------------------------
**
**  File:           sspcalls.c
**
**      Synopsis:   This module contains SSPI function calls for SSPI SPM DLL.
**
**      Copyright (C) 1995  Microsoft Corporation.  All Rights Reserved.
**
**  Authors:        LucyC       Created                         25 Sept. 1995
**
**---------------------------------------------------------------------------*/

#include "msnspmh.h"

/*****
#ifdef THIS_FILE
#undef THIS_FILE
#endif
static char __szTraceSourceFile[] = __FILE__;
#define THIS_FILE __szTraceSourceFile
*****/

BOOL g_fUUEncodeData = TRUE;


/*-----------------------------------------------------------------------------
**
**  Function:   GetSecAuthMsg
**
**  Synopsis:   This function generates a SSPI NEGOTIATE or RESPONSE 
**				authorization string for the specified SSPI package.
**				The authorization string generated by this function 
**				follows the format: 
**					"<Package Name> <Package Specific Auth. Data>"
**				If global uuencoding is turned on, this functions will 
**				uuencode the message before building it into an  
**				authorization string; by default, the uuencoding flag is 
**				always on.  
**				This functions calls InitializeSecurityContext() to 
**				generate the NEGOTIATE/RESPONSE message for the authori-
**				zation string. If the SSPI function returns NO_CREDENTIAL, 
**				and if the PROMPT_CREDS flag is not turned on when blocking
**				is permitted, this function will call the SSPI function 
**				again with the PROMPT_CREDS flag set; if SSPI returns 
**				NO_CREDENTIAL again, this SSPI will return ERROR to the 
**				caller.
**
**
**  Arguments:
**
**		pData - pointer to SspData containing the SSPI function table 
**				and the SSPI package list. 
**		pkgID - the package index of the SSPI package to use.
**		pMyContext - pointer to a context handle. If NULL is specified, 
**					 this function will use a temporary space for the context
**					 handle and delete the handle before returning to the 
**					 caller. If non-NULL address is specified, the context 
**					 handle created by the SSPI is returned to the caller. 
**					 And the caller will have to delete the handle when it's
**					 done with it.
**		fContextReq - the SSPI request flag to pass to InitializeSecurityContext
**		pBuffIn - pointer to the uudecoded CHALLENGE message if any. 
**				  For generating NEGOTIATE message, this pointer should be NULL.
**		cbBuffIn - length of the CHALLENGE message. This should be zero when  
**				   when pBuffIn is NULL.
**		pFinalBuff - pointer to a buffer for the final authorization string.
**		pszTarget - Server Host Name
**		bNonBlock - a flag which is set if blocking is not permitted.
**
**  Return Value:
**
**		HTSPM_STATUS_OK	- if an authorization string is generated successfully
**  	HTSPM_STATUS_WOULD_BLOCK - if generating an authorization string would 
**					cause blocking when blocking is not permitted. 
**		HTSPM_ERROR - if any problem/error is encountered in generating an 
**					authorization string, including user hitting cancel on 
**					the SSPI dialog prompt for name/password.
**
**---------------------------------------------------------------------------*/
HTSPMStatusCode
GetSecAuthMsg (
    F_UserInterface fpUI,
	void * pvOpaqueOS,
    PSspData        pData, 			 
    UCHAR           pkgID,              // the package index into package list
    PCtxtHandle     pMyContext,
    ULONG           fContextReq,        // Request Flags
    VOID            *pBuffIn, 
    DWORD           cbBuffIn, 
    char            *pFinalBuff, 
    SEC_CHAR        *pszTarget,         // Server Host Name
    UINT            bNonBlock
    )
{
    BOOLEAN               bOk;
    SECURITY_STATUS       SecStat;
    CtxtHandle            hContext;
    CtxtHandle            *phContext;
    TimeStamp             Lifetime;
    SecBufferDesc         OutBuffDesc;
    SecBuffer             OutSecBuff;
    SecBufferDesc         InBuffDesc;
    SecBuffer             InSecBuff;
    ULONG                 ContextAttributes;
    char                  OutBufPlain[MAX_AUTH_MSG_SIZE];
    char                  *pOutMsg = NULL;
    HTSPMStatusCode       RetStatus;
    UI_StatusCode uisc;
    ULONG bGet;
    UI_WindowHandle *pwh = NULL;

	//TraceFunctEnter("GetSecAuthMsg");

	//
	//	Initialize the final buffer to hold the package name followed by 
	//	a space. And setup the pOutMsg pointer to points to the character 
	//	following the space so that the final NEGOTIATE/RESPONSE can be 
	//	copied into the pFinalBuff starting at the character pointed to 
	//	by pOutMsg. 
	//
    if (pFinalBuff)
    {
        strcpy (pFinalBuff, pData->PkgList[pkgID]->pName);
        pOutMsg = pFinalBuff + strlen(pFinalBuff);
        *pOutMsg++ = ' ';
    }

    //
    //  If caller does not want the context handle back
    //
    if (pMyContext == NULL)
        phContext = &hContext;	 // use a temporary one 
    else
        phContext = pMyContext;

    //
    //  Prepare our output buffer.  We use a temporary buffer because
    //  the real output buffer will most likely need to be uuencoded
    //
    OutBuffDesc.ulVersion = 0;
    OutBuffDesc.cBuffers  = 1;
    OutBuffDesc.pBuffers  = &OutSecBuff;

    OutSecBuff.cbBuffer   = MAX_AUTH_MSG_SIZE;
    OutSecBuff.BufferType = SECBUFFER_TOKEN;
    OutSecBuff.pvBuffer   = OutBufPlain;

    //
    //  Prepare our Input buffer if a CHALLENGE message is passed in.
    //
    if ( pBuffIn )
    {
        InBuffDesc.ulVersion = 0;
        InBuffDesc.cBuffers  = 1;
        InBuffDesc.pBuffers  = &InSecBuff;

        InSecBuff.cbBuffer   = cbBuffIn;
        InSecBuff.BufferType = SECBUFFER_TOKEN;
        InSecBuff.pvBuffer   = pBuffIn;
    }


	//
	//	Call SSPI function generate the NEGOTIATE/RESPONSE message
	//
SspiRetry:

    SecStat = (*(pData->pFuncTbl->InitializeSecurityContext))(
                                &pData->PkgList[pkgID]->Credential, 
                                (pBuffIn ? phContext : NULL),
                                pszTarget,
                                fContextReq,
                                0,
                                SECURITY_NATIVE_DREP,
                                (pBuffIn) ? &InBuffDesc : NULL, 
                                0,
                                phContext, 
                                &OutBuffDesc,
                                &ContextAttributes,
                                &Lifetime );

	//
	//	If SSPI function fails 
	//
    if ( !NT_SUCCESS( SecStat ) )
    {
        RetStatus = HTSPM_ERROR;
        //ErrorTrace(SSPCALLSID,"InitializeSecurityContext failed [0x%x]\n",
        //           SecStat);

		//
		//	If SSPI do not have user name/password for the secified package,
		//
        if (SecStat == SEC_E_NO_CREDENTIALS)
        {
            //
            //  If we have prompted the user and still get back "No Credential"
            //  error, it means the user does not have valid credential; the 
            //	user hit <CANCEL> on the UI box. If we have supplied a valid 
			//	credential, but get back a "No Credential" error, then something
			//	has gone wrong; we definitely should return to caller with ERROR
            //
            if ((fContextReq & ISC_REQ_PROMPT_FOR_CREDS) ||
				(fContextReq & ISC_REQ_USE_SUPPLIED_CREDS))
			{
                RetStatus = HTSPM_ERROR;	// return ERROR to caller
            }
            else if (bNonBlock)
            {
				//
				//	Blocking is not permitted, return WOULD_BLOCK to caller
				//
                RetStatus = HTSPM_STATUS_WOULD_BLOCK;
            }
            else
            {
                //	Blocking is permitted and we have not asked the SSPI to
                //  prompt the user for proper credential, we should call  
                //  the SSPI again with PROMPT_CREDS flag set.
                //
                fContextReq = fContextReq | ISC_REQ_PROMPT_FOR_CREDS;
                goto SspiRetry;
            }
        }
        SetLastError( SecStat );

        //
        //  If we failed to generate a RESPONSE, and 
        //  if the context handle is not to be returned to the caller, we 
        //  should delete the context handle before we exit this function.
        //  However, if we failed to generate a NEGOTIATE, 
        //  we don't need to worry about deleting the context handle because 
        //  the context handle has not being created in this case.
        //
        if (pBuffIn != NULL && pMyContext == NULL)
            (*(pData->pFuncTbl->DeleteSecurityContext))(phContext);

        //TraceFunctLeave();
        return (RetStatus);
    }

    RetStatus = HTSPM_STATUS_OK;

    //
    //  Only uuencode message if a output buffer is specified
    //
    if (pOutMsg)
    {
        if ( g_fUUEncodeData && pOutMsg)
        {
            if ( !uuencode( (BYTE *) OutSecBuff.pvBuffer,
                            OutSecBuff.cbBuffer,
                            (CHAR *) pOutMsg,
                            MAX_AUTH_MSG_SIZE ))
            {
                //ErrorTrace(SSPCALLSID, 
                //           "Failed to uuencode NEGOTIATE/RESPONSE\n");
                RetStatus = HTSPM_ERROR;
            }
        }
        else
        {
            memcpy( (CHAR *) pOutMsg, 
                    OutSecBuff.pvBuffer,
                    OutSecBuff.cbBuffer );
        }
    }

    //
    //  We have successfully generated a NEGOTIATE or RESPONSE, so a context 
    //  handle has been created by the SSPI.
    //  If the context handle is not to be returned to the caller, we should 
    //  delete the context handle before we exit this function.
    //
    if (pMyContext == NULL)
        (*(pData->pFuncTbl->DeleteSecurityContext))(phContext);

    //TraceFunctLeave();
    return (RetStatus);
}

/*-----------------------------------------------------------------------------
**
**  Function:   GetSecCredential
**
**  Synopsis:   
**
**		This function go through the list of SSPI pacakges supported and 
**		call AcquireCredentialsHandle for each package. If the call fails, 
**		the SSPI package is deleted from the package list.
**
**  Arguments:  
**
**		fpUI - pointer from the Explorer for making UI_SERVICE call
**		pvOpaqueOS - pointer from the Explorer for making UI_SERVICE call
**		pData - pointer to the data structure containing the package list.
**
**  Return Value:
**
**    void.
**
**---------------------------------------------------------------------------*/
VOID
GetSecCredential (
    F_UserInterface fpUI,
    void        *pvOpaqueOS,
    PSspData    pData
    )
{
    int ii;
    SECURITY_STATUS       ss;
    TimeStamp             Lifetime;

    ii = 0;

	//
	//	For every package in the out package list
	//
    while (ii < pData->PkgCnt)
    {
		//
		//	Call SSPI function acquire security credential for this package
		//
        ss = (*(pData->pFuncTbl->AcquireCredentialsHandle))(
                       NULL,                // New principal
                       pData->PkgList[ii]->pName,      // Package name
                       SECPKG_CRED_OUTBOUND,// Credential Use
                       NULL,                // Logon ID
                       NULL,                // Auth Data
                       NULL,                // Get key func
                       NULL,                // Get key arg
                       &pData->PkgList[ii]->Credential, // Credential Handle
                       &Lifetime );

		//
		//	If SSPI can not create a credential handle for the package 
		//
        if ( ss != STATUS_SUCCESS )
        {
            SetLastError( ss );

            //
            //  Failed to acquire credential for this package,
            //	Deallocate memory for this package 
            //
            spm_free (fpUI, pvOpaqueOS, pData->PkgList[ii]->pName);
            spm_free (fpUI, pvOpaqueOS, pData->PkgList[ii]);

			//
            //	remove this SSPI package from the package list.
            //  
            if (ii < pData->PkgCnt - 1)
            {
                memcpy (&pData->PkgList[ii], &pData->PkgList[ii+1], 
                        (pData->PkgCnt - ii -1) * sizeof(PSSPAuthPkg));
            }
            pData->PkgList[--pData->PkgCnt] = NULL;
        }
        else
            ii++;	// proceed to the next package
    }
}