| |
1 /* |
| |
2 |
| |
3 silcgaim_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 "silcgaim.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 silcgaim_verify_ask(const char *entity, |
| |
43 const char *fingerprint, |
| |
44 const char *babbleprint, |
| |
45 PublicKeyVerify verify); |
| |
46 |
| |
47 static void silcgaim_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 silcgaim_verify_details_cb(PublicKeyVerify verify) |
| |
71 { |
| |
72 /* What a hack. We have to display the accept dialog _again_ |
| |
73 because Gaim closes the dialog after you press the button. Gaim |
| |
74 should have option for the dialogs whether the buttons close them |
| |
75 or not. */ |
| |
76 silcgaim_verify_ask(verify->entity, verify->fingerprint, |
| |
77 verify->babbleprint, verify); |
| |
78 } |
| |
79 |
| |
80 static void silcgaim_verify_details(PublicKeyVerify verify, gint id) |
| |
81 { |
| |
82 SilcPublicKey public_key; |
| |
83 GaimConnection *gc = verify->client->application; |
| |
84 SilcGaim sg = gc->proto_data; |
| |
85 |
| |
86 silc_pkcs_public_key_decode(verify->pk, verify->pk_len, |
| |
87 &public_key); |
| |
88 silcgaim_show_public_key(sg, verify->entity_name, public_key, |
| |
89 G_CALLBACK(silcgaim_verify_details_cb), |
| |
90 verify); |
| |
91 silc_pkcs_public_key_free(public_key); |
| |
92 } |
| |
93 |
| |
94 static void silcgaim_verify_ask(const char *entity, |
| |
95 const char *fingerprint, |
| |
96 const char *babbleprint, |
| |
97 PublicKeyVerify verify) |
| |
98 { |
| |
99 char tmp[256], tmp2[256]; |
| |
100 |
| |
101 if (verify->changed) { |
| |
102 g_snprintf(tmp, sizeof(tmp), |
| |
103 _("Received %s's public key. Your local copy does not match this " |
| |
104 "key. Would you still like to accept this public key?"), |
| |
105 entity); |
| |
106 } else { |
| |
107 g_snprintf(tmp, sizeof(tmp), |
| |
108 _("Received %s's public key. Would you like to accept this " |
| |
109 "public key?"), entity); |
| |
110 } |
| |
111 g_snprintf(tmp2, sizeof(tmp2), |
| |
112 _("Fingerprint and babbleprint for the %s key are:\n\n" |
| |
113 "%s\n%s\n"), entity, fingerprint, babbleprint); |
| |
114 |
| |
115 gaim_request_action(verify->client->application, _("Verify Public Key"), tmp, tmp2, |
| |
116 GAIM_DEFAULT_ACTION_NONE, verify, 3, |
| |
117 _("Yes"), G_CALLBACK(silcgaim_verify_cb), |
| |
118 _("No"), G_CALLBACK(silcgaim_verify_cb), |
| |
119 _("_View..."), G_CALLBACK(silcgaim_verify_details)); |
| |
120 } |
| |
121 |
| |
122 void silcgaim_verify_public_key(SilcClient client, SilcClientConnection conn, |
| |
123 const char *name, SilcSocketType conn_type, |
| |
124 unsigned char *pk, SilcUInt32 pk_len, |
| |
125 SilcSKEPKType pk_type, |
| |
126 SilcVerifyPublicKey completion, void *context) |
| |
127 { |
| |
128 GaimConnection *gc = client->application; |
| |
129 int i; |
| |
130 char file[256], filename[256], filename2[256], *ipf, *hostf = NULL; |
| |
131 char *fingerprint, *babbleprint; |
| |
132 struct passwd *pw; |
| |
133 struct stat st; |
| |
134 char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER || |
| |
135 conn_type == SILC_SOCKET_TYPE_ROUTER) ? |
| |
136 "server" : "client"); |
| |
137 PublicKeyVerify verify; |
| |
138 |
| |
139 if (pk_type != SILC_SKE_PK_TYPE_SILC) { |
| |
140 gaim_notify_error(gc, _("Verify Public Key"), |
| |
141 _("Unsupported public key type"), NULL); |
| |
142 if (completion) |
| |
143 completion(FALSE, context); |
| |
144 return; |
| |
145 } |
| |
146 |
| |
147 pw = getpwuid(getuid()); |
| |
148 if (!pw) { |
| |
149 if (completion) |
| |
150 completion(FALSE, context); |
| |
151 return; |
| |
152 } |
| |
153 |
| |
154 memset(filename, 0, sizeof(filename)); |
| |
155 memset(filename2, 0, sizeof(filename2)); |
| |
156 memset(file, 0, sizeof(file)); |
| |
157 |
| |
158 if (conn_type == SILC_SOCKET_TYPE_SERVER || |
| |
159 conn_type == SILC_SOCKET_TYPE_ROUTER) { |
| |
160 if (!name) { |
| |
161 g_snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, |
| |
162 conn->sock->ip, conn->sock->port); |
| |
163 g_snprintf(filename, sizeof(filename) - 1, |
| |
164 "%s" G_DIR_SEPARATOR_S "%skeys" G_DIR_SEPARATOR_S "%s", |
| |
165 silcgaim_silcdir(), entity, file); |
| |
166 |
| |
167 g_snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, |
| |
168 conn->sock->hostname, conn->sock->port); |
| |
169 g_snprintf(filename2, sizeof(filename2) - 1, |
| |
170 "%s" G_DIR_SEPARATOR_S "%skeys" G_DIR_SEPARATOR_S "%s", |
| |
171 silcgaim_silcdir(), entity, file); |
| |
172 |
| |
173 ipf = filename; |
| |
174 hostf = filename2; |
| |
175 } else { |
| |
176 g_snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, |
| |
177 name, conn->sock->port); |
| |
178 g_snprintf(filename, sizeof(filename) - 1, |
| |
179 "%s" G_DIR_SEPARATOR_S "%skeys" G_DIR_SEPARATOR_S "%s", |
| |
180 silcgaim_silcdir(), entity, file); |
| |
181 |
| |
182 ipf = filename; |
| |
183 } |
| |
184 } else { |
| |
185 /* Replace all whitespaces with `_'. */ |
| |
186 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len); |
| |
187 for (i = 0; i < strlen(fingerprint); i++) |
| |
188 if (fingerprint[i] == ' ') |
| |
189 fingerprint[i] = '_'; |
| |
190 |
| |
191 g_snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint); |
| |
192 g_snprintf(filename, sizeof(filename) - 1, |
| |
193 "%s" G_DIR_SEPARATOR_S "%skeys" G_DIR_SEPARATOR_S "%s", |
| |
194 silcgaim_silcdir(), entity, file); |
| |
195 silc_free(fingerprint); |
| |
196 |
| |
197 ipf = filename; |
| |
198 } |
| |
199 |
| |
200 verify = silc_calloc(1, sizeof(*verify)); |
| |
201 if (!verify) |
| |
202 return; |
| |
203 verify->client = client; |
| |
204 verify->conn = conn; |
| |
205 verify->filename = strdup(ipf); |
| |
206 verify->entity = strdup(entity); |
| |
207 verify->entity_name = (conn_type != SILC_SOCKET_TYPE_CLIENT ? |
| |
208 (name ? strdup(name) : strdup(conn->sock->hostname)) |
| |
209 : NULL); |
| |
210 verify->pk = silc_memdup(pk, pk_len); |
| |
211 verify->pk_len = pk_len; |
| |
212 verify->pk_type = pk_type; |
| |
213 verify->completion = completion; |
| |
214 verify->context = context; |
| |
215 fingerprint = verify->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len); |
| |
216 babbleprint = verify->babbleprint = silc_hash_babbleprint(NULL, pk, pk_len); |
| |
217 |
| |
218 /* Check whether this key already exists */ |
| |
219 if (g_stat(ipf, &st) < 0 && (!hostf || g_stat(hostf, &st) < 0)) { |
| |
220 /* Key does not exist, ask user to verify the key and save it */ |
| |
221 silcgaim_verify_ask(name ? name : entity, |
| |
222 fingerprint, babbleprint, verify); |
| |
223 return; |
| |
224 } else { |
| |
225 /* The key already exists, verify it. */ |
| |
226 SilcPublicKey public_key; |
| |
227 unsigned char *encpk; |
| |
228 SilcUInt32 encpk_len; |
| |
229 |
| |
230 /* Load the key file, try for both IP filename and hostname filename */ |
| |
231 if (!silc_pkcs_load_public_key(ipf, &public_key, |
| |
232 SILC_PKCS_FILE_PEM) && |
| |
233 !silc_pkcs_load_public_key(ipf, &public_key, |
| |
234 SILC_PKCS_FILE_BIN) && |
| |
235 (!hostf || (!silc_pkcs_load_public_key(hostf, &public_key, |
| |
236 SILC_PKCS_FILE_PEM) && |
| |
237 !silc_pkcs_load_public_key(hostf, &public_key, |
| |
238 SILC_PKCS_FILE_BIN)))) { |
| |
239 silcgaim_verify_ask(name ? name : entity, |
| |
240 fingerprint, babbleprint, verify); |
| |
241 return; |
| |
242 } |
| |
243 |
| |
244 /* Encode the key data */ |
| |
245 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len); |
| |
246 if (!encpk) { |
| |
247 silcgaim_verify_ask(name ? name : entity, |
| |
248 fingerprint, babbleprint, verify); |
| |
249 return; |
| |
250 } |
| |
251 |
| |
252 /* Compare the keys */ |
| |
253 if (memcmp(encpk, pk, encpk_len)) { |
| |
254 /* Ask user to verify the key and save it */ |
| |
255 verify->changed = TRUE; |
| |
256 silcgaim_verify_ask(name ? name : entity, |
| |
257 fingerprint, babbleprint, verify); |
| |
258 return; |
| |
259 } |
| |
260 |
| |
261 /* Local copy matched */ |
| |
262 if (completion) |
| |
263 completion(TRUE, context); |
| |
264 silc_free(verify->filename); |
| |
265 silc_free(verify->entity); |
| |
266 silc_free(verify->entity_name); |
| |
267 silc_free(verify->pk); |
| |
268 silc_free(verify->fingerprint); |
| |
269 silc_free(verify->babbleprint); |
| |
270 silc_free(verify); |
| |
271 } |
| |
272 } |