< Summary - CoreWCF Coverage — PR #1766

Information
Class: CoreWCF.Security.ContextImportHelper
Assembly: CoreWCF.Primitives
File(s): /home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.Primitives/src/CoreWCF/Security/SecurityVerifiedMessage.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 49
Coverable lines: 49
Total lines: 570
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 20
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
CreateSplicedReader(...)0%220%
GetPrefixIfNamespaceDeclaration(...)0%660%
IsNamespaceDeclaration(...)100%110%
SpliceBuffers(...)0%660%
WriteNamespaceDeclarations(...)0%660%

File(s)

/home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.Primitives/src/CoreWCF/Security/SecurityVerifiedMessage.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System;
 5using System.Diagnostics;
 6using System.IO;
 7using System.Xml;
 8using CoreWCF.Channels;
 9using CoreWCF.Diagnostics;
 10using CoreWCF.Runtime;
 11
 12namespace CoreWCF.Security
 13{
 14    internal sealed class SecurityVerifiedMessage : DelegatingMessage
 15    {
 16        private byte[] _decryptedBuffer;
 17        private XmlDictionaryReader _cachedDecryptedBodyContentReader;
 18        private XmlAttributeHolder[] _envelopeAttributes;
 19        private XmlAttributeHolder[] _headerAttributes;
 20        private XmlAttributeHolder[] _bodyAttributes;
 21        private string _envelopePrefix;
 22        private bool _bodyDecrypted;
 23        private BodyState _state = BodyState.Created;
 24        private string _bodyPrefix;
 25        private bool _isDecryptedBodyStatusDetermined;
 26        private bool _isDecryptedBodyFault;
 27        private bool _isDecryptedBodyEmpty;
 28        private XmlDictionaryReader _cachedReaderAtSecurityHeader;
 29        private XmlBuffer _messageBuffer;
 30        private bool _canDelegateCreateBufferedCopyToInnerMessage;
 31
 32        public SecurityVerifiedMessage(Message messageToProcess, ReceiveSecurityHeader securityHeader)
 33            : base(messageToProcess)
 34        {
 35            ReceivedSecurityHeader = securityHeader;
 36            if (securityHeader.RequireMessageProtection)
 37            {
 38                XmlDictionaryReader messageReader;
 39                if (InnerMessage is BufferedMessage bufferedMessage && Headers.ContainsOnlyBufferedMessageHeaders)
 40                {
 41                    messageReader = bufferedMessage.GetMessageReader();
 42                }
 43                else
 44                {
 45                    _messageBuffer = new XmlBuffer(int.MaxValue);
 46                    XmlDictionaryWriter writer = _messageBuffer.OpenSection(ReceivedSecurityHeader.ReaderQuotas);
 47                    InnerMessage.WriteMessage(writer);
 48                    _messageBuffer.CloseSection();
 49                    _messageBuffer.Close();
 50                    messageReader = _messageBuffer.GetReader(0);
 51                }
 52                MoveToSecurityHeader(messageReader, securityHeader.HeaderIndex, true);
 53                _cachedReaderAtSecurityHeader = messageReader;
 54                _state = BodyState.Buffered;
 55            }
 56            else
 57            {
 58                _envelopeAttributes = XmlAttributeHolder.emptyArray;
 59                _headerAttributes = XmlAttributeHolder.emptyArray;
 60                _bodyAttributes = XmlAttributeHolder.emptyArray;
 61                _canDelegateCreateBufferedCopyToInnerMessage = true;
 62            }
 63        }
 64
 65        public override bool IsEmpty
 66        {
 67            get
 68            {
 69                if (IsDisposed)
 70                {
 71                    throw TraceUtility.ThrowHelperError(CreateMessageDisposedException(), this);
 72                }
 73                if (!_bodyDecrypted)
 74                {
 75                    return InnerMessage.IsEmpty;
 76                }
 77
 78                EnsureDecryptedBodyStatusDetermined();
 79
 80                return _isDecryptedBodyEmpty;
 81            }
 82        }
 83
 84        public override bool IsFault
 85        {
 86            get
 87            {
 88                if (IsDisposed)
 89                {
 90                    throw TraceUtility.ThrowHelperError(CreateMessageDisposedException(), this);
 91                }
 92                if (!_bodyDecrypted)
 93                {
 94                    return InnerMessage.IsFault;
 95                }
 96
 97                EnsureDecryptedBodyStatusDetermined();
 98
 99                return _isDecryptedBodyFault;
 100            }
 101        }
 102
 103        internal byte[] PrimarySignatureValue => ReceivedSecurityHeader.PrimarySignatureValue;
 104
 105        internal ReceiveSecurityHeader ReceivedSecurityHeader { get; }
 106
 107        private Exception CreateBadStateException(string operation)
 108        {
 109            return new InvalidOperationException(SR.Format(SR.MessageBodyOperationNotValidInBodyState,
 110                operation, _state));
 111        }
 112
 113        public XmlDictionaryReader CreateFullBodyReader()
 114        {
 115            switch (_state)
 116            {
 117                case BodyState.Buffered:
 118                    return CreateFullBodyReaderFromBufferedState();
 119                case BodyState.Decrypted:
 120                    return CreateFullBodyReaderFromDecryptedState();
 121                default:
 122                    throw TraceUtility.ThrowHelperError(CreateBadStateException("CreateFullBodyReader"), this);
 123            }
 124        }
 125
 126        private XmlDictionaryReader CreateFullBodyReaderFromBufferedState()
 127        {
 128            if (_messageBuffer != null)
 129            {
 130                XmlDictionaryReader reader = _messageBuffer.GetReader(0);
 131                MoveToBody(reader);
 132                return reader;
 133            }
 134            else
 135            {
 136                return ((BufferedMessage)InnerMessage).GetBufferedReaderAtBody();
 137            }
 138        }
 139
 140        private XmlDictionaryReader CreateFullBodyReaderFromDecryptedState()
 141        {
 142            XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(_decryptedBuffer, 0, _decryptedBuffer.Leng
 143            MoveToBody(reader);
 144            return reader;
 145        }
 146
 147        private void EnsureDecryptedBodyStatusDetermined()
 148        {
 149            if (!_isDecryptedBodyStatusDetermined)
 150            {
 151                XmlDictionaryReader reader = CreateFullBodyReader();
 152                if (ReadStartBody(reader, InnerMessage.Version.Envelope, out _isDecryptedBodyFault, out _isDecryptedBody
 153                {
 154                    _cachedDecryptedBodyContentReader = reader;
 155                }
 156                else
 157                {
 158                    reader.Close();
 159                }
 160                _isDecryptedBodyStatusDetermined = true;
 161            }
 162        }
 163
 164        public XmlAttributeHolder[] GetEnvelopeAttributes()
 165        {
 166            return _envelopeAttributes;
 167        }
 168
 169        public XmlAttributeHolder[] GetHeaderAttributes()
 170        {
 171            return _headerAttributes;
 172        }
 173
 174        private XmlDictionaryReader GetReaderAtEnvelope()
 175        {
 176            if (_messageBuffer != null)
 177            {
 178                return _messageBuffer.GetReader(0);
 179            }
 180            else
 181            {
 182                return ((BufferedMessage)InnerMessage).GetMessageReader();
 183            }
 184        }
 185
 186        public XmlDictionaryReader GetReaderAtFirstHeader()
 187        {
 188            XmlDictionaryReader reader = GetReaderAtEnvelope();
 189            MoveToHeaderBlock(reader, false);
 190            reader.ReadStartElement();
 191            return reader;
 192        }
 193
 194        public XmlDictionaryReader GetReaderAtSecurityHeader()
 195        {
 196            if (_cachedReaderAtSecurityHeader != null)
 197            {
 198                XmlDictionaryReader result = _cachedReaderAtSecurityHeader;
 199                _cachedReaderAtSecurityHeader = null;
 200                return result;
 201            }
 202            return Headers.GetReaderAtHeader(ReceivedSecurityHeader.HeaderIndex);
 203        }
 204
 205        private void MoveToBody(XmlDictionaryReader reader)
 206        {
 207            if (reader.NodeType != XmlNodeType.Element)
 208            {
 209                reader.MoveToContent();
 210            }
 211            reader.ReadStartElement();
 212            if (reader.IsStartElement(XD.MessageDictionary.Header, Version.Envelope.DictionaryNamespace))
 213            {
 214                reader.Skip();
 215            }
 216            if (reader.NodeType != XmlNodeType.Element)
 217            {
 218                reader.MoveToContent();
 219            }
 220        }
 221
 222        private void MoveToHeaderBlock(XmlDictionaryReader reader, bool captureAttributes)
 223        {
 224            if (reader.NodeType != XmlNodeType.Element)
 225            {
 226                reader.MoveToContent();
 227            }
 228            if (captureAttributes)
 229            {
 230                _envelopePrefix = reader.Prefix;
 231                _envelopeAttributes = XmlAttributeHolder.ReadAttributes(reader);
 232            }
 233            reader.ReadStartElement();
 234            reader.MoveToStartElement(XD.MessageDictionary.Header, Version.Envelope.DictionaryNamespace);
 235            if (captureAttributes)
 236            {
 237                _headerAttributes = XmlAttributeHolder.ReadAttributes(reader);
 238            }
 239        }
 240
 241        private void MoveToSecurityHeader(XmlDictionaryReader reader, int headerIndex, bool captureAttributes)
 242        {
 243            MoveToHeaderBlock(reader, captureAttributes);
 244            reader.ReadStartElement();
 245            while (true)
 246            {
 247                if (reader.NodeType != XmlNodeType.Element)
 248                {
 249                    reader.MoveToContent();
 250                }
 251                if (headerIndex == 0)
 252                {
 253                    break;
 254                }
 255                reader.Skip();
 256                headerIndex--;
 257            }
 258        }
 259
 260        protected override void OnBodyToString(XmlDictionaryWriter writer)
 261        {
 262            if (_state == BodyState.Created)
 263            {
 264                base.OnBodyToString(writer);
 265            }
 266            else
 267            {
 268                OnWriteBodyContents(writer);
 269            }
 270        }
 271
 272        protected override void OnClose()
 273        {
 274            if (_cachedDecryptedBodyContentReader != null)
 275            {
 276                try
 277                {
 278                    _cachedDecryptedBodyContentReader.Close();
 279                }
 280                catch (System.IO.IOException exception)
 281                {
 282                    //
 283                    // We only want to catch and log the I/O exception here
 284                    // assuming reader only throw those exceptions
 285                    //
 286                    DiagnosticUtility.TraceHandledException(exception, TraceEventType.Warning);
 287                }
 288                finally
 289                {
 290                    _cachedDecryptedBodyContentReader = null;
 291                }
 292            }
 293
 294            if (_cachedReaderAtSecurityHeader != null)
 295            {
 296                try
 297                {
 298                    _cachedReaderAtSecurityHeader.Close();
 299                }
 300                catch (System.IO.IOException exception)
 301                {
 302                    //
 303                    // We only want to catch and log the I/O exception here
 304                    // assuming reader only throw those exceptions
 305                    //
 306                    DiagnosticUtility.TraceHandledException(exception, TraceEventType.Warning);
 307                }
 308                finally
 309                {
 310                    _cachedReaderAtSecurityHeader = null;
 311                }
 312            }
 313
 314            _messageBuffer = null;
 315            _decryptedBuffer = null;
 316            _state = BodyState.Disposed;
 317            InnerMessage.Close();
 318        }
 319
 320        protected override XmlDictionaryReader OnGetReaderAtBodyContents()
 321        {
 322            if (_state == BodyState.Created)
 323            {
 324                return InnerMessage.GetReaderAtBodyContents();
 325            }
 326            if (_bodyDecrypted)
 327            {
 328                EnsureDecryptedBodyStatusDetermined();
 329            }
 330            if (_cachedDecryptedBodyContentReader != null)
 331            {
 332                XmlDictionaryReader result = _cachedDecryptedBodyContentReader;
 333                _cachedDecryptedBodyContentReader = null;
 334                return result;
 335            }
 336            else
 337            {
 338                XmlDictionaryReader reader = CreateFullBodyReader();
 339                reader.ReadStartElement();
 340                reader.MoveToContent();
 341                return reader;
 342            }
 343        }
 344
 345        protected override MessageBuffer OnCreateBufferedCopy(int maxBufferSize)
 346        {
 347            if (_canDelegateCreateBufferedCopyToInnerMessage && InnerMessage is BufferedMessage)
 348            {
 349                return InnerMessage.CreateBufferedCopy(maxBufferSize);
 350            }
 351            else
 352            {
 353                return base.OnCreateBufferedCopy(maxBufferSize);
 354            }
 355        }
 356
 357        internal void OnMessageProtectionPassComplete(bool atLeastOneHeaderOrBodyEncrypted)
 358        {
 359            _canDelegateCreateBufferedCopyToInnerMessage = !atLeastOneHeaderOrBodyEncrypted;
 360        }
 361
 362        internal void OnUnencryptedPart(string name, string ns)
 363        {
 364            if (ns == null)
 365            {
 366                throw TraceUtility.ThrowHelperError(new MessageSecurityException(SR.Format(SR.RequiredMessagePartNotEncr
 367            }
 368            else
 369            {
 370                throw TraceUtility.ThrowHelperError(new MessageSecurityException(SR.Format(SR.RequiredMessagePartNotEncr
 371            }
 372        }
 373
 374        internal void OnUnsignedPart(string name, string ns)
 375        {
 376            if (ns == null)
 377            {
 378                throw TraceUtility.ThrowHelperError(new MessageSecurityException(SR.Format(SR.RequiredMessagePartNotSign
 379            }
 380            else
 381            {
 382                throw TraceUtility.ThrowHelperError(new MessageSecurityException(SR.Format(SR.RequiredMessagePartNotSign
 383            }
 384        }
 385
 386        protected override void OnWriteStartBody(XmlDictionaryWriter writer)
 387        {
 388            if (_state == BodyState.Created)
 389            {
 390                InnerMessage.WriteStartBody(writer);
 391                return;
 392            }
 393
 394            XmlDictionaryReader reader = CreateFullBodyReader();
 395            reader.MoveToContent();
 396            writer.WriteStartElement(reader.Prefix, reader.LocalName, reader.NamespaceURI);
 397            writer.WriteAttributes(reader, false);
 398            reader.Close();
 399        }
 400
 401        protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
 402        {
 403            if (_state == BodyState.Created)
 404            {
 405                InnerMessage.WriteBodyContents(writer);
 406                return;
 407            }
 408
 409            XmlDictionaryReader reader = CreateFullBodyReader();
 410            reader.ReadStartElement();
 411            while (reader.NodeType != XmlNodeType.EndElement)
 412            {
 413                writer.WriteNode(reader, false);
 414            }
 415
 416            reader.ReadEndElement();
 417            reader.Close();
 418        }
 419
 420        public void SetBodyPrefixAndAttributes(XmlDictionaryReader bodyReader)
 421        {
 422            _bodyPrefix = bodyReader.Prefix;
 423            _bodyAttributes = XmlAttributeHolder.ReadAttributes(bodyReader);
 424        }
 425
 426        public void SetDecryptedBody(byte[] decryptedBodyContent)
 427        {
 428            if (_state != BodyState.Buffered)
 429            {
 430                throw TraceUtility.ThrowHelperError(CreateBadStateException("SetDecryptedBody"), this);
 431            }
 432
 433            MemoryStream stream = new MemoryStream();
 434            XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter(stream);
 435
 436            writer.WriteStartElement(_envelopePrefix, XD.MessageDictionary.Envelope, Version.Envelope.DictionaryNamespac
 437            XmlAttributeHolder.WriteAttributes(_envelopeAttributes, writer);
 438
 439            writer.WriteStartElement(_bodyPrefix, XD.MessageDictionary.Body, Version.Envelope.DictionaryNamespace);
 440            XmlAttributeHolder.WriteAttributes(_bodyAttributes, writer);
 441            writer.WriteString(" "); // ensure non-empty element
 442            writer.WriteEndElement();
 443            writer.WriteEndElement();
 444            writer.Flush();
 445
 446            _decryptedBuffer = ContextImportHelper.SpliceBuffers(decryptedBodyContent, stream.GetBuffer(), (int)stream.L
 447
 448            _bodyDecrypted = true;
 449            _state = BodyState.Decrypted;
 450        }
 451
 452        private enum BodyState
 453        {
 454            Created,
 455            Buffered,
 456            Decrypted,
 457            Disposed,
 458        }
 459    }
 460
 461    //TODO investigate
 462    // Adding wrapping tags using a writer is a temporary feature to
 463    // support interop with a partner.  Eventually, the serialization
 464    // team will add a feature to XmlUTF8TextReader to directly
 465    // support the addition of outer namespaces before creating a
 466    // Reader.  This roundabout way of supporting context-sensitive
 467    // decryption can then be removed.
 468    internal static class ContextImportHelper
 469    {
 470        internal static XmlDictionaryReader CreateSplicedReader(byte[] decryptedBuffer,
 471            XmlAttributeHolder[] outerContext1, XmlAttributeHolder[] outerContext2, XmlAttributeHolder[] outerContext3, 
 472        {
 473            const string wrapper1 = "x";
 474            const string wrapper2 = "y";
 475            const string wrapper3 = "z";
 476            const int wrappingDepth = 3;
 477
 0478            MemoryStream stream = new MemoryStream();
 0479            XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter(stream);
 0480            writer.WriteStartElement(wrapper1);
 0481            WriteNamespaceDeclarations(outerContext1, writer);
 0482            writer.WriteStartElement(wrapper2);
 0483            WriteNamespaceDeclarations(outerContext2, writer);
 0484            writer.WriteStartElement(wrapper3);
 0485            WriteNamespaceDeclarations(outerContext3, writer);
 0486            writer.WriteString(" "); // ensure non-empty element
 0487            writer.WriteEndElement();
 0488            writer.WriteEndElement();
 0489            writer.WriteEndElement();
 0490            writer.Flush();
 491
 0492            byte[] splicedBuffer = SpliceBuffers(decryptedBuffer, stream.GetBuffer(), (int)stream.Length, wrappingDepth)
 0493            XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(splicedBuffer, quotas);
 0494            reader.ReadStartElement(wrapper1);
 0495            reader.ReadStartElement(wrapper2);
 0496            reader.ReadStartElement(wrapper3);
 0497            if (reader.NodeType != XmlNodeType.Element)
 498            {
 0499                reader.MoveToContent();
 500            }
 0501            return reader;
 502        }
 503
 504        internal static string GetPrefixIfNamespaceDeclaration(string prefix, string localName)
 505        {
 0506            if (prefix == "xmlns")
 507            {
 0508                return localName;
 509            }
 0510            if (prefix.Length == 0 && localName == "xmlns")
 511            {
 0512                return string.Empty;
 513            }
 0514            return null;
 515        }
 516
 517        private static bool IsNamespaceDeclaration(string prefix, string localName)
 518        {
 0519            return GetPrefixIfNamespaceDeclaration(prefix, localName) != null;
 520        }
 521
 522        internal static byte[] SpliceBuffers(byte[] middle, byte[] wrapper, int wrapperLength, int wrappingDepth)
 523        {
 524            const byte openChar = (byte)'<';
 0525            int openCharsFound = 0;
 526            int openCharIndex;
 0527            for (openCharIndex = wrapperLength - 1; openCharIndex >= 0; openCharIndex--)
 528            {
 0529                if (wrapper[openCharIndex] == openChar)
 530                {
 0531                    openCharsFound++;
 0532                    if (openCharsFound == wrappingDepth)
 533                    {
 534                        break;
 535                    }
 536                }
 537            }
 538
 539            Fx.Assert(openCharIndex > 0, "");
 540
 0541            byte[] splicedBuffer = Fx.AllocateByteArray(checked(middle.Length + wrapperLength - 1));
 0542            int offset = 0;
 0543            int count = openCharIndex - 1;
 0544            Buffer.BlockCopy(wrapper, 0, splicedBuffer, offset, count);
 0545            offset += count;
 0546            count = middle.Length;
 0547            Buffer.BlockCopy(middle, 0, splicedBuffer, offset, count);
 0548            offset += count;
 0549            count = wrapperLength - openCharIndex;
 0550            Buffer.BlockCopy(wrapper, openCharIndex, splicedBuffer, offset, count);
 551
 0552            return splicedBuffer;
 553        }
 554
 555        private static void WriteNamespaceDeclarations(XmlAttributeHolder[] attributes, XmlWriter writer)
 556        {
 0557            if (attributes != null)
 558            {
 0559                for (int i = 0; i < attributes.Length; i++)
 560                {
 0561                    XmlAttributeHolder a = attributes[i];
 0562                    if (IsNamespaceDeclaration(a.Prefix, a.LocalName))
 563                    {
 0564                        a.WriteTo(writer);
 565                    }
 566                }
 567            }
 0568        }
 569    }
 570}