SVNからのミラー

This commit is contained in:
2026-01-21 04:43:42 +09:00
commit 34b8d30c50
34 changed files with 2927 additions and 0 deletions

61
CHANGELOG.md Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View File

@@ -0,0 +1,6 @@
#ifndef DELPASS_H
#define DELPASS_H
int delpass(char *file, int force);
#endif

25
src/findpass.c Normal file
View 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
View 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
View 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
View 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
View 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
View File

@@ -0,0 +1,6 @@
#ifndef INITPASS_H
#define INITPASS_H
void initpass(char* gpgid);
#endif

56
src/listpass.c Normal file
View 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
View File

@@ -0,0 +1,6 @@
#ifndef LISTPASS_H
#define LISTPASS_H
void listpass(char *basePath, int level);
#endif

246
src/otppass.c Normal file
View 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
View 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
View 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
View File

@@ -0,0 +1,6 @@
#ifndef SHOWPASS_H
#define SHOWPASS_H
const char *showpass(char *file);
#endif

141
src/vulnpass.c Normal file
View 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
View File

@@ -0,0 +1,6 @@
#ifndef VULNPASS_H
#define VULNPASS_H
void vulnpass(const char *dpath);
#endif

154
src/yankpass.c Normal file
View 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
View File

@@ -0,0 +1,6 @@
#ifndef YANKPASS_H
#define YANKPASS_H
void yankpass(char *file, int copyTimeout);
#endif