]> git.ipfire.org Git - thirdparty/shadow.git/commitdiff
libmisc/salt.c: Use crypt_gensalt(), if available in libcrypt. 357/head
authorBjörn Esser <besser82@fedoraproject.org>
Tue, 15 Jun 2021 12:23:42 +0000 (14:23 +0200)
committerBjörn Esser <besser82@fedoraproject.org>
Sun, 4 Jul 2021 11:01:22 +0000 (13:01 +0200)
Most Linux distributions, including Fedora and RHEL 8, are shipping
with libxcrypt >= 4.0.

Since that version of libxcrypt the provided family of crypt_gensalt()
functions are able to use automatic entropy drawn from secure system
ressources, like arc4random(), getentropy() or getrandom().

Anyways, the settings generated by crypt_gensalt() are always
guaranteed to works with the crypt() function.

Using crypt_gensalt() is also needed to make proper use of newer
hashing methods, like yescrypt, provided by libxcrypt.

Signed-off-by: Björn Esser <besser82@fedoraproject.org>
libmisc/salt.c

index 13408a537f1b2764918c52572342b1616843f2ec..9fd3433281eca578b5cc6557dfe17e5fe73d7054 100644 (file)
 #include "defines.h"
 #include "getdef.h"
 
+#if (defined CRYPT_GENSALT_IMPLEMENTS_AUTO_ENTROPY && \
+     CRYPT_GENSALT_IMPLEMENTS_AUTO_ENTROPY)
+#define USE_XCRYPT_GENSALT 1
+#else
+#define USE_XCRYPT_GENSALT 0
+#endif
+
 /* Add the salt prefix. */
 #define MAGNUM(array,ch)       (array)[0]=(array)[2]='$',(array)[1]=(ch),(array)[3]='\0'
 
 
 /* local function prototypes */
 static long read_random_bytes (void);
+#if !USE_XCRYPT_GENSALT
 static /*@observer@*/const char *gensalt (size_t salt_size);
+#endif /* !USE_XCRYPT_GENSALT */
 #if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT)
 static long shadow_random (long min, long max);
 #endif /* USE_SHA_CRYPT || USE_BCRYPT */
 #ifdef USE_SHA_CRYPT
-static /*@observer@*/void SHA_salt_rounds_to_buf (char *buf, /*@null@*/int *prefered_rounds);
+static /*@observer@*/const unsigned long SHA_get_salt_rounds (/*@null@*/int *prefered_rounds);
+static /*@observer@*/void SHA_salt_rounds_to_buf (char *buf, unsigned long rounds);
 #endif /* USE_SHA_CRYPT */
 #ifdef USE_BCRYPT
-static /*@observer@*/void BCRYPT_salt_rounds_to_buf (char *buf, /*@null@*/int *prefered_rounds);
+static /*@observer@*/const unsigned long BCRYPT_get_salt_rounds (/*@null@*/int *prefered_rounds);
+static /*@observer@*/void BCRYPT_salt_rounds_to_buf (char *buf, unsigned long rounds);
 #endif /* USE_BCRYPT */
 #ifdef USE_YESCRYPT
-static /*@observer@*/void YESCRYPT_salt_cost_to_buf (char *buf, /*@null@*/int *prefered_cost);
+static /*@observer@*/const unsigned long YESCRYPT_get_salt_cost (/*@null@*/int *prefered_cost);
+static /*@observer@*/void YESCRYPT_salt_cost_to_buf (char *buf, unsigned long cost);
 #endif /* USE_YESCRYPT */
 
-#ifndef HAVE_L64A
+#if !USE_XCRYPT_GENSALT && !defined(HAVE_L64A)
 static /*@observer@*/char *l64a (long value)
 {
        static char buf[8];
@@ -125,7 +137,7 @@ static /*@observer@*/char *l64a (long value)
 
        return buf;
 }
-#endif /* !HAVE_L64A */
+#endif /* !USE_XCRYPT_GENSALT && !defined(HAVE_L64A) */
 
 /* Read sizeof (long) random bytes from /dev/urandom. */
 static long read_random_bytes (void)
@@ -199,14 +211,10 @@ static long shadow_random (long min, long max)
 #endif /* USE_SHA_CRYPT || USE_BCRYPT */
 
 #ifdef USE_SHA_CRYPT
-/*
- * Fill a salt prefix specifying the rounds number for the SHA crypt methods
- * to a buffer.
- */
-static /*@observer@*/void SHA_salt_rounds_to_buf (char *buf, /*@null@*/int *prefered_rounds)
+/* Return the the rounds number for the SHA crypt methods. */
+static /*@observer@*/const unsigned long SHA_get_salt_rounds (/*@null@*/int *prefered_rounds)
 {
        unsigned long rounds;
-       const size_t buf_begin = strlen (buf);
 
        if (NULL == prefered_rounds) {
                long min_rounds = getdef_long ("SHA_CRYPT_MIN_ROUNDS", -1);
@@ -245,6 +253,17 @@ static /*@observer@*/void SHA_salt_rounds_to_buf (char *buf, /*@null@*/int *pref
                rounds = SHA_ROUNDS_MAX;
        }
 
+       return rounds;
+}
+
+/*
+ * Fill a salt prefix specifying the rounds number for the SHA crypt methods
+ * to a buffer.
+ */
+static /*@observer@*/void SHA_salt_rounds_to_buf (char *buf, unsigned long rounds)
+{
+       const size_t buf_begin = strlen (buf);
+
        /* Nothing to do here if SHA_ROUNDS_DEFAULT is used. */
        if (rounds == SHA_ROUNDS_DEFAULT) {
                return;
@@ -265,14 +284,10 @@ static /*@observer@*/void SHA_salt_rounds_to_buf (char *buf, /*@null@*/int *pref
 #endif /* USE_SHA_CRYPT */
 
 #ifdef USE_BCRYPT
-/*
- * Fill a salt prefix specifying the rounds number for the BCRYPT method
- * to a buffer.
- */
-static /*@observer@*/void BCRYPT_salt_rounds_to_buf (char *buf, /*@null@*/int *prefered_rounds)
+/* Return the the rounds number for the BCRYPT method. */
+static /*@observer@*/const unsigned long BCRYPT_get_salt_rounds (/*@null@*/int *prefered_rounds)
 {
        unsigned long rounds;
-       const size_t buf_begin = strlen (buf);
 
        if (NULL == prefered_rounds) {
                long min_rounds = getdef_long ("BCRYPT_MIN_ROUNDS", -1);
@@ -306,6 +321,11 @@ static /*@observer@*/void BCRYPT_salt_rounds_to_buf (char *buf, /*@null@*/int *p
                rounds = B_ROUNDS_MIN;
        }
 
+#if USE_XCRYPT_GENSALT
+       if (rounds > B_ROUNDS_MAX) {
+               rounds = B_ROUNDS_MAX;
+       }
+#else /* USE_XCRYPT_GENSALT */
        /*
         * Use 19 as an upper bound for now,
         * because musl doesn't allow rounds >= 20.
@@ -314,6 +334,18 @@ static /*@observer@*/void BCRYPT_salt_rounds_to_buf (char *buf, /*@null@*/int *p
                /* rounds = B_ROUNDS_MAX; */
                rounds = 19;
        }
+#endif /* USE_XCRYPT_GENSALT */
+
+       return rounds;
+}
+
+/*
+ * Fill a salt prefix specifying the rounds number for the BCRYPT method
+ * to a buffer.
+ */
+static /*@observer@*/void BCRYPT_salt_rounds_to_buf (char *buf, unsigned long rounds)
+{
+       const size_t buf_begin = strlen (buf);
 
        /*
         * Check if the result buffer is long enough.
@@ -330,14 +362,10 @@ static /*@observer@*/void BCRYPT_salt_rounds_to_buf (char *buf, /*@null@*/int *p
 #endif /* USE_BCRYPT */
 
 #ifdef USE_YESCRYPT
-/*
- * Fill a salt prefix specifying the cost for the YESCRYPT method
- * to a buffer.
- */
-static /*@observer@*/void YESCRYPT_salt_cost_to_buf (char *buf, /*@null@*/int *prefered_cost)
+/* Return the the cost number for the YESCRYPT method. */
+static /*@observer@*/const unsigned long YESCRYPT_get_salt_cost (/*@null@*/int *prefered_cost)
 {
        unsigned long cost;
-       const size_t buf_begin = strlen (buf);
 
        if (NULL == prefered_cost) {
                cost = getdef_num ("YESCRYPT_COST_FACTOR", Y_COST_DEFAULT);
@@ -356,6 +384,17 @@ static /*@observer@*/void YESCRYPT_salt_cost_to_buf (char *buf, /*@null@*/int *p
                cost = Y_COST_MAX;
        }
 
+       return cost;
+}
+
+/*
+ * Fill a salt prefix specifying the cost for the YESCRYPT method
+ * to a buffer.
+ */
+static /*@observer@*/void YESCRYPT_salt_cost_to_buf (char *buf, unsigned long cost)
+{
+       const size_t buf_begin = strlen (buf);
+
        /*
         * Check if the result buffer is long enough.
         * We are going to write four bytes,
@@ -380,6 +419,7 @@ static /*@observer@*/void YESCRYPT_salt_cost_to_buf (char *buf, /*@null@*/int *p
 }
 #endif /* USE_YESCRYPT */
 
+#if !USE_XCRYPT_GENSALT
 static /*@observer@*/const char *gensalt (size_t salt_size)
 {
        static char salt[MAX_SALT_SIZE + 6];
@@ -397,6 +437,7 @@ static /*@observer@*/const char *gensalt (size_t salt_size)
 
        return salt;
 }
+#endif /* !USE_XCRYPT_GENSALT */
 
 /*
  * Generate 8 base64 ASCII characters of random salt.  If MD5_CRYPT_ENAB
@@ -420,6 +461,7 @@ static /*@observer@*/const char *gensalt (size_t salt_size)
        static char result[GENSALT_SETTING_SIZE];
        size_t salt_len = MAX_SALT_SIZE;
        const char *method;
+       unsigned long rounds = 0;
 
        memset (result, '\0', GENSALT_SETTING_SIZE);
 
@@ -435,27 +477,32 @@ static /*@observer@*/const char *gensalt (size_t salt_size)
        if (0 == strcmp (method, "MD5")) {
                MAGNUM(result, '1');
                salt_len = MD5_CRYPT_SALT_SIZE;
+               rounds = 0;
 #ifdef USE_BCRYPT
        } else if (0 == strcmp (method, "BCRYPT")) {
                BCRYPTMAGNUM(result);
                salt_len = BCRYPT_SALT_SIZE;
-               BCRYPT_salt_rounds_to_buf (result, (int *) arg);
+               rounds = BCRYPT_get_salt_rounds ((int *) arg);
+               BCRYPT_salt_rounds_to_buf (result, rounds);
 #endif /* USE_BCRYPT */
 #ifdef USE_YESCRYPT
        } else if (0 == strcmp (method, "YESCRYPT")) {
                MAGNUM(result, 'y');
                salt_len = YESCRYPT_SALT_SIZE;
-               YESCRYPT_salt_cost_to_buf (result, (int *) arg);
+               rounds = YESCRYPT_get_salt_cost ((int *) arg);
+               YESCRYPT_salt_cost_to_buf (result, rounds);
 #endif /* USE_YESCRYPT */
 #ifdef USE_SHA_CRYPT
        } else if (0 == strcmp (method, "SHA256")) {
                MAGNUM(result, '5');
                salt_len = SHA_CRYPT_SALT_SIZE;
-               SHA_salt_rounds_to_buf (result, (int *) arg);
+               rounds = SHA_get_salt_rounds ((int *) arg);
+               SHA_salt_rounds_to_buf (result, rounds);
        } else if (0 == strcmp (method, "SHA512")) {
                MAGNUM(result, '6');
                salt_len = SHA_CRYPT_SALT_SIZE;
-               SHA_salt_rounds_to_buf (result, (int *) arg);
+               rounds = SHA_get_salt_rounds ((int *) arg);
+               SHA_salt_rounds_to_buf (result, rounds);
 #endif /* USE_SHA_CRYPT */
        } else if (0 != strcmp (method, "DES")) {
                fprintf (shadow_logfd,
@@ -463,9 +510,39 @@ static /*@observer@*/const char *gensalt (size_t salt_size)
                           "Defaulting to DES.\n"),
                         method);
                salt_len = MAX_SALT_SIZE;
+               rounds = 0;
                memset (result, '\0', GENSALT_SETTING_SIZE);
        }
 
+#if USE_XCRYPT_GENSALT
+       /*
+        * Prepare DES setting for crypt_gensalt(), if result
+        * has not been filled with anything previously.
+        */
+       if ('\0' == result[0]) {
+               /* Avoid -Wunused-but-set-variable. */
+               salt_len = GENSALT_SETTING_SIZE - 1;
+               rounds = 0;
+               memset (result, '.', salt_len);
+               result[salt_len] = '\0';
+       }
+
+       char *retval = crypt_gensalt (result, rounds, NULL, 0);
+
+       /* Should not happen, but... */
+       if (NULL == retval) {
+               fprintf (shadow_logfd,
+                        _("Unable to generate a salt from setting "
+                          "\"%s\", check your settings in "
+                          "ENCRYPT_METHOD and the corresponding "
+                          "configuration for your selected hash "
+                          "method.\n"), result);
+
+               exit (1);
+       }
+
+       return retval;
+#else /* USE_XCRYPT_GENSALT */
        /* Check if the result buffer is long enough. */
        assert (GENSALT_SETTING_SIZE > strlen (result) + salt_len);
 
@@ -474,4 +551,5 @@ static /*@observer@*/const char *gensalt (size_t salt_size)
                 GENSALT_SETTING_SIZE - strlen (result) - 1);
 
        return result;
+#endif /* USE_XCRYPT_GENSALT */
 }