Skip to content

Commit 839be8a

Browse files
simo5frozencemetery
authored andcommitted
Change the way we handle encrypted buffers
The previous change has backwards incompatible behavior that may also lead to buffer overruns. Because we have no easy way to indicate a format change and to maintain backwards compatibility for the ciphers that were working (those that added padding were hopelessly borken anyway) introduce code to simply add padding that we can recognize and remove when we read back the token. On ciphers that do not add padding this is basically a no op and the tokens will be identical to the ones we previously emitted. On ciphers that add padding we pad the plaintext so that we hit a block boundary and cause no extra padding to be added by krb5_c_encrypt itself. On decryption we check if padding bytes are appended to the buffer and remove them. Signed-off-by: Simo Sorce <[email protected]> Reviewed-by: Robbie Harwood <[email protected]> Merges: #246
1 parent 736fdd4 commit 839be8a

File tree

1 file changed

+86
-24
lines changed

1 file changed

+86
-24
lines changed

src/gp_export.c

Lines changed: 86 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -193,47 +193,98 @@ uint32_t gp_init_creds_handle(uint32_t *min, const char *svc_name,
193193
return ret_maj;
194194
}
195195

196-
/* We need to include a length in our payloads because krb5_c_decrypt() will
197-
* pad the contents for some enctypes, and gss_import_cred() doesn't like
198-
* having extra bytes on tokens. */
196+
#define ENC_MIN_PAD_LEN 8
197+
198+
/* We need to pad our payloads because krb5_c_decrypt() may pad the
199+
* contents for some enctypes, and gss_import_cred() doesn't like
200+
* having extra bytes on tokens.
201+
* Explicit padding and depadding is used in order to maintain backwards
202+
* compatibility over upgrades (and downgrades), it would have been
203+
* better if we simply had a better formatting of the returned blob
204+
* so we could simply change a "blob version" number */
199205
static int gp_encrypt_buffer(krb5_context context, krb5_keyblock *key,
200206
size_t len, void *buf, octet_string *out)
201207
{
202208
int ret;
203209
krb5_data data_in;
204210
krb5_enc_data enc_handle;
205211
size_t cipherlen;
206-
char *packed = NULL;
207-
uint32_t netlen;
212+
size_t padcheck;
213+
uint8_t pad = 0;
214+
char *padded = NULL;
208215

209216
if (len > (uint32_t)(-1)) {
210217
/* Needs to fit in 4 bytes of payload, so... */
211218
ret = ENOMEM;
212219
goto done;
213220
}
214221

215-
packed = malloc(len);
216-
if (!packed) {
217-
ret = errno;
222+
ret = krb5_c_encrypt_length(context,
223+
key->enctype,
224+
len, &cipherlen);
225+
if (ret) {
218226
goto done;
219227
}
220228

221-
netlen = htonl(len);
222-
memcpy(packed, (uint8_t *)&netlen, 4);
223-
memcpy(packed + 4, buf, len);
224-
225-
data_in.length = len + 4;
226-
data_in.data = packed;
227-
228-
memset(&enc_handle, '\0', sizeof(krb5_enc_data));
229-
229+
/* try again with len + 1 to see if padding is required */
230230
ret = krb5_c_encrypt_length(context,
231231
key->enctype,
232-
data_in.length,
233-
&cipherlen);
232+
len + 1, &padcheck);
234233
if (ret) {
235234
goto done;
236235
}
236+
if (padcheck == cipherlen) {
237+
int i;
238+
/* padding required */
239+
pad = ENC_MIN_PAD_LEN;
240+
/* always add enough padding that it makes it extremely unlikley
241+
* legitimate plaintext will be incorrectly depadded in the
242+
* decrypt function */
243+
ret = krb5_c_encrypt_length(context,
244+
key->enctype,
245+
len + pad, &cipherlen);
246+
if (ret) {
247+
goto done;
248+
}
249+
/* we support only block sizes up to 16 bytes as this is the largest
250+
* supported block size in krb ciphers for now */
251+
for (i = 0; i < 15; i++) {
252+
/* find the point at which padcheck increases, that's when we
253+
* cross a blocksize boundary internally and we can calculate
254+
* the padding that will be used */
255+
ret = krb5_c_encrypt_length(context,
256+
key->enctype,
257+
len + pad + i + 1, &padcheck);
258+
if (ret) {
259+
goto done;
260+
}
261+
if (padcheck > cipherlen) {
262+
pad += i;
263+
break;
264+
}
265+
}
266+
if (i > 15) {
267+
ret = EINVAL;
268+
goto done;
269+
}
270+
}
271+
272+
if (pad != 0) {
273+
padded = malloc(len + pad);
274+
if (!padded) {
275+
ret = errno;
276+
goto done;
277+
}
278+
279+
memcpy(padded, buf, len);
280+
memset(padded + len, pad, pad);
281+
282+
data_in.length = len + pad;
283+
data_in.data = padded;
284+
} else {
285+
data_in.length = len;
286+
data_in.data = buf;
287+
}
237288

238289
enc_handle.ciphertext.length = cipherlen;
239290
enc_handle.ciphertext.data = malloc(enc_handle.ciphertext.length);
@@ -261,7 +312,7 @@ static int gp_encrypt_buffer(krb5_context context, krb5_keyblock *key,
261312
}
262313

263314
done:
264-
free(packed);
315+
free(padded);
265316
free(enc_handle.ciphertext.data);
266317
return ret;
267318
}
@@ -273,7 +324,8 @@ static int gp_decrypt_buffer(krb5_context context, krb5_keyblock *key,
273324
int ret;
274325
krb5_data data_out;
275326
krb5_enc_data enc_handle;
276-
uint32_t netlen;
327+
uint8_t pad;
328+
int i, j;
277329

278330
memset(&enc_handle, '\0', sizeof(krb5_enc_data));
279331

@@ -295,9 +347,19 @@ static int gp_decrypt_buffer(krb5_context context, krb5_keyblock *key,
295347
}
296348

297349
/* And handle the padding. */
298-
memcpy(&netlen, buf, 4);
299-
*len = ntohl(netlen);
300-
memmove(buf, buf + 4, *len);
350+
i = data_out.length - 1;
351+
pad = data_out.data[i];
352+
if (pad >= ENC_MIN_PAD_LEN && pad < i) {
353+
j = pad;
354+
while (j > 0) {
355+
j--;
356+
if (pad != data_out.data[i - j]) break;
357+
}
358+
if (j == 0) {
359+
data_out.length -= pad;
360+
}
361+
}
362+
*len = data_out.length;
301363

302364
return 0;
303365
}

0 commit comments

Comments
 (0)