| 1 /* |
|
| 2 |
|
| 3 silcpurple_pk.c |
|
| 4 |
|
| 5 Author: Pekka Riikonen <priikone@silcnet.org> |
|
| 6 |
|
| 7 Copyright (C) 2004 - 2007 Pekka Riikonen |
|
| 8 |
|
| 9 This program is free software; you can redistribute it and/or modify |
|
| 10 it under the terms of the GNU General Public License as published by |
|
| 11 the Free Software Foundation; version 2 of the License. |
|
| 12 |
|
| 13 This program is distributed in the hope that it will be useful, |
|
| 14 but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 16 GNU General Public License for more details. |
|
| 17 |
|
| 18 */ |
|
| 19 |
|
| 20 #include <glib/gi18n-lib.h> |
|
| 21 #include <glib/gstdio.h> |
|
| 22 |
|
| 23 #include "glibcompat.h" /* for purple_g_stat on win32 */ |
|
| 24 #include "silcpurple.h" |
|
| 25 |
|
| 26 /************************* Public Key Verification ***************************/ |
|
| 27 |
|
| 28 typedef struct { |
|
| 29 SilcClient client; |
|
| 30 SilcClientConnection conn; |
|
| 31 char *filename; |
|
| 32 char *entity; |
|
| 33 char *entity_name; |
|
| 34 char *fingerprint; |
|
| 35 char *babbleprint; |
|
| 36 SilcPublicKey public_key; |
|
| 37 SilcVerifyPublicKey completion; |
|
| 38 void *context; |
|
| 39 gboolean changed; |
|
| 40 } *PublicKeyVerify; |
|
| 41 |
|
| 42 static void silcpurple_verify_ask(const char *entity, |
|
| 43 const char *fingerprint, |
|
| 44 const char *babbleprint, |
|
| 45 PublicKeyVerify verify); |
|
| 46 |
|
| 47 static void silcpurple_verify_cb(PublicKeyVerify verify, gint id) |
|
| 48 { |
|
| 49 if (id != 2) { |
|
| 50 if (verify->completion) |
|
| 51 verify->completion(FALSE, verify->context); |
|
| 52 } else { |
|
| 53 if (verify->completion) |
|
| 54 verify->completion(TRUE, verify->context); |
|
| 55 |
|
| 56 /* Save the key for future checking */ |
|
| 57 silc_pkcs_save_public_key(verify->filename, verify->public_key, |
|
| 58 SILC_PKCS_FILE_BASE64); |
|
| 59 } |
|
| 60 |
|
| 61 g_free(verify->filename); |
|
| 62 g_free(verify->entity); |
|
| 63 g_free(verify->entity_name); |
|
| 64 silc_free(verify->fingerprint); |
|
| 65 silc_free(verify->babbleprint); |
|
| 66 silc_pkcs_public_key_free(verify->public_key); |
|
| 67 silc_free(verify); |
|
| 68 } |
|
| 69 |
|
| 70 static void silcpurple_verify_details_cb(PublicKeyVerify verify) |
|
| 71 { |
|
| 72 /* What a hack. We have to display the accept dialog _again_ |
|
| 73 because Purple closes the dialog after you press the button. Purple |
|
| 74 should have option for the dialogs whether the buttons close them |
|
| 75 or not. */ |
|
| 76 silcpurple_verify_ask(verify->entity, verify->fingerprint, |
|
| 77 verify->babbleprint, verify); |
|
| 78 } |
|
| 79 |
|
| 80 static void silcpurple_verify_details(PublicKeyVerify verify, gint id) |
|
| 81 { |
|
| 82 PurpleConnection *gc = verify->client->application; |
|
| 83 SilcPurple sg = purple_connection_get_protocol_data(gc); |
|
| 84 |
|
| 85 silcpurple_show_public_key(sg, verify->entity_name, verify->public_key, |
|
| 86 G_CALLBACK(silcpurple_verify_details_cb), |
|
| 87 verify); |
|
| 88 } |
|
| 89 |
|
| 90 static void silcpurple_verify_ask(const char *entity, |
|
| 91 const char *fingerprint, |
|
| 92 const char *babbleprint, |
|
| 93 PublicKeyVerify verify) |
|
| 94 { |
|
| 95 PurpleConnection *gc = verify->client->application; |
|
| 96 char tmp[256], tmp2[256]; |
|
| 97 |
|
| 98 if (verify->changed) { |
|
| 99 g_snprintf(tmp, sizeof(tmp), |
|
| 100 _("Received %s's public key. Your local copy does not match this " |
|
| 101 "key. Would you still like to accept this public key?"), |
|
| 102 entity); |
|
| 103 } else { |
|
| 104 g_snprintf(tmp, sizeof(tmp), |
|
| 105 _("Received %s's public key. Would you like to accept this " |
|
| 106 "public key?"), entity); |
|
| 107 } |
|
| 108 g_snprintf(tmp2, sizeof(tmp2), |
|
| 109 _("Fingerprint and babbleprint for the %s key are:\n\n" |
|
| 110 "%s\n%s\n"), entity, fingerprint, babbleprint); |
|
| 111 |
|
| 112 purple_request_action(gc, _("Verify Public Key"), tmp, tmp2, |
|
| 113 PURPLE_DEFAULT_ACTION_NONE, |
|
| 114 purple_request_cpar_from_connection(gc), verify, 3, |
|
| 115 _("Yes"), G_CALLBACK(silcpurple_verify_cb), |
|
| 116 _("No"), G_CALLBACK(silcpurple_verify_cb), |
|
| 117 _("_View..."), G_CALLBACK(silcpurple_verify_details)); |
|
| 118 } |
|
| 119 |
|
| 120 void silcpurple_verify_public_key(SilcClient client, SilcClientConnection conn, |
|
| 121 const char *name, SilcConnectionType conn_type, |
|
| 122 SilcPublicKey public_key, |
|
| 123 SilcVerifyPublicKey completion, void *context) |
|
| 124 { |
|
| 125 PurpleConnection *gc = client->application; |
|
| 126 gsize i; |
|
| 127 char file[256], filename[256], filename2[256], *ipf, *hostf = NULL; |
|
| 128 char *fingerprint, *babbleprint; |
|
| 129 struct passwd *pw; |
|
| 130 GStatBuf st; |
|
| 131 char *entity = ((conn_type == SILC_CONN_SERVER || |
|
| 132 conn_type == SILC_CONN_ROUTER) ? |
|
| 133 "server" : "client"); |
|
| 134 PublicKeyVerify verify; |
|
| 135 const char *ip, *hostname; |
|
| 136 SilcUInt16 port; |
|
| 137 unsigned char *pk; |
|
| 138 SilcUInt32 pk_len; |
|
| 139 |
|
| 140 if (silc_pkcs_get_type(public_key) != SILC_PKCS_SILC) { |
|
| 141 purple_notify_error(gc, _("Verify Public Key"), |
|
| 142 _("Unsupported public key type"), NULL, |
|
| 143 purple_request_cpar_from_connection(gc)); |
|
| 144 if (completion) |
|
| 145 completion(FALSE, context); |
|
| 146 return; |
|
| 147 } |
|
| 148 |
|
| 149 pw = getpwuid(getuid()); |
|
| 150 if (!pw) { |
|
| 151 if (completion) |
|
| 152 completion(FALSE, context); |
|
| 153 return; |
|
| 154 } |
|
| 155 |
|
| 156 memset(filename, 0, sizeof(filename)); |
|
| 157 memset(filename2, 0, sizeof(filename2)); |
|
| 158 memset(file, 0, sizeof(file)); |
|
| 159 |
|
| 160 silc_socket_stream_get_info(silc_packet_stream_get_stream(conn->stream), |
|
| 161 NULL, &hostname, &ip, &port); |
|
| 162 |
|
| 163 pk = silc_pkcs_public_key_encode(public_key, &pk_len); |
|
| 164 if (!pk) { |
|
| 165 if (completion) |
|
| 166 completion(FALSE, context); |
|
| 167 return; |
|
| 168 } |
|
| 169 |
|
| 170 if (conn_type == SILC_CONN_SERVER || |
|
| 171 conn_type == SILC_CONN_ROUTER) { |
|
| 172 if (!name) { |
|
| 173 g_snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, |
|
| 174 ip, port); |
|
| 175 g_snprintf(filename, sizeof(filename) - 1, |
|
| 176 "%s" G_DIR_SEPARATOR_S "%skeys" G_DIR_SEPARATOR_S "%s", |
|
| 177 silcpurple_silcdir(), entity, file); |
|
| 178 |
|
| 179 g_snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, |
|
| 180 hostname, port); |
|
| 181 g_snprintf(filename2, sizeof(filename2) - 1, |
|
| 182 "%s" G_DIR_SEPARATOR_S "%skeys" G_DIR_SEPARATOR_S "%s", |
|
| 183 silcpurple_silcdir(), entity, file); |
|
| 184 |
|
| 185 ipf = filename; |
|
| 186 hostf = filename2; |
|
| 187 } else { |
|
| 188 g_snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, |
|
| 189 name, port); |
|
| 190 g_snprintf(filename, sizeof(filename) - 1, |
|
| 191 "%s" G_DIR_SEPARATOR_S "%skeys" G_DIR_SEPARATOR_S "%s", |
|
| 192 silcpurple_silcdir(), entity, file); |
|
| 193 |
|
| 194 ipf = filename; |
|
| 195 } |
|
| 196 } else { |
|
| 197 /* Replace all whitespaces with `_'. */ |
|
| 198 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len); |
|
| 199 for (i = 0; i < strlen(fingerprint); i++) |
|
| 200 if (fingerprint[i] == ' ') |
|
| 201 fingerprint[i] = '_'; |
|
| 202 |
|
| 203 g_snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint); |
|
| 204 g_snprintf(filename, sizeof(filename) - 1, |
|
| 205 "%s" G_DIR_SEPARATOR_S "%skeys" G_DIR_SEPARATOR_S "%s", |
|
| 206 silcpurple_silcdir(), entity, file); |
|
| 207 silc_free(fingerprint); |
|
| 208 |
|
| 209 ipf = filename; |
|
| 210 } |
|
| 211 |
|
| 212 verify = silc_calloc(1, sizeof(*verify)); |
|
| 213 if (!verify) |
|
| 214 return; |
|
| 215 verify->client = client; |
|
| 216 verify->conn = conn; |
|
| 217 verify->filename = g_strdup(ipf); |
|
| 218 verify->entity = g_strdup(entity); |
|
| 219 verify->entity_name = (conn_type != SILC_CONN_CLIENT ? |
|
| 220 (name ? g_strdup(name) : g_strdup(hostname)) |
|
| 221 : NULL); |
|
| 222 verify->public_key = silc_pkcs_public_key_copy(public_key); |
|
| 223 verify->completion = completion; |
|
| 224 verify->context = context; |
|
| 225 fingerprint = verify->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len); |
|
| 226 babbleprint = verify->babbleprint = silc_hash_babbleprint(NULL, pk, pk_len); |
|
| 227 |
|
| 228 /* Check whether this key already exists */ |
|
| 229 if (g_stat(ipf, &st) < 0 && (!hostf || g_stat(hostf, &st) < 0)) { |
|
| 230 /* Key does not exist, ask user to verify the key and save it */ |
|
| 231 silcpurple_verify_ask(name ? name : entity, |
|
| 232 fingerprint, babbleprint, verify); |
|
| 233 return; |
|
| 234 } else { |
|
| 235 /* The key already exists, verify it. */ |
|
| 236 SilcPublicKey public_key; |
|
| 237 unsigned char *encpk; |
|
| 238 SilcUInt32 encpk_len; |
|
| 239 |
|
| 240 /* Load the key file, try for both IP filename and hostname filename */ |
|
| 241 if (!silc_pkcs_load_public_key(ipf, &public_key) && |
|
| 242 (!hostf || (!silc_pkcs_load_public_key(hostf, &public_key)))) { |
|
| 243 silcpurple_verify_ask(name ? name : entity, |
|
| 244 fingerprint, babbleprint, verify); |
|
| 245 return; |
|
| 246 } |
|
| 247 |
|
| 248 /* Encode the key data */ |
|
| 249 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len); |
|
| 250 if (!encpk) { |
|
| 251 silcpurple_verify_ask(name ? name : entity, |
|
| 252 fingerprint, babbleprint, verify); |
|
| 253 return; |
|
| 254 } |
|
| 255 |
|
| 256 /* Compare the keys */ |
|
| 257 if (memcmp(encpk, pk, encpk_len)) { |
|
| 258 /* Ask user to verify the key and save it */ |
|
| 259 verify->changed = TRUE; |
|
| 260 silcpurple_verify_ask(name ? name : entity, |
|
| 261 fingerprint, babbleprint, verify); |
|
| 262 return; |
|
| 263 } |
|
| 264 |
|
| 265 /* Local copy matched */ |
|
| 266 if (completion) |
|
| 267 completion(TRUE, context); |
|
| 268 g_free(verify->filename); |
|
| 269 g_free(verify->entity); |
|
| 270 g_free(verify->entity_name); |
|
| 271 silc_free(verify->fingerprint); |
|
| 272 silc_free(verify->babbleprint); |
|
| 273 silc_pkcs_public_key_free(verify->public_key); |
|
| 274 silc_free(verify); |
|
| 275 } |
|
| 276 } |
|