Force Turkish locales back to C/POSIX; bz#2643

Turkish locales are unique in their handling of the letters 'i' and
'I' (yes, they are different letters) and OpenSSH isn't remotely
prepared to deal with that. For now, the best we can do is to force
OpenSSH to use the C/POSIX locale and try to preserve the UTF-8
encoding if possible.

ok dtucker@
This commit is contained in:
Damien Miller 2016-12-12 13:57:10 +11:00
parent c35995048f
commit dda78a03af
5 changed files with 47 additions and 3 deletions

2
scp.c
View File

@ -379,7 +379,7 @@ main(int argc, char **argv)
/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
sanitise_stdfd();
setlocale(LC_CTYPE, "");
msetlocale();
/* Copy argv, because we modify it */
newargv = xcalloc(MAXIMUM(argc + 1, 1), sizeof(*newargv));

2
sftp.c
View File

@ -2272,7 +2272,7 @@ main(int argc, char **argv)
ssh_malloc_init(); /* must be called before any mallocs */
/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
sanitise_stdfd();
setlocale(LC_CTYPE, "");
msetlocale();
__progname = ssh_get_progname(argv[0]);
memset(&args, '\0', sizeof(args));

3
ssh.c
View File

@ -109,6 +109,7 @@
#include "version.h"
#include "ssherr.h"
#include "myproposal.h"
#include "utf8.h"
#ifdef ENABLE_PKCS11
#include "ssh-pkcs11.h"
@ -589,7 +590,7 @@ main(int ac, char **av)
*/
umask(022);
setlocale(LC_CTYPE, "");
msetlocale();
/*
* Initialize option structure to indicate that no values have been

42
utf8.c
View File

@ -27,6 +27,7 @@
# include <langinfo.h>
#endif
#include <limits.h>
#include <locale.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
@ -288,3 +289,44 @@ mprintf(const char *fmt, ...)
va_end(ap);
return ret;
}
/*
* Set up libc for multibyte output in the user's chosen locale.
*
* XXX: we are known to have problems with Turkish (i/I confusion) so we
* deliberately fall back to the C locale for now. Longer term we should
* always prefer to select C.[encoding] if possible, but there's no
* standardisation in locales between systems, so we'll need to survey
* what's out there first.
*/
void
msetlocale(void)
{
const char *vars[] = { "LC_ALL", "LC_CTYPE", "LANG", NULL };
char *cp;
int i;
/*
* We can't yet cope with dotless/dotted I in Turkish locales,
* so fall back to the C locale for these.
*/
for (i = 0; vars[i] != NULL; i++) {
if ((cp = getenv(vars[i])) == NULL)
continue;
if (strncasecmp(cp, "TR", 2) != 0)
break;
/*
* If we're in a UTF-8 locale then prefer to use
* the C.UTF-8 locale (or equivalent) if it exists.
*/
if ((strcasestr(cp, "UTF-8") != NULL ||
strcasestr(cp, "UTF8") != NULL) &&
(setlocale(LC_CTYPE, "C.UTF-8") != NULL ||
setlocale(LC_CTYPE, "POSIX.UTF-8") != NULL))
return;
setlocale(LC_CTYPE, "C");
return;
}
/* We can handle this locale */
setlocale(LC_CTYPE, "");
}

1
utf8.h
View File

@ -22,3 +22,4 @@ int fmprintf(FILE *, const char *, ...)
int vfmprintf(FILE *, const char *, va_list);
int snmprintf(char *, size_t, int *, const char *, ...)
__attribute__((format(printf, 4, 5)));
void msetlocale(void);