Bugzilla – Attachment 2277 Details for
Bug 2058
SSH Banner message displays UTF-8 multibyte char incorrrectly
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Apply RFC3454 stringprep to banners when possible
banner-stringprep.diff (text/plain), 12.64 KB, created by
Damien Miller
on 2013-05-30 12:07:39 AEST
(
hide
)
Description:
Apply RFC3454 stringprep to banners when possible
Filename:
MIME Type:
Creator:
Damien Miller
Created:
2013-05-30 12:07:39 AEST
Size:
12.64 KB
patch
obsolete
>Index: sshconnect2.c >=================================================================== >RCS file: /cvs/src/usr.bin/ssh/sshconnect2.c,v >retrieving revision 1.197 >diff -u -p -r1.197 sshconnect2.c >--- sshconnect2.c 17 May 2013 00:13:14 -0000 1.197 >+++ sshconnect2.c 30 May 2013 02:04:18 -0000 >@@ -39,6 +39,8 @@ > #include <pwd.h> > #include <unistd.h> > #include <vis.h> >+#include <locale.h> >+#include <langinfo.h> > > #include "xmalloc.h" > #include "ssh.h" >@@ -462,21 +464,51 @@ input_userauth_error(int type, u_int32_t > "type %d", type); > } > >+/* Check whether we can display UTF-8 safely */ >+static int >+utf8_ok(void) >+{ >+ static int ret = -1; >+ char *cp; >+ >+ if (ret == -1) { >+ setlocale(LC_CTYPE, ""); >+ cp = nl_langinfo(CODESET); >+ ret = strcmp(cp, "UTF-8") == 0; >+ } >+ return ret; >+} >+ > /* ARGSUSED */ > void > input_userauth_banner(int type, u_int32_t seq, void *ctxt) > { > char *msg, *raw, *lang; >- u_int len; >+ u_int done, len; > > debug3("input_userauth_banner"); >+ > raw = packet_get_string(&len); > lang = packet_get_string(NULL); > if (len > 0 && options.log_level >= SYSLOG_LEVEL_INFO) { > if (len > 65536) > len = 65536; > msg = xmalloc(len * 4 + 1); /* max expansion from strnvis() */ >- strnvis(msg, raw, len * 4 + 1, VIS_SAFE|VIS_OCTAL|VIS_NOSLASH); >+ done = 0; >+ if (utf8_ok()) { >+ if (ssh_utf8_stringprep(raw, msg, len * 4 + 1) == 0) >+ done = 1; >+ else >+ debug2("%s: UTF8 stringprep failed", __func__); >+ } >+ /* >+ * Fallback to strnvis if UTF8 display not supported or >+ * conversion failed. >+ */ >+ if (!done) { >+ strnvis(msg, raw, len * 4 + 1, >+ VIS_SAFE|VIS_OCTAL|VIS_NOSLASH); >+ } > fprintf(stderr, "%s", msg); > free(msg); > } >Index: misc.h >=================================================================== >RCS file: /cvs/src/usr.bin/ssh/misc.h,v >retrieving revision 1.48 >diff -u -p -r1.48 misc.h >--- misc.h 29 Mar 2011 18:54:17 -0000 1.48 >+++ misc.h 30 May 2013 02:04:18 -0000 >@@ -102,4 +102,7 @@ char *read_passphrase(const char *, int) > int ask_permission(const char *, ...) __attribute__((format(printf, 1, 2))); > int read_keyfile_line(FILE *, const char *, char *, size_t, u_long *); > >+/* stringprep.c */ >+int ssh_utf8_stringprep(const char *, char *, size_t); >+ > #endif /* _MISC_H */ >Index: stringprep.c >=================================================================== >RCS file: stringprep.c >diff -N stringprep.c >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ stringprep.c 30 May 2013 02:04:18 -0000 >@@ -0,0 +1,370 @@ >+/* >+ * Copyright (c) 2013 Damien Miller <djm@mindrot.org> >+ * >+ * Permission to use, copy, modify, and distribute this software for any >+ * purpose with or without fee is hereby granted, provided that the above >+ * copyright notice and this permission notice appear in all copies. >+ * >+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES >+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF >+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR >+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES >+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN >+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF >+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. >+ */ >+ >+/* >+ * This is a simple RFC3454 stringprep profile to normalise UTF-8 >+ * strings from untrusted sources. >+ * >+ * It is intended to be used prior to display of untrusted strings only. >+ * It should not be used for logging because of bi-di ambiguity. It >+ * should also not be used in any case where lack of normalisation may >+ * cause problems. >+ * >+ * This profile supports Unicode 3.2 using the RFC3454 tables as listed >+ * below, with an additional whitelist of whitespace characters (\n, \a >+ * and \t). Unicode normalisation and bi-di testing are not used. >+ */ >+ >+#include <sys/types.h> >+#include <stdio.h> >+#include <stdlib.h> >+#include <string.h> >+#include <limits.h> >+#include <ctype.h> >+ >+#include "misc.h" >+ >+#define BANNED_RFC3629 0 >+#define UTF8_DEBUG 0 >+ >+#if UTF8_DEBUG >+# define DBG(x) \ >+ do { \ >+ printf("%s:%d %s: ", __FILE__, __LINE__, __func__); \ >+ printf x; \ >+ printf("\n"); \ >+ fflush(stdout); \ >+ } while (0) >+#else >+# define DBG(x) >+#endif >+ >+struct u32_range { >+ u_int32_t lo, hi; /* Inclusive */ >+}; >+ >+/* RFC3454 Table B.1 */ >+static const struct u32_range map_to_nothing[] = { >+ { 0x00AD, 0x00AD }, >+ { 0x034F, 0x034F }, >+ { 0x1806, 0x1806 }, >+ { 0x180B, 0x180D }, >+ { 0x200B, 0x200D }, >+ { 0x2060, 0x2060 }, >+ { 0xFE00, 0xFE0F }, >+ { 0xFEFF, 0xFEFF }, >+}; >+ >+/* Local: allow tab, CR and LF */ >+static const struct u32_range whitelist[] = { >+ { 0x09, 0x00 }, >+ { 0x0a, 0x0a }, >+ { 0x0d, 0x0d }, >+}; >+ >+/* RFC3454 Tables in appendix C */ >+static const struct u32_range prohibited[] = { >+ /* C.2.1 ASCII control characters */ >+ { 0x0000, 0x001F }, >+ { 0x007F, 0x007F }, >+ /* C.2.2 Non-ASCII control characters */ >+ { 0x0080, 0x009F }, >+ { 0x06DD, 0x06DD }, >+ { 0x070F, 0x070F }, >+ { 0x180E, 0x180E }, >+ { 0x200C, 0x200C }, >+ { 0x200D, 0x200D }, >+ { 0x2028, 0x2028 }, >+ { 0x2029, 0x2029 }, >+ { 0x2060, 0x2060 }, >+ { 0x2061, 0x2061 }, >+ { 0x2062, 0x2062 }, >+ { 0x2063, 0x2063 }, >+ { 0x206A, 0x206F }, >+ { 0xFEFF, 0xFEFF }, >+ { 0xFFF9, 0xFFFC }, >+ { 0x1D173, 0x1D17A }, >+ /* C.3 Private use */ >+ { 0xE000, 0xF8FF }, >+ { 0xF0000, 0xFFFFD }, >+ { 0x100000, 0x10FFFD }, >+ /* C.4 Non-character code points */ >+ { 0xFDD0, 0xFDEF }, >+ { 0xFFFE, 0xFFFF }, >+ { 0x1FFFE, 0x1FFFF }, >+ { 0x2FFFE, 0x2FFFF }, >+ { 0x3FFFE, 0x3FFFF }, >+ { 0x4FFFE, 0x4FFFF }, >+ { 0x5FFFE, 0x5FFFF }, >+ { 0x6FFFE, 0x6FFFF }, >+ { 0x7FFFE, 0x7FFFF }, >+ { 0x8FFFE, 0x8FFFF }, >+ { 0x9FFFE, 0x9FFFF }, >+ { 0xAFFFE, 0xAFFFF }, >+ { 0xBFFFE, 0xBFFFF }, >+ { 0xCFFFE, 0xCFFFF }, >+ { 0xDFFFE, 0xDFFFF }, >+ { 0xEFFFE, 0xEFFFF }, >+ { 0xFFFFE, 0xFFFFF }, >+ { 0x10FFFE, 0x10FFFF }, >+ /* C.5 Surrogate codes */ >+ { 0xD800, 0xDFFF }, >+ /* C.6 Inappropriate for plain text */ >+ { 0xFFF9, 0xFFF9 }, >+ { 0xFFFA, 0xFFFA }, >+ { 0xFFFB, 0xFFFB }, >+ { 0xFFFC, 0xFFFC }, >+ { 0xFFFD, 0xFFFD }, >+ /* C.7 Inappropriate for canonical representation */ >+ { 0x2FF0, 0x2FFB }, >+ /* C.8 Change display properties or are deprecated */ >+ { 0x0340, 0x0340 }, >+ { 0x0341, 0x0341 }, >+ { 0x200E, 0x200E }, >+ { 0x200F, 0x200F }, >+ { 0x202A, 0x202A }, >+ { 0x202B, 0x202B }, >+ { 0x202C, 0x202C }, >+ { 0x202D, 0x202D }, >+ { 0x202E, 0x202E }, >+ { 0x206A, 0x206A }, >+ { 0x206B, 0x206B }, >+ { 0x206C, 0x206C }, >+ { 0x206D, 0x206D }, >+ { 0x206E, 0x206E }, >+ { 0x206F, 0x206F }, >+ /* C.9 Tagging characters */ >+ { 0xE0001, 0xE0001 }, >+ { 0xE0020, 0xE007F }, >+}; >+ >+static int >+code_in_table(u_int32_t c, const struct u32_range *table, size_t tlen) >+{ >+ const struct u32_range *e, *end = (void *)(tlen + (char *)table); >+ >+ for (e = table; e < end; e++) { >+ if (c >= e->lo && c <= e->hi) >+ return 1; >+ } >+ return 0; >+} >+ >+ >+int >+ssh_utf8_stringprep(const char *in, char *_out, size_t olen) >+{ >+ u_char *out = (u_char *)_out; >+ int state = 0; >+ size_t i, o, ilen = strlen(in); >+ u_int32_t c, e; >+ >+ e = c = 0; >+ for (i = o = 0; i < ilen; i++) { >+ e = (u_char)in[i]; >+ DBG(("top: i=%lu c=0x%02x e=0x%02x '%c' state=%d", >+ (u_long)i, c, e, isprint(e) ? e : ' ', state)); >+ /* Invalid code point state */ >+ if (state == -1) { >+ /* >+ * Continue eating continuation characters until >+ * a new start character comes along. >+ */ >+ if ((e & 0xc0) == 0x80) >+ continue; >+ state = 0; >+ } >+ >+ /* New code point state */ >+ if (state == 0) { >+ DBG(("new code point")); >+ if ((e & 0x80) == 0) { >+ /* 7 bit code point: skip to output */ >+ c = e & 0x7f; >+ DBG(("7 bit CP: c=0x%02x '%c'", >+ c, isprint(c) ? c : '.')); >+ goto encode; >+ } >+ if ((e & 0xe0) == 0xc0) { >+ /* 11 bit code point */ >+ state = 1; >+ c = (e & 0x1f) << 6; >+ DBG(("start 11 bit CP: c=0x%02x", c)); >+ if (c == 0) >+ goto bad_encoding; >+ continue; >+ } >+ if ((e & 0xf0) == 0xe0) { >+ /* 16 bit code point */ >+ state = 2; >+ c = (e & 0xf) << 12; >+ DBG(("start 16 bit CP: c=0x%02x", c)); >+ if (c == 0) >+ goto bad_encoding; >+ continue; >+ } >+ if ((e & 0xf8) == 0xf0) { >+ /* 21 bit code point */ >+ state = 3; >+ c = (e & 0x7) << 18; >+ DBG(("start 21 bit CP: c=0x%02x", c)); >+ if (c == 0) >+ goto bad_encoding; >+ continue; >+ } >+#if BANNED_RFC3629 >+ if ((e & 0xfc) == 0xf8) { >+ /* 26 bit code point */ >+ state = 4; >+ c = (e & 0x3) << 24; >+ DBG(("start 26 bit CP: c=0x%02x", c)); >+ if (c == 0) >+ goto bad_encoding; >+ continue; >+ } >+ if ((e & 0xfe) == 0xfc) { >+ /* 31 bit code point */ >+ state = 5; >+ c = (e & 0x1) << 30; >+ DBG(("start 31 bit CP: c=0x%02x", c)); >+ if (c == 0) >+ goto bad_encoding; >+ continue; >+ } >+#endif >+ bad_encoding: >+ DBG(("bad encoding")); >+ c = 0; >+ state = -1; >+ continue; >+ } >+ >+ /* In multibyte code point state */ >+ if (state < 1 || state > 5) { >+ DBG(("INTERNAL ERROR: invalid state %d", state)); >+ return -1; >+ } >+ DBG(("CP continuation; before c=0x%02x state=%d", c, state)); >+ state--; >+ c |= (e & 0x3f) << (state * 6); >+ DBG(("CP continuation; after c=0x%02x state=%d", c, state)); >+ if (state > 0) >+ continue; >+ >+ encode: >+ DBG(("CP done; final c=0x%02x", c)); >+ /* Character finished. prepare, encode and output */ >+ if (c == 0) >+ break; /* shouldn't happen */ >+ >+ /* RFC3629 bans codepoints > U+10FFFF */ >+ if (c > 0x10FFFF) { >+ DBG(("CP 0x%x > 0x10FFFF", c)); >+ goto bad_encoding; >+ } >+ /* Mapping */ >+ if (code_in_table(c, map_to_nothing, sizeof(map_to_nothing))) { >+ DBG(("CP 0x%x mapped to nothing", c)); >+ continue; >+ } >+ /* Prohibitied output */ >+ if (code_in_table(c, prohibited, sizeof(prohibited))) { >+ DBG(("CP 0x%x is in prohibited list", c)); >+ if (!code_in_table(c, whitelist, sizeof(whitelist))) >+ goto bad_encoding; >+ DBG(("CP 0x%x is in whitelist", c)); >+ } >+ >+ DBG(("encode CP 0x%x", c)); >+ if (c < 0x80) { >+ if (o + 1 > olen - 1) >+ return -1; >+ out[o++] = (char)c; >+ DBG(("CP 0x%x => %02x", c, out[o - 1])); >+ } else if (c < 0x800) { >+ if (o + 2 > olen - 1) >+ return -1; >+ out[o++] = (char)(0xc0 | (c >> 6)); >+ out[o++] = (char)(0x80 | (c & 0x3f)); >+ DBG(("CP 0x%x => %02x %02x", c, >+ out[o - 2], out[o - 1])); >+ } else if (c < 0x10000) { >+ if (o + 3 > olen - 1) >+ return -1; >+ out[o++] = (char)(0xe0 | (c >> 12)); >+ out[o++] = (char)(0x80 | ((c >> 6) & 0x3f)); >+ out[o++] = (char)(0x80 | (c & 0x3f)); >+ DBG(("CP 0x%x => %02x %02x %02x", c, >+ out[o - 3], out[o - 2], out[o - 1])); >+ } else if (c < 0x200000) { >+ if (o + 4 > olen - 1) >+ return -1; >+ out[o++] = (char)(0xf0 | (c >> 18)); >+ out[o++] = (char)(0x80 | ((c >> 12) & 0x3f)); >+ out[o++] = (char)(0x80 | ((c >> 6) & 0x3f)); >+ out[o++] = (char)(0x80 | (c & 0x3f)); >+ DBG(("CP 0x%x => %02x %02x %02x %02x", c, >+ out[o - 4], out[o - 3], out[o - 2], out[c - 1])); >+#if BANNED_RFC3629 >+ } else if (c < 0x4000000) { >+ if (o + 5 > olen - 1) >+ return -1; >+ out[o++] = (char)(0xf8 | (c >> 24)); >+ out[o++] = (char)(0x80 | ((c >> 18) & 0x3f)); >+ out[o++] = (char)(0x80 | ((c >> 12) & 0x3f)); >+ out[o++] = (char)(0x80 | ((c >> 6) & 0x3f)); >+ out[o++] = (char)(0x80 | (c & 0x3f)); >+ DBG(("CP 0x%x => %02x %02x %02x %02x %02x", c, >+ out[o - 5], out[o - 4], out[o - 3], out[c - 2], >+ out[o - 1])); >+ } else if (c < 0x80000000) { >+ if (o + 6 > olen - 1) >+ return -1; >+ out[o++] = (char)(0xfc | (c >> 30)); >+ out[o++] = (char)(0x80 | ((c >> 24) & 0x3f)); >+ out[o++] = (char)(0x80 | ((c >> 18) & 0x3f)); >+ out[o++] = (char)(0x80 | ((c >> 12) & 0x3f)); >+ out[o++] = (char)(0x80 | ((c >> 6) & 0x3f)); >+ out[o++] = (char)(0x80 | (c & 0x3f)); >+ DBG(("CP 0x%x => %02x %02x %02x %02x %02x %02x", c, >+ out[o - 6], out[o - 5], out[o - 4], out[c - 3], >+ out[o - 2], out[o - 1])); >+#endif >+ } else { >+ DBG(("INTERNAL ERROR: CP 0x%x not encodeable", c)); >+ return -1; >+ } >+ } >+ out[o] = '\0'; >+ return 0; >+} >+ >+#if 0 >+#include <err.h> >+ >+int >+main(int argc, char **argv) >+{ >+ char in[8192], out[8192]; >+ >+ while (fgets(in, sizeof(in), stdin) != NULL) { >+ if (ssh_utf8_stringprep(in, out, sizeof(out)) != 0) >+ errx(1, "bad input"); >+ fputs(out, stdout); >+ } >+ return 0; >+} >+#endif >Index: lib/Makefile >=================================================================== >RCS file: /cvs/src/usr.bin/ssh/lib/Makefile,v >retrieving revision 1.67 >diff -u -p -r1.67 Makefile >--- lib/Makefile 17 Jan 2013 23:00:01 -0000 1.67 >+++ lib/Makefile 30 May 2013 02:04:18 -0000 >@@ -13,7 +13,7 @@ SRCS= authfd.c authfile.c bufaux.c bufec > ssh-dss.c ssh-rsa.c ssh-ecdsa.c dh.c kexdh.c kexgex.c kexecdh.c \ > kexdhc.c kexgexc.c kexecdhc.c msg.c progressmeter.c dns.c \ > monitor_fdpass.c umac.c addrmatch.c schnorr.c jpake.c ssh-pkcs11.c \ >- krl.c >+ krl.c stringprep.c > > SRCS+= umac128.c > CLEANFILES+= umac128.c
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 2058
:
2205
|
2277
|
2280
|
2440