162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * COPYRIGHT (c) 2008 362306a36Sopenharmony_ci * The Regents of the University of Michigan 462306a36Sopenharmony_ci * ALL RIGHTS RESERVED 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Permission is granted to use, copy, create derivative works 762306a36Sopenharmony_ci * and redistribute this software and such derivative works 862306a36Sopenharmony_ci * for any purpose, so long as the name of The University of 962306a36Sopenharmony_ci * Michigan is not used in any advertising or publicity 1062306a36Sopenharmony_ci * pertaining to the use of distribution of this software 1162306a36Sopenharmony_ci * without specific, written prior authorization. If the 1262306a36Sopenharmony_ci * above copyright notice or any other identification of the 1362306a36Sopenharmony_ci * University of Michigan is included in any copy of any 1462306a36Sopenharmony_ci * portion of this software, then the disclaimer below must 1562306a36Sopenharmony_ci * also be included. 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION 1862306a36Sopenharmony_ci * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY 1962306a36Sopenharmony_ci * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF 2062306a36Sopenharmony_ci * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING 2162306a36Sopenharmony_ci * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF 2262306a36Sopenharmony_ci * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE 2362306a36Sopenharmony_ci * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE 2462306a36Sopenharmony_ci * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR 2562306a36Sopenharmony_ci * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING 2662306a36Sopenharmony_ci * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN 2762306a36Sopenharmony_ci * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF 2862306a36Sopenharmony_ci * SUCH DAMAGES. 2962306a36Sopenharmony_ci */ 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#include <crypto/skcipher.h> 3262306a36Sopenharmony_ci#include <linux/types.h> 3362306a36Sopenharmony_ci#include <linux/jiffies.h> 3462306a36Sopenharmony_ci#include <linux/sunrpc/gss_krb5.h> 3562306a36Sopenharmony_ci#include <linux/pagemap.h> 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#include "gss_krb5_internal.h" 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) 4062306a36Sopenharmony_ci# define RPCDBG_FACILITY RPCDBG_AUTH 4162306a36Sopenharmony_ci#endif 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* 4462306a36Sopenharmony_ci * We can shift data by up to LOCAL_BUF_LEN bytes in a pass. If we need 4562306a36Sopenharmony_ci * to do more than that, we shift repeatedly. Kevin Coffman reports 4662306a36Sopenharmony_ci * seeing 28 bytes as the value used by Microsoft clients and servers 4762306a36Sopenharmony_ci * with AES, so this constant is chosen to allow handling 28 in one pass 4862306a36Sopenharmony_ci * without using too much stack space. 4962306a36Sopenharmony_ci * 5062306a36Sopenharmony_ci * If that proves to a problem perhaps we could use a more clever 5162306a36Sopenharmony_ci * algorithm. 5262306a36Sopenharmony_ci */ 5362306a36Sopenharmony_ci#define LOCAL_BUF_LEN 32u 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic void rotate_buf_a_little(struct xdr_buf *buf, unsigned int shift) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci char head[LOCAL_BUF_LEN]; 5862306a36Sopenharmony_ci char tmp[LOCAL_BUF_LEN]; 5962306a36Sopenharmony_ci unsigned int this_len, i; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci BUG_ON(shift > LOCAL_BUF_LEN); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci read_bytes_from_xdr_buf(buf, 0, head, shift); 6462306a36Sopenharmony_ci for (i = 0; i + shift < buf->len; i += LOCAL_BUF_LEN) { 6562306a36Sopenharmony_ci this_len = min(LOCAL_BUF_LEN, buf->len - (i + shift)); 6662306a36Sopenharmony_ci read_bytes_from_xdr_buf(buf, i+shift, tmp, this_len); 6762306a36Sopenharmony_ci write_bytes_to_xdr_buf(buf, i, tmp, this_len); 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci write_bytes_to_xdr_buf(buf, buf->len - shift, head, shift); 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic void _rotate_left(struct xdr_buf *buf, unsigned int shift) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci int shifted = 0; 7562306a36Sopenharmony_ci int this_shift; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci shift %= buf->len; 7862306a36Sopenharmony_ci while (shifted < shift) { 7962306a36Sopenharmony_ci this_shift = min(shift - shifted, LOCAL_BUF_LEN); 8062306a36Sopenharmony_ci rotate_buf_a_little(buf, this_shift); 8162306a36Sopenharmony_ci shifted += this_shift; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic void rotate_left(u32 base, struct xdr_buf *buf, unsigned int shift) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci struct xdr_buf subbuf; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci xdr_buf_subsegment(buf, &subbuf, base, buf->len - base); 9062306a36Sopenharmony_ci _rotate_left(&subbuf, shift); 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ciu32 9462306a36Sopenharmony_cigss_krb5_wrap_v2(struct krb5_ctx *kctx, int offset, 9562306a36Sopenharmony_ci struct xdr_buf *buf, struct page **pages) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci u8 *ptr; 9862306a36Sopenharmony_ci time64_t now; 9962306a36Sopenharmony_ci u8 flags = 0x00; 10062306a36Sopenharmony_ci __be16 *be16ptr; 10162306a36Sopenharmony_ci __be64 *be64ptr; 10262306a36Sopenharmony_ci u32 err; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci dprintk("RPC: %s\n", __func__); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci /* make room for gss token header */ 10762306a36Sopenharmony_ci if (xdr_extend_head(buf, offset, GSS_KRB5_TOK_HDR_LEN)) 10862306a36Sopenharmony_ci return GSS_S_FAILURE; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci /* construct gss token header */ 11162306a36Sopenharmony_ci ptr = buf->head[0].iov_base + offset; 11262306a36Sopenharmony_ci *ptr++ = (unsigned char) ((KG2_TOK_WRAP>>8) & 0xff); 11362306a36Sopenharmony_ci *ptr++ = (unsigned char) (KG2_TOK_WRAP & 0xff); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci if ((kctx->flags & KRB5_CTX_FLAG_INITIATOR) == 0) 11662306a36Sopenharmony_ci flags |= KG2_TOKEN_FLAG_SENTBYACCEPTOR; 11762306a36Sopenharmony_ci if ((kctx->flags & KRB5_CTX_FLAG_ACCEPTOR_SUBKEY) != 0) 11862306a36Sopenharmony_ci flags |= KG2_TOKEN_FLAG_ACCEPTORSUBKEY; 11962306a36Sopenharmony_ci /* We always do confidentiality in wrap tokens */ 12062306a36Sopenharmony_ci flags |= KG2_TOKEN_FLAG_SEALED; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci *ptr++ = flags; 12362306a36Sopenharmony_ci *ptr++ = 0xff; 12462306a36Sopenharmony_ci be16ptr = (__be16 *)ptr; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci *be16ptr++ = 0; 12762306a36Sopenharmony_ci /* "inner" token header always uses 0 for RRC */ 12862306a36Sopenharmony_ci *be16ptr++ = 0; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci be64ptr = (__be64 *)be16ptr; 13162306a36Sopenharmony_ci *be64ptr = cpu_to_be64(atomic64_fetch_inc(&kctx->seq_send64)); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci err = (*kctx->gk5e->encrypt)(kctx, offset, buf, pages); 13462306a36Sopenharmony_ci if (err) 13562306a36Sopenharmony_ci return err; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci now = ktime_get_real_seconds(); 13862306a36Sopenharmony_ci return (kctx->endtime < now) ? GSS_S_CONTEXT_EXPIRED : GSS_S_COMPLETE; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ciu32 14262306a36Sopenharmony_cigss_krb5_unwrap_v2(struct krb5_ctx *kctx, int offset, int len, 14362306a36Sopenharmony_ci struct xdr_buf *buf, unsigned int *slack, 14462306a36Sopenharmony_ci unsigned int *align) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci time64_t now; 14762306a36Sopenharmony_ci u8 *ptr; 14862306a36Sopenharmony_ci u8 flags = 0x00; 14962306a36Sopenharmony_ci u16 ec, rrc; 15062306a36Sopenharmony_ci int err; 15162306a36Sopenharmony_ci u32 headskip, tailskip; 15262306a36Sopenharmony_ci u8 decrypted_hdr[GSS_KRB5_TOK_HDR_LEN]; 15362306a36Sopenharmony_ci unsigned int movelen; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci dprintk("RPC: %s\n", __func__); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci ptr = buf->head[0].iov_base + offset; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci if (be16_to_cpu(*((__be16 *)ptr)) != KG2_TOK_WRAP) 16162306a36Sopenharmony_ci return GSS_S_DEFECTIVE_TOKEN; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci flags = ptr[2]; 16462306a36Sopenharmony_ci if ((!kctx->initiate && (flags & KG2_TOKEN_FLAG_SENTBYACCEPTOR)) || 16562306a36Sopenharmony_ci (kctx->initiate && !(flags & KG2_TOKEN_FLAG_SENTBYACCEPTOR))) 16662306a36Sopenharmony_ci return GSS_S_BAD_SIG; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if ((flags & KG2_TOKEN_FLAG_SEALED) == 0) { 16962306a36Sopenharmony_ci dprintk("%s: token missing expected sealed flag\n", __func__); 17062306a36Sopenharmony_ci return GSS_S_DEFECTIVE_TOKEN; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (ptr[3] != 0xff) 17462306a36Sopenharmony_ci return GSS_S_DEFECTIVE_TOKEN; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci ec = be16_to_cpup((__be16 *)(ptr + 4)); 17762306a36Sopenharmony_ci rrc = be16_to_cpup((__be16 *)(ptr + 6)); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci /* 18062306a36Sopenharmony_ci * NOTE: the sequence number at ptr + 8 is skipped, rpcsec_gss 18162306a36Sopenharmony_ci * doesn't want it checked; see page 6 of rfc 2203. 18262306a36Sopenharmony_ci */ 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if (rrc != 0) 18562306a36Sopenharmony_ci rotate_left(offset + 16, buf, rrc); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci err = (*kctx->gk5e->decrypt)(kctx, offset, len, buf, 18862306a36Sopenharmony_ci &headskip, &tailskip); 18962306a36Sopenharmony_ci if (err) 19062306a36Sopenharmony_ci return GSS_S_FAILURE; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci /* 19362306a36Sopenharmony_ci * Retrieve the decrypted gss token header and verify 19462306a36Sopenharmony_ci * it against the original 19562306a36Sopenharmony_ci */ 19662306a36Sopenharmony_ci err = read_bytes_from_xdr_buf(buf, 19762306a36Sopenharmony_ci len - GSS_KRB5_TOK_HDR_LEN - tailskip, 19862306a36Sopenharmony_ci decrypted_hdr, GSS_KRB5_TOK_HDR_LEN); 19962306a36Sopenharmony_ci if (err) { 20062306a36Sopenharmony_ci dprintk("%s: error %u getting decrypted_hdr\n", __func__, err); 20162306a36Sopenharmony_ci return GSS_S_FAILURE; 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci if (memcmp(ptr, decrypted_hdr, 6) 20462306a36Sopenharmony_ci || memcmp(ptr + 8, decrypted_hdr + 8, 8)) { 20562306a36Sopenharmony_ci dprintk("%s: token hdr, plaintext hdr mismatch!\n", __func__); 20662306a36Sopenharmony_ci return GSS_S_FAILURE; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci /* do sequencing checks */ 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci /* it got through unscathed. Make sure the context is unexpired */ 21262306a36Sopenharmony_ci now = ktime_get_real_seconds(); 21362306a36Sopenharmony_ci if (now > kctx->endtime) 21462306a36Sopenharmony_ci return GSS_S_CONTEXT_EXPIRED; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci /* 21762306a36Sopenharmony_ci * Move the head data back to the right position in xdr_buf. 21862306a36Sopenharmony_ci * We ignore any "ec" data since it might be in the head or 21962306a36Sopenharmony_ci * the tail, and we really don't need to deal with it. 22062306a36Sopenharmony_ci * Note that buf->head[0].iov_len may indicate the available 22162306a36Sopenharmony_ci * head buffer space rather than that actually occupied. 22262306a36Sopenharmony_ci */ 22362306a36Sopenharmony_ci movelen = min_t(unsigned int, buf->head[0].iov_len, len); 22462306a36Sopenharmony_ci movelen -= offset + GSS_KRB5_TOK_HDR_LEN + headskip; 22562306a36Sopenharmony_ci BUG_ON(offset + GSS_KRB5_TOK_HDR_LEN + headskip + movelen > 22662306a36Sopenharmony_ci buf->head[0].iov_len); 22762306a36Sopenharmony_ci memmove(ptr, ptr + GSS_KRB5_TOK_HDR_LEN + headskip, movelen); 22862306a36Sopenharmony_ci buf->head[0].iov_len -= GSS_KRB5_TOK_HDR_LEN + headskip; 22962306a36Sopenharmony_ci buf->len = len - (GSS_KRB5_TOK_HDR_LEN + headskip); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci /* Trim off the trailing "extra count" and checksum blob */ 23262306a36Sopenharmony_ci xdr_buf_trim(buf, ec + GSS_KRB5_TOK_HDR_LEN + tailskip); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci *align = XDR_QUADLEN(GSS_KRB5_TOK_HDR_LEN + headskip); 23562306a36Sopenharmony_ci *slack = *align + XDR_QUADLEN(ec + GSS_KRB5_TOK_HDR_LEN + tailskip); 23662306a36Sopenharmony_ci return GSS_S_COMPLETE; 23762306a36Sopenharmony_ci} 238