Annotation of src/usr.bin/ssh/sftp-realpath.c, Revision 1.1
1.1 ! djm 1: /* $OpenBSD: realpath.c,v 1.20 2015/10/13 20:55:37 millert Exp $ */
! 2: /*
! 3: * Copyright (c) 2003 Constantin S. Svintsoff <kostik@iclub.nsu.ru>
! 4: *
! 5: * Redistribution and use in source and binary forms, with or without
! 6: * modification, are permitted provided that the following conditions
! 7: * are met:
! 8: * 1. Redistributions of source code must retain the above copyright
! 9: * notice, this list of conditions and the following disclaimer.
! 10: * 2. Redistributions in binary form must reproduce the above copyright
! 11: * notice, this list of conditions and the following disclaimer in the
! 12: * documentation and/or other materials provided with the distribution.
! 13: * 3. The names of the authors may not be used to endorse or promote
! 14: * products derived from this software without specific prior written
! 15: * permission.
! 16: *
! 17: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
! 18: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 19: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 20: * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
! 21: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 22: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 23: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 24: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 25: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 26: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 27: * SUCH DAMAGE.
! 28: */
! 29:
! 30: #include <sys/types.h>
! 31: #include <sys/param.h>
! 32: #include <sys/stat.h>
! 33:
! 34: #include <errno.h>
! 35: #include <stdlib.h>
! 36: #include <stddef.h>
! 37: #include <string.h>
! 38: #include <unistd.h>
! 39: #include <limits.h>
! 40:
! 41: #ifndef SYMLOOP_MAX
! 42: # define SYMLOOP_MAX 32
! 43: #endif
! 44:
! 45: /* XXX rewrite sftp-server to use POSIX realpath and remove this hack */
! 46:
! 47: char *sftp_realpath(const char *path, char *resolved);
! 48:
! 49: /*
! 50: * char *realpath(const char *path, char resolved[PATH_MAX]);
! 51: *
! 52: * Find the real name of path, by removing all ".", ".." and symlink
! 53: * components. Returns (resolved) on success, or (NULL) on failure,
! 54: * in which case the path which caused trouble is left in (resolved).
! 55: */
! 56: char *
! 57: sftp_realpath(const char *path, char *resolved)
! 58: {
! 59: struct stat sb;
! 60: char *p, *q, *s;
! 61: size_t left_len, resolved_len;
! 62: unsigned symlinks;
! 63: int serrno, slen, mem_allocated;
! 64: char left[PATH_MAX], next_token[PATH_MAX], symlink[PATH_MAX];
! 65:
! 66: if (path[0] == '\0') {
! 67: errno = ENOENT;
! 68: return (NULL);
! 69: }
! 70:
! 71: serrno = errno;
! 72:
! 73: if (resolved == NULL) {
! 74: resolved = malloc(PATH_MAX);
! 75: if (resolved == NULL)
! 76: return (NULL);
! 77: mem_allocated = 1;
! 78: } else
! 79: mem_allocated = 0;
! 80:
! 81: symlinks = 0;
! 82: if (path[0] == '/') {
! 83: resolved[0] = '/';
! 84: resolved[1] = '\0';
! 85: if (path[1] == '\0')
! 86: return (resolved);
! 87: resolved_len = 1;
! 88: left_len = strlcpy(left, path + 1, sizeof(left));
! 89: } else {
! 90: if (getcwd(resolved, PATH_MAX) == NULL) {
! 91: if (mem_allocated)
! 92: free(resolved);
! 93: else
! 94: strlcpy(resolved, ".", PATH_MAX);
! 95: return (NULL);
! 96: }
! 97: resolved_len = strlen(resolved);
! 98: left_len = strlcpy(left, path, sizeof(left));
! 99: }
! 100: if (left_len >= sizeof(left) || resolved_len >= PATH_MAX) {
! 101: errno = ENAMETOOLONG;
! 102: goto err;
! 103: }
! 104:
! 105: /*
! 106: * Iterate over path components in `left'.
! 107: */
! 108: while (left_len != 0) {
! 109: /*
! 110: * Extract the next path component and adjust `left'
! 111: * and its length.
! 112: */
! 113: p = strchr(left, '/');
! 114: s = p ? p : left + left_len;
! 115: if (s - left >= (ptrdiff_t)sizeof(next_token)) {
! 116: errno = ENAMETOOLONG;
! 117: goto err;
! 118: }
! 119: memcpy(next_token, left, s - left);
! 120: next_token[s - left] = '\0';
! 121: left_len -= s - left;
! 122: if (p != NULL)
! 123: memmove(left, s + 1, left_len + 1);
! 124: if (resolved[resolved_len - 1] != '/') {
! 125: if (resolved_len + 1 >= PATH_MAX) {
! 126: errno = ENAMETOOLONG;
! 127: goto err;
! 128: }
! 129: resolved[resolved_len++] = '/';
! 130: resolved[resolved_len] = '\0';
! 131: }
! 132: if (next_token[0] == '\0')
! 133: continue;
! 134: else if (strcmp(next_token, ".") == 0)
! 135: continue;
! 136: else if (strcmp(next_token, "..") == 0) {
! 137: /*
! 138: * Strip the last path component except when we have
! 139: * single "/"
! 140: */
! 141: if (resolved_len > 1) {
! 142: resolved[resolved_len - 1] = '\0';
! 143: q = strrchr(resolved, '/') + 1;
! 144: *q = '\0';
! 145: resolved_len = q - resolved;
! 146: }
! 147: continue;
! 148: }
! 149:
! 150: /*
! 151: * Append the next path component and lstat() it. If
! 152: * lstat() fails we still can return successfully if
! 153: * there are no more path components left.
! 154: */
! 155: resolved_len = strlcat(resolved, next_token, PATH_MAX);
! 156: if (resolved_len >= PATH_MAX) {
! 157: errno = ENAMETOOLONG;
! 158: goto err;
! 159: }
! 160: if (lstat(resolved, &sb) != 0) {
! 161: if (errno == ENOENT && p == NULL) {
! 162: errno = serrno;
! 163: return (resolved);
! 164: }
! 165: goto err;
! 166: }
! 167: if (S_ISLNK(sb.st_mode)) {
! 168: if (symlinks++ > SYMLOOP_MAX) {
! 169: errno = ELOOP;
! 170: goto err;
! 171: }
! 172: slen = readlink(resolved, symlink, sizeof(symlink) - 1);
! 173: if (slen < 0)
! 174: goto err;
! 175: symlink[slen] = '\0';
! 176: if (symlink[0] == '/') {
! 177: resolved[1] = 0;
! 178: resolved_len = 1;
! 179: } else if (resolved_len > 1) {
! 180: /* Strip the last path component. */
! 181: resolved[resolved_len - 1] = '\0';
! 182: q = strrchr(resolved, '/') + 1;
! 183: *q = '\0';
! 184: resolved_len = q - resolved;
! 185: }
! 186:
! 187: /*
! 188: * If there are any path components left, then
! 189: * append them to symlink. The result is placed
! 190: * in `left'.
! 191: */
! 192: if (p != NULL) {
! 193: if (symlink[slen - 1] != '/') {
! 194: if (slen + 1 >=
! 195: (ptrdiff_t)sizeof(symlink)) {
! 196: errno = ENAMETOOLONG;
! 197: goto err;
! 198: }
! 199: symlink[slen] = '/';
! 200: symlink[slen + 1] = 0;
! 201: }
! 202: left_len = strlcat(symlink, left, sizeof(symlink));
! 203: if (left_len >= sizeof(symlink)) {
! 204: errno = ENAMETOOLONG;
! 205: goto err;
! 206: }
! 207: }
! 208: left_len = strlcpy(left, symlink, sizeof(left));
! 209: }
! 210: }
! 211:
! 212: /*
! 213: * Remove trailing slash except when the resolved pathname
! 214: * is a single "/".
! 215: */
! 216: if (resolved_len > 1 && resolved[resolved_len - 1] == '/')
! 217: resolved[resolved_len - 1] = '\0';
! 218: return (resolved);
! 219:
! 220: err:
! 221: if (mem_allocated)
! 222: free(resolved);
! 223: return (NULL);
! 224: }