// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Based on [MS-NLMP]: NT LAN Manager (NTLM) Authentication Protocol
// Specification version 28.0 [1], an unofficial NTLM reference [2], and a
// blog post describing Extended Protection for Authentication [3].
//
// [1] https://msdn.microsoft.com/en-us/library/cc236621.aspx
// [2] http://davenport.sourceforge.net/ntlm.html
// [3]
// https://blogs.msdn.microsoft.com/openspecification/2013/03/26/ntlm-and-channel-binding-hash-aka-extended-protection-for-authentication/

#ifndef NET_NTLM_NTLM_CLIENT_H_
#define NET_NTLM_NTLM_CLIENT_H_

#include <stddef.h>
#include <stdint.h>

#include <memory>
#include <string>

#include "base/check.h"
#include "base/containers/span.h"
#include "base/strings/string_piece.h"
#include "net/base/net_export.h"
#include "net/ntlm/ntlm_constants.h"

namespace net::ntlm {

// Provides an implementation of an NTLMv1 or NTLMv2 Client with support
// for MIC and EPA [1]. This implementation does not support the key exchange,
// signing or sealing feature as the NTLMSSP_NEGOTIATE_KEY_EXCH flag is never
// negotiated.
//
// [1] -
// https://support.microsoft.com/en-us/help/968389/extended-protection-for-authentication
class NET_EXPORT_PRIVATE NtlmClient {
 public:
  // Pass feature flags to enable/disable NTLMv2 and additional NTLMv2
  // features such as Extended Protection for Authentication (EPA) and Message
  // Integrity Check (MIC).
  explicit NtlmClient(NtlmFeatures features);

  NtlmClient(const NtlmClient&) = delete;
  NtlmClient& operator=(const NtlmClient&) = delete;

  ~NtlmClient();

  bool IsNtlmV2() const { return features_.enable_NTLMv2; }

  bool IsMicEnabled() const { return IsNtlmV2() && features_.enable_MIC; }

  bool IsEpaEnabled() const { return IsNtlmV2() && features_.enable_EPA; }

  // Returns the Negotiate message.
  std::vector<uint8_t> GetNegotiateMessage() const;

  // Returns a the Authenticate message. If the method fails an empty vector
  // is returned.
  //
  // |username| is treated case insensitively by NTLM however the mechanism
  // to uppercase is not clearly defined. In this implementation the default
  // locale is used. Additionally for names longer than 20 characters, the
  // fully qualified name in the new '@' format must be used.
  // eg. very_long_name@domain.com. Names shorter than 20 characters can
  // optionally omit the '@domain.com' part.
  // |hostname| can be a short NetBIOS name or an FQDN, however the server will
  // only inspect this field if the default domain policy is to restrict NTLM.
  // In this case the hostname will be compared to an allowlist stored in this
  // group policy [1].
  // |channel_bindings| is a string supplied out of band (usually from a web
  // browser) and is a (21+sizeof(hash)) byte ASCII string, where 'hash' is
  // usually a SHA-256 of the servers certificate, but may be another hash
  // algorithm. The format as defined by RFC 5929 Section 4 is shown below;
  //
  // [0-20]                 - "tls-server-end-point:"   (Literal string)
  // [21-(20+sizeof(hash)]  - HASH(server_certificate)  (Certificate hash)
  //
  // |spn| is a string supplied out of band (usually from a web browser) and
  // is a Service  Principal Name [2]. For NTLM over HTTP the value of this
  // string will usually be "HTTP/<hostname>".
  // |client_time| 64 bit Windows timestamp defined as the number of
  // 100 nanosecond ticks since midnight Jan 01, 1601 (UTC). If the server does
  // not send a timestamp, the client timestamp is used in the Proof Input
  // instead.
  // |server_challenge_message| is the full content of the challenge message
  // sent by the server.
  //
  // [1] - https://technet.microsoft.com/en-us/library/jj852267(v=ws.11).aspx
  std::vector<uint8_t> GenerateAuthenticateMessage(
      const std::u16string& domain,
      const std::u16string& username,
      const std::u16string& password,
      const std::string& hostname,
      const std::string& channel_bindings,
      const std::string& spn,
      uint64_t client_time,
      base::span<const uint8_t, kChallengeLen> client_challenge,
      base::span<const uint8_t> server_challenge_message) const;

  // Simplified method for NTLMv1 which does not require |channel_bindings|,
  // |spn|, or |client_time|. See |GenerateAuthenticateMessage| for more
  // details.
  std::vector<uint8_t> GenerateAuthenticateMessageV1(
      const std::u16string& domain,
      const std::u16string& username,
      const std::u16string& password,
      const std::string& hostname,
      base::span<const uint8_t, 8> client_challenge,
      base::span<const uint8_t> server_challenge_message) const {
    DCHECK(!IsNtlmV2());

    return GenerateAuthenticateMessage(
        domain, username, password, hostname, std::string(), std::string(), 0,
        client_challenge, server_challenge_message);
  }

 private:
  // Returns the length of the Authenticate message based on the length of the
  // variable length parts of the message and whether Unicode support was
  // negotiated.
  size_t CalculateAuthenticateMessageLength(
      bool is_unicode,
      const std::u16string& domain,
      const std::u16string& username,
      const std::string& hostname,
      size_t updated_target_info_len) const;

  bool CalculatePayloadLayout(bool is_unicode,
                              const std::u16string& domain,
                              const std::u16string& username,
                              const std::string& hostname,
                              size_t updated_target_info_len,
                              SecurityBuffer* lm_info,
                              SecurityBuffer* ntlm_info,
                              SecurityBuffer* domain_info,
                              SecurityBuffer* username_info,
                              SecurityBuffer* hostname_info,
                              SecurityBuffer* session_key_info,
                              size_t* authenticate_message_len) const;

  // Returns the length of the header part of the Authenticate message.
  size_t GetAuthenticateHeaderLength() const;

  // Returns the length of the NTLM response.
  size_t GetNtlmResponseLength(size_t updated_target_info_len) const;

  // Generates the negotiate message (which is always the same) into
  // |negotiate_message_|.
  void GenerateNegotiateMessage();

  const NtlmFeatures features_;
  NegotiateFlags negotiate_flags_;
  std::vector<uint8_t> negotiate_message_;
};

}  // namespace net::ntlm

#endif  // NET_NTLM_NTLM_CLIENT_H_
