| 1 /* |
|
| 2 |
|
| 3 silcpurple_pk.c |
|
| 4 |
|
| 5 Author: Pekka Riikonen <priikone@silcnet.org> |
|
| 6 |
|
| 7 Copyright (C) 2004 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 "silcincludes.h" |
|
| 21 #include "silcclient.h" |
|
| 22 #include "silcpurple.h" |
|
| 23 |
|
| 24 /************************* Public Key Verification ***************************/ |
|
| 25 |
|
| 26 typedef struct { |
|
| 27 SilcClient client; |
|
| 28 SilcClientConnection conn; |
|
| 29 char *filename; |
|
| 30 char *entity; |
|
| 31 char *entity_name; |
|
| 32 char *fingerprint; |
|
| 33 char *babbleprint; |
|
| 34 unsigned char *pk; |
|
| 35 SilcUInt32 pk_len; |
|
| 36 SilcSKEPKType pk_type; |
|
| 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_data(verify->filename, verify->pk, |
|
| 58 verify->pk_len, SILC_PKCS_FILE_PEM); |
|
| 59 } |
|
| 60 |
|
| 61 silc_free(verify->filename); |
|
| 62 silc_free(verify->entity); |
|
| 63 silc_free(verify->entity_name); |
|
| 64 silc_free(verify->fingerprint); |
|
| 65 silc_free(verify->babbleprint); |
|
| 66 silc_free(verify->pk); |
|
| 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 SilcPublicKey public_key; |
|
| 83 PurpleConnection *gc = verify->client->application; |
|
| 84 SilcPurple sg = gc->proto_data; |
|
| 85 |
|
| 86 silc_pkcs_public_key_decode(verify->pk, verify->pk_len, |
|
| 87 &public_key); |
|
| 88 silcpurple_show_public_key(sg, verify->entity_name, public_key, |
|
| 89 G_CALLBACK(silcpurple_verify_details_cb), |
|
| 90 verify); |
|
| 91 silc_pkcs_public_key_free(public_key); |
|
| 92 } |
|
| 93 |
|
| 94 static void silcpurple_verify_ask(const char *entity, |
|
| 95 const char *fingerprint, |
|
| 96 const char *babbleprint, |
|
| 97 PublicKeyVerify verify) |
|
| 98 { |
|
| 99 PurpleConnection *gc = verify->client->application; |
|
| 100 char tmp[256], tmp2[256]; |
|
| 101 |
|
| 102 if (verify->changed) { |
|
| 103 g_snprintf(tmp, sizeof(tmp), |
|
| 104 _("Received %s's public key. Your local copy does not match this " |
|
| 105 "key. Would you still like to accept this public key?"), |
|
| 106 entity); |
|
| 107 } else { |
|
| 108 g_snprintf(tmp, sizeof(tmp), |
|
| 109 _("Received %s's public key. Would you like to accept this " |
|
| 110 "public key?"), entity); |
|
| 111 } |
|
| 112 g_snprintf(tmp2, sizeof(tmp2), |
|
| 113 _("Fingerprint and babbleprint for the %s key are:\n\n" |
|
| 114 "%s\n%s\n"), entity, fingerprint, babbleprint); |
|
| 115 |
|
| 116 purple_request_action(gc, _("Verify Public Key"), tmp, tmp2, |
|
| 117 PURPLE_DEFAULT_ACTION_NONE, |
|
| 118 purple_connection_get_account(gc), entity, NULL, verify, 3, |
|
| 119 _("Yes"), G_CALLBACK(silcpurple_verify_cb), |
|
| 120 _("No"), G_CALLBACK(silcpurple_verify_cb), |
|
| 121 _("_View..."), G_CALLBACK(silcpurple_verify_details)); |
|
| 122 } |
|
| 123 |
|
| 124 void silcpurple_verify_public_key(SilcClient client, SilcClientConnection conn, |
|
| 125 const char *name, SilcSocketType conn_type, |
|
| 126 unsigned char *pk, SilcUInt32 pk_len, |
|
| 127 SilcSKEPKType pk_type, |
|
| 128 SilcVerifyPublicKey completion, void *context) |
|
| 129 { |
|
| 130 PurpleConnection *gc = client->application; |
|
| 131 int i; |
|
| 132 char file[256], filename[256], filename2[256], *ipf, *hostf = NULL; |
|
| 133 char *fingerprint, *babbleprint; |
|
| 134 struct passwd *pw; |
|
| 135 struct stat st; |
|
| 136 char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER || |
|
| 137 conn_type == SILC_SOCKET_TYPE_ROUTER) ? |
|
| 138 "server" : "client"); |
|
| 139 PublicKeyVerify verify; |
|
| 140 |
|
| 141 if (pk_type != SILC_SKE_PK_TYPE_SILC) { |
|
| 142 purple_notify_error(gc, _("Verify Public Key"), |
|
| 143 _("Unsupported public key type"), NULL); |
|
| 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 if (conn_type == SILC_SOCKET_TYPE_SERVER || |
|
| 161 conn_type == SILC_SOCKET_TYPE_ROUTER) { |
|
| 162 if (!name) { |
|
| 163 g_snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, |
|
| 164 conn->sock->ip, conn->sock->port); |
|
| 165 g_snprintf(filename, sizeof(filename) - 1, |
|
| 166 "%s" G_DIR_SEPARATOR_S "%skeys" G_DIR_SEPARATOR_S "%s", |
|
| 167 silcpurple_silcdir(), entity, file); |
|
| 168 |
|
| 169 g_snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, |
|
| 170 conn->sock->hostname, conn->sock->port); |
|
| 171 g_snprintf(filename2, sizeof(filename2) - 1, |
|
| 172 "%s" G_DIR_SEPARATOR_S "%skeys" G_DIR_SEPARATOR_S "%s", |
|
| 173 silcpurple_silcdir(), entity, file); |
|
| 174 |
|
| 175 ipf = filename; |
|
| 176 hostf = filename2; |
|
| 177 } else { |
|
| 178 g_snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, |
|
| 179 name, conn->sock->port); |
|
| 180 g_snprintf(filename, sizeof(filename) - 1, |
|
| 181 "%s" G_DIR_SEPARATOR_S "%skeys" G_DIR_SEPARATOR_S "%s", |
|
| 182 silcpurple_silcdir(), entity, file); |
|
| 183 |
|
| 184 ipf = filename; |
|
| 185 } |
|
| 186 } else { |
|
| 187 /* Replace all whitespaces with `_'. */ |
|
| 188 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len); |
|
| 189 for (i = 0; i < strlen(fingerprint); i++) |
|
| 190 if (fingerprint[i] == ' ') |
|
| 191 fingerprint[i] = '_'; |
|
| 192 |
|
| 193 g_snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint); |
|
| 194 g_snprintf(filename, sizeof(filename) - 1, |
|
| 195 "%s" G_DIR_SEPARATOR_S "%skeys" G_DIR_SEPARATOR_S "%s", |
|
| 196 silcpurple_silcdir(), entity, file); |
|
| 197 silc_free(fingerprint); |
|
| 198 |
|
| 199 ipf = filename; |
|
| 200 } |
|
| 201 |
|
| 202 verify = silc_calloc(1, sizeof(*verify)); |
|
| 203 if (!verify) |
|
| 204 return; |
|
| 205 verify->client = client; |
|
| 206 verify->conn = conn; |
|
| 207 verify->filename = strdup(ipf); |
|
| 208 verify->entity = strdup(entity); |
|
| 209 verify->entity_name = (conn_type != SILC_SOCKET_TYPE_CLIENT ? |
|
| 210 (name ? strdup(name) : strdup(conn->sock->hostname)) |
|
| 211 : NULL); |
|
| 212 verify->pk = silc_memdup(pk, pk_len); |
|
| 213 verify->pk_len = pk_len; |
|
| 214 verify->pk_type = pk_type; |
|
| 215 verify->completion = completion; |
|
| 216 verify->context = context; |
|
| 217 fingerprint = verify->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len); |
|
| 218 babbleprint = verify->babbleprint = silc_hash_babbleprint(NULL, pk, pk_len); |
|
| 219 |
|
| 220 /* Check whether this key already exists */ |
|
| 221 if (g_stat(ipf, &st) < 0 && (!hostf || g_stat(hostf, &st) < 0)) { |
|
| 222 /* Key does not exist, ask user to verify the key and save it */ |
|
| 223 silcpurple_verify_ask(name ? name : entity, |
|
| 224 fingerprint, babbleprint, verify); |
|
| 225 return; |
|
| 226 } else { |
|
| 227 /* The key already exists, verify it. */ |
|
| 228 SilcPublicKey public_key; |
|
| 229 unsigned char *encpk; |
|
| 230 SilcUInt32 encpk_len; |
|
| 231 |
|
| 232 /* Load the key file, try for both IP filename and hostname filename */ |
|
| 233 if (!silc_pkcs_load_public_key(ipf, &public_key, |
|
| 234 SILC_PKCS_FILE_PEM) && |
|
| 235 !silc_pkcs_load_public_key(ipf, &public_key, |
|
| 236 SILC_PKCS_FILE_BIN) && |
|
| 237 (!hostf || (!silc_pkcs_load_public_key(hostf, &public_key, |
|
| 238 SILC_PKCS_FILE_PEM) && |
|
| 239 !silc_pkcs_load_public_key(hostf, &public_key, |
|
| 240 SILC_PKCS_FILE_BIN)))) { |
|
| 241 silcpurple_verify_ask(name ? name : entity, |
|
| 242 fingerprint, babbleprint, verify); |
|
| 243 return; |
|
| 244 } |
|
| 245 |
|
| 246 /* Encode the key data */ |
|
| 247 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len); |
|
| 248 if (!encpk) { |
|
| 249 silcpurple_verify_ask(name ? name : entity, |
|
| 250 fingerprint, babbleprint, verify); |
|
| 251 return; |
|
| 252 } |
|
| 253 |
|
| 254 /* Compare the keys */ |
|
| 255 if (memcmp(encpk, pk, encpk_len)) { |
|
| 256 /* Ask user to verify the key and save it */ |
|
| 257 verify->changed = TRUE; |
|
| 258 silcpurple_verify_ask(name ? name : entity, |
|
| 259 fingerprint, babbleprint, verify); |
|
| 260 return; |
|
| 261 } |
|
| 262 |
|
| 263 /* Local copy matched */ |
|
| 264 if (completion) |
|
| 265 completion(TRUE, context); |
|
| 266 silc_free(verify->filename); |
|
| 267 silc_free(verify->entity); |
|
| 268 silc_free(verify->entity_name); |
|
| 269 silc_free(verify->pk); |
|
| 270 silc_free(verify->fingerprint); |
|
| 271 silc_free(verify->babbleprint); |
|
| 272 silc_free(verify); |
|
| 273 } |
|
| 274 } |
|