X-Git-Url: http://git.silcnet.org/gitweb/?a=blobdiff_plain;f=lib%2Fsilccore%2Fsilcauth.c;h=f2c73f0ece9fad49a5081d464595d561760068db;hb=1ea936cbf1bb3b19bd55839b904ef59ada84b8b5;hp=e38eff0b3b5171e918271e8fbb8c5963fc083d40;hpb=b3e67d3dfa6409755be33f352b5a86fbb094a570;p=silc.git diff --git a/lib/silccore/silcauth.c b/lib/silccore/silcauth.c index e38eff0b..f2c73f0e 100644 --- a/lib/silccore/silcauth.c +++ b/lib/silccore/silcauth.c @@ -2,15 +2,14 @@ silcauth.c - Author: Pekka Riikonen + Author: Pekka Riikonen - Copyright (C) 2001 Pekka Riikonen + Copyright (C) 2001 - 2007 Pekka Riikonen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - + the Free Software Foundation; version 2 of the License. + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -19,7 +18,7 @@ */ /* $Id$ */ -#include "silcincludes.h" +#include "silc.h" #include "silcauth.h" /****************************************************************************** @@ -30,17 +29,19 @@ /* Authentication Payload structure */ struct SilcAuthPayloadStruct { + SilcStack stack; + unsigned char *random_data; + unsigned char *auth_data; + SilcUInt16 auth_len; SilcUInt16 len; SilcUInt16 auth_method; SilcUInt16 random_len; - unsigned char *random_data; - SilcUInt16 auth_len; - unsigned char *auth_data; }; /* Parses and returns Authentication Payload */ -SilcAuthPayload silc_auth_payload_parse(const unsigned char *data, +SilcAuthPayload silc_auth_payload_parse(SilcStack stack, + const unsigned char *data, SilcUInt32 data_len) { SilcBufferStruct buffer; @@ -50,25 +51,40 @@ SilcAuthPayload silc_auth_payload_parse(const unsigned char *data, SILC_LOG_DEBUG(("Parsing Authentication Payload")); silc_buffer_set(&buffer, (unsigned char *)data, data_len); - newp = silc_calloc(1, sizeof(*newp)); - if (!newp) + + if (stack) + stack = silc_stack_alloc(0, stack); + + newp = silc_scalloc(stack, 1, sizeof(*newp)); + if (!newp) { + silc_stack_free(stack); return NULL; + } + newp->stack = stack; /* Parse the payload */ - ret = silc_buffer_unformat(&buffer, - SILC_STR_UI_SHORT(&newp->len), - SILC_STR_UI_SHORT(&newp->auth_method), - SILC_STR_UI16_NSTRING_ALLOC(&newp->random_data, - &newp->random_len), - SILC_STR_UI16_NSTRING_ALLOC(&newp->auth_data, - &newp->auth_len), - SILC_STR_END); + ret = silc_buffer_sunformat(stack, &buffer, + SILC_STR_UI_SHORT(&newp->len), + SILC_STR_UI_SHORT(&newp->auth_method), + SILC_STR_UI16_NSTRING_ALLOC(&newp->random_data, + &newp->random_len), + SILC_STR_UI16_NSTRING_ALLOC(&newp->auth_data, + &newp->auth_len), + SILC_STR_END); if (ret == -1) { - silc_free(newp); + silc_sfree(stack, newp); + silc_stack_free(stack); + return NULL; + } + + if (newp->len != silc_buffer_len(&buffer) || + newp->random_len + newp->auth_len > silc_buffer_len(&buffer) - 8) { + silc_auth_payload_free(newp); return NULL; } - if (newp->len != buffer.len) { + /* Authentication data must be provided */ + if (newp->auth_len < 1) { silc_auth_payload_free(newp); return NULL; } @@ -79,12 +95,19 @@ SilcAuthPayload silc_auth_payload_parse(const unsigned char *data, return NULL; } + /* If public key authentication, random data must be at least 128 bytes */ + if (newp->auth_method == SILC_AUTH_PUBLIC_KEY && newp->random_len < 128) { + silc_auth_payload_free(newp); + return NULL; + } + return newp; } /* Encodes authentication payload into buffer and returns it */ -SilcBuffer silc_auth_payload_encode(SilcAuthMethod method, +SilcBuffer silc_auth_payload_encode(SilcStack stack, + SilcAuthMethod method, const unsigned char *random_data, SilcUInt16 random_len, const unsigned char *auth_data, @@ -102,28 +125,28 @@ SilcBuffer silc_auth_payload_encode(SilcAuthMethod method, autf8_len = silc_utf8_encoded_len(auth_data, auth_len, 0); if (!autf8_len) return NULL; - autf8 = silc_calloc(autf8_len, sizeof(*autf8)); + autf8 = silc_scalloc(stack, autf8_len, sizeof(*autf8)); auth_len = silc_utf8_encode(auth_data, auth_len, 0, autf8, autf8_len); auth_data = (const unsigned char *)autf8; } len = 2 + 2 + 2 + random_len + 2 + auth_len; - buffer = silc_buffer_alloc_size(len); + buffer = silc_buffer_salloc_size(stack, len); if (!buffer) { - silc_free(autf8); + silc_sfree(stack, autf8); return NULL; } - silc_buffer_format(buffer, - SILC_STR_UI_SHORT(len), - SILC_STR_UI_SHORT(method), - SILC_STR_UI_SHORT(random_len), - SILC_STR_UI_XNSTRING(random_data, random_len), - SILC_STR_UI_SHORT(auth_len), - SILC_STR_UI_XNSTRING(auth_data, auth_len), - SILC_STR_END); + silc_buffer_sformat(stack, buffer, + SILC_STR_UI_SHORT(len), + SILC_STR_UI_SHORT(method), + SILC_STR_UI_SHORT(random_len), + SILC_STR_UI_XNSTRING(random_data, random_len), + SILC_STR_UI_SHORT(auth_len), + SILC_STR_UI_XNSTRING(auth_data, auth_len), + SILC_STR_END); - silc_free(autf8); + silc_sfree(stack, autf8); return buffer; } @@ -132,15 +155,19 @@ SilcBuffer silc_auth_payload_encode(SilcAuthMethod method, void silc_auth_payload_free(SilcAuthPayload payload) { if (payload) { + SilcStack stack = payload->stack; + if (payload->random_data) { memset(payload->random_data, 0, payload->random_len); - silc_free(payload->random_data); + silc_sfree(stack, payload->random_data); } if (payload->auth_data) { memset(payload->auth_data, 0, payload->auth_len); - silc_free(payload->auth_data); + silc_sfree(stack, payload->auth_data); } - silc_free(payload); + + silc_sfree(stack, payload); + silc_stack_free(stack); } } @@ -151,13 +178,24 @@ SilcAuthMethod silc_auth_get_method(SilcAuthPayload payload) return payload->auth_method; } +/* Get the public data from the auth payload. */ + +unsigned char *silc_auth_get_public_data(SilcAuthPayload payload, + SilcUInt32 *pubdata_len) +{ + if (pubdata_len) + *pubdata_len = (SilcUInt32)payload->random_len; + + return payload->random_data; +} + /* Get the authentication data. If this is passphrase it is UTF-8 encoded. */ unsigned char *silc_auth_get_data(SilcAuthPayload payload, SilcUInt32 *auth_len) { if (auth_len) - *auth_len = payload->auth_len; + *auth_len = (SilcUInt32)payload->auth_len; return payload->auth_data; } @@ -172,229 +210,292 @@ unsigned char *silc_auth_get_data(SilcAuthPayload payload, dictates. */ static unsigned char * -silc_auth_public_key_encode_data(SilcPublicKey public_key, - const unsigned char *random, +silc_auth_public_key_encode_data(SilcStack stack, + SilcPublicKey public_key, + const unsigned char *randomdata, SilcUInt32 random_len, const void *id, SilcIdType type, SilcUInt32 *ret_len) { SilcBuffer buf; - unsigned char *pk, *id_data, *ret; + unsigned char *pk, id_data[32], *ret; SilcUInt32 pk_len, id_len; - pk = silc_pkcs_public_key_encode(public_key, &pk_len); + pk = silc_pkcs_public_key_encode(stack, public_key, &pk_len); if (!pk) return NULL; - id_data = silc_id_id2str(id, type); - if (!id_data) { + if (!silc_id_id2str(id, type, id_data, sizeof(id_data), &id_len)) { silc_free(pk); return NULL; } - id_len = silc_id_get_len(id, type); - buf = silc_buffer_alloc_size(random_len + id_len + pk_len); + buf = silc_buffer_salloc_size(stack, random_len + id_len + pk_len); if (!buf) { silc_free(pk); - silc_free(id_data); return NULL; } - silc_buffer_format(buf, - SILC_STR_UI_XNSTRING(random, random_len), - SILC_STR_UI_XNSTRING(id_data, id_len), - SILC_STR_UI_XNSTRING(pk, pk_len), - SILC_STR_END); - - ret = silc_memdup(buf->data, buf->len); - if (!ret) - return NULL; + silc_buffer_sformat(stack, buf, + SILC_STR_UI_XNSTRING(randomdata, random_len), + SILC_STR_UI_XNSTRING(id_data, id_len), + SILC_STR_UI_XNSTRING(pk, pk_len), + SILC_STR_END); - if (ret_len) - *ret_len = buf->len; + ret = silc_buffer_steal(buf, ret_len); - silc_buffer_free(buf); - silc_free(id_data); - silc_free(pk); + silc_buffer_sfree(stack, buf); + silc_sfree(stack, pk); return ret; } +typedef struct { + SilcStack stack; + unsigned char *pubdata; + SilcUInt32 pubdata_len; + SilcAuthGenerated generated; + void *context; +} *SilcAuthGenerateContext; + +/* Signature callback */ + +static void +silc_auth_public_key_auth_generate_cb(SilcBool success, + const unsigned char *signature, + SilcUInt32 signature_len, + void *context) +{ + SilcAuthGenerateContext a = context; + SilcStack stack = a->stack; + SilcBuffer buf; + + if (!success) { + a->generated(NULL, context); + silc_sfree(stack, a->pubdata); + silc_sfree(stack, a); + silc_stack_free(stack); + return; + } + + /* Encode Authentication Payload */ + buf = silc_auth_payload_encode(stack, SILC_AUTH_PUBLIC_KEY, a->pubdata, + a->pubdata_len, signature, signature_len); + + a->generated(buf, context); + + silc_buffer_sfree(stack, buf); + silc_sfree(stack, a->pubdata); + silc_sfree(stack, a); + silc_stack_free(stack); +} + +/* Generates Authentication Payload with authentication data. This is used + to do public key based authentication. This generates the random data + and the actual authentication data. Returns NULL on error. */ + +SilcAsyncOperation +silc_auth_public_key_auth_generate(SilcPublicKey public_key, + SilcPrivateKey private_key, + SilcRng rng, SilcHash hash, + const void *id, SilcIdType type, + SilcAuthGenerated generated, + void *context) +{ + unsigned char randomdata[256]; + + /* Get random data */ + if (rng) + silc_rng_get_rn_data(rng, sizeof(randomdata), randomdata, + sizeof(randomdata)); + else + silc_rng_global_get_rn_data(rng, sizeof(randomdata), randomdata, + sizeof(randomdata)); + + return silc_auth_public_key_auth_generate_wpub(public_key, private_key, + randomdata, sizeof(randomdata), + hash, rng, id, type, generated, + context); +} + /* Generates Authentication Payload with authentication data. This is used to do public key based authentication. This generates the random data and the actual authentication data. Returns NULL on error. */ -SilcBuffer silc_auth_public_key_auth_generate(SilcPublicKey public_key, - SilcPrivateKey private_key, - SilcRng rng, SilcHash hash, - const void *id, SilcIdType type) +SilcAsyncOperation +silc_auth_public_key_auth_generate_wpub(SilcPublicKey public_key, + SilcPrivateKey private_key, + const unsigned char *pubdata, + SilcUInt32 pubdata_len, + SilcHash hash, + SilcRng rng, + const void *id, SilcIdType type, + SilcAuthGenerated generated, + void *context) { - unsigned char *random; - unsigned char auth_data[1024]; - SilcUInt32 auth_len; + SilcAuthGenerateContext a; + SilcAsyncOperation op; unsigned char *tmp; SilcUInt32 tmp_len; - SilcBuffer buf; - SilcPKCS pkcs; + SilcStack stack; SILC_LOG_DEBUG(("Generating Authentication Payload with data")); - /* Get 256 bytes of random data */ - if (rng) - random = silc_rng_get_rn_data(rng, 256); - else - random = silc_rng_global_get_rn_data(256); - if (!random) - return NULL; - - /* Encode the auth data */ - tmp = silc_auth_public_key_encode_data(public_key, random, 256, id, type, - &tmp_len); - if (!tmp) + /* We use the Crypto Toolkit's stack since we're doing crypto */ + stack = silc_stack_alloc(2048, silc_crypto_stack()); + + a = silc_scalloc(stack, 1, sizeof(*a)); + if (!a) { + generated(NULL, context); return NULL; + } + a->stack = stack; - /* Allocate PKCS object */ - if (!silc_pkcs_alloc(public_key->name, &pkcs)) { - memset(tmp, 0, tmp_len); - silc_free(tmp); + /* Encode the auth data */ + tmp = silc_auth_public_key_encode_data(stack, public_key, pubdata, + pubdata_len, id, type, &tmp_len); + if (!tmp) { + silc_sfree(stack, a); + silc_stack_free(stack); + generated(NULL, context); return NULL; } - silc_pkcs_public_key_set(pkcs, public_key); - silc_pkcs_private_key_set(pkcs, private_key); - /* Compute the hash and the signature. */ - if (!silc_pkcs_sign_with_hash(pkcs, hash, tmp, tmp_len, auth_data, - &auth_len)) { - memset(random, 0, 256); + a->pubdata = silc_smemdup(stack, pubdata, pubdata_len); + if (!a->pubdata) { memset(tmp, 0, tmp_len); - silc_free(tmp); - silc_free(random); - silc_pkcs_free(pkcs); + silc_sfree(stack, tmp); + silc_sfree(stack, a); + silc_stack_free(stack); + generated(NULL, context); return NULL; } - /* Encode Authentication Payload */ - buf = silc_auth_payload_encode(SILC_AUTH_PUBLIC_KEY, random, 256, - auth_data, auth_len); + /* Compute the hash and the signature. */ + op = silc_pkcs_sign_async(private_key, tmp, tmp_len, TRUE, hash, rng, + silc_auth_public_key_auth_generate_cb, a); memset(tmp, 0, tmp_len); - memset(auth_data, 0, sizeof(auth_data)); - memset(random, 0, 256); - silc_free(tmp); - silc_free(random); - silc_pkcs_free(pkcs); + silc_sfree(stack, tmp); - return buf; + return op; } -/* Verifies the authentication data. Returns TRUE if authentication was - successful. */ +/* Verifies the authentication data. */ -bool silc_auth_public_key_auth_verify(SilcAuthPayload payload, - SilcPublicKey public_key, SilcHash hash, - const void *id, SilcIdType type) +SilcAsyncOperation +silc_auth_public_key_auth_verify(SilcAuthPayload payload, + SilcPublicKey public_key, + SilcHash hash, + const void *id, + SilcIdType type, + SilcAuthResultCb result, + void *context) { + SilcAsyncOperation op; unsigned char *tmp; SilcUInt32 tmp_len; - SilcPKCS pkcs; SILC_LOG_DEBUG(("Verifying authentication data")); /* Encode auth data */ - tmp = silc_auth_public_key_encode_data(public_key, payload->random_data, - payload->random_len, + tmp = silc_auth_public_key_encode_data(payload->stack, + public_key, payload->random_data, + payload->random_len, id, type, &tmp_len); if (!tmp) { SILC_LOG_DEBUG(("Authentication failed")); - return FALSE; - } - - /* Allocate PKCS object */ - if (!silc_pkcs_alloc(public_key->name, &pkcs)) { - memset(tmp, 0, tmp_len); - silc_free(tmp); - return FALSE; + result(FALSE, context); + return NULL; } - silc_pkcs_public_key_set(pkcs, public_key); /* Verify the authentication data */ - if (!silc_pkcs_verify_with_hash(pkcs, hash, payload->auth_data, - payload->auth_len, tmp, tmp_len)) { - - memset(tmp, 0, tmp_len); - silc_free(tmp); - silc_pkcs_free(pkcs); - SILC_LOG_DEBUG(("Authentication failed")); - return FALSE; - } + op = silc_pkcs_verify_async(public_key, payload->auth_data, + payload->auth_len, tmp, tmp_len, TRUE, hash, + result, context); memset(tmp, 0, tmp_len); - silc_free(tmp); - silc_pkcs_free(pkcs); + silc_sfree(payload->stack, tmp); - SILC_LOG_DEBUG(("Authentication successful")); - - return TRUE; + return op; } /* Same as above but the payload is not parsed yet. This will parse it. */ -bool silc_auth_public_key_auth_verify_data(const unsigned char *payload, - SilcUInt32 payload_len, - SilcPublicKey public_key, - SilcHash hash, - const void *id, SilcIdType type) +SilcAsyncOperation +silc_auth_public_key_auth_verify_data(const unsigned char *payload, + SilcUInt32 payload_len, + SilcPublicKey public_key, + SilcHash hash, + const void *id, + SilcIdType type, + SilcAuthResultCb result, + void *context) { + SilcAsyncOperation op; SilcAuthPayload auth_payload; - int ret; - auth_payload = silc_auth_payload_parse(payload, payload_len); + auth_payload = silc_auth_payload_parse(silc_crypto_stack(), payload, + payload_len); if (!auth_payload) { SILC_LOG_DEBUG(("Authentication failed")); - return FALSE; + result(FALSE, context); + return NULL; } - ret = silc_auth_public_key_auth_verify(auth_payload, public_key, hash, - id, type); + op = silc_auth_public_key_auth_verify(auth_payload, public_key, hash, + id, type, result, context); silc_auth_payload_free(auth_payload); - return ret; + return op; } -/* Verifies the authentication data directly from the Authentication +/* Verifies the authentication data directly from the Authentication Payload. Supports all authentication methods. If the authentication method is passphrase based then the `auth_data' and `auth_data_len' are the passphrase and its length. If the method is public key authentication then the `auth_data' is the SilcPublicKey and the `auth_data_len' is ignored. */ -bool silc_auth_verify(SilcAuthPayload payload, SilcAuthMethod auth_method, - const void *auth_data, SilcUInt32 auth_data_len, - SilcHash hash, const void *id, SilcIdType type) +SilcAsyncOperation +silc_auth_verify(SilcAuthPayload payload, SilcAuthMethod auth_method, + const void *auth_data, SilcUInt32 auth_data_len, + SilcHash hash, const void *id, SilcIdType type, + SilcAuthResultCb result, void *context) { SILC_LOG_DEBUG(("Verifying authentication")); - if (auth_method != payload->auth_method) - return FALSE; + if (!payload || auth_method != payload->auth_method) { + result(FALSE, context); + return NULL; + } switch (payload->auth_method) { case SILC_AUTH_NONE: /* No authentication */ SILC_LOG_DEBUG(("No authentication required")); - return TRUE; + result(TRUE, context); + return NULL; case SILC_AUTH_PASSWORD: /* Passphrase based authentication. The `pkcs', `hash', `id' and `type' arguments are not needed. */ + + /* Sanity checks */ + if ((payload->auth_len == 0) || !auth_data || + payload->auth_len != auth_data_len) + break; + if (!memcmp(payload->auth_data, auth_data, auth_data_len)) { - SILC_LOG_DEBUG(("Authentication successful")); - return TRUE; + SILC_LOG_DEBUG(("Passphrase Authentication successful")); + result(TRUE, context); + return NULL; } break; case SILC_AUTH_PUBLIC_KEY: /* Public key based authentication */ return silc_auth_public_key_auth_verify(payload, (SilcPublicKey)auth_data, - hash, id, type); + hash, id, type, result, context); break; default: @@ -402,31 +503,38 @@ bool silc_auth_verify(SilcAuthPayload payload, SilcAuthMethod auth_method, } SILC_LOG_DEBUG(("Authentication failed")); + result(FALSE, context); - return FALSE; + return NULL; } /* Same as above but parses the authentication payload before verify. */ -bool silc_auth_verify_data(const unsigned char *payload, - SilcUInt32 payload_len, - SilcAuthMethod auth_method, const void *auth_data, - SilcUInt32 auth_data_len, SilcHash hash, - const void *id, SilcIdType type) +SilcAsyncOperation +silc_auth_verify_data(const unsigned char *payload, + SilcUInt32 payload_len, + SilcAuthMethod auth_method, + const void *auth_data, + SilcUInt32 auth_data_len, SilcHash hash, + const void *id, SilcIdType type, + SilcAuthResultCb result, void *context) { + SilcAsyncOperation op; SilcAuthPayload auth_payload; - int ret; - auth_payload = silc_auth_payload_parse(payload, payload_len); - if (!auth_payload) - return FALSE; + auth_payload = silc_auth_payload_parse(silc_crypto_stack(), payload, + payload_len); + if (!auth_payload || (auth_payload->auth_len == 0)) { + result(FALSE, context); + return NULL; + } - ret = silc_auth_verify(auth_payload, auth_method, auth_data, auth_data_len, - hash, id, type); + op = silc_auth_verify(auth_payload, auth_method, auth_data, auth_data_len, + hash, id, type, result, context); silc_auth_payload_free(auth_payload); - return ret; + return op; } /****************************************************************************** @@ -439,12 +547,13 @@ bool silc_auth_verify_data(const unsigned char *payload, struct SilcKeyAgreementPayloadStruct { SilcUInt16 hostname_len; unsigned char *hostname; - SilcUInt32 port; + SilcUInt16 protocol; + SilcUInt16 port; }; /* Parses and returns an allocated Key Agreement payload. */ -SilcKeyAgreementPayload +SilcKeyAgreementPayload silc_key_agreement_payload_parse(const unsigned char *payload, SilcUInt32 payload_len) { @@ -454,18 +563,19 @@ silc_key_agreement_payload_parse(const unsigned char *payload, SILC_LOG_DEBUG(("Parsing Key Agreement Payload")); - silc_buffer_set(&buffer, (unsigned char *)payload, payload_len); newp = silc_calloc(1, sizeof(*newp)); if (!newp) return NULL; /* Parse the payload */ - ret = silc_buffer_unformat(&buffer, + silc_buffer_set(&buffer, (unsigned char *)payload, payload_len); + ret = silc_buffer_unformat(&buffer, SILC_STR_UI16_NSTRING_ALLOC(&newp->hostname, &newp->hostname_len), - SILC_STR_UI_INT(&newp->port), + SILC_STR_UI_SHORT(&newp->protocol), + SILC_STR_UI_SHORT(&newp->port), SILC_STR_END); - if (ret == -1) { + if (ret == -1 || newp->hostname_len > silc_buffer_len(&buffer) - 6) { silc_free(newp); return NULL; } @@ -476,7 +586,8 @@ silc_key_agreement_payload_parse(const unsigned char *payload, /* Encodes the Key Agreement protocol and returns the encoded buffer */ SilcBuffer silc_key_agreement_payload_encode(const char *hostname, - SilcUInt32 port) + SilcUInt16 protocol, + SilcUInt16 port) { SilcBuffer buffer; SilcUInt32 len = hostname ? strlen(hostname) : 0; @@ -489,7 +600,8 @@ SilcBuffer silc_key_agreement_payload_encode(const char *hostname, silc_buffer_format(buffer, SILC_STR_UI_SHORT(len), SILC_STR_UI_XNSTRING(hostname, len), - SILC_STR_UI_INT(port), + SILC_STR_UI_SHORT(protocol), + SILC_STR_UI_SHORT(port), SILC_STR_END); return buffer; @@ -512,9 +624,16 @@ char *silc_key_agreement_get_hostname(SilcKeyAgreementPayload payload) return payload->hostname; } +/* Returns the protocol in the payload */ + +SilcUInt16 silc_key_agreement_get_protocol(SilcKeyAgreementPayload payload) +{ + return payload->protocol; +} + /* Returns the port in the payload */ -SilcUInt32 silc_key_agreement_get_port(SilcKeyAgreementPayload payload) +SilcUInt16 silc_key_agreement_get_port(SilcKeyAgreementPayload payload) { return payload->port; }