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