1 /* 2 * Copyright (c) 2020,2021 by Solar Designer 3 * See LICENSE 4 */ 5 6 #define _FILE_OFFSET_BITS 64 7 #define _LARGEFILE_SOURCE 1 8 #define _LARGEFILE64_SOURCE 1 9 #define _LARGE_FILES 1 10 11 #ifdef _MSC_VER 12 #define _CRT_NONSTDC_NO_WARNINGS /* we use POSIX function names */ 13 #define _CRT_SECURE_NO_WARNINGS /* we use open() */ 14 #include <stdio.h> /* for SEEK_SET and SEEK_END */ 15 #include <io.h> 16 #define lseek _lseeki64 17 #define ssize_t int /* MSVC's read() returns int and we don't need more here */ 18 #define SSIZE_MAX INT_MAX 19 #define OPEN_FLAGS (O_RDONLY | _O_BINARY | _O_RANDOM) 20 #else 21 #include <unistd.h> 22 #define OPEN_FLAGS O_RDONLY 23 #endif 24 25 #include <stdint.h> 26 #include <limits.h> 27 #include <fcntl.h> 28 29 #include "passwdqc.h" 30 #define PASSWDQC_FILTER_INTERNALS 31 #include "passwdqc_filter.h" 32 33 static ssize_t read_loop(int fd, void *buffer, size_t count) 34 { 35 ssize_t offset, block; 36 37 offset = 0; 38 while (count > 0 && count <= SSIZE_MAX) { 39 block = read(fd, (char *)buffer + offset, count); 40 41 if (block < 0) 42 return block; 43 if (!block) 44 return offset; 45 46 offset += block; 47 count -= block; 48 } 49 50 return offset; 51 } 52 53 int passwdqc_filter_open(passwdqc_filter_t *flt, const char *filename) 54 { 55 if ((flt->fd = open(filename, OPEN_FLAGS)) < 0) 56 return -1; 57 58 if (read_loop(flt->fd, &flt->header, sizeof(flt->header)) != sizeof(flt->header) || 59 passwdqc_filter_verify_header(&flt->header) || 60 flt->header.hash_id < PASSWDQC_FILTER_HASH_MIN || flt->header.hash_id > PASSWDQC_FILTER_HASH_MAX || 61 (size_t)lseek(flt->fd, 0, SEEK_END) != sizeof(flt->header) + (flt->header.capacity << 2)) { 62 passwdqc_filter_close(flt); 63 return -1; 64 } 65 66 return 0; 67 } 68 69 int passwdqc_filter_close(passwdqc_filter_t *flt) 70 { 71 int retval = close(flt->fd); 72 flt->fd = -1; 73 return retval; 74 } 75 76 static int check(const passwdqc_filter_t *flt, passwdqc_filter_i_t i, passwdqc_filter_f_t f) 77 { 78 int retval = -1; 79 80 passwdqc_filter_packed_t p; 81 if (lseek(flt->fd, sizeof(flt->header) + (uint64_t)i * sizeof(p), SEEK_SET) < 0 || 82 read_loop(flt->fd, &p, sizeof(p)) != sizeof(p)) 83 goto out; 84 85 passwdqc_filter_unpacked_t u; 86 unsigned int n = (unsigned int)passwdqc_filter_unpack(&u, &p, NULL); 87 if (n > flt->header.bucket_size) 88 goto out; 89 90 unsigned int j; 91 for (j = 0; j < n; j++) { 92 if (passwdqc_filter_f_eq(u.slots[j], f, flt->header.bucket_size)) { 93 retval = 1; 94 goto out; 95 } 96 } 97 98 retval = (n < flt->header.threshold) ? 0 : 2; 99 100 out: 101 _passwdqc_memzero(&p, sizeof(p)); 102 _passwdqc_memzero(&u, sizeof(u)); 103 return retval; 104 } 105 106 int passwdqc_filter_lookup(const passwdqc_filter_t *flt, const char *plaintext) 107 { 108 int retval = 3; 109 passwdqc_filter_hash_t h; 110 passwdqc_filter_f_t ftest; 111 112 clean: 113 switch (flt->header.hash_id) { 114 case PASSWDQC_FILTER_HASH_MD4: 115 passwdqc_filter_md4(&h, plaintext); 116 ftest = 0x8c6420f439de2000ULL; 117 break; 118 case PASSWDQC_FILTER_HASH_NTLM_CP1252: 119 passwdqc_filter_ntlm_cp1252(&h, plaintext); 120 ftest = 0x26bd9256ff7e052eULL; 121 break; 122 default: 123 return -1; 124 } 125 126 uint32_t nbuckets = (uint32_t)(flt->header.capacity >> 2); 127 passwdqc_filter_i_t i = passwdqc_filter_h2i(&h, nbuckets); 128 passwdqc_filter_f_t f = passwdqc_filter_h2f(&h); 129 130 _passwdqc_memzero(&h, sizeof(h)); 131 132 /* 133 * The tests of i and f here not only self-test the code, but also prevent the 134 * compiler from moving "return retval;" to before the computation of h, i, and 135 * f, which would leave sensitive data from the real hash computation around. 136 */ 137 if (i >= nbuckets) 138 return -1; 139 140 if (retval <= 1) { 141 /* Waste two syscalls on overwriting lseek()'s stack and current file offset */ 142 i = passwdqc_filter_h2i(&h, nbuckets); /* 0 */ 143 if (check(flt, i, f) < 0) 144 return -1; 145 if (f != ftest) 146 return -1; 147 return retval; 148 } 149 150 /* At least 1 character to overwrite passwdqc_filter_ntlm_cp1252()'s buffer */ 151 plaintext = " 09AZaz\x7e\x7f\x80\x81\x9e\x9f\xa0\xff"; 152 153 retval = check(flt, i, f); 154 if (retval <= 1) 155 goto clean; 156 157 retval = check(flt, passwdqc_filter_alti(i, f, nbuckets), f); 158 if (retval == 2) 159 retval = 0; 160 goto clean; 161 } 162