xref: /plan9/sys/src/cmd/ssh1/smsg.c (revision 63afb9a5d3f910047231762bcce0ee49fed3d07c)
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