mirror of
https://github.com/clearml/dropbear
synced 2025-06-26 18:17:32 +00:00
Compare commits
83 Commits
DROPBEAR_0
...
DROPBEAR_0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e903109d9f | ||
|
|
a3bb3137ac | ||
|
|
2d28663f53 | ||
|
|
4127be0a95 | ||
|
|
03d78bbb31 | ||
|
|
6013d993b9 | ||
|
|
b7dbb29e68 | ||
|
|
37da919b7d | ||
|
|
9d4ef36e09 | ||
|
|
c0ddf3df0c | ||
|
|
74055a3884 | ||
|
|
8c1a429c44 | ||
|
|
b5bd8591e7 | ||
|
|
e6c957caaa | ||
|
|
6d75298284 | ||
|
|
9d43183704 | ||
|
|
e7677a5e8d | ||
|
|
7dfb2bfcda | ||
|
|
34445aa819 | ||
|
|
42c691a051 | ||
|
|
8b32e8a08c | ||
|
|
37ff2eaf2a | ||
|
|
27aa148e02 | ||
|
|
8f14a1cc38 | ||
|
|
3e53f28f6e | ||
|
|
96823c424d | ||
|
|
08cb903731 | ||
|
|
5c6e8b833e | ||
|
|
028e79ddda | ||
|
|
5ebfa4aaed | ||
|
|
c2af67efd3 | ||
|
|
6766dfae26 | ||
|
|
199b67a68c | ||
|
|
644488a5f3 | ||
|
|
1ace08645a | ||
|
|
636b041b9b | ||
|
|
a41f9dc036 | ||
|
|
448a05ae2c | ||
|
|
099c9a3232 | ||
|
|
2575e227a5 | ||
|
|
fa26b59b0c | ||
|
|
c23ffe4bc1 | ||
|
|
7ed5870ed9 | ||
|
|
8559be015a | ||
|
|
cb0657bdf3 | ||
|
|
15fb479e94 | ||
|
|
0378cffafc | ||
|
|
51fb224ef8 | ||
|
|
a4516b4261 | ||
|
|
a952d23781 | ||
|
|
69282617fd | ||
|
|
51a74b4799 | ||
|
|
2dcd6b22d9 | ||
|
|
857dfdeb9e | ||
|
|
70b7c24cb0 | ||
|
|
e089ee4d85 | ||
|
|
d254e0191d | ||
|
|
0ab18bd14c | ||
|
|
aaac206345 | ||
|
|
f3761a3eda | ||
|
|
aba8a3e2d4 | ||
|
|
545ce7d8bf | ||
|
|
3840a482c7 | ||
|
|
4dd70c433e | ||
|
|
abf66aa7a1 | ||
|
|
1e94425015 | ||
|
|
a69e355a06 | ||
|
|
e17d27d91d | ||
|
|
cc1860bc86 | ||
|
|
f3c8bb2bce | ||
|
|
ee23b01f0b | ||
|
|
3cacc54b78 | ||
|
|
4657ed1446 | ||
|
|
4a52217ed4 | ||
|
|
954a8dce0f | ||
|
|
6ec8183750 | ||
|
|
eb1f647c9c | ||
|
|
029ae35058 | ||
|
|
fdfc95df8b | ||
|
|
5a053fb6fc | ||
|
|
41f5e21044 | ||
|
|
3238bed9c9 | ||
|
|
4936c9a3f3 |
127
CHANGES
127
CHANGES
@@ -1,4 +1,129 @@
|
||||
0.44test1 - Sun Aug 16 2004 17:43:54 +0800
|
||||
0.45 - Mon March 7 2005
|
||||
|
||||
- Makefile no longer appends 'static' to statically linked binaries
|
||||
|
||||
- Add optional SSH_ASKPASS support to the client
|
||||
|
||||
- Respect HOST_LOOKUP option
|
||||
|
||||
- Fix accidentally removed "return;" statement which was removed in 0.44
|
||||
(causing clients which sent an empty terminal-modes string to fail to
|
||||
connect - including pssh, ssh.com, danger hiptop). (patches
|
||||
independently from Paul Fox, David Horwitt and Sven-Ola Tuecke)
|
||||
|
||||
- Read "y/n" response for fingerprints from /dev/tty directly so that dbclient
|
||||
will work with scp.
|
||||
|
||||
0.44 - Mon Jan 3 2005
|
||||
|
||||
- SECURITY: Fix for PAM auth so that usernames are logged and conversation
|
||||
function responses are allocated correctly - all 0.44test4 users with PAM
|
||||
compiled in (not default) are advised to upgrade.
|
||||
|
||||
- Fix calls to getnameinfo() for compatibility with Solaris
|
||||
|
||||
- Pristine compilation works (run 'configure' from a fresh dir and make it
|
||||
there)
|
||||
|
||||
- Fixes for compiling with most options disabled.
|
||||
|
||||
- Upgraded to LibTomCrypt 0.99 and LibTomMath 0.32
|
||||
|
||||
- Make sure that zeroing out of values in LTM and LTC won't get optimised away
|
||||
|
||||
- Removed unused functions from loginrec.c
|
||||
|
||||
- /dev/random is now the default entropy source rather than /dev/urandom
|
||||
|
||||
- Logging of IPs in auth success/failure messages for improved greppability
|
||||
|
||||
- Fix dbclient so that "scp -i keyfile" works. (It can handle "-ikeyfile
|
||||
properly)
|
||||
|
||||
- Avoid a race in server shell-handling code which prevents the exit-code
|
||||
from being returned to the client in some circumstances.
|
||||
|
||||
- Makefile modified so that install target works correctly (doesn't try
|
||||
to install "all" binary) - patch from Juergen Daubert
|
||||
|
||||
- Various minor fixes and compile warnings.
|
||||
|
||||
0.44test4 - Tue Sept 14 2004 21:15:54 +0800
|
||||
|
||||
- Fix inetd mode so it actually loads the hostkeys (oops)
|
||||
|
||||
- Changed DROPBEAR_DEFPORT properly everywhere
|
||||
|
||||
- Fix a small memory leak in the auth code
|
||||
|
||||
- WCOREDUMP is only used on systems which support it (ie not cygwin or AIX)
|
||||
|
||||
- Check (and fail for) cases when we can't negotiate algorithms with the
|
||||
remote side successfully (rather than bombing out ungracefully)
|
||||
|
||||
- Handle authorized_keys files without a terminating newline
|
||||
|
||||
- Fiddle the channel receive window size for possibly better performance
|
||||
|
||||
- Added in the PAM authentication code (finally! thanks to Martin Carlsson)
|
||||
|
||||
0.44test3 - Fri Aug 27 22:20:54 +0800
|
||||
|
||||
- Fixed a bunch of warnings.
|
||||
|
||||
- scp works correctly when passed a username (fix for the dbclient program
|
||||
itself as well, "-lmatt" works as well as "-l matt").
|
||||
|
||||
- Remove unrequired debian files
|
||||
|
||||
- Exit with the remote process's return code for dbclient
|
||||
|
||||
- Display stderr messages from the server in the client
|
||||
|
||||
- Add circular buffering to the channel code. This should dramatically reduce
|
||||
the amount of backtraffic sent in response to traffic incoming to the
|
||||
Dropbear end - improves high-latency performance (ie dialup).
|
||||
|
||||
- Various other related channel-handling fixups.
|
||||
|
||||
- Allow leading lines in the banner when connecting to servers
|
||||
|
||||
- Fixed printing out errors onto the network socket with stderr (for inetd
|
||||
mode when using xinetd)
|
||||
|
||||
- Remove obselete documentation
|
||||
|
||||
- Fix a null-pointer exception when trying to free non-existant listeners
|
||||
at cleanup.
|
||||
|
||||
- DEBUG_TRACE now only works if you add "-v" to the program commandline
|
||||
|
||||
- Don't leave stdin non-blocking on exit - this caused the parent shell
|
||||
of dbclient to close when dbclient exited, for some shells in BusyBox
|
||||
|
||||
- Server connections no longer timeout after 5 minutes
|
||||
|
||||
- Fixed stupid DSS hostkey typo (server couldn't load host keys)
|
||||
|
||||
0.44test2 - Tues Aug 17 2004 17:43:54 +0800
|
||||
|
||||
- Fix up dropbearmulti targets in the Makefile - symlinks are now created
|
||||
|
||||
- Compile fake-rfc2553 even with dropbearconvert/dropbearkey - this
|
||||
allows them to work on platforms without a native getaddrinfo()
|
||||
|
||||
- Create ~/.ssh/known_hosts properly if it doesn't exist
|
||||
|
||||
- Fix basename() function prototype
|
||||
|
||||
- Backport some local changes (more #ifdefs for termcodes.c, a fix for missing
|
||||
defines on AIX).
|
||||
|
||||
- Let dbclient be run as "ssh"
|
||||
|
||||
- Initialise mp_ints by default
|
||||
|
||||
0.44test1 - Sun Aug 16 2005 17:43:54 +0800
|
||||
|
||||
- TESTING RELEASE - this is the first public release of the client codebase,
|
||||
so there are sure to be bugs to be found. In addition, if you're just using
|
||||
|
||||
96
Makefile.in
96
Makefile.in
@@ -1,13 +1,13 @@
|
||||
# This Makefile is for Dropbear SSH Server and Client
|
||||
# @configure_input@
|
||||
|
||||
# invocation:
|
||||
# make PROGRAMS="dropbear dbclient scp" MULTI=1 STATIC=1 SCPPROGRESS=1
|
||||
#
|
||||
# to make a single multiple statically linked binary "staticdropbearmulti",
|
||||
# which includes dropbear, scp and dbclient functionality, and includes the
|
||||
# progress-bar functionality in scp. Hopefully that seems intuitive.
|
||||
|
||||
# This makefile is quite evil.
|
||||
# to make a multiple-program statically linked binary "staticdropbearmulti".
|
||||
# This example will include dropbear, scp, dropbearkey, dropbearconvert, and
|
||||
# dbclient functionality, and includes the progress-bar functionality in scp.
|
||||
# Hopefully that seems intuitive.
|
||||
|
||||
ifndef PROGRAMS
|
||||
PROGRAMS=dropbear dbclient dropbearkey dropbearconvert
|
||||
@@ -20,21 +20,21 @@ COMMONOBJS=dbutil.o buffer.o \
|
||||
dss.o bignum.o \
|
||||
signkey.o rsa.o random.o \
|
||||
queue.o \
|
||||
atomicio.o compat.o
|
||||
atomicio.o compat.o fake-rfc2553.o
|
||||
|
||||
SVROBJS=svr-kex.o svr-algo.o svr-auth.o sshpty.o \
|
||||
svr-authpasswd.o svr-authpubkey.o svr-session.o svr-service.o \
|
||||
svr-chansession.o svr-runopts.o svr-agentfwd.o svr-main.o svr-x11fwd.o\
|
||||
svr-tcpfwd.o
|
||||
svr-tcpfwd.o svr-authpam.o
|
||||
|
||||
CLIOBJS=cli-algo.o cli-main.o cli-auth.o cli-authpasswd.o cli-kex.o \
|
||||
cli-session.o cli-service.o cli-runopts.o cli-chansession.o \
|
||||
cli-authpubkey.o cli-tcpfwd.o
|
||||
cli-authpubkey.o cli-tcpfwd.o cli-channel.o
|
||||
|
||||
CLISVROBJS=common-session.o packet.o common-algo.o common-kex.o \
|
||||
common-channel.o common-chansession.o termcodes.o loginrec.o \
|
||||
tcp-accept.o listener.o process-packet.o \
|
||||
common-runopts.o fake-rfc2553.o
|
||||
common-runopts.o circbuffer.o
|
||||
|
||||
KEYOBJS=dropbearkey.o gendss.o genrsa.o
|
||||
|
||||
@@ -55,6 +55,9 @@ dropbearkeyobjs=$(COMMONOBJS) $(KEYOBJS)
|
||||
dropbearconvertobjs=$(COMMONOBJS) $(CONVERTOBJS)
|
||||
scpobjs=$(SCPOBJS)
|
||||
|
||||
VPATH=@srcdir@
|
||||
srcdir=@srcdir@
|
||||
|
||||
prefix=@prefix@
|
||||
exec_prefix=${prefix}
|
||||
bindir=${exec_prefix}/bin
|
||||
@@ -66,7 +69,7 @@ AR=@AR@
|
||||
RANLIB=@RANLIB@
|
||||
STRIP=@STRIP@
|
||||
INSTALL=@INSTALL@
|
||||
CFLAGS=-Ilibtomcrypt @CFLAGS@
|
||||
CFLAGS=-I. -I$(srcdir)/libtomcrypt @CFLAGS@
|
||||
LIBS=$(LTC) $(LTM) @LIBS@
|
||||
LDFLAGS=@LDFLAGS@
|
||||
|
||||
@@ -90,9 +93,6 @@ export RANLIB AR STRIP
|
||||
|
||||
ifeq ($(STATIC), 1)
|
||||
LDFLAGS+=-static
|
||||
SPREFIX=static
|
||||
else
|
||||
SPREFIX=
|
||||
endif
|
||||
|
||||
ifeq ($(MULTI), 1)
|
||||
@@ -113,27 +113,38 @@ endif
|
||||
all: $(TARGETS)
|
||||
|
||||
strip: $(TARGETS)
|
||||
$(STRIP) $(addsuffix $(EXEEXT), $(addprefix $(SPREFIX), $(TARGETS)))
|
||||
$(STRIP) $(addsuffix $(EXEEXT), $(TARGETS))
|
||||
|
||||
install: $(addprefix install, $(TARGETS))
|
||||
install: $(addprefix inst_, $(TARGETS))
|
||||
|
||||
installdropbearmulti: insdbmulti $(addprefix insmulti, $(PROGRAMS))
|
||||
|
||||
insdbmulti: dropbearmulti
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(bindir)
|
||||
$(INSTALL) -m 755 dropbearmulti$(EXEEXT) $(DESTDIR)$(bindir)
|
||||
-chown root $(DESTDIR)$(bindir)/dropbearmulti$(EXEEXT)
|
||||
-chgrp 0 $(DESTDIR)$(bindir)/dropbearmulti$(EXEEXT)
|
||||
|
||||
insmultidropbear: dropbearmulti
|
||||
-rm -f $(DESTDIR)$(sbindir)/dropbear$(EXEEXT)
|
||||
-ln -s $(DESTDIR)$(bindir)/dropbearmulti$(EXEEXT) $(DESTDIR)$(sbindir)/dropbear$(EXEEXT)
|
||||
|
||||
insmulti%: dropbearmulti
|
||||
-rm -f $(DESTDIR)$(bindir)/$*$(EXEEXT)
|
||||
-ln -s $(DESTDIR)$(bindir)/dropbearmulti$(EXEEXT) $(DESTDIR)$(bindir)/$*$(EXEEXT)
|
||||
|
||||
# dropbear should go in sbin, so it needs a seperate rule
|
||||
installdropbear: dropbear
|
||||
inst_dropbear: dropbear
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(sbindir)
|
||||
$(INSTALL) -m 755 $(SPREFIX)dropbear$(EXEEXT) $(DESTDIR)$(sbindir)
|
||||
-chown root $(DESTDIR)$(sbindir)/$(SPREFIX)dropbear$(EXEEXT)
|
||||
-chgrp 0 $(DESTDIR)$(sbindir)/$(SPREFIX)dropbear$(EXEEXT)
|
||||
$(INSTALL) -m 755 dropbear$(EXEEXT) $(DESTDIR)$(sbindir)
|
||||
-chown root $(DESTDIR)$(sbindir)/dropbear$(EXEEXT)
|
||||
-chgrp 0 $(DESTDIR)$(sbindir)/dropbear$(EXEEXT)
|
||||
|
||||
install%: $*
|
||||
inst_%: $*
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(bindir)
|
||||
$(INSTALL) -m 755 $(SPREFIX)$*$(EXEEXT) $(DESTDIR)$(bindir)
|
||||
-chown root $(DESTDIR)$(sbindir)/$(SPREFIX)$*$(EXEEXT)
|
||||
-chgrp 0 $(DESTDIR)$(sbindir)/$(SPREFIX)$*$(EXEEXT)
|
||||
ifeq ($(MULTI), 1)
|
||||
@echo
|
||||
@echo "You must manually create links for $*"
|
||||
endif
|
||||
|
||||
$(INSTALL) -m 755 $*$(EXEEXT) $(DESTDIR)$(bindir)
|
||||
-chown root $(DESTDIR)$(bindir)/$*$(EXEEXT)
|
||||
-chgrp 0 $(DESTDIR)$(bindir)/$*$(EXEEXT)
|
||||
|
||||
|
||||
# for some reason the rule further down doesn't like $($@objs) as a prereq.
|
||||
@@ -144,11 +155,11 @@ dropbearconvert: $(dropbearconvertobjs)
|
||||
|
||||
dropbear dbclient dropbearkey dropbearconvert: $(HEADERS) $(LTC) $(LTM) \
|
||||
Makefile
|
||||
$(LD) $(LDFLAGS) -o $(SPREFIX)$@$(EXEEXT) $($@objs) $(LIBS)
|
||||
$(LD) $(LDFLAGS) -o $@$(EXEEXT) $($@objs) $(LIBS)
|
||||
|
||||
# scp doesn't use the libs so is special.
|
||||
scp: $(SCPOBJS) $(HEADERS) Makefile
|
||||
$(LD) $(LDFLAGS) -o $(SPREFIX)$@$(EXEEXT) $(SCPOBJS)
|
||||
$(LD) $(LDFLAGS) -o $@$(EXEEXT) $(SCPOBJS)
|
||||
|
||||
|
||||
# multi-binary compilation.
|
||||
@@ -158,11 +169,16 @@ ifeq ($(MULTI),1)
|
||||
CFLAGS+=$(addprefix -DDBMULTI_, $(PROGRAMS)) -DDROPBEAR_MULTI
|
||||
endif
|
||||
|
||||
dropbearmulti: $(HEADERS) $(MULTIOBJS) $(LTC) $(LTM) Makefile
|
||||
$(LD) $(LDFLAGS) -o $(SPREFIX)$@$(EXEEXT) $(MULTIOBJS) $(LIBS)
|
||||
@echo
|
||||
@echo "You should now create symlinks to the programs you have included"
|
||||
@echo "ie 'ln -s dropbearmulti dropbear'"
|
||||
dropbearmulti: multilink
|
||||
|
||||
multibinary: $(HEADERS) $(MULTIOBJS) $(LTC) $(LTM) Makefile
|
||||
$(LD) $(LDFLAGS) -o dropbearmulti$(EXEEXT) $(MULTIOBJS) $(LIBS)
|
||||
|
||||
multilink: multibinary $(addprefix link, $(PROGRAMS))
|
||||
|
||||
link%:
|
||||
-rm -f $*$(EXEEXT)
|
||||
-ln -s dropbearmulti$(EXEEXT) $*$(EXEEXT)
|
||||
|
||||
$(LTC): options.h
|
||||
cd libtomcrypt && $(MAKE) clean && $(MAKE)
|
||||
@@ -179,11 +195,11 @@ ltm-clean:
|
||||
sizes: dropbear
|
||||
objdump -t dropbear|grep ".text"|cut -d "." -f 2|sort -rn
|
||||
|
||||
clean: ltc-clean ltm-clean
|
||||
-rm -f dropbear dbclient dropbearkey dropbearconvert scp scp-progress
|
||||
-rm -f staticdropbear staticdropbearkey staticdropbearconvert staticscp
|
||||
-rm -f dropbearmulti staticdropbearmulti
|
||||
-rm -f *.o *.da *.bb *.bbg *.prof
|
||||
clean: ltc-clean ltm-clean thisclean
|
||||
|
||||
thisclean:
|
||||
-rm -f dropbear dbclient dropbearkey dropbearconvert scp scp-progress \
|
||||
dropbearmulti *.o *.da *.bb *.bbg *.prof
|
||||
|
||||
distclean: clean tidy
|
||||
-rm -f config.h
|
||||
|
||||
19
README
19
README
@@ -17,7 +17,7 @@ matt@ucc.asn.au
|
||||
In the absence of detailed documentation, some notes follow:
|
||||
============================================================================
|
||||
|
||||
Public key auth:
|
||||
Server public key auth:
|
||||
|
||||
You can use ~/.ssh/authorized_keys in the same way as with OpenSSH, just put
|
||||
the key entries in that file. They should be of the form:
|
||||
@@ -32,6 +32,21 @@ OpenSSH sshd manpage, and will not allow a login for these keys.
|
||||
|
||||
============================================================================
|
||||
|
||||
Client public key auth:
|
||||
|
||||
Dropbear can do public key auth as a client, but you will have to convert
|
||||
OpenSSH style keys to Dropbear format, or use dropbearkey to create them.
|
||||
|
||||
If you have an OpenSSH-style private key ~/.ssh/id_rsa, you need to do:
|
||||
|
||||
dropbearconvert openssh dropbear ~/.ssh/id_rsa ~/.ssh/id_rsa.db
|
||||
dbclient -i ~/.ssh/id_rsa.db <hostname>
|
||||
|
||||
Currently encrypted keys aren't supported, neither is agent forwarding. At some
|
||||
stage both hopefully will be.
|
||||
|
||||
============================================================================
|
||||
|
||||
If you want to get the public-key portion of a Dropbear private key, look at
|
||||
dropbearkey's '-y' option.
|
||||
|
||||
@@ -54,6 +69,6 @@ pty, and you cannot login as any user other than that running the daemon
|
||||
|
||||
The Dropbear distribution includes a standalone version of OpenSSH's scp
|
||||
program. You can compile it with "make scp", you may want to change the path
|
||||
of the ssh binary, specified near the top of the scp.c file. By default
|
||||
of the ssh binary, specified by _PATH_SSH_PROGRAM in options.h . By default
|
||||
the progress meter isn't compiled in to save space, you can enable it by
|
||||
adding 'SCPPROGRESS=1' to the make commandline.
|
||||
|
||||
39
SMALL
39
SMALL
@@ -1,25 +1,36 @@
|
||||
Tips for a small system:
|
||||
|
||||
The following are set in options.h
|
||||
If you only want server functionality (for example), compile with
|
||||
make PROGRAMS=dropbear
|
||||
rather than just
|
||||
make dropbear
|
||||
so that client functionality in shared portions of Dropbear won't be included.
|
||||
The same applies if you are compiling just a client.
|
||||
|
||||
- You can safely disable blowfish and twofish ciphers, and MD5 hmac, without
|
||||
affecting interoperability
|
||||
---
|
||||
|
||||
- If you're compiling statically, you can turn off host lookups
|
||||
The following are set in options.h:
|
||||
|
||||
- You can disable either password or public-key authentication, though note
|
||||
that the IETF draft states that pubkey authentication is required.
|
||||
- You can safely disable blowfish and twofish ciphers, and MD5 hmac, without
|
||||
affecting interoperability
|
||||
|
||||
- Similarly with DSS and RSA, you can disable one of these if you know that
|
||||
all clients will be able to support a particular one. The IETF draft
|
||||
states that DSS is required, however you may prefer to use RSA.
|
||||
DON'T disable either of these on systems where you aren't 100% sure about
|
||||
who will be connecting and what clients they will be using.
|
||||
- If you're compiling statically, you can turn off host lookups
|
||||
|
||||
- Disabling the MOTD code and SFTP-SERVER may save a small amount of codesize
|
||||
- You can disable either password or public-key authentication, though note
|
||||
that the IETF draft states that pubkey authentication is required.
|
||||
|
||||
- You can disable x11, tcp and agent forwarding as desired. None of these are
|
||||
essential, although agent-forwarding is often useful even on firewall boxes.
|
||||
- Similarly with DSS and RSA, you can disable one of these if you know that
|
||||
all clients will be able to support a particular one. The IETF draft
|
||||
states that DSS is required, however you may prefer to use RSA.
|
||||
DON'T disable either of these on systems where you aren't 100% sure about
|
||||
who will be connecting and what clients they will be using.
|
||||
|
||||
- Disabling the MOTD code and SFTP-SERVER may save a small amount of codesize
|
||||
|
||||
- You can disable x11, tcp and agent forwarding as desired. None of these are
|
||||
essential, although agent-forwarding is often useful even on firewall boxes.
|
||||
|
||||
---
|
||||
|
||||
If you are compiling statically, you may want to disable zlib, as it will use
|
||||
a few tens of kB of binary-size (./configure --disable-zlib).
|
||||
|
||||
7
TODO
7
TODO
@@ -8,7 +8,7 @@ Things which might need doing:
|
||||
|
||||
- fix agent fwd problems
|
||||
|
||||
- improve channel window adjustment algorithm (circular buffering)
|
||||
- handle /etc/environment in AIX
|
||||
|
||||
- check that there aren't timing issues with valid/invalid user authentication
|
||||
feedback.
|
||||
@@ -20,10 +20,11 @@ Things which might need doing:
|
||||
- CTR mode, SSH_MSG_IGNORE sending to improve CBC security
|
||||
- DH Group Exchange possibly, or just add group14 (whatever it's called today)
|
||||
|
||||
- Use m_burn for clearing sensitive items in LTM/LTC
|
||||
|
||||
- fix scp.c for IRIX
|
||||
|
||||
- Be able to use OpenSSH keys for the client? or at least have some form of
|
||||
encrypted keys.
|
||||
|
||||
- Client agent forwarding
|
||||
|
||||
- Handle restrictions in ~/.ssh/authorized_keys ?
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copied from OpenSSH 3.6.1p2, required for loginrec.c
|
||||
* Copied from OpenSSH 3.6.1p2.
|
||||
*
|
||||
* Copyright (c) 1995,1999 Theo de Raadt. All rights reserved.
|
||||
* All rights reserved.
|
||||
@@ -25,8 +25,6 @@
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* Taken from OpenSSH for use with the loginrec code */
|
||||
|
||||
/* RCSID("OpenBSD: atomicio.c,v 1.10 2001/05/08 22:48:07 markus Exp "); */
|
||||
|
||||
#include "atomicio.h"
|
||||
@@ -42,7 +40,8 @@ atomicio(f, fd, _s, n)
|
||||
size_t n;
|
||||
{
|
||||
char *s = _s;
|
||||
ssize_t res, pos = 0;
|
||||
ssize_t res;
|
||||
size_t pos = 0;
|
||||
|
||||
while (n > pos) {
|
||||
res = (f) (fd, s + pos, n - pos);
|
||||
|
||||
1
auth.h
1
auth.h
@@ -36,6 +36,7 @@ void send_msg_userauth_failure(int partial, int incrfail);
|
||||
void send_msg_userauth_success();
|
||||
void svr_auth_password();
|
||||
void svr_auth_pubkey();
|
||||
void svr_auth_pam();
|
||||
|
||||
/* Client functions */
|
||||
void recv_msg_userauth_failure();
|
||||
|
||||
19
bignum.c
19
bignum.c
@@ -52,25 +52,6 @@ void m_mp_init_multi(mp_int *mp, ...)
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
/* convert an unsigned mp into an array of bytes, malloced.
|
||||
* This array must be freed after use, len contains the length of the array,
|
||||
* if len != NULL */
|
||||
unsigned char* mptobytes(mp_int *mp, int *len) {
|
||||
|
||||
unsigned char* ret;
|
||||
int size;
|
||||
|
||||
size = mp_unsigned_bin_size(mp);
|
||||
ret = m_malloc(size);
|
||||
if (mp_to_unsigned_bin(mp, ret) != MP_OKAY) {
|
||||
dropbear_exit("mem alloc error");
|
||||
}
|
||||
if (len != NULL) {
|
||||
*len = size;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void bytestomp(mp_int *mp, unsigned char* bytes, unsigned int len) {
|
||||
|
||||
if (mp_read_unsigned_bin(mp, bytes, len) != MP_OKAY) {
|
||||
|
||||
1
bignum.h
1
bignum.h
@@ -29,7 +29,6 @@
|
||||
|
||||
void m_mp_init(mp_int *mp);
|
||||
void m_mp_init_multi(mp_int *mp, ...);
|
||||
unsigned char* mptobytes(mp_int *mp, int *len);
|
||||
void bytestomp(mp_int *mp, unsigned char* bytes, unsigned int len);
|
||||
void sha1_process_mp(hash_state *hs, mp_int *mp);
|
||||
|
||||
|
||||
6
buffer.c
6
buffer.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Dropbear - a SSH2 server
|
||||
* Dropbear SSH
|
||||
*
|
||||
* Copyright (c) 2002,2003 Matt Johnston
|
||||
* All rights reserved.
|
||||
@@ -258,7 +258,7 @@ void buf_putbytes(buffer *buf, const unsigned char *bytes, unsigned int len) {
|
||||
void buf_putmpint(buffer* buf, mp_int * mp) {
|
||||
|
||||
unsigned int len, pad = 0;
|
||||
TRACE(("enter buf_putmpint"));
|
||||
TRACE(("enter buf_putmpint"))
|
||||
|
||||
assert(mp != NULL);
|
||||
|
||||
@@ -294,7 +294,7 @@ void buf_putmpint(buffer* buf, mp_int * mp) {
|
||||
buf_incrwritepos(buf, len-pad);
|
||||
}
|
||||
|
||||
TRACE(("leave buf_putmpint"));
|
||||
TRACE(("leave buf_putmpint"))
|
||||
}
|
||||
|
||||
/* Retrieve an mp_int from the buffer.
|
||||
|
||||
36
channel.h
36
channel.h
@@ -27,6 +27,7 @@
|
||||
|
||||
#include "includes.h"
|
||||
#include "buffer.h"
|
||||
#include "circbuffer.h"
|
||||
|
||||
/* channel->type values */
|
||||
#define CHANNEL_ID_NONE 0
|
||||
@@ -44,14 +45,15 @@
|
||||
/* Not a real type */
|
||||
#define SSH_OPEN_IN_PROGRESS 99
|
||||
|
||||
#define MAX_CHANNELS 60 /* simple mem restriction, includes each tcp/x11
|
||||
#define MAX_CHANNELS 100 /* simple mem restriction, includes each tcp/x11
|
||||
connection, so can't be _too_ small */
|
||||
|
||||
#define CHAN_EXTEND_SIZE 3 /* how many extra slots to add when we need more */
|
||||
|
||||
#define RECV_MAXWINDOW 6000 /* tweak */
|
||||
#define RECV_MAXPACKET 1400 /* tweak */
|
||||
#define RECV_MINWINDOW 19000 /* when we get below this, we send a windowadjust */
|
||||
#define RECV_MAXWINDOW 8000 /* tweak */
|
||||
#define RECV_WINDOWEXTEND 1000 /* We send a "window extend" every
|
||||
RECV_WINDOWEXTEND bytes */
|
||||
#define RECV_MAXPACKET RECV_MAXWINDOW /* tweak */
|
||||
|
||||
struct ChanType;
|
||||
|
||||
@@ -60,15 +62,16 @@ struct Channel {
|
||||
unsigned int index; /* the local channel index */
|
||||
unsigned int remotechan;
|
||||
unsigned int recvwindow, transwindow;
|
||||
unsigned int recvdonelen;
|
||||
unsigned int recvmaxpacket, transmaxpacket;
|
||||
void* typedata; /* a pointer to type specific data */
|
||||
int infd; /* stdin for the program, we write to this */
|
||||
int outfd; /* stdout for the program, we read from this */
|
||||
int errfd; /* stdout for a program. This doesn't really fit here,
|
||||
but makes the code a lot tidyer without being too bad. This
|
||||
is -1 for channels which don't requre it. Currently only
|
||||
a 'session' without a pty will use it */
|
||||
buffer *writebuf; /* data for the program */
|
||||
int infd; /* data to send over the wire */
|
||||
int outfd; /* data for consumption, what was in writebuf */
|
||||
int errfd; /* used like infd or errfd, depending if it's client or server.
|
||||
Doesn't exactly belong here, but is cleaner here */
|
||||
circbuffer *writebuf; /* data from the wire, for local consumption */
|
||||
circbuffer *extrabuf; /* extended-data for the program - used like writebuf
|
||||
but for stderr */
|
||||
|
||||
int sentclosed, recvclosed;
|
||||
|
||||
@@ -97,6 +100,7 @@ void chaninitialise();
|
||||
void chancleanup();
|
||||
void setchannelfds(fd_set *readfd, fd_set *writefd);
|
||||
void channelio(fd_set *readfd, fd_set *writefd);
|
||||
struct Channel* getchannel(unsigned int chan);
|
||||
struct Channel* newchannel(unsigned int remotechan,
|
||||
const struct ChanType *type,
|
||||
unsigned int transwindow, unsigned int transmaxpacket);
|
||||
@@ -106,11 +110,19 @@ void recv_msg_channel_request();
|
||||
void send_msg_channel_failure(struct Channel *channel);
|
||||
void send_msg_channel_success(struct Channel *channel);
|
||||
void recv_msg_channel_data();
|
||||
void recv_msg_channel_extended_data();
|
||||
void recv_msg_channel_window_adjust();
|
||||
void recv_msg_channel_close();
|
||||
void recv_msg_channel_eof();
|
||||
|
||||
#ifdef USING_LISTENERS
|
||||
void common_recv_msg_channel_data(struct Channel *channel, int fd,
|
||||
circbuffer * buf);
|
||||
|
||||
#ifdef DROPBEAR_CLIENT
|
||||
const struct ChanType clichansess;
|
||||
#endif
|
||||
|
||||
#if defined(USING_LISTENERS) || defined(DROPBEAR_CLIENT)
|
||||
int send_msg_channel_open_init(int fd, const struct ChanType *type);
|
||||
void recv_msg_channel_open_confirmation();
|
||||
void recv_msg_channel_open_failure();
|
||||
|
||||
@@ -29,6 +29,14 @@
|
||||
#include "channel.h"
|
||||
#include "listener.h"
|
||||
|
||||
struct exitinfo {
|
||||
|
||||
int exitpid; /* -1 if not exited */
|
||||
int exitstatus;
|
||||
int exitsignal;
|
||||
int exitcore;
|
||||
};
|
||||
|
||||
struct ChanSess {
|
||||
|
||||
unsigned char * cmd; /* command to exec */
|
||||
@@ -41,10 +49,7 @@ struct ChanSess {
|
||||
unsigned char * term;
|
||||
|
||||
/* exit details */
|
||||
int exited;
|
||||
int exitstatus;
|
||||
int exitsignal;
|
||||
unsigned char exitcore;
|
||||
struct exitinfo exit;
|
||||
|
||||
#ifndef DISABLE_X11FWD
|
||||
struct Listener * x11listener;
|
||||
@@ -68,11 +73,6 @@ struct ChildPid {
|
||||
};
|
||||
|
||||
|
||||
void chansessionrequest(struct Channel * channel);
|
||||
void send_msg_chansess_exitstatus(struct Channel * channel,
|
||||
struct ChanSess * chansess);
|
||||
void send_msg_chansess_exitsignal(struct Channel * channel,
|
||||
struct ChanSess * chansess);
|
||||
void addnewvar(const char* param, const char* var);
|
||||
|
||||
void cli_send_chansess_request();
|
||||
|
||||
138
circbuffer.c
Normal file
138
circbuffer.c
Normal file
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Dropbear SSH
|
||||
*
|
||||
* Copyright (c) 2002-2004 Matt Johnston
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE. */
|
||||
|
||||
#include "includes.h"
|
||||
#include "dbutil.h"
|
||||
#include "circbuffer.h"
|
||||
|
||||
#define MAX_CBUF_SIZE 100000000
|
||||
|
||||
circbuffer * cbuf_new(unsigned int size) {
|
||||
|
||||
circbuffer *cbuf = NULL;
|
||||
|
||||
if (size > MAX_CBUF_SIZE) {
|
||||
dropbear_exit("bad cbuf size");
|
||||
}
|
||||
|
||||
cbuf = (circbuffer*)m_malloc(sizeof(circbuffer));
|
||||
cbuf->data = (unsigned char*)m_malloc(size);
|
||||
cbuf->used = 0;
|
||||
cbuf->readpos = 0;
|
||||
cbuf->writepos = 0;
|
||||
cbuf->size = size;
|
||||
|
||||
return cbuf;
|
||||
}
|
||||
|
||||
void cbuf_free(circbuffer * cbuf) {
|
||||
|
||||
m_free(cbuf->data);
|
||||
m_free(cbuf);
|
||||
}
|
||||
|
||||
unsigned int cbuf_getused(circbuffer * cbuf) {
|
||||
|
||||
return cbuf->used;
|
||||
|
||||
}
|
||||
|
||||
unsigned int cbuf_getavail(circbuffer * cbuf) {
|
||||
|
||||
return cbuf->size - cbuf->used;
|
||||
|
||||
}
|
||||
|
||||
unsigned int cbuf_readlen(circbuffer *cbuf) {
|
||||
|
||||
assert(((2*cbuf->size)+cbuf->writepos-cbuf->readpos)%cbuf->size == cbuf->used%cbuf->size);
|
||||
assert(((2*cbuf->size)+cbuf->readpos-cbuf->writepos)%cbuf->size == (cbuf->size-cbuf->used)%cbuf->size);
|
||||
|
||||
if (cbuf->used == 0) {
|
||||
TRACE(("cbuf_readlen: unused buffer"))
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cbuf->readpos < cbuf->writepos) {
|
||||
return cbuf->writepos - cbuf->readpos;
|
||||
}
|
||||
|
||||
return cbuf->size - cbuf->readpos;
|
||||
}
|
||||
|
||||
unsigned int cbuf_writelen(circbuffer *cbuf) {
|
||||
|
||||
assert(cbuf->used <= cbuf->size);
|
||||
assert(((2*cbuf->size)+cbuf->writepos-cbuf->readpos)%cbuf->size == cbuf->used%cbuf->size);
|
||||
assert(((2*cbuf->size)+cbuf->readpos-cbuf->writepos)%cbuf->size == (cbuf->size-cbuf->used)%cbuf->size);
|
||||
|
||||
if (cbuf->used == cbuf->size) {
|
||||
TRACE(("cbuf_writelen: full buffer"))
|
||||
return 0; /* full */
|
||||
}
|
||||
|
||||
if (cbuf->writepos < cbuf->readpos) {
|
||||
return cbuf->readpos - cbuf->writepos;
|
||||
}
|
||||
|
||||
return cbuf->size - cbuf->writepos;
|
||||
}
|
||||
|
||||
unsigned char* cbuf_readptr(circbuffer *cbuf, unsigned int len) {
|
||||
if (len > cbuf_readlen(cbuf)) {
|
||||
dropbear_exit("bad cbuf read");
|
||||
}
|
||||
|
||||
return &cbuf->data[cbuf->readpos];
|
||||
}
|
||||
|
||||
unsigned char* cbuf_writeptr(circbuffer *cbuf, unsigned int len) {
|
||||
|
||||
if (len > cbuf_writelen(cbuf)) {
|
||||
dropbear_exit("bad cbuf write");
|
||||
}
|
||||
|
||||
return &cbuf->data[cbuf->writepos];
|
||||
}
|
||||
|
||||
void cbuf_incrwrite(circbuffer *cbuf, unsigned int len) {
|
||||
if (len > cbuf_writelen(cbuf)) {
|
||||
dropbear_exit("bad cbuf write");
|
||||
}
|
||||
|
||||
cbuf->used += len;
|
||||
assert(cbuf->used <= cbuf->size);
|
||||
cbuf->writepos = (cbuf->writepos + len) % cbuf->size;
|
||||
}
|
||||
|
||||
|
||||
void cbuf_incrread(circbuffer *cbuf, unsigned int len) {
|
||||
if (len > cbuf_readlen(cbuf)) {
|
||||
dropbear_exit("bad cbuf read");
|
||||
}
|
||||
|
||||
assert(cbuf->used >= len);
|
||||
cbuf->used -= len;
|
||||
cbuf->readpos = (cbuf->readpos + len) % cbuf->size;
|
||||
}
|
||||
50
circbuffer.h
Normal file
50
circbuffer.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Dropbear SSH
|
||||
*
|
||||
* Copyright (c) 2002-2004 Matt Johnston
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE. */
|
||||
|
||||
#ifndef _CIRCBUFFER_H_
|
||||
#define _CIRCBUFFER_H_
|
||||
struct circbuf {
|
||||
|
||||
unsigned int size;
|
||||
unsigned int readpos;
|
||||
unsigned int writepos;
|
||||
unsigned int used;
|
||||
unsigned char* data;
|
||||
};
|
||||
|
||||
typedef struct circbuf circbuffer;
|
||||
|
||||
circbuffer * cbuf_new(unsigned int size);
|
||||
void cbuf_free(circbuffer * cbuf);
|
||||
|
||||
unsigned int cbuf_getused(circbuffer * cbuf); /* how much data stored */
|
||||
unsigned int cbuf_getavail(circbuffer * cbuf); /* how much we can write */
|
||||
unsigned int cbuf_readlen(circbuffer *cbuf); /* max linear read len */
|
||||
unsigned int cbuf_writelen(circbuffer *cbuf); /* max linear write len */
|
||||
|
||||
unsigned char* cbuf_readptr(circbuffer *cbuf, unsigned int len);
|
||||
unsigned char* cbuf_writeptr(circbuffer *cbuf, unsigned int len);
|
||||
void cbuf_incrwrite(circbuffer *cbuf, unsigned int len);
|
||||
void cbuf_incrread(circbuffer *cbuf, unsigned int len);
|
||||
#endif
|
||||
@@ -46,7 +46,7 @@ algo_type * cli_buf_match_algo(buffer* buf, algo_type localalgos[],
|
||||
|
||||
/* get the comma-separated list from the buffer ie "algo1,algo2,algo3" */
|
||||
algolist = buf_getstring(buf, &len);
|
||||
TRACE(("cli_buf_match_algo: %s", algolist));
|
||||
TRACE(("cli_buf_match_algo: %s", algolist))
|
||||
if (len > MAX_PROPOSED_ALGO*(MAX_NAME_LEN+1)) {
|
||||
goto out; /* just a sanity check, no other use */
|
||||
}
|
||||
|
||||
32
cli-auth.c
32
cli-auth.c
@@ -42,7 +42,7 @@ void cli_authinitialise() {
|
||||
/* Send a "none" auth request to get available methods */
|
||||
void cli_auth_getmethods() {
|
||||
|
||||
TRACE(("enter cli_auth_getmethods"));
|
||||
TRACE(("enter cli_auth_getmethods"))
|
||||
|
||||
CHECKCLEARTOWRITE();
|
||||
|
||||
@@ -54,7 +54,7 @@ void cli_auth_getmethods() {
|
||||
buf_putstring(ses.writepayload, "none", 4); /* 'none' method */
|
||||
|
||||
encrypt_packet();
|
||||
TRACE(("leave cli_auth_getmethods"));
|
||||
TRACE(("leave cli_auth_getmethods"))
|
||||
|
||||
}
|
||||
|
||||
@@ -64,9 +64,9 @@ void recv_msg_userauth_banner() {
|
||||
unsigned int bannerlen;
|
||||
unsigned int i, linecount;
|
||||
|
||||
TRACE(("enter recv_msg_userauth_banner"));
|
||||
TRACE(("enter recv_msg_userauth_banner"))
|
||||
if (ses.authstate.authdone) {
|
||||
TRACE(("leave recv_msg_userauth_banner: banner after auth done"));
|
||||
TRACE(("leave recv_msg_userauth_banner: banner after auth done"))
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ void recv_msg_userauth_banner() {
|
||||
buf_eatstring(ses.payload); /* The language string */
|
||||
|
||||
if (bannerlen > MAX_BANNER_SIZE) {
|
||||
TRACE(("recv_msg_userauth_banner: bannerlen too long: %d", bannerlen));
|
||||
TRACE(("recv_msg_userauth_banner: bannerlen too long: %d", bannerlen))
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ void recv_msg_userauth_banner() {
|
||||
|
||||
out:
|
||||
m_free(banner);
|
||||
TRACE(("leave recv_msg_userauth_banner"));
|
||||
TRACE(("leave recv_msg_userauth_banner"))
|
||||
}
|
||||
|
||||
|
||||
@@ -108,12 +108,12 @@ void recv_msg_userauth_failure() {
|
||||
unsigned int partial = 0;
|
||||
unsigned int i = 0;
|
||||
|
||||
TRACE(("<- MSG_USERAUTH_FAILURE"));
|
||||
TRACE(("enter recv_msg_userauth_failure"));
|
||||
TRACE(("<- MSG_USERAUTH_FAILURE"))
|
||||
TRACE(("enter recv_msg_userauth_failure"))
|
||||
|
||||
if (cli_ses.state != USERAUTH_REQ_SENT) {
|
||||
/* Perhaps we should be more fatal? */
|
||||
TRACE(("But we didn't send a userauth request!!!!!!"));
|
||||
TRACE(("But we didn't send a userauth request!!!!!!"))
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@ void recv_msg_userauth_failure() {
|
||||
ses.authstate.failcount++;
|
||||
}
|
||||
|
||||
TRACE(("Methods (len %d): '%s'", methlen, methods));
|
||||
TRACE(("Methods (len %d): '%s'", methlen, methods))
|
||||
|
||||
ses.authstate.authdone=0;
|
||||
ses.authstate.authtypes=0;
|
||||
@@ -150,7 +150,7 @@ void recv_msg_userauth_failure() {
|
||||
tok = methods; /* tok stores the next method we'll compare */
|
||||
for (i = 0; i <= methlen; i++) {
|
||||
if (methods[i] == '\0') {
|
||||
TRACE(("auth method '%s'", tok));
|
||||
TRACE(("auth method '%s'", tok))
|
||||
#ifdef ENABLE_CLI_PUBKEY_AUTH
|
||||
if (strncmp(AUTH_METHOD_PUBKEY, tok,
|
||||
AUTH_METHOD_PUBKEY_LEN) == 0) {
|
||||
@@ -169,20 +169,22 @@ void recv_msg_userauth_failure() {
|
||||
}
|
||||
}
|
||||
|
||||
m_free(methods);
|
||||
|
||||
cli_ses.state = USERAUTH_FAIL_RCVD;
|
||||
|
||||
TRACE(("leave recv_msg_userauth_failure"));
|
||||
TRACE(("leave recv_msg_userauth_failure"))
|
||||
}
|
||||
|
||||
void recv_msg_userauth_success() {
|
||||
TRACE(("received msg_userauth_success"));
|
||||
TRACE(("received msg_userauth_success"))
|
||||
ses.authstate.authdone = 1;
|
||||
cli_ses.state = USERAUTH_SUCCESS_RCVD;
|
||||
}
|
||||
|
||||
void cli_auth_try() {
|
||||
|
||||
TRACE(("enter cli_auth_try"));
|
||||
TRACE(("enter cli_auth_try"))
|
||||
int finished = 0;
|
||||
|
||||
CHECKCLEARTOWRITE();
|
||||
@@ -206,5 +208,5 @@ void cli_auth_try() {
|
||||
dropbear_exit("No auth methods could be used.");
|
||||
}
|
||||
|
||||
TRACE(("leave cli_auth_try"));
|
||||
TRACE(("leave cli_auth_try"))
|
||||
}
|
||||
|
||||
100
cli-authpasswd.c
100
cli-authpasswd.c
@@ -30,13 +30,105 @@
|
||||
#include "runopts.h"
|
||||
|
||||
#ifdef ENABLE_CLI_PASSWORD_AUTH
|
||||
|
||||
#ifdef ENABLE_CLI_ASKPASS_HELPER
|
||||
/* Returns 1 if we want to use the askpass program, 0 otherwise */
|
||||
static int want_askpass()
|
||||
{
|
||||
char* askpass_prog = NULL;
|
||||
|
||||
askpass_prog = getenv("SSH_ASKPASS");
|
||||
return askpass_prog && !isatty(STDIN_FILENO) && getenv("DISPLAY");
|
||||
}
|
||||
|
||||
/* returns a statically allocated password from a helper app, or NULL
|
||||
* on failure */
|
||||
static char *gui_getpass(const char *prompt) {
|
||||
|
||||
pid_t pid;
|
||||
int p[2], maxlen, len, status;
|
||||
static char buf[DROPBEAR_MAX_CLI_PASS + 1];
|
||||
char* helper = NULL;
|
||||
|
||||
TRACE(("enter gui_getpass"))
|
||||
|
||||
helper = getenv("SSH_ASKPASS");
|
||||
if (!helper)
|
||||
{
|
||||
TRACE(("leave gui_getpass: no askpass program"))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (pipe(p) < 0) {
|
||||
TRACE(("error creating child pipe"))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pid = fork();
|
||||
|
||||
if (pid < 0) {
|
||||
TRACE(("fork error"))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!pid) {
|
||||
/* child */
|
||||
close(p[0]);
|
||||
if (dup2(p[1], STDOUT_FILENO) < 0) {
|
||||
TRACE(("error redirecting stdout"))
|
||||
exit(1);
|
||||
}
|
||||
close(p[1]);
|
||||
execlp(helper, helper, prompt, (char *)0);
|
||||
TRACE(("execlp error"))
|
||||
exit(1);
|
||||
}
|
||||
|
||||
close(p[1]);
|
||||
maxlen = sizeof(buf);
|
||||
while (maxlen > 0) {
|
||||
len = read(p[0], buf + sizeof(buf) - maxlen, maxlen);
|
||||
if (len > 0) {
|
||||
maxlen -= len;
|
||||
} else {
|
||||
if (errno != EINTR)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
close(p[0]);
|
||||
|
||||
while (waitpid(pid, &status, 0) < 0 && errno == EINTR)
|
||||
;
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
|
||||
return(NULL);
|
||||
|
||||
len = sizeof(buf) - maxlen;
|
||||
buf[len] = '\0';
|
||||
if (len > 0 && buf[len - 1] == '\n')
|
||||
buf[len - 1] = '\0';
|
||||
|
||||
TRACE(("leave gui_getpass"))
|
||||
return(buf);
|
||||
}
|
||||
#endif /* ENABLE_CLI_ASKPASS_HELPER */
|
||||
|
||||
int cli_auth_password() {
|
||||
|
||||
char* password = NULL;
|
||||
TRACE(("enter cli_auth_password"));
|
||||
|
||||
TRACE(("enter cli_auth_password"))
|
||||
CHECKCLEARTOWRITE();
|
||||
password = getpass("Password: ");
|
||||
|
||||
#ifdef ENABLE_CLI_ASKPASS_HELPER
|
||||
if (want_askpass())
|
||||
password = gui_getpass("Password: ");
|
||||
else
|
||||
#endif
|
||||
password = getpass("Password: ");
|
||||
|
||||
if (password == NULL)
|
||||
return 0;
|
||||
|
||||
buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_REQUEST);
|
||||
|
||||
@@ -56,8 +148,8 @@ int cli_auth_password() {
|
||||
encrypt_packet();
|
||||
m_burn(password, strlen(password));
|
||||
|
||||
TRACE(("leave cli_auth_password"));
|
||||
TRACE(("leave cli_auth_password"))
|
||||
return 1; /* Password auth can always be tried */
|
||||
|
||||
}
|
||||
#endif
|
||||
#endif /* ENABLE_CLI_PASSWORD_AUTH */
|
||||
|
||||
@@ -41,7 +41,7 @@ void cli_pubkeyfail() {
|
||||
struct PubkeyList *keyitem;
|
||||
struct PubkeyList **previtem;
|
||||
|
||||
TRACE(("enter cli_pubkeyfail"));
|
||||
TRACE(("enter cli_pubkeyfail"))
|
||||
previtem = &cli_opts.pubkeys;
|
||||
|
||||
/* Find the key we failed with, and remove it */
|
||||
@@ -55,7 +55,7 @@ void cli_pubkeyfail() {
|
||||
sign_key_free(cli_ses.lastpubkey->key); /* It won't be used again */
|
||||
m_free(cli_ses.lastpubkey);
|
||||
|
||||
TRACE(("leave cli_pubkeyfail"));
|
||||
TRACE(("leave cli_pubkeyfail"))
|
||||
}
|
||||
|
||||
void recv_msg_userauth_pk_ok() {
|
||||
@@ -67,11 +67,11 @@ void recv_msg_userauth_pk_ok() {
|
||||
int keytype;
|
||||
unsigned int remotelen;
|
||||
|
||||
TRACE(("enter recv_msg_userauth_pk_ok"));
|
||||
TRACE(("enter recv_msg_userauth_pk_ok"))
|
||||
|
||||
algotype = buf_getstring(ses.payload, &algolen);
|
||||
keytype = signkey_type_from_name(algotype, algolen);
|
||||
TRACE(("recv_msg_userauth_pk_ok: type %d", keytype));
|
||||
TRACE(("recv_msg_userauth_pk_ok: type %d", keytype))
|
||||
m_free(algotype);
|
||||
|
||||
keybuf = buf_new(MAX_PUBKEY_SIZE);
|
||||
@@ -84,7 +84,7 @@ void recv_msg_userauth_pk_ok() {
|
||||
|
||||
if (keyitem->type != keytype) {
|
||||
/* Types differed */
|
||||
TRACE(("types differed"));
|
||||
TRACE(("types differed"))
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -98,14 +98,14 @@ void recv_msg_userauth_pk_ok() {
|
||||
|
||||
|
||||
if (keybuf->len-4 != remotelen) {
|
||||
TRACE(("lengths differed: localh %d remote %d", keybuf->len, remotelen));
|
||||
TRACE(("lengths differed: localh %d remote %d", keybuf->len, remotelen))
|
||||
/* Lengths differed */
|
||||
continue;
|
||||
}
|
||||
if (memcmp(buf_getptr(keybuf, remotelen),
|
||||
buf_getptr(ses.payload, remotelen), remotelen) != 0) {
|
||||
/* Data didn't match this key */
|
||||
TRACE(("data differed"));
|
||||
TRACE(("data differed"))
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -114,15 +114,15 @@ void recv_msg_userauth_pk_ok() {
|
||||
}
|
||||
|
||||
if (keyitem != NULL) {
|
||||
TRACE(("matching key"));
|
||||
TRACE(("matching key"))
|
||||
/* XXX TODO: if it's an encrypted key, here we ask for their
|
||||
* password */
|
||||
send_msg_userauth_pubkey(keyitem->key, keytype, 1);
|
||||
} else {
|
||||
TRACE(("That was whacky. We got told that a key was valid, but it didn't match our list. Sounds like dodgy code on Dropbear's part"));
|
||||
TRACE(("That was whacky. We got told that a key was valid, but it didn't match our list. Sounds like dodgy code on Dropbear's part"))
|
||||
}
|
||||
|
||||
TRACE(("leave recv_msg_userauth_pk_ok"));
|
||||
TRACE(("leave recv_msg_userauth_pk_ok"))
|
||||
}
|
||||
|
||||
/* TODO: make it take an agent reference to use as well */
|
||||
@@ -132,7 +132,7 @@ static void send_msg_userauth_pubkey(sign_key *key, int type, int realsign) {
|
||||
int algolen;
|
||||
buffer* sigbuf = NULL;
|
||||
|
||||
TRACE(("enter send_msg_userauth_pubkey"));
|
||||
TRACE(("enter send_msg_userauth_pubkey"))
|
||||
CHECKCLEARTOWRITE();
|
||||
|
||||
buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_REQUEST);
|
||||
@@ -154,7 +154,7 @@ static void send_msg_userauth_pubkey(sign_key *key, int type, int realsign) {
|
||||
buf_put_pub_key(ses.writepayload, key, type);
|
||||
|
||||
if (realsign) {
|
||||
TRACE(("realsign"));
|
||||
TRACE(("realsign"))
|
||||
/* We put the signature as well - this contains string(session id), then
|
||||
* the contents of the write payload to this point */
|
||||
sigbuf = buf_new(4 + SHA1_HASH_SIZE + ses.writepayload->len);
|
||||
@@ -165,22 +165,22 @@ static void send_msg_userauth_pubkey(sign_key *key, int type, int realsign) {
|
||||
}
|
||||
|
||||
encrypt_packet();
|
||||
TRACE(("leave send_msg_userauth_pubkey"));
|
||||
TRACE(("leave send_msg_userauth_pubkey"))
|
||||
}
|
||||
|
||||
int cli_auth_pubkey() {
|
||||
|
||||
TRACE(("enter cli_auth_pubkey"));
|
||||
TRACE(("enter cli_auth_pubkey"))
|
||||
|
||||
if (cli_opts.pubkeys != NULL) {
|
||||
/* Send a trial request */
|
||||
send_msg_userauth_pubkey(cli_opts.pubkeys->key,
|
||||
cli_opts.pubkeys->type, 0);
|
||||
cli_ses.lastpubkey = cli_opts.pubkeys;
|
||||
TRACE(("leave cli_auth_pubkey-success"));
|
||||
TRACE(("leave cli_auth_pubkey-success"))
|
||||
return 1;
|
||||
} else {
|
||||
TRACE(("leave cli_auth_pubkey-failure"));
|
||||
TRACE(("leave cli_auth_pubkey-failure"))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
65
cli-channel.c
Normal file
65
cli-channel.c
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Dropbear SSH
|
||||
*
|
||||
* Copyright (c) 2002-2004 Matt Johnston
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE. */
|
||||
|
||||
#include "includes.h"
|
||||
#include "channel.h"
|
||||
#include "buffer.h"
|
||||
#include "circbuffer.h"
|
||||
#include "dbutil.h"
|
||||
#include "session.h"
|
||||
#include "ssh.h"
|
||||
|
||||
/* We receive channel data - only used by the client chansession code*/
|
||||
void recv_msg_channel_extended_data() {
|
||||
|
||||
unsigned int chan;
|
||||
struct Channel *channel;
|
||||
unsigned int datatype;
|
||||
|
||||
TRACE(("enter recv_msg_channel_extended_data"))
|
||||
|
||||
chan = buf_getint(ses.payload);
|
||||
channel = getchannel(chan);
|
||||
|
||||
if (channel == NULL) {
|
||||
dropbear_exit("Unknown channel");
|
||||
}
|
||||
|
||||
if (channel->type != &clichansess) {
|
||||
TRACE(("leave recv_msg_channel_extended_data: chantype is wrong"))
|
||||
return; /* we just ignore it */
|
||||
}
|
||||
|
||||
datatype = buf_getint(ses.payload);
|
||||
|
||||
if (datatype != SSH_EXTENDED_DATA_STDERR) {
|
||||
TRACE(("leave recv_msg_channel_extended_data: wrong datatype: %d",
|
||||
datatype))
|
||||
return;
|
||||
}
|
||||
|
||||
common_recv_msg_channel_data(channel, channel->errfd, channel->extrabuf);
|
||||
|
||||
TRACE(("leave recv_msg_channel_extended_data"))
|
||||
}
|
||||
@@ -32,9 +32,11 @@
|
||||
#include "ssh.h"
|
||||
#include "runopts.h"
|
||||
#include "termcodes.h"
|
||||
#include "chansession.h"
|
||||
|
||||
static void cli_closechansess(struct Channel *channel);
|
||||
static int cli_initchansess(struct Channel *channel);
|
||||
static void cli_chansessreq(struct Channel *channel);
|
||||
|
||||
static void start_channel_request(struct Channel *channel, unsigned char *type);
|
||||
|
||||
@@ -42,19 +44,43 @@ static void send_chansess_pty_req(struct Channel *channel);
|
||||
static void send_chansess_shell_req(struct Channel *channel);
|
||||
|
||||
static void cli_tty_setup();
|
||||
void cli_tty_cleanup();
|
||||
|
||||
static const struct ChanType clichansess = {
|
||||
const struct ChanType clichansess = {
|
||||
0, /* sepfds */
|
||||
"session", /* name */
|
||||
cli_initchansess, /* inithandler */
|
||||
NULL, /* checkclosehandler */
|
||||
NULL, /* reqhandler */
|
||||
cli_chansessreq, /* reqhandler */
|
||||
cli_closechansess, /* closehandler */
|
||||
};
|
||||
|
||||
static void cli_chansessreq(struct Channel *channel) {
|
||||
|
||||
unsigned char* type = NULL;
|
||||
int wantreply;
|
||||
|
||||
TRACE(("enter cli_chansessreq"))
|
||||
|
||||
type = buf_getstring(ses.payload, NULL);
|
||||
wantreply = buf_getbyte(ses.payload);
|
||||
|
||||
if (strcmp(type, "exit-status") != 0) {
|
||||
TRACE(("unknown request '%s'", type))
|
||||
send_msg_channel_failure(channel);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* We'll just trust what they tell us */
|
||||
cli_ses.retval = buf_getint(ses.payload);
|
||||
TRACE(("got exit-status of '%d'", cli_ses.retval))
|
||||
|
||||
out:
|
||||
m_free(type);
|
||||
}
|
||||
|
||||
|
||||
/* If the main session goes, we close it up */
|
||||
static void cli_closechansess(struct Channel *channel) {
|
||||
static void cli_closechansess(struct Channel *UNUSED(channel)) {
|
||||
|
||||
/* This channel hasn't gone yet, so we have > 1 */
|
||||
if (ses.chancount > 1) {
|
||||
@@ -82,10 +108,10 @@ static void cli_tty_setup() {
|
||||
|
||||
struct termios tio;
|
||||
|
||||
TRACE(("enter cli_pty_setup"));
|
||||
TRACE(("enter cli_pty_setup"))
|
||||
|
||||
if (cli_ses.tty_raw_mode == 1) {
|
||||
TRACE(("leave cli_tty_setup: already in raw mode!"));
|
||||
TRACE(("leave cli_tty_setup: already in raw mode!"))
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -113,15 +139,15 @@ static void cli_tty_setup() {
|
||||
}
|
||||
|
||||
cli_ses.tty_raw_mode = 1;
|
||||
TRACE(("leave cli_tty_setup"));
|
||||
TRACE(("leave cli_tty_setup"))
|
||||
}
|
||||
|
||||
void cli_tty_cleanup() {
|
||||
|
||||
TRACE(("enter cli_tty_cleanup"));
|
||||
TRACE(("enter cli_tty_cleanup"))
|
||||
|
||||
if (cli_ses.tty_raw_mode == 0) {
|
||||
TRACE(("leave cli_tty_cleanup: not in raw mode"));
|
||||
TRACE(("leave cli_tty_cleanup: not in raw mode"))
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -131,12 +157,12 @@ void cli_tty_cleanup() {
|
||||
cli_ses.tty_raw_mode = 0;
|
||||
}
|
||||
|
||||
TRACE(("leave cli_tty_cleanup"));
|
||||
TRACE(("leave cli_tty_cleanup"))
|
||||
}
|
||||
|
||||
static void put_termcodes() {
|
||||
|
||||
TRACE(("enter put_termcodes"));
|
||||
TRACE(("enter put_termcodes"))
|
||||
|
||||
struct termios tio;
|
||||
unsigned int sshcode;
|
||||
@@ -206,7 +232,7 @@ static void put_termcodes() {
|
||||
buf_putint(ses.writepayload, bufpos2 - bufpos1 - 4); /* len(termcodes) */
|
||||
buf_setpos(ses.writepayload, bufpos2); /* Back where we were */
|
||||
|
||||
TRACE(("leave put_termcodes"));
|
||||
TRACE(("leave put_termcodes"))
|
||||
}
|
||||
|
||||
static void put_winsize() {
|
||||
@@ -228,7 +254,7 @@ static void put_winsize() {
|
||||
|
||||
}
|
||||
|
||||
static void sigwinch_handler(int dummy) {
|
||||
static void sigwinch_handler(int UNUSED(unused)) {
|
||||
|
||||
cli_ses.winchange = 1;
|
||||
|
||||
@@ -258,7 +284,7 @@ static void send_chansess_pty_req(struct Channel *channel) {
|
||||
|
||||
unsigned char* term = NULL;
|
||||
|
||||
TRACE(("enter send_chansess_pty_req"));
|
||||
TRACE(("enter send_chansess_pty_req"))
|
||||
|
||||
start_channel_request(channel, "pty-req");
|
||||
|
||||
@@ -284,14 +310,14 @@ static void send_chansess_pty_req(struct Channel *channel) {
|
||||
if (signal(SIGWINCH, sigwinch_handler) == SIG_ERR) {
|
||||
dropbear_exit("signal error");
|
||||
}
|
||||
TRACE(("leave send_chansess_pty_req"));
|
||||
TRACE(("leave send_chansess_pty_req"))
|
||||
}
|
||||
|
||||
static void send_chansess_shell_req(struct Channel *channel) {
|
||||
|
||||
unsigned char* reqtype = NULL;
|
||||
|
||||
TRACE(("enter send_chansess_shell_req"));
|
||||
TRACE(("enter send_chansess_shell_req"))
|
||||
|
||||
if (cli_opts.cmd) {
|
||||
reqtype = "exec";
|
||||
@@ -308,14 +334,22 @@ static void send_chansess_shell_req(struct Channel *channel) {
|
||||
}
|
||||
|
||||
encrypt_packet();
|
||||
TRACE(("leave send_chansess_shell_req"));
|
||||
TRACE(("leave send_chansess_shell_req"))
|
||||
}
|
||||
|
||||
static int cli_initchansess(struct Channel *channel) {
|
||||
|
||||
|
||||
channel->infd = STDOUT_FILENO;
|
||||
//channel->outfd = STDIN_FILENO;
|
||||
//channel->errfd = STDERR_FILENO;
|
||||
setnonblocking(STDOUT_FILENO);
|
||||
|
||||
channel->outfd = STDIN_FILENO;
|
||||
setnonblocking(STDIN_FILENO);
|
||||
|
||||
channel->errfd = STDERR_FILENO;
|
||||
setnonblocking(STDERR_FILENO);
|
||||
|
||||
channel->extrabuf = cbuf_new(RECV_MAXWINDOW);
|
||||
|
||||
if (cli_opts.wantpty) {
|
||||
send_chansess_pty_req(channel);
|
||||
@@ -333,7 +367,7 @@ static int cli_initchansess(struct Channel *channel) {
|
||||
|
||||
void cli_send_chansess_request() {
|
||||
|
||||
TRACE(("enter cli_send_chansess_request"));
|
||||
TRACE(("enter cli_send_chansess_request"))
|
||||
if (send_msg_channel_open_init(STDIN_FILENO, &clichansess)
|
||||
== DROPBEAR_FAILURE) {
|
||||
dropbear_exit("Couldn't open initial channel");
|
||||
@@ -341,6 +375,6 @@ void cli_send_chansess_request() {
|
||||
|
||||
/* No special channel request data */
|
||||
encrypt_packet();
|
||||
TRACE(("leave cli_send_chansess_request"));
|
||||
TRACE(("leave cli_send_chansess_request"))
|
||||
|
||||
}
|
||||
|
||||
77
cli-kex.c
77
cli-kex.c
@@ -45,8 +45,8 @@ void send_msg_kexdh_init() {
|
||||
|
||||
cli_ses.dh_e = (mp_int*)m_malloc(sizeof(mp_int));
|
||||
cli_ses.dh_x = (mp_int*)m_malloc(sizeof(mp_int));
|
||||
|
||||
m_mp_init_multi(cli_ses.dh_e, cli_ses.dh_x, NULL);
|
||||
|
||||
gen_kexdh_vals(cli_ses.dh_e, cli_ses.dh_x);
|
||||
|
||||
CHECKCLEARTOWRITE();
|
||||
@@ -59,15 +59,20 @@ void send_msg_kexdh_init() {
|
||||
/* Handle a diffie-hellman key exchange reply. */
|
||||
void recv_msg_kexdh_reply() {
|
||||
|
||||
mp_int dh_f;
|
||||
DEF_MP_INT(dh_f);
|
||||
sign_key *hostkey = NULL;
|
||||
unsigned int type, keybloblen;
|
||||
unsigned char* keyblob = NULL;
|
||||
|
||||
|
||||
TRACE(("enter recv_msg_kexdh_reply"));
|
||||
TRACE(("enter recv_msg_kexdh_reply"))
|
||||
|
||||
if (cli_ses.kex_state != KEXDH_INIT_SENT) {
|
||||
dropbear_exit("Received out-of-order kexdhreply");
|
||||
}
|
||||
m_mp_init(&dh_f);
|
||||
type = ses.newkeys->algo_hostkey;
|
||||
TRACE(("type is %d", type));
|
||||
TRACE(("type is %d", type))
|
||||
|
||||
hostkey = new_sign_key();
|
||||
keybloblen = buf_getint(ses.payload);
|
||||
@@ -79,18 +84,20 @@ void recv_msg_kexdh_reply() {
|
||||
}
|
||||
|
||||
if (buf_get_pub_key(ses.payload, hostkey, &type) != DROPBEAR_SUCCESS) {
|
||||
TRACE(("failed getting pubkey"));
|
||||
TRACE(("failed getting pubkey"))
|
||||
dropbear_exit("Bad KEX packet");
|
||||
}
|
||||
|
||||
m_mp_init(&dh_f);
|
||||
if (buf_getmpint(ses.payload, &dh_f) != DROPBEAR_SUCCESS) {
|
||||
TRACE(("failed getting mpint"));
|
||||
TRACE(("failed getting mpint"))
|
||||
dropbear_exit("Bad KEX packet");
|
||||
}
|
||||
|
||||
kexdh_comb_key(cli_ses.dh_e, cli_ses.dh_x, &dh_f, hostkey);
|
||||
mp_clear(&dh_f);
|
||||
mp_clear_multi(cli_ses.dh_e, cli_ses.dh_x, NULL);
|
||||
m_free(cli_ses.dh_e);
|
||||
m_free(cli_ses.dh_x);
|
||||
|
||||
if (buf_verify(ses.payload, hostkey, ses.hash, SHA1_HASH_SIZE)
|
||||
!= DROPBEAR_SUCCESS) {
|
||||
@@ -102,19 +109,29 @@ void recv_msg_kexdh_reply() {
|
||||
|
||||
send_msg_newkeys();
|
||||
ses.requirenext = SSH_MSG_NEWKEYS;
|
||||
TRACE(("leave recv_msg_kexdh_init"));
|
||||
TRACE(("leave recv_msg_kexdh_init"))
|
||||
}
|
||||
|
||||
static void ask_to_confirm(unsigned char* keyblob, unsigned int keybloblen) {
|
||||
|
||||
char* fp = NULL;
|
||||
FILE *tty = NULL;
|
||||
char response = 'z';
|
||||
|
||||
fp = sign_key_fingerprint(keyblob, keybloblen);
|
||||
fprintf(stderr, "\nHost '%s' is not in the trusted hosts file.\n(fingerprint %s)\nDo you want to continue connecting? (y/n)\n",
|
||||
cli_opts.remotehost,
|
||||
fp);
|
||||
|
||||
if (getc(stdin) == 'y') {
|
||||
tty = fopen(_PATH_TTY, "r");
|
||||
if (tty) {
|
||||
response = getc(tty);
|
||||
fclose(tty);
|
||||
} else {
|
||||
response = getc(stdin);
|
||||
}
|
||||
|
||||
if (response == 'y') {
|
||||
m_free(fp);
|
||||
return;
|
||||
}
|
||||
@@ -147,21 +164,30 @@ static void checkhostkey(unsigned char* keyblob, unsigned int keybloblen) {
|
||||
/* Check that ~/.ssh exists - easiest way is just to mkdir */
|
||||
if (mkdir(filename, S_IRWXU) != 0) {
|
||||
if (errno != EEXIST) {
|
||||
dropbear_log(LOG_INFO, "Warning: failed creating ~/.ssh: %s",
|
||||
strerror(errno));
|
||||
TRACE(("mkdir didn't work: %s", strerror(errno)))
|
||||
ask_to_confirm(keyblob, keybloblen);
|
||||
goto out; /* only get here on success */
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(filename, len+18, "%s/.ssh/known_hosts", pw->pw_dir);
|
||||
hostsfile = fopen(filename, "r+");
|
||||
hostsfile = fopen(filename, "a+");
|
||||
|
||||
/* We mightn't have been able to open it if it was read-only */
|
||||
if (hostsfile == NULL && (errno == EACCES || errno == EROFS)) {
|
||||
readonly = 1;
|
||||
hostsfile = fopen(filename, "r");
|
||||
if (hostsfile != NULL) {
|
||||
fseek(hostsfile, 0, SEEK_SET);
|
||||
} else {
|
||||
/* We mightn't have been able to open it if it was read-only */
|
||||
if (errno == EACCES || errno == EROFS) {
|
||||
TRACE(("trying readonly: %s", strerror(errno)))
|
||||
readonly = 1;
|
||||
hostsfile = fopen(filename, "r");
|
||||
}
|
||||
}
|
||||
|
||||
if (hostsfile == NULL) {
|
||||
TRACE(("hostsfile didn't open: %s", strerror(errno)))
|
||||
ask_to_confirm(keyblob, keybloblen);
|
||||
goto out; /* We only get here on success */
|
||||
}
|
||||
@@ -172,7 +198,7 @@ static void checkhostkey(unsigned char* keyblob, unsigned int keybloblen) {
|
||||
|
||||
do {
|
||||
if (buf_getline(line, hostsfile) == DROPBEAR_FAILURE) {
|
||||
TRACE(("failed reading line: prob EOF"));
|
||||
TRACE(("failed reading line: prob EOF"))
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -181,32 +207,32 @@ static void checkhostkey(unsigned char* keyblob, unsigned int keybloblen) {
|
||||
* buf_getfoo() past the end and die horribly - the base64 parsing
|
||||
* code is what tiptoes up to the end nicely */
|
||||
if (line->len < (hostlen+30) ) {
|
||||
TRACE(("line is too short to be sensible"));
|
||||
TRACE(("line is too short to be sensible"))
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Compare hostnames */
|
||||
if (strncmp(cli_opts.remotehost, buf_getptr(line, hostlen),
|
||||
hostlen) != 0) {
|
||||
TRACE(("hosts don't match"));
|
||||
TRACE(("hosts don't match"))
|
||||
continue;
|
||||
}
|
||||
|
||||
buf_incrpos(line, hostlen);
|
||||
if (buf_getbyte(line) != ' ') {
|
||||
/* there wasn't a space after the hostname, something dodgy */
|
||||
TRACE(("missing space afte matching hostname"));
|
||||
TRACE(("missing space afte matching hostname"))
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( strncmp(buf_getptr(line, algolen), algoname, algolen) != 0) {
|
||||
TRACE(("algo doesn't match"));
|
||||
TRACE(("algo doesn't match"))
|
||||
continue;
|
||||
}
|
||||
|
||||
buf_incrpos(line, algolen);
|
||||
if (buf_getbyte(line) != ' ') {
|
||||
TRACE(("missing space after algo"));
|
||||
TRACE(("missing space after algo"))
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -215,7 +241,7 @@ static void checkhostkey(unsigned char* keyblob, unsigned int keybloblen) {
|
||||
|
||||
if (ret == DROPBEAR_SUCCESS) {
|
||||
/* Good matching key */
|
||||
TRACE(("good matching key"));
|
||||
TRACE(("good matching key"))
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -228,11 +254,12 @@ static void checkhostkey(unsigned char* keyblob, unsigned int keybloblen) {
|
||||
/* If we get here, they said yes */
|
||||
|
||||
if (readonly) {
|
||||
TRACE(("readonly"))
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* put the new entry in the file */
|
||||
fseek(hostsfile, 0, SEEK_END);
|
||||
fseek(hostsfile, 0, SEEK_END); /* In case it wasn't opened append */
|
||||
buf_setpos(line, 0);
|
||||
buf_setlen(line, 0);
|
||||
buf_putbytes(line, ses.remotehost, hostlen);
|
||||
@@ -240,7 +267,7 @@ static void checkhostkey(unsigned char* keyblob, unsigned int keybloblen) {
|
||||
buf_putbytes(line, algoname, algolen);
|
||||
buf_putbyte(line, ' ');
|
||||
len = line->size - line->pos;
|
||||
TRACE(("keybloblen %d, len %d", keybloblen, len));
|
||||
TRACE(("keybloblen %d, len %d", keybloblen, len))
|
||||
/* The only failure with base64 is buffer_overflow, but buf_getwriteptr
|
||||
* will die horribly in the case anyway */
|
||||
base64_encode(keyblob, keybloblen, buf_getwriteptr(line, len), &len);
|
||||
@@ -255,5 +282,7 @@ out:
|
||||
fclose(hostsfile);
|
||||
}
|
||||
m_free(filename);
|
||||
buf_free(line);
|
||||
if (line != NULL) {
|
||||
buf_free(line);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,11 @@ int main(int argc, char ** argv) {
|
||||
cli_getopts(argc, argv);
|
||||
|
||||
TRACE(("user='%s' host='%s' port='%s'", cli_opts.username,
|
||||
cli_opts.remotehost, cli_opts.remoteport));
|
||||
cli_opts.remotehost, cli_opts.remoteport))
|
||||
|
||||
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
|
||||
dropbear_exit("signal() error");
|
||||
}
|
||||
|
||||
sock = connect_remote(cli_opts.remotehost, cli_opts.remoteport,
|
||||
0, &error);
|
||||
@@ -96,7 +100,8 @@ static void cli_dropbear_exit(int exitcode, const char* format, va_list param) {
|
||||
exit(exitcode);
|
||||
}
|
||||
|
||||
static void cli_dropbear_log(int priority, const char* format, va_list param) {
|
||||
static void cli_dropbear_log(int UNUSED(priority),
|
||||
const char* format, va_list param) {
|
||||
|
||||
char printbuf[1024];
|
||||
|
||||
|
||||
@@ -59,6 +59,9 @@ static void printhelp() {
|
||||
"-R <listenport:remotehost:remoteport> Remote port forwarding\n"
|
||||
#endif
|
||||
"-l <username>\n"
|
||||
#ifdef DEBUG_TRACE
|
||||
"-v verbose\n"
|
||||
#endif
|
||||
,DROPBEAR_VERSION, cli_opts.progname);
|
||||
}
|
||||
|
||||
@@ -113,7 +116,7 @@ void cli_getopts(int argc, char ** argv) {
|
||||
#endif
|
||||
#ifdef ENABLE_CLI_REMOTETCPFWD
|
||||
if (nextisremote) {
|
||||
TRACE(("nextisremote true"));
|
||||
TRACE(("nextisremote true"))
|
||||
addforward(argv[i], &cli_opts.remotefwds);
|
||||
nextisremote = 0;
|
||||
continue;
|
||||
@@ -121,7 +124,7 @@ void cli_getopts(int argc, char ** argv) {
|
||||
#endif
|
||||
#ifdef ENABLE_CLI_LOCALTCPFWD
|
||||
if (nextislocal) {
|
||||
TRACE(("nextislocal true"));
|
||||
TRACE(("nextislocal true"))
|
||||
addforward(argv[i], &cli_opts.localfwds);
|
||||
nextislocal = 0;
|
||||
continue;
|
||||
@@ -140,19 +143,18 @@ void cli_getopts(int argc, char ** argv) {
|
||||
if (argv[i][0] == '-') {
|
||||
/* A flag *waves* */
|
||||
|
||||
if (strlen(argv[i]) > 2) {
|
||||
fprintf(stderr,
|
||||
"WARNING: Ignoring unknown argument '%s'\n", argv[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (argv[i][1]) {
|
||||
case 'p': /* remoteport */
|
||||
next = &cli_opts.remoteport;
|
||||
break;
|
||||
#ifdef ENABLE_CLI_PUBKEY_AUTH
|
||||
case 'i': /* an identityfile */
|
||||
nextiskey = 1;
|
||||
/* Keep scp happy when it changes "-i file" to "-ifile" */
|
||||
if (strlen(argv[i]) > 2) {
|
||||
loadidentityfile(&argv[i][2]);
|
||||
} else {
|
||||
nextiskey = 1;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case 't': /* we want a pty */
|
||||
@@ -178,6 +180,11 @@ void cli_getopts(int argc, char ** argv) {
|
||||
printhelp();
|
||||
exit(EXIT_SUCCESS);
|
||||
break;
|
||||
#ifdef DEBUG_TRACE
|
||||
case 'v':
|
||||
debug_trace = 1;
|
||||
break;
|
||||
#endif
|
||||
case 'F':
|
||||
case 'e':
|
||||
case 'c':
|
||||
@@ -197,11 +204,17 @@ void cli_getopts(int argc, char ** argv) {
|
||||
"WARNING: Ignoring unknown argument '%s'\n", argv[i]);
|
||||
break;
|
||||
} /* Switch */
|
||||
|
||||
/* Now we handle args where they might be "-luser" (no spaces)*/
|
||||
if (next && strlen(argv[i]) > 2) {
|
||||
*next = &argv[i][2];
|
||||
next = NULL;
|
||||
}
|
||||
|
||||
continue; /* next argument */
|
||||
|
||||
} else {
|
||||
TRACE(("non-flag arg: '%s'", argv[i]));
|
||||
TRACE(("non-flag arg: '%s'", argv[i]))
|
||||
|
||||
/* Either the hostname or commands */
|
||||
|
||||
@@ -330,7 +343,7 @@ static void addforward(char* origstr, struct TCPFwdList** fwdlist) {
|
||||
struct TCPFwdList* newfwd = NULL;
|
||||
char * str = NULL;
|
||||
|
||||
TRACE(("enter addforward"));
|
||||
TRACE(("enter addforward"))
|
||||
|
||||
/* We probably don't want to be editing argvs */
|
||||
str = m_strdup(origstr);
|
||||
@@ -339,7 +352,7 @@ static void addforward(char* origstr, struct TCPFwdList** fwdlist) {
|
||||
|
||||
connectaddr = strchr(str, ':');
|
||||
if (connectaddr == NULL) {
|
||||
TRACE(("connectaddr == NULL"));
|
||||
TRACE(("connectaddr == NULL"))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@@ -348,7 +361,7 @@ static void addforward(char* origstr, struct TCPFwdList** fwdlist) {
|
||||
|
||||
connectport = strchr(connectaddr, ':');
|
||||
if (connectport == NULL) {
|
||||
TRACE(("connectport == NULL"));
|
||||
TRACE(("connectport == NULL"))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@@ -361,32 +374,32 @@ static void addforward(char* origstr, struct TCPFwdList** fwdlist) {
|
||||
* the check later only checks for >= MAX_PORT */
|
||||
newfwd->listenport = strtol(listenport, NULL, 10);
|
||||
if (errno != 0) {
|
||||
TRACE(("bad listenport strtol"));
|
||||
TRACE(("bad listenport strtol"))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
newfwd->connectport = strtol(connectport, NULL, 10);
|
||||
if (errno != 0) {
|
||||
TRACE(("bad connectport strtol"));
|
||||
TRACE(("bad connectport strtol"))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
newfwd->connectaddr = connectaddr;
|
||||
|
||||
if (newfwd->listenport > 65535) {
|
||||
TRACE(("listenport > 65535"));
|
||||
TRACE(("listenport > 65535"))
|
||||
goto badport;
|
||||
}
|
||||
|
||||
if (newfwd->connectport > 65535) {
|
||||
TRACE(("connectport > 65535"));
|
||||
TRACE(("connectport > 65535"))
|
||||
goto badport;
|
||||
}
|
||||
|
||||
newfwd->next = *fwdlist;
|
||||
*fwdlist = newfwd;
|
||||
|
||||
TRACE(("leave addforward: done"));
|
||||
TRACE(("leave addforward: done"))
|
||||
return;
|
||||
|
||||
fail:
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
|
||||
void send_msg_service_request(char* servicename) {
|
||||
|
||||
TRACE(("enter send_msg_service_request: servicename='%s'", servicename));
|
||||
TRACE(("enter send_msg_service_request: servicename='%s'", servicename))
|
||||
|
||||
CHECKCLEARTOWRITE();
|
||||
|
||||
@@ -41,7 +41,7 @@ void send_msg_service_request(char* servicename) {
|
||||
buf_putstring(ses.writepayload, servicename, strlen(servicename));
|
||||
|
||||
encrypt_packet();
|
||||
TRACE(("leave send_msg_service_request"));
|
||||
TRACE(("leave send_msg_service_request"))
|
||||
}
|
||||
|
||||
/* This just sets up the state variables right for the main client session loop
|
||||
@@ -51,7 +51,7 @@ void recv_msg_service_accept() {
|
||||
unsigned char* servicename;
|
||||
unsigned int len;
|
||||
|
||||
TRACE(("enter recv_msg_service_accept"));
|
||||
TRACE(("enter recv_msg_service_accept"))
|
||||
|
||||
servicename = buf_getstring(ses.payload, &len);
|
||||
|
||||
@@ -62,7 +62,7 @@ void recv_msg_service_accept() {
|
||||
|
||||
cli_ses.state = SERVICE_AUTH_ACCEPT_RCVD;
|
||||
m_free(servicename);
|
||||
TRACE(("leave recv_msg_service_accept: done ssh-userauth"));
|
||||
TRACE(("leave recv_msg_service_accept: done ssh-userauth"))
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ void recv_msg_service_accept() {
|
||||
|
||||
cli_ses.state = SERVICE_CONN_ACCEPT_RCVD;
|
||||
m_free(servicename);
|
||||
TRACE(("leave recv_msg_service_accept: done ssh-connection"));
|
||||
TRACE(("leave recv_msg_service_accept: done ssh-connection"))
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ struct clientsession cli_ses; /* GLOBAL */
|
||||
static const packettype cli_packettypes[] = {
|
||||
/* TYPE, FUNCTION */
|
||||
{SSH_MSG_CHANNEL_DATA, recv_msg_channel_data},
|
||||
{SSH_MSG_CHANNEL_EXTENDED_DATA, recv_msg_channel_extended_data},
|
||||
{SSH_MSG_CHANNEL_WINDOW_ADJUST, recv_msg_channel_window_adjust},
|
||||
{SSH_MSG_USERAUTH_FAILURE, recv_msg_userauth_failure}, /* client */
|
||||
{SSH_MSG_USERAUTH_SUCCESS, recv_msg_userauth_success}, /* client */
|
||||
@@ -112,9 +113,17 @@ static void cli_session_init() {
|
||||
cli_ses.tty_raw_mode = 0;
|
||||
cli_ses.winchange = 0;
|
||||
|
||||
/* We store stdin's flags, so we can set them back on exit (otherwise
|
||||
* busybox's ash isn't happy */
|
||||
cli_ses.stdincopy = dup(STDIN_FILENO);
|
||||
cli_ses.stdinflags = fcntl(STDIN_FILENO, F_GETFL, 0);
|
||||
|
||||
cli_ses.retval = EXIT_SUCCESS; /* Assume it's clean if we don't get a
|
||||
specific exit status */
|
||||
|
||||
/* Auth */
|
||||
cli_ses.lastpubkey = NULL;
|
||||
cli_ses.lastauthtype = NULL;
|
||||
cli_ses.lastauthtype = 0;
|
||||
|
||||
/* For printing "remote host closed" for the user */
|
||||
ses.remoteclosed = cli_remoteclosed;
|
||||
@@ -130,7 +139,7 @@ static void cli_session_init() {
|
||||
* service, userauth and channel requests */
|
||||
static void cli_sessionloop() {
|
||||
|
||||
TRACE(("enter cli_sessionloop"));
|
||||
TRACE(("enter cli_sessionloop"))
|
||||
|
||||
if (ses.lastpacket == SSH_MSG_KEXINIT && cli_ses.kex_state == KEX_NOTHING) {
|
||||
cli_ses.kex_state = KEXINIT_RCVD;
|
||||
@@ -142,7 +151,7 @@ static void cli_sessionloop() {
|
||||
* negotiation would have failed. */
|
||||
send_msg_kexdh_init();
|
||||
cli_ses.kex_state = KEXDH_INIT_SENT;
|
||||
TRACE(("leave cli_sessionloop: done with KEXINIT_RCVD"));
|
||||
TRACE(("leave cli_sessionloop: done with KEXINIT_RCVD"))
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -154,14 +163,14 @@ static void cli_sessionloop() {
|
||||
|
||||
/* We shouldn't do anything else if a KEX is in progress */
|
||||
if (cli_ses.kex_state != KEX_NOTHING) {
|
||||
TRACE(("leave cli_sessionloop: kex_state != KEX_NOTHING"));
|
||||
TRACE(("leave cli_sessionloop: kex_state != KEX_NOTHING"))
|
||||
return;
|
||||
}
|
||||
|
||||
/* We should exit if we haven't donefirstkex: we shouldn't reach here
|
||||
* in normal operation */
|
||||
if (ses.kexstate.donefirstkex == 0) {
|
||||
TRACE(("XXX XXX might be bad! leave cli_sessionloop: haven't donefirstkex"));
|
||||
TRACE(("XXX XXX might be bad! leave cli_sessionloop: haven't donefirstkex"))
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -172,32 +181,32 @@ static void cli_sessionloop() {
|
||||
* userauth */
|
||||
send_msg_service_request(SSH_SERVICE_USERAUTH);
|
||||
cli_ses.state = SERVICE_AUTH_REQ_SENT;
|
||||
TRACE(("leave cli_sessionloop: sent userauth service req"));
|
||||
TRACE(("leave cli_sessionloop: sent userauth service req"))
|
||||
return;
|
||||
|
||||
/* userauth code */
|
||||
case SERVICE_AUTH_ACCEPT_RCVD:
|
||||
cli_auth_getmethods();
|
||||
cli_ses.state = USERAUTH_REQ_SENT;
|
||||
TRACE(("leave cli_sessionloop: sent userauth methods req"));
|
||||
TRACE(("leave cli_sessionloop: sent userauth methods req"))
|
||||
return;
|
||||
|
||||
case USERAUTH_FAIL_RCVD:
|
||||
cli_auth_try();
|
||||
cli_ses.state = USERAUTH_REQ_SENT;
|
||||
TRACE(("leave cli_sessionloop: cli_auth_try"));
|
||||
TRACE(("leave cli_sessionloop: cli_auth_try"))
|
||||
return;
|
||||
|
||||
/*
|
||||
case USERAUTH_SUCCESS_RCVD:
|
||||
send_msg_service_request(SSH_SERVICE_CONNECTION);
|
||||
cli_ses.state = SERVICE_CONN_REQ_SENT;
|
||||
TRACE(("leave cli_sessionloop: sent ssh-connection service req"));
|
||||
TRACE(("leave cli_sessionloop: sent ssh-connection service req"))
|
||||
return;
|
||||
|
||||
case SERVICE_CONN_ACCEPT_RCVD:
|
||||
cli_send_chansess_request();
|
||||
TRACE(("leave cli_sessionloop: cli_send_chansess_request"));
|
||||
TRACE(("leave cli_sessionloop: cli_send_chansess_request"))
|
||||
cli_ses.state = SESSION_RUNNING;
|
||||
return;
|
||||
*/
|
||||
@@ -210,7 +219,7 @@ static void cli_sessionloop() {
|
||||
setup_remotetcp();
|
||||
#endif
|
||||
cli_send_chansess_request();
|
||||
TRACE(("leave cli_sessionloop: cli_send_chansess_request"));
|
||||
TRACE(("leave cli_sessionloop: cli_send_chansess_request"))
|
||||
cli_ses.state = SESSION_RUNNING;
|
||||
return;
|
||||
|
||||
@@ -231,7 +240,7 @@ static void cli_sessionloop() {
|
||||
break;
|
||||
}
|
||||
|
||||
TRACE(("leave cli_sessionloop: fell out"));
|
||||
TRACE(("leave cli_sessionloop: fell out"))
|
||||
|
||||
}
|
||||
|
||||
@@ -240,6 +249,11 @@ void cli_session_cleanup() {
|
||||
if (!sessinitdone) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set stdin back to non-blocking - busybox ash dies nastily
|
||||
* if we don't revert the flags */
|
||||
fcntl(cli_ses.stdincopy, F_SETFL, cli_ses.stdinflags);
|
||||
|
||||
cli_tty_cleanup();
|
||||
|
||||
}
|
||||
@@ -250,7 +264,7 @@ static void cli_finished() {
|
||||
common_session_cleanup();
|
||||
fprintf(stderr, "Connection to %s@%s:%s closed.\n", cli_opts.username,
|
||||
cli_opts.remotehost, cli_opts.remoteport);
|
||||
exit(EXIT_SUCCESS);
|
||||
exit(cli_ses.retval);
|
||||
}
|
||||
|
||||
|
||||
|
||||
37
cli-tcpfwd.c
37
cli-tcpfwd.c
@@ -31,8 +31,7 @@
|
||||
#include "session.h"
|
||||
#include "ssh.h"
|
||||
|
||||
static int cli_localtcp(unsigned int listenport, const char* remoteaddr,
|
||||
unsigned int remoteport);
|
||||
#ifdef ENABLE_CLI_REMOTETCPFWD
|
||||
static int newtcpforwarded(struct Channel * channel);
|
||||
|
||||
const struct ChanType cli_chan_tcpremote = {
|
||||
@@ -43,6 +42,11 @@ const struct ChanType cli_chan_tcpremote = {
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_CLI_LOCALTCPFWD
|
||||
static int cli_localtcp(unsigned int listenport, const char* remoteaddr,
|
||||
unsigned int remoteport);
|
||||
static const struct ChanType cli_chan_tcplocal = {
|
||||
1, /* sepfds */
|
||||
"direct-tcpip",
|
||||
@@ -51,15 +55,17 @@ static const struct ChanType cli_chan_tcplocal = {
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_CLI_LOCALTCPFWD
|
||||
void setup_localtcp() {
|
||||
|
||||
int ret;
|
||||
|
||||
TRACE(("enter setup_localtcp"));
|
||||
TRACE(("enter setup_localtcp"))
|
||||
|
||||
if (cli_opts.localfwds == NULL) {
|
||||
TRACE(("cli_opts.localfwds == NULL"));
|
||||
TRACE(("cli_opts.localfwds == NULL"))
|
||||
}
|
||||
|
||||
while (cli_opts.localfwds != NULL) {
|
||||
@@ -75,7 +81,7 @@ void setup_localtcp() {
|
||||
|
||||
cli_opts.localfwds = cli_opts.localfwds->next;
|
||||
}
|
||||
TRACE(("leave setup_localtcp"));
|
||||
TRACE(("leave setup_localtcp"))
|
||||
|
||||
}
|
||||
|
||||
@@ -89,7 +95,7 @@ static int cli_localtcp(unsigned int listenport, const char* remoteaddr,
|
||||
remoteport));
|
||||
|
||||
tcpinfo = (struct TCPListener*)m_malloc(sizeof(struct TCPListener*));
|
||||
tcpinfo->sendaddr = remoteaddr;
|
||||
tcpinfo->sendaddr = m_strdup(remoteaddr);
|
||||
tcpinfo->sendport = remoteport;
|
||||
tcpinfo->listenport = listenport;
|
||||
tcpinfo->chantype = &cli_chan_tcplocal;
|
||||
@@ -99,13 +105,15 @@ static int cli_localtcp(unsigned int listenport, const char* remoteaddr,
|
||||
if (ret == DROPBEAR_FAILURE) {
|
||||
m_free(tcpinfo);
|
||||
}
|
||||
TRACE(("leave cli_localtcp: %d", ret));
|
||||
TRACE(("leave cli_localtcp: %d", ret))
|
||||
return ret;
|
||||
}
|
||||
#endif /* ENABLE_CLI_LOCALTCPFWD */
|
||||
|
||||
#ifdef ENABLE_CLI_REMOTETCPFWD
|
||||
static void send_msg_global_request_remotetcp(int port) {
|
||||
|
||||
TRACE(("enter send_msg_global_request_remotetcp"));
|
||||
TRACE(("enter send_msg_global_request_remotetcp"))
|
||||
|
||||
CHECKCLEARTOWRITE();
|
||||
buf_putbyte(ses.writepayload, SSH_MSG_GLOBAL_REQUEST);
|
||||
@@ -116,17 +124,17 @@ static void send_msg_global_request_remotetcp(int port) {
|
||||
|
||||
encrypt_packet();
|
||||
|
||||
TRACE(("leave send_msg_global_request_remotetcp"));
|
||||
TRACE(("leave send_msg_global_request_remotetcp"))
|
||||
}
|
||||
|
||||
void setup_remotetcp() {
|
||||
|
||||
struct TCPFwdList * iter = NULL;
|
||||
|
||||
TRACE(("enter setup_remotetcp"));
|
||||
TRACE(("enter setup_remotetcp"))
|
||||
|
||||
if (cli_opts.remotefwds == NULL) {
|
||||
TRACE(("cli_opts.remotefwds == NULL"));
|
||||
TRACE(("cli_opts.remotefwds == NULL"))
|
||||
}
|
||||
|
||||
iter = cli_opts.remotefwds;
|
||||
@@ -135,7 +143,7 @@ void setup_remotetcp() {
|
||||
send_msg_global_request_remotetcp(iter->listenport);
|
||||
iter = iter->next;
|
||||
}
|
||||
TRACE(("leave setup_remotetcp"));
|
||||
TRACE(("leave setup_remotetcp"))
|
||||
}
|
||||
|
||||
static int newtcpforwarded(struct Channel * channel) {
|
||||
@@ -171,7 +179,7 @@ static int newtcpforwarded(struct Channel * channel) {
|
||||
snprintf(portstring, sizeof(portstring), "%d", iter->connectport);
|
||||
sock = connect_remote(iter->connectaddr, portstring, 1, NULL);
|
||||
if (sock < 0) {
|
||||
TRACE(("leave newtcpdirect: sock failed"));
|
||||
TRACE(("leave newtcpdirect: sock failed"))
|
||||
err = SSH_OPEN_CONNECT_FAILED;
|
||||
goto out;
|
||||
}
|
||||
@@ -188,6 +196,7 @@ static int newtcpforwarded(struct Channel * channel) {
|
||||
err = SSH_OPEN_IN_PROGRESS;
|
||||
|
||||
out:
|
||||
TRACE(("leave newtcpdirect: err %d", err));
|
||||
TRACE(("leave newtcpdirect: err %d", err))
|
||||
return err;
|
||||
}
|
||||
#endif /* ENABLE_CLI_REMOTETCPFWD */
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
|
||||
#ifdef DROPBEAR_AES128_CBC
|
||||
const struct dropbear_cipher dropbear_aes128 =
|
||||
{&rijndael_desc, 16, 16};
|
||||
{&aes_desc, 16, 16};
|
||||
#endif
|
||||
#ifdef DROPBEAR_BLOWFISH_CBC
|
||||
const struct dropbear_cipher dropbear_blowfish =
|
||||
@@ -127,7 +127,7 @@ void crypto_init() {
|
||||
|
||||
const struct _cipher_descriptor *regciphers[] = {
|
||||
#ifdef DROPBEAR_AES128_CBC
|
||||
&rijndael_desc,
|
||||
&aes_desc,
|
||||
#endif
|
||||
#ifdef DROPBEAR_BLOWFISH_CBC
|
||||
&blowfish_desc,
|
||||
@@ -202,6 +202,6 @@ void buf_put_algolist(buffer * buf, algo_type localalgos[]) {
|
||||
}
|
||||
str[pos]=0;
|
||||
/* Debug this */
|
||||
TRACE(("buf_put_algolist: %s", str));
|
||||
TRACE(("buf_put_algolist: %s", str))
|
||||
buf_putstring(buf, str, pos);
|
||||
}
|
||||
|
||||
355
common-channel.c
355
common-channel.c
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Dropbear - a SSH2 server
|
||||
* Dropbear SSH
|
||||
*
|
||||
* Copyright (c) 2002,2003 Matt Johnston
|
||||
* Copyright (c) 2002-2004 Matt Johnston
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "packet.h"
|
||||
#include "ssh.h"
|
||||
#include "buffer.h"
|
||||
#include "circbuffer.h"
|
||||
#include "dbutil.h"
|
||||
#include "channel.h"
|
||||
#include "ssh.h"
|
||||
@@ -39,7 +40,7 @@ static void send_msg_channel_open_failure(unsigned int remotechan, int reason,
|
||||
static void send_msg_channel_open_confirmation(struct Channel* channel,
|
||||
unsigned int recvwindow,
|
||||
unsigned int recvmaxpacket);
|
||||
static void writechannel(struct Channel *channel);
|
||||
static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf);
|
||||
static void send_msg_channel_window_adjust(struct Channel *channel,
|
||||
unsigned int incr);
|
||||
static void send_msg_channel_data(struct Channel *channel, int isextended,
|
||||
@@ -80,15 +81,15 @@ void chancleanup() {
|
||||
|
||||
unsigned int i;
|
||||
|
||||
TRACE(("enter chancleanup"));
|
||||
TRACE(("enter chancleanup"))
|
||||
for (i = 0; i < ses.chansize; i++) {
|
||||
if (ses.channels[i] != NULL) {
|
||||
TRACE(("channel %d closing", i));
|
||||
TRACE(("channel %d closing", i))
|
||||
removechannel(ses.channels[i]);
|
||||
}
|
||||
}
|
||||
m_free(ses.channels);
|
||||
TRACE(("leave chancleanup"));
|
||||
TRACE(("leave chancleanup"))
|
||||
}
|
||||
|
||||
/* Create a new channel entry, send a reply confirm or failure */
|
||||
@@ -102,7 +103,7 @@ struct Channel* newchannel(unsigned int remotechan,
|
||||
struct Channel * newchan;
|
||||
unsigned int i, j;
|
||||
|
||||
TRACE(("enter newchannel"));
|
||||
TRACE(("enter newchannel"))
|
||||
|
||||
/* first see if we can use existing channels */
|
||||
for (i = 0; i < ses.chansize; i++) {
|
||||
@@ -114,7 +115,7 @@ struct Channel* newchannel(unsigned int remotechan,
|
||||
/* otherwise extend the list */
|
||||
if (i == ses.chansize) {
|
||||
if (ses.chansize >= MAX_CHANNELS) {
|
||||
TRACE(("leave newchannel: max chans reached"));
|
||||
TRACE(("leave newchannel: max chans reached"))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -147,20 +148,22 @@ struct Channel* newchannel(unsigned int remotechan,
|
||||
newchan->errfd = FD_CLOSED; /* this isn't always set to start with */
|
||||
newchan->initconn = 0;
|
||||
|
||||
newchan->writebuf = buf_new(RECV_MAXWINDOW);
|
||||
newchan->writebuf = cbuf_new(RECV_MAXWINDOW);
|
||||
newchan->extrabuf = NULL; /* The user code can set it up */
|
||||
newchan->recvwindow = RECV_MAXWINDOW;
|
||||
newchan->recvdonelen = 0;
|
||||
newchan->recvmaxpacket = RECV_MAXPACKET;
|
||||
|
||||
ses.channels[i] = newchan;
|
||||
ses.chancount++;
|
||||
|
||||
TRACE(("leave newchannel"));
|
||||
TRACE(("leave newchannel"))
|
||||
|
||||
return newchan;
|
||||
}
|
||||
|
||||
/* Get the channel structure corresponding to a channel number */
|
||||
static struct Channel* getchannel(unsigned int chan) {
|
||||
struct Channel* getchannel(unsigned int chan) {
|
||||
if (chan >= ses.chansize || ses.channels[chan] == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
@@ -189,7 +192,8 @@ void channelio(fd_set *readfd, fd_set *writefd) {
|
||||
}
|
||||
|
||||
/* read from program/pipe stderr */
|
||||
if (channel->errfd >= 0 && FD_ISSET(channel->errfd, readfd)) {
|
||||
if (channel->extrabuf == NULL &&
|
||||
channel->errfd >= 0 && FD_ISSET(channel->errfd, readfd)) {
|
||||
send_msg_channel_data(channel, 1, SSH_EXTENDED_DATA_STDERR);
|
||||
}
|
||||
|
||||
@@ -218,7 +222,13 @@ void channelio(fd_set *readfd, fd_set *writefd) {
|
||||
continue; /* Important not to use the channel after
|
||||
checkinitdone(), as it may be NULL */
|
||||
}
|
||||
writechannel(channel);
|
||||
writechannel(channel, channel->infd, channel->writebuf);
|
||||
}
|
||||
|
||||
/* stderr for client mode */
|
||||
if (channel->extrabuf != NULL
|
||||
&& channel->errfd >= 0 && FD_ISSET(channel->errfd, writefd)) {
|
||||
writechannel(channel, channel->errfd, channel->extrabuf);
|
||||
}
|
||||
|
||||
/* now handle any of the channel-closing type stuff */
|
||||
@@ -236,6 +246,14 @@ void channelio(fd_set *readfd, fd_set *writefd) {
|
||||
/* do all the EOF/close type stuff checking for a channel */
|
||||
static void checkclose(struct Channel *channel) {
|
||||
|
||||
TRACE(("checkclose: infd %d, outfd %d, errfd %d, sentclosed %d, recvclosed %d",
|
||||
channel->infd, channel->outfd,
|
||||
channel->errfd, channel->sentclosed, channel->recvclosed))
|
||||
TRACE(("writebuf %d extrabuf %s extrabuf %d",
|
||||
cbuf_getused(channel->writebuf),
|
||||
channel->writebuf,
|
||||
channel->writebuf ? 0 : cbuf_getused(channel->extrabuf)))
|
||||
|
||||
if (!channel->sentclosed) {
|
||||
|
||||
/* check for exited - currently only used for server sessions,
|
||||
@@ -248,13 +266,13 @@ static void checkclose(struct Channel *channel) {
|
||||
|
||||
if (!channel->senteof
|
||||
&& channel->outfd == FD_CLOSED
|
||||
&& channel->errfd == FD_CLOSED) {
|
||||
&& (channel->extrabuf != NULL || channel->errfd == FD_CLOSED)) {
|
||||
send_msg_channel_eof(channel);
|
||||
}
|
||||
|
||||
if (channel->infd == FD_CLOSED
|
||||
&& channel->outfd == FD_CLOSED
|
||||
&& channel->errfd == FD_CLOSED) {
|
||||
&& channel->outfd == FD_CLOSED
|
||||
&& (channel->extrabuf != NULL || channel->errfd == FD_CLOSED)) {
|
||||
send_msg_channel_close(channel);
|
||||
}
|
||||
}
|
||||
@@ -271,7 +289,7 @@ static void checkclose(struct Channel *channel) {
|
||||
*/
|
||||
if (channel->recvclosed) {
|
||||
if (! channel->sentclosed) {
|
||||
TRACE(("Sending MSG_CHANNEL_CLOSE in response to same."));
|
||||
TRACE(("Sending MSG_CHANNEL_CLOSE in response to same."))
|
||||
send_msg_channel_close(channel);
|
||||
}
|
||||
removechannel(channel);
|
||||
@@ -288,7 +306,7 @@ static void checkinitdone(struct Channel *channel) {
|
||||
int val;
|
||||
socklen_t vallen = sizeof(val);
|
||||
|
||||
TRACE(("enter checkinitdone"));
|
||||
TRACE(("enter checkinitdone"))
|
||||
|
||||
if (getsockopt(channel->infd, SOL_SOCKET, SO_ERROR, &val, &vallen)
|
||||
|| val != 0) {
|
||||
@@ -296,13 +314,13 @@ static void checkinitdone(struct Channel *channel) {
|
||||
SSH_OPEN_CONNECT_FAILED, "", "");
|
||||
close(channel->infd);
|
||||
deletechannel(channel);
|
||||
TRACE(("leave checkinitdone: fail"));
|
||||
TRACE(("leave checkinitdone: fail"))
|
||||
} else {
|
||||
send_msg_channel_open_confirmation(channel, channel->recvwindow,
|
||||
channel->recvmaxpacket);
|
||||
channel->outfd = channel->infd;
|
||||
channel->initconn = 0;
|
||||
TRACE(("leave checkinitdone: success"));
|
||||
TRACE(("leave checkinitdone: success"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -311,7 +329,7 @@ static void checkinitdone(struct Channel *channel) {
|
||||
/* Send the close message and set the channel as closed */
|
||||
static void send_msg_channel_close(struct Channel *channel) {
|
||||
|
||||
TRACE(("enter send_msg_channel_close"));
|
||||
TRACE(("enter send_msg_channel_close"))
|
||||
/* XXX server */
|
||||
if (channel->type->closehandler) {
|
||||
channel->type->closehandler(channel);
|
||||
@@ -326,13 +344,13 @@ static void send_msg_channel_close(struct Channel *channel) {
|
||||
|
||||
channel->senteof = 1;
|
||||
channel->sentclosed = 1;
|
||||
TRACE(("leave send_msg_channel_close"));
|
||||
TRACE(("leave send_msg_channel_close"))
|
||||
}
|
||||
|
||||
/* call this when trans/eof channels are closed */
|
||||
static void send_msg_channel_eof(struct Channel *channel) {
|
||||
|
||||
TRACE(("enter send_msg_channel_eof"));
|
||||
TRACE(("enter send_msg_channel_eof"))
|
||||
CHECKCLEARTOWRITE();
|
||||
|
||||
buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_EOF);
|
||||
@@ -342,53 +360,57 @@ static void send_msg_channel_eof(struct Channel *channel) {
|
||||
|
||||
channel->senteof = 1;
|
||||
|
||||
TRACE(("leave send_msg_channel_eof"));
|
||||
TRACE(("leave send_msg_channel_eof"))
|
||||
}
|
||||
|
||||
/* Called to write data out to the server side of a channel (eg a shell or a
|
||||
* program.
|
||||
/* Called to write data out to the local side of the channel.
|
||||
* Only called when we know we can write to a channel, writes as much as
|
||||
* possible */
|
||||
static void writechannel(struct Channel* channel) {
|
||||
static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf) {
|
||||
|
||||
int len, maxlen;
|
||||
buffer *buf;
|
||||
|
||||
TRACE(("enter writechannel"));
|
||||
TRACE(("enter writechannel"))
|
||||
|
||||
buf = channel->writebuf;
|
||||
maxlen = buf->len - buf->pos;
|
||||
maxlen = cbuf_readlen(cbuf);
|
||||
|
||||
len = write(channel->infd, buf_getptr(buf, maxlen), maxlen);
|
||||
/* Write the data out */
|
||||
len = write(fd, cbuf_readptr(cbuf, maxlen), maxlen);
|
||||
if (len <= 0) {
|
||||
if (len < 0 && errno != EINTR) {
|
||||
/* no more to write */
|
||||
/* no more to write - we close it even if the fd was stderr, since
|
||||
* that's a nasty failure too */
|
||||
closeinfd(channel);
|
||||
}
|
||||
TRACE(("leave writechannel: len <= 0"));
|
||||
TRACE(("leave writechannel: len <= 0"))
|
||||
return;
|
||||
}
|
||||
|
||||
if (len == maxlen) {
|
||||
buf_setpos(buf, 0);
|
||||
buf_setlen(buf, 0);
|
||||
|
||||
if (channel->recveof) {
|
||||
/* we're closing up */
|
||||
closeinfd(channel);
|
||||
return;
|
||||
TRACE(("leave writechannel: recveof set"));
|
||||
}
|
||||
cbuf_incrread(cbuf, len);
|
||||
channel->recvdonelen += len;
|
||||
|
||||
/* extend the window if we're at the end*/
|
||||
/* TODO - this is inefficient */
|
||||
send_msg_channel_window_adjust(channel, buf->size
|
||||
- channel->recvwindow);
|
||||
channel->recvwindow = buf->size;
|
||||
} else {
|
||||
buf_incrpos(buf, len);
|
||||
if (fd == channel->infd && len == maxlen && channel->recveof) {
|
||||
/* Check if we're closing up */
|
||||
closeinfd(channel);
|
||||
TRACE(("leave writechannel: recveof set"))
|
||||
return;
|
||||
}
|
||||
TRACE(("leave writechannel"));
|
||||
|
||||
/* Window adjust handling */
|
||||
if (channel->recvdonelen >= RECV_WINDOWEXTEND) {
|
||||
/* Set it back to max window */
|
||||
send_msg_channel_window_adjust(channel, channel->recvdonelen);
|
||||
channel->recvwindow += channel->recvdonelen;
|
||||
channel->recvdonelen = 0;
|
||||
}
|
||||
|
||||
assert(channel->recvwindow <= RECV_MAXWINDOW);
|
||||
assert(channel->recvwindow <= cbuf_getavail(channel->writebuf));
|
||||
assert(channel->extrabuf == NULL ||
|
||||
channel->recvwindow <= cbuf_getavail(channel->extrabuf));
|
||||
|
||||
|
||||
TRACE(("leave writechannel"))
|
||||
}
|
||||
|
||||
/* Set the file descriptors for the main select in session.c
|
||||
@@ -405,30 +427,38 @@ void setchannelfds(fd_set *readfd, fd_set *writefd) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* stdout and stderr */
|
||||
/* Stuff to put over the wire */
|
||||
if (channel->transwindow > 0) {
|
||||
|
||||
/* stdout */
|
||||
if (channel->outfd >= 0) {
|
||||
/* there's space to read more from the program */
|
||||
FD_SET(channel->outfd, readfd);
|
||||
}
|
||||
/* stderr */
|
||||
if (channel->errfd >= 0) {
|
||||
|
||||
if (channel->extrabuf == NULL && channel->errfd >= 0) {
|
||||
FD_SET(channel->errfd, readfd);
|
||||
}
|
||||
}
|
||||
|
||||
/* For checking FD status (ie closure etc) - we don't actually
|
||||
* read data from infd */
|
||||
TRACE(("infd = %d, outfd %d, errfd %d, bufused %d",
|
||||
channel->infd, channel->outfd,
|
||||
channel->errfd,
|
||||
cbuf_getused(channel->writebuf) ))
|
||||
if (channel->infd >= 0 && channel->infd != channel->outfd) {
|
||||
FD_SET(channel->infd, readfd);
|
||||
}
|
||||
|
||||
/* stdin */
|
||||
if (channel->infd >= 0 &&
|
||||
(channel->writebuf->pos < channel->writebuf->len ||
|
||||
channel->initconn)) {
|
||||
/* there's space to write more to the program */
|
||||
FD_SET(channel->infd, writefd);
|
||||
/* Stuff from the wire, to local program/shell/user etc */
|
||||
if ((channel->infd >= 0 && cbuf_getused(channel->writebuf) > 0 )
|
||||
|| channel->initconn) {
|
||||
|
||||
FD_SET(channel->infd, writefd);
|
||||
}
|
||||
|
||||
if (channel->extrabuf != NULL && channel->errfd >= 0
|
||||
&& cbuf_getused(channel->extrabuf) > 0 ) {
|
||||
FD_SET(channel->errfd, writefd);
|
||||
}
|
||||
|
||||
} /* foreach channel */
|
||||
@@ -447,7 +477,7 @@ void recv_msg_channel_eof() {
|
||||
unsigned int chan;
|
||||
struct Channel * channel;
|
||||
|
||||
TRACE(("enter recv_msg_channel_eof"));
|
||||
TRACE(("enter recv_msg_channel_eof"))
|
||||
|
||||
chan = buf_getint(ses.payload);
|
||||
channel = getchannel(chan);
|
||||
@@ -457,11 +487,13 @@ void recv_msg_channel_eof() {
|
||||
}
|
||||
|
||||
channel->recveof = 1;
|
||||
if (channel->writebuf->len == 0) {
|
||||
if (cbuf_getused(channel->writebuf) == 0
|
||||
&& (channel->extrabuf == NULL
|
||||
|| cbuf_getused(channel->extrabuf) == 0)) {
|
||||
closeinfd(channel);
|
||||
}
|
||||
|
||||
TRACE(("leave recv_msg_channel_eof"));
|
||||
TRACE(("leave recv_msg_channel_eof"))
|
||||
}
|
||||
|
||||
|
||||
@@ -471,10 +503,10 @@ void recv_msg_channel_close() {
|
||||
unsigned int chan;
|
||||
struct Channel * channel;
|
||||
|
||||
TRACE(("enter recv_msg_channel_close"));
|
||||
TRACE(("enter recv_msg_channel_close"))
|
||||
|
||||
chan = buf_getint(ses.payload);
|
||||
TRACE(("close channel = %d", chan));
|
||||
TRACE(("close channel = %d", chan))
|
||||
channel = getchannel(chan);
|
||||
|
||||
if (channel == NULL) {
|
||||
@@ -489,31 +521,36 @@ void recv_msg_channel_close() {
|
||||
removechannel(channel);
|
||||
}
|
||||
|
||||
TRACE(("leave recv_msg_channel_close"));
|
||||
TRACE(("leave recv_msg_channel_close"))
|
||||
}
|
||||
|
||||
/* Remove a channel entry, this is only executed after both sides have sent
|
||||
* channel close */
|
||||
static void removechannel(struct Channel * channel) {
|
||||
|
||||
TRACE(("enter removechannel"));
|
||||
TRACE(("channel index is %d", channel->index));
|
||||
TRACE(("enter removechannel"))
|
||||
TRACE(("channel index is %d", channel->index))
|
||||
|
||||
buf_free(channel->writebuf);
|
||||
cbuf_free(channel->writebuf);
|
||||
channel->writebuf = NULL;
|
||||
|
||||
if (channel->extrabuf) {
|
||||
cbuf_free(channel->extrabuf);
|
||||
channel->extrabuf = NULL;
|
||||
}
|
||||
|
||||
|
||||
/* close the FDs in case they haven't been done
|
||||
* yet (ie they were shutdown etc */
|
||||
close(channel->infd);
|
||||
close(channel->outfd);
|
||||
if (channel->errfd >= 0) {
|
||||
close(channel->errfd);
|
||||
}
|
||||
close(channel->errfd);
|
||||
|
||||
channel->typedata = NULL;
|
||||
|
||||
deletechannel(channel);
|
||||
|
||||
TRACE(("leave removechannel"));
|
||||
TRACE(("leave removechannel"))
|
||||
}
|
||||
|
||||
/* Remove a channel entry */
|
||||
@@ -533,7 +570,7 @@ void recv_msg_channel_request() {
|
||||
unsigned int chan;
|
||||
struct Channel *channel;
|
||||
|
||||
TRACE(("enter recv_msg_channel_request"));
|
||||
TRACE(("enter recv_msg_channel_request"))
|
||||
|
||||
chan = buf_getint(ses.payload);
|
||||
channel = getchannel(chan);
|
||||
@@ -549,7 +586,7 @@ void recv_msg_channel_request() {
|
||||
send_msg_channel_failure(channel);
|
||||
}
|
||||
|
||||
TRACE(("leave recv_msg_channel_request"));
|
||||
TRACE(("leave recv_msg_channel_request"))
|
||||
|
||||
}
|
||||
|
||||
@@ -566,8 +603,8 @@ static void send_msg_channel_data(struct Channel *channel, int isextended,
|
||||
unsigned int maxlen;
|
||||
int fd;
|
||||
|
||||
/* TRACE(("enter send_msg_channel_data"));
|
||||
TRACE(("extended = %d type = %d", isextended, exttype));*/
|
||||
/* TRACE(("enter send_msg_channel_data"))
|
||||
TRACE(("extended = %d type = %d", isextended, exttype))*/
|
||||
|
||||
CHECKCLEARTOWRITE();
|
||||
|
||||
@@ -586,21 +623,24 @@ static void send_msg_channel_data(struct Channel *channel, int isextended,
|
||||
maxlen = MIN(maxlen,
|
||||
ses.writepayload->size - 1 - 4 - 4 - (isextended ? 4 : 0));
|
||||
if (maxlen == 0) {
|
||||
TRACE(("leave send_msg_channel_data: no window"));
|
||||
TRACE(("leave send_msg_channel_data: no window"))
|
||||
return; /* the data will get written later */
|
||||
}
|
||||
|
||||
/* read the data */
|
||||
TRACE(("maxlen %d", maxlen))
|
||||
buf = buf_new(maxlen);
|
||||
TRACE(("buf pos %d data %x", buf->pos, buf->data))
|
||||
len = read(fd, buf_getwriteptr(buf, maxlen), maxlen);
|
||||
if (len <= 0) {
|
||||
/* on error/eof, send eof */
|
||||
if (len == 0 || errno != EINTR) {
|
||||
closeoutfd(channel, fd);
|
||||
TRACE(("leave send_msg_channel_data: read err %d", channel->index));
|
||||
}
|
||||
buf_free(buf);
|
||||
buf = NULL;
|
||||
TRACE(("leave send_msg_channel_data: read err or EOF for fd %d",
|
||||
channel->index));
|
||||
return;
|
||||
}
|
||||
buf_incrlen(buf, len);
|
||||
@@ -620,64 +660,77 @@ static void send_msg_channel_data(struct Channel *channel, int isextended,
|
||||
channel->transwindow -= len;
|
||||
|
||||
encrypt_packet();
|
||||
TRACE(("leave send_msg_channel_data"));
|
||||
TRACE(("leave send_msg_channel_data"))
|
||||
}
|
||||
|
||||
|
||||
/* when we receive channel data, put it in a buffer for writing to the program/
|
||||
* shell etc */
|
||||
/* We receive channel data */
|
||||
void recv_msg_channel_data() {
|
||||
|
||||
unsigned int chan;
|
||||
struct Channel * channel;
|
||||
unsigned int datalen;
|
||||
unsigned int pos;
|
||||
unsigned int maxdata;
|
||||
struct Channel *channel;
|
||||
|
||||
TRACE(("enter recv_msg_channel_data"));
|
||||
|
||||
chan = buf_getint(ses.payload);
|
||||
channel = getchannel(chan);
|
||||
|
||||
if (channel == NULL) {
|
||||
dropbear_exit("Unknown channel");
|
||||
}
|
||||
|
||||
common_recv_msg_channel_data(channel, channel->infd, channel->writebuf);
|
||||
}
|
||||
|
||||
/* Shared for data and stderr data - when we receive data, put it in a buffer
|
||||
* for writing to the local file descriptor */
|
||||
void common_recv_msg_channel_data(struct Channel *channel, int fd,
|
||||
circbuffer * cbuf) {
|
||||
|
||||
unsigned int datalen;
|
||||
unsigned int maxdata;
|
||||
unsigned int buflen;
|
||||
unsigned int len;
|
||||
|
||||
TRACE(("enter recv_msg_channel_data"))
|
||||
|
||||
if (channel->recveof) {
|
||||
dropbear_exit("received data after eof");
|
||||
}
|
||||
|
||||
if (channel->infd < 0) {
|
||||
if (fd < 0) {
|
||||
dropbear_exit("received data with bad infd");
|
||||
}
|
||||
|
||||
datalen = buf_getint(ses.payload);
|
||||
|
||||
/* if the client is going to send us more data than we've allocated, then
|
||||
* it has ignored the windowsize, so we "MAY ignore all extra data" */
|
||||
maxdata = channel->writebuf->size - channel->writebuf->pos;
|
||||
|
||||
maxdata = cbuf_getavail(cbuf);
|
||||
|
||||
/* Whilst the spec says we "MAY ignore data past the end" this could
|
||||
* lead to corrupted file transfers etc (chunks missed etc). It's better to
|
||||
* just die horribly */
|
||||
if (datalen > maxdata) {
|
||||
TRACE(("Warning: recv_msg_channel_data: extra data past window"));
|
||||
datalen = maxdata;
|
||||
dropbear_exit("Oversized packet");
|
||||
}
|
||||
|
||||
/* write to the buffer - we always append to the end of the buffer */
|
||||
pos = channel->writebuf->pos;
|
||||
buf_setpos(channel->writebuf, channel->writebuf->len);
|
||||
memcpy(buf_getwriteptr(channel->writebuf, datalen),
|
||||
buf_getptr(ses.payload, datalen), datalen);
|
||||
buf_incrwritepos(channel->writebuf, datalen);
|
||||
buf_setpos(channel->writebuf, pos); /* revert pos */
|
||||
/* We may have to run throught twice, if the buffer wraps around. Can't
|
||||
* just "leave it for next time" like with writechannel, since this
|
||||
* is payload data */
|
||||
len = datalen;
|
||||
while (len > 0) {
|
||||
buflen = cbuf_writelen(cbuf);
|
||||
buflen = MIN(buflen, len);
|
||||
|
||||
memcpy(cbuf_writeptr(cbuf, buflen),
|
||||
buf_getptr(ses.payload, buflen), buflen);
|
||||
cbuf_incrwrite(cbuf, buflen);
|
||||
buf_incrpos(ses.payload, buflen);
|
||||
len -= buflen;
|
||||
}
|
||||
|
||||
assert(channel->recvwindow >= datalen);
|
||||
channel->recvwindow -= datalen;
|
||||
assert(channel->recvwindow <= RECV_MAXWINDOW);
|
||||
|
||||
/* matt - this might be for later */
|
||||
/* if (channel->recvwindow < RECV_MINWINDOW) {
|
||||
send_msg_channel_window_adjust(channel,
|
||||
RECV_MAXWINDOW - channel->recvwindow);
|
||||
channel->recvwindow = RECV_MAXWINDOW;
|
||||
}*/
|
||||
|
||||
TRACE(("leave recv_msg_channel_data"));
|
||||
TRACE(("leave recv_msg_channel_data"))
|
||||
}
|
||||
|
||||
/* Increment the outgoing data window for a channel - the remote end limits
|
||||
@@ -697,7 +750,7 @@ void recv_msg_channel_window_adjust() {
|
||||
}
|
||||
|
||||
incr = buf_getint(ses.payload);
|
||||
TRACE(("received window increment %d", incr));
|
||||
TRACE(("received window increment %d", incr))
|
||||
incr = MIN(incr, MAX_TRANS_WIN_INCR);
|
||||
|
||||
channel->transwindow += incr;
|
||||
@@ -710,7 +763,7 @@ void recv_msg_channel_window_adjust() {
|
||||
static void send_msg_channel_window_adjust(struct Channel* channel,
|
||||
unsigned int incr) {
|
||||
|
||||
TRACE(("sending window adjust %d", incr));
|
||||
TRACE(("sending window adjust %d", incr))
|
||||
CHECKCLEARTOWRITE();
|
||||
|
||||
buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_WINDOW_ADJUST);
|
||||
@@ -734,7 +787,7 @@ void recv_msg_channel_open() {
|
||||
int ret;
|
||||
|
||||
|
||||
TRACE(("enter recv_msg_channel_open"));
|
||||
TRACE(("enter recv_msg_channel_open"))
|
||||
|
||||
/* get the packet contents */
|
||||
type = buf_getstring(ses.payload, &typelen);
|
||||
@@ -762,17 +815,17 @@ void recv_msg_channel_open() {
|
||||
}
|
||||
|
||||
if (chantype == NULL) {
|
||||
TRACE(("No matching type for '%s'", type));
|
||||
TRACE(("No matching type for '%s'", type))
|
||||
goto failure;
|
||||
}
|
||||
|
||||
TRACE(("matched type '%s'", type));
|
||||
TRACE(("matched type '%s'", type))
|
||||
|
||||
/* create the channel */
|
||||
channel = newchannel(remotechan, chantype, transwindow, transmaxpacket);
|
||||
|
||||
if (channel == NULL) {
|
||||
TRACE(("newchannel returned NULL"));
|
||||
TRACE(("newchannel returned NULL"))
|
||||
goto failure;
|
||||
}
|
||||
|
||||
@@ -785,7 +838,7 @@ void recv_msg_channel_open() {
|
||||
}
|
||||
errtype = ret;
|
||||
deletechannel(channel);
|
||||
TRACE(("inithandler returned failure %d", ret));
|
||||
TRACE(("inithandler returned failure %d", ret))
|
||||
goto failure;
|
||||
}
|
||||
}
|
||||
@@ -796,39 +849,39 @@ void recv_msg_channel_open() {
|
||||
goto cleanup;
|
||||
|
||||
failure:
|
||||
TRACE(("recv_msg_channel_open failure"));
|
||||
TRACE(("recv_msg_channel_open failure"))
|
||||
send_msg_channel_open_failure(remotechan, errtype, "", "");
|
||||
|
||||
cleanup:
|
||||
m_free(type);
|
||||
|
||||
TRACE(("leave recv_msg_channel_open"));
|
||||
TRACE(("leave recv_msg_channel_open"))
|
||||
}
|
||||
|
||||
/* Send a failure message */
|
||||
void send_msg_channel_failure(struct Channel *channel) {
|
||||
|
||||
TRACE(("enter send_msg_channel_failure"));
|
||||
TRACE(("enter send_msg_channel_failure"))
|
||||
CHECKCLEARTOWRITE();
|
||||
|
||||
buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_FAILURE);
|
||||
buf_putint(ses.writepayload, channel->remotechan);
|
||||
|
||||
encrypt_packet();
|
||||
TRACE(("leave send_msg_channel_failure"));
|
||||
TRACE(("leave send_msg_channel_failure"))
|
||||
}
|
||||
|
||||
/* Send a success message */
|
||||
void send_msg_channel_success(struct Channel *channel) {
|
||||
|
||||
TRACE(("enter send_msg_channel_success"));
|
||||
TRACE(("enter send_msg_channel_success"))
|
||||
CHECKCLEARTOWRITE();
|
||||
|
||||
buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_SUCCESS);
|
||||
buf_putint(ses.writepayload, channel->remotechan);
|
||||
|
||||
encrypt_packet();
|
||||
TRACE(("leave send_msg_channel_success"));
|
||||
TRACE(("leave send_msg_channel_success"))
|
||||
}
|
||||
|
||||
/* Send a channel open failure message, with a corresponding reason
|
||||
@@ -836,7 +889,7 @@ void send_msg_channel_success(struct Channel *channel) {
|
||||
static void send_msg_channel_open_failure(unsigned int remotechan,
|
||||
int reason, const unsigned char *text, const unsigned char *lang) {
|
||||
|
||||
TRACE(("enter send_msg_channel_open_failure"));
|
||||
TRACE(("enter send_msg_channel_open_failure"))
|
||||
CHECKCLEARTOWRITE();
|
||||
|
||||
buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_OPEN_FAILURE);
|
||||
@@ -846,7 +899,7 @@ static void send_msg_channel_open_failure(unsigned int remotechan,
|
||||
buf_putstring(ses.writepayload, lang, strlen((char*)lang));
|
||||
|
||||
encrypt_packet();
|
||||
TRACE(("leave send_msg_channel_open_failure"));
|
||||
TRACE(("leave send_msg_channel_open_failure"))
|
||||
}
|
||||
|
||||
/* Confirm a channel open, and let the remote end know what number we've
|
||||
@@ -855,7 +908,7 @@ static void send_msg_channel_open_confirmation(struct Channel* channel,
|
||||
unsigned int recvwindow,
|
||||
unsigned int recvmaxpacket) {
|
||||
|
||||
TRACE(("enter send_msg_channel_open_confirmation"));
|
||||
TRACE(("enter send_msg_channel_open_confirmation"))
|
||||
CHECKCLEARTOWRITE();
|
||||
|
||||
buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
|
||||
@@ -865,10 +918,10 @@ static void send_msg_channel_open_confirmation(struct Channel* channel,
|
||||
buf_putint(ses.writepayload, recvmaxpacket);
|
||||
|
||||
encrypt_packet();
|
||||
TRACE(("leave send_msg_channel_open_confirmation"));
|
||||
TRACE(("leave send_msg_channel_open_confirmation"))
|
||||
}
|
||||
|
||||
#ifdef USING_LISTENERS
|
||||
#if defined(USING_LISTENERS) || defined(DROPBEAR_CLIENT)
|
||||
/* Create a new channel, and start the open request. This is intended
|
||||
* for X11, agent, tcp forwarding, and should be filled with channel-specific
|
||||
* options, with the calling function calling encrypt_packet() after
|
||||
@@ -878,18 +931,15 @@ int send_msg_channel_open_init(int fd, const struct ChanType *type) {
|
||||
|
||||
struct Channel* chan;
|
||||
|
||||
TRACE(("enter send_msg_channel_open_init()"));
|
||||
TRACE(("enter send_msg_channel_open_init()"))
|
||||
chan = newchannel(0, type, 0, 0);
|
||||
if (!chan) {
|
||||
TRACE(("leave send_msg_channel_open_init() - FAILED in newchannel()"));
|
||||
TRACE(("leave send_msg_channel_open_init() - FAILED in newchannel()"))
|
||||
return DROPBEAR_FAILURE;
|
||||
}
|
||||
|
||||
/* set fd non-blocking */
|
||||
if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
|
||||
TRACE(("leave send_msg_channel_open_init() - FAILED in fcntl()"));
|
||||
return DROPBEAR_FAILURE;
|
||||
}
|
||||
setnonblocking(fd);
|
||||
|
||||
chan->infd = chan->outfd = fd;
|
||||
ses.maxfd = MAX(ses.maxfd, fd);
|
||||
@@ -903,7 +953,7 @@ int send_msg_channel_open_init(int fd, const struct ChanType *type) {
|
||||
buf_putint(ses.writepayload, RECV_MAXWINDOW);
|
||||
buf_putint(ses.writepayload, RECV_MAXPACKET);
|
||||
|
||||
TRACE(("leave send_msg_channel_open_init()"));
|
||||
TRACE(("leave send_msg_channel_open_init()"))
|
||||
return DROPBEAR_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -915,7 +965,7 @@ void recv_msg_channel_open_confirmation() {
|
||||
struct Channel * channel;
|
||||
int ret;
|
||||
|
||||
TRACE(("enter recv_msg_channel_open_confirmation"));
|
||||
TRACE(("enter recv_msg_channel_open_confirmation"))
|
||||
chan = buf_getint(ses.payload);
|
||||
|
||||
channel = getchannel(chan);
|
||||
@@ -927,19 +977,19 @@ void recv_msg_channel_open_confirmation() {
|
||||
channel->transwindow = buf_getint(ses.payload);
|
||||
channel->transmaxpacket = buf_getint(ses.payload);
|
||||
|
||||
TRACE(("new chan remote %d localho %d", channel->remotechan, chan));
|
||||
TRACE(("new chan remote %d localho %d", channel->remotechan, chan))
|
||||
|
||||
/* Run the inithandler callback */
|
||||
if (channel->type->inithandler) {
|
||||
ret = channel->type->inithandler(channel);
|
||||
if (ret > 0) {
|
||||
removechannel(channel);
|
||||
TRACE(("inithandler returned failure %d", ret));
|
||||
TRACE(("inithandler returned failure %d", ret))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TRACE(("leave recv_msg_channel_open_confirmation"));
|
||||
TRACE(("leave recv_msg_channel_open_confirmation"))
|
||||
}
|
||||
|
||||
/* Notification that our channel open request failed */
|
||||
@@ -956,23 +1006,24 @@ void recv_msg_channel_open_failure() {
|
||||
|
||||
removechannel(channel);
|
||||
}
|
||||
#endif /* USING_LISTENERS */
|
||||
|
||||
/* close a stdout/stderr fd */
|
||||
static void closeoutfd(struct Channel * channel, int fd) {
|
||||
|
||||
/* don't close it if it is the same as infd,
|
||||
* unless infd is already set -1 */
|
||||
TRACE(("enter closeoutfd"));
|
||||
TRACE(("enter closeoutfd"))
|
||||
closechanfd(channel, fd, 0);
|
||||
TRACE(("leave closeoutfd"));
|
||||
TRACE(("leave closeoutfd"))
|
||||
}
|
||||
|
||||
/* close a stdin fd */
|
||||
static void closeinfd(struct Channel * channel) {
|
||||
|
||||
TRACE(("enter closeinfd"));
|
||||
TRACE(("enter closeinfd"))
|
||||
closechanfd(channel, channel->infd, 1);
|
||||
TRACE(("leave closeinfd"));
|
||||
TRACE(("leave closeinfd"))
|
||||
}
|
||||
|
||||
/* close a fd, how is 0 for stdout/stderr, 1 for stdin */
|
||||
@@ -993,15 +1044,17 @@ static void closechanfd(struct Channel *channel, int fd, int how) {
|
||||
closein = closeout = 1;
|
||||
}
|
||||
|
||||
if (closeout && fd == channel->errfd) {
|
||||
channel->errfd = FD_CLOSED;
|
||||
}
|
||||
if (closeout && fd == channel->outfd) {
|
||||
channel->outfd = FD_CLOSED;
|
||||
}
|
||||
if (closeout && (channel->extrabuf == NULL) && (fd == channel->errfd)) {
|
||||
channel->errfd = FD_CLOSED;
|
||||
}
|
||||
|
||||
if (closein && fd == channel->infd) {
|
||||
channel->infd = FD_CLOSED;
|
||||
}
|
||||
if (closein && (channel->extrabuf != NULL) && (fd == channel->errfd)) {
|
||||
channel->errfd = FD_CLOSED;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* USING_LISTENERS */
|
||||
|
||||
81
common-kex.c
81
common-kex.c
@@ -114,8 +114,8 @@ void send_msg_kexinit() {
|
||||
encrypt_packet();
|
||||
ses.dataallowed = 0; /* don't send other packets during kex */
|
||||
|
||||
TRACE(("DATAALLOWED=0"));
|
||||
TRACE(("-> KEXINIT"));
|
||||
TRACE(("DATAALLOWED=0"))
|
||||
TRACE(("-> KEXINIT"))
|
||||
ses.kexstate.sentkexinit = 1;
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ void send_msg_kexinit() {
|
||||
/* Bring new keys into use after a key exchange, and let the client know*/
|
||||
void send_msg_newkeys() {
|
||||
|
||||
TRACE(("enter send_msg_newkeys"));
|
||||
TRACE(("enter send_msg_newkeys"))
|
||||
|
||||
/* generate the kexinit request */
|
||||
CHECKCLEARTOWRITE();
|
||||
@@ -138,42 +138,42 @@ void send_msg_newkeys() {
|
||||
|
||||
/* set up our state */
|
||||
if (ses.kexstate.recvnewkeys) {
|
||||
TRACE(("while RECVNEWKEYS=1"));
|
||||
TRACE(("while RECVNEWKEYS=1"))
|
||||
gen_new_keys();
|
||||
kexinitialise(); /* we've finished with this kex */
|
||||
TRACE((" -> DATAALLOWED=1"));
|
||||
TRACE((" -> DATAALLOWED=1"))
|
||||
ses.dataallowed = 1; /* we can send other packets again now */
|
||||
ses.kexstate.donefirstkex = 1;
|
||||
} else {
|
||||
ses.kexstate.sentnewkeys = 1;
|
||||
TRACE(("SENTNEWKEYS=1"));
|
||||
TRACE(("SENTNEWKEYS=1"))
|
||||
}
|
||||
|
||||
TRACE(("-> MSG_NEWKEYS"));
|
||||
TRACE(("leave send_msg_newkeys"));
|
||||
TRACE(("-> MSG_NEWKEYS"))
|
||||
TRACE(("leave send_msg_newkeys"))
|
||||
}
|
||||
|
||||
/* Bring the new keys into use after a key exchange */
|
||||
void recv_msg_newkeys() {
|
||||
|
||||
TRACE(("<- MSG_NEWKEYS"));
|
||||
TRACE(("enter recv_msg_newkeys"));
|
||||
TRACE(("<- MSG_NEWKEYS"))
|
||||
TRACE(("enter recv_msg_newkeys"))
|
||||
|
||||
/* simply check if we've sent SSH_MSG_NEWKEYS, and if so,
|
||||
* switch to the new keys */
|
||||
if (ses.kexstate.sentnewkeys) {
|
||||
TRACE(("while SENTNEWKEYS=1"));
|
||||
TRACE(("while SENTNEWKEYS=1"))
|
||||
gen_new_keys();
|
||||
kexinitialise(); /* we've finished with this kex */
|
||||
TRACE((" -> DATAALLOWED=1"));
|
||||
TRACE((" -> DATAALLOWED=1"))
|
||||
ses.dataallowed = 1; /* we can send other packets again now */
|
||||
ses.kexstate.donefirstkex = 1;
|
||||
} else {
|
||||
TRACE(("RECVNEWKEYS=1"));
|
||||
TRACE(("RECVNEWKEYS=1"))
|
||||
ses.kexstate.recvnewkeys = 1;
|
||||
}
|
||||
|
||||
TRACE(("leave recv_msg_newkeys"));
|
||||
TRACE(("leave recv_msg_newkeys"))
|
||||
}
|
||||
|
||||
|
||||
@@ -189,7 +189,7 @@ static void kexinitialise() {
|
||||
|
||||
struct timeval tv;
|
||||
|
||||
TRACE(("kexinitialise()"));
|
||||
TRACE(("kexinitialise()"))
|
||||
|
||||
/* sent/recv'd MSG_KEXINIT */
|
||||
ses.kexstate.sentkexinit = 0;
|
||||
@@ -262,7 +262,7 @@ void gen_new_keys() {
|
||||
unsigned int C2S_keysize, S2C_keysize;
|
||||
char mactransletter, macrecvletter; /* Client or server specific */
|
||||
|
||||
TRACE(("enter gen_new_keys"));
|
||||
TRACE(("enter gen_new_keys"))
|
||||
/* the dh_K and hash are the start of all hashes, we make use of that */
|
||||
|
||||
sha1_init(&hs);
|
||||
@@ -329,7 +329,7 @@ void gen_new_keys() {
|
||||
ses.keys = ses.newkeys;
|
||||
ses.newkeys = NULL;
|
||||
|
||||
TRACE(("leave gen_new_keys"));
|
||||
TRACE(("leave gen_new_keys"))
|
||||
}
|
||||
|
||||
#ifndef DISABLE_ZLIB
|
||||
@@ -393,8 +393,8 @@ static void gen_new_zstreams() {
|
||||
/* Belongs in common_kex.c where it should be moved after review */
|
||||
void recv_msg_kexinit() {
|
||||
|
||||
TRACE(("<- KEXINIT"));
|
||||
TRACE(("enter recv_msg_kexinit"));
|
||||
TRACE(("<- KEXINIT"))
|
||||
TRACE(("enter recv_msg_kexinit"))
|
||||
|
||||
/* start the kex hash */
|
||||
ses.kexhashbuf = buf_new(MAX_KEXHASHBUF);
|
||||
@@ -402,7 +402,7 @@ void recv_msg_kexinit() {
|
||||
if (!ses.kexstate.sentkexinit) {
|
||||
/* we need to send a kex packet */
|
||||
send_msg_kexinit();
|
||||
TRACE(("continue recv_msg_kexinit: sent kexinit"));
|
||||
TRACE(("continue recv_msg_kexinit: sent kexinit"))
|
||||
}
|
||||
|
||||
|
||||
@@ -459,20 +459,23 @@ void recv_msg_kexinit() {
|
||||
ses.kexstate.recvkexinit = 1;
|
||||
// ses.expecting = 0; // client matt
|
||||
|
||||
TRACE(("leave recv_msg_kexinit"));
|
||||
TRACE(("leave recv_msg_kexinit"))
|
||||
}
|
||||
|
||||
/* Initialises and generate one side of the diffie-hellman key exchange values.
|
||||
* See the ietf-secsh-transport draft, section 6, for details */
|
||||
/* dh_pub and dh_priv MUST be already initialised */
|
||||
void gen_kexdh_vals(mp_int *dh_pub, mp_int *dh_priv) {
|
||||
|
||||
mp_int dh_p, dh_q, dh_g;
|
||||
DEF_MP_INT(dh_p);
|
||||
DEF_MP_INT(dh_q);
|
||||
DEF_MP_INT(dh_g);
|
||||
unsigned char randbuf[DH_P_LEN];
|
||||
int dh_q_len;
|
||||
|
||||
TRACE(("enter send_msg_kexdh_reply"));
|
||||
TRACE(("enter send_msg_kexdh_reply"))
|
||||
|
||||
m_mp_init_multi(&dh_g, &dh_p, &dh_q, dh_priv, dh_pub, NULL);
|
||||
m_mp_init_multi(&dh_g, &dh_p, &dh_q, NULL);
|
||||
|
||||
/* read the prime and generator*/
|
||||
if (mp_read_unsigned_bin(&dh_p, (unsigned char*)dh_p_val, DH_P_LEN)
|
||||
@@ -616,7 +619,7 @@ static void read_kex_algos() {
|
||||
erralgo = "kex";
|
||||
goto error;
|
||||
}
|
||||
TRACE(("kex algo %s", algo->name));
|
||||
TRACE(("kex algo %s", algo->name))
|
||||
ses.newkeys->algo_kex = algo->val;
|
||||
|
||||
/* server_host_key_algorithms */
|
||||
@@ -626,47 +629,49 @@ static void read_kex_algos() {
|
||||
erralgo = "hostkey";
|
||||
goto error;
|
||||
}
|
||||
TRACE(("hostkey algo %s", algo->name));
|
||||
TRACE(("hostkey algo %s", algo->name))
|
||||
ses.newkeys->algo_hostkey = algo->val;
|
||||
|
||||
/* encryption_algorithms_client_to_server */
|
||||
c2s_cipher_algo = ses.buf_match_algo(ses.payload, sshciphers, &goodguess);
|
||||
if (algo == NULL) {
|
||||
if (c2s_cipher_algo == NULL) {
|
||||
erralgo = "enc c->s";
|
||||
goto error;
|
||||
}
|
||||
TRACE(("c2s is %s", c2s_cipher_algo->name))
|
||||
|
||||
/* encryption_algorithms_server_to_client */
|
||||
s2c_cipher_algo = ses.buf_match_algo(ses.payload, sshciphers, &goodguess);
|
||||
if (algo == NULL) {
|
||||
if (s2c_cipher_algo == NULL) {
|
||||
erralgo = "enc s->c";
|
||||
goto error;
|
||||
}
|
||||
TRACE(("s2c is %s", s2c_cipher_algo->name))
|
||||
|
||||
/* mac_algorithms_client_to_server */
|
||||
c2s_hash_algo = ses.buf_match_algo(ses.payload, sshhashes, &goodguess);
|
||||
if (algo == NULL) {
|
||||
if (c2s_hash_algo == NULL) {
|
||||
erralgo = "mac c->s";
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* mac_algorithms_server_to_client */
|
||||
s2c_hash_algo = ses.buf_match_algo(ses.payload, sshhashes, &goodguess);
|
||||
if (algo == NULL) {
|
||||
if (s2c_hash_algo == NULL) {
|
||||
erralgo = "mac s->c";
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* compression_algorithms_client_to_server */
|
||||
c2s_comp_algo = ses.buf_match_algo(ses.payload, sshcompress, &goodguess);
|
||||
if (algo == NULL) {
|
||||
if (c2s_comp_algo == NULL) {
|
||||
erralgo = "comp c->s";
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* compression_algorithms_server_to_client */
|
||||
s2c_comp_algo = ses.buf_match_algo(ses.payload, sshcompress, &goodguess);
|
||||
if (algo == NULL) {
|
||||
if (s2c_comp_algo == NULL) {
|
||||
erralgo = "comp s->c";
|
||||
goto error;
|
||||
}
|
||||
@@ -712,12 +717,12 @@ static void read_kex_algos() {
|
||||
ses.newkeys->trans_algo_comp = s2c_comp_algo->val;
|
||||
}
|
||||
|
||||
TRACE(("enc algo recv %s", algo->name));
|
||||
TRACE(("enc algo trans %s", algo->name));
|
||||
TRACE(("mac algo recv %s", algo->name));
|
||||
TRACE(("mac algo trans %s", algo->name));
|
||||
TRACE(("comp algo recv %s", algo->name));
|
||||
TRACE(("comp algo trans %s", algo->name));
|
||||
TRACE(("enc algo recv %s", algo->name))
|
||||
TRACE(("enc algo trans %s", algo->name))
|
||||
TRACE(("mac algo recv %s", algo->name))
|
||||
TRACE(("mac algo trans %s", algo->name))
|
||||
TRACE(("comp algo recv %s", algo->name))
|
||||
TRACE(("comp algo trans %s", algo->name))
|
||||
|
||||
/* reserved for future extensions */
|
||||
buf_getint(ses.payload);
|
||||
|
||||
@@ -35,6 +35,8 @@
|
||||
#include "channel.h"
|
||||
#include "atomicio.h"
|
||||
|
||||
static void checktimeouts();
|
||||
static int ident_readln(int fd, char* buf, int count);
|
||||
|
||||
struct sshsession ses; /* GLOBAL */
|
||||
|
||||
@@ -46,13 +48,11 @@ int sessinitdone = 0; /* GLOBAL */
|
||||
int exitflag = 0; /* GLOBAL */
|
||||
|
||||
|
||||
static void checktimeouts();
|
||||
static int ident_readln(int fd, char* buf, int count);
|
||||
|
||||
/* called only at the start of a session, set up initial state */
|
||||
void common_session_init(int sock, char* remotehost) {
|
||||
|
||||
TRACE(("enter session_init"));
|
||||
TRACE(("enter session_init"))
|
||||
|
||||
ses.remotehost = remotehost;
|
||||
|
||||
@@ -72,6 +72,8 @@ void common_session_init(int sock, char* remotehost) {
|
||||
ses.payload = NULL;
|
||||
ses.recvseq = 0;
|
||||
|
||||
initqueue(&ses.writequeue);
|
||||
|
||||
ses.requirenext = SSH_MSG_KEXINIT;
|
||||
ses.dataallowed = 0; /* don't send data yet, we'll wait until after kex */
|
||||
ses.ignorenext = 0;
|
||||
@@ -108,7 +110,7 @@ void common_session_init(int sock, char* remotehost) {
|
||||
ses.allowprivport = 0;
|
||||
|
||||
|
||||
TRACE(("leave session_init"));
|
||||
TRACE(("leave session_init"))
|
||||
}
|
||||
|
||||
void session_loop(void(*loophandler)()) {
|
||||
@@ -160,7 +162,7 @@ void session_loop(void(*loophandler)()) {
|
||||
|
||||
if (val == 0) {
|
||||
/* timeout */
|
||||
TRACE(("select timeout"));
|
||||
TRACE(("select timeout"))
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -199,11 +201,11 @@ void session_loop(void(*loophandler)()) {
|
||||
/* clean up a session on exit */
|
||||
void common_session_cleanup() {
|
||||
|
||||
TRACE(("enter session_cleanup"));
|
||||
TRACE(("enter session_cleanup"))
|
||||
|
||||
/* we can't cleanup if we don't know the session state */
|
||||
if (!sessinitdone) {
|
||||
TRACE(("leave session_cleanup: !sessinitdone"));
|
||||
TRACE(("leave session_cleanup: !sessinitdone"))
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -213,7 +215,7 @@ void common_session_cleanup() {
|
||||
|
||||
chancleanup();
|
||||
|
||||
TRACE(("leave session_cleanup"));
|
||||
TRACE(("leave session_cleanup"))
|
||||
}
|
||||
|
||||
|
||||
@@ -223,6 +225,7 @@ void session_identification() {
|
||||
char linebuf[256];
|
||||
int len = 0;
|
||||
char done = 0;
|
||||
int i;
|
||||
|
||||
/* write our version string, this blocks */
|
||||
if (atomicio(write, ses.sock, LOCAL_IDENT "\r\n",
|
||||
@@ -230,21 +233,34 @@ void session_identification() {
|
||||
dropbear_exit("Error writing ident string");
|
||||
}
|
||||
|
||||
len = ident_readln(ses.sock, linebuf, 256);
|
||||
if (len >= 4 && memcmp(linebuf, "SSH-", 4) == 0) {
|
||||
/* start of line matches */
|
||||
done = 1;
|
||||
/* We allow up to 9 lines before the actual version string, to
|
||||
* account for wrappers/cruft etc. According to the spec only the client
|
||||
* needs to handle this, but no harm in letting the server handle it too */
|
||||
for (i = 0; i < 10; i++) {
|
||||
len = ident_readln(ses.sock, linebuf, sizeof(linebuf));
|
||||
|
||||
if (len < 0 && errno != EINTR) {
|
||||
/* It failed */
|
||||
break;
|
||||
}
|
||||
|
||||
if (len >= 4 && memcmp(linebuf, "SSH-", 4) == 0) {
|
||||
/* start of line matches */
|
||||
done = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!done) {
|
||||
dropbear_exit("Failed to get client version");
|
||||
TRACE(("err: %s for '%s'\n", strerror(errno), linebuf))
|
||||
dropbear_exit("Failed to get remote version");
|
||||
} else {
|
||||
/* linebuf is already null terminated */
|
||||
ses.remoteident = m_malloc(len);
|
||||
memcpy(ses.remoteident, linebuf, len);
|
||||
}
|
||||
|
||||
TRACE(("remoteident: %s", ses.remoteident));
|
||||
TRACE(("remoteident: %s", ses.remoteident))
|
||||
|
||||
}
|
||||
|
||||
@@ -258,7 +274,7 @@ static int ident_readln(int fd, char* buf, int count) {
|
||||
fd_set fds;
|
||||
struct timeval timeout;
|
||||
|
||||
TRACE(("enter ident_readln"));
|
||||
TRACE(("enter ident_readln"))
|
||||
|
||||
if (count < 1) {
|
||||
return -1;
|
||||
@@ -279,7 +295,7 @@ static int ident_readln(int fd, char* buf, int count) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
TRACE(("leave ident_readln: select error"));
|
||||
TRACE(("leave ident_readln: select error"))
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -297,12 +313,12 @@ static int ident_readln(int fd, char* buf, int count) {
|
||||
if (errno == EINTR) {
|
||||
continue; /* not a real error */
|
||||
}
|
||||
TRACE(("leave ident_readln: read error"));
|
||||
TRACE(("leave ident_readln: read error"))
|
||||
return -1;
|
||||
}
|
||||
if (num == 0) {
|
||||
/* EOF */
|
||||
TRACE(("leave ident_readln: EOF"));
|
||||
TRACE(("leave ident_readln: EOF"))
|
||||
return -1;
|
||||
}
|
||||
if (in == '\n') {
|
||||
@@ -318,7 +334,7 @@ static int ident_readln(int fd, char* buf, int count) {
|
||||
}
|
||||
|
||||
buf[pos] = '\0';
|
||||
TRACE(("leave ident_readln: return %d", pos+1));
|
||||
TRACE(("leave ident_readln: return %d", pos+1))
|
||||
return pos+1;
|
||||
}
|
||||
|
||||
@@ -347,7 +363,7 @@ static void checktimeouts() {
|
||||
if (!ses.kexstate.sentkexinit
|
||||
&& (secs - ses.kexstate.lastkextime >= KEX_REKEY_TIMEOUT
|
||||
|| ses.kexstate.datarecv+ses.kexstate.datatrans >= KEX_REKEY_DATA)){
|
||||
TRACE(("rekeying after timeout or max data reached"));
|
||||
TRACE(("rekeying after timeout or max data reached"))
|
||||
send_msg_kexinit();
|
||||
}
|
||||
}
|
||||
|
||||
2
compat.c
2
compat.c
@@ -190,7 +190,7 @@ int daemon(int nochdir, int noclose) {
|
||||
|
||||
#ifndef HAVE_BASENAME
|
||||
|
||||
char *basename(char *path) {
|
||||
char *basename(const char *path) {
|
||||
|
||||
char *foo = strrchr(path, '/');
|
||||
return ++foo;
|
||||
|
||||
43
configure.in
43
configure.in
@@ -19,7 +19,7 @@ fi
|
||||
AC_SUBST(LD)
|
||||
|
||||
if test -z "$OLDCFLAGS" && test "$GCC" = "yes"; then
|
||||
AC_MSG_RESULT(No \$CFLAGS set... using "-Os -W -Wall for GCC")
|
||||
AC_MSG_RESULT(No \$CFLAGS set... using "-Os -W -Wall" for GCC)
|
||||
CFLAGS="-Os -W -Wall"
|
||||
fi
|
||||
|
||||
@@ -117,6 +117,43 @@ AC_ARG_ENABLE(zlib,
|
||||
]
|
||||
)
|
||||
|
||||
# Check if pam is needed
|
||||
AC_ARG_WITH(pam,
|
||||
[ --with-pam=PATH Use pam in PATH],
|
||||
[
|
||||
# option is given
|
||||
if test -d "$withval/lib"; then
|
||||
LDFLAGS="-L${withval}/lib ${LDFLAGS}"
|
||||
else
|
||||
LDFLAGS="-L${withval} ${LDFLAGS}"
|
||||
fi
|
||||
if test -d "$withval/include"; then
|
||||
CPPFLAGS="-I${withval}/include ${CPPFLAGS}"
|
||||
else
|
||||
CPPFLAGS="-I${withval} ${CPPFLAGS}"
|
||||
fi
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
AC_ARG_ENABLE(pam,
|
||||
[ --enable-pam Try to include PAM support],
|
||||
[
|
||||
if test "x$enableval" = "xyes"; then
|
||||
AC_CHECK_LIB(pam, pam_authenticate, , AC_MSG_ERROR([*** PAM missing - install first or check config.log ***]))
|
||||
AC_MSG_RESULT(Enabling PAM)
|
||||
else
|
||||
AC_DEFINE(DISABLE_PAM,, Use PAM)
|
||||
AC_MSG_RESULT(Disabling PAM)
|
||||
fi
|
||||
],
|
||||
[
|
||||
# disable it by default
|
||||
AC_DEFINE(DISABLE_PAM,, Use PAM)
|
||||
AC_MSG_RESULT(Disabling PAM)
|
||||
]
|
||||
)
|
||||
|
||||
AC_ARG_ENABLE(openpty,
|
||||
[ --disable-openpty Don't use openpty, use alternative method],
|
||||
[
|
||||
@@ -169,7 +206,7 @@ AC_ARG_ENABLE(shadow,
|
||||
# Checks for header files.
|
||||
AC_HEADER_STDC
|
||||
AC_HEADER_SYS_WAIT
|
||||
AC_CHECK_HEADERS([fcntl.h limits.h netinet/in.h netinet/tcp.h stdlib.h string.h sys/socket.h sys/time.h termios.h unistd.h crypt.h pty.h ioctl.h libutil.h libgen.h inttypes.h stropts.h utmp.h utmpx.h lastlog.h paths.h util.h netdb.h])
|
||||
AC_CHECK_HEADERS([fcntl.h limits.h netinet/in.h netinet/tcp.h stdlib.h string.h sys/socket.h sys/time.h termios.h unistd.h crypt.h pty.h ioctl.h libutil.h libgen.h inttypes.h stropts.h utmp.h utmpx.h lastlog.h paths.h util.h netdb.h security/pam_appl.h pam/pam_appl.h])
|
||||
|
||||
# Checks for typedefs, structures, and compiler characteristics.
|
||||
AC_C_CONST
|
||||
@@ -573,5 +610,7 @@ fi
|
||||
AC_EXEEXT
|
||||
AC_CONFIG_HEADER(config.h)
|
||||
AC_OUTPUT(Makefile)
|
||||
AC_OUTPUT(libtomcrypt/Makefile)
|
||||
AC_OUTPUT(libtommath/Makefile)
|
||||
AC_MSG_RESULT()
|
||||
AC_MSG_RESULT(Now edit options.h to choose features.)
|
||||
|
||||
@@ -44,7 +44,8 @@ int main(int argc, char ** argv) {
|
||||
}
|
||||
#endif
|
||||
#ifdef DBMULTI_dbclient
|
||||
if (strcmp(progname, "dbclient") == 0) {
|
||||
if (strcmp(progname, "dbclient") == 0
|
||||
|| strcmp(progname, "ssh") == 0) {
|
||||
return cli_main(argc, argv);
|
||||
}
|
||||
#endif
|
||||
@@ -71,7 +72,7 @@ int main(int argc, char ** argv) {
|
||||
"'dropbear' - the Dropbear server\n"
|
||||
#endif
|
||||
#ifdef DBMULTI_dbclient
|
||||
"'dbclient' - the Dropbear client\n"
|
||||
"'dbclient' or 'ssh' - the Dropbear client\n"
|
||||
#endif
|
||||
#ifdef DBMULTI_dropbearkey
|
||||
"'dropbearkey' - the key generator\n"
|
||||
|
||||
100
dbutil.c
100
dbutil.c
@@ -66,7 +66,10 @@ void (*_dropbear_exit)(int exitcode, const char* format, va_list param)
|
||||
void (*_dropbear_log)(int priority, const char* format, va_list param)
|
||||
= generic_dropbear_log;
|
||||
|
||||
int usingsyslog = 0; /* set by runopts, but required externally to sessions */
|
||||
#ifdef DEBUG_TRACE
|
||||
int debug_trace = 0;
|
||||
#endif
|
||||
|
||||
#ifndef DISABLE_SYSLOG
|
||||
void startsyslog() {
|
||||
|
||||
@@ -107,7 +110,7 @@ static void generic_dropbear_exit(int exitcode, const char* format,
|
||||
exit(exitcode);
|
||||
}
|
||||
|
||||
static void generic_dropbear_log(int priority, const char* format,
|
||||
static void generic_dropbear_log(int UNUSED(priority), const char* format,
|
||||
va_list param) {
|
||||
|
||||
char printbuf[1024];
|
||||
@@ -134,6 +137,10 @@ void dropbear_trace(const char* format, ...) {
|
||||
|
||||
va_list param;
|
||||
|
||||
if (!debug_trace) {
|
||||
return;
|
||||
}
|
||||
|
||||
va_start(param, format);
|
||||
fprintf(stderr, "TRACE: ");
|
||||
vfprintf(stderr, format, param);
|
||||
@@ -157,17 +164,17 @@ int dropbear_listen(const char* address, const char* port,
|
||||
int val;
|
||||
int sock;
|
||||
|
||||
TRACE(("enter dropbear_listen"));
|
||||
TRACE(("enter dropbear_listen"))
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC; /* TODO: let them flag v4 only etc */
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
if (address && address[0] == '\0') {
|
||||
TRACE(("dropbear_listen: local loopback"));
|
||||
TRACE(("dropbear_listen: local loopback"))
|
||||
address = NULL;
|
||||
} else {
|
||||
TRACE(("dropbear_listen: not local loopback"));
|
||||
TRACE(("dropbear_listen: not local loopback"))
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
}
|
||||
err = getaddrinfo(address, port, &hints, &res0);
|
||||
@@ -179,7 +186,7 @@ int dropbear_listen(const char* address, const char* port,
|
||||
*errstring = (char*)m_malloc(len);
|
||||
snprintf(*errstring, len, "Error resolving: %s", gai_strerror(err));
|
||||
}
|
||||
TRACE(("leave dropbear_listen: failed resolving"));
|
||||
TRACE(("leave dropbear_listen: failed resolving"))
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -196,7 +203,7 @@ int dropbear_listen(const char* address, const char* port,
|
||||
|
||||
if (sock < 0) {
|
||||
err = errno;
|
||||
TRACE(("socket() failed"));
|
||||
TRACE(("socket() failed"))
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -214,14 +221,14 @@ int dropbear_listen(const char* address, const char* port,
|
||||
if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) {
|
||||
err = errno;
|
||||
close(sock);
|
||||
TRACE(("bind(%s) failed", port));
|
||||
TRACE(("bind(%s) failed", port))
|
||||
continue;
|
||||
}
|
||||
|
||||
if (listen(sock, 20) < 0) {
|
||||
err = errno;
|
||||
close(sock);
|
||||
TRACE(("listen() failed"));
|
||||
TRACE(("listen() failed"))
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -236,12 +243,12 @@ int dropbear_listen(const char* address, const char* port,
|
||||
len = 20 + strlen(strerror(err));
|
||||
*errstring = (char*)m_malloc(len);
|
||||
snprintf(*errstring, len, "Error listening: %s", strerror(err));
|
||||
TRACE(("leave dropbear_listen: failure, %s", strerror(err)));
|
||||
TRACE(("leave dropbear_listen: failure, %s", strerror(err)))
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
TRACE(("leave dropbear_listen: success, %d socks bound", nsock));
|
||||
TRACE(("leave dropbear_listen: success, %d socks bound", nsock))
|
||||
return nsock;
|
||||
}
|
||||
|
||||
@@ -257,7 +264,7 @@ int connect_remote(const char* remotehost, const char* remoteport,
|
||||
int sock;
|
||||
int err;
|
||||
|
||||
TRACE(("enter connect_remote"));
|
||||
TRACE(("enter connect_remote"))
|
||||
|
||||
if (errstring != NULL) {
|
||||
*errstring = NULL;
|
||||
@@ -275,7 +282,7 @@ int connect_remote(const char* remotehost, const char* remoteport,
|
||||
*errstring = (char*)m_malloc(len);
|
||||
snprintf(*errstring, len, "Error resolving: %s", gai_strerror(err));
|
||||
}
|
||||
TRACE(("Error resolving: %s", gai_strerror(err)));
|
||||
TRACE(("Error resolving: %s", gai_strerror(err)))
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -296,14 +303,14 @@ int connect_remote(const char* remotehost, const char* remoteport,
|
||||
if (errstring != NULL && *errstring == NULL) {
|
||||
*errstring = m_strdup("Failed non-blocking");
|
||||
}
|
||||
TRACE(("Failed non-blocking: %s", strerror(errno)));
|
||||
TRACE(("Failed non-blocking: %s", strerror(errno)))
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) {
|
||||
if (errno == EINPROGRESS && nonblocking) {
|
||||
TRACE(("Connect in progress"));
|
||||
TRACE(("Connect in progress"))
|
||||
break;
|
||||
} else {
|
||||
err = errno;
|
||||
@@ -324,7 +331,7 @@ int connect_remote(const char* remotehost, const char* remoteport,
|
||||
*errstring = (char*)m_malloc(len);
|
||||
snprintf(*errstring, len, "Error connecting: %s", strerror(err));
|
||||
}
|
||||
TRACE(("Error connecting: %s", strerror(err)));
|
||||
TRACE(("Error connecting: %s", strerror(err)))
|
||||
} else {
|
||||
/* Success */
|
||||
/* (err is used as a dummy var here) */
|
||||
@@ -336,7 +343,7 @@ int connect_remote(const char* remotehost, const char* remoteport,
|
||||
m_free(*errstring);
|
||||
}
|
||||
|
||||
TRACE(("leave connect_remote: sock %d\n", sock));
|
||||
TRACE(("leave connect_remote: sock %d\n", sock))
|
||||
return sock;
|
||||
}
|
||||
|
||||
@@ -350,6 +357,16 @@ unsigned char * getaddrstring(struct sockaddr_storage* addr, int withport) {
|
||||
unsigned int len;
|
||||
|
||||
len = sizeof(struct sockaddr_storage);
|
||||
/* Some platforms such as Solaris 8 require that len is the length
|
||||
* of the specific structure. */
|
||||
if (addr->ss_family == AF_INET) {
|
||||
len = sizeof(struct sockaddr_in);
|
||||
}
|
||||
#ifdef AF_INET6
|
||||
if (addr->ss_family == AF_INET6) {
|
||||
len = sizeof(struct sockaddr_in6);
|
||||
}
|
||||
#endif
|
||||
|
||||
ret = getnameinfo((struct sockaddr*)addr, len, hbuf, sizeof(hbuf),
|
||||
sbuf, sizeof(sbuf), NI_NUMERICSERV | NI_NUMERICHOST);
|
||||
@@ -380,11 +397,27 @@ char* getaddrhostname(struct sockaddr_storage * addr) {
|
||||
char sbuf[NI_MAXSERV];
|
||||
int ret;
|
||||
unsigned int len;
|
||||
#ifdef DO_HOST_LOOKUP
|
||||
const int flags = NI_NUMERICSERV;
|
||||
#else
|
||||
const int flags = NI_NUMERICHOST | NI_NUMERICSERV;
|
||||
#endif
|
||||
|
||||
len = sizeof(struct sockaddr_storage);
|
||||
/* Some platforms such as Solaris 8 require that len is the length
|
||||
* of the specific structure. */
|
||||
if (addr->ss_family == AF_INET) {
|
||||
len = sizeof(struct sockaddr_in);
|
||||
}
|
||||
#ifdef AF_INET6
|
||||
if (addr->ss_family == AF_INET6) {
|
||||
len = sizeof(struct sockaddr_in6);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
ret = getnameinfo((struct sockaddr*)addr, len, hbuf, sizeof(hbuf),
|
||||
sbuf, sizeof(sbuf), NI_NUMERICSERV);
|
||||
sbuf, sizeof(sbuf), flags);
|
||||
|
||||
if (ret != 0) {
|
||||
/* On some systems (Darwin does it) we get EINTR from getnameinfo
|
||||
@@ -476,7 +509,7 @@ int buf_getline(buffer * line, FILE * authfile) {
|
||||
|
||||
int c = EOF;
|
||||
|
||||
TRACE(("enter buf_getline"));
|
||||
TRACE(("enter buf_getline"))
|
||||
|
||||
buf_setpos(line, 0);
|
||||
buf_setlen(line, 0);
|
||||
@@ -491,25 +524,24 @@ int buf_getline(buffer * line, FILE * authfile) {
|
||||
buf_putbyte(line, (unsigned char)c);
|
||||
}
|
||||
|
||||
TRACE(("leave getauthline: line too long"));
|
||||
TRACE(("leave getauthline: line too long"))
|
||||
/* We return success, but the line length will be zeroed - ie we just
|
||||
* ignore that line */
|
||||
buf_setlen(line, 0);
|
||||
|
||||
out:
|
||||
|
||||
buf_setpos(line, 0);
|
||||
|
||||
/* if we didn't read anything before EOF or error, exit */
|
||||
if (c == EOF && line->pos == 0) {
|
||||
TRACE(("leave getauthline: failure"));
|
||||
TRACE(("leave buf_getline: failure"))
|
||||
return DROPBEAR_FAILURE;
|
||||
} else {
|
||||
TRACE(("leave getauthline: success"));
|
||||
TRACE(("leave buf_getline: success"))
|
||||
buf_setpos(line, 0);
|
||||
return DROPBEAR_SUCCESS;
|
||||
}
|
||||
|
||||
TRACE(("leave buf_getline"));
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -537,7 +569,7 @@ void * m_malloc(size_t size) {
|
||||
if (size == 0) {
|
||||
dropbear_exit("m_malloc failed");
|
||||
}
|
||||
ret = malloc(size);
|
||||
ret = calloc(1, size);
|
||||
if (ret == NULL) {
|
||||
dropbear_exit("m_malloc failed");
|
||||
}
|
||||
@@ -577,6 +609,8 @@ void * m_realloc(void* ptr, size_t size) {
|
||||
|
||||
/* Clear the data, based on the method in David Wheeler's
|
||||
* "Secure Programming for Linux and Unix HOWTO" */
|
||||
/* Beware of calling this from within dbutil.c - things might get
|
||||
* optimised away */
|
||||
void m_burn(void *data, unsigned int len) {
|
||||
volatile char *p = data;
|
||||
|
||||
@@ -587,3 +621,19 @@ void m_burn(void *data, unsigned int len) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void setnonblocking(int fd) {
|
||||
|
||||
TRACE(("setnonblocking: %d", fd))
|
||||
|
||||
if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
|
||||
if (errno == ENODEV) {
|
||||
/* Some devices (like /dev/null redirected in)
|
||||
* can't be set to non-blocking */
|
||||
TRACE(("ignoring ENODEV for setnonblocking"))
|
||||
} else {
|
||||
dropbear_exit("Couldn't set nonblocking");
|
||||
}
|
||||
}
|
||||
TRACE(("leave setnonblocking"))
|
||||
}
|
||||
|
||||
2
dbutil.h
2
dbutil.h
@@ -42,6 +42,7 @@ void dropbear_log(int priority, const char* format, ...);
|
||||
#ifdef DEBUG_TRACE
|
||||
void dropbear_trace(const char* format, ...);
|
||||
void printhex(unsigned char* buf, int len);
|
||||
extern int debug_trace;
|
||||
#endif
|
||||
char * stripcontrol(const char * text);
|
||||
unsigned char * getaddrstring(struct sockaddr_storage* addr, int withport);
|
||||
@@ -60,6 +61,7 @@ void * m_realloc(void* ptr, size_t size);
|
||||
#define m_free(X) __m_free(X); (X) = NULL;
|
||||
void __m_free(void* ptr);
|
||||
void m_burn(void* data, unsigned int len);
|
||||
void setnonblocking(int fd);
|
||||
|
||||
/* Used to force mp_ints to be initialised */
|
||||
#define DEF_MP_INT(X) mp_int X = {0, 0, 0, NULL}
|
||||
|
||||
30
debian/changelog
vendored
30
debian/changelog
vendored
@@ -1,3 +1,33 @@
|
||||
dropbear (0.45-1) unstable; urgency=high
|
||||
|
||||
* New upstream release, various fixes.
|
||||
|
||||
-- Matt Johnston <matt@ucc.asn.au> Mon, 7 March 2005 00:44:54 +0800
|
||||
|
||||
dropbear (0.44+final-1) unstable; urgency=high
|
||||
|
||||
* New upstream release, various fixes.
|
||||
|
||||
-- Matt Johnston <matt@ucc.asn.au> Mon, 3 January 2005 00:44:54 +0800
|
||||
|
||||
dropbear (0.44test4-1) unstable; urgency=medium
|
||||
|
||||
* New upstream beta, various useful fixes.
|
||||
|
||||
-- Matt Johnston <matt@ucc.asn.au> Tues, 14 September 2004 21:20:00 +0800
|
||||
|
||||
dropbear (0.44test3-1) unstable; urgency=medium
|
||||
|
||||
* New upstream beta, various useful fixes.
|
||||
|
||||
-- Matt Johnston <matt@ucc.asn.au> Fri, 27 August 2004 22:20:00 +0800
|
||||
|
||||
dropbear (0.44test2-1) unstable; urgency=low
|
||||
|
||||
* New upstream beta, various minor fixes.
|
||||
|
||||
-- Matt Johnston <matt@ucc.asn.au> Tues, 17 August 2004 19:00:00 +0800
|
||||
|
||||
dropbear (0.44test1-1) unstable; urgency=low
|
||||
|
||||
* Upstream beta 0.44test1
|
||||
|
||||
1
debian/compat
vendored
1
debian/compat
vendored
@@ -1 +0,0 @@
|
||||
4
|
||||
0
debian/conffiles
vendored
0
debian/conffiles
vendored
2
debian/dirs
vendored
2
debian/dirs
vendored
@@ -1,2 +0,0 @@
|
||||
usr/bin
|
||||
usr/sbin
|
||||
2
debian/docs
vendored
2
debian/docs
vendored
@@ -1,2 +0,0 @@
|
||||
README
|
||||
TODO
|
||||
90
debian/postinst
vendored
90
debian/postinst
vendored
@@ -1,90 +0,0 @@
|
||||
#! /bin/sh
|
||||
# postinst script for #PACKAGE#
|
||||
#
|
||||
# see: dh_installdeb(1)
|
||||
|
||||
set -e
|
||||
|
||||
# summary of how this script can be called:
|
||||
# * <postinst> `configure' <most-recently-configured-version>
|
||||
# * <old-postinst> `abort-upgrade' <new version>
|
||||
# * <conflictor's-postinst> `abort-remove' `in-favour' <package>
|
||||
# <new-version>
|
||||
# * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
|
||||
# <failed-install-package> <version> `removing'
|
||||
# <conflicting-package> <version>
|
||||
# for details, see http://www.debian.org/doc/debian-policy/ or
|
||||
# the debian-policy package
|
||||
#
|
||||
# quoting from the policy:
|
||||
# Any necessary prompting should almost always be confined to the
|
||||
# post-installation script, and should be protected with a conditional
|
||||
# so that unnecessary prompting doesn't happen if a package's
|
||||
# installation fails and the `postinst' is called with `abort-upgrade',
|
||||
# `abort-remove' or `abort-deconfigure'.
|
||||
|
||||
case "$1" in
|
||||
configure)
|
||||
if [ ! -e /etc/dropbear/dropbear_rsa_host_key ]; then
|
||||
if [ -f /etc/ssh/ssh_host_rsa_key ]; then
|
||||
echo "Converting existing OpenSSH RSA host key to Dropbear format."
|
||||
/usr/bin/dropbearconvert openssh dropbear /etc/ssh/ssh_host_rsa_key /etc/dropbear/dropbear_rsa_host_key
|
||||
else
|
||||
echo "Generating Dropbear RSA key. Please wait."
|
||||
/usr/bin/dropbearkey -t rsa -f /etc/dropbear/dropbear_rsa_host_key
|
||||
fi
|
||||
fi
|
||||
if [ ! -e /etc/dropbear/dropbear_dss_host_key ]; then
|
||||
if [ -f /etc/ssh/ssh_host_dsa_key ]; then
|
||||
echo "Converting existing OpenSSH RSA host key to Dropbear format."
|
||||
/usr/bin/dropbearconvert openssh dropbear /etc/ssh/ssh_host_dsa_key /etc/dropbear/dropbear_dss_host_key
|
||||
else
|
||||
echo "Generating Dropbear DSS key. Please wait."
|
||||
/usr/bin/dropbearkey -t dss -f /etc/dropbear/dropbear_dss_host_key
|
||||
fi
|
||||
fi
|
||||
if [ ! -s /etc/default/dropbear ]; then
|
||||
# check whether OpenSSH seems to be installed.
|
||||
if dpkg -l ssh >/dev/null 2>&1; then
|
||||
echo "OpenSSH appears to be installed. Setting /etc/default/dropbear"
|
||||
echo "so that Dropbear will not start by default. Edit this file to change"
|
||||
echo "this behaviour."
|
||||
echo "# disabled because OpenSSH is installed, change to NO_START=0 to enable Dropbear" > /etc/default/dropbear
|
||||
echo "NO_START=1" >> /etc/default/dropbear
|
||||
fi
|
||||
echo "# the TCP port that Dropbear listens on" >> /etc/default/dropbear
|
||||
echo "DROPBEAR_PORT=22" >> /etc/default/dropbear
|
||||
echo "# any additional arguments for Dropbear" >> /etc/default/dropbear
|
||||
echo "DROPBEAR_EXTRA_ARGS=" >> /etc/default/dropbear
|
||||
echo "# specify an optional banner file containing a message to be" >> /etc/default/dropbear
|
||||
echo "# sent to clients before they connect, such as \"/etc/issue.net\"" >> /etc/default/dropbear
|
||||
echo "DROPBEAR_BANNER=\"\"" >> /etc/default/dropbear
|
||||
echo "# RSA hostkey file (default: /etc/dropbear/dropbear_rsa_host_key" >> /etc/default/dropbear
|
||||
echo "#DROPBEAR_RSAKEY=\"/etc/dropbear/dropbear_rsa_host_key\"" >> /etc/default/dropbear
|
||||
echo "# DSS hostkey file (default: /etc/dropbear/dropbear_dss_host_key" >> /etc/default/dropbear
|
||||
echo "#DROPBEAR_DSSKEY=\"/etc/dropbear/dropbear_dss_host_key\"" >> /etc/default/dropbear
|
||||
fi
|
||||
if [ -e /etc/init.d/dropbear ]; then
|
||||
update-rc.d dropbear defaults >/dev/null
|
||||
/etc/init.d/dropbear restart
|
||||
fi
|
||||
;;
|
||||
|
||||
abort-upgrade|abort-remove|abort-deconfigure)
|
||||
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "postinst called with unknown argument \`$1'" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# dh_installdeb will replace this with shell code automatically
|
||||
# generated by other debhelper scripts.
|
||||
|
||||
|
||||
|
||||
exit 0
|
||||
|
||||
|
||||
45
debian/postrm
vendored
45
debian/postrm
vendored
@@ -1,45 +0,0 @@
|
||||
#! /bin/sh
|
||||
# postrm script for #PACKAGE#
|
||||
#
|
||||
# see: dh_installdeb(1)
|
||||
|
||||
set -e
|
||||
|
||||
# summary of how this script can be called:
|
||||
# * <postrm> `remove'
|
||||
# * <postrm> `purge'
|
||||
# * <old-postrm> `upgrade' <new-version>
|
||||
# * <new-postrm> `failed-upgrade' <old-version>
|
||||
# * <new-postrm> `abort-install'
|
||||
# * <new-postrm> `abort-install' <old-version>
|
||||
# * <new-postrm> `abort-upgrade' <old-version>
|
||||
# * <disappearer's-postrm> `disappear' <r>overwrit>r> <new-version>
|
||||
# for details, see http://www.debian.org/doc/debian-policy/ or
|
||||
# the debian-policy package
|
||||
|
||||
|
||||
case "$1" in
|
||||
purge|remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
|
||||
if [ "$1" = "purge" ]
|
||||
then
|
||||
if [ -e /etc/dropbear ]; then
|
||||
rm -f /etc/dropbear/dropbear_rsa_host_key
|
||||
rm -f /etc/dropbear/dropbear_dss_host_key
|
||||
rmdir --ignore-fail-on-non-empty /etc/dropbear
|
||||
fi
|
||||
update-rc.d dropbear remove >/dev/null
|
||||
fi
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "postrm called with unknown argument \`$1'" >&2
|
||||
exit 1
|
||||
|
||||
esac
|
||||
|
||||
# dh_installdeb will replace this with shell code automatically
|
||||
# generated by other debhelper scripts.
|
||||
|
||||
#DEBHELPER#
|
||||
|
||||
exit 0
|
||||
0
debian/rules
vendored
Normal file → Executable file
0
debian/rules
vendored
Normal file → Executable file
14
debug.h
14
debug.h
@@ -33,10 +33,13 @@
|
||||
* etc. Don't use this normally, it might cause problems */
|
||||
/* #define DEBUG_VALGRIND */
|
||||
|
||||
/* Define this to print trace statements - very verbose */
|
||||
/* Caution: Don't use this in an unfriendly environment (ie unfirewalled),
|
||||
* since the printing does not sanitise strings etc */
|
||||
/* #define DEBUG_TRACE */
|
||||
/* Define this to compile in trace debugging printf()s.
|
||||
* You'll need to run programs with "-v" to turn this on.
|
||||
*
|
||||
* Caution: Don't use this in an unfriendly environment (ie unfirewalled),
|
||||
* since the printing may not sanitise strings etc. This will add a reasonable
|
||||
* amount to your executable size. */
|
||||
//#define DEBUG_TRACE
|
||||
|
||||
/* All functions writing to the cleartext payload buffer call
|
||||
* CHECKCLEARTOWRITE() before writing. This is only really useful if you're
|
||||
@@ -47,6 +50,7 @@
|
||||
/* Define this, compile with -pg and set GMON_OUT_PREFIX=gmon to get gmon
|
||||
* output when Dropbear forks. This will allow it gprof to be used.
|
||||
* It's useful to run dropbear -F, so you don't fork as much */
|
||||
/* (This is Linux specific) */
|
||||
/*#define DEBUG_FORKGPROF*/
|
||||
|
||||
/* A couple of flags, not usually useful, and mightn't do anything */
|
||||
@@ -56,7 +60,7 @@
|
||||
|
||||
/* you don't need to touch this block */
|
||||
#ifdef DEBUG_TRACE
|
||||
#define TRACE(X) (dropbear_trace X)
|
||||
#define TRACE(X) dropbear_trace X;
|
||||
#else /*DEBUG_TRACE*/
|
||||
#define TRACE(X)
|
||||
#endif /*DEBUG_TRACE*/
|
||||
|
||||
81
dropbear.8
Normal file
81
dropbear.8
Normal file
@@ -0,0 +1,81 @@
|
||||
.TH dropbear 8
|
||||
.SH NAME
|
||||
dropbear \- lightweight SSH2 server
|
||||
.SH SYNOPSIS
|
||||
.B dropbear
|
||||
[\-FEmwsgjki] [\-b
|
||||
.I banner\fR] [\-d
|
||||
.I dsskey\fR] [\-r
|
||||
.I rsakey\fR] [\-p
|
||||
.IR port ]
|
||||
.SH DESCRIPTION
|
||||
.B dropbear
|
||||
is a SSH 2 server designed to be small enough to be used in small memory
|
||||
environments, while still being functional and secure enough for general use.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.B \-b \fIbanner
|
||||
bannerfile.
|
||||
Display the contents of the file
|
||||
.I banner
|
||||
before user login (default: none).
|
||||
.TP
|
||||
.B \-d \fIdsskey
|
||||
dsskeyfile.
|
||||
Use the contents of the file
|
||||
.I dsskey
|
||||
for the dss host key (default: /etc/dropbear/dropbear_dss_host_key).
|
||||
This file is generated with
|
||||
.BR dropbearkey (8).
|
||||
.TP
|
||||
.B \-r \fIrsakey
|
||||
rsakeyfile.
|
||||
Use the contents of the file
|
||||
.I rsakey
|
||||
for the rsa host key (default: /etc/dropbear/dropbear_rsa_host_key).
|
||||
This file is generated with
|
||||
.BR dropbearkey (8).
|
||||
.TP
|
||||
.B \-F
|
||||
Don't fork into background.
|
||||
.TP
|
||||
.B \-E
|
||||
Log to standard error rather than syslog.
|
||||
.TP
|
||||
.B \-m
|
||||
Don't display the message of the day on login.
|
||||
.TP
|
||||
.B \-w
|
||||
Disallow root logins.
|
||||
.TP
|
||||
.B \-s
|
||||
Disable password logins.
|
||||
.TP
|
||||
.B \-g
|
||||
Disable password logins for root.
|
||||
.TP
|
||||
.B \-j
|
||||
Disable local port forwarding.
|
||||
.TP
|
||||
.B \-k
|
||||
Disable remote port forwarding.
|
||||
.TP
|
||||
.B \-p \fIport
|
||||
Listen on specified tcp port
|
||||
.IR port ;
|
||||
up to 10 can be specified (default 22 if none specified).
|
||||
.TP
|
||||
.B \-i
|
||||
Service program mode.
|
||||
Use this option to run
|
||||
.B dropbear
|
||||
under TCP/IP servers like inetd, tcpsvd, or tcpserver.
|
||||
In program mode the \-F option is implied, and \-p options are ignored.
|
||||
.SH AUTHOR
|
||||
Matt Johnston (matt@ucc.asn.au).
|
||||
.br
|
||||
Gerrit Pape (pape@smarden.org) wrote this manual page.
|
||||
.SH SEE ALSO
|
||||
dropbearkey(8)
|
||||
.P
|
||||
http://matt.ucc.asn.au/dropbear/dropbear.html
|
||||
@@ -47,10 +47,8 @@ static void printhelp(char * progname) {
|
||||
"dropbear\n"
|
||||
"\n"
|
||||
"Example:\n"
|
||||
"dropbearconvert openssh dropbear /etc/ssh/ssh_host_rsa_key /etc/dropbear_rsa_host_key\n"
|
||||
"\n"
|
||||
"The inputfile and outputfile can be '-' to specify\n"
|
||||
"standard input or standard output.\n", progname);
|
||||
"dropbearconvert openssh dropbear /etc/ssh/ssh_host_rsa_key /etc/dropbear_rsa_host_key\n",
|
||||
progname);
|
||||
}
|
||||
|
||||
#if defined(DBMULTI_dropbearconvert) || !defined(DROPBEAR_MULTI)
|
||||
@@ -64,6 +62,11 @@ int main(int argc, char ** argv) {
|
||||
const char* infile;
|
||||
const char* outfile;
|
||||
|
||||
#ifdef DEBUG_TRACE
|
||||
/* It's hard for it to get in the way _too_ much */
|
||||
debug_trace = 1;
|
||||
#endif
|
||||
|
||||
/* get the commandline options */
|
||||
if (argc != 5) {
|
||||
fprintf(stderr, "All arguments must be specified\n");
|
||||
|
||||
47
dropbearkey.8
Normal file
47
dropbearkey.8
Normal file
@@ -0,0 +1,47 @@
|
||||
.TH dropbearkey 8
|
||||
.SH NAME
|
||||
dropbearkey \- create private keys for the use with dropbear(8)
|
||||
.SH SYNOPSIS
|
||||
.B dropbearkey
|
||||
\-t
|
||||
.I type
|
||||
\-f
|
||||
.I file
|
||||
[\-s
|
||||
.IR bits ]
|
||||
.SH DESCRIPTION
|
||||
.B dropbearkey
|
||||
generates a type
|
||||
.I rsa
|
||||
or
|
||||
.I dss
|
||||
SSH private key, and saves it to a file for the use with the
|
||||
.BR dropbear (8)
|
||||
SSH 2 server.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.B \-t \fItype
|
||||
Type of key to generate.
|
||||
Must be one of
|
||||
.I rsa
|
||||
or
|
||||
.IR dss .
|
||||
.TP
|
||||
.B \-f \fIfile
|
||||
Write the secret key to the file
|
||||
.IR file .
|
||||
.TP
|
||||
.B \-s \fIbits
|
||||
Set the key size to
|
||||
.I bits
|
||||
bits, should be multiple of 8 (optional).
|
||||
.SH EXAMPLE
|
||||
# dropbearkey -t rsa -f /etc/dropbear/dropbear_rsa_host_key
|
||||
.SH AUTHOR
|
||||
Matt Johnston (matt@ucc.asn.au).
|
||||
.br
|
||||
Gerrit Pape (pape@smarden.org) wrote this manual page.
|
||||
.SH SEE ALSO
|
||||
dropbear(8)
|
||||
.P
|
||||
http://matt.ucc.asn.au/dropbear/dropbear.html
|
||||
@@ -75,8 +75,11 @@ static void printhelp(char * progname) {
|
||||
#endif
|
||||
"-f filename Use filename for the secret key\n"
|
||||
"-s bits Key size in bits, should be a multiple of 8 (optional)\n"
|
||||
"-y Just print the publickey and fingerprint for the\n private key in <filename>.\n",
|
||||
progname);
|
||||
"-y Just print the publickey and fingerprint for the\n private key in <filename>.\n"
|
||||
#ifdef DEBUG_TRACE
|
||||
"-v verbose\n"
|
||||
#endif
|
||||
,progname);
|
||||
}
|
||||
|
||||
#if defined(DBMULTI_dropbearkey) || !defined(DROPBEAR_MULTI)
|
||||
@@ -127,6 +130,11 @@ int main(int argc, char ** argv) {
|
||||
printhelp(argv[0]);
|
||||
exit(EXIT_SUCCESS);
|
||||
break;
|
||||
#ifdef DEBUG_TRACE
|
||||
case 'v':
|
||||
debug_trace = 1;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
fprintf(stderr, "Unknown argument %s\n", argv[i]);
|
||||
printhelp(argv[0]);
|
||||
@@ -158,13 +166,13 @@ int main(int argc, char ** argv) {
|
||||
#ifdef DROPBEAR_RSA
|
||||
if (strncmp(typetext, "rsa", 3) == 0) {
|
||||
keytype = DROPBEAR_SIGNKEY_RSA;
|
||||
TRACE(("type is rsa"));
|
||||
TRACE(("type is rsa"))
|
||||
}
|
||||
#endif
|
||||
#ifdef DROPBEAR_DSS
|
||||
if (strncmp(typetext, "dss", 3) == 0) {
|
||||
keytype = DROPBEAR_SIGNKEY_DSS;
|
||||
TRACE(("type is dss"));
|
||||
TRACE(("type is dss"))
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
59
dss.c
59
dss.c
@@ -45,7 +45,7 @@
|
||||
* Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
|
||||
int buf_get_dss_pub_key(buffer* buf, dss_key *key) {
|
||||
|
||||
TRACE(("enter buf_get_dss_pub_key"));
|
||||
TRACE(("enter buf_get_dss_pub_key"))
|
||||
assert(key != NULL);
|
||||
key->p = m_malloc(sizeof(mp_int));
|
||||
key->q = m_malloc(sizeof(mp_int));
|
||||
@@ -59,17 +59,17 @@ int buf_get_dss_pub_key(buffer* buf, dss_key *key) {
|
||||
|| buf_getmpint(buf, key->q) == DROPBEAR_FAILURE
|
||||
|| buf_getmpint(buf, key->g) == DROPBEAR_FAILURE
|
||||
|| buf_getmpint(buf, key->y) == DROPBEAR_FAILURE) {
|
||||
TRACE(("leave buf_get_dss_pub_key: failed reading mpints"));
|
||||
TRACE(("leave buf_get_dss_pub_key: failed reading mpints"))
|
||||
return DROPBEAR_FAILURE;
|
||||
}
|
||||
|
||||
if (mp_count_bits(key->p) < MIN_DSS_KEYLEN) {
|
||||
dropbear_log(LOG_WARNING, "DSS key too short");
|
||||
TRACE(("leave buf_get_dss_pub_key: short key"));
|
||||
TRACE(("leave buf_get_dss_pub_key: short key"))
|
||||
return DROPBEAR_FAILURE;
|
||||
}
|
||||
|
||||
TRACE(("leave buf_get_dss_pub_key: success"));
|
||||
TRACE(("leave buf_get_dss_pub_key: success"))
|
||||
return DROPBEAR_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -98,9 +98,9 @@ int buf_get_dss_priv_key(buffer* buf, dss_key *key) {
|
||||
/* Clear and free the memory used by a public or private key */
|
||||
void dss_key_free(dss_key *key) {
|
||||
|
||||
TRACE(("enter dsa_key_free"));
|
||||
TRACE(("enter dsa_key_free"))
|
||||
if (key == NULL) {
|
||||
TRACE(("enter dsa_key_free: key == NULL"));
|
||||
TRACE(("enter dsa_key_free: key == NULL"))
|
||||
return;
|
||||
}
|
||||
if (key->p) {
|
||||
@@ -124,7 +124,7 @@ void dss_key_free(dss_key *key) {
|
||||
m_free(key->x);
|
||||
}
|
||||
m_free(key);
|
||||
TRACE(("leave dsa_key_free"));
|
||||
TRACE(("leave dsa_key_free"))
|
||||
}
|
||||
|
||||
/* put the dss public key into the buffer in the required format:
|
||||
@@ -164,11 +164,14 @@ int buf_dss_verify(buffer* buf, dss_key *key, const unsigned char* data,
|
||||
unsigned char msghash[SHA1_HASH_SIZE];
|
||||
hash_state hs;
|
||||
int ret = DROPBEAR_FAILURE;
|
||||
mp_int val1, val2, val3, val4;
|
||||
DEF_MP_INT(val1);
|
||||
DEF_MP_INT(val2);
|
||||
DEF_MP_INT(val3);
|
||||
DEF_MP_INT(val4);
|
||||
char * string = NULL;
|
||||
int stringlen;
|
||||
|
||||
TRACE(("enter buf_dss_verify"));
|
||||
TRACE(("enter buf_dss_verify"))
|
||||
assert(key != NULL);
|
||||
|
||||
m_mp_init_multi(&val1, &val2, &val3, &val4, NULL);
|
||||
@@ -192,7 +195,7 @@ int buf_dss_verify(buffer* buf, dss_key *key, const unsigned char* data,
|
||||
goto out;
|
||||
}
|
||||
if (mp_cmp(&val1, key->q) != MP_LT) {
|
||||
TRACE(("verify failed, s' >= q"));
|
||||
TRACE(("verify failed, s' >= q"))
|
||||
goto out;
|
||||
}
|
||||
/* let val2 = w = (s')^-1 mod q*/
|
||||
@@ -217,7 +220,7 @@ int buf_dss_verify(buffer* buf, dss_key *key, const unsigned char* data,
|
||||
goto out;
|
||||
}
|
||||
if (mp_cmp(&val1, key->q) != MP_LT) {
|
||||
TRACE(("verify failed, r' >= q"));
|
||||
TRACE(("verify failed, r' >= q"))
|
||||
goto out;
|
||||
}
|
||||
/* let val4 = u2 = ((r')w) mod q */
|
||||
@@ -258,6 +261,25 @@ out:
|
||||
}
|
||||
#endif /* DROPBEAR_SIGNKEY_VERIFY */
|
||||
|
||||
/* convert an unsigned mp into an array of bytes, malloced.
|
||||
* This array must be freed after use, len contains the length of the array,
|
||||
* if len != NULL */
|
||||
static unsigned char* mptobytes(mp_int *mp, int *len) {
|
||||
|
||||
unsigned char* ret;
|
||||
int size;
|
||||
|
||||
size = mp_unsigned_bin_size(mp);
|
||||
ret = m_malloc(size);
|
||||
if (mp_to_unsigned_bin(mp, ret) != MP_OKAY) {
|
||||
dropbear_exit("mem alloc error");
|
||||
}
|
||||
if (len != NULL) {
|
||||
*len = size;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Sign the data presented with key, writing the signature contents
|
||||
* to the buffer
|
||||
*
|
||||
@@ -281,16 +303,19 @@ void buf_put_dss_sign(buffer* buf, dss_key *key, const unsigned char* data,
|
||||
unsigned char privkeyhash[SHA512_HASH_SIZE];
|
||||
unsigned char *privkeytmp;
|
||||
unsigned char proto_k[SHA512_HASH_SIZE];
|
||||
mp_int dss_protok;
|
||||
DEF_MP_INT(dss_protok);
|
||||
#else
|
||||
unsigned char kbuf[SHA1_HASH_SIZE];
|
||||
#endif
|
||||
mp_int dss_k, dss_m;
|
||||
mp_int dss_temp1, dss_temp2;
|
||||
mp_int dss_r, dss_s;
|
||||
DEF_MP_INT(dss_k);
|
||||
DEF_MP_INT(dss_m);
|
||||
DEF_MP_INT(dss_temp1);
|
||||
DEF_MP_INT(dss_temp2);
|
||||
DEF_MP_INT(dss_r);
|
||||
DEF_MP_INT(dss_s);
|
||||
hash_state hs;
|
||||
|
||||
TRACE(("enter buf_put_dss_sign"));
|
||||
TRACE(("enter buf_put_dss_sign"))
|
||||
assert(key != NULL);
|
||||
|
||||
/* hash the data */
|
||||
@@ -397,7 +422,7 @@ void buf_put_dss_sign(buffer* buf, dss_key *key, const unsigned char* data,
|
||||
|
||||
/* create the signature to return */
|
||||
|
||||
TRACE(("leave buf_put_dss_sign"));
|
||||
TRACE(("leave buf_put_dss_sign"))
|
||||
}
|
||||
|
||||
#endif /* DROPBEAR_DSS */
|
||||
|
||||
16
gendss.c
16
gendss.c
@@ -33,6 +33,8 @@
|
||||
|
||||
#define QSIZE 20 /* 160 bit */
|
||||
|
||||
/* This is just a test */
|
||||
|
||||
#ifdef DROPBEAR_DSS
|
||||
|
||||
static void getq(dss_key *key);
|
||||
@@ -89,7 +91,10 @@ static void getq(dss_key *key) {
|
||||
|
||||
static void getp(dss_key *key, unsigned int size) {
|
||||
|
||||
mp_int tempX, tempC, tempP, temp2q;
|
||||
DEF_MP_INT(tempX);
|
||||
DEF_MP_INT(tempC);
|
||||
DEF_MP_INT(tempP);
|
||||
DEF_MP_INT(temp2q);
|
||||
int result;
|
||||
unsigned char *buf;
|
||||
|
||||
@@ -147,8 +152,9 @@ static void getp(dss_key *key, unsigned int size) {
|
||||
|
||||
static void getg(dss_key * key) {
|
||||
|
||||
char printbuf[1000];
|
||||
mp_int div, h, val;
|
||||
DEF_MP_INT(div);
|
||||
DEF_MP_INT(h);
|
||||
DEF_MP_INT(val);
|
||||
|
||||
m_mp_init_multi(&div, &h, &val, NULL);
|
||||
|
||||
@@ -178,14 +184,12 @@ static void getg(dss_key * key) {
|
||||
|
||||
} while (mp_cmp_d(key->g, 1) != MP_GT);
|
||||
|
||||
mp_toradix(key->g, printbuf, 10);
|
||||
|
||||
mp_clear_multi(&div, &h, &val, NULL);
|
||||
}
|
||||
|
||||
static void getx(dss_key *key) {
|
||||
|
||||
mp_int val;
|
||||
DEF_MP_INT(val);
|
||||
char buf[QSIZE];
|
||||
|
||||
m_mp_init(&val);
|
||||
|
||||
6
genrsa.c
6
genrsa.c
@@ -40,7 +40,9 @@ static void getrsaprime(mp_int* prime, mp_int *primeminus,
|
||||
rsa_key * gen_rsa_priv_key(unsigned int size) {
|
||||
|
||||
rsa_key * key;
|
||||
mp_int pminus, qminus, lcm;
|
||||
DEF_MP_INT(pminus);
|
||||
DEF_MP_INT(qminus);
|
||||
DEF_MP_INT(lcm);
|
||||
|
||||
key = (rsa_key*)m_malloc(sizeof(rsa_key));
|
||||
|
||||
@@ -95,7 +97,7 @@ static void getrsaprime(mp_int* prime, mp_int *primeminus,
|
||||
mp_int* rsa_e, unsigned int size) {
|
||||
|
||||
unsigned char *buf;
|
||||
mp_int temp_gcd;
|
||||
DEF_MP_INT(temp_gcd);
|
||||
|
||||
buf = (unsigned char*)m_malloc(size+1);
|
||||
|
||||
|
||||
12
includes.h
12
includes.h
@@ -111,7 +111,7 @@
|
||||
#include <libgen.h>
|
||||
#endif
|
||||
|
||||
#include "libtomcrypt/mycrypt_custom.h"
|
||||
#include "libtomcrypt/mycrypt.h"
|
||||
#include "libtommath/tommath.h"
|
||||
|
||||
#include "compat.h"
|
||||
@@ -128,4 +128,14 @@ typedef u_int16_t uint16_t;
|
||||
#define LOG_AUTHPRIV LOG_AUTH
|
||||
#endif
|
||||
|
||||
/* so we can avoid warnings about unused params (ie in signal handlers etc) */
|
||||
#ifdef UNUSED
|
||||
#elif defined(__GNUC__)
|
||||
# define UNUSED(x) UNUSED_ ## x __attribute__((unused))
|
||||
#elif defined(__LCLINT__)
|
||||
# define UNUSED(x) /*@unused@*/ x
|
||||
#else
|
||||
# define UNUSED(x) x
|
||||
#endif
|
||||
|
||||
#endif /* _INCLUDES_H_ */
|
||||
|
||||
@@ -108,13 +108,10 @@ int import_write(const char *filename, sign_key *key, char *passphrase,
|
||||
static sign_key *dropbear_read(const char* filename) {
|
||||
|
||||
buffer * buf = NULL;
|
||||
int len, maxlen;
|
||||
FILE *fp = NULL;
|
||||
sign_key *ret = NULL;
|
||||
int type;
|
||||
|
||||
buf = buf_new(MAX_PRIVKEY_SIZE);
|
||||
/* buf_readfile knows about "-" */
|
||||
if (buf_readfile(buf, filename) == DROPBEAR_FAILURE) {
|
||||
goto error;
|
||||
}
|
||||
@@ -163,11 +160,7 @@ static int dropbear_write(const char*filename, sign_key * key) {
|
||||
buf = buf_new(MAX_PRIVKEY_SIZE);
|
||||
buf_put_priv_key(buf, key, keytype);
|
||||
|
||||
if (strlen(filename) == 1 && filename[0] == '-') {
|
||||
fp = stdout;
|
||||
} else {
|
||||
fp = fopen(filename, "w");
|
||||
}
|
||||
fp = fopen(filename, "w");
|
||||
if (!fp) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
|
||||
@@ -93,7 +93,7 @@ struct Listener* new_listener(int socks[], unsigned int nsocks,
|
||||
/* or create a new one */
|
||||
if (i == ses.listensize) {
|
||||
if (ses.listensize > MAX_LISTENERS) {
|
||||
TRACE(("leave newlistener: too many already"));
|
||||
TRACE(("leave newlistener: too many already"))
|
||||
for (j = 0; j < nsocks; j++) {
|
||||
close(socks[i]);
|
||||
}
|
||||
@@ -115,7 +115,7 @@ struct Listener* new_listener(int socks[], unsigned int nsocks,
|
||||
ses.maxfd = MAX(ses.maxfd, socks[j]);
|
||||
}
|
||||
|
||||
TRACE(("new listener num %d ", i));
|
||||
TRACE(("new listener num %d ", i))
|
||||
|
||||
newlisten = (struct Listener*)m_malloc(sizeof(struct Listener));
|
||||
newlisten->index = i;
|
||||
|
||||
153
loginrec.c
153
loginrec.c
@@ -29,6 +29,8 @@
|
||||
** loginrec.c: platform-independent login recording and lastlog retrieval
|
||||
**/
|
||||
|
||||
/* For now lastlog code has been removed as it wasn't being used by Dropbear. */
|
||||
|
||||
/*
|
||||
The new login code explained
|
||||
============================
|
||||
@@ -177,8 +179,6 @@ int wtmpx_write_entry(struct logininfo *li);
|
||||
int lastlog_write_entry(struct logininfo *li);
|
||||
int syslogin_write_entry(struct logininfo *li);
|
||||
|
||||
int getlast_entry(struct logininfo *li);
|
||||
int lastlog_get_entry(struct logininfo *li);
|
||||
int wtmp_get_entry(struct logininfo *li);
|
||||
int wtmpx_get_entry(struct logininfo *li);
|
||||
|
||||
@@ -221,74 +221,6 @@ login_logout(struct logininfo *li)
|
||||
return login_write(li);
|
||||
}
|
||||
|
||||
/* login_get_lastlog_time(int) - Retrieve the last login time
|
||||
*
|
||||
* Retrieve the last login time for the given uid. Will try to use the
|
||||
* system lastlog facilities if they are available, but will fall back
|
||||
* to looking in wtmp/wtmpx if necessary
|
||||
*
|
||||
* Returns:
|
||||
* 0 on failure, or if user has never logged in
|
||||
* Time in seconds from the epoch if successful
|
||||
*
|
||||
* Useful preprocessor symbols:
|
||||
* DISABLE_LASTLOG: If set, *never* even try to retrieve lastlog
|
||||
* info
|
||||
* USE_LASTLOG: If set, indicates the presence of system lastlog
|
||||
* facilities. If this and DISABLE_LASTLOG are not set,
|
||||
* try to retrieve lastlog information from wtmp/wtmpx.
|
||||
*/
|
||||
unsigned int
|
||||
login_get_lastlog_time(const int uid)
|
||||
{
|
||||
struct logininfo li;
|
||||
|
||||
if (login_get_lastlog(&li, uid))
|
||||
return li.tv_sec;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* login_get_lastlog(struct logininfo *, int) - Retrieve a lastlog entry
|
||||
*
|
||||
* Retrieve a logininfo structure populated (only partially) with
|
||||
* information from the system lastlog data, or from wtmp/wtmpx if no
|
||||
* system lastlog information exists.
|
||||
*
|
||||
* Note this routine must be given a pre-allocated logininfo.
|
||||
*
|
||||
* Returns:
|
||||
* >0: A pointer to your struct logininfo if successful
|
||||
* 0 on failure (will use OpenSSH's logging facilities for diagnostics)
|
||||
*
|
||||
*/
|
||||
struct logininfo *
|
||||
login_get_lastlog(struct logininfo *li, const int uid)
|
||||
{
|
||||
struct passwd *pw;
|
||||
|
||||
memset(li, '\0', sizeof(*li));
|
||||
li->uid = uid;
|
||||
|
||||
/*
|
||||
* If we don't have a 'real' lastlog, we need the username to
|
||||
* reliably search wtmp(x) for the last login (see
|
||||
* wtmp_get_entry().)
|
||||
*/
|
||||
pw = getpwuid(uid);
|
||||
if (pw == NULL)
|
||||
dropbear_exit("login_get_lastlog: Cannot find account for uid %i", uid);
|
||||
|
||||
/* No MIN_SIZEOF here - we absolutely *must not* truncate the
|
||||
* username */
|
||||
strlcpy(li->username, pw->pw_name, sizeof(li->username));
|
||||
|
||||
if (getlast_entry(li))
|
||||
return li;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* login_alloc_entry(int, char*, char*, char*) - Allocate and initialise
|
||||
* a logininfo structure
|
||||
@@ -450,42 +382,6 @@ login_utmp_only(struct logininfo *li)
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
** getlast_entry: Call low-level functions to retrieve the last login
|
||||
** time.
|
||||
**/
|
||||
|
||||
/* take the uid in li and return the last login time */
|
||||
int
|
||||
getlast_entry(struct logininfo *li)
|
||||
{
|
||||
#ifdef USE_LASTLOG
|
||||
return(lastlog_get_entry(li));
|
||||
#else /* !USE_LASTLOG */
|
||||
|
||||
#ifdef DISABLE_LASTLOG
|
||||
/* On some systems we shouldn't even try to obtain last login
|
||||
* time, e.g. AIX */
|
||||
return 0;
|
||||
# else /* DISABLE_LASTLOG */
|
||||
/* Try to retrieve the last login time from wtmp */
|
||||
# if defined(USE_WTMP) && (defined(HAVE_STRUCT_UTMP_UT_TIME) || defined(HAVE_STRUCT_UTMP_UT_TV))
|
||||
/* retrieve last login time from utmp */
|
||||
return (wtmp_get_entry(li));
|
||||
# else /* defined(USE_WTMP) && (defined(HAVE_STRUCT_UTMP_UT_TIME) || defined(HAVE_STRUCT_UTMP_UT_TV)) */
|
||||
/* If wtmp isn't available, try wtmpx */
|
||||
# if defined(USE_WTMPX) && (defined(HAVE_STRUCT_UTMPX_UT_TIME) || defined(HAVE_STRUCT_UTMPX_UT_TV))
|
||||
/* retrieve last login time from utmpx */
|
||||
return (wtmpx_get_entry(li));
|
||||
# else
|
||||
/* Give up: No means of retrieving last login time */
|
||||
return 0;
|
||||
# endif /* USE_WTMPX && (HAVE_STRUCT_UTMPX_UT_TIME || HAVE_STRUCT_UTMPX_UT_TV) */
|
||||
# endif /* USE_WTMP && (HAVE_STRUCT_UTMP_UT_TIME || HAVE_STRUCT_UTMP_UT_TV) */
|
||||
# endif /* DISABLE_LASTLOG */
|
||||
#endif /* USE_LASTLOG */
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
@@ -1401,8 +1297,8 @@ lastlog_filetype(char *filename)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
if (stat(LASTLOG_FILE, &st) != 0) {
|
||||
dropbear_log(LOG_WARNING, "lastlog_perform_login: Couldn't stat %s: %s", LASTLOG_FILE,
|
||||
if (stat(filename, &st) != 0) {
|
||||
dropbear_log(LOG_WARNING, "lastlog_perform_login: Couldn't stat %s: %s", filename,
|
||||
strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
@@ -1495,45 +1391,4 @@ lastlog_write_entry(struct logininfo *li)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
lastlog_populate_entry(struct logininfo *li, struct lastlog *last)
|
||||
{
|
||||
line_fullname(li->line, last->ll_line, sizeof(li->line));
|
||||
strlcpy(li->hostname, last->ll_host,
|
||||
MIN_SIZEOF(li->hostname, last->ll_host));
|
||||
li->tv_sec = last->ll_time;
|
||||
}
|
||||
|
||||
int
|
||||
lastlog_get_entry(struct logininfo *li)
|
||||
{
|
||||
struct lastlog last;
|
||||
int fd, ret;
|
||||
|
||||
if (!lastlog_openseek(li, &fd, O_RDONLY))
|
||||
return (0);
|
||||
|
||||
ret = atomicio(read, fd, &last, sizeof(last));
|
||||
close(fd);
|
||||
|
||||
switch (ret) {
|
||||
case 0:
|
||||
memset(&last, '\0', sizeof(last));
|
||||
/* FALLTHRU */
|
||||
case sizeof(last):
|
||||
lastlog_populate_entry(li, &last);
|
||||
return (1);
|
||||
case -1:
|
||||
dropbear_log(LOG_ERR, "Error reading from %s: %s",
|
||||
LASTLOG_FILE, strerror(errno));
|
||||
return (0);
|
||||
default:
|
||||
dropbear_log(LOG_ERR, "Error reading from %s: Expecting %d, got %d",
|
||||
LASTLOG_FILE, sizeof(last), ret);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* NOTREACHED */
|
||||
return (0);
|
||||
}
|
||||
#endif /* USE_LASTLOG */
|
||||
|
||||
@@ -150,7 +150,6 @@ struct logininfo {
|
||||
|
||||
/** 'public' functions */
|
||||
|
||||
/* construct a new login entry */
|
||||
struct logininfo *login_alloc_entry(int pid, const char *username,
|
||||
const char *hostname, const char *line);
|
||||
/* free a structure */
|
||||
@@ -178,14 +177,6 @@ int login_log_entry(struct logininfo *li);
|
||||
void login_set_addr(struct logininfo *li, const struct sockaddr *sa,
|
||||
const unsigned int sa_size);
|
||||
|
||||
/*
|
||||
* lastlog retrieval functions
|
||||
*/
|
||||
/* lastlog *entry* functions fill out a logininfo */
|
||||
struct logininfo *login_get_lastlog(struct logininfo *li, const int uid);
|
||||
/* lastlog *time* functions return time_t equivalent (uint) */
|
||||
unsigned int login_get_lastlog_time(const int uid);
|
||||
|
||||
/* produce various forms of the line filename */
|
||||
char *line_fullname(char *dst, const char *src, size_t dstsize);
|
||||
char *line_stripname(char *dst, const char *src, size_t dstsize);
|
||||
|
||||
94
options.h
94
options.h
@@ -1,26 +1,6 @@
|
||||
/*
|
||||
* Dropbear - a SSH2 server
|
||||
*
|
||||
/* Dropbear SSH
|
||||
* Copyright (c) 2002,2003 Matt Johnston
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE. */
|
||||
* All rights reserved. See LICENSE for the license. */
|
||||
|
||||
#ifndef _OPTIONS_H_
|
||||
#define _OPTIONS_H_
|
||||
@@ -30,8 +10,8 @@
|
||||
* parts are to allow for commandline -DDROPBEAR_XXX options etc.
|
||||
******************************************************************/
|
||||
|
||||
#ifndef DROPBEAR_PORT
|
||||
#define DROPBEAR_PORT 22
|
||||
#ifndef DROPBEAR_DEFPORT
|
||||
#define DROPBEAR_DEFPORT "22"
|
||||
#endif
|
||||
|
||||
/* Default hostkey paths - these can be specified on the command line */
|
||||
@@ -116,9 +96,7 @@ etc) slower (perhaps by 50%). Recommended for most small systems. */
|
||||
* if the random number source isn't good. In general this isn't required */
|
||||
/* #define DSS_PROTOK */
|
||||
|
||||
/* Whether to do reverse DNS lookups. This is advisable, though will add
|
||||
* code size with gethostbyname() etc, so for very small environments where
|
||||
* you are statically linking, you might want to undefine this */
|
||||
/* Whether to do reverse DNS lookups. */
|
||||
#define DO_HOST_LOOKUP
|
||||
|
||||
/* Whether to print the message of the day (MOTD). This doesn't add much code
|
||||
@@ -130,27 +108,45 @@ etc) slower (perhaps by 50%). Recommended for most small systems. */
|
||||
#define MOTD_FILENAME "/etc/motd"
|
||||
#endif
|
||||
|
||||
/* Authentication types to enable, at least one required.
|
||||
/* Authentication Types - at least one required.
|
||||
RFC Draft requires pubkey auth, and recommends password */
|
||||
|
||||
/* Note: PAM auth is quite simple, and only works for PAM modules which just do
|
||||
* a simple "Login: " "Password: " (you can edit the strings in svr-authpam.c).
|
||||
* It's useful for systems like OS X where standard password crypts don't work,
|
||||
* but there's an interface via a PAM module - don't bother using it otherwise.
|
||||
* You can't enable both PASSWORD and PAM. */
|
||||
|
||||
#define ENABLE_SVR_PASSWORD_AUTH
|
||||
/*#define ENABLE_SVR_PAM_AUTH*/
|
||||
#define ENABLE_SVR_PUBKEY_AUTH
|
||||
|
||||
#define ENABLE_CLI_PASSWORD_AUTH
|
||||
#define ENABLE_CLI_PUBKEY_AUTH
|
||||
|
||||
/* Random device to use - you must specify _one only_.
|
||||
* DEV_RANDOM is recommended on hosts with a good /dev/urandom, otherwise use
|
||||
* PRNGD and run prngd, specifying the socket. This device must be able to
|
||||
* produce a large amount of random data, so using /dev/random or Entropy
|
||||
* Gathering Daemon (egd) may result in halting, as it waits for more random
|
||||
* data */
|
||||
#define DROPBEAR_DEV_URANDOM /* use /dev/urandom */
|
||||
/* Define this (as well as ENABLE_CLI_PASSWORD_AUTH) to allow the use of
|
||||
* a helper program for the ssh client. The helper program should be
|
||||
* specified in the SSH_ASKPASS environment variable, and dbclient
|
||||
* should be run with DISPLAY set and no tty. The program should
|
||||
* return the password on standard output */
|
||||
/*#define ENABLE_CLI_ASKPASS_HELPER*/
|
||||
|
||||
/*#undef DROPBEAR_PRNGD */ /* use prngd socket - you must manually set up prngd
|
||||
to produce output */
|
||||
#ifndef DROPBEAR_PRNGD_SOCKET
|
||||
#define DROPBEAR_PRNGD_SOCKET "/var/run/dropbear-rng"
|
||||
#endif
|
||||
/* Random device to use - define either DROPBEAR_RANDOM_DEV or
|
||||
* DROPBEAR_PRNGD_SOCKET.
|
||||
* DROPBEAR_RANDOM_DEV is recommended on hosts with a good /dev/(u)random,
|
||||
* otherwise use run prngd (or egd if you want), specifying the socket.
|
||||
* The device will be queried for a few dozen bytes of seed a couple of times
|
||||
* per session (or more for very long-lived sessions). */
|
||||
|
||||
/* If you are lacking entropy on the system then using /dev/urandom
|
||||
* will prevent Dropbear from blocking on the device. This could
|
||||
* however significantly reduce the security of your ssh connections
|
||||
* if the PRNG state becomes guessable - make sure you know what you are
|
||||
* doing if you change this. */
|
||||
#define DROPBEAR_RANDOM_DEV "/dev/random"
|
||||
|
||||
/* prngd must be manually set up to produce output */
|
||||
/*#define DROPBEAR_PRNGD_SOCKET "/var/run/dropbear-rng"*/
|
||||
|
||||
/* Specify the number of clients we will allow to be connected but
|
||||
* not yet authenticated. After this limit, connections are rejected */
|
||||
@@ -193,7 +189,7 @@ etc) slower (perhaps by 50%). Recommended for most small systems. */
|
||||
*******************************************************************/
|
||||
|
||||
#ifndef DROPBEAR_VERSION
|
||||
#define DROPBEAR_VERSION "0.44test1"
|
||||
#define DROPBEAR_VERSION "0.45"
|
||||
#endif
|
||||
|
||||
#define LOCAL_IDENT "SSH-2.0-dropbear_" DROPBEAR_VERSION
|
||||
@@ -223,8 +219,6 @@ etc) slower (perhaps by 50%). Recommended for most small systems. */
|
||||
#define MAX_BANNER_SIZE 2000 /* this is 25*80 chars, any more is foolish */
|
||||
#define MAX_BANNER_LINES 20 /* How many lines the client will display */
|
||||
|
||||
#define DEV_URANDOM "/dev/urandom"
|
||||
|
||||
/* the number of NAME=VALUE pairs to malloc for environ, if we don't have
|
||||
* the clearenv() function */
|
||||
#define ENV_SIZE 100
|
||||
@@ -309,6 +303,8 @@ etc) slower (perhaps by 50%). Recommended for most small systems. */
|
||||
#define DROPBEAR_MAX_SOCKS 2 /* IPv4, IPv6 are all we'll get for now. Revisit
|
||||
in a few years time.... */
|
||||
|
||||
#define DROPBEAR_MAX_CLI_PASS 1024
|
||||
|
||||
#ifndef ENABLE_X11FWD
|
||||
#define DISABLE_X11FWD
|
||||
#endif
|
||||
@@ -342,6 +338,18 @@ etc) slower (perhaps by 50%). Recommended for most small systems. */
|
||||
#define DROPBEAR_KEY_LINES /* ie we're using authorized_keys or known_hosts */
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_SVR_PASSWORD_AUTH) && defined(ENABLE_SVR_PAM_AUTH)
|
||||
#error "You can't turn on PASSWORD and PAM auth both at once. Fix it in options.h"
|
||||
#endif
|
||||
|
||||
#if defined(DROPBEAR_RANDOM_DEV) && defined(DROPBEAR_PRNGD_SOCKET)
|
||||
#error "You can't turn on DROPBEAR_PRNGD_SOCKET and DROPBEAR_RANDOM_DEV at once"
|
||||
#endif
|
||||
|
||||
#if !defined(DROPBEAR_RANDOM_DEV) && !defined(DROPBEAR_PRNGD_SOCKET)
|
||||
#error "You must choose one of DROPBEAR_PRNGD_SOCKET or DROPBEAR_RANDOM_DEV in options.h"
|
||||
#endif
|
||||
|
||||
/* We use dropbear_client and dropbear_server as shortcuts to avoid redundant
|
||||
* code, if we're just compiling as client or server */
|
||||
#if defined(DROPBEAR_SERVER) && defined(DROPBEAR_CLIENT)
|
||||
|
||||
34
packet.c
34
packet.c
@@ -52,7 +52,7 @@ void write_packet() {
|
||||
int len, written;
|
||||
buffer * writebuf = NULL;
|
||||
|
||||
TRACE(("enter write_packet"));
|
||||
TRACE(("enter write_packet"))
|
||||
assert(!isempty(&ses.writequeue));
|
||||
|
||||
/* Get the next buffer in the queue of encrypted packets to write*/
|
||||
@@ -65,7 +65,7 @@ void write_packet() {
|
||||
|
||||
if (written < 0) {
|
||||
if (errno == EINTR) {
|
||||
TRACE(("leave writepacket: EINTR"));
|
||||
TRACE(("leave writepacket: EINTR"))
|
||||
return;
|
||||
} else {
|
||||
dropbear_exit("error writing");
|
||||
@@ -86,7 +86,7 @@ void write_packet() {
|
||||
buf_incrpos(writebuf, written);
|
||||
}
|
||||
|
||||
TRACE(("leave write_packet"));
|
||||
TRACE(("leave write_packet"))
|
||||
}
|
||||
|
||||
/* Non-blocking function reading available portion of a packet into the
|
||||
@@ -98,7 +98,7 @@ void read_packet() {
|
||||
unsigned int maxlen;
|
||||
unsigned char blocksize;
|
||||
|
||||
TRACE(("enter read_packet"));
|
||||
TRACE(("enter read_packet"))
|
||||
blocksize = ses.keys->recv_algo_crypt->blocksize;
|
||||
|
||||
if (ses.readbuf == NULL || ses.readbuf->len < blocksize) {
|
||||
@@ -111,7 +111,7 @@ void read_packet() {
|
||||
/* If we don't have the length of decryptreadbuf, we didn't read
|
||||
* a whole blocksize and should exit */
|
||||
if (ses.decryptreadbuf->len == 0) {
|
||||
TRACE(("leave read_packet: packetinit done"));
|
||||
TRACE(("leave read_packet: packetinit done"))
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -128,7 +128,7 @@ void read_packet() {
|
||||
|
||||
if (len < 0) {
|
||||
if (errno == EINTR || errno == EAGAIN) {
|
||||
TRACE(("leave read_packet: EINTR or EAGAIN"));
|
||||
TRACE(("leave read_packet: EINTR or EAGAIN"))
|
||||
return;
|
||||
} else {
|
||||
dropbear_exit("error reading: %s", strerror(errno));
|
||||
@@ -143,7 +143,7 @@ void read_packet() {
|
||||
/* The main select() loop process_packet() to
|
||||
* handle the packet contents... */
|
||||
}
|
||||
TRACE(("leave read_packet"));
|
||||
TRACE(("leave read_packet"))
|
||||
}
|
||||
|
||||
/* Function used to read the initial portion of a packet, and determine the
|
||||
@@ -176,7 +176,7 @@ static void read_packet_init() {
|
||||
}
|
||||
if (len < 0) {
|
||||
if (errno == EINTR) {
|
||||
TRACE(("leave read_packet_init: EINTR"));
|
||||
TRACE(("leave read_packet_init: EINTR"))
|
||||
return;
|
||||
}
|
||||
dropbear_exit("error reading: %s", strerror(errno));
|
||||
@@ -230,7 +230,7 @@ void decrypt_packet() {
|
||||
unsigned int padlen;
|
||||
unsigned int len;
|
||||
|
||||
TRACE(("enter decrypt_packet"));
|
||||
TRACE(("enter decrypt_packet"))
|
||||
blocksize = ses.keys->recv_algo_crypt->blocksize;
|
||||
macsize = ses.keys->recv_algo_mac->hashsize;
|
||||
|
||||
@@ -305,7 +305,7 @@ void decrypt_packet() {
|
||||
|
||||
ses.recvseq++;
|
||||
|
||||
TRACE(("leave decrypt_packet"));
|
||||
TRACE(("leave decrypt_packet"))
|
||||
}
|
||||
|
||||
/* Checks the mac in hashbuf, for the data in readbuf.
|
||||
@@ -413,8 +413,8 @@ void encrypt_packet() {
|
||||
buffer * writebuf; /* the packet which will go on the wire */
|
||||
buffer * clearwritebuf; /* unencrypted, possibly compressed */
|
||||
|
||||
TRACE(("enter encrypt_packet()"));
|
||||
TRACE(("encrypt_packet type is %d", ses.writepayload->data[0]));
|
||||
TRACE(("enter encrypt_packet()"))
|
||||
TRACE(("encrypt_packet type is %d", ses.writepayload->data[0]))
|
||||
blocksize = ses.keys->trans_algo_crypt->blocksize;
|
||||
macsize = ses.keys->trans_algo_mac->hashsize;
|
||||
|
||||
@@ -514,7 +514,7 @@ void encrypt_packet() {
|
||||
ses.kexstate.datatrans += writebuf->len;
|
||||
ses.transseq++;
|
||||
|
||||
TRACE(("leave encrypt_packet()"));
|
||||
TRACE(("leave encrypt_packet()"))
|
||||
}
|
||||
|
||||
|
||||
@@ -526,7 +526,7 @@ static void writemac(buffer * outputbuffer, buffer * clearwritebuf) {
|
||||
unsigned long hashsize;
|
||||
hmac_state hmac;
|
||||
|
||||
TRACE(("enter writemac"));
|
||||
TRACE(("enter writemac"))
|
||||
|
||||
macsize = ses.keys->trans_algo_mac->hashsize;
|
||||
|
||||
@@ -561,7 +561,7 @@ static void writemac(buffer * outputbuffer, buffer * clearwritebuf) {
|
||||
}
|
||||
buf_incrwritepos(outputbuffer, macsize);
|
||||
}
|
||||
TRACE(("leave writemac"));
|
||||
TRACE(("leave writemac"))
|
||||
}
|
||||
|
||||
#ifndef DISABLE_ZLIB
|
||||
@@ -572,7 +572,7 @@ static void buf_compress(buffer * dest, buffer * src, unsigned int len) {
|
||||
unsigned int endpos = src->pos + len;
|
||||
int result;
|
||||
|
||||
TRACE(("enter buf_compress"));
|
||||
TRACE(("enter buf_compress"))
|
||||
|
||||
while (1) {
|
||||
|
||||
@@ -606,6 +606,6 @@ static void buf_compress(buffer * dest, buffer * src, unsigned int len) {
|
||||
buf_resize(dest, dest->size + ZLIB_COMPRESS_INCR);
|
||||
|
||||
}
|
||||
TRACE(("leave buf_compress"));
|
||||
TRACE(("leave buf_compress"))
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -45,10 +45,10 @@ void process_packet() {
|
||||
unsigned char type;
|
||||
unsigned int i;
|
||||
|
||||
TRACE(("enter process_packet"));
|
||||
TRACE(("enter process_packet"))
|
||||
|
||||
type = buf_getbyte(ses.payload);
|
||||
TRACE(("process_packet: packet type = %d", type));
|
||||
TRACE(("process_packet: packet type = %d", type))
|
||||
|
||||
ses.lastpacket = type;
|
||||
|
||||
@@ -57,12 +57,12 @@ void process_packet() {
|
||||
|
||||
case SSH_MSG_IGNORE:
|
||||
case SSH_MSG_DEBUG:
|
||||
TRACE(("received SSH_MSG_IGNORE or SSH_MSG_DEBUG"));
|
||||
TRACE(("received SSH_MSG_IGNORE or SSH_MSG_DEBUG"))
|
||||
goto out;
|
||||
|
||||
case SSH_MSG_UNIMPLEMENTED:
|
||||
/* debugging XXX */
|
||||
TRACE(("SSH_MSG_UNIMPLEMENTED"));
|
||||
TRACE(("SSH_MSG_UNIMPLEMENTED"))
|
||||
dropbear_exit("received SSH_MSG_UNIMPLEMENTED");
|
||||
|
||||
case SSH_MSG_DISCONNECT:
|
||||
@@ -87,7 +87,7 @@ void process_packet() {
|
||||
/* Check if we should ignore this packet. Used currently only for
|
||||
* KEX code, with first_kex_packet_follows */
|
||||
if (ses.ignorenext) {
|
||||
TRACE(("Ignoring packet, type = %d", type));
|
||||
TRACE(("Ignoring packet, type = %d", type))
|
||||
ses.ignorenext = 0;
|
||||
goto out;
|
||||
}
|
||||
@@ -115,7 +115,7 @@ void process_packet() {
|
||||
|
||||
|
||||
/* TODO do something more here? */
|
||||
TRACE(("preauth unknown packet"));
|
||||
TRACE(("preauth unknown packet"))
|
||||
recv_unimplemented();
|
||||
|
||||
out:
|
||||
@@ -123,7 +123,7 @@ out:
|
||||
buf_free(ses.payload);
|
||||
ses.payload = NULL;
|
||||
|
||||
TRACE(("leave process_packet"));
|
||||
TRACE(("leave process_packet"))
|
||||
}
|
||||
|
||||
|
||||
|
||||
6
queue.c
6
queue.c
@@ -52,7 +52,7 @@ void* dequeue(struct Queue* queue) {
|
||||
} else {
|
||||
queue->head = NULL;
|
||||
queue->tail = NULL;
|
||||
TRACE(("empty queue dequeing"));
|
||||
TRACE(("empty queue dequeing"))
|
||||
}
|
||||
|
||||
m_free(oldhead);
|
||||
@@ -70,7 +70,7 @@ void enqueue(struct Queue* queue, void* item) {
|
||||
|
||||
struct Link* newlink;
|
||||
|
||||
TRACE(("enter enqueue"));
|
||||
TRACE(("enter enqueue"))
|
||||
newlink = (struct Link*)m_malloc(sizeof(struct Link));
|
||||
|
||||
newlink->item = item;
|
||||
@@ -85,5 +85,5 @@ void enqueue(struct Queue* queue, void* item) {
|
||||
queue->head = newlink;
|
||||
}
|
||||
queue->count++;
|
||||
TRACE(("leave enqueue"));
|
||||
TRACE(("leave enqueue"))
|
||||
}
|
||||
|
||||
32
random.c
32
random.c
@@ -38,7 +38,7 @@ unsigned char hashpool[SHA1_HASH_SIZE];
|
||||
|
||||
static void readrand(unsigned char* buf, unsigned int buflen);
|
||||
|
||||
/* The basic setup is we read some data from DEV_URANDOM or PRNGD and hash it
|
||||
/* The basic setup is we read some data from /dev/(u)random or prngd and hash it
|
||||
* into hashpool. To read data, we hash together current hashpool contents,
|
||||
* and a counter. We feed more data in by hashing the current pool and new
|
||||
* data into the pool.
|
||||
@@ -53,22 +53,22 @@ static void readrand(unsigned char* buf, unsigned int buflen) {
|
||||
int readfd;
|
||||
unsigned int readpos;
|
||||
int readlen;
|
||||
#ifdef DROPBEAR_EGD
|
||||
#ifdef DROPBEAR_PRNGD_SOCKET
|
||||
struct sockaddr_un egdsock;
|
||||
char egdcmd[2];
|
||||
#endif
|
||||
|
||||
#ifdef DROPBEAR_DEV_URANDOM
|
||||
readfd = open(DEV_URANDOM, O_RDONLY);
|
||||
#ifdef DROPBEAR_RANDOM_DEV
|
||||
readfd = open(DROPBEAR_RANDOM_DEV, O_RDONLY);
|
||||
if (readfd < 0) {
|
||||
dropbear_exit("couldn't open random device");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef DROPBEAR_EGD
|
||||
#ifdef DROPBEAR_PRNGD_SOCKET
|
||||
memset((void*)&egdsock, 0x0, sizeof(egdsock));
|
||||
egdsock.sun_family = AF_UNIX;
|
||||
strlcpy(egdsock.sun_path, DROPBEAR_EGD_SOCKET,
|
||||
strlcpy(egdsock.sun_path, DROPBEAR_PRNGD_SOCKET,
|
||||
sizeof(egdsock.sun_path));
|
||||
|
||||
readfd = socket(PF_UNIX, SOCK_STREAM, 0);
|
||||
@@ -105,7 +105,7 @@ static void readrand(unsigned char* buf, unsigned int buflen) {
|
||||
close (readfd);
|
||||
}
|
||||
|
||||
/* initialise the prng from /dev/urandom or prngd */
|
||||
/* initialise the prng from /dev/(u)random or prngd */
|
||||
void seedrandom() {
|
||||
|
||||
unsigned char readbuf[INIT_SEED_SIZE];
|
||||
@@ -159,21 +159,3 @@ void genrandom(unsigned char* buf, unsigned int len) {
|
||||
}
|
||||
m_burn(hash, sizeof(hash));
|
||||
}
|
||||
|
||||
/* Adds entropy to the PRNG state. As long as the hash is strong, then we
|
||||
* don't need to worry about entropy being added "diluting" the current
|
||||
* state - it should only make it stronger. */
|
||||
void addrandom(unsigned char* buf, unsigned int len) {
|
||||
|
||||
hash_state hs;
|
||||
if (!donerandinit) {
|
||||
dropbear_exit("seedrandom not done");
|
||||
}
|
||||
|
||||
sha1_init(&hs);
|
||||
sha1_process(&hs, (void*)buf, len);
|
||||
sha1_process(&hs, (void*)hashpool, sizeof(hashpool));
|
||||
sha1_done(&hs, hashpool);
|
||||
counter = 0;
|
||||
|
||||
}
|
||||
|
||||
55
rsa.c
55
rsa.c
@@ -47,7 +47,7 @@ static mp_int * rsa_pad_em(rsa_key * key,
|
||||
* Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
|
||||
int buf_get_rsa_pub_key(buffer* buf, rsa_key *key) {
|
||||
|
||||
TRACE(("enter buf_get_rsa_pub_key"));
|
||||
TRACE(("enter buf_get_rsa_pub_key"))
|
||||
assert(key != NULL);
|
||||
key->e = m_malloc(sizeof(mp_int));
|
||||
key->n = m_malloc(sizeof(mp_int));
|
||||
@@ -60,7 +60,7 @@ int buf_get_rsa_pub_key(buffer* buf, rsa_key *key) {
|
||||
|
||||
if (buf_getmpint(buf, key->e) == DROPBEAR_FAILURE
|
||||
|| buf_getmpint(buf, key->n) == DROPBEAR_FAILURE) {
|
||||
TRACE(("leave buf_get_rsa_pub_key: failure"));
|
||||
TRACE(("leave buf_get_rsa_pub_key: failure"))
|
||||
return DROPBEAR_FAILURE;
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ int buf_get_rsa_pub_key(buffer* buf, rsa_key *key) {
|
||||
return DROPBEAR_FAILURE;
|
||||
}
|
||||
|
||||
TRACE(("leave buf_get_rsa_pub_key: success"));
|
||||
TRACE(("leave buf_get_rsa_pub_key: success"))
|
||||
return DROPBEAR_SUCCESS;
|
||||
|
||||
}
|
||||
@@ -81,17 +81,17 @@ int buf_get_rsa_priv_key(buffer* buf, rsa_key *key) {
|
||||
|
||||
assert(key != NULL);
|
||||
|
||||
TRACE(("enter buf_get_rsa_priv_key"));
|
||||
TRACE(("enter buf_get_rsa_priv_key"))
|
||||
|
||||
if (buf_get_rsa_pub_key(buf, key) == DROPBEAR_FAILURE) {
|
||||
TRACE(("leave buf_get_rsa_priv_key: pub: ret == DROPBEAR_FAILURE"));
|
||||
TRACE(("leave buf_get_rsa_priv_key: pub: ret == DROPBEAR_FAILURE"))
|
||||
return DROPBEAR_FAILURE;
|
||||
}
|
||||
|
||||
key->d = m_malloc(sizeof(mp_int));
|
||||
m_mp_init(key->d);
|
||||
if (buf_getmpint(buf, key->d) == DROPBEAR_FAILURE) {
|
||||
TRACE(("leave buf_get_rsa_priv_key: d: ret == DROPBEAR_FAILURE"));
|
||||
TRACE(("leave buf_get_rsa_priv_key: d: ret == DROPBEAR_FAILURE"))
|
||||
return DROPBEAR_FAILURE;
|
||||
}
|
||||
|
||||
@@ -105,17 +105,17 @@ int buf_get_rsa_priv_key(buffer* buf, rsa_key *key) {
|
||||
m_mp_init_multi(key->p, key->q, NULL);
|
||||
|
||||
if (buf_getmpint(buf, key->p) == DROPBEAR_FAILURE) {
|
||||
TRACE(("leave buf_get_rsa_priv_key: p: ret == DROPBEAR_FAILURE"));
|
||||
TRACE(("leave buf_get_rsa_priv_key: p: ret == DROPBEAR_FAILURE"))
|
||||
return DROPBEAR_FAILURE;
|
||||
}
|
||||
|
||||
if (buf_getmpint(buf, key->q) == DROPBEAR_FAILURE) {
|
||||
TRACE(("leave buf_get_rsa_priv_key: q: ret == DROPBEAR_FAILURE"));
|
||||
TRACE(("leave buf_get_rsa_priv_key: q: ret == DROPBEAR_FAILURE"))
|
||||
return DROPBEAR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
TRACE(("leave buf_get_rsa_priv_key"));
|
||||
TRACE(("leave buf_get_rsa_priv_key"))
|
||||
return DROPBEAR_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -123,10 +123,10 @@ int buf_get_rsa_priv_key(buffer* buf, rsa_key *key) {
|
||||
/* Clear and free the memory used by a public or private key */
|
||||
void rsa_key_free(rsa_key *key) {
|
||||
|
||||
TRACE(("enter rsa_key_free"));
|
||||
TRACE(("enter rsa_key_free"))
|
||||
|
||||
if (key == NULL) {
|
||||
TRACE(("leave rsa_key_free: key == NULL"));
|
||||
TRACE(("leave rsa_key_free: key == NULL"))
|
||||
return;
|
||||
}
|
||||
if (key->d) {
|
||||
@@ -150,7 +150,7 @@ void rsa_key_free(rsa_key *key) {
|
||||
m_free(key->q);
|
||||
}
|
||||
m_free(key);
|
||||
TRACE(("leave rsa_key_free"));
|
||||
TRACE(("leave rsa_key_free"))
|
||||
}
|
||||
|
||||
/* Put the public rsa key into the buffer in the required format:
|
||||
@@ -161,21 +161,21 @@ void rsa_key_free(rsa_key *key) {
|
||||
*/
|
||||
void buf_put_rsa_pub_key(buffer* buf, rsa_key *key) {
|
||||
|
||||
TRACE(("enter buf_put_rsa_pub_key"));
|
||||
TRACE(("enter buf_put_rsa_pub_key"))
|
||||
assert(key != NULL);
|
||||
|
||||
buf_putstring(buf, SSH_SIGNKEY_RSA, SSH_SIGNKEY_RSA_LEN);
|
||||
buf_putmpint(buf, key->e);
|
||||
buf_putmpint(buf, key->n);
|
||||
|
||||
TRACE(("leave buf_put_rsa_pub_key"));
|
||||
TRACE(("leave buf_put_rsa_pub_key"))
|
||||
|
||||
}
|
||||
|
||||
/* Same as buf_put_rsa_pub_key, but with the private "x" key appended */
|
||||
void buf_put_rsa_priv_key(buffer* buf, rsa_key *key) {
|
||||
|
||||
TRACE(("enter buf_put_rsa_priv_key"));
|
||||
TRACE(("enter buf_put_rsa_priv_key"))
|
||||
|
||||
assert(key != NULL);
|
||||
buf_put_rsa_pub_key(buf, key);
|
||||
@@ -190,7 +190,7 @@ void buf_put_rsa_priv_key(buffer* buf, rsa_key *key) {
|
||||
}
|
||||
|
||||
|
||||
TRACE(("leave buf_put_rsa_priv_key"));
|
||||
TRACE(("leave buf_put_rsa_priv_key"))
|
||||
|
||||
}
|
||||
|
||||
@@ -201,11 +201,12 @@ int buf_rsa_verify(buffer * buf, rsa_key *key, const unsigned char* data,
|
||||
unsigned int len) {
|
||||
|
||||
unsigned int slen;
|
||||
mp_int rsa_s, rsa_mdash;
|
||||
DEF_MP_INT(rsa_s);
|
||||
DEF_MP_INT(rsa_mdash);
|
||||
mp_int *rsa_em = NULL;
|
||||
int ret = DROPBEAR_FAILURE;
|
||||
|
||||
TRACE(("enter buf_rsa_verify"));
|
||||
TRACE(("enter buf_rsa_verify"))
|
||||
|
||||
assert(key != NULL);
|
||||
|
||||
@@ -213,19 +214,19 @@ int buf_rsa_verify(buffer * buf, rsa_key *key, const unsigned char* data,
|
||||
|
||||
slen = buf_getint(buf);
|
||||
if (slen != (unsigned int)mp_unsigned_bin_size(key->n)) {
|
||||
TRACE(("bad size"));
|
||||
TRACE(("bad size"))
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (mp_read_unsigned_bin(&rsa_s, buf_getptr(buf, buf->len - buf->pos),
|
||||
buf->len - buf->pos) != MP_OKAY) {
|
||||
TRACE(("failed reading rsa_s"));
|
||||
TRACE(("failed reading rsa_s"))
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* check that s <= n-1 */
|
||||
if (mp_cmp(&rsa_s, key->n) != MP_LT) {
|
||||
TRACE(("s > n-1"));
|
||||
TRACE(("s > n-1"))
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -233,13 +234,13 @@ int buf_rsa_verify(buffer * buf, rsa_key *key, const unsigned char* data,
|
||||
rsa_em = rsa_pad_em(key, data, len);
|
||||
|
||||
if (mp_exptmod(&rsa_s, key->e, key->n, &rsa_mdash) != MP_OKAY) {
|
||||
TRACE(("failed exptmod rsa_s"));
|
||||
TRACE(("failed exptmod rsa_s"))
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (mp_cmp(rsa_em, &rsa_mdash) == MP_EQ) {
|
||||
/* signature is valid */
|
||||
TRACE(("success!"));
|
||||
TRACE(("success!"))
|
||||
ret = DROPBEAR_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -249,7 +250,7 @@ out:
|
||||
m_free(rsa_em);
|
||||
}
|
||||
mp_clear_multi(&rsa_mdash, &rsa_s, NULL);
|
||||
TRACE(("leave buf_rsa_verify: ret %d", ret));
|
||||
TRACE(("leave buf_rsa_verify: ret %d", ret))
|
||||
return ret;
|
||||
|
||||
}
|
||||
@@ -262,10 +263,10 @@ void buf_put_rsa_sign(buffer* buf, rsa_key *key, const unsigned char* data,
|
||||
|
||||
unsigned int nsize, ssize;
|
||||
unsigned int i;
|
||||
mp_int rsa_s;
|
||||
DEF_MP_INT(rsa_s);
|
||||
mp_int *rsa_em = NULL;
|
||||
|
||||
TRACE(("enter buf_put_rsa_sign"));
|
||||
TRACE(("enter buf_put_rsa_sign"))
|
||||
assert(key != NULL);
|
||||
|
||||
rsa_em = rsa_pad_em(key, data, len);
|
||||
@@ -305,7 +306,7 @@ void buf_put_rsa_sign(buffer* buf, rsa_key *key, const unsigned char* data,
|
||||
#endif
|
||||
|
||||
|
||||
TRACE(("leave buf_put_rsa_sign"));
|
||||
TRACE(("leave buf_put_rsa_sign"))
|
||||
}
|
||||
|
||||
/* Creates the message value as expected by PKCS, see rfc2437 etc */
|
||||
|
||||
@@ -52,7 +52,7 @@ typedef struct svr_runopts {
|
||||
int usingsyslog;
|
||||
|
||||
/* ports is an array of the portcount listening ports */
|
||||
uint16_t *ports;
|
||||
char *ports[DROPBEAR_MAX_PORTS];
|
||||
unsigned int portcount;
|
||||
|
||||
int inetdmode;
|
||||
@@ -81,6 +81,7 @@ typedef struct svr_runopts {
|
||||
extern svr_runopts svr_opts;
|
||||
|
||||
void svr_getopts(int argc, char ** argv);
|
||||
void loadhostkeys();
|
||||
|
||||
/* Uncompleted XXX matt */
|
||||
typedef struct cli_runopts {
|
||||
|
||||
6
scp.c
6
scp.c
@@ -178,8 +178,10 @@ do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, int argc)
|
||||
close(pout[1]);
|
||||
|
||||
args.list[0] = ssh_program;
|
||||
if (remuser != NULL)
|
||||
addargs(&args, "-l%s", remuser);
|
||||
if (remuser != NULL) {
|
||||
addargs(&args, "-l");
|
||||
addargs(&args, "%s", remuser);
|
||||
}
|
||||
addargs(&args, "%s", host);
|
||||
addargs(&args, "%s", cmd);
|
||||
|
||||
|
||||
20
session.h
20
session.h
@@ -36,6 +36,7 @@
|
||||
#include "listener.h"
|
||||
#include "packet.h"
|
||||
#include "tcpfwd.h"
|
||||
#include "chansession.h"
|
||||
|
||||
extern int sessinitdone; /* Is set to 0 somewhere */
|
||||
extern int exitflag;
|
||||
@@ -43,19 +44,16 @@ extern int exitflag;
|
||||
void common_session_init(int sock, char* remotehost);
|
||||
void session_loop(void(*loophandler)());
|
||||
void common_session_cleanup();
|
||||
void checktimeouts();
|
||||
void session_identification();
|
||||
|
||||
|
||||
/* Server */
|
||||
void svr_session(int sock, int childpipe, char *remotehost);
|
||||
void svr_session(int sock, int childpipe, char *remotehost, char *addrstring);
|
||||
void svr_dropbear_exit(int exitcode, const char* format, va_list param);
|
||||
void svr_dropbear_log(int priority, const char* format, va_list param);
|
||||
|
||||
/* Client */
|
||||
void cli_session(int sock, char *remotehost);
|
||||
void cli_dropbear_exit(int exitcode, const char* format, va_list param);
|
||||
void cli_dropbear_log(int priority, const char* format, va_list param);
|
||||
void cli_session_cleanup();
|
||||
void cleantext(unsigned char* dirtytext);
|
||||
|
||||
@@ -160,12 +158,11 @@ struct sshsession {
|
||||
|
||||
|
||||
/* TCP forwarding - where manage listeners */
|
||||
#ifdef USING_LISTENERS
|
||||
struct Listener ** listeners;
|
||||
unsigned int listensize;
|
||||
|
||||
/* Whether to allow binding to privileged ports (<1024). This doesn't
|
||||
* really belong here, but nowhere else fits nicely */
|
||||
#endif
|
||||
int allowprivport;
|
||||
|
||||
};
|
||||
@@ -179,6 +176,13 @@ struct serversession {
|
||||
struct ChildPid * childpids; /* array of mappings childpid<->channel */
|
||||
unsigned int childpidsize;
|
||||
|
||||
/* Used to avoid a race in the exit returncode handling - see
|
||||
* svr-chansession.c for details */
|
||||
struct exitinfo lastexit;
|
||||
|
||||
/* The numeric address they connected from, used for logging */
|
||||
char * addrstring;
|
||||
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
@@ -212,12 +216,16 @@ struct clientsession {
|
||||
|
||||
int tty_raw_mode; /* Whether we're in raw mode (and have to clean up) */
|
||||
struct termios saved_tio;
|
||||
int stdincopy;
|
||||
int stdinflags;
|
||||
|
||||
int winchange; /* Set to 1 when a windowchange signal happens */
|
||||
|
||||
int lastauthtype; /* either AUTH_TYPE_PUBKEY or AUTH_TYPE_PASSWORD,
|
||||
for the last type of auth we tried */
|
||||
struct PubkeyList *lastpubkey;
|
||||
|
||||
int retval; /* What the command exit status was - we emulate it */
|
||||
#if 0
|
||||
TODO
|
||||
struct AgentkeyList *agentkeys; /* Keys to use for public-key auth */
|
||||
|
||||
35
signkey.c
35
signkey.c
@@ -94,7 +94,7 @@ int buf_get_pub_key(buffer *buf, sign_key *key, int *type) {
|
||||
int keytype;
|
||||
int ret = DROPBEAR_FAILURE;
|
||||
|
||||
TRACE(("enter buf_get_pub_key"));
|
||||
TRACE(("enter buf_get_pub_key"))
|
||||
|
||||
ident = buf_getstring(buf, &len);
|
||||
keytype = signkey_type_from_name(ident, len);
|
||||
@@ -130,7 +130,7 @@ int buf_get_pub_key(buffer *buf, sign_key *key, int *type) {
|
||||
}
|
||||
#endif
|
||||
|
||||
TRACE(("leave buf_get_pub_key"));
|
||||
TRACE(("leave buf_get_pub_key"))
|
||||
|
||||
return ret;
|
||||
|
||||
@@ -146,13 +146,14 @@ int buf_get_priv_key(buffer *buf, sign_key *key, int *type) {
|
||||
int keytype;
|
||||
int ret = DROPBEAR_FAILURE;
|
||||
|
||||
TRACE(("enter buf_get_priv_key"));
|
||||
TRACE(("enter buf_get_priv_key"))
|
||||
|
||||
ident = buf_getstring(buf, &len);
|
||||
keytype = signkey_type_from_name(ident, len);
|
||||
m_free(ident);
|
||||
|
||||
if (*type != DROPBEAR_SIGNKEY_ANY && *type != keytype) {
|
||||
TRACE(("wrong key type: %d %d", *type, keytype))
|
||||
return DROPBEAR_FAILURE;
|
||||
}
|
||||
|
||||
@@ -182,7 +183,7 @@ int buf_get_priv_key(buffer *buf, sign_key *key, int *type) {
|
||||
}
|
||||
#endif
|
||||
|
||||
TRACE(("leave buf_get_priv_key"));
|
||||
TRACE(("leave buf_get_priv_key"))
|
||||
|
||||
return ret;
|
||||
|
||||
@@ -193,7 +194,7 @@ void buf_put_pub_key(buffer* buf, sign_key *key, int type) {
|
||||
|
||||
buffer *pubkeys;
|
||||
|
||||
TRACE(("enter buf_put_pub_key"));
|
||||
TRACE(("enter buf_put_pub_key"))
|
||||
pubkeys = buf_new(MAX_PUBKEY_SIZE);
|
||||
|
||||
#ifdef DROPBEAR_DSS
|
||||
@@ -215,26 +216,26 @@ void buf_put_pub_key(buffer* buf, sign_key *key, int type) {
|
||||
pubkeys->len);
|
||||
|
||||
buf_free(pubkeys);
|
||||
TRACE(("leave buf_put_pub_key"));
|
||||
TRACE(("leave buf_put_pub_key"))
|
||||
}
|
||||
|
||||
/* type is either DROPBEAR_SIGNKEY_DSS or DROPBEAR_SIGNKEY_RSA */
|
||||
void buf_put_priv_key(buffer* buf, sign_key *key, int type) {
|
||||
|
||||
TRACE(("enter buf_put_priv_key"));
|
||||
TRACE(("type is %d", type));
|
||||
TRACE(("enter buf_put_priv_key"))
|
||||
TRACE(("type is %d", type))
|
||||
|
||||
#ifdef DROPBEAR_DSS
|
||||
if (type == DROPBEAR_SIGNKEY_DSS) {
|
||||
buf_put_dss_priv_key(buf, key->dsskey);
|
||||
TRACE(("leave buf_put_priv_key: dss done"));
|
||||
TRACE(("leave buf_put_priv_key: dss done"))
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#ifdef DROPBEAR_RSA
|
||||
if (type == DROPBEAR_SIGNKEY_RSA) {
|
||||
buf_put_rsa_priv_key(buf, key->rsakey);
|
||||
TRACE(("leave buf_put_priv_key: rsa done"));
|
||||
TRACE(("leave buf_put_priv_key: rsa done"))
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
@@ -243,7 +244,7 @@ void buf_put_priv_key(buffer* buf, sign_key *key, int type) {
|
||||
|
||||
void sign_key_free(sign_key *key) {
|
||||
|
||||
TRACE(("enter sign_key_free"));
|
||||
TRACE(("enter sign_key_free"))
|
||||
|
||||
#ifdef DROPBEAR_DSS
|
||||
dss_key_free(key->dsskey);
|
||||
@@ -255,7 +256,7 @@ void sign_key_free(sign_key *key) {
|
||||
#endif
|
||||
|
||||
m_free(key);
|
||||
TRACE(("leave sign_key_free"));
|
||||
TRACE(("leave sign_key_free"))
|
||||
}
|
||||
|
||||
static char hexdig(unsigned char x) {
|
||||
@@ -392,7 +393,7 @@ int buf_verify(buffer * buf, sign_key *key, const unsigned char *data,
|
||||
unsigned char * ident = NULL;
|
||||
unsigned int identlen = 0;
|
||||
|
||||
TRACE(("enter buf_verify"));
|
||||
TRACE(("enter buf_verify"))
|
||||
|
||||
bloblen = buf_getint(buf);
|
||||
ident = buf_getstring(buf, &identlen);
|
||||
@@ -441,17 +442,17 @@ int cmp_base64_key(const unsigned char* keyblob, unsigned int keybloblen,
|
||||
if (base64_decode(buf_getptr(line, len), len,
|
||||
buf_getwriteptr(decodekey, decodekey->size),
|
||||
&decodekeylen) != CRYPT_OK) {
|
||||
TRACE(("checkpubkey: base64 decode failed"));
|
||||
TRACE(("checkpubkey: base64 decode failed"))
|
||||
goto out;
|
||||
}
|
||||
TRACE(("checkpubkey: base64_decode success"));
|
||||
TRACE(("checkpubkey: base64_decode success"))
|
||||
buf_incrlen(decodekey, decodekeylen);
|
||||
|
||||
/* compare the keys */
|
||||
if ( ( decodekeylen != keybloblen )
|
||||
|| memcmp( buf_getptr(decodekey, decodekey->len),
|
||||
keyblob, decodekey->len) != 0) {
|
||||
TRACE(("checkpubkey: compare failed"));
|
||||
TRACE(("checkpubkey: compare failed"))
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -460,7 +461,7 @@ int cmp_base64_key(const unsigned char* keyblob, unsigned int keybloblen,
|
||||
filealgolen = buf_getint(decodekey);
|
||||
filealgo = buf_getptr(decodekey, filealgolen);
|
||||
if (filealgolen != algolen || memcmp(filealgo, algoname, algolen) != 0) {
|
||||
TRACE(("checkpubkey: algo match failed"));
|
||||
TRACE(("checkpubkey: algo match failed"))
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
||||
@@ -73,9 +73,7 @@ int agentreq(struct ChanSess * chansess) {
|
||||
}
|
||||
|
||||
/* set non-blocking */
|
||||
if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
|
||||
goto fail;
|
||||
}
|
||||
setnonblocking(fd);
|
||||
|
||||
/* pass if off to listener */
|
||||
chansess->agentlistener = new_listener( &fd, 1, 0, chansess,
|
||||
@@ -97,13 +95,13 @@ fail:
|
||||
/* accepts a connection on the forwarded socket and opens a new channel for it
|
||||
* back to the client */
|
||||
/* returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
|
||||
static void agentaccept(struct Listener * listener, int sock) {
|
||||
static void agentaccept(struct Listener *UNUSED(listener), int sock) {
|
||||
|
||||
int fd;
|
||||
|
||||
fd = accept(sock, NULL, NULL);
|
||||
if (fd < 0) {
|
||||
TRACE(("accept failed"));
|
||||
TRACE(("accept failed"))
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ algo_type * svr_buf_match_algo(buffer* buf, algo_type localalgos[],
|
||||
/* get the comma-separated list from the buffer ie "algo1,algo2,algo3" */
|
||||
algolist = buf_getstring(buf, &len);
|
||||
/* Debug this */
|
||||
TRACE(("buf_match_algo: %s", algolist));
|
||||
TRACE(("buf_match_algo: %s", algolist))
|
||||
if (len > MAX_PROPOSED_ALGO*(MAX_NAME_LEN+1)) {
|
||||
goto out; /* just a sanity check, no other use */
|
||||
}
|
||||
|
||||
70
svr-auth.c
70
svr-auth.c
@@ -55,7 +55,7 @@ static void authclear() {
|
||||
#ifdef ENABLE_SVR_PUBKEY_AUTH
|
||||
ses.authstate.authtypes |= AUTH_TYPE_PUBKEY;
|
||||
#endif
|
||||
#ifdef ENABLE_SVR_PASSWORD_AUTH
|
||||
#if defined(ENABLE_SVR_PASSWORD_AUTH) || defined(ENABLE_SVR_PAM_AUTH)
|
||||
if (!svr_opts.noauthpass) {
|
||||
ses.authstate.authtypes |= AUTH_TYPE_PASSWORD;
|
||||
}
|
||||
@@ -67,9 +67,9 @@ static void authclear() {
|
||||
* ignore this, but possibly serves as a legal "no trespassing" sign */
|
||||
static void send_msg_userauth_banner() {
|
||||
|
||||
TRACE(("enter send_msg_userauth_banner"));
|
||||
TRACE(("enter send_msg_userauth_banner"))
|
||||
if (svr_opts.banner == NULL) {
|
||||
TRACE(("leave send_msg_userauth_banner: banner is NULL"));
|
||||
TRACE(("leave send_msg_userauth_banner: banner is NULL"))
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ static void send_msg_userauth_banner() {
|
||||
buf_free(svr_opts.banner);
|
||||
svr_opts.banner = NULL;
|
||||
|
||||
TRACE(("leave send_msg_userauth_banner"));
|
||||
TRACE(("leave send_msg_userauth_banner"))
|
||||
}
|
||||
|
||||
/* handle a userauth request, check validity, pass to password or pubkey
|
||||
@@ -94,11 +94,11 @@ void recv_msg_userauth_request() {
|
||||
unsigned char *username = NULL, *servicename = NULL, *methodname = NULL;
|
||||
unsigned int userlen, servicelen, methodlen;
|
||||
|
||||
TRACE(("enter recv_msg_userauth_request"));
|
||||
TRACE(("enter recv_msg_userauth_request"))
|
||||
|
||||
/* ignore packets if auth is already done */
|
||||
if (ses.authstate.authdone == 1) {
|
||||
TRACE(("leave recv_msg_userauth_request: authdone already"));
|
||||
TRACE(("leave recv_msg_userauth_request: authdone already"))
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ void recv_msg_userauth_request() {
|
||||
if (methodlen == AUTH_METHOD_NONE_LEN &&
|
||||
strncmp(methodname, AUTH_METHOD_NONE,
|
||||
AUTH_METHOD_NONE_LEN) == 0) {
|
||||
TRACE(("recv_msg_userauth_request: 'none' request"));
|
||||
TRACE(("recv_msg_userauth_request: 'none' request"))
|
||||
send_msg_userauth_failure(0, 0);
|
||||
goto out;
|
||||
}
|
||||
@@ -136,7 +136,7 @@ void recv_msg_userauth_request() {
|
||||
/* check username is good before continuing */
|
||||
if (checkusername(username, userlen) == DROPBEAR_FAILURE) {
|
||||
/* username is invalid/no shell/etc - send failure */
|
||||
TRACE(("sending checkusername failure"));
|
||||
TRACE(("sending checkusername failure"))
|
||||
send_msg_userauth_failure(0, 1);
|
||||
goto out;
|
||||
}
|
||||
@@ -154,6 +154,19 @@ void recv_msg_userauth_request() {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_SVR_PAM_AUTH
|
||||
if (!svr_opts.noauthpass &&
|
||||
!(svr_opts.norootpass && ses.authstate.pw->pw_uid == 0) ) {
|
||||
/* user wants to try password auth */
|
||||
if (methodlen == AUTH_METHOD_PASSWORD_LEN &&
|
||||
strncmp(methodname, AUTH_METHOD_PASSWORD,
|
||||
AUTH_METHOD_PASSWORD_LEN) == 0) {
|
||||
svr_auth_pam();
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_SVR_PUBKEY_AUTH
|
||||
/* user wants to try pubkey auth */
|
||||
if (methodlen == AUTH_METHOD_PUBKEY_LEN &&
|
||||
@@ -182,7 +195,7 @@ static int checkusername(unsigned char *username, unsigned int userlen) {
|
||||
char* listshell = NULL;
|
||||
char* usershell = NULL;
|
||||
|
||||
TRACE(("enter checkusername"));
|
||||
TRACE(("enter checkusername"))
|
||||
if (userlen > MAX_USERNAME_LEN) {
|
||||
return DROPBEAR_FAILURE;
|
||||
}
|
||||
@@ -192,7 +205,8 @@ static int checkusername(unsigned char *username, unsigned int userlen) {
|
||||
strcmp(username, ses.authstate.username) != 0) {
|
||||
/* the username needs resetting */
|
||||
if (ses.authstate.username != NULL) {
|
||||
dropbear_log(LOG_WARNING, "client trying multiple usernames");
|
||||
dropbear_log(LOG_WARNING, "client trying multiple usernames from %s",
|
||||
svr_ses.addrstring);
|
||||
m_free(ses.authstate.username);
|
||||
}
|
||||
authclear();
|
||||
@@ -203,9 +217,10 @@ static int checkusername(unsigned char *username, unsigned int userlen) {
|
||||
|
||||
/* check that user exists */
|
||||
if (ses.authstate.pw == NULL) {
|
||||
TRACE(("leave checkusername: user '%s' doesn't exist", username));
|
||||
TRACE(("leave checkusername: user '%s' doesn't exist", username))
|
||||
dropbear_log(LOG_WARNING,
|
||||
"login attempt for nonexistent user");
|
||||
"login attempt for nonexistent user from %s",
|
||||
svr_ses.addrstring);
|
||||
send_msg_userauth_failure(0, 1);
|
||||
return DROPBEAR_FAILURE;
|
||||
}
|
||||
@@ -215,7 +230,7 @@ static int checkusername(unsigned char *username, unsigned int userlen) {
|
||||
|
||||
/* check for non-root if desired */
|
||||
if (svr_opts.norootlogin && ses.authstate.pw->pw_uid == 0) {
|
||||
TRACE(("leave checkusername: root login disabled"));
|
||||
TRACE(("leave checkusername: root login disabled"))
|
||||
dropbear_log(LOG_WARNING, "root login rejected");
|
||||
send_msg_userauth_failure(0, 1);
|
||||
return DROPBEAR_FAILURE;
|
||||
@@ -223,14 +238,14 @@ static int checkusername(unsigned char *username, unsigned int userlen) {
|
||||
|
||||
/* check for an empty password */
|
||||
if (ses.authstate.pw->pw_passwd[0] == '\0') {
|
||||
TRACE(("leave checkusername: empty pword"));
|
||||
TRACE(("leave checkusername: empty pword"))
|
||||
dropbear_log(LOG_WARNING, "user '%s' has blank password, rejected",
|
||||
ses.authstate.printableuser);
|
||||
send_msg_userauth_failure(0, 1);
|
||||
return DROPBEAR_FAILURE;
|
||||
}
|
||||
|
||||
TRACE(("shell is %s", ses.authstate.pw->pw_shell));
|
||||
TRACE(("shell is %s", ses.authstate.pw->pw_shell))
|
||||
|
||||
/* check that the shell is set */
|
||||
usershell = ses.authstate.pw->pw_shell;
|
||||
@@ -244,7 +259,7 @@ static int checkusername(unsigned char *username, unsigned int userlen) {
|
||||
* is platform-specific) */
|
||||
setusershell();
|
||||
while ((listshell = getusershell()) != NULL) {
|
||||
TRACE(("test shell is '%s'", listshell));
|
||||
TRACE(("test shell is '%s'", listshell))
|
||||
if (strcmp(listshell, usershell) == 0) {
|
||||
/* have a match */
|
||||
goto goodshell;
|
||||
@@ -252,7 +267,7 @@ static int checkusername(unsigned char *username, unsigned int userlen) {
|
||||
}
|
||||
/* no matching shell */
|
||||
endusershell();
|
||||
TRACE(("no matching shell"));
|
||||
TRACE(("no matching shell"))
|
||||
dropbear_log(LOG_WARNING, "user '%s' has invalid shell, rejected",
|
||||
ses.authstate.printableuser);
|
||||
send_msg_userauth_failure(0, 1);
|
||||
@@ -260,10 +275,10 @@ static int checkusername(unsigned char *username, unsigned int userlen) {
|
||||
|
||||
goodshell:
|
||||
endusershell();
|
||||
TRACE(("matching shell"));
|
||||
TRACE(("matching shell"))
|
||||
|
||||
TRACE(("uid = %d", ses.authstate.pw->pw_uid));
|
||||
TRACE(("leave checkusername"));
|
||||
TRACE(("uid = %d", ses.authstate.pw->pw_uid))
|
||||
TRACE(("leave checkusername"))
|
||||
return DROPBEAR_SUCCESS;
|
||||
|
||||
}
|
||||
@@ -277,7 +292,7 @@ void send_msg_userauth_failure(int partial, int incrfail) {
|
||||
|
||||
buffer *typebuf = NULL;
|
||||
|
||||
TRACE(("enter send_msg_userauth_failure"));
|
||||
TRACE(("enter send_msg_userauth_failure"))
|
||||
|
||||
CHECKCLEARTOWRITE();
|
||||
|
||||
@@ -316,23 +331,24 @@ void send_msg_userauth_failure(int partial, int incrfail) {
|
||||
if (ses.authstate.failcount >= MAX_AUTH_TRIES) {
|
||||
char * userstr;
|
||||
/* XXX - send disconnect ? */
|
||||
TRACE(("Max auth tries reached, exiting"));
|
||||
TRACE(("Max auth tries reached, exiting"))
|
||||
|
||||
if (ses.authstate.printableuser == NULL) {
|
||||
userstr = "is invalid";
|
||||
} else {
|
||||
userstr = ses.authstate.printableuser;
|
||||
}
|
||||
dropbear_exit("Max auth tries reached - user %s", userstr);
|
||||
dropbear_exit("Max auth tries reached - user '%s' from %s",
|
||||
userstr, svr_ses.addrstring);
|
||||
}
|
||||
|
||||
TRACE(("leave send_msg_userauth_failure"));
|
||||
TRACE(("leave send_msg_userauth_failure"))
|
||||
}
|
||||
|
||||
/* Send a success message to the user, and set the "authdone" flag */
|
||||
void send_msg_userauth_success() {
|
||||
|
||||
TRACE(("enter send_msg_userauth_success"));
|
||||
TRACE(("enter send_msg_userauth_success"))
|
||||
|
||||
CHECKCLEARTOWRITE();
|
||||
|
||||
@@ -340,6 +356,8 @@ void send_msg_userauth_success() {
|
||||
encrypt_packet();
|
||||
|
||||
ses.authstate.authdone = 1;
|
||||
ses.connecttimeout = 0;
|
||||
|
||||
|
||||
if (ses.authstate.pw->pw_uid == 0) {
|
||||
ses.allowprivport = 1;
|
||||
@@ -350,6 +368,6 @@ void send_msg_userauth_success() {
|
||||
* logins - a nasty situation. */
|
||||
m_close(svr_ses.childpipe);
|
||||
|
||||
TRACE(("leave send_msg_userauth_success"));
|
||||
TRACE(("leave send_msg_userauth_success"))
|
||||
|
||||
}
|
||||
|
||||
230
svr-authpam.c
Normal file
230
svr-authpam.c
Normal file
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
* Dropbear SSH
|
||||
*
|
||||
* Copyright (c) 2004 Martin Carlsson
|
||||
* Portions (c) 2004 Matt Johnston
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE. */
|
||||
|
||||
/* Validates a user password using PAM */
|
||||
|
||||
#include "includes.h"
|
||||
#include "session.h"
|
||||
#include "buffer.h"
|
||||
#include "dbutil.h"
|
||||
#include "auth.h"
|
||||
|
||||
#if defined(HAVE_SECURITY_PAM_APPL_H)
|
||||
#include <security/pam_appl.h>
|
||||
#elif defined (HAVE_PAM_PAM_APPL_H)
|
||||
#include <pam/pam_appl.h>
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_SVR_PAM_AUTH
|
||||
|
||||
struct UserDataS {
|
||||
char* user;
|
||||
char* passwd;
|
||||
};
|
||||
|
||||
/* PAM conversation function - for now we only handle one message */
|
||||
int
|
||||
pamConvFunc(int num_msg,
|
||||
const struct pam_message **msg,
|
||||
struct pam_response **respp,
|
||||
void *appdata_ptr) {
|
||||
|
||||
int rc = PAM_SUCCESS;
|
||||
struct pam_response* resp = NULL;
|
||||
struct UserDataS* userDatap = (struct UserDataS*) appdata_ptr;
|
||||
|
||||
const char* message = (*msg)->msg;
|
||||
|
||||
TRACE(("enter pamConvFunc"))
|
||||
|
||||
if (num_msg != 1) {
|
||||
/* If you're getting here - Dropbear probably can't support your pam
|
||||
* modules. This whole file is a bit of a hack around lack of
|
||||
* asynchronocity in PAM anyway */
|
||||
dropbear_log(LOG_INFO, "pamConvFunc() called with >1 messages: not supported.");
|
||||
return PAM_CONV_ERR;
|
||||
}
|
||||
|
||||
TRACE(("msg_style is %d", (*msg)->msg_style))
|
||||
if (message) {
|
||||
TRACE(("message is '%s'", message))
|
||||
} else {
|
||||
TRACE(("null message"))
|
||||
}
|
||||
|
||||
switch((*msg)->msg_style) {
|
||||
|
||||
case PAM_PROMPT_ECHO_OFF:
|
||||
|
||||
if (strcmp(message, "Password:") != 0) {
|
||||
TRACE(("PAM_PROMPT_ECHO_OFF: unrecognized prompt"))
|
||||
rc = PAM_CONV_ERR;
|
||||
break;
|
||||
}
|
||||
|
||||
/* You have to read the PAM module-writers' docs (do we look like
|
||||
* module writers? no.) to find out that the module will
|
||||
* free the pam_response and its resp element - ie we _must_ malloc
|
||||
* it here */
|
||||
resp = (struct pam_response*) m_malloc(sizeof(struct pam_response));
|
||||
memset(resp, 0, sizeof(struct pam_response));
|
||||
|
||||
resp->resp = m_strdup(userDatap->passwd);
|
||||
m_burn(userDatap->passwd, strlen(userDatap->passwd));
|
||||
(*respp) = resp;
|
||||
break;
|
||||
|
||||
|
||||
case PAM_PROMPT_ECHO_ON:
|
||||
|
||||
if ((strcmp(message, "login: " ) != 0)
|
||||
&& (strcmp(message, "login:" ) != 0)
|
||||
&& (strcmp(message, "Please enter username: " ) != 0)) {
|
||||
TRACE(("PAM_PROMPT_ECHO_ON: unrecognized prompt"))
|
||||
rc = PAM_CONV_ERR;
|
||||
break;
|
||||
}
|
||||
|
||||
/* You have to read the PAM module-writers' docs (do we look like
|
||||
* module writers? no.) to find out that the module will
|
||||
* free the pam_response and its resp element - ie we _must_ malloc
|
||||
* it here */
|
||||
resp = (struct pam_response*) m_malloc(sizeof(struct pam_response));
|
||||
memset(resp, 0, sizeof(struct pam_response));
|
||||
|
||||
resp->resp = m_strdup(userDatap->user);
|
||||
TRACE(("userDatap->user='%s'", userDatap->user))
|
||||
(*respp) = resp;
|
||||
break;
|
||||
|
||||
default:
|
||||
TRACE(("Unknown message type"))
|
||||
rc = PAM_CONV_ERR;
|
||||
break;
|
||||
}
|
||||
|
||||
TRACE(("leave pamConvFunc, rc %d", rc))
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Process a password auth request, sending success or failure messages as
|
||||
* appropriate. To the client it looks like it's doing normal password auth (as
|
||||
* opposed to keyboard-interactive or something), so the pam module has to be
|
||||
* fairly standard (ie just "what's your username, what's your password, OK").
|
||||
*
|
||||
* Keyboard interactive would be a lot nicer, but since PAM is synchronous, it
|
||||
* gets very messy trying to send the interactive challenges, and read the
|
||||
* interactive responses, over the network. */
|
||||
void svr_auth_pam() {
|
||||
|
||||
struct UserDataS userData = {NULL, NULL};
|
||||
struct pam_conv pamConv = {
|
||||
pamConvFunc,
|
||||
&userData /* submitted to pamvConvFunc as appdata_ptr */
|
||||
};
|
||||
|
||||
pam_handle_t* pamHandlep = NULL;
|
||||
|
||||
unsigned char * password = NULL;
|
||||
unsigned int passwordlen;
|
||||
|
||||
int rc = PAM_SUCCESS;
|
||||
unsigned char changepw;
|
||||
|
||||
/* check if client wants to change password */
|
||||
changepw = buf_getbyte(ses.payload);
|
||||
if (changepw) {
|
||||
/* not implemented by this server */
|
||||
send_msg_userauth_failure(0, 1);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
password = buf_getstring(ses.payload, &passwordlen);
|
||||
|
||||
/* used to pass data to the PAM conversation function - don't bother with
|
||||
* strdup() etc since these are touched only by our own conversation
|
||||
* function (above) which takes care of it */
|
||||
userData.user = ses.authstate.printableuser;
|
||||
userData.passwd = password;
|
||||
|
||||
/* Init pam */
|
||||
if ((rc = pam_start("sshd", NULL, &pamConv, &pamHandlep)) != PAM_SUCCESS) {
|
||||
dropbear_log(LOG_WARNING, "pam_start() failed, rc=%d, %s\n",
|
||||
rc, pam_strerror(pamHandlep, rc));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* just to set it to something */
|
||||
if ((rc = pam_set_item(pamHandlep, PAM_TTY, "ssh") != PAM_SUCCESS)) {
|
||||
dropbear_log(LOG_WARNING, "pam_set_item() failed, rc=%d, %s\n",
|
||||
rc, pam_strerror(pamHandlep, rc));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
(void) pam_fail_delay(pamHandlep, 0 /* musec_delay */);
|
||||
|
||||
/* (void) pam_set_item(pamHandlep, PAM_FAIL_DELAY, (void*) pamDelayFunc); */
|
||||
|
||||
if ((rc = pam_authenticate(pamHandlep, 0)) != PAM_SUCCESS) {
|
||||
dropbear_log(LOG_WARNING, "pam_authenticate() failed, rc=%d, %s\n",
|
||||
rc, pam_strerror(pamHandlep, rc));
|
||||
dropbear_log(LOG_WARNING,
|
||||
"bad PAM password attempt for '%s' from %s",
|
||||
ses.authstate.printableuser,
|
||||
svr_ses.addrstring);
|
||||
send_msg_userauth_failure(0, 1);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if ((rc = pam_acct_mgmt(pamHandlep, 0)) != PAM_SUCCESS) {
|
||||
dropbear_log(LOG_WARNING, "pam_acct_mgmt() failed, rc=%d, %s\n",
|
||||
rc, pam_strerror(pamHandlep, rc));
|
||||
dropbear_log(LOG_WARNING,
|
||||
"bad PAM password attempt for '%s' from %s",
|
||||
ses.authstate.printableuser,
|
||||
svr_ses.addrstring);
|
||||
send_msg_userauth_failure(0, 1);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* successful authentication */
|
||||
dropbear_log(LOG_NOTICE, "PAM password auth succeeded for '%s' from %s",
|
||||
ses.authstate.printableuser,
|
||||
svr_ses.addrstring);
|
||||
send_msg_userauth_success();
|
||||
|
||||
cleanup:
|
||||
if (password != NULL) {
|
||||
m_burn(password, passwordlen);
|
||||
m_free(password);
|
||||
}
|
||||
if (pamHandlep != NULL) {
|
||||
TRACE(("pam_end"))
|
||||
(void) pam_end(pamHandlep, 0 /* pam_status */);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* ENABLE_SVR_PAM_AUTH */
|
||||
@@ -80,10 +80,6 @@ void svr_auth_password() {
|
||||
|
||||
password = buf_getstring(ses.payload, &passwordlen);
|
||||
|
||||
/* clear the buffer containing the password */
|
||||
buf_incrpos(ses.payload, -passwordlen - 4);
|
||||
m_burn(buf_getptr(ses.payload, passwordlen + 4), passwordlen + 4);
|
||||
|
||||
/* the first bytes of passwdcrypt are the salt */
|
||||
testcrypt = crypt((char*)password, passwdcrypt);
|
||||
m_burn(password, passwordlen);
|
||||
@@ -92,13 +88,15 @@ void svr_auth_password() {
|
||||
if (strcmp(testcrypt, passwdcrypt) == 0) {
|
||||
/* successful authentication */
|
||||
dropbear_log(LOG_NOTICE,
|
||||
"password auth succeeded for '%s'",
|
||||
ses.authstate.printableuser);
|
||||
"password auth succeeded for '%s' from %s",
|
||||
ses.authstate.printableuser,
|
||||
svr_ses.addrstring);
|
||||
send_msg_userauth_success();
|
||||
} else {
|
||||
dropbear_log(LOG_WARNING,
|
||||
"bad password attempt for '%s'",
|
||||
ses.authstate.printableuser);
|
||||
"bad password attempt for '%s' from %s",
|
||||
ses.authstate.printableuser,
|
||||
svr_ses.addrstring);
|
||||
send_msg_userauth_failure(0, 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ void svr_auth_pubkey() {
|
||||
char* fp = NULL;
|
||||
int type = -1;
|
||||
|
||||
TRACE(("enter pubkeyauth"));
|
||||
TRACE(("enter pubkeyauth"))
|
||||
|
||||
/* 0 indicates user just wants to check if key can be used, 1 is an
|
||||
* actual attempt*/
|
||||
@@ -104,13 +104,13 @@ void svr_auth_pubkey() {
|
||||
if (buf_verify(ses.payload, key, buf_getptr(signbuf, signbuf->len),
|
||||
signbuf->len) == DROPBEAR_SUCCESS) {
|
||||
dropbear_log(LOG_NOTICE,
|
||||
"pubkey auth succeeded for '%s' with key %s",
|
||||
ses.authstate.printableuser, fp);
|
||||
"pubkey auth succeeded for '%s' with key %s from %s",
|
||||
ses.authstate.printableuser, fp, svr_ses.addrstring);
|
||||
send_msg_userauth_success();
|
||||
} else {
|
||||
dropbear_log(LOG_WARNING,
|
||||
"pubkey auth bad signature for '%s' with key %s",
|
||||
ses.authstate.printableuser, fp);
|
||||
"pubkey auth bad signature for '%s' with key %s from %s",
|
||||
ses.authstate.printableuser, fp, svr_ses.addrstring);
|
||||
send_msg_userauth_failure(0, 1);
|
||||
}
|
||||
m_free(fp);
|
||||
@@ -127,7 +127,7 @@ out:
|
||||
sign_key_free(key);
|
||||
key = NULL;
|
||||
}
|
||||
TRACE(("leave pubkeyauth"));
|
||||
TRACE(("leave pubkeyauth"))
|
||||
}
|
||||
|
||||
/* Reply that the key is valid for auth, this is sent when the user sends
|
||||
@@ -136,7 +136,7 @@ out:
|
||||
static void send_msg_userauth_pk_ok(unsigned char* algo, unsigned int algolen,
|
||||
unsigned char* keyblob, unsigned int keybloblen) {
|
||||
|
||||
TRACE(("enter send_msg_userauth_pk_ok"));
|
||||
TRACE(("enter send_msg_userauth_pk_ok"))
|
||||
CHECKCLEARTOWRITE();
|
||||
|
||||
buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_PK_OK);
|
||||
@@ -144,7 +144,7 @@ static void send_msg_userauth_pk_ok(unsigned char* algo, unsigned int algolen,
|
||||
buf_putstring(ses.writepayload, keyblob, keybloblen);
|
||||
|
||||
encrypt_packet();
|
||||
TRACE(("leave send_msg_userauth_pk_ok"));
|
||||
TRACE(("leave send_msg_userauth_pk_ok"))
|
||||
|
||||
}
|
||||
|
||||
@@ -160,19 +160,19 @@ static int checkpubkey(unsigned char* algo, unsigned int algolen,
|
||||
buffer * line = NULL;
|
||||
unsigned int len, pos;
|
||||
|
||||
TRACE(("enter checkpubkey"));
|
||||
TRACE(("enter checkpubkey"))
|
||||
|
||||
/* check that we can use the algo */
|
||||
if (have_algo(algo, algolen, sshhostkey) == DROPBEAR_FAILURE) {
|
||||
dropbear_log(LOG_WARNING,
|
||||
"pubkey auth attempt with unknown algo for '%s'",
|
||||
ses.authstate.printableuser);
|
||||
"pubkey auth attempt with unknown algo for '%s' from %s",
|
||||
ses.authstate.printableuser, svr_ses.addrstring);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* check file permissions, also whether file exists */
|
||||
if (checkpubkeyperms() == DROPBEAR_FAILURE) {
|
||||
TRACE(("bad authorized_keys permissions, or file doesn't exist"));
|
||||
TRACE(("bad authorized_keys permissions, or file doesn't exist"))
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -190,7 +190,7 @@ static int checkpubkey(unsigned char* algo, unsigned int algolen,
|
||||
if (authfile == NULL) {
|
||||
goto out;
|
||||
}
|
||||
TRACE(("checkpubkey: opened authorized_keys OK"));
|
||||
TRACE(("checkpubkey: opened authorized_keys OK"))
|
||||
|
||||
line = buf_new(MAX_AUTHKEYS_LINE);
|
||||
|
||||
@@ -199,12 +199,12 @@ static int checkpubkey(unsigned char* algo, unsigned int algolen,
|
||||
|
||||
if (buf_getline(line, authfile) == DROPBEAR_FAILURE) {
|
||||
/* EOF reached */
|
||||
TRACE(("checkpubkey: authorized_keys EOF reached"));
|
||||
TRACE(("checkpubkey: authorized_keys EOF reached"))
|
||||
break;
|
||||
}
|
||||
|
||||
if (line->len < MIN_AUTHKEYS_LINE) {
|
||||
TRACE(("checkpubkey: line too short"));
|
||||
TRACE(("checkpubkey: line too short"))
|
||||
continue; /* line is too short for it to be a valid key */
|
||||
}
|
||||
|
||||
@@ -217,7 +217,7 @@ static int checkpubkey(unsigned char* algo, unsigned int algolen,
|
||||
|
||||
/* check for space (' ') character */
|
||||
if (buf_getbyte(line) != ' ') {
|
||||
TRACE(("checkpubkey: space character expected, isn't there"));
|
||||
TRACE(("checkpubkey: space character expected, isn't there"))
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -229,7 +229,7 @@ static int checkpubkey(unsigned char* algo, unsigned int algolen,
|
||||
buf_setpos(line, pos);
|
||||
buf_setlen(line, line->pos + len);
|
||||
|
||||
TRACE(("checkpubkey: line pos = %d len = %d", line->pos, line->len));
|
||||
TRACE(("checkpubkey: line pos = %d len = %d", line->pos, line->len))
|
||||
|
||||
ret = cmp_base64_key(keyblob, keybloblen, algo, algolen, line);
|
||||
if (ret == DROPBEAR_SUCCESS) {
|
||||
@@ -248,7 +248,7 @@ out:
|
||||
buf_free(line);
|
||||
}
|
||||
m_free(filename);
|
||||
TRACE(("leave checkpubkey: ret=%d", ret));
|
||||
TRACE(("leave checkpubkey: ret=%d", ret))
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -264,7 +264,7 @@ static int checkpubkeyperms() {
|
||||
int ret = DROPBEAR_FAILURE;
|
||||
unsigned int len;
|
||||
|
||||
TRACE(("enter checkpubkeyperms"));
|
||||
TRACE(("enter checkpubkeyperms"))
|
||||
|
||||
assert(ses.authstate.pw);
|
||||
if (ses.authstate.pw->pw_dir == NULL) {
|
||||
@@ -303,7 +303,7 @@ static int checkpubkeyperms() {
|
||||
out:
|
||||
m_free(filename);
|
||||
|
||||
TRACE(("leave checkpubkeyperms"));
|
||||
TRACE(("leave checkpubkeyperms"))
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -313,24 +313,24 @@ out:
|
||||
static int checkfileperm(char * filename) {
|
||||
struct stat filestat;
|
||||
|
||||
TRACE(("enter checkfileperm(%s)", filename));
|
||||
TRACE(("enter checkfileperm(%s)", filename))
|
||||
|
||||
if (stat(filename, &filestat) != 0) {
|
||||
TRACE(("leave checkfileperm: stat() != 0"));
|
||||
TRACE(("leave checkfileperm: stat() != 0"))
|
||||
return DROPBEAR_FAILURE;
|
||||
}
|
||||
/* check ownership - user or root only*/
|
||||
if (filestat.st_uid != ses.authstate.pw->pw_uid
|
||||
&& filestat.st_uid != 0) {
|
||||
TRACE(("leave checkfileperm: wrong ownership"));
|
||||
TRACE(("leave checkfileperm: wrong ownership"))
|
||||
return DROPBEAR_FAILURE;
|
||||
}
|
||||
/* check permissions - don't want group or others +w */
|
||||
if (filestat.st_mode & (S_IWGRP | S_IWOTH)) {
|
||||
TRACE(("leave checkfileperm: wrong perms"));
|
||||
TRACE(("leave checkfileperm: wrong perms"))
|
||||
return DROPBEAR_FAILURE;
|
||||
}
|
||||
TRACE(("leave checkfileperm: success"));
|
||||
TRACE(("leave checkfileperm: success"))
|
||||
return DROPBEAR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -55,6 +55,10 @@ static int newchansess(struct Channel *channel);
|
||||
static void chansessionrequest(struct Channel *channel);
|
||||
|
||||
static void send_exitsignalstatus(struct Channel *channel);
|
||||
static void send_msg_chansess_exitstatus(struct Channel * channel,
|
||||
struct ChanSess * chansess);
|
||||
static void send_msg_chansess_exitsignal(struct Channel * channel,
|
||||
struct ChanSess * chansess);
|
||||
static int sesscheckclose(struct Channel *channel);
|
||||
static void get_termmodes(struct ChanSess *chansess);
|
||||
|
||||
@@ -64,46 +68,65 @@ extern char** environ;
|
||||
|
||||
static int sesscheckclose(struct Channel *channel) {
|
||||
struct ChanSess *chansess = (struct ChanSess*)channel->typedata;
|
||||
return chansess->exited;
|
||||
return chansess->exit.exitpid >= 0;
|
||||
}
|
||||
|
||||
/* handler for childs exiting, store the state for return to the client */
|
||||
static void sesssigchild_handler(int dummy) {
|
||||
/* Handler for childs exiting, store the state for return to the client */
|
||||
|
||||
/* There's a particular race we have to watch out for: if the forked child
|
||||
* executes, exits, and this signal-handler is called, all before the parent
|
||||
* gets to run, then the childpids[] array won't have the pid in it. Hence we
|
||||
* use the svr_ses.lastexit struct to hold the exit, which is then compared by
|
||||
* the parent when it runs. This work correctly at least in the case of a
|
||||
* single shell spawned (ie the usual case) */
|
||||
static void sesssigchild_handler(int UNUSED(dummy)) {
|
||||
|
||||
int status;
|
||||
pid_t pid;
|
||||
unsigned int i;
|
||||
struct ChanSess * chansess;
|
||||
struct sigaction sa_chld;
|
||||
struct exitinfo *exit = NULL;
|
||||
|
||||
TRACE(("enter sigchld handler"));
|
||||
TRACE(("enter sigchld handler"))
|
||||
while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
|
||||
|
||||
/* find the corresponding chansess */
|
||||
for (i = 0; i < svr_ses.childpidsize; i++) {
|
||||
if (svr_ses.childpids[i].pid == pid) {
|
||||
|
||||
chansess = svr_ses.childpids[i].chansess;
|
||||
chansess->exited = 1;
|
||||
if (WIFEXITED(status)) {
|
||||
chansess->exitstatus = WEXITSTATUS(status);
|
||||
}
|
||||
if (WIFSIGNALED(status)) {
|
||||
chansess->exitsignal = WTERMSIG(status);
|
||||
#ifndef AIX
|
||||
chansess->exitcore = WCOREDUMP(status);
|
||||
#endif
|
||||
} else {
|
||||
/* we use this to determine how pid exited */
|
||||
chansess->exitsignal = -1;
|
||||
}
|
||||
exit = &svr_ses.childpids[i].chansess->exit;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* If the pid wasn't matched, then we might have hit the race mentioned
|
||||
* above. So we just store the info for the parent to deal with */
|
||||
if (i == svr_ses.childpidsize) {
|
||||
exit = &svr_ses.lastexit;
|
||||
}
|
||||
|
||||
exit->exitpid = pid;
|
||||
if (WIFEXITED(status)) {
|
||||
exit->exitstatus = WEXITSTATUS(status);
|
||||
}
|
||||
if (WIFSIGNALED(status)) {
|
||||
exit->exitsignal = WTERMSIG(status);
|
||||
#if !defined(AIX) && defined(WCOREDUMP)
|
||||
exit->exitcore = WCOREDUMP(status);
|
||||
#else
|
||||
exit->exitcore = 0;
|
||||
#endif
|
||||
} else {
|
||||
/* we use this to determine how pid exited */
|
||||
exit->exitsignal = -1;
|
||||
}
|
||||
exit = NULL;
|
||||
}
|
||||
|
||||
|
||||
sa_chld.sa_handler = sesssigchild_handler;
|
||||
sa_chld.sa_flags = SA_NOCLDSTOP;
|
||||
sigaction(SIGCHLD, &sa_chld, NULL);
|
||||
TRACE(("leave sigchld handler"));
|
||||
TRACE(("leave sigchld handler"))
|
||||
}
|
||||
|
||||
/* send the exit status or the signal causing termination for a session */
|
||||
@@ -112,8 +135,8 @@ static void send_exitsignalstatus(struct Channel *channel) {
|
||||
|
||||
struct ChanSess *chansess = (struct ChanSess*)channel->typedata;
|
||||
|
||||
if (chansess->exited) {
|
||||
if (chansess->exitsignal > 0) {
|
||||
if (chansess->exit.exitpid >= 0) {
|
||||
if (chansess->exit.exitsignal > 0) {
|
||||
send_msg_chansess_exitsignal(channel, chansess);
|
||||
} else {
|
||||
send_msg_chansess_exitstatus(channel, chansess);
|
||||
@@ -125,8 +148,8 @@ static void send_exitsignalstatus(struct Channel *channel) {
|
||||
static void send_msg_chansess_exitstatus(struct Channel * channel,
|
||||
struct ChanSess * chansess) {
|
||||
|
||||
assert(chansess->exited);
|
||||
assert(chansess->exitsignal == -1);
|
||||
assert(chansess->exit.exitpid != -1);
|
||||
assert(chansess->exit.exitsignal == -1);
|
||||
|
||||
CHECKCLEARTOWRITE();
|
||||
|
||||
@@ -134,7 +157,7 @@ static void send_msg_chansess_exitstatus(struct Channel * channel,
|
||||
buf_putint(ses.writepayload, channel->remotechan);
|
||||
buf_putstring(ses.writepayload, "exit-status", 11);
|
||||
buf_putbyte(ses.writepayload, 0); /* boolean FALSE */
|
||||
buf_putint(ses.writepayload, chansess->exitstatus);
|
||||
buf_putint(ses.writepayload, chansess->exit.exitstatus);
|
||||
|
||||
encrypt_packet();
|
||||
|
||||
@@ -147,15 +170,15 @@ static void send_msg_chansess_exitsignal(struct Channel * channel,
|
||||
int i;
|
||||
char* signame = NULL;
|
||||
|
||||
assert(chansess->exited);
|
||||
assert(chansess->exitsignal > 0);
|
||||
assert(chansess->exit.exitpid != -1);
|
||||
assert(chansess->exit.exitsignal > 0);
|
||||
|
||||
CHECKCLEARTOWRITE();
|
||||
|
||||
/* we check that we can match a signal name, otherwise
|
||||
* don't send anything */
|
||||
for (i = 0; signames[i].name != NULL; i++) {
|
||||
if (signames[i].signal == chansess->exitsignal) {
|
||||
if (signames[i].signal == chansess->exit.exitsignal) {
|
||||
signame = signames[i].name;
|
||||
break;
|
||||
}
|
||||
@@ -170,7 +193,7 @@ static void send_msg_chansess_exitsignal(struct Channel * channel,
|
||||
buf_putstring(ses.writepayload, "exit-signal", 11);
|
||||
buf_putbyte(ses.writepayload, 0); /* boolean FALSE */
|
||||
buf_putstring(ses.writepayload, signame, strlen(signame));
|
||||
buf_putbyte(ses.writepayload, chansess->exitcore);
|
||||
buf_putbyte(ses.writepayload, chansess->exit.exitcore);
|
||||
buf_putstring(ses.writepayload, "", 0); /* error msg */
|
||||
buf_putstring(ses.writepayload, "", 0); /* lang */
|
||||
|
||||
@@ -194,7 +217,7 @@ static int newchansess(struct Channel *channel) {
|
||||
chansess->tty = NULL;
|
||||
chansess->term = NULL;
|
||||
|
||||
chansess->exited = 0;
|
||||
chansess->exit.exitpid = -1;
|
||||
|
||||
channel->typedata = chansess;
|
||||
|
||||
@@ -225,9 +248,9 @@ static void closechansess(struct Channel *channel) {
|
||||
|
||||
send_exitsignalstatus(channel);
|
||||
|
||||
TRACE(("enter closechansess"));
|
||||
TRACE(("enter closechansess"))
|
||||
if (chansess == NULL) {
|
||||
TRACE(("leave closechansess: chansess == NULL"));
|
||||
TRACE(("leave closechansess: chansess == NULL"))
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -257,8 +280,8 @@ static void closechansess(struct Channel *channel) {
|
||||
for (i = 0; i < svr_ses.childpidsize; i++) {
|
||||
if (svr_ses.childpids[i].chansess == chansess) {
|
||||
assert(svr_ses.childpids[i].pid > 0);
|
||||
TRACE(("closing pid %d", svr_ses.childpids[i].pid));
|
||||
TRACE(("exited = %d", chansess->exited));
|
||||
TRACE(("closing pid %d", svr_ses.childpids[i].pid))
|
||||
TRACE(("exitpid = %d", chansess->exit.exitpid))
|
||||
svr_ses.childpids[i].pid = -1;
|
||||
svr_ses.childpids[i].chansess = NULL;
|
||||
}
|
||||
@@ -266,7 +289,7 @@ static void closechansess(struct Channel *channel) {
|
||||
|
||||
m_free(chansess);
|
||||
|
||||
TRACE(("leave closechansess"));
|
||||
TRACE(("leave closechansess"))
|
||||
}
|
||||
|
||||
/* Handle requests for a channel. These can be execution requests,
|
||||
@@ -279,19 +302,19 @@ static void chansessionrequest(struct Channel *channel) {
|
||||
int ret = 1;
|
||||
struct ChanSess *chansess;
|
||||
|
||||
TRACE(("enter chansessionrequest"));
|
||||
TRACE(("enter chansessionrequest"))
|
||||
|
||||
type = buf_getstring(ses.payload, &typelen);
|
||||
wantreply = buf_getbyte(ses.payload);
|
||||
|
||||
if (typelen > MAX_NAME_LEN) {
|
||||
TRACE(("leave chansessionrequest: type too long")); /* XXX send error?*/
|
||||
TRACE(("leave chansessionrequest: type too long")) /* XXX send error?*/
|
||||
goto out;
|
||||
}
|
||||
|
||||
chansess = (struct ChanSess*)channel->typedata;
|
||||
assert(chansess != NULL);
|
||||
TRACE(("type is %s", type));
|
||||
TRACE(("type is %s", type))
|
||||
|
||||
if (strcmp(type, "window-change") == 0) {
|
||||
ret = sessionwinchange(chansess);
|
||||
@@ -328,7 +351,7 @@ out:
|
||||
}
|
||||
|
||||
m_free(type);
|
||||
TRACE(("leave chansessionrequest"));
|
||||
TRACE(("leave chansessionrequest"))
|
||||
}
|
||||
|
||||
|
||||
@@ -398,7 +421,7 @@ static void get_termmodes(struct ChanSess *chansess) {
|
||||
const struct TermCode * termcode;
|
||||
unsigned int len;
|
||||
|
||||
TRACE(("enter get_termmodes"));
|
||||
TRACE(("enter get_termmodes"))
|
||||
|
||||
/* Term modes */
|
||||
/* We'll ignore errors and continue if we can't set modes.
|
||||
@@ -415,7 +438,8 @@ static void get_termmodes(struct ChanSess *chansess) {
|
||||
}
|
||||
|
||||
if (len == 0) {
|
||||
TRACE(("leave get_termmodes: empty terminal modes string"));
|
||||
TRACE(("leave get_termmodes: empty terminal modes string"))
|
||||
return;
|
||||
}
|
||||
|
||||
while (((opcode = buf_getbyte(ses.payload)) != 0x00) && opcode <= 159) {
|
||||
@@ -477,7 +501,7 @@ static void get_termmodes(struct ChanSess *chansess) {
|
||||
if (tcsetattr(chansess->master, TCSANOW, &termio) < 0) {
|
||||
dropbear_log(LOG_INFO, "error setting terminal attributes");
|
||||
}
|
||||
TRACE(("leave get_termmodes"));
|
||||
TRACE(("leave get_termmodes"))
|
||||
}
|
||||
|
||||
/* Set up a session pty which will be used to execute the shell or program.
|
||||
@@ -488,18 +512,20 @@ static int sessionpty(struct ChanSess * chansess) {
|
||||
unsigned int termlen;
|
||||
unsigned char namebuf[65];
|
||||
|
||||
TRACE(("enter sessionpty"));
|
||||
TRACE(("enter sessionpty"))
|
||||
chansess->term = buf_getstring(ses.payload, &termlen);
|
||||
if (termlen > MAX_TERM_LEN) {
|
||||
/* TODO send disconnect ? */
|
||||
TRACE(("leave sessionpty: term len too long"));
|
||||
TRACE(("leave sessionpty: term len too long"))
|
||||
return DROPBEAR_FAILURE;
|
||||
}
|
||||
|
||||
/* allocate the pty */
|
||||
assert(chansess->master == -1); /* haven't already got one */
|
||||
if (chansess->master != -1) {
|
||||
dropbear_exit("multiple pty requests");
|
||||
}
|
||||
if (pty_allocate(&chansess->master, &chansess->slave, namebuf, 64) == 0) {
|
||||
TRACE(("leave sessionpty: failed to allocate pty"));
|
||||
TRACE(("leave sessionpty: failed to allocate pty"))
|
||||
return DROPBEAR_FAILURE;
|
||||
}
|
||||
|
||||
@@ -516,7 +542,7 @@ static int sessionpty(struct ChanSess * chansess) {
|
||||
/* Read the terminal modes */
|
||||
get_termmodes(chansess);
|
||||
|
||||
TRACE(("leave sessionpty"));
|
||||
TRACE(("leave sessionpty"))
|
||||
return DROPBEAR_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -530,7 +556,7 @@ static int sessioncommand(struct Channel *channel, struct ChanSess *chansess,
|
||||
unsigned int cmdlen;
|
||||
int ret;
|
||||
|
||||
TRACE(("enter sessioncommand"));
|
||||
TRACE(("enter sessioncommand"))
|
||||
|
||||
if (chansess->cmd != NULL) {
|
||||
/* Note that only one command can _succeed_. The client might try
|
||||
@@ -585,8 +611,9 @@ static int noptycommand(struct Channel *channel, struct ChanSess *chansess) {
|
||||
int outfds[2];
|
||||
int errfds[2];
|
||||
pid_t pid;
|
||||
unsigned int i;
|
||||
|
||||
TRACE(("enter noptycommand"));
|
||||
TRACE(("enter noptycommand"))
|
||||
|
||||
/* redirect stdin/stdout/stderr */
|
||||
if (pipe(infds) != 0)
|
||||
@@ -609,7 +636,7 @@ static int noptycommand(struct Channel *channel, struct ChanSess *chansess) {
|
||||
if ((dup2(infds[FDIN], STDIN_FILENO) < 0) ||
|
||||
(dup2(outfds[FDOUT], STDOUT_FILENO) < 0) ||
|
||||
(dup2(errfds[FDOUT], STDERR_FILENO) < 0)) {
|
||||
TRACE(("leave noptycommand: error redirecting FDs"));
|
||||
TRACE(("leave noptycommand: error redirecting FDs"))
|
||||
return DROPBEAR_FAILURE;
|
||||
}
|
||||
|
||||
@@ -625,12 +652,24 @@ static int noptycommand(struct Channel *channel, struct ChanSess *chansess) {
|
||||
|
||||
} else {
|
||||
/* parent */
|
||||
TRACE(("continue noptycommand: parent"));
|
||||
TRACE(("continue noptycommand: parent"))
|
||||
chansess->pid = pid;
|
||||
|
||||
/* add a child pid */
|
||||
addchildpid(chansess, pid);
|
||||
|
||||
if (svr_ses.lastexit.exitpid != -1) {
|
||||
/* The child probably exited and the signal handler triggered
|
||||
* possibly before we got around to adding the childpid. So we fill
|
||||
* out it's data manually */
|
||||
for (i = 0; i < svr_ses.childpidsize; i++) {
|
||||
if (svr_ses.childpids[i].pid == pid) {
|
||||
svr_ses.childpids[i].chansess->exit = svr_ses.lastexit;
|
||||
svr_ses.lastexit.exitpid = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
close(infds[FDIN]);
|
||||
close(outfds[FDOUT]);
|
||||
close(errfds[FDOUT]);
|
||||
@@ -641,16 +680,15 @@ static int noptycommand(struct Channel *channel, struct ChanSess *chansess) {
|
||||
ses.maxfd = MAX(ses.maxfd, channel->outfd);
|
||||
ses.maxfd = MAX(ses.maxfd, channel->errfd);
|
||||
|
||||
if ((fcntl(channel->outfd, F_SETFL, O_NONBLOCK) < 0) ||
|
||||
(fcntl(channel->infd, F_SETFL, O_NONBLOCK) < 0) ||
|
||||
(fcntl(channel->errfd, F_SETFL, O_NONBLOCK) < 0)) {
|
||||
dropbear_exit("Couldn't set nonblocking");
|
||||
}
|
||||
setnonblocking(channel->outfd);
|
||||
setnonblocking(channel->infd);
|
||||
setnonblocking(channel->errfd);
|
||||
|
||||
}
|
||||
#undef FDIN
|
||||
#undef FDOUT
|
||||
|
||||
TRACE(("leave noptycommand"));
|
||||
TRACE(("leave noptycommand"))
|
||||
return DROPBEAR_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -668,7 +706,7 @@ static int ptycommand(struct Channel *channel, struct ChanSess *chansess) {
|
||||
char *hushpath = NULL;
|
||||
#endif
|
||||
|
||||
TRACE(("enter ptycommand"));
|
||||
TRACE(("enter ptycommand"))
|
||||
|
||||
/* we need to have a pty allocated */
|
||||
if (chansess->master == -1 || chansess->tty == NULL) {
|
||||
@@ -691,7 +729,7 @@ static int ptycommand(struct Channel *channel, struct ChanSess *chansess) {
|
||||
if ((dup2(chansess->slave, STDIN_FILENO) < 0) ||
|
||||
(dup2(chansess->slave, STDERR_FILENO) < 0) ||
|
||||
(dup2(chansess->slave, STDOUT_FILENO) < 0)) {
|
||||
TRACE(("leave ptycommand: error redirecting filedesc"));
|
||||
TRACE(("leave ptycommand: error redirecting filedesc"))
|
||||
return DROPBEAR_FAILURE;
|
||||
}
|
||||
|
||||
@@ -739,7 +777,7 @@ static int ptycommand(struct Channel *channel, struct ChanSess *chansess) {
|
||||
|
||||
} else {
|
||||
/* parent */
|
||||
TRACE(("continue ptycommand: parent"));
|
||||
TRACE(("continue ptycommand: parent"))
|
||||
chansess->pid = pid;
|
||||
|
||||
/* add a child pid */
|
||||
@@ -751,13 +789,11 @@ static int ptycommand(struct Channel *channel, struct ChanSess *chansess) {
|
||||
/* don't need to set stderr here */
|
||||
ses.maxfd = MAX(ses.maxfd, chansess->master);
|
||||
|
||||
if (fcntl(chansess->master, F_SETFL, O_NONBLOCK) < 0) {
|
||||
dropbear_exit("Couldn't set nonblocking");
|
||||
}
|
||||
setnonblocking(chansess->master);
|
||||
|
||||
}
|
||||
|
||||
TRACE(("leave ptycommand"));
|
||||
TRACE(("leave ptycommand"))
|
||||
return DROPBEAR_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -925,6 +961,7 @@ void svr_chansessinitialise() {
|
||||
svr_ses.childpids[0].pid = -1; /* unused */
|
||||
svr_ses.childpids[0].chansess = NULL;
|
||||
svr_ses.childpidsize = 1;
|
||||
svr_ses.lastexit.exitpid = -1; /* Nothing has exited yet */
|
||||
sa_chld.sa_handler = sesssigchild_handler;
|
||||
sa_chld.sa_flags = SA_NOCLDSTOP;
|
||||
if (sigaction(SIGCHLD, &sa_chld, NULL) < 0) {
|
||||
|
||||
14
svr-kex.c
14
svr-kex.c
@@ -44,9 +44,9 @@ static void send_msg_kexdh_reply(mp_int *dh_e);
|
||||
* that function, then brings the new keys into use */
|
||||
void recv_msg_kexdh_init() {
|
||||
|
||||
mp_int dh_e;
|
||||
DEF_MP_INT(dh_e);
|
||||
|
||||
TRACE(("enter recv_msg_kexdh_init"));
|
||||
TRACE(("enter recv_msg_kexdh_init"))
|
||||
if (!ses.kexstate.recvkexinit) {
|
||||
dropbear_exit("Premature kexdh_init message received");
|
||||
}
|
||||
@@ -60,7 +60,7 @@ void recv_msg_kexdh_init() {
|
||||
|
||||
send_msg_newkeys();
|
||||
ses.requirenext = SSH_MSG_NEWKEYS;
|
||||
TRACE(("leave recv_msg_kexdh_init"));
|
||||
TRACE(("leave recv_msg_kexdh_init"))
|
||||
}
|
||||
|
||||
/* Generate our side of the diffie-hellman key exchange value (dh_f), and
|
||||
@@ -71,9 +71,11 @@ void recv_msg_kexdh_init() {
|
||||
* See the ietf-secsh-transport draft, section 6, for details */
|
||||
static void send_msg_kexdh_reply(mp_int *dh_e) {
|
||||
|
||||
mp_int dh_y, dh_f;
|
||||
DEF_MP_INT(dh_y);
|
||||
DEF_MP_INT(dh_f);
|
||||
|
||||
TRACE(("enter send_msg_kexdh_reply"));
|
||||
TRACE(("enter send_msg_kexdh_reply"))
|
||||
m_mp_init_multi(&dh_y, &dh_f, NULL);
|
||||
|
||||
gen_kexdh_vals(&dh_f, &dh_y);
|
||||
|
||||
@@ -97,6 +99,6 @@ static void send_msg_kexdh_reply(mp_int *dh_e) {
|
||||
/* the SSH_MSG_KEXDH_REPLY is done */
|
||||
encrypt_packet();
|
||||
|
||||
TRACE(("leave send_msg_kexdh_reply"));
|
||||
TRACE(("leave send_msg_kexdh_reply"))
|
||||
}
|
||||
|
||||
|
||||
37
svr-main.c
37
svr-main.c
@@ -33,8 +33,12 @@ static int listensockets(int *sock, int sockcount, int *maxfd);
|
||||
static void sigchld_handler(int dummy);
|
||||
static void sigsegv_handler(int);
|
||||
static void sigintterm_handler(int fish);
|
||||
#ifdef INETD_MODE
|
||||
static void main_inetd();
|
||||
#endif
|
||||
#ifdef NON_INETD_MODE
|
||||
static void main_noinetd();
|
||||
#endif
|
||||
static void commonsetup();
|
||||
|
||||
static int childpipes[MAX_UNAUTH_CLIENTS];
|
||||
@@ -90,7 +94,6 @@ static void main_inetd() {
|
||||
/* In case our inetd was lax in logging source addresses */
|
||||
addrstring = getaddrstring(&remoteaddr, 1);
|
||||
dropbear_log(LOG_INFO, "Child connection from %s", addrstring);
|
||||
m_free(addrstring);
|
||||
|
||||
/* Don't check the return value - it may just fail since inetd has
|
||||
* already done setsid() after forking (xinetd on Darwin appears to do
|
||||
@@ -100,7 +103,7 @@ static void main_inetd() {
|
||||
/* Start service program
|
||||
* -1 is a dummy childpipe, just something we can close() without
|
||||
* mattering. */
|
||||
svr_session(0, -1, getaddrhostname(&remoteaddr));
|
||||
svr_session(0, -1, getaddrhostname(&remoteaddr), addrstring);
|
||||
|
||||
/* notreached */
|
||||
}
|
||||
@@ -123,7 +126,6 @@ void main_noinetd() {
|
||||
pid_t childpid;
|
||||
int childpipe[2];
|
||||
|
||||
struct sigaction sa_chld;
|
||||
/* fork */
|
||||
if (svr_opts.forkbg) {
|
||||
int closefds = 0;
|
||||
@@ -139,6 +141,7 @@ void main_noinetd() {
|
||||
|
||||
commonsetup();
|
||||
|
||||
|
||||
/* should be done after syslog is working */
|
||||
if (svr_opts.forkbg) {
|
||||
dropbear_log(LOG_INFO, "Running in background");
|
||||
@@ -244,7 +247,7 @@ void main_noinetd() {
|
||||
}
|
||||
|
||||
if (pipe(childpipe) < 0) {
|
||||
TRACE(("error creating child pipe"));
|
||||
TRACE(("error creating child pipe"))
|
||||
close(childsock);
|
||||
continue;
|
||||
}
|
||||
@@ -260,7 +263,6 @@ void main_noinetd() {
|
||||
|
||||
addrstring = getaddrstring(&remoteaddr, 1);
|
||||
dropbear_log(LOG_INFO, "Child connection from %s", addrstring);
|
||||
m_free(addrstring);
|
||||
|
||||
if (setsid() < 0) {
|
||||
dropbear_exit("setsid: %s", strerror(errno));
|
||||
@@ -279,7 +281,8 @@ void main_noinetd() {
|
||||
|
||||
/* start the session */
|
||||
svr_session(childsock, childpipe[1],
|
||||
getaddrhostname(&remoteaddr));
|
||||
getaddrhostname(&remoteaddr),
|
||||
addrstring);
|
||||
/* don't return */
|
||||
assert(0);
|
||||
}
|
||||
@@ -299,7 +302,7 @@ void main_noinetd() {
|
||||
|
||||
|
||||
/* catch + reap zombie children */
|
||||
static void sigchld_handler(int fish) {
|
||||
static void sigchld_handler(int UNUSED(unused)) {
|
||||
struct sigaction sa_chld;
|
||||
|
||||
while(waitpid(-1, NULL, WNOHANG) > 0);
|
||||
@@ -312,14 +315,14 @@ static void sigchld_handler(int fish) {
|
||||
}
|
||||
|
||||
/* catch any segvs */
|
||||
static void sigsegv_handler(int fish) {
|
||||
static void sigsegv_handler(int UNUSED(unused)) {
|
||||
fprintf(stderr, "Aiee, segfault! You should probably report "
|
||||
"this as a bug to the developer\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* catch ctrl-c or sigterm */
|
||||
static void sigintterm_handler(int fish) {
|
||||
static void sigintterm_handler(int UNUSED(unused)) {
|
||||
|
||||
exitflag = 1;
|
||||
}
|
||||
@@ -352,27 +355,33 @@ static void commonsetup() {
|
||||
if (signal(SIGSEGV, sigsegv_handler) == SIG_ERR) {
|
||||
dropbear_exit("signal() error");
|
||||
}
|
||||
|
||||
/* Now we can setup the hostkeys - needs to be after logging is on,
|
||||
* otherwise we might end up blatting error messages to the socket */
|
||||
loadhostkeys();
|
||||
}
|
||||
|
||||
/* Set up listening sockets for all the requested ports */
|
||||
static int listensockets(int *sock, int sockcount, int *maxfd) {
|
||||
|
||||
unsigned int i;
|
||||
char portstring[NI_MAXSERV];
|
||||
char* errstring = NULL;
|
||||
unsigned int sockpos = 0;
|
||||
int nsock;
|
||||
|
||||
TRACE(("listensockets: %d to try\n", svr_opts.portcount))
|
||||
|
||||
for (i = 0; i < svr_opts.portcount; i++) {
|
||||
|
||||
snprintf(portstring, sizeof(portstring), "%d", svr_opts.ports[i]);
|
||||
nsock = dropbear_listen(NULL, portstring, &sock[sockpos],
|
||||
TRACE(("listening on '%s'", svr_opts.ports[i]))
|
||||
|
||||
nsock = dropbear_listen(NULL, svr_opts.ports[i], &sock[sockpos],
|
||||
sockcount - sockpos,
|
||||
&errstring, maxfd);
|
||||
|
||||
if (nsock < 0) {
|
||||
dropbear_log(LOG_WARNING, "Failed listening on port %s: %s",
|
||||
portstring, errstring);
|
||||
dropbear_log(LOG_WARNING, "Failed listening on '%s': %s",
|
||||
svr_opts.ports[i], errstring);
|
||||
m_free(errstring);
|
||||
continue;
|
||||
}
|
||||
|
||||
109
svr-runopts.c
109
svr-runopts.c
@@ -31,8 +31,6 @@
|
||||
|
||||
svr_runopts svr_opts; /* GLOBAL */
|
||||
|
||||
static sign_key * loadhostkeys(const char * dsskeyfile,
|
||||
const char * rsakeyfile);
|
||||
static void printhelp(const char * progname);
|
||||
|
||||
static void printhelp(const char * progname) {
|
||||
@@ -61,7 +59,7 @@ static void printhelp(const char * progname) {
|
||||
"-m Don't display the motd on login\n"
|
||||
#endif
|
||||
"-w Disallow root logins\n"
|
||||
#ifdef ENABLE_SVR_PASSWORD_AUTH
|
||||
#if defined(ENABLE_SVR_PASSWORD_AUTH) || defined(ENABLE_SVR_PAM_AUTH)
|
||||
"-s Disable password logins\n"
|
||||
"-g Disable password logins for root\n"
|
||||
#endif
|
||||
@@ -72,9 +70,12 @@ static void printhelp(const char * progname) {
|
||||
"-k Disable remote port forwarding\n"
|
||||
#endif
|
||||
"-p port Listen on specified tcp port, up to %d can be specified\n"
|
||||
" (default %d if none specified)\n"
|
||||
" (default %s if none specified)\n"
|
||||
#ifdef INETD_MODE
|
||||
"-i Start for inetd\n"
|
||||
#endif
|
||||
#ifdef DEBUG_TRACE
|
||||
"-v verbose\n"
|
||||
#endif
|
||||
,DROPBEAR_VERSION, progname,
|
||||
#ifdef DROPBEAR_DSS
|
||||
@@ -83,16 +84,13 @@ static void printhelp(const char * progname) {
|
||||
#ifdef DROPBEAR_RSA
|
||||
RSA_PRIV_FILENAME,
|
||||
#endif
|
||||
DROPBEAR_MAX_PORTS, DROPBEAR_PORT);
|
||||
DROPBEAR_MAX_PORTS, DROPBEAR_DEFPORT);
|
||||
}
|
||||
|
||||
void svr_getopts(int argc, char ** argv) {
|
||||
|
||||
unsigned int i;
|
||||
char ** next = 0;
|
||||
unsigned int portnum = 0;
|
||||
char *portstring[DROPBEAR_MAX_PORTS];
|
||||
unsigned int longport;
|
||||
|
||||
/* see printhelp() for options */
|
||||
svr_opts.rsakeyfile = NULL;
|
||||
@@ -104,6 +102,8 @@ void svr_getopts(int argc, char ** argv) {
|
||||
svr_opts.noauthpass = 0;
|
||||
svr_opts.norootpass = 0;
|
||||
svr_opts.inetdmode = 0;
|
||||
svr_opts.portcount = 0;
|
||||
svr_opts.hostkey = NULL;
|
||||
opts.nolocaltcp = 0;
|
||||
opts.noremotetcp = 0;
|
||||
/* not yet
|
||||
@@ -166,10 +166,12 @@ void svr_getopts(int argc, char ** argv) {
|
||||
break;
|
||||
#endif
|
||||
case 'p':
|
||||
if (portnum < DROPBEAR_MAX_PORTS) {
|
||||
portstring[portnum] = NULL;
|
||||
next = &portstring[portnum];
|
||||
portnum++;
|
||||
if (svr_opts.portcount < DROPBEAR_MAX_PORTS) {
|
||||
svr_opts.ports[svr_opts.portcount] = NULL;
|
||||
next = &svr_opts.ports[svr_opts.portcount];
|
||||
/* Note: if it doesn't actually get set, we'll
|
||||
* decrement it after the loop */
|
||||
svr_opts.portcount++;
|
||||
}
|
||||
break;
|
||||
#ifdef DO_MOTD
|
||||
@@ -181,7 +183,7 @@ void svr_getopts(int argc, char ** argv) {
|
||||
case 'w':
|
||||
svr_opts.norootlogin = 1;
|
||||
break;
|
||||
#ifdef ENABLE_SVR_PASSWORD_AUTH
|
||||
#if defined(ENABLE_SVR_PASSWORD_AUTH) || defined(ENABLE_SVR_PAM_AUTH)
|
||||
case 's':
|
||||
svr_opts.noauthpass = 1;
|
||||
break;
|
||||
@@ -193,14 +195,11 @@ void svr_getopts(int argc, char ** argv) {
|
||||
printhelp(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
break;
|
||||
/*
|
||||
case '4':
|
||||
svr_opts.ipv4 = 0;
|
||||
#ifdef DEBUG_TRACE
|
||||
case 'v':
|
||||
debug_trace = 1;
|
||||
break;
|
||||
case '6':
|
||||
svr_opts.ipv6 = 0;
|
||||
break;
|
||||
*/
|
||||
#endif
|
||||
default:
|
||||
fprintf(stderr, "Unknown argument %s\n", argv[i]);
|
||||
printhelp(argv[0]);
|
||||
@@ -210,13 +209,24 @@ void svr_getopts(int argc, char ** argv) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Set up listening ports */
|
||||
if (svr_opts.portcount == 0) {
|
||||
svr_opts.ports[0] = m_strdup(DROPBEAR_DEFPORT);
|
||||
svr_opts.portcount = 1;
|
||||
} else {
|
||||
/* we may have been given a -p option but no argument to go with
|
||||
* it */
|
||||
if (svr_opts.ports[svr_opts.portcount-1] == NULL) {
|
||||
svr_opts.portcount--;
|
||||
}
|
||||
}
|
||||
|
||||
if (svr_opts.dsskeyfile == NULL) {
|
||||
svr_opts.dsskeyfile = DSS_PRIV_FILENAME;
|
||||
}
|
||||
if (svr_opts.rsakeyfile == NULL) {
|
||||
svr_opts.rsakeyfile = RSA_PRIV_FILENAME;
|
||||
}
|
||||
svr_opts.hostkey = loadhostkeys(svr_opts.dsskeyfile, svr_opts.rsakeyfile);
|
||||
|
||||
if (svr_opts.bannerfile) {
|
||||
struct stat buf;
|
||||
@@ -238,35 +248,6 @@ void svr_getopts(int argc, char ** argv) {
|
||||
buf_setpos(svr_opts.banner, 0);
|
||||
}
|
||||
|
||||
/* not yet
|
||||
if (!(svr_opts.ipv4 || svr_opts.ipv6)) {
|
||||
fprintf(stderr, "You can't disable ipv4 and ipv6.\n");
|
||||
exit(1);
|
||||
}
|
||||
*/
|
||||
|
||||
/* create the array of listening ports */
|
||||
if (portnum == 0) {
|
||||
/* non specified */
|
||||
svr_opts.portcount = 1;
|
||||
svr_opts.ports = m_malloc(sizeof(uint16_t));
|
||||
svr_opts.ports[0] = DROPBEAR_PORT;
|
||||
} else {
|
||||
svr_opts.portcount = portnum;
|
||||
svr_opts.ports = (uint16_t*)m_malloc(sizeof(uint16_t)*portnum);
|
||||
for (i = 0; i < portnum; i++) {
|
||||
if (portstring[i]) {
|
||||
longport = atoi(portstring[i]);
|
||||
if (longport <= 65535 && longport > 0) {
|
||||
svr_opts.ports[i] = (uint16_t)longport;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "Bad port '%s'\n",
|
||||
portstring[i] ? portstring[i] : "null");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void disablekey(int type, const char* filename) {
|
||||
@@ -279,47 +260,45 @@ static void disablekey(int type, const char* filename) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "Failed reading '%s', disabling %s\n", filename,
|
||||
dropbear_log(LOG_WARNING, "Failed reading '%s', disabling %s", filename,
|
||||
type == DROPBEAR_SIGNKEY_DSS ? "DSS" : "RSA");
|
||||
}
|
||||
|
||||
static sign_key * loadhostkeys(const char * dsskeyfile,
|
||||
const char * rsakeyfile) {
|
||||
/* Must be called after syslog/etc is working */
|
||||
void loadhostkeys() {
|
||||
|
||||
sign_key * hostkey;
|
||||
int ret;
|
||||
int type;
|
||||
|
||||
TRACE(("enter loadhostkeys"));
|
||||
TRACE(("enter loadhostkeys"))
|
||||
|
||||
hostkey = new_sign_key();
|
||||
svr_opts.hostkey = new_sign_key();
|
||||
|
||||
#ifdef DROPBEAR_RSA
|
||||
type = DROPBEAR_SIGNKEY_RSA;
|
||||
ret = readhostkey(rsakeyfile, hostkey, &type);
|
||||
ret = readhostkey(svr_opts.rsakeyfile, svr_opts.hostkey, &type);
|
||||
if (ret == DROPBEAR_FAILURE) {
|
||||
disablekey(DROPBEAR_SIGNKEY_RSA, rsakeyfile);
|
||||
disablekey(DROPBEAR_SIGNKEY_RSA, svr_opts.rsakeyfile);
|
||||
}
|
||||
#endif
|
||||
#ifdef DROPBEAR_DSS
|
||||
type = DROPBEAR_SIGNKEY_RSA;
|
||||
ret = readhostkey(dsskeyfile, hostkey, &type);
|
||||
type = DROPBEAR_SIGNKEY_DSS;
|
||||
ret = readhostkey(svr_opts.dsskeyfile, svr_opts.hostkey, &type);
|
||||
if (ret == DROPBEAR_FAILURE) {
|
||||
disablekey(DROPBEAR_SIGNKEY_DSS, dsskeyfile);
|
||||
disablekey(DROPBEAR_SIGNKEY_DSS, svr_opts.dsskeyfile);
|
||||
}
|
||||
#endif
|
||||
|
||||
if ( 1
|
||||
#ifdef DROPBEAR_DSS
|
||||
&& hostkey->dsskey == NULL
|
||||
&& svr_opts.hostkey->dsskey == NULL
|
||||
#endif
|
||||
#ifdef DROPBEAR_RSA
|
||||
&& hostkey->rsakey == NULL
|
||||
&& svr_opts.hostkey->rsakey == NULL
|
||||
#endif
|
||||
) {
|
||||
dropbear_exit("No hostkeys available");
|
||||
}
|
||||
|
||||
TRACE(("leave loadhostkeys"));
|
||||
return hostkey;
|
||||
TRACE(("leave loadhostkeys"))
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ void recv_msg_service_request() {
|
||||
unsigned char * name;
|
||||
unsigned int len;
|
||||
|
||||
TRACE(("enter recv_msg_service_request"));
|
||||
TRACE(("enter recv_msg_service_request"))
|
||||
|
||||
name = buf_getstring(ses.payload, &len);
|
||||
|
||||
@@ -49,7 +49,7 @@ void recv_msg_service_request() {
|
||||
|
||||
send_msg_service_accept(name, len);
|
||||
m_free(name);
|
||||
TRACE(("leave recv_msg_service_request: done ssh-userauth"));
|
||||
TRACE(("leave recv_msg_service_request: done ssh-userauth"))
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ void recv_msg_service_request() {
|
||||
|
||||
send_msg_service_accept(name, len);
|
||||
m_free(name);
|
||||
TRACE(("leave recv_msg_service_request: done ssh-connection"));
|
||||
TRACE(("leave recv_msg_service_request: done ssh-connection"))
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ void recv_msg_service_request() {
|
||||
|
||||
static void send_msg_service_accept(unsigned char *name, int len) {
|
||||
|
||||
TRACE(("accepting service %s", name));
|
||||
TRACE(("accepting service %s", name))
|
||||
|
||||
CHECKCLEARTOWRITE();
|
||||
|
||||
|
||||
@@ -52,23 +52,30 @@ static const packettype svr_packettypes[] = {
|
||||
{SSH_MSG_KEXINIT, recv_msg_kexinit},
|
||||
{SSH_MSG_KEXDH_INIT, recv_msg_kexdh_init}, /* server */
|
||||
{SSH_MSG_NEWKEYS, recv_msg_newkeys},
|
||||
#ifdef ENABLE_SVR_REMOTETCPFWD
|
||||
{SSH_MSG_GLOBAL_REQUEST, recv_msg_global_request_remotetcp},
|
||||
#endif
|
||||
{SSH_MSG_CHANNEL_REQUEST, recv_msg_channel_request},
|
||||
{SSH_MSG_CHANNEL_OPEN, recv_msg_channel_open},
|
||||
{SSH_MSG_CHANNEL_EOF, recv_msg_channel_eof},
|
||||
{SSH_MSG_CHANNEL_CLOSE, recv_msg_channel_close},
|
||||
#ifdef USING_LISTENERS
|
||||
{SSH_MSG_CHANNEL_OPEN_CONFIRMATION, recv_msg_channel_open_confirmation},
|
||||
{SSH_MSG_CHANNEL_OPEN_FAILURE, recv_msg_channel_open_failure},
|
||||
#endif
|
||||
{0, 0} /* End */
|
||||
};
|
||||
|
||||
static const struct ChanType *svr_chantypes[] = {
|
||||
&svrchansess,
|
||||
#ifdef ENABLE_SVR_LOCALTCPFWD
|
||||
&svr_chan_tcpdirect,
|
||||
#endif
|
||||
NULL /* Null termination is mandatory. */
|
||||
};
|
||||
|
||||
void svr_session(int sock, int childpipe, char* remotehost) {
|
||||
void svr_session(int sock, int childpipe,
|
||||
char* remotehost, char *addrstring) {
|
||||
|
||||
struct timeval timeout;
|
||||
|
||||
@@ -77,6 +84,7 @@ void svr_session(int sock, int childpipe, char* remotehost) {
|
||||
|
||||
/* Initialise server specific parts of the session */
|
||||
svr_ses.childpipe = childpipe;
|
||||
svr_ses.addrstring = addrstring;
|
||||
svr_authinitialise();
|
||||
chaninitialise(svr_chantypes);
|
||||
svr_chansessinitialise();
|
||||
@@ -168,7 +176,7 @@ void svr_dropbear_log(int priority, const char* format, va_list param) {
|
||||
/* if we are using DEBUG_TRACE, we want to print to stderr even if
|
||||
* syslog is used, so it is included in error reports */
|
||||
#ifdef DEBUG_TRACE
|
||||
havetrace = 1;
|
||||
havetrace = debug_trace;
|
||||
#endif
|
||||
|
||||
if (!svr_opts.usingsyslog || havetrace)
|
||||
|
||||
40
svr-tcpfwd.c
40
svr-tcpfwd.c
@@ -33,7 +33,7 @@
|
||||
#include "listener.h"
|
||||
#include "runopts.h"
|
||||
|
||||
#ifndef DISABLE_SVR_REMOTETCPFWD
|
||||
#ifdef ENABLE_SVR_REMOTETCPFWD
|
||||
|
||||
static void send_msg_request_success();
|
||||
static void send_msg_request_failure();
|
||||
@@ -70,10 +70,10 @@ void recv_msg_global_request_remotetcp() {
|
||||
unsigned int wantreply = 0;
|
||||
int ret = DROPBEAR_FAILURE;
|
||||
|
||||
TRACE(("enter recv_msg_global_request_remotetcp"));
|
||||
TRACE(("enter recv_msg_global_request_remotetcp"))
|
||||
|
||||
if (opts.noremotetcp) {
|
||||
TRACE(("leave recv_msg_global_request_remotetcp: remote tcp forwarding disabled"));
|
||||
TRACE(("leave recv_msg_global_request_remotetcp: remote tcp forwarding disabled"))
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ void recv_msg_global_request_remotetcp() {
|
||||
wantreply = buf_getbyte(ses.payload);
|
||||
|
||||
if (namelen > MAXNAMLEN) {
|
||||
TRACE(("name len is wrong: %d", namelen));
|
||||
TRACE(("name len is wrong: %d", namelen))
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ void recv_msg_global_request_remotetcp() {
|
||||
} else if (strcmp("cancel-tcpip-forward", reqname) == 0) {
|
||||
ret = svr_cancelremotetcp();
|
||||
} else {
|
||||
TRACE(("reqname isn't tcpip-forward: '%s'", reqname));
|
||||
TRACE(("reqname isn't tcpip-forward: '%s'", reqname))
|
||||
}
|
||||
|
||||
out:
|
||||
@@ -104,7 +104,7 @@ out:
|
||||
|
||||
m_free(reqname);
|
||||
|
||||
TRACE(("leave recv_msg_global_request"));
|
||||
TRACE(("leave recv_msg_global_request"))
|
||||
}
|
||||
|
||||
|
||||
@@ -143,11 +143,11 @@ static int svr_cancelremotetcp() {
|
||||
struct Listener * listener = NULL;
|
||||
struct TCPListener tcpinfo;
|
||||
|
||||
TRACE(("enter cancelremotetcp"));
|
||||
TRACE(("enter cancelremotetcp"))
|
||||
|
||||
bindaddr = buf_getstring(ses.payload, &addrlen);
|
||||
if (addrlen > MAX_IP_LEN) {
|
||||
TRACE(("addr len too long: %d", addrlen));
|
||||
TRACE(("addr len too long: %d", addrlen))
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -163,7 +163,7 @@ static int svr_cancelremotetcp() {
|
||||
|
||||
out:
|
||||
m_free(bindaddr);
|
||||
TRACE(("leave cancelremotetcp"));
|
||||
TRACE(("leave cancelremotetcp"))
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -175,12 +175,12 @@ static int svr_remotetcpreq() {
|
||||
struct TCPListener *tcpinfo = NULL;
|
||||
unsigned int port;
|
||||
|
||||
TRACE(("enter remotetcpreq"));
|
||||
TRACE(("enter remotetcpreq"))
|
||||
|
||||
/* NOTE: at this stage, we ignore bindaddr. see below and listen_tcpfwd */
|
||||
bindaddr = buf_getstring(ses.payload, &addrlen);
|
||||
if (addrlen > MAX_IP_LEN) {
|
||||
TRACE(("addr len too long: %d", addrlen));
|
||||
TRACE(("addr len too long: %d", addrlen))
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -192,12 +192,12 @@ static int svr_remotetcpreq() {
|
||||
}
|
||||
|
||||
if (port < 1 || port > 65535) {
|
||||
TRACE(("invalid port: %d", port));
|
||||
TRACE(("invalid port: %d", port))
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!ses.allowprivport && port < IPPORT_RESERVED) {
|
||||
TRACE(("can't assign port < 1024 for non-root"));
|
||||
TRACE(("can't assign port < 1024 for non-root"))
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -218,7 +218,7 @@ out:
|
||||
m_free(tcpinfo->sendaddr);
|
||||
m_free(tcpinfo);
|
||||
}
|
||||
TRACE(("leave remotetcpreq"));
|
||||
TRACE(("leave remotetcpreq"))
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -236,13 +236,13 @@ static int newtcpdirect(struct Channel * channel) {
|
||||
int err = SSH_OPEN_ADMINISTRATIVELY_PROHIBITED;
|
||||
|
||||
if (opts.nolocaltcp) {
|
||||
TRACE(("leave newtcpdirect: local tcp forwarding disabled"));
|
||||
TRACE(("leave newtcpdirect: local tcp forwarding disabled"))
|
||||
goto out;
|
||||
}
|
||||
|
||||
desthost = buf_getstring(ses.payload, &len);
|
||||
if (len > MAX_HOST_LEN) {
|
||||
TRACE(("leave newtcpdirect: desthost too long"));
|
||||
TRACE(("leave newtcpdirect: desthost too long"))
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -250,7 +250,7 @@ static int newtcpdirect(struct Channel * channel) {
|
||||
|
||||
orighost = buf_getstring(ses.payload, &len);
|
||||
if (len > MAX_HOST_LEN) {
|
||||
TRACE(("leave newtcpdirect: orighost too long"));
|
||||
TRACE(("leave newtcpdirect: orighost too long"))
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -258,7 +258,7 @@ static int newtcpdirect(struct Channel * channel) {
|
||||
|
||||
/* best be sure */
|
||||
if (origport > 65535 || destport > 65535) {
|
||||
TRACE(("leave newtcpdirect: port > 65535"));
|
||||
TRACE(("leave newtcpdirect: port > 65535"))
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -266,7 +266,7 @@ static int newtcpdirect(struct Channel * channel) {
|
||||
sock = connect_remote(desthost, portstring, 1, NULL);
|
||||
if (sock < 0) {
|
||||
err = SSH_OPEN_CONNECT_FAILED;
|
||||
TRACE(("leave newtcpdirect: sock failed"));
|
||||
TRACE(("leave newtcpdirect: sock failed"))
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -284,7 +284,7 @@ static int newtcpdirect(struct Channel * channel) {
|
||||
out:
|
||||
m_free(desthost);
|
||||
m_free(orighost);
|
||||
TRACE(("leave newtcpdirect: err %d", err));
|
||||
TRACE(("leave newtcpdirect: err %d", err))
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
12
svr-x11fwd.c
12
svr-x11fwd.c
@@ -75,9 +75,7 @@ int x11req(struct ChanSess * chansess) {
|
||||
}
|
||||
|
||||
/* set non-blocking */
|
||||
if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
|
||||
goto fail;
|
||||
}
|
||||
setnonblocking(fd);
|
||||
|
||||
/* listener code will handle the socket now.
|
||||
* No cleanup handler needed, since listener_remove only happens
|
||||
@@ -171,8 +169,12 @@ void x11cleanup(struct ChanSess *chansess) {
|
||||
|
||||
m_free(chansess->x11authprot);
|
||||
m_free(chansess->x11authcookie);
|
||||
remove_listener(chansess->x11listener);
|
||||
chansess->x11listener = NULL;
|
||||
|
||||
TRACE(("chansess %s", chansess))
|
||||
if (chansess->x11listener != NULL) {
|
||||
remove_listener(chansess->x11listener);
|
||||
chansess->x11listener = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct ChanType chan_x11 = {
|
||||
|
||||
@@ -87,7 +87,7 @@ int listen_tcpfwd(struct TCPListener* tcpinfo) {
|
||||
int nsocks;
|
||||
char* errstring = NULL;
|
||||
|
||||
TRACE(("enter listen_tcpfwd"));
|
||||
TRACE(("enter listen_tcpfwd"))
|
||||
|
||||
/* first we try to bind, so don't need to do so much cleanup on failure */
|
||||
snprintf(portstring, sizeof(portstring), "%d", tcpinfo->listenport);
|
||||
@@ -100,7 +100,7 @@ int listen_tcpfwd(struct TCPListener* tcpinfo) {
|
||||
if (nsocks < 0) {
|
||||
dropbear_log(LOG_INFO, "TCP forward failed: %s", errstring);
|
||||
m_free(errstring);
|
||||
TRACE(("leave listen_tcpfwd: dropbear_listen failed"));
|
||||
TRACE(("leave listen_tcpfwd: dropbear_listen failed"))
|
||||
return DROPBEAR_FAILURE;
|
||||
}
|
||||
|
||||
@@ -109,11 +109,11 @@ int listen_tcpfwd(struct TCPListener* tcpinfo) {
|
||||
|
||||
if (listener == NULL) {
|
||||
m_free(tcpinfo);
|
||||
TRACE(("leave listen_tcpfwd: listener failed"));
|
||||
TRACE(("leave listen_tcpfwd: listener failed"))
|
||||
return DROPBEAR_FAILURE;
|
||||
}
|
||||
|
||||
TRACE(("leave listen_tcpfwd: success"));
|
||||
TRACE(("leave listen_tcpfwd: success"))
|
||||
return DROPBEAR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
3
tcpfwd.h
3
tcpfwd.h
@@ -47,7 +47,7 @@ struct TCPListener {
|
||||
/* A link in a list of forwards */
|
||||
struct TCPFwdList {
|
||||
|
||||
char* connectaddr;
|
||||
const unsigned char* connectaddr;
|
||||
unsigned int connectport;
|
||||
unsigned int listenport;
|
||||
struct TCPFwdList * next;
|
||||
@@ -60,6 +60,7 @@ extern const struct ChanType svr_chan_tcpdirect;
|
||||
|
||||
/* Client */
|
||||
void setup_localtcp();
|
||||
void setup_remotetcp();
|
||||
extern const struct ChanType cli_chan_tcpremote;
|
||||
|
||||
/* Common */
|
||||
|
||||
@@ -131,7 +131,11 @@ const struct TermCode termcodes[MAX_TERMCODE+1] = {
|
||||
{IEXTEN, TERMCODE_LOCAL},
|
||||
{ECHOCTL, TERMCODE_LOCAL},
|
||||
{ECHOKE, TERMCODE_LOCAL},
|
||||
#ifdef PENDIN
|
||||
{PENDIN, TERMCODE_LOCAL},
|
||||
#else
|
||||
{0, 0},
|
||||
#endif
|
||||
{0, 0}, /* 63 */
|
||||
{0, 0},
|
||||
{0, 0},
|
||||
|
||||
Reference in New Issue
Block a user