A simple password manager in C












9












$begingroup$


I wrote this simple program similar to "tiny password manager". Now I am looking for feedback how I can improve it.



As always, a big topic in C code is error handling. So what programming techniques could I use to write nicer code instead of all this boilerplate. Also, which errors should I catch and what error could I safely ignore?



Also, code style in general. Are there any lines I could rewrite in a nicer way?



#include <assert.h>
#include <errno.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include <gpgme.h>

static void usage(void)
{
printf("USAGE: pw CMD KEYn");
}

#define ENV_PW_STORE "PW_STORE"
#define DEFAULT_PW_STORE ".pw_store"

enum {
ERR_OK = 0,
ERR_SYS_ERROR = 1,
ERR_NO_KEY = 2,
ERR_NO_CMD = 3,
ERR_CRYPTO_ERROR = 4,
};

static char input_buffer[256] = {''};
static char file_buffer[256] = {''};

static char *get_store_dir(void)
{
char *env = getenv(ENV_PW_STORE);
if (env != NULL)
return strdup(env);

// build the default from HOME/DEFAULT_PW_STORE
const char *home = getenv("HOME");
if (home == NULL)
return NULL;

size_t required = strlen(home) + strlen(DEFAULT_PW_STORE) + 2;
assert(required > 0);
char *def = malloc(required);
if (def == NULL)
return NULL;

snprintf(def, required, "%s/%s", home, DEFAULT_PW_STORE);
return def;
}

static char *open_password_store(void)
{
char *pstore = get_store_dir();
if (pstore == NULL)
return NULL;

struct stat sb;
if (!((stat(pstore, &sb) == 0) && S_ISDIR(sb.st_mode))) {
if (mkdir(pstore, S_IRWXU)) {
fprintf(stderr, "Failed to create keystore directoryn");
}
}

return pstore;
}

static char *get_passfile(const char *dir, const char *key)
{
assert(dir != NULL);
assert(key != NULL);

// build the filename from DIR/KEY.gpg
size_t required = strlen(dir) + strlen(key) + strlen(".gpg") + 2;
assert(required > 0);
char *path = malloc(required);

if (path == NULL)
return NULL;

snprintf(path, required, "%s/%s.gpg", dir, key);
return path;
}

static struct crypto_ctx {
gpgme_ctx_t ctx;
gpgme_key_t keylist[2];
gpgme_data_t data[2];
} cc = {};

static char *decrypt_from_file(const char *path, size_t *len)
{
assert(path != NULL);

if (gpgme_data_new_from_file(&cc.data[0], path, 1))
return NULL;

gpgme_data_new(&cc.data[1]);

if (gpgme_op_decrypt(cc.ctx, cc.data[0], cc.data[1])) {
gpgme_data_release(cc.data[0]);
gpgme_data_release(cc.data[1]);
return NULL;
}

gpgme_data_release(cc.data[0]);
return gpgme_data_release_and_get_mem(cc.data[1], len);
}

static int encrypt_to_file(const char *path, char *buf, size_t len)
{
gpgme_data_new_from_mem(&cc.data[0], buf, len, 1);
gpgme_data_new(&cc.data[1]);

memset(buf, '', len);

if (gpgme_op_encrypt(cc.ctx, cc.keylist, GPGME_ENCRYPT_ALWAYS_TRUST,
cc.data[0], cc.data[1])) {
gpgme_data_release(cc.data[0]);
gpgme_data_release(cc.data[1]);
return 1;
}

FILE *fd = fopen(path, "wb");
if (fd == NULL) {
gpgme_data_release(cc.data[0]);
gpgme_data_release(cc.data[1]);
return 1;
}

size_t enc_len = 0;
char *enc = gpgme_data_release_and_get_mem(cc.data[1], &enc_len);
fwrite(enc, sizeof(char), enc_len, fd);
gpgme_data_release(cc.data[0]);
gpgme_free(enc);
fclose(fd);
return 0;
}

static int get_console_input(char *buf, size_t bufsize)
{
fflush(stdin);
fgets(buf, bufsize, stdin);

size_t last = strlen(buf) - 1;
// get rid of the ending newline, if present
if (buf[last] == 'n')
buf[last] = '';

return last;
}

static int init_crypto(void)
{
gpgme_check_version(NULL);
setlocale(LC_ALL, "");
gpgme_set_locale(NULL, LC_CTYPE, setlocale(LC_CTYPE, NULL));
if (gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP)) {
return 1;
}

if (gpgme_new(&cc.ctx)) {
return 1;
}

char *key = getenv("PW_ENC_KEY");
if (key == NULL) {
gpgme_op_keylist_start(cc.ctx, NULL, 0);
if (gpgme_op_keylist_next(cc.ctx, &cc.keylist[0])) {
return 1;
}
gpgme_op_keylist_end(cc.ctx);
} else {
if (gpgme_get_key(cc.ctx, key, &cc.keylist[0], 0)) {
return 1;
}
}

if (cc.keylist[0] == NULL) {
return 1;
}

return 0;
}

static void cleanup_crypto(void)
{
if (cc.keylist[0])
gpgme_key_unref(cc.keylist[0]);

gpgme_release(cc.ctx);
}

static int insert_entry(const char *keyfile)
{
assert(keyfile != NULL);

if (access(keyfile, F_OK)) {
printf("Inserting new key...n");
} else {
printf("Updating existing key...n");
}

printf("Insert password (no input to abort): ");

size_t input_len = get_console_input(input_buffer, 255);
if (input_len <= 0) {
printf("No password inserted, aborting...n");
return ERR_OK;
}

if (encrypt_to_file(keyfile, input_buffer, input_len)) {
return ERR_CRYPTO_ERROR;
}

return ERR_OK;
}

static int get_entry(const char *keyfile)
{
assert(keyfile != NULL);

if (access(keyfile, F_OK)) {
printf("Given key does not exist.n");
return ERR_OK;
}

size_t plain_len = 0;
char *plain = decrypt_from_file(keyfile, &plain_len);

if (plain == NULL) {
return ERR_CRYPTO_ERROR;
}

printf("%.*sn", plain_len, plain);
gpgme_free(plain);

return ERR_OK;
}

int main(int argc, const char **argv)
{
if (argc != 3) {
usage();
return 1;
}

char *pstore = open_password_store();
if (pstore == NULL) {
fprintf(stderr, "Failed to open password storen");
return ERR_SYS_ERROR;
}

char *filename = get_passfile(pstore, argv[2]);
if (filename == NULL) {
fprintf(stderr, "Failed to modifify keyn");
return ERR_NO_KEY;
}

if (init_crypto()) {
fprintf(stderr, "Failed to set up crypto backendn");
return ERR_CRYPTO_ERROR;
}

int ret = 0;

// process possible commands
// new - insert or override an entry
// get - return an entry

if (strcmp("new", argv[1]) == 0) {
ret = insert_entry(filename);
} else if (strcmp("get", argv[1]) == 0) {
ret = get_entry(filename);
} else {
fprintf(stderr, "Unknown command! Use new or get!n");
ret = ERR_NO_CMD;
}

cleanup_crypto();

free(pstore);
free(filename);

return ret;
}









share|improve this question









$endgroup$












  • $begingroup$
    Did you run your code through a static code analyser? [CppCheck]|(cppcheck.sourceforge.net) works for C, as well as C++. And don't forget to do unit testing.
    $endgroup$
    – Mawg
    Dec 21 '18 at 9:16
















9












$begingroup$


I wrote this simple program similar to "tiny password manager". Now I am looking for feedback how I can improve it.



As always, a big topic in C code is error handling. So what programming techniques could I use to write nicer code instead of all this boilerplate. Also, which errors should I catch and what error could I safely ignore?



Also, code style in general. Are there any lines I could rewrite in a nicer way?



#include <assert.h>
#include <errno.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include <gpgme.h>

static void usage(void)
{
printf("USAGE: pw CMD KEYn");
}

#define ENV_PW_STORE "PW_STORE"
#define DEFAULT_PW_STORE ".pw_store"

enum {
ERR_OK = 0,
ERR_SYS_ERROR = 1,
ERR_NO_KEY = 2,
ERR_NO_CMD = 3,
ERR_CRYPTO_ERROR = 4,
};

static char input_buffer[256] = {''};
static char file_buffer[256] = {''};

static char *get_store_dir(void)
{
char *env = getenv(ENV_PW_STORE);
if (env != NULL)
return strdup(env);

// build the default from HOME/DEFAULT_PW_STORE
const char *home = getenv("HOME");
if (home == NULL)
return NULL;

size_t required = strlen(home) + strlen(DEFAULT_PW_STORE) + 2;
assert(required > 0);
char *def = malloc(required);
if (def == NULL)
return NULL;

snprintf(def, required, "%s/%s", home, DEFAULT_PW_STORE);
return def;
}

static char *open_password_store(void)
{
char *pstore = get_store_dir();
if (pstore == NULL)
return NULL;

struct stat sb;
if (!((stat(pstore, &sb) == 0) && S_ISDIR(sb.st_mode))) {
if (mkdir(pstore, S_IRWXU)) {
fprintf(stderr, "Failed to create keystore directoryn");
}
}

return pstore;
}

static char *get_passfile(const char *dir, const char *key)
{
assert(dir != NULL);
assert(key != NULL);

// build the filename from DIR/KEY.gpg
size_t required = strlen(dir) + strlen(key) + strlen(".gpg") + 2;
assert(required > 0);
char *path = malloc(required);

if (path == NULL)
return NULL;

snprintf(path, required, "%s/%s.gpg", dir, key);
return path;
}

static struct crypto_ctx {
gpgme_ctx_t ctx;
gpgme_key_t keylist[2];
gpgme_data_t data[2];
} cc = {};

static char *decrypt_from_file(const char *path, size_t *len)
{
assert(path != NULL);

if (gpgme_data_new_from_file(&cc.data[0], path, 1))
return NULL;

gpgme_data_new(&cc.data[1]);

if (gpgme_op_decrypt(cc.ctx, cc.data[0], cc.data[1])) {
gpgme_data_release(cc.data[0]);
gpgme_data_release(cc.data[1]);
return NULL;
}

gpgme_data_release(cc.data[0]);
return gpgme_data_release_and_get_mem(cc.data[1], len);
}

static int encrypt_to_file(const char *path, char *buf, size_t len)
{
gpgme_data_new_from_mem(&cc.data[0], buf, len, 1);
gpgme_data_new(&cc.data[1]);

memset(buf, '', len);

if (gpgme_op_encrypt(cc.ctx, cc.keylist, GPGME_ENCRYPT_ALWAYS_TRUST,
cc.data[0], cc.data[1])) {
gpgme_data_release(cc.data[0]);
gpgme_data_release(cc.data[1]);
return 1;
}

FILE *fd = fopen(path, "wb");
if (fd == NULL) {
gpgme_data_release(cc.data[0]);
gpgme_data_release(cc.data[1]);
return 1;
}

size_t enc_len = 0;
char *enc = gpgme_data_release_and_get_mem(cc.data[1], &enc_len);
fwrite(enc, sizeof(char), enc_len, fd);
gpgme_data_release(cc.data[0]);
gpgme_free(enc);
fclose(fd);
return 0;
}

static int get_console_input(char *buf, size_t bufsize)
{
fflush(stdin);
fgets(buf, bufsize, stdin);

size_t last = strlen(buf) - 1;
// get rid of the ending newline, if present
if (buf[last] == 'n')
buf[last] = '';

return last;
}

static int init_crypto(void)
{
gpgme_check_version(NULL);
setlocale(LC_ALL, "");
gpgme_set_locale(NULL, LC_CTYPE, setlocale(LC_CTYPE, NULL));
if (gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP)) {
return 1;
}

if (gpgme_new(&cc.ctx)) {
return 1;
}

char *key = getenv("PW_ENC_KEY");
if (key == NULL) {
gpgme_op_keylist_start(cc.ctx, NULL, 0);
if (gpgme_op_keylist_next(cc.ctx, &cc.keylist[0])) {
return 1;
}
gpgme_op_keylist_end(cc.ctx);
} else {
if (gpgme_get_key(cc.ctx, key, &cc.keylist[0], 0)) {
return 1;
}
}

if (cc.keylist[0] == NULL) {
return 1;
}

return 0;
}

static void cleanup_crypto(void)
{
if (cc.keylist[0])
gpgme_key_unref(cc.keylist[0]);

gpgme_release(cc.ctx);
}

static int insert_entry(const char *keyfile)
{
assert(keyfile != NULL);

if (access(keyfile, F_OK)) {
printf("Inserting new key...n");
} else {
printf("Updating existing key...n");
}

printf("Insert password (no input to abort): ");

size_t input_len = get_console_input(input_buffer, 255);
if (input_len <= 0) {
printf("No password inserted, aborting...n");
return ERR_OK;
}

if (encrypt_to_file(keyfile, input_buffer, input_len)) {
return ERR_CRYPTO_ERROR;
}

return ERR_OK;
}

static int get_entry(const char *keyfile)
{
assert(keyfile != NULL);

if (access(keyfile, F_OK)) {
printf("Given key does not exist.n");
return ERR_OK;
}

size_t plain_len = 0;
char *plain = decrypt_from_file(keyfile, &plain_len);

if (plain == NULL) {
return ERR_CRYPTO_ERROR;
}

printf("%.*sn", plain_len, plain);
gpgme_free(plain);

return ERR_OK;
}

int main(int argc, const char **argv)
{
if (argc != 3) {
usage();
return 1;
}

char *pstore = open_password_store();
if (pstore == NULL) {
fprintf(stderr, "Failed to open password storen");
return ERR_SYS_ERROR;
}

char *filename = get_passfile(pstore, argv[2]);
if (filename == NULL) {
fprintf(stderr, "Failed to modifify keyn");
return ERR_NO_KEY;
}

if (init_crypto()) {
fprintf(stderr, "Failed to set up crypto backendn");
return ERR_CRYPTO_ERROR;
}

int ret = 0;

// process possible commands
// new - insert or override an entry
// get - return an entry

if (strcmp("new", argv[1]) == 0) {
ret = insert_entry(filename);
} else if (strcmp("get", argv[1]) == 0) {
ret = get_entry(filename);
} else {
fprintf(stderr, "Unknown command! Use new or get!n");
ret = ERR_NO_CMD;
}

cleanup_crypto();

free(pstore);
free(filename);

return ret;
}









share|improve this question









$endgroup$












  • $begingroup$
    Did you run your code through a static code analyser? [CppCheck]|(cppcheck.sourceforge.net) works for C, as well as C++. And don't forget to do unit testing.
    $endgroup$
    – Mawg
    Dec 21 '18 at 9:16














9












9








9


1



$begingroup$


I wrote this simple program similar to "tiny password manager". Now I am looking for feedback how I can improve it.



As always, a big topic in C code is error handling. So what programming techniques could I use to write nicer code instead of all this boilerplate. Also, which errors should I catch and what error could I safely ignore?



Also, code style in general. Are there any lines I could rewrite in a nicer way?



#include <assert.h>
#include <errno.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include <gpgme.h>

static void usage(void)
{
printf("USAGE: pw CMD KEYn");
}

#define ENV_PW_STORE "PW_STORE"
#define DEFAULT_PW_STORE ".pw_store"

enum {
ERR_OK = 0,
ERR_SYS_ERROR = 1,
ERR_NO_KEY = 2,
ERR_NO_CMD = 3,
ERR_CRYPTO_ERROR = 4,
};

static char input_buffer[256] = {''};
static char file_buffer[256] = {''};

static char *get_store_dir(void)
{
char *env = getenv(ENV_PW_STORE);
if (env != NULL)
return strdup(env);

// build the default from HOME/DEFAULT_PW_STORE
const char *home = getenv("HOME");
if (home == NULL)
return NULL;

size_t required = strlen(home) + strlen(DEFAULT_PW_STORE) + 2;
assert(required > 0);
char *def = malloc(required);
if (def == NULL)
return NULL;

snprintf(def, required, "%s/%s", home, DEFAULT_PW_STORE);
return def;
}

static char *open_password_store(void)
{
char *pstore = get_store_dir();
if (pstore == NULL)
return NULL;

struct stat sb;
if (!((stat(pstore, &sb) == 0) && S_ISDIR(sb.st_mode))) {
if (mkdir(pstore, S_IRWXU)) {
fprintf(stderr, "Failed to create keystore directoryn");
}
}

return pstore;
}

static char *get_passfile(const char *dir, const char *key)
{
assert(dir != NULL);
assert(key != NULL);

// build the filename from DIR/KEY.gpg
size_t required = strlen(dir) + strlen(key) + strlen(".gpg") + 2;
assert(required > 0);
char *path = malloc(required);

if (path == NULL)
return NULL;

snprintf(path, required, "%s/%s.gpg", dir, key);
return path;
}

static struct crypto_ctx {
gpgme_ctx_t ctx;
gpgme_key_t keylist[2];
gpgme_data_t data[2];
} cc = {};

static char *decrypt_from_file(const char *path, size_t *len)
{
assert(path != NULL);

if (gpgme_data_new_from_file(&cc.data[0], path, 1))
return NULL;

gpgme_data_new(&cc.data[1]);

if (gpgme_op_decrypt(cc.ctx, cc.data[0], cc.data[1])) {
gpgme_data_release(cc.data[0]);
gpgme_data_release(cc.data[1]);
return NULL;
}

gpgme_data_release(cc.data[0]);
return gpgme_data_release_and_get_mem(cc.data[1], len);
}

static int encrypt_to_file(const char *path, char *buf, size_t len)
{
gpgme_data_new_from_mem(&cc.data[0], buf, len, 1);
gpgme_data_new(&cc.data[1]);

memset(buf, '', len);

if (gpgme_op_encrypt(cc.ctx, cc.keylist, GPGME_ENCRYPT_ALWAYS_TRUST,
cc.data[0], cc.data[1])) {
gpgme_data_release(cc.data[0]);
gpgme_data_release(cc.data[1]);
return 1;
}

FILE *fd = fopen(path, "wb");
if (fd == NULL) {
gpgme_data_release(cc.data[0]);
gpgme_data_release(cc.data[1]);
return 1;
}

size_t enc_len = 0;
char *enc = gpgme_data_release_and_get_mem(cc.data[1], &enc_len);
fwrite(enc, sizeof(char), enc_len, fd);
gpgme_data_release(cc.data[0]);
gpgme_free(enc);
fclose(fd);
return 0;
}

static int get_console_input(char *buf, size_t bufsize)
{
fflush(stdin);
fgets(buf, bufsize, stdin);

size_t last = strlen(buf) - 1;
// get rid of the ending newline, if present
if (buf[last] == 'n')
buf[last] = '';

return last;
}

static int init_crypto(void)
{
gpgme_check_version(NULL);
setlocale(LC_ALL, "");
gpgme_set_locale(NULL, LC_CTYPE, setlocale(LC_CTYPE, NULL));
if (gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP)) {
return 1;
}

if (gpgme_new(&cc.ctx)) {
return 1;
}

char *key = getenv("PW_ENC_KEY");
if (key == NULL) {
gpgme_op_keylist_start(cc.ctx, NULL, 0);
if (gpgme_op_keylist_next(cc.ctx, &cc.keylist[0])) {
return 1;
}
gpgme_op_keylist_end(cc.ctx);
} else {
if (gpgme_get_key(cc.ctx, key, &cc.keylist[0], 0)) {
return 1;
}
}

if (cc.keylist[0] == NULL) {
return 1;
}

return 0;
}

static void cleanup_crypto(void)
{
if (cc.keylist[0])
gpgme_key_unref(cc.keylist[0]);

gpgme_release(cc.ctx);
}

static int insert_entry(const char *keyfile)
{
assert(keyfile != NULL);

if (access(keyfile, F_OK)) {
printf("Inserting new key...n");
} else {
printf("Updating existing key...n");
}

printf("Insert password (no input to abort): ");

size_t input_len = get_console_input(input_buffer, 255);
if (input_len <= 0) {
printf("No password inserted, aborting...n");
return ERR_OK;
}

if (encrypt_to_file(keyfile, input_buffer, input_len)) {
return ERR_CRYPTO_ERROR;
}

return ERR_OK;
}

static int get_entry(const char *keyfile)
{
assert(keyfile != NULL);

if (access(keyfile, F_OK)) {
printf("Given key does not exist.n");
return ERR_OK;
}

size_t plain_len = 0;
char *plain = decrypt_from_file(keyfile, &plain_len);

if (plain == NULL) {
return ERR_CRYPTO_ERROR;
}

printf("%.*sn", plain_len, plain);
gpgme_free(plain);

return ERR_OK;
}

int main(int argc, const char **argv)
{
if (argc != 3) {
usage();
return 1;
}

char *pstore = open_password_store();
if (pstore == NULL) {
fprintf(stderr, "Failed to open password storen");
return ERR_SYS_ERROR;
}

char *filename = get_passfile(pstore, argv[2]);
if (filename == NULL) {
fprintf(stderr, "Failed to modifify keyn");
return ERR_NO_KEY;
}

if (init_crypto()) {
fprintf(stderr, "Failed to set up crypto backendn");
return ERR_CRYPTO_ERROR;
}

int ret = 0;

// process possible commands
// new - insert or override an entry
// get - return an entry

if (strcmp("new", argv[1]) == 0) {
ret = insert_entry(filename);
} else if (strcmp("get", argv[1]) == 0) {
ret = get_entry(filename);
} else {
fprintf(stderr, "Unknown command! Use new or get!n");
ret = ERR_NO_CMD;
}

cleanup_crypto();

free(pstore);
free(filename);

return ret;
}









share|improve this question









$endgroup$




I wrote this simple program similar to "tiny password manager". Now I am looking for feedback how I can improve it.



As always, a big topic in C code is error handling. So what programming techniques could I use to write nicer code instead of all this boilerplate. Also, which errors should I catch and what error could I safely ignore?



Also, code style in general. Are there any lines I could rewrite in a nicer way?



#include <assert.h>
#include <errno.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include <gpgme.h>

static void usage(void)
{
printf("USAGE: pw CMD KEYn");
}

#define ENV_PW_STORE "PW_STORE"
#define DEFAULT_PW_STORE ".pw_store"

enum {
ERR_OK = 0,
ERR_SYS_ERROR = 1,
ERR_NO_KEY = 2,
ERR_NO_CMD = 3,
ERR_CRYPTO_ERROR = 4,
};

static char input_buffer[256] = {''};
static char file_buffer[256] = {''};

static char *get_store_dir(void)
{
char *env = getenv(ENV_PW_STORE);
if (env != NULL)
return strdup(env);

// build the default from HOME/DEFAULT_PW_STORE
const char *home = getenv("HOME");
if (home == NULL)
return NULL;

size_t required = strlen(home) + strlen(DEFAULT_PW_STORE) + 2;
assert(required > 0);
char *def = malloc(required);
if (def == NULL)
return NULL;

snprintf(def, required, "%s/%s", home, DEFAULT_PW_STORE);
return def;
}

static char *open_password_store(void)
{
char *pstore = get_store_dir();
if (pstore == NULL)
return NULL;

struct stat sb;
if (!((stat(pstore, &sb) == 0) && S_ISDIR(sb.st_mode))) {
if (mkdir(pstore, S_IRWXU)) {
fprintf(stderr, "Failed to create keystore directoryn");
}
}

return pstore;
}

static char *get_passfile(const char *dir, const char *key)
{
assert(dir != NULL);
assert(key != NULL);

// build the filename from DIR/KEY.gpg
size_t required = strlen(dir) + strlen(key) + strlen(".gpg") + 2;
assert(required > 0);
char *path = malloc(required);

if (path == NULL)
return NULL;

snprintf(path, required, "%s/%s.gpg", dir, key);
return path;
}

static struct crypto_ctx {
gpgme_ctx_t ctx;
gpgme_key_t keylist[2];
gpgme_data_t data[2];
} cc = {};

static char *decrypt_from_file(const char *path, size_t *len)
{
assert(path != NULL);

if (gpgme_data_new_from_file(&cc.data[0], path, 1))
return NULL;

gpgme_data_new(&cc.data[1]);

if (gpgme_op_decrypt(cc.ctx, cc.data[0], cc.data[1])) {
gpgme_data_release(cc.data[0]);
gpgme_data_release(cc.data[1]);
return NULL;
}

gpgme_data_release(cc.data[0]);
return gpgme_data_release_and_get_mem(cc.data[1], len);
}

static int encrypt_to_file(const char *path, char *buf, size_t len)
{
gpgme_data_new_from_mem(&cc.data[0], buf, len, 1);
gpgme_data_new(&cc.data[1]);

memset(buf, '', len);

if (gpgme_op_encrypt(cc.ctx, cc.keylist, GPGME_ENCRYPT_ALWAYS_TRUST,
cc.data[0], cc.data[1])) {
gpgme_data_release(cc.data[0]);
gpgme_data_release(cc.data[1]);
return 1;
}

FILE *fd = fopen(path, "wb");
if (fd == NULL) {
gpgme_data_release(cc.data[0]);
gpgme_data_release(cc.data[1]);
return 1;
}

size_t enc_len = 0;
char *enc = gpgme_data_release_and_get_mem(cc.data[1], &enc_len);
fwrite(enc, sizeof(char), enc_len, fd);
gpgme_data_release(cc.data[0]);
gpgme_free(enc);
fclose(fd);
return 0;
}

static int get_console_input(char *buf, size_t bufsize)
{
fflush(stdin);
fgets(buf, bufsize, stdin);

size_t last = strlen(buf) - 1;
// get rid of the ending newline, if present
if (buf[last] == 'n')
buf[last] = '';

return last;
}

static int init_crypto(void)
{
gpgme_check_version(NULL);
setlocale(LC_ALL, "");
gpgme_set_locale(NULL, LC_CTYPE, setlocale(LC_CTYPE, NULL));
if (gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP)) {
return 1;
}

if (gpgme_new(&cc.ctx)) {
return 1;
}

char *key = getenv("PW_ENC_KEY");
if (key == NULL) {
gpgme_op_keylist_start(cc.ctx, NULL, 0);
if (gpgme_op_keylist_next(cc.ctx, &cc.keylist[0])) {
return 1;
}
gpgme_op_keylist_end(cc.ctx);
} else {
if (gpgme_get_key(cc.ctx, key, &cc.keylist[0], 0)) {
return 1;
}
}

if (cc.keylist[0] == NULL) {
return 1;
}

return 0;
}

static void cleanup_crypto(void)
{
if (cc.keylist[0])
gpgme_key_unref(cc.keylist[0]);

gpgme_release(cc.ctx);
}

static int insert_entry(const char *keyfile)
{
assert(keyfile != NULL);

if (access(keyfile, F_OK)) {
printf("Inserting new key...n");
} else {
printf("Updating existing key...n");
}

printf("Insert password (no input to abort): ");

size_t input_len = get_console_input(input_buffer, 255);
if (input_len <= 0) {
printf("No password inserted, aborting...n");
return ERR_OK;
}

if (encrypt_to_file(keyfile, input_buffer, input_len)) {
return ERR_CRYPTO_ERROR;
}

return ERR_OK;
}

static int get_entry(const char *keyfile)
{
assert(keyfile != NULL);

if (access(keyfile, F_OK)) {
printf("Given key does not exist.n");
return ERR_OK;
}

size_t plain_len = 0;
char *plain = decrypt_from_file(keyfile, &plain_len);

if (plain == NULL) {
return ERR_CRYPTO_ERROR;
}

printf("%.*sn", plain_len, plain);
gpgme_free(plain);

return ERR_OK;
}

int main(int argc, const char **argv)
{
if (argc != 3) {
usage();
return 1;
}

char *pstore = open_password_store();
if (pstore == NULL) {
fprintf(stderr, "Failed to open password storen");
return ERR_SYS_ERROR;
}

char *filename = get_passfile(pstore, argv[2]);
if (filename == NULL) {
fprintf(stderr, "Failed to modifify keyn");
return ERR_NO_KEY;
}

if (init_crypto()) {
fprintf(stderr, "Failed to set up crypto backendn");
return ERR_CRYPTO_ERROR;
}

int ret = 0;

// process possible commands
// new - insert or override an entry
// get - return an entry

if (strcmp("new", argv[1]) == 0) {
ret = insert_entry(filename);
} else if (strcmp("get", argv[1]) == 0) {
ret = get_entry(filename);
} else {
fprintf(stderr, "Unknown command! Use new or get!n");
ret = ERR_NO_CMD;
}

cleanup_crypto();

free(pstore);
free(filename);

return ret;
}






c






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Dec 20 '18 at 19:02









flowitflowit

1664




1664












  • $begingroup$
    Did you run your code through a static code analyser? [CppCheck]|(cppcheck.sourceforge.net) works for C, as well as C++. And don't forget to do unit testing.
    $endgroup$
    – Mawg
    Dec 21 '18 at 9:16


















  • $begingroup$
    Did you run your code through a static code analyser? [CppCheck]|(cppcheck.sourceforge.net) works for C, as well as C++. And don't forget to do unit testing.
    $endgroup$
    – Mawg
    Dec 21 '18 at 9:16
















$begingroup$
Did you run your code through a static code analyser? [CppCheck]|(cppcheck.sourceforge.net) works for C, as well as C++. And don't forget to do unit testing.
$endgroup$
– Mawg
Dec 21 '18 at 9:16




$begingroup$
Did you run your code through a static code analyser? [CppCheck]|(cppcheck.sourceforge.net) works for C, as well as C++. And don't forget to do unit testing.
$endgroup$
– Mawg
Dec 21 '18 at 9:16










3 Answers
3






active

oldest

votes


















5












$begingroup$


a big topic in C code is error handling




Yes. The best C code can do is to strive for uniformity as there are a number of good approaches.






which errors should I catch (?)




Some of the most important errors to catch are the ones outside code control - this is usually all I/O functions.



Missing check:



// fgets(buf, bufsize, stdin);
if (fgets(buf, bufsize, stdin) == NULL) {
Handle_EndOfFile_or_Error();
}


Naked fwrite():



// fwrite(enc, sizeof(char), enc_len, fd);
if (fwrite(enc, sizeof(char), enc_len, fd) < enc_len) {
// Report error
}





which errors ... could I safely ignore?




Code well does extensive checking.



The following assert() only applies if the addition rolls over to 0 - a pedantic concern.



size_t required = strlen(home) + strlen(DEFAULT_PW_STORE) + 2;
// Questionable assert.
assert(required > 0);


If code is pedantic, could detect overflow with wider math.



#include <stdint.h>
....
// v----------v form a `uintmax_t` and add using that math
uintmax_t sum = UINTMAX_C(2) + strlen(home) + strlen(DEFAULT_PW_STORE);
assert(sum <= SIZE_MAX);
size_t required = (size_t) sum;





how I can improve it.




Advanced: password scrubbing



Although not key to OP review request, secure code that uses passwords will 1) scrub buffers when done 2) insure atomic access 3) use functions that do 1 & 2.



Example scrubbing:



void scrub(void *p, size_t sz) {
volatile unsigned char *m = p;
while (sz-- > 0) m[sz] = 0;
}

char *filename = get_passfile(pstore, argv[2]);
// Code is done with `pstore, argv[2]`, so zero it.
scrub(pstore, strlen(pstore));
scrub(argv[2], strlen(argv[2]));


Scrubbing is especially important when an error occurs someplace as that is often a hack approach to cause a core dump, etc.



Interestingly, code can write to argv[2].



This is a deep issue only cursorily covered here.



Avoid UB



fflush(stdin); is undefined behavior (UB). Avoid this compiler specific feature.






share|improve this answer











$endgroup$









  • 3




    $begingroup$
    memset should not be used for memory scrubbing. See note here: en.cppreference.com/w/c/string/byte/memset
    $endgroup$
    – David Brown
    Dec 21 '18 at 0:57










  • $begingroup$
    @DavidBrown Fair enough - memset() is potentially optimized out. Post amened. I do not see scrub() will get optimized out. Your thoughts?
    $endgroup$
    – chux
    Dec 21 '18 at 4:22










  • $begingroup$
    So what could I do about fflush(stdin);? I need at least some way to ensure there are no characters read that the user did not enter after the promt.
    $endgroup$
    – flowit
    Dec 21 '18 at 8:43










  • $begingroup$
    @flowit 1) The usual solution is to insure the prior read of user input read all the data up to the end of line. 2) After that fixing that, please provide more detail why "ensure there are no characters read that the user did not enter after the prompt".
    $endgroup$
    – chux
    Dec 21 '18 at 14:37



















4












$begingroup$

I'm fairly new in C, so feel free to ignore me, however, this section concerned me:



    size_t input_len = get_console_input(input_buffer, 255);
if (input_len <= 0) {
printf("No password inserted, aborting...n");
return ERR_OK;
}


You've set an input length of 255, great. I noticed no error trapping for someone entering a length greater than 255. This could lead to buffer overflow attacks, since this program is expected to run as root level.



I'd suggest adding a routine for that, similar to the one that you've added to detect 0 characters entered.



Love the fact that you've ended with a cleanup command and the following two free commands!






share|improve this answer











$endgroup$













  • $begingroup$
    The two bits of code you posted seem identical. What am I missing?
    $endgroup$
    – Cris Luengo
    Dec 20 '18 at 21:58










  • $begingroup$
    Oops... my bad....they are! Sorry!
    $endgroup$
    – KoshVorlon
    Dec 20 '18 at 22:00










  • $begingroup$
    Thank you Cris Luengo. I hadn't seen that before. I fixed the problem! :)
    $endgroup$
    – KoshVorlon
    Dec 20 '18 at 22:10



















4












$begingroup$

static int insert_entry(const char *keyfile)


Don't write int. typedef your enum so that you can write your own type instead. Similarly, here:



if (argc != 3) {
usage();
return 1;
}


You're returning 1, but elsewhere in the same function you're returning enum constants. You should choose one or the other for consistency - probably the enum constants.



static struct crypto_ctx {
gpgme_ctx_t ctx;
gpgme_key_t keylist[2];
gpgme_data_t data[2];
} cc = {};


This is good, but not quite good enough. Since you've made cc a global, your code is non-reentrant. You should convert that struct to a typedef struct, remove the instance cc, and in all functions that use cc, accept a pointer to it as an argument.






share|improve this answer











$endgroup$









  • 1




    $begingroup$
    Could you cite exact section of C99 that declares (void) in argument list redundant? You seem to be confusing C99 with C++. At least GCC compiles the following without problem with -std=c99, which contradicts your claim: void f(){} void test(){f(2,3);}.
    $endgroup$
    – Ruslan
    Dec 21 '18 at 6:49












  • $begingroup$
    @Ruslan ISO9899, chapter 6.9.1. Given typedef int F(void), the following are compatible function signatures: int f(void), and int g().
    $endgroup$
    – Reinderien
    Dec 21 '18 at 15:44












  • $begingroup$
    @Ruslan Also, your example produces quite obvious warnings from gcc: warning: too many arguments in call to 'f' .
    $endgroup$
    – Reinderien
    Dec 21 '18 at 15:47










  • $begingroup$
    If you mean the footnote, then it doesn't say that int g() is the same signature as int f(void): it's simply compatible, i.e. won't cause an error when you declare as int(void) and define as int(), and vice-versa. Moreover, I've still not found any normative description of (void) as redundant (footnotes aren't normative). As for your warning, I can't reproduce it with gcc 7 with options -std=c99 -pedantic-errors -Wall -Wextra. I do get this as an error in C++ mode though, which once more confirms that it's only C++ feature, not C one.
    $endgroup$
    – Ruslan
    Dec 21 '18 at 16:03










  • $begingroup$
    @Ruslan See stackoverflow.com/questions/41803937/func-vs-funcvoid-in-c99
    $endgroup$
    – Reinderien
    Dec 21 '18 at 16:52











Your Answer





StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");

StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f210069%2fa-simple-password-manager-in-c%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























3 Answers
3






active

oldest

votes








3 Answers
3






active

oldest

votes









active

oldest

votes






active

oldest

votes









5












$begingroup$


a big topic in C code is error handling




Yes. The best C code can do is to strive for uniformity as there are a number of good approaches.






which errors should I catch (?)




Some of the most important errors to catch are the ones outside code control - this is usually all I/O functions.



Missing check:



// fgets(buf, bufsize, stdin);
if (fgets(buf, bufsize, stdin) == NULL) {
Handle_EndOfFile_or_Error();
}


Naked fwrite():



// fwrite(enc, sizeof(char), enc_len, fd);
if (fwrite(enc, sizeof(char), enc_len, fd) < enc_len) {
// Report error
}





which errors ... could I safely ignore?




Code well does extensive checking.



The following assert() only applies if the addition rolls over to 0 - a pedantic concern.



size_t required = strlen(home) + strlen(DEFAULT_PW_STORE) + 2;
// Questionable assert.
assert(required > 0);


If code is pedantic, could detect overflow with wider math.



#include <stdint.h>
....
// v----------v form a `uintmax_t` and add using that math
uintmax_t sum = UINTMAX_C(2) + strlen(home) + strlen(DEFAULT_PW_STORE);
assert(sum <= SIZE_MAX);
size_t required = (size_t) sum;





how I can improve it.




Advanced: password scrubbing



Although not key to OP review request, secure code that uses passwords will 1) scrub buffers when done 2) insure atomic access 3) use functions that do 1 & 2.



Example scrubbing:



void scrub(void *p, size_t sz) {
volatile unsigned char *m = p;
while (sz-- > 0) m[sz] = 0;
}

char *filename = get_passfile(pstore, argv[2]);
// Code is done with `pstore, argv[2]`, so zero it.
scrub(pstore, strlen(pstore));
scrub(argv[2], strlen(argv[2]));


Scrubbing is especially important when an error occurs someplace as that is often a hack approach to cause a core dump, etc.



Interestingly, code can write to argv[2].



This is a deep issue only cursorily covered here.



Avoid UB



fflush(stdin); is undefined behavior (UB). Avoid this compiler specific feature.






share|improve this answer











$endgroup$









  • 3




    $begingroup$
    memset should not be used for memory scrubbing. See note here: en.cppreference.com/w/c/string/byte/memset
    $endgroup$
    – David Brown
    Dec 21 '18 at 0:57










  • $begingroup$
    @DavidBrown Fair enough - memset() is potentially optimized out. Post amened. I do not see scrub() will get optimized out. Your thoughts?
    $endgroup$
    – chux
    Dec 21 '18 at 4:22










  • $begingroup$
    So what could I do about fflush(stdin);? I need at least some way to ensure there are no characters read that the user did not enter after the promt.
    $endgroup$
    – flowit
    Dec 21 '18 at 8:43










  • $begingroup$
    @flowit 1) The usual solution is to insure the prior read of user input read all the data up to the end of line. 2) After that fixing that, please provide more detail why "ensure there are no characters read that the user did not enter after the prompt".
    $endgroup$
    – chux
    Dec 21 '18 at 14:37
















5












$begingroup$


a big topic in C code is error handling




Yes. The best C code can do is to strive for uniformity as there are a number of good approaches.






which errors should I catch (?)




Some of the most important errors to catch are the ones outside code control - this is usually all I/O functions.



Missing check:



// fgets(buf, bufsize, stdin);
if (fgets(buf, bufsize, stdin) == NULL) {
Handle_EndOfFile_or_Error();
}


Naked fwrite():



// fwrite(enc, sizeof(char), enc_len, fd);
if (fwrite(enc, sizeof(char), enc_len, fd) < enc_len) {
// Report error
}





which errors ... could I safely ignore?




Code well does extensive checking.



The following assert() only applies if the addition rolls over to 0 - a pedantic concern.



size_t required = strlen(home) + strlen(DEFAULT_PW_STORE) + 2;
// Questionable assert.
assert(required > 0);


If code is pedantic, could detect overflow with wider math.



#include <stdint.h>
....
// v----------v form a `uintmax_t` and add using that math
uintmax_t sum = UINTMAX_C(2) + strlen(home) + strlen(DEFAULT_PW_STORE);
assert(sum <= SIZE_MAX);
size_t required = (size_t) sum;





how I can improve it.




Advanced: password scrubbing



Although not key to OP review request, secure code that uses passwords will 1) scrub buffers when done 2) insure atomic access 3) use functions that do 1 & 2.



Example scrubbing:



void scrub(void *p, size_t sz) {
volatile unsigned char *m = p;
while (sz-- > 0) m[sz] = 0;
}

char *filename = get_passfile(pstore, argv[2]);
// Code is done with `pstore, argv[2]`, so zero it.
scrub(pstore, strlen(pstore));
scrub(argv[2], strlen(argv[2]));


Scrubbing is especially important when an error occurs someplace as that is often a hack approach to cause a core dump, etc.



Interestingly, code can write to argv[2].



This is a deep issue only cursorily covered here.



Avoid UB



fflush(stdin); is undefined behavior (UB). Avoid this compiler specific feature.






share|improve this answer











$endgroup$









  • 3




    $begingroup$
    memset should not be used for memory scrubbing. See note here: en.cppreference.com/w/c/string/byte/memset
    $endgroup$
    – David Brown
    Dec 21 '18 at 0:57










  • $begingroup$
    @DavidBrown Fair enough - memset() is potentially optimized out. Post amened. I do not see scrub() will get optimized out. Your thoughts?
    $endgroup$
    – chux
    Dec 21 '18 at 4:22










  • $begingroup$
    So what could I do about fflush(stdin);? I need at least some way to ensure there are no characters read that the user did not enter after the promt.
    $endgroup$
    – flowit
    Dec 21 '18 at 8:43










  • $begingroup$
    @flowit 1) The usual solution is to insure the prior read of user input read all the data up to the end of line. 2) After that fixing that, please provide more detail why "ensure there are no characters read that the user did not enter after the prompt".
    $endgroup$
    – chux
    Dec 21 '18 at 14:37














5












5








5





$begingroup$


a big topic in C code is error handling




Yes. The best C code can do is to strive for uniformity as there are a number of good approaches.






which errors should I catch (?)




Some of the most important errors to catch are the ones outside code control - this is usually all I/O functions.



Missing check:



// fgets(buf, bufsize, stdin);
if (fgets(buf, bufsize, stdin) == NULL) {
Handle_EndOfFile_or_Error();
}


Naked fwrite():



// fwrite(enc, sizeof(char), enc_len, fd);
if (fwrite(enc, sizeof(char), enc_len, fd) < enc_len) {
// Report error
}





which errors ... could I safely ignore?




Code well does extensive checking.



The following assert() only applies if the addition rolls over to 0 - a pedantic concern.



size_t required = strlen(home) + strlen(DEFAULT_PW_STORE) + 2;
// Questionable assert.
assert(required > 0);


If code is pedantic, could detect overflow with wider math.



#include <stdint.h>
....
// v----------v form a `uintmax_t` and add using that math
uintmax_t sum = UINTMAX_C(2) + strlen(home) + strlen(DEFAULT_PW_STORE);
assert(sum <= SIZE_MAX);
size_t required = (size_t) sum;





how I can improve it.




Advanced: password scrubbing



Although not key to OP review request, secure code that uses passwords will 1) scrub buffers when done 2) insure atomic access 3) use functions that do 1 & 2.



Example scrubbing:



void scrub(void *p, size_t sz) {
volatile unsigned char *m = p;
while (sz-- > 0) m[sz] = 0;
}

char *filename = get_passfile(pstore, argv[2]);
// Code is done with `pstore, argv[2]`, so zero it.
scrub(pstore, strlen(pstore));
scrub(argv[2], strlen(argv[2]));


Scrubbing is especially important when an error occurs someplace as that is often a hack approach to cause a core dump, etc.



Interestingly, code can write to argv[2].



This is a deep issue only cursorily covered here.



Avoid UB



fflush(stdin); is undefined behavior (UB). Avoid this compiler specific feature.






share|improve this answer











$endgroup$




a big topic in C code is error handling




Yes. The best C code can do is to strive for uniformity as there are a number of good approaches.






which errors should I catch (?)




Some of the most important errors to catch are the ones outside code control - this is usually all I/O functions.



Missing check:



// fgets(buf, bufsize, stdin);
if (fgets(buf, bufsize, stdin) == NULL) {
Handle_EndOfFile_or_Error();
}


Naked fwrite():



// fwrite(enc, sizeof(char), enc_len, fd);
if (fwrite(enc, sizeof(char), enc_len, fd) < enc_len) {
// Report error
}





which errors ... could I safely ignore?




Code well does extensive checking.



The following assert() only applies if the addition rolls over to 0 - a pedantic concern.



size_t required = strlen(home) + strlen(DEFAULT_PW_STORE) + 2;
// Questionable assert.
assert(required > 0);


If code is pedantic, could detect overflow with wider math.



#include <stdint.h>
....
// v----------v form a `uintmax_t` and add using that math
uintmax_t sum = UINTMAX_C(2) + strlen(home) + strlen(DEFAULT_PW_STORE);
assert(sum <= SIZE_MAX);
size_t required = (size_t) sum;





how I can improve it.




Advanced: password scrubbing



Although not key to OP review request, secure code that uses passwords will 1) scrub buffers when done 2) insure atomic access 3) use functions that do 1 & 2.



Example scrubbing:



void scrub(void *p, size_t sz) {
volatile unsigned char *m = p;
while (sz-- > 0) m[sz] = 0;
}

char *filename = get_passfile(pstore, argv[2]);
// Code is done with `pstore, argv[2]`, so zero it.
scrub(pstore, strlen(pstore));
scrub(argv[2], strlen(argv[2]));


Scrubbing is especially important when an error occurs someplace as that is often a hack approach to cause a core dump, etc.



Interestingly, code can write to argv[2].



This is a deep issue only cursorily covered here.



Avoid UB



fflush(stdin); is undefined behavior (UB). Avoid this compiler specific feature.







share|improve this answer














share|improve this answer



share|improve this answer








edited Dec 21 '18 at 14:59

























answered Dec 20 '18 at 21:57









chuxchux

13k21344




13k21344








  • 3




    $begingroup$
    memset should not be used for memory scrubbing. See note here: en.cppreference.com/w/c/string/byte/memset
    $endgroup$
    – David Brown
    Dec 21 '18 at 0:57










  • $begingroup$
    @DavidBrown Fair enough - memset() is potentially optimized out. Post amened. I do not see scrub() will get optimized out. Your thoughts?
    $endgroup$
    – chux
    Dec 21 '18 at 4:22










  • $begingroup$
    So what could I do about fflush(stdin);? I need at least some way to ensure there are no characters read that the user did not enter after the promt.
    $endgroup$
    – flowit
    Dec 21 '18 at 8:43










  • $begingroup$
    @flowit 1) The usual solution is to insure the prior read of user input read all the data up to the end of line. 2) After that fixing that, please provide more detail why "ensure there are no characters read that the user did not enter after the prompt".
    $endgroup$
    – chux
    Dec 21 '18 at 14:37














  • 3




    $begingroup$
    memset should not be used for memory scrubbing. See note here: en.cppreference.com/w/c/string/byte/memset
    $endgroup$
    – David Brown
    Dec 21 '18 at 0:57










  • $begingroup$
    @DavidBrown Fair enough - memset() is potentially optimized out. Post amened. I do not see scrub() will get optimized out. Your thoughts?
    $endgroup$
    – chux
    Dec 21 '18 at 4:22










  • $begingroup$
    So what could I do about fflush(stdin);? I need at least some way to ensure there are no characters read that the user did not enter after the promt.
    $endgroup$
    – flowit
    Dec 21 '18 at 8:43










  • $begingroup$
    @flowit 1) The usual solution is to insure the prior read of user input read all the data up to the end of line. 2) After that fixing that, please provide more detail why "ensure there are no characters read that the user did not enter after the prompt".
    $endgroup$
    – chux
    Dec 21 '18 at 14:37








3




3




$begingroup$
memset should not be used for memory scrubbing. See note here: en.cppreference.com/w/c/string/byte/memset
$endgroup$
– David Brown
Dec 21 '18 at 0:57




$begingroup$
memset should not be used for memory scrubbing. See note here: en.cppreference.com/w/c/string/byte/memset
$endgroup$
– David Brown
Dec 21 '18 at 0:57












$begingroup$
@DavidBrown Fair enough - memset() is potentially optimized out. Post amened. I do not see scrub() will get optimized out. Your thoughts?
$endgroup$
– chux
Dec 21 '18 at 4:22




$begingroup$
@DavidBrown Fair enough - memset() is potentially optimized out. Post amened. I do not see scrub() will get optimized out. Your thoughts?
$endgroup$
– chux
Dec 21 '18 at 4:22












$begingroup$
So what could I do about fflush(stdin);? I need at least some way to ensure there are no characters read that the user did not enter after the promt.
$endgroup$
– flowit
Dec 21 '18 at 8:43




$begingroup$
So what could I do about fflush(stdin);? I need at least some way to ensure there are no characters read that the user did not enter after the promt.
$endgroup$
– flowit
Dec 21 '18 at 8:43












$begingroup$
@flowit 1) The usual solution is to insure the prior read of user input read all the data up to the end of line. 2) After that fixing that, please provide more detail why "ensure there are no characters read that the user did not enter after the prompt".
$endgroup$
– chux
Dec 21 '18 at 14:37




$begingroup$
@flowit 1) The usual solution is to insure the prior read of user input read all the data up to the end of line. 2) After that fixing that, please provide more detail why "ensure there are no characters read that the user did not enter after the prompt".
$endgroup$
– chux
Dec 21 '18 at 14:37













4












$begingroup$

I'm fairly new in C, so feel free to ignore me, however, this section concerned me:



    size_t input_len = get_console_input(input_buffer, 255);
if (input_len <= 0) {
printf("No password inserted, aborting...n");
return ERR_OK;
}


You've set an input length of 255, great. I noticed no error trapping for someone entering a length greater than 255. This could lead to buffer overflow attacks, since this program is expected to run as root level.



I'd suggest adding a routine for that, similar to the one that you've added to detect 0 characters entered.



Love the fact that you've ended with a cleanup command and the following two free commands!






share|improve this answer











$endgroup$













  • $begingroup$
    The two bits of code you posted seem identical. What am I missing?
    $endgroup$
    – Cris Luengo
    Dec 20 '18 at 21:58










  • $begingroup$
    Oops... my bad....they are! Sorry!
    $endgroup$
    – KoshVorlon
    Dec 20 '18 at 22:00










  • $begingroup$
    Thank you Cris Luengo. I hadn't seen that before. I fixed the problem! :)
    $endgroup$
    – KoshVorlon
    Dec 20 '18 at 22:10
















4












$begingroup$

I'm fairly new in C, so feel free to ignore me, however, this section concerned me:



    size_t input_len = get_console_input(input_buffer, 255);
if (input_len <= 0) {
printf("No password inserted, aborting...n");
return ERR_OK;
}


You've set an input length of 255, great. I noticed no error trapping for someone entering a length greater than 255. This could lead to buffer overflow attacks, since this program is expected to run as root level.



I'd suggest adding a routine for that, similar to the one that you've added to detect 0 characters entered.



Love the fact that you've ended with a cleanup command and the following two free commands!






share|improve this answer











$endgroup$













  • $begingroup$
    The two bits of code you posted seem identical. What am I missing?
    $endgroup$
    – Cris Luengo
    Dec 20 '18 at 21:58










  • $begingroup$
    Oops... my bad....they are! Sorry!
    $endgroup$
    – KoshVorlon
    Dec 20 '18 at 22:00










  • $begingroup$
    Thank you Cris Luengo. I hadn't seen that before. I fixed the problem! :)
    $endgroup$
    – KoshVorlon
    Dec 20 '18 at 22:10














4












4








4





$begingroup$

I'm fairly new in C, so feel free to ignore me, however, this section concerned me:



    size_t input_len = get_console_input(input_buffer, 255);
if (input_len <= 0) {
printf("No password inserted, aborting...n");
return ERR_OK;
}


You've set an input length of 255, great. I noticed no error trapping for someone entering a length greater than 255. This could lead to buffer overflow attacks, since this program is expected to run as root level.



I'd suggest adding a routine for that, similar to the one that you've added to detect 0 characters entered.



Love the fact that you've ended with a cleanup command and the following two free commands!






share|improve this answer











$endgroup$



I'm fairly new in C, so feel free to ignore me, however, this section concerned me:



    size_t input_len = get_console_input(input_buffer, 255);
if (input_len <= 0) {
printf("No password inserted, aborting...n");
return ERR_OK;
}


You've set an input length of 255, great. I noticed no error trapping for someone entering a length greater than 255. This could lead to buffer overflow attacks, since this program is expected to run as root level.



I'd suggest adding a routine for that, similar to the one that you've added to detect 0 characters entered.



Love the fact that you've ended with a cleanup command and the following two free commands!







share|improve this answer














share|improve this answer



share|improve this answer








edited Dec 20 '18 at 22:10

























answered Dec 20 '18 at 20:38









KoshVorlonKoshVorlon

585




585












  • $begingroup$
    The two bits of code you posted seem identical. What am I missing?
    $endgroup$
    – Cris Luengo
    Dec 20 '18 at 21:58










  • $begingroup$
    Oops... my bad....they are! Sorry!
    $endgroup$
    – KoshVorlon
    Dec 20 '18 at 22:00










  • $begingroup$
    Thank you Cris Luengo. I hadn't seen that before. I fixed the problem! :)
    $endgroup$
    – KoshVorlon
    Dec 20 '18 at 22:10


















  • $begingroup$
    The two bits of code you posted seem identical. What am I missing?
    $endgroup$
    – Cris Luengo
    Dec 20 '18 at 21:58










  • $begingroup$
    Oops... my bad....they are! Sorry!
    $endgroup$
    – KoshVorlon
    Dec 20 '18 at 22:00










  • $begingroup$
    Thank you Cris Luengo. I hadn't seen that before. I fixed the problem! :)
    $endgroup$
    – KoshVorlon
    Dec 20 '18 at 22:10
















$begingroup$
The two bits of code you posted seem identical. What am I missing?
$endgroup$
– Cris Luengo
Dec 20 '18 at 21:58




$begingroup$
The two bits of code you posted seem identical. What am I missing?
$endgroup$
– Cris Luengo
Dec 20 '18 at 21:58












$begingroup$
Oops... my bad....they are! Sorry!
$endgroup$
– KoshVorlon
Dec 20 '18 at 22:00




$begingroup$
Oops... my bad....they are! Sorry!
$endgroup$
– KoshVorlon
Dec 20 '18 at 22:00












$begingroup$
Thank you Cris Luengo. I hadn't seen that before. I fixed the problem! :)
$endgroup$
– KoshVorlon
Dec 20 '18 at 22:10




$begingroup$
Thank you Cris Luengo. I hadn't seen that before. I fixed the problem! :)
$endgroup$
– KoshVorlon
Dec 20 '18 at 22:10











4












$begingroup$

static int insert_entry(const char *keyfile)


Don't write int. typedef your enum so that you can write your own type instead. Similarly, here:



if (argc != 3) {
usage();
return 1;
}


You're returning 1, but elsewhere in the same function you're returning enum constants. You should choose one or the other for consistency - probably the enum constants.



static struct crypto_ctx {
gpgme_ctx_t ctx;
gpgme_key_t keylist[2];
gpgme_data_t data[2];
} cc = {};


This is good, but not quite good enough. Since you've made cc a global, your code is non-reentrant. You should convert that struct to a typedef struct, remove the instance cc, and in all functions that use cc, accept a pointer to it as an argument.






share|improve this answer











$endgroup$









  • 1




    $begingroup$
    Could you cite exact section of C99 that declares (void) in argument list redundant? You seem to be confusing C99 with C++. At least GCC compiles the following without problem with -std=c99, which contradicts your claim: void f(){} void test(){f(2,3);}.
    $endgroup$
    – Ruslan
    Dec 21 '18 at 6:49












  • $begingroup$
    @Ruslan ISO9899, chapter 6.9.1. Given typedef int F(void), the following are compatible function signatures: int f(void), and int g().
    $endgroup$
    – Reinderien
    Dec 21 '18 at 15:44












  • $begingroup$
    @Ruslan Also, your example produces quite obvious warnings from gcc: warning: too many arguments in call to 'f' .
    $endgroup$
    – Reinderien
    Dec 21 '18 at 15:47










  • $begingroup$
    If you mean the footnote, then it doesn't say that int g() is the same signature as int f(void): it's simply compatible, i.e. won't cause an error when you declare as int(void) and define as int(), and vice-versa. Moreover, I've still not found any normative description of (void) as redundant (footnotes aren't normative). As for your warning, I can't reproduce it with gcc 7 with options -std=c99 -pedantic-errors -Wall -Wextra. I do get this as an error in C++ mode though, which once more confirms that it's only C++ feature, not C one.
    $endgroup$
    – Ruslan
    Dec 21 '18 at 16:03










  • $begingroup$
    @Ruslan See stackoverflow.com/questions/41803937/func-vs-funcvoid-in-c99
    $endgroup$
    – Reinderien
    Dec 21 '18 at 16:52
















4












$begingroup$

static int insert_entry(const char *keyfile)


Don't write int. typedef your enum so that you can write your own type instead. Similarly, here:



if (argc != 3) {
usage();
return 1;
}


You're returning 1, but elsewhere in the same function you're returning enum constants. You should choose one or the other for consistency - probably the enum constants.



static struct crypto_ctx {
gpgme_ctx_t ctx;
gpgme_key_t keylist[2];
gpgme_data_t data[2];
} cc = {};


This is good, but not quite good enough. Since you've made cc a global, your code is non-reentrant. You should convert that struct to a typedef struct, remove the instance cc, and in all functions that use cc, accept a pointer to it as an argument.






share|improve this answer











$endgroup$









  • 1




    $begingroup$
    Could you cite exact section of C99 that declares (void) in argument list redundant? You seem to be confusing C99 with C++. At least GCC compiles the following without problem with -std=c99, which contradicts your claim: void f(){} void test(){f(2,3);}.
    $endgroup$
    – Ruslan
    Dec 21 '18 at 6:49












  • $begingroup$
    @Ruslan ISO9899, chapter 6.9.1. Given typedef int F(void), the following are compatible function signatures: int f(void), and int g().
    $endgroup$
    – Reinderien
    Dec 21 '18 at 15:44












  • $begingroup$
    @Ruslan Also, your example produces quite obvious warnings from gcc: warning: too many arguments in call to 'f' .
    $endgroup$
    – Reinderien
    Dec 21 '18 at 15:47










  • $begingroup$
    If you mean the footnote, then it doesn't say that int g() is the same signature as int f(void): it's simply compatible, i.e. won't cause an error when you declare as int(void) and define as int(), and vice-versa. Moreover, I've still not found any normative description of (void) as redundant (footnotes aren't normative). As for your warning, I can't reproduce it with gcc 7 with options -std=c99 -pedantic-errors -Wall -Wextra. I do get this as an error in C++ mode though, which once more confirms that it's only C++ feature, not C one.
    $endgroup$
    – Ruslan
    Dec 21 '18 at 16:03










  • $begingroup$
    @Ruslan See stackoverflow.com/questions/41803937/func-vs-funcvoid-in-c99
    $endgroup$
    – Reinderien
    Dec 21 '18 at 16:52














4












4








4





$begingroup$

static int insert_entry(const char *keyfile)


Don't write int. typedef your enum so that you can write your own type instead. Similarly, here:



if (argc != 3) {
usage();
return 1;
}


You're returning 1, but elsewhere in the same function you're returning enum constants. You should choose one or the other for consistency - probably the enum constants.



static struct crypto_ctx {
gpgme_ctx_t ctx;
gpgme_key_t keylist[2];
gpgme_data_t data[2];
} cc = {};


This is good, but not quite good enough. Since you've made cc a global, your code is non-reentrant. You should convert that struct to a typedef struct, remove the instance cc, and in all functions that use cc, accept a pointer to it as an argument.






share|improve this answer











$endgroup$



static int insert_entry(const char *keyfile)


Don't write int. typedef your enum so that you can write your own type instead. Similarly, here:



if (argc != 3) {
usage();
return 1;
}


You're returning 1, but elsewhere in the same function you're returning enum constants. You should choose one or the other for consistency - probably the enum constants.



static struct crypto_ctx {
gpgme_ctx_t ctx;
gpgme_key_t keylist[2];
gpgme_data_t data[2];
} cc = {};


This is good, but not quite good enough. Since you've made cc a global, your code is non-reentrant. You should convert that struct to a typedef struct, remove the instance cc, and in all functions that use cc, accept a pointer to it as an argument.







share|improve this answer














share|improve this answer



share|improve this answer








edited Dec 21 '18 at 16:52

























answered Dec 20 '18 at 21:51









ReinderienReinderien

4,130822




4,130822








  • 1




    $begingroup$
    Could you cite exact section of C99 that declares (void) in argument list redundant? You seem to be confusing C99 with C++. At least GCC compiles the following without problem with -std=c99, which contradicts your claim: void f(){} void test(){f(2,3);}.
    $endgroup$
    – Ruslan
    Dec 21 '18 at 6:49












  • $begingroup$
    @Ruslan ISO9899, chapter 6.9.1. Given typedef int F(void), the following are compatible function signatures: int f(void), and int g().
    $endgroup$
    – Reinderien
    Dec 21 '18 at 15:44












  • $begingroup$
    @Ruslan Also, your example produces quite obvious warnings from gcc: warning: too many arguments in call to 'f' .
    $endgroup$
    – Reinderien
    Dec 21 '18 at 15:47










  • $begingroup$
    If you mean the footnote, then it doesn't say that int g() is the same signature as int f(void): it's simply compatible, i.e. won't cause an error when you declare as int(void) and define as int(), and vice-versa. Moreover, I've still not found any normative description of (void) as redundant (footnotes aren't normative). As for your warning, I can't reproduce it with gcc 7 with options -std=c99 -pedantic-errors -Wall -Wextra. I do get this as an error in C++ mode though, which once more confirms that it's only C++ feature, not C one.
    $endgroup$
    – Ruslan
    Dec 21 '18 at 16:03










  • $begingroup$
    @Ruslan See stackoverflow.com/questions/41803937/func-vs-funcvoid-in-c99
    $endgroup$
    – Reinderien
    Dec 21 '18 at 16:52














  • 1




    $begingroup$
    Could you cite exact section of C99 that declares (void) in argument list redundant? You seem to be confusing C99 with C++. At least GCC compiles the following without problem with -std=c99, which contradicts your claim: void f(){} void test(){f(2,3);}.
    $endgroup$
    – Ruslan
    Dec 21 '18 at 6:49












  • $begingroup$
    @Ruslan ISO9899, chapter 6.9.1. Given typedef int F(void), the following are compatible function signatures: int f(void), and int g().
    $endgroup$
    – Reinderien
    Dec 21 '18 at 15:44












  • $begingroup$
    @Ruslan Also, your example produces quite obvious warnings from gcc: warning: too many arguments in call to 'f' .
    $endgroup$
    – Reinderien
    Dec 21 '18 at 15:47










  • $begingroup$
    If you mean the footnote, then it doesn't say that int g() is the same signature as int f(void): it's simply compatible, i.e. won't cause an error when you declare as int(void) and define as int(), and vice-versa. Moreover, I've still not found any normative description of (void) as redundant (footnotes aren't normative). As for your warning, I can't reproduce it with gcc 7 with options -std=c99 -pedantic-errors -Wall -Wextra. I do get this as an error in C++ mode though, which once more confirms that it's only C++ feature, not C one.
    $endgroup$
    – Ruslan
    Dec 21 '18 at 16:03










  • $begingroup$
    @Ruslan See stackoverflow.com/questions/41803937/func-vs-funcvoid-in-c99
    $endgroup$
    – Reinderien
    Dec 21 '18 at 16:52








1




1




$begingroup$
Could you cite exact section of C99 that declares (void) in argument list redundant? You seem to be confusing C99 with C++. At least GCC compiles the following without problem with -std=c99, which contradicts your claim: void f(){} void test(){f(2,3);}.
$endgroup$
– Ruslan
Dec 21 '18 at 6:49






$begingroup$
Could you cite exact section of C99 that declares (void) in argument list redundant? You seem to be confusing C99 with C++. At least GCC compiles the following without problem with -std=c99, which contradicts your claim: void f(){} void test(){f(2,3);}.
$endgroup$
– Ruslan
Dec 21 '18 at 6:49














$begingroup$
@Ruslan ISO9899, chapter 6.9.1. Given typedef int F(void), the following are compatible function signatures: int f(void), and int g().
$endgroup$
– Reinderien
Dec 21 '18 at 15:44






$begingroup$
@Ruslan ISO9899, chapter 6.9.1. Given typedef int F(void), the following are compatible function signatures: int f(void), and int g().
$endgroup$
– Reinderien
Dec 21 '18 at 15:44














$begingroup$
@Ruslan Also, your example produces quite obvious warnings from gcc: warning: too many arguments in call to 'f' .
$endgroup$
– Reinderien
Dec 21 '18 at 15:47




$begingroup$
@Ruslan Also, your example produces quite obvious warnings from gcc: warning: too many arguments in call to 'f' .
$endgroup$
– Reinderien
Dec 21 '18 at 15:47












$begingroup$
If you mean the footnote, then it doesn't say that int g() is the same signature as int f(void): it's simply compatible, i.e. won't cause an error when you declare as int(void) and define as int(), and vice-versa. Moreover, I've still not found any normative description of (void) as redundant (footnotes aren't normative). As for your warning, I can't reproduce it with gcc 7 with options -std=c99 -pedantic-errors -Wall -Wextra. I do get this as an error in C++ mode though, which once more confirms that it's only C++ feature, not C one.
$endgroup$
– Ruslan
Dec 21 '18 at 16:03




$begingroup$
If you mean the footnote, then it doesn't say that int g() is the same signature as int f(void): it's simply compatible, i.e. won't cause an error when you declare as int(void) and define as int(), and vice-versa. Moreover, I've still not found any normative description of (void) as redundant (footnotes aren't normative). As for your warning, I can't reproduce it with gcc 7 with options -std=c99 -pedantic-errors -Wall -Wextra. I do get this as an error in C++ mode though, which once more confirms that it's only C++ feature, not C one.
$endgroup$
– Ruslan
Dec 21 '18 at 16:03












$begingroup$
@Ruslan See stackoverflow.com/questions/41803937/func-vs-funcvoid-in-c99
$endgroup$
– Reinderien
Dec 21 '18 at 16:52




$begingroup$
@Ruslan See stackoverflow.com/questions/41803937/func-vs-funcvoid-in-c99
$endgroup$
– Reinderien
Dec 21 '18 at 16:52


















draft saved

draft discarded




















































Thanks for contributing an answer to Code Review Stack Exchange!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


Use MathJax to format equations. MathJax reference.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f210069%2fa-simple-password-manager-in-c%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

If I really need a card on my start hand, how many mulligans make sense? [duplicate]

Alcedinidae

Can an atomic nucleus contain both particles and antiparticles? [duplicate]