Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
#include "ssh.h"
2
 
3
static void
4
recv_ssh_smsg_public_key(Conn *c)
5
{
6
	Msg *m;
7
 
8
	m = recvmsg(c, SSH_SMSG_PUBLIC_KEY);
9
	memmove(c->cookie, getbytes(m, COOKIELEN), COOKIELEN);
10
	c->serverkey = getRSApub(m);
11
	c->hostkey = getRSApub(m);
12
	c->flags = getlong(m);
13
	c->ciphermask = getlong(m);
14
	c->authmask = getlong(m);
15
	free(m);
16
}
17
 
18
static void
19
send_ssh_cmsg_session_key(Conn *c)
20
{
21
	int i, n, buflen, serverkeylen, hostkeylen;
22
	mpint *b;
23
	uchar *buf;
24
	Msg *m;
25
	RSApub *ksmall, *kbig;
26
 
27
	m = allocmsg(c, SSH_CMSG_SESSION_KEY, 2048);
28
	putbyte(m, c->cipher->id);
29
	putbytes(m, c->cookie, COOKIELEN);
30
 
31
	serverkeylen = mpsignif(c->serverkey->n);
32
	hostkeylen = mpsignif(c->hostkey->n);
33
	ksmall = kbig = nil;
34
	if(serverkeylen+128 <= hostkeylen){
35
		ksmall = c->serverkey;
36
		kbig = c->hostkey;
37
	}else if(hostkeylen+128 <= serverkeylen){
38
		ksmall = c->hostkey;
39
		kbig = c->serverkey;
40
	}else
41
		error("server session and host keys do not differ by at least 128 bits");
42
 
43
	buflen = (mpsignif(kbig->n)+7)/8;
44
	buf = emalloc(buflen);
45
 
46
	debug(DBG_CRYPTO, "session key is %.*H\n", SESSKEYLEN, c->sesskey);
47
	memmove(buf, c->sesskey, SESSKEYLEN);
48
	for(i = 0; i < SESSIDLEN; i++)
49
		buf[i] ^= c->sessid[i];
50
	debug(DBG_CRYPTO, "munged session key is %.*H\n", SESSKEYLEN, buf);
51
 
52
	b = rsaencryptbuf(ksmall, buf, SESSKEYLEN);
53
	n = (mpsignif(ksmall->n)+7) / 8;
54
	mptoberjust(b, buf, n);
55
	mpfree(b);
56
	debug(DBG_CRYPTO, "encrypted with ksmall is %.*H\n", n, buf);
57
 
58
	b = rsaencryptbuf(kbig, buf, n);
59
	putmpint(m, b);
60
	debug(DBG_CRYPTO, "encrypted with kbig is %B\n", b);
61
	mpfree(b);
62
 
63
	memset(buf, 0, buflen);
64
	free(buf);
65
 
66
	putlong(m, c->flags);
67
	sendmsg(m);
68
}
69
 
70
static int
71
authuser(Conn *c)
72
{
73
	int i;
74
	Msg *m;
75
 
76
	m = allocmsg(c, SSH_CMSG_USER, 4+strlen(c->user));
77
	putstring(m, c->user);
78
	sendmsg(m);
79
 
80
	m = recvmsg(c, -1);
81
	switch(m->type){
82
	case SSH_SMSG_SUCCESS:
83
		free(m);
84
		return 0;
85
	case SSH_SMSG_FAILURE:
86
		free(m);
87
		break;
88
	default:
89
		badmsg(m, 0);
90
	}
91
 
92
	for(i=0; i<c->nokauth; i++){
93
		debug(DBG_AUTH, "authmask %#lux, consider %s (%#x)\n",
94
			c->authmask, c->okauth[i]->name, 1<<c->okauth[i]->id);
95
		if(c->authmask & (1<<c->okauth[i]->id))
96
			if((*c->okauth[i]->fn)(c) == 0)
97
				return 0;
98
	}
99
 
100
	debug(DBG_AUTH, "no auth methods worked; (authmask=%#lux)\n", c->authmask);
101
	return -1;
102
}
103
 
104
static char
105
ask(Conn *c, char *answers, char *question)
106
{
107
	int fd;
108
	char buf[256];
109
 
110
	if(!c->interactive)
111
		return answers[0];
112
 
113
	if((fd = open("/dev/cons", ORDWR)) < 0)
114
		return answers[0];
115
 
116
	fprint(fd, "%s", question);
117
	if(read(fd, buf, 256) <= 0 || buf[0]=='\n'){
118
		close(fd);
119
		return answers[0];
120
	}
121
	close(fd);
122
	return buf[0];
123
}
124
static void
125
checkkey(Conn *c)
126
{
127
	char *home, *keyfile;
128
 
129
	debug(DBG_CRYPTO, "checking key %B %B\n", c->hostkey->n, c->hostkey->ek);
130
	switch(findkey("/sys/lib/ssh/keyring", c->aliases, c->hostkey)){
131
	default:
132
		abort();
133
	case KeyOk:
134
		return;
135
	case KeyWrong:
136
		fprint(2, "server presented public key different than expected\n");
137
		fprint(2, "(expected key in /sys/lib/ssh/keyring).  will not continue.\n");
138
		error("bad server key");
139
 
140
	case NoKey:
141
	case NoKeyFile:
142
		break;
143
	}
144
 
145
	home = getenv("home");
146
	if(home == nil){
147
		fprint(2, "server %s not on keyring; will not continue.\n", c->host);
148
		error("bad server key");
149
	}
150
 
151
	keyfile = smprint("%s/lib/keyring", home);
152
	if(keyfile == nil)
153
		error("out of memory");
154
 
155
	switch(findkey(keyfile, c->aliases, c->hostkey)){
156
	default:
157
		abort();
158
	case KeyOk:
159
		return;
160
	case KeyWrong:
161
		fprint(2, "server %s presented public key different than expected\n", c->host);
162
		fprint(2, "(expected key in %s).  will not continue.\n", keyfile);
163
		fprint(2, "this could be a man-in-the-middle attack.\n");
164
		switch(ask(c, "eri", "replace key in keyfile (r), continue without replacing key (c), or exit (e) [e]")){
165
		case 'e':
166
			error("bad key");
167
		case 'r':
168
			if(replacekey(keyfile, c->aliases, c->hostkey) < 0)
169
				error("replacekey: %r");
170
			break;
171
		case 'c':
172
			break;
173
		}
174
		return;
175
	case NoKey:
176
	case NoKeyFile:
177
		fprint(2, "server %s not on keyring.\n", c->host);
178
		switch(ask(c, "eac", "add key to keyfile (a), continue without adding key (c), or exit (e) [e]")){
179
		case 'e':
180
			error("bad key");
181
		case 'a':
182
			if(appendkey(keyfile, c->aliases, c->hostkey) < 0)
183
				error("appendkey: %r");
184
			break;
185
		case 'c':
186
			break;
187
		}
188
		return;
189
	}
190
}
191
 
192
void
193
sshclienthandshake(Conn *c)
194
{
195
	char buf[128], *p;
196
	int i;
197
	Msg *m;
198
 
199
	/* receive id string */
200
	if(readstrnl(c->fd[0], buf, sizeof buf) < 0)
201
		error("reading server version: %r");
202
 
203
	/* id string is "SSH-m.n-comment".  We need m=1, n>=5. */
204
	if(strncmp(buf, "SSH-", 4) != 0
205
	|| strtol(buf+4, &p, 10) != 1
206
	|| *p != '.'
207
	|| strtol(p+1, &p, 10) < 5
208
	|| *p != '-')
209
		error("protocol mismatch; got %s, need SSH-1.x for x>=5", buf);
210
 
211
	/* send id string */
212
	fprint(c->fd[1], "SSH-1.5-Plan 9\n");
213
 
214
	recv_ssh_smsg_public_key(c);
215
	checkkey(c);
216
 
217
	for(i=0; i<SESSKEYLEN; i++)
218
		c->sesskey[i] = fastrand();
219
	c->cipher = nil;
220
	for(i=0; i<c->nokcipher; i++)
221
		if((1<<c->okcipher[i]->id) & c->ciphermask){
222
			c->cipher = c->okcipher[i];
223
			break;
224
		}
225
	if(c->cipher == nil)
226
		error("can't agree on ciphers: remote side supports %#lux", c->ciphermask);
227
 
228
	calcsessid(c);
229
 
230
	send_ssh_cmsg_session_key(c);
231
 
232
	c->cstate = (*c->cipher->init)(c, 0);		/* turns on encryption */
233
	m = recvmsg(c, SSH_SMSG_SUCCESS);
234
	free(m);
235
 
236
	if(authuser(c) < 0)
237
		error("client authentication failed");
238
}
239
 
240
static int
241
intgetenv(char *name, int def)
242
{
243
	char *s;
244
	int n, val;
245
 
246
	val = def;
247
	if((s = getenv(name))!=nil){
248
		if((n=atoi(s)) > 0)
249
			val = n;
250
		free(s);
251
	}
252
	return val;
253
}
254
 
255
/*
256
 * assumes that if you care, you're running under vt
257
 * and therefore these are set.
258
 */
259
int
260
readgeom(int *nrow, int *ncol, int *width, int *height)
261
{
262
	static int fd = -1;
263
	char buf[64];
264
 
265
	if(fd < 0 && (fd = open("/dev/wctl", OREAD)) < 0)
266
		return -1;
267
	/* wait for event, but don't care what it says */
268
	if(read(fd, buf, sizeof buf) < 0)
269
		return -1;
270
	*nrow = intgetenv("LINES", 24);
271
	*ncol = intgetenv("COLS", 80);
272
	*width = intgetenv("XPIXELS", 640);
273
	*height = intgetenv("YPIXELS", 480);
274
	return 0;
275
}
276
 
277
void
278
sendwindowsize(Conn *c, int nrow, int ncol, int width, int height)
279
{
280
	Msg *m;
281
 
282
	m = allocmsg(c, SSH_CMSG_WINDOW_SIZE, 4*4);
283
	putlong(m, nrow);
284
	putlong(m, ncol);
285
	putlong(m, width);
286
	putlong(m, height);
287
	sendmsg(m);
288
}
289
 
290
/*
291
 * In each option line, the first byte is the option number
292
 * and the second is either a boolean bit or actually an
293
 * ASCII code.
294
 */
295
static uchar ptyopt[] =
296
{
297
	0x01, 0x7F,	/* interrupt = DEL */
298
	0x02, 0x11,	/* quit = ^Q */
299
	0x03, 0x08,	/* backspace = ^H */
300
	0x04, 0x15,	/* line kill = ^U */
301
	0x05, 0x04,	/* EOF = ^D */
302
	0x20, 0x00,	/* don't strip high bit */
303
	0x48, 0x01,	/* give us CRs */
304
 
305
	0x00,		/* end options */
306
};
307
 
308
static uchar rawptyopt[] = 
309
{
310
	30,	0,		/* ignpar */
311
	31,	0,		/* parmrk */
312
	32,	0,		/* inpck */
313
	33,	0,		/* istrip */
314
	34,	0,		/* inlcr */
315
	35,	0,		/* igncr */
316
	36,	0,		/* icnrl */
317
	37,	0,		/* iuclc */
318
	38,	0,		/* ixon */
319
	39,	1,		/* ixany */
320
	40,	0,		/* ixoff */
321
	41,	0,		/* imaxbel */
322
 
323
	50,	0,		/* isig: intr, quit, susp processing */
324
	51,	0,		/* icanon: erase and kill processing */
325
	52,	0,		/* xcase */
326
 
327
	53,	0,		/* echo */
328
 
329
	57,	0,		/* noflsh */
330
	58,	0,		/* tostop */
331
	59,	0,		/* iexten: impl defined control chars */
332
 
333
	70,	0,		/* opost */
334
 
335
	0x00,
336
};
337
 
338
void
339
requestpty(Conn *c)
340
{
341
	char *term;
342
	int nrow, ncol, width, height;
343
	Msg *m;
344
 
345
	m = allocmsg(c, SSH_CMSG_REQUEST_PTY, 1024);
346
	if((term = getenv("TERM")) == nil)
347
		term = "9term";
348
	putstring(m, term);
349
 
350
	readgeom(&nrow, &ncol, &width, &height);
351
	putlong(m, nrow);	/* characters */
352
	putlong(m, ncol);
353
	putlong(m, width);	/* pixels */
354
	putlong(m, height);
355
 
356
	if(rawhack)
357
		putbytes(m, rawptyopt, sizeof rawptyopt);
358
	else
359
		putbytes(m, ptyopt, sizeof ptyopt);
360
 
361
	sendmsg(m);
362
 
363
	m = recvmsg(c, 0);
364
	switch(m->type){
365
	case SSH_SMSG_SUCCESS:
366
		debug(DBG_IO, "PTY allocated\n");
367
		break;
368
	case SSH_SMSG_FAILURE:
369
		debug(DBG_IO, "PTY allocation failed\n");
370
		break;
371
	default:
372
		badmsg(m, 0);
373
	}
374
	free(m);
375
}
376