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
38 static void checktimeouts();
39 static long select_timeout();
40 static int ident_readln(int fd
, char* buf
, int count
);
41 static void read_session_identification();
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 */
52 /* called only at the start of a session, set up initial state */
53 void common_session_init(int sock_in
, int sock_out
) {
55 TRACE(("enter session_init"))
57 ses
.sock_in
= sock_in
;
58 ses
.sock_out
= sock_out
;
59 ses
.maxfd
= MAX(sock_in
, sock_out
);
62 ses
.last_trx_packet_time
= 0;
63 ses
.last_packet_time
= 0;
65 if (pipe(ses
.signal_pipe
) < 0) {
66 dropbear_exit("Signal pipe failed");
68 setnonblocking(ses
.signal_pipe
[0]);
69 setnonblocking(ses
.signal_pipe
[1]);
71 ses
.maxfd
= MAX(ses
.maxfd
, ses
.signal_pipe
[0]);
72 ses
.maxfd
= MAX(ses
.maxfd
, ses
.signal_pipe
[1]);
74 kexfirstinitialise(); /* initialise the kex state */
76 ses
.writepayload
= buf_new(TRANS_MAX_PAYLOAD_LEN
);
83 initqueue(&ses
.writequeue
);
85 ses
.requirenext
[0] = SSH_MSG_KEXINIT
;
86 ses
.dataallowed
= 1; /* we can send data until we actually
87 send the SSH_MSG_KEXINIT */
90 ses
.reply_queue_head
= NULL
;
91 ses
.reply_queue_tail
= NULL
;
93 /* set all the algos to none */
94 ses
.keys
= (struct key_context
*)m_malloc(sizeof(struct key_context
));
96 ses
.keys
->recv
.algo_crypt
= &dropbear_nocipher
;
97 ses
.keys
->trans
.algo_crypt
= &dropbear_nocipher
;
98 ses
.keys
->recv
.crypt_mode
= &dropbear_mode_none
;
99 ses
.keys
->trans
.crypt_mode
= &dropbear_mode_none
;
101 ses
.keys
->recv
.algo_mac
= &dropbear_nohash
;
102 ses
.keys
->trans
.algo_mac
= &dropbear_nohash
;
104 ses
.keys
->algo_kex
= NULL
;
105 ses
.keys
->algo_hostkey
= -1;
106 ses
.keys
->recv
.algo_comp
= DROPBEAR_COMP_NONE
;
107 ses
.keys
->trans
.algo_comp
= DROPBEAR_COMP_NONE
;
110 ses
.keys
->recv
.zstream
= NULL
;
111 ses
.keys
->trans
.zstream
= NULL
;
114 /* key exchange buffers */
115 ses
.session_id
= NULL
;
116 ses
.kexhashbuf
= NULL
;
117 ses
.transkexinit
= NULL
;
119 ses
.remoteident
= NULL
;
121 ses
.chantypes
= NULL
;
123 ses
.allowprivport
= 0;
125 TRACE(("leave session_init"))
128 void session_loop(void(*loophandler
)()) {
130 fd_set readfd
, writefd
;
131 struct timeval timeout
;
134 /* main loop, select()s for all sockets in use */
137 timeout
.tv_sec
= select_timeout();
141 dropbear_assert(ses
.payload
== NULL
);
143 /* during initial setup we flush out the KEXINIT packet before
144 * attempting to read the remote version string, which might block */
145 if (ses
.sock_in
!= -1 && (ses
.remoteident
|| isempty(&ses
.writequeue
))) {
146 FD_SET(ses
.sock_in
, &readfd
);
148 if (ses
.sock_out
!= -1 && !isempty(&ses
.writequeue
)) {
149 FD_SET(ses
.sock_out
, &writefd
);
152 /* We get woken up when signal handlers write to this pipe.
153 SIGCHLD in svr-chansession is the only one currently. */
154 FD_SET(ses
.signal_pipe
[0], &readfd
);
156 /* set up for channels which require reading/writing */
157 if (ses
.dataallowed
) {
158 setchannelfds(&readfd
, &writefd
);
160 val
= select(ses
.maxfd
+1, &readfd
, &writefd
, NULL
, &timeout
);
163 dropbear_exit("Terminated by signal");
166 if (val
< 0 && errno
!= EINTR
) {
167 dropbear_exit("Error in select");
171 /* If we were interrupted or the select timed out, we still
172 * want to iterate over channels etc for reading, to handle
173 * server processes exiting etc.
174 * We don't want to read/write FDs. */
179 /* We'll just empty out the pipe if required. We don't do
180 any thing with the data, since the pipe's purpose is purely to
181 wake up the select() above. */
182 if (FD_ISSET(ses
.signal_pipe
[0], &readfd
)) {
184 while (read(ses
.signal_pipe
[0], &x
, 1) > 0) {}
187 /* check for auth timeout, rekeying required etc */
190 /* process session socket's incoming/outgoing data */
191 if (ses
.sock_out
!= -1) {
192 if (FD_ISSET(ses
.sock_out
, &writefd
) && !isempty(&ses
.writequeue
)) {
197 if (ses
.sock_in
!= -1) {
198 if (FD_ISSET(ses
.sock_in
, &readfd
)) {
199 if (!ses
.remoteident
) {
200 /* blocking read of the version string */
201 read_session_identification();
207 /* Process the decrypted packet. After this, the read buffer
208 * will be ready for a new packet */
209 if (ses
.payload
!= NULL
) {
214 /* if required, flush out any queued reply packets that
215 were being held up during a KEX */
216 maybe_flush_reply_queue();
218 /* process pipes etc for the channels, ses.dataallowed == 0
219 * during rekeying ) */
220 if (ses
.dataallowed
) {
221 channelio(&readfd
, &writefd
);
233 /* clean up a session on exit */
234 void session_cleanup() {
236 TRACE(("enter session_cleanup"))
238 /* we can't cleanup if we don't know the session state */
240 TRACE(("leave session_cleanup: !sessinitdone"))
244 if (ses
.extra_session_cleanup
) {
245 ses
.extra_session_cleanup();
248 if (ses
.session_id
) {
249 buf_burn(ses
.session_id
);
250 buf_free(ses
.session_id
);
251 ses
.session_id
= NULL
;
258 m_burn(ses
.keys
, sizeof(struct key_context
));
263 TRACE(("leave session_cleanup"))
266 void send_session_identification() {
267 buffer
*writebuf
= buf_new(strlen(LOCAL_IDENT
"\r\n") + 1);
268 buf_putbytes(writebuf
, LOCAL_IDENT
"\r\n", strlen(LOCAL_IDENT
"\r\n"));
269 buf_putbyte(writebuf
, 0x0); /* packet type */
270 buf_setpos(writebuf
, 0);
271 enqueue(&ses
.writequeue
, writebuf
);
274 static void read_session_identification() {
275 /* max length of 255 chars */
280 /* If they send more than 50 lines, something is wrong */
281 for (i
= 0; i
< 50; i
++) {
282 len
= ident_readln(ses
.sock_in
, linebuf
, sizeof(linebuf
));
284 if (len
< 0 && errno
!= EINTR
) {
289 if (len
>= 4 && memcmp(linebuf
, "SSH-", 4) == 0) {
290 /* start of line matches */
297 TRACE(("err: %s for '%s'\n", strerror(errno
), linebuf
))
300 /* linebuf is already null terminated */
301 ses
.remoteident
= m_malloc(len
);
302 memcpy(ses
.remoteident
, linebuf
, len
);
305 /* Shall assume that 2.x will be backwards compatible. */
306 if (strncmp(ses
.remoteident
, "SSH-2.", 6) != 0
307 && strncmp(ses
.remoteident
, "SSH-1.99-", 9) != 0) {
308 dropbear_exit("Incompatible remote version '%s'", ses
.remoteident
);
311 TRACE(("remoteident: %s", ses
.remoteident
))
315 /* returns the length including null-terminating zero on success,
316 * or -1 on failure */
317 static int ident_readln(int fd
, char* buf
, int count
) {
323 struct timeval timeout
;
325 TRACE(("enter ident_readln"))
333 /* select since it's a non-blocking fd */
335 /* leave space to null-terminate */
336 while (pos
< count
-1) {
342 if (select(fd
+1, &fds
, NULL
, NULL
, &timeout
) < 0) {
343 if (errno
== EINTR
) {
346 TRACE(("leave ident_readln: select error"))
352 /* Have to go one byte at a time, since we don't want to read past
353 * the end, and have to somehow shove bytes back into the normal
355 if (FD_ISSET(fd
, &fds
)) {
356 num
= read(fd
, &in
, 1);
357 /* a "\n" is a newline, "\r" we want to read in and keep going
358 * so that it won't be read as part of the next line */
361 if (errno
== EINTR
) {
362 continue; /* not a real error */
364 TRACE(("leave ident_readln: read error"))
369 TRACE(("leave ident_readln: EOF"))
373 /* end of ident string */
376 /* we don't want to include '\r's */
385 TRACE(("leave ident_readln: return %d", pos
+1))
389 void send_msg_ignore() {
391 buf_putbyte(ses
.writepayload
, SSH_MSG_IGNORE
);
392 buf_putstring(ses
.writepayload
, "", 0);
396 /* Check all timeouts which are required. Currently these are the time for
397 * user authentication, and the automatic rekeying. */
398 static void checktimeouts() {
404 if (ses
.connect_time
!= 0 && now
- ses
.connect_time
>= AUTH_TIMEOUT
) {
405 dropbear_close("Timeout before auth");
408 /* we can't rekey if we haven't done remote ident exchange yet */
409 if (ses
.remoteident
== NULL
) {
413 if (!ses
.kexstate
.sentkexinit
414 && (now
- ses
.kexstate
.lastkextime
>= KEX_REKEY_TIMEOUT
415 || ses
.kexstate
.datarecv
+ses
.kexstate
.datatrans
>= KEX_REKEY_DATA
)) {
416 TRACE(("rekeying after timeout or max data reached"))
420 if (opts
.keepalive_secs
> 0
421 && now
- ses
.last_trx_packet_time
>= opts
.keepalive_secs
) {
425 if (opts
.idle_timeout_secs
> 0 && ses
.last_packet_time
> 0
426 && now
- ses
.last_packet_time
>= opts
.idle_timeout_secs
) {
427 dropbear_close("Idle timeout");
431 static long select_timeout() {
432 /* determine the minimum timeout that might be required, so
433 as to avoid waking when unneccessary */
435 if (KEX_REKEY_TIMEOUT
> 0)
436 ret
= MIN(KEX_REKEY_TIMEOUT
, ret
);
437 if (AUTH_TIMEOUT
> 0)
438 ret
= MIN(AUTH_TIMEOUT
, ret
);
439 if (opts
.keepalive_secs
> 0)
440 ret
= MIN(opts
.keepalive_secs
, ret
);
441 if (opts
.idle_timeout_secs
> 0)
442 ret
= MIN(opts
.idle_timeout_secs
, ret
);
446 const char* get_user_shell() {
447 /* an empty shell should be interpreted as "/bin/sh" */
448 if (ses
.authstate
.pw_shell
[0] == '\0') {
451 return ses
.authstate
.pw_shell
;
454 void fill_passwd(const char* username
) {
455 struct passwd
*pw
= NULL
;
456 if (ses
.authstate
.pw_name
)
457 m_free(ses
.authstate
.pw_name
);
458 if (ses
.authstate
.pw_dir
)
459 m_free(ses
.authstate
.pw_dir
);
460 if (ses
.authstate
.pw_shell
)
461 m_free(ses
.authstate
.pw_shell
);
462 if (ses
.authstate
.pw_passwd
)
463 m_free(ses
.authstate
.pw_passwd
);
465 pw
= getpwnam(username
);
469 ses
.authstate
.pw_uid
= pw
->pw_uid
;
470 ses
.authstate
.pw_gid
= pw
->pw_gid
;
471 ses
.authstate
.pw_name
= m_strdup(pw
->pw_name
);
472 ses
.authstate
.pw_dir
= m_strdup(pw
->pw_dir
);
473 ses
.authstate
.pw_shell
= m_strdup(pw
->pw_shell
);
475 char *passwd_crypt
= pw
->pw_passwd
;
477 /* get the shadow password if possible */
478 struct spwd
*spasswd
= getspnam(ses
.authstate
.pw_name
);
479 if (spasswd
&& spasswd
->sp_pwdp
) {
480 passwd_crypt
= spasswd
->sp_pwdp
;
484 /* android supposedly returns NULL */
487 ses
.authstate
.pw_passwd
= m_strdup(passwd_crypt
);