Support updating the mpw, showing loginName, adding new sites & questions, fix password memory leak.
This commit is contained in:
parent
a261538602
commit
c044ae79cd
@ -49,17 +49,17 @@ MPMarshalledUser *mpw_marshall_user(
|
|||||||
};
|
};
|
||||||
|
|
||||||
MPMarshalledSite *mpw_marshall_site(
|
MPMarshalledSite *mpw_marshall_site(
|
||||||
MPMarshalledUser *marshalledUser,
|
MPMarshalledUser *user, const char *siteName, const MPPasswordType passwordType,
|
||||||
const char *siteName, const MPPasswordType siteType, const uint32_t siteCounter, const MPAlgorithmVersion algorithmVersion) {
|
const uint32_t siteCounter, const MPAlgorithmVersion algorithmVersion) {
|
||||||
|
|
||||||
if (!siteName || !mpw_realloc( &marshalledUser->sites, NULL, sizeof( MPMarshalledSite ) * ++marshalledUser->sites_count ))
|
if (!siteName || !mpw_realloc( &user->sites, NULL, sizeof( MPMarshalledSite ) * ++user->sites_count ))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
MPMarshalledSite *site = &marshalledUser->sites[marshalledUser->sites_count - 1];
|
MPMarshalledSite *site = &user->sites[user->sites_count - 1];
|
||||||
*site = (MPMarshalledSite){
|
*site = (MPMarshalledSite){
|
||||||
.name = strdup( siteName ),
|
.name = strdup( siteName ),
|
||||||
.content = NULL,
|
.content = NULL,
|
||||||
.type = siteType,
|
.type = passwordType,
|
||||||
.counter = siteCounter,
|
.counter = siteCounter,
|
||||||
.algorithm = algorithmVersion,
|
.algorithm = algorithmVersion,
|
||||||
|
|
||||||
@ -77,12 +77,12 @@ MPMarshalledSite *mpw_marshall_site(
|
|||||||
};
|
};
|
||||||
|
|
||||||
MPMarshalledQuestion *mpw_marshal_question(
|
MPMarshalledQuestion *mpw_marshal_question(
|
||||||
MPMarshalledSite *marshalledSite, const char *keyword) {
|
MPMarshalledSite *site, const char *keyword) {
|
||||||
|
|
||||||
if (!keyword || !mpw_realloc( &marshalledSite->questions, NULL, sizeof( MPMarshalledQuestion ) * ++marshalledSite->questions_count ))
|
if (!keyword || !mpw_realloc( &site->questions, NULL, sizeof( MPMarshalledQuestion ) * ++site->questions_count ))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
MPMarshalledQuestion *question = &marshalledSite->questions[marshalledSite->questions_count - 1];
|
MPMarshalledQuestion *question = &site->questions[site->questions_count - 1];
|
||||||
*question = (MPMarshalledQuestion){
|
*question = (MPMarshalledQuestion){
|
||||||
.keyword = strdup( keyword ),
|
.keyword = strdup( keyword ),
|
||||||
};
|
};
|
||||||
@ -90,11 +90,14 @@ MPMarshalledQuestion *mpw_marshal_question(
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool mpw_marshal_free(
|
bool mpw_marshal_free(
|
||||||
MPMarshalledUser *marshalledUser) {
|
MPMarshalledUser *user) {
|
||||||
|
|
||||||
|
if (!user)
|
||||||
|
return true;
|
||||||
|
|
||||||
bool success = true;
|
bool success = true;
|
||||||
for (size_t s = 0; s < marshalledUser->sites_count; ++s) {
|
for (size_t s = 0; s < user->sites_count; ++s) {
|
||||||
MPMarshalledSite *site = &marshalledUser->sites[s];
|
MPMarshalledSite *site = &user->sites[s];
|
||||||
success &= mpw_free_string( site->name );
|
success &= mpw_free_string( site->name );
|
||||||
for (size_t q = 0; q < site->questions_count; ++q) {
|
for (size_t q = 0; q < site->questions_count; ++q) {
|
||||||
MPMarshalledQuestion *question = &site->questions[q];
|
MPMarshalledQuestion *question = &site->questions[q];
|
||||||
@ -102,10 +105,10 @@ bool mpw_marshal_free(
|
|||||||
}
|
}
|
||||||
success &= mpw_free( site->questions, sizeof( MPMarshalledQuestion ) * site->questions_count );
|
success &= mpw_free( site->questions, sizeof( MPMarshalledQuestion ) * site->questions_count );
|
||||||
}
|
}
|
||||||
success &= mpw_free( marshalledUser->sites, sizeof( MPMarshalledSite ) * marshalledUser->sites_count );
|
success &= mpw_free( user->sites, sizeof( MPMarshalledSite ) * user->sites_count );
|
||||||
success &= mpw_free_string( marshalledUser->fullName );
|
success &= mpw_free_string( user->fullName );
|
||||||
success &= mpw_free_string( marshalledUser->masterPassword );
|
success &= mpw_free_string( user->masterPassword );
|
||||||
success &= mpw_free( marshalledUser, sizeof( MPMarshalledUser ) );
|
success &= mpw_free( user, sizeof( MPMarshalledUser ) );
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
@ -316,13 +319,13 @@ static bool mpw_marshall_write_json(
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool mpw_marshall_write(
|
bool mpw_marshall_write(
|
||||||
char **out, const MPMarshallFormat outFormat, const MPMarshalledUser *marshalledUser, MPMarshallError *error) {
|
char **out, const MPMarshallFormat outFormat, const MPMarshalledUser *user, MPMarshallError *error) {
|
||||||
|
|
||||||
switch (outFormat) {
|
switch (outFormat) {
|
||||||
case MPMarshallFormatFlat:
|
case MPMarshallFormatFlat:
|
||||||
return mpw_marshall_write_flat( out, marshalledUser, error );
|
return mpw_marshall_write_flat( out, user, error );
|
||||||
case MPMarshallFormatJSON:
|
case MPMarshallFormatJSON:
|
||||||
return mpw_marshall_write_json( out, marshalledUser, error );
|
return mpw_marshall_write_json( out, user, error );
|
||||||
}
|
}
|
||||||
|
|
||||||
*error = (MPMarshallError){ MPMarshallErrorFormat, mpw_str( "Unsupported output format: %u", outFormat ) };
|
*error = (MPMarshallError){ MPMarshallErrorFormat, mpw_str( "Unsupported output format: %u", outFormat ) };
|
||||||
|
@ -94,7 +94,7 @@ typedef struct MPMarshalledUser {
|
|||||||
//// Marshalling.
|
//// Marshalling.
|
||||||
|
|
||||||
bool mpw_marshall_write(
|
bool mpw_marshall_write(
|
||||||
char **out, const MPMarshallFormat outFormat, const MPMarshalledUser *marshalledUser, MPMarshallError *error);
|
char **out, const MPMarshallFormat outFormat, const MPMarshalledUser *user, MPMarshallError *error);
|
||||||
MPMarshalledUser *mpw_marshall_read(
|
MPMarshalledUser *mpw_marshall_read(
|
||||||
char *in, const MPMarshallFormat inFormat, const char *masterPassword, MPMarshallError *error);
|
char *in, const MPMarshallFormat inFormat, const char *masterPassword, MPMarshallError *error);
|
||||||
|
|
||||||
@ -103,12 +103,12 @@ MPMarshalledUser *mpw_marshall_read(
|
|||||||
MPMarshalledUser *mpw_marshall_user(
|
MPMarshalledUser *mpw_marshall_user(
|
||||||
const char *fullName, const char *masterPassword, const MPAlgorithmVersion algorithmVersion);
|
const char *fullName, const char *masterPassword, const MPAlgorithmVersion algorithmVersion);
|
||||||
MPMarshalledSite *mpw_marshall_site(
|
MPMarshalledSite *mpw_marshall_site(
|
||||||
MPMarshalledUser *marshalledUser,
|
MPMarshalledUser *user,
|
||||||
const char *siteName, const MPPasswordType siteType, const uint32_t siteCounter, const MPAlgorithmVersion algorithmVersion);
|
const char *siteName, const MPPasswordType passwordType, const uint32_t siteCounter, const MPAlgorithmVersion algorithmVersion);
|
||||||
MPMarshalledQuestion *mpw_marshal_question(
|
MPMarshalledQuestion *mpw_marshal_question(
|
||||||
MPMarshalledSite *marshalledSite, const char *keyword);
|
MPMarshalledSite *site, const char *keyword);
|
||||||
bool mpw_marshal_free(
|
bool mpw_marshal_free(
|
||||||
MPMarshalledUser *marshalledUser);
|
MPMarshalledUser *user);
|
||||||
|
|
||||||
//// Format.
|
//// Format.
|
||||||
|
|
||||||
|
2
platform-darwin/External/Pearl
vendored
2
platform-darwin/External/Pearl
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 0dbcd1cebc640a2498b63ff8d25dffb6bfc89577
|
Subproject commit f0a66e94e8456b1a4d0baa5d9ee5c9560e994ddd
|
@ -22,9 +22,10 @@
|
|||||||
static void usage() {
|
static void usage() {
|
||||||
|
|
||||||
inf( ""
|
inf( ""
|
||||||
"Usage: mpw [-u name] [-t type] [-c counter] [-a algorithm] [-p purpose] [-C context] [-f|-F format] [-v|-q] [-h] site\n\n" );
|
"Usage: mpw [-u|-U name] [-t type] [-c counter] [-a algorithm] [-p purpose] [-C context] [-f|-F format] [-v|-q] [-h] site\n\n" );
|
||||||
inf( ""
|
inf( ""
|
||||||
" -u name Specify the full name of the user.\n"
|
" -u name Specify the full name of the user.\n"
|
||||||
|
" -u checks the master password against the config, -U allows updating to a new master password.\n"
|
||||||
" Defaults to %s in env or prompts.\n\n", MP_env_fullName );
|
" Defaults to %s in env or prompts.\n\n", MP_env_fullName );
|
||||||
inf( ""
|
inf( ""
|
||||||
" -t type Specify the password's template.\n"
|
" -t type Specify the password's template.\n"
|
||||||
@ -95,18 +96,31 @@ static char *mpw_path(const char *prefix, const char *extension) {
|
|||||||
return mpwPath;
|
return mpwPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *getline_prompt(const char *prompt) {
|
static char *mpw_getline(const char *prompt) {
|
||||||
|
|
||||||
|
fprintf( stderr, "%s ", prompt );
|
||||||
|
|
||||||
char *buf = NULL;
|
char *buf = NULL;
|
||||||
size_t bufSize = 0;
|
size_t bufSize = 0;
|
||||||
ssize_t lineSize;
|
ssize_t lineSize = getline( &buf, &bufSize, stdin );
|
||||||
fprintf( stderr, "%s", prompt );
|
if (lineSize <= 1) {
|
||||||
fprintf( stderr, " " );
|
|
||||||
if ((lineSize = getline( &buf, &bufSize, stdin )) <= 1) {
|
|
||||||
free( buf );
|
free( buf );
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
buf[lineSize - 1] = 0;
|
|
||||||
|
// Remove the newline.
|
||||||
|
buf[lineSize - 1] = '\0';
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *mpw_getpass(const char *prompt) {
|
||||||
|
|
||||||
|
char *passBuf = getpass( prompt );
|
||||||
|
if (!passBuf)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
char *buf = strdup( passBuf );
|
||||||
|
bzero( passBuf, strlen( passBuf ) );
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,7 +133,7 @@ int main(int argc, char *const argv[]) {
|
|||||||
MPKeyPurpose keyPurpose = MPKeyPurposeAuthentication;
|
MPKeyPurpose keyPurpose = MPKeyPurposeAuthentication;
|
||||||
MPAlgorithmVersion algorithmVersion = MPAlgorithmVersionCurrent;
|
MPAlgorithmVersion algorithmVersion = MPAlgorithmVersionCurrent;
|
||||||
MPMarshallFormat sitesFormat = MPMarshallFormatDefault;
|
MPMarshallFormat sitesFormat = MPMarshallFormatDefault;
|
||||||
bool sitesFormatFixed = false;
|
bool allowPasswordUpdate = false, sitesFormatFixed = false;
|
||||||
|
|
||||||
// Read the environment.
|
// Read the environment.
|
||||||
const char *fullNameArg = getenv( MP_env_fullName ), *masterPasswordArg = NULL;
|
const char *fullNameArg = getenv( MP_env_fullName ), *masterPasswordArg = NULL;
|
||||||
@ -127,10 +141,15 @@ int main(int argc, char *const argv[]) {
|
|||||||
const char *keyPurposeArg = NULL, *keyContextArg = NULL, *sitesFormatArg = NULL, *siteNameArg = NULL;
|
const char *keyPurposeArg = NULL, *keyContextArg = NULL, *sitesFormatArg = NULL, *siteNameArg = NULL;
|
||||||
|
|
||||||
// Read the command-line options.
|
// Read the command-line options.
|
||||||
for (int opt; (opt = getopt( argc, argv, "u:P:t:c:a:p:C:f:F:vqh" )) != EOF;)
|
for (int opt; (opt = getopt( argc, argv, "u:U:P:t:c:a:p:C:f:F:vqh" )) != EOF;)
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
case 'u':
|
case 'u':
|
||||||
fullNameArg = optarg;
|
fullNameArg = optarg;
|
||||||
|
allowPasswordUpdate = false;
|
||||||
|
break;
|
||||||
|
case 'U':
|
||||||
|
fullNameArg = optarg;
|
||||||
|
allowPasswordUpdate = true;
|
||||||
break;
|
break;
|
||||||
case 'P':
|
case 'P':
|
||||||
// Passing your master password via the command-line is insecure. Testing purposes only.
|
// Passing your master password via the command-line is insecure. Testing purposes only.
|
||||||
@ -203,18 +222,18 @@ int main(int argc, char *const argv[]) {
|
|||||||
|
|
||||||
// Determine fullName, siteName & masterPassword.
|
// Determine fullName, siteName & masterPassword.
|
||||||
if (!(fullNameArg && (fullName = strdup( fullNameArg ))) &&
|
if (!(fullNameArg && (fullName = strdup( fullNameArg ))) &&
|
||||||
!(fullName = getline_prompt( "Your full name:" ))) {
|
!(fullName = mpw_getline( "Your full name:" ))) {
|
||||||
ftl( "Missing full name.\n" );
|
ftl( "Missing full name.\n" );
|
||||||
return EX_DATAERR;
|
return EX_DATAERR;
|
||||||
}
|
}
|
||||||
if (!(siteNameArg && (siteName = strdup( siteNameArg ))) &&
|
if (!(siteNameArg && (siteName = strdup( siteNameArg ))) &&
|
||||||
!(siteName = getline_prompt( "Site name:" ))) {
|
!(siteName = mpw_getline( "Site name:" ))) {
|
||||||
ftl( "Missing site name.\n" );
|
ftl( "Missing site name.\n" );
|
||||||
return EX_DATAERR;
|
return EX_DATAERR;
|
||||||
}
|
}
|
||||||
if (!(masterPasswordArg && (masterPassword = strdup( masterPasswordArg ))))
|
if (!(masterPasswordArg && (masterPassword = strdup( masterPasswordArg ))))
|
||||||
while (!masterPassword || !strlen( masterPassword ))
|
while (!masterPassword || !strlen( masterPassword ))
|
||||||
masterPassword = getpass( "Your master password: " );
|
masterPassword = mpw_getpass( "Your master password: " );
|
||||||
if (sitesFormatArg) {
|
if (sitesFormatArg) {
|
||||||
sitesFormat = mpw_formatWithName( sitesFormatArg );
|
sitesFormat = mpw_formatWithName( sitesFormatArg );
|
||||||
if (ERR == sitesFormat) {
|
if (ERR == sitesFormat) {
|
||||||
@ -260,21 +279,43 @@ int main(int argc, char *const argv[]) {
|
|||||||
// Parse file.
|
// Parse file.
|
||||||
MPMarshallError marshallError = { MPMarshallSuccess };
|
MPMarshallError marshallError = { MPMarshallSuccess };
|
||||||
user = mpw_marshall_read( buf, sitesFormat, masterPassword, &marshallError );
|
user = mpw_marshall_read( buf, sitesFormat, masterPassword, &marshallError );
|
||||||
mpw_free( buf, bufSize );
|
if (marshallError.type == MPMarshallErrorMasterPassword) {
|
||||||
if (!user || marshallError.type != MPMarshallSuccess) {
|
// Incorrect master password.
|
||||||
if (marshallError.type == MPMarshallErrorMasterPassword) {
|
if (!allowPasswordUpdate) {
|
||||||
ftl( "Incorrect master password according to configuration:\n %s: %s\n", sitesPath, marshallError.description );
|
ftl( "Incorrect master password according to configuration:\n %s: %s\n", sitesPath, marshallError.description );
|
||||||
|
mpw_marshal_free( user );
|
||||||
|
mpw_free( buf, bufSize );
|
||||||
|
free( sitesPath );
|
||||||
return EX_DATAERR;
|
return EX_DATAERR;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
err( "Couldn't parse configuration file:\n %s: %s\n", sitesPath, marshallError.description );
|
// Update user's master password.
|
||||||
|
while (marshallError.type == MPMarshallErrorMasterPassword) {
|
||||||
|
inf( "Given master password does not match configuration.\n" );
|
||||||
|
inf( "To update the configuration with this new master password, first confirm the old master password.\n" );
|
||||||
|
|
||||||
|
char *importMasterPassword = NULL;
|
||||||
|
while (!importMasterPassword || !strlen( importMasterPassword ))
|
||||||
|
importMasterPassword = mpw_getpass( "Old master password: " );
|
||||||
|
|
||||||
|
mpw_marshal_free( user );
|
||||||
|
user = mpw_marshall_read( buf, sitesFormat, importMasterPassword, &marshallError );
|
||||||
|
}
|
||||||
|
if (user) {
|
||||||
|
mpw_free_string( user->masterPassword );
|
||||||
|
user->masterPassword = strdup( masterPassword );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mpw_free( buf, bufSize );
|
||||||
|
if (!user || marshallError.type != MPMarshallSuccess) {
|
||||||
|
err( "Couldn't parse configuration file:\n %s: %s\n", sitesPath, marshallError.description );
|
||||||
mpw_marshal_free( user );
|
mpw_marshal_free( user );
|
||||||
user = NULL;
|
user = NULL;
|
||||||
free( sitesPath );
|
free( sitesPath );
|
||||||
sitesPath = NULL;
|
sitesPath = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
else {
|
if (user) {
|
||||||
// Load defaults.
|
// Load defaults.
|
||||||
mpw_free_string( fullName );
|
mpw_free_string( fullName );
|
||||||
mpw_free_string( masterPassword );
|
mpw_free_string( masterPassword );
|
||||||
@ -379,7 +420,10 @@ int main(int argc, char *const argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Output the result.
|
// Output the result.
|
||||||
if (passwordType & MPPasswordTypeClassGenerated) {
|
if (keyPurpose == MPKeyPurposeIdentification && site && !site->loginGenerated && site->loginName)
|
||||||
|
fprintf( stdout, "%s\n", site->loginName );
|
||||||
|
|
||||||
|
else if (passwordType & MPPasswordTypeClassGenerated) {
|
||||||
MPSiteKey siteKey = mpw_siteKey( masterKey, siteName, siteCounter, keyPurpose, keyContext, algorithmVersion );
|
MPSiteKey siteKey = mpw_siteKey( masterKey, siteName, siteCounter, keyPurpose, keyContext, algorithmVersion );
|
||||||
const char *sitePassword = mpw_sitePassword( siteKey, passwordType, algorithmVersion );
|
const char *sitePassword = mpw_sitePassword( siteKey, passwordType, algorithmVersion );
|
||||||
mpw_free( siteKey, MPSiteKeySize );
|
mpw_free( siteKey, MPSiteKeySize );
|
||||||
@ -409,10 +453,35 @@ int main(int argc, char *const argv[]) {
|
|||||||
|
|
||||||
// Update the mpsites file.
|
// Update the mpsites file.
|
||||||
if (user) {
|
if (user) {
|
||||||
|
if (keyPurpose == MPKeyPurposeAuthentication) {
|
||||||
|
if (!site)
|
||||||
|
site = mpw_marshall_site( user, siteName, passwordType, siteCounter, algorithmVersion );
|
||||||
|
else {
|
||||||
|
site->type = passwordType;
|
||||||
|
site->counter = siteCounter;
|
||||||
|
site->algorithm = algorithmVersion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (keyPurpose == MPKeyPurposeIdentification && site) {
|
||||||
|
// TODO: We're not persisting the passwordType of the generated login
|
||||||
|
if (passwordType & MPPasswordTypeClassGenerated)
|
||||||
|
site->loginGenerated = true;
|
||||||
|
}
|
||||||
|
else if (keyPurpose == MPKeyPurposeRecovery && site && keyContext) {
|
||||||
|
// TODO: We're not persisting the passwordType of the recovery question
|
||||||
|
MPMarshalledQuestion *question = NULL;
|
||||||
|
for (size_t q = 0; q < site->questions_count; ++q) {
|
||||||
|
question = &site->questions[q];
|
||||||
|
if (strcmp( keyContext, question->keyword ) != 0) {
|
||||||
|
question = NULL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!question)
|
||||||
|
mpw_marshal_question( site, keyContext );
|
||||||
|
}
|
||||||
if (site) {
|
if (site) {
|
||||||
site->type = passwordType;
|
|
||||||
site->counter = siteCounter;
|
|
||||||
site->algorithm = algorithmVersion;
|
|
||||||
site->lastUsed = user->lastUsed = time( NULL );
|
site->lastUsed = user->lastUsed = time( NULL );
|
||||||
site->uses++;
|
site->uses++;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user