xref: /plan9/sys/src/cmd/ssh1/cmsg.c (revision 63afb9a5d3f910047231762bcce0ee49fed3d07c)
1 #include "ssh.h"
2 
3 static void
recv_ssh_smsg_public_key(Conn * c)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
send_ssh_cmsg_session_key(Conn * c)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
authuser(Conn * c)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
ask(Conn * c,char * answers,char * question)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
checkkey(Conn * c)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
sshclienthandshake(Conn * c)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
intgetenv(char * name,int def)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
readgeom(int * nrow,int * ncol,int * width,int * height)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
sendwindowsize(Conn * c,int nrow,int ncol,int width,int height)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
requestpty(Conn * c)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 
377