# HG changeset patch # User Guido Berhoerster # Date 1501250037 -7200 # Node ID 17fb30016e64fd8f527b50c7b9d4cbae81cf1777 # Parent 60c8ab006e551a576bf6ad0689920940f1a43ae1 Enable access to record and file metadata Add info command to show file metadata. Enable display of creation and modification dates of records. diff -r 60c8ab006e55 -r 17fb30016e64 cmd.c --- a/cmd.c Fri Jul 28 09:53:46 2017 +0200 +++ b/cmd.c Fri Jul 28 15:53:57 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Guido Berhoerster + * Copyright (C) 2017 Guido Berhoerster * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -33,12 +33,16 @@ #endif /* READPASSPHRASE_H */ #include #include +#include #include #include "cmd.h" #include "pwfile.h" #include "util.h" +#define TIME_FORMAT "%Y-%m-%dT%TZ" +#define TIME_SIZE (4 + 1 + 2 + 1 + 2 + 1 + 8 + 1 + 1) + enum field_type { FIELD_UNKNOWN = -1, FIELD_GROUP, @@ -46,9 +50,12 @@ FIELD_USERNAME, FIELD_PASSWORD, FIELD_NOTES, - FIELD_URL + FIELD_URL, + FIELD_MTIME, + FIELD_CTIME }; +static enum cmd_return cmd_info(struct pwm_ctx *, int, char *[]); static enum cmd_return cmd_list(struct pwm_ctx *, int, char *[]); static enum cmd_return cmd_create(struct pwm_ctx *, int, char *[]); static enum cmd_return cmd_modify(struct pwm_ctx *, int, char *[]); @@ -68,7 +75,9 @@ "username", "password", "notes", - "url" + "url", + "ctime", + "mtime" }; static const char *field_labels[] = { @@ -77,10 +86,14 @@ "Username: ", "Password: ", "Notes: ", - "URL: " + "URL: ", + "Created: ", + "Modified: " }; static struct cmd cmds[] = { + { "i", "info", "info", "Show metadata information about the current file", + cmd_info }, { "ls", "list", "list", "List entries", cmd_list }, { "c", "create", "create field=value ...", "Create entry", cmd_create }, { "m", "modify", "modify id field=value ...", "Modify entry", cmd_modify }, @@ -150,7 +163,7 @@ record->url = value; break; default: - break; + return (FIELD_UNKNOWN); } return (i); } @@ -177,6 +190,37 @@ } static enum cmd_return +cmd_info(struct pwm_ctx *ctx, int argc, char *argv[]) +{ + struct metadata *metadata; + struct tm *tm; + char timebuf[TIME_SIZE]; + + if (argc != 1) { + return (CMD_USAGE); + } + + metadata = pwfile_get_metadata(ctx); + printf("Format: 0x%04x\n", metadata->version); + if (metadata->user != NULL) { + printf("User: %s\n", metadata->user); + } + if (metadata->user != NULL) { + printf("Host: %s\n", metadata->host); + } + if (metadata->user != NULL) { + printf("Application: %s\n", metadata->application); + } + tm = gmtime(&metadata->timestamp); + strftime(timebuf, sizeof (timebuf), TIME_FORMAT, tm); + printf("Last Saved: %s\n", timebuf); + + pwfile_destroy_metadata(metadata); + + return (CMD_OK); +} + +static enum cmd_return cmd_list(struct pwm_ctx *ctx, int argc, char *argv[]) { union list_item **list; @@ -290,6 +334,9 @@ static void print_record(struct record *record, int fields[], int show_labels, FILE *fp) { + struct tm *tm; + char timebuf[TIME_SIZE]; + if (fields[FIELD_TITLE]) { if (print_field(field_labels[FIELD_TITLE], record->title, show_labels, fp) != 0) { @@ -326,6 +373,22 @@ return; } } + if (fields[FIELD_CTIME]) { + tm = gmtime(&record->ctime); + strftime(timebuf, sizeof (timebuf), TIME_FORMAT, tm); + if (print_field(field_labels[FIELD_CTIME], timebuf, + show_labels, fp)) { + return; + } + } + if (fields[FIELD_MTIME]) { + tm = gmtime(&record->mtime); + strftime(timebuf, sizeof (timebuf), TIME_FORMAT, tm); + if (print_field(field_labels[FIELD_MTIME], timebuf, + show_labels, fp)) { + return; + } + } } static enum cmd_return diff -r 60c8ab006e55 -r 17fb30016e64 pwfile.c --- a/pwfile.c Fri Jul 28 09:53:46 2017 +0200 +++ b/pwfile.c Fri Jul 28 15:53:57 2017 +0200 @@ -23,6 +23,7 @@ #include "compat.h" +#include #ifdef HAVE_ERR_H #include #endif /* HAVE_ERR_H */ @@ -597,6 +598,94 @@ free(list); } +static int +parse_user_host(const char *user_host, char **userp, char **hostp) +{ + size_t user_host_len; + size_t i; + unsigned int user_len; + + user_host_len = strlen(user_host); + if (user_host_len < 4) { + return (-1); + } + for (i = 0; i < 4; i++) { + if (!isxdigit(user_host[i])) { + return (-1); + } + } + if (sscanf(user_host, "%04x", &user_len) != 1) { + return (-1); + } + if (4 + (size_t)user_len > user_host_len) { + return (-1); + } + + xasprintf(userp, "%.*s", (int)user_len, user_host + 4); + xasprintf(hostp, "%s", user_host + 4 + user_len); + + return (0); +} + +struct metadata * +pwfile_get_metadata(struct pwm_ctx *ctx) +{ + struct metadata *metadata; + struct pws3_field *version_field; + struct pws3_field *save_app_field; + struct pws3_field *save_timestamp_field; + struct pws3_field *save_user_field; + struct pws3_field *save_host_field; + struct pws3_field *save_user_host_field; + + metadata = xmalloc(sizeof (struct metadata)); + + version_field = pws3_file_get_header_field(ctx->file, + PWS3_HEADER_FIELD_VERSION); + metadata->version = pws3_field_get_uint16(version_field); + + save_app_field = pws3_file_get_header_field(ctx->file, + PWS3_HEADER_FIELD_SAVE_APPLICATION); + metadata->application = (save_app_field != NULL) ? + xstrdup(pws3_field_get_text(save_app_field)) : NULL; + + save_timestamp_field = pws3_file_get_header_field(ctx->file, + PWS3_HEADER_FIELD_SAVE_TIMESTAMP); + metadata->timestamp = (save_timestamp_field != NULL) ? + pws3_field_get_time(save_timestamp_field) : 0; + + save_user_field = pws3_file_get_header_field(ctx->file, + PWS3_HEADER_FIELD_SAVE_USER); + save_host_field = pws3_file_get_header_field(ctx->file, + PWS3_HEADER_FIELD_SAVE_HOST); + save_user_host_field = pws3_file_get_header_field(ctx->file, + PWS3_HEADER_FIELD_SAVE_USER_HOST); + metadata->user = NULL; + metadata->host = NULL; + if ((save_user_field != NULL) && (save_host_field != NULL)) { + metadata->user = xstrdup(pws3_field_get_text(save_user_field)); + metadata->host = xstrdup(pws3_field_get_text(save_host_field)); + } else if (save_user_host_field != NULL) { + parse_user_host(pws3_field_get_text(save_user_host_field), + &metadata->user, &metadata->host); + } + + return (metadata); +} + +void +pwfile_destroy_metadata(struct metadata *metadata) +{ + if (metadata == NULL) { + return; + } + + free(metadata->user); + free(metadata->host); + free(metadata->application); + free(metadata); +} + static void update_record(struct pws3_record *pws3_record, struct record *record) { @@ -820,6 +909,8 @@ struct record *record; const unsigned char *uuid; struct pws3_record *pws3_record; + struct pws3_field *ctime_field; + struct pws3_field *mtime_field; struct pws3_field *title_field; struct pws3_field *group_field; struct pws3_field *username_field; @@ -835,6 +926,16 @@ record = xmalloc(sizeof (struct record)); + ctime_field = pws3_record_get_field(pws3_record, + PWS3_RECORD_FIELD_CREATION_TIME); + record->ctime = (ctime_field != NULL) ? + pws3_field_get_time(ctime_field) : (time_t)0; + + mtime_field = pws3_record_get_field(pws3_record, + PWS3_RECORD_FIELD_MODIFICATION_TIME); + record->mtime = (mtime_field != NULL) ? + pws3_field_get_time(mtime_field) : (time_t)0; + title_field = pws3_record_get_field(pws3_record, PWS3_RECORD_FIELD_TITLE); record->title = (title_field != NULL) ? diff -r 60c8ab006e55 -r 17fb30016e64 pwfile.h --- a/pwfile.h Fri Jul 28 09:53:46 2017 +0200 +++ b/pwfile.h Fri Jul 28 15:53:57 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Guido Berhoerster + * Copyright (C) 2017 Guido Berhoerster * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -51,6 +51,14 @@ struct record_id_tree; +struct metadata { + int version; + char *user; + char *host; + char *application; + time_t timestamp; +}; + struct record { char *title; char *group; @@ -58,6 +66,8 @@ char *password; char *notes; char *url; + time_t ctime; + time_t mtime; }; void pwfile_init(struct pwm_ctx *ctx); @@ -66,6 +76,8 @@ int pwfile_write_file(struct pwm_ctx *); union list_item ** pwfile_create_list(struct pwm_ctx *); void pwfile_destroy_list(union list_item **); +struct metadata * pwfile_get_metadata(struct pwm_ctx *); +void pwfile_destroy_metadata(struct metadata *); int pwfile_create_record(struct pwm_ctx *, struct record *); int pwfile_modify_record(struct pwm_ctx *, unsigned int, struct record *); diff -r 60c8ab006e55 -r 17fb30016e64 pwm.1.xml --- a/pwm.1.xml Fri Jul 28 09:53:46 2017 +0200 +++ b/pwm.1.xml Fri Jul 28 15:53:57 2017 +0200 @@ -34,7 +34,7 @@ guido+pwm@berhoerster.name - 3 February, 2017 + 28 July, 2017 pwm @@ -80,11 +80,11 @@ character encoding. Output format - The show command displays selected fields by - printing the field name followed by a colon, one or more space characters - and the field's verbatim content to the standard output stream. Field - values may contain newlines, non-printable and/or control - characters. + The show and info commands + display fields by printing the field name followed by a colon, one or + more space characters and the field's verbatim content to the standard + output stream. Field content may contain newlines, non-printable and/or + control characters. The pipe prints the verbatim field content to the standard input stream of the given command. Error messages are printed to the standard error stream. @@ -128,16 +128,52 @@ Fields - The following entry fields are supported: - - group - title - username - password - notes - url - - + The following entry fields are supported: + + Fields and their identifiers + + + + Field + Field Identifier + + + + + Group + group + + + Title + title + + + Username + username + + + Password + password + + + Notes + notes + + + URL + url + + + Creation Time + ctime + + + Modification Time + mtime + + + +
Other, existing fields specified by the PasswordSafe file format will be preserved but cannot be displayed or modified.
@@ -374,6 +410,22 @@ + Show metadata information + + + info + + + i + + + Display metadata information such as the user who last wrote + to the database, the time when the database was last written to, + and the host on which the password database was last written + to. + + + Write database @@ -423,6 +475,15 @@ 5 + + + LOGNAME + + + The name of the logged in user which is recorded when writing + the password database + +