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