From 911ac8c433de703988e26f7d1c8673ff499bebfb Mon Sep 17 00:00:00 2001 From: John Safranek Date: Tue, 3 May 2016 09:51:01 -0700 Subject: [PATCH 1/5] 1. Adding parsing of publickey authentication data. 2. Changed logging of authentication requests. 3. Reply to "none" authentication types with a failure that has the supported auth type list. 4. Fixed bug where passing payload to the DoUserAuthRequest wasn't getting the correct payload length. 5. Reordered a couple utility functions. --- src/internal.c | 151 ++++++++++++++++++++++++++++++++------------- src/ssh.c | 3 + wolfssh/internal.h | 4 ++ 3 files changed, 114 insertions(+), 44 deletions(-) diff --git a/src/internal.c b/src/internal.c index ee9d1e6e..278bf557 100644 --- a/src/internal.c +++ b/src/internal.c @@ -163,6 +163,7 @@ static const NameIdPair NameIdMap[] = { /* UserAuth IDs */ { ID_USERAUTH_PASSWORD, "password" }, + { ID_USERAUTH_PUBLICKEY, "publickey" }, /* Channel Type IDs */ { ID_CHANTYPE_SESSION, "session" } @@ -1275,11 +1276,13 @@ static int DoDisconnect(WOLFSSH* ssh, uint8_t* buf, uint32_t len, uint32_t* idx) return WS_SUCCESS; } + #if 0 static const char serviceNameUserAuth[] = "ssh-userauth"; static const char serviceNameConnection[] = "ssh-connection"; #endif + static int DoServiceRequest(WOLFSSH* ssh, uint8_t* buf, uint32_t len, uint32_t* idx) { @@ -1305,45 +1308,6 @@ static int DoServiceRequest(WOLFSSH* ssh, } -static int DoUserAuthRequest(WOLFSSH* ssh, - uint8_t* buf, uint32_t len, uint32_t* idx) -{ - uint32_t begin = *idx; - uint32_t valueSz; - char value[32]; - - (void)ssh; - (void)len; - - ato32(buf + begin, &valueSz); - begin += LENGTH_SZ; - - XMEMCPY(value, buf + begin, valueSz); - begin += valueSz; - value[valueSz] = 0; - - ato32(buf + begin, &valueSz); - begin += LENGTH_SZ; - - XMEMCPY(value, buf + begin, valueSz); - begin += valueSz; - value[valueSz] = 0; - - ato32(buf + begin, &valueSz); - begin += LENGTH_SZ; - - XMEMCPY(value, buf + begin, valueSz); - begin += valueSz; - value[valueSz] = 0; - - *idx = begin; - - ssh->clientState = CLIENT_USERAUTH_DONE; - - return WS_SUCCESS; -} - - static int GetBoolean(uint8_t* v, uint8_t* buf, uint32_t len, uint32_t* idx) { int result = WS_BUFFER_E; @@ -1366,6 +1330,7 @@ static int GetUint32(uint32_t* v, uint8_t* buf, uint32_t len, uint32_t* idx) *idx += UINT32_SZ; result = WS_SUCCESS; } + return result; } @@ -1391,6 +1356,73 @@ static int GetString(char* s, uint32_t* sSz, } +static int DoUserAuthRequest(WOLFSSH* ssh, + uint8_t* buf, uint32_t len, uint32_t* idx) +{ + uint32_t begin = *idx; + int ret; + uint32_t valueSz; + char value[32]; + uint8_t authNameId; + + (void)ssh; + (void)len; + + DumpOctetString(buf, len); + ato32(buf + begin, &valueSz); + begin += LENGTH_SZ; + + XMEMCPY(value, buf + begin, valueSz); + begin += valueSz; + value[valueSz] = 0; + WLOG(WS_LOG_DEBUG, "DUAR: userName = %s", value); + + ato32(buf + begin, &valueSz); + begin += LENGTH_SZ; + + XMEMCPY(value, buf + begin, valueSz); + begin += valueSz; + value[valueSz] = 0; + WLOG(WS_LOG_DEBUG, "DUAR: serviceName = %s", value); + + ato32(buf + begin, &valueSz); + begin += LENGTH_SZ; + + XMEMCPY(value, buf + begin, valueSz); + begin += valueSz; + value[valueSz] = 0; + WLOG(WS_LOG_DEBUG, "DUAR: authName = %s", value); + authNameId = NameToId(value, valueSz); + + if (authNameId == ID_USERAUTH_PASSWORD) { + uint8_t pwChanged; + ret = GetBoolean(&pwChanged, buf, len, &begin); + WLOG(WS_LOG_DEBUG, "DUAR: pwChanged = %s", + (pwChanged ? "TRUE" : "FALSE")); + if (!pwChanged) { + ret = GetString(value, &valueSz, buf, len, &begin); + if (ret == WS_SUCCESS) + WLOG(WS_LOG_DEBUG, "DUAR: password = %s", value); + else + WLOG(WS_LOG_DEBUG, "DUAR: password = error? %d", ret); + } + else { + /* Skip the password change. Maybe error out since we aren't + * supporting password changes at this time. */ + } + } + else if (authNameId == ID_USERAUTH_PUBLICKEY) { + } else + SendUserAuthFailure(ssh, 0); + + *idx = begin; + + ssh->clientState = CLIENT_USERAUTH_DONE; + + return WS_SUCCESS; +} + + static int DoChannelOpen(WOLFSSH* ssh, uint8_t* buf, uint32_t len, uint32_t* idx) { @@ -1670,7 +1702,8 @@ static int DoPacket(WOLFSSH* ssh) case MSGID_USERAUTH_REQUEST: WLOG(WS_LOG_DEBUG, "Decoding MSGID_USERAUTH_REQUEST"); - DoUserAuthRequest(ssh, buf, payloadSz, &idx); + DoUserAuthRequest(ssh, buf + idx, payloadSz, &payloadIdx); + idx += payloadIdx; break; case MSGID_CHANNEL_OPEN: @@ -2623,18 +2656,48 @@ int SendServiceAccept(WOLFSSH* ssh) return ret; } -#if 0 + static const char cannedAuths[] = "password"; static const uint32_t cannedAuthsSz = sizeof(cannedAuths) - 1; int SendUserAuthFailure(WOLFSSH* ssh, uint8_t partialSuccess) { - (void)ssh; + uint8_t* output; + uint32_t idx; + int ret; + (void)partialSuccess; - return WS_SUCCESS; + + WLOG(WS_LOG_DEBUG, "Entering SendUserAuthFailure()"); + + if (ssh == NULL) + return WS_BAD_ARGUMENT; + + ret = PreparePacket(ssh, + MSG_ID_SZ + LENGTH_SZ + cannedAuthsSz + BOOLEAN_SZ); + if (ret != WS_SUCCESS) + return ret; + + output = ssh->outputBuffer.buffer; + idx = ssh->outputBuffer.length; + + output[idx++] = MSGID_USERAUTH_FAILURE; + c32toa(cannedAuthsSz, output + idx); + idx += LENGTH_SZ; + WMEMCPY(output + idx, cannedAuths, cannedAuthsSz); + idx += cannedAuthsSz; + output[idx++] = 0; + + ssh->outputBuffer.length = idx; + + ret = BundlePacket(ssh); + if (ret == WS_SUCCESS) + ret = SendBuffered(ssh); + + return ret; } -#endif + int SendUserAuthSuccess(WOLFSSH* ssh) { diff --git a/src/ssh.c b/src/ssh.c index 562fa10f..ba8ca924 100644 --- a/src/ssh.c +++ b/src/ssh.c @@ -228,6 +228,9 @@ static void SshResourceFree(WOLFSSH* ssh, void* heap) /* FreeRng(ssh->rng); */ WFREE(ssh->rng, heap, DYNTYPE_RNG); } + if (ssh->userName) { + WFREE(ssh->userName, heap, DYNTYPE_STRING); + } } diff --git a/wolfssh/internal.h b/wolfssh/internal.h index ed696659..d5d7d72c 100644 --- a/wolfssh/internal.h +++ b/wolfssh/internal.h @@ -80,6 +80,7 @@ enum { /* UserAuth IDs */ ID_USERAUTH_PASSWORD, + ID_USERAUTH_PUBLICKEY, /* Channel Type IDs */ ID_CHANTYPE_SESSION, @@ -246,6 +247,9 @@ struct WOLFSSH { uint8_t macKeyServerSz; HandshakeInfo* handshake; + + uint8_t* userName; + uint32_t userNameSz; }; From a744dcc540a528804ed7667af7ffd4b7db7dbc3e Mon Sep 17 00:00:00 2001 From: John Safranek Date: Wed, 4 May 2016 10:11:26 -0700 Subject: [PATCH 2/5] refactor DoPacket to pass the correct data pointer and data index update --- src/internal.c | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/src/internal.c b/src/internal.c index 278bf557..1e1c089a 100644 --- a/src/internal.c +++ b/src/internal.c @@ -1654,22 +1654,22 @@ static int DoPacket(WOLFSSH* ssh) case MSGID_DISCONNECT: WLOG(WS_LOG_DEBUG, "Decoding MSGID_KEXDH_INIT"); - DoDisconnect(ssh, buf, payloadSz, &idx); + DoDisconnect(ssh, buf + idx, payloadSz, &payloadIdx); break; case MSGID_IGNORE: WLOG(WS_LOG_DEBUG, "Decoding MSGID_KEXDH_INIT"); - DoIgnore(ssh, buf, payloadSz, &idx); + DoIgnore(ssh, buf + idx, payloadSz, &payloadIdx); break; case MSGID_UNIMPLEMENTED: WLOG(WS_LOG_DEBUG, "Decoding MSGID_KEXDH_INIT"); - DoUnimplemented(ssh, buf, payloadSz, &idx); + DoUnimplemented(ssh, buf + idx, payloadSz, &payloadIdx); break; case MSGID_DEBUG: WLOG(WS_LOG_DEBUG, "Decoding MSGID_KEXDH_INIT"); - DoDebug(ssh, buf, payloadSz, &idx); + DoDebug(ssh, buf + idx, payloadSz, &payloadIdx); break; case MSGID_KEXINIT: @@ -1681,53 +1681,48 @@ static int DoPacket(WOLFSSH* ssh) wc_ShaUpdate(&ssh->handshake->hash, scratchLen, LENGTH_SZ); wc_ShaUpdate(&ssh->handshake->hash, &msg, sizeof(msg)); wc_ShaUpdate(&ssh->handshake->hash, buf + idx, payloadSz); - DoKexInit(ssh, buf, payloadSz, &idx); + DoKexInit(ssh, buf + idx, payloadSz, &payloadIdx); } break; case MSGID_NEWKEYS: WLOG(WS_LOG_DEBUG, "Decoding MSGID_NEWKEYS"); - DoNewKeys(ssh, buf, payloadSz, &idx); + DoNewKeys(ssh, buf + idx, payloadSz, &payloadIdx); break; case MSGID_KEXDH_INIT: WLOG(WS_LOG_DEBUG, "Decoding MSGID_KEXDH_INIT"); - DoKexDhInit(ssh, buf, payloadSz, &idx); + DoKexDhInit(ssh, buf + idx, payloadSz, &payloadIdx); break; case MSGID_SERVICE_REQUEST: WLOG(WS_LOG_DEBUG, "Decoding MSGID_SERVICE_REQUEST"); - DoServiceRequest(ssh, buf, payloadSz, &idx); + DoServiceRequest(ssh, buf + idx, payloadSz, &payloadIdx); break; case MSGID_USERAUTH_REQUEST: WLOG(WS_LOG_DEBUG, "Decoding MSGID_USERAUTH_REQUEST"); DoUserAuthRequest(ssh, buf + idx, payloadSz, &payloadIdx); - idx += payloadIdx; break; case MSGID_CHANNEL_OPEN: WLOG(WS_LOG_DEBUG, "Decoding MSGID_CHANNEL_OPEN"); DoChannelOpen(ssh, buf + idx, payloadSz, &payloadIdx); - idx += payloadIdx; break; case MSGID_CHANNEL_WINDOW_ADJUST: WLOG(WS_LOG_DEBUG, "Decoding MSGID_CHANNEL_WINDOW_ADJUST"); DoChannelWindowAdjust(ssh, buf + idx, payloadSz, &payloadIdx); - idx += payloadIdx; break; case MSGID_CHANNEL_DATA: WLOG(WS_LOG_DEBUG, "Decoding MSGID_CHANNEL_DATA"); DoChannelData(ssh, buf + idx, payloadSz, &payloadIdx); - idx += payloadSz; break; case MSGID_CHANNEL_REQUEST: WLOG(WS_LOG_DEBUG, "Decoding MSGID_CHANNEL_REQUEST"); DoChannelRequest(ssh, buf + idx, payloadSz, &payloadIdx); - idx += payloadIdx; break; default: @@ -1735,10 +1730,10 @@ static int DoPacket(WOLFSSH* ssh) #ifdef SHOW_UNIMPLEMENTED DumpOctetString(buf + idx, payloadSz); #endif - idx += payloadSz; SendUnimplemented(ssh); break; } + idx += payloadIdx; if (idx + padSz > len) { WLOG(WS_LOG_DEBUG, "Not enough data in buffer for pad."); From a275ac59f0e8226a13c37cc6ebe7e31e5a563d59 Mon Sep 17 00:00:00 2001 From: John Safranek Date: Mon, 16 May 2016 18:16:53 -0700 Subject: [PATCH 3/5] 1. Flushed out the authentication callback. 2. Added public key authentication. --- certs/key-ecc.pem | 5 + certs/key-ecc.pub | 1 + certs/key-gretel.pem | 27 ++++ certs/key-hansel.pem | 27 ++++ certs/passwd.txt | 2 + certs/publickeys.txt | 2 + examples/echoserver/echoserver.c | 261 +++++++++++++++++++++++++++++++ src/internal.c | 203 ++++++++++++++++++------ src/ssh.c | 25 +++ wolfssh/internal.h | 22 +-- wolfssh/ssh.h | 56 ++++++- 11 files changed, 573 insertions(+), 58 deletions(-) create mode 100644 certs/key-ecc.pem create mode 100644 certs/key-ecc.pub create mode 100644 certs/key-gretel.pem create mode 100644 certs/key-hansel.pem create mode 100644 certs/passwd.txt create mode 100644 certs/publickeys.txt diff --git a/certs/key-ecc.pem b/certs/key-ecc.pem new file mode 100644 index 00000000..426658f0 --- /dev/null +++ b/certs/key-ecc.pem @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIPIxGlM2XBIY6NulXIJgXdOheM+mZ7ixU8RLaXy2OoJeoAoGCCqGSM49 +AwEHoUQDQgAEpalbz23q5UgbcVHU0uGllXYqEw9kAG/uyCSMfZ1wliKi6tXrs/9p +WCseyyREeyKmmMqt6WkekGJd54jHPa/Wyw== +-----END EC PRIVATE KEY----- diff --git a/certs/key-ecc.pub b/certs/key-ecc.pub new file mode 100644 index 00000000..4d63eaed --- /dev/null +++ b/certs/key-ecc.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBKWpW89t6uVIG3FR1NLhpZV2KhMPZABv7sgkjH2dcJYiourV67P/aVgrHsskRHsippjKrelpHpBiXeeIxz2v1ss= john@johnwork.local diff --git a/certs/key-gretel.pem b/certs/key-gretel.pem new file mode 100644 index 00000000..e5f7403f --- /dev/null +++ b/certs/key-gretel.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAqg8EVU0VZP8Iz4aKOuvjM7a3N9SrMQ2fpAFdNd/Tx0PsLLnj +aW2uozZ+aOplExDq8a89CzLAyTgTOwphtPfN5BeIESIoRAqUNK3Izj+gUn21UxPZ +nyCuSLFImRnfEqBPZEldqSdhb2XgUBDaAMRBZNM2S/bXIT0vjglBmyuEg487jWZf +99DHM7O9zdzAc4uidaD6O7BZaswennAYytiqY7rOGNa2BaYSZ1MbSrwdLPoaGmna +7m4hOe3Sugax+YmcFS00Crsd9bgiSz4YpLSN4i23ZRmRHLSZE2rH1UrFIs8FSMkY +4VSpm3SERsJFN/A9ONwEdYdJlKBgjyCrqnBTHwIDAQABAoIBADTkttRRRXZEXNkv +X480D1bmXdZfr19yfVTll7hKBfTUi4Dd0H3aP5dEO80mGonzmR/TAYmaH5x2dITI +ldtTuBZZu1iY5y1CnRZFd0+vOo5tyxgr9GQqJgs2GP6FrXx9oDPxHdCfDw83AK3m +j+ftIunZR+oYvJD6FvB2sJEy1+STBoI6znDILaJzm0sR6YCnaH05cfVTB/UyNisy +8OAIJrhB+oiH/BdWyYDhVM3E09uFt8b5+rJVqhQ+G+dFzRNsTLLAzADGWtxr3Yl6 +XtHjNDE/HobMgjlH6CkXPZ5ZO3wkUPg4EuTaS1atlaxWqHqp/3OffB8rPGxg0N+n +w9NRbwECgYEA1E6AgiCVOj7fIFcznoEJSu2tcdPcCD+PopWisNWNE678ZqlUsD/4 +7Fxz24RUn+1+pO4VjQ0vmDkhgM+O28gJdd1O+3loht9+h3Ie2+/EUXQudmqs7sPV +8DAuhWrnqrO6W4/nO/6MEgcXI5iJyi+uCNclMARDN1T3bh60UbcfAksCgYEAzQ6n +xGBkkAm2gItkjdIlZFVKJikSpKhLnQV5D04y/S5nw1XBdk7iL8rWIqZVYlE+7sjX +TB4gCXtFFZDTnM8fSzQU5I3ggo5NxmKq/Q4hl2aFSPEK3PMQ4Ik4jud85rluhTG8 +WRtIlvbqSKTSiuPbZXD/xmS3EL8Xf+V2wbK/zf0CgYEAsvCbZZIa1KXLIBH/Ytf1 +Qh8Dcg4TxSv1Xx5pqkvDhVSWTdzokUjKAEWILPvi64ybkl1M8r6rX8y/TTcjfGCk +gKAQAup4TD0xAu4PzmXO/KxEwO/2Y6PRvIiPnUnWiszDBItMZQeNfWBWg1z8vdnk +AHV9VXQyRv+pMDpW1wzV6PMCgYAnL6HH2VPeYrzJm9m/cGVM4y+kUz0I0lCA1Ubp +Mdx0naWeooix/ykiUPTS8k5m13fbUe4y0Z71sOTm1iJaWQp16KIFe0doK6GZQ8nB +Si4JLMJTyhx1VM3o2tBAHuSzgsQoF/USYjBhCRaEg1rox9ppbEq0sxJ41Mu82TD0 +myAkUQKBgFboHyz32Lx4/xB0fNTyw5c4tmnvQ8B1BFInXV3+yfL6vRIVDAfj5mMn +WUNo1Hd77uHLuB3n4TzU8KwbF1vD2aACLk3p+OVRramMyv98nrl83oBMONxUh9Qb +J1NBwEkdjaqk+s+r3SSl7avVJJOViEGwGnrm0a6kACd5zxdjx0vk +-----END RSA PRIVATE KEY----- diff --git a/certs/key-hansel.pem b/certs/key-hansel.pem new file mode 100644 index 00000000..f596f4d5 --- /dev/null +++ b/certs/key-hansel.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAvT92RaMDrDjVxw+TMFognIl8rQUWRoaDDYorFkoFLOR3R3AA +rh2D4tlumdTwRZgVk/aHTqxkY6GVyXww6D4vo/EknwxrHP4bApnNxqdshIVGVBJA +4bTl8qo57NYnJAvRoeLvNGklbcB0ZyWYfcT4UqubSzoSHeHj+tbPmuacI045xIQW +iD1CTtgvzNKRZ522cSoCZV+7dQ6Mu4eXl8b4spjiL2gmSlPseTqKX8zP8BZHstBD +1jZsyOcv/qc1OWn7HXhFnYkAyEHPNB+j8/H7KBT72EhvrOP8M9Hbru8nnldWKaIa +OuWa/qRJyH+3TtAfBG5YFrfrnfiSPMKwIXxOMQIDAQABAoIBAQCNpGEGL8NA9Gz0 +hzC4AMzlvHWHHgaVFHoj+STUkuQavIiV/DtWFhsu/2QrWNfYjsKfsuWEubyNYVQ1 +sHD+cgTAJG0vaWEGGx3mLW15YLf027dOlzbed8GfhU7Dd2lmLj5hdvNn+8aaxW+Z +/+aJQ5JEddJOVJFYskgq5voNSsrUFJ72J2e3JXpDuytn0f7RaCMGMHy/YEnezH4m +Wjv+pqbnqN2sua+CmjpBfmEhN6MI5MS8EfU7jk1R873aurLF7vvP34OhggHhUZ0H +Wl3Yx1s/lxNqTR6NOaxAlYJsoqHMipshMjpYzOctGnmkMVCxS3YjG7NAPT1ycjLs +Xzi1jbKNAoGBAO1afo6hYn0mXHjEh3HJQVd3lJOTJnjIoxW9WcsbtLJrD+eA8vr8 +jjKpGx5/4SbvACXY3ckaIwAmO0YjwFDnzmKyNrKYCRY0GJ5GvK8sKJQv4F3Jssj7 +XRPVNqoVD4mlFlldInSkR136+wxegL8PwpyVD+eqfxYb1Ns4fVguV3gvAoGBAMwd +f3Q2bbSSJWLFULBcodrzsv0emA2LBWlgjl7SiZBKDUZ+4lRprhbmy9W9ezAre1zu +kxLPY4mcPcgt5HphCV6A+zwDs3PWmNCEDFmfToDzRu0DndXci+ex6KpX3NFBVQfH +32c8cniwYI+FoZCZDKVnq/C2dJADVXtezMW/3qefAoGAQIFuka7UiHSrfvrSYJ80 +jePm0jCUrRDCGb9rLuLpue+U0/Lclk+bCbOhtilE9ILRxHdq1yOuTXUWeNpwgsxs +76/FY8Yj+g/QfPt2fhj/Mj7MuFB/sVV3F1PD1neA0IS4TTMdkRuwdZ8nKVZpoQNU +fZ+ZQfm5LjYEJEv27MczaGsCgYBgNcs80Ob3BSggHVeCObeFB/enPcN4Jr4/RGb3 +JQ/4dh85ylcOaN3JJ7KOpgip1OUKEd47MIv/cijg8VjPomuTIwLI8AmnIVDYgFV9 +7QxI1eLplxnPk2xSotZDbLTF4aCd0UVpWOGwJ5rsK5XTHYELeglepfHda+TgCPhG +gcEGiwKBgAD28uslungErQ4NLqdp1lfmNjJQ0vLrrTFGZcAHl4NsZic+lCwFAV9c +4DEw7GHSdDW3nzjnjmexUAhozs/Y7oj9XcTN4oY9Sg4Ef+6K6JsWofwJguJiAzzo +JX88mqqD+NiT0VT5zrT6NTbMGFSq8pC3fJcLJy+u/MOTrxp17Bjb +-----END RSA PRIVATE KEY----- diff --git a/certs/passwd.txt b/certs/passwd.txt new file mode 100644 index 00000000..c7f7ce31 --- /dev/null +++ b/certs/passwd.txt @@ -0,0 +1,2 @@ +jill:upthehill +jack:fetchapail diff --git a/certs/publickeys.txt b/certs/publickeys.txt new file mode 100644 index 00000000..7cb5e7c8 --- /dev/null +++ b/certs/publickeys.txt @@ -0,0 +1,2 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC9P3ZFowOsONXHD5MwWiCciXytBRZGhoMNiisWSgUs5HdHcACuHYPi2W6Z1PBFmBWT9odOrGRjoZXJfDDoPi+j8SSfDGsc/hsCmc3Gp2yEhUZUEkDhtOXyqjns1ickC9Gh4u80aSVtwHRnJZh9xPhSq5tLOhId4eP61s+a5pwjTjnEhBaIPUJO2C/M0pFnnbZxKgJlX7t1Doy7h5eXxviymOIvaCZKU+x5OopfzM/wFkey0EPWNmzI5y/+pzU5afsdeEWdiQDIQc80H6Pz8fsoFPvYSG+s4/wz0duu7yeeV1Ypoho65Zr+pEnIf7dO0B8EblgWt+ud+JI8wrAhfE4x hansel +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCqDwRVTRVk/wjPhoo66+Mztrc31KsxDZ+kAV0139PHQ+wsueNpba6jNn5o6mUTEOrxrz0LMsDJOBM7CmG0983kF4gRIihECpQ0rcjOP6BSfbVTE9mfIK5IsUiZGd8SoE9kSV2pJ2FvZeBQENoAxEFk0zZL9tchPS+OCUGbK4SDjzuNZl/30Mczs73N3MBzi6J1oPo7sFlqzB6ecBjK2Kpjus4Y1rYFphJnUxtKvB0s+hoaadrubiE57dK6BrH5iZwVLTQKux31uCJLPhiktI3iLbdlGZEctJkTasfVSsUizwVIyRjhVKmbdIRGwkU38D043AR1h0mUoGCPIKuqcFMf gretel diff --git a/examples/echoserver/echoserver.c b/examples/echoserver/echoserver.c index 780199d5..736d5a12 100644 --- a/examples/echoserver/echoserver.c +++ b/examples/echoserver/echoserver.c @@ -33,6 +33,8 @@ #include #include #include +#include +#include #include #ifndef SO_NOSIGPIPE #include @@ -315,9 +317,253 @@ static int load_file(const char* fileName, uint8_t* buf, uint32_t bufSz) } +static inline void c32toa(uint32_t u32, uint8_t* c) +{ + c[0] = (u32 >> 24) & 0xff; + c[1] = (u32 >> 16) & 0xff; + c[2] = (u32 >> 8) & 0xff; + c[3] = u32 & 0xff; +} + + +/* 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. */ +typedef struct PwMap { + uint8_t type; + uint8_t username[32]; + uint32_t usernameSz; + uint8_t p[SHA256_DIGEST_SIZE]; + struct PwMap* next; +} PwMap; + + +typedef struct PwMapList { + PwMap* head; +} PwMapList; + + +static PwMap* PwMapNew(PwMapList* list, uint8_t type, const uint8_t* username, + uint32_t usernameSz, const uint8_t* p, uint32_t pSz) +{ + PwMap* map; + + map = (PwMap*)malloc(sizeof(PwMap)); + if (map != NULL) { + Sha256 sha; + uint8_t flatSz[4]; + + map->type = type; + if (usernameSz >= sizeof(map->username)) + usernameSz = sizeof(map->username) - 1; + memcpy(map->username, username, usernameSz + 1); + map->username[usernameSz] = 0; + map->usernameSz = usernameSz; + + wc_InitSha256(&sha); + c32toa(pSz, flatSz); + wc_Sha256Update(&sha, flatSz, sizeof(flatSz)); + wc_Sha256Update(&sha, p, pSz); + wc_Sha256Final(&sha, map->p); + + map->next = list->head; + list->head = map; + } + + return map; +} + + +static void PwMapListDelete(PwMapList* list) +{ + if (list != NULL) { + PwMap* head = list->head; + + while (head != NULL) { + PwMap* cur = head; + head = head->next; + memset(cur, 0, sizeof(PwMap)); + } + } +} + + +static const char samplePasswordBuffer[] = + "jill:upthehill\n" + "jack:fetchapail\n"; + + +static const char samplePublicKeyBuffer[] = + "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC9P3ZFowOsONXHD5MwWiCciXytBRZGho" + "MNiisWSgUs5HdHcACuHYPi2W6Z1PBFmBWT9odOrGRjoZXJfDDoPi+j8SSfDGsc/hsCmc3G" + "p2yEhUZUEkDhtOXyqjns1ickC9Gh4u80aSVtwHRnJZh9xPhSq5tLOhId4eP61s+a5pwjTj" + "nEhBaIPUJO2C/M0pFnnbZxKgJlX7t1Doy7h5eXxviymOIvaCZKU+x5OopfzM/wFkey0EPW" + "NmzI5y/+pzU5afsdeEWdiQDIQc80H6Pz8fsoFPvYSG+s4/wz0duu7yeeV1Ypoho65Zr+pE" + "nIf7dO0B8EblgWt+ud+JI8wrAhfE4x hansel\n" + "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCqDwRVTRVk/wjPhoo66+Mztrc31KsxDZ" + "+kAV0139PHQ+wsueNpba6jNn5o6mUTEOrxrz0LMsDJOBM7CmG0983kF4gRIihECpQ0rcjO" + "P6BSfbVTE9mfIK5IsUiZGd8SoE9kSV2pJ2FvZeBQENoAxEFk0zZL9tchPS+OCUGbK4SDjz" + "uNZl/30Mczs73N3MBzi6J1oPo7sFlqzB6ecBjK2Kpjus4Y1rYFphJnUxtKvB0s+hoaadru" + "biE57dK6BrH5iZwVLTQKux31uCJLPhiktI3iLbdlGZEctJkTasfVSsUizwVIyRjhVKmbdI" + "RGwkU38D043AR1h0mUoGCPIKuqcFMf gretel\n"; + + +static int LoadPasswordBuffer(uint8_t* buf, uint32_t bufSz, PwMapList* list) +{ + char* str = (char*)buf; + char* delimiter; + char* username; + char* password; + + /* Each line of passwd.txt is in the format + * username:password\n + * This function modifies the passed-in buffer. */ + + if (list == NULL) + return -1; + + if (buf == NULL || bufSz == 0) + return 0; + + while (*str != 0) { + delimiter = strchr(str, ':'); + username = str; + *delimiter = 0; + password = delimiter + 1; + str = strchr(password, '\n'); + *str = 0; + str++; + if (PwMapNew(list, WOLFSSH_USERAUTH_PASSWORD, + (uint8_t*)username, (uint32_t)strlen(username), + (uint8_t*)password, (uint32_t)strlen(password)) == NULL ) { + + return -1; + } + } + + return 0; +} + + +static int LoadPublicKeyBuffer(uint8_t* buf, uint32_t bufSz, PwMapList* list) +{ + char* str = (char*)buf; + char* delimiter; + uint8_t* publicKey64; + uint32_t publicKey64Sz; + uint8_t* username; + uint32_t usernameSz; + uint8_t publicKey[300]; + uint32_t publicKeySz; + int decodeResult; + + /* Each line of passwd.txt is in the format + * ssh-rsa AAAB3BASE64ENCODEDPUBLICKEYBLOB username\n + * This function modifies the passed-in buffer. */ + if (list == NULL) + return -1; + + if (buf == NULL || bufSz == 0) + return 0; + + while (*str != 0) { + /* Skip the public key type. This example will always be ssh-rsa. */ + delimiter = strchr(str, ' '); + str = delimiter + 1; + delimiter = strchr(str, ' '); + publicKey64 = (uint8_t*)str; + publicKey64Sz = (uint32_t)(delimiter - str); + str = delimiter + 1; + delimiter = strchr(str, '\n'); + username = (uint8_t*)str; + usernameSz = (uint32_t)(delimiter - str); + str = delimiter + 1; + publicKeySz = sizeof(publicKey); + + decodeResult = Base64_Decode(publicKey64, publicKey64Sz, + publicKey, &publicKeySz); + + printf("Base64_Decode = %d\n", decodeResult); + + if (PwMapNew(list, WOLFSSH_USERAUTH_PUBLICKEY, + username, usernameSz, + publicKey, publicKeySz) == NULL ) { + + return -1; + } + } + + return 0; +} + + +static int wsUserAuth(uint8_t authType, + const WS_UserAuthData* authData, + void* ctx) +{ + PwMapList* list; + PwMap* map; + uint8_t authHash[SHA256_DIGEST_SIZE]; + + if (ctx == NULL) { + fprintf(stderr, "wsUserAuth: ctx not set"); + return WOLFSSH_USERAUTH_FAILURE; + } + + if (authType != WOLFSSH_USERAUTH_PASSWORD && + authType != WOLFSSH_USERAUTH_PUBLICKEY) { + + return WOLFSSH_USERAUTH_FAILURE; + } + + /* Hash the password or public key with its length. */ + { + Sha256 sha; + uint8_t flatSz[4]; + wc_InitSha256(&sha); + if (authType == WOLFSSH_USERAUTH_PASSWORD) { + c32toa(authData->sf.password.passwordSz, flatSz); + wc_Sha256Update(&sha, flatSz, sizeof(flatSz)); + wc_Sha256Update(&sha, + authData->sf.password.password, + authData->sf.password.passwordSz); + } + else if (authType == WOLFSSH_USERAUTH_PUBLICKEY) { + c32toa(authData->sf.publicKey.publicKeySz, flatSz); + wc_Sha256Update(&sha, flatSz, sizeof(flatSz)); + wc_Sha256Update(&sha, + authData->sf.publicKey.publicKey, + authData->sf.publicKey.publicKeySz); + } + wc_Sha256Final(&sha, authHash); + } + + list = (PwMapList*)ctx; + map = list->head; + + while (map != NULL) { + if (authData->type == map->type && + authData->usernameSz == map->usernameSz && + memcmp(authData->username, map->username, map->usernameSz) == 0) { + if (memcmp(map->p, authHash, SHA256_DIGEST_SIZE) != 0) { + return (authType == WOLFSSH_USERAUTH_PASSWORD ? + WOLFSSH_USERAUTH_INVALID_PASSWORD : + WOLFSSH_USERAUTH_INVALID_PUBLICKEY); + } + + return WOLFSSH_USERAUTH_SUCCESS; + } + map = map->next; + } + + return WOLFSSH_USERAUTH_INVALID_USER; +} + + int main(void) { WOLFSSH_CTX* ctx = NULL; + PwMapList pwMapList; SOCKET_T listenFd = 0; #ifdef DEBUG_WOLFSSH @@ -335,6 +581,9 @@ int main(void) exit(EXIT_FAILURE); } + memset(&pwMapList, 0, sizeof(pwMapList)); + wolfSSH_SetUserAuth(ctx, wsUserAuth); + { uint8_t buf[SCRATCH_BUFFER_SIZE]; uint32_t bufSz; @@ -360,6 +609,16 @@ int main(void) fprintf(stderr, "Couldn't use key buffer.\n"); exit(EXIT_FAILURE); } + + bufSz = (uint32_t)strlen((char*)samplePasswordBuffer); + memcpy(buf, samplePasswordBuffer, bufSz); + buf[bufSz] = 0; + LoadPasswordBuffer(buf, bufSz, &pwMapList); + + bufSz = (uint32_t)strlen((char*)samplePublicKeyBuffer); + memcpy(buf, samplePublicKeyBuffer, bufSz); + buf[bufSz] = 0; + LoadPublicKeyBuffer(buf, bufSz, &pwMapList); } tcp_bind(&listenFd, SERVER_PORT_NUMBER, 0); @@ -376,6 +635,7 @@ int main(void) fprintf(stderr, "Couldn't allocate SSH data.\n"); exit(EXIT_FAILURE); } + wolfSSH_SetUserAuthCtx(ssh, &pwMapList); if (listen(listenFd, 5) != 0) err_sys("tcp listen failed"); @@ -391,6 +651,7 @@ int main(void) pthread_detach(thread); } + PwMapListDelete(&pwMapList); if (wolfSSH_Cleanup() != WS_SUCCESS) { fprintf(stderr, "Couldn't clean up wolfSSH.\n"); exit(EXIT_FAILURE); diff --git a/src/internal.c b/src/internal.c index 1e1c089a..8d0983a9 100644 --- a/src/internal.c +++ b/src/internal.c @@ -1360,65 +1360,176 @@ static int DoUserAuthRequest(WOLFSSH* ssh, uint8_t* buf, uint32_t len, uint32_t* idx) { uint32_t begin = *idx; - int ret; - uint32_t valueSz; - char value[32]; - uint8_t authNameId; + int ret; + uint8_t authNameId; + uint32_t authNameSz; + WS_UserAuthData authData; - (void)ssh; - (void)len; + GetUint32(&authData.usernameSz, buf, len, &begin); + authData.username = buf + begin; + begin += authData.usernameSz; - DumpOctetString(buf, len); - ato32(buf + begin, &valueSz); - begin += LENGTH_SZ; + GetUint32(&authData.serviceNameSz, buf, len, &begin); + authData.serviceName = buf + begin; + begin += authData.serviceNameSz; - XMEMCPY(value, buf + begin, valueSz); - begin += valueSz; - value[valueSz] = 0; - WLOG(WS_LOG_DEBUG, "DUAR: userName = %s", value); - - ato32(buf + begin, &valueSz); - begin += LENGTH_SZ; - - XMEMCPY(value, buf + begin, valueSz); - begin += valueSz; - value[valueSz] = 0; - WLOG(WS_LOG_DEBUG, "DUAR: serviceName = %s", value); - - ato32(buf + begin, &valueSz); - begin += LENGTH_SZ; - - XMEMCPY(value, buf + begin, valueSz); - begin += valueSz; - value[valueSz] = 0; - WLOG(WS_LOG_DEBUG, "DUAR: authName = %s", value); - authNameId = NameToId(value, valueSz); + GetUint32(&authNameSz, buf, len, &begin); + authNameId = NameToId((const char*)(buf + begin), authNameSz); + begin += authNameSz; if (authNameId == ID_USERAUTH_PASSWORD) { - uint8_t pwChanged; - ret = GetBoolean(&pwChanged, buf, len, &begin); - WLOG(WS_LOG_DEBUG, "DUAR: pwChanged = %s", - (pwChanged ? "TRUE" : "FALSE")); - if (!pwChanged) { - ret = GetString(value, &valueSz, buf, len, &begin); - if (ret == WS_SUCCESS) - WLOG(WS_LOG_DEBUG, "DUAR: password = %s", value); - else - WLOG(WS_LOG_DEBUG, "DUAR: password = error? %d", ret); - } - else { + WS_UserAuthData_Password* pw = &authData.sf.password; + + authData.type = WOLFSSH_USERAUTH_PASSWORD; + ret = GetBoolean(&pw->hasNewPassword, buf, len, &begin); + ret = GetUint32(&pw->passwordSz, buf, len, &begin); + pw->password = buf + begin; + begin += pw->passwordSz; + + if (pw->hasNewPassword) { /* Skip the password change. Maybe error out since we aren't * supporting password changes at this time. */ + ret = GetUint32(&pw->newPasswordSz, buf, len, &begin); + pw->newPassword = buf + begin; + begin += pw->newPasswordSz; + } + else { + pw->newPassword = NULL; + pw->newPasswordSz = 0; + } + + if (ssh->ctx->userAuthCb != NULL) { + WLOG(WS_LOG_DEBUG, "DUAR: Checking the password"); + ret = ssh->ctx->userAuthCb(WOLFSSH_USERAUTH_PASSWORD, + &authData, ssh->userAuthCtx); + if (ret == WS_SUCCESS) { + WLOG(WS_LOG_DEBUG, "DUAR: password check successful"); + ssh->clientState = CLIENT_USERAUTH_DONE; + } + else { + WLOG(WS_LOG_DEBUG, "DUAR: password check failed"); + SendUserAuthFailure(ssh, 0); + } + } + else { + WLOG(WS_LOG_DEBUG, "DUAR: No user auth callback"); } } else if (authNameId == ID_USERAUTH_PUBLICKEY) { - } else + WS_UserAuthData_PublicKey* pk = &authData.sf.publicKey; + + authData.type = WOLFSSH_USERAUTH_PUBLICKEY; + ret = GetBoolean(&pk->hasSignature, buf, len, &begin); + GetUint32(&pk->publicKeyTypeSz, buf, len, &begin); + pk->publicKeyType = buf + begin; + begin += pk->publicKeyTypeSz; + GetUint32(&pk->publicKeySz, buf, len, &begin); + pk->publicKey = buf + begin; + begin += pk->publicKeySz; + + if (pk->hasSignature) { + GetUint32(&pk->signatureSz, buf, len, &begin); + pk->signature = buf + begin; + begin += pk->signatureSz; + } + else { + pk->signature = NULL; + pk->signatureSz = 0; + } + + if (ssh->ctx->userAuthCb != NULL) { + ret = ssh->ctx->userAuthCb(WOLFSSH_USERAUTH_PUBLICKEY, + &authData, ssh->userAuthCtx); + } + + if (pk->signature == NULL) { + WLOG(WS_LOG_DEBUG, "DUAR: Send the PK OK!"); + } + else { + uint8_t checkDigest[MAX_ENCODED_SIG_SZ]; + uint32_t checkDigestSz = 0; + uint8_t encDigest[MAX_ENCODED_SIG_SZ]; + uint32_t encDigestSz; + + { + RsaKey key; + uint8_t* n; + uint32_t nSz; + uint8_t* e; + uint32_t eSz; + uint32_t i = 0; + + GetUint32(&nSz, pk->publicKey, pk->publicKeySz, &i); + i += nSz; + GetUint32(&eSz, pk->publicKey, pk->publicKeySz, &i); + e = pk->publicKey + i; + i += eSz; + GetUint32(&nSz, pk->publicKey, pk->publicKeySz, &i); + n = authData.sf.publicKey.publicKey + i; + + wc_InitRsaKey(&key, ssh->ctx->heap); + ret = wc_RsaPublicKeyDecodeRaw(n, nSz, e, eSz, &key); + + i = 0; + GetUint32(&nSz, pk->signature, pk->signatureSz, &i); + i += nSz; + GetUint32(&nSz, pk->signature, pk->signatureSz, &i); + n = pk->signature + i; + ret = wc_RsaSSL_Verify(n, nSz, checkDigest, + sizeof(checkDigest), &key); + wc_FreeRsaKey(&key); + } + + if (ret > 0) { + checkDigestSz = (uint32_t)ret; + ret = 0; + } + + { + Sha sha; + uint8_t digest[SHA_DIGEST_SIZE]; + + wc_InitSha(&sha); + c32toa(ssh->sessionIdSz, digest); + wc_ShaUpdate(&sha, digest, UINT32_SZ); + wc_ShaUpdate(&sha, ssh->sessionId, ssh->sessionIdSz); + digest[0] = MSGID_USERAUTH_REQUEST; + wc_ShaUpdate(&sha, digest, MSG_ID_SZ); + + /* The rest of the fields in the signature are already + * in the buffer. Just need to account for the sizes. */ + wc_ShaUpdate(&sha, buf + *idx, + authData.usernameSz + authData.serviceNameSz + + authNameSz + BOOLEAN_SZ + + authData.sf.publicKey.publicKeyTypeSz + + authData.sf.publicKey.publicKeySz + (UINT32_SZ * 5)); + ShaFinal(&sha, digest); + + encDigestSz = wc_EncodeSignature(encDigest, digest, + SHA_DIGEST_SIZE, SHAh); + } + + { + volatile int compare; + volatile int sizeCompare; + + compare = ConstantCompare(encDigest, checkDigest, encDigestSz); + sizeCompare = encDigestSz != checkDigestSz; + + if (compare || sizeCompare || ret < 0) { + SendUserAuthFailure(ssh, 0); + } + else { + ssh->clientState = CLIENT_USERAUTH_DONE; + } + } + } + } + else SendUserAuthFailure(ssh, 0); *idx = begin; - ssh->clientState = CLIENT_USERAUTH_DONE; - return WS_SUCCESS; } @@ -2652,7 +2763,7 @@ int SendServiceAccept(WOLFSSH* ssh) } -static const char cannedAuths[] = "password"; +static const char cannedAuths[] = "publickey"; static const uint32_t cannedAuthsSz = sizeof(cannedAuths) - 1; diff --git a/src/ssh.c b/src/ssh.c index ba8ca924..d964da06 100644 --- a/src/ssh.c +++ b/src/ssh.c @@ -487,6 +487,31 @@ int wolfSSH_stream_send(WOLFSSH* ssh, uint8_t* buf, uint32_t bufSz) } +void wolfSSH_SetUserAuth(WOLFSSH_CTX* ctx, WS_CallbackUserAuth cb) +{ + if (ctx != NULL) { + ctx->userAuthCb = cb; + } +} + + +void wolfSSH_SetUserAuthCtx(WOLFSSH* ssh, void* userAuthCtx) +{ + if (ssh != NULL) { + ssh->userAuthCtx = userAuthCtx; + } +} + + +void* wolfSSH_GetUserAuthCtx(WOLFSSH* ssh) +{ + if (ssh != NULL) { + return ssh->userAuthCtx; + } + return NULL; +} + + static int ProcessBuffer(WOLFSSH_CTX* ctx, const uint8_t* in, uint32_t inSz, int format, int type) { diff --git a/wolfssh/internal.h b/wolfssh/internal.h index d5d7d72c..39271f65 100644 --- a/wolfssh/internal.h +++ b/wolfssh/internal.h @@ -134,16 +134,17 @@ WOLFSSH_LOCAL void ShrinkBuffer(Buffer* buf, int); /* our wolfSSH Context */ struct WOLFSSH_CTX { - void* heap; /* heap hint */ - WS_CallbackIORecv ioRecvCb; /* I/O Receive Callback */ - WS_CallbackIOSend ioSendCb; /* I/O Send Callback */ + void* heap; /* heap hint */ + WS_CallbackIORecv ioRecvCb; /* I/O Receive Callback */ + WS_CallbackIOSend ioSendCb; /* I/O Send Callback */ + WS_CallbackUserAuth userAuthCb; /* User Authentication Callback */ - uint8_t* cert; /* Owned by CTX */ - uint32_t certSz; - uint8_t* caCert; /* Owned by CTX */ - uint32_t caCertSz; - uint8_t* privateKey; /* Owned by CTX */ - uint32_t privateKeySz; + uint8_t* cert; /* Owned by CTX */ + uint32_t certSz; + uint8_t* caCert; /* Owned by CTX */ + uint32_t caCertSz; + uint8_t* privateKey; /* Owned by CTX */ + uint32_t privateKeySz; }; @@ -248,8 +249,11 @@ struct WOLFSSH { HandshakeInfo* handshake; + void* userAuthCtx; uint8_t* userName; uint32_t userNameSz; + uint8_t* pkBlob; + uint32_t pkBlobSz; }; diff --git a/wolfssh/ssh.h b/wolfssh/ssh.h index 929393c1..0bb05416 100644 --- a/wolfssh/ssh.h +++ b/wolfssh/ssh.h @@ -66,16 +66,51 @@ WOLFSSH_API const char* wolfSSH_get_error_name(const WOLFSSH*); /* I/O callbacks */ typedef int (*WS_CallbackIORecv)(WOLFSSH*, void*, uint32_t, void*); typedef int (*WS_CallbackIOSend)(WOLFSSH*, void*, uint32_t, void*); - WOLFSSH_API void wolfSSH_SetIORecv(WOLFSSH_CTX*, WS_CallbackIORecv); WOLFSSH_API void wolfSSH_SetIOSend(WOLFSSH_CTX*, WS_CallbackIOSend); - WOLFSSH_API void wolfSSH_SetIOReadCtx(WOLFSSH*, void*); WOLFSSH_API void wolfSSH_SetIOWriteCtx(WOLFSSH*, void*); - WOLFSSH_API void* wolfSSH_GetIOReadCtx(WOLFSSH*); WOLFSSH_API void* wolfSSH_GetIOWriteCtx(WOLFSSH*); +/* User Authentication callback */ + +typedef struct WS_UserAuthData_Password { + uint8_t* password; + uint32_t passwordSz; + /* The following are present for future use. */ + uint8_t hasNewPassword; + uint8_t* newPassword; + uint32_t newPasswordSz; +} WS_UserAuthData_Password; + +typedef struct WS_UserAuthData_PublicKey { + uint8_t* publicKeyType; + uint32_t publicKeyTypeSz; + uint8_t* publicKey; + uint32_t publicKeySz; + uint8_t hasSignature; + uint8_t* signature; + uint32_t signatureSz; +} WS_UserAuthData_PublicKey; + +typedef struct WS_UserAuthData { + uint8_t type; + uint8_t* username; + uint32_t usernameSz; + uint8_t* serviceName; + uint32_t serviceNameSz; + union { + WS_UserAuthData_Password password; + WS_UserAuthData_PublicKey publicKey; + } sf; +} WS_UserAuthData; + +typedef int (*WS_CallbackUserAuth)(uint8_t, const WS_UserAuthData*, void*); +WOLFSSH_API void wolfSSH_SetUserAuth(WOLFSSH_CTX*, WS_CallbackUserAuth); +WOLFSSH_API void wolfSSH_SetUserAuthCtx(WOLFSSH*, void*); +WOLFSSH_API void* wolfSSH_GetUserAuthCtx(WOLFSSH*); + WOLFSSH_API int wolfSSH_CTX_UsePrivateKey_buffer(WOLFSSH_CTX*, const uint8_t*, uint32_t, int); WOLFSSH_API int wolfSSH_CTX_UseCert_buffer(WOLFSSH_CTX*, @@ -104,6 +139,21 @@ enum WS_FormatTypes { }; +enum WS_UserAuthTypes { + WOLFSSH_USERAUTH_PASSWORD, + WOLFSSH_USERAUTH_PUBLICKEY +}; + + +enum WS_UserAuthResults { + WOLFSSH_USERAUTH_SUCCESS, + WOLFSSH_USERAUTH_FAILURE, + WOLFSSH_USERAUTH_INVALID_USER, + WOLFSSH_USERAUTH_INVALID_PASSWORD, + WOLFSSH_USERAUTH_INVALID_PUBLICKEY +}; + + enum WS_DisconnectReasonCodes { WOLFSSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT = 1, WOLFSSH_DISCONNECT_PROTOCOL_ERROR = 2, From 89d92bb886f2ca72ce43f6b2ce4122c93f9a046a Mon Sep 17 00:00:00 2001 From: John Safranek Date: Thu, 16 Jun 2016 15:50:11 -0700 Subject: [PATCH 4/5] refactor userauth --- README.md | 19 ++ examples/echoserver/echoserver.c | 2 - src/internal.c | 396 +++++++++++++++++++------------ wolfssh/internal.h | 3 + wolfssh/ssh.h | 3 + 5 files changed, 268 insertions(+), 155 deletions(-) diff --git a/README.md b/README.md index a62a1a54..b5176765 100644 --- a/README.md +++ b/README.md @@ -37,3 +37,22 @@ The server will send a canned banner to the client: Characters typed into the client will be echoed to the screen by the server. If the characters are echoed twice, the client has local echo enabled. + +testing notes +------------- + +Authentication against the example echoserver can be done with a password or +public key. To use a password the command line: + + $ ssh_client -p 22222 USER@localhost + +Where the `USER` and password pairs are: + + jill:upthehill + jack:fetchapail + +To use public key authentication use the command line: + + $ ssh_client -l ./certs/key-USER.pem -p 22222 USER@localhost + +Where the user can be `gretel` or `hansel`. diff --git a/examples/echoserver/echoserver.c b/examples/echoserver/echoserver.c index 736d5a12..cd32e920 100644 --- a/examples/echoserver/echoserver.c +++ b/examples/echoserver/echoserver.c @@ -483,8 +483,6 @@ static int LoadPublicKeyBuffer(uint8_t* buf, uint32_t bufSz, PwMapList* list) decodeResult = Base64_Decode(publicKey64, publicKey64Sz, publicKey, &publicKeySz); - printf("Base64_Decode = %d\n", decodeResult); - if (PwMapNew(list, WOLFSSH_USERAUTH_PUBLICKEY, username, usernameSz, publicKey, publicKeySz) == NULL ) { diff --git a/src/internal.c b/src/internal.c index 8d0983a9..0b4de403 100644 --- a/src/internal.c +++ b/src/internal.c @@ -1356,13 +1356,203 @@ static int GetString(char* s, uint32_t* sSz, } +/* Utility for DoUserAuthRequest() */ +static int DoUserAuthRequestPassword(WOLFSSH* ssh, WS_UserAuthData* authData, + uint8_t* buf, uint32_t len, uint32_t* idx) +{ + uint32_t begin = *idx; + WS_UserAuthData_Password* pw = &authData->sf.password; + int ret; + + authData->type = WOLFSSH_USERAUTH_PASSWORD; + ret = GetBoolean(&pw->hasNewPassword, buf, len, &begin); + ret = GetUint32(&pw->passwordSz, buf, len, &begin); + pw->password = buf + begin; + begin += pw->passwordSz; + + if (pw->hasNewPassword) { + /* Skip the password change. Maybe error out since we aren't + * supporting password changes at this time. */ + ret = GetUint32(&pw->newPasswordSz, buf, len, &begin); + pw->newPassword = buf + begin; + begin += pw->newPasswordSz; + } + else { + pw->newPassword = NULL; + pw->newPasswordSz = 0; + } + + if (ssh->ctx->userAuthCb != NULL) { + WLOG(WS_LOG_DEBUG, "DUAR: Checking the password"); + ret = ssh->ctx->userAuthCb(WOLFSSH_USERAUTH_PASSWORD, + authData, ssh->userAuthCtx); + if (ret == WS_SUCCESS) { + WLOG(WS_LOG_DEBUG, "DUAR: password check successful"); + ssh->clientState = CLIENT_USERAUTH_DONE; + } + else { + WLOG(WS_LOG_DEBUG, "DUAR: password check failed"); + SendUserAuthFailure(ssh, 0); + } + } + else { + WLOG(WS_LOG_DEBUG, "DUAR: No user auth callback"); + } + + *idx = begin; + return WS_SUCCESS; +} + + +/* Utility for DoUserAuthRequestPublicKey() */ +/* returns negative for error, positive is size of digest. */ +static int DoUserAuthRequestRsa(WOLFSSH* ssh, WS_UserAuthData_PublicKey* pk, + uint8_t* digest, uint32_t digestSz) +{ + RsaKey key; + uint8_t* n; + uint32_t nSz; + uint8_t* e; + uint32_t eSz; + uint32_t i = 0; + int ret; + + /* Skip the type string */ + GetUint32(&nSz, pk->publicKey, pk->publicKeySz, &i); + i += nSz; + + GetUint32(&eSz, pk->publicKey, pk->publicKeySz, &i); + e = pk->publicKey + i; + i += eSz; + GetUint32(&nSz, pk->publicKey, pk->publicKeySz, &i); + n = pk->publicKey + i; + + wc_InitRsaKey(&key, ssh->ctx->heap); + ret = wc_RsaPublicKeyDecodeRaw(n, nSz, e, eSz, &key); + + i = 0; + /* Skip the type string */ + GetUint32(&nSz, pk->signature, pk->signatureSz, &i); + i += nSz; + + GetUint32(&nSz, pk->signature, pk->signatureSz, &i); + n = pk->signature + i; + ret = wc_RsaSSL_Verify(n, nSz, digest, digestSz, &key); + wc_FreeRsaKey(&key); + + return ret; +} + + +/* Utility for DoUserAuthRequest() */ +static int DoUserAuthRequestPublicKey(WOLFSSH* ssh, WS_UserAuthData* authData, + uint8_t* buf, uint32_t len, uint32_t* idx) +{ + uint32_t begin = *idx; + WS_UserAuthData_PublicKey* pk = &authData->sf.publicKey; + int ret = WS_SUCCESS; + + authData->type = WOLFSSH_USERAUTH_PUBLICKEY; + GetBoolean(&pk->hasSignature, buf, len, &begin); + GetUint32(&pk->publicKeyTypeSz, buf, len, &begin); + pk->publicKeyType = buf + begin; + begin += pk->publicKeyTypeSz; + GetUint32(&pk->publicKeySz, buf, len, &begin); + pk->publicKey = buf + begin; + begin += pk->publicKeySz; + + if (pk->hasSignature) { + GetUint32(&pk->signatureSz, buf, len, &begin); + pk->signature = buf + begin; + begin += pk->signatureSz; + } + else { + pk->signature = NULL; + pk->signatureSz = 0; + } + + if (ssh->ctx->userAuthCb != NULL) { + ret = ssh->ctx->userAuthCb(WOLFSSH_USERAUTH_PUBLICKEY, + authData, ssh->userAuthCtx); + } + + if (pk->signature == NULL) { + WLOG(WS_LOG_DEBUG, "DUAR: Send the PK OK"); + ret = SendUserAuthPkOk(ssh, pk->publicKeyType, pk->publicKeyTypeSz, + pk->publicKey, pk->publicKeySz); + } + else { + uint8_t checkDigest[MAX_ENCODED_SIG_SZ]; + uint32_t checkDigestSz = sizeof(checkDigest); + uint8_t encDigest[MAX_ENCODED_SIG_SZ]; + uint32_t encDigestSz; + uint8_t pkTypeId; + + pkTypeId = NameToId((char*)pk->publicKeyType, pk->publicKeyTypeSz); + + if (pkTypeId == ID_SSH_RSA) + ret = DoUserAuthRequestRsa(ssh, pk, checkDigest, checkDigestSz); + else + ret = WS_INVALID_ALGO_ID; + + if (ret > 0) { + checkDigestSz = (uint32_t)ret; + ret = WS_SUCCESS; + } + + { + Sha sha; + uint8_t digest[SHA_DIGEST_SIZE]; + + wc_InitSha(&sha); + c32toa(ssh->sessionIdSz, digest); + wc_ShaUpdate(&sha, digest, UINT32_SZ); + wc_ShaUpdate(&sha, ssh->sessionId, ssh->sessionIdSz); + digest[0] = MSGID_USERAUTH_REQUEST; + wc_ShaUpdate(&sha, digest, MSG_ID_SZ); + + /* The rest of the fields in the signature are already + * in the buffer. Just need to account for the sizes. */ + wc_ShaUpdate(&sha, pk->dataToSign, + authData->usernameSz + authData->serviceNameSz + + authData->authNameSz + BOOLEAN_SZ + + pk->publicKeyTypeSz + pk->publicKeySz + + (UINT32_SZ * 5)); + wc_ShaFinal(&sha, digest); + + encDigestSz = wc_EncodeSignature(encDigest, digest, + SHA_DIGEST_SIZE, SHAh); + } + + { + volatile int compare; + volatile int sizeCompare; + + compare = ConstantCompare(encDigest, checkDigest, encDigestSz); + sizeCompare = encDigestSz != checkDigestSz; + + if (compare || sizeCompare || ret < 0) { + WLOG(WS_LOG_DEBUG, "signature compare failure"); + SendUserAuthFailure(ssh, 0); + } + else { + ssh->clientState = CLIENT_USERAUTH_DONE; + } + } + } + + *idx = begin; + + return ret; +} + + static int DoUserAuthRequest(WOLFSSH* ssh, uint8_t* buf, uint32_t len, uint32_t* idx) { uint32_t begin = *idx; int ret; uint8_t authNameId; - uint32_t authNameSz; WS_UserAuthData authData; GetUint32(&authData.usernameSz, buf, len, &begin); @@ -1373,164 +1563,25 @@ static int DoUserAuthRequest(WOLFSSH* ssh, authData.serviceName = buf + begin; begin += authData.serviceNameSz; - GetUint32(&authNameSz, buf, len, &begin); - authNameId = NameToId((const char*)(buf + begin), authNameSz); - begin += authNameSz; + GetUint32(&authData.authNameSz, buf, len, &begin); + authData.authName = buf + begin; + begin += authData.authNameSz; + authNameId = NameToId((char*)authData.authName, authData.authNameSz); - if (authNameId == ID_USERAUTH_PASSWORD) { - WS_UserAuthData_Password* pw = &authData.sf.password; - - authData.type = WOLFSSH_USERAUTH_PASSWORD; - ret = GetBoolean(&pw->hasNewPassword, buf, len, &begin); - ret = GetUint32(&pw->passwordSz, buf, len, &begin); - pw->password = buf + begin; - begin += pw->passwordSz; - - if (pw->hasNewPassword) { - /* Skip the password change. Maybe error out since we aren't - * supporting password changes at this time. */ - ret = GetUint32(&pw->newPasswordSz, buf, len, &begin); - pw->newPassword = buf + begin; - begin += pw->newPasswordSz; - } - else { - pw->newPassword = NULL; - pw->newPasswordSz = 0; - } - - if (ssh->ctx->userAuthCb != NULL) { - WLOG(WS_LOG_DEBUG, "DUAR: Checking the password"); - ret = ssh->ctx->userAuthCb(WOLFSSH_USERAUTH_PASSWORD, - &authData, ssh->userAuthCtx); - if (ret == WS_SUCCESS) { - WLOG(WS_LOG_DEBUG, "DUAR: password check successful"); - ssh->clientState = CLIENT_USERAUTH_DONE; - } - else { - WLOG(WS_LOG_DEBUG, "DUAR: password check failed"); - SendUserAuthFailure(ssh, 0); - } - } - else { - WLOG(WS_LOG_DEBUG, "DUAR: No user auth callback"); - } - } + if (authNameId == ID_USERAUTH_PASSWORD) + ret = DoUserAuthRequestPassword(ssh, &authData, buf, len, &begin); else if (authNameId == ID_USERAUTH_PUBLICKEY) { - WS_UserAuthData_PublicKey* pk = &authData.sf.publicKey; - - authData.type = WOLFSSH_USERAUTH_PUBLICKEY; - ret = GetBoolean(&pk->hasSignature, buf, len, &begin); - GetUint32(&pk->publicKeyTypeSz, buf, len, &begin); - pk->publicKeyType = buf + begin; - begin += pk->publicKeyTypeSz; - GetUint32(&pk->publicKeySz, buf, len, &begin); - pk->publicKey = buf + begin; - begin += pk->publicKeySz; - - if (pk->hasSignature) { - GetUint32(&pk->signatureSz, buf, len, &begin); - pk->signature = buf + begin; - begin += pk->signatureSz; - } - else { - pk->signature = NULL; - pk->signatureSz = 0; - } - - if (ssh->ctx->userAuthCb != NULL) { - ret = ssh->ctx->userAuthCb(WOLFSSH_USERAUTH_PUBLICKEY, - &authData, ssh->userAuthCtx); - } - - if (pk->signature == NULL) { - WLOG(WS_LOG_DEBUG, "DUAR: Send the PK OK!"); - } - else { - uint8_t checkDigest[MAX_ENCODED_SIG_SZ]; - uint32_t checkDigestSz = 0; - uint8_t encDigest[MAX_ENCODED_SIG_SZ]; - uint32_t encDigestSz; - - { - RsaKey key; - uint8_t* n; - uint32_t nSz; - uint8_t* e; - uint32_t eSz; - uint32_t i = 0; - - GetUint32(&nSz, pk->publicKey, pk->publicKeySz, &i); - i += nSz; - GetUint32(&eSz, pk->publicKey, pk->publicKeySz, &i); - e = pk->publicKey + i; - i += eSz; - GetUint32(&nSz, pk->publicKey, pk->publicKeySz, &i); - n = authData.sf.publicKey.publicKey + i; - - wc_InitRsaKey(&key, ssh->ctx->heap); - ret = wc_RsaPublicKeyDecodeRaw(n, nSz, e, eSz, &key); - - i = 0; - GetUint32(&nSz, pk->signature, pk->signatureSz, &i); - i += nSz; - GetUint32(&nSz, pk->signature, pk->signatureSz, &i); - n = pk->signature + i; - ret = wc_RsaSSL_Verify(n, nSz, checkDigest, - sizeof(checkDigest), &key); - wc_FreeRsaKey(&key); - } - - if (ret > 0) { - checkDigestSz = (uint32_t)ret; - ret = 0; - } - - { - Sha sha; - uint8_t digest[SHA_DIGEST_SIZE]; - - wc_InitSha(&sha); - c32toa(ssh->sessionIdSz, digest); - wc_ShaUpdate(&sha, digest, UINT32_SZ); - wc_ShaUpdate(&sha, ssh->sessionId, ssh->sessionIdSz); - digest[0] = MSGID_USERAUTH_REQUEST; - wc_ShaUpdate(&sha, digest, MSG_ID_SZ); - - /* The rest of the fields in the signature are already - * in the buffer. Just need to account for the sizes. */ - wc_ShaUpdate(&sha, buf + *idx, - authData.usernameSz + authData.serviceNameSz + - authNameSz + BOOLEAN_SZ + - authData.sf.publicKey.publicKeyTypeSz + - authData.sf.publicKey.publicKeySz + (UINT32_SZ * 5)); - ShaFinal(&sha, digest); - - encDigestSz = wc_EncodeSignature(encDigest, digest, - SHA_DIGEST_SIZE, SHAh); - } - - { - volatile int compare; - volatile int sizeCompare; - - compare = ConstantCompare(encDigest, checkDigest, encDigestSz); - sizeCompare = encDigestSz != checkDigestSz; - - if (compare || sizeCompare || ret < 0) { - SendUserAuthFailure(ssh, 0); - } - else { - ssh->clientState = CLIENT_USERAUTH_DONE; - } - } - } + authData.sf.publicKey.dataToSign = buf + *idx; + ret = DoUserAuthRequestPublicKey(ssh, &authData, buf, len, &begin); + } + else { + WLOG(WS_LOG_DEBUG, "invalid userauth type: %s", IdToName(authNameId)); + ret = SendUserAuthFailure(ssh, 0); } - else - SendUserAuthFailure(ssh, 0); *idx = begin; - return WS_SUCCESS; + return ret; } @@ -2833,6 +2884,45 @@ int SendUserAuthSuccess(WOLFSSH* ssh) } +int SendUserAuthPkOk(WOLFSSH* ssh, + const uint8_t* algoName, uint32_t algoNameSz, + const uint8_t* publicKey, uint32_t publicKeySz) +{ + uint8_t* output; + uint32_t idx; + int ret; + + if (ssh == NULL || + algoName == NULL || algoNameSz == 0 || + publicKey == NULL || publicKeySz == 0) { + + return WS_BAD_ARGUMENT; + } + + PreparePacket(ssh, MSG_ID_SZ + (LENGTH_SZ * 2) + algoNameSz + publicKeySz); + + output = ssh->outputBuffer.buffer; + idx = ssh->outputBuffer.length; + + output[idx++] = MSGID_USERAUTH_PK_OK; + c32toa(algoNameSz, output + idx); + idx += LENGTH_SZ; + WMEMCPY(output + idx, algoName, algoNameSz); + idx += algoNameSz; + c32toa(publicKeySz, output + idx); + idx += LENGTH_SZ; + WMEMCPY(output + idx, publicKey, publicKeySz); + idx += publicKeySz; + + ssh->outputBuffer.length = idx; + + BundlePacket(ssh); + ret = SendBuffered(ssh); + + return ret; +} + + static const char cannedBanner[] = "CANNED BANNER\r\n" "This server is an example test server. " diff --git a/wolfssh/internal.h b/wolfssh/internal.h index 39271f65..e1c03497 100644 --- a/wolfssh/internal.h +++ b/wolfssh/internal.h @@ -280,6 +280,8 @@ WOLFSSH_LOCAL int SendServiceAccept(WOLFSSH*); WOLFSSH_LOCAL int SendUserAuthSuccess(WOLFSSH*); WOLFSSH_LOCAL int SendUserAuthFailure(WOLFSSH*, uint8_t); WOLFSSH_LOCAL int SendUserAuthBanner(WOLFSSH*); +WOLFSSH_LOCAL int SendUserAuthPkOk(WOLFSSH*, const uint8_t*, uint32_t, + const uint8_t*, uint32_t); WOLFSSH_LOCAL int SendChannelOpenConf(WOLFSSH* ssh); WOLFSSH_LOCAL int SendChannelData(WOLFSSH* ssh, uint32_t, uint8_t*, uint32_t); WOLFSSH_LOCAL int GenerateKey(uint8_t, uint8_t, uint8_t*, uint32_t, @@ -344,6 +346,7 @@ enum WS_MessageIds { MSGID_USERAUTH_FAILURE = 51, MSGID_USERAUTH_SUCCESS = 52, MSGID_USERAUTH_BANNER = 53, + MSGID_USERAUTH_PK_OK = 60, MSGID_CHANNEL_OPEN = 90, MSGID_CHANNEL_OPEN_CONF = 91, diff --git a/wolfssh/ssh.h b/wolfssh/ssh.h index 0bb05416..27ddcbd2 100644 --- a/wolfssh/ssh.h +++ b/wolfssh/ssh.h @@ -85,6 +85,7 @@ typedef struct WS_UserAuthData_Password { } WS_UserAuthData_Password; typedef struct WS_UserAuthData_PublicKey { + uint8_t* dataToSign; uint8_t* publicKeyType; uint32_t publicKeyTypeSz; uint8_t* publicKey; @@ -100,6 +101,8 @@ typedef struct WS_UserAuthData { uint32_t usernameSz; uint8_t* serviceName; uint32_t serviceNameSz; + uint8_t* authName; + uint32_t authNameSz; union { WS_UserAuthData_Password password; WS_UserAuthData_PublicKey publicKey; From 15023f54b68c44834fd925bd6437a58912cf54c7 Mon Sep 17 00:00:00 2001 From: John Safranek Date: Fri, 17 Jun 2016 15:54:24 -0700 Subject: [PATCH 5/5] verify the correct public key type and signature type during auth --- src/internal.c | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/src/internal.c b/src/internal.c index 0b4de403..32f54931 100644 --- a/src/internal.c +++ b/src/internal.c @@ -1410,6 +1410,8 @@ static int DoUserAuthRequestRsa(WOLFSSH* ssh, WS_UserAuthData_PublicKey* pk, uint8_t* digest, uint32_t digestSz) { RsaKey key; + uint8_t* publicKeyType; + uint32_t publicKeyTypeSz; uint8_t* n; uint32_t nSz; uint8_t* e; @@ -1417,10 +1419,17 @@ static int DoUserAuthRequestRsa(WOLFSSH* ssh, WS_UserAuthData_PublicKey* pk, uint32_t i = 0; int ret; - /* Skip the type string */ - GetUint32(&nSz, pk->publicKey, pk->publicKeySz, &i); - i += nSz; + /* First check that the public key's type matches the one we are + * expecting. */ + GetUint32(&publicKeyTypeSz, pk->publicKey, pk->publicKeySz, &i); + publicKeyType = pk->publicKey + i; + i += publicKeyTypeSz; + if (publicKeyTypeSz != pk->publicKeyTypeSz && + WMEMCMP(publicKeyType, pk->publicKeyType, publicKeyTypeSz) != 0) { + WLOG(WS_LOG_DEBUG, "Public Key's type does not match public key type"); + return WS_INVALID_ALGO_ID; + } GetUint32(&eSz, pk->publicKey, pk->publicKeySz, &i); e = pk->publicKey + i; i += eSz; @@ -1431,9 +1440,17 @@ static int DoUserAuthRequestRsa(WOLFSSH* ssh, WS_UserAuthData_PublicKey* pk, ret = wc_RsaPublicKeyDecodeRaw(n, nSz, e, eSz, &key); i = 0; - /* Skip the type string */ - GetUint32(&nSz, pk->signature, pk->signatureSz, &i); - i += nSz; + /* First check that the signature's public key type matches the one + * we are expecting. */ + GetUint32(&publicKeyTypeSz, pk->publicKey, pk->publicKeySz, &i); + publicKeyType = pk->publicKey + i; + i += publicKeyTypeSz; + if (publicKeyTypeSz != pk->publicKeyTypeSz && + WMEMCMP(publicKeyType, pk->publicKeyType, publicKeyTypeSz) != 0) { + + WLOG(WS_LOG_DEBUG, "Signature's type does not match public key type"); + return WS_INVALID_ALGO_ID; + } GetUint32(&nSz, pk->signature, pk->signatureSz, &i); n = pk->signature + i;