2 * Dropbear - a SSH2 server
4 * Copyright (c) 2002,2003 Matt Johnston
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39 static void checktimeouts();
40 static long select_timeout();
41 static int ident_readln(int fd
, char* buf
, int count
);
43 struct sshsession ses
; /* GLOBAL */
45 /* need to know if the session struct has been initialised, this way isn't the
46 * cleanest, but works OK */
47 int sessinitdone
= 0; /* GLOBAL */
49 /* this is set when we get SIGINT or SIGTERM, the handler is in main.c */
50 int exitflag
= 0; /* GLOBAL */
54 /* called only at the start of a session, set up initial state */
55 void common_session_init(int sock_in
, int sock_out
) {
57 TRACE(("enter session_init"))
59 ses
.sock_in
= sock_in
;
60 ses
.sock_out
= sock_out
;
61 ses
.maxfd
= MAX(sock_in
, sock_out
);
64 ses
.last_trx_packet_time
= 0;
65 ses
.last_packet_time
= 0;
67 if (pipe(ses
.signal_pipe
) < 0) {
68 dropbear_exit("signal pipe failed");
70 setnonblocking(ses
.signal_pipe
[0]);
71 setnonblocking(ses
.signal_pipe
[1]);
73 ses
.maxfd
= MAX(ses
.maxfd
, ses
.signal_pipe
[0]);
74 ses
.maxfd
= MAX(ses
.maxfd
, ses
.signal_pipe
[1]);
76 kexfirstinitialise(); /* initialise the kex state */
78 ses
.writepayload
= buf_new(TRANS_MAX_PAYLOAD_LEN
);
85 initqueue(&ses
.writequeue
);
87 ses
.requirenext
= SSH_MSG_KEXINIT
;
88 ses
.dataallowed
= 1; /* we can send data until we actually
89 send the SSH_MSG_KEXINIT */
92 ses
.reply_queue_head
= NULL
;
93 ses
.reply_queue_tail
= NULL
;
95 /* set all the algos to none */
96 ses
.keys
= (struct key_context
*)m_malloc(sizeof(struct key_context
));
98 ses
.keys
->recv
.algo_crypt
= &dropbear_nocipher
;
99 ses
.keys
->trans
.algo_crypt
= &dropbear_nocipher
;
100 ses
.keys
->recv
.crypt_mode
= &dropbear_mode_none
;
101 ses
.keys
->trans
.crypt_mode
= &dropbear_mode_none
;
103 ses
.keys
->recv
.algo_mac
= &dropbear_nohash
;
104 ses
.keys
->trans
.algo_mac
= &dropbear_nohash
;
106 ses
.keys
->algo_kex
= -1;
107 ses
.keys
->algo_hostkey
= -1;
108 ses
.keys
->recv
.algo_comp
= DROPBEAR_COMP_NONE
;
109 ses
.keys
->trans
.algo_comp
= DROPBEAR_COMP_NONE
;
112 ses
.keys
->recv
.zstream
= NULL
;
113 ses
.keys
->trans
.zstream
= NULL
;
116 /* key exchange buffers */
117 ses
.session_id
= NULL
;
118 ses
.kexhashbuf
= NULL
;
119 ses
.transkexinit
= NULL
;
121 ses
.remoteident
= NULL
;
123 ses
.chantypes
= NULL
;
125 ses
.allowprivport
= 0;
127 TRACE(("leave session_init"))
130 void session_loop(void(*loophandler
)()) {
132 fd_set readfd
, writefd
;
133 struct timeval timeout
;
136 /* main loop, select()s for all sockets in use */
139 timeout
.tv_sec
= select_timeout();
143 dropbear_assert(ses
.payload
== NULL
);
144 if (ses
.sock_in
!= -1) {
145 FD_SET(ses
.sock_in
, &readfd
);
147 if (ses
.sock_out
!= -1 && !isempty(&ses
.writequeue
)) {
148 FD_SET(ses
.sock_out
, &writefd
);
151 /* We get woken up when signal handlers write to this pipe.
152 SIGCHLD in svr-chansession is the only one currently. */
153 FD_SET(ses
.signal_pipe
[0], &readfd
);
155 /* set up for channels which require reading/writing */
156 if (ses
.dataallowed
) {
157 setchannelfds(&readfd
, &writefd
);
159 val
= select(ses
.maxfd
+1, &readfd
, &writefd
, NULL
, &timeout
);
162 dropbear_exit("Terminated by signal");
165 if (val
< 0 && errno
!= EINTR
) {
166 dropbear_exit("Error in select");
170 /* If we were interrupted or the select timed out, we still
171 * want to iterate over channels etc for reading, to handle
172 * server processes exiting etc.
173 * We don't want to read/write FDs. */
178 /* We'll just empty out the pipe if required. We don't do
179 any thing with the data, since the pipe's purpose is purely to
180 wake up the select() above. */
181 if (FD_ISSET(ses
.signal_pipe
[0], &readfd
)) {
183 while (read(ses
.signal_pipe
[0], &x
, 1) > 0) {}
186 /* check for auth timeout, rekeying required etc */
189 /* process session socket's incoming/outgoing data */
190 if (ses
.sock_out
!= -1) {
191 if (FD_ISSET(ses
.sock_out
, &writefd
) && !isempty(&ses
.writequeue
)) {
196 if (ses
.sock_in
!= -1) {
197 if (FD_ISSET(ses
.sock_in
, &readfd
)) {
201 /* Process the decrypted packet. After this, the read buffer
202 * will be ready for a new packet */
203 if (ses
.payload
!= NULL
) {
208 /* if required, flush out any queued reply packets that
209 were being held up during a KEX */
210 maybe_flush_reply_queue();
212 /* process pipes etc for the channels, ses.dataallowed == 0
213 * during rekeying ) */
214 if (ses
.dataallowed
) {
215 channelio(&readfd
, &writefd
);
227 /* clean up a session on exit */
228 void common_session_cleanup() {
230 TRACE(("enter session_cleanup"))
232 /* we can't cleanup if we don't know the session state */
234 TRACE(("leave session_cleanup: !sessinitdone"))
238 m_free(ses
.session_id
);
239 m_burn(ses
.keys
, sizeof(struct key_context
));
244 TRACE(("leave session_cleanup"))
248 void session_identification() {
250 /* max length of 255 chars */
256 /* write our version string, this blocks */
257 if (atomicio(write
, ses
.sock_out
, LOCAL_IDENT
"\r\n",
258 strlen(LOCAL_IDENT
"\r\n")) == DROPBEAR_FAILURE
) {
262 /* If they send more than 50 lines, something is wrong */
263 for (i
= 0; i
< 50; i
++) {
264 len
= ident_readln(ses
.sock_in
, linebuf
, sizeof(linebuf
));
266 if (len
< 0 && errno
!= EINTR
) {
271 if (len
>= 4 && memcmp(linebuf
, "SSH-", 4) == 0) {
272 /* start of line matches */
279 TRACE(("err: %s for '%s'\n", strerror(errno
), linebuf
))
282 /* linebuf is already null terminated */
283 ses
.remoteident
= m_malloc(len
);
284 memcpy(ses
.remoteident
, linebuf
, len
);
287 /* Shall assume that 2.x will be backwards compatible. */
288 if (strncmp(ses
.remoteident
, "SSH-2.", 6) != 0
289 && strncmp(ses
.remoteident
, "SSH-1.99-", 9) != 0) {
290 dropbear_exit("Incompatible remote version '%s'", ses
.remoteident
);
293 TRACE(("remoteident: %s", ses
.remoteident
))
297 /* returns the length including null-terminating zero on success,
298 * or -1 on failure */
299 static int ident_readln(int fd
, char* buf
, int count
) {
305 struct timeval timeout
;
307 TRACE(("enter ident_readln"))
315 /* select since it's a non-blocking fd */
317 /* leave space to null-terminate */
318 while (pos
< count
-1) {
324 if (select(fd
+1, &fds
, NULL
, NULL
, &timeout
) < 0) {
325 if (errno
== EINTR
) {
328 TRACE(("leave ident_readln: select error"))
334 /* Have to go one byte at a time, since we don't want to read past
335 * the end, and have to somehow shove bytes back into the normal
337 if (FD_ISSET(fd
, &fds
)) {
338 num
= read(fd
, &in
, 1);
339 /* a "\n" is a newline, "\r" we want to read in and keep going
340 * so that it won't be read as part of the next line */
343 if (errno
== EINTR
) {
344 continue; /* not a real error */
346 TRACE(("leave ident_readln: read error"))
351 TRACE(("leave ident_readln: EOF"))
355 /* end of ident string */
358 /* we don't want to include '\r's */
367 TRACE(("leave ident_readln: return %d", pos
+1))
371 void send_msg_ignore() {
373 buf_putbyte(ses
.writepayload
, SSH_MSG_IGNORE
);
374 buf_putstring(ses
.writepayload
, "", 0);
378 /* Check all timeouts which are required. Currently these are the time for
379 * user authentication, and the automatic rekeying. */
380 static void checktimeouts() {
386 if (ses
.connect_time
!= 0 && now
- ses
.connect_time
>= AUTH_TIMEOUT
) {
387 dropbear_close("Timeout before auth");
390 /* we can't rekey if we haven't done remote ident exchange yet */
391 if (ses
.remoteident
== NULL
) {
395 if (!ses
.kexstate
.sentkexinit
396 && (now
- ses
.kexstate
.lastkextime
>= KEX_REKEY_TIMEOUT
397 || ses
.kexstate
.datarecv
+ses
.kexstate
.datatrans
>= KEX_REKEY_DATA
)) {
398 TRACE(("rekeying after timeout or max data reached"))
402 if (opts
.keepalive_secs
> 0
403 && now
- ses
.last_trx_packet_time
>= opts
.keepalive_secs
) {
407 if (opts
.idle_timeout_secs
> 0 && ses
.last_packet_time
> 0
408 && now
- ses
.last_packet_time
>= opts
.idle_timeout_secs
) {
409 dropbear_close("Idle timeout");
413 static long select_timeout() {
414 /* determine the minimum timeout that might be required, so
415 as to avoid waking when unneccessary */
417 if (KEX_REKEY_TIMEOUT
> 0)
418 ret
= MIN(KEX_REKEY_TIMEOUT
, ret
);
419 if (AUTH_TIMEOUT
> 0)
420 ret
= MIN(AUTH_TIMEOUT
, ret
);
421 if (opts
.keepalive_secs
> 0)
422 ret
= MIN(opts
.keepalive_secs
, ret
);
423 if (opts
.idle_timeout_secs
> 0)
424 ret
= MIN(opts
.idle_timeout_secs
, ret
);
428 const char* get_user_shell() {
429 /* an empty shell should be interpreted as "/bin/sh" */
430 if (ses
.authstate
.pw_shell
[0] == '\0') {
433 return ses
.authstate
.pw_shell
;
436 void fill_passwd(const char* username
) {
437 struct passwd
*pw
= NULL
;
438 if (ses
.authstate
.pw_name
)
439 m_free(ses
.authstate
.pw_name
);
440 if (ses
.authstate
.pw_dir
)
441 m_free(ses
.authstate
.pw_dir
);
442 if (ses
.authstate
.pw_shell
)
443 m_free(ses
.authstate
.pw_shell
);
444 if (ses
.authstate
.pw_passwd
)
445 m_free(ses
.authstate
.pw_passwd
);
447 pw
= getpwnam(username
);
451 ses
.authstate
.pw_uid
= pw
->pw_uid
;
452 ses
.authstate
.pw_gid
= pw
->pw_gid
;
453 ses
.authstate
.pw_name
= m_strdup(pw
->pw_name
);
454 ses
.authstate
.pw_dir
= m_strdup(pw
->pw_dir
);
455 ses
.authstate
.pw_shell
= m_strdup(pw
->pw_shell
);
456 ses
.authstate
.pw_passwd
= m_strdup(pw
->pw_passwd
);