From d7fa75bbd22a08b4e0b8c7e3ccab588c87d23835 Mon Sep 17 00:00:00 2001
From: root <root@localhost.localdomain>
Date: Wed, 29 Dec 2021 16:05:56 +0800
Subject: [PATCH] shadow add sm3 crypt support

---
 configure.ac    |  9 +++++
 etc/login.defs  | 17 +++++++++
 lib/encrypt.c   |  3 ++
 lib/getdef.c    |  4 ++
 lib/obscure.c   |  3 ++
 lib/salt.c      | 98 ++++++++++++++++++++++++++++++++++++++++++++++++-
 src/chgpasswd.c | 48 +++++++++++++++++-------
 src/chpasswd.c  | 44 ++++++++++++++++------
 src/newusers.c  | 59 +++++++++++++++++++++--------
 src/passwd.c    |  7 +++-
 10 files changed, 248 insertions(+), 44 deletions(-)

diff --git a/configure.ac b/configure.ac
index 5dcd22e..c9cbbf7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -249,6 +249,9 @@ AC_ARG_WITH(libcrack,
 AC_ARG_WITH(sha-crypt,
 	[AS_HELP_STRING([--with-sha-crypt], [allow the SHA256 and SHA512 password encryption algorithms @<:@default=yes@:>@])],
 	[with_sha_crypt=$withval], [with_sha_crypt=yes])
+AC_ARG_WITH(sm3-crypt,
+	[AS_HELP_STRING([--with-sm3-crypt], [allow the SM3 password encryption algorithms @<:@default=yes@:>@])],
+	[with_sm3_crypt=$withval], [with_sm3_crypt=yes])
 AC_ARG_WITH(bcrypt,
 	[AS_HELP_STRING([--with-bcrypt], [allow the bcrypt password encryption algorithm @<:@default=no@:>@])],
 	[with_bcrypt=$withval], [with_bcrypt=no])
@@ -285,6 +288,11 @@ if test "$with_sha_crypt" = "yes"; then
 	AC_DEFINE(USE_SHA_CRYPT, 1, [Define to allow the SHA256 and SHA512 password encryption algorithms])
 fi
 
+AM_CONDITIONAL(USE_SM3_CRYPT, test "x$with_sm3_crypt" = "xyes")
+if test "$with_sm3_crypt" = "yes"; then
+	AC_DEFINE(USE_SM3_CRYPT, 1, [Define to allow the SM3 password encryption algorithms])
+fi
+
 AM_CONDITIONAL(USE_BCRYPT, test "x$with_bcrypt" = "xyes")
 if test "$with_bcrypt" = "yes"; then
 	AC_DEFINE(USE_BCRYPT, 1, [Define to allow the bcrypt password encryption algorithm])
@@ -782,6 +790,7 @@ echo "	tcb support (incomplete):	$with_tcb"
 echo "	shadow group support:		$enable_shadowgrp"
 echo "	S/Key support:			$with_skey"
 echo "	SHA passwords encryption:	$with_sha_crypt"
+echo "	SM3 passwords encryption:	$with_sm3_crypt"
 echo "	bcrypt passwords encryption:	$with_bcrypt"
 echo "	yescrypt passwords encryption:	$with_yescrypt"
 echo "	nscd support:			$with_nscd"
diff --git a/etc/login.defs b/etc/login.defs
index 114dbcd..fd310b7 100644
--- a/etc/login.defs
+++ b/etc/login.defs
@@ -353,6 +353,23 @@ CHFN_RESTRICT		rwh
 #SHA_CRYPT_MIN_ROUNDS 5000
 #SHA_CRYPT_MAX_ROUNDS 5000
 
+#
+# Only works if ENCRYPT_METHOD is set to SM3.
+#
+# Define the number of SM3 rounds.
+# With a lot of rounds, it is more difficult to brute-force the password.
+# However, more CPU resources will be needed to authenticate users if
+# this value is increased.
+#
+# If not specified, the libc will choose the default number of rounds (5000),
+# which is orders of magnitude too low for modern hardware.
+# The values must be within the 1000-999999999 range.
+# If only one of the MIN or MAX values is set, then this value will be used.
+# If MIN > MAX, the highest value will be used.
+#
+#SM3_CRYPT_MAX_ROUNDS 5000
+#SM3_CRYPT_MIN_ROUNDS 5000
+
 #
 # Only works if ENCRYPT_METHOD is set to BCRYPT.
 #
diff --git a/lib/encrypt.c b/lib/encrypt.c
index c84a255..11b301b 100644
--- a/lib/encrypt.c
+++ b/lib/encrypt.c
@@ -52,6 +52,9 @@
 			case '6':
 				method = "SHA512";
 				break;
+			case 's':   // salt = $sm3$...
+				method = "SM3";
+				break;
 			case 'y':
 				method = "YESCRYPT";
 				break;
diff --git a/lib/getdef.c b/lib/getdef.c
index 7fe6cef..7314399 100644
--- a/lib/getdef.c
+++ b/lib/getdef.c
@@ -106,6 +106,10 @@ static struct itemdef def_table[] = {
 	{"SHA_CRYPT_MAX_ROUNDS", NULL},
 	{"SHA_CRYPT_MIN_ROUNDS", NULL},
 #endif
+#ifdef USE_SM3_CRYPT
+	{"SM3_CRYPT_MAX_ROUNDS", NULL},
+	{"SM3_CRYPT_MIN_ROUNDS", NULL},
+#endif
 #ifdef USE_BCRYPT
 	{"BCRYPT_MAX_ROUNDS", NULL},
 	{"BCRYPT_MIN_ROUNDS", NULL},
diff --git a/lib/obscure.c b/lib/obscure.c
index 88a8773..b452092 100644
--- a/lib/obscure.c
+++ b/lib/obscure.c
@@ -198,6 +198,9 @@ static /*@observer@*//*@null@*/const char *obscure_msg (
 		    || (strcmp (result, "SHA256") == 0)
 		    || (strcmp (result, "SHA512") == 0)
 #endif
+#ifdef USE_SM3_CRYPT
+		    || (strcmp (result, "SM3") == 0)
+#endif
 #ifdef USE_BCRYPT
 		    || (strcmp (result, "BCRYPT") == 0)
 #endif
diff --git a/lib/salt.c b/lib/salt.c
index dc242ff..e584cc1 100644
--- a/lib/salt.c
+++ b/lib/salt.c
@@ -58,6 +58,17 @@
 #define SHA_ROUNDS_MAX 999999999
 #endif
 
+#ifdef USE_SM3_CRYPT
+/* Fixed salt len for sm3 crypt. */
+#define SM3_CRYPT_SALT_SIZE 16
+/* Default number of rounds if not explicitly specified.  */
+#define SM3_ROUNDS_DEFAULT 5000
+/* Minimum number of rounds.  */
+#define SM3_ROUNDS_MIN 1000
+/* Maximum number of rounds.  */
+#define SM3_ROUNDS_MAX 999999999
+#endif
+
 #ifdef USE_YESCRYPT
 /*
  * Default number of base64 characters used for the salt.
@@ -93,6 +104,10 @@ static /*@observer@*/const char *gensalt (size_t salt_size);
 static /*@observer@*/unsigned long SHA_get_salt_rounds (/*@null@*/const int *prefered_rounds);
 static /*@observer@*/void SHA_salt_rounds_to_buf (char *buf, unsigned long rounds);
 #endif /* USE_SHA_CRYPT */
+#ifdef USE_SM3_CRYPT
+static /*@observer@*/const unsigned long SM3_get_salt_rounds (/*@null@*/int *prefered_rounds);
+static /*@observer@*/void SM3_salt_rounds_to_buf (char *buf, unsigned long rounds);
+#endif
 #ifdef USE_BCRYPT
 static /*@observer@*/unsigned long BCRYPT_get_salt_rounds (/*@null@*/const int *prefered_rounds);
 static /*@observer@*/void BCRYPT_salt_rounds_to_buf (char *buf, unsigned long rounds);
@@ -177,6 +192,80 @@ static /*@observer@*/void SHA_salt_rounds_to_buf (char *buf, unsigned long round
 }
 #endif /* USE_SHA_CRYPT */
 
+#ifdef USE_SM3_CRYPT
+/* Return the the rounds number for the SM3 crypt methods. */
+static /*@observer@*/const unsigned long SM3_get_salt_rounds (/*@null@*/int *prefered_rounds)
+{
+	unsigned long rounds;
+
+	if (NULL == prefered_rounds) {
+		long min_rounds = getdef_long ("SM3_CRYPT_MIN_ROUNDS", -1);
+		long max_rounds = getdef_long ("SM3_CRYPT_MAX_ROUNDS", -1);
+
+		if ((-1 == min_rounds) && (-1 == max_rounds)) {
+			rounds = SM3_ROUNDS_DEFAULT;
+		}
+		else {
+			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;
+			}
+
+			rounds = (unsigned long) csrand_interval (min_rounds, max_rounds);
+		}
+	} else if (0 == *prefered_rounds) {
+		rounds = SM3_ROUNDS_DEFAULT;
+	} else {
+		rounds = (unsigned long) *prefered_rounds;
+	}
+
+	/* Sanity checks. The libc should also check this, but this
+	 * protects against a rounds_prefix overflow. */
+	if (rounds < SM3_ROUNDS_MIN) {
+		rounds = SM3_ROUNDS_MIN;
+	}
+
+	if (rounds > SM3_ROUNDS_MAX) {
+		rounds = SM3_ROUNDS_MAX;
+	}
+
+	return rounds;
+}
+
+/*
+ * Fill a salt prefix specifying the rounds number for the SM3 crypt methods
+ * to a buffer.
+ */
+static /*@observer@*/void SM3_salt_rounds_to_buf (char *buf, unsigned long rounds)
+{
+	const size_t buf_begin = strlen (buf);
+
+	/* Nothing to do here if SM3_ROUNDS_DEFAULT is used. */
+	if (rounds == SM3_ROUNDS_DEFAULT) {
+		return;
+	}
+
+	/*
+	 * Check if the result buffer is long enough.
+	 * We are going to write a maximum of 17 bytes,
+	 * plus one byte for the terminator.
+	 *    rounds=XXXXXXXXX$
+	 *    00000000011111111
+	 *    12345678901234567
+	 */
+	assert (GENSALT_SETTING_SIZE > buf_begin + 17);
+
+	(void) snprintf (buf + buf_begin, 18, "rounds=%lu$", rounds);
+}
+#endif /* USE_SM3_CRYPT */
+
 #ifdef USE_BCRYPT
 /* Return the the rounds number for the BCRYPT method. */
 static /*@observer@*/unsigned long BCRYPT_get_salt_rounds (/*@null@*/const int *prefered_rounds)
@@ -347,7 +436,7 @@ static /*@observer@*/const char *gensalt (size_t salt_size)
  * which can both be set inside the login.defs file.
  *
  * If meth is specified, an additional parameter can be provided.
- *  * For the SHA256 and SHA512 method, this specifies the number of rounds
+ *  * For the SHA256 and SHA512 and SM3 method, this specifies the number of rounds
  *    (if not NULL).
  *  * For the YESCRYPT method, this specifies the cost factor (if not NULL).
  */
@@ -399,6 +488,13 @@ static /*@observer@*/const char *gensalt (size_t salt_size)
 		rounds = SHA_get_salt_rounds (arg);
 		SHA_salt_rounds_to_buf (result, rounds);
 #endif /* USE_SHA_CRYPT */
+#ifdef USE_SM3_CRYPT
+	} else if (0 == strcmp (method, "SM3")) {
+		strcpy(result, "$sm3$");
+		salt_len = SM3_CRYPT_SALT_SIZE;
+		rounds = SM3_get_salt_rounds ((int *) arg);
+		SM3_salt_rounds_to_buf (result, rounds);
+#endif /* USE_SM3_CRYPT */
 	} else if (0 != strcmp (method, "DES")) {
 		fprintf (log_get_logfd(),
 			 _("Invalid ENCRYPT_METHOD value: '%s'.\n"
diff --git a/src/chgpasswd.c b/src/chgpasswd.c
index 7b773e2..a751dda 100644
--- a/src/chgpasswd.c
+++ b/src/chgpasswd.c
@@ -39,15 +39,18 @@
 const char *Prog;
 static bool eflg   = false;
 static bool md5flg = false;
-#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT)
+#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) || defined(USE_SM3_CRYPT)
 static bool sflg   = false;
-#endif /* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT */
+#endif /* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT || USE_SM3_CRYPT */
 
 static /*@null@*//*@observer@*/const char *crypt_method = NULL;
 #define cflg (NULL != crypt_method)
 #ifdef USE_SHA_CRYPT
 static long sha_rounds = 5000;
 #endif
+#ifdef USE_SM3_CRYPT
+static long sm3_rounds = 5000;
+#endif
 #ifdef USE_BCRYPT
 static long bcrypt_rounds = 13;
 #endif
@@ -121,6 +124,9 @@ usage (int status)
 #if defined(USE_YESCRYPT)
 	                " YESCRYPT"
 #endif
+#if defined(USE_SM3_CRYPT)
+	                " SM3"
+ #endif
 	               );
 	(void) fputs (_("  -e, --encrypted               supplied passwords are encrypted\n"), usageout);
 	(void) fputs (_("  -h, --help                    display this help message and exit\n"), usageout);
@@ -128,11 +134,11 @@ usage (int status)
 	                "                                the MD5 algorithm\n"),
 	              usageout);
 	(void) fputs (_("  -R, --root CHROOT_DIR         directory to chroot into\n"), usageout);
-#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT)
-	(void) fputs (_("  -s, --sha-rounds              number of rounds for the SHA, BCRYPT\n"
+#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) || defined(USE_SM3_CRYPT)
+	(void) fputs (_("  -s, --sha-rounds              number of rounds for the SHA, BCRYPT, SM3\n"
 	                "                                or YESCRYPT crypt algorithms\n"),
 	              usageout);
-#endif				/* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT */
+#endif				/* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT || USE_SM3_CRYPT */
 	(void) fputs ("\n", usageout);
 
 	exit (status);
@@ -146,22 +152,22 @@ usage (int status)
 static void process_flags (int argc, char **argv)
 {
 	int c;
-#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT)
+#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) || defined(USE_SM3_CRYPT)
 	int bad_s;
-#endif				/* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT */
+#endif				/* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT || USE_SM3_CRYPT */
 	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'},
 		{"root",         required_argument, NULL, 'R'},
-#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT)
+#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) || defined(USE_SM3_CRYPT)
 		{"sha-rounds",   required_argument, NULL, 's'},
-#endif				/* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT */
+#endif				/* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT || USE_SM3_CRYPT */
 		{NULL, 0, NULL, '\0'}
 	};
 	while ((c = getopt_long (argc, argv,
-#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT)
+#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) || defined(USE_SM3_CRYPT)
 	                         "c:ehmR:s:",
 #else
 	                         "c:ehmR:",
@@ -182,7 +188,7 @@ static void process_flags (int argc, char **argv)
 			break;
 		case 'R': /* no-op, handled in process_root_flag () */
 			break;
-#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT)
+#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) || defined(USE_SM3_CRYPT)
 		case 's':
 			sflg = true;
                         bad_s = 0;
@@ -211,6 +217,12 @@ static void process_flags (int argc, char **argv)
                             bad_s = 1;
                         }
 #endif				/* USE_YESCRYPT */
+#if defined(USE_SM3_CRYPT)
+                        if ((   (0 == strcmp (crypt_method, "SM3"))
+			       && (0 == getlong(optarg, &sm3_rounds)))) {
+                            bad_s = 1;
+                        }
+#endif				/* USE_SM3_CRYPT */
                         if (bad_s != 0) {
 				fprintf (stderr,
 				         _("%s: invalid numeric argument '%s'\n"),
@@ -218,7 +230,7 @@ static void process_flags (int argc, char **argv)
 				usage (E_USAGE);
 			}
 			break;
-#endif				/* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT */
+#endif				/* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT || USE_SM3_CRYPT */
 
 		default:
 			usage (E_USAGE);
@@ -237,7 +249,7 @@ static void process_flags (int argc, char **argv)
  */
 static void check_flags (void)
 {
-#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT)
+#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) || defined(USE_SM3_CRYPT)
 	if (sflg && !cflg) {
 		fprintf (stderr,
 		         _("%s: %s flag is only allowed with the %s flag\n"),
@@ -268,6 +280,9 @@ static void check_flags (void)
 #ifdef USE_YESCRYPT
 		    && (0 != strcmp (crypt_method, "YESCRYPT"))
 #endif				/* USE_YESCRYPT */
+#ifdef USE_SM3_CRYPT
+		    && (0 != strcmp (crypt_method, "SM3"))
+#endif				/* USE_SM3_CRYPT */
 		    ) {
 			fprintf (stderr,
 			         _("%s: unsupported crypt method: %s\n"),
@@ -498,7 +513,7 @@ int main (int argc, char **argv)
 			if (md5flg) {
 				crypt_method = "MD5";
 			}
-#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT)
+#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) || defined(USE_SM3_CRYPT)
 			if (sflg) {
 #if defined(USE_SHA_CRYPT)
 				if (   (0 == strcmp (crypt_method, "SHA256"))
@@ -516,6 +531,11 @@ int main (int argc, char **argv)
 					arg = &yescrypt_cost;
 				}
 #endif				/* USE_YESCRYPT */
+#if defined(USE_SM3_CRYPT)
+				if (0 == strcmp (crypt_method, "SM3")) {
+					arg = &sm3_rounds;
+				}
+#endif				/* USE_SM3_CRYPT */
 			}
 #endif
 			salt = crypt_make_salt (crypt_method, arg);
diff --git a/src/chpasswd.c b/src/chpasswd.c
index 1a1a5d5..a2b6e9e 100644
--- a/src/chpasswd.c
+++ b/src/chpasswd.c
@@ -38,7 +38,7 @@
 const char *Prog;
 static bool eflg   = false;
 static bool md5flg = false;
-#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT)
+#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) || defined(USE_SM3_CRYPT)
 static bool sflg   = false;
 #endif
 
@@ -47,6 +47,9 @@ static /*@null@*//*@observer@*/const char *crypt_method = NULL;
 #ifdef USE_SHA_CRYPT
 static long sha_rounds = 5000;
 #endif
+#ifdef USE_SM3_CRYPT
+static long sm3_rounds = 5000;
+#endif
 #ifdef USE_BCRYPT
 static long bcrypt_rounds = 13;
 #endif
@@ -117,6 +120,9 @@ usage (int status)
 #endif
 #if defined(USE_YESCRYPT)
 	                " YESCRYPT"
+#endif
+#if defined(USE_SM3_CRYPT)
+	                " SM3"
 #endif
 	               );
 	(void) fputs (_("  -e, --encrypted               supplied passwords are encrypted\n"), usageout);
@@ -126,8 +132,8 @@ usage (int status)
 	              usageout);
 	(void) fputs (_("  -R, --root CHROOT_DIR         directory to chroot into\n"), usageout);
 	(void) fputs (_("  -P, --prefix PREFIX_DIR       directory prefix\n"), usageout);
-#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT)
-	(void) fputs (_("  -s, --sha-rounds              number of rounds for the SHA, BCRYPT\n"
+#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) || defined(USE_SM3_CRYPT)
+	(void) fputs (_("  -s, --sha-rounds              number of rounds for the SHA, BCRYPT, SM3\n"
 	                "                                or YESCRYPT crypt algorithms\n"),
 	              usageout);
 #endif				/* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT */
@@ -144,9 +150,9 @@ usage (int status)
 static void process_flags (int argc, char **argv)
 {
 	int c;
-#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT)
+#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) || defined(USE_SM3_CRYPT)
         int bad_s;
-#endif				/* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT */
+#endif				/* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT || USE_SM3_CRYPT */
 	static struct option long_options[] = {
 		{"crypt-method", required_argument, NULL, 'c'},
 		{"encrypted",    no_argument,       NULL, 'e'},
@@ -154,14 +160,14 @@ static void process_flags (int argc, char **argv)
 		{"md5",          no_argument,       NULL, 'm'},
 		{"root",         required_argument, NULL, 'R'},
 		{"prefix",       required_argument, NULL, 'P'},
-#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT)
+#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) || defined(USE_SM3_CRYPT)
 		{"sha-rounds",   required_argument, NULL, 's'},
-#endif				/* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT */
+#endif				/* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT || USE_SM3_CRYPT */
 		{NULL, 0, NULL, '\0'}
 	};
 
 	while ((c = getopt_long (argc, argv,
-#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT)
+#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) || defined(USE_SM3_CRYPT)
 	                         "c:ehmR:P:s:",
 #else
 	                         "c:ehmR:P:",
@@ -184,7 +190,7 @@ static void process_flags (int argc, char **argv)
 			break;
 		case 'P': /* no-op, handled in process_prefix_flag () */
 			break;
-#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT)
+#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) || defined(USE_SM3_CRYPT)
 		case 's':
 			sflg = true;
                         bad_s = 0;
@@ -206,14 +212,20 @@ static void process_flags (int argc, char **argv)
                             bad_s = 1;
                         }
 #endif				/* USE_YESCRYPT */
-                        if (bad_s != 0) {
+#if defined(USE_SM3_CRYPT)
+                        if ((   (0 == strcmp (crypt_method, "SM3"))
+			       && (0 == getlong(optarg, &sm3_rounds)))) {
+                            bad_s = 1;
+                        }
+#endif				/* USE_SM3_CRYPT */
+			if (bad_s != 0) {
 				fprintf (stderr,
 				         _("%s: invalid numeric argument '%s'\n"),
 				         Prog, optarg);
 				usage (E_USAGE);
 			}
 			break;
-#endif				/* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT */
+#endif				/* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT || USE_SM3_CRYPT */
 
 		default:
 			usage (E_USAGE);
@@ -232,7 +244,7 @@ static void process_flags (int argc, char **argv)
  */
 static void check_flags (void)
 {
-#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT)
+#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) || defined(USE_SM3_CRYPT)
 	if (sflg && !cflg) {
 		fprintf (stderr,
 		         _("%s: %s flag is only allowed with the %s flag\n"),
@@ -257,6 +269,9 @@ static void check_flags (void)
 		    &&(!IS_CRYPT_METHOD("SHA256"))
 		    &&(!IS_CRYPT_METHOD("SHA512"))
 #endif				/* USE_SHA_CRYPT */
+#ifdef USE_SM3_CRYPT
+		    &&(!IS_CRYPT_METHOD("SM3"))
+#endif				/* USE_SM3_CRYPT */
 #ifdef USE_BCRYPT
 		    &&(!IS_CRYPT_METHOD("BCRYPT"))
 #endif				/* USE_BCRYPT */
@@ -430,6 +445,11 @@ static const char *get_salt(void)
 			arg = &yescrypt_cost;
 		}
 #endif				/* USE_YESCRYPT */
+#if defined(USE_SM3_CRYPT)
+		if (IS_CRYPT_METHOD("SM3")) {
+			arg = &sm3_rounds;
+		}
+#endif				/* USE_SM3_CRYPT */
 	}
 #endif
 	return crypt_make_salt (crypt_method, arg);
diff --git a/src/newusers.c b/src/newusers.c
index 08f7979..6effa82 100644
--- a/src/newusers.c
+++ b/src/newusers.c
@@ -60,12 +60,15 @@ static bool rflg = false;	/* create a system account */
 #ifndef USE_PAM
 static /*@null@*//*@observer@*/char *crypt_method = NULL;
 #define cflg (NULL != crypt_method)
-#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT)
+#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) || defined(USE_SM3_CRYPT)
 static bool sflg = false;
 #endif
 #ifdef USE_SHA_CRYPT
 static long sha_rounds = 5000;
 #endif				/* USE_SHA_CRYPT */
+#ifdef USE_SM3_CRYPT
+static long sm3_rounds = 5000;
+#endif	
 #ifdef USE_BCRYPT
 static long bcrypt_rounds = 13;
 #endif				/* USE_BCRYPT */
@@ -131,6 +134,9 @@ static void usage (int status)
 #endif
 #if defined(USE_YESCRYPT)
 	                " YESCRYPT"
+#endif
+#if defined(USE_SM3_CRYPT)
+	                " SM3"
 #endif
 	               );
 #endif				/* !USE_PAM */
@@ -138,11 +144,11 @@ static void usage (int status)
 	(void) fputs (_("  -r, --system                  create system accounts\n"), usageout);
 	(void) fputs (_("  -R, --root CHROOT_DIR         directory to chroot into\n"), usageout);
 #ifndef USE_PAM
-#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT)
-	(void) fputs (_("  -s, --sha-rounds              number of rounds for the SHA, BCRYPT\n"
+#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) || defined(USE_SM3_CRYPT)
+	(void) fputs (_("  -s, --sha-rounds              number of rounds for the SHA, BCRYPT, SM3\n"
 	                "                                or YESCRYPT crypt algorithms\n"),
 	              usageout);
-#endif				/* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT */
+#endif				/* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT || USE_SM3_CRYPT */
 #endif				/* !USE_PAM */
 	(void) fputs ("\n", usageout);
 
@@ -436,6 +442,13 @@ static int update_passwd (struct passwd *pwd, const char *password)
 			}
 		}
 #endif				/* USE_YESCRYPT */
+#if defined(USE_SM3_CRYPT)
+		if (sflg) {
+			if (0 == strcmp (crypt_method, "SM3")) {
+				crypt_arg = &sm3_rounds;
+			}
+		}
+#endif				/* USE_SM3_CRYPT */
 	}
 
 	if ((NULL != crypt_method) && (0 == strcmp(crypt_method, "NONE"))) {
@@ -492,6 +505,13 @@ static int add_passwd (struct passwd *pwd, const char *password)
 			}
 		}
 #endif				/* USE_PAM */
+#if defined(USE_SM3_CRYPT)
+		if (sflg) {
+			if (0 == strcmp (crypt_method, "SM3")) {
+				crypt_arg = &sm3_rounds;
+			}
+		}
+#endif				/* USE_SM3_CRYPT */
 	}
 
 	/*
@@ -609,9 +629,9 @@ static void process_flags (int argc, char **argv)
 {
 	int c;
 #ifndef USE_PAM
-#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT)
+#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) || defined(USE_SM3_CRYPT)
         int bad_s;
-#endif				/* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT */
+#endif				/* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT || USE_SM3_CRYPT */
 #endif 				/* !USE_PAM */
 	static struct option long_options[] = {
 		{"badname",      no_argument,       NULL, 'b'},
@@ -622,20 +642,20 @@ static void process_flags (int argc, char **argv)
 		{"system",       no_argument,       NULL, 'r'},
 		{"root",         required_argument, NULL, 'R'},
 #ifndef USE_PAM
-#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT)
+#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) || defined(USE_SM3_CRYPT)
 		{"sha-rounds",   required_argument, NULL, 's'},
-#endif				/* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT */
+#endif				/* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT || USE_SM3_CRYPT */
 #endif				/* !USE_PAM */
 		{NULL, 0, NULL, '\0'}
 	};
 
 	while ((c = getopt_long (argc, argv,
 #ifndef USE_PAM
-#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT)
+#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) || defined(USE_SM3_CRYPT)
 	                         "c:bhrs:",
-#else				/* !USE_SHA_CRYPT && !USE_BCRYPT && !USE_YESCRYPT */
+#else				/* !USE_SHA_CRYPT && !USE_BCRYPT && !USE_YESCRYPT && !USE_SM3_CRYPT */
 	                         "c:bhr",
-#endif				/* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT */
+#endif				/* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT || USE_SM3_CRYPT */
 #else				/* USE_PAM */
 	                         "bhr",
 #endif
@@ -658,7 +678,7 @@ static void process_flags (int argc, char **argv)
 		case 'R': /* no-op, handled in process_root_flag () */
 			break;
 #ifndef USE_PAM
-#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT)
+#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) || defined(USE_SM3_CRYPT)
 		case 's':
 			sflg = true;
                         bad_s = 0;
@@ -687,6 +707,12 @@ static void process_flags (int argc, char **argv)
                             bad_s = 1;
                         }
 #endif				/* USE_YESCRYPT */
+#if defined(USE_SM3_CRYPT)
+                        if ((   (0 == strcmp (crypt_method, "SM3"))
+			       && (0 == getlong(optarg, &sm3_rounds)))) {
+                            bad_s = 1;
+                        }
+#endif				/* USE_SM3_CRYPT */
                         if (bad_s != 0) {
 				fprintf (stderr,
 				         _("%s: invalid numeric argument '%s'\n"),
@@ -694,7 +720,7 @@ static void process_flags (int argc, char **argv)
 				usage (EXIT_FAILURE);
 			}
 			break;
-#endif				/* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT */
+#endif				/* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT || USE_SM3_CRYPT */
 #endif				/* !USE_PAM */
 		default:
 			usage (EXIT_FAILURE);
@@ -728,14 +754,14 @@ static void process_flags (int argc, char **argv)
 static void check_flags (void)
 {
 #ifndef USE_PAM
-#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT)
+#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) || defined(USE_SM3_CRYPT)
 	if (sflg && !cflg) {
 		fprintf (stderr,
 		         _("%s: %s flag is only allowed with the %s flag\n"),
 		         Prog, "-s", "-c");
 		usage (EXIT_FAILURE);
 	}
-#endif				/* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT */
+#endif				/* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT || USE_SM3_CRYPT */
 
 	if (cflg) {
 		if (   (0 != strcmp (crypt_method, "DES"))
@@ -745,6 +771,9 @@ static void check_flags (void)
 		    && (0 != strcmp (crypt_method, "SHA256"))
 		    && (0 != strcmp (crypt_method, "SHA512"))
 #endif				/* USE_SHA_CRYPT */
+#ifdef USE_SM3_CRYPT
+		    && (0 != strcmp (crypt_method, "SM3"))
+#endif				/* USE_SM3_CRYPT */
 #ifdef USE_BCRYPT
 		    && (0 != strcmp (crypt_method, "BCRYPT"))
 #endif				/* USE_BCRYPT */
diff --git a/src/passwd.c b/src/passwd.c
index 5d59e8c..20284c6 100644
--- a/src/passwd.c
+++ b/src/passwd.c
@@ -90,7 +90,7 @@ static bool spw_locked = false;
 
 /*
  * Size of the biggest passwd:
- *   $6$	3
+ *   $sm3$	5
  *   rounds=	7
  *   999999999	9
  *   $		1
@@ -99,7 +99,7 @@ static bool spw_locked = false;
  *   SHA512	123
  *   nul	1
  *
- *   total	161
+ *   total	163
  */
 static char crypt_passwd[256];
 static bool do_update_pwd = false;
@@ -268,6 +268,9 @@ static int new_password (const struct passwd *pw)
 #ifdef USE_YESCRYPT
 		    || (strcmp (method, "YESCRYPT") == 0)
 #endif /* USE_YESCRYPT*/
+#ifdef USE_SM3_CRYPT
+		    || (strcmp (method, "SM3") == 0)
+#endif /* USE_SM3_CRYPT */
 
 		    ) {
 			pass_max_len = -1;
-- 
2.27.0

