]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ssh/smsg.c
9bootfat: rename open() to fileinit and make it static as its really a internal funct...
[plan9front.git] / sys / src / cmd / ssh / smsg.c
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 }