コミットを比較

...

36 コミット

作成者 SHA1 メッセージ 日付
守矢諏訪子 8084424b16 ごめん 2024-05-25 17:45:15 +09:00
守矢諏訪子 35fef77fef ごめん 2024-05-25 17:43:43 +09:00
守矢諏訪子 f64a68ddf2 TODOっていらん 2024-05-25 17:57:41 +09:00
守矢諏訪子 eed1ed1361 やはり成功だった 2024-05-25 17:40:41 +09:00
守矢諏訪子 45dc3ac6dc Merge pull request 'パスワードがなくなるまで削除したら、ディレクトリも削除する様に' (#17) from delpass into master
Reviewed-on: #17
2024-05-25 17:30:42 +09:00
守矢諏訪子 f652e8d5d6 Merge branch 'master' of gitler.moe:suwako/sp into delpass 2024-05-25 17:15:25 +09:00
守矢諏訪子 26c0d00954 そっか、spの場合はCHANGELOG.mdを変更する事が不要 2024-05-22 18:38:52 +09:00
守矢諏訪子 af6d9c174f 使い方の修正 2024-05-22 18:38:07 +09:00
守矢諏訪子 43f463ba75 WIP: パスワードがなくなるまで削除したら、ディレクトリも削除する様に 2024-05-22 12:27:35 +09:00
守矢諏訪子 cf7147a90f segfault 2024-05-22 10:35:22 +09:00
守矢諏訪子 c1e344ac71 manpageを細かくに 2024-05-22 10:34:56 +09:00
守矢諏訪子 6a5b916d29 パスワード作成関数のデフォルトな長さは64に 2024-05-22 10:26:42 +09:00
守矢諏訪子 5f7cb70922 . 2024-05-22 03:53:43 +09:00
守矢諏訪子 69ef928b4b ヘルプの表示の削除 2024-05-22 03:27:52 +09:00
守矢諏訪子 0668578cbd ソースコードは綺麗に、OTPのバグの修正、表示のバグの修正 2024-05-22 03:13:29 +09:00
守矢諏訪子 58793174a4 uname -m 2024-04-30 21:07:41 +09:00
守矢諏訪子 aac723e78c NetBSD向けのリリースコマンドの追加 2024-04-16 18:06:04 +09:00
守矢諏訪子 284abb1fad Merge branch 'v1.3.0' of gitler.moe:suwako/sp into v1.3.0 2024-04-16 13:34:13 +09:00
守矢諏訪子 fe286d22ce 忘れた 2024-04-16 13:33:38 +09:00
守矢諏訪子 2e5e0b61b2 ごめん 2024-04-16 13:09:01 +09:00
守矢諏訪子 a5c6e3fe54 最新ルールに従い 2024-04-16 13:06:34 +09:00
守矢諏訪子 6f7eeaa45c Merge branch 'make-release-linux' of gitler.moe:suwako/sp into v1.3.0 2024-04-16 12:31:45 +09:00
守矢諏訪子 386bac6792 Linux向けのリリースコマンドの追加 2024-04-16 12:30:37 +09:00
守矢諏訪子 5003ea13bc FreeBSD向けのリリースコマンドの追加 2024-04-16 12:27:59 +09:00
守矢諏訪子 61271640e7 ごみ 2024-04-16 00:52:48 +09:00
守矢諏訪子 51c119e794 . 2024-04-16 00:48:28 +09:00
守矢諏訪子 1f997af2c1 Merge pull request 'OpenBSD向けリリースコマンドの追加' (#11) from make-release-openbsd into v1.3.0
Reviewed-on: #11
2024-04-16 00:46:38 +09:00
守矢諏訪子 417423417c Merge branch 'v1.3.0' of gitler.moe:suwako/sp into make-release-openbsd 2024-04-16 00:46:09 +09:00
守矢諏訪子 1e3ce35091 Merge pull request 'bsdmake' (#10) from bsdmake into v1.3.0
Reviewed-on: #10
2024-04-16 00:44:19 +09:00
守矢諏訪子 a29867337e OpenBSD向けリリースコマンドの追加 2024-04-16 00:43:13 +09:00
守矢諏訪子 8ea7ebd47d GPLv2 → ISC 2024-04-16 00:40:50 +09:00
守矢諏訪子 7add5ffa21 GNU Make → BSD Make 2024-04-16 00:39:52 +09:00
守矢諏訪子 0aaf5f12b2 最新ルールに従う様に 2024-02-03 19:37:46 +09:00
守矢諏訪子 297b9766dd ごめん 2024-02-03 00:30:26 +09:00
守矢諏訪子 e6defe3271 MANページを追加 2024-02-03 00:17:50 +09:00
守矢諏訪子 461b952da7 英訳の追加 2024-02-02 12:15:58 +09:00
34個のファイルの変更1198行の追加620行の削除

3
.gitignore vendored
ファイルの表示

@ -1,4 +1,5 @@
sp
.ccls-cache
*.o
*.tar.gz
release
*.core

ファイルの表示

@ -1,3 +1,18 @@
# 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を実行したら、バイナリがもっと小さくなる

ファイルの表示

@ -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.

ファイルの表示

@ -1,21 +1,31 @@
UNAME_S := $(shell uname -s)
UNAME_S != uname -s
UNAME_M != uname -m
NAME := $(shell cat main.c | grep "const char\* sofname" | awk '{print $$5}' | sed "s/\"//g" | sed "s/;//" )
VERSION := $(shell cat main.c | grep "const char\* version" | awk '{print $$5}' | sed "s/\"//g" | sed "s/;//" )
PREFIX=/usr
ifeq ($(UNAME_S),FreeBSD)
PREFIX=/usr/local
endif
ifeq ($(UNAME_S),OpenBSD)
PREFIX=/usr/local
endif
ifeq ($(UNAME_S),NetBSD)
PREFIX=/usr/pkg
endif
CC=cc
FILES=main.c showpass.c yankpass.c addpass.c delpass.c listpass.c genpass.c initpass.c otppass.c base32.c
CFLAGS=-Wall -Wextra -O3 -I${PREFIX}/include -L${PREFIX}/lib
LDFLAGS=-lgpgme -lcrypto
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
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:
${CC} ${CFLAGS} -o ${NAME} ${FILES} ${LDFLAGS}
@ -25,17 +35,43 @@ clean:
rm -f ${NAME}
dist: clean
mkdir -p ${NAME}-${VERSION}
cp -R LICENSE.txt Makefile README.md CHANGELOG.md\
sp-completion.zsh\
*.c *.h ${NAME}-${VERSION}
tar zcfv ${NAME}-${VERSION}.tar.gz ${NAME}-${VERSION}
mkdir -p release/src ${NAME}-${VERSION}
cp -R LICENSE.txt Makefile README.md CHANGELOG.md \
${NAME}-completion.zsh ${NAME}.1 main.c src ${NAME}-${VERSION}
tar zcfv release/src/${NAME}-${VERSION}.tar.gz ${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
mkdir -p ${DESTDIR}${PREFIX}/bin
cp -f ${NAME} ${DESTDIR}${PREFIX}/bin
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:
cp sp-completion.zsh ${DESTDIR}${PREFIX}/share/zsh/site-functions/_sp

ファイルの表示

@ -89,8 +89,10 @@ $ sp -d 076.moe/suwako
## TOTP(ワンタイムパスワード)
### QRコードから
QRコードをダウンロードし、zbarimgを使用して「QR-Code:」以降の部分をコピーし、spに追加して下さい。\
`sp -a`を実行すると、「パスワード」を聞かれますが、TOTPの場合は「otpauth://」から始まる文字列をコピペして下さい。
QRコードをダウンロードし、zbarimgを使用して「QR-Code:」以降の部分をコピーし、
spに追加して下さい。\
`sp -a`を実行すると、「パスワード」を聞かれますが、
TOTPの場合は「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=

ファイルの表示

@ -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

ファイルの表示

@ -1,63 +0,0 @@
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include "delpass.h"
int delpass(char* file, int force) {
// パスを準備
char pwfile[512];
char* homedir = getenv("HOME");
if (homedir == NULL) {
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) {
perror("メモリを割当に失敗。");
return -1;
}
// ファイルが既に存在するかどうか確認
snprintf(gpgpathchk, alllen, "%s%s%s%s", homedir, basedir, file, ext);
if (access(gpgpathchk, F_OK) != 0) {
fprintf(stderr, "パスワードが存在しません。\n");
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)) {
fprintf(stderr, "エラー:パスが長すぎる。\n");
return -1;
}
// 削除を確認する
if (force == 0) { // パスワードの変更のばあい、確認は不要
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;
}
if (force == 1) return 0;
printf("パスワードを削除しました。\n");
return 0;
}

ファイルの表示

@ -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);
}

ファイルの表示

@ -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("初期設定に完了しました。");
}

ファイルの表示

@ -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);
}

172
main.c
ファイルの表示

@ -1,89 +1,119 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#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 <gpgme.h>
const char *sofname = "sp";
const char *version = "1.3.0";
#include "initpass.h"
#include "showpass.h"
#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.2.0";
void helpme() {
printf(" %s %s - シンプルなパスワードマネージャー\n", sofname, version);
printf("https://076.moe/ | https://gitler.moe/suwako/%s\n\n", sofname);
puts ("使い方:\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 -e <パスワード名> :パスワードを変更\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);
void usage() {
printf("%s-%s\nusage: %s [-adegilosvy]\n", sofname, version, 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) {
helpme();
usage();
return 0;
}
if (argc == 3 && strcmp(argv[1], "-i") == 0) initpass(argv[2]);
else if (argc == 3 && strcmp(argv[1], "-s") == 0) showpass(argv[2]);
else if (argc == 3 && strcmp(argv[1], "-y") == 0) yankpass(argv[2]);
else if (argc == 2 && strcmp(argv[1], "-l") == 0) {
char basePath[512];
char* homedir = getenv("HOME");
if (homedir == NULL) {
perror("ホームディレクトリを受取に失敗。");
return -1;
if (strcmp(argv[1], "-g") == 0) {
if (argc > 4) {
usage();
return 1;
}
char* basedir = "/.local/share/sp/";
snprintf(basePath, sizeof(basePath), "%s%s", homedir, basedir);
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);
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], 0);
else if (argc == 3 && strcmp(argv[1], "-e") == 0) {
delpass(argv[2], 1);
addpass(argv[2]);
}
else if (strcmp(argv[1], "-g") == 0) {
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);
else helpme();
}
else if (argc == 3 && strcmp(argv[1], "-o") == 0) {
char fullPath[512];
char* homedir = getenv("HOME");
if (homedir == NULL) {
perror("ホームディレクトリを受取に失敗。");
return -1;
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]);
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;
char* basedir = "/.local/share/sp/";
snprintf(fullPath, sizeof(fullPath), "%s%s%s.gpg", homedir, basedir, argv[2]);
otppass(fullPath);
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 == 2 && strcmp(argv[1], "-v") == 0) printf("%s-%s\n", sofname, version);
else helpme();
return 0;
}

123
otppass.c
ファイルの表示

@ -1,123 +0,0 @@
#include <stdio.h>
#include <openssl/hmac.h>
#include <openssl/sha.h>
#include <gpgme.h>
#include "base32.h"
#include "otppass.h"
unsigned char* extract_secret(const char* otpauth_url, size_t* decoded_len) {
const char* secret_start = strstr(otpauth_url, "secret=");
if (!secret_start) {
fprintf(stderr, "OTPAuth URLの中に、シークレットを見つけられませんでした。\n");
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) {
fprintf(stderr, "不正なシークレットの距離。\n");
return NULL;
}
size_t secret_len = secret_end - secret_start;
char* secret_encoded = (char*)malloc(secret_len + 1);
if (secret_encoded == NULL) {
fprintf(stderr, "メモリの役割に失敗。\n");
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) {
fprintf(stderr, "BASE32の復号化に失敗。\n");
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) {
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) {
fprintf(stderr, "GPGMEを創作に失敗\n");
exit(1);
}
err = gpgme_data_new_from_file(&in, file, 1);
if (err) {
fprintf(stderr, "GPGファイルを読込に失敗\n");
exit(1);
}
err = gpgme_data_new(&out);
if (err) {
fprintf(stderr, "GPGデータを読込に失敗\n");
exit(1);
}
err = gpgme_op_decrypt(ctx, in, out);
if (err) {
fprintf(stderr, "GPGを復号化に失敗\n");
exit(1);
}
char* secret = gpgme_data_release_and_get_mem(out, &secret_len);
if (!secret) {
fprintf(stderr, "GPGを受取に失敗\n");
exit(1);
}
size_t decoded_len;
unsigned char* secret_decoded = extract_secret(secret, &decoded_len);
if (!secret_decoded) {
fprintf(stderr, "シークレットの抽出またはデコードに失敗しました\n");
free(secret);
exit(1);
}
time_t current_time = time(NULL);
uint64_t counter = current_time / 30;
uint32_t otp = generate_totp((const char*)secret_decoded, counter);
printf("%06d\n", otp);
gpgme_data_release(in);
gpgme_release(ctx);
free(secret);
free(secret_decoded);
}

ファイルの表示

@ -1,110 +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 = 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) {
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;
}
snprintf(gpgpath, alllen, "%s%s%s%s", homedir, basedir, file, ext);
gpgfile = fopen(gpgpath, "rb");
if (gpgfile == NULL) {
perror("ファイルを開くに失敗。");
printf("失敗したパス: %s\n", gpgpath);
free(gpgpath);
return;
}
// ファイルからinデータオブジェクトを創作
if (gpgme_data_new_from_stream(&in, gpgfile) != GPG_ERR_NO_ERROR) {
fprintf(stderr, "GPGMEデータオブジェクトを創作に失敗。\n");
clean_up(ctx, in, out, gpgfile, gpgpath);
return;
}
// outデータオブジェクトを創作
if (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);
}

172
sp.1 ノーマルファイル
ファイルの表示

@ -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
テクニカル諏訪子

ファイルの表示

@ -1,14 +1,3 @@
#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) {
@ -18,33 +7,7 @@ void cleanup(gpgme_ctx_t ctx, gpgme_key_t key, gpgme_data_t in, gpgme_data_t out
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) {
void getpasswd(char *prompt, char *pw, size_t pwlen) {
struct termios old, new;
printf("%s", prompt);
@ -73,45 +36,70 @@ void getpasswd(char* prompt, char*pw, size_t pwlen) {
tcsetattr(fileno(stdin), TCSANOW, &old);
}
void addpass(char* file) {
void addpass(char *file) {
char *lang = getlang();
// パスを準備
char* homedir = getenv("HOME");
char *homedir = getenv("HOME");
if (homedir == NULL) {
perror("ホームディレクトリを受取に失敗。");
if (strncmp(lang, "en", 2) == 0)
perror("Failed to retrieving home directory");
else perror("ホームディレクトリを受取に失敗");
return;
}
char* basedir = "/.local/share/sp/";
char* ext = ".gpg";
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);
char *gpgpathchk = malloc(alllen);
if (gpgpathchk == NULL) {
perror("メモリを割当に失敗。");
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) {
fprintf(stderr, "パスワードが既に存在しています。\n変更するには、「 sp -e %s 」を実行して下さい。\n", file);
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);
// パスワードを受け取る
getpasswd("パスワード: ", pass, sizeof(pass));
if (strncmp(lang, "en", 2) == 0)
getpasswd("Password: ", pass, sizeof(pass));
else getpasswd("パスワード: ", pass, sizeof(pass));
puts("");
getpasswd("パスワード(確認用): ", knin, sizeof(knin));
if (strncmp(lang, "en", 2) == 0)
getpasswd("Password (for verification): ", knin, sizeof(knin));
else getpasswd("パスワード(確認用): ", knin, sizeof(knin));
puts("");
// パスワードが一致するかどうか確認
if (strcmp(pass, knin) != 0) {
fprintf(stderr, "パスワードが一致していません。終了…\n");
if (strncmp(lang, "en", 2) == 0)
perror("Password does not match. Ending...");
else perror("パスワードが一致していません。終了…");
return;
}
@ -130,14 +118,18 @@ void addpass(char* file) {
// GPGMEを創作
err = gpgme_new(&ctx);
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;
}
// GPGMEは非対話的モードに設定
err = gpgme_set_pinentry_mode(ctx, GPGME_PINENTRY_MODE_LOOPBACK);
if (err) {
fprintf(stderr, "pinentryモードを設定に失敗 %s\n", gpgme_strerror(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;
}
@ -145,7 +137,10 @@ void addpass(char* file) {
// パスワードからデータオブジェクトを創作
err = gpgme_data_new_from_mem(&in, pass, strlen(pass), 0);
if (err) {
fprintf(stderr, "データオブジェクトを創作に失敗: %s\n", gpgme_strerror(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;
}
@ -155,18 +150,26 @@ void addpass(char* file) {
// 鍵を受け取る
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);
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);
char *keyid = malloc(256);
if (!fgets(keyid, 256, keyfile)) {
perror("鍵IDを読込に失敗。");
if (strncmp(lang, "en", 2) == 0)
perror("Failed to reading key ID");
else perror("鍵IDを読込に失敗");
fclose(keyfile);
free(keyid);
return;
@ -177,13 +180,16 @@ void addpass(char* file) {
err = gpgme_get_key(ctx, keyid, &key[0], 0);
if (err) {
fprintf(stderr, "鍵を受取に失敗: %s\n", gpgme_strerror(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) {
fprintf(stderr, "エラー鍵はNULLです。\n");
if (strncmp(lang, "en", 2) == 0) perror("Error: Key is NULL");
else perror("エラー鍵はNULLです");
free(keyid);
return;
}
@ -193,48 +199,63 @@ void addpass(char* file) {
// 暗号化
err = gpgme_op_encrypt(ctx, &key[0], GPGME_ENCRYPT_ALWAYS_TRUST, in, out);
if (err) {
fprintf(stderr, "暗号化に失敗: %s\n", gpgme_strerror(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);
char *gpgpath = malloc(alllen);
if (gpgpath == NULL) {
cleanup(ctx, key[0], in, out);
perror("メモリを割当に失敗。");
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, '/');
char *lastsla = strrchr(dirpath, '/');
if (lastsla != NULL) {
*lastsla = '\0';
if (mkdir_r(dirpath, 0755) != 0) {
free(gpgpath);
cleanup(ctx, key[0], in, out);
perror("ディレクトリを創作に失敗。");
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);
fprintf(stderr, "パスワードは既に存在しています。\n");
if (strncmp(lang, "en", 2) == 0)
perror("Password is already exist");
else perror("パスワードは既に存在しています");
return;
}
gpgfile = fopen(gpgpath, "wb");
if (gpgfile == NULL) {
perror("ファイルを開くに失敗。");
printf("失敗したパス: %s\n", gpgpath);
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;
@ -243,7 +264,9 @@ void addpass(char* file) {
// データが保存したかどうか確認
ssize_t encrypted_data_size = gpgme_data_seek(out, 0, SEEK_END);
if (encrypted_data_size <= 0) {
fprintf(stderr, "データを保存に失敗。\n");
if (strncmp(lang, "en", 2) == 0)
perror("Failed to saving the data");
else perror("データを保存に失敗");
fclose(gpgfile);
free(gpgpath);
cleanup(ctx, key[0], in, out);
@ -258,7 +281,9 @@ void addpass(char* file) {
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("パスワードを書き込みに失敗");
if (strncmp(lang, "en", 2) == 0)
perror("Failed to writing password");
else perror("パスワードを書き込みに失敗");
free(gpgpath);
cleanup(ctx, key[0], in, out);
return;
@ -270,5 +295,7 @@ void addpass(char* file) {
free(gpgpath);
cleanup(ctx, key[0], in, out);
printf("パスワードを保存出来ました。\n");
if (strncmp(lang, "en", 2) == 0)
puts("I could save the password");
else puts("パスワードを保存出来ました");
}

13
src/addpass.h ノーマルファイル
ファイルの表示

@ -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

ファイルの表示

@ -1,3 +1,5 @@
#include <string.h>
#include "base32.h"
int char_to_val(char c) {

ファイルの表示

@ -2,7 +2,6 @@
#define BASE32_H
#include <stdlib.h>
#include <string.h>
unsigned char *base32_decode(const char *encoded, size_t *out_len);

37
src/common.c ノーマルファイル
ファイルの表示

@ -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;
}

16
src/common.h ノーマルファイル
ファイルの表示

@ -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

180
src/delpass.c ノーマルファイル
ファイルの表示

@ -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
#define DELPASS_H
int delpass(char* file, int force);
int delpass(char *file, int force);
#endif

32
src/genpass.c ノーマルファイル
ファイルの表示

@ -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);
}

ファイルの表示

58
src/initpass.c ノーマルファイル
ファイルの表示

@ -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("初期設定に完了しました。");
}

ファイルの表示

56
src/listpass.c ノーマルファイル
ファイルの表示

@ -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
#define LISTPASS_H
void listpass(char* basePath, int level);
void listpass(char *basePath, int level);
#endif

159
src/otppass.c ノーマルファイル
ファイルの表示

@ -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);
}

ファイルの表示

137
src/showpass.c ノーマルファイル
ファイルの表示

@ -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
#define SHOWPASS_H
void showpass(char* file);
const char *showpass(char *file);
#endif

ファイルの表示

@ -1,17 +1,18 @@
#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
#include <unistd.h>
#include <gpgme.h>
#include "common.h"
#include "yankpass.h"
#include "showpass.h"
void yankpass(char* file) {
void yankpass(char *file) {
char *lang = getlang();
// Xセッションではない場合(例えば、SSH、TTY、Gayland等)、showpass()を実行して
if (getenv("DISPLAY") == NULL) {
printf("Xセッションではありませんので、「sp -s」を実行します。\n");
if (getenv("DISPLAY") == NULL) {
if (strncmp(lang, "en", 2) == 0)
puts("There is no X session, so executing 'sp -s'.");
else puts("Xセッションではありませんので、「sp -s」を実行します。");
showpass(file);
return;
}
@ -29,7 +30,9 @@ void yankpass(char* file) {
// GPGMEを創作
err = gpgme_new(&ctx);
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;
}
@ -39,7 +42,9 @@ void yankpass(char* file) {
// 暗号化したタイルを開く
char* homedir = getenv("HOME");
if (homedir == NULL) {
perror("ホームディレクトリを受取に失敗。");
if (strncmp(lang, "en", 2) == 0)
perror("Failed to getting home directory");
else perror("ホームディレクトリを受取に失敗");
return;
}
@ -48,15 +53,22 @@ void yankpass(char* file) {
int alllen = snprintf(NULL, 0, "%s%s%s%s", homedir, basedir, file, ext) + 1;
char* gpgpath = malloc(alllen);
if (gpgpath == NULL) {
perror("メモリを割当に失敗。");
if (strncmp(lang, "en", 2) == 0)
perror("Failed to allocating memory");
else perror("メモリを割当に失敗");
return;
}
snprintf(gpgpath, alllen, "%s%s%s%s", homedir, basedir, file, ext);
gpgfile = fopen(gpgpath, "rb");
if (gpgfile == NULL) {
perror("ファイルを開くに失敗。");
printf("失敗したパス: %s\n", gpgpath);
if (strncmp(lang, "en", 2) == 0) {
perror("Failed to opening the file");
fprintf(stderr, "Failed path: %s\n", gpgpath);
} else {
perror("ファイルを開くに失敗");
fprintf(stderr, "失敗したパス: %s\n", gpgpath);
}
free(gpgpath);
return;
}
@ -68,7 +80,9 @@ void yankpass(char* file) {
// 復号化して
err = gpgme_op_decrypt(ctx, in, out);
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);
@ -89,7 +103,9 @@ void yankpass(char* file) {
gpgme_data_release(in);
gpgme_data_release(out);
gpgme_release(ctx);
perror("クリップボードを見つけられませんでした。");
if (strncmp(lang, "en", 2) == 0)
perror("Could not found a clipboard");
else perror("クリップボードを見つけられませんでした");
return;
}
@ -109,7 +125,18 @@ void yankpass(char* file) {
pclose(pipe);
// 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);
system("echo -n | xclip -selection clipboard");

ファイルの表示

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