CGI Bash Shell Interface
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

cbsi.c 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658
  1. /**
  2. * @PROJECT CGI Bash Shell Interface
  3. * @COPYRIGHT See COPYING in the top level directory
  4. * @FILE cbsi.c
  5. * @PURPOSE Common CBSI
  6. * @DEVELOPERS Nathan Angelacos <nangel@users.sourceforge.net>
  7. * Rafal Kupiec <belliash@asiotec.eu.org>
  8. */
  9. #include <stdio.h>
  10. #include <unistd.h>
  11. #include <time.h>
  12. #include <getopt.h>
  13. #include <sys/mman.h>
  14. #include <sys/types.h>
  15. #include <sys/wait.h>
  16. #include <sys/stat.h>
  17. #include <sys/fcntl.h>
  18. #include <stdlib.h>
  19. #include <string.h>
  20. #include <grp.h>
  21. #include "common.h"
  22. #include "error.h"
  23. #include "buffer.h"
  24. #include "subshell.h"
  25. #include "mimetype.h"
  26. #include "cbsi.h"
  27. struct option ga_long_options[] = {
  28. {"language-dir", required_argument, 0, 'l'},
  29. {"translation", required_argument, 0, 't'},
  30. {"upload-limit", required_argument, 0, 'u'},
  31. {"upload-dir", required_argument, 0, 'd'},
  32. {"shell", required_argument, 0, 's'},
  33. {0, 0, 0, 0}
  34. };
  35. const char *gs_short_options = "+u:d:l:t:s:";
  36. int argc_argv(char* instr, argv_t** argv) {
  37. char quote = '\0';
  38. int arg_count = 0;
  39. enum state_t { WHITESPACE, WORDSPACE, TOKENSTART } state = WHITESPACE;
  40. argv_t* argv_array = NULL;
  41. int argc_slots = 0;
  42. size_t len, pos;
  43. len = strlen(instr);
  44. pos = 0;
  45. while(pos < len) {
  46. switch(*instr) {
  47. case '"':
  48. case '\'':
  49. if(state == WHITESPACE) {
  50. quote = *instr;
  51. state = TOKENSTART;
  52. if(*(instr + 1) == quote) {
  53. quote = '\0';
  54. *instr = '\0';
  55. argv_array[arg_count].quoted = -1;
  56. } else {
  57. instr++;
  58. pos++;
  59. }
  60. } else {
  61. if(*instr == quote) {
  62. argv_array[arg_count - 1].quoted = -1;
  63. quote = '\0';
  64. *instr = '\0';
  65. state = WHITESPACE;
  66. }
  67. }
  68. break;
  69. case '\\':
  70. if((quote) && (*(instr + 1) == quote)) {
  71. memmove(instr, instr + 1, strlen(instr));
  72. len--;
  73. } else {
  74. if(state == WHITESPACE) {
  75. state = TOKENSTART;
  76. }
  77. }
  78. break;
  79. case ' ':
  80. case '\t':
  81. case '\r':
  82. case '\n':
  83. if((state == WORDSPACE) && (quote == '\0')) {
  84. state = WHITESPACE;
  85. *instr = '\0';
  86. }
  87. break;
  88. case '\0':
  89. break;
  90. default:
  91. if(state == WHITESPACE) {
  92. state = TOKENSTART;
  93. }
  94. }
  95. if(state == TOKENSTART) {
  96. arg_count++;
  97. if(arg_count > argc_slots) {
  98. argc_slots += ALLOC_CHUNK;
  99. argv_array = (argv_t*) xrealloc(argv_array, sizeof(argv_t) * (argc_slots + ALLOC_CHUNK));
  100. }
  101. if(argv_array == NULL) {
  102. return -1;
  103. }
  104. argv_array[arg_count - 1].string = instr;
  105. argv_array[arg_count].quoted = 0;
  106. state = WORDSPACE;
  107. }
  108. instr++;
  109. pos++;
  110. }
  111. argv_array[arg_count].string = NULL;
  112. *argv = argv_array;
  113. return arg_count;
  114. }
  115. void assignGlobalStartupValues() {
  116. global.uploadkb = -1;
  117. global.shell = SUBSHELL_CMD;
  118. global.langdir = LANGDIR;
  119. global.translation = NULL;
  120. global.uploaddir = TEMPDIR;
  121. global.uploadlist = NULL;
  122. global.file_prefix = "FILE_";
  123. global.filename_prefix = "FILENAME_";
  124. global.get_prefix = "GET_";
  125. global.post_prefix = "POST_";
  126. global.cbsi_prefix = "CBSI_";
  127. global.cookie_prefix = "COOKIE_";
  128. global.null_prefix = "";
  129. }
  130. int BecomeUser(uid_t uid, gid_t gid) {
  131. if(getuid() == 0) {
  132. setgroups(1, &gid);
  133. }
  134. setgid(gid);
  135. setgid(getgid());
  136. setuid(uid);
  137. setuid(getuid());
  138. return 0;
  139. }
  140. void cbsiflags(list_t* env) {
  141. char buf[200];
  142. snprintf(buf, 200, "UPLOAD_DIR=%s", global.uploaddir);
  143. myputenv(env, buf, global.cbsi_prefix);
  144. snprintf(buf, 200, "UPLOAD_LIMIT=%lu", global.uploadkb);
  145. myputenv(env, buf, global.cbsi_prefix);
  146. snprintf(buf, 200, "SHELL=%s", global.shell);
  147. myputenv(env, buf, global.cbsi_prefix);
  148. }
  149. void cleanup(void) {
  150. int i;
  151. if(global.uploadlist) {
  152. unlink_uploadlist();
  153. free_token_list(global.uploadlist);
  154. global.uploadlist = NULL;
  155. }
  156. if(language != NULL && translations > 0) {
  157. for (i = 0; i < HASH_BUF; ++i) {
  158. if(ltable[i]) {
  159. free(ltable[i]->msgid);
  160. free(ltable[i]->msgstr);
  161. free(ltable[i]);
  162. }
  163. }
  164. }
  165. }
  166. void CookieVars(list_t* env) {
  167. char* qs;
  168. char* token;
  169. if(getenv("HTTP_COOKIE") != NULL) {
  170. qs = strdup(getenv("HTTP_COOKIE"));
  171. } else {
  172. return;
  173. }
  174. token = strtok(qs, ";");
  175. while(token) {
  176. while(token[0] == ' ') {
  177. token++;
  178. }
  179. myputenv(env, token, global.cookie_prefix);
  180. token = strtok(NULL, ";");
  181. }
  182. free(qs);
  183. }
  184. int count_lines(char* instr, size_t len, char* where) {
  185. size_t line = 1;
  186. while((where > instr) && (len)) {
  187. if(*instr == '\n') {
  188. line++;
  189. }
  190. len--;
  191. instr++;
  192. }
  193. return line;
  194. }
  195. char* find_whitespace(char* instr) {
  196. while(!isspace(*instr) && *instr) {
  197. instr++;
  198. }
  199. return instr;
  200. }
  201. void free_list_chain(list_t* list) {
  202. list_t *next;
  203. while(list) {
  204. next = list->next;
  205. free(list->buf);
  206. free(list);
  207. list = next;
  208. }
  209. }
  210. unsigned short generateHash(char* str) {
  211. unsigned long hash = 5381;
  212. int c;
  213. while(c = *str++) {
  214. hash = ((hash << 5) + hash) + c;
  215. }
  216. return hash % HASH_BUF;
  217. }
  218. void loadDictionary(const char* filename) {
  219. char* b;
  220. FILE* fp;
  221. short hash;
  222. char msgid[TRANS_BUF];
  223. char msgstr[TRANS_BUF];
  224. lstr* p;
  225. lstr* s;
  226. sprintf(buffer, "%s/%s/%s.dic", global.langdir, language, filename);
  227. if((fp = fopen(buffer, "r")) != NULL) {
  228. memset(ltable, 0, sizeof(lstr*) * HASH_BUF);
  229. memset(msgid, '\0', TRANS_BUF);
  230. memset(msgstr, '\0', TRANS_BUF);
  231. while(!feof(fp) && (fgets(buffer, TRANS_BUF - 1, fp) != NULL)) {
  232. b = skip_whitespace(buffer);
  233. if((!*b) || (*b == '#')) {
  234. continue;
  235. }
  236. if(strstr(b, "msgid") != NULL) {
  237. b = trim(b + 5);
  238. strncpy(msgid, b, strlen(b) + 1);
  239. } else if(strstr(b, "msgstr") != NULL) {
  240. b = trim(b + 6);
  241. strncpy(msgstr, b, strlen(b) + 1);
  242. } else {
  243. continue;
  244. }
  245. if(msgid[0] != 0 && msgstr[0] != 0) {
  246. hash = generateHash(msgid);
  247. s = malloc(sizeof(lstr));
  248. s->msgid = (char*) malloc(strlen(msgid) + 1);
  249. s->msgstr = (char*) malloc(strlen(msgstr) + 1);
  250. strcpy(s->msgid, msgid);
  251. strcpy(s->msgstr, msgstr);
  252. s->next = NULL;
  253. if(ltable[hash] == NULL) {
  254. ltable[hash] = s;
  255. } else {
  256. for(p = ltable[hash]; p->next != NULL; p = p->next);
  257. p->next = s;
  258. }
  259. translations++;
  260. msgid[0] = 0;
  261. msgstr[0] = 0;
  262. }
  263. }
  264. fclose(fp);
  265. }
  266. }
  267. void lowercase(char* instr) {
  268. while(*instr != '\0') {
  269. *instr = tolower (*instr);
  270. instr++;
  271. }
  272. }
  273. int main(int argc, char* argv[]) {
  274. token_t* tokenchain = NULL;
  275. buffer_t script_text;
  276. script_t* scriptchain;
  277. int retval = 0;
  278. char* filename = NULL;
  279. argv_t* av = NULL;
  280. char** av2 = argv;
  281. int av2c = argc;
  282. int command;
  283. int count;
  284. list_t* env = NULL;
  285. if(atexit(cleanup) != 0) {
  286. die_with_message(NULL, NULL, "atexit() failed");
  287. }
  288. assignGlobalStartupValues();
  289. buffer_init(&script_text);
  290. switch(argc) {
  291. case 1:
  292. puts("This is CBSI version " CBSI_VERSION "\nThis program runs as a CGI interface, not interactively.\n");
  293. return 0;
  294. break;
  295. default:
  296. command = argc_argv(argv[1], &av);
  297. if(command > 1) {
  298. av2c = argc - 1 + command;
  299. av2 = xmalloc(sizeof(char*) * av2c);
  300. av2[0] = argv[0];
  301. for(count = 1; count <= command; count++) {
  302. av2[count] = av[count - 1].string;
  303. }
  304. for(; count < av2c; count++) {
  305. av2[count] = argv[count - command + 1];
  306. }
  307. }
  308. parseCommandLine(av2c, av2);
  309. free(av);
  310. if(av2 != argv) {
  311. free(av2);
  312. }
  313. if(optind < av2c) {
  314. filename = av2[optind];
  315. } else {
  316. die_with_message(NULL, NULL, "No script file specified");
  317. }
  318. break;
  319. }
  320. scriptchain = load_script(filename, NULL);
  321. BecomeUser(scriptchain->uid, scriptchain->gid);
  322. env = wcversion(env);
  323. readenv(env);
  324. sessionid(env);
  325. cbsiflags(env);
  326. prepareDictionary();
  327. tokenchain = build_token_list(scriptchain, NULL);
  328. preprocess_token_list(tokenchain);
  329. CookieVars(env);
  330. ReadCGIPOSTValues(env);
  331. ReadCGIQueryString(env);
  332. process_token_list(&script_text, tokenchain);
  333. subshell_setup(global.shell, env);
  334. subshell_doscript(&script_text, scriptchain->name);
  335. subshell_destroy();
  336. buffer_destroy(&script_text);
  337. free_token_list(tokenchain);
  338. free_list_chain(env);
  339. free_script_list(scriptchain);
  340. return 0;
  341. }
  342. list_t* myputenv(list_t* cur, char* str, char* prefix) {
  343. list_t* prev = NULL;
  344. size_t keylen;
  345. char* entry = NULL;
  346. char* temp = NULL;
  347. int array = 0;
  348. int len;
  349. temp = memchr(str, '=', strlen(str));
  350. if(temp == 0) {
  351. return cur;
  352. }
  353. keylen = (size_t) (temp - str);
  354. if(memcmp(str + keylen - 2, "[]", 2) == 0) {
  355. keylen = keylen - 2;
  356. array = 1;
  357. }
  358. entry = xmalloc(strlen (str) + strlen(prefix) + 1);
  359. entry[0] = '\0';
  360. if(strlen(prefix)) {
  361. strncat(entry, prefix, strlen(prefix));
  362. }
  363. if(array == 1) {
  364. strncat(entry, str, keylen);
  365. strcat(entry, str + keylen + 2);
  366. } else {
  367. strcat(entry, str);
  368. }
  369. len = keylen + strlen(prefix) + 1;
  370. while(cur != NULL) {
  371. if(memcmp(cur->buf, entry, len) == 0) {
  372. if(array == 1) {
  373. temp = xmalloc(strlen(cur->buf) + strlen(entry) - len + 2);
  374. memmove(temp, cur->buf, strlen(cur->buf) + 1);
  375. strcat(temp, "\n");
  376. strcat(temp, str + keylen + 3);
  377. free(entry);
  378. entry = temp;
  379. }
  380. free(cur->buf);
  381. if(prev != NULL) {
  382. prev->next = cur->next;
  383. }
  384. free(cur);
  385. cur = prev;
  386. }
  387. prev = cur;
  388. if(cur) {
  389. cur = (list_t*) cur->next;
  390. }
  391. }
  392. cur = xmalloc(sizeof(list_t));
  393. cur->buf = entry;
  394. if(prev != NULL) {
  395. prev->next = cur;
  396. }
  397. return cur;
  398. }
  399. int parseCommandLine(int argc, char *argv[]) {
  400. int c;
  401. int option_index = 0;
  402. optopt = 0;
  403. optind = 0;
  404. while((c = getopt_long(argc, argv, gs_short_options, ga_long_options, &option_index)) != -1) {
  405. switch(c) {
  406. case 's':
  407. global.shell = optarg;
  408. break;
  409. case 'u':
  410. global.uploadkb = atoi (optarg);
  411. break;
  412. case 'l':
  413. global.langdir = optarg;
  414. break;
  415. case 't':
  416. global.translation = optarg;
  417. break;
  418. case 'd':
  419. global.uploaddir = optarg;
  420. break;
  421. }
  422. }
  423. return optind;
  424. }
  425. void prepareDictionary() {
  426. translations = 0;
  427. if(getenv("WWW_LANGUAGE") != NULL) {
  428. language = strdup(getenv("WWW_LANGUAGE"));
  429. if(global.translation != NULL) {
  430. loadDictionary(global.translation);
  431. } else {
  432. loadDictionary("common");
  433. }
  434. }
  435. }
  436. int ReadCGIPOSTValues(list_t* env) {
  437. size_t content_length = 0;
  438. size_t max_len;
  439. size_t i, j, x;
  440. sbuffer_t sbuf;
  441. buffer_t token;
  442. unsigned char* data;
  443. const char* CONTENT_LENGTH = "CONTENT_LENGTH";
  444. if((getenv(CONTENT_LENGTH) == NULL) || (strtoul(getenv(CONTENT_LENGTH), NULL, 10) == 0)) {
  445. return 0;
  446. }
  447. if(getenv("CONTENT_TYPE")) {
  448. if(strncasecmp(getenv("CONTENT_TYPE"), "multipart/form-data", 19) == 0) {
  449. i = rfc2388_handler(env);
  450. return i;
  451. }
  452. }
  453. sbuffer_init(&sbuf, 32768);
  454. sbuf.fh = STDIN;
  455. if(getenv(CONTENT_LENGTH)) {
  456. sbuf.maxread = strtoul(getenv(CONTENT_LENGTH), NULL, 10);
  457. }
  458. buffer_init(&token);
  459. max_len = ((global.uploadkb == 0) ? 2048 : abs(global.uploadkb)) * 1024;
  460. do {
  461. x = sbuffer_read(&sbuf, "&");
  462. content_length += sbuf.len;
  463. if(content_length >= max_len && global.uploadkb != -1) {
  464. die_with_message(NULL, NULL, "Attempted to send content larger than allowed limits.");
  465. }
  466. if((x == 0) || (token.data)) {
  467. buffer_add(&token, (char*) sbuf.segment, sbuf.len);
  468. }
  469. if(x) {
  470. data = sbuf.segment;
  471. sbuf.segment[sbuf.len] = '\0';
  472. if(token.data) {
  473. buffer_add(&token, sbuf.segment + sbuf.len, 1);
  474. data = token.data;
  475. }
  476. j = strlen((char*) data);
  477. for(i = 0; i <= j; i++) {
  478. if(data[i] == '+') {
  479. data[i] = ' ';
  480. }
  481. }
  482. unescape_url((char*) data);
  483. myputenv(env, (char*) data, global.post_prefix);
  484. if(token.data) {
  485. buffer_reset(&token);
  486. }
  487. }
  488. } while(!sbuf.eof);
  489. sbuffer_destroy(&sbuf);
  490. buffer_destroy(&token);
  491. return 0;
  492. }
  493. int ReadCGIQueryString(list_t* env) {
  494. char* qs;
  495. char* token;
  496. int i;
  497. if(getenv("QUERY_STRING") != NULL) {
  498. qs = strdup(getenv("QUERY_STRING"));
  499. } else {
  500. return 0;
  501. }
  502. for(i = 0; qs[i]; i++) {
  503. if(qs[i] == '+') {
  504. qs[i] = ' ';
  505. }
  506. }
  507. token = strtok(qs, "&;");
  508. while(token) {
  509. unescape_url(token);
  510. myputenv(env, token, global.get_prefix);
  511. token = strtok(NULL, "&;");
  512. }
  513. free(qs);
  514. return 0;
  515. }
  516. void readenv(list_t* env) {
  517. extern char** environ;
  518. int count = 0;
  519. while(environ[count] != NULL) {
  520. myputenv(env, environ[count], global.null_prefix);
  521. count++;
  522. }
  523. }
  524. void sessionid(list_t* env) {
  525. char session[29];
  526. sprintf(session, "SESSIONID=%x%x", getpid(), (int) time(NULL));
  527. myputenv(env, session, global.cbsi_prefix);
  528. }
  529. char* skip_whitespace(char* instr) {
  530. while(isspace(*instr) && *instr) {
  531. instr++;
  532. }
  533. return instr;
  534. }
  535. char* trim(char* str) {
  536. char* end;
  537. while(isspace(*str) || *str == '"') {
  538. str++;
  539. }
  540. if(*str == 0) {
  541. return str;
  542. }
  543. end = str + strlen(str) - 1;
  544. while(end > str && (isspace(*end) || *end == '"')) {
  545. end--;
  546. }
  547. *(end + 1) = 0;
  548. return str;
  549. }
  550. void unescape_url(char* url) {
  551. int i, j;
  552. for(i = 0, j = 0; url[j]; ++i, ++j) {
  553. if((url[i] = url[j]) != '%') {
  554. continue;
  555. }
  556. if(!url[j + 1] || !url[j + 2]) {
  557. break;
  558. }
  559. url[i] = x2c(&url[j + 1]);
  560. j += 2;
  561. }
  562. url[i] = '\0';
  563. }
  564. void unlink_uploadlist() {
  565. token_t* me;
  566. me = global.uploadlist;
  567. while(me) {
  568. unlink(me->buf);
  569. free(me->buf);
  570. me = me->next;
  571. }
  572. }
  573. void uppercase(char* instr) {
  574. while(*instr != '\0') {
  575. *instr = toupper(*instr);
  576. instr++;
  577. }
  578. }
  579. list_t* wcversion(list_t* env) {
  580. char version[200];
  581. sprintf(version, "VERSION=%s", CBSI_VERSION);
  582. return(myputenv(env, version, global.cbsi_prefix));
  583. }
  584. char x2c(char* what) {
  585. char digit;
  586. digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A') + 10 : (what[0] - '0'));
  587. digit *= 16;
  588. digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10 : (what[1] - '0'));
  589. return digit;
  590. }
  591. void* xmalloc(size_t size) {
  592. void* buf;
  593. if((buf = malloc(size)) == NULL) {
  594. die_with_message(NULL, NULL, g_err_msg[E_MALLOC_FAIL]);
  595. }
  596. memset(buf, 0, size);
  597. return buf;
  598. }
  599. void* xrealloc(void* buf, size_t size) {
  600. if((buf = realloc(buf, size)) == NULL) {
  601. die_with_message(NULL, NULL, g_err_msg[E_MALLOC_FAIL]);
  602. }
  603. return buf;
  604. }