1 #include "ssh.h"
2 #include <bio.h>
3
4 static void
send_ssh_smsg_public_key(Conn * c)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*
rpcdecrypt(AuthRpc * rpc,mpint * b)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
recv_ssh_cmsg_session_key(Conn * c,AuthRpc * rpc)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*
responselogin(char * user,char * resp)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*
authusername(Conn * c)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
authsrvuser(Conn * c)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
sshserverhandshake(Conn * c)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