1 /* ************************************************************************** */
2 /* */
3 /* Copyright (C) 2006 Benjamin Drieu (bdrieu@april.org) */
4 /* http://www.grisbi.org/ */
5 /* */
6 /* This program is free software; you can redistribute it and/or modify */
7 /* it under the terms of the GNU General Public License as published by */
8 /* the Free Software Foundation; either version 2 of the License, or */
9 /* (at your option) any later version. */
10 /* */
11 /* This program is distributed in the hope that it will be useful, */
12 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
13 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
14 /* GNU General Public License for more details. */
15 /* */
16 /* You should have received a copy of the GNU General Public License */
17 /* along with this program; if not, write to the Free Software */
18 /* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
19 /* */
20 /* ************************************************************************** */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include "include.h"
27 #include <glib/gi18n.h>
28
29 /* This define is required to disable openssl's SSLeay support which redefines
30 * _(), which obvisouly breaks glib's gettext macros. */
31 #define OPENSSL_DISABLE_OLD_DES_SUPPORT
32 #include <openssl/des.h>
33
34 /*START_INCLUDE*/
35 #include "openssl.h"
36 #include "grisbi_app.h"
37 #include "structures.h"
38 #include "utils.h"
39 #include "dialog.h"
40 /*END_INCLUDE*/
41
42 /*START_EXTERN*/
43 /*END_EXTERN*/
44
45 /*START_STATIC*/
46 static gchar *gsb_file_util_ask_for_crypt_key ( const gchar * file_name, gchar * additional_message,
47 gboolean encrypt );
48 static void gsb_file_util_show_hide_passwd ( GtkToggleButton *togglebutton, GtkWidget *entry );
49
gsb_file_util_show_hide_passwd_from_icon(GtkEntry * entry,GtkEntryIconPosition icon_pos,GdkEvent * event,gpointer null)50 static void gsb_file_util_show_hide_passwd_from_icon (GtkEntry *entry,
51 GtkEntryIconPosition icon_pos,
52 GdkEvent *event,
53 gpointer null)
54 {
55 GdkPixbuf *pixbuf;
56 gint visibility;
57
58 visibility = gtk_entry_get_visibility (GTK_ENTRY (entry) );
59 if (visibility)
60 {
61 pixbuf = g_object_get_data (G_OBJECT (entry), "pixbuf_1");
62 gtk_entry_set_icon_from_pixbuf (GTK_ENTRY (entry), GTK_ENTRY_ICON_SECONDARY, pixbuf);
63 }
64 else
65 {
66 pixbuf = g_object_get_data (G_OBJECT (entry), "pixbuf_2");
67 gtk_entry_set_icon_from_pixbuf (GTK_ENTRY (entry), GTK_ENTRY_ICON_SECONDARY, pixbuf);
68 }
69 gtk_entry_set_visibility (GTK_ENTRY (entry), !visibility);
70
71 }
72
73 /*END_STATIC*/
74
75 static gchar *saved_crypt_key = NULL;
76
77 #define V1_MARKER "Grisbi encrypted file "
78 #define V1_MARKER_SIZE (sizeof(V1_MARKER) - 1)
79 #define V2_MARKER "Grisbi encryption v2: "
80 #define V2_MARKER_SIZE (sizeof(V2_MARKER) - 1)
81
82 #define ALIGN_TO_8_BYTES(l) ((l + 7) & (~7))
83
84
85
86 /**
87 *
88 */
89 static gulong
encrypt_v2(gchar * password,gchar ** file_content,gulong length)90 encrypt_v2(gchar *password, gchar **file_content, gulong length)
91 {
92 DES_cblock key;
93 DES_key_schedule sched;
94 gulong to_encrypt_length, output_length;
95 gchar *to_encrypt_content, *output_content;
96
97 /* Create a temporary buffer that will hold data to be encrypted. */
98 to_encrypt_length = V2_MARKER_SIZE + length;
99 to_encrypt_content = g_malloc ( to_encrypt_length );
100 memmove ( to_encrypt_content, V2_MARKER, V2_MARKER_SIZE );
101 memmove ( to_encrypt_content + V2_MARKER_SIZE, *file_content, length );
102
103 /* Allocate the output file and copy the special marker at its beginning.
104 * DES_cbc_encrypt output is always a multiple of 8 bytes. Adjust the
105 * length of the allocation accordingly. */
106 output_length = V2_MARKER_SIZE + ALIGN_TO_8_BYTES ( to_encrypt_length );
107 output_content = g_malloc ( output_length );
108 memmove ( output_content, V2_MARKER , V2_MARKER_SIZE );
109
110 /* Encrypt the data and put it in the right place in the output buffer. */
111 DES_string_to_key ( password, &key );
112 DES_set_key_unchecked ( &key, &sched );
113 DES_set_odd_parity ( &key );
114
115 DES_cbc_encrypt ( (guchar *) to_encrypt_content,
116 (guchar *) (output_content + V2_MARKER_SIZE),
117 to_encrypt_length,
118 &sched,
119 &key,
120 DES_ENCRYPT );
121
122 g_free ( to_encrypt_content );
123
124 *file_content = output_content;
125 return output_length;
126 }
127
128
129 /**
130 *
131 */
132 static gulong
decrypt_v2(gchar * password,gchar ** file_content,gulong length)133 decrypt_v2(gchar *password, gchar **file_content, gulong length)
134 {
135 DES_cblock key;
136 DES_key_schedule sched;
137 gulong decrypted_len, output_len;
138 gchar *decrypted_buf, *output_buf;
139
140 /* Create a temporary buffer that will hold the decrypted data without the
141 * first marker. */
142 decrypted_len = length - V2_MARKER_SIZE;
143 decrypted_buf = g_malloc ( decrypted_len );
144
145 DES_string_to_key ( password, &key );
146 DES_set_key_unchecked( &key, &sched );
147 DES_set_odd_parity ( &key );
148
149 DES_cbc_encrypt ( (guchar *) (* file_content + V2_MARKER_SIZE),
150 (guchar *) decrypted_buf,
151 (long) decrypted_len,
152 &sched,
153 &key,
154 DES_DECRYPT );
155
156 /* If the password was correct, the second marker should appear in the first
157 * few bytes of the decrypted content. */
158 if ( strncmp ( decrypted_buf, V2_MARKER, V2_MARKER_SIZE ) )
159 {
160 g_free ( decrypted_buf );
161 return 0;
162 }
163
164 /* Copy the decrypted data to a final buffer, leaving out the second
165 * marker. g_strndup() is used to add a trailing null byte. */
166 output_len = decrypted_len - V2_MARKER_SIZE;
167 output_buf = g_strndup ( decrypted_buf + V2_MARKER_SIZE, output_len );
168
169 g_free ( decrypted_buf );
170
171 *file_content = output_buf;
172 return output_len;
173 }
174
175
176 /**
177 *
178 */
179 static gulong
decrypt_v1(gchar * password,gchar ** file_content,gulong length)180 decrypt_v1(gchar *password, gchar **file_content, gulong length)
181 {
182 DES_cblock key;
183 DES_key_schedule sched;
184 gulong decrypted_len;
185 gchar *decrypted_buf;
186
187 /* Create a temporary buffer that will hold the decrypted data without the
188 * marker. A trailing null byte is also added. */
189 decrypted_len = length - V1_MARKER_SIZE;
190 decrypted_buf = g_malloc ( decrypted_len + 1 );
191 decrypted_buf[decrypted_len] = 0;
192
193 DES_string_to_key ( password, &key );
194 DES_set_key_unchecked( &key, &sched );
195 DES_set_odd_parity ( &key );
196 /* Set the DES key the WRONG AND BROKEN way. DO NOT REUSE THIS CODE EVER! */
197 memset ( &key, 0, sizeof ( DES_cblock ) );
198 memmove ( &key, password, MIN(sizeof(DES_cblock), strlen(password)) );
199
200 DES_cbc_encrypt ( (guchar *) (* file_content + V1_MARKER_SIZE),
201 (guchar *) decrypted_buf,
202 (long) decrypted_len,
203 &sched,
204 &key,
205 DES_DECRYPT );
206
207 /* If the password was correct, the first few bytes of the decrypted
208 * content should contain the following strings. */
209 if ( strncmp ( decrypted_buf, "<?xml version=\"1.0\"?>", 18 )
210 &&
211 strncmp ( decrypted_buf, "Grisbi compressed file ", 23 ) )
212 {
213 g_free ( decrypted_buf );
214 return 0;
215 }
216
217 *file_content = decrypted_buf;
218 return decrypted_len;
219 }
220
221
222 /**
223 * Crypt or decrypt string given in the param
224 *
225 * \param file_name File name, used to
226 * \param file_content A string which is the file
227 * \param crypt TRUE to crypt, FALSE to uncrypt
228 * \param length The length of the grisbi data,
229 * without "Grisbi encrypted file " if comes to crypt
230 * with "Grisbi encrypted file " if comes to decrypt
231 *
232 * \return the length of the new file_content or 0 if problem
233 */
gsb_file_util_crypt_file(const gchar * file_name,gchar ** file_content,gboolean crypt,gulong length)234 gulong gsb_file_util_crypt_file ( const gchar * file_name, gchar **file_content,
235 gboolean crypt, gulong length )
236 {
237 gchar * key, * message = NULL;
238 GrisbiWinRun *w_run;
239
240 w_run = grisbi_win_get_w_run ();
241 if (w_run->new_crypted_file )
242 {
243 if ( saved_crypt_key )
244 {
245 g_free ( saved_crypt_key );
246 }
247 saved_crypt_key = NULL;
248 }
249 if ( crypt )
250 {
251 /* now, if we know here a key to crypt, we use it, else, we ask for it */
252
253 if ( saved_crypt_key )
254 key = saved_crypt_key;
255 else
256 key = gsb_file_util_ask_for_crypt_key ( file_name, message, TRUE);
257
258 /* if we have no key, we will no crypt that file */
259
260 if ( !key )
261 return 0;
262
263 return encrypt_v2 ( key, file_content, length );
264 }
265 else
266 {
267 gulong returned_length;
268
269 return_bad_password:
270
271 /* now, if we know here a key to crypt, we use it, else, we ask for it */
272
273 if ( saved_crypt_key )
274 key = saved_crypt_key;
275 else
276 key = gsb_file_util_ask_for_crypt_key ( file_name, message, FALSE );
277
278 /* if we have no key, we stop the loading */
279
280 if ( !key )
281 return 0;
282
283 returned_length = decrypt_v2 ( key, file_content, length );
284
285 if ( returned_length == 0 )
286 returned_length = decrypt_v1 ( key, file_content, length );
287
288 if ( returned_length == 0 )
289 {
290 /* it seems that it was not the correct password */
291
292 message = _( "<span weight=\"bold\" foreground=\"red\">Password is incorrect!</span>\n\n");
293 g_free ( saved_crypt_key );
294 saved_crypt_key = NULL;
295 goto return_bad_password;
296 }
297
298 return returned_length;
299 }
300
301 return 0;
302 }
303
304
305
306 /**
307 * ask for the crypting key
308 * return the key, and save it in the variable crypt_key if asked
309 *
310 * \param encrypt : TRUE if comes to encrypt, FALSE to decrypt
311 *
312 * \return a string which is the crypt key or NULL if it was
313 * cancelled. */
gsb_file_util_ask_for_crypt_key(const gchar * file_name,gchar * additional_message,gboolean encrypt)314 gchar *gsb_file_util_ask_for_crypt_key ( const gchar * file_name, gchar * additional_message,
315 gboolean encrypt )
316 {
317 GdkPixbuf *pixbuf_1;
318 GdkPixbuf *pixbuf_2;
319 gchar *key = NULL;
320 GtkWidget *dialog, *button = NULL, *label, *entry, *hbox, *hbox2, *vbox, *icon;
321 gchar *tmp_msg;
322 gint result;
323 GrisbiWinRun *w_run;
324
325 w_run = grisbi_win_get_w_run ();
326
327 dialog = gtk_dialog_new_with_buttons ( _("Grisbi password"),
328 GTK_WINDOW ( grisbi_app_get_active_window (NULL) ),
329 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
330 "gtk-cancel", GTK_RESPONSE_CANCEL,
331 ( encrypt ? _("Crypt file") : _("Decrypt file") ), GTK_RESPONSE_OK,
332 NULL );
333
334 gtk_window_set_position ( GTK_WINDOW ( dialog ), GTK_WIN_POS_CENTER_ON_PARENT );
335 gtk_window_set_resizable ( GTK_WINDOW ( dialog ), FALSE );
336 gtk_dialog_set_default_response ( GTK_DIALOG ( dialog ), GTK_RESPONSE_OK );
337
338 hbox = gtk_box_new ( GTK_ORIENTATION_HORIZONTAL, MARGIN_BOX );
339 gtk_box_pack_start ( GTK_BOX ( gtk_dialog_get_content_area ( GTK_DIALOG ( dialog ) ) ), hbox, TRUE, TRUE, 6 );
340
341 /* Ugly dance to force alignement. */
342 vbox = gtk_box_new ( GTK_ORIENTATION_VERTICAL, MARGIN_BOX );
343 gtk_box_pack_start ( GTK_BOX ( hbox ), vbox, FALSE, FALSE, 6 );
344 icon = gtk_image_new_from_icon_name ( "gtk-dialog-authentication", GTK_ICON_SIZE_DIALOG );
345 gtk_box_pack_start ( GTK_BOX ( vbox ), icon, FALSE, FALSE, 6 );
346
347 vbox = gtk_box_new ( GTK_ORIENTATION_VERTICAL, MARGIN_BOX );
348 gtk_box_pack_start ( GTK_BOX ( hbox ), vbox, TRUE, TRUE, 6 );
349
350 label = gtk_label_new ("");
351 gtk_label_set_justify ( GTK_LABEL(label), GTK_JUSTIFY_LEFT );
352 utils_labels_set_alignment ( GTK_LABEL (label), 0, 0);
353 gtk_label_set_line_wrap ( GTK_LABEL(label), TRUE );
354
355 if ( encrypt )
356 {
357 if (additional_message && strlen (additional_message))
358 tmp_msg = g_strdup_printf (_("%sPlease enter password to encrypt file\n<span "
359 "foreground=\"blue\">%s</span>"),
360 additional_message,
361 file_name);
362 else
363 tmp_msg = g_strdup_printf (_("Please enter password to encrypt file\n<span "
364 "foreground=\"blue\">%s</span>"),
365 file_name);
366 }
367 else
368 {
369 if (additional_message && strlen (additional_message))
370 tmp_msg = g_strdup_printf (_("%sPlease enter password to decrypt file\n<span "
371 "foreground=\"blue\">%s</span>"),
372 additional_message,
373 file_name);
374 else
375 tmp_msg = g_strdup_printf (_("Please enter password to decrypt file\n<span "
376 "foreground=\"blue\">%s</span>"),
377 file_name);
378 }
379
380 gtk_label_set_markup ( GTK_LABEL (label), tmp_msg);
381 gtk_box_pack_start ( GTK_BOX ( vbox ), label, FALSE, FALSE, 6 );
382 g_free (tmp_msg);
383
384 hbox2 = gtk_box_new ( GTK_ORIENTATION_HORIZONTAL, MARGIN_BOX );
385 gtk_box_pack_start ( GTK_BOX ( vbox ), hbox2, FALSE, FALSE, 6 );
386 gtk_box_pack_start ( GTK_BOX ( hbox2 ),
387 gtk_label_new ( _("Password: ") ),
388 FALSE, FALSE, 0 );
389
390 entry = gtk_entry_new ();
391 gtk_entry_set_activates_default ( GTK_ENTRY ( entry ), TRUE );
392 gtk_entry_set_visibility ( GTK_ENTRY ( entry ), FALSE );
393 gtk_box_pack_start ( GTK_BOX ( hbox2 ), entry, TRUE, TRUE, 0 );
394 pixbuf_1 = gdk_pixbuf_new_from_resource ("/org/gtk/grisbi/images/gtk-eye-not-looking.svg", NULL);
395 pixbuf_2 = gdk_pixbuf_new_from_resource ("/org/gtk/grisbi/images/gtk-eye-looking.svg", NULL);
396
397 if (pixbuf_1 && pixbuf_2)
398 {
399 gtk_entry_set_icon_from_pixbuf (GTK_ENTRY (entry), GTK_ENTRY_ICON_SECONDARY, pixbuf_1);
400 g_object_set_data_full (G_OBJECT (entry), "pixbuf_1", pixbuf_1, g_object_unref);
401 g_object_set_data_full (G_OBJECT (entry), "pixbuf_2", pixbuf_2, g_object_unref);
402
403 g_signal_connect (G_OBJECT (entry),
404 "icon-press",
405 G_CALLBACK (gsb_file_util_show_hide_passwd_from_icon),
406 NULL);
407 }
408 else if (w_run->new_crypted_file)
409 {
410 button = gtk_check_button_new_with_label ( _("View password") );
411 gtk_box_pack_start ( GTK_BOX ( vbox ), button, FALSE, FALSE, 5 );
412 g_signal_connect ( G_OBJECT ( button ),
413 "toggled",
414 G_CALLBACK ( gsb_file_util_show_hide_passwd ),
415 entry );
416 }
417 if (!encrypt)
418 {
419 button = gtk_check_button_new_with_label ( _("Don't ask password again for this session."));
420 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON ( button ), TRUE );
421 gtk_box_pack_start ( GTK_BOX ( vbox ), button, FALSE, FALSE, 5 );
422 }
423
424 gtk_widget_show_all ( dialog );
425
426 #ifdef __APPLE__
427 return_bad_password:
428 #endif /* __APPLE__ */
429 result = gtk_dialog_run ( GTK_DIALOG ( dialog ));
430
431 switch (result)
432 {
433 case GTK_RESPONSE_OK:
434 key = g_strdup (gtk_entry_get_text ( GTK_ENTRY ( entry )));
435
436 if (!strlen ( key ) )
437 {
438 g_free ( key );
439 key = NULL;
440 }
441 #ifdef __APPLE__
442 else if ( g_utf8_strlen ( key, -1 ) < 7 )
443 {
444 dialogue_warning_hint ( _("The password must contain at least 7 characters"),
445 _("Password too short" ) );
446 gtk_entry_set_text ( GTK_ENTRY ( entry ), "" );
447
448 goto return_bad_password;
449 }
450 #endif /* __APPLE__ */
451
452 if ( button && gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON ( button )))
453 saved_crypt_key = key;
454 else
455 saved_crypt_key = NULL;
456 w_run->new_crypted_file = FALSE;
457
458 break;
459
460 case GTK_RESPONSE_CANCEL:
461 key = NULL;
462 }
463
464 gtk_widget_destroy ( dialog );
465
466 return key;
467 }
468
gsb_file_util_show_hide_passwd(GtkToggleButton * togglebutton,GtkWidget * entry)469 void gsb_file_util_show_hide_passwd ( GtkToggleButton *togglebutton, GtkWidget *entry )
470 {
471 gint visibility;
472
473 visibility = gtk_entry_get_visibility ( GTK_ENTRY ( entry ) );
474 if ( visibility )
475 gtk_button_set_label ( GTK_BUTTON ( togglebutton ), _("View password") );
476 else
477 gtk_button_set_label ( GTK_BUTTON ( togglebutton ), _("Hide password") );
478
479 gtk_entry_set_visibility ( GTK_ENTRY ( entry ), !visibility );
480 }
481
482 /* Local Variables: */
483 /* c-basic-offset: 4 */
484 /* End: */
485