cgi.c
上传用户:lampled
上传日期:2007-01-07
资源大小:94k
文件大小:13k
源码类别:

Web服务器

开发平台:

Unix_Linux

  1. /* Copyright (C) 1995, 1996 by Sven Berkvens (sven@stack.nl) */
  2. #include "config.h"
  3. #include <sys/types.h>
  4. #ifdef HAVE_SYS_TIME_H
  5. #include <sys/time.h>
  6. #endif /* HAVE_SYS_TIME_H */
  7. #ifdef HAVE_SYS_RESOURCE_H
  8. #include <sys/resource.h>
  9. #endif /* HAVE_SYS_RESOURCE_H */
  10. #include <fcntl.h>
  11. #include <sys/signal.h>
  12. #include <sys/stat.h>
  13. #include <stdio.h>
  14. #include <errno.h>
  15. #ifdef HAVE_TIME_H
  16. #ifdef SYS_TIME_WITH_TIME
  17. #include <time.h>
  18. #endif /* SYS_TIME_WITH_TIME */
  19. #endif /* HAVE_TIME_H */
  20. #include <stdlib.h>
  21. #include <signal.h>
  22. #include <pwd.h>
  23. #include <grp.h>
  24. #include <unistd.h>
  25. #ifdef HAVE_ERR_H
  26. #include <err.h>
  27. #else /* Not HAVE_ERR_H */
  28. #include "err.h"
  29. #endif /* HAVE_ERR_H */
  30. #include <ctype.h>
  31. #ifdef HAVE_MEMORY_H
  32. #include <memory.h>
  33. #endif /* HAVE_MEMORY_H */
  34. #include "httpd.h"
  35. #include "local.h"
  36. #include "procname.h"
  37. #include "ssi.h"
  38. #include "extra.h"
  39. #include "path.h"
  40. #include "convert.h"
  41. #include "setenv.h"
  42. #include "string.h"
  43. #ifndef NOFORWARDS
  44. static const char *skipspaces PROTO((const char *));
  45. static VOID time_is_up PROTO((int));
  46. #endif /* NOFORWARDS */
  47. pid_t child;
  48. static const char *
  49. skipspaces DECL1C(char *, string)
  50. {
  51. while ((*string == ' ') || (*string == 't'))
  52. string++;
  53. return(string);
  54. }
  55. static VOID
  56. time_is_up DECL1(int, sig)
  57. {
  58. if (child != (pid_t)-1)
  59. {
  60. killpg(child, SIGTERM);
  61. mysleep(1);
  62. killpg(child, SIGKILL);
  63. }
  64. alarm_handler(sig);
  65. }
  66. extern VOID
  67. do_script DECL2C_(char *, path, int, headers)
  68. {
  69. struct stat statbuf;
  70. uid_t savedeuid, currentuid;
  71. gid_t savedegid, currentgid;
  72. long size, received, written, writetodo,
  73. totalwritten;
  74. char errmsg[MYBUFSIZ], fullpath[XS_PATH_MAX],
  75. status[MYBUFSIZ], contenttype[MYBUFSIZ],
  76. location[MYBUFSIZ], base[XS_PATH_MAX], *temp,
  77. name[XS_PATH_MAX], cookie[MYBUFSIZ], *nextslash;
  78. const char *file, *argv1, *header;
  79. int p[2], nph, count, nouid;
  80. unsigned int left;
  81. struct rlimit limits;
  82. const struct passwd *userinfo;
  83. FILE *auth;
  84. struct sigaction action;
  85. child = (pid_t)-1;
  86. #ifdef HAVE_SIGEMPTYSET
  87. sigemptyset(&action.sa_mask);
  88. #else /* Not HAVE_SIGEMPYSET */
  89. action.sa_mask = 0;
  90. #endif /* HAVE_SIGEMPTYSET */
  91. action.sa_handler = time_is_up;
  92. action.sa_flags = 0;
  93. sigaction(SIGALRM, &action, NULL);
  94. left = alarm(360); fflush(stdout);
  95. unsetenv("PATH_INFO"); unsetenv("PATH_TRANSLATED");
  96. unsetenv("QUERY_STRING"); unsetenv("SCRIPT_NAME");
  97. savedeuid = savedegid = -1;
  98. if (!origeuid)
  99. {
  100. savedeuid = geteuid(); seteuid(origeuid);
  101. savedegid = getegid(); setegid(origegid);
  102. }
  103. p[0] = p[1] = -1;
  104. userinfo = NULL;
  105. if (path[0] != '/')
  106. {
  107. if (headers)
  108. error("403 Invalid pathname");
  109. else
  110. printf("[Invalid pathname]n");
  111. goto END;
  112. }
  113. currentgid = -1;
  114. if (path[1] == '~')
  115. {
  116. strncpy(name, path + 2, 15);
  117. name[15] = 0;
  118. if ((temp = strchr(name, '/')))
  119. *temp = 0;
  120. if (!(userinfo = getpwnam(name)))
  121. {
  122. if (headers)
  123. error("404 User is unknown");
  124. else
  125. printf("[User `%s' is unknown]n", name);
  126. goto END;
  127. }
  128. if (transform_user_dir(base, userinfo, headers))
  129. {
  130. if (!headers)
  131. printf("[User directory error]n");
  132. goto END;
  133. }
  134. size = strlen(base);
  135. strcpy(base + size, HTTPD_SCRIPT_ROOT);
  136. strcat(base + size, "/");
  137. if (!origeuid)
  138. {
  139. setegid(currentgid = userinfo->pw_gid);
  140. setgroups(1, (gid_t *)&userinfo->pw_gid);
  141. seteuid(userinfo->pw_uid);
  142. }
  143. if (!(currentuid = geteuid()))
  144. {
  145. if (headers)
  146. error("500 Effective UID is not valid");
  147. else
  148. printf("[UID error]n");
  149. goto END;
  150. }
  151. file = path + strlen(name) + 3;
  152. } else
  153. {
  154. file = path + 1;
  155. strcpy(base, calcpath(HTTPD_SCRIPT_ROOT_P));
  156. strcat(base, "/");
  157. if (!origeuid)
  158. {
  159. setegid(currentgid = group_id);
  160. setgroups(1, &group_id);
  161. seteuid(user_id);
  162. }
  163. if (!(currentuid = geteuid()))
  164. {
  165. if (headers)
  166. error("500 Effective UID is not valid");
  167. else
  168. printf("[UID error]n");
  169. goto END;
  170. }
  171. }
  172. size = strlen(HTTPD_SCRIPT_ROOT);
  173. if (strncmp(file, HTTPD_SCRIPT_ROOT, size) || (file[size] != '/'))
  174. {
  175. if (headers)
  176. error("403 Not a CGI path");
  177. else
  178. printf("[Not a CGI path]n");
  179. goto END;
  180. }
  181. strncpy(name, file + size + 1, XS_PATH_MAX - 64);
  182. name[XS_PATH_MAX - 64] = 0;
  183. argv1 = NULL;
  184. if ((temp = strchr(name, '?')))
  185. {
  186. *(temp++) = 0;
  187. setenv("QUERY_STRING", temp, 1);
  188. if (!strchr(temp, '='))
  189. argv1 = temp;
  190. }
  191. nextslash = name;
  192. for (;;)
  193. {
  194. if (!(nextslash = strchr(nextslash, '/')))
  195. break;
  196. *nextslash = 0;
  197. sprintf(fullpath, "%s%s", base, name);
  198. if (stat(fullpath, &statbuf))
  199. {
  200. if (headers)
  201. error("403 Nonexistent CGI binary");
  202. else
  203. printf("[Nonexistent CGI binary]n");
  204. goto END;
  205. }
  206. if ((statbuf.st_mode & S_IFMT) == S_IFREG)
  207. break;
  208. *(nextslash++) = '/';
  209. }
  210. if (nextslash)
  211. {
  212. *nextslash = '/';
  213. setenv("PATH_INFO", nextslash, 1);
  214. setenv("PATH_TRANSLATED", convertpath(nextslash), 1);
  215. *nextslash = 0;
  216. }
  217. if (strstr(name, "..") || strstr(name, "/.x"))
  218. {
  219. if (headers)
  220. error("400 Invalid URI");
  221. else
  222. printf("[Invalid URI]n");
  223. goto END;
  224. }
  225. if (userinfo)
  226. sprintf(fullpath, "/~%s/%s/%s", userinfo->pw_name,
  227. HTTPD_SCRIPT_ROOT, name);
  228. else
  229. sprintf(fullpath, "/%s/%s", HTTPD_SCRIPT_ROOT, name);
  230. setenv("SCRIPT_NAME", fullpath, 1);
  231. if (headers)
  232. {
  233. sprintf(fullpath, "%s%s", base, name);
  234. if ((nextslash = strrchr(fullpath, '/')))
  235. {
  236. strcpy(nextslash + 1, AUTHFILE);
  237. if ((auth = fopen(fullpath, "r")))
  238. {
  239. if (check_auth(auth))
  240. goto END;
  241. }
  242. }
  243. }
  244. sprintf(fullpath, "%s%s", base, name);
  245. if (stat(fullpath, &statbuf))
  246. {
  247. if (headers)
  248. {
  249. sprintf(errmsg, "403 Cannot stat(`%s'): %s",
  250. fullpath, strerror(errno));
  251. error(errmsg);
  252. } else
  253. printf("[Cannot stat(`%s'): %s]n",
  254. fullpath, strerror(errno));
  255. goto END;
  256. }
  257. if (statbuf.st_mode & (S_IWGRP | S_IWOTH))
  258. {
  259. if (headers)
  260. {
  261. sprintf(errmsg, "403 `%s' is writable", fullpath);
  262. error(errmsg);
  263. } else
  264. printf("[`%s' is writable]n", fullpath);
  265. goto END;
  266. }
  267. if (userinfo && (statbuf.st_uid != currentuid))
  268. {
  269. if (headers)
  270. {
  271. sprintf(errmsg, "403 Invalid owner for `%s'", fullpath);
  272. error(errmsg);
  273. } else
  274. printf("[Invalid owner for `%s']n", fullpath);
  275. goto END;
  276. }
  277. nph = (!strncmp(name, "nph-", 4) || strstr(name, "/nph-"));
  278. nouid = (strstr(name, "/nph-nid-") || strstr(name, "/nid-") ||
  279. !strncmp(name, "nph-nid-", 8) || !strncmp(name, "nid-", 4));
  280. if (pipe(p))
  281. {
  282. sprintf(errmsg, "500 pipe() failed: %s", strerror(errno));
  283. if (headers)
  284. error(errmsg);
  285. else
  286. printf("[%s]n", errmsg);
  287. goto END;
  288. }
  289. switch(child = fork())
  290. {
  291. case -1:
  292. sprintf(errmsg, "500 fork() failed: %s", strerror(errno));
  293. if (headers)
  294. error(errmsg);
  295. else
  296. printf("[%s]n", errmsg);
  297. goto END;
  298. case 0:
  299. #ifdef RLIMIT_CPU
  300. limits.rlim_cur = 120; limits.rlim_max = 128;
  301. setrlimit(RLIMIT_CPU, &limits);
  302. #endif /* RLIMIT_CPU */
  303. #ifdef RLIMIT_CORE
  304. limits.rlim_cur = limits.rlim_max = 0;
  305. setrlimit(RLIMIT_CORE, &limits);
  306. #endif /* RLIMIT_CORE */
  307. #ifdef RLIMIT_MEMLOCK
  308. limits.rlim_cur = limits.rlim_max = 1;
  309. setrlimit(RLIMIT_MEMLOCK, &limits);
  310. #endif /* RLIMIT_MEMLOCK */
  311. dup2(p[1], 1);
  312. #ifdef HAVE_SETSID
  313. if (setsid() == -1)
  314. {
  315. printf("Content-type: text/plainrnrn");
  316. printf("[setsid() failed]n");
  317. exit(1);
  318. }
  319. #else /* Not HAVE_SETSID */
  320. if (setpgrp(getpid(), 0)) == -1)
  321. {
  322. printf("Content-type: text/plainrnrn");
  323. printf("[setpgrp() failed]n");
  324. exit(1);
  325. }
  326. #endif /* HAVE_SETSID */
  327. for (count = 3; count < 64; count++)
  328. close(count);
  329. if (!origeuid)
  330. {
  331. seteuid(origeuid);
  332. if (nouid)
  333. {
  334. currentuid = user_id;
  335. currentgid = group_id;
  336. }
  337. setgid(currentgid); setuid(currentuid);
  338. if (!getuid() || !geteuid() ||
  339. (getuid() != currentuid) ||
  340. (getgid() != currentgid))
  341. {
  342. printf("Content-type: text/plainrnrn");
  343. printf("[Invalid UID setting]n");
  344. printf("UID = %ld, EUID = %ldn",
  345. (long)getuid(), (long)geteuid());
  346. exit(1);
  347. }
  348. }
  349. setenv("PATH", SCRIPT_PATH, 1);
  350. if (chdir(base))
  351. {
  352. printf("Content-type: text/plainrnrn");
  353. printf("[Cannot change directory]n");
  354. exit(1);
  355. }
  356. execl(fullpath, name, argv1, NULL);
  357. if (nph)
  358. {
  359. sprintf(errmsg, "500 execl(`%s'): %s",
  360. fullpath, strerror(errno));
  361. error(errmsg);
  362. } else
  363. {
  364. printf("Content-type: text/plainrnrn");
  365. printf("[execl(`%s') failed: %s",
  366. fullpath, strerror(errno));
  367. }
  368. exit(1);
  369. default:
  370. close(p[1]);
  371. break;
  372. }
  373. status[0] = contenttype[0] = location[0] = cookie[0] = 0;
  374. netbufind = netbufsiz = 0; readlinemode = 1;
  375. if (!nph)
  376. {
  377. while (1)
  378. {
  379. if (readline(p[0], errmsg) != ERR_NONE)
  380. {
  381. if (headers)
  382. error("503 Script did not end header");
  383. else
  384. printf("[Script did not end header]n");
  385. goto END;
  386. }
  387. received = strlen(errmsg);
  388. while ((received > 0) && (errmsg[received - 1] < 32))
  389. errmsg[--received] = 0;
  390. header = skipspaces(errmsg);
  391. if (!header[0])
  392. break;
  393. if (!strncasecmp(header, "Status:", 7))
  394. strcpy(status, skipspaces(header + 7));
  395. else if (!strncasecmp(header, "Location:", 9))
  396. strcpy(location, skipspaces(header + 9));
  397. else if (!strncasecmp(header, "Content-type:", 13))
  398. strcpy(contenttype, skipspaces(header + 13));
  399. else if (!strncasecmp(header, "Set-cookie:", 11))
  400. strcpy(cookie, skipspaces(header + 11));
  401. else
  402. {
  403. fprintf(stderr, "[%s] httpd: Invalid header `%s' from script `%s'n",
  404. currenttime, header, name);
  405. if (headers)
  406. error("503 Script gave invalid header");
  407. else
  408. printf("[Script gave invalid header]n");
  409. goto END;
  410. }
  411. }
  412. if (headers)
  413. {
  414. printf("%s %srn", version, status[0] ? status :
  415. (location[0] ? "302 Moved" : "200 OK"));
  416. printf("Content-type: %srn",
  417. (!contenttype[0]) ? "text/html" : contenttype);
  418. switch(location[0])
  419. {
  420. case '/':
  421. if (port == 80)
  422. printf("Location: http://%s%srn",
  423. thishostname, location);
  424. else
  425. printf("Location: http://%s:%d%srn",
  426. thishostname, port, location);
  427. break;
  428. case 0:
  429. break;
  430. default:
  431. printf("Location: %srn", location);
  432. break;
  433. }
  434. if (cookie[0])
  435. printf("Set-cookie: %srn", cookie);
  436. setcurrenttime();
  437. printf("Date: %srnLast-modified: %srn",
  438. currenttime, currenttime);
  439. printf("Server: %srnrn", SERVER_IDENT);
  440. } else
  441. {
  442. if (location[0])
  443. printf("[Internal `location' not supported]n");
  444. }
  445. } else
  446. {
  447. while (1)
  448. {
  449. if (readline(p[0], errmsg) != ERR_NONE)
  450. {
  451. if (headers)
  452. error("503 Script did not end header");
  453. else
  454. printf("[Script did not end header]n");
  455. goto END;
  456. }
  457. received = strlen(errmsg);
  458. if (headers)
  459. printf("%s", errmsg);
  460. while ((received > 0) && (errmsg[received - 1] < 32))
  461. errmsg[--received] = 0;
  462. if (!errmsg[0])
  463. break;
  464. }
  465. }
  466. fflush(stdout);
  467. if ((totalwritten = netbufsiz - netbufind) > 0)
  468. {
  469. writetodo = totalwritten; temp = netbuf + netbufind;
  470. while (writetodo > 0)
  471. {
  472. switch(written = write(fileno(stdout), temp, writetodo))
  473. {
  474. case -1:
  475. if (errno == EINTR)
  476. break;
  477. printf("[Connection closed: %s (fd = %d, temp = %p, todo = %ld]n",
  478. strerror(errno), fileno(stdout), temp,
  479. writetodo);
  480. goto END;
  481. case 0:
  482. printf("[Connection closed: couldn't write]n");
  483. goto END;
  484. default:
  485. writetodo -= written;
  486. temp += written;
  487. }
  488. }
  489. }
  490. for (;;)
  491. {
  492. received = read(p[0], errmsg, MYBUFSIZ);
  493. if (received == -1)
  494. {
  495. if (errno == EINTR)
  496. continue;
  497. printf("[read() error from CGI: %s]", strerror(errno));
  498. break;
  499. } else if (received == 0)
  500. break;
  501. writetodo = received; temp = errmsg;
  502. while (writetodo > 0)
  503. {
  504. switch(written = write(fileno(stdout), temp, writetodo))
  505. {
  506. case -1:
  507. if (errno == EINTR)
  508. break;
  509. printf("[Connection closed: %s (fd = %d, temp = %p, todo = %ld]n",
  510. strerror(errno), fileno(stdout), temp,
  511. writetodo);
  512. goto END;
  513. case 0:
  514. printf("[Connection closed: couldn't write]n");
  515. goto END;
  516. default:
  517. writetodo -= written;
  518. temp += written;
  519. }
  520. }
  521. totalwritten += received;
  522. }
  523. {
  524. char buffer[80];
  525. time_t theclock;
  526. time(&theclock);
  527. strftime(buffer, 80, "%d/%b/%Y:%H:%M:%S", localtime(&theclock));
  528. fprintf(access_log, "%s - - [%s +0000] "%s %s %s" 200 %ldn",
  529. remotehost, buffer, getenv("REQUEST_METHOD"), path,
  530. version, totalwritten > 0 ? totalwritten : (long)0);
  531. }
  532. END:
  533. close(p[0]); close(p[1]); fflush(stdout);
  534. if (!origeuid)
  535. {
  536. seteuid(origeuid); setegid(savedegid); seteuid(savedeuid);
  537. }
  538. #ifdef HAVE_SIGEMPTYSET
  539. sigemptyset(&action.sa_mask);
  540. #else /* Not HAVE_SIGEMPYSET */
  541. action.sa_mask = 0;
  542. #endif /* HAVE_SIGEMPTYSET */
  543. action.sa_handler = alarm_handler;
  544. action.sa_flags = 0;
  545. sigaction(SIGALRM, &action, NULL);
  546. alarm(left);
  547. }