Christian Hesse
2015-Jul-06 10:05 UTC
[PATCH 1/1] paint visual host key with unicode box-drawing characters
From: Christian Hesse <mail at eworm.de> Signed-off-by: Christian Hesse <mail at eworm.de> --- sshkey.c | 47 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/sshkey.c b/sshkey.c index cfe5980..47511c2 100644 --- a/sshkey.c +++ b/sshkey.c @@ -44,6 +44,9 @@ #include <stdio.h> #include <string.h> #include <resolv.h> +#ifdef HAVE_LOCALE_H +#include <locale.h> +#endif /* HAVE_LOCALE_H */ #ifdef HAVE_UTIL_H #include <util.h> #endif /* HAVE_UTIL_H */ @@ -1088,6 +1091,12 @@ fingerprint_randomart(const char *alg, u_char *dgst_raw, size_t dgst_raw_len, * Chars to be used after each other every time the worm * intersects with itself. Matter of taste. */ +#ifdef HAVE_LOCALE_H + char *locale; + char *border_utf8[] = { "?", "?", "?", "?", "?", "?" }; +#endif + char *border_ascii[] = { "+", "-", "+", "|", "+", "+" }; + char **border; char *augmentation_string = " .o+=*BOX@%&#/^SE"; char *retval, *p, title[FLDSIZE_X], hash[FLDSIZE_X]; u_char field[FLDSIZE_X][FLDSIZE_Y]; @@ -1096,9 +1105,25 @@ fingerprint_randomart(const char *alg, u_char *dgst_raw, size_t dgst_raw_len, int x, y, r; size_t len = strlen(augmentation_string) - 1; - if ((retval = calloc((FLDSIZE_X + 3), (FLDSIZE_Y + 2))) == NULL) + if ((retval = malloc((FLDSIZE_X + 7) * FLDSIZE_Y + FLDSIZE_X * 3 * 2)) == NULL) return NULL; +#ifdef HAVE_LOCALE_H + /* initialize locale */ + setlocale(LC_ALL, ""); + + /* get locale for LC_CTYPE and decide about characters to use */ + locale = setlocale(LC_CTYPE, NULL); + if (locale != NULL && *locale != 0 && + (strstr(locale, "UTF-8") || + strstr(locale, "utf-8") || + strstr(locale, "UTF8") || + strstr(locale, "utf8"))) + border = border_utf8; + else +#endif + border = border_ascii; + /* initialize field */ memset(field, 0, FLDSIZE_X * FLDSIZE_Y * sizeof(char)); x = FLDSIZE_X / 2; @@ -1145,34 +1170,34 @@ fingerprint_randomart(const char *alg, u_char *dgst_raw, size_t dgst_raw_len, /* output upper border */ p = retval; - *p++ = '+'; + p += sprintf(p, "%s", border[0]); for (i = 0; i < (FLDSIZE_X - tlen) / 2; i++) - *p++ = '-'; + p += sprintf(p, "%s", border[1]); memcpy(p, title, tlen); p += tlen; for (i += tlen; i < FLDSIZE_X; i++) - *p++ = '-'; - *p++ = '+'; + p += sprintf(p, "%s", border[1]); + p += sprintf(p, "%s", border[2]); *p++ = '\n'; /* output content */ for (y = 0; y < FLDSIZE_Y; y++) { - *p++ = '|'; + p += sprintf(p, "%s", border[3]); for (x = 0; x < FLDSIZE_X; x++) *p++ = augmentation_string[MIN(field[x][y], len)]; - *p++ = '|'; + p += sprintf(p, "%s", border[3]); *p++ = '\n'; } /* output lower border */ - *p++ = '+'; + p += sprintf(p, "%s", border[4]); for (i = 0; i < (FLDSIZE_X - hlen) / 2; i++) - *p++ = '-'; + p += sprintf(p, "%s", border[1]); memcpy(p, hash, hlen); p += hlen; for (i += hlen; i < FLDSIZE_X; i++) - *p++ = '-'; - *p++ = '+'; + p += sprintf(p, "%s", border[1]); + p += sprintf(p, "%s", border[5]); return retval; } -- 2.4.5
Alex Bligh
2015-Jul-06 10:33 UTC
[PATCH 1/1] paint visual host key with unicode box-drawing characters
On 6 Jul 2015, at 11:05, Christian Hesse <list at eworm.de> wrote:> +#ifdef HAVE_LOCALE_H > + char *locale; > + char *border_utf8[] = { "?", "?", "?", "?", "?", "?" }; > +#endif > + char *border_ascii[] = { "+", "-", "+", "|", "+", "+" }; > + char **border;What if LOCALE_H is present at compile time but the binary is then run in a non-unicode locale? -- Alex Bligh
Christian Hesse
2015-Jul-06 10:42 UTC
[PATCH 1/1] paint visual host key with unicode box-drawing characters
Alex Bligh <alex at alex.org.uk> on Mon, 2015/07/06 11:33:> > On 6 Jul 2015, at 11:05, Christian Hesse <list at eworm.de> wrote: > > > +#ifdef HAVE_LOCALE_H > > + char *locale; > > + char *border_utf8[] = { "?", "?", "?", "?", "?", "?" }; > > +#endif > > + char *border_ascii[] = { "+", "-", "+", "|", "+", "+" }; > > + char **border; > > What if LOCALE_H is present at compile time but the binary is then > run in a non-unicode locale?See below. setlocale() should return something without "utf-8" in its name, so the binary should select the ascii characters. This still has a serve issue, though: It works perfectly with ssh-keygen but breaks for ssh... I will have a look at that. However this is more like RFC, so comments are welcome. -- main(a){char*c=/* Schoene Gruesse */"B?IJj;MEH" "CX:;",b;for(a/* Chris get my mail address: */=0;b=c[a++];) putchar(b-1/(/* gcc -o sig sig.c && ./sig */b/42*2-3)*42);} -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 473 bytes Desc: OpenPGP digital signature URL: <http://lists.mindrot.org/pipermail/openssh-unix-dev/attachments/20150706/93156b6f/attachment-0001.bin>
Aris Adamantiadis
2015-Jul-06 11:22 UTC
[PATCH 1/1] paint visual host key with unicode box-drawing characters
Le 06/07/15 12:33, Alex Bligh a ?crit :> On 6 Jul 2015, at 11:05, Christian Hesse <list at eworm.de> wrote: > >> +#ifdef HAVE_LOCALE_H >> + char *locale; >> + char *border_utf8[] = { "?", "?", "?", "?", "?", "?" }; >> +#endif >> + char *border_ascii[] = { "+", "-", "+", "|", "+", "+" }; >> + char **border; > What if LOCALE_H is present at compile time but the binary is then > run in a non-unicode locale? >I'm more concerned about the UTF-8 in the source code... are all compilers able to deal with that? Simple workaround would be to use hex encoding instead. Aris
Roland Mainz
2015-Jul-07 14:25 UTC
[PATCH 1/1] paint visual host key with unicode box-drawing characters
On Mon, Jul 6, 2015 at 12:05 PM, Christian Hesse <list at eworm.de> wrote:> From: Christian Hesse <mail at eworm.de> > > Signed-off-by: Christian Hesse <mail at eworm.de> > --- > sshkey.c | 47 ++++++++++++++++++++++++++++++++++++----------- > 1 file changed, 36 insertions(+), 11 deletions(-) > > diff --git a/sshkey.c b/sshkey.c > index cfe5980..47511c2 100644 > --- a/sshkey.c > +++ b/sshkey.c > @@ -44,6 +44,9 @@ > #include <stdio.h> > #include <string.h> > #include <resolv.h> > +#ifdef HAVE_LOCALE_H > +#include <locale.h> > +#endif /* HAVE_LOCALE_H */ > #ifdef HAVE_UTIL_H > #include <util.h> > #endif /* HAVE_UTIL_H */ > @@ -1088,6 +1091,12 @@ fingerprint_randomart(const char *alg, u_char *dgst_raw, size_t dgst_raw_len, > * Chars to be used after each other every time the worm > * intersects with itself. Matter of taste. > */ > +#ifdef HAVE_LOCALE_H > + char *locale; > + char *border_utf8[] = { "?", "?", "?", "?", "?", "?" }; > +#endif > + char *border_ascii[] = { "+", "-", "+", "|", "+", "+" }; > + char **border; > char *augmentation_string = " .o+=*BOX@%&#/^SE"; > char *retval, *p, title[FLDSIZE_X], hash[FLDSIZE_X]; > u_char field[FLDSIZE_X][FLDSIZE_Y]; > @@ -1096,9 +1105,25 @@ fingerprint_randomart(const char *alg, u_char *dgst_raw, size_t dgst_raw_len, > int x, y, r; > size_t len = strlen(augmentation_string) - 1; > > - if ((retval = calloc((FLDSIZE_X + 3), (FLDSIZE_Y + 2))) == NULL) > + if ((retval = malloc((FLDSIZE_X + 7) * FLDSIZE_Y + FLDSIZE_X * 3 * 2)) == NULL) > return NULL; > > +#ifdef HAVE_LOCALE_H > + /* initialize locale */ > + setlocale(LC_ALL, ""); > + > + /* get locale for LC_CTYPE and decide about characters to use */ > + locale = setlocale(LC_CTYPE, NULL); > + if (locale != NULL && *locale != 0 && > + (strstr(locale, "UTF-8") || > + strstr(locale, "utf-8") || > + strstr(locale, "UTF8") || > + strstr(locale, "utf8"))) > + border = border_utf8; > + else > +#endif > + border = border_ascii; > + > /* initialize field */ > memset(field, 0, FLDSIZE_X * FLDSIZE_Y * sizeof(char)); > x = FLDSIZE_X / 2; > @@ -1145,34 +1170,34 @@ fingerprint_randomart(const char *alg, u_char *dgst_raw, size_t dgst_raw_len, > > /* output upper border */ > p = retval; > - *p++ = '+'; > + p += sprintf(p, "%s", border[0]); > for (i = 0; i < (FLDSIZE_X - tlen) / 2; i++) > - *p++ = '-'; > + p += sprintf(p, "%s", border[1]); > memcpy(p, title, tlen); > p += tlen; > for (i += tlen; i < FLDSIZE_X; i++) > - *p++ = '-'; > - *p++ = '+'; > + p += sprintf(p, "%s", border[1]); > + p += sprintf(p, "%s", border[2]); > *p++ = '\n'; > > /* output content */ > for (y = 0; y < FLDSIZE_Y; y++) { > - *p++ = '|'; > + p += sprintf(p, "%s", border[3]); > for (x = 0; x < FLDSIZE_X; x++) > *p++ = augmentation_string[MIN(field[x][y], len)]; > - *p++ = '|'; > + p += sprintf(p, "%s", border[3]); > *p++ = '\n'; > } > > /* output lower border */ > - *p++ = '+'; > + p += sprintf(p, "%s", border[4]); > for (i = 0; i < (FLDSIZE_X - hlen) / 2; i++) > - *p++ = '-'; > + p += sprintf(p, "%s", border[1]); > memcpy(p, hash, hlen); > p += hlen; > for (i += hlen; i < FLDSIZE_X; i++) > - *p++ = '-'; > - *p++ = '+'; > + p += sprintf(p, "%s", border[1]); > + p += sprintf(p, "%s", border[5]); > > return retval; > }General comments: 1. Not all locales use UTF-8 as encoding but can still use the Unicode characters you use (e.g. GB18030 is a modern example and it's use is mandated by all software vendors in PRC China). A quick solution is to use |iconv()| to convert the UTF-8 byte sequences to the local encoding (see http://svn.nrubsig.org/svn/people/gisburn/code/ucs4towchar_t/ucs4towchar_t.c - that could should be easy to modify). Note that if |iconf()| produces an empty string in a character-by-character conversion it means that the destination locale cannot represent that character in the local encoding (you have to fall-back to the ASCII representation then (this will also eliminate the need for the |setlocale()|&&co. testing)). 2. UTF-8 sequences in ISO C code are not portable and a lot of compilers will choke on that (e.g. if they are in a non-UTF-8 locale like "C", "POSIX" or any non-UTF-8 multibyte locale). Correct fix would be to provide the UTF-8 byte sequences for the characters as plain C strings escaped in octal or hexadecimal notation (and then squish them through |iconf()|) 3. Not all UTF-8 locales (or locale aliases) have "UTF-8" in their name (for example the name "en_US" is allowed to be an alias for "en_US.UTF-8" (this quickly becomes messy in the Chinese/Japanese environments, e.g. where "ja_JP" can be anything from ja_JP.PCK to ja_JP.UTF-8)) ---- Bye, Roland -- __ . . __ (o.\ \/ /.o) roland.mainz at nrubsig.org \__\/\/__/ MPEG specialist, C&&JAVA&&Sun&&Unix programmer /O /==\ O\ TEL +49 641 3992797 (;O/ \/ \O;)
Gert Doering
2015-Jul-07 14:52 UTC
[PATCH 1/1] paint visual host key with unicode box-drawing characters
Hi, On Tue, Jul 07, 2015 at 04:25:25PM +0200, Roland Mainz wrote:> General comments: > 1. Not all locales use UTF-8 as encoding but can still use the Unicode > characters you use (e.g. GB18030 is a modern example and it's use is > mandated by all software vendors in PRC China). A quick solution is to > use |iconv()| to convert the UTF-8 byte sequences to the local > encoding (see http://svn.nrubsig.org/svn/people/gisburn/code/ucs4towchar_t/ucs4towchar_t.cSo the suggestion is to use another external dependency (and one that updates quite frequently to the great joy of admins everywhere) just to paint some visual sugar which works about as well in ASCII... ... wtf? The whole idea of using UTF8 in a single place inside OpenSSH just for eye candy needs to be burnt in flames. gert -- USENET is *not* the non-clickable part of WWW! //www.muc.de/~gert/ Gert Doering - Munich, Germany gert at greenie.muc.de fax: +49-89-35655025 gert at net.informatik.tu-muenchen.de
lists at wrant.com
2015-Jul-07 15:15 UTC
[PATCH 1/1] paint visual host key with unicode box-drawing characters
What is the original purpose and main goal of this patch beyond the obvious "visual appeal"?
Christian Hesse
2015-Jul-08 08:21 UTC
[PATCH 1/1] paint visual host key with unicode box-drawing characters
Roland Mainz <roland.mainz at nrubsig.org> on Tue, 2015/07/07 16:25:> General comments: > 1. Not all locales use UTF-8 as encoding but can still use the Unicode > characters you use (e.g. GB18030 is a modern example and it's use is > mandated by all software vendors in PRC China). A quick solution is to > use |iconv()| to convert the UTF-8 byte sequences to the local > encoding (see > http://svn.nrubsig.org/svn/people/gisburn/code/ucs4towchar_t/ucs4towchar_t.c > - that could should be easy to modify). Note that if |iconf()| > produces an empty string in a character-by-character conversion it > means that the destination locale cannot represent that character in > the local encoding (you have to fall-back to the ASCII representation > then (this will also eliminate the need for the |setlocale()|&&co. > testing)). > > 2. UTF-8 sequences in ISO C code are not portable and a lot of > compilers will choke on that (e.g. if they are in a non-UTF-8 locale > like "C", "POSIX" or any non-UTF-8 multibyte locale). Correct fix > would be to provide the UTF-8 byte sequences for the characters as > plain C strings escaped in octal or hexadecimal notation (and then > squish them through |iconf()|) > > 3. Not all UTF-8 locales (or locale aliases) have "UTF-8" in their > name (for example the name "en_US" is allowed to be an alias for > "en_US.UTF-8" (this quickly becomes messy in the Chinese/Japanese > environments, e.g. where "ja_JP" can be anything from ja_JP.PCK to > ja_JP.UTF-8))Ok, this adds some more complexity. Looks like we can not just use a given value to get it right in all cases (for all locales, encodings, ...). This code goes though a loop to calculate the needed byte sequences once. Does that work everywhere? Probably we have to fiddle with header files. Not all systems do have iconv.h and langinfo.h (and corresponding libraries), no? diff --git a/log.c b/log.c index 32e1d2e..7463617 100644 --- a/log.c +++ b/log.c @@ -444,8 +444,9 @@ do_log(LogLevel level, const char *fmt, va_list args) tmp_handler(level, fmtbuf, log_handler_ctx); log_handler = tmp_handler; } else if (log_on_stderr) { - snprintf(msgbuf, sizeof msgbuf, "%s\r\n", fmtbuf); + /* we want unicode multi byte characters, so do not use fmtbuf here */ (void)write(log_stderr_fd, msgbuf, strlen(msgbuf)); + (void)write(log_stderr_fd, "\r\n", 2); } else { #if defined(HAVE_OPENLOG_R) && defined(SYSLOG_DATA_INIT) openlog_r(argv0 ? argv0 : __progname, LOG_PID, log_facility, &sdata); diff --git a/sshkey.c b/sshkey.c index cfe5980..2f5a2f7 100644 --- a/sshkey.c +++ b/sshkey.c @@ -44,6 +44,12 @@ #include <stdio.h> #include <string.h> #include <resolv.h> +#ifdef HAVE_LOCALE_H +#include <locale.h> +#include <unistd.h> +#include <iconv.h> +#include <langinfo.h> +#endif /* HAVE_LOCALE_H */ #ifdef HAVE_UTIL_H #include <util.h> #endif /* HAVE_UTIL_H */ @@ -1088,17 +1094,75 @@ fingerprint_randomart(const char *alg, u_char *dgst_raw, size_t dgst_raw_len, * Chars to be used after each other every time the worm * intersects with itself. Matter of taste. */ + char *border_ascii[] = { "+", "-", "[", "]", "+", "|", "+", "+" }; + char **border; char *augmentation_string = " .o+=*BOX@%&#/^SE"; - char *retval, *p, title[FLDSIZE_X], hash[FLDSIZE_X]; + char *retval, *p, title[FLDSIZE_X - 2], hash[FLDSIZE_X - 2]; u_char field[FLDSIZE_X][FLDSIZE_Y]; size_t i, tlen, hlen; u_int b; int x, y, r; size_t len = strlen(augmentation_string) - 1; - if ((retval = calloc((FLDSIZE_X + 3), (FLDSIZE_Y + 2))) == NULL) + if ((retval = malloc((FLDSIZE_X + 7) * FLDSIZE_Y + FLDSIZE_X * 3 * 2)) == NULL) return NULL; +#ifdef HAVE_LOCALE_H + iconv_t cd; + /* unicode character codes for box drawing + * http://unicode.org/charts/PDF/U2500.pdf */ + uint32_t border_unicode[] = { + 0x250c, /* ? upper left */ + 0x2500, /* ? horizontal */ + 0x2524, /* ? left of title/hash */ + 0x251c, /* ? right of title/hash */ + 0x2510, /* ? upper right */ + 0x2502, /* ? vertical */ + 0x2514, /* ? lower left */ + 0x2518 /* ? lower right */ }; + /* border_buffer is array of array of char + * we use this to have statically allocated buffer */ + char border_buffer[8][5]; + /* border_encoded is array of pointer to char */ + char *border_encoded[8]; + + if (isatty(fileno(stdout)) == 1) { + /* initialize locale */ + setlocale(LC_ALL, ""); + +#if __BYTE_ORDER == __LITTLE_ENDIAN + cd = iconv_open(nl_langinfo(CODESET), "UTF32LE"); +#elif __BYTE_ORDER == __BIG_ENDIAN + cd = iconv_open(nl_langinfo(CODESET), "UTF32BE"); +#else +#error Unknown __BYTE_ORDER +#endif + + /* encode the border elements */ + for (int i = 0; i < 8; i++) { + size_t in_size = sizeof(uint32_t);; + size_t out_size = sizeof(border_buffer[i]); + char *input = (char *) &border_unicode[i]; + char *output = border_buffer[i]; + + memset(border_buffer[i], 0, out_size); + iconv(cd, &input, &in_size, &output, &out_size); + + /* if iconv() was successful we have a string with non-zero length + * fall back to ascii otherwise */ + if (border_buffer[i][0] != 0) + border_encoded[i] = border_buffer[i]; + else + border_encoded[i] = border_ascii[i]; + } + + iconv_close(cd); + + border = border_encoded; + } else +#endif + border = border_ascii; + /* initialize field */ memset(field, 0, FLDSIZE_X * FLDSIZE_Y * sizeof(char)); x = FLDSIZE_X / 2; @@ -1132,47 +1196,51 @@ fingerprint_randomart(const char *alg, u_char *dgst_raw, size_t dgst_raw_len, field[x][y] = len; /* assemble title */ - r = snprintf(title, sizeof(title), "[%s %u]", + r = snprintf(title, sizeof(title), "%s %u", sshkey_type(k), sshkey_size(k)); - /* If [type size] won't fit, then try [type]; fits "[ED25519-CERT]" */ + /* If "type size" won't fit, then try "type"; fits "ED25519-CERT" */ if (r < 0 || r > (int)sizeof(title)) - r = snprintf(title, sizeof(title), "[%s]", sshkey_type(k)); + r = snprintf(title, sizeof(title), "%s", sshkey_type(k)); tlen = (r <= 0) ? 0 : strlen(title); /* assemble hash ID. */ - r = snprintf(hash, sizeof(hash), "[%s]", alg); + r = snprintf(hash, sizeof(hash), "%s", alg); hlen = (r <= 0) ? 0 : strlen(hash); /* output upper border */ p = retval; - *p++ = '+'; - for (i = 0; i < (FLDSIZE_X - tlen) / 2; i++) - *p++ = '-'; + p += sprintf(p, "%s", border[0]); + for (i = 0; i < (FLDSIZE_X - tlen - 2) / 2; i++) + p += sprintf(p, "%s", border[1]); + p += sprintf(p, "%s", border[2]); memcpy(p, title, tlen); p += tlen; - for (i += tlen; i < FLDSIZE_X; i++) - *p++ = '-'; - *p++ = '+'; + p += sprintf(p, "%s", border[3]); + for (i += tlen + 2; i < FLDSIZE_X; i++) + p += sprintf(p, "%s", border[1]); + p += sprintf(p, "%s", border[4]); *p++ = '\n'; /* output content */ for (y = 0; y < FLDSIZE_Y; y++) { - *p++ = '|'; + p += sprintf(p, "%s", border[5]); for (x = 0; x < FLDSIZE_X; x++) *p++ = augmentation_string[MIN(field[x][y], len)]; - *p++ = '|'; + p += sprintf(p, "%s", border[5]); *p++ = '\n'; } /* output lower border */ - *p++ = '+'; - for (i = 0; i < (FLDSIZE_X - hlen) / 2; i++) - *p++ = '-'; + p += sprintf(p, "%s", border[6]); + for (i = 0; i < (FLDSIZE_X - hlen - 2) / 2; i++) + p += sprintf(p, "%s", border[1]); + p += sprintf(p, "%s", border[2]); memcpy(p, hash, hlen); p += hlen; - for (i += hlen; i < FLDSIZE_X; i++) - *p++ = '-'; - *p++ = '+'; + p += sprintf(p, "%s", border[3]); + for (i += hlen + 2; i < FLDSIZE_X; i++) + p += sprintf(p, "%s", border[1]); + p += sprintf(p, "%s", border[7]); return retval; } -- main(a){char*c=/* Schoene Gruesse */"B?IJj;MEH" "CX:;",b;for(a/* Chris get my mail address: */=0;b=c[a++];) putchar(b-1/(/* gcc -o sig sig.c && ./sig */b/42*2-3)*42);} -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 473 bytes Desc: OpenPGP digital signature URL: <http://lists.mindrot.org/pipermail/openssh-unix-dev/attachments/20150708/87c15d24/attachment-0001.bin>
Reasonably Related Threads
- [PATCH 1/1] paint visual host key with unicode box-drawing characters
- [Bug 826] RFE: scp and ssh should have an option to set the group-id at login time
- Need Help to Fix CVE-2008-1483, CVE-2008-5161, CVE-2015-5600 and CVE-2015-6565
- Status of OpenSSL 1.1 support
- SFTP support for subsecond times