build: use pkg-config for i18n
[exim.git] / src / src / auths / auth-spa.c
index fc363df6fe5119826a21eea5244de5c25abe4842..96e7148ab4d10bbc8b46c5eabdf2d0d0ebf4a568 100644 (file)
@@ -8,6 +8,9 @@
 
  * All the original code used here was torn by Marc Prud'hommeaux out of the
  * Samba project (by Andrew Tridgell, Jeremy Allison, and others).
 
  * All the original code used here was torn by Marc Prud'hommeaux out of the
  * Samba project (by Andrew Tridgell, Jeremy Allison, and others).
+ *
+ * Copyright (c) The Exim Maintainers 2021 - 2023
+ * SPDX-License-Identifier: GPL-2.0-or-later
 
  * Tom Kistner provided additional code, adding spa_build_auth_challenge() to
  * support server authentication mode.
 
  * Tom Kistner provided additional code, adding spa_build_auth_challenge() to
  * support server authentication mode.
@@ -153,6 +156,9 @@ int main (int argc, char ** argv)
    up with a different answer to the one above)
 */
 
    up with a different answer to the one above)
 */
 
+#ifndef MACRO_PREDEF
+
+
 #define DEBUG_X(a,b) ;
 
 extern int DEBUGLEVEL;
 #define DEBUG_X(a,b) ;
 
 extern int DEBUGLEVEL;
@@ -374,27 +380,27 @@ void
 spa_bits_to_base64 (uschar *out, const uschar *in, int inlen)
 /* raw bytes in quasi-big-endian order to base 64 string (NUL-terminated) */
 {
 spa_bits_to_base64 (uschar *out, const uschar *in, int inlen)
 /* raw bytes in quasi-big-endian order to base 64 string (NUL-terminated) */
 {
-  for (; inlen >= 3; inlen -= 3)
-    {
-      *out++ = base64digits[in[0] >> 2];
-      *out++ = base64digits[((in[0] << 4) & 0x30) | (in[1] >> 4)];
-      *out++ = base64digits[((in[1] << 2) & 0x3c) | (in[2] >> 6)];
-      *out++ = base64digits[in[2] & 0x3f];
-      in += 3;
-    }
-  if (inlen > 0)
-    {
-      uschar fragment;
-
-      *out++ = base64digits[in[0] >> 2];
-      fragment = (in[0] << 4) & 0x30;
-      if (inlen > 1)
-       fragment |= in[1] >> 4;
-      *out++ = base64digits[fragment];
-      *out++ = (inlen < 2) ? '=' : base64digits[(in[1] << 2) & 0x3c];
-      *out++ = '=';
-    }
-  *out = '\0';
+for (; inlen >= 3; inlen -= 3)
+  {
+  *out++ = base64digits[in[0] >> 2];
+  *out++ = base64digits[((in[0] << 4) & 0x30) | (in[1] >> 4)];
+  *out++ = base64digits[((in[1] << 2) & 0x3c) | (in[2] >> 6)];
+  *out++ = base64digits[in[2] & 0x3f];
+  in += 3;
+  }
+if (inlen > 0)
+  {
+  uschar fragment;
+
+  *out++ = base64digits[in[0] >> 2];
+  fragment = (in[0] << 4) & 0x30;
+  if (inlen > 1)
+     fragment |= in[1] >> 4;
+  *out++ = base64digits[fragment];
+  *out++ = (inlen < 2) ? '=' : base64digits[(in[1] << 2) & 0x3c];
+  *out++ = '=';
+  }
+*out = '\0';
 }
 
 
 }
 
 
@@ -404,52 +410,52 @@ int
 spa_base64_to_bits (char *out, int outlength, const char *in)
 /* base 64 to raw bytes in quasi-big-endian order, returning count of bytes */
 {
 spa_base64_to_bits (char *out, int outlength, const char *in)
 /* base 64 to raw bytes in quasi-big-endian order, returning count of bytes */
 {
-  int len = 0;
-  register uschar digit1, digit2, digit3, digit4;
+int len = 0;
+uschar digit1, digit2, digit3, digit4;
 
 
-  if (in[0] == '+' && in[1] == ' ')
-    in += 2;
-  if (*in == '\r')
-    return (0);
+if (in[0] == '+' && in[1] == ' ')
+  in += 2;
+if (*in == '\r')
+  return (0);
 
 
-  do
+do
+  {
+  if (len >= outlength)                   /* Added by PH */
+    return -1;                          /* Added by PH */
+  digit1 = in[0];
+  if (DECODE64 (digit1) == BAD)
+    return -1;
+  digit2 = in[1];
+  if (DECODE64 (digit2) == BAD)
+    return -1;
+  digit3 = in[2];
+  if (digit3 != '=' && DECODE64 (digit3) == BAD)
+    return -1;
+  digit4 = in[3];
+  if (digit4 != '=' && DECODE64 (digit4) == BAD)
+    return -1;
+  in += 4;
+  *out++ = (DECODE64 (digit1) << 2) | (DECODE64 (digit2) >> 4);
+  ++len;
+  if (digit3 != '=')
     {
     {
+    if (len >= outlength)                   /* Added by PH */
+      return -1;                          /* Added by PH */
+    *out++ =
+      ((DECODE64 (digit2) << 4) & 0xf0) | (DECODE64 (digit3) >> 2);
+    ++len;
+    if (digit4 != '=')
+      {
       if (len >= outlength)                   /* Added by PH */
       if (len >= outlength)                   /* Added by PH */
-        return (-1);                          /* Added by PH */
-      digit1 = in[0];
-      if (DECODE64 (digit1) == BAD)
-       return (-1);
-      digit2 = in[1];
-      if (DECODE64 (digit2) == BAD)
-       return (-1);
-      digit3 = in[2];
-      if (digit3 != '=' && DECODE64 (digit3) == BAD)
-       return (-1);
-      digit4 = in[3];
-      if (digit4 != '=' && DECODE64 (digit4) == BAD)
-       return (-1);
-      in += 4;
-      *out++ = (DECODE64 (digit1) << 2) | (DECODE64 (digit2) >> 4);
+       return -1;                          /* Added by PH */
+      *out++ = ((DECODE64 (digit3) << 6) & 0xc0) | DECODE64 (digit4);
       ++len;
       ++len;
-      if (digit3 != '=')
-       {
-         if (len >= outlength)                   /* Added by PH */
-           return (-1);                          /* Added by PH */
-         *out++ =
-           ((DECODE64 (digit2) << 4) & 0xf0) | (DECODE64 (digit3) >> 2);
-         ++len;
-         if (digit4 != '=')
-           {
-             if (len >= outlength)                   /* Added by PH */
-               return (-1);                          /* Added by PH */
-             *out++ = ((DECODE64 (digit3) << 6) & 0xc0) | DECODE64 (digit4);
-             ++len;
-           }
-       }
+      }
     }
     }
-  while (*in && *in != '\r' && digit4 != '=');
+  }
+while (*in && *in != '\r' && digit4 != '=');
 
 
-  return (len);
+return len;
 }
 
 
 }
 
 
@@ -1197,65 +1203,74 @@ char versionString[] = "libntlm version 0.21";
 /* Utility routines that handle NTLM auth structures. */
 
 /* The [IS]VAL macros are to take care of byte order for non-Intel
 /* Utility routines that handle NTLM auth structures. */
 
 /* The [IS]VAL macros are to take care of byte order for non-Intel
- * Machines -- I think this file is OK, but it hasn't been tested.
- * The other files (the ones stolen from Samba) should be OK.
- */
+Machines -- I think this file is OK, but it hasn't been tested.
+The other files (the ones stolen from Samba) should be OK.  */
 
 
 
 
-/* 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
- */
+/* Append a string to the buffer and point the header struct at that. */
 
 
-#define spa_bytes_add(ptr, header, buf, count) \
-{ \
-if (buf && (count) != 0) /* we hate -Wint-in-bool-contex */ \
-  { \
-  SSVAL(&ptr->header.len,0,count); \
-  SSVAL(&ptr->header.maxlen,0,count); \
-  SIVAL(&ptr->header.offset,0,((ptr->buffer - ((uint8x*)ptr)) + ptr->bufIndex)); \
-  memcpy(ptr->buffer+ptr->bufIndex, buf, count); \
-  ptr->bufIndex += count; \
-  } \
-else \
-  { \
-  ptr->header.len = \
-  ptr->header.maxlen = 0; \
-  SIVAL(&ptr->header.offset,0,((ptr->buffer - ((uint8x*)ptr)) + ptr->bufIndex)); \
-  } \
+static void
+spa_bytes_add(SPAbuf * buffer, size_t off, SPAStrHeader * header,
+  const uschar * src, int count)
+{
+off += buffer->bufIndex;
+if (  src && count != 0                        /* we hate -Wint-in-bool-contex */
+   && buffer->bufIndex + count < sizeof(buffer->buffer)        
+   )
+  {
+  SSVAL(&header->len, 0, count);
+  SSVAL(&header->maxlen, 0, count);
+  SIVAL(&header->offset, 0, off);
+  memcpy(buffer->buffer + buffer->bufIndex, src, count);
+  buffer->bufIndex += count;
+  }
+else
+  {
+  header->len = header->maxlen = 0;
+  SIVAL(&header->offset, 0, off);
+  }
 }
 
 }
 
-#define spa_string_add(ptr, header, string) \
-{ \
-char *p = string; \
-int len = 0; \
-if (p) len = strlen(p); \
-spa_bytes_add(ptr, header, (US p), len); \
+static void
+spa_string_add(SPAbuf * buffer, size_t off, SPAStrHeader * header,
+  const uschar * string)
+{
+int len = string ? Ustrlen(string) : 0;
+spa_bytes_add(buffer, off, header, string, len);
 }
 
 }
 
-#define spa_unicode_add_string(ptr, header, string) \
-{ \
-char *p = string; \
-uschar *b = NULL; \
-int len = 0; \
-if (p) \
-  { \
-  len = strlen(p); \
-  b = strToUnicode(p); \
-  } \
-spa_bytes_add(ptr, header, b, len*2); \
+static uschar *
+strToUnicode(const uschar * p)
+{
+static uschar buf[1024];
+size_t l = Ustrlen(p);
+
+assert (l * 2 < sizeof buf);
+
+for (int i = 0; l--; ) { buf[i++] = *p++; buf[i++] = 0; }
+return buf;
 }
 
 }
 
+static void
+spa_unicode_add_string(SPAbuf * buffer, size_t off, SPAStrHeader * header,
+  const uschar * string)
+{
+const uschar * p = string;
+uschar * b = NULL;
+int len = 0;
+if (p)
+  {
+  len = Ustrlen(p);
+  b = US strToUnicode(p);
+  }
+spa_bytes_add(buffer, off, header, b, len*2);
+}
 
 
-#define GetUnicodeString(structPtr, header) \
-unicodeToString(((char*)structPtr) + IVAL(&structPtr->header.offset,0) , SVAL(&structPtr->header.len,0)/2)
-#define GetString(structPtr, header) \
-toString(((CS structPtr) + IVAL(&structPtr->header.offset,0)), SVAL(&structPtr->header.len,0))
 
 #ifdef notdef
 
 #define DumpBuffer(fp, structPtr, header) \
 
 #ifdef notdef
 
 #define DumpBuffer(fp, structPtr, header) \
-dumpRaw(fp,(US structPtr)+IVAL(&structPtr->header.offset,0),SVAL(&structPtr->header.len,0))
+ dumpRaw(fp,(US structPtr)+IVAL(&structPtr->header.offset,0),SVAL(&structPtr->header.len,0))
 
 
 static void
 
 
 static void
@@ -1289,24 +1304,6 @@ buf[i] = '\0';
 return buf;
 }
 
 return buf;
 }
 
-static uschar *
-strToUnicode (char *p)
-{
-static uschar buf[1024];
-size_t l = strlen (p);
-int i = 0;
-
-assert (l * 2 < sizeof buf);
-
-while (l--)
-  {
-  buf[i++] = *p++;
-  buf[i++] = 0;
-  }
-
-return buf;
-}
-
 static uschar *
 toString (char *p, size_t len)
 {
 static uschar *
 toString (char *p, size_t len)
 {
@@ -1319,8 +1316,33 @@ buf[len] = 0;
 return buf;
 }
 
 return buf;
 }
 
+static inline uschar *
+get_challenge_unistr(SPAAuthChallenge * challenge, SPAStrHeader * hdr)
+{
+int off = IVAL(&hdr->offset, 0);
+int len = SVAL(&hdr->len, 0);
+return off + len < sizeof(SPAAuthChallenge)
+  ? US unicodeToString(CS challenge + off, len/2) : US"";
+}
+
+static inline uschar *
+get_challenge_str(SPAAuthChallenge * challenge, SPAStrHeader * hdr)
+{
+int off = IVAL(&hdr->offset, 0);
+int len = SVAL(&hdr->len, 0);
+return off + len < sizeof(SPAAuthChallenge)
+  ? US toString(CS challenge + off, len) : US"";
+}
+
 #ifdef notdef
 
 #ifdef notdef
 
+#define GetUnicodeString(structPtr, header) \
+ unicodeToString(((char*)structPtr) + IVAL(&structPtr->header.offset,0) , SVAL(&structPtr->header.len,0)/2)
+
+#define GetString(structPtr, header) \
+ toString(((CS structPtr) + IVAL(&structPtr->header.offset,0)), SVAL(&structPtr->header.len,0))
+
+
 void
 dumpSmbNtlmAuthRequest (FILE * fp, SPAAuthRequest * request)
 {
 void
 dumpSmbNtlmAuthRequest (FILE * fp, SPAAuthRequest * request)
 {
@@ -1364,25 +1386,26 @@ fprintf (fp, "      Flags = %08x\n", IVAL (&response->flags, 0));
 #endif
 
 void
 #endif
 
 void
-spa_build_auth_request (SPAAuthRequest * request, char *user, char *domain)
+spa_build_auth_request (SPAAuthRequest * request, uschar * user, uschar * domain)
 {
 {
-char *u = strdup (user);
-char *p = strchr (u, '@');
+uschar * u = string_copy(user);
+uschar * p = Ustrchr(u, '@');
 
 if (p)
   {
   if (!domain)
 
 if (p)
   {
   if (!domain)
-   domain = p + 1;
+    domain = p + 1;
   *p = '\0';
   }
 
   *p = '\0';
   }
 
-request->bufIndex = 0;
+request->buf.bufIndex = 0;
 memcpy (request->ident, "NTLMSSP\0\0\0", 8);
 SIVAL (&request->msgType, 0, 1);
 SIVAL (&request->flags, 0, 0x0000b207);      /* have to figure out what these mean */
 memcpy (request->ident, "NTLMSSP\0\0\0", 8);
 SIVAL (&request->msgType, 0, 1);
 SIVAL (&request->flags, 0, 0x0000b207);      /* have to figure out what these mean */
-spa_string_add (request, user, u);
-spa_string_add (request, domain, domain);
-free (u);
+spa_string_add(&request->buf, offsetof(SPAAuthRequest, buf), &request->user,
+               u);
+spa_string_add(&request->buf, offsetof(SPAAuthRequest, buf), &request->domain,
+               domain);
 }
 
 
 }
 
 
@@ -1395,14 +1418,12 @@ int i;
 int p = (int)getpid();
 int random_seed = (int)time(NULL) ^ ((p << 16) | p);
 
 int p = (int)getpid();
 int random_seed = (int)time(NULL) ^ ((p << 16) | p);
 
-request = request;  /* Added by PH to stop compilers whinging */
-
 /* Ensure challenge data is cleared, in case it isn't all used. This
 patch added by PH on suggestion of Russell King */
 
 memset(challenge, 0, sizeof(SPAAuthChallenge));
 
 /* Ensure challenge data is cleared, in case it isn't all used. This
 patch added by PH on suggestion of Russell King */
 
 memset(challenge, 0, sizeof(SPAAuthChallenge));
 
-challenge->bufIndex = 0;
+challenge->buf.bufIndex = 0;
 memcpy (challenge->ident, "NTLMSSP\0", 8);
 SIVAL (&challenge->msgType, 0, 2);
 SIVAL (&challenge->flags, 0, 0x00008201);
 memcpy (challenge->ident, "NTLMSSP\0", 8);
 SIVAL (&challenge->msgType, 0, 2);
 SIVAL (&challenge->flags, 0, 0x00008201);
@@ -1424,24 +1445,27 @@ memcpy(challenge->challengeData,chalstr,8);
 
 
 
 
 
 
-/* This is the original source of this function, preserved here for reference.
+/* The original version of this function is available in git.
 The new version below was re-organized by PH following a patch and some further
 suggestions from Mark Lyda to fix the problem that is described at the head of
 this module. At the same time, I removed the untidiness in the code below that
 The new version below was re-organized by PH following a patch and some further
 suggestions from Mark Lyda to fix the problem that is described at the head of
 this module. At the same time, I removed the untidiness in the code below that
-involves the "d" and "domain" variables. */
+involves the "d" and "domain" variables.
+Further modified by JGH to replace complex macro "functions" with real ones. */
 
 
-#ifdef NEVER
 void
 spa_build_auth_response (SPAAuthChallenge * challenge,
 void
 spa_build_auth_response (SPAAuthChallenge * challenge,
-                        SPAAuthResponse * response, char *user,
-                        char *password)
+                        SPAAuthResponse * response, uschar * user,
+                        uschar * password)
 {
 uint8x lmRespData[24];
 uint8x ntRespData[24];
 {
 uint8x lmRespData[24];
 uint8x ntRespData[24];
-char *d = strdup (GetUnicodeString (challenge, uDomain));
-char *domain = d;
-char *u = strdup (user);
-char *p = strchr (u, '@');
+uint32x cf = IVAL(&challenge->flags, 0);
+uschar * u = string_copy(user);
+uschar * p = Ustrchr(u, '@');
+uschar * d = NULL;
+uschar * domain;
+SPAbuf * buf = &response->buf;
+const size_t off = offsetof(SPAAuthResponse, buf);
 
 if (p)
   {
 
 if (p)
   {
@@ -1449,76 +1473,36 @@ if (p)
   *p = '\0';
   }
 
   *p = '\0';
   }
 
-spa_smb_encrypt (US password, challenge->challengeData, lmRespData);
-spa_smb_nt_encrypt (US password, challenge->challengeData, ntRespData);
+else domain = d = string_copy(cf & 0x1
+  ? CUS get_challenge_unistr(challenge, &challenge->uDomain)
+  : CUS get_challenge_str(challenge, &challenge->uDomain));
+
+spa_smb_encrypt(password, challenge->challengeData, lmRespData);
+spa_smb_nt_encrypt(password, challenge->challengeData, ntRespData);
 
 
-response->bufIndex = 0;
+buf->bufIndex = 0;
 memcpy (response->ident, "NTLMSSP\0\0\0", 8);
 SIVAL (&response->msgType, 0, 3);
 
 memcpy (response->ident, "NTLMSSP\0\0\0", 8);
 SIVAL (&response->msgType, 0, 3);
 
-spa_bytes_add (response, lmResponse, lmRespData, 24);
-spa_bytes_add (response, ntResponse, ntRespData, 24);
-spa_unicode_add_string (response, uDomain, domain);
-spa_unicode_add_string (response, uUser, u);
-spa_unicode_add_string (response, uWks, u);
-spa_string_add (response, sessionKey, NULL);
-
-response->flags = challenge->flags;
-
-free (d);
-free (u);
-}
-#endif
-
-
-/* This is the re-organized version (see comments above) */
-
-void
-spa_build_auth_response (SPAAuthChallenge * challenge,
-                        SPAAuthResponse * response, char *user,
-                        char *password)
-{
-uint8x lmRespData[24];
-uint8x ntRespData[24];
-uint32x cf = IVAL(&challenge->flags, 0);
-char *u = strdup (user);
-char *p = strchr (u, '@');
-char *d = NULL;
-char *domain;
+spa_bytes_add(buf, off, &response->lmResponse, lmRespData, cf & 0x200 ? 24 : 0);
+spa_bytes_add(buf, off, &response->ntResponse, ntRespData, cf & 0x8000 ? 24 : 0);
 
 
-if (p)
+if (cf & 0x1)          /* Unicode Text */
   {
   {
-  domain = p + 1;
-  *p = '\0';
+  spa_unicode_add_string(buf, off, &response->uDomain, domain);
+  spa_unicode_add_string(buf, off, &response->uUser, u);
+  spa_unicode_add_string(buf, off, &response->uWks, u);
+  }
+else
+  {                    /* OEM Text */
+  spa_string_add(buf, off, &response->uDomain, domain);
+  spa_string_add(buf, off, &response->uUser, u);
+  spa_string_add(buf, off, &response->uWks, u);
   }
 
   }
 
-else domain = d = strdup((cf & 0x1)?
-  CCS GetUnicodeString(challenge, uDomain) :
-  CCS GetString(challenge, uDomain));
-
-spa_smb_encrypt (US password, challenge->challengeData, lmRespData);
-spa_smb_nt_encrypt (US password, challenge->challengeData, ntRespData);
-
-response->bufIndex = 0;
-memcpy (response->ident, "NTLMSSP\0\0\0", 8);
-SIVAL (&response->msgType, 0, 3);
-
-spa_bytes_add (response, lmResponse, lmRespData, (cf & 0x200) ? 24 : 0);
-spa_bytes_add (response, ntResponse, ntRespData, (cf & 0x8000) ? 24 : 0);
-
-if (cf & 0x1) {      /* Unicode Text */
-     spa_unicode_add_string (response, uDomain, domain);
-     spa_unicode_add_string (response, uUser, u);
-     spa_unicode_add_string (response, uWks, u);
-} else {             /* OEM Text */
-     spa_string_add (response, uDomain, domain);
-     spa_string_add (response, uUser, u);
-     spa_string_add (response, uWks, u);
+spa_string_add(buf, off, &response->sessionKey, NULL);
+response->flags = challenge->flags;
 }
 
 }
 
-spa_string_add (response, sessionKey, NULL);
-response->flags = challenge->flags;
 
 
-if (d != NULL) free (d);
-free (u);
-}
+#endif   /*!MACRO_PREDEF*/