From: Remco Rijnders Date: Sat, 7 Mar 2026 17:17:36 +0000 (-0500) Subject: Switch password hashing from DES crypt to SHA-256 X-Git-Url: http://git.serene-ircd.net/?a=commitdiff_plain;ds=sidebyside;p=serene-ircd.git Switch password hashing from DES crypt to SHA-256 DES crypt is trivially crackable. Passwords starting with $ are now auto-detected as crypt hashes (supports $5$ SHA-256, $6$ SHA-512, etc). Plaintext passwords still work for backwards compatibility. Remove crypt_oper_password and crypt_iline_password config options — detection is now automatic based on the $ prefix in stored passwords. Update mkpasswd to generate SHA-256 ($5$) hashes with random salts. Stop logging passwords (plaintext or hashed) in oper logs. Co-Authored-By: Claude Opus 4.6 --- diff --git a/doc/example.conf b/doc/example.conf index 09829d0..d5ec503 100644 --- a/doc/example.conf +++ b/doc/example.conf @@ -148,7 +148,8 @@ listen { # Defines who has Oper access on the server. # # host - Address mask the oper must connect from. -# password - Oper password (use mkpasswd if compiled with encryption). +# password - Oper password. Use mkpasswd to generate a SHA-256 hash, +# or use plaintext. Hashed passwords start with $5$. # name - Oper nickname. # flags - Access flags (see below). # class - Connection class number. @@ -232,15 +233,11 @@ uworld { # hub - Enable hub mode (accepts multiple server links). # throttle - Enable clone/throttle detection (yes/no). # seeuserstats - Show user stat notifications to opers (yes/no). -# crypt_oper_password - Oper passwords are encrypted (yes/no). -# crypt_iline_password - I-line passwords are encrypted (yes/no). # general { # hub yes; throttle yes; seeuserstats yes; - crypt_oper_password yes; - crypt_iline_password yes; }; # diff --git a/src/crypt/mkpasswd.c b/src/crypt/mkpasswd.c index eb6a855..a85d648 100755 --- a/src/crypt/mkpasswd.c +++ b/src/crypt/mkpasswd.c @@ -1,40 +1,44 @@ -/* simple password generator by Nelson Minar (minar@reed.edu) - * copyright 1991, all rights reserved. - * You can use this code as long as my name stays with it. +/* mkpasswd - generate SHA-256 password hashes for ircd.conf + * + * Originally by Nelson Minar (minar@reed.edu), updated for SHA-256. + * Usage: mkpasswd [salt] + * Without arguments, generates a random salt. + * With an argument, uses it as the salt. */ #include #include #include +#include +#include -extern char *getpass(); - -int main(argc, argv) -int argc; -char *argv[]; +int main(int argc, char *argv[]) { - static char saltChars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./"; - char salt[3]; - char * plaintext; - int i; - - if (argc < 2) { - srandom(time(0)); /* may not be the BEST salt, but its close */ - salt[0] = saltChars[random() % 64]; - salt[1] = saltChars[random() % 64]; - salt[2] = 0; - } - else { - salt[0] = argv[1][0]; - salt[1] = argv[1][1]; - salt[2] = '\0'; - if ((strchr(saltChars, salt[0]) == NULL) || (strchr(saltChars, salt[1]) == NULL)) - fprintf(stderr, "illegal salt %s\n", salt), exit(1); - } + static const char saltchars[] = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./"; + char salt[24]; + char *plaintext; + char *hash; + int i; - plaintext = getpass("plaintext: "); + /* Build $5$........$ salt (SHA-256) */ + strcpy(salt, "$5$"); + if (argc >= 2) { + strncat(salt, argv[1], 16); + } else { + srandom(time(NULL) ^ getpid()); + for (i = 0; i < 16; i++) + salt[3 + i] = saltchars[random() % 64]; + salt[19] = '\0'; + } + strcat(salt, "$"); - printf("%s\n", crypt(plaintext, salt)); - return 0; + plaintext = getpass("plaintext: "); + hash = crypt(plaintext, salt); + if (!hash || hash[0] == '*') { + fprintf(stderr, "crypt() failed — SHA-256 may not be supported\n"); + return 1; + } + printf("%s\n", hash); + return 0; } - diff --git a/src/s_user.c b/src/s_user.c index b5bdeb6..3d2f241 100644 --- a/src/s_user.c +++ b/src/s_user.c @@ -472,7 +472,6 @@ static int register_user (aClient *cptr, aClient *sptr, char *nick, char *userna anUser *user = sptr->user; aClient *nsptr; int i; - char salt[3]; extern char *crypt (); user->last = time (NULL); @@ -581,23 +580,15 @@ static int register_user (aClient *cptr, aClient *sptr, char *nick, char *userna u1 = NULL; if (!BadPtr (aconf->passwd)) { - if (cfg_crypt_iline_password) { - /* use first two chars of the password they send in as salt */ - /* passwd may be NULL. Head it off at the pass... */ - salt[0] = '\0'; - if (sptr->passwd && aconf->passwd && aconf->passwd[0] && - aconf->passwd[1]) { - salt[0] = aconf->passwd[0]; - salt[1] = aconf->passwd[1]; - salt[2] = '\0'; - encr = crypt (sptr->passwd, salt); - } - else - encr = ""; - } else - encr = sptr->passwd; + /* If stored password starts with $, it's a crypt hash — + * use it as the salt so crypt auto-detects the algorithm. + * Otherwise compare plaintext. */ + if (sptr->passwd && aconf->passwd[0] == '$') + encr = crypt (sptr->passwd, aconf->passwd); + else + encr = sptr->passwd ? sptr->passwd : ""; - if (!StrEq (encr, aconf->passwd)) { + if (!encr || !StrEq (encr, aconf->passwd)) { ircstp->is_ref++; sendto_one (sptr, err_str (ERR_PASSWDMISMATCH), me.name, parv[0]); @@ -2690,7 +2681,6 @@ int m_oper (aClient *cptr, aClient *sptr, int parc, char *parv[]) { aConfItem *aconf; char *name, *password, *encr; - char salt[3]; extern char *crypt (); if (check_registered_user (sptr)) @@ -2743,23 +2733,16 @@ int m_oper (aClient *cptr, aClient *sptr, int parc, char *parv[]) sptr->since += 7; return 0; } - if (cfg_crypt_oper_password) { - /* use first two chars of the password they send in as salt */ - /* passwd may be NULL. Head it off at the pass... */ - salt[0] = '\0'; - if (password && aconf->passwd && aconf->passwd[0] && aconf->passwd[1]) { - salt[0] = aconf->passwd[0]; - salt[1] = aconf->passwd[1]; - salt[2] = '\0'; - encr = crypt (password, salt); - } - else - encr = ""; - } else - encr = password; + /* If stored password starts with $, it's a crypt hash — + * use it as the salt so crypt auto-detects the algorithm. + * Otherwise compare plaintext. */ + if (password && aconf->passwd && aconf->passwd[0] == '$') + encr = crypt (password, aconf->passwd); + else + encr = password ? password : ""; if ((aconf->status & CONF_OPS) && - StrEq (encr, aconf->passwd) && !attach_conf (sptr, aconf)) { + encr && StrEq (encr, aconf->passwd) && !attach_conf (sptr, aconf)) { int old = (sptr->umodes & ALL_UMODES); char *s; @@ -2780,11 +2763,9 @@ int m_oper (aClient *cptr, aClient *sptr, int parc, char *parv[]) calc_mask(sptr) ; (void) m_opermotd (sptr, sptr, 1, parv); - if (!cfg_crypt_oper_password) - encr = ""; #if defined(USE_SYSLOG) && defined(SYSLOG_OPER) - syslog (LOG_INFO, "OPER (%s) (%s) by (%s!%s@%s)", - name, encr, parv[0], sptr->user->username, sptr->sockhost); + syslog (LOG_INFO, "OPER (%s) by (%s!%s@%s)", + name, parv[0], sptr->user->username, sptr->sockhost); #endif #ifdef FNAME_OPERLOG { @@ -2800,8 +2781,8 @@ int m_oper (aClient *cptr, aClient *sptr, int parc, char *parv[]) */ if (IsPerson (sptr) && (logfile = open (FNAME_OPERLOG, O_WRONLY | O_APPEND)) != -1) { - (void) sprintf (buf, "%s OPER (%s) (%s) by (%s!%s@%s)\n", - myctime (time (NULL)), name, encr, + (void) sprintf (buf, "%s OPER (%s) by (%s!%s@%s)\n", + myctime (time (NULL)), name, parv[0], sptr->user->username, sptr->sockhost); (void) write (logfile, buf, strlen (buf));