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 /* get a cloned copy of the input */ 66 clone = strdup(input); 67 if(!clone) { 68 free(out); 69 return NULL; 70 } 71 orgclone = clone; 72 outptr = out; 73 74 if(!*clone) { 75 /* zero length string, return that */ 76 free(out); 77 return clone; 78 } 79 80 /* 81 * To handle query-parts properly, we must find it and remove it during the 82 * dotdot-operation and then append it again at the end to the output 83 * string. 84 */ 85 queryp = strchr(clone, '?'); 86 if(queryp) 87 *queryp = 0; 88 89 do { 90 91 /* A. If the input buffer begins with a prefix of "../" or "./", then 92 remove that prefix from the input buffer; otherwise, */ 93 94 if(!strncmp("./", clone, 2)) { 95 clone += 2; 96 clen -= 2; 97 } 98 else if(!strncmp("../", clone, 3)) { 99 clone += 3; 100 clen -= 3; 101 } 102 103 /* B. if the input buffer begins with a prefix of "/./" or "/.", where 104 "." is a complete path segment, then replace that prefix with "/" in 105 the input buffer; otherwise, */ 106 else if(!strncmp("/./", clone, 3)) { 107 clone += 2; 108 clen -= 2; 109 } 110 else if(!strcmp("/.", clone)) { 111 clone[1]='/'; 112 clone++; 113 clen -= 1; 114 } 115 116 /* C. if the input buffer begins with a prefix of "/../" or "/..", where 117 ".." is a complete path segment, then replace that prefix with "/" in 118 the input buffer and remove the last segment and its preceding "/" (if 119 any) from the output buffer; otherwise, */ 120 121 else if(!strncmp("/../", clone, 4)) { 122 clone += 3; 123 clen -= 3; 124 /* remove the last segment from the output buffer */ 125 while(outptr > out) { 126 outptr--; 127 if(*outptr == '/') 128 break; 129 } 130 *outptr = 0; /* zero-terminate where it stops */ 131 } 132 else if(!strcmp("/..", clone)) { 133 clone[2]='/'; 134 clone += 2; 135 clen -= 2; 136 /* remove the last segment from the output buffer */ 137 while(outptr > out) { 138 outptr--; 139 if(*outptr == '/') 140 break; 141 } 142 *outptr = 0; /* zero-terminate where it stops */ 143 } 144 145 /* D. if the input buffer consists only of "." or "..", then remove 146 that from the input buffer; otherwise, */ 147 148 else if(!strcmp(".", clone) || !strcmp("..", clone)) { 149 *clone = 0; 150 *out = 0; 151 } 152 153 else { 154 /* E. move the first path segment in the input buffer to the end of 155 the output buffer, including the initial "/" character (if any) and 156 any subsequent characters up to, but not including, the next "/" 157 character or the end of the input buffer. */ 158 159 do { 160 *outptr++ = *clone++; 161 clen--; 162 } while(*clone && (*clone != '/')); 163 *outptr = 0; 164 } 165 166 } while(*clone); 167 168 if(queryp) { 169 size_t qlen; 170 /* There was a query part, append that to the output. The 'clone' string 171 may now have been altered so we copy from the original input string 172 from the correct index. */ 173 size_t oindex = queryp - orgclone; 174 qlen = strlen(&input[oindex]); 175 memcpy(outptr, &input[oindex], qlen + 1); /* include the end zero byte */ 176 } 177 178 free(orgclone); 179 return out; 180 } 181