swtpm: Introduce disable-auto-shutdown flag for --flags option

Introduce disable-auto-shutdown flag for the --flags option to disable
the sending of TPM2_Shutdown() if swtpm determines that it needs to send
this command to a TPM 2 before device reset or swtpm program termination.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
diff --git a/man/man8/swtpm.pod b/man/man8/swtpm.pod
index 605ed8d..f67a3e2 100644
--- a/man/man8/swtpm.pod
+++ b/man/man8/swtpm.pod
@@ -277,7 +277,7 @@
 This option is only available on Linux and only if swtpm was compiled with
 libseccomp support.
 
-=item B<--flags [not-need-init] [,startup-clear|startup-state|startup-deactivated|startup-none]>
+=item B<--flags [not-need-init][,startup-clear|startup-state|startup-deactivated|startup-none][,disable-auto-shutdown]>
 
 The I<not-need-init> flag enables the TPM to accept TPM commands right after
 start without requiring an INIT to be sent to it through the command channel
@@ -291,6 +291,13 @@
 If I<--vtpm-proxy> is used, I<startup-clear> is automatically chosen but
 this can be changed with this option.
 
+The I<disable-auto-shutdown> flag prevents swtpm from automatically sending a
+TPM2_Shutdown() before the reset of a TPM 2 or before the swtpm process
+is terminated. When this flag is not provide swtpm will send this command to
+avoid increasing the dictionary attack (DA) lockout counter and ulimately
+a DA lockout by the TPM 2 due to omission of sending a required TPM2_Shutdown()
+before TPM 2 reset or swtpm process termination.
+
 =item B<--print-capabilities> (since v0.2)
 
 Print capabilities that were added to swtpm after version 0.1. The output
@@ -309,6 +316,7 @@
         "nvram-backend-file",
         "tpm-send-command-header",
         "flags-opt-startup",
+        "flags-opt-disable-auto-shutdown",
         "rsa-keysize-1024",
         "rsa-keysize-2048",
         "rsa-keysize-3072"
@@ -369,6 +377,10 @@
 
 The I<--flags> option supports the I<startup-...> options.
 
+=item B<flags-opt-disable-auto-shutdown> (since v0.8)
+
+The I<--flags> option supports the I<disable-auto-shutdown> flag.
+
 =item B<rsa-keysize-2048> (since v0.4)
 
 The TPM 2 supports the shown RSA key sizes. If none of the
diff --git a/src/swtpm/capabilities.c b/src/swtpm/capabilities.c
index dfcac83..c81c79b 100644
--- a/src/swtpm/capabilities.c
+++ b/src/swtpm/capabilities.c
@@ -146,7 +146,7 @@
          "{ "
          "\"type\": \"swtpm\", "
          "\"features\": [ "
-             "%s%s%s%s%s%s%s%s%s%s%s"
+             "%s%s%s%s%s%s%s%s%s%s%s%s"
           " ], "
          "\"version\": \"" VERSION "\" "
          "}",
@@ -154,6 +154,7 @@
          with_tpm2,
          !cusetpm     ? "\"tpm-send-command-header\", ": "",
          true         ? "\"flags-opt-startup\", "      : "",
+         true         ? "\"flags-opt-disable-auto-shutdown\", ": "",
          cmdarg_seccomp,
          true         ? "\"cmdarg-key-fd\", "          : "",
          true         ? "\"cmdarg-pwd-fd\", "          : "",
diff --git a/src/swtpm/common.c b/src/swtpm/common.c
index 06408c6..b4e5803 100644
--- a/src/swtpm/common.c
+++ b/src/swtpm/common.c
@@ -246,6 +246,9 @@
     }, {
         .name = "startup-deactivated",
         .type = OPT_TYPE_BOOLEAN,
+    }, {
+        .name = "disable-auto-shutdown",
+        .type = OPT_TYPE_BOOLEAN,
     },
     END_OPTION_DESC
 };
@@ -1234,7 +1237,7 @@
 }
 
 static int parse_flags_options(char *options, bool *need_init_cmd,
-                               uint16_t *startupType)
+                               uint16_t *startupType, bool *disable_auto_shutdown)
 {
     OptionValues *ovs = NULL;
     char *error = NULL;
@@ -1247,6 +1250,8 @@
 
     if (option_get_bool(ovs, "not-need-init", false))
         *need_init_cmd = false;
+    if (option_get_bool(ovs, "disable-auto-shutdown", false))
+        *disable_auto_shutdown = true;
 
     if (option_get_bool(ovs, "startup-clear", false))
         *startupType = TPM_ST_CLEAR;
@@ -1280,12 +1285,13 @@
  * Returns 0 on success, -1 on failure.
  */
 int handle_flags_options(char *options, bool *need_init_cmd,
-                         uint16_t *startupType)
+                         uint16_t *startupType, bool *disable_auto_shutdown)
 {
     if (!options)
         return 0;
 
-    if (parse_flags_options(options, need_init_cmd, startupType) < 0)
+    if (parse_flags_options(options, need_init_cmd, startupType,
+                            disable_auto_shutdown) < 0)
         return -1;
 
     return 0;
diff --git a/src/swtpm/common.h b/src/swtpm/common.h
index f284028..1df3163 100644
--- a/src/swtpm/common.h
+++ b/src/swtpm/common.h
@@ -54,7 +54,7 @@
 int handle_server_options(char *options, struct server **s);
 int handle_locality_options(char *options, uint32_t *flags);
 int handle_flags_options(char *options, bool *need_init_cmd,
-                         uint16_t *startupType);
+                         uint16_t *startupType, bool *disable_auto_shutdown);
 #ifdef WITH_SECCOMP
 int handle_seccomp_options(char *options, unsigned int *seccomp_action);
 #else
diff --git a/src/swtpm/ctrlchannel.c b/src/swtpm/ctrlchannel.c
index babd150..2713b2c 100644
--- a/src/swtpm/ctrlchannel.c
+++ b/src/swtpm/ctrlchannel.c
@@ -554,7 +554,7 @@
         if (n != (ssize_t)sizeof(ptm_init)) /* r/w */
             goto err_bad_input;
 
-        if (*tpm_running)
+        if (*tpm_running && !mlp->disable_auto_shutdown)
             tpmlib_maybe_send_tpm2_shutdown(mlp->tpmversion,
                                             &mlp->lastCommand);
 
@@ -580,7 +580,7 @@
         if (n != 0) /* wo */
             goto err_bad_input;
 
-        if (*tpm_running)
+        if (*tpm_running && !mlp->disable_auto_shutdown)
             tpmlib_maybe_send_tpm2_shutdown(mlp->tpmversion,
                                             &mlp->lastCommand);
 
@@ -596,7 +596,7 @@
         if (n != 0) /* wo */
             goto err_bad_input;
 
-        if (*tpm_running)
+        if (*tpm_running && !mlp->disable_auto_shutdown)
             tpmlib_maybe_send_tpm2_shutdown(mlp->tpmversion,
                                             &mlp->lastCommand);
 
diff --git a/src/swtpm/cuse_tpm.c b/src/swtpm/cuse_tpm.c
index 61bd9a3..a011b44 100644
--- a/src/swtpm/cuse_tpm.c
+++ b/src/swtpm/cuse_tpm.c
@@ -113,6 +113,9 @@
 /* last command sent to the TPM */
 static uint32_t g_lastCommand = TPM_ORDINAL_NONE;
 
+/* TPM2_Shutdown() will NOT be sent by swtpm */
+static bool g_disable_auto_shutdown = false;
+
 #if GLIB_MAJOR_VERSION >= 2
 # if GLIB_MINOR_VERSION >= 32
 
@@ -229,10 +232,12 @@
 "                       instead of dir option;\n"
 "                       mode allows a user to set the file mode bits of the state\n"
 "                       files; the default mode is 0640;\n"
-"--flags [not-need-init][,startup-clear|startup-state|startup-deactivated|startup-none]\n"
+"--flags [not-need-init][,startup-clear|startup-state|startup-deactivated|startup-none][,disable-auto-shutdown]\n"
 "                    :  not-need-init: commands can be sent without needing to\n"
 "                       send an INIT via control channel;\n"
 "                       startup-...: send Startup command with this type;\n"
+"                       disable-auto-shutdown disables automatic sending of\n"
+"                       TPM2_Shutdown before TPM 2 reset or swtpm termination;\n"
 "-r|--runas <user>   :  after creating the CUSE device, change to the given\n"
 "                       user\n"
 "--tpm2              :  choose TPM2 functionality\n"
@@ -1091,7 +1096,7 @@
 
             worker_thread_end();
 
-            if (tpm_running)
+            if (tpm_running && !g_disable_auto_shutdown)
                 tpmlib_maybe_send_tpm2_shutdown(tpmversion,
                                                 &g_lastCommand);
 
@@ -1112,7 +1117,7 @@
     case PTM_STOP:
         worker_thread_end();
 
-        if (tpm_running)
+        if (tpm_running && !g_disable_auto_shutdown)
             tpmlib_maybe_send_tpm2_shutdown(tpmversion, &g_lastCommand);
 
         res = TPM_SUCCESS;
@@ -1130,7 +1135,7 @@
     case PTM_SHUTDOWN:
         worker_thread_end();
 
-        if (tpm_running)
+        if (tpm_running && !g_disable_auto_shutdown)
             tpmlib_maybe_send_tpm2_shutdown(tpmversion, &g_lastCommand);
 
         res = TPM_SUCCESS;
@@ -1705,7 +1710,7 @@
         handle_seccomp_options(param.seccompdata, &param.seccomp_action) < 0 ||
         handle_locality_options(param.localitydata, &locality_flags) < 0 ||
         handle_flags_options(param.flagsdata, &need_init_cmd,
-                             &param.startupType) < 0) {
+                             &param.startupType, &g_disable_auto_shutdown) < 0) {
         ret = -3;
         goto exit;
     }
diff --git a/src/swtpm/mainloop.h b/src/swtpm/mainloop.h
index 8feed1e..a1705a0 100644
--- a/src/swtpm/mainloop.h
+++ b/src/swtpm/mainloop.h
@@ -61,6 +61,7 @@
     TPMLIB_TPMVersion tpmversion;
     uint16_t startupType; /* use TPM 1.2 types */
     uint32_t lastCommand; /* last Command sent to TPM */
+    bool disable_auto_shutdown;   /* TPM2_Shutdown() will NOT be sent by swtpm */
 };
 
 int mainLoop(struct mainLoopParams *mlp,
diff --git a/src/swtpm/swtpm.c b/src/swtpm/swtpm.c
index c2c255b..94c8c09 100644
--- a/src/swtpm/swtpm.c
+++ b/src/swtpm/swtpm.c
@@ -168,10 +168,12 @@
     "                   mode allows a user to set the file mode bits of the socket; the\n"
     "                   value must be given in octal number format;\n"
     "                   uid and gid set the ownership of the Unixio socket's file;\n"
-    "--flags [not-need-init][,startup-clear|startup-state|startup-deactivated|startup-none]\n"
+    "--flags [not-need-init][,startup-clear|startup-state|startup-deactivated|startup-none][,disable-auto-shutdown]\n"
     "                 : not-need-init: commands can be sent without needing to\n"
     "                   send an INIT via control channel;\n"
     "                   startup-...: send Startup command with this type;\n"
+    "                   disable-auto-shutdown disables automatic sending of\n"
+    "                   TPM2_Shutdown before TPM 2 reset or swtpm termination;\n"
     "-r|--runas <user>: change to the given user\n"
     "--tpm2           : choose TPM2 functionality\n"
 #ifdef WITH_SECCOMP
@@ -216,6 +218,7 @@
         .tpmversion = TPMLIB_TPM_VERSION_1_2,
         .startupType = _TPM_ST_NONE,
         .lastCommand = TPM_ORDINAL_NONE,
+        .disable_auto_shutdown = false,
     };
     struct server *server = NULL;
     unsigned long val;
@@ -469,7 +472,7 @@
         handle_tpmstate_options(tpmstatedata) < 0 ||
         handle_seccomp_options(seccompdata, &seccomp_action) < 0 ||
         handle_flags_options(flagsdata, &need_init_cmd,
-                             &mlp.startupType) < 0) {
+                             &mlp.startupType, &mlp.disable_auto_shutdown) < 0) {
         goto exit_failure;
     }
 
diff --git a/src/swtpm/swtpm_chardev.c b/src/swtpm/swtpm_chardev.c
index a1d6c8d..8767d90 100644
--- a/src/swtpm/swtpm_chardev.c
+++ b/src/swtpm/swtpm_chardev.c
@@ -189,12 +189,14 @@
     "--locality [reject-locality-4][,allow-set-locality]\n"
     "                 : reject-locality-4: reject any command in locality 4\n"
     "                   allow-set-locality: accept SetLocality command\n"
-    "--flags [not-need-init][,startup-clear|startup-state|startup-deactivated|startup-none]\n"
+    "--flags [not-need-init][,startup-clear|startup-state|startup-deactivated|startup-none][,disable-auto-shutdown]\n"
     "                 : not-need-init: commands can be sent without needing to\n"
     "                   send an INIT via control channel; not needed when using\n"
     "                   --vtpm-proxy\n"
     "                   startup-...: send Startup command with this type;\n"
     "                   when --vtpm-proxy is used, startup-clear is used\n"
+    "                   disable-auto-shutdown disables automatic sending of\n"
+    "                   TPM2_Shutdown before TPM 2 reset or swtpm termination;\n"
     "--tpm2           : choose TPM2 functionality\n"
 #ifdef WITH_SECCOMP
 # ifndef SCMP_ACT_LOG
@@ -275,6 +277,7 @@
         .tpmversion = TPMLIB_TPM_VERSION_1_2,
         .startupType = _TPM_ST_NONE,
         .lastCommand = TPM_ORDINAL_NONE,
+        .disable_auto_shutdown = false,
     };
     unsigned long val;
     char *end_ptr;
@@ -532,7 +535,7 @@
         handle_tpmstate_options(tpmstatedata) < 0 ||
         handle_seccomp_options(seccompdata, &seccomp_action) < 0 ||
         handle_flags_options(flagsdata, &need_init_cmd,
-                             &mlp.startupType) < 0) {
+                             &mlp.startupType, &mlp.disable_auto_shutdown) < 0) {
         goto exit_failure;
     }
 
diff --git a/tests/_test_print_capabilities b/tests/_test_print_capabilities
index 92073e3..87b0fd5 100755
--- a/tests/_test_print_capabilities
+++ b/tests/_test_print_capabilities
@@ -25,7 +25,7 @@
 	noncuse='"tpm-send-command-header", '
 fi
 
-exp='\{ "type": "swtpm", "features": \[ "tpm-1.2",( "tpm-2.0",)? '${noncuse}'"flags-opt-startup", '${seccomp}'"cmdarg-key-fd", "cmdarg-pwd-fd", "cmdarg-print-states", "nvram-backend-dir", "nvram-backend-file" \], "version": "[^"]*" \}'
+exp='\{ "type": "swtpm", "features": \[ "tpm-1.2",( "tpm-2.0",)? '${noncuse}'"flags-opt-startup", "flags-opt-disable-auto-shutdown", '${seccomp}'"cmdarg-key-fd", "cmdarg-pwd-fd", "cmdarg-print-states", "nvram-backend-dir", "nvram-backend-file" \], "version": "[^"]*" \}'
 if ! [[ ${msg} =~ ${exp} ]]; then
 	echo "Unexpected response from ${SWTPM_IFACE} TPM to --print-capabilities:"
 	echo "Actual   : ${msg}"
diff --git a/tests/_test_tpm2_print_capabilities b/tests/_test_tpm2_print_capabilities
index 2e54587..35f77e6 100755
--- a/tests/_test_tpm2_print_capabilities
+++ b/tests/_test_tpm2_print_capabilities
@@ -26,7 +26,7 @@
 fi
 
 # The rsa key size reporting is variable, so use a regex
-exp='\{ "type": "swtpm", "features": \[( "tpm-1.2",)? "tpm-2.0", '${noncuse}'"flags-opt-startup", '${seccomp}'"cmdarg-key-fd", "cmdarg-pwd-fd", "cmdarg-print-states", "nvram-backend-dir", "nvram-backend-file"(, "rsa-keysize-1024")?(, "rsa-keysize-2048")?(, "rsa-keysize-3072")? \], "version": "[^"]*" \}'
+exp='\{ "type": "swtpm", "features": \[( "tpm-1.2",)? "tpm-2.0", '${noncuse}'"flags-opt-startup", "flags-opt-disable-auto-shutdown", '${seccomp}'"cmdarg-key-fd", "cmdarg-pwd-fd", "cmdarg-print-states", "nvram-backend-dir", "nvram-backend-file"(, "rsa-keysize-1024")?(, "rsa-keysize-2048")?(, "rsa-keysize-3072")? \], "version": "[^"]*" \}'
 if ! [[ ${msg} =~ ${exp} ]]; then
 	echo "Unexpected response from ${SWTPM_IFACE} TPM to --print-capabilities:"
 	echo "Actual   : ${msg}"