key.c (3529B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* Key to pathname encoder 3 * 4 * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved. 5 * Written by David Howells (dhowells@redhat.com) 6 */ 7 8#include <linux/slab.h> 9#include "internal.h" 10 11static const char cachefiles_charmap[64] = 12 "0123456789" /* 0 - 9 */ 13 "abcdefghijklmnopqrstuvwxyz" /* 10 - 35 */ 14 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" /* 36 - 61 */ 15 "_-" /* 62 - 63 */ 16 ; 17 18static const char cachefiles_filecharmap[256] = { 19 /* we skip space and tab and control chars */ 20 [33 ... 46] = 1, /* '!' -> '.' */ 21 /* we skip '/' as it's significant to pathwalk */ 22 [48 ... 127] = 1, /* '0' -> '~' */ 23}; 24 25static inline unsigned int how_many_hex_digits(unsigned int x) 26{ 27 return x ? round_up(ilog2(x) + 1, 4) / 4 : 0; 28} 29 30/* 31 * turn the raw key into something cooked 32 * - the key may be up to NAME_MAX in length (including the length word) 33 * - "base64" encode the strange keys, mapping 3 bytes of raw to four of 34 * cooked 35 * - need to cut the cooked key into 252 char lengths (189 raw bytes) 36 */ 37bool cachefiles_cook_key(struct cachefiles_object *object) 38{ 39 const u8 *key = fscache_get_key(object->cookie), *kend; 40 unsigned char ch; 41 unsigned int acc, i, n, nle, nbe, keylen = object->cookie->key_len; 42 unsigned int b64len, len, print, pad; 43 char *name, sep; 44 45 _enter(",%u,%*phN", keylen, keylen, key); 46 47 BUG_ON(keylen > NAME_MAX - 3); 48 49 print = 1; 50 for (i = 0; i < keylen; i++) { 51 ch = key[i]; 52 print &= cachefiles_filecharmap[ch]; 53 } 54 55 /* If the path is usable ASCII, then we render it directly */ 56 if (print) { 57 len = 1 + keylen; 58 name = kmalloc(len + 1, GFP_KERNEL); 59 if (!name) 60 return false; 61 62 name[0] = 'D'; /* Data object type, string encoding */ 63 memcpy(name + 1, key, keylen); 64 goto success; 65 } 66 67 /* See if it makes sense to encode it as "hex,hex,hex" for each 32-bit 68 * chunk. We rely on the key having been padded out to a whole number 69 * of 32-bit words. 70 */ 71 n = round_up(keylen, 4); 72 nbe = nle = 0; 73 for (i = 0; i < n; i += 4) { 74 u32 be = be32_to_cpu(*(__be32 *)(key + i)); 75 u32 le = le32_to_cpu(*(__le32 *)(key + i)); 76 77 nbe += 1 + how_many_hex_digits(be); 78 nle += 1 + how_many_hex_digits(le); 79 } 80 81 b64len = DIV_ROUND_UP(keylen, 3); 82 pad = b64len * 3 - keylen; 83 b64len = 2 + b64len * 4; /* Length if we base64-encode it */ 84 _debug("len=%u nbe=%u nle=%u b64=%u", keylen, nbe, nle, b64len); 85 if (nbe < b64len || nle < b64len) { 86 unsigned int nlen = min(nbe, nle) + 1; 87 name = kmalloc(nlen, GFP_KERNEL); 88 if (!name) 89 return false; 90 sep = (nbe <= nle) ? 'S' : 'T'; /* Encoding indicator */ 91 len = 0; 92 for (i = 0; i < n; i += 4) { 93 u32 x; 94 if (nbe <= nle) 95 x = be32_to_cpu(*(__be32 *)(key + i)); 96 else 97 x = le32_to_cpu(*(__le32 *)(key + i)); 98 name[len++] = sep; 99 if (x != 0) 100 len += snprintf(name + len, nlen - len, "%x", x); 101 sep = ','; 102 } 103 goto success; 104 } 105 106 /* We need to base64-encode it */ 107 name = kmalloc(b64len + 1, GFP_KERNEL); 108 if (!name) 109 return false; 110 111 name[0] = 'E'; 112 name[1] = '0' + pad; 113 len = 2; 114 kend = key + keylen; 115 do { 116 acc = *key++; 117 if (key < kend) { 118 acc |= *key++ << 8; 119 if (key < kend) 120 acc |= *key++ << 16; 121 } 122 123 name[len++] = cachefiles_charmap[acc & 63]; 124 acc >>= 6; 125 name[len++] = cachefiles_charmap[acc & 63]; 126 acc >>= 6; 127 name[len++] = cachefiles_charmap[acc & 63]; 128 acc >>= 6; 129 name[len++] = cachefiles_charmap[acc & 63]; 130 } while (key < kend); 131 132success: 133 name[len] = 0; 134 object->d_name = name; 135 object->d_name_len = len; 136 _leave(" = %s", object->d_name); 137 return true; 138}