Initial commit

This commit is contained in:
2025-03-07 19:22:02 +01:00
commit 4a98255d83
55743 changed files with 5280367 additions and 0 deletions
+242
View File
@@ -0,0 +1,242 @@
#include <string>
#include <vector>
#include "napi.h"
#include "keytar.h"
#include "async.h"
using keytar::KEYTAR_OP_RESULT;
SetPasswordWorker::SetPasswordWorker(
const std::string& service,
const std::string& account,
const std::string& password,
const Napi::Env &env
) : AsyncWorker(env),
service(service),
account(account),
password(password),
deferred(Napi::Promise::Deferred::New(env)) {}
SetPasswordWorker::~SetPasswordWorker() {}
Napi::Promise SetPasswordWorker::Promise() {
return deferred.Promise();
}
void SetPasswordWorker::Execute() {
std::string error;
KEYTAR_OP_RESULT result = keytar::SetPassword(service,
account,
password,
&error);
if (result == keytar::FAIL_ERROR) {
SetError(error.c_str());
}
}
void SetPasswordWorker::OnOK() {
Napi::HandleScope scope(Env());
deferred.Resolve(Env().Undefined());
}
void SetPasswordWorker::OnError(Napi::Error const &error) {
Napi::HandleScope scope(Env());
deferred.Reject(error.Value());
}
GetPasswordWorker::GetPasswordWorker(
const std::string& service,
const std::string& account,
const Napi::Env &env
) : AsyncWorker(env),
service(service),
account(account),
deferred(Napi::Promise::Deferred::New(env)) {}
GetPasswordWorker::~GetPasswordWorker() {}
Napi::Promise GetPasswordWorker::Promise() {
return deferred.Promise();
}
void GetPasswordWorker::Execute() {
std::string error;
KEYTAR_OP_RESULT result = keytar::GetPassword(service,
account,
&password,
&error);
if (result == keytar::FAIL_ERROR) {
SetError(error.c_str());
} else if (result == keytar::FAIL_NONFATAL) {
success = false;
} else {
success = true;
}
}
void GetPasswordWorker::OnOK() {
Napi::HandleScope scope(Env());
Napi::Value val = Env().Null();
if (success) {
val = Napi::String::New(Env(), password.data(),
password.length());
}
deferred.Resolve(val);
}
void GetPasswordWorker::OnError(Napi::Error const &error) {
Napi::HandleScope scope(Env());
deferred.Reject(error.Value());
}
DeletePasswordWorker::DeletePasswordWorker(
const std::string& service,
const std::string& account,
const Napi::Env &env
) : AsyncWorker(env),
service(service),
account(account),
deferred(Napi::Promise::Deferred::New(env)) {}
DeletePasswordWorker::~DeletePasswordWorker() {}
Napi::Promise DeletePasswordWorker::Promise() {
return deferred.Promise();
}
void DeletePasswordWorker::Execute() {
std::string error;
KEYTAR_OP_RESULT result = keytar::DeletePassword(service, account, &error);
if (result == keytar::FAIL_ERROR) {
SetError(error.c_str());
} else if (result == keytar::FAIL_NONFATAL) {
success = false;
} else {
success = true;
}
}
void DeletePasswordWorker::OnOK() {
Napi::HandleScope scope(Env());
deferred.Resolve(Napi::Boolean::New(Env(), success));
}
void DeletePasswordWorker::OnError(Napi::Error const &error) {
Napi::HandleScope scope(Env());
deferred.Reject(error.Value());
}
FindPasswordWorker::FindPasswordWorker(
const std::string& service,
const Napi::Env &env
) : AsyncWorker(env),
service(service),
deferred(Napi::Promise::Deferred::New(env)) {}
FindPasswordWorker::~FindPasswordWorker() {}
Napi::Promise FindPasswordWorker::Promise() {
return deferred.Promise();
}
void FindPasswordWorker::Execute() {
std::string error;
KEYTAR_OP_RESULT result = keytar::FindPassword(service,
&password,
&error);
if (result == keytar::FAIL_ERROR) {
SetError(error.c_str());
} else if (result == keytar::FAIL_NONFATAL) {
success = false;
} else {
success = true;
}
}
void FindPasswordWorker::OnOK() {
Napi::HandleScope scope(Env());
Napi::Value val = Env().Null();
if (success) {
val = Napi::String::New(Env(), password.data(),
password.length());
}
deferred.Resolve(val);
}
void FindPasswordWorker::OnError(Napi::Error const &error) {
Napi::HandleScope scope(Env());
deferred.Reject(error.Value());
}
FindCredentialsWorker::FindCredentialsWorker(
const std::string& service,
const Napi::Env &env
) : AsyncWorker(env),
service(service),
deferred(Napi::Promise::Deferred::New(env)) {}
FindCredentialsWorker::~FindCredentialsWorker() {}
Napi::Promise FindCredentialsWorker::Promise() {
return deferred.Promise();
}
void FindCredentialsWorker::Execute() {
std::string error;
KEYTAR_OP_RESULT result = keytar::FindCredentials(service,
&credentials,
&error);
if (result == keytar::FAIL_ERROR) {
SetError(error.c_str());
} else if (result == keytar::FAIL_NONFATAL) {
success = false;
} else {
success = true;
}
}
void FindCredentialsWorker::OnOK() {
Napi::HandleScope scope(Env());
Napi::Env env = Env();
if (success) {
Napi::Array val = Napi::Array::New(env, credentials.size());
unsigned int idx = 0;
std::vector<keytar::Credentials>::iterator it;
for (it = credentials.begin(); it != credentials.end(); it++) {
keytar::Credentials cred = *it;
Napi::Object obj = Napi::Object::New(env);
Napi::String account = Napi::String::New(env,
cred.first.data(),
cred.first.length());
Napi::String password = Napi::String::New(env,
cred.second.data(),
cred.second.length());
#ifndef _WIN32
#pragma GCC diagnostic ignored "-Wunused-result"
#endif
obj.Set("account", account);
#ifndef _WIN32
#pragma GCC diagnostic ignored "-Wunused-result"
#endif
obj.Set("password", password);
(val).Set(idx, obj);
++idx;
}
deferred.Resolve(val);
} else {
deferred.Resolve(Napi::Array::New(env, 0));
}
}
void FindCredentialsWorker::OnError(Napi::Error const &error) {
Napi::HandleScope scope(Env());
deferred.Reject(error.Value());
}
+103
View File
@@ -0,0 +1,103 @@
#ifndef SRC_ASYNC_H_
#define SRC_ASYNC_H_
#include <string>
#include "napi.h"
#include "credentials.h"
class SetPasswordWorker : public Napi::AsyncWorker {
public:
SetPasswordWorker(const std::string& service, const std::string& account, const std::string& password,
const Napi::Env &env);
~SetPasswordWorker();
void Execute();
void OnOK();
void OnError(Napi::Error const &error);
Napi::Promise Promise();
private:
const std::string service;
const std::string account;
const std::string password;
Napi::Promise::Deferred deferred;
};
class GetPasswordWorker : public Napi::AsyncWorker {
public:
GetPasswordWorker(const std::string& service, const std::string& account,
const Napi::Env &env);
~GetPasswordWorker();
void Execute();
void OnOK();
void OnError(Napi::Error const &error);
Napi::Promise Promise();
private:
const std::string service;
const std::string account;
std::string password;
bool success;
const Napi::Promise::Deferred deferred;
};
class DeletePasswordWorker : public Napi::AsyncWorker {
public:
DeletePasswordWorker(const std::string& service, const std::string& account,
const Napi::Env &env);
~DeletePasswordWorker();
void Execute();
void OnOK();
void OnError(Napi::Error const &error);
Napi::Promise Promise();
private:
const std::string service;
const std::string account;
bool success;
Napi::Promise::Deferred deferred;
};
class FindPasswordWorker : public Napi::AsyncWorker {
public:
FindPasswordWorker(const std::string& service, const Napi::Env &env);
~FindPasswordWorker();
void Execute();
void OnOK();
void OnError(Napi::Error const &error);
Napi::Promise Promise();
private:
const std::string service;
std::string password;
bool success;
const Napi::Promise::Deferred deferred;
};
class FindCredentialsWorker : public Napi::AsyncWorker {
public:
FindCredentialsWorker(const std::string& service, const Napi::Env &env);
~FindCredentialsWorker();
void Execute();
void OnOK();
void OnError(Napi::Error const &error);
Napi::Promise Promise();
private:
const std::string service;
std::vector<keytar::Credentials> credentials;
bool success;
const Napi::Promise::Deferred deferred;
};
#endif // SRC_ASYNC_H_
+13
View File
@@ -0,0 +1,13 @@
#ifndef SRC_CREDENTIALS_H_
#define SRC_CREDENTIALS_H_
#include <string>
#include <utility>
namespace keytar {
typedef std::pair<std::string, std::string> Credentials;
}
#endif // SRC_CREDENTIALS_H_
+41
View File
@@ -0,0 +1,41 @@
#ifndef SRC_KEYTAR_H_
#define SRC_KEYTAR_H_
#include <string>
#include <vector>
#include "credentials.h"
namespace keytar {
enum KEYTAR_OP_RESULT {
SUCCESS,
FAIL_ERROR,
FAIL_NONFATAL
};
KEYTAR_OP_RESULT SetPassword(const std::string& service,
const std::string& account,
const std::string& password,
std::string* error);
KEYTAR_OP_RESULT GetPassword(const std::string& service,
const std::string& account,
std::string* password,
std::string* error);
KEYTAR_OP_RESULT DeletePassword(const std::string& service,
const std::string& account,
std::string* error);
KEYTAR_OP_RESULT FindPassword(const std::string& service,
std::string* password,
std::string* error);
KEYTAR_OP_RESULT FindCredentials(const std::string& service,
std::vector<Credentials>*,
std::string* error);
} // namespace keytar
#endif // SRC_KEYTAR_H_
+296
View File
@@ -0,0 +1,296 @@
#include <Security/Security.h>
#include "keytar.h"
#include "credentials.h"
namespace keytar {
/**
* Converts a CFString to a std::string
*
* This either uses CFStringGetCStringPtr or (if that fails)
* CFStringGetCString, trying to be as efficient as possible.
*/
const std::string CFStringToStdString(CFStringRef cfstring) {
const char* cstr = CFStringGetCStringPtr(cfstring, kCFStringEncodingUTF8);
if (cstr != NULL) {
return std::string(cstr);
}
CFIndex length = CFStringGetLength(cfstring);
// Worst case: 2 bytes per character + NUL
CFIndex cstrPtrLen = length * 2 + 1;
char* cstrPtr = static_cast<char*>(malloc(cstrPtrLen));
Boolean result = CFStringGetCString(cfstring,
cstrPtr,
cstrPtrLen,
kCFStringEncodingUTF8);
std::string stdstring;
if (result) {
stdstring = std::string(cstrPtr);
}
free(cstrPtr);
return stdstring;
}
const std::string errorStatusToString(OSStatus status) {
std::string errorStr;
CFStringRef errorMessageString = SecCopyErrorMessageString(status, NULL);
const char* errorCStringPtr = CFStringGetCStringPtr(errorMessageString,
kCFStringEncodingUTF8);
if (errorCStringPtr) {
errorStr = std::string(errorCStringPtr);
} else {
errorStr = std::string("An unknown error occurred.");
}
CFRelease(errorMessageString);
return errorStr;
}
KEYTAR_OP_RESULT AddPassword(const std::string& service,
const std::string& account,
const std::string& password,
std::string* error) {
OSStatus status = SecKeychainAddGenericPassword(NULL,
service.length(),
service.data(),
account.length(),
account.data(),
password.length(),
password.data(),
NULL);
if (status != errSecSuccess) {
*error = errorStatusToString(status);
return FAIL_ERROR;
}
return SUCCESS;
}
KEYTAR_OP_RESULT SetPassword(const std::string& service,
const std::string& account,
const std::string& password,
std::string* error) {
SecKeychainItemRef item;
OSStatus result = SecKeychainFindGenericPassword(NULL,
service.length(),
service.data(),
account.length(),
account.data(),
NULL,
NULL,
&item);
if (result == errSecItemNotFound) {
return AddPassword(service, account, password, error);
} else if (result != errSecSuccess) {
*error = errorStatusToString(result);
return FAIL_ERROR;
}
result = SecKeychainItemModifyAttributesAndData(item,
NULL,
password.length(),
password.data());
CFRelease(item);
if (result != errSecSuccess) {
*error = errorStatusToString(result);
return FAIL_ERROR;
}
return SUCCESS;
}
KEYTAR_OP_RESULT GetPassword(const std::string& service,
const std::string& account,
std::string* password,
std::string* error) {
void *data;
UInt32 length;
OSStatus status = SecKeychainFindGenericPassword(NULL,
service.length(),
service.data(),
account.length(),
account.data(),
&length,
&data,
NULL);
if (status == errSecItemNotFound) {
return FAIL_NONFATAL;
} else if (status != errSecSuccess) {
*error = errorStatusToString(status);
return FAIL_ERROR;
}
*password = std::string(reinterpret_cast<const char*>(data), length);
SecKeychainItemFreeContent(NULL, data);
return SUCCESS;
}
KEYTAR_OP_RESULT DeletePassword(const std::string& service,
const std::string& account,
std::string* error) {
SecKeychainItemRef item;
OSStatus status = SecKeychainFindGenericPassword(NULL,
service.length(),
service.data(),
account.length(),
account.data(),
NULL,
NULL,
&item);
if (status == errSecItemNotFound) {
// Item could not be found, so already deleted.
return FAIL_NONFATAL;
} else if (status != errSecSuccess) {
*error = errorStatusToString(status);
return FAIL_ERROR;
}
status = SecKeychainItemDelete(item);
CFRelease(item);
if (status != errSecSuccess) {
*error = errorStatusToString(status);
return FAIL_ERROR;
}
return SUCCESS;
}
KEYTAR_OP_RESULT FindPassword(const std::string& service,
std::string* password,
std::string* error) {
SecKeychainItemRef item;
void *data;
UInt32 length;
OSStatus status = SecKeychainFindGenericPassword(NULL,
service.length(),
service.data(),
0,
NULL,
&length,
&data,
&item);
if (status == errSecItemNotFound) {
return FAIL_NONFATAL;
} else if (status != errSecSuccess) {
*error = errorStatusToString(status);
return FAIL_ERROR;
}
*password = std::string(reinterpret_cast<const char*>(data), length);
SecKeychainItemFreeContent(NULL, data);
CFRelease(item);
return SUCCESS;
}
Credentials getCredentialsForItem(CFDictionaryRef item) {
CFStringRef service = (CFStringRef) CFDictionaryGetValue(item,
kSecAttrService);
CFStringRef account = (CFStringRef) CFDictionaryGetValue(item,
kSecAttrAccount);
CFMutableDictionaryRef query = CFDictionaryCreateMutable(
NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFDictionaryAddValue(query, kSecAttrService, service);
CFDictionaryAddValue(query, kSecClass, kSecClassGenericPassword);
CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitOne);
CFDictionaryAddValue(query, kSecReturnAttributes, kCFBooleanTrue);
CFDictionaryAddValue(query, kSecReturnData, kCFBooleanTrue);
CFDictionaryAddValue(query, kSecAttrAccount, account);
Credentials cred;
CFTypeRef result = NULL;
OSStatus status = SecItemCopyMatching((CFDictionaryRef) query, &result);
CFRelease(query);
if (status == errSecSuccess) {
CFDataRef passwordData = (CFDataRef) CFDictionaryGetValue(
(CFDictionaryRef) result,
CFSTR("v_Data"));
CFStringRef password = CFStringCreateFromExternalRepresentation(
NULL,
passwordData,
kCFStringEncodingUTF8);
cred = Credentials(
CFStringToStdString(account),
CFStringToStdString(password));
CFRelease(password);
}
if (result != NULL) {
CFRelease(result);
}
return cred;
}
KEYTAR_OP_RESULT FindCredentials(const std::string& service,
std::vector<Credentials>* credentials,
std::string* error) {
CFStringRef serviceStr = CFStringCreateWithCString(
NULL,
service.c_str(),
kCFStringEncodingUTF8);
CFMutableDictionaryRef query = CFDictionaryCreateMutable(
NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFDictionaryAddValue(query, kSecClass, kSecClassGenericPassword);
CFDictionaryAddValue(query, kSecAttrService, serviceStr);
CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitAll);
CFDictionaryAddValue(query, kSecReturnRef, kCFBooleanTrue);
CFDictionaryAddValue(query, kSecReturnAttributes, kCFBooleanTrue);
CFTypeRef result = NULL;
OSStatus status = SecItemCopyMatching((CFDictionaryRef) query, &result);
CFRelease(serviceStr);
CFRelease(query);
if (status == errSecSuccess) {
CFArrayRef resultArray = (CFArrayRef) result;
int resultCount = CFArrayGetCount(resultArray);
for (int idx = 0; idx < resultCount; idx++) {
CFDictionaryRef item = (CFDictionaryRef) CFArrayGetValueAtIndex(
resultArray,
idx);
Credentials cred = getCredentialsForItem(item);
credentials->push_back(cred);
}
} else if (status == errSecItemNotFound) {
return FAIL_NONFATAL;
} else {
*error = errorStatusToString(status);
return FAIL_ERROR;
}
if (result != NULL) {
CFRelease(result);
}
return SUCCESS;
}
} // namespace keytar
+184
View File
@@ -0,0 +1,184 @@
#include "keytar.h"
// This is needed to make the builds on Ubuntu 14.04 / libsecret v0.16 work.
// The API we use has already stabilized.
#define SECRET_API_SUBJECT_TO_CHANGE
#include <libsecret/secret.h>
#include <stdio.h>
#include <string.h>
namespace keytar {
namespace {
static const SecretSchema schema = {
"org.freedesktop.Secret.Generic", SECRET_SCHEMA_NONE, {
{ "service", SECRET_SCHEMA_ATTRIBUTE_STRING },
{ "account", SECRET_SCHEMA_ATTRIBUTE_STRING }
}
};
} // namespace
KEYTAR_OP_RESULT SetPassword(const std::string& service,
const std::string& account,
const std::string& password,
std::string* errStr) {
GError* error = NULL;
secret_password_store_sync(
&schema, // The schema.
SECRET_COLLECTION_DEFAULT, // Default collection.
(service + "/" + account).c_str(), // The label.
password.c_str(), // The password.
NULL, // Cancellable. (unneeded)
&error, // Reference to the error.
"service", service.c_str(),
"account", account.c_str(),
NULL); // End of arguments.
if (error != NULL) {
*errStr = std::string(error->message);
g_error_free(error);
return FAIL_ERROR;
}
return SUCCESS;
}
KEYTAR_OP_RESULT GetPassword(const std::string& service,
const std::string& account,
std::string* password,
std::string* errStr) {
GError* error = NULL;
gchar* raw_password = secret_password_lookup_sync(
&schema, // The schema.
NULL, // Cancellable. (unneeded)
&error, // Reference to the error.
"service", service.c_str(),
"account", account.c_str(),
NULL); // End of arguments.
if (error != NULL) {
*errStr = std::string(error->message);
g_error_free(error);
return FAIL_ERROR;
}
if (raw_password == NULL)
return FAIL_NONFATAL;
*password = raw_password;
secret_password_free(raw_password);
return SUCCESS;
}
KEYTAR_OP_RESULT DeletePassword(const std::string& service,
const std::string& account,
std::string* errStr) {
GError* error = NULL;
gboolean result = secret_password_clear_sync(
&schema, // The schema.
NULL, // Cancellable. (unneeded)
&error, // Reference to the error.
"service", service.c_str(),
"account", account.c_str(),
NULL); // End of arguments.
if (error != NULL) {
*errStr = std::string(error->message);
g_error_free(error);
return FAIL_ERROR;
}
if (!result)
return FAIL_NONFATAL;
return SUCCESS;
}
KEYTAR_OP_RESULT FindPassword(const std::string& service,
std::string* password,
std::string* errStr) {
GError* error = NULL;
gchar* raw_password = secret_password_lookup_sync(
&schema, // The schema.
NULL, // Cancellable. (unneeded)
&error, // Reference to the error.
"service", service.c_str(),
NULL); // End of arguments.
if (error != NULL) {
*errStr = std::string(error->message);
g_error_free(error);
return FAIL_ERROR;
}
if (raw_password == NULL)
return FAIL_NONFATAL;
*password = raw_password;
secret_password_free(raw_password);
return SUCCESS;
}
KEYTAR_OP_RESULT FindCredentials(const std::string& service,
std::vector<Credentials>* credentials,
std::string* errStr) {
GError* error = NULL;
GHashTable* attributes = g_hash_table_new(NULL, NULL);
g_hash_table_replace(attributes,
(gpointer) "service",
(gpointer) service.c_str());
GList* items = secret_service_search_sync(
NULL,
&schema, // The schema.
attributes,
static_cast<SecretSearchFlags>(SECRET_SEARCH_ALL | SECRET_SEARCH_UNLOCK |
SECRET_SEARCH_LOAD_SECRETS),
NULL, // Cancellable. (unneeded)
&error); // Reference to the error.
g_hash_table_destroy(attributes);
if (error != NULL) {
*errStr = std::string(error->message);
g_error_free(error);
return FAIL_ERROR;
}
GList* current = items;
for (current = items; current != NULL; current = current->next) {
SecretItem* item = reinterpret_cast<SecretItem*>(current->data);
GHashTable* itemAttrs = secret_item_get_attributes(item);
char* account = strdup(
reinterpret_cast<char*>(g_hash_table_lookup(itemAttrs, "account")));
SecretValue* secret = secret_item_get_secret(item);
char* password = strdup(secret_value_get_text(secret));
if (account == NULL || password == NULL) {
if (account)
free(account);
if (password)
free(password);
continue;
}
credentials->push_back(Credentials(account, password));
free(account);
free(password);
}
return SUCCESS;
}
} // namespace keytar
+272
View File
@@ -0,0 +1,272 @@
#include "keytar.h"
#define UNICODE
#include <windows.h>
#include <wincred.h>
#include "credentials.h"
namespace keytar {
LPWSTR utf8ToWideChar(std::string utf8) {
int wide_char_length = MultiByteToWideChar(CP_UTF8,
0,
utf8.c_str(),
-1,
NULL,
0);
if (wide_char_length == 0) {
return NULL;
}
LPWSTR result = new WCHAR[wide_char_length];
if (MultiByteToWideChar(CP_UTF8,
0,
utf8.c_str(),
-1,
result,
wide_char_length) == 0) {
delete[] result;
return NULL;
}
return result;
}
std::string wideCharToAnsi(LPWSTR wide_char) {
if (wide_char == NULL) {
return std::string();
}
int ansi_length = WideCharToMultiByte(CP_ACP,
0,
wide_char,
-1,
NULL,
0,
NULL,
NULL);
if (ansi_length == 0) {
return std::string();
}
char* buffer = new char[ansi_length];
if (WideCharToMultiByte(CP_ACP,
0,
wide_char,
-1,
buffer,
ansi_length,
NULL,
NULL) == 0) {
delete[] buffer;
return std::string();
}
std::string result = std::string(buffer);
delete[] buffer;
return result;
}
std::string wideCharToUtf8(LPWSTR wide_char) {
if (wide_char == NULL) {
return std::string();
}
int utf8_length = WideCharToMultiByte(CP_UTF8,
0,
wide_char,
-1,
NULL,
0,
NULL,
NULL);
if (utf8_length == 0) {
return std::string();
}
char* buffer = new char[utf8_length];
if (WideCharToMultiByte(CP_UTF8,
0,
wide_char,
-1,
buffer,
utf8_length,
NULL,
NULL) == 0) {
delete[] buffer;
return std::string();
}
std::string result = std::string(buffer);
delete[] buffer;
return result;
}
std::string getErrorMessage(DWORD errorCode) {
LPWSTR errBuffer;
::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL, errorCode, 0, (LPWSTR) &errBuffer, 0, NULL);
std::string errMsg = wideCharToAnsi(errBuffer);
LocalFree(errBuffer);
return errMsg;
}
KEYTAR_OP_RESULT SetPassword(const std::string& service,
const std::string& account,
const std::string& password,
std::string* errStr) {
LPWSTR target_name = utf8ToWideChar(service + '/' + account);
if (target_name == NULL) {
return FAIL_ERROR;
}
LPWSTR user_name = utf8ToWideChar(account);
if (user_name == NULL) {
return FAIL_ERROR;
}
CREDENTIAL cred = { 0 };
cred.Type = CRED_TYPE_GENERIC;
cred.TargetName = target_name;
cred.UserName = user_name;
cred.CredentialBlobSize = password.size();
cred.CredentialBlob = (LPBYTE)(password.data());
cred.Persist = CRED_PERSIST_ENTERPRISE;
bool result = ::CredWrite(&cred, 0);
delete[] target_name;
if (!result) {
*errStr = getErrorMessage(::GetLastError());
return FAIL_ERROR;
} else {
return SUCCESS;
}
}
KEYTAR_OP_RESULT GetPassword(const std::string& service,
const std::string& account,
std::string* password,
std::string* errStr) {
LPWSTR target_name = utf8ToWideChar(service + '/' + account);
if (target_name == NULL) {
return FAIL_ERROR;
}
CREDENTIAL* cred;
bool result = ::CredRead(target_name, CRED_TYPE_GENERIC, 0, &cred);
delete[] target_name;
if (!result) {
DWORD code = ::GetLastError();
if (code == ERROR_NOT_FOUND) {
return FAIL_NONFATAL;
} else {
*errStr = getErrorMessage(code);
return FAIL_ERROR;
}
}
*password = std::string(reinterpret_cast<char*>(cred->CredentialBlob),
cred->CredentialBlobSize);
::CredFree(cred);
return SUCCESS;
}
KEYTAR_OP_RESULT DeletePassword(const std::string& service,
const std::string& account,
std::string* errStr) {
LPWSTR target_name = utf8ToWideChar(service + '/' + account);
if (target_name == NULL) {
return FAIL_ERROR;
}
bool result = ::CredDelete(target_name, CRED_TYPE_GENERIC, 0);
delete[] target_name;
if (!result) {
DWORD code = ::GetLastError();
if (code == ERROR_NOT_FOUND) {
return FAIL_NONFATAL;
} else {
*errStr = getErrorMessage(code);
return FAIL_ERROR;
}
}
return SUCCESS;
}
KEYTAR_OP_RESULT FindPassword(const std::string& service,
std::string* password,
std::string* errStr) {
LPWSTR filter = utf8ToWideChar(service + "*");
if (filter == NULL) {
return FAIL_ERROR;
}
DWORD count;
CREDENTIAL** creds;
bool result = ::CredEnumerate(filter, 0, &count, &creds);
delete[] filter;
if (!result) {
DWORD code = ::GetLastError();
if (code == ERROR_NOT_FOUND) {
return FAIL_NONFATAL;
} else {
*errStr = getErrorMessage(code);
return FAIL_ERROR;
}
}
*password = std::string(reinterpret_cast<char*>(creds[0]->CredentialBlob),
creds[0]->CredentialBlobSize);
::CredFree(creds);
return SUCCESS;
}
KEYTAR_OP_RESULT FindCredentials(const std::string& service,
std::vector<Credentials>* credentials,
std::string* errStr) {
LPWSTR filter = utf8ToWideChar(service + "*");
if (filter == NULL) {
*errStr = "Error generating credential filter";
return FAIL_ERROR;
}
DWORD count;
CREDENTIAL **creds;
bool result = ::CredEnumerate(filter, 0, &count, &creds);
if (!result) {
DWORD code = ::GetLastError();
if (code == ERROR_NOT_FOUND) {
return FAIL_NONFATAL;
} else {
*errStr = getErrorMessage(code);
return FAIL_ERROR;
}
}
for (unsigned int i = 0; i < count; ++i) {
CREDENTIAL* cred = creds[i];
if (cred->UserName == NULL || cred->CredentialBlobSize == NULL) {
continue;
}
std::string login = wideCharToUtf8(cred->UserName);
std::string password(
reinterpret_cast<char*>(
cred->CredentialBlob),
cred->CredentialBlobSize);
credentials->push_back(Credentials(login, password));
}
CredFree(creds);
return SUCCESS;
}
} // namespace keytar
+139
View File
@@ -0,0 +1,139 @@
#include "napi.h"
#include "async.h"
namespace {
Napi::Value SetPassword(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
if (!info[0].IsString()) {
Napi::TypeError::New(env, "Parameter 'service' must be a string").
ThrowAsJavaScriptException();
return env.Null();
}
std::string service = info[0].As<Napi::String>();
if (!info[1].IsString()) {
Napi::TypeError::New(env, "Parameter 'username' must be a string").
ThrowAsJavaScriptException();
return env.Null();
}
std::string username = info[1].As<Napi::String>();
if (!info[2].IsString()) {
Napi::TypeError::New(env, "Parameter 'password' must be a string").
ThrowAsJavaScriptException();
return env.Null();
}
std::string password = info[2].As<Napi::String>();
SetPasswordWorker* worker = new SetPasswordWorker(
service,
username,
password,
env);
worker->Queue();
return worker->Promise();
}
Napi::Value GetPassword(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
if (!info[0].IsString()) {
Napi::TypeError::New(env, "Parameter 'service' must be a string").
ThrowAsJavaScriptException();
return env.Null();
}
std::string service = info[0].As<Napi::String>();
if (!info[1].IsString()) {
Napi::TypeError::New(env, "Parameter 'username' must be a string").
ThrowAsJavaScriptException();
return env.Null();
}
std::string username = info[1].As<Napi::String>();
GetPasswordWorker* worker = new GetPasswordWorker(
service,
username,
env);
worker->Queue();
return worker->Promise();
}
Napi::Value DeletePassword(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
if (!info[0].IsString()) {
Napi::TypeError::New(env, "Parameter 'service' must be a string").
ThrowAsJavaScriptException();
return env.Null();
}
std::string service = info[0].As<Napi::String>();
if (!info[1].IsString()) {
Napi::TypeError::New(env, "Parameter 'username' must be a string").
ThrowAsJavaScriptException();
return env.Null();
}
std::string username = info[1].As<Napi::String>();
DeletePasswordWorker *worker = new DeletePasswordWorker(
service,
username,
env);
worker->Queue();
return worker->Promise();
}
Napi::Value FindPassword(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
if (!info[0].IsString()) {
Napi::TypeError::New(env, "Parameter 'service' must be a string").
ThrowAsJavaScriptException();
return env.Null();
}
std::string service = info[0].As<Napi::String>();
FindPasswordWorker* worker = new FindPasswordWorker(
service,
env);
worker->Queue();
return worker->Promise();
}
Napi::Value FindCredentials(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
if (!info[0].IsString()) {
Napi::TypeError::New(env, "Parameter 'service' must be a string").
ThrowAsJavaScriptException();
return env.Null();
}
std::string service = info[0].As<Napi::String>();
FindCredentialsWorker* worker = new FindCredentialsWorker(
service,
env);
worker->Queue();
return worker->Promise();
}
Napi::Object Init(Napi::Env env, Napi::Object exports) {
exports.Set("getPassword", Napi::Function::New(env, GetPassword));
exports.Set("setPassword", Napi::Function::New(env, SetPassword));
exports.Set("deletePassword", Napi::Function::New(env, DeletePassword));
exports.Set("findPassword", Napi::Function::New(env, FindPassword));
exports.Set("findCredentials", Napi::Function::New(env, FindCredentials));
return exports;
}
} // namespace
NODE_API_MODULE(keytar, Init)