/* * Copyright (C) 2004 Morten Fjord-Larsen * Copyright (C) 2005 Kouji TAKAO * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include "gpass/entry.h" #include "gpass/root-entry.h" #include "gpass/file04.h" /*********************************************************** * * GPassFile04 * ***********************************************************/ #define MAGIC "GNOME Password Manager\n" static GObjectClass *parent_db_class = NULL; static void gpass_file04_instance_init(GTypeInstance *instance, gpointer g_class) { GPassFile04 *self = GPASS_FILE04(instance); self->path = NULL; self->master_password = NULL; } enum { DB_PROP_0, DB_PROP_PATH, DB_PROP_MASTER_PASSWORD, }; static void gpass_file04_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GPassFile04 *self = GPASS_FILE04(object); switch (prop_id) { case DB_PROP_PATH: g_free(self->path); self->path = g_value_dup_string(value); break; case DB_PROP_MASTER_PASSWORD: g_free(self->master_password); self->master_password = g_value_dup_string(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } } static void gpass_file04_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GPassFile04 *self = GPASS_FILE04(object); switch (prop_id) { case DB_PROP_PATH: g_value_set_string(value, self->path); break; case DB_PROP_MASTER_PASSWORD: g_value_set_string(value, self->master_password); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } } static void gpass_file04_instance_finalize(GObject *object) { GPassFile04 *self = GPASS_FILE04(object); g_free(self->path); g_free(self->master_password); G_OBJECT_CLASS(parent_db_class)->finalize(object); } static void gpass_file04_class_init(gpointer g_class, gpointer g_class_data) { GObjectClass *gobject_class = G_OBJECT_CLASS(g_class); parent_db_class = g_type_class_peek_parent(g_class); gobject_class->set_property = gpass_file04_set_property; gobject_class->get_property = gpass_file04_get_property; gobject_class->finalize = gpass_file04_instance_finalize; g_object_class_install_property (gobject_class, DB_PROP_PATH, g_param_spec_string("path", _("Path"), _("The path of db"), NULL, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, DB_PROP_MASTER_PASSWORD, g_param_spec_string("master_password", _("Master password"), _("The master password"), NULL, G_PARAM_READWRITE)); } GType gpass_file04_get_type(void) { static GType type = 0; if (type == 0) { static const GTypeInfo info = { sizeof(GPassFile04Class), NULL, NULL, gpass_file04_class_init, NULL, NULL, sizeof(GPassFile04), 0, gpass_file04_instance_init }; type = g_type_register_static(G_TYPE_OBJECT, "GPassFile04Type", &info, 0); } return type; } static GError * db_open_read(const gchar *path, const gchar *master_password, GPassDecryptStream **decrypt) { GPassDecryptStream *result; FILE *fp; gchar buf[sizeof(MAGIC)]; ssize_t read_len; GError *error = NULL; if ((fp = fopen(path, "r")) == NULL) { g_set_error(&error, 0, errno, g_strerror(errno)); return error; } error = gpass_decrypt_stream_open(&result, fp, master_password); if (error != NULL) { fclose(fp); return error; } error = gpass_decrypt_stream_read(result, buf, sizeof(MAGIC) - 1, &read_len); if (error != NULL) { goto end; } if (read_len != sizeof(MAGIC) - 1) { g_set_error(&error, 0, 0, _("Premature end of file")); goto end; } if (strncmp(buf, MAGIC, sizeof(MAGIC) - 1)) { g_set_error(&error, 0, 0, _("Incorrect password!")); goto end; } end: if (error == NULL) { *decrypt = result; } else { gpass_decrypt_stream_close(result); } return error; } static GError * db_open_write(const gchar *path, const gchar *master_password, GPassEncryptStream **encrypt) { GPassEncryptStream *result; FILE *fp; int fd; GError *error = NULL; if ((fp = fopen(path, "w")) == NULL) { g_set_error(&error, 0, errno, g_strerror(errno)); return error; } fd = fileno(fp); if (fchmod(fd, 0600)) { g_set_error(&error, 0, errno, g_strerror(errno)); fclose(fp); return error; } error = gpass_encrypt_stream_open(&result, fp, master_password); if (error != NULL) { fclose(fp); return error; } error = gpass_encrypt_stream_write(result, MAGIC, sizeof(MAGIC) - 1); if (error != NULL) { goto end; } end: if (error == NULL) { *encrypt = result; } else { gpass_encrypt_stream_close(result); } return error; } GError * gpass_file04_create(const gchar *path, const gchar *master_password) { gchar *dirname; GPassEncryptStream *encrypt; GError *error = NULL; dirname = g_path_get_dirname(path); if (!g_file_test(dirname, G_FILE_TEST_IS_DIR)) { if (mkdir(dirname, 0700)) { g_set_error(&error, 0, errno, g_strerror(errno)); goto end; } } error = db_open_write(path, master_password, &encrypt); if (error != NULL) { goto end; } gpass_encrypt_stream_close(encrypt); end: g_free(dirname); return error; } GError * gpass_file04_open(GPassFile04 **self, const gchar *path, const gchar *master_password) { GPassDecryptStream *decrypt; GError *error = NULL; error = db_open_read(path, master_password, &decrypt); if (error == NULL) { *self = g_object_new(GPASS_TYPE_DB, "path", path, "master_password", master_password, NULL); gpass_decrypt_stream_close(decrypt); } return error; } GError * gpass_file04_read(GPassFile04 *self, GPassEntryFactory *factory, GPassEntry **entries) { GPassEntry *result; GPassDecryptStream *decrypt; GPassFile04Reader *reader; GError *error; error = db_open_read(self->path, self->master_password, &decrypt); if (error != NULL) { return error; } reader = g_object_new(GPASS_TYPE_DB_READER, "decrypt_stream", decrypt, "entry_factory", factory, NULL); result = g_object_new(GPASS_TYPE_ROOT_ENTRY, NULL); error = gpass_file04_reader_read(reader, result); g_object_unref(reader); if (error != NULL) { g_object_unref(result); return error; } *entries = result; return NULL; } GError * gpass_file04_backup(GPassFile04 *self, const gchar *backup_path) { GError *error = NULL; if (g_file_test(backup_path, G_FILE_TEST_EXISTS)) { if (unlink(backup_path) < 0) { g_set_error(&error, 0, errno, g_strerror(errno)); return error; } } if (rename(self->path, backup_path) == -1) { g_set_error(&error, 0, errno, g_strerror(errno)); } return error; } GError * gpass_file04_restore(GPassFile04 *self, const gchar *backup_path) { GError *error = NULL; if (g_file_test(backup_path, G_FILE_TEST_EXISTS)) { if (rename(backup_path, self->path) == -1) { g_set_error(&error, 0, errno, g_strerror(errno)); } } return error; } GError * gpass_file04_write(GPassFile04 *self, GPassEntry *entries) { gchar *backup_path; GPassEncryptStream *encrypt; GPassFile04Writer *writer; GError *error; backup_path = g_strdup_printf("%s.bak", self->path); error = gpass_file04_backup(self, backup_path); if (error != NULL) { g_free(backup_path); return error; } error = db_open_write(self->path, self->master_password, &encrypt); if (error != NULL) { goto end; } writer = g_object_new(GPASS_TYPE_DB_WRITER, "encrypt_stream", encrypt, NULL); error = gpass_file04_writer_write(writer, entries); g_object_unref(writer); end: if (error != NULL) { gpass_file04_restore(self, backup_path); } g_free(backup_path); return error; } void gpass_file04_close(GPassFile04 *self) { g_object_unref(self); } /*********************************************************** * * GPassFile04Reader * ***********************************************************/ static GObjectClass *parent_reader_class = NULL; static void gpass_file04_reader_instance_init(GTypeInstance *instance, gpointer g_class) { GPassFile04Reader *self = GPASS_FILE04_READER(instance); self->decrypt = NULL; self->factory = NULL; } enum { READER_PROP_0, READER_PROP_DECRYPT_STREAM, READER_PROP_ENTRY_FACTORY }; static void gpass_file04_reader_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GPassFile04Reader *self = GPASS_FILE04_READER(object); switch (prop_id) { case READER_PROP_DECRYPT_STREAM: self->decrypt = g_value_get_object(value); break; case READER_PROP_ENTRY_FACTORY: self->factory = g_value_get_object(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } } static void gpass_file04_reader_instance_finalize(GObject *object) { GPassFile04Reader *self = GPASS_FILE04_READER(object); if (self->decrypt != NULL) { gpass_decrypt_stream_close(self->decrypt); } G_OBJECT_CLASS(parent_reader_class)->finalize(object); } static void gpass_file04_reader_class_init(gpointer g_class, gpointer g_class_data) { GObjectClass *gobject_class = G_OBJECT_CLASS(g_class); parent_reader_class = g_type_class_peek_parent(g_class); gobject_class->set_property = gpass_file04_reader_set_property; gobject_class->finalize = gpass_file04_reader_instance_finalize; g_object_class_install_property (gobject_class, READER_PROP_DECRYPT_STREAM, g_param_spec_object("decrypt_stream", _("GPassDecryptStream"), _("The object of GPassDecryptStream"), GPASS_TYPE_DECRYPT_STREAM, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (gobject_class, READER_PROP_ENTRY_FACTORY, g_param_spec_object("entry_factory", _("GPassEntryFactory"), _("The object of GPassEntryFactory"), GPASS_TYPE_ENTRY_FACTORY, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); } GType gpass_file04_reader_get_type(void) { static GType type = 0; if (type == 0) { static const GTypeInfo info = { sizeof(GPassFile04ReaderClass), NULL, NULL, gpass_file04_reader_class_init, NULL, NULL, sizeof(GPassFile04Reader), 0, gpass_file04_reader_instance_init }; type = g_type_register_static(G_TYPE_OBJECT, "GPassFile04Reader", &info, 0); } return type; } static GError * reader_read_uint(GPassDecryptStream *decrypt, GString **buffer, guint *val) { unsigned int scanned; int result; GError *error = NULL; error = gpass_decrypt_stream_read_line(decrypt, buffer); if (error != NULL) { return error; } result = sscanf((*buffer)->str, "%u", &scanned); if (result == 0) { g_set_error(&error, 0, 0, _("Password file corrupted!")); return error; } *val = (guint) scanned; return NULL; } static GError * reader_read_entry(GPassFile04Reader *self, GPassEntry **entry) { GPassEntry *result; GString *line; guint val; gchar *buf; ssize_t read_len; GError *error = NULL; if (gpass_decrypt_stream_eof(self->decrypt)) { g_set_error(&error, 0, 0, _("Premature end of file")); return error; } error = gpass_entry_factory_create_entry(self->factory, "general", &result); if (error != NULL) { return error; } line = g_string_new(NULL); error = gpass_decrypt_stream_read_line(self->decrypt, &line); if (error != NULL) { goto end; } line = g_string_truncate(line, line->len - 1); g_object_set(result, "name", line->str, NULL); error = gpass_decrypt_stream_read_line(self->decrypt, &line); if (error != NULL) { goto end; } line = g_string_truncate(line, line->len - 1); g_object_set(result, "username", line->str, NULL); error = gpass_decrypt_stream_read_line(self->decrypt, &line); if (error != NULL) { goto end; } line = g_string_truncate(line, line->len - 1); g_object_set(result, "password", line->str, NULL); error = gpass_decrypt_stream_read_line(self->decrypt, &line); if (error != NULL) { goto end; } line = g_string_truncate(line, line->len - 1); g_object_set(result, "hostname", line->str, NULL); error = reader_read_uint(self->decrypt, &line, &val); if (error != NULL) { goto end; } g_object_set(result, "creation-time", val, NULL); error = reader_read_uint(self->decrypt, &line, &val); if (error != NULL) { goto end; } g_object_set(result, "modification-time", val, NULL); error = reader_read_uint(self->decrypt, &line, &val); if (error != NULL) { goto end; } if (val == 0) { g_object_set(result, "expiration", FALSE, NULL); } else { g_object_set(result, "expiration", TRUE, "expiration-time", val, NULL); } error = reader_read_uint(self->decrypt, &line, &val); if (error != NULL) { goto end; } buf = g_malloc(val); error = gpass_decrypt_stream_read(self->decrypt, buf, val, &read_len); if (error != NULL) { goto end; } if (read_len != val) { g_set_error(&error, 0, 0, _("Premature end of file")); goto end; } buf[val - 1] = '\0'; g_object_set(result, "description", buf, NULL); end: g_free(buf); if (error != NULL) { g_object_unref(result); } else { *entry = result; } g_string_free(line, TRUE); return error; } GError * gpass_file04_reader_read(GPassFile04Reader *self, GPassEntry *entries) { GPassEntry *entry; while (!gpass_decrypt_stream_eof(self->decrypt)) { GError *error = reader_read_entry(self, &entry); if (error != NULL) { return error; } gpass_entry_append(entries, entry); } return NULL; } /*********************************************************** * * GPassFile04Writer * ***********************************************************/ static GObjectClass *parent_writer_class = NULL; static void gpass_file04_writer_instance_init(GTypeInstance *instance, gpointer g_class) { GPassFile04Writer *self = GPASS_FILE04_WRITER(instance); self->encrypt = NULL; } enum { WRITER_PROP_0, WRITER_PROP_ENCRYPT_STREAM }; static void gpass_file04_writer_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GPassFile04Writer *self = GPASS_FILE04_WRITER(object); switch (prop_id) { case WRITER_PROP_ENCRYPT_STREAM: self->encrypt = g_value_get_object(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } } static void gpass_file04_writer_instance_finalize(GObject *object) { GPassFile04Writer *self = GPASS_FILE04_WRITER(object); if (self->encrypt != NULL) { gpass_encrypt_stream_close(self->encrypt); } G_OBJECT_CLASS(parent_writer_class)->finalize(object); } static void gpass_file04_writer_class_init(gpointer g_class, gpointer g_class_data) { GObjectClass *gobject_class = G_OBJECT_CLASS(g_class); parent_writer_class = g_type_class_peek_parent(g_class); gobject_class->set_property = gpass_file04_writer_set_property; gobject_class->finalize = gpass_file04_writer_instance_finalize; g_object_class_install_property (gobject_class, WRITER_PROP_ENCRYPT_STREAM, g_param_spec_object("encrypt_stream", _("GPassEncryptStream"), _("The object of GPassEncryptStream"), GPASS_TYPE_ENCRYPT_STREAM, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); } GType gpass_file04_writer_get_type(void) { static GType type = 0; if (type == 0) { static const GTypeInfo info = { sizeof(GPassFile04WriterClass), NULL, NULL, gpass_file04_writer_class_init, NULL, NULL, sizeof(GPassFile04Writer), 0, gpass_file04_writer_instance_init }; type = g_type_register_static(G_TYPE_OBJECT, "GPassFile04Writer", &info, 0); } return type; } static GError * writer_write_printf(GPassEncryptStream *encrypt, const char *format, ...) { va_list ap; gchar *buf; GError *error; va_start(ap, format); buf = g_strdup_vprintf(format, ap); va_end(ap); error = gpass_encrypt_stream_write(encrypt, buf, strlen(buf)); g_free(buf); return error; } GError * writer_write_entry(GPassFile04Writer *self, GPassEntry *entry) { gchar *str; guint t; GError *error = NULL; g_object_get(entry, "name", &str, NULL); error = writer_write_printf(self->encrypt, "%s\n", str); if (error != NULL) { return error; } g_object_get(entry, "username", &str, NULL); error = writer_write_printf(self->encrypt, "%s\n", str); if (error != NULL) { return error; } g_object_get(entry, "password", &str, NULL); error = writer_write_printf(self->encrypt, "%s\n", str); if (error != NULL) { return error; } g_object_get(entry, "hostname", &str, NULL); error = writer_write_printf(self->encrypt, "%s\n", str); if (error != NULL) { return error; } g_object_get(entry, "creation-time", &t, NULL); error = writer_write_printf(self->encrypt, "%u\n", t); if (error != NULL) { return error; } g_object_get(entry, "modification-time", &t, NULL); error = writer_write_printf(self->encrypt, "%u\n", t); if (error != NULL) { return error; } g_object_get(entry, "expiration-time", &t, NULL); error = writer_write_printf(self->encrypt, "%u\n", t); if (error != NULL) { return error; } g_object_get(entry, "description", &str, NULL); error = writer_write_printf(self->encrypt, "%u\n", strlen(str) + 1); if (error != NULL) { g_free(str); return error; } error = writer_write_printf(self->encrypt, "%s\n", str); if (error != NULL) { return error; } return NULL; } GError * gpass_file04_writer_write(GPassFile04Writer *writer, GPassEntry *entries) { GPassEntry *p; for (p = gpass_entry_first_child(entries); p != NULL; p = gpass_entry_next_sibling(p)) { GError *error = writer_write_entry(writer, p); if (error != NULL) { return error; } } return NULL; }