=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/mandoc/mandocdb.c,v retrieving revision 1.214 retrieving revision 1.215 diff -c -r1.214 -r1.215 *** src/usr.bin/mandoc/mandocdb.c 2020/01/26 11:15:49 1.214 --- src/usr.bin/mandoc/mandocdb.c 2020/01/26 21:24:58 1.215 *************** *** 1,4 **** ! /* $OpenBSD: mandocdb.c,v 1.214 2020/01/26 11:15:49 schwarze Exp $ */ /* * Copyright (c) 2011, 2012 Kristaps Dzonsons * Copyright (c) 2011-2020 Ingo Schwarze --- 1,4 ---- ! /* $OpenBSD: mandocdb.c,v 1.215 2020/01/26 21:24:58 schwarze Exp $ */ /* * Copyright (c) 2011, 2012 Kristaps Dzonsons * Copyright (c) 2011-2020 Ingo Schwarze *************** *** 744,760 **** * See treescan() for the fts(3) version of this. */ static void ! filescan(const char *file) { - char buf[PATH_MAX]; struct stat st; struct mlink *mlink; ! char *p, *start; assert(use_all); ! if (strncmp(file, "./", 2) == 0) ! file += 2; /* * We have to do lstat(2) before realpath(3) loses --- 744,760 ---- * See treescan() for the fts(3) version of this. */ static void ! filescan(const char *infile) { struct stat st; struct mlink *mlink; ! char *linkfile, *p, *realdir, *start, *usefile; ! size_t realdir_len; assert(use_all); ! if (strncmp(infile, "./", 2) == 0) ! infile += 2; /* * We have to do lstat(2) before realpath(3) loses *************** *** 763,775 **** * we want to use the orginal file name, while for * regular files, we want to use the real path. */ ! if (lstat(file, &st) == -1) { exitcode = (int)MANDOCLEVEL_BADARG; ! say(file, "&lstat"); return; } else if (S_ISREG(st.st_mode) == 0 && S_ISLNK(st.st_mode) == 0) { exitcode = (int)MANDOCLEVEL_BADARG; ! say(file, "Not a regular file"); return; } --- 763,775 ---- * we want to use the orginal file name, while for * regular files, we want to use the real path. */ ! if (lstat(infile, &st) == -1) { exitcode = (int)MANDOCLEVEL_BADARG; ! say(infile, "&lstat"); return; } else if (S_ISREG(st.st_mode) == 0 && S_ISLNK(st.st_mode) == 0) { exitcode = (int)MANDOCLEVEL_BADARG; ! say(infile, "Not a regular file"); return; } *************** *** 777,795 **** * We have to resolve the file name to the real path * in any case for the base directory check. */ ! if (realpath(file, buf) == NULL) { exitcode = (int)MANDOCLEVEL_BADARG; ! say(file, "&realpath"); return; } if (op == OP_TEST) ! start = buf; ! else if (strncmp(buf, basedir, basedir_len) == 0) ! start = buf + basedir_len; else { exitcode = (int)MANDOCLEVEL_BADARG; ! say("", "%s: outside base directory", buf); return; } --- 777,796 ---- * We have to resolve the file name to the real path * in any case for the base directory check. */ ! if ((usefile = realpath(infile, NULL)) == NULL) { exitcode = (int)MANDOCLEVEL_BADARG; ! say(infile, "&realpath"); return; } if (op == OP_TEST) ! start = usefile; ! else if (strncmp(usefile, basedir, basedir_len) == 0) ! start = usefile + basedir_len; else { exitcode = (int)MANDOCLEVEL_BADARG; ! say("", "%s: outside base directory", infile); ! free(usefile); return; } *************** *** 797,828 **** * Now we are sure the file is inside our tree. * If it is a symbolic link, ignore the real path * 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 (S_ISLNK(st.st_mode)) { ! if (stat(buf, &st) == -1) { exitcode = (int)MANDOCLEVEL_BADARG; ! say(file, "&stat"); return; } ! if (strlcpy(buf, file, sizeof(buf)) >= sizeof(buf)) { ! say(file, "Filename too long"); ! return; } ! start = buf; ! if (op != OP_TEST && strncmp(buf, basedir, basedir_len) == 0) ! start += basedir_len; ! } mlink = mandoc_calloc(1, sizeof(struct mlink)); mlink->dform = FORM_NONE; if (strlcpy(mlink->file, start, sizeof(mlink->file)) >= sizeof(mlink->file)) { say(start, "Filename too long"); free(mlink); return; } --- 798,877 ---- * Now we are sure the file is inside our tree. * If it is a symbolic link, ignore the real path * and use the original name. */ ! do { ! 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; ! say(infile, "&stat"); ! free(usefile); return; } ! linkfile = mandoc_strdup(infile); ! if (op == OP_TEST) { ! free(usefile); ! start = usefile = linkfile; ! break; } ! if (strncmp(infile, basedir, basedir_len) == 0) { ! free(usefile); ! 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->dform = FORM_NONE; if (strlcpy(mlink->file, start, sizeof(mlink->file)) >= sizeof(mlink->file)) { say(start, "Filename too long"); free(mlink); + free(usefile); return; } *************** *** 831,843 **** * but outside our tree, guess the base directory. */ ! if (op == OP_TEST || (start == buf && *start == '/')) { ! if (strncmp(buf, "man/", 4) == 0) ! start = buf + 4; ! else if ((start = strstr(buf, "/man/")) != NULL) start += 5; else ! start = buf; } /* --- 880,892 ---- * but outside our tree, guess the base directory. */ ! if (op == OP_TEST || (start == usefile && *start == '/')) { ! if (strncmp(usefile, "man/", 4) == 0) ! start = usefile + 4; ! else if ((start = strstr(usefile, "/man/")) != NULL) start += 5; else ! start = usefile; } /* *************** *** 887,892 **** --- 936,942 ---- *p = '\0'; } mlink_add(mlink, &st); + free(usefile); } static void