diff options
| author | Karel Klic <kklic@redhat.com> | 2011-01-20 20:48:11 +0100 |
|---|---|---|
| committer | Karel Klic <kklic@redhat.com> | 2011-01-20 20:48:11 +0100 |
| commit | f46fcd0cd0e66faa99b726747da504ba83ba9949 (patch) | |
| tree | f66ca471420e466d4e2a1d50f3722f514ea11204 | |
| parent | 139975f2369015d6ca02f4291ab4b953b1e41ce5 (diff) | |
| download | abrt-f46fcd0cd0e66faa99b726747da504ba83ba9949.tar.gz abrt-f46fcd0cd0e66faa99b726747da504ba83ba9949.tar.xz abrt-f46fcd0cd0e66faa99b726747da504ba83ba9949.zip | |
Retrace client work in progress.
| -rw-r--r-- | src/plugins/abrt-retrace-client.c | 344 |
1 files changed, 292 insertions, 52 deletions
diff --git a/src/plugins/abrt-retrace-client.c b/src/plugins/abrt-retrace-client.c index 28b8543f..7eef4844 100644 --- a/src/plugins/abrt-retrace-client.c +++ b/src/plugins/abrt-retrace-client.c @@ -23,21 +23,40 @@ #include <pk11pub.h> #include <ssl.h> #include <sslproto.h> +#include <sslerr.h> +#include <secerr.h> -static const char *dump_dir_name = "."; -static const char abrt_retrace_client_usage[] = "abrt-retrace-client [options] -d DIR"; +static const char *dump_dir_name = NULL; +static const char *coredump = NULL; +static const char *task_id = NULL; +static const char *task_password = NULL; +static const char abrt_retrace_client_usage[] = "abrt-retrace-client <operation> [options]\nOperations: create/status/backtrace/log"; enum { - OPT_v = 1 << 0, - OPT_d = 1 << 1, - OPT_s = 1 << 2, + OPT_verbose = 1 << 0, + OPT_syslog = 1 << 1, + OPT_insecure = 1 << 2, + OPT_dir = 1 << 3, + OPT_core = 1 << 4, + OPT_wait = 1 << 5, + OPT_bttodir = 1 << 6, + OPT_task = 1 << 7, + OPT_password = 1 << 8 }; /* Keep enum above and order of options below in sync! */ static struct options abrt_retrace_client_options[] = { OPT__VERBOSE(&g_verbose), - OPT_STRING( 'd', NULL, &dump_dir_name, "DIR", "Crash dump directory"), - OPT_BOOL( 's', NULL, NULL, "Log to syslog"), + OPT_BOOL( 's', "syslog", NULL, "log to syslog"), + OPT_BOOL( 'k', "insecure", NULL, "allow insecure connection to retrace server"), + OPT_GROUP("For create operation"), + OPT_STRING( 'd', "dir", &dump_dir_name, "DIR", "read data from ABRT crash dump directory"), + OPT_STRING( 'c', "core", &coredump, "COREDUMP", "read data from coredump"), + OPT_BOOL( 'w', "wait", NULL, "keep connected to the server, display progress and then backtrace"), + OPT_BOOL( 'r', "bttodir", NULL, "if both --dir and --wait are provided, store the backtrace to the DIR when it becomes available"), + OPT_GROUP("For status, backtrace, and log operations"), + OPT_STRING( 't', "task", &task_id, "ID", "id of your task on server"), + OPT_STRING( 'p', "password", &task_password, "PWD", "password of your task on server"), OPT_END() }; @@ -45,10 +64,10 @@ static struct options abrt_retrace_client_options[] = { * dump directory. The entry is added to argindex offset to the array, * and the argindex is then increased. */ -void args_add_if_exists(const char *args[], - struct dump_dir *dd, - const char *name, - int *argindex) +static void args_add_if_exists(const char *args[], + struct dump_dir *dd, + const char *name, + int *argindex) { if (dd_exist(dd, name)) { @@ -60,7 +79,7 @@ void args_add_if_exists(const char *args[], /* Create an archive with files required for retrace server and return * a file descriptor. Returns -1 if it fails. */ -int create_archive(const char *dump_dir_name) +static int create_archive(const char *dump_dir_name) { struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0); if (!dd) @@ -71,7 +90,7 @@ int create_archive(const char *dump_dir_name) int tempfd = mkstemps(filename, /*suffixlen:*/7); if (tempfd == -1) perror_msg_and_die("Cannot open temporary file"); - //xunlink(filename); + xunlink(filename); free(filename); /* Run xz: @@ -140,12 +159,98 @@ int create_archive(const char *dump_dir_name) waitpid(xz_child, &status, 0); VERB1 log_msg("Done..."); + xlseek(tempfd, 0, SEEK_SET); return tempfd; } -void ssl_connect(const char *host, PRFileDesc **tcp_sock, PRFileDesc **ssl_sock) +static SECStatus ssl_bad_cert_handler(void *arg, PRFileDesc *sock) +{ + PRErrorCode err = PR_GetError(); + CERTCertificate *cert = SSL_PeerCertificate(sock); + char *subject = CERT_NameToAscii(&cert->subject); + char *subject_cn = CERT_GetCommonName(&cert->subject); + char *issuer = CERT_NameToAscii(&cert->issuer); + CERT_DestroyCertificate(cert); + char *target_host = SSL_RevealURL(sock); + if (!target_host) + target_host = xstrdup("(unknown)"); + bool allow_insecure = (bool)arg; + + switch (err) + { + case SEC_ERROR_CA_CERT_INVALID: + error_msg("Issuer certificate is invalid: '%s'.", issuer); + break; + case SEC_ERROR_UNTRUSTED_ISSUER: + error_msg("Certificate is signed by an untrusted issuer: '%s'.", issuer); + break; + case SSL_ERROR_BAD_CERT_DOMAIN: + error_msg("Certificate subject name '%s' does not match target host name '%s'.", + subject_cn, target_host); + break; + case SEC_ERROR_EXPIRED_CERTIFICATE: + error_msg("Remote certificate has expired."); + break; + case SEC_ERROR_UNKNOWN_ISSUER: + error_msg("Certificate issuer is not recognized: '%s'", issuer); + break; + default: + error_msg("Bad certifiacte received. Subject '%s', issuer '%s'.", + subject, issuer); + break; + } + + + PR_Free(target_host); + return allow_insecure ? SECSuccess : SECFailure; +} + +static SECStatus ssl_handshake_callback(PRFileDesc *sock, void *arg) +{ + return SECSuccess; +} + +static const char *ssl_get_configdir() +{ + struct stat buf; + + if (getenv("SSL_DIR")) + { + if (0 == stat(getenv("SSL_DIR"), &buf) && + S_ISDIR(buf.st_mode)) + { + return getenv("SSL_DIR"); + } + } + + if (0 == stat("/etc/pki/nssdb", &buf) && + S_ISDIR(buf.st_mode)) + { + return "/etc/pki/nssdb"; + } + + return NULL; +} + +static char *ssl_get_password(PK11SlotInfo *slot, PRBool retry, void *arg) +{ + return NULL; +} + +static void ssl_connect(const char *host, + PRFileDesc **tcp_sock, + PRFileDesc **ssl_sock, + bool allow_insecure) { - NSS_Init("/etc/ssl"); + const char *configdir = ssl_get_configdir(); + if (configdir) + NSS_Initialize(configdir, "", "", "", NSS_INIT_READONLY); + else + NSS_NoDB_Init(NULL); + + PK11_SetPasswordFunc(ssl_get_password); + NSS_SetDomesticPolicy(); + *tcp_sock = PR_NewTCPSocket(); if (!*tcp_sock) error_msg_and_die("Failed to create a TCP socket"); @@ -153,7 +258,6 @@ void ssl_connect(const char *host, PRFileDesc **tcp_sock, PRFileDesc **ssl_sock) PRSocketOptionData sock_option; sock_option.option = PR_SockOpt_Nonblocking; sock_option.value.non_blocking = PR_FALSE; - PRStatus pr_status = PR_SetSocketOption(*tcp_sock, &sock_option); if (PR_SUCCESS != pr_status) { @@ -175,12 +279,12 @@ void ssl_connect(const char *host, PRFileDesc **tcp_sock, PRFileDesc **ssl_sock) error_msg_and_die("Failed to enable client handshake to SSL socket."); } - sec_status = SSL_OptionSet(*ssl_sock, SSL_ENABLE_FDX, PR_TRUE); - if (SECSuccess != sec_status) - { - PR_Close(*ssl_sock); - error_msg_and_die("Failed to set full duplex to SSL socket."); - } + if (SECSuccess != SSL_OptionSet(*ssl_sock, SSL_ENABLE_SSL2, PR_TRUE)) + error_msg_and_die("Failed to enable client handshake to SSL socket."); + if (SECSuccess != SSL_OptionSet(*ssl_sock, SSL_ENABLE_SSL3, PR_TRUE)) + error_msg_and_die("Failed to enable client handshake to SSL socket."); + if (SECSuccess != SSL_OptionSet(*ssl_sock, SSL_ENABLE_TLS, PR_TRUE)) + error_msg_and_die("Failed to enable client handshake to SSL socket."); sec_status = SSL_SetURL(*ssl_sock, host); if (SECSuccess != sec_status) @@ -218,7 +322,21 @@ void ssl_connect(const char *host, PRFileDesc **tcp_sock, PRFileDesc **ssl_sock) error_msg_and_die("Failed to connect SSL address."); } - sec_status = SSL_ResetHandshake(*ssl_sock, PR_FALSE); + if (SECSuccess != SSL_BadCertHook(*ssl_sock, + (SSLBadCertHandler)ssl_bad_cert_handler, + (void*)allow_insecure)) + { + PR_Close(*ssl_sock); + error_msg_and_die("Failed to set certificate hook."); + } + + if (SECSuccess != SSL_HandshakeCallback(*ssl_sock, (SSLHandshakeCallback)ssl_handshake_callback, NULL)) + { + PR_Close(*ssl_sock); + error_msg_and_die("Failed to set handshake callback."); + } + + sec_status = SSL_ResetHandshake(*ssl_sock, /*asServer:*/PR_FALSE); if (SECSuccess != sec_status) { PR_Close(*ssl_sock); @@ -229,11 +347,12 @@ void ssl_connect(const char *host, PRFileDesc **tcp_sock, PRFileDesc **ssl_sock) if (SECSuccess != sec_status) { PR_Close(*ssl_sock); - error_msg_and_die("Failed to force handshake."); + error_msg_and_die("Failed to force handshake: NSS error %d", + PR_GetError()); } } -void ssl_disconnect(PRFileDesc *ssl_sock) +static void ssl_disconnect(PRFileDesc *ssl_sock) { PRStatus pr_status = PR_Close(ssl_sock); if (PR_SUCCESS != pr_status) @@ -248,29 +367,23 @@ void ssl_disconnect(PRFileDesc *ssl_sock) PR_Cleanup(); } -int main(int argc, char **argv) +static int run_create(const char *dump_dir, + const char *coredump, + bool wait, + bool bttodir, + bool ssl_allow_insecure) { - char *env_verbose = getenv("ABRT_VERBOSE"); - if (env_verbose) - g_verbose = atoi(env_verbose); - - unsigned opts = parse_opts(argc, argv, abrt_retrace_client_options, - abrt_retrace_client_usage); - - if (opts & OPT_s) - { - openlog(msg_prefix, 0, LOG_DAEMON); - logmode = LOGMODE_SYSLOG; - } - int tempfd = create_archive(dump_dir_name); + if (-1 == tempfd) + return 1; /* Get the file size. */ struct stat tempfd_buf; fstat(tempfd, &tempfd_buf); PRFileDesc *tcp_sock, *ssl_sock; - ssl_connect("retrace01.fedoraproject.org", &tcp_sock, &ssl_sock); + ssl_connect("retrace01.fedoraproject.org", &tcp_sock, &ssl_sock, + ssl_allow_insecure); /* Upload the archive. */ struct strbuf *request = strbuf_new(); @@ -286,17 +399,12 @@ int main(int argc, char **argv) /*flags:*/0, PR_INTERVAL_NO_TIMEOUT); if (written == -1) { - char *error = xmalloc(PR_GetErrorTextLength()); - PRInt32 count = PR_GetErrorText(error); PR_Close(ssl_sock); - if (count) - error_msg_and_die("Failed to send HTTP header of length %d: %s", request->len, error); - else - error_msg_and_die("Failed to send HTTP header of length %d: pr_error == %d", - request->len, PR_GetError()); + error_msg_and_die("Failed to send HTTP header of length %d: NSS error %d", + request->len, PR_GetError()); } - xlseek(tempfd, 0, SEEK_SET); + strbuf_free(request); while (1) { @@ -318,14 +426,146 @@ int main(int argc, char **argv) if (written == -1) { PR_Close(ssl_sock); - error_msg_and_die("Failed to send data."); + error_msg_and_die("Failed to send data: NSS error %d", PR_GetError()); } } - //PRInt32 received = PR_Recv(tcp_sock, buf, amount, /*flags:*/0, - // PR_INTERVAL_NO_TIMEOUT); + close(tempfd); + + char buf[32768]; + PRInt32 received = 0; + do { + received = PR_Recv(tcp_sock, buf, sizeof(buf) - 1, /*flags:*/0, + PR_INTERVAL_NO_TIMEOUT); + if (received > 0) + write(STDOUT_FILENO, buf, received); + + if (received == -1) + error_msg_and_die("Receiving of data failed: NSS error %d", PR_GetError()); + + } while (received > 0); ssl_disconnect(ssl_sock); - close(tempfd); + return 0; +} + +static int run_status(const char *taskid, + const char *password, + bool ssl_allow_insecure) +{ + PRFileDesc *tcp_sock, *ssl_sock; + ssl_connect("retrace01.fedoraproject.org", &tcp_sock, &ssl_sock, + ssl_allow_insecure); + + struct strbuf *request = strbuf_new(); + strbuf_append_strf(request, + "POST /%s/status HTTP/1.1\r\n" + "Host: retrace01.fedoraproject.org\r\n" + "X-Task-Password: %s\r\n" + "Content-Length: 0\r\n" + "Connection: close\r\n" + "\r\n", taskid, password); + + PRInt32 written = PR_Send(tcp_sock, request->buf, request->len, + /*flags:*/0, PR_INTERVAL_NO_TIMEOUT); + if (written == -1) + { + PR_Close(ssl_sock); + error_msg_and_die("Failed to send HTTP header of length %d: NSS error %d", + request->len, PR_GetError()); + } + + strbuf_free(request); + + char buf[32768]; + PRInt32 received = 0; + do { + received = PR_Recv(tcp_sock, buf, sizeof(buf) - 1, /*flags:*/0, + PR_INTERVAL_NO_TIMEOUT); + if (received > 0) + write(STDOUT_FILENO, buf, received); + + if (received == -1) + error_msg_and_die("Receiving of data failed: NSS error %d", PR_GetError()); + + } while (received > 0); + + ssl_disconnect(ssl_sock); + return 0; +} + +static int run_backtrace(const char *taskid, + const char *password, + bool ssl_allow_insecure) +{ + return 0; +} + +static int run_log(const char *taskid, + const char *password, + bool ssl_allow_insecure) +{ + return 0; +} + +int main(int argc, char **argv) +{ + char *env_verbose = getenv("ABRT_VERBOSE"); + if (env_verbose) + g_verbose = atoi(env_verbose); + + unsigned opts = parse_opts(argc, argv, + abrt_retrace_client_options, + abrt_retrace_client_usage); + + if (opts & OPT_syslog) + { + openlog(msg_prefix, 0, LOG_DAEMON); + logmode = LOGMODE_SYSLOG; + } + + const char *operation = NULL; + if (optind < argc) + operation = argv[optind]; + else + { + parse_usage_and_die(abrt_retrace_client_usage, + abrt_retrace_client_options); + } + + if (0 == strcasecmp(operation, "create")) + { + if (!dump_dir_name && !coredump) + error_msg_and_die("Either dump directory or coredump is needed."); + return run_create(dump_dir_name, coredump, opts & OPT_wait, + opts & OPT_bttodir, opts & OPT_insecure); + } + else if (0 == strcasecmp(operation, "status")) + { + if (!task_id) + error_msg_and_die("Task id is needed."); + if (!task_password) + error_msg_and_die("Task password is needed."); + return run_status(task_id, task_password, opts & OPT_insecure); + } + else if (0 == strcasecmp(operation, "backtrace")) + { + if (!task_id) + error_msg_and_die("Task id is needed."); + if (!task_password) + error_msg_and_die("Task password is needed."); + return run_backtrace(task_id, task_password, opts & OPT_insecure); + } + else if (0 == strcasecmp(operation, "log")) + { + if (!task_id) + error_msg_and_die("Task id is needed."); + if (!task_password) + error_msg_and_die("Task password is needed."); + return run_log(task_id, task_password, opts & OPT_insecure); + } + else + error_msg_and_die("Unknown operation: %s", operation); + return 0; } |
