/* * salt.c - generate a random salt string for crypt() * * Written by Marek Michalkiewicz , * public domain. * * l64a was Written by J.T. Conklin . Public domain. */ #include #ident "$Id$" #include #include #include #include "prototypes.h" #include "defines.h" #include "getdef.h" /* local function prototypes */ #ifndef HAVE_L64A char *l64a(long value); #endif static char *gensalt (unsigned int salt_size); #ifdef USE_SHA_CRYPT static unsigned int SHA_salt_size (void); static const char *SHA_salt_rounds (int *prefered_rounds); #endif #ifndef HAVE_L64A static char *l64a(long value) { static char buf[8]; char *s = buf; int digit; int i; if (value < 0) { errno = EINVAL; return(NULL); } for (i = 0; value != 0 && i < 6; i++) { digit = value & 0x3f; if (digit < 2) *s = digit + '.'; else if (digit < 12) *s = digit + '0' - 2; else if (digit < 38) *s = digit + 'A' - 12; else *s = digit + 'a' - 38; value >>= 6; s++; } *s = '\0'; return(buf); } #endif /* !HAVE_L64A */ /* * Add the salt prefix. */ #define MAGNUM(array,ch) (array)[0]=(array)[2]='$',(array)[1]=(ch),(array)[3]='\0' #ifdef USE_SHA_CRYPT /* * Return the salt size. * The size of the salt string is between 8 and 16 bytes for the SHA crypt * methods. */ static unsigned int SHA_salt_size (void) { double rand_rounds = 9 * random (); rand_rounds /= RAND_MAX; return 8 + rand_rounds; } /* ! Arguments evaluated twice ! */ #define MAX(x,y) ((x) > (y) ? (x) : (y)) #define MIN(x,y) ((x) < (y) ? (x) : (y)) /* Default number of rounds if not explicitly specified. */ #define ROUNDS_DEFAULT 5000 /* Minimum number of rounds. */ #define ROUNDS_MIN 1000 /* Maximum number of rounds. */ #define ROUNDS_MAX 999999999 /* * Return a salt prefix specifying the rounds number for the SHA crypt methods. */ static const char *SHA_salt_rounds (int *prefered_rounds) { static char rounds_prefix[18]; long rounds; if (NULL == prefered_rounds) { long min_rounds = getdef_long ("SHA_CRYPT_MIN_ROUNDS", -1); long max_rounds = getdef_long ("SHA_CRYPT_MAX_ROUNDS", -1); double rand_rounds; if (-1 == min_rounds && -1 == max_rounds) return ""; if (-1 == min_rounds) min_rounds = max_rounds; if (-1 == max_rounds) max_rounds = min_rounds; if (min_rounds > max_rounds) max_rounds = min_rounds; srand (time (NULL)); rand_rounds = (max_rounds-min_rounds+1) * random (); rand_rounds /= RAND_MAX; rounds = min_rounds + rand_rounds; } else if (0 == *prefered_rounds) return ""; else rounds = *prefered_rounds; /* Sanity checks. The libc should also check this, but this * protects against a rounds_prefix overflow. */ if (rounds < ROUNDS_MIN) rounds = ROUNDS_MIN; if (rounds > ROUNDS_MAX) rounds = ROUNDS_MAX; snprintf (rounds_prefix, 18, "rounds=%ld$", rounds); /* Sanity checks. That should not be necessary. */ rounds_prefix[17] = '\0'; if ('$' != rounds_prefix[16]) rounds_prefix[17] = '$'; return rounds_prefix; } #endif /* * Generate salt of size salt_size. */ #define MAX_SALT_SIZE 16 #define MIN_SALT_SIZE 8 static char *gensalt (unsigned int salt_size) { static char salt[32]; salt[0] = '\0'; assert (salt_size >= MIN_SALT_SIZE && salt_size <= MAX_SALT_SIZE); srandom ((unsigned int)time(NULL)); strcat (salt, l64a (random())); do { strcat (salt, l64a (random())); } while (strlen (salt) < salt_size); salt[salt_size] = '\0'; return salt; } /* * Generate 8 base64 ASCII characters of random salt. If MD5_CRYPT_ENAB * in /etc/login.defs is "yes", the salt string will be prefixed by "$1$" * (magic) and pw_encrypt() will execute the MD5-based FreeBSD-compatible * version of crypt() instead of the standard one. * Other methods can be set with ENCRYPT_METHOD * * The method can be forced with the meth parameter. * If NULL, the method will be defined according to the MD5_CRYPT_ENAB and * ENCRYPT_METHOD login.defs variables. * * If meth is specified, an additional parameter can be provided. * * For the SHA256 and SHA512 method, this specifies the number of rounds * (if not NULL). */ char *crypt_make_salt (const char *meth, void *arg) { /* Max result size for the SHA methods: * +3 $5$ * +17 rounds=999999999$ * +16 salt * +1 \0 */ static char result[40]; size_t salt_len = 8; const char *method; result[0] = '\0'; if (NULL != meth) method = meth; else { if ((method = getdef_str ("ENCRYPT_METHOD")) == NULL) method = getdef_bool ("MD5_CRYPT_ENAB") ? "MD5" : "DES"; } if (!strcmp (method, "MD5")) { MAGNUM(result, '1'); #ifdef USE_SHA_CRYPT } else if (!strcmp (method, "SHA256")) { MAGNUM(result, '5'); strcat(result, SHA_salt_rounds((int *)arg)); salt_len = SHA_salt_size(); } else if (!strcmp (method, "SHA512")) { MAGNUM(result, '6'); strcat(result, SHA_salt_rounds((int *)arg)); salt_len = SHA_salt_size(); #endif } else if (0 != strcmp (method, "DES")) { fprintf (stderr, _("Invalid ENCRYPT_METHOD value: '%s'.\n" "Defaulting to DES.\n"), method); result[0] = '\0'; } /* * Concatenate a pseudo random salt. */ assert (sizeof (result) > strlen (result) + salt_len); strncat (result, gensalt (salt_len), sizeof (result) - strlen (result) - 1); return result; }