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