コミットを比較
47 コミット
作成者 | SHA1 | 日付 |
---|---|---|
守矢諏訪子 | 8084424b16 | |
守矢諏訪子 | 35fef77fef | |
守矢諏訪子 | f64a68ddf2 | |
守矢諏訪子 | eed1ed1361 | |
守矢諏訪子 | 45dc3ac6dc | |
守矢諏訪子 | f652e8d5d6 | |
守矢諏訪子 | 26c0d00954 | |
守矢諏訪子 | af6d9c174f | |
守矢諏訪子 | 43f463ba75 | |
守矢諏訪子 | cf7147a90f | |
守矢諏訪子 | c1e344ac71 | |
守矢諏訪子 | 6a5b916d29 | |
守矢諏訪子 | 5f7cb70922 | |
守矢諏訪子 | 69ef928b4b | |
守矢諏訪子 | 0668578cbd | |
守矢諏訪子 | 58793174a4 | |
守矢諏訪子 | aac723e78c | |
守矢諏訪子 | 284abb1fad | |
守矢諏訪子 | fe286d22ce | |
守矢諏訪子 | 2e5e0b61b2 | |
守矢諏訪子 | a5c6e3fe54 | |
守矢諏訪子 | 6f7eeaa45c | |
守矢諏訪子 | 386bac6792 | |
守矢諏訪子 | 5003ea13bc | |
守矢諏訪子 | 61271640e7 | |
守矢諏訪子 | 51c119e794 | |
守矢諏訪子 | 1f997af2c1 | |
守矢諏訪子 | 417423417c | |
守矢諏訪子 | 1e3ce35091 | |
守矢諏訪子 | a29867337e | |
守矢諏訪子 | 8ea7ebd47d | |
守矢諏訪子 | 7add5ffa21 | |
守矢諏訪子 | 0aaf5f12b2 | |
守矢諏訪子 | 297b9766dd | |
守矢諏訪子 | e6defe3271 | |
守矢諏訪子 | 461b952da7 | |
守矢諏訪子 | ffa3590ec7 | |
守矢諏訪子 | 53a5739fa4 | |
守矢諏訪子 | 3976c11d91 | |
守矢諏訪子 | a4f76ab2d9 | |
守矢諏訪子 | e0d792f5c3 | |
守矢諏訪子 | a7d6f12c46 | |
守矢諏訪子 | 330722bb68 | |
守矢諏訪子 | f68a09c490 | |
守矢諏訪子 | 55543618e5 | |
守矢諏訪子 | c42f23f79f | |
守矢諏訪子 | 000e111406 |
|
@ -1,4 +1,5 @@
|
||||||
sp
|
sp
|
||||||
.ccls-cache
|
.ccls-cache
|
||||||
*.o
|
*.o
|
||||||
*.tar.gz
|
release
|
||||||
|
*.core
|
||||||
|
|
30
CHANGELOG.md
30
CHANGELOG.md
|
@ -1,3 +1,33 @@
|
||||||
|
# 1.3.0
|
||||||
|
* 英訳の追加
|
||||||
|
* GNU Make → BSD Make
|
||||||
|
* GPLv2 → ISC
|
||||||
|
* OpenBSD向けのリリースコマンドの追加
|
||||||
|
* FreeBSD向けのリリースコマンドの追加
|
||||||
|
* Linux向けのリリースコマンドの追加
|
||||||
|
* 最新ルールに従い
|
||||||
|
* NetBSD向けのリリースコマンドの追加
|
||||||
|
* OpenBSD 7.5でTOTPの修正 (ヌル終端文字列のバグ)
|
||||||
|
* ヘルプの表示の削除 (manpageをご利用下さい)
|
||||||
|
* パスワード作成関数のデフォルトな長さは64に
|
||||||
|
* manpageを細かくに
|
||||||
|
* パスワードがなくなるまで削除したら、ディレクトリも削除する様に
|
||||||
|
|
||||||
|
# 1.2.0
|
||||||
|
* やっとTOTP機能性を修正した
|
||||||
|
* makeを実行したら、バイナリがもっと小さくなる
|
||||||
|
* パスワードの長さの延長
|
||||||
|
* パスワード追加機能性で、パスワードが既に存在するかどうか確認
|
||||||
|
* パスワード削除機能性で、パスワードが存在ないかどうか確認
|
||||||
|
* パスワード変更機能性の追加
|
||||||
|
* zsh対応の修正
|
||||||
|
|
||||||
|
# 1.1.2
|
||||||
|
* OpenBSDでのコンパイラーが発生された問題を修正した
|
||||||
|
|
||||||
|
# 1.1.1
|
||||||
|
* make install-zsh部分を修正
|
||||||
|
|
||||||
# 1.1.0
|
# 1.1.0
|
||||||
* TOTP対応
|
* TOTP対応
|
||||||
* READMEファイルで使い方を詳しく説明する
|
* READMEファイルで使い方を詳しく説明する
|
||||||
|
|
14
LICENSE.txt
14
LICENSE.txt
|
@ -0,0 +1,14 @@
|
||||||
|
Copyright © 2004-2011 by Internet Systems Consortium, Inc. ("ISC")
|
||||||
|
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.
|
75
Makefile
75
Makefile
|
@ -1,33 +1,80 @@
|
||||||
NAME=sp
|
UNAME_S != uname -s
|
||||||
VERSION=1.1.0
|
UNAME_M != uname -m
|
||||||
# Linux、Haiku、かIllumos = /usr、FreeBSDかOpenBSD = /usr/local、NetBSD = /usr/pkg
|
|
||||||
PREFIX=/usr
|
NAME != cat main.c | grep "const char \*sofname" | awk '{print $$5}' | \
|
||||||
CC=cc
|
sed "s/\"//g" | sed "s/;//"
|
||||||
FILES=main.c showpass.c yankpass.c addpass.c delpass.c listpass.c genpass.c initpass.c otppass.c
|
VERSION != cat main.c | grep "const char \*version" | awk '{print $$5}' | \
|
||||||
CFLAGS=-Wall -Wextra -g
|
sed "s/\"//g" | sed "s/;//"
|
||||||
LDFLAGS=-lgpgme -lcrypto
|
PREFIX = /usr/local
|
||||||
|
|
||||||
|
MANPREFIX = ${PREFIX}/man
|
||||||
|
|
||||||
|
.if ${UNAME_S} == "FreeBSD"
|
||||||
|
MANPREFIX = ${PREFIX}/share/man
|
||||||
|
.elif ${UNAME_S} == "Linux"
|
||||||
|
PREFIX = /usr
|
||||||
|
MANPREFIX = ${PREFIX}/share/man
|
||||||
|
.elif ${UNAME_S} == "NetBSD"
|
||||||
|
PREFIX = /usr/pkg
|
||||||
|
MANPREFIX = ${PREFIX}/share/man
|
||||||
|
.endif
|
||||||
|
|
||||||
|
CC = cc
|
||||||
|
FILES = main.c src/*.c
|
||||||
|
CFLAGS = -Wall -Wextra -O3 -I${PREFIX}/include -L${PREFIX}/lib
|
||||||
|
.if ${UNAME_S} == "NetBSD"
|
||||||
|
CFLAGS += -I/usr/local/include -L/usr/local/lib -I/usr/include -L/usr/lib
|
||||||
|
.endif
|
||||||
|
LDFLAGS = -lgpgme -lcrypto
|
||||||
|
|
||||||
all:
|
all:
|
||||||
${CC} ${CFLAGS} -o ${NAME} ${FILES} ${LDFLAGS}
|
${CC} ${CFLAGS} -o ${NAME} ${FILES} ${LDFLAGS}
|
||||||
|
strip ${NAME}
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f ${NAME}
|
rm -f ${NAME}
|
||||||
|
|
||||||
dist: clean
|
dist: clean
|
||||||
mkdir -p ${NAME}-${VERSION}
|
mkdir -p release/src ${NAME}-${VERSION}
|
||||||
cp -R LICENSE.txt Makefile README.md CHANGELOG.md\
|
cp -R LICENSE.txt Makefile README.md CHANGELOG.md \
|
||||||
sp-completion.zsh\
|
${NAME}-completion.zsh ${NAME}.1 main.c src ${NAME}-${VERSION}
|
||||||
*.c *.h ${NAME}-${VERSION}
|
tar zcfv release/src/${NAME}-${VERSION}.tar.gz ${NAME}-${VERSION}
|
||||||
tar zcfv ${NAME}-${VERSION}.tar.gz ${NAME}-${VERSION}
|
|
||||||
rm -rf ${NAME}-${VERSION}
|
rm -rf ${NAME}-${VERSION}
|
||||||
|
|
||||||
|
release-openbsd:
|
||||||
|
mkdir -p release/bin
|
||||||
|
${CC} ${CFLAGS} -o release/bin/${NAME}-${VERSION}-openbsd-${UNAME_M} ${FILES} \
|
||||||
|
-static -lgpgme -lcrypto -lc -lassuan -lgpg-error -lintl -liconv
|
||||||
|
strip release/bin/${NAME}-${VERSION}-openbsd-${UNAME_M}
|
||||||
|
|
||||||
|
release-freebsd:
|
||||||
|
mkdir -p release/bin
|
||||||
|
${CC} ${CFLAGS} -o release/bin/${NAME}-${VERSION}-freebsd-${UNAME_M} ${FILES} \
|
||||||
|
-static -lgpgme -lcrypto -lc -lassuan -lgpg-error -lthr -lintl
|
||||||
|
strip release/bin/${NAME}-${VERSION}-freebsd-${UNAME_M}
|
||||||
|
|
||||||
|
release-netbsd:
|
||||||
|
mkdir -p release/bin
|
||||||
|
${CC} ${CFLAGS} -o release/bin/${NAME}-${VERSION}-netbsd-${UNAME_M} ${FILES} \
|
||||||
|
-static -lgpgme -lcrypto -lcrypt -lc -lassuan -lgpg-error -lintl
|
||||||
|
strip release/bin/${NAME}-${VERSION}-netbsd-${UNAME_M}
|
||||||
|
|
||||||
|
release-linux:
|
||||||
|
mkdir -p release/bin
|
||||||
|
${CC} ${CFLAGS} -o release/bin/${NAME}-${VERSION}-linux-${UNAME_M} ${FILES} \
|
||||||
|
-static -lgpgme -lcrypto -lc -lassuan -lgpg-error
|
||||||
|
strip release/bin/${NAME}-${VERSION}-linux-${UNAME_M}
|
||||||
|
|
||||||
install: all
|
install: all
|
||||||
mkdir -p ${DESTDIR}${PREFIX}/bin
|
mkdir -p ${DESTDIR}${PREFIX}/bin
|
||||||
cp -f ${NAME} ${DESTDIR}${PREFIX}/bin
|
cp -f ${NAME} ${DESTDIR}${PREFIX}/bin
|
||||||
chmod 755 ${DESTDIR}${PREFIX}/bin/${NAME}
|
chmod 755 ${DESTDIR}${PREFIX}/bin/${NAME}
|
||||||
|
mkdir -p ${DESTDIR}${MANPREFIX}/man1
|
||||||
|
sed "s/VERSION/${VERSION}/g" < ${NAME}.1 > ${DESTDIR}${MANPREFIX}/man1/${NAME}.1
|
||||||
|
chmod 644 ${DESTDIR}${MANPREFIX}/man1/${NAME}.1
|
||||||
|
|
||||||
install-zsh:
|
install-zsh:
|
||||||
cp sp-completion.zsh /usr/share/zsh/site-functions/_sp
|
cp sp-completion.zsh ${DESTDIR}${PREFIX}/share/zsh/site-functions/_sp
|
||||||
|
|
||||||
uninstall:
|
uninstall:
|
||||||
rm -f ${DESTDIR}${PREFIX}/bin/${NAME}
|
rm -f ${DESTDIR}${PREFIX}/bin/${NAME}
|
||||||
|
|
10
README.md
10
README.md
|
@ -6,6 +6,7 @@
|
||||||
```sh
|
```sh
|
||||||
doas prt-get depinst gpgme gnupg pinentry
|
doas prt-get depinst gpgme gnupg pinentry
|
||||||
doas make install
|
doas make install
|
||||||
|
doas make install-zsh
|
||||||
```
|
```
|
||||||
|
|
||||||
又は
|
又は
|
||||||
|
@ -23,18 +24,21 @@ prt-get depinst sp
|
||||||
```sh
|
```sh
|
||||||
doas pacman -S base-devel gpgme gnupg pinentry
|
doas pacman -S base-devel gpgme gnupg pinentry
|
||||||
doas make install
|
doas make install
|
||||||
|
doas make install-zsh
|
||||||
```
|
```
|
||||||
|
|
||||||
### OpenBSD
|
### OpenBSD
|
||||||
```sh
|
```sh
|
||||||
doas pkg_add gmake gpgme gnupg pinentry
|
doas pkg_add gmake gpgme gnupg pinentry
|
||||||
doas gmake install PREFIX=/usr/local
|
doas gmake install PREFIX=/usr/local
|
||||||
|
doas gmake install-zsh PREFIX=/usr/local
|
||||||
```
|
```
|
||||||
|
|
||||||
### FreeBSD
|
### FreeBSD
|
||||||
```sh
|
```sh
|
||||||
doas pkg install gmake gpgme gnupg pinentry
|
doas pkg install gmake gpgme gnupg pinentry
|
||||||
doas gmake install PREFIX=/usr/local
|
doas gmake install PREFIX=/usr/local
|
||||||
|
doas gmake install-zsh PREFIX=/usr/local
|
||||||
```
|
```
|
||||||
|
|
||||||
## 初期設定
|
## 初期設定
|
||||||
|
@ -85,8 +89,10 @@ $ sp -d 076.moe/suwako
|
||||||
|
|
||||||
## TOTP(ワンタイムパスワード)
|
## TOTP(ワンタイムパスワード)
|
||||||
### QRコードから
|
### QRコードから
|
||||||
QRコードをダウンロードし、zbarimgを使用して「QR-Code:」以降の部分をコピーし、spに追加して下さい。\
|
QRコードをダウンロードし、zbarimgを使用して「QR-Code:」以降の部分をコピーし、
|
||||||
`sp -a`を実行すると、「パスワード」を聞かれますが、TOTPの場合は「otpauth://」から始まる文字列をコピペして下さい。
|
spに追加して下さい。\
|
||||||
|
`sp -a`を実行すると、「パスワード」を聞かれますが、
|
||||||
|
TOTPの場合は「otpauth://」から始まる文字列をコピペして下さい。
|
||||||
```sh
|
```sh
|
||||||
$ zbarimg -q gitler.png
|
$ 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=〇〇
|
QR-Code:otpauth://totp/Gitler%20%28gitler.moe%29:suwako?algorithm=SHA1&digits=6&issuer=Gitler%20%28gitler.moe%29&period=30&secret=〇〇
|
||||||
|
|
259
addpass.c
259
addpass.c
|
@ -1,259 +0,0 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <locale.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
#include <gpgme.h>
|
|
||||||
#include <termios.h>
|
|
||||||
|
|
||||||
#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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
void addpass(char* file) {
|
|
||||||
char pass[100];
|
|
||||||
char knin[100];
|
|
||||||
|
|
||||||
// パスワードを受け取る
|
|
||||||
getpasswd("パスワード: ", pass, sizeof(pass));
|
|
||||||
puts("");
|
|
||||||
getpasswd("パスワード(確認用): ", knin, sizeof(knin));
|
|
||||||
puts("");
|
|
||||||
|
|
||||||
// パスワードが一致するかどうか確認
|
|
||||||
if (strcmp(pass, knin) != 0) {
|
|
||||||
fprintf(stderr, "パスワードが一致していません。終了…\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// パスワードを保存する
|
|
||||||
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) {
|
|
||||||
fprintf(stderr, "GPGMEを創作に失敗:%s\n", gpgme_strerror(err));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// GPGMEは非対話的モードに設定
|
|
||||||
err = gpgme_set_pinentry_mode(ctx, GPGME_PINENTRY_MODE_LOOPBACK);
|
|
||||||
if (err) {
|
|
||||||
fprintf(stderr, "pinentryモードを設定に失敗: %s\n", gpgme_strerror(err));
|
|
||||||
gpgme_release(ctx);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// パスワードからデータオブジェクトを創作
|
|
||||||
err = gpgme_data_new_from_mem(&in, pass, strlen(pass), 0);
|
|
||||||
if (err) {
|
|
||||||
fprintf(stderr, "データオブジェクトを創作に失敗: %s\n", gpgme_strerror(err));
|
|
||||||
gpgme_release(ctx);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
gpgme_data_new(&out);
|
|
||||||
|
|
||||||
// パスを準備
|
|
||||||
char* homedir = getenv("HOME");
|
|
||||||
if (homedir == NULL) {
|
|
||||||
perror("ホームディレクトリを受取に失敗。");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
char* basedir = "/.local/share/sp/";
|
|
||||||
char* ext = ".gpg";
|
|
||||||
|
|
||||||
// 鍵を受け取る
|
|
||||||
char keypath[256];
|
|
||||||
snprintf(keypath, sizeof(keypath), "%s%s%s", homedir, basedir, ".gpg-id");
|
|
||||||
keypath[sizeof(keypath) - 1] = '\0';
|
|
||||||
|
|
||||||
FILE* keyfile = fopen(keypath, "rb");
|
|
||||||
if (keyfile == NULL) {
|
|
||||||
perror(".gpg-idファイルを開くに失敗。");
|
|
||||||
printf("失敗したパス: %s\n", keypath);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
char* keyid = malloc(100);
|
|
||||||
if (!fgets(keyid, 100, keyfile)) {
|
|
||||||
perror("鍵IDを読込に失敗。");
|
|
||||||
fclose(keyfile);
|
|
||||||
free(keyid);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
keyid[strcspn(keyid, "\n")] = 0;
|
|
||||||
fclose(keyfile);
|
|
||||||
|
|
||||||
err = gpgme_get_key(ctx, keyid, &key[0], 0);
|
|
||||||
if (err) {
|
|
||||||
fprintf(stderr, "鍵を受取に失敗: %s\n", gpgme_strerror(err));
|
|
||||||
free(keyid);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key[0] == NULL) {
|
|
||||||
fprintf(stderr, "エラー:鍵はNULLです。\n");
|
|
||||||
free(keyid);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
free(keyid);
|
|
||||||
|
|
||||||
// 暗号化
|
|
||||||
err = gpgme_op_encrypt(ctx, &key[0], GPGME_ENCRYPT_ALWAYS_TRUST, in, out);
|
|
||||||
if (err) {
|
|
||||||
fprintf(stderr, "暗号化に失敗: %s\n", gpgme_strerror(err));
|
|
||||||
cleanup(ctx, key[0], in, out);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 暗号化したタイルを開く
|
|
||||||
int alllen = snprintf(NULL, 0, "%s%s%s%s", homedir, basedir, file, ext) + 1;
|
|
||||||
char* gpgpath = malloc(alllen);
|
|
||||||
if (gpgpath == NULL) {
|
|
||||||
cleanup(ctx, key[0], in, out);
|
|
||||||
perror("メモリを割当に失敗。");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ディレクトリを創作
|
|
||||||
char dirpath[512];
|
|
||||||
snprintf(dirpath, sizeof(dirpath), "%s%s%s", homedir, basedir, file);
|
|
||||||
dirpath[sizeof(dirpath) - 1] = '\0';
|
|
||||||
|
|
||||||
char* lastsla = strrchr(dirpath, '/');
|
|
||||||
if (lastsla != NULL) {
|
|
||||||
*lastsla = '\0';
|
|
||||||
if (mkdir_r(dirpath, 0755) != 0) {
|
|
||||||
free(gpgpath);
|
|
||||||
cleanup(ctx, key[0], in, out);
|
|
||||||
perror("ディレクトリを創作に失敗。");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintf(gpgpath, alllen, "%s%s%s%s", homedir, basedir, file, ext);
|
|
||||||
struct stat statbuf;
|
|
||||||
if (stat(gpgpath, &statbuf) == 0) {
|
|
||||||
free(gpgpath);
|
|
||||||
cleanup(ctx, key[0], in, out);
|
|
||||||
fprintf(stderr, "パスワードは既に存在しています。\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
gpgfile = fopen(gpgpath, "wb");
|
|
||||||
if (gpgfile == NULL) {
|
|
||||||
perror("ファイルを開くに失敗。");
|
|
||||||
printf("失敗したパス: %s\n", gpgpath);
|
|
||||||
free(gpgpath);
|
|
||||||
cleanup(ctx, key[0], in, out);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// データが保存したかどうか確認
|
|
||||||
ssize_t encrypted_data_size = gpgme_data_seek(out, 0, SEEK_END);
|
|
||||||
if (encrypted_data_size <= 0) {
|
|
||||||
fprintf(stderr, "データを保存に失敗。\n");
|
|
||||||
fclose(gpgfile);
|
|
||||||
free(gpgpath);
|
|
||||||
cleanup(ctx, key[0], in, out);
|
|
||||||
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 (fwrite(buffer, 1, (size_t)read_bytes, gpgfile) != (size_t)read_bytes) {
|
|
||||||
perror("パスワードを書き込みに失敗");
|
|
||||||
free(gpgpath);
|
|
||||||
cleanup(ctx, key[0], in, out);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 掃除
|
|
||||||
fclose(gpgfile);
|
|
||||||
free(gpgpath);
|
|
||||||
cleanup(ctx, key[0], in, out);
|
|
||||||
|
|
||||||
printf("パスワードを保存出来ました。\n");
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
#ifndef ADDPASS_H
|
|
||||||
#define ADDPASS_H
|
|
||||||
|
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
int mkdir_r(const char *path, mode_t mode);
|
|
||||||
void addpass(char* file);
|
|
||||||
|
|
||||||
#endif
|
|
42
delpass.c
42
delpass.c
|
@ -1,42 +0,0 @@
|
||||||
#include <unistd.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#include "delpass.h"
|
|
||||||
|
|
||||||
int delpass(char* file) {
|
|
||||||
// パスを準備
|
|
||||||
char pwfile[512];
|
|
||||||
char* homedir = getenv("HOME");
|
|
||||||
if (homedir == NULL) {
|
|
||||||
perror("ホームディレクトリを受取に失敗。");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
char* basedir = "/.local/share/sp/";
|
|
||||||
char* ext = ".gpg";
|
|
||||||
int needed = snprintf(pwfile, sizeof(pwfile), "%s%s%s%s", homedir, basedir, file, ext);
|
|
||||||
if (needed >= (int)sizeof(pwfile)) {
|
|
||||||
fprintf(stderr, "エラー:パスが長すぎる。\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 削除を確認する
|
|
||||||
printf("パスワード「%s」を本当に削除する事が宜しいでしょうか? (y/N): ", file);
|
|
||||||
int confirm = getchar();
|
|
||||||
if (confirm != 'y' && confirm != 'Y') {
|
|
||||||
printf("削除しませんでした。\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ch;
|
|
||||||
while ((ch = getchar()) != '\n' && ch != EOF);
|
|
||||||
|
|
||||||
if (unlink(pwfile) == -1) {
|
|
||||||
perror("パスワードを削除出来ませんですた。");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("パスワードを削除しました。\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
29
genpass.c
29
genpass.c
|
@ -1,29 +0,0 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "genpass.h"
|
|
||||||
|
|
||||||
void genpass(int count, bool issecure) {
|
|
||||||
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) {
|
|
||||||
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);
|
|
||||||
}
|
|
50
initpass.c
50
initpass.c
|
@ -1,50 +0,0 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
#include "initpass.h"
|
|
||||||
#include "addpass.h"
|
|
||||||
|
|
||||||
void initpass(char* gpgid) {
|
|
||||||
char* homedir = getenv("HOME");
|
|
||||||
if (homedir == NULL) {
|
|
||||||
perror("ホームディレクトリを受取に失敗。");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
char* basedir = "/.local/share/sp/";
|
|
||||||
char dirpath[256];
|
|
||||||
snprintf(dirpath, sizeof(dirpath), "%s%s", homedir, basedir);
|
|
||||||
|
|
||||||
if (mkdir_r(dirpath, 0755) != 0 && errno != EEXIST) {
|
|
||||||
perror("ディレクトリを作成に失敗。");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
char gpgidpath[512];
|
|
||||||
snprintf(gpgidpath, sizeof(gpgidpath), "%s/.gpg-id", dirpath);
|
|
||||||
|
|
||||||
struct stat statbuf;
|
|
||||||
if (stat(gpgidpath, &statbuf) == 0) {
|
|
||||||
fprintf(stderr, ".gpg-idファイルは既に存在します。\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
FILE* gpgidfile = fopen(gpgidpath, "w");
|
|
||||||
if (gpgidfile == NULL) {
|
|
||||||
perror(".gpg-idファイルを書き込めません。");
|
|
||||||
fclose(gpgidfile);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fputs(gpgid, gpgidfile) == EOF) {
|
|
||||||
fprintf(stderr, ".gpg-idファイルへの書き込みに失敗しました。\n");
|
|
||||||
fclose(gpgidfile);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(gpgidfile);
|
|
||||||
printf("初期設定に完了しました。");
|
|
||||||
}
|
|
51
listpass.c
51
listpass.c
|
@ -1,51 +0,0 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include <dirent.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include "listpass.h"
|
|
||||||
|
|
||||||
void listpass(char* basePath, int level) {
|
|
||||||
struct dirent* entry;
|
|
||||||
DIR* dir = opendir(basePath);
|
|
||||||
if (!dir) {
|
|
||||||
perror("ディレクトリを開けられません。");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
while ((entry = readdir(dir)) != NULL) {
|
|
||||||
if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) {
|
|
||||||
char path[1000];
|
|
||||||
int needed = snprintf(path, sizeof(path), "%s/%s", basePath, entry->d_name);
|
|
||||||
if (needed >= (int)sizeof(path) || needed < 0) {
|
|
||||||
fprintf(stderr, "エラー:パスが長すぎる、又は長さを受取に失敗。");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct stat statbuf;
|
|
||||||
if (stat(path, &statbuf) == -1) {
|
|
||||||
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);
|
|
||||||
}
|
|
159
main.c
159
main.c
|
@ -1,72 +1,119 @@
|
||||||
#include <stdio.h>
|
#include "src/common.h"
|
||||||
#include <string.h>
|
#include "src/initpass.h"
|
||||||
#include <stdlib.h>
|
#include "src/showpass.h"
|
||||||
#include <stdbool.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 <gpgme.h>
|
const char *sofname = "sp";
|
||||||
|
const char *version = "1.3.0";
|
||||||
|
|
||||||
#include "initpass.h"
|
void usage() {
|
||||||
#include "showpass.h"
|
printf("%s-%s\nusage: %s [-adegilosvy]\n", sofname, version, sofname);
|
||||||
#include "yankpass.h"
|
|
||||||
#include "listpass.h"
|
|
||||||
#include "addpass.h"
|
|
||||||
#include "delpass.h"
|
|
||||||
#include "genpass.h"
|
|
||||||
#include "otppass.h"
|
|
||||||
void helpme();
|
|
||||||
|
|
||||||
const char* sofname = "sp";
|
|
||||||
const char* version = "1.1.0";
|
|
||||||
|
|
||||||
void helpme() {
|
|
||||||
printf("076 sp - シンプルなパスワードマネージャー\n");
|
|
||||||
printf("https://076.moe/ | https://gitler.moe/suwako/sp\n\n");
|
|
||||||
printf("使い方:\n");
|
|
||||||
printf("%s -i <gpg-id> :GPGと使ってパスワードストレージを初期設定\n", sofname);
|
|
||||||
printf("%s -s <パスワード名> :パスワードを表示\n", sofname);
|
|
||||||
printf("%s -y <パスワード名> :パスワードを表示せずクリップボードにコピーする\n", sofname);
|
|
||||||
printf("%s -l :パスワード一覧を表示\n", sofname);
|
|
||||||
printf("%s -a <パスワード名> :パスワードを追加\n", sofname);
|
|
||||||
printf("%s -d <パスワード名> :パスワードを削除\n", sofname);
|
|
||||||
printf("%s -g <文字数> [risk|secure] :希望文字数でパスワードをランダムに作成する。risk=英数字のみ(不安)、secure=英数字+特別文字(デフォルト)を使用\n", sofname);
|
|
||||||
printf("%s -o <パスワード名>\n :ワンタイムパスワード(TOTP)を表示。存在しなければ、創作する\n", sofname);
|
|
||||||
printf("%s -h :ヘルプを表示\n", sofname);
|
|
||||||
printf("%s -v :バージョンを表示\n", sofname);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main (int argc, char* argv[]) {
|
char *getfullpath(char *arg) {
|
||||||
|
char *lang = getlang();
|
||||||
|
|
||||||
|
char *homedir = getenv("HOME");
|
||||||
|
if (homedir == NULL) {
|
||||||
|
if (strncmp(lang, "en", 2) == 0)
|
||||||
|
perror("Failed to getting home directory");
|
||||||
|
else perror("ホームディレクトリを受取に失敗");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *basedir = "/.local/share/sp/";
|
||||||
|
size_t fullPathLen;
|
||||||
|
char *fullPath;
|
||||||
|
if (arg != NULL) {
|
||||||
|
fullPathLen = strlen(homedir) + strlen(basedir) + strlen(arg) + 5;
|
||||||
|
} else {
|
||||||
|
fullPathLen = strlen(homedir) + strlen(basedir);
|
||||||
|
}
|
||||||
|
|
||||||
|
fullPath = (char *)malloc(fullPathLen);
|
||||||
|
if (fullPath == NULL) {
|
||||||
|
if (strncmp(lang, "en", 2) == 0)
|
||||||
|
perror("Failed to allocating memory");
|
||||||
|
else perror("メモリの役割に失敗");
|
||||||
|
free(fullPath);
|
||||||
|
free(homedir);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg != NULL) {
|
||||||
|
snprintf(fullPath, fullPathLen, "%s%s%s.gpg", homedir, basedir, arg);
|
||||||
|
} else {
|
||||||
|
snprintf(fullPath, fullPathLen, "%s%s", homedir, basedir);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fullPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
if (argc < 2) {
|
if (argc < 2) {
|
||||||
helpme();
|
usage();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argc == 3 && strcmp(argv[1], "-i") == 0) initpass(argv[2]);
|
if (strcmp(argv[1], "-g") == 0) {
|
||||||
else if (argc == 3 && strcmp(argv[1], "-s") == 0) showpass(argv[2]);
|
if (argc > 4) {
|
||||||
else if (argc == 3 && strcmp(argv[1], "-y") == 0) yankpass(argv[2]);
|
usage();
|
||||||
else if (argc == 2 && strcmp(argv[1], "-l") == 0) {
|
return 1;
|
||||||
char basePath[512];
|
|
||||||
char* homedir = getenv("HOME");
|
|
||||||
if (homedir == NULL) {
|
|
||||||
perror("ホームディレクトリを受取に失敗。");
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char* basedir = "/.local/share/sp/";
|
if (argc == 2) genpass(64, true);
|
||||||
snprintf(basePath, sizeof(basePath), "%s%s", homedir, basedir);
|
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);
|
||||||
|
|
||||||
listpass(basePath, 0);
|
return 0;
|
||||||
}
|
}
|
||||||
else if (argc == 3 && strcmp(argv[1], "-a") == 0) addpass(argv[2]);
|
|
||||||
else if (argc == 3 && strcmp(argv[1], "-d") == 0) delpass(argv[2]);
|
if (argc == 3) {
|
||||||
else if (strcmp(argv[1], "-g") == 0) {
|
if (strcmp(argv[1], "-i") == 0) initpass(argv[2]);
|
||||||
if (argc == 3) genpass(atoi(argv[2]), true);
|
else if (strcmp(argv[1], "-s") == 0) {
|
||||||
else if (argc == 4 && strcmp(argv[3], "risk") == 0) genpass(atoi(argv[2]), false);
|
const char *pass = showpass(argv[2]);
|
||||||
else if (argc == 4 && strcmp(argv[3], "secure") == 0) genpass(atoi(argv[2]), true);
|
if (pass == NULL) return -1;
|
||||||
else helpme();
|
printf("%s\n", pass);
|
||||||
|
}
|
||||||
|
else if (strcmp(argv[1], "-y") == 0) yankpass(argv[2]);
|
||||||
|
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) {
|
||||||
|
delpass(argv[2], 1);
|
||||||
|
addpass(argv[2]);
|
||||||
|
} else if (strcmp(argv[1], "-o") == 0) {
|
||||||
|
char *fullPath = getfullpath(argv[2]);
|
||||||
|
if (fullPath == NULL) return -1;
|
||||||
|
otppass(fullPath);
|
||||||
|
free(fullPath);
|
||||||
|
} else {
|
||||||
|
usage();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} 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], "-v") == 0) printf("%s-%s\n", sofname, version);
|
||||||
|
else {
|
||||||
|
usage();
|
||||||
|
free(basePath);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
free(basePath);
|
||||||
|
} else {
|
||||||
|
usage();
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
else if (argc == 3 && strcmp(argv[1], "-o") == 0) otppass(argv[2]);
|
|
||||||
else if (argc == 2 && strcmp(argv[1], "-v") == 0) printf("%s-%s\n", sofname, version);
|
|
||||||
else helpme();
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
26
otppass.c
26
otppass.c
|
@ -1,26 +0,0 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <openssl/hmac.h>
|
|
||||||
|
|
||||||
unsigned long generate_totp(const unsigned char* secret, size_t keylen) {
|
|
||||||
unsigned long timestep = time(NULL) / 30;
|
|
||||||
unsigned char hmacres[20];
|
|
||||||
|
|
||||||
HMAC(EVP_sha1(), secret, keylen, (unsigned char*)×tep, sizeof(timestep), hmacres, NULL);
|
|
||||||
|
|
||||||
int offset = hmacres[19] & 0xF;
|
|
||||||
unsigned long trunhash = (hmacres[offset] & 0x7F) << 24 |
|
|
||||||
(hmacres[offset + 1] & 0xFF) << 16 |
|
|
||||||
(hmacres[offset + 2] & 0xFF) << 8 |
|
|
||||||
(hmacres[offset + 3] & 0xFF);
|
|
||||||
|
|
||||||
unsigned long otp = trunhash % 1000000;
|
|
||||||
return otp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void otppass(char* file) {
|
|
||||||
const char* secret = file;
|
|
||||||
unsigned long otp = generate_totp((unsigned char*)secret, strlen(secret));
|
|
||||||
printf("%06lu\n", otp);
|
|
||||||
}
|
|
102
showpass.c
102
showpass.c
|
@ -1,102 +0,0 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <locale.h>
|
|
||||||
|
|
||||||
#include <gpgme.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);
|
|
||||||
}
|
|
||||||
|
|
||||||
void showpass(char* file) {
|
|
||||||
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) {
|
|
||||||
fprintf(stderr, "GPGMEを創作に失敗:%s\n", gpgme_strerror(err));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// OpenPGPプロトコールを設定
|
|
||||||
gpgme_set_protocol(ctx, GPGME_PROTOCOL_OpenPGP);
|
|
||||||
|
|
||||||
// 暗号化したタイルを開く
|
|
||||||
char* homedir = getenv("HOME");
|
|
||||||
if (homedir == NULL) {
|
|
||||||
perror("ホームディレクトリを受取に失敗。");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
char* basedir = "/.local/share/sp/";
|
|
||||||
char* ext = ".gpg";
|
|
||||||
int alllen = snprintf(NULL, 0, "%s%s%s%s", homedir, basedir, file, ext) + 1;
|
|
||||||
char* gpgpath = malloc(alllen);
|
|
||||||
if (gpgpath == NULL) {
|
|
||||||
perror("メモリを割当に失敗。");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sprintf(gpgpath, "%s%s%s%s", homedir, basedir, file, ext);
|
|
||||||
gpgfile = fopen(gpgpath, "rb");
|
|
||||||
if (gpgfile == NULL) {
|
|
||||||
perror("ファイルを開くに失敗。");
|
|
||||||
printf("失敗したパス: %s\n", gpgpath);
|
|
||||||
free(gpgpath);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gpgme_data_new_from_stream(&in, gpgfile) != GPG_ERR_NO_ERROR || gpgme_data_new(&out) != GPG_ERR_NO_ERROR) {
|
|
||||||
fprintf(stderr, "GPGMEデータオブジェクトを創作に失敗。\n");
|
|
||||||
clean_up(ctx, in, out, gpgfile, gpgpath);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// データオブジェクトを創作
|
|
||||||
gpgme_data_new(&out);
|
|
||||||
|
|
||||||
// 復号化して
|
|
||||||
err = gpgme_op_decrypt(ctx, in, out);
|
|
||||||
if (err) {
|
|
||||||
fprintf(stderr, "復号化に失敗: %s\n", gpgme_strerror(err));
|
|
||||||
|
|
||||||
// 掃除
|
|
||||||
clean_up(ctx, in, out, gpgfile, gpgpath);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 復号化したパスワードを表示する
|
|
||||||
gpgme_data_seek(out, 0, SEEK_SET);
|
|
||||||
char buffer[512];
|
|
||||||
ssize_t read_bytes;
|
|
||||||
bool islastnl = false;
|
|
||||||
|
|
||||||
while ((read_bytes = gpgme_data_read(out, buffer, sizeof(buffer) - 1)) > 0) {
|
|
||||||
fwrite(buffer, 1, read_bytes, stdout);
|
|
||||||
if (buffer[read_bytes - 1] == '\n') {
|
|
||||||
islastnl = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!islastnl) {
|
|
||||||
putchar('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 掃除
|
|
||||||
clean_up(ctx, in, out, gpgfile, gpgpath);
|
|
||||||
}
|
|
|
@ -1,28 +1,48 @@
|
||||||
# compdef sp
|
#compdef sp
|
||||||
|
#autoload
|
||||||
|
|
||||||
_sp () {
|
_sp () {
|
||||||
local state
|
local cmd
|
||||||
local -a options
|
if (( CURRENT > 2)); then
|
||||||
local -a entries
|
cmd=${words[2]}
|
||||||
|
curcontext="${curcontext%:*:*}:pass-$cmd"
|
||||||
_arguments -C \
|
(( CURRENT-- ))
|
||||||
'-i[GPGと使ってパスワードストレージを初期設定]: :_sp_complete_keys' \
|
shift words
|
||||||
'-s[パスワードを表示]: :_sp_complete_entries' \
|
case "${cmd}" in
|
||||||
'-y[パスワードを表示せずクリップボードにコピーする]: :_sp_complete_entries' \
|
-i)
|
||||||
'-l[パスワード一覧を表示]' \
|
_sp_complete_keys
|
||||||
'-a[パスワードを追加]: :_sp_complete_entries' \
|
;;
|
||||||
'-d[パスワードを削除]: :_sp_complete_entries' \
|
-i|-y|-d|-a|-e|-o)
|
||||||
'-g[希望文字数でパスワードをランダムに作成する]: :_sp_complete_entries' \
|
_sp_complete_entries_helper
|
||||||
'-o[ワンタイムパスワード(TOTP)を表示]: :_sp_complete_entries' \
|
;;
|
||||||
'-h[ヘルプを表示]' \
|
-g)
|
||||||
'-v[バージョンを表示]' \
|
local -a subcommands
|
||||||
'*:: :->subcmds'
|
subcommands=(
|
||||||
|
"secure:英数字+特別文字(デフォルト)"
|
||||||
case $state in
|
"risk:英数字のみ(不安)"
|
||||||
subcmds)
|
)
|
||||||
_message "no more arguments"
|
;;
|
||||||
;;
|
-s)
|
||||||
esac
|
_sp_cmd_show
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
else
|
||||||
|
local -a subcommands
|
||||||
|
subcommands=(
|
||||||
|
"-i:GPGと使ってパスワードストレージを初期設定"
|
||||||
|
"-s:パスワードを表示"
|
||||||
|
"-y:パスワードを表示せずクリップボードにコピーする"
|
||||||
|
"-l:パスワード一覧を表示"
|
||||||
|
"-a:パスワードを追加"
|
||||||
|
"-d:パスワードを削除"
|
||||||
|
"-e:パスワードを変更"
|
||||||
|
"-g:希望文字数でパスワードをランダムに作成する。risk=英数字のみ(不安)、secure=英数字+特別文字(デフォルト)を使用"
|
||||||
|
"-o:ワンタイムパスワード(TOTP)を表示。存在しなければ、創作する"
|
||||||
|
"-h:ヘルプを表示"
|
||||||
|
"-v:バージョンを表示"
|
||||||
|
)
|
||||||
|
_sp_cmd_show
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
_sp_cmd_show () {
|
_sp_cmd_show () {
|
||||||
|
@ -35,9 +55,9 @@ _sp_complete_entries() {
|
||||||
|
|
||||||
_sp_complete_entries_helper () {
|
_sp_complete_entries_helper () {
|
||||||
local IFS=$'\n'
|
local IFS=$'\n'
|
||||||
local prefix="${SP_DIR:-$HOME/.local/share/sp}"
|
local prefix
|
||||||
entries=("${(f)$(find -L "$prefix" \( -name .git -o -name .gpg-id \) -prune -o -type f -print 2>/dev/null | sed -e "s#${prefix}/##" -e 's#\.gpg$##')}")
|
zstyle -s ":completion:${curcontext}:" prefix prefix || prefix="${SP_DIR:-$HOME/.local/share/sp}"
|
||||||
_describe 'password entries' entries
|
_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 () {
|
_sp_complete_keys () {
|
||||||
|
@ -45,4 +65,4 @@ _sp_complete_keys () {
|
||||||
_values 'gpg keys' $(gpg2 --list-secret-keys --with-colons | cut -d : -f 10 | sort -u | sed '/^$/d')
|
_values 'gpg keys' $(gpg2 --list-secret-keys --with-colons | cut -d : -f 10 | sort -u | sed '/^$/d')
|
||||||
}
|
}
|
||||||
|
|
||||||
compdef _sp sp
|
_sp
|
||||||
|
|
|
@ -0,0 +1,172 @@
|
||||||
|
.TH SP 1 VERSION
|
||||||
|
.SH NAME
|
||||||
|
sp - Simple Password Manager
|
||||||
|
.br
|
||||||
|
.B sp
|
||||||
|
[-i] [-i \fI\,gpg-id\fR] [-adeosy \fI\,password\fR] [-g \fI\,length\fR [\fI\,secure\fR|\fI\,risk\fR]]
|
||||||
|
.SH 説明
|
||||||
|
.PP
|
||||||
|
シンプルなパスワードマネージャー。
|
||||||
|
.TP
|
||||||
|
\fB\,a\fR \fI\,password\fR
|
||||||
|
パスワードを追加
|
||||||
|
.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\,s\fR \fI\,password\fR
|
||||||
|
パスワードを表示
|
||||||
|
.TP
|
||||||
|
\fB\,v\fR
|
||||||
|
バージョンを表示
|
||||||
|
.TP
|
||||||
|
\fB\,y\fR \fI\,password\fR
|
||||||
|
パスワードを表示せずクリップボードにコピーする
|
||||||
|
.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
|
||||||
|
テクニカル諏訪子
|
|
@ -0,0 +1,301 @@
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addpass(char *file) {
|
||||||
|
char *lang = getlang();
|
||||||
|
|
||||||
|
// パスを準備
|
||||||
|
char *homedir = getenv("HOME");
|
||||||
|
if (homedir == NULL) {
|
||||||
|
if (strncmp(lang, "en", 2) == 0)
|
||||||
|
perror("Failed to retrieving home directory");
|
||||||
|
else perror("ホームディレクトリを受取に失敗");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *basedir = "/.local/share/sp/";
|
||||||
|
char *ext = ".gpg";
|
||||||
|
|
||||||
|
char pass[256];
|
||||||
|
char knin[256];
|
||||||
|
|
||||||
|
int alllen = snprintf(NULL, 0, "%s%s%s%s", homedir, basedir, file, ext) + 1;
|
||||||
|
char *gpgpathchk = malloc(alllen);
|
||||||
|
if (gpgpathchk == NULL) {
|
||||||
|
if (strncmp(lang, "en", 2) == 0)
|
||||||
|
perror("Failed to allocating memory");
|
||||||
|
else perror("メモリを割当に失敗");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ファイルが既に存在するかどうか確認
|
||||||
|
snprintf(gpgpathchk, alllen, "%s%s%s%s", homedir, basedir, file, ext);
|
||||||
|
|
||||||
|
if (access(gpgpathchk, F_OK) != -1) {
|
||||||
|
if (strncmp(lang, "en", 2) == 0)
|
||||||
|
fprintf(
|
||||||
|
stderr,
|
||||||
|
"Password already exist.\nFor edit, please run ' sp -e %s '.\n",
|
||||||
|
file
|
||||||
|
);
|
||||||
|
else
|
||||||
|
fprintf(
|
||||||
|
stderr,
|
||||||
|
"%s\n変更するには、「 sp -e %s 」を実行して下さい。\n",
|
||||||
|
"パスワードが既に存在しています。",
|
||||||
|
file
|
||||||
|
);
|
||||||
|
free(gpgpathchk);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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) {
|
||||||
|
if (strncmp(lang, "en", 2) == 0)
|
||||||
|
perror("Password does not match. Ending...");
|
||||||
|
else perror("パスワードが一致していません。終了…");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// パスワードを保存する
|
||||||
|
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) {
|
||||||
|
if (strncmp(lang, "en", 2) == 0)
|
||||||
|
fprintf(stderr, "Failed to generating GPGME: %s\n", gpgme_strerror(err));
|
||||||
|
else fprintf(stderr, "GPGMEを創作に失敗:%s\n", gpgme_strerror(err));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GPGMEは非対話的モードに設定
|
||||||
|
err = gpgme_set_pinentry_mode(ctx, GPGME_PINENTRY_MODE_LOOPBACK);
|
||||||
|
if (err) {
|
||||||
|
if (strncmp(lang, "en", 2) == 0)
|
||||||
|
fprintf(stderr, "Failed to setting pinentry mode: %s\n", gpgme_strerror(err));
|
||||||
|
else fprintf(stderr, "pinentryモードを設定に失敗: %s\n", gpgme_strerror(err));
|
||||||
|
gpgme_release(ctx);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// パスワードからデータオブジェクトを創作
|
||||||
|
err = gpgme_data_new_from_mem(&in, pass, strlen(pass), 0);
|
||||||
|
if (err) {
|
||||||
|
if (strncmp(lang, "en", 2) == 0)
|
||||||
|
fprintf(stderr, "Failed to making data object: %s\n", gpgme_strerror(err));
|
||||||
|
else
|
||||||
|
fprintf(stderr, "データオブジェクトを創作に失敗: %s\n", gpgme_strerror(err));
|
||||||
|
gpgme_release(ctx);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gpgme_data_new(&out);
|
||||||
|
|
||||||
|
// 鍵を受け取る
|
||||||
|
char keypath[256];
|
||||||
|
snprintf(keypath, sizeof(keypath), "%s%s%s", homedir, basedir, ".gpg-id");
|
||||||
|
|
||||||
|
keypath[sizeof(keypath) - 1] = '\0';
|
||||||
|
|
||||||
|
FILE* keyfile = fopen(keypath, "rb");
|
||||||
|
if (keyfile == NULL) {
|
||||||
|
if (strncmp(lang, "en", 2) == 0) {
|
||||||
|
perror("Failed to opening .gpg-id file");
|
||||||
|
fprintf(stderr, "Failed path: %s\n", keypath);
|
||||||
|
} else {
|
||||||
|
perror(".gpg-idファイルを開くに失敗");
|
||||||
|
fprintf(stderr, "失敗したパス: %s\n", keypath);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *keyid = malloc(256);
|
||||||
|
if (!fgets(keyid, 256, keyfile)) {
|
||||||
|
if (strncmp(lang, "en", 2) == 0)
|
||||||
|
perror("Failed to reading key ID");
|
||||||
|
else perror("鍵IDを読込に失敗");
|
||||||
|
fclose(keyfile);
|
||||||
|
free(keyid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
keyid[strcspn(keyid, "\n")] = 0;
|
||||||
|
fclose(keyfile);
|
||||||
|
|
||||||
|
err = gpgme_get_key(ctx, keyid, &key[0], 0);
|
||||||
|
if (err) {
|
||||||
|
if (strncmp(lang, "en", 2) == 0)
|
||||||
|
fprintf(stderr, "Failed to getting key: %s\n", gpgme_strerror(err));
|
||||||
|
else fprintf(stderr, "鍵を受取に失敗: %s\n", gpgme_strerror(err));
|
||||||
|
free(keyid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key[0] == NULL) {
|
||||||
|
if (strncmp(lang, "en", 2) == 0) perror("Error: Key is NULL");
|
||||||
|
else perror("エラー:鍵はNULLです");
|
||||||
|
free(keyid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(keyid);
|
||||||
|
|
||||||
|
// 暗号化
|
||||||
|
err = gpgme_op_encrypt(ctx, &key[0], GPGME_ENCRYPT_ALWAYS_TRUST, in, out);
|
||||||
|
if (err) {
|
||||||
|
if (strncmp(lang, "en", 2) == 0)
|
||||||
|
fprintf(stderr, "Failed to encrypt: %s\n", gpgme_strerror(err));
|
||||||
|
else fprintf(stderr, "暗号化に失敗: %s\n", gpgme_strerror(err));
|
||||||
|
cleanup(ctx, key[0], in, out);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 暗号化したタイルを開く
|
||||||
|
char *gpgpath = malloc(alllen);
|
||||||
|
if (gpgpath == NULL) {
|
||||||
|
cleanup(ctx, key[0], in, out);
|
||||||
|
if (strncmp(lang, "en", 2) == 0)
|
||||||
|
perror("Failed to allocating memory");
|
||||||
|
else perror("メモリを割当に失敗");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ディレクトリを創作
|
||||||
|
char dirpath[512];
|
||||||
|
snprintf(dirpath, sizeof(dirpath), "%s%s%s", homedir, basedir, file);
|
||||||
|
|
||||||
|
dirpath[sizeof(dirpath) - 1] = '\0';
|
||||||
|
|
||||||
|
char *lastsla = strrchr(dirpath, '/');
|
||||||
|
if (lastsla != NULL) {
|
||||||
|
*lastsla = '\0';
|
||||||
|
if (mkdir_r(dirpath, 0755) != 0) {
|
||||||
|
free(gpgpath);
|
||||||
|
cleanup(ctx, key[0], in, out);
|
||||||
|
if (strncmp(lang, "en", 2) == 0)
|
||||||
|
perror("Failed to constructing directory");
|
||||||
|
else perror("ディレクトリを創作に失敗");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(gpgpath, alllen, "%s%s%s%s", homedir, basedir, file, ext);
|
||||||
|
|
||||||
|
struct stat statbuf;
|
||||||
|
if (stat(gpgpath, &statbuf) == 0) {
|
||||||
|
free(gpgpath);
|
||||||
|
cleanup(ctx, key[0], in, out);
|
||||||
|
if (strncmp(lang, "en", 2) == 0)
|
||||||
|
perror("Password is already exist");
|
||||||
|
else perror("パスワードは既に存在しています");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gpgfile = fopen(gpgpath, "wb");
|
||||||
|
if (gpgfile == NULL) {
|
||||||
|
if (strncmp(lang, "en", 2) == 0) {
|
||||||
|
perror("Failed to opening file.");
|
||||||
|
fprintf(stderr, "Failed path: %s\n", gpgpath);
|
||||||
|
} else {
|
||||||
|
perror("ファイルを開くに失敗。");
|
||||||
|
fprintf(stderr, "失敗したパス: %s\n", gpgpath);
|
||||||
|
}
|
||||||
|
free(gpgpath);
|
||||||
|
cleanup(ctx, key[0], in, out);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// データが保存したかどうか確認
|
||||||
|
ssize_t encrypted_data_size = gpgme_data_seek(out, 0, SEEK_END);
|
||||||
|
if (encrypted_data_size <= 0) {
|
||||||
|
if (strncmp(lang, "en", 2) == 0)
|
||||||
|
perror("Failed to saving the data");
|
||||||
|
else perror("データを保存に失敗");
|
||||||
|
fclose(gpgfile);
|
||||||
|
free(gpgpath);
|
||||||
|
cleanup(ctx, key[0], in, out);
|
||||||
|
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 (fwrite(buffer, 1, (size_t)read_bytes, gpgfile) != (size_t)read_bytes) {
|
||||||
|
if (strncmp(lang, "en", 2) == 0)
|
||||||
|
perror("Failed to writing password");
|
||||||
|
else perror("パスワードを書き込みに失敗");
|
||||||
|
free(gpgpath);
|
||||||
|
cleanup(ctx, key[0], in, out);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 掃除
|
||||||
|
fclose(gpgfile);
|
||||||
|
free(gpgpath);
|
||||||
|
cleanup(ctx, key[0], in, out);
|
||||||
|
|
||||||
|
if (strncmp(lang, "en", 2) == 0)
|
||||||
|
puts("I could save the password");
|
||||||
|
else puts("パスワードを保存出来ました");
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
#ifndef ADDPASS_H
|
||||||
|
#define ADDPASS_H
|
||||||
|
|
||||||
|
#include <locale.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <termios.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
void addpass(char *file);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,44 @@
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char *base32_decode(const char *encoded, size_t *out_len) {
|
||||||
|
size_t encoded_len = strlen(encoded);
|
||||||
|
size_t padding = 0;
|
||||||
|
for (int i = encoded_len - 1; i >= 0 && encoded[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[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;
|
||||||
|
}
|
|
@ -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
|
|
@ -0,0 +1,37 @@
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
#ifndef COMMON_H
|
||||||
|
#define COMMON_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include <gpgme.h>
|
||||||
|
|
||||||
|
char *getlang();
|
||||||
|
int mkdir_r(const char *path, mode_t mode);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,180 @@
|
||||||
|
#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 *homedir = getenv("HOME");
|
||||||
|
if (homedir == NULL) {
|
||||||
|
if (strncmp(lang, "en", 2) == 0)
|
||||||
|
perror("Failed to getting home directory");
|
||||||
|
else perror("ホームディレクトリを受取に失敗");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *basedir = "/.local/share/sp/";
|
||||||
|
char *ext = ".gpg";
|
||||||
|
|
||||||
|
int alllen = snprintf(NULL, 0, "%s%s%s%s", homedir, basedir, file, ext) + 1;
|
||||||
|
char *gpgpathchk = malloc(alllen);
|
||||||
|
if (gpgpathchk == NULL) {
|
||||||
|
if (strncmp(lang, "en", 2) == 0)
|
||||||
|
perror("Failed to allocating memory");
|
||||||
|
else perror("メモリを割当に失敗");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ファイルが既に存在するかどうか確認
|
||||||
|
snprintf(gpgpathchk, alllen, "%s%s%s%s", homedir, 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%s",
|
||||||
|
homedir,
|
||||||
|
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("Is it really good if I 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 delete");
|
||||||
|
else perror("パスワードを削除出来ませんですた");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 空のディレクトリの場合
|
||||||
|
int numt;
|
||||||
|
char **tokens = explode(file, '/', &numt);
|
||||||
|
|
||||||
|
char basepath[1024];
|
||||||
|
char passpath[1024];
|
||||||
|
snprintf(basepath, sizeof(basepath), "%s%s", homedir, basedir);
|
||||||
|
snprintf(passpath, sizeof(passpath), "%s%s%s", homedir, basedir, tokens[0]);
|
||||||
|
char *ls = strrchr(basepath, '/');
|
||||||
|
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, basepath, 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("Deleted password");
|
||||||
|
else puts("パスワードを削除しました");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
#ifndef DELPASS_H
|
#ifndef DELPASS_H
|
||||||
#define DELPASS_H
|
#define DELPASS_H
|
||||||
|
|
||||||
int delpass(char* file);
|
int delpass(char *file, int force);
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -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 opening /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);
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
#include "common.h"
|
||||||
|
#include "initpass.h"
|
||||||
|
|
||||||
|
void initpass(char *gpgid) {
|
||||||
|
char *lang = getlang();
|
||||||
|
|
||||||
|
char *homedir = getenv("HOME");
|
||||||
|
if (homedir == NULL) {
|
||||||
|
if (strncmp(lang, "en", 2) == 0)
|
||||||
|
perror("Failed to getting home directory.");
|
||||||
|
else perror("ホームディレクトリを受取に失敗。");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *basedir = "/.local/share/sp/";
|
||||||
|
char dirpath[256];
|
||||||
|
snprintf(dirpath, sizeof(dirpath), "%s%s", homedir, basedir);
|
||||||
|
|
||||||
|
if (mkdir_r(dirpath, 0755) != 0 && errno != EEXIST) {
|
||||||
|
if (strncmp(lang, "en", 2) == 0)
|
||||||
|
perror("Failed to creating directory.");
|
||||||
|
else perror("ディレクトリを作成に失敗。");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char gpgidpath[512];
|
||||||
|
snprintf(gpgidpath, sizeof(gpgidpath), "%s/.gpg-id", dirpath);
|
||||||
|
|
||||||
|
struct stat statbuf;
|
||||||
|
if (stat(gpgidpath, &statbuf) == 0) {
|
||||||
|
if (strncmp(lang, "en", 2) == 0)
|
||||||
|
perror(".gpg-id file is already exist.");
|
||||||
|
else perror(".gpg-idファイルは既に存在します。");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *gpgidfile = fopen(gpgidpath, "w");
|
||||||
|
if (gpgidfile == NULL) {
|
||||||
|
if (strncmp(lang, "en", 2) == 0)
|
||||||
|
perror("Failed to writing .gpg-id file.");
|
||||||
|
else perror(".gpg-idファイルを書き込めません。");
|
||||||
|
fclose(gpgidfile);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fputs(gpgid, gpgidfile) == EOF) {
|
||||||
|
if (strncmp(lang, "en", 2) == 0)
|
||||||
|
perror("Failed to writing .gpg-id file.");
|
||||||
|
else perror(".gpg-idファイルへの書き込みに失敗しました。");
|
||||||
|
fclose(gpgidfile);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(gpgidfile);
|
||||||
|
if (strncmp(lang, "en", 2) == 0)
|
||||||
|
puts("First time setup was complete.");
|
||||||
|
else puts("初期設定に完了しました。");
|
||||||
|
}
|
|
@ -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 opening 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 getting lenth");
|
||||||
|
else perror("エラー:パスが長すぎる、又は長さを受取に失敗");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct stat statbuf;
|
||||||
|
if (stat(path, &statbuf) == -1) {
|
||||||
|
if (strncmp(lang, "en", 2) == 0)
|
||||||
|
perror("Failed to reading 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);
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
#ifndef LISTPASS_H
|
#ifndef LISTPASS_H
|
||||||
#define LISTPASS_H
|
#define LISTPASS_H
|
||||||
|
|
||||||
void listpass(char* basePath, int level);
|
void listpass(char *basePath, int level);
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -0,0 +1,159 @@
|
||||||
|
#include <openssl/hmac.h>
|
||||||
|
#include <openssl/sha.h>
|
||||||
|
|
||||||
|
#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("In the middle of the OTPAuth URL, could not found secret");
|
||||||
|
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("Illegal 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 allocating 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 decrypting of the BASE32");
|
||||||
|
else perror("BASE32の復号化に失敗");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return secret_decoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
char *lang = getlang();
|
||||||
|
|
||||||
|
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 generating the GPG");
|
||||||
|
else perror("GPGMEを創作に失敗");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
err = gpgme_data_new_from_file(&in, file, 1);
|
||||||
|
if (err) {
|
||||||
|
if (strncmp(lang, "en", 2) == 0)
|
||||||
|
perror("Failed to reading the GPG file");
|
||||||
|
else perror("GPGファイルを読込に失敗");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
err = gpgme_data_new(&out);
|
||||||
|
if (err) {
|
||||||
|
if (strncmp(lang, "en", 2) == 0)
|
||||||
|
perror("Failed to reading the GPG data");
|
||||||
|
else perror("GPGデータを読込に失敗");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
err = gpgme_op_decrypt(ctx, in, out);
|
||||||
|
if (err) {
|
||||||
|
if (strncmp(lang, "en", 2) == 0)
|
||||||
|
perror("Failed to decrypting the GPG");
|
||||||
|
else perror("GPGを復号化に失敗");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *secret = gpgme_data_release_and_get_mem(out, &secret_len);
|
||||||
|
if (!secret) {
|
||||||
|
if (strncmp(lang, "en", 2) == 0)
|
||||||
|
perror("Failed to getting the GPG");
|
||||||
|
else perror("GPGを受取に失敗");
|
||||||
|
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 decoding or exporting secret");
|
||||||
|
else perror("シークレットの抽出又はデコードに失敗しました");
|
||||||
|
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);
|
||||||
|
|
||||||
|
printf("%06d\n", otp);
|
||||||
|
}
|
|
@ -0,0 +1,137 @@
|
||||||
|
#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;
|
||||||
|
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 generating 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);
|
||||||
|
|
||||||
|
// 暗号化したタイルを開く
|
||||||
|
char *homedir = getenv("HOME");
|
||||||
|
if (homedir == NULL) {
|
||||||
|
if (strncmp(lang, "en", 2) == 0)
|
||||||
|
perror("Failed to getting home directory");
|
||||||
|
else perror("ホームディレクトリを受取に失敗");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *basedir = "/.local/share/sp/";
|
||||||
|
char *ext = ".gpg";
|
||||||
|
int alllen = snprintf(NULL, 0, "%s%s%s%s", homedir, basedir, file, ext) + 1;
|
||||||
|
char *gpgpath = malloc(alllen);
|
||||||
|
if (gpgpath == NULL) {
|
||||||
|
if (strncmp(lang, "en", 2) == 0)
|
||||||
|
perror("Failed to allocating memeory");
|
||||||
|
else perror("メモリを割当に失敗");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(gpgpath, alllen, "%s%s%s%s", homedir, basedir, file, ext);
|
||||||
|
|
||||||
|
gpgfile = fopen(gpgpath, "rb");
|
||||||
|
if (gpgfile == NULL) {
|
||||||
|
if (strncmp(lang, "en", 2) == 0) {
|
||||||
|
perror("Failed to opening 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 generating 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 generating 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 decrypting: %s\n", gpgme_strerror(err));
|
||||||
|
else fprintf(stderr, "復号化に失敗: %s\n", gpgme_strerror(err));
|
||||||
|
|
||||||
|
// 掃除
|
||||||
|
clean_up(ctx, in, out, gpgfile, gpgpath);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 復号化したパスワードを表示する
|
||||||
|
gpgme_data_seek(out, 0, SEEK_SET);
|
||||||
|
char buffer[512];
|
||||||
|
char *res = malloc(512 * sizeof(char));
|
||||||
|
if (res == NULL) {
|
||||||
|
if (strncmp(lang, "en", 2) == 0)
|
||||||
|
perror("Failed to allocating memory");
|
||||||
|
else perror("メモリを役割に失敗");
|
||||||
|
clean_up(ctx, in, out, gpgfile, gpgpath);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t read_bytes;
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
while ((read_bytes = gpgme_data_read(out, buffer, sizeof(buffer) - 1)) > 0) {
|
||||||
|
memcpy(res + i, buffer, read_bytes);
|
||||||
|
i += read_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
res[i] = '\0';
|
||||||
|
if (res[i-1] == '\n') res[i-1] = '\0';
|
||||||
|
|
||||||
|
// 掃除
|
||||||
|
clean_up(ctx, in, out, gpgfile, gpgpath);
|
||||||
|
return res;
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
#ifndef SHOWPASS_H
|
#ifndef SHOWPASS_H
|
||||||
#define SHOWPASS_H
|
#define SHOWPASS_H
|
||||||
|
|
||||||
void showpass(char* file);
|
const char *showpass(char *file);
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -1,17 +1,18 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <locale.h>
|
#include <locale.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <gpgme.h>
|
#include "common.h"
|
||||||
|
|
||||||
#include "yankpass.h"
|
#include "yankpass.h"
|
||||||
#include "showpass.h"
|
#include "showpass.h"
|
||||||
|
|
||||||
void yankpass(char* file) {
|
void yankpass(char *file) {
|
||||||
|
char *lang = getlang();
|
||||||
|
|
||||||
// Xセッションではない場合(例えば、SSH、TTY、Gayland等)、showpass()を実行して
|
// Xセッションではない場合(例えば、SSH、TTY、Gayland等)、showpass()を実行して
|
||||||
if (getenv("DISPLAY") == NULL) {
|
if (getenv("DISPLAY") == NULL) {
|
||||||
printf("Xセッションではありませんので、「sp -s」を実行します。\n");
|
if (strncmp(lang, "en", 2) == 0)
|
||||||
|
puts("There is no X session, so executing 'sp -s'.");
|
||||||
|
else puts("Xセッションではありませんので、「sp -s」を実行します。");
|
||||||
showpass(file);
|
showpass(file);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -29,7 +30,9 @@ void yankpass(char* file) {
|
||||||
// GPGMEを創作
|
// GPGMEを創作
|
||||||
err = gpgme_new(&ctx);
|
err = gpgme_new(&ctx);
|
||||||
if (err) {
|
if (err) {
|
||||||
fprintf(stderr, "GPGMEを創作に失敗:%s\n", gpgme_strerror(err));
|
if (strncmp(lang, "en", 2) == 0)
|
||||||
|
fprintf(stderr, "Failed to generating GPGME: %s\n", gpgme_strerror(err));
|
||||||
|
else fprintf(stderr, "GPGMEを創作に失敗:%s\n", gpgme_strerror(err));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,7 +42,9 @@ void yankpass(char* file) {
|
||||||
// 暗号化したタイルを開く
|
// 暗号化したタイルを開く
|
||||||
char* homedir = getenv("HOME");
|
char* homedir = getenv("HOME");
|
||||||
if (homedir == NULL) {
|
if (homedir == NULL) {
|
||||||
perror("ホームディレクトリを受取に失敗。");
|
if (strncmp(lang, "en", 2) == 0)
|
||||||
|
perror("Failed to getting home directory");
|
||||||
|
else perror("ホームディレクトリを受取に失敗");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,15 +53,22 @@ void yankpass(char* file) {
|
||||||
int alllen = snprintf(NULL, 0, "%s%s%s%s", homedir, basedir, file, ext) + 1;
|
int alllen = snprintf(NULL, 0, "%s%s%s%s", homedir, basedir, file, ext) + 1;
|
||||||
char* gpgpath = malloc(alllen);
|
char* gpgpath = malloc(alllen);
|
||||||
if (gpgpath == NULL) {
|
if (gpgpath == NULL) {
|
||||||
perror("メモリを割当に失敗。");
|
if (strncmp(lang, "en", 2) == 0)
|
||||||
|
perror("Failed to allocating memory");
|
||||||
|
else perror("メモリを割当に失敗");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sprintf(gpgpath, "%s%s%s%s", homedir, basedir, file, ext);
|
snprintf(gpgpath, alllen, "%s%s%s%s", homedir, basedir, file, ext);
|
||||||
gpgfile = fopen(gpgpath, "rb");
|
gpgfile = fopen(gpgpath, "rb");
|
||||||
if (gpgfile == NULL) {
|
if (gpgfile == NULL) {
|
||||||
perror("ファイルを開くに失敗。");
|
if (strncmp(lang, "en", 2) == 0) {
|
||||||
printf("失敗したパス: %s\n", gpgpath);
|
perror("Failed to opening the file");
|
||||||
|
fprintf(stderr, "Failed path: %s\n", gpgpath);
|
||||||
|
} else {
|
||||||
|
perror("ファイルを開くに失敗");
|
||||||
|
fprintf(stderr, "失敗したパス: %s\n", gpgpath);
|
||||||
|
}
|
||||||
free(gpgpath);
|
free(gpgpath);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -68,7 +80,9 @@ void yankpass(char* file) {
|
||||||
// 復号化して
|
// 復号化して
|
||||||
err = gpgme_op_decrypt(ctx, in, out);
|
err = gpgme_op_decrypt(ctx, in, out);
|
||||||
if (err) {
|
if (err) {
|
||||||
fprintf(stderr, "復号化に失敗: %s\n", gpgme_strerror(err));
|
if (strncmp(lang, "en", 2) == 0)
|
||||||
|
fprintf(stderr, "Failed to decryption: %s\n", gpgme_strerror(err));
|
||||||
|
else fprintf(stderr, "復号化に失敗: %s\n", gpgme_strerror(err));
|
||||||
|
|
||||||
// 掃除
|
// 掃除
|
||||||
fclose(gpgfile);
|
fclose(gpgfile);
|
||||||
|
@ -89,7 +103,9 @@ void yankpass(char* file) {
|
||||||
gpgme_data_release(in);
|
gpgme_data_release(in);
|
||||||
gpgme_data_release(out);
|
gpgme_data_release(out);
|
||||||
gpgme_release(ctx);
|
gpgme_release(ctx);
|
||||||
perror("クリップボードを見つけられませんでした。");
|
if (strncmp(lang, "en", 2) == 0)
|
||||||
|
perror("Could not found a clipboard");
|
||||||
|
else perror("クリップボードを見つけられませんでした");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,7 +125,18 @@ void yankpass(char* file) {
|
||||||
pclose(pipe);
|
pclose(pipe);
|
||||||
|
|
||||||
// 45秒後、クリップボードから削除する
|
// 45秒後、クリップボードから削除する
|
||||||
printf("パスワードをクリップボードに追加しました。\n45秒後はクリップボードから取り消されます。\n");
|
if (strncmp(lang, "en", 2) == 0)
|
||||||
|
printf(
|
||||||
|
"%s\n%s\n",
|
||||||
|
"Added password to the clipboard.",
|
||||||
|
"I will take it away from the clipboard after 45 second."
|
||||||
|
);
|
||||||
|
else
|
||||||
|
printf(
|
||||||
|
"%s\n%s\n",
|
||||||
|
"パスワードをクリップボードに追加しました。",
|
||||||
|
"45秒後はクリップボードから取り消されます。"
|
||||||
|
);
|
||||||
sleep(45);
|
sleep(45);
|
||||||
system("echo -n | xclip -selection clipboard");
|
system("echo -n | xclip -selection clipboard");
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#ifndef YANKPASS_H
|
#ifndef YANKPASS_H
|
||||||
#define YANKPASS_H
|
#define YANKPASS_H
|
||||||
|
|
||||||
void yankpass(char* file);
|
void yankpass(char *file);
|
||||||
|
|
||||||
#endif
|
#endif
|
読み込み中…
新しいイシューから参照