ReceiveEvent HandleNetworkEvent()
	{
		ReceiveEvent e;
		using( BitReader reader = GetPooledReader() )
		{
			DataStream stream = reader.GetStream();

			int connectionId;
			ChannelIndex channel;
			e = PollReceive( stream, out connectionId, out channel );

			if( e == ReceiveEvent.Error || e == ReceiveEvent.NoMoreEvents ) { return e; }

			// on the server, the connectionId is one to one with the client index
			NodeIndex client = new NodeIndex( (uint)connectionId );

			switch( e )
			{
				case ReceiveEvent.Connect:
					//_profiler.RecordEvent(TickType.Receive, (uint)receivedSize, MessageManager.reverseChannels[channelId], "TRANSPORT_CONNECT");
					Log.Info( $"Client {client.GetClientIndex()} sent initial connection packet" );
					ClientConnection c;
					if( _commonSettings.enableEncryption )
					{
						// This client is required to complete the crypto-hail exchange.
						EllipticDiffieHellman keyExchange = SendCryptoHail();

						c = new ClientConnection( client, ClientConnection.Status.PendingConnectionChallengeResponse, keyExchange );
					}
					else
					{
						c = new ClientConnection( client, ClientConnection.Status.PendingConnectionRequest, null );		
					}
					_connection.Add( c.GetIndex(), c );
					// TODO: cozeroff NO, NO, NO, check for timeouts of clients manually
					//StartCoroutine(ApprovalTimeout(clientId));
					break;

				case ReceiveEvent.Message:
					HandleMessage( reader, client, channel );
					break;
					
				case ReceiveEvent.Disconnect:
					//_profiler.RecordEvent(TickType.Receive, 0, "NONE", "TRANSPORT_DISCONNECT");
					Log.Info( "Disconnect Event From " + client.GetClientIndex() );

					// TODO: cozeroff
					//OnClientDisconnectServer( client );

					if( _onClientDisconnect != null )
					{
						_onClientDisconnect.Invoke( client );
					}
					break;
			}
		}

		return e;
	}
		public ClientConnection( NodeIndex id, Status state, EllipticDiffieHellman keyExchange )
		{
			_id = id;
			_state = state;
			_keyExchange = keyExchange;
			_sharedSecretKey = null;
			_playerAvatar = null;
			_ownedEntity = new EntitySet( AlpacaConstant.CLIENT_OWNER_LIMIT );
		}
        // Runs on client
        internal static void HandleHailRequest(uint clientId, Stream stream, int channelId)
        {
            X509Certificate2 certificate = null;

            byte[] serverDiffieHellmanPublicPart = null;
            using (PooledBitReader reader = PooledBitReader.Get(stream))
            {
                if (netManager.NetworkConfig.EnableEncryption)
                {
                    // Read the certificate
                    if (netManager.NetworkConfig.SignKeyExchange)
                    {
                        // Allocation justification: This runs on client and only once, at initial connection
                        certificate = new X509Certificate2(reader.ReadByteArray());
                        if (CryptographyHelper.VerifyCertificate(certificate, netManager.ConnectedHostname))
                        {
                            // The certificate is not valid :(
                            // Man in the middle.
                            if (LogHelper.CurrentLogLevel <= LogLevel.Normal)
                            {
                                if (LogHelper.CurrentLogLevel <= LogLevel.Normal)
                                {
                                    LogHelper.LogWarning("Invalid certificate. Disconnecting");
                                }
                            }
                            netManager.StopClient();
                            return;
                        }
                        else
                        {
                            netManager.NetworkConfig.ServerX509Certificate = certificate;
                        }
                    }

                    // Read the ECDH
                    // Allocation justification: This runs on client and only once, at initial connection
                    serverDiffieHellmanPublicPart = reader.ReadByteArray();

                    // Verify the key exchange
                    if (netManager.NetworkConfig.SignKeyExchange)
                    {
                        byte[] serverDiffieHellmanPublicPartSignature = reader.ReadByteArray();

                        RSACryptoServiceProvider rsa = certificate.PublicKey.Key as RSACryptoServiceProvider;

                        if (rsa != null)
                        {
                            using (SHA256Managed sha = new SHA256Managed())
                            {
                                if (!rsa.VerifyData(serverDiffieHellmanPublicPart, sha, serverDiffieHellmanPublicPartSignature))
                                {
                                    if (LogHelper.CurrentLogLevel <= LogLevel.Normal)
                                    {
                                        if (LogHelper.CurrentLogLevel <= LogLevel.Normal)
                                        {
                                            LogHelper.LogWarning("Invalid signature. Disconnecting");
                                        }
                                    }
                                    netManager.StopClient();
                                    return;
                                }
                            }
                        }
                    }
                }
            }

            using (PooledBitStream outStream = PooledBitStream.Get())
            {
                using (PooledBitWriter writer = PooledBitWriter.Get(outStream))
                {
                    if (netManager.NetworkConfig.EnableEncryption)
                    {
                        // Create a ECDH key
                        EllipticDiffieHellman diffieHellman = new EllipticDiffieHellman(EllipticDiffieHellman.DEFAULT_CURVE, EllipticDiffieHellman.DEFAULT_GENERATOR, EllipticDiffieHellman.DEFAULT_ORDER);
                        netManager.clientAesKey = diffieHellman.GetSharedSecret(serverDiffieHellmanPublicPart);
                        byte[] diffieHellmanPublicKey = diffieHellman.GetPublicKey();
                        writer.WriteByteArray(diffieHellmanPublicKey);
                        if (netManager.NetworkConfig.SignKeyExchange)
                        {
                            RSACryptoServiceProvider rsa = certificate.PublicKey.Key as RSACryptoServiceProvider;

                            if (rsa != null)
                            {
                                using (SHA256CryptoServiceProvider sha = new SHA256CryptoServiceProvider())
                                {
                                    writer.WriteByteArray(rsa.Encrypt(sha.ComputeHash(diffieHellmanPublicKey), false));
                                }
                            }
                            else
                            {
                                throw new CryptographicException("[MLAPI] Only RSA certificates are supported. No valid RSA key was found");
                            }
                        }
                    }
                }
                // Send HailResponse
                InternalMessageHandler.Send(NetworkingManager.Singleton.ServerClientId, MLAPIConstants.MLAPI_CERTIFICATE_HAIL_RESPONSE, "MLAPI_INTERNAL", outStream, SecuritySendFlags.None, true);
            }
        }
Пример #4
0
        public static void Main(string[] args)
        {
            // Create a client session manager and allow sessions to remain valid for up to 5 minutes of inactivity (300 seconds)
            SessionManager manager = new SessionManager(300 * TimeSpan.TicksPerSecond, 20);

            SetConsoleCtrlHandler(i => {
                db.Flush(); // Ensures that the database is flushed before the program exits
                return(false);
            }, true);

            // Create a secure random provider and start getting RSA stuff
            CryptoRandomProvider random = new CryptoRandomProvider();
            Task <RSA>           t      = new Task <RSA>(() =>
            {
                RSA rsa = new RSA(Resources.e_0x100, Resources.n_0x100, Resources.d_0x100);
                if (rsa == null)
                {
                    Output.Fatal("No RSA keys found! Server identity will not be verifiable!");
                    Output.Info("Generating session-specific RSA-keys...");
                    rsa = new RSA(128, 8, 7, 5);
                    rsa.Save("0x100");
                    Output.Info("Done!");
                }
                return(rsa);
            });

            t.Start();

            // Local methods to simplify common operations
            bool ParseDataPair(string cmd, out string user, out string pass)
            {
                int idx = cmd.IndexOf(':');

                user = "";
                pass = "";
                if (idx == -1)
                {
                    return(false);
                }
                user = cmd.Substring(0, idx);
                try
                {
                    user = user.FromBase64String();
                    pass = cmd.Substring(idx + 1).FromBase64String();
                }
                catch
                {
                    Output.Error($"Recieved problematic username or password! (User: \"{user}\")");
                    return(false);
                }
                return(true);
            }

            int ParseDataSet(string cmd, out string[] data)
            {
                List <string> gen = new List <string>();
                int           idx;

                while ((idx = cmd.IndexOf(':')) != -1)
                {
                    try
                    {
                        gen.Add(cmd.Substring(0, idx).FromBase64String());
                    }
                    catch
                    {
                        data = null;
                        return(-1); // Hard error
                    }
                    cmd = cmd.Substring(idx + 1);
                }
                try
                {
                    gen.Add(cmd.FromBase64String());
                }
                catch
                {
                    data = null;
                    return(-1); // Hard error
                }
                data = gen.ToArray();
                return(gen.Count);
            }

            string[] ParseCommand(string cmd, out long id)
            {
                int    idx = cmd.IndexOf(':'), idx1;
                string sub;

                if (idx == -1 || !(sub = cmd.Substring(idx + 1)).Contains(':') || !long.TryParse(sub.Substring(0, idx1 = sub.IndexOf(':')), out id))
                {
                    id = 0;
                    return(null);
                }
                return(new string[] { cmd.Substring(0, idx), sub.Substring(idx1 + 1) });
            }

            string GenerateResponse(long id, dynamic d) => id + ":" + d.ToString();
            string ErrorResponse(long id, string i18n = null) => GenerateResponse(id, $"ERROR{(i18n==null?"":":"+VERBOSE_RESPONSE)}{i18n??""}");

            bool GetUser(string sid, out Database.User user)
            {
                user = manager.GetUser(sid);
                bool exists = user != null;

                if (exists)
                {
                    user = db.GetUser(user.Name);
                }
                return(exists && user != null);
            }

            bool GetAccount(string name, Database.User user, out Database.Account acc)
            {
                acc = user.accounts.FirstOrDefault(a => a.name.Equals(name));
                return(acc != null);
            }

            // Create server
            NetServer server = new NetServer(
                EllipticDiffieHellman.Curve25519(EllipticDiffieHellman.Curve25519_GeneratePrivate(random)),
                80,
                (string r, Dictionary <string, string> associations, ref bool s) =>
            {
                string[] cmd = ParseCommand(r, out long id);

                // Handle corrupt or badly formatted messages from client
                if (cmd == null)
                {
                    return(ErrorResponse(-1, "corrupt"));
                }

                // Server endpoints
                switch (cmd[0])
                {
                case "RmUsr":
                    {
                        if (!GetUser(cmd[1], out var user))
                        {
                            if (verbosity > 0)
                            {
                                Output.Error($"Could not delete user from session as session isn't valid. (SessionID=\"{cmd[1]}\")");
                            }
                            return(ErrorResponse(id, "badsession"));
                        }
                        manager.Expire(user);
                        db.RemoveUser(user);
                        if (verbosity > 0)
                        {
                            Output.Info($"Removed user \"{user.Name}\" (SessionID={cmd[1]})");
                        }
                        return(GenerateResponse(id, true));
                    }

                case "Auth":         // Log in to a user account (get a session id)
                    {
                        if (!ParseDataPair(cmd[1], out string user, out string pass))
                        {
                            if (verbosity > 0)
                            {
                                Output.Error($"Recieved problematic username or password! (User: \"{user}\")");
                            }
                            return(ErrorResponse(id));
                        }
                        Database.User usr = db.GetUser(user);
                        if (usr == null || !usr.Authenticate(pass))
                        {
                            if (verbosity > 0)
                            {
                                Output.Error("Authentcation failure for user: "******"ERROR");
                        Output.Positive("Authentication success for user: "******"\nSession: " + sess);
                        associations["session"] = sess;
                        return(GenerateResponse(id, sess));
                    }