TLSVerificationHandler.swift 4.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. /*
  2. * Copyright 2019, gRPC Authors All rights reserved.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. import Foundation
  17. import NIO
  18. import NIOSSL
  19. import NIOTLS
  20. import Logging
  21. /// Application protocol identifiers for ALPN.
  22. internal enum GRPCApplicationProtocolIdentifier: String, CaseIterable {
  23. // This is not in the IANA ALPN protocol ID registry, but may be used by servers to indicate that
  24. // they serve only gRPC traffic. It is part of the gRPC core implementation.
  25. case gRPC = "grpc-ext"
  26. case h2 = "h2"
  27. }
  28. /// A helper `ChannelInboundHandler` to verify that a TLS handshake was completed successfully
  29. /// and that the negotiated application protocol is valid.
  30. ///
  31. /// The handler holds a promise which is succeeded on successful verification of the negotiated
  32. /// application protocol and failed if any error is received by this handler or an invalid
  33. /// application protocol was negotiated.
  34. ///
  35. /// Users of this handler should rely on the `verification` future held by this instance.
  36. ///
  37. /// On fulfillment of the promise this handler is removed from the channel pipeline.
  38. internal class TLSVerificationHandler: ChannelInboundHandler, RemovableChannelHandler {
  39. typealias InboundIn = Any
  40. private let logger = Logger(subsystem: .clientChannel)
  41. private var verificationPromise: EventLoopPromise<Void>!
  42. /// A future which is fulfilled when the state of the TLS handshake is known. If the handshake
  43. /// was successful and the negotiated application protocol is valid then the future is succeeded.
  44. /// If an error occurred or the application protocol is not valid then the future will have been
  45. /// failed.
  46. ///
  47. /// - Important: The promise associated with this future is created in `handlerAdded(context:)`,
  48. /// and as such must _not_ be accessed before the handler has be added to a pipeline.
  49. var verification: EventLoopFuture<Void>! {
  50. return verificationPromise.futureResult
  51. }
  52. init() { }
  53. func handlerAdded(context: ChannelHandlerContext) {
  54. self.verificationPromise = context.eventLoop.makePromise()
  55. // Remove ourselves from the pipeline when the promise gets fulfilled.
  56. self.verificationPromise.futureResult.recover { error in
  57. // If we have an error we should let the rest of the pipeline know.
  58. context.fireErrorCaught(error)
  59. }.whenComplete { _ in
  60. context.pipeline.removeHandler(self, promise: nil)
  61. }
  62. }
  63. func errorCaught(context: ChannelHandlerContext, error: Error) {
  64. precondition(self.verificationPromise != nil, "handler has not been added to the pipeline")
  65. self.logger.error(
  66. "error caught before TLS was verified",
  67. metadata: [MetadataKey.error: "\(error)"]
  68. )
  69. verificationPromise.fail(error)
  70. }
  71. func userInboundEventTriggered(context: ChannelHandlerContext, event: Any) {
  72. precondition(self.verificationPromise != nil, "handler has not been added to the pipeline")
  73. guard let tlsEvent = event as? TLSUserEvent,
  74. case .handshakeCompleted(negotiatedProtocol: let negotiatedProtocol) = tlsEvent else {
  75. context.fireUserInboundEventTriggered(event)
  76. return
  77. }
  78. self.logger.debug("TLS handshake completed, negotiated protocol: \(String(describing: negotiatedProtocol))")
  79. if let proto = negotiatedProtocol, GRPCApplicationProtocolIdentifier(rawValue: proto) != nil {
  80. self.logger.debug("negotiated application protocol is valid")
  81. self.verificationPromise.succeed(())
  82. } else {
  83. self.logger.error("negotiated application protocol is invalid: \(String(describing: negotiatedProtocol))")
  84. let error = GRPCError.client(.applicationLevelProtocolNegotiationFailed)
  85. self.verificationPromise.fail(error)
  86. }
  87. }
  88. }