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