• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/cgi-io/main.c

  1 /*
  2  * cgi-io - LuCI non-RPC helper
  3  *
  4  *   Copyright (C) 2013 Jo-Philipp Wich <jo@mein.io>
  5  *
  6  * Permission to use, copy, modify, and/or distribute this software for any
  7  * purpose with or without fee is hereby granted, provided that the above
  8  * copyright notice and this permission notice appear in all copies.
  9  *
 10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 17  */
 18 
 19 #define _GNU_SOURCE /* splice(), SPLICE_F_MORE */
 20 
 21 #include <stdio.h>
 22 #include <stdlib.h>
 23 #include <stdbool.h>
 24 #include <unistd.h>
 25 #include <string.h>
 26 #include <errno.h>
 27 #include <fcntl.h>
 28 #include <ctype.h>
 29 #include <sys/stat.h>
 30 #include <sys/wait.h>
 31 #include <sys/sendfile.h>
 32 #include <sys/ioctl.h>
 33 #include <linux/fs.h>
 34 
 35 #include <libubus.h>
 36 #include <libubox/blobmsg.h>
 37 
 38 #include "util.h"
 39 #include "multipart_parser.h"
 40 
 41 #ifndef O_TMPFILE
 42 #define O_TMPFILE       (020000000 | O_DIRECTORY)
 43 #endif
 44 
 45 #define READ_BLOCK 4096
 46 
 47 enum part {
 48         PART_UNKNOWN,
 49         PART_SESSIONID,
 50         PART_FILENAME,
 51         PART_FILEMODE,
 52         PART_FILEDATA
 53 };
 54 
 55 const char *parts[] = {
 56         "(bug)",
 57         "sessionid",
 58         "filename",
 59         "filemode",
 60         "filedata",
 61 };
 62 
 63 struct state
 64 {
 65         bool is_content_disposition;
 66         enum part parttype;
 67         char *sessionid;
 68         char *filename;
 69         bool filedata;
 70         int filemode;
 71         int filefd;
 72         int tempfd;
 73 };
 74 
 75 static struct state st;
 76 
 77 #ifndef UNIT_TESTING
 78 
 79 enum {
 80         SES_ACCESS,
 81         __SES_MAX,
 82 };
 83 
 84 static const struct blobmsg_policy ses_policy[__SES_MAX] = {
 85         [SES_ACCESS] = { .name = "access", .type = BLOBMSG_TYPE_BOOL },
 86 };
 87 
 88 static void
 89 session_access_cb(struct ubus_request *req, int type, struct blob_attr *msg)
 90 {
 91         struct blob_attr *tb[__SES_MAX];
 92         bool *allow = (bool *)req->priv;
 93 
 94         if (!msg)
 95                 return;
 96 
 97         blobmsg_parse(ses_policy, __SES_MAX, tb, blob_data(msg), blob_len(msg));
 98 
 99         if (tb[SES_ACCESS])
100                 *allow = blobmsg_get_bool(tb[SES_ACCESS]);
101 }
102 #endif
103 
104 static bool
105 session_access(const char *sid, const char *scope, const char *obj, const char *func)
106 {
107 #ifdef UNIT_TESTING
108         return true;
109 #else
110         uint32_t id;
111         bool allow = false;
112         struct ubus_context *ctx;
113         static struct blob_buf req;
114 
115         ctx = ubus_connect(NULL);
116 
117         if (!ctx || !obj || ubus_lookup_id(ctx, "session", &id))
118                 goto out;
119 
120         blob_buf_init(&req, 0);
121         blobmsg_add_string(&req, "ubus_rpc_session", sid);
122         blobmsg_add_string(&req, "scope", scope);
123         blobmsg_add_string(&req, "object", obj);
124         blobmsg_add_string(&req, "function", func);
125 
126         ubus_invoke(ctx, id, "access", req.head, session_access_cb, &allow, 500);
127 
128 out:
129         if (ctx)
130                 ubus_free(ctx);
131 
132         return allow;
133 #endif
134 }
135 
136 static char *
137 checksum(const char *applet, size_t sumlen, const char *file)
138 {
139         pid_t pid;
140         int r;
141         int fds[2];
142         static char chksum[65];
143 
144         if (pipe(fds))
145                 return NULL;
146 
147         switch ((pid = fork()))
148         {
149         case -1:
150                 return NULL;
151 
152         case 0:
153                 uloop_done();
154 
155                 dup2(fds[1], 1);
156 
157                 close(0);
158                 close(2);
159                 close(fds[0]);
160                 close(fds[1]);
161 
162                 if (execl("/bin/busybox", "/bin/busybox", applet, file, NULL))
163                         return NULL;
164 
165                 break;
166 
167         default:
168                 memset(chksum, 0, sizeof(chksum));
169                 r = read(fds[0], chksum, sumlen);
170 
171                 waitpid(pid, NULL, 0);
172                 close(fds[0]);
173                 close(fds[1]);
174 
175                 if (r < 0)
176                         return NULL;
177         }
178 
179         return chksum;
180 }
181 
182 static int
183 response(bool success, const char *message)
184 {
185         char *chksum;
186         struct stat s;
187 
188         printf("Status: 200 OK\r\n");
189         printf("Content-Type: text/plain\r\n\r\n{\n");
190 
191         if (success)
192         {
193                 if (!stat(st.filename, &s))
194                         printf("\t\"size\": %u,\n", (unsigned int)s.st_size);
195                 else
196                         printf("\t\"size\": null,\n");
197 
198                 chksum = checksum("md5sum", 32, st.filename);
199                 printf("\t\"checksum\": %s%s%s,\n",
200                         chksum ? "\"" : "",
201                         chksum ? chksum : "null",
202                         chksum ? "\"" : "");
203 
204                 chksum = checksum("sha256sum", 64, st.filename);
205                 printf("\t\"sha256sum\": %s%s%s\n",
206                         chksum ? "\"" : "",
207                         chksum ? chksum : "null",
208                         chksum ? "\"" : "");
209         }
210         else
211         {
212                 if (message)
213                         printf("\t\"message\": \"%s\",\n", message);
214 
215                 printf("\t\"failure\": [ %u, \"%s\" ]\n", errno, strerror(errno));
216 
217                 if (st.filefd > -1 && st.filename)
218                         unlink(st.filename);
219         }
220 
221         printf("}\n");
222 
223         return -1;
224 }
225 
226 static int
227 failure(int code, int e, const char *message)
228 {
229         printf("Status: %d %s\r\n", code, message);
230         printf("Content-Type: text/plain\r\n\r\n");
231         printf("%s", message);
232 
233         if (e)
234                 printf(": %s", strerror(e));
235 
236         printf("\n");
237 
238         return -1;
239 }
240 
241 static int
242 filecopy(void)
243 {
244         int len;
245         char buf[READ_BLOCK];
246 
247         if (!st.filedata)
248         {
249                 close(st.tempfd);
250                 errno = EINVAL;
251                 return response(false, "No file data received");
252         }
253 
254         snprintf(buf, sizeof(buf), "/proc/self/fd/%d", st.tempfd);
255 
256         if (unlink(st.filename) < 0 && errno != ENOENT)
257         {
258                 close(st.tempfd);
259                 return response(false, "Failed to unlink existing file");
260         }
261 
262         if (linkat(AT_FDCWD, buf, AT_FDCWD, st.filename, AT_SYMLINK_FOLLOW) < 0)
263         {
264                 if (lseek(st.tempfd, 0, SEEK_SET) < 0)
265                 {
266                         close(st.tempfd);
267                         return response(false, "Failed to rewind temp file");
268                 }
269 
270                 st.filefd = open(st.filename, O_CREAT | O_TRUNC | O_WRONLY, 0600);
271 
272                 if (st.filefd < 0)
273                 {
274                         close(st.tempfd);
275                         return response(false, "Failed to open target file");
276                 }
277 
278                 while ((len = read(st.tempfd, buf, sizeof(buf))) > 0)
279                 {
280                         if (write(st.filefd, buf, len) != len)
281                         {
282                                 close(st.tempfd);
283                                 close(st.filefd);
284                                 return response(false, "I/O failure while writing target file");
285                         }
286                 }
287 
288                 close(st.filefd);
289         }
290 
291         close(st.tempfd);
292 
293         if (chmod(st.filename, st.filemode))
294                 return response(false, "Failed to chmod target file");
295 
296         return 0;
297 }
298 
299 static int
300 header_field(multipart_parser *p, const char *data, size_t len)
301 {
302         st.is_content_disposition = !strncasecmp(data, "Content-Disposition", len);
303         return 0;
304 }
305 
306 static int
307 header_value(multipart_parser *p, const char *data, size_t len)
308 {
309         size_t i, j;
310 
311         if (!st.is_content_disposition)
312                 return 0;
313 
314         if (len < 10 || strncasecmp(data, "form-data", 9))
315                 return 0;
316 
317         for (data += 9, len -= 9; len > 0 && (*data == ' ' || *data == ';'); data++, len--);
318 
319         if (len < 8 || strncasecmp(data, "name=\"", 6))
320                 return 0;
321 
322         for (data += 6, len -= 6, i = 1; i < len; i++)
323         {
324                 if (data[i] == '"')
325                 {
326                         for (j = 1; j < sizeof(parts) / sizeof(parts[0]); j++)
327                                 if (!strncmp(data, parts[j], i - 1))
328                                         st.parttype = j;
329 
330                         break;
331                 }
332         }
333 
334         return 0;
335 }
336 
337 static int
338 data_begin_cb(multipart_parser *p)
339 {
340         if (st.parttype == PART_FILEDATA)
341         {
342                 if (!st.sessionid)
343                         return response(false, "File data without session");
344 
345                 if (!st.filename)
346                         return response(false, "File data without name");
347 
348                 if (!session_access(st.sessionid, "file", st.filename, "write"))
349                         return response(false, "Access to path denied by ACL");
350 
351                 st.tempfd = open("/tmp", O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR);
352 
353                 if (st.tempfd < 0)
354                         return response(false, "Failed to create temporary file");
355         }
356 
357         return 0;
358 }
359 
360 static int
361 data_cb(multipart_parser *p, const char *data, size_t len)
362 {
363         int wlen = len;
364 
365         switch (st.parttype)
366         {
367         case PART_SESSIONID:
368                 st.sessionid = datadup(data, len);
369                 break;
370 
371         case PART_FILENAME:
372                 st.filename = canonicalize_path(data, len);
373                 break;
374 
375         case PART_FILEMODE:
376                 st.filemode = strtoul(data, NULL, 8);
377                 break;
378 
379         case PART_FILEDATA:
380                 if (write(st.tempfd, data, len) != wlen)
381                 {
382                         close(st.tempfd);
383                         return response(false, "I/O failure while writing temporary file");
384                 }
385 
386                 if (!st.filedata)
387                         st.filedata = !!wlen;
388 
389                 break;
390 
391         default:
392                 break;
393         }
394 
395         return 0;
396 }
397 
398 static int
399 data_end_cb(multipart_parser *p)
400 {
401         if (st.parttype == PART_SESSIONID)
402         {
403                 if (!session_access(st.sessionid, "cgi-io", "upload", "write"))
404                 {
405                         errno = EPERM;
406                         return response(false, "Upload permission denied");
407                 }
408         }
409         else if (st.parttype == PART_FILEDATA)
410         {
411                 if (st.tempfd < 0)
412                         return response(false, "Internal program failure");
413 
414 #if 0
415                 /* prepare directory */
416                 for (ptr = st.filename; *ptr; ptr++)
417                 {
418                         if (*ptr == '/')
419                         {
420                                 *ptr = 0;
421 
422                                 if (mkdir(st.filename, 0755))
423                                 {
424                                         unlink(st.tmpname);
425                                         return response(false, "Failed to create destination directory");
426                                 }
427 
428                                 *ptr = '/';
429                         }
430                 }
431 #endif
432 
433                 if (filecopy())
434                         return -1;
435 
436                 return response(true, NULL);
437         }
438 
439         st.parttype = PART_UNKNOWN;
440         return 0;
441 }
442 
443 static multipart_parser *
444 init_parser(void)
445 {
446         char *boundary;
447         const char *var;
448 
449         multipart_parser *p;
450         static multipart_parser_settings s = {
451                 .on_part_data        = data_cb,
452                 .on_headers_complete = data_begin_cb,
453                 .on_part_data_end    = data_end_cb,
454                 .on_header_field     = header_field,
455                 .on_header_value     = header_value
456         };
457 
458         var = getenv("CONTENT_TYPE");
459 
460         if (!var || strncmp(var, "multipart/form-data;", 20))
461                 return NULL;
462 
463         for (var += 20; *var && *var != '='; var++);
464 
465         if (*var++ != '=')
466                 return NULL;
467 
468         boundary = malloc(strlen(var) + 3);
469 
470         if (!boundary)
471                 return NULL;
472 
473         strcpy(boundary, "--");
474         strcpy(boundary + 2, var);
475 
476         st.tempfd = -1;
477         st.filefd = -1;
478         st.filemode = 0600;
479 
480         p = multipart_parser_init(boundary, &s);
481 
482         free(boundary);
483 
484         return p;
485 }
486 
487 static int
488 main_upload(int argc, char *argv[])
489 {
490         int rem, len;
491         bool done = false;
492         char buf[READ_BLOCK];
493         multipart_parser *p;
494 
495         p = init_parser();
496 
497         if (!p)
498         {
499                 errno = EINVAL;
500                 return response(false, "Invalid request");
501         }
502 
503         while ((len = read(0, buf, sizeof(buf))) > 0)
504         {
505                 if (!done) {
506                         rem = multipart_parser_execute(p, buf, len);
507                         done = (rem < len);
508                 }
509         }
510 
511         multipart_parser_free(p);
512 
513         return 0;
514 }
515 
516 static void
517 free_charp(char **ptr)
518 {
519         free(*ptr);
520 }
521 
522 #define autochar __attribute__((__cleanup__(free_charp))) char
523 
524 static int
525 main_download(int argc, char **argv)
526 {
527         char *fields[] = { "sessionid", NULL, "path", NULL, "filename", NULL, "mimetype", NULL };
528         unsigned long long size = 0;
529         char *p, buf[READ_BLOCK];
530         ssize_t len = 0;
531         struct stat s;
532         int rfd;
533 
534         autochar *post = postdecode(fields, 4);
535         (void) post;
536 
537         if (!fields[1] || !session_access(fields[1], "cgi-io", "download", "read"))
538                 return failure(403, 0, "Download permission denied");
539 
540         /*
541          * Canonicalize the requested path before checking it against the session
542          * ACL and opening it. The rpcd ACL matcher uses fnmatch() without
543          * FNM_PATHNAME, so a "../" suffix would otherwise match an allowed glob
544          * prefix during the ACL check while open() resolves the embedded ".."
545          * and reaches a file outside of that prefix.
546          */
547         autochar *path = fields[3] ? canonicalize_path(fields[3], strlen(fields[3])) : NULL;
548 
549         if (!path || !session_access(fields[1], "file", path, "read"))
550                 return failure(403, 0, "Access to path denied by ACL");
551 
552         if (stat(path, &s))
553                 return failure(404, errno, "Failed to stat requested path");
554 
555         if (!S_ISREG(s.st_mode) && !S_ISBLK(s.st_mode))
556                 return failure(403, 0, "Requested path is not a regular file or block device");
557 
558         for (p = fields[5]; p && *p; p++)
559                 if (!isalnum(*p) && !strchr(" ()<>@,;:[]?.=%-", *p))
560                         return failure(400, 0, "Invalid characters in filename");
561 
562         for (p = fields[7]; p && *p; p++)
563                 if (!isalnum(*p) && !strchr(" .;=/-", *p))
564                         return failure(400, 0, "Invalid characters in mimetype");
565 
566         rfd = open(path, O_RDONLY);
567 
568         if (rfd < 0)
569                 return failure(500, errno, "Failed to open requested path");
570 
571         if (S_ISBLK(s.st_mode))
572                 ioctl(rfd, BLKGETSIZE64, &size);
573         else
574                 size = (unsigned long long)s.st_size;
575 
576         printf("Status: 200 OK\r\n");
577         printf("Content-Type: %s\r\n", fields[7] ? fields[7] : "application/octet-stream");
578 
579         if (fields[5])
580                 printf("Content-Disposition: attachment; filename=\"%s\"\r\n", fields[5]);
581 
582         if (size > 0) {
583                 printf("Content-Length: %llu\r\n\r\n", size);
584                 fflush(stdout);
585 
586                 while (size > 0) {
587                         len = sendfile(1, rfd, NULL, size);
588 
589                         if (len == -1) {
590                                 if (errno == ENOSYS || errno == EINVAL) {
591                                         while ((len = read(rfd, buf, sizeof(buf))) > 0)
592                                                 fwrite(buf, len, 1, stdout);
593 
594                                         fflush(stdout);
595                                         break;
596                                 }
597 
598                                 if (errno == EINTR || errno == EAGAIN)
599                                         continue;
600                         }
601 
602                         if (len <= 0)
603                                 break;
604 
605                         size -= len;
606                 }
607         }
608         else {
609                 printf("\r\n");
610 
611                 while ((len = read(rfd, buf, sizeof(buf))) > 0)
612                         fwrite(buf, len, 1, stdout);
613 
614                 fflush(stdout);
615         }
616 
617         close(rfd);
618 
619         return 0;
620 }
621 
622 static int
623 main_backup(int argc, char **argv)
624 {
625         pid_t pid;
626         time_t now;
627         int r;
628         int len;
629         int status;
630         int fds[2];
631         char datestr[16] = { 0 };
632         char hostname[64] = { 0 };
633         char *fields[] = { "sessionid", NULL };
634 
635         autochar *post = postdecode(fields, 1);
636         (void) post;
637 
638         if (!fields[1] || !session_access(fields[1], "cgi-io", "backup", "read"))
639                 return failure(403, 0, "Backup permission denied");
640 
641         if (pipe(fds))
642                 return failure(500, errno, "Failed to spawn pipe");
643 
644         switch ((pid = fork()))
645         {
646         case -1:
647                 return failure(500, errno, "Failed to fork process");
648 
649         case 0:
650                 dup2(fds[1], 1);
651 
652                 close(0);
653                 close(2);
654                 close(fds[0]);
655                 close(fds[1]);
656 
657                 r = chdir("/");
658                 if (r < 0)
659                         return failure(500, errno, "Failed chdir('/')");
660 
661                 execl("/sbin/sysupgrade", "/sbin/sysupgrade",
662                       "--create-backup", "-", NULL);
663 
664                 return -1;
665 
666         default:
667                 close(fds[1]);
668 
669                 now = time(NULL);
670                 strftime(datestr, sizeof(datestr) - 1, "%Y-%m-%d", localtime(&now));
671 
672                 if (gethostname(hostname, sizeof(hostname) - 1))
673                         sprintf(hostname, "OpenWrt");
674 
675                 printf("Status: 200 OK\r\n");
676                 printf("Content-Type: application/x-targz\r\n");
677                 printf("Content-Disposition: attachment; "
678                        "filename=\"backup-%s-%s.tar.gz\"\r\n\r\n", hostname, datestr);
679 
680                 fflush(stdout);
681 
682                 do {
683                         len = splice(fds[0], NULL, 1, NULL, READ_BLOCK, SPLICE_F_MORE);
684                 } while (len > 0 || (len == -1 && errno == EINTR));
685 
686                 waitpid(pid, &status, 0);
687 
688                 close(fds[0]);
689 
690                 return 0;
691         }
692 }
693 
694 
695 static const char *
696 lookup_executable(const char *cmd)
697 {
698         size_t plen = 0, clen;
699         static char path[PATH_MAX];
700         char *search, *p;
701         struct stat s;
702 
703         if (!cmd)
704                 return NULL;
705 
706         clen = strlen(cmd) + 1;
707 
708         if (!stat(cmd, &s) && S_ISREG(s.st_mode))
709                 return cmd;
710 
711         search = getenv("PATH");
712 
713         if (!search)
714                 search = "/bin:/usr/bin:/sbin:/usr/sbin";
715 
716         p = search;
717 
718         do {
719                 if (*p != ':' && *p != '\0')
720                         continue;
721 
722                 plen = p - search;
723 
724                 if ((plen + clen) >= sizeof(path))
725                         continue;
726 
727                 strncpy(path, search, plen);
728                 sprintf(path + plen, "/%s", cmd);
729 
730                 if (!stat(path, &s) && S_ISREG(s.st_mode))
731                         return path;
732 
733                 search = p + 1;
734         } while (*p++);
735 
736         return NULL;
737 }
738 
739 static int
740 main_exec(int argc, char **argv)
741 {
742         char *fields[] = { "sessionid", NULL, "command", NULL, "filename", NULL, "mimetype", NULL, "stderr", NULL };
743         int i, devnull, status, fds[2];
744         bool allowed = false, redir_stderr = false;
745         ssize_t len = 0;
746         size_t exelen;
747         const char *exe;
748         char *p, **args;
749         autochar *canon = NULL;
750         pid_t pid;
751 
752         autochar *post = postdecode(fields, 5);
753         (void) post;
754 
755         if (!fields[1] || !session_access(fields[1], "cgi-io", "exec", "read"))
756                 return failure(403, 0, "Exec permission denied");
757 
758         for (p = fields[5]; p && *p; p++)
759                 if (!isalnum(*p) && !strchr(" ()<>@,;:[]?.=%-", *p))
760                         return failure(400, 0, "Invalid characters in filename");
761 
762         for (p = fields[7]; p && *p; p++)
763                 if (!isalnum(*p) && !strchr(" .;=/-", *p))
764                         return failure(400, 0, "Invalid characters in mimetype");
765 
766         p = fields[9];
767         if (p && p[0] == '1' && p[1] == '\0')
768                 redir_stderr = true;
769 
770         args = fields[3] ? parse_command(fields[3]) : NULL;
771 
772         if (!args)
773                 return failure(400, 0, "Invalid command parameter");
774 
775         /*
776          * Canonicalize the executable path before checking it against the
777          * session ACL and executing it. As the rpcd ACL matcher uses fnmatch()
778          * without FNM_PATHNAME, a "../" sequence in the leading token would
779          * otherwise let an allowed glob prefix match while execv() resolves to a
780          * binary outside of that prefix. The leading token is everything up to
781          * the first argument separator.
782          */
783         exelen = args[1] ? (size_t)(args[1] - args[0] - 1) : strlen(args[0]);
784         canon = canonicalize_path(args[0], exelen);
785 
786         if (!canon) {
787                 free(args);
788                 return failure(400, 0, "Invalid command parameter");
789         }
790 
791         /* First check if we find an ACL match for the whole cmdline ... */
792         {
793                 char *tail = args[0] + exelen;
794                 autochar *cmdline = malloc(strlen(canon) + strlen(tail) + 1);
795 
796                 if (!cmdline) {
797                         free(args);
798                         return failure(500, errno, "Out of memory");
799                 }
800 
801                 strcpy(cmdline, canon);
802                 strcat(cmdline, tail);
803 
804                 allowed = session_access(fields[1], "file", cmdline, "exec");
805         }
806 
807         /* Now split the command vector... */
808         for (i = 1; args[i]; i++)
809                 args[i][-1] = 0;
810 
811         /* Find executable... */
812         exe = lookup_executable(canon);
813 
814         if (!exe) {
815                 free(args);
816                 return failure(404, 0, "Executable not found");
817         }
818 
819         /* If there was no ACL match, check for a match on the executable */
820         if (!allowed && !session_access(fields[1], "file", exe, "exec")) {
821                 free(args);
822                 return failure(403, 0, "Access to command denied by ACL");
823         }
824 
825         if (pipe(fds)) {
826                 free(args);
827                 return failure(500, errno, "Failed to spawn pipe");
828         }
829 
830         switch ((pid = fork()))
831         {
832         case -1:
833                 free(args);
834                 close(fds[0]);
835                 close(fds[1]);
836                 return failure(500, errno, "Failed to fork process");
837 
838         case 0:
839                 devnull = open("/dev/null", O_RDWR);
840 
841                 if (devnull > -1) {
842                         dup2(devnull, 0);
843                         if (!redir_stderr)
844                                 dup2(devnull, 2);
845                         close(devnull);
846                 }
847                 else {
848                         close(0);
849                         close(2);
850                 }
851 
852                 dup2(fds[1], 1);
853                 if (redir_stderr)
854                         dup2(fds[1], 2);
855                 close(fds[0]);
856                 close(fds[1]);
857 
858                 if (chdir("/") < 0) {
859                         free(args);
860                         return failure(500, errno, "Failed chdir('/')");
861                 }
862 
863                 if (execv(exe, args) < 0) {
864                         free(args);
865                         return failure(500, errno, "Failed execv(...)");
866                 }
867 
868                 return -1;
869 
870         default:
871                 close(fds[1]);
872 
873                 printf("Status: 200 OK\r\n");
874                 printf("Content-Type: %s\r\n",
875                        fields[7] ? fields[7] : "application/octet-stream");
876 
877                 if (fields[5])
878                         printf("Content-Disposition: attachment; filename=\"%s\"\r\n",
879                                fields[5]);
880 
881                 printf("\r\n");
882                 fflush(stdout);
883 
884                 do {
885                         len = splice(fds[0], NULL, 1, NULL, READ_BLOCK, SPLICE_F_MORE);
886                 } while (len > 0 || (len == -1 && errno == EINTR));
887 
888                 waitpid(pid, &status, 0);
889 
890                 close(fds[0]);
891                 free(args);
892 
893                 return 0;
894         }
895 }
896 
897 int main(int argc, char **argv)
898 {
899         if (strstr(argv[0], "cgi-upload"))
900                 return main_upload(argc, argv);
901         else if (strstr(argv[0], "cgi-download"))
902                 return main_download(argc, argv);
903         else if (strstr(argv[0], "cgi-backup"))
904                 return main_backup(argc, argv);
905         else if (strstr(argv[0], "cgi-exec"))
906                 return main_exec(argc, argv);
907 
908         return -1;
909 }
910 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt