diff --git a/data/fftp.conf b/data/fftp.conf index e03b730..2c81a90 100644 --- a/data/fftp.conf +++ b/data/fftp.conf @@ -23,7 +23,7 @@ interface=0.0.0.0 # This will help clients to establish data connections # default: 0.0.0.0 -external_ip=123.45.67.89 +external_ip=0.0.0.0 # IP mask for local network # This will help the server to distinguish between local and Internet clients @@ -85,6 +85,7 @@ accs=upload root=/home/user/ftpshare [webadmin] -pswd=VeryStrongadminpassword222 +# Generate this value with: fftp -p +hashpswd=pbkdf2-sha256$200000$REPLACE_WITH_GENERATED_SALT$REPLACE_WITH_GENERATED_HASH accs=admin root=/home/user/ftpshare diff --git a/src/fcrypt.c b/src/fcrypt.c index 59e357a..04de3b2 100644 --- a/src/fcrypt.c +++ b/src/fcrypt.c @@ -3,276 +3,238 @@ * * Created on: Jun 12, 2026 * - * Modified on: Jun 12, 2026 + * Modified on: Sep 19, 2026 * * Author: lightftp */ +#include +#include +#include +#include + +#include +#include + #include "inc/fcrypt.h" -/* SHA256 constants */ -static const uint32_t k[64] = { - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, - 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, - 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, - 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, - 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, - 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 -}; - -/* Rotate right */ -#define ROTR(x, n) (((x) >> (n)) | ((x) << (32 - (n)))) - -/* SHA256 functions */ -#define CH(x, y, z) (((x) & (y)) ^ (~(x) & (z))) -#define MAJ(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) -#define EP0(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22)) -#define EP1(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25)) -#define SIG0(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ ((x) >> 3)) -#define SIG1(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ ((x) >> 10)) - -void sha256_transform(SHA256_CTX *ctx, const uint8_t data[]) { - uint32_t a, b, c, d, e, f, g, h, i, j, t1, t2, m[64]; - - for (i = 0, j = 0; i < 16; ++i, j += 4) { - m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]); - } - for (; i < 64; ++i) { - m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16]; - } +#define FTP_PASSWORD_SCHEME "pbkdf2-sha256" +#define FTP_PASSWORD_SCHEME_SEPARATOR '$' +#define FTP_PASSWORD_BASE64_SIZE 45 - a = ctx->state[0]; - b = ctx->state[1]; - c = ctx->state[2]; - d = ctx->state[3]; - e = ctx->state[4]; - f = ctx->state[5]; - g = ctx->state[6]; - h = ctx->state[7]; - - for (i = 0; i < 64; ++i) { - t1 = h + EP1(e) + CH(e, f, g) + k[i] + m[i]; - t2 = EP0(a) + MAJ(a, b, c); - h = g; - g = f; - f = e; - e = d + t1; - d = c; - c = b; - b = a; - a = t1 + t2; - } +static int password_derive_key(const char *password, const uint8_t *salt, + unsigned int iterations, uint8_t *hash) +{ + gnutls_datum_t password_data; + gnutls_datum_t salt_data; + int result; - ctx->state[0] += a; - ctx->state[1] += b; - ctx->state[2] += c; - ctx->state[3] += d; - ctx->state[4] += e; - ctx->state[5] += f; - ctx->state[6] += g; - ctx->state[7] += h; -} + if ((password == NULL) || (salt == NULL) || (hash == NULL) || (iterations == 0)) + return 0; -void sha256_init(SHA256_CTX *ctx) { - memset(ctx, 0, sizeof(*ctx)); - ctx->state[0] = 0x6a09e667; - ctx->state[1] = 0xbb67ae85; - ctx->state[2] = 0x3c6ef372; - ctx->state[3] = 0xa54ff53a; - ctx->state[4] = 0x510e527f; - ctx->state[5] = 0x9b05688c; - ctx->state[6] = 0x1f83d9ab; - ctx->state[7] = 0x5be0cd19; -} + password_data.data = (unsigned char *)password; + password_data.size = strlen(password); -void sha256_update(SHA256_CTX *ctx, const uint8_t data[], size_t len) { - uint32_t i; + salt_data.data = (unsigned char *)salt; + salt_data.size = FTP_PASSWORD_SALT_SIZE; - for (i = 0; i < len; ++i) { - ctx->data[ctx->datalen] = data[i]; - ctx->datalen++; - if (ctx->datalen == 64) { - sha256_transform(ctx, ctx->data); - ctx->bitlen += 512; - ctx->datalen = 0; - } - } + result = gnutls_pbkdf2(GNUTLS_MAC_SHA256, &password_data, &salt_data, + iterations, hash, FTP_PASSWORD_HASH_SIZE); + + return (result >= 0); } -void sha256_final(SHA256_CTX *ctx, uint8_t hash[]) { - uint32_t i; +static int password_base64_encode(const uint8_t *data, size_t data_size, + char *result, size_t result_size) +{ + gnutls_datum_t data_datum; + gnutls_datum_t encoded = {0}; + int status = 0; + int ret; - i = ctx->datalen; + if ((data == NULL) || (result == NULL) || (result_size == 0)) + return 0; - // Pad with 0x80 followed by zeros - if (ctx->datalen < 56) { - ctx->data[i++] = 0x80; - while (i < 56) { - ctx->data[i++] = 0x00; - } - } else { - ctx->data[i++] = 0x80; - while (i < 64) { - ctx->data[i++] = 0x00; - } - sha256_transform(ctx, ctx->data); - memset(ctx->data, 0, 56); - } + data_datum.data = (unsigned char *)data; + data_datum.size = data_size; - // Append original length in bits as 64-bit big-endian - ctx->bitlen += (uint64_t)ctx->datalen * 8; - ctx->data[63] = ctx->bitlen; - ctx->data[62] = ctx->bitlen >> 8; - ctx->data[61] = ctx->bitlen >> 16; - ctx->data[60] = ctx->bitlen >> 24; - ctx->data[59] = ctx->bitlen >> 32; - ctx->data[58] = ctx->bitlen >> 40; - ctx->data[57] = ctx->bitlen >> 48; - ctx->data[56] = ctx->bitlen >> 56; - sha256_transform(ctx, ctx->data); - - // Convert hash to bytes (big-endian) - for (i = 0; i < 4; ++i) { - hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff; - hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff; - hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff; - hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff; - hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff; - hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff; - hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff; - hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff; + ret = gnutls_base64_encode2(&data_datum, &encoded); + if ((ret >= 0) && (encoded.size < result_size)) + { + memcpy(result, encoded.data, encoded.size); + result[encoded.size] = 0; + status = 1; } + + if (encoded.data != NULL) + gnutls_free(encoded.data); + + return status; } -size_t base64decode(const char *b64, uint8_t *data, size_t data_size, size_t *cbfeed) +static int password_base64_decode(const char *data, size_t data_size, + uint8_t *result, size_t result_size) { - unsigned char b, c; - size_t decoded_size = 0; - const char *p0 = b64; - - static const unsigned char lookup_table[256] = { - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f, - 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, - 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, - 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff - }; - - if ((b64 == NULL) || (data == NULL) || (data_size == 0)) - goto done_decode; - - while (b64) + gnutls_datum_t data_datum; + gnutls_datum_t decoded = {0}; + int status = 0; + int ret; + + if ((data == NULL) || (result == NULL) || (data_size == 0)) + return 0; + + data_datum.data = (unsigned char *)data; + data_datum.size = data_size; + + ret = gnutls_base64_decode2(&data_datum, &decoded); + if ((ret >= 0) && (decoded.size == result_size)) { - do { - b = *b64++; - if ((b == 0) || (b == '=')) - goto done_decode; - b = lookup_table[b]; - } while (b == 0xff); - - do { - c = *b64++; - if ((c == 0) || (c == '=')) - goto done_decode; - c = lookup_table[c]; - } while (c == 0xff); - - *data++ = (b << 2) | (c >> 4); - ++decoded_size; - if (decoded_size >= data_size) - goto done_decode; - - do { - b = *b64++; - if ((b == 0) || (b == '=')) - goto done_decode; - b = lookup_table[b]; - } while (b == 0xff); - - *data++ = (c << 4) | (b >> 2); - ++decoded_size; - if (decoded_size >= data_size) - goto done_decode; - - do { - c = *b64++; - if ((c == 0) || (c == '=')) - goto done_decode; - c = lookup_table[c]; - } while (c == 0xff); - - *data++ = c | (b << 6); - ++decoded_size; - if (decoded_size >= data_size) - goto done_decode; + memcpy(result, decoded.data, result_size); + status = 1; } -done_decode: - if (cbfeed) + if (decoded.data != NULL) + gnutls_free(decoded.data); + + return status; +} + +static int password_parse_iterations(const char *text, size_t text_size, + unsigned int *iterations) +{ + char value[16]; + char *end; + unsigned long parsed; + + if ((text == NULL) || (iterations == NULL) || + (text_size == 0) || (text_size >= sizeof(value))) { - *cbfeed = b64 - p0; + return 0; } - return decoded_size; + + memcpy(value, text, text_size); + value[text_size] = 0; + + errno = 0; + parsed = strtoul(value, &end, 10); + if ((errno != 0) || (*end != 0) || (parsed == 0) || (parsed > UINT_MAX)) + return 0; + + *iterations = (unsigned int)parsed; + return 1; } -size_t base64encode(const uint8_t *s, size_t s_size, char *b64, size_t b64_size) +int password_generate_hash_record(const char *password, char *record, size_t record_size) { - static const char alpha64[] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - uint8_t b0, b1, b2; - size_t c = 0; + char salt_base64[FTP_PASSWORD_BASE64_SIZE]; + char hash_base64[FTP_PASSWORD_BASE64_SIZE]; + uint8_t salt[FTP_PASSWORD_SALT_SIZE]; + uint8_t hash[FTP_PASSWORD_HASH_SIZE]; + int length; + int status = 0; + + if ((password == NULL) || (record == NULL) || (record_size == 0)) + return 0; + + do { + + if (gnutls_rnd(GNUTLS_RND_KEY, salt, sizeof(salt)) < 0) + break; + + if (!password_derive_key(password, salt, FTP_PASSWORD_PBKDF2_ITERATIONS, hash)) + break; + + if (!password_base64_encode(salt, sizeof(salt), salt_base64, sizeof(salt_base64))) + break; - if ((s == NULL) || (b64 == NULL) || (b64_size == 0)) + if (!password_base64_encode(hash, sizeof(hash), hash_base64, sizeof(hash_base64))) + break; + + length = snprintf(record, record_size, "%s$%u$%s$%s", + FTP_PASSWORD_SCHEME, FTP_PASSWORD_PBKDF2_ITERATIONS, + salt_base64, hash_base64); + + if ((length >= 0) && ((size_t)length < record_size)) + status = 1; + + } while (0); + + // Cleanup. + gnutls_memset(hash, 0, sizeof(hash)); + gnutls_memset(salt, 0, sizeof(salt)); + return status; +} + +int password_verify_hash_record(const char *record, const char *password) +{ + const char *field_begin; + const char *field_end; + const char *salt_base64; + const char *hash_base64; + size_t field_size; + size_t salt_base64_size; + size_t hash_base64_size; + unsigned int iterations; + uint8_t salt[FTP_PASSWORD_SALT_SIZE]; + uint8_t expected_hash[FTP_PASSWORD_HASH_SIZE]; + uint8_t actual_hash[FTP_PASSWORD_HASH_SIZE]; + int status = 0; + + if ((record == NULL) || (password == NULL)) return 0; - /* Always leave room for terminator */ - b64[0] = '\0'; + do { + + field_begin = record; + field_end = strchr(field_begin, FTP_PASSWORD_SCHEME_SEPARATOR); + if ((field_end == NULL) || + ((size_t)(field_end - field_begin) != strlen(FTP_PASSWORD_SCHEME)) || + (memcmp(field_begin, FTP_PASSWORD_SCHEME, strlen(FTP_PASSWORD_SCHEME)) != 0)) + { + break; + } + + field_begin = field_end + 1; + field_end = strchr(field_begin, FTP_PASSWORD_SCHEME_SEPARATOR); + if (field_end == NULL) + break; - while (s_size > 0) { + field_size = (size_t)(field_end - field_begin); + if (!password_parse_iterations(field_begin, field_size, &iterations)) + break; - /* Need up to 4 output bytes plus trailing '\0' */ - if ((b64_size - c) <= 4) + salt_base64 = field_end + 1; + field_end = strchr(salt_base64, FTP_PASSWORD_SCHEME_SEPARATOR); + if (field_end == NULL) break; - b0 = (unsigned char)*s++; - --s_size; - b64[c++] = alpha64[b0 >> 2]; - - if (s_size > 0) { - b1 = (unsigned char)*s++; - --s_size; - b64[c++] = alpha64[((b0 & 0x03) << 4) | (b1 >> 4)]; - - if (s_size > 0) { - b2 = (unsigned char)*s++; - --s_size; - b64[c++] = alpha64[((b1 & 0x0f) << 2) | (b2 >> 6)]; - b64[c++] = alpha64[b2 & 0x3f]; - } else { - b64[c++] = alpha64[(b1 & 0x0f) << 2]; - b64[c++] = '='; - } - } else { - b64[c++] = alpha64[(b0 & 0x03) << 4]; - b64[c++] = '='; - b64[c++] = '='; + salt_base64_size = (size_t)(field_end - salt_base64); + hash_base64 = field_end + 1; + hash_base64_size = strlen(hash_base64); + + if (!password_base64_decode(salt_base64, salt_base64_size, + salt, sizeof(salt))) + { + break; } - } - b64[c] = '\0'; - return c; -} + if (!password_base64_decode(hash_base64, hash_base64_size, + expected_hash, sizeof(expected_hash))) + { + break; + } + + if (!password_derive_key(password, salt, iterations, actual_hash)) + break; + + if (gnutls_memcmp(expected_hash, actual_hash, sizeof(actual_hash)) == 0) + status = 1; + } while (0); + + // Cleanup. + gnutls_memset(actual_hash, 0, sizeof(actual_hash)); + gnutls_memset(expected_hash, 0, sizeof(expected_hash)); + gnutls_memset(salt, 0, sizeof(salt)); + return status; +} diff --git a/src/ftpserv.c b/src/ftpserv.c index 52515d0..8bb98bc 100644 --- a/src/ftpserv.c +++ b/src/ftpserv.c @@ -3,7 +3,7 @@ * * Created on: Aug 20, 2016 * - * Modified on: Jul 02, 2026 + * Modified on: Sep 19, 2026 * * Author: lightftp */ @@ -1124,10 +1124,8 @@ ssize_t ftpPASV(pftp_context context, const char *params) ssize_t ftpPASS(pftp_context context, const char *userpass) { - char temptext[PATH_MAX], b64text[64]; + char temptext[PATH_MAX]; int pswd_verified = 0; - SHA256_CTX shactx; - uint8_t salt[32], hash[32], phash[32]; if ( userpass == NULL ) return sendstring(context, error501); @@ -1140,19 +1138,7 @@ ssize_t ftpPASS(pftp_context context, const char *userpass) */ if (config_parse(g_cfg.config_file, context->user_name, "hashpswd", temptext, sizeof(temptext))) { - memcpy(&b64text, &temptext, 44); - b64text[44] = 0; - /* salt */ - base64decode((const char *)b64text, (uint8_t *)&salt, sizeof(salt), NULL); - /* hash */ - base64decode(&temptext[44], (uint8_t *)&phash, sizeof(phash), NULL); - - sha256_init(&shactx); - sha256_update(&shactx, (uint8_t *)&salt, sizeof(salt)); - sha256_update(&shactx, (uint8_t *)userpass, strlen(userpass)); - sha256_final(&shactx, (uint8_t *)&hash); - - if (memcmp(&phash, &hash, sizeof(hash)) == 0) + if (password_verify_hash_record(temptext, userpass)) pswd_verified = 1; } else diff --git a/src/inc/fcrypt.h b/src/inc/fcrypt.h index ce288cf..fc012b1 100644 --- a/src/inc/fcrypt.h +++ b/src/inc/fcrypt.h @@ -3,7 +3,7 @@ * * Created on: Jun 12, 2026 * - * Modified on: Jun 12, 2026 + * Modified on: Sep 19, 2026 * * Author: lightftp */ @@ -11,22 +11,16 @@ #ifndef FCRYPT_H_ #define FCRYPT_H_ 1 +#include #include -#include -#include +#include -typedef struct _SHA256_CTX { - uint8_t data[64]; - uint32_t datalen; - uint64_t bitlen; - uint32_t state[8]; -} SHA256_CTX, *PSHA256_CTX; +#define FTP_PASSWORD_SALT_SIZE 32 +#define FTP_PASSWORD_HASH_SIZE 32 +#define FTP_PASSWORD_PBKDF2_ITERATIONS 200000U +#define FTP_PASSWORD_RECORD_SIZE 128 -void sha256_init(SHA256_CTX *ctx); -void sha256_update(SHA256_CTX *ctx, const uint8_t data[], size_t len); -void sha256_final(SHA256_CTX *ctx, uint8_t hash[]); - -size_t base64encode(const uint8_t *s, size_t s_size, char *b64, size_t b64_size); -size_t base64decode(const char *b64, uint8_t *data, size_t data_size, size_t *cbfeed); +int password_generate_hash_record(const char *password, char *record, size_t record_size); +int password_verify_hash_record(const char *record, const char *password); #endif /* FCRYPT_H_ */ diff --git a/src/main.c b/src/main.c index 6b2c720..91e8c46 100644 --- a/src/main.c +++ b/src/main.c @@ -3,7 +3,7 @@ * * Created on: Aug 20, 2016 * - * Modified on: Jun 12, 2026 + * Modified on: Sep 19, 2026 * * Author: lightftp */ @@ -13,25 +13,27 @@ #include "inc/x_malloc.h" #include "inc/fcrypt.h" -ftp_config g_cfg; -int g_log = -1; +ftp_config g_cfg; +int g_log = -1; -static char CAFILE[PATH_MAX], CERTFILE[PATH_MAX], KEYFILE[PATH_MAX], KEYFILE_PASS[256]; -char GOODBYE_MSG[MSG_MAXLEN]; +static char CAFILE[PATH_MAX], CERTFILE[PATH_MAX], KEYFILE[PATH_MAX], KEYFILE_PASS[256]; +char GOODBYE_MSG[MSG_MAXLEN]; -gnutls_dh_params_t dh_params = NULL; -gnutls_certificate_credentials_t x509_cred = NULL; -gnutls_priority_t priority_cache = NULL; -gnutls_datum_t session_keys_storage = {0}; +gnutls_dh_params_t dh_params = NULL; +gnutls_certificate_credentials_t x509_cred = NULL; +gnutls_priority_t priority_cache = NULL; +gnutls_datum_t session_keys_storage = {0}; -void ftp_tls_init(); +static int gnutls_initialized = 0; + +int ftp_tls_init(); void ftp_tls_cleanup(); static int read_password_stars(char *buffer, size_t buffer_size) { - struct termios oldt, newt; - int ch; - size_t pos = 0; + struct termios oldt, newt; + int ch; + size_t pos = 0; if ((buffer == NULL) || (buffer_size < 2)) return 0; @@ -83,32 +85,18 @@ static int read_password_stars(char *buffer, size_t buffer_size) return 1; } -size_t get_salt(uint8_t *salt, size_t salt_size) -{ - int file_fd, result; - - file_fd = open("/dev/urandom", O_RDONLY); - if (file_fd == -1) - return 0; - result = read(file_fd, salt, salt_size); - close(file_fd); - return result; -} - /* Program entry point */ int main(int argc, char *argv[]) { - char *cfg = NULL, *textbuf = NULL, - *p, userpass[256], base64out[256]; - int c, i, use_cli_password = 0; - uint32_t bufsize = 65536; - pthread_t thid; - SHA256_CTX shactx; - uint8_t salt[32], hash[32]; - + char *cfg = NULL, *textbuf = NULL, + *p, userpass[256], password_record[FTP_PASSWORD_RECORD_SIZE]; + int c, i, use_cli_password = 0; + int crypto_initialized = 0; + uint32_t bufsize = 65536; + pthread_t thid; struct in_addr na; - if (sizeof (off_t) != 8) + if (sizeof(off_t) != 8) { printf("off_t is not 64 bits long"); exit(1); @@ -129,6 +117,8 @@ int main(int argc, char *argv[]) if (use_cli_password != 0) { memset(userpass, 0, sizeof(userpass)); + memset(password_record, 0, sizeof(password_record)); + printf("Enter key password: "); if (!read_password_stars(userpass, sizeof(userpass))) { @@ -136,22 +126,31 @@ int main(int argc, char *argv[]) exit(1); } - if (get_salt((uint8_t *)&salt, sizeof(salt)) < sizeof(salt)) + if (gnutls_global_init() < 0) { - printf("Error: Failed to get random salt value\r\n"); + gnutls_memset(userpass, 0, sizeof(userpass)); + printf("Error: Failed to initialize GnuTLS\r\n"); exit(1); } - sha256_init(&shactx); - sha256_update(&shactx, (uint8_t *)&salt, sizeof(salt)); - sha256_update(&shactx, (uint8_t *)&userpass, strlen(userpass)); - sha256_final(&shactx, (uint8_t *)&hash); + crypto_initialized = 1; - c = base64encode((uint8_t *)&salt, sizeof(salt), (char *)&base64out, sizeof(base64out)); - base64encode((uint8_t *)&hash, sizeof(hash), (char *)&base64out[c], sizeof(base64out)-c); + if (!password_generate_hash_record(userpass, password_record, + sizeof(password_record))) + { + gnutls_memset(userpass, 0, sizeof(userpass)); + gnutls_global_deinit(); + printf("Error: Failed to generate encrypted password\r\n"); + exit(1); + } - printf("%s\r\n", base64out); - exit(1); + gnutls_memset(userpass, 0, sizeof(userpass)); + printf("%s\r\n", password_record); + + if (crypto_initialized != 0) + gnutls_global_deinit(); + + exit(0); } if (cfg == NULL) @@ -189,10 +188,10 @@ int main(int argc, char *argv[]) g_cfg.file_open_flags = O_NOFOLLOW; if (config_parse(cfg, CONFIG_SECTION_NAME, "follow_symlinks", textbuf, bufsize)) - { - if (strtoul(textbuf, NULL, 10) != 0) - g_cfg.file_open_flags &= ~O_NOFOLLOW; - } + { + if (strtoul(textbuf, NULL, 10) != 0) + g_cfg.file_open_flags &= ~O_NOFOLLOW; + } g_cfg.pasv_port_base = 1024; if (config_parse(cfg, CONFIG_SECTION_NAME, "minport", textbuf, bufsize)) @@ -218,18 +217,24 @@ int main(int argc, char *argv[]) printf("Possible errors: 1) path is invalid; 2) file is read only; 3) file is directory; 4) insufficient permissions\r\n"); break; } - - } else + } + else printf("WARNING: logfilepath section is not found in configuration. Logging to file disabled.\r\n"); - if (g_log != -1) - lseek(g_log, 0L, SEEK_END); + if (g_log != -1) + lseek(g_log, 0L, SEEK_END); + + if (!ftp_tls_init()) + { + printf("Error: TLS initialization failed. Server startup aborted.\r\n"); + break; + } printf("\r\n [ LightFTP server v%s ]\r\n\r\n", FTP_VERSION); printf("Log file : %s\r\n", textbuf); p = getcwd(textbuf, bufsize); - if (p != NULL ) + if (p != NULL) printf("Working dir : %s\r\n", textbuf); if (argc > 1) @@ -251,8 +256,6 @@ int main(int argc, char *argv[]) printf("\r\n Use with -p to generate encrypted password\r\n"); printf("\r\n TYPE q or Ctrl+C to terminate >\r\n"); - ftp_tls_init(); - thid = (pthread_t)0; if (pthread_create(&thid, NULL, &ftpmain, NULL) != 0) { @@ -260,7 +263,8 @@ int main(int argc, char *argv[]) break; } - do { + do + { c = getc(stdin); sleep(1); } while ((c != 'q') && (c != 'Q')); @@ -271,69 +275,115 @@ int main(int argc, char *argv[]) memset(KEYFILE_PASS, 0, sizeof(KEYFILE_PASS)); if (cfg == NULL) - printf("Could not find configuration file\r\n\r\n Usage: fftp [CONFIGFILE] [-p]\r\n\r\n"); - else - free(cfg); + printf("Could not find configuration file\r\n\r\n Usage: fftp [CONFIGFILE] [-p]\r\n\r\n"); + else + free(cfg); - if (g_log != -1) - close(g_log); + if (g_log != -1) + close(g_log); - if (textbuf != NULL) - free(textbuf); + if (textbuf != NULL) + free(textbuf); - ftp_tls_cleanup(); + ftp_tls_cleanup(); exit(0); } -void ftp_tls_init() +int ftp_tls_init() { - while (gnutls_global_init() >= 0) + int result = GNUTLS_E_SUCCESS; + + do { - if (gnutls_certificate_allocate_credentials(&x509_cred) < 0) - break; - if (gnutls_certificate_set_x509_trust_file(x509_cred, CAFILE, GNUTLS_X509_FMT_PEM) < 0) - break; + result = gnutls_global_init(); + if (result < 0) + break; - if (gnutls_certificate_set_x509_key_file2(x509_cred, CERTFILE, KEYFILE, GNUTLS_X509_FMT_PEM, KEYFILE_PASS, 0) < 0) - break; + gnutls_initialized = 1; - if (gnutls_priority_init(&priority_cache, NULL, NULL) < 0) - break; + result = gnutls_certificate_allocate_credentials(&x509_cred); + if (result < 0) + break; - if (gnutls_session_ticket_key_generate(&session_keys_storage) != GNUTLS_E_SUCCESS) - break; + result = gnutls_certificate_set_x509_trust_file(x509_cred, CAFILE, + GNUTLS_X509_FMT_PEM); + if (result < 0) + break; + + result = gnutls_certificate_set_x509_key_file2(x509_cred, CERTFILE, KEYFILE, + GNUTLS_X509_FMT_PEM, KEYFILE_PASS, 0); + if (result < 0) + break; + + result = gnutls_priority_init(&priority_cache, NULL, NULL); + if (result < 0) + break; + + result = gnutls_session_ticket_key_generate(&session_keys_storage); + if (result < 0) + break; #if GNUTLS_VERSION_NUMBER >= 0x030506 - gnutls_certificate_set_known_dh_params(x509_cred, GNUTLS_SEC_PARAM_HIGH); + gnutls_certificate_set_known_dh_params(x509_cred, GNUTLS_SEC_PARAM_HIGH); #else - gnutls_dh_params_init(&dh_params); - gnutls_dh_params_generate2(dh_params, gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, GNUTLS_SEC_PARAM_HIGH)); - gnutls_certificate_set_dh_params(x509_cred, dh_params); + result = gnutls_dh_params_init(&dh_params); + if (result < 0) + break; + + result = gnutls_dh_params_generate2(dh_params, + gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, GNUTLS_SEC_PARAM_HIGH)); + if (result < 0) + break; + + gnutls_certificate_set_dh_params(x509_cred, dh_params); #endif - break; - } + } while (0); + + if (result < 0) + { + printf("GnuTLS initialization error: %s\r\n", gnutls_strerror(result)); + ftp_tls_cleanup(); + return 0; + } + + return 1; } void ftp_tls_cleanup() { #if GNUTLS_VERSION_NUMBER < 0x030506 - if ( dh_params != NULL) + if (dh_params != NULL) + { gnutls_dh_params_deinit(dh_params); + dh_params = NULL; + } #endif - if ( x509_cred != NULL ) + if (x509_cred != NULL) + { gnutls_certificate_free_credentials(x509_cred); + x509_cred = NULL; + } - if ( priority_cache != NULL ) + if (priority_cache != NULL) + { gnutls_priority_deinit(priority_cache); + priority_cache = NULL; + } if (session_keys_storage.data) { - gnutls_memset(session_keys_storage.data, 0, session_keys_storage.size); - gnutls_free(session_keys_storage.data); + gnutls_memset(session_keys_storage.data, 0, session_keys_storage.size); + gnutls_free(session_keys_storage.data); + session_keys_storage.data = NULL; + session_keys_storage.size = 0; } - gnutls_global_deinit(); + if (gnutls_initialized != 0) + { + gnutls_global_deinit(); + gnutls_initialized = 0; + } }