diff --git a/src/keyring.cpp b/src/keyring.cpp index e2ab9f5..ed341f5 100644 --- a/src/keyring.cpp +++ b/src/keyring.cpp @@ -65,7 +65,7 @@ std::string keyring::get_keyid (const std::string&pubkey) * * Whole thing is stored in two files just like in GnuPG: * - * ${CCR_DIR}/pubring + * ${CCR_DIR}/pubkeys * ${CCR_DIR}/secrets * * CCR_DIR is taken from environment, and defaults to ${HOME}/.ccr @@ -76,12 +76,16 @@ std::string keyring::get_keyid (const std::string&pubkey) * * ( * "ccr public key storage" - * ( "key-name" pubkey_as_embedded_sencode ) - * ( "key-name" pubkey_as_embedded_sencode ) - * ( "key-name" pubkey_as_embedded_sencode ) + * ( "key-name" pubkey_in_string_encoded_as_sencode ) + * ( "key-name" pubkey_in_... ) + * ( "key-name" pubkey ) * ... * ) * + * Reason for pubkeys not to be _embedded_ in sencode is for simpler KeyID + * computation. We'd either need too much encoding/decoding or some ugly magic + * otherwise. + * * Private keys are stored together with their pubkeys, so that they don't have * to be generated everytime user asks for them: * @@ -100,17 +104,59 @@ std::string keyring::get_keyid (const std::string&pubkey) static std::string get_user_dir() { const char*tmp = getenv ("CCR_DIR"); - if (tmp) return tmp; + if (tmp) return std::string (tmp); const char*home = getenv ("HOME"); - if (home) return home + "/.ccr"; + if (home) return std::string (home) + "/.ccr"; return "./.ccr"; //fallback for desolate systems } #include #include +#include #include +#include -static bool prepare_user_dir (const string&dir) +#include + +#define SECRETS_FILENAME "/secrets" +#define PUBKEYS_FILENAME "/pubkeys" + + +/* + * prepares the user directory with empty files and similar stuff. + * + * We try to setup file permissions properly here and don't care about it later + * (so that the user can override the default value by easy unixy way) + */ +static bool ensure_empty_sencode_file (const std::string&fn, mode_t mode) +{ + struct stat st; + if (stat (fn.c_str(), &st) ) { + if (errno != ENOENT) + return false; + + //if it simply doesn't exist, create it + sencode_list l; + std::string emptyfile = l.encode(); + + int fd, res; + fd = creat (fn.c_str(), mode); + if (fd < 0) return false; + res = write (fd, emptyfile.c_str(), emptyfile.length() ); + if (close (fd) ) return false; + if (res != emptyfile.length() ) return false; + + } else { + if (!S_ISREG (st.st_mode) ) + return false; + } + + if (access (fn.c_str(), R_OK | W_OK) ) return false; + + return true; +} + +static bool prepare_user_dir (const std::string&dir) { //try to create the directory mkdir (dir.c_str(), 0777); @@ -123,25 +169,215 @@ static bool prepare_user_dir (const string&dir) if (!S_ISDIR (st.st_mode) ) return false; + //create empty key storages, if not present + std::string fn; + + ensure_empty_sencode_file (dir + PUBKEYS_FILENAME, S_IRUSR | S_IWUSR); + ensure_empty_sencode_file (dir + SECRETS_FILENAME, S_IRUSR | S_IWUSR); return true; //seems m'kay } +static bool file_get_sencode (const std::string&fn, sencode**out) +{ + //check whether it is a file first + struct stat st; + if (stat (fn.c_str(), &st) ) + return false; + + if (!S_ISREG (st.st_mode) ) + return false; + + //not we got the size, prepare buffer space + std::string data; + data.resize (st.st_size, 0); + + std::ifstream in (fn.c_str(), std::ios::in | std::ios::binary); + if (!in) return false; + in.read (&data[0], st.st_size); + in.close(); + + if (!sencode_decode (data, out) ) + return false; + + return true; +} + +static bool file_put_sencode (const std::string&fn, sencode*in) +{ + std::string data = in->encode(); + + std::ofstream out (fn.c_str(), std::ios::out | std::ios::binary); + if (!out) return false; + out.write (data.c_str(), data.length() ); + if (!out.good() ) return false; + out.close(); + if (!out.good() ) return false; + + return true; +} + bool keyring::load() { + std::string dir = get_user_dir(); + std::string fn; + sencode_list*L; + /* + * pubkeys loading + */ + fn = dir + PUBKEYS_FILENAME; + sencode* pubkeys; + if (!file_get_sencode (fn, &pubkeys) ) + return false; + + L = dynamic_cast (pubkeys); + if (!L) goto pubkeys_fail; + + //parse all pubkey entries + for (std::vector::iterator + i = L->items.begin(), e = L->items.end(); + i != e; ++i) { + + sencode_list*entry = dynamic_cast (*i); + if (!entry) goto pubkeys_fail; + + if (entry->items.size() != 2) goto pubkeys_fail; + + sencode_bytes + *ident = dynamic_cast (entry->items[0]), + *pubkey = dynamic_cast (entry->items[1]); + + if (! (ident && pubkey) ) goto pubkeys_fail; + + std::string keyid = get_keyid (pubkey->b); + sencode*key; + if (!sencode_decode (pubkey->b, &key) ) + goto pubkeys_fail; + + pubs[keyid] = pubkey_entry (keyid, ident->b, key); + } + + sencode_destroy (pubkeys); + + + /* + * keypairs loading + */ + + fn = dir + SECRETS_FILENAME; + sencode*keypairs; + if (!file_get_sencode (fn, &keypairs) ) + return false; + + L = dynamic_cast (keypairs); + if (!L) goto keypairs_fail; + + //entries + for (std::vector::iterator + i = L->items.begin(), e = L->items.end(); + i != e; ++i) { + + sencode_list*entry = dynamic_cast (*i); + if (!entry) goto keypairs_fail; + if (entry->items.size() != 3) goto keypairs_fail; + + sencode_bytes + *ident = dynamic_cast (entry->items[0]), + *privkey = dynamic_cast (entry->items[1]), + *pubkey = dynamic_cast (entry->items[2]); + + if (! (ident && privkey && pubkey) ) goto keypairs_fail; + + std::string keyid = get_keyid (pubkey->b); + sencode *priv, *pub; + if (!sencode_decode (privkey->b, &priv) ) + goto keypairs_fail; + if (!sencode_decode (pubkey->b, &pub) ) { + sencode_destroy (priv); + goto keypairs_fail; + } + + pairs[keyid] = keypair_entry (keyid, ident->b, pub, priv); + } + + + sencode_destroy (keypairs); + return true; + +pubkeys_fail: + sencode_destroy (pubkeys); + return false; + +keypairs_fail: + sencode_destroy (keypairs); + return false; } bool keyring::save() { + std::string dir, fn; + sencode_list*L; + bool res; + dir = get_user_dir(); + + /* + * pubkeys + */ + L = new sencode_list(); + for (std::map::iterator + i = pubs.begin(), e = pubs.end(); + i != e; ++i) { + sencode_list*a = new sencode_list(); + a->items.resize (2); + a->items[0] = new sencode_bytes (i->second.name); + a->items[1] = new sencode_bytes (i->second.key->encode() ); + L->items.push_back (a); + } + + //save them + fn = dir + PUBKEYS_FILENAME; + res = file_put_sencode (fn, L); + sencode_destroy (L); + if (!res) return false; + + /* + * keypairs + */ + + L = new sencode_list(); + for (std::map::iterator + i = pairs.begin(), e = pairs.end(); + i != e; ++i) { + sencode_list*a = new sencode_list; + a->items.resize (3); + a->items[0] = new sencode_bytes (i->second.pub.name); + a->items[1] = new sencode_bytes (i->second.privkey->encode() ); + a->items[2] = new sencode_bytes (i->second.pub.key->encode() ); + L->items.push_back (a); + } + + //save + fn = dir + SECRETS_FILENAME; + res = file_put_sencode (fn, L); + sencode_destroy (L); + if (!res) return false; + + return true; } bool keyring::open() { + //ensure the existence of file structure + + + //create the lock + } bool keyring::close() { + //close the lock } diff --git a/src/keyring.h b/src/keyring.h index da3bd97..1c8aad4 100644 --- a/src/keyring.h +++ b/src/keyring.h @@ -26,7 +26,7 @@ class keyring { - int fd_priv, fd_pub; + int lockfd; public: struct pubkey_entry { sencode *key; @@ -66,7 +66,7 @@ public: std::map pairs; keyring() { - fd_priv = fd_pub = -1; + lockfd = -1; } ~keyring() {