summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatt Johnston <matt@ucc.asn.au>2006-03-08 13:23:49 +0000
committerMatt Johnston <matt@ucc.asn.au>2006-03-08 13:23:49 +0000
commit475aa151f9618036b90a25cf529517d04e61977f (patch)
tree453ebf8873dd287a2def6a674d42afcc6637cde2
parent2514a6739b3268c98ef09863eff52ed086e4b6f9 (diff)
parent2b43e46e826fe9905f36b8920386e13f1f47ed65 (diff)
downloaddropbear-475aa151f9618036b90a25cf529517d04e61977f.tar.gz
propagate from branch 'au.asn.ucc.matt.ltm.dropbear' (head 6c790cad5a7fa866ad062cb3a0c279f7ba788583)
to branch 'au.asn.ucc.matt.dropbear' (head fff0894a0399405a9410ea1c6d118f342cf2aa64)
-rw-r--r--CHANGES519
-rw-r--r--INSTALL79
-rw-r--r--LICENSE89
-rw-r--r--MULTI26
-rw-r--r--Makefile.in362
-rw-r--r--README74
-rw-r--r--SMALL53
-rw-r--r--TODO36
-rw-r--r--agentfwd.h43
-rw-r--r--algo.h74
-rw-r--r--atomicio.c63
-rw-r--r--atomicio.h36
-rw-r--r--auth.h111
-rw-r--r--bignum.c75
-rw-r--r--bignum.h35
-rw-r--r--buffer.c338
-rw-r--r--buffer.h66
-rw-r--r--channel.h135
-rw-r--r--chansession.h92
-rw-r--r--circbuffer.c138
-rw-r--r--circbuffer.h50
-rw-r--r--cli-algo.c99
-rw-r--r--cli-auth.c295
-rw-r--r--cli-authinteract.c168
-rw-r--r--cli-authpasswd.c150
-rw-r--r--cli-authpubkey.c187
-rw-r--r--cli-channel.c62
-rw-r--r--cli-chansession.c380
-rw-r--r--cli-kex.c288
-rw-r--r--cli-main.c112
-rw-r--r--cli-runopts.c414
-rw-r--r--cli-service.c87
-rw-r--r--cli-session.c303
-rw-r--r--cli-tcpfwd.c216
-rw-r--r--common-algo.c228
-rw-r--r--common-channel.c1038
-rw-r--r--common-chansession.c43
-rw-r--r--common-kex.c718
-rw-r--r--common-runopts.c57
-rw-r--r--common-session.c373
-rw-r--r--compat.c281
-rw-r--r--compat.h56
-rw-r--r--config.guess1391
-rw-r--r--config.sub1492
-rw-r--r--configure.in617
-rw-r--r--dbclient.174
-rw-r--r--dbmulti.c90
-rw-r--r--dbutil.c679
-rw-r--r--dbutil.h73
-rw-r--r--debian/README.Debian41
-rw-r--r--debian/README.Debian.diet15
-rw-r--r--debian/README.runit46
-rw-r--r--debian/changelog194
-rw-r--r--debian/control20
-rw-r--r--debian/copyright.in11
-rw-r--r--debian/dropbear.README.Debian41
-rw-r--r--debian/dropbear.conffiles3
-rw-r--r--debian/dropbear.default (renamed from logs/invmod.log)0
-rw-r--r--debian/dropbear.docs4
-rw-r--r--debian/dropbear.init61
-rw-r--r--debian/dropbear.postinst67
-rw-r--r--debian/dropbear.postrm12
-rw-r--r--debian/dropbear.prerm11
-rw-r--r--debian/implicit85
-rwxr-xr-xdebian/rules107
-rw-r--r--debian/service/log2
-rw-r--r--debian/service/run3
-rw-r--r--debug.h74
-rw-r--r--dropbear.884
-rw-r--r--dropbearconvert.c149
-rw-r--r--dropbearkey.847
-rw-r--r--dropbearkey.c355
-rw-r--r--dss.c416
-rw-r--r--dss.h61
-rw-r--r--fake-rfc2553.c227
-rw-r--r--fake-rfc2553.h162
-rw-r--r--filelist.txt117
-rw-r--r--gendss.c198
-rw-r--r--gendss.h36
-rw-r--r--genrsa.c137
-rw-r--r--genrsa.h36
-rw-r--r--includes.h155
-rw-r--r--install-sh251
-rw-r--r--kex.h64
-rw-r--r--keyimport.c1730
-rw-r--r--keyimport.h42
-rw-r--r--libtommath/LICENSE4
-rw-r--r--libtommath/Makefile.in165
-rw-r--r--libtommath/TODO16
-rw-r--r--libtommath/bn.tex (renamed from bn.tex)0
-rw-r--r--libtommath/bn_error.c (renamed from bn_error.c)0
-rw-r--r--libtommath/bn_fast_mp_invmod.c (renamed from bn_fast_mp_invmod.c)0
-rw-r--r--libtommath/bn_fast_mp_montgomery_reduce.c (renamed from bn_fast_mp_montgomery_reduce.c)0
-rw-r--r--libtommath/bn_fast_s_mp_mul_digs.c (renamed from bn_fast_s_mp_mul_digs.c)0
-rw-r--r--libtommath/bn_fast_s_mp_mul_high_digs.c (renamed from bn_fast_s_mp_mul_high_digs.c)0
-rw-r--r--libtommath/bn_fast_s_mp_sqr.c (renamed from bn_fast_s_mp_sqr.c)0
-rw-r--r--libtommath/bn_mp_2expt.c (renamed from bn_mp_2expt.c)0
-rw-r--r--libtommath/bn_mp_abs.c (renamed from bn_mp_abs.c)0
-rw-r--r--libtommath/bn_mp_add.c (renamed from bn_mp_add.c)0
-rw-r--r--libtommath/bn_mp_add_d.c (renamed from bn_mp_add_d.c)0
-rw-r--r--libtommath/bn_mp_addmod.c (renamed from bn_mp_addmod.c)0
-rw-r--r--libtommath/bn_mp_and.c (renamed from bn_mp_and.c)0
-rw-r--r--libtommath/bn_mp_clamp.c (renamed from bn_mp_clamp.c)0
-rw-r--r--libtommath/bn_mp_clear.c (renamed from bn_mp_clear.c)0
-rw-r--r--libtommath/bn_mp_clear_multi.c (renamed from bn_mp_clear_multi.c)0
-rw-r--r--libtommath/bn_mp_cmp.c (renamed from bn_mp_cmp.c)0
-rw-r--r--libtommath/bn_mp_cmp_d.c (renamed from bn_mp_cmp_d.c)0
-rw-r--r--libtommath/bn_mp_cmp_mag.c (renamed from bn_mp_cmp_mag.c)0
-rw-r--r--libtommath/bn_mp_cnt_lsb.c (renamed from bn_mp_cnt_lsb.c)0
-rw-r--r--libtommath/bn_mp_copy.c (renamed from bn_mp_copy.c)0
-rw-r--r--libtommath/bn_mp_count_bits.c (renamed from bn_mp_count_bits.c)0
-rw-r--r--libtommath/bn_mp_div.c (renamed from bn_mp_div.c)0
-rw-r--r--libtommath/bn_mp_div_2.c (renamed from bn_mp_div_2.c)0
-rw-r--r--libtommath/bn_mp_div_2d.c (renamed from bn_mp_div_2d.c)0
-rw-r--r--libtommath/bn_mp_div_3.c (renamed from bn_mp_div_3.c)0
-rw-r--r--libtommath/bn_mp_div_d.c (renamed from bn_mp_div_d.c)0
-rw-r--r--libtommath/bn_mp_dr_is_modulus.c (renamed from bn_mp_dr_is_modulus.c)0
-rw-r--r--libtommath/bn_mp_dr_reduce.c (renamed from bn_mp_dr_reduce.c)0
-rw-r--r--libtommath/bn_mp_dr_setup.c (renamed from bn_mp_dr_setup.c)0
-rw-r--r--libtommath/bn_mp_exch.c (renamed from bn_mp_exch.c)0
-rw-r--r--libtommath/bn_mp_expt_d.c (renamed from bn_mp_expt_d.c)0
-rw-r--r--libtommath/bn_mp_exptmod.c (renamed from bn_mp_exptmod.c)0
-rw-r--r--libtommath/bn_mp_exptmod_fast.c (renamed from bn_mp_exptmod_fast.c)0
-rw-r--r--libtommath/bn_mp_exteuclid.c (renamed from bn_mp_exteuclid.c)0
-rw-r--r--libtommath/bn_mp_fread.c (renamed from bn_mp_fread.c)0
-rw-r--r--libtommath/bn_mp_fwrite.c (renamed from bn_mp_fwrite.c)0
-rw-r--r--libtommath/bn_mp_gcd.c (renamed from bn_mp_gcd.c)0
-rw-r--r--libtommath/bn_mp_get_int.c (renamed from bn_mp_get_int.c)0
-rw-r--r--libtommath/bn_mp_grow.c (renamed from bn_mp_grow.c)0
-rw-r--r--libtommath/bn_mp_init.c (renamed from bn_mp_init.c)0
-rw-r--r--libtommath/bn_mp_init_copy.c (renamed from bn_mp_init_copy.c)0
-rw-r--r--libtommath/bn_mp_init_multi.c (renamed from bn_mp_init_multi.c)0
-rw-r--r--libtommath/bn_mp_init_set.c (renamed from bn_mp_init_set.c)0
-rw-r--r--libtommath/bn_mp_init_set_int.c (renamed from bn_mp_init_set_int.c)0
-rw-r--r--libtommath/bn_mp_init_size.c (renamed from bn_mp_init_size.c)0
-rw-r--r--libtommath/bn_mp_invmod.c (renamed from bn_mp_invmod.c)0
-rw-r--r--libtommath/bn_mp_invmod_slow.c (renamed from bn_mp_invmod_slow.c)0
-rw-r--r--libtommath/bn_mp_is_square.c (renamed from bn_mp_is_square.c)0
-rw-r--r--libtommath/bn_mp_jacobi.c (renamed from bn_mp_jacobi.c)0
-rw-r--r--libtommath/bn_mp_karatsuba_mul.c (renamed from bn_mp_karatsuba_mul.c)0
-rw-r--r--libtommath/bn_mp_karatsuba_sqr.c (renamed from bn_mp_karatsuba_sqr.c)0
-rw-r--r--libtommath/bn_mp_lcm.c (renamed from bn_mp_lcm.c)0
-rw-r--r--libtommath/bn_mp_lshd.c (renamed from bn_mp_lshd.c)0
-rw-r--r--libtommath/bn_mp_mod.c (renamed from bn_mp_mod.c)0
-rw-r--r--libtommath/bn_mp_mod_2d.c (renamed from bn_mp_mod_2d.c)0
-rw-r--r--libtommath/bn_mp_mod_d.c (renamed from bn_mp_mod_d.c)0
-rw-r--r--libtommath/bn_mp_montgomery_calc_normalization.c (renamed from bn_mp_montgomery_calc_normalization.c)0
-rw-r--r--libtommath/bn_mp_montgomery_reduce.c (renamed from bn_mp_montgomery_reduce.c)0
-rw-r--r--libtommath/bn_mp_montgomery_setup.c (renamed from bn_mp_montgomery_setup.c)0
-rw-r--r--libtommath/bn_mp_mul.c (renamed from bn_mp_mul.c)0
-rw-r--r--libtommath/bn_mp_mul_2.c (renamed from bn_mp_mul_2.c)0
-rw-r--r--libtommath/bn_mp_mul_2d.c (renamed from bn_mp_mul_2d.c)0
-rw-r--r--libtommath/bn_mp_mul_d.c (renamed from bn_mp_mul_d.c)0
-rw-r--r--libtommath/bn_mp_mulmod.c (renamed from bn_mp_mulmod.c)0
-rw-r--r--libtommath/bn_mp_n_root.c (renamed from bn_mp_n_root.c)0
-rw-r--r--libtommath/bn_mp_neg.c (renamed from bn_mp_neg.c)0
-rw-r--r--libtommath/bn_mp_or.c (renamed from bn_mp_or.c)0
-rw-r--r--libtommath/bn_mp_prime_fermat.c (renamed from bn_mp_prime_fermat.c)0
-rw-r--r--libtommath/bn_mp_prime_is_divisible.c (renamed from bn_mp_prime_is_divisible.c)0
-rw-r--r--libtommath/bn_mp_prime_is_prime.c (renamed from bn_mp_prime_is_prime.c)0
-rw-r--r--libtommath/bn_mp_prime_miller_rabin.c (renamed from bn_mp_prime_miller_rabin.c)0
-rw-r--r--libtommath/bn_mp_prime_next_prime.c (renamed from bn_mp_prime_next_prime.c)0
-rw-r--r--libtommath/bn_mp_prime_rabin_miller_trials.c (renamed from bn_mp_prime_rabin_miller_trials.c)0
-rw-r--r--libtommath/bn_mp_prime_random_ex.c (renamed from bn_mp_prime_random_ex.c)0
-rw-r--r--libtommath/bn_mp_radix_size.c (renamed from bn_mp_radix_size.c)0
-rw-r--r--libtommath/bn_mp_radix_smap.c (renamed from bn_mp_radix_smap.c)0
-rw-r--r--libtommath/bn_mp_rand.c (renamed from bn_mp_rand.c)0
-rw-r--r--libtommath/bn_mp_read_radix.c (renamed from bn_mp_read_radix.c)0
-rw-r--r--libtommath/bn_mp_read_signed_bin.c (renamed from bn_mp_read_signed_bin.c)0
-rw-r--r--libtommath/bn_mp_read_unsigned_bin.c (renamed from bn_mp_read_unsigned_bin.c)0
-rw-r--r--libtommath/bn_mp_reduce.c (renamed from bn_mp_reduce.c)0
-rw-r--r--libtommath/bn_mp_reduce_2k.c (renamed from bn_mp_reduce_2k.c)0
-rw-r--r--libtommath/bn_mp_reduce_2k_l.c (renamed from bn_mp_reduce_2k_l.c)0
-rw-r--r--libtommath/bn_mp_reduce_2k_setup.c (renamed from bn_mp_reduce_2k_setup.c)0
-rw-r--r--libtommath/bn_mp_reduce_2k_setup_l.c (renamed from bn_mp_reduce_2k_setup_l.c)0
-rw-r--r--libtommath/bn_mp_reduce_is_2k.c (renamed from bn_mp_reduce_is_2k.c)0
-rw-r--r--libtommath/bn_mp_reduce_is_2k_l.c (renamed from bn_mp_reduce_is_2k_l.c)0
-rw-r--r--libtommath/bn_mp_reduce_setup.c (renamed from bn_mp_reduce_setup.c)0
-rw-r--r--libtommath/bn_mp_rshd.c (renamed from bn_mp_rshd.c)0
-rw-r--r--libtommath/bn_mp_set.c (renamed from bn_mp_set.c)0
-rw-r--r--libtommath/bn_mp_set_int.c (renamed from bn_mp_set_int.c)0
-rw-r--r--libtommath/bn_mp_shrink.c (renamed from bn_mp_shrink.c)0
-rw-r--r--libtommath/bn_mp_signed_bin_size.c (renamed from bn_mp_signed_bin_size.c)0
-rw-r--r--libtommath/bn_mp_sqr.c (renamed from bn_mp_sqr.c)0
-rw-r--r--libtommath/bn_mp_sqrmod.c (renamed from bn_mp_sqrmod.c)0
-rw-r--r--libtommath/bn_mp_sqrt.c (renamed from bn_mp_sqrt.c)0
-rw-r--r--libtommath/bn_mp_sub.c (renamed from bn_mp_sub.c)0
-rw-r--r--libtommath/bn_mp_sub_d.c (renamed from bn_mp_sub_d.c)0
-rw-r--r--libtommath/bn_mp_submod.c (renamed from bn_mp_submod.c)0
-rw-r--r--libtommath/bn_mp_to_signed_bin.c (renamed from bn_mp_to_signed_bin.c)0
-rw-r--r--libtommath/bn_mp_to_signed_bin_n.c (renamed from bn_mp_to_signed_bin_n.c)0
-rw-r--r--libtommath/bn_mp_to_unsigned_bin.c (renamed from bn_mp_to_unsigned_bin.c)0
-rw-r--r--libtommath/bn_mp_to_unsigned_bin_n.c (renamed from bn_mp_to_unsigned_bin_n.c)0
-rw-r--r--libtommath/bn_mp_toom_mul.c (renamed from bn_mp_toom_mul.c)0
-rw-r--r--libtommath/bn_mp_toom_sqr.c (renamed from bn_mp_toom_sqr.c)0
-rw-r--r--libtommath/bn_mp_toradix.c (renamed from bn_mp_toradix.c)0
-rw-r--r--libtommath/bn_mp_toradix_n.c (renamed from bn_mp_toradix_n.c)0
-rw-r--r--libtommath/bn_mp_unsigned_bin_size.c (renamed from bn_mp_unsigned_bin_size.c)0
-rw-r--r--libtommath/bn_mp_xor.c (renamed from bn_mp_xor.c)0
-rw-r--r--libtommath/bn_mp_zero.c (renamed from bn_mp_zero.c)0
-rw-r--r--libtommath/bn_prime_tab.c (renamed from bn_prime_tab.c)0
-rw-r--r--libtommath/bn_reverse.c (renamed from bn_reverse.c)0
-rw-r--r--libtommath/bn_s_mp_add.c (renamed from bn_s_mp_add.c)0
-rw-r--r--libtommath/bn_s_mp_exptmod.c (renamed from bn_s_mp_exptmod.c)0
-rw-r--r--libtommath/bn_s_mp_mul_digs.c (renamed from bn_s_mp_mul_digs.c)0
-rw-r--r--libtommath/bn_s_mp_mul_high_digs.c (renamed from bn_s_mp_mul_high_digs.c)0
-rw-r--r--libtommath/bn_s_mp_sqr.c (renamed from bn_s_mp_sqr.c)0
-rw-r--r--libtommath/bn_s_mp_sub.c (renamed from bn_s_mp_sub.c)0
-rw-r--r--libtommath/bncore.c (renamed from bncore.c)0
-rw-r--r--libtommath/booker.pl (renamed from booker.pl)0
-rw-r--r--libtommath/callgraph.txt (renamed from callgraph.txt)0
-rw-r--r--libtommath/changes.txt (renamed from changes.txt)0
-rw-r--r--libtommath/demo/demo.c (renamed from demo/demo.c)0
-rw-r--r--libtommath/demo/timing.c (renamed from demo/timing.c)0
-rw-r--r--libtommath/dep.pl (renamed from dep.pl)0
-rw-r--r--libtommath/etc/2kprime.1 (renamed from etc/2kprime.1)0
-rw-r--r--libtommath/etc/2kprime.c (renamed from etc/2kprime.c)0
-rw-r--r--libtommath/etc/drprime.c (renamed from etc/drprime.c)0
-rw-r--r--libtommath/etc/drprimes.28 (renamed from etc/drprimes.28)0
-rw-r--r--libtommath/etc/drprimes.txt (renamed from etc/drprimes.txt)0
-rw-r--r--libtommath/etc/makefile (renamed from etc/makefile)0
-rw-r--r--libtommath/etc/makefile.icc (renamed from etc/makefile.icc)0
-rw-r--r--libtommath/etc/makefile.msvc (renamed from etc/makefile.msvc)0
-rw-r--r--libtommath/etc/mersenne.c (renamed from etc/mersenne.c)0
-rw-r--r--libtommath/etc/mont.c (renamed from etc/mont.c)0
-rw-r--r--libtommath/etc/pprime.c (renamed from etc/pprime.c)0
-rw-r--r--libtommath/etc/prime.1024 (renamed from etc/prime.1024)0
-rw-r--r--libtommath/etc/prime.512 (renamed from etc/prime.512)0
-rw-r--r--libtommath/etc/timer.asm (renamed from etc/timer.asm)0
-rw-r--r--libtommath/etc/tune.c (renamed from etc/tune.c)0
-rw-r--r--libtommath/gen.pl (renamed from gen.pl)0
-rw-r--r--libtommath/logs/README (renamed from logs/README)0
-rw-r--r--libtommath/logs/add.log (renamed from logs/add.log)0
-rw-r--r--libtommath/logs/addsub.png (renamed from logs/addsub.png)bin6254 -> 6254 bytes
-rw-r--r--libtommath/logs/expt.log (renamed from logs/expt.log)0
-rw-r--r--libtommath/logs/expt.png (renamed from logs/expt.png)bin6605 -> 6605 bytes
-rw-r--r--libtommath/logs/expt_2k.log (renamed from logs/expt_2k.log)0
-rw-r--r--libtommath/logs/expt_2kl.log (renamed from logs/expt_2kl.log)0
-rw-r--r--libtommath/logs/expt_dr.log (renamed from logs/expt_dr.log)0
-rw-r--r--libtommath/logs/graphs.dem (renamed from logs/graphs.dem)0
-rw-r--r--libtommath/logs/index.html (renamed from logs/index.html)0
-rw-r--r--libtommath/logs/invmod.log (renamed from poster.out)0
-rw-r--r--libtommath/logs/invmod.png (renamed from logs/invmod.png)bin4918 -> 4918 bytes
-rw-r--r--libtommath/logs/mult.log (renamed from logs/mult.log)0
-rw-r--r--libtommath/logs/mult.png (renamed from logs/mult.png)bin6770 -> 6770 bytes
-rw-r--r--libtommath/logs/mult_kara.log (renamed from logs/mult_kara.log)0
-rw-r--r--libtommath/logs/sqr.log (renamed from logs/sqr.log)0
-rw-r--r--libtommath/logs/sqr.old (renamed from logs/sqr.old)0
-rw-r--r--libtommath/logs/sqr_kara.log (renamed from logs/sqr_kara.log)0
-rw-r--r--libtommath/logs/sub.log (renamed from logs/sub.log)0
-rw-r--r--libtommath/makefile.bcc (renamed from makefile.bcc)0
-rw-r--r--libtommath/makefile.cygwin_dll (renamed from makefile.cygwin_dll)0
-rw-r--r--libtommath/makefile.icc (renamed from makefile.icc)0
-rw-r--r--libtommath/makefile.msvc (renamed from makefile.msvc)0
-rw-r--r--libtommath/makefile.shared (renamed from makefile.shared)0
-rw-r--r--libtommath/mtest/logtab.h (renamed from mtest/logtab.h)0
-rw-r--r--libtommath/mtest/mpi-config.h (renamed from mtest/mpi-config.h)0
-rw-r--r--libtommath/mtest/mpi-types.h (renamed from mtest/mpi-types.h)0
-rw-r--r--libtommath/mtest/mpi.c (renamed from mtest/mpi.c)0
-rw-r--r--libtommath/mtest/mpi.h (renamed from mtest/mpi.h)0
-rw-r--r--libtommath/mtest/mtest.c (renamed from mtest/mtest.c)0
-rw-r--r--libtommath/pics/design_process.sxd (renamed from pics/design_process.sxd)bin6950 -> 6950 bytes
-rw-r--r--libtommath/pics/design_process.tif (renamed from pics/design_process.tif)bin79042 -> 79042 bytes
-rw-r--r--libtommath/pics/expt_state.sxd (renamed from pics/expt_state.sxd)bin6869 -> 6869 bytes
-rw-r--r--libtommath/pics/expt_state.tif (renamed from pics/expt_state.tif)bin87542 -> 87542 bytes
-rw-r--r--libtommath/pics/makefile (renamed from pics/makefile)0
-rw-r--r--libtommath/pics/primality.tif (renamed from pics/primality.tif)bin85514 -> 85514 bytes
-rw-r--r--libtommath/pics/radix.sxd (renamed from pics/radix.sxd)bin6181 -> 6181 bytes
-rw-r--r--libtommath/pics/sliding_window.sxd (renamed from pics/sliding_window.sxd)bin6787 -> 6787 bytes
-rw-r--r--libtommath/pics/sliding_window.tif (renamed from pics/sliding_window.tif)bin53880 -> 53880 bytes
-rw-r--r--libtommath/poster.out0
-rw-r--r--libtommath/poster.tex (renamed from poster.tex)0
-rw-r--r--libtommath/pre_gen/mpi.c (renamed from pre_gen/mpi.c)0
-rw-r--r--libtommath/pretty.build (renamed from pretty.build)0
-rw-r--r--libtommath/tombc/grammar.txt (renamed from tombc/grammar.txt)0
-rw-r--r--libtommath/tommath.h (renamed from tommath.h)0
-rw-r--r--libtommath/tommath.out (renamed from tommath.out)0
-rw-r--r--libtommath/tommath.src (renamed from tommath.src)0
-rw-r--r--libtommath/tommath.tex (renamed from tommath.tex)0
-rw-r--r--libtommath/tommath_class.h (renamed from tommath_class.h)0
-rw-r--r--libtommath/tommath_superclass.h (renamed from tommath_superclass.h)0
-rw-r--r--listener.c165
-rw-r--r--listener.h63
-rw-r--r--loginrec.c1394
-rw-r--r--loginrec.h185
-rw-r--r--options.h401
-rw-r--r--packet.c613
-rw-r--r--packet.h48
-rw-r--r--process-packet.c145
-rw-r--r--progressmeter.c267
-rw-r--r--progressmeter.h27
-rw-r--r--queue.c89
-rw-r--r--queue.h49
-rw-r--r--random.c241
-rw-r--r--random.h36
-rw-r--r--rsa.c398
-rw-r--r--rsa.h61
-rw-r--r--runopts.h119
-rw-r--r--scp.c1199
-rw-r--r--scpmisc.c161
-rw-r--r--scpmisc.h44
-rw-r--r--service.h32
-rw-r--r--session.h257
-rw-r--r--signkey.c484
-rw-r--r--signkey.h63
-rw-r--r--ssh.h107
-rw-r--r--sshpty.c412
-rw-r--r--sshpty.h28
-rw-r--r--svr-agentfwd.c266
-rw-r--r--svr-algo.c100
-rw-r--r--svr-auth.c373
-rw-r--r--svr-authpam.c258
-rw-r--r--svr-authpasswd.c105
-rw-r--r--svr-authpubkey.c347
-rw-r--r--svr-chansession.c1003
-rw-r--r--svr-kex.c104
-rw-r--r--svr-main.c421
-rw-r--r--svr-runopts.c315
-rw-r--r--svr-service.c87
-rw-r--r--svr-session.c201
-rw-r--r--svr-tcpfwd.c290
-rw-r--r--svr-x11fwd.c236
-rw-r--r--tcp-accept.c143
-rw-r--r--tcpfwd.h69
-rw-r--r--termcodes.c187
-rw-r--r--termcodes.h46
-rw-r--r--x11fwd.h37
327 files changed, 29218 insertions, 172 deletions
diff --git a/CHANGES b/CHANGES
new file mode 100644
index 0000000..ffe9ff2
--- /dev/null
+++ b/CHANGES
@@ -0,0 +1,519 @@
+0.47 - Thurs Dec 8 2005
+
+- SECURITY: fix for buffer allocation error in server code, could potentially
+ allow authenticated users to gain elevated privileges. All multi-user systems
+ running the server should upgrade (or apply the patch available on the
+ Dropbear webpage).
+
+- Fix channel handling code so that redirecting to /dev/null doesn't use
+ 100% CPU.
+
+- Turn on zlib compression for dbclient.
+
+- Set "low delay" TOS bit, can significantly improve interactivity
+ over some links.
+
+- Added client keyboard-interactive mode support, allows operation with
+ newer OpenSSH servers in default config.
+
+- Log when pubkey auth fails because of bad ~/.ssh/authorized_keys permissions
+
+- Improve logging of assertions
+
+- Added aes-256 cipher and sha1-96 hmac.
+
+- Fix twofish so that it actually works.
+
+- Improve PAM prompt comparison.
+
+- Added -g (dbclient) and -a (dropbear server) options to allow
+ connections to listening forwarded ports from remote machines.
+
+- Various other minor fixes
+
+- Compile fixes for glibc 2.1 (ss_family vs __ss_family) and NetBSD
+ (netinet/in_systm.h needs to be included).
+
+0.46 - Sat July 9 2005
+
+- Fix long-standing bug which caused connections to be closed if an ssh-agent
+ socket was no longer available
+
+- Print a warning if we seem to be blocking on /dev/random
+ (suggested by Paul Fox)
+
+- Fixed a memory leak in DSS code (thanks to Boris Berezovsky for the patch)
+
+- dbclient -L no longer segfaults, allocate correct buffer size (thanks
+ to David Cook for reporting it, and Christopher Faylor for independently
+ sending in a patch)
+
+- Added RSA blinding to signing code (suggested by Dan Kaminsky)
+
+- Rearranged bignum reading/random generation code
+
+- Reset the non-blocking status on stderr and stdout as well as stdin,
+ fixes a problem where the shell running dbclient will exit (thanks to
+ Brent Roman for reporting it)
+
+- Fix so that all file descriptors are closed so the child shell doesn't
+ inherit descriptors (thanks to Linden May for the patch)
+
+- Change signkey.c to avoid gcc 4 generating incorrect code
+
+- After both sides of a file descriptor have been shutdown(), close()
+ it to avoid leaking descriptors (thanks to Ari Hyttinen for a patch)
+
+- Update to LibTomCrypt 1.05 and LibTomMath 0.35
+
+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
+ the server portion, the final binary size probably will increase - I'll
+ be trying to get it back down in future releases.
+
+- Dropbear client added - lots of changes to the server code as well to
+ generalise things
+
+- IPv6 support added for client, server, and forwarding
+
+- New makefile with more generic support for multiple-program binaries
+
+0.43 - Fri Jul 16 2004 17:44:54 +0800
+
+- SECURITY: Don't try to free() uninitialised variables in DSS verification
+ code. Thanks to Arne Bernin for pointing out this bug. This is possibly
+ exploitable, all users with DSS and pubkey-auth compiled in are advised to
+ upgrade.
+
+- Clean up agent forwarding socket files correctly, patch from Gerrit Pape.
+
+- Don't go into an infinite loop when portforwarding to servers which don't
+ send any initial data/banner. Patch from Nikola Vladov
+
+- Fix for network vs. host byte order in logging remote TCP ports, also
+ from Gerrit Pape.
+
+- Initialise many pointers to NULL, for general safety. Also checked cleanup
+ code for mp_ints (related to security issues above).
+
+0.42 - Wed Jun 16 2004 12:44:54 +0800
+
+- Updated to Gerrit Pape's official Debian subdirectory
+
+- Fixed bad check when opening /dev/urandom - thanks to Danny Sung.
+
+- Added -i inetd mode flag, and associated options in options.h . Dropbear
+ can be compiled with either normal mode, inetd, or both modes. Thanks
+ to Gerrit Pape for basic patch and motivation.
+
+- Use <dirent.h> rather than <sys/dir.h> for POSIX compliance. Thanks to Bill
+ Sommerfield.
+
+- Fixed a TCP forwarding (client-local, -L style) bug which caused the whole
+ session to close if the TCP connection failed. Thanks to Andrew Braund for
+ reporting it and helping track it down.
+
+- Re-enable sigpipe for child processes. Thanks to Gerrit Pape for some
+ suggestions, and BSD manpages for a clearer explanation of the behaviour.
+
+- Added manpages, thanks to Gerrit Pape.
+
+- Changed license text for LibTomCrypt and LibTomMath.
+
+- Added strip-static target
+
+- Fixed a bug in agent-forwarding cleanup handler - would segfault
+ (dereferencing a null pointer) if agent forwarding had failed.
+
+- Fix behaviour of authorized_keys parsing, so larger (>1024 bit) DSA keys will
+ work. Thanks to Dr. Markus Waldeck for the report.
+
+- Fixed local port forwarding code so that the "-j" option will make forwarding
+ attempts fail more gracefully.
+
+- Allow repeated requests in a single session if previous ones fail - this fixes PuTTY and some other SCP clients, which try SFTP, then fall-back to SCP if it
+ isn't available. Thanks to Stirling Westrup for the report.
+
+- Updated to LibTomCrypt 0.96 and LibTomMath 0.30. The AES code now uses
+ smaller non-precomputed tables if DROPBEAR_SMALL_CODE is defined in
+ options.h, leading to a significant reduction in the binary size.
+
+0.41 - Mon Jan 19 2004 22:40:19 +0800
+
+- Fix in configure so that cross-compiling works, thanks to numerous people for
+ reporting and testing
+
+- Terminal mode parsing now handles empty terminal mode strings (sent by
+ Windows ssh.com clients), thanks to Ricardo Derbes for the report
+
+- Handling is improved for users with no shell specified in /etc/passwd,
+ thanks again to Ricardo Derbes
+
+- Fix for compiling with --disable-syslog, thanks to gordonfh
+
+- Various minor fixes allow scp to work with irix, thanks to Paul Marinceu for
+ fixing it up
+
+- Use <stropts.h> not <sys/stropts.h>, since the former seems more common
+
+0.40 - Tue Jan 13 2004 21:05:19 +0800
+
+- Remote TCP forwarding (-R) style implemented
+
+- Local and remote TCP forwarding can each be disabled at runtime (-k and -j
+ switches)
+
+- Fix for problems detecting openpty() with uClibc - many thanks to various
+ people for reporting and testing fixes, including (in random order) Cristian
+ Ionescu-Idbohrn, James Ewing, Steve Dover, Thomas Lundquist and Frederic
+ Lavernhe
+
+- Improved portability for IRIX, thanks to Paul Marinceu
+
+- AIX and HPUX portability fixes, thanks to Darren Tucker for patches
+
+- prngd should now work correctly, thanks to Darren Tucker for the patch
+
+- scp compilation on systems without strlcpy() is fixed, thanks to Peter
+ Jannesen and David Muse for reporting it (independently and simultaneously :)
+
+- Merged in new LibTomCrypt 0.92 and LibTomMath 0.28
+
+0.39 - Tue Dec 16 2003 15:19:19 +0800
+
+- Better checking of key lengths and parameters for DSS and RSA auth
+
+- Print fingerprint of keys used for pubkey auth
+
+- More consistent logging of usernames and IPs
+
+- Added option to disable password auth (or just for root) at runtime
+
+- Avoid including bignum functions which don't give much speed benefit but
+ take up binary size
+
+- Added a stripped down version of OpenSSH's scp binary
+
+- Added additional supporting functions for Irix, thanks to Paul Marinceu
+
+- Don't check for unused libraries in configure script
+
+- Removed trailing comma in algorithm lists (thanks to Mihnea Stoenescu)
+
+- Fixed up channel close handling, always send close packet in response
+ (also thanks to Mihnea Stoenescu)
+
+- Various makefile improvements for cross-compiling, thanks to Friedrich
+ Lobenstock and Mihnea Stoenescu
+
+- Use daemon() function if available (or our own copy) rather than separate
+ code (thanks to Frédéric Lavernhe for the report and debugging, and Bernard
+ Blackham for his suggestion on what to look at)
+
+- Fixed up support for first_kex_packet_follows, required to talk to ssh.com
+ clients. Thanks to Marian Stagarescu for the bug report.
+
+- Avoid using MAXPATHLEN, pointer from Ian Morris
+
+- Improved input sanity checking
+
+0.38 - Sat Oct 11 2003 16:28:13 +0800
+
+- Default hostkey path changed to /etc/dropbear/dropbear_{rsa,dss}_host_key
+ rather than /etc/dropbear_{rsa,dss}_host_key
+
+- Added SMALL and MULTI text files which have info on compiling for multiple
+ binaries or small binaries
+
+- Allow for commandline definition of some options.h settings
+ (without warnings)
+
+- Be more careful handling EINTR
+
+- More fixes for channel closing
+
+- Added multi-binary support
+
+- Improved logging of IPs, now get logged in all cases
+
+- Don't chew cpu when waiting for version identification string, also
+ make sure that we kick off people if they don't auth within 5 minutes.
+
+- Various small fixes, warnings etc
+
+- Display MOTD if requested - suggested by
+ Trent Lloyd <lathiat at sixlabs.org> and
+ Zach White <zwhite at darkstar.frop.org>
+
+- sftp support works (relies on OpenSSH sftp binary or similar)
+
+- Added --disable-shadow option (requested by the floppyfw guys)
+
+0.37 - Wed Sept 24 2003 19:42:12 +0800
+
+- Various portability fixes, fixes for Solaris 9, Tru64 5.1, Mac OS X 10.2,
+ AIX, BSDs
+
+- Updated LibTomMath to 0.27 and LibTomCrypt to 0.90
+
+- Renamed util.{c,h} to dbutil.{c,h} to avoid conflicts with system util.h
+
+- Added some small changes so it'll work with AIX (plus Linux Affinity).
+ Thanks to Shig for them.
+
+- Improved the closing messages, so a clean exit is "Exited normally"
+
+- Added some more robust integer/size checking in buffer.c as a backstop for
+ integer overflows
+
+- X11 forwarding fixed for OSX, path for xauth changed to /usr/X11R6/bin/xauth
+
+- Channel code handles closing more nicely, doesn't sit waiting for an extra
+ keystroke on BSD/OSX platforms, and data is flushed fully before closing
+ child processes (thanks to
+ Cristian Ionescu-Idbohrn <cristian.ionescu-idbohrn at axis.com> for
+ pointing that out).
+
+- Changed "DISABLE_TCPFWD" to "ENABLE_TCPFWD" (and for x11/auth) so
+ "disable DISABLE_TCPWD" isn't so confusing.
+
+- Fix authorized_keys handling (don't crash on too-long keys, and
+ use fgetc not getc to avoid strange macro-related issues), thanks to
+ Cristian Ionescu-Idbohrn <cristian.ionescu-idbohrn at axis.com>
+ and Steve Rodgers <hwstar at cox.net> for reporting and testing.
+
+- Fixes to the README with regard to uClibc systems, thanks to
+ Cristian Ionescu-Idbohrn <cristian.ionescu-idbohrn at axis.com>,
+ as well as general improvements to documentation (split README/INSTALL)
+
+- Fixed up some compilation problems with dropbearconvert/dropbearkey if
+ DSS or RSA were disabled, reported by Patrik Karlsson <patrik at cqure.net>
+
+- Fix double-free bug for hostkeys, reported by
+ Vincent Sanders <vince at kyllikki.org>
+
+- Fix up missing \ns from dropbearconvert help message,
+ thanks to Mordy Ovits <movits at bloomberg.com> for the patch
+
+0.36 - Tue August 19 2003 12:16:23 +0800
+
+- Fix uninitialised temporary variable in DSS signing code
+ (thanks to Matthew Franz <mdfranz at io.com> for reporting, and the authors
+ of Valgrind for making it easy to track down)
+- Fix remote version-string parsing error
+ (thanks to Bernard Blackham <bernard at blackham.com.au> for noticing)
+- Improved host-algorithm-matching algorithm in algo.c
+- Decreased MAX_STRING_LEN to a more realistic value
+- Fix incorrect version (0.34) in this CHANGES file for the previous release.
+
+0.35 - Sun August 17 2003 05:37:47 +0800
+
+- Fix for remotely exploitable format string buffer overflow.
+ (thanks to Joel Eriksson <je at bitnux.com>)
+
+0.34 - Fri August 15 2003 15:10:00 +0800
+
+- Made syslog optional, both at compile time and as a compile option
+ (suggested by Laurent Bercot <ska at skarnet.org>)
+- Fixup for bad base64 parsing in authorized_keys
+ (noticed by Davyd Madeley <davyd at zdlcomputing.com>)
+- Added initial tcp forwarding code, only -L (local) at this stage
+- Improved "make install" with DESTDIR and changing ownership seperately,
+ don't check for setpgrp on Linux for crosscompiling.
+ (from Erik Andersen <andersen at codepoet.org>)
+- More commenting, fix minor compile warnings, make return values more
+ consistent etc
+- Various signedness fixes
+- Can listen on multiple ports
+- added option to disable openpty with configure script,
+ (from K.-P. Kirchdörfer <kapeka at epost.de>)
+- Various cleanups to bignum code
+ (thanks to Tom St Denis <tomstdenis at iahu.ca>)
+- Fix compile error when disabling RSA
+ (from Marc Kleine-Budde <kleine-budde at gmx.de>)
+- Other cleanups, splitting large functions for packet and kex handling etc
+
+0.33 - Sun June 22 2003 22:24:12 +0800
+
+- Fixed some invalid assertions in the channel code, fixing the server dying
+ when forwarding X11 connections.
+- Add dropbearconvert to convert to/from OpenSSH host keys and Dropbear keys
+- RSA keys now keep p and q parameters for compatibility -- old Dropbear keys
+ still work, but can't be converted to OpenSSH etc.
+- Debian packaging directory added, thanks to
+ Grahame (grahame at angrygoats.net)
+- 'install' target added to the makefile
+- general tidying, improve consistency of functions etc
+- If RSA or DSS hostkeys don't exist, that algorithm won't be used.
+- Improved RSA and DSS key generation, more efficient and fixed some minor bugs
+ (thanks to Tom St Denis for the advice)
+- Merged new versions of LibTomCrypt (0.86) and LibTomMath (0.21)
+
+0.32 - Sat May 24 2003 12:44:11 +0800
+
+- Don't compile unused code from libtomcrypt (test vectors etc)
+- Updated to libtommath 0.17 and libtomcrypt 0.83. New libtommath results
+ in smaller binary size, due to not linking unrequired code
+- X11 forwarding added
+- Agent forwarding added (for OpenSSH.com ssh client/agent)
+- Fix incorrect buffer freeing when banners are used
+- Hostname resolution works
+- Various minor bugfixes/code size improvements etc
+
+0.31 - Fri May 9 2003 17:57:16 +0800
+
+- Improved syslog messages - IP logging etc
+- Strip control characters from log messages (specified username currently)
+- Login recording (utmp/wtmp) support, so last/w/who work - taken from OpenSSH
+- Shell is started as a proper login shell, so /etc/profile etc is sourced
+- Ptys work on Solaris (2.8 x86 tested) now
+- Fixed bug in specifying the rsa hostkey
+- Fixed bug in compression code, could trigger if compression resulted in
+ larger output than input (uncommon but possible).
+
+0.30 - Thu Apr 17 2003 18:46:15 +0800
+
+- SECURITY: buffer.c had bad checking for buffer increment length - fixed
+- channel code now closes properly on EOF - scp processes don't hang around
+- syslog support added - improved auth/login/failure messages
+- general code tidying, made return codes more consistent
+- Makefile fixed for dependencies and makes libtomcrypt as well
+- Implemented sending SSH_MSG_UNIMPLEMENTED :)
+
+0.29 - Wed Apr 9 2003
+
+- Fixed a stupid bug in 0.28 release, 'newstr = strdup(oldstr)',
+ not 'newstr=oldstr'
+
+0.28 - Sun Apr 6 2003
+
+- Initial public release
+
+Development was started in October 2002
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..1bf444e
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,79 @@
+Basic Dropbear build instructions:
+
+- Edit options.h to set which features you want.
+- Edit debug.h if you want any debug options (not usually required).
+
+(If using a non-tarball copy, "autoconf; autoheader")
+
+./configure (optionally with --disable-zlib or --disable-syslog,
+ or --help for other options)
+
+Now compile:
+
+make PROGRAMS="dropbear dbclient dropbearkey dropbearconvert scp"
+
+And install (/usr/local/bin is usual default):
+
+make PROGRAMS="dropbear dbclient dropbearkey dropbearconvert scp" install
+
+(you can leave items out of the PROGRAMS list to avoid compiling them. If you
+recompile after changing the PROGRAMS list, you *MUST* "make clean" before
+recompiling - bad things will happen otherwise)
+
+See MULTI for instructions on making all-in-one binaries.
+
+If you want to compile statically, add "STATIC=1" to the make command-line.
+
+Binaries can be strippd with "make strip"
+
+============================================================================
+
+If you're compiling for a 386-class CPU, you will probably need to add
+CFLAGS=-DLTC_NO_BSWAP so that libtomcrypt doesn't use 486+ instructions.
+
+============================================================================
+
+Compiling with uClibc:
+
+Firstly, make sure you have at least uclibc 0.9.17, as getusershell() in prior
+versions is broken. Also note that you may get strange issues if your uClibc
+headers don't match the library you are running with, ie the headers might
+say that shadow password support exists, but the libraries don't have it.
+
+Compiling for uClibc should be the same as normal, just set CC to the magic
+uClibc toolchain compiler (ie export CC=i386-uclibc-gcc or whatever).
+You can use "make STATIC=1" to make statically linked binaries, and it is
+advisable to strip the binaries too. If you're looking to make a small binary,
+you should remove unneeded ciphers and MD5, by editing options.h
+
+It is possible to compile zlib in, by copying zlib.h and zconf.h into a
+subdirectory (ie zlibincludes), and
+
+export CFLAGS="-Izlibincludes -I../zlibincludes"
+export LDFLAGS=/usr/lib/libz.a
+
+before ./configure and make.
+
+If you disable zlib, you must explicitly disable compression for the client -
+OpenSSH is possibly buggy in this regard, it seems you need to disable it
+globally in ~/.ssh/config, not just in the host entry in that file.
+
+You may want to manually disable lastlog recording when using uClibc, configure
+with --disable-lastlog.
+
+One common problem is pty allocation. There are a number of types of pty
+allocation which can be used -- if they work properly, the end result is the
+same for each type. Running configure should detect the best type to use
+automatically, however for some systems, this may be incorrect. Some
+things to note:
+
+ If your system expects /dev/pts to be mounted (this is a uClibc option),
+ make sure that it is.
+
+ Make sure that your libc headers match the library version you are using.
+
+ If openpty() is being used (HAVE_OPENPTY defined in config.h) and it fails,
+ you can try compiling with --disable-openpty. You will probably then need
+ to create all the /dev/pty?? and /dev/tty?? devices, which can be
+ problematic for devfs. In general, openpty() is the best way to allocate
+ PTYs, so it's best to try and get it working.
diff --git a/LICENSE b/LICENSE
index 5baa792..e0a11ac 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,89 @@
-LibTomMath is hereby released into the Public Domain.
+Dropbear contains a number of components from different sources, hence there
+are a few licenses and authors involved. All licenses are fairly
+non-restrictive.
--- Tom St Denis
+The majority of code is written by Matt Johnston, under the license below.
+
+Portions of the client-mode work are (c) 2004 Mihnea Stoenescu, under the
+same license:
+
+Copyright (c) 2002-2004 Matt Johnston
+Portions copyright (c) 2004 Mihnea Stoenescu
+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.
+
+=====
+
+LibTomCrypt and LibTomMath are written by Tom St Denis, and are Public Domain.
+
+=====
+
+sshpty.c is taken from OpenSSH 3.5p1,
+ Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+ All rights reserved
+ "As far as I am concerned, the code I have written for this software
+ can be used freely for any purpose. Any derived versions of this
+ software must be clearly marked as such, and if the derived work is
+ incompatible with the protocol description in the RFC file, it must be
+ called by a name other than "ssh" or "Secure Shell". "
+
+=====
+
+loginrec.c
+loginrec.h
+atomicio.h
+atomicio.c
+and strlcat() (included in util.c) are from OpenSSH 3.6.1p2, and are licensed
+under the 2 point BSD license.
+
+loginrec is written primarily by Andre Lucas, atomicio.c by Theo de Raadt.
+
+strlcat() is (c) Todd C. Miller
+
+=====
+
+Import code in keyimport.c is modified from PuTTY's import.c, licensed as
+follows:
+
+PuTTY is copyright 1997-2003 Simon Tatham.
+
+Portions copyright Robert de Bath, Joris van Rantwijk, Delian
+Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry,
+Justin Bradford, and CORE SDI S.A.
+
+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 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.
diff --git a/MULTI b/MULTI
new file mode 100644
index 0000000..a50e30e
--- /dev/null
+++ b/MULTI
@@ -0,0 +1,26 @@
+Multi-binary compilation
+========================
+
+To compile for systems without much space (floppy distributions etc), you
+can create a single binary. This will save disk space by avoiding repeated
+code between the various parts.
+If you are familiar with "busybox", it's the same principle.
+
+To compile the multi-binary, first "make clean" (if you've compiled
+previously), then
+
+make PROGRAMS="programs you want here" MULTI=1
+
+To use the binary, symlink it from the desired executable:
+
+ln -s dropbearmulti dropbear
+ln -s dropbearmulti dbclient
+etc
+
+then execute as normal:
+
+./dropbear <options here>
+
+"make install" doesn't currently work for multi-binary configuration, though
+in most situations where it is being used, the target and build systems will
+differ.
diff --git a/Makefile.in b/Makefile.in
index e96173a..fc17c1f 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -1,165 +1,209 @@
-#Makefile for GCC
+# This Makefile is for Dropbear SSH Server and Client
+# @configure_input@
+
+# invocation:
+# make PROGRAMS="dropbear dbclient scp" MULTI=1 STATIC=1 SCPPROGRESS=1
#
-#Tom St Denis
+# 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
+endif
+
+LTC=libtomcrypt/libtomcrypt.a
+LTM=libtommath/libtommath.a
+
+COMMONOBJS=dbutil.o buffer.o \
+ dss.o bignum.o \
+ signkey.o rsa.o random.o \
+ queue.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-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-channel.o cli-authinteract.o
-#version of library
-VERSION=0.35
+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 circbuffer.o
+
+KEYOBJS=dropbearkey.o gendss.o genrsa.o
+
+CONVERTOBJS=dropbearconvert.o keyimport.o
+
+SCPOBJS=scp.o progressmeter.o atomicio.o scpmisc.o
+
+HEADERS=options.h dbutil.h session.h packet.h algo.h ssh.h buffer.h kex.h \
+ dss.h bignum.h signkey.h rsa.h random.h service.h auth.h \
+ debug.h channel.h chansession.h config.h queue.h sshpty.h \
+ termcodes.h gendss.h genrsa.h runopts.h includes.h \
+ loginrec.h atomicio.h x11fwd.h agentfwd.h tcpfwd.h compat.h \
+ listener.h fake-rfc2553.h
+
+dropbearobjs=$(COMMONOBJS) $(CLISVROBJS) $(SVROBJS)
+dbclientobjs=$(COMMONOBJS) $(CLISVROBJS) $(CLIOBJS)
+dropbearkeyobjs=$(COMMONOBJS) $(KEYOBJS)
+dropbearconvertobjs=$(COMMONOBJS) $(CONVERTOBJS)
+scpobjs=$(SCPOBJS)
VPATH=@srcdir@
srcdir=@srcdir@
-# Dropbear takes flags from the toplevel makefile
-CFLAGS += -I$(srcdir)
-
-#CFLAGS += -I./ -Wall -W -Wshadow -Wsign-compare
-
-#for speed
-#CFLAGS += -O3 -funroll-all-loops
-
-#for size
-#CFLAGS += -Os
-
-#x86 optimizations [should be valid for any GCC install though]
-#CFLAGS += -fomit-frame-pointer
-
-#debug
-#CFLAGS += -g3
-
-#install as this user
-USER=root
-GROUP=root
-
-default: libtommath.a
-
-#default files to install
-LIBNAME=libtommath.a
-HEADERS=tommath.h tommath_class.h tommath_superclass.h
-
-#LIBPATH-The directory for libtommath to be installed to.
-#INCPATH-The directory to install the header files for libtommath.
-#DATAPATH-The directory to install the pdf docs.
-DESTDIR=
-LIBPATH=/usr/lib
-INCPATH=/usr/include
-DATAPATH=/usr/share/doc/libtommath/pdf
-
-OBJECTS=bncore.o bn_mp_init.o bn_mp_clear.o bn_mp_exch.o bn_mp_grow.o bn_mp_shrink.o \
-bn_mp_clamp.o bn_mp_zero.o bn_mp_set.o bn_mp_set_int.o bn_mp_init_size.o bn_mp_copy.o \
-bn_mp_init_copy.o bn_mp_abs.o bn_mp_neg.o bn_mp_cmp_mag.o bn_mp_cmp.o bn_mp_cmp_d.o \
-bn_mp_rshd.o bn_mp_lshd.o bn_mp_mod_2d.o bn_mp_div_2d.o bn_mp_mul_2d.o bn_mp_div_2.o \
-bn_mp_mul_2.o bn_s_mp_add.o bn_s_mp_sub.o bn_fast_s_mp_mul_digs.o bn_s_mp_mul_digs.o \
-bn_fast_s_mp_mul_high_digs.o bn_s_mp_mul_high_digs.o bn_fast_s_mp_sqr.o bn_s_mp_sqr.o \
-bn_mp_add.o bn_mp_sub.o bn_mp_karatsuba_mul.o bn_mp_mul.o bn_mp_karatsuba_sqr.o \
-bn_mp_sqr.o bn_mp_div.o bn_mp_mod.o bn_mp_add_d.o bn_mp_sub_d.o bn_mp_mul_d.o \
-bn_mp_div_d.o bn_mp_mod_d.o bn_mp_expt_d.o bn_mp_addmod.o bn_mp_submod.o \
-bn_mp_mulmod.o bn_mp_sqrmod.o bn_mp_gcd.o bn_mp_lcm.o bn_fast_mp_invmod.o bn_mp_invmod.o \
-bn_mp_reduce.o bn_mp_montgomery_setup.o bn_fast_mp_montgomery_reduce.o bn_mp_montgomery_reduce.o \
-bn_mp_exptmod_fast.o bn_mp_exptmod.o bn_mp_2expt.o bn_mp_n_root.o bn_mp_jacobi.o bn_reverse.o \
-bn_mp_count_bits.o bn_mp_read_unsigned_bin.o bn_mp_read_signed_bin.o bn_mp_to_unsigned_bin.o \
-bn_mp_to_signed_bin.o bn_mp_unsigned_bin_size.o bn_mp_signed_bin_size.o \
-bn_mp_xor.o bn_mp_and.o bn_mp_or.o bn_mp_rand.o bn_mp_montgomery_calc_normalization.o \
-bn_mp_prime_is_divisible.o bn_prime_tab.o bn_mp_prime_fermat.o bn_mp_prime_miller_rabin.o \
-bn_mp_prime_is_prime.o bn_mp_prime_next_prime.o bn_mp_dr_reduce.o \
-bn_mp_dr_is_modulus.o bn_mp_dr_setup.o bn_mp_reduce_setup.o \
-bn_mp_toom_mul.o bn_mp_toom_sqr.o bn_mp_div_3.o bn_s_mp_exptmod.o \
-bn_mp_reduce_2k.o bn_mp_reduce_is_2k.o bn_mp_reduce_2k_setup.o \
-bn_mp_reduce_2k_l.o bn_mp_reduce_is_2k_l.o bn_mp_reduce_2k_setup_l.o \
-bn_mp_radix_smap.o bn_mp_read_radix.o bn_mp_toradix.o bn_mp_radix_size.o \
-bn_mp_fread.o bn_mp_fwrite.o bn_mp_cnt_lsb.o bn_error.o \
-bn_mp_init_multi.o bn_mp_clear_multi.o bn_mp_exteuclid.o bn_mp_toradix_n.o \
-bn_mp_prime_random_ex.o bn_mp_get_int.o bn_mp_sqrt.o bn_mp_is_square.o bn_mp_init_set.o \
-bn_mp_init_set_int.o bn_mp_invmod_slow.o bn_mp_prime_rabin_miller_trials.o \
-bn_mp_to_signed_bin_n.o bn_mp_to_unsigned_bin_n.o
-
-libtommath.a: $(OBJECTS)
- $(AR) $(ARFLAGS) libtommath.a $(OBJECTS)
- $(RANLIB) libtommath.a
-
-#make a profiled library (takes a while!!!)
-#
-# This will build the library with profile generation
-# then run the test demo and rebuild the library.
-#
-# So far I've seen improvements in the MP math
-profiled:
- make CFLAGS="$(CFLAGS) -fprofile-arcs -DTESTING" timing
- ./ltmtest
- rm -f *.a *.o ltmtest
- make CFLAGS="$(CFLAGS) -fbranch-probabilities"
-
-#make a single object profiled library
-profiled_single:
- perl gen.pl
- $(CC) $(CFLAGS) -fprofile-arcs -DTESTING -c mpi.c -o mpi.o
- $(CC) $(CFLAGS) -DTESTING -DTIMER demo/timing.c mpi.o -o ltmtest
- ./ltmtest
- rm -f *.o ltmtest
- $(CC) $(CFLAGS) -fbranch-probabilities -DTESTING -c mpi.c -o mpi.o
- $(AR) $(ARFLAGS) libtommath.a mpi.o
- ranlib libtommath.a
-
-install: libtommath.a
- install -d -g $(GROUP) -o $(USER) $(DESTDIR)$(LIBPATH)
- install -d -g $(GROUP) -o $(USER) $(DESTDIR)$(INCPATH)
- install -g $(GROUP) -o $(USER) $(LIBNAME) $(DESTDIR)$(LIBPATH)
- install -g $(GROUP) -o $(USER) $(HEADERS) $(DESTDIR)$(INCPATH)
-
-test: libtommath.a demo/demo.o
- $(CC) $(CFLAGS) demo/demo.o libtommath.a -o test
-
-mtest: test
- cd mtest ; $(CC) $(CFLAGS) mtest.c -o mtest
-
-timing: libtommath.a
- $(CC) $(CFLAGS) -DTIMER demo/timing.c libtommath.a -o ltmtest
-
-# makes the LTM book DVI file, requires tetex, perl and makeindex [part of tetex I think]
-docdvi: tommath.src
- cd pics ; make
- echo "hello" > tommath.ind
- perl booker.pl
- latex tommath > /dev/null
- latex tommath > /dev/null
- makeindex tommath
- latex tommath > /dev/null
-
-# poster, makes the single page PDF poster
-poster: poster.tex
- pdflatex poster
- rm -f poster.aux poster.log
-
-# makes the LTM book PDF file, requires tetex, cleans up the LaTeX temp files
-docs: docdvi
- dvipdf tommath
- rm -f tommath.log tommath.aux tommath.dvi tommath.idx tommath.toc tommath.lof tommath.ind tommath.ilg
- cd pics ; make clean
-
-#LTM user manual
-mandvi: bn.tex
- echo "hello" > bn.ind
- latex bn > /dev/null
- latex bn > /dev/null
- makeindex bn
- latex bn > /dev/null
-
-#LTM user manual [pdf]
-manual: mandvi
- pdflatex bn >/dev/null
- rm -f bn.aux bn.dvi bn.log bn.idx bn.lof bn.out bn.toc
-
-pretty:
- perl pretty.build
-
-clean:
- rm -f *.bat *.pdf *.o *.a *.obj *.lib *.exe *.dll etclib/*.o demo/demo.o test ltmtest mpitest mtest/mtest mtest/mtest.exe \
- *.idx *.toc *.log *.aux *.dvi *.lof *.ind *.ilg *.ps *.log *.s mpi.c *.da *.dyn *.dpi tommath.tex *.lo *.la
- rm -rf .libs
- cd etc && make clean
- cd pics && make clean
-
-zipup: clean manual poster docs
- perl gen.pl ; mv mpi.c pre_gen/ ; \
- cd .. ; rm -rf ltm* libtommath-$(VERSION) ; mkdir libtommath-$(VERSION) ; \
- cp -R ./libtommath/* ./libtommath-$(VERSION)/ ; \
- tar -c libtommath-$(VERSION)/* | bzip2 -9vvc > ltm-$(VERSION).tar.bz2 ; \
- zip -9 -r ltm-$(VERSION).zip libtommath-$(VERSION)/*
+prefix=@prefix@
+exec_prefix=${prefix}
+bindir=${exec_prefix}/bin
+sbindir=${exec_prefix}/sbin
+
+CC=@CC@
+LD=@LD@
+AR=@AR@
+RANLIB=@RANLIB@
+STRIP=@STRIP@
+INSTALL=@INSTALL@
+CFLAGS=-I. -I$(srcdir)/libtomcrypt/src/headers/ @CFLAGS@
+LIBS=$(LTC) $(LTM) @LIBS@
+LDFLAGS=@LDFLAGS@
+
+EXEEXT=@EXEEXT@
+
+# whether we're building client, server, or both for the common objects.
+# evilness so we detect 'dropbear' by itself as a word
+space:= $(empty) $(empty)
+ifneq (,$(strip $(foreach prog, $(PROGRAMS), $(findstring ZdropbearZ, Z$(prog)Z))))
+ CFLAGS+= -DDROPBEAR_SERVER
+endif
+ifneq (,$(strip $(foreach prog, $(PROGRAMS), $(findstring ZdbclientZ, Z$(prog)Z))))
+ CFLAGS+= -DDROPBEAR_CLIENT
+endif
+
+
+# these are exported so that libtomcrypt's makefile will use them
+export CC
+export CFLAGS
+export RANLIB AR STRIP
+
+ifeq ($(STATIC), 1)
+ LDFLAGS+=-static
+endif
+
+ifeq ($(MULTI), 1)
+ TARGETS=dropbearmulti
+else
+ TARGETS=$(PROGRAMS)
+endif
+
+# for the scp progress meter. The -D doesn't affect anything else.
+ifeq ($(SCPPROGRESS), 1)
+ CFLAGS+=-DPROGRESS_METER
+endif
+
+#%: $(HEADERS)
+#%: $(HEADERS) Makefile
+# TODO
+
+all: $(TARGETS)
+
+strip: $(TARGETS)
+ $(STRIP) $(addsuffix $(EXEEXT), $(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
+inst_dropbear: dropbear
+ $(INSTALL) -d -m 755 $(DESTDIR)$(sbindir)
+ $(INSTALL) -m 755 dropbear$(EXEEXT) $(DESTDIR)$(sbindir)
+ -chown root $(DESTDIR)$(sbindir)/dropbear$(EXEEXT)
+ -chgrp 0 $(DESTDIR)$(sbindir)/dropbear$(EXEEXT)
+
+inst_%: $*
+ $(INSTALL) -d -m 755 $(DESTDIR)$(bindir)
+ $(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.
+dropbear: $(dropbearobjs)
+dbclient: $(dbclientobjs)
+dropbearkey: $(dropbearkeyobjs)
+dropbearconvert: $(dropbearconvertobjs)
+
+dropbear dbclient dropbearkey dropbearconvert: $(HEADERS) $(LTC) $(LTM) \
+ Makefile
+ $(LD) $(LDFLAGS) -o $@$(EXEEXT) $($@objs) $(LIBS)
+
+# scp doesn't use the libs so is special.
+scp: $(SCPOBJS) $(HEADERS) Makefile
+ $(LD) $(LDFLAGS) -o $@$(EXEEXT) $(SCPOBJS)
+
+
+# multi-binary compilation.
+MULTIOBJS=
+ifeq ($(MULTI),1)
+ MULTIOBJS=dbmulti.o $(sort $(foreach prog, $(PROGRAMS), $($(prog)objs)))
+ CFLAGS+=$(addprefix -DDBMULTI_, $(PROGRAMS)) -DDROPBEAR_MULTI
+endif
+
+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)
+
+$(LTM): options.h
+ cd libtommath && $(MAKE)
+
+ltc-clean:
+ cd libtomcrypt && $(MAKE) clean
+
+ltm-clean:
+ cd libtommath && $(MAKE) clean
+
+sizes: dropbear
+ objdump -t dropbear|grep ".text"|cut -d "." -f 2|sort -rn
+
+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
+ -rm -f Makefile
+
+tidy:
+ -rm -f *~ *.gcov */*~
diff --git a/README b/README
new file mode 100644
index 0000000..43dd1f2
--- /dev/null
+++ b/README
@@ -0,0 +1,74 @@
+This is Dropbear, a smallish SSH 2 server and client.
+
+INSTALL has compilation instructions.
+
+MULTI has instructions on making a multi-purpose binary (ie a single binary
+which performs multiple tasks, to save disk space)
+
+SMALL has some tips on creating small binaries.
+
+See TODO for a few of the things I know need looking at, and please contact
+me if you have any questions/bugs found/features/ideas/comments etc :)
+
+Matt Johnston
+matt@ucc.asn.au
+
+
+In the absence of detailed documentation, some notes follow:
+============================================================================
+
+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:
+
+ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAwVa6M6cGVmUcLl2cFzkxEoJd06Ub4bVDsYrWvXhvUV+ZAM9uGuewZBDoAqNKJxoIn0Hyd0Nk/yU99UVv6NWV/5YSHtnf35LKds56j7cuzoQpFIdjNwdxAN0PCET/MG8qyskG/2IE2DPNIaJ3Wy+Ws4IZEgdJgPlTYUBWWtCWOGc= someone@hostname
+
+You must make sure that ~/.ssh, and the key file, are only writable by the
+user.
+
+NOTE: Dropbear ignores authorized_keys options such as those described in the
+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.
+
+============================================================================
+
+To run the server, you need to generate server keys, this is one-off:
+./dropbearkey -t rsa -f dropbear_rsa_host_key
+./dropbearkey -t dss -f dropbear_dss_host_key
+
+or alternatively convert OpenSSH keys to Dropbear:
+./dropbearconvert openssh dropbear /etc/ssh/ssh_host_dsa_key dropbear_dss_host_key
+
+============================================================================
+
+If the server is run as non-root, you most likely won't be able to allocate a
+pty, and you cannot login as any user other than that running the daemon
+(obviously). Shadow passwords will also be unusable as non-root.
+
+============================================================================
+
+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 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.
diff --git a/SMALL b/SMALL
new file mode 100644
index 0000000..babd671
--- /dev/null
+++ b/SMALL
@@ -0,0 +1,53 @@
+Tips for a small system:
+
+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.
+
+---
+
+The following are set in options.h:
+
+ - 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
+
+ - You can disable either password or public-key authentication, though note
+ that the IETF draft states that pubkey authentication is required.
+
+ - 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).
+
+You can create a combined binary, see the file MULTI, which will put all
+the functions into one binary, avoiding repeated code.
+
+If you're compiling with gcc, you might want to look at gcc's options for
+stripping unused code. The relevant vars to set before configure are:
+
+LDFLAGS=-Wl,--gc-sections
+CFLAGS="-ffunction-sections -fdata-sections"
+
+You can also experiment with optimisation flags such as -Os, note that in some
+cases these flags actually seem to increase size, so experiment before
+deciding.
+
+Of course using small C libraries such as uClibc and dietlibc can also help.
+
+If you have any queries, mail me and I'll see if I can help.
diff --git a/TODO b/TODO
index deffba1..9807f59 100644
--- a/TODO
+++ b/TODO
@@ -1,16 +1,30 @@
-things for book in order of importance...
+Current:
-- Fix up pseudo-code [only] for combas that are not consistent with source
-- Start in chapter 3 [basics] and work up...
- - re-write to prose [less abrupt]
- - clean up pseudo code [spacing]
- - more examples where appropriate and figures
+Things which might need doing:
-Goal:
- - Get sync done by mid January [roughly 8-12 hours work]
- - Finish ch3-6 by end of January [roughly 12-16 hours of work]
- - Finish ch7-end by mid Feb [roughly 20-24 hours of work].
+- default private dbclient keys
-Goal isn't "first edition" but merely cleaner to read.
+- Make options.h generated from configure perhaps?
+- Improved queueing of unauthed connections
+- handle /etc/environment in AIX
+
+- check that there aren't timing issues with valid/invalid user authentication
+ feedback.
+
+- Binding to different interfaces
+
+- check PRNG
+- CTR mode
+- SSH_MSG_IGNORE sending to improve CBC security
+- DH Group Exchange possibly, or just add group14 (whatever it's called today)
+
+- 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 ?
diff --git a/agentfwd.h b/agentfwd.h
new file mode 100644
index 0000000..a0f675d
--- /dev/null
+++ b/agentfwd.h
@@ -0,0 +1,43 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+#ifndef _AGENTFWD_H_
+#define _AGENTFWD_H_
+#ifndef DISABLE_AGENTFWD
+
+#include "includes.h"
+#include "chansession.h"
+#include "channel.h"
+
+int agentreq(struct ChanSess * chansess);
+void agentsetauth(struct ChanSess *chansess);
+void agentcleanup(struct ChanSess * chansess);
+void agentset(struct ChanSess *chansess);
+
+#ifdef __hpux
+#define seteuid(a) setresuid(-1, (a), -1)
+#define setegid(a) setresgid(-1, (a), -1)
+#endif
+
+#endif /* DROPBEAR_AGENTFWD */
+#endif /* _AGENTFWD_H_ */
diff --git a/algo.h b/algo.h
new file mode 100644
index 0000000..5ed01cc
--- /dev/null
+++ b/algo.h
@@ -0,0 +1,74 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+#ifndef _ALGO_H_
+
+#define _ALGO_H_
+
+#include "includes.h"
+#include "buffer.h"
+
+struct Algo_Type {
+
+ unsigned char *name; /* identifying name */
+ char val; /* a value for this cipher, or -1 for invalid */
+ void *data; /* algorithm specific data */
+ unsigned usable : 1; /* whether we can use this algorithm */
+
+};
+
+typedef struct Algo_Type algo_type;
+
+/* lists mapping ssh types of algorithms to internal values */
+extern algo_type sshkex[];
+extern algo_type sshhostkey[];
+extern algo_type sshciphers[];
+extern algo_type sshhashes[];
+extern algo_type sshcompress[];
+
+extern const struct dropbear_cipher dropbear_nocipher;
+extern const struct dropbear_hash dropbear_nohash;
+
+struct dropbear_cipher {
+ const struct ltc_cipher_descriptor *cipherdesc;
+ unsigned long keysize;
+ unsigned char blocksize;
+};
+
+struct dropbear_hash {
+ const struct ltc_hash_descriptor *hashdesc;
+ unsigned long keysize;
+ unsigned char hashsize;
+};
+
+void crypto_init();
+int have_algo(char* algo, size_t algolen, algo_type algos[]);
+void buf_put_algolist(buffer * buf, algo_type localalgos[]);
+
+algo_type * svr_buf_match_algo(buffer* buf, algo_type localalgos[],
+ int *goodguess);
+algo_type * cli_buf_match_algo(buffer* buf, algo_type localalgos[],
+ int *goodguess);
+
+#endif /* _ALGO_H_ */
diff --git a/atomicio.c b/atomicio.c
new file mode 100644
index 0000000..1915a7b
--- /dev/null
+++ b/atomicio.c
@@ -0,0 +1,63 @@
+/*
+ * Copied from OpenSSH 3.6.1p2.
+ *
+ * Copyright (c) 1995,1999 Theo de Raadt. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* RCSID("OpenBSD: atomicio.c,v 1.10 2001/05/08 22:48:07 markus Exp "); */
+
+#include "atomicio.h"
+
+/*
+ * ensure all of data on socket comes through. f==read || f==write
+ */
+ssize_t
+atomicio(f, fd, _s, n)
+ ssize_t (*f) ();
+ int fd;
+ void *_s;
+ size_t n;
+{
+ char *s = _s;
+ ssize_t res;
+ size_t pos = 0;
+
+ while (n > pos) {
+ res = (f) (fd, s + pos, n - pos);
+ switch (res) {
+ case -1:
+#ifdef EWOULDBLOCK
+ if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)
+#else
+ if (errno == EINTR || errno == EAGAIN)
+#endif
+ continue;
+ case 0:
+ return (res);
+ default:
+ pos += res;
+ }
+ }
+ return (pos);
+}
diff --git a/atomicio.h b/atomicio.h
new file mode 100644
index 0000000..6c1f3ac
--- /dev/null
+++ b/atomicio.h
@@ -0,0 +1,36 @@
+
+/*
+ * Copied from OpenSSH 3.6.1p2, required for loginrec.c
+ *
+ * $OpenBSD: atomicio.h,v 1.4 2001/06/26 06:32:46 itojun Exp $
+ *
+ * Copyright (c) 1995,1999 Theo de Raadt. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "includes.h"
+
+/*
+ * Ensure all of data on socket comes through. f==read || f==write
+ */
+ssize_t atomicio(ssize_t (*)(), int, void *, size_t);
diff --git a/auth.h b/auth.h
new file mode 100644
index 0000000..c407ad5
--- /dev/null
+++ b/auth.h
@@ -0,0 +1,111 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+#ifndef _AUTH_H_
+#define _AUTH_H_
+
+#include "includes.h"
+
+void svr_authinitialise();
+void cli_authinitialise();
+
+/* Server functions */
+void recv_msg_userauth_request();
+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();
+void recv_msg_userauth_success();
+void recv_msg_userauth_specific_60();
+void recv_msg_userauth_pk_ok();
+void recv_msg_userauth_info_request();
+void cli_get_user();
+void cli_auth_getmethods();
+void cli_auth_try();
+void recv_msg_userauth_banner();
+void cli_pubkeyfail();
+void cli_auth_password();
+int cli_auth_pubkey();
+void cli_auth_interactive();
+char* getpass_or_cancel();
+
+
+#define MAX_USERNAME_LEN 25 /* arbitrary for the moment */
+
+#define AUTH_TYPE_NONE 1
+#define AUTH_TYPE_PUBKEY 1 << 1
+#define AUTH_TYPE_PASSWORD 1 << 2
+#define AUTH_TYPE_INTERACT 1 << 3
+
+#define AUTH_METHOD_NONE "none"
+#define AUTH_METHOD_NONE_LEN 4
+#define AUTH_METHOD_PUBKEY "publickey"
+#define AUTH_METHOD_PUBKEY_LEN 9
+#define AUTH_METHOD_PASSWORD "password"
+#define AUTH_METHOD_PASSWORD_LEN 8
+#define AUTH_METHOD_INTERACT "keyboard-interactive"
+#define AUTH_METHOD_INTERACT_LEN 20
+
+
+
+/* This structure is shared between server and client - it contains
+ * relatively little extraneous bits when used for the client rather than the
+ * server */
+struct AuthState {
+
+ char *username; /* This is the username the client presents to check. It
+ is updated each run through, used for auth checking */
+ unsigned char authtypes; /* Flags indicating which auth types are still
+ valid */
+ unsigned int failcount; /* Number of (failed) authentication attempts.*/
+ unsigned authdone : 1; /* 0 if we haven't authed, 1 if we have. Applies for
+ client and server (though has differing [obvious]
+ meanings). */
+ unsigned perm_warn : 1; /* Server only, set if bad permissions on
+ ~/.ssh/authorized_keys have already been
+ logged. */
+
+ /* These are only used for the server */
+ char *printableuser; /* stripped of control chars, used for logs etc */
+ struct passwd * pw;
+
+};
+
+struct SignKeyList;
+/* A singly linked list of signing keys */
+struct SignKeyList {
+
+ sign_key *key;
+ int type; /* The type of key */
+ struct SignKeyList *next;
+ /* filename? or the buffer? for encrypted keys, so we can later get
+ * the private key portion */
+
+};
+
+#endif /* _AUTH_H_ */
diff --git a/bignum.c b/bignum.c
new file mode 100644
index 0000000..60b5220
--- /dev/null
+++ b/bignum.c
@@ -0,0 +1,75 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+/* Contains helper functions for mp_int handling */
+
+#include "includes.h"
+#include "dbutil.h"
+
+/* wrapper for mp_init, failing fatally on errors (memory allocation) */
+void m_mp_init(mp_int *mp) {
+
+ if (mp_init(mp) != MP_OKAY) {
+ dropbear_exit("mem alloc error");
+ }
+}
+
+/* simplified duplication of bn_mp_multi's mp_init_multi, but die fatally
+ * on error */
+void m_mp_init_multi(mp_int *mp, ...)
+{
+ mp_int* cur_arg = mp;
+ va_list args;
+
+ va_start(args, mp); /* init args to next argument from caller */
+ while (cur_arg != NULL) {
+ if (mp_init(cur_arg) != MP_OKAY) {
+ dropbear_exit("mem alloc error");
+ }
+ cur_arg = va_arg(args, mp_int*);
+ }
+ va_end(args);
+}
+
+void bytes_to_mp(mp_int *mp, const unsigned char* bytes, unsigned int len) {
+
+ if (mp_read_unsigned_bin(mp, (unsigned char*)bytes, len) != MP_OKAY) {
+ dropbear_exit("mem alloc error");
+ }
+}
+
+/* hash the ssh representation of the mp_int mp */
+void sha1_process_mp(hash_state *hs, mp_int *mp) {
+
+ int i;
+ buffer * buf;
+
+ buf = buf_new(512 + 20); /* max buffer is a 4096 bit key,
+ plus header + some leeway*/
+ buf_putmpint(buf, mp);
+ i = buf->pos;
+ buf_setpos(buf, 0);
+ sha1_process(hs, buf_getptr(buf, i), i);
+ buf_free(buf);
+}
diff --git a/bignum.h b/bignum.h
new file mode 100644
index 0000000..042f811
--- /dev/null
+++ b/bignum.h
@@ -0,0 +1,35 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+#ifndef _BIGNUM_H_
+#define _BIGNUM_H_
+
+#include "includes.h"
+
+void m_mp_init(mp_int *mp);
+void m_mp_init_multi(mp_int *mp, ...);
+void bytes_to_mp(mp_int *mp, const unsigned char* bytes, unsigned int len);
+void sha1_process_mp(hash_state *hs, mp_int *mp);
+
+#endif /* _BIGNUM_H_ */
diff --git a/buffer.c b/buffer.c
new file mode 100644
index 0000000..579fa6f
--- /dev/null
+++ b/buffer.c
@@ -0,0 +1,338 @@
+/*
+ * 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. */
+
+/* Buffer handling routines, designed to avoid overflows/using invalid data */
+
+#include "includes.h"
+#include "dbutil.h"
+#include "buffer.h"
+
+/* Prevent integer overflows when incrementing buffer position/length.
+ * Calling functions should check arguments first, but this provides a
+ * backstop */
+#define BUF_MAX_INCR 1000000000
+#define BUF_MAX_SIZE 1000000000
+
+/* avoid excessively large numbers, > ~8192 bits */
+#define BUF_MAX_MPINT (8240 / 8)
+
+/* Create (malloc) a new buffer of size */
+buffer* buf_new(unsigned int size) {
+
+ buffer* buf;
+
+ if (size > BUF_MAX_SIZE) {
+ dropbear_exit("buf->size too big");
+ }
+
+ buf = (buffer*)m_malloc(sizeof(buffer));
+
+ if (size > 0) {
+ buf->data = (unsigned char*)m_malloc(size);
+ } else {
+ buf->data = NULL;
+ }
+
+ buf->size = size;
+ buf->pos = 0;
+ buf->len = 0;
+
+ return buf;
+
+}
+
+/* free the buffer's data and the buffer itself */
+void buf_free(buffer* buf) {
+
+ m_free(buf->data)
+ m_free(buf);
+}
+
+/* overwrite the contents of the buffer to clear it */
+void buf_burn(buffer* buf) {
+
+ m_burn(buf->data, buf->size);
+
+}
+
+/* resize a buffer, pos and len will be repositioned if required when
+ * downsizing */
+void buf_resize(buffer *buf, unsigned int newsize) {
+
+ if (newsize > BUF_MAX_SIZE) {
+ dropbear_exit("buf->size too big");
+ }
+
+ buf->data = m_realloc(buf->data, newsize);
+ buf->size = newsize;
+ buf->len = MIN(newsize, buf->len);
+ buf->pos = MIN(newsize, buf->pos);
+
+}
+
+/* Create a copy of buf, allocating required memory etc. */
+/* The new buffer is sized the same as the length of the source buffer. */
+buffer* buf_newcopy(buffer* buf) {
+
+ buffer* ret;
+
+ ret = buf_new(buf->len);
+ ret->len = buf->len;
+ memcpy(ret->data, buf->data, buf->len);
+ return ret;
+}
+
+/* Set the length of the buffer */
+void buf_setlen(buffer* buf, unsigned int len) {
+ if (len > buf->size) {
+ dropbear_exit("bad buf_setlen");
+ }
+ buf->len = len;
+}
+
+/* Increment the length of the buffer */
+void buf_incrlen(buffer* buf, unsigned int incr) {
+ if (incr > BUF_MAX_INCR || buf->len + incr > buf->size) {
+ dropbear_exit("bad buf_incrlen");
+ }
+ buf->len += incr;
+}
+/* Set the position of the buffer */
+void buf_setpos(buffer* buf, unsigned int pos) {
+
+ if (pos > buf->len) {
+ dropbear_exit("bad buf_setpos");
+ }
+ buf->pos = pos;
+}
+
+/* increment the postion by incr, increasing the buffer length if required */
+void buf_incrwritepos(buffer* buf, unsigned int incr) {
+ if (incr > BUF_MAX_INCR || buf->pos + incr > buf->size) {
+ dropbear_exit("bad buf_incrwritepos");
+ }
+ buf->pos += incr;
+ if (buf->pos > buf->len) {
+ buf->len = buf->pos;
+ }
+}
+
+/* increment the position by incr, negative values are allowed, to
+ * decrement the pos*/
+void buf_incrpos(buffer* buf, int incr) {
+ if (incr > BUF_MAX_INCR ||
+ (unsigned int)((int)buf->pos + incr) > buf->len
+ || ((int)buf->pos + incr) < 0) {
+ dropbear_exit("bad buf_incrpos");
+ }
+ buf->pos += incr;
+}
+
+/* Get a byte from the buffer and increment the pos */
+unsigned char buf_getbyte(buffer* buf) {
+
+ /* This check is really just ==, but the >= allows us to check for the
+ * bad case of pos > len, which should _never_ happen. */
+ if (buf->pos >= buf->len) {
+ dropbear_exit("bad buf_getbyte");
+ }
+ return buf->data[buf->pos++];
+}
+
+/* Get a bool from the buffer and increment the pos */
+unsigned char buf_getbool(buffer* buf) {
+
+ unsigned char b;
+ b = buf_getbyte(buf);
+ if (b != 0)
+ b = 1;
+ return b;
+}
+
+/* put a byte, incrementing the length if required */
+void buf_putbyte(buffer* buf, unsigned char val) {
+
+ if (buf->pos >= buf->len) {
+ buf_incrlen(buf, 1);
+ }
+ buf->data[buf->pos] = val;
+ buf->pos++;
+}
+
+/* returns an in-place pointer to the buffer, checking that
+ * the next len bytes from that position can be used */
+unsigned char* buf_getptr(buffer* buf, unsigned int len) {
+
+ if (buf->pos + len > buf->len) {
+ dropbear_exit("bad buf_getptr");
+ }
+ return &buf->data[buf->pos];
+}
+
+/* like buf_getptr, but checks against total size, not used length.
+ * This allows writing past the used length, but not past the size */
+unsigned char* buf_getwriteptr(buffer* buf, unsigned int len) {
+
+ if (buf->pos + len > buf->size) {
+ dropbear_exit("bad buf_getwriteptr");
+ }
+ return &buf->data[buf->pos];
+}
+
+/* Return a null-terminated string, it is malloced, so must be free()ed
+ * Note that the string isn't checked for null bytes, hence the retlen
+ * may be longer than what is returned by strlen */
+unsigned char* buf_getstring(buffer* buf, unsigned int *retlen) {
+
+ unsigned int len;
+ unsigned char* ret;
+ len = buf_getint(buf);
+ if (len > MAX_STRING_LEN) {
+ dropbear_exit("string too long");
+ }
+
+ if (retlen != NULL) {
+ *retlen = len;
+ }
+ ret = m_malloc(len+1);
+ memcpy(ret, buf_getptr(buf, len), len);
+ buf_incrpos(buf, len);
+ ret[len] = '\0';
+
+ return ret;
+}
+
+/* Just increment the buffer position the same as if we'd used buf_getstring,
+ * but don't bother copying/malloc()ing for it */
+void buf_eatstring(buffer *buf) {
+
+ buf_incrpos( buf, buf_getint(buf) );
+}
+
+/* Get an uint32 from the buffer and increment the pos */
+unsigned int buf_getint(buffer* buf) {
+ unsigned int ret;
+
+ LOAD32H(ret, buf_getptr(buf, 4));
+ buf_incrpos(buf, 4);
+ return ret;
+}
+
+/* put a 32bit uint into the buffer, incr bufferlen & pos if required */
+void buf_putint(buffer* buf, int unsigned val) {
+
+ STORE32H(val, buf_getwriteptr(buf, 4));
+ buf_incrwritepos(buf, 4);
+
+}
+
+/* put a SSH style string into the buffer, increasing buffer len if required */
+void buf_putstring(buffer* buf, const unsigned char* str, unsigned int len) {
+
+ buf_putint(buf, len);
+ buf_putbytes(buf, str, len);
+
+}
+
+/* put the set of len bytes into the buffer, incrementing the pos, increasing
+ * len if required */
+void buf_putbytes(buffer *buf, const unsigned char *bytes, unsigned int len) {
+ memcpy(buf_getwriteptr(buf, len), bytes, len);
+ buf_incrwritepos(buf, len);
+}
+
+
+/* for our purposes we only need positive (or 0) numbers, so will
+ * fail if we get negative numbers */
+void buf_putmpint(buffer* buf, mp_int * mp) {
+
+ unsigned int len, pad = 0;
+ TRACE(("enter buf_putmpint"))
+
+ dropbear_assert(mp != NULL);
+
+ if (SIGN(mp) == MP_NEG) {
+ dropbear_exit("negative bignum");
+ }
+
+ /* zero check */
+ if (USED(mp) == 1 && DIGIT(mp, 0) == 0) {
+ len = 0;
+ } else {
+ /* SSH spec requires padding for mpints with the MSB set, this code
+ * implements it */
+ len = mp_count_bits(mp);
+ /* if the top bit of MSB is set, we need to pad */
+ pad = (len%8 == 0) ? 1 : 0;
+ len = len / 8 + 1; /* don't worry about rounding, we need it for
+ padding anyway when len%8 == 0 */
+
+ }
+
+ /* store the length */
+ buf_putint(buf, len);
+
+ /* store the actual value */
+ if (len > 0) {
+ if (pad) {
+ buf_putbyte(buf, 0x00);
+ }
+ if (mp_to_unsigned_bin(mp, buf_getwriteptr(buf, len-pad)) != MP_OKAY) {
+ dropbear_exit("mpint error");
+ }
+ buf_incrwritepos(buf, len-pad);
+ }
+
+ TRACE(("leave buf_putmpint"))
+}
+
+/* Retrieve an mp_int from the buffer.
+ * Will fail for -ve since they shouldn't be required here.
+ * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
+int buf_getmpint(buffer* buf, mp_int* mp) {
+
+ unsigned int len;
+ len = buf_getint(buf);
+
+ if (len == 0) {
+ mp_zero(mp);
+ return DROPBEAR_SUCCESS;
+ }
+
+ if (len > BUF_MAX_MPINT) {
+ return DROPBEAR_FAILURE;
+ }
+
+ /* check for negative */
+ if (*buf_getptr(buf, 1) & (1 << (CHAR_BIT-1))) {
+ return DROPBEAR_FAILURE;
+ }
+
+ if (mp_read_unsigned_bin(mp, buf_getptr(buf, len), len) != MP_OKAY) {
+ return DROPBEAR_FAILURE;
+ }
+
+ buf_incrpos(buf, len);
+ return DROPBEAR_SUCCESS;
+}
diff --git a/buffer.h b/buffer.h
new file mode 100644
index 0000000..f9aa6fa
--- /dev/null
+++ b/buffer.h
@@ -0,0 +1,66 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+#ifndef _BUFFER_H_
+
+#define _BUFFER_H_
+
+#include "includes.h"
+
+struct buf {
+
+ unsigned char * data;
+ unsigned int len; /* the used size */
+ unsigned int pos;
+ unsigned int size; /* the memory size */
+
+};
+
+typedef struct buf buffer;
+
+buffer * buf_new(unsigned int size);
+void buf_resize(buffer *buf, unsigned int newsize);
+void buf_free(buffer* buf);
+void buf_burn(buffer* buf);
+buffer* buf_newcopy(buffer* buf);
+void buf_setlen(buffer* buf, unsigned int len);
+void buf_incrlen(buffer* buf, unsigned int incr);
+void buf_setpos(buffer* buf, unsigned int pos);
+void buf_incrpos(buffer* buf, int incr); /* -ve is ok, to go backwards */
+void buf_incrwritepos(buffer* buf, unsigned int incr);
+unsigned char buf_getbyte(buffer* buf);
+unsigned char buf_getbool(buffer* buf);
+void buf_putbyte(buffer* buf, unsigned char val);
+unsigned char* buf_getptr(buffer* buf, unsigned int len);
+unsigned char* buf_getwriteptr(buffer* buf, unsigned int len);
+unsigned char* buf_getstring(buffer* buf, unsigned int *retlen);
+void buf_eatstring(buffer *buf);
+void buf_putint(buffer* buf, unsigned int val);
+void buf_putstring(buffer* buf, const unsigned char* str, unsigned int len);
+void buf_putbytes(buffer *buf, const unsigned char *bytes, unsigned int len);
+void buf_putmpint(buffer* buf, mp_int * mp);
+int buf_getmpint(buffer* buf, mp_int* mp);
+unsigned int buf_getint(buffer* buf);
+
+#endif /* _BUFFER_H_ */
diff --git a/channel.h b/channel.h
new file mode 100644
index 0000000..7030a0f
--- /dev/null
+++ b/channel.h
@@ -0,0 +1,135 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+#ifndef _CHANNEL_H_
+#define _CHANNEL_H_
+
+#include "includes.h"
+#include "buffer.h"
+#include "circbuffer.h"
+
+/* channel->type values */
+#define CHANNEL_ID_NONE 0
+#define CHANNEL_ID_SESSION 1
+#define CHANNEL_ID_X11 2
+#define CHANNEL_ID_AGENT 3
+#define CHANNEL_ID_TCPDIRECT 4
+#define CHANNEL_ID_TCPFORWARDED 5
+
+#define SSH_OPEN_ADMINISTRATIVELY_PROHIBITED 1
+#define SSH_OPEN_CONNECT_FAILED 2
+#define SSH_OPEN_UNKNOWN_CHANNEL_TYPE 3
+#define SSH_OPEN_RESOURCE_SHORTAGE 4
+
+/* Not a real type */
+#define SSH_OPEN_IN_PROGRESS 99
+
+#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 8000 /* tweak */
+#define RECV_WINDOWEXTEND 1000 /* We send a "window extend" every
+ RECV_WINDOWEXTEND bytes */
+#define RECV_MAXPACKET RECV_MAXWINDOW /* tweak */
+
+struct ChanType;
+
+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 writefd; /* read from wire, written to insecure side */
+ int readfd; /* read from insecure size, written to wire */
+ int errfd; /* used like writefd or readfd, 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;
+
+ /* this is set when we receive/send a channel eof packet */
+ int recveof, senteof;
+
+ int initconn; /* used for TCP forwarding, whether the channel has been
+ fully initialised */
+
+ int await_open; /* flag indicating whether we've sent an open request
+ for this channel (and are awaiting a confirmation
+ or failure). */
+
+ const struct ChanType* type;
+
+};
+
+struct ChanType {
+
+ int sepfds; /* Whether this channel has seperate pipes for in/out or not */
+ char *name;
+ int (*inithandler)(struct Channel*);
+ int (*checkclose)(struct Channel*);
+ void (*reqhandler)(struct Channel*);
+ void (*closehandler)(struct Channel*);
+
+};
+
+void chaninitialise(const struct ChanType *chantypes[]);
+void chancleanup();
+void setchannelfds(fd_set *readfd, fd_set *writefd);
+void channelio(fd_set *readfd, fd_set *writefd);
+struct Channel* getchannel();
+struct Channel* newchannel(unsigned int remotechan,
+ const struct ChanType *type,
+ unsigned int transwindow, unsigned int transmaxpacket);
+
+void recv_msg_channel_open();
+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();
+
+void common_recv_msg_channel_data(struct Channel *channel, int fd,
+ circbuffer * buf);
+
+#ifdef DROPBEAR_CLIENT
+extern 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();
+#endif
+
+#endif /* _CHANNEL_H_ */
diff --git a/chansession.h b/chansession.h
new file mode 100644
index 0000000..213c285
--- /dev/null
+++ b/chansession.h
@@ -0,0 +1,92 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+#ifndef _CHANSESSION_H_
+#define _CHANSESSION_H_
+
+#include "loginrec.h"
+#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 */
+ pid_t pid; /* child process pid */
+
+ /* pty details */
+ int master; /* the master terminal fd*/
+ int slave;
+ unsigned char * tty;
+ unsigned char * term;
+
+ /* exit details */
+ struct exitinfo exit;
+
+#ifndef DISABLE_X11FWD
+ struct Listener * x11listener;
+ int x11port;
+ char * x11authprot;
+ char * x11authcookie;
+ unsigned int x11screennum;
+ unsigned char x11singleconn;
+#endif
+
+#ifndef DISABLE_AGENTFWD
+ struct Listener * agentlistener;
+ char * agentfile;
+ char * agentdir;
+#endif
+};
+
+struct ChildPid {
+ pid_t pid;
+ struct ChanSess * chansess;
+};
+
+
+void addnewvar(const char* param, const char* var);
+
+void cli_send_chansess_request();
+void cli_tty_cleanup();
+void cli_chansess_winchange();
+
+void svr_chansessinitialise();
+extern const struct ChanType svrchansess;
+
+struct SigMap {
+ int signal;
+ char* name;
+};
+
+extern const struct SigMap signames[];
+
+#endif /* _CHANSESSION_H_ */
diff --git a/circbuffer.c b/circbuffer.c
new file mode 100644
index 0000000..e70087a
--- /dev/null
+++ b/circbuffer.c
@@ -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) {
+
+ dropbear_assert(((2*cbuf->size)+cbuf->writepos-cbuf->readpos)%cbuf->size == cbuf->used%cbuf->size);
+ dropbear_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) {
+
+ dropbear_assert(cbuf->used <= cbuf->size);
+ dropbear_assert(((2*cbuf->size)+cbuf->writepos-cbuf->readpos)%cbuf->size == cbuf->used%cbuf->size);
+ dropbear_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;
+ dropbear_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");
+ }
+
+ dropbear_assert(cbuf->used >= len);
+ cbuf->used -= len;
+ cbuf->readpos = (cbuf->readpos + len) % cbuf->size;
+}
diff --git a/circbuffer.h b/circbuffer.h
new file mode 100644
index 0000000..21c5134
--- /dev/null
+++ b/circbuffer.h
@@ -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
diff --git a/cli-algo.c b/cli-algo.c
new file mode 100644
index 0000000..ec3a1ff
--- /dev/null
+++ b/cli-algo.c
@@ -0,0 +1,99 @@
+/*
+ * Dropbear - a SSH2 server
+ * SSH client implementation
+ *
+ * Copyright (c) 2002,2003 Matt Johnston
+ * Copyright (c) 2004 by Mihnea Stoenescu
+ * 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 "algo.h"
+#include "dbutil.h"
+
+
+/*
+ * The chosen [encryption | MAC | compression] algorithm to each
+ * direction MUST be the first algorithm on the client's list
+ * that is also on the server's list.
+ */
+algo_type * cli_buf_match_algo(buffer* buf, algo_type localalgos[],
+ int *goodguess) {
+
+ unsigned char * algolist = NULL;
+ unsigned char * remotealgos[MAX_PROPOSED_ALGO];
+ unsigned int len;
+ unsigned int count, i, j;
+ algo_type * ret = NULL;
+
+ *goodguess = 0;
+
+ /* get the comma-separated list from the buffer ie "algo1,algo2,algo3" */
+ algolist = buf_getstring(buf, &len);
+ 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 */
+ }
+
+ /* remotealgos will contain a list of the strings parsed out */
+ /* We will have at least one string (even if it's just "") */
+ remotealgos[0] = algolist;
+ count = 1;
+ /* Iterate through, replacing ','s with NULs, to split it into
+ * words. */
+ for (i = 0; i < len; i++) {
+ if (algolist[i] == '\0') {
+ /* someone is trying something strange */
+ goto out;
+ }
+ if (algolist[i] == ',') {
+ algolist[i] = '\0';
+ remotealgos[count] = &algolist[i+1];
+ count++;
+ }
+ if (count == MAX_PROPOSED_ALGO) {
+ break;
+ }
+ }
+
+ /* iterate and find the first match */
+
+ for (j = 0; localalgos[j].name != NULL; j++) {
+ if (localalgos[j].usable) {
+ len = strlen(localalgos[j].name);
+ for (i = 0; i < count; i++) {
+ if (len == strlen(remotealgos[i])
+ && strncmp(localalgos[j].name,
+ remotealgos[i], len) == 0) {
+ if (i == 0 && j == 0) {
+ /* was a good guess */
+ *goodguess = 1;
+ }
+ ret = &localalgos[j];
+ goto out;
+ }
+ }
+ }
+ }
+
+out:
+ m_free(algolist);
+ return ret;
+}
+
diff --git a/cli-auth.c b/cli-auth.c
new file mode 100644
index 0000000..d08de9a
--- /dev/null
+++ b/cli-auth.c
@@ -0,0 +1,295 @@
+/*
+ * Dropbear SSH
+ *
+ * Copyright (c) 2002,2003 Matt Johnston
+ * Copyright (c) 2004 by Mihnea Stoenescu
+ * 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 "session.h"
+#include "auth.h"
+#include "dbutil.h"
+#include "buffer.h"
+#include "ssh.h"
+#include "packet.h"
+#include "runopts.h"
+
+void cli_authinitialise() {
+
+ memset(&ses.authstate, 0, sizeof(ses.authstate));
+}
+
+
+/* Send a "none" auth request to get available methods */
+void cli_auth_getmethods() {
+
+ TRACE(("enter cli_auth_getmethods"))
+
+ CHECKCLEARTOWRITE();
+
+ buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_REQUEST);
+ buf_putstring(ses.writepayload, cli_opts.username,
+ strlen(cli_opts.username));
+ buf_putstring(ses.writepayload, SSH_SERVICE_CONNECTION,
+ SSH_SERVICE_CONNECTION_LEN);
+ buf_putstring(ses.writepayload, "none", 4); /* 'none' method */
+
+ encrypt_packet();
+ TRACE(("leave cli_auth_getmethods"))
+
+}
+
+void recv_msg_userauth_banner() {
+
+ unsigned char* banner = NULL;
+ unsigned int bannerlen;
+ unsigned int i, linecount;
+
+ TRACE(("enter recv_msg_userauth_banner"))
+ if (ses.authstate.authdone) {
+ TRACE(("leave recv_msg_userauth_banner: banner after auth done"))
+ return;
+ }
+
+ banner = buf_getstring(ses.payload, &bannerlen);
+ buf_eatstring(ses.payload); /* The language string */
+
+ if (bannerlen > MAX_BANNER_SIZE) {
+ TRACE(("recv_msg_userauth_banner: bannerlen too long: %d", bannerlen))
+ goto out;
+ }
+
+ cleantext(banner);
+
+ /* Limit to 25 lines */
+ linecount = 1;
+ for (i = 0; i < bannerlen; i++) {
+ if (banner[i] == '\n') {
+ if (linecount >= MAX_BANNER_LINES) {
+ banner[i] = '\0';
+ break;
+ }
+ linecount++;
+ }
+ }
+
+ printf("%s\n", banner);
+
+out:
+ m_free(banner);
+ TRACE(("leave recv_msg_userauth_banner"))
+}
+
+/* This handles the message-specific types which
+ * all have a value of 60. These are
+ * SSH_MSG_USERAUTH_PASSWD_CHANGEREQ,
+ * SSH_MSG_USERAUTH_PK_OK, &
+ * SSH_MSG_USERAUTH_INFO_REQUEST. */
+void recv_msg_userauth_specific_60() {
+
+#ifdef ENABLE_CLI_PUBKEY_AUTH
+ if (cli_ses.lastauthtype == AUTH_TYPE_PUBKEY) {
+ recv_msg_userauth_pk_ok();
+ return;
+ }
+#endif
+
+#ifdef ENABLE_CLI_INTERACT_AUTH
+ if (cli_ses.lastauthtype == AUTH_TYPE_INTERACT) {
+ recv_msg_userauth_info_request();
+ return;
+ }
+#endif
+
+#ifdef ENABLE_CLI_PASSWORD_AUTH
+ if (cli_ses.lastauthtype == AUTH_TYPE_PASSWORD) {
+ /* Eventually there could be proper password-changing
+ * support. However currently few servers seem to
+ * implement it, and password auth is last-resort
+ * regardless - keyboard-interactive is more likely
+ * to be used anyway. */
+ dropbear_close("Your password has expired.");
+ }
+#endif
+
+ dropbear_exit("Unexpected userauth packet");
+}
+
+void recv_msg_userauth_failure() {
+
+ unsigned char * methods = NULL;
+ unsigned char * tok = NULL;
+ unsigned int methlen = 0;
+ unsigned int partial = 0;
+ unsigned int i = 0;
+
+ TRACE(("<- MSG_USERAUTH_FAILURE"))
+ TRACE(("enter recv_msg_userauth_failure"))
+
+ if (cli_ses.state != USERAUTH_REQ_SENT) {
+ /* Perhaps we should be more fatal? */
+ dropbear_exit("Unexpected userauth failure");
+ }
+
+#ifdef ENABLE_CLI_PUBKEY_AUTH
+ /* If it was a pubkey auth request, we should cross that key
+ * off the list. */
+ if (cli_ses.lastauthtype == AUTH_TYPE_PUBKEY) {
+ cli_pubkeyfail();
+ }
+#endif
+
+#ifdef ENABLE_CLI_INTERACT_AUTH
+ /* If we get a failure message for keyboard interactive without
+ * receiving any request info packet, then we don't bother trying
+ * keyboard interactive again */
+ if (cli_ses.lastauthtype == AUTH_TYPE_INTERACT
+ && !cli_ses.interact_request_received) {
+ TRACE(("setting auth_interact_failed = 1"))
+ cli_ses.auth_interact_failed = 1;
+ }
+#endif
+
+ cli_ses.lastauthtype = AUTH_TYPE_NONE;
+
+ methods = buf_getstring(ses.payload, &methlen);
+
+ partial = buf_getbool(ses.payload);
+
+ if (partial) {
+ dropbear_log(LOG_INFO, "Authentication partially succeeded, more attempts required");
+ } else {
+ ses.authstate.failcount++;
+ }
+
+ TRACE(("Methods (len %d): '%s'", methlen, methods))
+
+ ses.authstate.authdone=0;
+ ses.authstate.authtypes=0;
+
+ /* Split with nulls rather than commas */
+ for (i = 0; i < methlen; i++) {
+ if (methods[i] == ',') {
+ methods[i] = '\0';
+ }
+ }
+
+ 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))
+#ifdef ENABLE_CLI_PUBKEY_AUTH
+ if (strncmp(AUTH_METHOD_PUBKEY, tok,
+ AUTH_METHOD_PUBKEY_LEN) == 0) {
+ ses.authstate.authtypes |= AUTH_TYPE_PUBKEY;
+ }
+#endif
+#ifdef ENABLE_CLI_INTERACT_AUTH
+ if (strncmp(AUTH_METHOD_INTERACT, tok,
+ AUTH_METHOD_INTERACT_LEN) == 0) {
+ ses.authstate.authtypes |= AUTH_TYPE_INTERACT;
+ }
+#endif
+#ifdef ENABLE_CLI_PASSWORD_AUTH
+ if (strncmp(AUTH_METHOD_PASSWORD, tok,
+ AUTH_METHOD_PASSWORD_LEN) == 0) {
+ ses.authstate.authtypes |= AUTH_TYPE_PASSWORD;
+ }
+#endif
+ tok = &methods[i+1]; /* Must make sure we don't use it after the
+ last loop, since it'll point to something
+ undefined */
+ }
+ }
+
+ m_free(methods);
+
+ cli_ses.state = USERAUTH_FAIL_RCVD;
+
+ TRACE(("leave recv_msg_userauth_failure"))
+}
+
+void recv_msg_userauth_success() {
+ TRACE(("received msg_userauth_success"))
+ ses.authstate.authdone = 1;
+ cli_ses.state = USERAUTH_SUCCESS_RCVD;
+ cli_ses.lastauthtype = AUTH_TYPE_NONE;
+}
+
+void cli_auth_try() {
+
+ TRACE(("enter cli_auth_try"))
+ int finished = 0;
+
+ CHECKCLEARTOWRITE();
+
+ /* Order to try is pubkey, interactive, password.
+ * As soon as "finished" is set for one, we don't do any more. */
+#ifdef ENABLE_CLI_PUBKEY_AUTH
+ if (ses.authstate.authtypes & AUTH_TYPE_PUBKEY) {
+ finished = cli_auth_pubkey();
+ cli_ses.lastauthtype = AUTH_TYPE_PUBKEY;
+ }
+#endif
+
+#ifdef ENABLE_CLI_INTERACT_AUTH
+ if (!finished && ses.authstate.authtypes & AUTH_TYPE_INTERACT) {
+ if (cli_ses.auth_interact_failed) {
+ finished = 0;
+ } else {
+ cli_auth_interactive();
+ cli_ses.lastauthtype = AUTH_TYPE_INTERACT;
+ finished = 1;
+ }
+ }
+#endif
+
+#ifdef ENABLE_CLI_PASSWORD_AUTH
+ if (!finished && ses.authstate.authtypes & AUTH_TYPE_PASSWORD) {
+ cli_auth_password();
+ finished = 1;
+ cli_ses.lastauthtype = AUTH_TYPE_PASSWORD;
+ }
+#endif
+
+ TRACE(("cli_auth_try lastauthtype %d", cli_ses.lastauthtype))
+
+ if (!finished) {
+ dropbear_exit("No auth methods could be used.");
+ }
+
+ TRACE(("leave cli_auth_try"))
+}
+
+/* A helper for getpass() that exits if the user cancels. The returned
+ * password is statically allocated by getpass() */
+char* getpass_or_cancel()
+{
+ char* password = NULL;
+
+ password = getpass("Password: ");
+
+ /* 0x03 is a ctrl-c character in the buffer. */
+ if (password == NULL || strchr(password, '\3') != NULL) {
+ dropbear_close("Interrupted.");
+ }
+ return password;
+}
diff --git a/cli-authinteract.c b/cli-authinteract.c
new file mode 100644
index 0000000..5a169cb
--- /dev/null
+++ b/cli-authinteract.c
@@ -0,0 +1,168 @@
+/*
+ * Dropbear SSH
+ *
+ * Copyright (c) 2005 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 "buffer.h"
+#include "dbutil.h"
+#include "session.h"
+#include "ssh.h"
+#include "runopts.h"
+
+#ifdef ENABLE_CLI_INTERACT_AUTH
+
+static unsigned char* get_response(unsigned char* prompt)
+{
+ FILE* tty = NULL;
+ unsigned char* response = NULL;
+ /* not a password, but a reasonable limit */
+ char buf[DROPBEAR_MAX_CLI_PASS];
+ char* ret = NULL;
+
+ fprintf(stderr, "%s", prompt);
+
+ tty = fopen(_PATH_TTY, "r");
+ if (tty) {
+ ret = fgets(buf, sizeof(buf), tty);
+ fclose(tty);
+ } else {
+ ret = fgets(buf, sizeof(buf), stdin);
+ }
+
+ if (ret == NULL) {
+ response = (unsigned char*)m_strdup("");
+ } else {
+ unsigned int buflen = strlen(buf);
+ /* fgets includes newlines */
+ if (buflen > 0 && buf[buflen-1] == '\n')
+ buf[buflen-1] = '\0';
+ response = (unsigned char*)m_strdup(buf);
+ }
+
+ m_burn(buf, sizeof(buf));
+
+ return response;
+}
+
+void recv_msg_userauth_info_request() {
+
+ unsigned char *name = NULL;
+ unsigned char *instruction = NULL;
+ unsigned int num_prompts = 0;
+ unsigned int i;
+
+ unsigned char *prompt = NULL;
+ unsigned int echo = 0;
+ unsigned char *response = NULL;
+
+ TRACE(("enter recv_msg_recv_userauth_info_request"))
+
+ cli_ses.interact_request_received = 1;
+
+ name = buf_getstring(ses.payload, NULL);
+ instruction = buf_getstring(ses.payload, NULL);
+
+ /* language tag */
+ buf_eatstring(ses.payload);
+
+ num_prompts = buf_getint(ses.payload);
+
+ if (num_prompts >= DROPBEAR_MAX_CLI_INTERACT_PROMPTS) {
+ dropbear_exit("Too many prompts received for keyboard-interactive");
+ }
+
+ /* we'll build the response as we go */
+ CHECKCLEARTOWRITE();
+ buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_INFO_RESPONSE);
+ buf_putint(ses.writepayload, num_prompts);
+
+ if (strlen(name) > 0) {
+ cleantext(name);
+ fprintf(stderr, "%s", name);
+ m_free(name);
+ }
+ if (strlen(instruction) > 0) {
+ cleantext(instruction);
+ fprintf(stderr, "%s", instruction);
+ m_free(instruction);
+ }
+
+ for (i = 0; i < num_prompts; i++) {
+ unsigned int response_len = 0;
+ prompt = buf_getstring(ses.payload, NULL);
+ cleantext(prompt);
+
+ echo = buf_getbool(ses.payload);
+
+ if (!echo) {
+ unsigned char* p = getpass_or_cancel(prompt);
+ response = m_strdup(p);
+ m_burn(p, strlen(p));
+ } else {
+ response = get_response(prompt);
+ }
+
+ response_len = strlen(response);
+ buf_putstring(ses.writepayload, response, response_len);
+ m_burn(response, response_len);
+ m_free(response);
+ }
+
+ encrypt_packet();
+
+
+ TRACE(("leave recv_msg_recv_userauth_info_request"))
+}
+
+void cli_auth_interactive() {
+
+ TRACE(("enter cli_auth_interactive"))
+ CHECKCLEARTOWRITE();
+
+ buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_REQUEST);
+
+ /* username */
+ buf_putstring(ses.writepayload, cli_opts.username,
+ strlen(cli_opts.username));
+
+ /* service name */
+ buf_putstring(ses.writepayload, SSH_SERVICE_CONNECTION,
+ SSH_SERVICE_CONNECTION_LEN);
+
+ /* method */
+ buf_putstring(ses.writepayload, AUTH_METHOD_INTERACT,
+ AUTH_METHOD_INTERACT_LEN);
+
+ /* empty language tag */
+ buf_putstring(ses.writepayload, "", 0);
+
+ /* empty submethods */
+ buf_putstring(ses.writepayload, "", 0);
+
+ encrypt_packet();
+ cli_ses.interact_request_received = 0;
+
+ TRACE(("leave cli_auth_interactive"))
+
+}
+#endif /* ENABLE_CLI_INTERACT_AUTH */
diff --git a/cli-authpasswd.c b/cli-authpasswd.c
new file mode 100644
index 0000000..5dffac4
--- /dev/null
+++ b/cli-authpasswd.c
@@ -0,0 +1,150 @@
+/*
+ * 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. */
+
+#include "includes.h"
+#include "buffer.h"
+#include "dbutil.h"
+#include "session.h"
+#include "ssh.h"
+#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 */
+
+void cli_auth_password() {
+
+ char* password = NULL;
+
+ TRACE(("enter cli_auth_password"))
+ CHECKCLEARTOWRITE();
+
+#ifdef ENABLE_CLI_ASKPASS_HELPER
+ if (want_askpass())
+ password = gui_getpass("Password: ");
+ else
+#endif
+ password = getpass_or_cancel("Password: ");
+
+ buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_REQUEST);
+
+ buf_putstring(ses.writepayload, cli_opts.username,
+ strlen(cli_opts.username));
+
+ buf_putstring(ses.writepayload, SSH_SERVICE_CONNECTION,
+ SSH_SERVICE_CONNECTION_LEN);
+
+ buf_putstring(ses.writepayload, AUTH_METHOD_PASSWORD,
+ AUTH_METHOD_PASSWORD_LEN);
+
+ buf_putbyte(ses.writepayload, 0); /* FALSE - so says the spec */
+
+ buf_putstring(ses.writepayload, password, strlen(password));
+
+ encrypt_packet();
+ m_burn(password, strlen(password));
+
+ TRACE(("leave cli_auth_password"))
+}
+#endif /* ENABLE_CLI_PASSWORD_AUTH */
diff --git a/cli-authpubkey.c b/cli-authpubkey.c
new file mode 100644
index 0000000..9d36bc3
--- /dev/null
+++ b/cli-authpubkey.c
@@ -0,0 +1,187 @@
+/*
+ * Dropbear SSH
+ *
+ * Copyright (c) 2002,2003 Matt Johnston
+ * Copyright (c) 2004 by Mihnea Stoenescu
+ * 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 "buffer.h"
+#include "dbutil.h"
+#include "session.h"
+#include "ssh.h"
+#include "runopts.h"
+#include "auth.h"
+
+#ifdef ENABLE_CLI_PUBKEY_AUTH
+static void send_msg_userauth_pubkey(sign_key *key, int type, int realsign);
+
+/* Called when we receive a SSH_MSG_USERAUTH_FAILURE for a pubkey request.
+ * We use it to remove the key we tried from the list */
+void cli_pubkeyfail() {
+
+ struct SignKeyList *keyitem;
+ struct SignKeyList **previtem;
+
+ TRACE(("enter cli_pubkeyfail"))
+ previtem = &cli_opts.privkeys;
+
+ /* Find the key we failed with, and remove it */
+ for (keyitem = cli_opts.privkeys; keyitem != NULL; keyitem = keyitem->next) {
+ if (keyitem == cli_ses.lastprivkey) {
+ *previtem = keyitem->next;
+ }
+ previtem = &keyitem;
+ }
+
+ sign_key_free(cli_ses.lastprivkey->key); /* It won't be used again */
+ m_free(cli_ses.lastprivkey);
+
+ TRACE(("leave cli_pubkeyfail"))
+}
+
+void recv_msg_userauth_pk_ok() {
+
+ struct SignKeyList *keyitem;
+ buffer* keybuf;
+ char* algotype = NULL;
+ unsigned int algolen;
+ int keytype;
+ unsigned int remotelen;
+
+ 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))
+ m_free(algotype);
+
+ keybuf = buf_new(MAX_PUBKEY_SIZE);
+
+ remotelen = buf_getint(ses.payload);
+
+ /* Iterate through our keys, find which one it was that matched, and
+ * send a real request with that key */
+ for (keyitem = cli_opts.privkeys; keyitem != NULL; keyitem = keyitem->next) {
+
+ if (keyitem->type != keytype) {
+ /* Types differed */
+ TRACE(("types differed"))
+ continue;
+ }
+
+ /* Now we compare the contents of the key */
+ keybuf->pos = keybuf->len = 0;
+ buf_put_pub_key(keybuf, keyitem->key, keytype);
+ buf_setpos(keybuf, 0);
+ buf_incrpos(keybuf, 4); /* first int is the length of the remainder (ie
+ remotelen) which has already been taken from
+ the remote buffer */
+
+
+ if (keybuf->len-4 != 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"))
+ continue;
+ }
+
+ /* Success */
+ break;
+ }
+
+ if (keyitem != NULL) {
+ 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(("leave recv_msg_userauth_pk_ok"))
+}
+
+/* TODO: make it take an agent reference to use as well */
+static void send_msg_userauth_pubkey(sign_key *key, int type, int realsign) {
+
+ const char *algoname = NULL;
+ int algolen;
+ buffer* sigbuf = NULL;
+
+ TRACE(("enter send_msg_userauth_pubkey"))
+ CHECKCLEARTOWRITE();
+
+ buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_REQUEST);
+
+ buf_putstring(ses.writepayload, cli_opts.username,
+ strlen(cli_opts.username));
+
+ buf_putstring(ses.writepayload, SSH_SERVICE_CONNECTION,
+ SSH_SERVICE_CONNECTION_LEN);
+
+ buf_putstring(ses.writepayload, AUTH_METHOD_PUBKEY,
+ AUTH_METHOD_PUBKEY_LEN);
+
+ buf_putbyte(ses.writepayload, realsign);
+
+ algoname = signkey_name_from_type(type, &algolen);
+
+ buf_putstring(ses.writepayload, algoname, algolen);
+ buf_put_pub_key(ses.writepayload, key, type);
+
+ if (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);
+ buf_putstring(sigbuf, ses.session_id, SHA1_HASH_SIZE);
+ buf_putbytes(sigbuf, ses.writepayload->data, ses.writepayload->len);
+ buf_put_sign(ses.writepayload, key, type, sigbuf->data, sigbuf->len);
+ buf_free(sigbuf); /* Nothing confidential in the buffer */
+ }
+
+ encrypt_packet();
+ TRACE(("leave send_msg_userauth_pubkey"))
+}
+
+int cli_auth_pubkey() {
+
+ TRACE(("enter cli_auth_pubkey"))
+
+ if (cli_opts.privkeys != NULL) {
+ /* Send a trial request */
+ send_msg_userauth_pubkey(cli_opts.privkeys->key,
+ cli_opts.privkeys->type, 0);
+ cli_ses.lastprivkey = cli_opts.privkeys;
+ TRACE(("leave cli_auth_pubkey-success"))
+ return 1;
+ } else {
+ TRACE(("leave cli_auth_pubkey-failure"))
+ return 0;
+ }
+}
+#endif /* Pubkey auth */
diff --git a/cli-channel.c b/cli-channel.c
new file mode 100644
index 0000000..1bd49ab
--- /dev/null
+++ b/cli-channel.c
@@ -0,0 +1,62 @@
+/*
+ * 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() {
+
+ struct Channel *channel;
+ unsigned int datatype;
+
+ TRACE(("enter recv_msg_channel_extended_data"))
+
+ channel = getchannel();
+ 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"))
+}
diff --git a/cli-chansession.c b/cli-chansession.c
new file mode 100644
index 0000000..6d358b7
--- /dev/null
+++ b/cli-chansession.c
@@ -0,0 +1,380 @@
+/*
+ * Dropbear SSH
+ *
+ * Copyright (c) 2002,2003 Matt Johnston
+ * Copyright (c) 2004 by Mihnea Stoenescu
+ * 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 "packet.h"
+#include "buffer.h"
+#include "session.h"
+#include "dbutil.h"
+#include "channel.h"
+#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);
+
+static void send_chansess_pty_req(struct Channel *channel);
+static void send_chansess_shell_req(struct Channel *channel);
+
+static void cli_tty_setup();
+
+const struct ChanType clichansess = {
+ 0, /* sepfds */
+ "session", /* name */
+ cli_initchansess, /* inithandler */
+ NULL, /* checkclosehandler */
+ 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_getbool(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 *UNUSED(channel)) {
+
+ /* This channel hasn't gone yet, so we have > 1 */
+ if (ses.chancount > 1) {
+ dropbear_log(LOG_INFO, "Waiting for other channels to close...");
+ }
+
+ cli_tty_cleanup(); /* Restore tty modes etc */
+
+}
+
+static void start_channel_request(struct Channel *channel,
+ unsigned char *type) {
+
+ CHECKCLEARTOWRITE();
+ buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_REQUEST);
+ buf_putint(ses.writepayload, channel->remotechan);
+
+ buf_putstring(ses.writepayload, type, strlen(type));
+
+}
+
+/* Taken from OpenSSH's sshtty.c:
+ * RCSID("OpenBSD: sshtty.c,v 1.5 2003/09/19 17:43:35 markus Exp "); */
+static void cli_tty_setup() {
+
+ struct termios tio;
+
+ TRACE(("enter cli_pty_setup"))
+
+ if (cli_ses.tty_raw_mode == 1) {
+ TRACE(("leave cli_tty_setup: already in raw mode!"))
+ return;
+ }
+
+ if (tcgetattr(STDIN_FILENO, &tio) == -1) {
+ dropbear_exit("Failed to set raw TTY mode");
+ }
+
+ /* make a copy */
+ cli_ses.saved_tio = tio;
+
+ tio.c_iflag |= IGNPAR;
+ tio.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF);
+#ifdef IUCLC
+ tio.c_iflag &= ~IUCLC;
+#endif
+ tio.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL);
+#ifdef IEXTEN
+ tio.c_lflag &= ~IEXTEN;
+#endif
+ tio.c_oflag &= ~OPOST;
+ tio.c_cc[VMIN] = 1;
+ tio.c_cc[VTIME] = 0;
+ if (tcsetattr(STDIN_FILENO, TCSADRAIN, &tio) == -1) {
+ dropbear_exit("Failed to set raw TTY mode");
+ }
+
+ cli_ses.tty_raw_mode = 1;
+ TRACE(("leave cli_tty_setup"))
+}
+
+void cli_tty_cleanup() {
+
+ TRACE(("enter cli_tty_cleanup"))
+
+ if (cli_ses.tty_raw_mode == 0) {
+ TRACE(("leave cli_tty_cleanup: not in raw mode"))
+ return;
+ }
+
+ if (tcsetattr(STDIN_FILENO, TCSADRAIN, &cli_ses.saved_tio) == -1) {
+ dropbear_log(LOG_WARNING, "Failed restoring TTY");
+ } else {
+ cli_ses.tty_raw_mode = 0;
+ }
+
+ TRACE(("leave cli_tty_cleanup"))
+}
+
+static void put_termcodes() {
+
+ TRACE(("enter put_termcodes"))
+
+ struct termios tio;
+ unsigned int sshcode;
+ const struct TermCode *termcode;
+ unsigned int value;
+ unsigned int mapcode;
+
+ unsigned int bufpos1, bufpos2;
+
+ if (tcgetattr(STDIN_FILENO, &tio) == -1) {
+ dropbear_log(LOG_WARNING, "Failed reading termmodes");
+ buf_putint(ses.writepayload, 1); /* Just the terminator */
+ buf_putbyte(ses.writepayload, 0); /* TTY_OP_END */
+ return;
+ }
+
+ bufpos1 = ses.writepayload->pos;
+ buf_putint(ses.writepayload, 0); /* A placeholder for the final length */
+
+ /* As with Dropbear server, we ignore baud rates for now */
+ for (sshcode = 1; sshcode < MAX_TERMCODE; sshcode++) {
+
+ termcode = &termcodes[sshcode];
+ mapcode = termcode->mapcode;
+
+ switch (termcode->type) {
+
+ case TERMCODE_NONE:
+ continue;
+
+ case TERMCODE_CONTROLCHAR:
+ value = tio.c_cc[mapcode];
+ break;
+
+ case TERMCODE_INPUT:
+ value = tio.c_iflag & mapcode;
+ break;
+
+ case TERMCODE_OUTPUT:
+ value = tio.c_oflag & mapcode;
+ break;
+
+ case TERMCODE_LOCAL:
+ value = tio.c_lflag & mapcode;
+ break;
+
+ case TERMCODE_CONTROL:
+ value = tio.c_cflag & mapcode;
+ break;
+
+ default:
+ continue;
+
+ }
+
+ /* If we reach here, we have something to say */
+ buf_putbyte(ses.writepayload, sshcode);
+ buf_putint(ses.writepayload, value);
+ }
+
+ buf_putbyte(ses.writepayload, 0); /* THE END, aka TTY_OP_END */
+
+ /* Put the string length at the start of the buffer */
+ bufpos2 = ses.writepayload->pos;
+
+ buf_setpos(ses.writepayload, bufpos1); /* Jump back */
+ buf_putint(ses.writepayload, bufpos2 - bufpos1 - 4); /* len(termcodes) */
+ buf_setpos(ses.writepayload, bufpos2); /* Back where we were */
+
+ TRACE(("leave put_termcodes"))
+}
+
+static void put_winsize() {
+
+ struct winsize ws;
+
+ if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) < 0) {
+ /* Some sane defaults */
+ ws.ws_row = 25;
+ ws.ws_col = 80;
+ ws.ws_xpixel = 0;
+ ws.ws_ypixel = 0;
+ }
+
+ buf_putint(ses.writepayload, ws.ws_col); /* Cols */
+ buf_putint(ses.writepayload, ws.ws_row); /* Rows */
+ buf_putint(ses.writepayload, ws.ws_xpixel); /* Width */
+ buf_putint(ses.writepayload, ws.ws_ypixel); /* Height */
+
+}
+
+static void sigwinch_handler(int UNUSED(unused)) {
+
+ cli_ses.winchange = 1;
+
+}
+
+void cli_chansess_winchange() {
+
+ unsigned int i;
+ struct Channel *channel = NULL;
+
+ for (i = 0; i < ses.chansize; i++) {
+ channel = ses.channels[i];
+ if (channel != NULL && channel->type == &clichansess) {
+ CHECKCLEARTOWRITE();
+ buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_REQUEST);
+ buf_putint(ses.writepayload, channel->remotechan);
+ buf_putstring(ses.writepayload, "window-change", 13);
+ buf_putbyte(ses.writepayload, 0); /* FALSE says the spec */
+ put_winsize();
+ encrypt_packet();
+ }
+ }
+ cli_ses.winchange = 0;
+}
+
+static void send_chansess_pty_req(struct Channel *channel) {
+
+ unsigned char* term = NULL;
+
+ TRACE(("enter send_chansess_pty_req"))
+
+ start_channel_request(channel, "pty-req");
+
+ /* Don't want replies */
+ buf_putbyte(ses.writepayload, 0);
+
+ /* Get the terminal */
+ term = getenv("TERM");
+ if (term == NULL) {
+ term = "vt100"; /* Seems a safe default */
+ }
+ buf_putstring(ses.writepayload, term, strlen(term));
+
+ /* Window size */
+ put_winsize();
+
+ /* Terminal mode encoding */
+ put_termcodes();
+
+ encrypt_packet();
+
+ /* Set up a window-change handler */
+ if (signal(SIGWINCH, sigwinch_handler) == SIG_ERR) {
+ dropbear_exit("signal error");
+ }
+ 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"))
+
+ if (cli_opts.cmd) {
+ reqtype = "exec";
+ } else {
+ reqtype = "shell";
+ }
+
+ start_channel_request(channel, reqtype);
+
+ /* XXX TODO */
+ buf_putbyte(ses.writepayload, 0); /* Don't want replies */
+ if (cli_opts.cmd) {
+ buf_putstring(ses.writepayload, cli_opts.cmd, strlen(cli_opts.cmd));
+ }
+
+ encrypt_packet();
+ TRACE(("leave send_chansess_shell_req"))
+}
+
+static int cli_initchansess(struct Channel *channel) {
+
+
+ channel->writefd = STDOUT_FILENO;
+ setnonblocking(STDOUT_FILENO);
+
+ channel->readfd = 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);
+ }
+
+ send_chansess_shell_req(channel);
+
+ if (cli_opts.wantpty) {
+ cli_tty_setup();
+ }
+
+ return 0; /* Success */
+
+}
+
+void 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");
+ }
+
+ /* No special channel request data */
+ encrypt_packet();
+ TRACE(("leave cli_send_chansess_request"))
+
+}
diff --git a/cli-kex.c b/cli-kex.c
new file mode 100644
index 0000000..40d4e95
--- /dev/null
+++ b/cli-kex.c
@@ -0,0 +1,288 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * Copyright (c) 2002-2004 Matt Johnston
+ * Copyright (c) 2004 by Mihnea Stoenescu
+ * 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 "session.h"
+#include "dbutil.h"
+#include "algo.h"
+#include "buffer.h"
+#include "session.h"
+#include "kex.h"
+#include "ssh.h"
+#include "packet.h"
+#include "bignum.h"
+#include "random.h"
+#include "runopts.h"
+#include "signkey.h"
+
+
+static void checkhostkey(unsigned char* keyblob, unsigned int keybloblen);
+#define MAX_KNOWNHOSTS_LINE 4500
+
+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();
+ buf_putbyte(ses.writepayload, SSH_MSG_KEXDH_INIT);
+ buf_putmpint(ses.writepayload, cli_ses.dh_e);
+ encrypt_packet();
+ ses.requirenext = SSH_MSG_KEXDH_REPLY;
+}
+
+/* Handle a diffie-hellman key exchange reply. */
+void recv_msg_kexdh_reply() {
+
+ DEF_MP_INT(dh_f);
+ sign_key *hostkey = NULL;
+ unsigned int type, keybloblen;
+ unsigned char* keyblob = NULL;
+
+
+ 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))
+
+ hostkey = new_sign_key();
+ keybloblen = buf_getint(ses.payload);
+
+ keyblob = buf_getptr(ses.payload, keybloblen);
+ if (!ses.kexstate.donefirstkex) {
+ /* Only makes sense the first time */
+ checkhostkey(keyblob, keybloblen);
+ }
+
+ if (buf_get_pub_key(ses.payload, hostkey, &type) != DROPBEAR_SUCCESS) {
+ TRACE(("failed getting pubkey"))
+ dropbear_exit("Bad KEX packet");
+ }
+
+ if (buf_getmpint(ses.payload, &dh_f) != DROPBEAR_SUCCESS) {
+ 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) {
+ dropbear_exit("Bad hostkey signature");
+ }
+
+ sign_key_free(hostkey);
+ hostkey = NULL;
+
+ send_msg_newkeys();
+ ses.requirenext = SSH_MSG_NEWKEYS;
+ 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);
+
+ tty = fopen(_PATH_TTY, "r");
+ if (tty) {
+ response = getc(tty);
+ fclose(tty);
+ } else {
+ response = getc(stdin);
+ }
+
+ if (response == 'y') {
+ m_free(fp);
+ return;
+ }
+
+ dropbear_exit("Didn't validate host key");
+}
+
+static void checkhostkey(unsigned char* keyblob, unsigned int keybloblen) {
+
+ char * filename = NULL;
+ FILE *hostsfile = NULL;
+ int readonly = 0;
+ struct passwd *pw = NULL;
+ unsigned int hostlen, algolen;
+ unsigned long len;
+ const char *algoname = NULL;
+ buffer * line = NULL;
+ int ret;
+
+ pw = getpwuid(getuid());
+
+ if (pw == NULL) {
+ dropbear_exit("Failed to get homedir");
+ }
+
+ len = strlen(pw->pw_dir);
+ filename = m_malloc(len + 18); /* "/.ssh/known_hosts" and null-terminator*/
+
+ snprintf(filename, len+18, "%s/.ssh", pw->pw_dir);
+ /* 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, "a+");
+
+ 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 */
+ }
+
+ line = buf_new(MAX_KNOWNHOSTS_LINE);
+ hostlen = strlen(cli_opts.remotehost);
+ algoname = signkey_name_from_type(ses.newkeys->algo_hostkey, &algolen);
+
+ do {
+ if (buf_getline(line, hostsfile) == DROPBEAR_FAILURE) {
+ TRACE(("failed reading line: prob EOF"))
+ break;
+ }
+
+ /* The line is too short to be sensible */
+ /* "30" is 'enough to hold ssh-dss plus the spaces, ie so we don't
+ * 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"))
+ continue;
+ }
+
+ /* Compare hostnames */
+ if (strncmp(cli_opts.remotehost, buf_getptr(line, hostlen),
+ hostlen) != 0) {
+ 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"))
+ continue;
+ }
+
+ if ( strncmp(buf_getptr(line, algolen), algoname, algolen) != 0) {
+ TRACE(("algo doesn't match"))
+ continue;
+ }
+
+ buf_incrpos(line, algolen);
+ if (buf_getbyte(line) != ' ') {
+ TRACE(("missing space after algo"))
+ continue;
+ }
+
+ /* Now we're at the interesting hostkey */
+ ret = cmp_base64_key(keyblob, keybloblen, algoname, algolen, line);
+
+ if (ret == DROPBEAR_SUCCESS) {
+ /* Good matching key */
+ TRACE(("good matching key"))
+ goto out;
+ }
+
+ /* The keys didn't match. eep. */
+ } while (1); /* keep going 'til something happens */
+
+ /* Key doesn't exist yet */
+ ask_to_confirm(keyblob, 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); /* In case it wasn't opened append */
+ buf_setpos(line, 0);
+ buf_setlen(line, 0);
+ buf_putbytes(line, ses.remotehost, hostlen);
+ buf_putbyte(line, ' ');
+ buf_putbytes(line, algoname, algolen);
+ buf_putbyte(line, ' ');
+ len = line->size - line->pos;
+ 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);
+ buf_incrwritepos(line, len);
+ buf_putbyte(line, '\n');
+ buf_setpos(line, 0);
+ fwrite(buf_getptr(line, line->len), line->len, 1, hostsfile);
+ /* We ignore errors, since there's not much we can do about them */
+
+out:
+ if (hostsfile != NULL) {
+ fclose(hostsfile);
+ }
+ m_free(filename);
+ if (line != NULL) {
+ buf_free(line);
+ }
+}
diff --git a/cli-main.c b/cli-main.c
new file mode 100644
index 0000000..3f767c9
--- /dev/null
+++ b/cli-main.c
@@ -0,0 +1,112 @@
+/*
+ * Dropbear - a SSH2 server
+ * SSH client implementation
+ *
+ * Copyright (c) 2002,2003 Matt Johnston
+ * Copyright (c) 2004 by Mihnea Stoenescu
+ * 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 "runopts.h"
+#include "session.h"
+
+static void cli_dropbear_exit(int exitcode, const char* format, va_list param);
+static void cli_dropbear_log(int priority, const char* format, va_list param);
+
+#if defined(DBMULTI_dbclient) || !defined(DROPBEAR_MULTI)
+#if defined(DBMULTI_dbclient) && defined(DROPBEAR_MULTI)
+int cli_main(int argc, char ** argv) {
+#else
+int main(int argc, char ** argv) {
+#endif
+
+ int sock;
+ char* error = NULL;
+ char* hostandport;
+ int len;
+
+ _dropbear_exit = cli_dropbear_exit;
+ _dropbear_log = cli_dropbear_log;
+
+ cli_getopts(argc, argv);
+
+ TRACE(("user='%s' host='%s' port='%s'", cli_opts.username,
+ 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);
+
+ if (sock < 0) {
+ dropbear_exit("%s", error);
+ }
+
+ /* Set up the host:port log */
+ len = strlen(cli_opts.remotehost);
+ len += 10; /* 16 bit port and leeway*/
+ hostandport = (char*)m_malloc(len);
+ snprintf(hostandport, len, "%s:%s",
+ cli_opts.remotehost, cli_opts.remoteport);
+
+ cli_session(sock, hostandport);
+
+ /* not reached */
+ return -1;
+}
+#endif /* DBMULTI stuff */
+
+static void cli_dropbear_exit(int exitcode, const char* format, va_list param) {
+
+ char fmtbuf[300];
+
+ if (!sessinitdone) {
+ snprintf(fmtbuf, sizeof(fmtbuf), "exited: %s",
+ format);
+ } else {
+ snprintf(fmtbuf, sizeof(fmtbuf),
+ "connection to %s@%s:%s exited: %s",
+ cli_opts.username, cli_opts.remotehost,
+ cli_opts.remoteport, format);
+ }
+
+ /* Do the cleanup first, since then the terminal will be reset */
+ cli_session_cleanup();
+ common_session_cleanup();
+
+ _dropbear_log(LOG_INFO, fmtbuf, param);
+
+ exit(exitcode);
+}
+
+static void cli_dropbear_log(int UNUSED(priority),
+ const char* format, va_list param) {
+
+ char printbuf[1024];
+
+ vsnprintf(printbuf, sizeof(printbuf), format, param);
+
+ fprintf(stderr, "%s: %s\n", cli_opts.progname, printbuf);
+
+}
diff --git a/cli-runopts.c b/cli-runopts.c
new file mode 100644
index 0000000..54d4875
--- /dev/null
+++ b/cli-runopts.c
@@ -0,0 +1,414 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+#include "includes.h"
+#include "runopts.h"
+#include "signkey.h"
+#include "buffer.h"
+#include "dbutil.h"
+#include "algo.h"
+#include "tcpfwd.h"
+
+cli_runopts cli_opts; /* GLOBAL */
+
+static void printhelp();
+static void parsehostname(char* userhostarg);
+#ifdef ENABLE_CLI_PUBKEY_AUTH
+static void loadidentityfile(const char* filename);
+#endif
+#ifdef ENABLE_CLI_ANYTCPFWD
+static void addforward(char* str, struct TCPFwdList** fwdlist);
+#endif
+
+static void printhelp() {
+
+ fprintf(stderr, "Dropbear client v%s\n"
+ "Usage: %s [options] [user@]host\n"
+ "Options are:\n"
+ "-p <remoteport>\n"
+ "-l <username>\n"
+ "-t Allocate a pty\n"
+ "-T Don't allocate a pty\n"
+#ifdef ENABLE_CLI_PUBKEY_AUTH
+ "-i <identityfile> (multiple allowed)\n"
+#endif
+#ifdef ENABLE_CLI_LOCALTCPFWD
+ "-L <listenport:remotehost:remoteport> Local port forwarding\n"
+ "-g Allow remote hosts to connect to forwarded ports\n"
+#endif
+#ifdef ENABLE_CLI_REMOTETCPFWD
+ "-R <listenport:remotehost:remoteport> Remote port forwarding\n"
+#endif
+#ifdef DEBUG_TRACE
+ "-v verbose\n"
+#endif
+ ,DROPBEAR_VERSION, cli_opts.progname);
+}
+
+void cli_getopts(int argc, char ** argv) {
+
+ unsigned int i, j;
+ char ** next = 0;
+ unsigned int cmdlen;
+#ifdef ENABLE_CLI_PUBKEY_AUTH
+ int nextiskey = 0; /* A flag if the next argument is a keyfile */
+#endif
+#ifdef ENABLE_CLI_LOCALTCPFWD
+ int nextislocal = 0;
+#endif
+#ifdef ENABLE_CLI_REMOTETCPFWD
+ int nextisremote = 0;
+#endif
+ char* dummy = NULL; /* Not used for anything real */
+
+ /* see printhelp() for options */
+ cli_opts.progname = argv[0];
+ cli_opts.remotehost = NULL;
+ cli_opts.remoteport = NULL;
+ cli_opts.username = NULL;
+ cli_opts.cmd = NULL;
+ cli_opts.wantpty = 9; /* 9 means "it hasn't been touched", gets set later */
+#ifdef ENABLE_CLI_PUBKEY_AUTH
+ cli_opts.privkeys = NULL;
+#endif
+#ifdef ENABLE_CLI_LOCALTCPFWD
+ cli_opts.localfwds = NULL;
+ opts.listen_fwd_all = 0;
+#endif
+#ifdef ENABLE_CLI_REMOTETCPFWD
+ cli_opts.remotefwds = NULL;
+#endif
+ /* not yet
+ opts.ipv4 = 1;
+ opts.ipv6 = 1;
+ */
+
+ /* Iterate all the arguments */
+ for (i = 1; i < (unsigned int)argc; i++) {
+#ifdef ENABLE_CLI_PUBKEY_AUTH
+ if (nextiskey) {
+ /* Load a hostkey since the previous argument was "-i" */
+ loadidentityfile(argv[i]);
+ nextiskey = 0;
+ continue;
+ }
+#endif
+#ifdef ENABLE_CLI_REMOTETCPFWD
+ if (nextisremote) {
+ TRACE(("nextisremote true"))
+ addforward(argv[i], &cli_opts.remotefwds);
+ nextisremote = 0;
+ continue;
+ }
+#endif
+#ifdef ENABLE_CLI_LOCALTCPFWD
+ if (nextislocal) {
+ TRACE(("nextislocal true"))
+ addforward(argv[i], &cli_opts.localfwds);
+ nextislocal = 0;
+ continue;
+ }
+#endif
+ if (next) {
+ /* The previous flag set a value to assign */
+ *next = argv[i];
+ if (*next == NULL) {
+ dropbear_exit("Invalid null argument");
+ }
+ next = NULL;
+ continue;
+ }
+
+ if (argv[i][0] == '-') {
+ /* A flag *waves* */
+
+ switch (argv[i][1]) {
+ case 'p': /* remoteport */
+ next = &cli_opts.remoteport;
+ break;
+#ifdef ENABLE_CLI_PUBKEY_AUTH
+ case 'i': /* an identityfile */
+ /* 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 */
+ cli_opts.wantpty = 1;
+ break;
+ case 'T': /* don't want a pty */
+ cli_opts.wantpty = 0;
+ break;
+#ifdef ENABLE_CLI_LOCALTCPFWD
+ case 'L':
+ nextislocal = 1;
+ break;
+ case 'g':
+ opts.listen_fwd_all = 1;
+ break;
+#endif
+#ifdef ENABLE_CLI_REMOTETCPFWD
+ case 'R':
+ nextisremote = 1;
+ break;
+#endif
+ case 'l':
+ next = &cli_opts.username;
+ break;
+ case 'h':
+ printhelp();
+ exit(EXIT_SUCCESS);
+ break;
+#ifdef DEBUG_TRACE
+ case 'v':
+ debug_trace = 1;
+ break;
+#endif
+ case 'F':
+ case 'e':
+ case 'c':
+ case 'm':
+ case 'D':
+#ifndef ENABLE_CLI_REMOTETCPFWD
+ case 'R':
+#endif
+#ifndef ENABLE_CLI_LOCALTCPFWD
+ case 'L':
+#endif
+ case 'o':
+ case 'b':
+ next = &dummy;
+ default:
+ fprintf(stderr,
+ "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]))
+
+ /* Either the hostname or commands */
+
+ if (cli_opts.remotehost == NULL) {
+
+ parsehostname(argv[i]);
+
+ } else {
+
+ /* this is part of the commands to send - after this we
+ * don't parse any more options, and flags are sent as the
+ * command */
+ cmdlen = 0;
+ for (j = i; j < (unsigned int)argc; j++) {
+ cmdlen += strlen(argv[j]) + 1; /* +1 for spaces */
+ }
+ /* Allocate the space */
+ cli_opts.cmd = (char*)m_malloc(cmdlen);
+ cli_opts.cmd[0] = '\0';
+
+ /* Append all the bits */
+ for (j = i; j < (unsigned int)argc; j++) {
+ strlcat(cli_opts.cmd, argv[j], cmdlen);
+ strlcat(cli_opts.cmd, " ", cmdlen);
+ }
+ /* It'll be null-terminated here */
+
+ /* We've eaten all the options and flags */
+ break;
+ }
+ }
+ }
+
+ if (cli_opts.remotehost == NULL) {
+ printhelp();
+ exit(EXIT_FAILURE);
+ }
+
+ if (cli_opts.remoteport == NULL) {
+ cli_opts.remoteport = "22";
+ }
+
+ /* If not explicitly specified with -t or -T, we don't want a pty if
+ * there's a command, but we do otherwise */
+ if (cli_opts.wantpty == 9) {
+ if (cli_opts.cmd == NULL) {
+ cli_opts.wantpty = 1;
+ } else {
+ cli_opts.wantpty = 0;
+ }
+ }
+}
+
+#ifdef ENABLE_CLI_PUBKEY_AUTH
+static void loadidentityfile(const char* filename) {
+
+ struct SignKeyList * nextkey;
+ sign_key *key;
+ int keytype;
+
+ key = new_sign_key();
+ keytype = DROPBEAR_SIGNKEY_ANY;
+ if ( readhostkey(filename, key, &keytype) != DROPBEAR_SUCCESS ) {
+
+ fprintf(stderr, "Failed loading keyfile '%s'\n", filename);
+ sign_key_free(key);
+
+ } else {
+
+ nextkey = (struct SignKeyList*)m_malloc(sizeof(struct SignKeyList));
+ nextkey->key = key;
+ nextkey->next = cli_opts.privkeys;
+ nextkey->type = keytype;
+ cli_opts.privkeys = nextkey;
+ }
+}
+#endif
+
+
+/* Parses a [user@]hostname argument. userhostarg is the argv[i] corresponding
+ * - note that it will be modified */
+static void parsehostname(char* orighostarg) {
+
+ uid_t uid;
+ struct passwd *pw = NULL;
+ char *userhostarg = NULL;
+
+ /* We probably don't want to be editing argvs */
+ userhostarg = m_strdup(orighostarg);
+
+ cli_opts.remotehost = strchr(userhostarg, '@');
+ if (cli_opts.remotehost == NULL) {
+ /* no username portion, the cli-auth.c code can figure the
+ * local user's name */
+ cli_opts.remotehost = userhostarg;
+ } else {
+ cli_opts.remotehost[0] = '\0'; /* Split the user/host */
+ cli_opts.remotehost++;
+ cli_opts.username = userhostarg;
+ }
+
+ if (cli_opts.username == NULL) {
+ uid = getuid();
+
+ pw = getpwuid(uid);
+ if (pw == NULL || pw->pw_name == NULL) {
+ dropbear_exit("Unknown own user");
+ }
+
+ cli_opts.username = m_strdup(pw->pw_name);
+ }
+
+ if (cli_opts.remotehost[0] == '\0') {
+ dropbear_exit("Bad hostname");
+ }
+}
+
+#ifdef ENABLE_CLI_ANYTCPFWD
+/* Turn a "listenport:remoteaddr:remoteport" string into into a forwarding
+ * set, and add it to the forwarding list */
+static void addforward(char* origstr, struct TCPFwdList** fwdlist) {
+
+ char * listenport = NULL;
+ char * connectport = NULL;
+ char * connectaddr = NULL;
+ struct TCPFwdList* newfwd = NULL;
+ char * str = NULL;
+
+ TRACE(("enter addforward"))
+
+ /* We probably don't want to be editing argvs */
+ str = m_strdup(origstr);
+
+ listenport = str;
+
+ connectaddr = strchr(str, ':');
+ if (connectaddr == NULL) {
+ TRACE(("connectaddr == NULL"))
+ goto fail;
+ }
+
+ connectaddr[0] = '\0';
+ connectaddr++;
+
+ connectport = strchr(connectaddr, ':');
+ if (connectport == NULL) {
+ TRACE(("connectport == NULL"))
+ goto fail;
+ }
+
+ connectport[0] = '\0';
+ connectport++;
+
+ newfwd = (struct TCPFwdList*)m_malloc(sizeof(struct TCPFwdList));
+
+ /* Now we check the ports - note that the port ints are unsigned,
+ * the check later only checks for >= MAX_PORT */
+ newfwd->listenport = strtol(listenport, NULL, 10);
+ if (errno != 0) {
+ TRACE(("bad listenport strtol"))
+ goto fail;
+ }
+
+ newfwd->connectport = strtol(connectport, NULL, 10);
+ if (errno != 0) {
+ TRACE(("bad connectport strtol"))
+ goto fail;
+ }
+
+ newfwd->connectaddr = connectaddr;
+
+ if (newfwd->listenport > 65535) {
+ TRACE(("listenport > 65535"))
+ goto badport;
+ }
+
+ if (newfwd->connectport > 65535) {
+ TRACE(("connectport > 65535"))
+ goto badport;
+ }
+
+ newfwd->next = *fwdlist;
+ *fwdlist = newfwd;
+
+ TRACE(("leave addforward: done"))
+ return;
+
+fail:
+ dropbear_exit("Bad TCP forward '%s'", origstr);
+
+badport:
+ dropbear_exit("Bad TCP port in '%s'", origstr);
+}
+#endif
diff --git a/cli-service.c b/cli-service.c
new file mode 100644
index 0000000..87b6ed2
--- /dev/null
+++ b/cli-service.c
@@ -0,0 +1,87 @@
+/*
+ * Dropbear SSH
+ *
+ * Copyright (c) 2002,2003 Matt Johnston
+ * Copyright (c) 2004 by Mihnea Stoenescu
+ * 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 "service.h"
+#include "dbutil.h"
+#include "packet.h"
+#include "buffer.h"
+#include "session.h"
+#include "ssh.h"
+
+void send_msg_service_request(char* servicename) {
+
+ TRACE(("enter send_msg_service_request: servicename='%s'", servicename))
+
+ CHECKCLEARTOWRITE();
+
+ buf_putbyte(ses.writepayload, SSH_MSG_SERVICE_REQUEST);
+ buf_putstring(ses.writepayload, servicename, strlen(servicename));
+
+ encrypt_packet();
+ TRACE(("leave send_msg_service_request"))
+}
+
+/* This just sets up the state variables right for the main client session loop
+ * to deal with */
+void recv_msg_service_accept() {
+
+ unsigned char* servicename;
+ unsigned int len;
+
+ TRACE(("enter recv_msg_service_accept"))
+
+ servicename = buf_getstring(ses.payload, &len);
+
+ /* ssh-userauth */
+ if (cli_ses.state == SERVICE_AUTH_REQ_SENT
+ && len == SSH_SERVICE_USERAUTH_LEN
+ && strncmp(SSH_SERVICE_USERAUTH, servicename, len) == 0) {
+
+ cli_ses.state = SERVICE_AUTH_ACCEPT_RCVD;
+ m_free(servicename);
+ TRACE(("leave recv_msg_service_accept: done ssh-userauth"))
+ return;
+ }
+
+ /* ssh-connection */
+ if (cli_ses.state == SERVICE_CONN_REQ_SENT
+ && len == SSH_SERVICE_CONNECTION_LEN
+ && strncmp(SSH_SERVICE_CONNECTION, servicename, len) == 0) {
+
+ if (ses.authstate.authdone != 1) {
+ dropbear_exit("request for connection before auth");
+ }
+
+ cli_ses.state = SERVICE_CONN_ACCEPT_RCVD;
+ m_free(servicename);
+ TRACE(("leave recv_msg_service_accept: done ssh-connection"))
+ return;
+ }
+
+ dropbear_exit("unrecognised service accept");
+ /* m_free(servicename); not reached */
+
+}
diff --git a/cli-session.c b/cli-session.c
new file mode 100644
index 0000000..35510fa
--- /dev/null
+++ b/cli-session.c
@@ -0,0 +1,303 @@
+/*
+ * Dropbear SSH
+ *
+ * Copyright (c) 2002,2003 Matt Johnston
+ * Copyright (c) 2004 by Mihnea Stoenescu
+ * 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 "session.h"
+#include "dbutil.h"
+#include "kex.h"
+#include "ssh.h"
+#include "packet.h"
+#include "tcpfwd.h"
+#include "channel.h"
+#include "random.h"
+#include "service.h"
+#include "runopts.h"
+#include "chansession.h"
+
+static void cli_remoteclosed();
+static void cli_sessionloop();
+static void cli_session_init();
+static void cli_finished();
+
+struct clientsession cli_ses; /* GLOBAL */
+
+/* Sorted in decreasing frequency will be more efficient - data and window
+ * should be first */
+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 */
+ {SSH_MSG_KEXINIT, recv_msg_kexinit},
+ {SSH_MSG_KEXDH_REPLY, recv_msg_kexdh_reply}, /* client */
+ {SSH_MSG_NEWKEYS, recv_msg_newkeys},
+ {SSH_MSG_SERVICE_ACCEPT, recv_msg_service_accept}, /* client */
+ {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},
+ {SSH_MSG_CHANNEL_OPEN_CONFIRMATION, recv_msg_channel_open_confirmation},
+ {SSH_MSG_CHANNEL_OPEN_FAILURE, recv_msg_channel_open_failure},
+ {SSH_MSG_USERAUTH_BANNER, recv_msg_userauth_banner}, /* client */
+ {SSH_MSG_USERAUTH_SPECIFIC_60, recv_msg_userauth_specific_60}, /* client */
+ {0, 0} /* End */
+};
+
+static const struct ChanType *cli_chantypes[] = {
+#ifdef ENABLE_CLI_REMOTETCPFWD
+ &cli_chan_tcpremote,
+#endif
+ NULL /* Null termination */
+};
+
+void cli_session(int sock, char* remotehost) {
+
+ seedrandom();
+
+ crypto_init();
+
+ common_session_init(sock, remotehost);
+
+ chaninitialise(cli_chantypes);
+
+ /* Set up cli_ses vars */
+ cli_session_init();
+
+ /* Ready to go */
+ sessinitdone = 1;
+
+ /* Exchange identification */
+ session_identification();
+
+ send_msg_kexinit();
+
+ session_loop(cli_sessionloop);
+
+ /* Not reached */
+
+}
+
+static void cli_session_init() {
+
+ cli_ses.state = STATE_NOTHING;
+ cli_ses.kex_state = KEX_NOTHING;
+
+ cli_ses.tty_raw_mode = 0;
+ cli_ses.winchange = 0;
+
+ /* We store std{in,out,err}'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.stdoutcopy = dup(STDOUT_FILENO);
+ cli_ses.stdoutflags = fcntl(STDOUT_FILENO, F_GETFL, 0);
+ cli_ses.stderrcopy = dup(STDERR_FILENO);
+ cli_ses.stderrflags = fcntl(STDERR_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.lastprivkey = NULL;
+ cli_ses.lastauthtype = 0;
+
+ /* For printing "remote host closed" for the user */
+ ses.remoteclosed = cli_remoteclosed;
+ ses.buf_match_algo = cli_buf_match_algo;
+
+ /* packet handlers */
+ ses.packettypes = cli_packettypes;
+
+ ses.isserver = 0;
+}
+
+/* This function drives the progress of the session - it initiates KEX,
+ * service, userauth and channel requests */
+static void cli_sessionloop() {
+
+ TRACE(("enter cli_sessionloop"))
+
+ if (ses.lastpacket == SSH_MSG_KEXINIT && cli_ses.kex_state == KEX_NOTHING) {
+ cli_ses.kex_state = KEXINIT_RCVD;
+ }
+
+ if (cli_ses.kex_state == KEXINIT_RCVD) {
+
+ /* We initiate the KEXDH. If DH wasn't the correct type, the KEXINIT
+ * negotiation would have failed. */
+ send_msg_kexdh_init();
+ cli_ses.kex_state = KEXDH_INIT_SENT;
+ TRACE(("leave cli_sessionloop: done with KEXINIT_RCVD"))
+ return;
+ }
+
+ /* A KEX has finished, so we should go back to our KEX_NOTHING state */
+ if (cli_ses.kex_state != KEX_NOTHING && ses.kexstate.recvkexinit == 0
+ && ses.kexstate.sentkexinit == 0) {
+ cli_ses.kex_state = KEX_NOTHING;
+ }
+
+ /* 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"))
+ 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"))
+ return;
+ }
+
+ switch (cli_ses.state) {
+
+ case STATE_NOTHING:
+ /* We've got the transport layer sorted, we now need to request
+ * userauth */
+ send_msg_service_request(SSH_SERVICE_USERAUTH);
+ cli_ses.state = SERVICE_AUTH_REQ_SENT;
+ 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"))
+ return;
+
+ case USERAUTH_FAIL_RCVD:
+ cli_auth_try();
+ cli_ses.state = USERAUTH_REQ_SENT;
+ 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"))
+ return;
+
+ case SERVICE_CONN_ACCEPT_RCVD:
+ cli_send_chansess_request();
+ TRACE(("leave cli_sessionloop: cli_send_chansess_request"))
+ cli_ses.state = SESSION_RUNNING;
+ return;
+ */
+
+ case USERAUTH_SUCCESS_RCVD:
+#ifdef ENABLE_CLI_LOCALTCPFWD
+ setup_localtcp();
+#endif
+#ifdef ENABLE_CLI_REMOTETCPFWD
+ setup_remotetcp();
+#endif
+ cli_send_chansess_request();
+ TRACE(("leave cli_sessionloop: cli_send_chansess_request"))
+ cli_ses.state = SESSION_RUNNING;
+ return;
+
+ case SESSION_RUNNING:
+ if (ses.chancount < 1) {
+ cli_finished();
+ }
+
+ if (cli_ses.winchange) {
+ cli_chansess_winchange();
+ }
+ return;
+
+ /* XXX more here needed */
+
+
+ default:
+ break;
+ }
+
+ TRACE(("leave cli_sessionloop: fell out"))
+
+}
+
+void cli_session_cleanup() {
+
+ if (!sessinitdone) {
+ return;
+ }
+
+ /* Set std{in,out,err} back to non-blocking - busybox ash dies nastily if
+ * we don't revert the flags */
+ fcntl(cli_ses.stdincopy, F_SETFL, cli_ses.stdinflags);
+ fcntl(cli_ses.stdoutcopy, F_SETFL, cli_ses.stdoutflags);
+ fcntl(cli_ses.stderrcopy, F_SETFL, cli_ses.stderrflags);
+
+ cli_tty_cleanup();
+
+}
+
+static void cli_finished() {
+
+ cli_session_cleanup();
+ common_session_cleanup();
+ fprintf(stderr, "Connection to %s@%s:%s closed.\n", cli_opts.username,
+ cli_opts.remotehost, cli_opts.remoteport);
+ exit(cli_ses.retval);
+}
+
+
+/* called when the remote side closes the connection */
+static void cli_remoteclosed() {
+
+ /* XXX TODO perhaps print a friendlier message if we get this but have
+ * already sent/received disconnect message(s) ??? */
+ close(ses.sock);
+ ses.sock = -1;
+ dropbear_exit("remote closed the connection");
+}
+
+/* Operates in-place turning dirty (untrusted potentially containing control
+ * characters) text into clean text.
+ * Note: this is safe only with ascii - other charsets could have problems. */
+void cleantext(unsigned char* dirtytext) {
+
+ unsigned int i, j;
+ unsigned char c;
+
+ j = 0;
+ for (i = 0; dirtytext[i] != '\0'; i++) {
+
+ c = dirtytext[i];
+ /* We can ignore '\r's */
+ if ( (c >= ' ' && c <= '~') || c == '\n' || c == '\t') {
+ dirtytext[j] = c;
+ j++;
+ }
+ }
+ /* Null terminate */
+ dirtytext[j] = '\0';
+}
diff --git a/cli-tcpfwd.c b/cli-tcpfwd.c
new file mode 100644
index 0000000..c3bfd4d
--- /dev/null
+++ b/cli-tcpfwd.c
@@ -0,0 +1,216 @@
+/*
+ * 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. */
+
+#include "includes.h"
+#include "options.h"
+#include "dbutil.h"
+#include "tcpfwd.h"
+#include "channel.h"
+#include "runopts.h"
+#include "session.h"
+#include "ssh.h"
+
+#ifdef ENABLE_CLI_REMOTETCPFWD
+static int newtcpforwarded(struct Channel * channel);
+
+const struct ChanType cli_chan_tcpremote = {
+ 1, /* sepfds */
+ "forwarded-tcpip",
+ newtcpforwarded,
+ NULL,
+ 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",
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+#endif
+
+#ifdef ENABLE_CLI_LOCALTCPFWD
+void setup_localtcp() {
+
+ int ret;
+
+ TRACE(("enter setup_localtcp"))
+
+ if (cli_opts.localfwds == NULL) {
+ TRACE(("cli_opts.localfwds == NULL"))
+ }
+
+ while (cli_opts.localfwds != NULL) {
+ ret = cli_localtcp(cli_opts.localfwds->listenport,
+ cli_opts.localfwds->connectaddr,
+ cli_opts.localfwds->connectport);
+ if (ret == DROPBEAR_FAILURE) {
+ dropbear_log(LOG_WARNING, "Failed local port forward %d:%s:%d",
+ cli_opts.localfwds->listenport,
+ cli_opts.localfwds->connectaddr,
+ cli_opts.localfwds->connectport);
+ }
+
+ cli_opts.localfwds = cli_opts.localfwds->next;
+ }
+ TRACE(("leave setup_localtcp"))
+
+}
+
+static int cli_localtcp(unsigned int listenport, const char* remoteaddr,
+ unsigned int remoteport) {
+
+ struct TCPListener* tcpinfo = NULL;
+ int ret;
+
+ TRACE(("enter cli_localtcp: %d %s %d", listenport, remoteaddr,
+ remoteport));
+
+ tcpinfo = (struct TCPListener*)m_malloc(sizeof(struct TCPListener));
+
+ tcpinfo->sendaddr = m_strdup(remoteaddr);
+ tcpinfo->sendport = remoteport;
+
+ if (opts.listen_fwd_all) {
+ tcpinfo->listenaddr = m_strdup("");
+ } else {
+ tcpinfo->listenaddr = m_strdup("localhost");
+ }
+ tcpinfo->listenport = listenport;
+
+ tcpinfo->chantype = &cli_chan_tcplocal;
+ tcpinfo->tcp_type = direct;
+
+ ret = listen_tcpfwd(tcpinfo);
+
+ if (ret == DROPBEAR_FAILURE) {
+ m_free(tcpinfo);
+ }
+ 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) {
+
+ char* listenspec = NULL;
+ TRACE(("enter send_msg_global_request_remotetcp"))
+
+ CHECKCLEARTOWRITE();
+ buf_putbyte(ses.writepayload, SSH_MSG_GLOBAL_REQUEST);
+ buf_putstring(ses.writepayload, "tcpip-forward", 13);
+ buf_putbyte(ses.writepayload, 0);
+ if (opts.listen_fwd_all) {
+ listenspec = "";
+ } else {
+ listenspec = "localhost";
+ }
+ /* TODO: IPv6? */;
+ buf_putstring(ses.writepayload, listenspec, strlen(listenspec));
+ buf_putint(ses.writepayload, port);
+
+ encrypt_packet();
+
+ TRACE(("leave send_msg_global_request_remotetcp"))
+}
+
+void setup_remotetcp() {
+
+ struct TCPFwdList * iter = NULL;
+
+ TRACE(("enter setup_remotetcp"))
+
+ if (cli_opts.remotefwds == NULL) {
+ TRACE(("cli_opts.remotefwds == NULL"))
+ }
+
+ iter = cli_opts.remotefwds;
+
+ while (iter != NULL) {
+ send_msg_global_request_remotetcp(iter->listenport);
+ iter = iter->next;
+ }
+ TRACE(("leave setup_remotetcp"))
+}
+
+static int newtcpforwarded(struct Channel * channel) {
+
+ unsigned int origport;
+ struct TCPFwdList * iter = NULL;
+ char portstring[NI_MAXSERV];
+ int sock;
+ int err = SSH_OPEN_ADMINISTRATIVELY_PROHIBITED;
+
+ /* We don't care what address they connected to */
+ buf_eatstring(ses.payload);
+
+ origport = buf_getint(ses.payload);
+
+ /* Find which port corresponds */
+ iter = cli_opts.remotefwds;
+
+ while (iter != NULL) {
+ if (origport == iter->listenport) {
+ break;
+ }
+ iter = iter->next;
+ }
+
+ if (iter == NULL) {
+ /* We didn't request forwarding on that port */
+ dropbear_log(LOG_INFO, "Server send unrequested port, from port %d",
+ origport);
+ goto out;
+ }
+
+ snprintf(portstring, sizeof(portstring), "%d", iter->connectport);
+ sock = connect_remote(iter->connectaddr, portstring, 1, NULL);
+ if (sock < 0) {
+ TRACE(("leave newtcpdirect: sock failed"))
+ err = SSH_OPEN_CONNECT_FAILED;
+ goto out;
+ }
+
+ ses.maxfd = MAX(ses.maxfd, sock);
+
+ /* We don't set readfd, that will get set after the connection's
+ * progress succeeds */
+ channel->writefd = sock;
+ channel->initconn = 1;
+
+ err = SSH_OPEN_IN_PROGRESS;
+
+out:
+ TRACE(("leave newtcpdirect: err %d", err))
+ return err;
+}
+#endif /* ENABLE_CLI_REMOTETCPFWD */
diff --git a/common-algo.c b/common-algo.c
new file mode 100644
index 0000000..ae2102a
--- /dev/null
+++ b/common-algo.c
@@ -0,0 +1,228 @@
+/*
+ * Dropbear SSH
+ *
+ * Copyright (c) 2002,2003 Matt Johnston
+ * Copyright (c) 2004 by Mihnea Stoenescu
+ * 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 "algo.h"
+#include "dbutil.h"
+
+/* This file (algo.c) organises the ciphers which can be used, and is used to
+ * decide which ciphers/hashes/compression/signing to use during key exchange*/
+
+/* Mappings for ciphers, parameters are
+ {&cipher_desc, keysize, blocksize} */
+
+#ifdef DROPBEAR_AES256_CBC
+static const struct dropbear_cipher dropbear_aes256 =
+ {&aes_desc, 32, 16};
+#endif
+#ifdef DROPBEAR_AES128_CBC
+static const struct dropbear_cipher dropbear_aes128 =
+ {&aes_desc, 16, 16};
+#endif
+#ifdef DROPBEAR_BLOWFISH_CBC
+static const struct dropbear_cipher dropbear_blowfish =
+ {&blowfish_desc, 16, 8};
+#endif
+#ifdef DROPBEAR_TWOFISH256_CBC
+static const struct dropbear_cipher dropbear_twofish256 =
+ {&twofish_desc, 32, 16};
+#endif
+#ifdef DROPBEAR_TWOFISH128_CBC
+static const struct dropbear_cipher dropbear_twofish128 =
+ {&twofish_desc, 16, 16};
+#endif
+#ifdef DROPBEAR_3DES_CBC
+static const struct dropbear_cipher dropbear_3des =
+ {&des3_desc, 24, 8};
+#endif
+
+/* used to indicate no encryption, as defined in rfc2410 */
+const struct dropbear_cipher dropbear_nocipher =
+ {NULL, 16, 8};
+
+/* Mapping of ssh hashes to libtomcrypt hashes, including keysize etc.
+ {&hash_desc, keysize, hashsize} */
+
+#ifdef DROPBEAR_SHA1_HMAC
+static const struct dropbear_hash dropbear_sha1 =
+ {&sha1_desc, 20, 20};
+#endif
+#ifdef DROPBEAR_SHA1_96_HMAC
+static const struct dropbear_hash dropbear_sha1_96 =
+ {&sha1_desc, 20, 12};
+#endif
+#ifdef DROPBEAR_MD5_HMAC
+static const struct dropbear_hash dropbear_md5 =
+ {&md5_desc, 16, 16};
+#endif
+
+const struct dropbear_hash dropbear_nohash =
+ {NULL, 16, 0}; /* used initially */
+
+
+/* The following map ssh names to internal values */
+
+algo_type sshciphers[] = {
+#ifdef DROPBEAR_AES128_CBC
+ {"aes128-cbc", 0, (void*)&dropbear_aes128, 1},
+#endif
+#ifdef DROPBEAR_3DES_CBC
+ {"3des-cbc", 0, (void*)&dropbear_3des, 1},
+#endif
+#ifdef DROPBEAR_AES256_CBC
+ {"aes256-cbc", 0, (void*)&dropbear_aes256, 1},
+#endif
+#ifdef DROPBEAR_TWOFISH256_CBC
+ {"twofish256-cbc", 0, (void*)&dropbear_twofish256, 1},
+ {"twofish-cbc", 0, (void*)&dropbear_twofish256, 1},
+#endif
+#ifdef DROPBEAR_TWOFISH128_CBC
+ {"twofish128-cbc", 0, (void*)&dropbear_twofish128, 1},
+#endif
+#ifdef DROPBEAR_BLOWFISH_CBC
+ {"blowfish-cbc", 0, (void*)&dropbear_blowfish, 1},
+#endif
+ {NULL, 0, NULL, 0}
+};
+
+algo_type sshhashes[] = {
+#ifdef DROPBEAR_SHA1_96_HMAC
+ {"hmac-sha1-96", 0, (void*)&dropbear_sha1_96, 1},
+#endif
+#ifdef DROPBEAR_SHA1_HMAC
+ {"hmac-sha1", 0, (void*)&dropbear_sha1, 1},
+#endif
+#ifdef DROPBEAR_MD5_HMAC
+ {"hmac-md5", 0, (void*)&dropbear_md5, 1},
+#endif
+ {NULL, 0, NULL, 0}
+};
+
+algo_type sshcompress[] = {
+#ifndef DISABLE_ZLIB
+ {"zlib", DROPBEAR_COMP_ZLIB, NULL, 1},
+#endif
+ {"none", DROPBEAR_COMP_NONE, NULL, 1},
+ {NULL, 0, NULL, 0}
+};
+
+algo_type sshhostkey[] = {
+#ifdef DROPBEAR_RSA
+ {"ssh-rsa", DROPBEAR_SIGNKEY_RSA, NULL, 1},
+#endif
+#ifdef DROPBEAR_DSS
+ {"ssh-dss", DROPBEAR_SIGNKEY_DSS, NULL, 1},
+#endif
+ {NULL, 0, NULL, 0}
+};
+
+algo_type sshkex[] = {
+ {"diffie-hellman-group1-sha1", DROPBEAR_KEX_DH_GROUP1, NULL, 1},
+ {NULL, 0, NULL, 0}
+};
+
+
+/* Register the compiled in ciphers.
+ * This should be run before using any of the ciphers/hashes */
+void crypto_init() {
+
+ const struct ltc_cipher_descriptor *regciphers[] = {
+#ifdef DROPBEAR_AES_CBC
+ &aes_desc,
+#endif
+#ifdef DROPBEAR_BLOWFISH_CBC
+ &blowfish_desc,
+#endif
+#ifdef DROPBEAR_TWOFISH_CBC
+ &twofish_desc,
+#endif
+#ifdef DROPBEAR_3DES_CBC
+ &des3_desc,
+#endif
+ NULL
+ };
+
+ const struct ltc_hash_descriptor *reghashes[] = {
+ /* we need sha1 for hostkey stuff regardless */
+ &sha1_desc,
+#ifdef DROPBEAR_MD5_HMAC
+ &md5_desc,
+#endif
+ NULL
+ };
+ int i;
+
+ for (i = 0; regciphers[i] != NULL; i++) {
+ if (register_cipher(regciphers[i]) == -1) {
+ dropbear_exit("error registering crypto");
+ }
+ }
+
+ for (i = 0; reghashes[i] != NULL; i++) {
+ if (register_hash(reghashes[i]) == -1) {
+ dropbear_exit("error registering crypto");
+ }
+ }
+}
+
+/* algolen specifies the length of algo, algos is our local list to match
+ * against.
+ * Returns DROPBEAR_SUCCESS if we have a match for algo, DROPBEAR_FAILURE
+ * otherwise */
+int have_algo(char* algo, size_t algolen, algo_type algos[]) {
+
+ int i;
+
+ for (i = 0; algos[i].name != NULL; i++) {
+ if (strlen(algos[i].name) == algolen
+ && (strncmp(algos[i].name, algo, algolen) == 0)) {
+ return DROPBEAR_SUCCESS;
+ }
+ }
+
+ return DROPBEAR_FAILURE;
+}
+
+
+
+/* Output a comma separated list of algorithms to a buffer */
+void buf_put_algolist(buffer * buf, algo_type localalgos[]) {
+
+ unsigned int i, len;
+ unsigned int donefirst = 0;
+ buffer *algolist = NULL;
+
+ algolist = buf_new(100);
+ for (i = 0; localalgos[i].name != NULL; i++) {
+ if (localalgos[i].usable) {
+ if (donefirst)
+ buf_putbyte(algolist, ',');
+ donefirst = 1;
+ len = strlen(localalgos[i].name);
+ buf_putbytes(algolist, localalgos[i].name, len);
+ }
+ }
+ buf_putstring(buf, algolist->data, algolist->len);
+ buf_free(algolist);
+}
diff --git a/common-channel.c b/common-channel.c
new file mode 100644
index 0000000..68d2b48
--- /dev/null
+++ b/common-channel.c
@@ -0,0 +1,1038 @@
+/*
+ * 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. */
+
+/* Handle the multiplexed channels, such as sessions, x11, agent connections */
+
+#include "includes.h"
+#include "session.h"
+#include "packet.h"
+#include "ssh.h"
+#include "buffer.h"
+#include "circbuffer.h"
+#include "dbutil.h"
+#include "channel.h"
+#include "ssh.h"
+#include "listener.h"
+
+static void send_msg_channel_open_failure(unsigned int remotechan, int reason,
+ const unsigned char *text, const unsigned char *lang);
+static void send_msg_channel_open_confirmation(struct Channel* channel,
+ unsigned int recvwindow,
+ unsigned int recvmaxpacket);
+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,
+ unsigned int exttype);
+static void send_msg_channel_eof(struct Channel *channel);
+static void send_msg_channel_close(struct Channel *channel);
+static void removechannel(struct Channel *channel);
+static void deletechannel(struct Channel *channel);
+static void checkinitdone(struct Channel *channel);
+static void checkclose(struct Channel *channel);
+
+static void closewritefd(struct Channel * channel);
+static void closereadfd(struct Channel * channel, int fd);
+static void closechanfd(struct Channel *channel, int fd, int how);
+
+#define FD_UNINIT (-2)
+#define FD_CLOSED (-1)
+
+/* Initialise all the channels */
+void chaninitialise(const struct ChanType *chantypes[]) {
+
+ /* may as well create space for a single channel */
+ ses.channels = (struct Channel**)m_malloc(sizeof(struct Channel*));
+ ses.chansize = 1;
+ ses.channels[0] = NULL;
+ ses.chancount = 0;
+
+ ses.chantypes = chantypes;
+
+#ifdef USING_LISTENERS
+ listeners_initialise();
+#endif
+
+}
+
+/* Clean up channels, freeing allocated memory */
+void chancleanup() {
+
+ unsigned int i;
+
+ TRACE(("enter chancleanup"))
+ for (i = 0; i < ses.chansize; i++) {
+ if (ses.channels[i] != NULL) {
+ TRACE(("channel %d closing", i))
+ removechannel(ses.channels[i]);
+ }
+ }
+ m_free(ses.channels);
+ TRACE(("leave chancleanup"))
+}
+
+/* Create a new channel entry, send a reply confirm or failure */
+/* If remotechan, transwindow and transmaxpacket are not know (for a new
+ * outgoing connection, with them to be filled on confirmation), they should
+ * all be set to 0 */
+struct Channel* newchannel(unsigned int remotechan,
+ const struct ChanType *type,
+ unsigned int transwindow, unsigned int transmaxpacket) {
+
+ struct Channel * newchan;
+ unsigned int i, j;
+
+ TRACE(("enter newchannel"))
+
+ /* first see if we can use existing channels */
+ for (i = 0; i < ses.chansize; i++) {
+ if (ses.channels[i] == NULL) {
+ break;
+ }
+ }
+
+ /* otherwise extend the list */
+ if (i == ses.chansize) {
+ if (ses.chansize >= MAX_CHANNELS) {
+ TRACE(("leave newchannel: max chans reached"))
+ return NULL;
+ }
+
+ /* extend the channels */
+ ses.channels = (struct Channel**)m_realloc(ses.channels,
+ (ses.chansize+CHAN_EXTEND_SIZE)*sizeof(struct Channel*));
+
+ ses.chansize += CHAN_EXTEND_SIZE;
+
+ /* set the new channels to null */
+ for (j = i; j < ses.chansize; j++) {
+ ses.channels[j] = NULL;
+ }
+
+ }
+
+ newchan = (struct Channel*)m_malloc(sizeof(struct Channel));
+ newchan->type = type;
+ newchan->index = i;
+ newchan->sentclosed = newchan->recvclosed = 0;
+ newchan->senteof = newchan->recveof = 0;
+
+ newchan->remotechan = remotechan;
+ newchan->transwindow = transwindow;
+ newchan->transmaxpacket = transmaxpacket;
+
+ newchan->typedata = NULL;
+ newchan->writefd = FD_UNINIT;
+ newchan->readfd = FD_UNINIT;
+ newchan->errfd = FD_CLOSED; /* this isn't always set to start with */
+ newchan->initconn = 0;
+ newchan->await_open = 0;
+
+ 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"))
+
+ return newchan;
+}
+
+/* Returns the channel structure corresponding to the channel in the current
+ * data packet (ses.payload must be positioned appropriately) */
+struct Channel* getchannel() {
+
+ unsigned int chan;
+
+ chan = buf_getint(ses.payload);
+ if (chan >= ses.chansize || ses.channels[chan] == NULL) {
+ return NULL;
+ }
+ return ses.channels[chan];
+}
+
+/* Iterate through the channels, performing IO if available */
+void channelio(fd_set *readfds, fd_set *writefds) {
+
+ struct Channel *channel;
+ unsigned int i;
+
+ /* iterate through all the possible channels */
+ for (i = 0; i < ses.chansize; i++) {
+
+ channel = ses.channels[i];
+ if (channel == NULL) {
+ /* only process in-use channels */
+ continue;
+ }
+
+ /* read data and send it over the wire */
+ if (channel->readfd >= 0 && FD_ISSET(channel->readfd, readfds)) {
+ send_msg_channel_data(channel, 0, 0);
+ }
+
+ /* read stderr data and send it over the wire */
+ if (channel->extrabuf == NULL &&
+ channel->errfd >= 0 && FD_ISSET(channel->errfd, readfds)) {
+ send_msg_channel_data(channel, 1, SSH_EXTENDED_DATA_STDERR);
+ }
+
+ /* write to program/pipe stdin */
+ if (channel->writefd >= 0 && FD_ISSET(channel->writefd, writefds)) {
+ if (channel->initconn) {
+ checkinitdone(channel);
+ continue; /* Important not to use the channel after
+ checkinitdone(), as it may be NULL */
+ }
+ writechannel(channel, channel->writefd, channel->writebuf);
+ }
+
+ /* stderr for client mode */
+ if (channel->extrabuf != NULL
+ && channel->errfd >= 0 && FD_ISSET(channel->errfd, writefds)) {
+ writechannel(channel, channel->errfd, channel->extrabuf);
+ }
+
+ /* now handle any of the channel-closing type stuff */
+ checkclose(channel);
+
+ } /* foreach channel */
+
+ /* Listeners such as TCP, X11, agent-auth */
+#ifdef USING_LISTENERS
+ handle_listeners(readfds);
+#endif
+}
+
+
+/* do all the EOF/close type stuff checking for a channel */
+static void checkclose(struct Channel *channel) {
+
+ TRACE(("checkclose: writefd %d, readfd %d, errfd %d, sentclosed %d, recvclosed %d",
+ channel->writefd, channel->readfd,
+ 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,
+ * if the shell has exited etc */
+ if (channel->type->checkclose) {
+ if (channel->type->checkclose(channel)) {
+ closewritefd(channel);
+ }
+ }
+
+ if (!channel->senteof
+ && channel->readfd == FD_CLOSED
+ && (channel->extrabuf != NULL || channel->errfd == FD_CLOSED)) {
+ send_msg_channel_eof(channel);
+ }
+
+ if (channel->writefd == FD_CLOSED
+ && channel->readfd == FD_CLOSED
+ && (channel->extrabuf != NULL || channel->errfd == FD_CLOSED)) {
+ send_msg_channel_close(channel);
+ }
+ }
+
+ /* When either party wishes to terminate the channel, it sends
+ * SSH_MSG_CHANNEL_CLOSE. Upon receiving this message, a party MUST
+ * send back a SSH_MSG_CHANNEL_CLOSE unless it has already sent this
+ * message for the channel. The channel is considered closed for a
+ * party when it has both sent and received SSH_MSG_CHANNEL_CLOSE, and
+ * the party may then reuse the channel number. A party MAY send
+ * SSH_MSG_CHANNEL_CLOSE without having sent or received
+ * SSH_MSG_CHANNEL_EOF.
+ * (from draft-ietf-secsh-connect)
+ */
+ if (channel->recvclosed) {
+ if (! channel->sentclosed) {
+ TRACE(("Sending MSG_CHANNEL_CLOSE in response to same."))
+ send_msg_channel_close(channel);
+ }
+ removechannel(channel);
+ }
+}
+
+
+/* Check whether a deferred (EINPROGRESS) connect() was successful, and
+ * if so, set up the channel properly. Otherwise, the channel is cleaned up, so
+ * it is important that the channel reference isn't used after a call to this
+ * function */
+static void checkinitdone(struct Channel *channel) {
+
+ int val;
+ socklen_t vallen = sizeof(val);
+
+ TRACE(("enter checkinitdone"))
+
+ if (getsockopt(channel->writefd, SOL_SOCKET, SO_ERROR, &val, &vallen)
+ || val != 0) {
+ send_msg_channel_open_failure(channel->remotechan,
+ SSH_OPEN_CONNECT_FAILED, "", "");
+ close(channel->writefd);
+ deletechannel(channel);
+ TRACE(("leave checkinitdone: fail"))
+ } else {
+ send_msg_channel_open_confirmation(channel, channel->recvwindow,
+ channel->recvmaxpacket);
+ channel->readfd = channel->writefd;
+ channel->initconn = 0;
+ TRACE(("leave checkinitdone: success"))
+ }
+}
+
+
+
+/* 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"))
+ /* XXX server */
+ if (channel->type->closehandler) {
+ channel->type->closehandler(channel);
+ }
+
+ CHECKCLEARTOWRITE();
+
+ buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_CLOSE);
+ buf_putint(ses.writepayload, channel->remotechan);
+
+ encrypt_packet();
+
+ channel->senteof = 1;
+ channel->sentclosed = 1;
+ 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"))
+ CHECKCLEARTOWRITE();
+
+ buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_EOF);
+ buf_putint(ses.writepayload, channel->remotechan);
+
+ encrypt_packet();
+
+ channel->senteof = 1;
+
+ TRACE(("leave send_msg_channel_eof"))
+}
+
+/* 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, int fd, circbuffer *cbuf) {
+
+ int len, maxlen;
+
+ TRACE(("enter writechannel"))
+
+ maxlen = cbuf_readlen(cbuf);
+
+ /* Write the data out */
+ len = write(fd, cbuf_readptr(cbuf, maxlen), maxlen);
+ if (len <= 0) {
+ if (len < 0 && errno != EINTR) {
+ /* no more to write - we close it even if the fd was stderr, since
+ * that's a nasty failure too */
+ closewritefd(channel);
+ }
+ TRACE(("leave writechannel: len <= 0"))
+ return;
+ }
+
+ cbuf_incrread(cbuf, len);
+ channel->recvdonelen += len;
+
+ if (fd == channel->writefd && cbuf_getused(cbuf) == 0 && channel->recveof) {
+ /* Check if we're closing up */
+ closewritefd(channel);
+ TRACE(("leave writechannel: recveof set"))
+ return;
+ }
+
+ /* 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;
+ }
+
+ dropbear_assert(channel->recvwindow <= RECV_MAXWINDOW);
+ dropbear_assert(channel->recvwindow <= cbuf_getavail(channel->writebuf));
+ dropbear_assert(channel->extrabuf == NULL ||
+ channel->recvwindow <= cbuf_getavail(channel->extrabuf));
+
+
+ TRACE(("leave writechannel"))
+}
+
+/* Set the file descriptors for the main select in session.c
+ * This avoid channels which don't have any window available, are closed, etc*/
+void setchannelfds(fd_set *readfds, fd_set *writefds) {
+
+ unsigned int i;
+ struct Channel * channel;
+
+ for (i = 0; i < ses.chansize; i++) {
+
+ channel = ses.channels[i];
+ if (channel == NULL) {
+ continue;
+ }
+
+ /* Stuff to put over the wire */
+ if (channel->transwindow > 0) {
+
+ if (channel->readfd >= 0) {
+ FD_SET(channel->readfd, readfds);
+ }
+
+ if (channel->extrabuf == NULL && channel->errfd >= 0) {
+ FD_SET(channel->errfd, readfds);
+ }
+ }
+
+ /* Stuff from the wire */
+ if ((channel->writefd >= 0 && cbuf_getused(channel->writebuf) > 0 )
+ || channel->initconn) {
+
+ FD_SET(channel->writefd, writefds);
+ }
+
+ if (channel->extrabuf != NULL && channel->errfd >= 0
+ && cbuf_getused(channel->extrabuf) > 0 ) {
+ FD_SET(channel->errfd, writefds);
+ }
+
+ } /* foreach channel */
+
+#ifdef USING_LISTENERS
+ set_listener_fds(readfds);
+#endif
+
+}
+
+/* handle the channel EOF event, by closing the channel filedescriptor. The
+ * channel isn't closed yet, it is left until the incoming (from the program
+ * etc) FD is also EOF */
+void recv_msg_channel_eof() {
+
+ struct Channel * channel;
+
+ TRACE(("enter recv_msg_channel_eof"))
+
+ channel = getchannel();
+ if (channel == NULL) {
+ dropbear_exit("EOF for unknown channel");
+ }
+
+ channel->recveof = 1;
+ if (cbuf_getused(channel->writebuf) == 0
+ && (channel->extrabuf == NULL
+ || cbuf_getused(channel->extrabuf) == 0)) {
+ closewritefd(channel);
+ }
+
+ TRACE(("leave recv_msg_channel_eof"))
+}
+
+
+/* Handle channel closure(), respond in kind and close the channels */
+void recv_msg_channel_close() {
+
+ struct Channel * channel;
+
+ TRACE(("enter recv_msg_channel_close"))
+
+ channel = getchannel();
+ if (channel == NULL) {
+ /* disconnect ? */
+ dropbear_exit("Close for unknown channel");
+ }
+
+ channel->recveof = 1;
+ channel->recvclosed = 1;
+
+ if (channel->sentclosed) {
+ removechannel(channel);
+ }
+
+ 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))
+
+ 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->writefd);
+ close(channel->readfd);
+ close(channel->errfd);
+
+ channel->typedata = NULL;
+
+ deletechannel(channel);
+
+ TRACE(("leave removechannel"))
+}
+
+/* Remove a channel entry */
+static void deletechannel(struct Channel *channel) {
+
+ ses.channels[channel->index] = NULL;
+ m_free(channel);
+ ses.chancount--;
+
+}
+
+
+/* Handle channel specific requests, passing off to corresponding handlers
+ * such as chansession or x11fwd */
+void recv_msg_channel_request() {
+
+ struct Channel *channel;
+
+ TRACE(("enter recv_msg_channel_request"))
+
+ channel = getchannel();
+ if (channel == NULL) {
+ /* disconnect ? */
+ dropbear_exit("Unknown channel");
+ }
+
+ if (channel->type->reqhandler) {
+ channel->type->reqhandler(channel);
+ } else {
+ send_msg_channel_failure(channel);
+ }
+
+ TRACE(("leave recv_msg_channel_request"))
+
+}
+
+/* Reads data from the server's program/shell/etc, and puts it in a
+ * channel_data packet to send.
+ * chan is the remote channel, isextended is 0 if it is normal data, 1
+ * if it is extended data. if it is extended, then the type is in
+ * exttype */
+static void send_msg_channel_data(struct Channel *channel, int isextended,
+ unsigned int exttype) {
+
+ buffer *buf;
+ int len;
+ unsigned int maxlen;
+ int fd;
+
+/* TRACE(("enter send_msg_channel_data"))
+ TRACE(("extended = %d type = %d", isextended, exttype))*/
+
+ CHECKCLEARTOWRITE();
+
+ dropbear_assert(!channel->sentclosed);
+
+ if (isextended) {
+ fd = channel->errfd;
+ } else {
+ fd = channel->readfd;
+ }
+ dropbear_assert(fd >= 0);
+
+ maxlen = MIN(channel->transwindow, channel->transmaxpacket);
+ /* -(1+4+4) is SSH_MSG_CHANNEL_DATA, channel number, string length, and
+ * exttype if is extended */
+ maxlen = MIN(maxlen,
+ ses.writepayload->size - 1 - 4 - 4 - (isextended ? 4 : 0));
+ if (maxlen == 0) {
+ 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) {
+ closereadfd(channel, fd);
+ }
+ 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);
+
+ buf_putbyte(ses.writepayload,
+ isextended ? SSH_MSG_CHANNEL_EXTENDED_DATA : SSH_MSG_CHANNEL_DATA);
+ buf_putint(ses.writepayload, channel->remotechan);
+
+ if (isextended) {
+ buf_putint(ses.writepayload, exttype);
+ }
+
+ buf_putstring(ses.writepayload, buf_getptr(buf, len), len);
+ buf_free(buf);
+ buf = NULL;
+
+ channel->transwindow -= len;
+
+ encrypt_packet();
+ TRACE(("leave send_msg_channel_data"))
+}
+
+/* We receive channel data */
+void recv_msg_channel_data() {
+
+ struct Channel *channel;
+
+ channel = getchannel();
+ if (channel == NULL) {
+ dropbear_exit("Unknown channel");
+ }
+
+ common_recv_msg_channel_data(channel, channel->writefd, 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 (fd < 0) {
+ dropbear_exit("received data with bad writefd");
+ }
+
+ datalen = buf_getint(ses.payload);
+
+
+ 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) {
+ dropbear_exit("Oversized packet");
+ }
+
+ /* 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;
+ }
+
+ dropbear_assert(channel->recvwindow >= datalen);
+ channel->recvwindow -= datalen;
+ dropbear_assert(channel->recvwindow <= RECV_MAXWINDOW);
+
+ TRACE(("leave recv_msg_channel_data"))
+}
+
+/* Increment the outgoing data window for a channel - the remote end limits
+ * the amount of data which may be transmitted, this window is decremented
+ * as data is sent, and incremented upon receiving window-adjust messages */
+void recv_msg_channel_window_adjust() {
+
+ struct Channel * channel;
+ unsigned int incr;
+
+ channel = getchannel();
+ if (channel == NULL) {
+ dropbear_exit("Unknown channel");
+ }
+
+ incr = buf_getint(ses.payload);
+ TRACE(("received window increment %d", incr))
+ incr = MIN(incr, MAX_TRANS_WIN_INCR);
+
+ channel->transwindow += incr;
+ channel->transwindow = MIN(channel->transwindow, MAX_TRANS_WINDOW);
+
+}
+
+/* Increment the incoming data window for a channel, and let the remote
+ * end know */
+static void send_msg_channel_window_adjust(struct Channel* channel,
+ unsigned int incr) {
+
+ TRACE(("sending window adjust %d", incr))
+ CHECKCLEARTOWRITE();
+
+ buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_WINDOW_ADJUST);
+ buf_putint(ses.writepayload, channel->remotechan);
+ buf_putint(ses.writepayload, incr);
+
+ encrypt_packet();
+}
+
+/* Handle a new channel request, performing any channel-type-specific setup */
+/* XXX server */
+void recv_msg_channel_open() {
+
+ unsigned char *type;
+ unsigned int typelen;
+ unsigned int remotechan, transwindow, transmaxpacket;
+ struct Channel *channel;
+ const struct ChanType **cp;
+ const struct ChanType *chantype;
+ unsigned int errtype = SSH_OPEN_UNKNOWN_CHANNEL_TYPE;
+ int ret;
+
+
+ TRACE(("enter recv_msg_channel_open"))
+
+ /* get the packet contents */
+ type = buf_getstring(ses.payload, &typelen);
+
+ remotechan = buf_getint(ses.payload);
+ transwindow = buf_getint(ses.payload);
+ transwindow = MIN(transwindow, MAX_TRANS_WINDOW);
+ transmaxpacket = buf_getint(ses.payload);
+ transmaxpacket = MIN(transmaxpacket, MAX_TRANS_PAYLOAD_LEN);
+
+ /* figure what type of packet it is */
+ if (typelen > MAX_NAME_LEN) {
+ goto failure;
+ }
+
+ /* Get the channel type. Client and server style invokation will set up a
+ * different list for ses.chantypes at startup. We just iterate through
+ * this list and find the matching name */
+ for (cp = &ses.chantypes[0], chantype = (*cp);
+ chantype != NULL;
+ cp++, chantype = (*cp)) {
+ if (strcmp(type, chantype->name) == 0) {
+ break;
+ }
+ }
+
+ if (chantype == NULL) {
+ TRACE(("No matching type for '%s'", type))
+ goto failure;
+ }
+
+ TRACE(("matched type '%s'", type))
+
+ /* create the channel */
+ channel = newchannel(remotechan, chantype, transwindow, transmaxpacket);
+
+ if (channel == NULL) {
+ TRACE(("newchannel returned NULL"))
+ goto failure;
+ }
+
+ if (channel->type->inithandler) {
+ ret = channel->type->inithandler(channel);
+ if (ret > 0) {
+ if (ret == SSH_OPEN_IN_PROGRESS) {
+ /* We'll send the confirmation later */
+ goto cleanup;
+ }
+ errtype = ret;
+ deletechannel(channel);
+ TRACE(("inithandler returned failure %d", ret))
+ goto failure;
+ }
+ }
+
+ /* success */
+ send_msg_channel_open_confirmation(channel, channel->recvwindow,
+ channel->recvmaxpacket);
+ goto cleanup;
+
+failure:
+ TRACE(("recv_msg_channel_open failure"))
+ send_msg_channel_open_failure(remotechan, errtype, "", "");
+
+cleanup:
+ m_free(type);
+
+ TRACE(("leave recv_msg_channel_open"))
+}
+
+/* Send a failure message */
+void send_msg_channel_failure(struct Channel *channel) {
+
+ 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"))
+}
+
+/* Send a success message */
+void send_msg_channel_success(struct Channel *channel) {
+
+ 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"))
+}
+
+/* Send a channel open failure message, with a corresponding reason
+ * code (usually resource shortage or unknown chan type) */
+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"))
+ CHECKCLEARTOWRITE();
+
+ buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_OPEN_FAILURE);
+ buf_putint(ses.writepayload, remotechan);
+ buf_putint(ses.writepayload, reason);
+ buf_putstring(ses.writepayload, text, strlen((char*)text));
+ buf_putstring(ses.writepayload, lang, strlen((char*)lang));
+
+ encrypt_packet();
+ TRACE(("leave send_msg_channel_open_failure"))
+}
+
+/* Confirm a channel open, and let the remote end know what number we've
+ * allocated and the receive parameters */
+static void send_msg_channel_open_confirmation(struct Channel* channel,
+ unsigned int recvwindow,
+ unsigned int recvmaxpacket) {
+
+ TRACE(("enter send_msg_channel_open_confirmation"))
+ CHECKCLEARTOWRITE();
+
+ buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
+ buf_putint(ses.writepayload, channel->remotechan);
+ buf_putint(ses.writepayload, channel->index);
+ buf_putint(ses.writepayload, recvwindow);
+ buf_putint(ses.writepayload, recvmaxpacket);
+
+ encrypt_packet();
+ TRACE(("leave send_msg_channel_open_confirmation"))
+}
+
+#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
+ * completion. It is mandatory for the caller to encrypt_packet() if
+ * DROPBEAR_SUCCESS is returned */
+int send_msg_channel_open_init(int fd, const struct ChanType *type) {
+
+ struct Channel* chan;
+
+ 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()"))
+ return DROPBEAR_FAILURE;
+ }
+
+ /* set fd non-blocking */
+ setnonblocking(fd);
+
+ chan->writefd = chan->readfd = fd;
+ ses.maxfd = MAX(ses.maxfd, fd);
+
+ chan->await_open = 1;
+
+ /* now open the channel connection */
+ CHECKCLEARTOWRITE();
+
+ buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_OPEN);
+ buf_putstring(ses.writepayload, type->name, strlen(type->name));
+ buf_putint(ses.writepayload, chan->index);
+ buf_putint(ses.writepayload, RECV_MAXWINDOW);
+ buf_putint(ses.writepayload, RECV_MAXPACKET);
+
+ TRACE(("leave send_msg_channel_open_init()"))
+ return DROPBEAR_SUCCESS;
+}
+
+/* Confirmation that our channel open request (for forwardings) was
+ * successful*/
+void recv_msg_channel_open_confirmation() {
+
+ struct Channel * channel;
+ int ret;
+
+ TRACE(("enter recv_msg_channel_open_confirmation"))
+
+ channel = getchannel();
+ if (channel == NULL) {
+ dropbear_exit("Unknown channel");
+ }
+
+ if (!channel->await_open) {
+ dropbear_exit("unexpected channel reply");
+ }
+ channel->await_open = 0;
+
+ channel->remotechan = buf_getint(ses.payload);
+ channel->transwindow = buf_getint(ses.payload);
+ channel->transmaxpacket = buf_getint(ses.payload);
+
+ TRACE(("new chan remote %d local %d",
+ channel->remotechan, channel->index))
+
+ /* 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(("leave recv_msg_channel_open_confirmation"))
+}
+
+/* Notification that our channel open request failed */
+void recv_msg_channel_open_failure() {
+
+ struct Channel * channel;
+
+ channel = getchannel();
+ if (channel == NULL) {
+ dropbear_exit("Unknown channel");
+ }
+
+ if (!channel->await_open) {
+ dropbear_exit("unexpected channel reply");
+ }
+ channel->await_open = 0;
+
+ removechannel(channel);
+}
+#endif /* USING_LISTENERS */
+
+/* close a stdout/stderr fd */
+static void closereadfd(struct Channel * channel, int fd) {
+
+ /* don't close it if it is the same as writefd,
+ * unless writefd is already set -1 */
+ TRACE(("enter closereadfd"))
+ closechanfd(channel, fd, 0);
+ TRACE(("leave closereadfd"))
+}
+
+/* close a stdin fd */
+static void closewritefd(struct Channel * channel) {
+
+ TRACE(("enter closewritefd"))
+ closechanfd(channel, channel->writefd, 1);
+ TRACE(("leave closewritefd"))
+}
+
+/* close a fd, how is 0 for stdout/stderr, 1 for stdin */
+static void closechanfd(struct Channel *channel, int fd, int how) {
+
+ int closein = 0, closeout = 0;
+
+ /* XXX server */
+ if (channel->type->sepfds) {
+ TRACE(("shutdown((%d), %d)", fd, how))
+ shutdown(fd, how);
+ if (how == 0) {
+ closeout = 1;
+ } else {
+ closein = 1;
+ }
+ } else {
+ close(fd);
+ closein = closeout = 1;
+ }
+
+ if (closeout && fd == channel->readfd) {
+ channel->readfd = FD_CLOSED;
+ }
+ if (closeout && (channel->extrabuf == NULL) && (fd == channel->errfd)) {
+ channel->errfd = FD_CLOSED;
+ }
+
+ if (closein && fd == channel->writefd) {
+ channel->writefd = FD_CLOSED;
+ }
+ if (closein && (channel->extrabuf != NULL) && (fd == channel->errfd)) {
+ channel->errfd = FD_CLOSED;
+ }
+
+ /* if we called shutdown on it and all references are gone, then we
+ * need to close() it to stop it lingering */
+ if (channel->type->sepfds && channel->readfd == FD_CLOSED
+ && channel->writefd == FD_CLOSED && channel->errfd == FD_CLOSED) {
+ close(fd);
+ }
+}
diff --git a/common-chansession.c b/common-chansession.c
new file mode 100644
index 0000000..b350c6c
--- /dev/null
+++ b/common-chansession.c
@@ -0,0 +1,43 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+#include "chansession.h"
+
+/* Mapping of signal values to ssh signal strings */
+const struct SigMap signames[] = {
+ {SIGABRT, "ABRT"},
+ {SIGALRM, "ALRM"},
+ {SIGFPE, "FPE"},
+ {SIGHUP, "HUP"},
+ {SIGILL, "ILL"},
+ {SIGINT, "INT"},
+ {SIGKILL, "KILL"},
+ {SIGPIPE, "PIPE"},
+ {SIGQUIT, "QUIT"},
+ {SIGSEGV, "SEGV"},
+ {SIGTERM, "TERM"},
+ {SIGUSR1, "USR1"},
+ {SIGUSR2, "USR2"},
+ {0, NULL}
+};
diff --git a/common-kex.c b/common-kex.c
new file mode 100644
index 0000000..5db8e52
--- /dev/null
+++ b/common-kex.c
@@ -0,0 +1,718 @@
+/*
+ * Dropbear SSH
+ *
+ * Copyright (c) 2002-2004 Matt Johnston
+ * Portions Copyright (c) 2004 by Mihnea Stoenescu
+ * 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 "algo.h"
+#include "buffer.h"
+#include "session.h"
+#include "kex.h"
+#include "ssh.h"
+#include "packet.h"
+#include "bignum.h"
+#include "random.h"
+
+/* diffie-hellman-group1-sha1 value for p */
+static const unsigned char dh_p_val[] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2,
+ 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+ 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6,
+ 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+ 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D,
+ 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+ 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9,
+ 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+ 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11,
+ 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+#define DH_P_LEN sizeof(dh_p_val)
+
+static const int DH_G_VAL = 2;
+
+static void kexinitialise();
+void gen_new_keys();
+#ifndef DISABLE_ZLIB
+static void gen_new_zstreams();
+#endif
+static void read_kex_algos();
+/* helper function for gen_new_keys */
+static void hashkeys(unsigned char *out, int outlen,
+ const hash_state * hs, unsigned const char X);
+
+
+/* Send our list of algorithms we can use */
+void send_msg_kexinit() {
+
+ CHECKCLEARTOWRITE();
+ buf_putbyte(ses.writepayload, SSH_MSG_KEXINIT);
+
+ /* cookie */
+ genrandom(buf_getwriteptr(ses.writepayload, 16), 16);
+ buf_incrwritepos(ses.writepayload, 16);
+
+ /* kex algos */
+ buf_put_algolist(ses.writepayload, sshkex);
+
+ /* server_host_key_algorithms */
+ buf_put_algolist(ses.writepayload, sshhostkey);
+
+ /* encryption_algorithms_client_to_server */
+ buf_put_algolist(ses.writepayload, sshciphers);
+
+ /* encryption_algorithms_server_to_client */
+ buf_put_algolist(ses.writepayload, sshciphers);
+
+ /* mac_algorithms_client_to_server */
+ buf_put_algolist(ses.writepayload, sshhashes);
+
+ /* mac_algorithms_server_to_client */
+ buf_put_algolist(ses.writepayload, sshhashes);
+
+ /* compression_algorithms_client_to_server */
+ buf_put_algolist(ses.writepayload, sshcompress);
+
+ /* compression_algorithms_server_to_client */
+ buf_put_algolist(ses.writepayload, sshcompress);
+
+ /* languages_client_to_server */
+ buf_putstring(ses.writepayload, "", 0);
+
+ /* languages_server_to_client */
+ buf_putstring(ses.writepayload, "", 0);
+
+ /* first_kex_packet_follows - unimplemented for now */
+ buf_putbyte(ses.writepayload, 0x00);
+
+ /* reserved unit32 */
+ buf_putint(ses.writepayload, 0);
+
+ /* set up transmitted kex packet buffer for hashing.
+ * This is freed after the end of the kex */
+ ses.transkexinit = buf_newcopy(ses.writepayload);
+
+ encrypt_packet();
+ ses.dataallowed = 0; /* don't send other packets during kex */
+
+ TRACE(("DATAALLOWED=0"))
+ TRACE(("-> KEXINIT"))
+ ses.kexstate.sentkexinit = 1;
+}
+
+/* *** NOTE regarding (send|recv)_msg_newkeys ***
+ * Changed by mihnea from the original kex.c to set dataallowed after a
+ * completed key exchange, no matter the order in which it was performed.
+ * This enables client mode without affecting server functionality.
+ */
+
+/* Bring new keys into use after a key exchange, and let the client know*/
+void send_msg_newkeys() {
+
+ TRACE(("enter send_msg_newkeys"))
+
+ /* generate the kexinit request */
+ CHECKCLEARTOWRITE();
+ buf_putbyte(ses.writepayload, SSH_MSG_NEWKEYS);
+ encrypt_packet();
+
+
+ /* set up our state */
+ if (ses.kexstate.recvnewkeys) {
+ TRACE(("while RECVNEWKEYS=1"))
+ gen_new_keys();
+ kexinitialise(); /* we've finished with this kex */
+ 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(("-> 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"))
+
+ /* 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"))
+ gen_new_keys();
+ kexinitialise(); /* we've finished with this kex */
+ TRACE((" -> DATAALLOWED=1"))
+ ses.dataallowed = 1; /* we can send other packets again now */
+ ses.kexstate.donefirstkex = 1;
+ } else {
+ TRACE(("RECVNEWKEYS=1"))
+ ses.kexstate.recvnewkeys = 1;
+ }
+
+ TRACE(("leave recv_msg_newkeys"))
+}
+
+
+/* Set up the kex for the first time */
+void kexfirstinitialise() {
+
+ ses.kexstate.donefirstkex = 0;
+ kexinitialise();
+}
+
+/* Reset the kex state, ready for a new negotiation */
+static void kexinitialise() {
+
+ struct timeval tv;
+
+ TRACE(("kexinitialise()"))
+
+ /* sent/recv'd MSG_KEXINIT */
+ ses.kexstate.sentkexinit = 0;
+ ses.kexstate.recvkexinit = 0;
+
+ /* sent/recv'd MSG_NEWKEYS */
+ ses.kexstate.recvnewkeys = 0;
+ ses.kexstate.sentnewkeys = 0;
+
+ /* first_packet_follows */
+ ses.kexstate.firstfollows = 0;
+
+ ses.kexstate.datatrans = 0;
+ ses.kexstate.datarecv = 0;
+
+ if (gettimeofday(&tv, 0) < 0) {
+ dropbear_exit("Error getting time");
+ }
+ ses.kexstate.lastkextime = tv.tv_sec;
+
+}
+
+/* Helper function for gen_new_keys, creates a hash. It makes a copy of the
+ * already initialised hash_state hs, which should already have processed
+ * the dh_K and hash, since these are common. X is the letter 'A', 'B' etc.
+ * out must have at least min(SHA1_HASH_SIZE, outlen) bytes allocated.
+ * The output will only be expanded once, since that is all that is required
+ * (for 3DES and SHA, with 24 and 20 bytes respectively).
+ *
+ * See Section 5.2 of the IETF secsh Transport Draft for details */
+
+/* Duplicated verbatim from kex.c --mihnea */
+static void hashkeys(unsigned char *out, int outlen,
+ const hash_state * hs, const unsigned char X) {
+
+ hash_state hs2;
+ unsigned char k2[SHA1_HASH_SIZE]; /* used to extending */
+
+ memcpy(&hs2, hs, sizeof(hash_state));
+ sha1_process(&hs2, &X, 1);
+ sha1_process(&hs2, ses.session_id, SHA1_HASH_SIZE);
+ sha1_done(&hs2, out);
+ if (SHA1_HASH_SIZE < outlen) {
+ /* need to extend */
+ memcpy(&hs2, hs, sizeof(hash_state));
+ sha1_process(&hs2, out, SHA1_HASH_SIZE);
+ sha1_done(&hs2, k2);
+ memcpy(&out[SHA1_HASH_SIZE], k2, outlen - SHA1_HASH_SIZE);
+ }
+}
+
+/* Generate the actual encryption/integrity keys, using the results of the
+ * key exchange, as specified in section 5.2 of the IETF secsh-transport
+ * draft. This occurs after the DH key-exchange.
+ *
+ * ses.newkeys is the new set of keys which are generated, these are only
+ * taken into use after both sides have sent a newkeys message */
+
+/* Originally from kex.c, generalized for cli/svr mode --mihnea */
+void gen_new_keys() {
+
+ unsigned char C2S_IV[MAX_IV_LEN];
+ unsigned char C2S_key[MAX_KEY_LEN];
+ unsigned char S2C_IV[MAX_IV_LEN];
+ unsigned char S2C_key[MAX_KEY_LEN];
+ /* unsigned char key[MAX_KEY_LEN]; */
+ unsigned char *trans_IV, *trans_key, *recv_IV, *recv_key;
+
+ hash_state hs;
+ unsigned int C2S_keysize, S2C_keysize;
+ char mactransletter, macrecvletter; /* Client or server specific */
+
+ TRACE(("enter gen_new_keys"))
+ /* the dh_K and hash are the start of all hashes, we make use of that */
+
+ sha1_init(&hs);
+ sha1_process_mp(&hs, ses.dh_K);
+ mp_clear(ses.dh_K);
+ m_free(ses.dh_K);
+ sha1_process(&hs, ses.hash, SHA1_HASH_SIZE);
+ m_burn(ses.hash, SHA1_HASH_SIZE);
+
+ if (IS_DROPBEAR_CLIENT) {
+ trans_IV = C2S_IV;
+ recv_IV = S2C_IV;
+ trans_key = C2S_key;
+ recv_key = S2C_key;
+ C2S_keysize = ses.newkeys->trans_algo_crypt->keysize;
+ S2C_keysize = ses.newkeys->recv_algo_crypt->keysize;
+ mactransletter = 'E';
+ macrecvletter = 'F';
+ } else {
+ trans_IV = S2C_IV;
+ recv_IV = C2S_IV;
+ trans_key = S2C_key;
+ recv_key = C2S_key;
+ C2S_keysize = ses.newkeys->recv_algo_crypt->keysize;
+ S2C_keysize = ses.newkeys->trans_algo_crypt->keysize;
+ mactransletter = 'F';
+ macrecvletter = 'E';
+ }
+
+ hashkeys(C2S_IV, SHA1_HASH_SIZE, &hs, 'A');
+ hashkeys(S2C_IV, SHA1_HASH_SIZE, &hs, 'B');
+ hashkeys(C2S_key, C2S_keysize, &hs, 'C');
+ hashkeys(S2C_key, S2C_keysize, &hs, 'D');
+
+ if (cbc_start(
+ find_cipher(ses.newkeys->recv_algo_crypt->cipherdesc->name),
+ recv_IV, recv_key,
+ ses.newkeys->recv_algo_crypt->keysize, 0,
+ &ses.newkeys->recv_symmetric_struct) != CRYPT_OK) {
+ dropbear_exit("crypto error");
+ }
+
+ if (cbc_start(
+ find_cipher(ses.newkeys->trans_algo_crypt->cipherdesc->name),
+ trans_IV, trans_key,
+ ses.newkeys->trans_algo_crypt->keysize, 0,
+ &ses.newkeys->trans_symmetric_struct) != CRYPT_OK) {
+ dropbear_exit("crypto error");
+ }
+
+ /* MAC keys */
+ hashkeys(ses.newkeys->transmackey,
+ ses.newkeys->trans_algo_mac->keysize, &hs, mactransletter);
+ hashkeys(ses.newkeys->recvmackey,
+ ses.newkeys->recv_algo_mac->keysize, &hs, macrecvletter);
+
+#ifndef DISABLE_ZLIB
+ gen_new_zstreams();
+#endif
+
+ /* Switch over to the new keys */
+ m_burn(ses.keys, sizeof(struct key_context));
+ m_free(ses.keys);
+ ses.keys = ses.newkeys;
+ ses.newkeys = NULL;
+
+ TRACE(("leave gen_new_keys"))
+}
+
+#ifndef DISABLE_ZLIB
+/* Set up new zlib compression streams, close the old ones. Only
+ * called from gen_new_keys() */
+static void gen_new_zstreams() {
+
+ /* create new zstreams */
+ if (ses.newkeys->recv_algo_comp == DROPBEAR_COMP_ZLIB) {
+ ses.newkeys->recv_zstream = (z_streamp)m_malloc(sizeof(z_stream));
+ ses.newkeys->recv_zstream->zalloc = Z_NULL;
+ ses.newkeys->recv_zstream->zfree = Z_NULL;
+
+ if (inflateInit(ses.newkeys->recv_zstream) != Z_OK) {
+ dropbear_exit("zlib error");
+ }
+ } else {
+ ses.newkeys->recv_zstream = NULL;
+ }
+
+ if (ses.newkeys->trans_algo_comp == DROPBEAR_COMP_ZLIB) {
+ ses.newkeys->trans_zstream = (z_streamp)m_malloc(sizeof(z_stream));
+ ses.newkeys->trans_zstream->zalloc = Z_NULL;
+ ses.newkeys->trans_zstream->zfree = Z_NULL;
+
+ if (deflateInit(ses.newkeys->trans_zstream, Z_DEFAULT_COMPRESSION)
+ != Z_OK) {
+ dropbear_exit("zlib error");
+ }
+ } else {
+ ses.newkeys->trans_zstream = NULL;
+ }
+
+ /* clean up old keys */
+ if (ses.keys->recv_zstream != NULL) {
+ if (inflateEnd(ses.keys->recv_zstream) == Z_STREAM_ERROR) {
+ /* Z_DATA_ERROR is ok, just means that stream isn't ended */
+ dropbear_exit("crypto error");
+ }
+ m_free(ses.keys->recv_zstream);
+ }
+ if (ses.keys->trans_zstream != NULL) {
+ if (deflateEnd(ses.keys->trans_zstream) == Z_STREAM_ERROR) {
+ /* Z_DATA_ERROR is ok, just means that stream isn't ended */
+ dropbear_exit("crypto error");
+ }
+ m_free(ses.keys->trans_zstream);
+ }
+}
+#endif
+
+
+/* Executed upon receiving a kexinit message from the client to initiate
+ * key exchange. If we haven't already done so, we send the list of our
+ * preferred algorithms. The client's requested algorithms are processed,
+ * and we calculate the first portion of the key-exchange-hash for used
+ * later in the key exchange. No response is sent, as the client should
+ * initiate the diffie-hellman key exchange */
+
+/* Originally from kex.c, generalized for cli/svr mode --mihnea */
+/* Belongs in common_kex.c where it should be moved after review */
+void recv_msg_kexinit() {
+
+ unsigned int kexhashbuf_len = 0;
+ unsigned int remote_ident_len = 0;
+ unsigned int local_ident_len = 0;
+
+ TRACE(("<- KEXINIT"))
+ TRACE(("enter recv_msg_kexinit"))
+
+ if (!ses.kexstate.sentkexinit) {
+ /* we need to send a kex packet */
+ send_msg_kexinit();
+ TRACE(("continue recv_msg_kexinit: sent kexinit"))
+ }
+
+ /* start the kex hash */
+ local_ident_len = strlen(LOCAL_IDENT);
+ remote_ident_len = strlen((char*)ses.remoteident);
+
+ kexhashbuf_len = local_ident_len + remote_ident_len
+ + ses.transkexinit->len + ses.payload->len
+ + KEXHASHBUF_MAX_INTS;
+
+ ses.kexhashbuf = buf_new(kexhashbuf_len);
+
+ if (IS_DROPBEAR_CLIENT) {
+
+ /* read the peer's choice of algos */
+ read_kex_algos();
+
+ /* V_C, the client's version string (CR and NL excluded) */
+ buf_putstring(ses.kexhashbuf,
+ (unsigned char*)LOCAL_IDENT, local_ident_len);
+ /* V_S, the server's version string (CR and NL excluded) */
+ buf_putstring(ses.kexhashbuf, ses.remoteident, remote_ident_len);
+
+ /* I_C, the payload of the client's SSH_MSG_KEXINIT */
+ buf_putstring(ses.kexhashbuf,
+ ses.transkexinit->data, ses.transkexinit->len);
+ /* I_S, the payload of the server's SSH_MSG_KEXINIT */
+ buf_setpos(ses.payload, 0);
+ buf_putstring(ses.kexhashbuf, ses.payload->data, ses.payload->len);
+
+ } else {
+ /* SERVER */
+
+ /* read the peer's choice of algos */
+ read_kex_algos();
+ /* V_C, the client's version string (CR and NL excluded) */
+ buf_putstring(ses.kexhashbuf, ses.remoteident, remote_ident_len);
+ /* V_S, the server's version string (CR and NL excluded) */
+ buf_putstring(ses.kexhashbuf,
+ (unsigned char*)LOCAL_IDENT, local_ident_len);
+
+ /* I_C, the payload of the client's SSH_MSG_KEXINIT */
+ buf_setpos(ses.payload, 0);
+ buf_putstring(ses.kexhashbuf, ses.payload->data, ses.payload->len);
+
+ /* I_S, the payload of the server's SSH_MSG_KEXINIT */
+ buf_putstring(ses.kexhashbuf,
+ ses.transkexinit->data, ses.transkexinit->len);
+
+ ses.requirenext = SSH_MSG_KEXDH_INIT;
+ }
+
+ buf_free(ses.transkexinit);
+ ses.transkexinit = NULL;
+ /* the rest of ses.kexhashbuf will be done after DH exchange */
+
+ ses.kexstate.recvkexinit = 1;
+
+ 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) {
+
+ DEF_MP_INT(dh_p);
+ DEF_MP_INT(dh_q);
+ DEF_MP_INT(dh_g);
+
+ TRACE(("enter send_msg_kexdh_reply"))
+
+ m_mp_init_multi(&dh_g, &dh_p, &dh_q, NULL);
+
+ /* read the prime and generator*/
+ bytes_to_mp(&dh_p, (unsigned char*)dh_p_val, DH_P_LEN);
+
+ if (mp_set_int(&dh_g, DH_G_VAL) != MP_OKAY) {
+ dropbear_exit("Diffie-Hellman error");
+ }
+
+ /* calculate q = (p-1)/2 */
+ /* dh_priv is just a temp var here */
+ if (mp_sub_d(&dh_p, 1, dh_priv) != MP_OKAY) {
+ dropbear_exit("Diffie-Hellman error");
+ }
+ if (mp_div_2(dh_priv, &dh_q) != MP_OKAY) {
+ dropbear_exit("Diffie-Hellman error");
+ }
+
+ /* Generate a private portion 0 < dh_priv < dh_q */
+ gen_random_mpint(&dh_q, dh_priv);
+
+ /* f = g^y mod p */
+ if (mp_exptmod(&dh_g, dh_priv, &dh_p, dh_pub) != MP_OKAY) {
+ dropbear_exit("Diffie-Hellman error");
+ }
+ mp_clear_multi(&dh_g, &dh_p, &dh_q, NULL);
+}
+
+/* This function is fairly common between client/server, with some substitution
+ * of dh_e/dh_f etc. Hence these arguments:
+ * dh_pub_us is 'e' for the client, 'f' for the server. dh_pub_them is
+ * vice-versa. dh_priv is the x/y value corresponding to dh_pub_us */
+void kexdh_comb_key(mp_int *dh_pub_us, mp_int *dh_priv, mp_int *dh_pub_them,
+ sign_key *hostkey) {
+
+ mp_int dh_p;
+ mp_int *dh_e = NULL, *dh_f = NULL;
+ hash_state hs;
+
+ /* read the prime and generator*/
+ mp_init(&dh_p);
+ bytes_to_mp(&dh_p, dh_p_val, DH_P_LEN);
+
+ /* Check that dh_pub_them (dh_e or dh_f) is in the range [1, p-1] */
+ if (mp_cmp(dh_pub_them, &dh_p) != MP_LT
+ || mp_cmp_d(dh_pub_them, 0) != MP_GT) {
+ dropbear_exit("Diffie-Hellman error");
+ }
+
+ /* K = e^y mod p = f^x mod p */
+ ses.dh_K = (mp_int*)m_malloc(sizeof(mp_int));
+ m_mp_init(ses.dh_K);
+ if (mp_exptmod(dh_pub_them, dh_priv, &dh_p, ses.dh_K) != MP_OKAY) {
+ dropbear_exit("Diffie-Hellman error");
+ }
+
+ /* clear no longer needed vars */
+ mp_clear_multi(&dh_p, NULL);
+
+ /* From here on, the code needs to work with the _same_ vars on each side,
+ * not vice-versaing for client/server */
+ if (IS_DROPBEAR_CLIENT) {
+ dh_e = dh_pub_us;
+ dh_f = dh_pub_them;
+ } else {
+ dh_e = dh_pub_them;
+ dh_f = dh_pub_us;
+ }
+
+ /* Create the remainder of the hash buffer, to generate the exchange hash */
+ /* K_S, the host key */
+ buf_put_pub_key(ses.kexhashbuf, hostkey, ses.newkeys->algo_hostkey);
+ /* e, exchange value sent by the client */
+ buf_putmpint(ses.kexhashbuf, dh_e);
+ /* f, exchange value sent by the server */
+ buf_putmpint(ses.kexhashbuf, dh_f);
+ /* K, the shared secret */
+ buf_putmpint(ses.kexhashbuf, ses.dh_K);
+
+ /* calculate the hash H to sign */
+ sha1_init(&hs);
+ buf_setpos(ses.kexhashbuf, 0);
+ sha1_process(&hs, buf_getptr(ses.kexhashbuf, ses.kexhashbuf->len),
+ ses.kexhashbuf->len);
+ sha1_done(&hs, ses.hash);
+
+ buf_burn(ses.kexhashbuf);
+ buf_free(ses.kexhashbuf);
+ ses.kexhashbuf = NULL;
+
+ /* first time around, we set the session_id to H */
+ if (ses.session_id == NULL) {
+ /* create the session_id, this never needs freeing */
+ ses.session_id = (unsigned char*)m_malloc(SHA1_HASH_SIZE);
+ memcpy(ses.session_id, ses.hash, SHA1_HASH_SIZE);
+ }
+}
+
+/* read the other side's algo list. buf_match_algo is a callback to match
+ * algos for the client or server. */
+static void read_kex_algos() {
+
+ /* for asymmetry */
+ algo_type * c2s_hash_algo = NULL;
+ algo_type * s2c_hash_algo = NULL;
+ algo_type * c2s_cipher_algo = NULL;
+ algo_type * s2c_cipher_algo = NULL;
+ algo_type * c2s_comp_algo = NULL;
+ algo_type * s2c_comp_algo = NULL;
+ /* the generic one */
+ algo_type * algo = NULL;
+
+ /* which algo couldn't match */
+ char * erralgo = NULL;
+
+ int goodguess = 0;
+ int allgood = 1; /* we AND this with each goodguess and see if its still
+ true after */
+
+ buf_incrpos(ses.payload, 16); /* start after the cookie */
+
+ ses.newkeys = (struct key_context*)m_malloc(sizeof(struct key_context));
+
+ /* kex_algorithms */
+ algo = ses.buf_match_algo(ses.payload, sshkex, &goodguess);
+ allgood &= goodguess;
+ if (algo == NULL) {
+ erralgo = "kex";
+ goto error;
+ }
+ TRACE(("kex algo %s", algo->name))
+ ses.newkeys->algo_kex = algo->val;
+
+ /* server_host_key_algorithms */
+ algo = ses.buf_match_algo(ses.payload, sshhostkey, &goodguess);
+ allgood &= goodguess;
+ if (algo == NULL) {
+ erralgo = "hostkey";
+ goto error;
+ }
+ 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 (c2s_cipher_algo == NULL) {
+ erralgo = "enc c->s";
+ goto error;
+ }
+ TRACE(("enc c2s is %s", c2s_cipher_algo->name))
+
+ /* encryption_algorithms_server_to_client */
+ s2c_cipher_algo = ses.buf_match_algo(ses.payload, sshciphers, &goodguess);
+ if (s2c_cipher_algo == NULL) {
+ erralgo = "enc s->c";
+ goto error;
+ }
+ TRACE(("enc s2c is %s", s2c_cipher_algo->name))
+
+ /* mac_algorithms_client_to_server */
+ c2s_hash_algo = ses.buf_match_algo(ses.payload, sshhashes, &goodguess);
+ if (c2s_hash_algo == NULL) {
+ erralgo = "mac c->s";
+ goto error;
+ }
+ TRACE(("hash c2s is %s", c2s_hash_algo->name))
+
+ /* mac_algorithms_server_to_client */
+ s2c_hash_algo = ses.buf_match_algo(ses.payload, sshhashes, &goodguess);
+ if (s2c_hash_algo == NULL) {
+ erralgo = "mac s->c";
+ goto error;
+ }
+ TRACE(("hash s2c is %s", s2c_hash_algo->name))
+
+ /* compression_algorithms_client_to_server */
+ c2s_comp_algo = ses.buf_match_algo(ses.payload, sshcompress, &goodguess);
+ if (c2s_comp_algo == NULL) {
+ erralgo = "comp c->s";
+ goto error;
+ }
+ TRACE(("hash c2s is %s", c2s_comp_algo->name))
+
+ /* compression_algorithms_server_to_client */
+ s2c_comp_algo = ses.buf_match_algo(ses.payload, sshcompress, &goodguess);
+ if (s2c_comp_algo == NULL) {
+ erralgo = "comp s->c";
+ goto error;
+ }
+ TRACE(("hash s2c is %s", s2c_comp_algo->name))
+
+ /* languages_client_to_server */
+ buf_eatstring(ses.payload);
+
+ /* languages_server_to_client */
+ buf_eatstring(ses.payload);
+
+ /* first_kex_packet_follows */
+ if (buf_getbool(ses.payload)) {
+ ses.kexstate.firstfollows = 1;
+ /* if the guess wasn't good, we ignore the packet sent */
+ if (!allgood) {
+ ses.ignorenext = 1;
+ }
+ }
+
+ /* Handle the asymmetry */
+ if (IS_DROPBEAR_CLIENT) {
+ ses.newkeys->recv_algo_crypt =
+ (struct dropbear_cipher*)s2c_cipher_algo->data;
+ ses.newkeys->trans_algo_crypt =
+ (struct dropbear_cipher*)c2s_cipher_algo->data;
+ ses.newkeys->recv_algo_mac =
+ (struct dropbear_hash*)s2c_hash_algo->data;
+ ses.newkeys->trans_algo_mac =
+ (struct dropbear_hash*)c2s_hash_algo->data;
+ ses.newkeys->recv_algo_comp = s2c_comp_algo->val;
+ ses.newkeys->trans_algo_comp = c2s_comp_algo->val;
+ } else {
+ /* SERVER */
+ ses.newkeys->recv_algo_crypt =
+ (struct dropbear_cipher*)c2s_cipher_algo->data;
+ ses.newkeys->trans_algo_crypt =
+ (struct dropbear_cipher*)s2c_cipher_algo->data;
+ ses.newkeys->recv_algo_mac =
+ (struct dropbear_hash*)c2s_hash_algo->data;
+ ses.newkeys->trans_algo_mac =
+ (struct dropbear_hash*)s2c_hash_algo->data;
+ ses.newkeys->recv_algo_comp = c2s_comp_algo->val;
+ ses.newkeys->trans_algo_comp = s2c_comp_algo->val;
+ }
+
+ /* reserved for future extensions */
+ buf_getint(ses.payload);
+ return;
+
+error:
+ dropbear_exit("no matching algo %s", erralgo);
+}
diff --git a/common-runopts.c b/common-runopts.c
new file mode 100644
index 0000000..2de036e
--- /dev/null
+++ b/common-runopts.c
@@ -0,0 +1,57 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+#include "includes.h"
+#include "runopts.h"
+#include "signkey.h"
+#include "buffer.h"
+#include "dbutil.h"
+#include "auth.h"
+
+runopts opts; /* GLOBAL */
+
+/* returns success or failure, and the keytype in *type. If we want
+ * to restrict the type, type can contain a type to return */
+int readhostkey(const char * filename, sign_key * hostkey, int *type) {
+
+ int ret = DROPBEAR_FAILURE;
+ buffer *buf;
+
+ buf = buf_new(MAX_PRIVKEY_SIZE);
+
+ if (buf_readfile(buf, filename) == DROPBEAR_FAILURE) {
+ goto out;
+ }
+ buf_setpos(buf, 0);
+ if (buf_get_priv_key(buf, hostkey, type) == DROPBEAR_FAILURE) {
+ goto out;
+ }
+
+ ret = DROPBEAR_SUCCESS;
+out:
+
+ buf_burn(buf);
+ buf_free(buf);
+ return ret;
+}
diff --git a/common-session.c b/common-session.c
new file mode 100644
index 0000000..4c15391
--- /dev/null
+++ b/common-session.c
@@ -0,0 +1,373 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+#include "includes.h"
+#include "session.h"
+#include "dbutil.h"
+#include "packet.h"
+#include "algo.h"
+#include "buffer.h"
+#include "dss.h"
+#include "ssh.h"
+#include "random.h"
+#include "kex.h"
+#include "channel.h"
+#include "atomicio.h"
+
+static void checktimeouts();
+static int ident_readln(int fd, char* buf, int count);
+
+struct sshsession ses; /* GLOBAL */
+
+/* need to know if the session struct has been initialised, this way isn't the
+ * cleanest, but works OK */
+int sessinitdone = 0; /* GLOBAL */
+
+/* this is set when we get SIGINT or SIGTERM, the handler is in main.c */
+int exitflag = 0; /* GLOBAL */
+
+
+
+/* called only at the start of a session, set up initial state */
+void common_session_init(int sock, char* remotehost) {
+
+ TRACE(("enter session_init"))
+
+ ses.remotehost = remotehost;
+
+ ses.sock = sock;
+ ses.maxfd = sock;
+
+ ses.connecttimeout = 0;
+
+ kexfirstinitialise(); /* initialise the kex state */
+
+ ses.writepayload = buf_new(MAX_TRANS_PAYLOAD_LEN);
+ ses.transseq = 0;
+
+ ses.readbuf = NULL;
+ ses.decryptreadbuf = NULL;
+ 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;
+ ses.lastpacket = 0;
+
+ /* set all the algos to none */
+ ses.keys = (struct key_context*)m_malloc(sizeof(struct key_context));
+ ses.newkeys = NULL;
+ ses.keys->recv_algo_crypt = &dropbear_nocipher;
+ ses.keys->trans_algo_crypt = &dropbear_nocipher;
+
+ ses.keys->recv_algo_mac = &dropbear_nohash;
+ ses.keys->trans_algo_mac = &dropbear_nohash;
+
+ ses.keys->algo_kex = -1;
+ ses.keys->algo_hostkey = -1;
+ ses.keys->recv_algo_comp = DROPBEAR_COMP_NONE;
+ ses.keys->trans_algo_comp = DROPBEAR_COMP_NONE;
+
+#ifndef DISABLE_ZLIB
+ ses.keys->recv_zstream = NULL;
+ ses.keys->trans_zstream = NULL;
+#endif
+
+ /* key exchange buffers */
+ ses.session_id = NULL;
+ ses.kexhashbuf = NULL;
+ ses.transkexinit = NULL;
+ ses.dh_K = NULL;
+ ses.remoteident = NULL;
+
+ ses.chantypes = NULL;
+
+ ses.allowprivport = 0;
+
+
+ TRACE(("leave session_init"))
+}
+
+void session_loop(void(*loophandler)()) {
+
+ fd_set readfd, writefd;
+ struct timeval timeout;
+ int val;
+
+ /* main loop, select()s for all sockets in use */
+ for(;;) {
+
+ timeout.tv_sec = SELECT_TIMEOUT;
+ timeout.tv_usec = 0;
+ FD_ZERO(&writefd);
+ FD_ZERO(&readfd);
+ dropbear_assert(ses.payload == NULL);
+ if (ses.sock != -1) {
+ FD_SET(ses.sock, &readfd);
+ if (!isempty(&ses.writequeue)) {
+ FD_SET(ses.sock, &writefd);
+ }
+ }
+
+ /* set up for channels which require reading/writing */
+ if (ses.dataallowed) {
+ setchannelfds(&readfd, &writefd);
+ }
+ val = select(ses.maxfd+1, &readfd, &writefd, NULL, &timeout);
+
+ if (exitflag) {
+ dropbear_exit("Terminated by signal");
+ }
+
+ if (val < 0) {
+ if (errno == EINTR) {
+ /* This must happen even if we've been interrupted, so that
+ * changed signal-handler vars can take effect etc */
+ if (loophandler) {
+ loophandler();
+ }
+ continue;
+ } else {
+ dropbear_exit("Error in select");
+ }
+ }
+
+ /* check for auth timeout, rekeying required etc */
+ checktimeouts();
+
+ if (val == 0) {
+ /* timeout */
+ TRACE(("select timeout"))
+ continue;
+ }
+
+ /* process session socket's incoming/outgoing data */
+ if (ses.sock != -1) {
+ if (FD_ISSET(ses.sock, &writefd) && !isempty(&ses.writequeue)) {
+ write_packet();
+ }
+
+ if (FD_ISSET(ses.sock, &readfd)) {
+ read_packet();
+ }
+
+ /* Process the decrypted packet. After this, the read buffer
+ * will be ready for a new packet */
+ if (ses.payload != NULL) {
+ process_packet();
+ }
+ }
+
+ /* process pipes etc for the channels, ses.dataallowed == 0
+ * during rekeying ) */
+ if (ses.dataallowed) {
+ channelio(&readfd, &writefd);
+ }
+
+ if (loophandler) {
+ loophandler();
+ }
+
+ } /* for(;;) */
+
+ /* Not reached */
+}
+
+/* clean up a session on exit */
+void common_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"))
+ return;
+ }
+
+ m_free(ses.session_id);
+ m_burn(ses.keys, sizeof(struct key_context));
+ m_free(ses.keys);
+
+ chancleanup();
+
+ TRACE(("leave session_cleanup"))
+}
+
+
+void session_identification() {
+
+ /* max length of 255 chars */
+ 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",
+ strlen(LOCAL_IDENT "\r\n")) == DROPBEAR_FAILURE) {
+ dropbear_exit("Error writing ident string");
+ }
+
+ /* If they send more than 50 lines, something is wrong */
+ for (i = 0; i < 50; 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) {
+ 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);
+ }
+
+ /* Shall assume that 2.x will be backwards compatible. */
+ if (strncmp(ses.remoteident, "SSH-2.", 6) != 0
+ && strncmp(ses.remoteident, "SSH-1.99-", 9) != 0) {
+ dropbear_exit("Incompatible remote version '%s'", ses.remoteident);
+ }
+
+ TRACE(("remoteident: %s", ses.remoteident))
+
+}
+
+/* returns the length including null-terminating zero on success,
+ * or -1 on failure */
+static int ident_readln(int fd, char* buf, int count) {
+
+ char in;
+ int pos = 0;
+ int num = 0;
+ fd_set fds;
+ struct timeval timeout;
+
+ TRACE(("enter ident_readln"))
+
+ if (count < 1) {
+ return -1;
+ }
+
+ FD_ZERO(&fds);
+
+ /* select since it's a non-blocking fd */
+
+ /* leave space to null-terminate */
+ while (pos < count-1) {
+
+ FD_SET(fd, &fds);
+
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
+ if (select(fd+1, &fds, NULL, NULL, &timeout) < 0) {
+ if (errno == EINTR) {
+ continue;
+ }
+ TRACE(("leave ident_readln: select error"))
+ return -1;
+ }
+
+ checktimeouts();
+
+ /* Have to go one byte at a time, since we don't want to read past
+ * the end, and have to somehow shove bytes back into the normal
+ * packet reader */
+ if (FD_ISSET(fd, &fds)) {
+ num = read(fd, &in, 1);
+ /* a "\n" is a newline, "\r" we want to read in and keep going
+ * so that it won't be read as part of the next line */
+ if (num < 0) {
+ /* error */
+ if (errno == EINTR) {
+ continue; /* not a real error */
+ }
+ TRACE(("leave ident_readln: read error"))
+ return -1;
+ }
+ if (num == 0) {
+ /* EOF */
+ TRACE(("leave ident_readln: EOF"))
+ return -1;
+ }
+ if (in == '\n') {
+ /* end of ident string */
+ break;
+ }
+ /* we don't want to include '\r's */
+ if (in != '\r') {
+ buf[pos] = in;
+ pos++;
+ }
+ }
+ }
+
+ buf[pos] = '\0';
+ TRACE(("leave ident_readln: return %d", pos+1))
+ return pos+1;
+}
+
+/* Check all timeouts which are required. Currently these are the time for
+ * user authentication, and the automatic rekeying. */
+static void checktimeouts() {
+
+ struct timeval tv;
+ long secs;
+
+ if (gettimeofday(&tv, 0) < 0) {
+ dropbear_exit("Error getting time");
+ }
+
+ secs = tv.tv_sec;
+
+ if (ses.connecttimeout != 0 && secs > ses.connecttimeout) {
+ dropbear_close("Timeout before auth");
+ }
+
+ /* we can't rekey if we haven't done remote ident exchange yet */
+ if (ses.remoteident == NULL) {
+ return;
+ }
+
+ 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"))
+ send_msg_kexinit();
+ }
+}
+
diff --git a/compat.c b/compat.c
new file mode 100644
index 0000000..7e0c1ac
--- /dev/null
+++ b/compat.c
@@ -0,0 +1,281 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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.
+ *
+ * strlcat() is copyright as follows:
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * daemon() and getusershell() is copyright as follows:
+ *
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+* SUCH DAMAGE.
+*
+* Modifications for Dropbear to getusershell() are by Paul Marinceu
+*/
+
+#include "includes.h"
+
+#ifndef HAVE_GETUSERSHELL
+static char **curshell, **shells, *strings;
+static char **initshells();
+#endif
+
+#ifndef HAVE_STRLCPY
+/* Implemented by matt as specified in freebsd 4.7 manpage.
+ * We don't require great speed, is simply for use with sshpty code */
+size_t strlcpy(char *dst, const char *src, size_t size) {
+
+ size_t i;
+
+ /* this is undefined, though size==0 -> return 0 */
+ if (size < 1) {
+ return 0;
+ }
+
+ for (i = 0; i < size-1; i++) {
+ if (src[i] == '\0') {
+ break;
+ } else {
+ dst[i] = src[i];
+ }
+ }
+
+ dst[i] = '\0';
+ return strlen(src);
+
+}
+#endif /* HAVE_STRLCPY */
+
+#ifndef HAVE_STRLCAT
+/* taken from openbsd-compat for OpenSSH 3.6.1p1 */
+/* "$OpenBSD: strlcat.c,v 1.8 2001/05/13 15:40:15 deraadt Exp $"
+ *
+ * Appends src to string dst of size siz (unlike strncat, siz is the
+ * full size of dst, not space left). At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz <= strlen(dst)).
+ * Returns strlen(src) + MIN(siz, strlen(initial dst)).
+ * If retval >= siz, truncation occurred.
+ */
+ size_t
+strlcat(dst, src, siz)
+ char *dst;
+ const char *src;
+ size_t siz;
+{
+ register char *d = dst;
+ register const char *s = src;
+ register size_t n = siz;
+ size_t dlen;
+
+ /* Find the end of dst and adjust bytes left but don't go past end */
+ while (n-- != 0 && *d != '\0')
+ d++;
+ dlen = d - dst;
+ n = siz - dlen;
+
+ if (n == 0)
+ return(dlen + strlen(s));
+ while (*s != '\0') {
+ if (n != 1) {
+ *d++ = *s;
+ n--;
+ }
+ s++;
+ }
+ *d = '\0';
+
+ return(dlen + (s - src)); /* count does not include NUL */
+}
+#endif /* HAVE_STRLCAT */
+
+#ifndef HAVE_DAEMON
+/* From NetBSD - daemonise a process */
+
+int daemon(int nochdir, int noclose) {
+
+ int fd;
+
+ switch (fork()) {
+ case -1:
+ return (-1);
+ case 0:
+ break;
+ default:
+ _exit(0);
+ }
+
+ if (setsid() == -1)
+ return -1;
+
+ if (!nochdir)
+ (void)chdir("/");
+
+ if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
+ (void)dup2(fd, STDIN_FILENO);
+ (void)dup2(fd, STDOUT_FILENO);
+ (void)dup2(fd, STDERR_FILENO);
+ if (fd > STDERR_FILENO)
+ (void)close(fd);
+ }
+ return 0;
+}
+#endif /* HAVE_DAEMON */
+
+#ifndef HAVE_BASENAME
+
+char *basename(const char *path) {
+
+ char *foo = strrchr(path, '/');
+ return ++foo;
+}
+
+#endif /* HAVE_BASENAME */
+
+#ifndef HAVE_GETUSERSHELL
+
+/*
+ * Get a list of shells from /etc/shells, if it exists.
+ */
+char * getusershell() {
+ char *ret;
+
+ if (curshell == NULL)
+ curshell = initshells();
+ ret = *curshell;
+ if (ret != NULL)
+ curshell++;
+ return (ret);
+}
+
+void endusershell() {
+
+ if (shells != NULL)
+ free(shells);
+ shells = NULL;
+ if (strings != NULL)
+ free(strings);
+ strings = NULL;
+ curshell = NULL;
+}
+
+void setusershell() {
+ curshell = initshells();
+}
+
+static char **initshells() {
+ /* don't touch this list. */
+ const char *okshells[] = { "/bin/sh", "/bin/csh", NULL };
+ register char **sp, *cp;
+ register FILE *fp;
+ struct stat statb;
+ int flen;
+
+ if (shells != NULL)
+ free(shells);
+ shells = NULL;
+ if (strings != NULL)
+ free(strings);
+ strings = NULL;
+ if ((fp = fopen("/etc/shells", "rc")) == NULL)
+ return (char **) okshells;
+ if (fstat(fileno(fp), &statb) == -1) {
+ (void)fclose(fp);
+ return (char **) okshells;
+ }
+ if ((strings = malloc((u_int)statb.st_size + 1)) == NULL) {
+ (void)fclose(fp);
+ return (char **) okshells;
+ }
+ shells = calloc((unsigned)statb.st_size / 3, sizeof (char *));
+ if (shells == NULL) {
+ (void)fclose(fp);
+ free(strings);
+ strings = NULL;
+ return (char **) okshells;
+ }
+ sp = shells;
+ cp = strings;
+ flen = statb.st_size;
+ while (fgets(cp, flen - (cp - strings), fp) != NULL) {
+ while (*cp != '#' && *cp != '/' && *cp != '\0')
+ cp++;
+ if (*cp == '#' || *cp == '\0')
+ continue;
+ *sp++ = cp;
+ while (!isspace(*cp) && *cp != '#' && *cp != '\0')
+ cp++;
+ *cp++ = '\0';
+ }
+ *sp = NULL;
+ (void)fclose(fp);
+ return (shells);
+}
+
+#endif /* HAVE_GETUSERSHELL */
diff --git a/compat.h b/compat.h
new file mode 100644
index 0000000..1ab344f
--- /dev/null
+++ b/compat.h
@@ -0,0 +1,56 @@
+/*
+ * 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. */
+
+#ifndef _COMPAT_H_
+#define _COMPAT_H_
+
+#include "includes.h"
+
+#ifndef HAVE_STRLCPY
+size_t strlcpy(char *dst, const char *src, size_t size);
+#endif
+
+#ifndef HAVE_STRLCAT
+size_t strlcat(char *dst, const char *src, size_t siz);
+#endif
+
+#ifndef HAVE_DAEMON
+int daemon(int nochdir, int noclose);
+#endif
+
+#ifndef HAVE_BASENAME
+char *basename(const char* path);
+#endif
+
+#ifndef HAVE_GETUSERSHELL
+char *getusershell();
+void setusershell();
+void endusershell();
+#endif
+
+#ifndef _PATH_DEVNULL
+#define _PATH_DEVNULL "/dev/null"
+#endif
+
+#endif /* _COMPAT_H_ */
diff --git a/config.guess b/config.guess
new file mode 100644
index 0000000..9e55e1a
--- /dev/null
+++ b/config.guess
@@ -0,0 +1,1391 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+# 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+
+timestamp='2003-05-19'
+
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Originally written by Per Bothner <per@bothner.com>.
+# Please send patches to <config-patches@gnu.org>. Submit a context
+# diff and a properly formatted ChangeLog entry.
+#
+# This script attempts to guess a canonical system name similar to
+# config.sub. If it succeeds, it prints the system name on stdout, and
+# exits with 0. Otherwise, it exits with 1.
+#
+# The plan is that this can be called by configure scripts if you
+# don't specify an explicit build system type.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Operation modes:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit 0 ;;
+ --version | -v )
+ echo "$version" ; exit 0 ;;
+ --help | --h* | -h )
+ echo "$usage"; exit 0 ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help" >&2
+ exit 1 ;;
+ * )
+ break ;;
+ esac
+done
+
+if test $# != 0; then
+ echo "$me: too many arguments$help" >&2
+ exit 1
+fi
+
+trap 'exit 1' 1 2 15
+
+# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
+# compiler to aid in system detection is discouraged as it requires
+# temporary files to be created and, as you can see below, it is a
+# headache to deal with in a portable fashion.
+
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+# Portable tmp directory creation inspired by the Autoconf team.
+
+set_cc_for_build='
+trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
+trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
+: ${TMPDIR=/tmp} ;
+ { tmp=`(umask 077 && mktemp -d -q "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
+ { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
+ { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
+dummy=$tmp/dummy ;
+tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
+case $CC_FOR_BUILD,$HOST_CC,$CC in
+ ,,) echo "int x;" > $dummy.c ;
+ for c in cc gcc c89 c99 ; do
+ if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
+ CC_FOR_BUILD="$c"; break ;
+ fi ;
+ done ;
+ if test x"$CC_FOR_BUILD" = x ; then
+ CC_FOR_BUILD=no_compiler_found ;
+ fi
+ ;;
+ ,,*) CC_FOR_BUILD=$CC ;;
+ ,*,*) CC_FOR_BUILD=$HOST_CC ;;
+esac ;'
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+ PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+ *:NetBSD:*:*)
+ # NetBSD (nbsd) targets should (where applicable) match one or
+ # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
+ # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently
+ # switched to ELF, *-*-netbsd* would select the old
+ # object file format. This provides both forward
+ # compatibility and a consistent mechanism for selecting the
+ # object file format.
+ #
+ # Note: NetBSD doesn't particularly care about the vendor
+ # portion of the name. We always set it to "unknown".
+ sysctl="sysctl -n hw.machine_arch"
+ UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
+ /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
+ case "${UNAME_MACHINE_ARCH}" in
+ armeb) machine=armeb-unknown ;;
+ arm*) machine=arm-unknown ;;
+ sh3el) machine=shl-unknown ;;
+ sh3eb) machine=sh-unknown ;;
+ *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
+ esac
+ # The Operating System including object format, if it has switched
+ # to ELF recently, or will in the future.
+ case "${UNAME_MACHINE_ARCH}" in
+ arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+ eval $set_cc_for_build
+ if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep __ELF__ >/dev/null
+ then
+ # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+ # Return netbsd for either. FIX?
+ os=netbsd
+ else
+ os=netbsdelf
+ fi
+ ;;
+ *)
+ os=netbsd
+ ;;
+ esac
+ # The OS release
+ # Debian GNU/NetBSD machines have a different userland, and
+ # thus, need a distinct triplet. However, they do not need
+ # kernel version information, so it can be replaced with a
+ # suitable tag, in the style of linux-gnu.
+ case "${UNAME_VERSION}" in
+ Debian*)
+ release='-gnu'
+ ;;
+ *)
+ release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+ ;;
+ esac
+ # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+ # contains redundant information, the shorter form:
+ # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+ echo "${machine}-${os}${release}"
+ exit 0 ;;
+ amiga:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ arc:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ hp300:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mac68k:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ macppc:OpenBSD:*:*)
+ echo powerpc-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mvme68k:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mvme88k:OpenBSD:*:*)
+ echo m88k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mvmeppc:OpenBSD:*:*)
+ echo powerpc-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ pmax:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ sgi:OpenBSD:*:*)
+ echo mipseb-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ sun3:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ wgrisc:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ *:OpenBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ alpha:OSF1:*:*)
+ if test $UNAME_RELEASE = "V4.0"; then
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+ fi
+ # According to Compaq, /usr/sbin/psrinfo has been available on
+ # OSF/1 and Tru64 systems produced since 1995. I hope that
+ # covers most systems running today. This code pipes the CPU
+ # types through head -n 1, so we only detect the type of CPU 0.
+ ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1`
+ case "$ALPHA_CPU_TYPE" in
+ "EV4 (21064)")
+ UNAME_MACHINE="alpha" ;;
+ "EV4.5 (21064)")
+ UNAME_MACHINE="alpha" ;;
+ "LCA4 (21066/21068)")
+ UNAME_MACHINE="alpha" ;;
+ "EV5 (21164)")
+ UNAME_MACHINE="alphaev5" ;;
+ "EV5.6 (21164A)")
+ UNAME_MACHINE="alphaev56" ;;
+ "EV5.6 (21164PC)")
+ UNAME_MACHINE="alphapca56" ;;
+ "EV5.7 (21164PC)")
+ UNAME_MACHINE="alphapca57" ;;
+ "EV6 (21264)")
+ UNAME_MACHINE="alphaev6" ;;
+ "EV6.7 (21264A)")
+ UNAME_MACHINE="alphaev67" ;;
+ "EV6.8CB (21264C)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.8AL (21264B)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.8CX (21264D)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.9A (21264/EV69A)")
+ UNAME_MACHINE="alphaev69" ;;
+ "EV7 (21364)")
+ UNAME_MACHINE="alphaev7" ;;
+ "EV7.9 (21364A)")
+ UNAME_MACHINE="alphaev79" ;;
+ esac
+ # A Vn.n version is a released version.
+ # A Tn.n version is a released field test version.
+ # A Xn.n version is an unreleased experimental baselevel.
+ # 1.2 uses "1.2" for uname -r.
+ echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ exit 0 ;;
+ Alpha\ *:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # Should we change UNAME_MACHINE based on the output of uname instead
+ # of the specific Alpha model?
+ echo alpha-pc-interix
+ exit 0 ;;
+ 21064:Windows_NT:50:3)
+ echo alpha-dec-winnt3.5
+ exit 0 ;;
+ Amiga*:UNIX_System_V:4.0:*)
+ echo m68k-unknown-sysv4
+ exit 0;;
+ *:[Aa]miga[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-amigaos
+ exit 0 ;;
+ *:[Mm]orph[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-morphos
+ exit 0 ;;
+ *:OS/390:*:*)
+ echo i370-ibm-openedition
+ exit 0 ;;
+ arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+ echo arm-acorn-riscix${UNAME_RELEASE}
+ exit 0;;
+ SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+ echo hppa1.1-hitachi-hiuxmpp
+ exit 0;;
+ Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+ # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+ if test "`(/bin/universe) 2>/dev/null`" = att ; then
+ echo pyramid-pyramid-sysv3
+ else
+ echo pyramid-pyramid-bsd
+ fi
+ exit 0 ;;
+ NILE*:*:*:dcosx)
+ echo pyramid-pyramid-svr4
+ exit 0 ;;
+ DRS?6000:UNIX_SV:4.2*:7*)
+ case `/usr/bin/uname -p` in
+ sparc) echo sparc-icl-nx7 && exit 0 ;;
+ esac ;;
+ sun4H:SunOS:5.*:*)
+ echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+ echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ i86pc:SunOS:5.*:*)
+ echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:6*:*)
+ # According to config.sub, this is the proper way to canonicalize
+ # SunOS6. Hard to guess exactly what SunOS6 will be like, but
+ # it's likely to be more like Solaris than SunOS4.
+ echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:*:*)
+ case "`/usr/bin/arch -k`" in
+ Series*|S4*)
+ UNAME_RELEASE=`uname -v`
+ ;;
+ esac
+ # Japanese Language versions have a version number like `4.1.3-JL'.
+ echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+ exit 0 ;;
+ sun3*:SunOS:*:*)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ exit 0 ;;
+ sun*:*:4.2BSD:*)
+ UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+ test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+ case "`/bin/arch`" in
+ sun3)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ ;;
+ sun4)
+ echo sparc-sun-sunos${UNAME_RELEASE}
+ ;;
+ esac
+ exit 0 ;;
+ aushp:SunOS:*:*)
+ echo sparc-auspex-sunos${UNAME_RELEASE}
+ exit 0 ;;
+ # The situation for MiNT is a little confusing. The machine name
+ # can be virtually everything (everything which is not
+ # "atarist" or "atariste" at least should have a processor
+ # > m68000). The system name ranges from "MiNT" over "FreeMiNT"
+ # to the lowercase version "mint" (or "freemint"). Finally
+ # the system name "TOS" denotes a system which is actually not
+ # MiNT. But MiNT is downward compatible to TOS, so this should
+ # be no problem.
+ atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit 0 ;;
+ atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit 0 ;;
+ *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit 0 ;;
+ milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+ echo m68k-milan-mint${UNAME_RELEASE}
+ exit 0 ;;
+ hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+ echo m68k-hades-mint${UNAME_RELEASE}
+ exit 0 ;;
+ *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+ echo m68k-unknown-mint${UNAME_RELEASE}
+ exit 0 ;;
+ powerpc:machten:*:*)
+ echo powerpc-apple-machten${UNAME_RELEASE}
+ exit 0 ;;
+ RISC*:Mach:*:*)
+ echo mips-dec-mach_bsd4.3
+ exit 0 ;;
+ RISC*:ULTRIX:*:*)
+ echo mips-dec-ultrix${UNAME_RELEASE}
+ exit 0 ;;
+ VAX*:ULTRIX*:*:*)
+ echo vax-dec-ultrix${UNAME_RELEASE}
+ exit 0 ;;
+ 2020:CLIX:*:* | 2430:CLIX:*:*)
+ echo clipper-intergraph-clix${UNAME_RELEASE}
+ exit 0 ;;
+ mips:*:*:UMIPS | mips:*:*:RISCos)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+#ifdef __cplusplus
+#include <stdio.h> /* for printf() prototype */
+ int main (int argc, char *argv[]) {
+#else
+ int main (argc, argv) int argc; char *argv[]; {
+#endif
+ #if defined (host_mips) && defined (MIPSEB)
+ #if defined (SYSTYPE_SYSV)
+ printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_SVR4)
+ printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+ printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+ #endif
+ #endif
+ exit (-1);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c \
+ && $dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \
+ && exit 0
+ echo mips-mips-riscos${UNAME_RELEASE}
+ exit 0 ;;
+ Motorola:PowerMAX_OS:*:*)
+ echo powerpc-motorola-powermax
+ exit 0 ;;
+ Motorola:*:4.3:PL8-*)
+ echo powerpc-harris-powermax
+ exit 0 ;;
+ Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+ echo powerpc-harris-powermax
+ exit 0 ;;
+ Night_Hawk:Power_UNIX:*:*)
+ echo powerpc-harris-powerunix
+ exit 0 ;;
+ m88k:CX/UX:7*:*)
+ echo m88k-harris-cxux7
+ exit 0 ;;
+ m88k:*:4*:R4*)
+ echo m88k-motorola-sysv4
+ exit 0 ;;
+ m88k:*:3*:R3*)
+ echo m88k-motorola-sysv3
+ exit 0 ;;
+ AViiON:dgux:*:*)
+ # DG/UX returns AViiON for all architectures
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
+ then
+ if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
+ [ ${TARGET_BINARY_INTERFACE}x = x ]
+ then
+ echo m88k-dg-dgux${UNAME_RELEASE}
+ else
+ echo m88k-dg-dguxbcs${UNAME_RELEASE}
+ fi
+ else
+ echo i586-dg-dgux${UNAME_RELEASE}
+ fi
+ exit 0 ;;
+ M88*:DolphinOS:*:*) # DolphinOS (SVR3)
+ echo m88k-dolphin-sysv3
+ exit 0 ;;
+ M88*:*:R3*:*)
+ # Delta 88k system running SVR3
+ echo m88k-motorola-sysv3
+ exit 0 ;;
+ XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+ echo m88k-tektronix-sysv3
+ exit 0 ;;
+ Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+ echo m68k-tektronix-bsd
+ exit 0 ;;
+ *:IRIX*:*:*)
+ echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+ exit 0 ;;
+ ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+ echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id
+ exit 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX '
+ i*86:AIX:*:*)
+ echo i386-ibm-aix
+ exit 0 ;;
+ ia64:AIX:*:*)
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+ fi
+ echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
+ exit 0 ;;
+ *:AIX:2:3)
+ if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <sys/systemcfg.h>
+
+ main()
+ {
+ if (!__power_pc())
+ exit(1);
+ puts("powerpc-ibm-aix3.2.5");
+ exit(0);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0
+ echo rs6000-ibm-aix3.2.5
+ elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+ echo rs6000-ibm-aix3.2.4
+ else
+ echo rs6000-ibm-aix3.2
+ fi
+ exit 0 ;;
+ *:AIX:*:[45])
+ IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
+ if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
+ IBM_ARCH=rs6000
+ else
+ IBM_ARCH=powerpc
+ fi
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+ fi
+ echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+ exit 0 ;;
+ *:AIX:*:*)
+ echo rs6000-ibm-aix
+ exit 0 ;;
+ ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+ echo romp-ibm-bsd4.4
+ exit 0 ;;
+ ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and
+ echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to
+ exit 0 ;; # report: romp-ibm BSD 4.3
+ *:BOSX:*:*)
+ echo rs6000-bull-bosx
+ exit 0 ;;
+ DPX/2?00:B.O.S.:*:*)
+ echo m68k-bull-sysv3
+ exit 0 ;;
+ 9000/[34]??:4.3bsd:1.*:*)
+ echo m68k-hp-bsd
+ exit 0 ;;
+ hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+ echo m68k-hp-bsd4.4
+ exit 0 ;;
+ 9000/[34678]??:HP-UX:*:*)
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ case "${UNAME_MACHINE}" in
+ 9000/31? ) HP_ARCH=m68000 ;;
+ 9000/[34]?? ) HP_ARCH=m68k ;;
+ 9000/[678][0-9][0-9])
+ if [ -x /usr/bin/getconf ]; then
+ sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+ sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+ case "${sc_cpu_version}" in
+ 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
+ 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
+ 532) # CPU_PA_RISC2_0
+ case "${sc_kernel_bits}" in
+ 32) HP_ARCH="hppa2.0n" ;;
+ 64) HP_ARCH="hppa2.0w" ;;
+ '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20
+ esac ;;
+ esac
+ fi
+ if [ "${HP_ARCH}" = "" ]; then
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+
+ #define _HPUX_SOURCE
+ #include <stdlib.h>
+ #include <unistd.h>
+
+ int main ()
+ {
+ #if defined(_SC_KERNEL_BITS)
+ long bits = sysconf(_SC_KERNEL_BITS);
+ #endif
+ long cpu = sysconf (_SC_CPU_VERSION);
+
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+ case CPU_PA_RISC2_0:
+ #if defined(_SC_KERNEL_BITS)
+ switch (bits)
+ {
+ case 64: puts ("hppa2.0w"); break;
+ case 32: puts ("hppa2.0n"); break;
+ default: puts ("hppa2.0"); break;
+ } break;
+ #else /* !defined(_SC_KERNEL_BITS) */
+ puts ("hppa2.0"); break;
+ #endif
+ default: puts ("hppa1.0"); break;
+ }
+ exit (0);
+ }
+EOF
+ (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
+ test -z "$HP_ARCH" && HP_ARCH=hppa
+ fi ;;
+ esac
+ if [ ${HP_ARCH} = "hppa2.0w" ]
+ then
+ # avoid double evaluation of $set_cc_for_build
+ test -n "$CC_FOR_BUILD" || eval $set_cc_for_build
+ if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E -) | grep __LP64__ >/dev/null
+ then
+ HP_ARCH="hppa2.0w"
+ else
+ HP_ARCH="hppa64"
+ fi
+ fi
+ echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+ exit 0 ;;
+ ia64:HP-UX:*:*)
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ echo ia64-hp-hpux${HPUX_REV}
+ exit 0 ;;
+ 3050*:HI-UX:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <unistd.h>
+ int
+ main ()
+ {
+ long cpu = sysconf (_SC_CPU_VERSION);
+ /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+ true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct
+ results, however. */
+ if (CPU_IS_PA_RISC (cpu))
+ {
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+ default: puts ("hppa-hitachi-hiuxwe2"); break;
+ }
+ }
+ else if (CPU_IS_HP_MC68K (cpu))
+ puts ("m68k-hitachi-hiuxwe2");
+ else puts ("unknown-hitachi-hiuxwe2");
+ exit (0);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0
+ echo unknown-hitachi-hiuxwe2
+ exit 0 ;;
+ 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+ echo hppa1.1-hp-bsd
+ exit 0 ;;
+ 9000/8??:4.3bsd:*:*)
+ echo hppa1.0-hp-bsd
+ exit 0 ;;
+ *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+ echo hppa1.0-hp-mpeix
+ exit 0 ;;
+ hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+ echo hppa1.1-hp-osf
+ exit 0 ;;
+ hp8??:OSF1:*:*)
+ echo hppa1.0-hp-osf
+ exit 0 ;;
+ i*86:OSF1:*:*)
+ if [ -x /usr/sbin/sysversion ] ; then
+ echo ${UNAME_MACHINE}-unknown-osf1mk
+ else
+ echo ${UNAME_MACHINE}-unknown-osf1
+ fi
+ exit 0 ;;
+ parisc*:Lites*:*:*)
+ echo hppa1.1-hp-lites
+ exit 0 ;;
+ C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+ echo c1-convex-bsd
+ exit 0 ;;
+ C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit 0 ;;
+ C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+ echo c34-convex-bsd
+ exit 0 ;;
+ C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+ echo c38-convex-bsd
+ exit 0 ;;
+ C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+ echo c4-convex-bsd
+ exit 0 ;;
+ CRAY*Y-MP:*:*:*)
+ echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ CRAY*[A-Z]90:*:*:*)
+ echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+ -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ CRAY*TS:*:*:*)
+ echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ CRAY*T3E:*:*:*)
+ echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ CRAY*SV1:*:*:*)
+ echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ *:UNICOS/mp:*:*)
+ echo nv1-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+ FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+ FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+ echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit 0 ;;
+ i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+ echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ sparc*:BSD/OS:*:*)
+ echo sparc-unknown-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ *:BSD/OS:*:*)
+ echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ *:FreeBSD:*:*|*:GNU/FreeBSD:*:*)
+ # Determine whether the default compiler uses glibc.
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <features.h>
+ #if __GLIBC__ >= 2
+ LIBC=gnu
+ #else
+ LIBC=
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=`
+ echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`${LIBC:+-$LIBC}
+ exit 0 ;;
+ i*:CYGWIN*:*)
+ echo ${UNAME_MACHINE}-pc-cygwin
+ exit 0 ;;
+ i*:MINGW*:*)
+ echo ${UNAME_MACHINE}-pc-mingw32
+ exit 0 ;;
+ i*:PW*:*)
+ echo ${UNAME_MACHINE}-pc-pw32
+ exit 0 ;;
+ x86:Interix*:3*)
+ echo i586-pc-interix3
+ exit 0 ;;
+ [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
+ echo i${UNAME_MACHINE}-pc-mks
+ exit 0 ;;
+ i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
+ # UNAME_MACHINE based on the output of uname instead of i386?
+ echo i586-pc-interix
+ exit 0 ;;
+ i*:UWIN*:*)
+ echo ${UNAME_MACHINE}-pc-uwin
+ exit 0 ;;
+ p*:CYGWIN*:*)
+ echo powerpcle-unknown-cygwin
+ exit 0 ;;
+ prep*:SunOS:5.*:*)
+ echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ *:GNU:*:*)
+ echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+ exit 0 ;;
+ i*86:Minix:*:*)
+ echo ${UNAME_MACHINE}-pc-minix
+ exit 0 ;;
+ arm*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ cris:Linux:*:*)
+ echo cris-axis-linux-gnu
+ exit 0 ;;
+ ia64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ m68*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ mips:Linux:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #undef CPU
+ #undef mips
+ #undef mipsel
+ #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+ CPU=mipsel
+ #else
+ #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+ CPU=mips
+ #else
+ CPU=
+ #endif
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=`
+ test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0
+ ;;
+ mips64:Linux:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #undef CPU
+ #undef mips64
+ #undef mips64el
+ #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+ CPU=mips64el
+ #else
+ #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+ CPU=mips64
+ #else
+ CPU=
+ #endif
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=`
+ test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0
+ ;;
+ ppc:Linux:*:*)
+ echo powerpc-unknown-linux-gnu
+ exit 0 ;;
+ ppc64:Linux:*:*)
+ echo powerpc64-unknown-linux-gnu
+ exit 0 ;;
+ alpha:Linux:*:*)
+ case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+ EV5) UNAME_MACHINE=alphaev5 ;;
+ EV56) UNAME_MACHINE=alphaev56 ;;
+ PCA56) UNAME_MACHINE=alphapca56 ;;
+ PCA57) UNAME_MACHINE=alphapca56 ;;
+ EV6) UNAME_MACHINE=alphaev6 ;;
+ EV67) UNAME_MACHINE=alphaev67 ;;
+ EV68*) UNAME_MACHINE=alphaev68 ;;
+ esac
+ objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null
+ if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
+ echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
+ exit 0 ;;
+ parisc:Linux:*:* | hppa:Linux:*:*)
+ # Look for CPU level
+ case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+ PA7*) echo hppa1.1-unknown-linux-gnu ;;
+ PA8*) echo hppa2.0-unknown-linux-gnu ;;
+ *) echo hppa-unknown-linux-gnu ;;
+ esac
+ exit 0 ;;
+ parisc64:Linux:*:* | hppa64:Linux:*:*)
+ echo hppa64-unknown-linux-gnu
+ exit 0 ;;
+ s390:Linux:*:* | s390x:Linux:*:*)
+ echo ${UNAME_MACHINE}-ibm-linux
+ exit 0 ;;
+ sh*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ sparc:Linux:*:* | sparc64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ x86_64:Linux:*:*)
+ echo x86_64-unknown-linux-gnu
+ exit 0 ;;
+ i*86:Linux:*:*)
+ # The BFD linker knows what the default object file format is, so
+ # first see if it will tell us. cd to the root directory to prevent
+ # problems with other programs or directories called `ld' in the path.
+ # Set LC_ALL=C to ensure ld outputs messages in English.
+ ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \
+ | sed -ne '/supported targets:/!d
+ s/[ ][ ]*/ /g
+ s/.*supported targets: *//
+ s/ .*//
+ p'`
+ case "$ld_supported_targets" in
+ elf32-i386)
+ TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu"
+ ;;
+ a.out-i386-linux)
+ echo "${UNAME_MACHINE}-pc-linux-gnuaout"
+ exit 0 ;;
+ coff-i386)
+ echo "${UNAME_MACHINE}-pc-linux-gnucoff"
+ exit 0 ;;
+ "")
+ # Either a pre-BFD a.out linker (linux-gnuoldld) or
+ # one that does not give us useful --help.
+ echo "${UNAME_MACHINE}-pc-linux-gnuoldld"
+ exit 0 ;;
+ esac
+ # Determine whether the default compiler is a.out or elf
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <features.h>
+ #ifdef __ELF__
+ # ifdef __GLIBC__
+ # if __GLIBC__ >= 2
+ LIBC=gnu
+ # else
+ LIBC=gnulibc1
+ # endif
+ # else
+ LIBC=gnulibc1
+ # endif
+ #else
+ #ifdef __INTEL_COMPILER
+ LIBC=gnu
+ #else
+ LIBC=gnuaout
+ #endif
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=`
+ test x"${LIBC}" != x && echo "${UNAME_MACHINE}-pc-linux-${LIBC}" && exit 0
+ test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0
+ ;;
+ i*86:DYNIX/ptx:4*:*)
+ # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+ # earlier versions are messed up and put the nodename in both
+ # sysname and nodename.
+ echo i386-sequent-sysv4
+ exit 0 ;;
+ i*86:UNIX_SV:4.2MP:2.*)
+ # Unixware is an offshoot of SVR4, but it has its own version
+ # number series starting with 2...
+ # I am not positive that other SVR4 systems won't match this,
+ # I just have to hope. -- rms.
+ # Use sysv4.2uw... so that sysv4* matches it.
+ echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+ exit 0 ;;
+ i*86:OS/2:*:*)
+ # If we were able to find `uname', then EMX Unix compatibility
+ # is probably installed.
+ echo ${UNAME_MACHINE}-pc-os2-emx
+ exit 0 ;;
+ i*86:XTS-300:*:STOP)
+ echo ${UNAME_MACHINE}-unknown-stop
+ exit 0 ;;
+ i*86:atheos:*:*)
+ echo ${UNAME_MACHINE}-unknown-atheos
+ exit 0 ;;
+ i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
+ echo i386-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ i*86:*DOS:*:*)
+ echo ${UNAME_MACHINE}-pc-msdosdjgpp
+ exit 0 ;;
+ i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
+ UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
+ if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+ echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
+ else
+ echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
+ fi
+ exit 0 ;;
+ i*86:*:5:[78]*)
+ case `/bin/uname -X | grep "^Machine"` in
+ *486*) UNAME_MACHINE=i486 ;;
+ *Pentium) UNAME_MACHINE=i586 ;;
+ *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+ esac
+ echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+ exit 0 ;;
+ i*86:*:3.2:*)
+ if test -f /usr/options/cb.name; then
+ UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+ echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
+ elif /bin/uname -X 2>/dev/null >/dev/null ; then
+ UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
+ (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
+ (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
+ && UNAME_MACHINE=i586
+ (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
+ && UNAME_MACHINE=i686
+ (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
+ && UNAME_MACHINE=i686
+ echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+ else
+ echo ${UNAME_MACHINE}-pc-sysv32
+ fi
+ exit 0 ;;
+ pc:*:*:*)
+ # Left here for compatibility:
+ # uname -m prints for DJGPP always 'pc', but it prints nothing about
+ # the processor, so we play safe by assuming i386.
+ echo i386-pc-msdosdjgpp
+ exit 0 ;;
+ Intel:Mach:3*:*)
+ echo i386-pc-mach3
+ exit 0 ;;
+ paragon:*:*:*)
+ echo i860-intel-osf1
+ exit 0 ;;
+ i860:*:4.*:*) # i860-SVR4
+ if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+ echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+ else # Add other i860-SVR4 vendors below as they are discovered.
+ echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4
+ fi
+ exit 0 ;;
+ mini*:CTIX:SYS*5:*)
+ # "miniframe"
+ echo m68010-convergent-sysv
+ exit 0 ;;
+ mc68k:UNIX:SYSTEM5:3.51m)
+ echo m68k-convergent-sysv
+ exit 0 ;;
+ M680?0:D-NIX:5.3:*)
+ echo m68k-diab-dnix
+ exit 0 ;;
+ M68*:*:R3V[567]*:*)
+ test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;;
+ 3[34]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0)
+ OS_REL=''
+ test -r /etc/.relid \
+ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && echo i486-ncr-sysv4.3${OS_REL} && exit 0
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;;
+ 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && echo i486-ncr-sysv4 && exit 0 ;;
+ m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+ echo m68k-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ mc68030:UNIX_System_V:4.*:*)
+ echo m68k-atari-sysv4
+ exit 0 ;;
+ TSUNAMI:LynxOS:2.*:*)
+ echo sparc-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ rs6000:LynxOS:2.*:*)
+ echo rs6000-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)
+ echo powerpc-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ SM[BE]S:UNIX_SV:*:*)
+ echo mips-dde-sysv${UNAME_RELEASE}
+ exit 0 ;;
+ RM*:ReliantUNIX-*:*:*)
+ echo mips-sni-sysv4
+ exit 0 ;;
+ RM*:SINIX-*:*:*)
+ echo mips-sni-sysv4
+ exit 0 ;;
+ *:SINIX-*:*:*)
+ if uname -p 2>/dev/null >/dev/null ; then
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ echo ${UNAME_MACHINE}-sni-sysv4
+ else
+ echo ns32k-sni-sysv
+ fi
+ exit 0 ;;
+ PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+ # says <Richard.M.Bartel@ccMail.Census.GOV>
+ echo i586-unisys-sysv4
+ exit 0 ;;
+ *:UNIX_System_V:4*:FTX*)
+ # From Gerald Hewes <hewes@openmarket.com>.
+ # How about differentiating between stratus architectures? -djm
+ echo hppa1.1-stratus-sysv4
+ exit 0 ;;
+ *:*:*:FTX*)
+ # From seanf@swdc.stratus.com.
+ echo i860-stratus-sysv4
+ exit 0 ;;
+ *:VOS:*:*)
+ # From Paul.Green@stratus.com.
+ echo hppa1.1-stratus-vos
+ exit 0 ;;
+ mc68*:A/UX:*:*)
+ echo m68k-apple-aux${UNAME_RELEASE}
+ exit 0 ;;
+ news*:NEWS-OS:6*:*)
+ echo mips-sony-newsos6
+ exit 0 ;;
+ R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+ if [ -d /usr/nec ]; then
+ echo mips-nec-sysv${UNAME_RELEASE}
+ else
+ echo mips-unknown-sysv${UNAME_RELEASE}
+ fi
+ exit 0 ;;
+ BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
+ echo powerpc-be-beos
+ exit 0 ;;
+ BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only.
+ echo powerpc-apple-beos
+ exit 0 ;;
+ BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
+ echo i586-pc-beos
+ exit 0 ;;
+ SX-4:SUPER-UX:*:*)
+ echo sx4-nec-superux${UNAME_RELEASE}
+ exit 0 ;;
+ SX-5:SUPER-UX:*:*)
+ echo sx5-nec-superux${UNAME_RELEASE}
+ exit 0 ;;
+ SX-6:SUPER-UX:*:*)
+ echo sx6-nec-superux${UNAME_RELEASE}
+ exit 0 ;;
+ Power*:Rhapsody:*:*)
+ echo powerpc-apple-rhapsody${UNAME_RELEASE}
+ exit 0 ;;
+ *:Rhapsody:*:*)
+ echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+ exit 0 ;;
+ *:Darwin:*:*)
+ case `uname -p` in
+ *86) UNAME_PROCESSOR=i686 ;;
+ powerpc) UNAME_PROCESSOR=powerpc ;;
+ esac
+ echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
+ exit 0 ;;
+ *:procnto*:*:* | *:QNX:[0123456789]*:*)
+ UNAME_PROCESSOR=`uname -p`
+ if test "$UNAME_PROCESSOR" = "x86"; then
+ UNAME_PROCESSOR=i386
+ UNAME_MACHINE=pc
+ fi
+ echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
+ exit 0 ;;
+ *:QNX:*:4*)
+ echo i386-pc-qnx
+ exit 0 ;;
+ NSR-[DGKLNPTVW]:NONSTOP_KERNEL:*:*)
+ echo nsr-tandem-nsk${UNAME_RELEASE}
+ exit 0 ;;
+ *:NonStop-UX:*:*)
+ echo mips-compaq-nonstopux
+ exit 0 ;;
+ BS2000:POSIX*:*:*)
+ echo bs2000-siemens-sysv
+ exit 0 ;;
+ DS/*:UNIX_System_V:*:*)
+ echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
+ exit 0 ;;
+ *:Plan9:*:*)
+ # "uname -m" is not consistent, so use $cputype instead. 386
+ # is converted to i386 for consistency with other x86
+ # operating systems.
+ if test "$cputype" = "386"; then
+ UNAME_MACHINE=i386
+ else
+ UNAME_MACHINE="$cputype"
+ fi
+ echo ${UNAME_MACHINE}-unknown-plan9
+ exit 0 ;;
+ *:TOPS-10:*:*)
+ echo pdp10-unknown-tops10
+ exit 0 ;;
+ *:TENEX:*:*)
+ echo pdp10-unknown-tenex
+ exit 0 ;;
+ KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+ echo pdp10-dec-tops20
+ exit 0 ;;
+ XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+ echo pdp10-xkl-tops20
+ exit 0 ;;
+ *:TOPS-20:*:*)
+ echo pdp10-unknown-tops20
+ exit 0 ;;
+ *:ITS:*:*)
+ echo pdp10-unknown-its
+ exit 0 ;;
+esac
+
+#echo '(No uname command or uname output not recognized.)' 1>&2
+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+
+eval $set_cc_for_build
+cat >$dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+ /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,
+ I don't know.... */
+ printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+ printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+ "4"
+#else
+ ""
+#endif
+ ); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+ printf ("arm-acorn-riscix"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+ printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+ int version;
+ version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+ if (version < 4)
+ printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+ else
+ printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+ exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+ printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+ printf ("ns32k-encore-mach\n"); exit (0);
+#else
+ printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+ printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+ printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+ printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+ struct utsname un;
+
+ uname(&un);
+
+ if (strncmp(un.version, "V2", 2) == 0) {
+ printf ("i386-sequent-ptx2\n"); exit (0);
+ }
+ if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+ printf ("i386-sequent-ptx1\n"); exit (0);
+ }
+ printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+# if !defined (ultrix)
+# include <sys/param.h>
+# if defined (BSD)
+# if BSD == 43
+ printf ("vax-dec-bsd4.3\n"); exit (0);
+# else
+# if BSD == 199006
+ printf ("vax-dec-bsd4.3reno\n"); exit (0);
+# else
+ printf ("vax-dec-bsd\n"); exit (0);
+# endif
+# endif
+# else
+ printf ("vax-dec-bsd\n"); exit (0);
+# endif
+# else
+ printf ("vax-dec-ultrix\n"); exit (0);
+# endif
+#endif
+
+#if defined (alliant) && defined (i860)
+ printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+ exit (1);
+}
+EOF
+
+$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && $dummy && exit 0
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+ case `getsysinfo -f cpu_type` in
+ c1*)
+ echo c1-convex-bsd
+ exit 0 ;;
+ c2*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit 0 ;;
+ c34*)
+ echo c34-convex-bsd
+ exit 0 ;;
+ c38*)
+ echo c38-convex-bsd
+ exit 0 ;;
+ c4*)
+ echo c4-convex-bsd
+ exit 0 ;;
+ esac
+fi
+
+cat >&2 <<EOF
+$0: unable to guess system type
+
+This script, last modified $timestamp, has failed to recognize
+the operating system you are using. It is advised that you
+download the most up to date version of the config scripts from
+
+ ftp://ftp.gnu.org/pub/gnu/config/
+
+If the version you run ($0) is already up to date, please
+send the following data and any information you think might be
+pertinent to <config-patches@gnu.org> in order to provide the needed
+information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo = `(hostinfo) 2>/dev/null`
+/bin/universe = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = ${UNAME_MACHINE}
+UNAME_RELEASE = ${UNAME_RELEASE}
+UNAME_SYSTEM = ${UNAME_SYSTEM}
+UNAME_VERSION = ${UNAME_VERSION}
+EOF
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/config.sub b/config.sub
new file mode 100644
index 0000000..fe4f1ed
--- /dev/null
+++ b/config.sub
@@ -0,0 +1,1492 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+# 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+
+timestamp='2003-05-09'
+
+# This file is (in principle) common to ALL GNU software.
+# The presence of a machine in this file suggests that SOME GNU software
+# can handle that machine. It does not imply ALL GNU software can.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Please send patches to <config-patches@gnu.org>. Submit a context
+# diff and a properly formatted ChangeLog entry.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support. The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS
+ $0 [OPTION] ALIAS
+
+Canonicalize a configuration name.
+
+Operation modes:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit 0 ;;
+ --version | -v )
+ echo "$version" ; exit 0 ;;
+ --help | --h* | -h )
+ echo "$usage"; exit 0 ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help"
+ exit 1 ;;
+
+ *local*)
+ # First pass through any local machine types.
+ echo $1
+ exit 0;;
+
+ * )
+ break ;;
+ esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+ exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+ exit 1;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
+# Here we must recognize all the valid KERNEL-OS combinations.
+maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+case $maybe_os in
+ nto-qnx* | linux-gnu* | freebsd*-gnu* | netbsd*-gnu* | storm-chaos* | os2-emx* | rtmk-nova*)
+ os=-$maybe_os
+ basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+ ;;
+ *)
+ basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+ if [ $basic_machine != $1 ]
+ then os=`echo $1 | sed 's/.*-/-/'`
+ else os=; fi
+ ;;
+esac
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work. We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+ -sun*os*)
+ # Prevent following clause from handling this invalid input.
+ ;;
+ -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+ -apple | -axis)
+ os=
+ basic_machine=$1
+ ;;
+ -sim | -cisco | -oki | -wec | -winbond)
+ os=
+ basic_machine=$1
+ ;;
+ -scout)
+ ;;
+ -wrs)
+ os=-vxworks
+ basic_machine=$1
+ ;;
+ -chorusos*)
+ os=-chorusos
+ basic_machine=$1
+ ;;
+ -chorusrdb)
+ os=-chorusrdb
+ basic_machine=$1
+ ;;
+ -hiux*)
+ os=-hiuxwe2
+ ;;
+ -sco5)
+ os=-sco3.2v5
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco4)
+ os=-sco3.2v4
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2.[4-9]*)
+ os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2v[4-9]*)
+ # Don't forget version if it is 3.2v4 or newer.
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco*)
+ os=-sco3.2v2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -udk*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -isc)
+ os=-isc2.2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -clix*)
+ basic_machine=clipper-intergraph
+ ;;
+ -isc*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -lynx*)
+ os=-lynxos
+ ;;
+ -ptx*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+ ;;
+ -windowsnt*)
+ os=`echo $os | sed -e 's/windowsnt/winnt/'`
+ ;;
+ -psos*)
+ os=-psos
+ ;;
+ -mint | -mint[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+ # Recognize the basic CPU types without company name.
+ # Some are omitted here because they have special meanings below.
+ 1750a | 580 \
+ | a29k \
+ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
+ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
+ | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \
+ | clipper \
+ | d10v | d30v | dlx | dsp16xx \
+ | fr30 | frv \
+ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+ | i370 | i860 | i960 | ia64 \
+ | ip2k \
+ | m32r | m68000 | m68k | m88k | mcore \
+ | mips | mipsbe | mipseb | mipsel | mipsle \
+ | mips16 \
+ | mips64 | mips64el \
+ | mips64vr | mips64vrel \
+ | mips64orion | mips64orionel \
+ | mips64vr4100 | mips64vr4100el \
+ | mips64vr4300 | mips64vr4300el \
+ | mips64vr5000 | mips64vr5000el \
+ | mipsisa32 | mipsisa32el \
+ | mipsisa32r2 | mipsisa32r2el \
+ | mipsisa64 | mipsisa64el \
+ | mipsisa64sb1 | mipsisa64sb1el \
+ | mipsisa64sr71k | mipsisa64sr71kel \
+ | mipstx39 | mipstx39el \
+ | mn10200 | mn10300 \
+ | msp430 \
+ | ns16k | ns32k \
+ | openrisc | or32 \
+ | pdp10 | pdp11 | pj | pjl \
+ | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
+ | pyramid \
+ | sh | sh[1234] | sh[23]e | sh[34]eb | shbe | shle | sh[1234]le | sh3ele \
+ | sh64 | sh64le \
+ | sparc | sparc64 | sparc86x | sparclet | sparclite | sparcv9 | sparcv9b \
+ | strongarm \
+ | tahoe | thumb | tic80 | tron \
+ | v850 | v850e \
+ | we32k \
+ | x86 | xscale | xstormy16 | xtensa \
+ | z8k)
+ basic_machine=$basic_machine-unknown
+ ;;
+ m6811 | m68hc11 | m6812 | m68hc12)
+ # Motorola 68HC11/12.
+ basic_machine=$basic_machine-unknown
+ os=-none
+ ;;
+ m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
+ ;;
+
+ # We use `pc' rather than `unknown'
+ # because (1) that's what they normally are, and
+ # (2) the word "unknown" tends to confuse beginning users.
+ i*86 | x86_64)
+ basic_machine=$basic_machine-pc
+ ;;
+ # Object if more than one company name word.
+ *-*-*)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+ # Recognize the basic CPU types with company name.
+ 580-* \
+ | a29k-* \
+ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
+ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
+ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
+ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \
+ | avr-* \
+ | bs2000-* \
+ | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \
+ | clipper-* | cydra-* \
+ | d10v-* | d30v-* | dlx-* \
+ | elxsi-* \
+ | f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \
+ | h8300-* | h8500-* \
+ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
+ | i*86-* | i860-* | i960-* | ia64-* \
+ | ip2k-* \
+ | m32r-* \
+ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
+ | m88110-* | m88k-* | mcore-* \
+ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
+ | mips16-* \
+ | mips64-* | mips64el-* \
+ | mips64vr-* | mips64vrel-* \
+ | mips64orion-* | mips64orionel-* \
+ | mips64vr4100-* | mips64vr4100el-* \
+ | mips64vr4300-* | mips64vr4300el-* \
+ | mips64vr5000-* | mips64vr5000el-* \
+ | mipsisa32-* | mipsisa32el-* \
+ | mipsisa32r2-* | mipsisa32r2el-* \
+ | mipsisa64-* | mipsisa64el-* \
+ | mipsisa64sb1-* | mipsisa64sb1el-* \
+ | mipsisa64sr71k-* | mipsisa64sr71kel-* \
+ | mipstx39-* | mipstx39el-* \
+ | msp430-* \
+ | none-* | np1-* | nv1-* | ns16k-* | ns32k-* \
+ | orion-* \
+ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
+ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
+ | pyramid-* \
+ | romp-* | rs6000-* \
+ | sh-* | sh[1234]-* | sh[23]e-* | sh[34]eb-* | shbe-* \
+ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
+ | sparc-* | sparc64-* | sparc86x-* | sparclet-* | sparclite-* \
+ | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \
+ | tahoe-* | thumb-* \
+ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
+ | tron-* \
+ | v850-* | v850e-* | vax-* \
+ | we32k-* \
+ | x86-* | x86_64-* | xps100-* | xscale-* | xstormy16-* \
+ | xtensa-* \
+ | ymp-* \
+ | z8k-*)
+ ;;
+ # Recognize the various machine names and aliases which stand
+ # for a CPU type and a company and sometimes even an OS.
+ 386bsd)
+ basic_machine=i386-unknown
+ os=-bsd
+ ;;
+ 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+ basic_machine=m68000-att
+ ;;
+ 3b*)
+ basic_machine=we32k-att
+ ;;
+ a29khif)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ adobe68k)
+ basic_machine=m68010-adobe
+ os=-scout
+ ;;
+ alliant | fx80)
+ basic_machine=fx80-alliant
+ ;;
+ altos | altos3068)
+ basic_machine=m68k-altos
+ ;;
+ am29k)
+ basic_machine=a29k-none
+ os=-bsd
+ ;;
+ amd64)
+ basic_machine=x86_64-pc
+ ;;
+ amdahl)
+ basic_machine=580-amdahl
+ os=-sysv
+ ;;
+ amiga | amiga-*)
+ basic_machine=m68k-unknown
+ ;;
+ amigaos | amigados)
+ basic_machine=m68k-unknown
+ os=-amigaos
+ ;;
+ amigaunix | amix)
+ basic_machine=m68k-unknown
+ os=-sysv4
+ ;;
+ apollo68)
+ basic_machine=m68k-apollo
+ os=-sysv
+ ;;
+ apollo68bsd)
+ basic_machine=m68k-apollo
+ os=-bsd
+ ;;
+ aux)
+ basic_machine=m68k-apple
+ os=-aux
+ ;;
+ balance)
+ basic_machine=ns32k-sequent
+ os=-dynix
+ ;;
+ c90)
+ basic_machine=c90-cray
+ os=-unicos
+ ;;
+ convex-c1)
+ basic_machine=c1-convex
+ os=-bsd
+ ;;
+ convex-c2)
+ basic_machine=c2-convex
+ os=-bsd
+ ;;
+ convex-c32)
+ basic_machine=c32-convex
+ os=-bsd
+ ;;
+ convex-c34)
+ basic_machine=c34-convex
+ os=-bsd
+ ;;
+ convex-c38)
+ basic_machine=c38-convex
+ os=-bsd
+ ;;
+ cray | j90)
+ basic_machine=j90-cray
+ os=-unicos
+ ;;
+ crds | unos)
+ basic_machine=m68k-crds
+ ;;
+ cris | cris-* | etrax*)
+ basic_machine=cris-axis
+ ;;
+ da30 | da30-*)
+ basic_machine=m68k-da30
+ ;;
+ decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+ basic_machine=mips-dec
+ ;;
+ decsystem10* | dec10*)
+ basic_machine=pdp10-dec
+ os=-tops10
+ ;;
+ decsystem20* | dec20*)
+ basic_machine=pdp10-dec
+ os=-tops20
+ ;;
+ delta | 3300 | motorola-3300 | motorola-delta \
+ | 3300-motorola | delta-motorola)
+ basic_machine=m68k-motorola
+ ;;
+ delta88)
+ basic_machine=m88k-motorola
+ os=-sysv3
+ ;;
+ dpx20 | dpx20-*)
+ basic_machine=rs6000-bull
+ os=-bosx
+ ;;
+ dpx2* | dpx2*-bull)
+ basic_machine=m68k-bull
+ os=-sysv3
+ ;;
+ ebmon29k)
+ basic_machine=a29k-amd
+ os=-ebmon
+ ;;
+ elxsi)
+ basic_machine=elxsi-elxsi
+ os=-bsd
+ ;;
+ encore | umax | mmax)
+ basic_machine=ns32k-encore
+ ;;
+ es1800 | OSE68k | ose68k | ose | OSE)
+ basic_machine=m68k-ericsson
+ os=-ose
+ ;;
+ fx2800)
+ basic_machine=i860-alliant
+ ;;
+ genix)
+ basic_machine=ns32k-ns
+ ;;
+ gmicro)
+ basic_machine=tron-gmicro
+ os=-sysv
+ ;;
+ go32)
+ basic_machine=i386-pc
+ os=-go32
+ ;;
+ h3050r* | hiux*)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ h8300hms)
+ basic_machine=h8300-hitachi
+ os=-hms
+ ;;
+ h8300xray)
+ basic_machine=h8300-hitachi
+ os=-xray
+ ;;
+ h8500hms)
+ basic_machine=h8500-hitachi
+ os=-hms
+ ;;
+ harris)
+ basic_machine=m88k-harris
+ os=-sysv3
+ ;;
+ hp300-*)
+ basic_machine=m68k-hp
+ ;;
+ hp300bsd)
+ basic_machine=m68k-hp
+ os=-bsd
+ ;;
+ hp300hpux)
+ basic_machine=m68k-hp
+ os=-hpux
+ ;;
+ hp3k9[0-9][0-9] | hp9[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k2[0-9][0-9] | hp9k31[0-9])
+ basic_machine=m68000-hp
+ ;;
+ hp9k3[2-9][0-9])
+ basic_machine=m68k-hp
+ ;;
+ hp9k6[0-9][0-9] | hp6[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k7[0-79][0-9] | hp7[0-79][0-9])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k78[0-9] | hp78[0-9])
+ # FIXME: really hppa2.0-hp
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+ # FIXME: really hppa2.0-hp
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][13679] | hp8[0-9][13679])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][0-9] | hp8[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hppa-next)
+ os=-nextstep3
+ ;;
+ hppaosf)
+ basic_machine=hppa1.1-hp
+ os=-osf
+ ;;
+ hppro)
+ basic_machine=hppa1.1-hp
+ os=-proelf
+ ;;
+ i370-ibm* | ibm*)
+ basic_machine=i370-ibm
+ ;;
+# I'm not sure what "Sysv32" means. Should this be sysv3.2?
+ i*86v32)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv32
+ ;;
+ i*86v4*)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv4
+ ;;
+ i*86v)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv
+ ;;
+ i*86sol2)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-solaris2
+ ;;
+ i386mach)
+ basic_machine=i386-mach
+ os=-mach
+ ;;
+ i386-vsta | vsta)
+ basic_machine=i386-unknown
+ os=-vsta
+ ;;
+ iris | iris4d)
+ basic_machine=mips-sgi
+ case $os in
+ -irix*)
+ ;;
+ *)
+ os=-irix4
+ ;;
+ esac
+ ;;
+ isi68 | isi)
+ basic_machine=m68k-isi
+ os=-sysv
+ ;;
+ m88k-omron*)
+ basic_machine=m88k-omron
+ ;;
+ magnum | m3230)
+ basic_machine=mips-mips
+ os=-sysv
+ ;;
+ merlin)
+ basic_machine=ns32k-utek
+ os=-sysv
+ ;;
+ mingw32)
+ basic_machine=i386-pc
+ os=-mingw32
+ ;;
+ miniframe)
+ basic_machine=m68000-convergent
+ ;;
+ *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+ mips3*-*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+ ;;
+ mips3*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+ ;;
+ mmix*)
+ basic_machine=mmix-knuth
+ os=-mmixware
+ ;;
+ monitor)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ morphos)
+ basic_machine=powerpc-unknown
+ os=-morphos
+ ;;
+ msdos)
+ basic_machine=i386-pc
+ os=-msdos
+ ;;
+ mvs)
+ basic_machine=i370-ibm
+ os=-mvs
+ ;;
+ ncr3000)
+ basic_machine=i486-ncr
+ os=-sysv4
+ ;;
+ netbsd386)
+ basic_machine=i386-unknown
+ os=-netbsd
+ ;;
+ netwinder)
+ basic_machine=armv4l-rebel
+ os=-linux
+ ;;
+ news | news700 | news800 | news900)
+ basic_machine=m68k-sony
+ os=-newsos
+ ;;
+ news1000)
+ basic_machine=m68030-sony
+ os=-newsos
+ ;;
+ news-3600 | risc-news)
+ basic_machine=mips-sony
+ os=-newsos
+ ;;
+ necv70)
+ basic_machine=v70-nec
+ os=-sysv
+ ;;
+ next | m*-next )
+ basic_machine=m68k-next
+ case $os in
+ -nextstep* )
+ ;;
+ -ns2*)
+ os=-nextstep2
+ ;;
+ *)
+ os=-nextstep3
+ ;;
+ esac
+ ;;
+ nh3000)
+ basic_machine=m68k-harris
+ os=-cxux
+ ;;
+ nh[45]000)
+ basic_machine=m88k-harris
+ os=-cxux
+ ;;
+ nindy960)
+ basic_machine=i960-intel
+ os=-nindy
+ ;;
+ mon960)
+ basic_machine=i960-intel
+ os=-mon960
+ ;;
+ nonstopux)
+ basic_machine=mips-compaq
+ os=-nonstopux
+ ;;
+ np1)
+ basic_machine=np1-gould
+ ;;
+ nv1)
+ basic_machine=nv1-cray
+ os=-unicosmp
+ ;;
+ nsr-tandem)
+ basic_machine=nsr-tandem
+ ;;
+ op50n-* | op60c-*)
+ basic_machine=hppa1.1-oki
+ os=-proelf
+ ;;
+ or32 | or32-*)
+ basic_machine=or32-unknown
+ os=-coff
+ ;;
+ OSE68000 | ose68000)
+ basic_machine=m68000-ericsson
+ os=-ose
+ ;;
+ os68k)
+ basic_machine=m68k-none
+ os=-os68k
+ ;;
+ pa-hitachi)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ paragon)
+ basic_machine=i860-intel
+ os=-osf
+ ;;
+ pbd)
+ basic_machine=sparc-tti
+ ;;
+ pbb)
+ basic_machine=m68k-tti
+ ;;
+ pc532 | pc532-*)
+ basic_machine=ns32k-pc532
+ ;;
+ pentium | p5 | k5 | k6 | nexgen | viac3)
+ basic_machine=i586-pc
+ ;;
+ pentiumpro | p6 | 6x86 | athlon | athlon_*)
+ basic_machine=i686-pc
+ ;;
+ pentiumii | pentium2)
+ basic_machine=i686-pc
+ ;;
+ pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
+ basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumpro-* | p6-* | 6x86-* | athlon-*)
+ basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumii-* | pentium2-*)
+ basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pn)
+ basic_machine=pn-gould
+ ;;
+ power) basic_machine=power-ibm
+ ;;
+ ppc) basic_machine=powerpc-unknown
+ ;;
+ ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppcle | powerpclittle | ppc-le | powerpc-little)
+ basic_machine=powerpcle-unknown
+ ;;
+ ppcle-* | powerpclittle-*)
+ basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppc64) basic_machine=powerpc64-unknown
+ ;;
+ ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppc64le | powerpc64little | ppc64-le | powerpc64-little)
+ basic_machine=powerpc64le-unknown
+ ;;
+ ppc64le-* | powerpc64little-*)
+ basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ps2)
+ basic_machine=i386-ibm
+ ;;
+ pw32)
+ basic_machine=i586-unknown
+ os=-pw32
+ ;;
+ rom68k)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ rm[46]00)
+ basic_machine=mips-siemens
+ ;;
+ rtpc | rtpc-*)
+ basic_machine=romp-ibm
+ ;;
+ s390 | s390-*)
+ basic_machine=s390-ibm
+ ;;
+ s390x | s390x-*)
+ basic_machine=s390x-ibm
+ ;;
+ sa29200)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ sb1)
+ basic_machine=mipsisa64sb1-unknown
+ ;;
+ sb1el)
+ basic_machine=mipsisa64sb1el-unknown
+ ;;
+ sequent)
+ basic_machine=i386-sequent
+ ;;
+ sh)
+ basic_machine=sh-hitachi
+ os=-hms
+ ;;
+ sparclite-wrs | simso-wrs)
+ basic_machine=sparclite-wrs
+ os=-vxworks
+ ;;
+ sps7)
+ basic_machine=m68k-bull
+ os=-sysv2
+ ;;
+ spur)
+ basic_machine=spur-unknown
+ ;;
+ st2000)
+ basic_machine=m68k-tandem
+ ;;
+ stratus)
+ basic_machine=i860-stratus
+ os=-sysv4
+ ;;
+ sun2)
+ basic_machine=m68000-sun
+ ;;
+ sun2os3)
+ basic_machine=m68000-sun
+ os=-sunos3
+ ;;
+ sun2os4)
+ basic_machine=m68000-sun
+ os=-sunos4
+ ;;
+ sun3os3)
+ basic_machine=m68k-sun
+ os=-sunos3
+ ;;
+ sun3os4)
+ basic_machine=m68k-sun
+ os=-sunos4
+ ;;
+ sun4os3)
+ basic_machine=sparc-sun
+ os=-sunos3
+ ;;
+ sun4os4)
+ basic_machine=sparc-sun
+ os=-sunos4
+ ;;
+ sun4sol2)
+ basic_machine=sparc-sun
+ os=-solaris2
+ ;;
+ sun3 | sun3-*)
+ basic_machine=m68k-sun
+ ;;
+ sun4)
+ basic_machine=sparc-sun
+ ;;
+ sun386 | sun386i | roadrunner)
+ basic_machine=i386-sun
+ ;;
+ sv1)
+ basic_machine=sv1-cray
+ os=-unicos
+ ;;
+ symmetry)
+ basic_machine=i386-sequent
+ os=-dynix
+ ;;
+ t3e)
+ basic_machine=alphaev5-cray
+ os=-unicos
+ ;;
+ t90)
+ basic_machine=t90-cray
+ os=-unicos
+ ;;
+ tic4x | c4x*)
+ basic_machine=tic4x-unknown
+ os=-coff
+ ;;
+ tic54x | c54x*)
+ basic_machine=tic54x-unknown
+ os=-coff
+ ;;
+ tic55x | c55x*)
+ basic_machine=tic55x-unknown
+ os=-coff
+ ;;
+ tic6x | c6x*)
+ basic_machine=tic6x-unknown
+ os=-coff
+ ;;
+ tx39)
+ basic_machine=mipstx39-unknown
+ ;;
+ tx39el)
+ basic_machine=mipstx39el-unknown
+ ;;
+ toad1)
+ basic_machine=pdp10-xkl
+ os=-tops20
+ ;;
+ tower | tower-32)
+ basic_machine=m68k-ncr
+ ;;
+ udi29k)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ ultra3)
+ basic_machine=a29k-nyu
+ os=-sym1
+ ;;
+ v810 | necv810)
+ basic_machine=v810-nec
+ os=-none
+ ;;
+ vaxv)
+ basic_machine=vax-dec
+ os=-sysv
+ ;;
+ vms)
+ basic_machine=vax-dec
+ os=-vms
+ ;;
+ vpp*|vx|vx-*)
+ basic_machine=f301-fujitsu
+ ;;
+ vxworks960)
+ basic_machine=i960-wrs
+ os=-vxworks
+ ;;
+ vxworks68)
+ basic_machine=m68k-wrs
+ os=-vxworks
+ ;;
+ vxworks29k)
+ basic_machine=a29k-wrs
+ os=-vxworks
+ ;;
+ w65*)
+ basic_machine=w65-wdc
+ os=-none
+ ;;
+ w89k-*)
+ basic_machine=hppa1.1-winbond
+ os=-proelf
+ ;;
+ xps | xps100)
+ basic_machine=xps100-honeywell
+ ;;
+ ymp)
+ basic_machine=ymp-cray
+ os=-unicos
+ ;;
+ z8k-*-coff)
+ basic_machine=z8k-unknown
+ os=-sim
+ ;;
+ none)
+ basic_machine=none-none
+ os=-none
+ ;;
+
+# Here we handle the default manufacturer of certain CPU types. It is in
+# some cases the only manufacturer, in others, it is the most popular.
+ w89k)
+ basic_machine=hppa1.1-winbond
+ ;;
+ op50n)
+ basic_machine=hppa1.1-oki
+ ;;
+ op60c)
+ basic_machine=hppa1.1-oki
+ ;;
+ romp)
+ basic_machine=romp-ibm
+ ;;
+ rs6000)
+ basic_machine=rs6000-ibm
+ ;;
+ vax)
+ basic_machine=vax-dec
+ ;;
+ pdp10)
+ # there are many clones, so DEC is not a safe bet
+ basic_machine=pdp10-unknown
+ ;;
+ pdp11)
+ basic_machine=pdp11-dec
+ ;;
+ we32k)
+ basic_machine=we32k-att
+ ;;
+ sh3 | sh4 | sh[34]eb | sh[1234]le | sh[23]ele)
+ basic_machine=sh-unknown
+ ;;
+ sh64)
+ basic_machine=sh64-unknown
+ ;;
+ sparc | sparcv9 | sparcv9b)
+ basic_machine=sparc-sun
+ ;;
+ cydra)
+ basic_machine=cydra-cydrome
+ ;;
+ orion)
+ basic_machine=orion-highlevel
+ ;;
+ orion105)
+ basic_machine=clipper-highlevel
+ ;;
+ mac | mpw | mac-mpw)
+ basic_machine=m68k-apple
+ ;;
+ pmac | pmac-mpw)
+ basic_machine=powerpc-apple
+ ;;
+ *-unknown)
+ # Make sure to match an already-canonicalized machine name.
+ ;;
+ *)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+ *-digital*)
+ basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+ ;;
+ *-commodore*)
+ basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+ ;;
+ *)
+ ;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+ # First match some system type aliases
+ # that might get confused with valid system types.
+ # -solaris* is a basic system type, with this one exception.
+ -solaris1 | -solaris1.*)
+ os=`echo $os | sed -e 's|solaris1|sunos4|'`
+ ;;
+ -solaris)
+ os=-solaris2
+ ;;
+ -svr4*)
+ os=-sysv4
+ ;;
+ -unixware*)
+ os=-sysv4.2uw
+ ;;
+ -gnu/linux*)
+ os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+ ;;
+ # First accept the basic system types.
+ # The portable systems comes first.
+ # Each alternative MUST END IN A *, to match a version number.
+ # -sysv* is not here because it comes later, after sysvr4.
+ -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+ | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
+ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
+ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+ | -aos* \
+ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+ | -hiux* | -386bsd* | -netbsd* | -openbsd* | -freebsd* | -riscix* \
+ | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+ | -chorusos* | -chorusrdb* \
+ | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+ | -mingw32* | -linux-gnu* | -uxpv* | -beos* | -mpeix* | -udk* \
+ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
+ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
+ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
+ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
+ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
+ | -powermax* | -dnix*)
+ # Remember, each alternative MUST END IN *, to match a version number.
+ ;;
+ -qnx*)
+ case $basic_machine in
+ x86-* | i*86-*)
+ ;;
+ *)
+ os=-nto$os
+ ;;
+ esac
+ ;;
+ -nto-qnx*)
+ ;;
+ -nto*)
+ os=`echo $os | sed -e 's|nto|nto-qnx|'`
+ ;;
+ -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
+ | -windows* | -osx | -abug | -netware* | -os9* | -beos* \
+ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
+ ;;
+ -mac*)
+ os=`echo $os | sed -e 's|mac|macos|'`
+ ;;
+ -linux*)
+ os=`echo $os | sed -e 's|linux|linux-gnu|'`
+ ;;
+ -sunos5*)
+ os=`echo $os | sed -e 's|sunos5|solaris2|'`
+ ;;
+ -sunos6*)
+ os=`echo $os | sed -e 's|sunos6|solaris3|'`
+ ;;
+ -opened*)
+ os=-openedition
+ ;;
+ -wince*)
+ os=-wince
+ ;;
+ -osfrose*)
+ os=-osfrose
+ ;;
+ -osf*)
+ os=-osf
+ ;;
+ -utek*)
+ os=-bsd
+ ;;
+ -dynix*)
+ os=-bsd
+ ;;
+ -acis*)
+ os=-aos
+ ;;
+ -atheos*)
+ os=-atheos
+ ;;
+ -386bsd)
+ os=-bsd
+ ;;
+ -ctix* | -uts*)
+ os=-sysv
+ ;;
+ -nova*)
+ os=-rtmk-nova
+ ;;
+ -ns2 )
+ os=-nextstep2
+ ;;
+ -nsk*)
+ os=-nsk
+ ;;
+ # Preserve the version number of sinix5.
+ -sinix5.*)
+ os=`echo $os | sed -e 's|sinix|sysv|'`
+ ;;
+ -sinix*)
+ os=-sysv4
+ ;;
+ -triton*)
+ os=-sysv3
+ ;;
+ -oss*)
+ os=-sysv3
+ ;;
+ -svr4)
+ os=-sysv4
+ ;;
+ -svr3)
+ os=-sysv3
+ ;;
+ -sysvr4)
+ os=-sysv4
+ ;;
+ # This must come after -sysvr4.
+ -sysv*)
+ ;;
+ -ose*)
+ os=-ose
+ ;;
+ -es1800*)
+ os=-ose
+ ;;
+ -xenix)
+ os=-xenix
+ ;;
+ -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+ os=-mint
+ ;;
+ -aros*)
+ os=-aros
+ ;;
+ -kaos*)
+ os=-kaos
+ ;;
+ -none)
+ ;;
+ *)
+ # Get rid of the `-' at the beginning of $os.
+ os=`echo $os | sed 's/[^-]*-//'`
+ echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system. Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+ *-acorn)
+ os=-riscix1.2
+ ;;
+ arm*-rebel)
+ os=-linux
+ ;;
+ arm*-semi)
+ os=-aout
+ ;;
+ # This must come before the *-dec entry.
+ pdp10-*)
+ os=-tops20
+ ;;
+ pdp11-*)
+ os=-none
+ ;;
+ *-dec | vax-*)
+ os=-ultrix4.2
+ ;;
+ m68*-apollo)
+ os=-domain
+ ;;
+ i386-sun)
+ os=-sunos4.0.2
+ ;;
+ m68000-sun)
+ os=-sunos3
+ # This also exists in the configure program, but was not the
+ # default.
+ # os=-sunos4
+ ;;
+ m68*-cisco)
+ os=-aout
+ ;;
+ mips*-cisco)
+ os=-elf
+ ;;
+ mips*-*)
+ os=-elf
+ ;;
+ or32-*)
+ os=-coff
+ ;;
+ *-tti) # must be before sparc entry or we get the wrong os.
+ os=-sysv3
+ ;;
+ sparc-* | *-sun)
+ os=-sunos4.1.1
+ ;;
+ *-be)
+ os=-beos
+ ;;
+ *-ibm)
+ os=-aix
+ ;;
+ *-wec)
+ os=-proelf
+ ;;
+ *-winbond)
+ os=-proelf
+ ;;
+ *-oki)
+ os=-proelf
+ ;;
+ *-hp)
+ os=-hpux
+ ;;
+ *-hitachi)
+ os=-hiux
+ ;;
+ i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+ os=-sysv
+ ;;
+ *-cbm)
+ os=-amigaos
+ ;;
+ *-dg)
+ os=-dgux
+ ;;
+ *-dolphin)
+ os=-sysv3
+ ;;
+ m68k-ccur)
+ os=-rtu
+ ;;
+ m88k-omron*)
+ os=-luna
+ ;;
+ *-next )
+ os=-nextstep
+ ;;
+ *-sequent)
+ os=-ptx
+ ;;
+ *-crds)
+ os=-unos
+ ;;
+ *-ns)
+ os=-genix
+ ;;
+ i370-*)
+ os=-mvs
+ ;;
+ *-next)
+ os=-nextstep3
+ ;;
+ *-gould)
+ os=-sysv
+ ;;
+ *-highlevel)
+ os=-bsd
+ ;;
+ *-encore)
+ os=-bsd
+ ;;
+ *-sgi)
+ os=-irix
+ ;;
+ *-siemens)
+ os=-sysv4
+ ;;
+ *-masscomp)
+ os=-rtu
+ ;;
+ f30[01]-fujitsu | f700-fujitsu)
+ os=-uxpv
+ ;;
+ *-rom68k)
+ os=-coff
+ ;;
+ *-*bug)
+ os=-coff
+ ;;
+ *-apple)
+ os=-macos
+ ;;
+ *-atari*)
+ os=-mint
+ ;;
+ *)
+ os=-none
+ ;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer. We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+ *-unknown)
+ case $os in
+ -riscix*)
+ vendor=acorn
+ ;;
+ -sunos*)
+ vendor=sun
+ ;;
+ -aix*)
+ vendor=ibm
+ ;;
+ -beos*)
+ vendor=be
+ ;;
+ -hpux*)
+ vendor=hp
+ ;;
+ -mpeix*)
+ vendor=hp
+ ;;
+ -hiux*)
+ vendor=hitachi
+ ;;
+ -unos*)
+ vendor=crds
+ ;;
+ -dgux*)
+ vendor=dg
+ ;;
+ -luna*)
+ vendor=omron
+ ;;
+ -genix*)
+ vendor=ns
+ ;;
+ -mvs* | -opened*)
+ vendor=ibm
+ ;;
+ -ptx*)
+ vendor=sequent
+ ;;
+ -vxsim* | -vxworks* | -windiss*)
+ vendor=wrs
+ ;;
+ -aux*)
+ vendor=apple
+ ;;
+ -hms*)
+ vendor=hitachi
+ ;;
+ -mpw* | -macos*)
+ vendor=apple
+ ;;
+ -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+ vendor=atari
+ ;;
+ -vos*)
+ vendor=stratus
+ ;;
+ esac
+ basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+ ;;
+esac
+
+echo $basic_machine$os
+exit 0
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/configure.in b/configure.in
new file mode 100644
index 0000000..e860831
--- /dev/null
+++ b/configure.in
@@ -0,0 +1,617 @@
+# -*- Autoconf -*-
+# Process this file with autoconf and autoheader to produce a configure script.
+
+# This Autoconf file was cobbled from various locations. In particular, a bunch
+# of the platform checks have been taken straight from OpenSSH's configure.ac
+# Huge thanks to them for dealing with the horrible platform-specifics :)
+
+AC_PREREQ(2.50)
+AC_INIT(buffer.c)
+
+OLDCFLAGS=$CFLAGS
+# Checks for programs.
+AC_PROG_CC
+AC_PROG_MAKE_SET
+
+if test -z "$LD" ; then
+ LD=$CC
+fi
+AC_SUBST(LD)
+
+if test -z "$OLDCFLAGS" && test "$GCC" = "yes"; then
+ AC_MSG_RESULT(No \$CFLAGS set... using "-Os -W -Wall" for GCC)
+ CFLAGS="-Os -W -Wall"
+fi
+
+# Host specific options
+# this isn't a definitive list of hosts, they are just added as required
+AC_CANONICAL_HOST
+
+case "$host" in
+
+*-*-linux*)
+ no_ptmx_check=1
+ ;;
+
+*-*-solaris*)
+ CFLAGS="$CFLAGS -I/usr/local/include"
+ LDFLAGS="$LDFLAGS -L/usr/local/lib -R/usr/local/lib"
+ conf_lastlog_location="/var/adm/lastlog"
+ AC_MSG_CHECKING(for obsolete utmp and wtmp in solaris2.x)
+ sol2ver=`echo "$host"| sed -e 's/.*[[0-9]]\.//'`
+ if test "$sol2ver" -ge 8; then
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(DISABLE_UTMP,,Disable utmp)
+ AC_DEFINE(DISABLE_WTMP,,Disable wtmp)
+ else
+ AC_MSG_RESULT(no)
+ fi
+ AC_CHECK_LIB(socket, socket, LIBS="$LIBS -lsocket")
+ AC_CHECK_LIB(nsl, yp_match, LIBS="$LIBS -lnsl")
+ ;;
+
+*-*-aix*)
+ AC_DEFINE(AIX,,Using AIX)
+ # OpenSSH thinks it's broken. If it isn't, let me know.
+ AC_DEFINE(BROKEN_GETADDRINFO,,Broken getaddrinfo)
+ ;;
+
+*-*-hpux*)
+ LIBS="$LIBS -lsec"
+ # It's probably broken.
+ AC_DEFINE(BROKEN_GETADDRINFO,,Broken getaddrinfo)
+ ;;
+*-dec-osf*)
+ AC_DEFINE(BROKEN_GETADDRINFO,,Broken getaddrinfo)
+ ;;
+esac
+
+AC_CHECK_TOOL(AR, ar, :)
+AC_CHECK_TOOL(RANLIB, ranlib, :)
+AC_CHECK_TOOL(STRIP, strip, :)
+AC_CHECK_TOOL(INSTALL, install, :)
+
+dnl Can't use login() or logout() with uclibc
+AC_CHECK_DECL(__UCLIBC__,
+ [
+ no_loginfunc_check=1
+ AC_MSG_RESULT(Using uClibc - login() and logout() probably don't work, so we won't use them.)
+ ],,,)
+
+# Checks for libraries.
+AC_CHECK_LIB(crypt, crypt, LIBS="$LIBS -lcrypt")
+
+# Check if zlib is needed
+AC_ARG_WITH(zlib,
+ [ --with-zlib=PATH Use zlib 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(zlib,
+ [ --disable-zlib Don't include zlib support],
+ [
+ if test "x$enableval" = "xno"; then
+ AC_DEFINE(DISABLE_ZLIB,, Use zlib)
+ AC_MSG_RESULT(Disabling zlib)
+ else
+ AC_CHECK_LIB(z, deflate, , AC_MSG_ERROR([*** zlib missing - install first or check config.log ***]))
+ AC_MSG_RESULT(Enabling zlib)
+ fi
+ ],
+ [
+ # if not disabled, check for zlib
+ AC_CHECK_LIB(z, deflate, , AC_MSG_ERROR([*** zlib missing - install first or check config.log ***]))
+ AC_MSG_RESULT(Enabling 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],
+ [
+ if test "x$enableval" = "xno"; then
+ AC_MSG_RESULT(Not using openpty)
+ else
+ AC_MSG_RESULT(Using openpty if available)
+ AC_SEARCH_LIBS(openpty, util, [AC_DEFINE(HAVE_OPENPTY,,Have openpty() function)])
+ fi
+ ],
+ [
+ AC_MSG_RESULT(Using openpty if available)
+ AC_SEARCH_LIBS(openpty, util, [AC_DEFINE(HAVE_OPENPTY)])
+ ]
+)
+
+
+AC_ARG_ENABLE(syslog,
+ [ --disable-syslog Don't include syslog support],
+ [
+ if test "x$enableval" = "xno"; then
+ AC_DEFINE(DISABLE_SYSLOG,, Using syslog)
+ AC_MSG_RESULT(Disabling syslog)
+ else
+ AC_MSG_RESULT(Enabling syslog)
+ fi
+ ],
+ [
+ AC_MSG_RESULT(Enabling syslog)
+ ]
+)
+
+AC_ARG_ENABLE(shadow,
+ [ --disable-shadow Don't use shadow passwords (if available)],
+ [
+ if test "x$enableval" = "xno"; then
+ AC_MSG_RESULT(Not using shadow passwords)
+ else
+ AC_CHECK_HEADERS([shadow.h])
+ AC_MSG_RESULT(Using shadow passwords if available)
+ fi
+ ],
+ [
+ AC_CHECK_HEADERS([shadow.h])
+ AC_MSG_RESULT(Using shadow passwords if available)
+ ]
+)
+
+
+# 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 security/pam_appl.h pam/pam_appl.h netinet/in_systm.h])
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_C_CONST
+AC_TYPE_UID_T
+AC_TYPE_MODE_T
+AC_TYPE_PID_T
+AC_TYPE_SIZE_T
+AC_HEADER_TIME
+
+AC_CHECK_TYPES([uint16_t, u_int16_t, struct sockaddr_storage])
+AC_CHECK_TYPE([socklen_t], ,[
+ AC_MSG_CHECKING([for socklen_t equivalent])
+ AC_CACHE_VAL([curl_cv_socklen_t_equiv],
+ [
+ # Systems have either "struct sockaddr *" or
+ # "void *" as the second argument to getpeername
+ curl_cv_socklen_t_equiv=
+ for arg2 in "struct sockaddr" void; do
+ for t in int size_t unsigned long "unsigned long"; do
+ AC_TRY_COMPILE([
+ #include <sys/types.h>
+ #include <sys/socket.h>
+
+ int getpeername (int, $arg2 *, $t *);
+ ],[
+ $t len;
+ getpeername(0,0,&len);
+ ],[
+ curl_cv_socklen_t_equiv="$t"
+ break
+ ])
+ done
+ done
+
+ if test "x$curl_cv_socklen_t_equiv" = x; then
+ AC_MSG_ERROR([Cannot find a type to use in place of socklen_t])
+ fi
+ ])
+ AC_MSG_RESULT($curl_cv_socklen_t_equiv)
+ AC_DEFINE_UNQUOTED(socklen_t, $curl_cv_socklen_t_equiv,
+ [type to use in place of socklen_t if not defined])],
+ [#include <sys/types.h>
+ #include <sys/socket.h>])
+
+# for the fake-rfc2553 stuff - straight from OpenSSH
+
+AC_CACHE_CHECK([for struct sockaddr_storage], ac_cv_have_struct_sockaddr_storage, [
+ AC_TRY_COMPILE(
+ [
+#include <sys/types.h>
+#include <sys/socket.h>
+ ],
+ [ struct sockaddr_storage s; ],
+ [ ac_cv_have_struct_sockaddr_storage="yes" ],
+ [ ac_cv_have_struct_sockaddr_storage="no" ]
+ )
+])
+if test "x$ac_cv_have_struct_sockaddr_storage" = "xyes" ; then
+ AC_DEFINE(HAVE_STRUCT_SOCKADDR_STORAGE)
+fi
+
+AC_CACHE_CHECK([for struct sockaddr_in6], ac_cv_have_struct_sockaddr_in6, [
+ AC_TRY_COMPILE(
+ [
+#include <sys/types.h>
+#include <netinet/in.h>
+ ],
+ [ struct sockaddr_in6 s; s.sin6_family = 0; ],
+ [ ac_cv_have_struct_sockaddr_in6="yes" ],
+ [ ac_cv_have_struct_sockaddr_in6="no" ]
+ )
+])
+if test "x$ac_cv_have_struct_sockaddr_in6" = "xyes" ; then
+ AC_DEFINE(HAVE_STRUCT_SOCKADDR_IN6,,Have struct sockaddr_in6)
+fi
+
+AC_CACHE_CHECK([for struct in6_addr], ac_cv_have_struct_in6_addr, [
+ AC_TRY_COMPILE(
+ [
+#include <sys/types.h>
+#include <netinet/in.h>
+ ],
+ [ struct in6_addr s; s.s6_addr[0] = 0; ],
+ [ ac_cv_have_struct_in6_addr="yes" ],
+ [ ac_cv_have_struct_in6_addr="no" ]
+ )
+])
+if test "x$ac_cv_have_struct_in6_addr" = "xyes" ; then
+ AC_DEFINE(HAVE_STRUCT_IN6_ADDR,,Have struct in6_addr)
+fi
+
+AC_CACHE_CHECK([for struct addrinfo], ac_cv_have_struct_addrinfo, [
+ AC_TRY_COMPILE(
+ [
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+ ],
+ [ struct addrinfo s; s.ai_flags = AI_PASSIVE; ],
+ [ ac_cv_have_struct_addrinfo="yes" ],
+ [ ac_cv_have_struct_addrinfo="no" ]
+ )
+])
+if test "x$ac_cv_have_struct_addrinfo" = "xyes" ; then
+ AC_DEFINE(HAVE_STRUCT_ADDRINFO,,Have struct addrinfo)
+fi
+
+
+# IRIX has a const char return value for gai_strerror()
+AC_CHECK_FUNCS(gai_strerror,[
+ AC_DEFINE(HAVE_GAI_STRERROR)
+ AC_TRY_COMPILE([
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+const char *gai_strerror(int);],[
+char *str;
+
+str = gai_strerror(0);],[
+ AC_DEFINE(HAVE_CONST_GAI_STRERROR_PROTO, 1,
+ [Define if gai_strerror() returns const char *])])])
+
+# for loginrec.c
+
+AC_CHECK_MEMBERS([struct utmp.ut_host, struct utmp.ut_pid, struct utmp.ut_type, struct utmp.ut_tv, struct utmp.ut_id, struct utmp.ut_addr, struct utmp.ut_addr_v6, struct utmp.ut_exit, struct utmp.ut_time],,,[
+#include <sys/types.h>
+#if HAVE_UTMP_H
+#include <utmp.h>
+#endif
+])
+
+AC_CHECK_MEMBERS([struct utmpx.ut_host, struct utmpx.ut_syslen, struct utmpx.ut_type, struct utmpx.ut_id, struct utmpx.ut_addr, struct utmpx.ut_addr_v6, struct utmpx.ut_time, struct utmpx.ut_tv, struct sockaddr_storage.ss_family, struct sockadd_storage.__family],,,[
+#include <sys/types.h>
+#include <sys/socket.h>
+#if HAVE_UTMPX_H
+#include <utmpx.h>
+#endif
+])
+
+AC_CHECK_FUNCS(endutent getutent getutid getutline pututline setutent)
+AC_CHECK_FUNCS(utmpname)
+AC_CHECK_FUNCS(endutxent getutxent getutxid getutxline pututxline )
+AC_CHECK_FUNCS(setutxent utmpxname)
+AC_CHECK_FUNCS(logout updwtmp logwtmp)
+
+dnl Added from OpenSSH 3.6.1p2's configure.ac
+
+dnl allow user to disable some login recording features
+AC_ARG_ENABLE(lastlog,
+ [ --disable-lastlog Disable use of lastlog even if detected [no]],
+ [ AC_DEFINE(DISABLE_LASTLOG,,Disable use of lastlog()) ]
+)
+AC_ARG_ENABLE(utmp,
+ [ --disable-utmp Disable use of utmp even if detected [no]],
+ [ AC_DEFINE(DISABLE_UTMP,,Disable use of utmp) ]
+)
+AC_ARG_ENABLE(utmpx,
+ [ --disable-utmpx Disable use of utmpx even if detected [no]],
+ [ AC_DEFINE(DISABLE_UTMPX,,Disable use of utmpx) ]
+)
+AC_ARG_ENABLE(wtmp,
+ [ --disable-wtmp Disable use of wtmp even if detected [no]],
+ [ AC_DEFINE(DISABLE_WTMP,,Disable use of wtmp) ]
+)
+AC_ARG_ENABLE(wtmpx,
+ [ --disable-wtmpx Disable use of wtmpx even if detected [no]],
+ [ AC_DEFINE(DISABLE_WTMPX,,Disable use of wtmpx) ]
+)
+AC_ARG_ENABLE(loginfunc,
+ [ --disable-loginfunc Disable use of login() etc. [no]],
+ [ no_loginfunc_check=1
+ AC_MSG_RESULT(Not using login() etc) ]
+)
+AC_ARG_ENABLE(pututline,
+ [ --disable-pututline Disable use of pututline() etc. ([uw]tmp) [no]],
+ [ AC_DEFINE(DISABLE_PUTUTLINE,,Disable use of pututline()) ]
+)
+AC_ARG_ENABLE(pututxline,
+ [ --disable-pututxline Disable use of pututxline() etc. ([uw]tmpx) [no]],
+ [ AC_DEFINE(DISABLE_PUTUTXLINE,,Disable use of pututxline()) ]
+)
+AC_ARG_WITH(lastlog,
+ [ --with-lastlog=FILE|DIR specify lastlog location [common locations]],
+ [
+ if test "x$withval" = "xno" ; then
+ AC_DEFINE(DISABLE_LASTLOG)
+ else
+ conf_lastlog_location=$withval
+ fi
+ ]
+)
+
+if test -z "$no_loginfunc_check"; then
+ dnl Checks for libutil functions (login(), logout() etc, not openpty() )
+ AC_SEARCH_LIBS(login, util bsd, [AC_DEFINE(HAVE_LOGIN,,Have login() function)])
+ AC_CHECK_FUNCS(logout updwtmp logwtmp)
+fi
+
+dnl lastlog, [uw]tmpx? detection
+dnl NOTE: set the paths in the platform section to avoid the
+dnl need for command-line parameters
+dnl lastlog and [uw]tmp are subject to a file search if all else fails
+
+dnl lastlog detection
+dnl NOTE: the code itself will detect if lastlog is a directory
+AC_MSG_CHECKING([if your system defines LASTLOG_FILE])
+AC_TRY_COMPILE([
+#include <sys/types.h>
+#include <utmp.h>
+#ifdef HAVE_LASTLOG_H
+# include <lastlog.h>
+#endif
+#ifdef HAVE_PATHS_H
+# include <paths.h>
+#endif
+#ifdef HAVE_LOGIN_H
+# include <login.h>
+#endif
+ ],
+ [ char *lastlog = LASTLOG_FILE; ],
+ [ AC_MSG_RESULT(yes) ],
+ [
+ AC_MSG_RESULT(no)
+ AC_MSG_CHECKING([if your system defines _PATH_LASTLOG])
+ AC_TRY_COMPILE([
+#include <sys/types.h>
+#include <utmp.h>
+#ifdef HAVE_LASTLOG_H
+# include <lastlog.h>
+#endif
+#ifdef HAVE_PATHS_H
+# include <paths.h>
+#endif
+ ],
+ [ char *lastlog = _PATH_LASTLOG; ],
+ [ AC_MSG_RESULT(yes) ],
+ [
+ AC_MSG_RESULT(no)
+ system_lastlog_path=no
+ ])
+ ]
+)
+
+if test -z "$conf_lastlog_location"; then
+ if test x"$system_lastlog_path" = x"no" ; then
+ for f in /var/log/lastlog /usr/adm/lastlog /var/adm/lastlog /etc/security/lastlog ; do
+ if (test -d "$f" || test -f "$f") ; then
+ conf_lastlog_location=$f
+ fi
+ done
+ if test -z "$conf_lastlog_location"; then
+ AC_MSG_WARN([** Cannot find lastlog **])
+ dnl Don't define DISABLE_LASTLOG - that means we don't try wtmp/wtmpx
+ fi
+ fi
+fi
+
+if test -n "$conf_lastlog_location"; then
+ AC_DEFINE_UNQUOTED(CONF_LASTLOG_FILE, "$conf_lastlog_location", lastlog file location)
+fi
+
+dnl utmp detection
+AC_MSG_CHECKING([if your system defines UTMP_FILE])
+AC_TRY_COMPILE([
+#include <sys/types.h>
+#include <utmp.h>
+#ifdef HAVE_PATHS_H
+# include <paths.h>
+#endif
+ ],
+ [ char *utmp = UTMP_FILE; ],
+ [ AC_MSG_RESULT(yes) ],
+ [ AC_MSG_RESULT(no)
+ system_utmp_path=no ]
+)
+if test -z "$conf_utmp_location"; then
+ if test x"$system_utmp_path" = x"no" ; then
+ for f in /etc/utmp /usr/adm/utmp /var/run/utmp; do
+ if test -f $f ; then
+ conf_utmp_location=$f
+ fi
+ done
+ if test -z "$conf_utmp_location"; then
+ AC_DEFINE(DISABLE_UTMP)
+ fi
+ fi
+fi
+if test -n "$conf_utmp_location"; then
+ AC_DEFINE_UNQUOTED(CONF_UTMP_FILE, "$conf_utmp_location", utmp file location)
+fi
+
+dnl wtmp detection
+AC_MSG_CHECKING([if your system defines WTMP_FILE])
+AC_TRY_COMPILE([
+#include <sys/types.h>
+#include <utmp.h>
+#ifdef HAVE_PATHS_H
+# include <paths.h>
+#endif
+ ],
+ [ char *wtmp = WTMP_FILE; ],
+ [ AC_MSG_RESULT(yes) ],
+ [ AC_MSG_RESULT(no)
+ system_wtmp_path=no ]
+)
+if test -z "$conf_wtmp_location"; then
+ if test x"$system_wtmp_path" = x"no" ; then
+ for f in /usr/adm/wtmp /var/log/wtmp; do
+ if test -f $f ; then
+ conf_wtmp_location=$f
+ fi
+ done
+ if test -z "$conf_wtmp_location"; then
+ AC_DEFINE(DISABLE_WTMP)
+ fi
+ fi
+fi
+if test -n "$conf_wtmp_location"; then
+ AC_DEFINE_UNQUOTED(CONF_WTMP_FILE, "$conf_wtmp_location", wtmp file location)
+fi
+
+
+dnl utmpx detection - I don't know any system so perverse as to require
+dnl utmpx, but not define UTMPX_FILE (ditto wtmpx.) No doubt it's out
+dnl there, though.
+AC_MSG_CHECKING([if your system defines UTMPX_FILE])
+AC_TRY_COMPILE([
+#include <sys/types.h>
+#include <utmp.h>
+#ifdef HAVE_UTMPX_H
+#include <utmpx.h>
+#endif
+#ifdef HAVE_PATHS_H
+# include <paths.h>
+#endif
+ ],
+ [ char *utmpx = UTMPX_FILE; ],
+ [ AC_MSG_RESULT(yes) ],
+ [ AC_MSG_RESULT(no)
+ system_utmpx_path=no ]
+)
+if test -z "$conf_utmpx_location"; then
+ if test x"$system_utmpx_path" = x"no" ; then
+ AC_DEFINE(DISABLE_UTMPX)
+ fi
+else
+ AC_DEFINE_UNQUOTED(CONF_UTMPX_FILE, "$conf_utmpx_location", utmpx file location)
+fi
+
+dnl wtmpx detection
+AC_MSG_CHECKING([if your system defines WTMPX_FILE])
+AC_TRY_COMPILE([
+#include <sys/types.h>
+#include <utmp.h>
+#ifdef HAVE_UTMPX_H
+#include <utmpx.h>
+#endif
+#ifdef HAVE_PATHS_H
+# include <paths.h>
+#endif
+ ],
+ [ char *wtmpx = WTMPX_FILE; ],
+ [ AC_MSG_RESULT(yes) ],
+ [ AC_MSG_RESULT(no)
+ system_wtmpx_path=no ]
+)
+if test -z "$conf_wtmpx_location"; then
+ if test x"$system_wtmpx_path" = x"no" ; then
+ AC_DEFINE(DISABLE_WTMPX)
+ fi
+else
+ AC_DEFINE_UNQUOTED(CONF_WTMPX_FILE, "$conf_wtmpx_location", wtmpx file location)
+fi
+
+# Checks for library functions.
+AC_PROG_GCC_TRADITIONAL
+AC_FUNC_MEMCMP
+AC_FUNC_SELECT_ARGTYPES
+AC_TYPE_SIGNAL
+AC_CHECK_FUNCS([dup2 getspnam getusershell memset putenv select socket strdup clearenv strlcpy strlcat daemon basename _getpty getaddrinfo freeaddrinfo getnameinfo])
+
+AC_SEARCH_LIBS(basename, gen, AC_DEFINE(HAVE_BASENAME))
+
+# Solaris needs ptmx
+if test -z "$no_ptmx_check" ; then
+ if test x"$cross_compiling" = x"no" ; then
+ AC_CHECK_FILE("/dev/ptmx", AC_DEFINE(USE_DEV_PTMX,,Use /dev/ptmx))
+ else
+ AC_MSG_RESULT(Not checking for /dev/ptmx, we're cross-compiling)
+ fi
+fi
+
+if test -z "$no_ptc_check" ; then
+ if test x"$cross_compiling" = x"no" ; then
+ AC_CHECK_FILE("/dev/ptc", AC_DEFINE(HAVE_DEV_PTS_AND_PTC,,Use /dev/ptc & /dev/pts))
+ else
+ AC_MSG_RESULT(Not checking for /dev/ptc & /dev/pts\, we're cross-compiling)
+ fi
+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.)
diff --git a/dbclient.1 b/dbclient.1
new file mode 100644
index 0000000..4d7cc3c
--- /dev/null
+++ b/dbclient.1
@@ -0,0 +1,74 @@
+.TH dbclient 1
+.SH NAME
+dbclient \- lightweight SSH2 client
+.SH SYNOPSIS
+.B dbclient
+[\-Tt] [\-p
+.I port\fR] [\-i
+.I id\fR] [\-L
+.I l\fR:\fIh\fR:\fIr\fR] [\-R
+.I l\fR:\fIh\fR:\fIr\fR] [\-l
+.IR user ]
+.I host
+.SH DESCRIPTION
+.B dbclient
+is a SSH 2 client 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 \-p \fIport
+Remote port.
+Connect to port
+.I port
+on the remote host.
+Default is 22.
+.TP
+.B \-i \fIidfile
+Identity file.
+Read the identity from file
+.I idfile
+(multiple allowed).
+.TP
+.B \-L \fIlocalport\fR:\fIremotehost\fR:\fIremoteport\fR
+Local port forwarding.
+Forward the port
+.I localport
+on the local host to port
+.I remoteport
+on the remote host
+.IR remotehost .
+.TP
+.B \-R \fIlocalport\fR:\fIremotehost\fR:\fIremoteport\fR
+Remote port forwarding.
+Forward the port
+.I remoteport
+on the remote host
+.I remotehost
+to port
+.I localport
+on the local host.
+.TP
+.B \-l \fIuser
+Username.
+Login as
+.I user
+on the remote host.
+.TP
+.B \-t
+Allocate a pty.
+.TP
+.B \-T
+Don't allocate a pty.
+.TP
+.B \-g
+Allow non-local hosts to connect to forwarded ports. Applies to -L and -R
+forwarded ports, though remote connections to -R forwarded ports may be limited
+by the ssh server.
+.SH AUTHOR
+Matt Johnston (matt@ucc.asn.au).
+.br
+Gerrit Pape (pape@smarden.org) wrote this manual page.
+.SH SEE ALSO
+dropbear(8), dropbearkey(8)
+.P
+http://matt.ucc.asn.au/dropbear/dropbear.html
diff --git a/dbmulti.c b/dbmulti.c
new file mode 100644
index 0000000..2b8df3f
--- /dev/null
+++ b/dbmulti.c
@@ -0,0 +1,90 @@
+/*
+ * 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. */
+
+#include "includes.h"
+
+/* definitions are cleanest if we just put them here */
+int dropbear_main(int argc, char ** argv);
+int dropbearkey_main(int argc, char ** argv);
+int dropbearconvert_main(int argc, char ** argv);
+int scp_main(int argc, char ** argv);
+
+int main(int argc, char ** argv) {
+
+ char * progname;
+
+ if (argc > 0) {
+ /* figure which form we're being called as */
+ progname = basename(argv[0]);
+
+#ifdef DBMULTI_dropbear
+ if (strcmp(progname, "dropbear") == 0) {
+ return dropbear_main(argc, argv);
+ }
+#endif
+#ifdef DBMULTI_dbclient
+ if (strcmp(progname, "dbclient") == 0
+ || strcmp(progname, "ssh") == 0) {
+ return cli_main(argc, argv);
+ }
+#endif
+#ifdef DBMULTI_dropbearkey
+ if (strcmp(progname, "dropbearkey") == 0) {
+ return dropbearkey_main(argc, argv);
+ }
+#endif
+#ifdef DBMULTI_dropbearconvert
+ if (strcmp(progname, "dropbearconvert") == 0) {
+ return dropbearconvert_main(argc, argv);
+ }
+#endif
+#ifdef DBMULTI_scp
+ if (strcmp(progname, "scp") == 0) {
+ return scp_main(argc, argv);
+ }
+#endif
+ }
+
+ fprintf(stderr, "Dropbear multi-purpose version %s\n"
+ "Make a symlink pointing at this binary with one of the following names:\n"
+#ifdef DBMULTI_dropbear
+ "'dropbear' - the Dropbear server\n"
+#endif
+#ifdef DBMULTI_dbclient
+ "'dbclient' or 'ssh' - the Dropbear client\n"
+#endif
+#ifdef DBMULTI_dropbearkey
+ "'dropbearkey' - the key generator\n"
+#endif
+#ifdef DBMULTI_dropbearconvert
+ "'dropbearconvert' - the key converter\n"
+#endif
+#ifdef DBMULTI_scp
+ "'scp' - secure copy\n"
+#endif
+ ,
+ DROPBEAR_VERSION);
+ exit(1);
+
+}
diff --git a/dbutil.c b/dbutil.c
new file mode 100644
index 0000000..15f51ba
--- /dev/null
+++ b/dbutil.c
@@ -0,0 +1,679 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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.
+ *
+ * strlcat() is copyright as follows:
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#include "includes.h"
+#include "dbutil.h"
+#include "buffer.h"
+#include "session.h"
+#include "atomicio.h"
+
+#define MAX_FMT 100
+
+static void generic_dropbear_exit(int exitcode, const char* format,
+ va_list param);
+static void generic_dropbear_log(int priority, const char* format,
+ va_list param);
+
+void (*_dropbear_exit)(int exitcode, const char* format, va_list param)
+ = generic_dropbear_exit;
+void (*_dropbear_log)(int priority, const char* format, va_list param)
+ = generic_dropbear_log;
+
+#ifdef DEBUG_TRACE
+int debug_trace = 0;
+#endif
+
+#ifndef DISABLE_SYSLOG
+void startsyslog() {
+
+ openlog(PROGNAME, LOG_PID, LOG_AUTHPRIV);
+
+}
+#endif /* DISABLE_SYSLOG */
+
+/* the "format" string must be <= 100 characters */
+void dropbear_close(const char* format, ...) {
+
+ va_list param;
+
+ va_start(param, format);
+ _dropbear_exit(EXIT_SUCCESS, format, param);
+ va_end(param);
+
+}
+
+void dropbear_exit(const char* format, ...) {
+
+ va_list param;
+
+ va_start(param, format);
+ _dropbear_exit(EXIT_FAILURE, format, param);
+ va_end(param);
+}
+
+static void generic_dropbear_exit(int exitcode, const char* format,
+ va_list param) {
+
+ char fmtbuf[300];
+
+ snprintf(fmtbuf, sizeof(fmtbuf), "Exited: %s", format);
+
+ _dropbear_log(LOG_INFO, fmtbuf, param);
+
+ exit(exitcode);
+}
+
+void fail_assert(const char* expr, const char* file, int line) {
+ dropbear_exit("failed assertion (%s:%d): `%s'", file, line, expr);
+}
+
+static void generic_dropbear_log(int UNUSED(priority), const char* format,
+ va_list param) {
+
+ char printbuf[1024];
+
+ vsnprintf(printbuf, sizeof(printbuf), format, param);
+
+ fprintf(stderr, "%s\n", printbuf);
+
+}
+
+/* this is what can be called to write arbitrary log messages */
+void dropbear_log(int priority, const char* format, ...) {
+
+ va_list param;
+
+ va_start(param, format);
+ _dropbear_log(priority, format, param);
+ va_end(param);
+}
+
+
+#ifdef DEBUG_TRACE
+void dropbear_trace(const char* format, ...) {
+
+ va_list param;
+
+ if (!debug_trace) {
+ return;
+ }
+
+ va_start(param, format);
+ fprintf(stderr, "TRACE: ");
+ vfprintf(stderr, format, param);
+ fprintf(stderr, "\n");
+ va_end(param);
+}
+#endif /* DEBUG_TRACE */
+
+static void set_sock_priority(int sock) {
+
+ int val;
+
+ /* disable nagle */
+ val = 1;
+ setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&val, sizeof(val));
+
+ /* set the TOS bit. note that this will fail for ipv6, I can't find any
+ * equivalent. */
+#ifdef IPTOS_LOWDELAY
+ val = IPTOS_LOWDELAY;
+ setsockopt(sock, IPPROTO_IP, IP_TOS, (void*)&val, sizeof(val));
+#endif
+
+#ifdef SO_PRIORITY
+ /* linux specific, sets QoS class.
+ * 6 looks to be optimal for interactive traffic (see tc-prio(8) ). */
+ val = 6;
+ setsockopt(sock, SOL_SOCKET, SO_PRIORITY, (void*) &val, sizeof(val));
+#endif
+
+}
+
+/* Listen on address:port.
+ * Special cases are address of "" listening on everything,
+ * and address of NULL listening on localhost only.
+ * Returns the number of sockets bound on success, or -1 on failure. On
+ * failure, if errstring wasn't NULL, it'll be a newly malloced error
+ * string.*/
+int dropbear_listen(const char* address, const char* port,
+ int *socks, unsigned int sockcount, char **errstring, int *maxfd) {
+
+ struct addrinfo hints, *res = NULL, *res0 = NULL;
+ int err;
+ unsigned int nsock;
+ struct linger linger;
+ int val;
+ int sock;
+
+ 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;
+
+ // for calling getaddrinfo:
+ // address == NULL and !AI_PASSIVE: local loopback
+ // address == NULL and AI_PASSIVE: all interfaces
+ // address != NULL: whatever the address says
+ if (!address) {
+ TRACE(("dropbear_listen: local loopback"))
+ } else {
+ if (address[0] == '\0') {
+ TRACE(("dropbear_listen: all interfaces"))
+ address = NULL;
+ }
+ hints.ai_flags = AI_PASSIVE;
+ }
+ err = getaddrinfo(address, port, &hints, &res0);
+
+ if (err) {
+ if (errstring != NULL && *errstring == NULL) {
+ int len;
+ len = 20 + strlen(gai_strerror(err));
+ *errstring = (char*)m_malloc(len);
+ snprintf(*errstring, len, "Error resolving: %s", gai_strerror(err));
+ }
+ if (res0) {
+ freeaddrinfo(res0);
+ res0 = NULL;
+ }
+ TRACE(("leave dropbear_listen: failed resolving"))
+ return -1;
+ }
+
+
+ nsock = 0;
+ for (res = res0; res != NULL && nsock < sockcount;
+ res = res->ai_next) {
+
+ /* Get a socket */
+ socks[nsock] = socket(res->ai_family, res->ai_socktype,
+ res->ai_protocol);
+
+ sock = socks[nsock]; /* For clarity */
+
+ if (sock < 0) {
+ err = errno;
+ TRACE(("socket() failed"))
+ continue;
+ }
+
+ /* Various useful socket options */
+ val = 1;
+ /* set to reuse, quick timeout */
+ setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*) &val, sizeof(val));
+ linger.l_onoff = 1;
+ linger.l_linger = 5;
+ setsockopt(sock, SOL_SOCKET, SO_LINGER, (void*)&linger, sizeof(linger));
+
+ set_sock_priority(sock);
+
+ if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) {
+ err = errno;
+ close(sock);
+ TRACE(("bind(%s) failed", port))
+ continue;
+ }
+
+ if (listen(sock, 20) < 0) {
+ err = errno;
+ close(sock);
+ TRACE(("listen() failed"))
+ continue;
+ }
+
+ *maxfd = MAX(*maxfd, sock);
+
+ nsock++;
+ }
+
+ if (res0) {
+ freeaddrinfo(res0);
+ res0 = NULL;
+ }
+
+ if (nsock == 0) {
+ if (errstring != NULL && *errstring == NULL) {
+ int len;
+ 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)))
+ return -1;
+ }
+ }
+
+ TRACE(("leave dropbear_listen: success, %d socks bound", nsock))
+ return nsock;
+}
+
+/* Connect via TCP to a host. Connection will try ipv4 or ipv6, will
+ * return immediately if nonblocking is set. On failure, if errstring
+ * wasn't null, it will be a newly malloced error message */
+
+/* TODO: maxfd */
+int connect_remote(const char* remotehost, const char* remoteport,
+ int nonblocking, char ** errstring) {
+
+ struct addrinfo *res0 = NULL, *res = NULL, hints;
+ int sock;
+ int err;
+
+ TRACE(("enter connect_remote"))
+
+ if (errstring != NULL) {
+ *errstring = NULL;
+ }
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_family = PF_UNSPEC;
+
+ err = getaddrinfo(remotehost, remoteport, &hints, &res0);
+ if (err) {
+ if (errstring != NULL && *errstring == NULL) {
+ int len;
+ len = 20 + strlen(gai_strerror(err));
+ *errstring = (char*)m_malloc(len);
+ snprintf(*errstring, len, "Error resolving: %s", gai_strerror(err));
+ }
+ TRACE(("Error resolving: %s", gai_strerror(err)))
+ return -1;
+ }
+
+ sock = -1;
+ err = EADDRNOTAVAIL;
+ for (res = res0; res; res = res->ai_next) {
+
+ sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+ if (sock < 0) {
+ err = errno;
+ continue;
+ }
+
+ if (nonblocking) {
+ if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
+ close(sock);
+ sock = -1;
+ if (errstring != NULL && *errstring == NULL) {
+ *errstring = m_strdup("Failed non-blocking");
+ }
+ 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"))
+ break;
+ } else {
+ err = errno;
+ close(sock);
+ sock = -1;
+ continue;
+ }
+ }
+
+ break; /* Success */
+ }
+
+ if (sock < 0 && !(errno == EINPROGRESS && nonblocking)) {
+ /* Failed */
+ if (errstring != NULL && *errstring == NULL) {
+ int len;
+ len = 20 + strlen(strerror(err));
+ *errstring = (char*)m_malloc(len);
+ snprintf(*errstring, len, "Error connecting: %s", strerror(err));
+ }
+ TRACE(("Error connecting: %s", strerror(err)))
+ } else {
+ /* Success */
+ set_sock_priority(sock);
+ }
+
+ freeaddrinfo(res0);
+ if (sock > 0 && errstring != NULL && *errstring != NULL) {
+ m_free(*errstring);
+ }
+
+ TRACE(("leave connect_remote: sock %d\n", sock))
+ return sock;
+}
+
+/* Return a string representation of the socket address passed. The return
+ * value is allocated with malloc() */
+unsigned char * getaddrstring(struct sockaddr_storage* addr, int withport) {
+
+ char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
+ char *retstring = NULL;
+ int ret;
+ 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);
+
+ if (ret != 0) {
+ /* This is a fairly bad failure - it'll fallback to IP if it
+ * just can't resolve */
+ dropbear_exit("failed lookup (%d, %d)", ret, errno);
+ }
+
+ if (withport) {
+ len = strlen(hbuf) + 2 + strlen(sbuf);
+ retstring = (char*)m_malloc(len);
+ snprintf(retstring, len, "%s:%s", hbuf, sbuf);
+ } else {
+ retstring = m_strdup(hbuf);
+ }
+
+ return retstring;
+
+}
+
+/* Get the hostname corresponding to the address addr. On failure, the IP
+ * address is returned. The return value is allocated with strdup() */
+char* getaddrhostname(struct sockaddr_storage * addr) {
+
+ char hbuf[NI_MAXHOST];
+ 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), flags);
+
+ if (ret != 0) {
+ /* On some systems (Darwin does it) we get EINTR from getnameinfo
+ * somehow. Eew. So we'll just return the IP, since that doesn't seem
+ * to exhibit that behaviour. */
+ return getaddrstring(addr, 0);
+ }
+
+ return m_strdup(hbuf);
+}
+
+#ifdef DEBUG_TRACE
+void printhex(const char * label, const unsigned char * buf, int len) {
+
+ int i;
+
+ fprintf(stderr, "%s\n", label);
+ for (i = 0; i < len; i++) {
+ fprintf(stderr, "%02x", buf[i]);
+ if (i % 16 == 15) {
+ fprintf(stderr, "\n");
+ }
+ else if (i % 2 == 1) {
+ fprintf(stderr, " ");
+ }
+ }
+ fprintf(stderr, "\n");
+}
+#endif
+
+/* Strip all control characters from text (a null-terminated string), except
+ * for '\n', '\r' and '\t'.
+ * The result returned is a newly allocated string, this must be free()d after
+ * use */
+char * stripcontrol(const char * text) {
+
+ char * ret;
+ int len, pos;
+ int i;
+
+ len = strlen(text);
+ ret = m_malloc(len+1);
+
+ pos = 0;
+ for (i = 0; i < len; i++) {
+ if ((text[i] <= '~' && text[i] >= ' ') /* normal printable range */
+ || text[i] == '\n' || text[i] == '\r' || text[i] == '\t') {
+ ret[pos] = text[i];
+ pos++;
+ }
+ }
+ ret[pos] = 0x0;
+ return ret;
+}
+
+
+/* reads the contents of filename into the buffer buf, from the current
+ * position, either to the end of the file, or the buffer being full.
+ * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
+int buf_readfile(buffer* buf, const char* filename) {
+
+ int fd;
+ int len;
+ int maxlen;
+
+ fd = open(filename, O_RDONLY);
+
+ if (fd < 0) {
+ close(fd);
+ return DROPBEAR_FAILURE;
+ }
+
+ do {
+ maxlen = buf->size - buf->pos;
+ len = read(fd, buf_getwriteptr(buf, maxlen),
+ maxlen);
+ buf_incrwritepos(buf, len);
+ } while (len < maxlen && len > 0);
+
+ close(fd);
+ return DROPBEAR_SUCCESS;
+}
+
+/* get a line from the file into buffer in the style expected for an
+ * authkeys file.
+ * Will return DROPBEAR_SUCCESS if data is read, or DROPBEAR_FAILURE on EOF.*/
+/* Only used for ~/.ssh/known_hosts and ~/.ssh/authorized_keys */
+#if defined(DROPBEAR_CLIENT) || defined(ENABLE_SVR_PUBKEY_AUTH)
+int buf_getline(buffer * line, FILE * authfile) {
+
+ int c = EOF;
+
+ TRACE(("enter buf_getline"))
+
+ buf_setpos(line, 0);
+ buf_setlen(line, 0);
+
+ while (line->pos < line->size) {
+
+ c = fgetc(authfile); /*getc() is weird with some uClibc systems*/
+ if (c == EOF || c == '\n' || c == '\r') {
+ goto out;
+ }
+
+ buf_putbyte(line, (unsigned char)c);
+ }
+
+ 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:
+
+
+ /* if we didn't read anything before EOF or error, exit */
+ if (c == EOF && line->pos == 0) {
+ TRACE(("leave buf_getline: failure"))
+ return DROPBEAR_FAILURE;
+ } else {
+ TRACE(("leave buf_getline: success"))
+ buf_setpos(line, 0);
+ return DROPBEAR_SUCCESS;
+ }
+
+}
+#endif
+
+/* make sure that the socket closes */
+void m_close(int fd) {
+
+ int val;
+ do {
+ val = close(fd);
+ } while (val < 0 && errno == EINTR);
+
+ if (val < 0 && errno != EBADF) {
+ /* Linux says EIO can happen */
+ dropbear_exit("Error closing fd %d, %s", fd, strerror(errno));
+ }
+}
+
+void * m_malloc(size_t size) {
+
+ void* ret;
+
+ if (size == 0) {
+ dropbear_exit("m_malloc failed");
+ }
+ ret = calloc(1, size);
+ if (ret == NULL) {
+ dropbear_exit("m_malloc failed");
+ }
+ return ret;
+
+}
+
+void * m_strdup(const char * str) {
+ char* ret;
+
+ ret = strdup(str);
+ if (ret == NULL) {
+ dropbear_exit("m_strdup failed");
+ }
+ return ret;
+}
+
+void __m_free(void* ptr) {
+ if (ptr != NULL) {
+ free(ptr);
+ }
+}
+
+void * m_realloc(void* ptr, size_t size) {
+
+ void *ret;
+
+ if (size == 0) {
+ dropbear_exit("m_realloc failed");
+ }
+ ret = realloc(ptr, size);
+ if (ret == NULL) {
+ dropbear_exit("m_realloc failed");
+ }
+ return ret;
+}
+
+/* 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;
+
+ if (data == NULL)
+ return;
+ while (len--) {
+ *p++ = 0x66;
+ }
+}
+
+
+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"))
+}
diff --git a/dbutil.h b/dbutil.h
new file mode 100644
index 0000000..d74e17e
--- /dev/null
+++ b/dbutil.h
@@ -0,0 +1,73 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+#ifndef _DBUTIL_H_
+
+#define _DBUTIL_H_
+
+#include "includes.h"
+#include "buffer.h"
+
+#ifndef DISABLE_SYSLOG
+void startsyslog();
+#endif
+
+extern void (*_dropbear_exit)(int exitcode, const char* format, va_list param);
+extern void (*_dropbear_log)(int priority, const char* format, va_list param);
+
+void dropbear_exit(const char* format, ...);
+void dropbear_close(const char* format, ...);
+void dropbear_log(int priority, const char* format, ...);
+void fail_assert(const char* expr, const char* file, int line);
+#ifdef DEBUG_TRACE
+void dropbear_trace(const char* format, ...);
+void printhex(const char * label, const unsigned char * buf, int len);
+extern int debug_trace;
+#endif
+char * stripcontrol(const char * text);
+unsigned char * getaddrstring(struct sockaddr_storage* addr, int withport);
+int dropbear_listen(const char* address, const char* port,
+ int *socks, unsigned int sockcount, char **errstring, int *maxfd);
+int connect_remote(const char* remotehost, const char* remoteport,
+ int nonblocking, char ** errstring);
+char* getaddrhostname(struct sockaddr_storage * addr);
+int buf_readfile(buffer* buf, const char* filename);
+int buf_getline(buffer * line, FILE * authfile);
+
+void m_close(int fd);
+void * m_malloc(size_t size);
+void * m_strdup(const char * str);
+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}
+
+/* Dropbear assertion */
+#define dropbear_assert(X) do { if (!(X)) { fail_assert(#X, __FILE__, __LINE__); } } while (0)
+
+#endif /* _DBUTIL_H_ */
diff --git a/debian/README.Debian b/debian/README.Debian
new file mode 100644
index 0000000..8cdac38
--- /dev/null
+++ b/debian/README.Debian
@@ -0,0 +1,41 @@
+Dropbear for Debian
+-------------------
+
+This package will attempt to listen on port 22. If the OpenSSH
+package ("ssh") is installed, the file /etc/default/dropbear
+will be set up so that the server does not start by default.
+
+You can run Dropbear concurrently with OpenSSH 'sshd' by
+modifying /etc/default/dropbear so that "NO_START" is set to
+"0" and changing the port number that Dropbear runs on. Follow
+the instructions in the file.
+
+This package suggests you install the "ssh" package. This package
+provides the "ssh" client program, as well as the "/usr/bin/scp"
+binary you will need to be able to retrieve files from a server
+running Dropbear via SCP.
+
+Replacing OpenSSH "sshd" with Dropbear
+--------------------------------------
+
+You will still want to have the "ssh" package installed, as it
+provides the "ssh" and "scp" binaries. When you install this
+package, it checks for existing OpenSSH host keys and if found,
+converts them to the Dropbear format.
+
+If this appears to have worked, you should be able to change over
+by following these steps:
+
+1. Stop the OpenSSH server
+ % /etc/init.d/ssh stop
+2. Prevent the OpenSSH server from starting in the future
+ % touch /etc/ssh/sshd_not_to_be_run
+3. Modify the Dropbear defaults file, set NO_START to 0 and
+ ensure DROPBEAR_PORT is set to 22.
+ % editor /etc/default/dropbear
+4. Restart the Dropbear server.
+ % /etc/init.d/dropbear restart
+
+See the Dropbear homepage for more information:
+ http://matt.ucc.asn.au/dropbear/dropbear.html
+
diff --git a/debian/README.Debian.diet b/debian/README.Debian.diet
new file mode 100644
index 0000000..bd0cb5c
--- /dev/null
+++ b/debian/README.Debian.diet
@@ -0,0 +1,15 @@
+Building with the diet libc
+---------------------------
+
+This package optionally can be built with the diet libc instead of the
+glibc to provide small statically linked programs. The resulting package
+has no dependency on any other package.
+
+To use the diet libc, make sure the latest versions of the dietlibc-dev
+package is installed, and set DEB_BUILD_OPTIONS=diet in the environment
+when building the package, e.g.:
+
+ # apt-get install dietlibc-dev
+ $ DEB_BUILD_OPTIONS=diet fakeroot apt-get source -b dropbear
+
+ -- Gerrit Pape <pape@smarden.org>, Sat, 17 Jul 2004 19:09:34 +0000
diff --git a/debian/README.runit b/debian/README.runit
new file mode 100644
index 0000000..4ac2814
--- /dev/null
+++ b/debian/README.runit
@@ -0,0 +1,46 @@
+Using the dropbear SSH server with runit's services supervision
+---------------------------------------------------------------
+
+The dropbear SSH server is perfectly suited to be run under runit's
+service supervision, and this package already has prepared an adequate
+service directory. Follow these steps to enable the dropbear service
+using the runit package.
+
+If not yet installed on your system, install the runit package, and make
+sure its service supervision is enabled (it's by default)
+
+ # apt-get install runit
+
+Make sure the dropbear service normally handled through the sysv init
+script is stopped
+
+ # /etc/init.d/dropbear stop
+
+Create the system user ``dropbearlog'' which will run the logger service,
+and own the logs
+
+ # adduser --system --home /var/log/dropbear --no-create-home dropbearlog
+
+Create the log directory and make the newly created system user the owner
+of this directory
+
+ # mkdir -p /var/log/dropbear && chown dropbearlog /var/log/dropbear
+
+Optionally adjust the configuration of the dropbear service by editing the
+run script
+
+ # vi /etc/dropbear/run
+
+Finally enable the service by linking dropbear's service directory to
+/var/service/. The service will be started within five seconds, and
+automatically at boot time. The sysv init script is disabled; see the
+runsvctrl(8) program for information on how to control services handled by
+runit. See the svlogd(8) program on how to configure the log service.
+
+ # ln -s /etc/dropbear /var/service/
+
+Optionally check the status of the service a few seconds later
+
+ # runsvstat -l /var/service/dropbear
+
+ -- Gerrit Pape <pape@smarden.org>, Sun, 16 May 2004 15:52:34 +0000
diff --git a/debian/changelog b/debian/changelog
new file mode 100644
index 0000000..89c5875
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,194 @@
+dropbear (0.47-0.1) unstable; urgency=high
+
+ * New upstream release.
+ * SECURITY: Fix incorrect buffer sizing.
+
+ -- Matt Johnston <matt@ucc.asn.au> Thu, 8 Dec 2005 19:20:21 +0800
+
+dropbear (0.46-2) unstable; urgency=low
+
+ * debian/control: Standards-Version: 3.6.2.1; update descriptions to
+ mention included server and client (thx Tino Keitel).
+ * debian/dropbear.init: allow '/etc/init.d/dropbear stop' even though
+ 'NO_START is not set to zero.' (closes: #336723).
+
+ -- Gerrit Pape <pape@smarden.org> Tue, 6 Dec 2005 13:30:49 +0000
+
+dropbear (0.46-1) unstable; urgency=medium
+
+ * New upstream release, various fixes.
+ * debian/diff/dbclient-usage-typo.diff, debian/diff/manpages.diff: remove;
+ obsolete.
+ * debian/dbclient.1: move to ./dbclient.1.
+
+ -- Matt Johnston <matt@ucc.asn.au> Fri, 8 July 2005 21:32:55 +0800
+
+dropbear (0.45-3) unstable; urgency=low
+
+ * debian/dropbear.init: init script prints human readable message in case
+ it's disabled (closes: #309099).
+ * debian/dropbear.postinst: configure: restart service through init script
+ instead of start.
+ * debian/dropbear.prerm: set -u -> set -e.
+
+ -- Gerrit Pape <pape@smarden.org> Wed, 25 May 2005 22:38:17 +0000
+
+dropbear (0.45-2) unstable; urgency=low
+
+ * Matt Johnston:
+ * New upstream release, various fixes.
+
+ -- Gerrit Pape <pape@smarden.org> Sat, 12 Mar 2005 15:17:55 +0000
+
+dropbear (0.44-1) unstable; urgency=low
+
+ * New upstream release.
+ * debian/rules: install /usr/bin/dbclient; handle possible patches more
+ gracefully; install debian/dbclient.1 man page; enable target patch;
+ minor.
+ * debian/implicit: update to revision 1.10.
+ * debian/dbclient.1: new; man page.
+ * debian/diff/dbclient-usage-typo.diff: new; fix typo.
+ * debian/diff/manpages.diff: new; add references to dbclient man page.
+
+ -- Gerrit Pape <pape@smarden.org> Sat, 8 Jan 2005 22:50:43 +0000
+
+dropbear (0.43-2) unstable; urgency=high
+
+ * Matt Johnston:
+ * New upstream release 0.43
+ * SECURITY: Don't attempt to free uninitialised buffers in DSS verification
+ code
+ * Handle portforwarding to servers which don't send any initial data
+ (Closes: #258426)
+ * debian/dropbear.postinst: remove code causing bothersome warning on
+ package install (closes: #256752).
+ * debian/README.Debian.diet: new; how to build with the diet libc.
+ * debian/dropbear.docs: add debian/README.Debian.diet.
+ * debian/rules: support "diet" in DEB_BUILD_OPTIONS; minor cleanup.
+
+ -- Gerrit Pape <pape@smarden.org> Sat, 17 Jul 2004 19:31:19 +0000
+
+dropbear (0.42-1) unstable; urgency=low
+
+ * New upstream release 0.42.
+ * debian/diff/cvs-20040520.diff: remove; obsolete.
+ * debian/rules: disable target patch.
+
+ -- Matt Johnston <matt@ucc.asn.au> Wed, 16 June 2004 12:44:54 +0800
+
+dropbear (0.41-3) unstable; urgency=low
+
+ * 1st upload to the Debian archive (closes: #216553).
+ * debian/diff/cvs-20040520.diff: new; stable cvs snapshot.
+ * debian/rules: new target patch: apply diffs in debian/diff/, reverse
+ apply in target clean; install man pages.
+ * debian/control: Priority: optional.
+
+ -- Gerrit Pape <pape@smarden.org> Sun, 23 May 2004 08:32:37 +0000
+
+dropbear (0.41-2) unstable; urgency=low
+
+ * new maintainer.
+ * debian/control: no longer Build-Depends: debhelper; Build-Depends:
+ libz-dev; Standards-Version: 3.6.1.0; Suggests: runit; update
+ descriptions.
+ * debian/rules: stop using debhelper, use implicit rules; cleanup;
+ install dropbearconvert into /usr/lib/dropbear/.
+ * debian/impicit: new; implicit rules.
+ * debian/copyright.in: adapt.
+ * debian/dropbear.init: minor adaptions; test for dropbear service
+ directory.
+ * debian/README.runit: new; how to use dropbear with runit.
+ * debian/README.Debian, debian/docs: rename to debian/dropbear.*.
+ * debian/dropbear.docs: add debian/README.runit
+ * debian/conffiles: rename to debian/dropbear.conffiles; add init
+ script, and run scripts.
+ * debian/postinst: rename to debian/dropbear.postinst; adapt; use
+ invloke-rc.d dropbear start.
+ * debian/dropbear.prerm: new; invoke-rc.d dropbear stop.
+ * debian/postrm: rename to debian/dropbear.postrm; adapt; clean up
+ service directories.
+ * debian/compat, debian/dirs, dropbear.default: remove; obsolete.
+
+ -- Gerrit Pape <pape@smarden.org> Sun, 16 May 2004 16:50:55 +0000
+
+dropbear (0.41-1) unstable; urgency=low
+
+ * Updated to 0.41 release.
+ * Various minor fixes
+
+ -- Matt Johnston <matt@ucc.asn.au> Mon, 19 Jan 2004 23:20:54 +0800
+
+dropbear (0.39-1) unstable; urgency=low
+
+ * updated to 0.39 release. Some new features, some bugfixes.
+
+ -- Matt Johnston <matt@ucc.asn.au> Tue, 16 Dec 2003 16:20:54 +0800
+
+dropbear (0.38-1) unstable; urgency=medium
+
+ * updated to 0.38 release - various important bugfixes
+
+ -- Matt Johnston <matt@ucc.asn.au> Sat, 11 Oct 2003 16:28:54 +0800
+
+dropbear (0.37-1) unstable; urgency=medium
+
+ * updated to 0.37 release - various important bugfixes
+
+ -- Matt Johnston <matt@ucc.asn.au> Wed, 24 Sept 2003 19:43:54 +0800
+
+dropbear (0.36-1) unstable; urgency=high
+
+ * updated to 0.36 release - various important bugfixes
+
+ -- Matt Johnston <matt@ucc.asn.au> Tues, 19 Aug 2003 12:20:54 +0800
+
+dropbear (0.35-1) unstable; urgency=high
+
+ * updated to 0.35 release - contains fix for remotely exploitable
+ vulnerability.
+
+ -- Matt Johnston <matt@ucc.asn.au> Sun, 17 Aug 2003 05:37:47 +0800
+
+dropbear (0.34-1) unstable; urgency=medium
+
+ * updated to 0.34 release
+
+ -- Matt Johnston <matt@ucc.asn.au> Fri, 15 Aug 2003 15:10:00 +0800
+
+dropbear (0.33-1) unstable; urgency=medium
+
+ * updated to 0.33 release
+
+ -- Matt Johnston <matt@ucc.asn.au> Sun, 22 Jun 2003 22:22:00 +0800
+
+dropbear (0.32cvs-1) unstable; urgency=medium
+
+ * now maintained in UCC CVS
+ * debian/copyright.in file added, generated from LICENSE
+
+ -- Grahame Bowland <grahame@angrygoats.net> Tue, 21 Jun 2003 17:57:02 +0800
+
+dropbear (0.32cvs-1) unstable; urgency=medium
+
+ * sync with CVS
+ * fixes X crash bug
+
+ -- Grahame Bowland <grahame@angrygoats.net> Tue, 20 Jun 2003 15:04:47 +0800
+
+dropbear (0.32-2) unstable; urgency=low
+
+ * fix creation of host keys to use correct names in /etc/dropbear
+ * init script "restart" function fixed
+ * purging this package now deletes the host keys and /etc/dropbear
+ * change priority in debian/control to 'standard'
+
+ -- Grahame Bowland <grahame@angrygoats.net> Tue, 17 Jun 2003 15:04:47 +0800
+
+dropbear (0.32-1) unstable; urgency=low
+
+ * Initial Release.
+
+ -- Grahame Bowland <grahame@angrygoats.net> Tue, 17 Jun 2003 15:04:47 +0800
+
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000..81835b3
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,20 @@
+Source: dropbear
+Section: net
+Priority: optional
+Maintainer: Gerrit Pape <pape@smarden.org>
+Build-Depends: libz-dev
+Standards-Version: 3.6.2.1
+
+Package: dropbear
+Architecture: any
+Depends: ${shlibs:Depends}
+Suggests: ssh, runit
+Description: lightweight SSH2 server and client
+ dropbear is a SSH 2 server and client designed to be small enough to
+ be used in small memory environments, while still being functional and
+ secure enough for general use.
+ .
+ It implements most required features of the SSH 2 protocol, and other
+ features such as X11 and authentication agent forwarding.
+ .
+ See http://matt.ucc.asn.au/dropbear/dropbear.html
diff --git a/debian/copyright.in b/debian/copyright.in
new file mode 100644
index 0000000..79526d3
--- /dev/null
+++ b/debian/copyright.in
@@ -0,0 +1,11 @@
+This package was debianized by Grahame Bowland <grahame.angrygoats.net> on
+Tue, 17 Jun 2003 15:04:47 +0800, maintained temporarily by Matt Johnston
+<matt@ucc.asn.au>, and was adopted by Gerrit Pape <pape@smarden.org> on
+Sun, 16 May 2004 14:38:33 +0000.
+
+It was downloaded from http://matt.ucc.asn.au/dropbear/
+
+Upstream Author: Matt Johnston <matt@ucc.asn.au>
+
+Copyright:
+
diff --git a/debian/dropbear.README.Debian b/debian/dropbear.README.Debian
new file mode 100644
index 0000000..8cdac38
--- /dev/null
+++ b/debian/dropbear.README.Debian
@@ -0,0 +1,41 @@
+Dropbear for Debian
+-------------------
+
+This package will attempt to listen on port 22. If the OpenSSH
+package ("ssh") is installed, the file /etc/default/dropbear
+will be set up so that the server does not start by default.
+
+You can run Dropbear concurrently with OpenSSH 'sshd' by
+modifying /etc/default/dropbear so that "NO_START" is set to
+"0" and changing the port number that Dropbear runs on. Follow
+the instructions in the file.
+
+This package suggests you install the "ssh" package. This package
+provides the "ssh" client program, as well as the "/usr/bin/scp"
+binary you will need to be able to retrieve files from a server
+running Dropbear via SCP.
+
+Replacing OpenSSH "sshd" with Dropbear
+--------------------------------------
+
+You will still want to have the "ssh" package installed, as it
+provides the "ssh" and "scp" binaries. When you install this
+package, it checks for existing OpenSSH host keys and if found,
+converts them to the Dropbear format.
+
+If this appears to have worked, you should be able to change over
+by following these steps:
+
+1. Stop the OpenSSH server
+ % /etc/init.d/ssh stop
+2. Prevent the OpenSSH server from starting in the future
+ % touch /etc/ssh/sshd_not_to_be_run
+3. Modify the Dropbear defaults file, set NO_START to 0 and
+ ensure DROPBEAR_PORT is set to 22.
+ % editor /etc/default/dropbear
+4. Restart the Dropbear server.
+ % /etc/init.d/dropbear restart
+
+See the Dropbear homepage for more information:
+ http://matt.ucc.asn.au/dropbear/dropbear.html
+
diff --git a/debian/dropbear.conffiles b/debian/dropbear.conffiles
new file mode 100644
index 0000000..6919006
--- /dev/null
+++ b/debian/dropbear.conffiles
@@ -0,0 +1,3 @@
+/etc/init.d/dropbear
+/etc/dropbear/run
+/etc/dropbear/log/run
diff --git a/logs/invmod.log b/debian/dropbear.default
index e69de29..e69de29 100644
--- a/logs/invmod.log
+++ b/debian/dropbear.default
diff --git a/debian/dropbear.docs b/debian/dropbear.docs
new file mode 100644
index 0000000..94fec74
--- /dev/null
+++ b/debian/dropbear.docs
@@ -0,0 +1,4 @@
+README
+TODO
+debian/README.runit
+debian/README.Debian.diet
diff --git a/debian/dropbear.init b/debian/dropbear.init
new file mode 100644
index 0000000..7979c8d
--- /dev/null
+++ b/debian/dropbear.init
@@ -0,0 +1,61 @@
+#!/bin/sh
+#
+# Do not configure this file. Edit /etc/default/dropbear instead!
+#
+
+PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
+DAEMON=/usr/sbin/dropbear
+NAME=dropbear
+DESC="Dropbear SSH server"
+
+DROPBEAR_PORT=22
+DROPBEAR_EXTRA_ARGS=
+NO_START=0
+
+set -e
+
+cancel() { echo "$1" >&2; exit 0; };
+test ! -r /etc/default/dropbear || . /etc/default/dropbear
+test -x "$DAEMON" || cancel "$DAEMON does not exist or is not executable."
+test ! -h /var/service/dropbear || \
+ cancel '/var/service/dropbear exists, service is controlled through runit.'
+
+test -z "$DROPBEAR_BANNER" || \
+ DROPBEAR_EXTRA_ARGS="$DROPBEAR_EXTRA_ARGS -b $DROPBEAR_BANNER"
+test -n "$DROPBEAR_RSAKEY" || \
+ DROPBEAR_RSAKEY="/etc/dropbear/dropbear_rsa_host_key"
+test -n "$DROPBEAR_DSSKEY" || \
+ DROPBEAR_DSSKEY="/etc/dropbear/dropbear_dss_host_key"
+
+case "$1" in
+ start)
+ test "$NO_START" = "0" || cancel 'NO_START is not set to zero.'
+ echo -n "Starting $DESC: "
+ start-stop-daemon --start --quiet --pidfile /var/run/"$NAME".pid \
+ --exec "$DAEMON" -- -d "$DROPBEAR_DSSKEY" -r "$DROPBEAR_RSAKEY" \
+ -p "$DROPBEAR_PORT" $DROPBEAR_EXTRA_ARGS
+ echo "$NAME."
+ ;;
+ stop)
+ echo -n "Stopping $DESC: "
+ start-stop-daemon --stop --quiet --oknodo --pidfile /var/run/"$NAME".pid
+ echo "$NAME."
+ ;;
+ restart|force-reload)
+ test "$NO_START" = "0" || cancel 'NO_START is not set to zero.'
+ echo -n "Restarting $DESC: "
+ start-stop-daemon --stop --quiet --oknodo --pidfile /var/run/"$NAME".pid
+ sleep 1
+ start-stop-daemon --start --quiet --pidfile /var/run/"$NAME".pid \
+ --exec "$DAEMON" -- -d "$DROPBEAR_DSSKEY" -r "$DROPBEAR_RSAKEY" \
+ -p "$DROPBEAR_PORT" $DROPBEAR_EXTRA_ARGS
+ echo "$NAME."
+ ;;
+ *)
+ N=/etc/init.d/$NAME
+ echo "Usage: $N {start|stop|restart|force-reload}" >&2
+ exit 1
+ ;;
+esac
+
+exit 0
diff --git a/debian/dropbear.postinst b/debian/dropbear.postinst
new file mode 100644
index 0000000..312eb05
--- /dev/null
+++ b/debian/dropbear.postinst
@@ -0,0 +1,67 @@
+#!/bin/sh
+set -e
+
+test "$1" = 'configure' || exit 0
+
+if test ! -e /etc/dropbear/dropbear_rsa_host_key; then
+ if test -f /etc/ssh/ssh_host_rsa_key; then
+ echo "Converting existing OpenSSH RSA host key to Dropbear format."
+ /usr/lib/dropbear/dropbearconvert openssh dropbear \
+ /etc/ssh/ssh_host_rsa_key /etc/dropbear/dropbear_rsa_host_key
+ else
+ echo "Generating Dropbear RSA key. Please wait."
+ dropbearkey -t rsa -f /etc/dropbear/dropbear_rsa_host_key
+ fi
+fi
+if test ! -e /etc/dropbear/dropbear_dss_host_key; then
+ if test -f /etc/ssh/ssh_host_dsa_key; then
+ echo "Converting existing OpenSSH RSA host key to Dropbear format."
+ /usr/lib/dropbear/dropbearconvert openssh dropbear \
+ /etc/ssh/ssh_host_dsa_key /etc/dropbear/dropbear_dss_host_key
+ else
+ echo "Generating Dropbear DSS key. Please wait."
+ dropbearkey -t dss -f /etc/dropbear/dropbear_dss_host_key
+ fi
+fi
+if test ! -s /etc/default/dropbear; then
+ # check whether OpenSSH seems to be installed.
+ if test -x /usr/sbin/sshd; then
+ cat <<EOT
+OpenSSH appears to be installed. Setting /etc/default/dropbear so that
+Dropbear will not start by default. Edit this file to change this behaviour.
+
+EOT
+ cat >>/etc/default/dropbear <<EOT
+# disabled because OpenSSH is installed
+# change to NO_START=0 to enable Dropbear
+NO_START=1
+
+EOT
+ fi
+ cat >>/etc/default/dropbear <<EOT
+# the TCP port that Dropbear listens on
+DROPBEAR_PORT=22
+
+# any additional arguments for Dropbear
+DROPBEAR_EXTRA_ARGS=
+
+# specify an optional banner file containing a message to be
+# sent to clients before they connect, such as "/etc/issue.net"
+DROPBEAR_BANNER=""
+
+# RSA hostkey file (default: /etc/dropbear/dropbear_rsa_host_key)
+#DROPBEAR_RSAKEY="/etc/dropbear/dropbear_rsa_host_key"
+
+# DSS hostkey file (default: /etc/dropbear/dropbear_dss_host_key)
+#DROPBEAR_DSSKEY="/etc/dropbear/dropbear_dss_host_key"
+EOT
+fi
+
+if test -x /etc/init.d/dropbear; then
+ update-rc.d dropbear defaults >/dev/null
+ if test -x /usr/sbin/invoke-rc.d; then
+ invoke-rc.d dropbear restart
+ else
+ /etc/init.d/dropbear restart
+ fi
+fi
diff --git a/debian/dropbear.postrm b/debian/dropbear.postrm
new file mode 100644
index 0000000..d09dab0
--- /dev/null
+++ b/debian/dropbear.postrm
@@ -0,0 +1,12 @@
+#! /bin/sh
+set -e
+
+test "$1" = 'purge' || exit 0
+if test -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
+rm -f /etc/default/dropbear
+rm -rf /etc/dropbear/supervise /etc/dropbear/log/supervise
diff --git a/debian/dropbear.prerm b/debian/dropbear.prerm
new file mode 100644
index 0000000..e63cdb8
--- /dev/null
+++ b/debian/dropbear.prerm
@@ -0,0 +1,11 @@
+#!/bin/sh
+set -e
+
+test "$1" = 'remove' || test "$1" = 'deconfigure' || exit 0
+if test -x /etc/init.d/dropbear; then
+ if test -x /usr/sbin/invoke-rc.d; then
+ invoke-rc.d dropbear stop
+ else
+ /etc/init.d/dropbear stop
+ fi
+fi
diff --git a/debian/implicit b/debian/implicit
new file mode 100644
index 0000000..57a444a
--- /dev/null
+++ b/debian/implicit
@@ -0,0 +1,85 @@
+# $Id: implicit,v 1.10 2004/07/03 15:20:00 pape Exp $
+
+.PHONY: deb-checkdir deb-checkuid
+
+deb-checkdir:
+ @test -e debian/control || sh -cx '! : wrong directory'
+deb-checkuid:
+ @test "`id -u`" -eq 0 || sh -cx '! : need root privileges'
+
+%.deb: %.deb-docs %.deb-DEBIAN
+ @rm -f $*.deb $*.deb-checkdir $*.deb-docs $*.deb-docs-base \
+ $*.deb-docs-docs $*.deb-docs-examples $*.deb-DEBIAN \
+ $*.deb-DEBIAN-dir $*.deb-DEBIAN-scripts $*.deb-DEBIAN-md5sums
+
+%.udeb: %.deb-DEBIAN
+ @rm -f $*.deb $*.deb-checkdir $*.deb-DEBIAN $*.deb-DEBIAN-dir \
+ $*.deb-DEBIAN-scripts $*.deb-DEBIAN-md5sums
+
+%.deb-checkdir:
+ @test -d debian/$* || sh -cx '! : directory debian/$* missing'
+ @test "`id -u`" -eq 0 || sh -cx '! : need root privileges'
+
+%.deb-docs-base:
+ : implicit
+ @rm -f debian/$*/usr/share/doc/$*/* || :
+ @install -d -m0755 debian/$*/usr/share/doc/$*
+ : debian/$*/usr/share/doc/$*/
+ @sh -cx 'install -m0644 debian/copyright debian/$*/usr/share/doc/$*/'
+ @sh -cx 'install -m0644 debian/changelog \
+ debian/$*/usr/share/doc/$*/changelog.Debian'
+ @test ! -r changelog || \
+ sh -cx 'install -m0644 changelog debian/$*/usr/share/doc/$*/'
+ @test -r debian/$*/usr/share/doc/$*/changelog || \
+ sh -cx 'mv debian/$*/usr/share/doc/$*/changelog.Debian \
+ debian/$*/usr/share/doc/$*/changelog'
+ @test -s debian/$*/usr/share/doc/$*/changelog || \
+ sh -cx 'rm -f debian/$*/usr/share/doc/$*/changelog'
+ @gzip -9 debian/$*/usr/share/doc/$*/changelog*
+%.deb-docs-docs:
+ @for i in `cat debian/$*.docs 2>/dev/null || :`; do \
+ sh -cx "install -m0644 $$i debian/$*/usr/share/doc/$*/" || exit 1; \
+ done
+ @test ! -r debian/$*.README.Debian || \
+ sh -cx 'install -m0644 debian/$*.README.Debian \
+ debian/$*/usr/share/doc/$*/README.Debian'
+ @if test -r debian/$*.NEWS.Debian; then \
+ sh -cx 'install -m0644 debian/$*.NEWS.Debian \
+ debian/$*/usr/share/doc/$*/NEWS.Debian && \
+ gzip -9 debian/$*/usr/share/doc/$*/NEWS.Debian'; \
+ fi
+%.deb-docs-examples:
+ @rm -rf debian/$*/usr/share/doc/$*/examples
+ : debian/$*/usr/share/doc/$*/examples/
+ @test ! -r debian/$*.examples || \
+ install -d -m0755 debian/$*/usr/share/doc/$*/examples
+ @for i in `cat debian/$*.examples 2>/dev/null || :`; do \
+ sh -cx "install -m0644 $$i debian/$*/usr/share/doc/$*/examples/" \
+ || exit 1; \
+ done
+%.deb-docs: %.deb-checkdir %.deb-docs-base %.deb-docs-docs %.deb-docs-examples
+ : debian/$*/usr/share/doc/$*/ ok
+
+%.deb-DEBIAN-base:
+ @rm -rf debian/$*/DEBIAN
+ : debian/$*/DEBIAN/
+ @install -d -m0755 debian/$*/DEBIAN
+ @for i in conffiles shlibs templates; do \
+ test ! -r debian/$*.$$i || \
+ sh -cx "install -m0644 debian/$*.$$i debian/$*/DEBIAN/$$i" \
+ || exit 1; \
+ done
+%.deb-DEBIAN-scripts:
+ @for i in preinst prerm postinst postrm config; do \
+ test ! -r debian/$*.$$i || \
+ sh -cx "install -m0755 debian/$*.$$i debian/$*/DEBIAN/$$i" \
+ || exit 1; \
+ done
+%.deb-DEBIAN-md5sums:
+ : debian/$*/DEBIAN/md5sums
+ @rm -f debian/$*/DEBIAN/md5sums
+ @cd debian/$* && find * -path 'DEBIAN' -prune -o \
+ -type f -exec md5sum {} >>DEBIAN/md5sums \;
+%.deb-DEBIAN: %.deb-checkdir %.deb-DEBIAN-base %.deb-DEBIAN-scripts \
+ %.deb-DEBIAN-md5sums
+ : debian/$*/DEBIAN/ ok
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 0000000..52c3ea8
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,107 @@
+#!/usr/bin/make -f
+
+#export DH_OPTIONS
+DEB_HOST_GNU_TYPE ?=$(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
+DEB_BUILD_GNU_TYPE ?=$(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
+
+STRIP =strip
+ifneq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
+ STRIP =: nostrip
+endif
+
+CFLAGS =-Wall -g
+ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
+ CFLAGS +=-O0
+else
+ CFLAGS +=-O2
+endif
+
+CONFFLAGS =
+CC =gcc
+ifneq (,$(findstring diet,$(DEB_BUILD_OPTIONS)))
+ CONFFLAGS =--disable-zlib
+ CC =diet -v -Os gcc -nostdinc
+endif
+
+DIR =$(shell pwd)/debian/dropbear
+
+patch: deb-checkdir patch-stamp
+patch-stamp:
+ for i in `ls -1 debian/diff/*.diff || :`; do \
+ patch -p0 <$$i || exit 1; \
+ done
+ touch patch-stamp
+
+config.status: patch-stamp configure
+ CC='$(CC)' \
+ CFLAGS='$(CFLAGS)'' -DSFTPSERVER_PATH="\"/usr/lib/sftp-server\""' \
+ ./configure --host='$(DEB_HOST_GNU_TYPE)' \
+ --build='$(DEB_BUILD_GNU_TYPE)' --prefix=/usr \
+ --mandir=\$${prefix}/share/man --infodir=\$${prefix}/share/info \
+ $(CONFFLAGS)
+
+build: deb-checkdir build-stamp
+build-stamp: config.status
+ $(MAKE) CC='$(CC)' LD='$(CC)'
+ touch build-stamp
+
+clean: deb-checkdir deb-checkuid
+ -$(MAKE) distclean
+ test ! -e patch-stamp || \
+ for i in `ls -1r debian/diff/*.diff || :`; do \
+ patch -p0 -R <$$i; \
+ done
+ rm -f patch-stamp build-stamp config.log config.status
+ rm -rf '$(DIR)'
+ rm -f debian/files debian/substvars debian/copyright changelog
+
+install: deb-checkdir deb-checkuid build-stamp
+ rm -rf '$(DIR)'
+ install -d -m0755 '$(DIR)'/etc/dropbear
+ # programs
+ install -d -m0755 '$(DIR)'/usr/sbin
+ install -m0755 dropbear '$(DIR)'/usr/sbin/dropbear
+ install -d -m0755 '$(DIR)'/usr/bin
+ install -m0755 dbclient '$(DIR)'/usr/bin/dbclient
+ install -m0755 dropbearkey '$(DIR)'/usr/bin/dropbearkey
+ install -d -m0755 '$(DIR)'/usr/lib/dropbear
+ install -m0755 dropbearconvert \
+ '$(DIR)'/usr/lib/dropbear/dropbearconvert
+ $(STRIP) -R .comment -R .note '$(DIR)'/usr/sbin/* \
+ '$(DIR)'/usr/bin/* '$(DIR)'/usr/lib/dropbear/*
+ # init and run scripts
+ install -d -m0755 '$(DIR)'/etc/init.d
+ install -m0755 debian/dropbear.init '$(DIR)'/etc/init.d/dropbear
+ install -m0755 debian/service/run '$(DIR)'/etc/dropbear/run
+ install -d -m0755 '$(DIR)'/etc/dropbear/log
+ install -m0755 debian/service/log '$(DIR)'/etc/dropbear/log/run
+ ln -s /var/log/dropbear '$(DIR)'/etc/dropbear/log/main
+ ln -s /var/run/dropbear '$(DIR)'/etc/dropbear/supervise
+ ln -s /var/run/dropbear.log '$(DIR)'/etc/dropbear/log/supervise
+ # man pages
+ install -d -m0755 '$(DIR)'/usr/share/man/man8
+ for i in dropbear.8 dropbearkey.8; do \
+ install -m644 $$i '$(DIR)'/usr/share/man/man8/ || exit 1; \
+ done
+ gzip -9 '$(DIR)'/usr/share/man/man8/*.8
+ install -d -m0755 '$(DIR)'/usr/share/man/man1
+ install -m644 dbclient.1 '$(DIR)'/usr/share/man/man1/
+ gzip -9 '$(DIR)'/usr/share/man/man1/*.1
+ # copyright, changelog
+ cat debian/copyright.in LICENSE >debian/copyright
+ test -r changelog || ln -s CHANGES changelog
+
+binary-indep:
+
+binary-arch: install dropbear.deb
+ test '$(CC)' != 'gcc' || \
+ dpkg-shlibdeps '$(DIR)'/usr/sbin/* '$(DIR)'/usr/bin/* \
+ '$(DIR)'/usr/lib/dropbear/*
+ dpkg-gencontrol -isp -pdropbear -P'$(DIR)'
+ dpkg -b '$(DIR)' ..
+
+binary: binary-arch binary-indep
+
+.PHONY: patch build clean install binary-indep binary-arch binary
+
+include debian/implicit
diff --git a/debian/service/log b/debian/service/log
new file mode 100644
index 0000000..2ffb13d
--- /dev/null
+++ b/debian/service/log
@@ -0,0 +1,2 @@
+#!/bin/sh
+exec chpst -udropbearlog svlogd -tt ./main
diff --git a/debian/service/run b/debian/service/run
new file mode 100644
index 0000000..f208085
--- /dev/null
+++ b/debian/service/run
@@ -0,0 +1,3 @@
+#!/bin/sh
+exec 2>&1
+exec dropbear -d ./dropbear_dss_host_key -r ./dropbear_rsa_host_key -F -E -p 22
diff --git a/debug.h b/debug.h
new file mode 100644
index 0000000..93cb891
--- /dev/null
+++ b/debug.h
@@ -0,0 +1,74 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+#ifndef _DEBUG_H_
+#define _DEBUG_H_
+
+#include "includes.h"
+
+/* Debugging */
+
+/* Work well for valgrind - don't clear environment, be nicer with signals
+ * etc. Don't use this normally, it might cause problems */
+/* #define DEBUG_VALGRIND */
+
+/* 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
+ * attempting to track down a problem */
+#define CHECKCLEARTOWRITE() assert(ses.writepayload->len == 0 && \
+ ses.writepayload->pos == 0)
+
+/* 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 */
+
+/*#define DEBUG_KEXHASH*/
+/*#define DEBUG_RSA*/
+
+/* you don't need to touch this block */
+#ifdef DEBUG_TRACE
+#define TRACE(X) dropbear_trace X;
+#else /*DEBUG_TRACE*/
+#define TRACE(X)
+#endif /*DEBUG_TRACE*/
+
+/* For testing as non-root on shadowed systems, include the crypt of a password
+ * here. You can then log in as any user with this password. Ensure that you
+ * make your own password, and are careful about using this. This will also
+ * disable some of the chown pty code etc*/
+/* #define DEBUG_HACKCRYPT "hL8nrFDt0aJ3E" */ /* this is crypt("password") */
+
+#endif
diff --git a/dropbear.8 b/dropbear.8
new file mode 100644
index 0000000..38cf7e2
--- /dev/null
+++ b/dropbear.8
@@ -0,0 +1,84 @@
+.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.
+.TP
+.B \-a
+Allow remote hosts to connect to forwarded ports.
+.SH AUTHOR
+Matt Johnston (matt@ucc.asn.au).
+.br
+Gerrit Pape (pape@smarden.org) wrote this manual page.
+.SH SEE ALSO
+dropbearkey(8), dbclient(1)
+.P
+http://matt.ucc.asn.au/dropbear/dropbear.html
diff --git a/dropbearconvert.c b/dropbearconvert.c
new file mode 100644
index 0000000..9e16fe7
--- /dev/null
+++ b/dropbearconvert.c
@@ -0,0 +1,149 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+/* This program converts to/from Dropbear and OpenSSH private-key formats */
+#include "includes.h"
+#include "signkey.h"
+#include "buffer.h"
+#include "dbutil.h"
+#include "keyimport.h"
+
+
+static int do_convert(int intype, const char* infile, int outtype,
+ const char* outfile);
+
+static void printhelp(char * progname);
+
+static void printhelp(char * progname) {
+
+ fprintf(stderr, "Usage: %s <inputtype> <outputtype> <inputfile> <outputfile>\n\n"
+ "CAUTION: This program is for convenience only, and is not secure if used on\n"
+ "untrusted input files, ie it could allow arbitrary code execution.\n"
+ "All parameters must be specified in order.\n"
+ "\n"
+ "The input and output types are one of:\n"
+ "openssh\n"
+ "dropbear\n"
+ "\n"
+ "Example:\n"
+ "dropbearconvert openssh dropbear /etc/ssh/ssh_host_rsa_key /etc/dropbear_rsa_host_key\n",
+ progname);
+}
+
+#if defined(DBMULTI_dropbearconvert) || !defined(DROPBEAR_MULTI)
+#if defined(DBMULTI_dropbearconvert) && defined(DROPBEAR_MULTI)
+int dropbearconvert_main(int argc, char ** argv) {
+#else
+int main(int argc, char ** argv) {
+#endif
+
+ int intype, outtype;
+ 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");
+ goto usage;
+ }
+
+ /* input type */
+ if (argv[1][0] == 'd') {
+ intype = KEYFILE_DROPBEAR;
+ } else if (argv[1][0] == 'o') {
+ intype = KEYFILE_OPENSSH;
+ } else {
+ fprintf(stderr, "Invalid input key type\n");
+ goto usage;
+ }
+
+ /* output type */
+ if (argv[2][0] == 'd') {
+ outtype = KEYFILE_DROPBEAR;
+ } else if (argv[2][0] == 'o') {
+ outtype = KEYFILE_OPENSSH;
+ } else {
+ fprintf(stderr, "Invalid output key type\n");
+ goto usage;
+ }
+
+ /* we don't want output readable by others */
+ umask(077);
+
+ infile = argv[3];
+ outfile = argv[4];
+
+ return do_convert(intype, infile, outtype, outfile);
+
+usage:
+ printhelp(argv[0]);
+ return 1;
+}
+#endif
+
+static int do_convert(int intype, const char* infile, int outtype,
+ const char* outfile) {
+
+ sign_key * key = NULL;
+ char * keytype = NULL;
+ int ret = 1;
+
+ key = import_read(infile, NULL, intype);
+ if (!key) {
+ fprintf(stderr, "Error reading key from '%s'\n",
+ infile);
+ goto out;
+ }
+
+#ifdef DROPBEAR_RSA
+ if (key->rsakey != NULL) {
+ keytype = "RSA";
+ }
+#endif
+#ifdef DROPBEAR_DSS
+ if (key->dsskey != NULL) {
+ keytype = "DSS";
+ }
+#endif
+
+ fprintf(stderr, "Key is a %s key\n", keytype);
+
+ if (import_write(outfile, key, NULL, outtype) != 1) {
+ fprintf(stderr, "Error writing key to '%s'\n", outfile);
+ } else {
+ fprintf(stderr, "Wrote key to '%s'\n", outfile);
+ ret = 0;
+ }
+
+out:
+ if (key) {
+ sign_key_free(key);
+ }
+ return ret;
+}
diff --git a/dropbearkey.8 b/dropbearkey.8
new file mode 100644
index 0000000..a093d85
--- /dev/null
+++ b/dropbearkey.8
@@ -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), dbclient(1)
+.P
+http://matt.ucc.asn.au/dropbear/dropbear.html
diff --git a/dropbearkey.c b/dropbearkey.c
new file mode 100644
index 0000000..8ceefdc
--- /dev/null
+++ b/dropbearkey.c
@@ -0,0 +1,355 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+/* The format of the keyfiles is basically a raw dump of the buffer. Data types
+ * are specified in the transport draft - string is a 32-bit len then the
+ * non-null-terminated string, mp_int is a 32-bit len then the bignum data.
+ * The actual functions are buf_put_rsa_priv_key() and buf_put_dss_priv_key()
+
+ * RSA:
+ * string "ssh-rsa"
+ * mp_int e
+ * mp_int n
+ * mp_int d
+ * mp_int p (newer versions only)
+ * mp_int q (newer versions only)
+ *
+ * DSS:
+ * string "ssh-dss"
+ * mp_int p
+ * mp_int q
+ * mp_int g
+ * mp_int y
+ * mp_int x
+ *
+ */
+#include "includes.h"
+#include "signkey.h"
+#include "buffer.h"
+#include "dbutil.h"
+
+#include "genrsa.h"
+#include "gendss.h"
+
+static void printhelp(char * progname);
+
+#define RSA_SIZE (1024/8) /* 1024 bit */
+#define DSS_SIZE (1024/8) /* 1024 bit */
+
+static void buf_writefile(buffer * buf, const char * filename);
+static void printpubkey(sign_key * key, int keytype);
+static void justprintpub(const char* filename);
+
+/* Print a help message */
+static void printhelp(char * progname) {
+
+ fprintf(stderr, "Usage: %s -t <type> -f <filename> [-s bits]\n"
+ "Options are:\n"
+ "-t type Type of key to generate. One of:\n"
+#ifdef DROPBEAR_RSA
+ " rsa\n"
+#endif
+#ifdef DROPBEAR_DSS
+ " dss\n"
+#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"
+#ifdef DEBUG_TRACE
+ "-v verbose\n"
+#endif
+ ,progname);
+}
+
+#if defined(DBMULTI_dropbearkey) || !defined(DROPBEAR_MULTI)
+#if defined(DBMULTI_dropbearkey) && defined(DROPBEAR_MULTI)
+int dropbearkey_main(int argc, char ** argv) {
+#else
+int main(int argc, char ** argv) {
+#endif
+
+ int i;
+ char ** next = 0;
+ sign_key *key = NULL;
+ buffer *buf = NULL;
+ char * filename = NULL;
+ int keytype = -1;
+ char * typetext = NULL;
+ char * sizetext = NULL;
+ unsigned int bits;
+ unsigned int keysize;
+ int printpub = 0;
+
+ /* get the commandline options */
+ for (i = 1; i < argc; i++) {
+ if (argv[i] == NULL) {
+ continue; /* Whack */
+ }
+ if (next) {
+ *next = argv[i];
+ next = NULL;
+ continue;
+ }
+
+ if (argv[i][0] == '-') {
+ switch (argv[i][1]) {
+ case 'f':
+ next = &filename;
+ break;
+ case 't':
+ next = &typetext;
+ break;
+ case 's':
+ next = &sizetext;
+ break;
+ case 'y':
+ printpub = 1;
+ break;
+ case 'h':
+ 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]);
+ exit(EXIT_FAILURE);
+ break;
+ }
+ }
+ }
+
+ if (!filename) {
+ fprintf(stderr, "Must specify a key filename\n");
+ printhelp(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ if (printpub) {
+ justprintpub(filename);
+ /* Not reached */
+ }
+
+ /* check/parse args */
+ if (!typetext) {
+ fprintf(stderr, "Must specify key type\n");
+ printhelp(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ if (strlen(typetext) == 3) {
+#ifdef DROPBEAR_RSA
+ if (strncmp(typetext, "rsa", 3) == 0) {
+ keytype = DROPBEAR_SIGNKEY_RSA;
+ TRACE(("type is rsa"))
+ }
+#endif
+#ifdef DROPBEAR_DSS
+ if (strncmp(typetext, "dss", 3) == 0) {
+ keytype = DROPBEAR_SIGNKEY_DSS;
+ TRACE(("type is dss"))
+ }
+#endif
+ }
+ if (keytype == -1) {
+ fprintf(stderr, "Unknown key type '%s'\n", typetext);
+ printhelp(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ if (sizetext) {
+ if (sscanf(sizetext, "%u", &bits) != 1) {
+ fprintf(stderr, "Bits must be an integer\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (bits < 512 || bits > 4096 || (bits % 8 != 0)) {
+ fprintf(stderr, "Bits must satisfy 512 <= bits <= 4096, and be a"
+ " multiple of 8\n");
+ exit(EXIT_FAILURE);
+ }
+
+ keysize = bits / 8;
+ } else {
+ if (keytype == DROPBEAR_SIGNKEY_DSS) {
+ keysize = DSS_SIZE;
+ } else if (keytype == DROPBEAR_SIGNKEY_RSA) {
+ keysize = RSA_SIZE;
+ } else {
+ exit(EXIT_FAILURE); /* not reached */
+ }
+ }
+
+
+ fprintf(stderr, "Will output %d bit %s secret key to '%s'\n", keysize*8,
+ typetext, filename);
+
+ /* don't want the file readable by others */
+ umask(077);
+
+ /* now we can generate the key */
+ key = new_sign_key();
+
+ fprintf(stderr, "Generating key, this may take a while...\n");
+ switch(keytype) {
+#ifdef DROPBEAR_RSA
+ case DROPBEAR_SIGNKEY_RSA:
+ key->rsakey = gen_rsa_priv_key(keysize); /* 128 bytes = 1024 bit */
+ break;
+#endif
+#ifdef DROPBEAR_DSS
+ case DROPBEAR_SIGNKEY_DSS:
+ key->dsskey = gen_dss_priv_key(keysize); /* 128 bytes = 1024 bit */
+ break;
+#endif
+ default:
+ fprintf(stderr, "Internal error, bad key type\n");
+ exit(EXIT_FAILURE);
+ }
+
+ buf = buf_new(MAX_PRIVKEY_SIZE);
+
+ buf_put_priv_key(buf, key, keytype);
+ buf_setpos(buf, 0);
+ buf_writefile(buf, filename);
+
+ buf_burn(buf);
+ buf_free(buf);
+
+ printpubkey(key, keytype);
+
+ sign_key_free(key);
+
+ return EXIT_SUCCESS;
+}
+#endif
+
+static void justprintpub(const char* filename) {
+
+ buffer *buf = NULL;
+ sign_key *key = NULL;
+ int keytype;
+ int ret;
+ int err = DROPBEAR_FAILURE;
+
+ buf = buf_new(MAX_PRIVKEY_SIZE);
+ ret = buf_readfile(buf, filename);
+
+ if (ret != DROPBEAR_SUCCESS) {
+ fprintf(stderr, "Failed reading '%s'\n", filename);
+ goto out;
+ }
+
+ key = new_sign_key();
+ keytype = DROPBEAR_SIGNKEY_ANY;
+
+ buf_setpos(buf, 0);
+ ret = buf_get_priv_key(buf, key, &keytype);
+ if (ret == DROPBEAR_FAILURE) {
+ fprintf(stderr, "Bad key in '%s'\n", filename);
+ goto out;
+ }
+
+ printpubkey(key, keytype);
+
+ err = DROPBEAR_SUCCESS;
+
+out:
+ buf_burn(buf);
+ buf_free(buf);
+ buf = NULL;
+ sign_key_free(key);
+ key = NULL;
+ exit(err);
+}
+
+static void printpubkey(sign_key * key, int keytype) {
+
+ buffer * buf = NULL;
+ unsigned char base64key[MAX_PUBKEY_SIZE*2];
+ unsigned long base64len;
+ int err;
+ const char * typestring = NULL;
+ char *fp = NULL;
+ int len;
+
+ buf = buf_new(MAX_PUBKEY_SIZE);
+ buf_put_pub_key(buf, key, keytype);
+ buf_setpos(buf, 4);
+
+ len = buf->len - buf->pos;
+
+ base64len = sizeof(base64key);
+ err = base64_encode(buf_getptr(buf, len), len, base64key, &base64len);
+
+ if (err != CRYPT_OK) {
+ fprintf(stderr, "base64 failed");
+ }
+
+ typestring = signkey_name_from_type(keytype, &err);
+
+ fp = sign_key_fingerprint(buf_getptr(buf, len), len);
+
+ printf("Public key portion is:\n%s %s\nFingerprint: %s\n",
+ typestring, base64key, fp);
+
+ m_free(fp);
+ buf_free(buf);
+}
+
+/* Write a buffer to a file specified, failing if the file exists */
+static void buf_writefile(buffer * buf, const char * filename) {
+
+ int fd;
+ int len;
+
+ fd = open(filename, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
+ if (fd < 0) {
+ fprintf(stderr, "Couldn't create new file %s\n", filename);
+ perror("Reason");
+ buf_burn(buf);
+ exit(EXIT_FAILURE);
+ }
+
+ /* write the file now */
+ while (buf->pos != buf->len) {
+ len = write(fd, buf_getptr(buf, buf->len - buf->pos),
+ buf->len - buf->pos);
+ if (errno == EINTR) {
+ continue;
+ }
+ if (len <= 0) {
+ fprintf(stderr, "Failed writing file '%s'\n",filename);
+ perror("Reason");
+ exit(EXIT_FAILURE);
+ }
+ buf_incrpos(buf, len);
+ }
+
+ close(fd);
+}
diff --git a/dss.c b/dss.c
new file mode 100644
index 0000000..84a093c
--- /dev/null
+++ b/dss.c
@@ -0,0 +1,416 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+#include "includes.h"
+#include "dbutil.h"
+#include "bignum.h"
+#include "dss.h"
+#include "buffer.h"
+#include "ssh.h"
+#include "random.h"
+
+/* Handle DSS (Digital Signature Standard), aka DSA (D.S. Algorithm),
+ * operations, such as key reading, signing, verification. Key generation
+ * is in gendss.c, since it isn't required in the server itself.
+ *
+ * See FIPS186 or the Handbook of Applied Cryptography for details of the
+ * algorithm */
+
+#ifdef DROPBEAR_DSS
+
+/* Load a dss key from a buffer, initialising the values.
+ * The key will have the same format as buf_put_dss_key.
+ * These should be freed with dss_key_free.
+ * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
+int buf_get_dss_pub_key(buffer* buf, dss_key *key) {
+
+ TRACE(("enter buf_get_dss_pub_key"))
+ dropbear_assert(key != NULL);
+ key->p = m_malloc(sizeof(mp_int));
+ key->q = m_malloc(sizeof(mp_int));
+ key->g = m_malloc(sizeof(mp_int));
+ key->y = m_malloc(sizeof(mp_int));
+ m_mp_init_multi(key->p, key->q, key->g, key->y, NULL);
+ key->x = NULL;
+
+ buf_incrpos(buf, 4+SSH_SIGNKEY_DSS_LEN); /* int + "ssh-dss" */
+ if (buf_getmpint(buf, key->p) == DROPBEAR_FAILURE
+ || 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"))
+ 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"))
+ return DROPBEAR_FAILURE;
+ }
+
+ TRACE(("leave buf_get_dss_pub_key: success"))
+ return DROPBEAR_SUCCESS;
+}
+
+/* Same as buf_get_dss_pub_key, but reads a private "x" key at the end.
+ * Loads a private dss key from a buffer
+ * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
+int buf_get_dss_priv_key(buffer* buf, dss_key *key) {
+
+ int ret = DROPBEAR_FAILURE;
+
+ dropbear_assert(key != NULL);
+
+ ret = buf_get_dss_pub_key(buf, key);
+ if (ret == DROPBEAR_FAILURE) {
+ return DROPBEAR_FAILURE;
+ }
+
+ key->x = m_malloc(sizeof(mp_int));
+ m_mp_init(key->x);
+ ret = buf_getmpint(buf, key->x);
+
+ return ret;
+}
+
+
+/* Clear and free the memory used by a public or private key */
+void dss_key_free(dss_key *key) {
+
+ TRACE(("enter dsa_key_free"))
+ if (key == NULL) {
+ TRACE(("enter dsa_key_free: key == NULL"))
+ return;
+ }
+ if (key->p) {
+ mp_clear(key->p);
+ m_free(key->p);
+ }
+ if (key->q) {
+ mp_clear(key->q);
+ m_free(key->q);
+ }
+ if (key->g) {
+ mp_clear(key->g);
+ m_free(key->g);
+ }
+ if (key->y) {
+ mp_clear(key->y);
+ m_free(key->y);
+ }
+ if (key->x) {
+ mp_clear(key->x);
+ m_free(key->x);
+ }
+ m_free(key);
+ TRACE(("leave dsa_key_free"))
+}
+
+/* put the dss public key into the buffer in the required format:
+ *
+ * string "ssh-dss"
+ * mpint p
+ * mpint q
+ * mpint g
+ * mpint y
+ */
+void buf_put_dss_pub_key(buffer* buf, dss_key *key) {
+
+ dropbear_assert(key != NULL);
+ buf_putstring(buf, SSH_SIGNKEY_DSS, SSH_SIGNKEY_DSS_LEN);
+ buf_putmpint(buf, key->p);
+ buf_putmpint(buf, key->q);
+ buf_putmpint(buf, key->g);
+ buf_putmpint(buf, key->y);
+
+}
+
+/* Same as buf_put_dss_pub_key, but with the private "x" key appended */
+void buf_put_dss_priv_key(buffer* buf, dss_key *key) {
+
+ dropbear_assert(key != NULL);
+ buf_put_dss_pub_key(buf, key);
+ buf_putmpint(buf, key->x);
+
+}
+
+#ifdef DROPBEAR_SIGNKEY_VERIFY
+/* Verify a DSS signature (in buf) made on data by the key given.
+ * returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
+int buf_dss_verify(buffer* buf, dss_key *key, const unsigned char* data,
+ unsigned int len) {
+
+ unsigned char msghash[SHA1_HASH_SIZE];
+ hash_state hs;
+ int ret = DROPBEAR_FAILURE;
+ 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"))
+ dropbear_assert(key != NULL);
+
+ m_mp_init_multi(&val1, &val2, &val3, &val4, NULL);
+
+ /* get blob, check length */
+ string = buf_getstring(buf, &stringlen);
+ if (stringlen != 2*SHA1_HASH_SIZE) {
+ goto out;
+ }
+
+ /* hash the data */
+ sha1_init(&hs);
+ sha1_process(&hs, data, len);
+ sha1_done(&hs, msghash);
+
+ /* create the signature - s' and r' are the received signatures in buf */
+ /* w = (s')-1 mod q */
+ /* let val1 = s' */
+ bytes_to_mp(&val1, &string[SHA1_HASH_SIZE], SHA1_HASH_SIZE);
+
+ if (mp_cmp(&val1, key->q) != MP_LT) {
+ TRACE(("verify failed, s' >= q"))
+ goto out;
+ }
+ /* let val2 = w = (s')^-1 mod q*/
+ if (mp_invmod(&val1, key->q, &val2) != MP_OKAY) {
+ goto out;
+ }
+
+ /* u1 = ((SHA(M')w) mod q */
+ /* let val1 = SHA(M') = msghash */
+ bytes_to_mp(&val1, msghash, SHA1_HASH_SIZE);
+
+ /* let val3 = u1 = ((SHA(M')w) mod q */
+ if (mp_mulmod(&val1, &val2, key->q, &val3) != MP_OKAY) {
+ goto out;
+ }
+
+ /* u2 = ((r')w) mod q */
+ /* let val1 = r' */
+ bytes_to_mp(&val1, &string[0], SHA1_HASH_SIZE);
+ if (mp_cmp(&val1, key->q) != MP_LT) {
+ TRACE(("verify failed, r' >= q"))
+ goto out;
+ }
+ /* let val4 = u2 = ((r')w) mod q */
+ if (mp_mulmod(&val1, &val2, key->q, &val4) != MP_OKAY) {
+ goto out;
+ }
+
+ /* v = (((g)^u1 (y)^u2) mod p) mod q */
+ /* val2 = g^u1 mod p */
+ if (mp_exptmod(key->g, &val3, key->p, &val2) != MP_OKAY) {
+ goto out;
+ }
+ /* val3 = y^u2 mod p */
+ if (mp_exptmod(key->y, &val4, key->p, &val3) != MP_OKAY) {
+ goto out;
+ }
+ /* val4 = ((g)^u1 (y)^u2) mod p */
+ if (mp_mulmod(&val2, &val3, key->p, &val4) != MP_OKAY) {
+ goto out;
+ }
+ /* val2 = v = (((g)^u1 (y)^u2) mod p) mod q */
+ if (mp_mod(&val4, key->q, &val2) != MP_OKAY) {
+ goto out;
+ }
+
+ /* check whether signatures verify */
+ if (mp_cmp(&val2, &val1) == MP_EQ) {
+ /* good sig */
+ ret = DROPBEAR_SUCCESS;
+ }
+
+out:
+ mp_clear_multi(&val1, &val2, &val3, &val4, NULL);
+ m_free(string);
+
+ return ret;
+
+}
+#endif /* DROPBEAR_SIGNKEY_VERIFY */
+
+#ifdef DSS_PROTOK
+/* 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;
+}
+#endif
+
+/* Sign the data presented with key, writing the signature contents
+ * to the buffer
+ *
+ * When DSS_PROTOK is #defined:
+ * The alternate k generation method is based on the method used in PuTTY.
+ * In particular to avoid being vulnerable to attacks using flaws in random
+ * generation of k, we use the following:
+ *
+ * proto_k = SHA512 ( SHA512(x) || SHA160(message) )
+ * k = proto_k mod q
+ *
+ * Now we aren't relying on the random number generation to protect the private
+ * key x, which is a long term secret */
+void buf_put_dss_sign(buffer* buf, dss_key *key, const unsigned char* data,
+ unsigned int len) {
+
+ unsigned char msghash[SHA1_HASH_SIZE];
+ unsigned int writelen;
+ unsigned int i;
+#ifdef DSS_PROTOK
+ unsigned char privkeyhash[SHA512_HASH_SIZE];
+ unsigned char *privkeytmp;
+ unsigned char proto_k[SHA512_HASH_SIZE];
+ DEF_MP_INT(dss_protok);
+#endif
+ 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"))
+ dropbear_assert(key != NULL);
+
+ /* hash the data */
+ sha1_init(&hs);
+ sha1_process(&hs, data, len);
+ sha1_done(&hs, msghash);
+
+ m_mp_init_multi(&dss_k, &dss_temp1, &dss_temp2, &dss_r, &dss_s,
+ &dss_m, NULL);
+#ifdef DSS_PROTOK
+ /* hash the privkey */
+ privkeytmp = mptobytes(key->x, &i);
+ sha512_init(&hs);
+ sha512_process(&hs, "the quick brown fox jumped over the lazy dog", 44);
+ sha512_process(&hs, privkeytmp, i);
+ sha512_done(&hs, privkeyhash);
+ m_burn(privkeytmp, i);
+ m_free(privkeytmp);
+
+ /* calculate proto_k */
+ sha512_init(&hs);
+ sha512_process(&hs, privkeyhash, SHA512_HASH_SIZE);
+ sha512_process(&hs, msghash, SHA1_HASH_SIZE);
+ sha512_done(&hs, proto_k);
+
+ /* generate k */
+ m_mp_init(&dss_protok);
+ bytes_to_mp(&dss_protok, proto_k, SHA512_HASH_SIZE);
+ mp_mod(&dss_protok, key->q, &dss_k);
+ mp_clear(&dss_protok);
+ m_burn(proto_k, SHA512_HASH_SIZE);
+#else /* DSS_PROTOK not defined*/
+ gen_random_mpint(key->q, &dss_k);
+#endif
+
+ /* now generate the actual signature */
+ bytes_to_mp(&dss_m, msghash, SHA1_HASH_SIZE);
+
+ /* g^k mod p */
+ if (mp_exptmod(key->g, &dss_k, key->p, &dss_temp1) != MP_OKAY) {
+ dropbear_exit("dss error");
+ }
+ /* r = (g^k mod p) mod q */
+ if (mp_mod(&dss_temp1, key->q, &dss_r) != MP_OKAY) {
+ dropbear_exit("dss error");
+ }
+
+ /* x*r mod q */
+ if (mp_mulmod(&dss_r, key->x, key->q, &dss_temp1) != MP_OKAY) {
+ dropbear_exit("dss error");
+ }
+ /* (SHA1(M) + xr) mod q) */
+ if (mp_addmod(&dss_m, &dss_temp1, key->q, &dss_temp2) != MP_OKAY) {
+ dropbear_exit("dss error");
+ }
+
+ /* (k^-1) mod q */
+ if (mp_invmod(&dss_k, key->q, &dss_temp1) != MP_OKAY) {
+ dropbear_exit("dss error");
+ }
+
+ /* s = (k^-1(SHA1(M) + xr)) mod q */
+ if (mp_mulmod(&dss_temp1, &dss_temp2, key->q, &dss_s) != MP_OKAY) {
+ dropbear_exit("dss error");
+ }
+
+ buf_putstring(buf, SSH_SIGNKEY_DSS, SSH_SIGNKEY_DSS_LEN);
+ buf_putint(buf, 2*SHA1_HASH_SIZE);
+
+ writelen = mp_unsigned_bin_size(&dss_r);
+ dropbear_assert(writelen <= SHA1_HASH_SIZE);
+ /* need to pad to 160 bits with leading zeros */
+ for (i = 0; i < SHA1_HASH_SIZE - writelen; i++) {
+ buf_putbyte(buf, 0);
+ }
+ if (mp_to_unsigned_bin(&dss_r, buf_getwriteptr(buf, writelen))
+ != MP_OKAY) {
+ dropbear_exit("dss error");
+ }
+ mp_clear(&dss_r);
+ buf_incrwritepos(buf, writelen);
+
+ writelen = mp_unsigned_bin_size(&dss_s);
+ dropbear_assert(writelen <= SHA1_HASH_SIZE);
+ /* need to pad to 160 bits with leading zeros */
+ for (i = 0; i < SHA1_HASH_SIZE - writelen; i++) {
+ buf_putbyte(buf, 0);
+ }
+ if (mp_to_unsigned_bin(&dss_s, buf_getwriteptr(buf, writelen))
+ != MP_OKAY) {
+ dropbear_exit("dss error");
+ }
+ mp_clear(&dss_s);
+ buf_incrwritepos(buf, writelen);
+
+ mp_clear_multi(&dss_k, &dss_temp1, &dss_temp2, &dss_r, &dss_s,
+ &dss_m, NULL);
+
+ /* create the signature to return */
+
+ TRACE(("leave buf_put_dss_sign"))
+}
+
+#endif /* DROPBEAR_DSS */
diff --git a/dss.h b/dss.h
new file mode 100644
index 0000000..0b69256
--- /dev/null
+++ b/dss.h
@@ -0,0 +1,61 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+#ifndef _DSS_H_
+#define _DSS_H_
+
+#include "includes.h"
+#include "buffer.h"
+
+#ifdef DROPBEAR_DSS
+
+#define DSS_SIGNATURE_SIZE 4+SSH_SIGNKEY_DSS_LEN+4+2*SHA1_HASH_SIZE
+
+struct DSS_key {
+
+ mp_int* p;
+ mp_int* q;
+ mp_int* g;
+ mp_int* y;
+ mp_int* x;
+
+};
+
+typedef struct DSS_key dss_key;
+
+void buf_put_dss_sign(buffer* buf, dss_key *key, const unsigned char* data,
+ unsigned int len);
+#ifdef DROPBEAR_SIGNKEY_VERIFY
+int buf_dss_verify(buffer* buf, dss_key *key, const unsigned char* data,
+ unsigned int len);
+#endif
+int buf_get_dss_pub_key(buffer* buf, dss_key *key);
+int buf_get_dss_priv_key(buffer* buf, dss_key *key);
+void buf_put_dss_pub_key(buffer* buf, dss_key *key);
+void buf_put_dss_priv_key(buffer* buf, dss_key *key);
+void dss_key_free(dss_key *key);
+
+#endif /* DROPBEAR_DSS */
+
+#endif /* _DSS_H_ */
diff --git a/fake-rfc2553.c b/fake-rfc2553.c
new file mode 100644
index 0000000..afbea88
--- /dev/null
+++ b/fake-rfc2553.c
@@ -0,0 +1,227 @@
+/*
+ *
+ * Taken from OpenSSH 3.8.1p1
+ *
+ * Copyright (C) 2000-2003 Damien Miller. All rights reserved.
+ * Copyright (C) 1999 WIDE Project. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Pseudo-implementation of RFC2553 name / address resolution functions
+ *
+ * But these functions are not implemented correctly. The minimum subset
+ * is implemented for ssh use only. For example, this routine assumes
+ * that ai_family is AF_INET. Don't use it for another purpose.
+ */
+
+#include "includes.h"
+
+/* RCSID("$.Id: fake-rfc2553.c,v 1.5 2003/09/22 02:08:23 dtucker Exp $");*/
+
+#ifndef HAVE_GETNAMEINFO
+int getnameinfo(const struct sockaddr *sa, size_t salen, char *host,
+ size_t hostlen, char *serv, size_t servlen, int flags)
+{
+ struct sockaddr_in *sin = (struct sockaddr_in *)sa;
+ struct hostent *hp;
+ char tmpserv[16];
+
+ if (serv != NULL) {
+ snprintf(tmpserv, sizeof(tmpserv), "%d", ntohs(sin->sin_port));
+ if (strlcpy(serv, tmpserv, servlen) >= servlen)
+ return (EAI_MEMORY);
+ }
+
+ if (host != NULL) {
+ if (flags & NI_NUMERICHOST) {
+ if (strlcpy(host, inet_ntoa(sin->sin_addr),
+ hostlen) >= hostlen)
+ return (EAI_MEMORY);
+ else
+ return (0);
+ } else {
+ hp = gethostbyaddr((char *)&sin->sin_addr,
+ sizeof(struct in_addr), AF_INET);
+ if (hp == NULL)
+ return (EAI_NODATA);
+
+ if (strlcpy(host, hp->h_name, hostlen) >= hostlen)
+ return (EAI_MEMORY);
+ else
+ return (0);
+ }
+ }
+ return (0);
+}
+#endif /* !HAVE_GETNAMEINFO */
+
+#ifndef HAVE_GAI_STRERROR
+#ifdef HAVE_CONST_GAI_STRERROR_PROTO
+const char *
+#else
+char *
+#endif
+gai_strerror(int err)
+{
+ switch (err) {
+ case EAI_NODATA:
+ return ("no address associated with name");
+ case EAI_MEMORY:
+ return ("memory allocation failure.");
+ case EAI_NONAME:
+ return ("nodename nor servname provided, or not known");
+ default:
+ return ("unknown/invalid error.");
+ }
+}
+#endif /* !HAVE_GAI_STRERROR */
+
+#ifndef HAVE_FREEADDRINFO
+void
+freeaddrinfo(struct addrinfo *ai)
+{
+ struct addrinfo *next;
+
+ for(; ai != NULL;) {
+ next = ai->ai_next;
+ free(ai);
+ ai = next;
+ }
+}
+#endif /* !HAVE_FREEADDRINFO */
+
+#ifndef HAVE_GETADDRINFO
+static struct
+addrinfo *malloc_ai(int port, u_long addr, const struct addrinfo *hints)
+{
+ struct addrinfo *ai;
+
+ ai = malloc(sizeof(*ai) + sizeof(struct sockaddr_in));
+ if (ai == NULL)
+ return (NULL);
+
+ memset(ai, '\0', sizeof(*ai) + sizeof(struct sockaddr_in));
+
+ ai->ai_addr = (struct sockaddr *)(ai + 1);
+ /* XXX -- ssh doesn't use sa_len */
+ ai->ai_addrlen = sizeof(struct sockaddr_in);
+ ai->ai_addr->sa_family = ai->ai_family = AF_INET;
+
+ ((struct sockaddr_in *)(ai)->ai_addr)->sin_port = port;
+ ((struct sockaddr_in *)(ai)->ai_addr)->sin_addr.s_addr = addr;
+
+ /* XXX: the following is not generally correct, but does what we want */
+ if (hints->ai_socktype)
+ ai->ai_socktype = hints->ai_socktype;
+ else
+ ai->ai_socktype = SOCK_STREAM;
+
+ if (hints->ai_protocol)
+ ai->ai_protocol = hints->ai_protocol;
+
+ return (ai);
+}
+
+int
+getaddrinfo(const char *hostname, const char *servname,
+ const struct addrinfo *hints, struct addrinfo **res)
+{
+ struct hostent *hp;
+ struct servent *sp;
+ struct in_addr in;
+ int i;
+ long int port;
+ u_long addr;
+
+ port = 0;
+ if (servname != NULL) {
+ char *cp;
+
+ port = strtol(servname, &cp, 10);
+ if (port > 0 && port <= 65535 && *cp == '\0')
+ port = htons(port);
+ else if ((sp = getservbyname(servname, NULL)) != NULL)
+ port = sp->s_port;
+ else
+ port = 0;
+ }
+
+ if (hints && hints->ai_flags & AI_PASSIVE) {
+ addr = htonl(0x00000000);
+ if (hostname && inet_aton(hostname, &in) != 0)
+ addr = in.s_addr;
+ *res = malloc_ai(port, addr, hints);
+ if (*res == NULL)
+ return (EAI_MEMORY);
+ return (0);
+ }
+
+ if (!hostname) {
+ *res = malloc_ai(port, htonl(0x7f000001), hints);
+ if (*res == NULL)
+ return (EAI_MEMORY);
+ return (0);
+ }
+
+ if (inet_aton(hostname, &in)) {
+ *res = malloc_ai(port, in.s_addr, hints);
+ if (*res == NULL)
+ return (EAI_MEMORY);
+ return (0);
+ }
+
+ /* Don't try DNS if AI_NUMERICHOST is set */
+ if (hints && hints->ai_flags & AI_NUMERICHOST)
+ return (EAI_NONAME);
+
+ hp = gethostbyname(hostname);
+ if (hp && hp->h_name && hp->h_name[0] && hp->h_addr_list[0]) {
+ struct addrinfo *cur, *prev;
+
+ cur = prev = *res = NULL;
+ for (i = 0; hp->h_addr_list[i]; i++) {
+ struct in_addr *in = (struct in_addr *)hp->h_addr_list[i];
+
+ cur = malloc_ai(port, in->s_addr, hints);
+ if (cur == NULL) {
+ if (*res != NULL)
+ freeaddrinfo(*res);
+ return (EAI_MEMORY);
+ }
+ if (prev)
+ prev->ai_next = cur;
+ else
+ *res = cur;
+
+ prev = cur;
+ }
+ return (0);
+ }
+
+ return (EAI_NODATA);
+}
+#endif /* !HAVE_GETADDRINFO */
diff --git a/fake-rfc2553.h b/fake-rfc2553.h
new file mode 100644
index 0000000..053e6a6
--- /dev/null
+++ b/fake-rfc2553.h
@@ -0,0 +1,162 @@
+/* Taken from OpenSSH 3.8.1p1 */
+
+/* $.Id: fake-rfc2553.h,v 1.9 2004/03/10 10:06:33 dtucker Exp $ */
+
+/*
+ * Copyright (C) 2000-2003 Damien Miller. All rights reserved.
+ * Copyright (C) 1999 WIDE Project. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Pseudo-implementation of RFC2553 name / address resolution functions
+ *
+ * But these functions are not implemented correctly. The minimum subset
+ * is implemented for ssh use only. For example, this routine assumes
+ * that ai_family is AF_INET. Don't use it for another purpose.
+ */
+
+#ifndef _FAKE_RFC2553_H
+#define _FAKE_RFC2553_H
+
+#include "includes.h"
+
+/*
+ * First, socket and INET6 related definitions
+ */
+#ifndef HAVE_STRUCT_SOCKADDR_STORAGE
+# define _SS_MAXSIZE 128 /* Implementation specific max size */
+# define _SS_PADSIZE (_SS_MAXSIZE - sizeof (struct sockaddr))
+struct sockaddr_storage {
+ struct sockaddr ss_sa;
+ char __ss_pad2[_SS_PADSIZE];
+};
+# define ss_family ss_sa.sa_family
+#endif /* !HAVE_STRUCT_SOCKADDR_STORAGE */
+
+#ifndef IN6_IS_ADDR_LOOPBACK
+# define IN6_IS_ADDR_LOOPBACK(a) \
+ (((u_int32_t *)(a))[0] == 0 && ((u_int32_t *)(a))[1] == 0 && \
+ ((u_int32_t *)(a))[2] == 0 && ((u_int32_t *)(a))[3] == htonl(1))
+#endif /* !IN6_IS_ADDR_LOOPBACK */
+
+#ifndef HAVE_STRUCT_IN6_ADDR
+struct in6_addr {
+ u_int8_t s6_addr[16];
+};
+#endif /* !HAVE_STRUCT_IN6_ADDR */
+
+#ifndef HAVE_STRUCT_SOCKADDR_IN6
+struct sockaddr_in6 {
+ unsigned short sin6_family;
+ u_int16_t sin6_port;
+ u_int32_t sin6_flowinfo;
+ struct in6_addr sin6_addr;
+};
+#endif /* !HAVE_STRUCT_SOCKADDR_IN6 */
+
+#ifndef AF_INET6
+/* Define it to something that should never appear */
+#define AF_INET6 AF_MAX
+#endif
+
+/*
+ * Next, RFC2553 name / address resolution API
+ */
+
+#ifndef NI_NUMERICHOST
+# define NI_NUMERICHOST (1)
+#endif
+#ifndef NI_NAMEREQD
+# define NI_NAMEREQD (1<<1)
+#endif
+#ifndef NI_NUMERICSERV
+# define NI_NUMERICSERV (1<<2)
+#endif
+
+#ifndef AI_PASSIVE
+# define AI_PASSIVE (1)
+#endif
+#ifndef AI_CANONNAME
+# define AI_CANONNAME (1<<1)
+#endif
+#ifndef AI_NUMERICHOST
+# define AI_NUMERICHOST (1<<2)
+#endif
+
+#ifndef NI_MAXSERV
+# define NI_MAXSERV 32
+#endif /* !NI_MAXSERV */
+#ifndef NI_MAXHOST
+# define NI_MAXHOST 1025
+#endif /* !NI_MAXHOST */
+
+#ifndef EAI_NODATA
+# define EAI_NODATA 1
+# define EAI_MEMORY 2
+# define EAI_NONAME 3
+#endif
+
+#ifndef HAVE_STRUCT_ADDRINFO
+struct addrinfo {
+ int ai_flags; /* AI_PASSIVE, AI_CANONNAME */
+ int ai_family; /* PF_xxx */
+ int ai_socktype; /* SOCK_xxx */
+ int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */
+ size_t ai_addrlen; /* length of ai_addr */
+ char *ai_canonname; /* canonical name for hostname */
+ struct sockaddr *ai_addr; /* binary address */
+ struct addrinfo *ai_next; /* next structure in linked list */
+};
+#endif /* !HAVE_STRUCT_ADDRINFO */
+
+#ifndef HAVE_GETADDRINFO
+#ifdef getaddrinfo
+# undef getaddrinfo
+#endif
+#define getaddrinfo(a,b,c,d) (ssh_getaddrinfo(a,b,c,d))
+int getaddrinfo(const char *, const char *,
+ const struct addrinfo *, struct addrinfo **);
+#endif /* !HAVE_GETADDRINFO */
+
+#if !defined(HAVE_GAI_STRERROR) && !defined(HAVE_CONST_GAI_STRERROR_PROTO)
+#define gai_strerror(a) (ssh_gai_strerror(a))
+char *gai_strerror(int);
+#endif /* !HAVE_GAI_STRERROR */
+
+#ifndef HAVE_FREEADDRINFO
+#define freeaddrinfo(a) (ssh_freeaddrinfo(a))
+void freeaddrinfo(struct addrinfo *);
+#endif /* !HAVE_FREEADDRINFO */
+
+#ifndef HAVE_GETNAMEINFO
+#define getnameinfo(a,b,c,d,e,f,g) (ssh_getnameinfo(a,b,c,d,e,f,g))
+int getnameinfo(const struct sockaddr *, size_t, char *, size_t,
+ char *, size_t, int);
+#endif /* !HAVE_GETNAMEINFO */
+
+#endif /* !_FAKE_RFC2553_H */
+
diff --git a/filelist.txt b/filelist.txt
new file mode 100644
index 0000000..8281c14
--- /dev/null
+++ b/filelist.txt
@@ -0,0 +1,117 @@
+This file is out of date - it remains here in case it is still of use.
+The basic naming convention is svr- and cli- for seperate parts,
+then common- for common parts. Some files have no prefix.
+
+A brief rundown on which files do what, and their corresponding sections
+in the IETF drafts. The .c files usually have corresponding .h files.
+
+Transport layer draft-ietf-secsh-transport-16.txt
+===============
+
+session.c Contains the main select() loop, and handles setting
+ up/closing down ssh connections
+
+algo.c Framework for handling various ciphers/hashes/algos,
+ and choosing between the lists of client/server
+ preferred ones
+
+kex.c Key exchange routines, used at startup to negotiate
+ which algorithms to use, and also to obtain session
+ keys. This also runs when rekeying during the
+ connection.
+
+packet.c Handles the basic packet encryption/decryption,
+ and switching to the appropriate packet handlers.
+ Called from session.c's main select loop.
+
+service.c Handles service requests (userauth or connection)
+
+
+Authentication draft-ietf-secsh-userauth-17.txt
+==============
+
+auth.c General auth handling, including user checking etc,
+ passes different auth types to auth{passwd,pubkey}
+
+authpasswd.c Handles /etc/passwd or /etc/shadow auth
+
+authpubkey.c Handles ~/.ssh/authorized_keys auth
+
+
+Connection draft-ietf-secsh-connect-17.txt
+==========
+
+channel.c Channel handling routines - each shell/tcp conn/agent
+ etc is a channel.
+
+chansession.c Handles shell/exec requests
+
+sshpty.c From OpenSSH, allocates PTYs etc
+
+termcodes.c Mapping of POSIX terminal codes to SSH terminal codes
+
+loginrec.c From OpenSSH, handles utmp/wtmp logging
+
+x11fwd.c Handles X11 forwarding
+
+agentfwd.c Handles auth-agent forwarding requests
+
+localtcpfwd.c Handles -L style tcp forwarding requests, setting
+ up the listening port and also handling connections
+ to that port (and subsequent channels)
+
+
+Program-related
+===============
+
+dbmulti.c Combination binary chooser main() function
+
+dbutil.c Various utility functions, incl logging, memory etc
+
+dropbearconvert.c Conversion from dropbear<->openssh keys, uses
+ keyimport.c to do most of the work
+
+dropbearkey.c Generates keys, calling gen{dss,rsa}
+
+keyimport.c Modified from PuTTY, converts between key types
+
+main.c dropbear's main(), handles listening, forking for
+ new connections, child-process limits
+
+runopts.c Parses commandline options
+
+options.h Compile-time feature selection
+
+config.h Features selected from configure
+
+debug.h Compile-time selection of debug features
+
+includes.h Included system headers etc
+
+
+Generic Routines
+================
+
+signkey.c A generic handler for pubkeys, switches to dss or rsa
+ depending on the key type
+
+rsa.c RSA asymmetric crypto routines
+
+dss.c DSS asymmetric crypto routines
+
+gendss.c DSS key generation
+
+genrsa.c RSA key generation
+
+bignum.c Some bignum helper functions
+
+queue.c A queue, used to enqueue encrypted packets to send
+
+random.c PRNG, based on /dev/urandom or prngd
+
+atomicio.c From OpenSSH, does `blocking' IO on non-blocking fds
+
+buffer.c Buffer-usage routines, with size checking etc
+
+
+vim:set ts=8:
diff --git a/gendss.c b/gendss.c
new file mode 100644
index 0000000..bf46d3d
--- /dev/null
+++ b/gendss.c
@@ -0,0 +1,198 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+#include "includes.h"
+#include "dbutil.h"
+#include "signkey.h"
+#include "bignum.h"
+#include "random.h"
+#include "buffer.h"
+#include "gendss.h"
+#include "dss.h"
+
+#define QSIZE 20 /* 160 bit */
+
+/* This is just a test */
+
+#ifdef DROPBEAR_DSS
+
+static void getq(dss_key *key);
+static void getp(dss_key *key, unsigned int size);
+static void getg(dss_key *key);
+static void getx(dss_key *key);
+static void gety(dss_key *key);
+
+dss_key * gen_dss_priv_key(unsigned int size) {
+
+ dss_key *key;
+
+ key = (dss_key*)m_malloc(sizeof(dss_key));
+
+ key->p = (mp_int*)m_malloc(sizeof(mp_int));
+ key->q = (mp_int*)m_malloc(sizeof(mp_int));
+ key->g = (mp_int*)m_malloc(sizeof(mp_int));
+ key->y = (mp_int*)m_malloc(sizeof(mp_int));
+ key->x = (mp_int*)m_malloc(sizeof(mp_int));
+ m_mp_init_multi(key->p, key->q, key->g, key->y, key->x, NULL);
+
+ seedrandom();
+
+ getq(key);
+ getp(key, size);
+ getg(key);
+ getx(key);
+ gety(key);
+
+ return key;
+
+}
+
+static void getq(dss_key *key) {
+
+ char buf[QSIZE];
+
+ /* 160 bit prime */
+ genrandom(buf, QSIZE);
+ buf[0] |= 0x80; /* top bit high */
+ buf[QSIZE-1] |= 0x01; /* bottom bit high */
+
+ bytes_to_mp(key->q, buf, QSIZE);
+
+ /* 18 rounds are required according to HAC */
+ if (mp_prime_next_prime(key->q, 18, 0) != MP_OKAY) {
+ fprintf(stderr, "dss key generation failed\n");
+ exit(1);
+ }
+}
+
+static void getp(dss_key *key, unsigned int size) {
+
+ DEF_MP_INT(tempX);
+ DEF_MP_INT(tempC);
+ DEF_MP_INT(tempP);
+ DEF_MP_INT(temp2q);
+ int result;
+ unsigned char *buf;
+
+ m_mp_init_multi(&tempX, &tempC, &tempP, &temp2q, NULL);
+
+
+ /* 2*q */
+ if (mp_mul_d(key->q, 2, &temp2q) != MP_OKAY) {
+ fprintf(stderr, "dss key generation failed\n");
+ exit(1);
+ }
+
+ buf = (unsigned char*)m_malloc(size);
+
+ result = 0;
+ do {
+
+ genrandom(buf, size);
+ buf[0] |= 0x80; /* set the top bit high */
+
+ /* X is a random mp_int */
+ bytes_to_mp(&tempX, buf, size);
+
+ /* C = X mod 2q */
+ if (mp_mod(&tempX, &temp2q, &tempC) != MP_OKAY) {
+ fprintf(stderr, "dss key generation failed\n");
+ exit(1);
+ }
+
+ /* P = X - (C - 1) = X - C + 1*/
+ if (mp_sub(&tempX, &tempC, &tempP) != MP_OKAY) {
+ fprintf(stderr, "dss key generation failed\n");
+ exit(1);
+ }
+
+ if (mp_add_d(&tempP, 1, key->p) != MP_OKAY) {
+ fprintf(stderr, "dss key generation failed\n");
+ exit(1);
+ }
+
+ /* now check for prime, 5 rounds is enough according to HAC */
+ /* result == 1 => p is prime */
+ if (mp_prime_is_prime(key->p, 5, &result) != MP_OKAY) {
+ fprintf(stderr, "dss key generation failed\n");
+ exit(1);
+ }
+ } while (!result);
+
+ mp_clear_multi(&tempX, &tempC, &tempP, &temp2q, NULL);
+ m_burn(buf, size);
+ m_free(buf);
+}
+
+static void getg(dss_key * key) {
+
+ DEF_MP_INT(div);
+ DEF_MP_INT(h);
+ DEF_MP_INT(val);
+
+ m_mp_init_multi(&div, &h, &val, NULL);
+
+ /* get div=(p-1)/q */
+ if (mp_sub_d(key->p, 1, &val) != MP_OKAY) {
+ fprintf(stderr, "dss key generation failed\n");
+ exit(1);
+ }
+ if (mp_div(&val, key->q, &div, NULL) != MP_OKAY) {
+ fprintf(stderr, "dss key generation failed\n");
+ exit(1);
+ }
+
+ /* initialise h=1 */
+ mp_set(&h, 1);
+ do {
+ /* now keep going with g=h^div mod p, until g > 1 */
+ if (mp_exptmod(&h, &div, key->p, key->g) != MP_OKAY) {
+ fprintf(stderr, "dss key generation failed\n");
+ exit(1);
+ }
+
+ if (mp_add_d(&h, 1, &h) != MP_OKAY) {
+ fprintf(stderr, "dss key generation failed\n");
+ exit(1);
+ }
+
+ } while (mp_cmp_d(key->g, 1) != MP_GT);
+
+ mp_clear_multi(&div, &h, &val, NULL);
+}
+
+static void getx(dss_key *key) {
+
+ gen_random_mpint(key->q, key->x);
+}
+
+static void gety(dss_key *key) {
+
+ if (mp_exptmod(key->g, key->x, key->p, key->y) != MP_OKAY) {
+ fprintf(stderr, "dss key generation failed\n");
+ exit(1);
+ }
+}
+
+#endif /* DROPBEAR_DSS */
diff --git a/gendss.h b/gendss.h
new file mode 100644
index 0000000..246dae3
--- /dev/null
+++ b/gendss.h
@@ -0,0 +1,36 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+#ifndef _GENDSS_H_
+#define _GENDSS_H_
+
+#include "dss.h"
+
+#ifdef DROPBEAR_DSS
+
+dss_key * gen_dss_priv_key(unsigned int size);
+
+#endif /* DROPBEAR_DSS */
+
+#endif /* _GENDSS_H_ */
diff --git a/genrsa.c b/genrsa.c
new file mode 100644
index 0000000..73a7984
--- /dev/null
+++ b/genrsa.c
@@ -0,0 +1,137 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+#include "includes.h"
+#include "dbutil.h"
+#include "bignum.h"
+#include "random.h"
+#include "rsa.h"
+#include "genrsa.h"
+
+#define RSA_E 65537
+
+#ifdef DROPBEAR_RSA
+
+static void getrsaprime(mp_int* prime, mp_int *primeminus,
+ mp_int* rsa_e, unsigned int size);
+
+/* mostly taken from libtomcrypt's rsa key generation routine */
+rsa_key * gen_rsa_priv_key(unsigned int size) {
+
+ rsa_key * key;
+ DEF_MP_INT(pminus);
+ DEF_MP_INT(qminus);
+ DEF_MP_INT(lcm);
+
+ key = (rsa_key*)m_malloc(sizeof(rsa_key));
+
+ key->e = (mp_int*)m_malloc(sizeof(mp_int));
+ key->n = (mp_int*)m_malloc(sizeof(mp_int));
+ key->d = (mp_int*)m_malloc(sizeof(mp_int));
+ key->p = (mp_int*)m_malloc(sizeof(mp_int));
+ key->q = (mp_int*)m_malloc(sizeof(mp_int));
+
+ m_mp_init_multi(key->e, key->n, key->d, key->p, key->q,
+ &pminus, &lcm, &qminus, NULL);
+
+ seedrandom();
+
+ if (mp_set_int(key->e, RSA_E) != MP_OKAY) {
+ fprintf(stderr, "rsa generation failed\n");
+ exit(1);
+ }
+
+ /* PuTTY doesn't like it if the modulus isn't a multiple of 8 bits,
+ * so we just generate them until we get one which is OK */
+ getrsaprime(key->p, &pminus, key->e, size/2);
+ do {
+ getrsaprime(key->q, &qminus, key->e, size/2);
+
+ if (mp_mul(key->p, key->q, key->n) != MP_OKAY) {
+ fprintf(stderr, "rsa generation failed\n");
+ exit(1);
+ }
+ } while (mp_count_bits(key->n) % 8 != 0);
+
+ /* lcm(p-1, q-1) */
+ if (mp_lcm(&pminus, &qminus, &lcm) != MP_OKAY) {
+ fprintf(stderr, "rsa generation failed\n");
+ exit(1);
+ }
+
+ /* de = 1 mod lcm(p-1,q-1) */
+ /* therefore d = (e^-1) mod lcm(p-1,q-1) */
+ if (mp_invmod(key->e, &lcm, key->d) != MP_OKAY) {
+ fprintf(stderr, "rsa generation failed\n");
+ exit(1);
+ }
+
+ mp_clear_multi(&pminus, &qminus, &lcm, NULL);
+
+ return key;
+}
+
+/* return a prime suitable for p or q */
+static void getrsaprime(mp_int* prime, mp_int *primeminus,
+ mp_int* rsa_e, unsigned int size) {
+
+ unsigned char *buf;
+ DEF_MP_INT(temp_gcd);
+
+ buf = (unsigned char*)m_malloc(size+1);
+
+ m_mp_init(&temp_gcd);
+ do {
+ /* generate a random odd number with MSB set, then find the
+ the next prime above it */
+ genrandom(buf, size+1);
+ buf[0] |= 0x80; /* MSB set */
+
+ bytes_to_mp(prime, buf, size+1);
+
+ /* find the next integer which is prime, 8 round of miller-rabin */
+ if (mp_prime_next_prime(prime, 8, 0) != MP_OKAY) {
+ fprintf(stderr, "rsa generation failed\n");
+ exit(1);
+ }
+
+ /* subtract one to get p-1 */
+ if (mp_sub_d(prime, 1, primeminus) != MP_OKAY) {
+ fprintf(stderr, "rsa generation failed\n");
+ exit(1);
+ }
+ /* check relative primality to e */
+ if (mp_gcd(primeminus, rsa_e, &temp_gcd) != MP_OKAY) {
+ fprintf(stderr, "rsa generation failed\n");
+ exit(1);
+ }
+ } while (mp_cmp_d(&temp_gcd, 1) != MP_EQ); /* while gcd(p-1, e) != 1 */
+
+ /* now we have a good value for result */
+ mp_clear(&temp_gcd);
+ m_burn(buf, size+1);
+ m_free(buf);
+}
+
+#endif /* DROPBEAR_RSA */
diff --git a/genrsa.h b/genrsa.h
new file mode 100644
index 0000000..ef9f579
--- /dev/null
+++ b/genrsa.h
@@ -0,0 +1,36 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+#ifndef _GENRSA_H_
+#define _GENRSA_H_
+
+#include "rsa.h"
+
+#ifdef DROPBEAR_RSA
+
+rsa_key * gen_rsa_priv_key(unsigned int size);
+
+#endif /* DROPBEAR_RSA */
+
+#endif /* _GENRSA_H_ */
diff --git a/includes.h b/includes.h
new file mode 100644
index 0000000..06c9692
--- /dev/null
+++ b/includes.h
@@ -0,0 +1,155 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+#ifndef _INCLUDES_H_
+#define _INCLUDES_H_
+
+
+#include "config.h"
+#include "options.h"
+#include "debug.h"
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/param.h> /* required for BSD4_4 define */
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <limits.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <dirent.h>
+
+#ifdef HAVE_UTMP_H
+#include <utmp.h>
+#endif
+
+#ifdef HAVE_UTMPX_H
+#include <utmpx.h>
+#endif
+
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif
+
+#ifdef HAVE_LASTLOG_H
+#include <lastlog.h>
+#endif
+
+#include <arpa/inet.h>
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+/* netbsd 1.6 needs this to be included before netinet/ip.h for some
+ * undocumented reason */
+#ifdef HAVE_NETINET_IN_SYSTM_H
+#include <netinet/in_systm.h>
+#endif
+
+#include <netinet/ip.h>
+
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+
+#ifdef HAVE_LIBUTIL_H
+#include <libutil.h>
+#endif
+
+#ifdef HAVE_CRYPT_H
+#include <crypt.h>
+#endif
+
+#ifndef DISABLE_ZLIB
+#include <zlib.h>
+#endif
+
+#ifdef HAVE_UTIL_H
+#include <util.h>
+#endif
+
+#ifdef HAVE_SHADOW_H
+#include <shadow.h>
+#endif
+
+#ifdef HAVE_LIBGEN_H
+#include <libgen.h>
+#endif
+
+#include "libtomcrypt/src/headers/tomcrypt.h"
+#include "libtommath/tommath.h"
+
+#include "compat.h"
+#include "fake-rfc2553.h"
+
+#ifndef HAVE_UINT16_T
+#ifndef HAVE_U_INT16_T
+typedef unsigned short u_int16_t;
+#endif /* HAVE_U_INT16_T */
+typedef u_int16_t uint16_t;
+#endif /* HAVE_UINT16_T */
+
+#ifndef LOG_AUTHPRIV
+#define LOG_AUTHPRIV LOG_AUTH
+#endif
+
+/* glibc 2.1.3 systems have sockaddr_storage.__ss_family rather than
+ * sockaddr_storage.ss_family */
+#if !defined(HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY) \
+ && defined(HAVE_STRUCT_SOCKADDR_STORAGE___SS_FAMILY)
+#define ss_family __ss_family
+#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_ */
diff --git a/install-sh b/install-sh
new file mode 100644
index 0000000..e9de238
--- /dev/null
+++ b/install-sh
@@ -0,0 +1,251 @@
+#!/bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5 (mit/util/scripts/install.sh).
+#
+# Copyright 1991 by the Massachusetts Institute of Technology
+#
+# Permission to use, copy, modify, distribute, and sell this software and its
+# documentation for any purpose is hereby granted without fee, provided that
+# the above copyright notice appear in all copies and that both that
+# copyright notice and this permission notice appear in supporting
+# documentation, and that the name of M.I.T. not be used in advertising or
+# publicity pertaining to distribution of the software without specific,
+# written prior permission. M.I.T. makes no representations about the
+# suitability of this software for any purpose. It is provided "as is"
+# without express or implied warranty.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch. It can only install one file at a time, a restriction
+# shared with many OS's install programs.
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+ case $1 in
+ -c) instcmd="$cpprog"
+ shift
+ continue;;
+
+ -d) dir_arg=true
+ shift
+ continue;;
+
+ -m) chmodcmd="$chmodprog $2"
+ shift
+ shift
+ continue;;
+
+ -o) chowncmd="$chownprog $2"
+ shift
+ shift
+ continue;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift
+ shift
+ continue;;
+
+ -s) stripcmd="$stripprog"
+ shift
+ continue;;
+
+ -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+ shift
+ continue;;
+
+ -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+ shift
+ continue;;
+
+ *) if [ x"$src" = x ]
+ then
+ src=$1
+ else
+ # this colon is to work around a 386BSD /bin/sh bug
+ :
+ dst=$1
+ fi
+ shift
+ continue;;
+ esac
+done
+
+if [ x"$src" = x ]
+then
+ echo "install: no input file specified"
+ exit 1
+else
+ true
+fi
+
+if [ x"$dir_arg" != x ]; then
+ dst=$src
+ src=""
+
+ if [ -d $dst ]; then
+ instcmd=:
+ chmodcmd=""
+ else
+ instcmd=mkdir
+ fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad
+# if $src (and thus $dsttmp) contains '*'.
+
+ if [ -f $src -o -d $src ]
+ then
+ true
+ else
+ echo "install: $src does not exist"
+ exit 1
+ fi
+
+ if [ x"$dst" = x ]
+ then
+ echo "install: no destination specified"
+ exit 1
+ else
+ true
+ fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+ if [ -d $dst ]
+ then
+ dst="$dst"/`basename $src`
+ else
+ true
+ fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+# this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+ pathcomp="${pathcomp}${1}"
+ shift
+
+ if [ ! -d "${pathcomp}" ] ;
+ then
+ $mkdirprog "${pathcomp}"
+ else
+ true
+ fi
+
+ pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+ $doit $instcmd $dst &&
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+ if [ x"$transformarg" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ dstfile=`basename $dst $transformbasename |
+ sed $transformarg`$transformbasename
+ fi
+
+# don't allow the sed command to completely eliminate the filename
+
+ if [ x"$dstfile" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ true
+ fi
+
+# Make a temp file name in the proper directory.
+
+ dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+ $doit $instcmd $src $dsttmp &&
+
+ trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing. If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+ $doit $rmcmd -f $dstdir/$dstfile &&
+ $doit $mvcmd $dsttmp $dstdir/$dstfile
+
+fi &&
+
+
+exit 0
diff --git a/kex.h b/kex.h
new file mode 100644
index 0000000..448ad1b
--- /dev/null
+++ b/kex.h
@@ -0,0 +1,64 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+#ifndef _KEX_H_
+#define _KEX_H_
+
+#include "includes.h"
+#include "algo.h"
+
+void send_msg_kexinit();
+void recv_msg_kexinit();
+void send_msg_newkeys();
+void recv_msg_newkeys();
+void kexfirstinitialise();
+void gen_kexdh_vals(mp_int *dh_pub, mp_int *dh_priv);
+void kexdh_comb_key(mp_int *dh_pub_us, mp_int *dh_priv, mp_int *dh_pub_them,
+ sign_key *hostkey);
+
+void recv_msg_kexdh_init(); /* server */
+
+void send_msg_kexdh_init(); /* client */
+void recv_msg_kexdh_reply(); /* client */
+
+struct KEXState {
+
+ unsigned sentkexinit : 1; /*set when we've sent/recv kexinit packet */
+ unsigned recvkexinit : 1;
+ unsigned firstfollows : 1; /* true when first_kex_packet_follows is set */
+ unsigned sentnewkeys : 1; /* set once we've send/recv'ed MSG_NEWKEYS*/
+ unsigned recvnewkeys : 1;
+
+ unsigned donefirstkex : 1; /* Set to 1 after the first kex has completed,
+ ie the transport layer has been set up */
+
+ long lastkextime; /* time of the last kex */
+ unsigned int datatrans; /* data transmitted since last kex */
+ unsigned int datarecv; /* data received since last kex */
+
+};
+
+#define MAX_KEXHASHBUF 2000
+
+#endif /* _KEX_H_ */
diff --git a/keyimport.c b/keyimport.c
new file mode 100644
index 0000000..a0474f3
--- /dev/null
+++ b/keyimport.c
@@ -0,0 +1,1730 @@
+/*
+ * Based on PuTTY's import.c for importing/exporting OpenSSH and SSH.com
+ * keyfiles.
+ *
+ * The horribleness of the code is probably mine (matt).
+ *
+ * Modifications copyright 2003 Matt Johnston
+ *
+ * PuTTY is copyright 1997-2003 Simon Tatham.
+ *
+ * Portions copyright Robert de Bath, Joris van Rantwijk, Delian
+ * Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry,
+ * Justin Bradford, and CORE SDI S.A.
+ *
+ * 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 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 "keyimport.h"
+#include "bignum.h"
+#include "buffer.h"
+#include "dbutil.h"
+
+#define PUT_32BIT(cp, value) do { \
+ (cp)[3] = (unsigned char)(value); \
+ (cp)[2] = (unsigned char)((value) >> 8); \
+ (cp)[1] = (unsigned char)((value) >> 16); \
+ (cp)[0] = (unsigned char)((value) >> 24); } while (0)
+
+#define GET_32BIT(cp) \
+ (((unsigned long)(unsigned char)(cp)[0] << 24) | \
+ ((unsigned long)(unsigned char)(cp)[1] << 16) | \
+ ((unsigned long)(unsigned char)(cp)[2] << 8) | \
+ ((unsigned long)(unsigned char)(cp)[3]))
+
+static int openssh_encrypted(const char *filename);
+static sign_key *openssh_read(const char *filename, char *passphrase);
+static int openssh_write(const char *filename, sign_key *key,
+ char *passphrase);
+
+static int dropbear_write(const char*filename, sign_key * key);
+static sign_key *dropbear_read(const char* filename);
+
+#if 0
+static int sshcom_encrypted(const char *filename, char **comment);
+static struct ssh2_userkey *sshcom_read(const char *filename, char *passphrase);
+static int sshcom_write(const char *filename, struct ssh2_userkey *key,
+ char *passphrase);
+#endif
+
+int import_encrypted(const char* filename, int filetype) {
+
+ if (filetype == KEYFILE_OPENSSH) {
+ return openssh_encrypted(filename);
+#if 0
+ } else if (filetype == KEYFILE_SSHCOM) {
+ return sshcom_encrypted(filename, NULL);
+#endif
+ }
+ return 0;
+}
+
+sign_key *import_read(const char *filename, char *passphrase, int filetype) {
+
+ if (filetype == KEYFILE_OPENSSH) {
+ return openssh_read(filename, passphrase);
+ } else if (filetype == KEYFILE_DROPBEAR) {
+ return dropbear_read(filename);
+#if 0
+ } else if (filetype == KEYFILE_SSHCOM) {
+ return sshcom_read(filename, passphrase);
+#endif
+ }
+ return NULL;
+}
+
+int import_write(const char *filename, sign_key *key, char *passphrase,
+ int filetype) {
+
+ if (filetype == KEYFILE_OPENSSH) {
+ return openssh_write(filename, key, passphrase);
+ } else if (filetype == KEYFILE_DROPBEAR) {
+ return dropbear_write(filename, key);
+#if 0
+ } else if (filetype == KEYFILE_SSHCOM) {
+ return sshcom_write(filename, key, passphrase);
+#endif
+ }
+ return 0;
+}
+
+static sign_key *dropbear_read(const char* filename) {
+
+ buffer * buf = NULL;
+ sign_key *ret = NULL;
+ int type;
+
+ buf = buf_new(MAX_PRIVKEY_SIZE);
+ if (buf_readfile(buf, filename) == DROPBEAR_FAILURE) {
+ goto error;
+ }
+
+ buf_setpos(buf, 0);
+ ret = new_sign_key();
+
+ type = DROPBEAR_SIGNKEY_ANY;
+ if (buf_get_priv_key(buf, ret, &type) == DROPBEAR_FAILURE){
+ goto error;
+ }
+ buf_free(buf);
+
+ return ret;
+
+error:
+ if (buf) {
+ buf_free(buf);
+ }
+ if (ret) {
+ sign_key_free(ret);
+ }
+ return NULL;
+}
+
+/* returns 0 on fail, 1 on success */
+static int dropbear_write(const char*filename, sign_key * key) {
+
+ int keytype = -1;
+ buffer * buf;
+ FILE*fp;
+ int len;
+ int ret;
+
+#ifdef DROPBEAR_RSA
+ if (key->rsakey != NULL) {
+ keytype = DROPBEAR_SIGNKEY_RSA;
+ }
+#endif
+#ifdef DROPBEAR_DSS
+ if (key->dsskey != NULL) {
+ keytype = DROPBEAR_SIGNKEY_DSS;
+ }
+#endif
+
+ buf = buf_new(MAX_PRIVKEY_SIZE);
+ buf_put_priv_key(buf, key, keytype);
+
+ fp = fopen(filename, "w");
+ if (!fp) {
+ ret = 0;
+ goto out;
+ }
+
+ buf_setpos(buf, 0);
+ do {
+ len = fwrite(buf_getptr(buf, buf->len - buf->pos),
+ 1, buf->len - buf->pos, fp);
+ buf_incrpos(buf, len);
+ } while (len > 0 && buf->len != buf->pos);
+
+ fclose(fp);
+
+ if (buf->pos != buf->len) {
+ ret = 0;
+ } else {
+ ret = 1;
+ }
+out:
+ buf_free(buf);
+ return ret;
+}
+
+
+/* ----------------------------------------------------------------------
+ * Helper routines. (The base64 ones are defined in sshpubk.c.)
+ */
+
+#define isbase64(c) ( ((c) >= 'A' && (c) <= 'Z') || \
+ ((c) >= 'a' && (c) <= 'z') || \
+ ((c) >= '0' && (c) <= '9') || \
+ (c) == '+' || (c) == '/' || (c) == '=' \
+ )
+
+/* cpl has to be less than 100 */
+static void base64_encode_fp(FILE * fp, unsigned char *data,
+ int datalen, int cpl)
+{
+ char out[100];
+ int n;
+ unsigned long outlen;
+ int rawcpl;
+ rawcpl = cpl * 3 / 4;
+ dropbear_assert((unsigned int)cpl < sizeof(out));
+
+ while (datalen > 0) {
+ n = (datalen < rawcpl ? datalen : rawcpl);
+ outlen = sizeof(out);
+ base64_encode(data, n, out, &outlen);
+ data += n;
+ datalen -= n;
+ fwrite(out, 1, outlen, fp);
+ fputc('\n', fp);
+ }
+}
+/*
+ * Read an ASN.1/BER identifier and length pair.
+ *
+ * Flags are a combination of the #defines listed below.
+ *
+ * Returns -1 if unsuccessful; otherwise returns the number of
+ * bytes used out of the source data.
+ */
+
+/* ASN.1 tag classes. */
+#define ASN1_CLASS_UNIVERSAL (0 << 6)
+#define ASN1_CLASS_APPLICATION (1 << 6)
+#define ASN1_CLASS_CONTEXT_SPECIFIC (2 << 6)
+#define ASN1_CLASS_PRIVATE (3 << 6)
+#define ASN1_CLASS_MASK (3 << 6)
+
+/* Primitive versus constructed bit. */
+#define ASN1_CONSTRUCTED (1 << 5)
+
+static int ber_read_id_len(void *source, int sourcelen,
+ int *id, int *length, int *flags)
+{
+ unsigned char *p = (unsigned char *) source;
+
+ if (sourcelen == 0)
+ return -1;
+
+ *flags = (*p & 0xE0);
+ if ((*p & 0x1F) == 0x1F) {
+ *id = 0;
+ while (*p & 0x80) {
+ *id = (*id << 7) | (*p & 0x7F);
+ p++, sourcelen--;
+ if (sourcelen == 0)
+ return -1;
+ }
+ *id = (*id << 7) | (*p & 0x7F);
+ p++, sourcelen--;
+ } else {
+ *id = *p & 0x1F;
+ p++, sourcelen--;
+ }
+
+ if (sourcelen == 0)
+ return -1;
+
+ if (*p & 0x80) {
+ int n = *p & 0x7F;
+ p++, sourcelen--;
+ if (sourcelen < n)
+ return -1;
+ *length = 0;
+ while (n--)
+ *length = (*length << 8) | (*p++);
+ sourcelen -= n;
+ } else {
+ *length = *p;
+ p++, sourcelen--;
+ }
+
+ return p - (unsigned char *) source;
+}
+
+/*
+ * Write an ASN.1/BER identifier and length pair. Returns the
+ * number of bytes consumed. Assumes dest contains enough space.
+ * Will avoid writing anything if dest is NULL, but still return
+ * amount of space required.
+ */
+static int ber_write_id_len(void *dest, int id, int length, int flags)
+{
+ unsigned char *d = (unsigned char *)dest;
+ int len = 0;
+
+ if (id <= 30) {
+ /*
+ * Identifier is one byte.
+ */
+ len++;
+ if (d) *d++ = id | flags;
+ } else {
+ int n;
+ /*
+ * Identifier is multiple bytes: the first byte is 11111
+ * plus the flags, and subsequent bytes encode the value of
+ * the identifier, 7 bits at a time, with the top bit of
+ * each byte 1 except the last one which is 0.
+ */
+ len++;
+ if (d) *d++ = 0x1F | flags;
+ for (n = 1; (id >> (7*n)) > 0; n++)
+ continue; /* count the bytes */
+ while (n--) {
+ len++;
+ if (d) *d++ = (n ? 0x80 : 0) | ((id >> (7*n)) & 0x7F);
+ }
+ }
+
+ if (length < 128) {
+ /*
+ * Length is one byte.
+ */
+ len++;
+ if (d) *d++ = length;
+ } else {
+ int n;
+ /*
+ * Length is multiple bytes. The first is 0x80 plus the
+ * number of subsequent bytes, and the subsequent bytes
+ * encode the actual length.
+ */
+ for (n = 1; (length >> (8*n)) > 0; n++)
+ continue; /* count the bytes */
+ len++;
+ if (d) *d++ = 0x80 | n;
+ while (n--) {
+ len++;
+ if (d) *d++ = (length >> (8*n)) & 0xFF;
+ }
+ }
+
+ return len;
+}
+
+
+/* Simple structure to point to an mp-int within a blob. */
+struct mpint_pos { void *start; int bytes; };
+
+/* ----------------------------------------------------------------------
+ * Code to read and write OpenSSH private keys.
+ */
+
+enum { OSSH_DSA, OSSH_RSA };
+struct openssh_key {
+ int type;
+ int encrypted;
+ char iv[32];
+ unsigned char *keyblob;
+ unsigned int keyblob_len, keyblob_size;
+};
+
+static struct openssh_key *load_openssh_key(const char *filename)
+{
+ struct openssh_key *ret;
+ FILE *fp;
+ char buffer[256];
+ char *errmsg = NULL, *p = NULL;
+ int headers_done;
+ unsigned long len, outlen;
+
+ ret = (struct openssh_key*)m_malloc(sizeof(struct openssh_key));
+ ret->keyblob = NULL;
+ ret->keyblob_len = ret->keyblob_size = 0;
+ ret->encrypted = 0;
+ memset(ret->iv, 0, sizeof(ret->iv));
+
+ if (strlen(filename) == 1 && filename[0] == '-') {
+ fp = stdin;
+ } else {
+ fp = fopen(filename, "r");
+ }
+ if (!fp) {
+ errmsg = "Unable to open key file";
+ goto error;
+ }
+ if (!fgets(buffer, sizeof(buffer), fp) ||
+ 0 != strncmp(buffer, "-----BEGIN ", 11) ||
+ 0 != strcmp(buffer+strlen(buffer)-17, "PRIVATE KEY-----\n")) {
+ errmsg = "File does not begin with OpenSSH key header";
+ goto error;
+ }
+ if (!strcmp(buffer, "-----BEGIN RSA PRIVATE KEY-----\n"))
+ ret->type = OSSH_RSA;
+ else if (!strcmp(buffer, "-----BEGIN DSA PRIVATE KEY-----\n"))
+ ret->type = OSSH_DSA;
+ else {
+ errmsg = "Unrecognised key type";
+ goto error;
+ }
+
+ headers_done = 0;
+ while (1) {
+ if (!fgets(buffer, sizeof(buffer), fp)) {
+ errmsg = "Unexpected end of file";
+ goto error;
+ }
+ if (0 == strncmp(buffer, "-----END ", 9) &&
+ 0 == strcmp(buffer+strlen(buffer)-17, "PRIVATE KEY-----\n"))
+ break; /* done */
+ if ((p = strchr(buffer, ':')) != NULL) {
+ if (headers_done) {
+ errmsg = "Header found in body of key data";
+ goto error;
+ }
+ *p++ = '\0';
+ while (*p && isspace((unsigned char)*p)) p++;
+ if (!strcmp(buffer, "Proc-Type")) {
+ if (p[0] != '4' || p[1] != ',') {
+ errmsg = "Proc-Type is not 4 (only 4 is supported)";
+ goto error;
+ }
+ p += 2;
+ if (!strcmp(p, "ENCRYPTED\n"))
+ ret->encrypted = 1;
+ } else if (!strcmp(buffer, "DEK-Info")) {
+ int i, j;
+
+ if (strncmp(p, "DES-EDE3-CBC,", 13)) {
+ errmsg = "Ciphers other than DES-EDE3-CBC not supported";
+ goto error;
+ }
+ p += 13;
+ for (i = 0; i < 8; i++) {
+ if (1 != sscanf(p, "%2x", &j))
+ break;
+ ret->iv[i] = j;
+ p += 2;
+ }
+ if (i < 8) {
+ errmsg = "Expected 16-digit iv in DEK-Info";
+ goto error;
+ }
+ }
+ } else {
+ headers_done = 1;
+ len = strlen(buffer);
+ outlen = len*4/3;
+ if (ret->keyblob_len + outlen > ret->keyblob_size) {
+ ret->keyblob_size = ret->keyblob_len + outlen + 256;
+ ret->keyblob = (unsigned char*)m_realloc(ret->keyblob,
+ ret->keyblob_size);
+ }
+ outlen = ret->keyblob_size - ret->keyblob_len;
+ if (base64_decode(buffer, len,
+ ret->keyblob + ret->keyblob_len, &outlen) != CRYPT_OK){
+ errmsg = "Error decoding base64";
+ goto error;
+ }
+ ret->keyblob_len += outlen;
+ }
+ }
+
+ if (ret->keyblob_len == 0 || !ret->keyblob) {
+ errmsg = "Key body not present";
+ goto error;
+ }
+
+ if (ret->encrypted && ret->keyblob_len % 8 != 0) {
+ errmsg = "Encrypted key blob is not a multiple of cipher block size";
+ goto error;
+ }
+
+ memset(buffer, 0, sizeof(buffer));
+ return ret;
+
+ error:
+ memset(buffer, 0, sizeof(buffer));
+ if (ret) {
+ if (ret->keyblob) {
+ memset(ret->keyblob, 0, ret->keyblob_size);
+ m_free(ret->keyblob);
+ }
+ memset(&ret, 0, sizeof(ret));
+ m_free(ret);
+ }
+ if (errmsg) {
+ fprintf(stderr, "Error: %s\n", errmsg);
+ }
+ return NULL;
+}
+
+static int openssh_encrypted(const char *filename)
+{
+ struct openssh_key *key = load_openssh_key(filename);
+ int ret;
+
+ if (!key)
+ return 0;
+ ret = key->encrypted;
+ memset(key->keyblob, 0, key->keyblob_size);
+ m_free(key->keyblob);
+ memset(&key, 0, sizeof(key));
+ m_free(key);
+ return ret;
+}
+
+static sign_key *openssh_read(const char *filename, char *passphrase)
+{
+ struct openssh_key *key;
+ unsigned char *p;
+ int ret, id, len, flags;
+ int i, num_integers = 0;
+ sign_key *retval = NULL;
+ char *errmsg;
+ char *modptr = NULL;
+ int modlen = -9999;
+ int type;
+
+ sign_key *retkey;
+ buffer * blobbuf = NULL;
+
+ key = load_openssh_key(filename);
+
+ if (!key)
+ return NULL;
+
+ if (key->encrypted) {
+ errmsg = "encrypted keys not supported currently";
+ goto error;
+#if 0
+ /* matt TODO */
+ /*
+ * Derive encryption key from passphrase and iv/salt:
+ *
+ * - let block A equal MD5(passphrase || iv)
+ * - let block B equal MD5(A || passphrase || iv)
+ * - block C would be MD5(B || passphrase || iv) and so on
+ * - encryption key is the first N bytes of A || B
+ */
+ struct MD5Context md5c;
+ unsigned char keybuf[32];
+
+ MD5Init(&md5c);
+ MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase));
+ MD5Update(&md5c, (unsigned char *)key->iv, 8);
+ MD5Final(keybuf, &md5c);
+
+ MD5Init(&md5c);
+ MD5Update(&md5c, keybuf, 16);
+ MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase));
+ MD5Update(&md5c, (unsigned char *)key->iv, 8);
+ MD5Final(keybuf+16, &md5c);
+
+ /*
+ * Now decrypt the key blob.
+ */
+ des3_decrypt_pubkey_ossh(keybuf, (unsigned char *)key->iv,
+ key->keyblob, key->keyblob_len);
+
+ memset(&md5c, 0, sizeof(md5c));
+ memset(keybuf, 0, sizeof(keybuf));
+#endif
+ }
+
+ /*
+ * Now we have a decrypted key blob, which contains an ASN.1
+ * encoded private key. We must now untangle the ASN.1.
+ *
+ * We expect the whole key blob to be formatted as a SEQUENCE
+ * (0x30 followed by a length code indicating that the rest of
+ * the blob is part of the sequence). Within that SEQUENCE we
+ * expect to see a bunch of INTEGERs. What those integers mean
+ * depends on the key type:
+ *
+ * - For RSA, we expect the integers to be 0, n, e, d, p, q,
+ * dmp1, dmq1, iqmp in that order. (The last three are d mod
+ * (p-1), d mod (q-1), inverse of q mod p respectively.)
+ *
+ * - For DSA, we expect them to be 0, p, q, g, y, x in that
+ * order.
+ */
+
+ p = key->keyblob;
+
+ /* Expect the SEQUENCE header. Take its absence as a failure to decrypt. */
+ ret = ber_read_id_len(p, key->keyblob_len, &id, &len, &flags);
+ p += ret;
+ if (ret < 0 || id != 16) {
+ errmsg = "ASN.1 decoding failure - wrong password?";
+ goto error;
+ }
+
+ /* Expect a load of INTEGERs. */
+ if (key->type == OSSH_RSA)
+ num_integers = 9;
+ else if (key->type == OSSH_DSA)
+ num_integers = 6;
+
+ /*
+ * Space to create key blob in.
+ */
+ blobbuf = buf_new(3000);
+
+ if (key->type == OSSH_DSA) {
+ buf_putstring(blobbuf, "ssh-dss", 7);
+ } else if (key->type == OSSH_RSA) {
+ buf_putstring(blobbuf, "ssh-rsa", 7);
+ }
+
+ for (i = 0; i < num_integers; i++) {
+ ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,
+ &id, &len, &flags);
+ p += ret;
+ if (ret < 0 || id != 2 ||
+ key->keyblob+key->keyblob_len-p < len) {
+ errmsg = "ASN.1 decoding failure";
+ goto error;
+ }
+
+ if (i == 0) {
+ /*
+ * The first integer should be zero always (I think
+ * this is some sort of version indication).
+ */
+ if (len != 1 || p[0] != 0) {
+ errmsg = "Version number mismatch";
+ goto error;
+ }
+ } else if (key->type == OSSH_RSA) {
+ /*
+ * OpenSSH key order is n, e, d, p, q, dmp1, dmq1, iqmp
+ * but we want e, n, d, p, q
+ */
+ if (i == 1) {
+ /* Save the details for after we deal with number 2. */
+ modptr = (char *)p;
+ modlen = len;
+ } else if (i >= 2 && i <= 5) {
+ buf_putstring(blobbuf, p, len);
+ if (i == 2) {
+ buf_putstring(blobbuf, modptr, modlen);
+ }
+ }
+ } else if (key->type == OSSH_DSA) {
+ /*
+ * OpenSSH key order is p, q, g, y, x,
+ * we want the same.
+ */
+ buf_putstring(blobbuf, p, len);
+ }
+
+ /* Skip past the number. */
+ p += len;
+ }
+
+ /*
+ * Now put together the actual key. Simplest way to do this is
+ * to assemble our own key blobs and feed them to the createkey
+ * functions; this is a bit faffy but it does mean we get all
+ * the sanity checks for free.
+ */
+ retkey = new_sign_key();
+ buf_setpos(blobbuf, 0);
+ type = DROPBEAR_SIGNKEY_ANY;
+ if (buf_get_priv_key(blobbuf, retkey, &type)
+ != DROPBEAR_SUCCESS) {
+ errmsg = "unable to create key structure";
+ sign_key_free(retkey);
+ retkey = NULL;
+ goto error;
+ }
+
+ errmsg = NULL; /* no error */
+ retval = retkey;
+
+ error:
+ if (blobbuf) {
+ buf_burn(blobbuf);
+ buf_free(blobbuf);
+ }
+ m_burn(key->keyblob, key->keyblob_size);
+ m_free(key->keyblob);
+ m_burn(key, sizeof(key));
+ m_free(key);
+ if (errmsg) {
+ fprintf(stderr, "Error: %s\n", errmsg);
+ }
+ return retval;
+}
+
+static int openssh_write(const char *filename, sign_key *key,
+ char *passphrase)
+{
+ buffer * keyblob = NULL;
+ buffer * extrablob = NULL; /* used for calculated values to write */
+ unsigned char *outblob = NULL;
+ int outlen = -9999;
+ struct mpint_pos numbers[9];
+ int nnumbers = -1, pos, len, seqlen, i;
+ char *header = NULL, *footer = NULL;
+ char zero[1];
+ unsigned char iv[8];
+ int ret = 0;
+ FILE *fp;
+ int keytype = -1;
+
+#ifdef DROPBEAR_RSA
+ mp_int dmp1, dmq1, iqmp, tmpval; /* for rsa */
+
+ if (key->rsakey != NULL) {
+ keytype = DROPBEAR_SIGNKEY_RSA;
+ }
+#endif
+#ifdef DROPBEAR_DSS
+ if (key->dsskey != NULL) {
+ keytype = DROPBEAR_SIGNKEY_DSS;
+ }
+#endif
+
+ dropbear_assert(keytype != -1);
+
+ /*
+ * Fetch the key blobs.
+ */
+ keyblob = buf_new(3000);
+ buf_put_priv_key(keyblob, key, keytype);
+
+ buf_setpos(keyblob, 0);
+ /* skip the "ssh-rsa" or "ssh-dss" header */
+ buf_incrpos(keyblob, buf_getint(keyblob));
+
+ /*
+ * Find the sequence of integers to be encoded into the OpenSSH
+ * key blob, and also decide on the header line.
+ */
+ numbers[0].start = zero; numbers[0].bytes = 1; zero[0] = '\0';
+
+#ifdef DROPBEAR_RSA
+ if (keytype == DROPBEAR_SIGNKEY_RSA) {
+
+ if (key->rsakey->p == NULL || key->rsakey->q == NULL) {
+ fprintf(stderr, "Pre-0.33 Dropbear keys cannot be converted to OpenSSH keys.\n");
+ goto error;
+ }
+
+ /* e */
+ numbers[2].bytes = buf_getint(keyblob);
+ numbers[2].start = buf_getptr(keyblob, numbers[2].bytes);
+ buf_incrpos(keyblob, numbers[2].bytes);
+
+ /* n */
+ numbers[1].bytes = buf_getint(keyblob);
+ numbers[1].start = buf_getptr(keyblob, numbers[1].bytes);
+ buf_incrpos(keyblob, numbers[1].bytes);
+
+ /* d */
+ numbers[3].bytes = buf_getint(keyblob);
+ numbers[3].start = buf_getptr(keyblob, numbers[3].bytes);
+ buf_incrpos(keyblob, numbers[3].bytes);
+
+ /* p */
+ numbers[4].bytes = buf_getint(keyblob);
+ numbers[4].start = buf_getptr(keyblob, numbers[4].bytes);
+ buf_incrpos(keyblob, numbers[4].bytes);
+
+ /* q */
+ numbers[5].bytes = buf_getint(keyblob);
+ numbers[5].start = buf_getptr(keyblob, numbers[5].bytes);
+ buf_incrpos(keyblob, numbers[5].bytes);
+
+ /* now calculate some extra parameters: */
+ m_mp_init(&tmpval);
+ m_mp_init(&dmp1);
+ m_mp_init(&dmq1);
+ m_mp_init(&iqmp);
+
+ /* dmp1 = d mod (p-1) */
+ if (mp_sub_d(key->rsakey->p, 1, &tmpval) != MP_OKAY) {
+ fprintf(stderr, "Bignum error for p-1\n");
+ goto error;
+ }
+ if (mp_mod(key->rsakey->d, &tmpval, &dmp1) != MP_OKAY) {
+ fprintf(stderr, "Bignum error for dmp1\n");
+ goto error;
+ }
+
+ /* dmq1 = d mod (q-1) */
+ if (mp_sub_d(key->rsakey->q, 1, &tmpval) != MP_OKAY) {
+ fprintf(stderr, "Bignum error for q-1\n");
+ goto error;
+ }
+ if (mp_mod(key->rsakey->d, &tmpval, &dmq1) != MP_OKAY) {
+ fprintf(stderr, "Bignum error for dmq1\n");
+ goto error;
+ }
+
+ /* iqmp = (q^-1) mod p */
+ if (mp_invmod(key->rsakey->q, key->rsakey->p, &iqmp) != MP_OKAY) {
+ fprintf(stderr, "Bignum error for iqmp\n");
+ goto error;
+ }
+
+ extrablob = buf_new(2000);
+ buf_putmpint(extrablob, &dmp1);
+ buf_putmpint(extrablob, &dmq1);
+ buf_putmpint(extrablob, &iqmp);
+ buf_setpos(extrablob, 0);
+ mp_clear(&dmp1);
+ mp_clear(&dmq1);
+ mp_clear(&iqmp);
+ mp_clear(&tmpval);
+
+ /* dmp1 */
+ numbers[6].bytes = buf_getint(extrablob);
+ numbers[6].start = buf_getptr(extrablob, numbers[6].bytes);
+ buf_incrpos(extrablob, numbers[6].bytes);
+
+ /* dmq1 */
+ numbers[7].bytes = buf_getint(extrablob);
+ numbers[7].start = buf_getptr(extrablob, numbers[7].bytes);
+ buf_incrpos(extrablob, numbers[7].bytes);
+
+ /* iqmp */
+ numbers[8].bytes = buf_getint(extrablob);
+ numbers[8].start = buf_getptr(extrablob, numbers[8].bytes);
+ buf_incrpos(extrablob, numbers[8].bytes);
+
+ nnumbers = 9;
+ header = "-----BEGIN RSA PRIVATE KEY-----\n";
+ footer = "-----END RSA PRIVATE KEY-----\n";
+ }
+#endif /* DROPBEAR_RSA */
+
+#ifdef DROPBEAR_DSS
+ if (keytype == DROPBEAR_SIGNKEY_DSS) {
+
+ /* p */
+ numbers[1].bytes = buf_getint(keyblob);
+ numbers[1].start = buf_getptr(keyblob, numbers[1].bytes);
+ buf_incrpos(keyblob, numbers[1].bytes);
+
+ /* q */
+ numbers[2].bytes = buf_getint(keyblob);
+ numbers[2].start = buf_getptr(keyblob, numbers[2].bytes);
+ buf_incrpos(keyblob, numbers[2].bytes);
+
+ /* g */
+ numbers[3].bytes = buf_getint(keyblob);
+ numbers[3].start = buf_getptr(keyblob, numbers[3].bytes);
+ buf_incrpos(keyblob, numbers[3].bytes);
+
+ /* y */
+ numbers[4].bytes = buf_getint(keyblob);
+ numbers[4].start = buf_getptr(keyblob, numbers[4].bytes);
+ buf_incrpos(keyblob, numbers[4].bytes);
+
+ /* x */
+ numbers[5].bytes = buf_getint(keyblob);
+ numbers[5].start = buf_getptr(keyblob, numbers[5].bytes);
+ buf_incrpos(keyblob, numbers[5].bytes);
+
+ nnumbers = 6;
+ header = "-----BEGIN DSA PRIVATE KEY-----\n";
+ footer = "-----END DSA PRIVATE KEY-----\n";
+ }
+#endif /* DROPBEAR_DSS */
+
+ /*
+ * Now count up the total size of the ASN.1 encoded integers,
+ * so as to determine the length of the containing SEQUENCE.
+ */
+ len = 0;
+ for (i = 0; i < nnumbers; i++) {
+ len += ber_write_id_len(NULL, 2, numbers[i].bytes, 0);
+ len += numbers[i].bytes;
+ }
+ seqlen = len;
+ /* Now add on the SEQUENCE header. */
+ len += ber_write_id_len(NULL, 16, seqlen, ASN1_CONSTRUCTED);
+ /* Round up to the cipher block size, ensuring we have at least one
+ * byte of padding (see below). */
+ outlen = len;
+ if (passphrase)
+ outlen = (outlen+8) &~ 7;
+
+ /*
+ * Now we know how big outblob needs to be. Allocate it.
+ */
+ outblob = (unsigned char*)m_malloc(outlen);
+
+ /*
+ * And write the data into it.
+ */
+ pos = 0;
+ pos += ber_write_id_len(outblob+pos, 16, seqlen, ASN1_CONSTRUCTED);
+ for (i = 0; i < nnumbers; i++) {
+ pos += ber_write_id_len(outblob+pos, 2, numbers[i].bytes, 0);
+ memcpy(outblob+pos, numbers[i].start, numbers[i].bytes);
+ pos += numbers[i].bytes;
+ }
+
+ /*
+ * Padding on OpenSSH keys is deterministic. The number of
+ * padding bytes is always more than zero, and always at most
+ * the cipher block length. The value of each padding byte is
+ * equal to the number of padding bytes. So a plaintext that's
+ * an exact multiple of the block size will be padded with 08
+ * 08 08 08 08 08 08 08 (assuming a 64-bit block cipher); a
+ * plaintext one byte less than a multiple of the block size
+ * will be padded with just 01.
+ *
+ * This enables the OpenSSL key decryption function to strip
+ * off the padding algorithmically and return the unpadded
+ * plaintext to the next layer: it looks at the final byte, and
+ * then expects to find that many bytes at the end of the data
+ * with the same value. Those are all removed and the rest is
+ * returned.
+ */
+ dropbear_assert(pos == len);
+ while (pos < outlen) {
+ outblob[pos++] = outlen - len;
+ }
+
+ /*
+ * Encrypt the key.
+ */
+ if (passphrase) {
+ fprintf(stderr, "Encrypted keys aren't supported currently\n");
+ goto error;
+#if 0
+ /*
+ * Invent an iv. Then derive encryption key from passphrase
+ * and iv/salt:
+ *
+ * - let block A equal MD5(passphrase || iv)
+ * - let block B equal MD5(A || passphrase || iv)
+ * - block C would be MD5(B || passphrase || iv) and so on
+ * - encryption key is the first N bytes of A || B
+ */
+ struct MD5Context md5c;
+ unsigned char keybuf[32];
+
+ for (i = 0; i < 8; i++) iv[i] = random_byte();
+
+ MD5Init(&md5c);
+ MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase));
+ MD5Update(&md5c, iv, 8);
+ MD5Final(keybuf, &md5c);
+
+ MD5Init(&md5c);
+ MD5Update(&md5c, keybuf, 16);
+ MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase));
+ MD5Update(&md5c, iv, 8);
+ MD5Final(keybuf+16, &md5c);
+
+ /*
+ * Now encrypt the key blob.
+ */
+ des3_encrypt_pubkey_ossh(keybuf, iv, outblob, outlen);
+
+ memset(&md5c, 0, sizeof(md5c));
+ memset(keybuf, 0, sizeof(keybuf));
+#endif
+ }
+
+ /*
+ * And save it. We'll use Unix line endings just in case it's
+ * subsequently transferred in binary mode.
+ */
+ if (strlen(filename) == 1 && filename[0] == '-') {
+ fp = stdout;
+ } else {
+ fp = fopen(filename, "wb"); /* ensure Unix line endings */
+ }
+ if (!fp) {
+ fprintf(stderr, "Failed opening output file\n");
+ goto error;
+ }
+ fputs(header, fp);
+ if (passphrase) {
+ fprintf(fp, "Proc-Type: 4,ENCRYPTED\nDEK-Info: DES-EDE3-CBC,");
+ for (i = 0; i < 8; i++)
+ fprintf(fp, "%02X", iv[i]);
+ fprintf(fp, "\n\n");
+ }
+ base64_encode_fp(fp, outblob, outlen, 64);
+ fputs(footer, fp);
+ fclose(fp);
+ ret = 1;
+
+ error:
+ if (outblob) {
+ memset(outblob, 0, outlen);
+ m_free(outblob);
+ }
+ if (keyblob) {
+ buf_burn(keyblob);
+ buf_free(keyblob);
+ }
+ if (extrablob) {
+ buf_burn(extrablob);
+ buf_free(extrablob);
+ }
+ return ret;
+}
+
+#if 0
+/* XXX TODO ssh.com stuff isn't going yet */
+
+/* ----------------------------------------------------------------------
+ * Code to read ssh.com private keys.
+ */
+
+/*
+ * The format of the base64 blob is largely ssh2-packet-formatted,
+ * except that mpints are a bit different: they're more like the
+ * old ssh1 mpint. You have a 32-bit bit count N, followed by
+ * (N+7)/8 bytes of data.
+ *
+ * So. The blob contains:
+ *
+ * - uint32 0x3f6ff9eb (magic number)
+ * - uint32 size (total blob size)
+ * - string key-type (see below)
+ * - string cipher-type (tells you if key is encrypted)
+ * - string encrypted-blob
+ *
+ * (The first size field includes the size field itself and the
+ * magic number before it. All other size fields are ordinary ssh2
+ * strings, so the size field indicates how much data is to
+ * _follow_.)
+ *
+ * The encrypted blob, once decrypted, contains a single string
+ * which in turn contains the payload. (This allows padding to be
+ * added after that string while still making it clear where the
+ * real payload ends. Also it probably makes for a reasonable
+ * decryption check.)
+ *
+ * The payload blob, for an RSA key, contains:
+ * - mpint e
+ * - mpint d
+ * - mpint n (yes, the public and private stuff is intermixed)
+ * - mpint u (presumably inverse of p mod q)
+ * - mpint p (p is the smaller prime)
+ * - mpint q (q is the larger)
+ *
+ * For a DSA key, the payload blob contains:
+ * - uint32 0
+ * - mpint p
+ * - mpint g
+ * - mpint q
+ * - mpint y
+ * - mpint x
+ *
+ * Alternatively, if the parameters are `predefined', that
+ * (0,p,g,q) sequence can be replaced by a uint32 1 and a string
+ * containing some predefined parameter specification. *shudder*,
+ * but I doubt we'll encounter this in real life.
+ *
+ * The key type strings are ghastly. The RSA key I looked at had a
+ * type string of
+ *
+ * `if-modn{sign{rsa-pkcs1-sha1},encrypt{rsa-pkcs1v2-oaep}}'
+ *
+ * and the DSA key wasn't much better:
+ *
+ * `dl-modp{sign{dsa-nist-sha1},dh{plain}}'
+ *
+ * It isn't clear that these will always be the same. I think it
+ * might be wise just to look at the `if-modn{sign{rsa' and
+ * `dl-modp{sign{dsa' prefixes.
+ *
+ * Finally, the encryption. The cipher-type string appears to be
+ * either `none' or `3des-cbc'. Looks as if this is SSH2-style
+ * 3des-cbc (i.e. outer cbc rather than inner). The key is created
+ * from the passphrase by means of yet another hashing faff:
+ *
+ * - first 16 bytes are MD5(passphrase)
+ * - next 16 bytes are MD5(passphrase || first 16 bytes)
+ * - if there were more, they'd be MD5(passphrase || first 32),
+ * and so on.
+ */
+
+#define SSHCOM_MAGIC_NUMBER 0x3f6ff9eb
+
+struct sshcom_key {
+ char comment[256]; /* allowing any length is overkill */
+ unsigned char *keyblob;
+ int keyblob_len, keyblob_size;
+};
+
+static struct sshcom_key *load_sshcom_key(const char *filename)
+{
+ struct sshcom_key *ret;
+ FILE *fp;
+ char buffer[256];
+ int len;
+ char *errmsg, *p;
+ int headers_done;
+ char base64_bit[4];
+ int base64_chars = 0;
+
+ ret = snew(struct sshcom_key);
+ ret->comment[0] = '\0';
+ ret->keyblob = NULL;
+ ret->keyblob_len = ret->keyblob_size = 0;
+
+ fp = fopen(filename, "r");
+ if (!fp) {
+ errmsg = "Unable to open key file";
+ goto error;
+ }
+ if (!fgets(buffer, sizeof(buffer), fp) ||
+ 0 != strcmp(buffer, "---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----\n")) {
+ errmsg = "File does not begin with ssh.com key header";
+ goto error;
+ }
+
+ headers_done = 0;
+ while (1) {
+ if (!fgets(buffer, sizeof(buffer), fp)) {
+ errmsg = "Unexpected end of file";
+ goto error;
+ }
+ if (!strcmp(buffer, "---- END SSH2 ENCRYPTED PRIVATE KEY ----\n"))
+ break; /* done */
+ if ((p = strchr(buffer, ':')) != NULL) {
+ if (headers_done) {
+ errmsg = "Header found in body of key data";
+ goto error;
+ }
+ *p++ = '\0';
+ while (*p && isspace((unsigned char)*p)) p++;
+ /*
+ * Header lines can end in a trailing backslash for
+ * continuation.
+ */
+ while ((len = strlen(p)) > (int)(sizeof(buffer) - (p-buffer) -1) ||
+ p[len-1] != '\n' || p[len-2] == '\\') {
+ if (len > (int)((p-buffer) + sizeof(buffer)-2)) {
+ errmsg = "Header line too long to deal with";
+ goto error;
+ }
+ if (!fgets(p+len-2, sizeof(buffer)-(p-buffer)-(len-2), fp)) {
+ errmsg = "Unexpected end of file";
+ goto error;
+ }
+ }
+ p[strcspn(p, "\n")] = '\0';
+ if (!strcmp(buffer, "Comment")) {
+ /* Strip quotes in comment if present. */
+ if (p[0] == '"' && p[strlen(p)-1] == '"') {
+ p++;
+ p[strlen(p)-1] = '\0';
+ }
+ strncpy(ret->comment, p, sizeof(ret->comment));
+ ret->comment[sizeof(ret->comment)-1] = '\0';
+ }
+ } else {
+ headers_done = 1;
+
+ p = buffer;
+ while (isbase64(*p)) {
+ base64_bit[base64_chars++] = *p;
+ if (base64_chars == 4) {
+ unsigned char out[3];
+
+ base64_chars = 0;
+
+ len = base64_decode_atom(base64_bit, out);
+
+ if (len <= 0) {
+ errmsg = "Invalid base64 encoding";
+ goto error;
+ }
+
+ if (ret->keyblob_len + len > ret->keyblob_size) {
+ ret->keyblob_size = ret->keyblob_len + len + 256;
+ ret->keyblob = sresize(ret->keyblob, ret->keyblob_size,
+ unsigned char);
+ }
+
+ memcpy(ret->keyblob + ret->keyblob_len, out, len);
+ ret->keyblob_len += len;
+ }
+
+ p++;
+ }
+ }
+ }
+
+ if (ret->keyblob_len == 0 || !ret->keyblob) {
+ errmsg = "Key body not present";
+ goto error;
+ }
+
+ return ret;
+
+ error:
+ if (ret) {
+ if (ret->keyblob) {
+ memset(ret->keyblob, 0, ret->keyblob_size);
+ m_free(ret->keyblob);
+ }
+ memset(&ret, 0, sizeof(ret));
+ m_free(ret);
+ }
+ return NULL;
+}
+
+int sshcom_encrypted(const char *filename, char **comment)
+{
+ struct sshcom_key *key = load_sshcom_key(filename);
+ int pos, len, answer;
+
+ *comment = NULL;
+ if (!key)
+ return 0;
+
+ /*
+ * Check magic number.
+ */
+ if (GET_32BIT(key->keyblob) != 0x3f6ff9eb)
+ return 0; /* key is invalid */
+
+ /*
+ * Find the cipher-type string.
+ */
+ answer = 0;
+ pos = 8;
+ if (key->keyblob_len < pos+4)
+ goto done; /* key is far too short */
+ pos += 4 + GET_32BIT(key->keyblob + pos); /* skip key type */
+ if (key->keyblob_len < pos+4)
+ goto done; /* key is far too short */
+ len = GET_32BIT(key->keyblob + pos); /* find cipher-type length */
+ if (key->keyblob_len < pos+4+len)
+ goto done; /* cipher type string is incomplete */
+ if (len != 4 || 0 != memcmp(key->keyblob + pos + 4, "none", 4))
+ answer = 1;
+
+ done:
+ *comment = dupstr(key->comment);
+ memset(key->keyblob, 0, key->keyblob_size);
+ m_free(key->keyblob);
+ memset(&key, 0, sizeof(key));
+ m_free(key);
+ return answer;
+}
+
+static int sshcom_read_mpint(void *data, int len, struct mpint_pos *ret)
+{
+ int bits;
+ int bytes;
+ unsigned char *d = (unsigned char *) data;
+
+ if (len < 4)
+ goto error;
+ bits = GET_32BIT(d);
+
+ bytes = (bits + 7) / 8;
+ if (len < 4+bytes)
+ goto error;
+
+ ret->start = d + 4;
+ ret->bytes = bytes;
+ return bytes+4;
+
+ error:
+ ret->start = NULL;
+ ret->bytes = -1;
+ return len; /* ensure further calls fail as well */
+}
+
+static int sshcom_put_mpint(void *target, void *data, int len)
+{
+ unsigned char *d = (unsigned char *)target;
+ unsigned char *i = (unsigned char *)data;
+ int bits = len * 8 - 1;
+
+ while (bits > 0) {
+ if (*i & (1 << (bits & 7)))
+ break;
+ if (!(bits-- & 7))
+ i++, len--;
+ }
+
+ PUT_32BIT(d, bits+1);
+ memcpy(d+4, i, len);
+ return len+4;
+}
+
+sign_key *sshcom_read(const char *filename, char *passphrase)
+{
+ struct sshcom_key *key = load_sshcom_key(filename);
+ char *errmsg;
+ int pos, len;
+ const char prefix_rsa[] = "if-modn{sign{rsa";
+ const char prefix_dsa[] = "dl-modp{sign{dsa";
+ enum { RSA, DSA } type;
+ int encrypted;
+ char *ciphertext;
+ int cipherlen;
+ struct ssh2_userkey *ret = NULL, *retkey;
+ const struct ssh_signkey *alg;
+ unsigned char *blob = NULL;
+ int blobsize, publen, privlen;
+
+ if (!key)
+ return NULL;
+
+ /*
+ * Check magic number.
+ */
+ if (GET_32BIT(key->keyblob) != SSHCOM_MAGIC_NUMBER) {
+ errmsg = "Key does not begin with magic number";
+ goto error;
+ }
+
+ /*
+ * Determine the key type.
+ */
+ pos = 8;
+ if (key->keyblob_len < pos+4 ||
+ (len = GET_32BIT(key->keyblob + pos)) > key->keyblob_len - pos - 4) {
+ errmsg = "Key blob does not contain a key type string";
+ goto error;
+ }
+ if (len > sizeof(prefix_rsa) - 1 &&
+ !memcmp(key->keyblob+pos+4, prefix_rsa, sizeof(prefix_rsa) - 1)) {
+ type = RSA;
+ } else if (len > sizeof(prefix_dsa) - 1 &&
+ !memcmp(key->keyblob+pos+4, prefix_dsa, sizeof(prefix_dsa) - 1)) {
+ type = DSA;
+ } else {
+ errmsg = "Key is of unknown type";
+ goto error;
+ }
+ pos += 4+len;
+
+ /*
+ * Determine the cipher type.
+ */
+ if (key->keyblob_len < pos+4 ||
+ (len = GET_32BIT(key->keyblob + pos)) > key->keyblob_len - pos - 4) {
+ errmsg = "Key blob does not contain a cipher type string";
+ goto error;
+ }
+ if (len == 4 && !memcmp(key->keyblob+pos+4, "none", 4))
+ encrypted = 0;
+ else if (len == 8 && !memcmp(key->keyblob+pos+4, "3des-cbc", 8))
+ encrypted = 1;
+ else {
+ errmsg = "Key encryption is of unknown type";
+ goto error;
+ }
+ pos += 4+len;
+
+ /*
+ * Get hold of the encrypted part of the key.
+ */
+ if (key->keyblob_len < pos+4 ||
+ (len = GET_32BIT(key->keyblob + pos)) > key->keyblob_len - pos - 4) {
+ errmsg = "Key blob does not contain actual key data";
+ goto error;
+ }
+ ciphertext = (char *)key->keyblob + pos + 4;
+ cipherlen = len;
+ if (cipherlen == 0) {
+ errmsg = "Length of key data is zero";
+ goto error;
+ }
+
+ /*
+ * Decrypt it if necessary.
+ */
+ if (encrypted) {
+ /*
+ * Derive encryption key from passphrase and iv/salt:
+ *
+ * - let block A equal MD5(passphrase)
+ * - let block B equal MD5(passphrase || A)
+ * - block C would be MD5(passphrase || A || B) and so on
+ * - encryption key is the first N bytes of A || B
+ */
+ struct MD5Context md5c;
+ unsigned char keybuf[32], iv[8];
+
+ if (cipherlen % 8 != 0) {
+ errmsg = "Encrypted part of key is not a multiple of cipher block"
+ " size";
+ goto error;
+ }
+
+ MD5Init(&md5c);
+ MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase));
+ MD5Final(keybuf, &md5c);
+
+ MD5Init(&md5c);
+ MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase));
+ MD5Update(&md5c, keybuf, 16);
+ MD5Final(keybuf+16, &md5c);
+
+ /*
+ * Now decrypt the key blob.
+ */
+ memset(iv, 0, sizeof(iv));
+ des3_decrypt_pubkey_ossh(keybuf, iv, (unsigned char *)ciphertext,
+ cipherlen);
+
+ memset(&md5c, 0, sizeof(md5c));
+ memset(keybuf, 0, sizeof(keybuf));
+
+ /*
+ * Hereafter we return WRONG_PASSPHRASE for any parsing
+ * error. (But only if we've just tried to decrypt it!
+ * Returning WRONG_PASSPHRASE for an unencrypted key is
+ * automatic doom.)
+ */
+ if (encrypted)
+ ret = SSH2_WRONG_PASSPHRASE;
+ }
+
+ /*
+ * Strip away the containing string to get to the real meat.
+ */
+ len = GET_32BIT(ciphertext);
+ if (len > cipherlen-4) {
+ errmsg = "containing string was ill-formed";
+ goto error;
+ }
+ ciphertext += 4;
+ cipherlen = len;
+
+ /*
+ * Now we break down into RSA versus DSA. In either case we'll
+ * construct public and private blobs in our own format, and
+ * end up feeding them to alg->createkey().
+ */
+ blobsize = cipherlen + 256;
+ blob = snewn(blobsize, unsigned char);
+ privlen = 0;
+ if (type == RSA) {
+ struct mpint_pos n, e, d, u, p, q;
+ int pos = 0;
+ pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &e);
+ pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &d);
+ pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &n);
+ pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &u);
+ pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &p);
+ pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &q);
+ if (!q.start) {
+ errmsg = "key data did not contain six integers";
+ goto error;
+ }
+
+ alg = &ssh_rsa;
+ pos = 0;
+ pos += put_string(blob+pos, "ssh-rsa", 7);
+ pos += put_mp(blob+pos, e.start, e.bytes);
+ pos += put_mp(blob+pos, n.start, n.bytes);
+ publen = pos;
+ pos += put_string(blob+pos, d.start, d.bytes);
+ pos += put_mp(blob+pos, q.start, q.bytes);
+ pos += put_mp(blob+pos, p.start, p.bytes);
+ pos += put_mp(blob+pos, u.start, u.bytes);
+ privlen = pos - publen;
+ } else if (type == DSA) {
+ struct mpint_pos p, q, g, x, y;
+ int pos = 4;
+ if (GET_32BIT(ciphertext) != 0) {
+ errmsg = "predefined DSA parameters not supported";
+ goto error;
+ }
+ pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &p);
+ pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &g);
+ pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &q);
+ pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &y);
+ pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &x);
+ if (!x.start) {
+ errmsg = "key data did not contain five integers";
+ goto error;
+ }
+
+ alg = &ssh_dss;
+ pos = 0;
+ pos += put_string(blob+pos, "ssh-dss", 7);
+ pos += put_mp(blob+pos, p.start, p.bytes);
+ pos += put_mp(blob+pos, q.start, q.bytes);
+ pos += put_mp(blob+pos, g.start, g.bytes);
+ pos += put_mp(blob+pos, y.start, y.bytes);
+ publen = pos;
+ pos += put_mp(blob+pos, x.start, x.bytes);
+ privlen = pos - publen;
+ }
+
+ dropbear_assert(privlen > 0); /* should have bombed by now if not */
+
+ retkey = snew(struct ssh2_userkey);
+ retkey->alg = alg;
+ retkey->data = alg->createkey(blob, publen, blob+publen, privlen);
+ if (!retkey->data) {
+ m_free(retkey);
+ errmsg = "unable to create key data structure";
+ goto error;
+ }
+ retkey->comment = dupstr(key->comment);
+
+ errmsg = NULL; /* no error */
+ ret = retkey;
+
+ error:
+ if (blob) {
+ memset(blob, 0, blobsize);
+ m_free(blob);
+ }
+ memset(key->keyblob, 0, key->keyblob_size);
+ m_free(key->keyblob);
+ memset(&key, 0, sizeof(key));
+ m_free(key);
+ return ret;
+}
+
+int sshcom_write(const char *filename, sign_key *key,
+ char *passphrase)
+{
+ unsigned char *pubblob, *privblob;
+ int publen, privlen;
+ unsigned char *outblob;
+ int outlen;
+ struct mpint_pos numbers[6];
+ int nnumbers, initial_zero, pos, lenpos, i;
+ char *type;
+ char *ciphertext;
+ int cipherlen;
+ int ret = 0;
+ FILE *fp;
+
+ /*
+ * Fetch the key blobs.
+ */
+ pubblob = key->alg->public_blob(key->data, &publen);
+ privblob = key->alg->private_blob(key->data, &privlen);
+ outblob = NULL;
+
+ /*
+ * Find the sequence of integers to be encoded into the OpenSSH
+ * key blob, and also decide on the header line.
+ */
+ if (key->alg == &ssh_rsa) {
+ int pos;
+ struct mpint_pos n, e, d, p, q, iqmp;
+
+ pos = 4 + GET_32BIT(pubblob);
+ pos += ssh2_read_mpint(pubblob+pos, publen-pos, &e);
+ pos += ssh2_read_mpint(pubblob+pos, publen-pos, &n);
+ pos = 0;
+ pos += ssh2_read_mpint(privblob+pos, privlen-pos, &d);
+ pos += ssh2_read_mpint(privblob+pos, privlen-pos, &p);
+ pos += ssh2_read_mpint(privblob+pos, privlen-pos, &q);
+ pos += ssh2_read_mpint(privblob+pos, privlen-pos, &iqmp);
+
+ dropbear_assert(e.start && iqmp.start); /* can't go wrong */
+
+ numbers[0] = e;
+ numbers[1] = d;
+ numbers[2] = n;
+ numbers[3] = iqmp;
+ numbers[4] = q;
+ numbers[5] = p;
+
+ nnumbers = 6;
+ initial_zero = 0;
+ type = "if-modn{sign{rsa-pkcs1-sha1},encrypt{rsa-pkcs1v2-oaep}}";
+ } else if (key->alg == &ssh_dss) {
+ int pos;
+ struct mpint_pos p, q, g, y, x;
+
+ pos = 4 + GET_32BIT(pubblob);
+ pos += ssh2_read_mpint(pubblob+pos, publen-pos, &p);
+ pos += ssh2_read_mpint(pubblob+pos, publen-pos, &q);
+ pos += ssh2_read_mpint(pubblob+pos, publen-pos, &g);
+ pos += ssh2_read_mpint(pubblob+pos, publen-pos, &y);
+ pos = 0;
+ pos += ssh2_read_mpint(privblob+pos, privlen-pos, &x);
+
+ dropbear_assert(y.start && x.start); /* can't go wrong */
+
+ numbers[0] = p;
+ numbers[1] = g;
+ numbers[2] = q;
+ numbers[3] = y;
+ numbers[4] = x;
+
+ nnumbers = 5;
+ initial_zero = 1;
+ type = "dl-modp{sign{dsa-nist-sha1},dh{plain}}";
+ } else {
+ dropbear_assert(0); /* zoinks! */
+ }
+
+ /*
+ * Total size of key blob will be somewhere under 512 plus
+ * combined length of integers. We'll calculate the more
+ * precise size as we construct the blob.
+ */
+ outlen = 512;
+ for (i = 0; i < nnumbers; i++)
+ outlen += 4 + numbers[i].bytes;
+ outblob = snewn(outlen, unsigned char);
+
+ /*
+ * Create the unencrypted key blob.
+ */
+ pos = 0;
+ PUT_32BIT(outblob+pos, SSHCOM_MAGIC_NUMBER); pos += 4;
+ pos += 4; /* length field, fill in later */
+ pos += put_string(outblob+pos, type, strlen(type));
+ {
+ char *ciphertype = passphrase ? "3des-cbc" : "none";
+ pos += put_string(outblob+pos, ciphertype, strlen(ciphertype));
+ }
+ lenpos = pos; /* remember this position */
+ pos += 4; /* encrypted-blob size */
+ pos += 4; /* encrypted-payload size */
+ if (initial_zero) {
+ PUT_32BIT(outblob+pos, 0);
+ pos += 4;
+ }
+ for (i = 0; i < nnumbers; i++)
+ pos += sshcom_put_mpint(outblob+pos,
+ numbers[i].start, numbers[i].bytes);
+ /* Now wrap up the encrypted payload. */
+ PUT_32BIT(outblob+lenpos+4, pos - (lenpos+8));
+ /* Pad encrypted blob to a multiple of cipher block size. */
+ if (passphrase) {
+ int padding = -(pos - (lenpos+4)) & 7;
+ while (padding--)
+ outblob[pos++] = random_byte();
+ }
+ ciphertext = (char *)outblob+lenpos+4;
+ cipherlen = pos - (lenpos+4);
+ dropbear_assert(!passphrase || cipherlen % 8 == 0);
+ /* Wrap up the encrypted blob string. */
+ PUT_32BIT(outblob+lenpos, cipherlen);
+ /* And finally fill in the total length field. */
+ PUT_32BIT(outblob+4, pos);
+
+ dropbear_assert(pos < outlen);
+
+ /*
+ * Encrypt the key.
+ */
+ if (passphrase) {
+ /*
+ * Derive encryption key from passphrase and iv/salt:
+ *
+ * - let block A equal MD5(passphrase)
+ * - let block B equal MD5(passphrase || A)
+ * - block C would be MD5(passphrase || A || B) and so on
+ * - encryption key is the first N bytes of A || B
+ */
+ struct MD5Context md5c;
+ unsigned char keybuf[32], iv[8];
+
+ MD5Init(&md5c);
+ MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase));
+ MD5Final(keybuf, &md5c);
+
+ MD5Init(&md5c);
+ MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase));
+ MD5Update(&md5c, keybuf, 16);
+ MD5Final(keybuf+16, &md5c);
+
+ /*
+ * Now decrypt the key blob.
+ */
+ memset(iv, 0, sizeof(iv));
+ des3_encrypt_pubkey_ossh(keybuf, iv, (unsigned char *)ciphertext,
+ cipherlen);
+
+ memset(&md5c, 0, sizeof(md5c));
+ memset(keybuf, 0, sizeof(keybuf));
+ }
+
+ /*
+ * And save it. We'll use Unix line endings just in case it's
+ * subsequently transferred in binary mode.
+ */
+ fp = fopen(filename, "wb"); /* ensure Unix line endings */
+ if (!fp)
+ goto error;
+ fputs("---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----\n", fp);
+ fprintf(fp, "Comment: \"");
+ /*
+ * Comment header is broken with backslash-newline if it goes
+ * over 70 chars. Although it's surrounded by quotes, it
+ * _doesn't_ escape backslashes or quotes within the string.
+ * Don't ask me, I didn't design it.
+ */
+ {
+ int slen = 60; /* starts at 60 due to "Comment: " */
+ char *c = key->comment;
+ while ((int)strlen(c) > slen) {
+ fprintf(fp, "%.*s\\\n", slen, c);
+ c += slen;
+ slen = 70; /* allow 70 chars on subsequent lines */
+ }
+ fprintf(fp, "%s\"\n", c);
+ }
+ base64_encode_fp(fp, outblob, pos, 70);
+ fputs("---- END SSH2 ENCRYPTED PRIVATE KEY ----\n", fp);
+ fclose(fp);
+ ret = 1;
+
+ error:
+ if (outblob) {
+ memset(outblob, 0, outlen);
+ m_free(outblob);
+ }
+ if (privblob) {
+ memset(privblob, 0, privlen);
+ m_free(privblob);
+ }
+ if (pubblob) {
+ memset(pubblob, 0, publen);
+ m_free(pubblob);
+ }
+ return ret;
+}
+#endif /* ssh.com stuff disabled */
diff --git a/keyimport.h b/keyimport.h
new file mode 100644
index 0000000..19f212f
--- /dev/null
+++ b/keyimport.h
@@ -0,0 +1,42 @@
+/*
+ * 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. */
+
+#ifndef _KEYIMPORT_H_
+#define _KEYIMPORT_H_
+
+#include "includes.h"
+#include "signkey.h"
+
+enum {
+ KEYFILE_DROPBEAR,
+ KEYFILE_OPENSSH,
+ KEYFILE_SSHCOM
+};
+
+int import_write(const char *filename, sign_key *key, char *passphrase,
+ int filetype);
+sign_key *import_read(const char *filename, char *passphrase, int filetype);
+int import_encrypted(const char* filename, int filetype);
+
+#endif /* _KEYIMPORT_H_ */
diff --git a/libtommath/LICENSE b/libtommath/LICENSE
new file mode 100644
index 0000000..5baa792
--- /dev/null
+++ b/libtommath/LICENSE
@@ -0,0 +1,4 @@
+LibTomMath is hereby released into the Public Domain.
+
+-- Tom St Denis
+
diff --git a/libtommath/Makefile.in b/libtommath/Makefile.in
new file mode 100644
index 0000000..e96173a
--- /dev/null
+++ b/libtommath/Makefile.in
@@ -0,0 +1,165 @@
+#Makefile for GCC
+#
+#Tom St Denis
+
+#version of library
+VERSION=0.35
+
+VPATH=@srcdir@
+srcdir=@srcdir@
+
+# Dropbear takes flags from the toplevel makefile
+CFLAGS += -I$(srcdir)
+
+#CFLAGS += -I./ -Wall -W -Wshadow -Wsign-compare
+
+#for speed
+#CFLAGS += -O3 -funroll-all-loops
+
+#for size
+#CFLAGS += -Os
+
+#x86 optimizations [should be valid for any GCC install though]
+#CFLAGS += -fomit-frame-pointer
+
+#debug
+#CFLAGS += -g3
+
+#install as this user
+USER=root
+GROUP=root
+
+default: libtommath.a
+
+#default files to install
+LIBNAME=libtommath.a
+HEADERS=tommath.h tommath_class.h tommath_superclass.h
+
+#LIBPATH-The directory for libtommath to be installed to.
+#INCPATH-The directory to install the header files for libtommath.
+#DATAPATH-The directory to install the pdf docs.
+DESTDIR=
+LIBPATH=/usr/lib
+INCPATH=/usr/include
+DATAPATH=/usr/share/doc/libtommath/pdf
+
+OBJECTS=bncore.o bn_mp_init.o bn_mp_clear.o bn_mp_exch.o bn_mp_grow.o bn_mp_shrink.o \
+bn_mp_clamp.o bn_mp_zero.o bn_mp_set.o bn_mp_set_int.o bn_mp_init_size.o bn_mp_copy.o \
+bn_mp_init_copy.o bn_mp_abs.o bn_mp_neg.o bn_mp_cmp_mag.o bn_mp_cmp.o bn_mp_cmp_d.o \
+bn_mp_rshd.o bn_mp_lshd.o bn_mp_mod_2d.o bn_mp_div_2d.o bn_mp_mul_2d.o bn_mp_div_2.o \
+bn_mp_mul_2.o bn_s_mp_add.o bn_s_mp_sub.o bn_fast_s_mp_mul_digs.o bn_s_mp_mul_digs.o \
+bn_fast_s_mp_mul_high_digs.o bn_s_mp_mul_high_digs.o bn_fast_s_mp_sqr.o bn_s_mp_sqr.o \
+bn_mp_add.o bn_mp_sub.o bn_mp_karatsuba_mul.o bn_mp_mul.o bn_mp_karatsuba_sqr.o \
+bn_mp_sqr.o bn_mp_div.o bn_mp_mod.o bn_mp_add_d.o bn_mp_sub_d.o bn_mp_mul_d.o \
+bn_mp_div_d.o bn_mp_mod_d.o bn_mp_expt_d.o bn_mp_addmod.o bn_mp_submod.o \
+bn_mp_mulmod.o bn_mp_sqrmod.o bn_mp_gcd.o bn_mp_lcm.o bn_fast_mp_invmod.o bn_mp_invmod.o \
+bn_mp_reduce.o bn_mp_montgomery_setup.o bn_fast_mp_montgomery_reduce.o bn_mp_montgomery_reduce.o \
+bn_mp_exptmod_fast.o bn_mp_exptmod.o bn_mp_2expt.o bn_mp_n_root.o bn_mp_jacobi.o bn_reverse.o \
+bn_mp_count_bits.o bn_mp_read_unsigned_bin.o bn_mp_read_signed_bin.o bn_mp_to_unsigned_bin.o \
+bn_mp_to_signed_bin.o bn_mp_unsigned_bin_size.o bn_mp_signed_bin_size.o \
+bn_mp_xor.o bn_mp_and.o bn_mp_or.o bn_mp_rand.o bn_mp_montgomery_calc_normalization.o \
+bn_mp_prime_is_divisible.o bn_prime_tab.o bn_mp_prime_fermat.o bn_mp_prime_miller_rabin.o \
+bn_mp_prime_is_prime.o bn_mp_prime_next_prime.o bn_mp_dr_reduce.o \
+bn_mp_dr_is_modulus.o bn_mp_dr_setup.o bn_mp_reduce_setup.o \
+bn_mp_toom_mul.o bn_mp_toom_sqr.o bn_mp_div_3.o bn_s_mp_exptmod.o \
+bn_mp_reduce_2k.o bn_mp_reduce_is_2k.o bn_mp_reduce_2k_setup.o \
+bn_mp_reduce_2k_l.o bn_mp_reduce_is_2k_l.o bn_mp_reduce_2k_setup_l.o \
+bn_mp_radix_smap.o bn_mp_read_radix.o bn_mp_toradix.o bn_mp_radix_size.o \
+bn_mp_fread.o bn_mp_fwrite.o bn_mp_cnt_lsb.o bn_error.o \
+bn_mp_init_multi.o bn_mp_clear_multi.o bn_mp_exteuclid.o bn_mp_toradix_n.o \
+bn_mp_prime_random_ex.o bn_mp_get_int.o bn_mp_sqrt.o bn_mp_is_square.o bn_mp_init_set.o \
+bn_mp_init_set_int.o bn_mp_invmod_slow.o bn_mp_prime_rabin_miller_trials.o \
+bn_mp_to_signed_bin_n.o bn_mp_to_unsigned_bin_n.o
+
+libtommath.a: $(OBJECTS)
+ $(AR) $(ARFLAGS) libtommath.a $(OBJECTS)
+ $(RANLIB) libtommath.a
+
+#make a profiled library (takes a while!!!)
+#
+# This will build the library with profile generation
+# then run the test demo and rebuild the library.
+#
+# So far I've seen improvements in the MP math
+profiled:
+ make CFLAGS="$(CFLAGS) -fprofile-arcs -DTESTING" timing
+ ./ltmtest
+ rm -f *.a *.o ltmtest
+ make CFLAGS="$(CFLAGS) -fbranch-probabilities"
+
+#make a single object profiled library
+profiled_single:
+ perl gen.pl
+ $(CC) $(CFLAGS) -fprofile-arcs -DTESTING -c mpi.c -o mpi.o
+ $(CC) $(CFLAGS) -DTESTING -DTIMER demo/timing.c mpi.o -o ltmtest
+ ./ltmtest
+ rm -f *.o ltmtest
+ $(CC) $(CFLAGS) -fbranch-probabilities -DTESTING -c mpi.c -o mpi.o
+ $(AR) $(ARFLAGS) libtommath.a mpi.o
+ ranlib libtommath.a
+
+install: libtommath.a
+ install -d -g $(GROUP) -o $(USER) $(DESTDIR)$(LIBPATH)
+ install -d -g $(GROUP) -o $(USER) $(DESTDIR)$(INCPATH)
+ install -g $(GROUP) -o $(USER) $(LIBNAME) $(DESTDIR)$(LIBPATH)
+ install -g $(GROUP) -o $(USER) $(HEADERS) $(DESTDIR)$(INCPATH)
+
+test: libtommath.a demo/demo.o
+ $(CC) $(CFLAGS) demo/demo.o libtommath.a -o test
+
+mtest: test
+ cd mtest ; $(CC) $(CFLAGS) mtest.c -o mtest
+
+timing: libtommath.a
+ $(CC) $(CFLAGS) -DTIMER demo/timing.c libtommath.a -o ltmtest
+
+# makes the LTM book DVI file, requires tetex, perl and makeindex [part of tetex I think]
+docdvi: tommath.src
+ cd pics ; make
+ echo "hello" > tommath.ind
+ perl booker.pl
+ latex tommath > /dev/null
+ latex tommath > /dev/null
+ makeindex tommath
+ latex tommath > /dev/null
+
+# poster, makes the single page PDF poster
+poster: poster.tex
+ pdflatex poster
+ rm -f poster.aux poster.log
+
+# makes the LTM book PDF file, requires tetex, cleans up the LaTeX temp files
+docs: docdvi
+ dvipdf tommath
+ rm -f tommath.log tommath.aux tommath.dvi tommath.idx tommath.toc tommath.lof tommath.ind tommath.ilg
+ cd pics ; make clean
+
+#LTM user manual
+mandvi: bn.tex
+ echo "hello" > bn.ind
+ latex bn > /dev/null
+ latex bn > /dev/null
+ makeindex bn
+ latex bn > /dev/null
+
+#LTM user manual [pdf]
+manual: mandvi
+ pdflatex bn >/dev/null
+ rm -f bn.aux bn.dvi bn.log bn.idx bn.lof bn.out bn.toc
+
+pretty:
+ perl pretty.build
+
+clean:
+ rm -f *.bat *.pdf *.o *.a *.obj *.lib *.exe *.dll etclib/*.o demo/demo.o test ltmtest mpitest mtest/mtest mtest/mtest.exe \
+ *.idx *.toc *.log *.aux *.dvi *.lof *.ind *.ilg *.ps *.log *.s mpi.c *.da *.dyn *.dpi tommath.tex *.lo *.la
+ rm -rf .libs
+ cd etc && make clean
+ cd pics && make clean
+
+zipup: clean manual poster docs
+ perl gen.pl ; mv mpi.c pre_gen/ ; \
+ cd .. ; rm -rf ltm* libtommath-$(VERSION) ; mkdir libtommath-$(VERSION) ; \
+ cp -R ./libtommath/* ./libtommath-$(VERSION)/ ; \
+ tar -c libtommath-$(VERSION)/* | bzip2 -9vvc > ltm-$(VERSION).tar.bz2 ; \
+ zip -9 -r ltm-$(VERSION).zip libtommath-$(VERSION)/*
diff --git a/libtommath/TODO b/libtommath/TODO
new file mode 100644
index 0000000..deffba1
--- /dev/null
+++ b/libtommath/TODO
@@ -0,0 +1,16 @@
+things for book in order of importance...
+
+- Fix up pseudo-code [only] for combas that are not consistent with source
+- Start in chapter 3 [basics] and work up...
+ - re-write to prose [less abrupt]
+ - clean up pseudo code [spacing]
+ - more examples where appropriate and figures
+
+Goal:
+ - Get sync done by mid January [roughly 8-12 hours work]
+ - Finish ch3-6 by end of January [roughly 12-16 hours of work]
+ - Finish ch7-end by mid Feb [roughly 20-24 hours of work].
+
+Goal isn't "first edition" but merely cleaner to read.
+
+
diff --git a/bn.tex b/libtommath/bn.tex
index 244bd6f..244bd6f 100644
--- a/bn.tex
+++ b/libtommath/bn.tex
diff --git a/bn_error.c b/libtommath/bn_error.c
index 1546784..1546784 100644
--- a/bn_error.c
+++ b/libtommath/bn_error.c
diff --git a/bn_fast_mp_invmod.c b/libtommath/bn_fast_mp_invmod.c
index acc8364..acc8364 100644
--- a/bn_fast_mp_invmod.c
+++ b/libtommath/bn_fast_mp_invmod.c
diff --git a/bn_fast_mp_montgomery_reduce.c b/libtommath/bn_fast_mp_montgomery_reduce.c
index 14f307f..14f307f 100644
--- a/bn_fast_mp_montgomery_reduce.c
+++ b/libtommath/bn_fast_mp_montgomery_reduce.c
diff --git a/bn_fast_s_mp_mul_digs.c b/libtommath/bn_fast_s_mp_mul_digs.c
index df3da26..df3da26 100644
--- a/bn_fast_s_mp_mul_digs.c
+++ b/libtommath/bn_fast_s_mp_mul_digs.c
diff --git a/bn_fast_s_mp_mul_high_digs.c b/libtommath/bn_fast_s_mp_mul_high_digs.c
index ee657f9..ee657f9 100644
--- a/bn_fast_s_mp_mul_high_digs.c
+++ b/libtommath/bn_fast_s_mp_mul_high_digs.c
diff --git a/bn_fast_s_mp_sqr.c b/libtommath/bn_fast_s_mp_sqr.c
index 66a2942..66a2942 100644
--- a/bn_fast_s_mp_sqr.c
+++ b/libtommath/bn_fast_s_mp_sqr.c
diff --git a/bn_mp_2expt.c b/libtommath/bn_mp_2expt.c
index 45a6818..45a6818 100644
--- a/bn_mp_2expt.c
+++ b/libtommath/bn_mp_2expt.c
diff --git a/bn_mp_abs.c b/libtommath/bn_mp_abs.c
index 34f810f..34f810f 100644
--- a/bn_mp_abs.c
+++ b/libtommath/bn_mp_abs.c
diff --git a/bn_mp_add.c b/libtommath/bn_mp_add.c
index 554b7f7..554b7f7 100644
--- a/bn_mp_add.c
+++ b/libtommath/bn_mp_add.c
diff --git a/bn_mp_add_d.c b/libtommath/bn_mp_add_d.c
index bdd0280..bdd0280 100644
--- a/bn_mp_add_d.c
+++ b/libtommath/bn_mp_add_d.c
diff --git a/bn_mp_addmod.c b/libtommath/bn_mp_addmod.c
index 13eb33f..13eb33f 100644
--- a/bn_mp_addmod.c
+++ b/libtommath/bn_mp_addmod.c
diff --git a/bn_mp_and.c b/libtommath/bn_mp_and.c
index 61dc386..61dc386 100644
--- a/bn_mp_and.c
+++ b/libtommath/bn_mp_and.c
diff --git a/bn_mp_clamp.c b/libtommath/bn_mp_clamp.c
index c172611..c172611 100644
--- a/bn_mp_clamp.c
+++ b/libtommath/bn_mp_clamp.c
diff --git a/bn_mp_clear.c b/libtommath/bn_mp_clear.c
index 1dc053b..1dc053b 100644
--- a/bn_mp_clear.c
+++ b/libtommath/bn_mp_clear.c
diff --git a/bn_mp_clear_multi.c b/libtommath/bn_mp_clear_multi.c
index 24cbe73..24cbe73 100644
--- a/bn_mp_clear_multi.c
+++ b/libtommath/bn_mp_clear_multi.c
diff --git a/bn_mp_cmp.c b/libtommath/bn_mp_cmp.c
index 583b5f8..583b5f8 100644
--- a/bn_mp_cmp.c
+++ b/libtommath/bn_mp_cmp.c
diff --git a/bn_mp_cmp_d.c b/libtommath/bn_mp_cmp_d.c
index 882b1c9..882b1c9 100644
--- a/bn_mp_cmp_d.c
+++ b/libtommath/bn_mp_cmp_d.c
diff --git a/bn_mp_cmp_mag.c b/libtommath/bn_mp_cmp_mag.c
index a0f351c..a0f351c 100644
--- a/bn_mp_cmp_mag.c
+++ b/libtommath/bn_mp_cmp_mag.c
diff --git a/bn_mp_cnt_lsb.c b/libtommath/bn_mp_cnt_lsb.c
index 571f03f..571f03f 100644
--- a/bn_mp_cnt_lsb.c
+++ b/libtommath/bn_mp_cnt_lsb.c
diff --git a/bn_mp_copy.c b/libtommath/bn_mp_copy.c
index 183ec9b..183ec9b 100644
--- a/bn_mp_copy.c
+++ b/libtommath/bn_mp_copy.c
diff --git a/bn_mp_count_bits.c b/libtommath/bn_mp_count_bits.c
index f3f85ac..f3f85ac 100644
--- a/bn_mp_count_bits.c
+++ b/libtommath/bn_mp_count_bits.c
diff --git a/bn_mp_div.c b/libtommath/bn_mp_div.c
index 6b2b8f0..6b2b8f0 100644
--- a/bn_mp_div.c
+++ b/libtommath/bn_mp_div.c
diff --git a/bn_mp_div_2.c b/libtommath/bn_mp_div_2.c
index 5777997..5777997 100644
--- a/bn_mp_div_2.c
+++ b/libtommath/bn_mp_div_2.c
diff --git a/bn_mp_div_2d.c b/libtommath/bn_mp_div_2d.c
index cf103f2..cf103f2 100644
--- a/bn_mp_div_2d.c
+++ b/libtommath/bn_mp_div_2d.c
diff --git a/bn_mp_div_3.c b/libtommath/bn_mp_div_3.c
index 7cbafc1..7cbafc1 100644
--- a/bn_mp_div_3.c
+++ b/libtommath/bn_mp_div_3.c
diff --git a/bn_mp_div_d.c b/libtommath/bn_mp_div_d.c
index 9b58aa6..9b58aa6 100644
--- a/bn_mp_div_d.c
+++ b/libtommath/bn_mp_div_d.c
diff --git a/bn_mp_dr_is_modulus.c b/libtommath/bn_mp_dr_is_modulus.c
index 5ef78a3..5ef78a3 100644
--- a/bn_mp_dr_is_modulus.c
+++ b/libtommath/bn_mp_dr_is_modulus.c
diff --git a/bn_mp_dr_reduce.c b/libtommath/bn_mp_dr_reduce.c
index 9bb7ad7..9bb7ad7 100644
--- a/bn_mp_dr_reduce.c
+++ b/libtommath/bn_mp_dr_reduce.c
diff --git a/bn_mp_dr_setup.c b/libtommath/bn_mp_dr_setup.c
index 029d310..029d310 100644
--- a/bn_mp_dr_setup.c
+++ b/libtommath/bn_mp_dr_setup.c
diff --git a/bn_mp_exch.c b/libtommath/bn_mp_exch.c
index 0ef485a..0ef485a 100644
--- a/bn_mp_exch.c
+++ b/libtommath/bn_mp_exch.c
diff --git a/bn_mp_expt_d.c b/libtommath/bn_mp_expt_d.c
index fdb8bd9..fdb8bd9 100644
--- a/bn_mp_expt_d.c
+++ b/libtommath/bn_mp_expt_d.c
diff --git a/bn_mp_exptmod.c b/libtommath/bn_mp_exptmod.c
index 7c4e2f8..7c4e2f8 100644
--- a/bn_mp_exptmod.c
+++ b/libtommath/bn_mp_exptmod.c
diff --git a/bn_mp_exptmod_fast.c b/libtommath/bn_mp_exptmod_fast.c
index 82be9ac..82be9ac 100644
--- a/bn_mp_exptmod_fast.c
+++ b/libtommath/bn_mp_exptmod_fast.c
diff --git a/bn_mp_exteuclid.c b/libtommath/bn_mp_exteuclid.c
index c4ebab4..c4ebab4 100644
--- a/bn_mp_exteuclid.c
+++ b/libtommath/bn_mp_exteuclid.c
diff --git a/bn_mp_fread.c b/libtommath/bn_mp_fread.c
index 293df3f..293df3f 100644
--- a/bn_mp_fread.c
+++ b/libtommath/bn_mp_fread.c
diff --git a/bn_mp_fwrite.c b/libtommath/bn_mp_fwrite.c
index 8fa3129..8fa3129 100644
--- a/bn_mp_fwrite.c
+++ b/libtommath/bn_mp_fwrite.c
diff --git a/bn_mp_gcd.c b/libtommath/bn_mp_gcd.c
index 6265df1..6265df1 100644
--- a/bn_mp_gcd.c
+++ b/libtommath/bn_mp_gcd.c
diff --git a/bn_mp_get_int.c b/libtommath/bn_mp_get_int.c
index 034467b..034467b 100644
--- a/bn_mp_get_int.c
+++ b/libtommath/bn_mp_get_int.c
diff --git a/bn_mp_grow.c b/libtommath/bn_mp_grow.c
index 12a78a8..12a78a8 100644
--- a/bn_mp_grow.c
+++ b/libtommath/bn_mp_grow.c
diff --git a/bn_mp_init.c b/libtommath/bn_mp_init.c
index 9d70554..9d70554 100644
--- a/bn_mp_init.c
+++ b/libtommath/bn_mp_init.c
diff --git a/bn_mp_init_copy.c b/libtommath/bn_mp_init_copy.c
index b1b0fa2..b1b0fa2 100644
--- a/bn_mp_init_copy.c
+++ b/libtommath/bn_mp_init_copy.c
diff --git a/bn_mp_init_multi.c b/libtommath/bn_mp_init_multi.c
index 8cb123a..8cb123a 100644
--- a/bn_mp_init_multi.c
+++ b/libtommath/bn_mp_init_multi.c
diff --git a/bn_mp_init_set.c b/libtommath/bn_mp_init_set.c
index 0251e61..0251e61 100644
--- a/bn_mp_init_set.c
+++ b/libtommath/bn_mp_init_set.c
diff --git a/bn_mp_init_set_int.c b/libtommath/bn_mp_init_set_int.c
index f59fd19..f59fd19 100644
--- a/bn_mp_init_set_int.c
+++ b/libtommath/bn_mp_init_set_int.c
diff --git a/bn_mp_init_size.c b/libtommath/bn_mp_init_size.c
index 845ce2c..845ce2c 100644
--- a/bn_mp_init_size.c
+++ b/libtommath/bn_mp_init_size.c
diff --git a/bn_mp_invmod.c b/libtommath/bn_mp_invmod.c
index 46118ad..46118ad 100644
--- a/bn_mp_invmod.c
+++ b/libtommath/bn_mp_invmod.c
diff --git a/bn_mp_invmod_slow.c b/libtommath/bn_mp_invmod_slow.c
index c048655..c048655 100644
--- a/bn_mp_invmod_slow.c
+++ b/libtommath/bn_mp_invmod_slow.c
diff --git a/bn_mp_is_square.c b/libtommath/bn_mp_is_square.c
index 969d237..969d237 100644
--- a/bn_mp_is_square.c
+++ b/libtommath/bn_mp_is_square.c
diff --git a/bn_mp_jacobi.c b/libtommath/bn_mp_jacobi.c
index 74cbbf3..74cbbf3 100644
--- a/bn_mp_jacobi.c
+++ b/libtommath/bn_mp_jacobi.c
diff --git a/bn_mp_karatsuba_mul.c b/libtommath/bn_mp_karatsuba_mul.c
index daa78c7..daa78c7 100644
--- a/bn_mp_karatsuba_mul.c
+++ b/libtommath/bn_mp_karatsuba_mul.c
diff --git a/bn_mp_karatsuba_sqr.c b/libtommath/bn_mp_karatsuba_sqr.c
index 315ceab..315ceab 100644
--- a/bn_mp_karatsuba_sqr.c
+++ b/libtommath/bn_mp_karatsuba_sqr.c
diff --git a/bn_mp_lcm.c b/libtommath/bn_mp_lcm.c
index 8e3a759..8e3a759 100644
--- a/bn_mp_lcm.c
+++ b/libtommath/bn_mp_lcm.c
diff --git a/bn_mp_lshd.c b/libtommath/bn_mp_lshd.c
index 398b648..398b648 100644
--- a/bn_mp_lshd.c
+++ b/libtommath/bn_mp_lshd.c
diff --git a/bn_mp_mod.c b/libtommath/bn_mp_mod.c
index 75779bb..75779bb 100644
--- a/bn_mp_mod.c
+++ b/libtommath/bn_mp_mod.c
diff --git a/bn_mp_mod_2d.c b/libtommath/bn_mp_mod_2d.c
index 589e4ba..589e4ba 100644
--- a/bn_mp_mod_2d.c
+++ b/libtommath/bn_mp_mod_2d.c
diff --git a/bn_mp_mod_d.c b/libtommath/bn_mp_mod_d.c
index 8a2ad24..8a2ad24 100644
--- a/bn_mp_mod_d.c
+++ b/libtommath/bn_mp_mod_d.c
diff --git a/bn_mp_montgomery_calc_normalization.c b/libtommath/bn_mp_montgomery_calc_normalization.c
index e2efc34..e2efc34 100644
--- a/bn_mp_montgomery_calc_normalization.c
+++ b/libtommath/bn_mp_montgomery_calc_normalization.c
diff --git a/bn_mp_montgomery_reduce.c b/libtommath/bn_mp_montgomery_reduce.c
index 3095fa7..3095fa7 100644
--- a/bn_mp_montgomery_reduce.c
+++ b/libtommath/bn_mp_montgomery_reduce.c
diff --git a/bn_mp_montgomery_setup.c b/libtommath/bn_mp_montgomery_setup.c
index 9dfc087..9dfc087 100644
--- a/bn_mp_montgomery_setup.c
+++ b/libtommath/bn_mp_montgomery_setup.c
diff --git a/bn_mp_mul.c b/libtommath/bn_mp_mul.c
index f9cfa09..f9cfa09 100644
--- a/bn_mp_mul.c
+++ b/libtommath/bn_mp_mul.c
diff --git a/bn_mp_mul_2.c b/libtommath/bn_mp_mul_2.c
index 6936681..6936681 100644
--- a/bn_mp_mul_2.c
+++ b/libtommath/bn_mp_mul_2.c
diff --git a/bn_mp_mul_2d.c b/libtommath/bn_mp_mul_2d.c
index 04cb8dd..04cb8dd 100644
--- a/bn_mp_mul_2d.c
+++ b/libtommath/bn_mp_mul_2d.c
diff --git a/bn_mp_mul_d.c b/libtommath/bn_mp_mul_d.c
index 9e11eef..9e11eef 100644
--- a/bn_mp_mul_d.c
+++ b/libtommath/bn_mp_mul_d.c
diff --git a/bn_mp_mulmod.c b/libtommath/bn_mp_mulmod.c
index d34e90a..d34e90a 100644
--- a/bn_mp_mulmod.c
+++ b/libtommath/bn_mp_mulmod.c
diff --git a/bn_mp_n_root.c b/libtommath/bn_mp_n_root.c
index 7b11aa2..7b11aa2 100644
--- a/bn_mp_n_root.c
+++ b/libtommath/bn_mp_n_root.c
diff --git a/bn_mp_neg.c b/libtommath/bn_mp_neg.c
index 159cd74..159cd74 100644
--- a/bn_mp_neg.c
+++ b/libtommath/bn_mp_neg.c
diff --git a/bn_mp_or.c b/libtommath/bn_mp_or.c
index dccee7e..dccee7e 100644
--- a/bn_mp_or.c
+++ b/libtommath/bn_mp_or.c
diff --git a/bn_mp_prime_fermat.c b/libtommath/bn_mp_prime_fermat.c
index fd74dbe..fd74dbe 100644
--- a/bn_mp_prime_fermat.c
+++ b/libtommath/bn_mp_prime_fermat.c
diff --git a/bn_mp_prime_is_divisible.c b/libtommath/bn_mp_prime_is_divisible.c
index f85fe7c..f85fe7c 100644
--- a/bn_mp_prime_is_divisible.c
+++ b/libtommath/bn_mp_prime_is_divisible.c
diff --git a/bn_mp_prime_is_prime.c b/libtommath/bn_mp_prime_is_prime.c
index 188053a..188053a 100644
--- a/bn_mp_prime_is_prime.c
+++ b/libtommath/bn_mp_prime_is_prime.c
diff --git a/bn_mp_prime_miller_rabin.c b/libtommath/bn_mp_prime_miller_rabin.c
index 758a2c3..758a2c3 100644
--- a/bn_mp_prime_miller_rabin.c
+++ b/libtommath/bn_mp_prime_miller_rabin.c
diff --git a/bn_mp_prime_next_prime.c b/libtommath/bn_mp_prime_next_prime.c
index 24f93c4..24f93c4 100644
--- a/bn_mp_prime_next_prime.c
+++ b/libtommath/bn_mp_prime_next_prime.c
diff --git a/bn_mp_prime_rabin_miller_trials.c b/libtommath/bn_mp_prime_rabin_miller_trials.c
index d1d0867..d1d0867 100644
--- a/bn_mp_prime_rabin_miller_trials.c
+++ b/libtommath/bn_mp_prime_rabin_miller_trials.c
diff --git a/bn_mp_prime_random_ex.c b/libtommath/bn_mp_prime_random_ex.c
index 78c0583..78c0583 100644
--- a/bn_mp_prime_random_ex.c
+++ b/libtommath/bn_mp_prime_random_ex.c
diff --git a/bn_mp_radix_size.c b/libtommath/bn_mp_radix_size.c
index 3d423ba..3d423ba 100644
--- a/bn_mp_radix_size.c
+++ b/libtommath/bn_mp_radix_size.c
diff --git a/bn_mp_radix_smap.c b/libtommath/bn_mp_radix_smap.c
index bc7517d..bc7517d 100644
--- a/bn_mp_radix_smap.c
+++ b/libtommath/bn_mp_radix_smap.c
diff --git a/bn_mp_rand.c b/libtommath/bn_mp_rand.c
index 0dc7019..0dc7019 100644
--- a/bn_mp_rand.c
+++ b/libtommath/bn_mp_rand.c
diff --git a/bn_mp_read_radix.c b/libtommath/bn_mp_read_radix.c
index 1ec3937..1ec3937 100644
--- a/bn_mp_read_radix.c
+++ b/libtommath/bn_mp_read_radix.c
diff --git a/bn_mp_read_signed_bin.c b/libtommath/bn_mp_read_signed_bin.c
index 814d6c1..814d6c1 100644
--- a/bn_mp_read_signed_bin.c
+++ b/libtommath/bn_mp_read_signed_bin.c
diff --git a/bn_mp_read_unsigned_bin.c b/libtommath/bn_mp_read_unsigned_bin.c
index 946457d..946457d 100644
--- a/bn_mp_read_unsigned_bin.c
+++ b/libtommath/bn_mp_read_unsigned_bin.c
diff --git a/bn_mp_reduce.c b/libtommath/bn_mp_reduce.c
index d746445..d746445 100644
--- a/bn_mp_reduce.c
+++ b/libtommath/bn_mp_reduce.c
diff --git a/bn_mp_reduce_2k.c b/libtommath/bn_mp_reduce_2k.c
index 28c3a00..28c3a00 100644
--- a/bn_mp_reduce_2k.c
+++ b/libtommath/bn_mp_reduce_2k.c
diff --git a/bn_mp_reduce_2k_l.c b/libtommath/bn_mp_reduce_2k_l.c
index 1d7e1f0..1d7e1f0 100644
--- a/bn_mp_reduce_2k_l.c
+++ b/libtommath/bn_mp_reduce_2k_l.c
diff --git a/bn_mp_reduce_2k_setup.c b/libtommath/bn_mp_reduce_2k_setup.c
index 585e1b7..585e1b7 100644
--- a/bn_mp_reduce_2k_setup.c
+++ b/libtommath/bn_mp_reduce_2k_setup.c
diff --git a/bn_mp_reduce_2k_setup_l.c b/libtommath/bn_mp_reduce_2k_setup_l.c
index 810a456..810a456 100644
--- a/bn_mp_reduce_2k_setup_l.c
+++ b/libtommath/bn_mp_reduce_2k_setup_l.c
diff --git a/bn_mp_reduce_is_2k.c b/libtommath/bn_mp_reduce_is_2k.c
index 0fb8384..0fb8384 100644
--- a/bn_mp_reduce_is_2k.c
+++ b/libtommath/bn_mp_reduce_is_2k.c
diff --git a/bn_mp_reduce_is_2k_l.c b/libtommath/bn_mp_reduce_is_2k_l.c
index ceba0ed..ceba0ed 100644
--- a/bn_mp_reduce_is_2k_l.c
+++ b/libtommath/bn_mp_reduce_is_2k_l.c
diff --git a/bn_mp_reduce_setup.c b/libtommath/bn_mp_reduce_setup.c
index 99f158a..99f158a 100644
--- a/bn_mp_reduce_setup.c
+++ b/libtommath/bn_mp_reduce_setup.c
diff --git a/bn_mp_rshd.c b/libtommath/bn_mp_rshd.c
index 913dda6..913dda6 100644
--- a/bn_mp_rshd.c
+++ b/libtommath/bn_mp_rshd.c
diff --git a/bn_mp_set.c b/libtommath/bn_mp_set.c
index 078fd5f..078fd5f 100644
--- a/bn_mp_set.c
+++ b/libtommath/bn_mp_set.c
diff --git a/bn_mp_set_int.c b/libtommath/bn_mp_set_int.c
index bd47136..bd47136 100644
--- a/bn_mp_set_int.c
+++ b/libtommath/bn_mp_set_int.c
diff --git a/bn_mp_shrink.c b/libtommath/bn_mp_shrink.c
index b31f9d2..b31f9d2 100644
--- a/bn_mp_shrink.c
+++ b/libtommath/bn_mp_shrink.c
diff --git a/bn_mp_signed_bin_size.c b/libtommath/bn_mp_signed_bin_size.c
index 30048cb..30048cb 100644
--- a/bn_mp_signed_bin_size.c
+++ b/libtommath/bn_mp_signed_bin_size.c
diff --git a/bn_mp_sqr.c b/libtommath/bn_mp_sqr.c
index b1fdb57..b1fdb57 100644
--- a/bn_mp_sqr.c
+++ b/libtommath/bn_mp_sqr.c
diff --git a/bn_mp_sqrmod.c b/libtommath/bn_mp_sqrmod.c
index 1923be4..1923be4 100644
--- a/bn_mp_sqrmod.c
+++ b/libtommath/bn_mp_sqrmod.c
diff --git a/bn_mp_sqrt.c b/libtommath/bn_mp_sqrt.c
index 76cec87..76cec87 100644
--- a/bn_mp_sqrt.c
+++ b/libtommath/bn_mp_sqrt.c
diff --git a/bn_mp_sub.c b/libtommath/bn_mp_sub.c
index 97495f4..97495f4 100644
--- a/bn_mp_sub.c
+++ b/libtommath/bn_mp_sub.c
diff --git a/bn_mp_sub_d.c b/libtommath/bn_mp_sub_d.c
index 4923dde..4923dde 100644
--- a/bn_mp_sub_d.c
+++ b/libtommath/bn_mp_sub_d.c
diff --git a/bn_mp_submod.c b/libtommath/bn_mp_submod.c
index b999c85..b999c85 100644
--- a/bn_mp_submod.c
+++ b/libtommath/bn_mp_submod.c
diff --git a/bn_mp_to_signed_bin.c b/libtommath/bn_mp_to_signed_bin.c
index b0a597e..b0a597e 100644
--- a/bn_mp_to_signed_bin.c
+++ b/libtommath/bn_mp_to_signed_bin.c
diff --git a/bn_mp_to_signed_bin_n.c b/libtommath/bn_mp_to_signed_bin_n.c
index 0f765ee..0f765ee 100644
--- a/bn_mp_to_signed_bin_n.c
+++ b/libtommath/bn_mp_to_signed_bin_n.c
diff --git a/bn_mp_to_unsigned_bin.c b/libtommath/bn_mp_to_unsigned_bin.c
index 000967e..000967e 100644
--- a/bn_mp_to_unsigned_bin.c
+++ b/libtommath/bn_mp_to_unsigned_bin.c
diff --git a/bn_mp_to_unsigned_bin_n.c b/libtommath/bn_mp_to_unsigned_bin_n.c
index d0256b4..d0256b4 100644
--- a/bn_mp_to_unsigned_bin_n.c
+++ b/libtommath/bn_mp_to_unsigned_bin_n.c
diff --git a/bn_mp_toom_mul.c b/libtommath/bn_mp_toom_mul.c
index 125331b..125331b 100644
--- a/bn_mp_toom_mul.c
+++ b/libtommath/bn_mp_toom_mul.c
diff --git a/bn_mp_toom_sqr.c b/libtommath/bn_mp_toom_sqr.c
index 8c46fea..8c46fea 100644
--- a/bn_mp_toom_sqr.c
+++ b/libtommath/bn_mp_toom_sqr.c
diff --git a/bn_mp_toradix.c b/libtommath/bn_mp_toradix.c
index a206d5e..a206d5e 100644
--- a/bn_mp_toradix.c
+++ b/libtommath/bn_mp_toradix.c
diff --git a/bn_mp_toradix_n.c b/libtommath/bn_mp_toradix_n.c
index 7d43558..7d43558 100644
--- a/bn_mp_toradix_n.c
+++ b/libtommath/bn_mp_toradix_n.c
diff --git a/bn_mp_unsigned_bin_size.c b/libtommath/bn_mp_unsigned_bin_size.c
index 091f406..091f406 100644
--- a/bn_mp_unsigned_bin_size.c
+++ b/libtommath/bn_mp_unsigned_bin_size.c
diff --git a/bn_mp_xor.c b/libtommath/bn_mp_xor.c
index de7e62c..de7e62c 100644
--- a/bn_mp_xor.c
+++ b/libtommath/bn_mp_xor.c
diff --git a/bn_mp_zero.c b/libtommath/bn_mp_zero.c
index c8d8907..c8d8907 100644
--- a/bn_mp_zero.c
+++ b/libtommath/bn_mp_zero.c
diff --git a/bn_prime_tab.c b/libtommath/bn_prime_tab.c
index 14306c2..14306c2 100644
--- a/bn_prime_tab.c
+++ b/libtommath/bn_prime_tab.c
diff --git a/bn_reverse.c b/libtommath/bn_reverse.c
index 851a6e8..851a6e8 100644
--- a/bn_reverse.c
+++ b/libtommath/bn_reverse.c
diff --git a/bn_s_mp_add.c b/libtommath/bn_s_mp_add.c
index 2b378ae..2b378ae 100644
--- a/bn_s_mp_add.c
+++ b/libtommath/bn_s_mp_add.c
diff --git a/bn_s_mp_exptmod.c b/libtommath/bn_s_mp_exptmod.c
index 597e877..597e877 100644
--- a/bn_s_mp_exptmod.c
+++ b/libtommath/bn_s_mp_exptmod.c
diff --git a/bn_s_mp_mul_digs.c b/libtommath/bn_s_mp_mul_digs.c
index b40ae2e..b40ae2e 100644
--- a/bn_s_mp_mul_digs.c
+++ b/libtommath/bn_s_mp_mul_digs.c
diff --git a/bn_s_mp_mul_high_digs.c b/libtommath/bn_s_mp_mul_high_digs.c
index a060248..a060248 100644
--- a/bn_s_mp_mul_high_digs.c
+++ b/libtommath/bn_s_mp_mul_high_digs.c
diff --git a/bn_s_mp_sqr.c b/libtommath/bn_s_mp_sqr.c
index 9cdb563..9cdb563 100644
--- a/bn_s_mp_sqr.c
+++ b/libtommath/bn_s_mp_sqr.c
diff --git a/bn_s_mp_sub.c b/libtommath/bn_s_mp_sub.c
index 5b7aef9..5b7aef9 100644
--- a/bn_s_mp_sub.c
+++ b/libtommath/bn_s_mp_sub.c
diff --git a/bncore.c b/libtommath/bncore.c
index 82e3132..82e3132 100644
--- a/bncore.c
+++ b/libtommath/bncore.c
diff --git a/booker.pl b/libtommath/booker.pl
index 5c77e53..5c77e53 100644
--- a/booker.pl
+++ b/libtommath/booker.pl
diff --git a/callgraph.txt b/libtommath/callgraph.txt
index 2efcf24..2efcf24 100644
--- a/callgraph.txt
+++ b/libtommath/callgraph.txt
diff --git a/changes.txt b/libtommath/changes.txt
index 99e40c1..99e40c1 100644
--- a/changes.txt
+++ b/libtommath/changes.txt
diff --git a/demo/demo.c b/libtommath/demo/demo.c
index 0a6115a..0a6115a 100644
--- a/demo/demo.c
+++ b/libtommath/demo/demo.c
diff --git a/demo/timing.c b/libtommath/demo/timing.c
index bb3be52..bb3be52 100644
--- a/demo/timing.c
+++ b/libtommath/demo/timing.c
diff --git a/dep.pl b/libtommath/dep.pl
index c39e27e..c39e27e 100644
--- a/dep.pl
+++ b/libtommath/dep.pl
diff --git a/etc/2kprime.1 b/libtommath/etc/2kprime.1
index c41ded1..c41ded1 100644
--- a/etc/2kprime.1
+++ b/libtommath/etc/2kprime.1
diff --git a/etc/2kprime.c b/libtommath/etc/2kprime.c
index d48b83e..d48b83e 100644
--- a/etc/2kprime.c
+++ b/libtommath/etc/2kprime.c
diff --git a/etc/drprime.c b/libtommath/etc/drprime.c
index 0ab8ea6..0ab8ea6 100644
--- a/etc/drprime.c
+++ b/libtommath/etc/drprime.c
diff --git a/etc/drprimes.28 b/libtommath/etc/drprimes.28
index 9d438ad..9d438ad 100644
--- a/etc/drprimes.28
+++ b/libtommath/etc/drprimes.28
diff --git a/etc/drprimes.txt b/libtommath/etc/drprimes.txt
index 2c887ea..2c887ea 100644
--- a/etc/drprimes.txt
+++ b/libtommath/etc/drprimes.txt
diff --git a/etc/makefile b/libtommath/etc/makefile
index 99154d8..99154d8 100644
--- a/etc/makefile
+++ b/libtommath/etc/makefile
diff --git a/etc/makefile.icc b/libtommath/etc/makefile.icc
index 0a50728..0a50728 100644
--- a/etc/makefile.icc
+++ b/libtommath/etc/makefile.icc
diff --git a/etc/makefile.msvc b/libtommath/etc/makefile.msvc
index 2833372..2833372 100644
--- a/etc/makefile.msvc
+++ b/libtommath/etc/makefile.msvc
diff --git a/etc/mersenne.c b/libtommath/etc/mersenne.c
index 1cd5b50..1cd5b50 100644
--- a/etc/mersenne.c
+++ b/libtommath/etc/mersenne.c
diff --git a/etc/mont.c b/libtommath/etc/mont.c
index dbf1735..dbf1735 100644
--- a/etc/mont.c
+++ b/libtommath/etc/mont.c
diff --git a/etc/pprime.c b/libtommath/etc/pprime.c
index 26e0d84..26e0d84 100644
--- a/etc/pprime.c
+++ b/libtommath/etc/pprime.c
diff --git a/etc/prime.1024 b/libtommath/etc/prime.1024
index 5636e2d..5636e2d 100644
--- a/etc/prime.1024
+++ b/libtommath/etc/prime.1024
diff --git a/etc/prime.512 b/libtommath/etc/prime.512
index cb6ec30..cb6ec30 100644
--- a/etc/prime.512
+++ b/libtommath/etc/prime.512
diff --git a/etc/timer.asm b/libtommath/etc/timer.asm
index 35890d9..35890d9 100644
--- a/etc/timer.asm
+++ b/libtommath/etc/timer.asm
diff --git a/etc/tune.c b/libtommath/etc/tune.c
index d054d10..d054d10 100644
--- a/etc/tune.c
+++ b/libtommath/etc/tune.c
diff --git a/gen.pl b/libtommath/gen.pl
index 7236591..7236591 100644
--- a/gen.pl
+++ b/libtommath/gen.pl
diff --git a/logs/README b/libtommath/logs/README
index ea20c81..ea20c81 100644
--- a/logs/README
+++ b/libtommath/logs/README
diff --git a/logs/add.log b/libtommath/logs/add.log
index 43503ac..43503ac 100644
--- a/logs/add.log
+++ b/libtommath/logs/add.log
diff --git a/logs/addsub.png b/libtommath/logs/addsub.png
index a5679ac..a5679ac 100644
--- a/logs/addsub.png
+++ b/libtommath/logs/addsub.png
Binary files differ
diff --git a/logs/expt.log b/libtommath/logs/expt.log
index 920ba55..920ba55 100644
--- a/logs/expt.log
+++ b/libtommath/logs/expt.log
diff --git a/logs/expt.png b/libtommath/logs/expt.png
index 9ee8bb7..9ee8bb7 100644
--- a/logs/expt.png
+++ b/libtommath/logs/expt.png
Binary files differ
diff --git a/logs/expt_2k.log b/libtommath/logs/expt_2k.log
index 56b50db..56b50db 100644
--- a/logs/expt_2k.log
+++ b/libtommath/logs/expt_2k.log
diff --git a/logs/expt_2kl.log b/libtommath/logs/expt_2kl.log
index b2eb8c2..b2eb8c2 100644
--- a/logs/expt_2kl.log
+++ b/libtommath/logs/expt_2kl.log
diff --git a/logs/expt_dr.log b/libtommath/logs/expt_dr.log
index eb93fc9..eb93fc9 100644
--- a/logs/expt_dr.log
+++ b/libtommath/logs/expt_dr.log
diff --git a/logs/graphs.dem b/libtommath/logs/graphs.dem
index dfaf613..dfaf613 100644
--- a/logs/graphs.dem
+++ b/libtommath/logs/graphs.dem
diff --git a/logs/index.html b/libtommath/logs/index.html
index 19fe403..19fe403 100644
--- a/logs/index.html
+++ b/libtommath/logs/index.html
diff --git a/poster.out b/libtommath/logs/invmod.log
index e69de29..e69de29 100644
--- a/poster.out
+++ b/libtommath/logs/invmod.log
diff --git a/logs/invmod.png b/libtommath/logs/invmod.png
index 0a8a4ad..0a8a4ad 100644
--- a/logs/invmod.png
+++ b/libtommath/logs/invmod.png
Binary files differ
diff --git a/logs/mult.log b/libtommath/logs/mult.log
index 33563fc..33563fc 100644
--- a/logs/mult.log
+++ b/libtommath/logs/mult.log
diff --git a/logs/mult.png b/libtommath/logs/mult.png
index 4f7a4ee..4f7a4ee 100644
--- a/logs/mult.png
+++ b/libtommath/logs/mult.png
Binary files differ
diff --git a/logs/mult_kara.log b/libtommath/logs/mult_kara.log
index 7136c79..7136c79 100644
--- a/logs/mult_kara.log
+++ b/libtommath/logs/mult_kara.log
diff --git a/logs/sqr.log b/libtommath/logs/sqr.log
index cd29fc5..cd29fc5 100644
--- a/logs/sqr.log
+++ b/libtommath/logs/sqr.log
diff --git a/logs/sqr.old b/libtommath/logs/sqr.old
index 3c85882..3c85882 100644
--- a/logs/sqr.old
+++ b/libtommath/logs/sqr.old
diff --git a/logs/sqr_kara.log b/libtommath/logs/sqr_kara.log
index 06355a7..06355a7 100644
--- a/logs/sqr_kara.log
+++ b/libtommath/logs/sqr_kara.log
diff --git a/logs/sub.log b/libtommath/logs/sub.log
index 9f84fa2..9f84fa2 100644
--- a/logs/sub.log
+++ b/libtommath/logs/sub.log
diff --git a/makefile.bcc b/libtommath/makefile.bcc
index 647c69a..647c69a 100644
--- a/makefile.bcc
+++ b/libtommath/makefile.bcc
diff --git a/makefile.cygwin_dll b/libtommath/makefile.cygwin_dll
index 85b10c7..85b10c7 100644
--- a/makefile.cygwin_dll
+++ b/libtommath/makefile.cygwin_dll
diff --git a/makefile.icc b/libtommath/makefile.icc
index e764253..e764253 100644
--- a/makefile.icc
+++ b/libtommath/makefile.icc
diff --git a/makefile.msvc b/libtommath/makefile.msvc
index dbbf9f3..dbbf9f3 100644
--- a/makefile.msvc
+++ b/libtommath/makefile.msvc
diff --git a/makefile.shared b/libtommath/makefile.shared
index 7c35881..7c35881 100644
--- a/makefile.shared
+++ b/libtommath/makefile.shared
diff --git a/mtest/logtab.h b/libtommath/mtest/logtab.h
index 68462bd..68462bd 100644
--- a/mtest/logtab.h
+++ b/libtommath/mtest/logtab.h
diff --git a/mtest/mpi-config.h b/libtommath/mtest/mpi-config.h
index 1baf2c2..1baf2c2 100644
--- a/mtest/mpi-config.h
+++ b/libtommath/mtest/mpi-config.h
diff --git a/mtest/mpi-types.h b/libtommath/mtest/mpi-types.h
index e097188..e097188 100644
--- a/mtest/mpi-types.h
+++ b/libtommath/mtest/mpi-types.h
diff --git a/mtest/mpi.c b/libtommath/mtest/mpi.c
index f7688f3..f7688f3 100644
--- a/mtest/mpi.c
+++ b/libtommath/mtest/mpi.c
diff --git a/mtest/mpi.h b/libtommath/mtest/mpi.h
index f7a3d14..f7a3d14 100644
--- a/mtest/mpi.h
+++ b/libtommath/mtest/mpi.h
diff --git a/mtest/mtest.c b/libtommath/mtest/mtest.c
index d46f456..d46f456 100644
--- a/mtest/mtest.c
+++ b/libtommath/mtest/mtest.c
diff --git a/pics/design_process.sxd b/libtommath/pics/design_process.sxd
index 7414dbb..7414dbb 100644
--- a/pics/design_process.sxd
+++ b/libtommath/pics/design_process.sxd
Binary files differ
diff --git a/pics/design_process.tif b/libtommath/pics/design_process.tif
index 4a0c012..4a0c012 100644
--- a/pics/design_process.tif
+++ b/libtommath/pics/design_process.tif
Binary files differ
diff --git a/pics/expt_state.sxd b/libtommath/pics/expt_state.sxd
index 6518404..6518404 100644
--- a/pics/expt_state.sxd
+++ b/libtommath/pics/expt_state.sxd
Binary files differ
diff --git a/pics/expt_state.tif b/libtommath/pics/expt_state.tif
index cb06e8e..cb06e8e 100644
--- a/pics/expt_state.tif
+++ b/libtommath/pics/expt_state.tif
Binary files differ
diff --git a/pics/makefile b/libtommath/pics/makefile
index 3ecb02f..3ecb02f 100644
--- a/pics/makefile
+++ b/libtommath/pics/makefile
diff --git a/pics/primality.tif b/libtommath/pics/primality.tif
index 76d6be3..76d6be3 100644
--- a/pics/primality.tif
+++ b/libtommath/pics/primality.tif
Binary files differ
diff --git a/pics/radix.sxd b/libtommath/pics/radix.sxd
index b9eb9a0..b9eb9a0 100644
--- a/pics/radix.sxd
+++ b/libtommath/pics/radix.sxd
Binary files differ
diff --git a/pics/sliding_window.sxd b/libtommath/pics/sliding_window.sxd
index 91e7c0d..91e7c0d 100644
--- a/pics/sliding_window.sxd
+++ b/libtommath/pics/sliding_window.sxd
Binary files differ
diff --git a/pics/sliding_window.tif b/libtommath/pics/sliding_window.tif
index bb4cb96..bb4cb96 100644
--- a/pics/sliding_window.tif
+++ b/libtommath/pics/sliding_window.tif
Binary files differ
diff --git a/libtommath/poster.out b/libtommath/poster.out
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libtommath/poster.out
diff --git a/poster.tex b/libtommath/poster.tex
index e7388f4..e7388f4 100644
--- a/poster.tex
+++ b/libtommath/poster.tex
diff --git a/pre_gen/mpi.c b/libtommath/pre_gen/mpi.c
index 8ec8a10..8ec8a10 100644
--- a/pre_gen/mpi.c
+++ b/libtommath/pre_gen/mpi.c
diff --git a/pretty.build b/libtommath/pretty.build
index a708b8a..a708b8a 100644
--- a/pretty.build
+++ b/libtommath/pretty.build
diff --git a/tombc/grammar.txt b/libtommath/tombc/grammar.txt
index a780e75..a780e75 100644
--- a/tombc/grammar.txt
+++ b/libtommath/tombc/grammar.txt
diff --git a/tommath.h b/libtommath/tommath.h
index bcb9d86..bcb9d86 100644
--- a/tommath.h
+++ b/libtommath/tommath.h
diff --git a/tommath.out b/libtommath/tommath.out
index 9f62617..9f62617 100644
--- a/tommath.out
+++ b/libtommath/tommath.out
diff --git a/tommath.src b/libtommath/tommath.src
index 7a53860..7a53860 100644
--- a/tommath.src
+++ b/libtommath/tommath.src
diff --git a/tommath.tex b/libtommath/tommath.tex
index b016010..b016010 100644
--- a/tommath.tex
+++ b/libtommath/tommath.tex
diff --git a/tommath_class.h b/libtommath/tommath_class.h
index 9faf627..9faf627 100644
--- a/tommath_class.h
+++ b/libtommath/tommath_class.h
diff --git a/tommath_superclass.h b/libtommath/tommath_superclass.h
index b50ecb0..b50ecb0 100644
--- a/tommath_superclass.h
+++ b/libtommath/tommath_superclass.h
diff --git a/listener.c b/listener.c
new file mode 100644
index 0000000..dd90c6b
--- /dev/null
+++ b/listener.c
@@ -0,0 +1,165 @@
+/*
+ * 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. */
+
+#include "includes.h"
+#include "listener.h"
+#include "session.h"
+#include "dbutil.h"
+
+void listeners_initialise() {
+
+ /* just one slot to start with */
+ ses.listeners = (struct Listener**)m_malloc(sizeof(struct Listener*));
+ ses.listensize = 1;
+ ses.listeners[0] = NULL;
+
+}
+
+void set_listener_fds(fd_set * readfds) {
+
+ unsigned int i, j;
+ struct Listener *listener;
+
+ /* check each in turn */
+ for (i = 0; i < ses.listensize; i++) {
+ listener = ses.listeners[i];
+ if (listener != NULL) {
+ for (j = 0; j < listener->nsocks; j++) {
+ FD_SET(listener->socks[j], readfds);
+ }
+ }
+ }
+}
+
+
+void handle_listeners(fd_set * readfds) {
+
+ unsigned int i, j;
+ struct Listener *listener;
+ int sock;
+
+ /* check each in turn */
+ for (i = 0; i < ses.listensize; i++) {
+ listener = ses.listeners[i];
+ if (listener != NULL) {
+ for (j = 0; j < listener->nsocks; j++) {
+ sock = listener->socks[j];
+ if (FD_ISSET(sock, readfds)) {
+ listener->acceptor(listener, sock);
+ }
+ }
+ }
+ }
+} /* Woo brace matching */
+
+
+/* acceptor(int fd, void* typedata) is a function to accept connections,
+ * cleanup(void* typedata) happens when cleaning up */
+struct Listener* new_listener(int socks[], unsigned int nsocks,
+ int type, void* typedata,
+ void (*acceptor)(struct Listener* listener, int sock),
+ void (*cleanup)(struct Listener*)) {
+
+ unsigned int i, j;
+ struct Listener *newlisten = NULL;
+ /* try get a new structure to hold it */
+ for (i = 0; i < ses.listensize; i++) {
+ if (ses.listeners[i] == NULL) {
+ break;
+ }
+ }
+
+ /* or create a new one */
+ if (i == ses.listensize) {
+ if (ses.listensize > MAX_LISTENERS) {
+ TRACE(("leave newlistener: too many already"))
+ for (j = 0; j < nsocks; j++) {
+ close(socks[i]);
+ }
+ return NULL;
+ }
+
+ ses.listeners = (struct Listener**)m_realloc(ses.listeners,
+ (ses.listensize+LISTENER_EXTEND_SIZE)
+ *sizeof(struct Listener*));
+
+ ses.listensize += LISTENER_EXTEND_SIZE;
+
+ for (j = i; j < ses.listensize; j++) {
+ ses.listeners[j] = NULL;
+ }
+ }
+
+ for (j = 0; j < nsocks; j++) {
+ ses.maxfd = MAX(ses.maxfd, socks[j]);
+ }
+
+ TRACE(("new listener num %d ", i))
+
+ newlisten = (struct Listener*)m_malloc(sizeof(struct Listener));
+ newlisten->index = i;
+ newlisten->type = type;
+ newlisten->typedata = typedata;
+ newlisten->nsocks = nsocks;
+ memcpy(newlisten->socks, socks, nsocks * sizeof(int));
+ newlisten->acceptor = acceptor;
+ newlisten->cleanup = cleanup;
+
+ ses.listeners[i] = newlisten;
+ return newlisten;
+}
+
+/* Return the first listener which matches the type-specific comparison
+ * function. Particularly needed for global requests, like tcp */
+struct Listener * get_listener(int type, void* typedata,
+ int (*match)(void*, void*)) {
+
+ unsigned int i;
+ struct Listener* listener;
+
+ for (i = 0, listener = ses.listeners[i]; i < ses.listensize; i++) {
+ if (listener->type == type
+ && match(typedata, listener->typedata)) {
+ return listener;
+ }
+ }
+
+ return NULL;
+}
+
+void remove_listener(struct Listener* listener) {
+
+ unsigned int j;
+
+ if (listener->cleanup) {
+ listener->cleanup(listener);
+ }
+
+ for (j = 0; j < listener->nsocks; j++) {
+ close(listener->socks[j]);
+ }
+ ses.listeners[listener->index] = NULL;
+ m_free(listener);
+
+}
diff --git a/listener.h b/listener.h
new file mode 100644
index 0000000..5092efd
--- /dev/null
+++ b/listener.h
@@ -0,0 +1,63 @@
+/*
+ * 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. */
+
+#ifndef _LISTENER_H
+#define _LISTENER_H
+
+#define MAX_LISTENERS 20
+#define LISTENER_EXTEND_SIZE 1
+
+struct Listener {
+
+ int socks[DROPBEAR_MAX_SOCKS];
+ unsigned int nsocks;
+
+ int index; /* index in the array of listeners */
+
+ void (*acceptor)(struct Listener*, int sock);
+ void (*cleanup)(struct Listener*);
+
+ int type; /* CHANNEL_ID_X11, CHANNEL_ID_AGENT,
+ CHANNEL_ID_TCPDIRECT (for clients),
+ CHANNEL_ID_TCPFORWARDED (for servers) */
+
+ void *typedata;
+
+};
+
+void listeners_initialise();
+void handle_listeners(fd_set * readfds);
+void set_listener_fds(fd_set * readfds);
+
+struct Listener* new_listener(int socks[], unsigned int nsocks,
+ int type, void* typedata,
+ void (*acceptor)(struct Listener* listener, int sock),
+ void (*cleanup)(struct Listener*));
+
+struct Listener * get_listener(int type, void* typedata,
+ int (*match)(void*, void*));
+
+void remove_listener(struct Listener* listener);
+
+#endif /* _LISTENER_H */
diff --git a/loginrec.c b/loginrec.c
new file mode 100644
index 0000000..f084566
--- /dev/null
+++ b/loginrec.c
@@ -0,0 +1,1394 @@
+/*
+ * Copyright (c) 2000 Andre Lucas. All rights reserved.
+ * Portions copyright (c) 1998 Todd C. Miller
+ * Portions copyright (c) 1996 Jason Downs
+ * Portions copyright (c) 1996 Theo de Raadt
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ ** 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
+ ============================
+
+ This code attempts to provide a common interface to login recording
+ (utmp and friends) and last login time retrieval.
+
+ Its primary means of achieving this is to use 'struct logininfo', a
+ union of all the useful fields in the various different types of
+ system login record structures one finds on UNIX variants.
+
+ We depend on autoconf to define which recording methods are to be
+ used, and which fields are contained in the relevant data structures
+ on the local system. Many C preprocessor symbols affect which code
+ gets compiled here.
+
+ The code is designed to make it easy to modify a particular
+ recording method, without affecting other methods nor requiring so
+ many nested conditional compilation blocks as were commonplace in
+ the old code.
+
+ For login recording, we try to use the local system's libraries as
+ these are clearly most likely to work correctly. For utmp systems
+ this usually means login() and logout() or setutent() etc., probably
+ in libutil, along with logwtmp() etc. On these systems, we fall back
+ to writing the files directly if we have to, though this method
+ requires very thorough testing so we do not corrupt local auditing
+ information. These files and their access methods are very system
+ specific indeed.
+
+ For utmpx systems, the corresponding library functions are
+ setutxent() etc. To the author's knowledge, all utmpx systems have
+ these library functions and so no direct write is attempted. If such
+ a system exists and needs support, direct analogues of the [uw]tmp
+ code should suffice.
+
+ Retrieving the time of last login ('lastlog') is in some ways even
+ more problemmatic than login recording. Some systems provide a
+ simple table of all users which we seek based on uid and retrieve a
+ relatively standard structure. Others record the same information in
+ a directory with a separate file, and others don't record the
+ information separately at all. For systems in the latter category,
+ we look backwards in the wtmp or wtmpx file for the last login entry
+ for our user. Naturally this is slower and on busy systems could
+ incur a significant performance penalty.
+
+ Calling the new code
+ --------------------
+
+ In OpenSSH all login recording and retrieval is performed in
+ login.c. Here you'll find working examples. Also, in the logintest.c
+ program there are more examples.
+
+ Internal handler calling method
+ -------------------------------
+
+ When a call is made to login_login() or login_logout(), both
+ routines set a struct logininfo flag defining which action (log in,
+ or log out) is to be taken. They both then call login_write(), which
+ calls whichever of the many structure-specific handlers autoconf
+ selects for the local system.
+
+ The handlers themselves handle system data structure specifics. Both
+ struct utmp and struct utmpx have utility functions (see
+ construct_utmp*()) to try to make it simpler to add extra systems
+ that introduce new features to either structure.
+
+ While it may seem terribly wasteful to replicate so much similar
+ code for each method, experience has shown that maintaining code to
+ write both struct utmp and utmpx in one function, whilst maintaining
+ support for all systems whether they have library support or not, is
+ a difficult and time-consuming task.
+
+ Lastlog support proceeds similarly. Functions login_get_lastlog()
+ (and its OpenSSH-tuned friend login_get_lastlog_time()) call
+ getlast_entry(), which tries one of three methods to find the last
+ login time. It uses local system lastlog support if it can,
+ otherwise it tries wtmp or wtmpx before giving up and returning 0,
+ meaning "tilt".
+
+ Maintenance
+ -----------
+
+ In many cases it's possible to tweak autoconf to select the correct
+ methods for a particular platform, either by improving the detection
+ code (best), or by presetting DISABLE_<method> or CONF_<method>_FILE
+ symbols for the platform.
+
+ Use logintest to check which symbols are defined before modifying
+ configure.ac and loginrec.c. (You have to build logintest yourself
+ with 'make logintest' as it's not built by default.)
+
+ Otherwise, patches to the specific method(s) are very helpful!
+
+*/
+
+/**
+ ** TODO:
+ ** homegrown ttyslot()
+ ** test, test, test
+ **
+ ** Platform status:
+ ** ----------------
+ **
+ ** Known good:
+ ** Linux (Redhat 6.2, Debian)
+ ** Solaris
+ ** HP-UX 10.20 (gcc only)
+ ** IRIX
+ ** NeXT - M68k/HPPA/Sparc (4.2/3.3)
+ **
+ ** Testing required: Please send reports!
+ ** NetBSD
+ ** HP-UX 11
+ ** AIX
+ **
+ ** Platforms with known problems:
+ ** Some variants of Slackware Linux
+ **
+ **/
+
+
+#include "includes.h"
+#include "loginrec.h"
+#include "dbutil.h"
+#include "atomicio.h"
+
+/**
+ ** prototypes for helper functions in this file
+ **/
+
+#if HAVE_UTMP_H
+void set_utmp_time(struct logininfo *li, struct utmp *ut);
+void construct_utmp(struct logininfo *li, struct utmp *ut);
+#endif
+
+#ifdef HAVE_UTMPX_H
+void set_utmpx_time(struct logininfo *li, struct utmpx *ut);
+void construct_utmpx(struct logininfo *li, struct utmpx *ut);
+#endif
+
+int utmp_write_entry(struct logininfo *li);
+int utmpx_write_entry(struct logininfo *li);
+int wtmp_write_entry(struct logininfo *li);
+int wtmpx_write_entry(struct logininfo *li);
+int lastlog_write_entry(struct logininfo *li);
+int syslogin_write_entry(struct logininfo *li);
+
+int wtmp_get_entry(struct logininfo *li);
+int wtmpx_get_entry(struct logininfo *li);
+
+/* pick the shortest string */
+#define MIN_SIZEOF(s1,s2) ( sizeof(s1) < sizeof(s2) ? sizeof(s1) : sizeof(s2) )
+
+/**
+ ** platform-independent login functions
+ **/
+
+/* login_login(struct logininfo *) -Record a login
+ *
+ * Call with a pointer to a struct logininfo initialised with
+ * login_init_entry() or login_alloc_entry()
+ *
+ * Returns:
+ * >0 if successful
+ * 0 on failure (will use OpenSSH's logging facilities for diagnostics)
+ */
+int
+login_login (struct logininfo *li)
+{
+ li->type = LTYPE_LOGIN;
+ return login_write(li);
+}
+
+
+/* login_logout(struct logininfo *) - Record a logout
+ *
+ * Call as with login_login()
+ *
+ * Returns:
+ * >0 if successful
+ * 0 on failure (will use OpenSSH's logging facilities for diagnostics)
+ */
+int
+login_logout(struct logininfo *li)
+{
+ li->type = LTYPE_LOGOUT;
+ return login_write(li);
+}
+
+
+/* login_alloc_entry(int, char*, char*, char*) - Allocate and initialise
+ * a logininfo structure
+ *
+ * This function creates a new struct logininfo, a data structure
+ * meant to carry the information required to portably record login info.
+ *
+ * Returns a pointer to a newly created struct logininfo. If memory
+ * allocation fails, the program halts.
+ */
+struct
+logininfo *login_alloc_entry(int pid, const char *username,
+ const char *hostname, const char *line)
+{
+ struct logininfo *newli;
+
+ newli = (struct logininfo *) m_malloc (sizeof(*newli));
+ (void)login_init_entry(newli, pid, username, hostname, line);
+ return newli;
+}
+
+
+/* login_free_entry(struct logininfo *) - free struct memory */
+void
+login_free_entry(struct logininfo *li)
+{
+ m_free(li);
+}
+
+
+/* login_init_entry(struct logininfo *, int, char*, char*, char*)
+ * - initialise a struct logininfo
+ *
+ * Populates a new struct logininfo, a data structure meant to carry
+ * the information required to portably record login info.
+ *
+ * Returns: 1
+ */
+int
+login_init_entry(struct logininfo *li, int pid, const char *username,
+ const char *hostname, const char *line)
+{
+ struct passwd *pw;
+
+ memset(li, 0, sizeof(*li));
+
+ li->pid = pid;
+
+ /* set the line information */
+ if (line)
+ line_fullname(li->line, line, sizeof(li->line));
+
+ if (username) {
+ strlcpy(li->username, username, sizeof(li->username));
+ pw = getpwnam(li->username);
+ if (pw == NULL)
+ dropbear_exit("login_init_entry: Cannot find user \"%s\"",
+ li->username);
+ li->uid = pw->pw_uid;
+ }
+
+ if (hostname)
+ strlcpy(li->hostname, hostname, sizeof(li->hostname));
+
+ return 1;
+}
+
+/* login_set_current_time(struct logininfo *) - set the current time
+ *
+ * Set the current time in a logininfo structure. This function is
+ * meant to eliminate the need to deal with system dependencies for
+ * time handling.
+ */
+void
+login_set_current_time(struct logininfo *li)
+{
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+
+ li->tv_sec = tv.tv_sec;
+ li->tv_usec = tv.tv_usec;
+}
+
+/* copy a sockaddr_* into our logininfo */
+void
+login_set_addr(struct logininfo *li, const struct sockaddr *sa,
+ const unsigned int sa_size)
+{
+ unsigned int bufsize = sa_size;
+
+ /* make sure we don't overrun our union */
+ if (sizeof(li->hostaddr) < sa_size)
+ bufsize = sizeof(li->hostaddr);
+
+ memcpy((void *)&(li->hostaddr.sa), (const void *)sa, bufsize);
+}
+
+
+/**
+ ** login_write: Call low-level recording functions based on autoconf
+ ** results
+ **/
+int
+login_write (struct logininfo *li)
+{
+#ifndef HAVE_CYGWIN
+ if ((int)geteuid() != 0) {
+ dropbear_log(LOG_WARNING,
+ "Attempt to write login records by non-root user (aborting)");
+ return 1;
+ }
+#endif
+
+ /* set the timestamp */
+ login_set_current_time(li);
+#ifdef USE_LOGIN
+ syslogin_write_entry(li);
+#endif
+#ifdef USE_LASTLOG
+ if (li->type == LTYPE_LOGIN) {
+ lastlog_write_entry(li);
+ }
+#endif
+#ifdef USE_UTMP
+ utmp_write_entry(li);
+#endif
+#ifdef USE_WTMP
+ wtmp_write_entry(li);
+#endif
+#ifdef USE_UTMPX
+ utmpx_write_entry(li);
+#endif
+#ifdef USE_WTMPX
+ wtmpx_write_entry(li);
+#endif
+ return 0;
+}
+
+#ifdef LOGIN_NEEDS_UTMPX
+int
+login_utmp_only(struct logininfo *li)
+{
+ li->type = LTYPE_LOGIN;
+ login_set_current_time(li);
+# ifdef USE_UTMP
+ utmp_write_entry(li);
+# endif
+# ifdef USE_WTMP
+ wtmp_write_entry(li);
+# endif
+# ifdef USE_UTMPX
+ utmpx_write_entry(li);
+# endif
+# ifdef USE_WTMPX
+ wtmpx_write_entry(li);
+# endif
+ return 0;
+}
+#endif
+
+
+
+/*
+ * 'line' string utility functions
+ *
+ * These functions process the 'line' string into one of three forms:
+ *
+ * 1. The full filename (including '/dev')
+ * 2. The stripped name (excluding '/dev')
+ * 3. The abbreviated name (e.g. /dev/ttyp00 -> yp00
+ * /dev/pts/1 -> ts/1 )
+ *
+ * Form 3 is used on some systems to identify a .tmp.? entry when
+ * attempting to remove it. Typically both addition and removal is
+ * performed by one application - say, sshd - so as long as the choice
+ * uniquely identifies a terminal it's ok.
+ */
+
+
+/* line_fullname(): add the leading '/dev/' if it doesn't exist make
+ * sure dst has enough space, if not just copy src (ugh) */
+char *
+line_fullname(char *dst, const char *src, size_t dstsize)
+{
+ memset(dst, '\0', dstsize);
+ if ((strncmp(src, "/dev/", 5) == 0) || (dstsize < (strlen(src) + 5))) {
+ strlcpy(dst, src, dstsize);
+ } else {
+ strlcpy(dst, "/dev/", dstsize);
+ strlcat(dst, src, dstsize);
+ }
+ return dst;
+}
+
+/* line_stripname(): strip the leading '/dev' if it exists, return dst */
+char *
+line_stripname(char *dst, const char *src, size_t dstsize)
+{
+ memset(dst, '\0', dstsize);
+ if (strncmp(src, "/dev/", 5) == 0)
+ strlcpy(dst, src + 5, dstsize);
+ else
+ strlcpy(dst, src, dstsize);
+ return dst;
+}
+
+/* line_abbrevname(): Return the abbreviated (usually four-character)
+ * form of the line (Just use the last <dstsize> characters of the
+ * full name.)
+ *
+ * NOTE: use strncpy because we do NOT necessarily want zero
+ * termination */
+char *
+line_abbrevname(char *dst, const char *src, size_t dstsize)
+{
+ size_t len;
+
+ memset(dst, '\0', dstsize);
+
+ /* Always skip prefix if present */
+ if (strncmp(src, "/dev/", 5) == 0)
+ src += 5;
+
+#ifdef WITH_ABBREV_NO_TTY
+ if (strncmp(src, "tty", 3) == 0)
+ src += 3;
+#endif
+
+ len = strlen(src);
+
+ if (len > 0) {
+ if (((int)len - dstsize) > 0)
+ src += ((int)len - dstsize);
+
+ /* note: _don't_ change this to strlcpy */
+ strncpy(dst, src, (size_t)dstsize);
+ }
+
+ return dst;
+}
+
+/**
+ ** utmp utility functions
+ **
+ ** These functions manipulate struct utmp, taking system differences
+ ** into account.
+ **/
+
+#if defined(USE_UTMP) || defined (USE_WTMP) || defined (USE_LOGIN)
+
+/* build the utmp structure */
+void
+set_utmp_time(struct logininfo *li, struct utmp *ut)
+{
+# ifdef HAVE_STRUCT_UTMP_UT_TV
+ ut->ut_tv.tv_sec = li->tv_sec;
+ ut->ut_tv.tv_usec = li->tv_usec;
+# else
+# ifdef HAVE_STRUCT_UTMP_UT_TIME
+ ut->ut_time = li->tv_sec;
+# endif
+# endif
+}
+
+void
+construct_utmp(struct logininfo *li,
+ struct utmp *ut)
+{
+# ifdef HAVE_ADDR_V6_IN_UTMP
+ struct sockaddr_in6 *sa6;
+# endif
+ memset(ut, '\0', sizeof(*ut));
+
+ /* First fill out fields used for both logins and logouts */
+
+# ifdef HAVE_STRUCT_UTMP_UT_ID
+ line_abbrevname(ut->ut_id, li->line, sizeof(ut->ut_id));
+# endif
+
+# ifdef HAVE_STRUCT_UTMP_UT_TYPE
+ /* This is done here to keep utmp constants out of struct logininfo */
+ switch (li->type) {
+ case LTYPE_LOGIN:
+ ut->ut_type = USER_PROCESS;
+#ifdef _UNICOS
+ cray_set_tmpdir(ut);
+#endif
+ break;
+ case LTYPE_LOGOUT:
+ ut->ut_type = DEAD_PROCESS;
+#ifdef _UNICOS
+ cray_retain_utmp(ut, li->pid);
+#endif
+ break;
+ }
+# endif
+ set_utmp_time(li, ut);
+
+ line_stripname(ut->ut_line, li->line, sizeof(ut->ut_line));
+
+# ifdef HAVE_STRUCT_UTMP_UT_PID
+ ut->ut_pid = li->pid;
+# endif
+
+ /* If we're logging out, leave all other fields blank */
+ if (li->type == LTYPE_LOGOUT)
+ return;
+
+ /*
+ * These fields are only used when logging in, and are blank
+ * for logouts.
+ */
+
+ /* Use strncpy because we don't necessarily want null termination */
+ strncpy(ut->ut_name, li->username, MIN_SIZEOF(ut->ut_name, li->username));
+# ifdef HAVE_STRUCT_UTMP_UT_HOST
+ strncpy(ut->ut_host, li->hostname, MIN_SIZEOF(ut->ut_host, li->hostname));
+# endif
+# ifdef HAVE_STRUCT_UTMP_UT_ADDR
+ /* this is just a 32-bit IP address */
+ if (li->hostaddr.sa.sa_family == AF_INET)
+ ut->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr;
+# endif
+# ifdef HAVE_ADDR_V6_IN_UTMP
+ /* this is just a 128-bit IPv6 address */
+ if (li->hostaddr.sa.sa_family == AF_INET6) {
+ sa6 = ((struct sockaddr_in6 *)&li->hostaddr.sa);
+ memcpy(ut->ut_addr_v6, sa6->sin6_addr.s6_addr, 16);
+ if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) {
+ ut->ut_addr_v6[0] = ut->ut_addr_v6[3];
+ ut->ut_addr_v6[1] = 0;
+ ut->ut_addr_v6[2] = 0;
+ ut->ut_addr_v6[3] = 0;
+ }
+ }
+# endif
+}
+#endif /* USE_UTMP || USE_WTMP || USE_LOGIN */
+
+/**
+ ** utmpx utility functions
+ **
+ ** These functions manipulate struct utmpx, accounting for system
+ ** variations.
+ **/
+
+#if defined(USE_UTMPX) || defined (USE_WTMPX)
+/* build the utmpx structure */
+void
+set_utmpx_time(struct logininfo *li, struct utmpx *utx)
+{
+# ifdef HAVE_STRUCT_UTMPX_UT_TV
+ utx->ut_tv.tv_sec = li->tv_sec;
+ utx->ut_tv.tv_usec = li->tv_usec;
+# else /* HAVE_STRUCT_UTMPX_UT_TV */
+# ifdef HAVE_STRUCT_UTMPX_UT_TIME
+ utx->ut_time = li->tv_sec;
+# endif /* HAVE_STRUCT_UTMPX_UT_TIME */
+# endif /* HAVE_STRUCT_UTMPX_UT_TV */
+}
+
+void
+construct_utmpx(struct logininfo *li, struct utmpx *utx)
+{
+# ifdef HAVE_ADDR_V6_IN_UTMP
+ struct sockaddr_in6 *sa6;
+# endif
+ memset(utx, '\0', sizeof(*utx));
+# ifdef HAVE_STRUCT_UTMPX_UT_ID
+ line_abbrevname(utx->ut_id, li->line, sizeof(utx->ut_id));
+# endif
+
+ /* this is done here to keep utmp constants out of loginrec.h */
+ switch (li->type) {
+ case LTYPE_LOGIN:
+ utx->ut_type = USER_PROCESS;
+ break;
+ case LTYPE_LOGOUT:
+ utx->ut_type = DEAD_PROCESS;
+ break;
+ }
+ line_stripname(utx->ut_line, li->line, sizeof(utx->ut_line));
+ set_utmpx_time(li, utx);
+ utx->ut_pid = li->pid;
+ /* strncpy(): Don't necessarily want null termination */
+ strncpy(utx->ut_name, li->username, MIN_SIZEOF(utx->ut_name, li->username));
+
+ if (li->type == LTYPE_LOGOUT)
+ return;
+
+ /*
+ * These fields are only used when logging in, and are blank
+ * for logouts.
+ */
+
+# ifdef HAVE_STRUCT_UTMPX_UT_HOST
+ strncpy(utx->ut_host, li->hostname, MIN_SIZEOF(utx->ut_host, li->hostname));
+# endif
+# ifdef HAVE_STRUCT_UTMPX_UT_ADDR
+ /* this is just a 32-bit IP address */
+ if (li->hostaddr.sa.sa_family == AF_INET)
+ utx->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr;
+# endif
+# ifdef HAVE_ADDR_V6_IN_UTMP
+ /* this is just a 128-bit IPv6 address */
+ if (li->hostaddr.sa.sa_family == AF_INET6) {
+ sa6 = ((struct sockaddr_in6 *)&li->hostaddr.sa);
+ memcpy(ut->ut_addr_v6, sa6->sin6_addr.s6_addr, 16);
+ if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) {
+ ut->ut_addr_v6[0] = ut->ut_addr_v6[3];
+ ut->ut_addr_v6[1] = 0;
+ ut->ut_addr_v6[2] = 0;
+ ut->ut_addr_v6[3] = 0;
+ }
+ }
+# endif
+# ifdef HAVE_STRUCT_UTMPX_UT_SYSLEN
+ /* ut_syslen is the length of the utx_host string */
+ utx->ut_syslen = MIN(strlen(li->hostname), sizeof(utx->ut_host));
+# endif
+}
+#endif /* USE_UTMPX || USE_WTMPX */
+
+/**
+ ** Low-level utmp functions
+ **/
+
+/* FIXME: (ATL) utmp_write_direct needs testing */
+#ifdef USE_UTMP
+
+/* if we can, use pututline() etc. */
+# if !defined(DISABLE_PUTUTLINE) && defined(HAVE_SETUTENT) && \
+ defined(HAVE_PUTUTLINE)
+# define UTMP_USE_LIBRARY
+# endif
+
+
+/* write a utmp entry with the system's help (pututline() and pals) */
+# ifdef UTMP_USE_LIBRARY
+static int
+utmp_write_library(struct logininfo *li, struct utmp *ut)
+{
+ setutent();
+ pututline(ut);
+
+# ifdef HAVE_ENDUTENT
+ endutent();
+# endif
+ return 1;
+}
+# else /* UTMP_USE_LIBRARY */
+
+/* write a utmp entry direct to the file */
+/* This is a slightly modification of code in OpenBSD's login.c */
+static int
+utmp_write_direct(struct logininfo *li, struct utmp *ut)
+{
+ struct utmp old_ut;
+ register int fd;
+ int tty;
+
+ /* FIXME: (ATL) ttyslot() needs local implementation */
+
+#if defined(HAVE_GETTTYENT)
+ register struct ttyent *ty;
+
+ tty=0;
+
+ setttyent();
+ while ((struct ttyent *)0 != (ty = getttyent())) {
+ tty++;
+ if (!strncmp(ty->ty_name, ut->ut_line, sizeof(ut->ut_line)))
+ break;
+ }
+ endttyent();
+
+ if((struct ttyent *)0 == ty) {
+ dropbear_log(LOG_WARNING, "utmp_write_entry: tty not found");
+ return(1);
+ }
+#else /* FIXME */
+
+ tty = ttyslot(); /* seems only to work for /dev/ttyp? style names */
+
+#endif /* HAVE_GETTTYENT */
+
+ if (tty > 0 && (fd = open(UTMP_FILE, O_RDWR|O_CREAT, 0644)) >= 0) {
+ (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET);
+ /*
+ * Prevent luser from zero'ing out ut_host.
+ * If the new ut_line is empty but the old one is not
+ * and ut_line and ut_name match, preserve the old ut_line.
+ */
+ if (atomicio(read, fd, &old_ut, sizeof(old_ut)) == sizeof(old_ut) &&
+ (ut->ut_host[0] == '\0') && (old_ut.ut_host[0] != '\0') &&
+ (strncmp(old_ut.ut_line, ut->ut_line, sizeof(ut->ut_line)) == 0) &&
+ (strncmp(old_ut.ut_name, ut->ut_name, sizeof(ut->ut_name)) == 0)) {
+ (void)memcpy(ut->ut_host, old_ut.ut_host, sizeof(ut->ut_host));
+ }
+
+ (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET);
+ if (atomicio(write, fd, ut, sizeof(*ut)) != sizeof(*ut))
+ dropbear_log(LOG_WARNING, "utmp_write_direct: error writing %s: %s",
+ UTMP_FILE, strerror(errno));
+
+ (void)close(fd);
+ return 1;
+ } else {
+ return 0;
+ }
+}
+# endif /* UTMP_USE_LIBRARY */
+
+static int
+utmp_perform_login(struct logininfo *li)
+{
+ struct utmp ut;
+
+ construct_utmp(li, &ut);
+# ifdef UTMP_USE_LIBRARY
+ if (!utmp_write_library(li, &ut)) {
+ dropbear_log(LOG_WARNING, "utmp_perform_login: utmp_write_library() failed");
+ return 0;
+ }
+# else
+ if (!utmp_write_direct(li, &ut)) {
+ dropbear_log(LOG_WARNING, "utmp_perform_login: utmp_write_direct() failed");
+ return 0;
+ }
+# endif
+ return 1;
+}
+
+
+static int
+utmp_perform_logout(struct logininfo *li)
+{
+ struct utmp ut;
+
+ construct_utmp(li, &ut);
+# ifdef UTMP_USE_LIBRARY
+ if (!utmp_write_library(li, &ut)) {
+ dropbear_log(LOG_WARNING, "utmp_perform_logout: utmp_write_library() failed");
+ return 0;
+ }
+# else
+ if (!utmp_write_direct(li, &ut)) {
+ dropbear_log(LOG_WARNING, "utmp_perform_logout: utmp_write_direct() failed");
+ return 0;
+ }
+# endif
+ return 1;
+}
+
+
+int
+utmp_write_entry(struct logininfo *li)
+{
+ switch(li->type) {
+ case LTYPE_LOGIN:
+ return utmp_perform_login(li);
+
+ case LTYPE_LOGOUT:
+ return utmp_perform_logout(li);
+
+ default:
+ dropbear_log(LOG_WARNING, "utmp_write_entry: invalid type field");
+ return 0;
+ }
+}
+#endif /* USE_UTMP */
+
+
+/**
+ ** Low-level utmpx functions
+ **/
+
+/* not much point if we don't want utmpx entries */
+#ifdef USE_UTMPX
+
+/* if we have the wherewithall, use pututxline etc. */
+# if !defined(DISABLE_PUTUTXLINE) && defined(HAVE_SETUTXENT) && \
+ defined(HAVE_PUTUTXLINE)
+# define UTMPX_USE_LIBRARY
+# endif
+
+
+/* write a utmpx entry with the system's help (pututxline() and pals) */
+# ifdef UTMPX_USE_LIBRARY
+static int
+utmpx_write_library(struct logininfo *li, struct utmpx *utx)
+{
+ setutxent();
+ pututxline(utx);
+
+# ifdef HAVE_ENDUTXENT
+ endutxent();
+# endif
+ return 1;
+}
+
+# else /* UTMPX_USE_LIBRARY */
+
+/* write a utmp entry direct to the file */
+static int
+utmpx_write_direct(struct logininfo *li, struct utmpx *utx)
+{
+ dropbear_log(LOG_WARNING, "utmpx_write_direct: not implemented!");
+ return 0;
+}
+# endif /* UTMPX_USE_LIBRARY */
+
+static int
+utmpx_perform_login(struct logininfo *li)
+{
+ struct utmpx utx;
+
+ construct_utmpx(li, &utx);
+# ifdef UTMPX_USE_LIBRARY
+ if (!utmpx_write_library(li, &utx)) {
+ dropbear_log(LOG_WARNING, "utmpx_perform_login: utmp_write_library() failed");
+ return 0;
+ }
+# else
+ if (!utmpx_write_direct(li, &ut)) {
+ dropbear_log(LOG_WARNING, "utmpx_perform_login: utmp_write_direct() failed");
+ return 0;
+ }
+# endif
+ return 1;
+}
+
+
+static int
+utmpx_perform_logout(struct logininfo *li)
+{
+ struct utmpx utx;
+
+ construct_utmpx(li, &utx);
+# ifdef HAVE_STRUCT_UTMPX_UT_ID
+ line_abbrevname(utx.ut_id, li->line, sizeof(utx.ut_id));
+# endif
+# ifdef HAVE_STRUCT_UTMPX_UT_TYPE
+ utx.ut_type = DEAD_PROCESS;
+# endif
+
+# ifdef UTMPX_USE_LIBRARY
+ utmpx_write_library(li, &utx);
+# else
+ utmpx_write_direct(li, &utx);
+# endif
+ return 1;
+}
+
+int
+utmpx_write_entry(struct logininfo *li)
+{
+ switch(li->type) {
+ case LTYPE_LOGIN:
+ return utmpx_perform_login(li);
+ case LTYPE_LOGOUT:
+ return utmpx_perform_logout(li);
+ default:
+ dropbear_log(LOG_WARNING, "utmpx_write_entry: invalid type field");
+ return 0;
+ }
+}
+#endif /* USE_UTMPX */
+
+
+/**
+ ** Low-level wtmp functions
+ **/
+
+#ifdef USE_WTMP
+
+/* write a wtmp entry direct to the end of the file */
+/* This is a slight modification of code in OpenBSD's logwtmp.c */
+static int
+wtmp_write(struct logininfo *li, struct utmp *ut)
+{
+ struct stat buf;
+ int fd, ret = 1;
+
+ if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
+ dropbear_log(LOG_WARNING, "wtmp_write: problem writing %s: %s",
+ WTMP_FILE, strerror(errno));
+ return 0;
+ }
+ if (fstat(fd, &buf) == 0)
+ if (atomicio(write, fd, ut, sizeof(*ut)) != sizeof(*ut)) {
+ ftruncate(fd, buf.st_size);
+ dropbear_log(LOG_WARNING, "wtmp_write: problem writing %s: %s",
+ WTMP_FILE, strerror(errno));
+ ret = 0;
+ }
+ (void)close(fd);
+ return ret;
+}
+
+static int
+wtmp_perform_login(struct logininfo *li)
+{
+ struct utmp ut;
+
+ construct_utmp(li, &ut);
+ return wtmp_write(li, &ut);
+}
+
+
+static int
+wtmp_perform_logout(struct logininfo *li)
+{
+ struct utmp ut;
+
+ construct_utmp(li, &ut);
+ return wtmp_write(li, &ut);
+}
+
+
+int
+wtmp_write_entry(struct logininfo *li)
+{
+ switch(li->type) {
+ case LTYPE_LOGIN:
+ return wtmp_perform_login(li);
+ case LTYPE_LOGOUT:
+ return wtmp_perform_logout(li);
+ default:
+ dropbear_log(LOG_WARNING, "wtmp_write_entry: invalid type field");
+ return 0;
+ }
+}
+
+
+/* Notes on fetching login data from wtmp/wtmpx
+ *
+ * Logouts are usually recorded with (amongst other things) a blank
+ * username on a given tty line. However, some systems (HP-UX is one)
+ * leave all fields set, but change the ut_type field to DEAD_PROCESS.
+ *
+ * Since we're only looking for logins here, we know that the username
+ * must be set correctly. On systems that leave it in, we check for
+ * ut_type==USER_PROCESS (indicating a login.)
+ *
+ * Portability: Some systems may set something other than USER_PROCESS
+ * to indicate a login process. I don't know of any as I write. Also,
+ * it's possible that some systems may both leave the username in
+ * place and not have ut_type.
+ */
+
+/* return true if this wtmp entry indicates a login */
+static int
+wtmp_islogin(struct logininfo *li, struct utmp *ut)
+{
+ if (strncmp(li->username, ut->ut_name,
+ MIN_SIZEOF(li->username, ut->ut_name)) == 0) {
+# ifdef HAVE_STRUCT_UTMP_UT_TYPE
+ if (ut->ut_type & USER_PROCESS)
+ return 1;
+# else
+ return 1;
+# endif
+ }
+ return 0;
+}
+
+int
+wtmp_get_entry(struct logininfo *li)
+{
+ struct stat st;
+ struct utmp ut;
+ int fd, found=0;
+
+ /* Clear the time entries in our logininfo */
+ li->tv_sec = li->tv_usec = 0;
+
+ if ((fd = open(WTMP_FILE, O_RDONLY)) < 0) {
+ dropbear_log(LOG_WARNING, "wtmp_get_entry: problem opening %s: %s",
+ WTMP_FILE, strerror(errno));
+ return 0;
+ }
+ if (fstat(fd, &st) != 0) {
+ dropbear_log(LOG_WARNING, "wtmp_get_entry: couldn't stat %s: %s",
+ WTMP_FILE, strerror(errno));
+ close(fd);
+ return 0;
+ }
+
+ /* Seek to the start of the last struct utmp */
+ if (lseek(fd, -(off_t)sizeof(struct utmp), SEEK_END) == -1) {
+ /* Looks like we've got a fresh wtmp file */
+ close(fd);
+ return 0;
+ }
+
+ while (!found) {
+ if (atomicio(read, fd, &ut, sizeof(ut)) != sizeof(ut)) {
+ dropbear_log(LOG_WARNING, "wtmp_get_entry: read of %s failed: %s",
+ WTMP_FILE, strerror(errno));
+ close (fd);
+ return 0;
+ }
+ if ( wtmp_islogin(li, &ut) ) {
+ found = 1;
+ /* We've already checked for a time in struct
+ * utmp, in login_getlast(). */
+# ifdef HAVE_STRUCT_UTMP_UT_TIME
+ li->tv_sec = ut.ut_time;
+# else
+# if HAVE_STRUCT_UTMP_UT_TV
+ li->tv_sec = ut.ut_tv.tv_sec;
+# endif
+# endif
+ line_fullname(li->line, ut.ut_line,
+ MIN_SIZEOF(li->line, ut.ut_line));
+# ifdef HAVE_STRUCT_UTMP_UT_HOST
+ strlcpy(li->hostname, ut.ut_host,
+ MIN_SIZEOF(li->hostname, ut.ut_host));
+# endif
+ continue;
+ }
+ /* Seek back 2 x struct utmp */
+ if (lseek(fd, -(off_t)(2 * sizeof(struct utmp)), SEEK_CUR) == -1) {
+ /* We've found the start of the file, so quit */
+ close (fd);
+ return 0;
+ }
+ }
+
+ /* We found an entry. Tidy up and return */
+ close(fd);
+ return 1;
+}
+# endif /* USE_WTMP */
+
+
+/**
+ ** Low-level wtmpx functions
+ **/
+
+#ifdef USE_WTMPX
+/* write a wtmpx entry direct to the end of the file */
+/* This is a slight modification of code in OpenBSD's logwtmp.c */
+static int
+wtmpx_write(struct logininfo *li, struct utmpx *utx)
+{
+ struct stat buf;
+ int fd, ret = 1;
+
+ if ((fd = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
+ dropbear_log(LOG_WARNING, "wtmpx_write: problem opening %s: %s",
+ WTMPX_FILE, strerror(errno));
+ return 0;
+ }
+
+ if (fstat(fd, &buf) == 0)
+ if (atomicio(write, fd, utx, sizeof(*utx)) != sizeof(*utx)) {
+ ftruncate(fd, buf.st_size);
+ dropbear_log(LOG_WARNING, "wtmpx_write: problem writing %s: %s",
+ WTMPX_FILE, strerror(errno));
+ ret = 0;
+ }
+ (void)close(fd);
+
+ return ret;
+}
+
+
+static int
+wtmpx_perform_login(struct logininfo *li)
+{
+ struct utmpx utx;
+
+ construct_utmpx(li, &utx);
+ return wtmpx_write(li, &utx);
+}
+
+
+static int
+wtmpx_perform_logout(struct logininfo *li)
+{
+ struct utmpx utx;
+
+ construct_utmpx(li, &utx);
+ return wtmpx_write(li, &utx);
+}
+
+
+int
+wtmpx_write_entry(struct logininfo *li)
+{
+ switch(li->type) {
+ case LTYPE_LOGIN:
+ return wtmpx_perform_login(li);
+ case LTYPE_LOGOUT:
+ return wtmpx_perform_logout(li);
+ default:
+ dropbear_log(LOG_WARNING, "wtmpx_write_entry: invalid type field");
+ return 0;
+ }
+}
+
+/* Please see the notes above wtmp_islogin() for information about the
+ next two functions */
+
+/* Return true if this wtmpx entry indicates a login */
+static int
+wtmpx_islogin(struct logininfo *li, struct utmpx *utx)
+{
+ if ( strncmp(li->username, utx->ut_name,
+ MIN_SIZEOF(li->username, utx->ut_name)) == 0 ) {
+# ifdef HAVE_STRUCT_UTMPX_UT_TYPE
+ if (utx->ut_type == USER_PROCESS)
+ return 1;
+# else
+ return 1;
+# endif
+ }
+ return 0;
+}
+
+
+int
+wtmpx_get_entry(struct logininfo *li)
+{
+ struct stat st;
+ struct utmpx utx;
+ int fd, found=0;
+
+ /* Clear the time entries */
+ li->tv_sec = li->tv_usec = 0;
+
+ if ((fd = open(WTMPX_FILE, O_RDONLY)) < 0) {
+ dropbear_log(LOG_WARNING, "wtmpx_get_entry: problem opening %s: %s",
+ WTMPX_FILE, strerror(errno));
+ return 0;
+ }
+ if (fstat(fd, &st) != 0) {
+ dropbear_log(LOG_WARNING, "wtmpx_get_entry: couldn't stat %s: %s",
+ WTMPX_FILE, strerror(errno));
+ close(fd);
+ return 0;
+ }
+
+ /* Seek to the start of the last struct utmpx */
+ if (lseek(fd, -(off_t)sizeof(struct utmpx), SEEK_END) == -1 ) {
+ /* probably a newly rotated wtmpx file */
+ close(fd);
+ return 0;
+ }
+
+ while (!found) {
+ if (atomicio(read, fd, &utx, sizeof(utx)) != sizeof(utx)) {
+ dropbear_log(LOG_WARNING, "wtmpx_get_entry: read of %s failed: %s",
+ WTMPX_FILE, strerror(errno));
+ close (fd);
+ return 0;
+ }
+ /* Logouts are recorded as a blank username on a particular line.
+ * So, we just need to find the username in struct utmpx */
+ if ( wtmpx_islogin(li, &utx) ) {
+ found = 1;
+# ifdef HAVE_STRUCT_UTMPX_UT_TV
+ li->tv_sec = utx.ut_tv.tv_sec;
+# else
+# ifdef HAVE_STRUCT_UTMPX_UT_TIME
+ li->tv_sec = utx.ut_time;
+# endif
+# endif
+ line_fullname(li->line, utx.ut_line, sizeof(li->line));
+# ifdef HAVE_STRUCT_UTMPX_UT_HOST
+ strlcpy(li->hostname, utx.ut_host,
+ MIN_SIZEOF(li->hostname, utx.ut_host));
+# endif
+ continue;
+ }
+ if (lseek(fd, -(off_t)(2 * sizeof(struct utmpx)), SEEK_CUR) == -1) {
+ close (fd);
+ return 0;
+ }
+ }
+
+ close(fd);
+ return 1;
+}
+#endif /* USE_WTMPX */
+
+/**
+ ** Low-level libutil login() functions
+ **/
+
+#ifdef USE_LOGIN
+static int
+syslogin_perform_login(struct logininfo *li)
+{
+ struct utmp *ut;
+
+ if (! (ut = (struct utmp *)malloc(sizeof(*ut)))) {
+ dropbear_log(LOG_WARNING, "syslogin_perform_login: couldn't malloc()");
+ return 0;
+ }
+ construct_utmp(li, ut);
+ login(ut);
+ free(ut);
+
+ return 1;
+}
+
+static int
+syslogin_perform_logout(struct logininfo *li)
+{
+# ifdef HAVE_LOGOUT
+ char line[8];
+
+ (void)line_stripname(line, li->line, sizeof(line));
+
+ if (!logout(line)) {
+ dropbear_log(LOG_WARNING, "syslogin_perform_logout: logout(%s) returned an error: %s", line, strerror(errno));
+# ifdef HAVE_LOGWTMP
+ } else {
+ logwtmp(line, "", "");
+# endif
+ }
+ /* FIXME: (ATL - if the need arises) What to do if we have
+ * login, but no logout? what if logout but no logwtmp? All
+ * routines are in libutil so they should all be there,
+ * but... */
+# endif
+ return 1;
+}
+
+int
+syslogin_write_entry(struct logininfo *li)
+{
+ switch (li->type) {
+ case LTYPE_LOGIN:
+ return syslogin_perform_login(li);
+ case LTYPE_LOGOUT:
+ return syslogin_perform_logout(li);
+ default:
+ dropbear_log(LOG_WARNING, "syslogin_write_entry: Invalid type field");
+ return 0;
+ }
+}
+#endif /* USE_LOGIN */
+
+/* end of file log-syslogin.c */
+
+/**
+ ** Low-level lastlog functions
+ **/
+
+#ifdef USE_LASTLOG
+#define LL_FILE 1
+#define LL_DIR 2
+#define LL_OTHER 3
+
+static void
+lastlog_construct(struct logininfo *li, struct lastlog *last)
+{
+ /* clear the structure */
+ memset(last, '\0', sizeof(*last));
+
+ (void)line_stripname(last->ll_line, li->line, sizeof(last->ll_line));
+ strlcpy(last->ll_host, li->hostname,
+ MIN_SIZEOF(last->ll_host, li->hostname));
+ last->ll_time = li->tv_sec;
+}
+
+static int
+lastlog_filetype(char *filename)
+{
+ struct stat st;
+
+ if (stat(filename, &st) != 0) {
+ dropbear_log(LOG_WARNING, "lastlog_perform_login: Couldn't stat %s: %s", filename,
+ strerror(errno));
+ return 0;
+ }
+ if (S_ISDIR(st.st_mode))
+ return LL_DIR;
+ else if (S_ISREG(st.st_mode))
+ return LL_FILE;
+ else
+ return LL_OTHER;
+}
+
+
+/* open the file (using filemode) and seek to the login entry */
+static int
+lastlog_openseek(struct logininfo *li, int *fd, int filemode)
+{
+ off_t offset;
+ int type;
+ char lastlog_file[1024];
+
+ type = lastlog_filetype(LASTLOG_FILE);
+ switch (type) {
+ case LL_FILE:
+ strlcpy(lastlog_file, LASTLOG_FILE, sizeof(lastlog_file));
+ break;
+ case LL_DIR:
+ snprintf(lastlog_file, sizeof(lastlog_file), "%s/%s",
+ LASTLOG_FILE, li->username);
+ break;
+ default:
+ dropbear_log(LOG_WARNING, "lastlog_openseek: %.100s is not a file or directory!",
+ LASTLOG_FILE);
+ return 0;
+ }
+
+ *fd = open(lastlog_file, filemode);
+ if ( *fd < 0) {
+ dropbear_log(LOG_INFO, "lastlog_openseek: Couldn't open %s: %s",
+ lastlog_file, strerror(errno));
+ return 0;
+ }
+
+ if (type == LL_FILE) {
+ /* find this uid's offset in the lastlog file */
+ offset = (off_t) ((long)li->uid * sizeof(struct lastlog));
+
+ if ( lseek(*fd, offset, SEEK_SET) != offset ) {
+ dropbear_log(LOG_WARNING, "lastlog_openseek: %s->lseek(): %s",
+ lastlog_file, strerror(errno));
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static int
+lastlog_perform_login(struct logininfo *li)
+{
+ struct lastlog last;
+ int fd;
+
+ /* create our struct lastlog */
+ lastlog_construct(li, &last);
+
+ if (!lastlog_openseek(li, &fd, O_RDWR|O_CREAT))
+ return(0);
+
+ /* write the entry */
+ if (atomicio(write, fd, &last, sizeof(last)) != sizeof(last)) {
+ close(fd);
+ dropbear_log(LOG_WARNING, "lastlog_write_filemode: Error writing to %s: %s",
+ LASTLOG_FILE, strerror(errno));
+ return 0;
+ }
+
+ close(fd);
+ return 1;
+}
+
+int
+lastlog_write_entry(struct logininfo *li)
+{
+ switch(li->type) {
+ case LTYPE_LOGIN:
+ return lastlog_perform_login(li);
+ default:
+ dropbear_log(LOG_WARNING, "lastlog_write_entry: Invalid type field");
+ return 0;
+ }
+}
+
+#endif /* USE_LASTLOG */
diff --git a/loginrec.h b/loginrec.h
new file mode 100644
index 0000000..03d26b9
--- /dev/null
+++ b/loginrec.h
@@ -0,0 +1,185 @@
+#ifndef _HAVE_LOGINREC_H_
+#define _HAVE_LOGINREC_H_
+
+/*
+ * Copyright (c) 2000 Andre Lucas. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ ** loginrec.h: platform-independent login recording and lastlog retrieval
+ **/
+
+#include "includes.h"
+
+/* RCSID("Id: loginrec.h,v 1.2 2004/05/04 10:17:43 matt Exp "); */
+
+/* The following #defines are from OpenSSH's defines.h, required for loginrec */
+
+/* FIXME: put default paths back in */
+#ifndef UTMP_FILE
+# ifdef _PATH_UTMP
+# define UTMP_FILE _PATH_UTMP
+# else
+# ifdef CONF_UTMP_FILE
+# define UTMP_FILE CONF_UTMP_FILE
+# endif
+# endif
+#endif
+#ifndef WTMP_FILE
+# ifdef _PATH_WTMP
+# define WTMP_FILE _PATH_WTMP
+# else
+# ifdef CONF_WTMP_FILE
+# define WTMP_FILE CONF_WTMP_FILE
+# endif
+# endif
+#endif
+/* pick up the user's location for lastlog if given */
+#ifndef LASTLOG_FILE
+# ifdef _PATH_LASTLOG
+# define LASTLOG_FILE _PATH_LASTLOG
+# else
+# ifdef CONF_LASTLOG_FILE
+# define LASTLOG_FILE CONF_LASTLOG_FILE
+# endif
+# endif
+#endif
+
+
+/* The login() library function in libutil is first choice */
+#if defined(HAVE_LOGIN) && !defined(DISABLE_LOGIN)
+# define USE_LOGIN
+
+#else
+/* Simply select your favourite login types. */
+/* Can't do if-else because some systems use several... <sigh> */
+# if defined(UTMPX_FILE) && !defined(DISABLE_UTMPX)
+# define USE_UTMPX
+# endif
+# if defined(UTMP_FILE) && !defined(DISABLE_UTMP)
+# define USE_UTMP
+# endif
+# if defined(WTMPX_FILE) && !defined(DISABLE_WTMPX)
+# define USE_WTMPX
+# endif
+# if defined(WTMP_FILE) && !defined(DISABLE_WTMP)
+# define USE_WTMP
+# endif
+
+#endif
+
+/* I hope that the presence of LASTLOG_FILE is enough to detect this */
+#if defined(LASTLOG_FILE) && !defined(DISABLE_LASTLOG)
+# define USE_LASTLOG
+#endif
+
+
+/**
+ ** you should use the login_* calls to work around platform dependencies
+ **/
+
+/*
+ * login_netinfo structure
+ */
+
+union login_netinfo {
+ struct sockaddr sa;
+ struct sockaddr_in sa_in;
+#ifdef HAVE_STRUCT_SOCKADDR_STORAGE
+ struct sockaddr_storage sa_storage;
+#endif
+};
+
+/*
+ * * logininfo structure *
+ */
+/* types - different to utmp.h 'type' macros */
+/* (though set to the same value as linux, openbsd and others...) */
+#define LTYPE_LOGIN 7
+#define LTYPE_LOGOUT 8
+
+/* string lengths - set very long */
+#define LINFO_PROGSIZE 64
+#define LINFO_LINESIZE 64
+#define LINFO_NAMESIZE 64
+#define LINFO_HOSTSIZE 256
+
+struct logininfo {
+ char progname[LINFO_PROGSIZE]; /* name of program (for PAM) */
+ int progname_null;
+ short int type; /* type of login (LTYPE_*) */
+ int pid; /* PID of login process */
+ int uid; /* UID of this user */
+ char line[LINFO_LINESIZE]; /* tty/pty name */
+ char username[LINFO_NAMESIZE]; /* login username */
+ char hostname[LINFO_HOSTSIZE]; /* remote hostname */
+ /* 'exit_status' structure components */
+ int exit; /* process exit status */
+ int termination; /* process termination status */
+ /* struct timeval (sys/time.h) isn't always available, if it isn't we'll
+ * use time_t's value as tv_sec and set tv_usec to 0
+ */
+ unsigned int tv_sec;
+ unsigned int tv_usec;
+ union login_netinfo hostaddr; /* caller's host address(es) */
+}; /* struct logininfo */
+
+/*
+ * login recording functions
+ */
+
+/** 'public' functions */
+
+struct logininfo *login_alloc_entry(int pid, const char *username,
+ const char *hostname, const char *line);
+/* free a structure */
+void login_free_entry(struct logininfo *li);
+/* fill out a pre-allocated structure with useful information */
+int login_init_entry(struct logininfo *li, int pid, const char *username,
+ const char *hostname, const char *line);
+/* place the current time in a logininfo struct */
+void login_set_current_time(struct logininfo *li);
+
+/* record the entry */
+int login_login (struct logininfo *li);
+int login_logout(struct logininfo *li);
+#ifdef LOGIN_NEEDS_UTMPX
+int login_utmp_only(struct logininfo *li);
+#endif
+
+/** End of public functions */
+
+/* record the entry */
+int login_write (struct logininfo *li);
+int login_log_entry(struct logininfo *li);
+
+/* set the network address based on network address type */
+void login_set_addr(struct logininfo *li, const struct sockaddr *sa,
+ const unsigned int sa_size);
+
+/* 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);
+char *line_abbrevname(char *dst, const char *src, size_t dstsize);
+
+#endif /* _HAVE_LOGINREC_H_ */
diff --git a/options.h b/options.h
new file mode 100644
index 0000000..1feae40
--- /dev/null
+++ b/options.h
@@ -0,0 +1,401 @@
+/* Dropbear SSH
+ * Copyright (c) 2002,2003 Matt Johnston
+ * All rights reserved. See LICENSE for the license. */
+
+#ifndef _OPTIONS_H_
+#define _OPTIONS_H_
+
+/******************************************************************
+ * Define compile-time options below - the "#ifndef DROPBEAR_XXX .... #endif"
+ * parts are to allow for commandline -DDROPBEAR_XXX options etc.
+ ******************************************************************/
+
+#ifndef DROPBEAR_DEFPORT
+#define DROPBEAR_DEFPORT "22"
+#endif
+
+/* Default hostkey paths - these can be specified on the command line */
+#ifndef DSS_PRIV_FILENAME
+#define DSS_PRIV_FILENAME "/etc/dropbear/dropbear_dss_host_key"
+#endif
+#ifndef RSA_PRIV_FILENAME
+#define RSA_PRIV_FILENAME "/etc/dropbear/dropbear_rsa_host_key"
+#endif
+
+/* Set NON_INETD_MODE if you require daemon functionality (ie Dropbear listens
+ * on chosen ports and keeps accepting connections. This is the default.
+ *
+ * Set INETD_MODE if you want to be able to run Dropbear with inetd (or
+ * similar), where it will use stdin/stdout for connections, and each process
+ * lasts for a single connection. Dropbear should be invoked with the -i flag
+ * for inetd, and can only accept IPv4 connections.
+ *
+ * Both of these flags can be defined at once, don't compile without at least
+ * one of them. */
+#define NON_INETD_MODE
+#define INETD_MODE
+
+/* Setting this disables the fast exptmod bignum code. It saves ~5kB, but is
+ * perhaps 20% slower for pubkey operations (it is probably worth experimenting
+ * if you want to use this) */
+/*#define NO_FAST_EXPTMOD*/
+
+/* Set this if you want to use the DROPBEAR_SMALL_CODE option. This can save
+several kB in binary size, however will make the symmetrical ciphers (AES, DES
+etc) slower (perhaps by 50%). Recommended for most small systems. */
+#define DROPBEAR_SMALL_CODE
+
+/* Enable X11 Forwarding - server only */
+#define ENABLE_X11FWD
+
+/* Enable TCP Fowarding */
+/* 'Local' is "-L" style (client listening port forwarded via server)
+ * 'Remote' is "-R" style (server listening port forwarded via client) */
+
+#define ENABLE_CLI_LOCALTCPFWD
+#define ENABLE_CLI_REMOTETCPFWD
+
+#define ENABLE_SVR_LOCALTCPFWD
+#define ENABLE_SVR_REMOTETCPFWD
+
+/* Enable Authentication Agent Forwarding - server only for now */
+#define ENABLE_AGENTFWD
+
+/* Encryption - at least one required.
+ * RFC Draft requires 3DES and recommends AES128 for interoperability.
+ * Including multiple keysize variants the same cipher
+ * (eg AES256 as well as AES128) will result in a minimal size increase.*/
+#define DROPBEAR_AES128_CBC
+#define DROPBEAR_3DES_CBC
+#define DROPBEAR_AES256_CBC
+#define DROPBEAR_BLOWFISH_CBC
+#define DROPBEAR_TWOFISH256_CBC
+#define DROPBEAR_TWOFISH128_CBC
+
+/* Message Integrity - at least one required.
+ * RFC Draft requires sha1 and recommends sha1-96.
+ * sha1-96 may be of use for slow links, as it has a smaller overhead.
+ *
+ * Note: there's no point disabling sha1 to save space, since it's used
+ * for the random number generator and public-key cryptography anyway.
+ * Disabling it here will just stop it from being used as the integrity portion
+ * of the ssh protocol.
+ *
+ * These hashes are also used for public key fingerprints in logs.
+ * If you disable MD5, Dropbear will fall back to SHA1 fingerprints,
+ * which are not the standard form. */
+#define DROPBEAR_SHA1_HMAC
+#define DROPBEAR_SHA1_96_HMAC
+#define DROPBEAR_MD5_HMAC
+
+/* Hostkey/public key algorithms - at least one required, these are used
+ * for hostkey as well as for verifying signatures with pubkey auth.
+ * Removing either of these won't save very much space.
+ * SSH2 RFC Draft requires dss, recommends rsa */
+#define DROPBEAR_RSA
+#define DROPBEAR_DSS
+
+/* RSA can be vulnerable to timing attacks which use the time required for
+ * signing to guess the private key. Blinding avoids this attack, though makes
+ * signing operations slightly slower. */
+#define RSA_BLINDING
+
+/* Define DSS_PROTOK to use PuTTY's method of generating the value k for dss,
+ * rather than just from the random byte source. Undefining this will save you
+ * ~4k in binary size with static uclibc, but your DSS hostkey could be exposed
+ * if the random number source isn't good. In general this isn't required */
+/* #define DSS_PROTOK */
+
+/* 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
+ * size */
+#define DO_MOTD
+
+/* The MOTD file path */
+#ifndef MOTD_FILENAME
+#define MOTD_FILENAME "/etc/motd"
+#endif
+
+/* 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
+#define ENABLE_CLI_INTERACT_AUTH
+
+/* 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*/
+
+/* 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 */
+/* The first setting is per-IP, to avoid denial of service */
+#ifndef MAX_UNAUTH_PER_IP
+#define MAX_UNAUTH_PER_IP 5
+#endif
+
+/* And then a global limit to avoid chewing memory if connections
+ * come from many IPs */
+#ifndef MAX_UNAUTH_CLIENTS
+#define MAX_UNAUTH_CLIENTS 30
+#endif
+
+/* Maximum number of failed authentication tries (server option) */
+#ifndef MAX_AUTH_TRIES
+#define MAX_AUTH_TRIES 10
+#endif
+
+/* The file to store the daemon's process ID, for shutdown scripts etc */
+#ifndef DROPBEAR_PIDFILE
+#define DROPBEAR_PIDFILE "/var/run/dropbear.pid"
+#endif
+
+/* The command to invoke for xauth when using X11 forwarding.
+ * "-q" for quiet */
+#ifndef XAUTH_COMMAND
+#define XAUTH_COMMAND "/usr/X11R6/bin/xauth -q"
+#endif
+
+/* if you want to enable running an sftp server (such as the one included with
+ * OpenSSH), set the path below. If the path isn't defined, sftp will not
+ * be enabled */
+#ifndef SFTPSERVER_PATH
+#define SFTPSERVER_PATH "/usr/libexec/sftp-server"
+#endif
+
+/* This is used by the scp binary when used as a client binary. If you're
+ * not using the Dropbear client, you'll need to change it */
+#define _PATH_SSH_PROGRAM "/usr/bin/dbclient"
+
+/* Multi-purpose binary configuration has now moved. Look at the top
+ * of the Makefile for instructions, or INSTALL */
+
+/*******************************************************************
+ * You shouldn't edit below here unless you know you need to.
+ *******************************************************************/
+
+#ifndef DROPBEAR_VERSION
+#define DROPBEAR_VERSION "0.47"
+#endif
+
+#define LOCAL_IDENT "SSH-2.0-dropbear_" DROPBEAR_VERSION
+#define PROGNAME "dropbear"
+
+/* Spec recommends after one hour or 1 gigabyte of data. One hour
+ * is a bit too verbose, so we try 8 hours */
+#ifndef KEX_REKEY_TIMEOUT
+#define KEX_REKEY_TIMEOUT (3600 * 8)
+#endif
+#ifndef KEX_REKEY_DATA
+#define KEX_REKEY_DATA (1<<30) /* 2^30 == 1GB, this value must be < INT_MAX */
+#endif
+/* Close connections to clients which haven't authorised after AUTH_TIMEOUT */
+#ifndef AUTH_TIMEOUT
+#define AUTH_TIMEOUT 300 /* we choose 5 minutes */
+#endif
+
+/* Minimum key sizes for DSS and RSA */
+#ifndef MIN_DSS_KEYLEN
+#define MIN_DSS_KEYLEN 512
+#endif
+#ifndef MIN_RSA_KEYLEN
+#define MIN_RSA_KEYLEN 512
+#endif
+
+#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 */
+
+/* the number of NAME=VALUE pairs to malloc for environ, if we don't have
+ * the clearenv() function */
+#define ENV_SIZE 100
+
+#define MAX_CMD_LEN 1024 /* max length of a command */
+#define MAX_TERM_LEN 200 /* max length of TERM name */
+
+#define MAX_HOST_LEN 254 /* max hostname len for tcp fwding */
+#define MAX_IP_LEN 15 /* strlen("255.255.255.255") == 15 */
+
+#define DROPBEAR_MAX_PORTS 10 /* max number of ports which can be specified,
+ ipv4 and ipv6 don't count twice */
+
+#define _PATH_TTY "/dev/tty"
+
+/* Timeouts in seconds */
+#define SELECT_TIMEOUT 20
+
+/* success/failure defines */
+#define DROPBEAR_SUCCESS 0
+#define DROPBEAR_FAILURE -1
+
+/* various algorithm identifiers */
+#define DROPBEAR_KEX_DH_GROUP1 0
+
+#define DROPBEAR_SIGNKEY_ANY 0
+#define DROPBEAR_SIGNKEY_RSA 1
+#define DROPBEAR_SIGNKEY_DSS 2
+#define DROPBEAR_SIGNKEY_NONE 3
+
+#define DROPBEAR_COMP_NONE 0
+#define DROPBEAR_COMP_ZLIB 1
+
+/* Required for pubkey auth */
+#if defined(ENABLE_SVR_PUBKEY_AUTH) || defined(DROPBEAR_CLIENT)
+#define DROPBEAR_SIGNKEY_VERIFY
+#endif
+
+/* SHA1 is 20 bytes == 160 bits */
+#define SHA1_HASH_SIZE 20
+/* SHA512 is 64 bytes == 512 bits */
+#define SHA512_HASH_SIZE 64
+/* MD5 is 16 bytes = 128 bits */
+#define MD5_HASH_SIZE 16
+
+/* largest of MD5 and SHA1 */
+#define MAX_MAC_LEN SHA1_HASH_SIZE
+
+
+#define MAX_KEY_LEN 32 /* 256 bits for aes256 etc */
+#define MAX_IV_LEN 20 /* must be same as max blocksize,
+ and >= SHA1_HASH_SIZE */
+#define MAX_MAC_KEY 20
+
+#define MAX_NAME_LEN 64 /* maximum length of a protocol name, isn't
+ explicitly specified for all protocols (just
+ for algos) but seems valid */
+
+#define MAX_PROPOSED_ALGO 20
+
+/* size/count limits */
+#define MAX_LISTEN_ADDR 10
+
+#define MAX_PACKET_LEN 35000
+#define MIN_PACKET_LEN 16
+#define MAX_PAYLOAD_LEN 32768
+
+#define MAX_TRANS_PAYLOAD_LEN 32768
+#define MAX_TRANS_PACKET_LEN (MAX_TRANS_PAYLOAD_LEN+50)
+
+#define MAX_TRANS_WINDOW 500000000 /* 500MB is sufficient, stopping overflow */
+#define MAX_TRANS_WIN_INCR 500000000 /* overflow prevention */
+
+#define MAX_STRING_LEN 1400 /* ~= MAX_PROPOSED_ALGO * MAX_NAME_LEN, also
+ is the max length for a password etc */
+
+/* For a 4096 bit DSS key, empirically determined */
+#define MAX_PUBKEY_SIZE 1700
+/* For a 4096 bit DSS key, empirically determined */
+#define MAX_PRIVKEY_SIZE 1700
+
+/* The maximum size of the bignum portion of the kexhash buffer */
+/* Sect. 8 of the transport draft, K_S + e + f + K */
+#define KEXHASHBUF_MAX_INTS (1700 + 130 + 130 + 130)
+
+#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
+
+#define DROPBEAR_MAX_CLI_INTERACT_PROMPTS 80 /* The number of prompts we'll
+ accept for keyb-interactive
+ auth */
+
+#if defined(DROPBEAR_AES256_CBC) || defined(DROPBEAR_AES128_CBC)
+#define DROPBEAR_AES_CBC
+#endif
+
+#if defined(DROPBEAR_TWOFISH256_CBC) || defined(DROPBEAR_TWOFISH128_CBC)
+#define DROPBEAR_TWOFISH_CBC
+#endif
+
+#ifndef ENABLE_X11FWD
+#define DISABLE_X11FWD
+#endif
+
+#ifndef ENABLE_AGENTFWD
+#define DISABLE_AGENTFWD
+#endif
+
+#if defined(ENABLE_CLI_REMOTETCPFWD) || defined(ENABLE_CLI_LOCALTCPFWD)
+#define ENABLE_CLI_ANYTCPFWD
+#endif
+
+#if defined(ENABLE_CLI_LOCALTCPFWD) || defined(ENABLE_SVR_REMOTETCPFWD)
+#define DROPBEAR_TCP_ACCEPT
+#endif
+
+#if defined(ENABLE_CLI_REMOTETCPFWD) || defined(ENABLE_CLI_LOCALTCPFWD) || \
+ defined(ENABLE_SVR_REMOTETCPFWD) || defined(ENABLE_SVR_LOCALTCPFWD) || \
+ defined(ENABLE_AGENTFWD) || defined(ENABLE_X11FWD)
+#define USING_LISTENERS
+#endif
+
+#if defined(DROPBEAR_CLIENT) || defined(ENABLE_SVR_PUBKEY_AUTH)
+#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)
+
+#define IS_DROPBEAR_SERVER (ses.isserver == 1)
+#define IS_DROPBEAR_CLIENT (ses.isserver == 0)
+
+#elif defined(DROPBEAR_SERVER)
+
+#define IS_DROPBEAR_SERVER 1
+#define IS_DROPBEAR_CLIENT 0
+
+#elif defined(DROPBEAR_CLIENT)
+
+#define IS_DROPBEAR_SERVER 0
+#define IS_DROPBEAR_CLIENT 1
+
+#else
+#error You must compiled with either DROPBEAR_CLIENT or DROPBEAR_SERVER selected
+#endif
+
+#endif /* _OPTIONS_H_ */
diff --git a/packet.c b/packet.c
new file mode 100644
index 0000000..b2c6174
--- /dev/null
+++ b/packet.c
@@ -0,0 +1,613 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+#include "includes.h"
+#include "packet.h"
+#include "session.h"
+#include "dbutil.h"
+#include "ssh.h"
+#include "algo.h"
+#include "buffer.h"
+#include "kex.h"
+#include "random.h"
+#include "service.h"
+#include "auth.h"
+#include "channel.h"
+
+static void read_packet_init();
+static void writemac(buffer * outputbuffer, buffer * clearwritebuf);
+static int checkmac(buffer* hashbuf, buffer* readbuf);
+
+#define ZLIB_COMPRESS_INCR 20 /* this is 12 bytes + 0.1% of 8000 bytes */
+#define ZLIB_DECOMPRESS_INCR 100
+#ifndef DISABLE_ZLIB
+static buffer* buf_decompress(buffer* buf, unsigned int len);
+static void buf_compress(buffer * dest, buffer * src, unsigned int len);
+#endif
+
+/* non-blocking function writing out a current encrypted packet */
+void write_packet() {
+
+ int len, written;
+ buffer * writebuf = NULL;
+
+ TRACE(("enter write_packet"))
+ dropbear_assert(!isempty(&ses.writequeue));
+
+ /* Get the next buffer in the queue of encrypted packets to write*/
+ writebuf = (buffer*)examine(&ses.writequeue);
+
+ len = writebuf->len - writebuf->pos;
+ dropbear_assert(len > 0);
+ /* Try to write as much as possible */
+ written = write(ses.sock, buf_getptr(writebuf, len), len);
+
+ if (written < 0) {
+ if (errno == EINTR) {
+ TRACE(("leave writepacket: EINTR"))
+ return;
+ } else {
+ dropbear_exit("error writing");
+ }
+ }
+
+ if (written == 0) {
+ ses.remoteclosed();
+ }
+
+ if (written == len) {
+ /* We've finished with the packet, free it */
+ dequeue(&ses.writequeue);
+ buf_free(writebuf);
+ writebuf = NULL;
+ } else {
+ /* More packet left to write, leave it in the queue for later */
+ buf_incrpos(writebuf, written);
+ }
+
+ TRACE(("leave write_packet"))
+}
+
+/* Non-blocking function reading available portion of a packet into the
+ * ses's buffer, decrypting the length if encrypted, decrypting the
+ * full portion if possible */
+void read_packet() {
+
+ int len;
+ unsigned int maxlen;
+ unsigned char blocksize;
+
+ TRACE(("enter read_packet"))
+ blocksize = ses.keys->recv_algo_crypt->blocksize;
+
+ if (ses.readbuf == NULL || ses.readbuf->len < blocksize) {
+ /* In the first blocksize of a packet */
+
+ /* Read the first blocksize of the packet, so we can decrypt it and
+ * find the length of the whole packet */
+ read_packet_init();
+
+ /* 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"))
+ return;
+ }
+ }
+
+ /* Attempt to read the remainder of the packet, note that there
+ * mightn't be any available (EAGAIN) */
+ dropbear_assert(ses.readbuf != NULL);
+ maxlen = ses.readbuf->len - ses.readbuf->pos;
+ len = read(ses.sock, buf_getptr(ses.readbuf, maxlen), maxlen);
+
+ if (len == 0) {
+ ses.remoteclosed();
+ }
+
+ if (len < 0) {
+ if (errno == EINTR || errno == EAGAIN) {
+ TRACE(("leave read_packet: EINTR or EAGAIN"))
+ return;
+ } else {
+ dropbear_exit("error reading: %s", strerror(errno));
+ }
+ }
+
+ buf_incrpos(ses.readbuf, len);
+
+ if ((unsigned int)len == maxlen) {
+ /* The whole packet has been read */
+ decrypt_packet();
+ /* The main select() loop process_packet() to
+ * handle the packet contents... */
+ }
+ TRACE(("leave read_packet"))
+}
+
+/* Function used to read the initial portion of a packet, and determine the
+ * length. Only called during the first BLOCKSIZE of a packet. */
+static void read_packet_init() {
+
+ unsigned int maxlen;
+ int len;
+ unsigned char blocksize;
+ unsigned char macsize;
+
+
+ blocksize = ses.keys->recv_algo_crypt->blocksize;
+ macsize = ses.keys->recv_algo_mac->hashsize;
+
+ if (ses.readbuf == NULL) {
+ /* start of a new packet */
+ ses.readbuf = buf_new(INIT_READBUF);
+ dropbear_assert(ses.decryptreadbuf == NULL);
+ ses.decryptreadbuf = buf_new(blocksize);
+ }
+
+ maxlen = blocksize - ses.readbuf->pos;
+
+ /* read the rest of the packet if possible */
+ len = read(ses.sock, buf_getwriteptr(ses.readbuf, maxlen),
+ maxlen);
+ if (len == 0) {
+ ses.remoteclosed();
+ }
+ if (len < 0) {
+ if (errno == EINTR) {
+ TRACE(("leave read_packet_init: EINTR"))
+ return;
+ }
+ dropbear_exit("error reading: %s", strerror(errno));
+ }
+
+ buf_incrwritepos(ses.readbuf, len);
+
+ if ((unsigned int)len != maxlen) {
+ /* don't have enough bytes to determine length, get next time */
+ return;
+ }
+
+ /* now we have the first block, need to get packet length, so we decrypt
+ * the first block (only need first 4 bytes) */
+ buf_setpos(ses.readbuf, 0);
+ if (ses.keys->recv_algo_crypt->cipherdesc == NULL) {
+ /* copy it */
+ memcpy(buf_getwriteptr(ses.decryptreadbuf, blocksize),
+ buf_getptr(ses.readbuf, blocksize),
+ blocksize);
+ } else {
+ /* decrypt it */
+ if (cbc_decrypt(buf_getptr(ses.readbuf, blocksize),
+ buf_getwriteptr(ses.decryptreadbuf,blocksize),
+ blocksize,
+ &ses.keys->recv_symmetric_struct) != CRYPT_OK) {
+ dropbear_exit("error decrypting");
+ }
+ }
+ buf_setlen(ses.decryptreadbuf, blocksize);
+ len = buf_getint(ses.decryptreadbuf) + 4 + macsize;
+
+ buf_setpos(ses.readbuf, blocksize);
+
+ /* check packet length */
+ if ((len > MAX_PACKET_LEN) ||
+ (len < MIN_PACKET_LEN + macsize) ||
+ ((len - macsize) % blocksize != 0)) {
+ dropbear_exit("bad packet size %d", len);
+ }
+
+ buf_resize(ses.readbuf, len);
+ buf_setlen(ses.readbuf, len);
+
+}
+
+/* handle the received packet */
+void decrypt_packet() {
+
+ unsigned char blocksize;
+ unsigned char macsize;
+ unsigned int padlen;
+ unsigned int len;
+
+ TRACE(("enter decrypt_packet"))
+ blocksize = ses.keys->recv_algo_crypt->blocksize;
+ macsize = ses.keys->recv_algo_mac->hashsize;
+
+ ses.kexstate.datarecv += ses.readbuf->len;
+
+ /* we've already decrypted the first blocksize in read_packet_init */
+ buf_setpos(ses.readbuf, blocksize);
+
+ buf_resize(ses.decryptreadbuf, ses.readbuf->len - macsize);
+ buf_setlen(ses.decryptreadbuf, ses.decryptreadbuf->size);
+ buf_setpos(ses.decryptreadbuf, blocksize);
+
+ /* decrypt if encryption is set, memcpy otherwise */
+ if (ses.keys->recv_algo_crypt->cipherdesc == NULL) {
+ /* copy it */
+ len = ses.readbuf->len - macsize - blocksize;
+ memcpy(buf_getwriteptr(ses.decryptreadbuf, len),
+ buf_getptr(ses.readbuf, len), len);
+ } else {
+ /* decrypt */
+ while (ses.readbuf->pos < ses.readbuf->len - macsize) {
+ if (cbc_decrypt(buf_getptr(ses.readbuf, blocksize),
+ buf_getwriteptr(ses.decryptreadbuf, blocksize),
+ blocksize,
+ &ses.keys->recv_symmetric_struct) != CRYPT_OK) {
+ dropbear_exit("error decrypting");
+ }
+ buf_incrpos(ses.readbuf, blocksize);
+ buf_incrwritepos(ses.decryptreadbuf, blocksize);
+ }
+ }
+
+ /* check the hmac */
+ buf_setpos(ses.readbuf, ses.readbuf->len - macsize);
+ if (checkmac(ses.readbuf, ses.decryptreadbuf) != DROPBEAR_SUCCESS) {
+ dropbear_exit("Integrity error");
+ }
+
+ /* readbuf no longer required */
+ buf_free(ses.readbuf);
+ ses.readbuf = NULL;
+
+ /* get padding length */
+ buf_setpos(ses.decryptreadbuf, PACKET_PADDING_OFF);
+ padlen = buf_getbyte(ses.decryptreadbuf);
+
+ /* payload length */
+ /* - 4 - 1 is for LEN and PADLEN values */
+ len = ses.decryptreadbuf->len - padlen - 4 - 1;
+ if ((len > MAX_PAYLOAD_LEN) || (len < 1)) {
+ dropbear_exit("bad packet size");
+ }
+
+ buf_setpos(ses.decryptreadbuf, PACKET_PAYLOAD_OFF);
+
+#ifndef DISABLE_ZLIB
+ if (ses.keys->recv_algo_comp == DROPBEAR_COMP_ZLIB) {
+ /* decompress */
+ ses.payload = buf_decompress(ses.decryptreadbuf, len);
+
+ } else
+#endif
+ {
+ /* copy payload */
+ ses.payload = buf_new(len);
+ memcpy(ses.payload->data, buf_getptr(ses.decryptreadbuf, len), len);
+ buf_incrlen(ses.payload, len);
+ }
+
+ buf_free(ses.decryptreadbuf);
+ ses.decryptreadbuf = NULL;
+ buf_setpos(ses.payload, 0);
+
+ ses.recvseq++;
+
+ TRACE(("leave decrypt_packet"))
+}
+
+/* Checks the mac in hashbuf, for the data in readbuf.
+ * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
+static int checkmac(buffer* macbuf, buffer* sourcebuf) {
+
+ unsigned int macsize;
+ hmac_state hmac;
+ unsigned char tempbuf[MAX_MAC_LEN];
+ unsigned long bufsize;
+ unsigned int len;
+
+ macsize = ses.keys->recv_algo_mac->hashsize;
+ if (macsize == 0) {
+ return DROPBEAR_SUCCESS;
+ }
+
+ /* calculate the mac */
+ if (hmac_init(&hmac,
+ find_hash(ses.keys->recv_algo_mac->hashdesc->name),
+ ses.keys->recvmackey,
+ ses.keys->recv_algo_mac->keysize)
+ != CRYPT_OK) {
+ dropbear_exit("HMAC error");
+ }
+
+ /* sequence number */
+ STORE32H(ses.recvseq, tempbuf);
+ if (hmac_process(&hmac, tempbuf, 4) != CRYPT_OK) {
+ dropbear_exit("HMAC error");
+ }
+
+ buf_setpos(sourcebuf, 0);
+ len = sourcebuf->len;
+ if (hmac_process(&hmac, buf_getptr(sourcebuf, len), len) != CRYPT_OK) {
+ dropbear_exit("HMAC error");
+ }
+
+ bufsize = sizeof(tempbuf);
+ if (hmac_done(&hmac, tempbuf, &bufsize) != CRYPT_OK) {
+ dropbear_exit("HMAC error");
+ }
+
+ /* compare the hash */
+ if (memcmp(tempbuf, buf_getptr(macbuf, macsize), macsize) != 0) {
+ return DROPBEAR_FAILURE;
+ } else {
+ return DROPBEAR_SUCCESS;
+ }
+}
+
+#ifndef DISABLE_ZLIB
+/* returns a pointer to a newly created buffer */
+static buffer* buf_decompress(buffer* buf, unsigned int len) {
+
+ int result;
+ buffer * ret;
+ z_streamp zstream;
+
+ zstream = ses.keys->recv_zstream;
+ ret = buf_new(len);
+
+ zstream->avail_in = len;
+ zstream->next_in = buf_getptr(buf, len);
+
+ /* decompress the payload, incrementally resizing the output buffer */
+ while (1) {
+
+ zstream->avail_out = ret->size - ret->pos;
+ zstream->next_out = buf_getwriteptr(ret, zstream->avail_out);
+
+ result = inflate(zstream, Z_SYNC_FLUSH);
+
+ buf_setlen(ret, ret->size - zstream->avail_out);
+ buf_setpos(ret, ret->len);
+
+ if (result != Z_BUF_ERROR && result != Z_OK) {
+ dropbear_exit("zlib error");
+ }
+
+ if (zstream->avail_in == 0 &&
+ (zstream->avail_out != 0 || result == Z_BUF_ERROR)) {
+ /* we can only exit if avail_out hasn't all been used,
+ * and there's no remaining input */
+ return ret;
+ }
+
+ if (zstream->avail_out == 0) {
+ buf_resize(ret, ret->size + ZLIB_DECOMPRESS_INCR);
+ }
+ }
+}
+#endif
+
+
+
+
+/* encrypt the writepayload, putting into writebuf, ready for write_packet()
+ * to put on the wire */
+void encrypt_packet() {
+
+ unsigned char padlen;
+ unsigned char blocksize, macsize;
+ 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]))
+ blocksize = ses.keys->trans_algo_crypt->blocksize;
+ macsize = ses.keys->trans_algo_mac->hashsize;
+
+ /* Encrypted packet len is payload+5, then worst case is if we are 3 away
+ * from a blocksize multiple. In which case we need to pad to the
+ * multiple, then add another blocksize (or MIN_PACKET_LEN) */
+ clearwritebuf = buf_new((ses.writepayload->len+4+1) + MIN_PACKET_LEN + 3
+#ifndef DISABLE_ZLIB
+ + ZLIB_COMPRESS_INCR /* bit of a kludge, but we can't know len*/
+#endif
+ );
+ buf_setlen(clearwritebuf, PACKET_PAYLOAD_OFF);
+ buf_setpos(clearwritebuf, PACKET_PAYLOAD_OFF);
+
+ buf_setpos(ses.writepayload, 0);
+
+#ifndef DISABLE_ZLIB
+ /* compression */
+ if (ses.keys->trans_algo_comp == DROPBEAR_COMP_ZLIB) {
+ buf_compress(clearwritebuf, ses.writepayload, ses.writepayload->len);
+ } else
+#endif
+ {
+ memcpy(buf_getwriteptr(clearwritebuf, ses.writepayload->len),
+ buf_getptr(ses.writepayload, ses.writepayload->len),
+ ses.writepayload->len);
+ buf_incrwritepos(clearwritebuf, ses.writepayload->len);
+ }
+
+ /* finished with payload */
+ buf_burn(ses.writepayload); /* XXX This is probably a good idea, and isn't
+ _that_ likely to hurt performance too badly.
+ Buffers can have cleartext passwords etc, or
+ other sensitive data */
+ buf_setpos(ses.writepayload, 0);
+ buf_setlen(ses.writepayload, 0);
+
+ /* length of padding - packet length must be a multiple of blocksize,
+ * with a minimum of 4 bytes of padding */
+ padlen = blocksize - (clearwritebuf->len) % blocksize;
+ if (padlen < 4) {
+ padlen += blocksize;
+ }
+ /* check for min packet length */
+ if (clearwritebuf->len + padlen < MIN_PACKET_LEN) {
+ padlen += blocksize;
+ }
+
+ buf_setpos(clearwritebuf, 0);
+ /* packet length excluding the packetlength uint32 */
+ buf_putint(clearwritebuf, clearwritebuf->len + padlen - 4);
+
+ /* padding len */
+ buf_putbyte(clearwritebuf, padlen);
+ /* actual padding */
+ buf_setpos(clearwritebuf, clearwritebuf->len);
+ buf_incrlen(clearwritebuf, padlen);
+ genrandom(buf_getptr(clearwritebuf, padlen), padlen);
+
+ /* do the actual encryption */
+ buf_setpos(clearwritebuf, 0);
+ /* create a new writebuffer, this is freed when it has been put on the
+ * wire by writepacket() */
+ writebuf = buf_new(clearwritebuf->len + macsize);
+
+ if (ses.keys->trans_algo_crypt->cipherdesc == NULL) {
+ /* copy it */
+ memcpy(buf_getwriteptr(writebuf, clearwritebuf->len),
+ buf_getptr(clearwritebuf, clearwritebuf->len),
+ clearwritebuf->len);
+ buf_incrwritepos(writebuf, clearwritebuf->len);
+ } else {
+ /* encrypt it */
+ while (clearwritebuf->pos < clearwritebuf->len) {
+ if (cbc_encrypt(buf_getptr(clearwritebuf, blocksize),
+ buf_getwriteptr(writebuf, blocksize),
+ blocksize,
+ &ses.keys->trans_symmetric_struct) != CRYPT_OK) {
+ dropbear_exit("error encrypting");
+ }
+ buf_incrpos(clearwritebuf, blocksize);
+ buf_incrwritepos(writebuf, blocksize);
+ }
+ }
+
+ /* now add a hmac and we're done */
+ writemac(writebuf, clearwritebuf);
+
+ /* clearwritebuf is finished with */
+ buf_free(clearwritebuf);
+ clearwritebuf = NULL;
+
+ /* enqueue the packet for sending */
+ buf_setpos(writebuf, 0);
+ enqueue(&ses.writequeue, (void*)writebuf);
+
+ /* Update counts */
+ ses.kexstate.datatrans += writebuf->len;
+ ses.transseq++;
+
+ TRACE(("leave encrypt_packet()"))
+}
+
+
+/* Create the packet mac, and append H(seqno|clearbuf) to the output */
+static void writemac(buffer * outputbuffer, buffer * clearwritebuf) {
+
+ unsigned int macsize;
+ unsigned char seqbuf[4];
+ unsigned char tempbuf[MAX_MAC_LEN];
+ unsigned long bufsize;
+ hmac_state hmac;
+
+ TRACE(("enter writemac"))
+
+ macsize = ses.keys->trans_algo_mac->hashsize;
+ if (macsize > 0) {
+ /* calculate the mac */
+ if (hmac_init(&hmac,
+ find_hash(ses.keys->trans_algo_mac->hashdesc->name),
+ ses.keys->transmackey,
+ ses.keys->trans_algo_mac->keysize) != CRYPT_OK) {
+ dropbear_exit("HMAC error");
+ }
+
+ /* sequence number */
+ STORE32H(ses.transseq, seqbuf);
+ if (hmac_process(&hmac, seqbuf, 4) != CRYPT_OK) {
+ dropbear_exit("HMAC error");
+ }
+
+ /* the actual contents */
+ buf_setpos(clearwritebuf, 0);
+ if (hmac_process(&hmac,
+ buf_getptr(clearwritebuf,
+ clearwritebuf->len),
+ clearwritebuf->len) != CRYPT_OK) {
+ dropbear_exit("HMAC error");
+ }
+
+ bufsize = sizeof(tempbuf);
+ if (hmac_done(&hmac, tempbuf, &bufsize)
+ != CRYPT_OK) {
+ dropbear_exit("HMAC error");
+ }
+ buf_putbytes(outputbuffer, tempbuf, macsize);
+ }
+ TRACE(("leave writemac"))
+}
+
+#ifndef DISABLE_ZLIB
+/* compresses len bytes from src, outputting to dest (starting from the
+ * respective current positions. */
+static void buf_compress(buffer * dest, buffer * src, unsigned int len) {
+
+ unsigned int endpos = src->pos + len;
+ int result;
+
+ TRACE(("enter buf_compress"))
+
+ while (1) {
+
+ ses.keys->trans_zstream->avail_in = endpos - src->pos;
+ ses.keys->trans_zstream->next_in =
+ buf_getptr(src, ses.keys->trans_zstream->avail_in);
+
+ ses.keys->trans_zstream->avail_out = dest->size - dest->pos;
+ ses.keys->trans_zstream->next_out =
+ buf_getwriteptr(dest, ses.keys->trans_zstream->avail_out);
+
+ result = deflate(ses.keys->trans_zstream, Z_SYNC_FLUSH);
+
+ buf_setpos(src, endpos - ses.keys->trans_zstream->avail_in);
+ buf_setlen(dest, dest->size - ses.keys->trans_zstream->avail_out);
+ buf_setpos(dest, dest->len);
+
+ if (result != Z_OK) {
+ dropbear_exit("zlib error");
+ }
+
+ if (ses.keys->trans_zstream->avail_in == 0) {
+ break;
+ }
+
+ dropbear_assert(ses.keys->trans_zstream->avail_out == 0);
+
+ /* the buffer has been filled, we must extend. This only happens in
+ * unusual circumstances where the data grows in size after deflate(),
+ * but it is possible */
+ buf_resize(dest, dest->size + ZLIB_COMPRESS_INCR);
+
+ }
+ TRACE(("leave buf_compress"))
+}
+#endif
diff --git a/packet.h b/packet.h
new file mode 100644
index 0000000..e9768cd
--- /dev/null
+++ b/packet.h
@@ -0,0 +1,48 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+#ifndef _PACKET_H_
+
+#define _PACKET_H_
+
+#include "includes.h"
+
+void write_packet();
+void read_packet();
+void decrypt_packet();
+void encrypt_packet();
+
+void process_packet();
+
+typedef struct PacketType {
+ unsigned char type; /* SSH_MSG_FOO */
+ void (*handler)();
+} packettype;
+
+#define PACKET_PADDING_OFF 4
+#define PACKET_PAYLOAD_OFF 5
+
+#define INIT_READBUF 200
+
+#endif /* _PACKET_H_ */
diff --git a/process-packet.c b/process-packet.c
new file mode 100644
index 0000000..07fc130
--- /dev/null
+++ b/process-packet.c
@@ -0,0 +1,145 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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 "packet.h"
+#include "session.h"
+#include "dbutil.h"
+#include "ssh.h"
+#include "algo.h"
+#include "buffer.h"
+#include "kex.h"
+#include "random.h"
+#include "service.h"
+#include "auth.h"
+#include "channel.h"
+
+#define MAX_UNAUTH_PACKET_TYPE SSH_MSG_USERAUTH_PK_OK
+
+static void recv_unimplemented();
+
+/* process a decrypted packet, call the appropriate handler */
+void process_packet() {
+
+ unsigned char type;
+ unsigned int i;
+
+ TRACE(("enter process_packet"))
+
+ type = buf_getbyte(ses.payload);
+ TRACE(("process_packet: packet type = %d", type))
+
+ ses.lastpacket = type;
+
+ /* These packets we can receive at any time */
+ switch(type) {
+
+ case SSH_MSG_IGNORE:
+ case SSH_MSG_DEBUG:
+ TRACE(("received SSH_MSG_IGNORE or SSH_MSG_DEBUG"))
+ goto out;
+
+ case SSH_MSG_UNIMPLEMENTED:
+ /* debugging XXX */
+ TRACE(("SSH_MSG_UNIMPLEMENTED"))
+ dropbear_exit("received SSH_MSG_UNIMPLEMENTED");
+
+ case SSH_MSG_DISCONNECT:
+ /* TODO cleanup? */
+ dropbear_close("Disconnect received");
+ }
+
+
+ /* This applies for KEX, where the spec says the next packet MUST be
+ * NEWKEYS */
+ if (ses.requirenext != 0) {
+ if (ses.requirenext != type) {
+ /* TODO send disconnect? */
+ dropbear_exit("unexpected packet type %d, expected %d", type,
+ ses.requirenext);
+ } else {
+ /* Got what we expected */
+ ses.requirenext = 0;
+ }
+ }
+
+ /* 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))
+ ses.ignorenext = 0;
+ goto out;
+ }
+
+
+ /* Kindly the protocol authors gave all the preauth packets type values
+ * less-than-or-equal-to 60 ( == MAX_UNAUTH_PACKET_TYPE ).
+ * NOTE: if the protocol changes and new types are added, revisit this
+ * assumption */
+ if ( !ses.authstate.authdone && type > MAX_UNAUTH_PACKET_TYPE ) {
+ dropbear_exit("received message %d before userauth", type);
+ }
+
+ for (i = 0; ; i++) {
+ if (ses.packettypes[i].type == 0) {
+ /* end of list */
+ break;
+ }
+
+ if (ses.packettypes[i].type == type) {
+ ses.packettypes[i].handler();
+ goto out;
+ }
+ }
+
+
+ /* TODO do something more here? */
+ TRACE(("preauth unknown packet"))
+ recv_unimplemented();
+
+out:
+ buf_burn(ses.payload); /* Clear the memory to avoid swapping it out */
+ buf_free(ses.payload);
+ ses.payload = NULL;
+
+ TRACE(("leave process_packet"))
+}
+
+
+
+/* This must be called directly after receiving the unimplemented packet.
+ * Isn't the most clean implementation, it relies on packet processing
+ * occurring directly after decryption (direct use of ses.recvseq).
+ * This is reasonably valid, since there is only a single decryption buffer */
+static void recv_unimplemented() {
+
+ CHECKCLEARTOWRITE();
+
+ buf_putbyte(ses.writepayload, SSH_MSG_UNIMPLEMENTED);
+ /* the decryption routine increments the sequence number, we must
+ * decrement */
+ buf_putint(ses.writepayload, ses.recvseq - 1);
+
+ encrypt_packet();
+}
diff --git a/progressmeter.c b/progressmeter.c
new file mode 100644
index 0000000..0d856cb
--- /dev/null
+++ b/progressmeter.c
@@ -0,0 +1,267 @@
+#ifdef PROGRESS_METER
+/*
+ * Copyright (c) 2003 Nils Nordman. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "includes.h"
+/*RCSID("OpenBSD: progressmeter.c,v 1.15 2003/08/31 12:14:22 markus Exp ");*/
+
+#include "progressmeter.h"
+#include "atomicio.h"
+#include "scpmisc.h"
+
+#define DEFAULT_WINSIZE 80
+#define MAX_WINSIZE 512
+#define PADDING 1 /* padding between the progress indicators */
+#define UPDATE_INTERVAL 1 /* update the progress meter every second */
+#define STALL_TIME 5 /* we're stalled after this many seconds */
+
+/* determines whether we can output to the terminal */
+static int can_output(void);
+
+/* formats and inserts the specified size into the given buffer */
+static void format_size(char *, int, off_t);
+static void format_rate(char *, int, off_t);
+
+/* updates the progressmeter to reflect the current state of the transfer */
+void refresh_progress_meter(void);
+
+/* signal handler for updating the progress meter */
+static void update_progress_meter(int);
+
+static time_t start; /* start progress */
+static time_t last_update; /* last progress update */
+static char *file; /* name of the file being transferred */
+static off_t end_pos; /* ending position of transfer */
+static off_t cur_pos; /* transfer position as of last refresh */
+static volatile off_t *counter; /* progress counter */
+static long stalled; /* how long we have been stalled */
+static int bytes_per_second; /* current speed in bytes per second */
+static int win_size; /* terminal window size */
+
+/* units for format_size */
+static const char unit[] = " KMGT";
+
+static int
+can_output(void)
+{
+ return (getpgrp() == tcgetpgrp(STDOUT_FILENO));
+}
+
+static void
+format_rate(char *buf, int size, off_t bytes)
+{
+ int i;
+
+ bytes *= 100;
+ for (i = 0; bytes >= 100*1000 && unit[i] != 'T'; i++)
+ bytes = (bytes + 512) / 1024;
+ if (i == 0) {
+ i++;
+ bytes = (bytes + 512) / 1024;
+ }
+ snprintf(buf, size, "%3lld.%1lld%c%s",
+ (int64_t) bytes / 100,
+ (int64_t) (bytes + 5) / 10 % 10,
+ unit[i],
+ i ? "B" : " ");
+}
+
+static void
+format_size(char *buf, int size, off_t bytes)
+{
+ int i;
+
+ for (i = 0; bytes >= 10000 && unit[i] != 'T'; i++)
+ bytes = (bytes + 512) / 1024;
+ snprintf(buf, size, "%4lld%c%s",
+ (int64_t) bytes,
+ unit[i],
+ i ? "B" : " ");
+}
+
+void
+refresh_progress_meter(void)
+{
+ char buf[MAX_WINSIZE + 1];
+ time_t now;
+ off_t transferred;
+ double elapsed;
+ int percent;
+ int bytes_left;
+ int cur_speed;
+ int hours, minutes, seconds;
+ int i, len;
+ int file_len;
+
+ transferred = *counter - cur_pos;
+ cur_pos = *counter;
+ now = time(NULL);
+ bytes_left = end_pos - cur_pos;
+
+ if (bytes_left > 0)
+ elapsed = now - last_update;
+ else
+ elapsed = now - start;
+
+ /* calculate speed */
+ if (elapsed != 0)
+ cur_speed = (transferred / elapsed);
+ else
+ cur_speed = 0;
+
+#define AGE_FACTOR 0.9
+ if (bytes_per_second != 0) {
+ bytes_per_second = (bytes_per_second * AGE_FACTOR) +
+ (cur_speed * (1.0 - AGE_FACTOR));
+ } else
+ bytes_per_second = cur_speed;
+
+ /* filename */
+ buf[0] = '\0';
+ file_len = win_size - 35;
+ if (file_len > 0) {
+ len = snprintf(buf, file_len + 1, "\r%s", file);
+ if (len < 0)
+ len = 0;
+ for (i = len; i < file_len; i++ )
+ buf[i] = ' ';
+ buf[file_len] = '\0';
+ }
+
+ /* percent of transfer done */
+ if (end_pos != 0)
+ percent = ((float)cur_pos / end_pos) * 100;
+ else
+ percent = 100;
+ snprintf(buf + strlen(buf), win_size - strlen(buf),
+ " %3d%% ", percent);
+
+ /* amount transferred */
+ format_size(buf + strlen(buf), win_size - strlen(buf),
+ cur_pos);
+ strlcat(buf, " ", win_size);
+
+ /* bandwidth usage */
+ format_rate(buf + strlen(buf), win_size - strlen(buf),
+ bytes_per_second);
+ strlcat(buf, "/s ", win_size);
+
+ /* ETA */
+ if (!transferred)
+ stalled += elapsed;
+ else
+ stalled = 0;
+
+ if (stalled >= STALL_TIME)
+ strlcat(buf, "- stalled -", win_size);
+ else if (bytes_per_second == 0 && bytes_left)
+ strlcat(buf, " --:-- ETA", win_size);
+ else {
+ if (bytes_left > 0)
+ seconds = bytes_left / bytes_per_second;
+ else
+ seconds = elapsed;
+
+ hours = seconds / 3600;
+ seconds -= hours * 3600;
+ minutes = seconds / 60;
+ seconds -= minutes * 60;
+
+ if (hours != 0)
+ snprintf(buf + strlen(buf), win_size - strlen(buf),
+ "%d:%02d:%02d", hours, minutes, seconds);
+ else
+ snprintf(buf + strlen(buf), win_size - strlen(buf),
+ " %02d:%02d", minutes, seconds);
+
+ if (bytes_left > 0)
+ strlcat(buf, " ETA", win_size);
+ else
+ strlcat(buf, " ", win_size);
+ }
+
+ atomicio(vwrite, STDOUT_FILENO, buf, win_size);
+ last_update = now;
+}
+
+static void
+update_progress_meter(int ignore)
+{
+ int save_errno;
+
+ save_errno = errno;
+
+ if (can_output())
+ refresh_progress_meter();
+
+ signal(SIGALRM, update_progress_meter);
+ alarm(UPDATE_INTERVAL);
+ errno = save_errno;
+}
+
+void
+start_progress_meter(char *f, off_t filesize, off_t *stat)
+{
+ struct winsize winsize;
+
+ start = last_update = time(NULL);
+ file = f;
+ end_pos = filesize;
+ cur_pos = 0;
+ counter = stat;
+ stalled = 0;
+ bytes_per_second = 0;
+
+ if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &winsize) != -1 &&
+ winsize.ws_col != 0) {
+ if (winsize.ws_col > MAX_WINSIZE)
+ win_size = MAX_WINSIZE;
+ else
+ win_size = winsize.ws_col;
+ } else
+ win_size = DEFAULT_WINSIZE;
+ win_size += 1; /* trailing \0 */
+
+ if (can_output())
+ refresh_progress_meter();
+
+ signal(SIGALRM, update_progress_meter);
+ alarm(UPDATE_INTERVAL);
+}
+
+void
+stop_progress_meter(void)
+{
+ alarm(0);
+
+ if (!can_output())
+ return;
+
+ /* Ensure we complete the progress */
+ if (cur_pos != end_pos)
+ refresh_progress_meter();
+
+ atomicio(vwrite, STDOUT_FILENO, "\n", 1);
+}
+#endif /* PROGRESS_METER */
diff --git a/progressmeter.h b/progressmeter.h
new file mode 100644
index 0000000..bfb9a0b
--- /dev/null
+++ b/progressmeter.h
@@ -0,0 +1,27 @@
+/* $OpenBSD: progressmeter.h,v 1.1 2003/01/10 08:19:07 fgsch Exp $ */
+/*
+ * Copyright (c) 2002 Nils Nordman. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+void start_progress_meter(char *, off_t, off_t *);
+void stop_progress_meter(void);
diff --git a/queue.c b/queue.c
new file mode 100644
index 0000000..7a80124
--- /dev/null
+++ b/queue.c
@@ -0,0 +1,89 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+#include "includes.h"
+#include "dbutil.h"
+#include "queue.h"
+
+void initqueue(struct Queue* queue) {
+
+ queue->head = NULL;
+ queue->tail = NULL;
+ queue->count = 0;
+}
+
+int isempty(struct Queue* queue) {
+
+ return (queue->head == NULL);
+}
+
+void* dequeue(struct Queue* queue) {
+
+ void* ret;
+ struct Link* oldhead;
+ dropbear_assert(!isempty(queue));
+
+ ret = queue->head->item;
+ oldhead = queue->head;
+
+ if (oldhead->link != NULL) {
+ queue->head = oldhead->link;
+ } else {
+ queue->head = NULL;
+ queue->tail = NULL;
+ TRACE(("empty queue dequeing"))
+ }
+
+ m_free(oldhead);
+ queue->count--;
+ return ret;
+}
+
+void *examine(struct Queue* queue) {
+
+ dropbear_assert(!isempty(queue));
+ return queue->head->item;
+}
+
+void enqueue(struct Queue* queue, void* item) {
+
+ struct Link* newlink;
+
+ TRACE(("enter enqueue"))
+ newlink = (struct Link*)m_malloc(sizeof(struct Link));
+
+ newlink->item = item;
+ newlink->link = NULL;
+
+ if (queue->tail != NULL) {
+ queue->tail->link = newlink;
+ }
+ queue->tail = newlink;
+
+ if (queue->head == NULL) {
+ queue->head = newlink;
+ }
+ queue->count++;
+ TRACE(("leave enqueue"))
+}
diff --git a/queue.h b/queue.h
new file mode 100644
index 0000000..80fbb9d
--- /dev/null
+++ b/queue.h
@@ -0,0 +1,49 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+#ifndef _QUEUE_H_
+#define _QUEUE_H_
+
+struct Link {
+
+ void* item;
+ struct Link* link;
+
+};
+
+struct Queue {
+
+ struct Link* head;
+ struct Link* tail;
+ unsigned int count; /* safety value */
+
+};
+
+void initqueue(struct Queue* queue);
+int isempty(struct Queue* queue);
+void* dequeue(struct Queue* queue);
+void *examine(struct Queue* queue);
+void enqueue(struct Queue* queue, void* item);
+
+#endif
diff --git a/random.c b/random.c
new file mode 100644
index 0000000..cbbe016
--- /dev/null
+++ b/random.c
@@ -0,0 +1,241 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+#include "includes.h"
+#include "buffer.h"
+#include "dbutil.h"
+#include "bignum.h"
+
+static int donerandinit = 0;
+
+/* this is used to generate unique output from the same hashpool */
+static uint32_t counter = 0;
+#define MAX_COUNTER 1<<31 /* the max value for the counter, so it won't loop */
+
+static unsigned char hashpool[SHA1_HASH_SIZE];
+
+#define INIT_SEED_SIZE 32 /* 256 bits */
+
+static void readrand(unsigned char* buf, unsigned int buflen);
+
+/* 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.
+ *
+ * It is important to ensure that counter doesn't wrap around before we
+ * feed in new entropy.
+ *
+ */
+
+static void readrand(unsigned char* buf, unsigned int buflen) {
+
+ static int already_blocked = 0;
+ int readfd;
+ unsigned int readpos;
+ int readlen;
+#ifdef DROPBEAR_PRNGD_SOCKET
+ struct sockaddr_un egdsock;
+ char egdcmd[2];
+#endif
+
+#ifdef DROPBEAR_RANDOM_DEV
+ readfd = open(DROPBEAR_RANDOM_DEV, O_RDONLY);
+ if (readfd < 0) {
+ dropbear_exit("couldn't open random device");
+ }
+#endif
+
+#ifdef DROPBEAR_PRNGD_SOCKET
+ memset((void*)&egdsock, 0x0, sizeof(egdsock));
+ egdsock.sun_family = AF_UNIX;
+ strlcpy(egdsock.sun_path, DROPBEAR_PRNGD_SOCKET,
+ sizeof(egdsock.sun_path));
+
+ readfd = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (readfd < 0) {
+ dropbear_exit("couldn't open random device");
+ }
+ /* todo - try various common locations */
+ if (connect(readfd, (struct sockaddr*)&egdsock,
+ sizeof(struct sockaddr_un)) < 0) {
+ dropbear_exit("couldn't open random device");
+ }
+
+ if (buflen > 255)
+ dropbear_exit("can't request more than 255 bytes from egd");
+ egdcmd[0] = 0x02; /* blocking read */
+ egdcmd[1] = (unsigned char)buflen;
+ if (write(readfd, egdcmd, 2) < 0)
+ dropbear_exit("can't send command to egd");
+#endif
+
+ /* read the actual random data */
+ readpos = 0;
+ do {
+ if (!already_blocked)
+ {
+ int ret;
+ struct timeval timeout;
+ fd_set read_fds;
+
+ timeout.tv_sec = 2; /* two seconds should be enough */
+ timeout.tv_usec = 0;
+
+ FD_ZERO(&read_fds);
+ FD_SET(readfd, &read_fds);
+ ret = select(readfd + 1, &read_fds, NULL, NULL, &timeout);
+ if (ret == 0)
+ {
+ dropbear_log(LOG_INFO, "Warning: Reading the random source seems to have blocked.\nIf you experience problems, you probably need to find a better entropy source.");
+ already_blocked = 1;
+ }
+ }
+ readlen = read(readfd, &buf[readpos], buflen - readpos);
+ if (readlen <= 0) {
+ if (readlen < 0 && errno == EINTR) {
+ continue;
+ }
+ dropbear_exit("error reading random source");
+ }
+ readpos += readlen;
+ } while (readpos < buflen);
+
+ close (readfd);
+}
+
+/* initialise the prng from /dev/(u)random or prngd */
+void seedrandom() {
+
+ unsigned char readbuf[INIT_SEED_SIZE];
+
+ hash_state hs;
+
+ /* initialise so that things won't warn about
+ * hashing an undefined buffer */
+ if (!donerandinit) {
+ m_burn(hashpool, sizeof(hashpool));
+ }
+
+ /* get the seed data */
+ readrand(readbuf, sizeof(readbuf));
+
+ /* hash in the new seed data */
+ sha1_init(&hs);
+ sha1_process(&hs, (void*)hashpool, sizeof(hashpool));
+ sha1_process(&hs, (void*)readbuf, sizeof(readbuf));
+ sha1_done(&hs, hashpool);
+
+ counter = 0;
+ donerandinit = 1;
+}
+
+/* hash the current random pool with some unique identifiers
+ * for this process and point-in-time. this is used to separate
+ * the random pools for fork()ed processes. */
+void reseedrandom() {
+
+ pid_t pid;
+ struct timeval tv;
+
+ if (!donerandinit) {
+ dropbear_exit("seedrandom not done");
+ }
+
+ pid = getpid();
+ gettimeofday(&tv, NULL);
+
+ hash_state hs;
+ unsigned char hash[SHA1_HASH_SIZE];
+ sha1_init(&hs);
+ sha1_process(&hs, (void*)hashpool, sizeof(hashpool));
+ sha1_process(&hs, (void*)&pid, sizeof(pid));
+ sha1_process(&hs, (void*)&tv, sizeof(tv));
+ sha1_done(&hs, hashpool);
+}
+
+/* return len bytes of pseudo-random data */
+void genrandom(unsigned char* buf, unsigned int len) {
+
+ hash_state hs;
+ unsigned char hash[SHA1_HASH_SIZE];
+ unsigned int copylen;
+
+ if (!donerandinit) {
+ dropbear_exit("seedrandom not done");
+ }
+
+ while (len > 0) {
+ sha1_init(&hs);
+ sha1_process(&hs, (void*)hashpool, sizeof(hashpool));
+ sha1_process(&hs, (void*)&counter, sizeof(counter));
+ sha1_done(&hs, hash);
+
+ counter++;
+ if (counter > MAX_COUNTER) {
+ seedrandom();
+ }
+
+ copylen = MIN(len, SHA1_HASH_SIZE);
+ memcpy(buf, hash, copylen);
+ len -= copylen;
+ buf += copylen;
+ }
+ m_burn(hash, sizeof(hash));
+}
+
+/* Generates a random mp_int.
+ * max is a *mp_int specifying an upper bound.
+ * rand must be an initialised *mp_int for the result.
+ * the result rand satisfies: 0 < rand < max
+ * */
+void gen_random_mpint(mp_int *max, mp_int *rand) {
+
+ unsigned char *randbuf = NULL;
+ unsigned int len = 0;
+ const char masks[] = {0xff, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f};
+
+ const int size_bits = mp_count_bits(max);
+
+ len = size_bits / 8;
+ if ((size_bits % 8) != 0) {
+ len += 1;
+ }
+
+ randbuf = (unsigned char*)m_malloc(len);
+ do {
+ genrandom(randbuf, len);
+ /* Mask out the unrequired bits - mp_read_unsigned_bin expects
+ * MSB first.*/
+ randbuf[0] &= masks[size_bits % 8];
+
+ bytes_to_mp(rand, randbuf, len);
+
+ /* keep regenerating until we get one satisfying
+ * 0 < rand < max */
+ } while ( ( (max != NULL) && (mp_cmp(rand, max) != MP_LT) )
+ || (mp_cmp_d(rand, 0) != MP_GT) );
+ m_burn(randbuf, len);
+ m_free(randbuf);
+}
diff --git a/random.h b/random.h
new file mode 100644
index 0000000..84a0a39
--- /dev/null
+++ b/random.h
@@ -0,0 +1,36 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+#ifndef _RANDOM_H_
+#define _RANDOM_H_
+
+struct mp_int;
+
+void seedrandom();
+void reseedrandom();
+void genrandom(unsigned char* buf, int len);
+void addrandom(unsigned char* buf, int len);
+void gen_random_mpint(mp_int *max, mp_int *rand);
+
+#endif /* _RANDOM_H_ */
diff --git a/rsa.c b/rsa.c
new file mode 100644
index 0000000..005e4ca
--- /dev/null
+++ b/rsa.c
@@ -0,0 +1,398 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+/* Perform RSA operations on data, including reading keys, signing and
+ * verification.
+ *
+ * The format is specified in rfc2437, Applied Cryptography or The Handbook of
+ * Applied Cryptography detail the general algorithm. */
+
+#include "includes.h"
+#include "dbutil.h"
+#include "bignum.h"
+#include "rsa.h"
+#include "buffer.h"
+#include "ssh.h"
+#include "random.h"
+
+#ifdef DROPBEAR_RSA
+
+static void rsa_pad_em(rsa_key * key,
+ const unsigned char * data, unsigned int len,
+ mp_int * rsa_em);
+
+/* Load a public rsa key from a buffer, initialising the values.
+ * The key will have the same format as buf_put_rsa_key.
+ * These should be freed with rsa_key_free.
+ * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
+int buf_get_rsa_pub_key(buffer* buf, rsa_key *key) {
+
+ TRACE(("enter buf_get_rsa_pub_key"))
+ dropbear_assert(key != NULL);
+ key->e = m_malloc(sizeof(mp_int));
+ key->n = m_malloc(sizeof(mp_int));
+ m_mp_init_multi(key->e, key->n, NULL);
+ key->d = NULL;
+ key->p = NULL;
+ key->q = NULL;
+
+ buf_incrpos(buf, 4+SSH_SIGNKEY_RSA_LEN); /* int + "ssh-rsa" */
+
+ if (buf_getmpint(buf, key->e) == DROPBEAR_FAILURE
+ || buf_getmpint(buf, key->n) == DROPBEAR_FAILURE) {
+ TRACE(("leave buf_get_rsa_pub_key: failure"))
+ return DROPBEAR_FAILURE;
+ }
+
+ if (mp_count_bits(key->n) < MIN_RSA_KEYLEN) {
+ dropbear_log(LOG_WARNING, "rsa key too short");
+ return DROPBEAR_FAILURE;
+ }
+
+ TRACE(("leave buf_get_rsa_pub_key: success"))
+ return DROPBEAR_SUCCESS;
+
+}
+
+/* Same as buf_get_rsa_pub_key, but reads a private "x" key at the end.
+ * Loads a private rsa key from a buffer
+ * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
+int buf_get_rsa_priv_key(buffer* buf, rsa_key *key) {
+
+ dropbear_assert(key != NULL);
+
+ 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"))
+ 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"))
+ return DROPBEAR_FAILURE;
+ }
+
+ /* old Dropbear private keys didn't keep p and q, so we will ignore them*/
+ if (buf->pos == buf->len) {
+ key->p = NULL;
+ key->q = NULL;
+ } else {
+ key->p = m_malloc(sizeof(mp_int));
+ key->q = m_malloc(sizeof(mp_int));
+ 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"))
+ return DROPBEAR_FAILURE;
+ }
+
+ if (buf_getmpint(buf, key->q) == DROPBEAR_FAILURE) {
+ TRACE(("leave buf_get_rsa_priv_key: q: ret == DROPBEAR_FAILURE"))
+ return DROPBEAR_FAILURE;
+ }
+ }
+
+ TRACE(("leave buf_get_rsa_priv_key"))
+ return DROPBEAR_SUCCESS;
+}
+
+
+/* Clear and free the memory used by a public or private key */
+void rsa_key_free(rsa_key *key) {
+
+ TRACE(("enter rsa_key_free"))
+
+ if (key == NULL) {
+ TRACE(("leave rsa_key_free: key == NULL"))
+ return;
+ }
+ if (key->d) {
+ mp_clear(key->d);
+ m_free(key->d);
+ }
+ if (key->e) {
+ mp_clear(key->e);
+ m_free(key->e);
+ }
+ if (key->n) {
+ mp_clear(key->n);
+ m_free(key->n);
+ }
+ if (key->p) {
+ mp_clear(key->p);
+ m_free(key->p);
+ }
+ if (key->q) {
+ mp_clear(key->q);
+ m_free(key->q);
+ }
+ m_free(key);
+ TRACE(("leave rsa_key_free"))
+}
+
+/* Put the public rsa key into the buffer in the required format:
+ *
+ * string "ssh-rsa"
+ * mp_int e
+ * mp_int n
+ */
+void buf_put_rsa_pub_key(buffer* buf, rsa_key *key) {
+
+ TRACE(("enter buf_put_rsa_pub_key"))
+ dropbear_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"))
+
+}
+
+/* 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"))
+
+ dropbear_assert(key != NULL);
+ buf_put_rsa_pub_key(buf, key);
+ buf_putmpint(buf, key->d);
+
+ /* new versions have p and q, old versions don't */
+ if (key->p) {
+ buf_putmpint(buf, key->p);
+ }
+ if (key->q) {
+ buf_putmpint(buf, key->q);
+ }
+
+
+ TRACE(("leave buf_put_rsa_priv_key"))
+
+}
+
+#ifdef DROPBEAR_SIGNKEY_VERIFY
+/* Verify a signature in buf, made on data by the key given.
+ * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
+int buf_rsa_verify(buffer * buf, rsa_key *key, const unsigned char* data,
+ unsigned int len) {
+
+ unsigned int slen;
+ DEF_MP_INT(rsa_s);
+ DEF_MP_INT(rsa_mdash);
+ DEF_MP_INT(rsa_em);
+ int ret = DROPBEAR_FAILURE;
+
+ TRACE(("enter buf_rsa_verify"))
+
+ dropbear_assert(key != NULL);
+
+ m_mp_init_multi(&rsa_mdash, &rsa_s, &rsa_em, NULL);
+
+ slen = buf_getint(buf);
+ if (slen != (unsigned int)mp_unsigned_bin_size(key->n)) {
+ 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"))
+ goto out;
+ }
+
+ /* check that s <= n-1 */
+ if (mp_cmp(&rsa_s, key->n) != MP_LT) {
+ TRACE(("s > n-1"))
+ goto out;
+ }
+
+ /* create the magic PKCS padded value */
+ rsa_pad_em(key, data, len, &rsa_em);
+
+ if (mp_exptmod(&rsa_s, key->e, key->n, &rsa_mdash) != MP_OKAY) {
+ TRACE(("failed exptmod rsa_s"))
+ goto out;
+ }
+
+ if (mp_cmp(&rsa_em, &rsa_mdash) == MP_EQ) {
+ /* signature is valid */
+ TRACE(("success!"))
+ ret = DROPBEAR_SUCCESS;
+ }
+
+out:
+ mp_clear_multi(&rsa_mdash, &rsa_s, &rsa_em, NULL);
+ TRACE(("leave buf_rsa_verify: ret %d", ret))
+ return ret;
+}
+
+#endif /* DROPBEAR_SIGNKEY_VERIFY */
+
+/* Sign the data presented with key, writing the signature contents
+ * to the buffer */
+void buf_put_rsa_sign(buffer* buf, rsa_key *key, const unsigned char* data,
+ unsigned int len) {
+
+ unsigned int nsize, ssize;
+ unsigned int i;
+ DEF_MP_INT(rsa_s);
+ DEF_MP_INT(rsa_tmp1);
+ DEF_MP_INT(rsa_tmp2);
+ DEF_MP_INT(rsa_tmp3);
+
+ TRACE(("enter buf_put_rsa_sign"))
+ dropbear_assert(key != NULL);
+
+ m_mp_init_multi(&rsa_s, &rsa_tmp1, &rsa_tmp2, &rsa_tmp3, NULL);
+
+ rsa_pad_em(key, data, len, &rsa_tmp1);
+
+ /* the actual signing of the padded data */
+
+#ifdef RSA_BLINDING
+
+ /* With blinding, s = (r^(-1))((em)*r^e)^d mod n */
+
+ /* generate the r blinding value */
+ /* rsa_tmp2 is r */
+ gen_random_mpint(key->n, &rsa_tmp2);
+
+ /* rsa_tmp1 is em */
+ /* em' = em * r^e mod n */
+
+ mp_exptmod(&rsa_tmp2, key->e, key->n, &rsa_s); /* rsa_s used as a temp var*/
+ mp_invmod(&rsa_tmp2, key->n, &rsa_tmp3);
+ mp_mulmod(&rsa_tmp1, &rsa_s, key->n, &rsa_tmp2);
+
+ /* rsa_tmp2 is em' */
+ /* s' = (em')^d mod n */
+ mp_exptmod(&rsa_tmp2, key->d, key->n, &rsa_tmp1);
+
+ /* rsa_tmp1 is s' */
+ /* rsa_tmp3 is r^(-1) mod n */
+ /* s = (s')r^(-1) mod n */
+ mp_mulmod(&rsa_tmp1, &rsa_tmp3, key->n, &rsa_s);
+
+#else
+
+ /* s = em^d mod n */
+ /* rsa_tmp1 is em */
+ if (mp_exptmod(&rsa_tmp1, key->d, key->n, &rsa_s) != MP_OKAY) {
+ dropbear_exit("rsa error");
+ }
+
+#endif /* RSA_BLINDING */
+
+ mp_clear_multi(&rsa_tmp1, &rsa_tmp2, &rsa_tmp3, NULL);
+
+ /* create the signature to return */
+ buf_putstring(buf, SSH_SIGNKEY_RSA, SSH_SIGNKEY_RSA_LEN);
+
+ nsize = mp_unsigned_bin_size(key->n);
+
+ /* string rsa_signature_blob length */
+ buf_putint(buf, nsize);
+ /* pad out s to same length as n */
+ ssize = mp_unsigned_bin_size(&rsa_s);
+ dropbear_assert(ssize <= nsize);
+ for (i = 0; i < nsize-ssize; i++) {
+ buf_putbyte(buf, 0x00);
+ }
+
+ if (mp_to_unsigned_bin(&rsa_s, buf_getwriteptr(buf, ssize)) != MP_OKAY) {
+ dropbear_exit("rsa error");
+ }
+ buf_incrwritepos(buf, ssize);
+ mp_clear(&rsa_s);
+
+#if defined(DEBUG_RSA) && defined(DEBUG_TRACE)
+ printhex("RSA sig", buf->data, buf->len);
+#endif
+
+
+ TRACE(("leave buf_put_rsa_sign"))
+}
+
+/* Creates the message value as expected by PKCS, see rfc2437 etc */
+/* format to be padded to is:
+ * EM = 01 | FF* | 00 | prefix | hash
+ *
+ * where FF is repeated enough times to make EM one byte
+ * shorter than the size of key->n
+ *
+ * prefix is the ASN1 designator prefix,
+ * hex 30 21 30 09 06 05 2B 0E 03 02 1A 05 00 04 14
+ *
+ * rsa_em must be a pointer to an initialised mp_int.
+ */
+static void rsa_pad_em(rsa_key * key,
+ const unsigned char * data, unsigned int len,
+ mp_int * rsa_em) {
+
+ /* ASN1 designator (including the 0x00 preceding) */
+ const unsigned char rsa_asn1_magic[] =
+ {0x00, 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b,
+ 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14};
+ const unsigned int RSA_ASN1_MAGIC_LEN = 16;
+
+ buffer * rsa_EM = NULL;
+ hash_state hs;
+ unsigned int nsize;
+
+ dropbear_assert(key != NULL);
+ dropbear_assert(data != NULL);
+ nsize = mp_unsigned_bin_size(key->n);
+
+ rsa_EM = buf_new(nsize-1);
+ /* type byte */
+ buf_putbyte(rsa_EM, 0x01);
+ /* Padding with 0xFF bytes */
+ while(rsa_EM->pos != rsa_EM->size - RSA_ASN1_MAGIC_LEN - SHA1_HASH_SIZE) {
+ buf_putbyte(rsa_EM, 0xff);
+ }
+ /* Magic ASN1 stuff */
+ memcpy(buf_getwriteptr(rsa_EM, RSA_ASN1_MAGIC_LEN),
+ rsa_asn1_magic, RSA_ASN1_MAGIC_LEN);
+ buf_incrwritepos(rsa_EM, RSA_ASN1_MAGIC_LEN);
+
+ /* The hash of the data */
+ sha1_init(&hs);
+ sha1_process(&hs, data, len);
+ sha1_done(&hs, buf_getwriteptr(rsa_EM, SHA1_HASH_SIZE));
+ buf_incrwritepos(rsa_EM, SHA1_HASH_SIZE);
+
+ dropbear_assert(rsa_EM->pos == rsa_EM->size);
+
+ /* Create the mp_int from the encoded bytes */
+ buf_setpos(rsa_EM, 0);
+ bytes_to_mp(rsa_em, buf_getptr(rsa_EM, rsa_EM->size),
+ rsa_EM->size);
+ buf_free(rsa_EM);
+}
+
+#endif /* DROPBEAR_RSA */
diff --git a/rsa.h b/rsa.h
new file mode 100644
index 0000000..545ba9b
--- /dev/null
+++ b/rsa.h
@@ -0,0 +1,61 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+#ifndef _RSA_H_
+#define _RSA_H_
+
+#include "includes.h"
+#include "buffer.h"
+
+#ifdef DROPBEAR_RSA
+
+#define RSA_SIGNATURE_SIZE 4+7+4+40
+
+struct RSA_key {
+
+ mp_int* n;
+ mp_int* e;
+ mp_int* d;
+ mp_int* p;
+ mp_int* q;
+
+};
+
+typedef struct RSA_key rsa_key;
+
+void buf_put_rsa_sign(buffer* buf, rsa_key *key, const unsigned char* data,
+ unsigned int len);
+#ifdef DROPBEAR_SIGNKEY_VERIFY
+int buf_rsa_verify(buffer * buf, rsa_key *key, const unsigned char* data,
+ unsigned int len);
+#endif
+int buf_get_rsa_pub_key(buffer* buf, rsa_key *key);
+int buf_get_rsa_priv_key(buffer* buf, rsa_key *key);
+void buf_put_rsa_pub_key(buffer* buf, rsa_key *key);
+void buf_put_rsa_priv_key(buffer* buf, rsa_key *key);
+void rsa_key_free(rsa_key *key);
+
+#endif /* DROPBEAR_RSA */
+
+#endif /* _RSA_H_ */
diff --git a/runopts.h b/runopts.h
new file mode 100644
index 0000000..5107a9d
--- /dev/null
+++ b/runopts.h
@@ -0,0 +1,119 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+#ifndef _RUNOPTS_H_
+#define _RUNOPTS_H_
+
+#include "includes.h"
+#include "signkey.h"
+#include "buffer.h"
+#include "auth.h"
+#include "tcpfwd.h"
+
+typedef struct runopts {
+
+#if defined(ENABLE_SVR_REMOTETCPFWD) || defined(ENABLE_CLI_LOCALTCPFWD)
+ int listen_fwd_all;
+#endif
+
+} runopts;
+
+extern runopts opts;
+
+int readhostkey(const char * filename, sign_key * hostkey, int *type);
+
+typedef struct svr_runopts {
+
+ char * rsakeyfile;
+ char * dsskeyfile;
+ char * bannerfile;
+
+ int forkbg;
+ int usingsyslog;
+
+ /* ports is an array of the portcount listening ports */
+ char *ports[DROPBEAR_MAX_PORTS];
+ unsigned int portcount;
+
+ int inetdmode;
+
+ /* Flags indicating whether to use ipv4 and ipv6 */
+ /* not used yet
+ int ipv4;
+ int ipv6;
+ */
+
+#ifdef DO_MOTD
+ /* whether to print the MOTD */
+ int domotd;
+#endif
+
+ int norootlogin;
+
+ int noauthpass;
+ int norootpass;
+
+#ifdef ENABLE_SVR_REMOTETCPFWD
+ int noremotetcp;
+#endif
+#ifdef ENABLE_SVR_LOCALTCPFWD
+ int nolocaltcp;
+#endif
+
+ sign_key *hostkey;
+ buffer * banner;
+
+} svr_runopts;
+
+extern svr_runopts svr_opts;
+
+void svr_getopts(int argc, char ** argv);
+void loadhostkeys();
+
+typedef struct cli_runopts {
+
+ char *progname;
+ char *remotehost;
+ char *remoteport;
+
+ char *username;
+
+ char *cmd;
+ int wantpty;
+#ifdef ENABLE_CLI_PUBKEY_AUTH
+ struct SignKeyList *privkeys; /* Keys to use for public-key auth */
+#endif
+#ifdef ENABLE_CLI_REMOTETCPFWD
+ struct TCPFwdList * remotefwds;
+#endif
+#ifdef ENABLE_CLI_LOCALTCPFWD
+ struct TCPFwdList * localfwds;
+#endif
+
+} cli_runopts;
+
+extern cli_runopts cli_opts;
+void cli_getopts(int argc, char ** argv);
+
+#endif /* _RUNOPTS_H_ */
diff --git a/scp.c b/scp.c
new file mode 100644
index 0000000..ffc4deb
--- /dev/null
+++ b/scp.c
@@ -0,0 +1,1199 @@
+/*
+ * scp - secure remote copy. This is basically patched BSD rcp which
+ * uses ssh to do the data transfer (instead of using rcmd).
+ *
+ * NOTE: This version should NOT be suid root. (This uses ssh to
+ * do the transfer and ssh has the necessary privileges.)
+ *
+ * 1995 Timo Rinne <tri@iki.fi>, Tatu Ylonen <ylo@cs.hut.fi>
+ *
+ * As far as I am concerned, the code I have written for this software
+ * can be used freely for any purpose. Any derived versions of this
+ * software must be clearly marked as such, and if the derived work is
+ * incompatible with the protocol description in the RFC file, it must be
+ * called by a name other than "ssh" or "Secure Shell".
+ */
+/*
+ * Copyright (c) 1999 Theo de Raadt. All rights reserved.
+ * Copyright (c) 1999 Aaron Campbell. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Parts from:
+ *
+ * Copyright (c) 1983, 1990, 1992, 1993, 1995
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "includes.h"
+#include "atomicio.h"
+#include "compat.h"
+#include "scpmisc.h"
+#include "progressmeter.h"
+
+#define _PATH_CP "/bin/cp"
+
+#ifndef TIMEVAL_TO_TIMESPEC
+#define TIMEVAL_TO_TIMESPEC(tv, ts) { \
+ (ts)->tv_sec = (tv)->tv_sec; \
+ (ts)->tv_nsec = (tv)->tv_usec * 1000; \
+}
+#endif
+
+#ifndef timersub
+#define timersub(tvp, uvp, vvp) \
+ do { \
+ (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \
+ (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \
+ if ((vvp)->tv_usec < 0) { \
+ (vvp)->tv_sec--; \
+ (vvp)->tv_usec += 1000000; \
+ } \
+ } while (/* CONSTCOND */ 0)
+#endif /* timersub */
+
+
+void bwlimit(int);
+
+/* Struct for addargs */
+arglist args;
+
+/* Bandwidth limit */
+off_t limitbw = 0;
+
+/* Name of current file being transferred. */
+char *curfile;
+
+/* This is set to non-zero to enable verbose mode. */
+int verbose_mode = 0;
+
+#ifdef PROGRESS_METER
+/* This is set to zero if the progressmeter is not desired. */
+int showprogress = 1;
+#endif
+
+/* This is the program to execute for the secured connection. ("ssh" or -S) */
+char *ssh_program = _PATH_SSH_PROGRAM;
+
+/* This is used to store the pid of ssh_program */
+pid_t do_cmd_pid = -1;
+
+static void
+killchild(int signo)
+{
+ if (do_cmd_pid > 1)
+ kill(do_cmd_pid, signo);
+
+ _exit(1);
+}
+
+/*
+ * This function executes the given command as the specified user on the
+ * given host. This returns < 0 if execution fails, and >= 0 otherwise. This
+ * assigns the input and output file descriptors on success.
+ */
+
+int
+do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, int argc)
+{
+ int pin[2], pout[2], reserved[2];
+
+ if (verbose_mode)
+ fprintf(stderr,
+ "Executing: program %s host %s, user %s, command %s\n",
+ ssh_program, host,
+ remuser ? remuser : "(unspecified)", cmd);
+
+ /*
+ * Reserve two descriptors so that the real pipes won't get
+ * descriptors 0 and 1 because that will screw up dup2 below.
+ */
+ pipe(reserved);
+
+ /* Create a socket pair for communicating with ssh. */
+ if (pipe(pin) < 0 || pipe(pout) < 0)
+ {
+ fprintf(stderr, "Fatal error: pipe: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ /* Free the reserved descriptors. */
+ close(reserved[0]);
+ close(reserved[1]);
+
+ // uClinux needs to build the args here before vforking,
+ // otherwise we do it later on.
+#ifdef __uClinux__
+ args.list[0] = ssh_program;
+ if (remuser != NULL)
+ addargs(&args, "-l%s", remuser);
+ addargs(&args, "%s", host);
+ addargs(&args, "%s", cmd);
+#endif /* __uClinux__ */
+
+ /* Fork a child to execute the command on the remote host using ssh. */
+#ifdef __uClinux__
+ do_cmd_pid = vfork();
+#else
+ do_cmd_pid = fork();
+#endif /* __uClinux__ */
+ if (do_cmd_pid == 0) {
+ /* Child. */
+ close(pin[1]);
+ close(pout[0]);
+ dup2(pin[0], 0);
+ dup2(pout[1], 1);
+ close(pin[0]);
+ close(pout[1]);
+
+#ifndef __uClinux__
+ args.list[0] = ssh_program;
+ if (remuser != NULL) {
+ addargs(&args, "-l");
+ addargs(&args, "%s", remuser);
+ }
+ addargs(&args, "%s", host);
+ addargs(&args, "%s", cmd);
+#endif
+
+ execvp(ssh_program, args.list);
+ perror(ssh_program);
+ exit(1);
+ } else if (do_cmd_pid == -1) {
+ fprintf(stderr, "Fatal error: fork: %s\n", strerror(errno));
+ exit(1);
+ }
+
+#ifdef __uClinux__
+ /* clean up command */
+ /* pop cmd */
+ free(args->list[--args->num]);
+ args->list[args->num]=NULL;
+ /* pop host */
+ free(args->list[--args->num-1]);
+ args->list[args->num]=NULL;
+ /* pop user */
+ if (remuser != NULL) {
+ free(args->list[--args->num-1]);
+ args->list[args->num]=NULL;
+ }
+#endif /* __uClinux__
+
+ /* Parent. Close the other side, and return the local side. */
+ close(pin[0]);
+ *fdout = pin[1];
+ close(pout[1]);
+ *fdin = pout[0];
+ signal(SIGTERM, killchild);
+ signal(SIGINT, killchild);
+ signal(SIGHUP, killchild);
+ return 0;
+}
+
+typedef struct {
+ int cnt;
+ char *buf;
+} BUF;
+
+BUF *allocbuf(BUF *, int, int);
+void lostconn(int);
+void nospace(void);
+int okname(char *);
+void run_err(const char *,...);
+void verifydir(char *);
+
+struct passwd *pwd;
+uid_t userid;
+int errs, remin, remout;
+int pflag, iamremote, iamrecursive, targetshouldbedirectory;
+
+#define CMDNEEDS 64
+char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */
+
+int response(void);
+void rsource(char *, struct stat *);
+void sink(int, char *[]);
+void source(int, char *[]);
+void tolocal(int, char *[]);
+void toremote(char *, int, char *[]);
+void usage(void);
+
+#if defined(DBMULTI_scp) || !defined(DROPBEAR_MULTI)
+#if defined(DBMULTI_scp) && defined(DROPBEAR_MULTI)
+int scp_main(int argc, char **argv)
+#else
+main(int argc, char **argv)
+#endif
+{
+ int ch, fflag, tflag, status;
+ double speed;
+ char *targ, *endp;
+ extern char *optarg;
+ extern int optind;
+
+ args.list = NULL;
+ addargs(&args, "ssh"); /* overwritten with ssh_program */
+ addargs(&args, "-x");
+ addargs(&args, "-oForwardAgent no");
+ addargs(&args, "-oClearAllForwardings yes");
+
+ fflag = tflag = 0;
+ while ((ch = getopt(argc, argv, "dfl:prtvBCc:i:P:q1246S:o:F:")) != -1)
+ switch (ch) {
+ /* User-visible flags. */
+ case '1':
+ case '2':
+ case '4':
+ case '6':
+ case 'C':
+ addargs(&args, "-%c", ch);
+ break;
+ case 'o':
+ case 'c':
+ case 'i':
+ case 'F':
+ addargs(&args, "-%c%s", ch, optarg);
+ break;
+ case 'P':
+ addargs(&args, "-p%s", optarg);
+ break;
+ case 'B':
+ addargs(&args, "-oBatchmode yes");
+ break;
+ case 'l':
+ speed = strtod(optarg, &endp);
+ if (speed <= 0 || *endp != '\0')
+ usage();
+ limitbw = speed * 1024;
+ break;
+ case 'p':
+ pflag = 1;
+ break;
+ case 'r':
+ iamrecursive = 1;
+ break;
+ case 'S':
+ ssh_program = xstrdup(optarg);
+ break;
+ case 'v':
+ addargs(&args, "-v");
+ verbose_mode = 1;
+ break;
+#ifdef PROGRESS_METER
+ case 'q':
+ showprogress = 0;
+ break;
+#endif
+
+ /* Server options. */
+ case 'd':
+ targetshouldbedirectory = 1;
+ break;
+ case 'f': /* "from" */
+ iamremote = 1;
+ fflag = 1;
+ break;
+ case 't': /* "to" */
+ iamremote = 1;
+ tflag = 1;
+#ifdef HAVE_CYGWIN
+ setmode(0, O_BINARY);
+#endif
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if ((pwd = getpwuid(userid = getuid())) == NULL) {
+ fprintf(stderr, "unknown user %u", (u_int) userid);
+ }
+
+#ifdef PROGRESS_METER
+ if (!isatty(STDERR_FILENO))
+ showprogress = 0;
+#endif
+
+ remin = STDIN_FILENO;
+ remout = STDOUT_FILENO;
+
+ if (fflag) {
+ /* Follow "protocol", send data. */
+ (void) response();
+ source(argc, argv);
+ exit(errs != 0);
+ }
+ if (tflag) {
+ /* Receive data. */
+ sink(argc, argv);
+ exit(errs != 0);
+ }
+ if (argc < 2)
+ usage();
+ if (argc > 2)
+ targetshouldbedirectory = 1;
+
+ remin = remout = -1;
+ do_cmd_pid = -1;
+ /* Command to be executed on remote system using "ssh". */
+ (void) snprintf(cmd, sizeof cmd, "scp%s%s%s%s",
+ verbose_mode ? " -v" : "",
+ iamrecursive ? " -r" : "", pflag ? " -p" : "",
+ targetshouldbedirectory ? " -d" : "");
+
+ (void) signal(SIGPIPE, lostconn);
+
+ if ((targ = colon(argv[argc - 1]))) /* Dest is remote host. */
+ toremote(targ, argc, argv);
+ else {
+ tolocal(argc, argv); /* Dest is local host. */
+ if (targetshouldbedirectory)
+ verifydir(argv[argc - 1]);
+ }
+ /*
+ * Finally check the exit status of the ssh process, if one was forked
+ * and no error has occured yet
+ */
+ if (do_cmd_pid != -1 && errs == 0) {
+ if (remin != -1)
+ (void) close(remin);
+ if (remout != -1)
+ (void) close(remout);
+ if (waitpid(do_cmd_pid, &status, 0) == -1)
+ errs = 1;
+ else {
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
+ errs = 1;
+ }
+ }
+ exit(errs != 0);
+}
+#endif /* DBMULTI stuff */
+
+void
+toremote(char *targ, int argc, char **argv)
+{
+ int i, len;
+ char *bp, *host, *src, *suser, *thost, *tuser;
+
+ *targ++ = 0;
+ if (*targ == 0)
+ targ = ".";
+
+ if ((thost = strrchr(argv[argc - 1], '@'))) {
+ /* user@host */
+ *thost++ = 0;
+ tuser = argv[argc - 1];
+ if (*tuser == '\0')
+ tuser = NULL;
+ } else {
+ thost = argv[argc - 1];
+ tuser = NULL;
+ }
+
+ for (i = 0; i < argc - 1; i++) {
+ src = colon(argv[i]);
+ if (src) { /* remote to remote */
+ static char *ssh_options =
+ "-x -o'ClearAllForwardings yes'";
+ *src++ = 0;
+ if (*src == 0)
+ src = ".";
+ host = strrchr(argv[i], '@');
+ len = strlen(ssh_program) + strlen(argv[i]) +
+ strlen(src) + (tuser ? strlen(tuser) : 0) +
+ strlen(thost) + strlen(targ) +
+ strlen(ssh_options) + CMDNEEDS + 20;
+ bp = xmalloc(len);
+ if (host) {
+ *host++ = 0;
+ host = cleanhostname(host);
+ suser = argv[i];
+ if (*suser == '\0')
+ suser = pwd->pw_name;
+ else if (!okname(suser)) {
+ xfree(bp);
+ continue;
+ }
+ if (tuser && !okname(tuser)) {
+ xfree(bp);
+ continue;
+ }
+ snprintf(bp, len,
+ "%s%s %s -n "
+ "-l %s %s %s %s '%s%s%s:%s'",
+ ssh_program, verbose_mode ? " -v" : "",
+ ssh_options, suser, host, cmd, src,
+ tuser ? tuser : "", tuser ? "@" : "",
+ thost, targ);
+ } else {
+ host = cleanhostname(argv[i]);
+ snprintf(bp, len,
+ "exec %s%s %s -n %s "
+ "%s %s '%s%s%s:%s'",
+ ssh_program, verbose_mode ? " -v" : "",
+ ssh_options, host, cmd, src,
+ tuser ? tuser : "", tuser ? "@" : "",
+ thost, targ);
+ }
+ if (verbose_mode)
+ fprintf(stderr, "Executing: %s\n", bp);
+ (void) system(bp);
+ (void) xfree(bp);
+ } else { /* local to remote */
+ if (remin == -1) {
+ len = strlen(targ) + CMDNEEDS + 20;
+ bp = xmalloc(len);
+ (void) snprintf(bp, len, "%s -t %s", cmd, targ);
+ host = cleanhostname(thost);
+ if (do_cmd(host, tuser, bp, &remin,
+ &remout, argc) < 0)
+ exit(1);
+ if (response() < 0)
+ exit(1);
+ (void) xfree(bp);
+ }
+ source(1, argv + i);
+ }
+ }
+}
+
+void
+tolocal(int argc, char **argv)
+{
+ int i, len;
+ char *bp, *host, *src, *suser;
+
+ for (i = 0; i < argc - 1; i++) {
+ if (!(src = colon(argv[i]))) { /* Local to local. */
+ len = strlen(_PATH_CP) + strlen(argv[i]) +
+ strlen(argv[argc - 1]) + 20;
+ bp = xmalloc(len);
+ (void) snprintf(bp, len, "exec %s%s%s %s %s", _PATH_CP,
+ iamrecursive ? " -r" : "", pflag ? " -p" : "",
+ argv[i], argv[argc - 1]);
+ if (verbose_mode)
+ fprintf(stderr, "Executing: %s\n", bp);
+ if (system(bp))
+ ++errs;
+ (void) xfree(bp);
+ continue;
+ }
+ *src++ = 0;
+ if (*src == 0)
+ src = ".";
+ if ((host = strrchr(argv[i], '@')) == NULL) {
+ host = argv[i];
+ suser = NULL;
+ } else {
+ *host++ = 0;
+ suser = argv[i];
+ if (*suser == '\0')
+ suser = pwd->pw_name;
+ }
+ host = cleanhostname(host);
+ len = strlen(src) + CMDNEEDS + 20;
+ bp = xmalloc(len);
+ (void) snprintf(bp, len, "%s -f %s", cmd, src);
+ if (do_cmd(host, suser, bp, &remin, &remout, argc) < 0) {
+ (void) xfree(bp);
+ ++errs;
+ continue;
+ }
+ xfree(bp);
+ sink(1, argv + argc - 1);
+ (void) close(remin);
+ remin = remout = -1;
+ }
+}
+
+void
+source(int argc, char **argv)
+{
+ struct stat stb;
+ static BUF buffer;
+ BUF *bp;
+ off_t i, amt, result, statbytes;
+ int fd, haderr, indx;
+ char *last, *name, buf[2048];
+ int len;
+
+ for (indx = 0; indx < argc; ++indx) {
+ name = argv[indx];
+ statbytes = 0;
+ len = strlen(name);
+ while (len > 1 && name[len-1] == '/')
+ name[--len] = '\0';
+ if (strchr(name, '\n') != NULL) {
+ run_err("%s: skipping, filename contains a newline",
+ name);
+ goto next;
+ }
+ if ((fd = open(name, O_RDONLY, 0)) < 0)
+ goto syserr;
+ if (fstat(fd, &stb) < 0) {
+syserr: run_err("%s: %s", name, strerror(errno));
+ goto next;
+ }
+ switch (stb.st_mode & S_IFMT) {
+ case S_IFREG:
+ break;
+ case S_IFDIR:
+ if (iamrecursive) {
+ rsource(name, &stb);
+ goto next;
+ }
+ /* FALLTHROUGH */
+ default:
+ run_err("%s: not a regular file", name);
+ goto next;
+ }
+ if ((last = strrchr(name, '/')) == NULL)
+ last = name;
+ else
+ ++last;
+ curfile = last;
+ if (pflag) {
+ /*
+ * Make it compatible with possible future
+ * versions expecting microseconds.
+ */
+ (void) snprintf(buf, sizeof buf, "T%lu 0 %lu 0\n",
+ (u_long) stb.st_mtime,
+ (u_long) stb.st_atime);
+ (void) atomicio(vwrite, remout, buf, strlen(buf));
+ if (response() < 0)
+ goto next;
+ }
+#define FILEMODEMASK (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO)
+ snprintf(buf, sizeof buf, "C%04o %lld %s\n",
+ (u_int) (stb.st_mode & FILEMODEMASK),
+ (int64_t)stb.st_size, last);
+ if (verbose_mode) {
+ fprintf(stderr, "Sending file modes: %s", buf);
+ }
+ (void) atomicio(vwrite, remout, buf, strlen(buf));
+ if (response() < 0)
+ goto next;
+ if ((bp = allocbuf(&buffer, fd, 2048)) == NULL) {
+next: (void) close(fd);
+ continue;
+ }
+#ifdef PROGRESS_METER
+ if (showprogress)
+ start_progress_meter(curfile, stb.st_size, &statbytes);
+#endif
+ /* Keep writing after an error so that we stay sync'd up. */
+ for (haderr = i = 0; i < stb.st_size; i += bp->cnt) {
+ amt = bp->cnt;
+ if (i + amt > stb.st_size)
+ amt = stb.st_size - i;
+ if (!haderr) {
+ result = atomicio(read, fd, bp->buf, amt);
+ if (result != amt)
+ haderr = result >= 0 ? EIO : errno;
+ }
+ if (haderr)
+ (void) atomicio(vwrite, remout, bp->buf, amt);
+ else {
+ result = atomicio(vwrite, remout, bp->buf, amt);
+ if (result != amt)
+ haderr = result >= 0 ? EIO : errno;
+ statbytes += result;
+ }
+ if (limitbw)
+ bwlimit(amt);
+ }
+#ifdef PROGRESS_METER
+ if (showprogress)
+ stop_progress_meter();
+#endif
+
+ if (close(fd) < 0 && !haderr)
+ haderr = errno;
+ if (!haderr)
+ (void) atomicio(vwrite, remout, "", 1);
+ else
+ run_err("%s: %s", name, strerror(haderr));
+ (void) response();
+ }
+}
+
+void
+rsource(char *name, struct stat *statp)
+{
+ DIR *dirp;
+ struct dirent *dp;
+ char *last, *vect[1], path[1100];
+
+ if (!(dirp = opendir(name))) {
+ run_err("%s: %s", name, strerror(errno));
+ return;
+ }
+ last = strrchr(name, '/');
+ if (last == 0)
+ last = name;
+ else
+ last++;
+ if (pflag) {
+ (void) snprintf(path, sizeof(path), "T%lu 0 %lu 0\n",
+ (u_long) statp->st_mtime,
+ (u_long) statp->st_atime);
+ (void) atomicio(vwrite, remout, path, strlen(path));
+ if (response() < 0) {
+ closedir(dirp);
+ return;
+ }
+ }
+ (void) snprintf(path, sizeof path, "D%04o %d %.1024s\n",
+ (u_int) (statp->st_mode & FILEMODEMASK), 0, last);
+ if (verbose_mode)
+ fprintf(stderr, "Entering directory: %s", path);
+ (void) atomicio(vwrite, remout, path, strlen(path));
+ if (response() < 0) {
+ closedir(dirp);
+ return;
+ }
+ while ((dp = readdir(dirp)) != NULL) {
+ if (dp->d_ino == 0)
+ continue;
+ if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
+ continue;
+ if (strlen(name) + 1 + strlen(dp->d_name) >= sizeof(path) - 1) {
+ run_err("%s/%s: name too long", name, dp->d_name);
+ continue;
+ }
+ (void) snprintf(path, sizeof path, "%s/%s", name, dp->d_name);
+ vect[0] = path;
+ source(1, vect);
+ }
+ (void) closedir(dirp);
+ (void) atomicio(vwrite, remout, "E\n", 2);
+ (void) response();
+}
+
+void
+bwlimit(int amount)
+{
+ static struct timeval bwstart, bwend;
+ static int lamt, thresh = 16384;
+ uint64_t wait;
+ struct timespec ts, rm;
+
+ if (!timerisset(&bwstart)) {
+ gettimeofday(&bwstart, NULL);
+ return;
+ }
+
+ lamt += amount;
+ if (lamt < thresh)
+ return;
+
+ gettimeofday(&bwend, NULL);
+ timersub(&bwend, &bwstart, &bwend);
+ if (!timerisset(&bwend))
+ return;
+
+ lamt *= 8;
+ wait = (double)1000000L * lamt / limitbw;
+
+ bwstart.tv_sec = wait / 1000000L;
+ bwstart.tv_usec = wait % 1000000L;
+
+ if (timercmp(&bwstart, &bwend, >)) {
+ timersub(&bwstart, &bwend, &bwend);
+
+ /* Adjust the wait time */
+ if (bwend.tv_sec) {
+ thresh /= 2;
+ if (thresh < 2048)
+ thresh = 2048;
+ } else if (bwend.tv_usec < 100) {
+ thresh *= 2;
+ if (thresh > 32768)
+ thresh = 32768;
+ }
+
+ TIMEVAL_TO_TIMESPEC(&bwend, &ts);
+ while (nanosleep(&ts, &rm) == -1) {
+ if (errno != EINTR)
+ break;
+ ts = rm;
+ }
+ }
+
+ lamt = 0;
+ gettimeofday(&bwstart, NULL);
+}
+
+void
+sink(int argc, char **argv)
+{
+ static BUF buffer;
+ struct stat stb;
+ enum {
+ YES, NO, DISPLAYED
+ } wrerr;
+ BUF *bp;
+ off_t i, j;
+ int amt, count, exists, first, mask, mode, ofd, omode;
+ off_t size, statbytes;
+ int setimes, targisdir, wrerrno = 0;
+ char ch, *cp, *np, *targ, *why, *vect[1], buf[2048];
+ struct timeval tv[2];
+
+#define atime tv[0]
+#define mtime tv[1]
+#define SCREWUP(str) do { why = str; goto screwup; } while (0)
+
+ setimes = targisdir = 0;
+ mask = umask(0);
+ if (!pflag)
+ (void) umask(mask);
+ if (argc != 1) {
+ run_err("ambiguous target");
+ exit(1);
+ }
+ targ = *argv;
+ if (targetshouldbedirectory)
+ verifydir(targ);
+
+ (void) atomicio(vwrite, remout, "", 1);
+ if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode))
+ targisdir = 1;
+ for (first = 1;; first = 0) {
+ cp = buf;
+ if (atomicio(read, remin, cp, 1) <= 0)
+ return;
+ if (*cp++ == '\n')
+ SCREWUP("unexpected <newline>");
+ do {
+ if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch))
+ SCREWUP("lost connection");
+ *cp++ = ch;
+ } while (cp < &buf[sizeof(buf) - 1] && ch != '\n');
+ *cp = 0;
+
+ if (buf[0] == '\01' || buf[0] == '\02') {
+ if (iamremote == 0)
+ (void) atomicio(vwrite, STDERR_FILENO,
+ buf + 1, strlen(buf + 1));
+ if (buf[0] == '\02')
+ exit(1);
+ ++errs;
+ continue;
+ }
+ if (buf[0] == 'E') {
+ (void) atomicio(vwrite, remout, "", 1);
+ return;
+ }
+ if (ch == '\n')
+ *--cp = 0;
+
+ cp = buf;
+ if (*cp == 'T') {
+ setimes++;
+ cp++;
+ mtime.tv_sec = strtol(cp, &cp, 10);
+ if (!cp || *cp++ != ' ')
+ SCREWUP("mtime.sec not delimited");
+ mtime.tv_usec = strtol(cp, &cp, 10);
+ if (!cp || *cp++ != ' ')
+ SCREWUP("mtime.usec not delimited");
+ atime.tv_sec = strtol(cp, &cp, 10);
+ if (!cp || *cp++ != ' ')
+ SCREWUP("atime.sec not delimited");
+ atime.tv_usec = strtol(cp, &cp, 10);
+ if (!cp || *cp++ != '\0')
+ SCREWUP("atime.usec not delimited");
+ (void) atomicio(vwrite, remout, "", 1);
+ continue;
+ }
+ if (*cp != 'C' && *cp != 'D') {
+ /*
+ * Check for the case "rcp remote:foo\* local:bar".
+ * In this case, the line "No match." can be returned
+ * by the shell before the rcp command on the remote is
+ * executed so the ^Aerror_message convention isn't
+ * followed.
+ */
+ if (first) {
+ run_err("%s", cp);
+ exit(1);
+ }
+ SCREWUP("expected control record");
+ }
+ mode = 0;
+ for (++cp; cp < buf + 5; cp++) {
+ if (*cp < '0' || *cp > '7')
+ SCREWUP("bad mode");
+ mode = (mode << 3) | (*cp - '0');
+ }
+ if (*cp++ != ' ')
+ SCREWUP("mode not delimited");
+
+ for (size = 0; isdigit(*cp);)
+ size = size * 10 + (*cp++ - '0');
+ if (*cp++ != ' ')
+ SCREWUP("size not delimited");
+ if (targisdir) {
+ static char *namebuf;
+ static int cursize;
+ size_t need;
+
+ need = strlen(targ) + strlen(cp) + 250;
+ if (need > cursize) {
+ if (namebuf)
+ xfree(namebuf);
+ namebuf = xmalloc(need);
+ cursize = need;
+ }
+ (void) snprintf(namebuf, need, "%s%s%s", targ,
+ strcmp(targ, "/") ? "/" : "", cp);
+ np = namebuf;
+ } else
+ np = targ;
+ curfile = cp;
+ exists = stat(np, &stb) == 0;
+ if (buf[0] == 'D') {
+ int mod_flag = pflag;
+ if (exists) {
+ if (!S_ISDIR(stb.st_mode)) {
+ errno = ENOTDIR;
+ goto bad;
+ }
+ if (pflag)
+ (void) chmod(np, mode);
+ } else {
+ /* Handle copying from a read-only
+ directory */
+ mod_flag = 1;
+ if (mkdir(np, mode | S_IRWXU) < 0)
+ goto bad;
+ }
+ vect[0] = xstrdup(np);
+ sink(1, vect);
+ if (setimes) {
+ setimes = 0;
+ if (utimes(vect[0], tv) < 0)
+ run_err("%s: set times: %s",
+ vect[0], strerror(errno));
+ }
+ if (mod_flag)
+ (void) chmod(vect[0], mode);
+ if (vect[0])
+ xfree(vect[0]);
+ continue;
+ }
+ omode = mode;
+ mode |= S_IWRITE;
+ if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) {
+bad: run_err("%s: %s", np, strerror(errno));
+ continue;
+ }
+ (void) atomicio(vwrite, remout, "", 1);
+ if ((bp = allocbuf(&buffer, ofd, 4096)) == NULL) {
+ (void) close(ofd);
+ continue;
+ }
+ cp = bp->buf;
+ wrerr = NO;
+
+ statbytes = 0;
+#ifdef PROGRESS_METER
+ if (showprogress)
+ start_progress_meter(curfile, size, &statbytes);
+#endif
+ for (count = i = 0; i < size; i += 4096) {
+ amt = 4096;
+ if (i + amt > size)
+ amt = size - i;
+ count += amt;
+ do {
+ j = read(remin, cp, amt);
+ if (j == -1 && (errno == EINTR ||
+ errno == EAGAIN)) {
+ continue;
+ } else if (j <= 0) {
+ run_err("%s", j ? strerror(errno) :
+ "dropped connection");
+ exit(1);
+ }
+ amt -= j;
+ cp += j;
+ statbytes += j;
+ } while (amt > 0);
+
+ if (limitbw)
+ bwlimit(4096);
+
+ if (count == bp->cnt) {
+ /* Keep reading so we stay sync'd up. */
+ if (wrerr == NO) {
+ j = atomicio(vwrite, ofd, bp->buf, count);
+ if (j != count) {
+ wrerr = YES;
+ wrerrno = j >= 0 ? EIO : errno;
+ }
+ }
+ count = 0;
+ cp = bp->buf;
+ }
+ }
+#ifdef PROGRESS_METER
+ if (showprogress)
+ stop_progress_meter();
+#endif
+ if (count != 0 && wrerr == NO &&
+ (j = atomicio(vwrite, ofd, bp->buf, count)) != count) {
+ wrerr = YES;
+ wrerrno = j >= 0 ? EIO : errno;
+ }
+ if (wrerr == NO && ftruncate(ofd, size) != 0) {
+ run_err("%s: truncate: %s", np, strerror(errno));
+ wrerr = DISPLAYED;
+ }
+ if (pflag) {
+ if (exists || omode != mode)
+#ifdef HAVE_FCHMOD
+ if (fchmod(ofd, omode))
+#else /* HAVE_FCHMOD */
+ if (chmod(np, omode))
+#endif /* HAVE_FCHMOD */
+ run_err("%s: set mode: %s",
+ np, strerror(errno));
+ } else {
+ if (!exists && omode != mode)
+#ifdef HAVE_FCHMOD
+ if (fchmod(ofd, omode & ~mask))
+#else /* HAVE_FCHMOD */
+ if (chmod(np, omode & ~mask))
+#endif /* HAVE_FCHMOD */
+ run_err("%s: set mode: %s",
+ np, strerror(errno));
+ }
+ if (close(ofd) == -1) {
+ wrerr = YES;
+ wrerrno = errno;
+ }
+ (void) response();
+ if (setimes && wrerr == NO) {
+ setimes = 0;
+ if (utimes(np, tv) < 0) {
+ run_err("%s: set times: %s",
+ np, strerror(errno));
+ wrerr = DISPLAYED;
+ }
+ }
+ switch (wrerr) {
+ case YES:
+ run_err("%s: %s", np, strerror(wrerrno));
+ break;
+ case NO:
+ (void) atomicio(vwrite, remout, "", 1);
+ break;
+ case DISPLAYED:
+ break;
+ }
+ }
+screwup:
+ run_err("protocol error: %s", why);
+ exit(1);
+}
+
+int
+response(void)
+{
+ char ch, *cp, resp, rbuf[2048];
+
+ if (atomicio(read, remin, &resp, sizeof(resp)) != sizeof(resp))
+ lostconn(0);
+
+ cp = rbuf;
+ switch (resp) {
+ case 0: /* ok */
+ return (0);
+ default:
+ *cp++ = resp;
+ /* FALLTHROUGH */
+ case 1: /* error, followed by error msg */
+ case 2: /* fatal error, "" */
+ do {
+ if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch))
+ lostconn(0);
+ *cp++ = ch;
+ } while (cp < &rbuf[sizeof(rbuf) - 1] && ch != '\n');
+
+ if (!iamremote)
+ (void) atomicio(vwrite, STDERR_FILENO, rbuf, cp - rbuf);
+ ++errs;
+ if (resp == 1)
+ return (-1);
+ exit(1);
+ }
+ /* NOTREACHED */
+}
+
+void
+usage(void)
+{
+ (void) fprintf(stderr,
+ "usage: scp [-pqrvBC1246] [-F config] [-S program] [-P port]\n"
+ " [-c cipher] [-i identity] [-l limit] [-o option]\n"
+ " [[user@]host1:]file1 [...] [[user@]host2:]file2\n");
+ exit(1);
+}
+
+void
+run_err(const char *fmt,...)
+{
+ static FILE *fp;
+ va_list ap;
+
+ ++errs;
+ if (fp == NULL && !(fp = fdopen(remout, "w")))
+ return;
+ (void) fprintf(fp, "%c", 0x01);
+ (void) fprintf(fp, "scp: ");
+ va_start(ap, fmt);
+ (void) vfprintf(fp, fmt, ap);
+ va_end(ap);
+ (void) fprintf(fp, "\n");
+ (void) fflush(fp);
+
+ if (!iamremote) {
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+ }
+}
+
+void
+verifydir(char *cp)
+{
+ struct stat stb;
+
+ if (!stat(cp, &stb)) {
+ if (S_ISDIR(stb.st_mode))
+ return;
+ errno = ENOTDIR;
+ }
+ run_err("%s: %s", cp, strerror(errno));
+ exit(1);
+}
+
+int
+okname(char *cp0)
+{
+ int c;
+ char *cp;
+
+ cp = cp0;
+ do {
+ c = (int)*cp;
+ if (c & 0200)
+ goto bad;
+ if (!isalpha(c) && !isdigit(c)) {
+ switch (c) {
+ case '\'':
+ case '"':
+ case '`':
+ case ' ':
+ case '#':
+ goto bad;
+ default:
+ break;
+ }
+ }
+ } while (*++cp);
+ return (1);
+
+bad: fprintf(stderr, "%s: invalid user name\n", cp0);
+ return (0);
+}
+
+BUF *
+allocbuf(BUF *bp, int fd, int blksize)
+{
+ size_t size;
+#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
+ struct stat stb;
+
+ if (fstat(fd, &stb) < 0) {
+ run_err("fstat: %s", strerror(errno));
+ return (0);
+ }
+ size = roundup(stb.st_blksize, blksize);
+ if (size == 0)
+ size = blksize;
+#else /* HAVE_STRUCT_STAT_ST_BLKSIZE */
+ size = blksize;
+#endif /* HAVE_STRUCT_STAT_ST_BLKSIZE */
+ if (bp->cnt >= size)
+ return (bp);
+ if (bp->buf == NULL)
+ bp->buf = xmalloc(size);
+ else
+ bp->buf = xrealloc(bp->buf, size);
+ memset(bp->buf, 0, size);
+ bp->cnt = size;
+ return (bp);
+}
+
+void
+lostconn(int signo)
+{
+ if (!iamremote)
+ write(STDERR_FILENO, "lost connection\n", 16);
+ if (signo)
+ _exit(1);
+ else
+ exit(1);
+}
diff --git a/scpmisc.c b/scpmisc.c
new file mode 100644
index 0000000..e15bf4d
--- /dev/null
+++ b/scpmisc.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2000 Markus Friedl. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*RCSID("OpenBSD: misc.c,v 1.22 2003/09/18 08:49:45 markus Exp ");*/
+
+/* For xmalloc, xfree etc:
+ * Author: Tatu Ylonen <ylo@cs.hut.fi>
+ * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+ * All rights reserved
+ * Versions of malloc and friends that check their results, and never return
+ * failure (they call fatal if they encounter an error).
+ *
+ * As far as I am concerned, the code I have written for this software
+ * can be used freely for any purpose. Any derived versions of this
+ * software must be clearly marked as such, and if the derived work is
+ * incompatible with the protocol description in the RFC file, it must be
+ * called by a name other than "ssh" or "Secure Shell".
+ */
+
+/*RCSID("OpenBSD: xmalloc.c,v 1.16 2001/07/23 18:21:46 stevesk Exp ");*/
+
+#include "includes.h"
+#include "scpmisc.h"
+
+void *
+xmalloc(size_t size)
+{
+ void *ptr;
+
+ if (size == 0) {
+ fprintf(stderr, "xmalloc: zero size\n");
+ exit(EXIT_FAILURE);
+ }
+ ptr = malloc(size);
+ if (ptr == NULL) {
+ fprintf(stderr, "xmalloc: out of memory (allocating %lu bytes)\n", (u_long) size);
+ exit(EXIT_FAILURE);
+ }
+ return ptr;
+}
+
+void *
+xrealloc(void *ptr, size_t new_size)
+{
+ void *new_ptr;
+
+ if (new_size == 0) {
+ fprintf(stderr, "xrealloc: zero size\n");
+ exit(EXIT_FAILURE);
+ }
+ if (ptr == NULL)
+ new_ptr = malloc(new_size);
+ else
+ new_ptr = realloc(ptr, new_size);
+ if (new_ptr == NULL) {
+ fprintf(stderr, "xrealloc: out of memory (new_size %lu bytes)\n", (u_long) new_size);
+ exit(EXIT_FAILURE);
+ }
+ return new_ptr;
+}
+
+void
+xfree(void *ptr)
+{
+ if (ptr == NULL) {
+ fprintf(stderr, "xfree: NULL pointer given as argument\n");
+ exit(EXIT_FAILURE);
+ }
+ free(ptr);
+}
+
+char *
+xstrdup(const char *str)
+{
+ size_t len;
+ char *cp;
+
+ len = strlen(str) + 1;
+ cp = xmalloc(len);
+ strncpy(cp, str, len);
+ return cp;
+}
+
+char *
+cleanhostname(char *host)
+{
+ if (*host == '[' && host[strlen(host) - 1] == ']') {
+ host[strlen(host) - 1] = '\0';
+ return (host + 1);
+ } else
+ return host;
+}
+
+char *
+colon(char *cp)
+{
+ int flag = 0;
+
+ if (*cp == ':') /* Leading colon is part of file name. */
+ return (0);
+ if (*cp == '[')
+ flag = 1;
+
+ for (; *cp; ++cp) {
+ if (*cp == '@' && *(cp+1) == '[')
+ flag = 1;
+ if (*cp == ']' && *(cp+1) == ':' && flag)
+ return (cp+1);
+ if (*cp == ':' && !flag)
+ return (cp);
+ if (*cp == '/')
+ return (0);
+ }
+ return (0);
+}
+
+/* function to assist building execv() arguments */
+void
+addargs(arglist *args, char *fmt, ...)
+{
+ va_list ap;
+ char buf[1024];
+ int nalloc;
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ nalloc = args->nalloc;
+ if (args->list == NULL) {
+ nalloc = 32;
+ args->num = 0;
+ } else if (args->num+2 >= nalloc)
+ nalloc *= 2;
+
+ args->list = xrealloc(args->list, nalloc * sizeof(char *));
+ args->nalloc = nalloc;
+ args->list[args->num++] = xstrdup(buf);
+ args->list[args->num] = NULL;
+}
diff --git a/scpmisc.h b/scpmisc.h
new file mode 100644
index 0000000..b6059c6
--- /dev/null
+++ b/scpmisc.h
@@ -0,0 +1,44 @@
+/* $OpenBSD: misc.h,v 1.12 2002/03/19 10:49:35 markus Exp $ */
+
+/*
+ * Author: Tatu Ylonen <ylo@cs.hut.fi>
+ * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+ * All rights reserved
+ *
+ * As far as I am concerned, the code I have written for this software
+ * can be used freely for any purpose. Any derived versions of this
+ * software must be clearly marked as such, and if the derived work is
+ * incompatible with the protocol description in the RFC file, it must be
+ * called by a name other than "ssh" or "Secure Shell".
+ */
+
+/* actually from atomicio, but is only used in scp code */
+#define vwrite (ssize_t (*)(int, void *, size_t))write
+
+char *chop(char *);
+char *strdelim(char **);
+void set_nonblock(int);
+void unset_nonblock(int);
+void set_nodelay(int);
+int a2port(const char *);
+char *cleanhostname(char *);
+char *colon(char *);
+long convtime(const char *);
+
+struct passwd *pwcopy(struct passwd *);
+
+typedef struct arglist arglist;
+struct arglist {
+ char **list;
+ int num;
+ int nalloc;
+};
+void addargs(arglist *, char *, ...);
+
+/* from xmalloc.h */
+void *xmalloc(size_t);
+void *xrealloc(void *, size_t);
+void xfree(void *);
+char *xstrdup(const char *);
+
+
diff --git a/service.h b/service.h
new file mode 100644
index 0000000..197d8d1
--- /dev/null
+++ b/service.h
@@ -0,0 +1,32 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+#ifndef _SERVICE_H_
+#define _SERVICE_H_
+
+void recv_msg_service_request(); /* Server */
+void send_msg_service_request(); /* Client */
+void recv_msg_service_accept(); /* Client */
+
+#endif /* _SERVICE_H_ */
diff --git a/session.h b/session.h
new file mode 100644
index 0000000..a4ad45f
--- /dev/null
+++ b/session.h
@@ -0,0 +1,257 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+#ifndef _SESSION_H_
+#define _SESSION_H_
+
+#include "includes.h"
+#include "options.h"
+#include "buffer.h"
+#include "signkey.h"
+#include "kex.h"
+#include "auth.h"
+#include "channel.h"
+#include "queue.h"
+#include "listener.h"
+#include "packet.h"
+#include "tcpfwd.h"
+#include "chansession.h"
+
+extern int sessinitdone; /* Is set to 0 somewhere */
+extern int exitflag;
+
+void common_session_init(int sock, char* remotehost);
+void session_loop(void(*loophandler)());
+void common_session_cleanup();
+void session_identification();
+
+
+/* Server */
+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_session_cleanup();
+void cleantext(unsigned char* dirtytext);
+
+struct key_context {
+
+ const struct dropbear_cipher *recv_algo_crypt; /* NULL for none */
+ const struct dropbear_cipher *trans_algo_crypt; /* NULL for none */
+ const struct dropbear_hash *recv_algo_mac; /* NULL for none */
+ const struct dropbear_hash *trans_algo_mac; /* NULL for none */
+ char algo_kex;
+ char algo_hostkey;
+
+ char recv_algo_comp; /* compression */
+ char trans_algo_comp;
+#ifndef DISABLE_ZLIB
+ z_streamp recv_zstream;
+ z_streamp trans_zstream;
+#endif
+
+ /* actual keys */
+ symmetric_CBC recv_symmetric_struct;
+ symmetric_CBC trans_symmetric_struct;
+ unsigned char recvmackey[MAX_MAC_KEY];
+ unsigned char transmackey[MAX_MAC_KEY];
+
+};
+
+struct sshsession {
+
+ /* Is it a client or server? */
+ unsigned char isserver;
+
+ long connecttimeout; /* time to disconnect if we have a timeout (for
+ userauth etc), or 0 for no timeout */
+
+ int sock;
+
+ unsigned char *remotehost; /* the peer hostname */
+
+ unsigned char *remoteident;
+
+ int maxfd; /* the maximum file descriptor to check with select() */
+
+
+ /* Packet buffers/values etc */
+ buffer *writepayload; /* Unencrypted payload to write - this is used
+ throughout the code, as handlers fill out this
+ buffer with the packet to send. */
+ struct Queue writequeue; /* A queue of encrypted packets to send */
+ buffer *readbuf; /* Encrypted */
+ buffer *decryptreadbuf; /* Post-decryption */
+ buffer *payload; /* Post-decompression, the actual SSH packet */
+ unsigned int transseq, recvseq; /* Sequence IDs */
+
+ /* Packet-handling flags */
+ const packettype * packettypes; /* Packet handler mappings for this
+ session, see process-packet.c */
+
+ unsigned dataallowed : 1; /* whether we can send data packets or we are in
+ the middle of a KEX or something */
+
+ unsigned char requirenext; /* byte indicating what packet we require next,
+ or 0x00 for any */
+
+ unsigned char ignorenext; /* whether to ignore the next packet,
+ used for kex_follows stuff */
+
+ unsigned char lastpacket; /* What the last received packet type was */
+
+
+
+ /* KEX/encryption related */
+ struct KEXState kexstate;
+ struct key_context *keys;
+ struct key_context *newkeys;
+ unsigned char *session_id; /* this is the hash from the first kex */
+ /* The below are used temorarily during kex, are freed after use */
+ mp_int * dh_K; /* SSH_MSG_KEXDH_REPLY and sending SSH_MSH_NEWKEYS */
+ unsigned char hash[SHA1_HASH_SIZE]; /* the hash*/
+ buffer* kexhashbuf; /* session hash buffer calculated from various packets*/
+ buffer* transkexinit; /* the kexinit packet we send should be kept so we
+ can add it to the hash when generating keys */
+
+ algo_type*(*buf_match_algo)(buffer*buf, algo_type localalgos[],
+ int *goodguess); /* The function to use to choose which algorithm
+ to use from the ones presented by the remote
+ side. Is specific to the client/server mode,
+ hence the function-pointer callback.*/
+
+ void(*remoteclosed)(); /* A callback to handle closure of the
+ remote connection */
+
+
+ struct AuthState authstate; /* Common amongst client and server, since most
+ struct elements are common */
+
+ /* Channel related */
+ struct Channel ** channels; /* these pointers may be null */
+ unsigned int chansize; /* the number of Channel*s allocated for channels */
+ unsigned int chancount; /* the number of Channel*s in use */
+ const struct ChanType **chantypes; /* The valid channel types */
+
+
+ /* TCP forwarding - where manage 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 */
+ int allowprivport;
+
+};
+
+struct serversession {
+
+ /* Server specific options */
+ int childpipe; /* kept open until we successfully authenticate */
+ /* userauth */
+
+ 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 {
+ KEX_NOTHING,
+ KEXINIT_RCVD,
+ KEXDH_INIT_SENT,
+ KEXDONE,
+
+} cli_kex_state;
+
+typedef enum {
+ STATE_NOTHING,
+ SERVICE_AUTH_REQ_SENT,
+ SERVICE_AUTH_ACCEPT_RCVD,
+ SERVICE_CONN_REQ_SENT,
+ SERVICE_CONN_ACCEPT_RCVD,
+ USERAUTH_REQ_SENT,
+ USERAUTH_FAIL_RCVD,
+ USERAUTH_SUCCESS_RCVD,
+ SESSION_RUNNING,
+
+} cli_state;
+
+struct clientsession {
+
+ mp_int *dh_e, *dh_x; /* Used during KEX */
+ cli_kex_state kex_state; /* Used for progressing KEX */
+ cli_state state; /* Used to progress auth/channelsession etc */
+ unsigned donefirstkex : 1; /* Set when we set sentnewkeys, never reset */
+
+ int tty_raw_mode; /* Whether we're in raw mode (and have to clean up) */
+ struct termios saved_tio;
+ int stdincopy;
+ int stdinflags;
+ int stdoutcopy;
+ int stdoutflags;
+ int stderrcopy;
+ int stderrflags;
+
+ 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 */
+#ifdef ENABLE_CLI_INTERACT_AUTH
+ int auth_interact_failed; /* flag whether interactive auth can still
+ be used */
+ int interact_request_received; /* flag whether we've received an
+ info request from the server for
+ interactive auth.*/
+#endif
+ struct SignKeyList *lastprivkey;
+
+ int retval; /* What the command exit status was - we emulate it */
+#if 0
+ TODO
+ struct AgentkeyList *agentkeys; /* Keys to use for public-key auth */
+#endif
+
+};
+
+/* Global structs storing the state */
+extern struct sshsession ses;
+
+#ifdef DROPBEAR_SERVER
+extern struct serversession svr_ses;
+#endif /* DROPBEAR_SERVER */
+
+#ifdef DROPBEAR_CLIENT
+extern struct clientsession cli_ses;
+#endif /* DROPBEAR_CLIENT */
+
+#endif /* _SESSION_H_ */
diff --git a/signkey.c b/signkey.c
new file mode 100644
index 0000000..b8afb58
--- /dev/null
+++ b/signkey.c
@@ -0,0 +1,484 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+#include "includes.h"
+#include "dbutil.h"
+#include "signkey.h"
+#include "buffer.h"
+#include "ssh.h"
+
+/* malloc a new sign_key and set the dss and rsa keys to NULL */
+sign_key * new_sign_key() {
+
+ sign_key * ret;
+
+ ret = (sign_key*)m_malloc(sizeof(sign_key));
+#ifdef DROPBEAR_DSS
+ ret->dsskey = NULL;
+#endif
+#ifdef DROPBEAR_RSA
+ ret->rsakey = NULL;
+#endif
+ return ret;
+
+}
+
+/* Returns "ssh-dss" or "ssh-rsa" corresponding to the type. Exits fatally
+ * if the type is invalid */
+const char* signkey_name_from_type(int type, int *namelen) {
+
+#ifdef DROPBEAR_RSA
+ if (type == DROPBEAR_SIGNKEY_RSA) {
+ *namelen = SSH_SIGNKEY_RSA_LEN;
+ return SSH_SIGNKEY_RSA;
+ }
+#endif
+#ifdef DROPBEAR_DSS
+ if (type == DROPBEAR_SIGNKEY_DSS) {
+ *namelen = SSH_SIGNKEY_DSS_LEN;
+ return SSH_SIGNKEY_DSS;
+ }
+#endif
+ dropbear_exit("bad key type %d", type);
+ return NULL; /* notreached */
+}
+
+/* Returns DROPBEAR_SIGNKEY_RSA, DROPBEAR_SIGNKEY_DSS,
+ * or DROPBEAR_SIGNKEY_NONE */
+int signkey_type_from_name(const char* name, int namelen) {
+
+#ifdef DROPBEAR_RSA
+ if (namelen == SSH_SIGNKEY_RSA_LEN
+ && memcmp(name, SSH_SIGNKEY_RSA, SSH_SIGNKEY_RSA_LEN) == 0) {
+ return DROPBEAR_SIGNKEY_RSA;
+ }
+#endif
+#ifdef DROPBEAR_DSS
+ if (namelen == SSH_SIGNKEY_DSS_LEN
+ && memcmp(name, SSH_SIGNKEY_DSS, SSH_SIGNKEY_DSS_LEN) == 0) {
+ return DROPBEAR_SIGNKEY_DSS;
+ }
+#endif
+
+ return DROPBEAR_SIGNKEY_NONE;
+}
+
+/* returns DROPBEAR_SUCCESS on success, DROPBEAR_FAILURE on fail.
+ * type should be set by the caller to specify the type to read, and
+ * on return is set to the type read (useful when type = _ANY) */
+int buf_get_pub_key(buffer *buf, sign_key *key, int *type) {
+
+ unsigned char* ident;
+ unsigned int len;
+ int keytype;
+ int ret = DROPBEAR_FAILURE;
+
+ TRACE(("enter buf_get_pub_key"))
+
+ ident = buf_getstring(buf, &len);
+ keytype = signkey_type_from_name(ident, len);
+ m_free(ident);
+
+ if (*type != DROPBEAR_SIGNKEY_ANY && *type != keytype) {
+ return DROPBEAR_FAILURE;
+ }
+
+ *type = keytype;
+
+ /* Rewind the buffer back before "ssh-rsa" etc */
+ buf_incrpos(buf, -len - 4);
+
+#ifdef DROPBEAR_DSS
+ if (keytype == DROPBEAR_SIGNKEY_DSS) {
+ dss_key_free(key->dsskey);
+ key->dsskey = (dss_key*)m_malloc(sizeof(dss_key));
+ ret = buf_get_dss_pub_key(buf, key->dsskey);
+ if (ret == DROPBEAR_FAILURE) {
+ m_free(key->dsskey);
+ }
+ }
+#endif
+#ifdef DROPBEAR_RSA
+ if (keytype == DROPBEAR_SIGNKEY_RSA) {
+ rsa_key_free(key->rsakey);
+ key->rsakey = (rsa_key*)m_malloc(sizeof(rsa_key));
+ ret = buf_get_rsa_pub_key(buf, key->rsakey);
+ if (ret == DROPBEAR_FAILURE) {
+ m_free(key->rsakey);
+ }
+ }
+#endif
+
+ TRACE(("leave buf_get_pub_key"))
+
+ return ret;
+
+}
+
+/* returns DROPBEAR_SUCCESS on success, DROPBEAR_FAILURE on fail.
+ * type should be set by the caller to specify the type to read, and
+ * on return is set to the type read (useful when type = _ANY) */
+int buf_get_priv_key(buffer *buf, sign_key *key, int *type) {
+
+ unsigned char* ident;
+ unsigned int len;
+ int keytype;
+ int ret = DROPBEAR_FAILURE;
+
+ 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;
+ }
+
+ *type = keytype;
+
+ /* Rewind the buffer back before "ssh-rsa" etc */
+ buf_incrpos(buf, -len - 4);
+
+#ifdef DROPBEAR_DSS
+ if (keytype == DROPBEAR_SIGNKEY_DSS) {
+ dss_key_free(key->dsskey);
+ key->dsskey = (dss_key*)m_malloc(sizeof(dss_key));
+ ret = buf_get_dss_priv_key(buf, key->dsskey);
+ if (ret == DROPBEAR_FAILURE) {
+ m_free(key->dsskey);
+ }
+ }
+#endif
+#ifdef DROPBEAR_RSA
+ if (keytype == DROPBEAR_SIGNKEY_RSA) {
+ rsa_key_free(key->rsakey);
+ key->rsakey = (rsa_key*)m_malloc(sizeof(rsa_key));
+ ret = buf_get_rsa_priv_key(buf, key->rsakey);
+ if (ret == DROPBEAR_FAILURE) {
+ m_free(key->rsakey);
+ }
+ }
+#endif
+
+ TRACE(("leave buf_get_priv_key"))
+
+ return ret;
+
+}
+
+/* type is either DROPBEAR_SIGNKEY_DSS or DROPBEAR_SIGNKEY_RSA */
+void buf_put_pub_key(buffer* buf, sign_key *key, int type) {
+
+ buffer *pubkeys;
+
+ TRACE(("enter buf_put_pub_key"))
+ pubkeys = buf_new(MAX_PUBKEY_SIZE);
+
+#ifdef DROPBEAR_DSS
+ if (type == DROPBEAR_SIGNKEY_DSS) {
+ buf_put_dss_pub_key(pubkeys, key->dsskey);
+ }
+#endif
+#ifdef DROPBEAR_RSA
+ if (type == DROPBEAR_SIGNKEY_RSA) {
+ buf_put_rsa_pub_key(pubkeys, key->rsakey);
+ }
+#endif
+ if (pubkeys->len == 0) {
+ dropbear_exit("bad key types in buf_put_pub_key");
+ }
+
+ buf_setpos(pubkeys, 0);
+ buf_putstring(buf, buf_getptr(pubkeys, pubkeys->len),
+ pubkeys->len);
+
+ buf_free(pubkeys);
+ 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))
+
+#ifdef DROPBEAR_DSS
+ if (type == DROPBEAR_SIGNKEY_DSS) {
+ buf_put_dss_priv_key(buf, key->dsskey);
+ 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"))
+ return;
+ }
+#endif
+ dropbear_exit("bad key types in put pub key");
+}
+
+void sign_key_free(sign_key *key) {
+
+ TRACE(("enter sign_key_free"))
+
+#ifdef DROPBEAR_DSS
+ dss_key_free(key->dsskey);
+ key->dsskey = NULL;
+#endif
+#ifdef DROPBEAR_RSA
+ rsa_key_free(key->rsakey);
+ key->rsakey = NULL;
+#endif
+
+ m_free(key);
+ TRACE(("leave sign_key_free"))
+}
+
+static char hexdig(unsigned char x) {
+
+ if (x > 0xf)
+ return 'X';
+
+ if (x < 10)
+ return '0' + x;
+ else
+ return 'a' + x - 10;
+}
+
+/* Since we're not sure if we'll have md5 or sha1, we present both.
+ * MD5 is used in preference, but sha1 could still be useful */
+#ifdef DROPBEAR_MD5_HMAC
+static char * sign_key_md5_fingerprint(unsigned char* keyblob,
+ unsigned int keybloblen) {
+
+ char * ret;
+ hash_state hs;
+ unsigned char hash[MD5_HASH_SIZE];
+ unsigned int i;
+ unsigned int buflen;
+
+ md5_init(&hs);
+
+ /* skip the size int of the string - this is a bit messy */
+ md5_process(&hs, keyblob, keybloblen);
+
+ md5_done(&hs, hash);
+
+ /* "md5 hexfingerprinthere\0", each hex digit is "AB:" etc */
+ buflen = 4 + 3*MD5_HASH_SIZE;
+ ret = (char*)m_malloc(buflen);
+
+ memset(ret, 'Z', buflen);
+ strcpy(ret, "md5 ");
+
+ for (i = 0; i < MD5_HASH_SIZE; i++) {
+ unsigned int pos = 4 + i*3;
+ ret[pos] = hexdig(hash[i] >> 4);
+ ret[pos+1] = hexdig(hash[i] & 0x0f);
+ ret[pos+2] = ':';
+ }
+ ret[buflen-1] = 0x0;
+
+ return ret;
+}
+
+#else /* use SHA1 rather than MD5 for fingerprint */
+static char * sign_key_sha1_fingerprint(unsigned char* keyblob,
+ unsigned int keybloblen) {
+
+ char * ret;
+ hash_state hs;
+ unsigned char hash[SHA1_HASH_SIZE];
+ unsigned int i;
+ unsigned int buflen;
+
+ sha1_init(&hs);
+
+ /* skip the size int of the string - this is a bit messy */
+ sha1_process(&hs, keyblob, keybloblen);
+
+ sha1_done(&hs, hash);
+
+ /* "sha1 hexfingerprinthere\0", each hex digit is "AB:" etc */
+ buflen = 5 + 3*SHA1_HASH_SIZE;
+ ret = (char*)m_malloc(buflen);
+
+ strcpy(ret, "sha1 ");
+
+ for (i = 0; i < SHA1_HASH_SIZE; i++) {
+ unsigned int pos = 5 + 3*i;
+ ret[pos] = hexdig(hash[i] >> 4);
+ ret[pos+1] = hexdig(hash[i] & 0x0f);
+ ret[pos+2] = ':';
+ }
+ ret[buflen-1] = 0x0;
+
+ return ret;
+}
+
+#endif /* MD5/SHA1 switch */
+
+/* This will return a freshly malloced string, containing a fingerprint
+ * in either sha1 or md5 */
+char * sign_key_fingerprint(unsigned char* keyblob, unsigned int keybloblen) {
+
+#ifdef DROPBEAR_MD5_HMAC
+ return sign_key_md5_fingerprint(keyblob, keybloblen);
+#else
+ return sign_key_sha1_fingerprint(keyblob, keybloblen);
+#endif
+}
+
+void buf_put_sign(buffer* buf, sign_key *key, int type,
+ const unsigned char *data, unsigned int len) {
+
+ buffer *sigblob;
+
+ sigblob = buf_new(MAX_PUBKEY_SIZE);
+
+#ifdef DROPBEAR_DSS
+ if (type == DROPBEAR_SIGNKEY_DSS) {
+ buf_put_dss_sign(sigblob, key->dsskey, data, len);
+ }
+#endif
+#ifdef DROPBEAR_RSA
+ if (type == DROPBEAR_SIGNKEY_RSA) {
+ buf_put_rsa_sign(sigblob, key->rsakey, data, len);
+ }
+#endif
+ if (sigblob->len == 0) {
+ dropbear_exit("non-matching signing type");
+ }
+
+ buf_setpos(sigblob, 0);
+ buf_putstring(buf, buf_getptr(sigblob, sigblob->len),
+ sigblob->len);
+
+ buf_free(sigblob);
+
+}
+
+#ifdef DROPBEAR_SIGNKEY_VERIFY
+/* Return DROPBEAR_SUCCESS or DROPBEAR_FAILURE.
+ * If FAILURE is returned, the position of
+ * buf is undefined. If SUCCESS is returned, buf will be positioned after the
+ * signature blob */
+int buf_verify(buffer * buf, sign_key *key, const unsigned char *data,
+ unsigned int len) {
+
+ unsigned int bloblen;
+ unsigned char * ident = NULL;
+ unsigned int identlen = 0;
+
+ TRACE(("enter buf_verify"))
+
+ bloblen = buf_getint(buf);
+ ident = buf_getstring(buf, &identlen);
+
+#ifdef DROPBEAR_DSS
+ if (bloblen == DSS_SIGNATURE_SIZE &&
+ memcmp(ident, SSH_SIGNKEY_DSS, identlen) == 0) {
+ m_free(ident);
+ if (key->dsskey == NULL) {
+ dropbear_exit("no dss key to verify signature");
+ }
+ return buf_dss_verify(buf, key->dsskey, data, len);
+ }
+#endif
+
+#ifdef DROPBEAR_RSA
+ if (memcmp(ident, SSH_SIGNKEY_RSA, identlen) == 0) {
+ m_free(ident);
+ if (key->rsakey == NULL) {
+ dropbear_exit("no rsa key to verify signature");
+ }
+ return buf_rsa_verify(buf, key->rsakey, data, len);
+ }
+#endif
+
+ m_free(ident);
+ dropbear_exit("non-matching signing type");
+ return DROPBEAR_FAILURE;
+}
+#endif /* DROPBEAR_SIGNKEY_VERIFY */
+
+#ifdef DROPBEAR_KEY_LINES /* ie we're using authorized_keys or known_hosts */
+
+/* Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE when given a buffer containing
+ * a key, a key, and a type. The buffer is positioned at the start of the
+ * base64 data, and contains no trailing data */
+int cmp_base64_key(const unsigned char* keyblob, unsigned int keybloblen,
+ const unsigned char* algoname, unsigned int algolen,
+ buffer * line) {
+
+ buffer * decodekey = NULL;
+ int ret = DROPBEAR_FAILURE;
+ unsigned int len, filealgolen;
+ unsigned long decodekeylen;
+ unsigned char* filealgo = NULL;
+
+ /* now we have the actual data */
+ len = line->len - line->pos;
+ decodekeylen = len * 2; /* big to be safe */
+ decodekey = buf_new(decodekeylen);
+
+ if (base64_decode(buf_getptr(line, len), len,
+ buf_getwriteptr(decodekey, decodekey->size),
+ &decodekeylen) != CRYPT_OK) {
+ TRACE(("checkpubkey: base64 decode failed"))
+ goto out;
+ }
+ 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"))
+ goto out;
+ }
+
+ /* ... and also check that the algo specified and the algo in the key
+ * itself match */
+ filealgolen = buf_getint(decodekey);
+ filealgo = buf_getptr(decodekey, filealgolen);
+ if (filealgolen != algolen || memcmp(filealgo, algoname, algolen) != 0) {
+ TRACE(("checkpubkey: algo match failed"))
+ goto out;
+ }
+
+ /* All checks passed */
+ ret = DROPBEAR_SUCCESS;
+
+out:
+ buf_free(decodekey);
+ decodekey = NULL;
+ return ret;
+}
+#endif
diff --git a/signkey.h b/signkey.h
new file mode 100644
index 0000000..8bc7e8f
--- /dev/null
+++ b/signkey.h
@@ -0,0 +1,63 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+#ifndef _SIGNKEY_H_
+#define _SIGNKEY_H_
+
+#include "buffer.h"
+#include "dss.h"
+#include "rsa.h"
+
+struct SIGN_key {
+
+#ifdef DROPBEAR_DSS
+ dss_key * dsskey;
+#endif
+#ifdef DROPBEAR_RSA
+ rsa_key * rsakey;
+#endif
+};
+
+typedef struct SIGN_key sign_key;
+
+sign_key * new_sign_key();
+const char* signkey_name_from_type(int type, int *namelen);
+int signkey_type_from_name(const char* name, int namelen);
+int buf_get_pub_key(buffer *buf, sign_key *key, int *type);
+int buf_get_priv_key(buffer* buf, sign_key *key, int *type);
+void buf_put_pub_key(buffer* buf, sign_key *key, int type);
+void buf_put_priv_key(buffer* buf, sign_key *key, int type);
+void sign_key_free(sign_key *key);
+void buf_put_sign(buffer* buf, sign_key *key, int type,
+ const unsigned char *data, unsigned int len);
+#ifdef DROPBEAR_SIGNKEY_VERIFY
+int buf_verify(buffer * buf, sign_key *key, const unsigned char *data,
+ unsigned int len);
+char * sign_key_fingerprint(unsigned char* keyblob, unsigned int keybloblen);
+#endif
+int cmp_base64_key(const unsigned char* keyblob, unsigned int keybloblen,
+ const unsigned char* algoname, unsigned int algolen,
+ buffer * line);
+
+#endif /* _SIGNKEY_H_ */
diff --git a/ssh.h b/ssh.h
new file mode 100644
index 0000000..26b51bf
--- /dev/null
+++ b/ssh.h
@@ -0,0 +1,107 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+/* This file contains the various numbers in the protocol */
+
+
+/* message numbers */
+#define SSH_MSG_DISCONNECT 1
+#define SSH_MSG_IGNORE 2
+#define SSH_MSG_UNIMPLEMENTED 3
+#define SSH_MSG_DEBUG 4
+#define SSH_MSG_SERVICE_REQUEST 5
+#define SSH_MSG_SERVICE_ACCEPT 6
+#define SSH_MSG_KEXINIT 20
+#define SSH_MSG_NEWKEYS 21
+#define SSH_MSG_KEXDH_INIT 30
+#define SSH_MSG_KEXDH_REPLY 31
+
+/* userauth message numbers */
+#define SSH_MSG_USERAUTH_REQUEST 50
+#define SSH_MSG_USERAUTH_FAILURE 51
+#define SSH_MSG_USERAUTH_SUCCESS 52
+#define SSH_MSG_USERAUTH_BANNER 53
+
+/* packets 60-79 are method-specific, aren't one-one mapping */
+#define SSH_MSG_USERAUTH_SPECIFIC_60 60
+
+#define SSH_MSG_USERAUTH_PASSWD_CHANGEREQ 60
+
+#define SSH_MSG_USERAUTH_PK_OK 60
+
+/* keyboard interactive auth */
+#define SSH_MSG_USERAUTH_INFO_REQUEST 60
+#define SSH_MSG_USERAUTH_INFO_RESPONSE 61
+
+
+/* If adding numbers here, check MAX_UNAUTH_PACKET_TYPE in process-packet.c
+ * is still valid */
+
+/* connect message numbers */
+#define SSH_MSG_GLOBAL_REQUEST 80
+#define SSH_MSG_REQUEST_SUCCESS 81
+#define SSH_MSG_REQUEST_FAILURE 82
+#define SSH_MSG_CHANNEL_OPEN 90
+#define SSH_MSG_CHANNEL_OPEN_CONFIRMATION 91
+#define SSH_MSG_CHANNEL_OPEN_FAILURE 92
+#define SSH_MSG_CHANNEL_WINDOW_ADJUST 93
+#define SSH_MSG_CHANNEL_DATA 94
+#define SSH_MSG_CHANNEL_EXTENDED_DATA 95
+#define SSH_MSG_CHANNEL_EOF 96
+#define SSH_MSG_CHANNEL_CLOSE 97
+#define SSH_MSG_CHANNEL_REQUEST 98
+#define SSH_MSG_CHANNEL_SUCCESS 99
+#define SSH_MSG_CHANNEL_FAILURE 100
+
+/* extended data types */
+#define SSH_EXTENDED_DATA_STDERR 1
+
+/* disconnect codes */
+#define SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT 1
+#define SSH_DISCONNECT_PROTOCOL_ERROR 2
+#define SSH_DISCONNECT_KEY_EXCHANGE_FAILED 3
+#define SSH_DISCONNECT_RESERVED 4
+#define SSH_DISCONNECT_MAC_ERROR 5
+#define SSH_DISCONNECT_COMPRESSION_ERROR 6
+#define SSH_DISCONNECT_SERVICE_NOT_AVAILABLE 7
+#define SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED 8
+#define SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE 9
+#define SSH_DISCONNECT_CONNECTION_LOST 10
+#define SSH_DISCONNECT_BY_APPLICATION 11
+#define SSH_DISCONNECT_TOO_MANY_CONNECTIONS 12
+#define SSH_DISCONNECT_AUTH_CANCELLED_BY_USER 13
+#define SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE 14
+#define SSH_DISCONNECT_ILLEGAL_USER_NAME 15
+
+/* service types */
+#define SSH_SERVICE_USERAUTH "ssh-userauth"
+#define SSH_SERVICE_USERAUTH_LEN 12
+#define SSH_SERVICE_CONNECTION "ssh-connection"
+#define SSH_SERVICE_CONNECTION_LEN 14
+
+/* public key types */
+#define SSH_SIGNKEY_DSS "ssh-dss"
+#define SSH_SIGNKEY_DSS_LEN 7
+#define SSH_SIGNKEY_RSA "ssh-rsa"
+#define SSH_SIGNKEY_RSA_LEN 7
diff --git a/sshpty.c b/sshpty.c
new file mode 100644
index 0000000..3526ff0
--- /dev/null
+++ b/sshpty.c
@@ -0,0 +1,412 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * Copied from OpenSSH-3.5p1 source, modified by Matt Johnston 2003
+ *
+ * Author: Tatu Ylonen <ylo@cs.hut.fi>
+ * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+ * All rights reserved
+ * Allocating a pseudo-terminal, and making it the controlling tty.
+ *
+ * As far as I am concerned, the code I have written for this software
+ * can be used freely for any purpose. Any derived versions of this
+ * software must be clearly marked as such, and if the derived work is
+ * incompatible with the protocol description in the RFC file, it must be
+ * called by a name other than "ssh" or "Secure Shell".
+ */
+
+/*RCSID("OpenBSD: sshpty.c,v 1.7 2002/06/24 17:57:20 deraadt Exp ");*/
+
+#include "includes.h"
+#include "dbutil.h"
+#include "errno.h"
+#include "sshpty.h"
+
+/* Pty allocated with _getpty gets broken if we do I_PUSH:es to it. */
+#if defined(HAVE__GETPTY) || defined(HAVE_OPENPTY)
+#undef HAVE_DEV_PTMX
+#endif
+
+#ifdef HAVE_PTY_H
+# include <pty.h>
+#endif
+#if defined(USE_DEV_PTMX) && defined(HAVE_STROPTS_H)
+# include <stropts.h>
+#endif
+
+#ifndef O_NOCTTY
+#define O_NOCTTY 0
+#endif
+
+/*
+ * Allocates and opens a pty. Returns 0 if no pty could be allocated, or
+ * nonzero if a pty was successfully allocated. On success, open file
+ * descriptors for the pty and tty sides and the name of the tty side are
+ * returned (the buffer must be able to hold at least 64 characters).
+ */
+
+int
+pty_allocate(int *ptyfd, int *ttyfd, char *namebuf, int namebuflen)
+{
+#if defined(HAVE_OPENPTY)
+ /* exists in recent (4.4) BSDs and OSF/1 */
+ char *name;
+ int i;
+
+ i = openpty(ptyfd, ttyfd, NULL, NULL, NULL);
+ if (i < 0) {
+ dropbear_log(LOG_WARNING,
+ "pty_allocate: openpty: %.100s", strerror(errno));
+ return 0;
+ }
+ name = ttyname(*ttyfd);
+ if (!name) {
+ dropbear_exit("ttyname fails for openpty device");
+ }
+
+ strlcpy(namebuf, name, namebuflen); /* possible truncation */
+ return 1;
+#else /* HAVE_OPENPTY */
+#ifdef HAVE__GETPTY
+ /*
+ * _getpty(3) exists in SGI Irix 4.x, 5.x & 6.x -- it generates more
+ * pty's automagically when needed
+ */
+ char *slave;
+
+ slave = _getpty(ptyfd, O_RDWR, 0622, 0);
+ if (slave == NULL) {
+ dropbear_log(LOG_WARNING,
+ "pty_allocate: _getpty: %.100s", strerror(errno));
+ return 0;
+ }
+ strlcpy(namebuf, slave, namebuflen);
+ /* Open the slave side. */
+ *ttyfd = open(namebuf, O_RDWR | O_NOCTTY);
+ if (*ttyfd < 0) {
+ dropbear_log(LOG_WARNING,
+ "pty_allocate error: ttyftd open error");
+ close(*ptyfd);
+ return 0;
+ }
+ return 1;
+#else /* HAVE__GETPTY */
+#if defined(USE_DEV_PTMX)
+ /*
+ * This code is used e.g. on Solaris 2.x. (Note that Solaris 2.3
+ * also has bsd-style ptys, but they simply do not work.)
+ *
+ * Linux systems may have the /dev/ptmx device, but this code won't work.
+ */
+ int ptm;
+ char *pts;
+
+ ptm = open("/dev/ptmx", O_RDWR | O_NOCTTY);
+ if (ptm < 0) {
+ dropbear_log(LOG_WARNING,
+ "pty_allocate: /dev/ptmx: %.100s", strerror(errno));
+ return 0;
+ }
+ if (grantpt(ptm) < 0) {
+ dropbear_log(LOG_WARNING,
+ "grantpt: %.100s", strerror(errno));
+ return 0;
+ }
+ if (unlockpt(ptm) < 0) {
+ dropbear_log(LOG_WARNING,
+ "unlockpt: %.100s", strerror(errno));
+ return 0;
+ }
+ pts = ptsname(ptm);
+ if (pts == NULL) {
+ dropbear_log(LOG_WARNING,
+ "Slave pty side name could not be obtained.");
+ }
+ strlcpy(namebuf, pts, namebuflen);
+ *ptyfd = ptm;
+
+ /* Open the slave side. */
+ *ttyfd = open(namebuf, O_RDWR | O_NOCTTY);
+ if (*ttyfd < 0) {
+ dropbear_log(LOG_ERR,
+ "error opening pts %.100s: %.100s", namebuf, strerror(errno));
+ close(*ptyfd);
+ return 0;
+ }
+#ifndef HAVE_CYGWIN
+ /*
+ * Push the appropriate streams modules, as described in Solaris pts(7).
+ * HP-UX pts(7) doesn't have ttcompat module.
+ */
+ if (ioctl(*ttyfd, I_PUSH, "ptem") < 0) {
+ dropbear_log(LOG_WARNING,
+ "ioctl I_PUSH ptem: %.100s", strerror(errno));
+ }
+ if (ioctl(*ttyfd, I_PUSH, "ldterm") < 0) {
+ dropbear_log(LOG_WARNING,
+ "ioctl I_PUSH ldterm: %.100s", strerror(errno));
+ }
+#ifndef __hpux
+ if (ioctl(*ttyfd, I_PUSH, "ttcompat") < 0) {
+ dropbear_log(LOG_WARNING,
+ "ioctl I_PUSH ttcompat: %.100s", strerror(errno));
+ }
+#endif
+#endif
+ return 1;
+#else /* USE_DEV_PTMX */
+#ifdef HAVE_DEV_PTS_AND_PTC
+ /* AIX-style pty code. */
+ const char *name;
+
+ *ptyfd = open("/dev/ptc", O_RDWR | O_NOCTTY);
+ if (*ptyfd < 0) {
+ dropbear_log(LOG_ERR,
+ "Could not open /dev/ptc: %.100s", strerror(errno));
+ return 0;
+ }
+ name = ttyname(*ptyfd);
+ if (!name) {
+ dropbear_exit("ttyname fails for /dev/ptc device");
+ }
+ strlcpy(namebuf, name, namebuflen);
+ *ttyfd = open(name, O_RDWR | O_NOCTTY);
+ if (*ttyfd < 0) {
+ dropbear_log(LOG_ERR,
+ "Could not open pty slave side %.100s: %.100s",
+ name, strerror(errno));
+ close(*ptyfd);
+ return 0;
+ }
+ return 1;
+#else /* HAVE_DEV_PTS_AND_PTC */
+
+ /* BSD-style pty code. */
+ char buf[64];
+ int i;
+ const char *ptymajors = "pqrstuvwxyzabcdefghijklmnoABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ const char *ptyminors = "0123456789abcdef";
+ int num_minors = strlen(ptyminors);
+ int num_ptys = strlen(ptymajors) * num_minors;
+ struct termios tio;
+
+ for (i = 0; i < num_ptys; i++) {
+ snprintf(buf, sizeof buf, "/dev/pty%c%c", ptymajors[i / num_minors],
+ ptyminors[i % num_minors]);
+ snprintf(namebuf, namebuflen, "/dev/tty%c%c",
+ ptymajors[i / num_minors], ptyminors[i % num_minors]);
+
+ *ptyfd = open(buf, O_RDWR | O_NOCTTY);
+ if (*ptyfd < 0) {
+ /* Try SCO style naming */
+ snprintf(buf, sizeof buf, "/dev/ptyp%d", i);
+ snprintf(namebuf, namebuflen, "/dev/ttyp%d", i);
+ *ptyfd = open(buf, O_RDWR | O_NOCTTY);
+ if (*ptyfd < 0) {
+ continue;
+ }
+ }
+
+ /* Open the slave side. */
+ *ttyfd = open(namebuf, O_RDWR | O_NOCTTY);
+ if (*ttyfd < 0) {
+ dropbear_log(LOG_ERR,
+ "pty_allocate: %.100s: %.100s", namebuf, strerror(errno));
+ close(*ptyfd);
+ return 0;
+ }
+ /* set tty modes to a sane state for broken clients */
+ if (tcgetattr(*ptyfd, &tio) < 0) {
+ dropbear_log(LOG_WARNING,
+ "ptyallocate: tty modes failed: %.100s", strerror(errno));
+ } else {
+ tio.c_lflag |= (ECHO | ISIG | ICANON);
+ tio.c_oflag |= (OPOST | ONLCR);
+ tio.c_iflag |= ICRNL;
+
+ /* Set the new modes for the terminal. */
+ if (tcsetattr(*ptyfd, TCSANOW, &tio) < 0) {
+ dropbear_log(LOG_WARNING,
+ "Setting tty modes for pty failed: %.100s",
+ strerror(errno));
+ }
+ }
+
+ return 1;
+ }
+ dropbear_log(LOG_WARNING, "failed to open any /dev/pty?? devices");
+ return 0;
+#endif /* HAVE_DEV_PTS_AND_PTC */
+#endif /* USE_DEV_PTMX */
+#endif /* HAVE__GETPTY */
+#endif /* HAVE_OPENPTY */
+}
+
+/* Releases the tty. Its ownership is returned to root, and permissions to 0666. */
+
+void
+pty_release(const char *tty_name)
+{
+ if (chown(tty_name, (uid_t) 0, (gid_t) 0) < 0
+ && (errno != ENOENT)) {
+ dropbear_log(LOG_ERR,
+ "chown %.100s 0 0 failed: %.100s", tty_name, strerror(errno));
+ }
+ if (chmod(tty_name, (mode_t) 0666) < 0
+ && (errno != ENOENT)) {
+ dropbear_log(LOG_ERR,
+ "chmod %.100s 0666 failed: %.100s", tty_name, strerror(errno));
+ }
+}
+
+/* Makes the tty the processes controlling tty and sets it to sane modes. */
+
+void
+pty_make_controlling_tty(int *ttyfd, const char *tty_name)
+{
+ int fd;
+#ifdef USE_VHANGUP
+ void *old;
+#endif /* USE_VHANGUP */
+
+ /* Solaris has a problem with TIOCNOTTY for a bg process, so
+ * we disable the signal which would STOP the process - matt */
+ signal(SIGTTOU, SIG_IGN);
+
+ /* First disconnect from the old controlling tty. */
+#ifdef TIOCNOTTY
+ fd = open(_PATH_TTY, O_RDWR | O_NOCTTY);
+ if (fd >= 0) {
+ (void) ioctl(fd, TIOCNOTTY, NULL);
+ close(fd);
+ }
+#endif /* TIOCNOTTY */
+ if (setsid() < 0) {
+ dropbear_log(LOG_ERR,
+ "setsid: %.100s", strerror(errno));
+ }
+
+ /*
+ * Verify that we are successfully disconnected from the controlling
+ * tty.
+ */
+ fd = open(_PATH_TTY, O_RDWR | O_NOCTTY);
+ if (fd >= 0) {
+ dropbear_log(LOG_ERR,
+ "Failed to disconnect from controlling tty.\n");
+ close(fd);
+ }
+ /* Make it our controlling tty. */
+#ifdef TIOCSCTTY
+ if (ioctl(*ttyfd, TIOCSCTTY, NULL) < 0) {
+ dropbear_log(LOG_ERR,
+ "ioctl(TIOCSCTTY): %.100s", strerror(errno));
+ }
+#endif /* TIOCSCTTY */
+#ifdef HAVE_NEWS4
+ if (setpgrp(0,0) < 0) {
+ dropbear_log(LOG_ERR,
+ error("SETPGRP %s",strerror(errno)));
+ }
+#endif /* HAVE_NEWS4 */
+#ifdef USE_VHANGUP
+ old = mysignal(SIGHUP, SIG_IGN);
+ vhangup();
+ mysignal(SIGHUP, old);
+#endif /* USE_VHANGUP */
+ fd = open(tty_name, O_RDWR);
+ if (fd < 0) {
+ dropbear_log(LOG_ERR,
+ "%.100s: %.100s", tty_name, strerror(errno));
+ } else {
+#ifdef USE_VHANGUP
+ close(*ttyfd);
+ *ttyfd = fd;
+#else /* USE_VHANGUP */
+ close(fd);
+#endif /* USE_VHANGUP */
+ }
+ /* Verify that we now have a controlling tty. */
+ fd = open(_PATH_TTY, O_WRONLY);
+ if (fd < 0) {
+ dropbear_log(LOG_ERR,
+ "open /dev/tty failed - could not set controlling tty: %.100s",
+ strerror(errno));
+ } else {
+ close(fd);
+ }
+}
+
+/* Changes the window size associated with the pty. */
+
+void
+pty_change_window_size(int ptyfd, int row, int col,
+ int xpixel, int ypixel)
+{
+ struct winsize w;
+
+ w.ws_row = row;
+ w.ws_col = col;
+ w.ws_xpixel = xpixel;
+ w.ws_ypixel = ypixel;
+ (void) ioctl(ptyfd, TIOCSWINSZ, &w);
+}
+
+void
+pty_setowner(struct passwd *pw, const char *tty_name)
+{
+ struct group *grp;
+ gid_t gid;
+ mode_t mode;
+ struct stat st;
+
+ /* Determine the group to make the owner of the tty. */
+ grp = getgrnam("tty");
+ if (grp) {
+ gid = grp->gr_gid;
+ mode = S_IRUSR | S_IWUSR | S_IWGRP;
+ } else {
+ gid = pw->pw_gid;
+ mode = S_IRUSR | S_IWUSR | S_IWGRP | S_IWOTH;
+ }
+
+ /*
+ * Change owner and mode of the tty as required.
+ * Warn but continue if filesystem is read-only and the uids match/
+ * tty is owned by root.
+ */
+ if (stat(tty_name, &st)) {
+ dropbear_exit("pty_setowner: stat(%.101s) failed: %.100s",
+ tty_name, strerror(errno));
+ }
+
+ if (st.st_uid != pw->pw_uid || st.st_gid != gid) {
+ if (chown(tty_name, pw->pw_uid, gid) < 0) {
+ if (errno == EROFS &&
+ (st.st_uid == pw->pw_uid || st.st_uid == 0)) {
+ dropbear_log(LOG_ERR,
+ "chown(%.100s, %u, %u) failed: %.100s",
+ tty_name, (unsigned int)pw->pw_uid, (unsigned int)gid,
+ strerror(errno));
+ } else {
+ dropbear_exit("chown(%.100s, %u, %u) failed: %.100s",
+ tty_name, (unsigned int)pw->pw_uid, (unsigned int)gid,
+ strerror(errno));
+ }
+ }
+ }
+
+ if ((st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) != mode) {
+ if (chmod(tty_name, mode) < 0) {
+ if (errno == EROFS &&
+ (st.st_mode & (S_IRGRP | S_IROTH)) == 0) {
+ dropbear_log(LOG_ERR,
+ "chmod(%.100s, 0%o) failed: %.100s",
+ tty_name, mode, strerror(errno));
+ } else {
+ dropbear_exit("chmod(%.100s, 0%o) failed: %.100s",
+ tty_name, mode, strerror(errno));
+ }
+ }
+ }
+}
diff --git a/sshpty.h b/sshpty.h
new file mode 100644
index 0000000..cf72072
--- /dev/null
+++ b/sshpty.h
@@ -0,0 +1,28 @@
+/* $OpenBSD: sshpty.h,v 1.4 2002/03/04 17:27:39 stevesk Exp $ */
+
+/*
+ * Copied from openssh-3.5p1 source by Matt Johnston 2003
+ *
+ * Author: Tatu Ylonen <ylo@cs.hut.fi>
+ * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+ * All rights reserved
+ * Functions for allocating a pseudo-terminal and making it the controlling
+ * tty.
+ *
+ * As far as I am concerned, the code I have written for this software
+ * can be used freely for any purpose. Any derived versions of this
+ * software must be clearly marked as such, and if the derived work is
+ * incompatible with the protocol description in the RFC file, it must be
+ * called by a name other than "ssh" or "Secure Shell".
+ */
+
+#ifndef SSHPTY_H
+#define SSHPTY_H
+
+int pty_allocate(int *, int *, char *, int);
+void pty_release(const char *);
+void pty_make_controlling_tty(int *, const char *);
+void pty_change_window_size(int, int, int, int, int);
+void pty_setowner(struct passwd *, const char *);
+
+#endif /* SSHPTY_H */
diff --git a/svr-agentfwd.c b/svr-agentfwd.c
new file mode 100644
index 0000000..5127158
--- /dev/null
+++ b/svr-agentfwd.c
@@ -0,0 +1,266 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+/* This file (agentfwd.c) handles authentication agent forwarding, for OpenSSH
+ * style agents. */
+
+#include "includes.h"
+
+#ifndef DISABLE_AGENTFWD
+
+#include "agentfwd.h"
+#include "session.h"
+#include "ssh.h"
+#include "dbutil.h"
+#include "chansession.h"
+#include "channel.h"
+#include "packet.h"
+#include "buffer.h"
+#include "random.h"
+#include "listener.h"
+
+#define AGENTDIRPREFIX "/tmp/dropbear-"
+
+static int send_msg_channel_open_agent(int fd);
+static int bindagent(int fd, struct ChanSess * chansess);
+static void agentaccept(struct Listener * listener, int sock);
+
+/* Handles client requests to start agent forwarding, sets up listening socket.
+ * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
+int agentreq(struct ChanSess * chansess) {
+
+ int fd;
+
+ if (chansess->agentlistener != NULL) {
+ return DROPBEAR_FAILURE;
+ }
+
+ /* create listening socket */
+ fd = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0) {
+ goto fail;
+ }
+
+ /* create the unix socket dir and file */
+ if (bindagent(fd, chansess) == DROPBEAR_FAILURE) {
+ goto fail;
+ }
+
+ /* listen */
+ if (listen(fd, 20) < 0) {
+ goto fail;
+ }
+
+ /* set non-blocking */
+ setnonblocking(fd);
+
+ /* pass if off to listener */
+ chansess->agentlistener = new_listener( &fd, 1, 0, chansess,
+ agentaccept, NULL);
+
+ if (chansess->agentlistener == NULL) {
+ goto fail;
+ }
+
+ return DROPBEAR_SUCCESS;
+
+fail:
+ /* cleanup */
+ agentcleanup(chansess);
+
+ return DROPBEAR_FAILURE;
+}
+
+/* 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 *UNUSED(listener), int sock) {
+
+ int fd;
+
+ fd = accept(sock, NULL, NULL);
+ if (fd < 0) {
+ TRACE(("accept failed"))
+ return;
+ }
+
+ if (send_msg_channel_open_agent(fd) != DROPBEAR_SUCCESS) {
+ close(fd);
+ }
+
+}
+
+/* set up the environment variable pointing to the socket. This is called
+ * just before command/shell execution, after dropping priveleges */
+void agentset(struct ChanSess * chansess) {
+
+ char *path = NULL;
+ int len;
+
+ if (chansess->agentlistener == NULL) {
+ return;
+ }
+
+ /* 2 for "/" and "\0" */
+ len = strlen(chansess->agentdir) + strlen(chansess->agentfile) + 2;
+
+ path = m_malloc(len);
+ snprintf(path, len, "%s/%s", chansess->agentdir, chansess->agentfile);
+ addnewvar("SSH_AUTH_SOCK", path);
+ m_free(path);
+}
+
+/* close the socket, remove the socket-file */
+void agentcleanup(struct ChanSess * chansess) {
+
+ char *path = NULL;
+ uid_t uid;
+ gid_t gid;
+ int len;
+
+ if (chansess->agentlistener != NULL) {
+ remove_listener(chansess->agentlistener);
+ chansess->agentlistener = NULL;
+ }
+
+ if (chansess->agentfile != NULL && chansess->agentdir != NULL) {
+
+ /* Remove the dir as the user. That way they can't cause problems except
+ * for themselves */
+ uid = getuid();
+ gid = getgid();
+ if ((setegid(ses.authstate.pw->pw_gid)) < 0 ||
+ (seteuid(ses.authstate.pw->pw_uid)) < 0) {
+ dropbear_exit("failed to set euid");
+ }
+
+ /* 2 for "/" and "\0" */
+ len = strlen(chansess->agentdir) + strlen(chansess->agentfile) + 2;
+
+ path = m_malloc(len);
+ snprintf(path, len, "%s/%s", chansess->agentdir, chansess->agentfile);
+ unlink(path);
+ m_free(path);
+
+ rmdir(chansess->agentdir);
+
+ if ((seteuid(uid)) < 0 ||
+ (setegid(gid)) < 0) {
+ dropbear_exit("failed to revert euid");
+ }
+
+ m_free(chansess->agentfile);
+ m_free(chansess->agentdir);
+ }
+
+}
+
+static const struct ChanType chan_agent = {
+ 0, /* sepfds */
+ "auth-agent@openssh.com",
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+
+/* helper for accepting an agent request */
+static int send_msg_channel_open_agent(int fd) {
+
+ if (send_msg_channel_open_init(fd, &chan_agent) == DROPBEAR_SUCCESS) {
+ encrypt_packet();
+ return DROPBEAR_SUCCESS;
+ } else {
+ return DROPBEAR_FAILURE;
+ }
+}
+
+/* helper for creating the agent socket-file
+ returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
+static int bindagent(int fd, struct ChanSess * chansess) {
+
+ struct sockaddr_un addr;
+ unsigned int prefix;
+ char path[sizeof(addr.sun_path)], sockfile[sizeof(addr.sun_path)];
+ mode_t mode;
+ int i;
+ uid_t uid;
+ gid_t gid;
+ int ret = DROPBEAR_FAILURE;
+
+ /* drop to user privs to make the dir/file */
+ uid = getuid();
+ gid = getgid();
+ if ((setegid(ses.authstate.pw->pw_gid)) < 0 ||
+ (seteuid(ses.authstate.pw->pw_uid)) < 0) {
+ dropbear_exit("failed to set euid");
+ }
+
+ memset((void*)&addr, 0x0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+
+ mode = S_IRWXU;
+
+ for (i = 0; i < 20; i++) {
+ genrandom((unsigned char*)&prefix, sizeof(prefix));
+ /* we want 32 bits (8 hex digits) - "/tmp/dropbear-f19c62c0" */
+ snprintf(path, sizeof(path), AGENTDIRPREFIX "%.8x", prefix);
+
+ if (mkdir(path, mode) == 0) {
+ goto bindsocket;
+ }
+ if (errno != EEXIST) {
+ break;
+ }
+ }
+ /* couldn't make a dir */
+ goto out;
+
+bindsocket:
+ /* Format is "/tmp/dropbear-0246dead/auth-d00f7654-23".
+ * The "23" is the file desc, the random data is to avoid collisions
+ * between subsequent user processes reusing socket fds (odds are now
+ * 1/(2^64) */
+ genrandom((unsigned char*)&prefix, sizeof(prefix));
+ snprintf(sockfile, sizeof(sockfile), "auth-%.8x-%d", prefix, fd);
+
+ snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s", path, sockfile);
+
+ if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) == 0) {
+ chansess->agentdir = m_strdup(path);
+ chansess->agentfile = m_strdup(sockfile);
+ ret = DROPBEAR_SUCCESS;
+ }
+
+
+out:
+ if ((seteuid(uid)) < 0 ||
+ (setegid(gid)) < 0) {
+ dropbear_exit("failed to revert euid");
+ }
+ return ret;
+}
+
+#endif
diff --git a/svr-algo.c b/svr-algo.c
new file mode 100644
index 0000000..c0b7823
--- /dev/null
+++ b/svr-algo.c
@@ -0,0 +1,100 @@
+/*
+ * Dropbear - a SSH2 server
+ * SSH client implementation
+ *
+ * Copyright (c) 2002,2003 Matt Johnston
+ * Copyright (c) 2004 by Mihnea Stoenescu
+ * 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 "algo.h"
+#include "dbutil.h"
+
+/* match the first algorithm in the comma-separated list in buf which is
+ * also in localalgos[], or return NULL on failure.
+ * (*goodguess) is set to 1 if the preferred client/server algos match,
+ * 0 otherwise. This is used for checking if the kexalgo/hostkeyalgos are
+ * guessed correctly */
+algo_type * svr_buf_match_algo(buffer* buf, algo_type localalgos[],
+ int *goodguess)
+{
+
+ unsigned char * algolist = NULL;
+ unsigned char * remotealgos[MAX_PROPOSED_ALGO];
+ unsigned int len;
+ unsigned int count, i, j;
+ algo_type * ret = NULL;
+
+ *goodguess = 0;
+
+ /* 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))
+ if (len > MAX_PROPOSED_ALGO*(MAX_NAME_LEN+1)) {
+ goto out; /* just a sanity check, no other use */
+ }
+
+ /* remotealgos will contain a list of the strings parsed out */
+ /* We will have at least one string (even if it's just "") */
+ remotealgos[0] = algolist;
+ count = 1;
+ /* Iterate through, replacing ','s with NULs, to split it into
+ * words. */
+ for (i = 0; i < len; i++) {
+ if (algolist[i] == '\0') {
+ /* someone is trying something strange */
+ goto out;
+ }
+ if (algolist[i] == ',') {
+ algolist[i] = '\0';
+ remotealgos[count] = &algolist[i+1];
+ count++;
+ }
+ if (count == MAX_PROPOSED_ALGO) {
+ break;
+ }
+ }
+
+ /* iterate and find the first match */
+ for (i = 0; i < count; i++) {
+
+ len = strlen(remotealgos[i]);
+
+ for (j = 0; localalgos[j].name != NULL; j++) {
+ if (localalgos[j].usable) {
+ if (len == strlen(localalgos[j].name) &&
+ strncmp(localalgos[j].name, remotealgos[i], len) == 0) {
+ /* set if it was a good guess */
+ if (i == 0 && j == 0) {
+ *goodguess = 1;
+ }
+ /* set the algo to return */
+ ret = &localalgos[j];
+ goto out;
+ }
+ }
+ }
+ }
+
+out:
+ m_free(algolist);
+ return ret;
+}
diff --git a/svr-auth.c b/svr-auth.c
new file mode 100644
index 0000000..f0fca38
--- /dev/null
+++ b/svr-auth.c
@@ -0,0 +1,373 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+/* This file (auth.c) handles authentication requests, passing it to the
+ * particular type (auth-passwd, auth-pubkey). */
+
+#include "includes.h"
+#include "dbutil.h"
+#include "session.h"
+#include "buffer.h"
+#include "ssh.h"
+#include "packet.h"
+#include "auth.h"
+#include "runopts.h"
+
+static void authclear();
+static int checkusername(unsigned char *username, unsigned int userlen);
+static void send_msg_userauth_banner();
+
+/* initialise the first time for a session, resetting all parameters */
+void svr_authinitialise() {
+
+ ses.authstate.failcount = 0;
+ authclear();
+
+}
+
+/* Reset the auth state, but don't reset the failcount. This is for if the
+ * user decides to try with a different username etc, and is also invoked
+ * on initialisation */
+static void authclear() {
+
+ memset(&ses.authstate, 0, sizeof(ses.authstate));
+#ifdef ENABLE_SVR_PUBKEY_AUTH
+ ses.authstate.authtypes |= AUTH_TYPE_PUBKEY;
+#endif
+#if defined(ENABLE_SVR_PASSWORD_AUTH) || defined(ENABLE_SVR_PAM_AUTH)
+ if (!svr_opts.noauthpass) {
+ ses.authstate.authtypes |= AUTH_TYPE_PASSWORD;
+ }
+#endif
+
+}
+
+/* Send a banner message if specified to the client. The client might
+ * ignore this, but possibly serves as a legal "no trespassing" sign */
+static void send_msg_userauth_banner() {
+
+ TRACE(("enter send_msg_userauth_banner"))
+ if (svr_opts.banner == NULL) {
+ TRACE(("leave send_msg_userauth_banner: banner is NULL"))
+ return;
+ }
+
+ CHECKCLEARTOWRITE();
+
+ buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_BANNER);
+ buf_putstring(ses.writepayload, buf_getptr(svr_opts.banner,
+ svr_opts.banner->len), svr_opts.banner->len);
+ buf_putstring(ses.writepayload, "en", 2);
+
+ encrypt_packet();
+ buf_free(svr_opts.banner);
+ svr_opts.banner = NULL;
+
+ TRACE(("leave send_msg_userauth_banner"))
+}
+
+/* handle a userauth request, check validity, pass to password or pubkey
+ * checking, and handle success or failure */
+void recv_msg_userauth_request() {
+
+ unsigned char *username = NULL, *servicename = NULL, *methodname = NULL;
+ unsigned int userlen, servicelen, methodlen;
+
+ 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"))
+ return;
+ }
+
+ /* send the banner if it exists, it will only exist once */
+ if (svr_opts.banner) {
+ send_msg_userauth_banner();
+ }
+
+
+ username = buf_getstring(ses.payload, &userlen);
+ servicename = buf_getstring(ses.payload, &servicelen);
+ methodname = buf_getstring(ses.payload, &methodlen);
+
+ /* only handle 'ssh-connection' currently */
+ if (servicelen != SSH_SERVICE_CONNECTION_LEN
+ && (strncmp(servicename, SSH_SERVICE_CONNECTION,
+ SSH_SERVICE_CONNECTION_LEN) != 0)) {
+
+ /* TODO - disconnect here */
+ m_free(username);
+ m_free(servicename);
+ m_free(methodname);
+ dropbear_exit("unknown service in auth");
+ }
+
+ /* user wants to know what methods are supported */
+ if (methodlen == AUTH_METHOD_NONE_LEN &&
+ strncmp(methodname, AUTH_METHOD_NONE,
+ AUTH_METHOD_NONE_LEN) == 0) {
+ TRACE(("recv_msg_userauth_request: 'none' request"))
+ send_msg_userauth_failure(0, 0);
+ goto out;
+ }
+
+ /* check username is good before continuing */
+ if (checkusername(username, userlen) == DROPBEAR_FAILURE) {
+ /* username is invalid/no shell/etc - send failure */
+ TRACE(("sending checkusername failure"))
+ send_msg_userauth_failure(0, 1);
+ goto out;
+ }
+
+#ifdef ENABLE_SVR_PASSWORD_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_password();
+ goto out;
+ }
+ }
+#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 &&
+ strncmp(methodname, AUTH_METHOD_PUBKEY,
+ AUTH_METHOD_PUBKEY_LEN) == 0) {
+ svr_auth_pubkey();
+ goto out;
+ }
+#endif
+
+ /* nothing matched, we just fail */
+ send_msg_userauth_failure(0, 1);
+
+out:
+
+ m_free(username);
+ m_free(servicename);
+ m_free(methodname);
+}
+
+/* Check that the username exists, has a non-empty password, and has a valid
+ * shell.
+ * returns DROPBEAR_SUCCESS on valid username, DROPBEAR_FAILURE on failure */
+static int checkusername(unsigned char *username, unsigned int userlen) {
+
+ char* listshell = NULL;
+ char* usershell = NULL;
+
+ TRACE(("enter checkusername"))
+ if (userlen > MAX_USERNAME_LEN) {
+ return DROPBEAR_FAILURE;
+ }
+
+ /* new user or username has changed */
+ if (ses.authstate.username == NULL ||
+ strcmp(username, ses.authstate.username) != 0) {
+ /* the username needs resetting */
+ if (ses.authstate.username != NULL) {
+ dropbear_log(LOG_WARNING, "client trying multiple usernames from %s",
+ svr_ses.addrstring);
+ m_free(ses.authstate.username);
+ }
+ authclear();
+ ses.authstate.pw = getpwnam((char*)username);
+ ses.authstate.username = m_strdup(username);
+ m_free(ses.authstate.printableuser);
+ }
+
+ /* check that user exists */
+ if (ses.authstate.pw == NULL) {
+ TRACE(("leave checkusername: user '%s' doesn't exist", username))
+ dropbear_log(LOG_WARNING,
+ "login attempt for nonexistent user from %s",
+ svr_ses.addrstring);
+ send_msg_userauth_failure(0, 1);
+ return DROPBEAR_FAILURE;
+ }
+
+ /* We can set it once we know its a real user */
+ ses.authstate.printableuser = m_strdup(ses.authstate.pw->pw_name);
+
+ /* check for non-root if desired */
+ if (svr_opts.norootlogin && ses.authstate.pw->pw_uid == 0) {
+ TRACE(("leave checkusername: root login disabled"))
+ dropbear_log(LOG_WARNING, "root login rejected");
+ send_msg_userauth_failure(0, 1);
+ return DROPBEAR_FAILURE;
+ }
+
+ /* check for an empty password */
+ if (ses.authstate.pw->pw_passwd[0] == '\0') {
+ 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))
+
+ /* check that the shell is set */
+ usershell = ses.authstate.pw->pw_shell;
+ if (usershell[0] == '\0') {
+ /* empty shell in /etc/passwd means /bin/sh according to passwd(5) */
+ usershell = "/bin/sh";
+ }
+
+ /* check the shell is valid. If /etc/shells doesn't exist, getusershell()
+ * should return some standard shells like "/bin/sh" and "/bin/csh" (this
+ * is platform-specific) */
+ setusershell();
+ while ((listshell = getusershell()) != NULL) {
+ TRACE(("test shell is '%s'", listshell))
+ if (strcmp(listshell, usershell) == 0) {
+ /* have a match */
+ goto goodshell;
+ }
+ }
+ /* no matching shell */
+ endusershell();
+ TRACE(("no matching shell"))
+ dropbear_log(LOG_WARNING, "user '%s' has invalid shell, rejected",
+ ses.authstate.printableuser);
+ send_msg_userauth_failure(0, 1);
+ return DROPBEAR_FAILURE;
+
+goodshell:
+ endusershell();
+ TRACE(("matching shell"))
+
+ TRACE(("uid = %d", ses.authstate.pw->pw_uid))
+ TRACE(("leave checkusername"))
+ return DROPBEAR_SUCCESS;
+
+}
+
+/* Send a failure message to the client, in responds to a userauth_request.
+ * Partial indicates whether to set the "partial success" flag,
+ * incrfail is whether to count this failure in the failure count (which
+ * is limited. This function also handles disconnection after too many
+ * failures */
+void send_msg_userauth_failure(int partial, int incrfail) {
+
+ buffer *typebuf = NULL;
+
+ TRACE(("enter send_msg_userauth_failure"))
+
+ CHECKCLEARTOWRITE();
+
+ buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_FAILURE);
+
+ /* put a list of allowed types */
+ typebuf = buf_new(30); /* long enough for PUBKEY and PASSWORD */
+
+ if (ses.authstate.authtypes & AUTH_TYPE_PUBKEY) {
+ buf_putbytes(typebuf, AUTH_METHOD_PUBKEY, AUTH_METHOD_PUBKEY_LEN);
+ if (ses.authstate.authtypes & AUTH_TYPE_PASSWORD) {
+ buf_putbyte(typebuf, ',');
+ }
+ }
+
+ if (ses.authstate.authtypes & AUTH_TYPE_PASSWORD) {
+ buf_putbytes(typebuf, AUTH_METHOD_PASSWORD, AUTH_METHOD_PASSWORD_LEN);
+ }
+
+ buf_setpos(typebuf, 0);
+ buf_putstring(ses.writepayload, buf_getptr(typebuf, typebuf->len),
+ typebuf->len);
+ buf_free(typebuf);
+
+ buf_putbyte(ses.writepayload, partial ? 1 : 0);
+ encrypt_packet();
+
+ TRACE(("auth fail: methods %d, '%s'", ses.authstate.authtypes,
+ buf_getptr(typebuf, typebuf->len)));
+
+ if (incrfail) {
+ usleep(300000); /* XXX improve this */
+ ses.authstate.failcount++;
+ }
+
+ if (ses.authstate.failcount >= MAX_AUTH_TRIES) {
+ char * userstr;
+ /* XXX - send disconnect ? */
+ 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' from %s",
+ userstr, svr_ses.addrstring);
+ }
+
+ 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"))
+
+ CHECKCLEARTOWRITE();
+
+ buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_SUCCESS);
+ encrypt_packet();
+
+ ses.authstate.authdone = 1;
+ ses.connecttimeout = 0;
+
+
+ if (ses.authstate.pw->pw_uid == 0) {
+ ses.allowprivport = 1;
+ }
+
+ /* Remove from the list of pre-auth sockets. Should be m_close(), since if
+ * we fail, we might end up leaking connection slots, and disallow new
+ * logins - a nasty situation. */
+ m_close(svr_ses.childpipe);
+
+ TRACE(("leave send_msg_userauth_success"))
+
+}
diff --git a/svr-authpam.c b/svr-authpam.c
new file mode 100644
index 0000000..4e257ae
--- /dev/null
+++ b/svr-authpam.c
@@ -0,0 +1,258 @@
+/*
+ * 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;
+ unsigned int msg_len = 0;
+ unsigned int i = 0;
+
+ const char* message = (*msg)->msg;
+
+ /* make a copy we can strip */
+ char * compare_message = m_strdup(message);
+
+ 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 (compare_message) {
+ TRACE(("message is '%s'", compare_message))
+ } else {
+ TRACE(("null message"))
+ }
+
+
+ /* Make the string lowercase. */
+ msg_len = strlen(compare_message);
+ for (i = 0; i < msg_len; i++) {
+ compare_message[i] = tolower(compare_message[i]);
+ }
+
+ /* If the string ends with ": ", remove the space.
+ ie "login: " vs "login:" */
+ if (msg_len > 2
+ && compare_message[msg_len-2] == ':'
+ && compare_message[msg_len-1] == ' ') {
+ compare_message[msg_len-1] = '\0';
+ }
+
+ switch((*msg)->msg_style) {
+
+ case PAM_PROMPT_ECHO_OFF:
+
+ if (!(strcmp(compare_message, "password:") == 0)) {
+ /* We don't recognise the prompt as asking for a password,
+ so can't handle it. Add more above as required for
+ different pam modules/implementations */
+ dropbear_log(LOG_NOTICE, "PAM unknown prompt %s (no echo)",
+ compare_message);
+ 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(compare_message, "login:" ) == 0)
+ || (strcmp(compare_message, "please enter username:") == 0))) {
+ /* We don't recognise the prompt as asking for a username,
+ so can't handle it. Add more above as required for
+ different pam modules/implementations */
+ dropbear_log(LOG_NOTICE, "PAM unknown prompt %s (with echo)",
+ compare_message);
+ 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;
+ }
+
+ m_free(compare_message);
+ 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_getbool(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 */
diff --git a/svr-authpasswd.c b/svr-authpasswd.c
new file mode 100644
index 0000000..5be1e2a
--- /dev/null
+++ b/svr-authpasswd.c
@@ -0,0 +1,105 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+/* Validates a user password */
+
+#include "includes.h"
+#include "session.h"
+#include "buffer.h"
+#include "dbutil.h"
+#include "auth.h"
+
+#ifdef ENABLE_SVR_PASSWORD_AUTH
+
+/* Process a password auth request, sending success or failure messages as
+ * appropriate */
+void svr_auth_password() {
+
+#ifdef HAVE_SHADOW_H
+ struct spwd *spasswd = NULL;
+#endif
+ char * passwdcrypt = NULL; /* the crypt from /etc/passwd or /etc/shadow */
+ char * testcrypt = NULL; /* crypt generated from the user's password sent */
+ unsigned char * password;
+ unsigned int passwordlen;
+
+ unsigned int changepw;
+
+ passwdcrypt = ses.authstate.pw->pw_passwd;
+#ifdef HAVE_SHADOW_H
+ /* get the shadow password if possible */
+ spasswd = getspnam(ses.authstate.printableuser);
+ if (spasswd != NULL && spasswd->sp_pwdp != NULL) {
+ passwdcrypt = spasswd->sp_pwdp;
+ }
+#endif
+
+#ifdef DEBUG_HACKCRYPT
+ /* debugging crypt for non-root testing with shadows */
+ passwdcrypt = DEBUG_HACKCRYPT;
+#endif
+
+ /* check for empty password - need to do this again here
+ * since the shadow password may differ to that tested
+ * in auth.c */
+ if (passwdcrypt[0] == '\0') {
+ dropbear_log(LOG_WARNING, "user '%s' has blank password, rejected",
+ ses.authstate.printableuser);
+ send_msg_userauth_failure(0, 1);
+ return;
+ }
+
+ /* check if client wants to change password */
+ changepw = buf_getbool(ses.payload);
+ if (changepw) {
+ /* not implemented by this server */
+ send_msg_userauth_failure(0, 1);
+ return;
+ }
+
+ password = buf_getstring(ses.payload, &passwordlen);
+
+ /* the first bytes of passwdcrypt are the salt */
+ testcrypt = crypt((char*)password, passwdcrypt);
+ m_burn(password, passwordlen);
+ m_free(password);
+
+ if (strcmp(testcrypt, passwdcrypt) == 0) {
+ /* successful authentication */
+ dropbear_log(LOG_NOTICE,
+ "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' from %s",
+ ses.authstate.printableuser,
+ svr_ses.addrstring);
+ send_msg_userauth_failure(0, 1);
+ }
+
+}
+
+#endif
diff --git a/svr-authpubkey.c b/svr-authpubkey.c
new file mode 100644
index 0000000..3942bd5
--- /dev/null
+++ b/svr-authpubkey.c
@@ -0,0 +1,347 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+/* Process a pubkey auth request */
+
+#include "includes.h"
+#include "session.h"
+#include "dbutil.h"
+#include "buffer.h"
+#include "signkey.h"
+#include "auth.h"
+#include "ssh.h"
+#include "packet.h"
+#include "algo.h"
+
+#ifdef ENABLE_SVR_PUBKEY_AUTH
+
+#define MIN_AUTHKEYS_LINE 10 /* "ssh-rsa AB" - short but doesn't matter */
+#define MAX_AUTHKEYS_LINE 4200 /* max length of a line in authkeys */
+
+static int checkpubkey(unsigned char* algo, unsigned int algolen,
+ unsigned char* keyblob, unsigned int keybloblen);
+static int checkpubkeyperms();
+static void send_msg_userauth_pk_ok(unsigned char* algo, unsigned int algolen,
+ unsigned char* keyblob, unsigned int keybloblen);
+static int checkfileperm(char * filename);
+
+/* process a pubkey auth request, sending success or failure message as
+ * appropriate */
+void svr_auth_pubkey() {
+
+ unsigned char testkey; /* whether we're just checking if a key is usable */
+ unsigned char* algo = NULL; /* pubkey algo */
+ unsigned int algolen;
+ unsigned char* keyblob = NULL;
+ unsigned int keybloblen;
+ buffer * signbuf = NULL;
+ sign_key * key = NULL;
+ char* fp = NULL;
+ int type = -1;
+
+ TRACE(("enter pubkeyauth"))
+
+ /* 0 indicates user just wants to check if key can be used, 1 is an
+ * actual attempt*/
+ testkey = (buf_getbool(ses.payload) == 0);
+
+ algo = buf_getstring(ses.payload, &algolen);
+ keybloblen = buf_getint(ses.payload);
+ keyblob = buf_getptr(ses.payload, keybloblen);
+
+ /* check if the key is valid */
+ if (checkpubkey(algo, algolen, keyblob, keybloblen) == DROPBEAR_FAILURE) {
+ send_msg_userauth_failure(0, 0);
+ goto out;
+ }
+
+ /* let them know that the key is ok to use */
+ if (testkey) {
+ send_msg_userauth_pk_ok(algo, algolen, keyblob, keybloblen);
+ goto out;
+ }
+
+ /* now we can actually verify the signature */
+
+ /* get the key */
+ key = new_sign_key();
+ type = DROPBEAR_SIGNKEY_ANY;
+ if (buf_get_pub_key(ses.payload, key, &type) == DROPBEAR_FAILURE) {
+ send_msg_userauth_failure(0, 1);
+ goto out;
+ }
+
+ /* create the data which has been signed - this a string containing
+ * session_id, concatenated with the payload packet up to the signature */
+ signbuf = buf_new(ses.payload->pos + 4 + SHA1_HASH_SIZE);
+ buf_putstring(signbuf, ses.session_id, SHA1_HASH_SIZE);
+ buf_putbytes(signbuf, ses.payload->data, ses.payload->pos);
+ buf_setpos(signbuf, 0);
+
+ /* ... and finally verify the signature */
+ fp = sign_key_fingerprint(keyblob, keybloblen);
+ 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 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 from %s",
+ ses.authstate.printableuser, fp, svr_ses.addrstring);
+ send_msg_userauth_failure(0, 1);
+ }
+ m_free(fp);
+
+out:
+ /* cleanup stuff */
+ if (signbuf) {
+ buf_free(signbuf);
+ }
+ if (algo) {
+ m_free(algo);
+ }
+ if (key) {
+ sign_key_free(key);
+ key = NULL;
+ }
+ TRACE(("leave pubkeyauth"))
+}
+
+/* Reply that the key is valid for auth, this is sent when the user sends
+ * a straight copy of their pubkey to test, to avoid having to perform
+ * expensive signing operations with a worthless key */
+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"))
+ CHECKCLEARTOWRITE();
+
+ buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_PK_OK);
+ buf_putstring(ses.writepayload, algo, algolen);
+ buf_putstring(ses.writepayload, keyblob, keybloblen);
+
+ encrypt_packet();
+ TRACE(("leave send_msg_userauth_pk_ok"))
+
+}
+
+/* Checks whether a specified publickey (and associated algorithm) is an
+ * acceptable key for authentication */
+/* Returns DROPBEAR_SUCCESS if key is ok for auth, DROPBEAR_FAILURE otherwise */
+static int checkpubkey(unsigned char* algo, unsigned int algolen,
+ unsigned char* keyblob, unsigned int keybloblen) {
+
+ FILE * authfile = NULL;
+ char * filename = NULL;
+ int ret = DROPBEAR_FAILURE;
+ buffer * line = NULL;
+ unsigned int len, pos;
+
+ 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' 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"))
+ goto out;
+ }
+
+ /* we don't need to check pw and pw_dir for validity, since
+ * its been done in checkpubkeyperms. */
+ len = strlen(ses.authstate.pw->pw_dir);
+ /* allocate max required pathname storage,
+ * = path + "/.ssh/authorized_keys" + '\0' = pathlen + 22 */
+ filename = m_malloc(len + 22);
+ snprintf(filename, len + 22, "%s/.ssh/authorized_keys",
+ ses.authstate.pw->pw_dir);
+
+ /* open the file */
+ authfile = fopen(filename, "r");
+ if (authfile == NULL) {
+ goto out;
+ }
+ TRACE(("checkpubkey: opened authorized_keys OK"))
+
+ line = buf_new(MAX_AUTHKEYS_LINE);
+
+ /* iterate through the lines */
+ do {
+
+ if (buf_getline(line, authfile) == DROPBEAR_FAILURE) {
+ /* EOF reached */
+ TRACE(("checkpubkey: authorized_keys EOF reached"))
+ break;
+ }
+
+ if (line->len < MIN_AUTHKEYS_LINE) {
+ TRACE(("checkpubkey: line too short"))
+ continue; /* line is too short for it to be a valid key */
+ }
+
+ /* check the key type - this also stops us from using keys
+ * which have options with them */
+ if (strncmp(buf_getptr(line, algolen), algo, algolen) != 0) {
+ continue;
+ }
+ buf_incrpos(line, algolen);
+
+ /* check for space (' ') character */
+ if (buf_getbyte(line) != ' ') {
+ TRACE(("checkpubkey: space character expected, isn't there"))
+ continue;
+ }
+
+ /* truncate the line at the space after the base64 data */
+ pos = line->pos;
+ for (len = 0; line->pos < line->len; len++) {
+ if (buf_getbyte(line) == ' ') break;
+ }
+ buf_setpos(line, pos);
+ buf_setlen(line, line->pos + 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) {
+ break;
+ }
+
+ /* We continue to the next line otherwise */
+
+ } while (1);
+
+out:
+ if (authfile) {
+ fclose(authfile);
+ }
+ if (line) {
+ buf_free(line);
+ }
+ m_free(filename);
+ TRACE(("leave checkpubkey: ret=%d", ret))
+ return ret;
+}
+
+
+/* Returns DROPBEAR_SUCCESS if file permissions for pubkeys are ok,
+ * DROPBEAR_FAILURE otherwise.
+ * Checks that the user's homedir, ~/.ssh, and
+ * ~/.ssh/authorized_keys are all owned by either root or the user, and are
+ * g-w, o-w */
+static int checkpubkeyperms() {
+
+ char* filename = NULL;
+ int ret = DROPBEAR_FAILURE;
+ unsigned int len;
+
+ TRACE(("enter checkpubkeyperms"))
+
+ if (ses.authstate.pw->pw_dir == NULL) {
+ goto out;
+ }
+
+ if ((len = strlen(ses.authstate.pw->pw_dir)) == 0) {
+ goto out;
+ }
+
+ /* allocate max required pathname storage,
+ * = path + "/.ssh/authorized_keys" + '\0' = pathlen + 22 */
+ filename = m_malloc(len + 22);
+ strncpy(filename, ses.authstate.pw->pw_dir, len+1);
+
+ /* check ~ */
+ if (checkfileperm(filename) != DROPBEAR_SUCCESS) {
+ goto out;
+ }
+
+ /* check ~/.ssh */
+ strncat(filename, "/.ssh", 5); /* strlen("/.ssh") == 5 */
+ if (checkfileperm(filename) != DROPBEAR_SUCCESS) {
+ goto out;
+ }
+
+ /* now check ~/.ssh/authorized_keys */
+ strncat(filename, "/authorized_keys", 16);
+ if (checkfileperm(filename) != DROPBEAR_SUCCESS) {
+ goto out;
+ }
+
+ /* file looks ok, return success */
+ ret = DROPBEAR_SUCCESS;
+
+out:
+ m_free(filename);
+
+ TRACE(("leave checkpubkeyperms"))
+ return ret;
+}
+
+/* Checks that a file is owned by the user or root, and isn't writable by
+ * group or other */
+/* returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
+static int checkfileperm(char * filename) {
+ struct stat filestat;
+ int badperm = 0;
+
+ TRACE(("enter checkfileperm(%s)", filename))
+
+ if (stat(filename, &filestat) != 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) {
+ badperm = 1;
+ TRACE(("wrong ownership"))
+ }
+ /* check permissions - don't want group or others +w */
+ if (filestat.st_mode & (S_IWGRP | S_IWOTH)) {
+ badperm = 1;
+ TRACE(("wrong perms"))
+ }
+ if (badperm) {
+ if (!ses.authstate.perm_warn) {
+ ses.authstate.perm_warn = 1;
+ dropbear_log(LOG_INFO, "%s must be owned by user or root, and not writable by others", filename);
+ }
+ TRACE(("leave checkfileperm: failure perms/owner"))
+ return DROPBEAR_FAILURE;
+ }
+
+ TRACE(("leave checkfileperm: success"))
+ return DROPBEAR_SUCCESS;
+}
+
+
+#endif
diff --git a/svr-chansession.c b/svr-chansession.c
new file mode 100644
index 0000000..0916e7e
--- /dev/null
+++ b/svr-chansession.c
@@ -0,0 +1,1003 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+#include "includes.h"
+#include "packet.h"
+#include "buffer.h"
+#include "session.h"
+#include "dbutil.h"
+#include "channel.h"
+#include "chansession.h"
+#include "sshpty.h"
+#include "termcodes.h"
+#include "ssh.h"
+#include "random.h"
+#include "utmp.h"
+#include "x11fwd.h"
+#include "agentfwd.h"
+#include "runopts.h"
+
+/* Handles sessions (either shells or programs) requested by the client */
+
+static int sessioncommand(struct Channel *channel, struct ChanSess *chansess,
+ int iscmd, int issubsys);
+static int sessionpty(struct ChanSess * chansess);
+static int sessionsignal(struct ChanSess *chansess);
+static int noptycommand(struct Channel *channel, struct ChanSess *chansess);
+static int ptycommand(struct Channel *channel, struct ChanSess *chansess);
+static int sessionwinchange(struct ChanSess *chansess);
+static void execchild(struct ChanSess *chansess);
+static void addchildpid(struct ChanSess *chansess, pid_t pid);
+static void sesssigchild_handler(int val);
+static void closechansess(struct Channel *channel);
+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);
+
+
+/* required to clear environment */
+extern char** environ;
+
+static int sesscheckclose(struct Channel *channel) {
+ struct ChanSess *chansess = (struct ChanSess*)channel->typedata;
+ return chansess->exit.exitpid >= 0;
+}
+
+/* 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 sigaction sa_chld;
+ struct exitinfo *exit = NULL;
+
+ 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) {
+
+ 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"))
+}
+
+/* send the exit status or the signal causing termination for a session */
+/* XXX server */
+static void send_exitsignalstatus(struct Channel *channel) {
+
+ struct ChanSess *chansess = (struct ChanSess*)channel->typedata;
+
+ if (chansess->exit.exitpid >= 0) {
+ if (chansess->exit.exitsignal > 0) {
+ send_msg_chansess_exitsignal(channel, chansess);
+ } else {
+ send_msg_chansess_exitstatus(channel, chansess);
+ }
+ }
+}
+
+/* send the exitstatus to the client */
+static void send_msg_chansess_exitstatus(struct Channel * channel,
+ struct ChanSess * chansess) {
+
+ dropbear_assert(chansess->exit.exitpid != -1);
+ dropbear_assert(chansess->exit.exitsignal == -1);
+
+ CHECKCLEARTOWRITE();
+
+ buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_REQUEST);
+ 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->exit.exitstatus);
+
+ encrypt_packet();
+
+}
+
+/* send the signal causing the exit to the client */
+static void send_msg_chansess_exitsignal(struct Channel * channel,
+ struct ChanSess * chansess) {
+
+ int i;
+ char* signame = NULL;
+
+ dropbear_assert(chansess->exit.exitpid != -1);
+ dropbear_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->exit.exitsignal) {
+ signame = signames[i].name;
+ break;
+ }
+ }
+
+ if (signame == NULL) {
+ return;
+ }
+
+ buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_REQUEST);
+ buf_putint(ses.writepayload, channel->remotechan);
+ 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->exit.exitcore);
+ buf_putstring(ses.writepayload, "", 0); /* error msg */
+ buf_putstring(ses.writepayload, "", 0); /* lang */
+
+ encrypt_packet();
+}
+
+/* set up a session channel */
+static int newchansess(struct Channel *channel) {
+
+ struct ChanSess *chansess;
+
+ dropbear_assert(channel->typedata == NULL);
+
+ chansess = (struct ChanSess*)m_malloc(sizeof(struct ChanSess));
+ chansess->cmd = NULL;
+ chansess->pid = 0;
+
+ /* pty details */
+ chansess->master = -1;
+ chansess->slave = -1;
+ chansess->tty = NULL;
+ chansess->term = NULL;
+
+ chansess->exit.exitpid = -1;
+
+ channel->typedata = chansess;
+
+#ifndef DISABLE_X11FWD
+ chansess->x11listener = NULL;
+ chansess->x11authprot = NULL;
+ chansess->x11authcookie = NULL;
+#endif
+
+#ifndef DISABLE_AGENTFWD
+ chansess->agentlistener = NULL;
+ chansess->agentfile = NULL;
+ chansess->agentdir = NULL;
+#endif
+
+ return 0;
+
+}
+
+/* clean a session channel */
+static void closechansess(struct Channel *channel) {
+
+ struct ChanSess *chansess;
+ unsigned int i;
+ struct logininfo *li;
+
+ chansess = (struct ChanSess*)channel->typedata;
+
+ send_exitsignalstatus(channel);
+
+ TRACE(("enter closechansess"))
+ if (chansess == NULL) {
+ TRACE(("leave closechansess: chansess == NULL"))
+ return;
+ }
+
+ m_free(chansess->cmd);
+ m_free(chansess->term);
+
+ if (chansess->tty) {
+ /* write the utmp/wtmp login record */
+ li = login_alloc_entry(chansess->pid, ses.authstate.username,
+ ses.remotehost, chansess->tty);
+ login_logout(li);
+ login_free_entry(li);
+
+ pty_release(chansess->tty);
+ m_free(chansess->tty);
+ }
+
+#ifndef DISABLE_X11FWD
+ x11cleanup(chansess);
+#endif
+
+#ifndef DISABLE_AGENTFWD
+ agentcleanup(chansess);
+#endif
+
+ /* clear child pid entries */
+ for (i = 0; i < svr_ses.childpidsize; i++) {
+ if (svr_ses.childpids[i].chansess == chansess) {
+ dropbear_assert(svr_ses.childpids[i].pid > 0);
+ 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;
+ }
+ }
+
+ m_free(chansess);
+
+ TRACE(("leave closechansess"))
+}
+
+/* Handle requests for a channel. These can be execution requests,
+ * or x11/authagent forwarding. These are passed to appropriate handlers */
+static void chansessionrequest(struct Channel *channel) {
+
+ unsigned char * type = NULL;
+ unsigned int typelen;
+ unsigned char wantreply;
+ int ret = 1;
+ struct ChanSess *chansess;
+
+ TRACE(("enter chansessionrequest"))
+
+ type = buf_getstring(ses.payload, &typelen);
+ wantreply = buf_getbool(ses.payload);
+
+ if (typelen > MAX_NAME_LEN) {
+ TRACE(("leave chansessionrequest: type too long")) /* XXX send error?*/
+ goto out;
+ }
+
+ chansess = (struct ChanSess*)channel->typedata;
+ dropbear_assert(chansess != NULL);
+ TRACE(("type is %s", type))
+
+ if (strcmp(type, "window-change") == 0) {
+ ret = sessionwinchange(chansess);
+ } else if (strcmp(type, "shell") == 0) {
+ ret = sessioncommand(channel, chansess, 0, 0);
+ } else if (strcmp(type, "pty-req") == 0) {
+ ret = sessionpty(chansess);
+ } else if (strcmp(type, "exec") == 0) {
+ ret = sessioncommand(channel, chansess, 1, 0);
+ } else if (strcmp(type, "subsystem") == 0) {
+ ret = sessioncommand(channel, chansess, 1, 1);
+#ifndef DISABLE_X11FWD
+ } else if (strcmp(type, "x11-req") == 0) {
+ ret = x11req(chansess);
+#endif
+#ifndef DISABLE_AGENTFWD
+ } else if (strcmp(type, "auth-agent-req@openssh.com") == 0) {
+ ret = agentreq(chansess);
+#endif
+ } else if (strcmp(type, "signal") == 0) {
+ ret = sessionsignal(chansess);
+ } else {
+ /* etc, todo "env", "subsystem" */
+ }
+
+out:
+
+ if (wantreply) {
+ if (ret == DROPBEAR_SUCCESS) {
+ send_msg_channel_success(channel);
+ } else {
+ send_msg_channel_failure(channel);
+ }
+ }
+
+ m_free(type);
+ TRACE(("leave chansessionrequest"))
+}
+
+
+/* Send a signal to a session's process as requested by the client*/
+static int sessionsignal(struct ChanSess *chansess) {
+
+ int sig = 0;
+ unsigned char* signame = NULL;
+ int i;
+
+ if (chansess->pid == 0) {
+ /* haven't got a process pid yet */
+ return DROPBEAR_FAILURE;
+ }
+
+ signame = buf_getstring(ses.payload, NULL);
+
+ i = 0;
+ while (signames[i].name != 0) {
+ if (strcmp(signames[i].name, signame) == 0) {
+ sig = signames[i].signal;
+ break;
+ }
+ i++;
+ }
+
+ m_free(signame);
+
+ if (sig == 0) {
+ /* failed */
+ return DROPBEAR_FAILURE;
+ }
+
+ if (kill(chansess->pid, sig) < 0) {
+ return DROPBEAR_FAILURE;
+ }
+
+ return DROPBEAR_SUCCESS;
+}
+
+/* Let the process know that the window size has changed, as notified from the
+ * client. Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
+static int sessionwinchange(struct ChanSess *chansess) {
+
+ int termc, termr, termw, termh;
+
+ if (chansess->master < 0) {
+ /* haven't got a pty yet */
+ return DROPBEAR_FAILURE;
+ }
+
+ termc = buf_getint(ses.payload);
+ termr = buf_getint(ses.payload);
+ termw = buf_getint(ses.payload);
+ termh = buf_getint(ses.payload);
+
+ pty_change_window_size(chansess->master, termr, termc, termw, termh);
+
+ return DROPBEAR_FAILURE;
+}
+
+static void get_termmodes(struct ChanSess *chansess) {
+
+ struct termios termio;
+ unsigned char opcode;
+ unsigned int value;
+ const struct TermCode * termcode;
+ unsigned int len;
+
+ TRACE(("enter get_termmodes"))
+
+ /* Term modes */
+ /* We'll ignore errors and continue if we can't set modes.
+ * We're ignoring baud rates since they seem evil */
+ if (tcgetattr(chansess->master, &termio) == -1) {
+ return;
+ }
+
+ len = buf_getint(ses.payload);
+ TRACE(("term mode str %d p->l %d p->p %d",
+ len, ses.payload->len , ses.payload->pos));
+ if (len != ses.payload->len - ses.payload->pos) {
+ dropbear_exit("bad term mode string");
+ }
+
+ if (len == 0) {
+ TRACE(("leave get_termmodes: empty terminal modes string"))
+ return;
+ }
+
+ while (((opcode = buf_getbyte(ses.payload)) != 0x00) && opcode <= 159) {
+
+ /* must be before checking type, so that value is consumed even if
+ * we don't use it */
+ value = buf_getint(ses.payload);
+
+ /* handle types of code */
+ if (opcode > MAX_TERMCODE) {
+ continue;
+ }
+ termcode = &termcodes[(unsigned int)opcode];
+
+
+ switch (termcode->type) {
+
+ case TERMCODE_NONE:
+ break;
+
+ case TERMCODE_CONTROLCHAR:
+ termio.c_cc[termcode->mapcode] = value;
+ break;
+
+ case TERMCODE_INPUT:
+ if (value) {
+ termio.c_iflag |= termcode->mapcode;
+ } else {
+ termio.c_iflag &= ~(termcode->mapcode);
+ }
+ break;
+
+ case TERMCODE_OUTPUT:
+ if (value) {
+ termio.c_oflag |= termcode->mapcode;
+ } else {
+ termio.c_oflag &= ~(termcode->mapcode);
+ }
+ break;
+
+ case TERMCODE_LOCAL:
+ if (value) {
+ termio.c_lflag |= termcode->mapcode;
+ } else {
+ termio.c_lflag &= ~(termcode->mapcode);
+ }
+ break;
+
+ case TERMCODE_CONTROL:
+ if (value) {
+ termio.c_cflag |= termcode->mapcode;
+ } else {
+ termio.c_cflag &= ~(termcode->mapcode);
+ }
+ break;
+
+ }
+ }
+ if (tcsetattr(chansess->master, TCSANOW, &termio) < 0) {
+ dropbear_log(LOG_INFO, "error setting terminal attributes");
+ }
+ TRACE(("leave get_termmodes"))
+}
+
+/* Set up a session pty which will be used to execute the shell or program.
+ * The pty is allocated now, and kept for when the shell/program executes.
+ * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
+static int sessionpty(struct ChanSess * chansess) {
+
+ unsigned int termlen;
+ unsigned char namebuf[65];
+
+ 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"))
+ return DROPBEAR_FAILURE;
+ }
+
+ /* allocate the pty */
+ 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"))
+ return DROPBEAR_FAILURE;
+ }
+
+ chansess->tty = (char*)m_strdup(namebuf);
+ if (!chansess->tty) {
+ dropbear_exit("out of memory"); /* TODO disconnect */
+ }
+
+ pty_setowner(ses.authstate.pw, chansess->tty);
+
+ /* Set up the rows/col counts */
+ sessionwinchange(chansess);
+
+ /* Read the terminal modes */
+ get_termmodes(chansess);
+
+ TRACE(("leave sessionpty"))
+ return DROPBEAR_SUCCESS;
+}
+
+/* Handle a command request from the client. This is used for both shell
+ * and command-execution requests, and passes the command to
+ * noptycommand or ptycommand as appropriate.
+ * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
+static int sessioncommand(struct Channel *channel, struct ChanSess *chansess,
+ int iscmd, int issubsys) {
+
+ unsigned int cmdlen;
+ int ret;
+
+ TRACE(("enter sessioncommand"))
+
+ if (chansess->cmd != NULL) {
+ /* Note that only one command can _succeed_. The client might try
+ * one command (which fails), then try another. Ie fallback
+ * from sftp to scp */
+ return DROPBEAR_FAILURE;
+ }
+
+ if (iscmd) {
+ /* "exec" */
+ chansess->cmd = buf_getstring(ses.payload, &cmdlen);
+
+ if (cmdlen > MAX_CMD_LEN) {
+ m_free(chansess->cmd);
+ /* TODO - send error - too long ? */
+ return DROPBEAR_FAILURE;
+ }
+ if (issubsys) {
+#ifdef SFTPSERVER_PATH
+ if ((cmdlen == 4) && strncmp(chansess->cmd, "sftp", 4) == 0) {
+ m_free(chansess->cmd);
+ chansess->cmd = m_strdup(SFTPSERVER_PATH);
+ } else
+#endif
+ {
+ m_free(chansess->cmd);
+ return DROPBEAR_FAILURE;
+ }
+ }
+ }
+
+ if (chansess->term == NULL) {
+ /* no pty */
+ ret = noptycommand(channel, chansess);
+ } else {
+ /* want pty */
+ ret = ptycommand(channel, chansess);
+ }
+
+ if (ret == DROPBEAR_FAILURE) {
+ m_free(chansess->cmd);
+ }
+ return ret;
+}
+
+/* Execute a command and set up redirection of stdin/stdout/stderr without a
+ * pty.
+ * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
+static int noptycommand(struct Channel *channel, struct ChanSess *chansess) {
+
+ int infds[2];
+ int outfds[2];
+ int errfds[2];
+ pid_t pid;
+ unsigned int i;
+
+ TRACE(("enter noptycommand"))
+
+ /* redirect stdin/stdout/stderr */
+ if (pipe(infds) != 0)
+ return DROPBEAR_FAILURE;
+ if (pipe(outfds) != 0)
+ return DROPBEAR_FAILURE;
+ if (pipe(errfds) != 0)
+ return DROPBEAR_FAILURE;
+
+#ifdef __uClinux__
+ pid = vfork();
+#else
+ pid = fork();
+#endif
+
+ if (pid < 0)
+ return DROPBEAR_FAILURE;
+
+ if (!pid) {
+ /* child */
+
+ /* redirect stdin/stdout */
+#define FDIN 0
+#define FDOUT 1
+ 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"))
+ return DROPBEAR_FAILURE;
+ }
+
+ close(infds[FDOUT]);
+ close(infds[FDIN]);
+ close(outfds[FDIN]);
+ close(outfds[FDOUT]);
+ close(errfds[FDIN]);
+ close(errfds[FDOUT]);
+
+ execchild(chansess);
+ /* not reached */
+
+ } else {
+ /* parent */
+ TRACE(("continue noptycommand: parent"))
+ chansess->pid = 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]);
+ channel->writefd = infds[FDOUT];
+ channel->readfd = outfds[FDIN];
+ channel->errfd = errfds[FDIN];
+ ses.maxfd = MAX(ses.maxfd, channel->writefd);
+ ses.maxfd = MAX(ses.maxfd, channel->readfd);
+ ses.maxfd = MAX(ses.maxfd, channel->errfd);
+
+ setnonblocking(channel->readfd);
+ setnonblocking(channel->writefd);
+ setnonblocking(channel->errfd);
+
+ }
+#undef FDIN
+#undef FDOUT
+
+ TRACE(("leave noptycommand"))
+ return DROPBEAR_SUCCESS;
+}
+
+/* Execute a command or shell within a pty environment, and set up
+ * redirection as appropriate.
+ * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
+static int ptycommand(struct Channel *channel, struct ChanSess *chansess) {
+
+ pid_t pid;
+ struct logininfo *li = NULL;
+#ifdef DO_MOTD
+ buffer * motdbuf = NULL;
+ int len;
+ struct stat sb;
+ char *hushpath = NULL;
+#endif
+
+ TRACE(("enter ptycommand"))
+
+ /* we need to have a pty allocated */
+ if (chansess->master == -1 || chansess->tty == NULL) {
+ dropbear_log(LOG_WARNING, "no pty was allocated, couldn't execute");
+ return DROPBEAR_FAILURE;
+ }
+
+#ifdef __uClinux__
+ pid = vfork();
+#else
+ pid = fork();
+#endif
+ if (pid < 0)
+ return DROPBEAR_FAILURE;
+
+ if (pid == 0) {
+ /* child */
+
+ /* redirect stdin/stdout/stderr */
+ close(chansess->master);
+
+ pty_make_controlling_tty(&chansess->slave, chansess->tty);
+
+ 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"))
+ return DROPBEAR_FAILURE;
+ }
+
+ close(chansess->slave);
+
+ /* write the utmp/wtmp login record - must be after changing the
+ * terminal used for stdout with the dup2 above */
+ li= login_alloc_entry(getpid(), ses.authstate.username,
+ ses.remotehost, chansess->tty);
+ login_login(li);
+ login_free_entry(li);
+
+ m_free(chansess->tty);
+
+#ifdef DO_MOTD
+ if (svr_opts.domotd) {
+ /* don't show the motd if ~/.hushlogin exists */
+
+ /* 11 == strlen("/hushlogin\0") */
+ len = strlen(ses.authstate.pw->pw_dir) + 11;
+
+ hushpath = m_malloc(len);
+ snprintf(hushpath, len, "%s/hushlogin", ses.authstate.pw->pw_dir);
+
+ if (stat(hushpath, &sb) < 0) {
+ /* more than a screenful is stupid IMHO */
+ motdbuf = buf_new(80 * 25);
+ if (buf_readfile(motdbuf, MOTD_FILENAME) == DROPBEAR_SUCCESS) {
+ buf_setpos(motdbuf, 0);
+ while (motdbuf->pos != motdbuf->len) {
+ len = motdbuf->len - motdbuf->pos;
+ len = write(STDOUT_FILENO,
+ buf_getptr(motdbuf, len), len);
+ buf_incrpos(motdbuf, len);
+ }
+ }
+ buf_free(motdbuf);
+ }
+ m_free(hushpath);
+ }
+#endif /* DO_MOTD */
+
+ execchild(chansess);
+ /* not reached */
+
+ } else {
+ /* parent */
+ TRACE(("continue ptycommand: parent"))
+ chansess->pid = pid;
+
+ /* add a child pid */
+ addchildpid(chansess, pid);
+
+ close(chansess->slave);
+ channel->writefd = chansess->master;
+ channel->readfd = chansess->master;
+ /* don't need to set stderr here */
+ ses.maxfd = MAX(ses.maxfd, chansess->master);
+
+ setnonblocking(chansess->master);
+
+ }
+
+ TRACE(("leave ptycommand"))
+ return DROPBEAR_SUCCESS;
+}
+
+/* Add the pid of a child to the list for exit-handling */
+static void addchildpid(struct ChanSess *chansess, pid_t pid) {
+
+ unsigned int i;
+ for (i = 0; i < svr_ses.childpidsize; i++) {
+ if (svr_ses.childpids[i].pid == -1) {
+ break;
+ }
+ }
+
+ /* need to increase size */
+ if (i == svr_ses.childpidsize) {
+ svr_ses.childpids = (struct ChildPid*)m_realloc(svr_ses.childpids,
+ sizeof(struct ChildPid) * (svr_ses.childpidsize+1));
+ svr_ses.childpidsize++;
+ }
+
+ svr_ses.childpids[i].pid = pid;
+ svr_ses.childpids[i].chansess = chansess;
+
+}
+
+/* Clean up, drop to user privileges, set up the environment and execute
+ * the command/shell. This function does not return. */
+static void execchild(struct ChanSess *chansess) {
+
+ char *argv[4];
+ char * usershell = NULL;
+ char * baseshell = NULL;
+ unsigned int i;
+
+ /* with uClinux we'll have vfork()ed, so don't want to overwrite the
+ * hostkey. can't think of a workaround to clear it */
+#ifndef __uClinux__
+ /* wipe the hostkey */
+ sign_key_free(svr_opts.hostkey);
+ svr_opts.hostkey = NULL;
+
+ /* overwrite the prng state */
+ reseedrandom();
+#endif
+
+ /* close file descriptors except stdin/stdout/stderr
+ * Need to be sure FDs are closed here to avoid reading files as root */
+ for (i = 3; i <= (unsigned int)ses.maxfd; i++) {
+ m_close(i);
+ }
+
+ /* clear environment */
+ /* if we're debugging using valgrind etc, we need to keep the LD_PRELOAD
+ * etc. This is hazardous, so should only be used for debugging. */
+#ifndef DEBUG_VALGRIND
+#ifdef HAVE_CLEARENV
+ clearenv();
+#else /* don't HAVE_CLEARENV */
+ /* Yay for posix. */
+ if (environ) {
+ environ[0] = NULL;
+ }
+#endif /* HAVE_CLEARENV */
+#endif /* DEBUG_VALGRIND */
+
+ /* We can only change uid/gid as root ... */
+ if (getuid() == 0) {
+
+ if ((setgid(ses.authstate.pw->pw_gid) < 0) ||
+ (initgroups(ses.authstate.pw->pw_name,
+ ses.authstate.pw->pw_gid) < 0)) {
+ dropbear_exit("error changing user group");
+ }
+ if (setuid(ses.authstate.pw->pw_uid) < 0) {
+ dropbear_exit("error changing user");
+ }
+ } else {
+ /* ... but if the daemon is the same uid as the requested uid, we don't
+ * need to */
+
+ /* XXX - there is a minor issue here, in that if there are multiple
+ * usernames with the same uid, but differing groups, then the
+ * differing groups won't be set (as with initgroups()). The solution
+ * is for the sysadmin not to give out the UID twice */
+ if (getuid() != ses.authstate.pw->pw_uid) {
+ dropbear_exit("couldn't change user as non-root");
+ }
+ }
+
+ /* an empty shell should be interpreted as "/bin/sh" */
+ if (ses.authstate.pw->pw_shell[0] == '\0') {
+ usershell = "/bin/sh";
+ } else {
+ usershell = ses.authstate.pw->pw_shell;
+ }
+
+ /* set env vars */
+ addnewvar("USER", ses.authstate.pw->pw_name);
+ addnewvar("LOGNAME", ses.authstate.pw->pw_name);
+ addnewvar("HOME", ses.authstate.pw->pw_dir);
+ addnewvar("SHELL", usershell);
+ if (chansess->term != NULL) {
+ addnewvar("TERM", chansess->term);
+ }
+
+ /* change directory */
+ if (chdir(ses.authstate.pw->pw_dir) < 0) {
+ dropbear_exit("error changing directory");
+ }
+
+#ifndef DISABLE_X11FWD
+ /* set up X11 forwarding if enabled */
+ x11setauth(chansess);
+#endif
+#ifndef DISABLE_AGENTFWD
+ /* set up agent env variable */
+ agentset(chansess);
+#endif
+
+ /* Re-enable SIGPIPE for the executed process */
+ if (signal(SIGPIPE, SIG_DFL) == SIG_ERR) {
+ dropbear_exit("signal() error");
+ }
+
+ baseshell = basename(usershell);
+
+ if (chansess->cmd != NULL) {
+ argv[0] = baseshell;
+ } else {
+ /* a login shell should be "-bash" for "/bin/bash" etc */
+ int len = strlen(baseshell) + 2; /* 2 for "-" */
+ argv[0] = (char*)m_malloc(len);
+ snprintf(argv[0], len, "-%s", baseshell);
+ }
+
+ if (chansess->cmd != NULL) {
+ argv[1] = "-c";
+ argv[2] = chansess->cmd;
+ argv[3] = NULL;
+ } else {
+ /* construct a shell of the form "-bash" etc */
+ argv[1] = NULL;
+ }
+
+ execv(usershell, argv);
+
+ /* only reached on error */
+ dropbear_exit("child failed");
+}
+
+const struct ChanType svrchansess = {
+ 0, /* sepfds */
+ "session", /* name */
+ newchansess, /* inithandler */
+ sesscheckclose, /* checkclosehandler */
+ chansessionrequest, /* reqhandler */
+ closechansess, /* closehandler */
+};
+
+
+/* Set up the general chansession environment, in particular child-exit
+ * handling */
+void svr_chansessinitialise() {
+
+ struct sigaction sa_chld;
+
+ /* single child process intially */
+ svr_ses.childpids = (struct ChildPid*)m_malloc(sizeof(struct ChildPid));
+ 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) {
+ dropbear_exit("signal() error");
+ }
+
+}
+
+/* add a new environment variable, allocating space for the entry */
+void addnewvar(const char* param, const char* var) {
+
+ char* newvar = NULL;
+ int plen, vlen;
+
+ plen = strlen(param);
+ vlen = strlen(var);
+
+ newvar = m_malloc(plen + vlen + 2); /* 2 is for '=' and '\0' */
+ memcpy(newvar, param, plen);
+ newvar[plen] = '=';
+ memcpy(&newvar[plen+1], var, vlen);
+ newvar[plen+vlen+1] = '\0';
+ if (putenv(newvar) < 0) {
+ dropbear_exit("environ error");
+ }
+}
diff --git a/svr-kex.c b/svr-kex.c
new file mode 100644
index 0000000..a9954bb
--- /dev/null
+++ b/svr-kex.c
@@ -0,0 +1,104 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * Copyright (c) 2002,2003 Matt Johnston
+ * Copyright (c) 2004 by Mihnea Stoenescu
+ * 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 "algo.h"
+#include "buffer.h"
+#include "session.h"
+#include "kex.h"
+#include "ssh.h"
+#include "packet.h"
+#include "bignum.h"
+#include "random.h"
+#include "runopts.h"
+
+
+static void send_msg_kexdh_reply(mp_int *dh_e);
+
+/* Handle a diffie-hellman key exchange initialisation. This involves
+ * calculating a session key reply value, and corresponding hash. These
+ * are carried out by send_msg_kexdh_reply(). recv_msg_kexdh_init() calls
+ * that function, then brings the new keys into use */
+void recv_msg_kexdh_init() {
+
+ DEF_MP_INT(dh_e);
+
+ TRACE(("enter recv_msg_kexdh_init"))
+ if (!ses.kexstate.recvkexinit) {
+ dropbear_exit("Premature kexdh_init message received");
+ }
+
+ m_mp_init(&dh_e);
+ buf_getmpint(ses.payload, &dh_e);
+
+ send_msg_kexdh_reply(&dh_e);
+
+ mp_clear(&dh_e);
+
+ send_msg_newkeys();
+ ses.requirenext = SSH_MSG_NEWKEYS;
+ TRACE(("leave recv_msg_kexdh_init"))
+}
+
+/* Generate our side of the diffie-hellman key exchange value (dh_f), and
+ * calculate the session key using the diffie-hellman algorithm. Following
+ * that, the session hash is calculated, and signed with RSA or DSS. The
+ * result is sent to the client.
+ *
+ * See the ietf-secsh-transport draft, section 6, for details */
+static void send_msg_kexdh_reply(mp_int *dh_e) {
+
+ DEF_MP_INT(dh_y);
+ DEF_MP_INT(dh_f);
+
+ TRACE(("enter send_msg_kexdh_reply"))
+ m_mp_init_multi(&dh_y, &dh_f, NULL);
+
+ gen_kexdh_vals(&dh_f, &dh_y);
+
+ kexdh_comb_key(&dh_f, &dh_y, dh_e, svr_opts.hostkey);
+ mp_clear(&dh_y);
+
+ /* we can start creating the kexdh_reply packet */
+ CHECKCLEARTOWRITE();
+ buf_putbyte(ses.writepayload, SSH_MSG_KEXDH_REPLY);
+ buf_put_pub_key(ses.writepayload, svr_opts.hostkey,
+ ses.newkeys->algo_hostkey);
+
+ /* put f */
+ buf_putmpint(ses.writepayload, &dh_f);
+ mp_clear(&dh_f);
+
+ /* calc the signature */
+ buf_put_sign(ses.writepayload, svr_opts.hostkey,
+ ses.newkeys->algo_hostkey, ses.hash, SHA1_HASH_SIZE);
+
+ /* the SSH_MSG_KEXDH_REPLY is done */
+ encrypt_packet();
+
+ TRACE(("leave send_msg_kexdh_reply"))
+}
+
diff --git a/svr-main.c b/svr-main.c
new file mode 100644
index 0000000..e59d895
--- /dev/null
+++ b/svr-main.c
@@ -0,0 +1,421 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+#include "includes.h"
+#include "dbutil.h"
+#include "session.h"
+#include "buffer.h"
+#include "signkey.h"
+#include "runopts.h"
+
+static size_t listensockets(int *sock, size_t 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();
+
+#if defined(DBMULTI_dropbear) || !defined(DROPBEAR_MULTI)
+#if defined(DBMULTI_dropbear) && defined(DROPBEAR_MULTI)
+int dropbear_main(int argc, char ** argv)
+#else
+int main(int argc, char ** argv)
+#endif
+{
+
+
+ _dropbear_exit = svr_dropbear_exit;
+ _dropbear_log = svr_dropbear_log;
+
+ /* get commandline options */
+ svr_getopts(argc, argv);
+
+#ifdef INETD_MODE
+ /* service program mode */
+ if (svr_opts.inetdmode) {
+ main_inetd();
+ /* notreached */
+ }
+#endif
+
+#ifdef NON_INETD_MODE
+ main_noinetd();
+ /* notreached */
+#endif
+
+ dropbear_exit("Compiled without normal mode, can't run without -i\n");
+ return -1;
+}
+#endif
+
+#ifdef INETD_MODE
+static void main_inetd() {
+
+ struct sockaddr_storage remoteaddr;
+ socklen_t remoteaddrlen;
+ char * addrstring = NULL;
+
+ /* Set up handlers, syslog, seed random */
+ commonsetup();
+
+ remoteaddrlen = sizeof(remoteaddr);
+ if (getpeername(0, (struct sockaddr*)&remoteaddr, &remoteaddrlen) < 0) {
+ dropbear_exit("Unable to getpeername: %s", strerror(errno));
+ }
+
+ /* In case our inetd was lax in logging source addresses */
+ addrstring = getaddrstring(&remoteaddr, 1);
+ dropbear_log(LOG_INFO, "Child connection from %s", 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
+ * this */
+ setsid();
+
+ /* Start service program
+ * -1 is a dummy childpipe, just something we can close() without
+ * mattering. */
+ svr_session(0, -1, getaddrhostname(&remoteaddr), addrstring);
+
+ /* notreached */
+}
+#endif /* INETD_MODE */
+
+#ifdef NON_INETD_MODE
+void main_noinetd() {
+ fd_set fds;
+ struct timeval seltimeout;
+ unsigned int i, j;
+ int val;
+ int maxsock = -1;
+ int listensocks[MAX_LISTEN_ADDR];
+ size_t listensockcount = 0;
+ FILE *pidfile = NULL;
+
+ int childpipes[MAX_UNAUTH_CLIENTS];
+ char * preauth_addrs[MAX_UNAUTH_CLIENTS];
+
+ int childsock;
+ int childpipe[2];
+
+ /* fork */
+ if (svr_opts.forkbg) {
+ int closefds = 0;
+#ifndef DEBUG_TRACE
+ if (!svr_opts.usingsyslog) {
+ closefds = 1;
+ }
+#endif
+ if (daemon(0, closefds) < 0) {
+ dropbear_exit("Failed to daemonize: %s", strerror(errno));
+ }
+ }
+
+ commonsetup();
+
+
+ /* should be done after syslog is working */
+ if (svr_opts.forkbg) {
+ dropbear_log(LOG_INFO, "Running in background");
+ } else {
+ dropbear_log(LOG_INFO, "Not forking");
+ }
+
+ /* create a PID file so that we can be killed easily */
+ pidfile = fopen(DROPBEAR_PIDFILE, "w");
+ if (pidfile) {
+ fprintf(pidfile, "%d\n", getpid());
+ fclose(pidfile);
+ }
+
+ /* sockets to identify pre-authenticated clients */
+ for (i = 0; i < MAX_UNAUTH_CLIENTS; i++) {
+ childpipes[i] = -1;
+ }
+ bzero(preauth_addrs, sizeof(preauth_addrs));
+
+ /* Set up the listening sockets */
+ /* XXX XXX ports */
+ listensockcount = listensockets(listensocks, MAX_LISTEN_ADDR, &maxsock);
+ if (listensockcount == 0)
+ {
+ dropbear_exit("No listening ports available.");
+ }
+
+ /* incoming connection select loop */
+ for(;;) {
+
+ FD_ZERO(&fds);
+
+ seltimeout.tv_sec = 60;
+ seltimeout.tv_usec = 0;
+
+ /* listening sockets */
+ for (i = 0; i < listensockcount; i++) {
+ FD_SET(listensocks[i], &fds);
+ }
+
+ /* pre-authentication clients */
+ for (i = 0; i < MAX_UNAUTH_CLIENTS; i++) {
+ if (childpipes[i] >= 0) {
+ FD_SET(childpipes[i], &fds);
+ maxsock = MAX(maxsock, childpipes[i]);
+ }
+ }
+
+ val = select(maxsock+1, &fds, NULL, NULL, &seltimeout);
+
+ if (exitflag) {
+ unlink(DROPBEAR_PIDFILE);
+ dropbear_exit("Terminated by signal");
+ }
+
+ if (val == 0) {
+ /* timeout reached */
+ continue;
+ }
+
+ if (val < 0) {
+ if (errno == EINTR) {
+ continue;
+ }
+ dropbear_exit("Listening socket error");
+ }
+
+ /* close fds which have been authed or closed - svr-auth.c handles
+ * closing the auth sockets on success */
+ for (i = 0; i < MAX_UNAUTH_CLIENTS; i++) {
+ if (childpipes[i] >= 0 && FD_ISSET(childpipes[i], &fds)) {
+ m_close(childpipes[i]);
+ childpipes[i] = -1;
+ m_free(preauth_addrs[i]);
+ }
+ }
+
+ /* handle each socket which has something to say */
+ for (i = 0; i < listensockcount; i++) {
+
+ struct sockaddr_storage remoteaddr;
+ socklen_t remoteaddrlen = 0;
+ size_t num_unauthed_for_addr = 0;
+ size_t num_unauthed_total = 0;
+ char * remote_addr_str = NULL;
+ pid_t fork_ret = 0;
+ size_t conn_idx = 0;
+
+ if (!FD_ISSET(listensocks[i], &fds))
+ continue;
+
+ remoteaddrlen = sizeof(remoteaddr);
+ childsock = accept(listensocks[i],
+ (struct sockaddr*)&remoteaddr, &remoteaddrlen);
+
+ if (childsock < 0) {
+ /* accept failed */
+ continue;
+ }
+
+ /* Limit the number of unauthenticated connections per IP */
+ remote_addr_str = getaddrstring(&remoteaddr, 0);
+
+ num_unauthed_for_addr = 0;
+ num_unauthed_total = 0;
+ for (j = 0; j < MAX_UNAUTH_CLIENTS; j++) {
+ if (childpipes[j] >= 0) {
+ num_unauthed_total++;
+ if (strcmp(remote_addr_str, preauth_addrs[j]) == 0) {
+ num_unauthed_for_addr++;
+ }
+ } else {
+ /* a free slot */
+ conn_idx = j;
+ }
+ }
+
+ if (num_unauthed_total >= MAX_UNAUTH_CLIENTS
+ || num_unauthed_for_addr >= MAX_UNAUTH_PER_IP) {
+ goto out;
+ }
+
+ if (pipe(childpipe) < 0) {
+ TRACE(("error creating child pipe"))
+ goto out;
+ }
+
+ fork_ret = fork();
+ if (fork_ret < 0) {
+ dropbear_log(LOG_WARNING, "error forking: %s", strerror(errno));
+ goto out;
+
+ } else if (fork_ret > 0) {
+
+ /* parent */
+ childpipes[conn_idx] = childpipe[0];
+ m_close(childpipe[1]);
+ preauth_addrs[conn_idx] = remote_addr_str;
+ remote_addr_str = NULL;
+
+ } else {
+
+ /* child */
+ char * addrstring = NULL;
+#ifdef DEBUG_FORKGPROF
+ extern void _start(void), etext(void);
+ monstartup((u_long)&_start, (u_long)&etext);
+#endif /* DEBUG_FORKGPROF */
+
+ m_free(remote_addr_str);
+ addrstring = getaddrstring(&remoteaddr, 1);
+ dropbear_log(LOG_INFO, "Child connection from %s", addrstring);
+
+ if (setsid() < 0) {
+ dropbear_exit("setsid: %s", strerror(errno));
+ }
+
+ /* make sure we close sockets */
+ for (i = 0; i < listensockcount; i++) {
+ m_close(listensocks[i]);
+ }
+
+ m_close(childpipe[0]);
+
+ /* start the session */
+ svr_session(childsock, childpipe[1],
+ getaddrhostname(&remoteaddr),
+ addrstring);
+ /* don't return */
+ dropbear_assert(0);
+ }
+
+out:
+ /* This section is important for the parent too */
+ m_close(childsock);
+ if (remote_addr_str) {
+ m_free(remote_addr_str);
+ }
+ }
+ } /* for(;;) loop */
+
+ /* don't reach here */
+}
+#endif /* NON_INETD_MODE */
+
+
+/* catch + reap zombie children */
+static void sigchld_handler(int UNUSED(unused)) {
+ struct sigaction sa_chld;
+
+ while(waitpid(-1, NULL, WNOHANG) > 0);
+
+ sa_chld.sa_handler = sigchld_handler;
+ sa_chld.sa_flags = SA_NOCLDSTOP;
+ if (sigaction(SIGCHLD, &sa_chld, NULL) < 0) {
+ dropbear_exit("signal() error");
+ }
+}
+
+/* catch any segvs */
+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 UNUSED(unused)) {
+
+ exitflag = 1;
+}
+
+/* Things used by inetd and non-inetd modes */
+static void commonsetup() {
+
+ struct sigaction sa_chld;
+#ifndef DISABLE_SYSLOG
+ if (svr_opts.usingsyslog) {
+ startsyslog();
+ }
+#endif
+
+ /* set up cleanup handler */
+ if (signal(SIGINT, sigintterm_handler) == SIG_ERR ||
+#ifndef DEBUG_VALGRIND
+ signal(SIGTERM, sigintterm_handler) == SIG_ERR ||
+#endif
+ signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
+ dropbear_exit("signal() error");
+ }
+
+ /* catch and reap zombie children */
+ sa_chld.sa_handler = sigchld_handler;
+ sa_chld.sa_flags = SA_NOCLDSTOP;
+ if (sigaction(SIGCHLD, &sa_chld, NULL) < 0) {
+ dropbear_exit("signal() error");
+ }
+ 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();
+
+ seedrandom();
+}
+
+/* Set up listening sockets for all the requested ports */
+static size_t listensockets(int *sock, size_t sockcount, int *maxfd) {
+
+ unsigned int i;
+ char* errstring = NULL;
+ size_t sockpos = 0;
+ int nsock;
+
+ TRACE(("listensockets: %d to try\n", svr_opts.portcount))
+
+ for (i = 0; i < svr_opts.portcount; i++) {
+
+ TRACE(("listening on '%s'", svr_opts.ports[i]))
+
+ nsock = dropbear_listen("", svr_opts.ports[i], &sock[sockpos],
+ sockcount - sockpos,
+ &errstring, maxfd);
+
+ if (nsock < 0) {
+ dropbear_log(LOG_WARNING, "Failed listening on '%s': %s",
+ svr_opts.ports[i], errstring);
+ m_free(errstring);
+ continue;
+ }
+
+ sockpos += nsock;
+
+ }
+ return sockpos;
+}
diff --git a/svr-runopts.c b/svr-runopts.c
new file mode 100644
index 0000000..8d8b8df
--- /dev/null
+++ b/svr-runopts.c
@@ -0,0 +1,315 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+#include "includes.h"
+#include "runopts.h"
+#include "signkey.h"
+#include "buffer.h"
+#include "dbutil.h"
+#include "algo.h"
+
+svr_runopts svr_opts; /* GLOBAL */
+
+static void printhelp(const char * progname);
+
+static void printhelp(const char * progname) {
+
+ fprintf(stderr, "Dropbear sshd v%s\n"
+ "Usage: %s [options]\n"
+ "Options are:\n"
+ "-b bannerfile Display the contents of bannerfile"
+ " before user login\n"
+ " (default: none)\n"
+#ifdef DROPBEAR_DSS
+ "-d dsskeyfile Use dsskeyfile for the dss host key\n"
+ " (default: %s)\n"
+#endif
+#ifdef DROPBEAR_RSA
+ "-r rsakeyfile Use rsakeyfile for the rsa host key\n"
+ " (default: %s)\n"
+#endif
+ "-F Don't fork into background\n"
+#ifdef DISABLE_SYSLOG
+ "(Syslog support not compiled in, using stderr)\n"
+#else
+ "-E Log to stderr rather than syslog\n"
+#endif
+#ifdef DO_MOTD
+ "-m Don't display the motd on login\n"
+#endif
+ "-w Disallow root logins\n"
+#if defined(ENABLE_SVR_PASSWORD_AUTH) || defined(ENABLE_SVR_PAM_AUTH)
+ "-s Disable password logins\n"
+ "-g Disable password logins for root\n"
+#endif
+#ifdef ENABLE_SVR_LOCALTCPFWD
+ "-j Disable local port forwarding\n"
+#endif
+#ifdef ENABLE_SVR_REMOTETCPFWD
+ "-k Disable remote port forwarding\n"
+ "-a Allow connections to forwarded ports from any host\n"
+#endif
+ "-p port Listen on specified tcp port, up to %d can be 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
+ DSS_PRIV_FILENAME,
+#endif
+#ifdef DROPBEAR_RSA
+ RSA_PRIV_FILENAME,
+#endif
+ DROPBEAR_MAX_PORTS, DROPBEAR_DEFPORT);
+}
+
+void svr_getopts(int argc, char ** argv) {
+
+ unsigned int i;
+ char ** next = 0;
+
+ /* see printhelp() for options */
+ svr_opts.rsakeyfile = NULL;
+ svr_opts.dsskeyfile = NULL;
+ svr_opts.bannerfile = NULL;
+ svr_opts.banner = NULL;
+ svr_opts.forkbg = 1;
+ svr_opts.norootlogin = 0;
+ svr_opts.noauthpass = 0;
+ svr_opts.norootpass = 0;
+ svr_opts.inetdmode = 0;
+ svr_opts.portcount = 0;
+ svr_opts.hostkey = NULL;
+#ifdef ENABLE_SVR_LOCALTCPFWD
+ svr_opts.nolocaltcp = 0;
+#endif
+#ifdef ENABLE_SVR_REMOTETCPFWD
+ svr_opts.noremotetcp = 0;
+#endif
+ /* not yet
+ opts.ipv4 = 1;
+ opts.ipv6 = 1;
+ */
+#ifdef DO_MOTD
+ svr_opts.domotd = 1;
+#endif
+#ifndef DISABLE_SYSLOG
+ svr_opts.usingsyslog = 1;
+#endif
+#ifdef ENABLE_SVR_REMOTETCPFWD
+ opts.listen_fwd_all = 0;
+#endif
+
+ for (i = 1; i < (unsigned int)argc; i++) {
+ if (next) {
+ *next = argv[i];
+ if (*next == NULL) {
+ dropbear_exit("Invalid null argument");
+ }
+ next = 0x00;
+ continue;
+ }
+
+ if (argv[i][0] == '-') {
+ switch (argv[i][1]) {
+ case 'b':
+ next = &svr_opts.bannerfile;
+ break;
+#ifdef DROPBEAR_DSS
+ case 'd':
+ next = &svr_opts.dsskeyfile;
+ break;
+#endif
+#ifdef DROPBEAR_RSA
+ case 'r':
+ next = &svr_opts.rsakeyfile;
+ break;
+#endif
+ case 'F':
+ svr_opts.forkbg = 0;
+ break;
+#ifndef DISABLE_SYSLOG
+ case 'E':
+ svr_opts.usingsyslog = 0;
+ break;
+#endif
+#ifdef ENABLE_SVR_LOCALTCPFWD
+ case 'j':
+ svr_opts.nolocaltcp = 1;
+ break;
+#endif
+#ifdef ENABLE_SVR_REMOTETCPFWD
+ case 'k':
+ svr_opts.noremotetcp = 1;
+ break;
+ case 'a':
+ opts.listen_fwd_all = 1;
+ break;
+#endif
+#ifdef INETD_MODE
+ case 'i':
+ svr_opts.inetdmode = 1;
+ break;
+#endif
+ case 'p':
+ 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
+ /* motd is displayed by default, -m turns it off */
+ case 'm':
+ svr_opts.domotd = 0;
+ break;
+#endif
+ case 'w':
+ svr_opts.norootlogin = 1;
+ break;
+#if defined(ENABLE_SVR_PASSWORD_AUTH) || defined(ENABLE_SVR_PAM_AUTH)
+ case 's':
+ svr_opts.noauthpass = 1;
+ break;
+ case 'g':
+ svr_opts.norootpass = 1;
+ break;
+#endif
+ case 'h':
+ printhelp(argv[0]);
+ exit(EXIT_FAILURE);
+ break;
+#ifdef DEBUG_TRACE
+ case 'v':
+ debug_trace = 1;
+ break;
+#endif
+ default:
+ fprintf(stderr, "Unknown argument %s\n", argv[i]);
+ printhelp(argv[0]);
+ exit(EXIT_FAILURE);
+ break;
+ }
+ }
+ }
+
+ /* 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;
+ }
+
+ if (svr_opts.bannerfile) {
+ struct stat buf;
+ if (stat(svr_opts.bannerfile, &buf) != 0) {
+ dropbear_exit("Error opening banner file '%s'",
+ svr_opts.bannerfile);
+ }
+
+ if (buf.st_size > MAX_BANNER_SIZE) {
+ dropbear_exit("Banner file too large, max is %d bytes",
+ MAX_BANNER_SIZE);
+ }
+
+ svr_opts.banner = buf_new(buf.st_size);
+ if (buf_readfile(svr_opts.banner, svr_opts.bannerfile)!=DROPBEAR_SUCCESS) {
+ dropbear_exit("Error reading banner file '%s'",
+ svr_opts.bannerfile);
+ }
+ buf_setpos(svr_opts.banner, 0);
+ }
+
+}
+
+static void disablekey(int type, const char* filename) {
+
+ int i;
+
+ for (i = 0; sshhostkey[i].name != NULL; i++) {
+ if (sshhostkey[i].val == type) {
+ sshhostkey[i].usable = 0;
+ break;
+ }
+ }
+ dropbear_log(LOG_WARNING, "Failed reading '%s', disabling %s", filename,
+ type == DROPBEAR_SIGNKEY_DSS ? "DSS" : "RSA");
+}
+
+/* Must be called after syslog/etc is working */
+void loadhostkeys() {
+
+ int ret;
+ int type;
+
+ TRACE(("enter loadhostkeys"))
+
+ svr_opts.hostkey = new_sign_key();
+
+#ifdef DROPBEAR_RSA
+ type = DROPBEAR_SIGNKEY_RSA;
+ ret = readhostkey(svr_opts.rsakeyfile, svr_opts.hostkey, &type);
+ if (ret == DROPBEAR_FAILURE) {
+ disablekey(DROPBEAR_SIGNKEY_RSA, svr_opts.rsakeyfile);
+ }
+#endif
+#ifdef DROPBEAR_DSS
+ type = DROPBEAR_SIGNKEY_DSS;
+ ret = readhostkey(svr_opts.dsskeyfile, svr_opts.hostkey, &type);
+ if (ret == DROPBEAR_FAILURE) {
+ disablekey(DROPBEAR_SIGNKEY_DSS, svr_opts.dsskeyfile);
+ }
+#endif
+
+ if ( 1
+#ifdef DROPBEAR_DSS
+ && svr_opts.hostkey->dsskey == NULL
+#endif
+#ifdef DROPBEAR_RSA
+ && svr_opts.hostkey->rsakey == NULL
+#endif
+ ) {
+ dropbear_exit("No hostkeys available");
+ }
+
+ TRACE(("leave loadhostkeys"))
+}
diff --git a/svr-service.c b/svr-service.c
new file mode 100644
index 0000000..2c78e7d
--- /dev/null
+++ b/svr-service.c
@@ -0,0 +1,87 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+#include "includes.h"
+#include "dbutil.h"
+#include "service.h"
+#include "session.h"
+#include "packet.h"
+#include "ssh.h"
+#include "auth.h"
+
+static void send_msg_service_accept(unsigned char *name, int len);
+
+/* processes a SSH_MSG_SERVICE_REQUEST, returning 0 if finished,
+ * 1 if not */
+void recv_msg_service_request() {
+
+ unsigned char * name;
+ unsigned int len;
+
+ TRACE(("enter recv_msg_service_request"))
+
+ name = buf_getstring(ses.payload, &len);
+
+ /* ssh-userauth */
+ if (len == SSH_SERVICE_USERAUTH_LEN &&
+ strncmp(SSH_SERVICE_USERAUTH, name, len) == 0) {
+
+ send_msg_service_accept(name, len);
+ m_free(name);
+ TRACE(("leave recv_msg_service_request: done ssh-userauth"))
+ return;
+ }
+
+ /* ssh-connection */
+ if (len == SSH_SERVICE_CONNECTION_LEN &&
+ (strncmp(SSH_SERVICE_CONNECTION, name, len) == 0)) {
+ if (ses.authstate.authdone != 1) {
+ dropbear_exit("request for connection before auth");
+ }
+
+ send_msg_service_accept(name, len);
+ m_free(name);
+ TRACE(("leave recv_msg_service_request: done ssh-connection"))
+ return;
+ }
+
+ m_free(name);
+ /* TODO this should be a MSG_DISCONNECT */
+ dropbear_exit("unrecognised SSH_MSG_SERVICE_REQUEST");
+
+
+}
+
+static void send_msg_service_accept(unsigned char *name, int len) {
+
+ TRACE(("accepting service %s", name))
+
+ CHECKCLEARTOWRITE();
+
+ buf_putbyte(ses.writepayload, SSH_MSG_SERVICE_ACCEPT);
+ buf_putstring(ses.writepayload, name, len);
+
+ encrypt_packet();
+
+}
diff --git a/svr-session.c b/svr-session.c
new file mode 100644
index 0000000..70029f8
--- /dev/null
+++ b/svr-session.c
@@ -0,0 +1,201 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+#include "includes.h"
+#include "session.h"
+#include "dbutil.h"
+#include "packet.h"
+#include "algo.h"
+#include "buffer.h"
+#include "dss.h"
+#include "ssh.h"
+#include "random.h"
+#include "kex.h"
+#include "channel.h"
+#include "chansession.h"
+#include "atomicio.h"
+#include "tcpfwd.h"
+#include "service.h"
+#include "auth.h"
+#include "runopts.h"
+
+static void svr_remoteclosed();
+
+struct serversession svr_ses; /* GLOBAL */
+
+static const packettype svr_packettypes[] = {
+ {SSH_MSG_CHANNEL_DATA, recv_msg_channel_data},
+ {SSH_MSG_CHANNEL_WINDOW_ADJUST, recv_msg_channel_window_adjust},
+ {SSH_MSG_USERAUTH_REQUEST, recv_msg_userauth_request}, /* server */
+ {SSH_MSG_SERVICE_REQUEST, recv_msg_service_request}, /* server */
+ {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, char *addrstring) {
+
+ struct timeval timeout;
+
+ reseedrandom();
+
+ crypto_init();
+ common_session_init(sock, remotehost);
+
+ /* Initialise server specific parts of the session */
+ svr_ses.childpipe = childpipe;
+ svr_ses.addrstring = addrstring;
+ svr_authinitialise();
+ chaninitialise(svr_chantypes);
+ svr_chansessinitialise();
+
+ if (gettimeofday(&timeout, 0) < 0) {
+ dropbear_exit("Error getting time");
+ }
+
+ ses.connecttimeout = timeout.tv_sec + AUTH_TIMEOUT;
+
+ /* set up messages etc */
+ ses.remoteclosed = svr_remoteclosed;
+
+ /* packet handlers */
+ ses.packettypes = svr_packettypes;
+ ses.buf_match_algo = svr_buf_match_algo;
+
+ ses.isserver = 1;
+
+ /* We're ready to go now */
+ sessinitdone = 1;
+
+ /* exchange identification, version etc */
+ session_identification();
+
+ /* start off with key exchange */
+ send_msg_kexinit();
+
+ /* Run the main for loop. NULL is for the dispatcher - only the client
+ * code makes use of it */
+ session_loop(NULL);
+
+ /* Not reached */
+
+}
+
+/* failure exit - format must be <= 100 chars */
+void svr_dropbear_exit(int exitcode, const char* format, va_list param) {
+
+ char fmtbuf[300];
+
+ if (!sessinitdone) {
+ /* before session init */
+ snprintf(fmtbuf, sizeof(fmtbuf),
+ "premature exit: %s", format);
+ } else if (ses.authstate.authdone) {
+ /* user has authenticated */
+ snprintf(fmtbuf, sizeof(fmtbuf),
+ "exit after auth (%s): %s",
+ ses.authstate.printableuser, format);
+ } else if (ses.authstate.printableuser) {
+ /* we have a potential user */
+ snprintf(fmtbuf, sizeof(fmtbuf),
+ "exit before auth (user '%s', %d fails): %s",
+ ses.authstate.printableuser, ses.authstate.failcount, format);
+ } else {
+ /* before userauth */
+ snprintf(fmtbuf, sizeof(fmtbuf),
+ "exit before auth: %s", format);
+ }
+
+ _dropbear_log(LOG_INFO, fmtbuf, param);
+
+ /* must be after we've done with username etc */
+ common_session_cleanup();
+
+ exit(exitcode);
+
+}
+
+/* priority is priority as with syslog() */
+void svr_dropbear_log(int priority, const char* format, va_list param) {
+
+ char printbuf[1024];
+ char datestr[20];
+ time_t timesec;
+ int havetrace = 0;
+
+ vsnprintf(printbuf, sizeof(printbuf), format, param);
+
+#ifndef DISABLE_SYSLOG
+ if (svr_opts.usingsyslog) {
+ syslog(priority, "%s", printbuf);
+ }
+#endif
+
+ /* 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 = debug_trace;
+#endif
+
+ if (!svr_opts.usingsyslog || havetrace)
+ {
+ timesec = time(NULL);
+ if (strftime(datestr, sizeof(datestr), "%b %d %H:%M:%S",
+ localtime(&timesec)) == 0) {
+ datestr[0] = '?'; datestr[1] = '\0';
+ }
+ fprintf(stderr, "[%d] %s %s\n", getpid(), datestr, printbuf);
+ }
+}
+
+/* called when the remote side closes the connection */
+static void svr_remoteclosed() {
+
+ close(ses.sock);
+ ses.sock = -1;
+ dropbear_close("Exited normally");
+
+}
+
diff --git a/svr-tcpfwd.c b/svr-tcpfwd.c
new file mode 100644
index 0000000..6391c4c
--- /dev/null
+++ b/svr-tcpfwd.c
@@ -0,0 +1,290 @@
+/*
+ * Dropbear SSH
+ *
+ * Copyright (c) 2002,2003 Matt Johnston
+ * Copyright (c) 2004 by Mihnea Stoenescu
+ * 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 "ssh.h"
+#include "tcpfwd.h"
+#include "dbutil.h"
+#include "session.h"
+#include "buffer.h"
+#include "packet.h"
+#include "listener.h"
+#include "runopts.h"
+
+#ifdef ENABLE_SVR_REMOTETCPFWD
+
+static void send_msg_request_success();
+static void send_msg_request_failure();
+static int svr_cancelremotetcp();
+static int svr_remotetcpreq();
+static int newtcpdirect(struct Channel * channel);
+
+
+const struct ChanType svr_chan_tcpdirect = {
+ 1, /* sepfds */
+ "direct-tcpip",
+ newtcpdirect, /* init */
+ NULL, /* checkclose */
+ NULL, /* reqhandler */
+ NULL /* closehandler */
+};
+
+static const struct ChanType svr_chan_tcpremote = {
+ 1, /* sepfds */
+ "forwarded-tcpip",
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+/* At the moment this is completely used for tcp code (with the name reflecting
+ * that). If new request types are added, this should be replaced with code
+ * similar to the request-switching in chansession.c */
+void recv_msg_global_request_remotetcp() {
+
+ unsigned char* reqname = NULL;
+ unsigned int namelen;
+ unsigned int wantreply = 0;
+ int ret = DROPBEAR_FAILURE;
+
+ TRACE(("enter recv_msg_global_request_remotetcp"))
+
+ if (svr_opts.noremotetcp) {
+ TRACE(("leave recv_msg_global_request_remotetcp: remote tcp forwarding disabled"))
+ goto out;
+ }
+
+ reqname = buf_getstring(ses.payload, &namelen);
+ wantreply = buf_getbool(ses.payload);
+
+ if (namelen > MAX_NAME_LEN) {
+ TRACE(("name len is wrong: %d", namelen))
+ goto out;
+ }
+
+ if (strcmp("tcpip-forward", reqname) == 0) {
+ ret = svr_remotetcpreq();
+ } else if (strcmp("cancel-tcpip-forward", reqname) == 0) {
+ ret = svr_cancelremotetcp();
+ } else {
+ TRACE(("reqname isn't tcpip-forward: '%s'", reqname))
+ }
+
+out:
+ if (wantreply) {
+ if (ret == DROPBEAR_SUCCESS) {
+ send_msg_request_success();
+ } else {
+ send_msg_request_failure();
+ }
+ }
+
+ m_free(reqname);
+
+ TRACE(("leave recv_msg_global_request"))
+}
+
+
+static void send_msg_request_success() {
+
+ CHECKCLEARTOWRITE();
+ buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_SUCCESS);
+ encrypt_packet();
+
+}
+
+static void send_msg_request_failure() {
+
+ CHECKCLEARTOWRITE();
+ buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_FAILURE);
+ encrypt_packet();
+
+}
+
+static int matchtcp(void* typedata1, void* typedata2) {
+
+ const struct TCPListener *info1 = (struct TCPListener*)typedata1;
+ const struct TCPListener *info2 = (struct TCPListener*)typedata2;
+
+ return (info1->listenport == info2->listenport)
+ && (info1->chantype == info2->chantype)
+ && (strcmp(info1->listenaddr, info2->listenaddr) == 0);
+}
+
+static int svr_cancelremotetcp() {
+
+ int ret = DROPBEAR_FAILURE;
+ unsigned char * bindaddr = NULL;
+ unsigned int addrlen;
+ unsigned int port;
+ struct Listener * listener = NULL;
+ struct TCPListener tcpinfo;
+
+ TRACE(("enter cancelremotetcp"))
+
+ bindaddr = buf_getstring(ses.payload, &addrlen);
+ if (addrlen > MAX_IP_LEN) {
+ TRACE(("addr len too long: %d", addrlen))
+ goto out;
+ }
+
+ port = buf_getint(ses.payload);
+
+ tcpinfo.sendaddr = NULL;
+ tcpinfo.sendport = 0;
+ tcpinfo.listenaddr = bindaddr;
+ tcpinfo.listenport = port;
+ listener = get_listener(CHANNEL_ID_TCPFORWARDED, &tcpinfo, matchtcp);
+ if (listener) {
+ remove_listener( listener );
+ ret = DROPBEAR_SUCCESS;
+ }
+
+out:
+ m_free(bindaddr);
+ TRACE(("leave cancelremotetcp"))
+ return ret;
+}
+
+static int svr_remotetcpreq() {
+
+ int ret = DROPBEAR_FAILURE;
+ unsigned char * bindaddr = NULL;
+ unsigned int addrlen;
+ struct TCPListener *tcpinfo = NULL;
+ unsigned int port;
+
+ TRACE(("enter remotetcpreq"))
+
+ bindaddr = buf_getstring(ses.payload, &addrlen);
+ if (addrlen > MAX_IP_LEN) {
+ TRACE(("addr len too long: %d", addrlen))
+ goto out;
+ }
+
+ port = buf_getint(ses.payload);
+
+ if (port == 0) {
+ dropbear_log(LOG_INFO, "Server chosen tcpfwd ports are unsupported");
+ goto out;
+ }
+
+ if (port < 1 || port > 65535) {
+ TRACE(("invalid port: %d", port))
+ goto out;
+ }
+
+ if (!ses.allowprivport && port < IPPORT_RESERVED) {
+ TRACE(("can't assign port < 1024 for non-root"))
+ goto out;
+ }
+
+ tcpinfo = (struct TCPListener*)m_malloc(sizeof(struct TCPListener));
+ tcpinfo->sendaddr = NULL;
+ tcpinfo->sendport = 0;
+ tcpinfo->listenaddr = bindaddr;
+ tcpinfo->listenport = port;
+ tcpinfo->chantype = &svr_chan_tcpremote;
+ tcpinfo->tcp_type = forwarded;
+
+ ret = listen_tcpfwd(tcpinfo);
+
+out:
+ if (ret == DROPBEAR_FAILURE) {
+ /* we only free it if a listener wasn't created, since the listener
+ * has to remember it if it's to be cancelled */
+ m_free(tcpinfo->listenaddr);
+ m_free(tcpinfo);
+ }
+ TRACE(("leave remotetcpreq"))
+ return ret;
+}
+
+/* Called upon creating a new direct tcp channel (ie we connect out to an
+ * address */
+static int newtcpdirect(struct Channel * channel) {
+
+ unsigned char* desthost = NULL;
+ unsigned int destport;
+ unsigned char* orighost = NULL;
+ unsigned int origport;
+ char portstring[NI_MAXSERV];
+ int sock;
+ int len;
+ int err = SSH_OPEN_ADMINISTRATIVELY_PROHIBITED;
+
+ if (svr_opts.nolocaltcp) {
+ 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"))
+ goto out;
+ }
+
+ destport = buf_getint(ses.payload);
+
+ orighost = buf_getstring(ses.payload, &len);
+ if (len > MAX_HOST_LEN) {
+ TRACE(("leave newtcpdirect: orighost too long"))
+ goto out;
+ }
+
+ origport = buf_getint(ses.payload);
+
+ /* best be sure */
+ if (origport > 65535 || destport > 65535) {
+ TRACE(("leave newtcpdirect: port > 65535"))
+ goto out;
+ }
+
+ snprintf(portstring, sizeof(portstring), "%d", destport);
+ sock = connect_remote(desthost, portstring, 1, NULL);
+ if (sock < 0) {
+ err = SSH_OPEN_CONNECT_FAILED;
+ TRACE(("leave newtcpdirect: sock failed"))
+ goto out;
+ }
+
+ ses.maxfd = MAX(ses.maxfd, sock);
+
+ /* We don't set readfd, that will get set after the connection's
+ * progress succeeds */
+ channel->writefd = sock;
+ channel->initconn = 1;
+
+ err = SSH_OPEN_IN_PROGRESS;
+
+out:
+ m_free(desthost);
+ m_free(orighost);
+ TRACE(("leave newtcpdirect: err %d", err))
+ return err;
+}
+
+#endif
diff --git a/svr-x11fwd.c b/svr-x11fwd.c
new file mode 100644
index 0000000..cbc8a79
--- /dev/null
+++ b/svr-x11fwd.c
@@ -0,0 +1,236 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+#include "includes.h"
+
+#ifndef DISABLE_X11FWD
+#include "x11fwd.h"
+#include "session.h"
+#include "ssh.h"
+#include "dbutil.h"
+#include "chansession.h"
+#include "channel.h"
+#include "packet.h"
+#include "buffer.h"
+
+#define X11BASEPORT 6000
+#define X11BINDBASE 6010
+
+static void x11accept(struct Listener* listener, int sock);
+static int bindport(int fd);
+static int send_msg_channel_open_x11(int fd, struct sockaddr_in* addr);
+
+/* called as a request for a session channel, sets up listening X11 */
+/* returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
+int x11req(struct ChanSess * chansess) {
+
+ int fd;
+
+ /* we already have an x11 connection */
+ if (chansess->x11listener != NULL) {
+ return DROPBEAR_FAILURE;
+ }
+
+ chansess->x11singleconn = buf_getbool(ses.payload);
+ chansess->x11authprot = buf_getstring(ses.payload, NULL);
+ chansess->x11authcookie = buf_getstring(ses.payload, NULL);
+ chansess->x11screennum = buf_getint(ses.payload);
+
+ /* create listening socket */
+ fd = socket(PF_INET, SOCK_STREAM, 0);
+ if (fd < 0) {
+ goto fail;
+ }
+
+ /* allocate port and bind */
+ chansess->x11port = bindport(fd);
+ if (chansess->x11port < 0) {
+ goto fail;
+ }
+
+ /* listen */
+ if (listen(fd, 20) < 0) {
+ goto fail;
+ }
+
+ /* set non-blocking */
+ setnonblocking(fd);
+
+ /* listener code will handle the socket now.
+ * No cleanup handler needed, since listener_remove only happens
+ * from our cleanup anyway */
+ chansess->x11listener = new_listener( &fd, 1, 0, chansess, x11accept, NULL);
+ if (chansess->x11listener == NULL) {
+ goto fail;
+ }
+
+ return DROPBEAR_SUCCESS;
+
+fail:
+ /* cleanup */
+ m_free(chansess->x11authprot);
+ m_free(chansess->x11authcookie);
+ close(fd);
+
+ return DROPBEAR_FAILURE;
+}
+
+/* accepts a new X11 socket */
+/* returns DROPBEAR_FAILURE or DROPBEAR_SUCCESS */
+static void x11accept(struct Listener* listener, int sock) {
+
+ int fd;
+ struct sockaddr_in addr;
+ int len;
+ int ret;
+ struct ChanSess * chansess = (struct ChanSess *)(listener->typedata);
+
+ len = sizeof(addr);
+
+ fd = accept(sock, (struct sockaddr*)&addr, &len);
+ if (fd < 0) {
+ return;
+ }
+
+ /* if single-connection we close it up */
+ if (chansess->x11singleconn) {
+ x11cleanup(chansess);
+ }
+
+ ret = send_msg_channel_open_x11(fd, &addr);
+ if (ret == DROPBEAR_FAILURE) {
+ close(fd);
+ }
+}
+
+/* This is called after switching to the user, and sets up the xauth
+ * and environment variables. */
+void x11setauth(struct ChanSess *chansess) {
+
+ char display[20]; /* space for "localhost:12345.123" */
+ FILE * authprog = NULL;
+ int val;
+
+ if (chansess->x11listener == NULL) {
+ return;
+ }
+
+ /* create the DISPLAY string */
+ val = snprintf(display, sizeof(display), "localhost:%d.%d",
+ chansess->x11port - X11BASEPORT, chansess->x11screennum);
+ if (val < 0 || val >= (int)sizeof(display)) {
+ /* string was truncated */
+ return;
+ }
+
+ addnewvar("DISPLAY", display);
+
+ /* create the xauth string */
+ val = snprintf(display, sizeof(display), "unix:%d.%d",
+ chansess->x11port - X11BASEPORT, chansess->x11screennum);
+ if (val < 0 || val >= (int)sizeof(display)) {
+ /* string was truncated */
+ return;
+ }
+
+ /* popen is a nice function - code is strongly based on OpenSSH's */
+ authprog = popen(XAUTH_COMMAND, "w");
+ if (authprog) {
+ fprintf(authprog, "add %s %s %s\n",
+ display, chansess->x11authprot, chansess->x11authcookie);
+ pclose(authprog);
+ } else {
+ fprintf(stderr, "Failed to run %s\n", XAUTH_COMMAND);
+ }
+}
+
+void x11cleanup(struct ChanSess *chansess) {
+
+ m_free(chansess->x11authprot);
+ m_free(chansess->x11authcookie);
+
+ TRACE(("chansess %s", chansess))
+ if (chansess->x11listener != NULL) {
+ remove_listener(chansess->x11listener);
+ chansess->x11listener = NULL;
+ }
+}
+
+static const struct ChanType chan_x11 = {
+ 0, /* sepfds */
+ "x11",
+ NULL, /* inithandler */
+ NULL, /* checkclose */
+ NULL, /* reqhandler */
+ NULL /* closehandler */
+};
+
+
+static int send_msg_channel_open_x11(int fd, struct sockaddr_in* addr) {
+
+ char* ipstring = NULL;
+
+ if (send_msg_channel_open_init(fd, &chan_x11) == DROPBEAR_SUCCESS) {
+ ipstring = inet_ntoa(addr->sin_addr);
+ buf_putstring(ses.writepayload, ipstring, strlen(ipstring));
+ buf_putint(ses.writepayload, addr->sin_port);
+
+ encrypt_packet();
+ return DROPBEAR_SUCCESS;
+ } else {
+ return DROPBEAR_FAILURE;
+ }
+
+}
+
+/* returns the port bound to, or -1 on failure.
+ * Will attempt to bind to a port X11BINDBASE (6010 usually) or upwards */
+static int bindport(int fd) {
+
+ struct sockaddr_in addr;
+ uint16_t port;
+
+ memset((void*)&addr, 0x0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ /* if we can't find one in 2000 ports free, something's wrong */
+ for (port = X11BINDBASE; port < X11BINDBASE + 2000; port++) {
+ addr.sin_port = htons(port);
+ if (bind(fd, (struct sockaddr*)&addr,
+ sizeof(struct sockaddr_in)) == 0) {
+ /* success */
+ return port;
+ }
+ if (errno == EADDRINUSE) {
+ /* try the next port */
+ continue;
+ }
+ /* otherwise it was an error we don't know about */
+ dropbear_log(LOG_DEBUG, "failed to bind x11 socket");
+ break;
+ }
+ return -1;
+}
+#endif /* DROPBEAR_X11FWD */
diff --git a/tcp-accept.c b/tcp-accept.c
new file mode 100644
index 0000000..ffb175e
--- /dev/null
+++ b/tcp-accept.c
@@ -0,0 +1,143 @@
+/*
+ * 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. */
+
+#include "includes.h"
+#include "ssh.h"
+#include "tcpfwd.h"
+#include "dbutil.h"
+#include "session.h"
+#include "buffer.h"
+#include "packet.h"
+#include "listener.h"
+#include "runopts.h"
+
+#ifdef DROPBEAR_TCP_ACCEPT
+
+static void cleanup_tcp(struct Listener *listener) {
+
+ struct TCPListener *tcpinfo = (struct TCPListener*)(listener->typedata);
+
+ m_free(tcpinfo->sendaddr);
+ m_free(tcpinfo->listenaddr);
+ m_free(tcpinfo);
+}
+
+static void tcp_acceptor(struct Listener *listener, int sock) {
+
+ int fd;
+ struct sockaddr_storage addr;
+ socklen_t len;
+ char ipstring[NI_MAXHOST], portstring[NI_MAXSERV];
+ struct TCPListener *tcpinfo = (struct TCPListener*)(listener->typedata);
+
+ len = sizeof(addr);
+
+ fd = accept(sock, (struct sockaddr*)&addr, &len);
+ if (fd < 0) {
+ return;
+ }
+
+ if (getnameinfo((struct sockaddr*)&addr, len, ipstring, sizeof(ipstring),
+ portstring, sizeof(portstring),
+ NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
+ return;
+ }
+
+ if (send_msg_channel_open_init(fd, tcpinfo->chantype) == DROPBEAR_SUCCESS) {
+ unsigned char* addr = NULL;
+ unsigned int port = 0;
+
+ if (tcpinfo->tcp_type == direct) {
+ /* "direct-tcpip" */
+ /* host to connect, port to connect */
+ addr = tcpinfo->sendaddr;
+ port = tcpinfo->sendport;
+ } else {
+ dropbear_assert(tcpinfo->tcp_type == forwarded);
+ /* "forwarded-tcpip" */
+ /* address that was connected, port that was connected */
+ addr = tcpinfo->listenaddr;
+ port = tcpinfo->listenport;
+ }
+
+ buf_putstring(ses.writepayload, addr, strlen(addr));
+ buf_putint(ses.writepayload, port);
+
+ /* originator ip */
+ buf_putstring(ses.writepayload, ipstring, strlen(ipstring));
+ /* originator port */
+ buf_putint(ses.writepayload, atol(portstring));
+
+ encrypt_packet();
+
+ } else {
+ /* XXX debug? */
+ close(fd);
+ }
+}
+
+int listen_tcpfwd(struct TCPListener* tcpinfo) {
+
+ char portstring[NI_MAXSERV];
+ int socks[DROPBEAR_MAX_SOCKS];
+ struct Listener *listener = NULL;
+ int nsocks;
+ char* errstring = NULL;
+ // listen_spec = NULL indicates localhost
+ const char* listen_spec = NULL;
+
+ 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);
+
+ /* a listenaddr of "" will indicate all interfaces */
+ if (opts.listen_fwd_all
+ && (strcmp(tcpinfo->listenaddr, "localhost") != 0) ) {
+ listen_spec = tcpinfo->listenaddr;
+ }
+
+ nsocks = dropbear_listen(listen_spec, portstring, socks,
+ DROPBEAR_MAX_SOCKS, &errstring, &ses.maxfd);
+ if (nsocks < 0) {
+ dropbear_log(LOG_INFO, "TCP forward failed: %s", errstring);
+ m_free(errstring);
+ TRACE(("leave listen_tcpfwd: dropbear_listen failed"))
+ return DROPBEAR_FAILURE;
+ }
+
+ listener = new_listener(socks, nsocks, CHANNEL_ID_TCPFORWARDED, tcpinfo,
+ tcp_acceptor, cleanup_tcp);
+
+ if (listener == NULL) {
+ m_free(tcpinfo);
+ TRACE(("leave listen_tcpfwd: listener failed"))
+ return DROPBEAR_FAILURE;
+ }
+
+ TRACE(("leave listen_tcpfwd: success"))
+ return DROPBEAR_SUCCESS;
+}
+
+#endif /* DROPBEAR_TCP_ACCEPT */
diff --git a/tcpfwd.h b/tcpfwd.h
new file mode 100644
index 0000000..28af029
--- /dev/null
+++ b/tcpfwd.h
@@ -0,0 +1,69 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+#ifndef _TCPFWD_H
+#define _TCPFWD_H
+
+#include "channel.h"
+
+struct TCPListener {
+
+ /* For a direct-tcpip request, it's the addr/port we want the other
+ * end to connect to */
+ unsigned char *sendaddr;
+ unsigned int sendport;
+
+ /* This is the address/port that we listen on. The address has special
+ * meanings as per the rfc, "" for all interfaces, "localhost" for
+ * localhost, or a normal interface name. */
+ unsigned char *listenaddr;
+ unsigned int listenport;
+
+ const struct ChanType *chantype;
+ enum {direct, forwarded} tcp_type;
+};
+
+/* A link in a list of forwards */
+struct TCPFwdList {
+
+ const unsigned char* connectaddr;
+ unsigned int connectport;
+ unsigned int listenport;
+ struct TCPFwdList * next;
+
+};
+
+/* Server */
+void recv_msg_global_request_remotetcp();
+extern const struct ChanType svr_chan_tcpdirect;
+
+/* Client */
+void setup_localtcp();
+void setup_remotetcp();
+extern const struct ChanType cli_chan_tcpremote;
+
+/* Common */
+int listen_tcpfwd(struct TCPListener* tcpinfo);
+
+
+#endif
diff --git a/termcodes.c b/termcodes.c
new file mode 100644
index 0000000..d59505c
--- /dev/null
+++ b/termcodes.c
@@ -0,0 +1,187 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+#include "includes.h"
+#include "termcodes.h"
+
+const struct TermCode termcodes[MAX_TERMCODE+1] = {
+
+ {0, 0}, /* TTY_OP_END */
+ {VINTR, TERMCODE_CONTROLCHAR}, /* control character codes */
+ {VQUIT, TERMCODE_CONTROLCHAR},
+ {VERASE, TERMCODE_CONTROLCHAR},
+ {VKILL, TERMCODE_CONTROLCHAR},
+ {VEOF, TERMCODE_CONTROLCHAR},
+ {VEOL, TERMCODE_CONTROLCHAR},
+ {VEOL2, TERMCODE_CONTROLCHAR},
+ {VSTART, TERMCODE_CONTROLCHAR},
+ {VSTOP, TERMCODE_CONTROLCHAR},
+ {VSUSP, TERMCODE_CONTROLCHAR},
+#ifdef VDSUSP
+ {VDSUSP, TERMCODE_CONTROLCHAR},
+#else
+ {0, 0},
+#endif
+#ifdef VREPRINT
+ {VREPRINT, TERMCODE_CONTROLCHAR},
+#else
+ {0, 0},
+#endif
+#ifdef AIX
+ {CERASE, TERMCODE_CONTROLCHAR},
+#else
+ {VWERASE, TERMCODE_CONTROLCHAR},
+#endif
+ {VLNEXT, TERMCODE_CONTROLCHAR},
+#ifdef VFLUSH
+ {VFLUSH, TERMCODE_CONTROLCHAR},
+#else
+ {0, 0},
+#endif
+#ifdef VSWTCH
+ {VSWTCH, TERMCODE_CONTROLCHAR},
+#else
+ {0, 0},
+#endif
+#ifdef VSTATUS
+ {VSTATUS, TERMCODE_CONTROLCHAR},
+#else
+ {0, 0},
+#endif
+#ifdef AIX
+ {CKILL, TERMCODE_CONTROLCHAR},
+#elif defined(VDISCARD)
+ {VDISCARD, TERMCODE_CONTROLCHAR},
+#else
+ {0, 0},
+#endif
+ {0, 0}, /* 19 */
+ {0, 0},
+ {0, 0},
+ {0, 0},
+ {0, 0},
+ {0, 0},
+ {0, 0},
+ {0, 0},
+ {0, 0},
+ {0, 0},
+ {0, 0}, /* 29 */
+ {IGNPAR, TERMCODE_INPUT}, /* input flags */
+ {PARMRK, TERMCODE_INPUT},
+ {INPCK, TERMCODE_INPUT},
+ {ISTRIP, TERMCODE_INPUT},
+ {INLCR, TERMCODE_INPUT},
+ {IGNCR, TERMCODE_INPUT},
+ {ICRNL, TERMCODE_INPUT},
+#ifdef IUCLC
+ {IUCLC, TERMCODE_INPUT},
+#else
+ {0, 0},
+#endif
+ {IXON, TERMCODE_INPUT},
+ {IXANY, TERMCODE_INPUT},
+ {IXOFF, TERMCODE_INPUT},
+#ifdef IMAXBEL
+ {IMAXBEL, TERMCODE_INPUT},
+#else
+ {0, 0},
+#endif
+ {0, 0}, /* 42 */
+ {0, 0},
+ {0, 0},
+ {0, 0},
+ {0, 0},
+ {0, 0},
+ {0, 0},
+ {0, 0}, /* 49 */
+ {ISIG, TERMCODE_LOCAL}, /* local flags */
+ {ICANON, TERMCODE_LOCAL},
+#ifdef XCASE
+ {XCASE, TERMCODE_LOCAL},
+#else
+ {0, 0},
+#endif
+ {ECHO, TERMCODE_LOCAL},
+ {ECHOE, TERMCODE_LOCAL},
+ {ECHOK, TERMCODE_LOCAL},
+ {ECHONL, TERMCODE_LOCAL},
+ {NOFLSH, TERMCODE_LOCAL},
+ {TOSTOP, TERMCODE_LOCAL},
+ {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},
+ {0, 0},
+ {0, 0},
+ {0, 0},
+ {0, 0}, /* 69 */
+ {OPOST, TERMCODE_OUTPUT}, /* output flags */
+#ifdef OLCUC
+ {OLCUC, TERMCODE_OUTPUT},
+#else
+ {0, 0},
+#endif
+ {ONLCR, TERMCODE_OUTPUT},
+#ifdef OCRNL
+ {OCRNL, TERMCODE_OUTPUT},
+#else
+ {0, 0},
+#endif
+#ifdef ONOCR
+ {ONOCR, TERMCODE_OUTPUT},
+#else
+ {0, 0},
+#endif
+#ifdef ONLRET
+ {ONLRET, TERMCODE_OUTPUT},
+#else
+ {0, 0},
+#endif
+ {0, 0}, /* 76 */
+ {0, 0},
+ {0, 0},
+ {0, 0},
+ {0, 0},
+ {0, 0},
+ {0, 0},
+ {0, 0},
+ {0, 0},
+ {0, 0},
+ {0, 0},
+ {0, 0},
+ {0, 0},
+ {0, 0}, /* 89 */
+ {CS7, TERMCODE_CONTROL},
+ {CS8, TERMCODE_CONTROL},
+ {PARENB, TERMCODE_CONTROL},
+ {PARODD, TERMCODE_CONTROL}
+ /* 94 */
+};
diff --git a/termcodes.h b/termcodes.h
new file mode 100644
index 0000000..00792ea
--- /dev/null
+++ b/termcodes.h
@@ -0,0 +1,46 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+
+#ifndef _TERMCODES_H_
+#define _TERMCODES_H_
+
+#define TERMCODE_NONE 0
+#define TERMCODE_CONTROL 1
+#define TERMCODE_INPUT 2
+#define TERMCODE_OUTPUT 3
+#define TERMCODE_LOCAL 4
+#define TERMCODE_CONTROLCHAR 5
+
+#define MAX_TERMCODE 93
+
+struct TermCode {
+
+ unsigned int mapcode;
+ unsigned char type;
+
+};
+
+extern const struct TermCode termcodes[];
+
+#endif /* _TERMCODES_H_ */
diff --git a/x11fwd.h b/x11fwd.h
new file mode 100644
index 0000000..5855a68
--- /dev/null
+++ b/x11fwd.h
@@ -0,0 +1,37 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * 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. */
+#ifndef _X11FWD_H_
+#define _X11FWD_H_
+#ifndef DISABLE_X11FWD
+
+#include "includes.h"
+#include "chansession.h"
+#include "channel.h"
+
+int x11req(struct ChanSess * chansess);
+void x11setauth(struct ChanSess *chansess);
+void x11cleanup(struct ChanSess *chansess);
+
+#endif /* DROPBEAR_X11FWD */
+#endif /* _X11FWD_H_ */