1 /*
2    Copyright (C) 2006-2016 Con Kolivas
3    Copyright (C) 2011 Serge Belyshev
4    Copyright (C) 2008, 2011 Peter Hyman
5    Copyright (C) 1998 Andrew Tridgell
6 
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11 
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License
18    along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20 
21 /*
22   Utilities used in rzip
23 
24   tridge, June 1996
25   */
26 
27 /*
28  * Realloc removed
29  * Functions added
30  *    read_config()
31  * Peter Hyman, December 2008
32  */
33 
34 #ifdef HAVE_CONFIG_H
35 # include "config.h"
36 #endif
37 
38 #include <stdarg.h>
39 #ifdef HAVE_UNISTD_H
40 # include <unistd.h>
41 #endif
42 #include <termios.h>
43 
44 #ifdef _SC_PAGE_SIZE
45 # define PAGE_SIZE (sysconf(_SC_PAGE_SIZE))
46 #else
47 # define PAGE_SIZE (4096)
48 #endif
49 
50 #include <sys/types.h>
51 #include <sys/stat.h>
52 #include <sys/mman.h>
53 #include <fcntl.h>
54 #include "lrzip_private.h"
55 #include "util.h"
56 #include "sha4.h"
57 #include "aes.h"
58 #ifdef HAVE_CTYPE_H
59 # include <ctype.h>
60 #endif
61 
62 /* Macros for testing parameters */
63 #define isparameter( parmstring, value )	(!strcasecmp( parmstring, value ))
64 #define iscaseparameter( parmvalue, value )	(!strcmp( parmvalue, value ))
65 
register_infile(rzip_control * control,const char * name,char delete)66 void register_infile(rzip_control *control, const char *name, char delete)
67 {
68 	control->util_infile = name;
69 	control->delete_infile = delete;
70 }
71 
register_outfile(rzip_control * control,const char * name,char delete)72 void register_outfile(rzip_control *control, const char *name, char delete)
73 {
74 	control->util_outfile = name;
75 	control->delete_outfile = delete;
76 }
77 
register_outputfile(rzip_control * control,FILE * f)78 void register_outputfile(rzip_control *control, FILE *f)
79 {
80 	control->outputfile = f;
81 }
82 
unlink_files(rzip_control * control)83 void unlink_files(rzip_control *control)
84 {
85 	/* Delete temporary files generated for testing or faking stdio */
86 	if (control->util_outfile && control->delete_outfile)
87 		unlink(control->util_outfile);
88 
89 	if (control->util_infile && control->delete_infile)
90 		unlink(control->util_infile);
91 }
92 
fatal_exit(rzip_control * control)93 void fatal_exit(rzip_control *control)
94 {
95 	struct termios termios_p;
96 
97 	/* Make sure we haven't died after disabling stdin echo */
98 	tcgetattr(fileno(stdin), &termios_p);
99 	termios_p.c_lflag |= ECHO;
100 	tcsetattr(fileno(stdin), 0, &termios_p);
101 
102 	unlink_files(control);
103 	fprintf(control->outputfile, "Fatal error - exiting\n");
104 	fflush(control->outputfile);
105 	exit(1);
106 }
107 
setup_overhead(rzip_control * control)108 void setup_overhead(rzip_control *control)
109 {
110 	/* Work out the compression overhead per compression thread for the
111 	 * compression back-ends that need a lot of ram */
112 	if (LZMA_COMPRESS) {
113 		int level = control->compression_level * 7 / 9;
114 
115 		if (!level)
116 			level = 1;
117 		i64 dictsize = (level <= 5 ? (1 << (level * 2 + 14)) :
118 				(level == 6 ? (1 << 25) : (1 << 26)));
119 
120 		control->overhead = (dictsize * 23 / 2) + (4 * 1024 * 1024);
121 	} else if (ZPAQ_COMPRESS)
122 		control->overhead = 112 * 1024 * 1024;
123 }
124 
setup_ram(rzip_control * control)125 void setup_ram(rzip_control *control)
126 {
127 	/* Use less ram when using STDOUT to store the temporary output file. */
128 	if (STDOUT && ((STDIN && DECOMPRESS) || !(DECOMPRESS || TEST_ONLY)))
129 		control->maxram = control->ramsize / 6;
130 	else
131 		control->maxram = control->ramsize / 3;
132 	if (BITS32) {
133 		/* Decrease usable ram size on 32 bits due to kernel /
134 		 * userspace split. Cannot allocate larger than a 1
135 		 * gigabyte chunk due to 32 bit signed long being
136 		 * used in alloc, and at most 3GB can be malloced, and
137 		 * 2/3 of that makes for a total of 2GB to be split
138 		 * into thirds.
139 		 */
140 		control->usable_ram = MAX(control->ramsize - 900000000ll, 900000000ll);
141 		control->maxram = MIN(control->maxram, control->usable_ram);
142 		control->maxram = MIN(control->maxram, one_g * 2 / 3);
143 	} else
144 		control->usable_ram = control->maxram;
145 	round_to_page(&control->maxram);
146 }
147 
round_to_page(i64 * size)148 void round_to_page(i64 *size)
149 {
150 	*size -= *size % PAGE_SIZE;
151 	if (unlikely(!*size))
152 		*size = PAGE_SIZE;
153 }
154 
round_up_page(rzip_control * control,size_t len)155 size_t round_up_page(rzip_control *control, size_t len)
156 {
157 	int rem = len % control->page_size;
158 
159 	if (rem)
160 		len += control->page_size - rem;
161 	return len;
162 }
163 
get_rand(rzip_control * control,uchar * buf,int len)164 bool get_rand(rzip_control *control, uchar *buf, int len)
165 {
166 	int fd, i;
167 
168 	fd = open("/dev/urandom", O_RDONLY);
169 	if (fd == -1) {
170 		for (i = 0; i < len; i++)
171 			buf[i] = (uchar)random();
172 	} else {
173 		if (unlikely(read(fd, buf, len) != len))
174 			fatal_return(("Failed to read fd in get_rand\n"), false);
175 		if (unlikely(close(fd)))
176 			fatal_return(("Failed to close fd in get_rand\n"), false);
177 	}
178 	return true;
179 }
180 
read_config(rzip_control * control)181 bool read_config(rzip_control *control)
182 {
183 	/* check for lrzip.conf in ., $HOME/.lrzip and /etc/lrzip */
184 	char *HOME, homeconf[255];
185 	char *parametervalue;
186 	char *parameter;
187 	char line[255];
188 	FILE *fp;
189 
190 	fp = fopen("lrzip.conf", "r");
191 	if (fp)
192 		fprintf(control->msgout, "Using configuration file ./lrzip.conf\n");
193 	if (fp == NULL) {
194 		HOME=getenv("HOME");
195 		if (HOME) {
196 			snprintf(homeconf, sizeof(homeconf), "%s/.lrzip/lrzip.conf", HOME);
197 			fp = fopen(homeconf, "r");
198 			if (fp)
199 				fprintf(control->msgout, "Using configuration file %s\n", homeconf);
200 		}
201 	}
202 	if (fp == NULL) {
203 		fp = fopen("/etc/lrzip/lrzip.conf", "r");
204 		if (fp)
205 			fprintf(control->msgout, "Using configuration file /etc/lrzip/lrzip.conf\n");
206 	}
207 	if (fp == NULL)
208 		return true;
209 
210 	/* if we get here, we have a file. read until no more. */
211 
212 	while ((fgets(line, 255, fp)) != NULL) {
213 		if (strlen(line))
214 			line[strlen(line) - 1] = '\0';
215 		parameter = strtok(line, " =");
216 		if (parameter == NULL)
217 			continue;
218 		/* skip if whitespace or # */
219 		if (isspace(*parameter))
220 			continue;
221 		if (*parameter == '#')
222 			continue;
223 
224 		parametervalue = strtok(NULL, " =");
225 		if (parametervalue == NULL)
226 			continue;
227 
228 		/* have valid parameter line, now assign to control */
229 
230 		if (isparameter(parameter, "window"))
231 			control->window = atoi(parametervalue);
232 		else if (isparameter(parameter, "unlimited")) {
233 			if (isparameter(parametervalue, "yes"))
234 				control->flags |= FLAG_UNLIMITED;
235 		} else if (isparameter(parameter, "compressionlevel")) {
236 			control->compression_level = atoi(parametervalue);
237 			if ( control->compression_level < 1 || control->compression_level > 9 )
238 				failure_return(("CONF.FILE error. Compression Level must between 1 and 9"), false);
239 		} else if (isparameter(parameter, "compressionmethod")) {
240 			/* valid are rzip, gzip, bzip2, lzo, lzma (default), and zpaq */
241 			if (control->flags & FLAG_NOT_LZMA)
242 				failure_return(("CONF.FILE error. Can only specify one compression method"), false);
243 			if (isparameter(parametervalue, "bzip2"))
244 				control->flags |= FLAG_BZIP2_COMPRESS;
245 			else if (isparameter(parametervalue, "gzip"))
246 				control->flags |= FLAG_ZLIB_COMPRESS;
247 			else if (isparameter(parametervalue, "lzo"))
248 				control->flags |= FLAG_LZO_COMPRESS;
249 			else if (isparameter(parametervalue, "rzip"))
250 				control->flags |= FLAG_NO_COMPRESS;
251 			else if (isparameter(parametervalue, "zpaq"))
252 				control->flags |= FLAG_ZPAQ_COMPRESS;
253 			else if (!isparameter(parametervalue, "lzma")) /* oops, not lzma! */
254 				failure_return(("CONF.FILE error. Invalid compression method %s specified\n",parametervalue), false);
255 		} else if (isparameter(parameter, "lzotest")) {
256 			/* default is yes */
257 			if (isparameter(parametervalue, "no"))
258 				control->flags &= ~FLAG_THRESHOLD;
259 		} else if (isparameter(parameter, "hashcheck")) {
260 			if (isparameter(parametervalue, "yes")) {
261 				control->flags |= FLAG_CHECK;
262 				control->flags |= FLAG_HASH;
263 			}
264 		} else if (isparameter(parameter, "showhash")) {
265 			if (isparameter(parametervalue, "yes"))
266 				control->flags |= FLAG_HASH;
267 		} else if (isparameter(parameter, "outputdirectory")) {
268 			control->outdir = malloc(strlen(parametervalue) + 2);
269 			if (!control->outdir)
270 				fatal_return(("Fatal Memory Error in read_config"), false);
271 			strcpy(control->outdir, parametervalue);
272 			if (strcmp(parametervalue + strlen(parametervalue) - 1, "/"))
273 				strcat(control->outdir, "/");
274 		} else if (isparameter(parameter,"verbosity")) {
275 			if (control->flags & FLAG_VERBOSE)
276 				failure_return(("CONF.FILE error. Verbosity already defined."), false);
277 			if (isparameter(parametervalue, "yes"))
278 				control->flags |= FLAG_VERBOSITY;
279 			else if (isparameter(parametervalue,"max"))
280 				control->flags |= FLAG_VERBOSITY_MAX;
281 			else /* oops, unrecognized value */
282 				print_err("lrzip.conf: Unrecognized verbosity value %s. Ignored.\n", parametervalue);
283 		} else if (isparameter(parameter, "showprogress")) {
284 			/* Yes by default */
285 			if (isparameter(parametervalue, "NO"))
286 				control->flags &= ~FLAG_SHOW_PROGRESS;
287 		} else if (isparameter(parameter,"nice")) {
288 			control->nice_val = atoi(parametervalue);
289 			if (control->nice_val < -20 || control->nice_val > 19)
290 				failure_return(("CONF.FILE error. Nice must be between -20 and 19"), false);
291 		} else if (isparameter(parameter, "keepbroken")) {
292 			if (isparameter(parametervalue, "yes" ))
293 				control->flags |= FLAG_KEEP_BROKEN;
294 		} else if (iscaseparameter(parameter, "DELETEFILES")) {
295 			/* delete files must be case sensitive */
296 			if (iscaseparameter(parametervalue, "YES"))
297 				control->flags &= ~FLAG_KEEP_FILES;
298 		} else if (iscaseparameter(parameter, "REPLACEFILE")) {
299 			/* replace lrzip file must be case sensitive */
300 			if (iscaseparameter(parametervalue, "YES"))
301 				control->flags |= FLAG_FORCE_REPLACE;
302 		} else if (isparameter(parameter, "tmpdir")) {
303 			control->tmpdir = realloc(NULL, strlen(parametervalue) + 2);
304 			if (!control->tmpdir)
305 				fatal_return(("Fatal Memory Error in read_config"), false);
306 			strcpy(control->tmpdir, parametervalue);
307 			if (strcmp(parametervalue + strlen(parametervalue) - 1, "/"))
308 				strcat(control->tmpdir, "/");
309 		} else if (isparameter(parameter, "encrypt")) {
310 			if (isparameter(parameter, "YES"))
311 				control->flags |= FLAG_ENCRYPT;
312 		} else
313 			/* oops, we have an invalid parameter, display */
314 			print_err("lrzip.conf: Unrecognized parameter value, %s = %s. Continuing.\n",\
315 				       parameter, parametervalue);
316 	}
317 
318 	if (unlikely(fclose(fp)))
319 		fatal_return(("Failed to fclose fp in read_config\n"), false);
320 
321 /*	fprintf(stderr, "\nWindow = %d \
322 		\nCompression Level = %d \
323 		\nThreshold = %1.2f \
324 		\nOutput Directory = %s \
325 		\nFlags = %d\n", control->window,control->compression_level, control->threshold, control->outdir, control->flags);
326 */
327 	return true;
328 }
329 
xor128(void * pa,const void * pb)330 static void xor128 (void *pa, const void *pb)
331 {
332 	i64 *a = pa;
333 	const i64 *b = pb;
334 
335 	a [0] ^= b [0];
336 	a [1] ^= b [1];
337 }
338 
lrz_keygen(const rzip_control * control,const uchar * salt,uchar * key,uchar * iv)339 static void lrz_keygen(const rzip_control *control, const uchar *salt, uchar *key, uchar *iv)
340 {
341 	uchar buf [HASH_LEN + SALT_LEN + PASS_LEN];
342 	mlock(buf, HASH_LEN + SALT_LEN + PASS_LEN);
343 
344 	memcpy(buf, control->hash, HASH_LEN);
345 	memcpy(buf + HASH_LEN, salt, SALT_LEN);
346 	memcpy(buf + HASH_LEN + SALT_LEN, control->salt_pass, control->salt_pass_len);
347 	sha4(buf, HASH_LEN + SALT_LEN + control->salt_pass_len, key, 0);
348 
349 	memcpy(buf, key, HASH_LEN);
350 	memcpy(buf + HASH_LEN, salt, SALT_LEN);
351 	memcpy(buf + HASH_LEN + SALT_LEN, control->salt_pass, control->salt_pass_len);
352 	sha4(buf, HASH_LEN + SALT_LEN + control->salt_pass_len, iv, 0);
353 
354 	memset(buf, 0, sizeof(buf));
355 	munlock(buf, sizeof(buf));
356 }
357 
lrz_crypt(const rzip_control * control,uchar * buf,i64 len,const uchar * salt,int encrypt)358 bool lrz_crypt(const rzip_control *control, uchar *buf, i64 len, const uchar *salt, int encrypt)
359 {
360 	/* Encryption requires CBC_LEN blocks so we can use ciphertext
361 	* stealing to not have to pad the block */
362 	uchar key[HASH_LEN], iv[HASH_LEN];
363 	uchar tmp0[CBC_LEN], tmp1[CBC_LEN];
364 	aes_context aes_ctx;
365 	i64 N, M;
366 	bool ret = false;
367 
368 	/* Generate unique key and IV for each block of data based on salt */
369 	mlock(&aes_ctx, sizeof(aes_ctx));
370 	mlock(key, HASH_LEN);
371 	mlock(iv, HASH_LEN);
372 
373 	lrz_keygen(control, salt, key, iv);
374 
375 	M = len % CBC_LEN;
376 	N = len - M;
377 
378 	if (encrypt == LRZ_ENCRYPT) {
379 		print_maxverbose("Encrypting data        \n");
380 		if (unlikely(aes_setkey_enc(&aes_ctx, key, 128)))
381 			failure_goto(("Failed to aes_setkey_enc in lrz_crypt\n"), error);
382 		aes_crypt_cbc(&aes_ctx, AES_ENCRYPT, N, iv, buf, buf);
383 
384 		if (M) {
385 			memset(tmp0, 0, CBC_LEN);
386 			memcpy(tmp0, buf + N, M);
387 			aes_crypt_cbc(&aes_ctx, AES_ENCRYPT, CBC_LEN,
388 				iv, tmp0, tmp1);
389 			memcpy(buf + N, buf + N - CBC_LEN, M);
390 			memcpy(buf + N - CBC_LEN, tmp1, CBC_LEN);
391 		}
392 	} else {
393 		if (unlikely(aes_setkey_dec(&aes_ctx, key, 128)))
394 			failure_goto(("Failed to aes_setkey_dec in lrz_crypt\n"), error);
395 		print_maxverbose("Decrypting data        \n");
396 		if (M) {
397 			aes_crypt_cbc(&aes_ctx, AES_DECRYPT, N - CBC_LEN,
398 				      iv, buf, buf);
399 			aes_crypt_ecb(&aes_ctx, AES_DECRYPT,
400 				      buf + N - CBC_LEN, tmp0);
401 			memset(tmp1, 0, CBC_LEN);
402 			memcpy(tmp1, buf + N, M);
403 			xor128(tmp0, tmp1);
404 			memcpy(buf + N, tmp0, M);
405 			memcpy(tmp1 + M, tmp0 + M, CBC_LEN - M);
406 			aes_crypt_ecb(&aes_ctx, AES_DECRYPT, tmp1,
407 				      buf + N - CBC_LEN);
408 			xor128(buf + N - CBC_LEN, iv);
409 		} else
410 			aes_crypt_cbc(&aes_ctx, AES_DECRYPT, len,
411 				      iv, buf, buf);
412 	}
413 
414 	ret = true;
415 error:
416 	memset(&aes_ctx, 0, sizeof(aes_ctx));
417 	memset(iv, 0, HASH_LEN);
418 	memset(key, 0, HASH_LEN);
419 	munlock(&aes_ctx, sizeof(aes_ctx));
420 	munlock(iv, HASH_LEN);
421 	munlock(key, HASH_LEN);
422 	return ret;
423 }
424 
lrz_stretch(rzip_control * control)425 void lrz_stretch(rzip_control *control)
426 {
427 	sha4_context ctx;
428 	i64 j, n, counter;
429 
430 	mlock(&ctx, sizeof(ctx));
431 	sha4_starts(&ctx, 0);
432 
433 	n = control->encloops * HASH_LEN / (control->salt_pass_len + sizeof(i64));
434 	print_maxverbose("Hashing passphrase %lld (%lld) times \n", control->encloops, n);
435 	for (j = 0; j < n; j ++) {
436 		counter = htole64(j);
437 		sha4_update(&ctx, (uchar *)&counter, sizeof(counter));
438 		sha4_update(&ctx, control->salt_pass, control->salt_pass_len);
439 	}
440 	sha4_finish(&ctx, control->hash);
441 	memset(&ctx, 0, sizeof(ctx));
442 	munlock(&ctx, sizeof(ctx));
443 }
444