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