|
|
|
@@ -134,45 +134,47 @@ bool mpw_marshal_free(
|
|
|
|
|
return success;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool mpw_marshal_write_flat(
|
|
|
|
|
char **out, const MPMarshalledUser *user, MPMarshalError *error) {
|
|
|
|
|
static const char *mpw_marshal_write_flat(
|
|
|
|
|
const MPMarshalledUser *user, MPMarshalError *error) {
|
|
|
|
|
|
|
|
|
|
*error = (MPMarshalError){ MPMarshalErrorInternal, "Unexpected internal error." };
|
|
|
|
|
if (!user->fullName || !strlen( user->fullName )) {
|
|
|
|
|
*error = (MPMarshalError){ MPMarshalErrorMissing, "Missing full name." };
|
|
|
|
|
return false;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
MPMasterKey masterKey = user->masterKeyProvider( user->algorithm, user->fullName );
|
|
|
|
|
if (!masterKey) {
|
|
|
|
|
*error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." };
|
|
|
|
|
return false;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mpw_string_pushf( out, "# Master Password site export\n" );
|
|
|
|
|
char *out = NULL;
|
|
|
|
|
mpw_string_pushf( &out, "# Master Password site export\n" );
|
|
|
|
|
if (user->redacted)
|
|
|
|
|
mpw_string_pushf( out, "# Export of site names and stored passwords (unless device-private) encrypted with the master key.\n" );
|
|
|
|
|
mpw_string_pushf( &out,
|
|
|
|
|
"# Export of site names and stored passwords (unless device-private) encrypted with the master key.\n" );
|
|
|
|
|
else
|
|
|
|
|
mpw_string_pushf( out, "# Export of site names and passwords in clear-text.\n" );
|
|
|
|
|
mpw_string_pushf( out, "# \n" );
|
|
|
|
|
mpw_string_pushf( out, "##\n" );
|
|
|
|
|
mpw_string_pushf( out, "# Format: %d\n", 1 );
|
|
|
|
|
mpw_string_pushf( &out, "# Export of site names and passwords in clear-text.\n" );
|
|
|
|
|
mpw_string_pushf( &out, "# \n" );
|
|
|
|
|
mpw_string_pushf( &out, "##\n" );
|
|
|
|
|
mpw_string_pushf( &out, "# Format: %d\n", 1 );
|
|
|
|
|
|
|
|
|
|
char dateString[21];
|
|
|
|
|
time_t now = time( NULL );
|
|
|
|
|
if (strftime( dateString, sizeof( dateString ), "%FT%TZ", gmtime( &now ) ))
|
|
|
|
|
mpw_string_pushf( out, "# Date: %s\n", dateString );
|
|
|
|
|
mpw_string_pushf( out, "# User Name: %s\n", user->fullName );
|
|
|
|
|
mpw_string_pushf( out, "# Full Name: %s\n", user->fullName );
|
|
|
|
|
mpw_string_pushf( out, "# Avatar: %u\n", user->avatar );
|
|
|
|
|
mpw_string_pushf( out, "# Identicon: %s\n", mpw_identicon_encode( user->identicon ) );
|
|
|
|
|
mpw_string_pushf( out, "# Key ID: %s\n", mpw_id_buf( masterKey, MPMasterKeySize ) );
|
|
|
|
|
mpw_string_pushf( out, "# Algorithm: %d\n", user->algorithm );
|
|
|
|
|
mpw_string_pushf( out, "# Default Type: %d\n", user->defaultType );
|
|
|
|
|
mpw_string_pushf( out, "# Passwords: %s\n", user->redacted? "PROTECTED": "VISIBLE" );
|
|
|
|
|
mpw_string_pushf( out, "##\n" );
|
|
|
|
|
mpw_string_pushf( out, "#\n" );
|
|
|
|
|
mpw_string_pushf( out, "# Last Times Password Login\t Site\tSite\n" );
|
|
|
|
|
mpw_string_pushf( out, "# used used type name\t name\tpassword\n" );
|
|
|
|
|
mpw_string_pushf( &out, "# Date: %s\n", dateString );
|
|
|
|
|
mpw_string_pushf( &out, "# User Name: %s\n", user->fullName );
|
|
|
|
|
mpw_string_pushf( &out, "# Full Name: %s\n", user->fullName );
|
|
|
|
|
mpw_string_pushf( &out, "# Avatar: %u\n", user->avatar );
|
|
|
|
|
mpw_string_pushf( &out, "# Identicon: %s\n", mpw_identicon_encode( user->identicon ) );
|
|
|
|
|
mpw_string_pushf( &out, "# Key ID: %s\n", mpw_id_buf( masterKey, MPMasterKeySize ) );
|
|
|
|
|
mpw_string_pushf( &out, "# Algorithm: %d\n", user->algorithm );
|
|
|
|
|
mpw_string_pushf( &out, "# Default Type: %d\n", user->defaultType );
|
|
|
|
|
mpw_string_pushf( &out, "# Passwords: %s\n", user->redacted? "PROTECTED": "VISIBLE" );
|
|
|
|
|
mpw_string_pushf( &out, "##\n" );
|
|
|
|
|
mpw_string_pushf( &out, "#\n" );
|
|
|
|
|
mpw_string_pushf( &out, "# Last Times Password Login\t Site\tSite\n" );
|
|
|
|
|
mpw_string_pushf( &out, "# used used type name\t name\tpassword\n" );
|
|
|
|
|
|
|
|
|
|
// Sites.
|
|
|
|
|
for (size_t s = 0; s < user->sites_count; ++s) {
|
|
|
|
@@ -186,7 +188,8 @@ static bool mpw_marshal_write_flat(
|
|
|
|
|
mpw_free( &masterKey, MPMasterKeySize );
|
|
|
|
|
if (!(masterKey = user->masterKeyProvider( site->algorithm, user->fullName ))) {
|
|
|
|
|
*error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." };
|
|
|
|
|
return false;
|
|
|
|
|
mpw_free_string( &out );
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
resultState = mpw_site_result( masterKey, site->siteName, site->counter,
|
|
|
|
@@ -203,7 +206,7 @@ static bool mpw_marshal_write_flat(
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (strftime( dateString, sizeof( dateString ), "%FT%TZ", gmtime( &site->lastUsed ) ))
|
|
|
|
|
mpw_string_pushf( out, "%s %8ld %lu:%lu:%lu %25s\t%25s\t%s\n",
|
|
|
|
|
mpw_string_pushf( &out, "%s %8ld %lu:%lu:%lu %25s\t%25s\t%s\n",
|
|
|
|
|
dateString, (long)site->uses, (long)site->resultType, (long)site->algorithm, (long)site->counter,
|
|
|
|
|
loginState? loginState: "", site->siteName, resultState? resultState: "" );
|
|
|
|
|
mpw_free_strings( &resultState, &loginState, NULL );
|
|
|
|
@@ -211,23 +214,23 @@ static bool mpw_marshal_write_flat(
|
|
|
|
|
mpw_free( &masterKey, MPMasterKeySize );
|
|
|
|
|
|
|
|
|
|
*error = (MPMarshalError){ .type = MPMarshalSuccess };
|
|
|
|
|
return true;
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if MPW_JSON
|
|
|
|
|
|
|
|
|
|
static bool mpw_marshal_write_json(
|
|
|
|
|
char **out, const MPMarshalledUser *user, MPMarshalError *error) {
|
|
|
|
|
static const char *mpw_marshal_write_json(
|
|
|
|
|
const MPMarshalledUser *user, MPMarshalError *error) {
|
|
|
|
|
|
|
|
|
|
*error = (MPMarshalError){ MPMarshalErrorInternal, "Unexpected internal error." };
|
|
|
|
|
if (!user->fullName || !strlen( user->fullName )) {
|
|
|
|
|
*error = (MPMarshalError){ MPMarshalErrorMissing, "Missing full name." };
|
|
|
|
|
return false;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
MPMasterKey masterKey = user->masterKeyProvider( user->algorithm, user->fullName );
|
|
|
|
|
if (!masterKey) {
|
|
|
|
|
*error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." };
|
|
|
|
|
return false;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Section: "export"
|
|
|
|
@@ -261,7 +264,7 @@ static bool mpw_marshal_write_json(
|
|
|
|
|
json_object_object_add( json_file, "sites", json_sites );
|
|
|
|
|
for (size_t s = 0; s < user->sites_count; ++s) {
|
|
|
|
|
MPMarshalledSite *site = &user->sites[s];
|
|
|
|
|
if (!site->name || !strlen( site->name ))
|
|
|
|
|
if (!site->siteName || !strlen( site->siteName ))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
const char *resultState = NULL, *loginState = NULL;
|
|
|
|
@@ -271,12 +274,12 @@ static bool mpw_marshal_write_json(
|
|
|
|
|
if (!(masterKey = user->masterKeyProvider( site->algorithm, user->fullName ))) {
|
|
|
|
|
*error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." };
|
|
|
|
|
json_object_put( json_file );
|
|
|
|
|
return false;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
resultState = mpw_site_result( masterKey, site->name, site->counter,
|
|
|
|
|
resultState = mpw_site_result( masterKey, site->siteName, site->counter,
|
|
|
|
|
MPKeyPurposeAuthentication, NULL, site->resultType, site->resultState, site->algorithm );
|
|
|
|
|
loginState = mpw_site_result( masterKey, site->name, MPCounterValueInitial,
|
|
|
|
|
loginState = mpw_site_result( masterKey, site->siteName, MPCounterValueInitial,
|
|
|
|
|
MPKeyPurposeIdentification, NULL, site->loginType, site->loginState, site->algorithm );
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
@@ -288,7 +291,7 @@ static bool mpw_marshal_write_json(
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
json_object *json_site = json_object_new_object();
|
|
|
|
|
json_object_object_add( json_sites, site->name, json_site );
|
|
|
|
|
json_object_object_add( json_sites, site->siteName, json_site );
|
|
|
|
|
json_object_object_add( json_site, "type", json_object_new_int( (int32_t)site->resultType ) );
|
|
|
|
|
json_object_object_add( json_site, "counter", json_object_new_int( (int32_t)site->counter ) );
|
|
|
|
|
json_object_object_add( json_site, "algorithm", json_object_new_int( (int32_t)site->algorithm ) );
|
|
|
|
@@ -316,7 +319,7 @@ static bool mpw_marshal_write_json(
|
|
|
|
|
|
|
|
|
|
if (!user->redacted) {
|
|
|
|
|
// Clear Text
|
|
|
|
|
const char *answerState = mpw_site_result( masterKey, site->name, MPCounterValueInitial,
|
|
|
|
|
const char *answerState = mpw_site_result( masterKey, site->siteName, MPCounterValueInitial,
|
|
|
|
|
MPKeyPurposeRecovery, question->keyword, question->type, question->state, site->algorithm );
|
|
|
|
|
json_object_object_add( json_site_question, "answer", json_object_new_string( answerState ) );
|
|
|
|
|
}
|
|
|
|
@@ -337,33 +340,37 @@ static bool mpw_marshal_write_json(
|
|
|
|
|
mpw_free_strings( &resultState, &loginState, NULL );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mpw_string_pushf( out, "%s\n", json_object_to_json_string_ext( json_file,
|
|
|
|
|
const char *out = mpw_strdup( json_object_to_json_string_ext( json_file,
|
|
|
|
|
JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED | JSON_C_TO_STRING_NOSLASHESCAPE ) );
|
|
|
|
|
json_object_put( json_file );
|
|
|
|
|
mpw_free( &masterKey, MPMasterKeySize );
|
|
|
|
|
|
|
|
|
|
if (out)
|
|
|
|
|
*error = (MPMarshalError){ .type = MPMarshalSuccess };
|
|
|
|
|
return true;
|
|
|
|
|
else
|
|
|
|
|
*error = (MPMarshalError){ .type = MPMarshalErrorFormat, .description = "Couldn't encode JSON." };
|
|
|
|
|
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
bool mpw_marshal_write(
|
|
|
|
|
char **out, const MPMarshalFormat outFormat, const MPMarshalledUser *user, MPMarshalError *error) {
|
|
|
|
|
const char *mpw_marshal_write(
|
|
|
|
|
const MPMarshalFormat outFormat, const MPMarshalledUser *user, MPMarshalError *error) {
|
|
|
|
|
|
|
|
|
|
switch (outFormat) {
|
|
|
|
|
case MPMarshalFormatNone:
|
|
|
|
|
*error = (MPMarshalError){ .type = MPMarshalSuccess };
|
|
|
|
|
return false;
|
|
|
|
|
return NULL;
|
|
|
|
|
case MPMarshalFormatFlat:
|
|
|
|
|
return mpw_marshal_write_flat( out, user, error );
|
|
|
|
|
return mpw_marshal_write_flat( user, error );
|
|
|
|
|
#if MPW_JSON
|
|
|
|
|
case MPMarshalFormatJSON:
|
|
|
|
|
return mpw_marshal_write_json( out, user, error );
|
|
|
|
|
return mpw_marshal_write_json( user, error );
|
|
|
|
|
#endif
|
|
|
|
|
default:
|
|
|
|
|
*error = (MPMarshalError){ MPMarshalErrorFormat, mpw_str( "Unsupported output format: %u", outFormat ) };
|
|
|
|
|
return false;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -913,10 +920,10 @@ static MPMarshalledUser *mpw_marshal_read_json(
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (siteResultState && strlen( siteResultState ))
|
|
|
|
|
site->resultState = mpw_site_state( masterKey, site->name, site->counter,
|
|
|
|
|
site->resultState = mpw_site_state( masterKey, site->siteName, site->counter,
|
|
|
|
|
MPKeyPurposeAuthentication, NULL, site->resultType, siteResultState, site->algorithm );
|
|
|
|
|
if (siteLoginState && strlen( siteLoginState ))
|
|
|
|
|
site->loginState = mpw_site_state( masterKey, site->name, MPCounterValueInitial,
|
|
|
|
|
site->loginState = mpw_site_state( masterKey, site->siteName, MPCounterValueInitial,
|
|
|
|
|
MPKeyPurposeIdentification, NULL, site->loginType, siteLoginState, site->algorithm );
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
@@ -938,7 +945,7 @@ static MPMarshalledUser *mpw_marshal_read_json(
|
|
|
|
|
if (!user->redacted) {
|
|
|
|
|
// Clear Text
|
|
|
|
|
if (answerState && strlen( answerState ))
|
|
|
|
|
question->state = mpw_site_state( masterKey, site->name, MPCounterValueInitial,
|
|
|
|
|
question->state = mpw_site_state( masterKey, site->siteName, MPCounterValueInitial,
|
|
|
|
|
MPKeyPurposeRecovery, question->keyword, question->type, answerState, site->algorithm );
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|