MEDIUM: dumpstats: 'show tls-keys' is now able to show secrets

You can now dump the tls-tickets-keys from the unix socket using the
file ID prefixed by an '#' or using a '*' to dump everything.
diff --git a/src/dumpstats.c b/src/dumpstats.c
index 47fc4bc..05d30aa 100644
--- a/src/dumpstats.c
+++ b/src/dumpstats.c
@@ -91,6 +91,7 @@
 	STAT_CLI_O_MLOOK,    /* lookup a map entry */
 	STAT_CLI_O_POOLS,    /* dump memory pools */
 	STAT_CLI_O_TLSK,     /* list all TLS ticket keys references */
+	STAT_CLI_O_TLSK_ENT, /* list all TLS ticket keys entries for a reference */
 	STAT_CLI_O_RESOLVERS,/* dump a resolver's section nameservers counters */
 	STAT_CLI_O_SERVERS_STATE, /* dump server state and changing information */
 	STAT_CLI_O_BACKEND,  /* dump backend list */
@@ -378,6 +379,9 @@
 	"  del map        : delete map entry\n"
 	"  clear map <id> : clear the content of this map\n"
 	"  set ssl <stmt> : set statement for ssl\n"
+#if (defined SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB && TLS_TICKETS_NO > 0)
+	"  show tls-keys [id|*]: show tls keys references or dump tls ticket keys when id specified\n"
+#endif
 	"";
 
 static const char stats_permission_denied_msg[] =
@@ -1442,8 +1446,27 @@
 		}
 		else if (strcmp(args[1], "tls-keys") == 0) {
 #if (defined SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB && TLS_TICKETS_NO > 0)
+			/* no parameter, shows only file list */
+			if (!*args[2]) {
+				appctx->st2 = STAT_ST_INIT;
+				appctx->st0 = STAT_CLI_O_TLSK;
+				return 1;
+			}
+
+			if (args[2][0] == '*') {
+				/* list every TLS ticket keys */
+				appctx->ctx.tlskeys.ref = NULL;
+			} else {
+				appctx->ctx.tlskeys.ref = tlskeys_ref_lookup_ref(args[2]);
+				if(!appctx->ctx.tlskeys.ref) {
+					appctx->ctx.cli.msg = "'show tls-keys' unable to locate referenced filename\n";
+					appctx->st0 = STAT_CLI_PRINT;
+					return 1;
+				}
+			}
 			appctx->st2 = STAT_ST_INIT;
-			appctx->st0 = STAT_CLI_O_TLSK;
+			appctx->st0 = STAT_CLI_O_TLSK_ENT;
+
 #else
 			appctx->ctx.cli.msg = "HAProxy was compiled against a version of OpenSSL "
 						"that doesn't support specifying TLS ticket keys\n";
@@ -2757,6 +2780,10 @@
 				if (stats_tlskeys_list(si))
 					appctx->st0 = STAT_CLI_PROMPT;
 				break;
+			case STAT_CLI_O_TLSK_ENT:
+				if (stats_tlskeys_list(si))
+					appctx->st0 = STAT_CLI_PROMPT;
+				break;
 #endif
 			case STAT_CLI_O_ENV:	/* environment dump */
 				if (stats_dump_env_to_buffer(si))
@@ -6104,6 +6131,7 @@
 #if (defined SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB && TLS_TICKETS_NO > 0)
 static int stats_tlskeys_list(struct stream_interface *si) {
 	struct appctx *appctx = __objt_appctx(si->end);
+	struct tls_keys_ref *ref;
 
 	switch (appctx->st2) {
 	case STAT_ST_INIT:
@@ -6112,30 +6140,57 @@
 		 * later and restart at the state "STAT_ST_INIT".
 		 */
 		chunk_reset(&trash);
-		chunk_appendf(&trash, "# id (file)\n");
+
+		if (appctx->st0 == STAT_CLI_O_TLSK_ENT)
+			chunk_appendf(&trash, "# id secret\n");
+		else
+			chunk_appendf(&trash, "# id (file)\n");
+
 		if (bi_putchk(si_ic(si), &trash) == -1) {
 			si_applet_cant_put(si);
 			return 0;
 		}
 
+		ref = appctx->ctx.tlskeys.ref;
+
 		/* Now, we start the browsing of the references lists.
 		 * Note that the following call to LIST_ELEM return bad pointer. The only
 		 * available field of this pointer is <list>. It is used with the function
 		 * tlskeys_list_get_next() for retruning the first available entry
 		 */
-		appctx->ctx.tlskeys.ref = LIST_ELEM(&tlskeys_reference, struct tls_keys_ref *, list);
-		appctx->ctx.tlskeys.ref = tlskeys_list_get_next(appctx->ctx.tlskeys.ref, &tlskeys_reference);
+		if (ref == NULL) {
+			ref = LIST_ELEM(&tlskeys_reference, struct tls_keys_ref *, list);
+			ref = tlskeys_list_get_next(ref, &tlskeys_reference);
+		}
 
 		appctx->st2 = STAT_ST_LIST;
 		/* fall through */
 
 	case STAT_ST_LIST:
-		while (appctx->ctx.tlskeys.ref) {
+		while (ref) {
+			int i;
+			int head = ref->tls_ticket_enc_index;
+
 			chunk_reset(&trash);
+			if (appctx->st0 == STAT_CLI_O_TLSK_ENT)
+				chunk_appendf(&trash, "# ");
+			chunk_appendf(&trash, "%d (%s)\n", ref->unique_id,
+			              ref->filename);
 
-			chunk_appendf(&trash, "%d (%s)\n", appctx->ctx.tlskeys.ref->unique_id,
-			              appctx->ctx.tlskeys.ref->filename);
+			if (appctx->st0 == STAT_CLI_O_TLSK_ENT) {
+				for (i = 0; i < TLS_TICKETS_NO; i++) {
+					struct chunk *t2 = get_trash_chunk();
+					int b64_len;
 
+					chunk_reset(t2);
+					b64_len = a2base64((char *)(ref->tlskeys + (head + 2 + i) % TLS_TICKETS_NO),
+					                   sizeof(struct tls_sess_key), t2->str, t2->size);
+					if (b64_len < 0)
+						return 0;
+					t2->len = b64_len;
+					chunk_appendf(&trash, "%d.%d %s\n", ref->unique_id, i, t2->str);
+				}
+			}
 			if (bi_putchk(si_ic(si), &trash) == -1) {
 				/* let's try again later from this stream. We add ourselves into
 				 * this stream's users so that it can remove us upon termination.
@@ -6144,8 +6199,12 @@
 				return 0;
 			}
 
+			if (appctx->ctx.tlskeys.ref) /* don't display everything if don't null */
+				break;
+
 			/* get next list entry and check the end of the list */
-			appctx->ctx.tlskeys.ref = tlskeys_list_get_next(appctx->ctx.tlskeys.ref, &tlskeys_reference);
+			ref = tlskeys_list_get_next(ref, &tlskeys_reference);
+
 		}
 
 		appctx->st2 = STAT_ST_FIN;