Пример #1
0
        internal override void ProcessExtraHandshake()
        {
            /*
             * If we receive a non-empty handshake message, then
             * it should be an HelloRequest. Note that we expect
             * the request not to be mixed with application data
             * records (though it could be split).
             */
            ReadHelloRequests();

            /*
             * We accept to renegotiate only if the server supports
             * the Secure Renegotiation extension.
             */
            if (renegSupport != 1)
            {
                SendWarning(SSL.NO_RENEGOTIATION);
                SetAppData();
                return;
            }

            /*
             * We do a new handshake. We explicitly refuse to reuse
             * session parameters, because there is no point in
             * renegotiation if this resumes the same session.
             */
            resumeParams = null;
            DoHandshake();
        }
Пример #2
0
 /* see ISessionCache */
 public void Store(SSLSessionParameters sp)
 {
     lock (mutex) {
         string ids = IDToString(sp.SessionID);
         int    x;
         if (spx.TryGetValue(ids, out x))
         {
             if ((x + 1) < count)
             {
                 Array.Copy(data, x + 1,
                            data, x, count - x - 1);
             }
             spx[ids]        = count - 1;
             data[count - 1] = sp;
             return;
         }
         if (count == maxCount)
         {
             SSLSessionParameters esp = data[0];
             Array.Copy(data, 1, data, 0, count - 1);
             count--;
             spx.Remove(IDToString(esp.SessionID));
         }
         spx[ids]    = count;
         data[count] = sp;
         count++;
     }
 }
Пример #3
0
 /*
  * Create the client over the provided stream. If the
  * 'sessionParameters' are not null, then the client will try to
  * resume that session. Note that session parameters may include
  * a "target server name", in which case the ServerName
  * property will be set to that name, which will be included
  * in the ClientHello as the Server Name Indication extension.
  */
 public SSLClient(Stream sub, SSLSessionParameters sessionParameters)
     : base(sub)
 {
     sentExtensions = new List <int>();
     resumeParams   = sessionParameters;
     if (resumeParams != null)
     {
         string name = resumeParams.ServerName;
         if (name != null)
         {
             ServerName = name;
         }
     }
 }
Пример #4
0
 /* see ISessionCache */
 public SSLSessionParameters Retrieve(byte[] id, string serverName)
 {
     lock (mutex) {
         int x;
         if (!spx.TryGetValue(IDToString(id), out x))
         {
             return(null);
         }
         SSLSessionParameters sp = data[x];
         if ((x + 1) < count)
         {
             Array.Copy(data, x + 1,
                        data, x, count - x - 1);
             data[count - 1] = sp;
         }
         return(sp);
     }
 }
Пример #5
0
        bool ParseClientHello(out bool resume)
        {
            resume = false;
            int mt;

            byte[] msg = ReadHandshakeMessage(out mt, FirstHandshakeDone);
            if (msg == null)
            {
                /*
                 * Client rejected renegotiation attempt. This cannot
                 * happen if we are invoked from
                 * ProcessExtraHandshake() because that method is
                 * invoked only when there is buffered handshake
                 * data.
                 */
                return(false);
            }
            if (mt != SSL.CLIENT_HELLO)
            {
                throw new SSLException(string.Format("Unexpected"
                                                     + " handshake message {0} (expecting a"
                                                     + " ClientHello)", mt));
            }

            /*
             * Maximum protocol version supported by the client.
             */
            if (msg.Length < 35)
            {
                throw new SSLException("Invalid ClientHello");
            }
            ClientVersionMax = IO.Dec16be(msg, 0);
            if (ClientVersionMax < VersionMin)
            {
                throw new SSLException(string.Format(
                                           "No acceptable version (client max = 0x{0:X4})",
                                           ClientVersionMax));
            }

            /*
             * Client random (32 bytes).
             */
            Array.Copy(msg, 2, clientRandom, 0, 32);

            /*
             * Session ID sent by the client: at most 32 bytes.
             */
            int idLen = msg[34];
            int off   = 35;

            if (idLen > 32 || (off + idLen) > msg.Length)
            {
                throw new SSLException("Invalid ClientHello");
            }
            byte[] clientSessionID = new byte[idLen];
            Array.Copy(msg, off, clientSessionID, 0, idLen);
            off += idLen;

            /*
             * List of client cipher suites.
             */
            if ((off + 2) > msg.Length)
            {
                throw new SSLException("Invalid ClientHello");
            }
            int csLen = IO.Dec16be(msg, off);

            off += 2;
            if ((off + csLen) > msg.Length)
            {
                throw new SSLException("Invalid ClientHello");
            }
            List <int> clientSuites = new List <int>();
            bool       seenReneg    = false;

            while (csLen > 0)
            {
                int cs = IO.Dec16be(msg, off);
                off   += 2;
                csLen -= 2;
                if (cs == SSL.FALLBACK_SCSV)
                {
                    if (ClientVersionMax < VersionMax)
                    {
                        throw new SSLException(
                                  "Undue fallback detected");
                    }
                }
                else if (cs == SSL.EMPTY_RENEGOTIATION_INFO_SCSV)
                {
                    if (FirstHandshakeDone)
                    {
                        throw new SSLException(
                                  "Reneg SCSV in renegotiation");
                    }
                    seenReneg = true;
                }
                else
                {
                    clientSuites.Add(cs);
                }
            }

            /*
             * List of compression methods. We only accept method 0
             * (no compression).
             */
            if ((off + 1) > msg.Length)
            {
                throw new SSLException("Invalid ClientHello");
            }
            int compLen = msg[off++];

            if ((off + compLen) > msg.Length)
            {
                throw new SSLException("Invalid ClientHello");
            }
            bool foundUncompressed = false;

            while (compLen-- > 0)
            {
                if (msg[off++] == 0x00)
                {
                    foundUncompressed = true;
                }
            }
            if (!foundUncompressed)
            {
                throw new SSLException("No common compression support");
            }

            /*
             * Extensions.
             */
            ClientHashAndSign = null;
            ClientCurves      = null;
            if (off < msg.Length)
            {
                if ((off + 2) > msg.Length)
                {
                    throw new SSLException("Invalid ClientHello");
                }
                int tlen = IO.Dec16be(msg, off);
                off += 2;
                if ((off + tlen) != msg.Length)
                {
                    throw new SSLException("Invalid ClientHello");
                }
                while (off < msg.Length)
                {
                    if ((off + 4) > msg.Length)
                    {
                        throw new SSLException(
                                  "Invalid ClientHello");
                    }
                    int etype = IO.Dec16be(msg, off);
                    int elen  = IO.Dec16be(msg, off + 2);
                    off += 4;
                    if ((off + elen) > msg.Length)
                    {
                        throw new SSLException(
                                  "Invalid ClientHello");
                    }
                    switch (etype)
                    {
                    case 0x0000:
                        ParseExtSNI(msg, off, elen);
                        break;

                    case 0x000D:
                        ParseExtSignatures(msg, off, elen);
                        break;

                    case 0x000A:
                        ParseExtCurves(msg, off, elen);
                        break;

                    case 0xFF01:
                        ParseExtSecureReneg(msg, off, elen);
                        seenReneg = true;
                        break;

                        // Max Frag Length
                        // ALPN
                        // FIXME
                    }

                    off += elen;
                }
            }

            /*
             * If we are renegotiating and we did not see the
             * Secure Renegotiation extension, then this is an error.
             */
            if (FirstHandshakeDone && !seenReneg)
            {
                throw new SSLException(
                          "Missing Secure Renegotiation extension");
            }

            /*
             * Use prescribed default values for supported algorithms
             * and curves, when not otherwise advertised by the client.
             */
            if (ClientCurves == null)
            {
                ClientCurves = new List <int>();
                foreach (int cs in clientSuites)
                {
                    if (SSL.IsECDH(cs) || SSL.IsECDHE(cs))
                    {
                        ClientCurves.Add(SSL.NIST_P256);
                        break;
                    }
                }
            }
            if (ClientHashAndSign == null)
            {
                bool withRSA   = false;
                bool withECDSA = false;
                foreach (int cs in clientSuites)
                {
                    if (SSL.IsRSA(cs) ||
                        SSL.IsECDH_RSA(cs) ||
                        SSL.IsECDHE_RSA(cs))
                    {
                        withRSA = true;
                    }
                    if (SSL.IsECDH_ECDSA(cs) ||
                        SSL.IsECDHE_ECDSA(cs))
                    {
                        withECDSA = true;
                    }
                }
                ClientHashAndSign = new List <int>();
                if (withRSA)
                {
                    ClientHashAndSign.Add(SSL.RSA_SHA1);
                }
                if (withECDSA)
                {
                    ClientHashAndSign.Add(SSL.ECDSA_SHA1);
                }
            }

            /*
             * Filter curves and algorithms with regards to our own
             * configuration.
             */
            CommonCurves = FilterList(ClientCurves,
                                      SupportedCurves, EnforceServerOrder);
            ClientHashAndSign = FilterList(ClientHashAndSign,
                                           SupportedHashAndSign, EnforceServerOrder);

            /*
             * Selected protocol version (can be overridden by
             * resumption).
             */
            Version = Math.Min(ClientVersionMax, VersionMax);
            string forcedVersion = GetQuirkString("forceVersion");

            if (forcedVersion != null)
            {
                switch (forcedVersion)
                {
                case "TLS10": Version = SSL.TLS10; break;

                case "TLS11": Version = SSL.TLS11; break;

                case "TLS12": Version = SSL.TLS12; break;

                default:
                    throw new Exception(string.Format(
                                            "Unknown forced version: '{0}'",
                                            forcedVersion));
                }
            }

            /*
             * Recompute list of acceptable cipher suites. We keep
             * only suites which are common to the client and server,
             * with some extra filters.
             *
             * Note that when using static ECDH, it is up to the
             * policy callback to determine whether the curves match
             * the contents of the certificate.
             *
             * We also build a list of common suites for session
             * resumption: this one may include suites whose
             * asymmetric crypto is not supported, because session
             * resumption uses only symmetric crypto.
             */
            CommonCipherSuites = new List <int>();
            List <int> commonSuitesResume = new List <int>();
            bool       canTLS12           = Version >= SSL.TLS12;
            bool       mustTLS12          = false;

            if (GetQuirkBool("forceTls12CipherSuite"))
            {
                canTLS12  = true;
                mustTLS12 = true;
            }
            bool canSignRSA;
            bool canSignECDSA;

            if (Version >= SSL.TLS12)
            {
                canSignRSA   = false;
                canSignECDSA = false;
                foreach (int alg in ClientHashAndSign)
                {
                    int sa = alg & 0xFF;
                    switch (sa)
                    {
                    case SSL.RSA:    canSignRSA = true;    break;

                    case SSL.ECDSA:  canSignECDSA = true;  break;
                    }
                }
            }
            else
            {
                /*
                 * For pre-1.2, the hash-and-sign configuration does
                 * not matter, only the cipher suites themselves. So
                 * we claim support of both RSA and ECDSA signatures
                 * to avoid trimming the list too much.
                 */
                canSignRSA   = true;
                canSignECDSA = true;
            }
            bool canECDHE = CommonCurves.Count > 0;

            foreach (int cs in clientSuites)
            {
                if (!canTLS12 && SSL.IsTLS12(cs))
                {
                    continue;
                }
                if (mustTLS12 && !SSL.IsTLS12(cs))
                {
                    continue;
                }
                commonSuitesResume.Add(cs);
                if (!canECDHE && SSL.IsECDHE(cs))
                {
                    continue;
                }
                if (!canSignRSA && SSL.IsECDHE_RSA(cs))
                {
                    continue;
                }
                if (!canSignECDSA && SSL.IsECDHE_ECDSA(cs))
                {
                    continue;
                }
                CommonCipherSuites.Add(cs);
            }
            CommonCipherSuites = FilterList(CommonCipherSuites,
                                            SupportedCipherSuites, EnforceServerOrder);
            commonSuitesResume = FilterList(commonSuitesResume,
                                            SupportedCipherSuites, EnforceServerOrder);

            /*
             * If resuming, then use the remembered session parameters,
             * but only if they are compatible with what the client
             * sent AND what we currently support.
             */
            SSLSessionParameters sp = null;

            if (idLen > 0 && !NoResume && SessionCache != null)
            {
                sp = SessionCache.Retrieve(
                    clientSessionID, ServerName);
                if (sp != null && sp.ServerName != null &&
                    ServerName != null)
                {
                    /*
                     * When resuming a session, if there is
                     * an explicit name sent by the client,
                     * and the cached parameters also include
                     * an explicit name, then both names
                     * shall match.
                     */
                    string s1 = sp.ServerName.ToLowerInvariant();
                    string s2 = ServerName.ToLowerInvariant();
                    if (s1 != s2)
                    {
                        sp = null;
                    }
                }
            }
            if (sp != null)
            {
                bool resumeOK = true;
                if (sp.Version < VersionMin ||
                    sp.Version > VersionMax ||
                    sp.Version > ClientVersionMax)
                {
                    resumeOK = false;
                }
                if (!commonSuitesResume.Contains(sp.CipherSuite))
                {
                    resumeOK = false;
                }

                if (resumeOK)
                {
                    /*
                     * Session resumption is acceptable.
                     */
                    resume      = true;
                    sessionID   = clientSessionID;
                    Version     = sp.Version;
                    CipherSuite = sp.CipherSuite;
                    sessionID   = clientSessionID;
                    SetMasterSecret(sp.MasterSecret);
                    return(true);
                }
            }

            /*
             * Not resuming. Let's select parameters.
             * Protocol version was already set.
             */
            if (CommonCipherSuites.Count == 0)
            {
                throw new SSLException("No common cipher suite");
            }
            serverChoices = ServerPolicy.Apply(this);
            CipherSuite   = serverChoices.GetCipherSuite();

            /*
             * We create a new session ID, even if we don't have a
             * session cache, because the session parameters could
             * be extracted manually by the application.
             */
            sessionID = new byte[32];
            RNG.GetBytes(sessionID);

            return(true);
        }