/* * filename: cgi2shell.c * modified by: G. D. LaBossiere, Xview Solutions Inc. * last modified: 2021-03-21 * comments: processes cgi $QUERY_STRING (get) and stdin (post) url encoded * content and converts to var="value" or var[0]="value" shell expressions. * license: GNU GPL v3 * * compile on the host platform: * gcc -o /path/to/binary/cgi2shell /path/to/source/cgi2shell.c * * modified from 'proccgi.c' by Frank Pilhofer * http://www.fpx.de/fp/Software/ProcCGI.html * distributed under the GNU General Public License * * 2021-03-21 * - modified attribute parsing to accept bash indexed arrays, e.g. var_name[0] * 2018-06-05 * - changed name from 'webform2shell' to 'cgi2shell' * - modified error message strings * 2017-08-02 * - changed name from 'proccgi' to 'webform2shell' * - removed 'FORM_' prefix appended to html form 'name=' values (var names) * - modified error message strings * - modified comments * */ #include #include #include #include #include /* * duplicate string */ char * FP_strdup (char *string) { char *result; if (string == NULL) return NULL; if ((result = (char *) malloc (strlen (string) + 1)) == NULL) { fprintf (stderr, "cgi2shell -- out of memory duplicating %d bytes\n", (int) strlen (string)); return NULL; } strcpy (result, string); return result; } /* * read cgi input */ char * LoadInput (void) { char *result, *method, *p; int length, ts; if ((method = getenv ("REQUEST_METHOD")) == NULL) { return NULL; } if (strcmp (method, "GET") == 0) { if ((p = getenv ("QUERY_STRING")) == NULL) return NULL; else result = FP_strdup (p); } else if (strcmp (method, "POST") == 0) { if ((length = atoi (getenv ("CONTENT_LENGTH"))) == 0) return NULL; if ((result = malloc (length + 1)) == NULL) { fprintf (stderr, "cgi2shell -- out of memory allocating %d bytes\n", length); return NULL; } if ((ts = fread (result, sizeof (char), length, stdin)) < length) { fprintf (stderr, "cgi2shell -- error reading post data, %d bytes read, %d expected\n", ts, length); } result[length] = '\0'; } else { return NULL; } return result; } /* * replace '+' with ' ' (space) and '%XX' (e.g. '%2F') with corresponding * charset value (e.g. '/') in cgi data */ char * ParseString (char *instring) { char *ptr1=instring, *ptr2=instring; if (instring == NULL) return instring; while (isspace (*ptr1)) ptr1++; while (*ptr1) { if (*ptr1 == '+') { ptr1++; *ptr2++=' '; } else if (*ptr1 == '%' && isxdigit (*(ptr1+1)) && isxdigit (*(ptr1+2))) { ptr1++; *ptr2 = ((*ptr1>='0'&&*ptr1<='9')?(*ptr1-'0'):((char)toupper(*ptr1)-'A'+10)) << 4; ptr1++; *ptr2++ |= ((*ptr1>='0'&&*ptr1<='9')?(*ptr1-'0'):((char)toupper(*ptr1)-'A'+10)); ptr1++; } else *ptr2++ = *ptr1++; } while (ptr2>instring && isspace(*(ptr2-1))) ptr2--; *ptr2 = '\0'; return instring; } /* * break into attribute/value pair; must not use strtok, which is already used * one level below; attribute may have only permitted bash variable characters */ void HandleString (char *input) { char *data, *ptr, *p2; if (input == NULL) { return; } data = FP_strdup (input); ptr = ParseString (data); /* * parse attributes; accept only characters that are valid for bash variables * and indexed arrays; reject empty values */ if (!isalpha(*ptr) && *ptr != '_') {free (data); return;} ptr++; while (isalnum(*ptr) || *ptr == '_') ptr++; if (*ptr == '[') ptr++; while (isdigit(*ptr)) ptr++; if (*ptr == ']') ptr++; if (*ptr != '=') {free (data); return;} *ptr = '\0'; p2 = ptr+1; fprintf (stdout, "%s=\"", data); /* * escape value */ while (*p2) { switch (*p2) { case '"': case '\\': case '`': case '$': putc ('\\', stdout); default: putc (*p2, stdout); break; } p2++; } putc ('"', stdout); putc ('\n', stdout); *ptr = '='; free (data); } int main (int argc, char *argv[]) { char *ptr, *data = LoadInput(); int i; /* * remove '&' (ampersand) from cgi data */ if (data) { ptr = strtok (data, "&"); while (ptr) { HandleString (ptr); ptr = strtok (NULL, "&"); } free (data); } /* * add path info */ if (getenv ("PATH_INFO") != NULL) { data = FP_strdup (getenv ("PATH_INFO")); ptr = strtok (data, "/"); while (ptr) { HandleString (ptr); ptr = strtok (NULL, "/"); } free (data); } /* * add args */ for (i=1; i