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