commit b5e050afa647f0d00c4e9e74e7cdb6fac433e764 Author: 諏訪子 Date: Wed Nov 29 21:19:53 2023 +0900 最初コミット 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