From: Pekka Riikonen Date: Fri, 5 Apr 2002 17:36:12 +0000 (+0000) Subject: updates. X-Git-Tag: silc.client.0.8.6~4^2~24 X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=commitdiff_plain;h=bb8ce8c6460403e9220d9f96eb0e6bd2a20d31f0 updates. --- diff --git a/CHANGES b/CHANGES index 299db091..da17f33e 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,23 @@ +Fri Apr 5 16:03:03 EEST 2002 Pekka Riikonen + + * Splitted lib/silcutil/silcutil.h into silcstrutil.h for + string utility functions. Added there also new functions + silc_utf8_[encode/decode/valid] for UTF-8 string encoding. + Affected files lib/silcutil/silcstrutil.[ch]. + + * Renamed silc_*_pem functions to silc_pem_* functions. Affected + files are lib/silcutil/silcstrutil.[ch]. + + * Defined that the security property fields in SKE SHOULD be + UTF-8 encoded, defined that version string MUST be US-ASCII + encoded, defined that passphrases sent in connection + authentication protocol MUST be UTF-8 encoded. Implemented + these to the client and server. Defined also that other + passphrases sent in the protocol MUST be UTF-8 encoded. + Affected files are lib/silcske/silcske.c, + lib/silcclient/protocol.c, silcd/protocol.c, + silcd/serverconfig.c, and lib/silccore/silcauth.c. + Wed Apr 3 16:24:51 EEST 2002 Pekka Riikonen * Upgraded the protocol version to 1.1, updated protocol specs diff --git a/TODO b/TODO index 038bce1f..2f49dc64 100644 --- a/TODO +++ b/TODO @@ -157,5 +157,16 @@ describe new stuff to be added to protocol versions 1.x. 17. Cell wide channel founder support, and permanent channels when founder mode set. - 18. UTF-8 requirement checkings for all specifications, when strings - are sent. http://www.cl.cam.ac.uk/~mgk25/unicode.html + 18. Describe the SSH public key, X509, OpenPGP and SPKI certificates + encoding format in SKE (from their respective definitions). + + o UTF-8 support/requirement for nicknames & channel names. UTF-8 support + in terminals and OS's are so hazy that this matter is left for + consideration in next version of the protocol (1.2). For good UTF-8 + reference and tutorial see: http://www.cl.cam.ac.uk/~mgk25/unicode.html. + What should CLI application do if it receives nickname that it cannot + display without messing up the terminal? If UTF-8 is mandatory in + SILC then SILC clients cannot be allowed to start on terminals that do + not support UTF-8 (which renders 98% of users unable to use CLI SILC + app without hacking their environment). See also site + http://gratrix.net/unicode/ diff --git a/doc/draft-riikonen-silc-pp-05.nroff b/doc/draft-riikonen-silc-pp-05.nroff index 31b557d7..36dee34b 100644 --- a/doc/draft-riikonen-silc-pp-05.nroff +++ b/doc/draft-riikonen-silc-pp-05.nroff @@ -1189,7 +1189,9 @@ o Argument Nums (2 bytes) - Indicates the number of Argument The following list of currently defined notify types. The format for notify arguments is same as in SILC commands described in [SILC4]. -Also, all ID's sent in arguments are sent inside ID Payload. +Note that all ID's sent in arguments are sent inside ID Payload. Also +note that all passphrases that may be sent inside arguments MUST be +UTF-8 [RFC2279] encoded. .in 6 0 SILC_NOTIFY_TYPE_NONE @@ -2822,6 +2824,9 @@ security of this protocol. [SFTP] Ylonen T., and Lehtinen S., "Secure Shell File Transfer Protocol", Internet Draft, March 2001. +[RFC2279] Yergeau, F., "UTF-8, a transformation format of ISO + 10646", RFC 2279, January 1998. + .ti 0 5 Author's Address diff --git a/lib/silcclient/command.c b/lib/silcclient/command.c index 7ffc279f..f6813f34 100644 --- a/lib/silcclient/command.c +++ b/lib/silcclient/command.c @@ -948,8 +948,8 @@ SILC_CLIENT_CMD_FUNC(join) SilcClientConnection conn = cmd->conn; SilcChannelEntry channel; SilcBuffer buffer, idp, auth = NULL; - char *name, *passphrase = NULL, *cipher = NULL, *hmac = NULL; - int i; + char *name, *passphrase = NULL, *pu8, *cipher = NULL, *hmac = NULL; + int i, passphrase_len = 0; if (!cmd->conn) { SILC_NOT_CONNECTED(cmd->client, cmd->conn); @@ -995,7 +995,18 @@ SILC_CLIENT_CMD_FUNC(join) } i++; } else { - passphrase = cmd->argv[i]; + /* Passphrases must be UTF-8 encoded, so encode if it is not */ + if (!silc_utf8_valid(cmd->argv[i], cmd->argv_lens[i])) { + passphrase_len = silc_utf8_encoded_len(cmd->argv[i], + cmd->argv_lens[i], 0); + pu8 = silc_calloc(passphrase_len, sizeof(*pu8)); + passphrase_len = silc_utf8_encode(cmd->argv[i], cmd->argv_lens[i], + 0, pu8, passphrase_len); + passphrase = pu8; + } else { + passphrase = strdup(cmd->argv[i]); + passphrase_len = cmd->argv_lens[i]; + } } } @@ -1004,8 +1015,7 @@ SILC_CLIENT_CMD_FUNC(join) silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 6, 1, name, strlen(name), 2, idp->data, idp->len, - 3, passphrase, - passphrase ? strlen(passphrase) : 0, + 3, passphrase, passphrase_len, 4, cipher, cipher ? strlen(cipher) : 0, 5, hmac, hmac ? strlen(hmac) : 0, 6, auth ? auth->data : NULL, @@ -1016,6 +1026,7 @@ SILC_CLIENT_CMD_FUNC(join) silc_buffer_free(idp); if (auth) silc_buffer_free(auth); + silc_free(passphrase); /* Notify application */ COMMAND; @@ -1730,7 +1741,8 @@ SILC_CLIENT_CMD_FUNC(oper) } static void silc_client_command_silcoper_send(unsigned char *data, - SilcUInt32 data_len, void *context) + SilcUInt32 data_len, + void *context) { SilcClientCommandContext cmd = (SilcClientCommandContext)context; SilcClientConnection conn = cmd->conn; diff --git a/lib/silcclient/silcclient.h b/lib/silcclient/silcclient.h index 5f9f4437..b9647bdf 100644 --- a/lib/silcclient/silcclient.h +++ b/lib/silcclient/silcclient.h @@ -183,7 +183,9 @@ typedef struct SilcChannelPrivateKeyStruct { * * Ask passphrase callback. This is called by the application when the * library calls `ask_passphrase' client operation. The callback delivers - * the passphrase to the library. + * the passphrase to the library. The passphrases in SILC protocol + * MUST be in UTF-8 encoding, therefore the `passphrase' SHOULD be UTF-8 + * encoded, and if it is not then library will attempt to encode it. * ***/ typedef void (*SilcAskPassphrase)(unsigned char *passphrase, @@ -371,7 +373,8 @@ typedef struct { /* Ask (interact, that is) a passphrase from user. The passphrase is returned to the library by calling the `completion' callback with - the `context'. */ + the `context'. The returned passphrase SHOULD be in UTF-8 encoded, + if not then the library will attempt to encode. */ void (*ask_passphrase)(SilcClient client, SilcClientConnection conn, SilcAskPassphrase completion, void *context); diff --git a/lib/silcutil/silcstrutil.c b/lib/silcutil/silcstrutil.c new file mode 100644 index 00000000..fa598945 --- /dev/null +++ b/lib/silcutil/silcstrutil.c @@ -0,0 +1,409 @@ +/* + + silcstrutil.c + + Author: Pekka Riikonen + + Copyright (C) 2002 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; 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 + GNU General Public License for more details. + +*/ + +#include "silcincludes.h" +#include "silcstrutil.h" + +static unsigned char pem_enc[64] = +"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +/* Encodes data into PEM encoding. Returns NULL terminated PEM encoded + data string. Note: This is originally public domain code and is + still PD. */ + +char *silc_pem_encode(unsigned char *data, SilcUInt32 len) +{ + int i, j; + SilcUInt32 bits, c, char_count; + char *pem; + + char_count = 0; + bits = 0; + j = 0; + + pem = silc_calloc(((len * 8 + 5) / 6) + 5, sizeof(*pem)); + + for (i = 0; i < len; i++) { + c = data[i]; + bits += c; + char_count++; + + if (char_count == 3) { + pem[j++] = pem_enc[bits >> 18]; + pem[j++] = pem_enc[(bits >> 12) & 0x3f]; + pem[j++] = pem_enc[(bits >> 6) & 0x3f]; + pem[j++] = pem_enc[bits & 0x3f]; + bits = 0; + char_count = 0; + } else { + bits <<= 8; + } + } + + if (char_count != 0) { + bits <<= 16 - (8 * char_count); + pem[j++] = pem_enc[bits >> 18]; + pem[j++] = pem_enc[(bits >> 12) & 0x3f]; + + if (char_count == 1) { + pem[j++] = '='; + pem[j] = '='; + } else { + pem[j++] = pem_enc[(bits >> 6) & 0x3f]; + pem[j] = '='; + } + } + + return pem; +} + +/* Same as above but puts newline ('\n') every 72 characters. */ + +char *silc_pem_encode_file(unsigned char *data, SilcUInt32 data_len) +{ + int i, j; + SilcUInt32 len, cols; + char *pem, *pem2; + + pem = silc_pem_encode(data, data_len); + len = strlen(pem); + + pem2 = silc_calloc(len + (len / 72) + 1, sizeof(*pem2)); + + for (i = 0, j = 0, cols = 1; i < len; i++, cols++) { + if (cols == 72) { + pem2[i] = '\n'; + cols = 0; + len++; + continue; + } + + pem2[i] = pem[j++]; + } + + silc_free(pem); + return pem2; +} + +/* Decodes PEM into data. Returns the decoded data. Note: This is + originally public domain code and is still PD. */ + +unsigned char *silc_pem_decode(unsigned char *pem, SilcUInt32 pem_len, + SilcUInt32 *ret_len) +{ + int i, j; + SilcUInt32 len, c, char_count, bits; + unsigned char *data; + static char ialpha[256], decoder[256]; + + for (i = 64 - 1; i >= 0; i--) { + ialpha[pem_enc[i]] = 1; + decoder[pem_enc[i]] = i; + } + + char_count = 0; + bits = 0; + j = 0; + + if (!pem_len) + len = strlen(pem); + else + len = pem_len; + + data = silc_calloc(((len * 6) / 8), sizeof(*data)); + + for (i = 0; i < len; i++) { + c = pem[i]; + + if (c == '=') + break; + + if (c > 127 || !ialpha[c]) + continue; + + bits += decoder[c]; + char_count++; + + if (char_count == 4) { + data[j++] = bits >> 16; + data[j++] = (bits >> 8) & 0xff; + data[j++] = bits & 0xff; + bits = 0; + char_count = 0; + } else { + bits <<= 6; + } + } + + switch(char_count) { + case 1: + silc_free(data); + return NULL; + break; + case 2: + data[j++] = bits >> 10; + break; + case 3: + data[j++] = bits >> 16; + data[j++] = (bits >> 8) & 0xff; + break; + } + + if (ret_len) + *ret_len = j; + + return data; +} + +/* Encodes the string `bin' of which encoding is `bin_encoding' to the + UTF-8 encoding into the buffer `utf8' which is of size of `utf8_size'. + Returns the length of the UTF-8 encoded string, or zero (0) on error. + By default `bin_encoding' is ASCII, and the caller needs to know the + encoding of the input string if it is anything else. */ + +SilcUInt32 silc_utf8_encode(const unsigned char *bin, SilcUInt32 bin_len, + SilcStringEncoding bin_encoding, + unsigned char *utf8, SilcUInt32 utf8_size) +{ + SilcUInt32 enclen = 0, i, charval = 0; + + if (!bin || !bin_len) + return 0; + + for (i = 0; i < bin_len; i++) { + switch (bin_encoding) { + case SILC_STRING_ASCII: + charval = bin[i]; + break; + case SILC_STRING_ASCII_ESC: + break; + case SILC_STRING_BMP: + break; + case SILC_STRING_UNIVERSAL: + break; + } + + if (charval < 0x80) { + if (utf8) { + if (enclen > utf8_size) + return 0; + + utf8[enclen] = (unsigned char)charval; + } + enclen++; + } else if (charval < 0x800) { + if (utf8) { + if (enclen + 2 > utf8_size) + return 0; + + utf8[enclen ] = (unsigned char )(((charval >> 6) & 0x1f) | 0xc0); + utf8[enclen + 1] = (unsigned char )((charval & 0x3f) | 0x80); + } + enclen += 2; + } else if (charval < 0x10000) { + if (utf8) { + if (enclen + 3 > utf8_size) + return 0; + + utf8[enclen ] = (unsigned char )(((charval >> 12) & 0xf) | 0xe0); + utf8[enclen + 1] = (unsigned char )(((charval >> 6) & 0x3f) | 0x80); + utf8[enclen + 2] = (unsigned char )((charval & 0x3f) | 0x80); + } + enclen += 3; + } else if (charval < 0x200000) { + if (utf8) { + if (enclen + 4 > utf8_size) + return 0; + + utf8[enclen ] = (unsigned char )(((charval >> 18) & 0x7) | 0xf0); + utf8[enclen + 1] = (unsigned char )(((charval >> 12) & 0x3f) | 0x80); + utf8[enclen + 2] = (unsigned char )(((charval >> 6) & 0x3f) | 0x80); + utf8[enclen + 3] = (unsigned char )((charval & 0x3f) | 0x80); + } + enclen += 4; + } else if (charval < 0x4000000) { + if (utf8) { + if (enclen + 5 > utf8_size) + return 0; + + utf8[enclen ] = (unsigned char )(((charval >> 24) & 0x3) | 0xf8); + utf8[enclen + 1] = (unsigned char )(((charval >> 18) & 0x3f) | 0x80); + utf8[enclen + 2] = (unsigned char )(((charval >> 12) & 0x3f) | 0x80); + utf8[enclen + 3] = (unsigned char )(((charval >> 6) & 0x3f) | 0x80); + utf8[enclen + 4] = (unsigned char )((charval & 0x3f) | 0x80); + } + enclen += 5; + } else { + if (utf8) { + if (enclen + 6 > utf8_size) + return 0; + + utf8[enclen ] = (unsigned char )(((charval >> 30) & 0x1) | 0xfc); + utf8[enclen + 1] = (unsigned char )(((charval >> 24) & 0x3f) | 0x80); + utf8[enclen + 2] = (unsigned char )(((charval >> 18) & 0x3f) | 0x80); + utf8[enclen + 3] = (unsigned char )(((charval >> 12) & 0x3f) | 0x80); + utf8[enclen + 4] = (unsigned char )(((charval >> 6) & 0x3f) | 0x80); + utf8[enclen + 5] = (unsigned char )((charval & 0x3f) | 0x80); + } + enclen += 6; + } + } + + return enclen; +} + +/* Decodes UTF-8 encoded string `utf8' to string of which encoding is + to be `bin_encoding', into the `bin' buffer of size of `bin_size'. + Returns the length of the decoded buffer, or zero (0) on error. + By default `bin_encoding' is ASCII, and the caller needs to know to + which encoding the output string is to be encoded if ASCII is not + desired. */ + +SilcUInt32 silc_utf8_decode(const unsigned char *utf8, SilcUInt32 utf8_len, + SilcStringEncoding bin_encoding, + unsigned char *bin, SilcUInt32 bin_size) +{ + SilcUInt32 enclen = 0, i, charval; + + if (!utf8 || !utf8_len) + return 0; + + for (i = 0; i < utf8_len; i++) { + if ((utf8[i] & 0x80) == 0x00) { + charval = utf8[i] & 0x7f; + } else if ((utf8[i] & 0xe0) == 0xc0) { + if (utf8_len < 2) + return 0; + + if ((utf8[i + 1] & 0xc0) != 0x80) + return 0; + + charval = (utf8[i++] & 0x1f) << 6; + charval |= utf8[i] & 0x3f; + if (charval < 0x80) + return 0; + } else if ((utf8[i] & 0xf0) == 0xe0) { + if (utf8_len < 3) + return 0; + + if (((utf8[i + 1] & 0xc0) != 0x80) || + ((utf8[i + 2] & 0xc0) != 0x80)) + return 0; + + charval = (utf8[i++] & 0xf) << 12; + charval |= (utf8[i++] & 0x3f) << 6; + charval |= utf8[i] & 0x3f; + if (charval < 0x800) + return 0; + } else if ((utf8[i] & 0xf8) == 0xf0) { + if (utf8_len < 4) + return 0; + + if (((utf8[i + 1] & 0xc0) != 0x80) || + ((utf8[i + 2] & 0xc0) != 0x80) || + ((utf8[i + 3] & 0xc0) != 0x80)) + return 0; + + charval = ((SilcUInt32)(utf8[i++] & 0x7)) << 18; + charval |= (utf8[i++] & 0x3f) << 12; + charval |= (utf8[i++] & 0x3f) << 6; + charval |= utf8[i] & 0x3f; + if (charval < 0x10000) + return 0; + } else if ((utf8[i] & 0xfc) == 0xf8) { + if (utf8_len < 5) + return 0; + + if (((utf8[i + 1] & 0xc0) != 0x80) || + ((utf8[i + 2] & 0xc0) != 0x80) || + ((utf8[i + 3] & 0xc0) != 0x80) || + ((utf8[i + 4] & 0xc0) != 0x80)) + return 0; + + charval = ((SilcUInt32)(utf8[i++] & 0x3)) << 24; + charval |= ((SilcUInt32)(utf8[i++] & 0x3f)) << 18; + charval |= ((SilcUInt32)(utf8[i++] & 0x3f)) << 12; + charval |= (utf8[i++] & 0x3f) << 6; + charval |= utf8[i] & 0x3f; + if (charval < 0x200000) + return 0; + } else if ((utf8[i] & 0xfe) == 0xfc) { + if (utf8_len < 6) + return 0; + + if (((utf8[i + 1] & 0xc0) != 0x80) || + ((utf8[i + 2] & 0xc0) != 0x80) || + ((utf8[i + 3] & 0xc0) != 0x80) || + ((utf8[i + 4] & 0xc0) != 0x80) || + ((utf8[i + 5] & 0xc0) != 0x80)) + return 0; + + charval = ((SilcUInt32)(utf8[i++] & 0x1)) << 30; + charval |= ((SilcUInt32)(utf8[i++] & 0x3f)) << 24; + charval |= ((SilcUInt32)(utf8[i++] & 0x3f)) << 18; + charval |= ((SilcUInt32)(utf8[i++] & 0x3f)) << 12; + charval |= (utf8[i++] & 0x3f) << 6; + charval |= utf8[i] & 0x3f; + if (charval < 0x4000000) + return 0; + } else { + return 0; + } + + switch (bin_encoding) { + case SILC_STRING_ASCII: + if (bin) { + if (enclen + 1 > bin_size) + return 0; + + bin[enclen] = (unsigned char)charval; + } + enclen++; + break; + case SILC_STRING_ASCII_ESC: + return 0; + break; + case SILC_STRING_BMP: + return 0; + break; + case SILC_STRING_UNIVERSAL: + return 0; + break; + } + } + + return enclen; +} + +/* Returns the length of UTF-8 encoded string if the `bin' of + encoding of `bin_encoding' is encoded with silc_utf8_encode. */ + +SilcUInt32 silc_utf8_encoded_len(const unsigned char *bin, SilcUInt32 bin_len, + SilcStringEncoding bin_encoding) +{ + return silc_utf8_encode(bin, bin_len, bin_encoding, NULL, 0); +} + +/* Returns TRUE if the `utf8' string of length of `utf8_len' is valid + UTF-8 encoded string, FALSE if it is not UTF-8 encoded string. */ + +bool silc_utf8_valid(const unsigned char *utf8, SilcUInt32 utf8_len) +{ + return silc_utf8_decode(utf8, utf8_len, 0, NULL, 0) != 0; +}