From 0b695f5a762c59eb270c40faac6cbfdacd567a7f Mon Sep 17 00:00:00 2001 From: nekral-guest Date: Tue, 20 Nov 2007 09:33:52 +0000 Subject: [PATCH] * lib/prototypes.h, libmisc/salt.c: Add parameters to crypt_make_salt to force the crypt method and number of rounds. * libmisc/salt.c: Add parameter to SHA_salt_rounds to force the number of rounds. * libmisc/salt.c, lib/getdef.c: ENCRYPT_METHOD and MD5_CRYPT_ENAB are needed also when USE_PAM (e.g. for chpasswd). * src/newusers.c, src/gpasswd.c: Use the new crypt_make_salt prototype. * src/chpasswd.c, src/chgpasswd.c: Add option -c, --crypt-method and -s, --sha-rounds to specify the crypt method and number of rounds in case of one of the SHA methods. The new prototype of crypt_make_salt simplifies the handling of -m, --md5. --- ChangeLog | 14 +++++++ lib/getdef.c | 8 ++-- lib/prototypes.h | 2 +- libmisc/salt.c | 104 ++++++++++++++++++++++++++--------------------- src/chgpasswd.c | 89 ++++++++++++++++++++++++++++++++++------ src/chpasswd.c | 89 ++++++++++++++++++++++++++++++++++------ src/gpasswd.c | 2 +- src/newusers.c | 7 ++-- 8 files changed, 233 insertions(+), 82 deletions(-) diff --git a/ChangeLog b/ChangeLog index 82e1a769..cba2b88a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +2007-11-20 Nicolas François + + * lib/prototypes.h, libmisc/salt.c: Add parameters to + crypt_make_salt to force the crypt method and number of rounds. + * libmisc/salt.c: Add parameter to SHA_salt_rounds to force the + number of rounds. + * libmisc/salt.c, lib/getdef.c: ENCRYPT_METHOD and MD5_CRYPT_ENAB + are needed also when USE_PAM (e.g. for chpasswd). + * src/newusers.c, src/gpasswd.c: Use the new crypt_make_salt prototype + * src/chpasswd.c, src/chgpasswd.c: Add option -c, --crypt-method + and -s, --sha-rounds to specify the crypt method and number of + rounds in case of one of the SHA methods. The new prototype of + crypt_make_salt simplifies the handling of -m, --md5. + 2007-11-19 Nicolas François * libmisc/salt.c: The salt has a random size (between 8 and 16 diff --git a/lib/getdef.c b/lib/getdef.c index 7b8a798a..df474c81 100644 --- a/lib/getdef.c +++ b/lib/getdef.c @@ -52,6 +52,7 @@ static struct itemdef def_table[] = { {"CONSOLE", NULL}, {"CREATE_HOME", NULL}, {"DEFAULT_HOME", NULL}, + {"ENCRYPT_METHOD", NULL}, {"ENV_PATH", NULL}, {"ENV_SUPATH", NULL}, {"ERASECHAR", NULL}, @@ -67,9 +68,12 @@ static struct itemdef def_table[] = { {"LOG_UNKFAIL_ENAB", NULL}, {"MAIL_DIR", NULL}, {"MAIL_FILE", NULL}, + {"MD5_CRYPT_ENAB", NULL}, {"PASS_MAX_DAYS", NULL}, {"PASS_MIN_DAYS", NULL}, {"PASS_WARN_AGE", NULL}, + {"SHA_CRYPT_MAX_ROUNDS", NULL}, + {"SHA_CRYPT_MIN_ROUNDS", NULL}, {"SULOG_FILE", NULL}, {"SU_NAME", NULL}, {"TTYGROUP", NULL}, @@ -84,7 +88,6 @@ static struct itemdef def_table[] = { {"CHFN_AUTH", NULL}, {"CHSH_AUTH", NULL}, {"CRACKLIB_DICTPATH", NULL}, - {"ENCRYPT_METHOD", NULL}, {"ENV_HZ", NULL}, {"ENVIRON_FILE", NULL}, {"ENV_TZ", NULL}, @@ -94,7 +97,6 @@ static struct itemdef def_table[] = { {"LASTLOG_ENAB", NULL}, {"LOGIN_STRING", NULL}, {"MAIL_CHECK_ENAB", NULL}, - {"MD5_CRYPT_ENAB", NULL}, {"MOTD_FILE", NULL}, {"NOLOGINS_FILE", NULL}, {"OBSCURE_CHECKS_ENAB", NULL}, @@ -104,8 +106,6 @@ static struct itemdef def_table[] = { {"PASS_MIN_LEN", NULL}, {"PORTTIME_CHECKS_ENAB", NULL}, {"QUOTAS_ENAB", NULL}, - {"SHA_CRYPT_MAX_ROUNDS", NULL}, - {"SHA_CRYPT_MIN_ROUNDS", NULL}, {"SU_WHEEL_ONLY", NULL}, {"ULIMIT", NULL}, #endif diff --git a/lib/prototypes.h b/lib/prototypes.h index e18d71e8..86c4a7c8 100644 --- a/lib/prototypes.h +++ b/lib/prototypes.h @@ -132,7 +132,7 @@ extern void pwd_init (void); extern int do_rlogin (const char *, char *, int, char *, int); /* salt.c */ -extern char *crypt_make_salt (void); +extern char *crypt_make_salt (char *meth, void *arg); /* setugid.c */ extern int setup_groups (const struct passwd *); diff --git a/libmisc/salt.c b/libmisc/salt.c index ec3de46c..8ee0dcbb 100644 --- a/libmisc/salt.c +++ b/libmisc/salt.c @@ -85,29 +85,33 @@ static unsigned int SHA_salt_size (void) /* * Return a salt prefix specifying the rounds number for the SHA crypt methods. */ -static char *SHA_salt_rounds (void) +static char *SHA_salt_rounds (int *prefered_rounds) { - static char *rounds_prefix[18]; - long min_rounds = getdef_long ("SHA_CRYPT_MIN_ROUNDS", -1); - long max_rounds = getdef_long ("SHA_CRYPT_MAX_ROUNDS", -1); + static char rounds_prefix[18]; long rounds; - if (-1 == min_rounds && -1 == max_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); + + 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)); + rounds = min_rounds + + (double)rand () * (max_rounds-min_rounds+1)/RAND_MAX; + } else if (0 == *prefered_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)); - rounds = min_rounds + - (double)rand () * (max_rounds-min_rounds+1)/RAND_MAX; - /* Sanity checks. The libc should also check this, but this * protects against a rounds_prefix overflow. */ if (rounds < ROUNDS_MIN) @@ -133,8 +137,16 @@ static char *SHA_salt_rounds (void) * (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 (void) +char *crypt_make_salt (char *meth, void *arg) { struct timeval tv; /* Max result size for the SHA methods: @@ -145,41 +157,39 @@ char *crypt_make_salt (void) */ static char result[40]; int max_salt_len = 8; - char *method; + char *method = "DES"; result[0] = '\0'; -#ifndef USE_PAM + if (NULL != meth) + method = meth; + else #ifdef ENCRYPTMETHOD_SELECT - if ((method = getdef_str ("ENCRYPT_METHOD")) == NULL) { + if ((method = getdef_str ("ENCRYPT_METHOD")) == NULL) #endif - if (getdef_bool ("MD5_CRYPT_ENAB")) { - MAGNUM(result,'1'); - max_salt_len = 11; - } + if (getdef_bool ("MD5_CRYPT_ENAB")) + method = "MD5"; + + if (!strncmp (method, "MD5", 3)) { + MAGNUM(result, '1'); + max_salt_len = 11; #ifdef ENCRYPTMETHOD_SELECT - } else { - if (!strncmp (method, "MD5", 3)) { - MAGNUM(result, '1'); - max_salt_len = 11; - } else if (!strncmp (method, "SHA256", 6)) { - MAGNUM(result, '5'); - strcat(result, SHA_salt_rounds()); - max_salt_len = strlen(result) + SHA_salt_size(); - } else if (!strncmp (method, "SHA512", 6)) { - MAGNUM(result, '6'); - strcat(result, SHA_salt_rounds()); - max_salt_len = strlen(result) + SHA_salt_size(); - } else if (0 != strncmp (method, "DES", 3)) { - fprintf (stderr, - _("Invalid ENCRYPT_METHOD value: '%s'.\n" - "Defaulting to DES.\n"), - method); - result[0] = '\0'; - } + } else if (!strncmp (method, "SHA256", 6)) { + MAGNUM(result, '5'); + strcat(result, SHA_salt_rounds((int *)arg)); + max_salt_len = strlen(result) + SHA_salt_size(); + } else if (!strncmp (method, "SHA512", 6)) { + MAGNUM(result, '6'); + strcat(result, SHA_salt_rounds((int *)arg)); + max_salt_len = strlen(result) + SHA_salt_size(); +#endif + } else if (0 != strncmp (method, "DES", 3)) { + fprintf (stderr, + _("Invalid ENCRYPT_METHOD value: '%s'.\n" + "Defaulting to DES.\n"), + method); + result[0] = '\0'; } -#endif /* ENCRYPTMETHOD_SELECT */ -#endif /* USE_PAM */ /* * Concatenate a pseudo random salt. diff --git a/src/chgpasswd.c b/src/chgpasswd.c index 185d7ad0..e1f95db1 100644 --- a/src/chgpasswd.c +++ b/src/chgpasswd.c @@ -50,8 +50,13 @@ * Global variables */ static char *Prog; +static int cflg = 0; static int eflg = 0; static int md5flg = 0; +static int sflg = 0; + +static char *crypt_method = NULL; +static int sha_rounds = 5000; #ifdef SHADOWGRP static int is_shadow_grp; @@ -68,14 +73,36 @@ static void usage (void) fprintf (stderr, _("Usage: chgpasswd [options]\n" "\n" "Options:\n" + " -c, --crypt-method the crypt method (one of %s)\n" " -e, --encrypted supplied passwords are encrypted\n" " -h, --help display this help message and exit\n" " -m, --md5 use MD5 encryption instead DES when the supplied\n" " passwords are not encrypted\n" - "\n")); + "\n"), +#ifndef ENCRYPTMETHOD_SELECT + "DES MD5" +#else + "DES MD5 SHA256 SHA512" +#endif + ); exit (1); } +static long getnumber (const char *numstr) +{ + long val; + char *errptr; + + val = strtol (numstr, &errptr, 10); + if (*errptr || errno == ERANGE) { + fprintf (stderr, _("%s: invalid numeric argument '%s'\n"), Prog, + numstr); + exit (1); + } + + return val; +} + int main (int argc, char **argv) { char buf[BUFSIZ]; @@ -111,16 +138,22 @@ int main (int argc, char **argv) int option_index = 0; int c; static struct option long_options[] = { + {"crypt-method", required_argument, NULL, 'c'}, {"encrypted", no_argument, NULL, 'e'}, {"help", no_argument, NULL, 'h'}, {"md5", no_argument, NULL, 'm'}, + {"sha-rounds", required_argument, NULL, 's'}, {NULL, 0, NULL, '\0'} }; while ((c = - getopt_long (argc, argv, "ehm", long_options, + getopt_long (argc, argv, "c:ehms:", long_options, &option_index)) != -1) { switch (c) { + case 'c': + cflg = 1; + crypt_method = optarg; + break; case 'e': eflg = 1; break; @@ -130,6 +163,10 @@ int main (int argc, char **argv) case 'm': md5flg = 1; break; + case 's': + sflg = 1; + sha_rounds = getnumber(optarg); + break; case 0: /* long option */ break; @@ -140,6 +177,34 @@ int main (int argc, char **argv) } } + /* validate options */ + if (sflg && !cflg) { + fprintf (stderr, + _("%s: %s flag is ONLY allowed with the %s flag\n"), + Prog, "-s", "-c"); + usage (); + } + if (md5flg && cflg) { + fprintf (stderr, + _("%s: the -m and -c flags are exclusive\n"), + Prog); + usage (); + } + if (cflg) { + if (0 != strcmp (method, "DES") && + 0 != strcmp (method, "MD5") && +#ifdef ENCRYPTMETHOD_SELECT + 0 != strcmp (method, "SHA256") && + 0 != strcmp (method, "SHA512") +#endif + ) { + fprintf (stderr, + _("%s: unsupported crypt method: %s\n"), + Prog, method); + usage (); + } + } + #ifdef USE_PAM retval = PAM_SUCCESS; @@ -245,18 +310,16 @@ int main (int argc, char **argv) } newpwd = cp; if (!eflg) { - if (md5flg) { - char md5salt[12] = "$1$"; - char *salt = crypt_make_salt (); - - if (strncmp (salt, "$1$", 3) == 0) { - strncpy (md5salt, salt, 11); - } else { - strncat (md5salt, salt, 8); - } - cp = pw_encrypt (newpwd, md5salt); + void *arg = NULL; + if (md5flg) + crypt_method = "MD5"; + else if (crypt_method != NULL) { + if (sflg) + arg = &sha_rounds; } else - cp = pw_encrypt (newpwd, crypt_make_salt ()); + crypt_method = "DES"; + cp = pw_encrypt (newpwd, + crypt_make_salt(crypt_method, arg)); } /* diff --git a/src/chpasswd.c b/src/chpasswd.c index c8b00607..f4fe6067 100644 --- a/src/chpasswd.c +++ b/src/chpasswd.c @@ -49,8 +49,13 @@ * Global variables */ static char *Prog; +static int cflg = 0; static int eflg = 0; static int md5flg = 0; +static int sflg = 0; + +static char *crypt_method = NULL; +static int sha_rounds = 5000; static int is_shadow_pwd; @@ -65,14 +70,36 @@ static void usage (void) fprintf (stderr, _("Usage: chpasswd [options]\n" "\n" "Options:\n" + " -c, --crypt-method the crypt method (one of %s)\n" " -e, --encrypted supplied passwords are encrypted\n" " -h, --help display this help message and exit\n" " -m, --md5 use MD5 encryption instead DES when the supplied\n" " passwords are not encrypted\n" - "\n")); + "\n"), +#ifndef ENCRYPTMETHOD_SELECT + "DES MD5" +#else + "DES MD5 SHA256 SHA512" +#endif + ); exit (E_USAGE); } +static long getnumber (const char *numstr) +{ + long val; + char *errptr; + + val = strtol (numstr, &errptr, 10); + if (*errptr || errno == ERANGE) { + fprintf (stderr, _("%s: invalid numeric argument '%s'\n"), Prog, + numstr); + exit (1); + } + + return val; +} + int main (int argc, char **argv) { char buf[BUFSIZ]; @@ -105,16 +132,22 @@ int main (int argc, char **argv) int option_index = 0; int c; static struct option long_options[] = { + {"crypt-method", required_argument, NULL, 'c'}, {"encrypted", no_argument, NULL, 'e'}, {"help", no_argument, NULL, 'h'}, {"md5", no_argument, NULL, 'm'}, + {"sha-rounds", required_argument, NULL, 's'}, {NULL, 0, NULL, '\0'} }; while ((c = - getopt_long (argc, argv, "ehm", long_options, + getopt_long (argc, argv, "c:ehms:", long_options, &option_index)) != -1) { switch (c) { + case 'c': + cflg = 1; + crypt_method = optarg; + break; case 'e': eflg = 1; break; @@ -124,6 +157,10 @@ int main (int argc, char **argv) case 'm': md5flg = 1; break; + case 's': + sflg = 1; + sha_rounds = getnumber(optarg); + break; case 0: /* long option */ break; @@ -134,6 +171,34 @@ int main (int argc, char **argv) } } + /* validate options */ + if (sflg && !cflg) { + fprintf (stderr, + _("%s: %s flag is ONLY allowed with the %s flag\n"), + Prog, "-s", "-c"); + usage (); + } + if (md5flg && cflg) { + fprintf (stderr, + _("%s: the -m and -c flags are exclusive\n"), + Prog); + usage (); + } + if (cflg) { + if (0 != strcmp (method, "DES") && + 0 != strcmp (method, "MD5") && +#ifdef ENCRYPTMETHOD_SELECT + 0 != strcmp (method, "SHA256") && + 0 != strcmp (method, "SHA512") +#endif + ) { + fprintf (stderr, + _("%s: unsupported crypt method: %s\n"), + Prog, method); + usage (); + } + } + #ifdef USE_PAM retval = PAM_SUCCESS; @@ -241,18 +306,16 @@ int main (int argc, char **argv) } newpwd = cp; if (!eflg) { - if (md5flg) { - char md5salt[12] = "$1$"; - char *salt = crypt_make_salt (); - - if (strncmp (salt, "$1$", 3) == 0) { - strncpy (md5salt, salt, 11); - } else { - strncat (md5salt, salt, 8); - } - cp = pw_encrypt (newpwd, salt); + void *arg = NULL; + if (md5flg) + crypt_method = "MD5"; + else if (crypt_method != NULL) { + if (sflg) + arg = &sha_rounds; } else - cp = pw_encrypt (newpwd, crypt_make_salt ()); + crypt_method = "DES"; + cp = pw_encrypt (newpwd, + crypt_make_salt(crypt_method, arg)); } /* diff --git a/src/gpasswd.c b/src/gpasswd.c index f8f6832d..67a656ee 100644 --- a/src/gpasswd.c +++ b/src/gpasswd.c @@ -607,7 +607,7 @@ int main (int argc, char **argv) exit (1); } - cp = pw_encrypt (pass, crypt_make_salt ()); + cp = pw_encrypt (pass, crypt_make_salt (NULL, NULL)); memzero (pass, sizeof pass); #ifdef SHADOWGRP if (is_shadowgrp) diff --git a/src/newusers.c b/src/newusers.c index ce27fb9c..15af7c91 100644 --- a/src/newusers.c +++ b/src/newusers.c @@ -214,7 +214,7 @@ static int add_user (const char *name, const char *uid, uid_t * nuid, gid_t gid) static void update_passwd (struct passwd *pwd, const char *passwd) { - pwd->pw_passwd = pw_encrypt (passwd, crypt_make_salt ()); + pwd->pw_passwd = pw_encrypt (passwd, crypt_make_salt (NULL, NULL)); } /* @@ -241,7 +241,8 @@ static int add_passwd (struct passwd *pwd, const char *passwd) */ if ((sp = spw_locate (pwd->pw_name))) { spent = *sp; - spent.sp_pwdp = pw_encrypt (passwd, crypt_make_salt ()); + spent.sp_pwdp = pw_encrypt (passwd, + crypt_make_salt (NULL, NULL)); return !spw_update (&spent); } @@ -261,7 +262,7 @@ static int add_passwd (struct passwd *pwd, const char *passwd) * shadow password file entry. */ spent.sp_namp = pwd->pw_name; - spent.sp_pwdp = pw_encrypt (passwd, crypt_make_salt ()); + spent.sp_pwdp = pw_encrypt (passwd, crypt_make_salt (NULL, NULL)); spent.sp_lstchg = time ((time_t *) 0) / SCALE; spent.sp_min = getdef_num ("PASS_MIN_DAYS", 0); /* 10000 is infinity this week */