Coverage Report

Created: 2025-06-22 06:59

/src/MapServer/src/cgiutil.c
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 * $Id$
3
 *
4
 * Project:  MapServer
5
 * Purpose:  cgiRequestObj and CGI parameter parsing.
6
 * Author:   Steve Lime and the MapServer team.
7
 *
8
 * Notes: Portions derived from NCSA HTTPd Server's example CGI programs
9
 *(util.c).
10
 *
11
 ******************************************************************************
12
 * Copyright (c) 1996-2005 Regents of the University of Minnesota.
13
 *
14
 * Permission is hereby granted, free of charge, to any person obtaining a
15
 * copy of this software and associated documentation files (the "Software"),
16
 * to deal in the Software without restriction, including without limitation
17
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
18
 * and/or sell copies of the Software, and to permit persons to whom the
19
 * Software is furnished to do so, subject to the following conditions:
20
 *
21
 * The above copyright notice and this permission notice shall be included in
22
 * all copies of this Software or works derived from this Software.
23
 *
24
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
25
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
27
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
30
 * DEALINGS IN THE SOFTWARE.
31
 ****************************************************************************/
32
33
#include <stdio.h>
34
#include <stdlib.h>
35
#include <string.h>
36
#include <ctype.h>
37
#include "mapserver.h"
38
#include "cgiutil.h"
39
40
#include "cpl_conv.h"
41
42
#define LF 10
43
#define CR 13
44
45
0
int readPostBody(cgiRequestObj *request, char **data) {
46
0
  size_t data_max, data_len;
47
0
  int chunk_size;
48
49
0
  (void)request;
50
51
0
  msIO_needBinaryStdin();
52
53
  /* -------------------------------------------------------------------- */
54
  /*      If the length is provided, read in one gulp.                    */
55
  /* -------------------------------------------------------------------- */
56
0
  if (getenv("CONTENT_LENGTH") != NULL) {
57
0
    data_max = (size_t)atoi(getenv("CONTENT_LENGTH"));
58
    /* Test for suspicious CONTENT_LENGTH (negative value or SIZE_MAX) */
59
0
    if (data_max >= SIZE_MAX) {
60
      // msIO_setHeader("Content-Type","text/html");
61
      // msIO_sendHeaders();
62
      // msIO_printf("Suspicious Content-Length.\n");
63
0
      msSetError(MS_WEBERR, "Suspicious Content-Length.", "readPostBody()");
64
0
      return MS_FAILURE;
65
0
    }
66
0
    *data = (char *)malloc(data_max + 1);
67
0
    if (*data == NULL) {
68
      // msIO_setHeader("Content-Type","text/html");
69
      // msIO_sendHeaders();
70
      // msIO_printf("malloc() failed, Content-Length: %u unreasonably
71
      // large?\n", (unsigned int)data_max );
72
0
      msSetError(MS_WEBERR,
73
0
                 "malloc() failed, Content-Length: %u unreasonably large?",
74
0
                 "readPostBody()", (unsigned int)data_max);
75
0
      return MS_FAILURE;
76
0
    }
77
78
0
    if ((int)msIO_fread(*data, 1, data_max, stdin) < (int)data_max) {
79
      // msIO_setHeader("Content-Type","text/html");
80
      // msIO_sendHeaders();
81
      // msIO_printf("POST body is short\n");
82
0
      msSetError(MS_WEBERR, "POST body is short.", "readPostBody()");
83
0
      return MS_FAILURE;
84
0
    }
85
86
0
    (*data)[data_max] = '\0';
87
0
    return MS_SUCCESS;
88
0
  }
89
  /* -------------------------------------------------------------------- */
90
  /*      Otherwise read in chunks to the end.                            */
91
  /* -------------------------------------------------------------------- */
92
0
#define DATA_ALLOC_SIZE 10000
93
94
0
  data_max = DATA_ALLOC_SIZE;
95
0
  data_len = 0;
96
0
  *data = (char *)msSmallMalloc(data_max + 1);
97
0
  (*data)[data_max] = '\0';
98
99
0
  while ((chunk_size = msIO_fread(*data + data_len, 1, data_max - data_len,
100
0
                                  stdin)) > 0) {
101
0
    data_len += chunk_size;
102
103
0
    if (data_len == data_max) {
104
      /* Realloc buffer, making sure we check for possible size_t overflow */
105
0
      if (data_max > SIZE_MAX - (DATA_ALLOC_SIZE + 1)) {
106
        // msIO_setHeader("Content-Type","text/html");
107
        // msIO_sendHeaders();
108
        // msIO_printf("Possible size_t overflow, cannot reallocate input
109
        // buffer, POST body too large?\n" );
110
0
        msSetError(MS_WEBERR,
111
0
                   "Possible size_t overflow, cannot reallocate input buffer, "
112
0
                   "POST body too large?",
113
0
                   "readPostBody()");
114
0
        return MS_FAILURE;
115
0
      }
116
117
0
      data_max = data_max + DATA_ALLOC_SIZE;
118
0
      *data = (char *)msSmallRealloc(*data, data_max + 1);
119
0
    }
120
0
  }
121
122
0
  (*data)[data_len] = '\0';
123
0
  return MS_SUCCESS;
124
0
}
125
126
0
static char *msGetEnv(const char *name, void *thread_context) {
127
0
  (void)thread_context;
128
0
  return getenv(name);
129
0
}
130
131
int loadParams(cgiRequestObj *request,
132
               char *(*getenv2)(const char *, void *thread_context),
133
               char *raw_post_data, ms_uint32 raw_post_data_length,
134
0
               void *thread_context) {
135
0
  int m = 0;
136
0
  char *s, *queryString = NULL, *httpCookie = NULL;
137
0
  int debuglevel;
138
0
  int maxParams = MS_DEFAULT_CGI_PARAMS;
139
140
0
  if (getenv2 == NULL)
141
0
    getenv2 = &msGetEnv;
142
143
0
  if (getenv2("REQUEST_METHOD", thread_context) == NULL) {
144
0
    msIO_printf("This script can only be used to decode form results and \n");
145
0
    msIO_printf("should be initiated as a CGI process via a httpd server.\n");
146
0
    msIO_printf("For other options please try using the --help switch.\n");
147
0
    return -1;
148
0
  }
149
150
0
  debuglevel = (int)msGetGlobalDebugLevel();
151
152
0
  if (strcmp(getenv2("REQUEST_METHOD", thread_context), "POST") == 0 &&
153
0
      CPLGetConfigOption("MS_NO_POST", NULL) ==
154
0
          NULL) { /* we've got a post from a form */
155
0
    char *post_data;
156
0
    int data_len;
157
0
    request->type = MS_POST_REQUEST;
158
159
0
    if (request->contenttype == NULL) {
160
0
      s = getenv2("CONTENT_TYPE", thread_context);
161
0
      if (s != NULL) {
162
0
        request->contenttype = msStrdup(s);
163
0
      } else {
164
        /* we've to set default Content-Type which is
165
         * application/octet-stream according to
166
         * W3 RFC 2626 section 7.2.1 */
167
0
        request->contenttype = msStrdup("application/octet-stream");
168
0
      }
169
0
    }
170
171
0
    if (raw_post_data) {
172
0
      post_data = msStrdup(raw_post_data);
173
0
      data_len = raw_post_data_length;
174
0
    } else {
175
0
      if (MS_SUCCESS != readPostBody(request, &post_data))
176
0
        return -1;
177
0
      data_len = strlen(post_data);
178
0
    }
179
180
    /* if the content_type is application/x-www-form-urlencoded,
181
       we have to parse it like the QUERY_STRING variable */
182
0
    if (strncmp(request->contenttype, "application/x-www-form-urlencoded",
183
0
                strlen("application/x-www-form-urlencoded")) == 0) {
184
0
      while (data_len > 0 && isspace(post_data[data_len - 1]))
185
0
        post_data[--data_len] = '\0';
186
187
0
      while (post_data[0]) {
188
0
        if (m >= maxParams) {
189
0
          maxParams *= 2;
190
0
          request->ParamNames = (char **)msSmallRealloc(
191
0
              request->ParamNames, sizeof(char *) * maxParams);
192
0
          request->ParamValues = (char **)msSmallRealloc(
193
0
              request->ParamValues, sizeof(char *) * maxParams);
194
0
        }
195
0
        request->ParamValues[m] = makeword(post_data, '&');
196
0
        plustospace(request->ParamValues[m]);
197
0
        unescape_url(request->ParamValues[m]);
198
0
        request->ParamNames[m] = makeword(request->ParamValues[m], '=');
199
0
        m++;
200
0
      }
201
0
      free(post_data);
202
0
    } else
203
0
      request->postrequest = post_data;
204
205
    /* check the QUERY_STRING even in the post request since it can contain
206
       information. Eg a wfs request with  */
207
0
    s = getenv2("QUERY_STRING", thread_context);
208
0
    if (s) {
209
0
      if (debuglevel >= MS_DEBUGLEVEL_DEBUG)
210
0
        msDebug("loadParams() QUERY_STRING: %s\n", s);
211
212
0
      queryString = msStrdup(s);
213
0
      while (queryString[0] != '\0') {
214
0
        if (m >= maxParams) {
215
0
          maxParams *= 2;
216
0
          request->ParamNames = (char **)msSmallRealloc(
217
0
              request->ParamNames, sizeof(char *) * maxParams);
218
0
          request->ParamValues = (char **)msSmallRealloc(
219
0
              request->ParamValues, sizeof(char *) * maxParams);
220
0
        }
221
0
        request->ParamValues[m] = makeword(queryString, '&');
222
0
        plustospace(request->ParamValues[m]);
223
0
        unescape_url(request->ParamValues[m]);
224
0
        request->ParamNames[m] = makeword(request->ParamValues[m], '=');
225
0
        m++;
226
0
      }
227
0
    }
228
0
  } else {
229
0
    if (strcmp(getenv2("REQUEST_METHOD", thread_context), "GET") ==
230
0
        0) { /* we've got a get request */
231
0
      request->type = MS_GET_REQUEST;
232
233
0
      s = getenv2("QUERY_STRING", thread_context);
234
0
      if (s == NULL) {
235
        // msIO_setHeader("Content-Type","text/html");
236
        // msIO_sendHeaders();
237
        // msIO_printf("No query information to decode. QUERY_STRING not
238
        // set.\n");
239
0
        msSetError(MS_WEBERR,
240
0
                   "No query information to decode. QUERY_STRING not set.",
241
0
                   "loadParams()");
242
0
        return -1;
243
0
      }
244
245
0
      if (debuglevel >= MS_DEBUGLEVEL_DEBUG)
246
0
        msDebug("loadParams() QUERY_STRING: %s\n", s);
247
248
0
      if (strlen(s) == 0) {
249
        // msIO_setHeader("Content-Type","text/html");
250
        // msIO_sendHeaders();
251
        // msIO_printf("No query information to decode. QUERY_STRING is set, but
252
        // empty.\n");
253
0
        msSetError(
254
0
            MS_WEBERR,
255
0
            "No query information to decode. QUERY_STRING is set, but empty.",
256
0
            "loadParams()");
257
0
        return -1;
258
0
      }
259
260
      /* don't modify the string returned by getenv2 */
261
0
      queryString = msStrdup(s);
262
0
      while (queryString[0] != '\0') {
263
0
        if (m >= maxParams) {
264
0
          maxParams *= 2;
265
0
          request->ParamNames = (char **)msSmallRealloc(
266
0
              request->ParamNames, sizeof(char *) * maxParams);
267
0
          request->ParamValues = (char **)msSmallRealloc(
268
0
              request->ParamValues, sizeof(char *) * maxParams);
269
0
        }
270
0
        request->ParamValues[m] = makeword(queryString, '&');
271
0
        plustospace(request->ParamValues[m]);
272
0
        unescape_url(request->ParamValues[m]);
273
0
        request->ParamNames[m] = makeword(request->ParamValues[m], '=');
274
0
        m++;
275
0
      }
276
0
    } else {
277
      // msIO_setHeader("Content-Type","text/html");
278
      // msIO_sendHeaders();
279
      // msIO_printf("This script should be referenced with a METHOD of GET or
280
      // METHOD of POST.\n");
281
0
      msSetError(MS_WEBERR,
282
0
                 "This script should be referenced with a METHOD of GET or "
283
0
                 "METHOD of POST.",
284
0
                 "loadParams()");
285
0
      return -1;
286
0
    }
287
0
  }
288
289
  /* check for any available cookies */
290
0
  s = getenv2("HTTP_COOKIE", thread_context);
291
0
  if (s != NULL) {
292
0
    httpCookie = msStrdup(s);
293
0
    request->httpcookiedata = msStrdup(s);
294
0
    while (httpCookie[0] != '\0') {
295
0
      if (m >= maxParams) {
296
0
        maxParams *= 2;
297
0
        request->ParamNames = (char **)msSmallRealloc(
298
0
            request->ParamNames, sizeof(char *) * maxParams);
299
0
        request->ParamValues = (char **)msSmallRealloc(
300
0
            request->ParamValues, sizeof(char *) * maxParams);
301
0
      }
302
0
      request->ParamValues[m] = makeword(httpCookie, ';');
303
0
      plustospace(request->ParamValues[m]);
304
0
      unescape_url(request->ParamValues[m]);
305
0
      request->ParamNames[m] = makeword_skip(request->ParamValues[m], '=', ' ');
306
0
      m++;
307
0
    }
308
0
  }
309
310
0
  if (queryString)
311
0
    free(queryString);
312
0
  if (httpCookie)
313
0
    free(httpCookie);
314
315
0
  return (m);
316
0
}
317
318
0
void getword(char *word, char *line, char stop) {
319
0
  int x = 0, y;
320
321
0
  for (x = 0; ((line[x]) && (line[x] != stop)); x++)
322
0
    word[x] = line[x];
323
324
0
  word[x] = '\0';
325
0
  if (line[x])
326
0
    ++x;
327
0
  y = 0;
328
329
0
  while ((line[y++] = line[x++]))
330
0
    ;
331
0
}
332
333
0
char *makeword_skip(char *line, char stop, char skip) {
334
0
  int x = 0, y, offset = 0;
335
0
  char *word = (char *)msSmallMalloc(sizeof(char) * (strlen(line) + 1));
336
337
0
  for (x = 0; ((line[x]) && (line[x] == skip)); x++)
338
0
    ;
339
0
  offset = x;
340
341
0
  for (x = offset; ((line[x]) && (line[x] != stop)); x++)
342
0
    word[x - offset] = line[x];
343
344
0
  word[x - offset] = '\0';
345
0
  if (line[x])
346
0
    ++x;
347
0
  y = 0;
348
349
0
  while ((line[y++] = line[x++]))
350
0
    ;
351
0
  return word;
352
0
}
353
354
0
char *makeword(char *line, char stop) {
355
0
  int x = 0, y;
356
0
  char *word = (char *)msSmallMalloc(sizeof(char) * (strlen(line) + 1));
357
358
0
  for (x = 0; ((line[x]) && (line[x] != stop)); x++)
359
0
    word[x] = line[x];
360
361
0
  word[x] = '\0';
362
0
  if (line[x])
363
0
    ++x;
364
0
  y = 0;
365
366
0
  while ((line[y++] = line[x++]))
367
0
    ;
368
0
  return word;
369
0
}
370
371
0
char *fmakeword(FILE *f, char stop, int *cl) {
372
0
  int wsize;
373
0
  char *word;
374
0
  int ll;
375
376
0
  wsize = 102400;
377
0
  ll = 0;
378
0
  word = (char *)msSmallMalloc(sizeof(char) * (wsize + 1));
379
380
0
  while (1) {
381
0
    word[ll] = (char)fgetc(f);
382
0
    if (ll == wsize) {
383
0
      word[ll + 1] = '\0';
384
0
      wsize += 102400;
385
0
      word = (char *)msSmallRealloc(word, sizeof(char) * (wsize + 1));
386
0
    }
387
0
    --(*cl);
388
0
    if ((word[ll] == stop) || (feof(f)) || (!(*cl))) {
389
0
      if (word[ll] != stop)
390
0
        ll++;
391
0
      word[ll] = '\0';
392
0
      word = (char *)msSmallRealloc(word, ll + 1);
393
0
      return word;
394
0
    }
395
0
    ++ll;
396
0
  }
397
0
}
398
399
0
char x2c(char *what) {
400
0
  register char digit;
401
402
0
  digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A') + 10 : (what[0] - '0'));
403
0
  digit *= 16;
404
0
  digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10 : (what[1] - '0'));
405
0
  return (digit);
406
0
}
407
408
0
void unescape_url(char *url) {
409
0
  register int x, y;
410
411
0
  for (x = 0, y = 0; url[y]; ++x, ++y) {
412
0
    if ((url[x] = url[y]) == '%') {
413
0
      url[x] = x2c(&url[y + 1]);
414
0
      y += 2;
415
0
    }
416
0
  }
417
0
  url[x] = '\0';
418
0
}
419
420
0
void plustospace(char *str) {
421
0
  register int x;
422
423
0
  for (x = 0; str[x]; x++)
424
0
    if (str[x] == '+')
425
0
      str[x] = ' ';
426
0
}
427
428
0
int rind(char *s, char c) {
429
0
  register int x;
430
0
  for (x = strlen(s) - 1; x != -1; x--)
431
0
    if (s[x] == c)
432
0
      return x;
433
0
  return -1;
434
0
}
435
436
0
void send_fd(FILE *f, FILE *fd) {
437
0
  int c;
438
439
0
  while (1) {
440
0
    c = fgetc(f);
441
0
    if (c == EOF)
442
0
      return;
443
0
    fputc((char)c, fd);
444
0
  }
445
0
}
446
447
0
int ind(char *s, char c) {
448
0
  register int x;
449
450
0
  for (x = 0; s[x]; x++)
451
0
    if (s[x] == c)
452
0
      return x;
453
454
0
  return -1;
455
0
}
456
457
/*
458
** patched version according to CERT advisory...
459
*/
460
0
void escape_shell_cmd(char *cmd) {
461
0
  register int x, y, l;
462
463
0
  l = strlen(cmd);
464
0
  for (x = 0; cmd[x]; x++) {
465
0
    if (ind("&;`'\"|*?~<>^()[]{}$\\\n", cmd[x]) != -1) {
466
0
      for (y = l + 1; y > x; y--)
467
0
        cmd[y] = cmd[y - 1];
468
0
      l++; /* length has been increased */
469
0
      cmd[x] = '\\';
470
0
      x++; /* skip the character */
471
0
    }
472
0
  }
473
0
}
474
475
/*
476
** Allocate a new request holder structure
477
*/
478
0
cgiRequestObj *msAllocCgiObj() {
479
0
  cgiRequestObj *request = (cgiRequestObj *)malloc(sizeof(cgiRequestObj));
480
481
0
  if (!request)
482
0
    return NULL;
483
484
0
  request->ParamNames =
485
0
      (char **)msSmallMalloc(MS_DEFAULT_CGI_PARAMS * sizeof(char *));
486
0
  request->ParamValues =
487
0
      (char **)msSmallMalloc(MS_DEFAULT_CGI_PARAMS * sizeof(char *));
488
0
  request->NumParams = 0;
489
0
  request->type = MS_GET_REQUEST;
490
0
  request->contenttype = NULL;
491
0
  request->postrequest = NULL;
492
0
  request->httpcookiedata = NULL;
493
494
0
  request->path_info = NULL;
495
0
  request->api_path = NULL;
496
0
  request->api_path_length = 0;
497
498
0
  return request;
499
0
}
500
501
0
void msFreeCgiObj(cgiRequestObj *request) {
502
0
  msFreeCharArray(request->ParamNames, request->NumParams);
503
0
  msFreeCharArray(request->ParamValues, request->NumParams);
504
0
  request->ParamNames = NULL;
505
0
  request->ParamValues = NULL;
506
0
  request->NumParams = 0;
507
0
  request->type = -1;
508
0
  msFree(request->contenttype);
509
0
  msFree(request->postrequest);
510
0
  msFree(request->httpcookiedata);
511
0
  request->contenttype = NULL;
512
0
  request->postrequest = NULL;
513
0
  request->httpcookiedata = NULL;
514
515
0
  if (request->api_path) {
516
0
    msFreeCharArray(request->api_path, request->api_path_length);
517
0
    request->api_path = NULL;
518
0
    request->api_path_length = 0;
519
0
  }
520
521
0
  msFree(request);
522
0
}