From patchwork Thu Nov 12 12:57:53 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Howells X-Patchwork-Id: 11899943 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.9 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 93607C388F7 for ; Thu, 12 Nov 2020 12:58:07 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 39A2B21D91 for ; Thu, 12 Nov 2020 12:58:07 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="VDAtS6Za" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728237AbgKLM6G (ORCPT ); Thu, 12 Nov 2020 07:58:06 -0500 Received: from us-smtp-delivery-124.mimecast.com ([63.128.21.124]:24699 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728155AbgKLM6C (ORCPT ); Thu, 12 Nov 2020 07:58:02 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1605185880; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=EKLlZYAbQMt1tMpfeGrxE8DYlcnIAOJnTTrk+3E2Iec=; b=VDAtS6ZaCfS2mrd+8qwsXWkFG6lRyuiAC5EsNJC3wB60oq2+yShNwe1YTJHJaPDxvCjYBT MDzl6Fa//f6/d6YGeIJCz8Qbls0PSkoMdRrKfsJKPr5ymycV2Av4kNCZD0qwveMnZ/oR84 CgTJyGvWhyI0li7mnJIPf2MpvYd4WcM= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-84-kZ4_MPqZPryJ8JMNzo8XtA-1; Thu, 12 Nov 2020 07:57:58 -0500 X-MC-Unique: kZ4_MPqZPryJ8JMNzo8XtA-1 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 961031007B2A; Thu, 12 Nov 2020 12:57:56 +0000 (UTC) Received: from warthog.procyon.org.uk (ovpn-115-47.rdu2.redhat.com [10.10.115.47]) by smtp.corp.redhat.com (Postfix) with ESMTP id 921AF5D9E8; Thu, 12 Nov 2020 12:57:54 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH 01/18] crypto/krb5: Implement Kerberos crypto core From: David Howells To: herbert@gondor.apana.org.au, bfields@fieldses.org Cc: dhowells@redhat.com, trond.myklebust@hammerspace.com, linux-crypto@vger.kernel.org, linux-afs@lists.infradead.org, linux-cifs@vger.kernel.org, linux-nfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 12 Nov 2020 12:57:53 +0000 Message-ID: <160518587378.2277919.8315623921666827993.stgit@warthog.procyon.org.uk> In-Reply-To: <160518586534.2277919.14475638653680231924.stgit@warthog.procyon.org.uk> References: <160518586534.2277919.14475638653680231924.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Provide core structures, an encoding-type registry and basic module and config bits for a generic Kerberos crypto library. Signed-off-by: David Howells --- crypto/Kconfig | 1 + crypto/Makefile | 1 + crypto/krb5/Kconfig | 11 ++++++ crypto/krb5/Makefile | 9 +++++ crypto/krb5/internal.h | 87 ++++++++++++++++++++++++++++++++++++++++++++++++ crypto/krb5/main.c | 42 +++++++++++++++++++++++ include/crypto/krb5.h | 67 +++++++++++++++++++++++++++++++++++++ 7 files changed, 218 insertions(+) create mode 100644 crypto/krb5/Kconfig create mode 100644 crypto/krb5/Makefile create mode 100644 crypto/krb5/internal.h create mode 100644 crypto/krb5/main.c create mode 100644 include/crypto/krb5.h diff --git a/crypto/Kconfig b/crypto/Kconfig index 094ef56ab7b4..0d5ca023bb77 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -1940,5 +1940,6 @@ source "lib/crypto/Kconfig" source "drivers/crypto/Kconfig" source "crypto/asymmetric_keys/Kconfig" source "certs/Kconfig" +source "crypto/krb5/Kconfig" endif # if CRYPTO diff --git a/crypto/Makefile b/crypto/Makefile index b279483fba50..732467ed3c94 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -197,3 +197,4 @@ obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += asymmetric_keys/ obj-$(CONFIG_CRYPTO_HASH_INFO) += hash_info.o crypto_simd-y := simd.o obj-$(CONFIG_CRYPTO_SIMD) += crypto_simd.o +obj-$(CONFIG_CRYPTO_KRB5) += krb5/ diff --git a/crypto/krb5/Kconfig b/crypto/krb5/Kconfig new file mode 100644 index 000000000000..881754500732 --- /dev/null +++ b/crypto/krb5/Kconfig @@ -0,0 +1,11 @@ +config CRYPTO_KRB5 + tristate "Kerberos 5 crypto" + select CRYPTO_MANAGER + select CRYPTO_SKCIPHER + select CRYPTO_HASH_INFO + select CRYPTO_SHA1 + select CRYPTO_CBC + select CRYPTO_CTS + select CRYPTO_AES + help + Provide Kerberos-5-based security. diff --git a/crypto/krb5/Makefile b/crypto/krb5/Makefile new file mode 100644 index 000000000000..071ce2ff82e5 --- /dev/null +++ b/crypto/krb5/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for asymmetric cryptographic keys +# + +krb5-y += \ + main.o + +obj-$(CONFIG_CRYPTO_KRB5) += krb5.o diff --git a/crypto/krb5/internal.h b/crypto/krb5/internal.h new file mode 100644 index 000000000000..d2e3da7f101e --- /dev/null +++ b/crypto/krb5/internal.h @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Kerberos5 crypto internals + * + * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#include + +/* + * Profile used for key derivation and encryption. + */ +struct krb5_crypto_profile { + /* Pseudo-random function */ + int (*calc_PRF)(const struct krb5_enctype *krb5, + const struct krb5_buffer *protocol_key, + const struct krb5_buffer *octet_string, + struct krb5_buffer *result, + gfp_t gfp); + + /* Checksum key derivation */ + int (*calc_Kc)(const struct krb5_enctype *krb5, + const struct krb5_buffer *TK, + const struct krb5_buffer *usage_constant, + struct krb5_buffer *Kc, + gfp_t gfp); + + /* Encryption key derivation */ + int (*calc_Ke)(const struct krb5_enctype *krb5, + const struct krb5_buffer *TK, + const struct krb5_buffer *usage_constant, + struct krb5_buffer *Ke, + gfp_t gfp); + + /* Integrity key derivation */ + int (*calc_Ki)(const struct krb5_enctype *krb5, + const struct krb5_buffer *TK, + const struct krb5_buffer *usage_constant, + struct krb5_buffer *Ki, + gfp_t gfp); + + /* Encrypt data in-place, inserting confounder and checksum. */ + ssize_t (*encrypt)(const struct krb5_enctype *krb5, + struct krb5_enc_keys *keys, + struct scatterlist *sg, unsigned nr_sg, size_t sg_len, + size_t data_offset, size_t data_len, + bool preconfounded); + + /* Decrypt data in-place, removing confounder and checksum */ + int (*decrypt)(const struct krb5_enctype *krb5, + struct krb5_enc_keys *keys, + struct scatterlist *sg, unsigned nr_sg, + size_t *_offset, size_t *_len, + int *_error_code); + + /* Generate a MIC on part of a packet, inserting the checksum */ + ssize_t (*get_mic)(const struct krb5_enctype *krb5, + struct crypto_shash *shash, + const struct krb5_buffer *metadata, + struct scatterlist *sg, unsigned nr_sg, size_t sg_len, + size_t data_offset, size_t data_len); + + /* Verify the MIC on a piece of data, removing the checksum */ + int (*verify_mic)(const struct krb5_enctype *krb5, + struct crypto_shash *shash, + const struct krb5_buffer *metadata, + struct scatterlist *sg, unsigned nr_sg, + size_t *_offset, size_t *_len, + int *_error_code); +}; + +/* + * Crypto size/alignment rounding convenience macros. + */ +#define crypto_roundup(X) round_up((X), CRYPTO_MINALIGN) + +#define krb5_shash_size(TFM) \ + crypto_roundup(sizeof(struct shash_desc) + crypto_shash_descsize(TFM)) +#define krb5_skcipher_size(TFM) \ + crypto_roundup(sizeof(struct skcipher_request) + crypto_skcipher_reqsize(TFM)) +#define krb5_digest_size(TFM) \ + crypto_roundup(crypto_shash_digestsize(TFM)) +#define krb5_sync_skcipher_size(TFM) \ + krb5_skcipher_size(&(TFM)->base) +#define krb5_sync_skcipher_ivsize(TFM) \ + crypto_roundup(crypto_sync_skcipher_ivsize(TFM)) +#define round16(x) (((x) + 15) & ~15) diff --git a/crypto/krb5/main.c b/crypto/krb5/main.c new file mode 100644 index 000000000000..58d40252adc9 --- /dev/null +++ b/crypto/krb5/main.c @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* RxGK transport key derivation. + * + * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include "internal.h" + +MODULE_DESCRIPTION("Kerberos 5 crypto"); +MODULE_AUTHOR("Red Hat, Inc."); +MODULE_LICENSE("GPL"); + +static const struct krb5_enctype *const krb5_supported_enctypes[] = { +}; + +/** + * crypto_krb5_find_enctype - Find the handler for a Kerberos5 encryption type + * @enctype: The standard Kerberos encryption type number + * + * Look up a Kerberos encryption type by number. If successful, returns a + * pointer to the type tables; returns NULL otherwise. + */ +const struct krb5_enctype *crypto_krb5_find_enctype(u32 enctype) +{ + const struct krb5_enctype *krb5; + size_t i; + + for (i = 0; i < ARRAY_SIZE(krb5_supported_enctypes); i++) { + krb5 = krb5_supported_enctypes[i]; + if (krb5->etype == enctype) + return krb5; + } + + return NULL; +} +EXPORT_SYMBOL(crypto_krb5_find_enctype); diff --git a/include/crypto/krb5.h b/include/crypto/krb5.h new file mode 100644 index 000000000000..2bd6cfe50b85 --- /dev/null +++ b/include/crypto/krb5.h @@ -0,0 +1,67 @@ +/* Kerberos 5 crypto + * + * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _CRYPTO_KRB5_H +#define _CRYPTO_KRB5_H + +struct crypto_shash; +struct scatterlist; + +struct krb5_buffer { + unsigned int len; + void *data; +}; + +/* + * Encryption key and checksum for RxGK encryption. These always come + * as a pair as per RFC3961 encrypt(). + */ +struct krb5_enc_keys { + struct crypto_sync_skcipher *Ke; /* Encryption key */ + struct crypto_shash *Ki; /* Checksum key */ +}; + +/* + * Kerberos encoding type definition. + */ +struct krb5_enctype { + int etype; /* Encryption (key) type */ + int ctype; /* Checksum type */ + const char *name; /* "Friendly" name */ + const char *encrypt_name; /* Crypto encrypt name */ + const char *cksum_name; /* Crypto checksum name */ + const char *hash_name; /* Crypto hash name */ + u16 block_len; /* Length of encryption block */ + u16 conf_len; /* Length of confounder (normally == block_len) */ + u16 cksum_len; /* Length of checksum */ + u16 key_bytes; /* Length of raw key, in bytes */ + u16 key_len; /* Length of final key, in bytes */ + u16 hash_len; /* Length of hash in bytes */ + u16 prf_len; /* Length of PRF() result in bytes */ + u16 Kc_len; /* Length of Kc in bytes */ + u16 Ke_len; /* Length of Ke in bytes */ + u16 Ki_len; /* Length of Ki in bytes */ + bool keyed_cksum; /* T if a keyed cksum */ + bool pad; /* T if should pad */ + + const struct krb5_crypto_profile *profile; + + int (*random_to_key)(const struct krb5_enctype *krb5, + const struct krb5_buffer *in, + struct krb5_buffer *out); /* complete key generation */ +}; + +/* + * main.c + */ +extern const struct krb5_enctype *crypto_krb5_find_enctype(u32 enctype); + +#endif /* _CRYPTO_KRB5_H */ From patchwork Thu Nov 12 12:58:01 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Howells X-Patchwork-Id: 11900109 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.9 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4E15DC56201 for ; Thu, 12 Nov 2020 13:01:30 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id E043421D91 for ; Thu, 12 Nov 2020 13:01:29 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="I/aNE1D/" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728285AbgKLM6T (ORCPT ); Thu, 12 Nov 2020 07:58:19 -0500 Received: from us-smtp-delivery-124.mimecast.com ([63.128.21.124]:24561 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728253AbgKLM6M (ORCPT ); Thu, 12 Nov 2020 07:58:12 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1605185890; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=DAVAInVcp6zb/CSQMkiobU5XXmB1jibPH4NSxa0idq0=; b=I/aNE1D/2yuSv72lZtKu/yVaE7/x4RAKkRysmRU/nXHyFY1Gq4d0LOcRATO+vc9xXvPYQ1 hMQwUWtiq2BCwXcYGLXEfjRsXfnGVdake1eLoN1c9GxfoKy9PSa3gfjBI7u/FpG3nOWVNi k1Xkq2PJFdTaGYfEH6sq7IZAEy2bWnY= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-266-Cv7XutOCNf62MnfBD1BTcg-1; Thu, 12 Nov 2020 07:58:06 -0500 X-MC-Unique: Cv7XutOCNf62MnfBD1BTcg-1 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 714EF64156; Thu, 12 Nov 2020 12:58:04 +0000 (UTC) Received: from warthog.procyon.org.uk (ovpn-115-47.rdu2.redhat.com [10.10.115.47]) by smtp.corp.redhat.com (Postfix) with ESMTP id 944E65B4AD; Thu, 12 Nov 2020 12:58:02 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH 02/18] crypto/krb5: Add some constants out of sunrpc headers From: David Howells To: herbert@gondor.apana.org.au, bfields@fieldses.org Cc: dhowells@redhat.com, trond.myklebust@hammerspace.com, linux-crypto@vger.kernel.org, linux-afs@lists.infradead.org, linux-cifs@vger.kernel.org, linux-nfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 12 Nov 2020 12:58:01 +0000 Message-ID: <160518588181.2277919.13100188317180416955.stgit@warthog.procyon.org.uk> In-Reply-To: <160518586534.2277919.14475638653680231924.stgit@warthog.procyon.org.uk> References: <160518586534.2277919.14475638653680231924.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Add some constants from the sunrpc headers. Signed-off-by: David Howells --- include/crypto/krb5.h | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/include/crypto/krb5.h b/include/crypto/krb5.h index 2bd6cfe50b85..a7e4ab4e1348 100644 --- a/include/crypto/krb5.h +++ b/include/crypto/krb5.h @@ -15,6 +15,45 @@ struct crypto_shash; struct scatterlist; +/* per Kerberos v5 protocol spec crypto types from the wire. + * these get mapped to linux kernel crypto routines. + */ +#define KRB5_ENCTYPE_NULL 0x0000 +#define KRB5_ENCTYPE_DES_CBC_CRC 0x0001 /* DES cbc mode with CRC-32 */ +#define KRB5_ENCTYPE_DES_CBC_MD4 0x0002 /* DES cbc mode with RSA-MD4 */ +#define KRB5_ENCTYPE_DES_CBC_MD5 0x0003 /* DES cbc mode with RSA-MD5 */ +#define KRB5_ENCTYPE_DES_CBC_RAW 0x0004 /* DES cbc mode raw */ +/* XXX deprecated? */ +#define KRB5_ENCTYPE_DES3_CBC_SHA 0x0005 /* DES-3 cbc mode with NIST-SHA */ +#define KRB5_ENCTYPE_DES3_CBC_RAW 0x0006 /* DES-3 cbc mode raw */ +#define KRB5_ENCTYPE_DES_HMAC_SHA1 0x0008 +#define KRB5_ENCTYPE_DES3_CBC_SHA1 0x0010 +#define KRB5_ENCTYPE_AES128_CTS_HMAC_SHA1_96 0x0011 +#define KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96 0x0012 +#define KRB5_ENCTYPE_ARCFOUR_HMAC 0x0017 +#define KRB5_ENCTYPE_ARCFOUR_HMAC_EXP 0x0018 +#define KRB5_ENCTYPE_UNKNOWN 0x01ff + +#define KRB5_CKSUMTYPE_CRC32 0x0001 +#define KRB5_CKSUMTYPE_RSA_MD4 0x0002 +#define KRB5_CKSUMTYPE_RSA_MD4_DES 0x0003 +#define KRB5_CKSUMTYPE_DESCBC 0x0004 +#define KRB5_CKSUMTYPE_RSA_MD5 0x0007 +#define KRB5_CKSUMTYPE_RSA_MD5_DES 0x0008 +#define KRB5_CKSUMTYPE_NIST_SHA 0x0009 +#define KRB5_CKSUMTYPE_HMAC_SHA1_DES3 0x000c +#define KRB5_CKSUMTYPE_HMAC_SHA1_96_AES128 0x000f +#define KRB5_CKSUMTYPE_HMAC_SHA1_96_AES256 0x0010 +#define KRB5_CKSUMTYPE_HMAC_MD5_ARCFOUR -138 /* Microsoft md5 hmac cksumtype */ + +/* + * Constants used for key derivation + */ +/* from rfc3961 */ +#define KEY_USAGE_SEED_CHECKSUM (0x99) +#define KEY_USAGE_SEED_ENCRYPTION (0xAA) +#define KEY_USAGE_SEED_INTEGRITY (0x55) + struct krb5_buffer { unsigned int len; void *data; From patchwork Thu Nov 12 12:58:09 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Howells X-Patchwork-Id: 11900101 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.9 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 40AFBC388F7 for ; Thu, 12 Nov 2020 13:01:20 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id C66FF21D7F for ; Thu, 12 Nov 2020 13:01:19 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="fwO5y6k6" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728348AbgKLNBT (ORCPT ); Thu, 12 Nov 2020 08:01:19 -0500 Received: from us-smtp-delivery-124.mimecast.com ([63.128.21.124]:26025 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728275AbgKLM6V (ORCPT ); Thu, 12 Nov 2020 07:58:21 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1605185898; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=vlX81bfxkSnCx/kV2/K9DOewa6eFmGPkRqimXiZZar0=; b=fwO5y6k6zd03Ce/KDyFroReOuZ1euJ+jkRgXOWIjqTDhzwJ/mU7cBjKUcc9MC6QaRCNIaX Gy+VatQtMzCW+oobnaRL03Tig2cHOogJxjqevgwqxUHaUEvOWhwt6gJGeYAOX9x0mxGUOX byZtY2iCdvNVc5QaueLO1w6f5H/VwdI= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-12-qb-NUw5ONfGnrv_CijFFuQ-1; Thu, 12 Nov 2020 07:58:14 -0500 X-MC-Unique: qb-NUw5ONfGnrv_CijFFuQ-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 79BE080364D; Thu, 12 Nov 2020 12:58:12 +0000 (UTC) Received: from warthog.procyon.org.uk (ovpn-115-47.rdu2.redhat.com [10.10.115.47]) by smtp.corp.redhat.com (Postfix) with ESMTP id 74D5027BDC; Thu, 12 Nov 2020 12:58:10 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH 03/18] crypto/krb5: Provide infrastructure and key derivation From: David Howells To: herbert@gondor.apana.org.au, bfields@fieldses.org Cc: dhowells@redhat.com, trond.myklebust@hammerspace.com, linux-crypto@vger.kernel.org, linux-afs@lists.infradead.org, linux-cifs@vger.kernel.org, linux-nfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 12 Nov 2020 12:58:09 +0000 Message-ID: <160518588968.2277919.3783200728891264713.stgit@warthog.procyon.org.uk> In-Reply-To: <160518586534.2277919.14475638653680231924.stgit@warthog.procyon.org.uk> References: <160518586534.2277919.14475638653680231924.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Provide key derivation interface functions and a helper to implement the PRF+ function from rfc4402. Signed-off-by: David Howells --- crypto/krb5/Makefile | 1 crypto/krb5/kdf.c | 223 +++++++++++++++++++++++++++++++++++++++++++++++++ include/crypto/krb5.h | 29 ++++++ 3 files changed, 253 insertions(+) create mode 100644 crypto/krb5/kdf.c diff --git a/crypto/krb5/Makefile b/crypto/krb5/Makefile index 071ce2ff82e5..b764c4d09bf2 100644 --- a/crypto/krb5/Makefile +++ b/crypto/krb5/Makefile @@ -4,6 +4,7 @@ # krb5-y += \ + kdf.o \ main.o obj-$(CONFIG_CRYPTO_KRB5) += krb5.o diff --git a/crypto/krb5/kdf.c b/crypto/krb5/kdf.c new file mode 100644 index 000000000000..8ef7ea31ee8a --- /dev/null +++ b/crypto/krb5/kdf.c @@ -0,0 +1,223 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Kerberos key derivation. + * + * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include "internal.h" + +/** + * crypto_krb5_free_enc_keys - Free an encryption keypair + * @e: The key pair to free. + */ +void crypto_krb5_free_enc_keys(struct krb5_enc_keys *e) +{ + if (e->Ke) + crypto_free_sync_skcipher(e->Ke); + if (e->Ki) + crypto_free_shash(e->Ki); + e->Ke = NULL; + e->Ki = NULL; +} +EXPORT_SYMBOL(crypto_krb5_free_enc_keys); + +/** + * crypto_krb5_calc_PRFplus - Calculate PRF+ [RFC4402] + * @krb5: The encryption type to use + * @K: The protocol key for the pseudo-random function + * @L: The length of the output + * @S: The input octet string + * @result: Result buffer, sized to krb5->prf_len + * @gfp: Allocation restrictions + * + * Calculate the kerberos pseudo-random function, PRF+() by the following + * method: + * + * PRF+(K, L, S) = truncate(L, T1 || T2 || .. || Tn) + * Tn = PRF(K, n || S) + * [rfc4402 sec 2] + */ +int crypto_krb5_calc_PRFplus(const struct krb5_enctype *krb5, + const struct krb5_buffer *K, + unsigned int L, + const struct krb5_buffer *S, + struct krb5_buffer *result, + gfp_t gfp) +{ + struct krb5_buffer T_series, Tn, n_S; + void *buffer; + int ret, n = 1; + + Tn.len = krb5->prf_len; + T_series.len = 0; + n_S.len = 4 + S->len; + + buffer = kzalloc(round16(L + Tn.len) + round16(n_S.len), gfp); + if (!buffer) + return -ENOMEM; + + T_series.data = buffer; + n_S.data = buffer + round16(L + Tn.len); + memcpy(n_S.data + 4, S->data, S->len); + + while (T_series.len < L) { + *(__be32 *)(n_S.data) = htonl(n); + Tn.data = T_series.data + Tn.len * (n - 1); + ret = krb5->profile->calc_PRF(krb5, K, &n_S, &Tn, gfp); + if (ret < 0) + goto err; + T_series.len += Tn.len; + n++; + } + + /* Truncate to L */ + memcpy(result->data, T_series.data, L); + ret = 0; + +err: + kfree_sensitive(buffer); + return ret; +} +EXPORT_SYMBOL(crypto_krb5_calc_PRFplus); + +/** + * crypto_krb5_get_Kc - Derive key Kc and install into a hash + * @krb5: The encryption type to use + * @TK: The base key + * @usage: The key usage number + * @key: Prepped buffer to store the key into + * @_shash: Where to put the hash (or NULL if not wanted) + * @gfp: Allocation restrictions + * + * Derive the Kerberos Kc checksumming key and, optionally, allocate a hash and + * install the key into it, returning the hash. The key is stored into the + * prepared buffer. + */ +int crypto_krb5_get_Kc(const struct krb5_enctype *krb5, + const struct krb5_buffer *TK, + u32 usage, + struct krb5_buffer *key, + struct crypto_shash **_shash, + gfp_t gfp) +{ + struct crypto_shash *shash; + int ret; + u8 buf[CRYPTO_MINALIGN] __aligned(CRYPTO_MINALIGN); + struct krb5_buffer usage_constant = { .len = 5, .data = buf }; + + *(__be32 *)buf = cpu_to_be32(usage); + buf[4] = KEY_USAGE_SEED_CHECKSUM; + + key->len = krb5->Kc_len; + ret = krb5->profile->calc_Kc(krb5, TK, &usage_constant, key, gfp); + if (ret < 0) + return ret; + + if (_shash) { + shash = crypto_alloc_shash(krb5->cksum_name, 0, 0); + if (IS_ERR(shash)) + return (PTR_ERR(shash) == -ENOENT) ? -ENOPKG : PTR_ERR(shash); + *_shash = shash; + ret = crypto_shash_setkey(shash, key->data, key->len); + } + + return ret; +} +EXPORT_SYMBOL(crypto_krb5_get_Kc); + +/** + * crypto_krb5_get_Ke - Derive key Ke and install into an skcipher + * @krb5: The encryption type to use + * @TK: The base key + * @usage: The key usage number + * @key: Prepped buffer to store the key into + * @_ci: Where to put the cipher (or NULL if not wanted) + * @gfp: Allocation restrictions + * + * Derive the Kerberos Ke encryption key and, optionally, allocate an skcipher + * and install the key into it, returning the cipher. The key is stored into + * the prepared buffer. + */ +int crypto_krb5_get_Ke(const struct krb5_enctype *krb5, + const struct krb5_buffer *TK, + u32 usage, + struct krb5_buffer *key, + struct crypto_sync_skcipher **_ci, + gfp_t gfp) +{ + struct crypto_sync_skcipher *ci; + int ret; + u8 buf[CRYPTO_MINALIGN] __aligned(CRYPTO_MINALIGN); + struct krb5_buffer usage_constant = { .len = 5, .data = buf }; + + *(__be32 *)buf = cpu_to_be32(usage); + buf[4] = KEY_USAGE_SEED_ENCRYPTION; + + key->len = krb5->Ke_len; + ret = krb5->profile->calc_Ke(krb5, TK, &usage_constant, key, gfp); + if (ret < 0) + return ret; + + if (_ci) { + ci = crypto_alloc_sync_skcipher(krb5->encrypt_name, 0, 0); + if (IS_ERR(ci)) + return (PTR_ERR(ci) == -ENOENT) ? -ENOPKG : PTR_ERR(ci); + *_ci = ci; + ret = crypto_sync_skcipher_setkey(ci, key->data, key->len); + } + + return ret; +} +EXPORT_SYMBOL(crypto_krb5_get_Ke); + +/** + * crypto_krb5_get_Ki - Derive key Ki and install into a hash + * @krb5: The encryption type to use + * @TK: The base key + * @usage: The key usage number + * @key: Prepped buffer to store the key into + * @_shash: Where to put the hash (or NULL if not wanted) + * @gfp: Allocation restrictions + * + * Derive the Kerberos Ki integrity checksum key and, optionally, allocate a + * hash and install the key into it, returning the hash. The key is stored + * into the prepared buffer. + */ +int crypto_krb5_get_Ki(const struct krb5_enctype *krb5, + const struct krb5_buffer *TK, + u32 usage, + struct krb5_buffer *key, + struct crypto_shash **_shash, + gfp_t gfp) +{ + struct crypto_shash *shash; + int ret; + u8 buf[CRYPTO_MINALIGN] __aligned(CRYPTO_MINALIGN); + struct krb5_buffer usage_constant = { .len = 5, .data = buf }; + + *(__be32 *)buf = cpu_to_be32(usage); + buf[4] = KEY_USAGE_SEED_INTEGRITY; + + key->len = krb5->Ki_len; + ret = krb5->profile->calc_Kc(krb5, TK, &usage_constant, key, gfp); + if (ret < 0) + return ret; + + if (_shash) { + shash = crypto_alloc_shash(krb5->cksum_name, 0, 0); + if (IS_ERR(shash)) + return (PTR_ERR(shash) == -ENOENT) ? -ENOPKG : PTR_ERR(shash); + *_shash = shash; + ret = crypto_shash_setkey(shash, key->data, key->len); + } + + return ret; +} +EXPORT_SYMBOL(crypto_krb5_get_Ki); diff --git a/include/crypto/krb5.h b/include/crypto/krb5.h index a7e4ab4e1348..04286bacaf06 100644 --- a/include/crypto/krb5.h +++ b/include/crypto/krb5.h @@ -103,4 +103,33 @@ struct krb5_enctype { */ extern const struct krb5_enctype *crypto_krb5_find_enctype(u32 enctype); +/* + * kdf.c + */ +extern void crypto_krb5_free_enc_keys(struct krb5_enc_keys *e); +extern int crypto_krb5_calc_PRFplus(const struct krb5_enctype *krb5, + const struct krb5_buffer *K, + unsigned int L, + const struct krb5_buffer *S, + struct krb5_buffer *result, + gfp_t gfp); +extern int crypto_krb5_get_Kc(const struct krb5_enctype *krb5, + const struct krb5_buffer *TK, + u32 usage, + struct krb5_buffer *key, + struct crypto_shash **_shash, + gfp_t gfp); +extern int crypto_krb5_get_Ke(const struct krb5_enctype *krb5, + const struct krb5_buffer *TK, + u32 usage, + struct krb5_buffer *key, + struct crypto_sync_skcipher **_ci, + gfp_t gfp); +extern int crypto_krb5_get_Ki(const struct krb5_enctype *krb5, + const struct krb5_buffer *TK, + u32 usage, + struct krb5_buffer *key, + struct crypto_shash **_shash, + gfp_t gfp); + #endif /* _CRYPTO_KRB5_H */ From patchwork Thu Nov 12 12:58:17 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Howells X-Patchwork-Id: 11899945 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.9 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id F0B8CC56201 for ; Thu, 12 Nov 2020 12:58:34 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 9207B21D91 for ; Thu, 12 Nov 2020 12:58:34 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="Q8ocJ1+C" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728318AbgKLM6c (ORCPT ); Thu, 12 Nov 2020 07:58:32 -0500 Received: from us-smtp-delivery-124.mimecast.com ([216.205.24.124]:35739 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728317AbgKLM62 (ORCPT ); Thu, 12 Nov 2020 07:58:28 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1605185906; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=IGSn+5CyB84dfho0axy1WaM5GiYqaApgDpziJorTYmw=; b=Q8ocJ1+CRCM3ydhkrHwHvK2a12Jlfd6J/+OKWTWdeaYhOeTaKJYb20UqBMlAStFzOtKGdt GTGE1GOAQYy2QTBWdDmlmZFBKL7zs+ad5RsZwXQU9il0drMUiHuesWd974CjxcCXV10ZZr to6cBZH1osAoKbq/oVF09oP88zkcT3Q= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-249-pR2l0Xx-PxSWNb84SRurMg-1; Thu, 12 Nov 2020 07:58:22 -0500 X-MC-Unique: pR2l0Xx-PxSWNb84SRurMg-1 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id CC51D1007B04; Thu, 12 Nov 2020 12:58:20 +0000 (UTC) Received: from warthog.procyon.org.uk (ovpn-115-47.rdu2.redhat.com [10.10.115.47]) by smtp.corp.redhat.com (Postfix) with ESMTP id 85CB65D9E4; Thu, 12 Nov 2020 12:58:18 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH 04/18] crypto/krb5: Implement the Kerberos5 rfc3961 key derivation From: David Howells To: herbert@gondor.apana.org.au, bfields@fieldses.org Cc: dhowells@redhat.com, trond.myklebust@hammerspace.com, linux-crypto@vger.kernel.org, linux-afs@lists.infradead.org, linux-cifs@vger.kernel.org, linux-nfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 12 Nov 2020 12:58:17 +0000 Message-ID: <160518589770.2277919.8989214872237533700.stgit@warthog.procyon.org.uk> In-Reply-To: <160518586534.2277919.14475638653680231924.stgit@warthog.procyon.org.uk> References: <160518586534.2277919.14475638653680231924.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Implement the simplified crypto profile for Kerberos 5 rfc3961 with the pseudo-random function, PRF(), from section 5.3 and the key derivation function, DK() from section 5.1. Signed-off-by: David Howells --- crypto/krb5/Makefile | 3 crypto/krb5/internal.h | 6 + crypto/krb5/rfc3961_simplified.c | 394 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 402 insertions(+), 1 deletion(-) create mode 100644 crypto/krb5/rfc3961_simplified.c diff --git a/crypto/krb5/Makefile b/crypto/krb5/Makefile index b764c4d09bf2..67824c44aac3 100644 --- a/crypto/krb5/Makefile +++ b/crypto/krb5/Makefile @@ -5,6 +5,7 @@ krb5-y += \ kdf.o \ - main.o + main.o \ + rfc3961_simplified.o obj-$(CONFIG_CRYPTO_KRB5) += krb5.o diff --git a/crypto/krb5/internal.h b/crypto/krb5/internal.h index d2e3da7f101e..874dddada713 100644 --- a/crypto/krb5/internal.h +++ b/crypto/krb5/internal.h @@ -5,6 +5,7 @@ * Written by David Howells (dhowells@redhat.com) */ +#include #include /* @@ -85,3 +86,8 @@ struct krb5_crypto_profile { #define krb5_sync_skcipher_ivsize(TFM) \ crypto_roundup(crypto_sync_skcipher_ivsize(TFM)) #define round16(x) (((x) + 15) & ~15) + +/* + * rfc3961_simplified.c + */ +extern const struct krb5_crypto_profile rfc3961_simplified_profile; diff --git a/crypto/krb5/rfc3961_simplified.c b/crypto/krb5/rfc3961_simplified.c new file mode 100644 index 000000000000..0a5c689f6354 --- /dev/null +++ b/crypto/krb5/rfc3961_simplified.c @@ -0,0 +1,394 @@ +/* rfc3961 Kerberos 5 simplified crypto profile. + * + * Parts borrowed from net/sunrpc/auth_gss/. + */ +/* + * COPYRIGHT (c) 2008 + * The Regents of the University of Michigan + * ALL RIGHTS RESERVED + * + * Permission is granted to use, copy, create derivative works + * and redistribute this software and such derivative works + * for any purpose, so long as the name of The University of + * Michigan is not used in any advertising or publicity + * pertaining to the use of distribution of this software + * without specific, written prior authorization. If the + * above copyright notice or any other identification of the + * University of Michigan is included in any copy of any + * portion of this software, then the disclaimer below must + * also be included. + * + * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION + * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY + * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF + * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING + * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE + * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE + * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR + * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING + * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN + * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGES. + */ + +/* + * Copyright (C) 1998 by the FundsXpress, INC. + * + * All rights reserved. + * + * Export of this software from the United States of America may require + * a specific license from the United States Government. It is the + * responsibility of any person or organization contemplating export to + * obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of FundsXpress. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. FundsXpress makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* + * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include "internal.h" + +/* Maximum blocksize for the supported crypto algorithms */ +#define KRB5_MAX_BLOCKSIZE (16) + +static int rfc3961_do_encrypt(struct crypto_sync_skcipher *tfm, void *iv, + const struct krb5_buffer *in, struct krb5_buffer *out) +{ + struct scatterlist sg[1]; + u8 local_iv[KRB5_MAX_BLOCKSIZE] __aligned(KRB5_MAX_BLOCKSIZE) = {0}; + SYNC_SKCIPHER_REQUEST_ON_STACK(req, tfm); + int ret; + + if (WARN_ON(in->len != out->len)) + return -EINVAL; + if (out->len % crypto_sync_skcipher_blocksize(tfm) != 0) + return -EINVAL; + + if (crypto_sync_skcipher_ivsize(tfm) > KRB5_MAX_BLOCKSIZE) + return -EINVAL; + + if (iv) + memcpy(local_iv, iv, crypto_sync_skcipher_ivsize(tfm)); + + memcpy(out->data, in->data, out->len); + sg_init_one(sg, out->data, out->len); + + skcipher_request_set_sync_tfm(req, tfm); + skcipher_request_set_callback(req, 0, NULL, NULL); + skcipher_request_set_crypt(req, sg, sg, out->len, local_iv); + + ret = crypto_skcipher_encrypt(req); + skcipher_request_zero(req); + return ret; +} + +/* + * Calculate an unkeyed basic hash. + */ +static int rfc3961_calc_H(const struct krb5_enctype *krb5, + const struct krb5_buffer *data, + struct krb5_buffer *digest, + gfp_t gfp) +{ + struct crypto_shash *tfm; + struct shash_desc *desc; + size_t desc_size; + int ret = -ENOMEM; + + tfm = crypto_alloc_shash(krb5->hash_name, 0, 0); + if (IS_ERR(tfm)) + return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm); + + desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); + + desc = kzalloc(desc_size, GFP_KERNEL); + if (!desc) + goto error_tfm; + + digest->len = crypto_shash_digestsize(tfm); + digest->data = kzalloc(digest->len, gfp); + if (!digest->data) + goto error_desc; + + desc->tfm = tfm; + ret = crypto_shash_init(desc); + if (ret < 0) + goto error_digest; + + ret = crypto_shash_finup(desc, data->data, data->len, digest->data); + if (ret < 0) + goto error_digest; + + goto error_desc; + +error_digest: + kfree_sensitive(digest->data); +error_desc: + kfree_sensitive(desc); +error_tfm: + crypto_free_shash(tfm); + return ret; +} + +/* + * This is the n-fold function as described in rfc3961, sec 5.1 + * Taken from MIT Kerberos and modified. + */ +static void rfc3961_nfold(const struct krb5_buffer *source, struct krb5_buffer *result) +{ + const u8 *in = source->data; + u8 *out = result->data; + unsigned long ulcm; + unsigned int inbits, outbits; + int byte, i, msbit; + + /* the code below is more readable if I make these bytes instead of bits */ + inbits = source->len; + outbits = result->len; + + /* first compute lcm(n,k) */ + ulcm = lcm(inbits, outbits); + + /* now do the real work */ + memset(out, 0, outbits); + byte = 0; + + /* this will end up cycling through k lcm(k,n)/k times, which + * is correct */ + for (i = ulcm-1; i >= 0; i--) { + /* compute the msbit in k which gets added into this byte */ + msbit = ( + /* first, start with the msbit in the first, + * unrotated byte */ + ((inbits << 3) - 1) + + /* then, for each byte, shift to the right + * for each repetition */ + (((inbits << 3) + 13) * (i/inbits)) + + /* last, pick out the correct byte within + * that shifted repetition */ + ((inbits - (i % inbits)) << 3) + ) % (inbits << 3); + + /* pull out the byte value itself */ + byte += (((in[((inbits - 1) - (msbit >> 3)) % inbits] << 8) | + (in[((inbits) - (msbit >> 3)) % inbits])) + >> ((msbit & 7) + 1)) & 0xff; + + /* do the addition */ + byte += out[i % outbits]; + out[i % outbits] = byte & 0xff; + + /* keep around the carry bit, if any */ + byte >>= 8; + } + + /* if there's a carry bit left over, add it back in */ + if (byte) { + for (i = outbits - 1; i >= 0; i--) { + /* do the addition */ + byte += out[i]; + out[i] = byte & 0xff; + + /* keep around the carry bit, if any */ + byte >>= 8; + } + } +} + +/* + * Calculate a derived key, DK(Base Key, Well-Known Constant) + * + * DK(Key, Constant) = random-to-key(DR(Key, Constant)) + * DR(Key, Constant) = k-truncate(E(Key, Constant, initial-cipher-state)) + * K1 = E(Key, n-fold(Constant), initial-cipher-state) + * K2 = E(Key, K1, initial-cipher-state) + * K3 = E(Key, K2, initial-cipher-state) + * K4 = ... + * DR(Key, Constant) = k-truncate(K1 | K2 | K3 | K4 ...) + * [rfc3961 sec 5.1] + */ +static int rfc3961_calc_DK(const struct krb5_enctype *krb5, + const struct krb5_buffer *inkey, + const struct krb5_buffer *in_constant, + struct krb5_buffer *result, + gfp_t gfp) +{ + unsigned int blocksize, keybytes, keylength, n; + struct krb5_buffer inblock, outblock, rawkey; + struct crypto_sync_skcipher *cipher; + int ret = -EINVAL; + + blocksize = krb5->block_len; + keybytes = krb5->key_bytes; + keylength = krb5->key_len; + + if (inkey->len != keylength || result->len != keylength) + return -EINVAL; + + cipher = crypto_alloc_sync_skcipher(krb5->encrypt_name, 0, 0); + if (IS_ERR(cipher)) { + ret = (PTR_ERR(cipher) == -ENOENT) ? -ENOPKG : PTR_ERR(cipher); + goto err_return; + } + ret = crypto_sync_skcipher_setkey(cipher, inkey->data, inkey->len); + if (ret < 0) + goto err_free_cipher; + + ret = -ENOMEM; + inblock.data = kzalloc(blocksize * 2 + keybytes, gfp); + if (!inblock.data) + goto err_free_cipher; + + inblock.len = blocksize; + outblock.data = inblock.data + blocksize; + outblock.len = blocksize; + rawkey.data = outblock.data + blocksize; + rawkey.len = keybytes; + + /* initialize the input block */ + + if (in_constant->len == inblock.len) + memcpy(inblock.data, in_constant->data, inblock.len); + else + rfc3961_nfold(in_constant, &inblock); + + /* loop encrypting the blocks until enough key bytes are generated */ + n = 0; + while (n < rawkey.len) { + rfc3961_do_encrypt(cipher, NULL, &inblock, &outblock); + + if (keybytes - n <= outblock.len) { + memcpy(rawkey.data + n, outblock.data, keybytes - n); + break; + } + + memcpy(rawkey.data + n, outblock.data, outblock.len); + memcpy(inblock.data, outblock.data, outblock.len); + n += outblock.len; + } + + /* postprocess the key */ + ret = krb5->random_to_key(krb5, &rawkey, result); + + kfree_sensitive(inblock.data); +err_free_cipher: + crypto_free_sync_skcipher(cipher); +err_return: + return ret; +} + +/* + * Calculate single encryption, E() + * + * E(Key, octets) + */ +static int rfc3961_calc_E(const struct krb5_enctype *krb5, + const struct krb5_buffer *key, + const struct krb5_buffer *in_data, + struct krb5_buffer *result, + gfp_t gfp) +{ + struct crypto_sync_skcipher *cipher; + int ret; + + cipher = crypto_alloc_sync_skcipher(krb5->encrypt_name, 0, 0); + if (IS_ERR(cipher)) { + ret = (PTR_ERR(cipher) == -ENOENT) ? -ENOPKG : PTR_ERR(cipher); + goto err; + } + + ret = crypto_sync_skcipher_setkey(cipher, key->data, key->len); + if (ret < 0) + goto err_free; + + ret = rfc3961_do_encrypt(cipher, NULL, in_data, result); + +err_free: + crypto_free_sync_skcipher(cipher); +err: + return ret; +} + +/* + * Calculate the pseudo-random function, PRF(). + * + * tmp1 = H(octet-string) + * tmp2 = truncate tmp1 to multiple of m + * PRF = E(DK(protocol-key, prfconstant), tmp2, initial-cipher-state) + * + * The "prfconstant" used in the PRF operation is the three-octet string + * "prf". + * [rfc3961 sec 5.3] + */ +static int rfc3961_calc_PRF(const struct krb5_enctype *krb5, + const struct krb5_buffer *protocol_key, + const struct krb5_buffer *octet_string, + struct krb5_buffer *result, + gfp_t gfp) +{ + static const struct krb5_buffer prfconstant = { 3, "prf" }; + struct krb5_buffer derived_key; + struct krb5_buffer tmp1, tmp2; + unsigned int m = krb5->block_len; + void *buffer; + int ret; + + if (result->len != krb5->prf_len) + return -EINVAL; + + tmp1.len = krb5->hash_len; + derived_key.len = krb5->key_bytes; + buffer = kzalloc(round16(tmp1.len) + round16(derived_key.len), gfp); + if (!buffer) + return -ENOMEM; + + tmp1.data = buffer; + derived_key.data = buffer + round16(tmp1.len); + + ret = rfc3961_calc_H(krb5, octet_string, &tmp1, gfp); + if (ret < 0) + goto err; + + tmp2.len = tmp1.len & ~(m - 1); + tmp2.data = tmp1.data; + + ret = rfc3961_calc_DK(krb5, protocol_key, &prfconstant, &derived_key, gfp); + if (ret < 0) + goto err; + + ret = rfc3961_calc_E(krb5, &derived_key, &tmp2, result, gfp); + +err: + kfree_sensitive(buffer); + return ret; +} + +const struct krb5_crypto_profile rfc3961_simplified_profile = { + .calc_PRF = rfc3961_calc_PRF, + .calc_Kc = rfc3961_calc_DK, + .calc_Ke = rfc3961_calc_DK, + .calc_Ki = rfc3961_calc_DK, +}; From patchwork Thu Nov 12 12:58:26 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Howells X-Patchwork-Id: 11900007 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.9 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 87186C56201 for ; Thu, 12 Nov 2020 12:58:46 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 295ED221E9 for ; Thu, 12 Nov 2020 12:58:46 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="Z9hBglv6" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728357AbgKLM6o (ORCPT ); Thu, 12 Nov 2020 07:58:44 -0500 Received: from us-smtp-delivery-124.mimecast.com ([216.205.24.124]:41766 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728339AbgKLM6e (ORCPT ); Thu, 12 Nov 2020 07:58:34 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1605185912; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=SySPm7r+Xs/nsq8dnyoEL5ZEMshgWivB2CrExFwxAHM=; b=Z9hBglv6U5FlKln9X0/IjEbJ1B9Iz5K9dZ5t60Bmjq2er6Axls9Mn577p4iwfgi6WlJYyP MuIX7L7Blv1M8wgLzFUrZNIjFjbwsVQx9n5+0bZoPPQx9a4OWN3bgthgAj0J0J/vs3VrpW M7TzoXagGuHH+t6ZBlpUk64Ypqs7gOk= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-559-6asMlXxbMB2Dc-JzdtiQKA-1; Thu, 12 Nov 2020 07:58:30 -0500 X-MC-Unique: 6asMlXxbMB2Dc-JzdtiQKA-1 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id CD60C1882FA3; Thu, 12 Nov 2020 12:58:28 +0000 (UTC) Received: from warthog.procyon.org.uk (ovpn-115-47.rdu2.redhat.com [10.10.115.47]) by smtp.corp.redhat.com (Postfix) with ESMTP id C87D910013BD; Thu, 12 Nov 2020 12:58:26 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH 05/18] crypto/krb5: Implement the Kerberos5 rfc3961 encrypt and decrypt functions From: David Howells To: herbert@gondor.apana.org.au, bfields@fieldses.org Cc: dhowells@redhat.com, trond.myklebust@hammerspace.com, linux-crypto@vger.kernel.org, linux-afs@lists.infradead.org, linux-cifs@vger.kernel.org, linux-nfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 12 Nov 2020 12:58:26 +0000 Message-ID: <160518590606.2277919.18393717448457481276.stgit@warthog.procyon.org.uk> In-Reply-To: <160518586534.2277919.14475638653680231924.stgit@warthog.procyon.org.uk> References: <160518586534.2277919.14475638653680231924.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.22 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Add functions that encrypt and decrypt a piece of an skbuff according to rfc3961 sec 5.3, using Ki to checksum the data to be secured and Ke to encrypt it during the encryption phase, then decrypting with Ke and verifying the checksum with Ki in the decryption phase. Signed-off-by: David Howells --- crypto/krb5/internal.h | 18 +++ crypto/krb5/main.c | 102 +++++++++++++++++++ crypto/krb5/rfc3961_simplified.c | 204 ++++++++++++++++++++++++++++++++++++++ include/crypto/krb5.h | 12 ++ 4 files changed, 336 insertions(+) diff --git a/crypto/krb5/internal.h b/crypto/krb5/internal.h index 874dddada713..ce07decf19f0 100644 --- a/crypto/krb5/internal.h +++ b/crypto/krb5/internal.h @@ -7,6 +7,7 @@ #include #include +#include /* * Profile used for key derivation and encryption. @@ -87,7 +88,24 @@ struct krb5_crypto_profile { crypto_roundup(crypto_sync_skcipher_ivsize(TFM)) #define round16(x) (((x) + 15) & ~15) +/* + * main.c + */ +int crypto_shash_update_sg(struct shash_desc *desc, struct scatterlist *sg, + size_t offset, size_t len); + /* * rfc3961_simplified.c */ extern const struct krb5_crypto_profile rfc3961_simplified_profile; + +ssize_t rfc3961_encrypt(const struct krb5_enctype *krb5, + struct krb5_enc_keys *keys, + struct scatterlist *sg, unsigned nr_sg, size_t sg_len, + size_t data_offset, size_t data_len, + bool preconfounded); +int rfc3961_decrypt(const struct krb5_enctype *krb5, + struct krb5_enc_keys *keys, + struct scatterlist *sg, unsigned nr_sg, + size_t *_offset, size_t *_len, + int *_error_code); diff --git a/crypto/krb5/main.c b/crypto/krb5/main.c index 58d40252adc9..db3fc34be272 100644 --- a/crypto/krb5/main.c +++ b/crypto/krb5/main.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "internal.h" MODULE_DESCRIPTION("Kerberos 5 crypto"); @@ -40,3 +41,104 @@ const struct krb5_enctype *crypto_krb5_find_enctype(u32 enctype) return NULL; } EXPORT_SYMBOL(crypto_krb5_find_enctype); + +int crypto_shash_update_sg(struct shash_desc *desc, struct scatterlist *sg, + size_t offset, size_t len) +{ + for (;; sg++) { + int ret; + + if (offset < sg->length) { + struct page *page = sg_page(sg); + void *p = kmap_atomic(page); + size_t seg = min_t(size_t, len, sg->length - offset); + + ret = crypto_shash_update(desc, p + sg->offset + offset, seg); + kunmap_atomic(p); + if (ret < 0) + return ret; + len -= seg; + offset = 0; + } else { + offset -= sg->length; + } + if (sg_is_last(sg) || len > 0) + break; + } + + return 0; +} + +/** + * crypto_krb5_encrypt - Apply Kerberos encryption and integrity. + * @krb5: The encoding to use. + * @keys: The encryption and integrity keys to use. + * @sg: Scatterlist defining the crypto buffer. + * @nr_sg: The number of elements in @sg. + * @sg_len: The size of the buffer. + * @data_offset: The offset of the data in the @sg buffer. + * @data_len: The length of the data. + * @preconfounded: True if the confounder is already inserted. + * + * Using the specified Kerberos encoding, insert a confounder and padding as + * needed, encrypt this and the data in place and insert an integrity checksum + * into the buffer. + * + * The buffer must include space for the confounder, the checksum and any + * padding required. The caller can preinsert the confounder into the buffer + * (for testing, for example). + * + * The resulting secured blob may be less than the size of the buffer. + * + * Returns the size of the secure blob if successful, -ENOMEM on an allocation + * failure, -EFAULT if there is insufficient space, -EMSGSIZE if the confounder + * is too short or the data is misaligned. Other errors may also be returned + * from the crypto layer. + */ +ssize_t crypto_krb5_encrypt(const struct krb5_enctype *krb5, + struct krb5_enc_keys *keys, + struct scatterlist *sg, unsigned nr_sg, size_t sg_len, + size_t data_offset, size_t data_len, + bool preconfounded) +{ + if (WARN_ON(data_offset > sg_len || + data_len > sg_len || + data_offset > sg_len - data_len)) + return -EMSGSIZE; + return krb5->profile->encrypt(krb5, keys, sg, nr_sg, sg_len, + data_offset, data_len, preconfounded); +} +EXPORT_SYMBOL(crypto_krb5_encrypt); + +/** + * crypto_krb5_decrypt - Validate and remove Kerberos encryption and integrity. + * @krb5: The encoding to use. + * @keys: The encryption and integrity keys to use. + * @sg: Scatterlist defining the crypto buffer. + * @nr_sg: The number of elements in @sg. + * @_offset: Offset of the secure blob in the buffer; updated to data offset. + * @_len: The length of the secure blob; updated to data length. + * @_error_code: Set to a Kerberos error code for parsing/validation errors. + * + * Using the specified Kerberos encoding, check and remove the integrity + * checksum and decrypt the secure region, stripping off the confounder. + * + * If successful, @_offset and @_len are updated to outline the region in which + * the data plus the trailing padding are stored. The caller is responsible + * for working out how much padding there is and removing it. + * + * Returns the 0 if successful, -ENOMEM on an allocation failure; sets + * *_error_code and returns -EPROTO if the data cannot be parsed or if the + * integrity checksum doesn't match). Other errors may also be returned from + * the crypto layer. + */ +int crypto_krb5_decrypt(const struct krb5_enctype *krb5, + struct krb5_enc_keys *keys, + struct scatterlist *sg, unsigned nr_sg, + size_t *_offset, size_t *_len, + int *_error_code) +{ + return krb5->profile->decrypt(krb5, keys, sg, nr_sg, + _offset, _len, _error_code); +} +EXPORT_SYMBOL(crypto_krb5_decrypt); diff --git a/crypto/krb5/rfc3961_simplified.c b/crypto/krb5/rfc3961_simplified.c index 0a5c689f6354..0a5c19b83f51 100644 --- a/crypto/krb5/rfc3961_simplified.c +++ b/crypto/krb5/rfc3961_simplified.c @@ -65,6 +65,8 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include +#include #include #include #include @@ -386,9 +388,211 @@ static int rfc3961_calc_PRF(const struct krb5_enctype *krb5, return ret; } +/* + * Apply encryption and checksumming functions to part of a scatterlist. + */ +ssize_t rfc3961_encrypt(const struct krb5_enctype *krb5, + struct krb5_enc_keys *keys, + struct scatterlist *sg, unsigned nr_sg, size_t sg_len, + size_t data_offset, size_t data_len, + bool preconfounded) +{ + struct skcipher_request *req; + struct shash_desc *desc; + ssize_t ret, done; + size_t bsize, base_len, secure_offset, secure_len, pad_len, cksum_offset; + void *buffer; + u8 *cksum, *iv; + + if (WARN_ON(data_offset != krb5->conf_len)) + return -EINVAL; /* Can't set offset on skcipher */ + + secure_offset = 0; + base_len = krb5->conf_len + data_len; + if (krb5->pad) { + secure_len = round_up(base_len, krb5->block_len); + pad_len = secure_len - base_len; + } else { + secure_len = base_len; + pad_len = 0; + } + cksum_offset = secure_len; + if (WARN_ON(cksum_offset + krb5->cksum_len > sg_len)) + return -EFAULT; + + bsize = krb5_shash_size(keys->Ki) + + krb5_digest_size(keys->Ki) + + krb5_sync_skcipher_size(keys->Ke) + + krb5_sync_skcipher_ivsize(keys->Ke); + bsize = max_t(size_t, bsize, krb5->conf_len); + bsize = max_t(size_t, bsize, krb5->block_len); + buffer = kzalloc(bsize, GFP_NOFS); + if (!buffer) + return -ENOMEM; + + /* Insert the confounder into the skb */ + ret = -EFAULT; + if (!preconfounded) { + get_random_bytes(buffer, krb5->conf_len); + done = sg_pcopy_from_buffer(sg, nr_sg, buffer, krb5->conf_len, + secure_offset); + if (done != krb5->conf_len) + goto error; + } + + /* We need to pad out to the crypto blocksize. */ + if (pad_len) { + done = sg_zero_buffer(sg, nr_sg, pad_len, data_offset + data_len); + if (done != pad_len) + goto error; + } + + /* Calculate the checksum using key Ki */ + cksum = buffer + krb5_shash_size(keys->Ki); + + desc = buffer; + desc->tfm = keys->Ki; + ret = crypto_shash_init(desc); + if (ret < 0) + goto error; + + ret = crypto_shash_update_sg(desc, sg, secure_offset, secure_len); + if (ret < 0) + goto error; + + ret = crypto_shash_final(desc, cksum); + if (ret < 0) + goto error; + + /* Append the checksum into the buffer. */ + ret = -EFAULT; + done = sg_pcopy_from_buffer(sg, nr_sg, cksum, krb5->cksum_len, cksum_offset); + if (done != krb5->cksum_len) + goto error; + + /* Encrypt the secure region with key Ke. */ + req = buffer + + krb5_shash_size(keys->Ki) + + krb5_digest_size(keys->Ki); + iv = buffer + + krb5_shash_size(keys->Ki) + + krb5_digest_size(keys->Ki) + + krb5_sync_skcipher_size(keys->Ke); + + skcipher_request_set_sync_tfm(req, keys->Ke); + skcipher_request_set_callback(req, 0, NULL, NULL); + skcipher_request_set_crypt(req, sg, sg, secure_len, iv); + ret = crypto_skcipher_encrypt(req); + if (ret < 0) + goto error; + + ret = secure_len + krb5->cksum_len; + +error: + kfree_sensitive(buffer); + return ret; +} + +/* + * Apply decryption and checksumming functions to part of an skbuff. The + * offset and length are updated to reflect the actual content of the encrypted + * region. + */ +int rfc3961_decrypt(const struct krb5_enctype *krb5, + struct krb5_enc_keys *keys, + struct scatterlist *sg, unsigned nr_sg, + size_t *_offset, size_t *_len, + int *_error_code) +{ + struct skcipher_request *req; + struct shash_desc *desc; + ssize_t done; + size_t bsize, secure_len, offset = *_offset, len = *_len; + void *buffer = NULL; + int ret; + u8 *cksum, *cksum2, *iv; + + if (WARN_ON(*_offset != 0)) + return -EINVAL; /* Can't set offset on skcipher */ + + if (len < krb5->conf_len + krb5->cksum_len) { + *_error_code = 1; //RXGK_SEALED_INCON; + return -EPROTO; + } + secure_len = len - krb5->cksum_len; + + bsize = krb5_shash_size(keys->Ki) + + krb5_digest_size(keys->Ki) * 2 + + krb5_sync_skcipher_size(keys->Ke) + + krb5_sync_skcipher_ivsize(keys->Ke); + buffer = kzalloc(bsize, GFP_NOFS); + if (!buffer) + return -ENOMEM; + + /* Decrypt the secure region with key Ke. */ + req = buffer + + krb5_shash_size(keys->Ki) + + krb5_digest_size(keys->Ki) * 2; + iv = buffer + + krb5_shash_size(keys->Ki) + + krb5_digest_size(keys->Ki) * 2 + + krb5_sync_skcipher_size(keys->Ke); + + skcipher_request_set_sync_tfm(req, keys->Ke); + skcipher_request_set_callback(req, 0, NULL, NULL); + skcipher_request_set_crypt(req, sg, sg, secure_len, iv); + ret = crypto_skcipher_decrypt(req); + if (ret < 0) + goto error; + + /* Calculate the checksum using key Ki */ + cksum = buffer + + krb5_shash_size(keys->Ki); + cksum2 = buffer + + krb5_shash_size(keys->Ki) + + krb5_digest_size(keys->Ki); + + desc = buffer; + desc->tfm = keys->Ki; + ret = crypto_shash_init(desc); + if (ret < 0) + goto error; + + ret = crypto_shash_update_sg(desc, sg, 0, secure_len); + if (ret < 0) + goto error; + + ret = crypto_shash_final(desc, cksum); + if (ret < 0) + goto error; + + /* Get the checksum from the buffer. */ + ret = -EFAULT; + done = sg_pcopy_to_buffer(sg, nr_sg, cksum2, krb5->cksum_len, + offset + len - krb5->cksum_len); + if (done != krb5->cksum_len) + goto error; + + if (memcmp(cksum, cksum2, krb5->cksum_len) != 0) { + *_error_code = 1; //RXGK_SEALED_INCON; + ret = -EPROTO; + goto error; + } + + *_offset += krb5->conf_len; + *_len -= krb5->conf_len + krb5->cksum_len; + ret = 0; + +error: + kfree_sensitive(buffer); + return ret; +} + const struct krb5_crypto_profile rfc3961_simplified_profile = { .calc_PRF = rfc3961_calc_PRF, .calc_Kc = rfc3961_calc_DK, .calc_Ke = rfc3961_calc_DK, .calc_Ki = rfc3961_calc_DK, + .encrypt = rfc3961_encrypt, + .decrypt = rfc3961_decrypt, }; diff --git a/include/crypto/krb5.h b/include/crypto/krb5.h index 04286bacaf06..fb77f70117c1 100644 --- a/include/crypto/krb5.h +++ b/include/crypto/krb5.h @@ -12,6 +12,8 @@ #ifndef _CRYPTO_KRB5_H #define _CRYPTO_KRB5_H +#include + struct crypto_shash; struct scatterlist; @@ -103,6 +105,16 @@ struct krb5_enctype { */ extern const struct krb5_enctype *crypto_krb5_find_enctype(u32 enctype); +extern ssize_t crypto_krb5_encrypt(const struct krb5_enctype *krb5, + struct krb5_enc_keys *keys, + struct scatterlist *sg, unsigned nr_sg, size_t sg_len, + size_t data_offset, size_t data_len, + bool preconfounded); +extern int crypto_krb5_decrypt(const struct krb5_enctype *krb5, + struct krb5_enc_keys *keys, + struct scatterlist *sg, unsigned nr_sg, + size_t *_offset, size_t *_len, + int *_error_code); /* * kdf.c */ From patchwork Thu Nov 12 12:58:34 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Howells X-Patchwork-Id: 11900011 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.9 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 558CDC388F7 for ; Thu, 12 Nov 2020 12:59:01 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id EACA221D91 for ; Thu, 12 Nov 2020 12:58:58 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="jO7zDhbD" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728383AbgKLM65 (ORCPT ); Thu, 12 Nov 2020 07:58:57 -0500 Received: from us-smtp-delivery-124.mimecast.com ([216.205.24.124]:37700 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727899AbgKLM6m (ORCPT ); Thu, 12 Nov 2020 07:58:42 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1605185920; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=F9/Aivm7UY4Jytt/W9rNpmH3aylT9UiLECcuGtPXRFQ=; b=jO7zDhbDnu/moViO3cCQJGgGxUiaJ2IwbnRBwnb7vIcMSh0i4pzOCwKchJZfKIrSy7UPIr K0hH4KXMAWA+u85tqVXj5Wi+d2jl7FXVFwlnO88RW9r0eyRQyk1exPCjmbWGnytnvxs661 QK1Se29puAZ3kE5wAnznB0tJ9jBWUY4= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-303-W4ve4Mf7PUSviEIJUghsjA-1; Thu, 12 Nov 2020 07:58:38 -0500 X-MC-Unique: W4ve4Mf7PUSviEIJUghsjA-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 08EF9804777; Thu, 12 Nov 2020 12:58:37 +0000 (UTC) Received: from warthog.procyon.org.uk (ovpn-115-47.rdu2.redhat.com [10.10.115.47]) by smtp.corp.redhat.com (Postfix) with ESMTP id D754127CC1; Thu, 12 Nov 2020 12:58:34 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH 06/18] crypto/krb5: Implement the Kerberos5 rfc3961 get_mic and verify_mic From: David Howells To: herbert@gondor.apana.org.au, bfields@fieldses.org Cc: dhowells@redhat.com, trond.myklebust@hammerspace.com, linux-crypto@vger.kernel.org, linux-afs@lists.infradead.org, linux-cifs@vger.kernel.org, linux-nfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 12 Nov 2020 12:58:34 +0000 Message-ID: <160518591404.2277919.10560122154909780177.stgit@warthog.procyon.org.uk> In-Reply-To: <160518586534.2277919.14475638653680231924.stgit@warthog.procyon.org.uk> References: <160518586534.2277919.14475638653680231924.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Add functions that sign and verify a piece of an skbuff according to rfc3961 sec 5.4, using Kc to generate a checksum and insert it into the MIC field in the skbuff in the sign phase then checksum the data and compare it to the MIC in the verify phase. Signed-off-by: David Howells --- crypto/krb5/internal.h | 11 +++ crypto/krb5/main.c | 70 ++++++++++++++++++++ crypto/krb5/rfc3961_simplified.c | 134 ++++++++++++++++++++++++++++++++++++++ include/crypto/krb5.h | 12 +++ 4 files changed, 227 insertions(+) diff --git a/crypto/krb5/internal.h b/crypto/krb5/internal.h index ce07decf19f0..20b506327491 100644 --- a/crypto/krb5/internal.h +++ b/crypto/krb5/internal.h @@ -109,3 +109,14 @@ int rfc3961_decrypt(const struct krb5_enctype *krb5, struct scatterlist *sg, unsigned nr_sg, size_t *_offset, size_t *_len, int *_error_code); +ssize_t rfc3961_get_mic(const struct krb5_enctype *krb5, + struct crypto_shash *shash, + const struct krb5_buffer *metadata, + struct scatterlist *sg, unsigned nr_sg, size_t sg_len, + size_t data_offset, size_t data_len); +int rfc3961_verify_mic(const struct krb5_enctype *krb5, + struct crypto_shash *shash, + const struct krb5_buffer *metadata, + struct scatterlist *sg, unsigned nr_sg, + size_t *_offset, size_t *_len, + int *_error_code); diff --git a/crypto/krb5/main.c b/crypto/krb5/main.c index db3fc34be272..97b28e40f6d7 100644 --- a/crypto/krb5/main.c +++ b/crypto/krb5/main.c @@ -142,3 +142,73 @@ int crypto_krb5_decrypt(const struct krb5_enctype *krb5, _offset, _len, _error_code); } EXPORT_SYMBOL(crypto_krb5_decrypt); + +/** + * crypto_krb5_get_mic - Apply Kerberos integrity checksum. + * @krb5: The encoding to use. + * @shash: The keyed hash to use. + * @metadata: Metadata to add into the hash before adding the data. + * @sg: Scatterlist defining the crypto buffer. + * @nr_sg: The number of elements in @sg. + * @sg_len: The size of the buffer. + * @data_offset: The offset of the data in the @sg buffer. + * @data_len: The length of the data. + * + * Using the specified Kerberos encoding, calculate and insert an integrity + * checksum into the buffer. + * + * The buffer must include space for the checksum at the front. + * + * Returns the size of the secure blob if successful, -ENOMEM on an allocation + * failure, -EFAULT if there is insufficient space, -EMSGSIZE if the gap for + * the checksum is too short. Other errors may also be returned from the + * crypto layer. + */ +ssize_t crypto_krb5_get_mic(const struct krb5_enctype *krb5, + struct crypto_shash *shash, + const struct krb5_buffer *metadata, + struct scatterlist *sg, unsigned nr_sg, size_t sg_len, + size_t data_offset, size_t data_len) +{ + if (WARN_ON(data_offset > sg_len || + data_len > sg_len || + data_offset > sg_len - data_len)) + return -EMSGSIZE; + return krb5->profile->get_mic(krb5, shash, metadata, sg, nr_sg, sg_len, + data_offset, data_len); +} +EXPORT_SYMBOL(crypto_krb5_get_mic); + +/** + * crypto_krb5_verify_mic - Validate and remove Kerberos integrity checksum. + * @krb5: The encoding to use. + * @shash: The keyed hash to use. + * @metadata: Metadata to add into the hash before adding the data. + * @sg: Scatterlist defining the crypto buffer. + * @nr_sg: The number of elements in @sg. + * @_offset: Offset of the secure blob in the buffer; updated to data offset. + * @_len: The length of the secure blob; updated to data length. + * @_error_code: Set to a Kerberos error code for parsing/validation errors. + * + * Using the specified Kerberos encoding, check and remove the integrity + * checksum. + * + * If successful, @_offset and @_len are updated to outline the region in which + * the data is stored. + * + * Returns the 0 if successful, -ENOMEM on an allocation failure; sets + * *_error_code and returns -EPROTO if the data cannot be parsed or if the + * integrity checksum doesn't match). Other errors may also be returned from + * the crypto layer. + */ +int crypto_krb5_verify_mic(const struct krb5_enctype *krb5, + struct crypto_shash *shash, + const struct krb5_buffer *metadata, + struct scatterlist *sg, unsigned nr_sg, + size_t *_offset, size_t *_len, + int *_error_code) +{ + return krb5->profile->verify_mic(krb5, shash, metadata, sg, nr_sg, + _offset, _len, _error_code); +} +EXPORT_SYMBOL(crypto_krb5_verify_mic); diff --git a/crypto/krb5/rfc3961_simplified.c b/crypto/krb5/rfc3961_simplified.c index 0a5c19b83f51..f779f962b921 100644 --- a/crypto/krb5/rfc3961_simplified.c +++ b/crypto/krb5/rfc3961_simplified.c @@ -588,6 +588,138 @@ int rfc3961_decrypt(const struct krb5_enctype *krb5, return ret; } +/* + * Generate a checksum over some metadata and part of an skbuff and insert the + * MIC into the skbuff immediately prior to the data. + */ +ssize_t rfc3961_get_mic(const struct krb5_enctype *krb5, + struct crypto_shash *shash, + const struct krb5_buffer *metadata, + struct scatterlist *sg, unsigned nr_sg, size_t sg_len, + size_t data_offset, size_t data_len) +{ + struct shash_desc *desc; + ssize_t ret, done; + size_t bsize; + void *buffer, *digest; + + if (WARN_ON(data_offset != krb5->cksum_len)) + return -EMSGSIZE; + + bsize = krb5_shash_size(shash) + + krb5_digest_size(shash); + buffer = kzalloc(bsize, GFP_NOFS); + if (!buffer) + return -ENOMEM; + + /* Calculate the MIC with key Kc and store it into the skb */ + desc = buffer; + desc->tfm = shash; + ret = crypto_shash_init(desc); + if (ret < 0) + goto error; + + if (metadata) { + ret = crypto_shash_update(desc, metadata->data, metadata->len); + if (ret < 0) + goto error; + } + + ret = crypto_shash_update_sg(desc, sg, data_offset, data_len); + if (ret < 0) + goto error; + + digest = buffer + krb5_shash_size(shash); + ret = crypto_shash_final(desc, digest); + if (ret < 0) + goto error; + + ret = -EFAULT; + done = sg_pcopy_from_buffer(sg, nr_sg, digest, krb5->cksum_len, + data_offset - krb5->cksum_len); + if (done != krb5->cksum_len) + goto error; + + ret = krb5->cksum_len + data_len; + +error: + kfree_sensitive(buffer); + return ret; +} + +/* + * Check the MIC on a region of an skbuff. The offset and length are updated + * to reflect the actual content of the secure region. + */ +int rfc3961_verify_mic(const struct krb5_enctype *krb5, + struct crypto_shash *shash, + const struct krb5_buffer *metadata, + struct scatterlist *sg, unsigned nr_sg, + size_t *_offset, size_t *_len, + int *_error_code) +{ + struct shash_desc *desc; + ssize_t done; + size_t bsize, data_offset, data_len, offset = *_offset, len = *_len; + void *buffer = NULL; + int ret; + u8 *cksum, *cksum2; + + if (len < krb5->cksum_len) { + *_error_code = 1; //RXGK_SEALED_INCON; + return -EPROTO; + } + data_offset = offset + krb5->cksum_len; + data_len = len - krb5->cksum_len; + + bsize = krb5_shash_size(shash) + + krb5_digest_size(shash) * 2; + buffer = kzalloc(bsize, GFP_NOFS); + if (!buffer) + return -ENOMEM; + + cksum = buffer + + krb5_shash_size(shash); + cksum2 = buffer + + krb5_shash_size(shash) + + krb5_digest_size(shash); + + /* Calculate the MIC */ + desc = buffer; + desc->tfm = shash; + ret = crypto_shash_init(desc); + if (ret < 0) + goto error; + + if (metadata) { + ret = crypto_shash_update(desc, metadata->data, metadata->len); + if (ret < 0) + goto error; + } + + crypto_shash_update_sg(desc, sg, data_offset, data_len); + crypto_shash_final(desc, cksum); + + ret = -EFAULT; + done = sg_pcopy_to_buffer(sg, nr_sg, cksum2, krb5->cksum_len, offset); + if (done != krb5->cksum_len) + goto error; + + if (memcmp(cksum, cksum2, krb5->cksum_len) != 0) { + *_error_code = 1; //RXGK_SEALED_INCON; + ret = -EPROTO; + goto error; + } + + *_offset += krb5->cksum_len; + *_len -= krb5->cksum_len; + ret = 0; + +error: + kfree_sensitive(buffer); + return ret; +} + const struct krb5_crypto_profile rfc3961_simplified_profile = { .calc_PRF = rfc3961_calc_PRF, .calc_Kc = rfc3961_calc_DK, @@ -595,4 +727,6 @@ const struct krb5_crypto_profile rfc3961_simplified_profile = { .calc_Ki = rfc3961_calc_DK, .encrypt = rfc3961_encrypt, .decrypt = rfc3961_decrypt, + .get_mic = rfc3961_get_mic, + .verify_mic = rfc3961_verify_mic, }; diff --git a/include/crypto/krb5.h b/include/crypto/krb5.h index fb77f70117c1..b83d3d487753 100644 --- a/include/crypto/krb5.h +++ b/include/crypto/krb5.h @@ -115,6 +115,18 @@ extern int crypto_krb5_decrypt(const struct krb5_enctype *krb5, struct scatterlist *sg, unsigned nr_sg, size_t *_offset, size_t *_len, int *_error_code); +extern ssize_t crypto_krb5_get_mic(const struct krb5_enctype *krb5, + struct crypto_shash *shash, + const struct krb5_buffer *metadata, + struct scatterlist *sg, unsigned nr_sg, size_t sg_len, + size_t data_offset, size_t data_len); +extern int crypto_krb5_verify_mic(const struct krb5_enctype *krb5, + struct crypto_shash *shash, + const struct krb5_buffer *metadata, + struct scatterlist *sg, unsigned nr_sg, + size_t *_offset, size_t *_len, + int *_error_code); + /* * kdf.c */ From patchwork Thu Nov 12 12:58:42 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Howells X-Patchwork-Id: 11900103 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.9 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2340CC388F7 for ; Thu, 12 Nov 2020 13:01:26 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id AD20521D7F for ; Thu, 12 Nov 2020 13:01:25 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="DuAmM2AE" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728556AbgKLNBQ (ORCPT ); Thu, 12 Nov 2020 08:01:16 -0500 Received: from us-smtp-delivery-124.mimecast.com ([63.128.21.124]:32430 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728367AbgKLM6x (ORCPT ); Thu, 12 Nov 2020 07:58:53 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1605185930; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=HPWihtbjJWUbWPVbAr1/ioOfbP8rP3X1VCCFWfzZRq4=; b=DuAmM2AE96ae1pej4vshSahg2UeIWvnio1UlQNF5uyJWUvDDJ77Bv6vNGFOVF7AteDh3w3 gjlt6GcRLkVQ3IyVFmhUlY7pmRrCrbUB9VpWV5+sZ9pF78juJk375x65qqUFM/FjavXGK2 kuWWGK9YU/r2VYaGfWEXVB+0ROVM/DY= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-322-PyE3nt7zNTyv5KaVKob2SA-1; Thu, 12 Nov 2020 07:58:46 -0500 X-MC-Unique: PyE3nt7zNTyv5KaVKob2SA-1 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 427B0101AFB6; Thu, 12 Nov 2020 12:58:45 +0000 (UTC) Received: from warthog.procyon.org.uk (ovpn-115-47.rdu2.redhat.com [10.10.115.47]) by smtp.corp.redhat.com (Postfix) with ESMTP id 214485B4AD; Thu, 12 Nov 2020 12:58:42 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH 07/18] crypto/krb5: Implement the AES enctypes from rfc3962 From: David Howells To: herbert@gondor.apana.org.au, bfields@fieldses.org Cc: dhowells@redhat.com, trond.myklebust@hammerspace.com, linux-crypto@vger.kernel.org, linux-afs@lists.infradead.org, linux-cifs@vger.kernel.org, linux-nfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 12 Nov 2020 12:58:42 +0000 Message-ID: <160518592226.2277919.16458400030521324547.stgit@warthog.procyon.org.uk> In-Reply-To: <160518586534.2277919.14475638653680231924.stgit@warthog.procyon.org.uk> References: <160518586534.2277919.14475638653680231924.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Implement the aes128-cts-hmac-sha1-96 and aes256-cts-hmac-sha1-96 enctypes from rfc3962, using the rfc3961 kerberos 5 simplified crypto scheme. Signed-off-by: David Howells --- crypto/krb5/Makefile | 3 + crypto/krb5/internal.h | 6 ++ crypto/krb5/main.c | 2 + crypto/krb5/rfc3962_aes.c | 140 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 crypto/krb5/rfc3962_aes.c diff --git a/crypto/krb5/Makefile b/crypto/krb5/Makefile index 67824c44aac3..b81e2efac3c8 100644 --- a/crypto/krb5/Makefile +++ b/crypto/krb5/Makefile @@ -6,6 +6,7 @@ krb5-y += \ kdf.o \ main.o \ - rfc3961_simplified.o + rfc3961_simplified.o \ + rfc3962_aes.o obj-$(CONFIG_CRYPTO_KRB5) += krb5.o diff --git a/crypto/krb5/internal.h b/crypto/krb5/internal.h index 20b506327491..5d55a574536e 100644 --- a/crypto/krb5/internal.h +++ b/crypto/krb5/internal.h @@ -120,3 +120,9 @@ int rfc3961_verify_mic(const struct krb5_enctype *krb5, struct scatterlist *sg, unsigned nr_sg, size_t *_offset, size_t *_len, int *_error_code); + +/* + * rfc3962_aes.c + */ +extern const struct krb5_enctype krb5_aes128_cts_hmac_sha1_96; +extern const struct krb5_enctype krb5_aes256_cts_hmac_sha1_96; diff --git a/crypto/krb5/main.c b/crypto/krb5/main.c index 97b28e40f6d7..bce47580c33f 100644 --- a/crypto/krb5/main.c +++ b/crypto/krb5/main.c @@ -18,6 +18,8 @@ MODULE_AUTHOR("Red Hat, Inc."); MODULE_LICENSE("GPL"); static const struct krb5_enctype *const krb5_supported_enctypes[] = { + &krb5_aes128_cts_hmac_sha1_96, + &krb5_aes256_cts_hmac_sha1_96, }; /** diff --git a/crypto/krb5/rfc3962_aes.c b/crypto/krb5/rfc3962_aes.c new file mode 100644 index 000000000000..99297a698178 --- /dev/null +++ b/crypto/krb5/rfc3962_aes.c @@ -0,0 +1,140 @@ +/* rfc3962 Advanced Encryption Standard (AES) Encryption for Kerberos 5 + * + * Parts borrowed from net/sunrpc/auth_gss/. + */ +/* + * COPYRIGHT (c) 2008 + * The Regents of the University of Michigan + * ALL RIGHTS RESERVED + * + * Permission is granted to use, copy, create derivative works + * and redistribute this software and such derivative works + * for any purpose, so long as the name of The University of + * Michigan is not used in any advertising or publicity + * pertaining to the use of distribution of this software + * without specific, written prior authorization. If the + * above copyright notice or any other identification of the + * University of Michigan is included in any copy of any + * portion of this software, then the disclaimer below must + * also be included. + * + * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION + * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY + * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF + * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING + * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE + * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE + * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR + * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING + * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN + * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGES. + */ + +/* + * Copyright (C) 1998 by the FundsXpress, INC. + * + * All rights reserved. + * + * Export of this software from the United States of America may require + * a specific license from the United States Government. It is the + * responsibility of any person or organization contemplating export to + * obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of FundsXpress. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. FundsXpress makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* + * RxGK bits: + * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include "internal.h" + +/* + * AES random-to-key function. For AES, this is an identity operation. + */ +static int rfc3962_random_to_key(const struct krb5_enctype *krb5, + const struct krb5_buffer *randombits, + struct krb5_buffer *result) +{ + if (randombits->len != 16 && randombits->len != 32) + return -EINVAL; + + if (result->len != randombits->len) + return -EINVAL; + + memcpy(result->data, randombits->data, randombits->len); + return 0; +} + +const struct krb5_enctype krb5_aes128_cts_hmac_sha1_96 = { + .etype = KRB5_ENCTYPE_AES128_CTS_HMAC_SHA1_96, + .ctype = KRB5_CKSUMTYPE_HMAC_SHA1_96_AES128, + .name = "aes128-cts-hmac-sha1-96", + .encrypt_name = "cts(cbc(aes))", + .cksum_name = "hmac(sha1)", + .hash_name = "sha1", + .key_bytes = 16, + .key_len = 16, + .Kc_len = 16, + .Ke_len = 16, + .Ki_len = 16, + .block_len = 16, + .conf_len = 16, + .cksum_len = 12, + .hash_len = 20, + .prf_len = 16, + .keyed_cksum = true, + .pad = true, + .random_to_key = rfc3962_random_to_key, + .profile = &rfc3961_simplified_profile, +}; + +const struct krb5_enctype krb5_aes256_cts_hmac_sha1_96 = { + .etype = KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96, + .ctype = KRB5_CKSUMTYPE_HMAC_SHA1_96_AES256, + .name = "aes256-cts-hmac-sha1-96", + .encrypt_name = "cts(cbc(aes))", + .cksum_name = "hmac(sha1)", + .hash_name = "sha1", + .key_bytes = 32, + .key_len = 32, + .Kc_len = 32, + .Ke_len = 32, + .Ki_len = 32, + .block_len = 16, + .conf_len = 16, + .cksum_len = 12, + .hash_len = 20, + .prf_len = 16, + .keyed_cksum = true, + .pad = true, + .random_to_key = rfc3962_random_to_key, + .profile = &rfc3961_simplified_profile, +}; From patchwork Thu Nov 12 12:58:50 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Howells X-Patchwork-Id: 11900009 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.9 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4B49AC56201 for ; Thu, 12 Nov 2020 12:59:14 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id D2EA521D7F for ; Thu, 12 Nov 2020 12:59:13 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="XvfZQ4vs" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728305AbgKLM7M (ORCPT ); Thu, 12 Nov 2020 07:59:12 -0500 Received: from us-smtp-delivery-124.mimecast.com ([63.128.21.124]:40568 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728388AbgKLM7B (ORCPT ); Thu, 12 Nov 2020 07:59:01 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1605185939; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=eUE4adsOr6RUNRjKbFbF7nBnnBChKgq5c6GArq21E4o=; b=XvfZQ4vsfVFiElpWU6LZmgpf4UDYfC5D0X1d7B1PWf5divffuUoHMTBRmFLYpjxZxK/XeY XJJtw666amEcP2wU/T84nYCSUSvWBOLA1RFe81p5GihAQsdepRFlDmAtq6wMjdQK5uiXly E3dGoo9mLAWmqLJt42vHwbbaAp6sbEs= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-526-MNgpOwYePUmFU_MqJ_q4DA-1; Thu, 12 Nov 2020 07:58:55 -0500 X-MC-Unique: MNgpOwYePUmFU_MqJ_q4DA-1 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 9CBC786ABDD; Thu, 12 Nov 2020 12:58:53 +0000 (UTC) Received: from warthog.procyon.org.uk (ovpn-115-47.rdu2.redhat.com [10.10.115.47]) by smtp.corp.redhat.com (Postfix) with ESMTP id 52B6D60C13; Thu, 12 Nov 2020 12:58:51 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH 08/18] crypto/krb5: Implement crypto self-testing From: David Howells To: herbert@gondor.apana.org.au, bfields@fieldses.org Cc: dhowells@redhat.com, trond.myklebust@hammerspace.com, linux-crypto@vger.kernel.org, linux-afs@lists.infradead.org, linux-cifs@vger.kernel.org, linux-nfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 12 Nov 2020 12:58:50 +0000 Message-ID: <160518593050.2277919.4004451170398397487.stgit@warthog.procyon.org.uk> In-Reply-To: <160518586534.2277919.14475638653680231924.stgit@warthog.procyon.org.uk> References: <160518586534.2277919.14475638653680231924.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Implement self-testing infrastructure to test the pseudo-random function, key derivation, encryption and checksumming. Signed-off-by: David Howells --- crypto/krb5/Kconfig | 4 crypto/krb5/Makefile | 4 crypto/krb5/internal.h | 48 ++++ crypto/krb5/main.c | 12 + crypto/krb5/selftest.c | 543 +++++++++++++++++++++++++++++++++++++++++++ crypto/krb5/selftest_data.c | 38 +++ 6 files changed, 649 insertions(+) create mode 100644 crypto/krb5/selftest.c create mode 100644 crypto/krb5/selftest_data.c diff --git a/crypto/krb5/Kconfig b/crypto/krb5/Kconfig index 881754500732..e2eba1d689ab 100644 --- a/crypto/krb5/Kconfig +++ b/crypto/krb5/Kconfig @@ -9,3 +9,7 @@ config CRYPTO_KRB5 select CRYPTO_AES help Provide Kerberos-5-based security. + +config CRYPTO_KRB5_SELFTESTS + bool "Kerberos 5 crypto selftests" + depends on CRYPTO_KRB5 diff --git a/crypto/krb5/Makefile b/crypto/krb5/Makefile index b81e2efac3c8..b7da03cae6d1 100644 --- a/crypto/krb5/Makefile +++ b/crypto/krb5/Makefile @@ -9,4 +9,8 @@ krb5-y += \ rfc3961_simplified.o \ rfc3962_aes.o +krb5-$(CONFIG_CRYPTO_KRB5_SELFTESTS) += \ + selftest.o \ + selftest_data.o + obj-$(CONFIG_CRYPTO_KRB5) += krb5.o diff --git a/crypto/krb5/internal.h b/crypto/krb5/internal.h index 5d55a574536e..47424b433778 100644 --- a/crypto/krb5/internal.h +++ b/crypto/krb5/internal.h @@ -88,6 +88,37 @@ struct krb5_crypto_profile { crypto_roundup(crypto_sync_skcipher_ivsize(TFM)) #define round16(x) (((x) + 15) & ~15) +/* + * Self-testing data. + */ +struct krb5_prf_test { + const struct krb5_enctype *krb5; + const char *name, *key, *octet, *prf; +}; + +struct krb5_key_test_one { + u32 use; + const char *key; +}; + +struct krb5_key_test { + const struct krb5_enctype *krb5; + const char *name, *key; + struct krb5_key_test_one Kc, Ke, Ki; +}; + +struct krb5_enc_test { + const struct krb5_enctype *krb5; + const char *name, *plain, *conf, *K0, *Ke, *Ki, *ct; + __be32 usage; +}; + +struct krb5_mic_test { + const struct krb5_enctype *krb5; + const char *name, *plain, *K0, *Kc, *mic; + __be32 usage; +}; + /* * main.c */ @@ -126,3 +157,20 @@ int rfc3961_verify_mic(const struct krb5_enctype *krb5, */ extern const struct krb5_enctype krb5_aes128_cts_hmac_sha1_96; extern const struct krb5_enctype krb5_aes256_cts_hmac_sha1_96; + +/* + * selftest.c + */ +#ifdef CONFIG_CRYPTO_KRB5_SELFTESTS +void krb5_selftest(void); +#else +static inline void krb5_selftest(void) {} +#endif + +/* + * selftest_data.c + */ +extern const struct krb5_prf_test krb5_prf_tests[]; +extern const struct krb5_key_test krb5_key_tests[]; +extern const struct krb5_enc_test krb5_enc_tests[]; +extern const struct krb5_mic_test krb5_mic_tests[]; diff --git a/crypto/krb5/main.c b/crypto/krb5/main.c index bce47580c33f..b79127027551 100644 --- a/crypto/krb5/main.c +++ b/crypto/krb5/main.c @@ -214,3 +214,15 @@ int crypto_krb5_verify_mic(const struct krb5_enctype *krb5, _offset, _len, _error_code); } EXPORT_SYMBOL(crypto_krb5_verify_mic); + +static int __init crypto_krb5_init(void) +{ + krb5_selftest(); + return 0; +} +module_init(crypto_krb5_init); + +static void __exit crypto_krb5_exit(void) +{ +} +module_exit(crypto_krb5_exit); diff --git a/crypto/krb5/selftest.c b/crypto/krb5/selftest.c new file mode 100644 index 000000000000..df57ab24cc6e --- /dev/null +++ b/crypto/krb5/selftest.c @@ -0,0 +1,543 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* RxGK self-testing + * + * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include "internal.h" + +#define VALID(X) \ + ({ \ + bool __x = (X); \ + if (__x) { \ + pr_warn("!!! TESTINVAL %s:%u\n", __FILE__, __LINE__); \ + } \ + __x; \ + }) + +#define CHECK(X) \ + ({ \ + bool __x = (X); \ + if (__x) { \ + pr_warn("!!! TESTFAIL %s:%u\n", __FILE__, __LINE__); \ + } \ + __x; \ + }) + +enum which_key { + TEST_KC, TEST_KE, TEST_KI, +}; + +static int prep_buf(struct krb5_buffer *buf) +{ + buf->data = kmalloc(buf->len, GFP_KERNEL); + if (!buf->data) + return -ENOMEM; + return 0; +} + +#define PREP_BUF(BUF, LEN) \ + do { \ + (BUF)->len = (LEN); \ + if ((ret = prep_buf((BUF))) < 0) \ + goto out; \ + } while(0) + +static int load_buf(struct krb5_buffer *buf, const char *from) +{ + size_t len = strlen(from); + int ret; + + if (len > 1 && from[0] == '\'') { + PREP_BUF(buf, len - 1); + memcpy(buf->data, from + 1, len - 1); + ret = 0; + goto out; + } + + if (VALID(len & 1)) + return -EINVAL; + + PREP_BUF(buf, len / 2); + if ((ret = hex2bin(buf->data, from, buf->len)) < 0) { + VALID(1); + goto out; + } +out: + return ret; +} + +#define LOAD_BUF(BUF, FROM) do { if ((ret = load_buf(BUF, FROM)) < 0) goto out; } while(0) + +static void clear_buf(struct krb5_buffer *buf) +{ + kfree(buf->data); + buf->len = 0; + buf->data = NULL; +} + +/* + * Perform a pseudo-random function check. + */ +static int krb5_test_one_prf(const struct krb5_prf_test *test) +{ + const struct krb5_enctype *krb5 = test->krb5; + struct krb5_buffer key = {}, octet = {}, result = {}, prf = {}; + int ret; + + pr_notice("Running %s %s\n", krb5->name, test->name); + + LOAD_BUF(&key, test->key); + LOAD_BUF(&octet, test->octet); + LOAD_BUF(&prf, test->prf); + PREP_BUF(&result, krb5->prf_len); + + if (VALID(result.len != prf.len)) { + ret = -EINVAL; + goto out; + } + + if ((ret = krb5->profile->calc_PRF(krb5, &key, &octet, &result, GFP_KERNEL)) < 0) { + CHECK(1); + pr_warn("PRF calculation failed %d\n", ret); + goto out; + } + + if (memcmp(result.data, prf.data, result.len) != 0) { + CHECK(1); + ret = -EKEYREJECTED; + goto out; + } + + ret = 0; + +out: + clear_buf(&result); + clear_buf(&octet); + clear_buf(&key); + return ret; +} + +/* + * Perform a key derivation check. + */ +static int krb5_test_key(const struct krb5_enctype *krb5, + const struct krb5_buffer *base_key, + const struct krb5_key_test_one *test, + enum which_key which) +{ + struct krb5_buffer key = {}, result = {}; + int ret; + + LOAD_BUF(&key, test->key); + PREP_BUF(&result, key.len); + + switch (which) { + case TEST_KC: + ret = crypto_krb5_get_Kc(krb5, base_key, test->use, &result, + NULL, GFP_KERNEL); + break; + case TEST_KE: + ret = crypto_krb5_get_Ke(krb5, base_key, test->use, &result, + NULL, GFP_KERNEL); + break; + case TEST_KI: + ret = crypto_krb5_get_Ki(krb5, base_key, test->use, &result, + NULL, GFP_KERNEL); + break; + default: + VALID(1); + ret = -EINVAL; + goto out; + } + + if (ret < 0) { + CHECK(1); + pr_warn("Key derivation failed %d\n", ret); + goto out; + } + + if (memcmp(result.data, key.data, result.len) != 0) { + CHECK(1); + ret = -EKEYREJECTED; + goto out; + } + +out: + clear_buf(&key); + clear_buf(&result); + return ret; +} + +static int krb5_test_one_key(const struct krb5_key_test *test) +{ + const struct krb5_enctype *krb5 = test->krb5; + struct krb5_buffer base_key = {}; + int ret; + + pr_notice("Running %s %s\n", krb5->name, test->name); + + LOAD_BUF(&base_key, test->key); + + if ((ret = krb5_test_key(krb5, &base_key, &test->Kc, TEST_KC)) < 0) + goto out; + if ((ret = krb5_test_key(krb5, &base_key, &test->Ke, TEST_KE)) < 0) + goto out; + if ((ret = krb5_test_key(krb5, &base_key, &test->Ki, TEST_KI)) < 0) + goto out; + +out: + clear_buf(&base_key); + return ret; +} + +static int krb5_test_get_Kc(const struct krb5_mic_test *test, + struct crypto_shash **_Kc) +{ + const struct krb5_enctype *krb5 = test->krb5; + struct crypto_shash *shash; + struct krb5_buffer K0 = {}, key = {}; + int ret; + + shash = crypto_alloc_shash(krb5->cksum_name, 0, 0); + if (IS_ERR(shash)) + return (PTR_ERR(shash) == -ENOENT) ? -ENOPKG : PTR_ERR(shash); + *_Kc = shash; + + if (test->Kc) { + LOAD_BUF(&key, test->Kc); + } else { + char usage_data[5]; + struct krb5_buffer usage = { .len = 5, .data = usage_data }; + memcpy(usage_data, &test->usage, 4); + usage_data[4] = 0x99; + LOAD_BUF(&K0, test->K0); + PREP_BUF(&key, krb5->Kc_len); + ret = krb5->profile->calc_Kc(krb5, &K0, &usage, &key, GFP_KERNEL); + } + + ret = crypto_shash_setkey(shash, key.data, key.len); +out: + clear_buf(&key); + clear_buf(&K0); + return ret; +} + +static int krb5_test_get_Ke(const struct krb5_enc_test *test, + struct krb5_enc_keys *keys) +{ + const struct krb5_enctype *krb5 = test->krb5; + struct crypto_sync_skcipher *ci; + struct krb5_buffer K0 = {}, key = {}; + int ret; + + ci = crypto_alloc_sync_skcipher(krb5->encrypt_name, 0, 0); + if (IS_ERR(ci)) + return (PTR_ERR(ci) == -ENOENT) ? -ENOPKG : PTR_ERR(ci); + keys->Ke = ci; + + if (test->Ke) { + LOAD_BUF(&key, test->Ke); + } else { + char usage_data[5]; + struct krb5_buffer usage = { .len = 5, .data = usage_data }; + memcpy(usage_data, &test->usage, 4); + usage_data[4] = 0xAA; + LOAD_BUF(&K0, test->K0); + PREP_BUF(&key, krb5->Ke_len); + ret = krb5->profile->calc_Ke(krb5, &K0, &usage, &key, GFP_KERNEL); + } + + ret = crypto_sync_skcipher_setkey(ci, key.data, key.len); +out: + clear_buf(&key); + clear_buf(&K0); + return ret; +} + +static int krb5_test_get_Ki(const struct krb5_enc_test *test, + struct krb5_enc_keys *keys) +{ + const struct krb5_enctype *krb5 = test->krb5; + struct crypto_shash *shash; + struct krb5_buffer K0 = {}, key = {}; + int ret; + + shash = crypto_alloc_shash(krb5->cksum_name, 0, 0); + if (IS_ERR(shash)) + return (PTR_ERR(shash) == -ENOENT) ? -ENOPKG : PTR_ERR(shash); + keys->Ki = shash; + + if (test->Ki) { + LOAD_BUF(&key, test->Ki); + } else { + char usage_data[5]; + struct krb5_buffer usage = { .len = 5, .data = usage_data }; + memcpy(usage_data, &test->usage, 4); + usage_data[4] = 0x55; + LOAD_BUF(&K0, test->K0); + PREP_BUF(&key, krb5->Ki_len); + ret = krb5->profile->calc_Ki(krb5, &K0, &usage, &key, GFP_KERNEL); + } + + ret = crypto_shash_setkey(shash, key.data, key.len); +out: + clear_buf(&key); + clear_buf(&K0); + return ret; +} + +/* + * Generate a buffer containing encryption test data. + */ +static int krb5_load_enc_buf(const struct krb5_enc_test *test, + const struct krb5_buffer *plain, + void *buf) +{ + const struct krb5_enctype *krb5 = test->krb5; + unsigned int conf_len, pad_len, enc_len, ct_len; + int ret; + + conf_len = strlen(test->conf); + if (VALID((conf_len & 1) || conf_len / 2 != krb5->conf_len)) + return -EINVAL; + + if (krb5->pad) { + enc_len = round_up(krb5->conf_len + plain->len, krb5->block_len); + pad_len = enc_len - (krb5->conf_len + plain->len); + } else { + enc_len = krb5->conf_len + plain->len; + pad_len = 0; + } + + ct_len = strlen(test->ct); + if (VALID((ct_len & 1) || ct_len / 2 != enc_len + krb5->cksum_len)) + return -EINVAL; + ct_len = enc_len + krb5->cksum_len; + + if ((ret = hex2bin(buf, test->conf, krb5->conf_len)) < 0) + return ret; + buf += krb5->conf_len; + memcpy(buf, plain->data, plain->len); + return 0; +} + +/* + * Load checksum test data into a buffer. + */ +static int krb5_load_mic_buf(const struct krb5_mic_test *test, + const struct krb5_buffer *plain, + void *buf) +{ + const struct krb5_enctype *krb5 = test->krb5; + + memcpy(buf + krb5->cksum_len, plain->data, plain->len); + return 0; +} + +/* + * Perform an encryption test. + */ +static int krb5_test_one_enc(const struct krb5_enc_test *test, void *buf) +{ + const struct krb5_enctype *krb5 = test->krb5; + struct krb5_enc_keys keys = {}; + struct krb5_buffer plain = {}, ct = {}; + struct scatterlist sg[1]; + size_t offset, len; + int ret, error_code; + + pr_notice("Running %s %s\n", krb5->name, test->name); + + if ((ret = krb5_test_get_Ke(test, &keys)) < 0 || + (ret = krb5_test_get_Ki(test, &keys)) < 0) + goto out; + + LOAD_BUF(&plain, test->plain); + LOAD_BUF(&ct, test->ct); + + ret = krb5_load_enc_buf(test, &plain, buf); + if (ret < 0) + goto out; + + sg_init_one(sg, buf, 1024); + ret = crypto_krb5_encrypt(krb5, &keys, sg, 1, 1024, + krb5->conf_len, plain.len, true); + if (ret < 0) { + CHECK(1); + pr_warn("Encryption failed %d\n", ret); + goto out; + } + len = ret; + + if (CHECK(len != ct.len)) { + pr_warn("Encrypted length mismatch %zu != %u\n", len, ct.len); + goto out; + } + + if (memcmp(buf, ct.data, ct.len) != 0) { + CHECK(1); + pr_warn("Ciphertext mismatch\n"); + pr_warn("BUF %*phN\n", ct.len, buf); + pr_warn("CT %*phN\n", ct.len, ct.data); + ret = -EKEYREJECTED; + goto out; + } + + offset = 0; + ret = crypto_krb5_decrypt(krb5, &keys, sg, 1, &offset, &len, &error_code); + if (ret < 0) { + CHECK(1); + pr_warn("Decryption failed %d\n", ret); + goto out; + } + + if (CHECK(len != plain.len)) + goto out; + + if (memcmp(buf + offset, plain.data, plain.len) != 0) { + CHECK(1); + pr_warn("Plaintext mismatch\n"); + pr_warn("BUF %*phN\n", plain.len, buf + offset); + pr_warn("PT %*phN\n", plain.len, plain.data); + ret = -EKEYREJECTED; + goto out; + } + + ret = 0; + +out: + clear_buf(&ct); + clear_buf(&plain); + crypto_krb5_free_enc_keys(&keys); + return ret; +} + +static int krb5_test_one_mic(const struct krb5_mic_test *test, void *buf) +{ + const struct krb5_enctype *krb5 = test->krb5; + struct crypto_shash *Kc = NULL; + struct scatterlist sg[1]; + struct krb5_buffer plain = {}, mic = {}; + size_t offset, len; + int ret, error_code; + + pr_notice("Running %s %s\n", krb5->name, test->name); + + if ((ret = krb5_test_get_Kc(test, &Kc)) < 0) + goto out; + + LOAD_BUF(&plain, test->plain); + LOAD_BUF(&mic, test->mic); + + ret = krb5_load_mic_buf(test, &plain, buf); + if (ret < 0) + goto out; + + sg_init_one(sg, buf, 1024); + + ret = crypto_krb5_get_mic(krb5, Kc, NULL, sg, 1, 1024, + krb5->cksum_len, plain.len); + if (ret < 0) { + CHECK(1); + pr_warn("Get MIC failed %d\n", ret); + goto out; + } + len = ret; + + if (CHECK(len != plain.len + mic.len)) { + pr_warn("MIC length mismatch %zu != %u\n", len, plain.len + mic.len); + goto out; + } + + if (memcmp(buf, mic.data, mic.len) != 0) { + CHECK(1); + pr_warn("MIC mismatch\n"); + pr_warn("BUF %*phN\n", mic.len, buf); + pr_warn("MIC %*phN\n", mic.len, mic.data); + ret = -EKEYREJECTED; + goto out; + } + + offset = 0; + ret = crypto_krb5_verify_mic(krb5, Kc, NULL, sg, 1, + &offset, &len, &error_code); + if (ret < 0) { + CHECK(1); + pr_warn("Verify MIC failed %d\n", ret); + goto out; + } + + if (CHECK(len != plain.len)) + goto out; + if (CHECK(offset != mic.len)) + goto out; + + if (memcmp(buf + offset, plain.data, plain.len) != 0) { + CHECK(1); + pr_warn("Plaintext mismatch\n"); + pr_warn("BUF %*phN\n", plain.len, buf + offset); + pr_warn("PT %*phN\n", plain.len, plain.data); + ret = -EKEYREJECTED; + goto out; + } + + ret = 0; + +out: + clear_buf(&mic); + clear_buf(&plain); + if (Kc) + crypto_free_shash(Kc); + return ret; +} + +void krb5_selftest(void) +{ + void *buf; + bool fail = false; + int i; + + buf = kmalloc(1024, GFP_KERNEL); + if (!buf) + return; + + printk("\n"); + pr_notice("Running selftests\n"); + + for (i = 0; krb5_prf_tests[i].krb5; i++) { + fail |= krb5_test_one_prf(&krb5_prf_tests[i]) < 0; + if (fail) + goto out; + } + + for (i = 0; krb5_key_tests[i].krb5; i++) { + fail |= krb5_test_one_key(&krb5_key_tests[i]) < 0; + if (fail) + goto out; + } + + for (i = 0; krb5_enc_tests[i].krb5; i++) { + memset(buf, 0x5a, 1024); + fail |= krb5_test_one_enc(&krb5_enc_tests[i], buf) < 0; + if (fail) + goto out; + } + + for (i = 0; krb5_mic_tests[i].krb5; i++) { + memset(buf, 0x5a, 1024); + fail |= krb5_test_one_mic(&krb5_mic_tests[i], buf) < 0; + if (fail) + goto out; + } + +out: + pr_notice("Selftests %s\n", fail ? "failed" : "succeeded"); + kfree(buf); +} diff --git a/crypto/krb5/selftest_data.c b/crypto/krb5/selftest_data.c new file mode 100644 index 000000000000..9085723b730b --- /dev/null +++ b/crypto/krb5/selftest_data.c @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Data for RxGK self-testing + * + * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "internal.h" + +/* + * Pseudo-random function tests. + */ +const struct krb5_prf_test krb5_prf_tests[] = { + {/* END */} +}; + +/* + * Key derivation tests. + */ +const struct krb5_key_test krb5_key_tests[] = { + {/* END */} +}; + +/* + * Encryption tests. + */ +const struct krb5_enc_test krb5_enc_tests[] = { + {/* END */} +}; + +/* + * Checksum generation tests. + */ +const struct krb5_mic_test krb5_mic_tests[] = { + {/* END */} +}; From patchwork Thu Nov 12 12:58:58 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Howells X-Patchwork-Id: 11900013 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.9 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id F1A49C2D0A3 for ; Thu, 12 Nov 2020 12:59:17 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 944EE21D91 for ; Thu, 12 Nov 2020 12:59:17 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="hr35b+1i" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728425AbgKLM7Q (ORCPT ); Thu, 12 Nov 2020 07:59:16 -0500 Received: from us-smtp-delivery-124.mimecast.com ([216.205.24.124]:32489 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728408AbgKLM7K (ORCPT ); Thu, 12 Nov 2020 07:59:10 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1605185948; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=SIMOin/av7q3MUxt47nGrwOjr3mQiZb4LuG2UAH2unI=; b=hr35b+1ib6ExD2PEG9+3tneDFoqJAjHmNO/1ZqGdOxcVaqktU7KPx3nVHRrEwUyfW3iPUU 0SMnoVL0OwjQommqhy5/7MUKpqui9NZFGRi92LiQn+5xtjSfSwh4nueEcnQjb0lMiBA/pN 5xHbXgUGw25lhdaUmp68MTN9rJFVqFg= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-7-jQGw346-PjuIWaBBSUW1BA-1; Thu, 12 Nov 2020 07:59:03 -0500 X-MC-Unique: jQGw346-PjuIWaBBSUW1BA-1 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id A3C48801FDF; Thu, 12 Nov 2020 12:59:01 +0000 (UTC) Received: from warthog.procyon.org.uk (ovpn-115-47.rdu2.redhat.com [10.10.115.47]) by smtp.corp.redhat.com (Postfix) with ESMTP id A1C3B5D9E4; Thu, 12 Nov 2020 12:58:59 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH 09/18] crypto/krb5: Implement the AES enctypes from rfc8009 From: David Howells To: herbert@gondor.apana.org.au, bfields@fieldses.org Cc: dhowells@redhat.com, trond.myklebust@hammerspace.com, linux-crypto@vger.kernel.org, linux-afs@lists.infradead.org, linux-cifs@vger.kernel.org, linux-nfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 12 Nov 2020 12:58:58 +0000 Message-ID: <160518593886.2277919.4740986612401034649.stgit@warthog.procyon.org.uk> In-Reply-To: <160518586534.2277919.14475638653680231924.stgit@warthog.procyon.org.uk> References: <160518586534.2277919.14475638653680231924.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Implement the aes128-cts-hmac-sha256-128 and aes256-cts-hmac-sha384-192 enctypes from rfc8009, overriding the rfc3961 kerberos 5 simplified crypto scheme. Signed-off-by: David Howells --- crypto/krb5/Kconfig | 2 crypto/krb5/Makefile | 3 - crypto/krb5/internal.h | 6 + crypto/krb5/main.c | 2 crypto/krb5/rfc8009_aes2.c | 239 ++++++++++++++++++++++++++++++++++++++++++++ include/crypto/krb5.h | 4 + 6 files changed, 255 insertions(+), 1 deletion(-) create mode 100644 crypto/krb5/rfc8009_aes2.c diff --git a/crypto/krb5/Kconfig b/crypto/krb5/Kconfig index e2eba1d689ab..5607c0c81049 100644 --- a/crypto/krb5/Kconfig +++ b/crypto/krb5/Kconfig @@ -4,6 +4,8 @@ config CRYPTO_KRB5 select CRYPTO_SKCIPHER select CRYPTO_HASH_INFO select CRYPTO_SHA1 + select CRYPTO_SHA256 + select CRYPTO_SHA512 select CRYPTO_CBC select CRYPTO_CTS select CRYPTO_AES diff --git a/crypto/krb5/Makefile b/crypto/krb5/Makefile index b7da03cae6d1..85763131f7b6 100644 --- a/crypto/krb5/Makefile +++ b/crypto/krb5/Makefile @@ -7,7 +7,8 @@ krb5-y += \ kdf.o \ main.o \ rfc3961_simplified.o \ - rfc3962_aes.o + rfc3962_aes.o \ + rfc8009_aes2.o krb5-$(CONFIG_CRYPTO_KRB5_SELFTESTS) += \ selftest.o \ diff --git a/crypto/krb5/internal.h b/crypto/krb5/internal.h index 47424b433778..e64f5e58199f 100644 --- a/crypto/krb5/internal.h +++ b/crypto/krb5/internal.h @@ -158,6 +158,12 @@ int rfc3961_verify_mic(const struct krb5_enctype *krb5, extern const struct krb5_enctype krb5_aes128_cts_hmac_sha1_96; extern const struct krb5_enctype krb5_aes256_cts_hmac_sha1_96; +/* + * rfc8009_aes2.c + */ +extern const struct krb5_enctype krb5_aes128_cts_hmac_sha256_128; +extern const struct krb5_enctype krb5_aes256_cts_hmac_sha384_192; + /* * selftest.c */ diff --git a/crypto/krb5/main.c b/crypto/krb5/main.c index b79127027551..9914d3417c21 100644 --- a/crypto/krb5/main.c +++ b/crypto/krb5/main.c @@ -20,6 +20,8 @@ MODULE_LICENSE("GPL"); static const struct krb5_enctype *const krb5_supported_enctypes[] = { &krb5_aes128_cts_hmac_sha1_96, &krb5_aes256_cts_hmac_sha1_96, + &krb5_aes128_cts_hmac_sha256_128, + &krb5_aes256_cts_hmac_sha384_192, }; /** diff --git a/crypto/krb5/rfc8009_aes2.c b/crypto/krb5/rfc8009_aes2.c new file mode 100644 index 000000000000..9f0f0f410d91 --- /dev/null +++ b/crypto/krb5/rfc8009_aes2.c @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* rfc8009 AES Encryption with HMAC-SHA2 for Kerberos 5 + * + * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include "internal.h" + +static const struct krb5_buffer rfc8009_no_context = { .len = 0, .data = "" }; + +/* + * Calculate the key derivation function KDF-HMAC-SHA2(key, label, [context,] k) + * + * KDF-HMAC-SHA2(key, label, [context,] k) = k-truncate(K1) + * + * Using the appropriate one of: + * K1 = HMAC-SHA-256(key, 0x00000001 | label | 0x00 | k) + * K1 = HMAC-SHA-384(key, 0x00000001 | label | 0x00 | k) + * K1 = HMAC-SHA-256(key, 0x00000001 | label | 0x00 | context | k) + * K1 = HMAC-SHA-384(key, 0x00000001 | label | 0x00 | context | k) + * [rfc8009 sec 3] + */ +static int rfc8009_calc_KDF_HMAC_SHA2(const struct krb5_enctype *krb5, + const struct krb5_buffer *key, + const struct krb5_buffer *label, + const struct krb5_buffer *context, + unsigned int k, + struct krb5_buffer *result, + gfp_t gfp) +{ + struct crypto_shash *shash; + struct krb5_buffer K1, data; + struct shash_desc *desc; + __be32 tmp; + size_t bsize; + void *buffer; + u8 *p; + int ret = -ENOMEM; + + if (WARN_ON(result->len != k / 8)) + return -EINVAL; + + shash = crypto_alloc_shash(krb5->cksum_name, 0, 0); + if (IS_ERR(shash)) + return (PTR_ERR(shash) == -ENOENT) ? -ENOPKG : PTR_ERR(shash); + ret = crypto_shash_setkey(shash, key->data, key->len); + if (ret < 0) + goto error_shash; + + ret = -EINVAL; + if (WARN_ON(crypto_shash_digestsize(shash) * 8 < k)) + goto error_shash; + + ret = -ENOMEM; + data.len = 4 + label->len + 1 + context->len + 4; + bsize = krb5_shash_size(shash) + + krb5_digest_size(shash) + + crypto_roundup(data.len); + buffer = kzalloc(bsize, GFP_NOFS); + if (!buffer) + goto error_shash; + + desc = buffer; + desc->tfm = shash; + ret = crypto_shash_init(desc); + if (ret < 0) + goto error; + + p = data.data = buffer + + krb5_shash_size(shash) + + krb5_digest_size(shash); + *(__be32 *)p = htonl(0x00000001); + p += 4; + memcpy(p, label->data, label->len); + p += label->len; + *p++ = 0; + memcpy(p, context->data, context->len); + p += context->len; + tmp = htonl(k); + memcpy(p, &tmp, 4); + p += 4; + + ret = -EINVAL; + if (WARN_ON(p - (u8 *)data.data != data.len)) + goto error; + + K1.len = crypto_shash_digestsize(shash); + K1.data = buffer + + krb5_shash_size(shash); + + ret = crypto_shash_finup(desc, data.data, data.len, K1.data); + if (ret < 0) + goto error; + + memcpy(result->data, K1.data, result->len); + +error: + kfree_sensitive(buffer); +error_shash: + crypto_free_shash(shash); + return ret; +} + +/* + * Calculate the pseudo-random function, PRF(). + * + * PRF = KDF-HMAC-SHA2(input-key, "prf", octet-string, 256) + * PRF = KDF-HMAC-SHA2(input-key, "prf", octet-string, 384) + * + * The "prfconstant" used in the PRF operation is the three-octet string + * "prf". + * [rfc8009 sec 5] + */ +static int rfc8009_calc_PRF(const struct krb5_enctype *krb5, + const struct krb5_buffer *input_key, + const struct krb5_buffer *octet_string, + struct krb5_buffer *result, + gfp_t gfp) +{ + static const struct krb5_buffer prfconstant = { 3, "prf" }; + + return rfc8009_calc_KDF_HMAC_SHA2(krb5, input_key, &prfconstant, + octet_string, krb5->prf_len * 8, + result, gfp); +} + +/* + * Derive Ke. + * Ke = KDF-HMAC-SHA2(base-key, usage | 0xAA, 128) + * Ke = KDF-HMAC-SHA2(base-key, usage | 0xAA, 256) + * [rfc8009 sec 5] + */ +static int rfc8009_calc_Ke(const struct krb5_enctype *krb5, + const struct krb5_buffer *base_key, + const struct krb5_buffer *usage_constant, + struct krb5_buffer *result, + gfp_t gfp) +{ + return rfc8009_calc_KDF_HMAC_SHA2(krb5, base_key, usage_constant, + &rfc8009_no_context, krb5->key_bytes * 8, + result, gfp); +} + +/* + * Derive Kc/Ki + * Kc = KDF-HMAC-SHA2(base-key, usage | 0x99, 128) + * Ki = KDF-HMAC-SHA2(base-key, usage | 0x55, 128) + * Kc = KDF-HMAC-SHA2(base-key, usage | 0x99, 192) + * Ki = KDF-HMAC-SHA2(base-key, usage | 0x55, 192) + * [rfc8009 sec 5] + */ +static int rfc8009_calc_Ki(const struct krb5_enctype *krb5, + const struct krb5_buffer *base_key, + const struct krb5_buffer *usage_constant, + struct krb5_buffer *result, + gfp_t gfp) +{ + return rfc8009_calc_KDF_HMAC_SHA2(krb5, base_key, usage_constant, + &rfc8009_no_context, krb5->cksum_len * 8, + result, gfp); +} + +/* + * AES random-to-key function. For AES, this is an identity operation. + */ +static int rfc8009_random_to_key(const struct krb5_enctype *krb5, + const struct krb5_buffer *randombits, + struct krb5_buffer *result) +{ + if (randombits->len != 16 && randombits->len != 32) + return -EINVAL; + + if (result->len != randombits->len) + return -EINVAL; + + memcpy(result->data, randombits->data, randombits->len); + return 0; +} + +static const struct krb5_crypto_profile rfc8009_crypto_profile = { + .calc_PRF = rfc8009_calc_PRF, + .calc_Kc = rfc8009_calc_Ki, + .calc_Ke = rfc8009_calc_Ke, + .calc_Ki = rfc8009_calc_Ki, + .encrypt = NULL, //rfc8009_encrypt, + .decrypt = NULL, //rfc8009_decrypt, + .get_mic = rfc3961_get_mic, + .verify_mic = rfc3961_verify_mic, +}; + +const struct krb5_enctype krb5_aes128_cts_hmac_sha256_128 = { + .etype = KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128, + .ctype = KRB5_CKSUMTYPE_HMAC_SHA256_128_AES128, + .name = "aes128-cts-hmac-sha256-128", + .encrypt_name = "cts(cbc(aes))", + .cksum_name = "hmac(sha256)", + .hash_name = "sha256", + .key_bytes = 16, + .key_len = 16, + .Kc_len = 16, + .Ke_len = 16, + .Ki_len = 16, + .block_len = 16, + .conf_len = 16, + .cksum_len = 16, + .hash_len = 20, + .prf_len = 32, + .keyed_cksum = true, + .random_to_key = rfc8009_random_to_key, + .profile = &rfc8009_crypto_profile, +}; + +const struct krb5_enctype krb5_aes256_cts_hmac_sha384_192 = { + .etype = KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192, + .ctype = KRB5_CKSUMTYPE_HMAC_SHA384_192_AES256, + .name = "aes256-cts-hmac-sha384-192", + .encrypt_name = "cts(cbc(aes))", + .cksum_name = "hmac(sha384)", + .hash_name = "sha384", + .key_bytes = 32, + .key_len = 32, + .Kc_len = 24, + .Ke_len = 32, + .Ki_len = 24, + .block_len = 16, + .conf_len = 16, + .cksum_len = 24, + .hash_len = 20, + .prf_len = 48, + .keyed_cksum = true, + .random_to_key = rfc8009_random_to_key, + .profile = &rfc8009_crypto_profile, +}; diff --git a/include/crypto/krb5.h b/include/crypto/krb5.h index b83d3d487753..f38a5b4d97ee 100644 --- a/include/crypto/krb5.h +++ b/include/crypto/krb5.h @@ -32,6 +32,8 @@ struct scatterlist; #define KRB5_ENCTYPE_DES3_CBC_SHA1 0x0010 #define KRB5_ENCTYPE_AES128_CTS_HMAC_SHA1_96 0x0011 #define KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96 0x0012 +#define KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128 0x0013 +#define KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192 0x0014 #define KRB5_ENCTYPE_ARCFOUR_HMAC 0x0017 #define KRB5_ENCTYPE_ARCFOUR_HMAC_EXP 0x0018 #define KRB5_ENCTYPE_UNKNOWN 0x01ff @@ -46,6 +48,8 @@ struct scatterlist; #define KRB5_CKSUMTYPE_HMAC_SHA1_DES3 0x000c #define KRB5_CKSUMTYPE_HMAC_SHA1_96_AES128 0x000f #define KRB5_CKSUMTYPE_HMAC_SHA1_96_AES256 0x0010 +#define KRB5_CKSUMTYPE_HMAC_SHA256_128_AES128 0x0013 +#define KRB5_CKSUMTYPE_HMAC_SHA384_192_AES256 0x0014 #define KRB5_CKSUMTYPE_HMAC_MD5_ARCFOUR -138 /* Microsoft md5 hmac cksumtype */ /* From patchwork Thu Nov 12 12:59:06 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Howells X-Patchwork-Id: 11900015 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.9 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 65C89C56202 for ; Thu, 12 Nov 2020 12:59:19 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 011C921D91 for ; Thu, 12 Nov 2020 12:59:18 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="WDZcoANB" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727822AbgKLM7R (ORCPT ); Thu, 12 Nov 2020 07:59:17 -0500 Received: from us-smtp-delivery-124.mimecast.com ([216.205.24.124]:27814 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728416AbgKLM7P (ORCPT ); Thu, 12 Nov 2020 07:59:15 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1605185954; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=dnJhHrlydFaDDYDVAILjoCMJPy7l/G38vJ71iG4RtWQ=; b=WDZcoANBvWeQcISPnhPkMLoKlEoP9lWoQtscYoPS2tOwFm/EVjgGR0eHyYxSowBopZIC2t Q6GMvjrifWiRnA13JSDftfQke4VL2qPjOpkBYMYOWw6RtzNew4DfaAHtamrPuwJQeC6hU5 wg1dUzVmOC+Wbk7uLjeJSGPDUFUtn1c= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-385-k4rmHDhkN72dmJfCls03fA-1; Thu, 12 Nov 2020 07:59:11 -0500 X-MC-Unique: k4rmHDhkN72dmJfCls03fA-1 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id D1F2A879523; Thu, 12 Nov 2020 12:59:09 +0000 (UTC) Received: from warthog.procyon.org.uk (ovpn-115-47.rdu2.redhat.com [10.10.115.47]) by smtp.corp.redhat.com (Postfix) with ESMTP id B25D960C13; Thu, 12 Nov 2020 12:59:07 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH 10/18] crypto/krb5: Implement the AES encrypt/decrypt from rfc8009 From: David Howells To: herbert@gondor.apana.org.au, bfields@fieldses.org Cc: dhowells@redhat.com, trond.myklebust@hammerspace.com, linux-crypto@vger.kernel.org, linux-afs@lists.infradead.org, linux-cifs@vger.kernel.org, linux-nfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 12 Nov 2020 12:59:06 +0000 Message-ID: <160518594688.2277919.5319312384536950170.stgit@warthog.procyon.org.uk> In-Reply-To: <160518586534.2277919.14475638653680231924.stgit@warthog.procyon.org.uk> References: <160518586534.2277919.14475638653680231924.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Implement encryption and decryption functions for AES + HMAC-SHA2 as described in rfc8009 sec 5. Signed-off-by: David Howells --- crypto/krb5/rfc8009_aes2.c | 205 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 203 insertions(+), 2 deletions(-) diff --git a/crypto/krb5/rfc8009_aes2.c b/crypto/krb5/rfc8009_aes2.c index 9f0f0f410d91..df517435be73 100644 --- a/crypto/krb5/rfc8009_aes2.c +++ b/crypto/krb5/rfc8009_aes2.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "internal.h" static const struct krb5_buffer rfc8009_no_context = { .len = 0, .data = "" }; @@ -183,13 +184,213 @@ static int rfc8009_random_to_key(const struct krb5_enctype *krb5, return 0; } +/* + * Apply encryption and checksumming functions to part of an skbuff. + */ +static ssize_t rfc8009_encrypt(const struct krb5_enctype *krb5, + struct krb5_enc_keys *keys, + struct scatterlist *sg, unsigned nr_sg, size_t sg_len, + size_t data_offset, size_t data_len, + bool preconfounded) +{ + struct skcipher_request *req; + struct shash_desc *desc; + ssize_t ret, done; + size_t bsize, base_len, secure_offset, secure_len, pad_len, cksum_offset; + void *buffer; + u8 *cksum, *iv; + + if (WARN_ON(data_offset != krb5->conf_len)) + return -EINVAL; /* Can't set offset on skcipher */ + + base_len = krb5->conf_len + data_len; + secure_len = base_len; + pad_len = secure_len - base_len; + secure_offset = 0; + cksum_offset = secure_offset + secure_len; + + bsize = krb5_shash_size(keys->Ki) + + krb5_digest_size(keys->Ki) + + krb5_sync_skcipher_size(keys->Ke) + + krb5_sync_skcipher_ivsize(keys->Ke); + bsize = max_t(size_t, bsize, krb5->conf_len); + bsize = max_t(size_t, bsize, krb5->block_len); + buffer = kzalloc(bsize, GFP_NOFS); + if (!buffer) + return -ENOMEM; + + /* Insert the confounder into the skb */ + ret = -EFAULT; + if (!preconfounded) { + get_random_bytes(buffer, krb5->conf_len); + done = sg_pcopy_to_buffer(sg, nr_sg, buffer, krb5->conf_len, + secure_offset); + if (done != krb5->conf_len) + goto error; + } + + /* We need to pad out to the crypto blocksize. */ + if (pad_len) { + done = sg_zero_buffer(sg, nr_sg, pad_len, data_offset + data_len); + if (done != pad_len) + goto error; + } + + /* Encrypt the secure region with key Ke. */ + req = buffer + + krb5_shash_size(keys->Ki) + + krb5_digest_size(keys->Ki); + iv = buffer + + krb5_shash_size(keys->Ki) + + krb5_digest_size(keys->Ki) + + krb5_sync_skcipher_size(keys->Ke); + + skcipher_request_set_sync_tfm(req, keys->Ke); + skcipher_request_set_callback(req, 0, NULL, NULL); + skcipher_request_set_crypt(req, sg, sg, secure_len, iv); + ret = crypto_skcipher_encrypt(req); + if (ret < 0) + goto error; + + /* Calculate the checksum using key Ki */ + cksum = buffer + krb5_shash_size(keys->Ki); + + desc = buffer; + desc->tfm = keys->Ki; + ret = crypto_shash_init(desc); + if (ret < 0) + goto error; + + memset(iv, 0, crypto_sync_skcipher_ivsize(keys->Ke)); + ret = crypto_shash_update(desc, iv, crypto_sync_skcipher_ivsize(keys->Ke)); + if (ret < 0) + goto error; + + ret = crypto_shash_update_sg(desc, sg, secure_offset, secure_len); + if (ret < 0) + goto error; + + ret = crypto_shash_final(desc, cksum); + if (ret < 0) + goto error; + + /* Append the checksum into the buffer. */ + ret = -EFAULT; + sg_zero_buffer(sg, nr_sg, 3, cksum_offset); + done = sg_pcopy_from_buffer(sg, nr_sg, cksum, krb5->cksum_len, cksum_offset); + if (done != krb5->cksum_len) + goto error; + + ret = secure_len + krb5->cksum_len; + +error: + kfree_sensitive(buffer); + return ret; +} + +/* + * Apply decryption and checksumming functions to part of an skbuff. The + * offset and length are updated to reflect the actual content of the encrypted + * region. + */ +static int rfc8009_decrypt(const struct krb5_enctype *krb5, + struct krb5_enc_keys *keys, + struct scatterlist *sg, unsigned nr_sg, + size_t *_offset, size_t *_len, + int *_error_code) +{ + struct skcipher_request *req; + struct shash_desc *desc; + ssize_t done; + size_t bsize, secure_len, offset = *_offset, len = *_len; + void *buffer = NULL; + int ret; + u8 *cksum, *cksum2, *iv; + + if (WARN_ON(*_offset != 0)) + return -EINVAL; /* Can't set offset on skcipher */ + + if (len < krb5->conf_len + krb5->cksum_len) { + *_error_code = 1; //RXGK_SEALED_INCON; + return -EPROTO; + } + secure_len = len - krb5->cksum_len; + + bsize = krb5_shash_size(keys->Ki) + + krb5_digest_size(keys->Ki) * 2 + + krb5_sync_skcipher_size(keys->Ke) + + krb5_sync_skcipher_ivsize(keys->Ke); + buffer = kzalloc(bsize, GFP_NOFS); + if (!buffer) + return -ENOMEM; + + cksum = buffer + + krb5_shash_size(keys->Ki); + cksum2 = buffer + + krb5_shash_size(keys->Ki) + + krb5_digest_size(keys->Ki); + req = buffer + + krb5_shash_size(keys->Ki) + + krb5_digest_size(keys->Ki) * 2; + iv = buffer + + krb5_shash_size(keys->Ki) + + krb5_digest_size(keys->Ki) * 2 + + krb5_sync_skcipher_size(keys->Ke); + + /* Calculate the checksum using key Ki */ + desc = buffer; + desc->tfm = keys->Ki; + ret = crypto_shash_init(desc); + if (ret < 0) + goto error; + + ret = crypto_shash_update(desc, iv, crypto_sync_skcipher_ivsize(keys->Ke)); + if (ret < 0) + goto error; + + ret = crypto_shash_update_sg(desc, sg, 0, secure_len); + if (ret < 0) + goto error; + + ret = crypto_shash_final(desc, cksum); + if (ret < 0) + goto error; + + /* Get the checksum from the buffer. */ + ret = -EFAULT; + done = sg_pcopy_to_buffer(sg, nr_sg, cksum2, krb5->cksum_len, + offset + len - krb5->cksum_len); + if (done != krb5->cksum_len) + goto error; + + if (memcmp(cksum, cksum2, krb5->cksum_len) != 0) { + *_error_code = 1; //RXGK_SEALED_INCON; + ret = -EPROTO; + goto error; + } + + /* Decrypt the secure region with key Ke. */ + skcipher_request_set_sync_tfm(req, keys->Ke); + skcipher_request_set_callback(req, 0, NULL, NULL); + skcipher_request_set_crypt(req, sg, sg, secure_len, iv); + ret = crypto_skcipher_decrypt(req); + + *_offset += krb5->conf_len; + *_len -= krb5->conf_len + krb5->cksum_len; + ret = 0; + +error: + kfree_sensitive(buffer); + return ret; +} + static const struct krb5_crypto_profile rfc8009_crypto_profile = { .calc_PRF = rfc8009_calc_PRF, .calc_Kc = rfc8009_calc_Ki, .calc_Ke = rfc8009_calc_Ke, .calc_Ki = rfc8009_calc_Ki, - .encrypt = NULL, //rfc8009_encrypt, - .decrypt = NULL, //rfc8009_decrypt, + .encrypt = rfc8009_encrypt, + .decrypt = rfc8009_decrypt, .get_mic = rfc3961_get_mic, .verify_mic = rfc3961_verify_mic, }; From patchwork Thu Nov 12 12:59:15 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Howells X-Patchwork-Id: 11900017 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.9 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8AB88C388F7 for ; Thu, 12 Nov 2020 12:59:32 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 3395621D91 for ; Thu, 12 Nov 2020 12:59:32 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="Hpufy3QU" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728441AbgKLM7b (ORCPT ); Thu, 12 Nov 2020 07:59:31 -0500 Received: from us-smtp-delivery-124.mimecast.com ([63.128.21.124]:43648 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728384AbgKLM70 (ORCPT ); Thu, 12 Nov 2020 07:59:26 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1605185964; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=WoSb32kjs+HkCDM7/fcpglBleXtzkTO/HB6K14aykcc=; b=Hpufy3QUUcwJ636DgUkR8ApvNJlqITrFZbgKQVbHgZpiT+GTSv+Pl7rfkMCe1VldmfqRGh 5g1N1NLfEM7Mi5l69ef5QzhEGsfEtkh+mnbRbhu7+REmz0KnD/VsFCHiuz5zgQ1pp9U76P A5ljLYHrLGaDBk/WNYyOO1aXxB2Je54= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-20-pfyv39bUNAampm5-fnZTTg-1; Thu, 12 Nov 2020 07:59:20 -0500 X-MC-Unique: pfyv39bUNAampm5-fnZTTg-1 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id D68931016CE5; Thu, 12 Nov 2020 12:59:17 +0000 (UTC) Received: from warthog.procyon.org.uk (ovpn-115-47.rdu2.redhat.com [10.10.115.47]) by smtp.corp.redhat.com (Postfix) with ESMTP id D55B35D9E4; Thu, 12 Nov 2020 12:59:15 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH 11/18] crypto/krb5: Add the AES self-testing data from rfc8009 From: David Howells To: herbert@gondor.apana.org.au, bfields@fieldses.org Cc: dhowells@redhat.com, trond.myklebust@hammerspace.com, linux-crypto@vger.kernel.org, linux-afs@lists.infradead.org, linux-cifs@vger.kernel.org, linux-nfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 12 Nov 2020 12:59:15 +0000 Message-ID: <160518595508.2277919.15065092736353629314.stgit@warthog.procyon.org.uk> In-Reply-To: <160518586534.2277919.14475638653680231924.stgit@warthog.procyon.org.uk> References: <160518586534.2277919.14475638653680231924.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Add the self-testing data from rfc8009 to test AES + HMAC-SHA2. Signed-off-by: David Howells --- crypto/krb5/selftest_data.c | 116 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/crypto/krb5/selftest_data.c b/crypto/krb5/selftest_data.c index 9085723b730b..00c3b38c01d8 100644 --- a/crypto/krb5/selftest_data.c +++ b/crypto/krb5/selftest_data.c @@ -13,6 +13,20 @@ * Pseudo-random function tests. */ const struct krb5_prf_test krb5_prf_tests[] = { + /* rfc8009 Appendix A */ + { + .krb5 = &krb5_aes128_cts_hmac_sha256_128, + .name = "prf", + .key = "3705D96080C17728A0E800EAB6E0D23C", + .octet = "74657374", + .prf = "9D188616F63852FE86915BB840B4A886FF3E6BB0F819B49B893393D393854295", + }, { + .krb5 = &krb5_aes256_cts_hmac_sha384_192, + .name = "prf", + .key = "6D404D37FAF79F9DF0D33568D320669800EB4836472EA8A026D16B7182460C52", + .octet = "74657374", + .prf = "9801F69A368C2BF675E59521E177D9A07F67EFE1CFDE8D3C8D6F6A0256E3B17DB3C1B62AD1B8553360D17367EB1514D2", + }, {/* END */} }; @@ -20,6 +34,28 @@ const struct krb5_prf_test krb5_prf_tests[] = { * Key derivation tests. */ const struct krb5_key_test krb5_key_tests[] = { + /* rfc8009 Appendix A */ + { + .krb5 = &krb5_aes128_cts_hmac_sha256_128, + .name = "key", + .key = "3705D96080C17728A0E800EAB6E0D23C", + .Kc.use = 0x00000002, + .Kc.key = "B31A018A48F54776F403E9A396325DC3", + .Ke.use = 0x00000002, + .Ke.key = "9B197DD1E8C5609D6E67C3E37C62C72E", + .Ki.use = 0x00000002, + .Ki.key = "9FDA0E56AB2D85E1569A688696C26A6C", + }, { + .krb5 = &krb5_aes256_cts_hmac_sha384_192, + .name = "key", + .key = "6D404D37FAF79F9DF0D33568D320669800EB4836472EA8A026D16B7182460C52", + .Kc.use = 0x00000002, + .Kc.key = "EF5718BE86CC84963D8BBB5031E9F5C4BA41F28FAF69E73D", + .Ke.use = 0x00000002, + .Ke.key = "56AB22BEE63D82D7BC5227F6773F8EA7A5EB1C825160C38312980C442E5C7E49", + .Ki.use = 0x00000002, + .Ki.key = "69B16514E3CD8E56B82010D5C73012B622C4D00FFC23ED1F", + }, {/* END */} }; @@ -27,6 +63,72 @@ const struct krb5_key_test krb5_key_tests[] = { * Encryption tests. */ const struct krb5_enc_test krb5_enc_tests[] = { + /* rfc8009 Appendix A */ + { + .krb5 = &krb5_aes128_cts_hmac_sha256_128, + .name = "enc no plain", + .plain = "", + .conf = "7E5895EAF2672435BAD817F545A37148", + .Ke = "9B197DD1E8C5609D6E67C3E37C62C72E", + .Ki = "9FDA0E56AB2D85E1569A688696C26A6C", + .ct = "EF85FB890BB8472F4DAB20394DCA781DAD877EDA39D50C870C0D5A0A8E48C718", + }, { + .krb5 = &krb5_aes128_cts_hmac_sha256_128, + .name = "enc plain X-Patchwork-Id: 11900051 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH,MAILING_LIST_MULTI,PDS_BTC_ID,SIGNED_OFF_BY,SPF_HELO_NONE, SPF_PASS,UNWANTED_LANGUAGE_BODY,URIBL_BLOCKED autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 864F5C388F7 for ; Thu, 12 Nov 2020 12:59:46 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 1744321D7F for ; Thu, 12 Nov 2020 12:59:46 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="avafvUOY" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728470AbgKLM7h (ORCPT ); Thu, 12 Nov 2020 07:59:37 -0500 Received: from us-smtp-delivery-124.mimecast.com ([63.128.21.124]:39443 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728384AbgKLM7e (ORCPT ); Thu, 12 Nov 2020 07:59:34 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1605185971; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=eZU2g8g29AVG00nrqXhuF7WHQTv1UssjpwVpqGofl5Y=; b=avafvUOYRgUXpy+QStoM7SbxKud8XFCJ69t7IAifiQzqMQzLpZD7kaU/Ye70nTsrfg3Syq MoOsr8+35EaRAqPHmjGePKYjDIo4LjTh4Pafj4gKpzwen2/NvoD9qoLVE+PaedlVgCC2fI lPoSKysxlxuy3inwR2qbNZF20bc7pIU= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-104-UogHpdO7NX-KFKp1tTijVQ-1; Thu, 12 Nov 2020 07:59:27 -0500 X-MC-Unique: UogHpdO7NX-KFKp1tTijVQ-1 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 4772D1868405; Thu, 12 Nov 2020 12:59:26 +0000 (UTC) Received: from warthog.procyon.org.uk (ovpn-115-47.rdu2.redhat.com [10.10.115.47]) by smtp.corp.redhat.com (Postfix) with ESMTP id E559F5D9E4; Thu, 12 Nov 2020 12:59:23 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH 12/18] crypto/krb5: Implement the Camellia enctypes from rfc6803 From: David Howells To: herbert@gondor.apana.org.au, bfields@fieldses.org Cc: dhowells@redhat.com, trond.myklebust@hammerspace.com, linux-crypto@vger.kernel.org, linux-afs@lists.infradead.org, linux-cifs@vger.kernel.org, linux-nfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 12 Nov 2020 12:59:23 +0000 Message-ID: <160518596309.2277919.13684602434679285313.stgit@warthog.procyon.org.uk> In-Reply-To: <160518586534.2277919.14475638653680231924.stgit@warthog.procyon.org.uk> References: <160518586534.2277919.14475638653680231924.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Implement the camellia128-cts-cmac and camellia256-cts-cmac enctypes from rfc6803. Note that the test vectors in rfc6803 for encryption are incomplete, lacking the key usage number needed to derive Ke and Ki, and there are errata for this: https://www.rfc-editor.org/errata_search.php?rfc=6803 Signed-off-by: David Howells --- crypto/krb5/Kconfig | 3 crypto/krb5/Makefile | 3 crypto/krb5/internal.h | 6 + crypto/krb5/main.c | 2 crypto/krb5/rfc6803_camellia.c | 249 ++++++++++++++++++++++++++++++++++++++++ crypto/krb5/selftest_data.c | 135 ++++++++++++++++++++++ include/crypto/krb5.h | 4 + 7 files changed, 401 insertions(+), 1 deletion(-) create mode 100644 crypto/krb5/rfc6803_camellia.c diff --git a/crypto/krb5/Kconfig b/crypto/krb5/Kconfig index 5607c0c81049..6c0edf659c8f 100644 --- a/crypto/krb5/Kconfig +++ b/crypto/krb5/Kconfig @@ -3,12 +3,15 @@ config CRYPTO_KRB5 select CRYPTO_MANAGER select CRYPTO_SKCIPHER select CRYPTO_HASH_INFO + select CRYPTO_HMAC + select CRYPTO_CMAC select CRYPTO_SHA1 select CRYPTO_SHA256 select CRYPTO_SHA512 select CRYPTO_CBC select CRYPTO_CTS select CRYPTO_AES + select CRYPTO_CAMELLIA help Provide Kerberos-5-based security. diff --git a/crypto/krb5/Makefile b/crypto/krb5/Makefile index 85763131f7b6..974e0bcef91d 100644 --- a/crypto/krb5/Makefile +++ b/crypto/krb5/Makefile @@ -8,7 +8,8 @@ krb5-y += \ main.o \ rfc3961_simplified.o \ rfc3962_aes.o \ - rfc8009_aes2.o + rfc8009_aes2.o \ + rfc6803_camellia.o krb5-$(CONFIG_CRYPTO_KRB5_SELFTESTS) += \ selftest.o \ diff --git a/crypto/krb5/internal.h b/crypto/krb5/internal.h index e64f5e58199f..83662abc0765 100644 --- a/crypto/krb5/internal.h +++ b/crypto/krb5/internal.h @@ -158,6 +158,12 @@ int rfc3961_verify_mic(const struct krb5_enctype *krb5, extern const struct krb5_enctype krb5_aes128_cts_hmac_sha1_96; extern const struct krb5_enctype krb5_aes256_cts_hmac_sha1_96; +/* + * rfc6803_camellia.c + */ +extern const struct krb5_enctype krb5_camellia128_cts_cmac; +extern const struct krb5_enctype krb5_camellia256_cts_cmac; + /* * rfc8009_aes2.c */ diff --git a/crypto/krb5/main.c b/crypto/krb5/main.c index 9914d3417c21..b531eafb6db2 100644 --- a/crypto/krb5/main.c +++ b/crypto/krb5/main.c @@ -22,6 +22,8 @@ static const struct krb5_enctype *const krb5_supported_enctypes[] = { &krb5_aes256_cts_hmac_sha1_96, &krb5_aes128_cts_hmac_sha256_128, &krb5_aes256_cts_hmac_sha384_192, + &krb5_camellia128_cts_cmac, + &krb5_camellia256_cts_cmac, }; /** diff --git a/crypto/krb5/rfc6803_camellia.c b/crypto/krb5/rfc6803_camellia.c new file mode 100644 index 000000000000..adcb9c6481a3 --- /dev/null +++ b/crypto/krb5/rfc6803_camellia.c @@ -0,0 +1,249 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* rfc6803 Camellia Encryption for Kerberos 5 + * + * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include "internal.h" + +/* + * Calculate the key derivation function KDF-FEEDBACK_CMAC(key, constant) + * + * n = ceiling(k / 128) + * K(0) = zeros + * K(i) = CMAC(key, K(i-1) | i | constant | 0x00 | k) + * DR(key, constant) = k-truncate(K(1) | K(2) | ... | K(n)) + * KDF-FEEDBACK-CMAC(key, constant) = random-to-key(DR(key, constant)) + * + * [rfc6803 sec 3] + */ +static int rfc6803_calc_KDF_FEEDBACK_CMAC(const struct krb5_enctype *krb5, + const struct krb5_buffer *key, + const struct krb5_buffer *constant, + struct krb5_buffer *result, + gfp_t gfp) +{ + struct crypto_shash *shash; + struct krb5_buffer K, data; + struct shash_desc *desc; + __be32 tmp; + size_t bsize, offset, seg; + void *buffer; + u32 i = 0, k = result->len * 8; + u8 *p; + int ret = -ENOMEM; + + shash = crypto_alloc_shash(krb5->cksum_name, 0, 0); + if (IS_ERR(shash)) + return (PTR_ERR(shash) == -ENOENT) ? -ENOPKG : PTR_ERR(shash); + ret = crypto_shash_setkey(shash, key->data, key->len); + if (ret < 0) + goto error_shash; + + ret = -ENOMEM; + K.len = crypto_shash_digestsize(shash); + data.len = K.len + 4 + constant->len + 1 + 4; + bsize = krb5_shash_size(shash) + + krb5_digest_size(shash) + + crypto_roundup(K.len) + + crypto_roundup(data.len); + buffer = kzalloc(bsize, GFP_NOFS); + if (!buffer) + goto error_shash; + + desc = buffer; + desc->tfm = shash; + + K.data = buffer + + krb5_shash_size(shash) + + krb5_digest_size(shash); + data.data = buffer + + krb5_shash_size(shash) + + krb5_digest_size(shash) + + crypto_roundup(K.len); + + p = data.data + K.len + 4; + memcpy(p, constant->data, constant->len); + p += constant->len; + *p++ = 0x00; + tmp = htonl(k); + memcpy(p, &tmp, 4); + p += 4; + + ret = -EINVAL; + if (WARN_ON(p - (u8 *)data.data != data.len)) + goto error; + + offset = 0; + do { + i++; + p = data.data; + memcpy(p, K.data, K.len); + p += K.len; + *(__be32 *)p = htonl(i); + + ret = crypto_shash_init(desc); + if (ret < 0) + goto error; + ret = crypto_shash_finup(desc, data.data, data.len, K.data); + if (ret < 0) + goto error; + + seg = min_t(size_t, result->len - offset, K.len); + memcpy(result->data + offset, K.data, seg); + offset += seg; + } while (offset < result->len); + +error: + kfree_sensitive(buffer); +error_shash: + crypto_free_shash(shash); + return ret; +} + +/* + * Calculate the pseudo-random function, PRF(). + * + * Kp = KDF-FEEDBACK-CMAC(protocol-key, "prf") + * PRF = CMAC(Kp, octet-string) + * [rfc6803 sec 6] + */ +static int rfc6803_calc_PRF(const struct krb5_enctype *krb5, + const struct krb5_buffer *protocol_key, + const struct krb5_buffer *octet_string, + struct krb5_buffer *result, + gfp_t gfp) +{ + static const struct krb5_buffer prfconstant = { 3, "prf" }; + struct crypto_shash *shash; + struct krb5_buffer Kp; + struct shash_desc *desc; + size_t bsize; + void *buffer; + int ret; + + Kp.len = krb5->prf_len; + + shash = crypto_alloc_shash(krb5->cksum_name, 0, 0); + if (IS_ERR(shash)) + return (PTR_ERR(shash) == -ENOENT) ? -ENOPKG : PTR_ERR(shash); + + ret = -EINVAL; + if (result->len != crypto_shash_digestsize(shash)) + goto out_shash; + + ret = -ENOMEM; + bsize = krb5_shash_size(shash) + + krb5_digest_size(shash) + + crypto_roundup(Kp.len); + buffer = kzalloc(bsize, GFP_NOFS); + if (!buffer) + goto out_shash; + + Kp.data = buffer + + krb5_shash_size(shash) + + krb5_digest_size(shash); + + ret = rfc6803_calc_KDF_FEEDBACK_CMAC(krb5, protocol_key, &prfconstant, + &Kp, gfp); + if (ret < 0) + goto out; + + ret = crypto_shash_setkey(shash, Kp.data, Kp.len); + if (ret < 0) + goto out; + + desc = buffer; + desc->tfm = shash; + ret = crypto_shash_init(desc); + if (ret < 0) + goto out; + + ret = crypto_shash_finup(desc, octet_string->data, octet_string->len, result->data); + if (ret < 0) + goto out; + +out: + kfree_sensitive(buffer); +out_shash: + crypto_free_shash(shash); + return ret; +} + +/* + * Camellia random-to-key function. This is an identity operation. + */ +static int rfc6803_random_to_key(const struct krb5_enctype *krb5, + const struct krb5_buffer *randombits, + struct krb5_buffer *result) +{ + if (randombits->len != 16 && randombits->len != 32) + return -EINVAL; + + if (result->len != randombits->len) + return -EINVAL; + + memcpy(result->data, randombits->data, randombits->len); + return 0; +} + +static const struct krb5_crypto_profile rfc6803_crypto_profile = { + .calc_PRF = rfc6803_calc_PRF, + .calc_Kc = rfc6803_calc_KDF_FEEDBACK_CMAC, + .calc_Ke = rfc6803_calc_KDF_FEEDBACK_CMAC, + .calc_Ki = rfc6803_calc_KDF_FEEDBACK_CMAC, + .encrypt = rfc3961_encrypt, + .decrypt = rfc3961_decrypt, + .get_mic = rfc3961_get_mic, + .verify_mic = rfc3961_verify_mic, +}; + +const struct krb5_enctype krb5_camellia128_cts_cmac = { + .etype = KRB5_ENCTYPE_CAMELLIA128_CTS_CMAC, + .ctype = KRB5_CKSUMTYPE_CMAC_CAMELLIA128, + .name = "camellia128-cts-cmac", + .encrypt_name = "cts(cbc(camellia))", + .cksum_name = "cmac(camellia)", + .hash_name = NULL, + .key_bytes = 16, + .key_len = 16, + .Kc_len = 16, + .Ke_len = 16, + .Ki_len = 16, + .block_len = 16, + .conf_len = 16, + .cksum_len = 16, + .hash_len = 16, + .prf_len = 16, + .keyed_cksum = true, + .random_to_key = rfc6803_random_to_key, + .profile = &rfc6803_crypto_profile, +}; + +const struct krb5_enctype krb5_camellia256_cts_cmac = { + .etype = KRB5_ENCTYPE_CAMELLIA256_CTS_CMAC, + .ctype = KRB5_CKSUMTYPE_CMAC_CAMELLIA256, + .name = "camellia256-cts-cmac", + .encrypt_name = "cts(cbc(camellia))", + .cksum_name = "cmac(camellia)", + .hash_name = NULL, + .key_bytes = 32, + .key_len = 32, + .Kc_len = 32, + .Ke_len = 32, + .Ki_len = 32, + .block_len = 16, + .conf_len = 16, + .cksum_len = 16, + .hash_len = 16, + .prf_len = 16, + .keyed_cksum = true, + .random_to_key = rfc6803_random_to_key, + .profile = &rfc6803_crypto_profile, +}; diff --git a/crypto/krb5/selftest_data.c b/crypto/krb5/selftest_data.c index 00c3b38c01d8..f71023463b7f 100644 --- a/crypto/krb5/selftest_data.c +++ b/crypto/krb5/selftest_data.c @@ -56,6 +56,29 @@ const struct krb5_key_test krb5_key_tests[] = { .Ki.use = 0x00000002, .Ki.key = "69B16514E3CD8E56B82010D5C73012B622C4D00FFC23ED1F", }, + /* rfc6803 sec 10 */ + { + .krb5 = &krb5_camellia128_cts_cmac, + .name = "key", + .key = "57D0297298FFD9D35DE5A47FB4BDE24B", + .Kc.use = 0x00000002, + .Kc.key = "D155775A209D05F02B38D42A389E5A56", + .Ke.use = 0x00000002, + .Ke.key = "64DF83F85A532F17577D8C37035796AB", + .Ki.use = 0x00000002, + .Ki.key = "3E4FBDF30FB8259C425CB6C96F1F4635", + }, + { + .krb5 = &krb5_camellia256_cts_cmac, + .name = "key", + .key = "B9D6828B2056B7BE656D88A123B1FAC68214AC2B727ECF5F69AFE0C4DF2A6D2C", + .Kc.use = 0x00000002, + .Kc.key = "E467F9A9552BC7D3155A6220AF9C19220EEED4FF78B0D1E6A1544991461A9E50", + .Ke.use = 0x00000002, + .Ke.key = "412AEFC362A7285FC3966C6A5181E7605AE675235B6D549FBFC9AB6630A4C604", + .Ki.use = 0x00000002, + .Ki.key = "FA624FA0E523993FA388AEFDC67E67EBCD8C08E8A0246B1D73B0D1DD9FC582B0", + }, {/* END */} }; @@ -129,6 +152,88 @@ const struct krb5_enc_test krb5_enc_tests[] = { .Ki = "69B16514E3CD8E56B82010D5C73012B622C4D00FFC23ED1F", .ct = "40013E2DF58E8751957D2878BCD2D6FE101CCFD556CB1EAE79DB3C3EE86429F2B2A602AC86FEF6ECB647D6295FAE077A1FEB517508D2C16B4192E01F62", }, + /* rfc6803 sec 10 */ + { + .krb5 = &krb5_camellia128_cts_cmac, + .name = "enc no plain", + .plain = "", + .conf = "B69822A19A6B09C0EBC8557D1F1B6C0A", + .K0 = "1DC46A8D763F4F93742BCBA3387576C3", + .usage = htonl(0), + .ct = "C466F1871069921EDB7C6FDE244A52DB0BA10EDC197BDB8006658CA3CCCE6EB8", + }, { + .krb5 = &krb5_camellia128_cts_cmac, + .name = "enc 1 plain", + .plain = "'1", + .conf = "6F2FC3C2A166FD8898967A83DE9596D9", + .K0 = "5027BC231D0F3A9D23333F1CA6FDBE7C", + .usage = htonl(1), + .ct = "842D21FD950311C0DD464A3F4BE8D6DA88A56D559C9B47D3F9A85067AF661559B8", + }, { + .krb5 = &krb5_camellia128_cts_cmac, + .name = "enc 9 plain", + .plain = "'9 bytesss", + .conf = "A5B4A71E077AEEF93C8763C18FDB1F10", + .K0 = "A1BB61E805F9BA6DDE8FDBDDC05CDEA0", + .usage = htonl(2), + .ct = "619FF072E36286FF0A28DEB3A352EC0D0EDF5C5160D663C901758CCF9D1ED33D71DB8F23AABF8348A0", + }, { + .krb5 = &krb5_camellia128_cts_cmac, + .name = "enc 13 plain", + .plain = "'13 bytes byte", + .conf = "19FEE40D810C524B5B22F01874C693DA", + .K0 = "2CA27A5FAF5532244506434E1CEF6676", + .usage = htonl(3), + .ct = "B8ECA3167AE6315512E59F98A7C500205E5F63FF3BB389AF1C41A21D640D8615C9ED3FBEB05AB6ACB67689B5EA", + }, { + .krb5 = &krb5_camellia128_cts_cmac, + .name = "enc 30 plain", + .plain = "'30 bytes bytes bytes bytes byt", + .conf = "CA7A7AB4BE192DABD603506DB19C39E2", + .K0 = "7824F8C16F83FF354C6BF7515B973F43", + .usage = htonl(4), + .ct = "A26A3905A4FFD5816B7B1E27380D08090C8EC1F304496E1ABDCD2BDCD1DFFC660989E117A713DDBB57A4146C1587CBA4356665591D2240282F5842B105A5", + }, { + .krb5 = &krb5_camellia256_cts_cmac, + .name = "enc no plain", + .plain = "", + .conf = "3CBBD2B45917941067F96599BB98926C", + .K0 = "B61C86CC4E5D2757545AD423399FB7031ECAB913CBB900BD7A3C6DD8BF92015B", + .usage = htonl(0), + .ct = "03886D03310B47A6D8F06D7B94D1DD837ECCE315EF652AFF620859D94A259266", + }, { + .krb5 = &krb5_camellia256_cts_cmac, + .name = "enc 1 plain", + .plain = "'1", + .conf = "DEF487FCEBE6DE6346D4DA4521BBA2D2", + .K0 = "1B97FE0A190E2021EB30753E1B6E1E77B0754B1D684610355864104963463833", + .usage = htonl(1), + .ct = "2C9C1570133C99BF6A34BC1B0212002FD194338749DB4135497A347CFCD9D18A12", + }, { + .krb5 = &krb5_camellia256_cts_cmac, + .name = "enc 9 plain", + .plain = "'9 bytesss", + .conf = "AD4FF904D34E555384B14100FC465F88", + .K0 = "32164C5B434D1D1538E4CFD9BE8040FE8C4AC7ACC4B93D3314D2133668147A05", + .usage = htonl(2), + .ct = "9C6DE75F812DE7ED0D28B2963557A115640998275B0AF5152709913FF52A2A9C8E63B872F92E64C839", + }, { + .krb5 = &krb5_camellia256_cts_cmac, + .name = "enc 13 plain", + .plain = "'13 bytes byte", + .conf = "CF9BCA6DF1144E0C0AF9B8F34C90D514", + .K0 = "B038B132CD8E06612267FAB7170066D88AECCBA0B744BFC60DC89BCA182D0715", + .usage = htonl(3), + .ct = "EEEC85A9813CDC536772AB9B42DEFC5706F726E975DDE05A87EB5406EA324CA185C9986B42AABE794B84821BEE", + }, { + .krb5 = &krb5_camellia256_cts_cmac, + .name = "enc 30 plain", + .plain = "'30 bytes bytes bytes bytes byt", + .conf = "644DEF38DA35007275878D216855E228", + .K0 = "CCFCD349BF4C6677E86E4B02B8EAB924A546AC731CF9BF6989B996E7D6BFBBA7", + .usage = htonl(4), + .ct = "0E44680985855F2D1F1812529CA83BFD8E349DE6FD9ADA0BAAA048D68E265FEBF34AD1255A344999AD37146887A6C6845731AC7F46376A0504CD06571474", + }, {/* END */} }; @@ -150,5 +255,35 @@ const struct krb5_mic_test krb5_mic_tests[] = { .Kc = "EF5718BE86CC84963D8BBB5031E9F5C4BA41F28FAF69E73D", .mic = "45EE791567EEFCA37F4AC1E0222DE80D43C3BFA06699672A", }, + /* rfc6803 sec 10 */ + { + .krb5 = &krb5_camellia128_cts_cmac, + .name = "mic abc", + .plain = "'abcdefghijk", + .K0 = "1DC46A8D763F4F93742BCBA3387576C3", + .usage = htonl(7), + .mic = "1178E6C5C47A8C1AE0C4B9C7D4EB7B6B", + }, { + .krb5 = &krb5_camellia128_cts_cmac, + .name = "mic ABC", + .plain = "'ABCDEFGHIJKLMNOPQRSTUVWXYZ", + .K0 = "5027BC231D0F3A9D23333F1CA6FDBE7C", + .usage = htonl(8), + .mic = "D1B34F7004A731F23A0C00BF6C3F753A", + }, { + .krb5 = &krb5_camellia256_cts_cmac, + .name = "mic 123", + .plain = "'123456789", + .K0 = "B61C86CC4E5D2757545AD423399FB7031ECAB913CBB900BD7A3C6DD8BF92015B", + .usage = htonl(9), + .mic = "87A12CFD2B96214810F01C826E7744B1", + }, { + .krb5 = &krb5_camellia256_cts_cmac, + .name = "mic !@#", + .plain = "'!@#$%^&*()!@#$%^&*()!@#$%^&*()", + .K0 = "32164C5B434D1D1538E4CFD9BE8040FE8C4AC7ACC4B93D3314D2133668147A05", + .usage = htonl(10), + .mic = "3FA0B42355E52B189187294AA252AB64", + }, {/* END */} }; diff --git a/include/crypto/krb5.h b/include/crypto/krb5.h index f38a5b4d97ee..0b811fe27fec 100644 --- a/include/crypto/krb5.h +++ b/include/crypto/krb5.h @@ -36,6 +36,8 @@ struct scatterlist; #define KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192 0x0014 #define KRB5_ENCTYPE_ARCFOUR_HMAC 0x0017 #define KRB5_ENCTYPE_ARCFOUR_HMAC_EXP 0x0018 +#define KRB5_ENCTYPE_CAMELLIA128_CTS_CMAC 0x0019 +#define KRB5_ENCTYPE_CAMELLIA256_CTS_CMAC 0x001a #define KRB5_ENCTYPE_UNKNOWN 0x01ff #define KRB5_CKSUMTYPE_CRC32 0x0001 @@ -48,6 +50,8 @@ struct scatterlist; #define KRB5_CKSUMTYPE_HMAC_SHA1_DES3 0x000c #define KRB5_CKSUMTYPE_HMAC_SHA1_96_AES128 0x000f #define KRB5_CKSUMTYPE_HMAC_SHA1_96_AES256 0x0010 +#define KRB5_CKSUMTYPE_CMAC_CAMELLIA128 0x0011 +#define KRB5_CKSUMTYPE_CMAC_CAMELLIA256 0x0012 #define KRB5_CKSUMTYPE_HMAC_SHA256_128_AES128 0x0013 #define KRB5_CKSUMTYPE_HMAC_SHA384_192_AES256 0x0014 #define KRB5_CKSUMTYPE_HMAC_MD5_ARCFOUR -138 /* Microsoft md5 hmac cksumtype */ From patchwork Thu Nov 12 12:59:31 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Howells X-Patchwork-Id: 11900049 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.9 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 25EB1C56201 for ; Thu, 12 Nov 2020 12:59:52 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id BF380221E9 for ; Thu, 12 Nov 2020 12:59:51 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="Dmi6lLep" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728482AbgKLM7u (ORCPT ); Thu, 12 Nov 2020 07:59:50 -0500 Received: from us-smtp-delivery-124.mimecast.com ([63.128.21.124]:23610 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728474AbgKLM7j (ORCPT ); Thu, 12 Nov 2020 07:59:39 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1605185977; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=U+hogVrnEUXACHXVMILSDuAA70MeRvT/x4AeZ6HZaac=; b=Dmi6lLept+gGoUuohs5AajrELX7kSgTNQghT3EoNeWWGqpvoievll8mJRH8D1oqMNEmQr8 PuUw77+LDKy8XOkPhnuWnGYrZL8F/3j3SNfQt6VQcJoo8Jfp9mkeJwmwkZX3nRUwGdzHrr RZmaY530e7aEWswRV6X/soBtIBHpq4I= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-490-lo3DCCjIN7ixg4PNub7btA-1; Thu, 12 Nov 2020 07:59:35 -0500 X-MC-Unique: lo3DCCjIN7ixg4PNub7btA-1 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 227571017DC3; Thu, 12 Nov 2020 12:59:34 +0000 (UTC) Received: from warthog.procyon.org.uk (ovpn-115-47.rdu2.redhat.com [10.10.115.47]) by smtp.corp.redhat.com (Postfix) with ESMTP id 4284110013BD; Thu, 12 Nov 2020 12:59:32 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH 13/18] rxrpc: Add the security index for yfs-rxgk From: David Howells To: herbert@gondor.apana.org.au, bfields@fieldses.org Cc: dhowells@redhat.com, trond.myklebust@hammerspace.com, linux-crypto@vger.kernel.org, linux-afs@lists.infradead.org, linux-cifs@vger.kernel.org, linux-nfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 12 Nov 2020 12:59:31 +0000 Message-ID: <160518597145.2277919.16270470808861282038.stgit@warthog.procyon.org.uk> In-Reply-To: <160518586534.2277919.14475638653680231924.stgit@warthog.procyon.org.uk> References: <160518586534.2277919.14475638653680231924.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.22 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Add the security index for the YFS variant of rxgk. Signed-off-by: David Howells --- fs/afs/misc.c | 13 +++++++++++++ include/uapi/linux/rxrpc.h | 17 +++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/fs/afs/misc.c b/fs/afs/misc.c index 1d1a8debe472..796783774e36 100644 --- a/fs/afs/misc.c +++ b/fs/afs/misc.c @@ -102,6 +102,19 @@ int afs_abort_to_error(u32 abort_code) case RXKADDATALEN: return -EKEYREJECTED; case RXKADILLEGALLEVEL: return -EKEYREJECTED; + case RXGK_INCONSISTENCY: return -EPROTO; + case RXGK_PACKETSHORT: return -EPROTO; + case RXGK_BADCHALLENGE: return -EPROTO; + case RXGK_BADETYPE: return -ENOPKG; + case RXGK_BADLEVEL: return -EKEYREJECTED; + case RXGK_BADKEYNO: return -EKEYREJECTED; + case RXGK_EXPIRED: return -EKEYEXPIRED; + case RXGK_NOTAUTH: return -EKEYREJECTED; + case RXGK_BAD_TOKEN: return -EKEYREJECTED; + case RXGK_SEALED_INCON: return -EKEYREJECTED; + case RXGK_DATA_LEN: return -EPROTO; + case RXGK_BAD_QOP: return -EKEYREJECTED; + case RXGEN_OPCODE: return -ENOTSUPP; default: return -EREMOTEIO; diff --git a/include/uapi/linux/rxrpc.h b/include/uapi/linux/rxrpc.h index 8f8dc7a937a4..0e296d219191 100644 --- a/include/uapi/linux/rxrpc.h +++ b/include/uapi/linux/rxrpc.h @@ -73,6 +73,7 @@ enum rxrpc_cmsg_type { #define RXRPC_SECURITY_RXKAD 2 /* kaserver or kerberos 4 */ #define RXRPC_SECURITY_RXGK 4 /* gssapi-based */ #define RXRPC_SECURITY_RXK5 5 /* kerberos 5 */ +#define RXRPC_SECURITY_YFS_RXGK 6 /* YFS gssapi-based */ /* * RxRPC-level abort codes @@ -118,4 +119,20 @@ enum rxrpc_cmsg_type { #define RXKADDATALEN 19270411 /* user data too long */ #define RXKADILLEGALLEVEL 19270412 /* caller not authorised to use encrypted conns */ +/* + * RxGK GSSAPI security abort codes. + */ +#define RXGK_INCONSISTENCY 1233242880 /* Security module structure inconsistent */ +#define RXGK_PACKETSHORT 1233242881 /* Packet too short for security challenge */ +#define RXGK_BADCHALLENGE 1233242882 /* Invalid security challenge */ +#define RXGK_BADETYPE 1233242883 /* Invalid or impermissible encryption type */ +#define RXGK_BADLEVEL 1233242884 /* Invalid or impermissible security level */ +#define RXGK_BADKEYNO 1233242885 /* Key version number not found */ +#define RXGK_EXPIRED 1233242886 /* Token has expired */ +#define RXGK_NOTAUTH 1233242887 /* Caller not authorized */ +#define RXGK_BAD_TOKEN 1233242888 /* Security object was passed a bad token */ +#define RXGK_SEALED_INCON 1233242889 /* Sealed data inconsistent */ +#define RXGK_DATA_LEN 1233242890 /* User data too long */ +#define RXGK_BAD_QOP 1233242891 /* Inadequate quality of protection available */ + #endif /* _UAPI_LINUX_RXRPC_H */ From patchwork Thu Nov 12 12:59:39 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Howells X-Patchwork-Id: 11900047 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.9 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 61F2FC56202 for ; Thu, 12 Nov 2020 12:59:53 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id ECA5921D91 for ; Thu, 12 Nov 2020 12:59:52 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="BsX1zwym" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728504AbgKLM7w (ORCPT ); Thu, 12 Nov 2020 07:59:52 -0500 Received: from us-smtp-delivery-124.mimecast.com ([216.205.24.124]:28257 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727739AbgKLM7r (ORCPT ); Thu, 12 Nov 2020 07:59:47 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1605185986; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=B5WvSEa2+oXzMRb5vYLYlFmIu/7LQYDhHpYTqPt6M5g=; b=BsX1zwymlCObMSf3zmL9A4lgaskQ6eMWC6JphvoYvX8+1VR8m4cDy4Upapmjolhg3LTvIo 9zE2Czw8jQ+eTl1XNzT0hzfiQpLhPp4sXROuHOc1rtNH9DPZApKGCR1tMkkEKlO0ogh2Ys 8ceqmM/OZ5ZH0YgdTbrCDAF244O6il8= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-567-vcD4p_K8Pfaf_iXTg1N6nA-1; Thu, 12 Nov 2020 07:59:44 -0500 X-MC-Unique: vcD4p_K8Pfaf_iXTg1N6nA-1 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 52A72804753; Thu, 12 Nov 2020 12:59:42 +0000 (UTC) Received: from warthog.procyon.org.uk (ovpn-115-47.rdu2.redhat.com [10.10.115.47]) by smtp.corp.redhat.com (Postfix) with ESMTP id 2FBFF5D9E4; Thu, 12 Nov 2020 12:59:40 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH 14/18] rxrpc: Add YFS RxGK (GSSAPI) security class From: David Howells To: herbert@gondor.apana.org.au, bfields@fieldses.org Cc: dhowells@redhat.com, trond.myklebust@hammerspace.com, linux-crypto@vger.kernel.org, linux-afs@lists.infradead.org, linux-cifs@vger.kernel.org, linux-nfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 12 Nov 2020 12:59:39 +0000 Message-ID: <160518597935.2277919.239077296822909986.stgit@warthog.procyon.org.uk> In-Reply-To: <160518586534.2277919.14475638653680231924.stgit@warthog.procyon.org.uk> References: <160518586534.2277919.14475638653680231924.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org Add support for the YFS-variant RxGK security class to support GSSAPI-derived authentication. This also allows the use of better crypto over the rxkad security class. The key payload is XDR encoded of the form: typedef int64_t opr_time; const AFSTOKEN_RK_TIX_MAX = 12000; /* Matches entry in rxkad.h */ struct token_rxkad { afs_int32 viceid; afs_int32 kvno; afs_int64 key; afs_int32 begintime; afs_int32 endtime; afs_int32 primary_flag; opaque ticket; }; struct token_rxgk { opr_time begintime; opr_time endtime; afs_int64 level; afs_int64 lifetime; afs_int64 bytelife; afs_int64 enctype; opaque key<>; opaque ticket<>; }; const AFSTOKEN_UNION_NOAUTH = 0; const AFSTOKEN_UNION_KAD = 2; const AFSTOKEN_UNION_YFSGK = 6; union ktc_tokenUnion switch (afs_int32 type) { case AFSTOKEN_UNION_KAD: token_rxkad kad; case AFSTOKEN_UNION_YFSGK: token_rxgk gk; }; const AFSTOKEN_LENGTH_MAX = 16384; typedef opaque token_opaque; const AFSTOKEN_MAX = 8; const AFSTOKEN_CELL_MAX = 64; struct ktc_setTokenData { afs_int32 flags; string cell; token_opaque tokens; }; The parser for the basic token struct is already present, as is the rxkad token type. This adds a parser for the rxgk token type. Signed-off-by: David Howells --- include/keys/rxrpc-type.h | 17 ++++ net/rxrpc/key.c | 183 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 200 insertions(+) diff --git a/include/keys/rxrpc-type.h b/include/keys/rxrpc-type.h index 333c0f49a9cd..0ddbe197a261 100644 --- a/include/keys/rxrpc-type.h +++ b/include/keys/rxrpc-type.h @@ -9,6 +9,7 @@ #define _KEYS_RXRPC_TYPE_H #include +#include /* * key type for AF_RXRPC keys @@ -31,6 +32,21 @@ struct rxkad_key { u8 ticket[]; /* the encrypted ticket */ }; +/* + * RxRPC key for YFS-RxGK (type-6 security) + */ +struct rxgk_key { + s64 begintime; /* Time at which the ticket starts */ + s64 endtime; /* Time at which the ticket ends */ + u64 lifetime; /* Maximum lifespan of a connection (seconds) */ + u64 bytelife; /* Maximum number of bytes on a connection */ + unsigned int enctype; /* Encoding type */ + s8 level; /* Negotiated security RXRPC_SECURITY_PLAIN/AUTH/ENCRYPT */ + struct krb5_buffer key; /* Master key, K0 */ + struct krb5_buffer ticket; /* Ticket to be passed to server */ + u8 _key[]; /* Key storage */ +}; + /* * list of tokens attached to an rxrpc key */ @@ -40,6 +56,7 @@ struct rxrpc_key_token { struct rxrpc_key_token *next; /* the next token in the list */ union { struct rxkad_key *kad; + struct rxgk_key *rxgk; }; }; diff --git a/net/rxrpc/key.c b/net/rxrpc/key.c index 9631aa8543b5..b7f154701d97 100644 --- a/net/rxrpc/key.c +++ b/net/rxrpc/key.c @@ -129,6 +129,158 @@ static int rxrpc_preparse_xdr_rxkad(struct key_preparsed_payload *prep, return 0; } +static u64 xdr_dec64(const __be32 *xdr) +{ + return (u64)ntohl(xdr[0]) << 32 | (u64)ntohl(xdr[1]); +} + +static time64_t rxrpc_s64_to_time64(s64 time_in_100ns) +{ + bool neg = false; + u64 tmp = time_in_100ns; + + if (time_in_100ns < 0) { + tmp = -time_in_100ns; + neg = true; + } + do_div(tmp, 10000000); + return neg ? -tmp : tmp; +} + +/* + * Parse a YFS-RxGK type XDR format token + * - the caller guarantees we have at least 4 words + * + * struct token_rxgk { + * opr_time begintime; + * opr_time endtime; + * afs_int64 level; + * afs_int64 lifetime; + * afs_int64 bytelife; + * afs_int64 enctype; + * opaque key<>; + * opaque ticket<>; + * }; + */ +static int rxrpc_preparse_xdr_yfs_rxgk(struct key_preparsed_payload *prep, + size_t datalen, + const __be32 *xdr, unsigned int toklen) +{ + struct rxrpc_key_token *token, **pptoken; + time64_t expiry; + size_t plen; + const __be32 *ticket, *key; + s64 tmp; + u32 tktlen, keylen; + + _enter(",{%x,%x,%x,%x},%x", + ntohl(xdr[0]), ntohl(xdr[1]), ntohl(xdr[2]), ntohl(xdr[3]), + toklen); + + if (6 * 2 + 2 > toklen / 4) + goto reject; + + key = xdr + (6 * 2 + 1); + keylen = ntohl(key[-1]); + _debug("keylen: %x", keylen); + keylen = round_up(keylen, 4); + if ((6 * 2 + 2) * 4 + keylen > toklen) + goto reject; + + ticket = xdr + (6 * 2 + 1 + (keylen / 4) + 1); + tktlen = ntohl(ticket[-1]); + _debug("tktlen: %x", tktlen); + tktlen = round_up(tktlen, 4); + if ((6 * 2 + 2) * 4 + keylen + tktlen != toklen) { + kleave(" = -EKEYREJECTED [%x!=%x, %x,%x]", + (6 * 2 + 2) * 4 + keylen + tktlen, toklen, + keylen, tktlen); + goto reject; + } + + plen = sizeof(*token) + sizeof(*token->rxgk) + tktlen + keylen; + prep->quotalen = datalen + plen; + + plen -= sizeof(*token); + token = kzalloc(sizeof(*token), GFP_KERNEL); + if (!token) + goto nomem; + + token->rxgk = kzalloc(sizeof(struct rxgk_key) + keylen, GFP_KERNEL); + if (!token->rxgk) + goto nomem_token; + + token->security_index = RXRPC_SECURITY_YFS_RXGK; + token->rxgk->begintime = xdr_dec64(xdr + 0 * 2); + token->rxgk->endtime = xdr_dec64(xdr + 1 * 2); + token->rxgk->level = tmp = xdr_dec64(xdr + 2 * 2); + if (tmp < -1LL || tmp > RXRPC_SECURITY_ENCRYPT) + goto reject_token; + token->rxgk->lifetime = xdr_dec64(xdr + 3 * 2); + token->rxgk->bytelife = xdr_dec64(xdr + 4 * 2); + token->rxgk->enctype = tmp = xdr_dec64(xdr + 5 * 2); + if (tmp < 0 || tmp > UINT_MAX) + goto reject_token; + token->rxgk->key.len = ntohl(key[-1]); + token->rxgk->key.data = token->rxgk->_key; + token->rxgk->ticket.len = ntohl(ticket[-1]); + + expiry = rxrpc_s64_to_time64(token->rxgk->endtime); + if (expiry < 0) + goto expired; + if (expiry < prep->expiry) + prep->expiry = expiry; + + memcpy(token->rxgk->key.data, key, token->rxgk->key.len); + + /* Pad the ticket so that we can use it directly in XDR */ + token->rxgk->ticket.data = kzalloc(round_up(token->rxgk->ticket.len, 4), + GFP_KERNEL); + if (!token->rxgk->ticket.data) + goto nomem_yrxgk; + memcpy(token->rxgk->ticket.data, ticket, token->rxgk->ticket.len); + + _debug("SCIX: %u", token->security_index); + _debug("EXPY: %llx", token->rxgk->endtime); + _debug("LIFE: %llx", token->rxgk->lifetime); + _debug("BYTE: %llx", token->rxgk->bytelife); + _debug("ENC : %u", token->rxgk->enctype); + _debug("LEVL: %u", token->rxgk->level); + _debug("KLEN: %u", token->rxgk->key.len); + _debug("TLEN: %u", token->rxgk->ticket.len); + _debug("KEY0: %*phN", token->rxgk->key.len, token->rxgk->key.data); + _debug("TICK: %*phN", + min_t(u32, token->rxgk->ticket.len, 32), token->rxgk->ticket.data); + + /* count the number of tokens attached */ + prep->payload.data[1] = (void *)((unsigned long)prep->payload.data[1] + 1); + + /* attach the data */ + for (pptoken = (struct rxrpc_key_token **)&prep->payload.data[0]; + *pptoken; + pptoken = &(*pptoken)->next) + continue; + *pptoken = token; + + _leave(" = 0"); + return 0; + +nomem_yrxgk: + kfree(token->rxgk); +nomem_token: + kfree(token); +nomem: + return -ENOMEM; +reject_token: + kfree(token); +reject: + return -EKEYREJECTED; +expired: + kfree(token->rxgk); + kfree(token); + return -EKEYEXPIRED; +} + /* * attempt to parse the data as the XDR format * - the caller guarantees we have more than 7 words @@ -228,6 +380,9 @@ static int rxrpc_preparse_xdr(struct key_preparsed_payload *prep) case RXRPC_SECURITY_RXKAD: ret2 = rxrpc_preparse_xdr_rxkad(prep, datalen, token, toklen); break; + case RXRPC_SECURITY_YFS_RXGK: + ret2 = rxrpc_preparse_xdr_yfs_rxgk(prep, datalen, token, toklen); + break; default: ret2 = -EPROTONOSUPPORT; break; @@ -390,6 +545,10 @@ static void rxrpc_free_token_list(struct rxrpc_key_token *token) case RXRPC_SECURITY_RXKAD: kfree(token->kad); break; + case RXRPC_SECURITY_YFS_RXGK: + kfree(token->rxgk->ticket.data); + kfree(token->rxgk); + break; default: pr_err("Unknown token type %x on rxrpc key\n", token->security_index); @@ -433,6 +592,9 @@ static void rxrpc_describe(const struct key *key, struct seq_file *m) case RXRPC_SECURITY_RXKAD: seq_puts(m, "ka"); break; + case RXRPC_SECURITY_YFS_RXGK: + seq_puts(m, "ygk"); + break; default: /* we have a ticket we can't encode */ seq_printf(m, "%u", token->security_index); break; @@ -595,6 +757,13 @@ static long rxrpc_read(const struct key *key, toksize += RND(token->kad->ticket_len); break; + case RXRPC_SECURITY_YFS_RXGK: + toksize += 6 * 8 + 2 * 4; + if (!token->no_leak_key) + toksize += RND(token->rxgk->key.len); + toksize += RND(token->rxgk->ticket.len); + break; + default: /* we have a ticket we can't encode */ pr_err("Unsupported key token type (%u)\n", token->security_index); @@ -673,6 +842,20 @@ static long rxrpc_read(const struct key *key, ENCODE_DATA(token->kad->ticket_len, token->kad->ticket); break; + case RXRPC_SECURITY_YFS_RXGK: + ENCODE64(token->rxgk->begintime); + ENCODE64(token->rxgk->endtime); + ENCODE64(token->rxgk->level); + ENCODE64(token->rxgk->lifetime); + ENCODE64(token->rxgk->bytelife); + ENCODE64(token->rxgk->enctype); + ENCODE_DATA(token->rxgk->ticket.len, token->rxgk->ticket.data); + if (token->no_leak_key) + ENCODE(0); + else + ENCODE_DATA(token->rxgk->key.len, token->rxgk->key.data); + break; + default: break; } From patchwork Thu Nov 12 12:59:47 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Howells X-Patchwork-Id: 11900045 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.8 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS, URIBL_BLOCKED autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8B8DCC2D0A3 for ; Thu, 12 Nov 2020 13:00:18 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 137E622253 for ; Thu, 12 Nov 2020 13:00:18 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="LeOVMiwo" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728521AbgKLNAP (ORCPT ); Thu, 12 Nov 2020 08:00:15 -0500 Received: from us-smtp-delivery-124.mimecast.com ([216.205.24.124]:28240 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728511AbgKLNAB (ORCPT ); Thu, 12 Nov 2020 08:00:01 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1605185998; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=9hW4vk0aUFoz9XSByNK9zQsdYKTcMJtnyt/J9ELSBT8=; b=LeOVMiwoIVf5vfNgR6SqmPTN7eHZeBmFumroTDCyUOq92aPYIZM+ad/lNoy7j4Dq1f4HC8 NJvn56k6PBNuf2owHOnLPjSR2ggmWdI5w2VoSi6e308SxX0jsEifJ4f8ImTcTJJiJttWku xzFLP58dP6LOv+A5gd4onL2jvpb/LRg= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-109-65z7sS4zOAebOuYLBdY60w-1; Thu, 12 Nov 2020 07:59:52 -0500 X-MC-Unique: 65z7sS4zOAebOuYLBdY60w-1 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id B36801017DC3; Thu, 12 Nov 2020 12:59:50 +0000 (UTC) Received: from warthog.procyon.org.uk (ovpn-115-47.rdu2.redhat.com [10.10.115.47]) by smtp.corp.redhat.com (Postfix) with ESMTP id 64E315D9E8; Thu, 12 Nov 2020 12:59:48 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH 15/18] rxrpc: rxgk: Provide infrastructure and key derivation From: David Howells To: herbert@gondor.apana.org.au, bfields@fieldses.org Cc: dhowells@redhat.com, trond.myklebust@hammerspace.com, linux-crypto@vger.kernel.org, linux-afs@lists.infradead.org, linux-cifs@vger.kernel.org, linux-nfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 12 Nov 2020 12:59:47 +0000 Message-ID: <160518598756.2277919.7065060398620916059.stgit@warthog.procyon.org.uk> In-Reply-To: <160518586534.2277919.14475638653680231924.stgit@warthog.procyon.org.uk> References: <160518586534.2277919.14475638653680231924.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org Provide some infrastructure for implementing the RxGK transport security class: (1) A definition of an encoding type, including: - Relevant crypto-layer names - Lengths of the crypto keys and checksums involved - Crypto functions specific to the encoding type - Crypto scheme used for that type (2) A definition of a crypto scheme, including: - Underlying crypto handlers - The pseudo-random function, PRF, used in base key derivation - Functions for deriving usage keys Kc, Ke and Ki - Functions for en/decrypting parts of an sk_buff (3) A key context, with the usage keys required for a derivative of a transport key for a specific key number. This includes keys for securing packets for transmission, extracting received packets and dealing with response packets. (3) A function to look up an encoding type by number. (4) A function to set up a key context and derive the keys. (5) A function to set up the keys required to extract the ticket obtained from the GSS negotiation in the server. (6) Miscellaneous functions for context handling. The keys and key derivation functions are described in: tools.ietf.org/html/draft-wilkinson-afs3-rxgk-11 Signed-off-by: David Howells --- net/rxrpc/Kconfig | 10 ++ net/rxrpc/Makefile | 3 + net/rxrpc/ar-internal.h | 3 + net/rxrpc/rxgk_common.h | 44 ++++++++ net/rxrpc/rxgk_kdf.c | 271 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 331 insertions(+) create mode 100644 net/rxrpc/rxgk_common.h create mode 100644 net/rxrpc/rxgk_kdf.c diff --git a/net/rxrpc/Kconfig b/net/rxrpc/Kconfig index d706bb408365..62ff4b373d03 100644 --- a/net/rxrpc/Kconfig +++ b/net/rxrpc/Kconfig @@ -57,3 +57,13 @@ config RXKAD through the use of the key retention service. See Documentation/networking/rxrpc.rst. + +config RXGK + bool "RxRPC GSSAPI security" + depends on AF_RXRPC + depends on CRYPTO_KRB5 + help + Provide the GSSAPI-based RxGK security class for AFS. Keys are added + with add_key(). + + See Documentation/networking/rxrpc.rst. diff --git a/net/rxrpc/Makefile b/net/rxrpc/Makefile index b11281bed2a4..08636858e77f 100644 --- a/net/rxrpc/Makefile +++ b/net/rxrpc/Makefile @@ -35,3 +35,6 @@ rxrpc-y := \ rxrpc-$(CONFIG_PROC_FS) += proc.o rxrpc-$(CONFIG_RXKAD) += rxkad.o rxrpc-$(CONFIG_SYSCTL) += sysctl.o + +rxrpc-$(CONFIG_RXGK) += \ + rxgk_kdf.o diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index a3091a10b7c5..4e0766b4a714 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -456,6 +456,9 @@ struct rxrpc_connection { struct rxrpc_crypt csum_iv; /* packet checksum base */ u32 nonce; /* response re-use preventer */ } rxkad; + struct { + u64 start_time; /* The start time for TK derivation */ + } rxgk; }; unsigned long flags; unsigned long events; diff --git a/net/rxrpc/rxgk_common.h b/net/rxrpc/rxgk_common.h new file mode 100644 index 000000000000..3047ad531877 --- /dev/null +++ b/net/rxrpc/rxgk_common.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* rxgk common bits + * + * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#include +#include +#include + +/* + * Per-key number context. This is replaced when the connection is rekeyed. + */ +struct rxgk_context { + refcount_t usage; + unsigned int key_number; /* Rekeying number (goes in the rx header) */ + unsigned long flags; +#define RXGK_TK_NEEDS_REKEY 0 /* Set if this needs rekeying */ + unsigned long expiry; /* Expiration time of this key */ + long long bytes_remaining; /* Remaining Tx lifetime of this key */ + const struct krb5_enctype *krb5; /* RxGK encryption type */ + const struct rxgk_key *key; + + /* We need up to 7 keys derived from the transport key, but we don't + * actually need the transport key. Each key is derived by + * DK(TK,constant). + */ + struct krb5_enc_keys tx_enc; /* Transmission key */ + struct krb5_enc_keys rx_enc; /* Reception key */ + struct crypto_shash *tx_Kc; /* Transmission checksum key */ + struct crypto_shash *rx_Kc; /* Reception checksum key */ + struct krb5_enc_keys resp_enc; /* Response packet enc key */ +}; + +/* + * rxgk_kdf.c + */ +struct rxgk_context *rxgk_generate_transport_key(struct rxrpc_connection *, + const struct rxgk_key *, unsigned int, gfp_t); +int rxgk_set_up_token_cipher(const struct krb5_buffer *, struct krb5_enc_keys *, + unsigned int, const struct krb5_enctype **, + gfp_t); +void rxgk_put(struct rxgk_context *); diff --git a/net/rxrpc/rxgk_kdf.c b/net/rxrpc/rxgk_kdf.c new file mode 100644 index 000000000000..2d9353c1dee3 --- /dev/null +++ b/net/rxrpc/rxgk_kdf.c @@ -0,0 +1,271 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* RxGK transport key derivation. + * + * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include "ar-internal.h" +#include "rxgk_common.h" + +#define round16(x) (((x) + 15) & ~15) + +static void rxgk_free(struct rxgk_context *gk) +{ + if (gk->tx_Kc) + crypto_free_shash(gk->tx_Kc); + if (gk->rx_Kc) + crypto_free_shash(gk->rx_Kc); + crypto_krb5_free_enc_keys(&gk->tx_enc); + crypto_krb5_free_enc_keys(&gk->rx_enc); + crypto_krb5_free_enc_keys(&gk->resp_enc); + kfree(gk); +} + +void rxgk_put(struct rxgk_context *gk) +{ + if (gk && refcount_dec_and_test(&gk->usage)) + rxgk_free(gk); +} + +/* + * Transport key derivation function. + * + * TK = random-to-key(PRF+(K0, L, + * epoch || cid || start_time || key_number)) + * [tools.ietf.org/html/draft-wilkinson-afs3-rxgk-11 sec 8.3] + */ +static int rxgk_derive_transport_key(struct rxrpc_connection *conn, + struct rxgk_context *gk, + const struct rxgk_key *rxgk, + struct krb5_buffer *TK, + gfp_t gfp) +{ + const struct krb5_enctype *krb5 = gk->krb5; + struct krb5_buffer conn_info; + unsigned int L = krb5->key_bytes; + __be32 *info; + u8 *buffer; + int ret; + + _enter(""); + + conn_info.len = sizeof(__be32) * 5; + + buffer = kzalloc(round16(conn_info.len), gfp); + if (!buffer) + return -ENOMEM; + + conn_info.data = buffer; + + info = (__be32 *)conn_info.data; + info[0] = htonl(conn->proto.epoch); + info[1] = htonl(conn->proto.cid); + info[2] = htonl(conn->rxgk.start_time >> 32); + info[3] = htonl(conn->rxgk.start_time >> 0); + info[4] = htonl(gk->key_number); + + ret = crypto_krb5_calc_PRFplus(krb5, &rxgk->key, L, &conn_info, TK, gfp); + kfree_sensitive(buffer); + _leave(" = %d", ret); + return ret; +} + +/* + * Constants used to derive the keys and hmacs actually used for doing stuff. + */ +#define RXGK_CLIENT_ENC_PACKET 1026U // 0x402 +#define RXGK_CLIENT_MIC_PACKET 1027U // 0x403 +#define RXGK_SERVER_ENC_PACKET 1028U // 0x404 +#define RXGK_SERVER_MIC_PACKET 1029U // 0x405 +#define RXGK_CLIENT_ENC_RESPONSE 1030U // 0x406 +#define RXGK_SERVER_ENC_TOKEN 1036U // 0x40c + +/* + * Set up the ciphers for the usage keys. + */ +static int rxgk_set_up_ciphers(struct rxrpc_connection *conn, + struct rxgk_context *gk, + const struct rxgk_key *rxgk, + gfp_t gfp) +{ + const struct krb5_enctype *krb5 = gk->krb5; + struct krb5_buffer TK, key; + bool service = rxrpc_conn_is_service(conn); + int ret; + u8 *buffer; + + buffer = kzalloc(krb5->key_bytes * 2, gfp); + if (!buffer) + return -ENOMEM; + + TK.len = krb5->key_bytes; + TK.data = buffer; + key.len = krb5->key_bytes; + key.data = buffer + krb5->key_bytes; + + ret = rxgk_derive_transport_key(conn, gk, rxgk, &TK, gfp); + if (ret < 0) + goto out; + +#define DERIVE_Kc(KEY, USAGE, KC) \ + ret = crypto_krb5_get_Kc(krb5, KEY, USAGE, &key, KC, gfp); \ + if (ret < 0) goto out; +#define DERIVE_Ke(KEY, USAGE, KE) \ + ret = crypto_krb5_get_Ke(krb5, KEY, USAGE, &key, KE, gfp); \ + if (ret < 0) goto out; +#define DERIVE_Ki(KEY, USAGE, KI) \ + ret = crypto_krb5_get_Ki(krb5, KEY, USAGE, &key, KI, gfp); \ + if (ret < 0) goto out; + + DERIVE_Ke(&TK, RXGK_CLIENT_ENC_RESPONSE, &gk->resp_enc.Ke); + DERIVE_Ki(&TK, RXGK_CLIENT_ENC_RESPONSE, &gk->resp_enc.Ki); + + if (crypto_sync_skcipher_blocksize(gk->resp_enc.Ke) != krb5->block_len || + crypto_shash_digestsize(gk->resp_enc.Ki) < krb5->cksum_len) { + pr_notice("algo inconsistent with krb5 table %u!=%u or %u!=%u\n", + crypto_sync_skcipher_blocksize(gk->resp_enc.Ke), krb5->block_len, + crypto_shash_digestsize(gk->resp_enc.Ki), krb5->cksum_len); + return -EINVAL; + } + + if (service) { + switch (conn->params.security_level) { + case RXRPC_SECURITY_AUTH: + DERIVE_Kc(&TK, RXGK_CLIENT_MIC_PACKET, &gk->rx_Kc); + DERIVE_Kc(&TK, RXGK_SERVER_MIC_PACKET, &gk->tx_Kc); + break; + case RXRPC_SECURITY_ENCRYPT: + DERIVE_Ke(&TK, RXGK_CLIENT_ENC_PACKET, &gk->rx_enc.Ke); + DERIVE_Ki(&TK, RXGK_CLIENT_ENC_PACKET, &gk->rx_enc.Ki); + DERIVE_Ke(&TK, RXGK_SERVER_ENC_PACKET, &gk->tx_enc.Ke); + DERIVE_Ki(&TK, RXGK_SERVER_ENC_PACKET, &gk->tx_enc.Ki); + break; + } + } else { + switch (conn->params.security_level) { + case RXRPC_SECURITY_AUTH: + DERIVE_Kc(&TK, RXGK_CLIENT_MIC_PACKET, &gk->tx_Kc); + DERIVE_Kc(&TK, RXGK_SERVER_MIC_PACKET, &gk->rx_Kc); + break; + case RXRPC_SECURITY_ENCRYPT: + DERIVE_Ke(&TK, RXGK_CLIENT_ENC_PACKET, &gk->tx_enc.Ke); + DERIVE_Ki(&TK, RXGK_CLIENT_ENC_PACKET, &gk->tx_enc.Ki); + DERIVE_Ke(&TK, RXGK_SERVER_ENC_PACKET, &gk->rx_enc.Ke); + DERIVE_Ki(&TK, RXGK_SERVER_ENC_PACKET, &gk->rx_enc.Ki); + break; + } + } + + ret = 0; +out: + kfree_sensitive(buffer); + return ret; +} + +/* + * Derive a transport key for a connection and then derive a bunch of usage + * keys from it and set up ciphers using them. + */ +struct rxgk_context *rxgk_generate_transport_key(struct rxrpc_connection *conn, + const struct rxgk_key *key, + unsigned int key_number, + gfp_t gfp) +{ + struct rxgk_context *gk; + unsigned long lifetime; + int ret; + + _enter(""); + + gk = kzalloc(sizeof(struct rxgk_context), GFP_KERNEL); + if (!gk) + return ERR_PTR(-ENOMEM); + refcount_set(&gk->usage, 1); + gk->key = key; + gk->key_number = key_number; + + gk->krb5 = crypto_krb5_find_enctype(key->enctype); + if (!gk->krb5) { + ret = -ENOPKG; + goto err_tk; + } + + ret = rxgk_set_up_ciphers(conn, gk, key, gfp); + if (ret) + goto err_tk; + + /* Set the remaining number of bytes encrypted with this key that may + * be transmitted before rekeying. Note that the spec has been + * interpreted differently on this point... */ + switch (key->bytelife) { + case 0: + case 63: + gk->bytes_remaining = LLONG_MAX; + break; + case 1 ... 62: + gk->bytes_remaining = 1LL << key->bytelife; + break; + default: + gk->bytes_remaining = key->bytelife; + break; + } + + /* Set the time after which rekeying must occur */ + if (key->lifetime) { + lifetime = min_t(u64, key->lifetime, INT_MAX / HZ); + lifetime *= HZ; + } else { + lifetime = MAX_JIFFY_OFFSET; + } + gk->expiry = jiffies + lifetime; + return gk; + +err_tk: + rxgk_put(gk); + _leave(" = %d", ret); + return ERR_PTR(ret); +} + +/* + * Use the server secret key to set up the ciphers that will be used to extract + * the token from a response packet. + */ +int rxgk_set_up_token_cipher(const struct krb5_buffer *server_key, + struct krb5_enc_keys *token_key, + unsigned int enctype, + const struct krb5_enctype **_krb5, + gfp_t gfp) +{ + const struct krb5_enctype *krb5; + struct krb5_buffer key; + int ret; + + ret = -ENOPKG; + krb5 = crypto_krb5_find_enctype(enctype); + if (!krb5) + goto out_buf; + + *_krb5 = krb5; + + key.len = krb5->key_bytes; + key.data = kzalloc(krb5->key_bytes, gfp); + if (!key.data) + return -ENOMEM; + + DERIVE_Ke(server_key, RXGK_SERVER_ENC_TOKEN, &token_key->Ke); + DERIVE_Ki(server_key, RXGK_SERVER_ENC_TOKEN, &token_key->Ki); + ret = 0; +out_buf: + kfree_sensitive(key.data); + return ret; + +out: + crypto_krb5_free_enc_keys(token_key); + goto out; +} From patchwork Thu Nov 12 12:59:55 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Howells X-Patchwork-Id: 11900099 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.8 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS, URIBL_BLOCKED autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9450BC388F7 for ; Thu, 12 Nov 2020 13:00:39 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 1C0DF21D91 for ; Thu, 12 Nov 2020 13:00:39 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="aPAl1lN1" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728271AbgKLNAc (ORCPT ); Thu, 12 Nov 2020 08:00:32 -0500 Received: from us-smtp-delivery-124.mimecast.com ([216.205.24.124]:23387 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728531AbgKLNAR (ORCPT ); Thu, 12 Nov 2020 08:00:17 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1605186011; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=VacggVq1NxiTEfCcpXQrk3CybU38633I0WB8KyzzIbg=; b=aPAl1lN1f4f7XSzWn1bo3q8MElgsq5DN1orAfRkYAsA4Wz8Ucv0liYgdtttLPxIISOF3p4 7MstK2/OA7r8vjzKeXsPJTgS9zE/5SGnDBy1mTpLNO76izacdQVLMVhC2oWSOI4GKVuxu0 sg00I8z5EKBtW/8fypgKk3SoCb0MOYQ= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-463--7VXsiEvNyKULdskpphZjw-1; Thu, 12 Nov 2020 08:00:02 -0500 X-MC-Unique: -7VXsiEvNyKULdskpphZjw-1 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 269171017DD5; Thu, 12 Nov 2020 13:00:00 +0000 (UTC) Received: from warthog.procyon.org.uk (ovpn-115-47.rdu2.redhat.com [10.10.115.47]) by smtp.corp.redhat.com (Postfix) with ESMTP id B8DF05D98F; Thu, 12 Nov 2020 12:59:56 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH 16/18] rxrpc: rxgk: Implement the yfs-rxgk security class (GSSAPI) From: David Howells To: herbert@gondor.apana.org.au, bfields@fieldses.org Cc: dhowells@redhat.com, trond.myklebust@hammerspace.com, linux-crypto@vger.kernel.org, linux-afs@lists.infradead.org, linux-cifs@vger.kernel.org, linux-nfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 12 Nov 2020 12:59:55 +0000 Message-ID: <160518599595.2277919.13767473448752027378.stgit@warthog.procyon.org.uk> In-Reply-To: <160518586534.2277919.14475638653680231924.stgit@warthog.procyon.org.uk> References: <160518586534.2277919.14475638653680231924.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org Implement the basic parts of the yfs-rxgk security class (security index 6) to support GSSAPI-negotiated security. Signed-off-by: David Howells --- include/trace/events/rxrpc.h | 4 net/rxrpc/Makefile | 2 net/rxrpc/ar-internal.h | 12 net/rxrpc/rxgk.c | 1063 ++++++++++++++++++++++++++++++++++++++++++ net/rxrpc/rxgk_app.c | 289 +++++++++++ net/rxrpc/rxgk_common.h | 118 +++++ net/rxrpc/security.c | 3 7 files changed, 1491 insertions(+) create mode 100644 net/rxrpc/rxgk.c create mode 100644 net/rxrpc/rxgk_app.c diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h index e70c90116eda..dd541c6d5ea3 100644 --- a/include/trace/events/rxrpc.h +++ b/include/trace/events/rxrpc.h @@ -210,6 +210,8 @@ enum rxrpc_tx_point { rxrpc_tx_point_call_data_nofrag, rxrpc_tx_point_call_final_resend, rxrpc_tx_point_conn_abort, + rxrpc_tx_point_rxgk_challenge, + rxrpc_tx_point_rxgk_response, rxrpc_tx_point_rxkad_challenge, rxrpc_tx_point_rxkad_response, rxrpc_tx_point_reject, @@ -440,6 +442,8 @@ enum rxrpc_tx_point { EM(rxrpc_tx_point_call_final_resend, "CallFinalResend") \ EM(rxrpc_tx_point_conn_abort, "ConnAbort") \ EM(rxrpc_tx_point_reject, "Reject") \ + EM(rxrpc_tx_point_rxgk_challenge, "RxGKChall") \ + EM(rxrpc_tx_point_rxgk_response, "RxGKResp") \ EM(rxrpc_tx_point_rxkad_challenge, "RxkadChall") \ EM(rxrpc_tx_point_rxkad_response, "RxkadResp") \ EM(rxrpc_tx_point_version_keepalive, "VerKeepalive") \ diff --git a/net/rxrpc/Makefile b/net/rxrpc/Makefile index 08636858e77f..4be98775dc7f 100644 --- a/net/rxrpc/Makefile +++ b/net/rxrpc/Makefile @@ -37,4 +37,6 @@ rxrpc-$(CONFIG_RXKAD) += rxkad.o rxrpc-$(CONFIG_SYSCTL) += sysctl.o rxrpc-$(CONFIG_RXGK) += \ + rxgk.o \ + rxgk_app.o \ rxgk_kdf.o diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 4e0766b4a714..efdb3334ad88 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -37,6 +37,7 @@ struct rxrpc_crypt { struct key_preparsed_payload; struct rxrpc_connection; +struct rxgk_context; /* * Mark applied to socket buffers in skb->mark. skb->priority is used @@ -264,6 +265,10 @@ struct rxrpc_security { /* clear connection security */ void (*clear)(struct rxrpc_connection *); + + /* Default ticket -> key decoder */ + int (*default_decode_ticket)(struct sk_buff *, unsigned int, unsigned int, + u32 *, struct key **); }; /* @@ -457,7 +462,9 @@ struct rxrpc_connection { u32 nonce; /* response re-use preventer */ } rxkad; struct { + struct rxgk_context *keys[1]; u64 start_time; /* The start time for TK derivation */ + u8 nonce[20]; /* Response re-use preventer */ } rxgk; }; unsigned long flags; @@ -1056,6 +1063,11 @@ void rxrpc_peer_add_rtt(struct rxrpc_call *, enum rxrpc_rtt_rx_trace, int, unsigned long rxrpc_get_rto_backoff(struct rxrpc_peer *, bool); void rxrpc_peer_init_rtt(struct rxrpc_peer *); +/* + * rxgk.c + */ +extern const struct rxrpc_security rxgk_yfs; + /* * rxkad.c */ diff --git a/net/rxrpc/rxgk.c b/net/rxrpc/rxgk.c new file mode 100644 index 000000000000..703e46e8b508 --- /dev/null +++ b/net/rxrpc/rxgk.c @@ -0,0 +1,1063 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* GSSAPI-based RxRPC security + * + * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include "ar-internal.h" +#include "rxgk_common.h" + +struct rxgk_header { + __be32 epoch; + __be32 cid; + __be32 call_number; + __be32 seq; + __be32 sec_index; + __be32 data_len; +} __packed; + +struct rxgk_response { + __be64 start_time; + __be32 token_len; +} __packed; + +/* + * Parse the information from a server key + */ +static int rxgk_preparse_server_key(struct key_preparsed_payload *prep) +{ + const struct krb5_enctype *krb5; + struct krb5_buffer *server_key = (void *)&prep->payload.data[2]; + unsigned int service, sec_class, kvno, enctype; + int n = 0; + + _enter("%zu", prep->datalen); + + if (sscanf(prep->orig_description, "%u:%u:%u:%u%n", + &service, &sec_class, &kvno, &enctype, &n) != 4) + return -EINVAL; + + if (prep->orig_description[n]) + return -EINVAL; + + krb5 = crypto_krb5_find_enctype(enctype); + if (!krb5) + return -ENOPKG; + + prep->payload.data[0] = (struct krb5_enctype *)krb5; + + if (prep->datalen != krb5->key_len) + return -EKEYREJECTED; + + server_key->len = prep->datalen; + server_key->data = kmemdup(prep->data, prep->datalen, GFP_KERNEL); + if (!server_key->data) + return -ENOMEM; + + _leave(" = 0"); + return 0; +} + +static void rxgk_free_server_key(union key_payload *payload) +{ + struct krb5_buffer *server_key = (void *)&payload->data[2]; + + kfree_sensitive(server_key->data); +} + +static void rxgk_free_preparse_server_key(struct key_preparsed_payload *prep) +{ + rxgk_free_server_key(&prep->payload); +} + +static void rxgk_destroy_server_key(struct key *key) +{ + rxgk_free_server_key(&key->payload); +} + +static void rxgk_describe_server_key(const struct key *key, struct seq_file *m) +{ + const struct krb5_enctype *krb5 = key->payload.data[0]; + + if (krb5) + seq_printf(m, ": %s", krb5->name); +} + +static struct rxgk_context *rxgk_get_key(struct rxrpc_connection *conn, + u16 *specific_key_number) +{ + refcount_inc(&conn->rxgk.keys[0]->usage); + return conn->rxgk.keys[0]; +} + +/* + * initialise connection security + */ +static int rxgk_init_connection_security(struct rxrpc_connection *conn, + struct rxrpc_key_token *token) +{ + struct rxgk_context *gk; + int ret; + + _enter("{%d},{%x}", conn->debug_id, key_serial(conn->params.key)); + + conn->security_ix = token->security_index; + conn->params.security_level = token->rxgk->level; + + if (rxrpc_conn_is_client(conn)) { + conn->rxgk.start_time = ktime_get(); + do_div(conn->rxgk.start_time, 100); + } + + gk = rxgk_generate_transport_key(conn, token->rxgk, 0, GFP_NOFS); + if (IS_ERR(gk)) + return PTR_ERR(gk); + conn->rxgk.keys[0] = gk; + + switch (conn->params.security_level) { + case RXRPC_SECURITY_PLAIN: + break; + case RXRPC_SECURITY_AUTH: + conn->security_size = gk->krb5->cksum_len; + break; + case RXRPC_SECURITY_ENCRYPT: + if (gk->krb5->pad) + conn->size_align = gk->krb5->block_len; + conn->security_size = gk->krb5->conf_len + sizeof(struct rxgk_header); + conn->security_trailer = gk->krb5->cksum_len; + break; + default: + ret = -EKEYREJECTED; + goto error; + } + + ret = 0; +error: + _leave(" = %d", ret); + return ret; +} + +/* + * Clean up the crypto on a call. + */ +static void rxgk_free_call_crypto(struct rxrpc_call *call) +{ +} + +/* + * Integrity mode (sign a packet - level 1 security) + */ +static int rxgk_secure_packet_integrity(const struct rxrpc_call *call, + struct rxgk_context *gk, + struct sk_buff *skb, u32 data_size) +{ + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + struct rxgk_header *hdr; + struct krb5_buffer metadata; + int ret = -ENOMEM; + + _enter(""); + + hdr = kzalloc(sizeof(*hdr), GFP_NOFS); + if (!hdr) + goto error_gk; + + hdr->epoch = htonl(call->conn->proto.epoch); + hdr->cid = htonl(call->cid); + hdr->call_number = htonl(call->call_id); + hdr->seq = htonl(sp->hdr.seq); + hdr->sec_index = htonl(call->security_ix); + hdr->data_len = htonl(data_size); + + metadata.len = sizeof(*hdr); + metadata.data = hdr; + ret = rxgk_get_mic_skb(gk->krb5, gk->tx_Kc, &metadata, skb, + 0, skb->len, gk->krb5->cksum_len, data_size); + if (ret >= 0) + gk->bytes_remaining -= ret; + kfree(hdr); +error_gk: + rxgk_put(gk); + _leave(" = %d", ret); + return ret; +} + +/* + * wholly encrypt a packet (level 2 security) + */ +static int rxgk_secure_packet_encrypted(const struct rxrpc_call *call, + struct rxgk_context *gk, + struct sk_buff *skb, u32 data_size) +{ + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + struct rxgk_header hdr; + int ret; + + _enter("%x,%x", skb->len, data_size); + + /* Insert the header into the skb */ + hdr.epoch = htonl(call->conn->proto.epoch); + hdr.cid = htonl(call->cid); + hdr.call_number = htonl(call->call_id); + hdr.seq = htonl(sp->hdr.seq); + hdr.sec_index = htonl(call->security_ix); + hdr.data_len = htonl(data_size); + + ret = skb_store_bits(skb, gk->krb5->conf_len, &hdr, sizeof(hdr)); + if (ret < 0) + goto error; + + /* Increase the buffer size to allow for the checksum to be written in */ + skb->len += gk->krb5->cksum_len; + + ret = rxgk_encrypt_skb(gk->krb5, &gk->tx_enc, skb, + 0, skb->len, gk->krb5->conf_len, sizeof(hdr) + data_size, + false); + if (ret >= 0) + gk->bytes_remaining -= ret; + +error: + rxgk_put(gk); + _leave(" = %d", ret); + return ret; +} + +/* + * checksum an RxRPC packet header + */ +static int rxgk_secure_packet(struct rxrpc_call *call, + struct sk_buff *skb, + size_t data_size) +{ + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + struct rxgk_context *gk; + int ret; + + sp = rxrpc_skb(skb); + + _enter("{%d{%x}},{#%u},%zu,", + call->debug_id, key_serial(call->conn->params.key), + sp->hdr.seq, data_size); + + gk = rxgk_get_key(call->conn, NULL); + if (IS_ERR(gk)) + return PTR_ERR(gk) == -ESTALE ? -EKEYREJECTED : PTR_ERR(gk); + + ret = key_validate(call->conn->params.key); + if (ret < 0) + return ret; + + sp->hdr.cksum = gk->key_number; + + switch (call->conn->params.security_level) { + case RXRPC_SECURITY_PLAIN: + rxgk_put(gk); + return 0; + case RXRPC_SECURITY_AUTH: + return rxgk_secure_packet_integrity(call, gk, skb, data_size); + case RXRPC_SECURITY_ENCRYPT: + return rxgk_secure_packet_encrypted(call, gk, skb, data_size); + default: + rxgk_put(gk); + return -EPERM; + } +} + +/* + * Integrity mode (check the signature on a packet - level 1 security) + */ +static int rxgk_verify_packet_integrity(struct rxrpc_call *call, + struct rxgk_context *gk, + struct sk_buff *skb, + unsigned int offset, unsigned int len, + rxrpc_seq_t seq) +{ + struct rxgk_header *hdr; + struct krb5_buffer metadata; + bool aborted; + u32 ac; + int ret = -ENOMEM; + + _enter(""); + + hdr = kzalloc(sizeof(*hdr), GFP_NOFS); + if (!hdr) + goto error; + + hdr->epoch = htonl(call->conn->proto.epoch); + hdr->cid = htonl(call->cid); + hdr->call_number = htonl(call->call_id); + hdr->seq = htonl(seq); + hdr->sec_index = htonl(call->security_ix); + hdr->data_len = htonl(len - gk->krb5->cksum_len); + + metadata.len = sizeof(*hdr); + metadata.data = hdr; + ret = rxgk_verify_mic_skb(gk->krb5, gk->rx_Kc, &metadata, + skb, &offset, &len, &ac); + kfree(hdr); + if (ret < 0) { + if (ret == -EPROTO) { + aborted = rxrpc_abort_eproto(call, skb, "rxgk_2_vfy", + "V1V", ac); + goto protocol_error; + } + goto error; + } + +error: + rxgk_put(gk); + _leave(" = %d", ret); + return ret; + +protocol_error: + if (aborted) + rxrpc_send_abort_packet(call); + ret = -EPROTO; + goto error; +} + +/* + * Decrypt an encrypted packet (level 2 security). + */ +static int rxgk_verify_packet_encrypted(struct rxrpc_call *call, + struct rxgk_context *gk, + struct sk_buff *skb, + unsigned int offset, unsigned int len, + rxrpc_seq_t seq) +{ + struct rxgk_header hdr; + bool aborted; + int ret; + u32 ac; + + _enter(""); + + ret = rxgk_decrypt_skb(gk->krb5, &gk->rx_enc, skb, &offset, &len, &ac); + if (ret < 0) { + if (ret == -EPROTO) { + aborted = rxrpc_abort_eproto(call, skb, "rxgk_2_dec", + "V2D", ac); + goto protocol_error; + } + goto error; + } + + if (len < sizeof(hdr)) { + aborted = rxrpc_abort_eproto(call, skb, "rxgk_2_hdr", + "V2L", RXGK_PACKETSHORT); + goto protocol_error; + } + + /* Extract the header from the skb */ + ret = skb_copy_bits(skb, offset, &hdr, sizeof(hdr)); + if (ret < 0) + goto error; + len -= sizeof(hdr); + + if (ntohl(hdr.epoch) != call->conn->proto.epoch || + ntohl(hdr.cid) != call->cid || + ntohl(hdr.call_number) != call->call_id || + ntohl(hdr.seq) != seq || + ntohl(hdr.sec_index) != call->security_ix || + ntohl(hdr.data_len) > len) { + aborted = rxrpc_abort_eproto(call, skb, "rxgk_2_hdr", "V2H", + RXGK_SEALED_INCON); + goto protocol_error; + } + + ret = 0; + +error: + rxgk_put(gk); + _leave(" = %d", ret); + return ret; + +protocol_error: + if (aborted) + rxrpc_send_abort_packet(call); + ret = -EPROTO; + goto error; +} + +/* + * Verify the security on a received packet or subpacket (if part of a + * jumbo packet). + */ +static int rxgk_verify_packet(struct rxrpc_call *call, struct sk_buff *skb, + unsigned int offset, unsigned int len, + rxrpc_seq_t seq, u16 key_number) +{ + struct rxgk_context *gk; + bool aborted; + + _enter("{%d{%x}},{#%u}", + call->debug_id, key_serial(call->conn->params.key), seq); + + gk = rxgk_get_key(call->conn, &key_number); + if (IS_ERR(gk)) { + switch (PTR_ERR(gk)) { + case -ESTALE: + aborted = rxrpc_abort_eproto(call, skb, "rxgk_csum", "VKY", + RXGK_BADKEYNO); + gk = NULL; + goto protocol_error; + default: + return PTR_ERR(gk); + } + } + + switch (call->conn->params.security_level) { + case RXRPC_SECURITY_PLAIN: + return 0; + case RXRPC_SECURITY_AUTH: + return rxgk_verify_packet_integrity(call, gk, skb, offset, len, seq); + case RXRPC_SECURITY_ENCRYPT: + return rxgk_verify_packet_encrypted(call, gk, skb, offset, len, seq); + default: + rxgk_put(gk); + return -ENOANO; + } + +protocol_error: + if (aborted) + rxrpc_send_abort_packet(call); + rxgk_put(gk); + return -EPROTO; +} + +/* + * Locate the data contained in a packet that was partially encrypted. + */ +static void rxgk_locate_data_1(struct rxrpc_call *call, struct sk_buff *skb, + unsigned int *_offset, unsigned int *_len) +{ + *_offset += call->conn->security_size; + *_len -= call->conn->security_size; +} + +/* + * Locate the data contained in a packet that was completely encrypted. + */ +static void rxgk_locate_data_2(struct rxrpc_call *call, struct sk_buff *skb, + unsigned int *_offset, unsigned int *_len) +{ + unsigned int off = call->conn->security_size - sizeof(__be32); + __be32 data_length_be; + u32 data_length; + + if (skb_copy_bits(skb, *_offset + off, &data_length_be, sizeof(u32)) < 0) + BUG(); + data_length = ntohl(data_length_be); + *_offset += call->conn->security_size; + *_len = data_length; +} + +/* + * Locate the data contained in an already decrypted packet. + */ +static void rxgk_locate_data(struct rxrpc_call *call, struct sk_buff *skb, + unsigned int *_offset, unsigned int *_len) +{ + switch (call->conn->params.security_level) { + case RXRPC_SECURITY_AUTH: + rxgk_locate_data_1(call, skb, _offset, _len); + return; + case RXRPC_SECURITY_ENCRYPT: + rxgk_locate_data_2(call, skb, _offset, _len); + return; + default: + return; + } +} + +/* + * issue a challenge + */ +static int rxgk_issue_challenge(struct rxrpc_connection *conn) +{ + struct rxrpc_wire_header whdr; + struct msghdr msg; + struct kvec iov[2]; + size_t len; + u32 serial; + int ret; + + _enter("{%d}", conn->debug_id); + + get_random_bytes(&conn->rxgk.nonce, sizeof(conn->rxgk.nonce)); + + msg.msg_name = &conn->params.peer->srx.transport; + msg.msg_namelen = conn->params.peer->srx.transport_len; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + whdr.epoch = htonl(conn->proto.epoch); + whdr.cid = htonl(conn->proto.cid); + whdr.callNumber = 0; + whdr.seq = 0; + whdr.type = RXRPC_PACKET_TYPE_CHALLENGE; + whdr.flags = conn->out_clientflag; + whdr.userStatus = 0; + whdr.securityIndex = conn->security_ix; + whdr._rsvd = 0; + whdr.serviceId = htons(conn->service_id); + + iov[0].iov_base = &whdr; + iov[0].iov_len = sizeof(whdr); + iov[1].iov_base = conn->rxgk.nonce; + iov[1].iov_len = sizeof(conn->rxgk.nonce); + + len = iov[0].iov_len + iov[1].iov_len; + + serial = atomic_inc_return(&conn->serial); + whdr.serial = htonl(serial); + _proto("Tx CHALLENGE %%%u", serial); + + ret = kernel_sendmsg(conn->params.local->socket, &msg, iov, 2, len); + if (ret < 0) { + trace_rxrpc_tx_fail(conn->debug_id, serial, ret, + rxrpc_tx_point_rxgk_challenge); + return -EAGAIN; + } + + conn->params.peer->last_tx_at = ktime_get_seconds(); + trace_rxrpc_tx_packet(conn->debug_id, &whdr, + rxrpc_tx_point_rxgk_challenge); + _leave(" = 0"); + return 0; +} + +/* + * Send a response packet. + */ +static int rxgk_send_response(struct rxrpc_connection *conn, + struct sk_buff *skb) +{ + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + struct rxrpc_wire_header whdr; + struct msghdr msg; + struct kvec iov[2]; + size_t len; + u32 serial; + int ret, i; + + _enter(""); + + msg.msg_name = &conn->params.peer->srx.transport; + msg.msg_namelen = conn->params.peer->srx.transport_len; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + memset(&whdr, 0, sizeof(whdr)); + whdr.epoch = htonl(sp->hdr.epoch); + whdr.cid = htonl(sp->hdr.cid); + whdr.type = RXRPC_PACKET_TYPE_RESPONSE; + whdr.flags = sp->hdr.flags; + whdr.securityIndex = sp->hdr.securityIndex; + whdr.cksum = htons(sp->hdr.cksum); + whdr.serviceId = htons(sp->hdr.serviceId); + + iov[0].iov_base = &whdr; + iov[0].iov_len = sizeof(whdr); + iov[1].iov_base = skb->head; + iov[1].iov_len = skb->len; + + len = 0; + for (i = 0; i < ARRAY_SIZE(iov); i++) + len += iov[i].iov_len; + + serial = atomic_inc_return(&conn->serial); + whdr.serial = htonl(serial); + _proto("Tx RESPONSE %%%u", serial); + + ret = kernel_sendmsg(conn->params.local->socket, &msg, + iov, ARRAY_SIZE(iov), len); + if (ret < 0) { + trace_rxrpc_tx_fail(conn->debug_id, serial, ret, + rxrpc_tx_point_rxgk_response); + return -EAGAIN; + } + + conn->params.peer->last_tx_at = ktime_get_seconds(); + _leave(" = 0"); + return 0; +} + +/* + * Construct the authenticator to go in the response packet + * + * struct RXGK_Authenticator { + * opaque nonce[20]; + * opaque appdata<>; + * RXGK_Level level; + * unsigned int epoch; + * unsigned int cid; + * unsigned int call_numbers<>; + * }; + */ +static void rxgk_construct_authenticator(struct rxrpc_connection *conn, + const u8 *nonce, + struct sk_buff *skb) +{ + __be32 xdr[9]; + + __skb_put_data(skb, nonce, 20); + + xdr[0] = htonl(0); /* appdata len */ + xdr[1] = htonl(conn->params.security_level); + xdr[2] = htonl(conn->proto.epoch); + xdr[3] = htonl(conn->proto.cid); + xdr[4] = htonl(4); /* # call_numbers */ + xdr[5] = htonl(conn->channels[0].call_counter); + xdr[6] = htonl(conn->channels[1].call_counter); + xdr[7] = htonl(conn->channels[2].call_counter); + xdr[8] = htonl(conn->channels[3].call_counter); + + __skb_put_data(skb, xdr, sizeof(xdr)); +} + +/* + * Construct the response. + * + * struct RXGK_Response { + * rxgkTime start_time; + * RXGK_Data token; + * opaque authenticator + * }; + */ +static int rxgk_construct_response(struct rxrpc_connection *conn, + struct sk_buff *challenge, + const u8 *nonce) +{ + struct rxrpc_skb_priv *csp = rxrpc_skb(challenge), *rsp; + struct rxgk_context *gk; + struct sk_buff *skb; + unsigned short resp_len, auth_len, pad_len, enc_len, auth_pad_len, authx_len; + unsigned short auth_offset, authx_offset; + __be64 start_time; + __be32 tmp; + void *p; + int ret; + + gk = rxgk_get_key(conn, NULL); + if (IS_ERR(gk)) + return PTR_ERR(gk); + + auth_len = 20 + 4 /* appdatalen */ + 12 + (1 + 4) * 4; + if (gk->krb5->pad) { + enc_len = round_up(gk->krb5->conf_len + auth_len, gk->krb5->block_len); + pad_len = enc_len - (gk->krb5->conf_len + auth_len); + } else { + enc_len = gk->krb5->conf_len + auth_len; + pad_len = 0; + } + authx_len = enc_len + gk->krb5->cksum_len; + auth_pad_len = xdr_round_up(authx_len) - authx_len; + + resp_len = 8; + resp_len += 4 + xdr_round_up(gk->key->ticket.len); + resp_len += 4 + xdr_round_up(authx_len); + + ret = -ENOMEM; + skb = alloc_skb(resp_len, GFP_NOFS); + if (!skb) + goto error_gk; + + rsp = rxrpc_skb(skb); + rsp->hdr = csp->hdr; + rsp->hdr.flags = conn->out_clientflag; + rsp->hdr.cksum = gk->key_number; + + start_time = cpu_to_be64(conn->rxgk.start_time); + p = __skb_put_data(skb, &start_time, 8); + + tmp = htonl(gk->key->ticket.len); + __skb_put_data(skb, &tmp, 4); + __skb_put_data(skb, gk->key->ticket.data, xdr_round_up(gk->key->ticket.len)); + tmp = htonl(authx_len); + __skb_put_data(skb, &tmp, 4); + authx_offset = skb->len; + __skb_put_zero(skb, gk->krb5->conf_len); + auth_offset = skb->len; + rxgk_construct_authenticator(conn, nonce, skb); + __skb_put_zero(skb, pad_len + gk->krb5->cksum_len + auth_pad_len); + + ret = rxgk_encrypt_skb(gk->krb5, &gk->resp_enc, skb, + authx_offset, authx_len, + auth_offset, auth_len, false); + if (ret < 0) + goto error; + + ret = rxgk_send_response(conn, skb); +error: + kfree_skb(skb); +error_gk: + rxgk_put(gk); + _leave(" = %d", ret); + return ret; +} + +/* + * Respond to a challenge packet + */ +static int rxgk_respond_to_challenge(struct rxrpc_connection *conn, + struct sk_buff *skb, + u32 *_abort_code) +{ + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + const char *eproto; + u32 abort_code; + u8 nonce[20]; + int ret; + + _enter("{%d,%x}", conn->debug_id, key_serial(conn->params.key)); + + eproto = tracepoint_string("chall_no_key"); + abort_code = RX_PROTOCOL_ERROR; + if (!conn->params.key) + goto protocol_error; + + abort_code = RXGK_EXPIRED; + ret = key_validate(conn->params.key); + if (ret < 0) + goto other_error; + + eproto = tracepoint_string("chall_short"); + abort_code = RXGK_PACKETSHORT; + if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header), + nonce, sizeof(nonce)) < 0) + goto protocol_error; + + _proto("Rx CHALLENGE %%%u { n=%20phN }", sp->hdr.serial, nonce); + + ret = rxgk_construct_response(conn, skb, nonce); + if (ret < 0) + goto error; + return ret; + +protocol_error: + trace_rxrpc_rx_eproto(NULL, sp->hdr.serial, eproto); + ret = -EPROTO; +other_error: + *_abort_code = abort_code; +error: + return ret; +} + +/* + * Verify the authenticator. + * + * struct RXGK_Authenticator { + * opaque nonce[20]; + * opaque appdata<>; + * RXGK_Level level; + * unsigned int epoch; + * unsigned int cid; + * unsigned int call_numbers<>; + * }; + */ +static int rxgk_verify_authenticator(struct rxrpc_connection *conn, + const struct krb5_enctype *krb5, + struct sk_buff *skb, + unsigned int auth_offset, unsigned int auth_len, + u32 *_abort_code, const char **_eproto) +{ + void *auth; + __be32 *p, *end; + u32 app_len, call_count, level, epoch, cid, i; + int ret; + + _enter(""); + + auth = kmalloc(auth_len, GFP_NOFS); + if (!auth) + return -ENOMEM; + + ret = skb_copy_bits(skb, auth_offset, auth, auth_len); + if (ret < 0) + goto error; + + *_eproto = tracepoint_string("rxgk_rsp_nonce"); + p = auth; + end = auth + auth_len; + if (memcmp(auth, conn->rxgk.nonce, 20) != 0) + goto bad_auth; + p += 20 / sizeof(__be32); + + *_eproto = tracepoint_string("rxgk_rsp_applen"); + app_len = ntohl(*p++); + if (app_len > (end - p) * sizeof(__be32)) + goto bad_auth; + p += xdr_round_up(app_len) / sizeof(__be32); + if (end - p < 4) + goto bad_auth; + level = ntohl(*p++); + epoch = ntohl(*p++); + cid = ntohl(*p++); + call_count = ntohl(*p++); + + *_eproto = tracepoint_string("rxgk_rsp_params"); + if (level != conn->params.security_level || + epoch != conn->proto.epoch || + cid != conn->proto.cid || + call_count > 4) + goto bad_auth; + if (end - p < call_count) + goto bad_auth; + + spin_lock(&conn->bundle->channel_lock); + for (i = 0; i < call_count; i++) { + struct rxrpc_call *call; + u32 call_id = ntohl(*p++); + + *_eproto = tracepoint_string("rxgk_rsp_callid"); + if (call_id > INT_MAX) + goto bad_auth_unlock; + + *_eproto = tracepoint_string("rxgk_rsp_callctr"); + if (call_id < conn->channels[i].call_counter) + goto bad_auth_unlock; + + *_eproto = tracepoint_string("rxgk_rsp_callst"); + if (call_id > conn->channels[i].call_counter) { + call = rcu_dereference_protected( + conn->channels[i].call, + lockdep_is_held(&conn->bundle->channel_lock)); + if (call && call->state < RXRPC_CALL_COMPLETE) + goto bad_auth_unlock; + conn->channels[i].call_counter = call_id; + } + } + spin_unlock(&conn->bundle->channel_lock); + ret = 0; +error: + kfree(auth); + _leave(" = %d", ret); + return ret; + +bad_auth_unlock: + spin_unlock(&conn->bundle->channel_lock); +bad_auth: + *_abort_code = RXGK_NOTAUTH; + ret = -EPROTO; + goto error; +} + +/* + * Verify a response. + * + * struct RXGK_Response { + * rxgkTime start_time; + * RXGK_Data token; + * opaque authenticator + * }; + */ +static int rxgk_verify_response(struct rxrpc_connection *conn, + struct sk_buff *skb, + u32 *_abort_code) +{ + const struct krb5_enctype *krb5; + struct rxrpc_key_token *token; + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + struct krb5_enc_keys token_enc = {}; + struct rxgk_context *gk; + struct key *key = NULL; + const char *eproto; + unsigned int offset = sizeof(struct rxrpc_wire_header); + unsigned int len = skb->len - sizeof(struct rxrpc_wire_header); + unsigned int token_offset, token_len; + unsigned int auth_offset, auth_len; + __be32 xauth_len; + u32 abort_code; + int ret; + + struct rxgk_response rhdr; + + _enter("{%d}", conn->debug_id); + + /* Parse the RXGK_Response object */ + if (sizeof(rhdr) + sizeof(__be32) > len) + goto short_packet; + + if (skb_copy_bits(skb, offset, &rhdr, sizeof(rhdr)) < 0) + goto short_packet; + offset += sizeof(rhdr); + len -= sizeof(rhdr); + + token_offset = offset; + token_len = ntohl(rhdr.token_len); + if (xdr_round_up(token_len) + sizeof(__be32) > len) + goto short_packet; + + offset += xdr_round_up(token_len); + len -= xdr_round_up(token_len); + + if (skb_copy_bits(skb, offset, &xauth_len, sizeof(xauth_len)) < 0) + goto short_packet; + offset += sizeof(xauth_len); + len -= sizeof(xauth_len); + + auth_offset = offset; + auth_len = ntohl(xauth_len); + if (auth_len < len) + goto short_packet; + if (auth_len & 3) + goto inconsistent; + if (auth_len < 20 + 9 * 4) + goto auth_too_short; + + /* We need to extract and decrypt the token and instantiate a session + * key for it. This bit, however, is application-specific. If + * possible, we use a default parser, but we might end up bumping this + * to the app to deal with - which might mean a round trip to + * userspace. + */ + ret = rxgk_extract_token(conn, skb, token_offset, token_len, &key, + &abort_code, &eproto); + if (ret < 0) + goto protocol_error; + + /* We now have a key instantiated from the decrypted ticket. We can + * pass this to the application so that they can parse the ticket + * content and we can use the session key it contains to derive the + * keys we need. + * + * Note that we have to switch enctype at this point as the enctype of + * the ticket doesn't necessarily match that of the transport. + */ + token = key->payload.data[0]; + conn->params.security_level = token->rxgk->level; + conn->rxgk.start_time = __be64_to_cpu(rhdr.start_time); + + gk = rxgk_generate_transport_key(conn, token->rxgk, sp->hdr.cksum, GFP_NOFS); + if (IS_ERR(gk)) { + ret = PTR_ERR(gk); + goto cant_get_token; + } + + krb5 = gk->krb5; + + /* Decrypt, parse and verify the authenticator. */ + eproto = tracepoint_string("rxgk_rsp_dec_auth"); + ret = rxgk_decrypt_skb(krb5, &gk->resp_enc, skb, + &auth_offset, &auth_len, &abort_code); + if (ret < 0) + goto protocol_error; + + ret = rxgk_verify_authenticator(conn, krb5, skb, auth_offset, auth_len, + &abort_code, &eproto); + if (ret < 0) + goto protocol_error; + + conn->params.key = key; + key = NULL; + ret = 0; +out: + key_put(key); + crypto_krb5_free_enc_keys(&token_enc); + _leave(" = %d", ret); + return ret; + +inconsistent: + eproto = tracepoint_string("rxgk_rsp_xdr_align"); + abort_code = RXGK_INCONSISTENCY; + ret = -EPROTO; + goto protocol_error; +auth_too_short: + eproto = tracepoint_string("rxgk_rsp_short_auth"); + abort_code = RXGK_PACKETSHORT; + ret = -EPROTO; + goto protocol_error; +short_packet: + eproto = tracepoint_string("rxgk_rsp_short"); + abort_code = RXGK_PACKETSHORT; + ret = -EPROTO; +protocol_error: + trace_rxrpc_rx_eproto(NULL, sp->hdr.serial, eproto); + *_abort_code = abort_code; + goto out; + +cant_get_token: + switch (ret) { + case -ENOMEM: + goto temporary_error; + case -EINVAL: + eproto = tracepoint_string("rxgk_rsp_internal_error"); + abort_code = RXGK_NOTAUTH; + ret = -EKEYREJECTED; + goto protocol_error; + case -ENOPKG: + eproto = tracepoint_string("rxgk_rsp_nopkg"); + abort_code = RXGK_BADETYPE; + ret = -EKEYREJECTED; + goto protocol_error; + } + +temporary_error: + /* Ignore the response packet if we got a temporary error such as + * ENOMEM. We just want to send the challenge again. Note that we + * also come out this way if the ticket decryption fails. + */ + goto out; +} + +/* + * clear the connection security + */ +static void rxgk_clear(struct rxrpc_connection *conn) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(conn->rxgk.keys); i++) + rxgk_put(conn->rxgk.keys[i]); +} + +/* + * Initialise the RxGK security service. + */ +static int rxgk_init(void) +{ + return 0; +} + +/* + * Clean up the RxGK security service. + */ +static void rxgk_exit(void) +{ +} + +/* + * RxRPC YFS GSSAPI-based security + */ +const struct rxrpc_security rxgk_yfs = { + .name = "yfs-rxgk", + .security_index = RXRPC_SECURITY_YFS_RXGK, + .no_key_abort = RXGK_NOTAUTH, + .init = rxgk_init, + .exit = rxgk_exit, + .preparse_server_key = rxgk_preparse_server_key, + .free_preparse_server_key = rxgk_free_preparse_server_key, + .destroy_server_key = rxgk_destroy_server_key, + .describe_server_key = rxgk_describe_server_key, + .init_connection_security = rxgk_init_connection_security, + .secure_packet = rxgk_secure_packet, + .verify_packet = rxgk_verify_packet, + .free_call_crypto = rxgk_free_call_crypto, + .locate_data = rxgk_locate_data, + .issue_challenge = rxgk_issue_challenge, + .respond_to_challenge = rxgk_respond_to_challenge, + .verify_response = rxgk_verify_response, + .clear = rxgk_clear, + .default_decode_ticket = rxgk_yfs_decode_ticket, +}; diff --git a/net/rxrpc/rxgk_app.c b/net/rxrpc/rxgk_app.c new file mode 100644 index 000000000000..895879f3acfb --- /dev/null +++ b/net/rxrpc/rxgk_app.c @@ -0,0 +1,289 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Application-specific bits for GSSAPI-based RxRPC security + * + * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include "ar-internal.h" +#include "rxgk_common.h" + +/* + * Decode a default-style YFS ticket in a response and turn it into an + * rxrpc-type key. + * + * struct rxgk_key { + * afs_uint32 enctype; + * opaque key<>; + * }; + * + * struct RXGK_AuthName { + * afs_int32 kind; + * opaque data; + * opaque display; + * }; + * + * struct RXGK_Token { + * rxgk_key K0; + * RXGK_Level level; + * rxgkTime starttime; + * afs_int32 lifetime; + * afs_int32 bytelife; + * rxgkTime expirationtime; + * struct RXGK_AuthName identities<>; + * }; + */ +int rxgk_yfs_decode_ticket(struct sk_buff *skb, + unsigned int ticket_offset, unsigned int ticket_len, + u32 *_abort_code, + struct key **_key) +{ + struct rxrpc_key_token *token; + const struct cred *cred = current_cred(); // TODO - use socket creds + struct key *key; + size_t pre_ticket_len, payload_len; + unsigned int klen, enctype; + void *payload, *ticket; + __be32 *t, *p, *q, tmp[2]; + int ret; + + _enter(""); + + /* Get the session key length */ + ret = skb_copy_bits(skb, ticket_offset, tmp, sizeof(tmp)); + if (ret < 0) + goto error_out; + enctype = ntohl(tmp[0]); + klen = ntohl(tmp[1]); + + if (klen > ticket_len - 10 * sizeof(__be32)) { + *_abort_code = RXGK_INCONSISTENCY; + return -EPROTO; + } + + pre_ticket_len = ((5 + 14) * sizeof(__be32) + + xdr_round_up(klen) + + sizeof(__be32)); + payload_len = pre_ticket_len + xdr_round_up(ticket_len); + + payload = kzalloc(payload_len, GFP_NOFS); + if (!payload) + return -ENOMEM; + + /* We need to fill out the XDR form for a key payload that we can pass + * to add_key(). Start by copying in the ticket so that we can parse + * it. + */ + ticket = payload + pre_ticket_len; + ret = skb_copy_bits(skb, ticket_offset, ticket, ticket_len); + if (ret < 0) + goto error; + + /* Fill out the form header. */ + p = payload; + p[0] = htonl(0); /* Flags */ + p[1] = htonl(1); /* len(cellname) */ + p[2] = htonl(0x20000000); /* Cellname " " */ + p[3] = htonl(1); /* #tokens */ + p[4] = htonl(15 * sizeof(__be32) + xdr_round_up(klen) + xdr_round_up(ticket_len)); /* Token len */ + + /* Now fill in the body. Most of this we can just scrape directly from + * the ticket. + */ + t = ticket + sizeof(__be32) * 2 + xdr_round_up(klen); + q = payload + 5 * sizeof(__be32); + q[ 0] = htonl(RXRPC_SECURITY_YFS_RXGK); + q[ 1] = t[1]; /* begintime - msw */ + q[ 2] = t[2]; /* - lsw */ + q[ 3] = t[5]; /* endtime - msw */ + q[ 4] = t[6]; /* - lsw */ + q[ 5] = 0; /* level - msw */ + q[ 6] = t[0]; /* - lsw */ + q[ 7] = 0; /* lifetime - msw */ + q[ 8] = t[3]; /* - lsw */ + q[ 9] = 0; /* bytelife - msw */ + q[10] = t[4]; /* - lsw */ + q[11] = 0; /* enctype - msw */ + q[12] = htonl(enctype); /* - lsw */ + q[13] = htonl(klen); /* Key length */ + + q += 14; + + memcpy(q, ticket + sizeof(__be32) * 2, klen); + q += xdr_round_up(klen) / 4; + q[0] = ntohl(ticket_len); + q++; + if (WARN_ON((unsigned long)q != (unsigned long)ticket)) { + ret = -EIO; + goto error; + } + + /* Ticket read in with skb_copy_bits above */ + q += xdr_round_up(ticket_len) / 4; + if (WARN_ON((unsigned long)q - (unsigned long)payload != payload_len)) { + ret = -EIO; + goto error; + } + + /* Now turn that into a key. */ + key = key_alloc(&key_type_rxrpc, "x", + GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, 0, // TODO: Use socket owner + KEY_ALLOC_NOT_IN_QUOTA, NULL); + if (IS_ERR(key)) { + _leave(" = -ENOMEM [alloc %ld]", PTR_ERR(key)); + goto error; + } + + _debug("key %d", key_serial(key)); + + ret = key_instantiate_and_link(key, payload, payload_len, NULL, NULL); + if (ret < 0) + goto error_key; + + token = key->payload.data[0]; + token->no_leak_key = true; + *_key = key; + key = NULL; + ret = 0; + goto error; + +error_key: + key_put(key); +error: + kfree_sensitive(payload); +error_out: + _leave(" = %d", ret); + return ret; +} + +/* + * Extract the token and set up a session key from the details. + * + * struct RXGK_TokenContainer { + * afs_int32 kvno; + * afs_int32 enctype; + * opaque encrypted_token<>; + * }; + * + * [tools.ietf.org/html/draft-wilkinson-afs3-rxgk-afs-08 sec 6.1] + */ +int rxgk_extract_token(struct rxrpc_connection *conn, + struct sk_buff *skb, + unsigned int token_offset, unsigned int token_len, + struct key **_key, + u32 *_abort_code, const char **_eproto) +{ + const struct krb5_enctype *krb5; + const struct krb5_buffer *server_secret; + struct krb5_enc_keys token_enc = {}; + struct key *server_key; + unsigned int ticket_offset, ticket_len; + u32 kvno, enctype; + int ret; + + struct { + __be32 kvno; + __be32 enctype; + __be32 token_len; + } container; + + /* Decode the RXGK_TokenContainer object. This tells us which server + * key we should be using. We can then fetch the key, get the secret + * and set up the crypto to extract the token. + */ + if (skb_copy_bits(skb, token_offset, &container, sizeof(container)) < 0) + goto short_packet; + + kvno = ntohl(container.kvno); + enctype = ntohl(container.enctype); + ticket_len = ntohl(container.token_len); + ticket_offset = token_offset + sizeof(container); + + if (xdr_round_up(ticket_len) > token_len - 3 * 4) + goto short_packet; + + _debug("KVNO %u", kvno); + _debug("ENC %u", enctype); + _debug("TLEN %u", ticket_len); + + server_key = rxrpc_look_up_server_security(conn, skb, kvno, enctype); + if (IS_ERR(server_key)) + goto cant_get_server_key; + + down_read(&server_key->sem); + server_secret = (const void *)&server_key->payload.data[2]; + ret = rxgk_set_up_token_cipher(server_secret, &token_enc, enctype, &krb5, GFP_NOFS); + up_read(&server_key->sem); + key_put(server_key); + if (ret < 0) + goto cant_get_token; + + /* We can now decrypt and parse the token/ticket. This allows us to + * gain access to K0, from which we can derive the transport key and + * thence decode the authenticator. + */ + *_eproto = tracepoint_string("rxgk_rsp_dec_tkt"); + ret = rxgk_decrypt_skb(krb5, &token_enc, skb, + &ticket_offset, &ticket_len, _abort_code); + if (ret < 0) + return ret; + + ret = conn->security->default_decode_ticket(skb, ticket_offset, ticket_len, + _abort_code, _key); + if (ret < 0) + goto cant_get_token; + + _leave(" = 0"); + return ret; + +short_packet: + *_eproto = tracepoint_string("rxgk_rsp_short"); + *_abort_code = RXGK_PACKETSHORT; + return -EPROTO; + +cant_get_server_key: + ret = PTR_ERR(server_key); + switch (ret) { + case -ENOMEM: + goto temporary_error; + case -ENOKEY: + case -EKEYREJECTED: + case -EKEYEXPIRED: + case -EKEYREVOKED: + case -EPERM: + *_eproto = tracepoint_string("rxgk_rsp_nokey"); + *_abort_code = RXGK_BADKEYNO; + return -EKEYREJECTED; + default: + *_eproto = tracepoint_string("rxgk_rsp_keyerr"); + *_abort_code = RXGK_NOTAUTH; + return -EKEYREJECTED; + } + +cant_get_token: + switch (ret) { + case -ENOMEM: + goto temporary_error; + case -EINVAL: + *_eproto = tracepoint_string("rxgk_rsp_internal_error"); + *_abort_code = RXGK_NOTAUTH; + return -EKEYREJECTED; + case -ENOPKG: + *_eproto = tracepoint_string("rxgk_rsp_nopkg"); + *_abort_code = RXGK_BADETYPE; + return -EKEYREJECTED; + } + +temporary_error: + /* Ignore the response packet if we got a temporary error such as + * ENOMEM. We just want to send the challenge again. Note that we + * also come out this way if the ticket decryption fails. + */ + return ret; +} diff --git a/net/rxrpc/rxgk_common.h b/net/rxrpc/rxgk_common.h index 3047ad531877..38473b13e67d 100644 --- a/net/rxrpc/rxgk_common.h +++ b/net/rxrpc/rxgk_common.h @@ -33,6 +33,17 @@ struct rxgk_context { struct krb5_enc_keys resp_enc; /* Response packet enc key */ }; +#define xdr_round_up(x) (round_up((x), sizeof(__be32))) + +/* + * rxgk_app.c + */ +int rxgk_yfs_decode_ticket(struct sk_buff *, unsigned int, unsigned int, + u32 *, struct key **); +int rxgk_extract_token(struct rxrpc_connection *, + struct sk_buff *, unsigned int, unsigned int, + struct key **, u32 *, const char **); + /* * rxgk_kdf.c */ @@ -42,3 +53,110 @@ int rxgk_set_up_token_cipher(const struct krb5_buffer *, struct krb5_enc_keys *, unsigned int, const struct krb5_enctype **, gfp_t); void rxgk_put(struct rxgk_context *); + +/* + * Apply encryption and checksumming functions to part of an skbuff. + */ +static inline +int rxgk_encrypt_skb(const struct krb5_enctype *krb5, + struct krb5_enc_keys *keys, + struct sk_buff *skb, + u16 secure_offset, u16 secure_len, + u16 data_offset, u16 data_len, + bool preconfounded) +{ + struct scatterlist sg[16]; + int nr_sg; + + sg_init_table(sg, ARRAY_SIZE(sg)); + nr_sg = skb_to_sgvec(skb, sg, secure_offset, secure_len); + if (unlikely(nr_sg < 0)) + return nr_sg; + + data_offset -= secure_offset; + return crypto_krb5_encrypt(krb5, keys, sg, nr_sg, secure_len, + data_offset, data_len, preconfounded); +} + +/* + * Apply decryption and checksumming functions to part of an skbuff. The + * offset and length are updated to reflect the actual content of the encrypted + * region. + */ +static inline +int rxgk_decrypt_skb(const struct krb5_enctype *krb5, + struct krb5_enc_keys *keys, + struct sk_buff *skb, + unsigned int *_offset, unsigned int *_len, + int *_error_code) +{ + struct scatterlist sg[16]; + size_t offset = 0, len = *_len; + int nr_sg, ret; + + sg_init_table(sg, ARRAY_SIZE(sg)); + nr_sg = skb_to_sgvec(skb, sg, *_offset, len); + if (unlikely(nr_sg < 0)) + return nr_sg; + + ret = crypto_krb5_decrypt(krb5, keys, sg, nr_sg, + &offset, &len, _error_code); + + *_offset += offset; + *_len = len; + return ret; +} + +/* + * Generate a checksum over some metadata and part of an skbuff and insert the + * MIC into the skbuff immediately prior to the data. + */ +static inline +int rxgk_get_mic_skb(const struct krb5_enctype *krb5, + struct crypto_shash *shash, + const struct krb5_buffer *metadata, + struct sk_buff *skb, + u16 secure_offset, u16 secure_len, + u16 data_offset, u16 data_len) +{ + struct scatterlist sg[16]; + int nr_sg; + + sg_init_table(sg, ARRAY_SIZE(sg)); + nr_sg = skb_to_sgvec(skb, sg, secure_offset, secure_len); + if (unlikely(nr_sg < 0)) + return nr_sg; + + data_offset -= secure_offset; + return crypto_krb5_get_mic(krb5, shash, metadata, sg, nr_sg, secure_len, + data_offset, data_len); +} + +/* + * Check the MIC on a region of an skbuff. The offset and length are updated + * to reflect the actual content of the secure region. + */ +static inline +int rxgk_verify_mic_skb(const struct krb5_enctype *krb5, + struct crypto_shash *shash, + const struct krb5_buffer *metadata, + struct sk_buff *skb, + unsigned int *_offset, unsigned int *_len, + u32 *_error_code) +{ + struct scatterlist sg[16]; + size_t offset = 0, len = *_len; + int nr_sg, ret; + + sg_init_table(sg, ARRAY_SIZE(sg)); + nr_sg = skb_to_sgvec(skb, sg, *_offset, len); + if (unlikely(nr_sg < 0)) + return nr_sg; + + ret = crypto_krb5_verify_mic(krb5, shash, metadata, sg, nr_sg, + &offset, &len, _error_code); + + *_offset += offset; + *_len = len; + return 0; +} diff --git a/net/rxrpc/security.c b/net/rxrpc/security.c index 50cb5f1ee0c0..278a510b2956 100644 --- a/net/rxrpc/security.c +++ b/net/rxrpc/security.c @@ -20,6 +20,9 @@ static const struct rxrpc_security *rxrpc_security_types[] = { #ifdef CONFIG_RXKAD [RXRPC_SECURITY_RXKAD] = &rxkad, #endif +#ifdef CONFIG_RXGK + [RXRPC_SECURITY_YFS_RXGK] = &rxgk_yfs, +#endif }; int __init rxrpc_init_security(void) From patchwork Thu Nov 12 13:00:05 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Howells X-Patchwork-Id: 11900107 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.9 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 166DAC63697 for ; Thu, 12 Nov 2020 13:00:56 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id A743321D7F for ; Thu, 12 Nov 2020 13:00:55 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="WJNBx2fN" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728555AbgKLNAy (ORCPT ); Thu, 12 Nov 2020 08:00:54 -0500 Received: from us-smtp-delivery-124.mimecast.com ([63.128.21.124]:35407 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728264AbgKLNAt (ORCPT ); Thu, 12 Nov 2020 08:00:49 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1605186047; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=AenMsUHNek1p9GbSntFVDq8v3nHDim3WZP0WdDpuhRI=; b=WJNBx2fNyo5KOX7t74Nu1USJUrgTu4gIaZnIpYFcDu4/94pVaA7Q0MW6DWAxAMtx7I2ByB b3S7OkL0VegY5VLZ7mGGa3SQgQo7dkqcrL0XDYz/EdaixEeI9EEI7hnz4pURwjRvaJaOeE TfIzqGEB5Om2nt3RZ1Hkw25qH2VW+MU= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-322-jf8QS1iPOoejZOZYgMYiWQ-1; Thu, 12 Nov 2020 08:00:43 -0500 X-MC-Unique: jf8QS1iPOoejZOZYgMYiWQ-1 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 3ADC380A1BE; Thu, 12 Nov 2020 13:00:40 +0000 (UTC) Received: from warthog.procyon.org.uk (ovpn-115-47.rdu2.redhat.com [10.10.115.47]) by smtp.corp.redhat.com (Postfix) with ESMTP id 41EAF5D98F; Thu, 12 Nov 2020 13:00:06 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH 17/18] rxrpc: rxgk: Implement connection rekeying From: David Howells To: herbert@gondor.apana.org.au, bfields@fieldses.org Cc: dhowells@redhat.com, trond.myklebust@hammerspace.com, linux-crypto@vger.kernel.org, linux-afs@lists.infradead.org, linux-cifs@vger.kernel.org, linux-nfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 12 Nov 2020 13:00:05 +0000 Message-ID: <160518600537.2277919.5458566746685787619.stgit@warthog.procyon.org.uk> In-Reply-To: <160518586534.2277919.14475638653680231924.stgit@warthog.procyon.org.uk> References: <160518586534.2277919.14475638653680231924.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org Implement rekeying of connections with the RxGK security class. This involves regenerating the keys with a different key number as part of the input data after a certain amount of time or a certain amount of bytes encrypted. Rekeying may be triggered by either end. The LSW of the key number is inserted into the security-specific field in the RX header, and we try and expand it to 32-bits to make it last longer. Signed-off-by: David Howells --- net/rxrpc/ar-internal.h | 6 ++ net/rxrpc/conn_object.c | 2 + net/rxrpc/rxgk.c | 156 +++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 157 insertions(+), 7 deletions(-) diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index efdb3334ad88..3f2469714422 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -462,11 +462,15 @@ struct rxrpc_connection { u32 nonce; /* response re-use preventer */ } rxkad; struct { - struct rxgk_context *keys[1]; + struct rxgk_context *keys[4]; /* (Re-)keying buffer */ u64 start_time; /* The start time for TK derivation */ u8 nonce[20]; /* Response re-use preventer */ + u32 key_number; /* Current key number */ } rxgk; }; + rwlock_t security_lock; /* Lock allowing modification of security */ + struct mutex rekeying_lock; /* Lock allowing rekeying */ + unsigned long flags; unsigned long events; unsigned long idle_timestamp; /* Time at which last became idle */ diff --git a/net/rxrpc/conn_object.c b/net/rxrpc/conn_object.c index 8dd1ef25b98f..ff60526d0e0f 100644 --- a/net/rxrpc/conn_object.c +++ b/net/rxrpc/conn_object.c @@ -47,6 +47,8 @@ struct rxrpc_connection *rxrpc_alloc_connection(gfp_t gfp) INIT_LIST_HEAD(&conn->link); skb_queue_head_init(&conn->rx_queue); conn->security = &rxrpc_no_security; + rwlock_init(&conn->security_lock); + mutex_init(&conn->rekeying_lock); spin_lock_init(&conn->state_lock); conn->debug_id = atomic_inc_return(&rxrpc_debug_id); conn->size_align = 4; diff --git a/net/rxrpc/rxgk.c b/net/rxrpc/rxgk.c index 703e46e8b508..0aa6da93b8d4 100644 --- a/net/rxrpc/rxgk.c +++ b/net/rxrpc/rxgk.c @@ -90,11 +90,153 @@ static void rxgk_describe_server_key(const struct key *key, struct seq_file *m) seq_printf(m, ": %s", krb5->name); } +/* + * Handle rekeying the connection when the we see our limits overrun or when + * the far side decided to rekey. + * + * Returns a ref on the context if successful or -ESTALE if the key is out of + * date. + */ +static struct rxgk_context *rxgk_rekey(struct rxrpc_connection *conn, + const u16 *specific_key_number) +{ + struct rxgk_context *gk, *dead = NULL; + unsigned int key_number, current_key, mask = ARRAY_SIZE(conn->rxgk.keys) - 1; + bool crank = false; + + _enter("%d", specific_key_number ? *specific_key_number : -1); + + mutex_lock(&conn->rekeying_lock); + + current_key = conn->rxgk.key_number; + if (!specific_key_number) { + key_number = current_key; + } else { + if (*specific_key_number == (u16)current_key) + key_number = current_key; + else if (*specific_key_number == (u16)(current_key - 1)) + key_number = current_key - 1; + else if (*specific_key_number == (u16)(current_key + 1)) + goto crank_window; + else + goto bad_key; + } + + gk = conn->rxgk.keys[key_number & mask]; + if (!gk) + goto generate_key; + if (!specific_key_number && + test_bit(RXGK_TK_NEEDS_REKEY, &gk->flags)) + goto crank_window; + +grab: + refcount_inc(&gk->usage); + mutex_unlock(&conn->rekeying_lock); + rxgk_put(dead); + return gk; + +crank_window: + if (current_key == UINT_MAX) + goto bad_key; + if (current_key + 1 == UINT_MAX) + set_bit(RXRPC_CONN_DONT_REUSE, &conn->flags); + + key_number = current_key + 1; + if (WARN_ON(conn->rxgk.keys[key_number & mask])) + goto bad_key; + crank = true; + +generate_key: + gk = conn->rxgk.keys[current_key & mask]; + gk = rxgk_generate_transport_key(conn, gk->key, key_number, GFP_NOFS); + if (IS_ERR(gk)) { + mutex_unlock(&conn->rekeying_lock); + return gk; + } + + write_lock(&conn->security_lock); + if (crank) { + current_key++; + conn->rxgk.key_number = current_key; + dead = conn->rxgk.keys[(current_key - 2) & mask]; + conn->rxgk.keys[(current_key - 2) & mask] = NULL; + } + conn->rxgk.keys[current_key & mask] = gk; + write_unlock(&conn->security_lock); + goto grab; + +bad_key: + mutex_unlock(&conn->rekeying_lock); + return ERR_PTR(-ESTALE); +} + +/* + * Get the specified keying context. + * + * Returns a ref on the context if successful or -ESTALE if the key is out of + * date. + */ static struct rxgk_context *rxgk_get_key(struct rxrpc_connection *conn, - u16 *specific_key_number) + const u16 *specific_key_number) { - refcount_inc(&conn->rxgk.keys[0]->usage); - return conn->rxgk.keys[0]; + struct rxgk_context *gk; + unsigned int key_number, current_key, mask = ARRAY_SIZE(conn->rxgk.keys) - 1; + + _enter("{%u},%d", + conn->rxgk.key_number, specific_key_number ? *specific_key_number : -1); + + read_lock(&conn->security_lock); + + current_key = conn->rxgk.key_number; + if (!specific_key_number) { + key_number = current_key; + } else { + /* Only the bottom 16 bits of the key number are exposed in the + * header, so we try and keep the upper 16 bits in step. The + * whole 32 bits are used to generate the TK. + */ + if (*specific_key_number == (u16)current_key) + key_number = current_key; + else if (*specific_key_number == (u16)(current_key - 1)) + key_number = current_key - 1; + else if (*specific_key_number == (u16)(current_key + 1)) + goto rekey; + else + goto bad_key; + } + + gk = conn->rxgk.keys[key_number & mask]; + if (!gk) + goto slow_path; + if (!specific_key_number && + key_number < UINT_MAX) { + if (time_after(jiffies, gk->expiry) || + gk->bytes_remaining < 0) { + set_bit(RXGK_TK_NEEDS_REKEY, &gk->flags); + goto slow_path; + } + + if (test_bit(RXGK_TK_NEEDS_REKEY, &gk->flags)) + goto slow_path; + } + + refcount_inc(&gk->usage); + read_unlock(&conn->security_lock); + return gk; + +rekey: + _debug("rekey"); + if (current_key == UINT_MAX) + goto bad_key; + gk = conn->rxgk.keys[current_key & mask]; + if (gk) + set_bit(RXGK_TK_NEEDS_REKEY, &gk->flags); +slow_path: + read_unlock(&conn->security_lock); + return rxgk_rekey(conn, specific_key_number); +bad_key: + read_unlock(&conn->security_lock); + return ERR_PTR(-ESTALE); } /* @@ -106,7 +248,8 @@ static int rxgk_init_connection_security(struct rxrpc_connection *conn, struct rxgk_context *gk; int ret; - _enter("{%d},{%x}", conn->debug_id, key_serial(conn->params.key)); + _enter("{%d,%u},{%x}", + conn->debug_id, conn->rxgk.key_number, key_serial(conn->params.key)); conn->security_ix = token->security_index; conn->params.security_level = token->rxgk->level; @@ -116,10 +259,11 @@ static int rxgk_init_connection_security(struct rxrpc_connection *conn, do_div(conn->rxgk.start_time, 100); } - gk = rxgk_generate_transport_key(conn, token->rxgk, 0, GFP_NOFS); + gk = rxgk_generate_transport_key(conn, token->rxgk, conn->rxgk.key_number, + GFP_NOFS); if (IS_ERR(gk)) return PTR_ERR(gk); - conn->rxgk.keys[0] = gk; + conn->rxgk.keys[gk->key_number & 3] = gk; switch (conn->params.security_level) { case RXRPC_SECURITY_PLAIN: From patchwork Thu Nov 12 13:00:45 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Howells X-Patchwork-Id: 11900105 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS autolearn=no autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5C9B3C388F7 for ; Thu, 12 Nov 2020 13:01:11 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id E176D21D7F for ; Thu, 12 Nov 2020 13:01:10 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="TDzr6E43" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728576AbgKLNA7 (ORCPT ); Thu, 12 Nov 2020 08:00:59 -0500 Received: from us-smtp-delivery-124.mimecast.com ([216.205.24.124]:43127 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728559AbgKLNA5 (ORCPT ); Thu, 12 Nov 2020 08:00:57 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1605186054; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=qmTzZMw26ZOxVsw6AE+Yi1dOtYZgOHQMXHW1UsBHzzI=; b=TDzr6E43/xSW0ZUBO1S0QubSxxA2Cxg8dprElgAVXKrP0uHtzYBP5angP81M5eLvmNCtW8 fYIfew0Syf33bdomPcB7lgS6Mpakg3vqD8jNEVgI4Nb2rbNoJdkfyakXKKYIKSr1E8FSj3 w6ZeBghxuh2RWYR3TI2q6Tp7be/jxQw= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-61-0UbhHBIRPjeq6bbaj7-23A-1; Thu, 12 Nov 2020 08:00:51 -0500 X-MC-Unique: 0UbhHBIRPjeq6bbaj7-23A-1 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 7587811CC7EF; Thu, 12 Nov 2020 13:00:49 +0000 (UTC) Received: from warthog.procyon.org.uk (ovpn-115-47.rdu2.redhat.com [10.10.115.47]) by smtp.corp.redhat.com (Postfix) with ESMTP id BA9375B4DB; Thu, 12 Nov 2020 13:00:46 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH 18/18] rxgk: Support OpenAFS's rxgk implementation From: David Howells To: herbert@gondor.apana.org.au, bfields@fieldses.org Cc: dhowells@redhat.com, trond.myklebust@hammerspace.com, linux-crypto@vger.kernel.org, linux-afs@lists.infradead.org, linux-cifs@vger.kernel.org, linux-nfs@vger.kernel.org, linux-fsdevel@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 12 Nov 2020 13:00:45 +0000 Message-ID: <160518604546.2277919.883911770718886136.stgit@warthog.procyon.org.uk> In-Reply-To: <160518586534.2277919.14475638653680231924.stgit@warthog.procyon.org.uk> References: <160518586534.2277919.14475638653680231924.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org --- net/rxrpc/ar-internal.h | 1 net/rxrpc/key.c | 136 +++++++++++++++++++++++++++++++++++++++++++++++ net/rxrpc/rxgk.c | 25 +++++++++ net/rxrpc/rxgk_app.c | 135 +++++++++++++++++++++++++++++++++++++++++++++++ net/rxrpc/rxgk_common.h | 2 + net/rxrpc/security.c | 3 + 6 files changed, 302 insertions(+) diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 3f2469714422..ed44ceeeab68 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -1070,6 +1070,7 @@ void rxrpc_peer_init_rtt(struct rxrpc_peer *); /* * rxgk.c */ +extern const struct rxrpc_security rxgk_openafs; extern const struct rxrpc_security rxgk_yfs; /* diff --git a/net/rxrpc/key.c b/net/rxrpc/key.c index b7f154701d97..3479ef285980 100644 --- a/net/rxrpc/key.c +++ b/net/rxrpc/key.c @@ -147,6 +147,135 @@ static time64_t rxrpc_s64_to_time64(s64 time_in_100ns) return neg ? -tmp : tmp; } +/* + * Parse an OpenAFS RxGK type XDR format token + * - the caller guarantees we have at least 4 words + * + * struct token_rxgk { + * afs_int64 0 gk_viceid; + * afs_int32 2 gk_enctype; + * afs_int32 3 gk_level; + * afs_uint32 4 gk_lifetime; + * afs_uint32 5 gk_bytelife; + * afs_int64 6 gk_expiration; + * opaque 8 gk_token; + * opaque 9 gk_k0; + * }; + */ +static int rxrpc_preparse_xdr_rxgk(struct key_preparsed_payload *prep, + size_t datalen, + const __be32 *xdr, unsigned int toklen) +{ + struct rxrpc_key_token *token, **pptoken; + time64_t expiry; + size_t plen; + const __be32 *ticket, *key; + u32 tktlen, keylen; + + _enter(",{%x,%x,%x,%x},%x", + ntohl(xdr[0]), ntohl(xdr[1]), ntohl(xdr[2]), ntohl(xdr[3]), + toklen); + + if (toklen / 4 < 10) + goto reject; + + ticket = xdr + 9; + tktlen = ntohl(ticket[-1]); + _debug("tktlen: %x", tktlen); + tktlen = round_up(tktlen, 4); + if (toklen < 10 * 4 + tktlen) + goto reject; + + key = ticket + (tktlen / 4) + 1; + keylen = ntohl(key[-1]); + _debug("keylen: %x", keylen); + keylen = round_up(keylen, 4); + if (10 * 4 + tktlen + keylen != toklen) { + kleave(" = -EKEYREJECTED [%x!=%x, %x,%x]", + 10 * 4 + tktlen + keylen, toklen, tktlen, keylen); + goto reject; + } + + plen = sizeof(*token) + sizeof(*token->rxgk) + tktlen + keylen; + prep->quotalen = datalen + plen; + + plen -= sizeof(*token); + token = kzalloc(sizeof(*token), GFP_KERNEL); + if (!token) + goto nomem; + + token->rxgk = kzalloc(sizeof(struct rxgk_key) + keylen, GFP_KERNEL); + if (!token->rxgk) + goto nomem_token; + + token->security_index = RXRPC_SECURITY_RXGK; + token->rxgk->begintime = 0; + token->rxgk->endtime = xdr_dec64(xdr + 6); + token->rxgk->level = ntohl(xdr[3]); + if (token->rxgk->level > RXRPC_SECURITY_ENCRYPT) + goto reject_token; + token->rxgk->lifetime = ntohl(xdr[4]); + token->rxgk->bytelife = ntohl(xdr[5]); + token->rxgk->enctype = ntohl(xdr[2]); + token->rxgk->key.len = ntohl(key[-1]); + token->rxgk->key.data = token->rxgk->_key; + token->rxgk->ticket.len = ntohl(ticket[-1]); + + expiry = rxrpc_s64_to_time64(token->rxgk->endtime); + if (expiry < 0) + goto expired; + if (expiry < prep->expiry) + prep->expiry = expiry; + + memcpy(token->rxgk->key.data, key, token->rxgk->key.len); + + /* Pad the ticket so that we can use it directly in XDR */ + token->rxgk->ticket.data = kzalloc(round_up(token->rxgk->ticket.len, 4), + GFP_KERNEL); + if (!token->rxgk->ticket.data) + goto nomem_yrxgk; + memcpy(token->rxgk->ticket.data, ticket, token->rxgk->ticket.len); + + _debug("SCIX: %u", token->security_index); + _debug("LIFE: %llx", token->rxgk->lifetime); + _debug("BYTE: %llx", token->rxgk->bytelife); + _debug("ENC : %u", token->rxgk->enctype); + _debug("LEVL: %u", token->rxgk->level); + _debug("KLEN: %u", token->rxgk->key.len); + _debug("TLEN: %u", token->rxgk->ticket.len); + _debug("KEY0: %*phN", token->rxgk->key.len, token->rxgk->key.data); + _debug("TICK: %*phN", + min_t(u32, token->rxgk->ticket.len, 32), token->rxgk->ticket.data); + + /* count the number of tokens attached */ + prep->payload.data[1] = (void *)((unsigned long)prep->payload.data[1] + 1); + + /* attach the data */ + for (pptoken = (struct rxrpc_key_token **)&prep->payload.data[0]; + *pptoken; + pptoken = &(*pptoken)->next) + continue; + *pptoken = token; + + _leave(" = 0"); + return 0; + +nomem_yrxgk: + kfree(token->rxgk); +nomem_token: + kfree(token); +nomem: + return -ENOMEM; +reject_token: + kfree(token); +reject: + return -EKEYREJECTED; +expired: + kfree(token->rxgk); + kfree(token); + return -EKEYEXPIRED; +} + /* * Parse a YFS-RxGK type XDR format token * - the caller guarantees we have at least 4 words @@ -380,6 +509,9 @@ static int rxrpc_preparse_xdr(struct key_preparsed_payload *prep) case RXRPC_SECURITY_RXKAD: ret2 = rxrpc_preparse_xdr_rxkad(prep, datalen, token, toklen); break; + case RXRPC_SECURITY_RXGK: + ret2 = rxrpc_preparse_xdr_rxgk(prep, datalen, token, toklen); + break; case RXRPC_SECURITY_YFS_RXGK: ret2 = rxrpc_preparse_xdr_yfs_rxgk(prep, datalen, token, toklen); break; @@ -545,6 +677,7 @@ static void rxrpc_free_token_list(struct rxrpc_key_token *token) case RXRPC_SECURITY_RXKAD: kfree(token->kad); break; + case RXRPC_SECURITY_RXGK: case RXRPC_SECURITY_YFS_RXGK: kfree(token->rxgk->ticket.data); kfree(token->rxgk); @@ -592,6 +725,9 @@ static void rxrpc_describe(const struct key *key, struct seq_file *m) case RXRPC_SECURITY_RXKAD: seq_puts(m, "ka"); break; + case RXRPC_SECURITY_RXGK: + seq_puts(m, "ogk"); + break; case RXRPC_SECURITY_YFS_RXGK: seq_puts(m, "ygk"); break; diff --git a/net/rxrpc/rxgk.c b/net/rxrpc/rxgk.c index 0aa6da93b8d4..bad68d293ced 100644 --- a/net/rxrpc/rxgk.c +++ b/net/rxrpc/rxgk.c @@ -1181,6 +1181,31 @@ static void rxgk_exit(void) { } +/* + * RxRPC OpenAFS GSSAPI-based security + */ +const struct rxrpc_security rxgk_openafs = { + .name = "rxgk", + .security_index = RXRPC_SECURITY_RXGK, + .no_key_abort = RXGK_NOTAUTH, + .init = rxgk_init, + .exit = rxgk_exit, + .preparse_server_key = rxgk_preparse_server_key, + .free_preparse_server_key = rxgk_free_preparse_server_key, + .destroy_server_key = rxgk_destroy_server_key, + .describe_server_key = rxgk_describe_server_key, + .init_connection_security = rxgk_init_connection_security, + .secure_packet = rxgk_secure_packet, + .verify_packet = rxgk_verify_packet, + .free_call_crypto = rxgk_free_call_crypto, + .locate_data = rxgk_locate_data, + .issue_challenge = rxgk_issue_challenge, + .respond_to_challenge = rxgk_respond_to_challenge, + .verify_response = rxgk_verify_response, + .clear = rxgk_clear, + .default_decode_ticket = rxgk_openafs_decode_ticket, +}; + /* * RxRPC YFS GSSAPI-based security */ diff --git a/net/rxrpc/rxgk_app.c b/net/rxrpc/rxgk_app.c index 895879f3acfb..8c35e3a88119 100644 --- a/net/rxrpc/rxgk_app.c +++ b/net/rxrpc/rxgk_app.c @@ -14,6 +14,141 @@ #include "ar-internal.h" #include "rxgk_common.h" +/* + * Decode a default-style OpenAFS ticket in a response and turn it into an + * rxrpc-type key. + * + * struct RXGK_Token { + * afs_int32 enctype; + * opaque K0<>; + * RXGK_Level level; + * afs_int32 lifetime; + * afs_int32 bytelife; + * rxgkTime expirationtime; + * struct RXGK_PrAuthName identities<>; + * }; + */ +int rxgk_openafs_decode_ticket(struct sk_buff *skb, + unsigned int ticket_offset, unsigned int ticket_len, + u32 *_abort_code, + struct key **_key) +{ + struct rxrpc_key_token *token; + const struct cred *cred = current_cred(); // TODO - use socket creds + struct key *key; + size_t pre_ticket_len, payload_len; + unsigned int klen, enctype; + void *payload, *ticket; + __be32 *t, *p, *q, tmp[2]; + int ret; + + _enter(""); + + /* Get the session key length */ + ret = skb_copy_bits(skb, ticket_offset, tmp, sizeof(tmp)); + if (ret < 0) + goto error_out; + enctype = ntohl(tmp[0]); + klen = ntohl(tmp[1]); + + if (klen > ticket_len - 8 * sizeof(__be32)) { + *_abort_code = RXGK_INCONSISTENCY; + return -EPROTO; + } + + pre_ticket_len = ((5 + 10) * sizeof(__be32)); + payload_len = pre_ticket_len + xdr_round_up(ticket_len) + + sizeof(__be32) + xdr_round_up(klen); + + payload = kzalloc(payload_len, GFP_NOFS); + if (!payload) + return -ENOMEM; + + /* We need to fill out the XDR form for a key payload that we can pass + * to add_key(). Start by copying in the ticket so that we can parse + * it. + */ + ticket = payload + pre_ticket_len; + ret = skb_copy_bits(skb, ticket_offset, ticket, ticket_len); + if (ret < 0) + goto error; + + /* Fill out the form header. */ + p = payload; + p[0] = htonl(0); /* Flags */ + p[1] = htonl(1); /* len(cellname) */ + p[2] = htonl(0x20000000); /* Cellname " " */ + p[3] = htonl(1); /* #tokens */ + p[4] = htonl(11 * sizeof(__be32) + + xdr_round_up(klen) + xdr_round_up(ticket_len)); /* Token len */ + + /* Now fill in the body. Most of this we can just scrape directly from + * the ticket. + */ + t = ticket + sizeof(__be32) * 2 + xdr_round_up(klen); + q = payload + 5 * sizeof(__be32); + q[ 0] = htonl(RXRPC_SECURITY_RXGK); + q[ 1] = 0; /* gk_viceid - msw */ + q[ 2] = 0; /* - lsw */ + q[ 3] = htonl(enctype); /* gkenctype - msw */ + q[ 4] = t[0]; /* gk_level */ + q[ 5] = t[1]; /* gk_lifetime */ + q[ 6] = t[2]; /* gk_bytelife */ + q[ 7] = t[3]; /* gk_expiration - msw */ + q[ 8] = t[4]; /* - lsw */ + q[ 9] = htonl(ticket_len); /* gk_token.length */ + + q += 10; + if (WARN_ON((unsigned long)q != (unsigned long)ticket)) { + kdebug("%lx %lx", (long)q, (long)ticket); + ret = -EIO; + goto error; + } + + /* Ticket read in with skb_copy_bits above */ + q += xdr_round_up(ticket_len) / 4; + q[0] = ntohl(klen); + q++; + + memcpy(q, ticket + sizeof(__be32) * 2, klen); + + q += xdr_round_up(klen) / 4; + if (WARN_ON((unsigned long)q - (unsigned long)payload != payload_len)) { + ret = -EIO; + goto error; + } + + /* Now turn that into a key. */ + key = key_alloc(&key_type_rxrpc, "x", + GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, 0, // TODO: Use socket owner + KEY_ALLOC_NOT_IN_QUOTA, NULL); + if (IS_ERR(key)) { + _leave(" = -ENOMEM [alloc %ld]", PTR_ERR(key)); + goto error; + } + + _debug("key %d", key_serial(key)); + + ret = key_instantiate_and_link(key, payload, payload_len, NULL, NULL); + if (ret < 0) + goto error_key; + + token = key->payload.data[0]; + token->no_leak_key = true; + *_key = key; + key = NULL; + ret = 0; + goto error; + +error_key: + key_put(key); +error: + kfree_sensitive(payload); +error_out: + _leave(" = %d", ret); + return ret; +} + /* * Decode a default-style YFS ticket in a response and turn it into an * rxrpc-type key. diff --git a/net/rxrpc/rxgk_common.h b/net/rxrpc/rxgk_common.h index 38473b13e67d..88278da64c6a 100644 --- a/net/rxrpc/rxgk_common.h +++ b/net/rxrpc/rxgk_common.h @@ -38,6 +38,8 @@ struct rxgk_context { /* * rxgk_app.c */ +int rxgk_openafs_decode_ticket(struct sk_buff *, unsigned int, unsigned int, + u32 *, struct key **); int rxgk_yfs_decode_ticket(struct sk_buff *, unsigned int, unsigned int, u32 *, struct key **); int rxgk_extract_token(struct rxrpc_connection *, diff --git a/net/rxrpc/security.c b/net/rxrpc/security.c index 278a510b2956..dd11aa1aa137 100644 --- a/net/rxrpc/security.c +++ b/net/rxrpc/security.c @@ -20,6 +20,9 @@ static const struct rxrpc_security *rxrpc_security_types[] = { #ifdef CONFIG_RXKAD [RXRPC_SECURITY_RXKAD] = &rxkad, #endif +#ifdef CONFIG_RXGK + [RXRPC_SECURITY_RXGK] = &rxgk_openafs, +#endif #ifdef CONFIG_RXGK [RXRPC_SECURITY_YFS_RXGK] = &rxgk_yfs, #endif