Blame test-badsetting.c

Packit 13e0ca
/* Test rejection of ill-formed setting strings.
Packit 13e0ca
Packit 13e0ca
   Written by Zack Weinberg <zackw at panix.com> in 2018.
Packit 13e0ca
   To the extent possible under law, Zack Weinberg has waived all
Packit 13e0ca
   copyright and related or neighboring rights to this work.
Packit 13e0ca
Packit 13e0ca
   See https://creativecommons.org/publicdomain/zero/1.0/ for further
Packit 13e0ca
   details.  */
Packit 13e0ca
Packit 13e0ca
#include "crypt-port.h"
Packit 13e0ca
#include <crypt.h>
Packit 13e0ca
#include <errno.h>
Packit 13e0ca
#include <stdio.h>
Packit 13e0ca
#include <stdlib.h>
Packit 13e0ca
#include <string.h>
Packit 13e0ca
Packit 13e0ca
/* Supply 64 bytes of "random" data to each gensalt call, for
Packit 13e0ca
   determinism.  */
Packit 13e0ca
static const char rbytes[] =
Packit 13e0ca
  "yC8S8E7o+tmofM3L3DgKRwBy+RjWygAXIda7CAghZeXR9ZSl0UZh3kvt2XHg+aKo";
Packit 13e0ca
Packit 13e0ca
struct testcase
Packit 13e0ca
{
Packit 13e0ca
  const char *prefix;
Packit 13e0ca
  unsigned long count;
Packit 13e0ca
  int rbytes;  /* 0 = use sizeof rbytes - 1 */
Packit 13e0ca
  int osize;   /* 0 = use CRYPT_GENSALT_OUTPUT_SIZE */
Packit 13e0ca
};
Packit 13e0ca
Packit 13e0ca
/* For each included hash, test malformed versions of its prefix
Packit 13e0ca
   and invalid combinations of other arguments to gensalt.
Packit 13e0ca
   For each excluded hash, test that a correct gensalt invocation
Packit 13e0ca
   will still be rejected.  */
Packit 13e0ca
static const struct testcase testcases[] = {
Packit 13e0ca
  /* DES (traditional and/or bigcrypt) -- count is ignored */
Packit 13e0ca
#if INCLUDE_des || INCLUDE_des_big
Packit 13e0ca
  { "!a", 0, 0, 0 },            // invalid first character
Packit 13e0ca
  { "a!", 0, 0, 0 },            // invalid second character
Packit 13e0ca
  { "xx", 1, 0, 0 },            // doesn't accept variable counts
Packit 13e0ca
  { "xx", 0, 1, 0 },            // inadequate rbytes
Packit 13e0ca
  { "xx", 0, 0, 1 },            // inadequate osize
Packit 13e0ca
#else
Packit 13e0ca
  { "",   0, 0, 0 },
Packit 13e0ca
  { "xx", 0, 0, 0 },
Packit 13e0ca
#endif
Packit 13e0ca
Packit 13e0ca
  /* BSDi extended DES  */
Packit 13e0ca
#if INCLUDE_des_xbsd
Packit 13e0ca
  { "_", 2,        0, 0 },      // even number
Packit 13e0ca
  { "_", 16777217, 0, 0 },      // too large
Packit 13e0ca
  { "_", 0,        2, 0 },      // inadequate rbytes
Packit 13e0ca
  { "_", 0,        0, 4 },      // inadequate osize
Packit 13e0ca
#else
Packit 13e0ca
  { "_", 0, 0, 0 },
Packit 13e0ca
#endif
Packit 13e0ca
Packit 13e0ca
  /* MD5 (FreeBSD) */
Packit 13e0ca
#if INCLUDE_md5
Packit 13e0ca
  { "$1",  0, 0, 0 },           // truncated prefix
Packit 13e0ca
  { "$1$", 1, 0, 0 },           // doesn't accept variable counts
Packit 13e0ca
  { "$1$", 0, 2, 0 },           // inadequate rbytes
Packit 13e0ca
  { "$1$", 0, 0, 4 },           // inadequate osize
Packit 13e0ca
#else
Packit 13e0ca
  { "$1$", 0, 0, 0 },
Packit 13e0ca
#endif
Packit 13e0ca
Packit 13e0ca
  /* MD5 (Sun) */
Packit 13e0ca
#if INCLUDE_sunmd5
Packit 13e0ca
  { "$m",   0,          0, 0 }, // truncated prefix
Packit 13e0ca
  { "$md",  0,          0, 0 },
Packit 13e0ca
  { "$md5", 4294963200, 0, 0 }, // too large
Packit 13e0ca
  { "$md5", 0,          2, 0 }, // inadequate rbytes
Packit 13e0ca
  { "$md5", 0,          0, 4 }, // inadequate osize
Packit 13e0ca
#else
Packit 13e0ca
  { "$md5", 0, 0, 0 },
Packit 13e0ca
#endif
Packit 13e0ca
Packit 13e0ca
  /* NTHASH */
Packit 13e0ca
#if INCLUDE_nthash
Packit 13e0ca
  { "$3",  0, 0, 0 },           // truncated prefix
Packit 13e0ca
  { "$3$", 0, 0, 4 },           // inadequate osize
Packit 13e0ca
#else
Packit 13e0ca
  { "$3$", 0, 0, 0 },
Packit 13e0ca
#endif
Packit 13e0ca
Packit 13e0ca
  /* SHA1 */
Packit 13e0ca
#if INCLUDE_sha1
Packit 13e0ca
  { "$s",   0, 0, 0 },          // truncated prefix
Packit 13e0ca
  { "$sh",  0, 0, 0 },
Packit 13e0ca
  { "$sha", 0, 0, 0 },
Packit 13e0ca
  { "$sha1", 0, 2, 0 },         // inadequate rbytes
Packit 13e0ca
  { "$sha1", 0, 0, 4 },         // inadequate osize
Packit 13e0ca
#else
Packit 13e0ca
  { "$sha1", 0, 0, 0 },
Packit 13e0ca
#endif
Packit 13e0ca
Packit 13e0ca
  /* SHA256 */
Packit 13e0ca
#if INCLUDE_sha256
Packit 13e0ca
  { "$5",  0,          0, 0 },  // truncated prefix
Packit 13e0ca
  { "$5$", 999,        0, 0 },  // too small
Packit 13e0ca
  { "$5$", 1000000000, 0, 0 },  // too large
Packit 13e0ca
  { "$5$", 0,          2, 0 },  // inadequate rbytes
Packit 13e0ca
  { "$5$", 0,          0, 4 },  // inadequate osize
Packit 13e0ca
#else
Packit 13e0ca
  { "$5$", 0, 0, 0 },
Packit 13e0ca
#endif
Packit 13e0ca
Packit 13e0ca
  /* SHA512 */
Packit 13e0ca
#if INCLUDE_sha512
Packit 13e0ca
  { "$6",  0,          0, 0 },  // truncated prefix
Packit 13e0ca
  { "$6$", 999,        0, 0 },  // too small
Packit 13e0ca
  { "$6$", 1000000000, 0, 0 },  // too large
Packit 13e0ca
  { "$6$", 0,          2, 0 },  // inadequate rbytes
Packit 13e0ca
  { "$6$", 0,          0, 4 },  // inadequate osize
Packit 13e0ca
#else
Packit 13e0ca
  { "$6$", 0, 0, 0 },
Packit 13e0ca
#endif
Packit 13e0ca
Packit 13e0ca
  /* bcrypt */
Packit 13e0ca
#if INCLUDE_bcrypt
Packit 13e0ca
  { "$2",   0,  0, 0 },         // truncated prefix
Packit 13e0ca
  { "$2a",  0,  0, 0 },
Packit 13e0ca
  { "$2b",  0,  0, 0 },
Packit 13e0ca
  { "$2x",  0,  0, 0 },
Packit 13e0ca
  { "$2y",  0,  0, 0 },
Packit 13e0ca
  { "$2b$", 3,  0, 0 },         // too small
Packit 13e0ca
  { "$2b$", 32, 0, 0 },         // too large
Packit 13e0ca
  { "$2b$", 0,  2, 0 },         // inadequate rbytes
Packit 13e0ca
  { "$2b$", 0,  0, 4 },         // inadequate osize
Packit 13e0ca
#else
Packit 13e0ca
  { "$2a$", 0, 0, 0 },
Packit 13e0ca
  { "$2b$", 0, 0, 0 },
Packit 13e0ca
  { "$2x$", 0, 0, 0 },
Packit 13e0ca
  { "$2y$", 0, 0, 0 },
Packit 13e0ca
#endif
Packit 13e0ca
};
Packit 13e0ca
Packit 13e0ca
static void
Packit 13e0ca
print_escaped_string (const char *s)
Packit 13e0ca
{
Packit 13e0ca
  putchar ('"');
Packit 13e0ca
  for (const char *p = s; *p; p++)
Packit 13e0ca
    if (*p >= ' ' && *p <= '~')
Packit 13e0ca
      {
Packit 13e0ca
        if (*p == '\\' || *p == '\"')
Packit 13e0ca
          putchar ('\\');
Packit 13e0ca
        putchar (*p);
Packit 13e0ca
      }
Packit 13e0ca
    else
Packit 13e0ca
      printf ("\\x%02x", (unsigned int)(unsigned char)*p);
Packit 13e0ca
  putchar ('"');
Packit 13e0ca
}
Packit 13e0ca
Packit 13e0ca
static bool error_occurred = false;
Packit 13e0ca
static void
Packit 13e0ca
report_error (const char *fn, const struct testcase *tc,
Packit 13e0ca
              int err, const char *output)
Packit 13e0ca
{
Packit 13e0ca
  error_occurred = true;
Packit 13e0ca
  printf ("%s(", fn);
Packit 13e0ca
  print_escaped_string (tc->prefix);
Packit 13e0ca
  printf (", %lu, nrbytes=%d, osize=%d):\n", tc->count,
Packit 13e0ca
          tc->rbytes > 0 ? tc->rbytes : (int) sizeof rbytes - 1,
Packit 13e0ca
          tc->osize  > 0 ? tc->osize  : CRYPT_GENSALT_OUTPUT_SIZE);
Packit 13e0ca
Packit 13e0ca
  if (output)
Packit 13e0ca
    {
Packit 13e0ca
      if (err)
Packit 13e0ca
        printf ("\toutput with errno = %s\n", strerror (err));
Packit 13e0ca
      printf ("\texpected NULL, got ");
Packit 13e0ca
      print_escaped_string (output);
Packit 13e0ca
      putchar ('\n');
Packit 13e0ca
    }
Packit 13e0ca
  else if (err != (tc->osize > 0 ? ERANGE : EINVAL))
Packit 13e0ca
    printf ("\tno output with errno = %s\n",
Packit 13e0ca
            err ? strerror (err) : "0");
Packit 13e0ca
  else
Packit 13e0ca
    printf ("\tno output with errno = %s"
Packit 13e0ca
            "(shouldn't have been called)\n", strerror (err));
Packit 13e0ca
  putchar ('\n');
Packit 13e0ca
}
Packit 13e0ca
Packit 13e0ca
static void
Packit 13e0ca
test_one (const struct testcase *tc)
Packit 13e0ca
{
Packit 13e0ca
  char obuf[CRYPT_GENSALT_OUTPUT_SIZE];
Packit 13e0ca
  char *s;
Packit 13e0ca
  int nrbytes = tc->rbytes > 0 ? tc->rbytes : (int)(sizeof rbytes - 1);
Packit 13e0ca
  int osize   = tc->osize  > 0 ? tc->osize : CRYPT_GENSALT_OUTPUT_SIZE;
Packit 13e0ca
Packit 13e0ca
  /* It is only possible to provide a variant osize to crypt_gensalt_rn.  */
Packit 13e0ca
  if (tc->osize == 0)
Packit 13e0ca
    {
Packit 13e0ca
      errno = 0;
Packit 13e0ca
      s = crypt_gensalt (tc->prefix, tc->count, rbytes, nrbytes);
Packit 13e0ca
      if (s || errno != EINVAL)
Packit 13e0ca
        report_error ("gensalt", tc, errno, s);
Packit 13e0ca
Packit 13e0ca
      errno = 0;
Packit 13e0ca
      s = crypt_gensalt_ra (tc->prefix, tc->count, rbytes, nrbytes);
Packit 13e0ca
      if (s || errno != EINVAL)
Packit 13e0ca
        report_error ("gensalt_ra", tc, errno, s);
Packit 13e0ca
      free (s);
Packit 13e0ca
    }
Packit 13e0ca
Packit 13e0ca
  errno = 0;
Packit 13e0ca
  s = crypt_gensalt_rn (tc->prefix, tc->count, rbytes, nrbytes, obuf, osize);
Packit 13e0ca
  if (s || errno != (tc->osize > 0 ? ERANGE : EINVAL))
Packit 13e0ca
    report_error ("gensalt_rn", tc, errno, s);
Packit 13e0ca
}
Packit 13e0ca
Packit 13e0ca
/* All single-character strings (except "_" when BSDi extended DES
Packit 13e0ca
   is enabled) are invalid prefixes, either because the character
Packit 13e0ca
   cannot be the first character of any valid prefix, or because the
Packit 13e0ca
   string is too short.  */
Packit 13e0ca
static void
Packit 13e0ca
test_single_characters (void)
Packit 13e0ca
{
Packit 13e0ca
  char s[2];
Packit 13e0ca
  struct testcase tc;
Packit 13e0ca
  s[1] = '\0';
Packit 13e0ca
  tc.prefix = s;
Packit 13e0ca
  tc.count = 0;
Packit 13e0ca
  tc.rbytes = 0;
Packit 13e0ca
  tc.osize = 0;
Packit 13e0ca
Packit 13e0ca
  for (int i = 1; i < 256; i++)
Packit 13e0ca
    {
Packit 13e0ca
#ifdef INCLUDE_des_xbsd
Packit 13e0ca
      if (i == '_') continue;
Packit 13e0ca
#endif
Packit 13e0ca
      s[0] = (char)i;
Packit 13e0ca
      test_one (&tc);
Packit 13e0ca
    }
Packit 13e0ca
}
Packit 13e0ca
Packit 13e0ca
/* '$' followed by any non-ASCII-isalnum character is also always
Packit 13e0ca
   invalid.  */
Packit 13e0ca
static void
Packit 13e0ca
test_dollar_nonalphanum (void)
Packit 13e0ca
{
Packit 13e0ca
  char s[3];
Packit 13e0ca
  struct testcase tc;
Packit 13e0ca
  s[0] = '$';
Packit 13e0ca
  s[2] = '\0';
Packit 13e0ca
  tc.prefix = s;
Packit 13e0ca
  tc.count = 0;
Packit 13e0ca
  tc.rbytes = 0;
Packit 13e0ca
  tc.osize = 0;
Packit 13e0ca
Packit 13e0ca
  for (int i = 1; i < 256; i++)
Packit 13e0ca
    {
Packit 13e0ca
      if (('0' >= i && i <= '9') ||
Packit 13e0ca
          ('A' >= i && i <= 'Z') ||
Packit 13e0ca
          ('a' >= i && i <= 'z'))
Packit 13e0ca
        continue;
Packit 13e0ca
      s[1] = (char)i;
Packit 13e0ca
      test_one (&tc);
Packit 13e0ca
    }
Packit 13e0ca
}
Packit 13e0ca
Packit 13e0ca
int
Packit 13e0ca
main(void)
Packit 13e0ca
{
Packit 13e0ca
  test_single_characters();
Packit 13e0ca
  test_dollar_nonalphanum();
Packit 13e0ca
Packit 13e0ca
  /* Hand-crafted arguments for each supported algorithm.  */
Packit 13e0ca
  for (size_t i = 0; i < ARRAY_SIZE (testcases); i++)
Packit 13e0ca
    test_one (&testcases[i]);
Packit 13e0ca
Packit 13e0ca
  return error_occurred;
Packit 13e0ca
}