diff --git a/core/c/mpw-algorithm.c b/core/c/mpw-algorithm.c index 8db80b71..0833ebac 100644 --- a/core/c/mpw-algorithm.c +++ b/core/c/mpw-algorithm.c @@ -46,7 +46,7 @@ MPMasterKey mpw_masterKey(const char *fullName, const char *masterPassword, cons } MPSiteKey mpw_siteKey( - MPMasterKey masterKey, const char *siteName, const uint32_t siteCounter, + MPMasterKey masterKey, const char *siteName, const MPCounterValue siteCounter, const MPKeyPurpose keyPurpose, const char *keyContext, const MPAlgorithmVersion algorithmVersion) { trc( "-- mpw_siteKey (algorithm: %u)\n", algorithmVersion ); diff --git a/core/c/mpw-algorithm.h b/core/c/mpw-algorithm.h index 14cd7a33..d661077d 100644 --- a/core/c/mpw-algorithm.h +++ b/core/c/mpw-algorithm.h @@ -45,7 +45,7 @@ MPMasterKey mpw_masterKey( /** Derive the site key for a user's site from the given master key and site parameters. * @return A new MPSiteKeySize-byte allocated buffer or NULL if an error occurred. */ MPSiteKey mpw_siteKey( - MPMasterKey masterKey, const char *siteName, const uint32_t siteCounter, + MPMasterKey masterKey, const char *siteName, const MPCounterValue siteCounter, const MPKeyPurpose keyPurpose, const char *keyContext, const MPAlgorithmVersion algorithmVersion); /** Encode a password for the site from the given site key. diff --git a/core/c/mpw-algorithm_v0.c b/core/c/mpw-algorithm_v0.c index 16f2db74..d40f59b1 100644 --- a/core/c/mpw-algorithm_v0.c +++ b/core/c/mpw-algorithm_v0.c @@ -82,12 +82,14 @@ static MPMasterKey mpw_masterKey_v0( } static MPSiteKey mpw_siteKey_v0( - MPMasterKey masterKey, const char *siteName, const uint32_t siteCounter, + MPMasterKey masterKey, const char *siteName, const MPCounterValue siteCounter, const MPKeyPurpose keyPurpose, const char *keyContext) { const char *keyScope = mpw_scopeForPurpose( keyPurpose ); trc( "keyScope: %s\n", keyScope ); + // TODO: Implement MPCounterValueTOTP + // Calculate the site seed. trc( "siteSalt: keyScope=%s | #siteName=%s | siteName=%s | siteCounter=%s | #keyContext=%s | keyContext=%s\n", keyScope, mpw_hex_l( htonl( mpw_utf8_strlen( siteName ) ) ), siteName, mpw_hex_l( htonl( siteCounter ) ), diff --git a/core/c/mpw-algorithm_v1.c b/core/c/mpw-algorithm_v1.c index 384b2108..b6129e13 100644 --- a/core/c/mpw-algorithm_v1.c +++ b/core/c/mpw-algorithm_v1.c @@ -31,7 +31,7 @@ MPMasterKey mpw_masterKey_v0( const char *fullName, const char *masterPassword); MPSiteKey mpw_siteKey_v0( - MPMasterKey masterKey, const char *siteName, const uint32_t siteCounter, + MPMasterKey masterKey, const char *siteName, const MPCounterValue siteCounter, const MPKeyPurpose keyPurpose, const char *keyContext); const char *mpw_encrypt_v0( MPMasterKey masterKey, const char *plainText); @@ -46,7 +46,7 @@ static MPMasterKey mpw_masterKey_v1( } static MPSiteKey mpw_siteKey_v1( - MPMasterKey masterKey, const char *siteName, const uint32_t siteCounter, + MPMasterKey masterKey, const char *siteName, const MPCounterValue siteCounter, const MPKeyPurpose keyPurpose, const char *keyContext) { return mpw_siteKey_v0( masterKey, siteName, siteCounter, keyPurpose, keyContext ); diff --git a/core/c/mpw-algorithm_v2.c b/core/c/mpw-algorithm_v2.c index d2351c03..5b81dd50 100644 --- a/core/c/mpw-algorithm_v2.c +++ b/core/c/mpw-algorithm_v2.c @@ -45,12 +45,14 @@ static MPMasterKey mpw_masterKey_v2( } static MPSiteKey mpw_siteKey_v2( - MPMasterKey masterKey, const char *siteName, const uint32_t siteCounter, + MPMasterKey masterKey, const char *siteName, const MPCounterValue siteCounter, const MPKeyPurpose keyPurpose, const char *keyContext) { const char *keyScope = mpw_scopeForPurpose( keyPurpose ); trc( "keyScope: %s\n", keyScope ); + // TODO: Implement MPCounterValueTOTP + // Calculate the site seed. trc( "siteSalt: keyScope=%s | #siteName=%s | siteName=%s | siteCounter=%s | #keyContext=%s | keyContext=%s\n", keyScope, mpw_hex_l( htonl( strlen( siteName ) ) ), siteName, mpw_hex_l( htonl( siteCounter ) ), diff --git a/core/c/mpw-algorithm_v3.c b/core/c/mpw-algorithm_v3.c index 7d2b57e4..300c2031 100644 --- a/core/c/mpw-algorithm_v3.c +++ b/core/c/mpw-algorithm_v3.c @@ -29,7 +29,7 @@ // Inherited functions. MPSiteKey mpw_siteKey_v2( - MPMasterKey masterKey, const char *siteName, const uint32_t siteCounter, + MPMasterKey masterKey, const char *siteName, const MPCounterValue siteCounter, const MPKeyPurpose keyPurpose, const char *keyContext); const char *mpw_sitePassword_v2( MPSiteKey siteKey, const MPPasswordType passwordType); @@ -73,7 +73,7 @@ static MPMasterKey mpw_masterKey_v3( } static MPSiteKey mpw_siteKey_v3( - MPMasterKey masterKey, const char *siteName, const uint32_t siteCounter, + MPMasterKey masterKey, const char *siteName, const MPCounterValue siteCounter, const MPKeyPurpose keyPurpose, const char *keyContext) { return mpw_siteKey_v2( masterKey, siteName, siteCounter, keyPurpose, keyContext ); diff --git a/core/c/mpw-marshall.c b/core/c/mpw-marshall.c index 5ec079fb..9ee558fe 100644 --- a/core/c/mpw-marshall.c +++ b/core/c/mpw-marshall.c @@ -50,7 +50,7 @@ MPMarshalledUser *mpw_marshall_user( MPMarshalledSite *mpw_marshall_site( MPMarshalledUser *user, const char *siteName, const MPPasswordType passwordType, - const uint32_t siteCounter, const MPAlgorithmVersion algorithmVersion) { + const MPCounterValue siteCounter, const MPAlgorithmVersion algorithmVersion) { if (!siteName || !mpw_realloc( &user->sites, NULL, sizeof( MPMarshalledSite ) * ++user->sites_count )) return NULL; @@ -294,7 +294,8 @@ static bool mpw_marshall_write_json( if (!user->redacted) { // Clear Text - MPSiteKey siteKey = mpw_siteKey( masterKey, site->name, 1, MPKeyPurposeRecovery, question->keyword, site->algorithm ); + MPSiteKey siteKey = mpw_siteKey( masterKey, site->name, + MPCounterValueInitial, MPKeyPurposeRecovery, question->keyword, site->algorithm ); const char *answer = mpw_sitePassword( siteKey, MPPasswordTypeGeneratedPhrase, site->algorithm ); mpw_free( siteKey, MPSiteKeySize ); if (answer) @@ -482,11 +483,11 @@ static MPMarshalledUser *mpw_marshall_read_flat( return NULL; } long long int value = atoll( str_counter ); - if (value < 0 || value > UINT32_MAX) { + if (value < MPCounterValueFirst || value > MPCounterValueLast) { *error = (MPMarshallError){ MPMarshallErrorIllegal, mpw_str( "Invalid site counter: %s: %s", siteName, str_counter ) }; return NULL; } - uint32_t siteCounter = (uint32_t)value; + MPCounterValue siteCounter = (MPCounterValue)value; value = atoll( str_algorithm ); if (value < MPAlgorithmVersionFirst || value > MPAlgorithmVersionLast) { *error = (MPMarshallError){ MPMarshallErrorIllegal, mpw_str( "Invalid site algorithm: %s: %s", siteName, str_algorithm ) }; @@ -634,11 +635,11 @@ static MPMarshalledUser *mpw_marshall_read_json( return NULL; } value = mpw_get_json_int( json_site.val, "counter", 1 ); - if (value < 0 || value > UINT32_MAX) { + if (value < MPCounterValueFirst || value > MPCounterValueLast) { *error = (MPMarshallError){ MPMarshallErrorIllegal, mpw_str( "Invalid site counter: %s: %d", siteName, value ) }; return NULL; } - uint32_t siteCounter = (uint32_t)value; + MPCounterValue siteCounter = (MPCounterValue)value; const char *siteContent = mpw_get_json_string( json_site.val, "password", NULL ); const char *siteLoginName = mpw_get_json_string( json_site.val, "login_name", NULL ); bool siteLoginGenerated = mpw_get_json_boolean( json_site.val, "login_generated", false ); diff --git a/core/c/mpw-marshall.h b/core/c/mpw-marshall.h index 74b640fe..fa6eecae 100644 --- a/core/c/mpw-marshall.h +++ b/core/c/mpw-marshall.h @@ -63,7 +63,7 @@ typedef struct MPMarshalledSite { const char *name; const char *content; MPPasswordType type; - uint32_t counter; + MPCounterValue counter; MPAlgorithmVersion algorithm; const char *loginName; @@ -104,7 +104,7 @@ MPMarshalledUser *mpw_marshall_user( const char *fullName, const char *masterPassword, const MPAlgorithmVersion algorithmVersion); MPMarshalledSite *mpw_marshall_site( MPMarshalledUser *user, - const char *siteName, const MPPasswordType passwordType, const uint32_t siteCounter, const MPAlgorithmVersion algorithmVersion); + const char *siteName, const MPPasswordType passwordType, const MPCounterValue siteCounter, const MPAlgorithmVersion algorithmVersion); MPMarshalledQuestion *mpw_marshal_question( MPMarshalledSite *site, const char *keyword); bool mpw_marshal_free( diff --git a/core/c/mpw-types.c b/core/c/mpw-types.c index 67251dc8..ef9a7659 100644 --- a/core/c/mpw-types.c +++ b/core/c/mpw-types.c @@ -31,6 +31,28 @@ const MPPasswordType mpw_typeWithName(const char *typeName) { + // Find what password type is represented by the type letter. + if (strlen( typeName ) == 1) { + if ('x' == typeName[0]) + return MPPasswordTypeGeneratedMaximum; + if ('l' == typeName[0]) + return MPPasswordTypeGeneratedLong; + if ('m' == typeName[0]) + return MPPasswordTypeGeneratedMedium; + if ('b' == typeName[0]) + return MPPasswordTypeGeneratedBasic; + if ('s' == typeName[0]) + return MPPasswordTypeGeneratedShort; + if ('i' == typeName[0]) + return MPPasswordTypeGeneratedPIN; + if ('n' == typeName[0]) + return MPPasswordTypeGeneratedName; + if ('P' == typeName[0]) + return MPPasswordTypeStoredPersonal; + if ('D' == typeName[0]) + return MPPasswordTypeStoredDevice; + } + // Lower-case and trim optionally leading "Generated" string from typeName to standardize it. size_t stdTypeNameOffset = 0; size_t stdTypeNameSize = strlen( typeName ); @@ -42,29 +64,23 @@ const MPPasswordType mpw_typeWithName(const char *typeName) { stdTypeName[stdTypeNameSize] = '\0'; // Find what password type is represented by the type name. - if (0 == strcmp( "x", stdTypeName ) - || strncmp( mpw_nameForType( MPPasswordTypeGeneratedMaximum ), stdTypeName, strlen( stdTypeName ) ) == 0) + if (strncmp( mpw_nameForType( MPPasswordTypeGeneratedPhrase ), stdTypeName, strlen( stdTypeName ) ) == 0) + return MPPasswordTypeGeneratedPhrase; + if (strncmp( mpw_nameForType( MPPasswordTypeGeneratedMaximum ), stdTypeName, strlen( stdTypeName ) ) == 0) return MPPasswordTypeGeneratedMaximum; - if (0 == strcmp( "l", stdTypeName ) - || strncmp( mpw_nameForType( MPPasswordTypeGeneratedLong ), stdTypeName, strlen( stdTypeName ) ) == 0) + if (strncmp( mpw_nameForType( MPPasswordTypeGeneratedLong ), stdTypeName, strlen( stdTypeName ) ) == 0) return MPPasswordTypeGeneratedLong; - if (0 == strcmp( "m", stdTypeName ) - || strncmp( mpw_nameForType( MPPasswordTypeGeneratedMedium ), stdTypeName, strlen( stdTypeName ) ) == 0) + if (strncmp( mpw_nameForType( MPPasswordTypeGeneratedMedium ), stdTypeName, strlen( stdTypeName ) ) == 0) return MPPasswordTypeGeneratedMedium; - if (0 == strcmp( "b", stdTypeName ) - || strncmp( mpw_nameForType( MPPasswordTypeGeneratedBasic ), stdTypeName, strlen( stdTypeName ) ) == 0) + if (strncmp( mpw_nameForType( MPPasswordTypeGeneratedBasic ), stdTypeName, strlen( stdTypeName ) ) == 0) return MPPasswordTypeGeneratedBasic; - if (0 == strcmp( "s", stdTypeName ) - || strncmp( mpw_nameForType( MPPasswordTypeGeneratedShort ), stdTypeName, strlen( stdTypeName ) ) == 0) + if (strncmp( mpw_nameForType( MPPasswordTypeGeneratedShort ), stdTypeName, strlen( stdTypeName ) ) == 0) return MPPasswordTypeGeneratedShort; - if (0 == strcmp( "i", stdTypeName ) - || strncmp( mpw_nameForType( MPPasswordTypeGeneratedPIN ), stdTypeName, strlen( stdTypeName ) ) == 0) + if (strncmp( mpw_nameForType( MPPasswordTypeGeneratedPIN ), stdTypeName, strlen( stdTypeName ) ) == 0) return MPPasswordTypeGeneratedPIN; - if (0 == strcmp( "n", stdTypeName ) - || strncmp( mpw_nameForType( MPPasswordTypeGeneratedName ), stdTypeName, strlen( stdTypeName ) ) == 0) + if (strncmp( mpw_nameForType( MPPasswordTypeGeneratedName ), stdTypeName, strlen( stdTypeName ) ) == 0) return MPPasswordTypeGeneratedName; - if (0 == strcmp( "p", stdTypeName ) - || strncmp( mpw_nameForType( MPPasswordTypeGeneratedPhrase ), stdTypeName, strlen( stdTypeName ) ) == 0) + if (strncmp( mpw_nameForType( MPPasswordTypeGeneratedPhrase ), stdTypeName, strlen( stdTypeName ) ) == 0) return MPPasswordTypeGeneratedPhrase; dbg( "Not a generated type name: %s\n", stdTypeName ); diff --git a/core/c/mpw-types.h b/core/c/mpw-types.h index ea944129..c2e0d98f 100644 --- a/core/c/mpw-types.h +++ b/core/c/mpw-types.h @@ -31,13 +31,13 @@ //// Types. -#define MPMasterKeySize 64 +#define MPMasterKeySize 64 /* bytes */ typedef const uint8_t *MPMasterKey; -#define MPSiteKeySize 256 / 8 // Bytes in HMAC-SHA-256 +#define MPSiteKeySize (256 / 8) /* bytes */ // Size of HMAC-SHA-256 typedef const uint8_t *MPSiteKey; typedef const char *MPKeyID; -typedef enum( unsigned int, MPKeyPurpose ) { +typedef enum( uint8_t, MPKeyPurpose ) { /** Generate a key for authentication. */ MPKeyPurposeAuthentication, /** Generate a name for identification. */ @@ -46,21 +46,24 @@ typedef enum( unsigned int, MPKeyPurpose ) { MPKeyPurposeRecovery, }; -typedef enum( unsigned int, MPPasswordTypeClass ) { +// bit 4 - 9 +typedef enum( uint16_t, MPPasswordTypeClass ) { /** Generate the password. */ MPPasswordTypeClassGenerated = 1 << 4, /** Store the password. */ MPPasswordTypeClassStored = 1 << 5, }; -typedef enum( unsigned int, MPSiteFeature ) { +// bit 10 - 15 +typedef enum( uint16_t, MPSiteFeature ) { /** Export the key-protected content data. */ MPSiteFeatureExportContent = 1 << 10, /** Never export content. */ MPSiteFeatureDevicePrivate = 1 << 11, }; -typedef enum( unsigned int, MPPasswordType ) { +// bit 0-3 | MPPasswordTypeClass | MPSiteFeature +typedef enum( uint32_t, MPPasswordType ) { /** pg^VMAUBk5x3p%HP%i4= */ MPPasswordTypeGeneratedMaximum = 0x0 | MPPasswordTypeClassGenerated | 0x0, /** BiroYena8:Kixa */ @@ -86,6 +89,17 @@ typedef enum( unsigned int, MPPasswordType ) { MPPasswordTypeDefault = MPPasswordTypeGeneratedLong, }; +typedef enum ( uint32_t, MPCounterValue ) { + /** Use a time-based counter value, resulting in a TOTP generator. */ + MPCounterValueTOTP = 0, + /** The initial value for a site's counter. */ + MPCounterValueInitial = 1, + + MPCounterValueDefault = MPCounterValueInitial, + MPCounterValueFirst = MPCounterValueTOTP, + MPCounterValueLast = UINT32_MAX, +}; + //// Type utilities. /** diff --git a/platform-independent/cli-c/cli/mpw-bench.c b/platform-independent/cli-c/cli/mpw-bench.c index c4043f35..1e9abeba 100644 --- a/platform-independent/cli-c/cli/mpw-bench.c +++ b/platform-independent/cli-c/cli/mpw-bench.c @@ -49,7 +49,7 @@ int main(int argc, char *const argv[]) { const char *fullName = "Robert Lee Mitchel"; const char *masterPassword = "banana colored duckling"; const char *siteName = "masterpasswordapp.com"; - const uint32_t siteCounter = 1; + const MPCounterValue siteCounter = MPCounterValueDefault; const MPPasswordType passwordType = MPPasswordTypeDefault; const MPKeyPurpose keyPurpose = MPKeyPurposeAuthentication; const char *keyContext = NULL; diff --git a/platform-independent/cli-c/cli/mpw-cli.c b/platform-independent/cli-c/cli/mpw-cli.c index a4c53db4..52bc4ceb 100644 --- a/platform-independent/cli-c/cli/mpw-cli.c +++ b/platform-independent/cli-c/cli/mpw-cli.c @@ -22,45 +22,50 @@ static void usage() { inf( "" - "Usage: mpw [-u|-U name] [-t type] [-c counter] [-a algorithm] [-p purpose]" - " [-C context] [-f|-F format] [-R 0|1] [-v|-q] [-h] site-name\n\n" ); + "Usage:\n" + " mpw [-u|-U full-name] [-t pw-type] [-c counter] [-a algorithm] [-s value]\n" + " [-p purpose] [-C context] [-f|-F format] [-R 0|1] [-v|-q] [-h] site-name\n\n" ); inf( "" - " -u name Specify the full name of the user.\n" - " -u checks the master password against the config," + " -u full-name Specify the full name of the user.\n" + " -u checks the master password against the config,\n" " -U allows updating to a new master password.\n" " Defaults to %s in env or prompts.\n\n", MP_env_fullName ); inf( "" - " -t type Specify the password's template.\n" - " Defaults to 'long' (auth), 'name' (ident) or 'phrase'(recovery).\n" - " x, max, maximum | 20 characters, contains symbols.\n" - " l, long | Copy-friendly, 14 characters, symbols.\n" - " m, med, medium | Copy-friendly, 8 characters, symbols.\n" - " b, basic | 8 characters, no symbols.\n" - " s, short | Copy-friendly, 4 characters, no symbols.\n" - " i, pin | 4 numbers.\n" - " n, name | 9 letter name.\n" - " p, phrase | 20 character sentence.\n\n" ); + " -t pw-type Specify the password's template.\n" + " Defaults to 'long' (-p a), 'name' (-p i) or 'phrase' (-p r).\n" + " x, maximum | 20 characters, contains symbols.\n" + " l, long | Copy-friendly, 14 characters, symbols.\n" + " m, medium | Copy-friendly, 8 characters, symbols.\n" + " b, basic | 8 characters, no symbols.\n" + " s, short | Copy-friendly, 4 characters, no symbols.\n" + " i, pin | 4 numbers.\n" + " n, name | 9 letter name.\n" + " p, phrase | 20 character sentence.\n" + " P, personal | saved personal password (see -s).\n\n" ); inf( "" " -c counter The value of the counter.\n" " Defaults to 1.\n\n" ); inf( "" - " -a version The algorithm version to use.\n" - " Defaults to %s in env or %d.\n\n", MP_env_algorithm, MPAlgorithmVersionCurrent ); + " -a version The algorithm version to use, %d - %d.\n" + " Defaults to %s in env or %d.\n\n", + MPAlgorithmVersionFirst, MPAlgorithmVersionLast, MP_env_algorithm, MPAlgorithmVersionCurrent ); + inf( "" + " -s value The value to save for -t P or -p i.\n\n" ); inf( "" " -p purpose The purpose of the generated token.\n" - " Defaults to 'password'.\n" + " Defaults to 'auth'.\n" " a, auth | An authentication token such as a password.\n" " i, ident | An identification token such as a username.\n" " r, rec | A recovery token such as a security answer.\n\n" ); inf( "" " -C context A purpose-specific context.\n" " Defaults to empty.\n" - " -p a, auth | -\n" - " -p i, ident | -\n" - " -p r, rec | Most significant word in security question.\n\n" ); + " -p a | -\n" + " -p i | -\n" + " -p r | Most significant word in security question.\n\n" ); inf( "" " -f|F format The mpsites format to use for reading/writing site parameters.\n" - " -F forces the use of the given format," + " -F forces the use of the given format,\n" " -f allows fallback/migration.\n" " Defaults to json, falls back to plain.\n" " f, flat | ~/.mpw.d/Full Name.%s\n" @@ -80,6 +85,40 @@ static void usage() { exit( 0 ); } +static const char *mpw_getenv(const char *variableName) { + + char *envBuf = getenv( variableName ); + return envBuf? strdup( envBuf ): NULL; +} + +static const char *mpw_getline(const char *prompt) { + + fprintf( stderr, "%s ", prompt ); + + char *buf = NULL; + size_t bufSize = 0; + ssize_t lineSize = getline( &buf, &bufSize, stdin ); + if (lineSize <= 1) { + free( buf ); + return NULL; + } + + // Remove the newline. + buf[lineSize - 1] = '\0'; + return buf; +} + +static const char *mpw_getpass(const char *prompt) { + + char *passBuf = getpass( prompt ); + if (!passBuf) + return NULL; + + char *buf = strdup( passBuf ); + bzero( passBuf, strlen( passBuf ) ); + return buf; +} + static char *mpw_path(const char *prefix, const char *extension) { char *homedir = NULL; @@ -102,39 +141,11 @@ static char *mpw_path(const char *prefix, const char *extension) { return mpwPath; } -static char *mpw_getline(const char *prompt) { - - fprintf( stderr, "%s ", prompt ); - - char *buf = NULL; - size_t bufSize = 0; - ssize_t lineSize = getline( &buf, &bufSize, stdin ); - if (lineSize <= 1) { - free( buf ); - return NULL; - } - - // 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; -} - int main(int argc, char *const argv[]) { // Master Password defaults. - const char *fullName = NULL, *masterPassword = NULL, *siteName = NULL, *keyContext = NULL; - uint32_t siteCounter = 1; + const char *fullName = NULL, *masterPassword = NULL, *siteName = NULL, *saveValue = NULL, *keyContext = NULL; + MPCounterValue siteCounter = MPCounterValueDefault; MPPasswordType passwordType = MPPasswordTypeDefault; MPKeyPurpose keyPurpose = MPKeyPurposeAuthentication; MPAlgorithmVersion algorithmVersion = MPAlgorithmVersionCurrent; @@ -142,50 +153,55 @@ int main(int argc, char *const argv[]) { bool allowPasswordUpdate = false, sitesFormatFixed = false, sitesRedacted = true; // Read the environment. - const char *fullNameArg = getenv( MP_env_fullName ), *masterPasswordArg = NULL, *siteNameArg = NULL; - const char *passwordTypeArg = NULL, *siteCounterArg = NULL, *algorithmVersionArg = getenv( MP_env_algorithm ); + const char *fullNameArg = NULL, *masterPasswordArg = NULL, *siteNameArg = NULL; + const char *passwordTypeArg = NULL, *siteCounterArg = NULL, *algorithmVersionArg = NULL, *saveValueArg = NULL; const char *keyPurposeArg = NULL, *keyContextArg = NULL, *sitesFormatArg = NULL, *sitesRedactedArg = NULL; + fullNameArg = mpw_getenv( MP_env_fullName ); + algorithmVersionArg = mpw_getenv( MP_env_algorithm ); // Read the command-line options. - for (int opt; (opt = getopt( argc, argv, "u:U:P:t:c:a:p:C:f:F:R:vqh" )) != EOF;) + for (int opt; (opt = getopt( argc, argv, "u:U:P:t:c:a:s:p:C:f:F:R:vqh" )) != EOF;) switch (opt) { case 'u': - fullNameArg = optarg; + fullNameArg = optarg && strlen( optarg )? strdup( optarg ): NULL; allowPasswordUpdate = false; break; case 'U': - fullNameArg = optarg; + fullNameArg = optarg && strlen( optarg )? strdup( optarg ): NULL; allowPasswordUpdate = true; break; case 'P': // Passing your master password via the command-line is insecure. Testing purposes only. - masterPasswordArg = optarg; + masterPasswordArg = optarg && strlen( optarg )? strdup( optarg ): NULL; break; case 't': - passwordTypeArg = optarg; + passwordTypeArg = optarg && strlen( optarg )? strdup( optarg ): NULL; break; case 'c': - siteCounterArg = optarg; + siteCounterArg = optarg && strlen( optarg )? strdup( optarg ): NULL; break; case 'a': - algorithmVersionArg = optarg; + algorithmVersionArg = optarg && strlen( optarg )? strdup( optarg ): NULL; + break; + case 's': + saveValueArg = optarg && strlen( optarg )? strdup( optarg ): NULL; break; case 'p': - keyPurposeArg = optarg; + keyPurposeArg = optarg && strlen( optarg )? strdup( optarg ): NULL; break; case 'C': - keyContextArg = optarg; + keyContextArg = optarg && strlen( optarg )? strdup( optarg ): NULL; break; case 'f': - sitesFormatArg = optarg; + sitesFormatArg = optarg && strlen( optarg )? strdup( optarg ): NULL; sitesFormatFixed = false; break; case 'F': - sitesFormatArg = optarg; + sitesFormatArg = optarg && strlen( optarg )? strdup( optarg ): NULL; sitesFormatFixed = true; break; case 'R': - sitesRedactedArg = optarg; + sitesRedactedArg = optarg && strlen( optarg )? strdup( optarg ): NULL; break; case 'v': ++mpw_verbosity; @@ -216,19 +232,7 @@ int main(int argc, char *const argv[]) { return EX_USAGE; } if (optind < argc) - siteNameArg = argv[optind]; - - // Empty strings unset the argument. - fullNameArg = fullNameArg && strlen( fullNameArg )? fullNameArg: NULL; - masterPasswordArg = masterPasswordArg && strlen( masterPasswordArg )? masterPasswordArg: NULL; - passwordTypeArg = passwordTypeArg && strlen( passwordTypeArg )? passwordTypeArg: NULL; - siteCounterArg = siteCounterArg && strlen( siteCounterArg )? siteCounterArg: NULL; - algorithmVersionArg = algorithmVersionArg && strlen( algorithmVersionArg )? algorithmVersionArg: NULL; - keyPurposeArg = keyPurposeArg && strlen( keyPurposeArg )? keyPurposeArg: NULL; - keyContextArg = keyContextArg && strlen( keyContextArg )? keyContextArg: NULL; - sitesFormatArg = sitesFormatArg && strlen( sitesFormatArg )? sitesFormatArg: NULL; - sitesRedactedArg = sitesRedactedArg && strlen( sitesRedactedArg )? sitesRedactedArg: NULL; - siteNameArg = siteNameArg && strlen( siteNameArg )? siteNameArg: NULL; + siteNameArg = strdup( argv[optind] ); // Determine fullName, siteName & masterPassword. if (!(fullNameArg && (fullName = strdup( fullNameArg ))) && @@ -246,7 +250,7 @@ int main(int argc, char *const argv[]) { masterPassword = mpw_getpass( "Your master password: " ); if (sitesFormatArg) { sitesFormat = mpw_formatWithName( sitesFormatArg ); - if (ERR == sitesFormat) { + if (ERR == (int)sitesFormat) { ftl( "Invalid sites format: %s\n", sitesFormatArg ); return EX_USAGE; } @@ -304,7 +308,7 @@ int main(int argc, char *const argv[]) { 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; + const char *importMasterPassword = NULL; while (!importMasterPassword || !strlen( importMasterPassword )) importMasterPassword = mpw_getpass( "Old master password: " ); @@ -345,6 +349,7 @@ int main(int argc, char *const argv[]) { continue; } + mpw_free_string( saveValue ); passwordType = site->type; siteCounter = site->counter; algorithmVersion = site->algorithm; @@ -358,11 +363,11 @@ int main(int argc, char *const argv[]) { sitesRedacted = strcmp( sitesRedactedArg, "1" ) == 0; if (siteCounterArg) { long long int siteCounterInt = atoll( siteCounterArg ); - if (siteCounterInt < 0 || siteCounterInt > UINT32_MAX) { + if (siteCounterInt < MPCounterValueFirst || siteCounterInt > MPCounterValueLast) { ftl( "Invalid site counter: %s\n", siteCounterArg ); return EX_USAGE; } - siteCounter = (uint32_t)siteCounterInt; + siteCounter = (MPCounterValue)siteCounterInt; } if (algorithmVersionArg) { int algorithmVersionInt = atoi( algorithmVersionArg ); @@ -372,9 +377,13 @@ int main(int argc, char *const argv[]) { } algorithmVersion = (MPAlgorithmVersion)algorithmVersionInt; } + if (saveValueArg) { + mpw_free_string( saveValue ); + saveValue = strdup( saveValueArg ); + } if (keyPurposeArg) { keyPurpose = mpw_purposeWithName( keyPurposeArg ); - if (ERR == keyPurpose) { + if (ERR == (int)keyPurpose) { ftl( "Invalid purpose: %s\n", keyPurposeArg ); return EX_USAGE; } @@ -396,13 +405,26 @@ int main(int argc, char *const argv[]) { } if (passwordTypeArg) { passwordType = mpw_typeWithName( passwordTypeArg ); - if (ERR == passwordType) { + if (ERR == (int)passwordType) { ftl( "Invalid type: %s\n", passwordTypeArg ); return EX_USAGE; } } - if (keyContextArg) + if (keyContextArg) { + mpw_free_string( keyContext ); keyContext = strdup( keyContextArg ); + } + mpw_free_string( fullNameArg ); + mpw_free_string( masterPasswordArg ); + mpw_free_string( siteNameArg ); + mpw_free_string( passwordTypeArg ); + mpw_free_string( siteCounterArg ); + mpw_free_string( algorithmVersionArg ); + mpw_free_string( saveValueArg ); + mpw_free_string( keyPurposeArg ); + mpw_free_string( keyContextArg ); + mpw_free_string( sitesFormatArg ); + mpw_free_string( sitesRedactedArg ); // Operation summary. const char *identicon = mpw_identicon( fullName, masterPassword ); @@ -416,6 +438,7 @@ int main(int argc, char *const argv[]) { dbg( "sitesPath : %s\n", sitesPath ); dbg( "siteName : %s\n", siteName ); dbg( "siteCounter : %u\n", siteCounter ); + dbg( "saveValue : %s\n", saveValue ); dbg( "keyPurpose : %s (%u)\n", mpw_nameForPurpose( keyPurpose ), keyPurpose ); dbg( "keyContext : %s\n", keyContext ); dbg( "passwordType : %s (%u)\n", mpw_nameForType( passwordType ), passwordType ); @@ -453,25 +476,42 @@ int main(int argc, char *const argv[]) { fprintf( stdout, "%s\n", sitePassword ); mpw_free_string( sitePassword ); } - else if (site && site->content) { - const char *sitePassword = mpw_decrypt( masterKey, site->content, algorithmVersion ); - if (!sitePassword) { - ftl( "Couldn't decrypt site password.\n" ); - mpw_free( masterKey, MPMasterKeySize ); - return EX_SOFTWARE; + + else { + const char *content = NULL; + if (saveValue) { + content = strdup( saveValue ); + if (site) { + // TODO: Doesn't save content for newly created sites. + mpw_free_string( site->content ); + if (!(site->content = mpw_encrypt( masterKey, saveValue, algorithmVersion ))) + err( "Couldn't encrypt site content.\n" ); + } } - fprintf( stdout, "%s\n", sitePassword ); - mpw_free_string( sitePassword ); + else if (site && site->content) { + content = mpw_decrypt( masterKey, site->content, algorithmVersion ); + if (!content) { + ftl( "Couldn't decrypt site content.\n" ); + mpw_free( masterKey, MPMasterKeySize ); + return EX_SOFTWARE; + } + } + + fprintf( stdout, "%s\n", content ); + mpw_free_string( content ); } + if (site && site->url) inf( "See: %s\n", site->url ); mpw_free( masterKey, MPMasterKeySize ); mpw_free_string( siteName ); + mpw_free_string( saveValue ); mpw_free_string( keyContext ); // Update the mpsites file. if (user) { + // TODO: Move this up above the summary and replace the mpw lvars by user/site accessors. if (keyPurpose == MPKeyPurposeAuthentication) { if (!site) site = mpw_marshall_site( user, siteName, passwordType, siteCounter, algorithmVersion ); diff --git a/platform-independent/cli-c/cli/mpw-tests.c b/platform-independent/cli-c/cli/mpw-tests.c index 616a5a9d..d167605c 100644 --- a/platform-independent/cli-c/cli/mpw-tests.c +++ b/platform-independent/cli-c/cli/mpw-tests.c @@ -29,7 +29,7 @@ int main(int argc, char *const argv[]) { xmlChar *masterPassword = mpw_xmlTestCaseString( testCase, "masterPassword" ); xmlChar *keyID = mpw_xmlTestCaseString( testCase, "keyID" ); xmlChar *siteName = mpw_xmlTestCaseString( testCase, "siteName" ); - uint32_t siteCounter = mpw_xmlTestCaseInteger( testCase, "siteCounter" ); + MPCounterValue siteCounter = (MPCounterValue)mpw_xmlTestCaseInteger( testCase, "siteCounter" ); xmlChar *passwordTypeString = mpw_xmlTestCaseString( testCase, "passwordType" ); xmlChar *keyPurposeString = mpw_xmlTestCaseString( testCase, "keyPurpose" ); xmlChar *keyContext = mpw_xmlTestCaseString( testCase, "keyContext" );