📄 keys.c
字号:
/* Protocol-independent Key structures *//* Copyright (C) 2001-2003 William Tompkins *//* This plugin is free software, distributed under the GNU General Public *//* License. *//* Please see the file "COPYING" distributed with the Gaim source code *//* for more details *//* *//* *//* This software 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. *//* To compile and use: *//* See INSTALL file. *//* #define GAIM_PLUGINS */#include <glib/gstdio.h>#include <gdk/gdk.h>#include <gtk/gtk.h>#include <gtk/gtkplug.h>#include <debug.h>#include <gaim.h>#include <util.h>#include <time.h>#include <sys/types.h>#include <sys/time.h>#include <string.h>#include <unistd.h>#include <math.h>#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <errno.h>#include "keys.h"#include "cryptutil.h"#include "prefs.h"#include "encrypt.h"#include "keys_ui.h"#include "ge_ui.h"#include "nls.h"#ifdef _WIN32#include "win32dep.h"#endif/* List of all the keys we know about */key_ring *GE_buddy_ring = 0, *GE_saved_buddy_ring = 0, *GE_my_priv_ring = 0, *GE_my_pub_ring = 0;typedef enum {KEY_MATCH, KEY_NOT_THERE, KEY_CONFLICT} KeyCheckVal;static KeyCheckVal GE_check_known_key(const char *filename, key_ring_data* key);crypt_key * GE_find_key_by_name(key_ring *ring, const char *name, GaimAccount *acct) { key_ring *i = GE_find_key_node_by_name(ring, name, acct); gaim_debug(GAIM_DEBUG_MISC, "gaim-encryption", "find key by name: %s\n", name); return (i == NULL) ? NULL : ((key_ring_data *)i->data)->key;}crypt_key * GE_find_own_key_by_name(key_ring **ring, char *name, GaimAccount *acct, GaimConversation *conv) { crypt_key *key = GE_find_key_by_name(*ring, name, acct); if (key) return key; /* Can't find the key, but it's ours, so we'll make one */ gaim_debug(GAIM_DEBUG_ERROR, "gaim-encryption", "Error! Can't find own key for %s\n", name); gaim_debug(GAIM_DEBUG_ERROR, "gaim-encryption", "Dumping public keyring:\n"); GE_debug_dump_keyring(GE_my_pub_ring); if (conv != 0) { gaim_conversation_write(conv, "Encryption Manager", _("Making new key pair..."), GAIM_MESSAGE_SYSTEM, time((time_t)NULL)); } GE_make_private_pair((crypt_proto *)crypt_proto_list->data, name, conv->account, 1024); key = GE_find_key_by_name(*ring, name, conv->account); if (key) return key; /* Still no key: something is seriously wrong. Probably having trouble saving the */ /* key to the key file, or some such. */ gaim_debug(GAIM_DEBUG_ERROR, "gaim-encryption", "Error! Can't make new key for %s\n", name); if (conv != 0) { gaim_conversation_write(conv, "Encryption Manager", _("Error trying to make key."), GAIM_MESSAGE_SYSTEM, time((time_t)NULL)); } return 0;}key_ring * GE_find_key_node_by_name(key_ring *ring, const char *name, GaimAccount* acct) { key_ring *i = 0; for( i = ring; i != NULL; i = i->next ) { if( (strncmp(name, ((key_ring_data *)i->data)->name, sizeof(((key_ring_data*)i->data)->name)) == 0 ) && (acct == ((key_ring_data*)i->data)->account)) break; } return (i == NULL) ? NULL : i;}void GE_debug_dump_keyring(key_ring * ring) { key_ring *i = 0; for( i = ring; i != NULL; i = i->next ) { gaim_debug(GAIM_DEBUG_INFO, "gaim-encryption", "Key ring::%*s::%p\n", sizeof(((key_ring_data *)i->data)->name), ((key_ring_data *)i->data)->name, ((key_ring_data *)i->data)->account); }}/* add_key_to_ring will ensure that there is only one key on a ring that matches a given name. So your buddy switches computers (and keys), we will discard his old key when he sends us his new one. */key_ring* GE_add_key_to_ring(key_ring* ring, key_ring_data* key) { key_ring* old_key = GE_find_key_node_by_name(ring, key->name, key->account); if (old_key != NULL) { ring = g_slist_remove_link(ring, old_key); } ring = g_slist_prepend(ring, key); return ring;}key_ring* GE_del_key_from_ring(key_ring* ring, const char* name, GaimAccount* acct) { key_ring* old_key = GE_find_key_node_by_name(ring, name, acct); if (old_key != NULL) { gaim_debug(GAIM_DEBUG_INFO, "gaim-encryption", "Removing key for %s\n", name); ring = g_slist_remove_link(ring, old_key); } return ring;}key_ring* GE_clear_ring(key_ring* ring) { crypt_key* key; key_ring *iter = ring; while (iter != NULL) { key = ((key_ring_data *)(iter->data))->key; GE_free_key(key); g_free(iter->data); iter = iter->next; } g_slist_free(ring); return NULL;}void GE_received_key(char *key_msg, char *name, GaimAccount* acct, GaimConversation* conv, char** orig_msg) { GSList *protoiter; crypt_proto* proto=0; unsigned char* key_len_msg=0; unsigned int length; int realstart; gchar** after_key; gchar* resend_msg_id = 0; key_ring_data *new_key; KeyCheckVal keycheck_return; gaim_debug(GAIM_DEBUG_INFO, "gaim-encryption", "received_key\n"); if (strncmp(key_msg, ": Prot ", sizeof(": Prot ") - 1) != 0) { gaim_debug(GAIM_DEBUG_ERROR, "gaim-encryption", "Error in received key\n"); return; } key_msg += sizeof(": Prot ") - 1; protoiter = crypt_proto_list; while (protoiter != 0 && proto == 0) { if( (key_len_msg = ((crypt_proto *)protoiter->data)->parseable(key_msg)) != 0 ) { proto = ((crypt_proto *) protoiter->data); } } if (proto == 0) { gaim_debug(GAIM_DEBUG_ERROR, "gaim-encryption", "Unknown protocol type: %10s\n", key_msg); return; } if ( (sscanf(key_len_msg, ": Len %u:%n", &length, &realstart) < 1) || (realstart == 0) ) { gaim_debug(GAIM_DEBUG_ERROR, "gaim-encryption", "Error in key header\n"); return; } key_len_msg += realstart; if (strlen(key_len_msg) < length) { gaim_debug(GAIM_DEBUG_ERROR, "gaim-encryption", "Length doesn't match in add_key\n"); return; } gaim_debug(GAIM_DEBUG_MISC, "gaim-encryption", "After key:%s\n", key_len_msg+length); after_key = g_strsplit(key_len_msg+length, ":", 3); if (after_key[0] && (strcmp(after_key[0], "Resend") == 0)) { if (after_key[1]) { resend_msg_id = g_strdup(after_key[1]); } } g_strfreev(after_key); key_len_msg[length] = 0; /* Make a new node for the linked list */ new_key = g_malloc(sizeof(key_ring_data)); new_key->account = acct; new_key->key = proto->parse_sent_key(key_len_msg); if (new_key->key == 0) { g_free(new_key); gaim_debug(GAIM_DEBUG_ERROR, "gaim-encryption", "Invalid key received\n"); return; } strncpy(new_key->name, name, sizeof(new_key->name)); keycheck_return = GE_check_known_key(Buddy_key_file, new_key); /* Now that we've pulled the key out of the original message, we can free it */ /* so that (maybe) a stored message can be returned in it */ (*orig_msg)[0] = 0; g_free(*orig_msg); *orig_msg = 0; switch(keycheck_return) { case KEY_NOT_THERE: GE_choose_accept_unknown_key(new_key, resend_msg_id, conv); break; case KEY_MATCH: GE_buddy_ring = GE_add_key_to_ring(GE_buddy_ring, new_key); GE_send_stored_msgs(new_key->account, new_key->name); GE_show_stored_msgs(new_key->account, new_key->name, orig_msg); if (resend_msg_id) { GE_resend_msg(new_key->account, new_key->name, resend_msg_id); } break; case KEY_CONFLICT: if (conv) { gaim_conversation_write(conv, "Encryption Manager", _("Conflicting Key Received!"), GAIM_MESSAGE_SYSTEM, time((time_t)NULL)); } GE_choose_accept_conflict_key(new_key, resend_msg_id, conv); break; } if (resend_msg_id) { g_free(resend_msg_id); resend_msg_id = 0; }}static KeyCheckVal GE_check_known_key(const char* filename, key_ring_data* key) { char line[MAX_KEY_STORLEN]; GString *line_str, *key_str, *name_str; char path[4096]; struct stat fs; FILE* fp; int fd; int found_name = 0; g_snprintf(path, sizeof(path), "%s%s%s", gaim_user_dir(), G_DIR_SEPARATOR_S, filename); gaim_debug(GAIM_DEBUG_INFO, "gaim-encryption", "Checking key file %s for name %s\n", path, key->name); /* check file permissions */ if (stat(path, &fs) == -1) { /* file doesn't exist, so make it */ fd = g_open(path, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); if (fd == -1) { /* Ok, maybe something else strange is going on... */ gaim_debug(GAIM_DEBUG_ERROR, "gaim-encryption", "Error trying to create a known key file\n"); return KEY_NOT_THERE; } fstat(fd, &fs); fchmod(fd, fs.st_mode & S_IRWXU); /* zero out non-owner permissions */ close(fd); } else {#ifdef S_IWGRP /* WIN32 doesn't have user-based file permissions, so skips this */ if (fs.st_mode & (S_IWGRP | S_IWOTH)) { gaim_debug(GAIM_DEBUG_ERROR, "gaim-encryption", "Invalid permissions, rejecting file: %s\n", path); return KEY_CONFLICT; }#endif } /* build string from key */ name_str = g_string_new(key->name); GE_escape_name(name_str); g_string_append_printf(name_str, ",%s", gaim_account_get_protocol_id(key->account)); line_str = g_string_new(name_str->str); g_string_append_printf(line_str, " %s ", key->key->proto->name); key_str = GE_key_to_gstr(key->key); g_string_append(line_str, key_str->str); /* gaim_debug(GAIM_DEBUG_MISC, "gaim-encryption", "built line '%s'\n", line_str->str); */ /* look for key in file */ if( (fp = g_fopen(path, "r")) != NULL ) { while (!feof(fp)) { fgets(line, sizeof(line), fp); /* gaim_debug(GAIM_DEBUG_MISC, "gaim-encryption", "checking line '%s'\n", line); */ if ( (strchr(line, ' ') == line + name_str->len) && (strncmp(line_str->str, line, name_str->len) == 0) ) { gaim_debug(GAIM_DEBUG_MISC, "gaim-encryption", "Got Name\n"); found_name = 1; if (strncmp(line_str->str, line, line_str->len) == 0) { gaim_debug(GAIM_DEBUG_MISC, "gaim-encryption", "Got Match\n"); fclose(fp); g_string_free(line_str, TRUE); g_string_free(key_str, TRUE); g_string_free(name_str, TRUE); return KEY_MATCH; } } } fclose(fp); } g_string_free(line_str, TRUE); g_string_free(key_str, TRUE); g_string_free(name_str, TRUE); if (found_name) return KEY_CONFLICT; return KEY_NOT_THERE;}/* For now, we'll make all key files privately owned, even though the id.pub and known_keys files could be public. */ void GE_add_key_to_file(const char *filename, key_ring_data* key) { GString *line_str, *key_str; char path[4096]; char errbuf[500]; FILE* fp; int fd; char c; struct stat fdstat; gaim_debug(GAIM_DEBUG_INFO, "gaim-encryption", "Saving key to file:%s:%p\n", key->name, key->account); g_snprintf(path, sizeof(path), "%s%s%s", gaim_user_dir(), G_DIR_SEPARATOR_S, filename); fd = g_open(path, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); if (fd == -1) { gaim_debug(GAIM_DEBUG_ERROR, "gaim-encryption", "Error opening key file %s for write\n", path); /* WIN32 doesn't have user-based file permissions, so skips this */#ifdef S_IRWXG if (chmod(path, S_IRUSR | S_IWUSR) == -1) { gaim_debug(GAIM_DEBUG_ERROR, "gaim-encryption", "Unable to change file mode, aborting\n"); g_snprintf(errbuf, sizeof(errbuf), _("Error changing access mode for file: %s\nCannot save key."), filename); GE_ui_error(errbuf); return; }#endif fd = g_open(path, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); if (fd == -1) { gaim_debug(GAIM_DEBUG_ERROR, "gaim-encryption", "Changed mode, but still wonky. Aborting.\n"); g_snprintf(errbuf, sizeof(errbuf), _("Error (2) changing access mode for file: %s\nCannot save key."), filename); GE_ui_error(errbuf); return; } else { gaim_debug(GAIM_DEBUG_ERROR, "gaim-encryption", "Key file '%s' no longer read-only.\n"); } } fstat(fd, &fdstat);#ifdef S_IRWXG /* WIN32 doesn't have user-based file permissions, so skips this */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -