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