2
0

Compare commits

...

10 Commits

Author SHA1 Message Date
Maarten Billemont
f8043ae16d Fix chdir test & make curses work when stdout is redirected. 2017-10-06 14:59:29 -04:00
Maarten Billemont
7150f2f5c5 Fix test to match context against question keyword. 2017-10-06 14:58:42 -04:00
Maarten Billemont
81bd2e3065 Make mpw_mkpw symlinkable. 2017-10-06 13:33:08 -04:00
Maarten Billemont
78c9618807 Test chdir. 2017-09-26 11:07:02 -04:00
Maarten Billemont
bed8939b8a Debug issue in testSiteState 2017-09-25 18:35:57 -04:00
Maarten Billemont
9443d93500 Remove MPW_COLOR from core, safer decryption, more standard password input & curses dialog. 2017-09-25 18:34:12 -04:00
Maarten Billemont
877eba66be strdup also isn't standard. 2017-09-25 10:51:14 -04:00
Maarten Billemont
3af8aba40c Make source more standard C11 w/POSIX:2008 CLI. 2017-09-25 10:33:31 -04:00
Maarten Billemont
7ece02c73d Remove stale 2.6-cli-3 2017-09-25 03:05:38 -04:00
Maarten Billemont
ebbd2b3ac4 Re-distribute 2.6-cli-3 with standard build fixes. 2017-09-25 03:01:24 -04:00
19 changed files with 354 additions and 248 deletions

View File

@@ -176,7 +176,7 @@ static const char *mpw_sitePasswordFromCrypt_v0(
// Decrypt
const uint8_t *plainBytes = mpw_aes_decrypt( masterKey, MPMasterKeySize, cipherBuf, &bufSize );
mpw_free( &cipherBuf, cipherBufSize );
const char *plainText = strndup( (char *)plainBytes, bufSize );
const char *plainText = mpw_strndup( (char *)plainBytes, bufSize );
mpw_free( &plainBytes, bufSize );
if (!plainText)
err( "AES decryption error: %s\n", strerror( errno ) );

View File

@@ -28,7 +28,7 @@ char *mpw_get_token(const char **in, const char *eol, char *delim) {
// Find characters up to the first delim.
size_t len = strcspn( *in, delim );
char *token = len && len <= (size_t)(eol - *in)? strndup( *in, len ): NULL;
char *token = len && len <= (size_t)(eol - *in)? mpw_strndup( *in, len ): NULL;
// Advance past the delimitor.
*in = min( eol, *in + len + 1 );
@@ -38,30 +38,12 @@ char *mpw_get_token(const char **in, const char *eol, char *delim) {
time_t mpw_mktime(
const char *time) {
struct tm tm = { .tm_isdst = -1, .tm_gmtoff = 0 };
struct tm tm = { .tm_isdst = -1 };
if (time && sscanf( time, "%4d-%2d-%2dT%2d:%2d:%2dZ",
&tm.tm_year, &tm.tm_mon, &tm.tm_mday,
&tm.tm_hour, &tm.tm_min, &tm.tm_sec ) == 6) {
tm.tm_year -= 1900; // tm_year 0 = rfc3339 year 1900
tm.tm_mon -= 1; // tm_mon 0 = rfc3339 month 1
/*
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_ARITHMETIC (code=EXC_I386_DIV, subcode=0x0)
frame #0: 0x00007fff9fe4d219 libsystem_notify.dylib`_nc_table_find_64 + 22
libsystem_notify.dylib`_nc_table_find_64:
-> 0x7fff9fe4d219 <+22>: divl 0x4(%rdi)
0x7fff9fe4d21c <+25>: movq 0x8(%rdi), %rax
0x7fff9fe4d220 <+29>: movq (%rax,%rdx,8), %rcx
0x7fff9fe4d224 <+33>: xorl %eax, %eax
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_ARITHMETIC (code=EXC_I386_DIV, subcode=0x0)
* frame #0: 0x00007fff9fe4d219 libsystem_notify.dylib`_nc_table_find_64 + 22
frame #1: 0x00007fff9fe4a21e libsystem_notify.dylib`registration_node_find + 53
frame #2: 0x00007fff9fe4b78d libsystem_notify.dylib`notify_check + 105
frame #3: 0x00007fff9fccc164 libsystem_c.dylib`notify_check_tz + 24
frame #4: 0x00007fff9fccbd97 libsystem_c.dylib`tzsetwall_basic + 45
frame #5: 0x00007fff9fccdcd0 libsystem_c.dylib`mktime + 46
frame #6: 0x0000000100009496 mpw`mpw_mktime(time="2017-04-16T03:16:35Z") at mpw-marshal-util.c:47
*/
return mktime( &tm );
}
@@ -73,7 +55,7 @@ json_object *mpw_get_json_section(
json_object *obj, const char *section) {
json_object *json_value = obj;
char *sectionTokenizer = strdup( section ), *sectionToken = sectionTokenizer;
char *sectionTokenizer = mpw_strdup( section ), *sectionToken = sectionTokenizer;
for (sectionToken = strtok( sectionToken, "." ); sectionToken; sectionToken = strtok( NULL, "." ))
if (!json_object_object_get_ex( json_value, sectionToken, &json_value ) || !json_value) {
trc( "While resolving: %s: Missing value for: %s\n", section, sectionToken );

View File

@@ -33,8 +33,8 @@ MPMarshalledUser *mpw_marshal_user(
return NULL;
*user = (MPMarshalledUser){
.fullName = strdup( fullName ),
.masterPassword = strdup( masterPassword ),
.fullName = mpw_strdup( fullName ),
.masterPassword = mpw_strdup( masterPassword ),
.algorithm = algorithmVersion,
.redacted = true,
@@ -46,7 +46,7 @@ MPMarshalledUser *mpw_marshal_user(
.sites = NULL,
};
return user;
};
}
MPMarshalledSite *mpw_marshal_site(
MPMarshalledUser *user, const char *siteName, const MPResultType resultType,
@@ -57,7 +57,7 @@ MPMarshalledSite *mpw_marshal_site(
MPMarshalledSite *site = &user->sites[user->sites_count - 1];
*site = (MPMarshalledSite){
.name = strdup( siteName ),
.name = mpw_strdup( siteName ),
.content = NULL,
.type = resultType,
.counter = siteCounter,
@@ -74,7 +74,7 @@ MPMarshalledSite *mpw_marshal_site(
.questions = NULL,
};
return site;
};
}
MPMarshalledQuestion *mpw_marshal_question(
MPMarshalledSite *site, const char *keyword) {
@@ -86,7 +86,7 @@ MPMarshalledQuestion *mpw_marshal_question(
MPMarshalledQuestion *question = &site->questions[site->questions_count - 1];
*question = (MPMarshalledQuestion){
.keyword = strdup( keyword ),
.keyword = mpw_strdup( keyword ),
.content = NULL,
.type = MPResultTypeTemplatePhrase,
};
@@ -198,9 +198,9 @@ static bool mpw_marshal_write_flat(
else {
// Redacted
if (site->type & MPSiteFeatureExportContent && site->content && strlen( site->content ))
content = strdup( site->content );
content = mpw_strdup( site->content );
if (site->loginType & MPSiteFeatureExportContent && site->loginContent && strlen( site->loginContent ))
loginContent = strdup( site->loginContent );
loginContent = mpw_strdup( site->loginContent );
}
if (strftime( dateString, sizeof( dateString ), "%FT%TZ", gmtime( &site->lastUsed ) ))
@@ -284,9 +284,9 @@ static bool mpw_marshal_write_json(
else {
// Redacted
if (site->type & MPSiteFeatureExportContent && site->content && strlen( site->content ))
content = strdup( site->content );
content = mpw_strdup( site->content );
if (site->loginType & MPSiteFeatureExportContent && site->loginContent && strlen( site->loginContent ))
loginContent = strdup( site->loginContent );
loginContent = mpw_strdup( site->loginContent );
}
json_object *json_site = json_object_new_object();
@@ -397,9 +397,9 @@ static void mpw_marshal_read_flat_info(
if (strcmp( headerName, "Algorithm" ) == 0)
info->algorithm = (MPAlgorithmVersion)atoi( headerValue );
if (strcmp( headerName, "Full Name" ) == 0 || strcmp( headerName, "User Name" ) == 0)
info->fullName = strdup( headerValue );
info->fullName = mpw_strdup( headerValue );
if (strcmp( headerName, "Key ID" ) == 0)
info->keyID = strdup( headerValue );
info->keyID = mpw_strdup( headerValue );
if (strcmp( headerName, "Passwords" ) == 0)
info->redacted = strcmp( headerValue, "VISIBLE" ) != 0;
if (strcmp( headerName, "Date" ) == 0)
@@ -456,18 +456,18 @@ static MPMarshalledUser *mpw_marshal_read_flat(
char *headerValue = mpw_get_token( &positionInLine, endOfLine, "\n" );
if (!headerName || !headerValue) {
error->type = MPMarshalErrorStructure;
error->description = mpw_str( "Invalid header: %s", strndup( positionInLine, (size_t)(endOfLine - positionInLine) ) );
error->description = mpw_str( "Invalid header: %s", mpw_strndup( positionInLine, (size_t)(endOfLine - positionInLine) ) );
return NULL;
}
if (strcmp( headerName, "Format" ) == 0)
format = (unsigned int)atoi( headerValue );
if (strcmp( headerName, "Full Name" ) == 0 || strcmp( headerName, "User Name" ) == 0)
fullName = strdup( headerValue );
fullName = mpw_strdup( headerValue );
if (strcmp( headerName, "Avatar" ) == 0)
avatar = (unsigned int)atoi( headerValue );
if (strcmp( headerName, "Key ID" ) == 0)
keyID = strdup( headerValue );
keyID = mpw_strdup( headerValue );
if (strcmp( headerName, "Algorithm" ) == 0) {
int value = atoi( headerValue );
if (value < MPAlgorithmVersionFirst || value > MPAlgorithmVersionLast) {
@@ -527,11 +527,11 @@ static MPMarshalledUser *mpw_marshal_read_flat(
str_uses = mpw_get_token( &positionInLine, endOfLine, " \t\n" );
char *typeAndVersion = mpw_get_token( &positionInLine, endOfLine, " \t\n" );
if (typeAndVersion) {
str_type = strdup( strtok( typeAndVersion, ":" ) );
str_algorithm = strdup( strtok( NULL, "" ) );
str_type = mpw_strdup( strtok( typeAndVersion, ":" ) );
str_algorithm = mpw_strdup( strtok( NULL, "" ) );
mpw_free_string( &typeAndVersion );
}
str_counter = strdup( "1" );
str_counter = mpw_strdup( "1" );
siteLoginName = NULL;
siteName = mpw_get_token( &positionInLine, endOfLine, "\t\n" );
siteContent = mpw_get_token( &positionInLine, endOfLine, "\n" );
@@ -542,9 +542,9 @@ static MPMarshalledUser *mpw_marshal_read_flat(
str_uses = mpw_get_token( &positionInLine, endOfLine, " \t\n" );
char *typeAndVersionAndCounter = mpw_get_token( &positionInLine, endOfLine, " \t\n" );
if (typeAndVersionAndCounter) {
str_type = strdup( strtok( typeAndVersionAndCounter, ":" ) );
str_algorithm = strdup( strtok( NULL, ":" ) );
str_counter = strdup( strtok( NULL, "" ) );
str_type = mpw_strdup( strtok( typeAndVersionAndCounter, ":" ) );
str_algorithm = mpw_strdup( strtok( NULL, ":" ) );
str_counter = mpw_strdup( strtok( NULL, "" ) );
mpw_free_string( &typeAndVersionAndCounter );
}
siteLoginName = mpw_get_token( &positionInLine, endOfLine, "\t\n" );
@@ -608,9 +608,9 @@ static MPMarshalledUser *mpw_marshal_read_flat(
else {
// Redacted
if (siteContent && strlen( siteContent ))
site->content = strdup( siteContent );
site->content = mpw_strdup( siteContent );
if (siteLoginName && strlen( siteLoginName ))
site->loginContent = strdup( siteLoginName );
site->loginContent = mpw_strdup( siteLoginName );
}
}
else {
@@ -650,8 +650,8 @@ static void mpw_marshal_read_json_info(
// Section: "user"
info->algorithm = (MPAlgorithmVersion)mpw_get_json_int( json_file, "user.algorithm", MPAlgorithmVersionCurrent );
info->fullName = strdup( mpw_get_json_string( json_file, "user.full_name", NULL ) );
info->keyID = strdup( mpw_get_json_string( json_file, "user.key_id", NULL ) );
info->fullName = mpw_strdup( mpw_get_json_string( json_file, "user.full_name", NULL ) );
info->keyID = mpw_strdup( mpw_get_json_string( json_file, "user.key_id", NULL ) );
json_object_put( json_file );
}
@@ -772,7 +772,7 @@ static MPMarshalledUser *mpw_marshal_read_json(
}
site->loginType = siteLoginType;
site->url = siteURL? strdup( siteURL ): NULL;
site->url = siteURL? mpw_strdup( siteURL ): NULL;
site->uses = siteUses;
site->lastUsed = siteLastUsed;
if (!user->redacted) {
@@ -792,9 +792,9 @@ static MPMarshalledUser *mpw_marshal_read_json(
else {
// Redacted
if (siteContent && strlen( siteContent ))
site->content = strdup( siteContent );
site->content = mpw_strdup( siteContent );
if (siteLoginName && strlen( siteLoginName ))
site->loginContent = strdup( siteLoginName );
site->loginContent = mpw_strdup( siteLoginName );
}
json_object_iter json_site_question;
@@ -813,7 +813,7 @@ static MPMarshalledUser *mpw_marshal_read_json(
else {
// Redacted
if (answerContent && strlen( answerContent ))
question->content = strdup( answerContent );
question->content = mpw_strdup( answerContent );
}
}
}

View File

@@ -262,3 +262,40 @@ const char mpw_characterFromClass(char characterClass, uint8_t seedByte) {
return classCharacters[seedByte % strlen( classCharacters )];
}
MPIdenticon mpw_identicon(const char *fullName, const char *masterPassword) {
const char *leftArm[] = { "", "", "", "" };
const char *rightArm[] = { "", "", "", "" };
const char *body[] = { "", "", "", "", "", "" };
const char *accessory[] = {
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""
};
const uint8_t *identiconSeed = NULL;
if (fullName && strlen( fullName ) && masterPassword && strlen( masterPassword ))
identiconSeed = mpw_hash_hmac_sha256(
(const uint8_t *)masterPassword, strlen( masterPassword ),
(const uint8_t *)fullName, strlen( fullName ) );
if (!identiconSeed)
return (MPIdenticon){
.leftArm = "",
.body = "",
.rightArm = "",
.accessory = "",
.color=0,
};
MPIdenticon identicon = {
.leftArm = leftArm[identiconSeed[0] % (sizeof( leftArm ) / sizeof( leftArm[0] ))],
.body = body[identiconSeed[1] % (sizeof( body ) / sizeof( body[0] ))],
.rightArm = rightArm[identiconSeed[2] % (sizeof( rightArm ) / sizeof( rightArm[0] ))],
.accessory = accessory[identiconSeed[3] % (sizeof( accessory ) / sizeof( accessory[0] ))],
.color = (uint8_t)(identiconSeed[4] % 7 + 1),
};
mpw_free( &identiconSeed, 32 );
return identicon;
}

View File

@@ -111,6 +111,14 @@ typedef mpw_enum ( uint32_t, MPCounterValue ) {
MPCounterValueLast = UINT32_MAX,
};
typedef struct {
const char *leftArm;
const char *body;
const char *rightArm;
const char *accessory;
uint8_t color;
} MPIdenticon;
//// Type utilities.
/**
@@ -157,4 +165,7 @@ const char *mpw_charactersInClass(char characterClass);
*/
const char mpw_characterFromClass(char characterClass, uint8_t seedByte);
/** @return A fingerprint for a user. */
MPIdenticon mpw_identicon(const char *fullName, const char *masterPassword);
#endif // _MPW_TYPES_H

View File

@@ -20,12 +20,6 @@
#include <ctype.h>
#include <errno.h>
#if MPW_COLOR
#include <unistd.h>
#include <curses.h>
#include <term.h>
#endif
#if MPW_CPERCIVA
#include <scrypt/crypto_scrypt.h>
#include <scrypt/sha256.h>
@@ -141,7 +135,7 @@ void mpw_zero(void *buffer, size_t bufferSize) {
uint8_t *b = buffer;
for (; bufferSize > 0; --bufferSize)
*b++ = 0;
*b++ = 0;
}
bool __mpw_free(void **buffer, const size_t bufferSize) {
@@ -274,9 +268,10 @@ uint8_t const *mpw_hash_hmac_sha256(const uint8_t *key, const size_t keySize, co
return mac;
}
// We do our best to not fail on odd buf's, eg. non-padded cipher texts.
static uint8_t const *mpw_aes(bool encrypt, const uint8_t *key, const size_t keySize, const uint8_t *buf, size_t *bufSize) {
if (!key || keySize < 16)
if (!key || keySize < 16 || !*bufSize)
return NULL;
// IV = zero
@@ -284,9 +279,9 @@ static uint8_t const *mpw_aes(bool encrypt, const uint8_t *key, const size_t key
mpw_zero( iv, sizeof iv );
// Add PKCS#7 padding
uint32_t aesSize = (uint32_t)*bufSize;
if (encrypt)
aesSize = (aesSize / 16) * 16 + 16;
uint32_t aesSize = ((uint32_t)*bufSize + 15 / 16) * 16; // round up to block size.
if (encrypt && !(*bufSize % 16)) // add pad block if plain text fits block size.
encrypt += 16;
uint8_t aesBuf[aesSize];
memcpy( aesBuf, buf, *bufSize );
memset( aesBuf + *bufSize, aesSize - *bufSize, aesSize - *bufSize );
@@ -302,7 +297,7 @@ static uint8_t const *mpw_aes(bool encrypt, const uint8_t *key, const size_t key
// Truncate PKCS#7 padding
if (encrypt)
*bufSize = aesSize;
else
else if (*bufSize % 16 == 0 && resultBuf[aesSize - 1] < 16)
*bufSize -= resultBuf[aesSize - 1];
return resultBuf;
@@ -343,7 +338,7 @@ const char *mpw_hotp(const uint8_t *key, size_t keySize, uint64_t movingFactor,
// Render the OTP as `digits` decimal digits.
otp %= (int)pow(10, digits);
return strdup( mpw_str( "%0*d", digits, otp ) );
return mpw_strdup( mpw_str( "%0*d", digits, otp ) );
}
#endif
@@ -443,99 +438,6 @@ const char *mpw_hex_l(uint32_t number) {
return mpw_hex( &buf, sizeof( buf ) );
}
#if MPW_COLOR
static char *str_tputs;
static int str_tputs_cursor;
static const int str_tputs_max = 256;
static bool mpw_setupterm() {
if (!isatty( STDERR_FILENO ))
return false;
static bool termsetup;
if (!termsetup) {
int errret;
if (!(termsetup = (setupterm( NULL, STDERR_FILENO, &errret ) == OK))) {
wrn( "Terminal doesn't support color (setupterm errret %d).\n", errret );
return false;
}
}
return true;
}
static int mpw_tputc(int c) {
if (++str_tputs_cursor < str_tputs_max) {
str_tputs[str_tputs_cursor] = (char)c;
return OK;
}
return ERR;
}
static char *mpw_tputs(const char *str, int affcnt) {
if (str_tputs)
mpw_free( &str_tputs, str_tputs_max );
str_tputs = calloc( str_tputs_max, sizeof( char ) );
str_tputs_cursor = -1;
char *result = tputs( str, affcnt, mpw_tputc ) == ERR? NULL: strndup( str_tputs, str_tputs_max );
if (str_tputs)
mpw_free( &str_tputs, str_tputs_max );
return result;
}
#endif
const char *mpw_identicon(const char *fullName, const char *masterPassword) {
const char *leftArm[] = { "", "", "", "" };
const char *rightArm[] = { "", "", "", "" };
const char *body[] = { "", "", "", "", "", "" };
const char *accessory[] = {
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""
};
const uint8_t *identiconSeed = mpw_hash_hmac_sha256(
(const uint8_t *)masterPassword, strlen( masterPassword ),
(const uint8_t *)fullName, strlen( fullName ) );
if (!identiconSeed)
return NULL;
char *colorString, *resetString;
#ifdef MPW_COLOR
if (mpw_setupterm()) {
uint8_t colorIdentifier = (uint8_t)(identiconSeed[4] % 7 + 1);
colorString = mpw_tputs( tparm( tgetstr( "AF", NULL ), colorIdentifier ), 1 );
resetString = mpw_tputs( tgetstr( "me", NULL ), 1 );
}
else
#endif
{
colorString = calloc( 1, sizeof( char ) );
resetString = calloc( 1, sizeof( char ) );
}
char *identicon = (char *)calloc( 256, sizeof( char ) );
snprintf( identicon, 256, "%s%s%s%s%s%s",
colorString,
leftArm[identiconSeed[0] % (sizeof( leftArm ) / sizeof( leftArm[0] ))],
body[identiconSeed[1] % (sizeof( body ) / sizeof( body[0] ))],
rightArm[identiconSeed[2] % (sizeof( rightArm ) / sizeof( rightArm[0] ))],
accessory[identiconSeed[3] % (sizeof( accessory ) / sizeof( accessory[0] ))],
resetString );
mpw_free( &identiconSeed, 32 );
mpw_free_strings( &colorString, &resetString, NULL );
return identicon;
}
/**
* @return the amount of bytes used by UTF-8 to encode a single character that starts with the given byte.
*/
@@ -566,3 +468,31 @@ const size_t mpw_utf8_strlen(const char *utf8String) {
return charlen;
}
char *mpw_strdup(const char *src) {
if (!src)
return NULL;
size_t len = strlen( src );
char *dst = malloc( len + 1 );
memcpy( dst, src, len );
dst[len] = '\0';
return dst;
}
char *mpw_strndup(const char *src, size_t max) {
if (!src)
return NULL;
size_t len = 0;
for (; len < max && src[len] != '\0'; ++len);
char *dst = malloc( len + 1 );
memcpy( dst, src, len );
dst[len] = '\0';
return dst;
}

View File

@@ -203,13 +203,14 @@ MPKeyID mpw_id_buf(const void *buf, size_t length);
/** Compare two fingerprints for equality.
* @return true if the buffers represent identical fingerprints. */
bool mpw_id_buf_equals(const char *id1, const char *id2);
/** Encode a visual fingerprint for a user.
* @return A newly allocated string. */
const char *mpw_identicon(const char *fullName, const char *masterPassword);
//// String utilities.
/** @return The amount of display characters in the given UTF-8 string. */
const size_t mpw_utf8_strlen(const char *utf8String);
/** Drop-in for POSIX strdup(3). */
char *mpw_strdup(const char *src);
/** Drop-in for POSIX strndup(3). */
char *mpw_strndup(const char *src, size_t max);
#endif // _MPW_UTIL_H

View File

@@ -20,6 +20,7 @@ package com.lyndir.masterpassword;
import static org.testng.Assert.*;
import com.google.common.base.Charsets;
import com.lyndir.lhunath.opal.system.CodeUtils;
import com.lyndir.lhunath.opal.system.logging.Logger;
import java.util.Random;
@@ -112,6 +113,7 @@ public class MPMasterKeyTest {
StringBuilder password = new StringBuilder();
for (int p = 0; p < 8; ++p)
password.append( (char) (random.nextInt( Character.MAX_CODE_POINT - Character.MIN_CODE_POINT ) + Character.MIN_CODE_POINT) );
logger.dbg( "password: %s", CodeUtils.encodeHex( password.toString().getBytes( Charsets.UTF_8 ) ) );
for (final MPMasterKey.Version version : MPMasterKey.Version.values()) {
MPResultType resultType = MPResultType.StoredPersonal;
@@ -119,10 +121,12 @@ public class MPMasterKeyTest {
// Test site state
String state = masterKey.siteState( testCase.getSiteName(), testCase.getSiteCounter(), testCase.getKeyPurpose(),
testCase.getKeyContext(), resultType, password.toString(), version );
String result = masterKey.siteResult( testCase.getSiteName(), testCase.getSiteCounter(), testCase.getKeyPurpose(),
testCase.getKeyContext(), resultType, state, version );
logger.dbg( "result: %s", CodeUtils.encodeHex( result.getBytes( Charsets.UTF_8 ) ) );
assertEquals(
masterKey.siteResult( testCase.getSiteName(), testCase.getSiteCounter(), testCase.getKeyPurpose(),
testCase.getKeyContext(), resultType, state, version ),
result,
password.toString(),
"[testSiteState] state mismatch: " + testCase );
}

View File

@@ -16,7 +16,8 @@
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
#define _WITH_GETLINE
#define _POSIX_C_SOURCE 200809L
#include "mpw-cli-util.h"
#include <unistd.h>
@@ -27,18 +28,21 @@
#include <errno.h>
#include <sysexits.h>
#define MPW_MAX_INPUT 60
#if MPW_COLOR
#include <curses.h>
#include <term.h>
#endif
#include "mpw-util.h"
/** Read the value of an environment variable.
* @return A newly allocated string or NULL if the variable doesn't exist. */
const char *mpw_getenv(const char *variableName) {
char *envBuf = getenv( variableName );
return envBuf? strdup( envBuf ): NULL;
return envBuf? mpw_strdup( envBuf ): NULL;
}
/** Use the askpass program to prompt the user.
* @return A newly allocated string or NULL if askpass is not supported or an error occurred. */
char *mpw_askpass(const char *prompt) {
const char *askpass = mpw_getenv( MP_ENV_askpass );
@@ -90,15 +94,60 @@ char *mpw_askpass(const char *prompt) {
return NULL;
}
/** Ask the user a question.
* @return A newly allocated string or NULL if an error occurred trying to read from the user. */
const char *mpw_getline(const char *prompt) {
static const char *_mpw_getline(const char *prompt, bool silent) {
// Get answer from askpass.
char *answer = mpw_askpass( prompt );
if (answer)
return answer;
#if MPW_COLOR
// Initialize a curses screen.
SCREEN *screen = newterm( NULL, stderr, stdin );
start_color();
init_pair( 1, COLOR_WHITE, COLOR_BLUE );
init_pair( 2, COLOR_BLACK, COLOR_WHITE );
int rows, cols;
getmaxyx( stdscr, rows, cols );
// Display a dialog box.
int width = max( prompt? (int)strlen( prompt ): 0, MPW_MAX_INPUT ) + 6;
char *version = "mpw v" stringify_def( MP_VERSION );
mvprintw( rows - 1, (cols - (int)strlen( version )) / 2, "%s", version );
attron( A_BOLD );
color_set( 2, NULL );
mvprintw( rows / 2 - 1, (cols - width) / 2, "%s%*s%s", "*", width - 2, "", "*" );
mvprintw( rows / 2 - 1, (cols - (int)strlen( prompt )) / 2, "%s", prompt );
color_set( 1, NULL );
mvprintw( rows / 2 + 0, (cols - width) / 2, "%s%*s%s", "|", width - 2, "", "|" );
mvprintw( rows / 2 + 1, (cols - width) / 2, "%s%*s%s", "|", width - 2, "", "|" );
mvprintw( rows / 2 + 2, (cols - width) / 2, "%s%*s%s", "|", width - 2, "", "|" );
// Read response.
color_set( 2, NULL );
attron( A_STANDOUT );
int result = ERR;
char str[MPW_MAX_INPUT + 1];
if (silent) {
mvprintw( rows / 2 + 1, (cols - 5) / 2, "[ * ]" );
refresh();
noecho();
result = mvgetnstr( rows / 2 + 1, (cols - 1) / 2, str, MPW_MAX_INPUT );
echo();
} else {
mvprintw( rows / 2 + 1, (cols - (MPW_MAX_INPUT + 2)) / 2, "%*s", MPW_MAX_INPUT + 2, "" );
refresh();
echo();
result = mvgetnstr( rows / 2 + 1, (cols - MPW_MAX_INPUT) / 2, str, MPW_MAX_INPUT );
}
attrset( 0 );
endwin();
delscreen( screen );
return result == ERR? NULL: mpw_strndup( str, MPW_MAX_INPUT );
#else
// Get password from terminal.
fprintf( stderr, "%s ", prompt );
@@ -112,56 +161,44 @@ const char *mpw_getline(const char *prompt) {
// Remove trailing newline.
answer[lineSize - 1] = '\0';
return answer;
#endif
}
const char *mpw_getline(const char *prompt) {
return _mpw_getline( prompt, false );
}
/** Ask the user for a password.
* @return A newly allocated string or NULL if an error occurred trying to read from the user. */
const char *mpw_getpass(const char *prompt) {
// Get password from askpass.
const char *password = mpw_askpass( prompt );
if (password)
return password;
// Get password from terminal.
char *answer = getpass( prompt );
if (!answer)
return NULL;
password = strdup( answer );
mpw_zero( answer, strlen( answer ) );
return password;
return _mpw_getline( prompt, true );
}
/** Get the absolute path to the mpw configuration file with the given prefix name and file extension.
* Resolves the file <prefix.extension> as located in the <.mpw.d> directory inside the user's home directory
* or current directory if it couldn't be resolved.
* @return A newly allocated string. */
const char *mpw_path(const char *prefix, const char *extension) {
// Resolve user's home directory.
char *homeDir = NULL;
if (!homeDir)
if ((homeDir = getenv( "HOME" )))
homeDir = strdup( homeDir );
homeDir = mpw_strdup( homeDir );
if (!homeDir)
if ((homeDir = getenv( "USERPROFILE" )))
homeDir = strdup( homeDir );
homeDir = mpw_strdup( homeDir );
if (!homeDir) {
const char *homeDrive = getenv( "HOMEDRIVE" ), *homePath = getenv( "HOMEPATH" );
if (homeDrive && homePath)
homeDir = strdup( mpw_str( "%s%s", homeDrive, homePath ) );
homeDir = mpw_strdup( mpw_str( "%s%s", homeDrive, homePath ) );
}
if (!homeDir) {
struct passwd *passwd = getpwuid( getuid() );
if (passwd)
homeDir = strdup( passwd->pw_dir );
homeDir = mpw_strdup( passwd->pw_dir );
}
if (!homeDir)
homeDir = getcwd( NULL, 0 );
// Compose filename.
char *path = strdup( mpw_str( "%s.%s", prefix, extension ) );
char *path = mpw_strdup( mpw_str( "%s.%s", prefix, extension ) );
// This is a filename, remove all potential directory separators.
for (char *slash; (slash = strstr( path, "/" )); *slash = '_');
@@ -173,32 +210,31 @@ const char *mpw_path(const char *prefix, const char *extension) {
free( path );
if (homePath)
path = strdup( homePath );
path = mpw_strdup( homePath );
}
return path;
}
/** mkdir all the directories up to the directory of the given file path.
* @return true if the file's path exists. */
bool mpw_mkdirs(const char *filePath) {
if (!filePath)
return false;
// The path to mkdir is the filePath without the last path component.
char *pathEnd = strrchr( filePath, '/' );
char *path = pathEnd? strndup( filePath, (size_t)(pathEnd - filePath) ): NULL;
if (!path)
return false;
// Save the cwd and for absolute paths, start at the root.
char *cwd = getcwd( NULL, 0 );
if (*filePath == '/')
chdir( "/" );
if (chdir( "/" ) == ERR)
return false;
// The path to mkdir is the filePath without the last path component.
char *pathEnd = strrchr( filePath, '/' );
if (pathEnd)
return true;
// Walk the path.
bool success = true;
char *path = mpw_strndup( filePath, (size_t)(pathEnd - filePath) );
for (char *dirName = strtok( path, "/" ); success && dirName; dirName = strtok( NULL, "/" )) {
if (!strlen( dirName ))
continue;
@@ -214,8 +250,6 @@ bool mpw_mkdirs(const char *filePath) {
return success;
}
/** Read until EOF from the given file descriptor.
* @return A newly allocated string or NULL if the read buffer couldn't be allocated or an error occurred. */
char *mpw_read_fd(int fd) {
char *buf = NULL;
@@ -229,8 +263,6 @@ char *mpw_read_fd(int fd) {
return buf;
}
/** Read the file contents of a given file.
* @return A newly allocated string or NULL if the read buffer couldn't be allocated. */
char *mpw_read_file(FILE *file) {
if (!file)
@@ -244,3 +276,73 @@ char *mpw_read_file(FILE *file) {
return buf;
}
#if MPW_COLOR
static char *str_tputs;
static int str_tputs_cursor;
static const int str_tputs_max = 256;
static bool mpw_setupterm() {
if (!isatty( STDERR_FILENO ))
return false;
static bool termsetup;
if (!termsetup) {
int errret;
if (!(termsetup = (setupterm( NULL, STDERR_FILENO, &errret ) == OK))) {
wrn( "Terminal doesn't support color (setupterm errret %d).\n", errret );
return false;
}
}
return true;
}
static int mpw_tputc(int c) {
if (++str_tputs_cursor < str_tputs_max) {
str_tputs[str_tputs_cursor] = (char)c;
return OK;
}
return ERR;
}
static char *mpw_tputs(const char *str, int affcnt) {
if (str_tputs)
mpw_free( &str_tputs, str_tputs_max );
str_tputs = calloc( str_tputs_max, sizeof( char ) );
str_tputs_cursor = -1;
char *result = tputs( str, affcnt, mpw_tputc ) == ERR? NULL: mpw_strndup( str_tputs, str_tputs_max );
if (str_tputs)
mpw_free( &str_tputs, str_tputs_max );
return result;
}
#endif
const char *mpw_identicon_str(MPIdenticon identicon) {
char *colorString, *resetString;
#ifdef MPW_COLOR
if (mpw_setupterm()) {
colorString = mpw_tputs( tparm( tgetstr( "AF", NULL ), identicon.color ), 1 );
resetString = mpw_tputs( tgetstr( "me", NULL ), 1 );
}
else
#endif
{
colorString = calloc( 1, sizeof( char ) );
resetString = calloc( 1, sizeof( char ) );
}
const char *str = mpw_str( "%s%s%s%s%s%s",
colorString, identicon.leftArm, identicon.body, identicon.rightArm, identicon.accessory, resetString );
mpw_free_strings( &colorString, &resetString, NULL );
return mpw_strdup( str );
}

View File

@@ -19,6 +19,7 @@
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include "mpw-types.h"
#ifndef MP_VERSION
#define MP_VERSION ?
@@ -62,3 +63,7 @@ char *mpw_read_fd(int fd);
/** Read the file contents of a given file.
* @return A newly allocated string or NULL the read buffer couldn't be allocated. */
char *mpw_read_file(FILE *file);
/** Encode a visual fingerprint for a user.
* @return A newly allocated string. */
const char *mpw_identicon_str(MPIdenticon identicon);

View File

@@ -16,6 +16,8 @@
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
@@ -273,51 +275,51 @@ void cli_free(Arguments *args, Operation *operation) {
void cli_args(Arguments *args, Operation *operation, const int argc, char *const argv[]) {
for (int opt; (opt = getopt( argc, argv, "u:U:m:M:t:P:c:a:p:C:f:F:R:vqh" )) != EOF;
optarg? mpw_zero( optarg, strlen( optarg ) ): NULL)
optarg? mpw_zero( optarg, strlen( optarg ) ): (void)0)
switch (opt) {
case 'u':
args->fullName = optarg && strlen( optarg )? strdup( optarg ): NULL;
args->fullName = optarg && strlen( optarg )? mpw_strdup( optarg ): NULL;
operation->allowPasswordUpdate = false;
break;
case 'U':
args->fullName = optarg && strlen( optarg )? strdup( optarg ): NULL;
args->fullName = optarg && strlen( optarg )? mpw_strdup( optarg ): NULL;
operation->allowPasswordUpdate = true;
break;
case 'm':
args->masterPasswordFD = optarg && strlen( optarg )? strdup( optarg ): NULL;
args->masterPasswordFD = optarg && strlen( optarg )? mpw_strdup( optarg ): NULL;
break;
case 'M':
// Passing your master password via the command-line is insecure. Testing purposes only.
args->masterPassword = optarg && strlen( optarg )? strdup( optarg ): NULL;
args->masterPassword = optarg && strlen( optarg )? mpw_strdup( optarg ): NULL;
break;
case 't':
args->resultType = optarg && strlen( optarg )? strdup( optarg ): NULL;
args->resultType = optarg && strlen( optarg )? mpw_strdup( optarg ): NULL;
break;
case 'P':
args->resultParam = optarg && strlen( optarg )? strdup( optarg ): NULL;
args->resultParam = optarg && strlen( optarg )? mpw_strdup( optarg ): NULL;
break;
case 'c':
args->siteCounter = optarg && strlen( optarg )? strdup( optarg ): NULL;
args->siteCounter = optarg && strlen( optarg )? mpw_strdup( optarg ): NULL;
break;
case 'a':
args->algorithmVersion = optarg && strlen( optarg )? strdup( optarg ): NULL;
args->algorithmVersion = optarg && strlen( optarg )? mpw_strdup( optarg ): NULL;
break;
case 'p':
args->keyPurpose = optarg && strlen( optarg )? strdup( optarg ): NULL;
args->keyPurpose = optarg && strlen( optarg )? mpw_strdup( optarg ): NULL;
break;
case 'C':
args->keyContext = optarg && strlen( optarg )? strdup( optarg ): NULL;
args->keyContext = optarg && strlen( optarg )? mpw_strdup( optarg ): NULL;
break;
case 'f':
args->sitesFormat = optarg && strlen( optarg )? strdup( optarg ): NULL;
args->sitesFormat = optarg && strlen( optarg )? mpw_strdup( optarg ): NULL;
operation->sitesFormatFixed = false;
break;
case 'F':
args->sitesFormat = optarg && strlen( optarg )? strdup( optarg ): NULL;
args->sitesFormat = optarg && strlen( optarg )? mpw_strdup( optarg ): NULL;
operation->sitesFormatFixed = true;
break;
case 'R':
args->sitesRedacted = optarg && strlen( optarg )? strdup( optarg ): NULL;
args->sitesRedacted = optarg && strlen( optarg )? mpw_strdup( optarg ): NULL;
break;
case 'v':
++mpw_verbosity;
@@ -349,13 +351,13 @@ void cli_args(Arguments *args, Operation *operation, const int argc, char *const
}
if (optind < argc && argv[optind])
args->siteName = strdup( argv[optind] );
args->siteName = mpw_strdup( argv[optind] );
}
void cli_fullName(Arguments *args, Operation *operation) {
if ((!operation->fullName || !strlen( operation->fullName )) && args->fullName)
operation->fullName = strdup( args->fullName );
operation->fullName = mpw_strdup( args->fullName );
if (!operation->fullName || !strlen( operation->fullName ))
do {
@@ -378,7 +380,7 @@ void cli_masterPassword(Arguments *args, Operation *operation) {
}
if ((!operation->masterPassword || !strlen( operation->masterPassword )) && args->masterPassword)
operation->masterPassword = strdup( args->masterPassword );
operation->masterPassword = mpw_strdup( args->masterPassword );
if (!operation->masterPassword || !strlen( operation->masterPassword ))
do {
@@ -395,7 +397,7 @@ void cli_masterPassword(Arguments *args, Operation *operation) {
void cli_siteName(Arguments *args, Operation *operation) {
if ((!operation->siteName || !strlen( operation->siteName )) && args->siteName)
operation->siteName = strdup( args->siteName );
operation->siteName = mpw_strdup( args->siteName );
if (!operation->siteName || !strlen( operation->siteName ))
do {
operation->siteName = mpw_getline( "Site name:" );
@@ -439,7 +441,7 @@ void cli_keyContext(Arguments *args, Operation *operation) {
if (!args->keyContext)
return;
operation->keyContext = strdup( args->keyContext );
operation->keyContext = mpw_strdup( args->keyContext );
}
void cli_user(Arguments *args, Operation *operation) {
@@ -495,7 +497,7 @@ void cli_user(Arguments *args, Operation *operation) {
}
if (operation->user) {
mpw_free_string( &operation->user->masterPassword );
operation->user->masterPassword = strdup( operation->masterPassword );
operation->user->masterPassword = mpw_strdup( operation->masterPassword );
}
}
mpw_free_string( &sitesInputData );
@@ -550,7 +552,7 @@ void cli_question(Arguments __unused *args, Operation *operation) {
case MPKeyPurposeRecovery:
for (size_t q = 0; !operation->question && q < operation->site->questions_count; ++q)
if ((!operation->keyContext && !strlen( (&operation->site->questions[q])->keyword )) ||
(operation->keyContext && strcmp( (&operation->site->questions[q])->keyword, operation->keyContext ) != 0))
(operation->keyContext && strcmp( (&operation->site->questions[q])->keyword, operation->keyContext ) == 0))
operation->question = &operation->site->questions[q];
// If no question from mpsites, create a new one.
@@ -562,7 +564,7 @@ void cli_question(Arguments __unused *args, Operation *operation) {
void cli_operation(Arguments __unused *args, Operation *operation) {
operation->identicon = mpw_identicon( operation->user->fullName, operation->user->masterPassword );
operation->identicon = mpw_identicon_str( mpw_identicon( operation->user->fullName, operation->user->masterPassword ) );
if (!operation->site)
abort();
@@ -571,23 +573,23 @@ void cli_operation(Arguments __unused *args, Operation *operation) {
case MPKeyPurposeAuthentication: {
operation->purposeResult = "password";
operation->resultType = operation->site->type;
operation->resultState = operation->site->content? strdup( operation->site->content ): NULL;
operation->resultState = operation->site->content? mpw_strdup( operation->site->content ): NULL;
operation->siteCounter = operation->site->counter;
break;
}
case MPKeyPurposeIdentification: {
operation->purposeResult = "login";
operation->resultType = operation->site->loginType;
operation->resultState = operation->site->loginContent? strdup( operation->site->loginContent ): NULL;
operation->resultState = operation->site->loginContent? mpw_strdup( operation->site->loginContent ): NULL;
operation->siteCounter = MPCounterValueInitial;
break;
}
case MPKeyPurposeRecovery: {
mpw_free_string( &operation->keyContext );
operation->purposeResult = "answer";
operation->keyContext = operation->question->keyword? strdup( operation->question->keyword ): NULL;
operation->keyContext = operation->question->keyword? mpw_strdup( operation->question->keyword ): NULL;
operation->resultType = operation->question->type;
operation->resultState = operation->question->content? strdup( operation->question->content ): NULL;
operation->resultState = operation->question->content? mpw_strdup( operation->question->content ): NULL;
operation->siteCounter = MPCounterValueInitial;
break;
}
@@ -653,7 +655,7 @@ void cli_resultParam(Arguments *args, Operation *operation) {
if (!args->resultParam)
return;
operation->resultParam = strdup( args->resultParam );
operation->resultParam = mpw_strdup( args->resultParam );
}
void cli_algorithmVersion(Arguments *args, Operation *operation) {
@@ -713,18 +715,18 @@ void cli_mpw(Arguments *args, Operation *operation) {
switch (operation->keyPurpose) {
case MPKeyPurposeAuthentication: {
mpw_free_string( &operation->site->content );
operation->site->content = strdup( operation->resultState );
operation->site->content = mpw_strdup( operation->resultState );
break;
}
case MPKeyPurposeIdentification: {
mpw_free_string( &operation->site->loginContent );
operation->site->loginContent = strdup( operation->resultState );
operation->site->loginContent = mpw_strdup( operation->resultState );
break;
}
case MPKeyPurposeRecovery: {
mpw_free_string( &operation->question->content );
operation->question->content = strdup( operation->resultState );
operation->question->content = mpw_strdup( operation->resultState );
break;
}
}
@@ -735,7 +737,7 @@ void cli_mpw(Arguments *args, Operation *operation) {
// resultParam defaults to state.
if (!operation->resultParam && operation->resultState)
operation->resultParam = strdup( operation->resultState );
operation->resultParam = mpw_strdup( operation->resultState );
// Generate result.
const char *result = mpw_siteResult( masterKey, operation->site->name, operation->siteCounter,

View File

@@ -261,7 +261,10 @@ chr() {
# Outputs the decimal ASCII value of the given character.
#
ord() {
printf '%d' "'$1"
local str=$1 s
for (( s=0; s < ${#str}; ++s )); do
printf '%d' "'${str:s:1}"
done
} # _____________________________________________________________________
@@ -274,7 +277,10 @@ ord() {
# Outputs the hexadecimal ASCII value of the given character.
#
hex() {
printf '%x' "'$1"
local str=$1 s
for (( s=0; s < ${#str}; ++s )); do
printf '%02X' "'${str:s:1}"
done
} # _____________________________________________________________________
@@ -287,7 +293,10 @@ hex() {
# Outputs the character that has the given hexadecimal ASCII value.
#
unhex() {
printf "\\x$1"
local hex=$1 h
for (( h=0; h < ${#hex}; h+=2 )); do
printf "\\x${hex:h:2}"
done
} # _____________________________________________________________________
@@ -1422,6 +1431,29 @@ shorten() {
# ______________________________________________________________________
# |__ CdSource ________________________________________________________________|
#
# cdsource [file]
#
# Change the current directory into the directory where the file is located, resolving symlinks.
#
cdsource() {
local source=${1:-${BASH_SOURCE[1]}}
while [[ $source ]]; do
[[ $source = */* ]] && cd "${source%/*}"
if [[ -L ${source##*/} ]]; then
source=$(readlink "${source##*/}")
else
source=
fi
done
} # _____________________________________________________________________
# ______________________________________________________________________
# |__ Up ________________________________________________________________|
#

View File

@@ -1,4 +1,6 @@
#!/usr/bin/env bash
source bashlib
cdsource
getword() {
local cat=$1 pop_limit=$2 words=()
@@ -19,9 +21,7 @@ declare -A categoryByCharacter=(
['v']=verb
)
templates=(
nvan
anvr
anavan
ran # ~32.0 bit
)
permutations=1

View File

@@ -1 +1 @@
mpw-2.6-cli-3-0-g0ff6c93a.tar.gz
mpw-2.6-cli-3-0-ga85eff42.tar.gz

View File

@@ -1 +1 @@
mpw-2.6-cli-3-0-g0ff6c93a.tar.gz.sig
mpw-2.6-cli-3-0-ga85eff42.tar.gz.sig

Binary file not shown.