Created
January 27, 2023 11:05
-
-
Save eqvinox/6b3f801f88885ec961d8a308c1f6e38d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* compile: gcc -o creds creds.c -lcap */ | |
| #define _GNU_SOURCE 1 | |
| #include <stdarg.h> | |
| #include <stdbool.h> | |
| #include <stdlib.h> | |
| #include <stdio.h> | |
| #include <string.h> | |
| #include <unistd.h> | |
| #include <pthread.h> | |
| #include <sys/socket.h> | |
| #include <sys/types.h> | |
| #include <sys/syscall.h> | |
| #include <sys/capability.h> | |
| #include <sys/fsuid.h> | |
| #include <sys/prctl.h> | |
| #include <linux/securebits.h> | |
| #if 0 | |
| static pid_t gettid(void) | |
| { | |
| return (pid_t)syscall(__NR_gettid); | |
| } | |
| #endif | |
| static pthread_t t1, t2; | |
| static void show_state(void) | |
| { | |
| const char *self; | |
| pthread_t t_self; | |
| pid_t tid; | |
| uid_t ruid, euid, suid, fsuid; | |
| cap_t caps; | |
| char *caps_text; | |
| t_self = pthread_self(); | |
| self = (t_self == t1) ? "T1" : (t_self == t2) ? "T2" : "--"; | |
| tid = gettid(); | |
| getresuid(&ruid, &euid, &suid); | |
| fsuid = setfsuid(-1); | |
| caps = cap_get_proc(); | |
| caps_text = cap_to_text(caps, NULL); | |
| printf("thread %s (%6ld) uid{ real=%ld eff=%ld save=%ld fs=%ld } caps{%s}\n", | |
| self, (long)tid, (long)ruid, (long)euid, (long)suid, (long)fsuid, | |
| caps_text); | |
| cap_free(caps_text); | |
| } | |
| static int t1_fd, t2_fd; | |
| /* T2 just observes and prints its state when told to do so */ | |
| static void *t2func(void *arg) | |
| { | |
| char buf[1]; | |
| ssize_t nread; | |
| while (1) { | |
| nread = read(t2_fd, buf, 1); | |
| if (nread <= 0) | |
| break; | |
| show_state(); | |
| write(t2_fd, buf, 1); | |
| } | |
| close(t2_fd); | |
| return NULL; | |
| } | |
| static void show_poke_t2(void) | |
| { | |
| char buf[1] = { '\0' }; | |
| show_state(); | |
| write(t1_fd, buf, 1); | |
| read(t1_fd, buf, 1); | |
| } | |
| #define E(x) do { if ((x)) { fprintf(stderr, "%s failed: %m\n", #x); exit(1); } } while (0) | |
| static bool test_libcap; | |
| static bool test_libcapng; | |
| static const char *do_caps; | |
| /* T1 does the actual work */ | |
| static void *t1func(void *arg) | |
| { | |
| show_poke_t2(); | |
| if (test_libcap) { | |
| cap_t caps = cap_from_text(do_caps); | |
| printf("\nthread T1 cap_set_proc(%s)\n", do_caps); | |
| E(cap_set_proc(caps)); | |
| cap_free(caps); | |
| show_poke_t2(); | |
| } | |
| printf("\nthread T1 changing fsuid to 1234\n"); | |
| setfsuid(1234); | |
| show_poke_t2(); | |
| printf("\nthread T1 changing euid to 2345\n"); | |
| E(setresuid(-1, 2345, -1)); | |
| show_poke_t2(); | |
| printf("\nthread T1 changing fsuid to 3456\n"); | |
| setfsuid(3456); | |
| show_poke_t2(); | |
| printf("\nthread T1 changing fsuid to 0\n"); | |
| setfsuid(0); | |
| show_poke_t2(); | |
| printf("\nthread T1 changing euid to 0\n"); | |
| E(setresuid(-1, 0, -1)); | |
| show_poke_t2(); | |
| /* | |
| printf("\nthread T1 changing fsuid to 4567\n"); | |
| setfsuid(4567); | |
| show_poke_t2(); | |
| */ | |
| close(t1_fd); | |
| return NULL; | |
| } | |
| static void showsecbits(void) | |
| { | |
| unsigned long secbits; | |
| secbits = prctl(PR_GET_SECUREBITS, 0, 0, 0, 0); | |
| printf("secbits: KEEP_CAPS=%s NO_SETUID_FIXUP=%s\n", | |
| (secbits & SECBIT_KEEP_CAPS) ? "true" : "false", | |
| (secbits & SECBIT_NO_SETUID_FIXUP) ? "true" : "false"); | |
| } | |
| static void changesecbits(unsigned long clear, unsigned long set) | |
| { | |
| unsigned long secbits; | |
| int ret; | |
| secbits = prctl(PR_GET_SECUREBITS, 0, 0, 0, 0); | |
| secbits &= ~clear; | |
| secbits |= set; | |
| ret = prctl(PR_SET_SECUREBITS, secbits, 0, 0, 0); | |
| if (ret) | |
| perror("prctl(PR_SET_SECUREBITS)"); | |
| printf("-- change securebits(-%#lx, +%#lx)\n", clear, set); | |
| showsecbits(); | |
| } | |
| int main(int argc, char **argv) | |
| { | |
| int spair[2]; | |
| int ch; | |
| showsecbits(); | |
| while ((ch = getopt(argc, argv, "KkXxc:")) != -1) { | |
| switch (ch) { | |
| case 'K': | |
| changesecbits(0, SECBIT_KEEP_CAPS); | |
| break; | |
| case 'k': | |
| changesecbits(SECBIT_KEEP_CAPS, 0); | |
| break; | |
| case 'X': | |
| changesecbits(0, SECBIT_NO_SETUID_FIXUP); | |
| break; | |
| case 'x': | |
| changesecbits(SECBIT_NO_SETUID_FIXUP, 0); | |
| break; | |
| case 'c': | |
| test_libcap = true; | |
| do_caps = optarg; | |
| break; | |
| } | |
| } | |
| printf("\n"); | |
| show_state(); | |
| socketpair(AF_UNIX, SOCK_SEQPACKET, 0, spair); | |
| t1_fd = spair[0]; | |
| t2_fd = spair[1]; | |
| pthread_create(&t1, NULL, t1func, NULL); | |
| pthread_create(&t2, NULL, t2func, NULL); | |
| pthread_join(t1, NULL); | |
| pthread_join(t2, NULL); | |
| return 0; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment