1*540ca271Schristos /* Copyright (C) 2021-2022 Yubico AB - See COPYING */ 23ff1169cSchristos #include <sys/types.h> 33ff1169cSchristos #include <sys/stat.h> 4*540ca271Schristos #include <assert.h> 5*540ca271Schristos #include <errno.h> 6*540ca271Schristos #include <fcntl.h> 7*540ca271Schristos #include <pwd.h> 8*540ca271Schristos #include <stdarg.h> 9*540ca271Schristos #include <stdio.h> 10*540ca271Schristos #include <stdlib.h> 11*540ca271Schristos #include <string.h> 123ff1169cSchristos #include <unistd.h> 133ff1169cSchristos 143ff1169cSchristos #include <openssl/bio.h> 153ff1169cSchristos #include <openssl/ec.h> 163ff1169cSchristos #include <fido.h> 173ff1169cSchristos 18*540ca271Schristos #include "debug.h" 193ff1169cSchristos #include "drop_privs.h" 203ff1169cSchristos #include "fuzz/fuzz.h" 213ff1169cSchristos 22*540ca271Schristos extern int prng_up; 23*540ca271Schristos 243ff1169cSchristos #ifdef HAVE_PAM_MODUTIL_DROP_PRIV 253ff1169cSchristos typedef struct pam_modutil_privs fuzz_privs_t; 263ff1169cSchristos #else 273ff1169cSchristos typedef struct _ykman_privs fuzz_privs_t; 283ff1169cSchristos #endif 293ff1169cSchristos 303ff1169cSchristos /* In order to be able to fuzz pam-u2f, we need to be able to have a some 313ff1169cSchristos * predictable data regardless of where its being run. We therefore override 323ff1169cSchristos * functions which retrieve the local system's users, uid, hostnames, 333ff1169cSchristos * pam application data, and authenticator data. */ 343ff1169cSchristos static const char *user_ptr = NULL; 353ff1169cSchristos static struct pam_conv *conv_ptr = NULL; 363ff1169cSchristos static uint8_t *wiredata_ptr = NULL; 373ff1169cSchristos static size_t wiredata_len = 0; 383ff1169cSchristos static int authfile_fd = -1; 393ff1169cSchristos static char env[] = "value"; 403ff1169cSchristos 413ff1169cSchristos /* wrap a function, make it fail 0.25% of the time */ 423ff1169cSchristos #define WRAP(type, name, args, retval, param) \ 433ff1169cSchristos extern type __wrap_##name args; \ 443ff1169cSchristos extern type __real_##name args; \ 453ff1169cSchristos type __wrap_##name args { \ 46*540ca271Schristos if (prng_up && uniform_random(400) < 1) { \ 473ff1169cSchristos return (retval); \ 483ff1169cSchristos } \ 493ff1169cSchristos \ 503ff1169cSchristos return (__real_##name param); \ 513ff1169cSchristos } 523ff1169cSchristos 533ff1169cSchristos void set_wiredata(uint8_t *data, size_t len) { 543ff1169cSchristos wiredata_ptr = data; 553ff1169cSchristos wiredata_len = len; 563ff1169cSchristos } 573ff1169cSchristos void set_user(const char *user) { user_ptr = user; } 583ff1169cSchristos void set_conv(struct pam_conv *conv) { conv_ptr = conv; } 593ff1169cSchristos void set_authfile(int fd) { authfile_fd = fd; } 603ff1169cSchristos 613ff1169cSchristos WRAP(int, close, (int fd), -1, (fd)) 623ff1169cSchristos WRAP(void *, strdup, (const char *s), NULL, (s)) 633ff1169cSchristos WRAP(void *, calloc, (size_t nmemb, size_t size), NULL, (nmemb, size)) 643ff1169cSchristos WRAP(void *, malloc, (size_t size), NULL, (size)) 653ff1169cSchristos WRAP(int, gethostname, (char *name, size_t len), -1, (name, len)) 66*540ca271Schristos WRAP(ssize_t, getline, (char **s, size_t *n, FILE *fp), -1, (s, n, fp)) 673ff1169cSchristos WRAP(FILE *, fdopen, (int fd, const char *mode), NULL, (fd, mode)) 683ff1169cSchristos WRAP(int, fstat, (int fd, struct stat *st), -1, (fd, st)) 693ff1169cSchristos WRAP(BIO *, BIO_new, (const BIO_METHOD *type), NULL, (type)) 703ff1169cSchristos WRAP(int, BIO_write, (BIO * b, const void *data, int len), -1, (b, data, len)) 713ff1169cSchristos WRAP(int, BIO_read, (BIO * b, void *data, int len), -1, (b, data, len)) 723ff1169cSchristos WRAP(int, BIO_ctrl, (BIO * b, int cmd, long larg, void *parg), -1, 733ff1169cSchristos (b, cmd, larg, parg)) 743ff1169cSchristos WRAP(BIO *, BIO_new_mem_buf, (const void *buf, int len), NULL, (buf, len)) 753ff1169cSchristos WRAP(EC_KEY *, EC_KEY_new_by_curve_name, (int nid), NULL, (nid)) 763ff1169cSchristos WRAP(const EC_GROUP *, EC_KEY_get0_group, (const EC_KEY *key), NULL, (key)) 773ff1169cSchristos 78*540ca271Schristos extern ssize_t __real_read(int fildes, void *buf, size_t nbyte); 79*540ca271Schristos extern ssize_t __wrap_read(int fildes, void *buf, size_t nbyte); 80*540ca271Schristos extern ssize_t __wrap_read(int fildes, void *buf, size_t nbyte) { 81*540ca271Schristos assert(fildes >= 0); 82*540ca271Schristos assert(buf != NULL); 83*540ca271Schristos return __real_read(fildes, buf, nbyte); 84*540ca271Schristos } 85*540ca271Schristos 86*540ca271Schristos extern int __wrap_asprintf(char **strp, const char *fmt, ...) 87*540ca271Schristos ATTRIBUTE_FORMAT(printf, 2, 3); 88*540ca271Schristos extern int __wrap_asprintf(char **strp, const char *fmt, ...) { 89*540ca271Schristos va_list ap; 90*540ca271Schristos int r; 91*540ca271Schristos 92*540ca271Schristos if (uniform_random(400) < 1) { 93*540ca271Schristos *strp = (void *) 0xdeadbeef; 94*540ca271Schristos return -1; 95*540ca271Schristos } 96*540ca271Schristos 97*540ca271Schristos va_start(ap, fmt); 98*540ca271Schristos r = vasprintf(strp, fmt, ap); 99*540ca271Schristos va_end(ap); 100*540ca271Schristos 101*540ca271Schristos return r; 102*540ca271Schristos } 103*540ca271Schristos 1043ff1169cSchristos extern uid_t __wrap_geteuid(void); 1053ff1169cSchristos extern uid_t __wrap_geteuid(void) { 1063ff1169cSchristos return (uniform_random(10) < 1) ? 0 : 1008; 1073ff1169cSchristos } 1083ff1169cSchristos 1093ff1169cSchristos extern int __real_open(const char *pathname, int flags); 1103ff1169cSchristos extern int __wrap_open(const char *pathname, int flags); 1113ff1169cSchristos extern int __wrap_open(const char *pathname, int flags) { 112*540ca271Schristos if (prng_up && uniform_random(400) < 1) 1133ff1169cSchristos return -1; 1143ff1169cSchristos /* open write-only files as /dev/null */ 1153ff1169cSchristos if ((flags & O_ACCMODE) == O_WRONLY) 1163ff1169cSchristos return __real_open("/dev/null", flags); 1173ff1169cSchristos /* FIXME: special handling for /dev/random */ 1183ff1169cSchristos if (strcmp(pathname, "/dev/urandom") == 0) 1193ff1169cSchristos return __real_open(pathname, flags); 1203ff1169cSchristos /* open read-only files using a shared fd for the authfile */ 1213ff1169cSchristos if ((flags & O_ACCMODE) == O_RDONLY) 1223ff1169cSchristos return dup(authfile_fd); 1233ff1169cSchristos assert(0); /* unsupported */ 1243ff1169cSchristos return -1; 1253ff1169cSchristos } 1263ff1169cSchristos 1273ff1169cSchristos extern int __wrap_getpwuid_r(uid_t, struct passwd *, char *, size_t, 1283ff1169cSchristos struct passwd **); 1293ff1169cSchristos extern int __wrap_getpwuid_r(uid_t uid, struct passwd *pwd, char *buf, 1303ff1169cSchristos size_t buflen, struct passwd **result) { 1313ff1169cSchristos const char *user = user_ptr; 1323ff1169cSchristos int offset; 1333ff1169cSchristos 1343ff1169cSchristos *result = NULL; 1353ff1169cSchristos if (user == NULL || uniform_random(400) < 1) 1363ff1169cSchristos return EIO; 1373ff1169cSchristos if (uniform_random(400) < 1) 1383ff1169cSchristos return 0; /* No matching record */ 1393ff1169cSchristos if (uniform_random(400) < 1) 1403ff1169cSchristos user = "root"; 1413ff1169cSchristos 1423ff1169cSchristos pwd->pw_uid = uid; 1433ff1169cSchristos pwd->pw_gid = uid; 1443ff1169cSchristos 1453ff1169cSchristos if ((offset = snprintf(buf, buflen, "/home/")) < 0 || 1463ff1169cSchristos (size_t) offset >= buflen) 1473ff1169cSchristos return ENOMEM; 1483ff1169cSchristos 1493ff1169cSchristos pwd->pw_dir = buf; 1503ff1169cSchristos buf += offset; 1513ff1169cSchristos buflen -= offset; 1523ff1169cSchristos 1533ff1169cSchristos if ((offset = snprintf(buf, buflen, "%s", user)) < 0 || 1543ff1169cSchristos (size_t) offset >= buflen) 1553ff1169cSchristos return ENOMEM; 1563ff1169cSchristos 1573ff1169cSchristos if (offset > 1 && uniform_random(400) < 1) 1583ff1169cSchristos buf[offset - 1] = '\0'; /* unexpected username */ 1593ff1169cSchristos 1603ff1169cSchristos pwd->pw_name = buf; 1613ff1169cSchristos *result = pwd; 1623ff1169cSchristos return 0; 1633ff1169cSchristos } 1643ff1169cSchristos 1653ff1169cSchristos extern int __wrap_getpwnam_r(const char *, struct passwd *, char *, size_t, 1663ff1169cSchristos struct passwd **); 1673ff1169cSchristos extern int __wrap_getpwnam_r(const char *name, struct passwd *pwd, char *buf, 1683ff1169cSchristos size_t buflen, struct passwd **result) { 1693ff1169cSchristos assert(name); 1703ff1169cSchristos return __wrap_getpwuid_r(1008, pwd, buf, buflen, result); 1713ff1169cSchristos } 1723ff1169cSchristos 1733ff1169cSchristos extern int __wrap_pam_get_item(const pam_handle_t *, int, const void **); 1743ff1169cSchristos extern int __wrap_pam_get_item(const pam_handle_t *pamh, int item_type, 1753ff1169cSchristos const void **item) { 1763ff1169cSchristos assert(pamh == (void *) FUZZ_PAM_HANDLE); 1773ff1169cSchristos assert(item_type == PAM_CONV); /* other types unsupported */ 1783ff1169cSchristos assert(item != NULL); 1793ff1169cSchristos *item = conv_ptr; 1803ff1169cSchristos 1813ff1169cSchristos return uniform_random(400) < 1 ? PAM_CONV_ERR : PAM_SUCCESS; 1823ff1169cSchristos } 1833ff1169cSchristos 1843ff1169cSchristos extern int __wrap_pam_get_user(pam_handle_t *, const char **, const char *); 1853ff1169cSchristos extern int __wrap_pam_get_user(pam_handle_t *pamh, const char **user_p, 1863ff1169cSchristos const char *prompt) { 1873ff1169cSchristos assert(pamh == (void *) FUZZ_PAM_HANDLE); 1883ff1169cSchristos assert(user_p != NULL); 1893ff1169cSchristos assert(prompt == NULL); 1903ff1169cSchristos *user_p = user_ptr; 1913ff1169cSchristos 1923ff1169cSchristos return uniform_random(400) < 1 ? PAM_CONV_ERR : PAM_SUCCESS; 1933ff1169cSchristos } 1943ff1169cSchristos 1953ff1169cSchristos extern int __wrap_pam_modutil_drop_priv(pam_handle_t *, fuzz_privs_t *, 1963ff1169cSchristos struct passwd *); 1973ff1169cSchristos extern int __wrap_pam_modutil_drop_priv(pam_handle_t *pamh, fuzz_privs_t *privs, 1983ff1169cSchristos struct passwd *pwd) { 1993ff1169cSchristos assert(pamh == (void *) FUZZ_PAM_HANDLE); 2003ff1169cSchristos assert(privs != NULL); 2013ff1169cSchristos assert(pwd != NULL); 2023ff1169cSchristos 2033ff1169cSchristos return uniform_random(400) < 1 ? -1 : 0; 2043ff1169cSchristos } 2053ff1169cSchristos 2063ff1169cSchristos extern int __wrap_pam_modutil_regain_priv(pam_handle_t *, fuzz_privs_t *, 2073ff1169cSchristos struct passwd *); 2083ff1169cSchristos extern int __wrap_pam_modutil_regain_priv(pam_handle_t *pamh, 2093ff1169cSchristos fuzz_privs_t *privs, 2103ff1169cSchristos struct passwd *pwd) { 2113ff1169cSchristos assert(pamh == (void *) FUZZ_PAM_HANDLE); 2123ff1169cSchristos assert(privs != NULL); 2133ff1169cSchristos assert(pwd != NULL); 2143ff1169cSchristos 2153ff1169cSchristos return uniform_random(400) < 1 ? -1 : 0; 2163ff1169cSchristos } 2173ff1169cSchristos 2183ff1169cSchristos extern char *__wrap_secure_getenv(const char *); 2193ff1169cSchristos extern char *__wrap_secure_getenv(const char *name) { 2203ff1169cSchristos (void) name; 2213ff1169cSchristos 2223ff1169cSchristos if (uniform_random(400) < 1) 2233ff1169cSchristos return env; 2243ff1169cSchristos return NULL; 2253ff1169cSchristos } 2263ff1169cSchristos 2273ff1169cSchristos static int buf_read(unsigned char *ptr, size_t len, int ms) { 2283ff1169cSchristos size_t n; 2293ff1169cSchristos 2303ff1169cSchristos (void) ms; 2313ff1169cSchristos 2323ff1169cSchristos if (wiredata_len < len) 2333ff1169cSchristos n = wiredata_len; 2343ff1169cSchristos else 2353ff1169cSchristos n = len; 2363ff1169cSchristos 2373ff1169cSchristos memcpy(ptr, wiredata_ptr, n); 2383ff1169cSchristos wiredata_ptr += n; 2393ff1169cSchristos wiredata_len -= n; 2403ff1169cSchristos 2413ff1169cSchristos return (int) n; 2423ff1169cSchristos } 2433ff1169cSchristos 2443ff1169cSchristos static int buf_write(const unsigned char *ptr, size_t len) { 2453ff1169cSchristos (void) ptr; 2463ff1169cSchristos return (int) len; 2473ff1169cSchristos } 2483ff1169cSchristos 2493ff1169cSchristos static void *dev_open(const char *path) { 2503ff1169cSchristos (void) path; 2513ff1169cSchristos return (void *) FUZZ_DEV_HANDLE; 2523ff1169cSchristos } 2533ff1169cSchristos 2543ff1169cSchristos static void dev_close(void *handle) { 2553ff1169cSchristos assert(handle == (void *) FUZZ_DEV_HANDLE); 2563ff1169cSchristos } 2573ff1169cSchristos 2583ff1169cSchristos static int dev_read(void *handle, unsigned char *ptr, size_t len, int ms) { 2593ff1169cSchristos assert(handle == (void *) FUZZ_DEV_HANDLE); 2603ff1169cSchristos return buf_read(ptr, len, ms); 2613ff1169cSchristos } 2623ff1169cSchristos 2633ff1169cSchristos static int dev_write(void *handle, const unsigned char *ptr, size_t len) { 2643ff1169cSchristos assert(handle == (void *) FUZZ_DEV_HANDLE); 2653ff1169cSchristos return buf_write(ptr, len); 2663ff1169cSchristos } 2673ff1169cSchristos 2683ff1169cSchristos extern int __wrap_fido_dev_open(fido_dev_t *dev, const char *path); 2693ff1169cSchristos extern int __real_fido_dev_open(fido_dev_t *dev, const char *path); 2703ff1169cSchristos int __wrap_fido_dev_open(fido_dev_t *dev, const char *path) { 2713ff1169cSchristos fido_dev_io_t io; 2723ff1169cSchristos int r; 2733ff1169cSchristos 2743ff1169cSchristos (void) path; 2753ff1169cSchristos 2763ff1169cSchristos memset(&io, 0, sizeof(io)); 2773ff1169cSchristos 2783ff1169cSchristos io.open = dev_open; 2793ff1169cSchristos io.close = dev_close; 2803ff1169cSchristos io.read = dev_read; 2813ff1169cSchristos io.write = dev_write; 2823ff1169cSchristos 2833ff1169cSchristos if ((r = fido_dev_set_io_functions(dev, &io)) != FIDO_OK) 2843ff1169cSchristos goto err; 2853ff1169cSchristos 2863ff1169cSchristos if ((r = __real_fido_dev_open(dev, "nodev")) != FIDO_OK) 2873ff1169cSchristos goto err; 2883ff1169cSchristos 2893ff1169cSchristos err: 2903ff1169cSchristos return r; 2913ff1169cSchristos } 2923ff1169cSchristos 2933ff1169cSchristos extern int __wrap_fido_dev_info_manifest(fido_dev_info_t *, size_t, size_t *); 2943ff1169cSchristos extern int __wrap_fido_dev_info_manifest(fido_dev_info_t *devlist, size_t ilen, 2953ff1169cSchristos size_t *olen) { 2963ff1169cSchristos (void) devlist; 2973ff1169cSchristos (void) ilen; 2983ff1169cSchristos 2993ff1169cSchristos *olen = (size_t) uniform_random((uint32_t) ilen); 3003ff1169cSchristos 3013ff1169cSchristos return uniform_random(400) < 1 ? FIDO_ERR_INTERNAL : FIDO_OK; 3023ff1169cSchristos } 303