Home | History | Annotate | Line # | Download | only in fuzz
      1 /* Copyright (C) 2021-2022 Yubico AB - See COPYING */
      2 #include <sys/types.h>
      3 #include <sys/stat.h>
      4 #include <assert.h>
      5 #include <errno.h>
      6 #include <fcntl.h>
      7 #include <pwd.h>
      8 #include <stdarg.h>
      9 #include <stdio.h>
     10 #include <stdlib.h>
     11 #include <string.h>
     12 #include <unistd.h>
     13 
     14 #include <openssl/bio.h>
     15 #include <openssl/ec.h>
     16 #include <fido.h>
     17 
     18 #include "debug.h"
     19 #include "drop_privs.h"
     20 #include "fuzz/fuzz.h"
     21 
     22 extern int prng_up;
     23 
     24 #ifdef HAVE_PAM_MODUTIL_DROP_PRIV
     25 typedef struct pam_modutil_privs fuzz_privs_t;
     26 #else
     27 typedef struct _ykman_privs fuzz_privs_t;
     28 #endif
     29 
     30 /* In order to be able to fuzz pam-u2f, we need to be able to have a some
     31  * predictable data regardless of where its being run. We therefore override
     32  * functions which retrieve the local system's users, uid, hostnames,
     33  * pam application data, and authenticator data. */
     34 static const char *user_ptr = NULL;
     35 static struct pam_conv *conv_ptr = NULL;
     36 static uint8_t *wiredata_ptr = NULL;
     37 static size_t wiredata_len = 0;
     38 static int authfile_fd = -1;
     39 static char env[] = "value";
     40 
     41 /* wrap a function, make it fail 0.25% of the time */
     42 #define WRAP(type, name, args, retval, param)                                  \
     43   extern type __wrap_##name args;                                              \
     44   extern type __real_##name args;                                              \
     45   type __wrap_##name args {                                                    \
     46     if (prng_up && uniform_random(400) < 1) {                                  \
     47       return (retval);                                                         \
     48     }                                                                          \
     49                                                                                \
     50     return (__real_##name param);                                              \
     51   }
     52 
     53 void set_wiredata(uint8_t *data, size_t len) {
     54   wiredata_ptr = data;
     55   wiredata_len = len;
     56 }
     57 void set_user(const char *user) { user_ptr = user; }
     58 void set_conv(struct pam_conv *conv) { conv_ptr = conv; }
     59 void set_authfile(int fd) { authfile_fd = fd; }
     60 
     61 WRAP(int, close, (int fd), -1, (fd))
     62 WRAP(void *, strdup, (const char *s), NULL, (s))
     63 WRAP(void *, calloc, (size_t nmemb, size_t size), NULL, (nmemb, size))
     64 WRAP(void *, malloc, (size_t size), NULL, (size))
     65 WRAP(int, gethostname, (char *name, size_t len), -1, (name, len))
     66 WRAP(ssize_t, getline, (char **s, size_t *n, FILE *fp), -1, (s, n, fp))
     67 WRAP(FILE *, fdopen, (int fd, const char *mode), NULL, (fd, mode))
     68 WRAP(int, fstat, (int fd, struct stat *st), -1, (fd, st))
     69 WRAP(BIO *, BIO_new, (const BIO_METHOD *type), NULL, (type))
     70 WRAP(int, BIO_write, (BIO * b, const void *data, int len), -1, (b, data, len))
     71 WRAP(int, BIO_read, (BIO * b, void *data, int len), -1, (b, data, len))
     72 WRAP(int, BIO_ctrl, (BIO * b, int cmd, long larg, void *parg), -1,
     73      (b, cmd, larg, parg))
     74 WRAP(BIO *, BIO_new_mem_buf, (const void *buf, int len), NULL, (buf, len))
     75 WRAP(EC_KEY *, EC_KEY_new_by_curve_name, (int nid), NULL, (nid))
     76 WRAP(const EC_GROUP *, EC_KEY_get0_group, (const EC_KEY *key), NULL, (key))
     77 
     78 extern ssize_t __real_read(int fildes, void *buf, size_t nbyte);
     79 extern ssize_t __wrap_read(int fildes, void *buf, size_t nbyte);
     80 extern ssize_t __wrap_read(int fildes, void *buf, size_t nbyte) {
     81   assert(fildes >= 0);
     82   assert(buf != NULL);
     83   return __real_read(fildes, buf, nbyte);
     84 }
     85 
     86 extern int __wrap_asprintf(char **strp, const char *fmt, ...)
     87   ATTRIBUTE_FORMAT(printf, 2, 3);
     88 extern int __wrap_asprintf(char **strp, const char *fmt, ...) {
     89   va_list ap;
     90   int r;
     91 
     92   if (uniform_random(400) < 1) {
     93     *strp = (void *) 0xdeadbeef;
     94     return -1;
     95   }
     96 
     97   va_start(ap, fmt);
     98   r = vasprintf(strp, fmt, ap);
     99   va_end(ap);
    100 
    101   return r;
    102 }
    103 
    104 extern uid_t __wrap_geteuid(void);
    105 extern uid_t __wrap_geteuid(void) {
    106   return (uniform_random(10) < 1) ? 0 : 1008;
    107 }
    108 
    109 extern int __real_open(const char *pathname, int flags);
    110 extern int __wrap_open(const char *pathname, int flags);
    111 extern int __wrap_open(const char *pathname, int flags) {
    112   if (prng_up && uniform_random(400) < 1)
    113     return -1;
    114   /* open write-only files as /dev/null */
    115   if ((flags & O_ACCMODE) == O_WRONLY)
    116     return __real_open("/dev/null", flags);
    117   /* FIXME: special handling for /dev/random */
    118   if (strcmp(pathname, "/dev/urandom") == 0)
    119     return __real_open(pathname, flags);
    120   /* open read-only files using a shared fd for the authfile */
    121   if ((flags & O_ACCMODE) == O_RDONLY)
    122     return dup(authfile_fd);
    123   assert(0); /* unsupported */
    124   return -1;
    125 }
    126 
    127 extern int __wrap_getpwuid_r(uid_t, struct passwd *, char *, size_t,
    128                              struct passwd **);
    129 extern int __wrap_getpwuid_r(uid_t uid, struct passwd *pwd, char *buf,
    130                              size_t buflen, struct passwd **result) {
    131   const char *user = user_ptr;
    132   int offset;
    133 
    134   *result = NULL;
    135   if (user == NULL || uniform_random(400) < 1)
    136     return EIO;
    137   if (uniform_random(400) < 1)
    138     return 0; /* No matching record */
    139   if (uniform_random(400) < 1)
    140     user = "root";
    141 
    142   pwd->pw_uid = uid;
    143   pwd->pw_gid = uid;
    144 
    145   if ((offset = snprintf(buf, buflen, "/home/")) < 0 ||
    146       (size_t) offset >= buflen)
    147     return ENOMEM;
    148 
    149   pwd->pw_dir = buf;
    150   buf += offset;
    151   buflen -= offset;
    152 
    153   if ((offset = snprintf(buf, buflen, "%s", user)) < 0 ||
    154       (size_t) offset >= buflen)
    155     return ENOMEM;
    156 
    157   if (offset > 1 && uniform_random(400) < 1)
    158     buf[offset - 1] = '\0'; /* unexpected username */
    159 
    160   pwd->pw_name = buf;
    161   *result = pwd;
    162   return 0;
    163 }
    164 
    165 extern int __wrap_getpwnam_r(const char *, struct passwd *, char *, size_t,
    166                              struct passwd **);
    167 extern int __wrap_getpwnam_r(const char *name, struct passwd *pwd, char *buf,
    168                              size_t buflen, struct passwd **result) {
    169   assert(name);
    170   return __wrap_getpwuid_r(1008, pwd, buf, buflen, result);
    171 }
    172 
    173 extern int __wrap_pam_get_item(const pam_handle_t *, int, const void **);
    174 extern int __wrap_pam_get_item(const pam_handle_t *pamh, int item_type,
    175                                const void **item) {
    176   assert(pamh == (void *) FUZZ_PAM_HANDLE);
    177   assert(item_type == PAM_CONV); /* other types unsupported */
    178   assert(item != NULL);
    179   *item = conv_ptr;
    180 
    181   return uniform_random(400) < 1 ? PAM_CONV_ERR : PAM_SUCCESS;
    182 }
    183 
    184 extern int __wrap_pam_get_user(pam_handle_t *, const char **, const char *);
    185 extern int __wrap_pam_get_user(pam_handle_t *pamh, const char **user_p,
    186                                const char *prompt) {
    187   assert(pamh == (void *) FUZZ_PAM_HANDLE);
    188   assert(user_p != NULL);
    189   assert(prompt == NULL);
    190   *user_p = user_ptr;
    191 
    192   return uniform_random(400) < 1 ? PAM_CONV_ERR : PAM_SUCCESS;
    193 }
    194 
    195 extern int __wrap_pam_modutil_drop_priv(pam_handle_t *, fuzz_privs_t *,
    196                                         struct passwd *);
    197 extern int __wrap_pam_modutil_drop_priv(pam_handle_t *pamh, fuzz_privs_t *privs,
    198                                         struct passwd *pwd) {
    199   assert(pamh == (void *) FUZZ_PAM_HANDLE);
    200   assert(privs != NULL);
    201   assert(pwd != NULL);
    202 
    203   return uniform_random(400) < 1 ? -1 : 0;
    204 }
    205 
    206 extern int __wrap_pam_modutil_regain_priv(pam_handle_t *, fuzz_privs_t *,
    207                                           struct passwd *);
    208 extern int __wrap_pam_modutil_regain_priv(pam_handle_t *pamh,
    209                                           fuzz_privs_t *privs,
    210                                           struct passwd *pwd) {
    211   assert(pamh == (void *) FUZZ_PAM_HANDLE);
    212   assert(privs != NULL);
    213   assert(pwd != NULL);
    214 
    215   return uniform_random(400) < 1 ? -1 : 0;
    216 }
    217 
    218 extern char *__wrap_secure_getenv(const char *);
    219 extern char *__wrap_secure_getenv(const char *name) {
    220   (void) name;
    221 
    222   if (uniform_random(400) < 1)
    223     return env;
    224   return NULL;
    225 }
    226 
    227 static int buf_read(unsigned char *ptr, size_t len, int ms) {
    228   size_t n;
    229 
    230   (void) ms;
    231 
    232   if (wiredata_len < len)
    233     n = wiredata_len;
    234   else
    235     n = len;
    236 
    237   memcpy(ptr, wiredata_ptr, n);
    238   wiredata_ptr += n;
    239   wiredata_len -= n;
    240 
    241   return (int) n;
    242 }
    243 
    244 static int buf_write(const unsigned char *ptr, size_t len) {
    245   (void) ptr;
    246   return (int) len;
    247 }
    248 
    249 static void *dev_open(const char *path) {
    250   (void) path;
    251   return (void *) FUZZ_DEV_HANDLE;
    252 }
    253 
    254 static void dev_close(void *handle) {
    255   assert(handle == (void *) FUZZ_DEV_HANDLE);
    256 }
    257 
    258 static int dev_read(void *handle, unsigned char *ptr, size_t len, int ms) {
    259   assert(handle == (void *) FUZZ_DEV_HANDLE);
    260   return buf_read(ptr, len, ms);
    261 }
    262 
    263 static int dev_write(void *handle, const unsigned char *ptr, size_t len) {
    264   assert(handle == (void *) FUZZ_DEV_HANDLE);
    265   return buf_write(ptr, len);
    266 }
    267 
    268 extern int __wrap_fido_dev_open(fido_dev_t *dev, const char *path);
    269 extern int __real_fido_dev_open(fido_dev_t *dev, const char *path);
    270 int __wrap_fido_dev_open(fido_dev_t *dev, const char *path) {
    271   fido_dev_io_t io;
    272   int r;
    273 
    274   (void) path;
    275 
    276   memset(&io, 0, sizeof(io));
    277 
    278   io.open = dev_open;
    279   io.close = dev_close;
    280   io.read = dev_read;
    281   io.write = dev_write;
    282 
    283   if ((r = fido_dev_set_io_functions(dev, &io)) != FIDO_OK)
    284     goto err;
    285 
    286   if ((r = __real_fido_dev_open(dev, "nodev")) != FIDO_OK)
    287     goto err;
    288 
    289 err:
    290   return r;
    291 }
    292 
    293 extern int __wrap_fido_dev_info_manifest(fido_dev_info_t *, size_t, size_t *);
    294 extern int __wrap_fido_dev_info_manifest(fido_dev_info_t *devlist, size_t ilen,
    295                                          size_t *olen) {
    296   (void) devlist;
    297   (void) ilen;
    298 
    299   *olen = (size_t) uniform_random((uint32_t) ilen);
    300 
    301   return uniform_random(400) < 1 ? FIDO_ERR_INTERNAL : FIDO_OK;
    302 }
    303