SVNからのミラー
15
CHANGELOG.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# 1.1.1 (2025年03月13日)
|
||||
* OTPで、秘密鍵を自動的に大文字にする様に
|
||||
|
||||
# 1.1.0 (2024年12月17日)
|
||||
* パスワード表示で、「OpenPGP」かどうかの確認の追加
|
||||
* 侵害されたパスワードの確認の追加
|
||||
* 複数サイトで同じパスワードを利用かどうか、パスワードの長さ、又はパスワードの強さの確認
|
||||
* パスワードを表示・非表示にする機能性の追加(デフォルトは非表示)
|
||||
* ワンタイムパスワード(OTP)の場合、自動的に更新する様に
|
||||
* アプリケーションアイコンの追加
|
||||
* ライトモードの追加
|
||||
* コンフィグファイルの追加
|
||||
|
||||
# 1.0.0 (2024年09月24日)
|
||||
* 開始
|
||||
13
LICENSE.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
Copyright © 2018-2024 by 076.moe
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
|
||||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
|
||||
CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
||||
DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
||||
ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
||||
SOFTWARE.
|
||||
126
Makefile
Normal file
@@ -0,0 +1,126 @@
|
||||
UNAME_S != uname -s
|
||||
UNAME_M != uname -m
|
||||
OS = ${UNAME_S}
|
||||
ARCH = ${UNAME_M}
|
||||
|
||||
.if ${UNAME_S} == "OpenBSD"
|
||||
OS = openbsd
|
||||
.elif ${UNAME_S} == "NetBSD"
|
||||
OS = netbsd
|
||||
.elif ${UNAME_S} == "FreeBSD"
|
||||
OS = freebsd
|
||||
.elif ${UNAME_S} == "Dragonfly"
|
||||
OS = dragonfly
|
||||
.elif ${UNAME_S} == "Linux"
|
||||
OS = linux
|
||||
.endif
|
||||
|
||||
.if ${UNAME_M} == "x86_64"
|
||||
ARCH = amd64
|
||||
.endif
|
||||
|
||||
NAME != cat main.cc | grep "const char \*sofname" | awk '{print $$5}' |\
|
||||
sed "s/\"//g" | sed "s/;//"
|
||||
VERSION != cat main.cc | grep "const char \*version" | awk '{print $$5}' |\
|
||||
sed "s/\"//g" | sed "s/;//"
|
||||
|
||||
PREFIX = /usr/local
|
||||
.if ${OS} == "linux"
|
||||
PREFIX = /usr
|
||||
.endif
|
||||
|
||||
CC = c++
|
||||
FILES = main.cc src/*.cc
|
||||
|
||||
.if ${OS} == "openbsd"
|
||||
DEPS = pkg_add fltk gpgme gpgme-qt gnupg pinentry
|
||||
.elif ${OS} == "netbsd"
|
||||
DEPS = pkgin install fltk gpgme gpgmepp gnupg pinentry pinentry-fltk
|
||||
.elif ${OS} == "freebsd"
|
||||
DEPS = pkg install fltk gpgme gpgme-cpp gnupg pinentry pinentry-fltk
|
||||
.elif ${OS} == "linux"
|
||||
DEPS = xbps-install fltk fltk-devel gpgme gpgmepp gpgmepp-devel gnupg pinentry bmake
|
||||
.endif
|
||||
|
||||
CFLAGS = -Wall -Wextra -Wno-non-c-typedef-for-linkage -Wno-unused-parameter\
|
||||
-Wno-cast-function-type\
|
||||
-I/usr/include -L/usr/lib
|
||||
.if ${OS} == "freebsd" || ${OS} == "openbsd" || ${OS} == "netbsd" || ${OS} == "dragonfly
|
||||
CFLAGS += -I/usr/local/include -L/usr/local/lib
|
||||
.endif
|
||||
.if ${OS} == "netbsd"
|
||||
CFLAGS += -I/usr/X11R7/include -L/usr/X11R7/lib -I/usr/pkg/include -L/usr/pkg/lib
|
||||
.elif ${OS} == "openbsd"
|
||||
CFLAGS += -I/usr/X11R6/include -L/usr/X11R6/lib
|
||||
.endif
|
||||
|
||||
LDFLAGS = -lfltk_images -lfltk -lX11 -lassuan -lgpgmepp -lgpgme -lcrypto -lgpg-error
|
||||
|
||||
.if ${OS} == "openbsd"
|
||||
LDFLAGS += -lc++abi -lpthread -lm -lc\
|
||||
-lXcursor -lXfixes -lXext -lXft -lfontconfig -lXinerama -lXdmcp -lXau\
|
||||
-lz -lxcb -lXrender -lexpat -lfreetype -lc++ -lintl -liconv -lpng -ljpeg
|
||||
.elif ${OS} == "freebsd"
|
||||
LDFLAGS += -lcxxrt -lm -lXrender -lXcursor -lXfixes -lXext -lXft -lfontconfig\
|
||||
-lXinerama -lthr -lz -lxcb -lfreetype -lexpat -lXau -lXdmcp\
|
||||
-lbz2 -lbrotlidec -lbrotlicommon -lc++ -lgcc -lc -lgpgme -lassuan\
|
||||
-lintl -lpng16
|
||||
.elif ${OS} == "netbsd"
|
||||
LDFLAGS += -lstdc++ -lpthread -lm -lc -lXft -lxcb -lfontconfig -lfreetype\
|
||||
-lXau -lXdmcp -lXcursor -lXrandr -lXext -lXrender -lXfixes -lXinerama -lX11\
|
||||
-lexpat -lz -lbz2 -lgcc -lassuan -lintl
|
||||
.elif ${OS} == "linux"
|
||||
LDFLAGS += -lstdc++ -lgcc -lc -lXft -lXext -lXrender -lfontconfig -lXinerama\
|
||||
-lxcb -lfreetype -lpng16 -lz -lexpat -lXau -lXdmcp -lbz2\
|
||||
-lbrotlidec -lbrotlicommon -lassuan
|
||||
.endif
|
||||
|
||||
all:
|
||||
${CC} -O3 ${CFLAGS} -o ${NAME}\
|
||||
${FILES} -static ${LDFLAGS}
|
||||
strip ${NAME}
|
||||
|
||||
depend:
|
||||
${DEPS}
|
||||
|
||||
debug:
|
||||
${CC} -g ${CFLAGS} -o ${NAME} ${FILES} ${LDFLAGS}
|
||||
|
||||
clean:
|
||||
rm -rf ${NAME}
|
||||
|
||||
dist:
|
||||
mkdir -p ${NAME}-${VERSION} release/src release/desktop
|
||||
cp -R LICENSE.txt Makefile README.md CHANGELOG.md logo.png\
|
||||
main.* src icons ${NAME}.desktop ${NAME}-${VERSION}
|
||||
tar zcfv release/src/${NAME}-${VERSION}.tar.gz ${NAME}-${VERSION}
|
||||
cp ${NAME}.desktop release/desktop
|
||||
rm -rf ${NAME}-${VERSION}
|
||||
|
||||
release:
|
||||
mkdir -p release/bin/${VERSION}/${OS}/${ARCH}
|
||||
${CC} -O3 ${CFLAGS} -o release/bin/${VERSION}/${OS}/${ARCH}/${NAME} ${FILES}\
|
||||
-static ${LDFLAGS}
|
||||
strip release/bin/${VERSION}/${OS}/${ARCH}/${NAME}
|
||||
|
||||
publish:
|
||||
rsync -rtvzP release/bin/${VERSION} 192.168.0.143:/zroot/repo/bin/${NAME}
|
||||
rsync -rtvzP release/src/* 192.168.0.143:/zroot/repo/src/${NAME}
|
||||
rsync -rtvzP release/desktop/* 192.168.0.143:/zroot/repo/desktop
|
||||
rsync -rtvzP release/icons/* 192.168.0.143:/zroot/repo/icons
|
||||
|
||||
install:
|
||||
mkdir -p ${DESTDIR}${PREFIX}/bin ${DESTDIR}${PREFIX}/share/applications
|
||||
cp -f ${NAME} ${DESTDIR}${PREFIX}/bin
|
||||
cp -f ${NAME}.desktop ${DESTDIR}${PREFIX}/share/applications
|
||||
.if ${OS} == "linux"
|
||||
sed -i 's/\/local//' ${DESTDIR}${PREFIX}/share/applications/${NAME}.desktop
|
||||
.endif
|
||||
cp -rf icons/076 ${DESTDIR}${PREFIX}/share/icons
|
||||
chmod 755 ${DESTDIR}${PREFIX}/bin/${NAME}
|
||||
|
||||
uninstall:
|
||||
rm -f ${DESTDIR}${PREFIX}/share/applications/${NAME}.desktop
|
||||
rm -f ${DESTDIR}${PREFIX}/bin/${NAME}
|
||||
|
||||
.PHONY: all debug clean dist release publish install uninstall
|
||||
25
README.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# simpas
|
||||
シンプルなパスワードマネージャー Simple Password Manager\
|
||||
GUI版のsp
|
||||
|
||||
## インストールする方法 | Installation
|
||||
### BSD
|
||||
```sh
|
||||
doas make depends
|
||||
make
|
||||
doas make install
|
||||
```
|
||||
|
||||
### Void Linux
|
||||
```sh
|
||||
sudo bmake depends
|
||||
bmake
|
||||
sudo bmake install
|
||||
```
|
||||
|
||||
### Alpine Linux
|
||||
```sh
|
||||
sudo apk add fltk fltk-dev gpgme gpgmepp gnupg pinentry bmake
|
||||
bmake
|
||||
sudo bmake install
|
||||
```
|
||||
BIN
icons/076/128x128/simpas.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
icons/076/16x16/simpas.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
icons/076/18x18/simpas.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
icons/076/22x22/simpas.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
icons/076/24x24/simpas.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
icons/076/256x256/simpas.png
Normal file
|
After Width: | Height: | Size: 67 KiB |
BIN
icons/076/32x32/simpas.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
icons/076/42x42/simpas.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
icons/076/48x48/simpas.png
Normal file
|
After Width: | Height: | Size: 5.4 KiB |
BIN
icons/076/512x512/simpas.png
Normal file
|
After Width: | Height: | Size: 194 KiB |
BIN
icons/076/64x64/simpas.png
Normal file
|
After Width: | Height: | Size: 8.2 KiB |
BIN
icons/076/84x84/simpas.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
icons/076/8x8/simpas.png
Normal file
|
After Width: | Height: | Size: 790 B |
BIN
icons/076/96x96/simpas.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
75
icons/076/index.theme
Normal file
@@ -0,0 +1,75 @@
|
||||
[Icon Theme]
|
||||
Name=076 Icons
|
||||
Comment=076 Icons
|
||||
Inherits=default
|
||||
Directories=128x128,18x18,24x24,32x32,48x48,64x64,8x8,16x16,22x22,256x256,42x42,512x512,84x84,96x96
|
||||
|
||||
[128x128]
|
||||
Size=128
|
||||
Context=Icons
|
||||
Type=Fixed
|
||||
|
||||
[18x18]
|
||||
Size=18
|
||||
Context=Icons
|
||||
Type=Fixed
|
||||
|
||||
[24x24]
|
||||
Size=24
|
||||
Context=Icons
|
||||
Type=Fixed
|
||||
|
||||
[32x32]
|
||||
Size=32
|
||||
Context=Icons
|
||||
Type=Fixed
|
||||
|
||||
[48x48]
|
||||
Size=48
|
||||
Context=Icons
|
||||
Type=Fixed
|
||||
|
||||
[64x64]
|
||||
Size=64
|
||||
Context=Icons
|
||||
Type=Fixed
|
||||
|
||||
[8x8]
|
||||
Size=8
|
||||
Context=Icons
|
||||
Type=Fixed
|
||||
|
||||
[16x16]
|
||||
Size=16
|
||||
Context=Icons
|
||||
Type=Fixed
|
||||
|
||||
[22x22]
|
||||
Size=22
|
||||
Context=Icons
|
||||
Type=Fixed
|
||||
|
||||
[256x256]
|
||||
Size=256
|
||||
Context=Icons
|
||||
Type=Fixed
|
||||
|
||||
[42x42]
|
||||
Size=42
|
||||
Context=Icons
|
||||
Type=Fixed
|
||||
|
||||
[512x512]
|
||||
Size=512
|
||||
Context=Icons
|
||||
Type=Fixed
|
||||
|
||||
[84x84]
|
||||
Size=84
|
||||
Context=Icons
|
||||
Type=Fixed
|
||||
|
||||
[96x96]
|
||||
Size=96
|
||||
Context=Icons
|
||||
Type=Fixed
|
||||
362
main.cc
Normal file
@@ -0,0 +1,362 @@
|
||||
#include "src/addpass.hh"
|
||||
#include "src/delpass.hh"
|
||||
#include "src/editpass.hh"
|
||||
#include "src/genpass.hh"
|
||||
#include "src/initpass.hh"
|
||||
#include "src/showpass.hh"
|
||||
#include "src/vulnpass.hh"
|
||||
#include "src/chkpass.hh"
|
||||
#include "src/common.hh"
|
||||
#include "main.hh"
|
||||
|
||||
#undef Status
|
||||
#undef None
|
||||
#include <FL/Fl.H>
|
||||
#include <FL/Fl_Box.H>
|
||||
#include <FL/Fl_Widget.H>
|
||||
#include <FL/Fl_Window.H>
|
||||
#include <FL/Fl_Select_Browser.H>
|
||||
#include <FL/Fl_Text_Display.H>
|
||||
#include <FL/Fl_Text_Buffer.H>
|
||||
#include <FL/Fl_Button.H>
|
||||
#include <FL/Fl_Check_Button.H>
|
||||
#include <FL/Fl_Input.H>
|
||||
#include <FL/Fl_Secret_Input.H>
|
||||
#include <FL/Fl_Hold_Browser.H>
|
||||
#include <FL/Fl_Copy_Surface.H>
|
||||
#include <FL/Fl_PNG_Image.H>
|
||||
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
Fl_Select_Browser *browser = nullptr;
|
||||
Fl_Text_Display *textview = nullptr;
|
||||
Fl_Text_Buffer *textbuf = nullptr;
|
||||
Fl_Input *searchfield = nullptr;
|
||||
Fl_Check_Button *hidechk = nullptr;
|
||||
Fl_Button *showbtn = nullptr;
|
||||
|
||||
Addpass a;
|
||||
Chkpass c;
|
||||
Delpass d;
|
||||
Editpass e;
|
||||
Genpass g;
|
||||
Initpass i;
|
||||
Showpass s;
|
||||
Vulnpass v;
|
||||
|
||||
const char *sofname = "simpas";
|
||||
const char *intname = "SimPas";
|
||||
const char *version = "1.1.1";
|
||||
const char *basedof = "sp-1.5.1";
|
||||
|
||||
std::vector<std::string> fullpaths;
|
||||
std::vector<std::string> dispaths;
|
||||
std::vector<std::string> filterpaths;
|
||||
int browseId;
|
||||
bool isPassHidden = true;
|
||||
std::string realpass = "";
|
||||
|
||||
void browse(std::string &path, bool isNew) {
|
||||
std::string cont = s.exec(path.c_str(), false);
|
||||
realpass = cont;
|
||||
|
||||
if (isNew) browseId = browser->size();
|
||||
browser->value(browseId);
|
||||
|
||||
if (!cont.empty()) {
|
||||
if (isPassHidden) {
|
||||
std::string lang = Common::getlang();
|
||||
if (lang.compare(0, 2, "en") == 0) {
|
||||
textbuf->text("(Hidden, please click the \"show\" button to reveal)");
|
||||
} else {
|
||||
textbuf->text("(非表示、「表示」ボタンをクリックして確認して下さい)");
|
||||
}
|
||||
showbtn->activate();
|
||||
showbtn->label((Common::getlang().compare(0, 2, "en") == 0) ?
|
||||
"Show password" : "パスワードの表示");
|
||||
} else {
|
||||
textbuf->text(cont.c_str());
|
||||
showbtn->label((Common::getlang().compare(0, 2, "en") == 0) ?
|
||||
"Hide password" : "パスワードを隠す");
|
||||
showbtn->activate();}
|
||||
Editpass::setFile(path);
|
||||
Delpass::setFile(path);
|
||||
d.btn->activate();
|
||||
e.btn->activate();
|
||||
}
|
||||
}
|
||||
|
||||
void hide_cb(Fl_Widget *w, void *) {
|
||||
isPassHidden = ((Fl_Check_Button *)w)->value();
|
||||
int idx = browser->value();
|
||||
|
||||
if (isPassHidden && idx > 0) {
|
||||
showbtn->activate();
|
||||
showbtn->label((Common::getlang().compare(0, 2, "en") == 0) ?
|
||||
"Show password" : "パスワードの表示");
|
||||
} else {
|
||||
showbtn->deactivate();
|
||||
showbtn->label((Common::getlang().compare(0, 2, "en") == 0) ?
|
||||
"Hide password" : "パスワードを隠す");
|
||||
}
|
||||
|
||||
showbtn->label((Common::getlang().compare(0, 2, "en") == 0) ?
|
||||
"Show password" : "パスワードの表示");
|
||||
|
||||
if (idx > 0) {
|
||||
std::string path = filterpaths[idx - 1];
|
||||
browse(path, false);
|
||||
}
|
||||
}
|
||||
|
||||
void clearpaths(bool isReset, std::string &path) {
|
||||
fullpaths.clear();
|
||||
dispaths.clear();
|
||||
if (isReset) {
|
||||
std::string mockpath = "";
|
||||
Editpass::setFile(mockpath);
|
||||
Delpass::setFile(mockpath);
|
||||
d.btn->deactivate();
|
||||
e.btn->deactivate();
|
||||
}
|
||||
}
|
||||
|
||||
void updatelist() {
|
||||
browser->clear();
|
||||
filterpaths.clear();
|
||||
std::string searchtxt = searchfield->value();
|
||||
for (size_t i = 0; i < dispaths.size(); ++i) {
|
||||
if (dispaths[i].find(searchtxt) != std::string::npos) {
|
||||
browser->add(dispaths[i].c_str());
|
||||
filterpaths.push_back(fullpaths[i]);
|
||||
}
|
||||
}
|
||||
textbuf->text("");
|
||||
}
|
||||
|
||||
void search_cb(Fl_Widget *, void *) {
|
||||
std::string mockpath = "";
|
||||
Editpass::setFile(mockpath);
|
||||
Delpass::setFile(mockpath);
|
||||
d.btn->deactivate();
|
||||
e.btn->deactivate();
|
||||
updatelist();
|
||||
}
|
||||
|
||||
void copy_cb(Fl_Widget *, void *) {
|
||||
if (!realpass.empty()) {
|
||||
Fl::copy(realpass.c_str(), realpass.length(), 1, Fl::clipboard_plain_text);
|
||||
} else {
|
||||
Fl::copy("", 0, 1, Fl::clipboard_plain_text);
|
||||
}
|
||||
}
|
||||
|
||||
void browser_cb(Fl_Widget *w, void *) {
|
||||
(void)w;
|
||||
int idx = browser->value();
|
||||
if (idx == 0) return;
|
||||
browseId = idx;
|
||||
|
||||
std::string path = filterpaths[idx - 1];
|
||||
browse(path, false);
|
||||
}
|
||||
|
||||
void show_cb(Fl_Widget *, void *) {
|
||||
int idx = browser->value();
|
||||
if (idx == 0) return;
|
||||
std::string path = filterpaths[idx - 1];
|
||||
|
||||
if (isPassHidden) {
|
||||
std::string cont = s.exec(path.c_str(), false);
|
||||
realpass = cont;
|
||||
textbuf->text(cont.c_str());
|
||||
|
||||
std::string lang = Common::getlang();
|
||||
if (lang.compare(0, 2, "en") == 0) {
|
||||
showbtn->label("Hide password");
|
||||
} else {
|
||||
showbtn->label("パスワードを隠す");
|
||||
}
|
||||
isPassHidden = false;
|
||||
} else {
|
||||
std::string lang = Common::getlang();
|
||||
if (lang.compare(0, 2, "en") == 0) {
|
||||
textbuf->text("(Hidden, please click the \"show\" button to reveal)");
|
||||
showbtn->label("Show password");
|
||||
} else {
|
||||
textbuf->text("(非表示、「表示」ボタンをクリックして確認して下さい)");
|
||||
showbtn->label("パスワードの表示");
|
||||
}
|
||||
isPassHidden = true;
|
||||
}
|
||||
}
|
||||
|
||||
void scandir(const std::string &dpath, const std::string &rpath,
|
||||
std::vector<std::string> &fpaths) {
|
||||
DIR *dir = opendir(dpath.c_str());
|
||||
if (!dir) return;
|
||||
|
||||
struct dirent *entry;
|
||||
while ((entry = readdir(dir)) != nullptr) {
|
||||
std::string name = entry->d_name;
|
||||
if (name == "." || name == ".." || name == ".gpg-id") continue;
|
||||
|
||||
std::string fpath = std::string(dpath) + "/" + name;
|
||||
struct stat s;
|
||||
if (stat(fpath.c_str(), &s) != 0) {
|
||||
closedir(dir);
|
||||
return;
|
||||
}
|
||||
|
||||
if (S_ISDIR(s.st_mode)) {
|
||||
scandir(fpath, rpath, fpaths);
|
||||
} else if (name.find(".gpg") != std::string::npos) {
|
||||
std::string rel = fpath.substr(rpath.size() + 1);
|
||||
fpaths.push_back(rel);
|
||||
fullpaths.push_back(fpath);
|
||||
|
||||
std::string disname = rel.substr(0, rel.rfind(".gpg"));
|
||||
dispaths.push_back(disname);
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
void init_cb(Fl_Widget *w, void *data) {
|
||||
i.exec(i.gpgid->value());
|
||||
i.btn->deactivate();
|
||||
((Initpass *)data)->cancel_cb(w, data);
|
||||
}
|
||||
|
||||
void init_dialog_cb(Fl_Widget *w, void *) {
|
||||
(void)w;
|
||||
std::string lang = Common::getlang();
|
||||
Fl_Window *dialog = new Fl_Window(450, 120,
|
||||
(lang.compare(0, 2, "en") == 0 ?
|
||||
"Initialize password" : "パスワードの初期設定"));
|
||||
|
||||
i.gpgid = new Fl_Input(90, 20, 300, 30,
|
||||
(lang.compare(0, 2, "en") == 0 ? "GPG secret key:" : "gpg秘密鍵:"));
|
||||
dialog->add(i.gpgid);
|
||||
|
||||
Fl_Button *startbtn = new Fl_Button(185, 70, 80, 30,
|
||||
(lang.compare(0, 2, "en") == 0 ? "Start" : "開始"));
|
||||
|
||||
startbtn->callback(init_cb, dialog);
|
||||
|
||||
dialog->add(startbtn);
|
||||
|
||||
dialog->end();
|
||||
dialog->set_modal();
|
||||
dialog->show();
|
||||
}
|
||||
|
||||
void set_dark_theme() {
|
||||
Fl::background(35, 32, 35);
|
||||
Fl::background2(68, 59, 68);
|
||||
Fl::foreground(252, 252, 252);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
std::string lang = Common::getlang();
|
||||
std::string windowtit = std::string(intname) + " " + version;
|
||||
Fl_Window *window = new Fl_Window(790, 740, windowtit.c_str());
|
||||
|
||||
set_dark_theme();
|
||||
|
||||
#if defined(__linux)
|
||||
const char *iconPath = "/usr/share/icons/076/512x512/simpas.png";
|
||||
#else
|
||||
const char *iconPath = "/usr/local/share/icons/076/512x512/simpas.png";
|
||||
#endif
|
||||
|
||||
Fl_PNG_Image *icon = new Fl_PNG_Image(iconPath);
|
||||
|
||||
window->icon(icon);
|
||||
|
||||
searchfield = new Fl_Input(
|
||||
(lang.compare(0, 2, "en") == 0 ? 70 : 50), 10,
|
||||
(lang.compare(0, 2, "en") == 0 ? 710 : 730), 30,
|
||||
(lang.compare(0, 2, "en") == 0 ? "Search:" : "検索:"));
|
||||
searchfield->callback(search_cb);
|
||||
|
||||
browser = new Fl_Select_Browser(10, 50, 380, 500);
|
||||
textview = new Fl_Text_Display(400, 50, 380, 500);
|
||||
textbuf = new Fl_Text_Buffer();
|
||||
textview->buffer(textbuf);
|
||||
|
||||
browser->callback(browser_cb);
|
||||
|
||||
Fl_Button *copybtn = new Fl_Button(400, 600, 150, 30,
|
||||
(lang.compare(0, 2, "en") == 0 ? "Copy password" : "パスワードのコピー"));
|
||||
copybtn->callback(copy_cb);
|
||||
|
||||
a.btn = new Fl_Button(10, 560, 150, 30,
|
||||
(lang.compare(0, 2, "en") == 0 ? "Add password" : "パスワードの追加"));
|
||||
a.btn->callback(a.dialog_cb);
|
||||
|
||||
d.btn = new Fl_Button(10, 600, 150, 30,
|
||||
(lang.compare(0, 2, "en") == 0 ? "Delete password" : "パスワードの削除"));
|
||||
d.btn->deactivate();
|
||||
d.btn->callback(d.dialog_cb);
|
||||
|
||||
e.btn = new Fl_Button(400, 560, 150, 30,
|
||||
(lang.compare(0, 2, "en") == 0 ? "Edit password" : "パスワードの編集"));
|
||||
e.btn->deactivate();
|
||||
e.btn->callback(e.dialog_cb);
|
||||
|
||||
hidechk = new Fl_Check_Button(560, 560, 150, 30,
|
||||
(lang.compare(0, 2, "en") == 0 ? "Hide password" : "パスワードを隠す"));
|
||||
hidechk->set();
|
||||
hidechk->callback(hide_cb);
|
||||
|
||||
showbtn = new Fl_Button(560, 600, 150, 30,
|
||||
(lang.compare(0, 2, "en") == 0 ? "Show password" : "パスワードの表示"));
|
||||
showbtn->deactivate();
|
||||
showbtn->callback(show_cb);
|
||||
|
||||
g.btn = new Fl_Button(10, 640, 150, 30,
|
||||
(lang.compare(0, 2, "en") == 0 ? "Generate password" : "パスワードの作成"));
|
||||
g.btn->callback(g.dialog_cb);
|
||||
|
||||
i.btn = new Fl_Button(10, 680, 150, 30,
|
||||
(lang.compare(0, 2, "en") == 0 ?
|
||||
"Initialize password" :
|
||||
"パスワードの初期設定"));
|
||||
|
||||
v.btn = new Fl_Button(170, 560, 200, 30,
|
||||
(lang.compare(0, 2, "en") == 0 ? "Check for breach" : "漏洩されたかの確認"));
|
||||
v.btn->callback(v.dialog_cb);
|
||||
|
||||
c.btn = new Fl_Button(170, 600, 200, 30,
|
||||
(lang.compare(0, 2, "en") == 0 ?
|
||||
"Check for unsafe passwords" : "不安定的なパスワードの確認"));
|
||||
c.btn->callback(c.dialog_cb);
|
||||
|
||||
std::string gpgidpath = Common::getbasedir(true) + ".gpg-id";
|
||||
|
||||
struct stat buf;
|
||||
if (stat(gpgidpath.c_str(), &buf) == 0) {
|
||||
i.btn->deactivate();
|
||||
}
|
||||
i.btn->callback(init_dialog_cb);
|
||||
|
||||
std::string bothver = windowtit + " (" + std::string(basedof) + ")";
|
||||
Fl_Box *versionlabel = new Fl_Box(FL_NO_BOX, 620, 700, 160, 30, bothver.c_str());
|
||||
versionlabel->align(FL_ALIGN_RIGHT | FL_ALIGN_INSIDE);
|
||||
|
||||
std::vector<std::string> fpaths;
|
||||
std::string rdir = Common::getbasedir(false);
|
||||
scandir(rdir, rdir, fpaths);
|
||||
updatelist();
|
||||
|
||||
window->end();
|
||||
window->show(argc, argv);
|
||||
|
||||
return Fl::run();
|
||||
}
|
||||
24
main.hh
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef MAIN_HH
|
||||
#define MAIN_HH
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
extern std::vector<std::string> fullpaths;
|
||||
extern std::vector<std::string> dispaths;
|
||||
extern int browseId;
|
||||
extern bool isPassHidden;
|
||||
extern std::string realpass;
|
||||
|
||||
class Fl_Text_Display;
|
||||
class Fl_Text_Buffer;
|
||||
|
||||
extern Fl_Text_Display *textview;
|
||||
extern Fl_Text_Buffer *textbuf;
|
||||
|
||||
void browse(std::string &path, bool isNew);
|
||||
void clearpaths(bool isReset, std::string &path);
|
||||
void updatelist();
|
||||
void scandir(const std::string& dir, const std::string& root, std::vector<std::string>& paths);
|
||||
|
||||
#endif
|
||||
12
simpas.desktop
Normal file
@@ -0,0 +1,12 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=SimPas
|
||||
GenericName=Simple Password Manager
|
||||
GenericName[ja]=シンプルなパスワードマネージャー
|
||||
Exec=simpas %F
|
||||
Icon=/usr/local/share/icons/076/512x512/simpas
|
||||
StartupNotify=true
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=Application;Utility;System;Security;DeskopUtility;
|
||||
Keywords=password;password manager;security;otp;password generator;
|
||||
307
src/addpass.cc
Normal file
@@ -0,0 +1,307 @@
|
||||
#include "common.hh"
|
||||
#include "addpass.hh"
|
||||
#include "../main.hh"
|
||||
|
||||
#include <gpgme++/context.h>
|
||||
#include <gpgme++/data.h>
|
||||
#include <gpgme++/encryptionresult.h>
|
||||
|
||||
#undef None
|
||||
#include <FL/Fl_Window.H>
|
||||
#include <FL/fl_ask.H>
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
Addpass add;
|
||||
|
||||
struct InputData {
|
||||
Fl_Input *txtin;
|
||||
Fl_Secret_Input *pass1;
|
||||
Fl_Secret_Input *pass2;
|
||||
Fl_Window *dialog;
|
||||
};
|
||||
|
||||
bool Addpass::exec(const std::string &file, const std::string &pass, bool isEdit) {
|
||||
std::string lang = Common::getlang();
|
||||
|
||||
std::string basedir = Common::getbasedir(true);
|
||||
std::string ext = ".gpg";
|
||||
|
||||
std::string gpgoutfile = (isEdit ? file : basedir + file + ext);
|
||||
|
||||
if (access(gpgoutfile.c_str(), F_OK) != -1) {
|
||||
std::string err = (lang.compare(0, 2, "en") == 0 ?
|
||||
"Password already exist." :
|
||||
"パスワードが既に存在しています。");
|
||||
fl_alert("%s", err.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
// GPGMEライブラリを設置
|
||||
std::setlocale(LC_ALL, "");
|
||||
GpgME::initializeLibrary();
|
||||
GpgME::setDefaultLocale(LC_CTYPE, std::setlocale(LC_CTYPE, NULL));
|
||||
|
||||
GpgME::Error err;
|
||||
if (err) {
|
||||
std::string ero = (lang.compare(0, 2, "en") == 0 ?
|
||||
"Failed to generate GPGME" :
|
||||
"GPGエラーの設置に失敗");
|
||||
fl_alert("%s: %s", ero.c_str(), gpg_strerror(err.code()));
|
||||
return false;
|
||||
}
|
||||
|
||||
// GPGMEを創作
|
||||
std::unique_ptr<GpgME::Context> ctx =
|
||||
GpgME::Context::create(GpgME::Protocol::OpenPGP);
|
||||
|
||||
// GPGMEは非対話的モードに設定
|
||||
err = ctx->setPinentryMode(GpgME::Context::PinentryMode::PinentryLoopback);
|
||||
if (err) {
|
||||
std::string ero = (lang.compare(0, 2, "en") == 0 ?
|
||||
"Failed to set pinentry mode" :
|
||||
"pinentryモードを設定に失敗");
|
||||
fl_alert("%s: %s", ero.c_str(), gpg_strerror(err.code()));
|
||||
return false;
|
||||
}
|
||||
|
||||
// パスワードからデータオブジェクトを創作
|
||||
GpgME::Data in(pass.c_str(), strlen(pass.c_str()), false);
|
||||
GpgME::Data out;
|
||||
|
||||
// 鍵を受け取る
|
||||
std::string keypath = basedir + ".gpg-id";
|
||||
std::ifstream keyfile(keypath, std::ios::binary);
|
||||
if (!keyfile.is_open()) {
|
||||
std::string ero = (lang.compare(0, 2, "en") == 0 ?
|
||||
"Failed to open .gpg-id file" :
|
||||
".gpg-idファイルを開くに失敗");
|
||||
fl_alert("%s", ero.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string keyid;
|
||||
std::string line;
|
||||
while (std::getline(keyfile, line)) {
|
||||
line.erase(line.find_last_not_of(" \n\r\t") + 1);
|
||||
if (!line.empty()) keyid = line;
|
||||
}
|
||||
|
||||
keyfile.close();
|
||||
|
||||
if (keyid.empty()) {
|
||||
std::string ero = (lang.compare(0, 2, "en") == 0 ?
|
||||
"The .gpg-id file is empty or invalid" :
|
||||
".gpg-idファイルは空か無効です");
|
||||
fl_alert("%s", ero.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
GpgME::Key key = ctx->key(keyid.c_str(), err);
|
||||
if (key.isNull()) {
|
||||
std::string ero = (lang.compare(0, 2, "en") == 0 ?
|
||||
"Failed to get key" :
|
||||
"鍵を受取に失敗");
|
||||
fl_alert("%s", ero.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// 暗号化
|
||||
std::vector<GpgME::Key> keys = {key};
|
||||
|
||||
GpgME::EncryptionResult res =
|
||||
ctx->encrypt(keys, in, out, GpgME::Context::AlwaysTrust);
|
||||
if (res.error()) {
|
||||
std::string ero = (lang.compare(0, 2, "en") == 0 ?
|
||||
"Failed to encrypt" :
|
||||
"暗号化に失敗");
|
||||
fl_alert("%s: %s", ero.c_str(), res.error().asString());
|
||||
return false;
|
||||
}
|
||||
|
||||
// ディレクトリを創作
|
||||
std::string dirpath = (isEdit ? file : basedir + file);
|
||||
auto lastsla = dirpath.find_last_of('/');
|
||||
if (lastsla != std::string::npos) {
|
||||
dirpath = dirpath.substr(0, lastsla);
|
||||
|
||||
try {
|
||||
Common common;
|
||||
common.mkdir_r(dirpath, 0755);
|
||||
} catch (const std::runtime_error &e) {
|
||||
std::string ero = (lang.compare(0, 2, "en") == 0 ?
|
||||
"Failed to create directory" :
|
||||
"ディレクトリを創作に失敗");
|
||||
fl_alert("%s: %s", ero.c_str(), e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 暗号化したファイルを開く
|
||||
std::ofstream gpgpath(gpgoutfile, std::ios::binary);
|
||||
if (!gpgpath.is_open()) {
|
||||
std::string ero = (lang.compare(0, 2, "en") == 0 ?
|
||||
"Failed to write file '" + gpgoutfile + "'." :
|
||||
"「" + gpgoutfile + "」ファイルを書き込めません。");
|
||||
fl_alert("%s", ero.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// データが保存したかどうか確認
|
||||
ssize_t encrypted_data_size = out.seek(0, SEEK_END);
|
||||
if (encrypted_data_size <= 0) {
|
||||
std::string ero = (lang.compare(0, 2, "en") == 0 ?
|
||||
"Failed to store the data" :
|
||||
"データを保存に失敗");
|
||||
fl_alert("%s", ero.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// 復号化したパスワードを表示する
|
||||
out.seek(0, SEEK_SET);
|
||||
|
||||
char buffer[512];
|
||||
ssize_t read_bytes;
|
||||
while ((read_bytes = out.read(buffer, sizeof(buffer))) > 0) {
|
||||
gpgpath.write(buffer, read_bytes);
|
||||
}
|
||||
|
||||
gpgpath.close();
|
||||
|
||||
if (gpgpath.fail()) {
|
||||
std::string ero = (lang.compare(0, 2, "en") == 0 ?
|
||||
"Failed to write encrypted data to file" :
|
||||
"暗号化データを書き込めません。");
|
||||
fl_alert("%s", ero.c_str());
|
||||
return false;
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
std::string err = (lang.compare(0, 2, "en") == 0 ?
|
||||
"Error" :
|
||||
"エラー");
|
||||
fl_alert("%s: %s", err.c_str(), e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isEdit) return true;
|
||||
|
||||
std::string msg = (lang.compare(0, 2, "en") == 0 ?
|
||||
"The password got saved." :
|
||||
"パスワードを保存出来ました");
|
||||
fl_alert("%s", msg.c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Addpass::add_cb(Fl_Widget *, void *data) {
|
||||
InputData *inputs = (InputData *)data;
|
||||
std::string lang = Common::getlang();
|
||||
|
||||
if (inputs) {
|
||||
file = inputs->txtin->value();
|
||||
if (file.empty()) {
|
||||
std::string err =
|
||||
(lang.compare(0, 2, "en") == 0 ?
|
||||
"Please fill in the path." :
|
||||
"パスをご入力下さい。");
|
||||
fl_alert("%s", err.c_str());
|
||||
return;
|
||||
}
|
||||
inputpass1 = inputs->pass1->value();
|
||||
if (inputpass1.empty()) {
|
||||
std::string err =
|
||||
(lang.compare(0, 2, "en") == 0 ?
|
||||
"Please fill in the password." :
|
||||
"パスワードをご入力下さい。");
|
||||
fl_alert("%s", err.c_str());
|
||||
return;
|
||||
}
|
||||
inputpass2 = inputs->pass2->value();
|
||||
if (inputpass2.empty()) {
|
||||
std::string err =
|
||||
(lang.compare(0, 2, "en") == 0 ?
|
||||
"Please fill in the password (confirm)." :
|
||||
"パスワード (確認)をご入力下さい。");
|
||||
fl_alert("%s", err.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (inputpass1 != inputpass2) {
|
||||
std::string err =
|
||||
(lang.compare(0, 2, "en") == 0 ?
|
||||
"Password does not match." :
|
||||
"パスワードが一致していません。");
|
||||
fl_alert("%s", err.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (exec(file, inputpass1, false)) {
|
||||
inputs->dialog->hide();
|
||||
}
|
||||
} else {
|
||||
std::string err =
|
||||
(lang.compare(0, 2, "en") == 0 ?
|
||||
"Please fill in all the fields." :
|
||||
"全てのフィールドをご入力下さい。");
|
||||
fl_alert("%s", err.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void Addpass::dialog_cb(Fl_Widget *w, void *) {
|
||||
(void)w;
|
||||
std::string lang = Common::getlang();
|
||||
Fl_Window *dialog = new Fl_Window(400, 200,
|
||||
(lang.compare(0, 2, "en") == 0 ? "Add password" : "パスワードの追加"));
|
||||
|
||||
Fl_Input *txtin = new Fl_Input(150, 20, 180, 30,
|
||||
(lang.compare(0, 2, "en") == 0 ? "Path:" : "パス:"));
|
||||
dialog->add(txtin);
|
||||
|
||||
Fl_Secret_Input *pass1 = new Fl_Secret_Input(150, 60, 180, 30,
|
||||
(lang.compare(0, 2, "en") == 0 ? "Password:" : "パスワード:"));
|
||||
Fl_Secret_Input *pass2 = new Fl_Secret_Input(150, 100, 180, 30,
|
||||
(lang.compare(0, 2, "en") == 0 ? "Password (confirm):" : "パスワード (確認):"));
|
||||
dialog->add(pass1);
|
||||
dialog->add(pass2);
|
||||
|
||||
InputData *inputs = new InputData();
|
||||
inputs->txtin = txtin;
|
||||
inputs->pass1 = pass1;
|
||||
inputs->pass2 = pass2;
|
||||
inputs->dialog = dialog;
|
||||
|
||||
Fl_Button *okbtn = new Fl_Button(60, 150, 80, 30, "OK");
|
||||
Fl_Button *cancelbtn = new Fl_Button(160, 150, 80, 30,
|
||||
(lang.compare(0, 2, "en") == 0 ? "Cancel" : "キャンセル"));
|
||||
|
||||
okbtn->callback(static_ok_cb, inputs);
|
||||
cancelbtn->callback(static_cancel_cb, dialog);
|
||||
|
||||
dialog->add(okbtn);
|
||||
dialog->add(cancelbtn);
|
||||
|
||||
dialog->end();
|
||||
dialog->set_modal();
|
||||
dialog->show();
|
||||
}
|
||||
|
||||
void Addpass::static_ok_cb(Fl_Widget *w, void *data) {
|
||||
(void)w;
|
||||
InputData *inputs = (InputData *)data;
|
||||
|
||||
add.add_cb(nullptr, inputs);
|
||||
std::vector<std::string> fpaths;
|
||||
std::string rdir = Common::getbasedir(false);
|
||||
std::string curpath = rdir + "/" + inputs->txtin->value() + ".gpg";
|
||||
clearpaths(false, curpath);
|
||||
scandir(rdir, rdir, fpaths);
|
||||
updatelist();
|
||||
browse(curpath, true);
|
||||
}
|
||||
|
||||
void Addpass::static_cancel_cb(Fl_Widget *w, void *data) {
|
||||
((Addpass *)data)->cancel_cb(w, data);
|
||||
}
|
||||
28
src/addpass.hh
Normal file
@@ -0,0 +1,28 @@
|
||||
#ifndef ADDPASS_HH
|
||||
#define ADDPASS_HH
|
||||
|
||||
#include <FL/Fl_Button.H>
|
||||
#include <FL/Fl_Input.H>
|
||||
#include <FL/Fl_Secret_Input.H>
|
||||
|
||||
#include "dialog.hh"
|
||||
|
||||
#include <string>
|
||||
|
||||
class Addpass : public Dialog {
|
||||
public:
|
||||
Fl_Button *btn = nullptr;
|
||||
std::string file;
|
||||
std::string inputpass1;
|
||||
std::string inputpass2;
|
||||
|
||||
static void dialog_cb(Fl_Widget *w, void *);
|
||||
void add_cb(Fl_Widget *, void *);
|
||||
bool exec(const std::string &file, const std::string &pass, bool isEdit);
|
||||
|
||||
private:
|
||||
static void static_ok_cb(Fl_Widget *w, void *data);
|
||||
static void static_cancel_cb(Fl_Widget *w, void *data);
|
||||
};
|
||||
|
||||
#endif
|
||||
53
src/base32.cc
Normal file
@@ -0,0 +1,53 @@
|
||||
#include "base32.hh"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
int Base32::char_to_val(char c) {
|
||||
std::string alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
||||
auto it = std::find(alphabet.begin(), alphabet.end(), c);
|
||||
return (it != alphabet.end()) ? std::distance(alphabet.begin(), it) : -1;
|
||||
}
|
||||
|
||||
std::vector<unsigned char> Base32::decode(const std::string &encoded) {
|
||||
std::string encoded_up = encoded;
|
||||
for (auto &c : encoded_up) {
|
||||
c = std::toupper(static_cast<unsigned char>(c));
|
||||
}
|
||||
|
||||
size_t encoded_len = encoded_up.length();
|
||||
size_t padding = 0;
|
||||
for (int i = encoded_len - 1; i >= 0 && encoded_up[i] == '='; --i) {
|
||||
++padding;
|
||||
}
|
||||
|
||||
size_t out_len = (encoded_len - padding) * 5 / 8;
|
||||
if (out_len == 0) return {};
|
||||
|
||||
std::vector<unsigned char> decoded(out_len);
|
||||
|
||||
int buffer = 0, bits_left = 0, count = 0;
|
||||
Base32 b32;
|
||||
for (size_t i = 0; i < encoded_len - padding; ++i) {
|
||||
int val = b32.char_to_val(encoded_up[i]);
|
||||
if (val < 0) {
|
||||
throw std::runtime_error("Base32エンコードした文字の中に不正な文字があります。");
|
||||
}
|
||||
|
||||
buffer <<= 5;
|
||||
buffer |= val;
|
||||
bits_left += 5;
|
||||
|
||||
if (bits_left >= 8) {
|
||||
decoded[count++] = static_cast<unsigned char>(buffer >> (bits_left - 8));
|
||||
bits_left -= 8;
|
||||
}
|
||||
}
|
||||
|
||||
decoded.resize(count);
|
||||
return decoded;
|
||||
}
|
||||
15
src/base32.hh
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef BASE32_HH
|
||||
#define BASE32_HH
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
class Base32 {
|
||||
public:
|
||||
static std::vector<unsigned char> decode(const std::string &encoded);
|
||||
|
||||
private:
|
||||
int char_to_val(char c);
|
||||
};
|
||||
|
||||
#endif
|
||||
279
src/chkpass.cc
Normal file
@@ -0,0 +1,279 @@
|
||||
#include "common.hh"
|
||||
#include "chkpass.hh"
|
||||
#include "../main.hh"
|
||||
#include "showpass.hh"
|
||||
|
||||
#include <FL/Fl.H>
|
||||
#include <FL/Fl_Window.H>
|
||||
#include <FL/fl_ask.H>
|
||||
#include <FL/Fl_Box.H>
|
||||
#include <FL/Fl_Button.H>
|
||||
#include <FL/Fl_Text_Display.H>
|
||||
#include <FL/Fl_Text_Buffer.H>
|
||||
#include <FL/Fl_Scrollbar.H>
|
||||
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <algorithm>
|
||||
|
||||
Chkpass chk;
|
||||
std::vector<std::string> foundRes;
|
||||
std::vector<std::string> duppath;
|
||||
std::vector<std::string> duppasswd;
|
||||
|
||||
struct InputData {
|
||||
Fl_Radio_Button *all;
|
||||
Fl_Radio_Button *len;
|
||||
Fl_Radio_Button *chr;
|
||||
Fl_Radio_Button *dup;
|
||||
Fl_Window *dialog;
|
||||
};
|
||||
|
||||
void Chkpass::lenPass(const std::string &path, const std::string &pass,
|
||||
const std::string &lang) {
|
||||
if (pass.length() > 0 && pass.length() < minimumlen) {
|
||||
std::string res = "【E】";
|
||||
res += (lang.compare(0, 2, "en") == 0 ?
|
||||
"The password \"" + path + "\" is too short, minimum length should be " +
|
||||
std::to_string(minimumlen) + " characters, recommended is " +
|
||||
std::to_string(recommendlen) + " characters.":
|
||||
"パスワード「" + path + "」は短すぎます。最短パスワードの長さは" +
|
||||
std::to_string(minimumlen) + "文字ですが、勧めが" +
|
||||
std::to_string(recommendlen) + "文字です。");
|
||||
|
||||
foundRes.push_back(res);
|
||||
weaklencount++;
|
||||
vulncount++;
|
||||
} else if (pass.length() >= minimumlen && pass.length() < recommendlen) {
|
||||
std::string res = "【W】";
|
||||
res += (lang.compare(0, 2, "en") == 0 ?
|
||||
"The password \"" + path + "\" is long enough, but for optimal security, " +
|
||||
std::to_string(recommendlen) + " characters is recommended.\n" :
|
||||
"パスワード「" + path + "」の長さは十分ですが、最強のセキュリティには" +
|
||||
std::to_string(recommendlen) + "文字が勧めします。\n");
|
||||
|
||||
foundRes.push_back(res);
|
||||
}
|
||||
}
|
||||
|
||||
void Chkpass::charPass(const std::string &path, const std::string &pass,
|
||||
const std::string &lang) {
|
||||
bool isUpper = false;
|
||||
bool isLower = false;
|
||||
bool isDigit = false;
|
||||
bool isSpecial = false;
|
||||
|
||||
for (char ch : pass) {
|
||||
if (std::isupper(static_cast<unsigned char>(ch))) isUpper = true;
|
||||
if (std::islower(static_cast<unsigned char>(ch))) isLower = true;
|
||||
if (std::isdigit(static_cast<unsigned char>(ch))) isDigit = true;
|
||||
if (std::find(spchar.begin(), spchar.end(), ch) != spchar.end()) isSpecial = true;
|
||||
}
|
||||
|
||||
if (!isUpper || !isLower || !isDigit || !isSpecial) {
|
||||
std::string res = "【E】";
|
||||
res += (lang.compare(0, 2, "en") == 0 ?
|
||||
"The password \"" + path + "\" is too weak! A strong password contains " +
|
||||
"at least 1 uppercase, 1 lowercase, 1 digit, and 1 special character." :
|
||||
"パスワード「" + path + "」は弱すぎます!強いパスワードは最大1大文字、" +
|
||||
"1小文字、1数字、及び1記号の文字が含みます。");
|
||||
|
||||
foundRes.push_back(res);
|
||||
weakcharcount++;
|
||||
vulncount++;
|
||||
}
|
||||
}
|
||||
|
||||
void Chkpass::dupPass(const std::string &path, const std::string &pass,
|
||||
const std::string &lang) {
|
||||
for (std::size_t k = 0; k < duppasswd.size(); k++) {
|
||||
if (pass.compare(duppasswd[k]) == 0) {
|
||||
std::string res = "【E】";
|
||||
res += (lang.compare(0, 2, "en") == 0 ?
|
||||
"The password \"" + path + "\" is the same as \"" + duppath[k] + "\". " +
|
||||
"For security, please keep passwords unique!":
|
||||
"パスワード「" + path + "」は「" + duppath[k] + "」と一致しています。" +
|
||||
"セキュリティの為、各パスワードはユニークにする様にして下さい!");
|
||||
|
||||
foundRes.push_back(res);
|
||||
duppasscount++;
|
||||
vulncount++;
|
||||
}
|
||||
}
|
||||
|
||||
duppath.push_back(path);
|
||||
duppasswd.push_back(pass);
|
||||
}
|
||||
|
||||
bool Chkpass::exec() {
|
||||
std::string lang = Common::getlang();
|
||||
|
||||
// パスワードをスキャンして
|
||||
Showpass show;
|
||||
|
||||
for (const auto &dispath : dispaths) {
|
||||
std::string fullpath = Common::getbasedir(true) + dispath + ".gpg";
|
||||
std::string pass = show.exec(fullpath.c_str(), true);
|
||||
if (pass.empty()) continue;
|
||||
if (pass.rfind("otpauth://totp/", 0) == 0) continue;
|
||||
|
||||
if (isAll) {
|
||||
lenPass(dispath, pass, lang);
|
||||
charPass(dispath, pass, lang);
|
||||
dupPass(dispath, pass, lang);
|
||||
} else if (isLen) {
|
||||
lenPass(dispath, pass, lang);
|
||||
} else if (isChar) {
|
||||
charPass(dispath, pass, lang);
|
||||
} else if (isDup) {
|
||||
dupPass(dispath, pass, lang);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Chkpass::showRes() {
|
||||
std::string lang = Common::getlang();
|
||||
|
||||
std::string res;
|
||||
if (lang.compare(0, 2, "en") == 0) {
|
||||
res = "Weak passwords:\n";
|
||||
} else {
|
||||
res = "不安定なパスワード:\n";
|
||||
}
|
||||
|
||||
for (const auto &path : foundRes) {
|
||||
res += path + "\n";
|
||||
}
|
||||
|
||||
if (lang.compare(0, 2, "en") == 0) {
|
||||
res += "Short password count: " + std::to_string(weaklencount) + "\n";
|
||||
res += "Weak password count: " + std::to_string(weakcharcount) + "\n";
|
||||
res += "Duplicate password count: " + std::to_string(duppasscount) + "\n";
|
||||
res += "Total: " + std::to_string(duppath.size()) + "\n";
|
||||
res += "It's advised to change any of the";
|
||||
res += "weak passwords as soon as possible!";
|
||||
} else {
|
||||
res += "短いパスワード数: " + std::to_string(weaklencount) + "\n";
|
||||
res += "弱いパスワード数: " + std::to_string(weakcharcount) + "\n";
|
||||
res += "同じパスワード数: " + std::to_string(duppasscount) + "\n";
|
||||
res += "合計: " + std::to_string(duppath.size()) + "\n";
|
||||
res += "不安定なパスワードは出来るだけ早く変更する事をお勧めします!";
|
||||
}
|
||||
|
||||
Fl_Window *win = new Fl_Window(500, 440, lang.compare(0, 2, "en") == 0 ?
|
||||
"Results" : "結果");
|
||||
Fl_Text_Display *display = new Fl_Text_Display(10, 10, 480, 380);
|
||||
Fl_Text_Buffer *textbuf = new Fl_Text_Buffer();
|
||||
|
||||
textbuf->text(res.c_str());
|
||||
display->buffer(textbuf);
|
||||
display->scrollbar_width(15);
|
||||
win->resizable(display);
|
||||
|
||||
Fl_Button *okBtn = new Fl_Button(210, 400, 80, 30, "OK");
|
||||
okBtn->callback([](Fl_Widget *widget, void *win) {
|
||||
(void)widget;
|
||||
reinterpret_cast<Fl_Window*>(win)->hide();
|
||||
}, win);
|
||||
|
||||
win->add(okBtn);
|
||||
|
||||
win->end();
|
||||
win->show();
|
||||
}
|
||||
|
||||
void Chkpass::chk_cb(Fl_Widget *, void *data) {
|
||||
InputData *inputs = (InputData *)data;
|
||||
std::string lang = Common::getlang();
|
||||
|
||||
if (inputs) {
|
||||
isAll = inputs->all->value();
|
||||
isLen = inputs->len->value();
|
||||
isChar = inputs->chr->value();
|
||||
isDup = inputs->dup->value();
|
||||
}
|
||||
|
||||
Fl_Window *dialog = new Fl_Window(400, 50,
|
||||
(lang.compare(0, 2, "en") == 0 ?
|
||||
"Checking for weak password" : "不安的なパスワードの確認中"));
|
||||
|
||||
Fl_Box *box = new Fl_Box(10, 10, 380, 20,
|
||||
(lang.compare(0, 2, "en") == 0 ?
|
||||
"Checking, please wait for a while..." :
|
||||
"確認中。暫くお待ち下さい・・・"));
|
||||
|
||||
dialog->add(box);
|
||||
|
||||
dialog->end();
|
||||
dialog->set_modal();
|
||||
dialog->show();
|
||||
|
||||
std::thread checker([dialog]() {
|
||||
chk.exec();
|
||||
|
||||
Fl::lock();
|
||||
dialog->hide();
|
||||
chk.showRes();
|
||||
Fl::unlock();
|
||||
});
|
||||
|
||||
checker.detach();
|
||||
}
|
||||
|
||||
void Chkpass::dialog_cb(Fl_Widget *w, void *) {
|
||||
(void)w;
|
||||
|
||||
std::string lang = Common::getlang();
|
||||
|
||||
Fl_Window *dialog = new Fl_Window(390, 145,
|
||||
(lang.compare(0, 2, "en") == 0 ?
|
||||
"Check for weak password" : "不安的なパスワードの確認"));
|
||||
|
||||
Fl_Radio_Button *all = new Fl_Radio_Button(10, 10, 180, 30,
|
||||
(lang.compare(0, 2, "en") == 0) ? "Everything" : "全部");
|
||||
Fl_Radio_Button *len = new Fl_Radio_Button(10, 50, 180, 30,
|
||||
(lang.compare(0, 2, "en") == 0) ? "Length" : "長さ");
|
||||
Fl_Radio_Button *chr = new Fl_Radio_Button(200, 10, 180, 30,
|
||||
(lang.compare(0, 2, "en") == 0) ? "Strength" : "強さ");
|
||||
Fl_Radio_Button *dup = new Fl_Radio_Button(200, 50, 180, 30,
|
||||
(lang.compare(0, 2, "en") == 0) ? "Duplicate" : "服数度");
|
||||
|
||||
dialog->add(all);
|
||||
dialog->add(len);
|
||||
dialog->add(chr);
|
||||
dialog->add(dup);
|
||||
|
||||
InputData *inputs = new InputData();
|
||||
inputs->all = all;
|
||||
inputs->len = len;
|
||||
inputs->chr = chr;
|
||||
inputs->dup = dup;
|
||||
inputs->dialog = dialog;
|
||||
|
||||
Fl_Button *okbtn = new Fl_Button(105, 100, 80, 30, "OK");
|
||||
Fl_Button *cancelbtn = new Fl_Button(205, 100, 80, 30,
|
||||
(lang.compare(0, 2, "en") == 0 ? "Cancel" : "キャンセル"));
|
||||
|
||||
okbtn->callback(static_ok_cb, inputs);
|
||||
cancelbtn->callback(static_cancel_cb, dialog);
|
||||
|
||||
dialog->add(okbtn);
|
||||
dialog->add(cancelbtn);
|
||||
|
||||
dialog->end();
|
||||
dialog->set_modal();
|
||||
dialog->show();
|
||||
}
|
||||
|
||||
void Chkpass::static_ok_cb(Fl_Widget *w, void *data) {
|
||||
(void)w;
|
||||
InputData *inputs = (InputData *)data;
|
||||
|
||||
chk.chk_cb(nullptr, inputs);
|
||||
}
|
||||
|
||||
void Chkpass::static_cancel_cb(Fl_Widget *w, void *data) {
|
||||
((Chkpass *)data)->cancel_cb(w, data);
|
||||
}
|
||||
45
src/chkpass.hh
Normal file
@@ -0,0 +1,45 @@
|
||||
#ifndef CHKPASS_HH
|
||||
#define CHKPASS_HH
|
||||
|
||||
#include <FL/Fl_Button.H>
|
||||
#include <FL/Fl_Radio_Button.H>
|
||||
|
||||
#include "dialog.hh"
|
||||
|
||||
#include <string>
|
||||
|
||||
class Chkpass : public Dialog {
|
||||
public:
|
||||
Fl_Button *btn = nullptr;
|
||||
Fl_Radio_Button *allChk = nullptr;
|
||||
Fl_Radio_Button *lenChk = nullptr;
|
||||
Fl_Radio_Button *charChk = nullptr;
|
||||
Fl_Radio_Button *dupChk = nullptr;
|
||||
bool isAll, isLen, isChar, isDup = false;
|
||||
|
||||
static void dialog_cb(Fl_Widget *w, void *);
|
||||
void showRes();
|
||||
void chk_cb(Fl_Widget *, void *);
|
||||
bool exec();
|
||||
|
||||
private:
|
||||
int vulncount = 0;
|
||||
int weaklencount = 0;
|
||||
int weakcharcount = 0;
|
||||
int duppasscount = 0;
|
||||
|
||||
std::size_t minimumlen = 12;
|
||||
std::size_t recommendlen = 64;
|
||||
|
||||
std::string spchar = "!@#$%^&*()-_=+[]{}|;:'\",.<>?/\\`~";
|
||||
|
||||
void lenPass(const std::string &path, const std::string &pass,
|
||||
const std::string &lang);
|
||||
void charPass(const std::string &path, const std::string &pass,
|
||||
const std::string &lang);
|
||||
void dupPass(const std::string &path, const std::string &pass,
|
||||
const std::string &lang);
|
||||
static void static_ok_cb(Fl_Widget *w, void *data);
|
||||
static void static_cancel_cb(Fl_Widget *w, void *data);
|
||||
};
|
||||
#endif
|
||||
82
src/common.cc
Normal file
@@ -0,0 +1,82 @@
|
||||
#include "common.hh"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <errno.h>
|
||||
|
||||
std::string Common::getbasedir(bool trailing) {
|
||||
std::string homedir = std::getenv("HOME") ? std::getenv("HOME") : "";
|
||||
if (homedir.empty()) return "";
|
||||
|
||||
#if defined(__HAIKU__)
|
||||
std::string basedir = "/config/settings/sp";
|
||||
std::string slash = "/";
|
||||
#elif defined(_WIN32)
|
||||
std::string basedir = "\\AppData\\Local\\076\\sp";
|
||||
std::string slash = "\\";
|
||||
#else
|
||||
std::string basedir = "/.local/share/sp";
|
||||
std::string slash = "/";
|
||||
#endif
|
||||
|
||||
return trailing ? (homedir + basedir + slash) : (homedir + basedir);
|
||||
}
|
||||
|
||||
std::string Common::getlang() {
|
||||
const char *env = std::getenv("SP_LANG");
|
||||
std::string lang;
|
||||
|
||||
if (env) lang = env;
|
||||
else lang = "ja";
|
||||
|
||||
return lang;
|
||||
}
|
||||
|
||||
int Common::mkdir_r(const std::string &path, mode_t mode) {
|
||||
char tmp[256];
|
||||
size_t len;
|
||||
|
||||
std::snprintf(tmp, sizeof(tmp), "%s", path.c_str());
|
||||
|
||||
len = std::strlen(tmp);
|
||||
if (tmp[len - 1] == '/') {
|
||||
tmp[len - 1] = 0;
|
||||
}
|
||||
|
||||
for (char *p = tmp + 1; *p; p++) {
|
||||
if (*p == '/') {
|
||||
*p = 0;
|
||||
if (mkdir(tmp, mode) != 0 && errno != EEXIST) return -1;
|
||||
*p = '/';
|
||||
}
|
||||
}
|
||||
|
||||
if (mkdir(tmp, mode) != 0 && errno != EEXIST) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Common::tmpcopy(const std::string &inpath, const std::string &outpath) {
|
||||
std::ifstream src(inpath, std::ios::binary);
|
||||
std::ofstream dst(outpath, std::ios::binary);
|
||||
}
|
||||
|
||||
std::vector<std::string> Common::explode(const std::string &str, char delimiter) {
|
||||
std::vector<std::string> tokens;
|
||||
std::string token;
|
||||
size_t start = 0, end = 0;
|
||||
|
||||
while ((end = str.find(delimiter, start)) != std::string::npos) {
|
||||
tokens.push_back(str.substr(start, end - start));
|
||||
start = end + 1;
|
||||
}
|
||||
tokens.push_back(str.substr(start));
|
||||
|
||||
return tokens;
|
||||
}
|
||||
18
src/common.hh
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef COMMON_HH
|
||||
#define COMMON_HH
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
class Common {
|
||||
public:
|
||||
static std::string getbasedir(bool trailing);
|
||||
static std::string getlang();
|
||||
int mkdir_r(const std::string &path, mode_t mode);
|
||||
void tmpcopy(const std::string &inpath, const std::string &outpath);
|
||||
static std::vector<std::string> explode(const std::string &str, char delimiter);
|
||||
};
|
||||
|
||||
#endif
|
||||
147
src/delpass.cc
Normal file
@@ -0,0 +1,147 @@
|
||||
#include "common.hh"
|
||||
#include "delpass.hh"
|
||||
#include "../main.hh"
|
||||
|
||||
#include <FL/fl_ask.H>
|
||||
#include <FL/Fl_Input.H>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
Delpass del;
|
||||
|
||||
struct InputData {
|
||||
Fl_Input *txtin;
|
||||
Fl_Window *dialog;
|
||||
};
|
||||
|
||||
void Delpass::setFile(std::string &f) {
|
||||
del.file = f;
|
||||
}
|
||||
|
||||
std::string Delpass::getFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
bool Delpass::exec(const std::string &file, bool force) {
|
||||
std::string lang = Common::getlang();
|
||||
|
||||
std::string basedir = Common::getbasedir(true);
|
||||
std::string ext = ".gpg";
|
||||
|
||||
// ファイルが既に存在するかどうか確認
|
||||
if (access(file.c_str(), F_OK) != 0) {
|
||||
std::string err = (lang.compare(0, 2, "en") == 0 ?
|
||||
"Password does not exist" :
|
||||
"パスワードが存在しません");
|
||||
fl_alert("%s", err.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (unlink(file.c_str()) == -1) {
|
||||
std::string err = (lang.compare(0, 2, "en") == 0 ?
|
||||
"Password cannot be deleted" :
|
||||
"パスワードを削除出来ませんですた");
|
||||
fl_alert("%s", err.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// 空のディレクトリの場合
|
||||
std::vector<std::string> tokens = Common::explode(file, '/');
|
||||
std::string passpath = basedir + tokens[0];
|
||||
|
||||
for (size_t i = 1; i < tokens.size(); ++i) {
|
||||
if (i == tokens.size() - 1) continue;
|
||||
passpath += "/" + tokens[i];
|
||||
}
|
||||
|
||||
for (int i = tokens.size() - 1; i >= 0; --i) {
|
||||
// ~/.local/share/sp を削除したら危険
|
||||
if (passpath.compare(0, basedir.size(), basedir) == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
// ディレクトリが空じゃない場合、削除を止める
|
||||
if (rmdir(passpath.c_str()) == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
size_t last_slash = passpath.find_last_of('/');
|
||||
if (last_slash != std::string::npos) {
|
||||
passpath.erase(last_slash);
|
||||
}
|
||||
}
|
||||
|
||||
if (force) return true;
|
||||
|
||||
std::string msg = (lang.compare(0, 2, "en") == 0 ?
|
||||
"The password got deleted" :
|
||||
"パスワードを削除しました");
|
||||
fl_alert("%s", msg.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
void Delpass::delete_cb(Fl_Widget *, void *data) {
|
||||
InputData *inputs = (InputData *)data;
|
||||
|
||||
if (inputs) {
|
||||
file = inputs->txtin->value();
|
||||
}
|
||||
|
||||
exec(file, false);
|
||||
std::vector<std::string> fpaths;
|
||||
std::string rdir = Common::getbasedir(false);
|
||||
|
||||
std::string mockpath = "";
|
||||
clearpaths(true, mockpath);
|
||||
scandir(rdir, rdir, fpaths);
|
||||
updatelist();
|
||||
}
|
||||
|
||||
void Delpass::dialog_cb(Fl_Widget *w, void *data) {
|
||||
(void)w;
|
||||
(void)data;
|
||||
std::string lang = Common::getlang();
|
||||
|
||||
Fl_Input *txtin = new Fl_Input(150, 20, 180, 30,
|
||||
(lang.compare(0, 2, "en") == 0 ? "Path:" : "パス:"));
|
||||
txtin->hide();
|
||||
txtin->value(del.getFile().c_str());
|
||||
|
||||
InputData *inputs = new InputData();
|
||||
inputs->txtin = txtin;
|
||||
|
||||
std::string asking =
|
||||
(lang.compare(0, 2, "en") == 0 ?
|
||||
"Are you sure you want to delete the password '" + del.getFile() + "'?" :
|
||||
"パスワード「" + del.getFile() + "」を本当に削除する事が宜しいでしょうか?");
|
||||
int confirm = fl_choice("%s",
|
||||
(lang.compare(0, 2, "en") == 0 ? "Cancel" : "キャンセル"),
|
||||
(lang.compare(0, 2, "en") == 0 ? "Delete" : "削除"),
|
||||
nullptr, asking.c_str());
|
||||
|
||||
if (confirm == 0) {
|
||||
std::string err = (lang.compare(0, 2, "en") == 0 ?
|
||||
"Not deleted" :
|
||||
"削除しませんでした");
|
||||
fl_alert("%s", err.c_str());
|
||||
} else {
|
||||
static_ok_cb(w, inputs);
|
||||
}
|
||||
}
|
||||
|
||||
void Delpass::static_ok_cb(Fl_Widget *w, void *data) {
|
||||
(void)w;
|
||||
InputData *inputs = (InputData *)data;
|
||||
|
||||
del.delete_cb(nullptr, inputs);
|
||||
std::vector<std::string> fpaths;
|
||||
std::string rdir = Common::getbasedir(false);
|
||||
std::string curpath = "";
|
||||
clearpaths(true, curpath);
|
||||
scandir(rdir, rdir, fpaths);
|
||||
updatelist();
|
||||
}
|
||||
|
||||
void Delpass::static_cancel_cb(Fl_Widget *w, void *data) {
|
||||
((Delpass *)data)->cancel_cb(w, data);
|
||||
}
|
||||
25
src/delpass.hh
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef DELPASS_HH
|
||||
#define DELPASS_HH
|
||||
|
||||
#include <FL/Fl_Button.H>
|
||||
#include <string>
|
||||
|
||||
#include "dialog.hh"
|
||||
|
||||
class Delpass : public Dialog {
|
||||
public:
|
||||
Fl_Button *btn = nullptr;
|
||||
std::string file;
|
||||
|
||||
static void dialog_cb(Fl_Widget *w, void *);
|
||||
void delete_cb(Fl_Widget *, void *data);
|
||||
static void setFile(std::string &f);
|
||||
std::string getFile();
|
||||
bool exec(const std::string &file, bool force);
|
||||
|
||||
private:
|
||||
static void static_ok_cb(Fl_Widget *w, void *data);
|
||||
static void static_cancel_cb(Fl_Widget *w, void *data);
|
||||
};
|
||||
|
||||
#endif
|
||||
13
src/dialog.cc
Normal file
@@ -0,0 +1,13 @@
|
||||
#include "dialog.hh"
|
||||
|
||||
#include <FL/Fl_Window.H>
|
||||
|
||||
void Dialog::ok_cb(Fl_Widget *w, void *dialog) {
|
||||
(void)w;
|
||||
((Fl_Window *)dialog)->hide();
|
||||
}
|
||||
|
||||
void Dialog::cancel_cb(Fl_Widget *w, void *dialog) {
|
||||
(void)w;
|
||||
((Fl_Window *)dialog)->hide();
|
||||
}
|
||||
12
src/dialog.hh
Normal file
@@ -0,0 +1,12 @@
|
||||
#ifndef DIALOG_HH
|
||||
#define DIALOG_HH
|
||||
|
||||
#include <FL/Fl_Widget.H>
|
||||
|
||||
class Dialog {
|
||||
public:
|
||||
static void ok_cb(Fl_Widget *w, void *dialog);
|
||||
static void cancel_cb(Fl_Widget *w, void *dialog);
|
||||
};
|
||||
|
||||
#endif
|
||||
189
src/editpass.cc
Normal file
@@ -0,0 +1,189 @@
|
||||
#include "common.hh"
|
||||
#include "editpass.hh"
|
||||
#include "delpass.hh"
|
||||
#include "addpass.hh"
|
||||
#include "../main.hh"
|
||||
|
||||
#include <FL/Fl_Window.H>
|
||||
#include <FL/fl_ask.H>
|
||||
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
|
||||
Editpass edit;
|
||||
|
||||
struct InputData {
|
||||
Fl_Input *txtin;
|
||||
Fl_Secret_Input *pass1;
|
||||
Fl_Secret_Input *pass2;
|
||||
Fl_Window *dialog;
|
||||
};
|
||||
|
||||
void Editpass::setFile(std::string &f) {
|
||||
edit.file = f;
|
||||
}
|
||||
|
||||
std::string Editpass::getFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
bool Editpass::exec(const std::string &file, const std::string &pass) {
|
||||
std::string lang = Common::getlang();
|
||||
|
||||
Common c;
|
||||
c.tmpcopy(file, "/tmp/simpas-tmp.gpg");
|
||||
|
||||
Delpass d;
|
||||
bool isdel = d.exec(file, true);
|
||||
if (!isdel) {
|
||||
std::string err =
|
||||
(lang.compare(0, 2, "en") == 0 ? "Failed to edit." : "編集に失敗。");
|
||||
fl_alert("%s", err.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
Addpass a;
|
||||
bool isadd = a.exec(file, pass, true);
|
||||
if (!isadd) {
|
||||
std::string err =
|
||||
(lang.compare(0, 2, "en") == 0 ? "Failed to edit." : "編集に失敗。");
|
||||
fl_alert("%s", err.c_str());
|
||||
// TODO: うまく動いているかの確認。それは次のバージョンから・・・
|
||||
/* std::vector<std::string> tokens = Common::explode(file, '/'); */
|
||||
/* std::string passpath = tokens[0]; */
|
||||
|
||||
/* for (size_t i = 1; i < tokens.size(); ++i) { */
|
||||
/* if (i == tokens.size() - 1) continue; */
|
||||
/* passpath += "/" + tokens[i]; */
|
||||
/* } */
|
||||
|
||||
/* for (int i = tokens.size() - 1; i >= 0; ++i) { */
|
||||
/* if (c.mkdir_r(passpath, 0755) == -1) break; */
|
||||
|
||||
/* size_t last_slash = passpath.find_last_of('/'); */
|
||||
/* if (last_slash != std::string::npos) passpath.erase(last_slash); */
|
||||
/* } */
|
||||
c.tmpcopy("/tmp/simpas-tmp.gpg", file);
|
||||
unlink("/tmp/simpas-tmp.gpg");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string msg =
|
||||
(lang.compare(0, 2, "en") == 0 ? "Edit success." : "編集に成功。");
|
||||
fl_alert("%s", msg.c_str());
|
||||
unlink("/tmp/simpas-tmp.gpg");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Editpass::edit_cb(Fl_Widget *, void *data) {
|
||||
InputData *inputs = (InputData *)data;
|
||||
std::string lang = Common::getlang();
|
||||
|
||||
if (inputs) {
|
||||
file = inputs->txtin->value();
|
||||
if (file.empty()) {
|
||||
std::string err =
|
||||
(lang.compare(0, 2, "en") == 0 ?
|
||||
"Please fill in the path." :
|
||||
"パスをご入力下さい。");
|
||||
fl_alert("%s", err.c_str());
|
||||
return;
|
||||
}
|
||||
inputpass1 = inputs->pass1->value();
|
||||
if (inputpass1.empty()) {
|
||||
std::string err =
|
||||
(lang.compare(0, 2, "en") == 0 ?
|
||||
"Please fill in the password." :
|
||||
"パスワードをご入力下さい。");
|
||||
fl_alert("%s", err.c_str());
|
||||
return;
|
||||
}
|
||||
inputpass2 = inputs->pass2->value();
|
||||
if (inputpass2.empty()) {
|
||||
std::string err =
|
||||
(lang.compare(0, 2, "en") == 0 ?
|
||||
"Please fill in the password (confirm)." :
|
||||
"パスワード (確認)をご入力下さい。");
|
||||
fl_alert("%s", err.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (inputpass1 != inputpass2) {
|
||||
std::string err =
|
||||
(lang.compare(0, 2, "en") == 0 ?
|
||||
"Password does not match." :
|
||||
"パスワードが一致していません。");
|
||||
fl_alert("%s", err.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (exec(file, inputpass1)) {
|
||||
inputs->dialog->hide();
|
||||
}
|
||||
} else {
|
||||
std::string err =
|
||||
(lang.compare(0, 2, "en") == 0 ?
|
||||
"Please fill in all the fields." :
|
||||
"全てのフィールドをご入力下さい。");
|
||||
fl_alert("%s", err.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void Editpass::dialog_cb(Fl_Widget *w, void *data) {
|
||||
(void)w;
|
||||
(void)data;
|
||||
std::string lang = Common::getlang();
|
||||
Fl_Window *dialog = new Fl_Window(400, 160,
|
||||
(lang.compare(0, 2, "en") == 0 ? "Edit password" : "パスワードの編集"));
|
||||
|
||||
Fl_Input *txtin = new Fl_Input(150, 20, 180, 30,
|
||||
(lang.compare(0, 2, "en") == 0 ? "Path:" : "パス:"));
|
||||
Fl_Secret_Input *pass1 = new Fl_Secret_Input(150, 20, 180, 30,
|
||||
(lang.compare(0, 2, "en") == 0 ? "Password:" : "パスワード:"));
|
||||
Fl_Secret_Input *pass2 = new Fl_Secret_Input(150, 60, 180, 30,
|
||||
(lang.compare(0, 2, "en") == 0 ? "Password (confirm):" : "パスワード (確認):"));
|
||||
dialog->add(pass1);
|
||||
dialog->add(pass2);
|
||||
txtin->hide();
|
||||
txtin->value(edit.getFile().c_str());
|
||||
|
||||
InputData *inputs = new InputData();
|
||||
inputs->txtin = txtin;
|
||||
inputs->pass1 = pass1;
|
||||
inputs->pass2 = pass2;
|
||||
inputs->dialog = dialog;
|
||||
|
||||
Fl_Button *okbtn = new Fl_Button(60, 110, 80, 30, "OK");
|
||||
Fl_Button *cancelbtn = new Fl_Button(160, 110, 80, 30,
|
||||
(lang.compare(0, 2, "en") == 0 ? "Cancel" : "キャンセル"));
|
||||
|
||||
okbtn->callback(static_ok_cb, inputs);
|
||||
cancelbtn->callback(static_cancel_cb, dialog);
|
||||
|
||||
dialog->add(okbtn);
|
||||
dialog->add(cancelbtn);
|
||||
|
||||
dialog->end();
|
||||
dialog->set_modal();
|
||||
dialog->show();
|
||||
}
|
||||
|
||||
void Editpass::static_ok_cb(Fl_Widget *w, void *data) {
|
||||
(void)w;
|
||||
InputData *inputs = (InputData *)data;
|
||||
|
||||
edit.edit_cb(nullptr, inputs);
|
||||
std::vector<std::string> fpaths;
|
||||
std::string rdir = Common::getbasedir(false);
|
||||
std::string curpath = inputs->txtin->value();
|
||||
clearpaths(false, curpath);
|
||||
scandir(rdir, rdir, fpaths);
|
||||
updatelist();
|
||||
browse(curpath, false);
|
||||
}
|
||||
|
||||
void Editpass::static_cancel_cb(Fl_Widget *w, void *data) {
|
||||
((Editpass *)data)->cancel_cb(w, data);
|
||||
}
|
||||
30
src/editpass.hh
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifndef EDITPASS_HH
|
||||
#define EDITPASS_HH
|
||||
|
||||
#include <FL/Fl_Button.H>
|
||||
#include <FL/Fl_Input.H>
|
||||
#include <FL/Fl_Secret_Input.H>
|
||||
|
||||
#include "dialog.hh"
|
||||
|
||||
#include <string>
|
||||
|
||||
class Editpass : public Dialog {
|
||||
public:
|
||||
Fl_Button *btn = nullptr;
|
||||
std::string file;
|
||||
std::string inputpass1;
|
||||
std::string inputpass2;
|
||||
|
||||
static void dialog_cb(Fl_Widget *w, void *);
|
||||
void edit_cb(Fl_Widget *, void *data);
|
||||
static void setFile(std::string &f);
|
||||
std::string getFile();
|
||||
bool exec(const std::string &file, const std::string &pass);
|
||||
|
||||
private:
|
||||
static void static_ok_cb(Fl_Widget *w, void *data);
|
||||
static void static_cancel_cb(Fl_Widget *w, void *data);
|
||||
};
|
||||
|
||||
#endif
|
||||
108
src/genpass.cc
Normal file
@@ -0,0 +1,108 @@
|
||||
#include "common.hh"
|
||||
#include "genpass.hh"
|
||||
|
||||
#include <FL/Fl_Window.H>
|
||||
#include <FL/fl_ask.H>
|
||||
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
|
||||
Genpass gen;
|
||||
|
||||
std::string Genpass::exec(int count, bool issecure) {
|
||||
std::string lang = Common::getlang();
|
||||
|
||||
const std::string charset_risky =
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
const std::string charset_secure =
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!\"#$%&'()=~-^\\|_@`[{]};:+*<>,./?";
|
||||
const std::string &charset = issecure ? charset_secure : charset_risky;
|
||||
|
||||
std::ifstream fp("/dev/random", std::ios_base::binary);
|
||||
if (!fp.is_open()) {
|
||||
std::string err = (lang.compare(0, 2, "en") == 0 ?
|
||||
"Could not open /dev/random" :
|
||||
"/dev/randomを開けられませんでした");
|
||||
fl_alert("%s", err.c_str());
|
||||
return "";
|
||||
}
|
||||
|
||||
std::vector<char> password(count + 1);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
unsigned char key;
|
||||
fp.read(reinterpret_cast<char *>(&key), sizeof(key));
|
||||
if (!fp) {
|
||||
std::string err = (lang.compare(0, 2, "en") == 0 ?
|
||||
"Could not read /dev/random" :
|
||||
"/dev/randomから読み込みに失敗");
|
||||
fl_alert("%s", err.c_str());
|
||||
fp.close();
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
password[i] = charset[key % charset.size()];
|
||||
}
|
||||
|
||||
password[count] = '\0';
|
||||
fp.close();
|
||||
|
||||
return std::string(password.data());
|
||||
}
|
||||
|
||||
void Genpass::generate_cb(Fl_Widget *, void *) {
|
||||
int count = std::stoi(gen.counter->value());
|
||||
bool issecure = gen.securechk->value() > 0;
|
||||
|
||||
std::string password = gen.exec(count, issecure);
|
||||
gen.res->value(password.c_str());
|
||||
}
|
||||
|
||||
void Genpass::dialog_cb(Fl_Widget *w, void *) {
|
||||
(void)w;
|
||||
std::string lang = Common::getlang();
|
||||
Fl_Window *dialog = new Fl_Window(450, 250,
|
||||
(lang.compare(0, 2, "en") == 0 ? "Generate password" : "パスワードの作成"));
|
||||
|
||||
gen.counter = new Fl_Input(120, 20, 100, 30,
|
||||
(lang.compare(0, 2, "en") == 0 ? "Length:" : "長さ:"));
|
||||
gen.counter->type(FL_INT_INPUT);
|
||||
gen.counter->value("64");
|
||||
dialog->add(gen.counter);
|
||||
|
||||
gen.securechk = new Fl_Check_Button(120, 70, 150, 30,
|
||||
(lang.compare(0, 2, "en") == 0 ? "Secure?" : "安全化?"));
|
||||
gen.securechk->value(1);
|
||||
dialog->add(gen.securechk);
|
||||
|
||||
gen.genbtn = new Fl_Button(120, 110, 150, 30,
|
||||
(lang.compare(0, 2, "en") == 0 ? "Generate" : "作成"));
|
||||
gen.genbtn->callback(generate_cb);
|
||||
dialog->add(gen.genbtn);
|
||||
|
||||
gen.res = new Fl_Output(120, 150, 300, 30,
|
||||
(lang.compare(0, 2, "en") == 0 ? "Password:" : "パスワード:"));
|
||||
gen.res->value("");
|
||||
dialog->add(gen.res);
|
||||
|
||||
Fl_Button *okbtn = new Fl_Button(60, 200, 80, 30, "OK");
|
||||
Fl_Button *cancelbtn = new Fl_Button(160, 200, 80, 30,
|
||||
(lang.compare(0, 2, "en") == 0 ? "Cancel" : "キャンセル"));
|
||||
|
||||
okbtn->callback(static_ok_cb, dialog);
|
||||
cancelbtn->callback(static_cancel_cb, dialog);
|
||||
|
||||
dialog->add(okbtn);
|
||||
dialog->add(cancelbtn);
|
||||
|
||||
dialog->end();
|
||||
dialog->set_modal();
|
||||
dialog->show();
|
||||
}
|
||||
|
||||
void Genpass::static_ok_cb(Fl_Widget *w, void *data) {
|
||||
((Genpass *)data)->ok_cb(w, data);
|
||||
}
|
||||
|
||||
void Genpass::static_cancel_cb(Fl_Widget *w, void *data) {
|
||||
((Genpass *)data)->cancel_cb(w, data);
|
||||
}
|
||||
33
src/genpass.hh
Normal file
@@ -0,0 +1,33 @@
|
||||
#ifndef GENPASS_HH
|
||||
#define GENPASS_HH
|
||||
|
||||
#include <FL/Fl_Button.H>
|
||||
#include <FL/Fl_Input.H>
|
||||
#include <FL/Fl_Secret_Input.H>
|
||||
#include <FL/Fl_Check_Button.H>
|
||||
#include <FL/Fl_Button.H>
|
||||
#include <FL/Fl_Output.H>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "dialog.hh"
|
||||
|
||||
class Genpass : public Dialog {
|
||||
public:
|
||||
Fl_Button *btn = nullptr;
|
||||
|
||||
std::string exec(int count, bool issecure);
|
||||
static void dialog_cb(Fl_Widget *w, void *);
|
||||
static void generate_cb(Fl_Widget *, void *data);
|
||||
|
||||
private:
|
||||
Fl_Input *counter = nullptr;
|
||||
Fl_Check_Button *securechk = nullptr;
|
||||
Fl_Button *genbtn = nullptr;
|
||||
Fl_Output *res = nullptr;
|
||||
|
||||
static void static_ok_cb(Fl_Widget *w, void *data);
|
||||
static void static_cancel_cb(Fl_Widget *w, void *data);
|
||||
};
|
||||
|
||||
#endif
|
||||
58
src/initpass.cc
Normal file
@@ -0,0 +1,58 @@
|
||||
#include "common.hh"
|
||||
#include "initpass.hh"
|
||||
|
||||
#include <FL/Fl_Window.H>
|
||||
#include <FL/fl_ask.H>
|
||||
|
||||
#include <cerrno>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
Initpass init;
|
||||
|
||||
void Initpass::exec(const std::string &gpgid) {
|
||||
std::string lang = Common::getlang();
|
||||
std::string basedir = Common::getbasedir(true);
|
||||
|
||||
Common common;
|
||||
if (common.mkdir_r(basedir, 0755) != 0 && errno != EEXIST) {
|
||||
std::cout << basedir << std::endl;
|
||||
std::string err = (lang.compare(0, 2, "en") == 0 ?
|
||||
"Failed to create directory." :
|
||||
"ディレクトリを作成に失敗。");
|
||||
fl_alert("%s", err.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
std::string gpgidpath = basedir + "/.gpg-id";
|
||||
|
||||
struct stat statbuf;
|
||||
if (stat(gpgidpath.c_str(), &statbuf) == 0) {
|
||||
std::string err = (lang.compare(0, 2, "en") == 0 ?
|
||||
".gpg-id file already exists." :
|
||||
".gpg-idファイルは既に存在します。");
|
||||
fl_alert("%s", err.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
std::ofstream gpgidfile(gpgidpath);
|
||||
if (!gpgidfile.is_open()) {
|
||||
std::string err = (lang.compare(0, 2, "en") == 0 ?
|
||||
"Failed to write .gpg-id file." :
|
||||
".gpg-idファイルを書き込めません。");
|
||||
fl_alert("%s", err.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
gpgidfile << gpgid + '\n';
|
||||
|
||||
gpgidfile.close();
|
||||
|
||||
std::string msg = (lang.compare(0, 2, "en") == 0 ?
|
||||
"Initialization completed." :
|
||||
"初期設定に完了しました。");
|
||||
fl_alert("%s", msg.c_str());
|
||||
}
|
||||
19
src/initpass.hh
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef INITPASS_HH
|
||||
#define INITPASS_HH
|
||||
|
||||
#include <FL/Fl_Button.H>
|
||||
#include <FL/Fl_Input.H>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "dialog.hh"
|
||||
|
||||
class Initpass : public Dialog {
|
||||
public:
|
||||
Fl_Button *btn = nullptr;
|
||||
Fl_Input *gpgid = nullptr;
|
||||
|
||||
void exec(const std::string &gpgid);
|
||||
};
|
||||
|
||||
#endif
|
||||
119
src/otppass.cc
Normal file
@@ -0,0 +1,119 @@
|
||||
#include "common.hh"
|
||||
#include "base32.hh"
|
||||
#include "otppass.hh"
|
||||
|
||||
#include <openssl/hmac.h>
|
||||
#include <openssl/sha.h>
|
||||
|
||||
#include <FL/fl_ask.H>
|
||||
|
||||
#if defined(__APPLE)
|
||||
#include <libkern/OSByteOrder.h>
|
||||
#define htobe64(x) OSSwapHostToBigInt64(x)
|
||||
#endif
|
||||
|
||||
std::vector<unsigned char> Otppass::extract_secret(const std::string &otpauth_url) {
|
||||
std::string lang = Common::getlang();
|
||||
|
||||
auto secret_start = otpauth_url.find("secret=");
|
||||
if (secret_start == std::string::npos) {
|
||||
std::string err = (lang.compare(0, 2, "en") == 0 ?
|
||||
"Failed to find secret in the OTPAuth URL" :
|
||||
"OTPAuth URLの中に、シークレットを見つけられませんでした");
|
||||
fl_alert("%s", err.c_str());
|
||||
return {};
|
||||
}
|
||||
secret_start += 7;
|
||||
|
||||
auto secret_end = otpauth_url.find('&', secret_start);
|
||||
if (secret_end == std::string::npos) {
|
||||
secret_end = otpauth_url.length();
|
||||
}
|
||||
|
||||
if (secret_end < secret_start) {
|
||||
std::string err = (lang.compare(0, 2, "en") == 0 ?
|
||||
"Incorrect secret range" :
|
||||
"不正なシークレットの距離");
|
||||
fl_alert("%s", err.c_str());
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string secret_encoded =
|
||||
otpauth_url.substr(secret_start, secret_end - secret_start);
|
||||
|
||||
std::vector<unsigned char> secret_decoded;
|
||||
secret_decoded = Base32::decode(secret_encoded.c_str());
|
||||
|
||||
if (secret_decoded.empty()) {
|
||||
std::string err = (lang.compare(0, 2, "en") == 0 ?
|
||||
"Failed to decrypt BASE32" :
|
||||
"BASE32の復号化に失敗");
|
||||
fl_alert("%s", err.c_str());
|
||||
return {};
|
||||
}
|
||||
|
||||
return secret_decoded;
|
||||
}
|
||||
|
||||
#if defined(__HAIKU__) || defined(__linux)
|
||||
uint64_t Otppass::htobe64(uint64_t counter) {
|
||||
uint64_t res = 0;
|
||||
uint8_t *dest = reinterpret_cast<uint8_t*>(&res);
|
||||
const uint8_t *src = reinterpret_cast<const uint8_t*>(&counter);
|
||||
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
dest[i] = src[7 - i];
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t Otppass::generate_totp(const std::vector<unsigned char> &secret,
|
||||
uint64_t counter) {
|
||||
counter = htobe64(counter);
|
||||
|
||||
unsigned char hash[SHA_DIGEST_LENGTH];
|
||||
HMAC(
|
||||
EVP_sha1(),
|
||||
secret.data(),
|
||||
secret.size(),
|
||||
reinterpret_cast<unsigned char *>(&counter),
|
||||
sizeof(counter),
|
||||
hash,
|
||||
NULL
|
||||
);
|
||||
|
||||
int offset = hash[SHA_DIGEST_LENGTH - 1] & 0x0F;
|
||||
uint32_t truncated_hash =
|
||||
(hash[offset] & 0x7F) << 24 |
|
||||
(hash[offset + 1] & 0xFF) << 16 |
|
||||
(hash[offset + 2] & 0xFF) << 8 |
|
||||
(hash[offset + 3] & 0xFF);
|
||||
|
||||
return truncated_hash % 1000000;
|
||||
}
|
||||
|
||||
std::string Otppass::exec(std::string &secret) {
|
||||
std::string lang = Common::getlang();
|
||||
|
||||
try {
|
||||
std::vector<unsigned char> secret_decoded = extract_secret(secret);
|
||||
|
||||
time_t current_time = time(nullptr);
|
||||
uint64_t counter = current_time / 30;
|
||||
uint32_t otp =
|
||||
generate_totp(secret_decoded, counter);
|
||||
|
||||
char otpres[7];
|
||||
std::snprintf(otpres, sizeof(otpres), "%06u", otp);
|
||||
|
||||
return std::string(otpres);
|
||||
} catch (const std::exception &e) {
|
||||
std::string err = (lang.compare(0, 2, "en") == 0 ?
|
||||
"Error" :
|
||||
"エラー");
|
||||
fl_alert("%s: %s", err.c_str(), e.what());
|
||||
return "";
|
||||
}
|
||||
}
|
||||
21
src/otppass.hh
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef OTPPASS_HH
|
||||
#define OTPPASS_HH
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class Otppass {
|
||||
public:
|
||||
std::string exec(std::string &file);
|
||||
|
||||
private:
|
||||
std::vector<unsigned char> extract_secret(const std::string &otpauth_url);
|
||||
#if defined(__HAIKU__) || defined(__linux)
|
||||
uint64_t htobe64(uint64_t counter);
|
||||
#endif
|
||||
uint32_t generate_totp(const std::vector<unsigned char> &secret,
|
||||
uint64_t counter);
|
||||
};
|
||||
|
||||
#endif
|
||||
129
src/showpass.cc
Normal file
@@ -0,0 +1,129 @@
|
||||
#include "common.hh"
|
||||
#include "showpass.hh"
|
||||
#include "../main.hh"
|
||||
#include "otppass.hh"
|
||||
|
||||
#include <gpgme++/decryptionresult.h>
|
||||
|
||||
#undef Status
|
||||
#undef None
|
||||
#include <FL/Fl.H>
|
||||
#include <FL/fl_ask.H>
|
||||
#include <FL/Fl_Text_Buffer.H>
|
||||
#include <FL/Fl_Text_Display.H>
|
||||
|
||||
void Showpass::otpupdate_cb(void *o) {
|
||||
Showpass *show = static_cast<Showpass *>(o);
|
||||
Otppass otp;
|
||||
|
||||
if (!show->otpSav.empty()) {
|
||||
std::string dec = otp.exec(show->otpSav);
|
||||
|
||||
realpass = dec;
|
||||
if (!isPassHidden) {
|
||||
textbuf->text(dec.c_str());
|
||||
textview->redraw();
|
||||
}
|
||||
}
|
||||
|
||||
Fl::repeat_timeout(1.0, otpupdate_cb, o);
|
||||
}
|
||||
|
||||
std::string Showpass::exec(const char *file, bool stfu) {
|
||||
std::string lang = Common::getlang();
|
||||
|
||||
try {
|
||||
std::setlocale(LC_ALL, "");
|
||||
GpgME::initializeLibrary();
|
||||
|
||||
gpg_error_t err = gpg_err_init();
|
||||
if (err) {
|
||||
if (!stfu) {
|
||||
std::string ero = (lang.compare(0, 2, "en") == 0 ?
|
||||
"Failed to generate GPGME" :
|
||||
"GPGエラーの設置に失敗");
|
||||
fl_alert("%s: %s", ero.c_str(), gpg_strerror(err));
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
std::unique_ptr<GpgME::Context> ctx =
|
||||
GpgME::Context::create(GpgME::Protocol::OpenPGP);
|
||||
if (!ctx) {
|
||||
if (!stfu) {
|
||||
std::string ero = (lang.compare(0, 2, "en") == 0 ?
|
||||
"Failed to create GPGME context" :
|
||||
"GPGMEコンテキストの作成に失敗");
|
||||
fl_alert("%s", ero.c_str());
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
if (ctx->protocol() != GpgME::Protocol::OpenPGP) {
|
||||
if (!stfu) {
|
||||
std::string ero = (lang.compare(0, 2, "en") == 0 ?
|
||||
"Failed to set OpenPGP protocol" :
|
||||
"OpenPGPプロトコールの設置に失敗");
|
||||
fl_alert("%s", ero.c_str());
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
std::ifstream gpgfile(file, std::ios::binary);
|
||||
if (!gpgfile.is_open()) {
|
||||
if (!stfu) {
|
||||
std::string ero = (lang.compare(0, 2, "en") == 0 ?
|
||||
"Unable to open the specified file" :
|
||||
"指定されたファイルを開けません");
|
||||
fl_alert("%s", ero.c_str());
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
GpgME::Data in(file);
|
||||
GpgME::Data out;
|
||||
|
||||
GpgME::DecryptionResult res = ctx->decrypt(in, out);
|
||||
if (res.error()) {
|
||||
if (!stfu) {
|
||||
std::string ero = (lang.compare(0, 2, "en") == 0 ?
|
||||
"Failed to decrypt" :
|
||||
"復号化に失敗");
|
||||
fl_alert("%s: %s", ero.c_str(), res.error().asString());
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string dec;
|
||||
char buf[512];
|
||||
ssize_t read_bytes;
|
||||
|
||||
out.seek(0, SEEK_SET);
|
||||
|
||||
while ((read_bytes = out.read(buf, sizeof(buf))) > 0) {
|
||||
dec.append(buf, read_bytes);
|
||||
}
|
||||
|
||||
if (dec.rfind("otpauth://", 0) == 0 && !stfu) {
|
||||
Otppass o;
|
||||
otpSav = dec;
|
||||
|
||||
Fl::remove_timeout(otpupdate_cb, this);
|
||||
Fl::add_timeout(1.0, otpupdate_cb, this);
|
||||
|
||||
return o.exec(dec);
|
||||
}
|
||||
|
||||
Fl::remove_timeout(otpupdate_cb, this);
|
||||
otpSav = "";
|
||||
return dec;
|
||||
} catch (const std::exception &e) {
|
||||
if (!stfu) {
|
||||
std::string err = (lang.compare(0, 2, "en") == 0 ?
|
||||
"Error" :
|
||||
"エラー");
|
||||
fl_alert("%s: %s", err.c_str(), e.what());
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
21
src/showpass.hh
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef SHOWPASS_HH
|
||||
#define SHOWPASS_HH
|
||||
|
||||
#include <gpgme++/context.h>
|
||||
#include <gpgme++/data.h>
|
||||
|
||||
#include <fstream>
|
||||
|
||||
class Showpass {
|
||||
public:
|
||||
std::string otpSav = "";
|
||||
|
||||
static void otpupdate_cb(void *o);
|
||||
std::string exec(const char *file, bool stfu);
|
||||
|
||||
private:
|
||||
void clean_up(GpgME::Context &ctx, GpgME::Data &in, GpgME::Data &out,
|
||||
std::ifstream &gpgfile);
|
||||
};
|
||||
|
||||
#endif
|
||||
198
src/vulnpass.cc
Normal file
@@ -0,0 +1,198 @@
|
||||
#include "common.hh"
|
||||
#include "vulnpass.hh"
|
||||
#include "showpass.hh"
|
||||
#include "../main.hh"
|
||||
|
||||
#include <FL/Fl.H>
|
||||
#include <FL/Fl_Window.H>
|
||||
#include <FL/fl_ask.H>
|
||||
#include <FL/Fl_Box.H>
|
||||
#include <FL/Fl_Button.H>
|
||||
#include <FL/Fl_Text_Display.H>
|
||||
#include <FL/Fl_Text_Buffer.H>
|
||||
#include <FL/Fl_Scrollbar.H>
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
Vulnpass vuln;
|
||||
int vulncount = 0;
|
||||
std::vector<std::string> vulnpaths;
|
||||
|
||||
bool Vulnpass::exec() {
|
||||
std::string lang = Common::getlang();
|
||||
|
||||
// pwndサーバに接続
|
||||
int sock;
|
||||
struct sockaddr_in srv;
|
||||
struct addrinfo hints, *addr;
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
int status = getaddrinfo("076.moe", nullptr, &hints, &addr);
|
||||
if (status != 0) {
|
||||
fl_alert("getaddrinfo: %s", gai_strerror(status));
|
||||
return false;
|
||||
}
|
||||
|
||||
sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sock == -1) {
|
||||
fl_alert(lang.compare(0, 2, "en") == 0 ?
|
||||
"Failed to create socket" : "ソケットを作成に失敗");
|
||||
return false;
|
||||
}
|
||||
|
||||
srv.sin_addr = ((struct sockaddr_in *)(addr->ai_addr))->sin_addr;
|
||||
srv.sin_family = AF_INET;
|
||||
srv.sin_port = htons(9951);
|
||||
|
||||
freeaddrinfo(addr);
|
||||
|
||||
if (connect(sock, (struct sockaddr *)&srv, sizeof(srv)) < 0) {
|
||||
fl_alert(lang.compare(0, 2, "en") == 0 ?
|
||||
"Failed to connect" : "接続に失敗");
|
||||
close(sock);
|
||||
return false;
|
||||
}
|
||||
|
||||
// パスワードをスキャンして
|
||||
Showpass show;
|
||||
|
||||
for (const auto &dispath : dispaths) {
|
||||
std::string fullpath = Common::getbasedir(true) + dispath + ".gpg";
|
||||
std::string pass = show.exec(fullpath.c_str(), true);
|
||||
if (pass.empty()) continue;
|
||||
|
||||
if (send(sock, pass.c_str(), pass.length(), 0) < 0) {
|
||||
fl_alert(lang.compare(0, 2, "en") == 0 ?
|
||||
"Failed to send" : "送信に失敗");
|
||||
close(sock);
|
||||
return false;
|
||||
}
|
||||
|
||||
char res[256] = {0};
|
||||
int reslen = recv(sock, res, sizeof(res) - 1, 0);
|
||||
if (reslen < 0) {
|
||||
fl_alert(lang.compare(0, 2, "en") == 0 ?
|
||||
"Failed to retrieve" : "受取に失敗");
|
||||
close(sock);
|
||||
return false;
|
||||
}
|
||||
|
||||
res[reslen] = '\0';
|
||||
std::string response(res);
|
||||
|
||||
if (response.compare(0, 1, "0") != 0) {
|
||||
vulnpaths.push_back(dispath);
|
||||
vulncount++;
|
||||
}
|
||||
|
||||
close(sock);
|
||||
|
||||
// 再接続
|
||||
sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sock == -1) {
|
||||
fl_alert(lang.compare(0, 2, "en") == 0 ?
|
||||
"Failed to create socket" : "ソケットを作成に失敗");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (connect(sock, (struct sockaddr *)&srv, sizeof(srv)) < 0) {
|
||||
fl_alert(lang.compare(0, 2, "en") == 0 ?
|
||||
"Failed to reconnect" : "再接続に失敗");
|
||||
close(sock);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
close(sock);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Vulnpass::showRes() {
|
||||
std::string lang = Common::getlang();
|
||||
|
||||
std::string res;
|
||||
if (lang.compare(0, 2, "en") == 0) {
|
||||
res = "Breached passwords:\n";
|
||||
} else {
|
||||
res = "漏洩したパスワード:\n";
|
||||
}
|
||||
|
||||
for (const auto &path : vulnpaths) {
|
||||
res += path + "\n";
|
||||
}
|
||||
|
||||
if (lang.compare(0, 2, "en") == 0) {
|
||||
res += "Total: " + std::to_string(vulnpaths.size()) + "\n";
|
||||
res += "It's advised to change any of the";
|
||||
res += "breached passwords as soon as possible!";
|
||||
} else {
|
||||
res += "合計: " + std::to_string(vulnpaths.size()) + "\n";
|
||||
res += "漏洩したパスワードは出来るだけ早く変更する事をお勧めします!";
|
||||
}
|
||||
|
||||
Fl_Window *win = new Fl_Window(500, 440, lang.compare(0, 2, "en") == 0 ?
|
||||
"Results" : "結果");
|
||||
Fl_Text_Display *display = new Fl_Text_Display(10, 10, 480, 380);
|
||||
Fl_Text_Buffer *textbuf = new Fl_Text_Buffer();
|
||||
|
||||
textbuf->text(res.c_str());
|
||||
display->buffer(textbuf);
|
||||
display->scrollbar_width(15);
|
||||
win->resizable(display);
|
||||
|
||||
Fl_Button *okBtn = new Fl_Button(210, 400, 80, 30, "OK");
|
||||
okBtn->callback([](Fl_Widget *widget, void *win) {
|
||||
(void)widget;
|
||||
reinterpret_cast<Fl_Window*>(win)->hide();
|
||||
}, win);
|
||||
|
||||
win->add(okBtn);
|
||||
|
||||
win->end();
|
||||
win->show();
|
||||
}
|
||||
|
||||
void Vulnpass::dialog_cb(Fl_Widget *w, void *) {
|
||||
(void)w;
|
||||
|
||||
std::string lang = Common::getlang();
|
||||
|
||||
Fl_Window *dialog = new Fl_Window(400, 50,
|
||||
(lang.compare(0, 2, "en") == 0 ?
|
||||
"Check for branched password" : "漏洩されたパスワードの確認"));
|
||||
|
||||
Fl_Box *box = new Fl_Box(10, 10, 380, 20,
|
||||
(lang.compare(0, 2, "en") == 0 ?
|
||||
"Checking, please wait for a while..." :
|
||||
"確認中。暫くお待ち下さい・・・"));
|
||||
|
||||
dialog->add(box);
|
||||
|
||||
dialog->end();
|
||||
dialog->set_modal();
|
||||
dialog->show();
|
||||
|
||||
std::thread checker([dialog]() {
|
||||
vuln.exec();
|
||||
|
||||
Fl::lock();
|
||||
dialog->hide();
|
||||
vuln.showRes();
|
||||
Fl::unlock();
|
||||
});
|
||||
|
||||
checker.detach();
|
||||
}
|
||||
18
src/vulnpass.hh
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef VULNPASS_HH
|
||||
#define VULNPASS_HH
|
||||
|
||||
#include <FL/Fl_Button.H>
|
||||
|
||||
#include "dialog.hh"
|
||||
|
||||
class Vulnpass : public Dialog {
|
||||
public:
|
||||
Fl_Button *btn = nullptr;
|
||||
|
||||
static void dialog_cb(Fl_Widget *w, void *);
|
||||
void vuln_cb(Fl_Widget *, void *);
|
||||
bool exec();
|
||||
void showRes();
|
||||
};
|
||||
|
||||
#endif
|
||||