/* * Off-the-Record Messaging library * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov * * * This library is free software; you can redistribute it and/or * modify it under the terms of version 2.1 of the GNU Lesser General * Public License as published by the Free Software Foundation. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* system headers */ #include #include /* libgcrypt headers */ #include /* libotr headers */ #include "context.h" /* Create a new connection context. */ static ConnContext * new_context(const char * user, const char * accountname, const char * protocol) { ConnContext * context; OtrlSMState *smstate; context = malloc(sizeof(*context)); assert(context != NULL); context->username = _strdup(user); context->accountname = _strdup(accountname); context->protocol = _strdup(protocol); context->fragment = NULL; context->fragment_len = 0; context->fragment_n = 0; context->fragment_k = 0; context->msgstate = OTRL_MSGSTATE_PLAINTEXT; otrl_auth_new(&(context->auth)); smstate = malloc(sizeof(OtrlSMState)); assert(smstate != NULL); otrl_sm_state_new(smstate); context->smstate = smstate; context->fingerprint_root.fingerprint = NULL; context->fingerprint_root.context = context; context->fingerprint_root.next = NULL; context->fingerprint_root.tous = NULL; context->active_fingerprint = NULL; context->their_keyid = 0; context->their_y = NULL; context->their_old_y = NULL; context->our_keyid = 0; context->our_dh_key.groupid = 0; context->our_dh_key.priv = NULL; context->our_dh_key.pub = NULL; context->our_old_dh_key.groupid = 0; context->our_old_dh_key.priv = NULL; context->our_old_dh_key.pub = NULL; otrl_dh_session_blank(&(context->sesskeys[0][0])); otrl_dh_session_blank(&(context->sesskeys[0][1])); otrl_dh_session_blank(&(context->sesskeys[1][0])); otrl_dh_session_blank(&(context->sesskeys[1][1])); memset(context->sessionid, 0, 20); context->sessionid_len = 0; context->protocol_version = 0; context->numsavedkeys = 0; context->preshared_secret = NULL; context->preshared_secret_len = 0; context->saved_mac_keys = NULL; context->generation = 0; context->lastsent = 0; context->lastmessage = NULL; context->may_retransmit = 0; context->otr_offer = OFFER_NOT; context->app_data = NULL; context->app_data_free = NULL; context->next = NULL; return context; } ConnContext * otrl_context_new(const char * user, const char * accountname, const char * protocol) { return new_context(user, accountname, protocol); } /* Look up a connection context by name/account/protocol from the given * OtrlUserState. If add_if_missing is true, allocate and return a new * context if one does not currently exist. In that event, call * add_app_data(data, context) so that app_data and app_data_free can be * filled in by the application, and set *addedp to 1. */ ConnContext * otrl_context_find(OtrlUserState us, const char *user, const char *accountname, const char *protocol, int add_if_missing, int *addedp, void (*add_app_data)(void *data, ConnContext *context), void *data) { ConnContext ** curp; int usercmp = 1, acctcmp = 1, protocmp = 1; if (addedp) *addedp = 0; if (!user || !accountname || !protocol) return NULL; for (curp = &(us->context_root); *curp; curp = &((*curp)->next)) { if ((usercmp = strcmp((*curp)->username, user)) > 0 || (usercmp == 0 && (acctcmp = strcmp((*curp)->accountname, accountname)) > 0) || (usercmp == 0 && acctcmp == 0 && (protocmp = strcmp((*curp)->protocol, protocol)) >= 0)) /* We're at the right place in the list. We've either found * it, or gone too far. */ break; } if (usercmp == 0 && acctcmp == 0 && protocmp == 0) { /* Found it! */ return *curp; } if (add_if_missing) { ConnContext *newctx; if (addedp) *addedp = 1; newctx = new_context(user, accountname, protocol); newctx->next = *curp; if (*curp) { (*curp)->tous = &(newctx->next); } *curp = newctx; newctx->tous = curp; if (add_app_data) { add_app_data(data, *curp); } return *curp; } return NULL; } /* Find a fingerprint in a given context, perhaps adding it if not * present. */ Fingerprint *otrl_context_find_fingerprint(ConnContext *context, unsigned char fingerprint[20], int add_if_missing, int *addedp) { Fingerprint *f = context->fingerprint_root.next; if (addedp) *addedp = 0; while(f) { if (!memcmp(f->fingerprint, fingerprint, 20)) return f; f = f->next; } /* Didn't find it. */ if (add_if_missing) { if (addedp) *addedp = 1; f = malloc(sizeof(*f)); assert(f != NULL); f->fingerprint = malloc(20); assert(f->fingerprint != NULL); memmove(f->fingerprint, fingerprint, 20); f->context = context; f->trust = NULL; f->next = context->fingerprint_root.next; if (f->next) { f->next->tous = &(f->next); } context->fingerprint_root.next = f; f->tous = &(context->fingerprint_root.next); return f; } return NULL; } /* Set the trust level for a given fingerprint */ void otrl_context_set_trust(Fingerprint *fprint, const char *trust) { if (fprint == NULL) return; free(fprint->trust); fprint->trust = trust ? _strdup(trust) : NULL; } /* Set the preshared secret for a given fingerprint. Note that this * currently only stores the secret in the ConnContext structure, but * doesn't yet do anything with it. */ void otrl_context_set_preshared_secret(ConnContext *context, const unsigned char *secret, size_t secret_len) { free(context->preshared_secret); context->preshared_secret = NULL; context->preshared_secret_len = 0; if (secret_len) { context->preshared_secret = malloc(secret_len); if (context->preshared_secret) { memmove(context->preshared_secret, secret, secret_len); context->preshared_secret_len = secret_len; } } } /* Force a context into the OTRL_MSGSTATE_FINISHED state. */ void otrl_context_force_finished(ConnContext *context) { context->msgstate = OTRL_MSGSTATE_FINISHED; otrl_auth_clear(&(context->auth)); free(context->fragment); context->fragment = NULL; context->fragment_len = 0; context->fragment_n = 0; context->fragment_k = 0; context->active_fingerprint = NULL; context->their_keyid = 0; gcry_mpi_release(context->their_y); context->their_y = NULL; gcry_mpi_release(context->their_old_y); context->their_old_y = NULL; context->our_keyid = 0; otrl_dh_keypair_free(&(context->our_dh_key)); otrl_dh_keypair_free(&(context->our_old_dh_key)); otrl_dh_session_free(&(context->sesskeys[0][0])); otrl_dh_session_free(&(context->sesskeys[0][1])); otrl_dh_session_free(&(context->sesskeys[1][0])); otrl_dh_session_free(&(context->sesskeys[1][1])); memset(context->sessionid, 0, 20); context->sessionid_len = 0; free(context->preshared_secret); context->preshared_secret = NULL; context->preshared_secret_len = 0; context->protocol_version = 0; context->numsavedkeys = 0; free(context->saved_mac_keys); context->saved_mac_keys = NULL; gcry_free(context->lastmessage); context->lastmessage = NULL; context->may_retransmit = 0; otrl_sm_state_free(context->smstate); } /* Force a context into the OTRL_MSGSTATE_PLAINTEXT state. */ void otrl_context_force_plaintext(ConnContext *context) { /* First clean up everything we'd need to do for the FINISHED state */ otrl_context_force_finished(context); /* And just set the state properly */ context->msgstate = OTRL_MSGSTATE_PLAINTEXT; } /* Forget a fingerprint (so long as it's not the active one. If it's a * fingerprint_root, forget the whole context (as long as * and_maybe_context is set, and it's PLAINTEXT). Also, if it's not * the fingerprint_root, but it's the only fingerprint, and we're * PLAINTEXT, forget the whole context if and_maybe_context is set. */ void otrl_context_forget_fingerprint(Fingerprint *fprint, int and_maybe_context) { ConnContext *context = fprint->context; if (fprint == &(context->fingerprint_root)) { if (context->msgstate == OTRL_MSGSTATE_PLAINTEXT && and_maybe_context) { otrl_context_forget(context); } } else { if (context->msgstate != OTRL_MSGSTATE_PLAINTEXT || context->active_fingerprint != fprint) { free(fprint->fingerprint); free(fprint->trust); *(fprint->tous) = fprint->next; if (fprint->next) { fprint->next->tous = fprint->tous; } free(fprint); if (context->msgstate == OTRL_MSGSTATE_PLAINTEXT && context->fingerprint_root.next == NULL && and_maybe_context) { /* We just deleted the only fingerprint. Forget the * whole thing. */ otrl_context_forget(context); } } } } /* Forget a whole context, so long as it's PLAINTEXT. */ void otrl_context_forget(ConnContext *context) { if (context->msgstate != OTRL_MSGSTATE_PLAINTEXT) return; /* Just to be safe, force to plaintext. This also frees any * extraneous data lying around. */ otrl_context_force_plaintext(context); /* First free all the Fingerprints */ while(context->fingerprint_root.next) { otrl_context_forget_fingerprint(context->fingerprint_root.next, 0); } /* Now free all the dynamic info here */ free(context->username); free(context->accountname); free(context->protocol); free(context->smstate); context->username = NULL; context->accountname = NULL; context->protocol = NULL; context->smstate = NULL; /* Free the application data, if it exists */ if (context->app_data && context->app_data_free) { (context->app_data_free)(context->app_data); context->app_data = NULL; } /* Fix the list linkages */ *(context->tous) = context->next; if (context->next) { context->next->tous = context->tous; } free(context); } /* Forget all the contexts in a given OtrlUserState. */ void otrl_context_forget_all(OtrlUserState us) { while (us->context_root) { otrl_context_force_plaintext(us->context_root); otrl_context_forget(us->context_root); } }