cmsg.c 8.2 KB

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