1 #include "ssh.h" 2 #include <bio.h> 3 4 static void 5 send_ssh_smsg_public_key(Conn *c) 6 { 7 int i; 8 Msg *m; 9 10 m = allocmsg(c, SSH_SMSG_PUBLIC_KEY, 2048); 11 putbytes(m, c->cookie, COOKIELEN); 12 putRSApub(m, c->serverkey); 13 putRSApub(m, c->hostkey); 14 putlong(m, c->flags); 15 16 for(i=0; i<c->nokcipher; i++) 17 c->ciphermask |= 1<<c->okcipher[i]->id; 18 putlong(m, c->ciphermask); 19 for(i=0; i<c->nokauthsrv; i++) 20 c->authmask |= 1<<c->okauthsrv[i]->id; 21 putlong(m, c->authmask); 22 23 sendmsg(m); 24 } 25 26 static mpint* 27 rpcdecrypt(AuthRpc *rpc, mpint *b) 28 { 29 mpint *a; 30 char *p; 31 32 p = mptoa(b, 16, nil, 0); 33 if(auth_rpc(rpc, "write", p, strlen(p)) != ARok) 34 sysfatal("factotum rsa write: %r"); 35 free(p); 36 if(auth_rpc(rpc, "read", nil, 0) != ARok) 37 sysfatal("factotum rsa read: %r"); 38 a = strtomp(rpc->arg, nil, 16, nil); 39 mpfree(b); 40 return a; 41 } 42 43 static void 44 recv_ssh_cmsg_session_key(Conn *c, AuthRpc *rpc) 45 { 46 int i, id, n, serverkeylen, hostkeylen; 47 mpint *a, *b; 48 uchar *buf; 49 Msg *m; 50 RSApriv *ksmall, *kbig; 51 52 m = recvmsg(c, SSH_CMSG_SESSION_KEY); 53 id = getbyte(m); 54 c->cipher = nil; 55 for(i=0; i<c->nokcipher; i++) 56 if(c->okcipher[i]->id == id) 57 c->cipher = c->okcipher[i]; 58 if(c->cipher == nil) 59 sysfatal("invalid cipher selected"); 60 61 if(memcmp(getbytes(m, COOKIELEN), c->cookie, COOKIELEN) != 0) 62 sysfatal("bad cookie"); 63 64 serverkeylen = mpsignif(c->serverkey->n); 65 hostkeylen = mpsignif(c->hostkey->n); 66 ksmall = kbig = nil; 67 if(serverkeylen+128 <= hostkeylen){ 68 ksmall = c->serverpriv; 69 kbig = nil; 70 }else if(hostkeylen+128 <= serverkeylen){ 71 ksmall = nil; 72 kbig = c->serverpriv; 73 }else 74 sysfatal("server session and host keys do not differ by at least 128 bits"); 75 76 b = getmpint(m); 77 78 debug(DBG_CRYPTO, "encrypted with kbig is %B\n", b); 79 if(kbig){ 80 a = rsadecrypt(kbig, b, nil); 81 mpfree(b); 82 b = a; 83 }else 84 b = rpcdecrypt(rpc, b); 85 a = rsaunpad(b); 86 mpfree(b); 87 b = a; 88 89 debug(DBG_CRYPTO, "encrypted with ksmall is %B\n", b); 90 if(ksmall){ 91 a = rsadecrypt(ksmall, b, nil); 92 mpfree(b); 93 b = a; 94 }else 95 b = rpcdecrypt(rpc, b); 96 a = rsaunpad(b); 97 mpfree(b); 98 b = a; 99 100 debug(DBG_CRYPTO, "munged is %B\n", b); 101 102 n = (mpsignif(b)+7)/8; 103 if(n > SESSKEYLEN) 104 sysfatal("client sent short session key"); 105 106 buf = emalloc(SESSKEYLEN); 107 mptoberjust(b, buf, SESSKEYLEN); 108 mpfree(b); 109 110 for(i=0; i<SESSIDLEN; i++) 111 buf[i] ^= c->sessid[i]; 112 113 memmove(c->sesskey, buf, SESSKEYLEN); 114 115 debug(DBG_CRYPTO, "unmunged is %.*H\n", SESSKEYLEN, buf); 116 117 c->flags = getlong(m); 118 free(m); 119 } 120 121 static AuthInfo* 122 responselogin(char *user, char *resp) 123 { 124 Chalstate *c; 125 AuthInfo *ai; 126 127 if((c = auth_challenge("proto=p9cr user=%q role=server", user)) == nil){ 128 sshlog("auth_challenge failed for %s", user); 129 return nil; 130 } 131 c->resp = resp; 132 c->nresp = strlen(resp); 133 ai = auth_response(c); 134 auth_freechal(c); 135 return ai; 136 } 137 138 static AuthInfo* 139 authusername(Conn *c) 140 { 141 char *p; 142 AuthInfo *ai; 143 144 /* 145 * hack for sam users: 'name numbers' gets tried as securid login. 146 */ 147 if(p = strchr(c->user, ' ')){ 148 *p++ = '\0'; 149 if((ai=responselogin(c->user, p)) != nil) 150 return ai; 151 *--p = ' '; 152 sshlog("bad response: %s", c->user); 153 } 154 return nil; 155 } 156 157 static void 158 authsrvuser(Conn *c) 159 { 160 int i; 161 char *ns, *user; 162 AuthInfo *ai; 163 Msg *m; 164 165 m = recvmsg(c, SSH_CMSG_USER); 166 user = getstring(m); 167 c->user = emalloc(strlen(user)+1); 168 strcpy(c->user, user); 169 free(m); 170 171 ai = authusername(c); 172 while(ai == nil){ 173 /* 174 * clumsy: if the client aborted the auth_tis early 175 * we don't send a new failure. we check this by 176 * looking at c->unget, which is only used in that 177 * case. 178 */ 179 if(c->unget != nil) 180 goto skipfailure; 181 sendmsg(allocmsg(c, SSH_SMSG_FAILURE, 0)); 182 skipfailure: 183 m = recvmsg(c, -1); 184 for(i=0; i<c->nokauthsrv; i++) 185 if(c->okauthsrv[i]->firstmsg == m->type){ 186 ai = (*c->okauthsrv[i]->fn)(c, m); 187 break; 188 } 189 if(i==c->nokauthsrv) 190 badmsg(m, 0); 191 } 192 sendmsg(allocmsg(c, SSH_SMSG_SUCCESS, 0)); 193 194 if(noworld(ai->cuid)) 195 ns = "/lib/namespace.noworld"; 196 else 197 ns = nil; 198 if(auth_chuid(ai, ns) < 0){ 199 sshlog("auth_chuid to %s: %r", ai->cuid); 200 sysfatal("auth_chuid: %r"); 201 } 202 sshlog("logged in as %s", ai->cuid); 203 auth_freeAI(ai); 204 } 205 206 void 207 sshserverhandshake(Conn *c) 208 { 209 char *p, buf[128]; 210 Biobuf *b; 211 Attr *a; 212 int i, afd; 213 mpint *m; 214 AuthRpc *rpc; 215 RSApub *key; 216 217 /* 218 * BUG: should use `attr' to get the key attributes 219 * after the read, but that's not implemented yet. 220 */ 221 if((b = Bopen("/mnt/factotum/ctl", OREAD)) == nil) 222 sysfatal("open /mnt/factotum/ctl: %r"); 223 while((p = Brdline(b, '\n')) != nil){ 224 p[Blinelen(b)-1] = '\0'; 225 if(strstr(p, " proto=rsa ") && strstr(p, " service=sshserve ")) 226 break; 227 } 228 if(p == nil) 229 sysfatal("no sshserve keys found in /mnt/factotum/ctl"); 230 a = _parseattr(p); 231 Bterm(b); 232 key = emalloc(sizeof(*key)); 233 if((p = _strfindattr(a, "n")) == nil) 234 sysfatal("no n in sshserve key"); 235 if((key->n = strtomp(p, &p, 16, nil)) == nil || *p != 0) 236 sysfatal("bad n in sshserve key"); 237 if((p = _strfindattr(a, "ek")) == nil) 238 sysfatal("no ek in sshserve key"); 239 if((key->ek = strtomp(p, &p, 16, nil)) == nil || *p != 0) 240 sysfatal("bad ek in sshserve key"); 241 _freeattr(a); 242 243 if((afd = open("/mnt/factotum/rpc", ORDWR)) < 0) 244 sysfatal("open /mnt/factotum/rpc: %r"); 245 if((rpc = auth_allocrpc(afd)) == nil) 246 sysfatal("auth_allocrpc: %r"); 247 p = "proto=rsa role=client service=sshserve"; 248 if(auth_rpc(rpc, "start", p, strlen(p)) != ARok) 249 sysfatal("auth_rpc start %s: %r", p); 250 if(auth_rpc(rpc, "read", nil, 0) != ARok) 251 sysfatal("auth_rpc read: %r"); 252 m = strtomp(rpc->arg, nil, 16, nil); 253 if(mpcmp(m, key->n) != 0) 254 sysfatal("key in /mnt/factotum/ctl does not match rpc key"); 255 mpfree(m); 256 c->hostkey = key; 257 258 /* send id string */ 259 fprint(c->fd[0], "SSH-1.5-Plan9\n"); 260 261 /* receive id string */ 262 if(readstrnl(c->fd[0], buf, sizeof buf) < 0) 263 sysfatal("reading server version: %r"); 264 265 /* id string is "SSH-m.n-comment". We need m=1, n>=5. */ 266 if(strncmp(buf, "SSH-", 4) != 0 267 || strtol(buf+4, &p, 10) != 1 268 || *p != '.' 269 || strtol(p+1, &p, 10) < 5 270 || *p != '-') 271 sysfatal("protocol mismatch; got %s, need SSH-1.x for x>=5", buf); 272 273 for(i=0; i<COOKIELEN; i++) 274 c->cookie[i] = fastrand(); 275 calcsessid(c); 276 send_ssh_smsg_public_key(c); 277 recv_ssh_cmsg_session_key(c, rpc); 278 auth_freerpc(rpc); 279 close(afd); 280 281 c->cstate = (*c->cipher->init)(c, 1); /* turns on encryption */ 282 sendmsg(allocmsg(c, SSH_SMSG_SUCCESS, 0)); 283 284 authsrvuser(c); 285 } 286