SVNからのミラー
This commit is contained in:
61
CHANGELOG.md
Normal file
61
CHANGELOG.md
Normal file
@@ -0,0 +1,61 @@
|
||||
# 1.5.1 (2025年03月13日)
|
||||
* OTPで、秘密鍵を自動的に大文字にする様に
|
||||
|
||||
# 1.5.0 (2024年12月17日)
|
||||
* パスワード表示で、「OpenPGP」かどうかの確認の追加
|
||||
* 侵害されたパスワードの確認の追加
|
||||
* 複数サイトで同じパスワードを利用かどうか、パスワードの長さ、又はパスワードの強さの確認
|
||||
* パスワードコピーの期間を設定出来る様に
|
||||
* ワンタイムパスワード(OTP)を表示せずにコピー機能性の追加
|
||||
* Wayland対応の追加
|
||||
* コピータイムアウトの間にCTRL+Cを押したら、クリップボードから取り消す様に
|
||||
* コンフィグファイルの追加
|
||||
|
||||
# 1.4.0 (2024年09月24日)
|
||||
* Haiku対応
|
||||
* macOS対応
|
||||
* パスワードの検索の追加
|
||||
* 静的リンクはデフォルトに
|
||||
* simpas情報の表示
|
||||
* ベースディレクトリを関数化
|
||||
* パスワードの編集する前に、バックアップを作成する様に
|
||||
|
||||
# 1.3.0 (2024年06月17日)
|
||||
* 英訳の追加 (レミリア・スカーレットさん)
|
||||
* GNU Make → BSD Make
|
||||
* GPLv2 → ISC
|
||||
* OpenBSD向けのリリースコマンドの追加
|
||||
* FreeBSD向けのリリースコマンドの追加
|
||||
* Linux向けのリリースコマンドの追加
|
||||
* 最新ルールに従い
|
||||
* NetBSD向けのリリースコマンドの追加
|
||||
* OpenBSD 7.5でTOTPの修正 (ヌル終端文字列のバグ)
|
||||
* ヘルプの表示の削除 (manpageをご利用下さい)
|
||||
* パスワード作成関数のデフォルトな長さは64に
|
||||
* manpageを細かくに
|
||||
* パスワードがなくなるまで削除したら、ディレクトリも削除する様に
|
||||
|
||||
# 1.2.0 (2024年02月01日)
|
||||
* やっとTOTP機能性を修正した
|
||||
* makeを実行したら、バイナリがもっと小さくなる
|
||||
* パスワードの長さの延長
|
||||
* パスワード追加機能性で、パスワードが既に存在するかどうか確認
|
||||
* パスワード削除機能性で、パスワードが存在ないかどうか確認
|
||||
* パスワード変更機能性の追加
|
||||
* zsh対応の修正
|
||||
|
||||
# 1.1.2 (2023年12月01日)
|
||||
* OpenBSDでのコンパイラーが発生された問題を修正した
|
||||
|
||||
# 1.1.1 (2023年12月01日)
|
||||
* make install-zsh部分を修正
|
||||
|
||||
# 1.1.0 (2023年12月01日)
|
||||
* TOTP対応
|
||||
* READMEファイルで使い方を詳しく説明する
|
||||
* zshキャプチャー
|
||||
* パスワード追加関数を安定化
|
||||
* パスワード表示とパスワードのコピー関数で、GNU Passで保存したパスワードの場合は改行を追加しない様に
|
||||
|
||||
# 1.0.0 (2023年11月30日)
|
||||
* 最初リリース
|
||||
13
LICENSE.txt
Normal file
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.
|
||||
121
Makefile
Normal file
121
Makefile
Normal file
@@ -0,0 +1,121 @@
|
||||
UNAME_S != uname -s
|
||||
UNAME_M != uname -m
|
||||
OS = ${UNAME_S}
|
||||
|
||||
.if ${UNAME_S} == "OpenBSD"
|
||||
OS = openbsd
|
||||
.elif ${UNAME_S} == "NetBSD"
|
||||
OS = netbsd
|
||||
.elif ${UNAME_S} == "FreeBSD"
|
||||
OS = freebsd
|
||||
.elif ${UNAME_S} == "Linux"
|
||||
OS = linux
|
||||
.endif
|
||||
|
||||
.if ${UNAME_M} == "x86_64"
|
||||
UNAME_M = amd64
|
||||
.endif
|
||||
|
||||
NAME != cat main.c | grep "const char \*sofname" | awk '{print $$5}' | \
|
||||
sed "s/\"//g" | sed "s/;//"
|
||||
VERSION != cat main.c | grep "const char \*version" | awk '{print $$5}' | \
|
||||
sed "s/\"//g" | sed "s/;//"
|
||||
PREFIX = /usr/local
|
||||
.if ${UNAME_S} == "Haiku"
|
||||
PREFIX = /boot/home/config/non-packaged
|
||||
.elif ${UNAME_S} == "Linux"
|
||||
PREFIX = /usr
|
||||
.endif
|
||||
|
||||
MANPREFIX = ${PREFIX}/share/man
|
||||
.if ${UNAME_S} == "OpenBSD"
|
||||
MANPREFIX = ${PREFIX}/man
|
||||
.elif ${UNAME_S} == "Haiku"
|
||||
MANPREFIX = ${PREFIX}/documentation/man
|
||||
.endif
|
||||
|
||||
DATAPREFIX = ${PREFIX}/share
|
||||
.if ${UNAME_S} == "Haiku"
|
||||
DATAPREFIX = ${PREFIX}/data
|
||||
.endif
|
||||
|
||||
CC = cc
|
||||
FILES = main.c src/*.c
|
||||
|
||||
CFLAGS = -Wall -Wextra -I/usr/include -L/usr/lib
|
||||
.if ${UNAME_S} == "NetBSD"
|
||||
CFLAGS += -I/usr/pkg/include -L/usr/pkg/lib -I/usr/local/include -L/usr/local/lib
|
||||
.elif ${UNAME_S} == "OpenBSD" || ${UNAME_S} == "FreeBSD"
|
||||
CFLAGS += -I/usr/local/include -L/usr/local/lib
|
||||
.endif
|
||||
|
||||
LDFLAGS = -lgpgme -lcrypto
|
||||
|
||||
.if ${UNAME_S} == "OpenBSD"
|
||||
LDFLAGS += -lc -lassuan -lgpg-error -lintl -liconv
|
||||
.elif ${UNAME_S} == "FreeBSD"
|
||||
LDFLAGS += -lc -lassuan -lgpg-error -lthr -lintl
|
||||
.elif ${UNAME_S} == "NetBSD"
|
||||
LDFLAGS += -lcrypt -lc -lassuan -lgpg-error -lintl
|
||||
.elif ${UNAME_S} == "Linux"
|
||||
LDFLAGS += -lc -lassuan -lgpg-error
|
||||
.endif
|
||||
|
||||
all:
|
||||
${CC} -O3 ${CFLAGS} -o ${NAME} ${FILES} -static ${LDFLAGS}
|
||||
strip ${NAME}
|
||||
|
||||
debug:
|
||||
${CC} -g ${CFLAGS} -o ${NAME} ${FILES} ${LDFLAGS}
|
||||
|
||||
clean:
|
||||
rm -f ${NAME}
|
||||
|
||||
dist: clean
|
||||
mkdir -p release/src ${NAME}-${VERSION}
|
||||
cp -R LICENSE.txt Makefile README.md CHANGELOG.md \
|
||||
${NAME}-completion.zsh man main.c src ${NAME}-${VERSION}
|
||||
tar zcfv release/src/${NAME}-${VERSION}.tar.gz ${NAME}-${VERSION}
|
||||
rm -rf ${NAME}-${VERSION}
|
||||
|
||||
man:
|
||||
mkdir -p release/man/${VERSION}
|
||||
sed "s/VERSION/${VERSION}/g" < man/${NAME}-en.1 > \
|
||||
release/man/${VERSION}/${NAME}-en.1
|
||||
sed "s/VERSION/${VERSION}/g" < man/${NAME}-jp.1 > \
|
||||
release/man/${VERSION}/${NAME}-jp.1
|
||||
|
||||
release:
|
||||
mkdir -p release/bin/${VERSION}/${OS}/${UNAME_M}
|
||||
${CC} -O3 ${CFLAGS} -o release/bin/${VERSION}/${OS}/${UNAME_M}/${NAME} ${FILES} \
|
||||
-static ${LDFLAGS}
|
||||
strip release/bin/${VERSION}/${OS}/${UNAME_M}/${NAME}
|
||||
|
||||
install:
|
||||
mkdir -p ${DESTDIR}${PREFIX}/bin ${DESTDIR}${MANPREFIX}/man1
|
||||
cp -f ${NAME} ${DESTDIR}${PREFIX}/bin
|
||||
chmod 755 ${DESTDIR}${PREFIX}/bin/${NAME}
|
||||
mkdir -p ${DESTDIR}${MANPREFIX}/man1
|
||||
sed "s/VERSION/${VERSION}/g" < man/${NAME}-en.1 > \
|
||||
${DESTDIR}${MANPREFIX}/man1/${NAME}-en.1
|
||||
sed "s/VERSION/${VERSION}/g" < man/${NAME}-jp.1 > \
|
||||
${DESTDIR}${MANPREFIX}/man1/${NAME}-jp.1
|
||||
chmod 644 ${DESTDIR}${MANPREFIX}/man1/${NAME}-en.1
|
||||
chmod 644 ${DESTDIR}${MANPREFIX}/man1/${NAME}-jp.1
|
||||
|
||||
install-zsh:
|
||||
mkdir -p ${DESTDIR}${DATAPREFIX}/zsh/site-functions
|
||||
cp ${NAME}-completion.zsh ${DESTDIR}${DATAPREFIX}/zsh/site-functions/_${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/man/* 192.168.0.143:/zroot/repo/man/${NAME}
|
||||
|
||||
uninstall:
|
||||
rm -rf ${DESTDIR}${PREFIX}/bin/${NAME}
|
||||
rm -rf ${DESTDIR}${MANPREFIX}/man1/${NAME}-en.1
|
||||
rm -rf ${DESTDIR}${MANPREFIX}/man1/${NAME}-jp.1
|
||||
rm -rf ${DESTDIR}${DATAPREFIX}/zsh/site-functions/_${NAME}
|
||||
|
||||
.PHONY: all clean dist man release install install-zsh uninstall
|
||||
151
README.md
Normal file
151
README.md
Normal file
@@ -0,0 +1,151 @@
|
||||
# sp
|
||||
シンプルなパスワードマネージャー
|
||||
Simple Password Manager
|
||||
|
||||
## インストールする方法 | Installation
|
||||
### CRUX
|
||||
```sh
|
||||
doas prt-get depinst gpgme gnupg pinentry bmake
|
||||
bmake
|
||||
doas bmake install PREFIX=/usr
|
||||
doas bmake install-zsh PREFIX=/usr
|
||||
```
|
||||
|
||||
### Artix
|
||||
```sh
|
||||
doas pacman -S base-devel gpgme gnupg pinentry bmake
|
||||
bmake
|
||||
doas bmake install PREFIX=/usr
|
||||
doas bmake install-zsh PREFIX=/usr
|
||||
```
|
||||
|
||||
### OpenBSD
|
||||
```sh
|
||||
doas pkg_add gpgme gnupg pinentry
|
||||
make
|
||||
doas make install
|
||||
doas make install-zsh
|
||||
```
|
||||
|
||||
### NetBSD
|
||||
```sh
|
||||
doas pkgin install gpgme gnupg pinentry
|
||||
make
|
||||
doas make install
|
||||
doas make install-zsh
|
||||
```
|
||||
|
||||
### FreeBSD
|
||||
```sh
|
||||
doas pkg install gpgme gnupg pinentry
|
||||
make
|
||||
doas make install
|
||||
doas make install-zsh
|
||||
```
|
||||
|
||||
### Haiku
|
||||
```sh
|
||||
pkgman install bmake libassuan libassuan_devel gpgme gpgme_devel gnupg llvm18_clang
|
||||
bmake
|
||||
bmake install
|
||||
bmake install-zsh
|
||||
```
|
||||
|
||||
### macOS
|
||||
```sh
|
||||
brew install bmake libassuan gpgme gnupg pinentry pinentry-mac
|
||||
bmake
|
||||
doas bmake install
|
||||
doas bmake install-zsh
|
||||
```
|
||||
|
||||
## 初期設定 | Initial setup
|
||||
「gpg -k」でGPG鍵IDを確認して、「sp -i [GPG ID]」を実行して下さい。
|
||||
Confirm your GPG key ID with "gpg -k", and run "sp -i [GPG ID]".
|
||||
|
||||
## 使い方 | Usage
|
||||
### パスワードの作成 | Password generation
|
||||
#### 強いパスワードの場合 | Strong password
|
||||
```sh
|
||||
$ sp -g 64
|
||||
nSYGSF2lWGCUsqKCRB_~mZm+spaU<zvtt%um'01$tj4h,^nB6JqX#Cm$!U+s;c7:
|
||||
```
|
||||
又は
|
||||
Or
|
||||
```sh
|
||||
$ sp -g 64 secure
|
||||
nSYGSF2lWGCUsqKCRB_~mZm+spaU<zvtt%um'01$tj4h,^nB6JqX#Cm$!U+s;c7:
|
||||
```
|
||||
|
||||
#### 弱いパスワードの場合 | Weak password
|
||||
```sh
|
||||
$ sp -g 12 risk
|
||||
2aKBwb858mzg
|
||||
```
|
||||
|
||||
### パスワードの追加 | Add password
|
||||
「suwako」というユーザー名の場合:
|
||||
If the username is "suwake":
|
||||
```sh
|
||||
$ sp -a 076.moe/suwako
|
||||
パスワード:
|
||||
パスワード(確認用):
|
||||
パスワードを保存出来ました。
|
||||
```
|
||||
入力中は何も表示されないので、ご注意下さい。
|
||||
|
||||
### 英語で表示する場合 | For English
|
||||
```sh
|
||||
$ SP_LANG=en sp -a 076.moe/suwako
|
||||
Password:
|
||||
Password (for verification):
|
||||
The password got saved.
|
||||
```
|
||||
|
||||
### パスワードの表示 | Show password
|
||||
```sh
|
||||
$ sp -s 076.moe/suwako
|
||||
nSYGSF2lWGCUsqKCRB_~mZm+spaU<zvtt%um'01$tj4h,^nB6JqX#Cm$!U+s;c7:
|
||||
```
|
||||
表示せずコピーする場合は、「sp -y 076.moe/suwako」を使用して下さい。
|
||||
If you want to copy the password to the clipboard without showing it,
|
||||
use "sp -y 076.moe/suwako" instead.
|
||||
|
||||
### パスワードの削除 | Delete password
|
||||
```sh
|
||||
$ sp -d 076.moe/suwako
|
||||
パスワード「076.moe/suwako」を本当に削除する事が宜しいでしょうか? (y/N): y
|
||||
パスワードを削除しました。
|
||||
```
|
||||
|
||||
### パスワードの編集 | Edit password
|
||||
```sh
|
||||
$ sp -e 076.moe/suwako
|
||||
パスワード:
|
||||
パスワード(確認用):
|
||||
パスワードを保存出来ました。
|
||||
```
|
||||
|
||||
## TOTP(ワンタイムパスワード) | TOTP (one time password)
|
||||
### QRコードから | From QR code
|
||||
QRコードをダウンロードし、zbarimgを使用して「QR-Code:」以降の部分をコピーし、
|
||||
spに追加して下さい。\
|
||||
`sp -a`を実行すると、「パスワード」を聞かれますが、
|
||||
TOTPの場合は「otpauth://」から始まる文字列をコピペして下さい。
|
||||
|
||||
Download the QR code image, copy everything after "QR-Code:"
|
||||
you get after using zbarimg, and add into sp.
|
||||
If you run "sp -a", it'll ask for a "password", but in the case of TOTP,
|
||||
paste everything starting from "otpauth://".
|
||||
```sh
|
||||
$ zbarimg -q gitler.png
|
||||
QR-Code:otpauth://totp/Gitler%20%28gitler.moe%29:suwako?algorithm=SHA1&digits=6&issuer=Gitler%20%28gitler.moe%29&period=30&secret=〇〇
|
||||
|
||||
$ sp -a gitler-otp
|
||||
パスワード:
|
||||
パスワード(確認用):
|
||||
パスワードを保存出来ました。
|
||||
|
||||
$ sp -o gitler-otp
|
||||
123456
|
||||
```
|
||||
170
main.c
Normal file
170
main.c
Normal file
@@ -0,0 +1,170 @@
|
||||
#include "src/common.h"
|
||||
#include "src/initpass.h"
|
||||
#include "src/showpass.h"
|
||||
#include "src/yankpass.h"
|
||||
#include "src/listpass.h"
|
||||
#include "src/addpass.h"
|
||||
#include "src/delpass.h"
|
||||
#include "src/genpass.h"
|
||||
#include "src/otppass.h"
|
||||
#include "src/findpass.h"
|
||||
#include "src/vulnpass.h"
|
||||
#include "src/chkpass.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
const char *sofname = "sp";
|
||||
const char *version = "1.5.1";
|
||||
const char *avalopt = "abcdefgiloOsvy";
|
||||
const char *madefor = "simpas 1.1.1";
|
||||
|
||||
void usage() {
|
||||
printf("%s-%s (%s)\nusage: %s [-%s]\n",
|
||||
sofname, version, madefor, sofname, avalopt);
|
||||
}
|
||||
|
||||
char *getfullpath(char *arg) {
|
||||
char *lang = getlang();
|
||||
|
||||
char *basedir = getbasedir(1);
|
||||
size_t fullPathLen;
|
||||
char *fullPath;
|
||||
fullPathLen = strlen(basedir) + (arg != NULL ? strlen(arg) + 5 : 0);
|
||||
|
||||
fullPath = (char *)malloc(fullPathLen);
|
||||
if (fullPath == NULL) {
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
perror("Failed to allocating memory");
|
||||
else perror("メモリの役割に失敗");
|
||||
free(basedir);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (arg != NULL) {
|
||||
snprintf(fullPath, fullPathLen, "%s%s.gpg", basedir, arg);
|
||||
} else {
|
||||
snprintf(fullPath, fullPathLen, "%s", basedir);
|
||||
}
|
||||
|
||||
return fullPath;
|
||||
}
|
||||
|
||||
void editpass(char *file) {
|
||||
char *lang = getlang();
|
||||
|
||||
#if defined(__HAIKU__)
|
||||
const char *tmpfile = "/boot/system/cache/sp-tmp.gpg";
|
||||
#else
|
||||
const char *tmpfile = "/tmp/sp-tmp.gpg";
|
||||
#endif
|
||||
tmpcopy(file, tmpfile);
|
||||
if (delpass(file, 1) != 0) {
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
perror("Editing failed: Failed to delete.");
|
||||
else perror("編集に失敗:削除に失敗");
|
||||
return;
|
||||
}
|
||||
if (addpass(file) != 0) {
|
||||
tmpcopy(tmpfile, file);
|
||||
unlink(tmpfile);
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
perror("Editing failed: Failed to add.");
|
||||
else perror("編集に失敗:追加に失敗");
|
||||
return;
|
||||
}
|
||||
|
||||
unlink(tmpfile);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc < 2) {
|
||||
usage();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(argv[1], "-g") == 0) {
|
||||
if (argc > 4) {
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (argc == 2) genpass(64, true);
|
||||
else if (argc == 3) genpass(atoi(argv[2]), true);
|
||||
else if (argc == 4 && strcmp(argv[3], "risk") == 0)
|
||||
genpass(atoi(argv[2]), false);
|
||||
else if (argc == 4 && strcmp(argv[3], "secure") == 0)
|
||||
genpass(atoi(argv[2]), true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (argc == 3) {
|
||||
if (strcmp(argv[1], "-i") == 0) initpass(argv[2]);
|
||||
else if (strcmp(argv[1], "-s") == 0) {
|
||||
const char *pass = showpass(argv[2]);
|
||||
if (pass == NULL) return -1;
|
||||
printf("%s\n", pass);
|
||||
}
|
||||
else if (strcmp(argv[1], "-y") == 0) yankpass(argv[2], 45);
|
||||
else if (strcmp(argv[1], "-a") == 0) addpass(argv[2]);
|
||||
else if (strcmp(argv[1], "-d") == 0) delpass(argv[2], 0);
|
||||
else if (strcmp(argv[1], "-e") == 0) {
|
||||
editpass(argv[2]);
|
||||
} else if (strcmp(argv[1], "-o") == 0) {
|
||||
char *fullPath = getfullpath(argv[2]);
|
||||
if (fullPath == NULL) return -1;
|
||||
otppass(fullPath, 0, 0);
|
||||
free(fullPath);
|
||||
} else if (strcmp(argv[1], "-O") == 0) {
|
||||
char *fullPath = getfullpath(argv[2]);
|
||||
if (fullPath == NULL) return -1;
|
||||
otppass(fullPath, 1, 30);
|
||||
free(fullPath);
|
||||
} else if (strcmp(argv[1], "-f") == 0) {
|
||||
char *fullPath = getfullpath(NULL);
|
||||
if (fullPath == NULL) return -1;
|
||||
findpass(fullPath, argv[2]);
|
||||
free(fullPath);
|
||||
} else if (strcmp(argv[1], "-c") == 0) {
|
||||
char *fullPath = getfullpath(NULL);
|
||||
if (fullPath == NULL) return -1;
|
||||
chkpass(fullPath, argv[2]);
|
||||
free(fullPath);
|
||||
} else {
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
} else if (argc == 4) {
|
||||
if (strcmp(argv[1], "-y") == 0) {
|
||||
int i;
|
||||
if (sscanf(argv[3], "%d", &i) == 0) yankpass(argv[2], 45);
|
||||
else yankpass(argv[2], atoi(argv[3]));
|
||||
} else if (strcmp(argv[1], "-O") == 0) {
|
||||
char *fullPath = getfullpath(argv[2]);
|
||||
if (fullPath == NULL) return -1;
|
||||
int i;
|
||||
if (sscanf(argv[3], "%d", &i) == 0) otppass(fullPath, 1, 30);
|
||||
else otppass(fullPath, 1, atoi(argv[3]));
|
||||
free(fullPath);
|
||||
}
|
||||
} else if (argc == 2) {
|
||||
char *basePath = getfullpath(NULL);
|
||||
if (basePath == NULL) return -1;
|
||||
|
||||
if (strcmp(argv[1], "-l") == 0) listpass(basePath, 0);
|
||||
else if (strcmp(argv[1], "-b") == 0) vulnpass(basePath);
|
||||
else if (strcmp(argv[1], "-v") == 0) {
|
||||
printf("%s-%s (%s)\n", sofname, version, madefor);
|
||||
} else {
|
||||
usage();
|
||||
free(basePath);
|
||||
return 1;
|
||||
}
|
||||
free(basePath);
|
||||
} else {
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
191
man/sp-en.1
Normal file
191
man/sp-en.1
Normal file
@@ -0,0 +1,191 @@
|
||||
.TH SP 1 VERSION
|
||||
.SH NAME
|
||||
sp - Simple Password Manager
|
||||
.br
|
||||
.B sp
|
||||
[-bfi] [-i \fI\,gpg-id\fR] [-adeos \fI\,password\fR] [-c \fI,all\fR|\fI,length\fR|\fI,strength\fR|\fI,duplicate\fR] [-g [\fI\,length\fR] [\fI\,secure\fR|\fI\,risk\fR]] [-Oy \fI\,password\fR [\fI,seconds\fR]]
|
||||
.SH DESCRIPTION
|
||||
.PP
|
||||
Simple Password Manager
|
||||
.TP
|
||||
\fB\,a\fR \fI\,password\fR
|
||||
Add password
|
||||
.TP
|
||||
\fB\,b\fR
|
||||
Check for breached passwords (internet connection is required)
|
||||
.TP
|
||||
\fB\,c\fR \fI,all|length|strength|duplicate\fR
|
||||
Check for password length, strength, or duplicates.
|
||||
.br
|
||||
length = Check for password length
|
||||
strength = Check for password strength
|
||||
duplicate = Check for duplicate passwords
|
||||
all = Check all of the above
|
||||
.TP
|
||||
\fB\,d\fR \fI\,password\fR
|
||||
Delete password
|
||||
.TP
|
||||
\fB\,e\fR \fI\,password\fR
|
||||
Edit password
|
||||
.TP
|
||||
\fB\,g\fR \fI,length\fR \fI,risk|secure\fR
|
||||
Generate a random password using the preferred amount of digits.
|
||||
.br
|
||||
The default is 64 characters.
|
||||
.br
|
||||
secure = Lower and uppercase + digits + special characters (default)
|
||||
.br
|
||||
risk = only lowercase and uppercase + digits
|
||||
.TP
|
||||
\fB\,i\fR \fI\,gpg-id\fR
|
||||
Initialize password storage using GPG
|
||||
.TP
|
||||
\fB\,l\fR
|
||||
Show a list of passwords
|
||||
.TP
|
||||
\fB\,o\fR \fI\,password\fR
|
||||
Show one time password (TOTP)
|
||||
.TP
|
||||
\fB\,O\fR \fI\,password\fR \fI\,seconds\fR
|
||||
Copy one time password (TOTP)
|
||||
.br
|
||||
The default is 30 seconds.
|
||||
.TP
|
||||
\fB\,s\fR \fI\,password\fR
|
||||
Show password
|
||||
.TP
|
||||
\fB\,v\fR
|
||||
Show version
|
||||
.TP
|
||||
\fB\,y\fR \fI\,password\fR \fI,seconds\fR
|
||||
Copy password to the clipboard without showing the password (Xorg-only)
|
||||
.br
|
||||
The default is 45 seconds.
|
||||
.SH LANGUAGE
|
||||
The default language is Japanese. In order to use English,
|
||||
.br
|
||||
please add "SP_LANG=en" to the `.zshrc` or `.bashrc` file.
|
||||
.SH EXAMPLES
|
||||
\&...
|
||||
|
||||
$ sp -i 12345678901234567890ABCDEFABCDEF1234ABCD
|
||||
.br
|
||||
初期設定に完了しました。
|
||||
|
||||
$ cat ~/.local/share/sp/.gpg-id
|
||||
.br
|
||||
12345678901234567890ABCDEFABCDEF1234ABCD
|
||||
.ED
|
||||
|
||||
\&...
|
||||
|
||||
$ sp -a 076.moe/nitori
|
||||
.br
|
||||
Password:
|
||||
.br
|
||||
Password (for verification):
|
||||
.br
|
||||
The password got saved.
|
||||
|
||||
$ sp -s 076.moe/nitori
|
||||
.br
|
||||
kyuuri
|
||||
|
||||
$ sp -d 076.moe/nitori
|
||||
.br
|
||||
Are you sure you want to delete the password '076.moe/nitori' (y/N): y
|
||||
.br
|
||||
The password got deleted
|
||||
.br
|
||||
|
||||
$ sp -s 076.moe/nitori
|
||||
.br
|
||||
Failed to open file: No such file or directory
|
||||
.ED
|
||||
|
||||
\&...
|
||||
|
||||
$ sp -a 076.moe/nitori
|
||||
.br
|
||||
Password:
|
||||
.br
|
||||
Password (for verification):
|
||||
.br
|
||||
The password got saved.
|
||||
|
||||
$ sp -s 076.moe/nitori
|
||||
.br
|
||||
kyuuri
|
||||
|
||||
$ sp -a 076.moe/nitori
|
||||
.br
|
||||
パスワードが既に存在しています。
|
||||
.br
|
||||
変更するには、「 sp -e 076.moe/nitori 」を実行して下さい。
|
||||
|
||||
$ SP_LANG=en sp -a 076.moe/nitori
|
||||
.br
|
||||
Password already exist.
|
||||
.br
|
||||
For edit, please run ' sp -e 076.moe/nitori '.
|
||||
|
||||
$ sp -e 076.moe/nitori
|
||||
.br
|
||||
Password:
|
||||
.br
|
||||
Password (for verification):
|
||||
.br
|
||||
The password got saved.
|
||||
|
||||
$ sp -s 076.moe/nitori
|
||||
.br
|
||||
kappa
|
||||
.ED
|
||||
|
||||
\&...
|
||||
|
||||
$ sp -g
|
||||
.br
|
||||
\5C'F6=8r&:OO=P?{Ry-3d4%z#7Hki}965l`j2xJSRoHQkvj^nz+YPx4g74yu_OT
|
||||
|
||||
$ sp -g 12
|
||||
.br
|
||||
{%#upiPiayqZ
|
||||
|
||||
$ sp -g 12 risk
|
||||
.br
|
||||
iwxoumJC9wZH
|
||||
.ED
|
||||
|
||||
\&...
|
||||
|
||||
$ doas pkg_add zbar
|
||||
.br
|
||||
quirks-7.14 signed on 2024-05-21T22:52:07Z
|
||||
.br
|
||||
zbar-0.23.90p2: ok
|
||||
|
||||
$ zbarimg -q --raw Untitled.png
|
||||
.br
|
||||
otpauth://totp/GitHub:TechnicalSuwako?secret=ABCDEFGHIJKLMNOP&issuer=GitHub
|
||||
|
||||
$ sp -a github.com/2fa
|
||||
.br
|
||||
Password:
|
||||
.br
|
||||
Password (for verification):
|
||||
.br
|
||||
The password got saved.
|
||||
|
||||
$ sp -s github.com/2fa
|
||||
.br
|
||||
otpauth://totp/GitHub:TechnicalSuwako?secret=ABCDEFGHIJKLMNOP&issuer=GitHub
|
||||
|
||||
$ sp -o github.com/2fa
|
||||
.br
|
||||
123456
|
||||
.Ed
|
||||
.SH AUTHORS
|
||||
.PP
|
||||
Technical Suwako (developer)
|
||||
Remilia Scarlet (English translation)
|
||||
191
man/sp-jp.1
Normal file
191
man/sp-jp.1
Normal file
@@ -0,0 +1,191 @@
|
||||
.TH SP 1 VERSION
|
||||
.SH NAME
|
||||
sp - Simple Password Manager
|
||||
.br
|
||||
.B sp
|
||||
[-bfi] [-i \fI\,gpg-id\fR] [-adeos \fI\,password\fR] [-c \fI,all\fR|\fI,length\fR|\fI,strength\fR|\fI,duplicate\fR] [-g [\fI\,length\fR] [\fI\,secure\fR|\fI\,risk\fR]] [-Oy \fI\,password\fR [\fI,seconds\fR]]
|
||||
.SH 説明
|
||||
.PP
|
||||
シンプルなパスワードマネージャー。
|
||||
.TP
|
||||
\fB\,a\fR \fI\,password\fR
|
||||
パスワードを追加
|
||||
.TP
|
||||
\fB\,b\fR
|
||||
漏洩されたパスワードを確認(インターネット接続が必須)
|
||||
.TP
|
||||
\fB\,c\fR \fI,all|length|strength|duplicate\fR
|
||||
パスワードの長さ、強さ、や服数度を確認
|
||||
.br
|
||||
length=パスワードの長さの確認
|
||||
strength=パスワードの強さの確認
|
||||
duplicate=服数パスワードが同じかどうか確認
|
||||
all=上記の全部の確認
|
||||
.TP
|
||||
\fB\,d\fR \fI\,password\fR
|
||||
パスワードを削除
|
||||
.TP
|
||||
\fB\,e\fR \fI\,password\fR
|
||||
パスワードを変更
|
||||
.TP
|
||||
\fB\,g\fR \fI,length\fR \fI,risk|secure\fR
|
||||
希望文字数でパスワードをランダムに作成する。
|
||||
.br
|
||||
デフォルトな長さは64文字です。
|
||||
.br
|
||||
secure=英数字+記号文字(デフォルト)を使用
|
||||
.br
|
||||
risk=英数字のみ(不安)を使用
|
||||
.TP
|
||||
\fB\,i\fR \fI\,gpg-id\fR
|
||||
GPGと使ってパスワードストレージを初期設定
|
||||
.TP
|
||||
\fB\,l\fR
|
||||
パスワード一覧を表示
|
||||
.TP
|
||||
\fB\,o\fR \fI\,password\fR
|
||||
ワンタイムパスワード(TOTP)を表示
|
||||
.TP
|
||||
\fB\,O\fR \fI\,password\fR \fI,seconds\fR
|
||||
ワンタイムパスワード(TOTP)をコピー
|
||||
.br
|
||||
デフォルトは30秒です。
|
||||
.TP
|
||||
\fB\,s\fR \fI\,password\fR
|
||||
パスワードを表示
|
||||
.TP
|
||||
\fB\,v\fR
|
||||
バージョンを表示
|
||||
.TP
|
||||
\fB\,y\fR \fI\,password\fR \fI,seconds\fR
|
||||
パスワードを表示せずクリップボードにコピーする(Xorgのみ)
|
||||
.br
|
||||
デフォルトは45秒です。
|
||||
.SH LANGUAGE
|
||||
デフォルトの言語は日本語ですが、英語で利用するには、
|
||||
.br
|
||||
「.zshrc」、「.bashrc」等ファイルで「SP_LANG=en」を追加して下さい。
|
||||
.SH EXAMPLES
|
||||
\&...
|
||||
|
||||
$ sp -i 12345678901234567890ABCDEFABCDEF1234ABCD
|
||||
.br
|
||||
初期設定に完了しました。
|
||||
|
||||
$ cat ~/.local/share/sp/.gpg-id
|
||||
.br
|
||||
12345678901234567890ABCDEFABCDEF1234ABCD
|
||||
.ED
|
||||
|
||||
\&...
|
||||
|
||||
$ sp -a 076.moe/nitori
|
||||
.br
|
||||
パスワード:
|
||||
.br
|
||||
パスワード(確認用):
|
||||
.br
|
||||
パスワードを保存出来ました
|
||||
|
||||
$ sp -s 076.moe/nitori
|
||||
.br
|
||||
kyuuri
|
||||
|
||||
$ sp -d 076.moe/nitori
|
||||
.br
|
||||
パスワード「076.moe/nitori」を本当に削除する事が宜しいでしょうか? (y/N): y
|
||||
.br
|
||||
パスワードを削除しました
|
||||
.br
|
||||
|
||||
$ sp -s 076.moe/nitori
|
||||
.br
|
||||
ファイルを開くに失敗: No such file or directory
|
||||
.ED
|
||||
|
||||
\&...
|
||||
|
||||
$ sp -a 076.moe/nitori
|
||||
.br
|
||||
パスワード:
|
||||
.br
|
||||
パスワード(確認用):
|
||||
.br
|
||||
パスワードを保存出来ました
|
||||
|
||||
$ sp -s 076.moe/nitori
|
||||
.br
|
||||
kyuuri
|
||||
|
||||
$ sp -a 076.moe/nitori
|
||||
.br
|
||||
パスワードが既に存在しています。
|
||||
.br
|
||||
変更するには、「 sp -e 076.moe/nitori 」を実行して下さい。
|
||||
|
||||
$ SP_LANG=en sp -a 076.moe/nitori
|
||||
.br
|
||||
Password already exist.
|
||||
.br
|
||||
For edit, please run ' sp -e 076.moe/nitori '.
|
||||
|
||||
$ sp -e 076.moe/nitori
|
||||
.br
|
||||
パスワード:
|
||||
.br
|
||||
パスワード(確認用):
|
||||
.br
|
||||
パスワードを保存出来ました
|
||||
|
||||
$ sp -s 076.moe/nitori
|
||||
.br
|
||||
kappa
|
||||
.ED
|
||||
|
||||
\&...
|
||||
|
||||
$ sp -g
|
||||
.br
|
||||
\5C'F6=8r&:OO=P?{Ry-3d4%z#7Hki}965l`j2xJSRoHQkvj^nz+YPx4g74yu_OT
|
||||
|
||||
$ sp -g 12
|
||||
.br
|
||||
{%#upiPiayqZ
|
||||
|
||||
$ sp -g 12 risk
|
||||
.br
|
||||
iwxoumJC9wZH
|
||||
.ED
|
||||
|
||||
\&...
|
||||
|
||||
$ doas pkg_add zbar
|
||||
.br
|
||||
quirks-7.14 signed on 2024-05-21T22:52:07Z
|
||||
.br
|
||||
zbar-0.23.90p2: ok
|
||||
|
||||
$ zbarimg -q --raw Untitled.png
|
||||
.br
|
||||
otpauth://totp/GitHub:TechnicalSuwako?secret=ABCDEFGHIJKLMNOP&issuer=GitHub
|
||||
|
||||
$ sp -a github.com/2fa
|
||||
.br
|
||||
パスワード:
|
||||
.br
|
||||
パスワード(確認用):
|
||||
.br
|
||||
パスワードを保存出来ました
|
||||
|
||||
$ sp -s github.com/2fa
|
||||
.br
|
||||
otpauth://totp/GitHub:TechnicalSuwako?secret=ABCDEFGHIJKLMNOP&issuer=GitHub
|
||||
|
||||
$ sp -o github.com/2fa
|
||||
.br
|
||||
123456
|
||||
.Ed
|
||||
.SH AUTHORS
|
||||
.PP
|
||||
テクニカル諏訪子(開発者)
|
||||
レミリア・スカーレット(英訳)
|
||||
70
sp-completion.zsh
Normal file
70
sp-completion.zsh
Normal file
@@ -0,0 +1,70 @@
|
||||
#compdef sp
|
||||
#autoload
|
||||
|
||||
_sp () {
|
||||
local cmd
|
||||
if (( CURRENT > 2)); then
|
||||
cmd=${words[2]}
|
||||
curcontext="${curcontext%:*:*}:pass-$cmd"
|
||||
(( CURRENT-- ))
|
||||
shift words
|
||||
case "${cmd}" in
|
||||
-i)
|
||||
_sp_complete_keys
|
||||
;;
|
||||
-i|-y|-d|-a|-e|-o|-O)
|
||||
_sp_complete_entries_helper
|
||||
;;
|
||||
-g)
|
||||
local -a subcommands
|
||||
subcommands=(
|
||||
"secure:英数字+記号文字(デフォルト)"
|
||||
"risk:英数字のみ(不安)"
|
||||
)
|
||||
;;
|
||||
-s)
|
||||
_sp_cmd_show
|
||||
;;
|
||||
esac
|
||||
else
|
||||
local -a subcommands
|
||||
subcommands=(
|
||||
"-a:パスワードを追加"
|
||||
"-b:漏洩されたパスワードを確認(インターネット接続が必須)"
|
||||
"-c:パスワードの長さ、強さ、や服数度を確認する。length=パスワードの長さの確認、strength=パスワードの強さの確認、duplicate=服数パスワードが同じかどうか確認、all=上記の全部の確認"
|
||||
"-d:パスワードを削除"
|
||||
"-e:パスワードを変更"
|
||||
"-g:希望文字数でパスワードをランダムに作成する。デフォルトな長さは64文字です。secure=英数字+記号文字(デフォルト)を使用、risk=英数字のみ(不安)を使用"
|
||||
"-i:GPGと使ってパスワードストレージを初期設定"
|
||||
"-l:パスワード一覧を表示"
|
||||
"-o:ワンタイムパスワード(TOTP)を表示"
|
||||
"-O:ワンタイムパスワード(TOTP)をコピー。デフォルトは30秒です。"
|
||||
"-s:パスワードを表示"
|
||||
"-v:バージョンを表示"
|
||||
"-y:パスワードを表示せずクリップボードにコピーする(Xorgのみ)。デフォルトは45秒です。"
|
||||
)
|
||||
_sp_cmd_show
|
||||
fi
|
||||
}
|
||||
|
||||
_sp_cmd_show () {
|
||||
_sp_complete_entries
|
||||
}
|
||||
|
||||
_sp_complete_entries() {
|
||||
_sp_complete_entries_helper -type f
|
||||
}
|
||||
|
||||
_sp_complete_entries_helper () {
|
||||
local IFS=$'\n'
|
||||
local prefix
|
||||
zstyle -s ":completion:${curcontext}:" prefix prefix || prefix="${SP_DIR:-$HOME/.local/share/sp}"
|
||||
_values -C 'passwords' ${$(find -L "$prefix" \( -name .git -o -name .gpg-id \) -prune -o $@ -print 2>/dev/null | sed -e "s#${prefix}/\{0,1\}##" -e 's#\.gpg##' -e 's#\\#\\\\#g' -e 's#:#\\:#g' | sort):-""}
|
||||
}
|
||||
|
||||
_sp_complete_keys () {
|
||||
local IFS=$'\n'
|
||||
_values 'gpg keys' $(gpg2 --list-secret-keys --with-colons | cut -d : -f 10 | sort -u | sed '/^$/d')
|
||||
}
|
||||
|
||||
_sp
|
||||
288
src/addpass.c
Normal file
288
src/addpass.c
Normal file
@@ -0,0 +1,288 @@
|
||||
#include "addpass.h"
|
||||
|
||||
void cleanup(gpgme_ctx_t ctx, gpgme_key_t key, gpgme_data_t in, gpgme_data_t out) {
|
||||
gpgme_data_release(in);
|
||||
gpgme_data_release(out);
|
||||
gpgme_release(ctx);
|
||||
gpgme_key_release(key);
|
||||
}
|
||||
|
||||
void getpasswd(char *prompt, char *pw, size_t pwlen) {
|
||||
struct termios old, new;
|
||||
printf("%s", prompt);
|
||||
|
||||
// 端末設定を受け取る
|
||||
tcgetattr(fileno(stdin), &old);
|
||||
new = old;
|
||||
|
||||
// echoを無効にして
|
||||
new.c_lflag &= ~ECHO;
|
||||
tcsetattr(fileno(stdin), TCSANOW, &new);
|
||||
|
||||
// パスワードを読み込む
|
||||
if (fgets(pw, pwlen, stdin) != NULL) {
|
||||
// あれば、改行を取り消す
|
||||
size_t len = strlen(pw);
|
||||
if (len > 0 && pw[len - 1] == '\n') {
|
||||
pw[len - 1] = '\0';
|
||||
} else {
|
||||
// 掃除
|
||||
int c;
|
||||
while ((c = getchar()) != '\n' && c != EOF);
|
||||
}
|
||||
}
|
||||
|
||||
// 端末設定をもとに戻す
|
||||
tcsetattr(fileno(stdin), TCSANOW, &old);
|
||||
}
|
||||
|
||||
int addpass(char *file) {
|
||||
char *lang = getlang();
|
||||
|
||||
// パスを準備
|
||||
char *basedir = getbasedir(1);
|
||||
char *ext = ".gpg";
|
||||
|
||||
char pass[256];
|
||||
char knin[256];
|
||||
|
||||
int alllen = snprintf(NULL, 0, "%s%s%s", basedir, file, ext) + 1;
|
||||
char *gpgpathchk = malloc(alllen);
|
||||
if (gpgpathchk == NULL) {
|
||||
const char *ero = (strncmp(lang, "en", 2) == 0 ?
|
||||
"Failed to allocate memory" : "メモリを割当に失敗");
|
||||
fprintf(stderr, "%s\n", ero);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// ファイルが既に存在するかどうか確認
|
||||
snprintf(gpgpathchk, alllen, "%s%s%s", basedir, file, ext);
|
||||
|
||||
if (access(gpgpathchk, F_OK) != -1) {
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
fprintf(
|
||||
stderr,
|
||||
"Password already exist.\nTo edit, please run ' sp -e %s '.\n",
|
||||
file
|
||||
);
|
||||
else
|
||||
fprintf(
|
||||
stderr,
|
||||
"%s\n変更するには、「 sp -e %s 」を実行して下さい。\n",
|
||||
"パスワードが既に存在しています。",
|
||||
file
|
||||
);
|
||||
free(gpgpathchk);
|
||||
return -1;
|
||||
}
|
||||
free(gpgpathchk);
|
||||
|
||||
// パスワードを受け取る
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
getpasswd("Password: ", pass, sizeof(pass));
|
||||
else getpasswd("パスワード: ", pass, sizeof(pass));
|
||||
puts("");
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
getpasswd("Password (for verification): ", knin, sizeof(knin));
|
||||
else getpasswd("パスワード(確認用): ", knin, sizeof(knin));
|
||||
puts("");
|
||||
|
||||
// パスワードが一致するかどうか確認
|
||||
if (strcmp(pass, knin) != 0) {
|
||||
const char *ero = (strncmp(lang, "en", 2) == 0 ?
|
||||
"Password does not match. Terminating..." :
|
||||
"パスワードが一致していません。終了…");
|
||||
fprintf(stderr, "%s\n", ero);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// パスワードを保存する
|
||||
gpgme_ctx_t ctx;
|
||||
gpgme_error_t err;
|
||||
gpgme_key_t key[2] = {NULL, NULL};
|
||||
gpgme_data_t in, out;
|
||||
FILE *gpgfile;
|
||||
|
||||
// GPGMEライブラリを設置
|
||||
setlocale(LC_ALL, "");
|
||||
gpgme_check_version(NULL);
|
||||
gpgme_set_locale(NULL, LC_CTYPE, setlocale(LC_CTYPE, NULL));
|
||||
|
||||
// GPGMEを創作
|
||||
err = gpgme_new(&ctx);
|
||||
if (err) {
|
||||
const char *ero = (strncmp(lang, "en", 2) == 0 ?
|
||||
"Failed to generate GPGME" : "GPGMEを創作に失敗");
|
||||
fprintf(stderr, "%s: %s\n", ero, gpgme_strerror(err));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// GPGMEは非対話的モードに設定
|
||||
err = gpgme_set_pinentry_mode(ctx, GPGME_PINENTRY_MODE_LOOPBACK);
|
||||
if (err) {
|
||||
const char *ero = (strncmp(lang, "en", 2) == 0 ?
|
||||
"Failed to set pinentry mode" : "pinentryモードを設定に失敗");
|
||||
fprintf(stderr, "%s: %s\n", ero, gpgme_strerror(err));
|
||||
gpgme_release(ctx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// パスワードからデータオブジェクトを創作
|
||||
err = gpgme_data_new_from_mem(&in, pass, strlen(pass), 0);
|
||||
if (err) {
|
||||
const char *ero = (strncmp(lang, "en", 2) == 0 ?
|
||||
"Failed to make data object" : "データオブジェクトを創作に失敗");
|
||||
fprintf(stderr, "%s: %s\n", ero, gpgme_strerror(err));
|
||||
gpgme_release(ctx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
gpgme_data_new(&out);
|
||||
|
||||
// 鍵を受け取る
|
||||
char keypath[256];
|
||||
snprintf(keypath, sizeof(keypath), "%s%s", basedir, ".gpg-id");
|
||||
|
||||
keypath[sizeof(keypath) - 1] = '\0';
|
||||
|
||||
FILE* keyfile = fopen(keypath, "rb");
|
||||
if (keyfile == NULL) {
|
||||
const char *ero = (strncmp(lang, "en", 2) == 0 ?
|
||||
"Failed to open .gpg-id file" : ".gpg-idファイルを開くに失敗");
|
||||
fprintf(stderr, "%s\n", ero);
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *keyid = malloc(256);
|
||||
if (!fgets(keyid, 256, keyfile)) {
|
||||
const char *ero = (strncmp(lang, "en", 2) == 0 ?
|
||||
"Failed to reading key ID" : "鍵IDを読込に失敗");
|
||||
fprintf(stderr, "%s\n", ero);
|
||||
fclose(keyfile);
|
||||
free(keyid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
keyid[strcspn(keyid, "\n")] = 0;
|
||||
fclose(keyfile);
|
||||
|
||||
err = gpgme_get_key(ctx, keyid, &key[0], 0);
|
||||
if (err) {
|
||||
const char *ero = (strncmp(lang, "en", 2) == 0 ?
|
||||
"Failed to get key" : "鍵Dを受取に失敗");
|
||||
fprintf(stderr, "%s: %s\n", ero, gpgme_strerror(err));
|
||||
free(keyid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (key[0] == NULL) {
|
||||
const char *ero = (strncmp(lang, "en", 2) == 0 ?
|
||||
"Error: Key is NULL" : "エラー:鍵はNULLです");
|
||||
fprintf(stderr, "%s\n", ero);
|
||||
free(keyid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
free(keyid);
|
||||
|
||||
// 暗号化
|
||||
err = gpgme_op_encrypt(ctx, &key[0], GPGME_ENCRYPT_ALWAYS_TRUST, in, out);
|
||||
if (err) {
|
||||
const char *ero = (strncmp(lang, "en", 2) == 0 ?
|
||||
"Failed to encrypt" : "暗号化に失敗");
|
||||
fprintf(stderr, "%s: %s\n", ero, gpgme_strerror(err));
|
||||
cleanup(ctx, key[0], in, out);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 暗号化したファイルを開く
|
||||
char *gpgpath = malloc(alllen);
|
||||
if (gpgpath == NULL) {
|
||||
const char *ero = (strncmp(lang, "en", 2) == 0 ?
|
||||
"Failed to allocate memory" : "メモリを割当に失敗");
|
||||
fprintf(stderr, "%s\n", ero);
|
||||
cleanup(ctx, key[0], in, out);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// ディレクトリを創作
|
||||
char dirpath[512];
|
||||
snprintf(dirpath, sizeof(dirpath), "%s%s", basedir, file);
|
||||
|
||||
dirpath[sizeof(dirpath) - 1] = '\0';
|
||||
|
||||
char *lastsla = strrchr(dirpath, '/');
|
||||
if (lastsla != NULL) {
|
||||
*lastsla = '\0';
|
||||
if (mkdir_r(dirpath, 0755) != 0) {
|
||||
const char *ero = (strncmp(lang, "en", 2) == 0 ?
|
||||
"Failed to create directory" : "ディレクトリを創作に失敗");
|
||||
fprintf(stderr, "%s\n", ero);
|
||||
free(gpgpath);
|
||||
cleanup(ctx, key[0], in, out);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(gpgpath, alllen, "%s%s%s", basedir, file, ext);
|
||||
|
||||
struct stat statbuf;
|
||||
if (stat(gpgpath, &statbuf) == 0) {
|
||||
const char *ero = (strncmp(lang, "en", 2) == 0 ?
|
||||
"Password already exists" : "パスワードは既に存在しています");
|
||||
fprintf(stderr, "%s\n", ero);
|
||||
free(gpgpath);
|
||||
cleanup(ctx, key[0], in, out);
|
||||
return -1;
|
||||
}
|
||||
|
||||
gpgfile = fopen(gpgpath, "wb");
|
||||
if (gpgfile == NULL) {
|
||||
const char *ero = (strncmp(lang, "en", 2) == 0 ?
|
||||
"Failed to open file." : "ファイルを開くに失敗。");
|
||||
fprintf(stderr, "%s\n", ero);
|
||||
free(gpgpath);
|
||||
cleanup(ctx, key[0], in, out);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// データが保存したかどうか確認
|
||||
ssize_t encrypted_data_size = gpgme_data_seek(out, 0, SEEK_END);
|
||||
if (encrypted_data_size <= 0) {
|
||||
const char *ero = (strncmp(lang, "en", 2) == 0 ?
|
||||
"Failed to store the data" : "データを保存に失敗");
|
||||
fprintf(stderr, "%s\n", ero);
|
||||
fclose(gpgfile);
|
||||
free(gpgpath);
|
||||
cleanup(ctx, key[0], in, out);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 復号化したパスワードを表示する
|
||||
gpgme_data_seek(out, 0, SEEK_SET);
|
||||
|
||||
char buffer[512];
|
||||
ssize_t read_bytes;
|
||||
|
||||
while ((read_bytes = gpgme_data_read(out, buffer, sizeof(buffer))) > 0) {
|
||||
if (fwrite(buffer, 1, (size_t)read_bytes, gpgfile) != (size_t)read_bytes) {
|
||||
const char *ero = (strncmp(lang, "en", 2) == 0 ?
|
||||
"Failed to write password" : "パスワードを書き込みに失敗");
|
||||
fprintf(stderr, "%s\n", ero);
|
||||
free(gpgpath);
|
||||
cleanup(ctx, key[0], in, out);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// 掃除
|
||||
fclose(gpgfile);
|
||||
free(gpgpath);
|
||||
cleanup(ctx, key[0], in, out);
|
||||
|
||||
const char *msg = (strncmp(lang, "en", 2) == 0 ?
|
||||
"The password got saved." : "パスワードを保存出来ました。");
|
||||
printf("%s\n", msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
13
src/addpass.h
Normal file
13
src/addpass.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifndef ADDPASS_H
|
||||
#define ADDPASS_H
|
||||
|
||||
#include <locale.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <termios.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
int addpass(char *file);
|
||||
|
||||
#endif
|
||||
65
src/base32.c
Normal file
65
src/base32.c
Normal file
@@ -0,0 +1,65 @@
|
||||
#include <ctype.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "base32.h"
|
||||
|
||||
int char_to_val(char c) {
|
||||
const char *base32_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
||||
char *ptr = strchr(base32_alphabet, c);
|
||||
return ptr ? ptr - base32_alphabet : -1;
|
||||
}
|
||||
|
||||
char *to_upper(const char *str) {
|
||||
if (str == NULL) return NULL;
|
||||
|
||||
size_t len = strlen(str);
|
||||
char *res = (char *)malloc(len + 1);
|
||||
|
||||
if (res == NULL) return NULL;
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
res[i] = toupper(str[i]);
|
||||
}
|
||||
|
||||
res[len] = '\0';
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
unsigned char *base32_decode(const char *encoded, size_t *out_len) {
|
||||
char *encoded_up = to_upper(encoded);
|
||||
size_t encoded_len = strlen(encoded_up);
|
||||
size_t padding = 0;
|
||||
for (int i = encoded_len - 1; i >= 0 && encoded_up[i] == '='; --i) {
|
||||
++padding;
|
||||
}
|
||||
|
||||
*out_len = (encoded_len - padding) * 5 / 8;
|
||||
if (*out_len == 0) return NULL;
|
||||
|
||||
unsigned char *decoded = malloc(*out_len);
|
||||
if (!decoded) return NULL;
|
||||
|
||||
int buffer = 0, bits_left = 0, count = 0;
|
||||
for (size_t i = 0; i < encoded_len - padding; ++i) {
|
||||
int val = char_to_val(encoded_up[i]);
|
||||
if (val < 0) {
|
||||
free(decoded);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buffer <<= 5;
|
||||
buffer |= val;
|
||||
bits_left += 5;
|
||||
|
||||
if (bits_left >= 8) {
|
||||
decoded[count++] = (unsigned char) (buffer >> (bits_left - 8));
|
||||
bits_left -= 8;
|
||||
}
|
||||
}
|
||||
|
||||
*out_len = count;
|
||||
return decoded;
|
||||
}
|
||||
8
src/base32.h
Normal file
8
src/base32.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef BASE32_H
|
||||
#define BASE32_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
unsigned char *base32_decode(const char *encoded, size_t *out_len);
|
||||
|
||||
#endif
|
||||
184
src/chkpass.c
Normal file
184
src/chkpass.c
Normal file
@@ -0,0 +1,184 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include "chkpass.h"
|
||||
#include "showpass.h"
|
||||
#include "common.h"
|
||||
|
||||
List chkFullpaths;
|
||||
List chkDispaths;
|
||||
List dupPasswd;
|
||||
List dupPath;
|
||||
|
||||
int vulncount = 0;
|
||||
int weaklencount = 0;
|
||||
int weakcharcount = 0;
|
||||
int duppasscount = 0;
|
||||
|
||||
size_t minimumlen = 12;
|
||||
size_t recommendlen = 64;
|
||||
|
||||
const char *spchar = "!@#$%^&*()-_=+[]{}|;:'\",.<>?/\\`~";
|
||||
|
||||
void chkLenPass(const char *path, const char *pass, char *lang) {
|
||||
if (strlen(pass) > 0 && strlen(pass) < minimumlen) {
|
||||
printf("【E】");
|
||||
if (strncmp(lang, "en", 2) == 0) {
|
||||
printf("The password \"%s\" is too short, minimum length should be "
|
||||
"%ld characters, recommended is %ld characters.\n",
|
||||
path, minimumlen, recommendlen);
|
||||
} else {
|
||||
printf("パスワード「%s」は短すぎます。最短パスワードの長さは%ld文字ですが、"
|
||||
"勧めが%ld文字です。\n",
|
||||
path, minimumlen, recommendlen);
|
||||
}
|
||||
|
||||
weaklencount++;
|
||||
vulncount++;
|
||||
} else if (strlen(pass) >= minimumlen && strlen(pass) < recommendlen) {
|
||||
printf("【W】");
|
||||
if (strncmp(lang, "en", 2) == 0) {
|
||||
printf("The password \"%s\" is long enough, but for optimal security, "
|
||||
"%ld characters is recommended.\n",
|
||||
path, recommendlen);
|
||||
} else {
|
||||
printf("パスワード「%s」の長さは十分ですが、最強のセキュリティには%ld文字が"
|
||||
"勧めします。\n",
|
||||
path, recommendlen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void chkCharPass(const char *path, const char *pass, char *lang) {
|
||||
int isUpper = 0;
|
||||
int isLower = 0;
|
||||
int isDigit = 0;
|
||||
int isSpecial = 0;
|
||||
|
||||
for (size_t j = 0; j < strlen(pass); j++) {
|
||||
if (isupper(pass[j])) isUpper = 1;
|
||||
else if (islower(pass[j])) isLower = 1;
|
||||
else if (isdigit(pass[j])) isDigit = 1;
|
||||
else if (strchr(spchar, pass[j])) isSpecial = 1;
|
||||
}
|
||||
|
||||
if (isUpper == 0 || isLower == 0 || isDigit == 0 || isSpecial == 0) {
|
||||
printf("【E】");
|
||||
if (strncmp(lang, "en", 2) == 0) {
|
||||
printf("The password \"%s\" is too weak! A strong password contains at least "
|
||||
"1 uppercase, 1 lowercase, 1 digit, and 1 special character.\n",
|
||||
path);
|
||||
} else {
|
||||
printf("パスワード「%s」は弱すぎます!強いパスワードは最大1大文字、1小文字、"
|
||||
"1数字、及び1記号の文字が含みます。\n",
|
||||
path);
|
||||
}
|
||||
|
||||
weakcharcount++;
|
||||
vulncount++;
|
||||
}
|
||||
}
|
||||
|
||||
void chkDupPass(const char *path, const char *pass, char *lang) {
|
||||
for (size_t k = 0; k < dupPasswd.size; k++) {
|
||||
if (strncmp(getElement(&dupPasswd, k), pass, strlen(pass)) == 0) {
|
||||
printf("【E】");
|
||||
if (strncmp(lang, "en", 2) == 0) {
|
||||
printf("The password \"%s\" is the same as \"%s\". "
|
||||
"For security, please keep passwords unique!\n",
|
||||
path, getElement(&dupPath, k));
|
||||
} else {
|
||||
printf("パスワード「%s」は「%s」と一致しています。"
|
||||
"セキュリティの為、各パスワードはユニークにする様にして下さい!\n",
|
||||
path, getElement(&dupPath, k));
|
||||
}
|
||||
|
||||
duppasscount++;
|
||||
vulncount++;
|
||||
}
|
||||
}
|
||||
|
||||
addElement(&dupPath, path);
|
||||
addElement(&dupPasswd, pass);
|
||||
}
|
||||
|
||||
void chkpass(const char *dpath, const char *mode) {
|
||||
char *lang = getlang();
|
||||
|
||||
if (strncmp(mode, "all", 3) != 0 && strncmp(mode, "length", 6) != 0 &&
|
||||
strncmp(mode, "strength", 8) != 0 && strncmp(mode, "duplicate", 9) != 0) {
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
printf("Invalid mode. Possible modes: all, length, strength, duplicate\n");
|
||||
else
|
||||
printf("不正なモード。可能なモード: all(全部)、length(長さ)、"
|
||||
"strength(強さ)、duplicate(同じパスワードの確認)\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// パスワードをスキャンして
|
||||
List fpaths;
|
||||
initList(&fpaths);
|
||||
initList(&chkFullpaths);
|
||||
initList(&chkDispaths);
|
||||
initList(&dupPasswd);
|
||||
initList(&dupPath);
|
||||
scanDir(dpath, dpath, &fpaths, &chkFullpaths, &chkDispaths);
|
||||
|
||||
puts(strncmp(lang, "en", 2) == 0 ?
|
||||
"Checking, please wait for a while..." : "確認中。暫くお待ち下さい・・・");
|
||||
|
||||
for (size_t i = 0; i < chkDispaths.size; i++) {
|
||||
const char *pass = showpass(getElement(&chkDispaths, i));
|
||||
if (!pass) continue;
|
||||
if (strncmp(pass, "otpauth://totp/", strlen(pass)) == 0) continue;
|
||||
|
||||
if (strncmp(mode, "all", 3) == 0) {
|
||||
// 全部を確認する
|
||||
chkLenPass(getElement(&chkDispaths, i), pass, lang);
|
||||
chkCharPass(getElement(&chkDispaths, i), pass, lang);
|
||||
chkDupPass(getElement(&chkDispaths, i), pass, lang);
|
||||
} else if (strncmp(mode, "length", 6) == 0) {
|
||||
// 長さの確認
|
||||
chkLenPass(getElement(&chkDispaths, i), pass, lang);
|
||||
} else if (strncmp(mode, "strength", 8) == 0) {
|
||||
// 強さの確認
|
||||
chkCharPass(getElement(&chkDispaths, i), pass, lang);
|
||||
} else if (strncmp(mode, "duplicate", 9) == 0) {
|
||||
// 複数回同じパスワードの確認
|
||||
chkDupPass(getElement(&chkDispaths, i), pass, lang);
|
||||
}
|
||||
}
|
||||
|
||||
if (strncmp(lang, "en", 2) == 0) {
|
||||
if (strncmp(mode, "all", 3) == 0 || strncmp(mode, "length", 6) == 0)
|
||||
printf("\nShort password count: %d\n", weaklencount);
|
||||
if (strncmp(mode, "all", 3) == 0 || strncmp(mode, "strength", 8) == 0)
|
||||
printf("\nWeak password count: %d\n", weakcharcount);
|
||||
if (strncmp(mode, "all", 3) == 0 || strncmp(mode, "duplicate", 9) == 0)
|
||||
printf("\nDuplicate password count: %d\n", duppasscount);
|
||||
|
||||
printf("\nTotal weak passwords: %d\n", vulncount);
|
||||
if (vulncount > 0)
|
||||
printf("It's advised to change any of the weak "
|
||||
"passwords as soon as possible!\n");
|
||||
} else {
|
||||
if (strncmp(mode, "all", 3) == 0 || strncmp(mode, "length", 6) == 0)
|
||||
printf("\n短いパスワード数: %d\n", weaklencount);
|
||||
if (strncmp(mode, "all", 3) == 0 || strncmp(mode, "strength", 8) == 0)
|
||||
printf("\n弱いパスワード数: %d\n", weakcharcount);
|
||||
if (strncmp(mode, "all", 3) == 0 || strncmp(mode, "duplicate", 9) == 0)
|
||||
printf("\n同じパスワード数: %d\n", duppasscount);
|
||||
|
||||
printf("\n不安のパスワードの合計: %d\n", vulncount);
|
||||
if (vulncount > 0)
|
||||
printf("不安的なパスワードは出来るだけ早く変更する事をお勧めします!\n");
|
||||
}
|
||||
|
||||
freeList(&fpaths);
|
||||
freeList(&dupPath);
|
||||
freeList(&dupPasswd);
|
||||
}
|
||||
18
src/chkpass.h
Normal file
18
src/chkpass.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef CHKPASS_H
|
||||
#define CHKPASS_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
void chkpass(const char *dpath, const char *mode);
|
||||
|
||||
extern int vulncount;
|
||||
extern int weaklencount;
|
||||
extern int weakcharcount;
|
||||
extern int duppasscount;
|
||||
|
||||
extern size_t minimumlen;
|
||||
extern size_t recommendlen;
|
||||
|
||||
extern const char *spchar;
|
||||
|
||||
#endif
|
||||
273
src/common.c
Normal file
273
src/common.c
Normal file
@@ -0,0 +1,273 @@
|
||||
#include "common.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#define MAXFINDLEN 1024
|
||||
|
||||
char *getbasedir(int trailing) {
|
||||
char *lang = getlang();
|
||||
|
||||
char *homedir = getenv("HOME");
|
||||
if (homedir == NULL) {
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
perror("Failed to get home directory");
|
||||
else perror("ホームディレクトリを受取に失敗");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if defined(__HAIKU__)
|
||||
char *basedir = "/config/settings/sp";
|
||||
char *slash = "/";
|
||||
#elif defined(_WIN32)
|
||||
char *basedir = "\\AppData\\Local\\076\\sp";
|
||||
char *slash = "\\";
|
||||
#else
|
||||
char *basedir = "/.local/share/sp";
|
||||
char *slash = "/";
|
||||
#endif
|
||||
|
||||
size_t len = strlen(homedir) + strlen(basedir) + strlen(slash) + 4;
|
||||
char *res = malloc(len);
|
||||
if (res == NULL) {
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
perror("Failed to allocate memory");
|
||||
else perror("メモリの役割に失敗");
|
||||
return NULL;
|
||||
}
|
||||
if (trailing == 1)
|
||||
snprintf(res, len, "%s%s%s", homedir, basedir, slash);
|
||||
else
|
||||
snprintf(res, len, "%s%s", homedir, basedir);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
char *getlang() {
|
||||
char *lang = NULL;
|
||||
|
||||
lang = getenv("SP_LANG");
|
||||
if (lang == NULL) lang = "ja";
|
||||
|
||||
return lang;
|
||||
}
|
||||
|
||||
int mkdir_r(const char *path, mode_t mode) {
|
||||
char tmp[256];
|
||||
char *p = NULL;
|
||||
size_t len;
|
||||
|
||||
snprintf(tmp, sizeof(tmp), "%s", path);
|
||||
|
||||
len = strlen(tmp);
|
||||
if (tmp[len - 1] == '/') {
|
||||
tmp[len - 1] = 0; // 最後の「/」文字を取り消す
|
||||
}
|
||||
|
||||
for (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;
|
||||
}
|
||||
|
||||
int tmpcopy(const char *inpath, const char *outpath) {
|
||||
FILE *src = fopen(inpath, "rb");
|
||||
if (!src) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
FILE *dst = fopen(outpath, "wb");
|
||||
if (!dst) {
|
||||
fclose(src);
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t n, m;
|
||||
unsigned char buf[8192];
|
||||
do {
|
||||
n = fread(buf, 1, sizeof buf, src);
|
||||
if (n) m = fwrite(buf, 1, n, dst);
|
||||
else m = 0;
|
||||
} while ((n > 0) && (n == m));
|
||||
if (m) return -1;
|
||||
|
||||
fclose(src);
|
||||
fclose(dst);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
char *strcasestr(const char *haystack, const char *needle) {
|
||||
size_t needle_len = strlen(needle);
|
||||
if (needle_len == 0) {
|
||||
return (char *)haystack;
|
||||
}
|
||||
|
||||
while (*haystack) {
|
||||
if (strncasecmp(haystack, needle, needle_len) == 0) {
|
||||
return (char *)haystack;
|
||||
}
|
||||
haystack++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
void rmext(char *filename) {
|
||||
char *ext = strrchr(filename, '.');
|
||||
if (ext != NULL && strcmp(ext, ".gpg") == 0) {
|
||||
*ext = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
void scanDir(const char *dpath, const char *rpath, List *fpaths,
|
||||
List *fullpaths, List *dispaths) {
|
||||
char *lang = getlang();
|
||||
|
||||
DIR *dir = opendir(dpath);
|
||||
if (!dir) {
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
perror("Could not open directory");
|
||||
else perror("ディレクトリを開けられません");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
struct dirent *entry;
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
const char *name = entry->d_name;
|
||||
if (strncmp(name, ".", 1) == 0 || strncmp(name, "..", 2) == 0) continue;
|
||||
|
||||
char fpath[MAXFINDLEN];
|
||||
snprintf(fpath, sizeof(fpath), "%s/%s", dpath, name);
|
||||
|
||||
struct stat s;
|
||||
if (stat(fpath, &s) != 0) {
|
||||
closedir(dir);
|
||||
return;
|
||||
}
|
||||
|
||||
if (S_ISDIR(s.st_mode)) {
|
||||
scanDir(fpath, rpath, fpaths, fullpaths, dispaths);
|
||||
} else if (strstr(name, ".gpg") != NULL) {
|
||||
const char *rel = fpath + strlen(rpath) + 1;
|
||||
addElement(fpaths, rel);
|
||||
addElement(fullpaths, fpath);
|
||||
char *disname = strdup(rel);
|
||||
rmext(disname);
|
||||
addElement(dispaths, disname);
|
||||
|
||||
free(disname);
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
void initList(List *list) {
|
||||
list->head = NULL;
|
||||
list->tail = NULL;
|
||||
list->size = 0;
|
||||
}
|
||||
|
||||
void addElement(List *list, const char *data) {
|
||||
Node *newNode = (Node *)malloc(sizeof(Node));
|
||||
if (!newNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
newNode->data = strdup(data);
|
||||
if (!newNode->data) {
|
||||
free(newNode);
|
||||
return;
|
||||
}
|
||||
newNode->next = NULL;
|
||||
|
||||
if (list->tail) {
|
||||
list->tail->next = newNode;
|
||||
} else {
|
||||
list->head = newNode;
|
||||
}
|
||||
|
||||
list->tail = newNode;
|
||||
list->size++;
|
||||
}
|
||||
|
||||
char *getElement(List *list, size_t idx) {
|
||||
if (idx >= list->size) return NULL;
|
||||
|
||||
Node *current = list->head;
|
||||
for (size_t i = 0; i < idx; i++)
|
||||
current = current->next;
|
||||
|
||||
return current->data;
|
||||
}
|
||||
|
||||
void rmElement(List *list, size_t idx) {
|
||||
if (idx >= list->size) return;
|
||||
|
||||
Node *current = list->head;
|
||||
Node *previous = NULL;
|
||||
|
||||
if (idx == 0) {
|
||||
list->head = current->next;
|
||||
if (list->size == 1) list->tail = NULL;
|
||||
} else {
|
||||
for (size_t i = 0; i < idx; i++) {
|
||||
previous = current;
|
||||
current = current->next;
|
||||
}
|
||||
previous->next = current->next;
|
||||
if (idx == list->size - 1) {
|
||||
list->tail = previous;
|
||||
}
|
||||
}
|
||||
|
||||
free(current->data);
|
||||
free(current);
|
||||
list->size--;
|
||||
}
|
||||
|
||||
void freeList(List *list) {
|
||||
Node *current = list->head;
|
||||
|
||||
while (current) {
|
||||
Node *next = current->next;
|
||||
free(current->data);
|
||||
free(current);
|
||||
current = next;
|
||||
}
|
||||
|
||||
list->head = NULL;
|
||||
list->tail = NULL;
|
||||
list->size = 0;
|
||||
}
|
||||
|
||||
void handle_sigint(int sig) {
|
||||
(void)sig;
|
||||
|
||||
if (getenv("WAYLAND_DISPLAY") != NULL) {
|
||||
system("echo -n \"\" | wl-copy");
|
||||
} else {
|
||||
system("echo -n \"\" | xclip -selection clipboard");
|
||||
}
|
||||
|
||||
if (strncmp(getlang(), "en", 2) == 0) {
|
||||
printf("\nClipboard cleared and program aborted.\n");
|
||||
} else {
|
||||
printf("\nクリップボードをクリアし、プログラムが中止されました。\n");
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
||||
40
src/common.h
Normal file
40
src/common.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#ifndef COMMON_H
|
||||
#define COMMON_H
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <gpgme.h>
|
||||
|
||||
typedef struct Node {
|
||||
char *data;
|
||||
struct Node *next;
|
||||
} Node;
|
||||
|
||||
typedef struct {
|
||||
Node *head;
|
||||
Node *tail;
|
||||
size_t size;
|
||||
} List;
|
||||
|
||||
char *getbasedir(int trailing);
|
||||
char *getlang();
|
||||
int mkdir_r(const char *path, mode_t mode);
|
||||
int tmpcopy(const char *inpath, const char *outpath);
|
||||
void scanDir(const char *dpath, const char *rpath, List *fpaths,
|
||||
List *fullpaths, List *dispaths);
|
||||
void handle_sigint(int sig);
|
||||
|
||||
// C言語のvector
|
||||
void initList(List *list);
|
||||
void addElement(List *list, const char *data);
|
||||
char *getElement(List *list, size_t idx);
|
||||
void rmElement(List *list, size_t idx);
|
||||
void freeList(List *list);
|
||||
|
||||
#endif
|
||||
162
src/delpass.c
Normal file
162
src/delpass.c
Normal file
@@ -0,0 +1,162 @@
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "delpass.h"
|
||||
|
||||
int delcnt(const char *str, char delimiter) {
|
||||
int count = 0;
|
||||
while (*str) {
|
||||
if (*str == delimiter) {
|
||||
count++;
|
||||
}
|
||||
str++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
char **explode(const char *str, char delimiter, int *numtokens) {
|
||||
int count = delcnt(str, delimiter) + 1;
|
||||
*numtokens = count;
|
||||
|
||||
char **tokens = malloc(count * sizeof(char *));
|
||||
if (tokens == NULL) {
|
||||
perror("malloc");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
char *strcopy = strdup(str);
|
||||
if (strcopy == NULL) {
|
||||
perror("strdup");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
char *token = strtok(strcopy, &delimiter);
|
||||
int i = 0;
|
||||
while (token != NULL) {
|
||||
tokens[i] = strdup(token);
|
||||
if (tokens[i] == NULL) {
|
||||
perror("strdup");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
i++;
|
||||
token = strtok(NULL, &delimiter);
|
||||
}
|
||||
|
||||
free(strcopy);
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
void freetokens(char **tokens, int numtokens) {
|
||||
for (int i = 0; i < numtokens; i++) {
|
||||
free(tokens[i]);
|
||||
}
|
||||
|
||||
free(tokens);
|
||||
}
|
||||
|
||||
int delpass(char *file, int force) {
|
||||
char *lang = getlang();
|
||||
|
||||
// パスを準備
|
||||
char pwfile[512];
|
||||
char *basedir = getbasedir(1);
|
||||
char *ext = ".gpg";
|
||||
|
||||
int alllen = snprintf(NULL, 0, "%s%s%s", basedir, file, ext) + 1;
|
||||
char *gpgpathchk = malloc(alllen);
|
||||
if (gpgpathchk == NULL) {
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
perror("Failed to allocate memory");
|
||||
else perror("メモリを割当に失敗");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// ファイルが既に存在するかどうか確認
|
||||
snprintf(gpgpathchk, alllen, "%s%s%s", basedir, file, ext);
|
||||
if (access(gpgpathchk, F_OK) != 0) {
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
perror("Password does not exist");
|
||||
else perror("パスワードが存在しません");
|
||||
free(gpgpathchk);
|
||||
return -1;
|
||||
}
|
||||
free(gpgpathchk);
|
||||
|
||||
int needed = snprintf( pwfile, sizeof(pwfile), "%s%s%s", basedir, file, ext);
|
||||
if (needed >= (int)sizeof(pwfile)) {
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
perror("Error: Path is too long");
|
||||
else perror("エラー:パスが長すぎる");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 削除を確認する
|
||||
if (force == 0) { // パスワードの変更の場合、確認は不要
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
printf("Are you sure you want to delete the password '%s'? (y/N): ", file);
|
||||
printf("パスワード「%s」を本当に削除する事が宜しいでしょうか? (y/N): ", file);
|
||||
int confirm = getchar();
|
||||
if (confirm != 'y' && confirm != 'Y') {
|
||||
if (strncmp(lang, "en", 2) == 0) puts("Not deleted");
|
||||
else puts("削除しませんでした");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ch;
|
||||
while ((ch = getchar()) != '\n' && ch != EOF);
|
||||
}
|
||||
|
||||
if (unlink(pwfile) == -1) {
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
perror("Password cannot be deleted");
|
||||
else perror("パスワードを削除出来ませんでした");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 空のディレクトリの場合
|
||||
int numt;
|
||||
char **tokens = explode(file, '/', &numt);
|
||||
|
||||
char passpath[1024];
|
||||
snprintf(passpath, sizeof(passpath), "%s%s", basedir, tokens[0]);
|
||||
char *ls = strrchr(basedir, '/');
|
||||
if (ls != NULL) {
|
||||
*ls = '\0';
|
||||
}
|
||||
|
||||
for (int i = 1; i < numt; i++) {
|
||||
if (i == (numt-1)) continue;
|
||||
strncat(passpath, "/", sizeof(passpath) - strlen(passpath) - 1);
|
||||
strncat(passpath, tokens[i], sizeof(passpath) - strlen(passpath) - 1);
|
||||
}
|
||||
|
||||
for (int i = numt - 1; i >= 0; i--) {
|
||||
// ~/.local/share/sp を削除したら危険
|
||||
if (strncmp(passpath, basedir, sizeof(passpath)) == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
// ディレクトリが空じゃない場合、削除を止める
|
||||
if (rmdir(passpath) == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
char *last_slash = strrchr(passpath, '/');
|
||||
if (last_slash != NULL) {
|
||||
*last_slash = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
freetokens(tokens, numt);
|
||||
|
||||
// sp -e の場合、「パスワードを削除しました」って要らない
|
||||
if (force == 1) return 0;
|
||||
|
||||
if (strncmp(lang, "en", 2) == 0) puts("The password got deleted");
|
||||
else puts("パスワードを削除しました");
|
||||
|
||||
return 0;
|
||||
}
|
||||
6
src/delpass.h
Normal file
6
src/delpass.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef DELPASS_H
|
||||
#define DELPASS_H
|
||||
|
||||
int delpass(char *file, int force);
|
||||
|
||||
#endif
|
||||
25
src/findpass.c
Normal file
25
src/findpass.c
Normal file
@@ -0,0 +1,25 @@
|
||||
#include "common.h"
|
||||
#include "findpass.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
List findFullpaths;
|
||||
List findDispaths;
|
||||
|
||||
void findpass(const char *dpath, const char *txt) {
|
||||
List fpaths;
|
||||
initList(&fpaths);
|
||||
initList(&findFullpaths);
|
||||
initList(&findDispaths);
|
||||
scanDir(dpath, dpath, &fpaths, &findFullpaths, &findDispaths);
|
||||
|
||||
for (size_t i = 0; i < findDispaths.size; i++) {
|
||||
if (strcasestr(getElement(&findDispaths, i), txt) != NULL) {
|
||||
printf("%s\n", getElement(&findDispaths, i));
|
||||
}
|
||||
}
|
||||
|
||||
freeList(&fpaths);
|
||||
}
|
||||
8
src/findpass.h
Normal file
8
src/findpass.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef FINDPASS_H
|
||||
#define FINDPASS_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
void findpass(const char *dpath, const char *txt);
|
||||
|
||||
#endif
|
||||
32
src/genpass.c
Normal file
32
src/genpass.c
Normal file
@@ -0,0 +1,32 @@
|
||||
#include "common.h"
|
||||
#include "genpass.h"
|
||||
|
||||
void genpass(int count, bool issecure) {
|
||||
char *lang = getlang();
|
||||
|
||||
const char *charset_risky =
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
const char *charset_secure =
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!\"#$%&'()=~-^\\|_@`[{]};:+*<>,./?";
|
||||
const char *charset = issecure ? charset_secure : charset_risky;
|
||||
|
||||
FILE *fp = fopen("/dev/random", "rb");
|
||||
if (fp == NULL) {
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
perror("Could not open /dev/random");
|
||||
else perror("/dev/randomを開けられませんでした");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
char password[count + 1];
|
||||
for (int i = 0; i < count; i++) {
|
||||
unsigned char key;
|
||||
fread(&key, sizeof(key), 1, fp);
|
||||
password[i] = charset[key % strlen(charset)];
|
||||
}
|
||||
|
||||
password[count] = '\0';
|
||||
fclose(fp);
|
||||
|
||||
printf("%s\n", password);
|
||||
}
|
||||
8
src/genpass.h
Normal file
8
src/genpass.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef GENPASS_H
|
||||
#define GENPASS_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
void genpass(int count, bool issecure);
|
||||
|
||||
#endif
|
||||
48
src/initpass.c
Normal file
48
src/initpass.c
Normal file
@@ -0,0 +1,48 @@
|
||||
#include "common.h"
|
||||
#include "initpass.h"
|
||||
|
||||
void initpass(char *gpgid) {
|
||||
char *lang = getlang();
|
||||
|
||||
char *basedir = getbasedir(1);
|
||||
|
||||
if (mkdir_r(basedir, 0755) != 0 && errno != EEXIST) {
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
fprintf(stderr, "Failed to create directory.\n");
|
||||
else fprintf(stderr, "ディレクトリを作成に失敗。\n");
|
||||
return;
|
||||
}
|
||||
|
||||
char gpgidpath[512];
|
||||
snprintf(gpgidpath, sizeof(gpgidpath), "%s/.gpg-id", basedir);
|
||||
|
||||
struct stat statbuf;
|
||||
if (stat(gpgidpath, &statbuf) == 0) {
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
fprintf(stderr, ".gpg-id file already exists.\n");
|
||||
else fprintf(stderr, ".gpg-idファイルは既に存在します。\n");
|
||||
return;
|
||||
}
|
||||
|
||||
FILE *gpgidfile = fopen(gpgidpath, "w");
|
||||
if (gpgidfile == NULL) {
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
fprintf(stderr, "Failed to write .gpg-id file.\n");
|
||||
else fprintf(stderr, ".gpg-idファイルを書き込めません。\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (fprintf(gpgidfile, "%s\n", gpgid) < 0) {
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
fprintf(stderr, "Failed to write .gpg-id file.\n");
|
||||
else fprintf(stderr, ".gpg-idファイルへの書き込みに失敗しました。\n");
|
||||
fclose(gpgidfile);
|
||||
return;
|
||||
}
|
||||
|
||||
fclose(gpgidfile);
|
||||
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
puts("Initialization completed.");
|
||||
else puts("初期設定に完了しました。");
|
||||
}
|
||||
6
src/initpass.h
Normal file
6
src/initpass.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef INITPASS_H
|
||||
#define INITPASS_H
|
||||
|
||||
void initpass(char* gpgid);
|
||||
|
||||
#endif
|
||||
56
src/listpass.c
Normal file
56
src/listpass.c
Normal file
@@ -0,0 +1,56 @@
|
||||
#include <dirent.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "listpass.h"
|
||||
|
||||
void listpass(char *basePath, int level) {
|
||||
char *lang = getlang();
|
||||
|
||||
struct dirent *entry;
|
||||
DIR* dir = opendir(basePath);
|
||||
if (!dir) {
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
perror("Could not open directory");
|
||||
else perror("ディレクトリを開けられません");
|
||||
return;
|
||||
}
|
||||
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
char path[1000];
|
||||
int needed = snprintf(path, sizeof(path), "%s/%s", basePath, entry->d_name);
|
||||
if (needed >= (int)sizeof(path) || needed < 0) {
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
perror("Error: Path is too long, or failed to get lenth");
|
||||
else perror("エラー:パスが長すぎる、又は長さを受取に失敗");
|
||||
continue;
|
||||
}
|
||||
|
||||
struct stat statbuf;
|
||||
if (stat(path, &statbuf) == -1) {
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
perror("Failed to read file status");
|
||||
else perror("ファイル状況を読込に失敗");
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i = 0; i < level; i++) {
|
||||
printf(" ");
|
||||
}
|
||||
|
||||
if (S_ISDIR(statbuf.st_mode)) {
|
||||
printf("|-- %s\n", entry->d_name);
|
||||
listpass(path, level + 1);
|
||||
} else if (S_ISREG(statbuf.st_mode)) {
|
||||
char *filename = entry->d_name;
|
||||
char *ext = strstr(filename, ".gpg");
|
||||
if (ext) *ext = '\0';
|
||||
printf("|-- %s\n", filename);
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
6
src/listpass.h
Normal file
6
src/listpass.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef LISTPASS_H
|
||||
#define LISTPASS_H
|
||||
|
||||
void listpass(char *basePath, int level);
|
||||
|
||||
#endif
|
||||
246
src/otppass.c
Normal file
246
src/otppass.c
Normal file
@@ -0,0 +1,246 @@
|
||||
#include <openssl/hmac.h>
|
||||
#include <openssl/sha.h>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#if defined(__APPLE)
|
||||
#include <libkern/OSByteOrder.h>
|
||||
#define htobe64(x) OSSwapHostToBigInt64(x)
|
||||
#endif
|
||||
|
||||
#include "base32.h"
|
||||
#include "common.h"
|
||||
#include "otppass.h"
|
||||
|
||||
unsigned char *extract_secret(const char *otpauth_url, size_t *decoded_len) {
|
||||
char *lang = getlang();
|
||||
|
||||
const char *secret_start = strstr(otpauth_url, "secret=");
|
||||
if (!secret_start) {
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
perror("Failed to find secret in the OTPAuth URL");
|
||||
else perror("OTPAuth URLの中に、シークレットを見つけられませんでした");
|
||||
return NULL;
|
||||
}
|
||||
secret_start += 7;
|
||||
|
||||
const char *secret_end = strchr(secret_start, '&');
|
||||
if (!secret_end) {
|
||||
if (secret_start[0] != '\0') {
|
||||
secret_end = secret_start + strlen(secret_start);
|
||||
} else {
|
||||
secret_end = secret_start;
|
||||
}
|
||||
}
|
||||
|
||||
if (secret_end < secret_start) {
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
perror("Incorrect secret range");
|
||||
else perror("不正なシークレットの距離");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t secret_len = secret_end - secret_start;
|
||||
char *secret_encoded = (char *)malloc(secret_len + 1);
|
||||
|
||||
if (secret_encoded == NULL) {
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
perror("Failed to allocate memory");
|
||||
else perror("メモリの役割に失敗");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
strncpy(secret_encoded, secret_start, secret_len);
|
||||
secret_encoded[secret_len] = '\0';
|
||||
|
||||
unsigned char *secret_decoded = base32_decode(secret_encoded, decoded_len);
|
||||
free(secret_encoded);
|
||||
|
||||
if (!secret_decoded) {
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
perror("Failed to decrypt BASE32");
|
||||
else perror("BASE32の復号化に失敗");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return secret_decoded;
|
||||
}
|
||||
|
||||
#if defined(__HAIKU__)
|
||||
uint64_t htobe64(uint64_t counter) {
|
||||
uint64_t res = 0;
|
||||
uint8_t *dest = (uint8_t *)&res;
|
||||
uint8_t *src = (uint8_t *)&counter;
|
||||
|
||||
dest[0] = src[7];
|
||||
dest[1] = src[6];
|
||||
dest[2] = src[5];
|
||||
dest[3] = src[4];
|
||||
dest[4] = src[3];
|
||||
dest[5] = src[2];
|
||||
dest[6] = src[1];
|
||||
dest[7] = src[0];
|
||||
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t generate_totp(const char *secret, uint64_t counter) {
|
||||
counter = htobe64(counter);
|
||||
|
||||
unsigned char hash[SHA_DIGEST_LENGTH];
|
||||
HMAC(
|
||||
EVP_sha1(),
|
||||
secret,
|
||||
strlen(secret),
|
||||
(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;
|
||||
}
|
||||
|
||||
void otppass(char *file, int isCopy, int copyTimeout) {
|
||||
if (isCopy == 1 && copyTimeout > 300) copyTimeout = 300;
|
||||
char *lang = getlang();
|
||||
|
||||
int isGay = (getenv("WAYLAND_DISPLAY") != NULL);
|
||||
|
||||
// Xセッションではない場合(例えば、SSH、TTY等)、otppass()を実行して
|
||||
if (isCopy == 1 && getenv("DISPLAY") == NULL && getenv("WAYLAND_DISPLAY") == NULL) {
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
puts("There is no X or Wayland session, so running 'sp -o'.");
|
||||
else puts("X又はWaylandセッションではありませんので、「sp -o」を実行します。");
|
||||
otppass(file, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
// CTRL + Cを押す場合
|
||||
if (isCopy) {
|
||||
signal(SIGINT, handle_sigint);
|
||||
}
|
||||
|
||||
gpgme_ctx_t ctx;
|
||||
gpgme_error_t err;
|
||||
gpgme_data_t in, out;
|
||||
size_t secret_len;
|
||||
|
||||
gpgme_check_version(NULL);
|
||||
err = gpgme_new(&ctx);
|
||||
if (err) {
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
perror("Failed to generate the GPG");
|
||||
else perror("GPGを創作に失敗");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
err = gpgme_data_new_from_file(&in, file, 1);
|
||||
if (err) {
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
perror("Failed to read the GPG file");
|
||||
else perror("GPGファイルを読込に失敗");
|
||||
gpgme_release(ctx);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
err = gpgme_data_new(&out);
|
||||
if (err) {
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
perror("Failed to read the GPG data");
|
||||
else perror("GPGデータを読込に失敗");
|
||||
gpgme_release(ctx);
|
||||
gpgme_data_release(in);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
err = gpgme_op_decrypt(ctx, in, out);
|
||||
if (err) {
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
perror("Failed to decrypt the GPG");
|
||||
else perror("GPGを復号化に失敗");
|
||||
gpgme_release(ctx);
|
||||
gpgme_data_release(in);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
char *secret = gpgme_data_release_and_get_mem(out, &secret_len);
|
||||
if (!secret) {
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
perror("Failed to get the GPG");
|
||||
else perror("GPGを受取に失敗");
|
||||
gpgme_data_release(in);
|
||||
gpgme_release(ctx);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
secret[secret_len] = '\0';
|
||||
|
||||
size_t decoded_len;
|
||||
unsigned char *secret_decoded = extract_secret(secret, &decoded_len);
|
||||
if (!secret_decoded) {
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
perror("Failed to decode or export secret");
|
||||
else perror("シークレットの抽出又はデコードに失敗しました");
|
||||
gpgme_data_release(in);
|
||||
gpgme_release(ctx);
|
||||
gpgme_free(secret);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
secret_decoded[decoded_len] = '\0';
|
||||
|
||||
time_t current_time = time(NULL);
|
||||
uint64_t counter = current_time / 30;
|
||||
uint32_t otp = generate_totp((const char *)secret_decoded, counter);
|
||||
|
||||
gpgme_data_release(in);
|
||||
gpgme_release(ctx);
|
||||
gpgme_free(secret);
|
||||
free(secret_decoded);
|
||||
|
||||
if (isCopy) {
|
||||
char cmd[64];
|
||||
if (isGay) snprintf(cmd, sizeof(cmd), "echo -n %06d | wl-copy", otp);
|
||||
else snprintf(cmd, sizeof(cmd), "echo -n %06d | xclip -selection clipboard", otp);
|
||||
|
||||
int ret = system(cmd);
|
||||
if (ret != 0) {
|
||||
char *ero = (strncmp(lang, "en", 2) == 0 ?
|
||||
"Failed to copy OTP." : "ワンタイムパスワードをコピーに失敗。");
|
||||
fprintf(stderr, "%s\n", ero);
|
||||
}
|
||||
|
||||
// 何(デフォルトは30)秒後、クリップボードから削除する
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
printf(
|
||||
"%s\n%s%d%s\n",
|
||||
"Added the one time password to the clipboard.",
|
||||
"After ",
|
||||
copyTimeout,
|
||||
" seconds it'll be deleted from the clipboard."
|
||||
);
|
||||
else
|
||||
printf(
|
||||
"%s\n%d%s\n",
|
||||
"ワンタイムパスワードをクリップボードに追加しました。",
|
||||
copyTimeout,
|
||||
"秒後はクリップボードから取り消されます。"
|
||||
);
|
||||
|
||||
sleep(copyTimeout);
|
||||
|
||||
if (isGay) system("echo -n \"\" | wl-copy");
|
||||
else system("echo -n \"\" | xclip -selection clipboard");
|
||||
} else {
|
||||
printf("%06d\n", otp);
|
||||
}
|
||||
}
|
||||
6
src/otppass.h
Normal file
6
src/otppass.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef OTPPASS_H
|
||||
#define OTPPASS_H
|
||||
|
||||
void otppass(char* file, int isCopy, int copyTimeout);
|
||||
|
||||
#endif
|
||||
148
src/showpass.c
Normal file
148
src/showpass.c
Normal file
@@ -0,0 +1,148 @@
|
||||
#include <locale.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "showpass.h"
|
||||
|
||||
void clean_up(
|
||||
gpgme_ctx_t ctx,
|
||||
gpgme_data_t in,
|
||||
gpgme_data_t out,
|
||||
FILE *gpgfile,
|
||||
char *gpgpath
|
||||
) {
|
||||
if (gpgfile) fclose(gpgfile);
|
||||
if (gpgpath) free(gpgpath);
|
||||
gpgme_data_release(in);
|
||||
gpgme_data_release(out);
|
||||
gpgme_release(ctx);
|
||||
}
|
||||
|
||||
const char *showpass(char *file) {
|
||||
char *lang = getlang();
|
||||
|
||||
gpgme_ctx_t ctx;
|
||||
gpgme_error_t err;
|
||||
gpgme_data_t in = NULL, out = NULL;
|
||||
char *gpgpath = NULL;
|
||||
FILE *gpgfile = NULL;
|
||||
|
||||
// GPGMEライブラリを設置
|
||||
setlocale(LC_ALL, "");
|
||||
gpgme_check_version(NULL);
|
||||
gpgme_set_locale(NULL, LC_CTYPE, setlocale(LC_CTYPE, NULL));
|
||||
|
||||
// GPGMEを創作
|
||||
err = gpgme_new(&ctx);
|
||||
if (err) {
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
fprintf(stderr, "Failed to generate GPGME: %s\n", gpgme_strerror(err));
|
||||
else fprintf(stderr, "GPGMEを創作に失敗:%s\n", gpgme_strerror(err));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// OpenPGPプロトコールを設定
|
||||
gpgme_set_protocol(ctx, GPGME_PROTOCOL_OpenPGP);
|
||||
if (err) {
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
fprintf(stderr, "Failed to set OpenPGP protocol: %s\n", gpgme_strerror(err));
|
||||
else fprintf(stderr, "OpenPGP付トロコールの設置に失敗:%s\n", gpgme_strerror(err));
|
||||
clean_up(ctx, in, out, gpgfile, gpgpath);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// 暗号化したタイルを開く
|
||||
char *basedir = getbasedir(1);
|
||||
char *ext = ".gpg";
|
||||
int alllen = snprintf(NULL, 0, "%s%s%s", basedir, file, ext) + 1;
|
||||
gpgpath = malloc(alllen);
|
||||
if (gpgpath == NULL) {
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
perror("Failed to allocate memeory");
|
||||
else perror("メモリを割当に失敗");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
snprintf(gpgpath, alllen, "%s%s%s", basedir, file, ext);
|
||||
|
||||
gpgfile = fopen(gpgpath, "rb");
|
||||
if (gpgfile == NULL) {
|
||||
if (strncmp(lang, "en", 2) == 0) {
|
||||
perror("Failed to open file");
|
||||
} else {
|
||||
perror("ファイルを開くに失敗");
|
||||
}
|
||||
free(gpgpath);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// ファイルからinデータオブジェクトを創作
|
||||
if (gpgme_data_new_from_stream(&in, gpgfile) != GPG_ERR_NO_ERROR) {
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
perror("Failed to generate the GPGME data object");
|
||||
else perror("GPGMEデータオブジェクトを創作に失敗");
|
||||
clean_up(ctx, in, out, gpgfile, gpgpath);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// outデータオブジェクトを創作
|
||||
if (gpgme_data_new(&out) != GPG_ERR_NO_ERROR) {
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
perror("Failed to generate the GPGME data object");
|
||||
else perror("GPGMEデータオブジェクトを創作に失敗");
|
||||
clean_up(ctx, in, out, gpgfile, gpgpath);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// データオブジェクトを創作
|
||||
gpgme_data_new(&out);
|
||||
|
||||
// 復号化して
|
||||
err = gpgme_op_decrypt(ctx, in, out);
|
||||
if (err) {
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
fprintf(stderr, "Failed to decrypt: %s (source: %s)\n",
|
||||
gpgme_strerror(err), gpgme_strsource(err));
|
||||
else fprintf(stderr, "復号化に失敗: %s (元: %s)\n",
|
||||
gpgme_strerror(err), gpgme_strsource(err));
|
||||
|
||||
// 掃除
|
||||
clean_up(ctx, in, out, gpgfile, gpgpath);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// 復号化したパスワードを表示する
|
||||
gpgme_data_seek(out, 0, SEEK_SET);
|
||||
size_t bufsize = 512;
|
||||
size_t totsize = 0;
|
||||
char buffer[512];
|
||||
char *res = malloc(bufsize);
|
||||
if (res == NULL) {
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
perror("Failed to allocate memory");
|
||||
else perror("メモリを役割に失敗");
|
||||
clean_up(ctx, in, out, gpgfile, gpgpath);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ssize_t read_bytes;
|
||||
|
||||
while ((read_bytes = gpgme_data_read(out, buffer, sizeof(buffer) - 1)) > 0) {
|
||||
if (totsize + read_bytes >= bufsize) {
|
||||
bufsize *= 2;
|
||||
res = realloc(res, bufsize);
|
||||
if (res == NULL) {
|
||||
perror("Failed to reallocate memory");
|
||||
clean_up(ctx, in, out, gpgfile, gpgpath);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
memcpy(res + totsize, buffer, read_bytes);
|
||||
totsize += read_bytes;
|
||||
}
|
||||
|
||||
res[totsize] = '\0';
|
||||
|
||||
// 掃除
|
||||
clean_up(ctx, in, out, gpgfile, gpgpath);
|
||||
return res;
|
||||
}
|
||||
6
src/showpass.h
Normal file
6
src/showpass.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef SHOWPASS_H
|
||||
#define SHOWPASS_H
|
||||
|
||||
const char *showpass(char *file);
|
||||
|
||||
#endif
|
||||
141
src/vulnpass.c
Normal file
141
src/vulnpass.c
Normal file
@@ -0,0 +1,141 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include "vulnpass.h"
|
||||
#include "showpass.h"
|
||||
#include "common.h"
|
||||
|
||||
List vulnFullpaths;
|
||||
List vulnDispaths;
|
||||
|
||||
void vulnpass(const char *dpath) {
|
||||
char *lang = 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", NULL, &hints, &addr);
|
||||
if (status != 0) {
|
||||
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
|
||||
return;
|
||||
}
|
||||
|
||||
sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sock == -1) {
|
||||
const char *ero = strncmp(lang, "en", 2) == 0 ?
|
||||
"Failed to create socket" : "ソケットを作成に失敗";
|
||||
perror(ero);
|
||||
return;
|
||||
}
|
||||
|
||||
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) {
|
||||
const char *ero = strncmp(lang, "en", 2) == 0 ? "Failed to connect" : "接続に失敗";
|
||||
perror(ero);
|
||||
close(sock);
|
||||
return;
|
||||
}
|
||||
|
||||
// パスワードをスキャンして
|
||||
List fpaths;
|
||||
initList(&fpaths);
|
||||
initList(&vulnFullpaths);
|
||||
initList(&vulnDispaths);
|
||||
scanDir(dpath, dpath, &fpaths, &vulnFullpaths, &vulnDispaths);
|
||||
int vulncount = 0;
|
||||
|
||||
puts(strncmp(lang, "en", 2) == 0 ?
|
||||
"Checking, please wait for a while..." : "確認中。暫くお待ち下さい・・・");
|
||||
|
||||
for (size_t i = 0; i < vulnDispaths.size; i++) {
|
||||
const char *pass = showpass(getElement(&vulnDispaths, i));
|
||||
if (!pass) continue;
|
||||
char res[256];
|
||||
int reslen = 0;
|
||||
|
||||
if (send(sock, pass, strlen(pass), 0) < 0) {
|
||||
const char *ero = strncmp(lang, "en", 2) == 0 ?
|
||||
"Failed to send" : "送信に失敗";
|
||||
perror(ero);
|
||||
close(sock);
|
||||
freeList(&fpaths);
|
||||
return;
|
||||
}
|
||||
|
||||
reslen = recv(sock, res, sizeof(res) -1, 0);
|
||||
if (reslen < 0) {
|
||||
const char *ero = strncmp(lang, "en", 2) == 0 ?
|
||||
"Failed to retreive" : "受取に失敗";
|
||||
perror(ero);
|
||||
close(sock);
|
||||
freeList(&fpaths);
|
||||
return;
|
||||
}
|
||||
|
||||
res[reslen] = '\0';
|
||||
|
||||
if (strncmp(res, "0", 1) != 0) {
|
||||
printf("【!】");
|
||||
if (strncmp(lang, "en", 2) == 0) {
|
||||
printf("The password \"%s\" has been breached %d times.\n",
|
||||
getElement(&vulnDispaths, i), atoi(res));
|
||||
} else {
|
||||
printf("パスワード「%s」は%d回に漏洩されました。\n",
|
||||
getElement(&vulnDispaths, i), atoi(res));
|
||||
}
|
||||
|
||||
vulncount++;
|
||||
}
|
||||
|
||||
close(sock);
|
||||
|
||||
// 再接続
|
||||
sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sock == -1) {
|
||||
const char *ero = strncmp(lang, "en", 2) == 0 ?
|
||||
"Failed to create socket" : "ソケットを作成に失敗";
|
||||
perror(ero);
|
||||
return;
|
||||
}
|
||||
|
||||
if (connect(sock, (struct sockaddr *)&srv, sizeof(srv)) < 0) {
|
||||
const char *ero = strncmp(lang, "en", 2) == 0 ?
|
||||
"Failed to reconnect" : "再接続に失敗";
|
||||
perror(ero);
|
||||
close(sock);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (strncmp(lang, "en", 2) == 0) {
|
||||
printf("\nTotal breached: %d\n", vulncount);
|
||||
if (vulncount > 0)
|
||||
printf("It's advised to change any of the breached "
|
||||
"passwords as soon as possible!\n");
|
||||
} else {
|
||||
printf("\n漏洩したパスワードの合計: %d\n", vulncount);
|
||||
if (vulncount > 0)
|
||||
printf("漏洩したパスワードは出来るだけ早く変更する事をお勧めします!\n");
|
||||
}
|
||||
|
||||
freeList(&fpaths);
|
||||
}
|
||||
6
src/vulnpass.h
Normal file
6
src/vulnpass.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef VULNPASS_H
|
||||
#define VULNPASS_H
|
||||
|
||||
void vulnpass(const char *dpath);
|
||||
|
||||
#endif
|
||||
154
src/yankpass.c
Normal file
154
src/yankpass.c
Normal file
@@ -0,0 +1,154 @@
|
||||
#include <locale.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "yankpass.h"
|
||||
#include "showpass.h"
|
||||
|
||||
void yankpass(char *file, int copyTimeout) {
|
||||
if (copyTimeout > 300) copyTimeout = 300;
|
||||
char *lang = getlang();
|
||||
|
||||
int isGay = (getenv("WAYLAND_DISPLAY") != NULL);
|
||||
|
||||
// Xセッションではない場合(例えば、SSH、TTY等)、showpass()を実行して
|
||||
if (getenv("DISPLAY") == NULL && getenv("WAYLAND_DISPLAY") == NULL) {
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
puts("There is no X or Wayland session, so running 'sp -s'.");
|
||||
else puts("X又はWaylandセッションではありませんので、「sp -s」を実行します。");
|
||||
showpass(file);
|
||||
return;
|
||||
}
|
||||
|
||||
gpgme_ctx_t ctx;
|
||||
gpgme_error_t err;
|
||||
gpgme_data_t in, out;
|
||||
FILE *gpgfile;
|
||||
|
||||
// GPGMEライブラリを設置
|
||||
setlocale(LC_ALL, "");
|
||||
gpgme_check_version(NULL);
|
||||
gpgme_set_locale(NULL, LC_CTYPE, setlocale(LC_CTYPE, NULL));
|
||||
|
||||
// GPGMEを創作
|
||||
err = gpgme_new(&ctx);
|
||||
if (err) {
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
fprintf(stderr, "Failed to generate GPGME: %s\n", gpgme_strerror(err));
|
||||
else fprintf(stderr, "GPGMEを創作に失敗:%s\n", gpgme_strerror(err));
|
||||
return;
|
||||
}
|
||||
|
||||
// CTRL + Cを押す場合
|
||||
signal(SIGINT, handle_sigint);
|
||||
|
||||
// OpenPGPプロトコールを設定
|
||||
gpgme_set_protocol(ctx, GPGME_PROTOCOL_OpenPGP);
|
||||
|
||||
// 暗号化したタイルを開く
|
||||
char *basedir = getbasedir(1);
|
||||
char* ext = ".gpg";
|
||||
int alllen = snprintf(NULL, 0, "%s%s%s", basedir, file, ext) + 1;
|
||||
char* gpgpath = malloc(alllen);
|
||||
if (gpgpath == NULL) {
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
perror("Failed to allocate memory");
|
||||
else perror("メモリを割当に失敗");
|
||||
return;
|
||||
}
|
||||
|
||||
snprintf(gpgpath, alllen, "%s%s%s", basedir, file, ext);
|
||||
gpgfile = fopen(gpgpath, "rb");
|
||||
if (gpgfile == NULL) {
|
||||
if (strncmp(lang, "en", 2) == 0) {
|
||||
perror("Failed to open the file");
|
||||
fprintf(stderr, "Failed path: %s\n", gpgpath);
|
||||
} else {
|
||||
perror("ファイルを開くに失敗");
|
||||
fprintf(stderr, "失敗したパス: %s\n", gpgpath);
|
||||
}
|
||||
free(gpgpath);
|
||||
return;
|
||||
}
|
||||
gpgme_data_new_from_stream(&in, gpgfile);
|
||||
|
||||
// データオブジェクトを創作
|
||||
gpgme_data_new(&out);
|
||||
|
||||
// 復号化して
|
||||
err = gpgme_op_decrypt(ctx, in, out);
|
||||
if (err) {
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
fprintf(stderr, "Failed to decrypt: %s\n", gpgme_strerror(err));
|
||||
else fprintf(stderr, "復号化に失敗: %s\n", gpgme_strerror(err));
|
||||
|
||||
// 掃除
|
||||
fclose(gpgfile);
|
||||
free(gpgpath);
|
||||
gpgme_data_release(in);
|
||||
gpgme_data_release(out);
|
||||
gpgme_release(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
// xclip又はwl-copyを準備して
|
||||
char *cmd;
|
||||
if (isGay) cmd = "wl-copy";
|
||||
else cmd = "xclip -selection clipboard";
|
||||
|
||||
FILE *pipe = popen(cmd, "w");
|
||||
if (pipe == NULL) {
|
||||
// 掃除
|
||||
fclose(gpgfile);
|
||||
free(gpgpath);
|
||||
gpgme_data_release(in);
|
||||
gpgme_data_release(out);
|
||||
gpgme_release(ctx);
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
perror("Could not find a clipboard");
|
||||
else perror("クリップボードを見つけられませんでした");
|
||||
return;
|
||||
}
|
||||
|
||||
// 復号化したパスワードを表示する
|
||||
gpgme_data_seek(out, 0, SEEK_SET);
|
||||
|
||||
char buffer[512];
|
||||
ssize_t read_bytes;
|
||||
|
||||
while ((read_bytes = gpgme_data_read(out, buffer, sizeof(buffer))) > 0) {
|
||||
if (buffer[read_bytes - 1] == '\n') {
|
||||
read_bytes--;
|
||||
}
|
||||
fwrite(buffer, 1, read_bytes, pipe);
|
||||
}
|
||||
|
||||
pclose(pipe);
|
||||
|
||||
// 何(デフォルトは45)秒後、クリップボードから削除する
|
||||
if (strncmp(lang, "en", 2) == 0)
|
||||
printf(
|
||||
"%s\n%s%d%s\n",
|
||||
"Added password to the clipboard.",
|
||||
"After ",
|
||||
copyTimeout,
|
||||
" seconds it'll be deleted from the clipboard."
|
||||
);
|
||||
else
|
||||
printf(
|
||||
"%s\n%d%s\n",
|
||||
"パスワードをクリップボードに追加しました。",
|
||||
copyTimeout,
|
||||
"秒後はクリップボードから取り消されます。"
|
||||
);
|
||||
sleep(copyTimeout);
|
||||
if (isGay) system("echo -n \"\" | wl-copy");
|
||||
else system("echo -n \"\" | xclip -selection clipboard");
|
||||
|
||||
// 掃除
|
||||
fclose(gpgfile);
|
||||
free(gpgpath);
|
||||
gpgme_data_release(in);
|
||||
gpgme_data_release(out);
|
||||
gpgme_release(ctx);
|
||||
}
|
||||
6
src/yankpass.h
Normal file
6
src/yankpass.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef YANKPASS_H
|
||||
#define YANKPASS_H
|
||||
|
||||
void yankpass(char *file, int copyTimeout);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user