Remove SUNMD5 support due to potential incompatibility with the CDDL license. According to https://lists.fedoraproject.org/archives/list/legal@lists.fedoraproject.org/message/KCDXNZJQ4S24NVB6OUPDGQA2VF53YZIT/ we do not have to repackage the source tarball, patching out the code like this is sufficient. diff --git a/Makefile.am b/Makefile.am index a7e726f9cb82a4b1..ca2b0accf886895d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -115,7 +115,6 @@ check_PROGRAMS = \ if ENABLE_WEAK_HASHES libcrypt_la_SOURCES += \ crypt-md5.c crypt-des.c crypt-nthash.c crypt-pbkdf1-sha1.c \ - crypt-sunmd5.c \ alg-des.c alg-hmac-sha1.c alg-md4.c alg-md5.c alg-sha1.c nodist_libcrypt_la_SOURCES = \ @@ -146,7 +145,7 @@ check_PROGRAMS += \ test-alg-des test-alg-hmac-sha1 test-alg-md4 \ test-alg-md5 test-alg-sha1 \ test-crypt-badsalt test-crypt-des test-crypt-md5 \ - test-crypt-nthash test-crypt-sunmd5 test-crypt-pbkdf1-sha1 + test-crypt-nthash test-crypt-pbkdf1-sha1 endif if ENABLE_OBSOLETE_API @@ -187,7 +186,6 @@ test_crypt_nthash_LDADD = libcrypt.la test_crypt_pbkdf1_sha1_LDADD = libcrypt.la test_crypt_sha256_LDADD = libcrypt.la test_crypt_sha512_LDADD = libcrypt.la -test_crypt_sunmd5_LDADD = libcrypt.la test_gensalt_LDADD = libcrypt.la test_des_obsolete_LDADD = libcrypt.la test_des_obsolete_r_LDADD = libcrypt.la diff --git a/crypt-port.h b/crypt-port.h index 2b74ec9ef7252ba4..6331d56391a0b093 100644 --- a/crypt-port.h +++ b/crypt-port.h @@ -219,7 +219,6 @@ void _xcrypt_secure_memset (void *s, size_t len) #define crypt_md5_rn _crypt_crypt_md5_rn #define crypt_nthash_rn _crypt_crypt_nthash_rn #define crypt_sha1_rn _crypt_crypt_sha1_rn -#define crypt_sunmd5_rn _crypt_crypt_sunmd5_rn #define des_crypt_block _crypt_des_crypt_block #define des_set_key _crypt_des_set_key #define des_set_salt _crypt_des_set_salt @@ -230,7 +229,6 @@ void _xcrypt_secure_memset (void *s, size_t len) #define gensalt_md5_rn _crypt_gensalt_md5_rn #define gensalt_nthash_rn _crypt_gensalt_nthash_rn #define gensalt_sha1_rn _crypt_gensalt_sha1_rn -#define gensalt_sunmd5_rn _crypt_gensalt_sunmd5_rn #define ip_maskl _crypt_ip_maskl #define ip_maskr _crypt_ip_maskr #define key_perm_maskl _crypt_key_perm_maskl diff --git a/crypt-private.h b/crypt-private.h index 80a916d76181ed66..c595c0fc163b2639 100644 --- a/crypt-private.h +++ b/crypt-private.h @@ -54,9 +54,6 @@ extern void crypt_nthash_rn (const char *phrase, const char *setting, extern void crypt_sha1_rn (const char *phrase, const char *setting, uint8_t *output, size_t o_size, void *scratch, size_t s_size); -extern void crypt_sunmd5_rn (const char *phrase, const char *setting, - uint8_t *output, size_t o_size, - void *scratch, size_t s_size); #endif extern void crypt_sha256_rn (const char *phrase, const char *setting, @@ -85,9 +82,6 @@ extern void gensalt_nthash_rn (unsigned long count, extern void gensalt_sha1_rn (unsigned long count, const uint8_t *rbytes, size_t nrbytes, uint8_t *output, size_t o_size); -extern void gensalt_sunmd5_rn (unsigned long count, - const uint8_t *rbytes, size_t nrbytes, - uint8_t *output, size_t o_size); #endif extern void gensalt_sha256_rn (unsigned long count, diff --git a/crypt-sunmd5.c b/crypt-sunmd5.c deleted file mode 100644 index 43054b96be357df3..0000000000000000 --- a/crypt-sunmd5.c +++ /dev/null @@ -1,421 +0,0 @@ -/* - * CDDL HEADER START - * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. - * - * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE - * or http://www.opensolaris.org/os/licensing. - * See the License for the specific language governing permissions - * and limitations under the License. - * - * When distributing Covered Code, include this CDDL HEADER in each - * file and include the License file at usr/src/OPENSOLARIS.LICENSE. - * If applicable, add the following below this CDDL HEADER, with the - * fields enclosed by brackets "[]" replaced with your own identifying - * information: Portions Copyright [yyyy] [name of copyright owner] - * - * CDDL HEADER END - */ -/* - * Copyright 2003 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - -#include "crypt-port.h" -#include "crypt-private.h" -#include "alg-md5.h" - -#include -#include -#include - -#define CRYPT_ALGNAME "md5" - -/* minimum number of rounds we do, not including the per-user ones */ -#define BASIC_ROUND_COUNT 4096 /* enough to make things interesting */ -#define DIGEST_LEN 16 -#define ROUND_BUFFER_LEN 64 - -/* - * Public domain quotation courtesy of Project Gutenberg. - * ftp://metalab.unc.edu/pub/docs/books/gutenberg/etext98/2ws2610.txt - * Hamlet III.ii - 1517 bytes, including trailing NUL - * ANSI-C string constant concatenation is a requirement here. - */ -static const char constant_phrase[] = - "To be, or not to be,--that is the question:--\n" - "Whether 'tis nobler in the mind to suffer\n" - "The slings and arrows of outrageous fortune\n" - "Or to take arms against a sea of troubles,\n" - "And by opposing end them?--To die,--to sleep,--\n" - "No more; and by a sleep to say we end\n" - "The heartache, and the thousand natural shocks\n" - "That flesh is heir to,--'tis a consummation\n" - "Devoutly to be wish'd. To die,--to sleep;--\n" - "To sleep! perchance to dream:--ay, there's the rub;\n" - "For in that sleep of death what dreams may come,\n" - "When we have shuffled off this mortal coil,\n" - "Must give us pause: there's the respect\n" - "That makes calamity of so long life;\n" - "For who would bear the whips and scorns of time,\n" - "The oppressor's wrong, the proud man's contumely,\n" - "The pangs of despis'd love, the law's delay,\n" - "The insolence of office, and the spurns\n" - "That patient merit of the unworthy takes,\n" - "When he himself might his quietus make\n" - "With a bare bodkin? who would these fardels bear,\n" - "To grunt and sweat under a weary life,\n" - "But that the dread of something after death,--\n" - "The undiscover'd country, from whose bourn\n" - "No traveller returns,--puzzles the will,\n" - "And makes us rather bear those ills we have\n" - "Than fly to others that we know not of?\n" - "Thus conscience does make cowards of us all;\n" - "And thus the native hue of resolution\n" - "Is sicklied o'er with the pale cast of thought;\n" - "And enterprises of great pith and moment,\n" - "With this regard, their currents turn awry,\n" - "And lose the name of action.--Soft you now!\n" - "The fair Ophelia!--Nymph, in thy orisons\n" - "Be all my sins remember'd.\n"; - -/* ------------------------------------------------------------------ */ - -static int -md5bit (uint8_t *digest, int bit_num) -{ - int byte_off; - int bit_off; - - bit_num %= 128; /* keep this bounded for convenience */ - byte_off = bit_num / 8; - bit_off = bit_num % 8; - - /* return the value of bit N from the digest */ - return ((digest[byte_off] & (0x01 << bit_off)) ? 1 : 0); -} - -/* 0 ... 63 => ascii - 64 */ -static unsigned char itoa64[] = - "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; - -static void -to64 (char *s, uint64_t v, int n) -{ - while (--n >= 0) - { - *s++ = (char)itoa64[v&0x3f]; - v >>= 6; - } -} - -#define ROUNDS "rounds=" -#define ROUNDSLEN (sizeof (ROUNDS) - 1) - -/* - * get the integer value after rounds= where ever it occurs in the string. - * if the last char after the int is a , or $ that is fine anything else is an - * error. - */ -static uint32_t -getrounds (const char *s) -{ - char *r, *p, *e; - long val; - - if (s == NULL) - return (0); - - if ((r = strstr (s, ROUNDS)) == NULL) - return (0); - - if (strncmp (r, ROUNDS, ROUNDSLEN) != 0) - return (0); - - p = r + ROUNDSLEN; - errno = 0; - val = strtol (p, &e, 10); - /* - * An error occured or there is non-numeric stuff at the end - * which isn't one of the crypt(3c) special chars ',' or '$' - */ - if (errno != 0 || val < 0 || - !(*e == '\0' || *e == ',' || *e == '$')) - { - return (0); - } - - return ((uint32_t)val); -} - -void -gensalt_sunmd5_rn (unsigned long count, - const uint8_t *rbytes, size_t nrbytes, - uint8_t *output, size_t o_size) -{ - /* This should not happen, but. */ - if ((nrbytes < sizeof (uint64_t)) || (o_size < 33)) - { - errno = ERANGE; - return; - } - - uint64_t rndval; - char rndstr[sizeof (rndval) + 1]; /* rndval as a base64 string */ - const uint8_t minrounds = 15; /* Min. number of rounds = 2^X */ - - /* Set count to a reasonable random value, - if count was not set high enough by the - caller. */ - if (count < (unsigned long)(1 << minrounds)) - { - uint64_t rand1, rand2; - get_random_bytes(&rand1, sizeof (uint64_t)); - get_random_bytes(&rand2, sizeof (uint64_t)); - count = (long unsigned int)(1 << ((rand1 % 2) + minrounds)); - count += (long unsigned int)(rand2 % (uint64_t)((1 << (minrounds - 1)) + 1)); - } - - memcpy (&rndval, rbytes, sizeof (rndval)); - to64 ((char *)&rndstr, rndval, sizeof (rndval)); - rndstr[sizeof (rndstr) - 1] = '\0'; - - /* Generated salt is at least 27 bytes - and a maximum of 32 bytes long. */ - snprintf ((char *)output, o_size, - "$" CRYPT_ALGNAME "," ROUNDS "%u$%s$", - (unsigned int)count, rndstr); -} - -void -crypt_sunmd5_rn (const char *phrase, const char *setting, - uint8_t *output, size_t o_size, - void *scratch, size_t s_size) -{ - /* put all the sensitive data in a struct */ - struct sunmd5_ctx - { - struct md5_ctx context; /* working buffer for MD5 algorithm */ - uint8_t digest[DIGEST_LEN]; /* where the MD5 digest is stored */ - - int indirect_4[16]; /* extracted array of 4bit values */ - int shift_4[16]; /* shift schedule, vals 0..4 */ - - int s7shift; /* shift for shift_7 creation, vals 0..7 */ - int indirect_7[16]; /* extracted array of 7bit values */ - int shift_7[16]; /* shift schedule, vals 0..1 */ - - int indirect_a; /* 7bit index into digest */ - int shift_a; /* shift schedule, vals 0..1 */ - - int indirect_b; /* 7bit index into digest */ - int shift_b; /* shift schedule, vals 0..1 */ - - int bit_a; /* single bit for cointoss */ - int bit_b; /* single bit for cointoss */ - - char roundascii[ROUND_BUFFER_LEN]; /* ascii rep of roundcount */ - }; - - /* Scratch space needs to be large enough - to fit struct sunmd5_ctx. Output must - be able to fit up to 32 bytes for the - setting + '$' + 22 bytes of hash. */ - if (s_size < sizeof (struct sunmd5_ctx) || (o_size < 32 + 1 + 22)) - { - errno = ERANGE; - return; - } - - /* If the magic does not match, this - should not have been called. */ - if (!strncmp ("$" CRYPT_ALGNAME, setting, sizeof ("$" CRYPT_ALGNAME))) - { - errno = EINVAL; - return; - } - - int i; - int round; - uint32_t maxrounds = BASIC_ROUND_COUNT; - uint32_t l; - char *puresalt; - char *saltend; - char *p; - struct sunmd5_ctx *data = scratch; - - /* - * Extract the puresalt (if it exists) from the existing salt string - * $md5[,rounds=%d]$$ - */ - saltend = strrchr (setting, '$'); - - if (saltend == NULL || saltend == setting) - { - errno = EINVAL; - return; - } - - if (saltend[1] != '\0') - { - size_t len = (size_t)(saltend - setting + 1); - - if ((puresalt = malloc (len)) == NULL) - /* malloc() is supposed to set errno == ENOMEM. */ - return; - - /* The original implementation used strlcpy(), - which is not portable. Since strlcpy() - always terminated a C string properly after - copying len - 1 bytes of data, we need to - do that manually. */ - (void)strncpy (puresalt, setting, len); - puresalt[len - 1] = '\0'; - } - else - { - puresalt = strdup(setting); - - if (puresalt == NULL) - { - /* strdup() is supposed to set errno == ENOMEM. */ - return; - } - } - - /* There must not be any dollar sign '$', but - the last character before the terminating - '\0' in the string containing the salt. */ - if (puresalt[strlen (puresalt) - 2] == '$') - { - errno = EINVAL; - return; - } - - maxrounds += getrounds (setting); - - /* initialise the context */ - md5_init_ctx (&(data->context)); - - /* update with the (hopefully entropic) plaintext */ - md5_process_bytes ((const unsigned char *)phrase, strlen (phrase), &(data->context)); - - /* update with the (publically known) salt */ - md5_process_bytes ((unsigned char *)puresalt, strlen (puresalt), &(data->context)); - - - /* compute the digest */ - md5_finish_ctx (&(data->context), &(data->digest)); - - /* - * now to delay high-speed md5 implementations that have stuff - * like code inlining, loops unrolled and table lookup - */ - - for (round = 0; (uint32_t)round < maxrounds; round++) - { - /* re-initialise the context */ - md5_init_ctx (&(data->context)); - - /* update with the previous digest */ - md5_process_bytes (&(data->digest), sizeof (data->digest), &(data->context)); - - /* populate the shift schedules for use later */ - for (i = 0; i < 16; i++) - { - int j; - - /* offset 3 -> occasionally span more than 1 int32 fetch */ - j = (i + 3) % 16; - data->s7shift = data->digest[i] % 8; - data->shift_4[i] = data->digest[j] % 5; - data->shift_7[i] = (data->digest[j] >> data->s7shift) & 0x01; - } - - data->shift_a = md5bit (data->digest, round); - data->shift_b = md5bit (data->digest, round + 64); - - /* populate indirect_4 with 4bit values extracted from digest */ - for (i = 0; i < 16; i++) - /* shift the digest byte and extract four bits */ - data->indirect_4[i] = (data->digest[i] >> data->shift_4[i]) & 0x0f; - - /* - * populate indirect_7 with 7bit values from digest - * indexed via indirect_4 - */ - - for (i = 0; i < 16; i++) - /* shift the digest byte and extract seven bits */ - data->indirect_7[i] = (data->digest[data->indirect_4[i]] - >> data->shift_7[i]) & 0x7f; - - /* - * use the 7bit values to indirect into digest, - * and create two 8bit values from the results. - */ - data->indirect_a = data->indirect_b = 0; - - for (i = 0; i < 8; i++) - { - data->indirect_a |= (md5bit (data->digest, - data->indirect_7[i]) << i); - - data->indirect_b |= (md5bit (data->digest, - data->indirect_7[i + 8]) << i); - } - - /* shall we utilise the top or bottom 7 bits? */ - data->indirect_a = (data->indirect_a >> data->shift_a) & 0x7f; - data->indirect_b = (data->indirect_b >> data->shift_b) & 0x7f; - - /* extract two data->digest bits */ - data->bit_a = md5bit (data->digest, data->indirect_a); - data->bit_b = md5bit (data->digest, data->indirect_b); - - /* xor a coin-toss; if true, mix-in the constant phrase */ - - if (data->bit_a ^ data->bit_b) - md5_process_bytes ((const unsigned char *) constant_phrase, - sizeof (constant_phrase), - &(data->context)); - - /* digest a decimal sprintf of the current roundcount */ - snprintf (data->roundascii, ROUND_BUFFER_LEN, "%d", round); - md5_process_bytes ((unsigned char *) data->roundascii, - strlen (data->roundascii), - &(data->context)); - - /* compute/flush the digest, and loop */ - md5_finish_ctx (&(data->context), &(data->digest)); - } - - (void)snprintf ((char *)output, o_size, "%s$", puresalt); - - free (puresalt); - - p = (char *)output + strlen ((const char *)output); - - l = (uint32_t)((data->digest[ 0]<<16) | (data->digest[ 6]<<8) | data->digest[12]); - to64 (p, l, 4); - p += 4; - l = (uint32_t)((data->digest[ 1]<<16) | (data->digest[ 7]<<8) | data->digest[13]); - to64 (p, l, 4); - p += 4; - l = (uint32_t)((data->digest[ 2]<<16) | (data->digest[ 8]<<8) | data->digest[14]); - to64 (p, l, 4); - p += 4; - l = (uint32_t)((data->digest[ 3]<<16) | (data->digest[ 9]<<8) | data->digest[15]); - to64 (p, l, 4); - p += 4; - l = (uint32_t)((data->digest[ 4]<<16) | (data->digest[10]<<8) | data->digest[ 5]); - to64 (p, l, 4); - p += 4; - l = (uint32_t)data->digest[11]; - to64 (p, l, 2); - p += 2; - *p = '\0'; -} diff --git a/crypt.c b/crypt.c index d7f0ffcdd10ee0fa..8437d96834824d45 100644 --- a/crypt.c +++ b/crypt.c @@ -88,7 +88,6 @@ static const struct hashfn tagged_hashes[] = #if ENABLE_WEAK_HASHES { "$1$", crypt_md5_rn, gensalt_md5_rn }, { "$3$", crypt_nthash_rn, gensalt_nthash_rn }, - { "$md5", crypt_sunmd5_rn, gensalt_sunmd5_rn }, { "$sha1", crypt_sha1_rn, gensalt_sha1_rn }, #endif { "$5$", crypt_sha256_rn, gensalt_sha256_rn }, diff --git a/test-crypt-sunmd5.c b/test-crypt-sunmd5.c deleted file mode 100644 index d4fc0dc9a3716381..0000000000000000 --- a/test-crypt-sunmd5.c +++ /dev/null @@ -1,130 +0,0 @@ -#include "crypt-port.h" -#include "crypt-base.h" - -#include -#include - -const char *password = "abcdefg"; -const char *tests[][3] = -{ - /* Hashes have been computed with the following program: - - #!/usr/bin/python - - from passlib.hash import sun_md5_crypt - import csv - - with open('sunmd5.txt') as csvfile: - params = csv.reader(csvfile) - for row in params: - print(sun_md5_crypt.using(rounds=int(row[0]), - salt=row[1]).hash("abcdefg")) - - The used csv file had the following format: - - , (when no rounds parameter was given, - rounds were set to zero.) - - The salts have been generated by crypt_gensalt on OpenSolaris. - - - Test X.0: checks the password is encryptable with the full hash - as setting. - Test X.1: checks the password is encryptable with the initially - used salt as setting. - Test X.0: checks for the bug in Sun's original implementation - is present. - See: http://passlib.readthedocs.io/en/stable/lib/passlib.hash.sun_md5_crypt.html#smc-bare-salt - */ - { - "$md5,rounds=5619$9ZLwtuT0$$mLoRQuWY/qcxCRWhD1C2M.", - "$md5,rounds=5619$9ZLwtuT0$", - "$md5,rounds=5619$9ZLwtuT0$mLoRQuWY/qcxCRWhD1C2M." - }, - { - "$md5,rounds=963$er0EceI7$$SdWKu/EgaVvya0m3T4Ml61", - "$md5,rounds=963$er0EceI7$", - "$md5,rounds=963$er0EceI7$SdWKu/EgaVvya0m3T4Ml61" - }, - { - "$md5$1xMeE.at$$qRpVD46c.sEWM/48tNk191", - "$md5$1xMeE.at$", - "$md5$1xMeE.at$qRpVD46c.sEWM/48tNk191" - }, - { - "$md5,rounds=9748$2kkhnoZI$$HzOCKmX2sus/1S9CmohBY/", - "$md5,rounds=9748$2kkhnoZI$", - "$md5,rounds=9748$2kkhnoZI$HzOCKmX2sus/1S9CmohBY/" - }, - { - "$md5$9ZLwtuT0$$ZRfjIfcFjDekvFzC6wCa2/", - "$md5$9ZLwtuT0$", - "$md5$9ZLwtuT0$ZRfjIfcFjDekvFzC6wCa2/" - }, - { - "$md5,rounds=5619$9ZLwtuT0$5.D2mO0RKrZtrrBh3fduf.", - "$md5,rounds=5619$9ZLwtuT0$x", - "$md5,rounds=5619$9ZLwtuT0$$5.D2mO0RKrZtrrBh3fduf." - }, - { - "$md5,rounds=963$er0EceI7$Pt3h1M5TiSImJ4jk663aR/", - "$md5,rounds=963$er0EceI7$x", - "$md5,rounds=963$er0EceI7$$Pt3h1M5TiSImJ4jk663aR/" - }, - { - "$md5$1xMeE.at$I566aJ9IitIdjKKjZJ8Zo0", - "$md5$1xMeE.at$x", - "$md5$1xMeE.at$$I566aJ9IitIdjKKjZJ8Zo0" - }, - { - "$md5,rounds=9748$2kkhnoZI$suo2yEVmCZnnnz6ZZHYit0", - "$md5,rounds=9748$2kkhnoZI$x", - "$md5,rounds=9748$2kkhnoZI$$suo2yEVmCZnnnz6ZZHYit0" - }, - { - "$md5$9ZLwtuT0$UvTt17ajkoa7kwpCrtMeb1", - "$md5$9ZLwtuT0$x", - "$md5$9ZLwtuT0$$UvTt17ajkoa7kwpCrtMeb1" - }, -}; - -#define ntests (sizeof (tests) / sizeof (tests[0])) - -int -main (void) -{ - struct crypt_data output; - int result = 0; - unsigned int i, j; - char *previous = malloc (sizeof (output.output) + 1); - - for (i = 0; i < ntests; ++i) - { - for (j = 0; j < 3; ++j) - { - char *cp = crypt_r (password, tests[i][j], &output); - if ((j == 0) && (strcmp (cp, tests[i][j]) != 0)) - { - printf ("test %u.%u: expected \"%s\", got \"%s\"\n", - i, j, tests[i][j], cp); - result = 1; - } - if ((j == 1) && (strcmp (cp, previous) != 0)) - { - printf ("test %u.%u: expected \"%s\", got \"%s\"\n", - i, j, previous, cp); - result = 1; - } - if ((j == 2) && (strcmp (cp, tests[i][j]) == 0)) - { - printf ("test %u.%u: \"%s\" was not different from returned hash.\n", - i, j, tests[i][j]); - result = 1; - } - strcpy (previous, tests[i][j]); - } - } - - free (previous); - return result; -} diff --git a/test-gensalt.c b/test-gensalt.c index dfe194c69b732ee8..e02eb284c72fb514 100644 --- a/test-gensalt.c +++ b/test-gensalt.c @@ -27,7 +27,6 @@ static const struct testcase testcases[] = { "_", 9 }, // BSDi extended DES { "$1$", 11 }, // MD5 { "$3$", 29 }, // NTHASH - { "$md5", 27 }, // SUNMD5 { "$sha1", 34 }, // PBKDF with SHA1 #endif { "$5$", 19 }, // SHA-2-256