1 #include "ssh.h"
2
3 char *cipherlist = "blowfish rc4 3des";
4 char *authlist = "tis";
5
6 void fromnet(Conn*);
7 void startcmd(Conn*, char*, int*, int*);
8 int maxmsg = 256*1024;
9
10 Cipher *allcipher[] = {
11 &cipherrc4,
12 &cipherblowfish,
13 &cipher3des,
14 &cipherdes,
15 &ciphernone,
16 &ciphertwiddle,
17 };
18
19 Authsrv *allauthsrv[] = {
20 &authsrvpassword,
21 &authsrvtis,
22 };
23
24 Cipher*
findcipher(char * name,Cipher ** list,int nlist)25 findcipher(char *name, Cipher **list, int nlist)
26 {
27 int i;
28
29 for(i=0; i<nlist; i++)
30 if(strcmp(name, list[i]->name) == 0)
31 return list[i];
32 error("unknown cipher %s", name);
33 return nil;
34 }
35
36 Authsrv*
findauthsrv(char * name,Authsrv ** list,int nlist)37 findauthsrv(char *name, Authsrv **list, int nlist)
38 {
39 int i;
40
41 for(i=0; i<nlist; i++)
42 if(strcmp(name, list[i]->name) == 0)
43 return list[i];
44 error("unknown authsrv %s", name);
45 return nil;
46 }
47
48 void
usage(void)49 usage(void)
50 {
51 fprint(2, "usage: sshserve [-A authlist] [-c cipherlist] client-ip-address\n");
52 exits("usage");
53 }
54
55 void
main(int argc,char ** argv)56 main(int argc, char **argv)
57 {
58 char *f[16];
59 int i;
60 Conn c;
61
62 fmtinstall('B', mpfmt);
63 fmtinstall('H', encodefmt);
64 atexit(atexitkiller);
65 atexitkill(getpid());
66
67 memset(&c, 0, sizeof c);
68
69 ARGBEGIN{
70 case 'D':
71 debuglevel = atoi(EARGF(usage()));
72 break;
73 case 'A':
74 authlist = EARGF(usage());
75 break;
76 case 'c':
77 cipherlist = EARGF(usage());
78 break;
79 default:
80 usage();
81 }ARGEND
82
83 if(argc != 1)
84 usage();
85 c.host = argv[0];
86
87 sshlog("connect from %s", c.host);
88
89 /* limit of 768 bits in remote host key? */
90 c.serverpriv = rsagen(768, 6, 0);
91 if(c.serverpriv == nil)
92 sysfatal("rsagen failed: %r");
93 c.serverkey = &c.serverpriv->pub;
94
95 c.nokcipher = getfields(cipherlist, f, nelem(f), 1, ", ");
96 c.okcipher = emalloc(sizeof(Cipher*)*c.nokcipher);
97 for(i=0; i<c.nokcipher; i++)
98 c.okcipher[i] = findcipher(f[i], allcipher, nelem(allcipher));
99
100 c.nokauthsrv = getfields(authlist, f, nelem(f), 1, ", ");
101 c.okauthsrv = emalloc(sizeof(Authsrv*)*c.nokauthsrv);
102 for(i=0; i<c.nokauthsrv; i++)
103 c.okauthsrv[i] = findauthsrv(f[i], allauthsrv, nelem(allauthsrv));
104
105 sshserverhandshake(&c);
106
107 fromnet(&c);
108 }
109
110 void
fromnet(Conn * c)111 fromnet(Conn *c)
112 {
113 int infd, kidpid, n;
114 char *cmd;
115 Msg *m;
116
117 infd = kidpid = -1;
118 for(;;){
119 m = recvmsg(c, -1);
120 if(m == nil)
121 exits(nil);
122 switch(m->type){
123 default:
124 //badmsg(m, 0);
125 sendmsg(allocmsg(c, SSH_SMSG_FAILURE, 0));
126 break;
127
128 case SSH_MSG_DISCONNECT:
129 sysfatal("client disconnected");
130
131 case SSH_CMSG_REQUEST_PTY:
132 sendmsg(allocmsg(c, SSH_SMSG_SUCCESS, 0));
133 break;
134
135 case SSH_CMSG_X11_REQUEST_FORWARDING:
136 sendmsg(allocmsg(c, SSH_SMSG_FAILURE, 0));
137 break;
138
139 case SSH_CMSG_MAX_PACKET_SIZE:
140 maxmsg = getlong(m);
141 sendmsg(allocmsg(c, SSH_SMSG_SUCCESS, 0));
142 break;
143
144 case SSH_CMSG_REQUEST_COMPRESSION:
145 sendmsg(allocmsg(c, SSH_SMSG_FAILURE, 0));
146 break;
147
148 case SSH_CMSG_EXEC_SHELL:
149 startcmd(c, nil, &kidpid, &infd);
150 goto InteractiveMode;
151
152 case SSH_CMSG_EXEC_CMD:
153 cmd = getstring(m);
154 startcmd(c, cmd, &kidpid, &infd);
155 goto InteractiveMode;
156 }
157 free(m);
158 }
159
160 InteractiveMode:
161 for(;;){
162 free(m);
163 m = recvmsg(c, -1);
164 if(m == nil)
165 exits(nil);
166 switch(m->type){
167 default:
168 badmsg(m, 0);
169
170 case SSH_MSG_DISCONNECT:
171 postnote(PNGROUP, kidpid, "hangup");
172 sysfatal("client disconnected");
173
174 case SSH_CMSG_STDIN_DATA:
175 if(infd != 0){
176 n = getlong(m);
177 write(infd, getbytes(m, n), n);
178 }
179 break;
180
181 case SSH_CMSG_EOF:
182 close(infd);
183 infd = -1;
184 break;
185
186 case SSH_CMSG_EXIT_CONFIRMATION:
187 /* sent by some clients as dying breath */
188 exits(nil);
189
190 case SSH_CMSG_WINDOW_SIZE:
191 /* we don't care */
192 break;
193 }
194 }
195 }
196
197 void
copyout(Conn * c,int fd,int mtype)198 copyout(Conn *c, int fd, int mtype)
199 {
200 char buf[8192];
201 int n, max, pid;
202 Msg *m;
203
204 max = sizeof buf;
205 if(max > maxmsg - 32) /* 32 is an overestimate of packet overhead */
206 max = maxmsg - 32;
207 if(max <= 0)
208 sysfatal("maximum message size too small");
209
210 switch(pid = rfork(RFPROC|RFMEM|RFNOWAIT)){
211 case -1:
212 sysfatal("fork: %r");
213 case 0:
214 break;
215 default:
216 atexitkill(pid);
217 return;
218 }
219
220 while((n = read(fd, buf, max)) > 0){
221 m = allocmsg(c, mtype, 4+n);
222 putlong(m, n);
223 putbytes(m, buf, n);
224 sendmsg(m);
225 }
226 exits(nil);
227 }
228
229 void
startcmd(Conn * c,char * cmd,int * kidpid,int * kidin)230 startcmd(Conn *c, char *cmd, int *kidpid, int *kidin)
231 {
232 int i, pid, kpid;
233 int pfd[3][2];
234 char *dir;
235 char *sysname, *tz;
236 Msg *m;
237 Waitmsg *w;
238
239 for(i=0; i<3; i++)
240 if(pipe(pfd[i]) < 0)
241 sysfatal("pipe: %r");
242
243 sysname = getenv("sysname");
244 tz = getenv("timezone");
245
246 switch(pid = rfork(RFPROC|RFMEM|RFNOWAIT)){
247 case -1:
248 sysfatal("fork: %r");
249 case 0:
250 switch(kpid = rfork(RFPROC|RFNOTEG|RFENVG|RFFDG)){
251 case -1:
252 sysfatal("fork: %r");
253 case 0:
254 for(i=0; i<3; i++){
255 if(dup(pfd[i][1], i) < 0)
256 sysfatal("dup: %r");
257 close(pfd[i][0]);
258 close(pfd[i][1]);
259 }
260 putenv("user", c->user);
261 if(sysname)
262 putenv("sysname", sysname);
263 if(tz)
264 putenv("tz", tz);
265
266 dir = smprint("/usr/%s", c->user);
267 if(dir == nil || chdir(dir) < 0)
268 chdir("/");
269 if(cmd){
270 putenv("service", "rx");
271 execl("/bin/rc", "rc", "-lc", cmd, nil);
272 sysfatal("cannot exec /bin/rc: %r");
273 }else{
274 putenv("service", "con");
275 execl("/bin/ip/telnetd", "telnetd", "-tn", nil);
276 sysfatal("cannot exec /bin/ip/telnetd: %r");
277 }
278 default:
279 *kidpid = kpid;
280 rendezvous(kidpid, 0);
281 for(;;){
282 if((w = wait()) == nil)
283 sysfatal("wait: %r");
284 if(w->pid == kpid)
285 break;
286 free(w);
287 }
288 if(w->msg[0]){
289 m = allocmsg(c, SSH_MSG_DISCONNECT, 4+strlen(w->msg));
290 putstring(m, w->msg);
291 sendmsg(m);
292 }else{
293 m = allocmsg(c, SSH_SMSG_EXITSTATUS, 4);
294 putlong(m, 0);
295 sendmsg(m);
296 }
297 for(i=0; i<3; i++)
298 close(pfd[i][0]);
299 free(w);
300 exits(nil);
301 break;
302 }
303 default:
304 atexitkill(pid);
305 rendezvous(kidpid, 0);
306 break;
307 }
308
309 for(i=0; i<3; i++)
310 close(pfd[i][1]);
311
312 copyout(c, pfd[1][0], SSH_SMSG_STDOUT_DATA);
313 copyout(c, pfd[2][0], SSH_SMSG_STDERR_DATA);
314 *kidin = pfd[0][0];
315 }
316