From b5e050afa647f0d00c4e9e74e7cdb6fac433e764 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AB=8F=E8=A8=AA=E5=AD=90?= Date: Wed, 29 Nov 2023 21:19:53 +0900 Subject: [PATCH] =?UTF-8?q?=E6=9C=80=E5=88=9D=E3=82=B3=E3=83=9F=E3=83=83?= =?UTF-8?q?=E3=83=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 + Makefile | 24 +++++ addpass.c | 250 +++++++++++++++++++++++++++++++++++++++++++++++++++++ addpass.h | 6 ++ main.c | 52 +++++++++++ showpass.c | 90 +++++++++++++++++++ showpass.h | 6 ++ yankpass.c | 116 +++++++++++++++++++++++++ yankpass.h | 6 ++ 9 files changed, 553 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 addpass.c create mode 100644 addpass.h create mode 100644 main.c create mode 100644 showpass.c create mode 100644 showpass.h create mode 100644 yankpass.c create mode 100644 yankpass.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..324e468 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +sp +.ccls-cache +*.o diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1817151 --- /dev/null +++ b/Makefile @@ -0,0 +1,24 @@ +NAME=sp +VERSION=0.0.1 +# Linux、Haiku、かIllumos = /usr、FreeBSDかOpenBSD = /usr/local、NetBSD = /usr/pkg +PREFIX=/usr +CC=cc +FILES=main.c showpass.c yankpass.c addpass.c +CFLAGS=-Wall -Wextra -g +LDFLAGS=-lgpgme + +all: + ${CC} ${CFLAGS} -o ${NAME} ${FILES} ${LDFLAGS} + +clean: + rm -f ${NAME} + +install: all + mkdir -p ${DESTDIR}${PREFIX}/bin + cp -f ${NAME} ${DESTDIR}${PREFIX}/bin + chmod 755 ${DESTDIR}${PREFIX}/bin/${NAME} + +uninstall: + rm -f ${DESTDIR}${PREFIX}/bin/${NAME} + +.PHONY: all clean install uninstall diff --git a/addpass.c b/addpass.c new file mode 100644 index 0000000..d4ac8f9 --- /dev/null +++ b/addpass.c @@ -0,0 +1,250 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "addpass.h" + +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); + + // パスワードを読み込む + fgets(pw, pwlen, stdin); + + // 端末設定をもとに戻す + tcsetattr(fileno(stdin), TCSANOW, &old); + + // 改行文字を取り消す + pw[strcspn(pw, "\n")] = 0; +} + +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; + 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 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); + if (err) { + fprintf(stderr, "鍵を受取に失敗: %s\n", gpgme_strerror(err)); + free(keyid); + return; + } + + if (key == NULL) { + fprintf(stderr, "エラー:鍵はNULLです。\n"); + free(keyid); + return; + } + + free(keyid); + + // 暗号化 + gpgme_key_t recp[1] = {key}; + err = gpgme_op_encrypt(ctx, recp, GPGME_ENCRYPT_ALWAYS_TRUST, in, out); + if (err) { + fprintf(stderr, "暗号化に失敗: %s\n", gpgme_strerror(err)); + gpgme_data_release(in); + gpgme_data_release(out); + gpgme_release(ctx); + gpgme_key_release(key); + return; + } + + // 暗号化したタイルを開く + char* ext = ".gpg"; + int alllen = snprintf(NULL, 0, "%s%s%s%s", homedir, basedir, file, ext) + 1; + char* gpgpath = malloc(alllen); + if (gpgpath == NULL) { + gpgme_data_release(in); + gpgme_data_release(out); + gpgme_release(ctx); + gpgme_key_release(key); + 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); + gpgme_data_release(in); + gpgme_data_release(out); + gpgme_release(ctx); + gpgme_key_release(key); + perror("ディレクトリを創作に失敗。"); + return; + } + } + + sprintf(gpgpath, "%s%s%s%s", homedir, basedir, file, ext); + gpgfile = fopen(gpgpath, "wb"); + if (gpgfile == NULL) { + perror("ファイルを開くに失敗。"); + printf("失敗したパス: %s\n", gpgpath); + free(gpgpath); + gpgme_data_release(in); + gpgme_data_release(out); + gpgme_release(ctx); + gpgme_key_release(key); + 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); + gpgme_data_release(in); + gpgme_data_release(out); + gpgme_release(ctx); + gpgme_key_release(key); + 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) { + fwrite(buffer, 1, read_bytes, gpgfile); + } + + // 掃除 + fclose(gpgfile); + free(gpgpath); + gpgme_data_release(in); + gpgme_data_release(out); + gpgme_release(ctx); + gpgme_key_release(key); + + printf("パスワードを保存出来ました。\n"); +} diff --git a/addpass.h b/addpass.h new file mode 100644 index 0000000..2fdff4f --- /dev/null +++ b/addpass.h @@ -0,0 +1,6 @@ +#ifndef ADDPASS_H +#define ADDPASS_H + +void addpass(char* file); + +#endif diff --git a/main.c b/main.c new file mode 100644 index 0000000..2ea0579 --- /dev/null +++ b/main.c @@ -0,0 +1,52 @@ +#include +#include +#include + +#include + +void initpass(char* gpgid); +#include "showpass.h" +#include "yankpass.h" +void listpass(char* dir); +#include "addpass.h" +void delpass(char* file); +void genpass(char* file, int count, bool issecure); +void otppass(char* file); +void helpme(); + +const char* sofname = "sp"; +const char* version = "0.0.1"; + +void helpme() { + printf("使い方:\n"); + //printf("%s -i :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)を表示。存在しなければ、創作する", sofname); + printf("%s -h :ヘルプを表示\n", sofname); + printf("%s -v :バージョンを表示\n", sofname); +} + +int main (int argc, char* argv[]) { + if (argc < 2) { + helpme(); + return 1; + } + + if (argc == 3 && strcmp(argv[1], "-i") == 0) printf("TODO: 初期設定\n"); + 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) printf("TODO: 一覧\n"); + else if (argc == 3 && strcmp(argv[1], "-a") == 0) addpass(argv[2]); + else if (argc == 3 && strcmp(argv[1], "-d") == 0) printf("TODO: 削除\n"); + else if ((argc == 4 || argc == 5) && strcmp(argv[1], "-g") == 0) printf("TODO: パスワードを創作\n"); + else if (argc == 3 && strcmp(argv[1], "-o") == 0) printf("TODO: otp\n"); + else if (argc == 2 && strcmp(argv[1], "-v") == 0) printf("%s-%s\n", sofname, version); + else helpme(); + + return 0; +} diff --git a/showpass.c b/showpass.c new file mode 100644 index 0000000..8cb8dc8 --- /dev/null +++ b/showpass.c @@ -0,0 +1,90 @@ +#include +#include +#include + +#include + +#include "showpass.h" + +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; + } + gpgme_data_new_from_stream(&in, gpgfile); + + // データオブジェクトを創作 + gpgme_data_new(&out); + + // 復号化して + err = gpgme_op_decrypt(ctx, in, out); + if (err) { + fprintf(stderr, "復号化に失敗: %s\n", gpgme_strerror(err)); + + // 掃除 + fclose(gpgfile); + free(gpgpath); + gpgme_data_release(in); + gpgme_data_release(out); + gpgme_release(ctx); + 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) { + buffer[read_bytes] = '\0'; + printf("%s", buffer); + } + puts(""); + + // 掃除 + fclose(gpgfile); + free(gpgpath); + gpgme_data_release(in); + gpgme_data_release(out); + gpgme_release(ctx); +} diff --git a/showpass.h b/showpass.h new file mode 100644 index 0000000..3c47d0f --- /dev/null +++ b/showpass.h @@ -0,0 +1,6 @@ +#ifndef SHOWPASS_H +#define SHOWPASS_H + +void showpass(char* file); + +#endif diff --git a/yankpass.c b/yankpass.c new file mode 100644 index 0000000..e79fc51 --- /dev/null +++ b/yankpass.c @@ -0,0 +1,116 @@ +#include +#include +#include +#include + +#include + +#include "yankpass.h" +#include "showpass.h" + +void yankpass(char* file) { + // Xセッションではない場合(例えば、SSH、TTY、Gayland等)、showpass()を実行して + if (getenv("DISPLAY") == NULL) { + printf("Xセッションではありませんので、「sp -s」を実行します。\n"); + showpass(file); + return; + } + + gpgme_ctx_t ctx; + gpgme_error_t err; + gpgme_data_t in, out; + FILE *gpgfile; + + // GPGMEライブラリを設置 + setlocale(LC_ALL, ""); + gpgme_check_version(NULL); + gpgme_set_locale(NULL, LC_CTYPE, setlocale(LC_CTYPE, NULL)); + + // GPGMEを創作 + err = gpgme_new(&ctx); + if (err) { + 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; + } + gpgme_data_new_from_stream(&in, gpgfile); + + // データオブジェクトを創作 + gpgme_data_new(&out); + + // 復号化して + err = gpgme_op_decrypt(ctx, in, out); + if (err) { + fprintf(stderr, "復号化に失敗: %s\n", gpgme_strerror(err)); + + // 掃除 + fclose(gpgfile); + free(gpgpath); + gpgme_data_release(in); + gpgme_data_release(out); + gpgme_release(ctx); + return; + } + + // xclipを準備して + FILE *pipe = popen("xclip -selection clipboard", "w"); + if (pipe == NULL) { + + // 掃除 + fclose(gpgfile); + free(gpgpath); + gpgme_data_release(in); + gpgme_data_release(out); + gpgme_release(ctx); + perror("クリップボードを見つけられませんでした。"); + return; + } + + // 復号化したパスワードを表示する + gpgme_data_seek(out, 0, SEEK_SET); + + char buffer[512]; + ssize_t read_bytes; + while ((read_bytes = gpgme_data_read(out, buffer, 511)) > 0) { + fwrite(buffer, 1, read_bytes, pipe); + } + pclose(pipe); + + // 45秒後、クリップボードから削除する + sleep(45); + system("echo -n | xclip -selection clipboard"); + + // 掃除 + fclose(gpgfile); + free(gpgpath); + gpgme_data_release(in); + gpgme_data_release(out); + gpgme_release(ctx); +} diff --git a/yankpass.h b/yankpass.h new file mode 100644 index 0000000..7219787 --- /dev/null +++ b/yankpass.h @@ -0,0 +1,6 @@ +#ifndef YANKPASS_H +#define YANKPASS_H + +void yankpass(char* file); + +#endif