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