xref: /netbsd-src/external/bsd/pam-u2f/dist/fuzz/fuzz_auth.c (revision 540ca2718c85d7b6631d382e3e93f4dc91e51282)
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