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
= -1;
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 m_free(ses
.session_id
);
249 m_burn(ses
.keys
, sizeof(struct key_context
));
254 TRACE(("leave session_cleanup"))
257 void send_session_identification() {
258 buffer
*writebuf
= buf_new(strlen(LOCAL_IDENT
"\r\n") + 1);
259 buf_putbytes(writebuf
, LOCAL_IDENT
"\r\n", strlen(LOCAL_IDENT
"\r\n"));
260 buf_putbyte(writebuf
, 0x0); // packet type
261 buf_setpos(writebuf
, 0);
262 enqueue(&ses
.writequeue
, writebuf
);
265 static void read_session_identification() {
266 /* max length of 255 chars */
271 /* If they send more than 50 lines, something is wrong */
272 for (i
= 0; i
< 50; i
++) {
273 len
= ident_readln(ses
.sock_in
, linebuf
, sizeof(linebuf
));
275 if (len
< 0 && errno
!= EINTR
) {
280 if (len
>= 4 && memcmp(linebuf
, "SSH-", 4) == 0) {
281 /* start of line matches */
288 TRACE(("err: %s for '%s'\n", strerror(errno
), linebuf
))
291 /* linebuf is already null terminated */
292 ses
.remoteident
= m_malloc(len
);
293 memcpy(ses
.remoteident
, linebuf
, len
);
296 /* Shall assume that 2.x will be backwards compatible. */
297 if (strncmp(ses
.remoteident
, "SSH-2.", 6) != 0
298 && strncmp(ses
.remoteident
, "SSH-1.99-", 9) != 0) {
299 dropbear_exit("Incompatible remote version '%s'", ses
.remoteident
);
302 TRACE(("remoteident: %s", ses
.remoteident
))
306 /* returns the length including null-terminating zero on success,
307 * or -1 on failure */
308 static int ident_readln(int fd
, char* buf
, int count
) {
314 struct timeval timeout
;
316 TRACE(("enter ident_readln"))
324 /* select since it's a non-blocking fd */
326 /* leave space to null-terminate */
327 while (pos
< count
-1) {
333 if (select(fd
+1, &fds
, NULL
, NULL
, &timeout
) < 0) {
334 if (errno
== EINTR
) {
337 TRACE(("leave ident_readln: select error"))
343 /* Have to go one byte at a time, since we don't want to read past
344 * the end, and have to somehow shove bytes back into the normal
346 if (FD_ISSET(fd
, &fds
)) {
347 num
= read(fd
, &in
, 1);
348 /* a "\n" is a newline, "\r" we want to read in and keep going
349 * so that it won't be read as part of the next line */
352 if (errno
== EINTR
) {
353 continue; /* not a real error */
355 TRACE(("leave ident_readln: read error"))
360 TRACE(("leave ident_readln: EOF"))
364 /* end of ident string */
367 /* we don't want to include '\r's */
376 TRACE(("leave ident_readln: return %d", pos
+1))
380 void send_msg_ignore() {
382 buf_putbyte(ses
.writepayload
, SSH_MSG_IGNORE
);
383 buf_putstring(ses
.writepayload
, "", 0);
387 /* Check all timeouts which are required. Currently these are the time for
388 * user authentication, and the automatic rekeying. */
389 static void checktimeouts() {
395 if (ses
.connect_time
!= 0 && now
- ses
.connect_time
>= AUTH_TIMEOUT
) {
396 dropbear_close("Timeout before auth");
399 /* we can't rekey if we haven't done remote ident exchange yet */
400 if (ses
.remoteident
== NULL
) {
404 if (!ses
.kexstate
.sentkexinit
405 && (now
- ses
.kexstate
.lastkextime
>= KEX_REKEY_TIMEOUT
406 || ses
.kexstate
.datarecv
+ses
.kexstate
.datatrans
>= KEX_REKEY_DATA
)) {
407 TRACE(("rekeying after timeout or max data reached"))
411 if (opts
.keepalive_secs
> 0
412 && now
- ses
.last_trx_packet_time
>= opts
.keepalive_secs
) {
416 if (opts
.idle_timeout_secs
> 0 && ses
.last_packet_time
> 0
417 && now
- ses
.last_packet_time
>= opts
.idle_timeout_secs
) {
418 dropbear_close("Idle timeout");
422 static long select_timeout() {
423 /* determine the minimum timeout that might be required, so
424 as to avoid waking when unneccessary */
426 if (KEX_REKEY_TIMEOUT
> 0)
427 ret
= MIN(KEX_REKEY_TIMEOUT
, ret
);
428 if (AUTH_TIMEOUT
> 0)
429 ret
= MIN(AUTH_TIMEOUT
, ret
);
430 if (opts
.keepalive_secs
> 0)
431 ret
= MIN(opts
.keepalive_secs
, ret
);
432 if (opts
.idle_timeout_secs
> 0)
433 ret
= MIN(opts
.idle_timeout_secs
, ret
);
437 const char* get_user_shell() {
438 /* an empty shell should be interpreted as "/bin/sh" */
439 if (ses
.authstate
.pw_shell
[0] == '\0') {
442 return ses
.authstate
.pw_shell
;
445 void fill_passwd(const char* username
) {
446 struct passwd
*pw
= NULL
;
447 if (ses
.authstate
.pw_name
)
448 m_free(ses
.authstate
.pw_name
);
449 if (ses
.authstate
.pw_dir
)
450 m_free(ses
.authstate
.pw_dir
);
451 if (ses
.authstate
.pw_shell
)
452 m_free(ses
.authstate
.pw_shell
);
453 if (ses
.authstate
.pw_passwd
)
454 m_free(ses
.authstate
.pw_passwd
);
456 pw
= getpwnam(username
);
460 ses
.authstate
.pw_uid
= pw
->pw_uid
;
461 ses
.authstate
.pw_gid
= pw
->pw_gid
;
462 ses
.authstate
.pw_name
= m_strdup(pw
->pw_name
);
463 ses
.authstate
.pw_dir
= m_strdup(pw
->pw_dir
);
464 ses
.authstate
.pw_shell
= m_strdup(pw
->pw_shell
);
466 char *passwd_crypt
= pw
->pw_passwd
;
468 /* get the shadow password if possible */
469 struct spwd
*spasswd
= getspnam(ses
.authstate
.pw_name
);
470 if (spasswd
&& spasswd
->sp_pwdp
) {
471 passwd_crypt
= spasswd
->sp_pwdp
;
475 /* android supposedly returns NULL */
478 ses
.authstate
.pw_passwd
= m_strdup(passwd_crypt
);