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