1 | /***************************************************************************
|
---|
2 | * _ _ ____ _
|
---|
3 | * Project ___| | | | _ \| |
|
---|
4 | * / __| | | | |_) | |
|
---|
5 | * | (__| |_| | _ <| |___
|
---|
6 | * \___|\___/|_| \_\_____|
|
---|
7 | *
|
---|
8 | * Copyright (C) 1998 - 2017, Daniel Stenberg, <[email protected]>, et al.
|
---|
9 | *
|
---|
10 | * This software is licensed as described in the file COPYING, which
|
---|
11 | * you should have received as part of this distribution. The terms
|
---|
12 | * are also available at https://curl.haxx.se/docs/copyright.html.
|
---|
13 | *
|
---|
14 | * You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
---|
15 | * copies of the Software, and permit persons to whom the Software is
|
---|
16 | * furnished to do so, under the terms of the COPYING file.
|
---|
17 | *
|
---|
18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
---|
19 | * KIND, either express or implied.
|
---|
20 | *
|
---|
21 | ***************************************************************************/
|
---|
22 |
|
---|
23 | #include "curl_setup.h"
|
---|
24 |
|
---|
25 | #include <curl/curl.h>
|
---|
26 |
|
---|
27 | #include "dotdot.h"
|
---|
28 | #include "curl_memory.h"
|
---|
29 |
|
---|
30 | /* The last #include file should be: */
|
---|
31 | #include "memdebug.h"
|
---|
32 |
|
---|
33 | /*
|
---|
34 | * "Remove Dot Segments"
|
---|
35 | * https://tools.ietf.org/html/rfc3986#section-5.2.4
|
---|
36 | */
|
---|
37 |
|
---|
38 | /*
|
---|
39 | * Curl_dedotdotify()
|
---|
40 | * @unittest: 1395
|
---|
41 | *
|
---|
42 | * This function gets a zero-terminated path with dot and dotdot sequences
|
---|
43 | * passed in and strips them off according to the rules in RFC 3986 section
|
---|
44 | * 5.2.4.
|
---|
45 | *
|
---|
46 | * The function handles a query part ('?' + stuff) appended but it expects
|
---|
47 | * that fragments ('#' + stuff) have already been cut off.
|
---|
48 | *
|
---|
49 | * RETURNS
|
---|
50 | *
|
---|
51 | * an allocated dedotdotified output string
|
---|
52 | */
|
---|
53 | char *Curl_dedotdotify(const char *input)
|
---|
54 | {
|
---|
55 | size_t inlen = strlen(input);
|
---|
56 | char *clone;
|
---|
57 | size_t clen = inlen; /* the length of the cloned input */
|
---|
58 | char *out = malloc(inlen + 1);
|
---|
59 | char *outptr;
|
---|
60 | char *orgclone;
|
---|
61 | char *queryp;
|
---|
62 | if(!out)
|
---|
63 | return NULL; /* out of memory */
|
---|
64 |
|
---|
65 | *out = 0; /* zero terminates, for inputs like "./" */
|
---|
66 |
|
---|
67 | /* get a cloned copy of the input */
|
---|
68 | clone = strdup(input);
|
---|
69 | if(!clone) {
|
---|
70 | free(out);
|
---|
71 | return NULL;
|
---|
72 | }
|
---|
73 | orgclone = clone;
|
---|
74 | outptr = out;
|
---|
75 |
|
---|
76 | if(!*clone) {
|
---|
77 | /* zero length string, return that */
|
---|
78 | free(out);
|
---|
79 | return clone;
|
---|
80 | }
|
---|
81 |
|
---|
82 | /*
|
---|
83 | * To handle query-parts properly, we must find it and remove it during the
|
---|
84 | * dotdot-operation and then append it again at the end to the output
|
---|
85 | * string.
|
---|
86 | */
|
---|
87 | queryp = strchr(clone, '?');
|
---|
88 | if(queryp)
|
---|
89 | *queryp = 0;
|
---|
90 |
|
---|
91 | do {
|
---|
92 |
|
---|
93 | /* A. If the input buffer begins with a prefix of "../" or "./", then
|
---|
94 | remove that prefix from the input buffer; otherwise, */
|
---|
95 |
|
---|
96 | if(!strncmp("./", clone, 2)) {
|
---|
97 | clone += 2;
|
---|
98 | clen -= 2;
|
---|
99 | }
|
---|
100 | else if(!strncmp("../", clone, 3)) {
|
---|
101 | clone += 3;
|
---|
102 | clen -= 3;
|
---|
103 | }
|
---|
104 |
|
---|
105 | /* B. if the input buffer begins with a prefix of "/./" or "/.", where
|
---|
106 | "." is a complete path segment, then replace that prefix with "/" in
|
---|
107 | the input buffer; otherwise, */
|
---|
108 | else if(!strncmp("/./", clone, 3)) {
|
---|
109 | clone += 2;
|
---|
110 | clen -= 2;
|
---|
111 | }
|
---|
112 | else if(!strcmp("/.", clone)) {
|
---|
113 | clone[1]='/';
|
---|
114 | clone++;
|
---|
115 | clen -= 1;
|
---|
116 | }
|
---|
117 |
|
---|
118 | /* C. if the input buffer begins with a prefix of "/../" or "/..", where
|
---|
119 | ".." is a complete path segment, then replace that prefix with "/" in
|
---|
120 | the input buffer and remove the last segment and its preceding "/" (if
|
---|
121 | any) from the output buffer; otherwise, */
|
---|
122 |
|
---|
123 | else if(!strncmp("/../", clone, 4)) {
|
---|
124 | clone += 3;
|
---|
125 | clen -= 3;
|
---|
126 | /* remove the last segment from the output buffer */
|
---|
127 | while(outptr > out) {
|
---|
128 | outptr--;
|
---|
129 | if(*outptr == '/')
|
---|
130 | break;
|
---|
131 | }
|
---|
132 | *outptr = 0; /* zero-terminate where it stops */
|
---|
133 | }
|
---|
134 | else if(!strcmp("/..", clone)) {
|
---|
135 | clone[2]='/';
|
---|
136 | clone += 2;
|
---|
137 | clen -= 2;
|
---|
138 | /* remove the last segment from the output buffer */
|
---|
139 | while(outptr > out) {
|
---|
140 | outptr--;
|
---|
141 | if(*outptr == '/')
|
---|
142 | break;
|
---|
143 | }
|
---|
144 | *outptr = 0; /* zero-terminate where it stops */
|
---|
145 | }
|
---|
146 |
|
---|
147 | /* D. if the input buffer consists only of "." or "..", then remove
|
---|
148 | that from the input buffer; otherwise, */
|
---|
149 |
|
---|
150 | else if(!strcmp(".", clone) || !strcmp("..", clone)) {
|
---|
151 | *clone = 0;
|
---|
152 | *out = 0;
|
---|
153 | }
|
---|
154 |
|
---|
155 | else {
|
---|
156 | /* E. move the first path segment in the input buffer to the end of
|
---|
157 | the output buffer, including the initial "/" character (if any) and
|
---|
158 | any subsequent characters up to, but not including, the next "/"
|
---|
159 | character or the end of the input buffer. */
|
---|
160 |
|
---|
161 | do {
|
---|
162 | *outptr++ = *clone++;
|
---|
163 | clen--;
|
---|
164 | } while(*clone && (*clone != '/'));
|
---|
165 | *outptr = 0;
|
---|
166 | }
|
---|
167 |
|
---|
168 | } while(*clone);
|
---|
169 |
|
---|
170 | if(queryp) {
|
---|
171 | size_t qlen;
|
---|
172 | /* There was a query part, append that to the output. The 'clone' string
|
---|
173 | may now have been altered so we copy from the original input string
|
---|
174 | from the correct index. */
|
---|
175 | size_t oindex = queryp - orgclone;
|
---|
176 | qlen = strlen(&input[oindex]);
|
---|
177 | memcpy(outptr, &input[oindex], qlen + 1); /* include the end zero byte */
|
---|
178 | }
|
---|
179 |
|
---|
180 | free(orgclone);
|
---|
181 | return out;
|
---|
182 | }
|
---|