added libvnc/ with RealVNC compatibility fix
[presencevnc] / libvnc / libvncserver / auth.c
diff --git a/libvnc/libvncserver/auth.c b/libvnc/libvncserver/auth.c
new file mode 100755 (executable)
index 0000000..c4c5ff1
--- /dev/null
@@ -0,0 +1,375 @@
+/*
+ * 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>
+
+/* 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;
+}