/* * Copyright (C) 2014 - David Goulet * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License, version 2 only, as * published by the Free Software Foundation. * * 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, write to the Free Software Foundation, Inc., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define zmalloc(x) calloc(1, x) GCRY_THREAD_OPTION_PTHREAD_IMPL; /* Global OTR user state. */ static OtrlUserState user_state; /* Getopt options. */ static struct option long_opts[] = { { "load-instag", 1, NULL, 'i' }, { "load-key", 1, NULL, 'k' }, { "load-fp", 1, NULL, 'f' }, { "timeout", 1, NULL, 't' }, { "max-msg", 1, NULL, 'm' }, { "disconnect", 0, NULL, 'd' }, { "auth", 0, NULL, 'a' }, { "fragment", 0, NULL, 'F' }, /* Closure. */ { NULL, 0, NULL, 0 } }; static char *opt_instag_path; static char *opt_key_path; static char *opt_key_fp_path; static unsigned int opt_max_num_msg; static int opt_disconnect; static int opt_auth; static int opt_fragment; /* Currently, the message size sent is between 1 and 600 len so 100 is a good * middle ground. */ static const int fragment_size = 100; /* By default, don't send frag. */ static OtrlFragmentPolicy fragPolicy = OTRL_FRAGMENT_SEND_SKIP; static const char *protocol = "otr-test"; static const char *alice_name = "alice"; static const char *bob_name = "bob"; static const char *unix_sock_bob_path = "/tmp/otr-test-bob.sock"; static const char *unix_sock_alice_path = "/tmp/otr-test-alice.sock"; static const char *auth_question = "What is NSA?"; static const char *auth_secret = "No Sugar Added"; /* Alice and Bob thread's socket. */ static int alice_sock; static int bob_sock; /* Declare it global because we use it multiple times. */ static struct sockaddr_un alice_sun; static struct sockaddr_un bob_sun; static int timeout_max = 1000; static unsigned int num_recv_msg; static unsigned int session_disconnected; static int quit_pipe[2] = { -1, -1 }; /* * For now not really do much more but if we want to use the OK condition at * some point to do something else, that will ease our life :). */ #define OK(cond, fmt, args...) \ do { \ ok(cond, fmt, ## args); \ } while (0) /* * Used to pass OTR message between threads. This contains the cipher and * plaintext so we are able to validate what's expected in both threads. */ struct otr_msg { size_t plaintext_len; size_t ciphertext_len; char *plaintext; char *ciphertext; }; struct otr_info { const char *user; int sock; unsigned int gone_secure; unsigned int auth_done; }; /* Stub */ static int send_otr_msg(int sock, const char *to, const char *from, struct otr_info *oinfo, const char *message); static OtrlPolicy ops_policy(void *opdata, ConnContext *context) { return OTRL_POLICY_DEFAULT; } static void ops_inject_msg(void *opdata, const char *accountname, const char *protocol, const char *recipient, const char *message) { ssize_t ret; struct otr_info *oinfo = opdata; struct otr_msg *msg; msg = zmalloc(sizeof(*msg)); if (!msg) { perror("zmalloc inject"); return; } msg->ciphertext = strdup(message); msg->ciphertext_len = strlen(message); ret = send(oinfo->sock, &msg, sizeof(msg), 0); if (ret < 0) { perror("send msg"); } } static void ops_gone_secure(void *opdata, ConnContext *context) { struct otr_info *oinfo = opdata; session_disconnected = 0; oinfo->gone_secure = 1; /* XXX: gone_insecure is never called ref bug #40 so this will always be * true. */ OK(oinfo->gone_secure, "Gone secure for %s", oinfo->user); } static void ops_gone_insecure(void *opdata, ConnContext *context) { struct otr_info *oinfo = opdata; OK(oinfo->gone_secure, "Gone insecure for %s", oinfo->user); oinfo->gone_secure = 0; } static int ops_max_message_size(void *opdata, ConnContext *context) { if (opt_fragment) { return fragment_size; } return 0; } static const char *ops_otr_error_message(void *opdata, ConnContext *context, OtrlErrorCode code) { char *msg = NULL; switch (code) { case OTRL_ERRCODE_NONE: break; case OTRL_ERRCODE_ENCRYPTION_ERROR: msg = strdup("OTRL_ERRCODE_ENCRYPTION_ERROR"); break; case OTRL_ERRCODE_MSG_NOT_IN_PRIVATE: msg = strdup("OTRL_ERRCODE_MSG_NOT_IN_PRIVATE"); break; case OTRL_ERRCODE_MSG_UNREADABLE: msg = strdup("OTRL_ERRCODE_MSG_UNREADABLE"); break; case OTRL_ERRCODE_MSG_MALFORMED: msg = strdup("OTRL_ERRCODE_MSG_MALFORMED"); break; } return msg; } static void ops_otr_error_message_free(void *opdata, const char *err_msg) { free((char *) err_msg); } static void ops_handle_msg_event(void *opdata, OtrlMessageEvent msg_event, ConnContext *context, const char *message, gcry_error_t err) { //char* msg = ""; struct otr_info *oinfo = opdata; switch(msg_event) { case OTRL_MSGEVENT_NONE: //msg = "OTRL_MSGEVENT_NONE"; break; case OTRL_MSGEVENT_ENCRYPTION_REQUIRED: //msg = "OTRL_MSGEVENT_ENCRYPTION_REQUIRED"; break; case OTRL_MSGEVENT_ENCRYPTION_ERROR: //msg = "OTRL_MSGEVENT_ENCRYPTION_ERROR"; break; case OTRL_MSGEVENT_CONNECTION_ENDED: //msg = "OTRL_MSGEVENT_CONNECTION_ENDED"; oinfo->gone_secure = 0; break; case OTRL_MSGEVENT_SETUP_ERROR: //msg = "OTRL_MSGEVENT_SETUP_ERROR"; break; case OTRL_MSGEVENT_MSG_REFLECTED: //msg = "OTRL_MSGEVENT_MSG_REFLECTED"; break; case OTRL_MSGEVENT_MSG_RESENT: //msg = "OTRL_MSGEVENT_MSG_RESENT"; break; case OTRL_MSGEVENT_RCVDMSG_NOT_IN_PRIVATE: //msg = "OTRL_MSGEVENT_RCVDMSG_NOT_IN_PRIVATE"; break; case OTRL_MSGEVENT_RCVDMSG_UNREADABLE: //msg = "OTRL_MSGEVENT_RCVDMSG_UNREADABLE"; break; case OTRL_MSGEVENT_RCVDMSG_MALFORMED: //msg = "OTRL_MSGEVENT_RCVDMSG_MALFORMED"; break; case OTRL_MSGEVENT_LOG_HEARTBEAT_RCVD: //msg = "OTRL_MSGEVENT_LOG_HEARTBEAT_RCVD"; break; case OTRL_MSGEVENT_LOG_HEARTBEAT_SENT: //msg = "OTRL_MSGEVENT_LOG_HEARTBEAT_SENT"; break; case OTRL_MSGEVENT_RCVDMSG_GENERAL_ERR: //msg = "OTRL_MSGEVENT_RCVDMSG_GENERAL_ERR"; break; case OTRL_MSGEVENT_RCVDMSG_UNENCRYPTED: //msg = "OTRL_MSGEVENT_RCVDMSG_UNENCRYPTED"; break; case OTRL_MSGEVENT_RCVDMSG_UNRECOGNIZED: //msg = "OTRL_MSGEVENT_RCVDMSG_UNRECOGNIZED"; break; case OTRL_MSGEVENT_RCVDMSG_FOR_OTHER_INSTANCE: //msg = "OTRL_MSGEVENT_RCVDMSG_FOR_OTHER_INSTANCE"; break; default: //msg = "Unknown OTRL message event"; break; } } static void ops_create_instag(void *opdata, const char *accountname, const char *protocol) { otrl_instag_generate(user_state, "/dev/null", accountname, protocol); } static int ops_is_logged_in(void *opdata, const char *accountname, const char *protocol, const char *recipient) { /* Always logged in or else we don't receive a disconnected TLV. */ return 1; } static void ops_create_privkey(void *opdata, const char *accountname, const char *protocol) { /* XXX: We should gen. our own key each time at some point? */ return; } static void ops_update_context_list(void *opdata) { return; } static void ops_new_fingerprint(void *opdata, OtrlUserState us, const char *accountname, const char *protocol, const char *username, unsigned char fingerprint[20]) { return; } static void ops_write_fingerprints(void *opdata) { return; } static void ops_still_secure(void *opdata, ConnContext *context, int is_reply) { struct otr_info *oinfo = opdata; OK(oinfo->gone_secure, "OP still secure"); } static void ops_received_symkey(void *opdata, ConnContext *context, unsigned int use, const unsigned char *usedata, size_t usedatalen, const unsigned char *symkey) { return; } static const char *ops_resent_msg_prefix(void *opdata, ConnContext *context) { /* Just so we can test resent_msg_prefix_free */ char *prefix = zmalloc(32); strncpy(prefix, "[such resent]", 32); return prefix; } static void ops_resent_msg_prefix_free(void *opdata, const char *prefix) { free((char *) prefix); } static void ops_convert_msg(void *opdata, ConnContext *context, OtrlConvertType convert_type, char ** dest, const char *src) { switch (convert_type) { case OTRL_CONVERT_SENDING: case OTRL_CONVERT_RECEIVING: break; default: OK(0, "OP convert_msg, got a unknown type %d", convert_type); break; } *dest = NULL; } static void ops_convert_free(void *opdata, ConnContext *context, char *dest) { return; } /* Stub */ static void ops_handle_smp_event(void *opdata, OtrlSMPEvent smp_event, ConnContext *context, unsigned short progress_percent, char *question); static void ops_timer_control(void *opdata, unsigned int interval); /* OTR message operations. */ static OtrlMessageAppOps ops = { ops_policy, ops_create_privkey, ops_is_logged_in, ops_inject_msg, ops_update_context_list, ops_new_fingerprint, ops_write_fingerprints, ops_gone_secure, ops_gone_insecure, ops_still_secure, ops_max_message_size, NULL, /* account_name - NOT USED */ NULL, /* account_name_free - NOT USED */ ops_received_symkey, ops_otr_error_message, ops_otr_error_message_free, ops_resent_msg_prefix, ops_resent_msg_prefix_free, ops_handle_smp_event, ops_handle_msg_event, ops_create_instag, ops_convert_msg, ops_convert_free, ops_timer_control, }; static void ops_timer_control(void *opdata, unsigned int interval) { otrl_message_poll(user_state, &ops, NULL); } static void ops_handle_smp_event(void *opdata, OtrlSMPEvent smp_event, ConnContext *context, unsigned short progress_percent, char *question) { struct otr_info *oinfo = opdata; switch (smp_event) { case OTRL_SMPEVENT_ASK_FOR_SECRET: OK(!oinfo->auth_done && !strncmp(oinfo->user, alice_name, strlen(alice_name)), "SMP Event, %s asking for secret", alice_name); break; case OTRL_SMPEVENT_ASK_FOR_ANSWER: OK(!oinfo->auth_done && !strncmp(oinfo->user, bob_name, strlen(bob_name)) && !strncmp(auth_question, question, strlen(auth_question)), "SMP Event, %s asking for answer", bob_name); /* * Directly respond to the SMP auth here. Much more easy instead of in * bob's thread. */ otrl_message_respond_smp(user_state, &ops, opdata, context, (unsigned char *) auth_secret, strlen(auth_secret)); break; case OTRL_SMPEVENT_IN_PROGRESS: OK(!oinfo->auth_done && !strncmp(oinfo->user, alice_name, strlen(alice_name)), "SMP Event, %s asking for secret", alice_name); break; case OTRL_SMPEVENT_SUCCESS: oinfo->auth_done = 1; OK(oinfo->auth_done, "SMP authentication success for %s", oinfo->user); break; case OTRL_SMPEVENT_ABORT: case OTRL_SMPEVENT_FAILURE: case OTRL_SMPEVENT_CHEATED: case OTRL_SMPEVENT_ERROR: default: OK(0, "SMP auth failed with event %d", smp_event); break; } } static void cleanup(void) { ssize_t ret; /* Wake up threads. */ ret = write(quit_pipe[1], "42", 2); if (ret < 0) { perror("write quit pipe"); } /* Cleanup residual Unix socket path. */ unlink(alice_sun.sun_path); unlink(bob_sun.sun_path); } static void update_msg_counter(void) { num_recv_msg++; if (num_recv_msg == opt_max_num_msg) { cleanup(); } } /* * Generate random string and stores it in out of size len. */ static void gen_random_string(char *out, size_t len) { size_t i; static const char alphanum[] = "0123456789" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz"; for (i = 0; i < len; i++) { out[i] = alphanum[rand() % (sizeof(alphanum) - 1)]; } out[len - 1] = '\0'; } static int send_otr_msg(int sock, const char *to, const char *from, struct otr_info *oinfo, const char *message) { char *new_msg = NULL; ssize_t ret; gcry_error_t err; struct otr_msg *omsg; omsg = zmalloc(sizeof(*omsg)); if (!omsg) { perror("zmalloc send otr msg"); goto error; } if (!message) { size_t len = rand() % 600; char *msg = zmalloc(len); if (!msg) { perror("random msg"); goto error; } gen_random_string(msg, len); omsg->plaintext = msg; omsg->plaintext_len = strlen(msg); } else { omsg->plaintext = strdup(message); omsg->plaintext_len = strlen(message); } err = otrl_message_sending(user_state, &ops, oinfo, from, protocol, to, OTRL_INSTAG_BEST, omsg->plaintext, NULL, &new_msg, fragPolicy, NULL, NULL, NULL); if (err) { goto error; } if (new_msg) { free(omsg->ciphertext); omsg->ciphertext = strdup(new_msg); omsg->ciphertext_len = strlen(omsg->ciphertext); otrl_message_free(new_msg); } ret = send(sock, &omsg, sizeof(omsg), 0); if (ret < 0) { perror("send OTR msg"); goto error; } return 0; error: if(omsg){ free(omsg->plaintext); free(omsg->ciphertext); free(omsg); } return -1; } static int recv_otr_msg(int sock, const char *to, const char *from, struct otr_info *oinfo) { int err; ssize_t ret; char *new_msg = NULL; struct otr_msg *omsg; OtrlTLV *tlvs = NULL; ret = recv(sock, &omsg, sizeof(omsg), 0); if (ret < 0) { goto error; } err = otrl_message_receiving(user_state, &ops, oinfo, to, protocol, from, omsg->ciphertext, &new_msg, &tlvs, NULL, NULL, NULL); if (!err) { if (new_msg) { OK(strncmp(omsg->plaintext, new_msg, omsg->plaintext_len) == 0, "Message exchanged is valid"); update_msg_counter(); } } else { OK(err == 1, "Internal OTR message valid"); } free(omsg->plaintext); free(omsg->ciphertext); free(omsg); OtrlTLV *tlv = otrl_tlv_find(tlvs, OTRL_TLV_DISCONNECTED); /* * XXX: Somehow you can end up with a disconnected TLV in a gone secure * session (see #54). This is probably a bug but since the gone_insecure is * never called (see bug #48) we have no reliable way of knowing the state * of the session at this point. */ if (tlv && !oinfo->gone_secure) { OK(session_disconnected, "Disconnected TLV confirmed"); } otrl_tlv_free(tlvs); return 0; error: return -1; } static int add_sock_to_pollset(int epfd, int sock, uint32_t req_ev) { int ret; struct epoll_event ev; memset(&ev, 0, sizeof(ev)); ev.events = req_ev; ev.data.fd = sock; ret = epoll_ctl(epfd, EPOLL_CTL_ADD, sock, &ev); if (ret < 0) { perror("epoll_ctl add"); } return ret; } static void *alice_thread(void *data) { int sock_to_bob, sock_from_bob = 0, epfd, ret; unsigned int auth_started = 0; struct otr_info oinfo; memset(&oinfo, 0, sizeof(oinfo)); /* Poll size is ignored since 2.6.8 */ epfd = epoll_create(42); if (epfd < 0) { perror("epoll_create Bob"); goto error; } sock_to_bob = socket(PF_UNIX, SOCK_STREAM, 0); if (sock_to_bob < 0) { perror("Bob socket to Alice"); goto sock_error; } oinfo.sock = sock_to_bob; oinfo.user = alice_name; if (!opt_auth) { /* We are not going to SMP auth for this session so indicate it's * completed so we can go forward with random disconnect. */ oinfo.auth_done = 1; } ret = connect(sock_to_bob, (struct sockaddr *) &bob_sun, sizeof(bob_sun)); if (ret < 0) { perror("connect to Alice"); goto end; } /* Add quit pipe to pollset trigger by a cleanup. */ ret = add_sock_to_pollset(epfd, quit_pipe[0], EPOLLIN); if (ret < 0) { goto end; } /* Add our socket to epoll set. */ ret = add_sock_to_pollset(epfd, alice_sock, EPOLLIN | EPOLLRDHUP); if (ret < 0) { goto end; } while (1) { int i, nb_fd, timeout; struct epoll_event ev[3]; memset(ev, 0, sizeof(ev)); /* * Set random timeout and when we do timeout, use that to send message * to Alice. */ timeout = (rand() % (timeout_max - 1)); ret = epoll_wait(epfd, ev, sizeof(ev), timeout); if (ret < 0) { perror("epoll_wait Alice"); goto end; } nb_fd = ret; /* Each timeout to 10 finishes the OTR session. */ if (!(timeout % 3) && opt_disconnect && oinfo.auth_done) { session_disconnected = 1; oinfo.gone_secure = 0; otrl_message_disconnect(user_state, &ops, &oinfo, alice_name, protocol, bob_name, OTRL_INSTAG_BEST); OK(!oinfo.gone_secure, "OTR message disconnect"); } /* Start authentication with Bob. */ if (opt_auth && !auth_started && oinfo.gone_secure) { ConnContext *ctx; /* We have to find our context before auth. */ ctx = otrl_context_find(user_state, bob_name, alice_name, protocol, OTRL_INSTAG_BEST, 0, NULL, NULL, &oinfo); OK(ctx, "Alice context found for SMP auth"); otrl_message_initiate_smp_q(user_state, &ops, &oinfo, ctx, auth_question, (unsigned char *) auth_secret, strlen(auth_secret)); auth_started = 1; } /* No event thus timeout, send message to Alice. */ if (nb_fd == 0) { (void) send_otr_msg(sock_to_bob, bob_name, alice_name, &oinfo, NULL); continue; } for (i = 0; i < nb_fd; i++) { int fd; uint32_t event; fd = ev[i].data.fd; event = ev[i].events; if (fd == quit_pipe[0]) { /* Time to leave. */ goto end; } else if (fd == alice_sock) { if (event & (EPOLLERR | EPOLLHUP | EPOLLRDHUP)) { goto end; } else if (event & EPOLLIN) { socklen_t len; struct sockaddr_un sun; /* Connection from Bob, accept it so we can handle it. */ sock_from_bob = accept(fd, (struct sockaddr *) &sun, &len); ret = add_sock_to_pollset(epfd, sock_from_bob, EPOLLIN | EPOLLERR | EPOLLHUP); if (ret < 0) { goto end; } } continue; } else if (fd == sock_from_bob) { if (event & (EPOLLERR | EPOLLHUP)) { /* Stop since Bob's thread just shut us down. */ goto end; } else if (event & EPOLLIN) { (void) recv_otr_msg(sock_from_bob, alice_name, bob_name, &oinfo); } continue; } else { goto end; } } } end: if (sock_from_bob) { (void) close(sock_from_bob); } (void) close(sock_to_bob); sock_error: (void) close(epfd); error: (void) close(alice_sock); return NULL; } static void *bob_thread(void *data) { int sock_to_alice, sock_from_alice = 0, epfd, ret; struct otr_info oinfo; memset(&oinfo, 0, sizeof(oinfo)); /* Poll size is ignored since 2.6.8 */ epfd = epoll_create(42); if (epfd < 0) { perror("epoll_create Bob"); goto error; } sock_to_alice = socket(PF_UNIX, SOCK_STREAM, 0); if (sock_to_alice < 0) { perror("Bob socket to Alice"); goto sock_error; } oinfo.sock = sock_to_alice; oinfo.user = bob_name; ret = connect(sock_to_alice, (struct sockaddr *) &alice_sun, sizeof(alice_sun)); if (ret < 0) { perror("connect to Alice"); goto end; } /* Add quit pipe to pollset trigger by a cleanup. */ ret = add_sock_to_pollset(epfd, quit_pipe[0], EPOLLIN); if (ret < 0) { goto end; } /* Add our socket to epoll set. */ ret = add_sock_to_pollset(epfd, bob_sock, EPOLLIN | EPOLLERR | EPOLLHUP | EPOLLRDHUP); if (ret < 0) { goto end; } while (1) { int i, timeout = 500, nb_fd; struct epoll_event ev[3]; memset(ev, 0, sizeof(ev)); /* * Set random timeout and when we do timeout, use that to send message * to Alice. */ timeout = (rand() % (timeout_max - 1)); ret = epoll_wait(epfd, ev, sizeof(ev), timeout); if (ret < 0) { perror("epoll_wait Bob"); goto end; } nb_fd = ret; /* No event thus timeout, send message to Alice. */ if (nb_fd == 0) { (void) send_otr_msg(sock_to_alice, alice_name, bob_name, &oinfo, NULL); continue; } for (i = 0; i < nb_fd; i++) { int fd; uint32_t event; fd = ev[i].data.fd; event = ev[i].events; if (fd == quit_pipe[0]) { /* Time to leave. */ goto end; } else if (fd == bob_sock) { if (event & (EPOLLERR | EPOLLHUP | EPOLLRDHUP)) { goto end; } else if (event & (EPOLLIN | EPOLLHUP)) { socklen_t len; struct sockaddr_un sun; /* Connection from Alice, accept it so we can handle it. */ sock_from_alice = accept(fd, (struct sockaddr *) &sun, &len); ret = add_sock_to_pollset(epfd, sock_from_alice, EPOLLIN | EPOLLERR | EPOLLHUP); if (ret < 0) { goto end; } } continue; } else if (fd == sock_from_alice) { if (event & (EPOLLERR | EPOLLHUP)) { goto end; } else if (event & EPOLLIN) { (void) recv_otr_msg(sock_from_alice, bob_name, alice_name, &oinfo); } continue; } else { goto end; } } } end: if (sock_from_alice) { (void) close(sock_from_alice); } (void) close(sock_to_alice); sock_error: (void) close(epfd); error: (void) close(bob_sock); return NULL; } static void run(void) { int ret; void *status; pthread_t alice_th, bob_th; /* Init quit pipe. */ ret = pipe(quit_pipe); if (ret < 0) { perror("pipe quit pipe"); goto end; } ret = pthread_create(&alice_th, NULL, alice_thread, NULL); if (ret) { fail("pthread_create sender thread failed (errno: %d)", errno); goto end; } ret = pthread_create(&bob_th, NULL, bob_thread, NULL); if (ret) { fail("pthread_create receiver thread failed (errno: %d)", errno); goto exit_receiver; } (void) pthread_join(bob_th, &status); exit_receiver: (void) pthread_join(alice_th, &status); end: /* Get rid of the quit pipe. */ close(quit_pipe[0]); close(quit_pipe[1]); return; } /* * Load OTR instag using the given opt argument. */ static void load_instag(void) { int ret; gcry_error_t err; ret = access(opt_instag_path, R_OK); if (ret < 0) { fail("Instag file %s is not readable", opt_instag_path); return; } err = otrl_instag_read(user_state, opt_instag_path); OK(err == GPG_ERR_NO_ERROR, "Loading instag from given file"); } /* * Load private key file using the given opt argument. */ static void load_key(void) { int ret; gcry_error_t err; ret = access(opt_key_path, R_OK); if (ret < 0) { fail("Key file %s is not readable", opt_key_path); return; } err = otrl_privkey_read(user_state, opt_key_path); OK(err == GPG_ERR_NO_ERROR, "Loading key from given file"); } /* * Load private key fingerprint file using the given opt argument. */ static void load_key_fp(void) { int ret; gcry_error_t err; ret = access(opt_key_fp_path, R_OK); if (ret < 0) { fail("Key fingerprints file %s is not readable", opt_key_fp_path); return; } err = otrl_privkey_read_fingerprints(user_state, opt_key_fp_path, NULL, NULL); OK(err == GPG_ERR_NO_ERROR, "Loading key fingerprints from given file"); } static int create_unix_socket(const char *pathname, struct sockaddr_un *sun) { int sock, ret; /* Create both Unix socket. */ if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { ret = -errno; perror("Unix socket"); goto error; } memset(sun, 0, sizeof(struct sockaddr_un)); sun->sun_family = AF_UNIX; strncpy(sun->sun_path, pathname, sizeof(sun->sun_path)); sun->sun_path[sizeof(sun->sun_path) - 1] = '\0'; ret = bind(sock, (struct sockaddr *) sun, sizeof(struct sockaddr_un)); if (ret < 0) { perror("bind unix sock"); goto error; } ret = listen(sock, 10); if (ret < 0) { perror("listen unix sock"); goto error; } return sock; error: return ret; } /* * Bootstrap client by initializing the OTR library and creating an OTR user * state. * * Return 0 on success else a negative value on error. */ static int init_client(void) { int ret; /* Init libgcrypt threading system. */ gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread); /* Init OTR library. */ OTRL_INIT; OK(1, "OTR library initialization done."); user_state = otrl_userstate_create(); OK(user_state, "OTR userstate creation done."); if (!user_state) { fail("Out of memory on userstate create"); ret = -ENOMEM; goto error; } /* Seed the prng. */ srand(time(NULL)); /* Cleanup Unix socket file before creating them. */ unlink(unix_sock_alice_path); unlink(unix_sock_bob_path); alice_sock = create_unix_socket(unix_sock_alice_path, &alice_sun); bob_sock = create_unix_socket(unix_sock_bob_path, &bob_sun); if (alice_sock < 0 || bob_sock < 0) { ret = -EINVAL; goto error; } if (opt_fragment) { fragPolicy = OTRL_FRAGMENT_SEND_ALL; } return 0; error: return ret; } static void sighandler(int sig) { switch (sig) { case SIGPIPE: case SIGINT: case SIGTERM: cleanup(); break; default: break; } } /* * main entry point. */ int main(int argc, char **argv) { int ret, opt; struct sigaction sa; sigset_t sigset; plan_no_plan(); if ((ret = sigemptyset(&sigset)) < 0) { perror("sigemptyset"); goto error; } sa.sa_handler = sighandler; sa.sa_mask = sigset; sa.sa_flags = 0; if ((ret = sigaction(SIGTERM, &sa, NULL)) < 0) { perror("sigaction"); goto error; } if ((ret = sigaction(SIGINT, &sa, NULL)) < 0) { perror("sigaction"); goto error; } if ((ret = sigaction(SIGPIPE, &sa, NULL)) < 0) { perror("sigaction"); goto error; } while ((opt = getopt_long(argc, argv, "+i:k:f:t:m:daF", long_opts, NULL)) != -1) { switch (opt) { case 'i': opt_instag_path = strdup(optarg); break; case 'k': opt_key_path = strdup(optarg); break; case 'f': opt_key_fp_path = strdup(optarg); break; case 't': timeout_max = atoi(optarg); break; case 'm': opt_max_num_msg = atoi(optarg); break; case 'd': opt_disconnect = 1; break; case 'a': opt_auth = 1; break; case 'F': opt_fragment = 1; break; default: goto error; } } if (!opt_key_path) { fail("No key file, failing"); goto error; } /* Running OTR tests. */ ret = init_client(); if (ret < 0) { goto error; } if (opt_instag_path) { load_instag(); } if (opt_key_fp_path) { load_key_fp(); } load_key(); run(); return exit_status(); error: return -1; }