X.509 Certificate Support

1. Quality of Life improvements to echoserver: command line user config
2. adding policy checking
pull/415/head
John Safranek 2022-04-26 15:40:19 -07:00 committed by JacobBarthelmeh
parent a8fa38d7f8
commit 13e525478c
4 changed files with 345 additions and 57 deletions

View File

@ -34,6 +34,7 @@
#include <wolfssl/wolfcrypt/hash.h>
#include <wolfssl/wolfcrypt/coding.h>
#include <wolfssl/wolfcrypt/wc_port.h>
#include <wolfssl/wolfcrypt/asn_public.h>
#include <wolfssh/ssh.h>
#include <wolfssh/internal.h>
#include <wolfssh/wolfsftp.h>
@ -1445,6 +1446,39 @@ static int load_key(byte isEcc, byte* buf, word32 bufSz)
}
typedef struct StrList {
const char* str;
struct StrList* next;
} StrList;
static StrList* StrListAdd(StrList* list, const char* str)
{
if (str != NULL) {
StrList* newStr = (StrList*)WMALLOC(sizeof *newStr, NULL, 0);
if (newStr != NULL) {
newStr->str = str;
newStr->next = list;
list = newStr;
}
}
return list;
}
static void StrListFree(StrList* list)
{
StrList* curStr;
while (list != NULL) {
curStr = list;
list = list->next;
WFREE(curStr, NULL, 0);
}
}
/* Map user names to passwords */
/* Use arrays for username and p. The password or public key can
* be hashed and the hash stored here. Then I won't need the type. */
@ -1508,6 +1542,8 @@ static const char samplePasswordBuffer[] =
"jack:fetchapail\n";
#if 0
#ifndef NO_FILESYSTEM
#ifndef WOLFSSH_NO_ECC
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256
static const char samplePublicKeyEccBuffer[] =
@ -1547,6 +1583,11 @@ static const char samplePublicKeyRsaBuffer[] =
"biE57dK6BrH5iZwVLTQKux31uCJLPhiktI3iLbdlGZEctJkTasfVSsUizwVIyRjhVKmbdI"
"RGwkU38D043AR1h0mUoGCPIKuqcFMf gretel\n";
#endif
#endif /* NO_FILESYSTEM */
#endif
#ifdef WOLFSSH_ALLOW_USERAUTH_NONE
static const char sampleNoneBuffer[] =
"holmes\n"
@ -1587,6 +1628,7 @@ static int LoadNoneBuffer(byte* buf, word32 bufSz, PwMapList* list)
return 0;
}
#endif /* WOLFSSH_ALLOW_USERAUTH_NONE */
static int LoadPasswordBuffer(byte* buf, word32 bufSz, PwMapList* list)
{
@ -1714,24 +1756,102 @@ static int LoadPublicKeyBuffer(byte* buf, word32 bufSz, PwMapList* list)
}
#ifdef WOLFSSH_CERTS
static int LoadCertBuffer(byte* buf, word32 bufSz, PwMapList* list)
static int LoadPasswdList(StrList* strList, PwMapList* mapList)
{
if (list == NULL)
return -1;
char names[256];
char* passwd;
int count = 0;
if (buf == NULL || bufSz == 0)
return 0;
while (strList) {
WSTRNCPY(names, strList->str, sizeof names);
passwd = WSTRCHR(names, ':');
if (passwd != NULL) {
*passwd = 0;
passwd++;
if (PwMapNew(list,
WOLFSSH_USERAUTH_PUBLICKEY,
(const byte*)"orange", 6, buf, bufSz) == NULL ) {
return -1;
PwMapNew(mapList, WOLFSSH_USERAUTH_PASSWORD,
(byte*)names, (word32)WSTRLEN(names),
(byte*)passwd, (word32)WSTRLEN(passwd));
}
else {
fprintf(stderr, "Ignoring password: %s\n", names);
}
strList = strList->next;
count++;
}
return 0;
return count;
}
static int LoadPubKeyList(StrList* strList, int format, PwMapList* mapList)
{
char names[256];
char* fileName;
byte* buf;
word32 bufSz;
int count = 0;
while (strList) {
buf = NULL;
bufSz = 0;
WSTRNCPY(names, strList->str, sizeof names);
fileName = WSTRCHR(names, ':');
if (fileName != NULL) {
*fileName = 0;
fileName++;
load_file(fileName, NULL, &bufSz);
buf = (byte*)WMALLOC(bufSz, NULL, 0);
bufSz = load_file(fileName, buf, &bufSz);
if (bufSz > 0) {
if (format == WOLFSSH_FORMAT_SSH) {
const byte* type = NULL;
byte* out = NULL;
word32 typeSz, outSz;
wolfSSH_ReadKey_buffer(buf, bufSz, WOLFSSH_FORMAT_SSH,
&out, &outSz, &type, &typeSz, NULL);
(void)type;
(void)typeSz;
WFREE(buf, NULL, 0);
buf = out;
bufSz = outSz;
}
else if (format == WOLFSSH_FORMAT_PEM) {
byte* out = NULL;
word32 outSz;
out = (byte*)WMALLOC(bufSz, NULL, 0);
outSz = wc_CertPemToDer(buf, bufSz, out, bufSz, CERT_TYPE);
WFREE(buf, NULL, 0);
buf = out;
bufSz = outSz;
}
PwMapNew(mapList, WOLFSSH_USERAUTH_PUBLICKEY,
(byte*)names, (word32)WSTRLEN(names), buf, bufSz);
}
else {
fprintf(stderr, "File error: %s\n", names);
}
}
else {
fprintf(stderr, "Ignoring key: %s\n", names);
}
WFREE(buf, NULL, 0);
strList = strList->next;
count++;
}
return count;
}
#endif
static int wsUserAuth(byte authType,
@ -1847,7 +1967,16 @@ static void ShowUsage(void)
#ifdef WOLFSSH_SFTP
printf(" -d <string> set the home directory for SFTP connections\n");
#endif
printf(" -j <file> load in a public key to accept from peer\n");
printf(" -j <file> load in a SSH public key to accept from peer\n"
" (user assumed in comment)\n");
printf(" -I <name>:<file>\n"
" load in a SSH public key to accept from peer\n");
printf(" -J <name>:<file>\n"
" load in an X.509 PEM cert to accept from peer\n");
printf(" -K <name>:<file>\n"
" load in an X.509 DER cert to accept from peer\n");
printf(" -P <name>:<password>\n"
" add password to accept from peer\n");
#ifdef WOLFSSH_CERTS
printf(" -a <file> load in a root CA certificate file\n");
#endif
@ -1875,6 +2004,10 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args)
func_args* serverArgs = (func_args*)args;
WOLFSSH_CTX* ctx = NULL;
PwMapList pwMapList;
StrList* sshPubKeyList = NULL;
StrList* pemPubKeyList = NULL;
StrList* derPubKeyList = NULL;
StrList* passwdList = NULL;
WS_SOCKET_T listenFd = 0;
word32 defaultHighwater = EXAMPLE_HIGHWATER_MARK;
word32 threadCount = 0;
@ -1897,7 +2030,7 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args)
serverArgs->return_code = 0;
if (argc > 0) {
while ((ch = mygetopt(argc, argv, "?1a:d:efEp:R:Nj:")) != -1) {
while ((ch = mygetopt(argc, argv, "?1a:d:efEp:R:Ni:j:I:J:K:P:")) != -1) {
switch (ch) {
case '?' :
ShowUsage();
@ -1955,6 +2088,22 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args)
userPubKey = myoptarg;
break;
case 'I':
sshPubKeyList = StrListAdd(sshPubKeyList, myoptarg);
break;
case 'J':
pemPubKeyList = StrListAdd(pemPubKeyList, myoptarg);
break;
case 'K':
derPubKeyList = StrListAdd(derPubKeyList, myoptarg);
break;
case 'P':
passwdList = StrListAdd(passwdList, myoptarg);
break;
default:
ShowUsage();
WEXIT(MY_EX_USAGE);
@ -1980,6 +2129,7 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args)
userEcc = 0;
peerEcc = 0;
#endif
(void)userEcc;
if (wolfSSH_Init() != WS_SUCCESS) {
fprintf(stderr, "Couldn't initialize wolfSSH.\n");
@ -2005,8 +2155,29 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args)
wolfSSH_CTX_SetFwdCb(ctx, wolfSSH_FwdDefaultActions, NULL);
#endif
if (sshPubKeyList) {
LoadPubKeyList(sshPubKeyList, WOLFSSH_FORMAT_SSH, &pwMapList);
StrListFree(sshPubKeyList);
sshPubKeyList = NULL;
}
if (pemPubKeyList) {
LoadPubKeyList(pemPubKeyList, WOLFSSH_FORMAT_PEM, &pwMapList);
StrListFree(pemPubKeyList);
pemPubKeyList = NULL;
}
if (derPubKeyList) {
LoadPubKeyList(derPubKeyList, WOLFSSH_FORMAT_ASN1, &pwMapList);
StrListFree(derPubKeyList);
derPubKeyList = NULL;
}
if (passwdList) {
LoadPasswdList(passwdList, &pwMapList);
StrListFree(passwdList);
passwdList = NULL;
}
{
const char* bufName = NULL;
//const char* bufName = NULL;
#ifndef WOLFSSH_SMALL_STACK
byte buf[EXAMPLE_KEYLOAD_BUFFER_SZ];
#endif
@ -2055,30 +2226,10 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args)
}
load_file(userPubKey, userBuf, &userBufSz);
LoadPublicKeyBuffer(userBuf, userBufSz, &pwMapList);
WFREE(userBuf, NULL, 0);
}
#ifdef WOLFSSH_CERTS
{
byte* certBuf = NULL;
word32 certBufSz = 0;
const char* filename = "../ca/orange-cert.der";
load_file(filename, NULL, &certBufSz);
if (certBufSz == 0) {
fprintf(stderr,
"Couldn't find size of file %s.\n", filename);
WEXIT(EXIT_FAILURE);
}
certBuf = (byte*)WMALLOC(certBufSz, NULL, 0);
if (certBuf == NULL) {
fprintf(stderr, "WMALLOC failed\n");
WEXIT(EXIT_FAILURE);
}
load_file(filename, certBuf, &certBufSz);
LoadCertBuffer(certBuf, certBufSz, &pwMapList);
}
if (caCert) {
byte* certBuf = NULL;
word32 certBufSz = 0;
@ -2113,6 +2264,7 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args)
keyLoadBuf[bufSz] = 0;
LoadPasswordBuffer(keyLoadBuf, bufSz, &pwMapList);
#if 0
if (userEcc) {
#ifndef WOLFSSH_NO_ECC
bufName = samplePublicKeyEccBuffer;
@ -2130,10 +2282,13 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args)
LoadPublicKeyBuffer(keyLoadBuf, bufSz, &pwMapList);
}
bufSz = (word32)WSTRLEN(sampleNoneBuffer);
WMEMCPY(keyLoadBuf, sampleNoneBuffer, bufSz);
keyLoadBuf[bufSz] = 0;
LoadNoneBuffer(keyLoadBuf, bufSz, &pwMapList);
#ifdef WOLFSSH_ALLOW_USERAUTH_NONE
bufSz = (word32)WSTRLEN(sampleNoneBuffer);
WMEMCPY(keyLoadBuf, sampleNoneBuffer, bufSz);
keyLoadBuf[bufSz] = 0;
LoadNoneBuffer(keyLoadBuf, bufSz, &pwMapList);
#endif /* WOLFSSH_ALLOW_USERAUTH_NONE */
#endif
#ifdef WOLFSSH_SMALL_STACK
WFREE(keyLoadBuf, NULL, 0);

View File

@ -181,6 +181,13 @@ int wolfSSH_CERTMAN_LoadRootCA_buffer(WOLFSSH_CERTMAN* cm,
}
static int CheckProfile(DecodedCert* cert, int profile);
enum {
PROFILE_FPKI_WORKSHEET_6 = 6,
PROFILE_FPKI_WORKSHEET_10 = 10,
PROFILE_FPKI_WORKSHEET_16 = 16
};
int wolfSSH_CERTMAN_VerifyCert_buffer(WOLFSSH_CERTMAN* cm,
const unsigned char* cert, word32 certSz)
{
@ -226,11 +233,113 @@ int wolfSSH_CERTMAN_VerifyCert_buffer(WOLFSSH_CERTMAN* cm,
else {
WLOG(WS_LOG_CERTMAN, "ocsp lookup: other error (%d)", ret);
ret = WS_CERT_OTHER_E;
ret = WS_SUCCESS;
}
}
if (ret == WS_SUCCESS) {
DecodedCert decoded;
InitDecodedCert(&decoded, cert, certSz, cm->cm);
ret = ParseCert(&decoded, WOLFSSL_FILETYPE_ASN1, 0, cm->cm);
if (ret == 0) {
ret =
CheckProfile(&decoded, PROFILE_FPKI_WORKSHEET_6) ||
CheckProfile(&decoded, PROFILE_FPKI_WORKSHEET_10) ||
CheckProfile(&decoded, PROFILE_FPKI_WORKSHEET_16);
if (ret != 0) {
WLOG(WS_LOG_CERTMAN, "certificate didn't match profile");
ret = WS_CERT_PROFILE_E;
}
else
ret = WS_SUCCESS;
}
FreeDecodedCert(&decoded);
}
WLOG_LEAVE(ret);
return ret;
}
static int CheckProfile(DecodedCert* cert, int profile)
{
int valid = (cert != NULL);
const char* certPolicies[2] = {NULL, NULL};
byte extKeyUsage = 0, extKeyUsageSsh = 0, extKeyUsageSshAllowed = 0;
if (profile == PROFILE_FPKI_WORKSHEET_6) {
certPolicies[0] = "2.16.840.1.101.3.2.1.3.13";
extKeyUsage = EXTKEYUSE_CLIENT_AUTH;
extKeyUsageSsh = EXTKEYUSE_SSH_MSCL;
extKeyUsageSshAllowed =
EXTKEYUSE_SSH_KP_CLIENT_AUTH |
EXTKEYUSE_SSH_CLIENT_AUTH;
}
else if (profile == PROFILE_FPKI_WORKSHEET_10) {
certPolicies[0] = "2.16.840.1.101.3.2.1.3.40";
certPolicies[1] = "2.16.840.1.101.3.2.1.3.41";
extKeyUsage = EXTKEYUSE_CLIENT_AUTH;
extKeyUsageSshAllowed =
EXTKEYUSE_SSH_MSCL |
EXTKEYUSE_SSH_KP_CLIENT_AUTH |
EXTKEYUSE_SSH_CLIENT_AUTH;
}
else if (profile == PROFILE_FPKI_WORKSHEET_16) {
certPolicies[0] = "2.16.840.1.101.3.2.1.3.45";
extKeyUsage = EXTKEYUSE_CLIENT_AUTH;
extKeyUsageSsh = EXTKEYUSE_SSH_MSCL;
extKeyUsageSshAllowed =
EXTKEYUSE_SSH_KP_CLIENT_AUTH |
EXTKEYUSE_SSH_CLIENT_AUTH;
}
else {
valid = 0;
}
if (valid) {
valid = cert->extKeyUsageSet &&
cert->extKeyUsage == KEYUSE_DIGITAL_SIG &&
/*cert->extBasicConstCrit;*/ 1;
}
if (valid) {
valid = WSTRCMP(cert->countryOfCitizenship, "US") == 0;
}
if (valid) {
valid = !cert->isCA;
}
if (valid) {
valid =
((certPolicies[1] != NULL) &&
(WSTRCMP(certPolicies[1], cert->extCertPolicies[0]) == 0 ||
WSTRCMP(certPolicies[1], cert->extCertPolicies[1]) == 0)) ||
((certPolicies[0] != NULL) &&
(WSTRCMP(certPolicies[0], cert->extCertPolicies[0]) == 0 ||
WSTRCMP(certPolicies[0], cert->extCertPolicies[1]) == 0));
}
if (valid) {
valid =
/* Must include all in extKeyUsage */
((extKeyUsage == 0) ||
((cert->extExtKeyUsage & extKeyUsage) != extKeyUsage)) &&
/* Must include all in extKeyUsageSsh */
((extKeyUsageSsh == 0) ||
((cert->extExtKeyUsageSsh & extKeyUsageSsh)
!= extKeyUsageSsh)) &&
/* Must include at least one in extKeyUsageSshAllowed */
((extKeyUsageSshAllowed == 0) ||
((cert->extExtKeyUsageSsh & extKeyUsageSshAllowed) != 0));
}
return valid;
}
#endif /* WOLFSSH_CERTS */

View File

@ -399,6 +399,9 @@ const char* GetErrorString(int err)
case WS_CERT_OTHER_E:
return "other certificate error";
case WS_CERT_PROFILE_E:
return "certificate profile requirements error";
default:
return "Unknown error code";
}
@ -4837,6 +4840,7 @@ static int DoUserAuthRequestPublicKey(WOLFSSH* ssh, WS_UserAuthData* authData,
WS_UserAuthData_PublicKey* pk = NULL;
int ret = WS_SUCCESS;
int authFailure = 0;
byte pkTypeId;
WLOG(WS_LOG_DEBUG, "Entering DoUserAuthRequestPublicKey()");
@ -4859,13 +4863,25 @@ static int DoUserAuthRequestPublicKey(WOLFSSH* ssh, WS_UserAuthData* authData,
if (ret == WS_SUCCESS) {
pk->publicKeyType = buf + begin;
begin += pk->publicKeyTypeSz;
ret = GetSize(&pk->publicKeySz, buf, len, &begin);
pkTypeId = NameToId((char*)pk->publicKeyType, pk->publicKeyTypeSz);
if (pkTypeId == ID_UNKNOWN) {
WLOG(WS_LOG_DEBUG, "DUARPK: Unknown / Unsupported key type");
ret = WS_INVALID_ALGO_ID;
}
}
if (ret == WS_SUCCESS)
ret = GetSize(&pk->publicKeySz, buf, len, &begin);
if (ret == WS_SUCCESS) {
pk->publicKey = buf + begin;
begin += pk->publicKeySz;
{
#ifdef WOLFSSH_CERTS
if (pkTypeId == ID_X509V3_SSH_RSA ||
pkTypeId == ID_X509V3_ECDSA_SHA2_NISTP256 ||
pkTypeId == ID_X509V3_ECDSA_SHA2_NISTP384 ||
pkTypeId == ID_X509V3_ECDSA_SHA2_NISTP521) {
word32 l = 0, m = 0;
/* Skip the name */
@ -4877,6 +4893,7 @@ static int DoUserAuthRequestPublicKey(WOLFSSH* ssh, WS_UserAuthData* authData,
pk->publicKeySz = l;
pk->publicKey = pk->publicKey + m;
}
#endif /* WOLFSSH_CERTS */
if (pk->hasSignature) {
ret = GetSize(&pk->signatureSz, buf, len, &begin);
@ -4929,14 +4946,6 @@ static int DoUserAuthRequestPublicKey(WOLFSSH* ssh, WS_UserAuthData* authData,
byte digest[WC_MAX_DIGEST_SIZE];
word32 digestSz = 0;
enum wc_HashType hashId = WC_HASH_TYPE_SHA;
byte pkTypeId;
pkTypeId = NameToId((char*)pk->publicKeyType,
pk->publicKeyTypeSz);
if (pkTypeId == ID_UNKNOWN) {
WLOG(WS_LOG_DEBUG, "DUARPK: Unknown / Unsupported key type");
ret = WS_INVALID_ALGO_ID;
}
if (ret == WS_SUCCESS) {
hashId = HashForId(pkTypeId);
@ -4968,12 +4977,26 @@ static int DoUserAuthRequestPublicKey(WOLFSSH* ssh, WS_UserAuthData* authData,
/* The rest of the fields in the signature are already
* in the buffer. Just need to account for the sizes. */
if (ret == 0) {
ret = wc_HashUpdate(&hash, hashId, pk->dataToSign,
authData->usernameSz +
authData->serviceNameSz +
authData->authNameSz + BOOLEAN_SZ +
(pk->publicKeyTypeSz*2) + pk->publicKeySz +
(UINT32_SZ * 9));
word32 dataToSignSz;
dataToSignSz = authData->usernameSz +
authData->serviceNameSz +
authData->authNameSz + BOOLEAN_SZ +
pk->publicKeyTypeSz + pk->publicKeySz +
(UINT32_SZ * 5);
#ifdef WOLFSSH_CERTS
if (pkTypeId == ID_X509V3_SSH_RSA ||
pkTypeId == ID_X509V3_ECDSA_SHA2_NISTP256 ||
pkTypeId == ID_X509V3_ECDSA_SHA2_NISTP384 ||
pkTypeId == ID_X509V3_ECDSA_SHA2_NISTP521) {
dataToSignSz += pk->publicKeyTypeSz + (UINT32_SZ * 4);
}
#endif /* WOLFSSH_CERTS */
ret = wc_HashUpdate(&hash, hashId,
pk->dataToSign, dataToSignSz);
}
if (ret == 0) {
ret = wc_HashFinal(&hash, hashId, digest);

View File

@ -124,8 +124,9 @@ enum WS_ErrorCodes {
WS_CERT_REVOKED_E = -1083, /* User certificate reported revoked */
WS_CERT_SIG_CONFIRM_E = -1084, /* Root cert sig verify fail */
WS_CERT_OTHER_E = -1085, /* Other certificate issue */
WS_CERT_PROFILE_E = -1086, /* Cert doesn't meet profile reqs */
WS_LAST_E = -1085 /* Update this to indicate last error */
WS_LAST_E = -1086 /* Update this to indicate last error */
};