swtpm_localca: Add support for up to 20 bytes serial numbers
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
diff --git a/man/man5/swtpm-localca.conf.pod b/man/man5/swtpm-localca.conf.pod
index 10933dc..cb472c2 100644
--- a/man/man5/swtpm-localca.conf.pod
+++ b/man/man5/swtpm-localca.conf.pod
@@ -41,6 +41,10 @@
The name of file containing the serial number for the next certificate.
+The serial number must be a decimal number and must be representable
+with 20 bytes or less. Once 21 bytes are used a new random serial
+number with 20 decimal digits will be created.
+
=item B<TSS_TCSD_HOSTNAME>
This variable can be set to the host where B<tcsd> is running on in case
diff --git a/src/swtpm_localca/Makefile.am b/src/swtpm_localca/Makefile.am
index 285d4fc..74532a8 100644
--- a/src/swtpm_localca/Makefile.am
+++ b/src/swtpm_localca/Makefile.am
@@ -20,6 +20,7 @@
$(MY_CFLAGS) \
$(CFLAGS) \
$(GLIB_CFLAGS) \
+ $(GMP_CFLAGS) \
$(HARDENING_CFLAGS)
swtpm_localca_DEPENDENCIES = \
@@ -32,6 +33,7 @@
-L$(top_builddir)/src/utils -lswtpm_utils \
$(MY_LDFLAGS) \
$(GLIB_LIBS) \
+ $(GMP_LIBS) \
$(HARDENING_LDFLAGS)
swtpm_localca_SOURCES = \
diff --git a/src/swtpm_localca/swtpm_localca.c b/src/swtpm_localca/swtpm_localca.c
index eb75b83..1d620bf 100644
--- a/src/swtpm_localca/swtpm_localca.c
+++ b/src/swtpm_localca/swtpm_localca.c
@@ -24,6 +24,8 @@
#include <glib.h>
+#include <gmp.h>
+
#include "swtpm_conf.h"
#include "swtpm_utils.h"
#include "swtpm_localca_utils.h"
@@ -282,16 +284,36 @@
return ret;
}
+/* Create a random ASCII decimal number of given length.
+ * The buffer is not NUL terminated.
+ */
+static void get_random_serial(char *buffer, size_t length)
+{
+ GRand *grand = g_rand_new();
+ size_t i;
+
+ buffer[0] = '0' + g_rand_int_range(grand, 1, 9);
+ for (i = 1; i < length; i++)
+ buffer[i] = '0' + g_rand_int_range(grand, 0, 9);
+
+ g_rand_free(grand);
+}
+
/* Get the next serial number from the certserial file; if it contains
- * a non-numeric content start over with serial number '1'.
+ * a non-numeric content start over with a random 20 digit serial number.
+ * Up to 20 bytes of serial number are supported. The max.
+ * serial number is decimal: 1461501637330902918203684832716283019655932542975
+ * This decimal number is 49 digits long.
+ * This function will write back the used serial number that the next
+ * caller must increase by '1' to be allowed to use it.
*/
static int get_next_serial(const gchar *certserial, const gchar *lockfile,
gchar **serial_str)
{
g_autofree gchar *buffer = NULL;
+ char serialbuffer[50];
size_t buffer_len;
- unsigned long long serial, serial_n;
- char *endptr = NULL;
+ mpz_t serial;
int lockfd;
int ret = 1;
@@ -299,25 +321,43 @@
if (lockfd < 0)
return 1;
- if (access(certserial, R_OK) != 0)
- write_file(certserial, (unsigned char *)"1", 1);
+ if (access(certserial, R_OK) != 0) {
+ get_random_serial(serialbuffer, 20);
+ write_file(certserial, (unsigned char *)serialbuffer, 20);
+ }
if (read_file(certserial, &buffer, &buffer_len) != 0)
goto error;
- if (buffer_len > 0) {
- serial = strtoull(buffer, &endptr, 10);
- if (*endptr == '\0') {
- serial_n = serial + 1;
- } else {
- serial_n = 1;
- }
+ mpz_init(serial);
+
+ if (buffer_len > 0 && buffer_len <= 49) {
+ memcpy(serialbuffer, buffer, buffer_len);
+ serialbuffer[buffer_len] = 0;
+
+ if (gmp_sscanf(serialbuffer, "%Zu", serial) != 1)
+ goto new_serial;
+ mpz_add_ui(serial, serial, 1);
+
+ if ((mpz_sizeinbase(serial, 2) + 7) / 8 > 20)
+ goto new_serial;
+
+ if (gmp_snprintf(serialbuffer,
+ sizeof(serialbuffer),
+ "%Zu", serial) >= (int)sizeof(serialbuffer))
+ goto new_serial;
} else {
- serial_n = 1;
+new_serial:
+ /* start with random serial number */
+ buffer_len = 20;
+ get_random_serial(serialbuffer, buffer_len);
+ serialbuffer[buffer_len] = 0;
}
- *serial_str = g_strdup_printf("%llu", serial_n);
+ *serial_str = g_strdup(serialbuffer);
write_file(certserial, (unsigned char *)*serial_str, strlen(*serial_str));
ret = 0;
+ mpz_clear(serial);
+
error:
unlock_file(lockfd);