/* * Off-the-Record Messaging library * Copyright (C) 2004-2009 Ian Goldberg, Chris Alexander, Willy Lew, * 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 #include #include #include #include #include /* libgcrypt headers */ #include /* libotr headers */ #include "context_priv.h" // cleanup routines for persistent file transfer data // FIXME: make sure file transfers are gracefully shut down before use it void otrl_ft_free(ConnContextPriv *context_priv, bool release) { //fprintf(stderr, "otrl_ft_free(%d) for %u -> %u called:\n", (int)release, context_priv->our_keyid, context_priv->their_keyid); int i; if(context_priv->file_transfers_recv) { for(i = 1; i < OTR_MAX_FT; i++) { if(release) if(context_priv->file_transfers_recv[i] != -1) { //fprintf(stderr, "\tclosing descriptor %d for id %d\n", context_priv->file_transfers_recv[i], i); close(context_priv->file_transfers_recv[i]); } // it never heart to be paranoid context_priv->file_transfers_recv[i] = -1; } } if(context_priv->file_transfers_send) { for(i = 1; i < OTR_MAX_FT; i++) { if(release) if(context_priv->file_transfers_send[i] != NULL && context_priv->ft_send_len != NULL) { //fprintf(stderr, "\tunmapping region for id %d\n", i); munmap(context_priv->file_transfers_send[i], context_priv->ft_send_len[i]); } context_priv->file_transfers_send[i] = NULL; if(context_priv->ft_send_len) context_priv->ft_send_len[i] = 0; if(context_priv->ft_send_offset) context_priv->ft_send_offset[i] = 0; } } if(release) { //fprintf(stderr, "\treleasing ft_send_len...\n"); if(context_priv->ft_send_len) free(context_priv->ft_send_len); //else fprintf(stderr, "NULL already\n"); //fprintf(stderr, "\treleasing ft_send_offset...\n"); if(context_priv->ft_send_offset) free(context_priv->ft_send_offset); //else fprintf(stderr, "NULL already\n"); //fprintf(stderr, "\treleasing file_transfers_recv...\n"); if(context_priv->file_transfers_recv) free(context_priv->file_transfers_recv); //else fprintf(stderr, "NULL already\n"); //fprintf(stderr, "\treleasing file_transfers_send...\n"); if(context_priv->file_transfers_send) free(context_priv->file_transfers_send); //else fprintf(stderr, "NULL already\n"); // complete parnoia... context_priv->ft_send_len = NULL; context_priv->ft_send_offset = NULL; context_priv->file_transfers_recv = NULL; context_priv->file_transfers_send = NULL; } //fprintf(stderr, "otrl_ft_free(%d) completed\n", (int)release); } /* Create a new private connection context */ ConnContextPriv *context_priv_new() { ConnContextPriv *context_priv; context_priv = malloc(sizeof(*context_priv)); assert(context_priv != NULL); context_priv->fragment = NULL; context_priv->fragment_len = 0; context_priv->fragment_n = 0; context_priv->fragment_k = 0; context_priv->numsavedkeys = 0; context_priv->saved_mac_keys = NULL; context_priv->generation = 0; context_priv->lastsent = 0; context_priv->lastmessage = NULL; context_priv->may_retransmit = 0; context_priv->their_keyid = 0; context_priv->their_y = NULL; context_priv->their_old_y = NULL; context_priv->our_keyid = 0; context_priv->our_dh_key.groupid = 0; context_priv->our_dh_key.priv = NULL; context_priv->our_dh_key.pub = NULL; context_priv->our_old_dh_key.groupid = 0; context_priv->our_old_dh_key.priv = NULL; context_priv->our_old_dh_key.pub = NULL; otrl_dh_session_blank(&(context_priv->sesskeys[0][0])); otrl_dh_session_blank(&(context_priv->sesskeys[0][1])); otrl_dh_session_blank(&(context_priv->sesskeys[1][0])); otrl_dh_session_blank(&(context_priv->sesskeys[1][1])); context_priv->file_transfers_recv = malloc(OTR_MAX_FT * sizeof(int)); context_priv->file_transfers_send = malloc(OTR_MAX_FT * sizeof(char *)); context_priv->ft_send_len = calloc(OTR_MAX_FT, sizeof(size_t)); context_priv->ft_send_offset = calloc(OTR_MAX_FT, sizeof(size_t)); if(!context_priv->file_transfers_recv || !context_priv->file_transfers_send) return NULL; otrl_ft_free(context_priv, false); return context_priv; } /* Resets the appropriate variables when a context is being force finished */ void context_priv_force_finished(ConnContextPriv *context_priv) { free(context_priv->fragment); context_priv->fragment = NULL; context_priv->fragment_len = 0; context_priv->fragment_n = 0; context_priv->fragment_k = 0; context_priv->numsavedkeys = 0; free(context_priv->saved_mac_keys); context_priv->saved_mac_keys = NULL; gcry_free(context_priv->lastmessage); context_priv->lastmessage = NULL; context_priv->may_retransmit = 0; context_priv->their_keyid = 0; gcry_mpi_release(context_priv->their_y); context_priv->their_y = NULL; gcry_mpi_release(context_priv->their_old_y); context_priv->their_old_y = NULL; context_priv->our_keyid = 0; otrl_dh_keypair_free(&(context_priv->our_dh_key)); otrl_dh_keypair_free(&(context_priv->our_old_dh_key)); otrl_dh_session_free(&(context_priv->sesskeys[0][0])); otrl_dh_session_free(&(context_priv->sesskeys[0][1])); otrl_dh_session_free(&(context_priv->sesskeys[1][0])); otrl_dh_session_free(&(context_priv->sesskeys[1][1])); otrl_ft_free(context_priv, true); } bool context_priv_ft_recv_id(const ConnContextPriv * context_priv, unsigned char id) { if(!context_priv->file_transfers_recv) return false; if(context_priv->file_transfers_recv[id] != -1) return true; return false; } bool context_priv_ft_send_id(const ConnContextPriv * context_priv, unsigned char id) { if(!context_priv->file_transfers_send) return false; if(context_priv->file_transfers_send[id] != NULL) return true; return false; } char * context_priv_ft_chunk(ConnContextPriv * context_priv, unsigned char id, size_t chunk_len) { if(!context_priv->file_transfers_send || context_priv->ft_send_offset) return NULL; if(context_priv_ft_send_id(context_priv, id)) { if(context_priv->ft_send_len - context_priv->ft_send_offset > chunk_len) {// this isn't the last data chunk context_priv->ft_send_offset += chunk_len; return context_priv->file_transfers_send[id] + context_priv->ft_send_offset[id] - chunk_len; } else {// this is last data chunk return context_priv->file_transfers_send[id] + context_priv->ft_send_offset[id]; } } else return NULL; } unsigned int context_priv_ft_get_total_id_recv(ConnContextPriv * context_priv) {// id == 0 is always "used" by design if(!context_priv->file_transfers_recv) return 0; unsigned int id_count = 0; int i; for(i = OTR_MIN_FT; i < OTR_MAX_FT; i++) if(context_priv->file_transfers_recv[i] != -1) id_count++; return id_count; } unsigned int context_priv_ft_get_total_id_send(ConnContextPriv * context_priv) {// id==0-4 is always "used" by design if(!context_priv->file_transfers_send) return 0; unsigned int id_count = 0; int i; for(i = OTR_MIN_FT; i < OTR_MAX_FT; i++) if(context_priv->file_transfers_send[i]) id_count++; return id_count; } unsigned char context_priv_ft_get_next_recv(ConnContextPriv * context_priv) { if(!context_priv->file_transfers_recv) return 0; unsigned char i; for(i = OTR_MIN_FT; i < OTR_MAX_FT; i++) if(context_priv->file_transfers_recv[i] == -1) return i; } unsigned char context_priv_ft_get_next_send(ConnContextPriv * context_priv) { if(!context_priv->file_transfers_send) return 0; unsigned char i; for(i = OTR_MIN_FT; i < OTR_MAX_FT; i++) if(!context_priv->file_transfers_send[i]) return i; } // return ft_id or 0 if error unsigned char context_priv_ft_add_recv(ConnContextPriv * context_priv, char * filename) { unsigned char id = context_priv_ft_get_next_recv(context_priv); if(!id) fprintf(stderr, "failed to get new file transfer id, total %u ids used\n", context_priv_ft_get_total_id_recv(context_priv)); if(!context_priv->file_transfers_recv) return 0; if(context_priv->file_transfers_recv[id] != -1) return 0; context_priv->file_transfers_recv[id] = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if(-1 == context_priv->file_transfers_recv[id]) return 0; return id; } // return ft_id or 0 if error unsigned char context_priv_ft_add_send(ConnContextPriv * context_priv, char * filename) { unsigned char id = context_priv_ft_get_next_send(context_priv); if(!id) fprintf(stderr, "failed to get new file transfer id, total %u ids used\n", context_priv_ft_get_total_id_send(context_priv)); if(!context_priv->file_transfers_send) return 0; int fd = open(filename, O_RDONLY); struct stat statbuf; if(-1 == fd) { fprintf(stderr, "failed to open file %s", filename); return 0; } if(fstat(fd, &statbuf) < 0) { fprintf(stderr, "failed to stat file %s\n", filename); return 0; } context_priv->file_transfers_send[id] = mmap(NULL, statbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0); if(MAP_FAILED == context_priv->file_transfers_send[id]) { context_priv->file_transfers_send[id] = NULL; fprintf(stderr, "mapping failed for %s\n", filename); return 0; } else if(context_priv->ft_send_len) context_priv->ft_send_len[id] = statbuf.st_size; close(fd); return id; } int context_priv_ft_del_recv(ConnContextPriv * context_priv, unsigned char id) { fprintf(stderr, "removing RECV with id %d\n", id); if(!context_priv->file_transfers_recv) return -2; if(-1 == context_priv->file_transfers_recv[id]) return -1; fprintf(stderr, "\tclosing %d\n", context_priv->file_transfers_recv[id]); int clret = close(context_priv->file_transfers_recv[id]); fprintf(stderr, "\tcleanup\n"); context_priv->file_transfers_recv[id] = -1; fprintf(stderr, "removed.\n", id); return clret; } int context_priv_ft_del_send(ConnContextPriv * context_priv, unsigned char id) { fprintf(stderr, "removing SEND with id %d\n", id); if(!context_priv->file_transfers_send) return -2; if(!context_priv->ft_send_len) return -3; if(NULL == context_priv->file_transfers_send[id]) return -1; fprintf(stderr, "\tunmapping %zd bytes\n", context_priv->ft_send_len[id]); int unmret = munmap(context_priv->file_transfers_send[id], context_priv->ft_send_len[id]); fprintf(stderr, "\tcleanup\n"); context_priv->file_transfers_send[id] = NULL; context_priv->ft_send_len = 0; fprintf(stderr, "removed.\n", id); return unmret; }