version 1.264, 2020/01/25 22:59:22 |
version 1.268, 2021/08/07 13:02:10 |
|
|
/* $Id$ */ |
/* $Id$ */ |
/* |
/* |
* Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv> |
|
* Copyright (c) 2011-2020 Ingo Schwarze <schwarze@openbsd.org> |
* Copyright (c) 2011-2020 Ingo Schwarze <schwarze@openbsd.org> |
|
* Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv> |
* Copyright (c) 2016 Ed Maste <emaste@freebsd.org> |
* Copyright (c) 2016 Ed Maste <emaste@freebsd.org> |
* |
* |
* Permission to use, copy, modify, and distribute this software for any |
* Permission to use, copy, modify, and distribute this software for any |
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|
* |
|
* Implementation of the makewhatis(8) program. |
*/ |
*/ |
#include "config.h" |
#include "config.h" |
|
|
Line 118 struct mdoc_handler { |
|
Line 120 struct mdoc_handler { |
|
int mandocdb(int, char *[]); |
int mandocdb(int, char *[]); |
|
|
static void dbadd(struct dba *, struct mpage *); |
static void dbadd(struct dba *, struct mpage *); |
static void dbadd_mlink(const struct mlink *mlink); |
static void dbadd_mlink(const struct mlink *); |
static void dbprune(struct dba *); |
static void dbprune(struct dba *); |
static void dbwrite(struct dba *); |
static void dbwrite(struct dba *); |
static void filescan(const char *); |
static void filescan(const char *); |
Line 163 static void putkey(const struct mpage *, char *, uint |
|
Line 165 static void putkey(const struct mpage *, char *, uint |
|
static void putkeys(const struct mpage *, char *, size_t, uint64_t); |
static void putkeys(const struct mpage *, char *, size_t, uint64_t); |
static void putmdockey(const struct mpage *, |
static void putmdockey(const struct mpage *, |
const struct roff_node *, uint64_t, int); |
const struct roff_node *, uint64_t, int); |
|
#ifdef READ_ALLOWED_PATH |
|
static int read_allowed(const char *); |
|
#endif |
static int render_string(char **, size_t *); |
static int render_string(char **, size_t *); |
static void say(const char *, const char *, ...) |
static void say(const char *, const char *, ...) |
__attribute__((__format__ (__printf__, 2, 3))); |
__attribute__((__format__ (__printf__, 2, 3))); |
|
|
continue; |
continue; |
} |
} |
if (strncmp(buf, basedir, basedir_len) != 0 |
if (strncmp(buf, basedir, basedir_len) != 0 |
#ifdef HOMEBREWDIR |
#ifdef READ_ALLOWED_PATH |
&& strncmp(buf, HOMEBREWDIR, strlen(HOMEBREWDIR)) |
&& !read_allowed(buf) |
#endif |
#endif |
) { |
) { |
if (warnings) say("", |
if (warnings) say("", |
|
|
* See treescan() for the fts(3) version of this. |
* See treescan() for the fts(3) version of this. |
*/ |
*/ |
static void |
static void |
filescan(const char *file) |
filescan(const char *infile) |
{ |
{ |
char buf[PATH_MAX]; |
|
struct stat st; |
struct stat st; |
struct mlink *mlink; |
struct mlink *mlink; |
char *p, *start; |
char *linkfile, *p, *realdir, *start, *usefile; |
|
size_t realdir_len; |
|
|
assert(use_all); |
assert(use_all); |
|
|
if (strncmp(file, "./", 2) == 0) |
if (strncmp(infile, "./", 2) == 0) |
file += 2; |
infile += 2; |
|
|
/* |
/* |
* We have to do lstat(2) before realpath(3) loses |
* We have to do lstat(2) before realpath(3) loses |
Line 797 filescan(const char *file) |
|
Line 802 filescan(const char *file) |
|
* we want to use the orginal file name, while for |
* we want to use the orginal file name, while for |
* regular files, we want to use the real path. |
* regular files, we want to use the real path. |
*/ |
*/ |
if (lstat(file, &st) == -1) { |
if (lstat(infile, &st) == -1) { |
exitcode = (int)MANDOCLEVEL_BADARG; |
exitcode = (int)MANDOCLEVEL_BADARG; |
say(file, "&lstat"); |
say(infile, "&lstat"); |
return; |
return; |
} else if ((st.st_mode & (S_IFREG | S_IFLNK)) == 0) { |
} else if (S_ISREG(st.st_mode) == 0 && S_ISLNK(st.st_mode) == 0) { |
exitcode = (int)MANDOCLEVEL_BADARG; |
exitcode = (int)MANDOCLEVEL_BADARG; |
say(file, "Not a regular file"); |
say(infile, "Not a regular file"); |
return; |
return; |
} |
} |
|
|
Line 811 filescan(const char *file) |
|
Line 816 filescan(const char *file) |
|
* We have to resolve the file name to the real path |
* We have to resolve the file name to the real path |
* in any case for the base directory check. |
* in any case for the base directory check. |
*/ |
*/ |
if (realpath(file, buf) == NULL) { |
if ((usefile = realpath(infile, NULL)) == NULL) { |
exitcode = (int)MANDOCLEVEL_BADARG; |
exitcode = (int)MANDOCLEVEL_BADARG; |
say(file, "&realpath"); |
say(infile, "&realpath"); |
return; |
return; |
} |
} |
|
|
if (op == OP_TEST) |
if (op == OP_TEST) |
start = buf; |
start = usefile; |
else if (strncmp(buf, basedir, basedir_len) == 0) |
else if (strncmp(usefile, basedir, basedir_len) == 0) |
start = buf + basedir_len; |
start = usefile + basedir_len; |
#ifdef HOMEBREWDIR |
#ifdef READ_ALLOWED_PATH |
else if (strncmp(buf, HOMEBREWDIR, strlen(HOMEBREWDIR)) == 0) |
else if (read_allowed(usefile)) |
start = buf; |
start = usefile; |
#endif |
#endif |
else { |
else { |
exitcode = (int)MANDOCLEVEL_BADARG; |
exitcode = (int)MANDOCLEVEL_BADARG; |
say("", "%s: outside base directory", buf); |
say("", "%s: outside base directory", infile); |
|
free(usefile); |
return; |
return; |
} |
} |
|
|
Line 835 filescan(const char *file) |
|
Line 841 filescan(const char *file) |
|
* Now we are sure the file is inside our tree. |
* Now we are sure the file is inside our tree. |
* If it is a symbolic link, ignore the real path |
* If it is a symbolic link, ignore the real path |
* and use the original name. |
* and use the original name. |
* This implies passing stuff like "cat1/../man1/foo.1" |
|
* on the command line won't work. So don't do that. |
|
* Note the stat(2) can still fail if the link target |
|
* doesn't exist. |
|
*/ |
*/ |
if (st.st_mode & S_IFLNK) { |
do { |
if (stat(buf, &st) == -1) { |
if (S_ISLNK(st.st_mode) == 0) |
|
break; |
|
|
|
/* |
|
* Some implementations of realpath(3) may succeed |
|
* even if the target of the link does not exist, |
|
* so check again for extra safety. |
|
*/ |
|
if (stat(usefile, &st) == -1) { |
exitcode = (int)MANDOCLEVEL_BADARG; |
exitcode = (int)MANDOCLEVEL_BADARG; |
say(file, "&stat"); |
say(infile, "&stat"); |
|
free(usefile); |
return; |
return; |
} |
} |
if (strlcpy(buf, file, sizeof(buf)) >= sizeof(buf)) { |
linkfile = mandoc_strdup(infile); |
say(file, "Filename too long"); |
if (op == OP_TEST) { |
return; |
free(usefile); |
|
start = usefile = linkfile; |
|
break; |
} |
} |
start = buf; |
if (strncmp(infile, basedir, basedir_len) == 0) { |
if (op != OP_TEST && strncmp(buf, basedir, basedir_len) == 0) |
free(usefile); |
start += basedir_len; |
usefile = linkfile; |
} |
start = usefile + basedir_len; |
|
break; |
|
} |
|
|
|
/* |
|
* This symbolic link points into the basedir |
|
* from the outside. Let's see whether any of |
|
* the parent directories resolve to the basedir. |
|
*/ |
|
p = strchr(linkfile, '\0'); |
|
do { |
|
while (*--p != '/') |
|
continue; |
|
*p = '\0'; |
|
if ((realdir = realpath(linkfile, NULL)) == NULL) { |
|
exitcode = (int)MANDOCLEVEL_BADARG; |
|
say(infile, "&realpath"); |
|
free(linkfile); |
|
free(usefile); |
|
return; |
|
} |
|
realdir_len = strlen(realdir) + 1; |
|
free(realdir); |
|
*p = '/'; |
|
} while (realdir_len > basedir_len); |
|
|
|
/* |
|
* If one of the directories resolves to the basedir, |
|
* use the rest of the original name. |
|
* Otherwise, the best we can do |
|
* is to use the filename pointed to. |
|
*/ |
|
if (realdir_len == basedir_len) { |
|
free(usefile); |
|
usefile = linkfile; |
|
start = p + 1; |
|
} else { |
|
free(linkfile); |
|
start = usefile + basedir_len; |
|
} |
|
} while (/* CONSTCOND */ 0); |
|
|
mlink = mandoc_calloc(1, sizeof(struct mlink)); |
mlink = mandoc_calloc(1, sizeof(struct mlink)); |
mlink->dform = FORM_NONE; |
mlink->dform = FORM_NONE; |
if (strlcpy(mlink->file, start, sizeof(mlink->file)) >= |
if (strlcpy(mlink->file, start, sizeof(mlink->file)) >= |
sizeof(mlink->file)) { |
sizeof(mlink->file)) { |
say(start, "Filename too long"); |
say(start, "Filename too long"); |
free(mlink); |
free(mlink); |
|
free(usefile); |
return; |
return; |
} |
} |
|
|
Line 869 filescan(const char *file) |
|
Line 923 filescan(const char *file) |
|
* but outside our tree, guess the base directory. |
* but outside our tree, guess the base directory. |
*/ |
*/ |
|
|
if (op == OP_TEST || (start == buf && *start == '/')) { |
if (op == OP_TEST || (start == usefile && *start == '/')) { |
if (strncmp(buf, "man/", 4) == 0) |
if (strncmp(usefile, "man/", 4) == 0) |
start = buf + 4; |
start = usefile + 4; |
else if ((start = strstr(buf, "/man/")) != NULL) |
else if ((start = strstr(usefile, "/man/")) != NULL) |
start += 5; |
start += 5; |
else |
else |
start = buf; |
start = usefile; |
} |
} |
|
|
/* |
/* |
Line 925 filescan(const char *file) |
|
Line 979 filescan(const char *file) |
|
*p = '\0'; |
*p = '\0'; |
} |
} |
mlink_add(mlink, &st); |
mlink_add(mlink, &st); |
|
free(usefile); |
} |
} |
|
|
static void |
static void |
Line 2328 set_basedir(const char *targetdir, int report_baddir) |
|
Line 2383 set_basedir(const char *targetdir, int report_baddir) |
|
} |
} |
return 1; |
return 1; |
} |
} |
|
|
|
#ifdef READ_ALLOWED_PATH |
|
static int |
|
read_allowed(const char *candidate) |
|
{ |
|
const char *cp; |
|
size_t len; |
|
|
|
for (cp = READ_ALLOWED_PATH;; cp += len) { |
|
while (*cp == ':') |
|
cp++; |
|
if (*cp == '\0') |
|
return 0; |
|
len = strcspn(cp, ":"); |
|
if (strncmp(candidate, cp, len) == 0) |
|
return 1; |
|
} |
|
} |
|
#endif |
|
|
static void |
static void |
say(const char *file, const char *format, ...) |
say(const char *file, const char *format, ...) |