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.
21 #include "glibcompat.h"
22 PURPLE_BEGIN_IGNORE_CAST_ALIGN
24 PURPLE_END_IGNORE_CAST_ALIGN
25 #include "silcclient.h"
26 #include "silcpurple.h"
28 /************************* Public Key Verification ***************************/
32 SilcClientConnection conn
;
38 SilcPublicKey public_key
;
39 SilcVerifyPublicKey completion
;
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
)
52 if (verify
->completion
)
53 verify
->completion(FALSE
, verify
->context
);
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
);
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
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
),
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?"),
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
;
129 char file
[256], filename
[256], filename2
[256], *ipf
, *hostf
= NULL
;
130 char *fingerprint
, *babbleprint
;
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
;
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
));
147 completion(FALSE
, context
);
151 pw
= getpwuid(getuid());
154 completion(FALSE
, context
);
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
);
168 completion(FALSE
, context
);
172 if (conn_type
== SILC_CONN_SERVER
||
173 conn_type
== SILC_CONN_ROUTER
) {
175 g_snprintf(file
, sizeof(file
) - 1, "%skey_%s_%d.pub", entity
,
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
,
183 g_snprintf(filename2
, sizeof(filename2
) - 1,
184 "%s" G_DIR_SEPARATOR_S
"%skeys" G_DIR_SEPARATOR_S
"%s",
185 silcpurple_silcdir(), entity
, file
);
190 g_snprintf(file
, sizeof(file
) - 1, "%skey_%s_%d.pub", entity
,
192 g_snprintf(filename
, sizeof(filename
) - 1,
193 "%s" G_DIR_SEPARATOR_S
"%skeys" G_DIR_SEPARATOR_S
"%s",
194 silcpurple_silcdir(), entity
, file
);
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
);
214 verify
= silc_calloc(1, sizeof(*verify
));
217 verify
->client
= client
;
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
))
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
);
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
);
250 /* Encode the key data */
251 encpk
= silc_pkcs_public_key_encode(public_key
, &encpk_len
);
253 silcpurple_verify_ask(name
? name
: entity
,
254 fingerprint
, babbleprint
, verify
);
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
);
267 /* Local copy matched */
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
);