]> Raphaël G. Git Repositories - ihttpd/blob - SOURCES/index.bin.c
Default to 503 code
[ihttpd] / SOURCES / index.bin.c
1 /**
2 * This program is free software: you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation, either version 3 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 *
15 * Written by Raphaël Gertz <rapsys@rapsys.eu>
16 */
17
18 //Required for mkostemp, O_CLOEXEC, strdup, memmem
19 #define _GNU_SOURCE
20
21 //Required for SSIZE_MAX, LONG_MAX
22 #include <limits.h>
23
24 //Required for pid_t, kill, waitpid
25 #include <sys/wait.h>
26 #include <signal.h>
27
28 //Required for printf, sscanf, sprintf
29 #include <stdio.h>
30
31 //Required for struct stat, fstat
32 #include <sys/stat.h>
33
34 //Required for closedir, DIR, opendir, readdir, struct dirent
35 #include <dirent.h>
36
37 //Required for close, dup2, execve, fork, read, STDERR_FILENO, STDOUT_FILENO, STDIN_FILENO, write
38 #include <unistd.h>
39
40 //Required for open, O_CLOEXEC, O_NOATIME, O_NOFOLLOW
41 #include <fcntl.h>
42
43 //Required for atoi, calloc, exit, EXIT_FAILURE, EXIT_SUCCESS, free, getenv, malloc, realloc
44 #include <stdlib.h>
45
46 //Required for memchr, memcpy, memmem, strdup, strlen, strncmp
47 #include <string.h>
48
49 //Required for bool
50 #include <stdbool.h>
51
52 //Required for nanosleep
53 #include <time.h>
54
55 //Default passphrase max size
56 #define DEFAULT_PASSPHRASE_SIZE_MAX 512
57
58 //Default keyfile max size
59 //XXX: file should be maximum 8192*1024-1 character long
60 #define DEFAULT_KEYFILE_SIZE_MAX (8192 * 1024)
61
62 //Default crypttab
63 #define CRYPTTAB "/etc/crypttab"
64
65 //Default cryptsetup
66 #define CRYPTSETUP "/sbin/cryptsetup"
67
68 //Systemd cryptsetup
69 #define SYSTEMDCRYPTSETUP "/usr/lib/systemd/systemd-cryptsetup"
70
71 //Default pid file
72 #define IHTTPDPID "/run/ihttpd/ihttpd.pid"
73
74 //Default systemd ask-password dir
75 #define ASKPASSWORDDIR "/run/systemd/ask-password"
76
77 //Define child log
78 #define ASKPASSWORDLOG "/run/ihttpd/log/child.askpassword.log"
79 #define IHTTPDLOG "/run/ihttpd/log/child.ihttpd.log"
80
81 //Create struct for http error status
82 struct httpStatusStruct {
83 int value;
84 char *description;
85 };
86
87 //Declare http error status array
88 const struct httpStatusStruct httpStatuses[] = {
89 {200, "OK"},
90 {400, "Bad Request"},
91 {405, "Method Not Allowed"},
92 {411, "Length Required"},
93 {500, "Internal Server Error"},
94 {503, "Service Unavailable"}
95 };
96
97 /**
98 * Prototype
99 */
100 void die(const int, const char*);
101 void header(const int, const char*);
102 void showForm(const char*, const int, const int);
103 int extractValue(char**, int*, char*, int);
104 int extractLuksDevice(char**, char**);
105 int extractIHttpdPid(pid_t *);
106 int extractAskPasswordPid(pid_t *);
107
108 /**
109 * Die with error
110 */
111 void die(const int code, const char *err) {
112 //TODO: see if we add a nice text/html template ?
113 //Send content as text
114 header(code, "text/plain");
115 //Print error line if available
116 if (err != NULL)
117 printf("%s", err);
118 //Flush all
119 if (fflush(NULL) == -1) {
120 perror("fflush");
121 }
122 //Exit with failure code
123 exit(EXIT_FAILURE);
124 }
125
126 /**
127 * Send header
128 */
129 void header(const int code, const char *ctype) {
130 int k;
131 switch(code) {
132 case 400:
133 k = 1;
134 break;
135 case 405:
136 k = 2;
137 break;
138 case 411:
139 k = 3;
140 break;
141 case 500:
142 k = 4;
143 break;
144 case 503:
145 k = 5;
146 break;
147 default:
148 k = 0;
149 }
150 //Send http status
151 printf("Status: %d %s\r\n", httpStatuses[k].value, httpStatuses[k].description);
152 //Make sure no cache
153 printf("Cache-Control: no-cache, no-store, must-revalidate\r\n");
154 printf("Pragma: no-cache\r\n");
155 printf("Expires: 0\r\n");
156 printf("X-Robots-Tag: noindex, nofollow, noarchive, nosnippet, noodp\r\n");
157 printf("Content-type: %s\r\n\r\n", ctype);
158 }
159
160 /**
161 * Show form
162 */
163 void showForm(const char *requestUri, const int keyfileSizeMax, const int passphraseSizeMax) {
164 header(503, "text/html");
165 printf("<!DOCTYPE HTML>\r\n");
166 printf("<html>\r\n");
167 printf("<head><title>Key upload form</title></head>\r\n");
168 printf("<body>\r\n");
169 printf("<div id=\"wrapper\">\r\n");
170 printf("<form enctype=\"multipart/form-data\" action=\"%s\" method=\"post\"><fieldset><legend>Upload key</legend><label for=\"file\"></label><input type=\"hidden\" name=\"MAX_FILE_SIZE\" value=\"%d\" /><input id=\"file\" type=\"file\" name=\"key\" /><input type=\"submit\" value=\"Send\" /></fieldset></form>\r\n", requestUri, keyfileSizeMax);
171 printf("<form action=\"%s\" method=\"post\"><fieldset><legend>Type key</legend><label for=\"password\"></label><input id=\"password\" type=\"password\" name=\"key\" maxlength=\"%d\" /><input type=\"submit\" value=\"Send\" /></fieldset></form>\r\n", requestUri, passphraseSizeMax);
172 printf("</div>\r\n");
173 printf("</body>\r\n");
174 printf("</html>\r\n");
175 }
176
177 /**
178 * Extract value
179 */
180 int extractValue(char **value, int *valueLength, char *contentType, int contentLength) {
181 //Handle application/x-www-form-urlencoded request
182 if (contentType != NULL && !strncmp(contentType, "application/x-www-form-urlencoded", 33)) {
183 //Indexes and return
184 int i, k, v, r;
185 //Declare key and buf
186 char *key, *buf;
187 //Allocate key
188 if ((key = malloc(4*sizeof(char))) == NULL) {
189 //Unable to allocate key
190 return -1;
191 }
192 //Allocate buf to maximum possible value size + 1 for trailing \0
193 if ((buf = calloc(1, (DEFAULT_PASSPHRASE_SIZE_MAX+1)*sizeof(char))) == NULL) {
194 //Unable to allocate value
195 free(key);
196 return -2;
197 }
198 //Char buffer
199 char d = '\0';
200 //Char delimiter
201 //XXX: initialised as & for new key, becomes = when fetching value
202 char delim = '&';
203 for (i = 0, k = 0, v = 0; i < contentLength; i++) {
204 //Safeguard against a value greater than DEFAULT_PASSPHRASE_SIZE_MAX
205 //XXX: this should never happen because we should be protected by contentLength already
206 if (v == DEFAULT_PASSPHRASE_SIZE_MAX+1) {
207 //Invalid value
208 free(key);
209 free(buf);
210 return -3;
211 }
212 //Read one character from stdin
213 r = read(STDIN_FILENO, &d, 1);
214 //Handle errors
215 if (r < 0) {
216 //Error while parsing post data
217 free(key);
218 free(buf);
219 return -4;
220 } else if (r == 0) {
221 //Partial receive
222 free(key);
223 free(buf);
224 return -5;
225 }
226 //Handle case where key has an other name
227 if (i == 3) {
228 //Check key is "key" and we get '=' as new char and assign delim to =
229 if (strncmp(key, "key", 3) || (delim = d) != '=') {
230 //Invalid key
231 free(key);
232 free(buf);
233 return -6;
234 }
235 //Handle key or value separator in query string
236 } else if (d == '&' || d == '=') {
237 //Invalid query string
238 free(key);
239 free(buf);
240 return -7;
241 //Handle a value
242 } else {
243 //Save key char
244 if (delim == '&') {
245 key[k] = d;
246 k++;
247 //Save buf char
248 } else {
249 buf[v] = d;
250 v++;
251 }
252 }
253 }
254 //Free key
255 free(key);
256 //Unescape and reduce value if not empty
257 if (v > 0) {
258 //Declare iterators
259 int l,m, s = v;
260 //Loop on value and reduce length on the fly
261 for(l=0,m=0; m < v; l++,m++) {
262 //Replace + with space
263 if (buf[m] == '+') {
264 buf[l] = ' ';
265 //Skip partial present %XX
266 } else if (
267 //Assign char
268 (buf[l] = buf[m]) == '%' &&
269 //Check we didn't reach valueStrLength
270 (m+2 < v) &&
271 //Check next two digits are valid
272 ((buf[m+1] >= 'A' && buf[m+1] <= 'F') || (buf[m+1] >= 'a' && buf[m+1] <= 'f') || (buf[m+1] >= '0' && buf[m+1] <= '9')) &&
273 ((buf[m+2] >= 'A' && buf[m+2] <= 'F') || (buf[m+2] >= 'a' && buf[m+2] <= 'f') || (buf[m+2] >= '0' && buf[m+2] <= '9'))
274 ) {
275 buf[l] = (buf[m+1] >= 'A' ? (buf[m+1] & 0xdf) - 'A' + 10 : buf[m+1] - '0') * 16 + (buf[m+2] >= 'A' ? (buf[m+2] & 0xdf) - 'A' + 10 : buf[m+2] - '0');
276 m += 2;
277 s -= 2;
278 }
279 }
280 //Set value length
281 *valueLength = s;
282 //Alloc value
283 if ((*value = malloc((*valueLength)*sizeof(char))) == NULL) {
284 //Unable to alloc value
285 free(key);
286 free(buf);
287 return -8;
288 }
289 //Copy value
290 memcpy(*value, buf, *valueLength);
291 }
292 //Free buf
293 free(buf);
294 //Handle multipart/form-data request
295 } else if (contentType != NULL && !strncmp(contentType, "multipart/form-data", 19)) {
296 //Indexes and return
297 int b, s, r;
298 //Boundary length
299 int boundaryLength = strlen(contentType) - 30 + 1;
300 //Client send LF ending without CR
301 int lfstyle = 0;
302 //Declare boundary
303 char *boundary;
304 //Allocate boundary
305 if ((boundary = calloc(1, boundaryLength*sizeof(char))) == NULL) {
306 //Unable to allocate boundary
307 return -9;
308 }
309 //Extract boundary
310 memcpy(boundary, contentType + 30, boundaryLength - 1);
311
312 //Declare buffers
313 char *start, *buf;
314 //Allocate buffer
315 if ((buf = malloc(contentLength*sizeof(char))) == NULL) {
316 //Unable to allocate buffer
317 free(boundary);
318 return -10;
319 }
320 //Char buffer
321 char d = '\0';
322
323 //Loop
324 for (b = 0, s = 0; b < contentLength; b++) {
325 //Detect possible boundary
326 //XXX: b is boundaryLength-1 + 2 after s
327 //XXX: d will be a CR(CRLF line ending) or LF(LF line ending) or -(of final --)
328 if (b == s + 2 + boundaryLength - 1 && !strncmp(boundary, buf + b - boundaryLength + 1, boundaryLength - 1)) {
329 //Only after first boundary match
330 if (s > 0) {
331 //Trim line jump
332 if (start[0] == '\r' && start[1] == '\n') {
333 start += 2;
334 lfstyle = 1;
335 } else if (start[0] == '\n') {
336 start++;
337 }
338 //Found flag
339 bool found = false;
340 //Allocate pointers
341 char *new = start, *prev = start, *name = NULL;
342 //Search for a new line
343 while ((new = memchr(new, '\n', b - (new - buf)))) {
344 //Jump to first character after new line
345 new++;
346 //Init line length
347 int lineLength = new - prev - 1;
348 //Remove chariage return if necessary
349 if (prev[lineLength] == '\r') {
350 lineLength--;
351 lfstyle = 1;
352 }
353 //Break loop after headers end
354 if (lineLength == 1) {
355 break;
356 }
357 //Allocate a name buf of maximum line length
358 //XXX: need to be filled with zero as we don't know what will comes with sscanf call and we rely on strlen
359 if ((name = calloc(1, lineLength*sizeof(char))) == NULL) {
360 //Unable to allocate name
361 free(boundary);
362 free(buf);
363 return -11;
364 }
365 //Search for name
366 if (sscanf(prev, "Content-Disposition: form-data; name=\"%[^\"]\"", name)) {
367 //Realloc name
368 if ((name = realloc(name, (strlen(name)+1)*sizeof(char))) == NULL) {
369 //Unable to reduce name
370 free(boundary);
371 free(buf);
372 free(name);
373 return -12;
374 }
375 if (!strncmp(name, "key", 3)) {
376 found = true;
377 }
378 }
379 //Free name
380 free(name);
381 //Jump to next one
382 prev = new;
383 }
384 //Init value if found
385 if (found) {
386 //Declare end
387 char *end = buf + b - boundaryLength - 3;
388 //Remove CR at end if provided
389 //XXX: only remove CR at end if we encountered one before
390 if (lfstyle) {
391 end++;
392 }
393 //On too big keyfile
394 if (end - new - 1 >= DEFAULT_KEYFILE_SIZE_MAX) {
395 //Keyfile too large
396 free(boundary);
397 free(buf);
398 return -13;
399 }
400 //On non empty value
401 if (end - new > 1) {
402 //Set value length
403 *valueLength = end - new - 1;
404 //Allocate value
405 if ((*value = malloc((*valueLength)*sizeof(char))) == NULL) {
406 //Unable to allocate value
407 free(boundary);
408 free(buf);
409 return -14;
410 }
411 //Copy value
412 memcpy(*value, new, *valueLength);
413 }
414 }
415 }
416 //Set start to matched boundary
417 start = buf + b;
418 }
419 //Read one character from stdin
420 r = read(STDIN_FILENO, &d, 1);
421 //Handle errors
422 if (r < 0) {
423 //Error while parsing post data
424 free(boundary);
425 free(buf);
426 return -15;
427 } else if (r == 0) {
428 //Partial receive
429 free(boundary);
430 free(buf);
431 return -16;
432 }
433 //New line case
434 if (d == '\n') {
435 //Store new possible boundary start
436 s = b + 1;
437 }
438 buf[b] = d;
439 }
440
441 //Free buffers
442 free(boundary);
443 free(buf);
444 //Unhandled request
445 } else {
446 return -17;
447 }
448
449 //Send value
450 return 0;
451 }
452
453 /**
454 * Extract luks and device
455 */
456 int extractLuksDevice(char **luks, char **device) {
457 //Declare file descriptor
458 int fd, bufLength;
459 //Declare buf, device and luks pointer
460 char *buf, *d, *l;
461 //Declare stat struct
462 struct stat *stats;
463 //Open file
464 if ((fd = open(CRYPTTAB, O_RDONLY|O_NOATIME|O_NOFOLLOW|O_CLOEXEC)) == -1) {
465 //Can't open crypttab file
466 return -1;
467 }
468 //Allocate stats
469 if ((stats = calloc(1, sizeof(struct stat))) == NULL) {
470 //Unable to allocate stats
471 return -2;
472 }
473 //Stat file
474 if (fstat(fd, stats) == -1) {
475 //Can't stat crypttab
476 return -3;
477 }
478 //Check file size
479 if ((bufLength = stats->st_size) > SSIZE_MAX) {
480 //Crypttab too big
481 return -4;
482 }
483 //Allocate buf
484 if ((buf = malloc(bufLength*sizeof(char))) == NULL) {
485 //Unable to allocate buf
486 return -5;
487 }
488 //Read file
489 if ((read(fd, buf, bufLength)) < bufLength) {
490 //Fail to read crypttab file
491 return -6;
492 }
493 //Close file
494 close(fd);
495 //Free stats buffer
496 free(stats);
497
498 //Search first separator (\s|\t) after luks
499 if ((l = memchr(buf, ' ', bufLength)) == NULL && (l = memchr(buf, ' ', bufLength)) == NULL) {
500 return -7;
501 }
502 //Jump to next char
503 l++;
504 //Search first separator (\s|\t) after device
505 if ((d = memchr(l, ' ', bufLength - (l - buf))) == NULL && (d = memchr(l, ' ', bufLength - (l - buf))) == NULL && (d = memchr(l, '\n', bufLength - (l - buf))) == NULL) {
506 return -8;
507 }
508 //Jump to next char
509 d++;
510
511 //Alloc luks
512 if ((*luks = malloc((l - buf - 1)*sizeof(char))) == NULL) {
513 return -9;
514 }
515 //Allocate device
516 if ((*device = malloc((d - l - 1)*sizeof(char))) == NULL) {
517 //Free and reset luks
518 free(luks);
519 luks=NULL;
520 return -10;
521 }
522
523 //Copy luks
524 memcpy(*luks, buf, l - buf - 1);
525 //Terminate luks
526 (*luks)[l - buf - 1] = '\0';
527
528 //Copy device
529 memcpy(*device, l, d - l - 1);
530 //Terminate device
531 (*device)[d - l - 1] = '\0';
532
533 //Free buffer
534 free(buf);
535
536 //Success
537 return 0;
538 }
539
540 /**
541 * Extract ask-password pid
542 */
543 int extractAskPasswordPid(pid_t *pid) {
544 //Declare stuct dirent
545 struct dirent *entry;
546 //Declare askdir
547 DIR *askdir;
548 //Declare found
549 int found = 0;
550
551 //Allocate dirent struct
552 if ((entry = calloc(1, sizeof(struct dirent))) == NULL) {
553 //Unable to allocate entry
554 return -1;
555 }
556
557 //Open systemd ask-password dir
558 if ((askdir = opendir(ASKPASSWORDDIR)) == NULL) {
559 //Can't open ask dir
560 return -2;
561 }
562
563 //Change dir
564 if (chdir(ASKPASSWORDDIR) == -1) {
565 //Can't change to ask dir
566 return -3;
567 }
568
569 //Loop on dir content
570 while((entry = readdir(askdir))) {
571 //Handle each ask.XXXXXX file
572 if (!strncmp(entry->d_name, "ask.", 4) && strlen(entry->d_name) == 10) {
573 //Declare file descriptor
574 int fd, bufLength;
575 //Declare buf
576 char *buf;
577 //Declare stat struct
578 struct stat *stats;
579 //Open file
580 if ((fd = open(entry->d_name, O_RDONLY|O_NOATIME|O_NOFOLLOW|O_CLOEXEC)) == -1) {
581 //Can't open ask file
582 return -4;
583 }
584 //Allocate stats
585 if ((stats = calloc(1, sizeof(struct stat))) == NULL) {
586 //Unable to allocate stats
587 return -5;
588 }
589 //Stat file
590 if (fstat(fd, stats) == -1) {
591 //Can't stat ask file
592 return -6;
593 }
594 //Check file size
595 if ((bufLength = stats->st_size) > SSIZE_MAX) {
596 //Ask file too big
597 return -7;
598 }
599 //Allocate buf
600 if ((buf = malloc(bufLength*sizeof(char))) == NULL) {
601 //Unable to allocate buf
602 return -8;
603 }
604 //Read file
605 if ((read(fd, buf, bufLength)) < bufLength) {
606 //Fail to read ask file
607 return -9;
608 }
609 //Close file
610 close(fd);
611 //Free stats buffer
612 free(stats);
613
614 //Allocate pointers
615 char *nl = buf, *pl = buf, *e;
616 //Allocate pid
617 *pid = 0;
618
619 //Search for a new line
620 while ((nl = memmem(nl, bufLength - (nl - buf), "\n", strlen("\n")))) {
621 //Jump to next char
622 nl++;
623
624 //Check if we have a = in line but not empty value ("=\n")
625 if ((e = memmem(pl, bufLength - (pl - buf), "=", strlen("="))) && e < nl - 2) {
626 //Jump to next char
627 e++;
628 //Look for PID
629 if (!strncmp(pl, "PID", 3)) {
630 //Declade pid string
631 char *pidStr;
632 //Allocate pid string
633 if ((pidStr = malloc((nl - e)*sizeof(char))) == NULL) {
634 //Unable to allocate pid string
635 return -10;
636 }
637 //Copy pid
638 memcpy(pidStr, e, nl - e - 1);
639 //Terminate pid string
640 pidStr[nl - e] = '\0';
641 //Check pid value
642 if ((*pid = atoi(pidStr)) <= 1) {
643 //Invalid pid
644 return -11;
645 }
646 //Free pid string
647 free(pidStr);
648 //Found a valid process
649 found++;
650 }
651 }
652
653 //Jump prev line to new line
654 pl = nl;
655 }
656
657 //Free buffers
658 free(buf);
659 }
660 }
661
662 //Close systemd ask-password dir
663 if (closedir(askdir) == -1) {
664 //Can't close ask dir
665 return -13;
666 }
667
668 //Free entry
669 free(entry);
670
671 //Found no valid pid
672 if (found == 0) {
673 //No pid found
674 return -14;
675 //Found more than one pid
676 } else if (found > 1) {
677 //No pid found
678 return -15;
679 }
680
681 //Success
682 return 0;
683 }
684
685 /**
686 * Extract ihttpd pid
687 */
688 int extractIHttpdPid(pid_t *pid) {
689 //Declare file descriptor
690 int fd, bufLength;
691 //Declare buf, device and luks pointer
692 char *buf, *l, *pidStr;
693 //Declare stat struct
694 struct stat *stats;
695 //Open file
696 if ((fd = open(IHTTPDPID, O_RDONLY|O_NOATIME|O_NOFOLLOW|O_CLOEXEC)) == -1) {
697 //Can't open crypttab file
698 return -1;
699 }
700 //Allocate stats
701 if ((stats = calloc(1, sizeof(struct stat))) == NULL) {
702 //Unable to allocate stats
703 return -2;
704 }
705 //Stat file
706 if (fstat(fd, stats) == -1) {
707 //Can't stat crypttab
708 return -3;
709 }
710 //Check file size
711 if ((bufLength = stats->st_size) > SSIZE_MAX) {
712 //Crypttab too big
713 return -4;
714 }
715 //Allocate buf
716 if ((buf = malloc(bufLength*sizeof(char))) == NULL) {
717 //Unable to allocate buf
718 return -5;
719 }
720 //Read file
721 if ((read(fd, buf, bufLength)) < bufLength) {
722 //Fail to read crypttab file
723 return -6;
724 }
725 //Close file
726 close(fd);
727 //Free stats buffer
728 free(stats);
729
730 //Search first separator (\s|\t|\n) after pid
731 if ((l = memchr(buf, ' ', bufLength)) == NULL && (l = memchr(buf, ' ', bufLength)) == NULL && (l = memchr(buf, '\n', bufLength)) == NULL) {
732 return -7;
733 }
734 //Jump to next char
735 l++;
736
737 //Alloc pid string
738 if ((pidStr = malloc((l - buf - 1)*sizeof(char))) == NULL) {
739 return -9;
740 }
741
742 //Copy luks
743 memcpy(pidStr, buf, l - buf - 1);
744 //Terminate luks
745 pidStr[l - buf - 1] = '\0';
746
747 //Free buffer
748 free(buf);
749
750 //Store pid
751 if ((*pid = atoi(pidStr)) <= 1) {
752 //Invalid pid
753 return -10;
754 }
755
756 //Free pid string
757 free(pidStr);
758
759 //Success
760 return 0;
761 }
762
763 /**
764 * Main function
765 */
766 int main(int argc, char **argv) {
767 //Get request method
768 char *requestMethod = getenv("REQUEST_METHOD");
769
770 //Handle unknown requests
771 if (requestMethod == NULL || (strncmp(requestMethod, "GET", 3) && strncmp(requestMethod, "HEAD", 4) && strncmp(requestMethod, "POST", 4))) {
772 //Send method not allowed
773 die(405, "Unsupported request method");
774 //Handle get and head
775 } else if (!strncmp(requestMethod, "GET", 3) || !strncmp(requestMethod, "HEAD", 4)) {
776 //Send form
777 showForm(getenv("REQUEST_URI")?getenv("REQUEST_URI"):"/", DEFAULT_KEYFILE_SIZE_MAX, DEFAULT_PASSPHRASE_SIZE_MAX);
778 //Handle post
779 } else /*if (!strncmp(requestMethod, "POST", 4))*/ {
780 //Return value
781 int ret;
782
783 //Child pid
784 pid_t pid;
785
786 //Value length
787 //XXX: will contain number of char in value without trailing \0
788 int valueLength;
789 //Value string
790 //XXX: will contain value without a tailing \0
791 char *value = NULL;
792
793 //Content length
794 int contentLength;
795 //Content length string from env
796 char *contentLengthStr = getenv("CONTENT_LENGTH");
797 //Content type
798 char *contentType = getenv("CONTENT_TYPE");
799
800 //Declare luks and device
801 char *luks = NULL, *device = NULL;
802
803 //Declare cargv
804 char **cargv = NULL;
805
806 //Pairs of pipe for stdin, stdout and stderr
807 int inPipe[2], errPipe[2];
808
809 //Handle unknown content type
810 if (contentType == NULL || (strncmp(contentType, "application/x-www-form-urlencoded", 33) && strncmp(contentType, "multipart/form-data", 19))) {
811 die(400, "Unknown content type");
812 }
813
814 //Handle invalid multipart/form-data content type
815 //XXX: max boundary length is 70 as per rfc1521 & rfc2046
816 if (!strncmp(contentType, "multipart/form-data", 19) && (strncmp(contentType, "multipart/form-data; boundary=", 30) || strlen(contentType) <= 30 || strlen(contentType) > 100)) {
817 die(400, "Malformed boundary in multipart/form-data request");
818 }
819
820 //Handle invalid content length
821 //XXX: deny empty contentLength as chrome send a contentLength even for a device
822 if (contentLengthStr == NULL || (contentLength = atoi(contentLengthStr)) <= 0) {
823 die(411, "Invalid content length");
824 }
825
826 //Handle application/x-www-form-urlencoded request length
827 //XXX: limit to key=xyz where xyz can be all encoded in %XX
828 if (!strncmp(contentType, "application/x-www-form-urlencoded", 33) && contentLength > (DEFAULT_PASSPHRASE_SIZE_MAX * 3 + 4)) {
829 die(400, "Invalid application/x-www-form-urlencoded request length");
830 }
831
832 //Handle multipart/form-data request length
833 //XXX: limit to arbitrary 3 times the keyfile max size
834 if (!strncmp(contentType, "multipart/form-data", 19) && contentLength > (DEFAULT_KEYFILE_SIZE_MAX * 3)) {
835 die(400, "Invalid multipart/form-data request length");
836 }
837
838 //Extract value
839 if ((ret = extractValue(&value, &valueLength, contentType, contentLength)) < 0) {
840 die(500, "Failed to extract value");
841 }
842
843 //Extract luks and device
844 if ((ret = extractLuksDevice(&luks, &device)) < 0) {
845 die(500, "Failed to extract luks and device");
846 }
847
848 //Declare cargv array
849 char *cargvs[] = { CRYPTSETUP, "-d", "-", "luksOpen", device, luks, NULL };
850 //TODO: device cannot be an UUID=xyz, a resolved block device is required for it
851 char *scargvs[] = { SYSTEMDCRYPTSETUP, "attach", luks, device, "-", NULL };
852
853 //Check cryptsetup binary
854 if (access(CRYPTSETUP, F_OK|X_OK) == -1) {
855 //Check systemdcryptsetup binary
856 if (access(SYSTEMDCRYPTSETUP, F_OK|X_OK) == -1) {
857 die(500, "No cryptsetup available");
858 } else {
859 //Set contextual env
860 //TODO: resolve UUID in real device name
861 //TODO: passing password through the socket is not possible, as it rely on password ending with \0
862 die(500, "systemd-cryptsetupd is not implementable");
863 }
864 } else {
865 //Set contextual env
866 cargv = cargvs;
867 }
868
869 //Create stdin pipe
870 if (pipe(inPipe) == -1) {
871 die(500, "Failed to create in pipe");
872 }
873
874 //Create stderr pipe
875 if (pipe(errPipe) == -1) {
876 die(500, "Failed to create err pipe");
877 }
878
879 //Fork process
880 if ((pid = fork()) == -1) {
881 die(500, "Failed to fork");
882 }
883
884 //Child process
885 if (pid == 0) {
886 //Child arge
887 char *carge[] = { NULL };
888 //Free value
889 free(value);
890 //Redirect stdin to pipe
891 if (dup2(inPipe[0], STDIN_FILENO) == -1) {
892 die(500, "Failed to redirect in pipe");
893 }
894 //Close inPipe
895 close(inPipe[0]);
896 close(inPipe[1]);
897 //Redirect stderr to pipe
898 if (dup2(errPipe[1], STDERR_FILENO) == -1) {
899 die(500, "Failed to redirect err pipe");
900 }
901 //Close errPipe
902 close(errPipe[0]);
903 close(errPipe[1]);
904
905 //Call cryptsetup
906 if (execve(cargv[0], cargv, carge) == -1) {
907 die(500, "Failed to call cryptsetup");
908 }
909 //Parent process
910 } else {
911 //Free luks
912 free(luks);
913 //Free device
914 free(device);
915
916 //Close unused inPipe end
917 close(inPipe[0]);
918 //Close unused errPipe end
919 close(errPipe[1]);
920
921 //Send password on stdin anyway
922 //XXX: this fail if device is already unlocked for example
923 write(inPipe[1], value, valueLength);
924
925 //Free value
926 free(value);
927
928 //Close stdin with EOF
929 close(inPipe[1]);
930
931 //Wait child
932 if (waitpid(pid, &ret, 0) == -1) {
933 die(500, "Failed to wait child");
934 }
935
936 //Handle already in use device
937 if (ret == 5) {
938 die(500, "Device already in use");
939 //Handle already unlocked device
940 //} else if (ret == 1280) {
941 // die(200, "Device already unlocked");
942 //Handle invalid luks device
943 //} else if (ret == 256) {
944 // die(500, "Device is now a valid device");
945 //Handle no key available with this passphrase
946 } else if (ret == 512) {
947 die(500, "No slot for this value");
948 //Handle unexisting device or permission denied
949 } else if (ret == 1014) {
950 die(500, "Device doesn't exist or access denied");
951 //Unknown error
952 } else if (ret != 0) {
953 //Err length and counter
954 int errLength = 2048, e = 0;
955 //Declare err buffer
956 char *err;
957 //Buffer char
958 char c;
959 //Alloc err buffer
960 if ((err = malloc(errLength*sizeof(char))) == NULL) {
961 die(500, "Couldn't alloc err buffer");
962 }
963 //Advance after ret code
964 e = sprintf(err, "%d:", ret);
965 //Fetch stderr and store in err buffer
966 while(read(errPipe[0], &c, 1) > 0) {
967 //Grow buffer if we reach end
968 if (e == errLength) {
969 if ((err = realloc(err, (errLength+2048)*sizeof(char))) == NULL) {
970 die(500, "Couldn't grow err buffer");
971 }
972 errLength += 2048;
973 }
974 //Store character
975 err[e] = c;
976 //Pass to next
977 e++;
978 }
979 //Terminate err buffer
980 err[e] = '\0';
981 //Realloc err buffer
982 if ((err = realloc(err, (e+1)*sizeof(char))) == NULL) {
983 die(500, "Couldn't ungrow err buffer");
984 }
985 //Die with luks error
986 die(500, err);
987 }
988 //Close errPipe
989 close(errPipe[0]);
990 }
991
992 //Removed as it was making fail the process of booting sometimes
993 #if 0
994 //Fork process
995 if ((pid = fork()) == -1) {
996 die(500, "Failed to fork");
997 }
998
999 //IHttpd killing child process
1000 if (pid == 0) {
1001 //File descriptor
1002 int fd;
1003
1004 //Declare ihttpd pid
1005 pid_t ihttpdPid;
1006
1007 //Close stdin
1008 close(STDIN_FILENO);
1009
1010 //Disable line buffering on stdout and stderr
1011 setvbuf(stdout, NULL, _IONBF, 0);
1012 setvbuf(stderr, NULL, _IONBF, 0);
1013
1014 //Redirect output to log
1015 if ((fd = open(IHTTPDLOG, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR)) == -1) {
1016 fprintf(stderr, "Open ihttpd child log failed\n");
1017 } else {
1018 //Close stdout
1019 close(STDOUT_FILENO);
1020 //Redirect stdout on childlog
1021 if (dup2(fd, STDOUT_FILENO) == -1) {
1022 fprintf(stderr, "Redirect stdout to ihttpd child log failed\n");
1023 }
1024 //Close stderr
1025 close(STDERR_FILENO);
1026 //Redirect stderr on childlog
1027 if (dup2(fd, STDERR_FILENO) == -1) {
1028 fprintf(stderr, "Redirect stderr to ihttpd child log failed\n");
1029 }
1030 //Close childlog fd
1031 close(fd);
1032 }
1033
1034 //Extract ihttpd pid
1035 if (extractIHttpdPid(&ihttpdPid) < 0) {
1036 fprintf(stderr, "Failed to extract ihttpd pid");
1037 exit(EXIT_FAILURE);
1038 }
1039
1040 //Close stdout and stderr without childlog
1041 if (fd == -1) {
1042 close(STDOUT_FILENO);
1043 close(STDERR_FILENO);
1044 }
1045
1046 //Wait until get rattached to init(getppid()==1)
1047 //XXX: we are really blind here
1048 while(getppid() != 1) {
1049 //Sleep half a second
1050 if (usleep(500000) == -1 && fd != -1) {
1051 printf("Usleep failed\n");
1052 }
1053 }
1054
1055 //Termiate ihttpd
1056 if (kill(ihttpdPid, 0) == 0 && kill(ihttpdPid, SIGTERM) == -1 && fd != -1) {
1057 printf("Termiate ihttpd failed\n");
1058 }
1059
1060 //Sleep half a second
1061 if (usleep(500000) == -1 && fd != -1) {
1062 printf("Usleep failed\n");
1063 }
1064
1065 //Kill ihttpd
1066 if (kill(ihttpdPid, 0) == 0 && kill(ihttpdPid, SIGKILL) == -1) {
1067 printf("Kill ihttpd failed\n");
1068 }
1069
1070 //Parent process
1071 } else {
1072 #endif
1073
1074 //Sleep before killing askpassword process
1075 if (usleep(500000) == -1) {
1076 die(500, "Usleep failed");
1077 }
1078
1079 //Fork process
1080 if ((pid = fork()) == -1) {
1081 die(500, "Failed to fork");
1082 }
1083
1084 //Ask password killing child process
1085 //XXX: we are blind here
1086 if (pid == 0) {
1087 //File descriptor
1088 int fd;
1089
1090 //Declare ask password pid
1091 pid_t askPasswordPid;
1092
1093 //Close stdin
1094 close(STDIN_FILENO);
1095
1096 //Disable line buffering on stdout and stderr
1097 setvbuf(stdout, NULL, _IONBF, 0);
1098 setvbuf(stderr, NULL, _IONBF, 0);
1099
1100 //Redirect output to log
1101 if ((fd = open(ASKPASSWORDLOG, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR)) == -1) {
1102 fprintf(stderr, "Open ask password child log failed\n");
1103 } else {
1104 //Close stdout
1105 close(STDOUT_FILENO);
1106 //Redirect stdout on childlog
1107 if (dup2(fd, STDOUT_FILENO) == -1) {
1108 fprintf(stderr, "Redirect stdout to ask password child log failed\n");
1109 }
1110 //Close stderr
1111 close(STDERR_FILENO);
1112 //Redirect stderr on childlog
1113 if (dup2(fd, STDERR_FILENO) == -1) {
1114 fprintf(stderr, "Redirect stderr to ask password child log failed\n");
1115 }
1116 //Close childlog fd
1117 close(fd);
1118 }
1119
1120 //Extract ask password pid
1121 if (extractAskPasswordPid(&askPasswordPid) < 0) {
1122 fprintf(stderr, "Failed to extract ask password pid");
1123 exit(EXIT_FAILURE);
1124 }
1125
1126 //Close stdout and stderr without childlog
1127 if (fd == -1) {
1128 close(STDOUT_FILENO);
1129 close(STDERR_FILENO);
1130 }
1131
1132 //Wait until get rattached to init(getppid()==1)
1133 //XXX: we are really blind here
1134 while(getppid() != 1) {
1135 //Sleep half a second
1136 if (usleep(500000) == -1 && fd != -1) {
1137 printf("Usleep failed\n");
1138 }
1139 }
1140
1141 //Termitate ask password
1142 if (kill(askPasswordPid, 0) == 0 && kill(askPasswordPid, SIGTERM) == -1 && fd != -1) {
1143 printf("Termiate ask password failed\n");
1144 }
1145
1146
1147 //Sleep half a second
1148 if (usleep(500000) == -1 && fd != -1) {
1149 printf("Usleep failed\n");
1150 }
1151
1152 //Kill ask password
1153 if (kill(askPasswordPid, 0) == 0 && kill(askPasswordPid, SIGKILL) == -1) {
1154 printf("Kill ask password failed\n");
1155 }
1156
1157 //Parent process
1158 } else {
1159
1160 //Process success
1161 header(200, "text/plain");
1162 printf("Sent value, boot should resume now");
1163 fflush(NULL);
1164
1165 }
1166
1167 #if 0
1168 }
1169 #endif
1170
1171 }
1172
1173 exit(EXIT_SUCCESS);
1174 }