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