Annotation of src/usr.bin/ssh/sftp-realpath.c, Revision 1.2
1.2 ! deraadt 1: /* $OpenBSD: sftp-realpath.c,v 1.1 2019/07/05 04:55:40 djm Exp $ */
1.1 djm 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/stat.h>
32:
33: #include <errno.h>
34: #include <stdlib.h>
35: #include <stddef.h>
36: #include <string.h>
37: #include <unistd.h>
38: #include <limits.h>
39:
40: #ifndef SYMLOOP_MAX
41: # define SYMLOOP_MAX 32
42: #endif
43:
44: /* XXX rewrite sftp-server to use POSIX realpath and remove this hack */
45:
46: char *sftp_realpath(const char *path, char *resolved);
47:
48: /*
49: * char *realpath(const char *path, char resolved[PATH_MAX]);
50: *
51: * Find the real name of path, by removing all ".", ".." and symlink
52: * components. Returns (resolved) on success, or (NULL) on failure,
53: * in which case the path which caused trouble is left in (resolved).
54: */
55: char *
56: sftp_realpath(const char *path, char *resolved)
57: {
58: struct stat sb;
59: char *p, *q, *s;
60: size_t left_len, resolved_len;
61: unsigned symlinks;
62: int serrno, slen, mem_allocated;
63: char left[PATH_MAX], next_token[PATH_MAX], symlink[PATH_MAX];
64:
65: if (path[0] == '\0') {
66: errno = ENOENT;
67: return (NULL);
68: }
69:
70: serrno = errno;
71:
72: if (resolved == NULL) {
73: resolved = malloc(PATH_MAX);
74: if (resolved == NULL)
75: return (NULL);
76: mem_allocated = 1;
77: } else
78: mem_allocated = 0;
79:
80: symlinks = 0;
81: if (path[0] == '/') {
82: resolved[0] = '/';
83: resolved[1] = '\0';
84: if (path[1] == '\0')
85: return (resolved);
86: resolved_len = 1;
87: left_len = strlcpy(left, path + 1, sizeof(left));
88: } else {
89: if (getcwd(resolved, PATH_MAX) == NULL) {
90: if (mem_allocated)
91: free(resolved);
92: else
93: strlcpy(resolved, ".", PATH_MAX);
94: return (NULL);
95: }
96: resolved_len = strlen(resolved);
97: left_len = strlcpy(left, path, sizeof(left));
98: }
99: if (left_len >= sizeof(left) || resolved_len >= PATH_MAX) {
100: errno = ENAMETOOLONG;
101: goto err;
102: }
103:
104: /*
105: * Iterate over path components in `left'.
106: */
107: while (left_len != 0) {
108: /*
109: * Extract the next path component and adjust `left'
110: * and its length.
111: */
112: p = strchr(left, '/');
113: s = p ? p : left + left_len;
114: if (s - left >= (ptrdiff_t)sizeof(next_token)) {
115: errno = ENAMETOOLONG;
116: goto err;
117: }
118: memcpy(next_token, left, s - left);
119: next_token[s - left] = '\0';
120: left_len -= s - left;
121: if (p != NULL)
122: memmove(left, s + 1, left_len + 1);
123: if (resolved[resolved_len - 1] != '/') {
124: if (resolved_len + 1 >= PATH_MAX) {
125: errno = ENAMETOOLONG;
126: goto err;
127: }
128: resolved[resolved_len++] = '/';
129: resolved[resolved_len] = '\0';
130: }
131: if (next_token[0] == '\0')
132: continue;
133: else if (strcmp(next_token, ".") == 0)
134: continue;
135: else if (strcmp(next_token, "..") == 0) {
136: /*
137: * Strip the last path component except when we have
138: * single "/"
139: */
140: if (resolved_len > 1) {
141: resolved[resolved_len - 1] = '\0';
142: q = strrchr(resolved, '/') + 1;
143: *q = '\0';
144: resolved_len = q - resolved;
145: }
146: continue;
147: }
148:
149: /*
150: * Append the next path component and lstat() it. If
151: * lstat() fails we still can return successfully if
152: * there are no more path components left.
153: */
154: resolved_len = strlcat(resolved, next_token, PATH_MAX);
155: if (resolved_len >= PATH_MAX) {
156: errno = ENAMETOOLONG;
157: goto err;
158: }
159: if (lstat(resolved, &sb) != 0) {
160: if (errno == ENOENT && p == NULL) {
161: errno = serrno;
162: return (resolved);
163: }
164: goto err;
165: }
166: if (S_ISLNK(sb.st_mode)) {
167: if (symlinks++ > SYMLOOP_MAX) {
168: errno = ELOOP;
169: goto err;
170: }
171: slen = readlink(resolved, symlink, sizeof(symlink) - 1);
172: if (slen < 0)
173: goto err;
174: symlink[slen] = '\0';
175: if (symlink[0] == '/') {
176: resolved[1] = 0;
177: resolved_len = 1;
178: } else if (resolved_len > 1) {
179: /* Strip the last path component. */
180: resolved[resolved_len - 1] = '\0';
181: q = strrchr(resolved, '/') + 1;
182: *q = '\0';
183: resolved_len = q - resolved;
184: }
185:
186: /*
187: * If there are any path components left, then
188: * append them to symlink. The result is placed
189: * in `left'.
190: */
191: if (p != NULL) {
192: if (symlink[slen - 1] != '/') {
193: if (slen + 1 >=
194: (ptrdiff_t)sizeof(symlink)) {
195: errno = ENAMETOOLONG;
196: goto err;
197: }
198: symlink[slen] = '/';
199: symlink[slen + 1] = 0;
200: }
201: left_len = strlcat(symlink, left, sizeof(symlink));
202: if (left_len >= sizeof(symlink)) {
203: errno = ENAMETOOLONG;
204: goto err;
205: }
206: }
207: left_len = strlcpy(left, symlink, sizeof(left));
208: }
209: }
210:
211: /*
212: * Remove trailing slash except when the resolved pathname
213: * is a single "/".
214: */
215: if (resolved_len > 1 && resolved[resolved_len - 1] == '/')
216: resolved[resolved_len - 1] = '\0';
217: return (resolved);
218:
219: err:
220: if (mem_allocated)
221: free(resolved);
222: return (NULL);
223: }