/*
 * auth.c - deal with authentication.
 *
 * This file implements the VNC authentication protocol when setting up an RFB
 * connection.
 */

/*
 *  Copyright (C) 2005 Rohit Kumar, Johannes E. Schindelin
 *  OSXvnc Copyright (C) 2001 Dan McGuirk <mcguirk@incompleteness.net>.
 *  Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge.
 *  All Rights Reserved.
 *
 *  This is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This software 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this software; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
 *  USA.
 */

#include "rfb/rfb.h"
#include "defines.h"

/* RFB 3.8 clients are well informed */
void rfbClientSendString(rfbClientPtr cl, char *reason);


/*
 * Handle security types
 */

static rfbSecurityHandler* securityHandlers = NULL;

/*
 * This method registers a list of new security types.
 * It avoids same security type getting registered multiple times.
 * The order is not preserved if multiple security types are
 * registered at one-go.
 */
void
rfbRegisterSecurityHandler(rfbSecurityHandler* handler)
{
    rfbSecurityHandler *head = securityHandlers, *next = NULL;

    if(handler == NULL)
        return;

    next = handler->next;

    while(head != NULL)
    {
        if(head == handler)
        {
            rfbRegisterSecurityHandler(next);
            return;
        }

        head = head->next;
    }

    handler->next = securityHandlers;
    securityHandlers = handler;

    rfbRegisterSecurityHandler(next);
}

/*
 * This method unregisters a list of security types.
 * These security types won't be available for any new
 * client connection.
 */
void
rfbUnregisterSecurityHandler(rfbSecurityHandler* handler)
{
    rfbSecurityHandler *cur = NULL, *pre = NULL;

    if(handler == NULL)
        return;

    if(securityHandlers == handler)
    {
        securityHandlers = securityHandlers->next;
        rfbUnregisterSecurityHandler(handler->next);
        return;
    }

    cur = pre = securityHandlers;

    while(cur)
    {
        if(cur == handler)
        {
            pre->next = cur->next;
            break;
        }
        pre = cur;
        cur = cur->next;
    }
    rfbUnregisterSecurityHandler(handler->next);
}

/*
 * Send the authentication challenge.
 */

static void
rfbVncAuthSendChallenge(rfbClientPtr cl)
{

    /* 4 byte header is alreay sent. Which is rfbSecTypeVncAuth
       (same as rfbVncAuth). Just send the challenge. */
    rfbRandomBytes(cl->authChallenge);
    if (rfbWriteExact(cl, (char *)cl->authChallenge, CHALLENGESIZE) < 0)
    {
        ///rfbLogPerror("rfbAuthNewClient: write");
        rfbCloseClient(cl);
        return;
    }

    /* Dispatch client input to rfbVncAuthProcessResponse. */
    cl->state = RFB_AUTHENTICATION;
}

/*
 * Send the NO AUTHENTICATION. SCARR
 */

static void
rfbVncAuthNone(rfbClientPtr cl)
{
    uint32_t authResult;

    if (cl->protocolMajorVersion==3 && cl->protocolMinorVersion > 7)
    {
        ///rfbLog("rfbProcessClientSecurityType: returning securityResult for client rfb version >= 3.8\n");
        authResult = Swap32IfLE(rfbVncAuthOK);
        if (rfbWriteExact(cl, (char *)&authResult, 4) < 0)
        {
            ///rfbLogPerror("rfbAuthProcessClientMessage: write");
            rfbCloseClient(cl);
            return;
        }
    }
    cl->state = RFB_INITIALISATION;
    return;
}


/*
 * Advertise the supported security types (protocol 3.7). Here before sending
 * the list of security types to the client one more security type is added
 * to the list if primaryType is not set to rfbSecTypeInvalid. This security
 * type is the standard vnc security type which does the vnc authentication
 * or it will be security type for no authentication.
 * Different security types will be added by applications using this library.
 */

static rfbSecurityHandler VncSecurityHandlerVncAuth =
{
    rfbSecTypeVncAuth,
    rfbVncAuthSendChallenge,
    NULL
};

static rfbSecurityHandler VncSecurityHandlerNone =
{
    rfbSecTypeNone,
    rfbVncAuthNone,
    NULL
};


static void
rfbSendSecurityTypeList(rfbClientPtr cl, int primaryType)
{
    /* The size of the message is the count of security types +1,
     * since the first byte is the number of types. */
    int size = 1;
    rfbSecurityHandler* handler;
#define MAX_SECURITY_TYPES 255
    uint8_t buffer[MAX_SECURITY_TYPES+1];


    /* Fill in the list of security types in the client structure. (NOTE: Not really in the client structure) */
    switch (primaryType)
    {
    case rfbSecTypeNone:
        rfbRegisterSecurityHandler(&VncSecurityHandlerNone);
        break;
    case rfbSecTypeVncAuth:
        rfbRegisterSecurityHandler(&VncSecurityHandlerVncAuth);
        break;
    }

    for (handler = securityHandlers;
            handler && size<MAX_SECURITY_TYPES; handler = handler->next)
    {
        buffer[size] = handler->type;
        size++;
    }
    buffer[0] = (unsigned char)size-1;

    /* Send the list. */
    if (rfbWriteExact(cl, (char *)buffer, size) < 0)
    {
        ///rfbLogPerror("rfbSendSecurityTypeList: write");
        rfbCloseClient(cl);
        return;
    }

    /*
      * if count is 0, we need to send the reason and close the connection.
      */
    if(size <= 1)
    {
        /* This means total count is Zero and so reason msg should be sent */
        /* The execution should never reach here */
        char* reason = "No authentication mode is registered!";

        rfbClientSendString(cl, reason);
        return;
    }

    /* Dispatch client input to rfbProcessClientSecurityType. */
    cl->state = RFB_SECURITY_TYPE;
}




/*
 * Tell the client what security type will be used (protocol 3.3).
 */
static void
rfbSendSecurityType(rfbClientPtr cl, int32_t securityType)
{
    uint32_t value32;

    /* Send the value. */
    value32 = Swap32IfLE(securityType);
    if (rfbWriteExact(cl, (char *)&value32, 4) < 0)
    {
        ///rfbLogPerror("rfbSendSecurityType: write");
        rfbCloseClient(cl);
        return;
    }

    /* Decide what to do next. */
    switch (securityType)
    {
    case rfbSecTypeNone:
        /* Dispatch client input to rfbProcessClientInitMessage. */
        cl->state = RFB_INITIALISATION;
        break;
    case rfbSecTypeVncAuth:
        /* Begin the standard VNC authentication procedure. */
        rfbVncAuthSendChallenge(cl);
        break;
    default:
        /* Impossible case (hopefully). */
        ///rfbLogPerror("rfbSendSecurityType: assertion failed");
        rfbCloseClient(cl);
    }
}



/*
 * rfbAuthNewClient is called right after negotiating the protocol
 * version. Depending on the protocol version, we send either a code
 * for authentication scheme to be used (protocol 3.3), or a list of
 * possible "security types" (protocol 3.7).
 */

void
rfbAuthNewClient(rfbClientPtr cl)
{
    int32_t securityType = rfbSecTypeInvalid;

    if (!cl->screen->authPasswdData || cl->reverseConnection)
    {
        /* chk if this condition is valid or not. */
        securityType = rfbSecTypeNone;
    }
    else if (cl->screen->authPasswdData)
    {
        securityType = rfbSecTypeVncAuth;
    }

    if (cl->protocolMajorVersion==3 && cl->protocolMinorVersion < 7)
    {
        /* Make sure we use only RFB 3.3 compatible security types. */
        if (securityType == rfbSecTypeInvalid)
        {
            ///rfbLog("VNC authentication disabled - RFB 3.3 client rejected\n");
            rfbClientConnFailed(cl, "Your viewer cannot handle required "
                                "authentication methods");
            return;
        }
        rfbSendSecurityType(cl, securityType);
    }
    else
    {
        /* Here it's ok when securityType is set to rfbSecTypeInvalid. */
        rfbSendSecurityTypeList(cl, securityType);
    }
}

/*
 * Read the security type chosen by the client (protocol 3.7).
 */

void
rfbProcessClientSecurityType(rfbClientPtr cl)
{
    int n;
    uint8_t chosenType;
    rfbSecurityHandler* handler;

    /* Read the security type. */
    n = rfbReadExact(cl, (char *)&chosenType, 1);
    if (n <= 0)
    {
        /**if (n == 0)
            rfbLog("rfbProcessClientSecurityType: client gone\n");
        else
            rfbLogPerror("rfbProcessClientSecurityType: read");
        **/
        rfbCloseClient(cl);
        return;
    }

    /* Make sure it was present in the list sent by the server. */
    for (handler = securityHandlers; handler; handler = handler->next)
    {
        if (chosenType == handler->type)
        {
            ///rfbLog("rfbProcessClientSecurityType: executing handler for type %d\n", chosenType);
            handler->handler(cl);
            return;
        }
    }

    ///rfbLog("rfbProcessClientSecurityType: wrong security type (%d) requested\n", chosenType);
    rfbCloseClient(cl);
}



/*
 * rfbAuthProcessClientMessage is called when the client sends its
 * authentication response.
 */

void
rfbAuthProcessClientMessage(rfbClientPtr cl)
{
    int n;
    uint8_t response[CHALLENGESIZE];
    uint32_t authResult;

    if ((n = rfbReadExact(cl, (char *)response, CHALLENGESIZE)) <= 0)
    {
        if (n != 0)
            ///rfbLogPerror("rfbAuthProcessClientMessage: read");
            rfbCloseClient(cl);
        return;
    }

    if(!cl->screen->passwordCheck(cl,(const char*)response,CHALLENGESIZE))
    {
        ///rfbErr("rfbAuthProcessClientMessage: password check failed\n");
        authResult = Swap32IfLE(rfbVncAuthFailed);
        if (rfbWriteExact(cl, (char *)&authResult, 4) < 0)
        {
            ///rfbLogPerror("rfbAuthProcessClientMessage: write");
        }
        /* support RFB 3.8 clients, they expect a reason *why* it was disconnected */
        if (cl->protocolMinorVersion > 7)
        {
            rfbClientSendString(cl, "password check failed!");
        }
        else
            rfbCloseClient(cl);
        return;
    }

    authResult = Swap32IfLE(rfbVncAuthOK);

    if (rfbWriteExact(cl, (char *)&authResult, 4) < 0)
    {
        ///rfbLogPerror("rfbAuthProcessClientMessage: write");
        rfbCloseClient(cl);
        return;
    }

    cl->state = RFB_INITIALISATION;
}
