Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

KEYS: Fix the size of the key description passed to/from userspace

When a key description argument is imported into the kernel from userspace, as
happens in add_key(), request_key(), KEYCTL_JOIN_SESSION_KEYRING,
KEYCTL_SEARCH, the description is copied into a buffer up to PAGE_SIZE in size.
PAGE_SIZE, however, is a variable quantity, depending on the arch. Fix this at
4096 instead (ie. 4095 plus a NUL termination) and define a constant
(KEY_MAX_DESC_SIZE) to this end.

When reading the description back with KEYCTL_DESCRIBE, a PAGE_SIZE internal
buffer is allocated into which the information and description will be
rendered. This means that the description will get truncated if an extremely
long description it has to be crammed into the buffer with the stringified
information. There is no particular need to copy the description into the
buffer, so just copy it directly to userspace in a separate operation.

Reported-by: Christian Kastner <debian@kvr.at>
Signed-off-by: David Howells <dhowells@redhat.com>
Tested-by: Christian Kastner <debian@kvr.at>

+26 -30
+26 -30
security/keys/keyctl.c
··· 26 26 #include <asm/uaccess.h> 27 27 #include "internal.h" 28 28 29 + #define KEY_MAX_DESC_SIZE 4096 30 + 29 31 static int key_get_type_from_user(char *type, 30 32 const char __user *_type, 31 33 unsigned len) ··· 80 78 81 79 description = NULL; 82 80 if (_description) { 83 - description = strndup_user(_description, PAGE_SIZE); 81 + description = strndup_user(_description, KEY_MAX_DESC_SIZE); 84 82 if (IS_ERR(description)) { 85 83 ret = PTR_ERR(description); 86 84 goto error; ··· 179 177 goto error; 180 178 181 179 /* pull the description into kernel space */ 182 - description = strndup_user(_description, PAGE_SIZE); 180 + description = strndup_user(_description, KEY_MAX_DESC_SIZE); 183 181 if (IS_ERR(description)) { 184 182 ret = PTR_ERR(description); 185 183 goto error; ··· 289 287 /* fetch the name from userspace */ 290 288 name = NULL; 291 289 if (_name) { 292 - name = strndup_user(_name, PAGE_SIZE); 290 + name = strndup_user(_name, KEY_MAX_DESC_SIZE); 293 291 if (IS_ERR(name)) { 294 292 ret = PTR_ERR(name); 295 293 goto error; ··· 564 562 { 565 563 struct key *key, *instkey; 566 564 key_ref_t key_ref; 567 - char *tmpbuf; 565 + char *infobuf; 568 566 long ret; 567 + int desclen, infolen; 569 568 570 569 key_ref = lookup_user_key(keyid, KEY_LOOKUP_PARTIAL, KEY_NEED_VIEW); 571 570 if (IS_ERR(key_ref)) { ··· 589 586 } 590 587 591 588 okay: 592 - /* calculate how much description we're going to return */ 593 - ret = -ENOMEM; 594 - tmpbuf = kmalloc(PAGE_SIZE, GFP_KERNEL); 595 - if (!tmpbuf) 596 - goto error2; 597 - 598 589 key = key_ref_to_ptr(key_ref); 590 + desclen = strlen(key->description); 599 591 600 - ret = snprintf(tmpbuf, PAGE_SIZE - 1, 601 - "%s;%d;%d;%08x;%s", 602 - key->type->name, 603 - from_kuid_munged(current_user_ns(), key->uid), 604 - from_kgid_munged(current_user_ns(), key->gid), 605 - key->perm, 606 - key->description ?: ""); 607 - 608 - /* include a NUL char at the end of the data */ 609 - if (ret > PAGE_SIZE - 1) 610 - ret = PAGE_SIZE - 1; 611 - tmpbuf[ret] = 0; 612 - ret++; 592 + /* calculate how much information we're going to return */ 593 + ret = -ENOMEM; 594 + infobuf = kasprintf(GFP_KERNEL, 595 + "%s;%d;%d;%08x;", 596 + key->type->name, 597 + from_kuid_munged(current_user_ns(), key->uid), 598 + from_kgid_munged(current_user_ns(), key->gid), 599 + key->perm); 600 + if (!infobuf) 601 + goto error2; 602 + infolen = strlen(infobuf); 603 + ret = infolen + desclen + 1; 613 604 614 605 /* consider returning the data */ 615 - if (buffer && buflen > 0) { 616 - if (buflen > ret) 617 - buflen = ret; 618 - 619 - if (copy_to_user(buffer, tmpbuf, buflen) != 0) 606 + if (buffer && buflen >= ret) { 607 + if (copy_to_user(buffer, infobuf, infolen) != 0 || 608 + copy_to_user(buffer + infolen, key->description, 609 + desclen + 1) != 0) 620 610 ret = -EFAULT; 621 611 } 622 612 623 - kfree(tmpbuf); 613 + kfree(infobuf); 624 614 error2: 625 615 key_ref_put(key_ref); 626 616 error: ··· 645 649 if (ret < 0) 646 650 goto error; 647 651 648 - description = strndup_user(_description, PAGE_SIZE); 652 + description = strndup_user(_description, KEY_MAX_DESC_SIZE); 649 653 if (IS_ERR(description)) { 650 654 ret = PTR_ERR(description); 651 655 goto error;