Beispiel #1
0
        /// <summary>
        /// Tries to parse the given input buffer into a new <see cref="AuthenticationResults"/> instance.
        /// </summary>
        /// <remarks>
        /// Parses an Authentication-Results header value from the supplied buffer starting at the given index
        /// and spanning across the specified number of bytes.
        /// </remarks>
        /// <returns><c>true</c> if the authentication results were successfully parsed; otherwise, <c>false</c>.</returns>
        /// <param name="buffer">The input buffer.</param>
        /// <param name="startIndex">The starting index of the input buffer.</param>
        /// <param name="length">The number of bytes in the input buffer to parse.</param>
        /// <param name="authres">The parsed authentication results.</param>
        /// <exception cref="System.ArgumentNullException">
        /// <paramref name="buffer"/> is <c>null</c>.
        /// </exception>
        /// <exception cref="System.ArgumentOutOfRangeException">
        /// <paramref name="startIndex"/> and <paramref name="length"/> do not specify
        /// a valid range in the byte array.
        /// </exception>
        public static bool TryParse(byte[] buffer, int startIndex, int length, out AuthenticationResults authres)
        {
            ParseUtils.ValidateArguments(buffer, startIndex, length);

            int index = startIndex;

            return(TryParse(buffer, ref index, startIndex + length, false, out authres));
        }
Beispiel #2
0
        /// <summary>
        /// Tries to parse the given input buffer into a new <see cref="AuthenticationResults"/> instance.
        /// </summary>
        /// <remarks>
        /// Parses an Authentication-Results header value from the supplied buffer.
        /// </remarks>
        /// <returns><c>true</c> if the authentication results were successfully parsed; otherwise, <c>false</c>.</returns>
        /// <param name="buffer">The input buffer.</param>
        /// <param name="authres">The parsed authentication results.</param>
        /// <exception cref="System.ArgumentNullException">
        /// <paramref name="buffer"/> is <c>null</c>.
        /// </exception>
        public static bool TryParse(byte[] buffer, out AuthenticationResults authres)
        {
            if (buffer == null)
            {
                throw new ArgumentNullException(nameof(buffer));
            }

            int index = 0;

            return(TryParse(buffer, ref index, buffer.Length, false, out authres));
        }
Beispiel #3
0
        internal static ArcSignatureValidationResult GetArcHeaderSets(MimeMessage message, bool throwOnError, out ArcHeaderSet[] sets, out int count)
        {
            ArcHeaderSet set;

            sets  = new ArcHeaderSet[50];
            count = 0;

            for (int i = 0; i < message.Headers.Count; i++)
            {
                Dictionary <string, string> parameters = null;
                var    header = message.Headers[i];
                int    instance;
                string value;

                switch (header.Id)
                {
                case HeaderId.ArcAuthenticationResults:
                    if (!AuthenticationResults.TryParse(header.RawValue, out AuthenticationResults authres))
                    {
                        if (throwOnError)
                        {
                            throw new FormatException("Invalid ARC-Authentication-Results header.");
                        }

                        return(ArcSignatureValidationResult.Fail);
                    }

                    if (!authres.Instance.HasValue)
                    {
                        if (throwOnError)
                        {
                            throw new FormatException("Missing instance tag in ARC-Authentication-Results header.");
                        }

                        return(ArcSignatureValidationResult.Fail);
                    }

                    instance = authres.Instance.Value;

                    if (instance < 1 || instance > 50)
                    {
                        if (throwOnError)
                        {
                            throw new FormatException(string.Format("Invalid instance tag in ARC-Authentication-Results header: i={0}", instance));
                        }

                        return(ArcSignatureValidationResult.Fail);
                    }
                    break;

                case HeaderId.ArcMessageSignature:
                case HeaderId.ArcSeal:
                    try {
                        parameters = ParseParameterTags(header.Id, header.Value);
                    } catch {
                        if (throwOnError)
                        {
                            throw;
                        }

                        return(ArcSignatureValidationResult.Fail);
                    }

                    if (!parameters.TryGetValue("i", out value))
                    {
                        if (throwOnError)
                        {
                            throw new FormatException(string.Format("Missing instance tag in {0} header.", header.Id.ToHeaderName()));
                        }

                        return(ArcSignatureValidationResult.Fail);
                    }

                    if (!int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out instance) || instance < 1 || instance > 50)
                    {
                        if (throwOnError)
                        {
                            throw new FormatException(string.Format("Invalid instance tag in {0} header: i={1}", header.Id.ToHeaderName(), value));
                        }

                        return(ArcSignatureValidationResult.Fail);
                    }
                    break;

                default:
                    instance = 0;
                    break;
                }

                if (instance == 0)
                {
                    continue;
                }

                set = sets[instance - 1];
                if (set == null)
                {
                    sets[instance - 1] = set = new ArcHeaderSet();
                }

                if (!set.Add(header, parameters))
                {
                    return(ArcSignatureValidationResult.Fail);
                }

                if (instance > count)
                {
                    count = instance;
                }
            }

            if (count == 0)
            {
                // there are no ARC sets
                return(ArcSignatureValidationResult.None);
            }

            // verify that all ARC sets are complete
            for (int i = 0; i < count; i++)
            {
                set = sets[i];

                if (set == null)
                {
                    if (throwOnError)
                    {
                        throw new FormatException(string.Format("Missing ARC headers for i={0}", i + 1));
                    }

                    return(ArcSignatureValidationResult.Fail);
                }

                if (set.ArcAuthenticationResult == null)
                {
                    if (throwOnError)
                    {
                        throw new FormatException(string.Format("Missing ARC-Authentication-Results header for i={0}", i + 1));
                    }

                    return(ArcSignatureValidationResult.Fail);
                }

                if (set.ArcMessageSignature == null)
                {
                    if (throwOnError)
                    {
                        throw new FormatException(string.Format("Missing ARC-Message-Signature header for i={0}", i + 1));
                    }

                    return(ArcSignatureValidationResult.Fail);
                }

                if (set.ArcSeal == null)
                {
                    if (throwOnError)
                    {
                        throw new FormatException(string.Format("Missing ARC-Seal header for i={0}", i + 1));
                    }

                    return(ArcSignatureValidationResult.Fail);
                }

                if (!set.ArcSealParameters.TryGetValue("cv", out string cv))
                {
                    if (throwOnError)
                    {
                        throw new FormatException(string.Format("Missing chain validation tag in ARC-Seal header for i={0}.", i + 1));
                    }

                    return(ArcSignatureValidationResult.Fail);
                }

                // The "cv" value for all ARC-Seal header fields MUST NOT be
                // "fail". For ARC Sets with instance values > 1, the values
                // MUST be "pass". For the ARC Set with instance value = 1, the
                // value MUST be "none".
                if (!cv.Equals(i == 0 ? "none" : "pass", StringComparison.Ordinal))
                {
                    return(ArcSignatureValidationResult.Fail);
                }
            }

            return(ArcSignatureValidationResult.Pass);
        }
Beispiel #4
0
        static bool TryParse(byte[] text, ref int index, int endIndex, bool throwOnError, out AuthenticationResults authres)
        {
            int?   instance = null;
            string srvid    = null;
            string value;
            bool   quoted;

            authres = null;

            if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError))
            {
                return(false);
            }

            do
            {
                int start = index;

                if (index >= endIndex || !SkipValue(text, ref index, endIndex, out quoted))
                {
                    if (throwOnError)
                    {
                        throw new ParseException(string.Format("Incomplete authserv-id token at offset {0}", start), start, index);
                    }

                    return(false);
                }

                value = Encoding.UTF8.GetString(text, start, index - start);

                if (quoted)
                {
                    // this can only be the authserv-id token
                    srvid = MimeUtils.Unquote(value);
                }
                else
                {
                    // this could either be the authserv-id or it could be "i=#" (ARC instance)
                    if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError))
                    {
                        return(false);
                    }

                    if (index < endIndex && text[index] == (byte)'=')
                    {
                        // probably i=#
                        if (instance.HasValue)
                        {
                            if (throwOnError)
                            {
                                throw new ParseException(string.Format("Invalid token at offset {0}", start), start, index);
                            }

                            return(false);
                        }

                        if (value != "i")
                        {
                            if (throwOnError)
                            {
                                throw new ParseException(string.Format("Invalid instance token at offset {0}", start), start, index);
                            }

                            return(false);
                        }

                        // skip over '='
                        index++;

                        if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError))
                        {
                            return(false);
                        }

                        start = index;

                        if (!ParseUtils.TryParseInt32(text, ref index, endIndex, out int i))
                        {
                            if (throwOnError)
                            {
                                throw new ParseException(string.Format("Invalid instance value at offset {0}", start), start, index);
                            }

                            return(false);
                        }

                        instance = i;

                        if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError))
                        {
                            return(false);
                        }

                        if (index >= endIndex)
                        {
                            if (throwOnError)
                            {
                                throw new ParseException(string.Format("Missing semi-colon after instance value at offset {0}", start), start, index);
                            }

                            return(false);
                        }

                        if (text[index] != ';')
                        {
                            if (throwOnError)
                            {
                                throw new ParseException(string.Format("Unexpected token after instance value at offset {0}", index), index, index);
                            }

                            return(false);
                        }

                        // skip over ';'
                        index++;

                        if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError))
                        {
                            return(false);
                        }
                    }
                    else
                    {
                        srvid = value;
                    }
                }
            } while (srvid == null);

            authres = new AuthenticationResults(srvid)
            {
                Instance = instance
            };

            if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError))
            {
                return(false);
            }

            if (index >= endIndex)
            {
                return(true);
            }

            if (text[index] != (byte)';')
            {
                // might be the authres-version token
                int start = index;

                if (!ParseUtils.TryParseInt32(text, ref index, endIndex, out int version))
                {
                    if (throwOnError)
                    {
                        throw new ParseException(string.Format("Invalid authres-version at offset {0}", start), start, index);
                    }

                    return(false);
                }

                authres.Version = version;

                if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError))
                {
                    return(false);
                }

                if (index >= endIndex)
                {
                    return(true);
                }

                if (text[index] != (byte)';')
                {
                    if (throwOnError)
                    {
                        throw new ParseException(string.Format("Unknown token at offset {0}", index), index, index);
                    }

                    return(false);
                }
            }

            // skip the ';'
            index++;

            while (index < endIndex)
            {
                if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError))
                {
                    return(false);
                }

                if (index >= endIndex)
                {
                    break;
                }

                int methodIndex = index;

                // skip the method name
                if (!SkipKeyword(text, ref index, endIndex))
                {
                    if (throwOnError)
                    {
                        throw new ParseException(string.Format("Invalid method token at offset {0}", methodIndex), methodIndex, index);
                    }

                    return(false);
                }

                var method = Encoding.ASCII.GetString(text, methodIndex, index - methodIndex);

                if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError))
                {
                    return(false);
                }

                if (index >= endIndex)
                {
                    if (method != "none")
                    {
                        if (throwOnError)
                        {
                            throw new ParseException(string.Format("Incomplete methodspec token at offset {0}", methodIndex), methodIndex, index);
                        }

                        return(false);
                    }

                    if (authres.Results.Count > 0)
                    {
                        if (throwOnError)
                        {
                            throw new ParseException(string.Format("Invalid no-result token at offset {0}", methodIndex), methodIndex, index);
                        }

                        return(false);
                    }

                    break;
                }

                var resinfo = new AuthenticationMethodResult(method);
                authres.Results.Add(resinfo);

                int tokenIndex;

                if (text[index] == (byte)'/')
                {
                    // optional method-version token
                    index++;

                    if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError))
                    {
                        return(false);
                    }

                    tokenIndex = index;

                    if (!ParseUtils.TryParseInt32(text, ref index, endIndex, out int version))
                    {
                        if (throwOnError)
                        {
                            throw new ParseException(string.Format("Invalid method-version token at offset {0}", tokenIndex), tokenIndex, index);
                        }

                        return(false);
                    }

                    resinfo.Version = version;

                    if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError))
                    {
                        return(false);
                    }

                    if (index >= endIndex)
                    {
                        if (throwOnError)
                        {
                            throw new ParseException(string.Format("Incomplete methodspec token at offset {0}", methodIndex), methodIndex, index);
                        }

                        return(false);
                    }
                }

                if (text[index] != (byte)'=')
                {
                    if (throwOnError)
                    {
                        throw new ParseException(string.Format("Invalid methodspec token at offset {0}", methodIndex), methodIndex, index);
                    }

                    return(false);
                }

                // skip over '='
                index++;

                if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError))
                {
                    return(false);
                }

                if (index >= endIndex)
                {
                    if (throwOnError)
                    {
                        throw new ParseException(string.Format("Incomplete methodspec token at offset {0}", methodIndex), methodIndex, index);
                    }

                    return(false);
                }

                tokenIndex = index;

                if (!SkipKeyword(text, ref index, endIndex))
                {
                    if (throwOnError)
                    {
                        throw new ParseException(string.Format("Invalid result token at offset {0}", tokenIndex), tokenIndex, index);
                    }

                    return(false);
                }

                resinfo.Result = Encoding.ASCII.GetString(text, tokenIndex, index - tokenIndex);

                ParseUtils.SkipWhiteSpace(text, ref index, endIndex);

                if (index < endIndex && text[index] == '(')
                {
                    int commentIndex = index;

                    if (!ParseUtils.SkipComment(text, ref index, endIndex))
                    {
                        if (throwOnError)
                        {
                            throw new ParseException(string.Format("Incomplete comment token at offset {0}", commentIndex), commentIndex, index);
                        }

                        return(false);
                    }

                    commentIndex++;

                    resinfo.ResultComment = Header.Unfold(Encoding.UTF8.GetString(text, commentIndex, (index - 1) - commentIndex));

                    if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError))
                    {
                        return(false);
                    }
                }

                if (index >= endIndex)
                {
                    break;
                }

                if (text[index] == (byte)';')
                {
                    index++;
                    continue;
                }

                // optional reasonspec or propspec
                tokenIndex = index;

                if (!SkipKeyword(text, ref index, endIndex))
                {
                    if (throwOnError)
                    {
                        throw new ParseException(string.Format("Invalid reasonspec or propspec token at offset {0}", tokenIndex), tokenIndex, index);
                    }

                    return(false);
                }

                value = Encoding.ASCII.GetString(text, tokenIndex, index - tokenIndex);

                if (value == "reason")
                {
                    if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError))
                    {
                        return(false);
                    }

                    if (index >= endIndex)
                    {
                        if (throwOnError)
                        {
                            throw new ParseException(string.Format("Incomplete reasonspec token at offset {0}", tokenIndex), tokenIndex, index);
                        }

                        return(false);
                    }

                    if (text[index] != (byte)'=')
                    {
                        if (throwOnError)
                        {
                            throw new ParseException(string.Format("Invalid reasonspec token at offset {0}", tokenIndex), tokenIndex, index);
                        }

                        return(false);
                    }

                    index++;

                    if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError))
                    {
                        return(false);
                    }

                    int reasonIndex = index;

                    if (index >= endIndex || !SkipValue(text, ref index, endIndex, out quoted))
                    {
                        if (throwOnError)
                        {
                            throw new ParseException(string.Format("Invalid reasonspec value token at offset {0}", reasonIndex), reasonIndex, index);
                        }

                        return(false);
                    }

                    resinfo.Reason = Encoding.UTF8.GetString(text, reasonIndex, index - reasonIndex);

                    if (quoted)
                    {
                        resinfo.Reason = MimeUtils.Unquote(resinfo.Reason);
                    }

                    if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError))
                    {
                        return(false);
                    }

                    if (index >= endIndex)
                    {
                        break;
                    }

                    if (text[index] == (byte)';')
                    {
                        index++;
                        continue;
                    }

                    // optional propspec
                    tokenIndex = index;

                    if (!SkipKeyword(text, ref index, endIndex))
                    {
                        if (throwOnError)
                        {
                            throw new ParseException(string.Format("Invalid propspec token at offset {0}", tokenIndex), tokenIndex, index);
                        }

                        return(false);
                    }

                    value = Encoding.ASCII.GetString(text, tokenIndex, index - tokenIndex);
                }

                do
                {
                    // value is a propspec ptype token
                    var ptype = value;

                    if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError))
                    {
                        return(false);
                    }

                    if (index >= endIndex)
                    {
                        if (throwOnError)
                        {
                            throw new ParseException(string.Format("Incomplete propspec token at offset {0}", tokenIndex), tokenIndex, index);
                        }

                        return(false);
                    }

                    if (text[index] != (byte)'.')
                    {
                        if (throwOnError)
                        {
                            throw new ParseException(string.Format("Invalid propspec token at offset {0}", tokenIndex), tokenIndex, index);
                        }

                        return(false);
                    }

                    index++;

                    if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError))
                    {
                        return(false);
                    }

                    if (index >= endIndex)
                    {
                        if (throwOnError)
                        {
                            throw new ParseException(string.Format("Incomplete propspec token at offset {0}", tokenIndex), tokenIndex, index);
                        }

                        return(false);
                    }

                    int propertyIndex = index;

                    if (!SkipKeyword(text, ref index, endIndex))
                    {
                        if (throwOnError)
                        {
                            throw new ParseException(string.Format("Invalid property token at offset {0}", propertyIndex), propertyIndex, index);
                        }

                        return(false);
                    }

                    var property = Encoding.ASCII.GetString(text, propertyIndex, index - propertyIndex);

                    if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError))
                    {
                        return(false);
                    }

                    if (index >= endIndex)
                    {
                        if (throwOnError)
                        {
                            throw new ParseException(string.Format("Incomplete propspec token at offset {0}", tokenIndex), tokenIndex, index);
                        }

                        return(false);
                    }

                    if (text[index] != (byte)'=')
                    {
                        if (throwOnError)
                        {
                            throw new ParseException(string.Format("Invalid propspec token at offset {0}", tokenIndex), tokenIndex, index);
                        }

                        return(false);
                    }

                    index++;

                    if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError))
                    {
                        return(false);
                    }

                    if (index >= endIndex)
                    {
                        if (throwOnError)
                        {
                            throw new ParseException(string.Format("Incomplete propspec token at offset {0}", tokenIndex), tokenIndex, index);
                        }

                        return(false);
                    }

                    int valueIndex = index;

                    while (index < endIndex && text[index] != ';' && !text[index].IsWhitespace())
                    {
                        index++;
                    }

                    if (index == valueIndex)
                    {
                        if (throwOnError)
                        {
                            throw new ParseException(string.Format("Incomplete propspec token at offset {0}", tokenIndex), tokenIndex, index);
                        }

                        return(false);
                    }

                    value = Encoding.UTF8.GetString(text, valueIndex, index - valueIndex);

                    var propspec = new AuthenticationMethodProperty(ptype, property, value);
                    resinfo.Properties.Add(propspec);

                    if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError))
                    {
                        return(false);
                    }

                    if (index >= endIndex || text[index] == (byte)';')
                    {
                        break;
                    }

                    tokenIndex = index;

                    if (!SkipKeyword(text, ref index, endIndex))
                    {
                        if (throwOnError)
                        {
                            throw new ParseException(string.Format("Invalid propspec token at offset {0}", tokenIndex), tokenIndex, index);
                        }

                        return(false);
                    }

                    value = Encoding.ASCII.GetString(text, tokenIndex, index - tokenIndex);
                } while (true);

                // skip over ';'
                index++;
            }

            return(true);
        }