SSH-AGENT

1. Added option to client to load a public key.
2. Added function ReadKey to load a key from a buffer or from a file
   and store it. Utility for the client.
pull/269/head
John Safranek 2020-07-17 14:51:45 -07:00
parent 636cd81d32
commit 365d1fd8ea
No known key found for this signature in database
GPG Key ID: 8CE817DE0D3CCB4A
5 changed files with 302 additions and 50 deletions

View File

@ -26,7 +26,6 @@
#include <wolfssh/agent.h>
#endif
#include <wolfssl/wolfcrypt/ecc.h>
#include <wolfssl/wolfcrypt/coding.h>
#include "examples/client/client.h"
#if !defined(USE_WINDOWS_API) && !defined(MICROCHIP_PIC32)
#include <termios.h>
@ -158,6 +157,8 @@ static void ShowUsage(void)
printf(" -u <username> username to authenticate as (REQUIRED)\n");
printf(" -P <password> password for username, prompted if omitted\n");
printf(" -e use sample ecc key for user\n");
printf(" -i <filename> filename for the user's private key\n");
printf(" -j <filename> filename for the user's public key\n");
printf(" -x exit after successful connection without doing\n"
" read/write\n");
printf(" -N use non-blocking sockets\n");
@ -174,20 +175,24 @@ static void ShowUsage(void)
static byte userPassword[256];
static byte userPublicKeyType[32];
static byte userPublicKey[512];
static word32 userPublicKeySz;
static const byte* userPrivateKey;
static word32 userPrivateKeySz;
static const byte* userPublicKeyType = NULL;
static byte* userPrivateKey = NULL; /* Will be allocated by Read Key. */
static const byte* userPrivateKeyType = NULL;
static word32 userPublicKeySz = 0;
static word32 userPublicKeyTypeSz = 0;
static word32 userPrivateKeySz = 0;
static word32 userPrivateKeyTypeSz = 0;
static byte isPrivate = 0;
static const char hanselPublicRsa[] =
"AAAAB3NzaC1yc2EAAAADAQABAAABAQC9P3ZFowOsONXHD5MwWiCciXytBRZGho"
static const char* hanselPublicRsa =
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC9P3ZFowOsONXHD5MwWiCciXytBRZGho"
"MNiisWSgUs5HdHcACuHYPi2W6Z1PBFmBWT9odOrGRjoZXJfDDoPi+j8SSfDGsc/hsCmc3G"
"p2yEhUZUEkDhtOXyqjns1ickC9Gh4u80aSVtwHRnJZh9xPhSq5tLOhId4eP61s+a5pwjTj"
"nEhBaIPUJO2C/M0pFnnbZxKgJlX7t1Doy7h5eXxviymOIvaCZKU+x5OopfzM/wFkey0EPW"
"NmzI5y/+pzU5afsdeEWdiQDIQc80H6Pz8fsoFPvYSG+s4/wz0duu7yeeV1Ypoho65Zr+pE"
"nIf7dO0B8EblgWt+ud+JI8wrAhfE4x";
"nIf7dO0B8EblgWt+ud+JI8wrAhfE4x hansel";
static const byte hanselPrivateRsa[] = {
0x30, 0x82, 0x04, 0xa3, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, 0x01, 0x00,
@ -295,9 +300,10 @@ static const byte hanselPrivateRsa[] = {
static const unsigned int hanselPrivateRsaSz = 1191;
static const char hanselPublicEcc[] =
"AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNkI5JTP6D0lF42tbx"
"X19cE87hztUS6FSDoGvPfiU0CgeNSbI+aFdKIzTP5CQEJSvm25qUzgDtH7oyaQROUnNvk=";
const char* hanselPublicEcc =
"ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAA"
"BBBNkI5JTP6D0lF42tbxX19cE87hztUS6FSDoGvPfiU0CgeNSbI+aFdKIzTP5CQEJSvm25"
"qUzgDtH7oyaQROUnNvk= hansel";
static const byte hanselPrivateEcc[] = {
0x30, 0x77, 0x02, 0x01, 0x01, 0x04, 0x20, 0x03, 0x6e, 0x17, 0xd3, 0xb9,
@ -336,13 +342,16 @@ static int wsUserAuth(byte authType,
#endif
/* We know hansel has a key, wait for request of public key */
if (authType == WOLFSSH_USERAUTH_PUBLICKEY &&
if ((authData->type & WOLFSSH_USERAUTH_PUBLICKEY) &&
authData->username != NULL &&
authData->usernameSz > 0 &&
XSTRNCMP((char*)authData->username, "hansel",
authData->usernameSz) == 0) {
(XSTRNCMP((char*)authData->username, "hansel",
authData->usernameSz) == 0 ||
XSTRNCMP((char*)authData->username, "john",
authData->usernameSz) == 0)) {
if (authType == WOLFSSH_USERAUTH_PASSWORD) {
printf("rejecting password type with hansel in favor of pub key\n");
printf("rejecting password type with %s in favor of pub key\n",
(char*)authData->username);
return WOLFSSH_USERAUTH_FAILURE;
}
}
@ -350,21 +359,14 @@ static int wsUserAuth(byte authType,
if (authType == WOLFSSH_USERAUTH_PUBLICKEY) {
WS_UserAuthData_PublicKey* pk = &authData->sf.publicKey;
/* we only have hansel's sample key loaded */
if (authData->username != NULL && authData->usernameSz > 0 &&
XSTRNCMP((char*)authData->username, "hansel",
authData->usernameSz) == 0) {
pk->publicKeyType = userPublicKeyType;
pk->publicKeyTypeSz = (word32)WSTRLEN((char*)userPublicKeyType);
pk->publicKey = userPublicKey;
pk->publicKeySz = userPublicKeySz;
pk->privateKey = userPrivateKey;
pk->privateKeySz = userPrivateKeySz;
ret = WOLFSSH_USERAUTH_SUCCESS;
}
else {
ret = WOLFSSH_USERAUTH_INVALID_USER;
}
pk->publicKeyType = userPublicKeyType;
pk->publicKeyTypeSz = userPublicKeyTypeSz;
pk->publicKey = userPublicKey;
pk->publicKeySz = userPublicKeySz;
pk->privateKey = userPrivateKey;
pk->privateKeySz = userPrivateKeySz;
ret = WOLFSSH_USERAUTH_SUCCESS;
}
else if (authType == WOLFSSH_USERAUTH_PASSWORD) {
const char* defaultPassword = (const char*)ctx;
@ -383,7 +385,7 @@ static int wsUserAuth(byte authType,
ret = WOLFSSH_USERAUTH_FAILURE;
}
else {
char* c = strpbrk((char*)userPassword, "\r\n");;
char* c = strpbrk((char*)userPassword, "\r\n");
if (c != NULL)
*c = '\0';
}
@ -752,6 +754,8 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args)
const char* username = NULL;
const char* password = NULL;
const char* cmd = NULL;
const char* privKeyName = NULL;
const char* pubKeyName = NULL;
byte imExit = 0;
byte nonBlock = 0;
byte keepOpen = 0;
@ -766,7 +770,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:p:tu:xzNP:R")) != -1) {
while ((ch = mygetopt(argc, argv, "?c:eh:i:j:p:tu:xzNP:R")) != -1) {
switch (ch) {
case 'h':
host = myoptarg;
@ -799,6 +803,14 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args)
password = myoptarg;
break;
case 'i':
privKeyName = myoptarg;
break;
case 'j':
pubKeyName = myoptarg;
break;
case 'x':
/* exit after successful connection without read/write */
imExit = 1;
@ -843,27 +855,62 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args)
err_sys("Threading needed for terminal session\n");
#endif
if (userEcc) {
userPublicKeySz = (word32)sizeof(userPublicKey);
Base64_Decode((byte*)hanselPublicEcc,
(word32)WSTRLEN(hanselPublicEcc),
(byte*)userPublicKey, &userPublicKeySz);
if (pubKeyName == NULL && privKeyName != NULL) {
err_sys("If setting priv key, need pub key.");
}
WSTRNCPY((char*)userPublicKeyType, "ecdsa-sha2-nistp256",
sizeof(userPublicKeyType));
userPrivateKey = hanselPrivateEcc;
userPrivateKeySz = hanselPrivateEccSz;
if (privKeyName == NULL) {
if (userEcc) {
ret = wolfSSH_ReadKey_buffer(hanselPrivateEcc, hanselPrivateEccSz,
WOLFSSH_FORMAT_ASN1, &userPrivateKey, &userPrivateKeySz,
&userPrivateKeyType, &userPrivateKeyTypeSz, NULL);
isPrivate = 1;
}
else {
ret = wolfSSH_ReadKey_buffer(hanselPrivateRsa, hanselPrivateRsaSz,
WOLFSSH_FORMAT_ASN1, &userPrivateKey, &userPrivateKeySz,
&userPrivateKeyType, &userPrivateKeyTypeSz, NULL);
isPrivate = 1;
}
if (ret != 0) err_sys("Couldn't load private key buffer.");
}
else {
userPublicKeySz = (word32)sizeof(userPublicKey);
Base64_Decode((byte*)hanselPublicRsa,
(word32)WSTRLEN(hanselPublicRsa),
(byte*)userPublicKey, &userPublicKeySz);
ret = wolfSSH_ReadKey_file(privKeyName,
(byte**)&userPrivateKey, &userPrivateKeySz,
(const byte**)&userPrivateKeyType, &userPrivateKeyTypeSz,
&isPrivate, NULL);
if (ret != 0) err_sys("Couldn't load private key file.");
}
WSTRNCPY((char*)userPublicKeyType, "ssh-rsa",
sizeof(userPublicKeyType));
userPrivateKey = hanselPrivateRsa;
userPrivateKeySz = hanselPrivateRsaSz;
if (pubKeyName == NULL) {
byte* p = userPublicKey;
userPublicKeySz = sizeof(userPublicKey);
if (userEcc) {
ret = wolfSSH_ReadKey_buffer((const byte*)hanselPublicEcc,
(word32)strlen(hanselPublicEcc), WOLFSSH_FORMAT_SSH,
&p, &userPublicKeySz,
&userPublicKeyType, &userPublicKeyTypeSz, NULL);
isPrivate = 1;
}
else {
ret = wolfSSH_ReadKey_buffer((const byte*)hanselPublicRsa,
(word32)strlen(hanselPublicRsa), WOLFSSH_FORMAT_SSH,
&p, &userPublicKeySz,
&userPublicKeyType, &userPublicKeyTypeSz, NULL);
isPrivate = 1;
}
if (ret != 0) err_sys("Couldn't load public key buffer.");
}
else {
byte* p = userPublicKey;
userPublicKeySz = sizeof(userPublicKey);
ret = wolfSSH_ReadKey_file(pubKeyName,
&p, &userPublicKeySz,
(const byte**)&userPublicKeyType, &userPublicKeyTypeSz,
&isPrivate, NULL);
if (ret != 0) err_sys("Couldn't load public key file.");
}
ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_CLIENT, NULL);
@ -1007,6 +1054,8 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args)
WCLOSESOCKET(sockFd);
wolfSSH_free(ssh);
wolfSSH_CTX_free(ctx);
if (userPrivateKey != NULL)
free(userPrivateKey);
if (ret != WS_SUCCESS)
err_sys("Closing stream failed. Connection could have been closed by peer");

192
src/ssh.c
View File

@ -1362,6 +1362,198 @@ char* wolfSSH_GetUsername(WOLFSSH* ssh)
}
#include <wolfssl/wolfcrypt/rsa.h>
#include <wolfssl/wolfcrypt/asn_public.h>
#include <wolfssl/wolfcrypt/coding.h>
#define WSTRDUP(x,y) strdup((x))
#define WSTRSEP(x,y) strsep((x),(y))
int wolfSSH_ReadKey_buffer(const byte* in, word32 inSz, int format,
byte** out, word32* outSz, const byte** outType, word32* outTypeSz,
void* heap)
{
int ret = WS_SUCCESS;
(void)heap;
if (in == NULL || inSz == 0 || out == NULL || outSz == NULL ||
outType == NULL || outTypeSz == NULL)
return WS_BAD_ARGUMENT;
if (format == WOLFSSH_FORMAT_SSH) {
char* dup;
char* c;
char* type;
char* key;
/*
SSH format is:
type AAAABASE64ENCODEDKEYDATA comment
*/
c = dup = WSTRDUP((const char*)in, heap);
type = WSTRSEP(&c, " \n");
key = WSTRSEP(&c, " \n");
if (type != NULL && key != NULL) {
const char* name;
word32 typeSz;
typeSz = (word32)WSTRLEN(type);
name = IdToName(ID_SSH_RSA);
if (WSTRNCMP(type, name, typeSz) == 0) {
*outType = (const byte*)name;
}
else {
name = IdToName(ID_ECDSA_SHA2_NISTP256);
if (WSTRNCMP(type, name, typeSz) == 0) {
*outType = (const byte*)name;
}
else {
name = IdToName(ID_UNKNOWN);
*outType = (const byte*)name;
typeSz = (word32)WSTRLEN(name);
}
}
*outTypeSz = typeSz;
ret = Base64_Decode((byte*)key, (word32)WSTRLEN(key), *out, outSz);
}
if (ret != 0)
ret = WS_ERROR;
WFREE(dup, heap, DYNTYPE_STRING);
}
else if (format == WOLFSSH_FORMAT_ASN1) {
byte* newKey;
union {
RsaKey rsa;
ecc_key ecc;
} testKey;
word32 scratch = 0;
if (*out == NULL) {
newKey = (byte*)WMALLOC(inSz, heap, DYNTYPE_PRIVKEY);
if (newKey == NULL)
return WS_MEMORY_E;
*out = newKey;
}
else {
if (*outSz < inSz)
return WS_ERROR;
newKey = *out;
}
*outSz = inSz;
WMEMCPY(newKey, in, inSz);
/* TODO: This is copied and modified from a function in src/internal.c.
This and that code should be combined into a single function. */
if (wc_InitRsaKey(&testKey.rsa, heap) < 0)
return WS_RSA_E;
ret = wc_RsaPrivateKeyDecode(in, &scratch, &testKey.rsa, inSz);
wc_FreeRsaKey(&testKey.rsa);
if (ret == 0) {
*outType = (const byte*)IdToName(ID_SSH_RSA);
*outTypeSz = (word32)WSTRLEN((const char*)*outType);
}
else {
/* Couldn't decode as RSA testKey. Try decoding as ECC testKey. */
scratch = 0;
if (wc_ecc_init_ex(&testKey.ecc, heap, INVALID_DEVID) != 0)
return WS_ECC_E;
ret = wc_EccPrivateKeyDecode(in, &scratch,
&testKey.ecc, inSz);
wc_ecc_free(&testKey.ecc);
if (ret == 0) {
*outType = (const byte*)IdToName(ID_ECDH_SHA2_NISTP256);
*outTypeSz = (word32)WSTRLEN((const char*)*outType);
}
else
return WS_BAD_FILE_E;
}
}
else
ret = WS_ERROR;
return ret;
}
#ifndef NO_FILESYSTEM
int wolfSSH_ReadKey_file(const char* name,
byte** out, word32* outSz, const byte** outType, word32* outTypeSz,
byte* isPrivate, void* heap)
{
WFILE* file;
byte* in;
word32 inSz;
int format;
int ret;
if (name == NULL)
return WS_BAD_FILE_E;
if (out == NULL || outSz == NULL || outType == NULL || outTypeSz == NULL ||
isPrivate == NULL)
return WS_BAD_ARGUMENT;
ret = WFOPEN(&file, name, "rb");
if (file == WBADFILE) return WS_BAD_FILE_E;
if (WFSEEK(file, 0, WSEEK_END) != 0) {
WFCLOSE(file);
return WS_BAD_FILE_E;
}
inSz = (word32)WFTELL(file);
WREWIND(file);
if (inSz > WOLFSSH_MAX_FILE_SIZE || inSz == 0) {
WFCLOSE(file);
return WS_BAD_FILE_E;
}
in = (byte*)WMALLOC(inSz + 1, heap, DYNTYPE_FILE);
if (in == NULL) {
WFCLOSE(file);
return WS_MEMORY_E;
}
ret = (int)XFREAD(in, 1, inSz, file);
if (ret <= 0 || (word32)ret != inSz) {
ret = WS_BAD_FILE_E;
}
else {
if (WSTRNSTR((const char*)in,
"ssh-rsa", inSz) == (const char*)in ||
WSTRNSTR((const char*)in,
"ecdsa-sha2-nistp", inSz) == (const char*)in) {
*isPrivate = 0;
format = WOLFSSH_FORMAT_SSH;
in[inSz] = 0;
}
else {
*isPrivate = 1;
format = WOLFSSH_FORMAT_ASN1;
}
ret = wolfSSH_ReadKey_buffer(in, inSz, format,
out, outSz, outType, outTypeSz, heap);
}
WFCLOSE(file);
WFREE(in, heap, DYNTYPE_FILE);
return ret;
}
#endif
int wolfSSH_CTX_SetBanner(WOLFSSH_CTX* ctx,
const char* newBanner)
{

View File

@ -163,6 +163,9 @@ enum {
/* This is based on the 8192-bit DH key that is the max size. */
#define MAX_KEX_KEY_SZ (WOLFSSH_DEFAULT_GEXDH_MAX / 8)
#endif
#ifndef WOLFSSH_MAX_FILE_SIZE
#define WOLFSSH_MAX_FILE_SIZE (1024ul * 1024ul * 4)
#endif
WOLFSSH_LOCAL byte NameToId(const char*, word32);
WOLFSSH_LOCAL const char* IdToName(byte);
@ -744,6 +747,7 @@ enum WS_DynamicTypes {
DYNTYPE_AGENT_ID,
DYNTYPE_AGENT_KEY,
DYNTYPE_AGENT_BUFFER,
DYNTYPE_FILE,
DYNTYPE_TEMP
};

View File

@ -222,6 +222,7 @@ extern "C" {
#define WFTELL(s) ftell((s))
#define WREWIND(s) rewind((s))
#define WSEEK_END SEEK_END
#define WBADFILE NULL
#ifdef WOLFSSL_VXWORKS
#define WUTIMES(f,t) (WS_SUCCESS)
#else

View File

@ -80,6 +80,11 @@ WOLFSSH_API void wolfSSH_SetHighwaterCb(WOLFSSH_CTX*, word32,
WOLFSSH_API void wolfSSH_SetHighwaterCtx(WOLFSSH*, void*);
WOLFSSH_API void* wolfSSH_GetHighwaterCtx(WOLFSSH*);
WOLFSSH_API int wolfSSH_ReadKey_buffer(const byte*, word32, int,
byte**, word32*, const byte**, word32*, void*);
WOLFSSH_API int wolfSSH_ReadKey_file(const char*,
byte**, word32*, const byte**, word32*, byte*, void*);
#define WS_CHANNEL_ID_SELF 0
#define WS_CHANNEL_ID_PEER 1
@ -237,7 +242,8 @@ enum WS_EndpointTypes {
enum WS_FormatTypes {
WOLFSSH_FORMAT_ASN1,
WOLFSSH_FORMAT_PEM,
WOLFSSH_FORMAT_RAW
WOLFSSH_FORMAT_RAW,
WOLFSSH_FORMAT_SSH,
};