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 
silcpurple_verify_cb(PublicKeyVerify verify,gint id)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 
silcpurple_verify_details_cb(PublicKeyVerify verify)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 
silcpurple_verify_details(PublicKeyVerify verify,gint id)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 
silcpurple_verify_ask(const char * entity,const char * fingerprint,const char * babbleprint,PublicKeyVerify verify)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 
silcpurple_verify_public_key(SilcClient client,SilcClientConnection conn,const char * name,SilcSocketType conn_type,unsigned char * pk,SilcUInt32 pk_len,SilcSKEPKType pk_type,SilcVerifyPublicKey completion,void * context)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 }
275