[PATCH v2 02/11] crypto: Documentation - userspace interface spec

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The userspace interface of the kernel crypto API is documented with
 * a general explanation
 * a discussion of the memory in-place operation
 * the description of the message digest API
 * the description of the symmetric cipher API

In addition, a fully self contained example that can readily be used as
a library is added as well.

Signed-off-by: Stephan Mueller <smueller@xxxxxxxxxx>
CC: Marek Vasut <marex@xxxxxxx>
---
 Documentation/crypto/crypto-API-userspace.txt | 662 ++++++++++++++++++++++++++
 1 file changed, 662 insertions(+)
 create mode 100644 Documentation/crypto/crypto-API-userspace.txt

diff --git a/Documentation/crypto/crypto-API-userspace.txt b/Documentation/crypto/crypto-API-userspace.txt
new file mode 100644
index 0000000..30ca6a7
--- /dev/null
+++ b/Documentation/crypto/crypto-API-userspace.txt
@@ -0,0 +1,662 @@
+Introduction
+============
+
+The concepts of the kernel crypto API visible to kernel space is fully
+applicable to the user space interface as well. Therefore, the kernel crypto API
+high level discussion for the in-kernel use cases applies here as well.
+
+The major difference, however, is that user space can only act as a consumer
+and never as a provider of a transformation or cipher algorithm.
+
+The following covers the user space interface exported by the kernel crypto
+API. It provides a fully working sample code at the that can be used as a
+library for user space applications that require cryptographic services from
+the kernel.
+
+Some details of the in-kernel kernel crypto API aspects do not
+apply to user space, however. This includes the difference between synchronous
+and asynchronous invocations. The user space API call is fully synchronous.
+In addition, only a subset of all cipher types are available as documented
+below.
+
+
+User space API general remarks
+==============================
+
+The kernel crypto API is accessible from user space. Currently, the following
+ciphers are accessible:
+
+	* Message digest including keyed message digest (HMAC, CMAC)
+
+	* Symmetric ciphers
+
+Note, AEAD ciphers are currently not supported via the symmetric cipher
+interface.
+
+The interface is provided via Netlink using the type AF_ALG. In addition, the
+setsockopt option type is SOL_ALG. In case the user space header files do not
+export these flags yet, use the following macros:
+
+#ifndef AF_ALG
+#define AF_ALG 38
+#endif
+#ifndef SOL_ALG
+#define SOL_ALG 279
+#endif
+
+A cipher is accessed with the same name as done for the in-kernel API calls.
+This includes the generic vs. unique naming schema for ciphers as well as the
+enforcement of priorities for generic names.
+
+To interact with the kernel crypto API, a Netlink socket must be created by
+the user space application. User space invokes the cipher operation with the
+send/write system call family. The result of the cipher operation is obtained
+with the read/recv system call family.
+
+The following API calls assume that the Netlink socket descriptor is already
+opened by the user space application and discusses only the kernel crypto API
+specific invocations.
+
+In-place cipher operation
+=========================
+
+Just like the in-kernel operation of the kernel crypto API, the user space
+interface allows the cipher operation in-place. That means that the input buffer
+used for the send/write system call and the output buffer used by the read/recv
+system call may be one and the same. This is of particular interest for
+symmetric cipher operations where a copying of the output data to its final
+destination can be avoided.
+
+Message digest API
+==================
+
+The message digest type to be used for the cipher operation is selected when
+invoking the bind syscall. bind requires the caller to provide a filled
+struct sockaddr data structure. This data structure must be filled as follows:
+
+struct sockaddr_alg sa = {
+	.salg_family = AF_ALG,
+	.salg_type = "hash", /* this selects the hash logic in the kernel */
+	.salg_name = "sha1" /* this is the cipher name */
+};
+
+The salg_type value "hash" applies to message digests and keyed message digests.
+Though, a keyed message digest is referenced by the appropriate salg_name and
+providing a key for the cipher operation.
+
+Using the send() system call, the application provides the data that should be
+processed with the message digest. The send system call allows the following
+flags to be specified:
+
+	* MSG_MORE: If this flag is set, the send system call acts like a
+		    message digest update function where the final hash is not
+		    yet calculated. If the flag is not set, the send system call
+		    calculates the final message digest immediately.
+
+With the recv() system call, the application can read the message digest from
+the kernel crypto API. If the buffer is too small for the message digest, the
+flag MSG_TRUNC is set by the kernel.
+
+In order to set a message digest key, the calling application must use the
+setsockopt() option of ALG_SET_KEY. If the key is not set the HMAC operation is
+performed without the initial HMAC state change caused by the key.
+
+
+Symmetric cipher API
+====================
+
+The operation is very similar to the message digest discussion. During
+initialization, the struct sockaddr data structure must be filled as follows:
+
+struct sockaddr_alg sa = {
+	.salg_family = AF_ALG,
+	.salg_type = "skcipher", /* this selects the symmetric cipher */
+	.salg_name = "cbc(aes)" /* this is the cipher name */
+};
+
+Using the sendmsg() system call, the application provides the data that should
+be processed for encryption or decryption. In addition, the IV is specified
+with the data structure provided by the sendmsg() system call.
+
+The sendmsg system call parameter of struct msghdr is embedded into the
+struct cmsghdr data structure. See recv(2) and cmsg(3) for more information
+on how the cmsghdr data structure is used together with the send/recv system
+call family. That cmsghdr data structure holds the following information
+specified with a separate header instances:
+
+	* specification of the cipher operation type with one of these flags:
+		ALG_OP_ENCRYPT - encryption of data
+		ALG_OP_DECRYPT - decryption of data
+
+	* specification of the IV information marked with the flag ALG_SET_IV
+
+The send system call family allows the following flag to be specified:
+
+	* MSG_MORE: If this flag is set, the send system call acts like a
+		    cipher update function where more input data is expected
+		    with a subsequent invocation of the send system call.
+
+Note: The kernel reports -EINVAL for any unexpected data. The caller must
+make sure that all data matches the constraints given in /proc/crypto for the
+selected cipher.
+
+With the recv() system call, the application can read the result of the
+cipher operation from the kernel crypto API. The output buffer must be at least
+as large as to hold all blocks of the encrypted or decrypted data. If the output
+data size is smaller, only as many blocks are returned that fit into that
+output buffer size.
+
+User space API example
+======================
+
+Compile the following code with the gcc flags of "-Wextra -Wall -pedantic".
+
+/*
+ * Generic kernel crypto API user space interface library
+ *
+ * Copyright (C) 2014, Stephan Mueller <smueller@xxxxxxxxxx>
+ *
+ * Derived from cryptsetup 1.6.4:
+ *
+ * Linux kernel user space API crypto backend implementation (skcipher)
+ *
+ * Copyright (C) 2012, Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2012, Milan Broz
+ *
+ * This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this file; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*
+ * Code from cryptsetup version 1.6.4 used as a basis. See files
+ * lib/crypto_backend/crypto_cipher_kernel.c and
+ * lib/crypto_backend/crypto_kernel.c
+ */
+
+#include <stdio.h>
+
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <linux/if_alg.h>
+#include <stdint.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+
+#ifndef AF_ALG
+#define AF_ALG 38
+#endif
+#ifndef SOL_ALG
+#define SOL_ALG 279
+#endif
+
+/************************************************************
+ * Application interfaces
+ ************************************************************/
+
+/* Cipher handle */
+struct kcapi_handle {
+	int tfmfd;
+	int opfd;
+};
+
+/************************************************************
+ * Internal logic
+ ************************************************************/
+
+/* The in/out should be aligned to page boundary */
+static int _kcapi_cipher_crypt(struct kcapi_handle *handle,
+			       const unsigned char *in, size_t inlen,
+			       unsigned char *out, size_t outlen,
+			       const unsigned char *iv, size_t ivlen,
+			       uint32_t enc)
+{
+	int r = 0;
+	ssize_t ret;
+	struct af_alg_iv *alg_iv;
+	struct cmsghdr *header;
+	uint32_t *type;
+	struct iovec iov;
+	int iv_msg_size = iv ? CMSG_SPACE(sizeof(*alg_iv) + ivlen) : 0;
+	char *buffer = NULL;
+	volatile void *_buffer = NULL;
+	unsigned int bufferlen = CMSG_SPACE(sizeof(*type)) + iv_msg_size;
+	struct msghdr msg;
+
+	memset(&msg, 0, sizeof(msg));
+
+	if (!in || !out || !inlen || !outlen)
+		return -EINVAL;
+
+	if ((!iv && ivlen) || (iv && !ivlen))
+		return -EINVAL;
+
+	buffer = calloc(1, bufferlen);
+	if (!buffer)
+		return -ENOMEM;
+
+	iov.iov_base = (void*)(uintptr_t)in;
+	iov.iov_len = inlen;
+	msg.msg_control = buffer;
+	msg.msg_controllen = bufferlen;
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
+
+	/* encrypt/decrypt operation */
+	header = CMSG_FIRSTHDR(&msg);
+	header->cmsg_level = SOL_ALG;
+	header->cmsg_type = ALG_SET_OP;
+	header->cmsg_len = CMSG_LEN(sizeof(*type));
+	type = (void*)CMSG_DATA(header);
+	*type = enc;
+
+	/* set IV */
+	if (iv) {
+		header = CMSG_NXTHDR(&msg, header);
+		header->cmsg_level = SOL_ALG;
+		header->cmsg_type = ALG_SET_IV;
+		header->cmsg_len = iv_msg_size;
+		alg_iv = (void*)CMSG_DATA(header);
+		alg_iv->ivlen = ivlen;
+		memcpy(alg_iv->iv, iv, ivlen);
+	}
+
+	ret = sendmsg(handle->opfd, &msg, 0);
+	if (ret != (ssize_t)inlen) {
+		r = -EIO;
+		goto bad;
+	}
+
+	ret = read(handle->opfd, out, outlen);
+	if (ret != (ssize_t)outlen)
+		r = -EIO;
+bad:
+	memset(buffer, 0, bufferlen);
+	_buffer = memchr(buffer, 1, bufferlen);
+	if (_buffer)
+		_buffer = '\0';
+	free(buffer);
+	return r;
+}
+
+/************************************************************
+ * API to application
+ ************************************************************/
+
+/*
+ * Initialization of a cipher handle and establishing the connection to
+ * the kernel
+ *
+ * @handle cipher handle filled during the call - output
+ * @type cipher type, one of the following - input:
+ *	 "hash" for message digests (including keyed message digests)
+ *	 "skcipher" for symmetric ciphers
+ * @ciphername kernel crypto API cipher name as specified in
+ *	       /proc/crypto - input
+ *
+ * return: 0 upon success
+ *	   < 0 in case of error
+ *		ENOENT - algorithm not available
+ *		ENOTSUP - AF_ALG family not available
+ *		EINVAL - accept syscall failed
+ */
+int kcapi_cipher_init(struct kcapi_handle *handle,
+		      const char *type, const char *ciphername)
+{
+	struct sockaddr_alg sa;
+
+	memset(&sa, 0, sizeof(sa));
+	sa.salg_family = AF_ALG;
+	snprintf((char *)sa.salg_type, sizeof(sa.salg_type),"%s", type);
+	snprintf((char *)sa.salg_name, sizeof(sa.salg_name),"%s", ciphername);
+
+	handle->tfmfd = socket(AF_ALG, SOCK_SEQPACKET, 0);
+	if (handle->tfmfd == -1)
+		return -ENOTSUP;
+
+	if (bind(handle->tfmfd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
+		close(handle->tfmfd);
+		handle->tfmfd = -1;
+		return -ENOENT;
+	}
+
+	handle->opfd = accept(handle->tfmfd, NULL, 0);
+	if (handle->opfd == -1) {
+		close(handle->tfmfd);
+		handle->tfmfd = -1;
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/*
+ * Close the cipher handle and release resources
+ *
+ * @handle cipher handle to release - input
+ *
+ * return: 0 upon success
+ */
+int kcapi_cipher_destory(struct kcapi_handle *handle)
+{
+	if (handle->tfmfd != -1)
+		close(handle->tfmfd);
+	if (handle->opfd != -1)
+		close(handle->opfd);
+	return 0;
+}
+
+
+/*
+ * Set the key for the cipher handle
+ *
+ * This call is applicable for keyed message digests and symmetric ciphers.
+ *
+ * @handle cipher handle - input
+ * @key key buffer - input
+ * @keylen length of key buffer - input
+ *
+ * return: 0 upon success
+ *	   < 0 in case of error
+ */
+int kcapi_cipher_setkey(struct kcapi_handle *handle,
+			const unsigned char *key, size_t keylen)
+{
+	if (setsockopt(handle->tfmfd, SOL_ALG, ALG_SET_KEY,
+		       key, keylen) == -1)
+		return -EINVAL;
+
+	return 0;
+}
+
+/*
+ * Message digest update function
+ *
+ * @handle cipher handle - input
+ * @buffer holding the data to add to the message digest - input
+ * @len buffer length - input
+ *
+ * return: 0 upon success
+ *	   < 0 in case of error
+ */
+int kcapi_md_update(struct kcapi_handle *handle,
+		    const unsigned char *buffer, size_t len)
+{
+	ssize_t r;
+
+	r = send(handle->opfd, buffer, len, MSG_MORE);
+	if (r < 0 || (size_t)r < len)
+		return -EIO;
+
+	return 0;
+}
+
+/*
+ * Message digest finalization function
+ *
+ * @handle cipher handle - input
+ * @buffer filled with the message digest - output
+ * @len buffer length - input
+ *
+ * return: 0 upon success
+ *	   < 0 in case of error
+ *		EIO - data cannot be obtained
+ *		ENOMEM - buffer is too small for the complete message digest,
+ *			 the buffer is filled with the truncated message digest
+ */
+
+int kcapi_md_final(struct kcapi_handle *handle,
+		   unsigned char *buffer, size_t len)
+{
+	ssize_t r;
+	struct iovec iov;
+	struct msghdr msg;
+
+	iov.iov_base = (void*)(uintptr_t)buffer;
+	iov.iov_len = len;
+	msg.msg_name = NULL;
+	msg.msg_namelen = 0;
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
+	msg.msg_control = NULL;
+	msg.msg_controllen = 0;
+
+	r = recvmsg(handle->opfd, &msg, 0);
+	if (r < 0)
+		return -EIO;
+	if (msg.msg_flags & MSG_TRUNC)
+		return -ENOMEM;
+
+	return 0;
+}
+
+/*
+ * Encrypt data
+ *
+ * @handle cipher handle - input
+ * @in plaintext data buffer - input
+ * @inlen length of in buffer - input
+ * @out ciphertext data buffer - output
+ * @outlen length of out buffer - input
+ * @iv buffer holding the IV (may be NULL if IV is not needed) - input
+ * @ivlen length of iv (should be zero if iv is NULL) - input
+ *
+ * return: 0 upon success
+ *	   < 0 in case of error
+ */
+int kcapi_cipher_encrypt(struct kcapi_handle *handle,
+			 const unsigned char *in, size_t inlen,
+			 unsigned char *out, size_t outlen,
+			 const unsigned char *iv, size_t ivlen)
+{
+	return _kcapi_cipher_crypt(handle, in, inlen, out, outlen,
+				   iv, ivlen, ALG_OP_ENCRYPT);
+}
+
+/*
+ * Decrypt data
+ *
+ * @handle cipher handle - input
+ * @in ciphertext data buffer - input
+ * @inlen length of in buffer - input
+ * @out plaintext data buffer - output
+ * @outlen length of out buffer - input
+ * @iv buffer holding the IV (may be NULL if IV is not needed) - input
+ * @ivlen length of iv (should be zero if iv is NULL) - input
+ *
+ * return: 0 upon success
+ *	   < 0 in case of error
+ */
+int kcapi_cipher_decrypt(struct kcapi_handle *handle,
+			 const unsigned char *in, size_t inlen,
+			 unsigned char *out, size_t outlen,
+			 const unsigned char *iv, size_t ivlen)
+{
+	return _kcapi_cipher_crypt(handle, in, inlen, out, outlen,
+				   iv, ivlen, ALG_OP_DECRYPT);
+}
+
+/************************************************************
+ * Application requiring cryptographic services
+ ************************************************************/
+
+static char hex_char_map_l[] = { '0', '1', '2', '3', '4', '5', '6', '7',
+				 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+static char hex_char_map_u[] = { '0', '1', '2', '3', '4', '5', '6', '7',
+				 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+static char hex_char(unsigned int bin, int u)
+{
+	if (bin < sizeof(hex_char_map_l))
+		return (u) ? hex_char_map_u[bin] : hex_char_map_l[bin];
+	return 'X';
+}
+
+/*
+ * Convert binary string into hex representation
+ * @bin input buffer with binary data
+ * @binlen length of bin
+ * @hex output buffer to store hex data
+ * @hexlen length of already allocated hex buffer (should be at least
+ *	   twice binlen -- if not, only a fraction of binlen is converted)
+ * @u case of hex characters (0=>lower case, 1=>upper case)
+ */
+static void bin2hex(const unsigned char *bin, size_t binlen,
+		    char *hex, size_t hexlen, int u)
+{
+	size_t i = 0;
+	size_t chars = (binlen > (hexlen / 2)) ? (hexlen / 2) : binlen;
+
+	for (i = 0; i < chars; i++) {
+		hex[(i*2)] = hex_char((bin[i] >> 4), u);
+		hex[((i*2)+1)] = hex_char((bin[i] & 0x0f), u);
+	}
+}
+
+
+int main(int argc, char *argv[])
+{
+	struct kcapi_handle handle;
+#define BUFLEN 32
+	unsigned char inbuf[BUFLEN]; /* Plaintext */
+#define IVLEN 16
+	unsigned char ivbuf[IVLEN]; /* IV */
+	unsigned char outbuf[BUFLEN]; /* ciphertext for encryption */
+	unsigned char outbuf2[BUFLEN]; /* plaintext for decryption */
+	char hexbuf[BUFLEN * 2 + 1];
+
+	(void)argc;
+	(void)argv;
+
+	/*
+	 * Calculate a message digest
+	 */
+	if (kcapi_cipher_init(&handle, "hash", "sha256")) {
+		printf("Allocation of hash failed\n");
+		return(1);
+	}
+	memcpy(inbuf, "\x00\x01\x02\x03\x04\x05\x06\x07"
+		      "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
+		      "\x00\x01\x02\x03\x04\x05\x06\x07"
+		      "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", BUFLEN);
+	if (kcapi_md_update(&handle, inbuf, BUFLEN)) {
+		printf("Hash update of buffer failed\n");
+		return(1);
+	}
+	if (kcapi_md_final(&handle, outbuf, BUFLEN)) {
+		printf("Hash final failed\n");
+		return(1);
+	}
+	kcapi_cipher_destory(&handle);
+	memset(hexbuf, 0, BUFLEN * 2 + 1);
+	bin2hex(outbuf, BUFLEN, hexbuf, BUFLEN * 2 + 1, 0);
+	printf("Calculated hash %s\n", hexbuf);
+
+	/*
+	 * Calculate a keyed message digest
+	 */
+	if (kcapi_cipher_init(&handle, "hash", "hmac(sha256)")) {
+		printf("Allocation of HMAC failed\n");
+		return(1);
+	}
+	memcpy(inbuf, "\x00\x01\x02\x03\x04\x05\x06\x07"
+		      "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
+		      "\x00\x01\x02\x03\x04\x05\x06\x07"
+		      "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", BUFLEN);
+	if (kcapi_cipher_setkey(&handle, inbuf, BUFLEN)) {
+		printf("HMAC setkey failed\n");
+		return(1);
+	}
+	if (kcapi_md_update(&handle, inbuf, BUFLEN)) {
+		printf("HMAC update of buffer failed\n");
+		return(1);
+	}
+	if (kcapi_md_final(&handle, outbuf, BUFLEN)) {
+		printf("HMAC final failed\n");
+		return(1);
+	}
+	kcapi_cipher_destory(&handle);
+	memset(hexbuf, 0, BUFLEN * 2 + 1);
+	bin2hex(outbuf, BUFLEN, hexbuf, BUFLEN * 2 + 1, 0);
+	printf("Calculated hmac %s\n", hexbuf);
+
+	/*
+	 * Encrypt data
+	 */
+	if (kcapi_cipher_init(&handle, "skcipher", "cbc(aes)")) {
+		printf("Allocation of cipher failed\n");
+		return(1);
+	}
+
+	/* Set key */
+	memcpy(inbuf, "\x00\x01\x02\x03\x04\x05\x06\x07"
+		      "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
+		      "\x00\x01\x02\x03\x04\x05\x06\x07"
+		      "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", BUFLEN);
+	if (kcapi_cipher_setkey(&handle, inbuf, BUFLEN)) {
+		printf("AES setkey failed\n");
+		return(1);
+	}
+
+	/* Prepare IV */
+	memcpy(ivbuf, "\x00\x01\x02\x03\x04\x05\x06\x07"
+		      "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", IVLEN);
+
+	/*
+	 * Encrypt inbuf -- key and plaintext are the same in this example
+	 *
+	 * It is perfectly legal to use inbuf for the plaintext and ciphertext
+	 * pointers. That would mean that after the encryption operation, the
+	 * plaintext is overwritten with the cipher text.
+	 */
+	if (kcapi_cipher_encrypt(&handle, inbuf, BUFLEN,
+				 outbuf, BUFLEN, ivbuf, IVLEN)) {
+		printf("Encryption buffer failed\n");
+		return(1);
+	}
+
+	/* outbuf now contains the cipher text */
+	memset(hexbuf, 0, BUFLEN * 2 + 1);
+	bin2hex(outbuf, BUFLEN, hexbuf, BUFLEN * 2 + 1, 0);
+	printf("Encrypted data %s\n", hexbuf);
+
+	/*
+	 * Decrypt previously encrypted data
+	 *
+	 * Just like for the encryption operation, the ciphertext buffer pointer
+	 * and the plaintext buffer pointer may point to the same memory
+	 * location. After completion of this operation, the ciphertext is
+	 * overwritten with the plaintext.
+	 */
+	if (kcapi_cipher_decrypt(&handle, outbuf, BUFLEN,
+				 outbuf2, BUFLEN, ivbuf, IVLEN)) {
+		printf("Decryption buffer failed\n");
+		return(1);
+	}
+	kcapi_cipher_destory(&handle);
+	memset(hexbuf, 0, BUFLEN * 2 + 1);
+	bin2hex(outbuf2, BUFLEN, hexbuf, BUFLEN * 2 + 1, 0);
+	printf("Decrypted data %s\n", hexbuf);
+	if (!memcmp(inbuf, outbuf2, BUFLEN))
+		printf("Decrypted data match original plaintext as expected\n");
+	else
+		printf("FAILURE: Decrypted data does not match original plaintext\n");
+
+	return 0;
+}
+
+Author
+======
+
+Stephan Mueller <smueller@xxxxxxxxxx>
-- 
2.1.0


--
To unsubscribe from this list: send the line "unsubscribe linux-crypto" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Kernel]     [Gnu Classpath]     [Gnu Crypto]     [DM Crypt]     [Netfilter]     [Bugtraq]

  Powered by Linux