Merged pidgin/main into default
[pidgin-git.git] / libpurple / protocols / silc / pk.c
blobb62db3aeac74fe1536b455fe056c6edcd980a0fc
1 /*
3 silcpurple_pk.c
5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2004 - 2007 Pekka Riikonen
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.
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.
20 #include "internal.h"
21 #include "glibcompat.h"
22 PURPLE_BEGIN_IGNORE_CAST_ALIGN
23 #include "silc.h"
24 PURPLE_END_IGNORE_CAST_ALIGN
25 #include "silcclient.h"
26 #include "silcpurple.h"
28 /************************* Public Key Verification ***************************/
30 typedef struct {
31 SilcClient client;
32 SilcClientConnection conn;
33 char *filename;
34 char *entity;
35 char *entity_name;
36 char *fingerprint;
37 char *babbleprint;
38 SilcPublicKey public_key;
39 SilcVerifyPublicKey completion;
40 void *context;
41 gboolean changed;
42 } *PublicKeyVerify;
44 static void silcpurple_verify_ask(const char *entity,
45 const char *fingerprint,
46 const char *babbleprint,
47 PublicKeyVerify verify);
49 static void silcpurple_verify_cb(PublicKeyVerify verify, gint id)
51 if (id != 2) {
52 if (verify->completion)
53 verify->completion(FALSE, verify->context);
54 } else {
55 if (verify->completion)
56 verify->completion(TRUE, verify->context);
58 /* Save the key for future checking */
59 silc_pkcs_save_public_key(verify->filename, verify->public_key,
60 SILC_PKCS_FILE_BASE64);
63 g_free(verify->filename);
64 g_free(verify->entity);
65 g_free(verify->entity_name);
66 silc_free(verify->fingerprint);
67 silc_free(verify->babbleprint);
68 silc_pkcs_public_key_free(verify->public_key);
69 silc_free(verify);
72 static void silcpurple_verify_details_cb(PublicKeyVerify verify)
74 /* What a hack. We have to display the accept dialog _again_
75 because Purple closes the dialog after you press the button. Purple
76 should have option for the dialogs whether the buttons close them
77 or not. */
78 silcpurple_verify_ask(verify->entity, verify->fingerprint,
79 verify->babbleprint, verify);
82 static void silcpurple_verify_details(PublicKeyVerify verify, gint id)
84 PurpleConnection *gc = verify->client->application;
85 SilcPurple sg = purple_connection_get_protocol_data(gc);
87 silcpurple_show_public_key(sg, verify->entity_name, verify->public_key,
88 G_CALLBACK(silcpurple_verify_details_cb),
89 verify);
92 static void silcpurple_verify_ask(const char *entity,
93 const char *fingerprint,
94 const char *babbleprint,
95 PublicKeyVerify verify)
97 PurpleConnection *gc = verify->client->application;
98 char tmp[256], tmp2[256];
100 if (verify->changed) {
101 g_snprintf(tmp, sizeof(tmp),
102 _("Received %s's public key. Your local copy does not match this "
103 "key. Would you still like to accept this public key?"),
104 entity);
105 } else {
106 g_snprintf(tmp, sizeof(tmp),
107 _("Received %s's public key. Would you like to accept this "
108 "public key?"), entity);
110 g_snprintf(tmp2, sizeof(tmp2),
111 _("Fingerprint and babbleprint for the %s key are:\n\n"
112 "%s\n%s\n"), entity, fingerprint, babbleprint);
114 purple_request_action(gc, _("Verify Public Key"), tmp, tmp2,
115 PURPLE_DEFAULT_ACTION_NONE,
116 purple_request_cpar_from_connection(gc), verify, 3,
117 _("Yes"), G_CALLBACK(silcpurple_verify_cb),
118 _("No"), G_CALLBACK(silcpurple_verify_cb),
119 _("_View..."), G_CALLBACK(silcpurple_verify_details));
122 void silcpurple_verify_public_key(SilcClient client, SilcClientConnection conn,
123 const char *name, SilcConnectionType conn_type,
124 SilcPublicKey public_key,
125 SilcVerifyPublicKey completion, void *context)
127 PurpleConnection *gc = client->application;
128 gsize i;
129 char file[256], filename[256], filename2[256], *ipf, *hostf = NULL;
130 char *fingerprint, *babbleprint;
131 struct passwd *pw;
132 GStatBuf st;
133 char *entity = ((conn_type == SILC_CONN_SERVER ||
134 conn_type == SILC_CONN_ROUTER) ?
135 "server" : "client");
136 PublicKeyVerify verify;
137 const char *ip, *hostname;
138 SilcUInt16 port;
139 unsigned char *pk;
140 SilcUInt32 pk_len;
142 if (silc_pkcs_get_type(public_key) != SILC_PKCS_SILC) {
143 purple_notify_error(gc, _("Verify Public Key"),
144 _("Unsupported public key type"), NULL,
145 purple_request_cpar_from_connection(gc));
146 if (completion)
147 completion(FALSE, context);
148 return;
151 pw = getpwuid(getuid());
152 if (!pw) {
153 if (completion)
154 completion(FALSE, context);
155 return;
158 memset(filename, 0, sizeof(filename));
159 memset(filename2, 0, sizeof(filename2));
160 memset(file, 0, sizeof(file));
162 silc_socket_stream_get_info(silc_packet_stream_get_stream(conn->stream),
163 NULL, &hostname, &ip, &port);
165 pk = silc_pkcs_public_key_encode(public_key, &pk_len);
166 if (!pk) {
167 if (completion)
168 completion(FALSE, context);
169 return;
172 if (conn_type == SILC_CONN_SERVER ||
173 conn_type == SILC_CONN_ROUTER) {
174 if (!name) {
175 g_snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
176 ip, port);
177 g_snprintf(filename, sizeof(filename) - 1,
178 "%s" G_DIR_SEPARATOR_S "%skeys" G_DIR_SEPARATOR_S "%s",
179 silcpurple_silcdir(), entity, file);
181 g_snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
182 hostname, port);
183 g_snprintf(filename2, sizeof(filename2) - 1,
184 "%s" G_DIR_SEPARATOR_S "%skeys" G_DIR_SEPARATOR_S "%s",
185 silcpurple_silcdir(), entity, file);
187 ipf = filename;
188 hostf = filename2;
189 } else {
190 g_snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
191 name, port);
192 g_snprintf(filename, sizeof(filename) - 1,
193 "%s" G_DIR_SEPARATOR_S "%skeys" G_DIR_SEPARATOR_S "%s",
194 silcpurple_silcdir(), entity, file);
196 ipf = filename;
198 } else {
199 /* Replace all whitespaces with `_'. */
200 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
201 for (i = 0; i < strlen(fingerprint); i++)
202 if (fingerprint[i] == ' ')
203 fingerprint[i] = '_';
205 g_snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
206 g_snprintf(filename, sizeof(filename) - 1,
207 "%s" G_DIR_SEPARATOR_S "%skeys" G_DIR_SEPARATOR_S "%s",
208 silcpurple_silcdir(), entity, file);
209 silc_free(fingerprint);
211 ipf = filename;
214 verify = silc_calloc(1, sizeof(*verify));
215 if (!verify)
216 return;
217 verify->client = client;
218 verify->conn = conn;
219 verify->filename = g_strdup(ipf);
220 verify->entity = g_strdup(entity);
221 verify->entity_name = (conn_type != SILC_CONN_CLIENT ?
222 (name ? g_strdup(name) : g_strdup(hostname))
223 : NULL);
224 verify->public_key = silc_pkcs_public_key_copy(public_key);
225 verify->completion = completion;
226 verify->context = context;
227 fingerprint = verify->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
228 babbleprint = verify->babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
230 /* Check whether this key already exists */
231 if (g_stat(ipf, &st) < 0 && (!hostf || g_stat(hostf, &st) < 0)) {
232 /* Key does not exist, ask user to verify the key and save it */
233 silcpurple_verify_ask(name ? name : entity,
234 fingerprint, babbleprint, verify);
235 return;
236 } else {
237 /* The key already exists, verify it. */
238 SilcPublicKey public_key;
239 unsigned char *encpk;
240 SilcUInt32 encpk_len;
242 /* Load the key file, try for both IP filename and hostname filename */
243 if (!silc_pkcs_load_public_key(ipf, &public_key) &&
244 (!hostf || (!silc_pkcs_load_public_key(hostf, &public_key)))) {
245 silcpurple_verify_ask(name ? name : entity,
246 fingerprint, babbleprint, verify);
247 return;
250 /* Encode the key data */
251 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
252 if (!encpk) {
253 silcpurple_verify_ask(name ? name : entity,
254 fingerprint, babbleprint, verify);
255 return;
258 /* Compare the keys */
259 if (memcmp(encpk, pk, encpk_len)) {
260 /* Ask user to verify the key and save it */
261 verify->changed = TRUE;
262 silcpurple_verify_ask(name ? name : entity,
263 fingerprint, babbleprint, verify);
264 return;
267 /* Local copy matched */
268 if (completion)
269 completion(TRUE, context);
270 g_free(verify->filename);
271 g_free(verify->entity);
272 g_free(verify->entity_name);
273 silc_free(verify->fingerprint);
274 silc_free(verify->babbleprint);
275 silc_pkcs_public_key_free(verify->public_key);
276 silc_free(verify);