From 759b7d8b83132085ce6c832fbe2b4a162ca2cd72 Mon Sep 17 00:00:00 2001 From: sinanmohd Date: Sat, 30 Dec 2023 15:52:10 +0530 Subject: pass/add: init --- gpg.c | 34 ++++++++++++++++++++++++++++++++ gpg.h | 2 ++ pass.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- pass_util.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- pass_util.h | 2 +- util.h | 2 +- 6 files changed, 161 insertions(+), 4 deletions(-) diff --git a/gpg.c b/gpg.c index 5206a48..3d37c09 100644 --- a/gpg.c +++ b/gpg.c @@ -96,3 +96,37 @@ int gpg_decrypt(const char *fpr, const char *path, char *pass_out, size_t n) return 0; } + +int gpg_encrypt(FILE *stream, const char *fpr, const char *pass, size_t n) +{ + int r; + char buf[BUFSIZ]; + gpgme_data_t in, out; + gpgme_error_t err; + + r = gpg_init(); + if (r) + return r; + + err = gpgme_get_key(ctx, fpr, &key, 1); + fail_if_err(err); + + err = gpgme_data_new_from_mem(&in, pass, n, 0); + fail_if_err(err); + err = gpgme_data_new(&out); + fail_if_err(err); + err = gpgme_op_encrypt(ctx, &key, GPGME_ENCRYPT_ALWAYS_TRUST, in, out); + fail_if_err(err); + + r = gpgme_data_seek(out, 0, SEEK_SET); + if (r) + fail_if_err (gpgme_err_code_from_errno(errno)); + + while ((r = gpgme_data_read(out, buf, BUFSIZ))) + fwrite(buf, r, 1, stream); + gpg_cleanup(); + if (r < 0) + fail_if_err(gpgme_err_code_from_errno(errno)); + + return 0; +} diff --git a/gpg.h b/gpg.h index 36c2b87..461a9d5 100644 --- a/gpg.h +++ b/gpg.h @@ -1,4 +1,6 @@ +#include #include int gpg_key_validate(const char *fpr); int gpg_decrypt(const char *fpr, const char *path, char *pass_out, size_t n); +int gpg_encrypt(FILE *stream, const char *fpr, const char *pass, size_t n); diff --git a/pass.c b/pass.c index 842e3e9..5425245 100644 --- a/pass.c +++ b/pass.c @@ -1,12 +1,14 @@ #include #include #include +#include #include "pass_util.h" #include "util.h" void print_usage(void); int cat(const char *path); +int add(const char *path); void print_usage(void) { @@ -40,6 +42,59 @@ int cat(const char *path) return (s == NULL); } +int add(const char *path) +{ + char *p1 = NULL, *p2 = NULL; + FILE *in; + size_t n; + int r; + + in = fopen("/dev/tty", "r"); + if (!in) + in = stdin; + + fputs("Password: ", stdout); + r = pass_getpass(&p1, &n, in); + if (r < 0) { + if (in != stdin) + fclose(in); + if (p1) + free(p1); + err_die(1, "%d:%s:", errno, strerror(errno)); + } + + fputs("\nRetype password: ", stdout); + r = pass_getpass(&p2, &n, in); + putc('\n', stdout); + if (r < 0) { + if (in != stdin) + fclose(in); + if (p2) + free(p1); + if (p2) + free(p2); + err_die(1, "%d:%s:", errno, strerror(errno)); + } + + if (in != stdin) + fclose(in); + + if (n > PASS_MAX - 1) /* TODO: get rid of the limit */ + err_die(1, "password must not exceed %d characters", PASS_MAX); + + if (strcmp(p1, p2)) { + free(p1); + free(p2); + err_die(1, "Sorry, passwords do not match"); + } + + free(p1); + r = pass_add(path, p2, n); + + free(p2); + return r; +} + int main(int argc, char *argv[]) { int r = 0; @@ -63,7 +118,15 @@ int main(int argc, char *argv[]) err_die(1, "invalid usage, try pass help"); r = cat(argv[1]); + } else if (!strcmp("add", *argv)) { + if (!argv[1]) + err_die(1, "invalid usage, try pass help"); + + r = add(argv[1]); } - return r; + if (r) + err_die(r, "Command '%s' failed", *argv); + + return 0; } diff --git a/pass_util.c b/pass_util.c index b302c55..d08f982 100644 --- a/pass_util.c +++ b/pass_util.c @@ -3,7 +3,9 @@ #include #include #include +#include #include +#include #include #include @@ -11,7 +13,8 @@ #include "util.h" #include "gpg.h" -#define DEF_PASS_DIR "pass" +#define DEF_PASS_DIR "pass" +#define FPR_MAX 256 static char pass_dir[PATH_MAX] = {0}; static char pass_out[PASS_MAX] = {0}; @@ -132,7 +135,62 @@ size_t pass_getpass(char **lineptr, size_t *n, FILE *stream) return -1; r = getline (lineptr, n, stream); + if (r > 0 && (*lineptr)[r - 1] == '\n') + (*lineptr)[r - 1] = '\0'; + (void) tcsetattr (fileno (stream), TCSAFLUSH, &old); return r; } + +int pass_add(const char *path, const char *pass, size_t n) +{ + int r; + char *rc; + FILE *gpg_id, *out_stream; + char gpg_id_path[PATH_MAX], fpr[FPR_MAX], pass_path[PATH_MAX]; + + r = set_pass_dir(); + if (r) + err_die(1, "PASSWORD_STORE_DIR not set"); + + r = snprintf(gpg_id_path, sizeof(gpg_id_path), "%s/%s", pass_dir, ".gpg-id"); + if (r > (int) sizeof(gpg_id_path)) + err_die(1, "path exceeded PATH_MAX"); + + gpg_id = fopen(gpg_id_path, "r"); + if (!gpg_id) + err_die(1, "%s %s", gpg_id_path, strerror(errno)); + + rc = fgets(fpr, sizeof(fpr), gpg_id); + if (!rc) + err_die(1, "failed to read %s", gpg_id_path); + fclose(gpg_id); + + r = gpg_key_validate(fpr); + if (r) + err_die(1, "key not usable, try gpg --list-keys"); + + r = snprintf(pass_path, sizeof(pass_path), "%s/%s.gpg", pass_dir, path); + if (r > (int) sizeof(gpg_id_path)) + err_die(1, "path exceeded PATH_MAX"); + + rc = strdup(pass_path); + if (!rc) + err_die(1, "%s", strerror(errno)); + (void) r_mkdir(dirname(rc), S_IRWXU); + free(rc); + + r = access(pass_path, F_OK); + if (!(errno & ENOENT)) + err_die(1, "an entry already exists for %s", path); + + out_stream = fopen(pass_path, "w"); + if (!out_stream) + err_die(1, "%s", strerror(errno)); + + r = gpg_encrypt(out_stream, fpr, pass, n); + + fclose(out_stream); + return r; +} diff --git a/pass_util.h b/pass_util.h index 1caf6cf..6d03285 100644 --- a/pass_util.h +++ b/pass_util.h @@ -1,8 +1,8 @@ #include #define PASS_MAX 4096 -#define FPR_MAX 128 int pass_init(const char *fpr); const char *pass_cat(const char *path); +int pass_add(const char *path, const char *pass, size_t n); size_t pass_getpass(char **lineptr, size_t *n, FILE *stream); diff --git a/util.h b/util.h index 9353849..04c5b88 100644 --- a/util.h +++ b/util.h @@ -4,7 +4,7 @@ #include #define err_die(r, ...) {\ - fprintf(stderr, "err: %s:%d: ", __FILE__, __LINE__); \ + fprintf(stderr, "%s:%d: ", __FILE__, __LINE__); \ fprintf(stderr, __VA_ARGS__); \ fputc('\n', stderr); \ return r; \ -- cgit v1.2.3