mirror of https://github.com/wolfSSL/wolfssh.git
commit
8a00381e0c
|
@ -172,6 +172,9 @@ static void ShowUsage(void)
|
|||
printf(" -R raw untranslated output\n");
|
||||
#endif
|
||||
#endif
|
||||
#ifdef WOLFSSH_AGENT
|
||||
printf(" -a Attempt to use SSH-AGENT\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
@ -815,6 +818,7 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args)
|
|||
byte rawMode = 0;
|
||||
#endif
|
||||
#ifdef WOLFSSH_AGENT
|
||||
byte useAgent = 0;
|
||||
WS_AgentCbActionCtx agentCbCtx;
|
||||
#endif
|
||||
|
||||
|
@ -822,7 +826,7 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args)
|
|||
char** argv = ((func_args*)args)->argv;
|
||||
((func_args*)args)->return_code = 0;
|
||||
|
||||
while ((ch = mygetopt(argc, argv, "?c:eh:i:j:p:tu:xzNP:R")) != -1) {
|
||||
while ((ch = mygetopt(argc, argv, "?ac:eh:i:j:p:tu:xzNP:R")) != -1) {
|
||||
switch (ch) {
|
||||
case 'h':
|
||||
host = myoptarg;
|
||||
|
@ -888,6 +892,13 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args)
|
|||
keepOpen = 1;
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef WOLFSSH_AGENT
|
||||
case 'a':
|
||||
useAgent = 1;
|
||||
break;
|
||||
#endif
|
||||
|
||||
case '?':
|
||||
ShowUsage();
|
||||
exit(EXIT_SUCCESS);
|
||||
|
@ -995,9 +1006,11 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args)
|
|||
wolfSSH_SetUserAuth(ctx, ((func_args*)args)->user_auth);
|
||||
|
||||
#ifdef WOLFSSH_AGENT
|
||||
if (useAgent) {
|
||||
wolfSSH_CTX_set_agent_cb(ctx,
|
||||
wolfSSH_AGENT_DefaultActions, wolfSSH_AGENT_IO_Cb);
|
||||
wolfSSH_CTX_AGENT_enable(ctx, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
ssh = wolfSSH_new(ctx);
|
||||
|
@ -1013,9 +1026,11 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args)
|
|||
wolfSSH_SetUserAuthCtx(ssh, (void*)password);
|
||||
|
||||
#ifdef WOLFSSH_AGENT
|
||||
if (useAgent) {
|
||||
memset(&agentCbCtx, 0, sizeof(agentCbCtx));
|
||||
agentCbCtx.state = AGENT_STATE_INIT;
|
||||
wolfSSH_set_agent_cb_ctx(ssh, &agentCbCtx);
|
||||
}
|
||||
#endif
|
||||
|
||||
wolfSSH_CTX_SetPublicKeyCheck(ctx, wsPublicKeyCheck);
|
||||
|
|
249
src/agent.c
249
src/agent.c
|
@ -173,6 +173,33 @@ static int SendSuccess(WOLFSSH_AGENT_CTX* agent)
|
|||
}
|
||||
|
||||
|
||||
#if 0
|
||||
static int SendRequestIdentities(WOLFSSH_AGENT_CTX* agent)
|
||||
{
|
||||
int ret = WS_SUCCESS;
|
||||
WLOG_ENTER();
|
||||
|
||||
if (agent == NULL)
|
||||
ret = WS_BAD_ARGUMENT;
|
||||
|
||||
if (ret == WS_SUCCESS)
|
||||
ret = PrepareMessage(agent, MSG_ID_SZ);
|
||||
|
||||
if (ret == WS_SUCCESS) {
|
||||
agent->msg[agent->msgIdx++] = MSGID_AGENT_REQUEST_IDENTITIES;
|
||||
ret = BundleMessage(agent, MSG_ID_SZ);
|
||||
}
|
||||
|
||||
if (ret == WS_SUCCESS) {
|
||||
DUMP(agent->msg, agent->msgSz);
|
||||
}
|
||||
|
||||
WLOG_LEAVE(ret);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static int SendIdentitiesAnswer(WOLFSSH_AGENT_CTX* agent)
|
||||
{
|
||||
WOLFSSH_AGENT_ID* id;
|
||||
|
@ -614,20 +641,13 @@ static int PostRequestIds(WOLFSSH_AGENT_CTX* agent)
|
|||
}
|
||||
|
||||
|
||||
static int PostSignRequest(WOLFSSH_AGENT_CTX* agent,
|
||||
byte* keyBlob, word32 keyBlobSz, byte* data, word32 dataSz,
|
||||
word32 flags)
|
||||
static WOLFSSH_AGENT_ID* FindKeyId(WOLFSSH_AGENT_ID* id,
|
||||
const byte* keyBlob, word32 keyBlobSz)
|
||||
{
|
||||
WOLFSSH_AGENT_ID* cur = NULL;
|
||||
int ret;
|
||||
wc_Sha256 sha;
|
||||
byte digest[WC_SHA256_DIGEST_SIZE];
|
||||
|
||||
int ret = WS_SUCCESS;
|
||||
|
||||
if (agent == NULL || keyBlob == NULL || keyBlobSz == 0)
|
||||
ret = WS_BAD_ARGUMENT;
|
||||
|
||||
if (ret == WS_SUCCESS) {
|
||||
ret = wc_InitSha256(&sha);
|
||||
if (ret == 0)
|
||||
ret = wc_Sha256Update(&sha, keyBlob, keyBlobSz);
|
||||
|
@ -637,64 +657,35 @@ static int PostSignRequest(WOLFSSH_AGENT_CTX* agent,
|
|||
}
|
||||
|
||||
if (ret == WS_SUCCESS) {
|
||||
cur = agent->idList;
|
||||
while (cur != NULL &&
|
||||
WMEMCMP(digest, cur->id, WC_SHA256_DIGEST_SIZE) != 0) {
|
||||
cur = cur->next;
|
||||
}
|
||||
|
||||
if (cur == NULL) {
|
||||
WLOG(WS_LOG_AGENT, "Sign: Key not found.");
|
||||
ret = WS_AGENT_NO_KEY_E;
|
||||
}
|
||||
while (id != NULL &&
|
||||
WMEMCMP(digest, id, WC_SHA256_DIGEST_SIZE) != 0 &&
|
||||
WMEMCMP(keyBlob, id->keyBlob, keyBlobSz)) {
|
||||
id = id->next;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == WS_SUCCESS) {
|
||||
if ((cur->keyType == ID_SSH_RSA && flags & AGENT_SIGN_RSA_SHA2_256) ||
|
||||
cur->keyType == ID_ECDSA_SHA2_NISTP256) {
|
||||
|
||||
ret = wc_InitSha256(&sha);
|
||||
if (ret == 0)
|
||||
ret = wc_Sha256Update(&sha, data, dataSz);
|
||||
if (ret == 0) {
|
||||
ret = wc_Sha256Final(&sha, digest);
|
||||
ret = (ret == 0) ? WS_SUCCESS : WS_CRYPTO_FAILED;
|
||||
}
|
||||
}
|
||||
else
|
||||
ret = WS_INVALID_ALGO_ID;
|
||||
return id;
|
||||
}
|
||||
|
||||
if (ret == WS_SUCCESS) {
|
||||
byte sig[256];
|
||||
int sigSz = sizeof(sig);
|
||||
|
||||
if (cur->keyType == ID_SSH_RSA) {
|
||||
#ifndef WOLFSSH_NO_RSA
|
||||
WOLFSSH_AGENT_KEY_RSA* key;
|
||||
RsaKey rsa;
|
||||
static int SignHashRsa(WOLFSSH_AGENT_KEY_RSA* rawKey, enum wc_HashType hashType,
|
||||
const byte* digest, word32 digestSz, byte* sig, word32* sigSz,
|
||||
WC_RNG* rng, void* heap)
|
||||
{
|
||||
RsaKey key;
|
||||
byte encSig[MAX_ENCODED_SIG_SZ];
|
||||
int encSigSz;
|
||||
enum wc_HashType hashType = WC_HASH_TYPE_NONE;
|
||||
int ret = 0;
|
||||
|
||||
key = &cur->key.rsa;
|
||||
wc_InitRsaKey(&key, heap);
|
||||
mp_read_unsigned_bin(&key.n, rawKey->n, rawKey->nSz);
|
||||
mp_read_unsigned_bin(&key.e, rawKey->e, rawKey->eSz);
|
||||
mp_read_unsigned_bin(&key.d, rawKey->d, rawKey->dSz);
|
||||
mp_read_unsigned_bin(&key.p, rawKey->p, rawKey->pSz);
|
||||
mp_read_unsigned_bin(&key.q, rawKey->q, rawKey->qSz);
|
||||
mp_read_unsigned_bin(&key.u, rawKey->iqmp, rawKey->iqmpSz);
|
||||
|
||||
if (flags & AGENT_SIGN_RSA_SHA2_256)
|
||||
hashType = WC_HASH_TYPE_SHA256;
|
||||
else if (flags & AGENT_SIGN_RSA_SHA2_512)
|
||||
hashType = WC_HASH_TYPE_SHA512;
|
||||
|
||||
wc_InitRsaKey(&rsa, agent->heap);
|
||||
mp_read_unsigned_bin(&rsa.n, key->n, key->nSz);
|
||||
mp_read_unsigned_bin(&rsa.e, key->e, key->eSz);
|
||||
mp_read_unsigned_bin(&rsa.d, key->d, key->dSz);
|
||||
mp_read_unsigned_bin(&rsa.p, key->p, key->pSz);
|
||||
mp_read_unsigned_bin(&rsa.q, key->q, key->qSz);
|
||||
mp_read_unsigned_bin(&rsa.u, key->iqmp, key->iqmpSz);
|
||||
|
||||
encSigSz = wc_EncodeSignature(encSig, digest,
|
||||
wc_HashGetDigestSize(hashType),
|
||||
encSigSz = wc_EncodeSignature(encSig, digest, digestSz,
|
||||
wc_HashGetOID(hashType));
|
||||
if (encSigSz <= 0) {
|
||||
WLOG(WS_LOG_DEBUG, "Bad Encode Sig");
|
||||
|
@ -702,33 +693,33 @@ static int PostSignRequest(WOLFSSH_AGENT_CTX* agent,
|
|||
}
|
||||
else {
|
||||
WLOG(WS_LOG_INFO, "Signing hash with RSA.");
|
||||
sigSz = wc_RsaSSL_Sign(encSig, encSigSz, sig, sizeof(sig),
|
||||
&rsa, &agent->rng);
|
||||
if (sigSz <= 0) {
|
||||
*sigSz = wc_RsaSSL_Sign(encSig, encSigSz, sig, *sigSz, &key, rng);
|
||||
if (*sigSz <= 0) {
|
||||
WLOG(WS_LOG_DEBUG, "Bad RSA Sign");
|
||||
ret = WS_RSA_E;
|
||||
}
|
||||
}
|
||||
|
||||
wc_FreeRsaKey(&rsa);
|
||||
wc_FreeRsaKey(&key);
|
||||
if (ret != 0)
|
||||
ret = WS_RSA_E;
|
||||
#endif
|
||||
}
|
||||
else if (cur->keyType == ID_ECDSA_SHA2_NISTP256) {
|
||||
#ifndef WOLFSSH_NO_ECDSA
|
||||
WOLFSSH_AGENT_KEY_ECDSA* key;
|
||||
ecc_key ecc;
|
||||
enum wc_HashType hashType = WC_HASH_TYPE_SHA256;
|
||||
|
||||
key = &cur->key.ecdsa;
|
||||
ret = wc_ecc_import_private_key_ex(key->d, key->dSz,
|
||||
key->q, key->qSz,
|
||||
&ecc, ECC_SECP256R1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int SignHashEcc(WOLFSSH_AGENT_KEY_ECDSA* rawKey, int curveId,
|
||||
const byte* digest, word32 digestSz,
|
||||
byte* sig, word32* sigSz, WC_RNG* rng)
|
||||
{
|
||||
ecc_key key;
|
||||
int ret;
|
||||
|
||||
ret = wc_ecc_import_private_key_ex(rawKey->d, rawKey->dSz,
|
||||
rawKey->q, rawKey->qSz, &key, curveId);
|
||||
|
||||
if (ret == 0) {
|
||||
ret = wc_ecc_sign_hash(digest, wc_HashGetDigestSize(hashType),
|
||||
sig, (word32*)&sigSz, &agent->rng, &ecc);
|
||||
ret = wc_ecc_sign_hash(digest, digestSz, sig, sigSz, rng, &key);
|
||||
}
|
||||
if (ret != 0) {
|
||||
WLOG(WS_LOG_DEBUG, "SendKexDhReply: Bad ECDSA Sign");
|
||||
|
@ -743,12 +734,12 @@ static int PostSignRequest(WOLFSSH_AGENT_CTX* agent,
|
|||
byte sPad;
|
||||
int idx;
|
||||
|
||||
ret = wc_ecc_sig_to_rs(sig, sigSz, r, &rSz, s, &sSz);
|
||||
ret = wc_ecc_sig_to_rs(sig, *sigSz, r, &rSz, s, &sSz);
|
||||
if (ret == 0) {
|
||||
idx = 0;
|
||||
rPad = (r[0] & 0x80) ? 1 : 0;
|
||||
sPad = (s[0] & 0x80) ? 1 : 0;
|
||||
sigSz = (LENGTH_SZ * 2) + rSz + rPad + sSz + sPad;
|
||||
*sigSz = (LENGTH_SZ * 2) + rSz + rPad + sSz + sPad;
|
||||
|
||||
c32toa(rSz + rPad, sig + idx);
|
||||
idx += LENGTH_SZ;
|
||||
|
@ -764,13 +755,107 @@ static int PostSignRequest(WOLFSSH_AGENT_CTX* agent,
|
|||
}
|
||||
}
|
||||
|
||||
wc_ecc_free(&ecc);
|
||||
wc_ecc_free(&key);
|
||||
if (ret != 0)
|
||||
ret = WS_ECC_E;
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int PostSignRequest(WOLFSSH_AGENT_CTX* agent,
|
||||
byte* keyBlob, word32 keyBlobSz, byte* data, word32 dataSz,
|
||||
word32 flags)
|
||||
{
|
||||
WOLFSSH_AGENT_ID* id = NULL;
|
||||
int ret = WS_SUCCESS;
|
||||
byte sig[256];
|
||||
byte digest[WC_MAX_DIGEST_SIZE];
|
||||
word32 sigSz = sizeof(sig);
|
||||
word32 digestSz = sizeof(digest);
|
||||
enum wc_HashType hashType;
|
||||
int curveId = 0, signRsa = 0, signEcc = 0;
|
||||
|
||||
WLOG_ENTER();
|
||||
|
||||
(void)flags;
|
||||
(void)curveId;
|
||||
|
||||
if (agent == NULL || keyBlob == NULL || keyBlobSz == 0)
|
||||
ret = WS_BAD_ARGUMENT;
|
||||
|
||||
if (ret == WS_SUCCESS) {
|
||||
if ((id = FindKeyId(agent->idList, keyBlob, keyBlobSz)) == NULL) {
|
||||
WLOG(WS_LOG_AGENT, "Sign: Key not found.");
|
||||
ret = WS_AGENT_NO_KEY_E;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == WS_SUCCESS) {
|
||||
switch (id->keyType) {
|
||||
#if !defined(WOLFSSH_NO_SSH_RSA_SHA2_256) || \
|
||||
!defined(WOLFSSH_NO_SSH_RSA_SHA2_512)
|
||||
case ID_SSH_RSA:
|
||||
signRsa = 1;
|
||||
#ifndef WOLFSSH_NO_SSH_RSA_SHA2_256
|
||||
if (flags & AGENT_SIGN_RSA_SHA2_256)
|
||||
hashType = WC_HASH_TYPE_SHA256;
|
||||
else
|
||||
#endif
|
||||
#ifndef WOLFSSH_NO_SSH_RSA_SHA2_512
|
||||
if (flags & AGENT_SIGN_RSA_SHA2_512)
|
||||
hashType = WC_HASH_TYPE_SHA512;
|
||||
else
|
||||
#endif
|
||||
ret = WS_INVALID_ALGO_ID;
|
||||
break;
|
||||
#endif
|
||||
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256
|
||||
case ID_ECDSA_SHA2_NISTP256:
|
||||
hashType = WC_HASH_TYPE_SHA256;
|
||||
curveId = ECC_SECP256R1;
|
||||
signEcc = 1;
|
||||
break;
|
||||
#endif
|
||||
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP384
|
||||
case ID_ECDSA_SHA2_NISTP384:
|
||||
hashType = WC_HASH_TYPE_SHA384;
|
||||
curveId = ECC_SECP384R1;
|
||||
signEcc = 1;
|
||||
break;
|
||||
#endif
|
||||
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP512
|
||||
case ID_ECDSA_SHA2_NISTP521:
|
||||
hashType = WC_HASH_TYPE_SHA512;
|
||||
curveId = ECC_SECP521R1;
|
||||
signEcc = 1;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
ret = WS_INVALID_ALGO_ID;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == WS_SUCCESS) {
|
||||
ret = wc_Hash(hashType, data, dataSz, digest, digestSz);
|
||||
if (ret != 0)
|
||||
ret = WS_CRYPTO_FAILED;
|
||||
}
|
||||
|
||||
if (ret == WS_SUCCESS) {
|
||||
#if !defined(WOLFSSH_NO_SSH_RSA_SHA2_256) || \
|
||||
!defined(WOLFSSH_NO_SSH_RSA_SHA2_512)
|
||||
if (signRsa)
|
||||
ret = SignHashRsa(&id->key.rsa, hashType,
|
||||
digest, digestSz, sig, &sigSz, &agent->rng, agent->heap);
|
||||
#endif
|
||||
#if !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP256) || \
|
||||
!defined(WOLFSSH_NO_ECDSA_SHA2_NISTP384) || \
|
||||
!defined(WOLFSSH_NO_ECDSA_SHA2_NISTP512)
|
||||
if (signEcc)
|
||||
ret = SignHashEcc(&id->key.ecdsa, curveId, digest, digestSz,
|
||||
sig, &sigSz, &agent->rng);
|
||||
#endif
|
||||
|
||||
if (ret == WS_SUCCESS)
|
||||
ret = SendSignResponse(agent, sig, sigSz);
|
||||
|
@ -1708,9 +1793,15 @@ int wolfSSH_AGENT_SignRequest(WOLFSSH* ssh,
|
|||
rxBuf, sizeof(rxBuf), ssh->agentCbCtx);
|
||||
if (rxSz > 0) {
|
||||
ret = DoMessage(ssh->agent, rxBuf, rxSz, &idx);
|
||||
if (ssh->agent->requestFailure) {
|
||||
ssh->agent->requestFailure = 0;
|
||||
ret = WS_AGENT_NO_KEY_E;
|
||||
}
|
||||
else {
|
||||
WMEMCPY(sig, ssh->agent->msg, ssh->agent->msgSz);
|
||||
*sigSz = ssh->agent->msgSz;
|
||||
}
|
||||
}
|
||||
else ret = WS_AGENT_NO_KEY_E;
|
||||
}
|
||||
|
||||
|
|
|
@ -2721,6 +2721,7 @@ static int DoKexDhInit(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
|
|||
&& ssh->handshake->kexIdGuess != ssh->handshake->kexId) {
|
||||
|
||||
/* skip this message. */
|
||||
WLOG(WS_LOG_DEBUG, "Skipping the client's KEX init function.");
|
||||
ssh->handshake->kexPacketFollows = 0;
|
||||
*idx += len;
|
||||
return WS_SUCCESS;
|
||||
|
@ -6936,7 +6937,8 @@ int SendKexDhReply(WOLFSSH* ssh)
|
|||
ret = WS_CRYPTO_FAILED;
|
||||
}
|
||||
else {
|
||||
WLOG(WS_LOG_INFO, "Signing hash with RSA.");
|
||||
WLOG(WS_LOG_INFO, "Signing hash with %s.",
|
||||
IdToName(ssh->handshake->pubKeyId));
|
||||
sigSz = wc_RsaSSL_Sign(encSig, encSigSz, sig, sizeof(sig),
|
||||
&sigKeyBlock.sk.rsa.key, ssh->rng);
|
||||
if (sigSz <= 0) {
|
||||
|
@ -6948,7 +6950,8 @@ int SendKexDhReply(WOLFSSH* ssh)
|
|||
}
|
||||
else {
|
||||
#ifndef WOLFSSH_NO_ECDSA
|
||||
WLOG(WS_LOG_INFO, "Signing hash with ECDSA.");
|
||||
WLOG(WS_LOG_INFO, "Signing hash with %s.",
|
||||
IdToName(ssh->handshake->pubKeyId));
|
||||
sigSz = sizeof(sig);
|
||||
ret = wc_ecc_sign_hash(digest, wc_HashGetDigestSize(sigHashId),
|
||||
sig, &sigSz,
|
||||
|
@ -8360,8 +8363,9 @@ int SendUserAuthRequest(WOLFSSH* ssh, byte authId, int addSig)
|
|||
|
||||
if (authId == ID_USERAUTH_PASSWORD)
|
||||
ret = PrepareUserAuthRequestPassword(ssh, &payloadSz, &authData);
|
||||
else if (authId == ID_USERAUTH_PUBLICKEY) {
|
||||
else if (authId == ID_USERAUTH_PUBLICKEY && !ssh->userAuthPkDone) {
|
||||
authData.sf.publicKey.hasSignature = 1;
|
||||
ssh->userAuthPkDone = 1;
|
||||
ret = PrepareUserAuthRequestPublicKey(ssh, &payloadSz, &authData,
|
||||
&keySig);
|
||||
}
|
||||
|
|
|
@ -35,7 +35,6 @@
|
|||
#include <wolfssl/wolfcrypt/dh.h>
|
||||
#include <wolfssl/wolfcrypt/ecc.h>
|
||||
#include <wolfssl/wolfcrypt/rsa.h>
|
||||
#include <wolfssl/wolfcrypt/sha256.h>
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
@ -160,6 +160,15 @@ extern "C" {
|
|||
#undef WOLFSSH_NO_SSH_RSA_SHA1
|
||||
#define WOLFSSH_NO_SSH_RSA_SHA1
|
||||
#endif
|
||||
#if defined(WOLFSSH_NO_RSA) || defined(NO_SHA256)
|
||||
#undef WOLFSSH_NO_SSH_RSA_SHA2_256
|
||||
#define WOLFSSH_NO_SSH_RSA_SHA2_256
|
||||
#endif
|
||||
#if defined(WOLFSSH_NO_RSA) || !defined(WOLFSSL_SHA512)
|
||||
#undef WOLFSSH_NO_SSH_RSA_SHA2_512
|
||||
#define WOLFSSH_NO_SSH_RSA_SHA2_512
|
||||
#endif
|
||||
|
||||
#if defined(WOLFSSH_NO_ECDSA) || \
|
||||
defined(NO_SHA256) || defined(NO_ECC256)
|
||||
#undef WOLFSSH_NO_ECDSA_SHA2_NISTP256
|
||||
|
@ -610,6 +619,7 @@ struct WOLFSSH {
|
|||
word32 peerProtoIdSz;
|
||||
void* publicKeyCheckCtx;
|
||||
byte sendTerminalRequest;
|
||||
byte userAuthPkDone;
|
||||
|
||||
#ifdef USE_WINDOWS_API
|
||||
word32 defaultAttr; /* default windows attributes */
|
||||
|
|
Loading…
Reference in New Issue