+/**
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Written by Raphaël Gertz <rapsys@rapsys.eu>
+ */
+
+//Required for mkostemp, O_CLOEXEC, strdup, memmem
+#define _GNU_SOURCE
+
+//Required for SSIZE_MAX, LONG_MAX
+#include <limits.h>
+
+//Required for pid_t, kill, waitpid
+#include <sys/wait.h>
+#include <signal.h>
+
+//Required for printf, sscanf, sprintf
+#include <stdio.h>
+
+//Required for struct stat, fstat
+#include <sys/stat.h>
+
+//Required for closedir, DIR, opendir, readdir, struct dirent
+#include <dirent.h>
+
+//Required for close, dup2, execve, fork, read, STDERR_FILENO, STDOUT_FILENO, STDIN_FILENO, write
+#include <unistd.h>
+
+//Required for open, O_CLOEXEC, O_NOATIME, O_NOFOLLOW
+#include <fcntl.h>
+
+//Required for atoi, calloc, exit, EXIT_FAILURE, EXIT_SUCCESS, free, getenv, malloc, realloc
+#include <stdlib.h>
+
+//Required for memchr, memcpy, memmem, strdup, strlen, strncmp
+#include <string.h>
+
+//Required for bool
+#include <stdbool.h>
+
+//Required for nanosleep
+#include <time.h>
+
+//Default passphrase max size
+#define DEFAULT_PASSPHRASE_SIZE_MAX 512
+
+//Default keyfile max size
+//XXX: file should be maximum 8192*1024-1 character long
+#define DEFAULT_KEYFILE_SIZE_MAX (8192 * 1024)
+
+//Default crypttab
+#define CRYPTTAB "/etc/crypttab"
+
+//Default cryptsetup
+#define CRYPTSETUP "/sbin/cryptsetup"
+
+//Default pid file
+#define IHTTPDPID "/run/ihttpd/ihttpd.pid"
+
+//Default systemd ask-password dir
+#define ASKPASSWORDDIR "/run/systemd/ask-password"
+
+//Define child log
+#define ASKPASSWORDLOG "/run/ihttpd/log/child.askpassword.log"
+#define IHTTPDLOG "/run/ihttpd/log/child.ihttpd.log"
+
+//Create struct for http error status
+struct httpStatusStruct {
+ int value;
+ char *description;
+};
+
+//Declare http error status array
+const struct httpStatusStruct httpStatuses[] = {
+ {200, "OK"},
+ {400, "Bad Request"},
+ {405, "Method Not Allowed"},
+ {411, "Length Required"},
+ {500, "Internal Server Error"}
+};
+
+/**
+ * Prototype
+ */
+void die(const int, const char*);
+void header(const int, const char*);
+void showForm(const char*, const int, const int);
+int extractValue(char**, int*, char*, int);
+int extractLuksDevice(char**, char**);
+int extractIHttpdPid(pid_t *);
+int extractAskPasswordPid(pid_t *);
+
+/**
+ * Die with error
+ */
+void die(const int code, const char *err) {
+ //TODO: see if we add a nice text/html template ?
+ //Send content as text
+ header(code, "text/plain");
+ //Print error line if available
+ if (err != NULL)
+ printf("%s", err);
+ //Flush all
+ if (fflush(NULL) == -1) {
+ perror("fflush");
+ }
+ //Exit with failure code
+ exit(EXIT_FAILURE);
+}
+
+/**
+ * Send header
+ */
+void header(const int code, const char *ctype) {
+ int k;
+ switch(code) {
+ case 400:
+ k = 1;
+ break;
+ case 405:
+ k = 2;
+ break;
+ case 411:
+ k = 3;
+ break;
+ case 500:
+ k = 4;
+ break;
+ default:
+ k = 0;
+ }
+ //Send http status
+ printf("Status: %d %s\r\n", httpStatuses[k].value, httpStatuses[k].description);
+ //Make sure no cache
+ printf("Cache-Control: no-cache, no-store, must-revalidate\r\n");
+ printf("Pragma: no-cache\r\n");
+ printf("Expires: 0\r\n");
+ printf("X-Robots-Tag: noindex, nofollow, noarchive, nosnippet, noodp\r\n");
+ printf("Content-type: %s\r\n\r\n", ctype);
+}
+
+/**
+ * Show form
+ */
+void showForm(const char *requestUri, const int keyfileSizeMax, const int passphraseSizeMax) {
+ header(200, "text/html");
+ printf("<!DOCTYPE HTML>\r\n");
+ printf("<html>\r\n");
+ printf("<head><title>Key upload form</title></head>\r\n");
+ printf("<body>\r\n");
+ printf("<div id=\"wrapper\">\r\n");
+ 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);
+ 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);
+ printf("</div>\r\n");
+ printf("</body>\r\n");
+ printf("</html>\r\n");
+}
+
+/**
+ * Extract value
+ */
+int extractValue(char **value, int *valueLength, char *contentType, int contentLength) {
+ //Handle application/x-www-form-urlencoded request
+ if (contentType != NULL && !strncmp(contentType, "application/x-www-form-urlencoded", 33)) {
+ //Indexes and return
+ int i, k, v, r;
+ //Declare key and buf
+ char *key, *buf;
+ //Allocate key
+ if ((key = malloc(4*sizeof(char))) == NULL) {
+ //Unable to allocate key
+ return -1;
+ }
+ //Allocate buf to maximum possible value size + 1 for trailing \0
+ if ((buf = calloc(1, (DEFAULT_PASSPHRASE_SIZE_MAX+1)*sizeof(char))) == NULL) {
+ //Unable to allocate value
+ free(key);
+ return -2;
+ }
+ //Char buffer
+ char d = '\0';
+ //Char delimiter
+ //XXX: initialised as & for new key, becomes = when fetching value
+ char delim = '&';
+ for (i = 0, k = 0, v = 0; i < contentLength; i++) {
+ //Safeguard against a value greater than DEFAULT_PASSPHRASE_SIZE_MAX
+ //XXX: this should never happen because we should be protected by contentLength already
+ if (v == DEFAULT_PASSPHRASE_SIZE_MAX+1) {
+ //Invalid value
+ free(key);
+ free(buf);
+ return -3;
+ }
+ //Read one character from stdin
+ r = read(STDIN_FILENO, &d, 1);
+ //Handle errors
+ if (r < 0) {
+ //Error while parsing post data
+ free(key);
+ free(buf);
+ return -4;
+ } else if (r == 0) {
+ //Partial receive
+ free(key);
+ free(buf);
+ return -5;
+ }
+ //Handle case where key has an other name
+ if (i == 3) {
+ //Check key is "key" and we get '=' as new char and assign delim to =
+ if (strncmp(key, "key", 3) || (delim = d) != '=') {
+ //Invalid key
+ free(key);
+ free(buf);
+ return -6;
+ }
+ //Handle key or value separator in query string
+ } else if (d == '&' || d == '=') {
+ //Invalid query string
+ free(key);
+ free(buf);
+ return -7;
+ //Handle a value
+ } else {
+ //Save key char
+ if (delim == '&') {
+ key[k] = d;
+ k++;
+ //Save buf char
+ } else {
+ buf[v] = d;
+ v++;
+ }
+ }
+ }
+ //Free key
+ free(key);
+ //Unescape and reduce value if not empty
+ if (v > 0) {
+ //Declare iterators
+ int l,m, s = v;
+ //Loop on value and reduce length on the fly
+ for(l=0,m=0; m < v; l++,m++) {
+ //Replace + with space
+ if (buf[m] == '+') {
+ buf[l] = ' ';
+ //Skip partial present %XX
+ } else if (
+ //Assign char
+ (buf[l] = buf[m]) == '%' &&
+ //Check we didn't reach valueStrLength
+ (m+2 < v) &&
+ //Check next two digits are valid
+ ((buf[m+1] >= 'A' && buf[m+1] <= 'F') || (buf[m+1] >= 'a' && buf[m+1] <= 'f') || (buf[m+1] >= '0' && buf[m+1] <= '9')) &&
+ ((buf[m+2] >= 'A' && buf[m+2] <= 'F') || (buf[m+2] >= 'a' && buf[m+2] <= 'f') || (buf[m+2] >= '0' && buf[m+2] <= '9'))
+ ) {
+ 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');
+ m += 2;
+ s -= 2;
+ }
+ }
+ //Set value length
+ *valueLength = s;
+ //Alloc value
+ if ((*value = malloc((*valueLength)*sizeof(char))) == NULL) {
+ //Unable to alloc value
+ free(key);
+ free(buf);
+ return -8;
+ }
+ //Copy value
+ memcpy(*value, buf, *valueLength);
+ }
+ //Free buf
+ free(buf);
+ //Handle multipart/form-data request
+ } else if (contentType != NULL && !strncmp(contentType, "multipart/form-data", 19)) {
+ //Indexes and return
+ int b, s, r;
+ //Boundary length
+ int boundaryLength = strlen(contentType) - 30 + 1;
+ //Client send LF ending without CR
+ int lfstyle = 0;
+ //Declare boundary
+ char *boundary;
+ //Allocate boundary
+ if ((boundary = calloc(1, boundaryLength*sizeof(char))) == NULL) {
+ //Unable to allocate boundary
+ return -9;
+ }
+ //Extract boundary
+ memcpy(boundary, contentType + 30, boundaryLength - 1);
+
+ //Declare buffers
+ char *start, *buf;
+ //Allocate buffer
+ if ((buf = malloc(contentLength*sizeof(char))) == NULL) {
+ //Unable to allocate buffer
+ free(boundary);
+ return -10;
+ }
+ //Char buffer
+ char d = '\0';
+
+ //Loop
+ for (b = 0, s = 0; b < contentLength; b++) {
+ //Detect possible boundary
+ //XXX: b is boundaryLength-1 + 2 after s
+ //XXX: d will be a CR(CRLF line ending) or LF(LF line ending) or -(of final --)
+ if (b == s + 2 + boundaryLength - 1 && !strncmp(boundary, buf + b - boundaryLength + 1, boundaryLength - 1)) {
+ //Only after first boundary match
+ if (s > 0) {
+ //Trim line jump
+ if (start[0] == '\r' && start[1] == '\n') {
+ start += 2;
+ lfstyle = 1;
+ } else if (start[0] == '\n') {
+ start++;
+ }
+ //Found flag
+ bool found = false;
+ //Allocate pointers
+ char *new = start, *prev = start, *name = NULL;
+ //Search for a new line
+ while ((new = memchr(new, '\n', b - (new - buf)))) {
+ //Jump to first character after new line
+ new++;
+ //Init line length
+ int lineLength = new - prev - 1;
+ //Remove chariage return if necessary
+ if (prev[lineLength] == '\r') {
+ lineLength--;
+ lfstyle = 1;
+ }
+ //Break loop after headers end
+ if (lineLength == 1) {
+ break;
+ }
+ //Allocate a name buf of maximum line length
+ //XXX: need to be filled with zero as we don't know what will comes with sscanf call and we rely on strlen
+ if ((name = calloc(1, lineLength*sizeof(char))) == NULL) {
+ //Unable to allocate name
+ free(boundary);
+ free(buf);
+ return -11;
+ }
+ //Search for name
+ if (sscanf(prev, "Content-Disposition: form-data; name=\"%[^\"]\"", name)) {
+ //Realloc name
+ if ((name = realloc(name, (strlen(name)+1)*sizeof(char))) == NULL) {
+ //Unable to reduce name
+ free(boundary);
+ free(buf);
+ free(name);
+ return -12;
+ }
+ if (!strncmp(name, "key", 3)) {
+ found = true;
+ }
+ }
+ //Free name
+ free(name);
+ //Jump to next one
+ prev = new;
+ }
+ //Init value if found
+ if (found) {
+ //Declare end
+ char *end = buf + b - boundaryLength - 3;
+ //Remove CR at end if provided
+ //XXX: only remove CR at end if we encountered one before
+ if (lfstyle) {
+ end++;
+ }
+ //On too big keyfile
+ if (end - new - 1 >= DEFAULT_KEYFILE_SIZE_MAX) {
+ //Keyfile too large
+ free(boundary);
+ free(buf);
+ return -13;
+ }
+ //On non empty value
+ if (end - new > 1) {
+ //Set value length
+ *valueLength = end - new - 1;
+ //Allocate value
+ if ((*value = malloc((*valueLength)*sizeof(char))) == NULL) {
+ //Unable to allocate value
+ free(boundary);
+ free(buf);
+ return -14;
+ }
+ //Copy value
+ memcpy(*value, new, *valueLength);
+ }
+ }
+ }
+ //Set start to matched boundary
+ start = buf + b;
+ }
+ //Read one character from stdin
+ r = read(STDIN_FILENO, &d, 1);
+ //Handle errors
+ if (r < 0) {
+ //Error while parsing post data
+ free(boundary);
+ free(buf);
+ return -15;
+ } else if (r == 0) {
+ //Partial receive
+ free(boundary);
+ free(buf);
+ return -16;
+ }
+ //New line case
+ if (d == '\n') {
+ //Store new possible boundary start
+ s = b + 1;
+ }
+ buf[b] = d;
+ }
+
+ //Free buffers
+ free(boundary);
+ free(buf);
+ //Unhandled request
+ } else {
+ return -17;
+ }
+
+ //Send value
+ return 0;
+}
+
+/**
+ * Extract luks and device
+ */
+int extractLuksDevice(char **luks, char **device) {
+ //Declare file descriptor
+ int fd, bufLength;
+ //Declare buf, device and luks pointer
+ char *buf, *d, *l;
+ //Declare stat struct
+ struct stat *stats;
+ //Open file
+ if ((fd = open(CRYPTTAB, O_RDONLY|O_NOATIME|O_NOFOLLOW|O_CLOEXEC)) == -1) {
+ //Can't open crypttab file
+ return -1;
+ }
+ //Allocate stats
+ if ((stats = calloc(1, sizeof(struct stat))) == NULL) {
+ //Unable to allocate stats
+ return -2;
+ }
+ //Stat file
+ if (fstat(fd, stats) == -1) {
+ //Can't stat crypttab
+ return -3;
+ }
+ //Check file size
+ if ((bufLength = stats->st_size) > SSIZE_MAX) {
+ //Crypttab too big
+ return -4;
+ }
+ //Allocate buf
+ if ((buf = malloc(bufLength*sizeof(char))) == NULL) {
+ //Unable to allocate buf
+ return -5;
+ }
+ //Read file
+ if ((read(fd, buf, bufLength)) < bufLength) {
+ //Fail to read crypttab file
+ return -6;
+ }
+ //Close file
+ close(fd);
+ //Free stats buffer
+ free(stats);
+
+ //Search first separator (\s|\t) after luks
+ if ((l = memchr(buf, ' ', bufLength)) == NULL && (l = memchr(buf, ' ', bufLength)) == NULL) {
+ return -7;
+ }
+ //Jump to next char
+ l++;
+ //Search first separator (\s|\t) after device
+ if ((d = memchr(l, ' ', bufLength - (l - buf))) == NULL && (d = memchr(l, ' ', bufLength - (l - buf))) == NULL && (d = memchr(l, '\n', bufLength - (l - buf))) == NULL) {
+ return -8;
+ }
+ //Jump to next char
+ d++;
+
+ //Alloc luks
+ if ((*luks = malloc((l - buf - 1)*sizeof(char))) == NULL) {
+ return -9;
+ }
+ //Allocate device
+ if ((*device = malloc((d - l - 1)*sizeof(char))) == NULL) {
+ //Free and reset luks
+ free(luks);
+ luks=NULL;
+ return -10;
+ }
+
+ //Copy luks
+ memcpy(*luks, buf, l - buf - 1);
+ //Terminate luks
+ (*luks)[l - buf - 1] = '\0';
+
+ //Copy device
+ memcpy(*device, l, d - l - 1);
+ //Terminate device
+ (*device)[d - l - 1] = '\0';
+
+ //Free buffer
+ free(buf);
+
+ //Success
+ return 0;
+}
+
+/**
+ * Extract ask-password pid
+ */
+int extractAskPasswordPid(pid_t *pid) {
+ //Declare stuct dirent
+ struct dirent *entry;
+ //Declare askdir
+ DIR *askdir;
+ //Declare found
+ int found = 0;
+
+ //Allocate dirent struct
+ if ((entry = calloc(1, sizeof(struct dirent))) == NULL) {
+ //Unable to allocate entry
+ return -1;
+ }
+
+ //Open systemd ask-password dir
+ if ((askdir = opendir(ASKPASSWORDDIR)) == NULL) {
+ //Can't open ask dir
+ return -2;
+ }
+
+ //Change dir
+ if (chdir(ASKPASSWORDDIR) == -1) {
+ //Can't change to ask dir
+ return -3;
+ }
+
+ //Loop on dir content
+ while((entry = readdir(askdir))) {
+ //Handle each ask.XXXXXX file
+ if (!strncmp(entry->d_name, "ask.", 4) && strlen(entry->d_name) == 10) {
+ //Declare file descriptor
+ int fd, bufLength;
+ //Declare buf
+ char *buf;
+ //Declare stat struct
+ struct stat *stats;
+ //Open file
+ if ((fd = open(entry->d_name, O_RDONLY|O_NOATIME|O_NOFOLLOW|O_CLOEXEC)) == -1) {
+ //Can't open ask file
+ return -4;
+ }
+ //Allocate stats
+ if ((stats = calloc(1, sizeof(struct stat))) == NULL) {
+ //Unable to allocate stats
+ return -5;
+ }
+ //Stat file
+ if (fstat(fd, stats) == -1) {
+ //Can't stat ask file
+ return -6;
+ }
+ //Check file size
+ if ((bufLength = stats->st_size) > SSIZE_MAX) {
+ //Ask file too big
+ return -7;
+ }
+ //Allocate buf
+ if ((buf = malloc(bufLength*sizeof(char))) == NULL) {
+ //Unable to allocate buf
+ return -8;
+ }
+ //Read file
+ if ((read(fd, buf, bufLength)) < bufLength) {
+ //Fail to read ask file
+ return -9;
+ }
+ //Close file
+ close(fd);
+ //Free stats buffer
+ free(stats);
+
+ //Allocate pointers
+ char *nl = buf, *pl = buf, *e;
+ //Allocate pid
+ *pid = 0;
+
+ //Search for a new line
+ while ((nl = memmem(nl, bufLength - (nl - buf), "\n", strlen("\n")))) {
+ //Jump to next char
+ nl++;
+
+ //Check if we have a = in line but not empty value ("=\n")
+ if ((e = memmem(pl, bufLength - (pl - buf), "=", strlen("="))) && e < nl - 2) {
+ //Jump to next char
+ e++;
+ //Look for PID
+ if (!strncmp(pl, "PID", 3)) {
+ //Declade pid string
+ char *pidStr;
+ //Allocate pid string
+ if ((pidStr = malloc((nl - e)*sizeof(char))) == NULL) {
+ //Unable to allocate pid string
+ return -10;
+ }
+ //Copy pid
+ memcpy(pidStr, e, nl - e - 1);
+ //Terminate pid string
+ pidStr[nl - e] = '\0';
+ //Check pid value
+ if ((*pid = atoi(pidStr)) <= 1) {
+ //Invalid pid
+ return -11;
+ }
+ //Free pid string
+ free(pidStr);
+ //Found a valid process
+ found++;
+ }
+ }
+
+ //Jump prev line to new line
+ pl = nl;
+ }
+
+ //Free buffers
+ free(buf);
+ }
+ }
+
+ //Close systemd ask-password dir
+ if (closedir(askdir) == -1) {
+ //Can't close ask dir
+ return -13;
+ }
+
+ //Free entry
+ free(entry);
+
+ //Found no valid pid
+ if (found == 0) {
+ //No pid found
+ return -14;
+ //Found more than one pid
+ } else if (found > 1) {
+ //No pid found
+ return -15;
+ }
+
+ //Success
+ return 0;
+}
+
+/**
+ * Extract ihttpd pid
+ */
+int extractIHttpdPid(pid_t *pid) {
+ //Declare file descriptor
+ int fd, bufLength;
+ //Declare buf, device and luks pointer
+ char *buf, *l, *pidStr;
+ //Declare stat struct
+ struct stat *stats;
+ //Open file
+ if ((fd = open(IHTTPDPID, O_RDONLY|O_NOATIME|O_NOFOLLOW|O_CLOEXEC)) == -1) {
+ //Can't open crypttab file
+ return -1;
+ }
+ //Allocate stats
+ if ((stats = calloc(1, sizeof(struct stat))) == NULL) {
+ //Unable to allocate stats
+ return -2;
+ }
+ //Stat file
+ if (fstat(fd, stats) == -1) {
+ //Can't stat crypttab
+ return -3;
+ }
+ //Check file size
+ if ((bufLength = stats->st_size) > SSIZE_MAX) {
+ //Crypttab too big
+ return -4;
+ }
+ //Allocate buf
+ if ((buf = malloc(bufLength*sizeof(char))) == NULL) {
+ //Unable to allocate buf
+ return -5;
+ }
+ //Read file
+ if ((read(fd, buf, bufLength)) < bufLength) {
+ //Fail to read crypttab file
+ return -6;
+ }
+ //Close file
+ close(fd);
+ //Free stats buffer
+ free(stats);
+
+ //Search first separator (\s|\t|\n) after pid
+ if ((l = memchr(buf, ' ', bufLength)) == NULL && (l = memchr(buf, ' ', bufLength)) == NULL && (l = memchr(buf, '\n', bufLength)) == NULL) {
+ return -7;
+ }
+ //Jump to next char
+ l++;
+
+ //Alloc pid string
+ if ((pidStr = malloc((l - buf - 1)*sizeof(char))) == NULL) {
+ return -9;
+ }
+
+ //Copy luks
+ memcpy(pidStr, buf, l - buf - 1);
+ //Terminate luks
+ pidStr[l - buf - 1] = '\0';
+
+ //Free buffer
+ free(buf);
+
+ //Store pid
+ if ((*pid = atoi(pidStr)) <= 1) {
+ //Invalid pid
+ return -10;
+ }
+
+ //Free pid string
+ free(pidStr);
+
+ //Success
+ return 0;
+}
+
+/**
+ * Main function
+ */
+int main(int argc, char **argv) {
+
+ //Get request method
+ char *requestMethod = getenv("REQUEST_METHOD");
+
+ //Handle unknown requests
+ if (requestMethod == NULL || (strncmp(requestMethod, "GET", 3) && strncmp(requestMethod, "HEAD", 4) && strncmp(requestMethod, "POST", 4))) {
+ //Send method not allowed
+ die(405, "Unsupported request method");
+ //Handle get and head
+ } else if (!strncmp(requestMethod, "GET", 3) || !strncmp(requestMethod, "HEAD", 4)) {
+ //Send form
+ showForm(getenv("REQUEST_URI")?getenv("REQUEST_URI"):"/", DEFAULT_KEYFILE_SIZE_MAX, DEFAULT_PASSPHRASE_SIZE_MAX);
+ //Handle post
+ } else /*if (!strncmp(requestMethod, "POST", 4))*/ {
+ //Return value
+ int ret;
+
+ //Child pid
+ pid_t pid;
+
+ //Value length
+ //XXX: will contain number of char in value without trailing \0
+ int valueLength;
+ //Value string
+ //XXX: will contain value without a tailing \0
+ char *value = NULL;
+
+ //Content length
+ int contentLength;
+ //Content length string from env
+ char *contentLengthStr = getenv("CONTENT_LENGTH");
+ //Content type
+ char *contentType = getenv("CONTENT_TYPE");
+
+ //Declare luks and device
+ char *luks = NULL, *device = NULL;
+
+ //Pairs of pipe for stdin, stdout and stderr
+ int inPipe[2], errPipe[2];
+
+ //Handle unknown content type
+ if (contentType == NULL || (strncmp(contentType, "application/x-www-form-urlencoded", 33) && strncmp(contentType, "multipart/form-data", 19))) {
+ die(400, "Unknown content type");
+ }
+
+ //Handle invalid multipart/form-data content type
+ //XXX: max boundary length is 70 as per rfc1521 & rfc2046
+ if (!strncmp(contentType, "multipart/form-data", 19) && (strncmp(contentType, "multipart/form-data; boundary=", 30) || strlen(contentType) <= 30 || strlen(contentType) > 100)) {
+ die(400, "Malformed boundary in multipart/form-data request");
+ }
+
+ //Handle invalid content length
+ //XXX: deny empty contentLength as chrome send a contentLength even for a device
+ if (contentLengthStr == NULL || (contentLength = atoi(contentLengthStr)) <= 0) {
+ die(411, "Invalid content length");
+ }
+
+ //Handle application/x-www-form-urlencoded request length
+ //XXX: limit to key=xyz where xyz can be all encoded in %XX
+ if (!strncmp(contentType, "application/x-www-form-urlencoded", 33) && contentLength > (DEFAULT_PASSPHRASE_SIZE_MAX * 3 + 4)) {
+ die(400, "Invalid application/x-www-form-urlencoded request length");
+ }
+
+ //Handle multipart/form-data request length
+ //XXX: limit to arbitrary 3 times the keyfile max size
+ if (!strncmp(contentType, "multipart/form-data", 19) && contentLength > (DEFAULT_KEYFILE_SIZE_MAX * 3)) {
+ die(400, "Invalid multipart/form-data request length");
+ }
+
+ //Extract value
+ if ((ret = extractValue(&value, &valueLength, contentType, contentLength)) < 0) {
+ die(500, "Failed to extract value");
+ }
+
+
+ //Extract luks and device
+ if ((ret = extractLuksDevice(&luks, &device)) < 0) {
+ die(500, "Failed to extract luks and device");
+ }
+
+ //Create stdin pipe
+ if (pipe(inPipe) == -1) {
+ die(500, "Failed to create in pipe");
+ }
+
+ //Create stderr pipe
+ if (pipe(errPipe) == -1) {
+ die(500, "Failed to create err pipe");
+ }
+
+ //Fork process
+ if ((pid = fork()) == -1) {
+ die(500, "Failed to fork");
+ }
+
+ //Child process
+ if (pid == 0) {
+ //Child argv
+ char *cargv[] = { CRYPTSETUP, "-d", "-", "luksOpen", device, luks, NULL };
+ char *carge[] = { NULL };
+ //Free value
+ free(value);
+ //Redirect stdin to pipe
+ if (dup2(inPipe[0], STDIN_FILENO) == -1) {
+ die(500, "Failed to redirect in pipe");
+ }
+ //Close inPipe
+ close(inPipe[0]);
+ close(inPipe[1]);
+ //Redirect stderr to pipe
+ if (dup2(errPipe[1], STDERR_FILENO) == -1) {
+ die(500, "Failed to redirect err pipe");
+ }
+ //Close errPipe
+ close(errPipe[0]);
+ close(errPipe[1]);
+ //Call cryptsetup
+ if (execve(CRYPTSETUP, cargv, carge) == -1) {
+ die(500, "Failed to call cryptsetup");
+ }
+ //Parent process
+ } else {
+ //Free luks
+ free(luks);
+ //Free device
+ free(device);
+
+ //Close unused inPipe end
+ close(inPipe[0]);
+ //Close unused errPipe end
+ close(errPipe[1]);
+
+ //Send password on stdin anyway
+ //XXX: this fail if device is already unlocked for example
+ write(inPipe[1], value, valueLength);
+
+ //Free value
+ free(value);
+
+ //Close stdin with EOF
+ close(inPipe[1]);
+
+ //Wait child
+ if (waitpid(pid, &ret, 0) == -1) {
+ die(500, "Failed to wait child");
+ }
+
+ //Handle already unlocked device
+ if (ret == 1280) {
+ die(200, "Device already unlocked");
+ //Handle already in use device
+ } else if (ret == 5) {
+ die(500, "Device already in use");
+ //Handle invalid luks device
+ } else if (ret == 256) {
+ die(500, "Device is now a valid device");
+ //Handle no key available with this passphrase
+ } else if (ret == 512) {
+ die(500, "No slot for this value");
+ //Handle unexisting device or permission denied
+ } else if (ret == 1014) {
+ die(500, "Device doesn't exist or access denied");
+ //Unknown error
+ } else if (ret != 0) {
+ //Err length and counter
+ int errLength = 2048, e = 0;
+ //Declare err buffer
+ char *err;
+ //Buffer char
+ char c;
+ //Alloc err buffer
+ if ((err = malloc(errLength*sizeof(char))) == NULL) {
+ die(500, "Couldn't alloc err buffer");
+ }
+ //Advance after ret code
+ e = sprintf(err, "%d:", ret);
+ //Fetch stderr and store in err buffer
+ while(read(errPipe[0], &c, 1) > 0) {
+ //Grow buffer if we reach end
+ if (e == errLength) {
+ if ((err = realloc(err, (errLength+2048)*sizeof(char))) == NULL) {
+ die(500, "Couldn't grow err buffer");
+ }
+ errLength += 2048;
+ }
+ //Store character
+ err[e] = c;
+ //Pass to next
+ e++;
+ }
+ //Terminate err buffer
+ err[e] = '\0';
+ //Realloc err buffer
+ if ((err = realloc(err, (e+1)*sizeof(char))) == NULL) {
+ die(500, "Couldn't ungrow err buffer");
+ }
+ //Die with luks error
+ die(500, err);
+ }
+ //Close errPipe
+ close(errPipe[0]);
+ }
+
+ //Fork process
+ if ((pid = fork()) == -1) {
+ die(500, "Failed to fork");
+ }
+
+ //IHttpd killing child process
+ if (pid == 0) {
+ //File descriptor
+ int fd;
+
+ //Declare ihttpd pid
+ pid_t ihttpdPid;
+
+ //Close stdin
+ close(STDIN_FILENO);
+
+ //Disable line buffering on stdout and stderr
+ setvbuf(stdout, NULL, _IONBF, 0);
+ setvbuf(stderr, NULL, _IONBF, 0);
+
+ //Redirect output to log
+ if ((fd = open(IHTTPDLOG, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR)) == -1) {
+ fprintf(stderr, "Open ihttpd child log failed\n");
+ } else {
+ //Close stdout
+ close(STDOUT_FILENO);
+ //Redirect stdout on childlog
+ if (dup2(fd, STDOUT_FILENO) == -1) {
+ fprintf(stderr, "Redirect stdout to ihttpd child log failed\n");
+ }
+ //Close stderr
+ close(STDERR_FILENO);
+ //Redirect stderr on childlog
+ if (dup2(fd, STDERR_FILENO) == -1) {
+ fprintf(stderr, "Redirect stderr to ihttpd child log failed\n");
+ }
+ //Close childlog fd
+ close(fd);
+ }
+
+ //Extract ihttpd pid
+ if (extractIHttpdPid(&ihttpdPid) < 0) {
+ fprintf(stderr, "Failed to extract ihttpd pid");
+ exit(EXIT_FAILURE);
+ }
+
+ //Close stdout and stderr without childlog
+ if (fd == -1) {
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+ }
+
+ //Wait until get rattached to init(getppid()==1)
+ //XXX: we are really blind here
+ while(getppid() != 1) {
+ //Sleep half a second
+ if (usleep(500000) == -1 && fd != -1) {
+ printf("Usleep failed\n");
+ }
+ }
+
+ //Termiate ihttpd
+ if (kill(ihttpdPid, 0) == 0 && kill(ihttpdPid, SIGTERM) == -1 && fd != -1) {
+ printf("Termiate ihttpd failed\n");
+ }
+
+ //Sleep half a second
+ if (usleep(500000) == -1 && fd != -1) {
+ printf("Usleep failed\n");
+ }
+
+ //Kill ihttpd
+ if (kill(ihttpdPid, 0) == 0 && kill(ihttpdPid, SIGKILL) == -1) {
+ printf("Kill ihttpd failed\n");
+ }
+
+ //Parent process
+ } else {
+
+ //Fork process
+ if ((pid = fork()) == -1) {
+ die(500, "Failed to fork");
+ }
+
+ //Ask password killing child process
+ //XXX: we are blind here
+ if (pid == 0) {
+ //File descriptor
+ int fd;
+
+ //Declare ask password pid
+ pid_t askPasswordPid;
+
+ //Close stdin
+ close(STDIN_FILENO);
+
+ //Disable line buffering on stdout and stderr
+ setvbuf(stdout, NULL, _IONBF, 0);
+ setvbuf(stderr, NULL, _IONBF, 0);
+
+ //Redirect output to log
+ if ((fd = open(ASKPASSWORDLOG, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR)) == -1) {
+ fprintf(stderr, "Open ask password child log failed\n");
+ } else {
+ //Close stdout
+ close(STDOUT_FILENO);
+ //Redirect stdout on childlog
+ if (dup2(fd, STDOUT_FILENO) == -1) {
+ fprintf(stderr, "Redirect stdout to ask password child log failed\n");
+ }
+ //Close stderr
+ close(STDERR_FILENO);
+ //Redirect stderr on childlog
+ if (dup2(fd, STDERR_FILENO) == -1) {
+ fprintf(stderr, "Redirect stderr to ask password child log failed\n");
+ }
+ //Close childlog fd
+ close(fd);
+ }
+
+ //Extract ask password pid
+ if (extractAskPasswordPid(&askPasswordPid) < 0) {
+ fprintf(stderr, "Failed to extract ask password pid");
+ exit(EXIT_FAILURE);
+ }
+
+ //Close stdout and stderr without childlog
+ if (fd == -1) {
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+ }
+
+ //Wait until get rattached to init(getppid()==1)
+ //XXX: we are really blind here
+ while(getppid() != 1) {
+ //Sleep half a second
+ if (usleep(500000) == -1 && fd != -1) {
+ printf("Usleep failed\n");
+ }
+ }
+
+ //Termitate ask password
+ if (kill(askPasswordPid, 0) == 0 && kill(askPasswordPid, SIGTERM) == -1 && fd != -1) {
+ printf("Termiate ask password failed\n");
+ }
+
+
+ //Sleep half a second
+ if (usleep(500000) == -1 && fd != -1) {
+ printf("Usleep failed\n");
+ }
+
+ //Kill ask password
+ if (kill(askPasswordPid, 0) == 0 && kill(askPasswordPid, SIGKILL) == -1) {
+ printf("Kill ask password failed\n");
+ }
+
+ //Parent process
+ } else {
+
+ //Process success
+ header(200, "text/plain");
+ printf("Sent value, boot should resume now");
+ fflush(NULL);
+
+ }
+
+ }
+
+ }
+
+ exit(EXIT_SUCCESS);
+}