tpmdd-devel Archive on lore.kernel.org
 help / color / Atom feed
From: David Howells <dhowells-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
To: denkenz-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org,
	jarkko.sakkinen-VuQAYsv1563Yd54FQh9/CA@public.gmane.org,
	jejb-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org
Cc: tpmdd-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org,
	linux-integrity-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	keyrings-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-security-module-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Subject: [PATCH 17/23] TPMLIB: Provide a wrapper to load bytes out of the reply
Date: Tue, 21 Aug 2018 16:58:45 +0100
Message-ID: <153486712522.13066.16199758850117818658.stgit@warthog.procyon.org.uk> (raw)
In-Reply-To: <153486700916.13066.12870860668352070081.stgit-S6HVgzuS8uM4Awkfq6JHfwNdhmdF6hFW@public.gmane.org>

Provide a wrapper for memcpy to load bytes out of the reply, similar to
LOAD32() and friends.

Signed-off-by: David Howells <dhowells-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
---

 drivers/char/tpm/tpm-library.c |   87 ++++++++++++++++++++--------------------
 drivers/char/tpm/tpm-library.h |   43 +++++++++++++++++---
 include/linux/tpm.h            |    3 +
 3 files changed, 82 insertions(+), 51 deletions(-)

diff --git a/drivers/char/tpm/tpm-library.c b/drivers/char/tpm/tpm-library.c
index 46cd12d30ec6..329b5c3f23a2 100644
--- a/drivers/char/tpm/tpm-library.c
+++ b/drivers/char/tpm/tpm-library.c
@@ -199,7 +199,7 @@ out:
  * @...: Pairs of size and pointer of data elements to load into hash
  * @0,NULL: Terminator
  */
-static int TSS_checkhmac1(unsigned char *buffer,
+static int TSS_checkhmac1(struct tpm_buf *tb,
 			  __be32 ordinal,
 			  const struct tpm_odd_nonce *ononce,
 			  const unsigned char *key, unsigned keylen,
@@ -219,14 +219,16 @@ static int TSS_checkhmac1(unsigned char *buffer,
 	va_list argp;
 	int ret;
 
-	bufsize = LOAD32(buffer, TPM_SIZE_OFFSET);
-	tag = LOAD16(buffer, 0);
-	result = LOAD32BE(buffer, TPM_RETURN_OFFSET);
+	SET_BUF_OFFSET(tb, 0);
+	tag = LOAD16(tb);
+	bufsize = LOAD32(tb);
+	result = LOAD32BE(tb);
 	if (tag == TPM_TAG_RSP_COMMAND)
 		return 0;
 	if (tag != TPM_TAG_RSP_AUTH1_COMMAND)
 		return -EINVAL;
-	authdata = buffer + bufsize - SHA1_DIGEST_SIZE;
+
+	authdata = tb->data + bufsize - SHA1_DIGEST_SIZE;
 	continueflag = authdata - 1;
 	enonce = (void *)continueflag - TPM_NONCE_SIZE;
 
@@ -255,7 +257,7 @@ static int TSS_checkhmac1(unsigned char *buffer,
 		dpos = va_arg(argp, unsigned int);
 		if (!dlen && !dpos)
 			break;
-		ret = crypto_shash_update(&sdesc->shash, buffer + dpos, dlen);
+		ret = crypto_shash_update(&sdesc->shash, tb->data + dpos, dlen);
 		if (ret < 0)
 			break;
 	}
@@ -296,7 +298,7 @@ out:
  *
  * verify the AUTH2_COMMAND (unseal) result from TPM
  */
-static int TSS_checkhmac2(const unsigned char *buffer,
+static int TSS_checkhmac2(struct tpm_buf *tb,
 			  __be32 ordinal,
 			  const struct tpm_odd_nonce *ononce,
 			  const unsigned char *key1, unsigned keylen1,
@@ -321,17 +323,17 @@ static int TSS_checkhmac2(const unsigned char *buffer,
 	va_list argp;
 	int ret;
 
-	bufsize = LOAD32(buffer, TPM_SIZE_OFFSET);
-	tag = LOAD16(buffer, 0);
-	result = LOAD32BE(buffer, TPM_RETURN_OFFSET);
+	bufsize = LOAD32(tb);
+	tag = LOAD16(tb);
+	result = LOAD32BE(tb);
 
 	if (tag == TPM_TAG_RSP_COMMAND)
 		return 0;
 	if (tag != TPM_TAG_RSP_AUTH2_COMMAND)
 		return -EINVAL;
-	authdata1 = buffer + bufsize - (SHA1_DIGEST_SIZE + 1
+	authdata1 = tb->data + bufsize - (SHA1_DIGEST_SIZE + 1
 			+ SHA1_DIGEST_SIZE + SHA1_DIGEST_SIZE);
-	authdata2 = buffer + bufsize - (SHA1_DIGEST_SIZE);
+	authdata2 = tb->data + bufsize - (SHA1_DIGEST_SIZE);
 	continueflag1 = authdata1 - 1;
 	continueflag2 = authdata2 - 1;
 	enonce1 = (const void *)continueflag1 - TPM_NONCE_SIZE;
@@ -363,7 +365,7 @@ static int TSS_checkhmac2(const unsigned char *buffer,
 		dpos = va_arg(argp, unsigned int);
 		if (!dlen && !dpos)
 			break;
-		ret = crypto_shash_update(&sdesc->shash, buffer + dpos, dlen);
+		ret = crypto_shash_update(&sdesc->shash, tb->data + dpos, dlen);
 		if (ret < 0)
 			break;
 	}
@@ -404,17 +406,19 @@ out:
  * For key specific tpm requests, we will generate and send our
  * own TPM command packets using the drivers send function.
  */
-static int tpm_send_dump(struct tpm_chip *chip,
-			 unsigned char *cmd, size_t buflen, const char *desc)
+static int tpm_send_dump(struct tpm_chip *chip, struct tpm_buf *cmd,
+			 const char *desc)
 {
 	int rc;
 
 	dump_tpm_buf(cmd);
-	rc = tpm_send_command(chip, cmd, buflen, desc);
+	rc = tpm_send_command(chip, cmd->data, MAX_BUF_SIZE, desc);
 	dump_tpm_buf(cmd);
 	if (rc > 0)
 		/* Can't return positive return codes values to keyctl */
 		rc = -EPERM;
+	else
+		SET_BUF_OFFSET(cmd, TPM_DATA_OFFSET);
 	return rc;
 }
 
@@ -442,16 +446,14 @@ static int tpm_create_osap(struct tpm_chip *chip,
 	store32(tb, keyhandle);
 	store_s(tb, ononce.data, TPM_NONCE_SIZE);
 
-	ret = tpm_send_dump(chip, tb->data, MAX_BUF_SIZE,
-			    "creating OSAP session");
+	ret = tpm_send_dump(chip, tb, "creating OSAP session");
 	if (ret < 0)
 		return ret;
 
-	s->handle = LOAD32(tb->data, TPM_DATA_OFFSET);
-	memcpy(s->enonce.data, &(tb->data[TPM_DATA_OFFSET + sizeof(uint32_t)]),
-	       TPM_NONCE_SIZE);
-	memcpy(enonce.data, &(tb->data[TPM_DATA_OFFSET + sizeof(uint32_t) +
-				  TPM_NONCE_SIZE]), TPM_NONCE_SIZE);
+	s->handle = LOAD32(tb);
+	LOAD_S(tb, s->enonce.data, TPM_NONCE_SIZE);
+	LOAD_S(tb, enonce.data, TPM_NONCE_SIZE);
+
 	return TSS_rawhmac(s->secret, keyauth, SHA1_DIGEST_SIZE,
 			   TPM_NONCE_SIZE, enonce.data,
 			   TPM_NONCE_SIZE, ononce.data,
@@ -470,14 +472,12 @@ static int tpm_create_oiap(struct tpm_chip *chip, struct tpm_buf *tb,
 	store16(tb, TPM_TAG_RQU_COMMAND);
 	store32(tb, TPM_OIAP_SIZE);
 	store32(tb, TPM_ORD_OIAP);
-	ret = tpm_send_dump(chip, tb->data, MAX_BUF_SIZE,
-			    "creating OIAP session");
+	ret = tpm_send_dump(chip, tb, "creating OIAP session");
 	if (ret < 0)
 		return ret;
 
-	*handle = LOAD32(tb->data, TPM_DATA_OFFSET);
-	memcpy(enonce->data, &tb->data[TPM_DATA_OFFSET + sizeof(uint32_t)],
-	       TPM_NONCE_SIZE);
+	*handle = LOAD32(tb);
+	LOAD_S(tb, enonce->data, TPM_NONCE_SIZE);
 	return 0;
 }
 
@@ -590,27 +590,29 @@ int tpm_seal(struct tpm_chip *chip,
 	store_8(tb, cont);
 	store_s(tb, td->pubauth, SHA1_DIGEST_SIZE);
 
-	ret = tpm_send_dump(chip, tb->data, MAX_BUF_SIZE,
-			    "sealing data");
+	ret = tpm_send_dump(chip, tb, "sealing data");
 	if (ret < 0)
 		goto out;
 
-	/* calculate the size of the returned encrypted data */
-	sealinfosize = LOAD32(tb->data, TPM_DATA_OFFSET + sizeof(uint32_t));
-	encdatasize = LOAD32(tb->data, TPM_DATA_OFFSET + sizeof(uint32_t) +
-			     sizeof(uint32_t) + sealinfosize);
-	storedsize = sizeof(uint32_t) + sizeof(uint32_t) + sealinfosize +
-	    sizeof(uint32_t) + encdatasize;
+	/* Look inside the TPM_STORED_DATA object to calculate the size of the
+	 * returned encrypted data.
+	 */
+	SET_BUF_OFFSET(tb, TPM_DATA_OFFSET + sizeof(uint32_t));
+	sealinfosize = LOAD32(tb);
+	SET_BUF_OFFSET(tb, TPM_DATA_OFFSET + sizeof(uint32_t) * 2 + sealinfosize);
+	storedsize = sizeof(uint32_t) * 2 + sealinfosize +
+		sizeof(uint32_t) + encdatasize;
 
 	/* check the HMAC in the response */
-	ret = TSS_checkhmac1(tb->data, ordinal_be, &td->ononce,
+	ret = TSS_checkhmac1(tb, ordinal_be, &td->ononce,
 			     sess.secret, SHA1_DIGEST_SIZE,
 			     /* 3S */ storedsize, TPM_DATA_OFFSET,
 			     0, NULL);
 
 	/* copy the encrypted data to caller's buffer */
 	if (!ret) {
-		memcpy(encbuffer, tb->data + TPM_DATA_OFFSET, storedsize);
+		SET_BUF_OFFSET(tb, TPM_DATA_OFFSET);
+		LOAD_S(tb, encbuffer, storedsize);
 		*_enclen = storedsize;
 	}
 out:
@@ -697,15 +699,14 @@ int tpm_unseal(struct tpm_chip *chip, struct tpm_buf *tb,
 	store_8(tb, cont);
 	store_s(tb, authdata2, SHA1_DIGEST_SIZE);
 
-	ret = tpm_send_dump(chip, tb->data, MAX_BUF_SIZE,
-			    "unsealing data");
+	ret = tpm_send_dump(chip, tb, "unsealing data");
 	if (ret < 0) {
 		pr_info("authhmac failed (%d)\n", ret);
 		return ret;
 	}
 
-	*_rawlen = LOAD32(tb->data, TPM_DATA_OFFSET);
-	ret = TSS_checkhmac2(tb->data, ordinal, &ononce,
+	*_rawlen = LOAD32(tb);
+	ret = TSS_checkhmac2(tb, ordinal, &ononce,
 			     keyauth, SHA1_DIGEST_SIZE,
 			     decauth, SHA1_DIGEST_SIZE,
 			     /* 3S */ sizeof(uint32_t), TPM_DATA_OFFSET,
@@ -715,7 +716,7 @@ int tpm_unseal(struct tpm_chip *chip, struct tpm_buf *tb,
 		pr_info("TSS_checkhmac2 failed (%d)\n", ret);
 		return ret;
 	}
-	memcpy(rawbuffer, tb->data + TPM_DATA_OFFSET + sizeof(uint32_t), *_rawlen);
+	LOAD_S(tb, rawbuffer, *_rawlen);
 	return 0;
 }
 EXPORT_SYMBOL_GPL(tpm_unseal);
diff --git a/drivers/char/tpm/tpm-library.h b/drivers/char/tpm/tpm-library.h
index c12d451704a2..3e288624b8e8 100644
--- a/drivers/char/tpm/tpm-library.h
+++ b/drivers/char/tpm/tpm-library.h
@@ -11,10 +11,6 @@
  */
 
 
-#define LOAD32BE(buffer, offset) (*(__be32 *)&buffer[(offset)])
-#define LOAD16(buffer, offset)	(be16_to_cpu(*(__be16 *)&buffer[(offset)]))
-#define LOAD32(buffer, offset)	(be32_to_cpu(LOAD32BE(buffer, (offset))))
-
 struct tpm_even_nonce {
 	unsigned char data[TPM_NONCE_SIZE];
 };
@@ -29,6 +25,38 @@ struct tpm_osapsess {
 	struct tpm_even_nonce enonce;
 };
 
+static inline void SET_BUF_OFFSET(struct tpm_buf *buffer, unsigned offset)
+{
+	buffer->offset = offset;
+}
+
+static inline uint16_t LOAD16(struct tpm_buf *buffer)
+{
+	__be16 *p = (__be16 *)(buffer->data + buffer->offset);
+	buffer->offset += 2;
+	return be16_to_cpup(p);
+}
+
+static inline __be32 LOAD32BE(struct tpm_buf *buffer)
+{
+	__be32 val = *(__be32 *)(buffer->data + buffer->offset);
+	buffer->offset += 4;
+	return val;
+}
+
+static inline uint32_t LOAD32(struct tpm_buf *buffer)
+{
+	__be32 *p = (__be32 *)(buffer->data + buffer->offset);
+	buffer->offset += 4;
+	return be32_to_cpup(p);
+}
+
+static inline void LOAD_S(struct tpm_buf *buffer, void *data_buffer, size_t amount)
+{
+	memcpy(data_buffer, buffer->data + buffer->offset, amount);
+	buffer->offset += amount;
+}
+
 static inline void store_8(struct tpm_buf *buf, unsigned char value)
 {
 	buf->data[buf->len++] = value;
@@ -70,13 +98,14 @@ static inline void dump_sess(struct tpm_osapsess *s)
 		       16, 1, &s->enonce, SHA1_DIGEST_SIZE, 0);
 }
 
-static inline void dump_tpm_buf(unsigned char *buf)
+static inline void dump_tpm_buf(struct tpm_buf *tb)
 {
 	int len;
 
 	pr_info("\ntpm buffer\n");
-	len = LOAD32(buf, TPM_SIZE_OFFSET);
-	print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1, buf, len, 0);
+	SET_BUF_OFFSET(tb, TPM_SIZE_OFFSET);
+	len = LOAD32(tb);
+	print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1, tb->data, len, 0);
 }
 
 #else
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index cbd13e03a869..398bfaef2325 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -110,7 +110,8 @@ enum tpm_entity_type {
 };
 
 struct tpm_buf {
-	int len;
+	unsigned short len;
+	unsigned short offset;
 	unsigned char data[MAX_BUF_SIZE];
 };
 


------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot

  parent reply index

Thread overview: 42+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-08-21 15:56 tpm: Provide a TPM access library David Howells
     [not found] ` <153486700916.13066.12870860668352070081.stgit-S6HVgzuS8uM4Awkfq6JHfwNdhmdF6hFW@public.gmane.org>
2018-08-21 15:56   ` [PATCH 01/23] TPM: Add new TPMs to the tail of the list to prevent inadvertent change of dev David Howells
     [not found]     ` <153486701644.13066.13372706238885253812.stgit-S6HVgzuS8uM4Awkfq6JHfwNdhmdF6hFW@public.gmane.org>
2018-08-21 18:30       ` Jason Gunthorpe
     [not found]         ` <20180821183004.GB25543-uk2M96/98Pc@public.gmane.org>
2018-08-24  6:24           ` Jarkko Sakkinen
     [not found]             ` <20180824062434.GB3584-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
2018-08-24  6:25               ` Jarkko Sakkinen
     [not found]                 ` <20180824062557.GC3584-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
2018-08-24 11:22                   ` Mimi Zohar
2018-08-24  6:19       ` Jarkko Sakkinen
2018-08-21 15:57   ` [PATCH 02/23] TPM: Provide a facility for a userspace TPM emulator David Howells
     [not found]     ` <153486702302.13066.15889029286852815542.stgit-S6HVgzuS8uM4Awkfq6JHfwNdhmdF6hFW@public.gmane.org>
2018-08-21 18:31       ` Jason Gunthorpe
     [not found]         ` <20180821183140.GD25543-uk2M96/98Pc@public.gmane.org>
2018-08-24  6:29           ` Jarkko Sakkinen
2018-08-21 15:57   ` [PATCH 03/23] TPM: Provide a platform driver for the user emulator driver David Howells
     [not found]     ` <153486702979.13066.16900998092976336647.stgit-S6HVgzuS8uM4Awkfq6JHfwNdhmdF6hFW@public.gmane.org>
2018-08-24  6:30       ` Jarkko Sakkinen
2018-08-21 15:57   ` [PATCH 04/23] TPM: Expose struct tpm_chip and related find_get and put functions David Howells
     [not found]     ` <153486703636.13066.16209594327379341518.stgit-S6HVgzuS8uM4Awkfq6JHfwNdhmdF6hFW@public.gmane.org>
2018-08-21 18:31       ` Jason Gunthorpe
     [not found]     ` <20180821183108.GC25543-uk2M96/98Pc@public.gmane.org>
2018-08-21 18:35       ` David Howells
2018-08-21 15:57   ` [PATCH 05/23] TPM: Use struct tpm_chip rather than chip number as interface parameter David Howells
     [not found]     ` <153486704294.13066.8818198038331415342.stgit-S6HVgzuS8uM4Awkfq6JHfwNdhmdF6hFW@public.gmane.org>
2018-08-24  7:42       ` Jarkko Sakkinen
2018-08-21 15:57   ` [PATCH 06/23] TPM: Move ordinal values from interface file to header with other ordinals David Howells
2018-08-21 15:57   ` [PATCH 07/23] TPM: Consolidate tpm_send(), transmit_cmd() and tpm_transmit() David Howells
2018-08-21 15:57   ` [PATCH 08/23] TPMLIB: Break TPM bits out of security/keys/trusted.c David Howells
     [not found]     ` <153486706322.13066.3105842100625841410.stgit-S6HVgzuS8uM4Awkfq6JHfwNdhmdF6hFW@public.gmane.org>
2018-08-24  7:52       ` Jarkko Sakkinen
     [not found]         ` <20180824075227.GG3584-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
2018-08-24  8:49           ` Jarkko Sakkinen
     [not found]         ` <20180824084930.GA10266-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
2018-08-24  9:33           ` David Howells
     [not found]             ` <25340.1535103190-S6HVgzuS8uM4Awkfq6JHfwNdhmdF6hFW@public.gmane.org>
2018-08-27  8:25               ` Jarkko Sakkinen
2018-08-21 15:57   ` [PATCH 09/23] TPMLIB: Do some source cleanups David Howells
2018-08-21 15:57   ` [PATCH 10/23] TPMLIB: Better format calls to TSS_*hmac*() David Howells
2018-08-21 15:58   ` [PATCH 11/23] TPMLIB: Put banner comments on public TPM library functions David Howells
2018-08-21 15:58   ` [PATCH 12/23] TPMLIB: Create tpm_{even, odd}_nonce structs to represent nonces David Howells
2018-08-21 15:58   ` [PATCH 13/23] TPMLIB: Rename store8() and storebytes() David Howells
2018-08-21 15:58   ` [PATCH 14/23] TPMLIB: Make store_s() take a void* data argument, not unsigned char* David Howells
2018-08-21 15:58   ` [PATCH 15/23] TPMLIB: Use __be32 rather than int32_t and use cpu_to_beX() and co David Howells
2018-08-21 15:58   ` [PATCH 16/23] TPMLIB: Put more comments into the HMAC generation functions David Howells
2018-08-21 15:58   ` David Howells [this message]
2018-08-21 15:58   ` [PATCH 18/23] TPMLIB: Encapsulate XOR-based encryption with authkey derivative David Howells
2018-08-21 15:58   ` [PATCH 19/23] TPMLIB: Add some debugging code David Howells
2018-08-21 15:59   ` [PATCH 20/23] TPMLIB: Implement call to TPM_CreateWrapKey David Howells
2018-08-21 15:59   ` [PATCH 21/23] TPMLIB: Implement call to TPM_LoadKey2 David Howells
2018-08-21 15:59   ` [PATCH 22/23] TPMLIB: Provide call for TPM_FlushSpecific David Howells
2018-08-21 15:59   ` [PATCH 23/23] TPM: Add an asymmetric key subtype for handling TPM-based keys David Howells
2018-08-22 14:19   ` tpm: Provide a TPM access library Jarkko Sakkinen
     [not found] ` <20180822141956.GA28110-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
2018-08-22 14:45   ` David Howells
     [not found]     ` <13611.1534949106-S6HVgzuS8uM4Awkfq6JHfwNdhmdF6hFW@public.gmane.org>
2018-08-23 22:49       ` Jarkko Sakkinen

Reply instructions:

You may reply publically to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=153486712522.13066.16199758850117818658.stgit@warthog.procyon.org.uk \
    --to=dhowells-h+wxahxf7alqt0dzr+alfa@public.gmane.org \
    --cc=denkenz-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org \
    --cc=jarkko.sakkinen-VuQAYsv1563Yd54FQh9/CA@public.gmane.org \
    --cc=jejb-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org \
    --cc=keyrings-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
    --cc=linux-integrity-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
    --cc=linux-security-module-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
    --cc=tpmdd-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

tpmdd-devel Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/tpmdd-devel/0 tpmdd-devel/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 tpmdd-devel tpmdd-devel/ https://lore.kernel.org/tpmdd-devel \
		tpmdd-devel@lists.sourceforge.net tpmdd-devel@archiver.kernel.org
	public-inbox-index tpmdd-devel


Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/net.sourceforge.lists.tpmdd-devel


AGPL code for this site: git clone https://public-inbox.org/ public-inbox