(root)/
libxcrypt-4.4.36/
test/
badsalt.c
       1  /* Test rejection of ill-formed password hashes.
       2     Copyright (C) 2012-2018 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <http://www.gnu.org/licenses/>.  */
      18  
      19  #include "crypt-port.h"
      20  #undef yescrypt
      21  
      22  #include <errno.h>
      23  #include <setjmp.h>
      24  #include <stdio.h>
      25  #include <string.h>
      26  #include <signal.h>
      27  #include <sys/mman.h>
      28  
      29  /* If VERBOSE is true, passing testcases will be printed out as well
      30     as failing ones.  */
      31  static bool verbose = false;
      32  
      33  /* All hashes are hashes of this passphrase, an infamous error message
      34     used for some forgotten can't-happen condition in Unix V6; see
      35     <https://wiki.tuhs.org/doku.php?id=anecdotes:values_of_beta>.  */
      36  static const char phrase[] = "values of β will give rise to dom!";
      37  
      38  /* Correct setting strings, from which we derive incorrect ones by
      39     replacing one character at a time with a character that cannot
      40     appear in a valid passphrase (namely ':') and/or truncating the
      41     string.  */
      42  struct valid_setting;
      43  
      44  /* Type of functions to use in is_valid_trunc.  */
      45  typedef bool (*valid_trunc_p)(const struct valid_setting *original,
      46                                const char *truncated);
      47  
      48  struct valid_setting
      49  {
      50    /* Human-readable name for this test */
      51    const char *tag;
      52  
      53    /* The setting string */
      54    const char *setting;
      55  
      56    /* Length of the actual setting, within the setting string.  This is
      57       usually equal to strlen(setting), but a couple of the strings are
      58       padded on the right for hash-specific reasons.  */
      59    size_t setting_len;
      60  
      61    /* Given a truncation of a valid setting string, decide whether the
      62     truncation is also valid.  */
      63    valid_trunc_p is_valid_trunc;
      64  
      65    /* Numeric parameter for is_valid_trunc; usually the length of a
      66       subfield of the setting.  */
      67    uint16_t is_valid_trunc_param;
      68  
      69    /* Whether support for this hash was compiled into the library.  */
      70    bool enabled;
      71  
      72  };
      73  
      74  /* is_valid_trunc functions -- forward declarations */
      75  
      76  static bool vt_never(const struct valid_setting *, const char *);
      77  static bool vt_varsuffix(const struct valid_setting *, const char *);
      78  static bool vt_sunmd5(const struct valid_setting *, const char *);
      79  static bool vt_sha2gnu(const struct valid_setting *, const char *);
      80  static bool vt_yescrypt(const struct valid_setting *, const char *);
      81  
      82  /* shorthands for use in valid_cases */
      83  
      84  #define V_(  hash,      setting, vt, vp) \
      85    { #hash,               setting, sizeof setting - 1, vt, vp, INCLUDE_##hash }
      86  #define Vp_( hash,      setting, vt, vp) \
      87    { #hash,               setting, vp,                 vt, vp, INCLUDE_##hash }
      88  #define Vt_( hash, tag, setting, vt, vp) \
      89    { #hash " (" #tag ")", setting, sizeof setting - 1, vt, vp, INCLUDE_##hash }
      90  #define Vtp_(hash, tag, setting, vt, vp) \
      91    { #hash " (" #tag ")", setting, vp,                 vt, vp, INCLUDE_##hash }
      92  
      93  #define V(  hash,          setting) V_(  hash,      setting, vt_never,     0)
      94  #define Vn( hash,      vt, setting) V_(  hash,      setting, vt_##vt,      0)
      95  #define Vp( hash,      sl, setting) Vp_( hash,      setting, vt_varsuffix, sl)
      96  #define Vv( hash,      sl, setting) V_(  hash,      setting, vt_varsuffix, sl)
      97  #define Vt( hash, tag,     setting) Vt_( hash, tag, setting, vt_never,     0)
      98  #define Vtn(hash, tag, vt, setting) Vt_( hash, tag, setting, vt_##vt,      0)
      99  #define Vtp(hash, tag, sl, setting) Vtp_(hash, tag, setting, vt_varsuffix, sl)
     100  #define Vtv(hash, tag, sl, setting) Vt_( hash, tag, setting, vt_varsuffix, sl)
     101  
     102  /* Each of these is a valid setting string for some algorithm,
     103     from which we will derive many invalid setting strings.
     104     This is an expensive test, so where possible, the number of
     105     "rounds" of the hash function has been set abnormally low.  */
     106  static const struct valid_setting valid_cases[] =
     107  {
     108    V  (descrypt,                            "Mp"                               ),
     109    /* bigcrypt is extra special:
     110       this salt is a valid descrypt salt when bigcrypt isn't enabled
     111         but descrypt is;
     112       truncations down to 2 are valid when descrypt is enabled, but
     113         if *only* bigcrypt is enabled, then truncations can only
     114         go down to 14.  */
     115    {
     116      INCLUDE_bigcrypt ? "bigcrypt" : "descrypt (padded)",
     117      "Mp............", 2, vt_varsuffix,
     118      INCLUDE_descrypt ? 2 : 14,
     119      INCLUDE_descrypt || INCLUDE_bigcrypt
     120    },
     121    V  (bsdicrypt,                           "_J9..MJHn"                        ),
     122    Vv (md5crypt,                  3,        "$1$MJHnaAke$"                     ),
     123    Vtn(sunmd5,        plain,      sunmd5,   "$md5$1xMeE.at$"                   ),
     124    Vtn(sunmd5,        rounds,     sunmd5,   "$md5,rounds=123$1xMeE.at$"        ),
     125    Vt (nt,            plain,                "$3$"                              ),
     126    Vtp(nt,            fake salt,  3,        "$3$__not_used__c809a450df09a3"    ),
     127    Vv (sha1crypt,                 11,       "$sha1$123$GGXpNqoJvglVTkGU$"      ),
     128    Vtn(sha256crypt,   plain,      sha2gnu,  "$5$MJHnaAkegEVYHsFK$"             ),
     129    Vtn(sha256crypt,   rounds,     sha2gnu,  "$5$rounds=1000$MJHnaAkegEVYHsFK$" ),
     130    Vtn(sha512crypt,   plain,      sha2gnu,  "$6$MJHnaAkegEVYHsFK$"             ),
     131    Vtn(sha512crypt,   rounds,     sha2gnu,  "$6$rounds=1000$MJHnaAkegEVYHsFK$" ),
     132    V  (bcrypt,                              "$2b$04$UBVLHeMpJ/QQCv3XqJx8zO"    ),
     133    V  (bcrypt_a,                            "$2a$04$UBVLHeMpJ/QQCv3XqJx8zO"    ),
     134    V  (bcrypt_x,                            "$2x$04$UBVLHeMpJ/QQCv3XqJx8zO"    ),
     135    V  (bcrypt_y,                            "$2y$04$UBVLHeMpJ/QQCv3XqJx8zO"    ),
     136    Vv (scrypt,                    14,       "$7$C6..../....SodiumChloride$"    ),
     137    Vn (yescrypt,                  yescrypt, "$y$j9T$PKXc3hCOSyMqdaEQArI62/$"   ),
     138    Vn (gost_yescrypt,             yescrypt, "$gy$j9T$PKXc3hCOSyMqdaEQArI62/$"  ),
     139  };
     140  
     141  #undef V_
     142  #undef Vp_
     143  #undef Vt_
     144  #undef Vtp_
     145  
     146  #undef V
     147  #undef Vn
     148  #undef Vp
     149  #undef Vv
     150  #undef Vt
     151  #undef Vtn
     152  #undef Vtp
     153  #undef Vtv
     154  
     155  /* Additional tests of manually constructed, invalid setting
     156     strings.  */
     157  struct invalid_setting
     158  {
     159    const char *tag;
     160    const char *setting;
     161  };
     162  static const struct invalid_setting invalid_cases[] =
     163  {
     164    /* These strings are invalid regardless of the algorithm.  */
     165    { "too short 1",                 "/"                                      },
     166    { "too short 2",                 "M"                                      },
     167    { "too short 3",                 "$"                                      },
     168    { "too short 4",                 "_"                                      },
     169    { "too short 5",                 "."                                      },
     170    { "invalid char :",              ":"                                      },
     171    { "invalid char ;",              ";"                                      },
     172    { "invalid char *",              "*"                                      },
     173    { "invalid char !",              "!"                                      },
     174    { "invalid char \\",             "\\"                                     },
     175    { "invalid char SPC",            " "                                      },
     176    { "invalid char TAB",            "\t"                                     },
     177    { "invalid char ^M",             "\r"                                     },
     178    { "invalid char ^J",             "\n"                                     },
     179    { "invalid char ^L",             "\f"                                     },
     180    { "invalid char ^A",             "\001"                                   },
     181    { "invalid char DEL",            "\177"                                   },
     182    { "failure token 1",             "*0"                                     },
     183    { "failure token 2",             "*1"                                     },
     184    { "unsupported algorithm",       "$un$upp0rt3d$"                          },
     185    { "empty string",                ""                                       },
     186  
     187    /* These strings are invalid for specific algorithms, in ways
     188       that the generic error generator cannot produce.  */
     189    { "sunmd5 absent rounds",        "$md5,rounds=$1xMeE.at$"                 },
     190    { "sunmd5 low rounds",           "$md5,rounds=0$1xMeE.at$"                },
     191    { "sunmd5 octal rounds",         "$md5,rounds=012$1xMeE.at$"              },
     192    { "sunmd5 high rounds",          "$md5,rounds=4294967296$1xMeE.at$"       },
     193    { "sha256 absent rounds",        "$5$rounds=$MJHnaAkegEVYHsFK$"           },
     194    { "sha256 low rounds",           "$5$rounds=0$MJHnaAkegEVYHsFK$"          },
     195    { "sha256 octal rounds",         "$5$rounds=0100$MJHnaAkegEVYHsFK$"       },
     196    { "sha256 high rounds",          "$5$rounds=4294967295$MJHnaAkegEVYHsFK$" },
     197    { "sha512 absent rounds",        "$6$rounds=$MJHnaAkegEVYHsFK$"           },
     198    { "sha512 low rounds",           "$6$rounds=0$MJHnaAkegEVYHsFK$"          },
     199    { "sha512 octal rounds",         "$6$rounds=0100$MJHnaAkegEVYHsFK$"       },
     200    { "sha512 high rounds",          "$6$rounds=4294967295$MJHnaAkegEVYHsFK$" },
     201    { "bcrypt no subtype",           "$2$04$UBVLHeMpJ/QQCv3XqJx8zO"           },
     202    { "bcrypt_b low rounds",         "$2b$03$UBVLHeMpJ/QQCv3XqJx8zO"          },
     203    { "bcrypt_b high rounds",        "$2b$32$UBVLHeMpJ/QQCv3XqJx8zO"          },
     204    { "bcrypt_a low rounds",         "$2a$03$UBVLHeMpJ/QQCv3XqJx8zO"          },
     205    { "bcrypt_a high rounds",        "$2a$32$UBVLHeMpJ/QQCv3XqJx8zO"          },
     206    { "bcrypt_x low rounds",         "$2x$03$UBVLHeMpJ/QQCv3XqJx8zO"          },
     207    { "bcrypt_x high rounds",        "$2x$32$UBVLHeMpJ/QQCv3XqJx8zO"          },
     208    { "bcrypt_y low rounds",         "$2y$03$UBVLHeMpJ/QQCv3XqJx8zO"          },
     209    { "bcrypt_y low rounds",         "$2y$32$UBVLHeMpJ/QQCv3XqJx8zO"          },
     210    { "yescrypt short params",       "$y$j9$PKXc3hCOSyMqdaEQArI62/$"          },
     211    { "gost-yescrypt short params",  "$gy$j9$PKXc3hCOSyMqdaEQArI62/$"         },
     212  };
     213  
     214  /* is_valid_trunc functions -- definitions.
     215     Note: these only need to be correct for the patterns we actually test.  */
     216  
     217  /* All truncations of this setting string are invalid.  */
     218  static bool
     219  vt_never(const struct valid_setting * ARG_UNUSED(original),
     220           const char * ARG_UNUSED(truncated))
     221  {
     222    return false;
     223  }
     224  
     225  /* This setting string has a variable-length suffix; truncations are
     226     valid as long as the result has at least `is_valid_trunc_param'
     227     characters.  */
     228  static bool
     229  vt_varsuffix(const struct valid_setting *original,
     230               const char *truncated)
     231  {
     232    return strlen(truncated) >= original->is_valid_trunc_param;
     233  }
     234  
     235  /* Special validity rule for sunmd5, sha256crypt, and sha512crypt: ... */
     236  static bool
     237  vt_roundseq(const char *truncated, size_t minlen, size_t roundslen,
     238              const char *roundstag1, const char *roundstag2)
     239  {
     240    /* ... the setting cannot be valid if it's shorter than 'minlen'
     241       characters ... */
     242    if (strlen(truncated) < minlen)
     243      return false;
     244  
     245    /* ... if it begins with roundstag1 or roundstag2 then a sequence of
     246       digits must follow, then a dollar sign; roundstag2 may be null;
     247       ... */
     248    if (!strncmp(truncated, roundstag1, roundslen)
     249        || (roundstag2 && !strncmp(truncated, roundstag2, roundslen)))
     250      {
     251        size_t i = roundslen;
     252        while (truncated[i] >= '0' && truncated[i] <= '9')
     253          i++;
     254        if (truncated[i] != '$')
     255          return false;
     256      }
     257  
     258    /* ... otherwise it's ok.  */
     259    return true;
     260  }
     261  
     262  /* Special validity rule for sunmd5.  */
     263  static bool
     264  vt_sunmd5(const struct valid_setting *ARG_UNUSED(original),
     265            const char *truncated)
     266  {
     267    return vt_roundseq(truncated, strlen("$md5$"), strlen("$md5,rounds="),
     268                       "$md5,rounds=", 0);
     269  }
     270  
     271  /* Special validity rule for sha256crypt and sha512crypt.  */
     272  static bool
     273  vt_sha2gnu(const struct valid_setting *ARG_UNUSED(original),
     274             const char *truncated)
     275  {
     276    return vt_roundseq(truncated, strlen("$5$"), strlen("$5$rounds="),
     277                       "$5$rounds=", "$6$rounds=");
     278  }
     279  
     280  /* Special validity rule for yescrypt and gost_yescrypt: ... */
     281  static bool
     282  vt_yescrypt(const struct valid_setting *ARG_UNUSED(original),
     283              const char *truncated)
     284  {
     285    /* ... the setting string must begin with "$y$j9T$" or "$gy$j9T$"
     286       (other introductory sequences are possible but those are the
     287       only ones we use); ... */
     288    size_t y_intro_len = strlen("$y$j9T$");
     289    size_t gy_intro_len = strlen("$gy$j9T$");
     290    size_t intro_len;
     291    if (!strncmp(truncated, "$y$j9T$", y_intro_len))
     292      intro_len = y_intro_len;
     293    else if (!strncmp(truncated, "$gy$j9T$", gy_intro_len))
     294      intro_len = gy_intro_len;
     295    else
     296      return false;
     297  
     298    /* ... and the remainder must be one of these lengths.  (I do not
     299       see a pattern.)  */
     300    switch (strlen(truncated) - intro_len)
     301      {
     302      case  0:
     303      case  4:
     304      case  7:
     305      case  8:
     306      case 12:
     307      case 16:
     308      case 20:
     309      case 22:
     310      case 23:
     311        return true;
     312      default:
     313        return false;
     314      }
     315  }
     316  
     317  
     318  /* Some of the test setting strings contain unprintable characters,
     319     which we print as hex escapes.  For readability, whenever we print
     320     out a setting string we pad it on the right with spaces to the
     321     length of the longest setting string we have.  (There is always
     322     something after that on the line.)  */
     323  static size_t longest_setting;
     324  
     325  static void
     326  print_setting (const char *setting, bool pad)
     327  {
     328    size_t n = 0;
     329    for (; *setting; setting++)
     330      {
     331        unsigned int c = (unsigned int)(unsigned char) *setting;
     332        if (0x20 <= c && c <= 0x7e)
     333          {
     334            putchar ((int)c);
     335            n += 1;
     336          }
     337        else
     338          {
     339            printf ("\\x%02x", c);
     340            n += 4;
     341          }
     342      }
     343    if (!pad)
     344      return;
     345    while (n < longest_setting)
     346      {
     347        putchar (' ');
     348        n += 1;
     349      }
     350  }
     351  
     352  static size_t
     353  measure_setting (const char *setting)
     354  {
     355    size_t n = 0;
     356    for (; *setting; setting++)
     357      {
     358        unsigned int c = (unsigned int)(unsigned char) *setting;
     359        if (0x20 <= c && c <= 0x7e)
     360          n += 1;
     361        else
     362          n += 4;
     363      }
     364    return n;
     365  }
     366  
     367  static void
     368  measure_settings (void)
     369  {
     370    size_t ls = 0;
     371    for (size_t i = 0; i < ARRAY_SIZE (valid_cases); i++)
     372      ls = MAX (ls, measure_setting(valid_cases[i].setting));
     373  
     374    for (size_t i = 0; i < ARRAY_SIZE (invalid_cases); i++)
     375      ls = MAX (ls, measure_setting(invalid_cases[i].setting));
     376  
     377    longest_setting = ls;
     378  }
     379  
     380  static void
     381  print_result (const char *result, const char *setting,
     382                const char *tag, bool expected_valid)
     383  {
     384    printf ("%s: ", result);
     385    print_setting (setting, true);
     386    printf (" (%s, %s)", tag, expected_valid ? "valid" : "invalid");
     387  }
     388  
     389  /* Part of what we're testing, is whether any of the hashing methods
     390     can read past the end of a properly terminated C string that
     391     happens to contain an invalid setting.  We do this by placing the
     392     invalid setting right next to a page of inaccessible memory and
     393     trapping the fault.  */
     394  static volatile sig_atomic_t signal_loop = 0;
     395  static sigjmp_buf env;
     396  static void
     397  segv_handler (int sig)
     398  {
     399    if (signal_loop == 0)
     400      {
     401        signal_loop = 1;
     402        siglongjmp (env, sig);
     403      }
     404    else
     405      {
     406        signal (sig, SIG_DFL);
     407        raise (sig);
     408      }
     409  }
     410  
     411  /* We use only crypt_rn in this test, because it only exercises the
     412     error handling logic within the hashing methods, not the
     413     higher-level error handling logic that varies slightly among the
     414     entry points (that's all taken care of in crypt-badargs.c).  We use
     415     crypt_rn instead of crypt_r so that this test does not need to vary
     416     any of its logic based on --enable-failure-tokens.  */
     417  static bool
     418  test_one_setting (const char *setting, size_t l_setting,
     419                    const char *tag, bool expected_valid,
     420                    struct crypt_data *cd)
     421  {
     422    volatile bool fail = false;
     423    signal_loop = 0;
     424    int sig = sigsetjmp (env, 1);
     425    if (!sig)
     426      {
     427        char *retval = crypt_rn (phrase, setting, cd, (int) sizeof *cd);
     428        if (expected_valid)
     429          {
     430            if (!retval)
     431              {
     432                fail = true;
     433                print_result ("FAIL", setting, tag, expected_valid);
     434                puts(": returned NULL");
     435              }
     436            else if (retval != cd->output)
     437              {
     438                fail = true;
     439                print_result ("FAIL", setting, tag, expected_valid);
     440                printf(": returned %p, should be %p\n",
     441                       (const void *)retval, (const void *)cd->output);
     442              }
     443            else if (strncmp (retval, setting, l_setting))
     444              {
     445                fail = true;
     446                print_result("FAIL", setting, tag, expected_valid);
     447                fputs(": got non-matching ", stdout);
     448                print_setting(retval, false);
     449                putchar('\n');
     450              }
     451          }
     452        else
     453          {
     454            if (retval)
     455              {
     456                fail = true;
     457                print_result ("FAIL", setting, tag, expected_valid);
     458                fputs(": expected NULL, got ", stdout);
     459                print_setting (retval, false);
     460                putchar('\n');
     461              }
     462          }
     463      }
     464    else
     465      {
     466        fail = true;
     467        print_result("FAIL", setting, tag, expected_valid);
     468        printf(": %s\n", strsignal (sig));
     469      }
     470  
     471    if (verbose && !fail)
     472      {
     473        print_result("PASS", setting, tag, expected_valid);
     474        putchar('\n');
     475      }
     476  
     477    return fail;
     478  }
     479  
     480  static bool
     481  test_one_valid(const struct valid_setting *tc,
     482                 char *page, size_t pagesize, struct crypt_data *cd)
     483  {
     484    /* Caution: tc->setting_len is _not_ always equal to strlen(tc->setting).
     485       Sometimes it is smaller.  */
     486    size_t l_setting = strlen(tc->setting) + 1;
     487    char *setting = page + pagesize - l_setting;
     488    memcpy(setting, tc->setting, l_setting);
     489  
     490    /* crypt_rn() using this setting, unmodified, is expected to
     491       succeed, unless the hash function is disabled.  */
     492    if (test_one_setting (setting, tc->setting_len, tc->tag, tc->enabled, cd))
     493      return true;
     494  
     495    /* Rechecking the hash with the full output should also succeed.
     496       In this subtest we expect to get the same _complete hash_
     497       back out, not just the same setting.  */
     498    if (tc->enabled)
     499      {
     500        size_t l_hash = strlen (cd->output);
     501        char *p = page + pagesize - (l_hash + 1);
     502        assert (l_hash + 1 <= CRYPT_OUTPUT_SIZE);
     503        memcpy (p, cd->output, l_hash + 1);
     504  
     505        if (test_one_setting (p, l_hash, tc->tag, true, cd))
     506          return true;
     507  
     508        /* When crypt() is called with a complete hashed passphrase as the
     509           setting string, the hashing method must not look at the hash
     510           component of the setting _at all_.  We test this by supplying a
     511           string with one extra character, an A, which _could_ be part of
     512           the hash component for all supported methods, but which is much
     513           too short by itself.  This should produce the same complete hash
     514           as the previous test.  (It has to be a character which _could_
     515           appear, because the generic crypt() machinery rejects setting
     516           strings containing invalid characters in any position.)
     517  
     518           Super special case: Don't do this subtest for sunmd5,
     519           because, due to a bug in its original implementation, the
     520           first character after the end of the salt _does_ affect the
     521           hash output.  We have to preserve this bug for compatibility
     522           with existing sunmd5 hashed passphrases.  */
     523        if (!INCLUDE_sunmd5 || strncmp(tc->setting, "$md5", 4))
     524          {
     525            p = page + pagesize - (l_hash + 1 + l_setting + 1);
     526            memcpy (p, cd->output, l_hash + 1);
     527  
     528            char *settingA = page + pagesize - (l_setting + 1);
     529            memcpy(settingA, tc->setting, l_setting - 1);
     530            settingA[l_setting - 1] = 'A';
     531            settingA[l_setting - 0] = '\0';
     532            if (test_one_setting (settingA, tc->setting_len, tc->tag, true, cd))
     533              return true;
     534            if (strcmp (cd->output, p))
     535              {
     536                print_result ("FAIL", settingA, tc->tag, true);
     537                /* Since cd->output and p are both hashed passphrases, not
     538                   handcrafted invalid setting strings, we can safely print
     539                   them with %s.  */
     540                printf (": expected %s, got %s\n", p, cd->output);
     541                return true;
     542              }
     543            else if (verbose)
     544              {
     545                print_result ("PASS", settingA, tc->tag, true);
     546                printf (": got %s, as expected\n", cd->output);
     547              }
     548          }
     549  
     550        /* Restore the original data at 'setting', as expected by code
     551           below.  */
     552        memcpy(setting, tc->setting, l_setting);
     553      }
     554  
     555    /* The rest of the subtests in this function are logically independent.  */
     556    bool failed = false;
     557  
     558    /* Replacing any one character of this setting with a ':', leaving
     559       the rest of the string intact, should cause crypt_rn to fail.  */
     560    for (size_t i = 0; i < l_setting - 1; i++)
     561      {
     562        char saved = setting[i];
     563        setting[i] = ':';
     564        failed |= test_one_setting(setting, tc->setting_len, tc->tag, false, cd);
     565        setting[i] = saved;
     566      }
     567  
     568    /* Chop off the last character of the setting string and test that.
     569       Then, replace the new last character of the setting string with a
     570       colon, and test that.  (This is different from the earlier test
     571       where we replaced each character in turn with a colon but kept
     572       the rest of the string intact, because the hashing method might
     573       be calling strlen() on the setting string.)  Repeat these two
     574       steps until we have just one character left, then stop.
     575  
     576       For instance, if the original setting string is
     577           $1$MJHnaAke$
     578       then we test
     579           $1$MJHnaAke
     580           $1$MJHnaAk:
     581           $1$MJHnaAk
     582           $1$MJHnaA:
     583           $1$MJHnaA
     584           ...
     585           $1
     586           $:
     587  
     588       ($1$MJHnaAke: would have been tested by the loop above.  All the
     589       single-character strings that can be a prefix of a setting string
     590       from valid_cases---"$", "_", "M"---are tested by invalid_cases,
     591       is ":".)
     592  
     593       Up till this point l_setting has been _one more than_
     594       strlen(setting), but in this loop it is more convenient to have
     595       it be equal to strlen(setting).  */
     596    l_setting -= 1;
     597  
     598    while (l_setting > 2)
     599      {
     600        memmove(setting + 1, setting, l_setting - 1);
     601        setting += 1;
     602        l_setting -= 1;
     603        failed |= test_one_setting(setting, MIN (l_setting, tc->setting_len),
     604                                   tc->tag,
     605                                   tc->enabled
     606                                   && tc->is_valid_trunc(tc, setting),
     607                                   cd);
     608        page[pagesize - 2] = ':';
     609        failed |= test_one_setting(setting, l_setting, tc->tag, false, cd);
     610      }
     611  
     612    return failed;
     613  }
     614  
     615  static bool
     616  test_one_invalid(const struct invalid_setting *tc,
     617                   char *page, size_t pagesize, struct crypt_data *cd)
     618  {
     619    size_t l_setting = strlen(tc->setting) + 1;
     620    char *setting = page + pagesize - l_setting;
     621    memcpy(setting, tc->setting, l_setting);
     622    return test_one_setting(setting, l_setting - 1, tc->tag, false, cd);
     623  }
     624  
     625  static bool
     626  do_tests(char *page, size_t pagesize)
     627  {
     628    bool failed = false;
     629  
     630    struct crypt_data cd;
     631    memset (&cd, 0, sizeof cd);
     632  
     633    for (size_t i = 0; i < ARRAY_SIZE (valid_cases); i++)
     634      failed |= test_one_valid (&valid_cases[i], page, pagesize, &cd);
     635  
     636    for (size_t i = 0; i < ARRAY_SIZE (invalid_cases); i++)
     637      failed |= test_one_invalid (&invalid_cases[i], page, pagesize, &cd);
     638  
     639    return failed;
     640  }
     641  
     642  int
     643  main (int argc, char **argv)
     644  {
     645    if (argc <= 1)
     646      ;
     647    else if (argc == 2
     648             && (!strcmp(argv[1], "-v")
     649                 || !strcmp(argv[1], "--verbose")))
     650      verbose = true;
     651    else
     652      {
     653        fprintf(stderr, "usage: %s [-v | --verbose]\n", argv[0]);
     654        return 99;
     655      }
     656  
     657    if (setvbuf(stdout, 0, _IOLBF, 0) || setvbuf(stderr, 0, _IOLBF, 0))
     658      {
     659        perror ("setvbuf");
     660        return 99;
     661      }
     662  
     663    /* Set up a two-page region whose first page is read-write and
     664       whose second page is inaccessible.  */
     665    long pagesize_l = sysconf (_SC_PAGESIZE);
     666    if (pagesize_l < (long) CRYPT_OUTPUT_SIZE)
     667      {
     668        printf ("ERROR: pagesize of %ld is too small\n", pagesize_l);
     669        return 99;
     670      }
     671  
     672    size_t pagesize = (size_t) pagesize_l;
     673    char *page = mmap (0, pagesize * 2, PROT_READ|PROT_WRITE,
     674                       MAP_PRIVATE|MAP_ANON, -1, 0);
     675    if (page == MAP_FAILED)
     676      {
     677        perror ("mmap");
     678        return 99;
     679      }
     680    memset (page, 'x', pagesize * 2);
     681    if (mprotect (page + pagesize, pagesize, PROT_NONE))
     682      {
     683        perror ("mprotect");
     684        return 99;
     685      }
     686  
     687    struct sigaction sa, os, ob;
     688    sigfillset (&sa.sa_mask);
     689    sa.sa_flags = SA_RESTART;
     690    sa.sa_handler = segv_handler;
     691    if (sigaction (SIGBUS, &sa, &ob) || sigaction (SIGSEGV, &sa, &os))
     692      {
     693        perror ("sigaction");
     694        return 1;
     695      }
     696  
     697    measure_settings();
     698    bool failed = do_tests (page, pagesize);
     699  
     700    sigaction (SIGBUS, &ob, 0);
     701    sigaction (SIGSEGV, &os, 0);
     702  
     703    return failed;
     704  }