1 /*
2  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3  *
4  * This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
7  *
8  * See the COPYRIGHT file distributed with this work for additional
9  * information regarding copyright ownership.
10  */
11 
12 
13 #include <config.h>
14 
15 #undef rename
16 #include <errno.h>
17 #include <limits.h>
18 #include <stdbool.h>
19 #include <stdlib.h>
20 #include <io.h>
21 #include <process.h>
22 
23 #include <sys/stat.h>
24 #include <fcntl.h>
25 #include <sys/utime.h>
26 
27 #include <isc/file.h>
28 #include <isc/mem.h>
29 #include <isc/platform.h>
30 #include <isc/print.h>
31 #include <isc/random.h>
32 #include <isc/result.h>
33 #include <isc/sha2.h>
34 #include <isc/stat.h>
35 #include <isc/string.h>
36 #include <isc/time.h>
37 #include <isc/util.h>
38 
39 #include "errno2result.h"
40 
41 static const char alphnum[] =
42 	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
43 
44 /*
45  * Emulate UNIX mkstemp, which returns an open FD to the new file
46  *
47  */
48 static int
gettemp(char * path,bool binary,int * doopen)49 gettemp(char *path, bool binary, int *doopen) {
50 	char *start, *trv;
51 	struct stat sbuf;
52 	int flags = O_CREAT|O_EXCL|O_RDWR;
53 
54 	if (binary)
55 		flags |= _O_BINARY;
56 
57 	trv = strrchr(path, 'X');
58 	trv++;
59 	/* extra X's get set to 0's */
60 	while (*--trv == 'X') {
61 		uint32_t which;
62 
63 		isc_random_get(&which);
64 		*trv = alphnum[which % (sizeof(alphnum) - 1)];
65 	}
66 	/*
67 	 * check the target directory; if you have six X's and it
68 	 * doesn't exist this runs for a *very* long time.
69 	 */
70 	for (start = trv + 1;; --trv) {
71 		if (trv <= path)
72 			break;
73 		if (*trv == '\\') {
74 			*trv = '\0';
75 			if (stat(path, &sbuf))
76 				return (0);
77 			if (!S_ISDIR(sbuf.st_mode)) {
78 				errno = ENOTDIR;
79 				return (0);
80 			}
81 			*trv = '\\';
82 			break;
83 		}
84 	}
85 
86 	for (;;) {
87 		if (doopen) {
88 			if ((*doopen =
89 			    open(path, flags, _S_IREAD | _S_IWRITE)) >= 0)
90 				return (1);
91 			if (errno != EEXIST)
92 				return (0);
93 		} else if (stat(path, &sbuf))
94 			return (errno == ENOENT ? 1 : 0);
95 
96 		/* tricky little algorithm for backward compatibility */
97 		for (trv = start;;) {
98 			if (!*trv)
99 				return (0);
100 			if (*trv == 'z')
101 				*trv++ = 'a';
102 			else {
103 				if (isdigit(*trv))
104 					*trv = 'a';
105 				else
106 					++*trv;
107 				break;
108 			}
109 		}
110 	}
111 	/*NOTREACHED*/
112 }
113 
114 static int
mkstemp(char * path,bool binary)115 mkstemp(char *path, bool binary) {
116 	int fd;
117 
118 	return (gettemp(path, binary, &fd) ? fd : -1);
119 }
120 
121 /*
122  * XXXDCL As the API for accessing file statistics undoubtedly gets expanded,
123  * it might be good to provide a mechanism that allows for the results
124  * of a previous stat() to be used again without having to do another stat,
125  * such as perl's mechanism of using "_" in place of a file name to indicate
126  * that the results of the last stat should be used.  But then you get into
127  * annoying MP issues.   BTW, Win32 has stat().
128  */
129 static isc_result_t
file_stats(const char * file,struct stat * stats)130 file_stats(const char *file, struct stat *stats) {
131 	isc_result_t result = ISC_R_SUCCESS;
132 
133 	REQUIRE(file != NULL);
134 	REQUIRE(stats != NULL);
135 
136 	if (stat(file, stats) != 0)
137 		result = isc__errno2result(errno);
138 
139 	return (result);
140 }
141 
142 static isc_result_t
fd_stats(int fd,struct stat * stats)143 fd_stats(int fd, struct stat *stats) {
144 	isc_result_t result = ISC_R_SUCCESS;
145 
146 	REQUIRE(stats != NULL);
147 
148 	if (fstat(fd, stats) != 0)
149 		result = isc__errno2result(errno);
150 
151 	return (result);
152 }
153 
154 isc_result_t
isc_file_getsizefd(int fd,off_t * size)155 isc_file_getsizefd(int fd, off_t *size) {
156 	isc_result_t result;
157 	struct stat stats;
158 
159 	REQUIRE(size != NULL);
160 
161 	result = fd_stats(fd, &stats);
162 
163 	if (result == ISC_R_SUCCESS)
164 		*size = stats.st_size;
165 	return (result);
166 }
167 
168 isc_result_t
isc_file_mode(const char * file,mode_t * modep)169 isc_file_mode(const char *file, mode_t *modep) {
170 	isc_result_t result;
171 	struct stat stats;
172 
173 	REQUIRE(modep != NULL);
174 
175 	result = file_stats(file, &stats);
176 	if (result == ISC_R_SUCCESS)
177 		*modep = (stats.st_mode & 07777);
178 	return (result);
179 }
180 
181 /*
182  * isc_file_safemovefile is needed to be defined here to ensure that
183  * any file with the new name is renamed to a backup name and then the
184  * rename is done. If all goes well then the backup can be deleted,
185  * otherwise it gets renamed back.
186  */
187 
188 int
isc_file_safemovefile(const char * oldname,const char * newname)189 isc_file_safemovefile(const char *oldname, const char *newname) {
190 	BOOL filestatus;
191 	char buf[512];
192 	struct stat sbuf;
193 	BOOL exists = FALSE;
194 	int tmpfd;
195 
196 	/*
197 	 * Make sure we have something to do
198 	 */
199 	if (stat(oldname, &sbuf) != 0) {
200 		errno = ENOENT;
201 		return (-1);
202 	}
203 
204 	/*
205 	 * Rename to a backup the new file if it still exists
206 	 */
207 	if (stat(newname, &sbuf) == 0) {
208 		exists = TRUE;
209 		strlcpy(buf, newname, sizeof(buf));
210 		strlcat(buf, ".XXXXX", sizeof(buf));
211 		tmpfd = mkstemp(buf, true);
212 		if (tmpfd > 0)
213 			_close(tmpfd);
214 		(void)DeleteFile(buf);
215 		_chmod(newname, _S_IREAD | _S_IWRITE);
216 
217 		filestatus = MoveFile(newname, buf);
218 	}
219 	/* Now rename the file to the new name
220 	 */
221 	_chmod(oldname, _S_IREAD | _S_IWRITE);
222 
223 	filestatus = MoveFile(oldname, newname);
224 	if (filestatus == 0) {
225 		/*
226 		 * Try to rename the backup back to the original name
227 		 * if the backup got created
228 		 */
229 		if (exists == TRUE) {
230 			filestatus = MoveFile(buf, newname);
231 			if (filestatus == 0)
232 				errno = EACCES;
233 		}
234 		return (-1);
235 	}
236 
237 	/*
238 	 * Delete the backup file if it got created
239 	 */
240 	if (exists == TRUE)
241 		(void)DeleteFile(buf);
242 	return (0);
243 }
244 
245 isc_result_t
isc_file_getmodtime(const char * file,isc_time_t * time)246 isc_file_getmodtime(const char *file, isc_time_t *time) {
247 	int fh;
248 
249 	REQUIRE(file != NULL);
250 	REQUIRE(time != NULL);
251 
252 	if ((fh = open(file, _O_RDONLY | _O_BINARY)) < 0)
253 		return (isc__errno2result(errno));
254 
255 	if (!GetFileTime((HANDLE) _get_osfhandle(fh),
256 			 NULL,
257 			 NULL,
258 			 &time->absolute))
259 	{
260 		close(fh);
261 		errno = EINVAL;
262 		return (isc__errno2result(errno));
263 	}
264 	close(fh);
265 	return (ISC_R_SUCCESS);
266 }
267 
268 isc_result_t
isc_file_getsize(const char * file,off_t * size)269 isc_file_getsize(const char *file, off_t *size) {
270 	isc_result_t result;
271 	struct stat stats;
272 
273 	REQUIRE(file != NULL);
274 	REQUIRE(size != NULL);
275 
276 	result = file_stats(file, &stats);
277 
278 	if (result == ISC_R_SUCCESS)
279 		*size = stats.st_size;
280 
281 	return (result);
282 }
283 
284 isc_result_t
isc_file_settime(const char * file,isc_time_t * time)285 isc_file_settime(const char *file, isc_time_t *time) {
286 	int fh;
287 
288 	REQUIRE(file != NULL && time != NULL);
289 
290 	if ((fh = open(file, _O_RDWR | _O_BINARY)) < 0)
291 		return (isc__errno2result(errno));
292 
293 	/*
294 	 * Set the date via the filedate system call and return.  Failing
295 	 * this call implies the new file times are not supported by the
296 	 * underlying file system.
297 	 */
298 	if (!SetFileTime((HANDLE) _get_osfhandle(fh),
299 			 NULL,
300 			 &time->absolute,
301 			 &time->absolute))
302 	{
303 		close(fh);
304 		errno = EINVAL;
305 		return (isc__errno2result(errno));
306 	}
307 
308 	close(fh);
309 	return (ISC_R_SUCCESS);
310 
311 }
312 
313 #undef TEMPLATE
314 #define TEMPLATE "XXXXXXXXXX.tmp" /* 14 characters. */
315 
316 isc_result_t
isc_file_mktemplate(const char * path,char * buf,size_t buflen)317 isc_file_mktemplate(const char *path, char *buf, size_t buflen) {
318 	return (isc_file_template(path, TEMPLATE, buf, buflen));
319 }
320 
321 isc_result_t
isc_file_template(const char * path,const char * templet,char * buf,size_t buflen)322 isc_file_template(const char *path, const char *templet, char *buf,
323 		  size_t buflen)
324 {
325 	char *s;
326 
327 	REQUIRE(templet != NULL);
328 	REQUIRE(buf != NULL);
329 
330 	if (path == NULL)
331 		path = "";
332 
333 	s = strrchr(templet, '\\');
334 	if (s != NULL)
335 		templet = s + 1;
336 
337 	s = strrchr(path, '\\');
338 
339 	if (s != NULL) {
340 		size_t prefixlen = s - path + 1;
341 		if ((prefixlen + strlen(templet) + 1) > buflen)
342 			return (ISC_R_NOSPACE);
343 
344 		/* Copy 'prefixlen' bytes and NUL terminate. */
345 		strlcpy(buf, path, ISC_MIN(prefixlen + 1, buflen));
346 		strlcat(buf, templet, buflen);
347 	} else {
348 		if ((strlen(templet) + 1) > buflen)
349 			return (ISC_R_NOSPACE);
350 
351 		strlcpy(buf, templet, buflen);
352 	}
353 
354 	return (ISC_R_SUCCESS);
355 }
356 
357 isc_result_t
isc_file_renameunique(const char * file,char * templet)358 isc_file_renameunique(const char *file, char *templet) {
359 	int fd;
360 	isc_result_t result = ISC_R_SUCCESS;
361 
362 	REQUIRE(file != NULL);
363 	REQUIRE(templet != NULL);
364 
365 	fd = mkstemp(templet, true);
366 	if (fd == -1)
367 		result = isc__errno2result(errno);
368 	else
369 		close(fd);
370 
371 	if (result == ISC_R_SUCCESS) {
372 		int res;
373 		res = isc_file_safemovefile(file, templet);
374 		if (res != 0) {
375 			result = isc__errno2result(errno);
376 			(void)unlink(templet);
377 		}
378 	}
379 	return (result);
380 }
381 
382 static isc_result_t
openuniquemode(char * templet,int mode,bool binary,FILE ** fp)383 openuniquemode(char *templet, int mode, bool binary, FILE **fp) {
384 	int fd;
385 	FILE *f;
386 	isc_result_t result = ISC_R_SUCCESS;
387 
388 	REQUIRE(templet != NULL);
389 	REQUIRE(fp != NULL && *fp == NULL);
390 
391 	/*
392 	 * Win32 does not have mkstemp. Using emulation above.
393 	 */
394 	fd = mkstemp(templet, binary);
395 
396 	if (fd == -1)
397 		result = isc__errno2result(errno);
398 	if (result == ISC_R_SUCCESS) {
399 #if 1
400 		UNUSED(mode);
401 #else
402 		(void)fchmod(fd, mode);
403 #endif
404 		f = fdopen(fd, binary ? "wb+" : "w+");
405 		if (f == NULL) {
406 			result = isc__errno2result(errno);
407 			(void)remove(templet);
408 			(void)close(fd);
409 		} else
410 			*fp = f;
411 	}
412 
413 	return (result);
414 }
415 
416 isc_result_t
isc_file_openuniqueprivate(char * templet,FILE ** fp)417 isc_file_openuniqueprivate(char *templet, FILE **fp) {
418 	int mode = _S_IREAD | _S_IWRITE;
419 	return (openuniquemode(templet, mode, false, fp));
420 }
421 
422 isc_result_t
isc_file_openunique(char * templet,FILE ** fp)423 isc_file_openunique(char *templet, FILE **fp) {
424 	int mode = _S_IREAD | _S_IWRITE;
425 	return (openuniquemode(templet, mode, false, fp));
426 }
427 
428 isc_result_t
isc_file_openuniquemode(char * templet,int mode,FILE ** fp)429 isc_file_openuniquemode(char *templet, int mode, FILE **fp) {
430 	return (openuniquemode(templet, mode, false, fp));
431 }
432 
433 isc_result_t
isc_file_bopenuniqueprivate(char * templet,FILE ** fp)434 isc_file_bopenuniqueprivate(char *templet, FILE **fp) {
435 	int mode = _S_IREAD | _S_IWRITE;
436 	return (openuniquemode(templet, mode, true, fp));
437 }
438 
439 isc_result_t
isc_file_bopenunique(char * templet,FILE ** fp)440 isc_file_bopenunique(char *templet, FILE **fp) {
441 	int mode = _S_IREAD | _S_IWRITE;
442 	return (openuniquemode(templet, mode, true, fp));
443 }
444 
445 isc_result_t
isc_file_bopenuniquemode(char * templet,int mode,FILE ** fp)446 isc_file_bopenuniquemode(char *templet, int mode, FILE **fp) {
447 	return (openuniquemode(templet, mode, true, fp));
448 }
449 
450 isc_result_t
isc_file_remove(const char * filename)451 isc_file_remove(const char *filename) {
452 	int r;
453 
454 	REQUIRE(filename != NULL);
455 
456 	r = unlink(filename);
457 	if (r == 0)
458 		return (ISC_R_SUCCESS);
459 	else
460 		return (isc__errno2result(errno));
461 }
462 
463 isc_result_t
isc_file_rename(const char * oldname,const char * newname)464 isc_file_rename(const char *oldname, const char *newname) {
465 	int r;
466 
467 	REQUIRE(oldname != NULL);
468 	REQUIRE(newname != NULL);
469 
470 	r = isc_file_safemovefile(oldname, newname);
471 	if (r == 0)
472 		return (ISC_R_SUCCESS);
473 	else
474 		return (isc__errno2result(errno));
475 }
476 
477 bool
isc_file_exists(const char * pathname)478 isc_file_exists(const char *pathname) {
479 	struct stat stats;
480 
481 	REQUIRE(pathname != NULL);
482 
483 	return (file_stats(pathname, &stats) == ISC_R_SUCCESS);
484 }
485 
486 isc_result_t
isc_file_isplainfile(const char * filename)487 isc_file_isplainfile(const char *filename) {
488 	/*
489 	 * This function returns success if filename is a plain file.
490 	 */
491 	struct stat filestat;
492 	memset(&filestat,0,sizeof(struct stat));
493 
494 	if ((stat(filename, &filestat)) == -1)
495 		return(isc__errno2result(errno));
496 
497 	if(! S_ISREG(filestat.st_mode))
498 		return(ISC_R_INVALIDFILE);
499 
500 	return(ISC_R_SUCCESS);
501 }
502 
503 isc_result_t
isc_file_isplainfilefd(int fd)504 isc_file_isplainfilefd(int fd) {
505 	/*
506 	 * This function returns success if filename is a plain file.
507 	 */
508 	struct stat filestat;
509 	memset(&filestat,0,sizeof(struct stat));
510 
511 	if ((fstat(fd, &filestat)) == -1)
512 		return(isc__errno2result(errno));
513 
514 	if(! S_ISREG(filestat.st_mode))
515 		return(ISC_R_INVALIDFILE);
516 
517 	return(ISC_R_SUCCESS);
518 }
519 
520 isc_result_t
isc_file_isdirectory(const char * filename)521 isc_file_isdirectory(const char *filename) {
522 	/*
523 	 * This function returns success if filename is a directory.
524 	 */
525 	struct stat filestat;
526 	memset(&filestat,0,sizeof(struct stat));
527 
528 	if ((stat(filename, &filestat)) == -1)
529 		return(isc__errno2result(errno));
530 
531 	if(! S_ISDIR(filestat.st_mode))
532 		return(ISC_R_INVALIDFILE);
533 
534 	return(ISC_R_SUCCESS);
535 }
536 
537 
538 bool
isc_file_isabsolute(const char * filename)539 isc_file_isabsolute(const char *filename) {
540 	REQUIRE(filename != NULL);
541 	/*
542 	 * Look for c:\path\... style, c:/path/... or \\computer\shar\path...
543 	 * the UNC style file specs
544 	 */
545 	if ((filename[0] == '\\') && (filename[1] == '\\'))
546 		return (true);
547 	if (isalpha(filename[0]) && filename[1] == ':' && filename[2] == '\\')
548 		return (true);
549 	if (isalpha(filename[0]) && filename[1] == ':' && filename[2] == '/')
550 		return (true);
551 	return (false);
552 }
553 
554 bool
isc_file_iscurrentdir(const char * filename)555 isc_file_iscurrentdir(const char *filename) {
556 	REQUIRE(filename != NULL);
557 	return (filename[0] == '.' && filename[1] == '\0');
558 }
559 
560 bool
isc_file_ischdiridempotent(const char * filename)561 isc_file_ischdiridempotent(const char *filename) {
562 	REQUIRE(filename != NULL);
563 
564 	if (isc_file_isabsolute(filename))
565 		return (true);
566 	if (filename[0] == '\\')
567 		return (true);
568 	if (filename[0] == '/')
569 		return (true);
570 	if (isc_file_iscurrentdir(filename))
571 		return (true);
572 	return (false);
573 }
574 
575 const char *
isc_file_basename(const char * filename)576 isc_file_basename(const char *filename) {
577 	char *s;
578 
579 	REQUIRE(filename != NULL);
580 
581 	s = strrchr(filename, '\\');
582 	if (s == NULL)
583 		return (filename);
584 	return (s + 1);
585 }
586 
587 isc_result_t
isc_file_progname(const char * filename,char * progname,size_t namelen)588 isc_file_progname(const char *filename, char *progname, size_t namelen) {
589 	const char *s;
590 	const char *p;
591 	size_t len;
592 
593 	REQUIRE(filename != NULL);
594 	REQUIRE(progname != NULL);
595 
596 	/*
597 	 * Strip the path from the name
598 	 */
599 	s = isc_file_basename(filename);
600 	if (s == NULL) {
601 		return (ISC_R_NOSPACE);
602 	}
603 
604 	/*
605 	 * Strip any and all suffixes
606 	 */
607 	p = strchr(s, '.');
608 	if (p == NULL) {
609 		if (namelen <= strlen(s))
610 			return (ISC_R_NOSPACE);
611 
612 		strlcpy(progname, s, namelen);
613 		return (ISC_R_SUCCESS);
614 	}
615 
616 	/*
617 	 * Copy the result to the buffer
618 	 */
619 	len = p - s;
620 	if (len >= namelen)
621 		return (ISC_R_NOSPACE);
622 
623 	/* Copy up to 'len' bytes and NUL terminate. */
624 	strlcpy(progname, s, ISC_MIN(len + 1, namelen));
625 	return (ISC_R_SUCCESS);
626 }
627 
628 isc_result_t
isc_file_absolutepath(const char * filename,char * path,size_t pathlen)629 isc_file_absolutepath(const char *filename, char *path, size_t pathlen) {
630 	char *ptrname;
631 	DWORD retval;
632 
633 	REQUIRE(filename != NULL);
634 	REQUIRE(path != NULL);
635 
636 	retval = GetFullPathName(filename, (DWORD) pathlen, path, &ptrname);
637 
638 	/* Something went wrong in getting the path */
639 	if (retval == 0)
640 		return (ISC_R_NOTFOUND);
641 	/* Caller needs to provide a larger buffer to contain the string */
642 	if (retval >= pathlen)
643 		return (ISC_R_NOSPACE);
644 	return (ISC_R_SUCCESS);
645 }
646 
647 isc_result_t
isc_file_truncate(const char * filename,isc_offset_t size)648 isc_file_truncate(const char *filename, isc_offset_t size) {
649 	int fh;
650 
651 	REQUIRE(filename != NULL && size >= 0);
652 
653 	if ((fh = open(filename, _O_RDWR | _O_BINARY)) < 0)
654 		return (isc__errno2result(errno));
655 
656 	if(_chsize(fh, size) != 0) {
657 		close(fh);
658 		return (isc__errno2result(errno));
659 	}
660 	close(fh);
661 
662 	return (ISC_R_SUCCESS);
663 }
664 
665 isc_result_t
isc_file_safecreate(const char * filename,FILE ** fp)666 isc_file_safecreate(const char *filename, FILE **fp) {
667 	isc_result_t result;
668 	int flags;
669 	struct stat sb;
670 	FILE *f;
671 	int fd;
672 
673 	REQUIRE(filename != NULL);
674 	REQUIRE(fp != NULL && *fp == NULL);
675 
676 	result = file_stats(filename, &sb);
677 	if (result == ISC_R_SUCCESS) {
678 		if ((sb.st_mode & S_IFREG) == 0)
679 			return (ISC_R_INVALIDFILE);
680 		flags = O_WRONLY | O_TRUNC;
681 	} else if (result == ISC_R_FILENOTFOUND) {
682 		flags = O_WRONLY | O_CREAT | O_EXCL;
683 	} else
684 		return (result);
685 
686 	fd = open(filename, flags, S_IRUSR | S_IWUSR);
687 	if (fd == -1)
688 		return (isc__errno2result(errno));
689 
690 	f = fdopen(fd, "w");
691 	if (f == NULL) {
692 		result = isc__errno2result(errno);
693 		close(fd);
694 		return (result);
695 	}
696 
697 	*fp = f;
698 	return (ISC_R_SUCCESS);
699 }
700 
701 isc_result_t
isc_file_splitpath(isc_mem_t * mctx,const char * path,char ** dirname,char const ** basename)702 isc_file_splitpath(isc_mem_t *mctx, const char *path, char **dirname,
703 		   char const ** basename)
704 {
705 	char *dir;
706 	const char *file, *slash;
707 	char *backslash;
708 
709 	slash = strrchr(path, '/');
710 
711 	backslash = strrchr(path, '\\');
712 	if ((slash != NULL && backslash != NULL && backslash > slash) ||
713 	    (slash == NULL && backslash != NULL))
714 		slash = backslash;
715 
716 	if (slash == path) {
717 		file = ++slash;
718 		dir = isc_mem_strdup(mctx, "/");
719 	} else if (slash != NULL) {
720 		file = ++slash;
721 		dir = isc_mem_allocate(mctx, slash - path);
722 		if (dir != NULL)
723 			strlcpy(dir, path, slash - path);
724 	} else {
725 		file = path;
726 		dir = isc_mem_strdup(mctx, ".");
727 	}
728 
729 	if (dir == NULL)
730 		return (ISC_R_NOMEMORY);
731 
732 	if (*file == '\0') {
733 		isc_mem_free(mctx, dir);
734 		return (ISC_R_INVALIDFILE);
735 	}
736 
737 	*dirname = dir;
738 	*basename = file;
739 
740 	return (ISC_R_SUCCESS);
741 }
742 
743 void *
isc_file_mmap(void * addr,size_t len,int prot,int flags,int fd,off_t offset)744 isc_file_mmap(void *addr, size_t len, int prot,
745 	      int flags, int fd, off_t offset)
746 {
747 	void *buf;
748 	ssize_t ret;
749 	off_t end;
750 
751 	UNUSED(addr);
752 	UNUSED(prot);
753 	UNUSED(flags);
754 
755 	end = lseek(fd, 0, SEEK_END);
756 	lseek(fd, offset, SEEK_SET);
757 	if (end - offset < (off_t) len)
758 		len = end - offset;
759 
760 	buf = malloc(len);
761 	if (buf == NULL)
762 		return (NULL);
763 
764 	ret = read(fd, buf, (unsigned int) len);
765 	if (ret != (ssize_t) len) {
766 		free(buf);
767 		buf = NULL;
768 	}
769 
770 	return (buf);
771 }
772 
773 int
isc_file_munmap(void * addr,size_t len)774 isc_file_munmap(void *addr, size_t len) {
775 	UNUSED(len);
776 	free(addr);
777 	return (0);
778 }
779 
780 #define DISALLOW "\\/:ABCDEFGHIJKLMNOPQRSTUVWXYZ"
781 
782 isc_result_t
isc_file_sanitize(const char * dir,const char * base,const char * ext,char * path,size_t length)783 isc_file_sanitize(const char *dir, const char *base, const char *ext,
784 		  char *path, size_t length)
785 {
786 	char buf[PATH_MAX], hash[ISC_SHA256_DIGESTSTRINGLENGTH];
787 	size_t l = 0;
788 
789 	REQUIRE(base != NULL);
790 	REQUIRE(path != NULL);
791 
792 	l = strlen(base) + 1;
793 
794 	/*
795 	 * allow room for a full sha256 hash (64 chars
796 	 * plus null terminator)
797 	 */
798 	if (l < 65)
799 		l = 65;
800 
801 	if (dir != NULL)
802 		l += strlen(dir) + 1;
803 	if (ext != NULL)
804 		l += strlen(ext) + 1;
805 
806 	if (l > length || l > PATH_MAX)
807 		return (ISC_R_NOSPACE);
808 
809 	/* Check whether the full-length SHA256 hash filename exists */
810 	isc_sha256_data((const void *) base, strlen(base), hash);
811 	snprintf(buf, sizeof(buf), "%s%s%s%s%s",
812 		dir != NULL ? dir : "", dir != NULL ? "/" : "",
813 		hash, ext != NULL ? "." : "", ext != NULL ? ext : "");
814 	if (isc_file_exists(buf)) {
815 		strlcpy(path, buf, length);
816 		return (ISC_R_SUCCESS);
817 	}
818 
819 	/* Check for a truncated SHA256 hash filename */
820 	hash[16] = '\0';
821 	snprintf(buf, sizeof(buf), "%s%s%s%s%s",
822 		dir != NULL ? dir : "", dir != NULL ? "/" : "",
823 		hash, ext != NULL ? "." : "", ext != NULL ? ext : "");
824 	if (isc_file_exists(buf)) {
825 		strlcpy(path, buf, length);
826 		return (ISC_R_SUCCESS);
827 	}
828 
829 	/*
830 	 * If neither hash filename already exists, then we'll use
831 	 * the original base name if it has no disallowed characters,
832 	 * or the truncated hash name if it does.
833 	 */
834 	if (strpbrk(base, DISALLOW) != NULL) {
835 		strlcpy(path, buf, length);
836 		return (ISC_R_SUCCESS);
837 	}
838 
839 	snprintf(buf, sizeof(buf), "%s%s%s%s%s",
840 		dir != NULL ? dir : "", dir != NULL ? "/" : "",
841 		base, ext != NULL ? "." : "", ext != NULL ? ext : "");
842 	strlcpy(path, buf, length);
843 	return (ISC_R_SUCCESS);
844 }
845 
846 /*
847  * Based on http://blog.aaronballman.com/2011/08/how-to-check-access-rights/
848  */
849 bool
isc_file_isdirwritable(const char * path)850 isc_file_isdirwritable(const char *path) {
851 	DWORD length = 0;
852 	HANDLE hToken = NULL;
853 	PSECURITY_DESCRIPTOR security = NULL;
854 	bool answer = false;
855 
856 	if (isc_file_isdirectory(path) != ISC_R_SUCCESS) {
857 		return (answer);
858 	}
859 
860 	/*
861 	 * Figure out buffer size. GetFileSecurity() should not succeed.
862 	 */
863 	if (GetFileSecurity(path, OWNER_SECURITY_INFORMATION |
864 				  GROUP_SECURITY_INFORMATION |
865 				  DACL_SECURITY_INFORMATION,
866 			    NULL, 0, &length))
867 	{
868 		return (answer);
869 	}
870 
871 	if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
872 		return (answer);
873 	}
874 
875 	security = malloc(length);
876 	if (security == NULL) {
877 		return (answer);
878 	}
879 
880 	/*
881 	 * GetFileSecurity() should succeed.
882 	 */
883 	if (!GetFileSecurity(path, OWNER_SECURITY_INFORMATION |
884 				   GROUP_SECURITY_INFORMATION |
885 				   DACL_SECURITY_INFORMATION,
886 			     security, length, &length))
887 	{
888 		return (answer);
889 	}
890 
891 	if (OpenProcessToken(GetCurrentProcess(), TOKEN_IMPERSONATE |
892 			     TOKEN_QUERY | TOKEN_DUPLICATE |
893 			     STANDARD_RIGHTS_READ, &hToken))
894 	{
895 		HANDLE hImpersonatedToken = NULL;
896 
897 		if (DuplicateToken(hToken, SecurityImpersonation,
898 				   &hImpersonatedToken))
899 		{
900 			GENERIC_MAPPING mapping;
901 			PRIVILEGE_SET privileges = { 0 };
902 			DWORD grantedAccess = 0;
903 			DWORD privilegesLength = sizeof(privileges);
904 			BOOL result = FALSE;
905 			DWORD genericAccessRights = GENERIC_WRITE;
906 
907 			mapping.GenericRead = FILE_GENERIC_READ;
908 			mapping.GenericWrite = FILE_GENERIC_WRITE;
909 			mapping.GenericExecute = FILE_GENERIC_EXECUTE;
910 			mapping.GenericAll = FILE_ALL_ACCESS;
911 
912 			MapGenericMask(&genericAccessRights, &mapping);
913 			if (AccessCheck(security, hImpersonatedToken,
914 					genericAccessRights, &mapping,
915 					&privileges, &privilegesLength,
916 					&grantedAccess, &result))
917 			{
918 				answer = result;
919 			}
920 			CloseHandle(hImpersonatedToken);
921 		}
922 		CloseHandle(hToken);
923 	}
924 	free(security);
925 	return (answer);
926 }
927