password.bac
'
' This is a remake of my Password Manager program. I am actually using this
' program to store all my passwords.
'
' Blowfish-cbc encyption with:
' openssl enc -e -a -salt -bf -md md5 -in data.txt -out data.pwd -pass pass:test
'
' Simple frontend for SSL to keep passwords in a safe place.
'
' (c) Peter van Eerten, February 2010 - GPL. Use with HUG 0.16 or higher.
'
' Version 1.0: Initial release
' Version 1.1: Capture wrong file reading, show total entries
' Version 1.2: Adapted for Cancel-button in FILEDIALOG
' Version 1.3: Compliancy with BaCon 1.0 build 11
' Version 1.4: Compliancy with BaCon 1.0 build 26
' Version 1.5: - Major code updates, now requires BaCon 3.5 and higher
' - Added md5 hash to let newer OpenSSL open old .pwd files
' Version 1.6: Added -pbkdf2 option.
' Version 1.7: Added providers for decrypt and encrypt (OpenSSL 1.3).
'-------------------------------------------------------------------
SETENVIRON "LANG", "C"
INCLUDE hug
CONST GTK_JUSTIFY_LEFT = 0
' Data is stored as a delim string
GLOBAL Pw_Data$
'-------------------------------------------------------------------
SUB Toggle_Visible
IF GET(chk) = 1 THEN
SET(pw, TRUE)
ELSE
SET(pw, FALSE)
ENDIF
END SUB
'-------------------------------------------------------------------
SUB Show_File_Dlg
SHOW(file_dlg)
END SUB
'-------------------------------------------------------------------
SUB Show_Save_Dlg
IF AMOUNT(Pw_Data$, NL$) THEN SHOW(save_dlg)
END SUB
'-------------------------------------------------------------------
SUB Close_Dialog(NUMBER widget)
HIDE(widget)
FOCUS(pw)
END SUB
'-------------------------------------------------------------------
SUB Refresh_List
LOCAL line$
' Put into list
TEXT(lst, "")
FOR line$ IN Pw_Data$ STEP NL$
TEXT(lst, TOKEN$(line$, 1, CHR$(9)))
NEXT
TEXT(user_lbl_name, "")
TEXT(pass_lbl_name, "")
TEXT(mainwin, "Password Manager - " & STR$(AMOUNT(Pw_Data$, NL$)) & " entries.")
ENDSUB
'-------------------------------------------------------------------
SUB Hide_File_Dlg(NUMBER dialog, int button)
HIDE(dialog)
IF button <> GTK_RESPONSE_CANCEL THEN
' Decrypt using unlock key and put into list
Pw_Data$ = SORT$(CHOP$(EXEC$("openssl enc -d -a -salt -pbkdf2 -bf -md md5 -provider legacy -provider default -in " & GRAB$(dialog) & " -pass pass:" & GRAB$(pw) & " 2>&1")), NL$)
IF TALLY(Pw_Data$, "bad decrypt") THEN
SHOW(errdlg1)
EXIT SUB
ELIF TALLY(Pw_Data$, "error") THEN
SHOW(errdlg3)
EXIT SUB
END IF
Refresh_List()
ENDIF
ENDSUB
'-------------------------------------------------------------------
SUB Save_Data(NUMBER dialog, int button)
HIDE(dialog)
IF button = GTK_RESPONSE_YES THEN
' Encrypt using unlock key and store as file
SYSTEM "echo \"" & Pw_Data$ & "\" | openssl enc -e -a -salt -pbkdf2 -bf -md md5 -provider legacy -provider default -out \"" & GRAB$(save_dlg) & "\" -pass pass:" & GRAB$(pw) & " 2>&1"
SHOW(cfrmdlg3)
END IF
END SUB
'-------------------------------------------------------------------
SUB Hide_Save_Dlg(NUMBER dialog, int button)
HIDE(dialog)
IF button <> GTK_RESPONSE_CANCEL THEN
IF FILEEXISTS(GRAB$(save_dlg)) THEN
SHOW(cfrmdlg2)
ELIF LEN(GRAB$(save_dlg)) > 0 THEN
Save_Data(cfrmdlg2, GTK_RESPONSE_YES)
ENDIF
ENDIF
ENDSUB
'-------------------------------------------------------------------
SUB Lookup_Data
LOCAL line$
line$ = TOKEN$(Pw_Data$, GET(lst)+1, NL$)
' Show data
TEXT(user_lbl_name, TOKEN$(line$, 2, CHR$(9)))
TEXT(pass_lbl_name, TOKEN$(line$, 3, CHR$(9)))
END SUB
'-------------------------------------------------------------------
SUB Delete_Entry(NUMBER widget, int button)
' Hide the confirmation dialog
HIDE(widget)
IF button = GTK_RESPONSE_YES THEN
' Delete the selected entry
Pw_Data$ = DEL$(Pw_Data$, GET(lst)+1, NL$)
Refresh_List()
END IF
END SUB
'-------------------------------------------------------------------
SUB Delete_Confirm
IF AMOUNT(Pw_Data$, NL$) AND GET(lst) >= 0 THEN SHOW(cfrmdlg1)
END SUB
'-------------------------------------------------------------------
SUB Process_Entry
' Hide the dialog
HIDE(subwin)
' Are we editing or adding
IF GRAB$(subwin) = "Edit existing entry" THEN
Pw_Data$ = CHANGE$(Pw_Data$, GET(lst)+1, GRAB$(entry1) & CHR$(9) & GRAB$(entry2) & CHR$(9) & GRAB$(entry3), NL$)
ELSE
Pw_Data$ = SORT$(APPEND$(Pw_Data$, 0, GRAB$(entry1) & CHR$(9) & GRAB$(entry2) & CHR$(9) & GRAB$(entry3), NL$), NL$)
END IF
Refresh_List()
END SUB
'-------------------------------------------------------------------
SUB Add_Entry
TEXT(subwin, "Add new entry")
' Cleanup
TEXT(entry1, "")
TEXT(entry2, "")
TEXT(entry3, "")
FOCUS(entry1)
SHOW(subwin)
ENDSUB
'-------------------------------------------------------------------
SUB Edit_Entry
LOCAL line$
TEXT(subwin, "Edit existing entry")
IF GET(lst) >= 0 THEN
line$ = TOKEN$(Pw_Data$, GET(lst)+1, NL$)
TEXT(entry1, TOKEN$(line$, 1, CHR$(9)))
TEXT(entry2, TOKEN$(line$, 2, CHR$(9)))
TEXT(entry3, TOKEN$(line$, 3, CHR$(9)))
SHOW(subwin)
ENDIF
ENDSUB
'-------------------------------------------------------------------
SUB Close_Subwin
HIDE(subwin)
END SUB
'-------------------------------------------------------------------
' Check for OpenSSL on the system
IF ISFALSE(LEN(EXEC$("which openssl 2>/dev/null"))) THEN
PRINT "No OpenSSL found on this system! Exiting..."
END
ENDIF
' Create main window
mainwin = WINDOW("Password Manager", 400, 300)
' Top frame
frame1 = FRAME(390, 50)
ATTACH(mainwin, frame1, 5, 5)
label = MARK("Unlock key: ", 80, 30)
ATTACH(mainwin, label, 15, 15)
pw = PASSWORD(195, 30)
ATTACH(mainwin, pw, 100, 15)
chk = CHECK("Visible", 70, 30)
ATTACH(mainwin, chk, 315, 15)
' Body frame
frame2 = FRAME(300, 235)
ATTACH(mainwin, frame2, 5, 60)
lst = LIST(280, 150)
ATTACH(mainwin, lst, 15, 70)
user_label = MARK("Username: ", 80, 30)
ATTACH(mainwin, user_label, 15, 230)
user_lbl_name = MARK("|------------------------------------|", 260, 30)
SET(user_lbl_name, 1)
gtk_misc_set_alignment(user_lbl_name, 0, 0.5)
ATTACH(mainwin, user_lbl_name, 95, 230)
pass_label = MARK("Password: ", 80, 30)
ATTACH(mainwin, pass_label, 15, 260)
pass_lbl_name = MARK("|------------------------------------|", 260, 30)
SET(pass_lbl_name, 1)
gtk_misc_set_alignment(pass_lbl_name, 0, 0.5)
ATTACH(mainwin, pass_lbl_name, 95, 260)
' Buttons
file_btn = STOCK("gtk-open", 85, 35)
ATTACH(mainwin, file_btn, 310, 60)
add_btn = STOCK("gtk-add", 85, 35)
ATTACH(mainwin, add_btn, 310, 100)
edit_btn = STOCK("gtk-edit", 85, 35)
ATTACH(mainwin, edit_btn, 310, 140)
del_btn = STOCK("gtk-delete", 85, 35)
ATTACH(mainwin, del_btn, 310, 180)
save_btn = STOCK("gtk-save", 85, 35)
ATTACH(mainwin, save_btn, 310, 220)
quit_btn = STOCK("gtk-quit", 85, 35)
ATTACH(mainwin, quit_btn, 310, 260)
' Define subwindow for adding/editing entries
subwin = WINDOW("Add new entry", 300, 175)
frame3 = FRAME(290, 165)
ATTACH(subwin, frame3, 5, 5)
label1 = MARK(" Instance: ", 90, 30)
ATTACH(subwin, label1, 15, 15)
entry1 = ENTRY("", 170, 30)
ATTACH(subwin, entry1, 115, 15)
label2 = MARK(" User name: ", 90, 30)
ATTACH(subwin, label2, 15, 50)
entry2 = ENTRY("", 170, 30)
ATTACH(subwin, entry2, 115, 50)
label3 = MARK(" Password: ", 90, 30)
ATTACH(subwin, label3, 15, 85)
entry3 = ENTRY("", 170, 30)
ATTACH(subwin, entry3, 115, 85)
ok_btn = STOCK("gtk-ok", 80, 35)
ATTACH(subwin, ok_btn, 205, 125)
can_btn = STOCK("gtk-cancel", 80, 35)
ATTACH(subwin, can_btn, 115, 125)
Close_Subwin
' Create filebrowser, save dialog
file_dlg = FILEDIALOG("Select file...", "gtk-open", 200, 80, 0)
save_dlg = FILEDIALOG("Save as file...", "gtk-save", 200, 80, 1)
' Some info messages
errdlg1 = MSGDIALOG("Wrong unlock key entered!", 250, 120, 3, 2)
errdlg2 = MSGDIALOG("No space left to store new entries!", 350, 140, 3, 2)
errdlg3 = MSGDIALOG("Could not read file!", 250, 120, 3, 2)
cfrmdlg1 = MSGDIALOG("Are you sure to delete this entry?", 350, 140, 2, 4)
cfrmdlg2 = MSGDIALOG("File exists! Overwrite?", 250, 120, 2, 4)
cfrmdlg3 = MSGDIALOG("File saved.", 250, 120, 0, 1)
' Define the callbacks
CALLBACK(quit_btn, QUIT)
CALLBACK(chk, Toggle_Visible)
CALLBACK(file_btn, Show_File_Dlg)
CALLBACK(file_dlg, Hide_File_Dlg)
CALLBACK(add_btn, Add_Entry)
CALLBACK(edit_btn, Edit_Entry)
CALLBACK(del_btn, Delete_Confirm)
CALLBACK(lst, Lookup_Data)
CALLBACK(save_btn, Show_Save_Dlg)
CALLBACK(save_dlg, Hide_Save_Dlg)
CALLBACK(can_btn, Close_Subwin)
CALLBACK(ok_btn, Process_Entry)
CALLBACK(errdlg1, Close_Dialog)
CALLBACK(errdlg2, Close_Dialog)
CALLBACK(errdlg3, Close_Dialog)
CALLBACK(cfrmdlg1, Delete_Entry)
CALLBACK(cfrmdlg2, Save_Data)
CALLBACK(cfrmdlg3, Close_Dialog)
FOCUS(pw)
' Endless GTK loop
DISPLAY
Generated by GNU Enscript 1.6.5.90.