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 377