summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDenys Vlasenko <dvlasenk@redhat.com>2011-06-13 19:43:56 +0200
committerDenys Vlasenko <dvlasenk@redhat.com>2011-06-13 19:43:56 +0200
commitbcbe6aef128f51ed28bffc5c0d8ddeb1c2d77da8 (patch)
tree6e2469e89356850c51d89512b98f33a50f1c3f8a
parent8edc80c6a08ef30d02cad35b736c0c7dcb62a7f3 (diff)
downloadabrt-bcbe6aef128f51ed28bffc5c0d8ddeb1c2d77da8.tar.gz
abrt-bcbe6aef128f51ed28bffc5c0d8ddeb1c2d77da8.tar.xz
abrt-bcbe6aef128f51ed28bffc5c0d8ddeb1c2d77da8.zip
abrt-server: convert to using simple HTTP; add deletion op
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
-rw-r--r--src/daemon/abrt-server.c335
-rw-r--r--src/hooks/abrt_exception_handler.py.in3
2 files changed, 293 insertions, 45 deletions
diff --git a/src/daemon/abrt-server.c b/src/daemon/abrt-server.c
index 49b7e0ce..367289f8 100644
--- a/src/daemon/abrt-server.c
+++ b/src/daemon/abrt-server.c
@@ -72,10 +72,6 @@ Finalizing dump creation:
\0
*/
-/* Buffer for incomplete incoming messages. */
-static char *messagebuf_data = NULL;
-static unsigned messagebuf_len = 0;
-
static unsigned total_bytes_read = 0;
static uid_t client_uid = (uid_t)-1L;
@@ -98,7 +94,7 @@ static char *reason;
* Caller must ensure that all fields in struct client
* are properly filled.
*/
-static void create_debug_dump()
+static int create_debug_dump()
{
/* Create temp directory with the debug dump.
This directory is renamed to final directory name after
@@ -158,6 +154,52 @@ static void create_debug_dump()
}
free(path);
+
+ return 201; /* Created */
+}
+
+/* Remove dump dir */
+static int delete_path(const char *dump_dir_name)
+{
+ /* If doesn't start with "DEBUG_DUMPS_DIR/"... */
+ if (strncmp(dump_dir_name, DEBUG_DUMPS_DIR"/", strlen(DEBUG_DUMPS_DIR"/")) != 0
+ /* or contains "/." anywhere (-> might contain ".." component) */
+ || strstr(dump_dir_name + strlen(DEBUG_DUMPS_DIR), "/.")
+ ) {
+ /* Then refuse to operate on it (someone is attacking us??) */
+ error_msg("Bad dump directory name '%s', not deleting", dump_dir_name);
+ return 400; /* Bad Request */
+ }
+
+ struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
+ if (!dd)
+ return 404; /* Not Found */
+
+ if (client_uid != 0) /* not called by root */
+ {
+ char client_uid_str[sizeof(long) * 3 + 2];
+ sprintf(client_uid_str, "%ld", (long)client_uid);
+
+ char *uid = dd_load_text_ext(dd, FILENAME_UID, DD_FAIL_QUIETLY_ENOENT | DD_LOAD_TEXT_RETURN_NULL_ON_FAILURE);
+ /* we assume that the dump_dir can be handled by everyone if uid == NULL
+ * e.g: kerneloops
+ */
+ if (uid != NULL)
+ {
+ bool uid_matches = (strcmp(uid, client_uid_str) == 0);
+ free(uid);
+ if (!uid_matches)
+ {
+ dd_close(dd);
+ error_msg("Dump directory '%s' can't be accessed by user with uid %ld", dump_dir_name, (long)client_uid);
+ return 403; /* Forbidden */
+ }
+ }
+ }
+
+ dd_delete(dd);
+
+ return 0; /* success */
}
/* Checks if a string contains only printable characters. */
@@ -254,24 +296,139 @@ static void process_message(const char *message)
VERB3 log("Saved PID %u", pid);
return;
}
+}
- /* Creates debug dump if all fields were already provided. */
- if (starts_with(message, "DONE"))
+static int perform_http_xact(void)
+{
+ /* Read header */
+ char *body_start = NULL;
+ char *messagebuf_data = NULL;
+ unsigned messagebuf_len = 0;
+ /* Loop until EOF/error/timeout/end_fo_header */
+ while (1)
{
- if (!pid || !backtrace || !executable
- || !analyzer || !dir_basename || !reason
- ) {
- error_msg_and_die("Got DONE, but some data are missing. Aborting");
+ messagebuf_data = xrealloc(messagebuf_data, messagebuf_len + INPUT_BUFFER_SIZE);
+ char *p = messagebuf_data + messagebuf_len;
+ int rd = read(STDIN_FILENO, p, INPUT_BUFFER_SIZE);
+ if (rd < 0)
+ {
+ if (errno == EINTR) /* SIGALRM? */
+ error_msg_and_die("Timed out");
+ perror_msg_and_die("read");
}
+ if (rd == 0)
+ break;
+
+ VERB3 log("Received %u bytes of data", rd);
+ messagebuf_len += rd;
+ total_bytes_read += rd;
+ if (total_bytes_read > MAX_MESSAGE_SIZE)
+ error_msg_and_die("Message is too long, aborting");
- /* Write out the crash dump. Don't let alarm to interrupt here */
+ /* Check whether we see end of header */
+ /* Note: we support both [\r]\n\r\n and \n\n */
+ char *past_end = messagebuf_data + messagebuf_len;
+ if (p > messagebuf_data+1)
+ p -= 2; /* start search from two last bytes in last read - they might be '\n\r' */
+ while (p < past_end)
+ {
+ p = memchr(p, '\n', past_end - p);
+ if (!p)
+ break;
+ p++;
+ if (p >= past_end)
+ break;
+ if (*p == '\n'
+ || (*p == '\r' && p+1 < past_end && p[1] == '\n')
+ ) {
+ body_start = p + 1 + (*p == '\r');
+ *p = '\0';
+ goto found_end_of_header;
+ }
+ }
+ } /* while (read) */
+ found_end_of_header: ;
+ VERB3 log("Request: %s", messagebuf_data);
+
+ /* Sanitize and analyze header.
+ * Header now is in messagebuf_data, NUL terminated string,
+ * with last empty line deleted (by placement of NUL).
+ * \r\n are not (yet) converted to \n, multi-line headers also
+ * not converted.
+ */
+ /* First line must be "op<space>[http://host]/path<space>HTTP/n.n".
+ * <space> is exactly one space char.
+ */
+ if (strncmp(messagebuf_data, "DELETE ", strlen("DELETE ")) == 0)
+ {
+ messagebuf_data += strlen("DELETE ");
+ char *space = strchr(messagebuf_data, ' ');
+ if (!space || strncmp(space+1, "HTTP/", strlen("HTTP/")) != 0)
+ return 400; /* Bad Request */
+ *space = '\0';
+ //decode_url(messagebuf_data); %20 => ' '
alarm(0);
- create_debug_dump();
+ return delete_path(messagebuf_data);
+ }
+
+ if (strncmp(messagebuf_data, "PUT ", strlen("PUT ")) != 0)
+ {
+ return 400; /* Bad Request */;
+ }
+
+ /* Read body */
+ if (!body_start)
+ {
+ VERB1 log("EOF detected, exiting");
+ return 400; /* Bad Request */
+ }
+
+ messagebuf_len -= (body_start - messagebuf_data);
+ memmove(messagebuf_data, body_start, messagebuf_len);
+ VERB3 log("Body so far: %u bytes, '%s'", messagebuf_len, messagebuf_data);
+
+ /* Loop until EOF/error/timeout */
+ while (1)
+ {
+ while (1)
+ {
+ unsigned len = strnlen(messagebuf_data, messagebuf_len);
+ if (len >= messagebuf_len)
+ break;
+ /* messagebuf has at least one NUL - process the line */
+ process_message(messagebuf_data);
+ messagebuf_len -= (len + 1);
+ memmove(messagebuf_data, messagebuf_data + len + 1, messagebuf_len);
+ }
+
+ messagebuf_data = xrealloc(messagebuf_data, messagebuf_len + INPUT_BUFFER_SIZE);
+ int rd = read(STDIN_FILENO, messagebuf_data + messagebuf_len, INPUT_BUFFER_SIZE);
+ if (rd < 0)
+ {
+ if (errno == EINTR) /* SIGALRM? */
+ error_msg_and_die("Timed out");
+ perror_msg_and_die("read");
+ }
+ if (rd == 0)
+ break;
- /* Reset alarm and the counter which detects oversized dumps */
- alarm(TIMEOUT);
- total_bytes_read = 0;
+ VERB3 log("Received %u bytes of data", rd);
+ messagebuf_len += rd;
+ total_bytes_read += rd;
+ if (total_bytes_read > MAX_MESSAGE_SIZE)
+ error_msg_and_die("Message is too long, aborting");
+ }
+
+ /* Creates debug dump if all fields were already provided. */
+ if (!pid || !backtrace || !executable
+ || !analyzer || !dir_basename || !reason
+ ) {
+ error_msg_and_die("Some data are missing. Aborting");
}
+
+ /* Write out the crash dump. Don't let alarm to interrupt here */
+ alarm(0);
+ return create_debug_dump();
}
static void dummy_handler(int sig_unused) {}
@@ -333,38 +490,128 @@ int main(int argc, char **argv)
client_uid = cr.uid;
}
- /* Loop until EOF/error/timeout */
- while (1)
- {
- messagebuf_data = xrealloc(messagebuf_data, messagebuf_len + INPUT_BUFFER_SIZE);
- int rd = read(STDIN_FILENO, messagebuf_data + messagebuf_len, INPUT_BUFFER_SIZE);
- if (rd < 0)
- {
- if (errno == EINTR) /* SIGALRM? */
- error_msg_and_die("Timed out");
- perror_msg_and_die("read");
+ int r = perform_http_xact();
+ if (r == 0)
+ r = 200;
+
+ printf("HTTP/1.1 %u \r\n\r\n", r);
+
+ return (r >= 400); /* Error if 400+ */
+}
+
+
+
+
+
+
+#if 0
+
+// TODO: example of SSLed connection
+
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+ if (flags & OPT_SSL) {
+ /* load key and cert files */
+ SSL_CTX *ctx;
+ SSL *ssl;
+
+ ctx = init_ssl_context();
+ if (SSL_CTX_use_certificate_file(ctx, cert_path, SSL_FILETYPE_PEM) <= 0
+ || SSL_CTX_use_PrivateKey_file(ctx, key_path, SSL_FILETYPE_PEM) <= 0
+ ) {
+ ERR_print_errors_fp(stderr);
+ error_msg_and_die("SSL certificates err\n");
}
- if (rd == 0)
- break;
+ if (!SSL_CTX_check_private_key(ctx)) {
+ error_msg_and_die("Private key does not match public key\n");
+ }
+ (void)SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
+
+ //TODO more errors?
+ ssl = SSL_new(ctx);
+ SSL_set_fd(ssl, sockfd_in);
+ //SSL_set_accept_state(ssl);
+ if (SSL_accept(ssl) == 1) {
+ //while whatever serve
+ while (serve(ssl, flags))
+ continue;
+ //TODO errors
+ SSL_shutdown(ssl);
+ }
+ SSL_free(ssl);
+ SSL_CTX_free(ctx);
+ } else {
+ while (serve(&sockfd_in, flags))
+ continue;
+ }
- VERB3 log("Received %u bytes of data", rd);
- messagebuf_len += rd;
- total_bytes_read += rd;
- if (total_bytes_read > MAX_MESSAGE_SIZE)
- error_msg_and_die("Message is too long, aborting");
- while (1)
- {
- unsigned len = strnlen(messagebuf_data, messagebuf_len);
- if (len >= messagebuf_len)
+ err = (flags & OPT_SSL) ? SSL_read(sock, buffer, READ_BUF-1):
+ read(*(int*)sock, buffer, READ_BUF-1);
+
+ if ( err < 0 ) {
+ //TODO handle errno || SSL_get_error(ssl,err);
+ break;
+ }
+ if ( err == 0 ) break;
+
+ if (!head) {
+ buffer[err] = '\0';
+ clean[i%2] = delete_cr(buffer);
+ cut = g_strstr_len(buffer, -1, "\n\n");
+ if ( cut == NULL ) {
+ g_string_append(headers, buffer);
+ } else {
+ g_string_append_len(headers, buffer, cut-buffer);
+ }
+ }
+
+ /* end of header section? */
+ if ( !head && ( cut != NULL || (clean[(i+1)%2] && buffer[0]=='\n') ) ) {
+ parse_head(&request, headers);
+ head = TRUE;
+ c_len = has_body(&request);
+
+ if ( c_len ) {
+ //if we want to read body some day - this will be the right place to begin
+ //malloc body append rest of the (fixed) buffer at the beginning of a body
+ //if clean buffer[1];
+ } else {
break;
- /* messagebuf has at least one NUL - process the line */
- process_message(messagebuf_data);
- messagebuf_len -= (len + 1);
- memmove(messagebuf_data, messagebuf_data + len + 1, messagebuf_len);
+ }
+ break; //because we don't support body yet
+ } else if ( head == TRUE ) {
+ /* body-reading stuff
+ * read body, check content-len
+ * save body to request
+ */
+ break;
+ } else {
+ // count header size
+ len += err;
+ if ( len > READ_BUF-1 ) {
+ //TODO header is too long
+ break;
+ }
}
+
+ i++;
}
- VERB1 log("EOF detected, exiting");
- return 0;
-}
+ g_string_free(headers, true); //because we allocated it
+
+ rt = generate_response(&request, &response);
+
+ /* write headers */
+ if ( flags & OPT_SSL ) {
+ //TODO err
+ err = SSL_write(sock, response.response_line, strlen(response.response_line));
+ err = SSL_write(sock, response.head->str , strlen(response.head->str));
+ err = SSL_write(sock, "\r\n", 2);
+ } else {
+ //TODO err
+ err = write(*(int*)sock, response.response_line, strlen(response.response_line));
+ err = write(*(int*)sock, response.head->str , strlen(response.head->str));
+ err = write(*(int*)sock, "\r\n", 2);
+ }
+#endif
diff --git a/src/hooks/abrt_exception_handler.py.in b/src/hooks/abrt_exception_handler.py.in
index 5b45fcd4..ace51c5c 100644
--- a/src/hooks/abrt_exception_handler.py.in
+++ b/src/hooks/abrt_exception_handler.py.in
@@ -35,6 +35,7 @@ def write_dump(pid, tb):
try:
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
s.connect(@VAR_RUN@ + "/abrt/abrt.socket")
+ s.sendall("PUT / HTTP/1.1\r\n\r\n")
s.sendall("PID=%s\0" % pid)
s.sendall("EXECUTABLE=%s\0" % executable)
s.sendall("ANALYZER=Python\0")
@@ -44,7 +45,7 @@ def write_dump(pid, tb):
# CCMainWindow.py:1:<module>:ZeroDivisionError: integer division or modulo by zero
s.sendall("REASON=%s\0" % tb.splitlines()[0])
s.sendall("BACKTRACE=%s\0" % tb)
- s.sendall("DONE\0")
+ # TODO: read back response and complain if it is not "xxx Created"
s.close()
except Exception, ex:
import syslog