/* smbutil.c --- Main library functions.
 * Copyright (C) 2002-2024 Simon Josefsson
 * Copyright (C) 1999-2001 Grant Edwards
 * Copyright (C) 2004 Frediano Ziglio
 *
 * This file is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This file is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this file; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 *
 */

#include <config.h>

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <unistd.h>
#include <byteswap.h>

#include "ntlm.h"

#ifdef NTLM_UNIQUE_MODULE
# include "des.c"
# include "md4.c"
# include "smbencrypt.c"
#else
# include "des.h"
# include "md4.h"
#endif

char versionString[] = PACKAGE_STRING;

/* Utility routines that handle NTLM auth structures. */

/*
 * Must be multiple of two
 * We use a statis buffer of NTLM_MSG_BUFSIZE [1024] bytes for message
 * At maximun we but 48 bytes (ntlm responses) and 3 unicode strings so
 * NTLM_BUFSIZE * 3 + 48 <= NTLM_MSG_BUFSIZE
 */
#define NTLM_BUFSIZE 320

/*
 * all bytes in our structures are aligned so just swap bytes so
 * we have just to swap order
 */
#ifdef WORDS_BIGENDIAN
# define UI16LE(n) bswap_16(n)
# define UI32LE(n) bswap_32(n)
#else
# define UI16LE(n) (n)
# define UI32LE(n) (n)
#endif

/* I am not crazy about these macros -- they seem to have gotten
 * a bit complex.  A new scheme for handling string/buffer fields
 * in the structures probably needs to be designed
 */
#define AddBytes(ptr, header, buf, count) \
{ \
  size_t count2 = count; \
  if (count2  > NTLM_MSG_BUFSIZE - ptr->bufIndex)  \
    count2 = NTLM_MSG_BUFSIZE - ptr->bufIndex; \
  ptr->header.len = ptr->header.maxlen = UI16LE(count2); \
  ptr->header.offset = UI32LE((ptr->buffer - ((uint8*)ptr)) + ptr->bufIndex); \
  memcpy(ptr->buffer+ptr->bufIndex, buf, count2); \
  ptr->bufIndex += count2; \
}

#define AddString(ptr, header, string) \
{ \
const char *p = (string); \
size_t len = p ? strlen(p) : 0; \
AddBytes(ptr, header, p, len); \
}

#define AddUnicodeStringLen(ptr, header, string, len) \
{ \
unsigned char buf[NTLM_BUFSIZE]; \
unsigned char *b = strToUnicode(string, len, buf); \
AddBytes(ptr, header, b, len*2); \
}

#define AddUnicodeString(ptr, header, string) \
{ \
size_t len = strlen(string); \
AddUnicodeStringLen(ptr, header, string, len); \
}

#define GetUnicodeString(structPtr, header, output) \
getUnicodeString(UI32LE(structPtr->header.offset), UI16LE(structPtr->header.len), ((char*)structPtr), (structPtr->buffer - (uint8*) structPtr), sizeof(structPtr->buffer), output)
#define GetString(structPtr, header, output) \
getString(UI32LE(structPtr->header.offset), UI16LE(structPtr->header.len), ((char*)structPtr), (structPtr->buffer - (uint8*) structPtr), sizeof(structPtr->buffer), output)
#define DumpBuffer(fp, structPtr, header) \
dumpBuffer(fp, UI32LE(structPtr->header.offset), UI16LE(structPtr->header.len), ((char*)structPtr), (structPtr->buffer - (uint8*) structPtr), sizeof(structPtr->buffer))


static void
dumpRaw (FILE *fp, const unsigned char *buf, size_t len)
{
  size_t i;

  for (i = 0; i < len; ++i)
    fprintf (fp, "%02x ", buf[i]);

  fprintf (fp, "\n");
}

static inline void
dumpBuffer (FILE *fp, uint32 offset, uint32 len, char *structPtr,
	    size_t buf_start, size_t buf_len)
{
  /* prevent buffer reading overflow */
  if (offset < buf_start || offset > buf_len + buf_start
      || offset + len > buf_len + buf_start)
    len = 0;
  dumpRaw (fp, structPtr + offset, len);
}

static char *
unicodeToString (const char *p, size_t len, char *buf)
{
  size_t i;

  if (len >= NTLM_BUFSIZE)
    len = NTLM_BUFSIZE - 1;

  for (i = 0; i < len; ++i)
    {
      buf[i] = *p & 0x7f;
      p += 2;
    }

  buf[i] = '\0';
  return buf;
}

static inline char *
getUnicodeString (uint32 offset, uint32 len, char *structPtr,
		  size_t buf_start, size_t buf_len, char *output)
{
  /* prevent buffer reading overflow */
  if (offset < buf_start || offset > buf_len + buf_start
      || offset + len > buf_len + buf_start)
    len = 0;
  return unicodeToString (structPtr + offset, len / 2, output);
}

static unsigned char *
strToUnicode (const char *p, size_t l, unsigned char *buf)
{
  int i = 0;

  if (l > (NTLM_BUFSIZE / 2))
    l = (NTLM_BUFSIZE / 2);

  while (l--)
    {
      buf[i++] = *p++;
      buf[i++] = 0;
    }

  return buf;
}

static char *
toString (const char *p, size_t len, char *buf)
{
  if (len >= NTLM_BUFSIZE)
    len = NTLM_BUFSIZE - 1;

  memcpy (buf, p, len);
  buf[len] = 0;
  return buf;
}

static inline char *
getString (uint32 offset, uint32 len, char *structPtr, size_t buf_start,
	   size_t buf_len, char *output)
{
  /* prevent buffer reading overflow */
  if (offset < buf_start || offset > buf_len + buf_start
      || offset + len > buf_len + buf_start)
    len = 0;
  return toString (structPtr + offset, len, output);
}

void
dumpSmbNtlmAuthRequest (FILE *fp, tSmbNtlmAuthRequest *request)
{
  char buf1[NTLM_BUFSIZE], buf2[NTLM_BUFSIZE];
  fprintf (fp, "NTLM Request:\n"
	   "      Ident = %.8s\n"
	   "      mType = %u\n"
	   "      Flags = %08x\n"
	   "       User = %s\n"
	   "     Domain = %s\n",
	   request->ident,
	   UI32LE (request->msgType),
	   UI32LE (request->flags),
	   GetString (request, user, buf1),
	   GetString (request, domain, buf2));
}

void
dumpSmbNtlmAuthChallenge (FILE *fp, tSmbNtlmAuthChallenge *challenge)
{
  unsigned char buf[NTLM_BUFSIZE];
  fprintf (fp, "NTLM Challenge:\n"
	   "      Ident = %.8s\n"
	   "      mType = %u\n"
	   "     Domain = %s\n"
	   "      Flags = %08x\n"
	   "  Challenge = ",
	   challenge->ident,
	   UI32LE (challenge->msgType),
	   GetUnicodeString (challenge, uDomain, buf),
	   UI32LE (challenge->flags));
  dumpRaw (fp, challenge->challengeData, 8);
}

void
dumpSmbNtlmAuthResponse (FILE *fp, tSmbNtlmAuthResponse *response)
{
  unsigned char buf1[NTLM_BUFSIZE], buf2[NTLM_BUFSIZE], buf3[NTLM_BUFSIZE];
  fprintf (fp, "NTLM Response:\n"
	   "      Ident = %.8s\n"
	   "      mType = %u\n"
	   "     LmResp = ", response->ident, UI32LE (response->msgType));
  DumpBuffer (fp, response, lmResponse);
  fprintf (fp, "     NTResp = ");
  DumpBuffer (fp, response, ntResponse);
  fprintf (fp, "     Domain = %s\n"
	   "       User = %s\n"
	   "        Wks = %s\n"
	   "       sKey = ",
	   GetUnicodeString (response, uDomain, buf1),
	   GetUnicodeString (response, uUser, buf2),
	   GetUnicodeString (response, uWks, buf3));
  DumpBuffer (fp, response, sessionKey);
  fprintf (fp, "      Flags = %08x\n", UI32LE (response->flags));
}

static void
buildSmbNtlmAuthRequest_userlen (tSmbNtlmAuthRequest *request,
				 const char *user,
				 size_t user_len, const char *domain)
{
  request->bufIndex = 0;
  memcpy (request->ident, "NTLMSSP\0\0\0", 8);
  request->msgType = UI32LE (1);
  request->flags = UI32LE (0x0000b207);	/* have to figure out what these mean */
  /* FIXME this should be workstation, not username */
  AddBytes (request, user, user, user_len);
  AddString (request, domain, domain);
}

void
buildSmbNtlmAuthRequest (tSmbNtlmAuthRequest *request,
			 const char *user, const char *domain)
{
  const char *p = strchr (user, '@');
  size_t user_len = strlen (user);

  if (p)
    {
      if (!domain)
	domain = p + 1;
      user_len = p - user;
    }

  buildSmbNtlmAuthRequest_userlen (request, user, user_len, domain);
}

void
buildSmbNtlmAuthRequest_noatsplit (tSmbNtlmAuthRequest *request,
				   const char *user, const char *domain)
{
  buildSmbNtlmAuthRequest_userlen (request, user, strlen (user), domain);
}


static void
buildSmbNtlmAuthResponse_userlen (tSmbNtlmAuthChallenge *challenge,
				  tSmbNtlmAuthResponse *response,
				  const char *user, size_t user_len,
				  const char *domain, const char *password)
{
  uint8 lmRespData[24];
  uint8 ntRespData[24];

  ntlm_smb_encrypt (password, challenge->challengeData, lmRespData);
  ntlm_smb_nt_encrypt (password, challenge->challengeData, ntRespData);

  response->bufIndex = 0;
  memcpy (response->ident, "NTLMSSP\0\0\0", 8);
  response->msgType = UI32LE (3);

  AddUnicodeString (response, uDomain, domain);
  AddUnicodeStringLen (response, uUser, user, user_len);
  /* TODO just a dummy value for workstation */
  AddUnicodeStringLen (response, uWks, user, user_len);
  AddBytes (response, lmResponse, lmRespData, 24);
  AddBytes (response, ntResponse, ntRespData, 24);
  AddString (response, sessionKey, NULL);

  response->flags = challenge->flags;
}

void
buildSmbNtlmAuthResponse (tSmbNtlmAuthChallenge *challenge,
			  tSmbNtlmAuthResponse *response,
			  const char *user, const char *password)
{
  const char *p = strchr (user, '@');
  size_t user_len = strlen (user);
  unsigned char buf[NTLM_BUFSIZE];
  const char *domain = GetUnicodeString (challenge, uDomain, buf);

  if (p)
    {
      domain = p + 1;
      user_len = p - user;
    }

  buildSmbNtlmAuthResponse_userlen (challenge, response,
				    user, user_len, domain, password);
}

void
buildSmbNtlmAuthResponse_noatsplit (tSmbNtlmAuthChallenge *challenge,
				    tSmbNtlmAuthResponse *response,
				    const char *user, const char *password)
{
  unsigned char buf[NTLM_BUFSIZE];
  const char *domain = GetUnicodeString (challenge, uDomain, buf);

  buildSmbNtlmAuthResponse_userlen (challenge, response,
				    user, strlen (user), domain, password);
}
