1*540ca271Schristos /* Copyright (C) 2021-2022 Yubico AB - See COPYING */ 2*540ca271Schristos #include <sys/types.h> 3*540ca271Schristos #include <sys/mman.h> 4*540ca271Schristos #include <sys/stat.h> 53ff1169cSchristos #include <assert.h> 63ff1169cSchristos #include <err.h> 73ff1169cSchristos #include <errno.h> 83ff1169cSchristos #include <pwd.h> 93ff1169cSchristos #include <stdio.h> 103ff1169cSchristos #include <stdlib.h> 113ff1169cSchristos #include <string.h> 123ff1169cSchristos #include <unistd.h> 133ff1169cSchristos 143ff1169cSchristos #include "fuzz/fuzz.h" 153ff1169cSchristos #include "fuzz/wiredata.h" 163ff1169cSchristos #include "fuzz/authfile.h" 173ff1169cSchristos 183ff1169cSchristos #define MUTATE_SEED 0x01 193ff1169cSchristos #define MUTATE_PARAM 0x02 203ff1169cSchristos #define MUTATE_WIREDATA 0x04 213ff1169cSchristos #define MUTATE_ALL (MUTATE_SEED | MUTATE_PARAM | MUTATE_WIREDATA) 223ff1169cSchristos 233ff1169cSchristos size_t LLVMFuzzerMutate(uint8_t *, size_t, size_t); 243ff1169cSchristos int LLVMFuzzerInitialize(int *, char ***); 253ff1169cSchristos int LLVMFuzzerTestOneInput(const uint8_t *, size_t); 263ff1169cSchristos size_t LLVMFuzzerCustomMutator(uint8_t *, size_t, size_t, unsigned int); 273ff1169cSchristos 283ff1169cSchristos struct param { 293ff1169cSchristos uint32_t seed; 303ff1169cSchristos char user[MAXSTR]; 313ff1169cSchristos char conf[MAXSTR]; 323ff1169cSchristos char conv[MAXSTR]; 333ff1169cSchristos struct blob authfile; 343ff1169cSchristos struct blob wiredata; 353ff1169cSchristos }; 363ff1169cSchristos 373ff1169cSchristos struct conv_appdata { 383ff1169cSchristos char *str; 393ff1169cSchristos char *save; 403ff1169cSchristos }; 413ff1169cSchristos 423ff1169cSchristos /* fuzzer configuration */ 433ff1169cSchristos static unsigned int flags = MUTATE_ALL; 443ff1169cSchristos 453ff1169cSchristos /* it is far easier for the fuzzer to guess the native format */ 463ff1169cSchristos static const char dummy_authfile[] = AUTHFILE_SSH; 473ff1169cSchristos 483ff1169cSchristos /* module configuration split by fuzzer on semicolon */ 493ff1169cSchristos static const char *dummy_conf = "sshformat;pinverification=0;manual;"; 503ff1169cSchristos 513ff1169cSchristos /* conversation dummy for manual authentication */ 523ff1169cSchristos static const char *dummy_conv = 533ff1169cSchristos "94/ZgCC5htEl9SRmTRfUffKCzU/2ScRJYNFSlC5U+ik=\n" 543ff1169cSchristos "ssh:\n" 553ff1169cSchristos "WCXjBhDooWIRWWD+HsIj5lKcn0tugCANy15cMhyK8eKxvwEAAAAP\n" 563ff1169cSchristos "MEQCIDBrIO3J/B9Y7LJca3A7t0m76WcxoATJe0NG/" 573ff1169cSchristos "ZsjOMq2AiAdBGrjMalfVtzEe0rjWfnRrGhMFyRyaRuPfCHVYdIWdg==\n"; 583ff1169cSchristos 593ff1169cSchristos /* wiredata collected from an authenticator during authentication */ 603ff1169cSchristos static unsigned char dummy_wiredata[] = { 613ff1169cSchristos WIREDATA_CTAP_INIT, 623ff1169cSchristos WIREDATA_CTAP_CBOR_INFO, 633ff1169cSchristos WIREDATA_CTAP_CBOR_ASSERT_DISCOVER, 643ff1169cSchristos WIREDATA_CTAP_CBOR_ASSERT_AUTHENTICATE, 653ff1169cSchristos }; 663ff1169cSchristos 673ff1169cSchristos static size_t pack(uint8_t *data, size_t len, const struct param *p) { 683ff1169cSchristos size_t ilen = len; 693ff1169cSchristos 703ff1169cSchristos if (pack_u32(&data, &len, p->seed) != 1 || 713ff1169cSchristos pack_string(&data, &len, p->user) != 1 || 723ff1169cSchristos pack_string(&data, &len, p->conf) != 1 || 733ff1169cSchristos pack_string(&data, &len, p->conv) != 1 || 743ff1169cSchristos pack_blob(&data, &len, &p->authfile) != 1 || 753ff1169cSchristos pack_blob(&data, &len, &p->wiredata) != 1) { 763ff1169cSchristos return 0; 773ff1169cSchristos } 783ff1169cSchristos 793ff1169cSchristos return ilen - len; 803ff1169cSchristos } 813ff1169cSchristos 823ff1169cSchristos static int set_blob(struct blob *blob, const void *data, size_t len) { 833ff1169cSchristos if (len > MAXBLOB) 843ff1169cSchristos return 0; 853ff1169cSchristos memcpy(blob->body, data, len); 863ff1169cSchristos blob->len = len; 873ff1169cSchristos return 1; 883ff1169cSchristos } 893ff1169cSchristos 903ff1169cSchristos static int set_string(char *dst, const char *src, size_t size) { 913ff1169cSchristos int n; 923ff1169cSchristos 933ff1169cSchristos /* FIXME: use strlcpy */ 943ff1169cSchristos n = snprintf(dst, size, "%s", src); 953ff1169cSchristos if (n < 0 || (size_t) n >= size) 963ff1169cSchristos return 0; 973ff1169cSchristos return 1; 983ff1169cSchristos } 993ff1169cSchristos 1003ff1169cSchristos static size_t pack_dummy(uint8_t *data, size_t len) { 1013ff1169cSchristos struct param dummy; 1023ff1169cSchristos size_t r; 1033ff1169cSchristos 1043ff1169cSchristos memset(&dummy, 0, sizeof(dummy)); 1053ff1169cSchristos if (!set_string(dummy.user, "user", MAXSTR) || 1063ff1169cSchristos !set_string(dummy.conf, dummy_conf, MAXSTR) || 1073ff1169cSchristos !set_string(dummy.conv, dummy_conv, MAXSTR) || 1083ff1169cSchristos !set_blob(&dummy.authfile, dummy_authfile, sizeof(dummy_authfile)) || 1093ff1169cSchristos !set_blob(&dummy.wiredata, dummy_wiredata, sizeof(dummy_wiredata))) { 1103ff1169cSchristos assert(0); /* dummy couldn't be prepared */ 1113ff1169cSchristos return 0; 1123ff1169cSchristos } 1133ff1169cSchristos 1143ff1169cSchristos r = pack(data, len, &dummy); 1153ff1169cSchristos assert(r != 0); /* dummy couldn't be packed */ 1163ff1169cSchristos return r; 1173ff1169cSchristos } 1183ff1169cSchristos 1193ff1169cSchristos static struct param *unpack(const uint8_t *data, size_t len) { 1203ff1169cSchristos struct param *p = NULL; 1213ff1169cSchristos 1223ff1169cSchristos if ((p = calloc(1, sizeof(*p))) == NULL || 1233ff1169cSchristos unpack_u32(&data, &len, &p->seed) != 1 || 1243ff1169cSchristos unpack_string(&data, &len, p->user) != 1 || 1253ff1169cSchristos unpack_string(&data, &len, p->conf) != 1 || 1263ff1169cSchristos unpack_string(&data, &len, p->conv) != 1 || 1273ff1169cSchristos unpack_blob(&data, &len, &p->authfile) != 1 || 1283ff1169cSchristos unpack_blob(&data, &len, &p->wiredata) != 1) { 1293ff1169cSchristos free(p); 1303ff1169cSchristos return NULL; 1313ff1169cSchristos } 1323ff1169cSchristos 1333ff1169cSchristos return p; 1343ff1169cSchristos } 1353ff1169cSchristos 1363ff1169cSchristos static void mutate_blob(struct blob *blob) { 1373ff1169cSchristos blob->len = 1383ff1169cSchristos LLVMFuzzerMutate((uint8_t *) blob->body, blob->len, sizeof(blob->body)); 1393ff1169cSchristos } 1403ff1169cSchristos 1413ff1169cSchristos static void mutate_string(char *s, size_t maxlen) { 1423ff1169cSchristos size_t len; 1433ff1169cSchristos 1443ff1169cSchristos len = LLVMFuzzerMutate((uint8_t *) s, strlen(s), maxlen); 1453ff1169cSchristos s[len - 1] = '\0'; 1463ff1169cSchristos } 1473ff1169cSchristos 1483ff1169cSchristos static void mutate(struct param *p, uint32_t seed) { 1493ff1169cSchristos if (flags & MUTATE_SEED) 1503ff1169cSchristos p->seed = seed; 1513ff1169cSchristos if (flags & MUTATE_PARAM) { 1523ff1169cSchristos mutate_string(p->user, MAXSTR); 1533ff1169cSchristos mutate_string(p->conf, MAXSTR); 1543ff1169cSchristos mutate_string(p->conv, MAXSTR); 1553ff1169cSchristos mutate_blob(&p->authfile); 1563ff1169cSchristos } 1573ff1169cSchristos if (flags & MUTATE_WIREDATA) 1583ff1169cSchristos mutate_blob(&p->wiredata); 1593ff1169cSchristos } 1603ff1169cSchristos 1613ff1169cSchristos static void consume(const void *body, size_t len) { 1623ff1169cSchristos const volatile uint8_t *ptr = body; 1633ff1169cSchristos volatile uint8_t x = 0; 1643ff1169cSchristos 1653ff1169cSchristos while (len--) 1663ff1169cSchristos x ^= *ptr++; 167*540ca271Schristos 168*540ca271Schristos (void) x; 1693ff1169cSchristos } 1703ff1169cSchristos 1713ff1169cSchristos static int conv_cb(int num_msg, const struct pam_message **msg, 1723ff1169cSchristos struct pam_response **resp_p, void *appdata_ptr) { 1733ff1169cSchristos struct conv_appdata *conv = appdata_ptr; 1743ff1169cSchristos struct pam_response *resp = NULL; 1753ff1169cSchristos const char *str = NULL; 1763ff1169cSchristos 1773ff1169cSchristos assert(num_msg == 1); 1783ff1169cSchristos assert(resp_p != NULL); 1793ff1169cSchristos 1803ff1169cSchristos consume(msg[0]->msg, strlen(msg[0]->msg)); 1813ff1169cSchristos 1823ff1169cSchristos if ((*resp_p = resp = calloc(1, sizeof(*resp))) == NULL) 1833ff1169cSchristos return PAM_CONV_ERR; 1843ff1169cSchristos 1853ff1169cSchristos if (msg[0]->msg_style == PAM_PROMPT_ECHO_OFF || 1863ff1169cSchristos msg[0]->msg_style == PAM_PROMPT_ECHO_ON) { 1873ff1169cSchristos str = strtok_r(conv->save ? NULL : conv->str, "\n", &conv->save); 1883ff1169cSchristos if (str != NULL && (resp->resp = strdup(str)) == NULL) { 1893ff1169cSchristos free(resp); 1903ff1169cSchristos return PAM_CONV_ERR; 1913ff1169cSchristos } 1923ff1169cSchristos } 1933ff1169cSchristos 1943ff1169cSchristos return PAM_SUCCESS; 1953ff1169cSchristos } 1963ff1169cSchristos 1973ff1169cSchristos static void prepare_argv(char *s, const char **argv, int *argc) { 1983ff1169cSchristos const char *delim = ";"; 1993ff1169cSchristos char *token, *save; 2003ff1169cSchristos int size = *argc; 2013ff1169cSchristos 2023ff1169cSchristos *argc = 0; 2033ff1169cSchristos 2043ff1169cSchristos token = strtok_r(s, delim, &save); 2053ff1169cSchristos while (token != NULL && *argc < size) { 2063ff1169cSchristos argv[(*argc)++] = token; 2073ff1169cSchristos token = strtok_r(NULL, delim, &save); 2083ff1169cSchristos } 2093ff1169cSchristos } 2103ff1169cSchristos 2113ff1169cSchristos static void prepare_conv(struct pam_conv *conv, struct conv_appdata *data, 2123ff1169cSchristos char *str) { 2133ff1169cSchristos data->str = str; 2143ff1169cSchristos conv->conv = conv_cb; 2153ff1169cSchristos conv->appdata_ptr = data; 2163ff1169cSchristos } 2173ff1169cSchristos 2183ff1169cSchristos static int prepare_authfile(const unsigned char *data, size_t len) { 2193ff1169cSchristos int fd; 2203ff1169cSchristos ssize_t r; 2213ff1169cSchristos 2223ff1169cSchristos if ((fd = memfd_create("u2f_keys", MFD_CLOEXEC)) == -1) 2233ff1169cSchristos return -1; 2243ff1169cSchristos 2253ff1169cSchristos if ((r = write(fd, data, len)) == -1 || (size_t) r != len || 2263ff1169cSchristos lseek(fd, 0, SEEK_SET) == -1) { 2273ff1169cSchristos close(fd); 2283ff1169cSchristos return -1; 2293ff1169cSchristos } 2303ff1169cSchristos 2313ff1169cSchristos return fd; 2323ff1169cSchristos } 2333ff1169cSchristos 2343ff1169cSchristos int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { 2353ff1169cSchristos 2363ff1169cSchristos struct param *param = NULL; 2373ff1169cSchristos struct pam_conv conv; 2383ff1169cSchristos struct conv_appdata conv_data; 2393ff1169cSchristos const char *argv[32]; 2403ff1169cSchristos int argc = 32; 2413ff1169cSchristos int fd = -1; 2423ff1169cSchristos 2433ff1169cSchristos memset(&argv, 0, sizeof(*argv)); 2443ff1169cSchristos memset(&conv, 0, sizeof(conv)); 2453ff1169cSchristos memset(&conv_data, 0, sizeof(conv_data)); 2463ff1169cSchristos 2473ff1169cSchristos if ((param = unpack(data, size)) == NULL) 2483ff1169cSchristos goto err; 2493ff1169cSchristos 2503ff1169cSchristos /* init libfido2's fuzzing prng */ 2513ff1169cSchristos prng_init(param->seed); 2523ff1169cSchristos 2533ff1169cSchristos /* configure wrappers */ 2543ff1169cSchristos prepare_conv(&conv, &conv_data, param->conv); 2553ff1169cSchristos set_conv(&conv); 2563ff1169cSchristos set_user(param->user); 2573ff1169cSchristos set_wiredata(param->wiredata.body, param->wiredata.len); 2583ff1169cSchristos 2593ff1169cSchristos if ((fd = prepare_authfile(param->authfile.body, param->authfile.len)) == -1) 2603ff1169cSchristos goto err; 2613ff1169cSchristos set_authfile(fd); 2623ff1169cSchristos 2633ff1169cSchristos prepare_argv(param->conf, &argv[0], &argc); 2643ff1169cSchristos pam_sm_authenticate((void *) FUZZ_PAM_HANDLE, 0, argc, argv); 2653ff1169cSchristos 2663ff1169cSchristos err: 2673ff1169cSchristos if (fd != -1) 2683ff1169cSchristos close(fd); 2693ff1169cSchristos free(param); 2703ff1169cSchristos return 0; 2713ff1169cSchristos } 2723ff1169cSchristos 2733ff1169cSchristos size_t LLVMFuzzerCustomMutator(uint8_t *data, size_t size, size_t maxsize, 2743ff1169cSchristos unsigned int seed) { 2753ff1169cSchristos size_t blob_len; 2763ff1169cSchristos struct param *p = NULL; 2773ff1169cSchristos 2783ff1169cSchristos if ((p = unpack(data, size)) == NULL) 2793ff1169cSchristos return pack_dummy(data, maxsize); 2803ff1169cSchristos 2813ff1169cSchristos mutate(p, seed); 2823ff1169cSchristos blob_len = pack(data, maxsize, p); 2833ff1169cSchristos free(p); 2843ff1169cSchristos 2853ff1169cSchristos return blob_len; 2863ff1169cSchristos } 2873ff1169cSchristos 2883ff1169cSchristos static void parse_mutate_flags(const char *opt, unsigned int *mutate_flags) { 2893ff1169cSchristos const char *f; 2903ff1169cSchristos 2913ff1169cSchristos if ((f = strchr(opt, '=')) == NULL || strlen(++f) == 0) 2923ff1169cSchristos errx(1, "usage: --pam-u2f-mutate=<flag>"); 2933ff1169cSchristos 2943ff1169cSchristos if (strcmp(f, "seed") == 0) 2953ff1169cSchristos *mutate_flags |= MUTATE_SEED; 2963ff1169cSchristos else if (strcmp(f, "param") == 0) 2973ff1169cSchristos *mutate_flags |= MUTATE_PARAM; 2983ff1169cSchristos else if (strcmp(f, "wiredata") == 0) 2993ff1169cSchristos *mutate_flags |= MUTATE_WIREDATA; 3003ff1169cSchristos else 3013ff1169cSchristos errx(1, "--pam-u2f-mutate: unknown flag '%s'", f); 3023ff1169cSchristos } 3033ff1169cSchristos 3043ff1169cSchristos int LLVMFuzzerInitialize(int *argc, char ***argv) { 3053ff1169cSchristos unsigned int mutate_flags = 0; 3063ff1169cSchristos 3073ff1169cSchristos for (int i = 0; i < *argc; i++) { 3083ff1169cSchristos if (strncmp((*argv)[i], "--pam-u2f-mutate=", 17) == 0) { 3093ff1169cSchristos parse_mutate_flags((*argv)[i], &mutate_flags); 3103ff1169cSchristos } 3113ff1169cSchristos } 3123ff1169cSchristos 3133ff1169cSchristos if (mutate_flags) 3143ff1169cSchristos flags = mutate_flags; 3153ff1169cSchristos 3163ff1169cSchristos return 0; 3173ff1169cSchristos } 318