1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel (at) haxx.se>, 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 } 183