View | Details | Raw Unified | Return to bug 2058 | Differences between
and this patch

Collapse All | Expand All

(-)sshconnect2.c (-2 / +34 lines)
Lines 39-44 Link Here
39
#include <pwd.h>
39
#include <pwd.h>
40
#include <unistd.h>
40
#include <unistd.h>
41
#include <vis.h>
41
#include <vis.h>
42
#include <locale.h>
43
#include <langinfo.h>
42
44
43
#include "xmalloc.h"
45
#include "xmalloc.h"
44
#include "ssh.h"
46
#include "ssh.h"
Lines 462-482 input_userauth_error(int type, u_int32_t Link Here
462
	    "type %d", type);
464
	    "type %d", type);
463
}
465
}
464
466
467
/* Check whether we can display UTF-8 safely */
468
static int
469
utf8_ok(void)
470
{
471
	static int ret = -1;
472
	char *cp;
473
474
	if (ret == -1) {
475
		setlocale(LC_CTYPE, "");
476
		cp = nl_langinfo(CODESET);
477
		ret = strcmp(cp, "UTF-8") == 0;
478
	}
479
	return ret;
480
}
481
465
/* ARGSUSED */
482
/* ARGSUSED */
466
void
483
void
467
input_userauth_banner(int type, u_int32_t seq, void *ctxt)
484
input_userauth_banner(int type, u_int32_t seq, void *ctxt)
468
{
485
{
469
	char *msg, *raw, *lang;
486
	char *msg, *raw, *lang;
470
	u_int len;
487
	u_int done, len;
471
488
472
	debug3("input_userauth_banner");
489
	debug3("input_userauth_banner");
490
473
	raw = packet_get_string(&len);
491
	raw = packet_get_string(&len);
474
	lang = packet_get_string(NULL);
492
	lang = packet_get_string(NULL);
475
	if (len > 0 && options.log_level >= SYSLOG_LEVEL_INFO) {
493
	if (len > 0 && options.log_level >= SYSLOG_LEVEL_INFO) {
476
		if (len > 65536)
494
		if (len > 65536)
477
			len = 65536;
495
			len = 65536;
478
		msg = xmalloc(len * 4 + 1); /* max expansion from strnvis() */
496
		msg = xmalloc(len * 4 + 1); /* max expansion from strnvis() */
479
		strnvis(msg, raw, len * 4 + 1, VIS_SAFE|VIS_OCTAL|VIS_NOSLASH);
497
		done = 0;
498
		if (utf8_ok()) {
499
			if (ssh_utf8_stringprep(raw, msg, len * 4 + 1) == 0)
500
				done = 1;
501
			else
502
				debug2("%s: UTF8 stringprep failed", __func__);
503
		}
504
		/*
505
		 * Fallback to strnvis if UTF8 display not supported or
506
		 * conversion failed.
507
		 */
508
		if (!done) {
509
			strnvis(msg, raw, len * 4 + 1,
510
			    VIS_SAFE|VIS_OCTAL|VIS_NOSLASH);
511
		}
480
		fprintf(stderr, "%s", msg);
512
		fprintf(stderr, "%s", msg);
481
		free(msg);
513
		free(msg);
482
	}
514
	}
(-)misc.h (+3 lines)
Lines 102-105 char *read_passphrase(const char *, int) Link Here
102
int	 ask_permission(const char *, ...) __attribute__((format(printf, 1, 2)));
102
int	 ask_permission(const char *, ...) __attribute__((format(printf, 1, 2)));
103
int	 read_keyfile_line(FILE *, const char *, char *, size_t, u_long *);
103
int	 read_keyfile_line(FILE *, const char *, char *, size_t, u_long *);
104
104
105
/* stringprep.c */
106
int ssh_utf8_stringprep(const char *, char *, size_t);
107
105
#endif /* _MISC_H */
108
#endif /* _MISC_H */
(-)stringprep.c (+370 lines)
Added Link Here
1
/*
2
 * Copyright (c) 2013 Damien Miller <djm@mindrot.org>
3
 *
4
 * Permission to use, copy, modify, and distribute this software for any
5
 * purpose with or without fee is hereby granted, provided that the above
6
 * copyright notice and this permission notice appear in all copies.
7
 *
8
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15
 */
16
17
/*
18
 * This is a simple RFC3454 stringprep profile to normalise UTF-8
19
 * strings from untrusted sources.
20
 *
21
 * It is intended to be used prior to display of untrusted strings only.
22
 * It should not be used for logging because of bi-di ambiguity. It
23
 * should also not be used in any case where lack of normalisation may
24
 * cause problems.
25
 *
26
 * This profile supports Unicode 3.2 using the RFC3454 tables as listed
27
 * below, with an additional whitelist of whitespace characters (\n, \a
28
 * and \t). Unicode normalisation and bi-di testing are not used.
29
 */
30
31
#include <sys/types.h>
32
#include <stdio.h>
33
#include <stdlib.h>
34
#include <string.h>
35
#include <limits.h>
36
#include <ctype.h>
37
38
#include "misc.h"
39
40
#define BANNED_RFC3629 0
41
#define UTF8_DEBUG 0
42
43
#if UTF8_DEBUG
44
# define DBG(x) \
45
	do { \
46
		printf("%s:%d %s: ", __FILE__, __LINE__, __func__); \
47
		printf x; \
48
		printf("\n"); \
49
		fflush(stdout); \
50
	} while (0)
51
#else
52
# define DBG(x)
53
#endif
54
55
struct u32_range {
56
	u_int32_t lo, hi;  /* Inclusive */
57
};
58
59
/* RFC3454 Table B.1 */
60
static const struct u32_range map_to_nothing[] = {
61
	{ 0x00AD, 0x00AD },
62
	{ 0x034F, 0x034F },
63
	{ 0x1806, 0x1806 },
64
	{ 0x180B, 0x180D },
65
	{ 0x200B, 0x200D },
66
	{ 0x2060, 0x2060 },
67
	{ 0xFE00, 0xFE0F },
68
	{ 0xFEFF, 0xFEFF },
69
};
70
71
/* Local: allow tab, CR and LF */
72
static const struct u32_range whitelist[] = {
73
	{ 0x09, 0x00 },
74
	{ 0x0a, 0x0a },
75
	{ 0x0d, 0x0d },
76
};
77
78
/* RFC3454 Tables in appendix C */
79
static const struct u32_range prohibited[] = {
80
	/* C.2.1 ASCII control characters */
81
	{ 0x0000, 0x001F },
82
	{ 0x007F, 0x007F },
83
	/* C.2.2 Non-ASCII control characters */
84
	{ 0x0080, 0x009F },
85
	{ 0x06DD, 0x06DD },
86
	{ 0x070F, 0x070F },
87
	{ 0x180E, 0x180E },
88
	{ 0x200C, 0x200C },
89
	{ 0x200D, 0x200D },
90
	{ 0x2028, 0x2028 },
91
	{ 0x2029, 0x2029 },
92
	{ 0x2060, 0x2060 },
93
	{ 0x2061, 0x2061 },
94
	{ 0x2062, 0x2062 },
95
	{ 0x2063, 0x2063 },
96
	{ 0x206A, 0x206F },
97
	{ 0xFEFF, 0xFEFF },
98
	{ 0xFFF9, 0xFFFC },
99
	{ 0x1D173, 0x1D17A },
100
	/* C.3 Private use */
101
	{ 0xE000, 0xF8FF },
102
	{ 0xF0000, 0xFFFFD },
103
	{ 0x100000, 0x10FFFD },
104
	/* C.4 Non-character code points */
105
	{ 0xFDD0, 0xFDEF },
106
	{ 0xFFFE, 0xFFFF },
107
	{ 0x1FFFE, 0x1FFFF },
108
	{ 0x2FFFE, 0x2FFFF },
109
	{ 0x3FFFE, 0x3FFFF },
110
	{ 0x4FFFE, 0x4FFFF },
111
	{ 0x5FFFE, 0x5FFFF },
112
	{ 0x6FFFE, 0x6FFFF },
113
	{ 0x7FFFE, 0x7FFFF },
114
	{ 0x8FFFE, 0x8FFFF },
115
	{ 0x9FFFE, 0x9FFFF },
116
	{ 0xAFFFE, 0xAFFFF },
117
	{ 0xBFFFE, 0xBFFFF },
118
	{ 0xCFFFE, 0xCFFFF },
119
	{ 0xDFFFE, 0xDFFFF },
120
	{ 0xEFFFE, 0xEFFFF },
121
	{ 0xFFFFE, 0xFFFFF },
122
	{ 0x10FFFE, 0x10FFFF },
123
	/* C.5 Surrogate codes */
124
	{ 0xD800, 0xDFFF },
125
	/* C.6 Inappropriate for plain text */
126
	{ 0xFFF9, 0xFFF9 },
127
	{ 0xFFFA, 0xFFFA },
128
	{ 0xFFFB, 0xFFFB },
129
	{ 0xFFFC, 0xFFFC },
130
	{ 0xFFFD, 0xFFFD },
131
	/* C.7 Inappropriate for canonical representation */
132
	{ 0x2FF0, 0x2FFB },
133
	/* C.8 Change display properties or are deprecated */
134
	{ 0x0340, 0x0340 },
135
	{ 0x0341, 0x0341 },
136
	{ 0x200E, 0x200E },
137
	{ 0x200F, 0x200F },
138
	{ 0x202A, 0x202A },
139
	{ 0x202B, 0x202B },
140
	{ 0x202C, 0x202C },
141
	{ 0x202D, 0x202D },
142
	{ 0x202E, 0x202E },
143
	{ 0x206A, 0x206A },
144
	{ 0x206B, 0x206B },
145
	{ 0x206C, 0x206C },
146
	{ 0x206D, 0x206D },
147
	{ 0x206E, 0x206E },
148
	{ 0x206F, 0x206F },
149
	/* C.9 Tagging characters */
150
	{ 0xE0001, 0xE0001 },
151
	{ 0xE0020, 0xE007F },
152
};
153
154
static int
155
code_in_table(u_int32_t c, const struct u32_range *table, size_t tlen)
156
{
157
	const struct u32_range *e, *end = (void *)(tlen + (char *)table);
158
159
	for (e = table; e < end; e++) {
160
		if (c >= e->lo && c <= e->hi)
161
			return 1;
162
	}
163
	return 0;
164
}
165
166
167
int
168
ssh_utf8_stringprep(const char *in, char *_out, size_t olen)
169
{
170
	u_char *out = (u_char *)_out;
171
	int state = 0;
172
	size_t i, o, ilen = strlen(in);
173
	u_int32_t c, e;
174
175
	e = c = 0;
176
	for (i = o = 0; i < ilen; i++) {
177
		e = (u_char)in[i];
178
		DBG(("top: i=%lu c=0x%02x e=0x%02x '%c' state=%d",
179
		    (u_long)i, c, e, isprint(e) ? e : ' ', state));
180
		/* Invalid code point state */
181
		if (state == -1) {
182
			/*
183
			 * Continue eating continuation characters until
184
			 * a new start character comes along.
185
			 */
186
			if ((e & 0xc0) == 0x80)
187
				continue;
188
			state = 0;
189
		}
190
191
		/* New code point state */
192
		if (state == 0) {
193
			DBG(("new code point"));
194
			if ((e & 0x80) == 0) {
195
				/* 7 bit code point: skip to output */
196
				c = e & 0x7f;
197
				DBG(("7 bit CP: c=0x%02x '%c'",
198
				    c, isprint(c) ? c : '.'));
199
				goto encode;
200
			}
201
			if ((e & 0xe0) == 0xc0) {
202
				/* 11 bit code point */
203
				state = 1;
204
				c = (e & 0x1f) << 6;
205
				DBG(("start 11 bit CP: c=0x%02x", c));
206
				if (c == 0)
207
					goto bad_encoding;
208
				continue;
209
			}
210
			if ((e & 0xf0) == 0xe0) {
211
				/* 16 bit code point */
212
				state = 2;
213
				c = (e & 0xf) << 12;
214
				DBG(("start 16 bit CP: c=0x%02x", c));
215
				if (c == 0)
216
					goto bad_encoding;
217
				continue;
218
			}
219
			if ((e & 0xf8) == 0xf0) {
220
				/* 21 bit code point */
221
				state = 3;
222
				c = (e & 0x7) << 18;
223
				DBG(("start 21 bit CP: c=0x%02x", c));
224
				if (c == 0)
225
					goto bad_encoding;
226
				continue;
227
			}
228
#if BANNED_RFC3629
229
			if ((e & 0xfc) == 0xf8) {
230
				/* 26 bit code point */
231
				state = 4;
232
				c = (e & 0x3) << 24;
233
				DBG(("start 26 bit CP: c=0x%02x", c));
234
				if (c == 0)
235
					goto bad_encoding;
236
				continue;
237
			}
238
			if ((e & 0xfe) == 0xfc) {
239
				/* 31 bit code point */
240
				state = 5;
241
				c = (e & 0x1) << 30;
242
				DBG(("start 31 bit CP: c=0x%02x", c));
243
				if (c == 0)
244
					goto bad_encoding;
245
				continue;
246
			}
247
#endif
248
 bad_encoding:
249
			DBG(("bad encoding"));
250
			c = 0;
251
			state = -1;
252
			continue;
253
		}
254
255
		/* In multibyte code point state */
256
		if (state < 1 || state > 5) {
257
			DBG(("INTERNAL ERROR: invalid state %d", state));
258
			return -1;
259
		}
260
		DBG(("CP continuation; before c=0x%02x state=%d", c, state));
261
		state--;
262
		c |= (e & 0x3f) << (state * 6);	
263
		DBG(("CP continuation; after c=0x%02x state=%d", c, state));
264
		if (state > 0)
265
			continue;
266
267
 encode:
268
		DBG(("CP done; final c=0x%02x", c));
269
		/* Character finished. prepare, encode and output */
270
		if (c == 0)
271
			break; /* shouldn't happen */
272
273
		/* RFC3629 bans codepoints > U+10FFFF */
274
		if (c > 0x10FFFF) {
275
			DBG(("CP 0x%x > 0x10FFFF", c));
276
			goto bad_encoding;
277
		}
278
		/* Mapping */
279
		if (code_in_table(c, map_to_nothing, sizeof(map_to_nothing))) {
280
			DBG(("CP 0x%x mapped to nothing", c));
281
			continue;
282
		}
283
		/* Prohibitied output */
284
		if (code_in_table(c, prohibited, sizeof(prohibited))) {
285
			DBG(("CP 0x%x is in prohibited list", c));
286
			if (!code_in_table(c, whitelist, sizeof(whitelist)))
287
				goto bad_encoding;
288
			DBG(("CP 0x%x is in whitelist", c));
289
		}
290
291
		DBG(("encode CP 0x%x", c));
292
		if (c < 0x80) {
293
			if (o + 1 > olen - 1)
294
				return -1;
295
			out[o++] = (char)c;
296
			DBG(("CP 0x%x => %02x", c, out[o - 1]));
297
		} else if (c < 0x800) {
298
			if (o + 2 > olen - 1)
299
				return -1;
300
			out[o++] = (char)(0xc0 | (c >> 6));
301
			out[o++] = (char)(0x80 | (c & 0x3f));
302
			DBG(("CP 0x%x => %02x %02x", c,
303
			    out[o - 2], out[o - 1]));
304
		} else if (c < 0x10000) {
305
			if (o + 3 > olen - 1)
306
				return -1;
307
			out[o++] = (char)(0xe0 | (c >> 12));
308
			out[o++] = (char)(0x80 | ((c >> 6) & 0x3f));
309
			out[o++] = (char)(0x80 | (c & 0x3f));
310
			DBG(("CP 0x%x => %02x %02x %02x", c,
311
			    out[o - 3], out[o - 2], out[o - 1]));
312
		} else if (c < 0x200000) {
313
			if (o + 4 > olen - 1)
314
				return -1;
315
			out[o++] = (char)(0xf0 | (c >> 18));
316
			out[o++] = (char)(0x80 | ((c >> 12) & 0x3f));
317
			out[o++] = (char)(0x80 | ((c >> 6) & 0x3f));
318
			out[o++] = (char)(0x80 | (c & 0x3f));
319
			DBG(("CP 0x%x => %02x %02x %02x %02x", c,
320
			    out[o - 4], out[o - 3], out[o - 2], out[c - 1]));
321
#if BANNED_RFC3629
322
		} else if (c < 0x4000000) {
323
			if (o + 5 > olen - 1)
324
				return -1;
325
			out[o++] = (char)(0xf8 | (c >> 24));
326
			out[o++] = (char)(0x80 | ((c >> 18) & 0x3f));
327
			out[o++] = (char)(0x80 | ((c >> 12) & 0x3f));
328
			out[o++] = (char)(0x80 | ((c >> 6) & 0x3f));
329
			out[o++] = (char)(0x80 | (c & 0x3f));
330
			DBG(("CP 0x%x => %02x %02x %02x %02x %02x", c,
331
			    out[o - 5], out[o - 4], out[o - 3], out[c - 2],
332
			    out[o - 1]));
333
		} else if (c < 0x80000000) {
334
			if (o + 6 > olen - 1)
335
				return -1;
336
			out[o++] = (char)(0xfc | (c >> 30));
337
			out[o++] = (char)(0x80 | ((c >> 24) & 0x3f));
338
			out[o++] = (char)(0x80 | ((c >> 18) & 0x3f));
339
			out[o++] = (char)(0x80 | ((c >> 12) & 0x3f));
340
			out[o++] = (char)(0x80 | ((c >> 6) & 0x3f));
341
			out[o++] = (char)(0x80 | (c & 0x3f));
342
			DBG(("CP 0x%x => %02x %02x %02x %02x %02x %02x", c,
343
			    out[o - 6], out[o - 5], out[o - 4], out[c - 3],
344
			    out[o - 2], out[o - 1]));
345
#endif
346
		} else {
347
			DBG(("INTERNAL ERROR: CP 0x%x not encodeable", c));
348
			return -1;
349
		}
350
	}
351
	out[o] = '\0';
352
	return 0;
353
}
354
355
#if 0
356
#include <err.h>
357
358
int
359
main(int argc, char **argv)
360
{
361
	char in[8192], out[8192];
362
	
363
	while (fgets(in, sizeof(in), stdin) != NULL) {
364
		if (ssh_utf8_stringprep(in, out, sizeof(out)) != 0)
365
			errx(1, "bad input");
366
		fputs(out, stdout);
367
	}
368
	return 0;
369
}
370
#endif
(-)lib/Makefile (-1 / +1 lines)
Lines 13-19 SRCS= authfd.c authfile.c bufaux.c bufec Link Here
13
	ssh-dss.c ssh-rsa.c ssh-ecdsa.c dh.c kexdh.c kexgex.c kexecdh.c \
13
	ssh-dss.c ssh-rsa.c ssh-ecdsa.c dh.c kexdh.c kexgex.c kexecdh.c \
14
	kexdhc.c kexgexc.c kexecdhc.c msg.c progressmeter.c dns.c \
14
	kexdhc.c kexgexc.c kexecdhc.c msg.c progressmeter.c dns.c \
15
	monitor_fdpass.c umac.c addrmatch.c schnorr.c jpake.c ssh-pkcs11.c \
15
	monitor_fdpass.c umac.c addrmatch.c schnorr.c jpake.c ssh-pkcs11.c \
16
	krl.c
16
	krl.c stringprep.c
17
17
18
SRCS+=	umac128.c
18
SRCS+=	umac128.c
19
CLEANFILES+=   umac128.c
19
CLEANFILES+=   umac128.c

Return to bug 2058